@openacp/cli 0.2.30 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-CEAIK37M.js → chunk-7W5SOJPD.js} +2 -2
- package/dist/{chunk-7VEHVMVM.js → chunk-CA6FXPLH.js} +9 -8
- package/dist/chunk-CA6FXPLH.js.map +1 -0
- package/dist/{chunk-2QSUSMBI.js → chunk-LVSQQRCF.js} +2 -2
- package/dist/{chunk-ZMNBZAXN.js → chunk-NS2L445T.js} +4 -4
- package/dist/chunk-NS2L445T.js.map +1 -0
- package/dist/{chunk-WKHCVK5P.js → chunk-SWQRUVBW.js} +595 -106
- package/dist/chunk-SWQRUVBW.js.map +1 -0
- package/dist/{chunk-GP66XLS6.js → chunk-YXMRR2E3.js} +23 -3
- package/dist/chunk-YXMRR2E3.js.map +1 -0
- package/dist/cli.js +16 -16
- package/dist/{config-3EDZ3IMJ.js → config-2CBRLF3R.js} +2 -2
- package/dist/config-editor-UN56HQCW.js +11 -0
- package/dist/{daemon-TNQKCKTB.js → daemon-UXC7PB4P.js} +3 -3
- package/dist/index.d.ts +5 -0
- package/dist/index.js +6 -6
- package/dist/{main-CW4GRPSX.js → main-PLTMIVYL.js} +10 -10
- package/dist/{setup-JRNMPTIT.js → setup-UKWBLJIT.js} +3 -3
- package/package.json +1 -1
- package/dist/chunk-7VEHVMVM.js.map +0 -1
- package/dist/chunk-GP66XLS6.js.map +0 -1
- package/dist/chunk-WKHCVK5P.js.map +0 -1
- package/dist/chunk-ZMNBZAXN.js.map +0 -1
- package/dist/config-editor-KCD6FFC3.js +0 -11
- /package/dist/{chunk-CEAIK37M.js.map → chunk-7W5SOJPD.js.map} +0 -0
- /package/dist/{chunk-2QSUSMBI.js.map → chunk-LVSQQRCF.js.map} +0 -0
- /package/dist/{config-3EDZ3IMJ.js.map → config-2CBRLF3R.js.map} +0 -0
- /package/dist/{config-editor-KCD6FFC3.js.map → config-editor-UN56HQCW.js.map} +0 -0
- /package/dist/{daemon-TNQKCKTB.js.map → daemon-UXC7PB4P.js.map} +0 -0
- /package/dist/{main-CW4GRPSX.js.map → main-PLTMIVYL.js.map} +0 -0
- /package/dist/{setup-JRNMPTIT.js.map → setup-UKWBLJIT.js.map} +0 -0
|
@@ -523,6 +523,7 @@ var Session = class {
|
|
|
523
523
|
adapter;
|
|
524
524
|
// Set by wireSessionEvents for renaming
|
|
525
525
|
pendingPermission;
|
|
526
|
+
dangerousMode = false;
|
|
526
527
|
log;
|
|
527
528
|
constructor(opts) {
|
|
528
529
|
this.id = opts.id || nanoid(12);
|
|
@@ -593,7 +594,8 @@ var Session = class {
|
|
|
593
594
|
async warmup() {
|
|
594
595
|
this.promptRunning = true;
|
|
595
596
|
const prevHandler = this.agentInstance.onSessionUpdate;
|
|
596
|
-
this.agentInstance.onSessionUpdate = () => {
|
|
597
|
+
this.agentInstance.onSessionUpdate = (event) => {
|
|
598
|
+
if (event.type === "commands_update") prevHandler(event);
|
|
597
599
|
};
|
|
598
600
|
try {
|
|
599
601
|
const start = Date.now();
|
|
@@ -612,6 +614,7 @@ var Session = class {
|
|
|
612
614
|
}
|
|
613
615
|
}
|
|
614
616
|
async cancel() {
|
|
617
|
+
this.promptQueue = [];
|
|
615
618
|
this.status = "cancelled";
|
|
616
619
|
this.log.info("Session cancelled");
|
|
617
620
|
await this.agentInstance.cancel();
|
|
@@ -666,6 +669,12 @@ var SessionManager = class {
|
|
|
666
669
|
}
|
|
667
670
|
return void 0;
|
|
668
671
|
}
|
|
672
|
+
getRecordByThread(channelId, threadId) {
|
|
673
|
+
return this.store?.findByPlatform(
|
|
674
|
+
channelId,
|
|
675
|
+
(p) => String(p.topicId) === threadId
|
|
676
|
+
);
|
|
677
|
+
}
|
|
669
678
|
registerSession(session) {
|
|
670
679
|
this.sessions.set(session.id, session);
|
|
671
680
|
}
|
|
@@ -707,11 +716,11 @@ var SessionManager = class {
|
|
|
707
716
|
const session = this.sessions.get(sessionId);
|
|
708
717
|
if (session) {
|
|
709
718
|
await session.cancel();
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
}
|
|
719
|
+
}
|
|
720
|
+
if (this.store) {
|
|
721
|
+
const record = this.store.get(sessionId);
|
|
722
|
+
if (record && record.status !== "cancelled") {
|
|
723
|
+
await this.store.save({ ...record, status: "cancelled" });
|
|
715
724
|
}
|
|
716
725
|
}
|
|
717
726
|
}
|
|
@@ -1044,9 +1053,9 @@ var OpenACPCore = class {
|
|
|
1044
1053
|
);
|
|
1045
1054
|
const adapter = this.adapters.get(message.channelId);
|
|
1046
1055
|
if (adapter) {
|
|
1047
|
-
await adapter.sendMessage(
|
|
1056
|
+
await adapter.sendMessage(message.threadId, {
|
|
1048
1057
|
type: "error",
|
|
1049
|
-
text:
|
|
1058
|
+
text: `\u26A0\uFE0F Session limit reached (${config.security.maxConcurrentSessions}). Please cancel existing sessions with /cancel before starting new ones.`
|
|
1050
1059
|
});
|
|
1051
1060
|
}
|
|
1052
1061
|
return;
|
|
@@ -1086,11 +1095,19 @@ var OpenACPCore = class {
|
|
|
1086
1095
|
channelId,
|
|
1087
1096
|
currentThreadId
|
|
1088
1097
|
);
|
|
1089
|
-
if (
|
|
1098
|
+
if (currentSession) {
|
|
1099
|
+
return this.handleNewSession(
|
|
1100
|
+
channelId,
|
|
1101
|
+
currentSession.agentName,
|
|
1102
|
+
currentSession.workingDirectory
|
|
1103
|
+
);
|
|
1104
|
+
}
|
|
1105
|
+
const record = this.sessionManager.getRecordByThread(channelId, currentThreadId);
|
|
1106
|
+
if (!record || record.status === "cancelled" || record.status === "error") return null;
|
|
1090
1107
|
return this.handleNewSession(
|
|
1091
1108
|
channelId,
|
|
1092
|
-
|
|
1093
|
-
|
|
1109
|
+
record.agentName,
|
|
1110
|
+
record.workingDir
|
|
1094
1111
|
);
|
|
1095
1112
|
}
|
|
1096
1113
|
// --- Lazy Resume ---
|
|
@@ -1609,20 +1626,23 @@ function formatViewerLinks(links, filePath) {
|
|
|
1609
1626
|
\u{1F4DD} <a href="${escapeHtml(links.diff)}">View diff${fileName ? ` \u2014 ${escapeHtml(fileName)}` : ""}</a>`;
|
|
1610
1627
|
return text;
|
|
1611
1628
|
}
|
|
1612
|
-
function
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
);
|
|
1617
|
-
return
|
|
1618
|
-
${lines.join("\n")}`;
|
|
1629
|
+
function formatTokens(n) {
|
|
1630
|
+
return n >= 1e3 ? `${Math.round(n / 1e3)}k` : String(n);
|
|
1631
|
+
}
|
|
1632
|
+
function progressBar(ratio) {
|
|
1633
|
+
const filled = Math.round(Math.min(ratio, 1) * 10);
|
|
1634
|
+
return "\u2593".repeat(filled) + "\u2591".repeat(10 - filled);
|
|
1619
1635
|
}
|
|
1620
1636
|
function formatUsage(usage) {
|
|
1621
|
-
const
|
|
1622
|
-
if (
|
|
1623
|
-
if (
|
|
1624
|
-
|
|
1625
|
-
|
|
1637
|
+
const { tokensUsed, contextSize } = usage;
|
|
1638
|
+
if (tokensUsed == null) return "\u{1F4CA} Usage data unavailable";
|
|
1639
|
+
if (contextSize == null) return `\u{1F4CA} ${formatTokens(tokensUsed)} tokens`;
|
|
1640
|
+
const ratio = tokensUsed / contextSize;
|
|
1641
|
+
const pct = Math.round(ratio * 100);
|
|
1642
|
+
const bar = progressBar(ratio);
|
|
1643
|
+
const emoji = pct >= 85 ? "\u26A0\uFE0F" : "\u{1F4CA}";
|
|
1644
|
+
return `${emoji} ${formatTokens(tokensUsed)} / ${formatTokens(contextSize)} tokens
|
|
1645
|
+
${bar} ${pct}%`;
|
|
1626
1646
|
}
|
|
1627
1647
|
function splitMessage(text, maxLength = 4096) {
|
|
1628
1648
|
if (text.length <= maxLength) return [text];
|
|
@@ -1663,6 +1683,7 @@ var MessageDraft = class {
|
|
|
1663
1683
|
flushPromise = Promise.resolve();
|
|
1664
1684
|
lastSentBuffer = "";
|
|
1665
1685
|
append(text) {
|
|
1686
|
+
if (!text) return;
|
|
1666
1687
|
this.buffer += text;
|
|
1667
1688
|
this.scheduleFlush();
|
|
1668
1689
|
}
|
|
@@ -1709,7 +1730,6 @@ var MessageDraft = class {
|
|
|
1709
1730
|
);
|
|
1710
1731
|
this.lastSentBuffer = this.buffer;
|
|
1711
1732
|
} catch {
|
|
1712
|
-
this.messageId = void 0;
|
|
1713
1733
|
}
|
|
1714
1734
|
}
|
|
1715
1735
|
}
|
|
@@ -1813,6 +1833,8 @@ function setupCommands(bot, core, chatId, assistant) {
|
|
|
1813
1833
|
bot.command("agents", (ctx) => handleAgents(ctx, core));
|
|
1814
1834
|
bot.command("help", (ctx) => handleHelp(ctx));
|
|
1815
1835
|
bot.command("menu", (ctx) => handleMenu(ctx));
|
|
1836
|
+
bot.command("enable_dangerous", (ctx) => handleEnableDangerous(ctx, core));
|
|
1837
|
+
bot.command("disable_dangerous", (ctx) => handleDisableDangerous(ctx, core));
|
|
1816
1838
|
}
|
|
1817
1839
|
function buildMenuKeyboard() {
|
|
1818
1840
|
return new InlineKeyboard().text("\u{1F195} New Session", "m:new").text("\u{1F4AC} New Chat", "m:new_chat").row().text("\u26D4 Cancel", "m:cancel").text("\u{1F4CA} Status", "m:status").row().text("\u{1F916} Agents", "m:agents").text("\u2753 Help", "m:help");
|
|
@@ -1898,7 +1920,8 @@ async function handleNew(ctx, core, chatId, assistant) {
|
|
|
1898
1920
|
<b>Workspace:</b> <code>${escapeHtml(session.workingDirectory)}</code>`,
|
|
1899
1921
|
{
|
|
1900
1922
|
message_thread_id: threadId,
|
|
1901
|
-
parse_mode: "HTML"
|
|
1923
|
+
parse_mode: "HTML",
|
|
1924
|
+
reply_markup: buildDangerousModeKeyboard(session.id, false)
|
|
1902
1925
|
}
|
|
1903
1926
|
);
|
|
1904
1927
|
session.warmup().catch((err) => log6.error({ err }, "Warm-up error"));
|
|
@@ -1952,7 +1975,8 @@ async function handleNewChat(ctx, core, chatId) {
|
|
|
1952
1975
|
<b>Workspace:</b> <code>${escapeHtml(session.workingDirectory)}</code>`,
|
|
1953
1976
|
{
|
|
1954
1977
|
message_thread_id: newThreadId,
|
|
1955
|
-
parse_mode: "HTML"
|
|
1978
|
+
parse_mode: "HTML",
|
|
1979
|
+
reply_markup: buildDangerousModeKeyboard(session.id, false)
|
|
1956
1980
|
}
|
|
1957
1981
|
);
|
|
1958
1982
|
session.warmup().catch((err) => log6.error({ err }, "Warm-up error"));
|
|
@@ -1981,6 +2005,13 @@ async function handleCancel(ctx, core, assistant) {
|
|
|
1981
2005
|
log6.info({ sessionId: session.id }, "Cancel session command");
|
|
1982
2006
|
await session.cancel();
|
|
1983
2007
|
await ctx.reply("\u26D4 Session cancelled.", { parse_mode: "HTML" });
|
|
2008
|
+
return;
|
|
2009
|
+
}
|
|
2010
|
+
const record = core.sessionManager.getRecordByThread("telegram", String(threadId));
|
|
2011
|
+
if (record && record.status !== "cancelled" && record.status !== "error") {
|
|
2012
|
+
log6.info({ sessionId: record.sessionId }, "Cancel session command (from store)");
|
|
2013
|
+
await core.sessionManager.cancelSession(record.sessionId);
|
|
2014
|
+
await ctx.reply("\u26D4 Session cancelled.", { parse_mode: "HTML" });
|
|
1984
2015
|
}
|
|
1985
2016
|
}
|
|
1986
2017
|
async function handleStatus(ctx, core) {
|
|
@@ -2000,9 +2031,20 @@ async function handleStatus(ctx, core) {
|
|
|
2000
2031
|
{ parse_mode: "HTML" }
|
|
2001
2032
|
);
|
|
2002
2033
|
} else {
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2034
|
+
const record = core.sessionManager.getRecordByThread("telegram", String(threadId));
|
|
2035
|
+
if (record) {
|
|
2036
|
+
await ctx.reply(
|
|
2037
|
+
`<b>Session:</b> ${escapeHtml(record.name || record.sessionId)}
|
|
2038
|
+
<b>Agent:</b> ${escapeHtml(record.agentName)}
|
|
2039
|
+
<b>Status:</b> ${escapeHtml(record.status)} (not loaded)
|
|
2040
|
+
<b>Workspace:</b> <code>${escapeHtml(record.workingDir)}</code>`,
|
|
2041
|
+
{ parse_mode: "HTML" }
|
|
2042
|
+
);
|
|
2043
|
+
} else {
|
|
2044
|
+
await ctx.reply("No active session in this topic.", {
|
|
2045
|
+
parse_mode: "HTML"
|
|
2046
|
+
});
|
|
2047
|
+
}
|
|
2006
2048
|
}
|
|
2007
2049
|
} else {
|
|
2008
2050
|
const sessions = core.sessionManager.listSessions("telegram");
|
|
@@ -2047,6 +2089,81 @@ Or just chat in the \u{1F916} Assistant topic for help!`,
|
|
|
2047
2089
|
{ parse_mode: "HTML" }
|
|
2048
2090
|
);
|
|
2049
2091
|
}
|
|
2092
|
+
function buildDangerousModeKeyboard(sessionId, enabled) {
|
|
2093
|
+
return new InlineKeyboard().text(
|
|
2094
|
+
enabled ? "\u{1F510} Disable Dangerous Mode" : "\u2620\uFE0F Enable Dangerous Mode",
|
|
2095
|
+
`d:${sessionId}`
|
|
2096
|
+
);
|
|
2097
|
+
}
|
|
2098
|
+
function setupDangerousModeCallbacks(bot, core) {
|
|
2099
|
+
bot.callbackQuery(/^d:/, async (ctx) => {
|
|
2100
|
+
const sessionId = ctx.callbackQuery.data.slice(2);
|
|
2101
|
+
const session = core.sessionManager.getSession(sessionId);
|
|
2102
|
+
if (!session) {
|
|
2103
|
+
try {
|
|
2104
|
+
await ctx.answerCallbackQuery({ text: "\u26A0\uFE0F Session not found or already ended." });
|
|
2105
|
+
} catch {
|
|
2106
|
+
}
|
|
2107
|
+
return;
|
|
2108
|
+
}
|
|
2109
|
+
session.dangerousMode = !session.dangerousMode;
|
|
2110
|
+
log6.info({ sessionId, dangerousMode: session.dangerousMode }, "Dangerous mode toggled via button");
|
|
2111
|
+
const toastText = session.dangerousMode ? "\u2620\uFE0F Dangerous mode enabled \u2014 permissions auto-approved" : "\u{1F510} Dangerous mode disabled \u2014 permissions shown normally";
|
|
2112
|
+
try {
|
|
2113
|
+
await ctx.answerCallbackQuery({ text: toastText });
|
|
2114
|
+
} catch {
|
|
2115
|
+
}
|
|
2116
|
+
try {
|
|
2117
|
+
await ctx.editMessageReplyMarkup({
|
|
2118
|
+
reply_markup: buildDangerousModeKeyboard(sessionId, session.dangerousMode)
|
|
2119
|
+
});
|
|
2120
|
+
} catch {
|
|
2121
|
+
}
|
|
2122
|
+
});
|
|
2123
|
+
}
|
|
2124
|
+
async function handleEnableDangerous(ctx, core) {
|
|
2125
|
+
const threadId = ctx.message?.message_thread_id;
|
|
2126
|
+
if (!threadId) {
|
|
2127
|
+
await ctx.reply("\u26A0\uFE0F This command only works inside a session topic.", { parse_mode: "HTML" });
|
|
2128
|
+
return;
|
|
2129
|
+
}
|
|
2130
|
+
const session = core.sessionManager.getSessionByThread("telegram", String(threadId));
|
|
2131
|
+
if (!session) {
|
|
2132
|
+
await ctx.reply("\u26A0\uFE0F No active session in this topic.", { parse_mode: "HTML" });
|
|
2133
|
+
return;
|
|
2134
|
+
}
|
|
2135
|
+
if (session.dangerousMode) {
|
|
2136
|
+
await ctx.reply("\u2620\uFE0F Dangerous mode is already enabled.", { parse_mode: "HTML" });
|
|
2137
|
+
return;
|
|
2138
|
+
}
|
|
2139
|
+
session.dangerousMode = true;
|
|
2140
|
+
await ctx.reply(
|
|
2141
|
+
`\u26A0\uFE0F <b>Dangerous mode enabled</b>
|
|
2142
|
+
|
|
2143
|
+
All permission requests will be auto-approved. Claude can run arbitrary commands without asking.
|
|
2144
|
+
|
|
2145
|
+
Use /disable_dangerous to restore normal behaviour.`,
|
|
2146
|
+
{ parse_mode: "HTML" }
|
|
2147
|
+
);
|
|
2148
|
+
}
|
|
2149
|
+
async function handleDisableDangerous(ctx, core) {
|
|
2150
|
+
const threadId = ctx.message?.message_thread_id;
|
|
2151
|
+
if (!threadId) {
|
|
2152
|
+
await ctx.reply("\u26A0\uFE0F This command only works inside a session topic.", { parse_mode: "HTML" });
|
|
2153
|
+
return;
|
|
2154
|
+
}
|
|
2155
|
+
const session = core.sessionManager.getSessionByThread("telegram", String(threadId));
|
|
2156
|
+
if (!session) {
|
|
2157
|
+
await ctx.reply("\u26A0\uFE0F No active session in this topic.", { parse_mode: "HTML" });
|
|
2158
|
+
return;
|
|
2159
|
+
}
|
|
2160
|
+
if (!session.dangerousMode) {
|
|
2161
|
+
await ctx.reply("\u{1F510} Dangerous mode is already disabled.", { parse_mode: "HTML" });
|
|
2162
|
+
return;
|
|
2163
|
+
}
|
|
2164
|
+
session.dangerousMode = false;
|
|
2165
|
+
await ctx.reply("\u{1F510} <b>Dangerous mode disabled</b>\n\nPermission requests will be shown normally.", { parse_mode: "HTML" });
|
|
2166
|
+
}
|
|
2050
2167
|
function botFromCtx(ctx) {
|
|
2051
2168
|
return { api: ctx.api };
|
|
2052
2169
|
}
|
|
@@ -2088,10 +2205,11 @@ function setupSkillCallbacks(bot, core) {
|
|
|
2088
2205
|
}
|
|
2089
2206
|
async function executeNewSession(bot, core, chatId, agentName, workspace) {
|
|
2090
2207
|
const threadId = await createSessionTopic(bot, chatId, "\u{1F504} New Session");
|
|
2091
|
-
await bot.api.sendMessage(chatId, "\u23F3 Setting up session, please wait...", {
|
|
2208
|
+
const setupMsg = await bot.api.sendMessage(chatId, "\u23F3 Setting up session, please wait...", {
|
|
2092
2209
|
message_thread_id: threadId,
|
|
2093
2210
|
parse_mode: "HTML"
|
|
2094
2211
|
});
|
|
2212
|
+
const firstMsgId = setupMsg.message_id;
|
|
2095
2213
|
try {
|
|
2096
2214
|
const session = await core.handleNewSession(
|
|
2097
2215
|
"telegram",
|
|
@@ -2105,7 +2223,7 @@ async function executeNewSession(bot, core, chatId, agentName, workspace) {
|
|
|
2105
2223
|
const finalName = `\u{1F504} ${session.agentName} \u2014 New Session`;
|
|
2106
2224
|
await renameSessionTopic(bot, chatId, threadId, finalName);
|
|
2107
2225
|
session.warmup().catch((err) => log6.error({ err }, "Warm-up error"));
|
|
2108
|
-
return { session, threadId };
|
|
2226
|
+
return { session, threadId, firstMsgId };
|
|
2109
2227
|
} catch (err) {
|
|
2110
2228
|
try {
|
|
2111
2229
|
await bot.api.deleteForumTopic(chatId, threadId);
|
|
@@ -2128,7 +2246,9 @@ var STATIC_COMMANDS = [
|
|
|
2128
2246
|
{ command: "status", description: "Show status" },
|
|
2129
2247
|
{ command: "agents", description: "List available agents" },
|
|
2130
2248
|
{ command: "help", description: "Help" },
|
|
2131
|
-
{ command: "menu", description: "Show menu" }
|
|
2249
|
+
{ command: "menu", description: "Show menu" },
|
|
2250
|
+
{ command: "enable_dangerous", description: "Auto-approve all permission requests (session only)" },
|
|
2251
|
+
{ command: "disable_dangerous", description: "Restore normal permission prompts (session only)" }
|
|
2132
2252
|
];
|
|
2133
2253
|
|
|
2134
2254
|
// src/adapters/telegram/permissions.ts
|
|
@@ -2213,8 +2333,10 @@ ${escapeHtml(request.description)}`,
|
|
|
2213
2333
|
};
|
|
2214
2334
|
|
|
2215
2335
|
// src/adapters/telegram/assistant.ts
|
|
2336
|
+
var log8 = createChildLogger({ module: "telegram-assistant" });
|
|
2216
2337
|
async function spawnAssistant(core, adapter, assistantTopicId) {
|
|
2217
2338
|
const config = core.configManager.get();
|
|
2339
|
+
log8.info({ agent: config.defaultAgent }, "Creating assistant session...");
|
|
2218
2340
|
const session = await core.sessionManager.createSession(
|
|
2219
2341
|
"telegram",
|
|
2220
2342
|
config.defaultAgent,
|
|
@@ -2222,10 +2344,16 @@ async function spawnAssistant(core, adapter, assistantTopicId) {
|
|
|
2222
2344
|
core.agentManager
|
|
2223
2345
|
);
|
|
2224
2346
|
session.threadId = String(assistantTopicId);
|
|
2225
|
-
|
|
2226
|
-
|
|
2347
|
+
session.name = "Assistant";
|
|
2348
|
+
log8.info({ sessionId: session.id }, "Assistant agent spawned");
|
|
2227
2349
|
core.wireSessionEvents(session, adapter);
|
|
2228
|
-
|
|
2350
|
+
const systemPrompt = buildAssistantSystemPrompt(config);
|
|
2351
|
+
const ready = session.enqueuePrompt(systemPrompt).then(() => {
|
|
2352
|
+
log8.info({ sessionId: session.id }, "Assistant system prompt completed");
|
|
2353
|
+
}).catch((err) => {
|
|
2354
|
+
log8.warn({ err }, "Assistant system prompt failed");
|
|
2355
|
+
});
|
|
2356
|
+
return { session, ready };
|
|
2229
2357
|
}
|
|
2230
2358
|
function buildAssistantSystemPrompt(config) {
|
|
2231
2359
|
const agentNames = Object.keys(config.agents).join(", ");
|
|
@@ -2260,6 +2388,286 @@ function redirectToAssistant(chatId, assistantTopicId) {
|
|
|
2260
2388
|
return `\u{1F4AC} Please use the <a href="${link}">\u{1F916} Assistant</a> topic to chat with OpenACP.`;
|
|
2261
2389
|
}
|
|
2262
2390
|
|
|
2391
|
+
// src/adapters/telegram/activity.ts
|
|
2392
|
+
var log9 = createChildLogger({ module: "telegram:activity" });
|
|
2393
|
+
var THINKING_REFRESH_MS = 15e3;
|
|
2394
|
+
var THINKING_MAX_MS = 3 * 60 * 1e3;
|
|
2395
|
+
var ThinkingIndicator = class {
|
|
2396
|
+
constructor(api, chatId, threadId, sendQueue) {
|
|
2397
|
+
this.api = api;
|
|
2398
|
+
this.chatId = chatId;
|
|
2399
|
+
this.threadId = threadId;
|
|
2400
|
+
this.sendQueue = sendQueue;
|
|
2401
|
+
}
|
|
2402
|
+
msgId;
|
|
2403
|
+
sending = false;
|
|
2404
|
+
dismissed = false;
|
|
2405
|
+
refreshTimer;
|
|
2406
|
+
showTime = 0;
|
|
2407
|
+
async show() {
|
|
2408
|
+
if (this.msgId || this.sending || this.dismissed) return;
|
|
2409
|
+
this.sending = true;
|
|
2410
|
+
this.showTime = Date.now();
|
|
2411
|
+
try {
|
|
2412
|
+
const result = await this.sendQueue.enqueue(
|
|
2413
|
+
() => this.api.sendMessage(this.chatId, "\u{1F4AD} <i>Thinking...</i>", {
|
|
2414
|
+
message_thread_id: this.threadId,
|
|
2415
|
+
parse_mode: "HTML",
|
|
2416
|
+
disable_notification: true
|
|
2417
|
+
})
|
|
2418
|
+
);
|
|
2419
|
+
if (result && !this.dismissed) {
|
|
2420
|
+
this.msgId = result.message_id;
|
|
2421
|
+
this.startRefreshTimer();
|
|
2422
|
+
}
|
|
2423
|
+
} catch (err) {
|
|
2424
|
+
log9.warn({ err }, "ThinkingIndicator.show() failed");
|
|
2425
|
+
} finally {
|
|
2426
|
+
this.sending = false;
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
/** Clear state — stops refresh timer, no Telegram API call */
|
|
2430
|
+
dismiss() {
|
|
2431
|
+
this.dismissed = true;
|
|
2432
|
+
this.msgId = void 0;
|
|
2433
|
+
this.stopRefreshTimer();
|
|
2434
|
+
}
|
|
2435
|
+
/** Reset for a new prompt cycle */
|
|
2436
|
+
reset() {
|
|
2437
|
+
this.dismissed = false;
|
|
2438
|
+
}
|
|
2439
|
+
startRefreshTimer() {
|
|
2440
|
+
this.stopRefreshTimer();
|
|
2441
|
+
this.refreshTimer = setInterval(() => {
|
|
2442
|
+
if (this.dismissed || !this.msgId || Date.now() - this.showTime >= THINKING_MAX_MS) {
|
|
2443
|
+
this.stopRefreshTimer();
|
|
2444
|
+
return;
|
|
2445
|
+
}
|
|
2446
|
+
const elapsed = Math.round((Date.now() - this.showTime) / 1e3);
|
|
2447
|
+
this.sendQueue.enqueue(() => {
|
|
2448
|
+
if (this.dismissed) return Promise.resolve(void 0);
|
|
2449
|
+
return this.api.sendMessage(this.chatId, `\u{1F4AD} <i>Still thinking... (${elapsed}s)</i>`, {
|
|
2450
|
+
message_thread_id: this.threadId,
|
|
2451
|
+
parse_mode: "HTML",
|
|
2452
|
+
disable_notification: true
|
|
2453
|
+
});
|
|
2454
|
+
}).then((result) => {
|
|
2455
|
+
if (result && !this.dismissed) {
|
|
2456
|
+
this.msgId = result.message_id;
|
|
2457
|
+
}
|
|
2458
|
+
}).catch(() => {
|
|
2459
|
+
});
|
|
2460
|
+
}, THINKING_REFRESH_MS);
|
|
2461
|
+
}
|
|
2462
|
+
stopRefreshTimer() {
|
|
2463
|
+
if (this.refreshTimer) {
|
|
2464
|
+
clearInterval(this.refreshTimer);
|
|
2465
|
+
this.refreshTimer = void 0;
|
|
2466
|
+
}
|
|
2467
|
+
}
|
|
2468
|
+
};
|
|
2469
|
+
var UsageMessage = class {
|
|
2470
|
+
constructor(api, chatId, threadId, sendQueue) {
|
|
2471
|
+
this.api = api;
|
|
2472
|
+
this.chatId = chatId;
|
|
2473
|
+
this.threadId = threadId;
|
|
2474
|
+
this.sendQueue = sendQueue;
|
|
2475
|
+
}
|
|
2476
|
+
msgId;
|
|
2477
|
+
async send(usage) {
|
|
2478
|
+
const text = formatUsage(usage);
|
|
2479
|
+
try {
|
|
2480
|
+
if (this.msgId) {
|
|
2481
|
+
await this.sendQueue.enqueue(
|
|
2482
|
+
() => this.api.editMessageText(this.chatId, this.msgId, text, {
|
|
2483
|
+
parse_mode: "HTML"
|
|
2484
|
+
})
|
|
2485
|
+
);
|
|
2486
|
+
} else {
|
|
2487
|
+
const result = await this.sendQueue.enqueue(
|
|
2488
|
+
() => this.api.sendMessage(this.chatId, text, {
|
|
2489
|
+
message_thread_id: this.threadId,
|
|
2490
|
+
parse_mode: "HTML",
|
|
2491
|
+
disable_notification: true
|
|
2492
|
+
})
|
|
2493
|
+
);
|
|
2494
|
+
if (result) this.msgId = result.message_id;
|
|
2495
|
+
}
|
|
2496
|
+
} catch (err) {
|
|
2497
|
+
log9.warn({ err }, "UsageMessage.send() failed");
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
getMsgId() {
|
|
2501
|
+
return this.msgId;
|
|
2502
|
+
}
|
|
2503
|
+
async delete() {
|
|
2504
|
+
if (!this.msgId) return;
|
|
2505
|
+
const id = this.msgId;
|
|
2506
|
+
this.msgId = void 0;
|
|
2507
|
+
try {
|
|
2508
|
+
await this.sendQueue.enqueue(() => this.api.deleteMessage(this.chatId, id));
|
|
2509
|
+
} catch (err) {
|
|
2510
|
+
log9.warn({ err }, "UsageMessage.delete() failed");
|
|
2511
|
+
}
|
|
2512
|
+
}
|
|
2513
|
+
};
|
|
2514
|
+
function formatPlanCard(entries) {
|
|
2515
|
+
const statusIcon = {
|
|
2516
|
+
completed: "\u2705",
|
|
2517
|
+
in_progress: "\u{1F504}",
|
|
2518
|
+
pending: "\u2B1C",
|
|
2519
|
+
failed: "\u274C"
|
|
2520
|
+
};
|
|
2521
|
+
const total = entries.length;
|
|
2522
|
+
const done = entries.filter((e) => e.status === "completed").length;
|
|
2523
|
+
const ratio = total > 0 ? done / total : 0;
|
|
2524
|
+
const filled = Math.round(ratio * 10);
|
|
2525
|
+
const bar = "\u2593".repeat(filled) + "\u2591".repeat(10 - filled);
|
|
2526
|
+
const pct = Math.round(ratio * 100);
|
|
2527
|
+
const header = `\u{1F4CB} <b>Plan</b>
|
|
2528
|
+
${bar} ${pct}% \xB7 ${done}/${total}`;
|
|
2529
|
+
const lines = entries.map((e, i) => {
|
|
2530
|
+
const icon = statusIcon[e.status] ?? "\u2B1C";
|
|
2531
|
+
return `${icon} ${i + 1}. ${e.content}`;
|
|
2532
|
+
});
|
|
2533
|
+
return [header, ...lines].join("\n");
|
|
2534
|
+
}
|
|
2535
|
+
var PlanCard = class {
|
|
2536
|
+
constructor(api, chatId, threadId, sendQueue) {
|
|
2537
|
+
this.api = api;
|
|
2538
|
+
this.chatId = chatId;
|
|
2539
|
+
this.threadId = threadId;
|
|
2540
|
+
this.sendQueue = sendQueue;
|
|
2541
|
+
}
|
|
2542
|
+
msgId;
|
|
2543
|
+
flushPromise = Promise.resolve();
|
|
2544
|
+
latestEntries;
|
|
2545
|
+
flushTimer;
|
|
2546
|
+
update(entries) {
|
|
2547
|
+
this.latestEntries = entries;
|
|
2548
|
+
if (this.flushTimer) clearTimeout(this.flushTimer);
|
|
2549
|
+
this.flushTimer = setTimeout(() => {
|
|
2550
|
+
this.flushTimer = void 0;
|
|
2551
|
+
this.flushPromise = this.flushPromise.then(() => this._flush()).catch(() => {
|
|
2552
|
+
});
|
|
2553
|
+
}, 3500);
|
|
2554
|
+
}
|
|
2555
|
+
async finalize() {
|
|
2556
|
+
if (!this.latestEntries) return;
|
|
2557
|
+
if (this.flushTimer) {
|
|
2558
|
+
clearTimeout(this.flushTimer);
|
|
2559
|
+
this.flushTimer = void 0;
|
|
2560
|
+
}
|
|
2561
|
+
await this.flushPromise;
|
|
2562
|
+
this.flushPromise = this.flushPromise.then(() => this._flush()).catch(() => {
|
|
2563
|
+
});
|
|
2564
|
+
await this.flushPromise;
|
|
2565
|
+
}
|
|
2566
|
+
destroy() {
|
|
2567
|
+
if (this.flushTimer) {
|
|
2568
|
+
clearTimeout(this.flushTimer);
|
|
2569
|
+
this.flushTimer = void 0;
|
|
2570
|
+
}
|
|
2571
|
+
}
|
|
2572
|
+
async _flush() {
|
|
2573
|
+
if (!this.latestEntries) return;
|
|
2574
|
+
const text = formatPlanCard(this.latestEntries);
|
|
2575
|
+
try {
|
|
2576
|
+
if (this.msgId) {
|
|
2577
|
+
await this.sendQueue.enqueue(
|
|
2578
|
+
() => this.api.editMessageText(this.chatId, this.msgId, text, {
|
|
2579
|
+
parse_mode: "HTML"
|
|
2580
|
+
})
|
|
2581
|
+
);
|
|
2582
|
+
} else {
|
|
2583
|
+
const result = await this.sendQueue.enqueue(
|
|
2584
|
+
() => this.api.sendMessage(this.chatId, text, {
|
|
2585
|
+
message_thread_id: this.threadId,
|
|
2586
|
+
parse_mode: "HTML",
|
|
2587
|
+
disable_notification: true
|
|
2588
|
+
})
|
|
2589
|
+
);
|
|
2590
|
+
if (result) this.msgId = result.message_id;
|
|
2591
|
+
}
|
|
2592
|
+
} catch (err) {
|
|
2593
|
+
log9.warn({ err }, "PlanCard flush failed");
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
};
|
|
2597
|
+
var ActivityTracker = class {
|
|
2598
|
+
constructor(api, chatId, threadId, sendQueue) {
|
|
2599
|
+
this.api = api;
|
|
2600
|
+
this.chatId = chatId;
|
|
2601
|
+
this.threadId = threadId;
|
|
2602
|
+
this.sendQueue = sendQueue;
|
|
2603
|
+
this.thinking = new ThinkingIndicator(api, chatId, threadId, sendQueue);
|
|
2604
|
+
this.planCard = new PlanCard(api, chatId, threadId, sendQueue);
|
|
2605
|
+
this.usage = new UsageMessage(api, chatId, threadId, sendQueue);
|
|
2606
|
+
}
|
|
2607
|
+
isFirstEvent = true;
|
|
2608
|
+
hasPlanCard = false;
|
|
2609
|
+
thinking;
|
|
2610
|
+
planCard;
|
|
2611
|
+
usage;
|
|
2612
|
+
async onNewPrompt() {
|
|
2613
|
+
this.isFirstEvent = true;
|
|
2614
|
+
this.hasPlanCard = false;
|
|
2615
|
+
this.thinking.dismiss();
|
|
2616
|
+
this.thinking.reset();
|
|
2617
|
+
}
|
|
2618
|
+
async onThought() {
|
|
2619
|
+
await this._firstEventGuard();
|
|
2620
|
+
await this.thinking.show();
|
|
2621
|
+
}
|
|
2622
|
+
async onPlan(entries) {
|
|
2623
|
+
await this._firstEventGuard();
|
|
2624
|
+
this.thinking.dismiss();
|
|
2625
|
+
this.hasPlanCard = true;
|
|
2626
|
+
this.planCard.update(entries);
|
|
2627
|
+
}
|
|
2628
|
+
async onToolCall() {
|
|
2629
|
+
await this._firstEventGuard();
|
|
2630
|
+
this.thinking.dismiss();
|
|
2631
|
+
this.thinking.reset();
|
|
2632
|
+
}
|
|
2633
|
+
async onTextStart() {
|
|
2634
|
+
await this._firstEventGuard();
|
|
2635
|
+
this.thinking.dismiss();
|
|
2636
|
+
}
|
|
2637
|
+
async sendUsage(data) {
|
|
2638
|
+
await this.usage.send(data);
|
|
2639
|
+
}
|
|
2640
|
+
getUsageMsgId() {
|
|
2641
|
+
return this.usage.getMsgId();
|
|
2642
|
+
}
|
|
2643
|
+
async onComplete() {
|
|
2644
|
+
if (this.hasPlanCard) {
|
|
2645
|
+
await this.planCard.finalize();
|
|
2646
|
+
} else {
|
|
2647
|
+
try {
|
|
2648
|
+
await this.sendQueue.enqueue(
|
|
2649
|
+
() => this.api.sendMessage(this.chatId, "\u2705 <b>Done</b>", {
|
|
2650
|
+
message_thread_id: this.threadId,
|
|
2651
|
+
parse_mode: "HTML",
|
|
2652
|
+
disable_notification: true
|
|
2653
|
+
})
|
|
2654
|
+
);
|
|
2655
|
+
} catch (err) {
|
|
2656
|
+
log9.warn({ err }, "ActivityTracker.onComplete() Done send failed");
|
|
2657
|
+
}
|
|
2658
|
+
}
|
|
2659
|
+
}
|
|
2660
|
+
destroy() {
|
|
2661
|
+
this.thinking.dismiss();
|
|
2662
|
+
this.planCard.destroy();
|
|
2663
|
+
}
|
|
2664
|
+
async _firstEventGuard() {
|
|
2665
|
+
if (!this.isFirstEvent) return;
|
|
2666
|
+
this.isFirstEvent = false;
|
|
2667
|
+
await this.usage.delete();
|
|
2668
|
+
}
|
|
2669
|
+
};
|
|
2670
|
+
|
|
2263
2671
|
// src/adapters/telegram/send-queue.ts
|
|
2264
2672
|
var TelegramSendQueue = class {
|
|
2265
2673
|
items = [];
|
|
@@ -2408,14 +2816,14 @@ function setupActionCallbacks(bot, core, chatId, getAssistantSessionId) {
|
|
|
2408
2816
|
try {
|
|
2409
2817
|
if (action.action === "new_session") {
|
|
2410
2818
|
await ctx.answerCallbackQuery({ text: "\u23F3 Creating session..." });
|
|
2411
|
-
const { threadId } = await executeNewSession(
|
|
2819
|
+
const { threadId, firstMsgId } = await executeNewSession(
|
|
2412
2820
|
bot,
|
|
2413
2821
|
core,
|
|
2414
2822
|
chatId,
|
|
2415
2823
|
action.agent,
|
|
2416
2824
|
action.workspace
|
|
2417
2825
|
);
|
|
2418
|
-
const topicLink = `https://t.me/c/${String(chatId).replace("-100", "")}/${threadId}`;
|
|
2826
|
+
const topicLink = `https://t.me/c/${String(chatId).replace("-100", "")}/${firstMsgId ?? threadId}`;
|
|
2419
2827
|
const originalText = ctx.callbackQuery.message?.text ?? "";
|
|
2420
2828
|
try {
|
|
2421
2829
|
await ctx.editMessageText(
|
|
@@ -2472,7 +2880,7 @@ function setupActionCallbacks(bot, core, chatId, getAssistantSessionId) {
|
|
|
2472
2880
|
}
|
|
2473
2881
|
|
|
2474
2882
|
// src/adapters/telegram/adapter.ts
|
|
2475
|
-
var
|
|
2883
|
+
var log10 = createChildLogger({ module: "telegram" });
|
|
2476
2884
|
function patchedFetch(input, init) {
|
|
2477
2885
|
if (init?.signal && !(init.signal instanceof AbortSignal)) {
|
|
2478
2886
|
const nativeController = new AbortController();
|
|
@@ -2495,11 +2903,26 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2495
2903
|
// sessionId → (toolCallId → state)
|
|
2496
2904
|
permissionHandler;
|
|
2497
2905
|
assistantSession = null;
|
|
2906
|
+
assistantInitializing = false;
|
|
2498
2907
|
notificationTopicId;
|
|
2499
2908
|
assistantTopicId;
|
|
2500
2909
|
skillMessages = /* @__PURE__ */ new Map();
|
|
2501
2910
|
// sessionId → pinned messageId
|
|
2502
2911
|
sendQueue = new TelegramSendQueue(3e3);
|
|
2912
|
+
sessionTrackers = /* @__PURE__ */ new Map();
|
|
2913
|
+
getOrCreateTracker(sessionId, threadId) {
|
|
2914
|
+
let tracker = this.sessionTrackers.get(sessionId);
|
|
2915
|
+
if (!tracker) {
|
|
2916
|
+
tracker = new ActivityTracker(
|
|
2917
|
+
this.bot.api,
|
|
2918
|
+
this.telegramConfig.chatId,
|
|
2919
|
+
threadId,
|
|
2920
|
+
this.sendQueue
|
|
2921
|
+
);
|
|
2922
|
+
this.sessionTrackers.set(sessionId, tracker);
|
|
2923
|
+
}
|
|
2924
|
+
return tracker;
|
|
2925
|
+
}
|
|
2503
2926
|
constructor(core, config) {
|
|
2504
2927
|
super(core, config);
|
|
2505
2928
|
this.telegramConfig = config;
|
|
@@ -2508,7 +2931,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2508
2931
|
this.bot = new Bot(this.telegramConfig.botToken, { client: { fetch: patchedFetch } });
|
|
2509
2932
|
this.bot.catch((err) => {
|
|
2510
2933
|
const rootCause = err.error instanceof Error ? err.error : err;
|
|
2511
|
-
|
|
2934
|
+
log10.error({ err: rootCause }, "Telegram bot error");
|
|
2512
2935
|
});
|
|
2513
2936
|
this.bot.api.config.use(async (prev, method, payload, signal) => {
|
|
2514
2937
|
const maxRetries = 3;
|
|
@@ -2522,7 +2945,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2522
2945
|
if (rateLimitedMethods.includes(method)) {
|
|
2523
2946
|
this.sendQueue.onRateLimited();
|
|
2524
2947
|
}
|
|
2525
|
-
|
|
2948
|
+
log10.warn(
|
|
2526
2949
|
{ method, retryAfter, attempt: attempt + 1 },
|
|
2527
2950
|
"Rate limited by Telegram, retrying"
|
|
2528
2951
|
);
|
|
@@ -2567,6 +2990,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2567
2990
|
(notification) => this.sendNotification(notification)
|
|
2568
2991
|
);
|
|
2569
2992
|
setupSkillCallbacks(this.bot, this.core);
|
|
2993
|
+
setupDangerousModeCallbacks(this.bot, this.core);
|
|
2570
2994
|
setupActionCallbacks(
|
|
2571
2995
|
this.bot,
|
|
2572
2996
|
this.core,
|
|
@@ -2591,26 +3015,15 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2591
3015
|
this.setupRoutes();
|
|
2592
3016
|
this.bot.start({
|
|
2593
3017
|
allowed_updates: ["message", "callback_query"],
|
|
2594
|
-
onStart: () =>
|
|
3018
|
+
onStart: () => log10.info(
|
|
2595
3019
|
{ chatId: this.telegramConfig.chatId },
|
|
2596
3020
|
"Telegram bot started"
|
|
2597
3021
|
)
|
|
2598
3022
|
});
|
|
2599
|
-
try {
|
|
2600
|
-
this.assistantSession = await spawnAssistant(
|
|
2601
|
-
this.core,
|
|
2602
|
-
this,
|
|
2603
|
-
this.assistantTopicId
|
|
2604
|
-
);
|
|
2605
|
-
} catch (err) {
|
|
2606
|
-
log8.error({ err }, "Failed to spawn assistant");
|
|
2607
|
-
}
|
|
2608
3023
|
try {
|
|
2609
3024
|
const config = this.core.configManager.get();
|
|
2610
3025
|
const agents = this.core.agentManager.getAvailableAgents();
|
|
2611
|
-
const agentList = agents.map(
|
|
2612
|
-
(a) => `${escapeHtml(a.name)}${a.name === config.defaultAgent ? " (default)" : ""}`
|
|
2613
|
-
).join(", ");
|
|
3026
|
+
const agentList = agents.map((a) => `${escapeHtml(a.name)}${a.name === config.defaultAgent ? " (default)" : ""}`).join(", ");
|
|
2614
3027
|
const workspace = escapeHtml(config.workspace.baseDir);
|
|
2615
3028
|
const welcomeText = `\u{1F44B} <b>OpenACP Assistant</b> is online.
|
|
2616
3029
|
|
|
@@ -2624,7 +3037,32 @@ Workspace: <code>${workspace}</code>
|
|
|
2624
3037
|
reply_markup: buildMenuKeyboard()
|
|
2625
3038
|
});
|
|
2626
3039
|
} catch (err) {
|
|
2627
|
-
|
|
3040
|
+
log10.warn({ err }, "Failed to send welcome message");
|
|
3041
|
+
}
|
|
3042
|
+
try {
|
|
3043
|
+
log10.info("Spawning assistant session...");
|
|
3044
|
+
const { session, ready } = await spawnAssistant(
|
|
3045
|
+
this.core,
|
|
3046
|
+
this,
|
|
3047
|
+
this.assistantTopicId
|
|
3048
|
+
);
|
|
3049
|
+
this.assistantSession = session;
|
|
3050
|
+
this.assistantInitializing = true;
|
|
3051
|
+
log10.info({ sessionId: session.id }, "Assistant session ready, system prompt running in background");
|
|
3052
|
+
ready.then(() => {
|
|
3053
|
+
this.assistantInitializing = false;
|
|
3054
|
+
log10.info({ sessionId: session.id }, "Assistant ready for user messages");
|
|
3055
|
+
});
|
|
3056
|
+
} catch (err) {
|
|
3057
|
+
log10.error({ err }, "Failed to spawn assistant");
|
|
3058
|
+
this.bot.api.sendMessage(
|
|
3059
|
+
this.telegramConfig.chatId,
|
|
3060
|
+
`\u26A0\uFE0F <b>Failed to start assistant session.</b>
|
|
3061
|
+
|
|
3062
|
+
<code>${err instanceof Error ? err.message : String(err)}</code>`,
|
|
3063
|
+
{ message_thread_id: this.assistantTopicId, parse_mode: "HTML" }
|
|
3064
|
+
).catch(() => {
|
|
3065
|
+
});
|
|
2628
3066
|
}
|
|
2629
3067
|
}
|
|
2630
3068
|
async stop() {
|
|
@@ -2632,7 +3070,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2632
3070
|
await this.assistantSession.destroy();
|
|
2633
3071
|
}
|
|
2634
3072
|
await this.bot.stop();
|
|
2635
|
-
|
|
3073
|
+
log10.info("Telegram bot stopped");
|
|
2636
3074
|
}
|
|
2637
3075
|
setupRoutes() {
|
|
2638
3076
|
this.bot.on("message:text", async (ctx) => {
|
|
@@ -2647,16 +3085,24 @@ Workspace: <code>${workspace}</code>
|
|
|
2647
3085
|
}
|
|
2648
3086
|
if (threadId === this.notificationTopicId) return;
|
|
2649
3087
|
if (threadId === this.assistantTopicId) {
|
|
3088
|
+
if (!this.assistantSession) {
|
|
3089
|
+
await ctx.reply("\u26A0\uFE0F Assistant is not available yet. Please try again shortly.", { parse_mode: "HTML" });
|
|
3090
|
+
return;
|
|
3091
|
+
}
|
|
2650
3092
|
await this.finalizeDraft(this.assistantSession.id);
|
|
2651
3093
|
ctx.replyWithChatAction("typing").catch(() => {
|
|
2652
3094
|
});
|
|
2653
3095
|
handleAssistantMessage(this.assistantSession, ctx.message.text).catch(
|
|
2654
|
-
(err) =>
|
|
3096
|
+
(err) => log10.error({ err }, "Assistant error")
|
|
2655
3097
|
);
|
|
2656
3098
|
return;
|
|
2657
3099
|
}
|
|
2658
3100
|
const sessionId = this.core.sessionManager.getSessionByThread("telegram", String(threadId))?.id;
|
|
2659
3101
|
if (sessionId) await this.finalizeDraft(sessionId);
|
|
3102
|
+
if (sessionId) {
|
|
3103
|
+
const tracker = this.sessionTrackers.get(sessionId);
|
|
3104
|
+
if (tracker) await tracker.onNewPrompt();
|
|
3105
|
+
}
|
|
2660
3106
|
ctx.replyWithChatAction("typing").catch(() => {
|
|
2661
3107
|
});
|
|
2662
3108
|
this.core.handleMessage({
|
|
@@ -2664,11 +3110,12 @@ Workspace: <code>${workspace}</code>
|
|
|
2664
3110
|
threadId: String(threadId),
|
|
2665
3111
|
userId: String(ctx.from.id),
|
|
2666
3112
|
text: ctx.message.text
|
|
2667
|
-
}).catch((err) =>
|
|
3113
|
+
}).catch((err) => log10.error({ err }, "handleMessage error"));
|
|
2668
3114
|
});
|
|
2669
3115
|
}
|
|
2670
3116
|
// --- ChannelAdapter implementations ---
|
|
2671
3117
|
async sendMessage(sessionId, content) {
|
|
3118
|
+
if (this.assistantInitializing && sessionId === this.assistantSession?.id) return;
|
|
2672
3119
|
const session = this.core.sessionManager.getSession(
|
|
2673
3120
|
sessionId
|
|
2674
3121
|
);
|
|
@@ -2676,11 +3123,15 @@ Workspace: <code>${workspace}</code>
|
|
|
2676
3123
|
const threadId = Number(session.threadId);
|
|
2677
3124
|
switch (content.type) {
|
|
2678
3125
|
case "thought": {
|
|
3126
|
+
const tracker = this.getOrCreateTracker(sessionId, threadId);
|
|
3127
|
+
await tracker.onThought();
|
|
2679
3128
|
break;
|
|
2680
3129
|
}
|
|
2681
3130
|
case "text": {
|
|
2682
3131
|
let draft = this.sessionDrafts.get(sessionId);
|
|
2683
3132
|
if (!draft) {
|
|
3133
|
+
const tracker = this.getOrCreateTracker(sessionId, threadId);
|
|
3134
|
+
await tracker.onTextStart();
|
|
2684
3135
|
draft = new MessageDraft(
|
|
2685
3136
|
this.bot,
|
|
2686
3137
|
this.telegramConfig.chatId,
|
|
@@ -2698,6 +3149,8 @@ Workspace: <code>${workspace}</code>
|
|
|
2698
3149
|
break;
|
|
2699
3150
|
}
|
|
2700
3151
|
case "tool_call": {
|
|
3152
|
+
const tracker = this.getOrCreateTracker(sessionId, threadId);
|
|
3153
|
+
await tracker.onToolCall();
|
|
2701
3154
|
await this.finalizeDraft(sessionId);
|
|
2702
3155
|
const meta = content.metadata;
|
|
2703
3156
|
if (!this.toolCallMessages.has(sessionId)) {
|
|
@@ -2737,7 +3190,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2737
3190
|
if (toolState) {
|
|
2738
3191
|
if (meta.viewerLinks) {
|
|
2739
3192
|
toolState.viewerLinks = meta.viewerLinks;
|
|
2740
|
-
|
|
3193
|
+
log10.debug({ toolId: meta.id, viewerLinks: meta.viewerLinks }, "Accumulated viewerLinks");
|
|
2741
3194
|
}
|
|
2742
3195
|
const viewerFilePath = content.metadata?.viewerFilePath;
|
|
2743
3196
|
if (viewerFilePath) toolState.viewerFilePath = viewerFilePath;
|
|
@@ -2746,7 +3199,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2746
3199
|
const isTerminal = meta.status === "completed" || meta.status === "failed";
|
|
2747
3200
|
if (!isTerminal && !meta.viewerLinks) break;
|
|
2748
3201
|
await toolState.ready;
|
|
2749
|
-
|
|
3202
|
+
log10.debug(
|
|
2750
3203
|
{ toolId: meta.id, status: meta.status, hasViewerLinks: !!toolState.viewerLinks, viewerLinks: toolState.viewerLinks, name: toolState.name, msgId: toolState.msgId },
|
|
2751
3204
|
"Tool completed, preparing edit"
|
|
2752
3205
|
);
|
|
@@ -2768,7 +3221,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2768
3221
|
)
|
|
2769
3222
|
);
|
|
2770
3223
|
} catch (err) {
|
|
2771
|
-
|
|
3224
|
+
log10.warn(
|
|
2772
3225
|
{ err, msgId: toolState.msgId, textLen: formattedText.length, hasViewerLinks: !!merged.viewerLinks },
|
|
2773
3226
|
"Tool update edit failed"
|
|
2774
3227
|
);
|
|
@@ -2777,37 +3230,42 @@ Workspace: <code>${workspace}</code>
|
|
|
2777
3230
|
break;
|
|
2778
3231
|
}
|
|
2779
3232
|
case "plan": {
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
2785
|
-
|
|
2786
|
-
|
|
2787
|
-
|
|
2788
|
-
message_thread_id: threadId,
|
|
2789
|
-
parse_mode: "HTML",
|
|
2790
|
-
disable_notification: true
|
|
2791
|
-
}
|
|
2792
|
-
)
|
|
3233
|
+
const meta = content.metadata;
|
|
3234
|
+
const tracker = this.getOrCreateTracker(sessionId, threadId);
|
|
3235
|
+
await tracker.onPlan(
|
|
3236
|
+
meta.entries.map((e) => ({
|
|
3237
|
+
content: e.content,
|
|
3238
|
+
status: e.status,
|
|
3239
|
+
priority: e.priority ?? "medium"
|
|
3240
|
+
}))
|
|
2793
3241
|
);
|
|
2794
3242
|
break;
|
|
2795
3243
|
}
|
|
2796
3244
|
case "usage": {
|
|
3245
|
+
const meta = content.metadata;
|
|
2797
3246
|
await this.finalizeDraft(sessionId);
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
3247
|
+
const tracker = this.getOrCreateTracker(sessionId, threadId);
|
|
3248
|
+
await tracker.sendUsage(meta);
|
|
3249
|
+
if (this.notificationTopicId && sessionId !== this.assistantSession?.id) {
|
|
3250
|
+
const sess = this.core.sessionManager.getSession(sessionId);
|
|
3251
|
+
const sessionName = sess?.name || "Session";
|
|
3252
|
+
const chatIdStr = String(this.telegramConfig.chatId);
|
|
3253
|
+
const numericId = chatIdStr.startsWith("-100") ? chatIdStr.slice(4) : chatIdStr.replace("-", "");
|
|
3254
|
+
const usageMsgId = tracker.getUsageMsgId();
|
|
3255
|
+
const deepLink = `https://t.me/c/${numericId}/${usageMsgId ?? threadId}`;
|
|
3256
|
+
const text = `\u2705 <b>${escapeHtml(sessionName)}</b>
|
|
3257
|
+
Task completed.
|
|
3258
|
+
|
|
3259
|
+
<a href="${deepLink}">\u2192 Go to topic</a>`;
|
|
3260
|
+
this.sendQueue.enqueue(
|
|
3261
|
+
() => this.bot.api.sendMessage(this.telegramConfig.chatId, text, {
|
|
3262
|
+
message_thread_id: this.notificationTopicId,
|
|
2806
3263
|
parse_mode: "HTML",
|
|
2807
|
-
disable_notification:
|
|
2808
|
-
}
|
|
2809
|
-
)
|
|
2810
|
-
|
|
3264
|
+
disable_notification: false
|
|
3265
|
+
})
|
|
3266
|
+
).catch(() => {
|
|
3267
|
+
});
|
|
3268
|
+
}
|
|
2811
3269
|
break;
|
|
2812
3270
|
}
|
|
2813
3271
|
case "session_end": {
|
|
@@ -2815,21 +3273,33 @@ Workspace: <code>${workspace}</code>
|
|
|
2815
3273
|
this.sessionDrafts.delete(sessionId);
|
|
2816
3274
|
this.toolCallMessages.delete(sessionId);
|
|
2817
3275
|
await this.cleanupSkillCommands(sessionId);
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
3276
|
+
const tracker = this.sessionTrackers.get(sessionId);
|
|
3277
|
+
if (tracker) {
|
|
3278
|
+
await tracker.onComplete();
|
|
3279
|
+
tracker.destroy();
|
|
3280
|
+
this.sessionTrackers.delete(sessionId);
|
|
3281
|
+
} else {
|
|
3282
|
+
await this.sendQueue.enqueue(
|
|
3283
|
+
() => this.bot.api.sendMessage(
|
|
3284
|
+
this.telegramConfig.chatId,
|
|
3285
|
+
`\u2705 <b>Done</b>`,
|
|
3286
|
+
{
|
|
3287
|
+
message_thread_id: threadId,
|
|
3288
|
+
parse_mode: "HTML",
|
|
3289
|
+
disable_notification: true
|
|
3290
|
+
}
|
|
3291
|
+
)
|
|
3292
|
+
);
|
|
3293
|
+
}
|
|
2829
3294
|
break;
|
|
2830
3295
|
}
|
|
2831
3296
|
case "error": {
|
|
2832
3297
|
await this.finalizeDraft(sessionId);
|
|
3298
|
+
const tracker = this.sessionTrackers.get(sessionId);
|
|
3299
|
+
if (tracker) {
|
|
3300
|
+
tracker.destroy();
|
|
3301
|
+
this.sessionTrackers.delete(sessionId);
|
|
3302
|
+
}
|
|
2833
3303
|
await this.sendQueue.enqueue(
|
|
2834
3304
|
() => this.bot.api.sendMessage(
|
|
2835
3305
|
this.telegramConfig.chatId,
|
|
@@ -2846,17 +3316,27 @@ Workspace: <code>${workspace}</code>
|
|
|
2846
3316
|
}
|
|
2847
3317
|
}
|
|
2848
3318
|
async sendPermissionRequest(sessionId, request) {
|
|
2849
|
-
|
|
3319
|
+
log10.info({ sessionId, requestId: request.id }, "Permission request sent");
|
|
2850
3320
|
const session = this.core.sessionManager.getSession(
|
|
2851
3321
|
sessionId
|
|
2852
3322
|
);
|
|
2853
3323
|
if (!session) return;
|
|
3324
|
+
if (session.dangerousMode) {
|
|
3325
|
+
const allowOption = request.options.find((o) => o.isAllow);
|
|
3326
|
+
if (allowOption && session.pendingPermission?.requestId === request.id) {
|
|
3327
|
+
log10.info({ sessionId, requestId: request.id, optionId: allowOption.id }, "Dangerous mode: auto-approving permission");
|
|
3328
|
+
session.pendingPermission.resolve(allowOption.id);
|
|
3329
|
+
session.pendingPermission = void 0;
|
|
3330
|
+
}
|
|
3331
|
+
return;
|
|
3332
|
+
}
|
|
2854
3333
|
await this.sendQueue.enqueue(
|
|
2855
3334
|
() => this.permissionHandler.sendPermissionRequest(session, request)
|
|
2856
3335
|
);
|
|
2857
3336
|
}
|
|
2858
3337
|
async sendNotification(notification) {
|
|
2859
|
-
|
|
3338
|
+
if (notification.sessionId === this.assistantSession?.id) return;
|
|
3339
|
+
log10.info(
|
|
2860
3340
|
{ sessionId: notification.sessionId, type: notification.type },
|
|
2861
3341
|
"Notification sent"
|
|
2862
3342
|
);
|
|
@@ -2870,10 +3350,18 @@ Workspace: <code>${workspace}</code>
|
|
|
2870
3350
|
let text = `${emoji[notification.type] || "\u2139\uFE0F"} <b>${escapeHtml(notification.sessionName || "New session")}</b>
|
|
2871
3351
|
`;
|
|
2872
3352
|
text += escapeHtml(notification.summary);
|
|
2873
|
-
|
|
3353
|
+
const deepLink = notification.deepLink ?? (() => {
|
|
3354
|
+
const session = this.core.sessionManager.getSession(notification.sessionId);
|
|
3355
|
+
const threadId = session?.threadId;
|
|
3356
|
+
if (!threadId) return void 0;
|
|
3357
|
+
const chatIdStr = String(this.telegramConfig.chatId);
|
|
3358
|
+
const numericId = chatIdStr.startsWith("-100") ? chatIdStr.slice(4) : chatIdStr.replace("-", "");
|
|
3359
|
+
return `https://t.me/c/${numericId}/${threadId}`;
|
|
3360
|
+
})();
|
|
3361
|
+
if (deepLink) {
|
|
2874
3362
|
text += `
|
|
2875
3363
|
|
|
2876
|
-
<a href="${
|
|
3364
|
+
<a href="${deepLink}">\u2192 Go to topic</a>`;
|
|
2877
3365
|
}
|
|
2878
3366
|
await this.sendQueue.enqueue(
|
|
2879
3367
|
() => this.bot.api.sendMessage(this.telegramConfig.chatId, text, {
|
|
@@ -2884,7 +3372,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2884
3372
|
);
|
|
2885
3373
|
}
|
|
2886
3374
|
async createSessionThread(sessionId, name) {
|
|
2887
|
-
|
|
3375
|
+
log10.info({ sessionId, name }, "Session topic created");
|
|
2888
3376
|
return String(
|
|
2889
3377
|
await createSessionTopic(this.bot, this.telegramConfig.chatId, name)
|
|
2890
3378
|
);
|
|
@@ -2906,6 +3394,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2906
3394
|
);
|
|
2907
3395
|
}
|
|
2908
3396
|
async sendSkillCommands(sessionId, commands) {
|
|
3397
|
+
if (sessionId === this.assistantSession?.id) return;
|
|
2909
3398
|
const session = this.core.sessionManager.getSession(
|
|
2910
3399
|
sessionId
|
|
2911
3400
|
);
|
|
@@ -2968,7 +3457,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2968
3457
|
}
|
|
2969
3458
|
);
|
|
2970
3459
|
} catch (err) {
|
|
2971
|
-
|
|
3460
|
+
log10.error({ err, sessionId }, "Failed to send skill commands");
|
|
2972
3461
|
}
|
|
2973
3462
|
await this.updateCommandAutocomplete(session.agentName, commands);
|
|
2974
3463
|
}
|
|
@@ -3004,12 +3493,12 @@ Workspace: <code>${workspace}</code>
|
|
|
3004
3493
|
await this.bot.api.setMyCommands(all, {
|
|
3005
3494
|
scope: { type: "chat", chat_id: this.telegramConfig.chatId }
|
|
3006
3495
|
});
|
|
3007
|
-
|
|
3496
|
+
log10.info(
|
|
3008
3497
|
{ count: all.length, skills: validSkills.length },
|
|
3009
3498
|
"Updated command autocomplete"
|
|
3010
3499
|
);
|
|
3011
3500
|
} catch (err) {
|
|
3012
|
-
|
|
3501
|
+
log10.error(
|
|
3013
3502
|
{ err, commands: all },
|
|
3014
3503
|
"Failed to update command autocomplete"
|
|
3015
3504
|
);
|
|
@@ -3018,8 +3507,8 @@ Workspace: <code>${workspace}</code>
|
|
|
3018
3507
|
async finalizeDraft(sessionId) {
|
|
3019
3508
|
const draft = this.sessionDrafts.get(sessionId);
|
|
3020
3509
|
if (!draft) return;
|
|
3021
|
-
const finalMsgId = await draft.finalize();
|
|
3022
3510
|
this.sessionDrafts.delete(sessionId);
|
|
3511
|
+
const finalMsgId = await draft.finalize();
|
|
3023
3512
|
if (sessionId === this.assistantSession?.id) {
|
|
3024
3513
|
const fullText = this.sessionTextBuffers.get(sessionId);
|
|
3025
3514
|
this.sessionTextBuffers.delete(sessionId);
|
|
@@ -3058,4 +3547,4 @@ export {
|
|
|
3058
3547
|
ApiServer,
|
|
3059
3548
|
TelegramAdapter
|
|
3060
3549
|
};
|
|
3061
|
-
//# sourceMappingURL=chunk-
|
|
3550
|
+
//# sourceMappingURL=chunk-SWQRUVBW.js.map
|