@openacp/cli 0.2.30 → 0.3.0
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-WKHCVK5P.js → chunk-C6IFPAWN.js} +581 -102
- package/dist/chunk-C6IFPAWN.js.map +1 -0
- 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-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-OVEJEUX5.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-OVEJEUX5.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();
|
|
@@ -666,6 +668,12 @@ var SessionManager = class {
|
|
|
666
668
|
}
|
|
667
669
|
return void 0;
|
|
668
670
|
}
|
|
671
|
+
getRecordByThread(channelId, threadId) {
|
|
672
|
+
return this.store?.findByPlatform(
|
|
673
|
+
channelId,
|
|
674
|
+
(p) => String(p.topicId) === threadId
|
|
675
|
+
);
|
|
676
|
+
}
|
|
669
677
|
registerSession(session) {
|
|
670
678
|
this.sessions.set(session.id, session);
|
|
671
679
|
}
|
|
@@ -707,11 +715,11 @@ var SessionManager = class {
|
|
|
707
715
|
const session = this.sessions.get(sessionId);
|
|
708
716
|
if (session) {
|
|
709
717
|
await session.cancel();
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
}
|
|
718
|
+
}
|
|
719
|
+
if (this.store) {
|
|
720
|
+
const record = this.store.get(sessionId);
|
|
721
|
+
if (record && record.status !== "cancelled") {
|
|
722
|
+
await this.store.save({ ...record, status: "cancelled" });
|
|
715
723
|
}
|
|
716
724
|
}
|
|
717
725
|
}
|
|
@@ -1044,9 +1052,9 @@ var OpenACPCore = class {
|
|
|
1044
1052
|
);
|
|
1045
1053
|
const adapter = this.adapters.get(message.channelId);
|
|
1046
1054
|
if (adapter) {
|
|
1047
|
-
await adapter.sendMessage(
|
|
1055
|
+
await adapter.sendMessage(message.threadId, {
|
|
1048
1056
|
type: "error",
|
|
1049
|
-
text:
|
|
1057
|
+
text: `\u26A0\uFE0F Session limit reached (${config.security.maxConcurrentSessions}). Please cancel existing sessions with /cancel before starting new ones.`
|
|
1050
1058
|
});
|
|
1051
1059
|
}
|
|
1052
1060
|
return;
|
|
@@ -1086,11 +1094,19 @@ var OpenACPCore = class {
|
|
|
1086
1094
|
channelId,
|
|
1087
1095
|
currentThreadId
|
|
1088
1096
|
);
|
|
1089
|
-
if (
|
|
1097
|
+
if (currentSession) {
|
|
1098
|
+
return this.handleNewSession(
|
|
1099
|
+
channelId,
|
|
1100
|
+
currentSession.agentName,
|
|
1101
|
+
currentSession.workingDirectory
|
|
1102
|
+
);
|
|
1103
|
+
}
|
|
1104
|
+
const record = this.sessionManager.getRecordByThread(channelId, currentThreadId);
|
|
1105
|
+
if (!record || record.status === "cancelled" || record.status === "error") return null;
|
|
1090
1106
|
return this.handleNewSession(
|
|
1091
1107
|
channelId,
|
|
1092
|
-
|
|
1093
|
-
|
|
1108
|
+
record.agentName,
|
|
1109
|
+
record.workingDir
|
|
1094
1110
|
);
|
|
1095
1111
|
}
|
|
1096
1112
|
// --- Lazy Resume ---
|
|
@@ -1609,20 +1625,23 @@ function formatViewerLinks(links, filePath) {
|
|
|
1609
1625
|
\u{1F4DD} <a href="${escapeHtml(links.diff)}">View diff${fileName ? ` \u2014 ${escapeHtml(fileName)}` : ""}</a>`;
|
|
1610
1626
|
return text;
|
|
1611
1627
|
}
|
|
1612
|
-
function
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
);
|
|
1617
|
-
return
|
|
1618
|
-
${lines.join("\n")}`;
|
|
1628
|
+
function formatTokens(n) {
|
|
1629
|
+
return n >= 1e3 ? `${Math.round(n / 1e3)}k` : String(n);
|
|
1630
|
+
}
|
|
1631
|
+
function progressBar(ratio) {
|
|
1632
|
+
const filled = Math.round(Math.min(ratio, 1) * 10);
|
|
1633
|
+
return "\u2593".repeat(filled) + "\u2591".repeat(10 - filled);
|
|
1619
1634
|
}
|
|
1620
1635
|
function formatUsage(usage) {
|
|
1621
|
-
const
|
|
1622
|
-
if (
|
|
1623
|
-
if (
|
|
1624
|
-
|
|
1625
|
-
|
|
1636
|
+
const { tokensUsed, contextSize } = usage;
|
|
1637
|
+
if (tokensUsed == null) return "\u{1F4CA} Usage data unavailable";
|
|
1638
|
+
if (contextSize == null) return `\u{1F4CA} ${formatTokens(tokensUsed)} tokens`;
|
|
1639
|
+
const ratio = tokensUsed / contextSize;
|
|
1640
|
+
const pct = Math.round(ratio * 100);
|
|
1641
|
+
const bar = progressBar(ratio);
|
|
1642
|
+
const emoji = pct >= 85 ? "\u26A0\uFE0F" : "\u{1F4CA}";
|
|
1643
|
+
return `${emoji} ${formatTokens(tokensUsed)} / ${formatTokens(contextSize)} tokens
|
|
1644
|
+
${bar} ${pct}%`;
|
|
1626
1645
|
}
|
|
1627
1646
|
function splitMessage(text, maxLength = 4096) {
|
|
1628
1647
|
if (text.length <= maxLength) return [text];
|
|
@@ -1663,6 +1682,7 @@ var MessageDraft = class {
|
|
|
1663
1682
|
flushPromise = Promise.resolve();
|
|
1664
1683
|
lastSentBuffer = "";
|
|
1665
1684
|
append(text) {
|
|
1685
|
+
if (!text) return;
|
|
1666
1686
|
this.buffer += text;
|
|
1667
1687
|
this.scheduleFlush();
|
|
1668
1688
|
}
|
|
@@ -1709,7 +1729,6 @@ var MessageDraft = class {
|
|
|
1709
1729
|
);
|
|
1710
1730
|
this.lastSentBuffer = this.buffer;
|
|
1711
1731
|
} catch {
|
|
1712
|
-
this.messageId = void 0;
|
|
1713
1732
|
}
|
|
1714
1733
|
}
|
|
1715
1734
|
}
|
|
@@ -1813,6 +1832,8 @@ function setupCommands(bot, core, chatId, assistant) {
|
|
|
1813
1832
|
bot.command("agents", (ctx) => handleAgents(ctx, core));
|
|
1814
1833
|
bot.command("help", (ctx) => handleHelp(ctx));
|
|
1815
1834
|
bot.command("menu", (ctx) => handleMenu(ctx));
|
|
1835
|
+
bot.command("enable_dangerous", (ctx) => handleEnableDangerous(ctx, core));
|
|
1836
|
+
bot.command("disable_dangerous", (ctx) => handleDisableDangerous(ctx, core));
|
|
1816
1837
|
}
|
|
1817
1838
|
function buildMenuKeyboard() {
|
|
1818
1839
|
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 +1919,8 @@ async function handleNew(ctx, core, chatId, assistant) {
|
|
|
1898
1919
|
<b>Workspace:</b> <code>${escapeHtml(session.workingDirectory)}</code>`,
|
|
1899
1920
|
{
|
|
1900
1921
|
message_thread_id: threadId,
|
|
1901
|
-
parse_mode: "HTML"
|
|
1922
|
+
parse_mode: "HTML",
|
|
1923
|
+
reply_markup: buildDangerousModeKeyboard(session.id, false)
|
|
1902
1924
|
}
|
|
1903
1925
|
);
|
|
1904
1926
|
session.warmup().catch((err) => log6.error({ err }, "Warm-up error"));
|
|
@@ -1952,7 +1974,8 @@ async function handleNewChat(ctx, core, chatId) {
|
|
|
1952
1974
|
<b>Workspace:</b> <code>${escapeHtml(session.workingDirectory)}</code>`,
|
|
1953
1975
|
{
|
|
1954
1976
|
message_thread_id: newThreadId,
|
|
1955
|
-
parse_mode: "HTML"
|
|
1977
|
+
parse_mode: "HTML",
|
|
1978
|
+
reply_markup: buildDangerousModeKeyboard(session.id, false)
|
|
1956
1979
|
}
|
|
1957
1980
|
);
|
|
1958
1981
|
session.warmup().catch((err) => log6.error({ err }, "Warm-up error"));
|
|
@@ -1981,6 +2004,13 @@ async function handleCancel(ctx, core, assistant) {
|
|
|
1981
2004
|
log6.info({ sessionId: session.id }, "Cancel session command");
|
|
1982
2005
|
await session.cancel();
|
|
1983
2006
|
await ctx.reply("\u26D4 Session cancelled.", { parse_mode: "HTML" });
|
|
2007
|
+
return;
|
|
2008
|
+
}
|
|
2009
|
+
const record = core.sessionManager.getRecordByThread("telegram", String(threadId));
|
|
2010
|
+
if (record && record.status !== "cancelled" && record.status !== "error") {
|
|
2011
|
+
log6.info({ sessionId: record.sessionId }, "Cancel session command (from store)");
|
|
2012
|
+
await core.sessionManager.cancelSession(record.sessionId);
|
|
2013
|
+
await ctx.reply("\u26D4 Session cancelled.", { parse_mode: "HTML" });
|
|
1984
2014
|
}
|
|
1985
2015
|
}
|
|
1986
2016
|
async function handleStatus(ctx, core) {
|
|
@@ -2000,9 +2030,20 @@ async function handleStatus(ctx, core) {
|
|
|
2000
2030
|
{ parse_mode: "HTML" }
|
|
2001
2031
|
);
|
|
2002
2032
|
} else {
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2033
|
+
const record = core.sessionManager.getRecordByThread("telegram", String(threadId));
|
|
2034
|
+
if (record) {
|
|
2035
|
+
await ctx.reply(
|
|
2036
|
+
`<b>Session:</b> ${escapeHtml(record.name || record.sessionId)}
|
|
2037
|
+
<b>Agent:</b> ${escapeHtml(record.agentName)}
|
|
2038
|
+
<b>Status:</b> ${escapeHtml(record.status)} (not loaded)
|
|
2039
|
+
<b>Workspace:</b> <code>${escapeHtml(record.workingDir)}</code>`,
|
|
2040
|
+
{ parse_mode: "HTML" }
|
|
2041
|
+
);
|
|
2042
|
+
} else {
|
|
2043
|
+
await ctx.reply("No active session in this topic.", {
|
|
2044
|
+
parse_mode: "HTML"
|
|
2045
|
+
});
|
|
2046
|
+
}
|
|
2006
2047
|
}
|
|
2007
2048
|
} else {
|
|
2008
2049
|
const sessions = core.sessionManager.listSessions("telegram");
|
|
@@ -2047,6 +2088,81 @@ Or just chat in the \u{1F916} Assistant topic for help!`,
|
|
|
2047
2088
|
{ parse_mode: "HTML" }
|
|
2048
2089
|
);
|
|
2049
2090
|
}
|
|
2091
|
+
function buildDangerousModeKeyboard(sessionId, enabled) {
|
|
2092
|
+
return new InlineKeyboard().text(
|
|
2093
|
+
enabled ? "\u{1F510} Disable Dangerous Mode" : "\u2620\uFE0F Enable Dangerous Mode",
|
|
2094
|
+
`d:${sessionId}`
|
|
2095
|
+
);
|
|
2096
|
+
}
|
|
2097
|
+
function setupDangerousModeCallbacks(bot, core) {
|
|
2098
|
+
bot.callbackQuery(/^d:/, async (ctx) => {
|
|
2099
|
+
const sessionId = ctx.callbackQuery.data.slice(2);
|
|
2100
|
+
const session = core.sessionManager.getSession(sessionId);
|
|
2101
|
+
if (!session) {
|
|
2102
|
+
try {
|
|
2103
|
+
await ctx.answerCallbackQuery({ text: "\u26A0\uFE0F Session not found or already ended." });
|
|
2104
|
+
} catch {
|
|
2105
|
+
}
|
|
2106
|
+
return;
|
|
2107
|
+
}
|
|
2108
|
+
session.dangerousMode = !session.dangerousMode;
|
|
2109
|
+
log6.info({ sessionId, dangerousMode: session.dangerousMode }, "Dangerous mode toggled via button");
|
|
2110
|
+
const toastText = session.dangerousMode ? "\u2620\uFE0F Dangerous mode enabled \u2014 permissions auto-approved" : "\u{1F510} Dangerous mode disabled \u2014 permissions shown normally";
|
|
2111
|
+
try {
|
|
2112
|
+
await ctx.answerCallbackQuery({ text: toastText });
|
|
2113
|
+
} catch {
|
|
2114
|
+
}
|
|
2115
|
+
try {
|
|
2116
|
+
await ctx.editMessageReplyMarkup({
|
|
2117
|
+
reply_markup: buildDangerousModeKeyboard(sessionId, session.dangerousMode)
|
|
2118
|
+
});
|
|
2119
|
+
} catch {
|
|
2120
|
+
}
|
|
2121
|
+
});
|
|
2122
|
+
}
|
|
2123
|
+
async function handleEnableDangerous(ctx, core) {
|
|
2124
|
+
const threadId = ctx.message?.message_thread_id;
|
|
2125
|
+
if (!threadId) {
|
|
2126
|
+
await ctx.reply("\u26A0\uFE0F This command only works inside a session topic.", { parse_mode: "HTML" });
|
|
2127
|
+
return;
|
|
2128
|
+
}
|
|
2129
|
+
const session = core.sessionManager.getSessionByThread("telegram", String(threadId));
|
|
2130
|
+
if (!session) {
|
|
2131
|
+
await ctx.reply("\u26A0\uFE0F No active session in this topic.", { parse_mode: "HTML" });
|
|
2132
|
+
return;
|
|
2133
|
+
}
|
|
2134
|
+
if (session.dangerousMode) {
|
|
2135
|
+
await ctx.reply("\u2620\uFE0F Dangerous mode is already enabled.", { parse_mode: "HTML" });
|
|
2136
|
+
return;
|
|
2137
|
+
}
|
|
2138
|
+
session.dangerousMode = true;
|
|
2139
|
+
await ctx.reply(
|
|
2140
|
+
`\u26A0\uFE0F <b>Dangerous mode enabled</b>
|
|
2141
|
+
|
|
2142
|
+
All permission requests will be auto-approved. Claude can run arbitrary commands without asking.
|
|
2143
|
+
|
|
2144
|
+
Use /disable_dangerous to restore normal behaviour.`,
|
|
2145
|
+
{ parse_mode: "HTML" }
|
|
2146
|
+
);
|
|
2147
|
+
}
|
|
2148
|
+
async function handleDisableDangerous(ctx, core) {
|
|
2149
|
+
const threadId = ctx.message?.message_thread_id;
|
|
2150
|
+
if (!threadId) {
|
|
2151
|
+
await ctx.reply("\u26A0\uFE0F This command only works inside a session topic.", { parse_mode: "HTML" });
|
|
2152
|
+
return;
|
|
2153
|
+
}
|
|
2154
|
+
const session = core.sessionManager.getSessionByThread("telegram", String(threadId));
|
|
2155
|
+
if (!session) {
|
|
2156
|
+
await ctx.reply("\u26A0\uFE0F No active session in this topic.", { parse_mode: "HTML" });
|
|
2157
|
+
return;
|
|
2158
|
+
}
|
|
2159
|
+
if (!session.dangerousMode) {
|
|
2160
|
+
await ctx.reply("\u{1F510} Dangerous mode is already disabled.", { parse_mode: "HTML" });
|
|
2161
|
+
return;
|
|
2162
|
+
}
|
|
2163
|
+
session.dangerousMode = false;
|
|
2164
|
+
await ctx.reply("\u{1F510} <b>Dangerous mode disabled</b>\n\nPermission requests will be shown normally.", { parse_mode: "HTML" });
|
|
2165
|
+
}
|
|
2050
2166
|
function botFromCtx(ctx) {
|
|
2051
2167
|
return { api: ctx.api };
|
|
2052
2168
|
}
|
|
@@ -2128,7 +2244,9 @@ var STATIC_COMMANDS = [
|
|
|
2128
2244
|
{ command: "status", description: "Show status" },
|
|
2129
2245
|
{ command: "agents", description: "List available agents" },
|
|
2130
2246
|
{ command: "help", description: "Help" },
|
|
2131
|
-
{ command: "menu", description: "Show menu" }
|
|
2247
|
+
{ command: "menu", description: "Show menu" },
|
|
2248
|
+
{ command: "enable_dangerous", description: "Auto-approve all permission requests (session only)" },
|
|
2249
|
+
{ command: "disable_dangerous", description: "Restore normal permission prompts (session only)" }
|
|
2132
2250
|
];
|
|
2133
2251
|
|
|
2134
2252
|
// src/adapters/telegram/permissions.ts
|
|
@@ -2213,8 +2331,10 @@ ${escapeHtml(request.description)}`,
|
|
|
2213
2331
|
};
|
|
2214
2332
|
|
|
2215
2333
|
// src/adapters/telegram/assistant.ts
|
|
2334
|
+
var log8 = createChildLogger({ module: "telegram-assistant" });
|
|
2216
2335
|
async function spawnAssistant(core, adapter, assistantTopicId) {
|
|
2217
2336
|
const config = core.configManager.get();
|
|
2337
|
+
log8.info({ agent: config.defaultAgent }, "Creating assistant session...");
|
|
2218
2338
|
const session = await core.sessionManager.createSession(
|
|
2219
2339
|
"telegram",
|
|
2220
2340
|
config.defaultAgent,
|
|
@@ -2222,10 +2342,16 @@ async function spawnAssistant(core, adapter, assistantTopicId) {
|
|
|
2222
2342
|
core.agentManager
|
|
2223
2343
|
);
|
|
2224
2344
|
session.threadId = String(assistantTopicId);
|
|
2225
|
-
|
|
2226
|
-
|
|
2345
|
+
session.name = "Assistant";
|
|
2346
|
+
log8.info({ sessionId: session.id }, "Assistant agent spawned");
|
|
2227
2347
|
core.wireSessionEvents(session, adapter);
|
|
2228
|
-
|
|
2348
|
+
const systemPrompt = buildAssistantSystemPrompt(config);
|
|
2349
|
+
const ready = session.enqueuePrompt(systemPrompt).then(() => {
|
|
2350
|
+
log8.info({ sessionId: session.id }, "Assistant system prompt completed");
|
|
2351
|
+
}).catch((err) => {
|
|
2352
|
+
log8.warn({ err }, "Assistant system prompt failed");
|
|
2353
|
+
});
|
|
2354
|
+
return { session, ready };
|
|
2229
2355
|
}
|
|
2230
2356
|
function buildAssistantSystemPrompt(config) {
|
|
2231
2357
|
const agentNames = Object.keys(config.agents).join(", ");
|
|
@@ -2260,6 +2386,279 @@ function redirectToAssistant(chatId, assistantTopicId) {
|
|
|
2260
2386
|
return `\u{1F4AC} Please use the <a href="${link}">\u{1F916} Assistant</a> topic to chat with OpenACP.`;
|
|
2261
2387
|
}
|
|
2262
2388
|
|
|
2389
|
+
// src/adapters/telegram/activity.ts
|
|
2390
|
+
var log9 = createChildLogger({ module: "telegram:activity" });
|
|
2391
|
+
var THINKING_REFRESH_MS = 15e3;
|
|
2392
|
+
var THINKING_MAX_MS = 3 * 60 * 1e3;
|
|
2393
|
+
var ThinkingIndicator = class {
|
|
2394
|
+
constructor(api, chatId, threadId, sendQueue) {
|
|
2395
|
+
this.api = api;
|
|
2396
|
+
this.chatId = chatId;
|
|
2397
|
+
this.threadId = threadId;
|
|
2398
|
+
this.sendQueue = sendQueue;
|
|
2399
|
+
}
|
|
2400
|
+
msgId;
|
|
2401
|
+
sending = false;
|
|
2402
|
+
dismissed = false;
|
|
2403
|
+
refreshTimer;
|
|
2404
|
+
showTime = 0;
|
|
2405
|
+
async show() {
|
|
2406
|
+
if (this.msgId || this.sending || this.dismissed) return;
|
|
2407
|
+
this.sending = true;
|
|
2408
|
+
this.showTime = Date.now();
|
|
2409
|
+
try {
|
|
2410
|
+
const result = await this.sendQueue.enqueue(
|
|
2411
|
+
() => this.api.sendMessage(this.chatId, "\u{1F4AD} <i>Thinking...</i>", {
|
|
2412
|
+
message_thread_id: this.threadId,
|
|
2413
|
+
parse_mode: "HTML",
|
|
2414
|
+
disable_notification: true
|
|
2415
|
+
})
|
|
2416
|
+
);
|
|
2417
|
+
if (result && !this.dismissed) {
|
|
2418
|
+
this.msgId = result.message_id;
|
|
2419
|
+
this.startRefreshTimer();
|
|
2420
|
+
}
|
|
2421
|
+
} catch (err) {
|
|
2422
|
+
log9.warn({ err }, "ThinkingIndicator.show() failed");
|
|
2423
|
+
} finally {
|
|
2424
|
+
this.sending = false;
|
|
2425
|
+
}
|
|
2426
|
+
}
|
|
2427
|
+
/** Clear state — stops refresh timer, no Telegram API call */
|
|
2428
|
+
dismiss() {
|
|
2429
|
+
this.dismissed = true;
|
|
2430
|
+
this.msgId = void 0;
|
|
2431
|
+
this.stopRefreshTimer();
|
|
2432
|
+
}
|
|
2433
|
+
/** Reset for a new prompt cycle */
|
|
2434
|
+
reset() {
|
|
2435
|
+
this.dismissed = false;
|
|
2436
|
+
}
|
|
2437
|
+
startRefreshTimer() {
|
|
2438
|
+
this.stopRefreshTimer();
|
|
2439
|
+
this.refreshTimer = setInterval(() => {
|
|
2440
|
+
if (this.dismissed || !this.msgId || Date.now() - this.showTime >= THINKING_MAX_MS) {
|
|
2441
|
+
this.stopRefreshTimer();
|
|
2442
|
+
return;
|
|
2443
|
+
}
|
|
2444
|
+
const elapsed = Math.round((Date.now() - this.showTime) / 1e3);
|
|
2445
|
+
this.sendQueue.enqueue(() => {
|
|
2446
|
+
if (this.dismissed) return Promise.resolve(void 0);
|
|
2447
|
+
return this.api.sendMessage(this.chatId, `\u{1F4AD} <i>Still thinking... (${elapsed}s)</i>`, {
|
|
2448
|
+
message_thread_id: this.threadId,
|
|
2449
|
+
parse_mode: "HTML",
|
|
2450
|
+
disable_notification: true
|
|
2451
|
+
});
|
|
2452
|
+
}).then((result) => {
|
|
2453
|
+
if (result && !this.dismissed) {
|
|
2454
|
+
this.msgId = result.message_id;
|
|
2455
|
+
}
|
|
2456
|
+
}).catch(() => {
|
|
2457
|
+
});
|
|
2458
|
+
}, THINKING_REFRESH_MS);
|
|
2459
|
+
}
|
|
2460
|
+
stopRefreshTimer() {
|
|
2461
|
+
if (this.refreshTimer) {
|
|
2462
|
+
clearInterval(this.refreshTimer);
|
|
2463
|
+
this.refreshTimer = void 0;
|
|
2464
|
+
}
|
|
2465
|
+
}
|
|
2466
|
+
};
|
|
2467
|
+
var UsageMessage = class {
|
|
2468
|
+
constructor(api, chatId, threadId, sendQueue) {
|
|
2469
|
+
this.api = api;
|
|
2470
|
+
this.chatId = chatId;
|
|
2471
|
+
this.threadId = threadId;
|
|
2472
|
+
this.sendQueue = sendQueue;
|
|
2473
|
+
}
|
|
2474
|
+
msgId;
|
|
2475
|
+
async send(usage) {
|
|
2476
|
+
const text = formatUsage(usage);
|
|
2477
|
+
try {
|
|
2478
|
+
if (this.msgId) {
|
|
2479
|
+
await this.sendQueue.enqueue(
|
|
2480
|
+
() => this.api.editMessageText(this.chatId, this.msgId, text, {
|
|
2481
|
+
parse_mode: "HTML"
|
|
2482
|
+
})
|
|
2483
|
+
);
|
|
2484
|
+
} else {
|
|
2485
|
+
const result = await this.sendQueue.enqueue(
|
|
2486
|
+
() => this.api.sendMessage(this.chatId, text, {
|
|
2487
|
+
message_thread_id: this.threadId,
|
|
2488
|
+
parse_mode: "HTML",
|
|
2489
|
+
disable_notification: true
|
|
2490
|
+
})
|
|
2491
|
+
);
|
|
2492
|
+
if (result) this.msgId = result.message_id;
|
|
2493
|
+
}
|
|
2494
|
+
} catch (err) {
|
|
2495
|
+
log9.warn({ err }, "UsageMessage.send() failed");
|
|
2496
|
+
}
|
|
2497
|
+
}
|
|
2498
|
+
async delete() {
|
|
2499
|
+
if (!this.msgId) return;
|
|
2500
|
+
const id = this.msgId;
|
|
2501
|
+
this.msgId = void 0;
|
|
2502
|
+
try {
|
|
2503
|
+
await this.sendQueue.enqueue(() => this.api.deleteMessage(this.chatId, id));
|
|
2504
|
+
} catch (err) {
|
|
2505
|
+
log9.warn({ err }, "UsageMessage.delete() failed");
|
|
2506
|
+
}
|
|
2507
|
+
}
|
|
2508
|
+
};
|
|
2509
|
+
function formatPlanCard(entries) {
|
|
2510
|
+
const statusIcon = {
|
|
2511
|
+
completed: "\u2705",
|
|
2512
|
+
in_progress: "\u{1F504}",
|
|
2513
|
+
pending: "\u2B1C",
|
|
2514
|
+
failed: "\u274C"
|
|
2515
|
+
};
|
|
2516
|
+
const total = entries.length;
|
|
2517
|
+
const done = entries.filter((e) => e.status === "completed").length;
|
|
2518
|
+
const ratio = total > 0 ? done / total : 0;
|
|
2519
|
+
const filled = Math.round(ratio * 10);
|
|
2520
|
+
const bar = "\u2593".repeat(filled) + "\u2591".repeat(10 - filled);
|
|
2521
|
+
const pct = Math.round(ratio * 100);
|
|
2522
|
+
const header = `\u{1F4CB} <b>Plan</b>
|
|
2523
|
+
${bar} ${pct}% \xB7 ${done}/${total}`;
|
|
2524
|
+
const lines = entries.map((e, i) => {
|
|
2525
|
+
const icon = statusIcon[e.status] ?? "\u2B1C";
|
|
2526
|
+
return `${icon} ${i + 1}. ${e.content}`;
|
|
2527
|
+
});
|
|
2528
|
+
return [header, ...lines].join("\n");
|
|
2529
|
+
}
|
|
2530
|
+
var PlanCard = class {
|
|
2531
|
+
constructor(api, chatId, threadId, sendQueue) {
|
|
2532
|
+
this.api = api;
|
|
2533
|
+
this.chatId = chatId;
|
|
2534
|
+
this.threadId = threadId;
|
|
2535
|
+
this.sendQueue = sendQueue;
|
|
2536
|
+
}
|
|
2537
|
+
msgId;
|
|
2538
|
+
flushPromise = Promise.resolve();
|
|
2539
|
+
latestEntries;
|
|
2540
|
+
flushTimer;
|
|
2541
|
+
update(entries) {
|
|
2542
|
+
this.latestEntries = entries;
|
|
2543
|
+
if (this.flushTimer) clearTimeout(this.flushTimer);
|
|
2544
|
+
this.flushTimer = setTimeout(() => {
|
|
2545
|
+
this.flushTimer = void 0;
|
|
2546
|
+
this.flushPromise = this.flushPromise.then(() => this._flush()).catch(() => {
|
|
2547
|
+
});
|
|
2548
|
+
}, 3500);
|
|
2549
|
+
}
|
|
2550
|
+
async finalize() {
|
|
2551
|
+
if (!this.latestEntries) return;
|
|
2552
|
+
if (this.flushTimer) {
|
|
2553
|
+
clearTimeout(this.flushTimer);
|
|
2554
|
+
this.flushTimer = void 0;
|
|
2555
|
+
}
|
|
2556
|
+
await this.flushPromise;
|
|
2557
|
+
this.flushPromise = this.flushPromise.then(() => this._flush()).catch(() => {
|
|
2558
|
+
});
|
|
2559
|
+
await this.flushPromise;
|
|
2560
|
+
}
|
|
2561
|
+
destroy() {
|
|
2562
|
+
if (this.flushTimer) {
|
|
2563
|
+
clearTimeout(this.flushTimer);
|
|
2564
|
+
this.flushTimer = void 0;
|
|
2565
|
+
}
|
|
2566
|
+
}
|
|
2567
|
+
async _flush() {
|
|
2568
|
+
if (!this.latestEntries) return;
|
|
2569
|
+
const text = formatPlanCard(this.latestEntries);
|
|
2570
|
+
try {
|
|
2571
|
+
if (this.msgId) {
|
|
2572
|
+
await this.sendQueue.enqueue(
|
|
2573
|
+
() => this.api.editMessageText(this.chatId, this.msgId, text, {
|
|
2574
|
+
parse_mode: "HTML"
|
|
2575
|
+
})
|
|
2576
|
+
);
|
|
2577
|
+
} else {
|
|
2578
|
+
const result = await this.sendQueue.enqueue(
|
|
2579
|
+
() => this.api.sendMessage(this.chatId, text, {
|
|
2580
|
+
message_thread_id: this.threadId,
|
|
2581
|
+
parse_mode: "HTML",
|
|
2582
|
+
disable_notification: true
|
|
2583
|
+
})
|
|
2584
|
+
);
|
|
2585
|
+
if (result) this.msgId = result.message_id;
|
|
2586
|
+
}
|
|
2587
|
+
} catch (err) {
|
|
2588
|
+
log9.warn({ err }, "PlanCard flush failed");
|
|
2589
|
+
}
|
|
2590
|
+
}
|
|
2591
|
+
};
|
|
2592
|
+
var ActivityTracker = class {
|
|
2593
|
+
constructor(api, chatId, threadId, sendQueue) {
|
|
2594
|
+
this.api = api;
|
|
2595
|
+
this.chatId = chatId;
|
|
2596
|
+
this.threadId = threadId;
|
|
2597
|
+
this.sendQueue = sendQueue;
|
|
2598
|
+
this.thinking = new ThinkingIndicator(api, chatId, threadId, sendQueue);
|
|
2599
|
+
this.planCard = new PlanCard(api, chatId, threadId, sendQueue);
|
|
2600
|
+
this.usage = new UsageMessage(api, chatId, threadId, sendQueue);
|
|
2601
|
+
}
|
|
2602
|
+
isFirstEvent = true;
|
|
2603
|
+
hasPlanCard = false;
|
|
2604
|
+
thinking;
|
|
2605
|
+
planCard;
|
|
2606
|
+
usage;
|
|
2607
|
+
async onNewPrompt() {
|
|
2608
|
+
this.isFirstEvent = true;
|
|
2609
|
+
this.hasPlanCard = false;
|
|
2610
|
+
this.thinking.dismiss();
|
|
2611
|
+
this.thinking.reset();
|
|
2612
|
+
}
|
|
2613
|
+
async onThought() {
|
|
2614
|
+
await this._firstEventGuard();
|
|
2615
|
+
await this.thinking.show();
|
|
2616
|
+
}
|
|
2617
|
+
async onPlan(entries) {
|
|
2618
|
+
await this._firstEventGuard();
|
|
2619
|
+
this.thinking.dismiss();
|
|
2620
|
+
this.hasPlanCard = true;
|
|
2621
|
+
this.planCard.update(entries);
|
|
2622
|
+
}
|
|
2623
|
+
async onToolCall() {
|
|
2624
|
+
await this._firstEventGuard();
|
|
2625
|
+
this.thinking.dismiss();
|
|
2626
|
+
this.thinking.reset();
|
|
2627
|
+
}
|
|
2628
|
+
async onTextStart() {
|
|
2629
|
+
await this._firstEventGuard();
|
|
2630
|
+
this.thinking.dismiss();
|
|
2631
|
+
}
|
|
2632
|
+
async sendUsage(data) {
|
|
2633
|
+
await this.usage.send(data);
|
|
2634
|
+
}
|
|
2635
|
+
async onComplete() {
|
|
2636
|
+
if (this.hasPlanCard) {
|
|
2637
|
+
await this.planCard.finalize();
|
|
2638
|
+
} else {
|
|
2639
|
+
try {
|
|
2640
|
+
await this.sendQueue.enqueue(
|
|
2641
|
+
() => this.api.sendMessage(this.chatId, "\u2705 <b>Done</b>", {
|
|
2642
|
+
message_thread_id: this.threadId,
|
|
2643
|
+
parse_mode: "HTML",
|
|
2644
|
+
disable_notification: true
|
|
2645
|
+
})
|
|
2646
|
+
);
|
|
2647
|
+
} catch (err) {
|
|
2648
|
+
log9.warn({ err }, "ActivityTracker.onComplete() Done send failed");
|
|
2649
|
+
}
|
|
2650
|
+
}
|
|
2651
|
+
}
|
|
2652
|
+
destroy() {
|
|
2653
|
+
this.planCard.destroy();
|
|
2654
|
+
}
|
|
2655
|
+
async _firstEventGuard() {
|
|
2656
|
+
if (!this.isFirstEvent) return;
|
|
2657
|
+
this.isFirstEvent = false;
|
|
2658
|
+
await this.usage.delete();
|
|
2659
|
+
}
|
|
2660
|
+
};
|
|
2661
|
+
|
|
2263
2662
|
// src/adapters/telegram/send-queue.ts
|
|
2264
2663
|
var TelegramSendQueue = class {
|
|
2265
2664
|
items = [];
|
|
@@ -2472,7 +2871,7 @@ function setupActionCallbacks(bot, core, chatId, getAssistantSessionId) {
|
|
|
2472
2871
|
}
|
|
2473
2872
|
|
|
2474
2873
|
// src/adapters/telegram/adapter.ts
|
|
2475
|
-
var
|
|
2874
|
+
var log10 = createChildLogger({ module: "telegram" });
|
|
2476
2875
|
function patchedFetch(input, init) {
|
|
2477
2876
|
if (init?.signal && !(init.signal instanceof AbortSignal)) {
|
|
2478
2877
|
const nativeController = new AbortController();
|
|
@@ -2495,11 +2894,26 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2495
2894
|
// sessionId → (toolCallId → state)
|
|
2496
2895
|
permissionHandler;
|
|
2497
2896
|
assistantSession = null;
|
|
2897
|
+
assistantInitializing = false;
|
|
2498
2898
|
notificationTopicId;
|
|
2499
2899
|
assistantTopicId;
|
|
2500
2900
|
skillMessages = /* @__PURE__ */ new Map();
|
|
2501
2901
|
// sessionId → pinned messageId
|
|
2502
2902
|
sendQueue = new TelegramSendQueue(3e3);
|
|
2903
|
+
sessionTrackers = /* @__PURE__ */ new Map();
|
|
2904
|
+
getOrCreateTracker(sessionId, threadId) {
|
|
2905
|
+
let tracker = this.sessionTrackers.get(sessionId);
|
|
2906
|
+
if (!tracker) {
|
|
2907
|
+
tracker = new ActivityTracker(
|
|
2908
|
+
this.bot.api,
|
|
2909
|
+
this.telegramConfig.chatId,
|
|
2910
|
+
threadId,
|
|
2911
|
+
this.sendQueue
|
|
2912
|
+
);
|
|
2913
|
+
this.sessionTrackers.set(sessionId, tracker);
|
|
2914
|
+
}
|
|
2915
|
+
return tracker;
|
|
2916
|
+
}
|
|
2503
2917
|
constructor(core, config) {
|
|
2504
2918
|
super(core, config);
|
|
2505
2919
|
this.telegramConfig = config;
|
|
@@ -2508,7 +2922,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2508
2922
|
this.bot = new Bot(this.telegramConfig.botToken, { client: { fetch: patchedFetch } });
|
|
2509
2923
|
this.bot.catch((err) => {
|
|
2510
2924
|
const rootCause = err.error instanceof Error ? err.error : err;
|
|
2511
|
-
|
|
2925
|
+
log10.error({ err: rootCause }, "Telegram bot error");
|
|
2512
2926
|
});
|
|
2513
2927
|
this.bot.api.config.use(async (prev, method, payload, signal) => {
|
|
2514
2928
|
const maxRetries = 3;
|
|
@@ -2522,7 +2936,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2522
2936
|
if (rateLimitedMethods.includes(method)) {
|
|
2523
2937
|
this.sendQueue.onRateLimited();
|
|
2524
2938
|
}
|
|
2525
|
-
|
|
2939
|
+
log10.warn(
|
|
2526
2940
|
{ method, retryAfter, attempt: attempt + 1 },
|
|
2527
2941
|
"Rate limited by Telegram, retrying"
|
|
2528
2942
|
);
|
|
@@ -2567,6 +2981,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2567
2981
|
(notification) => this.sendNotification(notification)
|
|
2568
2982
|
);
|
|
2569
2983
|
setupSkillCallbacks(this.bot, this.core);
|
|
2984
|
+
setupDangerousModeCallbacks(this.bot, this.core);
|
|
2570
2985
|
setupActionCallbacks(
|
|
2571
2986
|
this.bot,
|
|
2572
2987
|
this.core,
|
|
@@ -2591,26 +3006,15 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2591
3006
|
this.setupRoutes();
|
|
2592
3007
|
this.bot.start({
|
|
2593
3008
|
allowed_updates: ["message", "callback_query"],
|
|
2594
|
-
onStart: () =>
|
|
3009
|
+
onStart: () => log10.info(
|
|
2595
3010
|
{ chatId: this.telegramConfig.chatId },
|
|
2596
3011
|
"Telegram bot started"
|
|
2597
3012
|
)
|
|
2598
3013
|
});
|
|
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
3014
|
try {
|
|
2609
3015
|
const config = this.core.configManager.get();
|
|
2610
3016
|
const agents = this.core.agentManager.getAvailableAgents();
|
|
2611
|
-
const agentList = agents.map(
|
|
2612
|
-
(a) => `${escapeHtml(a.name)}${a.name === config.defaultAgent ? " (default)" : ""}`
|
|
2613
|
-
).join(", ");
|
|
3017
|
+
const agentList = agents.map((a) => `${escapeHtml(a.name)}${a.name === config.defaultAgent ? " (default)" : ""}`).join(", ");
|
|
2614
3018
|
const workspace = escapeHtml(config.workspace.baseDir);
|
|
2615
3019
|
const welcomeText = `\u{1F44B} <b>OpenACP Assistant</b> is online.
|
|
2616
3020
|
|
|
@@ -2624,7 +3028,32 @@ Workspace: <code>${workspace}</code>
|
|
|
2624
3028
|
reply_markup: buildMenuKeyboard()
|
|
2625
3029
|
});
|
|
2626
3030
|
} catch (err) {
|
|
2627
|
-
|
|
3031
|
+
log10.warn({ err }, "Failed to send welcome message");
|
|
3032
|
+
}
|
|
3033
|
+
try {
|
|
3034
|
+
log10.info("Spawning assistant session...");
|
|
3035
|
+
const { session, ready } = await spawnAssistant(
|
|
3036
|
+
this.core,
|
|
3037
|
+
this,
|
|
3038
|
+
this.assistantTopicId
|
|
3039
|
+
);
|
|
3040
|
+
this.assistantSession = session;
|
|
3041
|
+
this.assistantInitializing = true;
|
|
3042
|
+
log10.info({ sessionId: session.id }, "Assistant session ready, system prompt running in background");
|
|
3043
|
+
ready.then(() => {
|
|
3044
|
+
this.assistantInitializing = false;
|
|
3045
|
+
log10.info({ sessionId: session.id }, "Assistant ready for user messages");
|
|
3046
|
+
});
|
|
3047
|
+
} catch (err) {
|
|
3048
|
+
log10.error({ err }, "Failed to spawn assistant");
|
|
3049
|
+
this.bot.api.sendMessage(
|
|
3050
|
+
this.telegramConfig.chatId,
|
|
3051
|
+
`\u26A0\uFE0F <b>Failed to start assistant session.</b>
|
|
3052
|
+
|
|
3053
|
+
<code>${err instanceof Error ? err.message : String(err)}</code>`,
|
|
3054
|
+
{ message_thread_id: this.assistantTopicId, parse_mode: "HTML" }
|
|
3055
|
+
).catch(() => {
|
|
3056
|
+
});
|
|
2628
3057
|
}
|
|
2629
3058
|
}
|
|
2630
3059
|
async stop() {
|
|
@@ -2632,7 +3061,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2632
3061
|
await this.assistantSession.destroy();
|
|
2633
3062
|
}
|
|
2634
3063
|
await this.bot.stop();
|
|
2635
|
-
|
|
3064
|
+
log10.info("Telegram bot stopped");
|
|
2636
3065
|
}
|
|
2637
3066
|
setupRoutes() {
|
|
2638
3067
|
this.bot.on("message:text", async (ctx) => {
|
|
@@ -2647,16 +3076,24 @@ Workspace: <code>${workspace}</code>
|
|
|
2647
3076
|
}
|
|
2648
3077
|
if (threadId === this.notificationTopicId) return;
|
|
2649
3078
|
if (threadId === this.assistantTopicId) {
|
|
3079
|
+
if (!this.assistantSession) {
|
|
3080
|
+
await ctx.reply("\u26A0\uFE0F Assistant is not available yet. Please try again shortly.", { parse_mode: "HTML" });
|
|
3081
|
+
return;
|
|
3082
|
+
}
|
|
2650
3083
|
await this.finalizeDraft(this.assistantSession.id);
|
|
2651
3084
|
ctx.replyWithChatAction("typing").catch(() => {
|
|
2652
3085
|
});
|
|
2653
3086
|
handleAssistantMessage(this.assistantSession, ctx.message.text).catch(
|
|
2654
|
-
(err) =>
|
|
3087
|
+
(err) => log10.error({ err }, "Assistant error")
|
|
2655
3088
|
);
|
|
2656
3089
|
return;
|
|
2657
3090
|
}
|
|
2658
3091
|
const sessionId = this.core.sessionManager.getSessionByThread("telegram", String(threadId))?.id;
|
|
2659
3092
|
if (sessionId) await this.finalizeDraft(sessionId);
|
|
3093
|
+
if (sessionId) {
|
|
3094
|
+
const tracker = this.sessionTrackers.get(sessionId);
|
|
3095
|
+
if (tracker) await tracker.onNewPrompt();
|
|
3096
|
+
}
|
|
2660
3097
|
ctx.replyWithChatAction("typing").catch(() => {
|
|
2661
3098
|
});
|
|
2662
3099
|
this.core.handleMessage({
|
|
@@ -2664,11 +3101,12 @@ Workspace: <code>${workspace}</code>
|
|
|
2664
3101
|
threadId: String(threadId),
|
|
2665
3102
|
userId: String(ctx.from.id),
|
|
2666
3103
|
text: ctx.message.text
|
|
2667
|
-
}).catch((err) =>
|
|
3104
|
+
}).catch((err) => log10.error({ err }, "handleMessage error"));
|
|
2668
3105
|
});
|
|
2669
3106
|
}
|
|
2670
3107
|
// --- ChannelAdapter implementations ---
|
|
2671
3108
|
async sendMessage(sessionId, content) {
|
|
3109
|
+
if (this.assistantInitializing && sessionId === this.assistantSession?.id) return;
|
|
2672
3110
|
const session = this.core.sessionManager.getSession(
|
|
2673
3111
|
sessionId
|
|
2674
3112
|
);
|
|
@@ -2676,11 +3114,15 @@ Workspace: <code>${workspace}</code>
|
|
|
2676
3114
|
const threadId = Number(session.threadId);
|
|
2677
3115
|
switch (content.type) {
|
|
2678
3116
|
case "thought": {
|
|
3117
|
+
const tracker = this.getOrCreateTracker(sessionId, threadId);
|
|
3118
|
+
await tracker.onThought();
|
|
2679
3119
|
break;
|
|
2680
3120
|
}
|
|
2681
3121
|
case "text": {
|
|
2682
3122
|
let draft = this.sessionDrafts.get(sessionId);
|
|
2683
3123
|
if (!draft) {
|
|
3124
|
+
const tracker = this.getOrCreateTracker(sessionId, threadId);
|
|
3125
|
+
await tracker.onTextStart();
|
|
2684
3126
|
draft = new MessageDraft(
|
|
2685
3127
|
this.bot,
|
|
2686
3128
|
this.telegramConfig.chatId,
|
|
@@ -2698,6 +3140,8 @@ Workspace: <code>${workspace}</code>
|
|
|
2698
3140
|
break;
|
|
2699
3141
|
}
|
|
2700
3142
|
case "tool_call": {
|
|
3143
|
+
const tracker = this.getOrCreateTracker(sessionId, threadId);
|
|
3144
|
+
await tracker.onToolCall();
|
|
2701
3145
|
await this.finalizeDraft(sessionId);
|
|
2702
3146
|
const meta = content.metadata;
|
|
2703
3147
|
if (!this.toolCallMessages.has(sessionId)) {
|
|
@@ -2737,7 +3181,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2737
3181
|
if (toolState) {
|
|
2738
3182
|
if (meta.viewerLinks) {
|
|
2739
3183
|
toolState.viewerLinks = meta.viewerLinks;
|
|
2740
|
-
|
|
3184
|
+
log10.debug({ toolId: meta.id, viewerLinks: meta.viewerLinks }, "Accumulated viewerLinks");
|
|
2741
3185
|
}
|
|
2742
3186
|
const viewerFilePath = content.metadata?.viewerFilePath;
|
|
2743
3187
|
if (viewerFilePath) toolState.viewerFilePath = viewerFilePath;
|
|
@@ -2746,7 +3190,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2746
3190
|
const isTerminal = meta.status === "completed" || meta.status === "failed";
|
|
2747
3191
|
if (!isTerminal && !meta.viewerLinks) break;
|
|
2748
3192
|
await toolState.ready;
|
|
2749
|
-
|
|
3193
|
+
log10.debug(
|
|
2750
3194
|
{ toolId: meta.id, status: meta.status, hasViewerLinks: !!toolState.viewerLinks, viewerLinks: toolState.viewerLinks, name: toolState.name, msgId: toolState.msgId },
|
|
2751
3195
|
"Tool completed, preparing edit"
|
|
2752
3196
|
);
|
|
@@ -2768,7 +3212,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2768
3212
|
)
|
|
2769
3213
|
);
|
|
2770
3214
|
} catch (err) {
|
|
2771
|
-
|
|
3215
|
+
log10.warn(
|
|
2772
3216
|
{ err, msgId: toolState.msgId, textLen: formattedText.length, hasViewerLinks: !!merged.viewerLinks },
|
|
2773
3217
|
"Tool update edit failed"
|
|
2774
3218
|
);
|
|
@@ -2777,37 +3221,41 @@ Workspace: <code>${workspace}</code>
|
|
|
2777
3221
|
break;
|
|
2778
3222
|
}
|
|
2779
3223
|
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
|
-
)
|
|
3224
|
+
const meta = content.metadata;
|
|
3225
|
+
const tracker = this.getOrCreateTracker(sessionId, threadId);
|
|
3226
|
+
await tracker.onPlan(
|
|
3227
|
+
meta.entries.map((e) => ({
|
|
3228
|
+
content: e.content,
|
|
3229
|
+
status: e.status,
|
|
3230
|
+
priority: e.priority ?? "medium"
|
|
3231
|
+
}))
|
|
2793
3232
|
);
|
|
2794
3233
|
break;
|
|
2795
3234
|
}
|
|
2796
3235
|
case "usage": {
|
|
3236
|
+
const meta = content.metadata;
|
|
2797
3237
|
await this.finalizeDraft(sessionId);
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
3238
|
+
const tracker = this.getOrCreateTracker(sessionId, threadId);
|
|
3239
|
+
await tracker.sendUsage(meta);
|
|
3240
|
+
if (this.notificationTopicId && sessionId !== this.assistantSession?.id) {
|
|
3241
|
+
const sess = this.core.sessionManager.getSession(sessionId);
|
|
3242
|
+
const sessionName = sess?.name || "Session";
|
|
3243
|
+
const chatIdStr = String(this.telegramConfig.chatId);
|
|
3244
|
+
const numericId = chatIdStr.startsWith("-100") ? chatIdStr.slice(4) : chatIdStr.replace("-", "");
|
|
3245
|
+
const deepLink = `https://t.me/c/${numericId}/${threadId}`;
|
|
3246
|
+
const text = `\u2705 <b>${escapeHtml(sessionName)}</b>
|
|
3247
|
+
Task completed.
|
|
3248
|
+
|
|
3249
|
+
<a href="${deepLink}">\u2192 Go to topic</a>`;
|
|
3250
|
+
this.sendQueue.enqueue(
|
|
3251
|
+
() => this.bot.api.sendMessage(this.telegramConfig.chatId, text, {
|
|
3252
|
+
message_thread_id: this.notificationTopicId,
|
|
2806
3253
|
parse_mode: "HTML",
|
|
2807
|
-
disable_notification:
|
|
2808
|
-
}
|
|
2809
|
-
)
|
|
2810
|
-
|
|
3254
|
+
disable_notification: false
|
|
3255
|
+
})
|
|
3256
|
+
).catch(() => {
|
|
3257
|
+
});
|
|
3258
|
+
}
|
|
2811
3259
|
break;
|
|
2812
3260
|
}
|
|
2813
3261
|
case "session_end": {
|
|
@@ -2815,21 +3263,33 @@ Workspace: <code>${workspace}</code>
|
|
|
2815
3263
|
this.sessionDrafts.delete(sessionId);
|
|
2816
3264
|
this.toolCallMessages.delete(sessionId);
|
|
2817
3265
|
await this.cleanupSkillCommands(sessionId);
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
3266
|
+
const tracker = this.sessionTrackers.get(sessionId);
|
|
3267
|
+
if (tracker) {
|
|
3268
|
+
await tracker.onComplete();
|
|
3269
|
+
tracker.destroy();
|
|
3270
|
+
this.sessionTrackers.delete(sessionId);
|
|
3271
|
+
} else {
|
|
3272
|
+
await this.sendQueue.enqueue(
|
|
3273
|
+
() => this.bot.api.sendMessage(
|
|
3274
|
+
this.telegramConfig.chatId,
|
|
3275
|
+
`\u2705 <b>Done</b>`,
|
|
3276
|
+
{
|
|
3277
|
+
message_thread_id: threadId,
|
|
3278
|
+
parse_mode: "HTML",
|
|
3279
|
+
disable_notification: true
|
|
3280
|
+
}
|
|
3281
|
+
)
|
|
3282
|
+
);
|
|
3283
|
+
}
|
|
2829
3284
|
break;
|
|
2830
3285
|
}
|
|
2831
3286
|
case "error": {
|
|
2832
3287
|
await this.finalizeDraft(sessionId);
|
|
3288
|
+
const tracker = this.sessionTrackers.get(sessionId);
|
|
3289
|
+
if (tracker) {
|
|
3290
|
+
tracker.destroy();
|
|
3291
|
+
this.sessionTrackers.delete(sessionId);
|
|
3292
|
+
}
|
|
2833
3293
|
await this.sendQueue.enqueue(
|
|
2834
3294
|
() => this.bot.api.sendMessage(
|
|
2835
3295
|
this.telegramConfig.chatId,
|
|
@@ -2846,17 +3306,27 @@ Workspace: <code>${workspace}</code>
|
|
|
2846
3306
|
}
|
|
2847
3307
|
}
|
|
2848
3308
|
async sendPermissionRequest(sessionId, request) {
|
|
2849
|
-
|
|
3309
|
+
log10.info({ sessionId, requestId: request.id }, "Permission request sent");
|
|
2850
3310
|
const session = this.core.sessionManager.getSession(
|
|
2851
3311
|
sessionId
|
|
2852
3312
|
);
|
|
2853
3313
|
if (!session) return;
|
|
3314
|
+
if (session.dangerousMode) {
|
|
3315
|
+
const allowOption = request.options.find((o) => o.isAllow);
|
|
3316
|
+
if (allowOption && session.pendingPermission?.requestId === request.id) {
|
|
3317
|
+
log10.info({ sessionId, requestId: request.id, optionId: allowOption.id }, "Dangerous mode: auto-approving permission");
|
|
3318
|
+
session.pendingPermission.resolve(allowOption.id);
|
|
3319
|
+
session.pendingPermission = void 0;
|
|
3320
|
+
}
|
|
3321
|
+
return;
|
|
3322
|
+
}
|
|
2854
3323
|
await this.sendQueue.enqueue(
|
|
2855
3324
|
() => this.permissionHandler.sendPermissionRequest(session, request)
|
|
2856
3325
|
);
|
|
2857
3326
|
}
|
|
2858
3327
|
async sendNotification(notification) {
|
|
2859
|
-
|
|
3328
|
+
if (notification.sessionId === this.assistantSession?.id) return;
|
|
3329
|
+
log10.info(
|
|
2860
3330
|
{ sessionId: notification.sessionId, type: notification.type },
|
|
2861
3331
|
"Notification sent"
|
|
2862
3332
|
);
|
|
@@ -2870,10 +3340,18 @@ Workspace: <code>${workspace}</code>
|
|
|
2870
3340
|
let text = `${emoji[notification.type] || "\u2139\uFE0F"} <b>${escapeHtml(notification.sessionName || "New session")}</b>
|
|
2871
3341
|
`;
|
|
2872
3342
|
text += escapeHtml(notification.summary);
|
|
2873
|
-
|
|
3343
|
+
const deepLink = notification.deepLink ?? (() => {
|
|
3344
|
+
const session = this.core.sessionManager.getSession(notification.sessionId);
|
|
3345
|
+
const threadId = session?.threadId;
|
|
3346
|
+
if (!threadId) return void 0;
|
|
3347
|
+
const chatIdStr = String(this.telegramConfig.chatId);
|
|
3348
|
+
const numericId = chatIdStr.startsWith("-100") ? chatIdStr.slice(4) : chatIdStr.replace("-", "");
|
|
3349
|
+
return `https://t.me/c/${numericId}/${threadId}`;
|
|
3350
|
+
})();
|
|
3351
|
+
if (deepLink) {
|
|
2874
3352
|
text += `
|
|
2875
3353
|
|
|
2876
|
-
<a href="${
|
|
3354
|
+
<a href="${deepLink}">\u2192 Go to topic</a>`;
|
|
2877
3355
|
}
|
|
2878
3356
|
await this.sendQueue.enqueue(
|
|
2879
3357
|
() => this.bot.api.sendMessage(this.telegramConfig.chatId, text, {
|
|
@@ -2884,7 +3362,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2884
3362
|
);
|
|
2885
3363
|
}
|
|
2886
3364
|
async createSessionThread(sessionId, name) {
|
|
2887
|
-
|
|
3365
|
+
log10.info({ sessionId, name }, "Session topic created");
|
|
2888
3366
|
return String(
|
|
2889
3367
|
await createSessionTopic(this.bot, this.telegramConfig.chatId, name)
|
|
2890
3368
|
);
|
|
@@ -2906,6 +3384,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2906
3384
|
);
|
|
2907
3385
|
}
|
|
2908
3386
|
async sendSkillCommands(sessionId, commands) {
|
|
3387
|
+
if (sessionId === this.assistantSession?.id) return;
|
|
2909
3388
|
const session = this.core.sessionManager.getSession(
|
|
2910
3389
|
sessionId
|
|
2911
3390
|
);
|
|
@@ -2968,7 +3447,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2968
3447
|
}
|
|
2969
3448
|
);
|
|
2970
3449
|
} catch (err) {
|
|
2971
|
-
|
|
3450
|
+
log10.error({ err, sessionId }, "Failed to send skill commands");
|
|
2972
3451
|
}
|
|
2973
3452
|
await this.updateCommandAutocomplete(session.agentName, commands);
|
|
2974
3453
|
}
|
|
@@ -3004,12 +3483,12 @@ Workspace: <code>${workspace}</code>
|
|
|
3004
3483
|
await this.bot.api.setMyCommands(all, {
|
|
3005
3484
|
scope: { type: "chat", chat_id: this.telegramConfig.chatId }
|
|
3006
3485
|
});
|
|
3007
|
-
|
|
3486
|
+
log10.info(
|
|
3008
3487
|
{ count: all.length, skills: validSkills.length },
|
|
3009
3488
|
"Updated command autocomplete"
|
|
3010
3489
|
);
|
|
3011
3490
|
} catch (err) {
|
|
3012
|
-
|
|
3491
|
+
log10.error(
|
|
3013
3492
|
{ err, commands: all },
|
|
3014
3493
|
"Failed to update command autocomplete"
|
|
3015
3494
|
);
|
|
@@ -3018,8 +3497,8 @@ Workspace: <code>${workspace}</code>
|
|
|
3018
3497
|
async finalizeDraft(sessionId) {
|
|
3019
3498
|
const draft = this.sessionDrafts.get(sessionId);
|
|
3020
3499
|
if (!draft) return;
|
|
3021
|
-
const finalMsgId = await draft.finalize();
|
|
3022
3500
|
this.sessionDrafts.delete(sessionId);
|
|
3501
|
+
const finalMsgId = await draft.finalize();
|
|
3023
3502
|
if (sessionId === this.assistantSession?.id) {
|
|
3024
3503
|
const fullText = this.sessionTextBuffers.get(sessionId);
|
|
3025
3504
|
this.sessionTextBuffers.delete(sessionId);
|
|
@@ -3058,4 +3537,4 @@ export {
|
|
|
3058
3537
|
ApiServer,
|
|
3059
3538
|
TelegramAdapter
|
|
3060
3539
|
};
|
|
3061
|
-
//# sourceMappingURL=chunk-
|
|
3540
|
+
//# sourceMappingURL=chunk-C6IFPAWN.js.map
|