@openacp/cli 0.4.9 → 0.4.10
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/{chunk-45DFYWJT.js → chunk-KPI4HGJC.js} +707 -622
- package/dist/chunk-KPI4HGJC.js.map +1 -0
- package/dist/cli.js +2 -2
- package/dist/index.js +1 -1
- package/dist/{main-7T5YHFHO.js → main-G6XDM7EZ.js} +2 -2
- package/package.json +1 -1
- package/dist/chunk-45DFYWJT.js.map +0 -1
- /package/dist/{main-7T5YHFHO.js.map → main-G6XDM7EZ.js.map} +0 -0
|
@@ -2428,12 +2428,14 @@ function splitMessage(text, maxLength = 3800) {
|
|
|
2428
2428
|
chunks.push(remaining);
|
|
2429
2429
|
break;
|
|
2430
2430
|
}
|
|
2431
|
-
|
|
2432
|
-
|
|
2433
|
-
|
|
2431
|
+
const wouldLeaveSmall = remaining.length < maxLength * 1.3;
|
|
2432
|
+
const searchLimit = wouldLeaveSmall ? Math.floor(remaining.length / 2) + 300 : maxLength;
|
|
2433
|
+
let splitAt = remaining.lastIndexOf("\n\n", searchLimit);
|
|
2434
|
+
if (splitAt === -1 || splitAt < searchLimit * 0.2) {
|
|
2435
|
+
splitAt = remaining.lastIndexOf("\n", searchLimit);
|
|
2434
2436
|
}
|
|
2435
|
-
if (splitAt === -1 || splitAt <
|
|
2436
|
-
splitAt =
|
|
2437
|
+
if (splitAt === -1 || splitAt < searchLimit * 0.2) {
|
|
2438
|
+
splitAt = searchLimit;
|
|
2437
2439
|
}
|
|
2438
2440
|
const candidate = remaining.slice(0, splitAt);
|
|
2439
2441
|
const fences = candidate.match(/```/g);
|
|
@@ -2466,6 +2468,7 @@ var MessageDraft = class {
|
|
|
2466
2468
|
flushTimer;
|
|
2467
2469
|
flushPromise = Promise.resolve();
|
|
2468
2470
|
lastSentBuffer = "";
|
|
2471
|
+
displayTruncated = false;
|
|
2469
2472
|
append(text) {
|
|
2470
2473
|
if (!text) return;
|
|
2471
2474
|
this.buffer += text;
|
|
@@ -2482,16 +2485,20 @@ var MessageDraft = class {
|
|
|
2482
2485
|
async flush() {
|
|
2483
2486
|
if (!this.buffer) return;
|
|
2484
2487
|
if (this.firstFlushPending) return;
|
|
2485
|
-
|
|
2486
|
-
|
|
2487
|
-
let cutAt = displayBuffer.lastIndexOf("\n", 3800);
|
|
2488
|
-
if (cutAt < 800) cutAt = 3800;
|
|
2489
|
-
displayBuffer = displayBuffer.slice(0, cutAt) + "\n\u2026";
|
|
2490
|
-
}
|
|
2491
|
-
let html = markdownToTelegramHtml(displayBuffer);
|
|
2488
|
+
const snapshot = this.buffer;
|
|
2489
|
+
let html = markdownToTelegramHtml(snapshot);
|
|
2492
2490
|
if (!html) return;
|
|
2491
|
+
let truncated = false;
|
|
2493
2492
|
if (html.length > 4096) {
|
|
2494
|
-
|
|
2493
|
+
const ratio = 4e3 / html.length;
|
|
2494
|
+
const targetLen = Math.floor(snapshot.length * ratio);
|
|
2495
|
+
let cutAt = snapshot.lastIndexOf("\n", targetLen);
|
|
2496
|
+
if (cutAt < targetLen * 0.5) cutAt = targetLen;
|
|
2497
|
+
html = markdownToTelegramHtml(snapshot.slice(0, cutAt) + "\n\u2026");
|
|
2498
|
+
truncated = true;
|
|
2499
|
+
if (html.length > 4096) {
|
|
2500
|
+
html = html.slice(0, 4090) + "\n\u2026";
|
|
2501
|
+
}
|
|
2495
2502
|
}
|
|
2496
2503
|
if (!this.messageId) {
|
|
2497
2504
|
this.firstFlushPending = true;
|
|
@@ -2506,7 +2513,12 @@ var MessageDraft = class {
|
|
|
2506
2513
|
);
|
|
2507
2514
|
if (result) {
|
|
2508
2515
|
this.messageId = result.message_id;
|
|
2509
|
-
|
|
2516
|
+
if (!truncated) {
|
|
2517
|
+
this.lastSentBuffer = snapshot;
|
|
2518
|
+
this.displayTruncated = false;
|
|
2519
|
+
} else {
|
|
2520
|
+
this.displayTruncated = true;
|
|
2521
|
+
}
|
|
2510
2522
|
}
|
|
2511
2523
|
} catch {
|
|
2512
2524
|
} finally {
|
|
@@ -2514,13 +2526,20 @@ var MessageDraft = class {
|
|
|
2514
2526
|
}
|
|
2515
2527
|
} else {
|
|
2516
2528
|
try {
|
|
2517
|
-
await this.sendQueue.enqueue(
|
|
2529
|
+
const result = await this.sendQueue.enqueue(
|
|
2518
2530
|
() => this.bot.api.editMessageText(this.chatId, this.messageId, html, {
|
|
2519
2531
|
parse_mode: "HTML"
|
|
2520
2532
|
}),
|
|
2521
2533
|
{ type: "text", key: this.sessionId }
|
|
2522
2534
|
);
|
|
2523
|
-
|
|
2535
|
+
if (result !== void 0) {
|
|
2536
|
+
if (!truncated) {
|
|
2537
|
+
this.lastSentBuffer = snapshot;
|
|
2538
|
+
this.displayTruncated = false;
|
|
2539
|
+
} else {
|
|
2540
|
+
this.displayTruncated = true;
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2524
2543
|
} catch {
|
|
2525
2544
|
}
|
|
2526
2545
|
}
|
|
@@ -2532,9 +2551,34 @@ var MessageDraft = class {
|
|
|
2532
2551
|
}
|
|
2533
2552
|
await this.flushPromise;
|
|
2534
2553
|
if (!this.buffer) return this.messageId;
|
|
2535
|
-
if (this.messageId && this.buffer === this.lastSentBuffer) {
|
|
2554
|
+
if (this.messageId && this.buffer === this.lastSentBuffer && !this.displayTruncated) {
|
|
2536
2555
|
return this.messageId;
|
|
2537
2556
|
}
|
|
2557
|
+
const fullHtml = markdownToTelegramHtml(this.buffer);
|
|
2558
|
+
if (fullHtml.length <= 4096) {
|
|
2559
|
+
try {
|
|
2560
|
+
if (this.messageId) {
|
|
2561
|
+
await this.sendQueue.enqueue(
|
|
2562
|
+
() => this.bot.api.editMessageText(this.chatId, this.messageId, fullHtml, {
|
|
2563
|
+
parse_mode: "HTML"
|
|
2564
|
+
}),
|
|
2565
|
+
{ type: "other" }
|
|
2566
|
+
);
|
|
2567
|
+
} else {
|
|
2568
|
+
const msg = await this.sendQueue.enqueue(
|
|
2569
|
+
() => this.bot.api.sendMessage(this.chatId, fullHtml, {
|
|
2570
|
+
message_thread_id: this.threadId,
|
|
2571
|
+
parse_mode: "HTML",
|
|
2572
|
+
disable_notification: true
|
|
2573
|
+
}),
|
|
2574
|
+
{ type: "other" }
|
|
2575
|
+
);
|
|
2576
|
+
if (msg) this.messageId = msg.message_id;
|
|
2577
|
+
}
|
|
2578
|
+
return this.messageId;
|
|
2579
|
+
} catch {
|
|
2580
|
+
}
|
|
2581
|
+
}
|
|
2538
2582
|
const mdChunks = splitMessage(this.buffer);
|
|
2539
2583
|
for (let i = 0; i < mdChunks.length; i++) {
|
|
2540
2584
|
const html = markdownToTelegramHtml(mdChunks[i]);
|
|
@@ -2620,202 +2664,229 @@ function buildDeepLink(chatId, messageId) {
|
|
|
2620
2664
|
return `https://t.me/c/${cleanId}/${messageId}`;
|
|
2621
2665
|
}
|
|
2622
2666
|
|
|
2623
|
-
// src/adapters/telegram/commands.ts
|
|
2667
|
+
// src/adapters/telegram/commands/new-session.ts
|
|
2668
|
+
import { InlineKeyboard as InlineKeyboard2 } from "grammy";
|
|
2669
|
+
|
|
2670
|
+
// src/adapters/telegram/commands/admin.ts
|
|
2624
2671
|
import { InlineKeyboard } from "grammy";
|
|
2625
|
-
var log8 = createChildLogger({ module: "telegram-
|
|
2626
|
-
|
|
2627
|
-
|
|
2628
|
-
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
clearTimeout(pending.timer);
|
|
2632
|
-
pendingNewSessions.delete(userId);
|
|
2633
|
-
}
|
|
2634
|
-
}
|
|
2635
|
-
function setupCommands(bot, core, chatId, assistant) {
|
|
2636
|
-
bot.command("new", (ctx) => handleNew(ctx, core, chatId, assistant));
|
|
2637
|
-
bot.command("newchat", (ctx) => handleNewChat(ctx, core, chatId));
|
|
2638
|
-
bot.command("cancel", (ctx) => handleCancel(ctx, core, assistant));
|
|
2639
|
-
bot.command("status", (ctx) => handleStatus(ctx, core));
|
|
2640
|
-
bot.command("sessions", (ctx) => handleTopics(ctx, core));
|
|
2641
|
-
bot.command("agents", (ctx) => handleAgents(ctx, core));
|
|
2642
|
-
bot.command("help", (ctx) => handleHelp(ctx));
|
|
2643
|
-
bot.command("menu", (ctx) => handleMenu(ctx));
|
|
2644
|
-
bot.command("enable_dangerous", (ctx) => handleEnableDangerous(ctx, core));
|
|
2645
|
-
bot.command("disable_dangerous", (ctx) => handleDisableDangerous(ctx, core));
|
|
2646
|
-
bot.command("restart", (ctx) => handleRestart(ctx, core));
|
|
2647
|
-
bot.command("update", (ctx) => handleUpdate(ctx, core));
|
|
2648
|
-
bot.command("integrate", (ctx) => handleIntegrate(ctx, core));
|
|
2649
|
-
bot.command("clear", (ctx) => handleClear(ctx, assistant));
|
|
2650
|
-
}
|
|
2651
|
-
function buildMenuKeyboard() {
|
|
2652
|
-
return new InlineKeyboard().text("\u{1F195} New Session", "m:new").text("\u{1F4CB} Sessions", "m:topics").row().text("\u{1F4CA} Status", "m:status").text("\u{1F916} Agents", "m:agents").row().text("\u{1F517} Integrate", "m:integrate").text("\u2753 Help", "m:help").row().text("\u{1F504} Restart", "m:restart").text("\u2B06\uFE0F Update", "m:update");
|
|
2672
|
+
var log8 = createChildLogger({ module: "telegram-cmd-admin" });
|
|
2673
|
+
function buildDangerousModeKeyboard(sessionId, enabled) {
|
|
2674
|
+
return new InlineKeyboard().text(
|
|
2675
|
+
enabled ? "\u{1F510} Disable Dangerous Mode" : "\u2620\uFE0F Enable Dangerous Mode",
|
|
2676
|
+
`d:${sessionId}`
|
|
2677
|
+
);
|
|
2653
2678
|
}
|
|
2654
|
-
function
|
|
2655
|
-
bot.callbackQuery(/^
|
|
2656
|
-
const
|
|
2657
|
-
|
|
2658
|
-
|
|
2659
|
-
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
const
|
|
2664
|
-
if (userId) await startWorkspaceStep(ctx, core, chatId, userId, agentName);
|
|
2665
|
-
return;
|
|
2666
|
-
}
|
|
2667
|
-
if (data === "m:new:ws:default") {
|
|
2668
|
-
const userId = ctx.from?.id;
|
|
2669
|
-
if (!userId) return;
|
|
2670
|
-
const pending = pendingNewSessions.get(userId);
|
|
2671
|
-
if (!pending?.agentName) return;
|
|
2672
|
-
const workspace = core.configManager.get().workspace.baseDir;
|
|
2673
|
-
await startConfirmStep(ctx, chatId, userId, pending.agentName, workspace);
|
|
2674
|
-
return;
|
|
2675
|
-
}
|
|
2676
|
-
if (data === "m:new:ws:custom") {
|
|
2677
|
-
const userId = ctx.from?.id;
|
|
2678
|
-
if (!userId) return;
|
|
2679
|
-
const pending = pendingNewSessions.get(userId);
|
|
2680
|
-
if (!pending?.agentName) return;
|
|
2681
|
-
try {
|
|
2682
|
-
await ctx.api.editMessageText(
|
|
2683
|
-
chatId,
|
|
2684
|
-
pending.messageId,
|
|
2685
|
-
`\u270F\uFE0F <b>Enter your project path:</b>
|
|
2686
|
-
|
|
2687
|
-
Full path like <code>~/code/my-project</code>
|
|
2688
|
-
Or just the folder name like <code>my-project</code> (will use ${core.configManager.get().workspace.baseDir}/)`,
|
|
2689
|
-
{ parse_mode: "HTML" }
|
|
2690
|
-
);
|
|
2691
|
-
} catch {
|
|
2692
|
-
await ctx.reply(
|
|
2693
|
-
`\u270F\uFE0F <b>Enter your project path:</b>`,
|
|
2694
|
-
{ parse_mode: "HTML" }
|
|
2695
|
-
);
|
|
2696
|
-
}
|
|
2697
|
-
clearTimeout(pending.timer);
|
|
2698
|
-
pending.step = "workspace_input";
|
|
2699
|
-
pending.timer = setTimeout(() => pendingNewSessions.delete(userId), PENDING_TIMEOUT_MS);
|
|
2700
|
-
return;
|
|
2701
|
-
}
|
|
2702
|
-
if (data === "m:new:confirm") {
|
|
2703
|
-
const userId = ctx.from?.id;
|
|
2704
|
-
if (!userId) return;
|
|
2705
|
-
const pending = pendingNewSessions.get(userId);
|
|
2706
|
-
if (!pending?.agentName || !pending?.workspace) return;
|
|
2707
|
-
cleanupPending(userId);
|
|
2708
|
-
const confirmMsgId = pending.messageId;
|
|
2679
|
+
function setupDangerousModeCallbacks(bot, core) {
|
|
2680
|
+
bot.callbackQuery(/^d:/, async (ctx) => {
|
|
2681
|
+
const sessionId = ctx.callbackQuery.data.slice(2);
|
|
2682
|
+
const session = core.sessionManager.getSession(sessionId);
|
|
2683
|
+
if (session) {
|
|
2684
|
+
session.dangerousMode = !session.dangerousMode;
|
|
2685
|
+
log8.info({ sessionId, dangerousMode: session.dangerousMode }, "Dangerous mode toggled via button");
|
|
2686
|
+
core.sessionManager.updateSessionDangerousMode(sessionId, session.dangerousMode).catch(() => {
|
|
2687
|
+
});
|
|
2688
|
+
const toastText2 = session.dangerousMode ? "\u2620\uFE0F Dangerous mode enabled \u2014 permissions auto-approved" : "\u{1F510} Dangerous mode disabled \u2014 permissions shown normally";
|
|
2709
2689
|
try {
|
|
2710
|
-
await ctx.
|
|
2690
|
+
await ctx.answerCallbackQuery({ text: toastText2 });
|
|
2711
2691
|
} catch {
|
|
2712
2692
|
}
|
|
2713
|
-
const resultThreadId = await createSessionDirect(ctx, core, chatId, pending.agentName, pending.workspace);
|
|
2714
2693
|
try {
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
} else {
|
|
2719
|
-
await ctx.api.editMessageText(chatId, confirmMsgId, `\u274C Session creation failed.`, { parse_mode: "HTML" });
|
|
2720
|
-
}
|
|
2694
|
+
await ctx.editMessageReplyMarkup({
|
|
2695
|
+
reply_markup: buildDangerousModeKeyboard(sessionId, session.dangerousMode)
|
|
2696
|
+
});
|
|
2721
2697
|
} catch {
|
|
2722
2698
|
}
|
|
2723
2699
|
return;
|
|
2724
2700
|
}
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
if (userId) cleanupPending(userId);
|
|
2701
|
+
const record = core.sessionManager.getSessionRecord(sessionId);
|
|
2702
|
+
if (!record || record.status === "cancelled" || record.status === "error") {
|
|
2728
2703
|
try {
|
|
2729
|
-
await ctx.
|
|
2704
|
+
await ctx.answerCallbackQuery({ text: "\u26A0\uFE0F Session not found or already ended." });
|
|
2730
2705
|
} catch {
|
|
2731
2706
|
}
|
|
2732
2707
|
return;
|
|
2733
2708
|
}
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
await handleRestart(ctx, core);
|
|
2749
|
-
break;
|
|
2750
|
-
case "m:update":
|
|
2751
|
-
await handleUpdate(ctx, core);
|
|
2752
|
-
break;
|
|
2753
|
-
case "m:integrate":
|
|
2754
|
-
await handleIntegrate(ctx, core);
|
|
2755
|
-
break;
|
|
2756
|
-
case "m:topics":
|
|
2757
|
-
await handleTopics(ctx, core);
|
|
2758
|
-
break;
|
|
2759
|
-
case "m:cleanup:finished":
|
|
2760
|
-
await handleCleanup(ctx, core, chatId, ["finished"]);
|
|
2761
|
-
break;
|
|
2762
|
-
case "m:cleanup:errors":
|
|
2763
|
-
await handleCleanup(ctx, core, chatId, ["error", "cancelled"]);
|
|
2764
|
-
break;
|
|
2765
|
-
case "m:cleanup:all":
|
|
2766
|
-
await handleCleanup(ctx, core, chatId, ["finished", "error", "cancelled"]);
|
|
2767
|
-
break;
|
|
2768
|
-
case "m:cleanup:everything":
|
|
2769
|
-
await handleCleanupEverything(ctx, core, chatId, systemTopicIds);
|
|
2770
|
-
break;
|
|
2771
|
-
case "m:cleanup:everything:confirm":
|
|
2772
|
-
await handleCleanupEverythingConfirmed(ctx, core, chatId, systemTopicIds);
|
|
2773
|
-
break;
|
|
2709
|
+
const newDangerousMode = !(record.dangerousMode ?? false);
|
|
2710
|
+
core.sessionManager.updateSessionDangerousMode(sessionId, newDangerousMode).catch(() => {
|
|
2711
|
+
});
|
|
2712
|
+
log8.info({ sessionId, dangerousMode: newDangerousMode }, "Dangerous mode toggled via button (store-only, session not in memory)");
|
|
2713
|
+
const toastText = newDangerousMode ? "\u2620\uFE0F Dangerous mode enabled \u2014 permissions auto-approved" : "\u{1F510} Dangerous mode disabled \u2014 permissions shown normally";
|
|
2714
|
+
try {
|
|
2715
|
+
await ctx.answerCallbackQuery({ text: toastText });
|
|
2716
|
+
} catch {
|
|
2717
|
+
}
|
|
2718
|
+
try {
|
|
2719
|
+
await ctx.editMessageReplyMarkup({
|
|
2720
|
+
reply_markup: buildDangerousModeKeyboard(sessionId, newDangerousMode)
|
|
2721
|
+
});
|
|
2722
|
+
} catch {
|
|
2774
2723
|
}
|
|
2775
2724
|
});
|
|
2776
2725
|
}
|
|
2777
|
-
async function
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
parse_mode: "HTML"
|
|
2781
|
-
reply_markup: buildMenuKeyboard()
|
|
2782
|
-
});
|
|
2783
|
-
}
|
|
2784
|
-
async function handleNew(ctx, core, chatId, assistant) {
|
|
2785
|
-
const rawMatch = ctx.match;
|
|
2786
|
-
const matchStr = typeof rawMatch === "string" ? rawMatch : "";
|
|
2787
|
-
const args = matchStr.split(" ").filter(Boolean);
|
|
2788
|
-
const agentName = args[0];
|
|
2789
|
-
const workspace = args[1];
|
|
2790
|
-
if (agentName && workspace) {
|
|
2791
|
-
await createSessionDirect(ctx, core, chatId, agentName, workspace);
|
|
2726
|
+
async function handleEnableDangerous(ctx, core) {
|
|
2727
|
+
const threadId = ctx.message?.message_thread_id;
|
|
2728
|
+
if (!threadId) {
|
|
2729
|
+
await ctx.reply("\u26A0\uFE0F This command only works inside a session topic.", { parse_mode: "HTML" });
|
|
2792
2730
|
return;
|
|
2793
2731
|
}
|
|
2794
|
-
const
|
|
2795
|
-
if (
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
const prompt = agentName ? `User wants to create a new session with agent "${agentName}" but didn't specify a workspace. Ask them which project directory to use as workspace.` : `User wants to create a new session. Guide them through choosing an agent and workspace (project directory).`;
|
|
2799
|
-
await assistantSession.enqueuePrompt(prompt);
|
|
2732
|
+
const session = core.sessionManager.getSessionByThread("telegram", String(threadId));
|
|
2733
|
+
if (session) {
|
|
2734
|
+
if (session.dangerousMode) {
|
|
2735
|
+
await ctx.reply("\u2620\uFE0F Dangerous mode is already enabled.", { parse_mode: "HTML" });
|
|
2800
2736
|
return;
|
|
2801
2737
|
}
|
|
2738
|
+
session.dangerousMode = true;
|
|
2739
|
+
core.sessionManager.updateSessionDangerousMode(session.id, true).catch(() => {
|
|
2740
|
+
});
|
|
2741
|
+
} else {
|
|
2742
|
+
const record = core.sessionManager.getRecordByThread("telegram", String(threadId));
|
|
2743
|
+
if (!record || record.status === "cancelled" || record.status === "error") {
|
|
2744
|
+
await ctx.reply("\u26A0\uFE0F No active session in this topic.", { parse_mode: "HTML" });
|
|
2745
|
+
return;
|
|
2746
|
+
}
|
|
2747
|
+
if (record.dangerousMode) {
|
|
2748
|
+
await ctx.reply("\u2620\uFE0F Dangerous mode is already enabled.", { parse_mode: "HTML" });
|
|
2749
|
+
return;
|
|
2750
|
+
}
|
|
2751
|
+
core.sessionManager.updateSessionDangerousMode(record.sessionId, true).catch(() => {
|
|
2752
|
+
});
|
|
2802
2753
|
}
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2754
|
+
await ctx.reply(
|
|
2755
|
+
`\u26A0\uFE0F <b>Dangerous mode enabled</b>
|
|
2756
|
+
|
|
2757
|
+
All permission requests will be auto-approved. Claude can run arbitrary commands without asking.
|
|
2758
|
+
|
|
2759
|
+
Use /disable_dangerous to restore normal behaviour.`,
|
|
2760
|
+
{ parse_mode: "HTML" }
|
|
2761
|
+
);
|
|
2762
|
+
}
|
|
2763
|
+
async function handleDisableDangerous(ctx, core) {
|
|
2764
|
+
const threadId = ctx.message?.message_thread_id;
|
|
2765
|
+
if (!threadId) {
|
|
2766
|
+
await ctx.reply("\u26A0\uFE0F This command only works inside a session topic.", { parse_mode: "HTML" });
|
|
2767
|
+
return;
|
|
2768
|
+
}
|
|
2769
|
+
const session = core.sessionManager.getSessionByThread("telegram", String(threadId));
|
|
2770
|
+
if (session) {
|
|
2771
|
+
if (!session.dangerousMode) {
|
|
2772
|
+
await ctx.reply("\u{1F510} Dangerous mode is already disabled.", { parse_mode: "HTML" });
|
|
2773
|
+
return;
|
|
2774
|
+
}
|
|
2775
|
+
session.dangerousMode = false;
|
|
2776
|
+
core.sessionManager.updateSessionDangerousMode(session.id, false).catch(() => {
|
|
2777
|
+
});
|
|
2778
|
+
} else {
|
|
2779
|
+
const record = core.sessionManager.getRecordByThread("telegram", String(threadId));
|
|
2780
|
+
if (!record || record.status === "cancelled" || record.status === "error") {
|
|
2781
|
+
await ctx.reply("\u26A0\uFE0F No active session in this topic.", { parse_mode: "HTML" });
|
|
2782
|
+
return;
|
|
2783
|
+
}
|
|
2784
|
+
if (!record.dangerousMode) {
|
|
2785
|
+
await ctx.reply("\u{1F510} Dangerous mode is already disabled.", { parse_mode: "HTML" });
|
|
2786
|
+
return;
|
|
2787
|
+
}
|
|
2788
|
+
core.sessionManager.updateSessionDangerousMode(record.sessionId, false).catch(() => {
|
|
2789
|
+
});
|
|
2790
|
+
}
|
|
2791
|
+
await ctx.reply("\u{1F510} <b>Dangerous mode disabled</b>\n\nPermission requests will be shown normally.", { parse_mode: "HTML" });
|
|
2792
|
+
}
|
|
2793
|
+
async function handleUpdate(ctx, core) {
|
|
2794
|
+
if (!core.requestRestart) {
|
|
2795
|
+
await ctx.reply("\u26A0\uFE0F Update is not available (no restart handler registered).", { parse_mode: "HTML" });
|
|
2796
|
+
return;
|
|
2797
|
+
}
|
|
2798
|
+
const { getCurrentVersion, getLatestVersion, compareVersions, runUpdate } = await import("./version-VC5CPXBX.js");
|
|
2799
|
+
const current = getCurrentVersion();
|
|
2800
|
+
const statusMsg = await ctx.reply(`\u{1F50D} Checking for updates... (current: v${escapeHtml(current)})`, { parse_mode: "HTML" });
|
|
2801
|
+
const latest = await getLatestVersion();
|
|
2802
|
+
if (!latest) {
|
|
2803
|
+
await ctx.api.editMessageText(ctx.chat.id, statusMsg.message_id, "\u274C Could not check for updates.", { parse_mode: "HTML" });
|
|
2804
|
+
return;
|
|
2805
|
+
}
|
|
2806
|
+
if (compareVersions(current, latest) >= 0) {
|
|
2807
|
+
await ctx.api.editMessageText(ctx.chat.id, statusMsg.message_id, `\u2705 Already up to date (v${escapeHtml(current)}).`, { parse_mode: "HTML" });
|
|
2808
|
+
return;
|
|
2809
|
+
}
|
|
2810
|
+
await ctx.api.editMessageText(
|
|
2811
|
+
ctx.chat.id,
|
|
2812
|
+
statusMsg.message_id,
|
|
2813
|
+
`\u2B07\uFE0F Updating v${escapeHtml(current)} \u2192 v${escapeHtml(latest)}...`,
|
|
2814
|
+
{ parse_mode: "HTML" }
|
|
2815
|
+
);
|
|
2816
|
+
const ok = await runUpdate();
|
|
2817
|
+
if (!ok) {
|
|
2818
|
+
await ctx.api.editMessageText(ctx.chat.id, statusMsg.message_id, "\u274C Update failed. Try manually: <code>npm install -g @openacp/cli@latest</code>", { parse_mode: "HTML" });
|
|
2819
|
+
return;
|
|
2820
|
+
}
|
|
2821
|
+
await ctx.api.editMessageText(
|
|
2822
|
+
ctx.chat.id,
|
|
2823
|
+
statusMsg.message_id,
|
|
2824
|
+
`\u2705 Updated to v${escapeHtml(latest)}. Restarting...`,
|
|
2825
|
+
{ parse_mode: "HTML" }
|
|
2826
|
+
);
|
|
2827
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
2828
|
+
await core.requestRestart();
|
|
2829
|
+
}
|
|
2830
|
+
async function handleRestart(ctx, core) {
|
|
2831
|
+
if (!core.requestRestart) {
|
|
2832
|
+
await ctx.reply("\u26A0\uFE0F Restart is not available (no restart handler registered).", { parse_mode: "HTML" });
|
|
2833
|
+
return;
|
|
2834
|
+
}
|
|
2835
|
+
await ctx.reply("\u{1F504} <b>Restarting OpenACP...</b>\nRebuilding and restarting. Be back shortly.", { parse_mode: "HTML" });
|
|
2836
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
2837
|
+
await core.requestRestart();
|
|
2838
|
+
}
|
|
2839
|
+
|
|
2840
|
+
// src/adapters/telegram/commands/new-session.ts
|
|
2841
|
+
var log9 = createChildLogger({ module: "telegram-cmd-new-session" });
|
|
2842
|
+
var pendingNewSessions = /* @__PURE__ */ new Map();
|
|
2843
|
+
var PENDING_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
2844
|
+
function cleanupPending(userId) {
|
|
2845
|
+
const pending = pendingNewSessions.get(userId);
|
|
2846
|
+
if (pending) {
|
|
2847
|
+
clearTimeout(pending.timer);
|
|
2848
|
+
pendingNewSessions.delete(userId);
|
|
2849
|
+
}
|
|
2850
|
+
}
|
|
2851
|
+
function botFromCtx(ctx) {
|
|
2852
|
+
return { api: ctx.api };
|
|
2853
|
+
}
|
|
2854
|
+
async function handleNew(ctx, core, chatId, assistant) {
|
|
2855
|
+
const rawMatch = ctx.match;
|
|
2856
|
+
const matchStr = typeof rawMatch === "string" ? rawMatch : "";
|
|
2857
|
+
const args = matchStr.split(" ").filter(Boolean);
|
|
2858
|
+
const agentName = args[0];
|
|
2859
|
+
const workspace = args[1];
|
|
2860
|
+
if (agentName && workspace) {
|
|
2861
|
+
await createSessionDirect(ctx, core, chatId, agentName, workspace);
|
|
2862
|
+
return;
|
|
2863
|
+
}
|
|
2864
|
+
const currentThreadId = ctx.message?.message_thread_id;
|
|
2865
|
+
if (assistant && currentThreadId === assistant.topicId) {
|
|
2866
|
+
const assistantSession = assistant.getSession();
|
|
2867
|
+
if (assistantSession) {
|
|
2868
|
+
const prompt = agentName ? `User wants to create a new session with agent "${agentName}" but didn't specify a workspace. Ask them which project directory to use as workspace.` : `User wants to create a new session. Guide them through choosing an agent and workspace (project directory).`;
|
|
2869
|
+
await assistantSession.enqueuePrompt(prompt);
|
|
2870
|
+
return;
|
|
2871
|
+
}
|
|
2872
|
+
}
|
|
2873
|
+
const userId = ctx.from?.id;
|
|
2874
|
+
if (!userId) return;
|
|
2875
|
+
const agents = core.agentManager.getAvailableAgents();
|
|
2876
|
+
const config = core.configManager.get();
|
|
2877
|
+
if (agentName || agents.length === 1) {
|
|
2878
|
+
const selectedAgent = agentName || config.defaultAgent;
|
|
2879
|
+
await startWorkspaceStep(ctx, core, chatId, userId, selectedAgent);
|
|
2880
|
+
return;
|
|
2881
|
+
}
|
|
2882
|
+
const keyboard = new InlineKeyboard2();
|
|
2883
|
+
for (const agent of agents) {
|
|
2884
|
+
const label = agent.name === config.defaultAgent ? `${agent.name} (default)` : agent.name;
|
|
2885
|
+
keyboard.text(label, `m:new:agent:${agent.name}`).row();
|
|
2886
|
+
}
|
|
2887
|
+
keyboard.text("\u274C Cancel", "m:new:cancel");
|
|
2888
|
+
const msg = await ctx.reply(
|
|
2889
|
+
`\u{1F916} <b>Choose an agent:</b>`,
|
|
2819
2890
|
{ parse_mode: "HTML", reply_markup: keyboard }
|
|
2820
2891
|
);
|
|
2821
2892
|
cleanupPending(userId);
|
|
@@ -2829,7 +2900,7 @@ async function handleNew(ctx, core, chatId, assistant) {
|
|
|
2829
2900
|
async function startWorkspaceStep(ctx, core, chatId, userId, agentName) {
|
|
2830
2901
|
const config = core.configManager.get();
|
|
2831
2902
|
const baseDir = config.workspace.baseDir;
|
|
2832
|
-
const keyboard = new
|
|
2903
|
+
const keyboard = new InlineKeyboard2().text(`\u{1F4C1} Use ${baseDir}`, "m:new:ws:default").row().text("\u270F\uFE0F Enter project path", "m:new:ws:custom").row().text("\u274C Cancel", "m:new:cancel");
|
|
2833
2904
|
const text = `\u{1F4C1} <b>Where should ${escapeHtml(agentName)} work?</b>
|
|
2834
2905
|
|
|
2835
2906
|
Enter the path to your project folder \u2014 the agent will read, write, and run code there.
|
|
@@ -2860,7 +2931,7 @@ Or use the default directory below:`;
|
|
|
2860
2931
|
});
|
|
2861
2932
|
}
|
|
2862
2933
|
async function startConfirmStep(ctx, chatId, userId, agentName, workspace) {
|
|
2863
|
-
const keyboard = new
|
|
2934
|
+
const keyboard = new InlineKeyboard2().text("\u2705 Create", "m:new:confirm").text("\u274C Cancel", "m:new:cancel");
|
|
2864
2935
|
const text = `\u2705 <b>Ready to create session?</b>
|
|
2865
2936
|
|
|
2866
2937
|
<b>Agent:</b> ${escapeHtml(agentName)}
|
|
@@ -2891,7 +2962,7 @@ async function startConfirmStep(ctx, chatId, userId, agentName, workspace) {
|
|
|
2891
2962
|
});
|
|
2892
2963
|
}
|
|
2893
2964
|
async function createSessionDirect(ctx, core, chatId, agentName, workspace) {
|
|
2894
|
-
|
|
2965
|
+
log9.info({ userId: ctx.from?.id, agentName, workspace }, "New session command (direct)");
|
|
2895
2966
|
let threadId;
|
|
2896
2967
|
try {
|
|
2897
2968
|
const topicName = `\u{1F504} New Session`;
|
|
@@ -2921,99 +2992,262 @@ This is your coding session \u2014 chat here to work with the agent.`,
|
|
|
2921
2992
|
reply_markup: buildDangerousModeKeyboard(session.id, false)
|
|
2922
2993
|
}
|
|
2923
2994
|
);
|
|
2924
|
-
session.warmup().catch((err) =>
|
|
2995
|
+
session.warmup().catch((err) => log9.error({ err }, "Warm-up error"));
|
|
2925
2996
|
return threadId ?? null;
|
|
2926
2997
|
} catch (err) {
|
|
2927
|
-
|
|
2998
|
+
log9.error({ err }, "Session creation failed");
|
|
2928
2999
|
if (threadId) {
|
|
2929
3000
|
try {
|
|
2930
3001
|
await ctx.api.deleteForumTopic(chatId, threadId);
|
|
2931
3002
|
} catch {
|
|
2932
3003
|
}
|
|
2933
|
-
}
|
|
2934
|
-
const message = err instanceof Error ? err.message : typeof err === "object" ? JSON.stringify(err) : String(err);
|
|
2935
|
-
await ctx.reply(`\u274C ${escapeHtml(message)}`, { parse_mode: "HTML" });
|
|
2936
|
-
return null;
|
|
2937
|
-
}
|
|
2938
|
-
}
|
|
2939
|
-
async function handleNewChat(ctx, core, chatId) {
|
|
2940
|
-
const threadId = ctx.message?.message_thread_id;
|
|
2941
|
-
if (!threadId) {
|
|
2942
|
-
await ctx.reply(
|
|
2943
|
-
"Use /newchat inside a session topic to inherit its config.",
|
|
2944
|
-
{ parse_mode: "HTML" }
|
|
2945
|
-
);
|
|
2946
|
-
return;
|
|
2947
|
-
}
|
|
2948
|
-
const currentSession = core.sessionManager.getSessionByThread(
|
|
2949
|
-
"telegram",
|
|
2950
|
-
String(threadId)
|
|
2951
|
-
);
|
|
2952
|
-
let agentName;
|
|
2953
|
-
let workspace;
|
|
2954
|
-
if (currentSession) {
|
|
2955
|
-
agentName = currentSession.agentName;
|
|
2956
|
-
workspace = currentSession.workingDirectory;
|
|
2957
|
-
} else {
|
|
2958
|
-
const record = core.sessionManager.getRecordByThread("telegram", String(threadId));
|
|
2959
|
-
if (!record || record.status === "cancelled" || record.status === "error") {
|
|
2960
|
-
await ctx.reply("No active session in this topic.", {
|
|
2961
|
-
parse_mode: "HTML"
|
|
2962
|
-
});
|
|
3004
|
+
}
|
|
3005
|
+
const message = err instanceof Error ? err.message : typeof err === "object" ? JSON.stringify(err) : String(err);
|
|
3006
|
+
await ctx.reply(`\u274C ${escapeHtml(message)}`, { parse_mode: "HTML" });
|
|
3007
|
+
return null;
|
|
3008
|
+
}
|
|
3009
|
+
}
|
|
3010
|
+
async function handleNewChat(ctx, core, chatId) {
|
|
3011
|
+
const threadId = ctx.message?.message_thread_id;
|
|
3012
|
+
if (!threadId) {
|
|
3013
|
+
await ctx.reply(
|
|
3014
|
+
"Use /newchat inside a session topic to inherit its config.",
|
|
3015
|
+
{ parse_mode: "HTML" }
|
|
3016
|
+
);
|
|
3017
|
+
return;
|
|
3018
|
+
}
|
|
3019
|
+
const currentSession = core.sessionManager.getSessionByThread(
|
|
3020
|
+
"telegram",
|
|
3021
|
+
String(threadId)
|
|
3022
|
+
);
|
|
3023
|
+
let agentName;
|
|
3024
|
+
let workspace;
|
|
3025
|
+
if (currentSession) {
|
|
3026
|
+
agentName = currentSession.agentName;
|
|
3027
|
+
workspace = currentSession.workingDirectory;
|
|
3028
|
+
} else {
|
|
3029
|
+
const record = core.sessionManager.getRecordByThread("telegram", String(threadId));
|
|
3030
|
+
if (!record || record.status === "cancelled" || record.status === "error") {
|
|
3031
|
+
await ctx.reply("No active session in this topic.", {
|
|
3032
|
+
parse_mode: "HTML"
|
|
3033
|
+
});
|
|
3034
|
+
return;
|
|
3035
|
+
}
|
|
3036
|
+
agentName = record.agentName;
|
|
3037
|
+
workspace = record.workingDir;
|
|
3038
|
+
}
|
|
3039
|
+
let newThreadId;
|
|
3040
|
+
try {
|
|
3041
|
+
const topicName = `\u{1F504} ${agentName} \u2014 New Chat`;
|
|
3042
|
+
newThreadId = await createSessionTopic(
|
|
3043
|
+
botFromCtx(ctx),
|
|
3044
|
+
chatId,
|
|
3045
|
+
topicName
|
|
3046
|
+
);
|
|
3047
|
+
const topicLink = buildDeepLink(chatId, newThreadId);
|
|
3048
|
+
await ctx.reply(
|
|
3049
|
+
`\u2705 New chat created \u2192 <a href="${topicLink}">Open topic</a>`,
|
|
3050
|
+
{ parse_mode: "HTML" }
|
|
3051
|
+
);
|
|
3052
|
+
await ctx.api.sendMessage(chatId, `\u23F3 Setting up session, please wait...`, {
|
|
3053
|
+
message_thread_id: newThreadId,
|
|
3054
|
+
parse_mode: "HTML"
|
|
3055
|
+
});
|
|
3056
|
+
const session = await core.handleNewSession(
|
|
3057
|
+
"telegram",
|
|
3058
|
+
agentName,
|
|
3059
|
+
workspace
|
|
3060
|
+
);
|
|
3061
|
+
session.threadId = String(newThreadId);
|
|
3062
|
+
await core.sessionManager.updateSessionPlatform(session.id, {
|
|
3063
|
+
topicId: newThreadId
|
|
3064
|
+
});
|
|
3065
|
+
await ctx.api.sendMessage(
|
|
3066
|
+
chatId,
|
|
3067
|
+
`\u2705 New chat (same agent & workspace)
|
|
3068
|
+
<b>Agent:</b> ${escapeHtml(session.agentName)}
|
|
3069
|
+
<b>Workspace:</b> <code>${escapeHtml(session.workingDirectory)}</code>`,
|
|
3070
|
+
{
|
|
3071
|
+
message_thread_id: newThreadId,
|
|
3072
|
+
parse_mode: "HTML",
|
|
3073
|
+
reply_markup: buildDangerousModeKeyboard(session.id, false)
|
|
3074
|
+
}
|
|
3075
|
+
);
|
|
3076
|
+
session.warmup().catch((err) => log9.error({ err }, "Warm-up error"));
|
|
3077
|
+
} catch (err) {
|
|
3078
|
+
if (newThreadId) {
|
|
3079
|
+
try {
|
|
3080
|
+
await ctx.api.deleteForumTopic(chatId, newThreadId);
|
|
3081
|
+
} catch {
|
|
3082
|
+
}
|
|
3083
|
+
}
|
|
3084
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3085
|
+
await ctx.reply(`\u274C ${escapeHtml(message)}`, { parse_mode: "HTML" });
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
async function executeNewSession(bot, core, chatId, agentName, workspace) {
|
|
3089
|
+
const threadId = await createSessionTopic(bot, chatId, "\u{1F504} New Session");
|
|
3090
|
+
const setupMsg = await bot.api.sendMessage(chatId, "\u23F3 Setting up session, please wait...", {
|
|
3091
|
+
message_thread_id: threadId,
|
|
3092
|
+
parse_mode: "HTML"
|
|
3093
|
+
});
|
|
3094
|
+
const firstMsgId = setupMsg.message_id;
|
|
3095
|
+
try {
|
|
3096
|
+
const session = await core.handleNewSession(
|
|
3097
|
+
"telegram",
|
|
3098
|
+
agentName,
|
|
3099
|
+
workspace
|
|
3100
|
+
);
|
|
3101
|
+
session.threadId = String(threadId);
|
|
3102
|
+
await core.sessionManager.updateSessionPlatform(session.id, {
|
|
3103
|
+
topicId: threadId
|
|
3104
|
+
});
|
|
3105
|
+
const finalName = `\u{1F504} ${session.agentName} \u2014 New Session`;
|
|
3106
|
+
await renameSessionTopic(bot, chatId, threadId, finalName);
|
|
3107
|
+
session.warmup().catch((err) => log9.error({ err }, "Warm-up error"));
|
|
3108
|
+
return { session, threadId, firstMsgId };
|
|
3109
|
+
} catch (err) {
|
|
3110
|
+
try {
|
|
3111
|
+
await bot.api.deleteForumTopic(chatId, threadId);
|
|
3112
|
+
} catch {
|
|
3113
|
+
}
|
|
3114
|
+
throw err;
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3117
|
+
async function handlePendingWorkspaceInput(ctx, core, chatId, assistantTopicId) {
|
|
3118
|
+
const userId = ctx.from?.id;
|
|
3119
|
+
if (!userId) return false;
|
|
3120
|
+
const pending = pendingNewSessions.get(userId);
|
|
3121
|
+
if (!pending || !ctx.message?.text) return false;
|
|
3122
|
+
if (pending.step !== "workspace_input" && pending.step !== "workspace") return false;
|
|
3123
|
+
const threadId = ctx.message.message_thread_id;
|
|
3124
|
+
if (threadId && threadId !== assistantTopicId) return false;
|
|
3125
|
+
let workspace = ctx.message.text.trim();
|
|
3126
|
+
if (!workspace || !pending.agentName) {
|
|
3127
|
+
await ctx.reply("\u26A0\uFE0F Please enter a valid directory path.", { parse_mode: "HTML" });
|
|
3128
|
+
return true;
|
|
3129
|
+
}
|
|
3130
|
+
if (!workspace.startsWith("/") && !workspace.startsWith("~")) {
|
|
3131
|
+
const baseDir = core.configManager.get().workspace.baseDir;
|
|
3132
|
+
workspace = `${baseDir.replace(/\/$/, "")}/${workspace}`;
|
|
3133
|
+
}
|
|
3134
|
+
await startConfirmStep(ctx, chatId, userId, pending.agentName, workspace);
|
|
3135
|
+
return true;
|
|
3136
|
+
}
|
|
3137
|
+
async function startInteractiveNewSession(ctx, core, chatId, agentName) {
|
|
3138
|
+
const userId = ctx.from?.id;
|
|
3139
|
+
if (!userId) return;
|
|
3140
|
+
const agents = core.agentManager.getAvailableAgents();
|
|
3141
|
+
const config = core.configManager.get();
|
|
3142
|
+
if (agentName || agents.length === 1) {
|
|
3143
|
+
const selectedAgent = agentName || config.defaultAgent;
|
|
3144
|
+
await startWorkspaceStep(ctx, core, chatId, userId, selectedAgent);
|
|
3145
|
+
return;
|
|
3146
|
+
}
|
|
3147
|
+
const keyboard = new InlineKeyboard2();
|
|
3148
|
+
for (const agent of agents) {
|
|
3149
|
+
const label = agent.name === config.defaultAgent ? `${agent.name} (default)` : agent.name;
|
|
3150
|
+
keyboard.text(label, `m:new:agent:${agent.name}`).row();
|
|
3151
|
+
}
|
|
3152
|
+
keyboard.text("\u274C Cancel", "m:new:cancel");
|
|
3153
|
+
const msg = await ctx.reply(
|
|
3154
|
+
`\u{1F916} <b>Choose an agent:</b>`,
|
|
3155
|
+
{ parse_mode: "HTML", reply_markup: keyboard }
|
|
3156
|
+
);
|
|
3157
|
+
cleanupPending(userId);
|
|
3158
|
+
pendingNewSessions.set(userId, {
|
|
3159
|
+
step: "agent",
|
|
3160
|
+
messageId: msg.message_id,
|
|
3161
|
+
threadId: ctx.callbackQuery?.message?.message_thread_id,
|
|
3162
|
+
timer: setTimeout(() => pendingNewSessions.delete(userId), PENDING_TIMEOUT_MS)
|
|
3163
|
+
});
|
|
3164
|
+
}
|
|
3165
|
+
function setupNewSessionCallbacks(bot, core, chatId) {
|
|
3166
|
+
bot.callbackQuery(/^m:new:/, async (ctx) => {
|
|
3167
|
+
const data = ctx.callbackQuery.data;
|
|
3168
|
+
try {
|
|
3169
|
+
await ctx.answerCallbackQuery();
|
|
3170
|
+
} catch {
|
|
3171
|
+
}
|
|
3172
|
+
if (data.startsWith("m:new:agent:")) {
|
|
3173
|
+
const agentName = data.replace("m:new:agent:", "");
|
|
3174
|
+
const userId = ctx.from?.id;
|
|
3175
|
+
if (userId) await startWorkspaceStep(ctx, core, chatId, userId, agentName);
|
|
3176
|
+
return;
|
|
3177
|
+
}
|
|
3178
|
+
if (data === "m:new:ws:default") {
|
|
3179
|
+
const userId = ctx.from?.id;
|
|
3180
|
+
if (!userId) return;
|
|
3181
|
+
const pending = pendingNewSessions.get(userId);
|
|
3182
|
+
if (!pending?.agentName) return;
|
|
3183
|
+
const workspace = core.configManager.get().workspace.baseDir;
|
|
3184
|
+
await startConfirmStep(ctx, chatId, userId, pending.agentName, workspace);
|
|
3185
|
+
return;
|
|
3186
|
+
}
|
|
3187
|
+
if (data === "m:new:ws:custom") {
|
|
3188
|
+
const userId = ctx.from?.id;
|
|
3189
|
+
if (!userId) return;
|
|
3190
|
+
const pending = pendingNewSessions.get(userId);
|
|
3191
|
+
if (!pending?.agentName) return;
|
|
3192
|
+
try {
|
|
3193
|
+
await ctx.api.editMessageText(
|
|
3194
|
+
chatId,
|
|
3195
|
+
pending.messageId,
|
|
3196
|
+
`\u270F\uFE0F <b>Enter your project path:</b>
|
|
3197
|
+
|
|
3198
|
+
Full path like <code>~/code/my-project</code>
|
|
3199
|
+
Or just the folder name like <code>my-project</code> (will use ${core.configManager.get().workspace.baseDir}/)`,
|
|
3200
|
+
{ parse_mode: "HTML" }
|
|
3201
|
+
);
|
|
3202
|
+
} catch {
|
|
3203
|
+
await ctx.reply(
|
|
3204
|
+
`\u270F\uFE0F <b>Enter your project path:</b>`,
|
|
3205
|
+
{ parse_mode: "HTML" }
|
|
3206
|
+
);
|
|
3207
|
+
}
|
|
3208
|
+
clearTimeout(pending.timer);
|
|
3209
|
+
pending.step = "workspace_input";
|
|
3210
|
+
pending.timer = setTimeout(() => pendingNewSessions.delete(userId), PENDING_TIMEOUT_MS);
|
|
3211
|
+
return;
|
|
3212
|
+
}
|
|
3213
|
+
if (data === "m:new:confirm") {
|
|
3214
|
+
const userId = ctx.from?.id;
|
|
3215
|
+
if (!userId) return;
|
|
3216
|
+
const pending = pendingNewSessions.get(userId);
|
|
3217
|
+
if (!pending?.agentName || !pending?.workspace) return;
|
|
3218
|
+
cleanupPending(userId);
|
|
3219
|
+
const confirmMsgId = pending.messageId;
|
|
3220
|
+
try {
|
|
3221
|
+
await ctx.api.editMessageText(chatId, confirmMsgId, `\u23F3 Creating session...`, { parse_mode: "HTML" });
|
|
3222
|
+
} catch {
|
|
3223
|
+
}
|
|
3224
|
+
const resultThreadId = await createSessionDirect(ctx, core, chatId, pending.agentName, pending.workspace);
|
|
3225
|
+
try {
|
|
3226
|
+
if (resultThreadId) {
|
|
3227
|
+
const link = buildDeepLink(chatId, resultThreadId);
|
|
3228
|
+
await ctx.api.editMessageText(chatId, confirmMsgId, `\u2705 Session created \u2192 <a href="${link}">Open topic</a>`, { parse_mode: "HTML" });
|
|
3229
|
+
} else {
|
|
3230
|
+
await ctx.api.editMessageText(chatId, confirmMsgId, `\u274C Session creation failed.`, { parse_mode: "HTML" });
|
|
3231
|
+
}
|
|
3232
|
+
} catch {
|
|
3233
|
+
}
|
|
2963
3234
|
return;
|
|
2964
3235
|
}
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
let newThreadId;
|
|
2969
|
-
try {
|
|
2970
|
-
const topicName = `\u{1F504} ${agentName} \u2014 New Chat`;
|
|
2971
|
-
newThreadId = await createSessionTopic(
|
|
2972
|
-
botFromCtx(ctx),
|
|
2973
|
-
chatId,
|
|
2974
|
-
topicName
|
|
2975
|
-
);
|
|
2976
|
-
const topicLink = buildDeepLink(chatId, newThreadId);
|
|
2977
|
-
await ctx.reply(
|
|
2978
|
-
`\u2705 New chat created \u2192 <a href="${topicLink}">Open topic</a>`,
|
|
2979
|
-
{ parse_mode: "HTML" }
|
|
2980
|
-
);
|
|
2981
|
-
await ctx.api.sendMessage(chatId, `\u23F3 Setting up session, please wait...`, {
|
|
2982
|
-
message_thread_id: newThreadId,
|
|
2983
|
-
parse_mode: "HTML"
|
|
2984
|
-
});
|
|
2985
|
-
const session = await core.handleNewSession(
|
|
2986
|
-
"telegram",
|
|
2987
|
-
agentName,
|
|
2988
|
-
workspace
|
|
2989
|
-
);
|
|
2990
|
-
session.threadId = String(newThreadId);
|
|
2991
|
-
await core.sessionManager.updateSessionPlatform(session.id, {
|
|
2992
|
-
topicId: newThreadId
|
|
2993
|
-
});
|
|
2994
|
-
await ctx.api.sendMessage(
|
|
2995
|
-
chatId,
|
|
2996
|
-
`\u2705 New chat (same agent & workspace)
|
|
2997
|
-
<b>Agent:</b> ${escapeHtml(session.agentName)}
|
|
2998
|
-
<b>Workspace:</b> <code>${escapeHtml(session.workingDirectory)}</code>`,
|
|
2999
|
-
{
|
|
3000
|
-
message_thread_id: newThreadId,
|
|
3001
|
-
parse_mode: "HTML",
|
|
3002
|
-
reply_markup: buildDangerousModeKeyboard(session.id, false)
|
|
3003
|
-
}
|
|
3004
|
-
);
|
|
3005
|
-
session.warmup().catch((err) => log8.error({ err }, "Warm-up error"));
|
|
3006
|
-
} catch (err) {
|
|
3007
|
-
if (newThreadId) {
|
|
3236
|
+
if (data === "m:new:cancel") {
|
|
3237
|
+
const userId = ctx.from?.id;
|
|
3238
|
+
if (userId) cleanupPending(userId);
|
|
3008
3239
|
try {
|
|
3009
|
-
await ctx.
|
|
3240
|
+
await ctx.editMessageText("\u274C Session creation cancelled.", { parse_mode: "HTML" });
|
|
3010
3241
|
} catch {
|
|
3011
3242
|
}
|
|
3243
|
+
return;
|
|
3012
3244
|
}
|
|
3013
|
-
|
|
3014
|
-
await ctx.reply(`\u274C ${escapeHtml(message)}`, { parse_mode: "HTML" });
|
|
3015
|
-
}
|
|
3245
|
+
});
|
|
3016
3246
|
}
|
|
3247
|
+
|
|
3248
|
+
// src/adapters/telegram/commands/session.ts
|
|
3249
|
+
import { InlineKeyboard as InlineKeyboard3 } from "grammy";
|
|
3250
|
+
var log10 = createChildLogger({ module: "telegram-cmd-session" });
|
|
3017
3251
|
async function handleCancel(ctx, core, assistant) {
|
|
3018
3252
|
const threadId = ctx.message?.message_thread_id;
|
|
3019
3253
|
if (!threadId) return;
|
|
@@ -3031,14 +3265,14 @@ async function handleCancel(ctx, core, assistant) {
|
|
|
3031
3265
|
String(threadId)
|
|
3032
3266
|
);
|
|
3033
3267
|
if (session) {
|
|
3034
|
-
|
|
3268
|
+
log10.info({ sessionId: session.id }, "Cancel session command");
|
|
3035
3269
|
await session.cancel();
|
|
3036
3270
|
await ctx.reply("\u26D4 Session cancelled.", { parse_mode: "HTML" });
|
|
3037
3271
|
return;
|
|
3038
3272
|
}
|
|
3039
3273
|
const record = core.sessionManager.getRecordByThread("telegram", String(threadId));
|
|
3040
3274
|
if (record && record.status !== "cancelled" && record.status !== "error") {
|
|
3041
|
-
|
|
3275
|
+
log10.info({ sessionId: record.sessionId }, "Cancel session command (from store)");
|
|
3042
3276
|
await core.sessionManager.cancelSession(record.sessionId);
|
|
3043
3277
|
await ctx.reply("\u26D4 Session cancelled.", { parse_mode: "HTML" });
|
|
3044
3278
|
}
|
|
@@ -3124,8 +3358,7 @@ async function handleTopics(ctx, core) {
|
|
|
3124
3358
|
<i>...and ${records.length - MAX_DISPLAY} more</i>` : "";
|
|
3125
3359
|
const finishedCount = records.filter((r) => r.status === "finished").length;
|
|
3126
3360
|
const errorCount = records.filter((r) => r.status === "error" || r.status === "cancelled").length;
|
|
3127
|
-
const
|
|
3128
|
-
const keyboard = new InlineKeyboard();
|
|
3361
|
+
const keyboard = new InlineKeyboard3();
|
|
3129
3362
|
if (finishedCount > 0) {
|
|
3130
3363
|
keyboard.text(`Cleanup finished (${finishedCount})`, "m:cleanup:finished").row();
|
|
3131
3364
|
}
|
|
@@ -3144,7 +3377,7 @@ ${lines.join("\n")}${truncated}`,
|
|
|
3144
3377
|
{ parse_mode: "HTML", reply_markup: keyboard }
|
|
3145
3378
|
);
|
|
3146
3379
|
} catch (err) {
|
|
3147
|
-
|
|
3380
|
+
log10.error({ err }, "handleTopics error");
|
|
3148
3381
|
await ctx.reply("\u274C Failed to list sessions.", { parse_mode: "HTML" }).catch(() => {
|
|
3149
3382
|
});
|
|
3150
3383
|
}
|
|
@@ -3168,13 +3401,13 @@ async function handleCleanup(ctx, core, chatId, statuses) {
|
|
|
3168
3401
|
try {
|
|
3169
3402
|
await ctx.api.deleteForumTopic(chatId, topicId);
|
|
3170
3403
|
} catch (err) {
|
|
3171
|
-
|
|
3404
|
+
log10.warn({ err, sessionId: record.sessionId, topicId }, "Failed to delete forum topic during cleanup");
|
|
3172
3405
|
}
|
|
3173
3406
|
}
|
|
3174
3407
|
await core.sessionManager.removeRecord(record.sessionId);
|
|
3175
3408
|
deleted++;
|
|
3176
3409
|
} catch (err) {
|
|
3177
|
-
|
|
3410
|
+
log10.error({ err, sessionId: record.sessionId }, "Failed to cleanup session");
|
|
3178
3411
|
failed++;
|
|
3179
3412
|
}
|
|
3180
3413
|
}
|
|
@@ -3211,7 +3444,7 @@ async function handleCleanupEverything(ctx, core, chatId, systemTopicIds) {
|
|
|
3211
3444
|
const activeWarning = activeCount > 0 ? `
|
|
3212
3445
|
|
|
3213
3446
|
\u26A0\uFE0F <b>${activeCount} active session(s) will be cancelled and their agents stopped!</b>` : "";
|
|
3214
|
-
const keyboard = new
|
|
3447
|
+
const keyboard = new InlineKeyboard3().text("Yes, delete all", "m:cleanup:everything:confirm").text("Cancel", "m:topics");
|
|
3215
3448
|
await ctx.reply(
|
|
3216
3449
|
`<b>Delete ${cleanable.length} topics?</b>
|
|
3217
3450
|
|
|
@@ -3247,7 +3480,7 @@ async function handleCleanupEverythingConfirmed(ctx, core, chatId, systemTopicId
|
|
|
3247
3480
|
try {
|
|
3248
3481
|
await core.sessionManager.cancelSession(record.sessionId);
|
|
3249
3482
|
} catch (err) {
|
|
3250
|
-
|
|
3483
|
+
log10.warn({ err, sessionId: record.sessionId }, "Failed to cancel session during cleanup");
|
|
3251
3484
|
}
|
|
3252
3485
|
}
|
|
3253
3486
|
const topicId = record.platform?.topicId;
|
|
@@ -3255,13 +3488,13 @@ async function handleCleanupEverythingConfirmed(ctx, core, chatId, systemTopicId
|
|
|
3255
3488
|
try {
|
|
3256
3489
|
await ctx.api.deleteForumTopic(chatId, topicId);
|
|
3257
3490
|
} catch (err) {
|
|
3258
|
-
|
|
3491
|
+
log10.warn({ err, sessionId: record.sessionId, topicId }, "Failed to delete forum topic during cleanup");
|
|
3259
3492
|
}
|
|
3260
3493
|
}
|
|
3261
3494
|
await core.sessionManager.removeRecord(record.sessionId);
|
|
3262
3495
|
deleted++;
|
|
3263
3496
|
} catch (err) {
|
|
3264
|
-
|
|
3497
|
+
log10.error({ err, sessionId: record.sessionId }, "Failed to cleanup session");
|
|
3265
3498
|
failed++;
|
|
3266
3499
|
}
|
|
3267
3500
|
}
|
|
@@ -3270,238 +3503,115 @@ async function handleCleanupEverythingConfirmed(ctx, core, chatId, systemTopicId
|
|
|
3270
3503
|
{ parse_mode: "HTML" }
|
|
3271
3504
|
);
|
|
3272
3505
|
}
|
|
3273
|
-
async function
|
|
3274
|
-
const
|
|
3275
|
-
const
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
);
|
|
3280
|
-
const text = lines.length > 0 ? `<b>Available Agents:</b>
|
|
3281
|
-
|
|
3282
|
-
${lines.join("\n")}` : `<b>Available Agents:</b>
|
|
3283
|
-
|
|
3284
|
-
No agents configured.`;
|
|
3285
|
-
await ctx.reply(text, { parse_mode: "HTML" });
|
|
3286
|
-
}
|
|
3287
|
-
async function handleHelp(ctx) {
|
|
3288
|
-
await ctx.reply(
|
|
3289
|
-
`\u{1F4D6} <b>OpenACP Help</b>
|
|
3290
|
-
|
|
3291
|
-
\u{1F680} <b>Getting Started</b>
|
|
3292
|
-
Tap \u{1F195} New Session to start coding with AI.
|
|
3293
|
-
Each session gets its own topic \u2014 chat there to work with the agent.
|
|
3294
|
-
|
|
3295
|
-
\u{1F4A1} <b>Common Tasks</b>
|
|
3296
|
-
/new [agent] [workspace] \u2014 Create new session
|
|
3297
|
-
/cancel \u2014 Cancel session (in session topic)
|
|
3298
|
-
/status \u2014 Show session or system status
|
|
3299
|
-
/sessions \u2014 List all sessions
|
|
3300
|
-
/agents \u2014 List available agents
|
|
3301
|
-
|
|
3302
|
-
\u2699\uFE0F <b>System</b>
|
|
3303
|
-
/restart \u2014 Restart OpenACP
|
|
3304
|
-
/update \u2014 Update to latest version
|
|
3305
|
-
/integrate \u2014 Manage agent integrations
|
|
3306
|
-
/menu \u2014 Show action menu
|
|
3307
|
-
|
|
3308
|
-
\u{1F512} <b>Session Options</b>
|
|
3309
|
-
/enable_dangerous \u2014 Auto-approve permissions
|
|
3310
|
-
/disable_dangerous \u2014 Restore permission prompts
|
|
3311
|
-
/handoff \u2014 Continue session in terminal
|
|
3312
|
-
/clear \u2014 Clear assistant history
|
|
3313
|
-
|
|
3314
|
-
\u{1F4AC} Need help? Just ask me in this topic!`,
|
|
3315
|
-
{ parse_mode: "HTML" }
|
|
3316
|
-
);
|
|
3317
|
-
}
|
|
3318
|
-
async function handleClear(ctx, assistant) {
|
|
3319
|
-
if (!assistant) {
|
|
3320
|
-
await ctx.reply("\u26A0\uFE0F Assistant is not available.", { parse_mode: "HTML" });
|
|
3321
|
-
return;
|
|
3322
|
-
}
|
|
3323
|
-
const threadId = ctx.message?.message_thread_id;
|
|
3324
|
-
if (threadId !== assistant.topicId) {
|
|
3325
|
-
await ctx.reply("\u2139\uFE0F /clear only works in the Assistant topic.", { parse_mode: "HTML" });
|
|
3326
|
-
return;
|
|
3327
|
-
}
|
|
3328
|
-
await ctx.reply("\u{1F504} Clearing assistant history...", { parse_mode: "HTML" });
|
|
3329
|
-
try {
|
|
3330
|
-
await assistant.respawn();
|
|
3331
|
-
await ctx.reply("\u2705 Assistant history cleared.", { parse_mode: "HTML" });
|
|
3332
|
-
} catch (err) {
|
|
3333
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
3334
|
-
await ctx.reply(`\u274C Failed to clear: <code>${message}</code>`, { parse_mode: "HTML" });
|
|
3335
|
-
}
|
|
3336
|
-
}
|
|
3337
|
-
function buildDangerousModeKeyboard(sessionId, enabled) {
|
|
3338
|
-
return new InlineKeyboard().text(
|
|
3339
|
-
enabled ? "\u{1F510} Disable Dangerous Mode" : "\u2620\uFE0F Enable Dangerous Mode",
|
|
3340
|
-
`d:${sessionId}`
|
|
3341
|
-
);
|
|
3506
|
+
async function executeCancelSession(core, excludeSessionId) {
|
|
3507
|
+
const sessions = core.sessionManager.listSessions("telegram").filter((s) => s.status === "active" && s.id !== excludeSessionId).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
3508
|
+
const session = sessions[0];
|
|
3509
|
+
if (!session) return null;
|
|
3510
|
+
await session.cancel();
|
|
3511
|
+
return session;
|
|
3342
3512
|
}
|
|
3343
|
-
function
|
|
3344
|
-
bot.callbackQuery(/^
|
|
3345
|
-
const
|
|
3346
|
-
const session = core.sessionManager.getSession(sessionId);
|
|
3347
|
-
if (session) {
|
|
3348
|
-
session.dangerousMode = !session.dangerousMode;
|
|
3349
|
-
log8.info({ sessionId, dangerousMode: session.dangerousMode }, "Dangerous mode toggled via button");
|
|
3350
|
-
core.sessionManager.updateSessionDangerousMode(sessionId, session.dangerousMode).catch(() => {
|
|
3351
|
-
});
|
|
3352
|
-
const toastText2 = session.dangerousMode ? "\u2620\uFE0F Dangerous mode enabled \u2014 permissions auto-approved" : "\u{1F510} Dangerous mode disabled \u2014 permissions shown normally";
|
|
3353
|
-
try {
|
|
3354
|
-
await ctx.answerCallbackQuery({ text: toastText2 });
|
|
3355
|
-
} catch {
|
|
3356
|
-
}
|
|
3357
|
-
try {
|
|
3358
|
-
await ctx.editMessageReplyMarkup({
|
|
3359
|
-
reply_markup: buildDangerousModeKeyboard(sessionId, session.dangerousMode)
|
|
3360
|
-
});
|
|
3361
|
-
} catch {
|
|
3362
|
-
}
|
|
3363
|
-
return;
|
|
3364
|
-
}
|
|
3365
|
-
const record = core.sessionManager.getSessionRecord(sessionId);
|
|
3366
|
-
if (!record || record.status === "cancelled" || record.status === "error") {
|
|
3367
|
-
try {
|
|
3368
|
-
await ctx.answerCallbackQuery({ text: "\u26A0\uFE0F Session not found or already ended." });
|
|
3369
|
-
} catch {
|
|
3370
|
-
}
|
|
3371
|
-
return;
|
|
3372
|
-
}
|
|
3373
|
-
const newDangerousMode = !(record.dangerousMode ?? false);
|
|
3374
|
-
core.sessionManager.updateSessionDangerousMode(sessionId, newDangerousMode).catch(() => {
|
|
3375
|
-
});
|
|
3376
|
-
log8.info({ sessionId, dangerousMode: newDangerousMode }, "Dangerous mode toggled via button (store-only, session not in memory)");
|
|
3377
|
-
const toastText = newDangerousMode ? "\u2620\uFE0F Dangerous mode enabled \u2014 permissions auto-approved" : "\u{1F510} Dangerous mode disabled \u2014 permissions shown normally";
|
|
3513
|
+
function setupSessionCallbacks(bot, core, chatId, systemTopicIds) {
|
|
3514
|
+
bot.callbackQuery(/^m:cleanup/, async (ctx) => {
|
|
3515
|
+
const data = ctx.callbackQuery.data;
|
|
3378
3516
|
try {
|
|
3379
|
-
await ctx.answerCallbackQuery(
|
|
3517
|
+
await ctx.answerCallbackQuery();
|
|
3380
3518
|
} catch {
|
|
3381
3519
|
}
|
|
3382
|
-
|
|
3383
|
-
|
|
3384
|
-
|
|
3385
|
-
|
|
3386
|
-
|
|
3520
|
+
switch (data) {
|
|
3521
|
+
case "m:cleanup:finished":
|
|
3522
|
+
await handleCleanup(ctx, core, chatId, ["finished"]);
|
|
3523
|
+
break;
|
|
3524
|
+
case "m:cleanup:errors":
|
|
3525
|
+
await handleCleanup(ctx, core, chatId, ["error", "cancelled"]);
|
|
3526
|
+
break;
|
|
3527
|
+
case "m:cleanup:all":
|
|
3528
|
+
await handleCleanup(ctx, core, chatId, ["finished", "error", "cancelled"]);
|
|
3529
|
+
break;
|
|
3530
|
+
case "m:cleanup:everything":
|
|
3531
|
+
await handleCleanupEverything(ctx, core, chatId, systemTopicIds);
|
|
3532
|
+
break;
|
|
3533
|
+
case "m:cleanup:everything:confirm":
|
|
3534
|
+
await handleCleanupEverythingConfirmed(ctx, core, chatId, systemTopicIds);
|
|
3535
|
+
break;
|
|
3387
3536
|
}
|
|
3388
3537
|
});
|
|
3389
3538
|
}
|
|
3390
|
-
async function handleEnableDangerous(ctx, core) {
|
|
3391
|
-
const threadId = ctx.message?.message_thread_id;
|
|
3392
|
-
if (!threadId) {
|
|
3393
|
-
await ctx.reply("\u26A0\uFE0F This command only works inside a session topic.", { parse_mode: "HTML" });
|
|
3394
|
-
return;
|
|
3395
|
-
}
|
|
3396
|
-
const session = core.sessionManager.getSessionByThread("telegram", String(threadId));
|
|
3397
|
-
if (session) {
|
|
3398
|
-
if (session.dangerousMode) {
|
|
3399
|
-
await ctx.reply("\u2620\uFE0F Dangerous mode is already enabled.", { parse_mode: "HTML" });
|
|
3400
|
-
return;
|
|
3401
|
-
}
|
|
3402
|
-
session.dangerousMode = true;
|
|
3403
|
-
core.sessionManager.updateSessionDangerousMode(session.id, true).catch(() => {
|
|
3404
|
-
});
|
|
3405
|
-
} else {
|
|
3406
|
-
const record = core.sessionManager.getRecordByThread("telegram", String(threadId));
|
|
3407
|
-
if (!record || record.status === "cancelled" || record.status === "error") {
|
|
3408
|
-
await ctx.reply("\u26A0\uFE0F No active session in this topic.", { parse_mode: "HTML" });
|
|
3409
|
-
return;
|
|
3410
|
-
}
|
|
3411
|
-
if (record.dangerousMode) {
|
|
3412
|
-
await ctx.reply("\u2620\uFE0F Dangerous mode is already enabled.", { parse_mode: "HTML" });
|
|
3413
|
-
return;
|
|
3414
|
-
}
|
|
3415
|
-
core.sessionManager.updateSessionDangerousMode(record.sessionId, true).catch(() => {
|
|
3416
|
-
});
|
|
3417
|
-
}
|
|
3418
|
-
await ctx.reply(
|
|
3419
|
-
`\u26A0\uFE0F <b>Dangerous mode enabled</b>
|
|
3420
3539
|
|
|
3421
|
-
|
|
3540
|
+
// src/adapters/telegram/commands/menu.ts
|
|
3541
|
+
import { InlineKeyboard as InlineKeyboard4 } from "grammy";
|
|
3542
|
+
function buildMenuKeyboard() {
|
|
3543
|
+
return new InlineKeyboard4().text("\u{1F195} New Session", "m:new").text("\u{1F4CB} Sessions", "m:topics").row().text("\u{1F4CA} Status", "m:status").text("\u{1F916} Agents", "m:agents").row().text("\u{1F517} Integrate", "m:integrate").text("\u2753 Help", "m:help").row().text("\u{1F504} Restart", "m:restart").text("\u2B06\uFE0F Update", "m:update");
|
|
3544
|
+
}
|
|
3545
|
+
async function handleMenu(ctx) {
|
|
3546
|
+
await ctx.reply(`<b>OpenACP Menu</b>
|
|
3547
|
+
Choose an action:`, {
|
|
3548
|
+
parse_mode: "HTML",
|
|
3549
|
+
reply_markup: buildMenuKeyboard()
|
|
3550
|
+
});
|
|
3551
|
+
}
|
|
3552
|
+
async function handleAgents(ctx, core) {
|
|
3553
|
+
const agents = core.agentManager.getAvailableAgents();
|
|
3554
|
+
const defaultAgent = core.configManager.get().defaultAgent;
|
|
3555
|
+
const lines = agents.map(
|
|
3556
|
+
(a) => `\u2022 <b>${escapeHtml(a.name)}</b>${a.name === defaultAgent ? " (default)" : ""}
|
|
3557
|
+
<code>${escapeHtml(a.command)} ${a.args.map((arg) => escapeHtml(arg)).join(" ")}</code>`
|
|
3558
|
+
);
|
|
3559
|
+
const text = lines.length > 0 ? `<b>Available Agents:</b>
|
|
3560
|
+
|
|
3561
|
+
${lines.join("\n")}` : `<b>Available Agents:</b>
|
|
3422
3562
|
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
);
|
|
3563
|
+
No agents configured.`;
|
|
3564
|
+
await ctx.reply(text, { parse_mode: "HTML" });
|
|
3426
3565
|
}
|
|
3427
|
-
async function
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3437
|
-
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
await ctx.api.editMessageText(
|
|
3456
|
-
ctx.chat.id,
|
|
3457
|
-
statusMsg.message_id,
|
|
3458
|
-
`\u2705 Updated to v${escapeHtml(latest)}. Restarting...`,
|
|
3566
|
+
async function handleHelp(ctx) {
|
|
3567
|
+
await ctx.reply(
|
|
3568
|
+
`\u{1F4D6} <b>OpenACP Help</b>
|
|
3569
|
+
|
|
3570
|
+
\u{1F680} <b>Getting Started</b>
|
|
3571
|
+
Tap \u{1F195} New Session to start coding with AI.
|
|
3572
|
+
Each session gets its own topic \u2014 chat there to work with the agent.
|
|
3573
|
+
|
|
3574
|
+
\u{1F4A1} <b>Common Tasks</b>
|
|
3575
|
+
/new [agent] [workspace] \u2014 Create new session
|
|
3576
|
+
/cancel \u2014 Cancel session (in session topic)
|
|
3577
|
+
/status \u2014 Show session or system status
|
|
3578
|
+
/sessions \u2014 List all sessions
|
|
3579
|
+
/agents \u2014 List available agents
|
|
3580
|
+
|
|
3581
|
+
\u2699\uFE0F <b>System</b>
|
|
3582
|
+
/restart \u2014 Restart OpenACP
|
|
3583
|
+
/update \u2014 Update to latest version
|
|
3584
|
+
/integrate \u2014 Manage agent integrations
|
|
3585
|
+
/menu \u2014 Show action menu
|
|
3586
|
+
|
|
3587
|
+
\u{1F512} <b>Session Options</b>
|
|
3588
|
+
/enable_dangerous \u2014 Auto-approve permissions
|
|
3589
|
+
/disable_dangerous \u2014 Restore permission prompts
|
|
3590
|
+
/handoff \u2014 Continue session in terminal
|
|
3591
|
+
/clear \u2014 Clear assistant history
|
|
3592
|
+
|
|
3593
|
+
\u{1F4AC} Need help? Just ask me in this topic!`,
|
|
3459
3594
|
{ parse_mode: "HTML" }
|
|
3460
3595
|
);
|
|
3461
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
3462
|
-
await core.requestRestart();
|
|
3463
3596
|
}
|
|
3464
|
-
async function
|
|
3465
|
-
if (!
|
|
3466
|
-
await ctx.reply("\u26A0\uFE0F
|
|
3597
|
+
async function handleClear(ctx, assistant) {
|
|
3598
|
+
if (!assistant) {
|
|
3599
|
+
await ctx.reply("\u26A0\uFE0F Assistant is not available.", { parse_mode: "HTML" });
|
|
3467
3600
|
return;
|
|
3468
3601
|
}
|
|
3469
|
-
await ctx.reply("\u{1F504} <b>Restarting OpenACP...</b>\nRebuilding and restarting. Be back shortly.", { parse_mode: "HTML" });
|
|
3470
|
-
await new Promise((r) => setTimeout(r, 500));
|
|
3471
|
-
await core.requestRestart();
|
|
3472
|
-
}
|
|
3473
|
-
async function handleDisableDangerous(ctx, core) {
|
|
3474
3602
|
const threadId = ctx.message?.message_thread_id;
|
|
3475
|
-
if (
|
|
3476
|
-
await ctx.reply("\
|
|
3603
|
+
if (threadId !== assistant.topicId) {
|
|
3604
|
+
await ctx.reply("\u2139\uFE0F /clear only works in the Assistant topic.", { parse_mode: "HTML" });
|
|
3477
3605
|
return;
|
|
3478
3606
|
}
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
core.sessionManager.updateSessionDangerousMode(session.id, false).catch(() => {
|
|
3487
|
-
});
|
|
3488
|
-
} else {
|
|
3489
|
-
const record = core.sessionManager.getRecordByThread("telegram", String(threadId));
|
|
3490
|
-
if (!record || record.status === "cancelled" || record.status === "error") {
|
|
3491
|
-
await ctx.reply("\u26A0\uFE0F No active session in this topic.", { parse_mode: "HTML" });
|
|
3492
|
-
return;
|
|
3493
|
-
}
|
|
3494
|
-
if (!record.dangerousMode) {
|
|
3495
|
-
await ctx.reply("\u{1F510} Dangerous mode is already disabled.", { parse_mode: "HTML" });
|
|
3496
|
-
return;
|
|
3497
|
-
}
|
|
3498
|
-
core.sessionManager.updateSessionDangerousMode(record.sessionId, false).catch(() => {
|
|
3499
|
-
});
|
|
3607
|
+
await ctx.reply("\u{1F504} Clearing assistant history...", { parse_mode: "HTML" });
|
|
3608
|
+
try {
|
|
3609
|
+
await assistant.respawn();
|
|
3610
|
+
await ctx.reply("\u2705 Assistant history cleared.", { parse_mode: "HTML" });
|
|
3611
|
+
} catch (err) {
|
|
3612
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
3613
|
+
await ctx.reply(`\u274C Failed to clear: <code>${message}</code>`, { parse_mode: "HTML" });
|
|
3500
3614
|
}
|
|
3501
|
-
await ctx.reply("\u{1F510} <b>Dangerous mode disabled</b>\n\nPermission requests will be shown normally.", { parse_mode: "HTML" });
|
|
3502
|
-
}
|
|
3503
|
-
function botFromCtx(ctx) {
|
|
3504
|
-
return { api: ctx.api };
|
|
3505
3615
|
}
|
|
3506
3616
|
var TELEGRAM_MSG_LIMIT = 4096;
|
|
3507
3617
|
function buildSkillMessages(commands) {
|
|
@@ -3522,46 +3632,13 @@ function buildSkillMessages(commands) {
|
|
|
3522
3632
|
if (current) messages.push(current);
|
|
3523
3633
|
return messages;
|
|
3524
3634
|
}
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
message_thread_id: threadId,
|
|
3529
|
-
parse_mode: "HTML"
|
|
3530
|
-
});
|
|
3531
|
-
const firstMsgId = setupMsg.message_id;
|
|
3532
|
-
try {
|
|
3533
|
-
const session = await core.handleNewSession(
|
|
3534
|
-
"telegram",
|
|
3535
|
-
agentName,
|
|
3536
|
-
workspace
|
|
3537
|
-
);
|
|
3538
|
-
session.threadId = String(threadId);
|
|
3539
|
-
await core.sessionManager.updateSessionPlatform(session.id, {
|
|
3540
|
-
topicId: threadId
|
|
3541
|
-
});
|
|
3542
|
-
const finalName = `\u{1F504} ${session.agentName} \u2014 New Session`;
|
|
3543
|
-
await renameSessionTopic(bot, chatId, threadId, finalName);
|
|
3544
|
-
session.warmup().catch((err) => log8.error({ err }, "Warm-up error"));
|
|
3545
|
-
return { session, threadId, firstMsgId };
|
|
3546
|
-
} catch (err) {
|
|
3547
|
-
try {
|
|
3548
|
-
await bot.api.deleteForumTopic(chatId, threadId);
|
|
3549
|
-
} catch {
|
|
3550
|
-
}
|
|
3551
|
-
throw err;
|
|
3552
|
-
}
|
|
3553
|
-
}
|
|
3554
|
-
async function executeCancelSession(core, excludeSessionId) {
|
|
3555
|
-
const sessions = core.sessionManager.listSessions("telegram").filter((s) => s.status === "active" && s.id !== excludeSessionId).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
3556
|
-
const session = sessions[0];
|
|
3557
|
-
if (!session) return null;
|
|
3558
|
-
await session.cancel();
|
|
3559
|
-
return session;
|
|
3560
|
-
}
|
|
3635
|
+
|
|
3636
|
+
// src/adapters/telegram/commands/integrate.ts
|
|
3637
|
+
import { InlineKeyboard as InlineKeyboard5 } from "grammy";
|
|
3561
3638
|
async function handleIntegrate(ctx, _core) {
|
|
3562
3639
|
const { listIntegrations } = await import("./integrate-WUPLRJD3.js");
|
|
3563
3640
|
const agents = listIntegrations();
|
|
3564
|
-
const keyboard = new
|
|
3641
|
+
const keyboard = new InlineKeyboard5();
|
|
3565
3642
|
for (const agent of agents) {
|
|
3566
3643
|
keyboard.text(`\u{1F916} ${agent}`, `i:agent:${agent}`).row();
|
|
3567
3644
|
}
|
|
@@ -3573,7 +3650,7 @@ Select an agent to manage its integrations.`,
|
|
|
3573
3650
|
);
|
|
3574
3651
|
}
|
|
3575
3652
|
function buildAgentItemsKeyboard(agentName, items) {
|
|
3576
|
-
const keyboard = new
|
|
3653
|
+
const keyboard = new InlineKeyboard5();
|
|
3577
3654
|
for (const item of items) {
|
|
3578
3655
|
const installed = item.isInstalled();
|
|
3579
3656
|
keyboard.text(
|
|
@@ -3594,7 +3671,7 @@ function setupIntegrateCallbacks(bot, core) {
|
|
|
3594
3671
|
if (data === "i:back") {
|
|
3595
3672
|
const { listIntegrations } = await import("./integrate-WUPLRJD3.js");
|
|
3596
3673
|
const agents = listIntegrations();
|
|
3597
|
-
const keyboard2 = new
|
|
3674
|
+
const keyboard2 = new InlineKeyboard5();
|
|
3598
3675
|
for (const agent of agents) {
|
|
3599
3676
|
keyboard2.text(`\u{1F916} ${agent}`, `i:agent:${agent}`).row();
|
|
3600
3677
|
}
|
|
@@ -3673,51 +3750,59 @@ ${resultText}`,
|
|
|
3673
3750
|
}
|
|
3674
3751
|
});
|
|
3675
3752
|
}
|
|
3676
|
-
|
|
3677
|
-
|
|
3678
|
-
|
|
3679
|
-
|
|
3680
|
-
|
|
3681
|
-
|
|
3682
|
-
|
|
3683
|
-
|
|
3684
|
-
|
|
3685
|
-
|
|
3686
|
-
|
|
3687
|
-
|
|
3688
|
-
|
|
3689
|
-
|
|
3690
|
-
|
|
3691
|
-
|
|
3692
|
-
|
|
3693
|
-
await startConfirmStep(ctx, chatId, userId, pending.agentName, workspace);
|
|
3694
|
-
return true;
|
|
3753
|
+
|
|
3754
|
+
// src/adapters/telegram/commands/index.ts
|
|
3755
|
+
function setupCommands(bot, core, chatId, assistant) {
|
|
3756
|
+
bot.command("new", (ctx) => handleNew(ctx, core, chatId, assistant));
|
|
3757
|
+
bot.command("newchat", (ctx) => handleNewChat(ctx, core, chatId));
|
|
3758
|
+
bot.command("cancel", (ctx) => handleCancel(ctx, core, assistant));
|
|
3759
|
+
bot.command("status", (ctx) => handleStatus(ctx, core));
|
|
3760
|
+
bot.command("sessions", (ctx) => handleTopics(ctx, core));
|
|
3761
|
+
bot.command("agents", (ctx) => handleAgents(ctx, core));
|
|
3762
|
+
bot.command("help", (ctx) => handleHelp(ctx));
|
|
3763
|
+
bot.command("menu", (ctx) => handleMenu(ctx));
|
|
3764
|
+
bot.command("enable_dangerous", (ctx) => handleEnableDangerous(ctx, core));
|
|
3765
|
+
bot.command("disable_dangerous", (ctx) => handleDisableDangerous(ctx, core));
|
|
3766
|
+
bot.command("restart", (ctx) => handleRestart(ctx, core));
|
|
3767
|
+
bot.command("update", (ctx) => handleUpdate(ctx, core));
|
|
3768
|
+
bot.command("integrate", (ctx) => handleIntegrate(ctx, core));
|
|
3769
|
+
bot.command("clear", (ctx) => handleClear(ctx, assistant));
|
|
3695
3770
|
}
|
|
3696
|
-
|
|
3697
|
-
|
|
3698
|
-
|
|
3699
|
-
|
|
3700
|
-
|
|
3701
|
-
|
|
3702
|
-
|
|
3703
|
-
|
|
3704
|
-
|
|
3705
|
-
|
|
3706
|
-
|
|
3707
|
-
|
|
3708
|
-
|
|
3709
|
-
|
|
3710
|
-
|
|
3711
|
-
|
|
3712
|
-
|
|
3713
|
-
|
|
3714
|
-
|
|
3715
|
-
|
|
3716
|
-
|
|
3717
|
-
|
|
3718
|
-
|
|
3719
|
-
|
|
3720
|
-
|
|
3771
|
+
function setupAllCallbacks(bot, core, chatId, systemTopicIds) {
|
|
3772
|
+
setupNewSessionCallbacks(bot, core, chatId);
|
|
3773
|
+
setupSessionCallbacks(bot, core, chatId, systemTopicIds);
|
|
3774
|
+
bot.callbackQuery(/^m:/, async (ctx) => {
|
|
3775
|
+
const data = ctx.callbackQuery.data;
|
|
3776
|
+
try {
|
|
3777
|
+
await ctx.answerCallbackQuery();
|
|
3778
|
+
} catch {
|
|
3779
|
+
}
|
|
3780
|
+
switch (data) {
|
|
3781
|
+
case "m:new":
|
|
3782
|
+
await handleNew(ctx, core, chatId);
|
|
3783
|
+
break;
|
|
3784
|
+
case "m:status":
|
|
3785
|
+
await handleStatus(ctx, core);
|
|
3786
|
+
break;
|
|
3787
|
+
case "m:agents":
|
|
3788
|
+
await handleAgents(ctx, core);
|
|
3789
|
+
break;
|
|
3790
|
+
case "m:help":
|
|
3791
|
+
await handleHelp(ctx);
|
|
3792
|
+
break;
|
|
3793
|
+
case "m:restart":
|
|
3794
|
+
await handleRestart(ctx, core);
|
|
3795
|
+
break;
|
|
3796
|
+
case "m:update":
|
|
3797
|
+
await handleUpdate(ctx, core);
|
|
3798
|
+
break;
|
|
3799
|
+
case "m:integrate":
|
|
3800
|
+
await handleIntegrate(ctx, core);
|
|
3801
|
+
break;
|
|
3802
|
+
case "m:topics":
|
|
3803
|
+
await handleTopics(ctx, core);
|
|
3804
|
+
break;
|
|
3805
|
+
}
|
|
3721
3806
|
});
|
|
3722
3807
|
}
|
|
3723
3808
|
var STATIC_COMMANDS = [
|
|
@@ -3739,9 +3824,9 @@ var STATIC_COMMANDS = [
|
|
|
3739
3824
|
];
|
|
3740
3825
|
|
|
3741
3826
|
// src/adapters/telegram/permissions.ts
|
|
3742
|
-
import { InlineKeyboard as
|
|
3827
|
+
import { InlineKeyboard as InlineKeyboard6 } from "grammy";
|
|
3743
3828
|
import { nanoid as nanoid2 } from "nanoid";
|
|
3744
|
-
var
|
|
3829
|
+
var log11 = createChildLogger({ module: "telegram-permissions" });
|
|
3745
3830
|
var PermissionHandler = class {
|
|
3746
3831
|
constructor(bot, chatId, getSession, sendNotification) {
|
|
3747
3832
|
this.bot = bot;
|
|
@@ -3758,7 +3843,7 @@ var PermissionHandler = class {
|
|
|
3758
3843
|
requestId: request.id,
|
|
3759
3844
|
options: request.options.map((o) => ({ id: o.id, isAllow: o.isAllow }))
|
|
3760
3845
|
});
|
|
3761
|
-
const keyboard = new
|
|
3846
|
+
const keyboard = new InlineKeyboard6();
|
|
3762
3847
|
for (const option of request.options) {
|
|
3763
3848
|
const emoji = option.isAllow ? "\u2705" : "\u274C";
|
|
3764
3849
|
keyboard.text(`${emoji} ${option.label}`, `p:${callbackKey}:${option.id}`);
|
|
@@ -3801,7 +3886,7 @@ ${escapeHtml(request.description)}`,
|
|
|
3801
3886
|
}
|
|
3802
3887
|
const session = this.getSession(pending.sessionId);
|
|
3803
3888
|
const isAllow = pending.options.find((o) => o.id === optionId)?.isAllow ?? false;
|
|
3804
|
-
|
|
3889
|
+
log11.info({ requestId: pending.requestId, optionId, isAllow }, "Permission responded");
|
|
3805
3890
|
if (session?.permissionGate.requestId === pending.requestId) {
|
|
3806
3891
|
session.permissionGate.resolve(optionId);
|
|
3807
3892
|
}
|
|
@@ -4183,10 +4268,10 @@ Session logs auto-cleanup: 30 days (configurable via \`logging.sessionLogRetenti
|
|
|
4183
4268
|
`;
|
|
4184
4269
|
|
|
4185
4270
|
// src/adapters/telegram/assistant.ts
|
|
4186
|
-
var
|
|
4271
|
+
var log12 = createChildLogger({ module: "telegram-assistant" });
|
|
4187
4272
|
async function spawnAssistant(core, adapter, assistantTopicId) {
|
|
4188
4273
|
const config = core.configManager.get();
|
|
4189
|
-
|
|
4274
|
+
log12.info({ agent: config.defaultAgent }, "Creating assistant session...");
|
|
4190
4275
|
const session = await core.sessionManager.createSession(
|
|
4191
4276
|
"telegram",
|
|
4192
4277
|
config.defaultAgent,
|
|
@@ -4195,7 +4280,7 @@ async function spawnAssistant(core, adapter, assistantTopicId) {
|
|
|
4195
4280
|
);
|
|
4196
4281
|
session.threadId = String(assistantTopicId);
|
|
4197
4282
|
session.name = "Assistant";
|
|
4198
|
-
|
|
4283
|
+
log12.info({ sessionId: session.id }, "Assistant agent spawned");
|
|
4199
4284
|
core.wireSessionEvents(session, adapter);
|
|
4200
4285
|
const allRecords = core.sessionManager.listRecords();
|
|
4201
4286
|
const activeCount = allRecords.filter((r) => r.status === "active" || r.status === "initializing").length;
|
|
@@ -4212,9 +4297,9 @@ async function spawnAssistant(core, adapter, assistantTopicId) {
|
|
|
4212
4297
|
};
|
|
4213
4298
|
const systemPrompt = buildAssistantSystemPrompt(ctx);
|
|
4214
4299
|
const ready = session.enqueuePrompt(systemPrompt).then(() => {
|
|
4215
|
-
|
|
4300
|
+
log12.info({ sessionId: session.id }, "Assistant system prompt completed");
|
|
4216
4301
|
}).catch((err) => {
|
|
4217
|
-
|
|
4302
|
+
log12.warn({ err }, "Assistant system prompt failed");
|
|
4218
4303
|
});
|
|
4219
4304
|
return { session, ready };
|
|
4220
4305
|
}
|
|
@@ -4347,7 +4432,7 @@ function redirectToAssistant(chatId, assistantTopicId) {
|
|
|
4347
4432
|
}
|
|
4348
4433
|
|
|
4349
4434
|
// src/adapters/telegram/activity.ts
|
|
4350
|
-
var
|
|
4435
|
+
var log13 = createChildLogger({ module: "telegram:activity" });
|
|
4351
4436
|
var THINKING_REFRESH_MS = 15e3;
|
|
4352
4437
|
var THINKING_MAX_MS = 3 * 60 * 1e3;
|
|
4353
4438
|
var ThinkingIndicator = class {
|
|
@@ -4379,7 +4464,7 @@ var ThinkingIndicator = class {
|
|
|
4379
4464
|
this.startRefreshTimer();
|
|
4380
4465
|
}
|
|
4381
4466
|
} catch (err) {
|
|
4382
|
-
|
|
4467
|
+
log13.warn({ err }, "ThinkingIndicator.show() failed");
|
|
4383
4468
|
} finally {
|
|
4384
4469
|
this.sending = false;
|
|
4385
4470
|
}
|
|
@@ -4452,7 +4537,7 @@ var UsageMessage = class {
|
|
|
4452
4537
|
if (result) this.msgId = result.message_id;
|
|
4453
4538
|
}
|
|
4454
4539
|
} catch (err) {
|
|
4455
|
-
|
|
4540
|
+
log13.warn({ err }, "UsageMessage.send() failed");
|
|
4456
4541
|
}
|
|
4457
4542
|
}
|
|
4458
4543
|
getMsgId() {
|
|
@@ -4465,7 +4550,7 @@ var UsageMessage = class {
|
|
|
4465
4550
|
try {
|
|
4466
4551
|
await this.sendQueue.enqueue(() => this.api.deleteMessage(this.chatId, id));
|
|
4467
4552
|
} catch (err) {
|
|
4468
|
-
|
|
4553
|
+
log13.warn({ err }, "UsageMessage.delete() failed");
|
|
4469
4554
|
}
|
|
4470
4555
|
}
|
|
4471
4556
|
};
|
|
@@ -4551,7 +4636,7 @@ var PlanCard = class {
|
|
|
4551
4636
|
if (result) this.msgId = result.message_id;
|
|
4552
4637
|
}
|
|
4553
4638
|
} catch (err) {
|
|
4554
|
-
|
|
4639
|
+
log13.warn({ err }, "PlanCard flush failed");
|
|
4555
4640
|
}
|
|
4556
4641
|
}
|
|
4557
4642
|
};
|
|
@@ -4614,7 +4699,7 @@ var ActivityTracker = class {
|
|
|
4614
4699
|
})
|
|
4615
4700
|
);
|
|
4616
4701
|
} catch (err) {
|
|
4617
|
-
|
|
4702
|
+
log13.warn({ err }, "ActivityTracker.onComplete() Done send failed");
|
|
4618
4703
|
}
|
|
4619
4704
|
}
|
|
4620
4705
|
}
|
|
@@ -4697,7 +4782,7 @@ var TelegramSendQueue = class {
|
|
|
4697
4782
|
|
|
4698
4783
|
// src/adapters/telegram/action-detect.ts
|
|
4699
4784
|
import { nanoid as nanoid3 } from "nanoid";
|
|
4700
|
-
import { InlineKeyboard as
|
|
4785
|
+
import { InlineKeyboard as InlineKeyboard7 } from "grammy";
|
|
4701
4786
|
var CMD_NEW_RE = /\/new(?:\s+([^\s\u0080-\uFFFF]+)(?:\s+([^\s\u0080-\uFFFF]+))?)?/;
|
|
4702
4787
|
var CMD_CANCEL_RE = /\/cancel\b/;
|
|
4703
4788
|
var KW_NEW_RE = /(?:create|new)\s+session/i;
|
|
@@ -4744,7 +4829,7 @@ function removeAction(id) {
|
|
|
4744
4829
|
actionMap.delete(id);
|
|
4745
4830
|
}
|
|
4746
4831
|
function buildActionKeyboard(actionId, action) {
|
|
4747
|
-
const keyboard = new
|
|
4832
|
+
const keyboard = new InlineKeyboard7();
|
|
4748
4833
|
if (action.action === "new_session") {
|
|
4749
4834
|
keyboard.text("\u2705 Create session", `a:${actionId}`);
|
|
4750
4835
|
keyboard.text("\u274C Cancel", `a:dismiss:${actionId}`);
|
|
@@ -4852,7 +4937,7 @@ function setupActionCallbacks(bot, core, chatId, getAssistantSessionId) {
|
|
|
4852
4937
|
}
|
|
4853
4938
|
|
|
4854
4939
|
// src/adapters/telegram/adapter.ts
|
|
4855
|
-
var
|
|
4940
|
+
var log14 = createChildLogger({ module: "telegram" });
|
|
4856
4941
|
function patchedFetch(input, init) {
|
|
4857
4942
|
if (init?.signal && !(init.signal instanceof AbortSignal)) {
|
|
4858
4943
|
const nativeController = new AbortController();
|
|
@@ -4903,7 +4988,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
4903
4988
|
this.bot = new Bot(this.telegramConfig.botToken, { client: { fetch: patchedFetch } });
|
|
4904
4989
|
this.bot.catch((err) => {
|
|
4905
4990
|
const rootCause = err.error instanceof Error ? err.error : err;
|
|
4906
|
-
|
|
4991
|
+
log14.error({ err: rootCause }, "Telegram bot error");
|
|
4907
4992
|
});
|
|
4908
4993
|
this.bot.api.config.use(async (prev, method, payload, signal) => {
|
|
4909
4994
|
const maxRetries = 3;
|
|
@@ -4917,7 +5002,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
4917
5002
|
if (rateLimitedMethods.includes(method)) {
|
|
4918
5003
|
this.sendQueue.onRateLimited();
|
|
4919
5004
|
}
|
|
4920
|
-
|
|
5005
|
+
log14.warn(
|
|
4921
5006
|
{ method, retryAfter, attempt: attempt + 1 },
|
|
4922
5007
|
"Rate limited by Telegram, retrying"
|
|
4923
5008
|
);
|
|
@@ -4969,7 +5054,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
4969
5054
|
() => this.assistantSession?.id
|
|
4970
5055
|
);
|
|
4971
5056
|
setupIntegrateCallbacks(this.bot, this.core);
|
|
4972
|
-
|
|
5057
|
+
setupAllCallbacks(
|
|
4973
5058
|
this.bot,
|
|
4974
5059
|
this.core,
|
|
4975
5060
|
this.telegramConfig.chatId,
|
|
@@ -5042,7 +5127,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
5042
5127
|
this.setupRoutes();
|
|
5043
5128
|
this.bot.start({
|
|
5044
5129
|
allowed_updates: ["message", "callback_query"],
|
|
5045
|
-
onStart: () =>
|
|
5130
|
+
onStart: () => log14.info(
|
|
5046
5131
|
{ chatId: this.telegramConfig.chatId },
|
|
5047
5132
|
"Telegram bot started"
|
|
5048
5133
|
)
|
|
@@ -5064,10 +5149,10 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
5064
5149
|
reply_markup: buildMenuKeyboard()
|
|
5065
5150
|
});
|
|
5066
5151
|
} catch (err) {
|
|
5067
|
-
|
|
5152
|
+
log14.warn({ err }, "Failed to send welcome message");
|
|
5068
5153
|
}
|
|
5069
5154
|
try {
|
|
5070
|
-
|
|
5155
|
+
log14.info("Spawning assistant session...");
|
|
5071
5156
|
const { session, ready } = await spawnAssistant(
|
|
5072
5157
|
this.core,
|
|
5073
5158
|
this,
|
|
@@ -5075,13 +5160,13 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
5075
5160
|
);
|
|
5076
5161
|
this.assistantSession = session;
|
|
5077
5162
|
this.assistantInitializing = true;
|
|
5078
|
-
|
|
5163
|
+
log14.info({ sessionId: session.id }, "Assistant session ready, system prompt running in background");
|
|
5079
5164
|
ready.then(() => {
|
|
5080
5165
|
this.assistantInitializing = false;
|
|
5081
|
-
|
|
5166
|
+
log14.info({ sessionId: session.id }, "Assistant ready for user messages");
|
|
5082
5167
|
});
|
|
5083
5168
|
} catch (err) {
|
|
5084
|
-
|
|
5169
|
+
log14.error({ err }, "Failed to spawn assistant");
|
|
5085
5170
|
this.bot.api.sendMessage(
|
|
5086
5171
|
this.telegramConfig.chatId,
|
|
5087
5172
|
`\u26A0\uFE0F <b>Failed to start assistant session.</b>
|
|
@@ -5097,7 +5182,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
5097
5182
|
await this.assistantSession.destroy();
|
|
5098
5183
|
}
|
|
5099
5184
|
await this.bot.stop();
|
|
5100
|
-
|
|
5185
|
+
log14.info("Telegram bot stopped");
|
|
5101
5186
|
}
|
|
5102
5187
|
setupRoutes() {
|
|
5103
5188
|
this.bot.on("message:text", async (ctx) => {
|
|
@@ -5123,7 +5208,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
5123
5208
|
ctx.replyWithChatAction("typing").catch(() => {
|
|
5124
5209
|
});
|
|
5125
5210
|
handleAssistantMessage(this.assistantSession, ctx.message.text).catch(
|
|
5126
|
-
(err) =>
|
|
5211
|
+
(err) => log14.error({ err }, "Assistant error")
|
|
5127
5212
|
);
|
|
5128
5213
|
return;
|
|
5129
5214
|
}
|
|
@@ -5140,7 +5225,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
5140
5225
|
threadId: String(threadId),
|
|
5141
5226
|
userId: String(ctx.from.id),
|
|
5142
5227
|
text: ctx.message.text
|
|
5143
|
-
}).catch((err) =>
|
|
5228
|
+
}).catch((err) => log14.error({ err }, "handleMessage error"));
|
|
5144
5229
|
});
|
|
5145
5230
|
}
|
|
5146
5231
|
// --- ChannelAdapter implementations ---
|
|
@@ -5152,7 +5237,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
5152
5237
|
if (!session) return;
|
|
5153
5238
|
const threadId = Number(session.threadId);
|
|
5154
5239
|
if (!threadId || isNaN(threadId)) {
|
|
5155
|
-
|
|
5240
|
+
log14.warn({ sessionId, threadId: session.threadId }, "Session has no valid threadId, skipping message");
|
|
5156
5241
|
return;
|
|
5157
5242
|
}
|
|
5158
5243
|
switch (content.type) {
|
|
@@ -5224,16 +5309,16 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
5224
5309
|
if (toolState) {
|
|
5225
5310
|
if (meta.viewerLinks) {
|
|
5226
5311
|
toolState.viewerLinks = meta.viewerLinks;
|
|
5227
|
-
|
|
5312
|
+
log14.debug({ toolId: meta.id, viewerLinks: meta.viewerLinks }, "Accumulated viewerLinks");
|
|
5228
5313
|
}
|
|
5229
5314
|
const viewerFilePath = content.metadata?.viewerFilePath;
|
|
5230
5315
|
if (viewerFilePath) toolState.viewerFilePath = viewerFilePath;
|
|
5231
5316
|
if (meta.name) toolState.name = meta.name;
|
|
5232
5317
|
if (meta.kind) toolState.kind = meta.kind;
|
|
5233
5318
|
const isTerminal = meta.status === "completed" || meta.status === "failed";
|
|
5234
|
-
if (!isTerminal
|
|
5319
|
+
if (!isTerminal) break;
|
|
5235
5320
|
await toolState.ready;
|
|
5236
|
-
|
|
5321
|
+
log14.debug(
|
|
5237
5322
|
{ toolId: meta.id, status: meta.status, hasViewerLinks: !!toolState.viewerLinks, viewerLinks: toolState.viewerLinks, name: toolState.name, msgId: toolState.msgId },
|
|
5238
5323
|
"Tool completed, preparing edit"
|
|
5239
5324
|
);
|
|
@@ -5255,7 +5340,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
5255
5340
|
)
|
|
5256
5341
|
);
|
|
5257
5342
|
} catch (err) {
|
|
5258
|
-
|
|
5343
|
+
log14.warn(
|
|
5259
5344
|
{ err, msgId: toolState.msgId, textLen: formattedText.length, hasViewerLinks: !!merged.viewerLinks },
|
|
5260
5345
|
"Tool update edit failed"
|
|
5261
5346
|
);
|
|
@@ -5350,7 +5435,7 @@ Task completed.
|
|
|
5350
5435
|
}
|
|
5351
5436
|
}
|
|
5352
5437
|
async sendPermissionRequest(sessionId, request) {
|
|
5353
|
-
|
|
5438
|
+
log14.info({ sessionId, requestId: request.id }, "Permission request sent");
|
|
5354
5439
|
const session = this.core.sessionManager.getSession(
|
|
5355
5440
|
sessionId
|
|
5356
5441
|
);
|
|
@@ -5358,7 +5443,7 @@ Task completed.
|
|
|
5358
5443
|
if (request.description.includes("openacp")) {
|
|
5359
5444
|
const allowOption = request.options.find((o) => o.isAllow);
|
|
5360
5445
|
if (allowOption && session.permissionGate.requestId === request.id) {
|
|
5361
|
-
|
|
5446
|
+
log14.info({ sessionId, requestId: request.id }, "Auto-approving openacp command");
|
|
5362
5447
|
session.permissionGate.resolve(allowOption.id);
|
|
5363
5448
|
}
|
|
5364
5449
|
return;
|
|
@@ -5366,7 +5451,7 @@ Task completed.
|
|
|
5366
5451
|
if (session.dangerousMode) {
|
|
5367
5452
|
const allowOption = request.options.find((o) => o.isAllow);
|
|
5368
5453
|
if (allowOption && session.permissionGate.requestId === request.id) {
|
|
5369
|
-
|
|
5454
|
+
log14.info({ sessionId, requestId: request.id, optionId: allowOption.id }, "Dangerous mode: auto-approving permission");
|
|
5370
5455
|
session.permissionGate.resolve(allowOption.id);
|
|
5371
5456
|
}
|
|
5372
5457
|
return;
|
|
@@ -5377,7 +5462,7 @@ Task completed.
|
|
|
5377
5462
|
}
|
|
5378
5463
|
async sendNotification(notification) {
|
|
5379
5464
|
if (notification.sessionId === this.assistantSession?.id) return;
|
|
5380
|
-
|
|
5465
|
+
log14.info(
|
|
5381
5466
|
{ sessionId: notification.sessionId, type: notification.type },
|
|
5382
5467
|
"Notification sent"
|
|
5383
5468
|
);
|
|
@@ -5413,7 +5498,7 @@ Task completed.
|
|
|
5413
5498
|
);
|
|
5414
5499
|
}
|
|
5415
5500
|
async createSessionThread(sessionId, name) {
|
|
5416
|
-
|
|
5501
|
+
log14.info({ sessionId, name }, "Session topic created");
|
|
5417
5502
|
return String(
|
|
5418
5503
|
await createSessionTopic(this.bot, this.telegramConfig.chatId, name)
|
|
5419
5504
|
);
|
|
@@ -5442,7 +5527,7 @@ Task completed.
|
|
|
5442
5527
|
try {
|
|
5443
5528
|
await this.bot.api.deleteForumTopic(this.telegramConfig.chatId, topicId);
|
|
5444
5529
|
} catch (err) {
|
|
5445
|
-
|
|
5530
|
+
log14.warn({ err, sessionId, topicId }, "Failed to delete forum topic (may already be deleted)");
|
|
5446
5531
|
}
|
|
5447
5532
|
}
|
|
5448
5533
|
async sendSkillCommands(sessionId, commands) {
|
|
@@ -5515,7 +5600,7 @@ Task completed.
|
|
|
5515
5600
|
{ disable_notification: true }
|
|
5516
5601
|
);
|
|
5517
5602
|
} catch (err) {
|
|
5518
|
-
|
|
5603
|
+
log14.error({ err, sessionId }, "Failed to send skill commands");
|
|
5519
5604
|
}
|
|
5520
5605
|
}
|
|
5521
5606
|
async cleanupSkillCommands(sessionId) {
|
|
@@ -5586,4 +5671,4 @@ export {
|
|
|
5586
5671
|
TopicManager,
|
|
5587
5672
|
TelegramAdapter
|
|
5588
5673
|
};
|
|
5589
|
-
//# sourceMappingURL=chunk-
|
|
5674
|
+
//# sourceMappingURL=chunk-KPI4HGJC.js.map
|