@openacp/cli 0.2.29 → 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/{autostart-YBYXQA77.js → autostart-N4HIL6C3.js} +3 -3
- package/dist/{chunk-PQRVTUNH.js → chunk-5E6ZXCNN.js} +2 -2
- package/dist/{chunk-S6O7SM6A.js → chunk-7W5SOJPD.js} +2 -2
- package/dist/{chunk-6HORD4FS.js → chunk-C6IFPAWN.js} +823 -243
- package/dist/chunk-C6IFPAWN.js.map +1 -0
- package/dist/{chunk-4BN7NSKB.js → chunk-CA6FXPLH.js} +11 -10
- package/dist/chunk-CA6FXPLH.js.map +1 -0
- package/dist/{chunk-MNJDYDGH.js → chunk-JOSJGZGF.js} +19 -15
- package/dist/chunk-JOSJGZGF.js.map +1 -0
- package/dist/{chunk-CQMS5U7Z.js → chunk-LVSQQRCF.js} +3 -3
- package/dist/{chunk-QWUJIKTX.js → chunk-NS2L445T.js} +5 -5
- package/dist/chunk-NS2L445T.js.map +1 -0
- package/dist/{chunk-WXS6ONOD.js → chunk-RBDPCHGD.js} +2 -2
- package/dist/{chunk-FGXG3H3F.js → chunk-YXMRR2E3.js} +24 -4
- package/dist/chunk-YXMRR2E3.js.map +1 -0
- package/dist/cli.js +18 -18
- package/dist/{config-2XALNLAA.js → config-2CBRLF3R.js} +3 -3
- package/dist/config-editor-UN56HQCW.js +11 -0
- package/dist/{daemon-3E5OMLT3.js → daemon-UXC7PB4P.js} +4 -4
- package/dist/index.d.ts +10 -1
- package/dist/index.js +8 -8
- package/dist/install-cloudflared-LMM7MFQX.js +8 -0
- package/dist/{main-XAUS3VZW.js → main-OVEJEUX5.js} +13 -13
- package/dist/{setup-FTNJACSC.js → setup-UKWBLJIT.js} +4 -4
- package/dist/{tunnel-service-YQ4RG652.js → tunnel-service-4GISQZNP.js} +3 -3
- package/package.json +2 -2
- package/dist/chunk-4BN7NSKB.js.map +0 -1
- package/dist/chunk-6HORD4FS.js.map +0 -1
- package/dist/chunk-FGXG3H3F.js.map +0 -1
- package/dist/chunk-MNJDYDGH.js.map +0 -1
- package/dist/chunk-QWUJIKTX.js.map +0 -1
- package/dist/config-editor-56B6YU7B.js +0 -11
- package/dist/install-cloudflared-57NRTI4E.js +0 -8
- /package/dist/{autostart-YBYXQA77.js.map → autostart-N4HIL6C3.js.map} +0 -0
- /package/dist/{chunk-PQRVTUNH.js.map → chunk-5E6ZXCNN.js.map} +0 -0
- /package/dist/{chunk-S6O7SM6A.js.map → chunk-7W5SOJPD.js.map} +0 -0
- /package/dist/{chunk-CQMS5U7Z.js.map → chunk-LVSQQRCF.js.map} +0 -0
- /package/dist/{chunk-WXS6ONOD.js.map → chunk-RBDPCHGD.js.map} +0 -0
- /package/dist/{config-2XALNLAA.js.map → config-2CBRLF3R.js.map} +0 -0
- /package/dist/{config-editor-56B6YU7B.js.map → config-editor-UN56HQCW.js.map} +0 -0
- /package/dist/{daemon-3E5OMLT3.js.map → daemon-UXC7PB4P.js.map} +0 -0
- /package/dist/{install-cloudflared-57NRTI4E.js.map → install-cloudflared-LMM7MFQX.js.map} +0 -0
- /package/dist/{main-XAUS3VZW.js.map → main-OVEJEUX5.js.map} +0 -0
- /package/dist/{setup-FTNJACSC.js.map → setup-UKWBLJIT.js.map} +0 -0
- /package/dist/{tunnel-service-YQ4RG652.js.map → tunnel-service-4GISQZNP.js.map} +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createChildLogger,
|
|
3
3
|
createSessionLogger
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-JOSJGZGF.js";
|
|
5
5
|
|
|
6
6
|
// src/core/streams.ts
|
|
7
7
|
function nodeToWebWritable(nodeStream) {
|
|
@@ -304,6 +304,8 @@ ${stderr}`
|
|
|
304
304
|
event = {
|
|
305
305
|
type: "tool_update",
|
|
306
306
|
id: update.toolCallId,
|
|
307
|
+
name: update.title ?? void 0,
|
|
308
|
+
kind: update.kind ?? void 0,
|
|
307
309
|
status: update.status ?? "pending",
|
|
308
310
|
content: update.content ?? void 0,
|
|
309
311
|
rawInput: update.rawInput ?? void 0,
|
|
@@ -521,6 +523,7 @@ var Session = class {
|
|
|
521
523
|
adapter;
|
|
522
524
|
// Set by wireSessionEvents for renaming
|
|
523
525
|
pendingPermission;
|
|
526
|
+
dangerousMode = false;
|
|
524
527
|
log;
|
|
525
528
|
constructor(opts) {
|
|
526
529
|
this.id = opts.id || nanoid(12);
|
|
@@ -591,7 +594,8 @@ var Session = class {
|
|
|
591
594
|
async warmup() {
|
|
592
595
|
this.promptRunning = true;
|
|
593
596
|
const prevHandler = this.agentInstance.onSessionUpdate;
|
|
594
|
-
this.agentInstance.onSessionUpdate = () => {
|
|
597
|
+
this.agentInstance.onSessionUpdate = (event) => {
|
|
598
|
+
if (event.type === "commands_update") prevHandler(event);
|
|
595
599
|
};
|
|
596
600
|
try {
|
|
597
601
|
const start = Date.now();
|
|
@@ -664,6 +668,12 @@ var SessionManager = class {
|
|
|
664
668
|
}
|
|
665
669
|
return void 0;
|
|
666
670
|
}
|
|
671
|
+
getRecordByThread(channelId, threadId) {
|
|
672
|
+
return this.store?.findByPlatform(
|
|
673
|
+
channelId,
|
|
674
|
+
(p) => String(p.topicId) === threadId
|
|
675
|
+
);
|
|
676
|
+
}
|
|
667
677
|
registerSession(session) {
|
|
668
678
|
this.sessions.set(session.id, session);
|
|
669
679
|
}
|
|
@@ -691,15 +701,25 @@ var SessionManager = class {
|
|
|
691
701
|
await this.store.save({ ...record, status });
|
|
692
702
|
}
|
|
693
703
|
}
|
|
704
|
+
async updateSessionName(sessionId, name) {
|
|
705
|
+
if (!this.store) return;
|
|
706
|
+
const record = this.store.get(sessionId);
|
|
707
|
+
if (record) {
|
|
708
|
+
await this.store.save({ ...record, name });
|
|
709
|
+
}
|
|
710
|
+
}
|
|
711
|
+
getSessionRecord(sessionId) {
|
|
712
|
+
return this.store?.get(sessionId);
|
|
713
|
+
}
|
|
694
714
|
async cancelSession(sessionId) {
|
|
695
715
|
const session = this.sessions.get(sessionId);
|
|
696
716
|
if (session) {
|
|
697
717
|
await session.cancel();
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
}
|
|
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" });
|
|
703
723
|
}
|
|
704
724
|
}
|
|
705
725
|
}
|
|
@@ -871,17 +891,21 @@ function extractFileInfo(name, kind, content, rawInput, meta) {
|
|
|
871
891
|
let info = null;
|
|
872
892
|
if (meta) {
|
|
873
893
|
const m = meta;
|
|
874
|
-
const
|
|
894
|
+
const tr = m?.claudeCode?.toolResponse;
|
|
895
|
+
const file = tr?.file;
|
|
875
896
|
if (file?.filePath && file?.content) {
|
|
876
897
|
info = { filePath: file.filePath, content: file.content };
|
|
877
898
|
}
|
|
899
|
+
if (!info && tr?.filePath && tr?.content) {
|
|
900
|
+
info = { filePath: tr.filePath, content: tr.content };
|
|
901
|
+
}
|
|
878
902
|
}
|
|
879
903
|
if (!info && rawInput) {
|
|
880
904
|
const ri = rawInput;
|
|
881
905
|
const filePath = ri?.file_path || ri?.filePath || ri?.path;
|
|
882
906
|
if (typeof filePath === "string") {
|
|
883
907
|
const parsed = content ? parseContent(content) : null;
|
|
884
|
-
info = { filePath, content: parsed?.content || ri?.content };
|
|
908
|
+
info = { filePath, content: parsed?.content || ri?.content, oldContent: parsed?.oldContent };
|
|
885
909
|
}
|
|
886
910
|
}
|
|
887
911
|
if (!info && content) {
|
|
@@ -1028,9 +1052,9 @@ var OpenACPCore = class {
|
|
|
1028
1052
|
);
|
|
1029
1053
|
const adapter = this.adapters.get(message.channelId);
|
|
1030
1054
|
if (adapter) {
|
|
1031
|
-
await adapter.sendMessage(
|
|
1055
|
+
await adapter.sendMessage(message.threadId, {
|
|
1032
1056
|
type: "error",
|
|
1033
|
-
text:
|
|
1057
|
+
text: `\u26A0\uFE0F Session limit reached (${config.security.maxConcurrentSessions}). Please cancel existing sessions with /cancel before starting new ones.`
|
|
1034
1058
|
});
|
|
1035
1059
|
}
|
|
1036
1060
|
return;
|
|
@@ -1070,11 +1094,19 @@ var OpenACPCore = class {
|
|
|
1070
1094
|
channelId,
|
|
1071
1095
|
currentThreadId
|
|
1072
1096
|
);
|
|
1073
|
-
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;
|
|
1074
1106
|
return this.handleNewSession(
|
|
1075
1107
|
channelId,
|
|
1076
|
-
|
|
1077
|
-
|
|
1108
|
+
record.agentName,
|
|
1109
|
+
record.workingDir
|
|
1078
1110
|
);
|
|
1079
1111
|
}
|
|
1080
1112
|
// --- Lazy Resume ---
|
|
@@ -1144,6 +1176,7 @@ var OpenACPCore = class {
|
|
|
1144
1176
|
case "tool_call": {
|
|
1145
1177
|
const metadata = {
|
|
1146
1178
|
id: event.id,
|
|
1179
|
+
name: event.name,
|
|
1147
1180
|
kind: event.kind,
|
|
1148
1181
|
status: event.status,
|
|
1149
1182
|
content: event.content,
|
|
@@ -1155,6 +1188,8 @@ var OpenACPCore = class {
|
|
|
1155
1188
|
case "tool_update": {
|
|
1156
1189
|
const metadata = {
|
|
1157
1190
|
id: event.id,
|
|
1191
|
+
name: event.name,
|
|
1192
|
+
kind: event.kind,
|
|
1158
1193
|
status: event.status,
|
|
1159
1194
|
content: event.content
|
|
1160
1195
|
};
|
|
@@ -1538,6 +1573,7 @@ function extractContentText(content, depth = 0) {
|
|
|
1538
1573
|
if (c.type === "text" && typeof c.text === "string") return c.text;
|
|
1539
1574
|
if (typeof c.text === "string") return c.text;
|
|
1540
1575
|
if (typeof c.content === "string") return c.content;
|
|
1576
|
+
if (c.content && typeof c.content === "object") return extractContentText(c.content, depth + 1);
|
|
1541
1577
|
if (c.input) return extractContentText(c.input, depth + 1);
|
|
1542
1578
|
if (c.output) return extractContentText(c.output, depth + 1);
|
|
1543
1579
|
const keys = Object.keys(c).filter((k) => k !== "type");
|
|
@@ -1554,12 +1590,14 @@ function formatToolCall(tool) {
|
|
|
1554
1590
|
const si = STATUS_ICON[tool.status || ""] || "\u{1F527}";
|
|
1555
1591
|
const ki = KIND_ICON[tool.kind || ""] || "\u{1F6E0}\uFE0F";
|
|
1556
1592
|
let text = `${si} ${ki} <b>${escapeHtml(tool.name || "Tool")}</b>`;
|
|
1557
|
-
|
|
1558
|
-
if (
|
|
1559
|
-
|
|
1593
|
+
text += formatViewerLinks(tool.viewerLinks, tool.viewerFilePath);
|
|
1594
|
+
if (!tool.viewerLinks) {
|
|
1595
|
+
const details = extractContentText(tool.content);
|
|
1596
|
+
if (details) {
|
|
1597
|
+
text += `
|
|
1560
1598
|
<pre>${escapeHtml(truncateContent(details))}</pre>`;
|
|
1599
|
+
}
|
|
1561
1600
|
}
|
|
1562
|
-
text += formatViewerLinks(tool.viewerLinks, tool.viewerFilePath);
|
|
1563
1601
|
return text;
|
|
1564
1602
|
}
|
|
1565
1603
|
function formatToolUpdate(update) {
|
|
@@ -1567,12 +1605,14 @@ function formatToolUpdate(update) {
|
|
|
1567
1605
|
const ki = KIND_ICON[update.kind || ""] || "\u{1F6E0}\uFE0F";
|
|
1568
1606
|
const name = update.name || "Tool";
|
|
1569
1607
|
let text = `${si} ${ki} <b>${escapeHtml(name)}</b>`;
|
|
1570
|
-
|
|
1571
|
-
if (
|
|
1572
|
-
|
|
1608
|
+
text += formatViewerLinks(update.viewerLinks, update.viewerFilePath);
|
|
1609
|
+
if (!update.viewerLinks) {
|
|
1610
|
+
const details = extractContentText(update.content);
|
|
1611
|
+
if (details) {
|
|
1612
|
+
text += `
|
|
1573
1613
|
<pre>${escapeHtml(truncateContent(details))}</pre>`;
|
|
1614
|
+
}
|
|
1574
1615
|
}
|
|
1575
|
-
text += formatViewerLinks(update.viewerLinks, update.viewerFilePath);
|
|
1576
1616
|
return text;
|
|
1577
1617
|
}
|
|
1578
1618
|
function formatViewerLinks(links, filePath) {
|
|
@@ -1585,20 +1625,23 @@ function formatViewerLinks(links, filePath) {
|
|
|
1585
1625
|
\u{1F4DD} <a href="${escapeHtml(links.diff)}">View diff${fileName ? ` \u2014 ${escapeHtml(fileName)}` : ""}</a>`;
|
|
1586
1626
|
return text;
|
|
1587
1627
|
}
|
|
1588
|
-
function
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
);
|
|
1593
|
-
return
|
|
1594
|
-
${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);
|
|
1595
1634
|
}
|
|
1596
1635
|
function formatUsage(usage) {
|
|
1597
|
-
const
|
|
1598
|
-
if (
|
|
1599
|
-
if (
|
|
1600
|
-
|
|
1601
|
-
|
|
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}%`;
|
|
1602
1645
|
}
|
|
1603
1646
|
function splitMessage(text, maxLength = 4096) {
|
|
1604
1647
|
if (text.length <= maxLength) return [text];
|
|
@@ -1623,94 +1666,68 @@ function splitMessage(text, maxLength = 4096) {
|
|
|
1623
1666
|
}
|
|
1624
1667
|
|
|
1625
1668
|
// src/adapters/telegram/streaming.ts
|
|
1626
|
-
var
|
|
1669
|
+
var FLUSH_INTERVAL = 5e3;
|
|
1627
1670
|
var MessageDraft = class {
|
|
1628
|
-
|
|
1629
|
-
constructor(bot, chatId, threadId, throttleMs = 200, sendQueue) {
|
|
1671
|
+
constructor(bot, chatId, threadId, sendQueue, sessionId) {
|
|
1630
1672
|
this.bot = bot;
|
|
1631
1673
|
this.chatId = chatId;
|
|
1632
1674
|
this.threadId = threadId;
|
|
1633
1675
|
this.sendQueue = sendQueue;
|
|
1634
|
-
this.
|
|
1635
|
-
this.minInterval = throttleMs;
|
|
1676
|
+
this.sessionId = sessionId;
|
|
1636
1677
|
}
|
|
1637
|
-
draftId;
|
|
1638
1678
|
buffer = "";
|
|
1639
|
-
|
|
1679
|
+
messageId;
|
|
1680
|
+
firstFlushPending = false;
|
|
1640
1681
|
flushTimer;
|
|
1641
1682
|
flushPromise = Promise.resolve();
|
|
1642
|
-
|
|
1643
|
-
useFallback = false;
|
|
1644
|
-
messageId;
|
|
1683
|
+
lastSentBuffer = "";
|
|
1645
1684
|
append(text) {
|
|
1685
|
+
if (!text) return;
|
|
1646
1686
|
this.buffer += text;
|
|
1647
1687
|
this.scheduleFlush();
|
|
1648
1688
|
}
|
|
1649
1689
|
scheduleFlush() {
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1690
|
+
if (this.flushTimer) return;
|
|
1691
|
+
this.flushTimer = setTimeout(() => {
|
|
1692
|
+
this.flushTimer = void 0;
|
|
1653
1693
|
this.flushPromise = this.flushPromise.then(() => this.flush()).catch(() => {
|
|
1654
1694
|
});
|
|
1655
|
-
}
|
|
1656
|
-
this.flushTimer = setTimeout(() => {
|
|
1657
|
-
this.flushTimer = void 0;
|
|
1658
|
-
this.flushPromise = this.flushPromise.then(() => this.flush()).catch(() => {
|
|
1659
|
-
});
|
|
1660
|
-
}, this.minInterval - elapsed);
|
|
1661
|
-
}
|
|
1695
|
+
}, FLUSH_INTERVAL);
|
|
1662
1696
|
}
|
|
1663
1697
|
async flush() {
|
|
1664
1698
|
if (!this.buffer) return;
|
|
1665
|
-
this.
|
|
1699
|
+
if (this.firstFlushPending) return;
|
|
1666
1700
|
const html = markdownToTelegramHtml(this.buffer);
|
|
1667
1701
|
const truncated = html.length > 4096 ? html.slice(0, 4090) + "\n..." : html;
|
|
1668
1702
|
if (!truncated) return;
|
|
1669
|
-
if (this.
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
await this.bot.api.sendMessageDraft(this.chatId, this.draftId, truncated, {
|
|
1675
|
-
message_thread_id: this.threadId,
|
|
1676
|
-
parse_mode: "HTML"
|
|
1677
|
-
});
|
|
1678
|
-
} catch {
|
|
1679
|
-
this.useFallback = true;
|
|
1680
|
-
this.minInterval = 1e3;
|
|
1681
|
-
await this.flushFallback(truncated);
|
|
1682
|
-
}
|
|
1683
|
-
}
|
|
1684
|
-
async flushFallback(html) {
|
|
1685
|
-
const exec = this.sendQueue ? (fn) => this.sendQueue.enqueue(fn) : (fn) => fn();
|
|
1686
|
-
try {
|
|
1687
|
-
if (!this.messageId) {
|
|
1688
|
-
const msg = await exec(
|
|
1689
|
-
() => this.bot.api.sendMessage(this.chatId, html, {
|
|
1703
|
+
if (!this.messageId) {
|
|
1704
|
+
this.firstFlushPending = true;
|
|
1705
|
+
try {
|
|
1706
|
+
const result = await this.sendQueue.enqueue(
|
|
1707
|
+
() => this.bot.api.sendMessage(this.chatId, truncated, {
|
|
1690
1708
|
message_thread_id: this.threadId,
|
|
1691
1709
|
parse_mode: "HTML",
|
|
1692
1710
|
disable_notification: true
|
|
1693
|
-
})
|
|
1694
|
-
|
|
1695
|
-
this.messageId = msg.message_id;
|
|
1696
|
-
} else {
|
|
1697
|
-
await exec(
|
|
1698
|
-
() => this.bot.api.editMessageText(this.chatId, this.messageId, html, {
|
|
1699
|
-
parse_mode: "HTML"
|
|
1700
|
-
})
|
|
1711
|
+
}),
|
|
1712
|
+
{ type: "other" }
|
|
1701
1713
|
);
|
|
1714
|
+
if (result) {
|
|
1715
|
+
this.messageId = result.message_id;
|
|
1716
|
+
this.lastSentBuffer = this.buffer;
|
|
1717
|
+
}
|
|
1718
|
+
} catch {
|
|
1719
|
+
} finally {
|
|
1720
|
+
this.firstFlushPending = false;
|
|
1702
1721
|
}
|
|
1703
|
-
}
|
|
1722
|
+
} else {
|
|
1704
1723
|
try {
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
this.messageId = msg.message_id;
|
|
1713
|
-
}
|
|
1724
|
+
await this.sendQueue.enqueue(
|
|
1725
|
+
() => this.bot.api.editMessageText(this.chatId, this.messageId, truncated, {
|
|
1726
|
+
parse_mode: "HTML"
|
|
1727
|
+
}),
|
|
1728
|
+
{ type: "text", key: this.sessionId }
|
|
1729
|
+
);
|
|
1730
|
+
this.lastSentBuffer = this.buffer;
|
|
1714
1731
|
} catch {
|
|
1715
1732
|
}
|
|
1716
1733
|
}
|
|
@@ -1722,31 +1739,47 @@ var MessageDraft = class {
|
|
|
1722
1739
|
}
|
|
1723
1740
|
await this.flushPromise;
|
|
1724
1741
|
if (!this.buffer) return this.messageId;
|
|
1742
|
+
if (this.messageId && this.buffer === this.lastSentBuffer) {
|
|
1743
|
+
return this.messageId;
|
|
1744
|
+
}
|
|
1725
1745
|
const html = markdownToTelegramHtml(this.buffer);
|
|
1726
1746
|
const chunks = splitMessage(html);
|
|
1727
1747
|
try {
|
|
1728
1748
|
for (let i = 0; i < chunks.length; i++) {
|
|
1729
1749
|
const chunk = chunks[i];
|
|
1730
1750
|
if (i === 0 && this.messageId) {
|
|
1731
|
-
await this.
|
|
1732
|
-
|
|
1733
|
-
|
|
1751
|
+
await this.sendQueue.enqueue(
|
|
1752
|
+
() => this.bot.api.editMessageText(this.chatId, this.messageId, chunk, {
|
|
1753
|
+
parse_mode: "HTML"
|
|
1754
|
+
}),
|
|
1755
|
+
{ type: "other" }
|
|
1756
|
+
);
|
|
1734
1757
|
} else {
|
|
1735
|
-
const msg = await this.
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1758
|
+
const msg = await this.sendQueue.enqueue(
|
|
1759
|
+
() => this.bot.api.sendMessage(this.chatId, chunk, {
|
|
1760
|
+
message_thread_id: this.threadId,
|
|
1761
|
+
parse_mode: "HTML",
|
|
1762
|
+
disable_notification: true
|
|
1763
|
+
}),
|
|
1764
|
+
{ type: "other" }
|
|
1765
|
+
);
|
|
1766
|
+
if (msg) {
|
|
1767
|
+
this.messageId = msg.message_id;
|
|
1768
|
+
}
|
|
1741
1769
|
}
|
|
1742
1770
|
}
|
|
1743
1771
|
} catch {
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1772
|
+
if (this.buffer !== this.lastSentBuffer) {
|
|
1773
|
+
try {
|
|
1774
|
+
await this.sendQueue.enqueue(
|
|
1775
|
+
() => this.bot.api.sendMessage(this.chatId, this.buffer.slice(0, 4096), {
|
|
1776
|
+
message_thread_id: this.threadId,
|
|
1777
|
+
disable_notification: true
|
|
1778
|
+
}),
|
|
1779
|
+
{ type: "other" }
|
|
1780
|
+
);
|
|
1781
|
+
} catch {
|
|
1782
|
+
}
|
|
1750
1783
|
}
|
|
1751
1784
|
}
|
|
1752
1785
|
return this.messageId;
|
|
@@ -1799,6 +1832,8 @@ function setupCommands(bot, core, chatId, assistant) {
|
|
|
1799
1832
|
bot.command("agents", (ctx) => handleAgents(ctx, core));
|
|
1800
1833
|
bot.command("help", (ctx) => handleHelp(ctx));
|
|
1801
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));
|
|
1802
1837
|
}
|
|
1803
1838
|
function buildMenuKeyboard() {
|
|
1804
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");
|
|
@@ -1884,7 +1919,8 @@ async function handleNew(ctx, core, chatId, assistant) {
|
|
|
1884
1919
|
<b>Workspace:</b> <code>${escapeHtml(session.workingDirectory)}</code>`,
|
|
1885
1920
|
{
|
|
1886
1921
|
message_thread_id: threadId,
|
|
1887
|
-
parse_mode: "HTML"
|
|
1922
|
+
parse_mode: "HTML",
|
|
1923
|
+
reply_markup: buildDangerousModeKeyboard(session.id, false)
|
|
1888
1924
|
}
|
|
1889
1925
|
);
|
|
1890
1926
|
session.warmup().catch((err) => log6.error({ err }, "Warm-up error"));
|
|
@@ -1938,7 +1974,8 @@ async function handleNewChat(ctx, core, chatId) {
|
|
|
1938
1974
|
<b>Workspace:</b> <code>${escapeHtml(session.workingDirectory)}</code>`,
|
|
1939
1975
|
{
|
|
1940
1976
|
message_thread_id: newThreadId,
|
|
1941
|
-
parse_mode: "HTML"
|
|
1977
|
+
parse_mode: "HTML",
|
|
1978
|
+
reply_markup: buildDangerousModeKeyboard(session.id, false)
|
|
1942
1979
|
}
|
|
1943
1980
|
);
|
|
1944
1981
|
session.warmup().catch((err) => log6.error({ err }, "Warm-up error"));
|
|
@@ -1967,6 +2004,13 @@ async function handleCancel(ctx, core, assistant) {
|
|
|
1967
2004
|
log6.info({ sessionId: session.id }, "Cancel session command");
|
|
1968
2005
|
await session.cancel();
|
|
1969
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" });
|
|
1970
2014
|
}
|
|
1971
2015
|
}
|
|
1972
2016
|
async function handleStatus(ctx, core) {
|
|
@@ -1986,9 +2030,20 @@ async function handleStatus(ctx, core) {
|
|
|
1986
2030
|
{ parse_mode: "HTML" }
|
|
1987
2031
|
);
|
|
1988
2032
|
} else {
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
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
|
+
}
|
|
1992
2047
|
}
|
|
1993
2048
|
} else {
|
|
1994
2049
|
const sessions = core.sessionManager.listSessions("telegram");
|
|
@@ -2033,6 +2088,81 @@ Or just chat in the \u{1F916} Assistant topic for help!`,
|
|
|
2033
2088
|
{ parse_mode: "HTML" }
|
|
2034
2089
|
);
|
|
2035
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
|
+
}
|
|
2036
2166
|
function botFromCtx(ctx) {
|
|
2037
2167
|
return { api: ctx.api };
|
|
2038
2168
|
}
|
|
@@ -2114,7 +2244,9 @@ var STATIC_COMMANDS = [
|
|
|
2114
2244
|
{ command: "status", description: "Show status" },
|
|
2115
2245
|
{ command: "agents", description: "List available agents" },
|
|
2116
2246
|
{ command: "help", description: "Help" },
|
|
2117
|
-
{ 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)" }
|
|
2118
2250
|
];
|
|
2119
2251
|
|
|
2120
2252
|
// src/adapters/telegram/permissions.ts
|
|
@@ -2155,7 +2287,7 @@ ${escapeHtml(request.description)}`,
|
|
|
2155
2287
|
}
|
|
2156
2288
|
);
|
|
2157
2289
|
const deepLink = buildDeepLink(this.chatId, msg.message_id);
|
|
2158
|
-
|
|
2290
|
+
void this.sendNotification({
|
|
2159
2291
|
sessionId: session.id,
|
|
2160
2292
|
sessionName: session.name,
|
|
2161
2293
|
type: "permission",
|
|
@@ -2199,8 +2331,10 @@ ${escapeHtml(request.description)}`,
|
|
|
2199
2331
|
};
|
|
2200
2332
|
|
|
2201
2333
|
// src/adapters/telegram/assistant.ts
|
|
2334
|
+
var log8 = createChildLogger({ module: "telegram-assistant" });
|
|
2202
2335
|
async function spawnAssistant(core, adapter, assistantTopicId) {
|
|
2203
2336
|
const config = core.configManager.get();
|
|
2337
|
+
log8.info({ agent: config.defaultAgent }, "Creating assistant session...");
|
|
2204
2338
|
const session = await core.sessionManager.createSession(
|
|
2205
2339
|
"telegram",
|
|
2206
2340
|
config.defaultAgent,
|
|
@@ -2208,10 +2342,16 @@ async function spawnAssistant(core, adapter, assistantTopicId) {
|
|
|
2208
2342
|
core.agentManager
|
|
2209
2343
|
);
|
|
2210
2344
|
session.threadId = String(assistantTopicId);
|
|
2211
|
-
|
|
2212
|
-
|
|
2345
|
+
session.name = "Assistant";
|
|
2346
|
+
log8.info({ sessionId: session.id }, "Assistant agent spawned");
|
|
2213
2347
|
core.wireSessionEvents(session, adapter);
|
|
2214
|
-
|
|
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 };
|
|
2215
2355
|
}
|
|
2216
2356
|
function buildAssistantSystemPrompt(config) {
|
|
2217
2357
|
const agentNames = Object.keys(config.agents).join(", ");
|
|
@@ -2246,36 +2386,342 @@ function redirectToAssistant(chatId, assistantTopicId) {
|
|
|
2246
2386
|
return `\u{1F4AC} Please use the <a href="${link}">\u{1F916} Assistant</a> topic to chat with OpenACP.`;
|
|
2247
2387
|
}
|
|
2248
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
|
+
|
|
2249
2662
|
// src/adapters/telegram/send-queue.ts
|
|
2250
2663
|
var TelegramSendQueue = class {
|
|
2251
|
-
|
|
2664
|
+
items = [];
|
|
2665
|
+
processing = false;
|
|
2252
2666
|
lastExec = 0;
|
|
2253
2667
|
minInterval;
|
|
2254
|
-
constructor(minInterval =
|
|
2668
|
+
constructor(minInterval = 3e3) {
|
|
2255
2669
|
this.minInterval = minInterval;
|
|
2256
2670
|
}
|
|
2257
|
-
enqueue(fn) {
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
resolve(result);
|
|
2272
|
-
} catch (err) {
|
|
2273
|
-
reject(err);
|
|
2274
|
-
} finally {
|
|
2275
|
-
this.lastExec = Date.now();
|
|
2671
|
+
enqueue(fn, opts) {
|
|
2672
|
+
const type = opts?.type ?? "other";
|
|
2673
|
+
const key = opts?.key;
|
|
2674
|
+
return new Promise((resolve, reject) => {
|
|
2675
|
+
if (type === "text" && key) {
|
|
2676
|
+
const idx = this.items.findIndex(
|
|
2677
|
+
(item) => item.type === "text" && item.key === key
|
|
2678
|
+
);
|
|
2679
|
+
if (idx !== -1) {
|
|
2680
|
+
this.items[idx].resolve(void 0);
|
|
2681
|
+
this.items[idx] = { fn, type, key, resolve, reject };
|
|
2682
|
+
this.scheduleProcess();
|
|
2683
|
+
return;
|
|
2684
|
+
}
|
|
2276
2685
|
}
|
|
2686
|
+
this.items.push({ fn, type, key, resolve, reject });
|
|
2687
|
+
this.scheduleProcess();
|
|
2277
2688
|
});
|
|
2278
|
-
|
|
2689
|
+
}
|
|
2690
|
+
onRateLimited() {
|
|
2691
|
+
const remaining = [];
|
|
2692
|
+
for (const item of this.items) {
|
|
2693
|
+
if (item.type === "text") {
|
|
2694
|
+
item.resolve(void 0);
|
|
2695
|
+
} else {
|
|
2696
|
+
remaining.push(item);
|
|
2697
|
+
}
|
|
2698
|
+
}
|
|
2699
|
+
this.items = remaining;
|
|
2700
|
+
}
|
|
2701
|
+
scheduleProcess() {
|
|
2702
|
+
if (this.processing) return;
|
|
2703
|
+
if (this.items.length === 0) return;
|
|
2704
|
+
const elapsed = Date.now() - this.lastExec;
|
|
2705
|
+
const delay = Math.max(0, this.minInterval - elapsed);
|
|
2706
|
+
this.processing = true;
|
|
2707
|
+
setTimeout(() => void this.processNext(), delay);
|
|
2708
|
+
}
|
|
2709
|
+
async processNext() {
|
|
2710
|
+
const item = this.items.shift();
|
|
2711
|
+
if (!item) {
|
|
2712
|
+
this.processing = false;
|
|
2713
|
+
return;
|
|
2714
|
+
}
|
|
2715
|
+
try {
|
|
2716
|
+
const result = await item.fn();
|
|
2717
|
+
item.resolve(result);
|
|
2718
|
+
} catch (err) {
|
|
2719
|
+
item.reject(err);
|
|
2720
|
+
} finally {
|
|
2721
|
+
this.lastExec = Date.now();
|
|
2722
|
+
this.processing = false;
|
|
2723
|
+
this.scheduleProcess();
|
|
2724
|
+
}
|
|
2279
2725
|
}
|
|
2280
2726
|
};
|
|
2281
2727
|
|
|
@@ -2284,8 +2730,8 @@ import { nanoid as nanoid4 } from "nanoid";
|
|
|
2284
2730
|
import { InlineKeyboard as InlineKeyboard3 } from "grammy";
|
|
2285
2731
|
var CMD_NEW_RE = /\/new(?:\s+([^\s\u0080-\uFFFF]+)(?:\s+([^\s\u0080-\uFFFF]+))?)?/;
|
|
2286
2732
|
var CMD_CANCEL_RE = /\/cancel\b/;
|
|
2287
|
-
var KW_NEW_RE = /(?:
|
|
2288
|
-
var KW_CANCEL_RE = /(?:
|
|
2733
|
+
var KW_NEW_RE = /(?:create|new)\s+session/i;
|
|
2734
|
+
var KW_CANCEL_RE = /(?:cancel|stop)\s+session/i;
|
|
2289
2735
|
function detectAction(text) {
|
|
2290
2736
|
if (!text) return null;
|
|
2291
2737
|
const cancelCmd = CMD_CANCEL_RE.exec(text);
|
|
@@ -2330,11 +2776,11 @@ function removeAction(id) {
|
|
|
2330
2776
|
function buildActionKeyboard(actionId, action) {
|
|
2331
2777
|
const keyboard = new InlineKeyboard3();
|
|
2332
2778
|
if (action.action === "new_session") {
|
|
2333
|
-
keyboard.text("\u2705
|
|
2334
|
-
keyboard.text("\u274C
|
|
2779
|
+
keyboard.text("\u2705 Create session", `a:${actionId}`);
|
|
2780
|
+
keyboard.text("\u274C Cancel", `a:dismiss:${actionId}`);
|
|
2335
2781
|
} else {
|
|
2336
|
-
keyboard.text("\u26D4
|
|
2337
|
-
keyboard.text("\u274C
|
|
2782
|
+
keyboard.text("\u26D4 Cancel session", `a:${actionId}`);
|
|
2783
|
+
keyboard.text("\u274C No", `a:dismiss:${actionId}`);
|
|
2338
2784
|
}
|
|
2339
2785
|
return keyboard;
|
|
2340
2786
|
}
|
|
@@ -2348,19 +2794,19 @@ function setupActionCallbacks(bot, core, chatId, getAssistantSessionId) {
|
|
|
2348
2794
|
});
|
|
2349
2795
|
} catch {
|
|
2350
2796
|
}
|
|
2351
|
-
await ctx.answerCallbackQuery({ text: "
|
|
2797
|
+
await ctx.answerCallbackQuery({ text: "Dismissed" });
|
|
2352
2798
|
});
|
|
2353
2799
|
bot.callbackQuery(/^a:(?!dismiss)/, async (ctx) => {
|
|
2354
2800
|
const actionId = ctx.callbackQuery.data.replace("a:", "");
|
|
2355
2801
|
const action = getAction(actionId);
|
|
2356
2802
|
if (!action) {
|
|
2357
|
-
await ctx.answerCallbackQuery({ text: "Action
|
|
2803
|
+
await ctx.answerCallbackQuery({ text: "Action expired" });
|
|
2358
2804
|
return;
|
|
2359
2805
|
}
|
|
2360
2806
|
removeAction(actionId);
|
|
2361
2807
|
try {
|
|
2362
2808
|
if (action.action === "new_session") {
|
|
2363
|
-
await ctx.answerCallbackQuery({ text: "\u23F3
|
|
2809
|
+
await ctx.answerCallbackQuery({ text: "\u23F3 Creating session..." });
|
|
2364
2810
|
const { threadId } = await executeNewSession(
|
|
2365
2811
|
bot,
|
|
2366
2812
|
core,
|
|
@@ -2386,13 +2832,13 @@ function setupActionCallbacks(bot, core, chatId, getAssistantSessionId) {
|
|
|
2386
2832
|
const assistantId = getAssistantSessionId();
|
|
2387
2833
|
const cancelled = await executeCancelSession(core, assistantId);
|
|
2388
2834
|
if (cancelled) {
|
|
2389
|
-
await ctx.answerCallbackQuery({ text: "\u26D4 Session
|
|
2835
|
+
await ctx.answerCallbackQuery({ text: "\u26D4 Session cancelled" });
|
|
2390
2836
|
const originalText = ctx.callbackQuery.message?.text ?? "";
|
|
2391
2837
|
try {
|
|
2392
2838
|
await ctx.editMessageText(
|
|
2393
2839
|
originalText + `
|
|
2394
2840
|
|
|
2395
|
-
\u26D4 Session "${cancelled.name ?? cancelled.id}"
|
|
2841
|
+
\u26D4 Session "${cancelled.name ?? cancelled.id}" cancelled`,
|
|
2396
2842
|
{ parse_mode: "HTML" }
|
|
2397
2843
|
);
|
|
2398
2844
|
} catch {
|
|
@@ -2402,7 +2848,7 @@ function setupActionCallbacks(bot, core, chatId, getAssistantSessionId) {
|
|
|
2402
2848
|
}
|
|
2403
2849
|
} else {
|
|
2404
2850
|
await ctx.answerCallbackQuery({
|
|
2405
|
-
text: "
|
|
2851
|
+
text: "No active session"
|
|
2406
2852
|
});
|
|
2407
2853
|
try {
|
|
2408
2854
|
await ctx.editMessageReplyMarkup({
|
|
@@ -2413,7 +2859,7 @@ function setupActionCallbacks(bot, core, chatId, getAssistantSessionId) {
|
|
|
2413
2859
|
}
|
|
2414
2860
|
}
|
|
2415
2861
|
} catch {
|
|
2416
|
-
await ctx.answerCallbackQuery({ text: "\u274C
|
|
2862
|
+
await ctx.answerCallbackQuery({ text: "\u274C Error, try again later" });
|
|
2417
2863
|
try {
|
|
2418
2864
|
await ctx.editMessageReplyMarkup({
|
|
2419
2865
|
reply_markup: { inline_keyboard: [] }
|
|
@@ -2425,7 +2871,7 @@ function setupActionCallbacks(bot, core, chatId, getAssistantSessionId) {
|
|
|
2425
2871
|
}
|
|
2426
2872
|
|
|
2427
2873
|
// src/adapters/telegram/adapter.ts
|
|
2428
|
-
var
|
|
2874
|
+
var log10 = createChildLogger({ module: "telegram" });
|
|
2429
2875
|
function patchedFetch(input, init) {
|
|
2430
2876
|
if (init?.signal && !(init.signal instanceof AbortSignal)) {
|
|
2431
2877
|
const nativeController = new AbortController();
|
|
@@ -2448,11 +2894,26 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2448
2894
|
// sessionId → (toolCallId → state)
|
|
2449
2895
|
permissionHandler;
|
|
2450
2896
|
assistantSession = null;
|
|
2897
|
+
assistantInitializing = false;
|
|
2451
2898
|
notificationTopicId;
|
|
2452
2899
|
assistantTopicId;
|
|
2453
2900
|
skillMessages = /* @__PURE__ */ new Map();
|
|
2454
2901
|
// sessionId → pinned messageId
|
|
2455
|
-
sendQueue = new TelegramSendQueue();
|
|
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
|
+
}
|
|
2456
2917
|
constructor(core, config) {
|
|
2457
2918
|
super(core, config);
|
|
2458
2919
|
this.telegramConfig = config;
|
|
@@ -2461,7 +2922,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2461
2922
|
this.bot = new Bot(this.telegramConfig.botToken, { client: { fetch: patchedFetch } });
|
|
2462
2923
|
this.bot.catch((err) => {
|
|
2463
2924
|
const rootCause = err.error instanceof Error ? err.error : err;
|
|
2464
|
-
|
|
2925
|
+
log10.error({ err: rootCause }, "Telegram bot error");
|
|
2465
2926
|
});
|
|
2466
2927
|
this.bot.api.config.use(async (prev, method, payload, signal) => {
|
|
2467
2928
|
const maxRetries = 3;
|
|
@@ -2471,7 +2932,11 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2471
2932
|
return result;
|
|
2472
2933
|
}
|
|
2473
2934
|
const retryAfter = (result.parameters?.retry_after ?? 5) + 1;
|
|
2474
|
-
|
|
2935
|
+
const rateLimitedMethods = ["sendMessage", "editMessageText", "editMessageReplyMarkup"];
|
|
2936
|
+
if (rateLimitedMethods.includes(method)) {
|
|
2937
|
+
this.sendQueue.onRateLimited();
|
|
2938
|
+
}
|
|
2939
|
+
log10.warn(
|
|
2475
2940
|
{ method, retryAfter, attempt: attempt + 1 },
|
|
2476
2941
|
"Rate limited by Telegram, retrying"
|
|
2477
2942
|
);
|
|
@@ -2516,6 +2981,7 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2516
2981
|
(notification) => this.sendNotification(notification)
|
|
2517
2982
|
);
|
|
2518
2983
|
setupSkillCallbacks(this.bot, this.core);
|
|
2984
|
+
setupDangerousModeCallbacks(this.bot, this.core);
|
|
2519
2985
|
setupActionCallbacks(
|
|
2520
2986
|
this.bot,
|
|
2521
2987
|
this.core,
|
|
@@ -2540,26 +3006,15 @@ var TelegramAdapter = class extends ChannelAdapter {
|
|
|
2540
3006
|
this.setupRoutes();
|
|
2541
3007
|
this.bot.start({
|
|
2542
3008
|
allowed_updates: ["message", "callback_query"],
|
|
2543
|
-
onStart: () =>
|
|
3009
|
+
onStart: () => log10.info(
|
|
2544
3010
|
{ chatId: this.telegramConfig.chatId },
|
|
2545
3011
|
"Telegram bot started"
|
|
2546
3012
|
)
|
|
2547
3013
|
});
|
|
2548
|
-
try {
|
|
2549
|
-
this.assistantSession = await spawnAssistant(
|
|
2550
|
-
this.core,
|
|
2551
|
-
this,
|
|
2552
|
-
this.assistantTopicId
|
|
2553
|
-
);
|
|
2554
|
-
} catch (err) {
|
|
2555
|
-
log8.error({ err }, "Failed to spawn assistant");
|
|
2556
|
-
}
|
|
2557
3014
|
try {
|
|
2558
3015
|
const config = this.core.configManager.get();
|
|
2559
3016
|
const agents = this.core.agentManager.getAvailableAgents();
|
|
2560
|
-
const agentList = agents.map(
|
|
2561
|
-
(a) => `${escapeHtml(a.name)}${a.name === config.defaultAgent ? " (default)" : ""}`
|
|
2562
|
-
).join(", ");
|
|
3017
|
+
const agentList = agents.map((a) => `${escapeHtml(a.name)}${a.name === config.defaultAgent ? " (default)" : ""}`).join(", ");
|
|
2563
3018
|
const workspace = escapeHtml(config.workspace.baseDir);
|
|
2564
3019
|
const welcomeText = `\u{1F44B} <b>OpenACP Assistant</b> is online.
|
|
2565
3020
|
|
|
@@ -2573,7 +3028,32 @@ Workspace: <code>${workspace}</code>
|
|
|
2573
3028
|
reply_markup: buildMenuKeyboard()
|
|
2574
3029
|
});
|
|
2575
3030
|
} catch (err) {
|
|
2576
|
-
|
|
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
|
+
});
|
|
2577
3057
|
}
|
|
2578
3058
|
}
|
|
2579
3059
|
async stop() {
|
|
@@ -2581,7 +3061,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2581
3061
|
await this.assistantSession.destroy();
|
|
2582
3062
|
}
|
|
2583
3063
|
await this.bot.stop();
|
|
2584
|
-
|
|
3064
|
+
log10.info("Telegram bot stopped");
|
|
2585
3065
|
}
|
|
2586
3066
|
setupRoutes() {
|
|
2587
3067
|
this.bot.on("message:text", async (ctx) => {
|
|
@@ -2596,13 +3076,24 @@ Workspace: <code>${workspace}</code>
|
|
|
2596
3076
|
}
|
|
2597
3077
|
if (threadId === this.notificationTopicId) return;
|
|
2598
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
|
+
}
|
|
3083
|
+
await this.finalizeDraft(this.assistantSession.id);
|
|
2599
3084
|
ctx.replyWithChatAction("typing").catch(() => {
|
|
2600
3085
|
});
|
|
2601
3086
|
handleAssistantMessage(this.assistantSession, ctx.message.text).catch(
|
|
2602
|
-
(err) =>
|
|
3087
|
+
(err) => log10.error({ err }, "Assistant error")
|
|
2603
3088
|
);
|
|
2604
3089
|
return;
|
|
2605
3090
|
}
|
|
3091
|
+
const sessionId = this.core.sessionManager.getSessionByThread("telegram", String(threadId))?.id;
|
|
3092
|
+
if (sessionId) await this.finalizeDraft(sessionId);
|
|
3093
|
+
if (sessionId) {
|
|
3094
|
+
const tracker = this.sessionTrackers.get(sessionId);
|
|
3095
|
+
if (tracker) await tracker.onNewPrompt();
|
|
3096
|
+
}
|
|
2606
3097
|
ctx.replyWithChatAction("typing").catch(() => {
|
|
2607
3098
|
});
|
|
2608
3099
|
this.core.handleMessage({
|
|
@@ -2610,11 +3101,12 @@ Workspace: <code>${workspace}</code>
|
|
|
2610
3101
|
threadId: String(threadId),
|
|
2611
3102
|
userId: String(ctx.from.id),
|
|
2612
3103
|
text: ctx.message.text
|
|
2613
|
-
}).catch((err) =>
|
|
3104
|
+
}).catch((err) => log10.error({ err }, "handleMessage error"));
|
|
2614
3105
|
});
|
|
2615
3106
|
}
|
|
2616
3107
|
// --- ChannelAdapter implementations ---
|
|
2617
3108
|
async sendMessage(sessionId, content) {
|
|
3109
|
+
if (this.assistantInitializing && sessionId === this.assistantSession?.id) return;
|
|
2618
3110
|
const session = this.core.sessionManager.getSession(
|
|
2619
3111
|
sessionId
|
|
2620
3112
|
);
|
|
@@ -2622,17 +3114,21 @@ Workspace: <code>${workspace}</code>
|
|
|
2622
3114
|
const threadId = Number(session.threadId);
|
|
2623
3115
|
switch (content.type) {
|
|
2624
3116
|
case "thought": {
|
|
3117
|
+
const tracker = this.getOrCreateTracker(sessionId, threadId);
|
|
3118
|
+
await tracker.onThought();
|
|
2625
3119
|
break;
|
|
2626
3120
|
}
|
|
2627
3121
|
case "text": {
|
|
2628
3122
|
let draft = this.sessionDrafts.get(sessionId);
|
|
2629
3123
|
if (!draft) {
|
|
3124
|
+
const tracker = this.getOrCreateTracker(sessionId, threadId);
|
|
3125
|
+
await tracker.onTextStart();
|
|
2630
3126
|
draft = new MessageDraft(
|
|
2631
3127
|
this.bot,
|
|
2632
3128
|
this.telegramConfig.chatId,
|
|
2633
3129
|
threadId,
|
|
2634
|
-
this.
|
|
2635
|
-
|
|
3130
|
+
this.sendQueue,
|
|
3131
|
+
sessionId
|
|
2636
3132
|
);
|
|
2637
3133
|
this.sessionDrafts.set(sessionId, draft);
|
|
2638
3134
|
}
|
|
@@ -2644,8 +3140,25 @@ Workspace: <code>${workspace}</code>
|
|
|
2644
3140
|
break;
|
|
2645
3141
|
}
|
|
2646
3142
|
case "tool_call": {
|
|
3143
|
+
const tracker = this.getOrCreateTracker(sessionId, threadId);
|
|
3144
|
+
await tracker.onToolCall();
|
|
2647
3145
|
await this.finalizeDraft(sessionId);
|
|
2648
3146
|
const meta = content.metadata;
|
|
3147
|
+
if (!this.toolCallMessages.has(sessionId)) {
|
|
3148
|
+
this.toolCallMessages.set(sessionId, /* @__PURE__ */ new Map());
|
|
3149
|
+
}
|
|
3150
|
+
let resolveReady;
|
|
3151
|
+
const ready = new Promise((r) => {
|
|
3152
|
+
resolveReady = r;
|
|
3153
|
+
});
|
|
3154
|
+
this.toolCallMessages.get(sessionId).set(meta.id, {
|
|
3155
|
+
msgId: 0,
|
|
3156
|
+
name: meta.name,
|
|
3157
|
+
kind: meta.kind,
|
|
3158
|
+
viewerLinks: meta.viewerLinks,
|
|
3159
|
+
viewerFilePath: content.metadata?.viewerFilePath,
|
|
3160
|
+
ready
|
|
3161
|
+
});
|
|
2649
3162
|
const msg = await this.sendQueue.enqueue(
|
|
2650
3163
|
() => this.bot.api.sendMessage(
|
|
2651
3164
|
this.telegramConfig.chatId,
|
|
@@ -2657,79 +3170,92 @@ Workspace: <code>${workspace}</code>
|
|
|
2657
3170
|
}
|
|
2658
3171
|
)
|
|
2659
3172
|
);
|
|
2660
|
-
|
|
2661
|
-
|
|
2662
|
-
|
|
2663
|
-
this.toolCallMessages.get(sessionId).set(meta.id, {
|
|
2664
|
-
msgId: msg.message_id,
|
|
2665
|
-
name: meta.name,
|
|
2666
|
-
kind: meta.kind,
|
|
2667
|
-
viewerLinks: meta.viewerLinks,
|
|
2668
|
-
viewerFilePath: content.metadata?.viewerFilePath
|
|
2669
|
-
});
|
|
3173
|
+
const toolEntry = this.toolCallMessages.get(sessionId).get(meta.id);
|
|
3174
|
+
toolEntry.msgId = msg.message_id;
|
|
3175
|
+
resolveReady();
|
|
2670
3176
|
break;
|
|
2671
3177
|
}
|
|
2672
3178
|
case "tool_update": {
|
|
2673
3179
|
const meta = content.metadata;
|
|
2674
3180
|
const toolState = this.toolCallMessages.get(sessionId)?.get(meta.id);
|
|
2675
3181
|
if (toolState) {
|
|
2676
|
-
|
|
2677
|
-
|
|
2678
|
-
|
|
3182
|
+
if (meta.viewerLinks) {
|
|
3183
|
+
toolState.viewerLinks = meta.viewerLinks;
|
|
3184
|
+
log10.debug({ toolId: meta.id, viewerLinks: meta.viewerLinks }, "Accumulated viewerLinks");
|
|
3185
|
+
}
|
|
3186
|
+
const viewerFilePath = content.metadata?.viewerFilePath;
|
|
2679
3187
|
if (viewerFilePath) toolState.viewerFilePath = viewerFilePath;
|
|
3188
|
+
if (meta.name) toolState.name = meta.name;
|
|
3189
|
+
if (meta.kind) toolState.kind = meta.kind;
|
|
3190
|
+
const isTerminal = meta.status === "completed" || meta.status === "failed";
|
|
3191
|
+
if (!isTerminal && !meta.viewerLinks) break;
|
|
3192
|
+
await toolState.ready;
|
|
3193
|
+
log10.debug(
|
|
3194
|
+
{ toolId: meta.id, status: meta.status, hasViewerLinks: !!toolState.viewerLinks, viewerLinks: toolState.viewerLinks, name: toolState.name, msgId: toolState.msgId },
|
|
3195
|
+
"Tool completed, preparing edit"
|
|
3196
|
+
);
|
|
2680
3197
|
const merged = {
|
|
2681
3198
|
...meta,
|
|
2682
|
-
name:
|
|
2683
|
-
kind:
|
|
2684
|
-
viewerLinks,
|
|
2685
|
-
viewerFilePath
|
|
3199
|
+
name: toolState.name,
|
|
3200
|
+
kind: toolState.kind,
|
|
3201
|
+
viewerLinks: toolState.viewerLinks,
|
|
3202
|
+
viewerFilePath: toolState.viewerFilePath
|
|
2686
3203
|
};
|
|
3204
|
+
const formattedText = formatToolUpdate(merged);
|
|
2687
3205
|
try {
|
|
2688
3206
|
await this.sendQueue.enqueue(
|
|
2689
3207
|
() => this.bot.api.editMessageText(
|
|
2690
3208
|
this.telegramConfig.chatId,
|
|
2691
3209
|
toolState.msgId,
|
|
2692
|
-
|
|
3210
|
+
formattedText,
|
|
2693
3211
|
{ parse_mode: "HTML" }
|
|
2694
3212
|
)
|
|
2695
3213
|
);
|
|
2696
|
-
} catch {
|
|
3214
|
+
} catch (err) {
|
|
3215
|
+
log10.warn(
|
|
3216
|
+
{ err, msgId: toolState.msgId, textLen: formattedText.length, hasViewerLinks: !!merged.viewerLinks },
|
|
3217
|
+
"Tool update edit failed"
|
|
3218
|
+
);
|
|
2697
3219
|
}
|
|
2698
3220
|
}
|
|
2699
3221
|
break;
|
|
2700
3222
|
}
|
|
2701
3223
|
case "plan": {
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
message_thread_id: threadId,
|
|
2711
|
-
parse_mode: "HTML",
|
|
2712
|
-
disable_notification: true
|
|
2713
|
-
}
|
|
2714
|
-
)
|
|
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
|
+
}))
|
|
2715
3232
|
);
|
|
2716
3233
|
break;
|
|
2717
3234
|
}
|
|
2718
3235
|
case "usage": {
|
|
3236
|
+
const meta = content.metadata;
|
|
2719
3237
|
await this.finalizeDraft(sessionId);
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
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,
|
|
2728
3253
|
parse_mode: "HTML",
|
|
2729
|
-
disable_notification:
|
|
2730
|
-
}
|
|
2731
|
-
)
|
|
2732
|
-
|
|
3254
|
+
disable_notification: false
|
|
3255
|
+
})
|
|
3256
|
+
).catch(() => {
|
|
3257
|
+
});
|
|
3258
|
+
}
|
|
2733
3259
|
break;
|
|
2734
3260
|
}
|
|
2735
3261
|
case "session_end": {
|
|
@@ -2737,21 +3263,33 @@ Workspace: <code>${workspace}</code>
|
|
|
2737
3263
|
this.sessionDrafts.delete(sessionId);
|
|
2738
3264
|
this.toolCallMessages.delete(sessionId);
|
|
2739
3265
|
await this.cleanupSkillCommands(sessionId);
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
|
|
2749
|
-
|
|
2750
|
-
|
|
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
|
+
}
|
|
2751
3284
|
break;
|
|
2752
3285
|
}
|
|
2753
3286
|
case "error": {
|
|
2754
3287
|
await this.finalizeDraft(sessionId);
|
|
3288
|
+
const tracker = this.sessionTrackers.get(sessionId);
|
|
3289
|
+
if (tracker) {
|
|
3290
|
+
tracker.destroy();
|
|
3291
|
+
this.sessionTrackers.delete(sessionId);
|
|
3292
|
+
}
|
|
2755
3293
|
await this.sendQueue.enqueue(
|
|
2756
3294
|
() => this.bot.api.sendMessage(
|
|
2757
3295
|
this.telegramConfig.chatId,
|
|
@@ -2768,17 +3306,27 @@ Workspace: <code>${workspace}</code>
|
|
|
2768
3306
|
}
|
|
2769
3307
|
}
|
|
2770
3308
|
async sendPermissionRequest(sessionId, request) {
|
|
2771
|
-
|
|
3309
|
+
log10.info({ sessionId, requestId: request.id }, "Permission request sent");
|
|
2772
3310
|
const session = this.core.sessionManager.getSession(
|
|
2773
3311
|
sessionId
|
|
2774
3312
|
);
|
|
2775
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
|
+
}
|
|
2776
3323
|
await this.sendQueue.enqueue(
|
|
2777
3324
|
() => this.permissionHandler.sendPermissionRequest(session, request)
|
|
2778
3325
|
);
|
|
2779
3326
|
}
|
|
2780
3327
|
async sendNotification(notification) {
|
|
2781
|
-
|
|
3328
|
+
if (notification.sessionId === this.assistantSession?.id) return;
|
|
3329
|
+
log10.info(
|
|
2782
3330
|
{ sessionId: notification.sessionId, type: notification.type },
|
|
2783
3331
|
"Notification sent"
|
|
2784
3332
|
);
|
|
@@ -2789,13 +3337,21 @@ Workspace: <code>${workspace}</code>
|
|
|
2789
3337
|
permission: "\u{1F510}",
|
|
2790
3338
|
input_required: "\u{1F4AC}"
|
|
2791
3339
|
};
|
|
2792
|
-
let text = `${emoji[notification.type] || "\u2139\uFE0F"} <b>${escapeHtml(notification.sessionName ||
|
|
3340
|
+
let text = `${emoji[notification.type] || "\u2139\uFE0F"} <b>${escapeHtml(notification.sessionName || "New session")}</b>
|
|
2793
3341
|
`;
|
|
2794
3342
|
text += escapeHtml(notification.summary);
|
|
2795
|
-
|
|
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) {
|
|
2796
3352
|
text += `
|
|
2797
3353
|
|
|
2798
|
-
<a href="${
|
|
3354
|
+
<a href="${deepLink}">\u2192 Go to topic</a>`;
|
|
2799
3355
|
}
|
|
2800
3356
|
await this.sendQueue.enqueue(
|
|
2801
3357
|
() => this.bot.api.sendMessage(this.telegramConfig.chatId, text, {
|
|
@@ -2806,7 +3362,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2806
3362
|
);
|
|
2807
3363
|
}
|
|
2808
3364
|
async createSessionThread(sessionId, name) {
|
|
2809
|
-
|
|
3365
|
+
log10.info({ sessionId, name }, "Session topic created");
|
|
2810
3366
|
return String(
|
|
2811
3367
|
await createSessionTopic(this.bot, this.telegramConfig.chatId, name)
|
|
2812
3368
|
);
|
|
@@ -2822,14 +3378,26 @@ Workspace: <code>${workspace}</code>
|
|
|
2822
3378
|
Number(session.threadId),
|
|
2823
3379
|
newName
|
|
2824
3380
|
);
|
|
3381
|
+
await this.core.sessionManager.updateSessionName(
|
|
3382
|
+
sessionId,
|
|
3383
|
+
newName
|
|
3384
|
+
);
|
|
2825
3385
|
}
|
|
2826
3386
|
async sendSkillCommands(sessionId, commands) {
|
|
3387
|
+
if (sessionId === this.assistantSession?.id) return;
|
|
2827
3388
|
const session = this.core.sessionManager.getSession(
|
|
2828
3389
|
sessionId
|
|
2829
3390
|
);
|
|
2830
3391
|
if (!session) return;
|
|
2831
3392
|
const threadId = Number(session.threadId);
|
|
2832
3393
|
if (!threadId) return;
|
|
3394
|
+
if (!this.skillMessages.has(sessionId)) {
|
|
3395
|
+
const record = this.core.sessionManager.getSessionRecord(sessionId);
|
|
3396
|
+
const platform = record?.platform;
|
|
3397
|
+
if (platform?.skillMsgId) {
|
|
3398
|
+
this.skillMessages.set(sessionId, platform.skillMsgId);
|
|
3399
|
+
}
|
|
3400
|
+
}
|
|
2833
3401
|
if (commands.length === 0) {
|
|
2834
3402
|
await this.cleanupSkillCommands(sessionId);
|
|
2835
3403
|
return;
|
|
@@ -2864,6 +3432,13 @@ Workspace: <code>${workspace}</code>
|
|
|
2864
3432
|
)
|
|
2865
3433
|
);
|
|
2866
3434
|
this.skillMessages.set(sessionId, msg.message_id);
|
|
3435
|
+
const record = this.core.sessionManager.getSessionRecord(sessionId);
|
|
3436
|
+
if (record) {
|
|
3437
|
+
await this.core.sessionManager.updateSessionPlatform(
|
|
3438
|
+
sessionId,
|
|
3439
|
+
{ ...record.platform, skillMsgId: msg.message_id }
|
|
3440
|
+
);
|
|
3441
|
+
}
|
|
2867
3442
|
await this.bot.api.pinChatMessage(
|
|
2868
3443
|
this.telegramConfig.chatId,
|
|
2869
3444
|
msg.message_id,
|
|
@@ -2872,7 +3447,7 @@ Workspace: <code>${workspace}</code>
|
|
|
2872
3447
|
}
|
|
2873
3448
|
);
|
|
2874
3449
|
} catch (err) {
|
|
2875
|
-
|
|
3450
|
+
log10.error({ err, sessionId }, "Failed to send skill commands");
|
|
2876
3451
|
}
|
|
2877
3452
|
await this.updateCommandAutocomplete(session.agentName, commands);
|
|
2878
3453
|
}
|
|
@@ -2891,6 +3466,11 @@ Workspace: <code>${workspace}</code>
|
|
|
2891
3466
|
}
|
|
2892
3467
|
this.skillMessages.delete(sessionId);
|
|
2893
3468
|
clearSkillCallbacks(sessionId);
|
|
3469
|
+
const record = this.core.sessionManager.getSessionRecord(sessionId);
|
|
3470
|
+
if (record) {
|
|
3471
|
+
const { skillMsgId: _removed, ...rest } = record.platform;
|
|
3472
|
+
await this.core.sessionManager.updateSessionPlatform(sessionId, rest);
|
|
3473
|
+
}
|
|
2894
3474
|
}
|
|
2895
3475
|
async updateCommandAutocomplete(agentName, skillCommands) {
|
|
2896
3476
|
const prefix = `[${agentName}] `;
|
|
@@ -2903,12 +3483,12 @@ Workspace: <code>${workspace}</code>
|
|
|
2903
3483
|
await this.bot.api.setMyCommands(all, {
|
|
2904
3484
|
scope: { type: "chat", chat_id: this.telegramConfig.chatId }
|
|
2905
3485
|
});
|
|
2906
|
-
|
|
3486
|
+
log10.info(
|
|
2907
3487
|
{ count: all.length, skills: validSkills.length },
|
|
2908
3488
|
"Updated command autocomplete"
|
|
2909
3489
|
);
|
|
2910
3490
|
} catch (err) {
|
|
2911
|
-
|
|
3491
|
+
log10.error(
|
|
2912
3492
|
{ err, commands: all },
|
|
2913
3493
|
"Failed to update command autocomplete"
|
|
2914
3494
|
);
|
|
@@ -2917,8 +3497,8 @@ Workspace: <code>${workspace}</code>
|
|
|
2917
3497
|
async finalizeDraft(sessionId) {
|
|
2918
3498
|
const draft = this.sessionDrafts.get(sessionId);
|
|
2919
3499
|
if (!draft) return;
|
|
2920
|
-
const finalMsgId = await draft.finalize();
|
|
2921
3500
|
this.sessionDrafts.delete(sessionId);
|
|
3501
|
+
const finalMsgId = await draft.finalize();
|
|
2922
3502
|
if (sessionId === this.assistantSession?.id) {
|
|
2923
3503
|
const fullText = this.sessionTextBuffers.get(sessionId);
|
|
2924
3504
|
this.sessionTextBuffers.delete(sessionId);
|
|
@@ -2957,4 +3537,4 @@ export {
|
|
|
2957
3537
|
ApiServer,
|
|
2958
3538
|
TelegramAdapter
|
|
2959
3539
|
};
|
|
2960
|
-
//# sourceMappingURL=chunk-
|
|
3540
|
+
//# sourceMappingURL=chunk-C6IFPAWN.js.map
|