@openacp/cli 2026.405.2 → 2026.406.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{channel-DstweC6V.d.ts → channel-CKXNnTy4.d.ts} +1 -0
- package/dist/cli.js +686 -457
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +29 -14
- package/dist/index.js +481 -267
- package/dist/index.js.map +1 -1
- package/dist/testing.d.ts +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -517,6 +517,7 @@ var init_config = __esm({
|
|
|
517
517
|
defaultAgent: z.string(),
|
|
518
518
|
workspace: z.object({
|
|
519
519
|
baseDir: z.string().default("~/openacp-workspace"),
|
|
520
|
+
allowExternalWorkspaces: z.boolean().default(true),
|
|
520
521
|
security: z.object({
|
|
521
522
|
allowedPaths: z.array(z.string()).default([]),
|
|
522
523
|
envWhitelist: z.array(z.string()).default([])
|
|
@@ -637,13 +638,22 @@ var init_config = __esm({
|
|
|
637
638
|
if (input2.startsWith("/") || input2.startsWith("~")) {
|
|
638
639
|
const resolved2 = expandHome3(input2);
|
|
639
640
|
const base = expandHome3(this.config.workspace.baseDir);
|
|
640
|
-
|
|
641
|
-
|
|
641
|
+
const isInternal = resolved2 === base || resolved2.startsWith(base + path4.sep);
|
|
642
|
+
if (!isInternal) {
|
|
643
|
+
if (!this.config.workspace.allowExternalWorkspaces) {
|
|
644
|
+
throw new Error(
|
|
645
|
+
`Workspace path "${input2}" is outside base directory "${this.config.workspace.baseDir}". Set allowExternalWorkspaces: true to allow this.`
|
|
646
|
+
);
|
|
647
|
+
}
|
|
648
|
+
if (!fs4.existsSync(resolved2)) {
|
|
649
|
+
throw new Error(
|
|
650
|
+
`Workspace path "${input2}" does not exist.`
|
|
651
|
+
);
|
|
652
|
+
}
|
|
642
653
|
return resolved2;
|
|
643
654
|
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
);
|
|
655
|
+
fs4.mkdirSync(resolved2, { recursive: true });
|
|
656
|
+
return resolved2;
|
|
647
657
|
}
|
|
648
658
|
const name = input2.replace(/[^a-zA-Z0-9_-]/g, "");
|
|
649
659
|
if (name !== input2) {
|
|
@@ -765,6 +775,104 @@ var init_read_text_file = __esm({
|
|
|
765
775
|
}
|
|
766
776
|
});
|
|
767
777
|
|
|
778
|
+
// src/core/events.ts
|
|
779
|
+
var Hook, BusEvent, SessionEv;
|
|
780
|
+
var init_events = __esm({
|
|
781
|
+
"src/core/events.ts"() {
|
|
782
|
+
"use strict";
|
|
783
|
+
Hook = {
|
|
784
|
+
// --- Message flow ---
|
|
785
|
+
/** Incoming message from any adapter — modifiable, can block. */
|
|
786
|
+
MESSAGE_INCOMING: "message:incoming",
|
|
787
|
+
/** Outgoing message before it reaches the adapter — modifiable, can block. */
|
|
788
|
+
MESSAGE_OUTGOING: "message:outgoing",
|
|
789
|
+
// --- Agent / turn lifecycle ---
|
|
790
|
+
/** Before a user prompt is sent to the agent — modifiable, can block. */
|
|
791
|
+
AGENT_BEFORE_PROMPT: "agent:beforePrompt",
|
|
792
|
+
/** Before an agent event is dispatched — modifiable, can block. */
|
|
793
|
+
AGENT_BEFORE_EVENT: "agent:beforeEvent",
|
|
794
|
+
/** After an agent event is dispatched — read-only, fire-and-forget. */
|
|
795
|
+
AGENT_AFTER_EVENT: "agent:afterEvent",
|
|
796
|
+
/** Before the current prompt is cancelled — modifiable, can block. */
|
|
797
|
+
AGENT_BEFORE_CANCEL: "agent:beforeCancel",
|
|
798
|
+
/** Before the agent is switched — modifiable, can block. */
|
|
799
|
+
AGENT_BEFORE_SWITCH: "agent:beforeSwitch",
|
|
800
|
+
/** After the agent has been switched — read-only, fire-and-forget. */
|
|
801
|
+
AGENT_AFTER_SWITCH: "agent:afterSwitch",
|
|
802
|
+
// --- Turn boundaries ---
|
|
803
|
+
/** Turn started — read-only, fire-and-forget. */
|
|
804
|
+
TURN_START: "turn:start",
|
|
805
|
+
/** Turn ended (always fires, even on error) — read-only, fire-and-forget. */
|
|
806
|
+
TURN_END: "turn:end",
|
|
807
|
+
// --- Session lifecycle ---
|
|
808
|
+
/** Before a new session is created — modifiable, can block. */
|
|
809
|
+
SESSION_BEFORE_CREATE: "session:beforeCreate",
|
|
810
|
+
/** After a session is destroyed — read-only, fire-and-forget. */
|
|
811
|
+
SESSION_AFTER_DESTROY: "session:afterDestroy",
|
|
812
|
+
// --- Permissions ---
|
|
813
|
+
/** Before a permission request is shown to the user — modifiable, can block. */
|
|
814
|
+
PERMISSION_BEFORE_REQUEST: "permission:beforeRequest",
|
|
815
|
+
/** After a permission request is resolved — read-only, fire-and-forget. */
|
|
816
|
+
PERMISSION_AFTER_RESOLVE: "permission:afterResolve",
|
|
817
|
+
// --- Config ---
|
|
818
|
+
/** Before config options change — modifiable, can block. */
|
|
819
|
+
CONFIG_BEFORE_CHANGE: "config:beforeChange",
|
|
820
|
+
// --- Filesystem (agent-level) ---
|
|
821
|
+
/** Before a file read operation — modifiable. */
|
|
822
|
+
FS_BEFORE_READ: "fs:beforeRead",
|
|
823
|
+
/** Before a file write operation — modifiable. */
|
|
824
|
+
FS_BEFORE_WRITE: "fs:beforeWrite",
|
|
825
|
+
// --- Terminal ---
|
|
826
|
+
/** Before a terminal session is created — modifiable, can block. */
|
|
827
|
+
TERMINAL_BEFORE_CREATE: "terminal:beforeCreate",
|
|
828
|
+
/** After a terminal session exits — read-only, fire-and-forget. */
|
|
829
|
+
TERMINAL_AFTER_EXIT: "terminal:afterExit"
|
|
830
|
+
};
|
|
831
|
+
BusEvent = {
|
|
832
|
+
// --- Session lifecycle ---
|
|
833
|
+
SESSION_CREATED: "session:created",
|
|
834
|
+
SESSION_UPDATED: "session:updated",
|
|
835
|
+
SESSION_DELETED: "session:deleted",
|
|
836
|
+
SESSION_ENDED: "session:ended",
|
|
837
|
+
SESSION_NAMED: "session:named",
|
|
838
|
+
SESSION_THREAD_READY: "session:threadReady",
|
|
839
|
+
SESSION_CONFIG_CHANGED: "session:configChanged",
|
|
840
|
+
SESSION_AGENT_SWITCH: "session:agentSwitch",
|
|
841
|
+
// --- Agent ---
|
|
842
|
+
AGENT_EVENT: "agent:event",
|
|
843
|
+
AGENT_PROMPT: "agent:prompt",
|
|
844
|
+
// --- Permissions ---
|
|
845
|
+
PERMISSION_REQUEST: "permission:request",
|
|
846
|
+
PERMISSION_RESOLVED: "permission:resolved",
|
|
847
|
+
// --- Message visibility ---
|
|
848
|
+
MESSAGE_QUEUED: "message:queued",
|
|
849
|
+
MESSAGE_PROCESSING: "message:processing",
|
|
850
|
+
// --- System lifecycle ---
|
|
851
|
+
KERNEL_BOOTED: "kernel:booted",
|
|
852
|
+
SYSTEM_READY: "system:ready",
|
|
853
|
+
SYSTEM_SHUTDOWN: "system:shutdown",
|
|
854
|
+
SYSTEM_COMMANDS_READY: "system:commands-ready",
|
|
855
|
+
// --- Plugin lifecycle ---
|
|
856
|
+
PLUGIN_LOADED: "plugin:loaded",
|
|
857
|
+
PLUGIN_FAILED: "plugin:failed",
|
|
858
|
+
PLUGIN_DISABLED: "plugin:disabled",
|
|
859
|
+
PLUGIN_UNLOADED: "plugin:unloaded",
|
|
860
|
+
// --- Usage ---
|
|
861
|
+
USAGE_RECORDED: "usage:recorded"
|
|
862
|
+
};
|
|
863
|
+
SessionEv = {
|
|
864
|
+
AGENT_EVENT: "agent_event",
|
|
865
|
+
PERMISSION_REQUEST: "permission_request",
|
|
866
|
+
SESSION_END: "session_end",
|
|
867
|
+
STATUS_CHANGE: "status_change",
|
|
868
|
+
NAMED: "named",
|
|
869
|
+
ERROR: "error",
|
|
870
|
+
PROMPT_COUNT_CHANGED: "prompt_count_changed",
|
|
871
|
+
TURN_STARTED: "turn_started"
|
|
872
|
+
};
|
|
873
|
+
}
|
|
874
|
+
});
|
|
875
|
+
|
|
768
876
|
// src/core/utils/bypass-detection.ts
|
|
769
877
|
function isPermissionBypass(value) {
|
|
770
878
|
const lower = value.toLowerCase();
|
|
@@ -3108,6 +3216,7 @@ var MAX_SSE_CONNECTIONS, SSEManager;
|
|
|
3108
3216
|
var init_sse_manager = __esm({
|
|
3109
3217
|
"src/plugins/api-server/sse-manager.ts"() {
|
|
3110
3218
|
"use strict";
|
|
3219
|
+
init_events();
|
|
3111
3220
|
MAX_SSE_CONNECTIONS = 50;
|
|
3112
3221
|
SSEManager = class {
|
|
3113
3222
|
constructor(eventBus, getSessionStats, startedAt) {
|
|
@@ -3122,14 +3231,14 @@ var init_sse_manager = __esm({
|
|
|
3122
3231
|
setup() {
|
|
3123
3232
|
if (!this.eventBus) return;
|
|
3124
3233
|
const events = [
|
|
3125
|
-
|
|
3126
|
-
|
|
3127
|
-
|
|
3128
|
-
|
|
3129
|
-
|
|
3130
|
-
|
|
3131
|
-
|
|
3132
|
-
|
|
3234
|
+
BusEvent.SESSION_CREATED,
|
|
3235
|
+
BusEvent.SESSION_UPDATED,
|
|
3236
|
+
BusEvent.SESSION_DELETED,
|
|
3237
|
+
BusEvent.AGENT_EVENT,
|
|
3238
|
+
BusEvent.PERMISSION_REQUEST,
|
|
3239
|
+
BusEvent.PERMISSION_RESOLVED,
|
|
3240
|
+
BusEvent.MESSAGE_QUEUED,
|
|
3241
|
+
BusEvent.MESSAGE_PROCESSING
|
|
3133
3242
|
];
|
|
3134
3243
|
for (const eventName of events) {
|
|
3135
3244
|
const handler = (data) => {
|
|
@@ -3192,12 +3301,12 @@ data: ${JSON.stringify(data)}
|
|
|
3192
3301
|
|
|
3193
3302
|
`;
|
|
3194
3303
|
const sessionEvents = [
|
|
3195
|
-
|
|
3196
|
-
|
|
3197
|
-
|
|
3198
|
-
|
|
3199
|
-
|
|
3200
|
-
|
|
3304
|
+
BusEvent.AGENT_EVENT,
|
|
3305
|
+
BusEvent.PERMISSION_REQUEST,
|
|
3306
|
+
BusEvent.PERMISSION_RESOLVED,
|
|
3307
|
+
BusEvent.SESSION_UPDATED,
|
|
3308
|
+
BusEvent.MESSAGE_QUEUED,
|
|
3309
|
+
BusEvent.MESSAGE_PROCESSING
|
|
3201
3310
|
];
|
|
3202
3311
|
for (const res of this.sseConnections) {
|
|
3203
3312
|
const filter = res.sessionFilter;
|
|
@@ -7318,7 +7427,6 @@ var menu_exports = {};
|
|
|
7318
7427
|
__export(menu_exports, {
|
|
7319
7428
|
buildMenuKeyboard: () => buildMenuKeyboard,
|
|
7320
7429
|
buildSkillMessages: () => buildSkillMessages,
|
|
7321
|
-
handleClear: () => handleClear,
|
|
7322
7430
|
handleHelp: () => handleHelp,
|
|
7323
7431
|
handleMenu: () => handleMenu
|
|
7324
7432
|
});
|
|
@@ -7379,31 +7487,11 @@ Each session gets its own topic \u2014 chat there to work with the agent.
|
|
|
7379
7487
|
/bypass_permissions \u2014 Toggle bypass permissions
|
|
7380
7488
|
/handoff \u2014 Continue session in terminal
|
|
7381
7489
|
/archive \u2014 Archive session topic
|
|
7382
|
-
/clear \u2014 Clear assistant history
|
|
7383
7490
|
|
|
7384
7491
|
\u{1F4AC} Need help? Just ask me in this topic!`,
|
|
7385
7492
|
{ parse_mode: "HTML" }
|
|
7386
7493
|
);
|
|
7387
7494
|
}
|
|
7388
|
-
async function handleClear(ctx, assistant) {
|
|
7389
|
-
if (!assistant) {
|
|
7390
|
-
await ctx.reply("\u26A0\uFE0F Assistant is not available.", { parse_mode: "HTML" });
|
|
7391
|
-
return;
|
|
7392
|
-
}
|
|
7393
|
-
const threadId = ctx.message?.message_thread_id;
|
|
7394
|
-
if (threadId !== assistant.topicId) {
|
|
7395
|
-
await ctx.reply("\u2139\uFE0F /clear only works in the Assistant topic.", { parse_mode: "HTML" });
|
|
7396
|
-
return;
|
|
7397
|
-
}
|
|
7398
|
-
await ctx.reply("\u{1F504} Clearing assistant history...", { parse_mode: "HTML" });
|
|
7399
|
-
try {
|
|
7400
|
-
await assistant.respawn();
|
|
7401
|
-
await ctx.reply("\u2705 Assistant history cleared.", { parse_mode: "HTML" });
|
|
7402
|
-
} catch (err) {
|
|
7403
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
7404
|
-
await ctx.reply(`\u274C Failed to clear: <code>${message}</code>`, { parse_mode: "HTML" });
|
|
7405
|
-
}
|
|
7406
|
-
}
|
|
7407
7495
|
function buildSkillMessages(commands) {
|
|
7408
7496
|
const sorted = [...commands].sort((a, b) => a.name.localeCompare(b.name));
|
|
7409
7497
|
const header2 = "\u{1F6E0} <b>Available Skills</b>\n";
|
|
@@ -8096,7 +8184,10 @@ function setupAllCallbacks(bot, core, chatId, systemTopicIds, getAssistantSessio
|
|
|
8096
8184
|
const menuRegistry = core.lifecycleManager?.serviceRegistry?.get("menu-registry");
|
|
8097
8185
|
if (!menuRegistry) return;
|
|
8098
8186
|
const item = menuRegistry.getItem(itemId);
|
|
8099
|
-
if (!item)
|
|
8187
|
+
if (!item) {
|
|
8188
|
+
log30.warn({ itemId }, "Menu item not found in registry");
|
|
8189
|
+
return;
|
|
8190
|
+
}
|
|
8100
8191
|
const topicId = ctx.callbackQuery.message?.message_thread_id;
|
|
8101
8192
|
const registry = core.lifecycleManager?.serviceRegistry?.get("command-registry");
|
|
8102
8193
|
switch (item.action.type) {
|
|
@@ -8180,7 +8271,7 @@ ${lines}`, { parse_mode: "HTML" }).catch(() => {
|
|
|
8180
8271
|
}
|
|
8181
8272
|
});
|
|
8182
8273
|
}
|
|
8183
|
-
var STATIC_COMMANDS;
|
|
8274
|
+
var log30, STATIC_COMMANDS;
|
|
8184
8275
|
var init_commands = __esm({
|
|
8185
8276
|
"src/plugins/telegram/commands/index.ts"() {
|
|
8186
8277
|
"use strict";
|
|
@@ -8193,6 +8284,7 @@ var init_commands = __esm({
|
|
|
8193
8284
|
init_tunnel2();
|
|
8194
8285
|
init_switch();
|
|
8195
8286
|
init_telegram_overrides();
|
|
8287
|
+
init_log();
|
|
8196
8288
|
init_menu();
|
|
8197
8289
|
init_menu();
|
|
8198
8290
|
init_telegram_overrides();
|
|
@@ -8204,6 +8296,7 @@ var init_commands = __esm({
|
|
|
8204
8296
|
init_settings();
|
|
8205
8297
|
init_doctor2();
|
|
8206
8298
|
init_resume();
|
|
8299
|
+
log30 = createChildLogger({ module: "telegram-menu-callbacks" });
|
|
8207
8300
|
STATIC_COMMANDS = [
|
|
8208
8301
|
{ command: "new", description: "Create new session" },
|
|
8209
8302
|
{ command: "newchat", description: "New chat, same agent & workspace" },
|
|
@@ -8216,7 +8309,6 @@ var init_commands = __esm({
|
|
|
8216
8309
|
{ command: "menu", description: "Show menu" },
|
|
8217
8310
|
{ command: "integrate", description: "Manage agent integrations" },
|
|
8218
8311
|
{ command: "handoff", description: "Continue this session in your terminal" },
|
|
8219
|
-
{ command: "clear", description: "Clear assistant history" },
|
|
8220
8312
|
{ command: "restart", description: "Restart OpenACP" },
|
|
8221
8313
|
{ command: "update", description: "Update to latest version and restart" },
|
|
8222
8314
|
{ command: "doctor", description: "Run system diagnostics" },
|
|
@@ -8239,14 +8331,14 @@ var init_commands = __esm({
|
|
|
8239
8331
|
// src/plugins/telegram/permissions.ts
|
|
8240
8332
|
import { InlineKeyboard as InlineKeyboard11 } from "grammy";
|
|
8241
8333
|
import { nanoid as nanoid4 } from "nanoid";
|
|
8242
|
-
var
|
|
8334
|
+
var log31, PermissionHandler;
|
|
8243
8335
|
var init_permissions = __esm({
|
|
8244
8336
|
"src/plugins/telegram/permissions.ts"() {
|
|
8245
8337
|
"use strict";
|
|
8246
8338
|
init_formatting();
|
|
8247
8339
|
init_topics();
|
|
8248
8340
|
init_log();
|
|
8249
|
-
|
|
8341
|
+
log31 = createChildLogger({ module: "telegram-permissions" });
|
|
8250
8342
|
PermissionHandler = class {
|
|
8251
8343
|
constructor(bot, chatId, getSession, sendNotification) {
|
|
8252
8344
|
this.bot = bot;
|
|
@@ -8290,11 +8382,11 @@ ${escapeHtml(request.description)}`,
|
|
|
8290
8382
|
});
|
|
8291
8383
|
}
|
|
8292
8384
|
setupCallbackHandler() {
|
|
8293
|
-
this.bot.on("callback_query:data", async (ctx) => {
|
|
8385
|
+
this.bot.on("callback_query:data", async (ctx, next) => {
|
|
8294
8386
|
const data = ctx.callbackQuery.data;
|
|
8295
|
-
if (!data.startsWith("p:")) return;
|
|
8387
|
+
if (!data.startsWith("p:")) return next();
|
|
8296
8388
|
const parts = data.split(":");
|
|
8297
|
-
if (parts.length < 3) return;
|
|
8389
|
+
if (parts.length < 3) return next();
|
|
8298
8390
|
const [, callbackKey, optionId] = parts;
|
|
8299
8391
|
const pending = this.pending.get(callbackKey);
|
|
8300
8392
|
if (!pending) {
|
|
@@ -8306,7 +8398,7 @@ ${escapeHtml(request.description)}`,
|
|
|
8306
8398
|
}
|
|
8307
8399
|
const session = this.getSession(pending.sessionId);
|
|
8308
8400
|
const isAllow = pending.options.find((o) => o.id === optionId)?.isAllow ?? false;
|
|
8309
|
-
|
|
8401
|
+
log31.info({ requestId: pending.requestId, optionId, isAllow }, "Permission responded");
|
|
8310
8402
|
if (session?.permissionGate.requestId === pending.requestId) {
|
|
8311
8403
|
session.permissionGate.resolve(optionId);
|
|
8312
8404
|
}
|
|
@@ -8326,7 +8418,7 @@ ${escapeHtml(request.description)}`,
|
|
|
8326
8418
|
});
|
|
8327
8419
|
|
|
8328
8420
|
// src/plugins/telegram/activity.ts
|
|
8329
|
-
var
|
|
8421
|
+
var log32, THINKING_REFRESH_MS, THINKING_MAX_MS, ThinkingIndicator, ToolCard, ActivityTracker2;
|
|
8330
8422
|
var init_activity = __esm({
|
|
8331
8423
|
"src/plugins/telegram/activity.ts"() {
|
|
8332
8424
|
"use strict";
|
|
@@ -8336,7 +8428,7 @@ var init_activity = __esm({
|
|
|
8336
8428
|
init_stream_accumulator();
|
|
8337
8429
|
init_stream_accumulator();
|
|
8338
8430
|
init_display_spec_builder();
|
|
8339
|
-
|
|
8431
|
+
log32 = createChildLogger({ module: "telegram:activity" });
|
|
8340
8432
|
THINKING_REFRESH_MS = 15e3;
|
|
8341
8433
|
THINKING_MAX_MS = 3 * 60 * 1e3;
|
|
8342
8434
|
ThinkingIndicator = class {
|
|
@@ -8377,7 +8469,7 @@ var init_activity = __esm({
|
|
|
8377
8469
|
}
|
|
8378
8470
|
}
|
|
8379
8471
|
} catch (err) {
|
|
8380
|
-
|
|
8472
|
+
log32.warn({ err }, "ThinkingIndicator.show() failed");
|
|
8381
8473
|
} finally {
|
|
8382
8474
|
this.sending = false;
|
|
8383
8475
|
}
|
|
@@ -8545,7 +8637,7 @@ var init_activity = __esm({
|
|
|
8545
8637
|
this.tracer?.log("telegram", { action: "telegram:delete:overflow", sessionId: this.sessionId, msgId: staleId });
|
|
8546
8638
|
}
|
|
8547
8639
|
} catch (err) {
|
|
8548
|
-
|
|
8640
|
+
log32.warn({ err }, "[ToolCard] send/edit failed");
|
|
8549
8641
|
}
|
|
8550
8642
|
}
|
|
8551
8643
|
};
|
|
@@ -8649,7 +8741,7 @@ var init_activity = __esm({
|
|
|
8649
8741
|
const entry = this.toolStateMap.merge(id, status, rawInput, content, viewerLinks, diffStats);
|
|
8650
8742
|
if (!existed || !entry) return;
|
|
8651
8743
|
if (viewerLinks || entry.viewerLinks) {
|
|
8652
|
-
|
|
8744
|
+
log32.debug({ toolId: id, status, hasIncomingLinks: !!viewerLinks, hasEntryLinks: !!entry.viewerLinks, entryLinks: entry.viewerLinks }, "toolUpdate: viewer links trace");
|
|
8653
8745
|
}
|
|
8654
8746
|
const spec = this.specBuilder.buildToolSpec(entry, this._outputMode, this.sessionContext);
|
|
8655
8747
|
this.toolCard.updateFromSpec(spec);
|
|
@@ -8968,13 +9060,13 @@ var init_draft_manager = __esm({
|
|
|
8968
9060
|
});
|
|
8969
9061
|
|
|
8970
9062
|
// src/plugins/telegram/skill-command-manager.ts
|
|
8971
|
-
var
|
|
9063
|
+
var log33, SkillCommandManager;
|
|
8972
9064
|
var init_skill_command_manager = __esm({
|
|
8973
9065
|
"src/plugins/telegram/skill-command-manager.ts"() {
|
|
8974
9066
|
"use strict";
|
|
8975
9067
|
init_commands();
|
|
8976
9068
|
init_log();
|
|
8977
|
-
|
|
9069
|
+
log33 = createChildLogger({ module: "skill-commands" });
|
|
8978
9070
|
SkillCommandManager = class {
|
|
8979
9071
|
// sessionId → pinned msgId
|
|
8980
9072
|
constructor(bot, chatId, sendQueue, sessionManager) {
|
|
@@ -9040,7 +9132,7 @@ var init_skill_command_manager = __esm({
|
|
|
9040
9132
|
disable_notification: true
|
|
9041
9133
|
});
|
|
9042
9134
|
} catch (err) {
|
|
9043
|
-
|
|
9135
|
+
log33.error({ err, sessionId }, "Failed to send skill commands");
|
|
9044
9136
|
}
|
|
9045
9137
|
}
|
|
9046
9138
|
async cleanup(sessionId) {
|
|
@@ -9219,7 +9311,7 @@ async function validateBotAdmin(token, chatId) {
|
|
|
9219
9311
|
};
|
|
9220
9312
|
}
|
|
9221
9313
|
const { status } = data.result;
|
|
9222
|
-
|
|
9314
|
+
log34.info(
|
|
9223
9315
|
{ status, can_manage_topics: data.result.can_manage_topics, raw_result: data.result },
|
|
9224
9316
|
"validateBotAdmin: getChatMember raw result"
|
|
9225
9317
|
);
|
|
@@ -9239,7 +9331,7 @@ async function validateBotAdmin(token, chatId) {
|
|
|
9239
9331
|
}
|
|
9240
9332
|
async function checkTopicsPrerequisites(token, chatId) {
|
|
9241
9333
|
const issues = [];
|
|
9242
|
-
|
|
9334
|
+
log34.info({ chatId }, "checkTopicsPrerequisites: starting checks");
|
|
9243
9335
|
try {
|
|
9244
9336
|
const res = await fetch(`https://api.telegram.org/bot${token}/getChat`, {
|
|
9245
9337
|
method: "POST",
|
|
@@ -9247,7 +9339,7 @@ async function checkTopicsPrerequisites(token, chatId) {
|
|
|
9247
9339
|
body: JSON.stringify({ chat_id: chatId })
|
|
9248
9340
|
});
|
|
9249
9341
|
const data = await res.json();
|
|
9250
|
-
|
|
9342
|
+
log34.info(
|
|
9251
9343
|
{ chatId, apiOk: data.ok, is_forum: data.result?.is_forum, type: data.result?.type, title: data.result?.title },
|
|
9252
9344
|
"checkTopicsPrerequisites: getChat result"
|
|
9253
9345
|
);
|
|
@@ -9257,11 +9349,11 @@ async function checkTopicsPrerequisites(token, chatId) {
|
|
|
9257
9349
|
);
|
|
9258
9350
|
}
|
|
9259
9351
|
} catch (err) {
|
|
9260
|
-
|
|
9352
|
+
log34.warn({ err, chatId }, "checkTopicsPrerequisites: getChat failed (network error)");
|
|
9261
9353
|
issues.push("\u274C Could not check if Topics are enabled (network error).");
|
|
9262
9354
|
}
|
|
9263
9355
|
const adminResult = await validateBotAdmin(token, chatId);
|
|
9264
|
-
|
|
9356
|
+
log34.info(
|
|
9265
9357
|
{ chatId, adminOk: adminResult.ok, canManageTopics: adminResult.ok ? adminResult.canManageTopics : void 0, error: !adminResult.ok ? adminResult.error : void 0 },
|
|
9266
9358
|
"checkTopicsPrerequisites: validateBotAdmin result"
|
|
9267
9359
|
);
|
|
@@ -9275,16 +9367,16 @@ async function checkTopicsPrerequisites(token, chatId) {
|
|
|
9275
9367
|
'\u274C Bot cannot manage topics.\n\u2192 In Admin settings, enable the "Manage Topics" permission'
|
|
9276
9368
|
);
|
|
9277
9369
|
}
|
|
9278
|
-
|
|
9370
|
+
log34.info({ chatId, issueCount: issues.length, ok: issues.length === 0 }, "checkTopicsPrerequisites: result");
|
|
9279
9371
|
if (issues.length > 0) return { ok: false, issues };
|
|
9280
9372
|
return { ok: true };
|
|
9281
9373
|
}
|
|
9282
|
-
var
|
|
9374
|
+
var log34;
|
|
9283
9375
|
var init_validators = __esm({
|
|
9284
9376
|
"src/plugins/telegram/validators.ts"() {
|
|
9285
9377
|
"use strict";
|
|
9286
9378
|
init_log();
|
|
9287
|
-
|
|
9379
|
+
log34 = createChildLogger({ module: "telegram-validators" });
|
|
9288
9380
|
}
|
|
9289
9381
|
});
|
|
9290
9382
|
|
|
@@ -9303,10 +9395,11 @@ function patchedFetch(input2, init) {
|
|
|
9303
9395
|
}
|
|
9304
9396
|
return fetch(input2, init);
|
|
9305
9397
|
}
|
|
9306
|
-
var
|
|
9398
|
+
var log35, TelegramAdapter;
|
|
9307
9399
|
var init_adapter = __esm({
|
|
9308
9400
|
"src/plugins/telegram/adapter.ts"() {
|
|
9309
9401
|
"use strict";
|
|
9402
|
+
init_events();
|
|
9310
9403
|
init_log();
|
|
9311
9404
|
init_topics();
|
|
9312
9405
|
init_commands();
|
|
@@ -9322,7 +9415,7 @@ var init_adapter = __esm({
|
|
|
9322
9415
|
init_messaging_adapter();
|
|
9323
9416
|
init_renderer2();
|
|
9324
9417
|
init_output_mode_resolver();
|
|
9325
|
-
|
|
9418
|
+
log35 = createChildLogger({ module: "telegram" });
|
|
9326
9419
|
TelegramAdapter = class extends MessagingAdapter {
|
|
9327
9420
|
name = "telegram";
|
|
9328
9421
|
renderer = new TelegramRenderer();
|
|
@@ -9449,7 +9542,7 @@ var init_adapter = __esm({
|
|
|
9449
9542
|
);
|
|
9450
9543
|
this.bot.catch((err) => {
|
|
9451
9544
|
const rootCause = err.error instanceof Error ? err.error : err;
|
|
9452
|
-
|
|
9545
|
+
log35.error({ err: rootCause }, "Telegram bot error");
|
|
9453
9546
|
});
|
|
9454
9547
|
this.bot.api.config.use(async (prev, method, payload, signal) => {
|
|
9455
9548
|
const maxRetries = 3;
|
|
@@ -9467,7 +9560,7 @@ var init_adapter = __esm({
|
|
|
9467
9560
|
if (rateLimitedMethods.includes(method)) {
|
|
9468
9561
|
this.sendQueue.onRateLimited();
|
|
9469
9562
|
}
|
|
9470
|
-
|
|
9563
|
+
log35.warn(
|
|
9471
9564
|
{ method, retryAfter, attempt: attempt + 1 },
|
|
9472
9565
|
"Rate limited by Telegram, retrying"
|
|
9473
9566
|
);
|
|
@@ -9655,9 +9748,9 @@ ${p}` : p;
|
|
|
9655
9748
|
this.setupRoutes();
|
|
9656
9749
|
this.bot.start({
|
|
9657
9750
|
allowed_updates: ["message", "callback_query"],
|
|
9658
|
-
onStart: () =>
|
|
9751
|
+
onStart: () => log35.info({ chatId: this.telegramConfig.chatId }, "Telegram bot started")
|
|
9659
9752
|
});
|
|
9660
|
-
|
|
9753
|
+
log35.info(
|
|
9661
9754
|
{
|
|
9662
9755
|
chatId: this.telegramConfig.chatId,
|
|
9663
9756
|
notificationTopicId: this.telegramConfig.notificationTopicId,
|
|
@@ -9671,12 +9764,12 @@ ${p}` : p;
|
|
|
9671
9764
|
this.telegramConfig.chatId
|
|
9672
9765
|
);
|
|
9673
9766
|
if (prereqResult.ok) {
|
|
9674
|
-
|
|
9767
|
+
log35.info("Telegram adapter: prerequisites OK, initializing topic-dependent features");
|
|
9675
9768
|
await this.initTopicDependentFeatures();
|
|
9676
9769
|
} else {
|
|
9677
|
-
|
|
9770
|
+
log35.warn({ issues: prereqResult.issues }, "Telegram adapter: prerequisites NOT met, starting watcher");
|
|
9678
9771
|
for (const issue of prereqResult.issues) {
|
|
9679
|
-
|
|
9772
|
+
log35.warn({ issue }, "Telegram prerequisite not met");
|
|
9680
9773
|
}
|
|
9681
9774
|
this.startPrerequisiteWatcher(prereqResult.issues);
|
|
9682
9775
|
}
|
|
@@ -9692,7 +9785,7 @@ ${p}` : p;
|
|
|
9692
9785
|
} catch (err) {
|
|
9693
9786
|
if (attempt === maxRetries) throw err;
|
|
9694
9787
|
const delay = baseDelayMs * Math.pow(2, attempt - 1);
|
|
9695
|
-
|
|
9788
|
+
log35.warn(
|
|
9696
9789
|
{ err, attempt, maxRetries, delayMs: delay, operation: label },
|
|
9697
9790
|
`${label} failed, retrying in ${delay}ms`
|
|
9698
9791
|
);
|
|
@@ -9712,12 +9805,12 @@ ${p}` : p;
|
|
|
9712
9805
|
}),
|
|
9713
9806
|
"setMyCommands"
|
|
9714
9807
|
).catch((err) => {
|
|
9715
|
-
|
|
9808
|
+
log35.warn({ err }, "Failed to register Telegram commands after retries (non-critical)");
|
|
9716
9809
|
});
|
|
9717
9810
|
}
|
|
9718
9811
|
async initTopicDependentFeatures() {
|
|
9719
9812
|
if (this._topicsInitialized) return;
|
|
9720
|
-
|
|
9813
|
+
log35.info(
|
|
9721
9814
|
{ notificationTopicId: this.telegramConfig.notificationTopicId, assistantTopicId: this.telegramConfig.assistantTopicId },
|
|
9722
9815
|
"initTopicDependentFeatures: starting (existing IDs in config)"
|
|
9723
9816
|
);
|
|
@@ -9742,7 +9835,7 @@ ${p}` : p;
|
|
|
9742
9835
|
this.assistantTopicId = topics.assistantTopicId;
|
|
9743
9836
|
this._systemTopicIds.notificationTopicId = topics.notificationTopicId;
|
|
9744
9837
|
this._systemTopicIds.assistantTopicId = topics.assistantTopicId;
|
|
9745
|
-
|
|
9838
|
+
log35.info(
|
|
9746
9839
|
{ notificationTopicId: this.notificationTopicId, assistantTopicId: this.assistantTopicId },
|
|
9747
9840
|
"initTopicDependentFeatures: topics ready"
|
|
9748
9841
|
);
|
|
@@ -9773,16 +9866,16 @@ ${p}` : p;
|
|
|
9773
9866
|
).then((msg) => {
|
|
9774
9867
|
if (msg) this.storeControlMsgId(sessionId, msg.message_id);
|
|
9775
9868
|
}).catch((err) => {
|
|
9776
|
-
|
|
9869
|
+
log35.warn({ err, sessionId }, "Failed to send initial messages for new session");
|
|
9777
9870
|
});
|
|
9778
9871
|
};
|
|
9779
|
-
this.core.eventBus.on(
|
|
9872
|
+
this.core.eventBus.on(BusEvent.SESSION_THREAD_READY, this._threadReadyHandler);
|
|
9780
9873
|
this._configChangedHandler = ({ sessionId }) => {
|
|
9781
9874
|
this.updateControlMessage(sessionId).catch(() => {
|
|
9782
9875
|
});
|
|
9783
9876
|
};
|
|
9784
9877
|
this.core.eventBus.on("session:configChanged", this._configChangedHandler);
|
|
9785
|
-
|
|
9878
|
+
log35.info({ assistantTopicId: this.assistantTopicId }, "initTopicDependentFeatures: sending welcome message");
|
|
9786
9879
|
try {
|
|
9787
9880
|
const config = this.core.configManager.get();
|
|
9788
9881
|
const agents = this.core.agentManager.getAvailableAgents();
|
|
@@ -9804,17 +9897,17 @@ ${p}` : p;
|
|
|
9804
9897
|
this.core.lifecycleManager?.serviceRegistry?.get("menu-registry")
|
|
9805
9898
|
)
|
|
9806
9899
|
});
|
|
9807
|
-
|
|
9900
|
+
log35.info("initTopicDependentFeatures: welcome message sent");
|
|
9808
9901
|
} catch (err) {
|
|
9809
|
-
|
|
9902
|
+
log35.warn({ err }, "Failed to send welcome message");
|
|
9810
9903
|
}
|
|
9811
9904
|
try {
|
|
9812
|
-
await this.core.assistantManager.
|
|
9905
|
+
await this.core.assistantManager.getOrSpawn("telegram", String(this.assistantTopicId));
|
|
9813
9906
|
} catch (err) {
|
|
9814
|
-
|
|
9907
|
+
log35.error({ err }, "Failed to spawn assistant");
|
|
9815
9908
|
}
|
|
9816
9909
|
this._topicsInitialized = true;
|
|
9817
|
-
|
|
9910
|
+
log35.info("Telegram adapter fully initialized");
|
|
9818
9911
|
}
|
|
9819
9912
|
startPrerequisiteWatcher(issues) {
|
|
9820
9913
|
const setupMessage = `\u26A0\uFE0F <b>OpenACP needs setup before it can start.</b>
|
|
@@ -9825,7 +9918,7 @@ OpenACP will automatically retry until this is resolved.`;
|
|
|
9825
9918
|
this.bot.api.sendMessage(this.telegramConfig.chatId, setupMessage, {
|
|
9826
9919
|
parse_mode: "HTML"
|
|
9827
9920
|
}).catch((err) => {
|
|
9828
|
-
|
|
9921
|
+
log35.warn({ err }, "Failed to send setup guidance to General topic");
|
|
9829
9922
|
});
|
|
9830
9923
|
const schedule = [5e3, 1e4, 3e4];
|
|
9831
9924
|
let attempt = 1;
|
|
@@ -9838,7 +9931,7 @@ OpenACP will automatically retry until this is resolved.`;
|
|
|
9838
9931
|
);
|
|
9839
9932
|
if (result.ok) {
|
|
9840
9933
|
this._prerequisiteWatcher = null;
|
|
9841
|
-
|
|
9934
|
+
log35.info("Prerequisites met \u2014 completing Telegram adapter initialization");
|
|
9842
9935
|
try {
|
|
9843
9936
|
await this.initTopicDependentFeatures();
|
|
9844
9937
|
await this.bot.api.sendMessage(
|
|
@@ -9847,11 +9940,11 @@ OpenACP will automatically retry until this is resolved.`;
|
|
|
9847
9940
|
{ parse_mode: "HTML" }
|
|
9848
9941
|
);
|
|
9849
9942
|
} catch (err) {
|
|
9850
|
-
|
|
9943
|
+
log35.error({ err }, "Failed to complete initialization after prerequisites met");
|
|
9851
9944
|
}
|
|
9852
9945
|
return;
|
|
9853
9946
|
}
|
|
9854
|
-
|
|
9947
|
+
log35.debug({ issues: result.issues }, "Prerequisites not yet met, retrying");
|
|
9855
9948
|
const delay = schedule[Math.min(attempt, schedule.length - 1)];
|
|
9856
9949
|
attempt++;
|
|
9857
9950
|
this._prerequisiteWatcher = setTimeout(retry, delay);
|
|
@@ -9868,7 +9961,7 @@ OpenACP will automatically retry until this is resolved.`;
|
|
|
9868
9961
|
}
|
|
9869
9962
|
this.sessionTrackers.clear();
|
|
9870
9963
|
if (this._threadReadyHandler) {
|
|
9871
|
-
this.core.eventBus.off(
|
|
9964
|
+
this.core.eventBus.off(BusEvent.SESSION_THREAD_READY, this._threadReadyHandler);
|
|
9872
9965
|
this._threadReadyHandler = void 0;
|
|
9873
9966
|
}
|
|
9874
9967
|
if (this._configChangedHandler) {
|
|
@@ -9877,7 +9970,7 @@ OpenACP will automatically retry until this is resolved.`;
|
|
|
9877
9970
|
}
|
|
9878
9971
|
this.sendQueue.clear();
|
|
9879
9972
|
await this.bot.stop();
|
|
9880
|
-
|
|
9973
|
+
log35.info("Telegram bot stopped");
|
|
9881
9974
|
}
|
|
9882
9975
|
// --- CommandRegistry response rendering ---
|
|
9883
9976
|
async renderCommandResponse(response, chatId, topicId) {
|
|
@@ -9962,6 +10055,13 @@ ${lines.join("\n")}`;
|
|
|
9962
10055
|
}
|
|
9963
10056
|
setupRoutes() {
|
|
9964
10057
|
this.bot.on("message:text", async (ctx) => {
|
|
10058
|
+
if (!this._topicsInitialized) {
|
|
10059
|
+
await ctx.reply(
|
|
10060
|
+
"\u23F3 OpenACP is still setting up. Check the General topic for instructions."
|
|
10061
|
+
).catch(() => {
|
|
10062
|
+
});
|
|
10063
|
+
return;
|
|
10064
|
+
}
|
|
9965
10065
|
const threadId = ctx.message.message_thread_id;
|
|
9966
10066
|
const text3 = ctx.message.text;
|
|
9967
10067
|
if (!threadId) {
|
|
@@ -9994,7 +10094,7 @@ ${lines.join("\n")}`;
|
|
|
9994
10094
|
threadId: String(threadId),
|
|
9995
10095
|
userId: String(ctx.from.id),
|
|
9996
10096
|
text: forwardText
|
|
9997
|
-
}).catch((err) =>
|
|
10097
|
+
}).catch((err) => log35.error({ err }, "handleMessage error"));
|
|
9998
10098
|
});
|
|
9999
10099
|
this.bot.on("message:photo", async (ctx) => {
|
|
10000
10100
|
const threadId = ctx.message.message_thread_id;
|
|
@@ -10083,7 +10183,7 @@ ${lines.join("\n")}`;
|
|
|
10083
10183
|
if (session.archiving) return;
|
|
10084
10184
|
const threadId = Number(session.threadId);
|
|
10085
10185
|
if (!threadId || isNaN(threadId)) {
|
|
10086
|
-
|
|
10186
|
+
log35.warn(
|
|
10087
10187
|
{ sessionId, threadId: session.threadId },
|
|
10088
10188
|
"Session has no valid threadId, skipping message"
|
|
10089
10189
|
);
|
|
@@ -10099,7 +10199,7 @@ ${lines.join("\n")}`;
|
|
|
10099
10199
|
this._sessionThreadIds.delete(sessionId);
|
|
10100
10200
|
}
|
|
10101
10201
|
}).catch((err) => {
|
|
10102
|
-
|
|
10202
|
+
log35.warn({ err, sessionId }, "Dispatch queue error");
|
|
10103
10203
|
});
|
|
10104
10204
|
this._dispatchQueues.set(sessionId, next);
|
|
10105
10205
|
await next;
|
|
@@ -10221,7 +10321,7 @@ ${lines.join("\n")}`;
|
|
|
10221
10321
|
);
|
|
10222
10322
|
usageMsgId = result?.message_id;
|
|
10223
10323
|
} catch (err) {
|
|
10224
|
-
|
|
10324
|
+
log35.warn({ err, sessionId }, "Failed to send usage message");
|
|
10225
10325
|
}
|
|
10226
10326
|
if (this.notificationTopicId && sessionId !== this.core.assistantManager?.get("telegram")?.id) {
|
|
10227
10327
|
const sess = this.core.sessionManager.getSession(sessionId);
|
|
@@ -10249,7 +10349,7 @@ Task completed.
|
|
|
10249
10349
|
if (!content.attachment) return;
|
|
10250
10350
|
const { attachment } = content;
|
|
10251
10351
|
if (attachment.size > 50 * 1024 * 1024) {
|
|
10252
|
-
|
|
10352
|
+
log35.warn(
|
|
10253
10353
|
{
|
|
10254
10354
|
sessionId,
|
|
10255
10355
|
fileName: attachment.fileName,
|
|
@@ -10293,7 +10393,7 @@ Task completed.
|
|
|
10293
10393
|
);
|
|
10294
10394
|
}
|
|
10295
10395
|
} catch (err) {
|
|
10296
|
-
|
|
10396
|
+
log35.error(
|
|
10297
10397
|
{ err, sessionId, fileName: attachment.fileName },
|
|
10298
10398
|
"Failed to send attachment"
|
|
10299
10399
|
);
|
|
@@ -10392,7 +10492,7 @@ Task completed.
|
|
|
10392
10492
|
}
|
|
10393
10493
|
async sendPermissionRequest(sessionId, request) {
|
|
10394
10494
|
this.getTracer(sessionId)?.log("telegram", { action: "permission:send", sessionId, requestId: request.id, description: request.description });
|
|
10395
|
-
|
|
10495
|
+
log35.info({ sessionId, requestId: request.id }, "Permission request sent");
|
|
10396
10496
|
const session = this.core.sessionManager.getSession(sessionId);
|
|
10397
10497
|
if (!session) return;
|
|
10398
10498
|
await this.sendQueue.enqueue(
|
|
@@ -10402,7 +10502,7 @@ Task completed.
|
|
|
10402
10502
|
async sendNotification(notification) {
|
|
10403
10503
|
this.getTracer(notification.sessionId)?.log("telegram", { action: "notification:send", sessionId: notification.sessionId, type: notification.type });
|
|
10404
10504
|
if (notification.sessionId === this.core.assistantManager?.get("telegram")?.id) return;
|
|
10405
|
-
|
|
10505
|
+
log35.info(
|
|
10406
10506
|
{ sessionId: notification.sessionId, type: notification.type },
|
|
10407
10507
|
"Notification sent"
|
|
10408
10508
|
);
|
|
@@ -10441,7 +10541,7 @@ Task completed.
|
|
|
10441
10541
|
}
|
|
10442
10542
|
async createSessionThread(sessionId, name) {
|
|
10443
10543
|
this.getTracer(sessionId)?.log("telegram", { action: "thread:create", sessionId, name });
|
|
10444
|
-
|
|
10544
|
+
log35.info({ sessionId, name }, "Session topic created");
|
|
10445
10545
|
return String(
|
|
10446
10546
|
await createSessionTopic(this.bot, this.telegramConfig.chatId, name)
|
|
10447
10547
|
);
|
|
@@ -10452,7 +10552,7 @@ Task completed.
|
|
|
10452
10552
|
if (!session) return;
|
|
10453
10553
|
const threadId = Number(session.threadId);
|
|
10454
10554
|
if (!threadId) {
|
|
10455
|
-
|
|
10555
|
+
log35.debug({ sessionId, newName }, "Cannot rename thread \u2014 threadId not set yet");
|
|
10456
10556
|
return;
|
|
10457
10557
|
}
|
|
10458
10558
|
await renameSessionTopic(
|
|
@@ -10471,7 +10571,7 @@ Task completed.
|
|
|
10471
10571
|
try {
|
|
10472
10572
|
await this.bot.api.deleteForumTopic(this.telegramConfig.chatId, topicId);
|
|
10473
10573
|
} catch (err) {
|
|
10474
|
-
|
|
10574
|
+
log35.warn(
|
|
10475
10575
|
{ err, sessionId, topicId },
|
|
10476
10576
|
"Failed to delete forum topic (may already be deleted)"
|
|
10477
10577
|
);
|
|
@@ -10515,7 +10615,7 @@ Task completed.
|
|
|
10515
10615
|
const buffer = Buffer.from(await response.arrayBuffer());
|
|
10516
10616
|
return { buffer, filePath: file.file_path };
|
|
10517
10617
|
} catch (err) {
|
|
10518
|
-
|
|
10618
|
+
log35.error({ err }, "Failed to download file from Telegram");
|
|
10519
10619
|
return null;
|
|
10520
10620
|
}
|
|
10521
10621
|
}
|
|
@@ -10536,7 +10636,7 @@ Task completed.
|
|
|
10536
10636
|
try {
|
|
10537
10637
|
buffer = await this.fileService.convertOggToWav(buffer);
|
|
10538
10638
|
} catch (err) {
|
|
10539
|
-
|
|
10639
|
+
log35.warn({ err }, "OGG\u2192WAV conversion failed, saving original OGG");
|
|
10540
10640
|
fileName = "voice.ogg";
|
|
10541
10641
|
mimeType = "audio/ogg";
|
|
10542
10642
|
originalFilePath = void 0;
|
|
@@ -10568,7 +10668,7 @@ Task completed.
|
|
|
10568
10668
|
userId: String(userId),
|
|
10569
10669
|
text: text3,
|
|
10570
10670
|
attachments: [att]
|
|
10571
|
-
}).catch((err) =>
|
|
10671
|
+
}).catch((err) => log35.error({ err }, "handleMessage error"));
|
|
10572
10672
|
}
|
|
10573
10673
|
async cleanupSkillCommands(sessionId) {
|
|
10574
10674
|
this._pendingSkillCommands.delete(sessionId);
|
|
@@ -10931,11 +11031,49 @@ var TypedEmitter = class _TypedEmitter {
|
|
|
10931
11031
|
|
|
10932
11032
|
// src/core/agents/agent-instance.ts
|
|
10933
11033
|
init_read_text_file();
|
|
11034
|
+
|
|
11035
|
+
// src/core/agents/attachment-blocks.ts
|
|
11036
|
+
var SUPPORTED_IMAGE_MIMES = /* @__PURE__ */ new Set([
|
|
11037
|
+
"image/jpeg",
|
|
11038
|
+
"image/png",
|
|
11039
|
+
"image/gif",
|
|
11040
|
+
"image/webp",
|
|
11041
|
+
"image/avif",
|
|
11042
|
+
"image/heic",
|
|
11043
|
+
"image/heif"
|
|
11044
|
+
]);
|
|
11045
|
+
var MAX_ATTACHMENT_SIZE = 10 * 1024 * 1024;
|
|
11046
|
+
function buildAttachmentNote(att, capabilities) {
|
|
11047
|
+
const tooLarge = att.size > MAX_ATTACHMENT_SIZE;
|
|
11048
|
+
if (tooLarge) {
|
|
11049
|
+
const sizeMB = Math.round(att.size / 1024 / 1024);
|
|
11050
|
+
return `[Attachment skipped: "${att.fileName}" is too large (${sizeMB}MB > 10MB limit)]`;
|
|
11051
|
+
}
|
|
11052
|
+
if (att.type === "image") {
|
|
11053
|
+
if (!capabilities.image) {
|
|
11054
|
+
return null;
|
|
11055
|
+
}
|
|
11056
|
+
if (!SUPPORTED_IMAGE_MIMES.has(att.mimeType)) {
|
|
11057
|
+
return `[Attachment skipped: image format not supported (${att.mimeType})]`;
|
|
11058
|
+
}
|
|
11059
|
+
return null;
|
|
11060
|
+
}
|
|
11061
|
+
if (att.type === "audio") {
|
|
11062
|
+
if (!capabilities.audio) {
|
|
11063
|
+
return null;
|
|
11064
|
+
}
|
|
11065
|
+
return null;
|
|
11066
|
+
}
|
|
11067
|
+
return null;
|
|
11068
|
+
}
|
|
11069
|
+
|
|
11070
|
+
// src/core/agents/agent-instance.ts
|
|
10934
11071
|
import { PROTOCOL_VERSION } from "@agentclientprotocol/sdk";
|
|
10935
11072
|
|
|
10936
11073
|
// src/core/sessions/terminal-manager.ts
|
|
10937
11074
|
import { spawn } from "child_process";
|
|
10938
11075
|
import { randomUUID } from "crypto";
|
|
11076
|
+
init_events();
|
|
10939
11077
|
var TerminalManager = class {
|
|
10940
11078
|
terminals = /* @__PURE__ */ new Map();
|
|
10941
11079
|
maxOutputBytes;
|
|
@@ -10952,7 +11090,7 @@ var TerminalManager = class {
|
|
|
10952
11090
|
for (const ev of termEnvArr) {
|
|
10953
11091
|
envRecord[ev.name] = ev.value;
|
|
10954
11092
|
}
|
|
10955
|
-
const result = await middlewareChain.execute(
|
|
11093
|
+
const result = await middlewareChain.execute(Hook.TERMINAL_BEFORE_CREATE, {
|
|
10956
11094
|
sessionId,
|
|
10957
11095
|
command: termCommand,
|
|
10958
11096
|
args: termArgs,
|
|
@@ -11006,7 +11144,7 @@ var TerminalManager = class {
|
|
|
11006
11144
|
childProcess.on("exit", (code, signal) => {
|
|
11007
11145
|
state.exitStatus = { exitCode: code, signal };
|
|
11008
11146
|
if (middlewareChain) {
|
|
11009
|
-
middlewareChain.execute(
|
|
11147
|
+
middlewareChain.execute(Hook.TERMINAL_AFTER_EXIT, {
|
|
11010
11148
|
sessionId,
|
|
11011
11149
|
terminalId,
|
|
11012
11150
|
command: state.command,
|
|
@@ -11136,6 +11274,7 @@ function createDebugTracer(sessionId, workingDirectory) {
|
|
|
11136
11274
|
|
|
11137
11275
|
// src/core/agents/agent-instance.ts
|
|
11138
11276
|
init_log();
|
|
11277
|
+
init_events();
|
|
11139
11278
|
var log4 = createChildLogger({ module: "agent-instance" });
|
|
11140
11279
|
function findPackageRoot(startDir) {
|
|
11141
11280
|
let dir = startDir;
|
|
@@ -11218,7 +11357,7 @@ var AgentInstance = class _AgentInstance extends TypedEmitter {
|
|
|
11218
11357
|
super();
|
|
11219
11358
|
this.agentName = agentName;
|
|
11220
11359
|
}
|
|
11221
|
-
static async spawnSubprocess(agentDef, workingDirectory) {
|
|
11360
|
+
static async spawnSubprocess(agentDef, workingDirectory, allowedPaths = []) {
|
|
11222
11361
|
const instance = new _AgentInstance(agentDef.name);
|
|
11223
11362
|
const resolved = resolveAgentCommand(agentDef.command);
|
|
11224
11363
|
log4.debug(
|
|
@@ -11232,10 +11371,7 @@ var AgentInstance = class _AgentInstance extends TypedEmitter {
|
|
|
11232
11371
|
const ignorePatterns = PathGuard.loadIgnoreFile(workingDirectory);
|
|
11233
11372
|
instance.pathGuard = new PathGuard({
|
|
11234
11373
|
cwd: workingDirectory,
|
|
11235
|
-
|
|
11236
|
-
// spawnSubprocess would need to receive a SecurityConfig param to use it.
|
|
11237
|
-
// Tracked as follow-up: pass workspace security config through spawn/resume call chain.
|
|
11238
|
-
allowedPaths: [],
|
|
11374
|
+
allowedPaths,
|
|
11239
11375
|
ignorePatterns
|
|
11240
11376
|
});
|
|
11241
11377
|
instance.child = spawn2(
|
|
@@ -11329,7 +11465,7 @@ var AgentInstance = class _AgentInstance extends TypedEmitter {
|
|
|
11329
11465
|
if (signal === "SIGINT" || signal === "SIGTERM") return;
|
|
11330
11466
|
if (code !== 0 && code !== null || signal) {
|
|
11331
11467
|
const stderr = this.stderrCapture.getLastLines();
|
|
11332
|
-
this.emit(
|
|
11468
|
+
this.emit(SessionEv.AGENT_EVENT, {
|
|
11333
11469
|
type: "error",
|
|
11334
11470
|
message: signal ? `Agent killed by signal ${signal}
|
|
11335
11471
|
${stderr}` : `Agent crashed (exit code ${code})
|
|
@@ -11341,7 +11477,7 @@ ${stderr}`
|
|
|
11341
11477
|
log4.debug({ sessionId: this.sessionId }, "ACP connection closed");
|
|
11342
11478
|
});
|
|
11343
11479
|
}
|
|
11344
|
-
static async spawn(agentDef, workingDirectory, mcpServers) {
|
|
11480
|
+
static async spawn(agentDef, workingDirectory, mcpServers, allowedPaths) {
|
|
11345
11481
|
log4.debug(
|
|
11346
11482
|
{ agentName: agentDef.name, command: agentDef.command },
|
|
11347
11483
|
"Spawning agent"
|
|
@@ -11349,7 +11485,8 @@ ${stderr}`
|
|
|
11349
11485
|
const spawnStart = Date.now();
|
|
11350
11486
|
const instance = await _AgentInstance.spawnSubprocess(
|
|
11351
11487
|
agentDef,
|
|
11352
|
-
workingDirectory
|
|
11488
|
+
workingDirectory,
|
|
11489
|
+
allowedPaths
|
|
11353
11490
|
);
|
|
11354
11491
|
const resolvedMcp = _AgentInstance.mcpManager.resolve(mcpServers);
|
|
11355
11492
|
const response = await instance.connection.newSession({
|
|
@@ -11372,12 +11509,13 @@ ${stderr}`
|
|
|
11372
11509
|
);
|
|
11373
11510
|
return instance;
|
|
11374
11511
|
}
|
|
11375
|
-
static async resume(agentDef, workingDirectory, agentSessionId, mcpServers) {
|
|
11512
|
+
static async resume(agentDef, workingDirectory, agentSessionId, mcpServers, allowedPaths) {
|
|
11376
11513
|
log4.debug({ agentName: agentDef.name, agentSessionId }, "Resuming agent");
|
|
11377
11514
|
const spawnStart = Date.now();
|
|
11378
11515
|
const instance = await _AgentInstance.spawnSubprocess(
|
|
11379
11516
|
agentDef,
|
|
11380
|
-
workingDirectory
|
|
11517
|
+
workingDirectory,
|
|
11518
|
+
allowedPaths
|
|
11381
11519
|
);
|
|
11382
11520
|
const resolvedMcp = _AgentInstance.mcpManager.resolve(mcpServers);
|
|
11383
11521
|
try {
|
|
@@ -11565,7 +11703,7 @@ ${stderr}`
|
|
|
11565
11703
|
return;
|
|
11566
11704
|
}
|
|
11567
11705
|
if (event !== null) {
|
|
11568
|
-
self.emit(
|
|
11706
|
+
self.emit(SessionEv.AGENT_EVENT, event);
|
|
11569
11707
|
}
|
|
11570
11708
|
},
|
|
11571
11709
|
// ── Permission requests ──────────────────────────────────────────────
|
|
@@ -11592,7 +11730,7 @@ ${stderr}`
|
|
|
11592
11730
|
throw new Error(`[Access denied] ${pathCheck.reason}`);
|
|
11593
11731
|
}
|
|
11594
11732
|
if (self.middlewareChain) {
|
|
11595
|
-
const result = await self.middlewareChain.execute(
|
|
11733
|
+
const result = await self.middlewareChain.execute(Hook.FS_BEFORE_READ, { sessionId: self.sessionId, path: p.path, line: p.line, limit: p.limit }, async (r) => r);
|
|
11596
11734
|
if (!result) return { content: "" };
|
|
11597
11735
|
p.path = result.path;
|
|
11598
11736
|
}
|
|
@@ -11610,7 +11748,7 @@ ${stderr}`
|
|
|
11610
11748
|
throw new Error(`[Access denied] ${pathCheck.reason}`);
|
|
11611
11749
|
}
|
|
11612
11750
|
if (self.middlewareChain) {
|
|
11613
|
-
const result = await self.middlewareChain.execute(
|
|
11751
|
+
const result = await self.middlewareChain.execute(Hook.FS_BEFORE_WRITE, { sessionId: self.sessionId, path: writePath, content: writeContent }, async (r) => r);
|
|
11614
11752
|
if (!result) return {};
|
|
11615
11753
|
writePath = result.path;
|
|
11616
11754
|
writeContent = result.content;
|
|
@@ -11707,10 +11845,16 @@ ${stderr}`
|
|
|
11707
11845
|
// ── Prompt & lifecycle ──────────────────────────────────────────────
|
|
11708
11846
|
async prompt(text3, attachments) {
|
|
11709
11847
|
const contentBlocks = [{ type: "text", text: text3 }];
|
|
11710
|
-
const
|
|
11848
|
+
const capabilities = this.promptCapabilities ?? {};
|
|
11711
11849
|
for (const att of attachments ?? []) {
|
|
11712
|
-
const
|
|
11713
|
-
if (
|
|
11850
|
+
const skipNote = buildAttachmentNote(att, capabilities);
|
|
11851
|
+
if (skipNote !== null) {
|
|
11852
|
+
contentBlocks[0].text += `
|
|
11853
|
+
|
|
11854
|
+
${skipNote}`;
|
|
11855
|
+
continue;
|
|
11856
|
+
}
|
|
11857
|
+
if (att.type === "image" && capabilities.image && SUPPORTED_IMAGE_MIMES.has(att.mimeType)) {
|
|
11714
11858
|
const attCheck = this.pathGuard.validatePath(att.filePath, "read");
|
|
11715
11859
|
if (!attCheck.allowed) {
|
|
11716
11860
|
contentBlocks[0].text += `
|
|
@@ -11720,7 +11864,7 @@ ${stderr}`
|
|
|
11720
11864
|
}
|
|
11721
11865
|
const data = await fs8.promises.readFile(att.filePath);
|
|
11722
11866
|
contentBlocks.push({ type: "image", data: data.toString("base64"), mimeType: att.mimeType });
|
|
11723
|
-
} else if (att.type === "audio" &&
|
|
11867
|
+
} else if (att.type === "audio" && capabilities.audio) {
|
|
11724
11868
|
const attCheck = this.pathGuard.validatePath(att.filePath, "read");
|
|
11725
11869
|
if (!attCheck.allowed) {
|
|
11726
11870
|
contentBlocks[0].text += `
|
|
@@ -11731,9 +11875,9 @@ ${stderr}`
|
|
|
11731
11875
|
const data = await fs8.promises.readFile(att.filePath);
|
|
11732
11876
|
contentBlocks.push({ type: "audio", data: data.toString("base64"), mimeType: att.mimeType });
|
|
11733
11877
|
} else {
|
|
11734
|
-
if (
|
|
11878
|
+
if (att.type === "image" || att.type === "audio") {
|
|
11735
11879
|
log4.debug(
|
|
11736
|
-
{ type: att.type, capabilities
|
|
11880
|
+
{ type: att.type, capabilities },
|
|
11737
11881
|
"Agent does not support %s content, falling back to file path",
|
|
11738
11882
|
att.type
|
|
11739
11883
|
);
|
|
@@ -11789,15 +11933,15 @@ var AgentManager = class {
|
|
|
11789
11933
|
getAgent(name) {
|
|
11790
11934
|
return this.catalog.resolve(name);
|
|
11791
11935
|
}
|
|
11792
|
-
async spawn(agentName, workingDirectory) {
|
|
11936
|
+
async spawn(agentName, workingDirectory, allowedPaths) {
|
|
11793
11937
|
const agentDef = this.getAgent(agentName);
|
|
11794
11938
|
if (!agentDef) throw new Error(`Agent "${agentName}" is not installed. Run "openacp agents install ${agentName}" to add it.`);
|
|
11795
|
-
return AgentInstance.spawn(agentDef, workingDirectory);
|
|
11939
|
+
return AgentInstance.spawn(agentDef, workingDirectory, void 0, allowedPaths);
|
|
11796
11940
|
}
|
|
11797
|
-
async resume(agentName, workingDirectory, agentSessionId) {
|
|
11941
|
+
async resume(agentName, workingDirectory, agentSessionId, allowedPaths) {
|
|
11798
11942
|
const agentDef = this.getAgent(agentName);
|
|
11799
11943
|
if (!agentDef) throw new Error(`Agent "${agentName}" is not installed. Run "openacp agents install ${agentName}" to add it.`);
|
|
11800
|
-
return AgentInstance.resume(agentDef, workingDirectory, agentSessionId);
|
|
11944
|
+
return AgentInstance.resume(agentDef, workingDirectory, agentSessionId, void 0, allowedPaths);
|
|
11801
11945
|
}
|
|
11802
11946
|
};
|
|
11803
11947
|
|
|
@@ -11970,6 +12114,7 @@ function isSystemEvent(event) {
|
|
|
11970
12114
|
}
|
|
11971
12115
|
|
|
11972
12116
|
// src/core/sessions/session.ts
|
|
12117
|
+
init_events();
|
|
11973
12118
|
var moduleLog = createChildLogger({ module: "session" });
|
|
11974
12119
|
var TTS_PROMPT_INSTRUCTION = `
|
|
11975
12120
|
|
|
@@ -11998,7 +12143,15 @@ var Session = class extends TypedEmitter {
|
|
|
11998
12143
|
}
|
|
11999
12144
|
agentName;
|
|
12000
12145
|
workingDirectory;
|
|
12001
|
-
|
|
12146
|
+
_agentInstance;
|
|
12147
|
+
get agentInstance() {
|
|
12148
|
+
return this._agentInstance;
|
|
12149
|
+
}
|
|
12150
|
+
set agentInstance(agent) {
|
|
12151
|
+
this._agentInstance = agent;
|
|
12152
|
+
this.wireAgentRelay();
|
|
12153
|
+
this.wireCommandsBuffer();
|
|
12154
|
+
}
|
|
12002
12155
|
agentSessionId = "";
|
|
12003
12156
|
_status = "initializing";
|
|
12004
12157
|
name;
|
|
@@ -12022,9 +12175,6 @@ var Session = class extends TypedEmitter {
|
|
|
12022
12175
|
threadIds = /* @__PURE__ */ new Map();
|
|
12023
12176
|
/** Active turn context — sealed on prompt dequeue, cleared on turn end */
|
|
12024
12177
|
activeTurnContext = null;
|
|
12025
|
-
/** The agentInstance for which the agent→session event relay is wired (prevents duplicate relays from multiple bridges).
|
|
12026
|
-
* When the agent is swapped, the relay must be re-wired to the new instance. */
|
|
12027
|
-
agentRelaySource = null;
|
|
12028
12178
|
permissionGate = new PermissionGate();
|
|
12029
12179
|
queue;
|
|
12030
12180
|
speechService;
|
|
@@ -12048,23 +12198,37 @@ var Session = class extends TypedEmitter {
|
|
|
12048
12198
|
this.log.error({ err }, "Prompt execution failed");
|
|
12049
12199
|
const message = err instanceof Error ? err.message : String(err);
|
|
12050
12200
|
this.fail(message);
|
|
12051
|
-
this.emit(
|
|
12201
|
+
this.emit(SessionEv.AGENT_EVENT, { type: "error", message: `Prompt execution failed: ${message}` });
|
|
12052
12202
|
}
|
|
12053
12203
|
);
|
|
12054
|
-
|
|
12204
|
+
}
|
|
12205
|
+
/** Wire the agent→session event relay on the current agentInstance.
|
|
12206
|
+
* Removes any previous relay first to avoid duplicates on agent switch.
|
|
12207
|
+
* This relay ensures session.emit("agent_event") fires for ALL sessions,
|
|
12208
|
+
* including headless API sessions that have no SessionBridge attached. */
|
|
12209
|
+
agentRelayCleanup;
|
|
12210
|
+
wireAgentRelay() {
|
|
12211
|
+
this.agentRelayCleanup?.();
|
|
12212
|
+
const instance = this._agentInstance;
|
|
12213
|
+
const handler = (event) => {
|
|
12214
|
+
this.emit(SessionEv.AGENT_EVENT, event);
|
|
12215
|
+
};
|
|
12216
|
+
instance.on(SessionEv.AGENT_EVENT, handler);
|
|
12217
|
+
this.agentRelayCleanup = () => instance.off(SessionEv.AGENT_EVENT, handler);
|
|
12055
12218
|
}
|
|
12056
12219
|
/** Wire a listener on the current agentInstance to buffer commands_update events.
|
|
12057
12220
|
* Must be called after every agentInstance replacement (constructor + switchAgent). */
|
|
12058
12221
|
commandsBufferCleanup;
|
|
12059
12222
|
wireCommandsBuffer() {
|
|
12060
12223
|
this.commandsBufferCleanup?.();
|
|
12224
|
+
const instance = this._agentInstance;
|
|
12061
12225
|
const handler = (event) => {
|
|
12062
12226
|
if (event.type === "commands_update") {
|
|
12063
12227
|
this.latestCommands = event.commands;
|
|
12064
12228
|
}
|
|
12065
12229
|
};
|
|
12066
|
-
|
|
12067
|
-
this.commandsBufferCleanup = () =>
|
|
12230
|
+
instance.on(SessionEv.AGENT_EVENT, handler);
|
|
12231
|
+
this.commandsBufferCleanup = () => instance.off(SessionEv.AGENT_EVENT, handler);
|
|
12068
12232
|
}
|
|
12069
12233
|
// --- State Machine ---
|
|
12070
12234
|
get status() {
|
|
@@ -12078,12 +12242,12 @@ var Session = class extends TypedEmitter {
|
|
|
12078
12242
|
fail(reason) {
|
|
12079
12243
|
if (this._status === "error") return;
|
|
12080
12244
|
this.transition("error");
|
|
12081
|
-
this.emit(
|
|
12245
|
+
this.emit(SessionEv.ERROR, new Error(reason));
|
|
12082
12246
|
}
|
|
12083
12247
|
/** Transition to finished — from active only. Emits session_end for backward compat. */
|
|
12084
12248
|
finish(reason) {
|
|
12085
12249
|
this.transition("finished");
|
|
12086
|
-
this.emit(
|
|
12250
|
+
this.emit(SessionEv.SESSION_END, reason ?? "completed");
|
|
12087
12251
|
}
|
|
12088
12252
|
/** Transition to cancelled — from active only (terminal session cancel) */
|
|
12089
12253
|
markCancelled() {
|
|
@@ -12099,7 +12263,7 @@ var Session = class extends TypedEmitter {
|
|
|
12099
12263
|
}
|
|
12100
12264
|
this._status = to;
|
|
12101
12265
|
this.log.debug({ from, to }, "Session status transition");
|
|
12102
|
-
this.emit(
|
|
12266
|
+
this.emit(SessionEv.STATUS_CHANGE, from, to);
|
|
12103
12267
|
}
|
|
12104
12268
|
/** Number of prompts waiting in queue */
|
|
12105
12269
|
get queueDepth() {
|
|
@@ -12121,8 +12285,8 @@ var Session = class extends TypedEmitter {
|
|
|
12121
12285
|
async enqueuePrompt(text3, attachments, routing, externalTurnId) {
|
|
12122
12286
|
const turnId = externalTurnId ?? nanoid2(8);
|
|
12123
12287
|
if (this.middlewareChain) {
|
|
12124
|
-
const payload = { text: text3, attachments, sessionId: this.id };
|
|
12125
|
-
const result = await this.middlewareChain.execute(
|
|
12288
|
+
const payload = { text: text3, attachments, sessionId: this.id, sourceAdapterId: routing?.sourceAdapterId };
|
|
12289
|
+
const result = await this.middlewareChain.execute(Hook.AGENT_BEFORE_PROMPT, payload, async (p) => p);
|
|
12126
12290
|
if (!result) return turnId;
|
|
12127
12291
|
text3 = result.text;
|
|
12128
12292
|
attachments = result.attachments;
|
|
@@ -12137,9 +12301,9 @@ var Session = class extends TypedEmitter {
|
|
|
12137
12301
|
routing?.responseAdapterId,
|
|
12138
12302
|
turnId
|
|
12139
12303
|
);
|
|
12140
|
-
this.emit(
|
|
12304
|
+
this.emit(SessionEv.TURN_STARTED, this.activeTurnContext);
|
|
12141
12305
|
this.promptCount++;
|
|
12142
|
-
this.emit(
|
|
12306
|
+
this.emit(SessionEv.PROMPT_COUNT_CHANGED, this.promptCount);
|
|
12143
12307
|
if (this._status === "initializing" || this._status === "cancelled" || this._status === "error") {
|
|
12144
12308
|
this.activate();
|
|
12145
12309
|
}
|
|
@@ -12168,10 +12332,18 @@ ${text3}`;
|
|
|
12168
12332
|
}
|
|
12169
12333
|
} : null;
|
|
12170
12334
|
if (accumulatorListener) {
|
|
12171
|
-
this.on(
|
|
12335
|
+
this.on(SessionEv.AGENT_EVENT, accumulatorListener);
|
|
12336
|
+
}
|
|
12337
|
+
const mw = this.middlewareChain;
|
|
12338
|
+
const afterEventListener = mw ? (event) => {
|
|
12339
|
+
mw.execute(Hook.AGENT_AFTER_EVENT, { sessionId: this.id, event, outgoingMessage: { type: "text", text: "" } }, async (e) => e).catch(() => {
|
|
12340
|
+
});
|
|
12341
|
+
} : null;
|
|
12342
|
+
if (afterEventListener) {
|
|
12343
|
+
this.agentInstance.on(SessionEv.AGENT_EVENT, afterEventListener);
|
|
12172
12344
|
}
|
|
12173
12345
|
if (this.middlewareChain) {
|
|
12174
|
-
this.middlewareChain.execute(
|
|
12346
|
+
this.middlewareChain.execute(Hook.TURN_START, { sessionId: this.id, promptText: processed.text, promptNumber: this.promptCount }, async (p) => p).catch(() => {
|
|
12175
12347
|
});
|
|
12176
12348
|
}
|
|
12177
12349
|
let stopReason = "end_turn";
|
|
@@ -12192,10 +12364,13 @@ ${text3}`;
|
|
|
12192
12364
|
promptError = err;
|
|
12193
12365
|
} finally {
|
|
12194
12366
|
if (accumulatorListener) {
|
|
12195
|
-
this.off(
|
|
12367
|
+
this.off(SessionEv.AGENT_EVENT, accumulatorListener);
|
|
12368
|
+
}
|
|
12369
|
+
if (afterEventListener) {
|
|
12370
|
+
this.agentInstance.off(SessionEv.AGENT_EVENT, afterEventListener);
|
|
12196
12371
|
}
|
|
12197
12372
|
if (this.middlewareChain) {
|
|
12198
|
-
this.middlewareChain.execute(
|
|
12373
|
+
this.middlewareChain.execute(Hook.TURN_END, { sessionId: this.id, stopReason, durationMs: Date.now() - promptStart }, async (p) => p).catch(() => {
|
|
12199
12374
|
});
|
|
12200
12375
|
}
|
|
12201
12376
|
this.activeTurnContext = null;
|
|
@@ -12240,7 +12415,7 @@ ${text3}`;
|
|
|
12240
12415
|
const audioBuffer = await fs9.promises.readFile(audioPath);
|
|
12241
12416
|
const result = await this.speechService.transcribe(audioBuffer, audioMime);
|
|
12242
12417
|
this.log.info({ provider: "stt", duration: result.duration }, "Voice transcribed");
|
|
12243
|
-
this.emit(
|
|
12418
|
+
this.emit(SessionEv.AGENT_EVENT, {
|
|
12244
12419
|
type: "system_message",
|
|
12245
12420
|
message: `\u{1F3A4} You said: ${result.text}`
|
|
12246
12421
|
});
|
|
@@ -12249,7 +12424,7 @@ ${text3}`;
|
|
|
12249
12424
|
${result.text}` : result.text;
|
|
12250
12425
|
} catch (err) {
|
|
12251
12426
|
this.log.warn({ err }, "STT transcription failed, keeping audio attachment");
|
|
12252
|
-
this.emit(
|
|
12427
|
+
this.emit(SessionEv.AGENT_EVENT, {
|
|
12253
12428
|
type: "error",
|
|
12254
12429
|
message: `Voice transcription failed: ${err.message}`
|
|
12255
12430
|
});
|
|
@@ -12283,12 +12458,12 @@ ${result.text}` : result.text;
|
|
|
12283
12458
|
timeoutPromise
|
|
12284
12459
|
]);
|
|
12285
12460
|
const base64 = result.audioBuffer.toString("base64");
|
|
12286
|
-
this.emit(
|
|
12461
|
+
this.emit(SessionEv.AGENT_EVENT, {
|
|
12287
12462
|
type: "audio_content",
|
|
12288
12463
|
data: base64,
|
|
12289
12464
|
mimeType: result.mimeType
|
|
12290
12465
|
});
|
|
12291
|
-
this.emit(
|
|
12466
|
+
this.emit(SessionEv.AGENT_EVENT, { type: "tts_strip" });
|
|
12292
12467
|
this.log.info("TTS synthesis completed");
|
|
12293
12468
|
} finally {
|
|
12294
12469
|
clearTimeout(ttsTimer);
|
|
@@ -12303,19 +12478,19 @@ ${result.text}` : result.text;
|
|
|
12303
12478
|
const captureHandler = (event) => {
|
|
12304
12479
|
if (event.type === "text") title += event.content;
|
|
12305
12480
|
};
|
|
12306
|
-
this.pause((event) => event !==
|
|
12307
|
-
this.agentInstance.on(
|
|
12481
|
+
this.pause((event) => event !== SessionEv.AGENT_EVENT);
|
|
12482
|
+
this.agentInstance.on(SessionEv.AGENT_EVENT, captureHandler);
|
|
12308
12483
|
try {
|
|
12309
12484
|
await this.agentInstance.prompt(
|
|
12310
12485
|
"Summarize this conversation in max 5 words for a topic title. Reply ONLY with the title, nothing else."
|
|
12311
12486
|
);
|
|
12312
12487
|
this.name = title.trim().slice(0, 50) || `Session ${this.id.slice(0, 6)}`;
|
|
12313
12488
|
this.log.info({ name: this.name }, "Session auto-named");
|
|
12314
|
-
this.emit(
|
|
12489
|
+
this.emit(SessionEv.NAMED, this.name);
|
|
12315
12490
|
} catch {
|
|
12316
12491
|
this.name = `Session ${this.id.slice(0, 6)}`;
|
|
12317
12492
|
} finally {
|
|
12318
|
-
this.agentInstance.off(
|
|
12493
|
+
this.agentInstance.off(SessionEv.AGENT_EVENT, captureHandler);
|
|
12319
12494
|
this.clearBuffer();
|
|
12320
12495
|
this.resume();
|
|
12321
12496
|
}
|
|
@@ -12377,7 +12552,7 @@ ${result.text}` : result.text;
|
|
|
12377
12552
|
/** Set session name explicitly and emit 'named' event */
|
|
12378
12553
|
setName(name) {
|
|
12379
12554
|
this.name = name;
|
|
12380
|
-
this.emit(
|
|
12555
|
+
this.emit(SessionEv.NAMED, name);
|
|
12381
12556
|
}
|
|
12382
12557
|
/** Send a config option change to the agent and update local state from the response. */
|
|
12383
12558
|
async setConfigOption(configId, value) {
|
|
@@ -12393,7 +12568,7 @@ ${result.text}` : result.text;
|
|
|
12393
12568
|
}
|
|
12394
12569
|
async updateConfigOptions(options) {
|
|
12395
12570
|
if (this.middlewareChain) {
|
|
12396
|
-
const result = await this.middlewareChain.execute(
|
|
12571
|
+
const result = await this.middlewareChain.execute(Hook.CONFIG_BEFORE_CHANGE, { sessionId: this.id, configId: "options", oldValue: this.configOptions, newValue: options }, async (p) => p);
|
|
12397
12572
|
if (!result) return;
|
|
12398
12573
|
}
|
|
12399
12574
|
this.configOptions = options;
|
|
@@ -12413,7 +12588,7 @@ ${result.text}` : result.text;
|
|
|
12413
12588
|
/** Cancel the current prompt and clear the queue. Stays in active state. */
|
|
12414
12589
|
async abortPrompt() {
|
|
12415
12590
|
if (this.middlewareChain) {
|
|
12416
|
-
const result = await this.middlewareChain.execute(
|
|
12591
|
+
const result = await this.middlewareChain.execute(Hook.AGENT_BEFORE_CANCEL, { sessionId: this.id }, async (p) => p);
|
|
12417
12592
|
if (!result) return;
|
|
12418
12593
|
}
|
|
12419
12594
|
this.queue.clear();
|
|
@@ -12454,7 +12629,6 @@ ${result.text}` : result.text;
|
|
|
12454
12629
|
this.configOptions = [];
|
|
12455
12630
|
this.latestCommands = null;
|
|
12456
12631
|
this.applySpawnResponse(newAgent.initialSessionResponse, newAgent.agentCapabilities);
|
|
12457
|
-
this.wireCommandsBuffer();
|
|
12458
12632
|
this.log.info({ from: this.agentSwitchHistory.at(-1).agentName, to: agentName }, "Agent switched");
|
|
12459
12633
|
}
|
|
12460
12634
|
async destroy() {
|
|
@@ -12893,6 +13067,7 @@ var MessageTransformer = class {
|
|
|
12893
13067
|
};
|
|
12894
13068
|
|
|
12895
13069
|
// src/core/sessions/session-manager.ts
|
|
13070
|
+
init_events();
|
|
12896
13071
|
var SessionManager = class {
|
|
12897
13072
|
sessions = /* @__PURE__ */ new Map();
|
|
12898
13073
|
store;
|
|
@@ -12997,18 +13172,18 @@ var SessionManager = class {
|
|
|
12997
13172
|
}
|
|
12998
13173
|
}
|
|
12999
13174
|
if (this.middlewareChain) {
|
|
13000
|
-
this.middlewareChain.execute(
|
|
13175
|
+
this.middlewareChain.execute(Hook.SESSION_AFTER_DESTROY, { sessionId }, async (p) => p).catch(() => {
|
|
13001
13176
|
});
|
|
13002
13177
|
}
|
|
13003
13178
|
}
|
|
13004
13179
|
listSessions(channelId) {
|
|
13005
|
-
const all = Array.from(this.sessions.values());
|
|
13180
|
+
const all = Array.from(this.sessions.values()).filter((s) => !s.isAssistant);
|
|
13006
13181
|
if (channelId) return all.filter((s) => s.channelId === channelId);
|
|
13007
13182
|
return all;
|
|
13008
13183
|
}
|
|
13009
13184
|
listAllSessions(channelId) {
|
|
13010
13185
|
if (this.store) {
|
|
13011
|
-
let records = this.store.list();
|
|
13186
|
+
let records = this.store.list().filter((r) => !r.isAssistant);
|
|
13012
13187
|
if (channelId) records = records.filter((r) => r.channelId === channelId);
|
|
13013
13188
|
return records.map((record) => {
|
|
13014
13189
|
const live2 = this.sessions.get(record.sessionId);
|
|
@@ -13048,7 +13223,7 @@ var SessionManager = class {
|
|
|
13048
13223
|
};
|
|
13049
13224
|
});
|
|
13050
13225
|
}
|
|
13051
|
-
let live = Array.from(this.sessions.values());
|
|
13226
|
+
let live = Array.from(this.sessions.values()).filter((s) => !s.isAssistant);
|
|
13052
13227
|
if (channelId) live = live.filter((s) => s.channelId === channelId);
|
|
13053
13228
|
return live.map((s) => ({
|
|
13054
13229
|
id: s.id,
|
|
@@ -13069,7 +13244,7 @@ var SessionManager = class {
|
|
|
13069
13244
|
}
|
|
13070
13245
|
listRecords(filter) {
|
|
13071
13246
|
if (!this.store) return [];
|
|
13072
|
-
let records = this.store.list();
|
|
13247
|
+
let records = this.store.list().filter((r) => !r.isAssistant);
|
|
13073
13248
|
if (filter?.statuses?.length) {
|
|
13074
13249
|
records = records.filter((r) => filter.statuses.includes(r.status));
|
|
13075
13250
|
}
|
|
@@ -13078,7 +13253,7 @@ var SessionManager = class {
|
|
|
13078
13253
|
async removeRecord(sessionId) {
|
|
13079
13254
|
if (!this.store) return;
|
|
13080
13255
|
await this.store.remove(sessionId);
|
|
13081
|
-
this.eventBus?.emit(
|
|
13256
|
+
this.eventBus?.emit(BusEvent.SESSION_DELETED, { sessionId });
|
|
13082
13257
|
}
|
|
13083
13258
|
/**
|
|
13084
13259
|
* Graceful shutdown: persist session state without killing agent subprocesses.
|
|
@@ -13126,7 +13301,7 @@ var SessionManager = class {
|
|
|
13126
13301
|
this.sessions.clear();
|
|
13127
13302
|
if (this.middlewareChain) {
|
|
13128
13303
|
for (const sessionId of sessionIds) {
|
|
13129
|
-
this.middlewareChain.execute(
|
|
13304
|
+
this.middlewareChain.execute(Hook.SESSION_AFTER_DESTROY, { sessionId }, async (p) => p).catch(() => {
|
|
13130
13305
|
});
|
|
13131
13306
|
}
|
|
13132
13307
|
}
|
|
@@ -13136,6 +13311,7 @@ var SessionManager = class {
|
|
|
13136
13311
|
// src/core/sessions/session-bridge.ts
|
|
13137
13312
|
init_log();
|
|
13138
13313
|
init_bypass_detection();
|
|
13314
|
+
init_events();
|
|
13139
13315
|
var log6 = createChildLogger({ module: "session-bridge" });
|
|
13140
13316
|
var SessionBridge = class {
|
|
13141
13317
|
constructor(session, adapter, deps, adapterId) {
|
|
@@ -13160,7 +13336,7 @@ var SessionBridge = class {
|
|
|
13160
13336
|
try {
|
|
13161
13337
|
const mw = this.deps.middlewareChain;
|
|
13162
13338
|
if (mw) {
|
|
13163
|
-
const result = await mw.execute(
|
|
13339
|
+
const result = await mw.execute(Hook.MESSAGE_OUTGOING, { sessionId, message }, async (m) => m);
|
|
13164
13340
|
this.tracer?.log("core", { step: "middleware:outgoing", sessionId, hook: "message:outgoing", blocked: !result });
|
|
13165
13341
|
if (!result) return;
|
|
13166
13342
|
this.tracer?.log("core", { step: "dispatch", sessionId, message: result.message });
|
|
@@ -13189,17 +13365,11 @@ var SessionBridge = class {
|
|
|
13189
13365
|
connect() {
|
|
13190
13366
|
if (this.connected) return;
|
|
13191
13367
|
this.connected = true;
|
|
13192
|
-
|
|
13193
|
-
this.listen(this.session.agentInstance, "agent_event", (event) => {
|
|
13194
|
-
this.session.emit("agent_event", event);
|
|
13195
|
-
});
|
|
13196
|
-
this.session.agentRelaySource = this.session.agentInstance;
|
|
13197
|
-
}
|
|
13198
|
-
this.listen(this.session, "agent_event", (event) => {
|
|
13368
|
+
this.listen(this.session, SessionEv.AGENT_EVENT, (event) => {
|
|
13199
13369
|
if (this.shouldForward(event)) {
|
|
13200
13370
|
this.dispatchAgentEvent(event);
|
|
13201
13371
|
} else {
|
|
13202
|
-
this.deps.eventBus?.emit(
|
|
13372
|
+
this.deps.eventBus?.emit(BusEvent.AGENT_EVENT, { sessionId: this.session.id, event });
|
|
13203
13373
|
}
|
|
13204
13374
|
});
|
|
13205
13375
|
if (!this.session.agentInstance.onPermissionRequest || this.session.agentInstance.onPermissionRequest.__bridgeId === void 0) {
|
|
@@ -13209,7 +13379,7 @@ var SessionBridge = class {
|
|
|
13209
13379
|
handler.__bridgeId = this.adapterId;
|
|
13210
13380
|
this.session.agentInstance.onPermissionRequest = handler;
|
|
13211
13381
|
}
|
|
13212
|
-
this.listen(this.session,
|
|
13382
|
+
this.listen(this.session, SessionEv.PERMISSION_REQUEST, async (request) => {
|
|
13213
13383
|
const current = this.session.agentInstance.onPermissionRequest;
|
|
13214
13384
|
if (current?.__bridgeId === this.adapterId) return;
|
|
13215
13385
|
if (!this.session.permissionGate.isPending) return;
|
|
@@ -13219,37 +13389,41 @@ var SessionBridge = class {
|
|
|
13219
13389
|
log6.error({ err, sessionId: this.session.id, adapterId: this.adapterId }, "Failed to send permission request to adapter");
|
|
13220
13390
|
}
|
|
13221
13391
|
});
|
|
13222
|
-
this.listen(this.session,
|
|
13392
|
+
this.listen(this.session, SessionEv.STATUS_CHANGE, (from, to) => {
|
|
13223
13393
|
this.deps.sessionManager.patchRecord(this.session.id, {
|
|
13224
13394
|
status: to,
|
|
13225
13395
|
lastActiveAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
13226
13396
|
});
|
|
13227
|
-
this.
|
|
13228
|
-
|
|
13229
|
-
|
|
13230
|
-
|
|
13397
|
+
if (!this.session.isAssistant) {
|
|
13398
|
+
this.deps.eventBus?.emit(BusEvent.SESSION_UPDATED, {
|
|
13399
|
+
sessionId: this.session.id,
|
|
13400
|
+
status: to
|
|
13401
|
+
});
|
|
13402
|
+
}
|
|
13231
13403
|
if (to === "finished") {
|
|
13232
13404
|
queueMicrotask(() => this.disconnect());
|
|
13233
13405
|
}
|
|
13234
13406
|
});
|
|
13235
|
-
this.listen(this.session,
|
|
13407
|
+
this.listen(this.session, SessionEv.NAMED, async (name) => {
|
|
13236
13408
|
const record = this.deps.sessionManager.getSessionRecord(this.session.id);
|
|
13237
13409
|
const alreadyNamed = !!record?.name;
|
|
13238
13410
|
await this.deps.sessionManager.patchRecord(this.session.id, { name });
|
|
13239
|
-
this.
|
|
13240
|
-
|
|
13241
|
-
|
|
13242
|
-
|
|
13411
|
+
if (!this.session.isAssistant) {
|
|
13412
|
+
this.deps.eventBus?.emit(BusEvent.SESSION_UPDATED, {
|
|
13413
|
+
sessionId: this.session.id,
|
|
13414
|
+
name
|
|
13415
|
+
});
|
|
13416
|
+
}
|
|
13243
13417
|
if (!alreadyNamed) {
|
|
13244
13418
|
await this.adapter.renameSessionThread(this.session.id, name);
|
|
13245
13419
|
}
|
|
13246
13420
|
});
|
|
13247
|
-
this.listen(this.session,
|
|
13421
|
+
this.listen(this.session, SessionEv.PROMPT_COUNT_CHANGED, (count) => {
|
|
13248
13422
|
this.deps.sessionManager.patchRecord(this.session.id, { currentPromptCount: count });
|
|
13249
13423
|
});
|
|
13250
|
-
this.listen(this.session,
|
|
13424
|
+
this.listen(this.session, SessionEv.TURN_STARTED, (ctx) => {
|
|
13251
13425
|
if (ctx.sourceAdapterId !== "sse" && ctx.sourceAdapterId !== "api") {
|
|
13252
|
-
this.deps.eventBus?.emit(
|
|
13426
|
+
this.deps.eventBus?.emit(BusEvent.MESSAGE_PROCESSING, {
|
|
13253
13427
|
sessionId: this.session.id,
|
|
13254
13428
|
turnId: ctx.turnId,
|
|
13255
13429
|
sourceAdapterId: ctx.sourceAdapterId,
|
|
@@ -13258,10 +13432,10 @@ var SessionBridge = class {
|
|
|
13258
13432
|
}
|
|
13259
13433
|
});
|
|
13260
13434
|
if (this.session.latestCommands !== null) {
|
|
13261
|
-
this.session.emit(
|
|
13435
|
+
this.session.emit(SessionEv.AGENT_EVENT, { type: "commands_update", commands: this.session.latestCommands });
|
|
13262
13436
|
}
|
|
13263
13437
|
if (this.session.configOptions.length > 0) {
|
|
13264
|
-
this.session.emit(
|
|
13438
|
+
this.session.emit(SessionEv.AGENT_EVENT, { type: "config_option_update", options: this.session.configOptions });
|
|
13265
13439
|
}
|
|
13266
13440
|
}
|
|
13267
13441
|
disconnect() {
|
|
@@ -13281,17 +13455,11 @@ var SessionBridge = class {
|
|
|
13281
13455
|
const mw = this.deps.middlewareChain;
|
|
13282
13456
|
if (mw) {
|
|
13283
13457
|
try {
|
|
13284
|
-
const result = await mw.execute(
|
|
13458
|
+
const result = await mw.execute(Hook.AGENT_BEFORE_EVENT, { sessionId: this.session.id, event }, async (e) => e);
|
|
13285
13459
|
this.tracer?.log("core", { step: "middleware:before", sessionId: this.session.id, hook: "agent:beforeEvent", blocked: !result });
|
|
13286
13460
|
if (!result) return;
|
|
13287
13461
|
const transformedEvent = result.event;
|
|
13288
|
-
|
|
13289
|
-
mw.execute("agent:afterEvent", {
|
|
13290
|
-
sessionId: this.session.id,
|
|
13291
|
-
event: transformedEvent,
|
|
13292
|
-
outgoingMessage: outgoing ?? { type: "text", text: "" }
|
|
13293
|
-
}, async (e) => e).catch(() => {
|
|
13294
|
-
});
|
|
13462
|
+
this.handleAgentEvent(transformedEvent);
|
|
13295
13463
|
} catch {
|
|
13296
13464
|
try {
|
|
13297
13465
|
this.handleAgentEvent(event);
|
|
@@ -13424,7 +13592,7 @@ var SessionBridge = class {
|
|
|
13424
13592
|
this.adapter.stripTTSBlock?.(this.session.id);
|
|
13425
13593
|
break;
|
|
13426
13594
|
}
|
|
13427
|
-
this.deps.eventBus?.emit(
|
|
13595
|
+
this.deps.eventBus?.emit(BusEvent.AGENT_EVENT, {
|
|
13428
13596
|
sessionId: this.session.id,
|
|
13429
13597
|
event
|
|
13430
13598
|
});
|
|
@@ -13443,7 +13611,7 @@ var SessionBridge = class {
|
|
|
13443
13611
|
let permReq = request;
|
|
13444
13612
|
if (mw) {
|
|
13445
13613
|
const payload = { sessionId: this.session.id, request, autoResolve: void 0 };
|
|
13446
|
-
const result = await mw.execute(
|
|
13614
|
+
const result = await mw.execute(Hook.PERMISSION_BEFORE_REQUEST, payload, async (r) => r);
|
|
13447
13615
|
if (!result) return "";
|
|
13448
13616
|
permReq = result.request;
|
|
13449
13617
|
if (result.autoResolve) {
|
|
@@ -13451,21 +13619,21 @@ var SessionBridge = class {
|
|
|
13451
13619
|
return result.autoResolve;
|
|
13452
13620
|
}
|
|
13453
13621
|
}
|
|
13454
|
-
this.deps.eventBus?.emit(
|
|
13622
|
+
this.deps.eventBus?.emit(BusEvent.PERMISSION_REQUEST, {
|
|
13455
13623
|
sessionId: this.session.id,
|
|
13456
13624
|
permission: permReq
|
|
13457
13625
|
});
|
|
13458
13626
|
const autoDecision = this.checkAutoApprove(permReq);
|
|
13459
13627
|
if (autoDecision) {
|
|
13460
|
-
this.session.emit(
|
|
13628
|
+
this.session.emit(SessionEv.PERMISSION_REQUEST, permReq);
|
|
13461
13629
|
this.emitAfterResolve(mw, permReq.id, autoDecision, "system", startTime);
|
|
13462
13630
|
return autoDecision;
|
|
13463
13631
|
}
|
|
13464
13632
|
const promise = this.session.permissionGate.setPending(permReq);
|
|
13465
|
-
this.session.emit(
|
|
13633
|
+
this.session.emit(SessionEv.PERMISSION_REQUEST, permReq);
|
|
13466
13634
|
await this.adapter.sendPermissionRequest(this.session.id, permReq);
|
|
13467
13635
|
const optionId = await promise;
|
|
13468
|
-
this.deps.eventBus?.emit(
|
|
13636
|
+
this.deps.eventBus?.emit(BusEvent.PERMISSION_RESOLVED, {
|
|
13469
13637
|
sessionId: this.session.id,
|
|
13470
13638
|
requestId: permReq.id,
|
|
13471
13639
|
decision: optionId,
|
|
@@ -13497,7 +13665,7 @@ var SessionBridge = class {
|
|
|
13497
13665
|
/** Emit permission:afterResolve middleware hook (fire-and-forget) */
|
|
13498
13666
|
emitAfterResolve(mw, requestId, decision, userId, startTime) {
|
|
13499
13667
|
if (mw) {
|
|
13500
|
-
mw.execute(
|
|
13668
|
+
mw.execute(Hook.PERMISSION_AFTER_RESOLVE, {
|
|
13501
13669
|
sessionId: this.session.id,
|
|
13502
13670
|
requestId,
|
|
13503
13671
|
decision,
|
|
@@ -13511,6 +13679,7 @@ var SessionBridge = class {
|
|
|
13511
13679
|
|
|
13512
13680
|
// src/core/sessions/session-factory.ts
|
|
13513
13681
|
init_log();
|
|
13682
|
+
init_events();
|
|
13514
13683
|
var log7 = createChildLogger({ module: "session-factory" });
|
|
13515
13684
|
var SessionFactory = class {
|
|
13516
13685
|
constructor(agentManager, sessionManager, speechServiceAccessor, eventBus, instanceRoot) {
|
|
@@ -13553,7 +13722,7 @@ var SessionFactory = class {
|
|
|
13553
13722
|
threadId: ""
|
|
13554
13723
|
// threadId is assigned after session creation
|
|
13555
13724
|
};
|
|
13556
|
-
const result = await this.middlewareChain.execute(
|
|
13725
|
+
const result = await this.middlewareChain.execute(Hook.SESSION_BEFORE_CREATE, payload, async (p) => p);
|
|
13557
13726
|
if (!result) throw new Error("Session creation blocked by middleware");
|
|
13558
13727
|
createParams = {
|
|
13559
13728
|
...params,
|
|
@@ -13562,6 +13731,7 @@ var SessionFactory = class {
|
|
|
13562
13731
|
channelId: result.channelId
|
|
13563
13732
|
};
|
|
13564
13733
|
}
|
|
13734
|
+
const configAllowedPaths = this.configManager?.get().workspace?.security?.allowedPaths ?? [];
|
|
13565
13735
|
let agentInstance;
|
|
13566
13736
|
try {
|
|
13567
13737
|
if (createParams.resumeAgentSessionId) {
|
|
@@ -13569,7 +13739,8 @@ var SessionFactory = class {
|
|
|
13569
13739
|
agentInstance = await this.agentManager.resume(
|
|
13570
13740
|
createParams.agentName,
|
|
13571
13741
|
createParams.workingDirectory,
|
|
13572
|
-
createParams.resumeAgentSessionId
|
|
13742
|
+
createParams.resumeAgentSessionId,
|
|
13743
|
+
configAllowedPaths
|
|
13573
13744
|
);
|
|
13574
13745
|
} catch (resumeErr) {
|
|
13575
13746
|
log7.warn(
|
|
@@ -13578,13 +13749,15 @@ var SessionFactory = class {
|
|
|
13578
13749
|
);
|
|
13579
13750
|
agentInstance = await this.agentManager.spawn(
|
|
13580
13751
|
createParams.agentName,
|
|
13581
|
-
createParams.workingDirectory
|
|
13752
|
+
createParams.workingDirectory,
|
|
13753
|
+
configAllowedPaths
|
|
13582
13754
|
);
|
|
13583
13755
|
}
|
|
13584
13756
|
} else {
|
|
13585
13757
|
agentInstance = await this.agentManager.spawn(
|
|
13586
13758
|
createParams.agentName,
|
|
13587
|
-
createParams.workingDirectory
|
|
13759
|
+
createParams.workingDirectory,
|
|
13760
|
+
configAllowedPaths
|
|
13588
13761
|
);
|
|
13589
13762
|
}
|
|
13590
13763
|
} catch (err) {
|
|
@@ -13616,7 +13789,7 @@ var SessionFactory = class {
|
|
|
13616
13789
|
message: guidanceLines.join("\n")
|
|
13617
13790
|
};
|
|
13618
13791
|
const failedSessionId = createParams.existingSessionId ?? `failed-${Date.now()}`;
|
|
13619
|
-
this.eventBus.emit(
|
|
13792
|
+
this.eventBus.emit(BusEvent.AGENT_EVENT, {
|
|
13620
13793
|
sessionId: failedSessionId,
|
|
13621
13794
|
event: guidance
|
|
13622
13795
|
});
|
|
@@ -13642,11 +13815,13 @@ var SessionFactory = class {
|
|
|
13642
13815
|
}
|
|
13643
13816
|
session.applySpawnResponse(agentInstance.initialSessionResponse, agentInstance.agentCapabilities);
|
|
13644
13817
|
this.sessionManager.registerSession(session);
|
|
13645
|
-
|
|
13646
|
-
|
|
13647
|
-
|
|
13648
|
-
|
|
13649
|
-
|
|
13818
|
+
if (!session.isAssistant) {
|
|
13819
|
+
this.eventBus.emit(BusEvent.SESSION_CREATED, {
|
|
13820
|
+
sessionId: session.id,
|
|
13821
|
+
agent: session.agentName,
|
|
13822
|
+
status: session.status
|
|
13823
|
+
});
|
|
13824
|
+
}
|
|
13650
13825
|
return session;
|
|
13651
13826
|
}
|
|
13652
13827
|
/**
|
|
@@ -13664,6 +13839,7 @@ var SessionFactory = class {
|
|
|
13664
13839
|
if (!this.sessionStore || !this.createFullSession) return null;
|
|
13665
13840
|
const record = this.sessionStore.get(sessionId);
|
|
13666
13841
|
if (!record) return null;
|
|
13842
|
+
if (record.isAssistant) return null;
|
|
13667
13843
|
if (record.status === "error" || record.status === "cancelled") return null;
|
|
13668
13844
|
const existing = this.resumeLocks.get(sessionId);
|
|
13669
13845
|
if (existing) return existing;
|
|
@@ -13731,6 +13907,7 @@ var SessionFactory = class {
|
|
|
13731
13907
|
log7.debug({ threadId, channelId }, "No session record found for thread");
|
|
13732
13908
|
return null;
|
|
13733
13909
|
}
|
|
13910
|
+
if (record.isAssistant) return null;
|
|
13734
13911
|
if (record.status === "error" || record.status === "cancelled") {
|
|
13735
13912
|
log7.warn(
|
|
13736
13913
|
{ threadId, sessionId: record.sessionId, status: record.status },
|
|
@@ -13890,9 +14067,9 @@ var SessionFactory = class {
|
|
|
13890
14067
|
return { session, contextResult };
|
|
13891
14068
|
}
|
|
13892
14069
|
wireSideEffects(session, deps) {
|
|
13893
|
-
session.on(
|
|
14070
|
+
session.on(SessionEv.AGENT_EVENT, (event) => {
|
|
13894
14071
|
if (event.type !== "usage") return;
|
|
13895
|
-
deps.eventBus.emit(
|
|
14072
|
+
deps.eventBus.emit(BusEvent.USAGE_RECORDED, {
|
|
13896
14073
|
sessionId: session.id,
|
|
13897
14074
|
agentName: session.agentName,
|
|
13898
14075
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -13901,7 +14078,7 @@ var SessionFactory = class {
|
|
|
13901
14078
|
cost: event.cost
|
|
13902
14079
|
});
|
|
13903
14080
|
});
|
|
13904
|
-
session.on(
|
|
14081
|
+
session.on(SessionEv.STATUS_CHANGE, (_from, to) => {
|
|
13905
14082
|
if ((to === "finished" || to === "cancelled") && deps.tunnelService) {
|
|
13906
14083
|
deps.tunnelService.stopBySession(session.id).then((stopped) => {
|
|
13907
14084
|
for (const entry of stopped) {
|
|
@@ -13981,6 +14158,14 @@ var JsonFileSessionStore = class {
|
|
|
13981
14158
|
}
|
|
13982
14159
|
return void 0;
|
|
13983
14160
|
}
|
|
14161
|
+
findAssistant(channelId) {
|
|
14162
|
+
for (const record of this.records.values()) {
|
|
14163
|
+
if (record.isAssistant === true && record.channelId === channelId) {
|
|
14164
|
+
return record;
|
|
14165
|
+
}
|
|
14166
|
+
}
|
|
14167
|
+
return void 0;
|
|
14168
|
+
}
|
|
13984
14169
|
list(channelId) {
|
|
13985
14170
|
const all = [...this.records.values()];
|
|
13986
14171
|
if (channelId) return all.filter((r) => r.channelId === channelId);
|
|
@@ -14060,6 +14245,8 @@ var JsonFileSessionStore = class {
|
|
|
14060
14245
|
for (const [id, record] of this.records) {
|
|
14061
14246
|
if (record.status === "active" || record.status === "initializing")
|
|
14062
14247
|
continue;
|
|
14248
|
+
if (record.isAssistant === true)
|
|
14249
|
+
continue;
|
|
14063
14250
|
const raw = record.lastActiveAt;
|
|
14064
14251
|
if (!raw) continue;
|
|
14065
14252
|
const lastActive = new Date(raw).getTime();
|
|
@@ -14088,6 +14275,7 @@ init_agent_registry();
|
|
|
14088
14275
|
// src/core/agent-switch-handler.ts
|
|
14089
14276
|
init_agent_registry();
|
|
14090
14277
|
init_log();
|
|
14278
|
+
init_events();
|
|
14091
14279
|
var log9 = createChildLogger({ module: "agent-switch" });
|
|
14092
14280
|
var AgentSwitchHandler = class {
|
|
14093
14281
|
constructor(deps) {
|
|
@@ -14113,7 +14301,7 @@ var AgentSwitchHandler = class {
|
|
|
14113
14301
|
if (!agentDef) throw new Error(`Agent "${toAgent}" is not installed`);
|
|
14114
14302
|
const fromAgent = session.agentName;
|
|
14115
14303
|
const middlewareChain = this.deps.getMiddlewareChain();
|
|
14116
|
-
const result = await middlewareChain?.execute(
|
|
14304
|
+
const result = await middlewareChain?.execute(Hook.AGENT_BEFORE_SWITCH, {
|
|
14117
14305
|
sessionId,
|
|
14118
14306
|
fromAgent,
|
|
14119
14307
|
toAgent
|
|
@@ -14127,9 +14315,9 @@ var AgentSwitchHandler = class {
|
|
|
14127
14315
|
type: "system_message",
|
|
14128
14316
|
message: `Switching from ${fromAgent} to ${toAgent}...`
|
|
14129
14317
|
};
|
|
14130
|
-
session.emit(
|
|
14131
|
-
eventBus.emit(
|
|
14132
|
-
eventBus.emit(
|
|
14318
|
+
session.emit(SessionEv.AGENT_EVENT, startEvent);
|
|
14319
|
+
eventBus.emit(BusEvent.AGENT_EVENT, { sessionId, event: startEvent });
|
|
14320
|
+
eventBus.emit(BusEvent.SESSION_AGENT_SWITCH, {
|
|
14133
14321
|
sessionId,
|
|
14134
14322
|
fromAgent,
|
|
14135
14323
|
toAgent,
|
|
@@ -14153,11 +14341,12 @@ var AgentSwitchHandler = class {
|
|
|
14153
14341
|
}
|
|
14154
14342
|
const fromAgentSessionId = session.agentSessionId;
|
|
14155
14343
|
const fileService = this.deps.getService("file-service");
|
|
14344
|
+
const configAllowedPaths = configManager.get().workspace?.security?.allowedPaths ?? [];
|
|
14156
14345
|
try {
|
|
14157
14346
|
await session.switchAgent(toAgent, async () => {
|
|
14158
14347
|
if (canResume) {
|
|
14159
14348
|
try {
|
|
14160
|
-
const instance2 = await agentManager.resume(toAgent, session.workingDirectory, lastEntry.agentSessionId);
|
|
14349
|
+
const instance2 = await agentManager.resume(toAgent, session.workingDirectory, lastEntry.agentSessionId, configAllowedPaths);
|
|
14161
14350
|
if (fileService) instance2.addAllowedPath(fileService.baseDir);
|
|
14162
14351
|
resumed = true;
|
|
14163
14352
|
return instance2;
|
|
@@ -14165,7 +14354,7 @@ var AgentSwitchHandler = class {
|
|
|
14165
14354
|
log9.warn({ sessionId, toAgent }, "Resume failed, falling back to new agent with context injection");
|
|
14166
14355
|
}
|
|
14167
14356
|
}
|
|
14168
|
-
const instance = await agentManager.spawn(toAgent, session.workingDirectory);
|
|
14357
|
+
const instance = await agentManager.spawn(toAgent, session.workingDirectory, configAllowedPaths);
|
|
14169
14358
|
if (fileService) instance.addAllowedPath(fileService.baseDir);
|
|
14170
14359
|
try {
|
|
14171
14360
|
const contextService = this.deps.getService("context");
|
|
@@ -14189,9 +14378,9 @@ var AgentSwitchHandler = class {
|
|
|
14189
14378
|
type: "system_message",
|
|
14190
14379
|
message: resumed ? `Switched to ${toAgent} (resumed previous session).` : `Switched to ${toAgent} (new session).`
|
|
14191
14380
|
};
|
|
14192
|
-
session.emit(
|
|
14193
|
-
eventBus.emit(
|
|
14194
|
-
eventBus.emit(
|
|
14381
|
+
session.emit(SessionEv.AGENT_EVENT, successEvent);
|
|
14382
|
+
eventBus.emit(BusEvent.AGENT_EVENT, { sessionId, event: successEvent });
|
|
14383
|
+
eventBus.emit(BusEvent.SESSION_AGENT_SWITCH, {
|
|
14195
14384
|
sessionId,
|
|
14196
14385
|
fromAgent,
|
|
14197
14386
|
toAgent,
|
|
@@ -14204,9 +14393,9 @@ var AgentSwitchHandler = class {
|
|
|
14204
14393
|
type: "system_message",
|
|
14205
14394
|
message: `Failed to switch to ${toAgent}: ${errorMessage}`
|
|
14206
14395
|
};
|
|
14207
|
-
session.emit(
|
|
14208
|
-
eventBus.emit(
|
|
14209
|
-
eventBus.emit(
|
|
14396
|
+
session.emit(SessionEv.AGENT_EVENT, failedEvent);
|
|
14397
|
+
eventBus.emit(BusEvent.AGENT_EVENT, { sessionId, event: failedEvent });
|
|
14398
|
+
eventBus.emit(BusEvent.SESSION_AGENT_SWITCH, {
|
|
14210
14399
|
sessionId,
|
|
14211
14400
|
fromAgent,
|
|
14212
14401
|
toAgent,
|
|
@@ -14255,7 +14444,7 @@ var AgentSwitchHandler = class {
|
|
|
14255
14444
|
currentPromptCount: 0,
|
|
14256
14445
|
agentSwitchHistory: session.agentSwitchHistory
|
|
14257
14446
|
});
|
|
14258
|
-
middlewareChain?.execute(
|
|
14447
|
+
middlewareChain?.execute(Hook.AGENT_AFTER_SWITCH, {
|
|
14259
14448
|
sessionId,
|
|
14260
14449
|
fromAgent,
|
|
14261
14450
|
toAgent,
|
|
@@ -14940,7 +15129,7 @@ function createPluginContext(opts) {
|
|
|
14940
15129
|
}
|
|
14941
15130
|
};
|
|
14942
15131
|
const baseLog = opts.log ?? noopLog;
|
|
14943
|
-
const
|
|
15132
|
+
const log36 = typeof baseLog.child === "function" ? baseLog.child({ plugin: pluginName }) : baseLog;
|
|
14944
15133
|
const storageImpl = new PluginStorageImpl(storagePath);
|
|
14945
15134
|
const storage = {
|
|
14946
15135
|
async get(key) {
|
|
@@ -14967,7 +15156,7 @@ function createPluginContext(opts) {
|
|
|
14967
15156
|
const ctx = {
|
|
14968
15157
|
pluginName,
|
|
14969
15158
|
pluginConfig,
|
|
14970
|
-
log:
|
|
15159
|
+
log: log36,
|
|
14971
15160
|
storage,
|
|
14972
15161
|
on(event, handler) {
|
|
14973
15162
|
requirePermission(permissions, "events:read", "on()");
|
|
@@ -15002,7 +15191,7 @@ function createPluginContext(opts) {
|
|
|
15002
15191
|
const registry = serviceRegistry.get("command-registry");
|
|
15003
15192
|
if (registry && typeof registry.register === "function") {
|
|
15004
15193
|
registry.register(def, pluginName);
|
|
15005
|
-
|
|
15194
|
+
log36.debug(`Command '/${def.name}' registered`);
|
|
15006
15195
|
}
|
|
15007
15196
|
},
|
|
15008
15197
|
async sendMessage(_sessionId, _content) {
|
|
@@ -15045,7 +15234,7 @@ function createPluginContext(opts) {
|
|
|
15045
15234
|
const registry = serviceRegistry.get("field-registry");
|
|
15046
15235
|
if (registry && typeof registry.register === "function") {
|
|
15047
15236
|
registry.register(pluginName, fields);
|
|
15048
|
-
|
|
15237
|
+
log36.debug(`Registered ${fields.length} editable field(s) for ${pluginName}`);
|
|
15049
15238
|
}
|
|
15050
15239
|
},
|
|
15051
15240
|
get sessions() {
|
|
@@ -15097,6 +15286,7 @@ function createPluginContext(opts) {
|
|
|
15097
15286
|
}
|
|
15098
15287
|
|
|
15099
15288
|
// src/core/plugin/lifecycle-manager.ts
|
|
15289
|
+
init_events();
|
|
15100
15290
|
var SETUP_TIMEOUT_MS = 3e4;
|
|
15101
15291
|
var TEARDOWN_TIMEOUT_MS = 1e4;
|
|
15102
15292
|
function withTimeout(promise, ms, label) {
|
|
@@ -15242,7 +15432,7 @@ var LifecycleManager = class {
|
|
|
15242
15432
|
}
|
|
15243
15433
|
const registryEntry = this.pluginRegistry?.get(plugin.name);
|
|
15244
15434
|
if (registryEntry && registryEntry.enabled === false) {
|
|
15245
|
-
this.eventBus?.emit(
|
|
15435
|
+
this.eventBus?.emit(BusEvent.PLUGIN_DISABLED, { name: plugin.name });
|
|
15246
15436
|
continue;
|
|
15247
15437
|
}
|
|
15248
15438
|
if (registryEntry && plugin.migrate && registryEntry.version !== plugin.version && this.settingsManager) {
|
|
@@ -15285,7 +15475,7 @@ var LifecycleManager = class {
|
|
|
15285
15475
|
if (!validation.valid) {
|
|
15286
15476
|
this._failed.add(plugin.name);
|
|
15287
15477
|
this.getPluginLogger(plugin.name).error(`Settings validation failed: ${validation.errors?.join("; ")}`);
|
|
15288
|
-
this.eventBus?.emit(
|
|
15478
|
+
this.eventBus?.emit(BusEvent.PLUGIN_FAILED, { name: plugin.name, error: `Settings validation failed: ${validation.errors?.join("; ")}` });
|
|
15289
15479
|
continue;
|
|
15290
15480
|
}
|
|
15291
15481
|
}
|
|
@@ -15308,13 +15498,13 @@ var LifecycleManager = class {
|
|
|
15308
15498
|
await withTimeout(plugin.setup(ctx), SETUP_TIMEOUT_MS, `${plugin.name}.setup()`);
|
|
15309
15499
|
this.contexts.set(plugin.name, ctx);
|
|
15310
15500
|
this._loaded.add(plugin.name);
|
|
15311
|
-
this.eventBus?.emit(
|
|
15501
|
+
this.eventBus?.emit(BusEvent.PLUGIN_LOADED, { name: plugin.name, version: plugin.version });
|
|
15312
15502
|
} catch (err) {
|
|
15313
15503
|
this._failed.add(plugin.name);
|
|
15314
15504
|
ctx.cleanup();
|
|
15315
15505
|
console.error(`[lifecycle] Plugin ${plugin.name} setup() FAILED:`, err);
|
|
15316
15506
|
this.getPluginLogger(plugin.name).error(`setup() failed: ${err}`);
|
|
15317
|
-
this.eventBus?.emit(
|
|
15507
|
+
this.eventBus?.emit(BusEvent.PLUGIN_FAILED, { name: plugin.name, error: String(err) });
|
|
15318
15508
|
}
|
|
15319
15509
|
}
|
|
15320
15510
|
}
|
|
@@ -15335,7 +15525,7 @@ var LifecycleManager = class {
|
|
|
15335
15525
|
this._loaded.delete(name);
|
|
15336
15526
|
this._failed.delete(name);
|
|
15337
15527
|
this.loadOrder = this.loadOrder.filter((p) => p.name !== name);
|
|
15338
|
-
this.eventBus?.emit(
|
|
15528
|
+
this.eventBus?.emit(BusEvent.PLUGIN_UNLOADED, { name });
|
|
15339
15529
|
}
|
|
15340
15530
|
async shutdown() {
|
|
15341
15531
|
const reversed = [...this.loadOrder].reverse();
|
|
@@ -15352,7 +15542,7 @@ var LifecycleManager = class {
|
|
|
15352
15542
|
ctx.cleanup();
|
|
15353
15543
|
this.contexts.delete(plugin.name);
|
|
15354
15544
|
}
|
|
15355
|
-
this.eventBus?.emit(
|
|
15545
|
+
this.eventBus?.emit(BusEvent.PLUGIN_UNLOADED, { name: plugin.name });
|
|
15356
15546
|
}
|
|
15357
15547
|
this._loaded.clear();
|
|
15358
15548
|
this.loadOrder = [];
|
|
@@ -15500,21 +15690,25 @@ var AssistantManager = class {
|
|
|
15500
15690
|
this.registry = registry;
|
|
15501
15691
|
}
|
|
15502
15692
|
sessions = /* @__PURE__ */ new Map();
|
|
15503
|
-
respawning = /* @__PURE__ */ new Set();
|
|
15504
15693
|
pendingSystemPrompts = /* @__PURE__ */ new Map();
|
|
15505
|
-
async
|
|
15694
|
+
async getOrSpawn(channelId, threadId) {
|
|
15695
|
+
const existing = this.core.sessionStore?.findAssistant(channelId);
|
|
15506
15696
|
const session = await this.core.createSession({
|
|
15507
15697
|
channelId,
|
|
15508
15698
|
agentName: this.core.configManager.get().defaultAgent,
|
|
15509
15699
|
workingDirectory: this.core.configManager.resolveWorkspace(),
|
|
15510
15700
|
initialName: "Assistant",
|
|
15511
15701
|
isAssistant: true,
|
|
15512
|
-
threadId
|
|
15702
|
+
threadId,
|
|
15703
|
+
existingSessionId: existing?.sessionId
|
|
15513
15704
|
});
|
|
15514
15705
|
this.sessions.set(channelId, session);
|
|
15515
15706
|
const systemPrompt = this.registry.buildSystemPrompt(channelId);
|
|
15516
15707
|
this.pendingSystemPrompts.set(channelId, systemPrompt);
|
|
15517
|
-
log15.info(
|
|
15708
|
+
log15.info(
|
|
15709
|
+
{ sessionId: session.id, channelId, reused: !!existing },
|
|
15710
|
+
existing ? "Assistant session reused (system prompt deferred)" : "Assistant spawned (system prompt deferred)"
|
|
15711
|
+
);
|
|
15518
15712
|
return session;
|
|
15519
15713
|
}
|
|
15520
15714
|
get(channelId) {
|
|
@@ -15535,19 +15729,6 @@ var AssistantManager = class {
|
|
|
15535
15729
|
}
|
|
15536
15730
|
return false;
|
|
15537
15731
|
}
|
|
15538
|
-
async respawn(channelId, threadId) {
|
|
15539
|
-
if (this.respawning.has(channelId)) {
|
|
15540
|
-
return this.sessions.get(channelId);
|
|
15541
|
-
}
|
|
15542
|
-
this.respawning.add(channelId);
|
|
15543
|
-
try {
|
|
15544
|
-
const old = this.sessions.get(channelId);
|
|
15545
|
-
if (old) await old.destroy();
|
|
15546
|
-
return await this.spawn(channelId, threadId);
|
|
15547
|
-
} finally {
|
|
15548
|
-
this.respawning.delete(channelId);
|
|
15549
|
-
}
|
|
15550
|
-
}
|
|
15551
15732
|
};
|
|
15552
15733
|
|
|
15553
15734
|
// src/core/assistant/sections/sessions.ts
|
|
@@ -15717,6 +15898,7 @@ function registerCoreMenuItems(registry) {
|
|
|
15717
15898
|
|
|
15718
15899
|
// src/core/core.ts
|
|
15719
15900
|
init_log();
|
|
15901
|
+
init_events();
|
|
15720
15902
|
var log16 = createChildLogger({ module: "core" });
|
|
15721
15903
|
var OpenACPCore = class {
|
|
15722
15904
|
configManager;
|
|
@@ -15951,7 +16133,7 @@ var OpenACPCore = class {
|
|
|
15951
16133
|
);
|
|
15952
16134
|
if (this.lifecycleManager?.middlewareChain) {
|
|
15953
16135
|
const result = await this.lifecycleManager.middlewareChain.execute(
|
|
15954
|
-
|
|
16136
|
+
Hook.MESSAGE_INCOMING,
|
|
15955
16137
|
message,
|
|
15956
16138
|
async (msg) => msg
|
|
15957
16139
|
);
|
|
@@ -16003,9 +16185,10 @@ ${text3}`;
|
|
|
16003
16185
|
}
|
|
16004
16186
|
}
|
|
16005
16187
|
const sourceAdapterId = message.routing?.sourceAdapterId ?? message.channelId;
|
|
16188
|
+
const routing = sourceAdapterId !== message.routing?.sourceAdapterId ? { ...message.routing, sourceAdapterId } : message.routing;
|
|
16006
16189
|
if (sourceAdapterId && sourceAdapterId !== "sse" && sourceAdapterId !== "api") {
|
|
16007
16190
|
const turnId = nanoid3(8);
|
|
16008
|
-
this.eventBus.emit(
|
|
16191
|
+
this.eventBus.emit(BusEvent.MESSAGE_QUEUED, {
|
|
16009
16192
|
sessionId: session.id,
|
|
16010
16193
|
turnId,
|
|
16011
16194
|
text: text3,
|
|
@@ -16014,9 +16197,9 @@ ${text3}`;
|
|
|
16014
16197
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16015
16198
|
queueDepth: session.queueDepth
|
|
16016
16199
|
});
|
|
16017
|
-
await session.enqueuePrompt(text3, message.attachments,
|
|
16200
|
+
await session.enqueuePrompt(text3, message.attachments, routing, turnId);
|
|
16018
16201
|
} else {
|
|
16019
|
-
await session.enqueuePrompt(text3, message.attachments,
|
|
16202
|
+
await session.enqueuePrompt(text3, message.attachments, routing);
|
|
16020
16203
|
}
|
|
16021
16204
|
}
|
|
16022
16205
|
// --- Unified Session Creation Pipeline ---
|
|
@@ -16060,6 +16243,7 @@ ${text3}`;
|
|
|
16060
16243
|
createdAt: session.createdAt.toISOString(),
|
|
16061
16244
|
lastActiveAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
16062
16245
|
name: session.name,
|
|
16246
|
+
isAssistant: params.isAssistant,
|
|
16063
16247
|
platform: platform2,
|
|
16064
16248
|
platforms,
|
|
16065
16249
|
firstAgent: session.firstAgent,
|
|
@@ -16075,7 +16259,7 @@ ${text3}`;
|
|
|
16075
16259
|
log16.warn({ err, sessionId: session.id }, "Failed to flush pending skill commands");
|
|
16076
16260
|
});
|
|
16077
16261
|
if (params.createThread && session.threadId) {
|
|
16078
|
-
this.eventBus.emit(
|
|
16262
|
+
this.eventBus.emit(BusEvent.SESSION_THREAD_READY, {
|
|
16079
16263
|
sessionId: session.id,
|
|
16080
16264
|
channelId: params.channelId,
|
|
16081
16265
|
threadId: session.threadId
|
|
@@ -16099,6 +16283,36 @@ ${text3}`;
|
|
|
16099
16283
|
);
|
|
16100
16284
|
return allowOption.id;
|
|
16101
16285
|
};
|
|
16286
|
+
session.on(SessionEv.NAMED, async (name) => {
|
|
16287
|
+
await this.sessionManager.patchRecord(session.id, { name });
|
|
16288
|
+
this.eventBus.emit(BusEvent.SESSION_UPDATED, { sessionId: session.id, name });
|
|
16289
|
+
});
|
|
16290
|
+
const mw = () => this.lifecycleManager?.middlewareChain;
|
|
16291
|
+
session.on(SessionEv.AGENT_EVENT, async (event) => {
|
|
16292
|
+
let processedEvent = event;
|
|
16293
|
+
const chain = mw();
|
|
16294
|
+
if (chain) {
|
|
16295
|
+
const result = await chain.execute(Hook.AGENT_BEFORE_EVENT, { sessionId: session.id, event }, async (e) => e);
|
|
16296
|
+
if (!result) return;
|
|
16297
|
+
processedEvent = result.event;
|
|
16298
|
+
}
|
|
16299
|
+
if (processedEvent.type === "session_end") {
|
|
16300
|
+
session.finish(processedEvent.reason);
|
|
16301
|
+
} else if (processedEvent.type === "error") {
|
|
16302
|
+
session.fail(processedEvent.message);
|
|
16303
|
+
}
|
|
16304
|
+
this.eventBus.emit(BusEvent.AGENT_EVENT, { sessionId: session.id, event: processedEvent });
|
|
16305
|
+
});
|
|
16306
|
+
session.on(SessionEv.STATUS_CHANGE, (_from, to) => {
|
|
16307
|
+
this.sessionManager.patchRecord(session.id, {
|
|
16308
|
+
status: to,
|
|
16309
|
+
lastActiveAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
16310
|
+
});
|
|
16311
|
+
this.eventBus.emit(BusEvent.SESSION_UPDATED, { sessionId: session.id, status: to });
|
|
16312
|
+
});
|
|
16313
|
+
session.on(SessionEv.PROMPT_COUNT_CHANGED, (count) => {
|
|
16314
|
+
this.sessionManager.patchRecord(session.id, { currentPromptCount: count });
|
|
16315
|
+
});
|
|
16102
16316
|
}
|
|
16103
16317
|
this.sessionFactory.wireSideEffects(session, {
|
|
16104
16318
|
eventBus: this.eventBus,
|