pikiloom 0.4.26 → 0.4.27

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.
Files changed (155) hide show
  1. package/dashboard/dist/assets/{AgentTab-BNJ73P0m.js → AgentTab-DIP-y4dl.js} +1 -1
  2. package/dashboard/dist/assets/ConnectionModal-BCfvhkiI.js +1 -0
  3. package/dashboard/dist/assets/{DirBrowser-D-GT-G-w.js → DirBrowser-Dot7ua9-.js} +1 -1
  4. package/dashboard/dist/assets/{ExtensionsTab-BrJMulJR.js → ExtensionsTab-GioMx4S6.js} +1 -1
  5. package/dashboard/dist/assets/{IMAccessTab-auPnx468.js → IMAccessTab-C9_r8jJP.js} +1 -1
  6. package/dashboard/dist/assets/{Modal-lYfO9m-I.js → Modal-M1AP1sgR.js} +1 -1
  7. package/dashboard/dist/assets/{Modals-PRdWOIY9.js → Modals-CCm3UUfJ.js} +1 -1
  8. package/dashboard/dist/assets/{Select-BhbU-rll.js → Select-YubDM16P.js} +1 -1
  9. package/dashboard/dist/assets/{SessionPanel-_0dLWvEb.js → SessionPanel-Cnv9o96n.js} +1 -1
  10. package/dashboard/dist/assets/SystemTab-VgNivIe3.js +1 -0
  11. package/dashboard/dist/assets/index-BY7l20bi.js +23 -0
  12. package/dashboard/dist/assets/index-BlqvzDUL.js +3 -0
  13. package/dashboard/dist/assets/index-CXIN3nTr.css +1 -0
  14. package/dashboard/dist/assets/{shared-C5uXvhZJ.js → shared-C0SAInnT.js} +1 -1
  15. package/dashboard/dist/index.html +2 -2
  16. package/dist/agent/acp-client.js +0 -59
  17. package/dist/agent/artifacts.js +1 -38
  18. package/dist/agent/auto-update.js +2 -78
  19. package/dist/agent/await-resume.js +0 -20
  20. package/dist/agent/cli/auth.js +3 -51
  21. package/dist/agent/cli/catalog.js +0 -3
  22. package/dist/agent/cli/detector.js +1 -19
  23. package/dist/agent/cli/index.js +0 -3
  24. package/dist/agent/cli/registry.js +0 -25
  25. package/dist/agent/driver.js +0 -14
  26. package/dist/agent/drivers/claude-tui.js +2 -769
  27. package/dist/agent/drivers/claude.js +12 -631
  28. package/dist/agent/drivers/codex.js +11 -162
  29. package/dist/agent/drivers/gemini.js +6 -114
  30. package/dist/agent/drivers/hermes.js +0 -128
  31. package/dist/agent/goal.js +0 -45
  32. package/dist/agent/handover.js +1 -46
  33. package/dist/agent/images.js +7 -133
  34. package/dist/agent/index.js +1 -29
  35. package/dist/agent/mcp/bridge.js +1 -197
  36. package/dist/agent/mcp/extensions.js +5 -84
  37. package/dist/agent/mcp/oauth.js +2 -69
  38. package/dist/agent/mcp/registry.js +2 -33
  39. package/dist/agent/mcp/session-server.js +2 -45
  40. package/dist/agent/mcp/tools/ask-user.js +0 -6
  41. package/dist/agent/mcp/tools/await-resume.js +0 -15
  42. package/dist/agent/mcp/tools/goal.js +0 -25
  43. package/dist/agent/mcp/tools/types.js +0 -5
  44. package/dist/agent/mcp/tools/workspace.js +0 -23
  45. package/dist/agent/npm.js +0 -14
  46. package/dist/agent/session.js +86 -196
  47. package/dist/agent/skill-installer.js +3 -70
  48. package/dist/agent/skills.js +0 -43
  49. package/dist/agent/stream.js +5 -148
  50. package/dist/agent/types.js +0 -12
  51. package/dist/agent/utils.js +2 -120
  52. package/dist/bot/bot.js +3 -468
  53. package/dist/bot/command-ui.js +0 -45
  54. package/dist/bot/commands.js +0 -51
  55. package/dist/bot/headless-bot.js +0 -21
  56. package/dist/bot/host.js +1 -5
  57. package/dist/bot/human-loop.js +0 -8
  58. package/dist/bot/menu.js +0 -3
  59. package/dist/bot/orchestration.js +0 -3
  60. package/dist/bot/render-shared.js +0 -63
  61. package/dist/bot/session-hub.js +14 -92
  62. package/dist/bot/session-status.js +1 -5
  63. package/dist/bot/streaming.js +0 -53
  64. package/dist/browser-profile.js +0 -51
  65. package/dist/browser-supervisor.js +0 -75
  66. package/dist/catalog/cli-tools.js +0 -53
  67. package/dist/catalog/index.js +0 -18
  68. package/dist/catalog/local-models.js +0 -18
  69. package/dist/catalog/mcp-servers.js +0 -35
  70. package/dist/catalog/skill-repos.js +0 -32
  71. package/dist/channels/base.js +0 -19
  72. package/dist/channels/dingtalk/bot.js +0 -3
  73. package/dist/channels/dingtalk/channel.js +0 -33
  74. package/dist/channels/discord/bot.js +0 -7
  75. package/dist/channels/discord/channel.js +0 -22
  76. package/dist/channels/feishu/bot.js +0 -71
  77. package/dist/channels/feishu/channel.js +0 -78
  78. package/dist/channels/feishu/markdown.js +0 -5
  79. package/dist/channels/feishu/render.js +0 -45
  80. package/dist/channels/health.js +0 -46
  81. package/dist/channels/slack/bot.js +0 -7
  82. package/dist/channels/slack/channel.js +1 -27
  83. package/dist/channels/states.js +0 -3
  84. package/dist/channels/telegram/bot.js +0 -60
  85. package/dist/channels/telegram/channel.js +2 -99
  86. package/dist/channels/telegram/directory.js +0 -3
  87. package/dist/channels/telegram/live-preview.js +0 -8
  88. package/dist/channels/telegram/render.js +0 -30
  89. package/dist/channels/wecom/bot.js +0 -3
  90. package/dist/channels/wecom/channel.js +0 -58
  91. package/dist/channels/weixin/api.js +0 -9
  92. package/dist/channels/weixin/bot.js +0 -88
  93. package/dist/channels/weixin/channel.js +0 -6
  94. package/dist/cli/autostart.js +1 -44
  95. package/dist/cli/channel-supervisor.js +1 -59
  96. package/dist/cli/channels.js +0 -6
  97. package/dist/cli/main.js +1 -145
  98. package/dist/cli/onboarding.js +0 -6
  99. package/dist/cli/run.js +0 -11
  100. package/dist/cli/setup-wizard.js +0 -3
  101. package/dist/core/config/runtime-config.js +0 -38
  102. package/dist/core/config/user-config.js +0 -63
  103. package/dist/core/config/validation.js +0 -9
  104. package/dist/core/constants.js +0 -268
  105. package/dist/core/git.js +0 -31
  106. package/dist/core/legacy-compat.js +1 -28
  107. package/dist/core/logging.js +0 -3
  108. package/dist/core/platform.js +0 -28
  109. package/dist/core/process-control.js +0 -8
  110. package/dist/core/secrets/index.js +0 -17
  111. package/dist/core/secrets/inline-seal.js +0 -7
  112. package/dist/core/secrets/ref.js +0 -19
  113. package/dist/core/secrets/resolver.js +0 -8
  114. package/dist/core/secrets/store.js +1 -12
  115. package/dist/core/utils.js +1 -37
  116. package/dist/core/version.js +0 -3
  117. package/dist/dashboard/platform.js +1 -35
  118. package/dist/dashboard/routes/agents.js +2 -71
  119. package/dist/dashboard/routes/cli.js +0 -27
  120. package/dist/dashboard/routes/config.js +0 -54
  121. package/dist/dashboard/routes/extensions.js +0 -132
  122. package/dist/dashboard/routes/local-models.js +0 -68
  123. package/dist/dashboard/routes/models.js +0 -46
  124. package/dist/dashboard/routes/sessions.js +0 -100
  125. package/dist/dashboard/runtime.js +1 -58
  126. package/dist/dashboard/server.js +0 -49
  127. package/dist/dashboard/session-control.js +2 -69
  128. package/dist/model/catalog.js +1 -17
  129. package/dist/model/index.js +0 -15
  130. package/dist/model/injector.js +1 -181
  131. package/dist/model/provider-models.js +1 -48
  132. package/dist/model/responses-bridge.js +3 -45
  133. package/dist/model/store.js +0 -17
  134. package/dist/model/types.js +0 -12
  135. package/dist/model/validation.js +1 -18
  136. package/dist/pikichannel/adapter-pikiloom.js +0 -31
  137. package/dist/pikichannel/code.js +0 -12
  138. package/dist/pikichannel/codec.js +0 -14
  139. package/dist/pikichannel/host.js +4 -33
  140. package/dist/pikichannel/protocol.js +0 -52
  141. package/dist/pikichannel/rendezvous-broker.js +5 -26
  142. package/dist/pikichannel/rendezvous-host.js +3 -16
  143. package/dist/pikichannel/server.js +6 -55
  144. package/dist/pikichannel/transport.js +0 -25
  145. package/dist/pikichannel/transports/webrtc-host.js +0 -18
  146. package/dist/pikichannel/transports/webrtc-shared.js +4 -34
  147. package/dist/pikichannel/transports/websocket-host.js +1 -14
  148. package/dist/pikichannel/turn.js +2 -69
  149. package/dist/pikichannel/web/sdk.js +14 -71
  150. package/package.json +1 -1
  151. package/dashboard/dist/assets/ConnectionModal-Dt05H2f9.js +0 -1
  152. package/dashboard/dist/assets/SystemTab-CN_QrRKt.js +0 -1
  153. package/dashboard/dist/assets/index-CP3LYJR2.js +0 -23
  154. package/dashboard/dist/assets/index-C_neLnaO.js +0 -3
  155. 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 { /* ignore */ }
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 { /* ignore */ }
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,6 +1,3 @@
1
- /**
2
- * Telegram workdir browser for interactive directory navigation.
3
- */
4
1
  import path from 'node:path';
5
2
  import { listSubdirs } from '../../bot/bot.js';
6
3
  import { buildCompactSelectionTitle, compactCode, truncateMiddle } from './render.js';
@@ -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,6 +1,3 @@
1
- /**
2
- * WeChat Work / 企业微信 智能机器人 bot orchestration.
3
- */
4
1
  import fs from 'node:fs';
5
2
  import os from 'node:os';
6
3
  import path from 'node:path';
@@ -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)