@openacp/cli 2026.413.1 → 2026.414.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-Dg1nGCYa.d.ts → channel-bWC2vDOv.d.ts} +21 -6
- package/dist/cli.js +251 -110
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +66 -10
- package/dist/index.js +146 -59
- package/dist/index.js.map +1 -1
- package/dist/testing.d.ts +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1870,6 +1870,8 @@ var init_events = __esm({
|
|
|
1870
1870
|
MESSAGE_QUEUED: "message:queued",
|
|
1871
1871
|
/** Fired when a queued message starts processing. */
|
|
1872
1872
|
MESSAGE_PROCESSING: "message:processing",
|
|
1873
|
+
/** Fired when a queued message is rejected (e.g. blocked by middleware). */
|
|
1874
|
+
MESSAGE_FAILED: "message:failed",
|
|
1873
1875
|
// --- System lifecycle ---
|
|
1874
1876
|
/** Fired after kernel (core + plugin infrastructure) has booted. */
|
|
1875
1877
|
KERNEL_BOOTED: "kernel:booted",
|
|
@@ -2031,7 +2033,16 @@ function createSecurityPlugin() {
|
|
|
2031
2033
|
handler: async (payload, next) => {
|
|
2032
2034
|
const access2 = await guard.checkAccess(payload);
|
|
2033
2035
|
if (!access2.allowed) {
|
|
2034
|
-
ctx.log.info(`Access denied: ${access2.reason}`);
|
|
2036
|
+
ctx.log.info(`Access denied for user=${payload.userId} channel=${payload.channelId}: ${access2.reason}`);
|
|
2037
|
+
const adapter = core.adapters?.get?.(payload.channelId);
|
|
2038
|
+
if (adapter?.sendMessage && payload.threadId) {
|
|
2039
|
+
adapter.sendMessage(payload.threadId, {
|
|
2040
|
+
type: "error",
|
|
2041
|
+
message: `Access denied: ${access2.reason ?? "You are not allowed to use this service."}`
|
|
2042
|
+
}).catch((err) => {
|
|
2043
|
+
ctx.log.warn(`Failed to send access-denied message to adapter: ${err}`);
|
|
2044
|
+
});
|
|
2045
|
+
}
|
|
2035
2046
|
return null;
|
|
2036
2047
|
}
|
|
2037
2048
|
return next();
|
|
@@ -2622,6 +2633,22 @@ function registerSetupRoutes(app, deps) {
|
|
|
2622
2633
|
if (user2) return user2;
|
|
2623
2634
|
}
|
|
2624
2635
|
const body = request.body;
|
|
2636
|
+
if (body?.identitySecret) {
|
|
2637
|
+
const oldToken = tokenStore?.getByIdentitySecret(body.identitySecret);
|
|
2638
|
+
if (!oldToken) {
|
|
2639
|
+
return reply.status(401).send({ error: "Invalid identity secret" });
|
|
2640
|
+
}
|
|
2641
|
+
const userId = tokenStore?.getUserId(oldToken.id);
|
|
2642
|
+
if (!userId) {
|
|
2643
|
+
return reply.status(401).send({ error: "No identity linked to this secret" });
|
|
2644
|
+
}
|
|
2645
|
+
await service.createIdentity(userId, {
|
|
2646
|
+
source: "api",
|
|
2647
|
+
platformId: auth.tokenId
|
|
2648
|
+
});
|
|
2649
|
+
tokenStore?.setUserId?.(auth.tokenId, userId);
|
|
2650
|
+
return service.getUser(userId);
|
|
2651
|
+
}
|
|
2625
2652
|
if (body?.linkCode) {
|
|
2626
2653
|
const entry = linkCodes.get(body.linkCode);
|
|
2627
2654
|
if (!entry || entry.expiresAt < Date.now()) {
|
|
@@ -2700,23 +2727,30 @@ function createIdentityPlugin() {
|
|
|
2700
2727
|
});
|
|
2701
2728
|
ctx.registerCommand({
|
|
2702
2729
|
name: "whoami",
|
|
2703
|
-
description: "Set your display name
|
|
2704
|
-
usage: "[
|
|
2730
|
+
description: "Set your username and display name",
|
|
2731
|
+
usage: "@username [Display Name]",
|
|
2705
2732
|
category: "plugin",
|
|
2706
2733
|
async handler(args2) {
|
|
2707
|
-
const
|
|
2708
|
-
if (!
|
|
2709
|
-
|
|
2710
|
-
|
|
2734
|
+
const raw = args2.raw.trim();
|
|
2735
|
+
if (!raw) return { type: "error", message: "Usage: /whoami @username [Display Name]" };
|
|
2736
|
+
const tokens = raw.split(/\s+/);
|
|
2737
|
+
const first = tokens[0];
|
|
2738
|
+
const usernameRaw = first.startsWith("@") ? first.slice(1) : first;
|
|
2739
|
+
if (!/^[a-zA-Z0-9_.-]+$/.test(usernameRaw)) {
|
|
2740
|
+
return { type: "error", message: "Invalid username. Only letters, numbers, _ . - allowed." };
|
|
2741
|
+
}
|
|
2742
|
+
const username = usernameRaw;
|
|
2743
|
+
const displayName = tokens.slice(1).join(" ") || void 0;
|
|
2711
2744
|
const identityId = formatIdentityId(args2.channelId, args2.userId);
|
|
2712
2745
|
const user = await service.getUserByIdentity(identityId);
|
|
2713
2746
|
if (!user) {
|
|
2714
|
-
return { type: "error", message: "
|
|
2747
|
+
return { type: "error", message: "Identity not found. Send a message first." };
|
|
2715
2748
|
}
|
|
2716
2749
|
try {
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2750
|
+
await service.updateUser(user.userId, { username, ...displayName && { displayName } });
|
|
2751
|
+
const parts = [`@${username}`];
|
|
2752
|
+
if (displayName) parts.push(`"${displayName}"`);
|
|
2753
|
+
return { type: "text", text: `\u2705 Profile updated: ${parts.join(" ")}` };
|
|
2720
2754
|
} catch (err) {
|
|
2721
2755
|
const message = err instanceof Error ? err.message : String(err);
|
|
2722
2756
|
return { type: "error", message };
|
|
@@ -8255,9 +8289,15 @@ var init_token_store = __esm({
|
|
|
8255
8289
|
return;
|
|
8256
8290
|
}
|
|
8257
8291
|
this.tokens.clear();
|
|
8292
|
+
let needsMigration = false;
|
|
8258
8293
|
for (const token of parsed.tokens) {
|
|
8294
|
+
if (!token.identitySecret) {
|
|
8295
|
+
token.identitySecret = randomBytes2(16).toString("hex");
|
|
8296
|
+
needsMigration = true;
|
|
8297
|
+
}
|
|
8259
8298
|
this.tokens.set(token.id, token);
|
|
8260
8299
|
}
|
|
8300
|
+
if (needsMigration) this.scheduleSave();
|
|
8261
8301
|
this.codes.clear();
|
|
8262
8302
|
for (const code of parsed.codes ?? []) {
|
|
8263
8303
|
this.codes.set(code.code, code);
|
|
@@ -8303,7 +8343,8 @@ var init_token_store = __esm({
|
|
|
8303
8343
|
scopes: opts.scopes,
|
|
8304
8344
|
createdAt: now.toISOString(),
|
|
8305
8345
|
refreshDeadline: new Date(now.getTime() + REFRESH_DEADLINE_MS).toISOString(),
|
|
8306
|
-
revoked: false
|
|
8346
|
+
revoked: false,
|
|
8347
|
+
identitySecret: randomBytes2(16).toString("hex")
|
|
8307
8348
|
};
|
|
8308
8349
|
this.tokens.set(token.id, token);
|
|
8309
8350
|
this.scheduleSave();
|
|
@@ -8372,6 +8413,19 @@ var init_token_store = __esm({
|
|
|
8372
8413
|
getUserId(tokenId) {
|
|
8373
8414
|
return this.tokens.get(tokenId)?.userId;
|
|
8374
8415
|
}
|
|
8416
|
+
/**
|
|
8417
|
+
* Looks up a non-revoked token by its identity secret.
|
|
8418
|
+
*
|
|
8419
|
+
* Used by the identity re-link flow: the App sends the old token's identitySecret
|
|
8420
|
+
* to prove continuity of identity when reconnecting with a new JWT.
|
|
8421
|
+
* Returns undefined if no match, or if the matching token is revoked.
|
|
8422
|
+
*/
|
|
8423
|
+
getByIdentitySecret(secret) {
|
|
8424
|
+
for (const token of this.tokens.values()) {
|
|
8425
|
+
if (!token.revoked && token.identitySecret === secret) return token;
|
|
8426
|
+
}
|
|
8427
|
+
return void 0;
|
|
8428
|
+
}
|
|
8375
8429
|
/**
|
|
8376
8430
|
* Generates a one-time authorization code that can be exchanged for a JWT.
|
|
8377
8431
|
*
|
|
@@ -8987,7 +9041,8 @@ var init_sse_manager = __esm({
|
|
|
8987
9041
|
BusEvent.PERMISSION_REQUEST,
|
|
8988
9042
|
BusEvent.PERMISSION_RESOLVED,
|
|
8989
9043
|
BusEvent.MESSAGE_QUEUED,
|
|
8990
|
-
BusEvent.MESSAGE_PROCESSING
|
|
9044
|
+
BusEvent.MESSAGE_PROCESSING,
|
|
9045
|
+
BusEvent.MESSAGE_FAILED
|
|
8991
9046
|
];
|
|
8992
9047
|
for (const eventName of events) {
|
|
8993
9048
|
const handler = (data) => {
|
|
@@ -9069,7 +9124,8 @@ data: ${JSON.stringify(data)}
|
|
|
9069
9124
|
BusEvent.PERMISSION_RESOLVED,
|
|
9070
9125
|
BusEvent.SESSION_UPDATED,
|
|
9071
9126
|
BusEvent.MESSAGE_QUEUED,
|
|
9072
|
-
BusEvent.MESSAGE_PROCESSING
|
|
9127
|
+
BusEvent.MESSAGE_PROCESSING,
|
|
9128
|
+
BusEvent.MESSAGE_FAILED
|
|
9073
9129
|
];
|
|
9074
9130
|
for (const res of this.sseConnections) {
|
|
9075
9131
|
const filter = res.sessionFilter;
|
|
@@ -9339,7 +9395,6 @@ var sessions_exports = {};
|
|
|
9339
9395
|
__export(sessions_exports, {
|
|
9340
9396
|
sessionRoutes: () => sessionRoutes
|
|
9341
9397
|
});
|
|
9342
|
-
import { nanoid as nanoid3 } from "nanoid";
|
|
9343
9398
|
async function sessionRoutes(app, deps) {
|
|
9344
9399
|
app.get("/", { preHandler: requireScopes("sessions:read") }, async () => {
|
|
9345
9400
|
const summaries = deps.core.sessionManager.listAllSessions();
|
|
@@ -9515,34 +9570,36 @@ async function sessionRoutes(app, deps) {
|
|
|
9515
9570
|
attachments = await resolveAttachments(fileService, sessionId, body.attachments);
|
|
9516
9571
|
}
|
|
9517
9572
|
const sourceAdapterId = body.sourceAdapterId ?? "sse";
|
|
9518
|
-
const turnId = body.turnId ?? nanoid3(8);
|
|
9519
9573
|
const userId = request.auth?.tokenId ?? "api";
|
|
9520
|
-
const
|
|
9521
|
-
|
|
9522
|
-
|
|
9523
|
-
|
|
9524
|
-
|
|
9525
|
-
|
|
9574
|
+
const result = await deps.core.handleMessageInSession(
|
|
9575
|
+
session,
|
|
9576
|
+
{ channelId: sourceAdapterId, userId, text: body.prompt, attachments },
|
|
9577
|
+
{ channelUser: { channelId: "sse", userId } },
|
|
9578
|
+
{ externalTurnId: body.turnId, responseAdapterId: body.responseAdapterId }
|
|
9579
|
+
);
|
|
9580
|
+
if (!result) {
|
|
9581
|
+
throw new AuthError("MESSAGE_BLOCKED", "Message was blocked by a middleware plugin.", 403);
|
|
9582
|
+
}
|
|
9583
|
+
return { ok: true, sessionId, queueDepth: result.queueDepth, turnId: result.turnId };
|
|
9584
|
+
}
|
|
9585
|
+
);
|
|
9586
|
+
app.get(
|
|
9587
|
+
"/:sessionId/queue",
|
|
9588
|
+
{ preHandler: requireScopes("sessions:read") },
|
|
9589
|
+
async (request) => {
|
|
9590
|
+
const { sessionId: rawId } = SessionIdParamSchema.parse(request.params);
|
|
9591
|
+
const sessionId = decodeURIComponent(rawId);
|
|
9592
|
+
const session = await deps.core.getOrResumeSessionById(sessionId);
|
|
9593
|
+
if (!session) {
|
|
9594
|
+
throw new NotFoundError(
|
|
9595
|
+
"SESSION_NOT_FOUND",
|
|
9596
|
+
`Session "${sessionId}" not found`
|
|
9526
9597
|
);
|
|
9527
9598
|
}
|
|
9528
|
-
deps.core.eventBus.emit(BusEvent.MESSAGE_QUEUED, {
|
|
9529
|
-
sessionId,
|
|
9530
|
-
turnId,
|
|
9531
|
-
text: body.prompt,
|
|
9532
|
-
sourceAdapterId,
|
|
9533
|
-
attachments,
|
|
9534
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
9535
|
-
queueDepth: session.queueDepth
|
|
9536
|
-
});
|
|
9537
|
-
await session.enqueuePrompt(body.prompt, attachments, {
|
|
9538
|
-
sourceAdapterId,
|
|
9539
|
-
responseAdapterId: body.responseAdapterId
|
|
9540
|
-
}, turnId, meta);
|
|
9541
9599
|
return {
|
|
9542
|
-
|
|
9543
|
-
|
|
9544
|
-
queueDepth: session.queueDepth
|
|
9545
|
-
turnId
|
|
9600
|
+
pending: session.queueItems,
|
|
9601
|
+
processing: session.promptRunning,
|
|
9602
|
+
queueDepth: session.queueDepth
|
|
9546
9603
|
};
|
|
9547
9604
|
}
|
|
9548
9605
|
);
|
|
@@ -9810,7 +9867,6 @@ var init_sessions2 = __esm({
|
|
|
9810
9867
|
init_error_handler();
|
|
9811
9868
|
init_auth();
|
|
9812
9869
|
init_attachment_utils();
|
|
9813
|
-
init_events();
|
|
9814
9870
|
init_sessions();
|
|
9815
9871
|
}
|
|
9816
9872
|
});
|
|
@@ -10792,7 +10848,8 @@ async function authRoutes(app, deps) {
|
|
|
10792
10848
|
tokenId: stored.id,
|
|
10793
10849
|
accessToken,
|
|
10794
10850
|
expiresAt,
|
|
10795
|
-
refreshDeadline: stored.refreshDeadline
|
|
10851
|
+
refreshDeadline: stored.refreshDeadline,
|
|
10852
|
+
identitySecret: stored.identitySecret
|
|
10796
10853
|
};
|
|
10797
10854
|
});
|
|
10798
10855
|
app.get("/tokens", {
|
|
@@ -11552,7 +11609,8 @@ function createApiServerPlugin() {
|
|
|
11552
11609
|
accessToken,
|
|
11553
11610
|
tokenId: token.id,
|
|
11554
11611
|
expiresAt: new Date(Date.now() + parseDuration2(code.expire)).toISOString(),
|
|
11555
|
-
refreshDeadline: token.refreshDeadline
|
|
11612
|
+
refreshDeadline: token.refreshDeadline,
|
|
11613
|
+
identitySecret: token.identitySecret
|
|
11556
11614
|
});
|
|
11557
11615
|
});
|
|
11558
11616
|
}, { auth: false });
|
|
@@ -12139,14 +12197,13 @@ async function sseRoutes(app, deps) {
|
|
|
12139
12197
|
}
|
|
12140
12198
|
attachments = await resolveAttachments(fileService, sessionId, body.attachments);
|
|
12141
12199
|
}
|
|
12142
|
-
const queueDepth = session.queueDepth + 1;
|
|
12143
12200
|
const userId = request.auth?.tokenId ?? "api";
|
|
12144
|
-
await deps.core.handleMessageInSession(
|
|
12201
|
+
const { turnId, queueDepth } = await deps.core.handleMessageInSession(
|
|
12145
12202
|
session,
|
|
12146
12203
|
{ channelId: "sse", userId, text: body.prompt, attachments },
|
|
12147
12204
|
{ channelUser: { channelId: "sse", userId } }
|
|
12148
12205
|
);
|
|
12149
|
-
return { ok: true, sessionId, queueDepth };
|
|
12206
|
+
return { ok: true, sessionId, queueDepth, turnId };
|
|
12150
12207
|
}
|
|
12151
12208
|
);
|
|
12152
12209
|
app.post(
|
|
@@ -16363,7 +16420,7 @@ var init_commands3 = __esm({
|
|
|
16363
16420
|
|
|
16364
16421
|
// src/plugins/telegram/permissions.ts
|
|
16365
16422
|
import { InlineKeyboard as InlineKeyboard11 } from "grammy";
|
|
16366
|
-
import { nanoid as
|
|
16423
|
+
import { nanoid as nanoid3 } from "nanoid";
|
|
16367
16424
|
var log26, PermissionHandler;
|
|
16368
16425
|
var init_permissions = __esm({
|
|
16369
16426
|
"src/plugins/telegram/permissions.ts"() {
|
|
@@ -16390,7 +16447,7 @@ var init_permissions = __esm({
|
|
|
16390
16447
|
*/
|
|
16391
16448
|
async sendPermissionRequest(session, request) {
|
|
16392
16449
|
const threadId = Number(session.threadId);
|
|
16393
|
-
const callbackKey =
|
|
16450
|
+
const callbackKey = nanoid3(8);
|
|
16394
16451
|
this.pending.set(callbackKey, {
|
|
16395
16452
|
sessionId: session.id,
|
|
16396
16453
|
requestId: request.id,
|
|
@@ -18401,7 +18458,11 @@ var init_adapter2 = __esm({
|
|
|
18401
18458
|
}
|
|
18402
18459
|
return prev(method, payload, signal);
|
|
18403
18460
|
});
|
|
18404
|
-
|
|
18461
|
+
const onCommandsReady = ({ commands }) => {
|
|
18462
|
+
this.core.eventBus.off(BusEvent.SYSTEM_COMMANDS_READY, onCommandsReady);
|
|
18463
|
+
this.syncCommandsWithRetry(commands);
|
|
18464
|
+
};
|
|
18465
|
+
this.core.eventBus.on(BusEvent.SYSTEM_COMMANDS_READY, onCommandsReady);
|
|
18405
18466
|
this.bot.use((ctx, next) => {
|
|
18406
18467
|
const chatId = ctx.chat?.id ?? ctx.callbackQuery?.message?.chat?.id;
|
|
18407
18468
|
if (chatId !== this.telegramConfig.chatId) return;
|
|
@@ -18627,12 +18688,16 @@ ${p2}` : p2;
|
|
|
18627
18688
|
throw new Error("unreachable");
|
|
18628
18689
|
}
|
|
18629
18690
|
/**
|
|
18630
|
-
*
|
|
18631
|
-
*
|
|
18691
|
+
* Sync Telegram autocomplete commands after all plugins are ready.
|
|
18692
|
+
* Merges STATIC_COMMANDS (hardcoded system commands) with plugin commands
|
|
18693
|
+
* from the registry, deduplicating by command name. Non-critical.
|
|
18632
18694
|
*/
|
|
18633
|
-
|
|
18695
|
+
syncCommandsWithRetry(registryCommands) {
|
|
18696
|
+
const staticNames = new Set(STATIC_COMMANDS.map((c3) => c3.command));
|
|
18697
|
+
const pluginCommands = registryCommands.filter((c3) => c3.category === "plugin" && !staticNames.has(c3.name) && /^[a-z0-9_]+$/.test(c3.name)).map((c3) => ({ command: c3.name, description: c3.description.slice(0, 256) }));
|
|
18698
|
+
const allCommands = [...STATIC_COMMANDS, ...pluginCommands].slice(0, 100);
|
|
18634
18699
|
this.retryWithBackoff(
|
|
18635
|
-
() => this.bot.api.setMyCommands(
|
|
18700
|
+
() => this.bot.api.setMyCommands(allCommands, {
|
|
18636
18701
|
scope: { type: "chat", chat_id: this.telegramConfig.chatId }
|
|
18637
18702
|
}),
|
|
18638
18703
|
"setMyCommands"
|
|
@@ -22161,16 +22226,16 @@ var init_prompt_queue = __esm({
|
|
|
22161
22226
|
* immediately. Otherwise, it's buffered and the returned promise resolves
|
|
22162
22227
|
* only after the prompt finishes processing.
|
|
22163
22228
|
*/
|
|
22164
|
-
async enqueue(text5, attachments, routing, turnId, meta) {
|
|
22229
|
+
async enqueue(text5, userPrompt, attachments, routing, turnId, meta) {
|
|
22165
22230
|
if (this.processing) {
|
|
22166
22231
|
return new Promise((resolve9) => {
|
|
22167
|
-
this.queue.push({ text: text5, attachments, routing, turnId, meta, resolve: resolve9 });
|
|
22232
|
+
this.queue.push({ text: text5, userPrompt, attachments, routing, turnId, meta, resolve: resolve9 });
|
|
22168
22233
|
});
|
|
22169
22234
|
}
|
|
22170
|
-
await this.process(text5, attachments, routing, turnId, meta);
|
|
22235
|
+
await this.process(text5, userPrompt, attachments, routing, turnId, meta);
|
|
22171
22236
|
}
|
|
22172
22237
|
/** Run a single prompt through the processor, then drain the next queued item. */
|
|
22173
|
-
async process(text5, attachments, routing, turnId, meta) {
|
|
22238
|
+
async process(text5, userPrompt, attachments, routing, turnId, meta) {
|
|
22174
22239
|
this.processing = true;
|
|
22175
22240
|
this.abortController = new AbortController();
|
|
22176
22241
|
const { signal } = this.abortController;
|
|
@@ -22180,7 +22245,7 @@ var init_prompt_queue = __esm({
|
|
|
22180
22245
|
});
|
|
22181
22246
|
try {
|
|
22182
22247
|
await Promise.race([
|
|
22183
|
-
this.processor(text5, attachments, routing, turnId, meta),
|
|
22248
|
+
this.processor(text5, userPrompt, attachments, routing, turnId, meta),
|
|
22184
22249
|
new Promise((_, reject) => {
|
|
22185
22250
|
signal.addEventListener("abort", () => reject(new Error("Prompt aborted")), { once: true });
|
|
22186
22251
|
})
|
|
@@ -22201,7 +22266,7 @@ var init_prompt_queue = __esm({
|
|
|
22201
22266
|
drainNext() {
|
|
22202
22267
|
const next = this.queue.shift();
|
|
22203
22268
|
if (next) {
|
|
22204
|
-
this.process(next.text, next.attachments, next.routing, next.turnId, next.meta).then(next.resolve);
|
|
22269
|
+
this.process(next.text, next.userPrompt, next.attachments, next.routing, next.turnId, next.meta).then(next.resolve);
|
|
22205
22270
|
}
|
|
22206
22271
|
}
|
|
22207
22272
|
/**
|
|
@@ -22223,6 +22288,13 @@ var init_prompt_queue = __esm({
|
|
|
22223
22288
|
get isProcessing() {
|
|
22224
22289
|
return this.processing;
|
|
22225
22290
|
}
|
|
22291
|
+
/** Snapshot of queued (not yet processing) items — used for queue inspection by callers. */
|
|
22292
|
+
get pendingItems() {
|
|
22293
|
+
return this.queue.map((item) => ({
|
|
22294
|
+
userPrompt: item.userPrompt,
|
|
22295
|
+
turnId: item.turnId
|
|
22296
|
+
}));
|
|
22297
|
+
}
|
|
22226
22298
|
};
|
|
22227
22299
|
}
|
|
22228
22300
|
});
|
|
@@ -22307,18 +22379,32 @@ var init_permission_gate = __esm({
|
|
|
22307
22379
|
});
|
|
22308
22380
|
|
|
22309
22381
|
// src/core/sessions/turn-context.ts
|
|
22310
|
-
import { nanoid as
|
|
22311
|
-
function
|
|
22382
|
+
import { nanoid as nanoid4 } from "nanoid";
|
|
22383
|
+
function extractSender(meta) {
|
|
22384
|
+
const identity = meta?.identity;
|
|
22385
|
+
if (!identity || !identity.userId || !identity.identityId) return null;
|
|
22312
22386
|
return {
|
|
22313
|
-
|
|
22314
|
-
|
|
22315
|
-
|
|
22387
|
+
userId: identity.userId,
|
|
22388
|
+
identityId: identity.identityId,
|
|
22389
|
+
displayName: identity.displayName,
|
|
22390
|
+
username: identity.username
|
|
22316
22391
|
};
|
|
22317
22392
|
}
|
|
22318
22393
|
function getEffectiveTarget(ctx) {
|
|
22319
22394
|
if (ctx.responseAdapterId === null) return null;
|
|
22320
22395
|
return ctx.responseAdapterId ?? ctx.sourceAdapterId;
|
|
22321
22396
|
}
|
|
22397
|
+
function createTurnContext(sourceAdapterId, responseAdapterId, turnId, userPrompt, finalPrompt, attachments, meta) {
|
|
22398
|
+
return {
|
|
22399
|
+
turnId: turnId ?? nanoid4(8),
|
|
22400
|
+
sourceAdapterId,
|
|
22401
|
+
responseAdapterId,
|
|
22402
|
+
userPrompt,
|
|
22403
|
+
finalPrompt,
|
|
22404
|
+
attachments,
|
|
22405
|
+
meta
|
|
22406
|
+
};
|
|
22407
|
+
}
|
|
22322
22408
|
function isSystemEvent(event) {
|
|
22323
22409
|
return SYSTEM_EVENT_TYPES.has(event.type);
|
|
22324
22410
|
}
|
|
@@ -22338,7 +22424,7 @@ var init_turn_context = __esm({
|
|
|
22338
22424
|
});
|
|
22339
22425
|
|
|
22340
22426
|
// src/core/sessions/session.ts
|
|
22341
|
-
import { nanoid as
|
|
22427
|
+
import { nanoid as nanoid5 } from "nanoid";
|
|
22342
22428
|
import * as fs41 from "fs";
|
|
22343
22429
|
var moduleLog, TTS_PROMPT_INSTRUCTION, TTS_BLOCK_REGEX, TTS_MAX_LENGTH, TTS_TIMEOUT_MS, VALID_TRANSITIONS, Session;
|
|
22344
22430
|
var init_session2 = __esm({
|
|
@@ -22418,7 +22504,7 @@ Additionally, include a [TTS]...[/TTS] block with a spoken-friendly summary of y
|
|
|
22418
22504
|
pendingContext = null;
|
|
22419
22505
|
constructor(opts) {
|
|
22420
22506
|
super();
|
|
22421
|
-
this.id = opts.id ||
|
|
22507
|
+
this.id = opts.id || nanoid5(12);
|
|
22422
22508
|
this.channelId = opts.channelId;
|
|
22423
22509
|
this.attachedAdapters = [opts.channelId];
|
|
22424
22510
|
this.agentName = opts.agentName;
|
|
@@ -22430,7 +22516,7 @@ Additionally, include a [TTS]...[/TTS] block with a spoken-friendly summary of y
|
|
|
22430
22516
|
this.log = createSessionLogger(this.id, moduleLog);
|
|
22431
22517
|
this.log.info({ agentName: this.agentName }, "Session created");
|
|
22432
22518
|
this.queue = new PromptQueue(
|
|
22433
|
-
(text5, attachments, routing, turnId, meta) => this.processPrompt(text5, attachments, routing, turnId, meta),
|
|
22519
|
+
(text5, userPrompt, attachments, routing, turnId, meta) => this.processPrompt(text5, userPrompt, attachments, routing, turnId, meta),
|
|
22434
22520
|
(err) => {
|
|
22435
22521
|
this.log.error({ err }, "Prompt execution failed");
|
|
22436
22522
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -22510,6 +22596,10 @@ Additionally, include a [TTS]...[/TTS] block with a spoken-friendly summary of y
|
|
|
22510
22596
|
get promptRunning() {
|
|
22511
22597
|
return this.queue.isProcessing;
|
|
22512
22598
|
}
|
|
22599
|
+
/** Snapshot of queued (not yet processing) items — for inspection by API consumers. */
|
|
22600
|
+
get queueItems() {
|
|
22601
|
+
return this.queue.pendingItems;
|
|
22602
|
+
}
|
|
22513
22603
|
// --- Context Injection ---
|
|
22514
22604
|
/** Store context markdown to be prepended to the next prompt (used for session resume with history). */
|
|
22515
22605
|
setContext(markdown) {
|
|
@@ -22530,24 +22620,30 @@ Additionally, include a [TTS]...[/TTS] block with a spoken-friendly summary of y
|
|
|
22530
22620
|
* queued/processing events before the prompt actually runs.
|
|
22531
22621
|
*/
|
|
22532
22622
|
async enqueuePrompt(text5, attachments, routing, externalTurnId, meta) {
|
|
22533
|
-
const turnId = externalTurnId ??
|
|
22623
|
+
const turnId = externalTurnId ?? nanoid5(8);
|
|
22534
22624
|
const turnMeta = meta ?? { turnId };
|
|
22625
|
+
const userPrompt = text5;
|
|
22535
22626
|
if (this.middlewareChain) {
|
|
22536
22627
|
const payload = { text: text5, attachments, sessionId: this.id, sourceAdapterId: routing?.sourceAdapterId, meta: turnMeta };
|
|
22537
22628
|
const result = await this.middlewareChain.execute(Hook.AGENT_BEFORE_PROMPT, payload, async (p2) => p2);
|
|
22538
|
-
if (!result)
|
|
22629
|
+
if (!result) throw new Error("PROMPT_BLOCKED");
|
|
22539
22630
|
text5 = result.text;
|
|
22540
22631
|
attachments = result.attachments;
|
|
22541
22632
|
}
|
|
22542
|
-
await this.queue.enqueue(text5, attachments, routing, turnId, turnMeta);
|
|
22633
|
+
await this.queue.enqueue(text5, userPrompt, attachments, routing, turnId, turnMeta);
|
|
22543
22634
|
return turnId;
|
|
22544
22635
|
}
|
|
22545
|
-
async processPrompt(text5, attachments, routing, turnId, meta) {
|
|
22636
|
+
async processPrompt(text5, userPrompt, attachments, routing, turnId, meta) {
|
|
22546
22637
|
if (this._status === "finished") return;
|
|
22547
22638
|
this.activeTurnContext = createTurnContext(
|
|
22548
22639
|
routing?.sourceAdapterId ?? this.channelId,
|
|
22549
22640
|
routing?.responseAdapterId,
|
|
22550
|
-
turnId
|
|
22641
|
+
turnId,
|
|
22642
|
+
userPrompt,
|
|
22643
|
+
text5,
|
|
22644
|
+
// finalPrompt (after middleware transformations)
|
|
22645
|
+
attachments,
|
|
22646
|
+
meta
|
|
22551
22647
|
);
|
|
22552
22648
|
this.emit(SessionEv.TURN_STARTED, this.activeTurnContext);
|
|
22553
22649
|
this.promptCount++;
|
|
@@ -22598,7 +22694,16 @@ ${text5}`;
|
|
|
22598
22694
|
this.agentInstance.on(SessionEv.AGENT_EVENT, afterEventListener);
|
|
22599
22695
|
}
|
|
22600
22696
|
if (this.middlewareChain) {
|
|
22601
|
-
this.middlewareChain.execute(Hook.TURN_START, {
|
|
22697
|
+
this.middlewareChain.execute(Hook.TURN_START, {
|
|
22698
|
+
sessionId: this.id,
|
|
22699
|
+
promptText: processed.text,
|
|
22700
|
+
promptNumber: this.promptCount,
|
|
22701
|
+
turnId: this.activeTurnContext?.turnId ?? turnId ?? "",
|
|
22702
|
+
meta,
|
|
22703
|
+
userPrompt: this.activeTurnContext?.userPrompt,
|
|
22704
|
+
sourceAdapterId: this.activeTurnContext?.sourceAdapterId,
|
|
22705
|
+
responseAdapterId: this.activeTurnContext?.responseAdapterId
|
|
22706
|
+
}, async (p2) => p2).catch(() => {
|
|
22602
22707
|
});
|
|
22603
22708
|
}
|
|
22604
22709
|
let stopReason = "end_turn";
|
|
@@ -23269,7 +23374,7 @@ var init_session_bridge = __esm({
|
|
|
23269
23374
|
if (this.shouldForward(event)) {
|
|
23270
23375
|
this.dispatchAgentEvent(event);
|
|
23271
23376
|
} else {
|
|
23272
|
-
this.deps.eventBus?.emit(BusEvent.AGENT_EVENT, { sessionId: this.session.id, event });
|
|
23377
|
+
this.deps.eventBus?.emit(BusEvent.AGENT_EVENT, { sessionId: this.session.id, turnId: "", event });
|
|
23273
23378
|
}
|
|
23274
23379
|
});
|
|
23275
23380
|
if (!this.session.agentInstance.onPermissionRequest || this.session.agentInstance.onPermissionRequest.__bridgeId === void 0) {
|
|
@@ -23322,14 +23427,16 @@ var init_session_bridge = __esm({
|
|
|
23322
23427
|
this.deps.sessionManager.patchRecord(this.session.id, { currentPromptCount: count });
|
|
23323
23428
|
});
|
|
23324
23429
|
this.listen(this.session, SessionEv.TURN_STARTED, (ctx) => {
|
|
23325
|
-
|
|
23326
|
-
this.
|
|
23327
|
-
|
|
23328
|
-
|
|
23329
|
-
|
|
23330
|
-
|
|
23331
|
-
|
|
23332
|
-
|
|
23430
|
+
this.deps.eventBus?.emit(BusEvent.MESSAGE_PROCESSING, {
|
|
23431
|
+
sessionId: this.session.id,
|
|
23432
|
+
turnId: ctx.turnId,
|
|
23433
|
+
sourceAdapterId: ctx.sourceAdapterId,
|
|
23434
|
+
userPrompt: ctx.userPrompt,
|
|
23435
|
+
finalPrompt: ctx.finalPrompt,
|
|
23436
|
+
attachments: ctx.attachments,
|
|
23437
|
+
sender: extractSender(ctx.meta),
|
|
23438
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
23439
|
+
});
|
|
23333
23440
|
});
|
|
23334
23441
|
if (this.session.latestCommands !== null) {
|
|
23335
23442
|
this.session.emit(SessionEv.AGENT_EVENT, { type: "commands_update", commands: this.session.latestCommands });
|
|
@@ -23495,6 +23602,7 @@ var init_session_bridge = __esm({
|
|
|
23495
23602
|
}
|
|
23496
23603
|
this.deps.eventBus?.emit(BusEvent.AGENT_EVENT, {
|
|
23497
23604
|
sessionId: this.session.id,
|
|
23605
|
+
turnId: this.session.activeTurnContext?.turnId ?? "",
|
|
23498
23606
|
event
|
|
23499
23607
|
});
|
|
23500
23608
|
return outgoing;
|
|
@@ -24408,6 +24516,7 @@ var init_session_factory = __esm({
|
|
|
24408
24516
|
const failedSessionId = createParams.existingSessionId ?? `failed-${Date.now()}`;
|
|
24409
24517
|
this.eventBus.emit(BusEvent.AGENT_EVENT, {
|
|
24410
24518
|
sessionId: failedSessionId,
|
|
24519
|
+
turnId: "",
|
|
24411
24520
|
event: guidance
|
|
24412
24521
|
});
|
|
24413
24522
|
throw err;
|
|
@@ -24778,7 +24887,7 @@ var init_agent_switch_handler = __esm({
|
|
|
24778
24887
|
message: `Switching from ${fromAgent} to ${toAgent}...`
|
|
24779
24888
|
};
|
|
24780
24889
|
session.emit(SessionEv.AGENT_EVENT, startEvent);
|
|
24781
|
-
eventBus.emit(BusEvent.AGENT_EVENT, { sessionId, event: startEvent });
|
|
24890
|
+
eventBus.emit(BusEvent.AGENT_EVENT, { sessionId, turnId: "", event: startEvent });
|
|
24782
24891
|
eventBus.emit(BusEvent.SESSION_AGENT_SWITCH, {
|
|
24783
24892
|
sessionId,
|
|
24784
24893
|
fromAgent,
|
|
@@ -24841,7 +24950,7 @@ var init_agent_switch_handler = __esm({
|
|
|
24841
24950
|
message: resumed ? `Switched to ${toAgent} (resumed previous session).` : `Switched to ${toAgent} (new session).`
|
|
24842
24951
|
};
|
|
24843
24952
|
session.emit(SessionEv.AGENT_EVENT, successEvent);
|
|
24844
|
-
eventBus.emit(BusEvent.AGENT_EVENT, { sessionId, event: successEvent });
|
|
24953
|
+
eventBus.emit(BusEvent.AGENT_EVENT, { sessionId, turnId: "", event: successEvent });
|
|
24845
24954
|
eventBus.emit(BusEvent.SESSION_AGENT_SWITCH, {
|
|
24846
24955
|
sessionId,
|
|
24847
24956
|
fromAgent,
|
|
@@ -24856,7 +24965,7 @@ var init_agent_switch_handler = __esm({
|
|
|
24856
24965
|
message: `Failed to switch to ${toAgent}: ${errorMessage}`
|
|
24857
24966
|
};
|
|
24858
24967
|
session.emit(SessionEv.AGENT_EVENT, failedEvent);
|
|
24859
|
-
eventBus.emit(BusEvent.AGENT_EVENT, { sessionId, event: failedEvent });
|
|
24968
|
+
eventBus.emit(BusEvent.AGENT_EVENT, { sessionId, turnId: "", event: failedEvent });
|
|
24860
24969
|
eventBus.emit(BusEvent.SESSION_AGENT_SWITCH, {
|
|
24861
24970
|
sessionId,
|
|
24862
24971
|
fromAgent,
|
|
@@ -26769,7 +26878,7 @@ var init_core_items = __esm({
|
|
|
26769
26878
|
|
|
26770
26879
|
// src/core/core.ts
|
|
26771
26880
|
import path51 from "path";
|
|
26772
|
-
import { nanoid as
|
|
26881
|
+
import { nanoid as nanoid6 } from "nanoid";
|
|
26773
26882
|
var log44, OpenACPCore;
|
|
26774
26883
|
var init_core = __esm({
|
|
26775
26884
|
"src/core/core.ts"() {
|
|
@@ -26795,6 +26904,7 @@ var init_core = __esm({
|
|
|
26795
26904
|
init_error_tracker();
|
|
26796
26905
|
init_log();
|
|
26797
26906
|
init_events();
|
|
26907
|
+
init_turn_context();
|
|
26798
26908
|
log44 = createChildLogger({ module: "core" });
|
|
26799
26909
|
OpenACPCore = class {
|
|
26800
26910
|
configManager;
|
|
@@ -27080,7 +27190,7 @@ var init_core = __esm({
|
|
|
27080
27190
|
},
|
|
27081
27191
|
"Incoming message"
|
|
27082
27192
|
);
|
|
27083
|
-
const turnId =
|
|
27193
|
+
const turnId = nanoid6(8);
|
|
27084
27194
|
const meta = { turnId, ...initialMeta };
|
|
27085
27195
|
if (this.lifecycleManager?.middlewareChain) {
|
|
27086
27196
|
const result = await this.lifecycleManager.middlewareChain.execute(
|
|
@@ -27120,9 +27230,6 @@ var init_core = __esm({
|
|
|
27120
27230
|
}
|
|
27121
27231
|
return;
|
|
27122
27232
|
}
|
|
27123
|
-
this.sessionManager.patchRecord(session.id, {
|
|
27124
|
-
lastActiveAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
27125
|
-
});
|
|
27126
27233
|
let text5 = message.text;
|
|
27127
27234
|
if (this.assistantManager?.isAssistant(session.id)) {
|
|
27128
27235
|
const pending = this.assistantManager.consumePendingSystemPrompt(message.channelId);
|
|
@@ -27135,38 +27242,56 @@ User message:
|
|
|
27135
27242
|
${text5}`;
|
|
27136
27243
|
}
|
|
27137
27244
|
}
|
|
27138
|
-
const sourceAdapterId = message.routing?.sourceAdapterId ?? message.channelId;
|
|
27139
|
-
const routing = sourceAdapterId !== message.routing?.sourceAdapterId ? { ...message.routing, sourceAdapterId } : message.routing;
|
|
27140
27245
|
const enrichedMeta = message.meta ?? meta;
|
|
27141
|
-
|
|
27142
|
-
|
|
27246
|
+
await this._dispatchToSession(session, text5, message.attachments, {
|
|
27247
|
+
sourceAdapterId: message.routing?.sourceAdapterId ?? message.channelId,
|
|
27248
|
+
responseAdapterId: message.routing?.responseAdapterId
|
|
27249
|
+
}, turnId, enrichedMeta);
|
|
27250
|
+
}
|
|
27251
|
+
/**
|
|
27252
|
+
* Shared dispatch path for sending a prompt to a session.
|
|
27253
|
+
* Called by both handleMessage (Telegram) and handleMessageInSession (SSE/API)
|
|
27254
|
+
* after their respective middleware/enrichment steps.
|
|
27255
|
+
*/
|
|
27256
|
+
async _dispatchToSession(session, text5, attachments, routing, turnId, meta) {
|
|
27257
|
+
this.sessionManager.patchRecord(session.id, {
|
|
27258
|
+
lastActiveAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
27259
|
+
});
|
|
27260
|
+
this.eventBus.emit(BusEvent.MESSAGE_QUEUED, {
|
|
27261
|
+
sessionId: session.id,
|
|
27262
|
+
turnId,
|
|
27263
|
+
text: text5,
|
|
27264
|
+
sourceAdapterId: routing.sourceAdapterId,
|
|
27265
|
+
attachments,
|
|
27266
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27267
|
+
queueDepth: session.queueDepth + 1,
|
|
27268
|
+
sender: extractSender(meta)
|
|
27269
|
+
});
|
|
27270
|
+
session.enqueuePrompt(text5, attachments, routing, turnId, meta).catch((err) => {
|
|
27271
|
+
const reason = err instanceof Error ? err.message : String(err);
|
|
27272
|
+
log44.warn({ err, sessionId: session.id, turnId, reason }, "enqueuePrompt failed \u2014 emitting message:failed");
|
|
27273
|
+
this.eventBus.emit(BusEvent.MESSAGE_FAILED, {
|
|
27143
27274
|
sessionId: session.id,
|
|
27144
27275
|
turnId,
|
|
27145
|
-
|
|
27146
|
-
sourceAdapterId,
|
|
27147
|
-
attachments: message.attachments,
|
|
27148
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
27149
|
-
queueDepth: session.queueDepth
|
|
27276
|
+
reason
|
|
27150
27277
|
});
|
|
27151
|
-
|
|
27152
|
-
} else {
|
|
27153
|
-
await session.enqueuePrompt(text5, message.attachments, routing, turnId, enrichedMeta);
|
|
27154
|
-
}
|
|
27278
|
+
});
|
|
27155
27279
|
}
|
|
27156
27280
|
/**
|
|
27157
27281
|
* Send a message to a known session, running the full message:incoming → agent:beforePrompt
|
|
27158
27282
|
* middleware chain (same as handleMessage) but without the threadId-based session lookup.
|
|
27159
27283
|
*
|
|
27160
|
-
* Used by channels that already hold a direct session reference (e.g. SSE adapter),
|
|
27161
|
-
* looking up by channelId+threadId is unreliable (API sessions may have no threadId).
|
|
27284
|
+
* Used by channels that already hold a direct session reference (e.g. SSE adapter, api-server),
|
|
27285
|
+
* where looking up by channelId+threadId is unreliable (API sessions may have no threadId).
|
|
27162
27286
|
*
|
|
27163
27287
|
* @param session The target session — caller is responsible for validating its status.
|
|
27164
27288
|
* @param message Sender context and message content.
|
|
27165
27289
|
* @param initialMeta Optional adapter-specific context to seed the TurnMeta bag
|
|
27166
27290
|
* (e.g. channelUser with display name/username).
|
|
27291
|
+
* @param options Optional turnId override and response routing.
|
|
27167
27292
|
*/
|
|
27168
|
-
async handleMessageInSession(session, message, initialMeta) {
|
|
27169
|
-
const turnId =
|
|
27293
|
+
async handleMessageInSession(session, message, initialMeta, options) {
|
|
27294
|
+
const turnId = options?.externalTurnId ?? nanoid6(8);
|
|
27170
27295
|
const meta = { turnId, ...initialMeta };
|
|
27171
27296
|
let text5 = message.text;
|
|
27172
27297
|
let { attachments } = message;
|
|
@@ -27185,13 +27310,17 @@ ${text5}`;
|
|
|
27185
27310
|
payload,
|
|
27186
27311
|
async (p2) => p2
|
|
27187
27312
|
);
|
|
27188
|
-
if (!result) return;
|
|
27313
|
+
if (!result) return { turnId, queueDepth: session.queueDepth };
|
|
27189
27314
|
text5 = result.text;
|
|
27190
27315
|
attachments = result.attachments;
|
|
27191
27316
|
enrichedMeta = result.meta ?? meta;
|
|
27192
27317
|
}
|
|
27193
|
-
const routing = {
|
|
27194
|
-
|
|
27318
|
+
const routing = {
|
|
27319
|
+
sourceAdapterId: message.channelId,
|
|
27320
|
+
responseAdapterId: options?.responseAdapterId
|
|
27321
|
+
};
|
|
27322
|
+
await this._dispatchToSession(session, text5, attachments, routing, turnId, enrichedMeta);
|
|
27323
|
+
return { turnId, queueDepth: session.queueDepth };
|
|
27195
27324
|
}
|
|
27196
27325
|
// --- Unified Session Creation Pipeline ---
|
|
27197
27326
|
/**
|
|
@@ -27303,7 +27432,7 @@ ${text5}`;
|
|
|
27303
27432
|
} else if (processedEvent.type === "error") {
|
|
27304
27433
|
session.fail(processedEvent.message);
|
|
27305
27434
|
}
|
|
27306
|
-
this.eventBus.emit(BusEvent.AGENT_EVENT, { sessionId: session.id, event: processedEvent });
|
|
27435
|
+
this.eventBus.emit(BusEvent.AGENT_EVENT, { sessionId: session.id, turnId: session.activeTurnContext?.turnId ?? "", event: processedEvent });
|
|
27307
27436
|
});
|
|
27308
27437
|
session.on(SessionEv.STATUS_CHANGE, (_from, to) => {
|
|
27309
27438
|
this.sessionManager.patchRecord(session.id, {
|
|
@@ -27315,6 +27444,18 @@ ${text5}`;
|
|
|
27315
27444
|
session.on(SessionEv.PROMPT_COUNT_CHANGED, (count) => {
|
|
27316
27445
|
this.sessionManager.patchRecord(session.id, { currentPromptCount: count });
|
|
27317
27446
|
});
|
|
27447
|
+
session.on(SessionEv.TURN_STARTED, (ctx) => {
|
|
27448
|
+
this.eventBus.emit(BusEvent.MESSAGE_PROCESSING, {
|
|
27449
|
+
sessionId: session.id,
|
|
27450
|
+
turnId: ctx.turnId,
|
|
27451
|
+
sourceAdapterId: ctx.sourceAdapterId,
|
|
27452
|
+
userPrompt: ctx.userPrompt,
|
|
27453
|
+
finalPrompt: ctx.finalPrompt,
|
|
27454
|
+
attachments: ctx.attachments,
|
|
27455
|
+
sender: extractSender(ctx.meta),
|
|
27456
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
27457
|
+
});
|
|
27458
|
+
});
|
|
27318
27459
|
}
|
|
27319
27460
|
this.sessionFactory.wireSideEffects(session, {
|
|
27320
27461
|
eventBus: this.eventBus,
|