pikiloom 0.4.26 → 0.4.28
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/dashboard/dist/assets/{AgentTab-BNJ73P0m.js → AgentTab-DtjuI8NU.js} +1 -1
- package/dashboard/dist/assets/ConnectionModal-BIx6a5o1.js +1 -0
- package/dashboard/dist/assets/{DirBrowser-D-GT-G-w.js → DirBrowser-BINh_7NB.js} +1 -1
- package/dashboard/dist/assets/{ExtensionsTab-BrJMulJR.js → ExtensionsTab-CHpd147b.js} +1 -1
- package/dashboard/dist/assets/{IMAccessTab-auPnx468.js → IMAccessTab-C4DbGxbG.js} +1 -1
- package/dashboard/dist/assets/{Modal-lYfO9m-I.js → Modal-D_PUi-5O.js} +1 -1
- package/dashboard/dist/assets/{Modals-PRdWOIY9.js → Modals-BahDEMqf.js} +1 -1
- package/dashboard/dist/assets/{Select-BhbU-rll.js → Select-Cp5VXb_2.js} +1 -1
- package/dashboard/dist/assets/SessionPanel-DZVNXptx.js +1 -0
- package/dashboard/dist/assets/SystemTab-CLznS31f.js +1 -0
- package/dashboard/dist/assets/index-CLGovkkw.js +3 -0
- package/dashboard/dist/assets/index-CXIN3nTr.css +1 -0
- package/dashboard/dist/assets/index-DcYOI6rL.js +23 -0
- package/dashboard/dist/assets/{shared-C5uXvhZJ.js → shared-C8DCTJ44.js} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/dist/agent/acp-client.js +0 -59
- package/dist/agent/artifacts.js +1 -38
- package/dist/agent/auto-update.js +2 -78
- package/dist/agent/await-resume.js +0 -20
- package/dist/agent/cli/auth.js +3 -51
- package/dist/agent/cli/catalog.js +0 -3
- package/dist/agent/cli/detector.js +1 -19
- package/dist/agent/cli/index.js +0 -3
- package/dist/agent/cli/registry.js +0 -25
- package/dist/agent/driver.js +0 -14
- package/dist/agent/drivers/claude-tui.js +2 -769
- package/dist/agent/drivers/claude.js +12 -631
- package/dist/agent/drivers/codex.js +11 -162
- package/dist/agent/drivers/gemini.js +6 -114
- package/dist/agent/drivers/hermes.js +0 -128
- package/dist/agent/goal.js +0 -45
- package/dist/agent/handover.js +1 -46
- package/dist/agent/images.js +7 -133
- package/dist/agent/index.js +1 -29
- package/dist/agent/mcp/bridge.js +1 -197
- package/dist/agent/mcp/extensions.js +5 -84
- package/dist/agent/mcp/oauth.js +2 -69
- package/dist/agent/mcp/registry.js +2 -33
- package/dist/agent/mcp/session-server.js +2 -45
- package/dist/agent/mcp/tools/ask-user.js +0 -6
- package/dist/agent/mcp/tools/await-resume.js +0 -15
- package/dist/agent/mcp/tools/goal.js +0 -25
- package/dist/agent/mcp/tools/types.js +0 -5
- package/dist/agent/mcp/tools/workspace.js +0 -23
- package/dist/agent/npm.js +0 -14
- package/dist/agent/session.js +86 -196
- package/dist/agent/skill-installer.js +3 -70
- package/dist/agent/skills.js +0 -43
- package/dist/agent/stream.js +5 -148
- package/dist/agent/types.js +0 -12
- package/dist/agent/utils.js +2 -120
- package/dist/bot/bot.js +3 -468
- package/dist/bot/command-ui.js +0 -45
- package/dist/bot/commands.js +0 -51
- package/dist/bot/headless-bot.js +0 -21
- package/dist/bot/host.js +1 -5
- package/dist/bot/human-loop.js +0 -8
- package/dist/bot/menu.js +0 -3
- package/dist/bot/orchestration.js +0 -3
- package/dist/bot/render-shared.js +0 -63
- package/dist/bot/session-hub.js +14 -92
- package/dist/bot/session-status.js +1 -5
- package/dist/bot/streaming.js +0 -53
- package/dist/browser-profile.js +0 -51
- package/dist/browser-supervisor.js +0 -75
- package/dist/catalog/cli-tools.js +0 -53
- package/dist/catalog/index.js +0 -18
- package/dist/catalog/local-models.js +0 -18
- package/dist/catalog/mcp-servers.js +0 -35
- package/dist/catalog/skill-repos.js +0 -32
- package/dist/channels/base.js +0 -19
- package/dist/channels/dingtalk/bot.js +0 -3
- package/dist/channels/dingtalk/channel.js +0 -33
- package/dist/channels/discord/bot.js +0 -7
- package/dist/channels/discord/channel.js +0 -22
- package/dist/channels/feishu/bot.js +0 -71
- package/dist/channels/feishu/channel.js +0 -78
- package/dist/channels/feishu/markdown.js +0 -5
- package/dist/channels/feishu/render.js +0 -45
- package/dist/channels/health.js +0 -46
- package/dist/channels/slack/bot.js +0 -7
- package/dist/channels/slack/channel.js +1 -27
- package/dist/channels/states.js +0 -3
- package/dist/channels/telegram/bot.js +0 -60
- package/dist/channels/telegram/channel.js +2 -99
- package/dist/channels/telegram/directory.js +0 -3
- package/dist/channels/telegram/live-preview.js +0 -8
- package/dist/channels/telegram/render.js +0 -30
- package/dist/channels/wecom/bot.js +0 -3
- package/dist/channels/wecom/channel.js +0 -58
- package/dist/channels/weixin/api.js +0 -9
- package/dist/channels/weixin/bot.js +0 -88
- package/dist/channels/weixin/channel.js +0 -6
- package/dist/cli/autostart.js +1 -44
- package/dist/cli/channel-supervisor.js +1 -59
- package/dist/cli/channels.js +0 -6
- package/dist/cli/main.js +1 -145
- package/dist/cli/onboarding.js +0 -6
- package/dist/cli/run.js +0 -11
- package/dist/cli/setup-wizard.js +0 -3
- package/dist/core/config/runtime-config.js +0 -38
- package/dist/core/config/user-config.js +0 -63
- package/dist/core/config/validation.js +0 -9
- package/dist/core/constants.js +0 -268
- package/dist/core/git.js +0 -31
- package/dist/core/legacy-compat.js +1 -28
- package/dist/core/logging.js +0 -3
- package/dist/core/platform.js +0 -28
- package/dist/core/process-control.js +0 -8
- package/dist/core/secrets/index.js +0 -17
- package/dist/core/secrets/inline-seal.js +0 -7
- package/dist/core/secrets/ref.js +0 -19
- package/dist/core/secrets/resolver.js +0 -8
- package/dist/core/secrets/store.js +1 -12
- package/dist/core/utils.js +1 -37
- package/dist/core/version.js +0 -3
- package/dist/dashboard/platform.js +1 -35
- package/dist/dashboard/routes/agents.js +2 -71
- package/dist/dashboard/routes/cli.js +0 -27
- package/dist/dashboard/routes/config.js +0 -54
- package/dist/dashboard/routes/extensions.js +0 -132
- package/dist/dashboard/routes/local-models.js +0 -68
- package/dist/dashboard/routes/models.js +0 -46
- package/dist/dashboard/routes/sessions.js +0 -100
- package/dist/dashboard/runtime.js +1 -58
- package/dist/dashboard/server.js +0 -49
- package/dist/dashboard/session-control.js +2 -69
- package/dist/model/catalog.js +1 -17
- package/dist/model/index.js +0 -15
- package/dist/model/injector.js +1 -181
- package/dist/model/provider-models.js +1 -48
- package/dist/model/responses-bridge.js +3 -45
- package/dist/model/store.js +0 -17
- package/dist/model/types.js +0 -12
- package/dist/model/validation.js +1 -18
- package/dist/pikichannel/adapter-pikiloom.js +0 -31
- package/dist/pikichannel/code.js +0 -12
- package/dist/pikichannel/codec.js +0 -14
- package/dist/pikichannel/host.js +4 -33
- package/dist/pikichannel/protocol.js +0 -52
- package/dist/pikichannel/rendezvous-broker.js +5 -26
- package/dist/pikichannel/rendezvous-host.js +3 -16
- package/dist/pikichannel/server.js +6 -55
- package/dist/pikichannel/transport.js +0 -25
- package/dist/pikichannel/transports/webrtc-host.js +0 -18
- package/dist/pikichannel/transports/webrtc-shared.js +4 -34
- package/dist/pikichannel/transports/websocket-host.js +1 -14
- package/dist/pikichannel/turn.js +2 -69
- package/dist/pikichannel/web/sdk.js +14 -71
- package/package.json +1 -1
- package/dashboard/dist/assets/ConnectionModal-Dt05H2f9.js +0 -1
- package/dashboard/dist/assets/SessionPanel-_0dLWvEb.js +0 -1
- package/dashboard/dist/assets/SystemTab-CN_QrRKt.js +0 -1
- package/dashboard/dist/assets/index-CP3LYJR2.js +0 -23
- package/dashboard/dist/assets/index-C_neLnaO.js +0 -3
- package/dashboard/dist/assets/index-CyCspgtc.css +0 -1
|
@@ -1,55 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Telegram channel — Telegram Bot API comms with Telegram-specific hooks.
|
|
3
|
-
*
|
|
4
|
-
* ┌─ Lifecycle ─────────────────────────────────────────────────────────────┐
|
|
5
|
-
* │ connect() — getMe, 获取 bot 信息 (id, username, displayName) │
|
|
6
|
-
* │ listen() — 启动 long-polling 循环,持续接收更新 │
|
|
7
|
-
* │ disconnect() — 停止 polling,中断进行中的请求 │
|
|
8
|
-
* │ drain() — 跳过所有积压的旧更新,返回跳过数量 │
|
|
9
|
-
* ├─ 发送 (bot → user) ────────────────────────────────────────────────────┤
|
|
10
|
-
* │ send(chatId, text, opts?) — 发送文本,支持 HTML/Markdown、 │
|
|
11
|
-
* │ 回复引用、inline keyboard, │
|
|
12
|
-
* │ 超长自动分片 (4096 上限) │
|
|
13
|
-
* │ editMessage(chatId, msgId, text) — 编辑已发送消息 (流式输出模拟) │
|
|
14
|
-
* │ deleteMessage(chatId, msgId) — 删除消息 │
|
|
15
|
-
* │ sendPhoto(chatId, photo, opts?) — 发送图片 (Buffer),支持 caption │
|
|
16
|
-
* │ sendDocument(chatId, content, filename, opts?) — 发送文件 │
|
|
17
|
-
* │ sendTyping(chatId) — 发送"正在输入"状态 │
|
|
18
|
-
* │ answerCallback(callbackId, text?) — 响应 inline 按钮回调 │
|
|
19
|
-
* ├─ 菜单管理 ─────────────────────────────────────────────────────────────┤
|
|
20
|
-
* │ setMenu(commands) — 注册底部菜单命令 (全局 + knownChats 级别), │
|
|
21
|
-
* │ 同时 setChatMenuButton 让菜单按钮可见 │
|
|
22
|
-
* │ clearMenu() — 删除所有命令,重置菜单按钮为默认 │
|
|
23
|
-
* ├─ 接收 (user → bot) — Hook 注册 ────────────────────────────────────────┤
|
|
24
|
-
* │ onCommand(handler) — /command args,自动解析命令名和参数; │
|
|
25
|
-
* │ 无 handler 时 fallthrough 到 onMessage │
|
|
26
|
-
* │ onMessage(handler) — 聚合消息 { text, files[] }; │
|
|
27
|
-
* │ 图片/文档自动下载到 workdir,提供本地路径 │
|
|
28
|
-
* │ onCallback(handler) — inline keyboard 按钮点击 │
|
|
29
|
-
* │ onError(handler) — polling / handler 错误 │
|
|
30
|
-
* ├─ Handler Context (ctx) ────────────────────────────────────────────────┤
|
|
31
|
-
* │ chatId / messageId / from (id, username, firstName) │
|
|
32
|
-
* │ reply(text, opts) — 直接回复当前消息 │
|
|
33
|
-
* │ editReply(msgId, text, opts) — 编辑之前的消息 │
|
|
34
|
-
* │ answerCallback(text?) — 响应 callback query (仅 callback) │
|
|
35
|
-
* │ channel — channel 实例,可调高级方法 │
|
|
36
|
-
* │ raw — 原始 Telegram update 对象 │
|
|
37
|
-
* ├─ 智能行为 ─────────────────────────────────────────────────────────────┤
|
|
38
|
-
* │ knownChats — 自动记录所有交互过的 chatId,setMenu 自动遍历 │
|
|
39
|
-
* │ 消息聚合 — photo/document 自动下载,统一为 { text, files[] } │
|
|
40
|
-
* │ 群组过滤 — 群聊默认只响应 @mention / 回复 bot 的消息 │
|
|
41
|
-
* │ Chat 白名单 — allowedChatIds 限制只处理特定聊天 │
|
|
42
|
-
* │ 解析失败降级 — HTML 解析失败自动去掉 parseMode 重试 │
|
|
43
|
-
* │ 超长消息分片 — 超过 4096 字符按换行符自动分片发送 │
|
|
44
|
-
* └────────────────────────────────────────────────────────────────────────┘
|
|
45
|
-
*
|
|
46
|
-
* Standalone usage:
|
|
47
|
-
* const ch = new TelegramChannel({ token: 'BOT_TOKEN', workdir: '/tmp' });
|
|
48
|
-
* await ch.connect();
|
|
49
|
-
* ch.onCommand((cmd, args, ctx) => ctx.reply(`Got /${cmd} ${args}`));
|
|
50
|
-
* ch.onMessage((msg, ctx) => ctx.reply(`Echo: ${msg.text} (files: ${msg.files.length})`));
|
|
51
|
-
* await ch.listen();
|
|
52
|
-
*/
|
|
53
1
|
import crypto from 'node:crypto';
|
|
54
2
|
import fs from 'node:fs';
|
|
55
3
|
import path from 'node:path';
|
|
@@ -61,9 +9,6 @@ import { TELEGRAM_LIMITS } from '../../core/constants.js';
|
|
|
61
9
|
import { ChannelHealth } from '../health.js';
|
|
62
10
|
import { formatScopedLogLine, shouldLog, writeScopedLog } from '../../core/logging.js';
|
|
63
11
|
import { recordKnownChatId } from '../../core/config/user-config.js';
|
|
64
|
-
// ---------------------------------------------------------------------------
|
|
65
|
-
// Proxy support — automatically respects HTTPS_PROXY / HTTP_PROXY / NO_PROXY
|
|
66
|
-
// ---------------------------------------------------------------------------
|
|
67
12
|
setGlobalDispatcher(new EnvHttpProxyAgent());
|
|
68
13
|
export { TelegramChannel };
|
|
69
14
|
const TG_MAX = TELEGRAM_LIMITS.maxMessageLength;
|
|
@@ -177,9 +122,6 @@ function isPollingConflictError(err) {
|
|
|
177
122
|
const msg = err instanceof Error ? err.message : String(err ?? '');
|
|
178
123
|
return msg.startsWith('Telegram polling conflict:');
|
|
179
124
|
}
|
|
180
|
-
// ---------------------------------------------------------------------------
|
|
181
|
-
// TelegramChannel
|
|
182
|
-
// ---------------------------------------------------------------------------
|
|
183
125
|
class TelegramChannel extends Channel {
|
|
184
126
|
capabilities = {
|
|
185
127
|
...DEFAULT_CHANNEL_CAPABILITIES,
|
|
@@ -189,9 +131,6 @@ class TelegramChannel extends Channel {
|
|
|
189
131
|
messageReactions: true,
|
|
190
132
|
sendImage: true,
|
|
191
133
|
};
|
|
192
|
-
/** Implementation of Channel.sendImage — wraps sendPhoto with an explicit
|
|
193
|
-
* byte buffer + MIME. Used by the bot's final-reply dispatcher for image
|
|
194
|
-
* MessageBlocks produced by the agent. */
|
|
195
134
|
async sendImage(chatId, bytes, opts) {
|
|
196
135
|
const filename = opts.filename || `image${extForMime(opts.mime)}`;
|
|
197
136
|
return this.sendPhoto(chatId, bytes, {
|
|
@@ -218,9 +157,7 @@ class TelegramChannel extends Channel {
|
|
|
218
157
|
_hMessage = null;
|
|
219
158
|
_hCallback = null;
|
|
220
159
|
_hError = null;
|
|
221
|
-
/** Chat IDs seen from incoming updates. */
|
|
222
160
|
knownChats = new Set();
|
|
223
|
-
/** Cached menu commands for applying to newly discovered chats. */
|
|
224
161
|
_menuCommands = null;
|
|
225
162
|
constructor(opts) {
|
|
226
163
|
super();
|
|
@@ -234,14 +171,10 @@ class TelegramChannel extends Channel {
|
|
|
234
171
|
if (opts.botUsername)
|
|
235
172
|
this.bot = { id: 0, username: opts.botUsername, displayName: '' };
|
|
236
173
|
}
|
|
237
|
-
// ---- Telegram-specific hook registration ----------------------------------
|
|
238
174
|
onCommand(h) { this._hCommand = h; }
|
|
239
175
|
onMessage(h) { this._hMessage = h; }
|
|
240
176
|
onCallback(h) { this._hCallback = h; }
|
|
241
177
|
onError(h) { this._hError = h; }
|
|
242
|
-
// ========================================================================
|
|
243
|
-
// Lifecycle
|
|
244
|
-
// ========================================================================
|
|
245
178
|
async connect() {
|
|
246
179
|
let delay = 2000;
|
|
247
180
|
for (let attempt = 1;; attempt++) {
|
|
@@ -337,9 +270,6 @@ class TelegramChannel extends Channel {
|
|
|
337
270
|
});
|
|
338
271
|
}
|
|
339
272
|
}
|
|
340
|
-
// ========================================================================
|
|
341
|
-
// Outgoing primitives (Channel interface)
|
|
342
|
-
// ========================================================================
|
|
343
273
|
_applyThreadId(payload, opts) {
|
|
344
274
|
if (opts?.messageThreadId != null)
|
|
345
275
|
payload.message_thread_id = opts.messageThreadId;
|
|
@@ -415,7 +345,7 @@ class TelegramChannel extends Channel {
|
|
|
415
345
|
try {
|
|
416
346
|
await this.api('editMessageText', p);
|
|
417
347
|
}
|
|
418
|
-
catch {
|
|
348
|
+
catch { }
|
|
419
349
|
}
|
|
420
350
|
}
|
|
421
351
|
}
|
|
@@ -434,16 +364,13 @@ class TelegramChannel extends Channel {
|
|
|
434
364
|
try {
|
|
435
365
|
await this.api('deleteMessage', { chat_id: chatId, message_id: msgId });
|
|
436
366
|
}
|
|
437
|
-
catch {
|
|
367
|
+
catch { }
|
|
438
368
|
}
|
|
439
369
|
async sendTyping(chatId, opts = {}) {
|
|
440
370
|
const payload = { chat_id: chatId, action: 'typing' };
|
|
441
371
|
this._applyThreadId(payload, opts);
|
|
442
372
|
await this.api('sendChatAction', payload).catch(() => { });
|
|
443
373
|
}
|
|
444
|
-
// ========================================================================
|
|
445
|
-
// Telegram-specific outgoing
|
|
446
|
-
// ========================================================================
|
|
447
374
|
async answerCallback(callbackId, text) {
|
|
448
375
|
if (text)
|
|
449
376
|
this._logOutgoingText('answerCallbackQuery', `callback_id=${callbackId}`, text);
|
|
@@ -542,8 +469,6 @@ class TelegramChannel extends Channel {
|
|
|
542
469
|
messageThreadId: opts.messageThreadId,
|
|
543
470
|
});
|
|
544
471
|
}
|
|
545
|
-
/** Set bottom menu commands and ensure the menu button is visible.
|
|
546
|
-
* Automatically applies to all known chats (from incoming updates). */
|
|
547
472
|
async setMenu(commands) {
|
|
548
473
|
this._menuCommands = commands;
|
|
549
474
|
await this.api('setMyCommands', { commands });
|
|
@@ -552,7 +477,6 @@ class TelegramChannel extends Channel {
|
|
|
552
477
|
await this._applyMenuToChat(cid);
|
|
553
478
|
}
|
|
554
479
|
}
|
|
555
|
-
/** Track a chat ID; apply menu on first discovery and persist for restart. */
|
|
556
480
|
_trackChat(chatId) {
|
|
557
481
|
if (this.knownChats.has(chatId))
|
|
558
482
|
return;
|
|
@@ -563,7 +487,6 @@ class TelegramChannel extends Channel {
|
|
|
563
487
|
catch { }
|
|
564
488
|
this._applyMenuToChat(chatId).catch(() => { });
|
|
565
489
|
}
|
|
566
|
-
/** Apply cached menu commands to a single chat. */
|
|
567
490
|
async _applyMenuToChat(chatId) {
|
|
568
491
|
if (!this._menuCommands)
|
|
569
492
|
return;
|
|
@@ -576,7 +499,6 @@ class TelegramChannel extends Channel {
|
|
|
576
499
|
menu_button: { type: 'commands' },
|
|
577
500
|
}).catch(() => { });
|
|
578
501
|
}
|
|
579
|
-
/** Remove all bot commands and reset menu button to default. */
|
|
580
502
|
async clearMenu() {
|
|
581
503
|
this._menuCommands = null;
|
|
582
504
|
await this.api('deleteMyCommands', {}).catch(() => { });
|
|
@@ -586,7 +508,6 @@ class TelegramChannel extends Channel {
|
|
|
586
508
|
await this.api('setChatMenuButton', { chat_id: cid, menu_button: { type: 'default' } }).catch(() => { });
|
|
587
509
|
}
|
|
588
510
|
}
|
|
589
|
-
/** Drain pending updates (call before listen to skip stale messages). */
|
|
590
511
|
async drain() {
|
|
591
512
|
const data = await this.api('getUpdates', { offset: -1, timeout: 0 });
|
|
592
513
|
const results = data.result || [];
|
|
@@ -594,7 +515,6 @@ class TelegramChannel extends Channel {
|
|
|
594
515
|
this.offset = results[results.length - 1].update_id + 1;
|
|
595
516
|
return results.length;
|
|
596
517
|
}
|
|
597
|
-
/** Get the chat ID from the most recent incoming message (useful for 1v1 bot setup). */
|
|
598
518
|
async getRecentChatId() {
|
|
599
519
|
const data = await this.api('getUpdates', { offset: -1, timeout: 0 });
|
|
600
520
|
const results = data.result || [];
|
|
@@ -603,7 +523,6 @@ class TelegramChannel extends Channel {
|
|
|
603
523
|
const u = results[results.length - 1];
|
|
604
524
|
return u.message?.chat?.id ?? u.callback_query?.message?.chat?.id ?? null;
|
|
605
525
|
}
|
|
606
|
-
/** Download a Telegram file to a local path. Returns the local path. */
|
|
607
526
|
async downloadFile(fileId, destFilename) {
|
|
608
527
|
const meta = await this.api('getFile', { file_id: fileId });
|
|
609
528
|
const filePath = meta.result.file_path;
|
|
@@ -624,7 +543,6 @@ class TelegramChannel extends Channel {
|
|
|
624
543
|
const buf = Buffer.from(await resp.arrayBuffer());
|
|
625
544
|
fs.writeFileSync(localPath, buf);
|
|
626
545
|
}
|
|
627
|
-
// Check downloaded file size
|
|
628
546
|
const stat = fs.statSync(localPath);
|
|
629
547
|
if (stat.size > FILE_MAX_BYTES) {
|
|
630
548
|
fs.rmSync(localPath, { force: true });
|
|
@@ -632,9 +550,6 @@ class TelegramChannel extends Channel {
|
|
|
632
550
|
}
|
|
633
551
|
return localPath;
|
|
634
552
|
}
|
|
635
|
-
// ========================================================================
|
|
636
|
-
// Low-level API
|
|
637
|
-
// ========================================================================
|
|
638
553
|
async api(method, payload) {
|
|
639
554
|
const timeout = method === 'getUpdates' ? (this.pollTimeout + 10) * 1000 : this.apiTimeout * 1000;
|
|
640
555
|
const resp = await this._fetchResponse(`Telegram API ${method}`, `${this.base}/${method}`, {
|
|
@@ -654,9 +569,6 @@ class TelegramChannel extends Channel {
|
|
|
654
569
|
}
|
|
655
570
|
return data;
|
|
656
571
|
}
|
|
657
|
-
// ========================================================================
|
|
658
|
-
// Internal: dispatch
|
|
659
|
-
// ========================================================================
|
|
660
572
|
async _dispatch(update) {
|
|
661
573
|
const key = this._queueKey(update);
|
|
662
574
|
if (!key) {
|
|
@@ -685,7 +597,6 @@ class TelegramChannel extends Channel {
|
|
|
685
597
|
return String(raw.chat.id);
|
|
686
598
|
}
|
|
687
599
|
async _dispatchNow(update) {
|
|
688
|
-
// callback query
|
|
689
600
|
if (update.callback_query) {
|
|
690
601
|
const cq = update.callback_query;
|
|
691
602
|
const chatId = cq.message?.chat?.id;
|
|
@@ -703,7 +614,6 @@ class TelegramChannel extends Channel {
|
|
|
703
614
|
await this._hCallback(cq.data || '', ctx);
|
|
704
615
|
return;
|
|
705
616
|
}
|
|
706
|
-
// message
|
|
707
617
|
const raw = update.message || update.edited_message;
|
|
708
618
|
if (!raw || !raw.chat?.id)
|
|
709
619
|
return;
|
|
@@ -722,7 +632,6 @@ class TelegramChannel extends Channel {
|
|
|
722
632
|
}
|
|
723
633
|
const from = { id: raw.from?.id, username: raw.from?.username, firstName: raw.from?.first_name };
|
|
724
634
|
const ctx = this._makeCtx(chatId, raw.message_id, from, raw);
|
|
725
|
-
// command — if no command handler registered, fall through to message handler
|
|
726
635
|
const entities = raw.entities || [];
|
|
727
636
|
const cmdEntity = entities.find((e) => e.type === 'bot_command' && e.offset === 0);
|
|
728
637
|
if (cmdEntity) {
|
|
@@ -735,12 +644,10 @@ class TelegramChannel extends Channel {
|
|
|
735
644
|
return;
|
|
736
645
|
}
|
|
737
646
|
}
|
|
738
|
-
// message (text + files aggregation)
|
|
739
647
|
if (!this._hMessage)
|
|
740
648
|
return;
|
|
741
649
|
const text = this._cleanMention(raw.text || raw.caption || '');
|
|
742
650
|
const files = [];
|
|
743
|
-
// download photo
|
|
744
651
|
if (raw.photo?.length) {
|
|
745
652
|
const best = raw.photo[raw.photo.length - 1];
|
|
746
653
|
this._debug(`[recv] downloading photo file_id=${best.file_id} size=${best.width}x${best.height}`);
|
|
@@ -754,7 +661,6 @@ class TelegramChannel extends Channel {
|
|
|
754
661
|
this._hError?.(e);
|
|
755
662
|
}
|
|
756
663
|
}
|
|
757
|
-
// download document
|
|
758
664
|
if (raw.document) {
|
|
759
665
|
const origName = raw.document.file_name || `doc_${raw.message_id}`;
|
|
760
666
|
this._debug(`[recv] downloading document "${origName}" file_id=${raw.document.file_id}`);
|
|
@@ -771,9 +677,6 @@ class TelegramChannel extends Channel {
|
|
|
771
677
|
this._debug(`[dispatch] -> onMessage text="${text.slice(0, 80)}" files=${files.length} chat=${chatId}`);
|
|
772
678
|
await this._hMessage({ text, files }, ctx);
|
|
773
679
|
}
|
|
774
|
-
// ========================================================================
|
|
775
|
-
// Internal: helpers
|
|
776
|
-
// ========================================================================
|
|
777
680
|
_makeCtx(chatId, messageId, from, raw) {
|
|
778
681
|
const messageThreadId = typeof raw?.message_thread_id === 'number' ? raw.message_thread_id : undefined;
|
|
779
682
|
return {
|
|
@@ -1,14 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Channel-agnostic live preview controller for streaming updates.
|
|
3
|
-
*/
|
|
4
1
|
import { hasPreviewMeta, samePreviewMeta, samePreviewPlan } from '../../bot/streaming.js';
|
|
5
2
|
import { STREAM_PREVIEW_TIMEOUTS } from '../../core/constants.js';
|
|
6
3
|
const STREAM_PREVIEW_HEARTBEAT_MS = STREAM_PREVIEW_TIMEOUTS.heartbeat;
|
|
7
4
|
const STREAM_TYPING_HEARTBEAT_MS = STREAM_PREVIEW_TIMEOUTS.typing;
|
|
8
5
|
const STREAM_STALLED_NOTICE_MS = STREAM_PREVIEW_TIMEOUTS.stalledNotice;
|
|
9
|
-
// ---------------------------------------------------------------------------
|
|
10
|
-
// LivePreview — generic streaming preview controller
|
|
11
|
-
// ---------------------------------------------------------------------------
|
|
12
6
|
export class LivePreview {
|
|
13
7
|
initialText;
|
|
14
8
|
agent;
|
|
@@ -200,8 +194,6 @@ export class LivePreview {
|
|
|
200
194
|
}
|
|
201
195
|
});
|
|
202
196
|
}
|
|
203
|
-
/** True when streaming gave up on the placeholder (e.g. Feishu rejected too many
|
|
204
|
-
* consecutive edits). The owner should send the final reply as a fresh card. */
|
|
205
197
|
isPlaceholderAbandoned() {
|
|
206
198
|
return this.placeholderAbandoned;
|
|
207
199
|
}
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Telegram-specific message rendering and formatting.
|
|
3
|
-
*/
|
|
4
1
|
import { encodeCommandAction } from '../../bot/command-ui.js';
|
|
5
2
|
import { currentHumanLoopQuestion, humanLoopAnsweredCount, isHumanLoopAwaitingText, isHumanLoopQuestionAnswered, summarizeHumanLoopAnswer, } from '../../bot/human-loop.js';
|
|
6
3
|
import { footerStatusSymbol, formatFooterParts, trimActivityForPreview, buildProviderUsageLines, extractFinalReplyData, extractStreamPreviewData, parseGfmTable, } from '../../bot/render-shared.js';
|
|
@@ -85,15 +82,6 @@ export function renderCommandSelectionHtml(view) {
|
|
|
85
82
|
lines.push('', `<i>${escapeHtml(view.helperText)}</i>`);
|
|
86
83
|
return lines.join('\n');
|
|
87
84
|
}
|
|
88
|
-
/**
|
|
89
|
-
* Telegram caps `callback_data` at 64 bytes. Most encoded actions fit easily,
|
|
90
|
-
* but BYOK model rows encode as `md:p:<uuid>:<modelId>` (~42 bytes of overhead
|
|
91
|
-
* before the model id even starts), so a single long provider/model id blows
|
|
92
|
-
* the limit — and Telegram then rejects the *entire* message with
|
|
93
|
-
* BUTTON_DATA_INVALID, killing the whole menu. Mirror the PathRegistry idiom
|
|
94
|
-
* from directory.ts: stash the over-length payload and ship a short `r:<id>`
|
|
95
|
-
* token instead, resolving it back on the callback round-trip.
|
|
96
|
-
*/
|
|
97
85
|
const TELEGRAM_CALLBACK_LIMIT = 64;
|
|
98
86
|
class CallbackDataRegistry {
|
|
99
87
|
idToData = new Map();
|
|
@@ -127,7 +115,6 @@ class CallbackDataRegistry {
|
|
|
127
115
|
}
|
|
128
116
|
}
|
|
129
117
|
const callbackDataRegistry = new CallbackDataRegistry();
|
|
130
|
-
/** Resolve a `r:<id>` token back to its original encoded action payload. */
|
|
131
118
|
export function unpackCallbackData(data) {
|
|
132
119
|
return callbackDataRegistry.unpack(data);
|
|
133
120
|
}
|
|
@@ -179,11 +166,6 @@ export function buildHumanLoopPromptHtml(prompt) {
|
|
|
179
166
|
}
|
|
180
167
|
return lines.join('\n');
|
|
181
168
|
}
|
|
182
|
-
/**
|
|
183
|
-
* Closed-state rendering for a resolved human-loop card. The original prompt
|
|
184
|
-
* collapses to a single header line plus one line per question showing the
|
|
185
|
-
* answer — no buttons, no "submit hint", just the decision frozen in place.
|
|
186
|
-
*/
|
|
187
169
|
export function buildAnsweredHumanLoopPromptHtml(prompt, summary) {
|
|
188
170
|
const symbol = summary.status === 'cancelled' ? '⊘' : '✓';
|
|
189
171
|
const lines = [`<b>${symbol} ${escapeHtml(prompt.title)}</b>`];
|
|
@@ -195,7 +177,6 @@ export function buildAnsweredHumanLoopPromptHtml(prompt, summary) {
|
|
|
195
177
|
return lines.join('\n');
|
|
196
178
|
}
|
|
197
179
|
function mdInline(line) {
|
|
198
|
-
// Strip inline backtick code — IM channels render them with heavy styling
|
|
199
180
|
const stripped = line.replace(/`([^`\n]+)`/g, '$1');
|
|
200
181
|
return formatMarkdownSegment(stripped);
|
|
201
182
|
}
|
|
@@ -331,9 +312,6 @@ export function formatProviderUsageLines(usage) {
|
|
|
331
312
|
return buildProviderUsageLines(usage).map(line => line.bold ? `<b>${escapeHtml(line.text)}</b>` : escapeHtml(line.text));
|
|
332
313
|
}
|
|
333
314
|
export function buildInitialPreviewHtml(agent, modelOrWaiting, effortOrQueuePosition, waitingArg = false, queuePositionArg = 0) {
|
|
334
|
-
// Backwards-compat shim: legacy calls pass `(agent, waiting, queuePosition)`,
|
|
335
|
-
// new calls pass `(agent, model, effort, waiting, queuePosition)`. Normalise
|
|
336
|
-
// both shapes here so existing tests + callsites keep working.
|
|
337
315
|
let model = null;
|
|
338
316
|
let effort = null;
|
|
339
317
|
let waiting = waitingArg;
|
|
@@ -359,9 +337,6 @@ export function buildStreamPreviewHtml(input) {
|
|
|
359
337
|
const data = extractStreamPreviewData(input);
|
|
360
338
|
const parts = [];
|
|
361
339
|
if (data.planDisplay) {
|
|
362
|
-
// The first line of planDisplay is already "Plan N/M" (rendered by
|
|
363
|
-
// renderPlanForPreview). Promote it to the bold heading and inline the
|
|
364
|
-
// rest, so the card doesn't show a redundant "Plan" line above "Plan N/M".
|
|
365
340
|
const planText = data.planDisplay;
|
|
366
341
|
const nl = planText.indexOf('\n');
|
|
367
342
|
const head = nl >= 0 ? planText.slice(0, nl) : planText;
|
|
@@ -375,7 +350,6 @@ export function buildStreamPreviewHtml(input) {
|
|
|
375
350
|
parts.push(`<blockquote><b>Sub-agent</b>\n${escapeHtml(data.subAgentsDisplay)}</blockquote>`);
|
|
376
351
|
}
|
|
377
352
|
if (data.thinkDisplay && !data.display) {
|
|
378
|
-
// Elapsed lives in the footer only (single timer). Header is the bare label.
|
|
379
353
|
parts.push(`<blockquote><b>${escapeHtml(data.label)}</b>\n${escapeHtml(data.thinkDisplay)}</blockquote>`);
|
|
380
354
|
}
|
|
381
355
|
else if (data.display) {
|
|
@@ -385,10 +359,6 @@ export function buildStreamPreviewHtml(input) {
|
|
|
385
359
|
parts.push(mdToTgHtml(data.preview));
|
|
386
360
|
}
|
|
387
361
|
else if (data.thinkingProgressText) {
|
|
388
|
-
// Thinking phase with no streamed thinking/body text yet — show the bare
|
|
389
|
-
// "{thinkLabel}" so the card isn't blank. The elapsed tick lives in the
|
|
390
|
-
// footer only (one timer, not two); the footer re-renders on the channel
|
|
391
|
-
// heartbeat, so the card still visibly advances.
|
|
392
362
|
parts.push(`<blockquote><b>${escapeHtml(data.label)}</b></blockquote>`);
|
|
393
363
|
}
|
|
394
364
|
parts.push(formatPreviewFooterHtml(input.agent, input.elapsedMs, input.meta ?? null, {
|
|
@@ -1,31 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WeChat Work / 企业微信 智能机器人 channel transport — Smart Bot WebSocket.
|
|
3
|
-
*
|
|
4
|
-
* Endpoint: wss://openws.work.weixin.qq.com
|
|
5
|
-
* Frame envelope:
|
|
6
|
-
* { cmd, headers: { req_id }, body }
|
|
7
|
-
* responses omit `cmd` and may carry { errcode, errmsg }
|
|
8
|
-
*
|
|
9
|
-
* Outbound:
|
|
10
|
-
* - aibot_subscribe { bot_id, secret } — auth + register
|
|
11
|
-
* - ping — heartbeat (30s)
|
|
12
|
-
* - aibot_respond_msg (stream, finish=true) — reply to a callback
|
|
13
|
-
* - aibot_send_msg (markdown) — proactive send
|
|
14
|
-
*
|
|
15
|
-
* Inbound:
|
|
16
|
-
* - aibot_msg_callback { msgid, chatid, chattype, from.userid, msgtype, text/voice/image/file/mixed/quote }
|
|
17
|
-
*
|
|
18
|
-
* Caveats:
|
|
19
|
-
* - text/voice messages are dispatched as text. Image/file/mixed payloads
|
|
20
|
-
* reference encrypted URLs that require a separate corp-secret + AES key
|
|
21
|
-
* to download — out of scope for this minimal transport.
|
|
22
|
-
* - The protocol has no edit primitive. editMessage is a no-op.
|
|
23
|
-
* - Plain text messages may be sent via aibot_respond_msg (in conversation,
|
|
24
|
-
* when we still hold the original req_id) or aibot_send_msg (proactive
|
|
25
|
-
* send to a known chatid). We use the original req_id for the first
|
|
26
|
-
* reply and switch to aibot_send_msg for follow-up messages in the same
|
|
27
|
-
* conversation (e.g. streaming result deliveries).
|
|
28
|
-
*/
|
|
29
1
|
import { EventEmitter } from 'node:events';
|
|
30
2
|
import { WebSocket } from 'ws';
|
|
31
3
|
import { Channel, DEFAULT_CHANNEL_CAPABILITIES, splitText, sleep, } from '../base.js';
|
|
@@ -40,7 +12,6 @@ function describeError(err) {
|
|
|
40
12
|
export class WeComChannel extends Channel {
|
|
41
13
|
capabilities = {
|
|
42
14
|
...DEFAULT_CHANNEL_CAPABILITIES,
|
|
43
|
-
// Smart Bot WS protocol has no edit primitive.
|
|
44
15
|
};
|
|
45
16
|
knownChats = new Set();
|
|
46
17
|
botId;
|
|
@@ -54,13 +25,10 @@ export class WeComChannel extends Channel {
|
|
|
54
25
|
missedPong = 0;
|
|
55
26
|
reqSeq = 0;
|
|
56
27
|
chatMeta = new Map();
|
|
57
|
-
/** req_id -> deferred ack handler. */
|
|
58
28
|
pendingAcks = new Map();
|
|
59
|
-
/** Dedup by msgid. */
|
|
60
29
|
seenMsgIds = new Set();
|
|
61
30
|
seenMsgQueue = [];
|
|
62
31
|
static SEEN_CAP = 256;
|
|
63
|
-
/** Subscribe ack waiter — only set during initial handshake. */
|
|
64
32
|
subscribeAck = null;
|
|
65
33
|
internalEmitter = new EventEmitter();
|
|
66
34
|
messageHandlers = new Set();
|
|
@@ -75,9 +43,6 @@ export class WeComChannel extends Channel {
|
|
|
75
43
|
}
|
|
76
44
|
onMessage(handler) { this.messageHandlers.add(handler); return this; }
|
|
77
45
|
onError(handler) { this.errorHandlers.add(handler); return this; }
|
|
78
|
-
// ========================================================================
|
|
79
|
-
// Lifecycle
|
|
80
|
-
// ========================================================================
|
|
81
46
|
async connect() {
|
|
82
47
|
const shortId = this.botId.length > 12 ? `${this.botId.slice(0, 6)}...${this.botId.slice(-4)}` : this.botId;
|
|
83
48
|
this.bot = {
|
|
@@ -109,9 +74,6 @@ export class WeComChannel extends Channel {
|
|
|
109
74
|
}
|
|
110
75
|
if (!this.running)
|
|
111
76
|
break;
|
|
112
|
-
// A connection that survived >2 heartbeats counts as "healthy session
|
|
113
|
-
// ended" → reset backoff and reconnect quickly. Anything shorter is
|
|
114
|
-
// treated as a failed attempt that advances exponential backoff.
|
|
115
77
|
const wasLongLived = Date.now() - connectedAt > 2 * WC_HEARTBEAT_MS;
|
|
116
78
|
const delayMs = wasLongLived
|
|
117
79
|
? (health.recordSuccess(), WECOM_LIMITS.initialRetryDelay)
|
|
@@ -132,9 +94,6 @@ export class WeComChannel extends Channel {
|
|
|
132
94
|
this.listenResolve?.();
|
|
133
95
|
this.listenResolve = null;
|
|
134
96
|
}
|
|
135
|
-
// ========================================================================
|
|
136
|
-
// Single-connection lifecycle
|
|
137
|
-
// ========================================================================
|
|
138
97
|
runConnection() {
|
|
139
98
|
return new Promise((resolve, reject) => {
|
|
140
99
|
let settled = false;
|
|
@@ -194,7 +153,6 @@ export class WeComChannel extends Channel {
|
|
|
194
153
|
this.subscribeAck = null;
|
|
195
154
|
reject(err instanceof Error ? err : new Error(describeError(err)));
|
|
196
155
|
});
|
|
197
|
-
// Defensive timeout — server should ack within seconds.
|
|
198
156
|
const timer = setTimeout(() => {
|
|
199
157
|
if (!this.subscribeAck)
|
|
200
158
|
return;
|
|
@@ -229,9 +187,6 @@ export class WeComChannel extends Channel {
|
|
|
229
187
|
clearInterval(this.heartbeatTimer);
|
|
230
188
|
this.heartbeatTimer = null;
|
|
231
189
|
}
|
|
232
|
-
// ========================================================================
|
|
233
|
-
// Frame dispatch
|
|
234
|
-
// ========================================================================
|
|
235
190
|
handleFrame(frame) {
|
|
236
191
|
const cmd = frame.cmd || '';
|
|
237
192
|
const reqId = frame.headers?.req_id || '';
|
|
@@ -244,7 +199,6 @@ export class WeComChannel extends Channel {
|
|
|
244
199
|
return;
|
|
245
200
|
}
|
|
246
201
|
if (!cmd) {
|
|
247
|
-
// Response frame; route by req_id prefix.
|
|
248
202
|
if (reqId.startsWith('ping')) {
|
|
249
203
|
this.missedPong = 0;
|
|
250
204
|
return;
|
|
@@ -358,9 +312,6 @@ export class WeComChannel extends Channel {
|
|
|
358
312
|
}
|
|
359
313
|
}
|
|
360
314
|
}
|
|
361
|
-
// ========================================================================
|
|
362
|
-
// Outgoing primitives
|
|
363
|
-
// ========================================================================
|
|
364
315
|
async send(chatId, text, _opts) {
|
|
365
316
|
const chat = String(chatId);
|
|
366
317
|
const trimmed = (text || '').trim() || '(empty)';
|
|
@@ -370,7 +321,6 @@ export class WeComChannel extends Channel {
|
|
|
370
321
|
const replyReqId = meta?.pendingReqId;
|
|
371
322
|
for (let i = 0; i < chunks.length; i++) {
|
|
372
323
|
const chunk = chunks[i];
|
|
373
|
-
// First chunk: prefer aibot_respond_msg if we hold a fresh req_id.
|
|
374
324
|
if (i === 0 && replyReqId) {
|
|
375
325
|
const streamId = this.makeReqId('stream');
|
|
376
326
|
const frame = {
|
|
@@ -382,7 +332,6 @@ export class WeComChannel extends Channel {
|
|
|
382
332
|
},
|
|
383
333
|
};
|
|
384
334
|
await this.writeFrame(frame);
|
|
385
|
-
// The server treats the req_id as consumed after stream finish=true.
|
|
386
335
|
if (meta)
|
|
387
336
|
meta.pendingReqId = null;
|
|
388
337
|
lastReqId = replyReqId;
|
|
@@ -404,17 +353,11 @@ export class WeComChannel extends Channel {
|
|
|
404
353
|
return lastReqId;
|
|
405
354
|
}
|
|
406
355
|
async editMessage(_chatId, _msgId, _text, _opts) {
|
|
407
|
-
// No edit primitive in the Smart Bot WS protocol.
|
|
408
356
|
}
|
|
409
357
|
async deleteMessage(_chatId, _msgId) {
|
|
410
|
-
// No delete primitive.
|
|
411
358
|
}
|
|
412
359
|
async sendTyping(_chatId, _opts) {
|
|
413
|
-
// No typing indicator.
|
|
414
360
|
}
|
|
415
|
-
// ========================================================================
|
|
416
|
-
// Frame writing helpers
|
|
417
|
-
// ========================================================================
|
|
418
361
|
writeFrame(frame) {
|
|
419
362
|
return new Promise((resolve, reject) => {
|
|
420
363
|
const ws = this.ws;
|
|
@@ -434,7 +377,6 @@ export class WeComChannel extends Channel {
|
|
|
434
377
|
return new Promise((resolve, reject) => {
|
|
435
378
|
const timer = setTimeout(() => {
|
|
436
379
|
this.pendingAcks.delete(reqId);
|
|
437
|
-
// Fall back to non-blocking on timeout to avoid deadlocks.
|
|
438
380
|
this.debug(`[ws] ack timeout req_id=${reqId} — proceeding`);
|
|
439
381
|
resolve();
|
|
440
382
|
}, timeoutMs);
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* WeChat official account API integration.
|
|
3
|
-
*/
|
|
4
1
|
import crypto from 'node:crypto';
|
|
5
2
|
import QRCode from 'qrcode';
|
|
6
3
|
import { VALIDATION_TIMEOUTS, WEIXIN_LIMITS } from '../../core/constants.js';
|
|
@@ -43,12 +40,6 @@ function withTimeoutSignal(timeoutMs) {
|
|
|
43
40
|
dispose: () => clearTimeout(timer),
|
|
44
41
|
};
|
|
45
42
|
}
|
|
46
|
-
/**
|
|
47
|
-
* Combine a timeout signal with an optional external "stop" signal so that
|
|
48
|
-
* either source can abort the in-flight request. Used by long-poll callers
|
|
49
|
-
* to cut over immediately when the channel is being torn down (config change,
|
|
50
|
-
* channel removal) without waiting out the long-poll timeout.
|
|
51
|
-
*/
|
|
52
43
|
function combineAbortSignals(timeoutMs, external) {
|
|
53
44
|
const timeout = withTimeoutSignal(timeoutMs);
|
|
54
45
|
if (!external)
|