cli-jaw 2.17.9 → 2.17.10

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 (38) hide show
  1. package/dist/src/core/config.js +34 -3
  2. package/dist/src/core/config.js.map +1 -1
  3. package/dist/src/core/settings-merge.js +40 -0
  4. package/dist/src/core/settings-merge.js.map +1 -1
  5. package/dist/src/discord/bot.js +140 -26
  6. package/dist/src/discord/bot.js.map +1 -1
  7. package/dist/src/discord/reactions.js +92 -0
  8. package/dist/src/discord/reactions.js.map +1 -0
  9. package/dist/src/messaging/ack-reaction.js +192 -0
  10. package/dist/src/messaging/ack-reaction.js.map +1 -0
  11. package/dist/src/messaging/channel-capabilities.js +9 -3
  12. package/dist/src/messaging/channel-capabilities.js.map +1 -1
  13. package/dist/src/messaging/channel-validate.js +12 -0
  14. package/dist/src/messaging/channel-validate.js.map +1 -1
  15. package/dist/src/messaging/queue-notice.js +164 -0
  16. package/dist/src/messaging/queue-notice.js.map +1 -0
  17. package/dist/src/slack/api.js +93 -0
  18. package/dist/src/slack/api.js.map +1 -1
  19. package/dist/src/slack/auto-join.js +330 -0
  20. package/dist/src/slack/auto-join.js.map +1 -0
  21. package/dist/src/slack/bot.js +237 -21
  22. package/dist/src/slack/bot.js.map +1 -1
  23. package/dist/src/slack/manifest.js +12 -0
  24. package/dist/src/slack/manifest.js.map +1 -1
  25. package/dist/src/slack/send-only-client.js +14 -2
  26. package/dist/src/slack/send-only-client.js.map +1 -1
  27. package/dist/src/telegram/bot.js +146 -64
  28. package/dist/src/telegram/bot.js.map +1 -1
  29. package/dist/src/telegram/ipv4-fetch.js +74 -0
  30. package/dist/src/telegram/ipv4-fetch.js.map +1 -0
  31. package/dist/src/telegram/reactions.js +126 -0
  32. package/dist/src/telegram/reactions.js.map +1 -0
  33. package/package.json +1 -1
  34. package/public/locales/en.json +1 -0
  35. package/public/locales/ja.json +1 -0
  36. package/public/locales/ko.json +1 -0
  37. package/public/locales/zh.json +1 -0
  38. package/scripts/check-redaction-sinks.mjs +5 -0
@@ -0,0 +1,126 @@
1
+ // ─── Telegram Message Reactions (Bot API 7.0+) ───────
2
+ // Telegram does NOT accept arbitrary emoji here. ReactionTypeEmoji has a fixed
3
+ // allowlist, so a settings typo becomes a runtime API error unless it is checked
4
+ // before the call.
5
+ //
6
+ // Two consequences that look like bugs if you do not know them:
7
+ // 1. ✅ and ❌ are NOT on the list. Success/failure have to use 👍/👎, which is
8
+ // also what hermes-agent settled on — not a stylistic choice.
9
+ // 2. A bot's previous reaction is REPLACED atomically, so there is no remove
10
+ // step. Clearing first would show an empty state and cost an extra request.
11
+ /** ReactionTypeEmoji's complete allowlist, core.telegram.org/bots/api (2026-08-21). */
12
+ export const TELEGRAM_REACTION_EMOJI = new Set([
13
+ '\u2764', '\u{1F44D}', '\u{1F44E}', '\u{1F525}', '\u{1F970}', '\u{1F44F}',
14
+ '\u{1F601}', '\u{1F914}', '\u{1F92F}', '\u{1F631}', '\u{1F92C}', '\u{1F622}',
15
+ '\u{1F389}', '\u{1F929}', '\u{1F92E}', '\u{1F4A9}', '\u{1F64F}', '\u{1F44C}',
16
+ '\u{1F54A}', '\u{1F921}', '\u{1F971}', '\u{1F974}', '\u{1F60D}', '\u{1F433}',
17
+ '\u2764\u200D\u{1F525}', '\u{1F31A}', '\u{1F32D}', '\u{1F4AF}', '\u{1F923}',
18
+ '\u26A1', '\u{1F34C}', '\u{1F3C6}', '\u{1F494}', '\u{1F928}', '\u{1F610}',
19
+ '\u{1F353}', '\u{1F37E}', '\u{1F48B}', '\u{1F595}', '\u{1F608}', '\u{1F634}',
20
+ '\u{1F62D}', '\u{1F913}', '\u{1F47B}', '\u{1F468}\u200D\u{1F4BB}', '\u{1F440}',
21
+ '\u{1F383}', '\u{1F648}', '\u{1F607}', '\u{1F628}', '\u{1F91D}', '\u270D',
22
+ '\u{1F917}', '\u{1FAE1}', '\u{1F385}', '\u{1F384}', '\u2603', '\u{1F485}',
23
+ '\u{1F92A}', '\u{1F5FF}', '\u{1F192}', '\u{1F498}', '\u{1F649}', '\u{1F984}',
24
+ '\u{1F618}', '\u{1F48A}', '\u{1F64A}', '\u{1F60E}', '\u{1F47E}',
25
+ '\u{1F937}\u200D\u2642', '\u{1F937}', '\u{1F937}\u200D\u2640', '\u{1F621}',
26
+ ]);
27
+ export function isTelegramReactionEmoji(emoji) {
28
+ return TELEGRAM_REACTION_EMOJI.has(emoji);
29
+ }
30
+ /**
31
+ * Coerce an emoji into something Telegram will accept, or null to skip.
32
+ *
33
+ * Falls back rather than refusing: a bad config value should cost the reaction's
34
+ * expressiveness, not the acknowledgement itself. Returning null would silently
35
+ * turn the feature off for that user.
36
+ */
37
+ export function coerceTelegramReaction(emoji, fallback = '\u{1F440}') {
38
+ if (isTelegramReactionEmoji(emoji))
39
+ return emoji;
40
+ return isTelegramReactionEmoji(fallback) ? fallback : null;
41
+ }
42
+ /** A reaction is decoration on top of the answer; it must never be the reason a
43
+ * shutdown hangs. Tight enough that remove+apply stays inside a drain deadline. */
44
+ export const TELEGRAM_REACTION_TIMEOUT_MS = 2500;
45
+ /** Same reasoning for the notice: a stuck cleanup must not hold shutdown open. */
46
+ export const TELEGRAM_NOTICE_TIMEOUT_MS = 5000;
47
+ /**
48
+ * Set (or clear) this bot's reaction on a message.
49
+ *
50
+ * Non-premium bots may set ONE reaction per message, so the array is length 0 or
51
+ * 1 and never more. An empty array clears. grammY throws on API failure, which is
52
+ * the reject-on-vendor-failure contract AckTransport requires.
53
+ */
54
+ export async function setTelegramReaction(api, chatId, messageId, emoji, options = {}) {
55
+ const timeoutMs = options.timeoutMs ?? TELEGRAM_REACTION_TIMEOUT_MS;
56
+ // Both bounds matter and they mean different things: the caller's signal is a
57
+ // lifecycle cancellation (shutdown), the timeout is protection against a
58
+ // vendor call that simply never answers.
59
+ const signal = options.signal
60
+ ? AbortSignal.any([options.signal, AbortSignal.timeout(timeoutMs)])
61
+ : AbortSignal.timeout(timeoutMs);
62
+ await api.raw.setMessageReaction({
63
+ chat_id: chatId,
64
+ message_id: messageId,
65
+ reaction: emoji ? [{ type: 'emoji', emoji }] : [],
66
+ }, signal);
67
+ }
68
+ /**
69
+ * The ACK transport for a Telegram message.
70
+ *
71
+ * Exported so production and tests build the SAME seam. A test that defines its
72
+ * own adapter proves only its own adapter: it would stay green if the wiring
73
+ * later picked remove-then-add or dropped the allowlist coercion.
74
+ *
75
+ * `replace` is a vendor fact, not a preference — Telegram swaps a bot's reaction
76
+ * atomically, so clearing first would show an empty state and cost a request.
77
+ */
78
+ export function createTelegramAckTransport(api, chatId, messageId, options = {}) {
79
+ return {
80
+ mode: 'replace',
81
+ apply: async (emoji) => {
82
+ await setTelegramReaction(api, chatId, messageId, emoji, options);
83
+ },
84
+ remove: async () => {
85
+ // An empty reaction list is how Telegram clears; there is no
86
+ // dedicated remove call.
87
+ await setTelegramReaction(api, chatId, messageId, null, options);
88
+ },
89
+ coerce: (emoji) => coerceTelegramReaction(emoji),
90
+ };
91
+ }
92
+ /**
93
+ * The queue-notice transport for a posted Telegram message.
94
+ *
95
+ * Exported for the same reason as the ACK transport: production and tests must
96
+ * drive the SAME binding, or a test proves only its own fixture.
97
+ *
98
+ * Both calls forward the signal grammY takes positionally. deleteMessage has a
99
+ * 48-hour limit and needs permission in groups; both failures are harmless here,
100
+ * since the worst case is the stale notice this whole unit exists to remove.
101
+ */
102
+ export function createTelegramNoticeTransport(api, chatId, messageId, timeoutMs = TELEGRAM_NOTICE_TIMEOUT_MS) {
103
+ // A per-call timeout is composed even when the caller supplies nothing.
104
+ // QueueNotice pins whichever signal the FIRST close receives, and ordinary
105
+ // delivery and the 5-minute timer both close without one — so relying on the
106
+ // shutdown drain to supply it would leave the common path on grammY's
107
+ // 500-second default.
108
+ //
109
+ // The duration is a parameter purely so a test can prove the timeout FIRES
110
+ // without spending five real seconds waiting for it. A wall-clock wait that
111
+ // long is also what made the assertion flaky on CI, where the runner tore
112
+ // the event loop down before the timer landed.
113
+ const bounded = (signal) => {
114
+ const timeout = AbortSignal.timeout(timeoutMs);
115
+ return signal ? AbortSignal.any([signal, timeout]) : timeout;
116
+ };
117
+ return {
118
+ delete: async (signal) => {
119
+ await api.deleteMessage(chatId, messageId, bounded(signal));
120
+ },
121
+ edit: async (text, signal) => {
122
+ await api.editMessageText(chatId, messageId, text, undefined, bounded(signal));
123
+ },
124
+ };
125
+ }
126
+ //# sourceMappingURL=reactions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reactions.js","sourceRoot":"","sources":["../../../src/telegram/reactions.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cli-jaw",
3
- "version": "2.17.9",
3
+ "version": "2.17.10",
4
4
  "description": "Personal AI assistant powered by Pi, Antigravity, AI-E, Claude, Claude E, Codex, Codex App, Cursor, Grok, Kiro, OpenCode, and Copilot — Web, Terminal, Telegram, and Discord interfaces with 107 built-in skills",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -156,6 +156,7 @@
156
156
  "cmd.runtimeSelectionInstanceWide": "The CLI and model are shared by every session on this instance. Change them from the instance web settings.",
157
157
  "tg.promptUnsupported": "(Not supported on Telegram)",
158
158
  "tg.queued": "Added to queue (#{count})",
159
+ "tg.queueExpired": "The queued turn timed out",
159
160
  "tg.imageCaption": "[📷 Image] {caption}",
160
161
  "tg.imageFail": "Image processing failed: {msg}",
161
162
  "tg.fileFail": "File processing failed: {msg}",
@@ -154,6 +154,7 @@
154
154
  "cmd.runtimeSelectionInstanceWide": "CLI とモデルの選択はこのインスタンスの全セッションで共有されます。インスタンスのウェブ設定から変更してください。",
155
155
  "tg.promptUnsupported": "(Telegram では未対応)",
156
156
  "tg.queued": "キューに追加しました(#{count})",
157
+ "tg.queueExpired": "キューの待機時間が超過しました",
157
158
  "tg.imageCaption": "[📷 画像] {caption}",
158
159
  "tg.imageFail": "画像処理に失敗しました: {msg}",
159
160
  "tg.fileFail": "ファイル処理に失敗しました: {msg}",
@@ -156,6 +156,7 @@
156
156
  "cmd.runtimeSelectionInstanceWide": "CLI와 모델 선택은 이 인스턴스의 모든 세션이 함께 씁니다. 인스턴스 웹 설정에서 바꿔주세요.",
157
157
  "tg.promptUnsupported": "(Telegram에서 미지원)",
158
158
  "tg.queued": "대기열에 추가됨 ({count}번째)",
159
+ "tg.queueExpired": "대기 시간이 초과되었습니다",
159
160
  "tg.imageCaption": "[📷 이미지] {caption}",
160
161
  "tg.imageFail": "이미지 처리 실패: {msg}",
161
162
  "tg.fileFail": "파일 처리 실패: {msg}",
@@ -154,6 +154,7 @@
154
154
  "cmd.runtimeSelectionInstanceWide": "CLI 与模型选择由本实例的所有会话共用。请在实例网页设置中修改。",
155
155
  "tg.promptUnsupported": "(Telegram 不支持)",
156
156
  "tg.queued": "已加入队列(第 {count} 个)",
157
+ "tg.queueExpired": "排队等待已超时",
157
158
  "tg.imageCaption": "[📷 图片] {caption}",
158
159
  "tg.imageFail": "图片处理失败:{msg}",
159
160
  "tg.fileFail": "文件处理失败:{msg}",
@@ -86,6 +86,11 @@ const ALLOWLIST = [
86
86
  { contains: "persist failed:", why: "a Node.js Error.message from fs write, not a credential" },
87
87
  { contains: 'await ctx.reply(syncText)', why: 'syncText is masked where it is built' },
88
88
  { contains: 'Chat ID: <code>', why: 'a chat id the user just asked for' },
89
+ // Queue-notice lifecycle (#412). Both carry text this repo built, not text
90
+ // that passed through a credential-bearing surface.
91
+ { contains: "t('tg.queued'", why: 'a locale string plus a pending count' },
92
+ { contains: 'const posted = await msg.reply(', why: 'the queue notice: a locale string plus a pending count' },
93
+ { contains: 'api.editMessageText(chatId, messageId, text', why: 'text is the notice expiry constant supplied at createQueueNotice' },
89
94
  ];
90
95
 
91
96
  /**