@wrongstack/telegram 0.286.0 → 0.289.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/README.md CHANGED
@@ -7,7 +7,7 @@ Send messages, receive instructions, get notified when long tasks finish.
7
7
 
8
8
  - **`telegram_read`** — Agent reads incoming Telegram messages (newest first, filtered by chat, with ack support)
9
9
  - **`telegram_send`** — Agent sends messages via Telegram (HTML formatting, confirm permission)
10
- - **System prompt injection** — Unread messages appear in the agent's system prompt so it sees them naturally
10
+ - **Metadata-only inbox notice** — The system prompt reports only an unread count; message content stays behind the explicit `telegram_read` tool boundary
11
11
  - **Slash commands** — `/telegram`, `/telegram-health`, `/telegram:send`, `/telegram:chatid` in the TUI
12
12
  - **Event notifications** — Session end summaries and long tool completions forwarded to Telegram
13
13
  - **Allowlist filtering** — Restrict which users/chats can interact with the bot
@@ -24,16 +24,7 @@ Message [@BotFather](https://t.me/BotFather) on Telegram:
24
24
 
25
25
  Copy the token (looks like `123456789:ABCdef...`).
26
26
 
27
- ### 2. Get your chat ID
28
-
29
- Message your new bot, then visit:
30
- ```
31
- https://api.telegram.org/bot<YOUR_TOKEN>/getUpdates
32
- ```
33
-
34
- Find your `chat.id` in the response.
35
-
36
- ### 3. Enable the official plugin
27
+ ### 2. Enable the official plugin
37
28
 
38
29
  ```bash
39
30
  wstack plugin install telegram
@@ -51,9 +42,17 @@ CLI package, add it like a normal public package:
51
42
  npm install @wrongstack/telegram
52
43
  ```
53
44
 
54
- ### 4. Configure
45
+ ### 3. Pair a private chat securely
46
+
47
+ Run `/telegram-setup` in WrongStack. Enter the bot token in the masked prompt,
48
+ message the bot once from the private Telegram account you want to pair, then
49
+ explicitly select that discovered identity. WrongStack does not print or ask
50
+ you to open a credential-bearing Bot API URL.
51
+
52
+ ### 4. Configure manually (optional)
55
53
 
56
- In `~/.wrongstack/config.json` or `.wrongstack/config.json`:
54
+ The setup command writes this configuration securely. For custom hosts, the
55
+ equivalent shape in `~/.wrongstack/config.json` or `.wrongstack/config.json` is:
57
56
 
58
57
  ```jsonc
59
58
  {
@@ -65,7 +64,10 @@ In `~/.wrongstack/config.json` or `.wrongstack/config.json`:
65
64
  "telegram": {
66
65
  "botToken": "123456789:ABCdefGHIjkl...",
67
66
  "notifyChatId": "987654321",
67
+ "inboundMode": "paired",
68
68
  "allowedUsers": [987654321],
69
+ "allowedChats": [987654321],
70
+ "allowedOutboundChats": [987654321],
69
71
  "notifyOnSessionEnd": true,
70
72
  "longToolThresholdMs": 30000,
71
73
  "pollIntervalSec": 2
@@ -87,9 +89,13 @@ the Telegram options.
87
89
  | Field | Type | Required | Default | Description |
88
90
  |---|---|---|---|---|
89
91
  | `botToken` | `string` | **yes** | — | Bot token from @BotFather |
90
- | `notifyChatId` | `string \| number` | no | — | Default chat for outgoing messages and notifications |
91
- | `allowedUsers` | `(string \| number)[]` | no | `[]` | User IDs allowed to interact. Empty = all allowed |
92
- | `allowedChats` | `(string \| number)[]` | no | `[]` | Chat IDs the bot reads from. Empty = all allowed |
92
+ | `notifyChatId` | `string \| number` | no | — | Default chat for outgoing messages and notifications; it is also the paired chat when `inboundMode` is `paired` |
93
+ | `inboundMode` | `disabled \| paired \| allowlist \| public` | no | `disabled` | Inbound authorization policy. `public` must be selected explicitly. |
94
+ | `allowedUsers` | `(string \| number)[]` | no | `[]` | Immutable user IDs accepted by paired/allowlist authorization and remote approvals |
95
+ | `allowedChats` | `(string \| number)[]` | no | `[]` | Chat IDs accepted when `inboundMode` is `allowlist` |
96
+ | `allowedOutboundChats` | `(string \| number)[]` | no | `[]` | Additional explicitly trusted outbound targets beyond `notifyChatId` |
97
+ | `allowGroupChats` | `boolean` | no | `false` | Let `/telegram-settings chat` select a negative group/channel ID as an outbound-only target |
98
+ | `allowGroupApprovals` | `boolean` | no | `false` | Permit approvals in a group target; still requires explicit `allowedUsers` identity binding |
93
99
  | `pollIntervalSec` | `number` | no | `2` | How often to poll Telegram for new messages (1–60) |
94
100
  | `notifyOnSessionEnd` | `boolean` | no | `false` | Send token usage summary when a session ends |
95
101
  | `longToolThresholdMs` | `number` | no | `30000` | Notify when a tool runs longer than this (ms). `0` = off |
@@ -163,8 +169,8 @@ Message text supports Telegram HTML: `<b>bold</b>`, `<i>italic</i>`, `<code>mono
163
169
 
164
170
  1. Bot polls Telegram every N seconds via `getUpdates`
165
171
  2. Incoming messages go into a circular buffer (50 max)
166
- 3. A system prompt contributor injects unread messages so the agent sees them
167
- 4. Agent reads with `telegram_read`, responds with `telegram_send`
172
+ 3. A system prompt contributor exposes only the bounded unread count
173
+ 4. Agent explicitly reads content with `telegram_read`, then responds with `telegram_send`
168
174
  5. Custom event `telegram:message_received` fires for TUI panels / other plugins
169
175
 
170
176
  ## Events
@@ -0,0 +1,119 @@
1
+ export interface TelegramApiUser {
2
+ id: number;
3
+ is_bot: boolean;
4
+ first_name: string;
5
+ username?: string | undefined;
6
+ }
7
+ export type TelegramApiChatType = 'private' | 'group' | 'supergroup' | 'channel';
8
+ export interface TelegramApiChat {
9
+ id: number;
10
+ type: TelegramApiChatType;
11
+ title?: string | undefined;
12
+ username?: string | undefined;
13
+ }
14
+ export interface TelegramApiMessage {
15
+ message_id: number;
16
+ from?: TelegramApiUser | undefined;
17
+ chat: TelegramApiChat;
18
+ date: number;
19
+ text?: string | undefined;
20
+ }
21
+ export interface TelegramApiCallbackQuery {
22
+ id: string;
23
+ from?: TelegramApiUser | undefined;
24
+ message?: {
25
+ message_id: number;
26
+ chat: TelegramApiChat;
27
+ } | undefined;
28
+ data?: string | undefined;
29
+ }
30
+ export interface TelegramApiUpdate {
31
+ update_id: number;
32
+ message?: TelegramApiMessage | undefined;
33
+ edited_message?: TelegramApiMessage | undefined;
34
+ callback_query?: TelegramApiCallbackQuery | undefined;
35
+ }
36
+ export interface TelegramInlineKeyboardButton {
37
+ text: string;
38
+ callback_data: string;
39
+ }
40
+ export type TelegramApiClientErrorKind = 'network' | 'http' | 'parse' | 'api';
41
+ export declare abstract class TelegramApiClientError extends Error {
42
+ readonly kind: TelegramApiClientErrorKind;
43
+ readonly method: string;
44
+ protected constructor(kind: TelegramApiClientErrorKind, method: string, message: string);
45
+ }
46
+ export declare class TelegramNetworkError extends TelegramApiClientError {
47
+ readonly detail: string;
48
+ readonly aborted: boolean;
49
+ constructor(method: string, detail: string, aborted?: boolean);
50
+ }
51
+ export declare class TelegramHttpError extends TelegramApiClientError {
52
+ readonly status: number;
53
+ constructor(method: string, status: number, statusText?: string | undefined);
54
+ }
55
+ export declare class TelegramResponseParseError extends TelegramApiClientError {
56
+ constructor(method: string, detail: string);
57
+ }
58
+ export declare class TelegramBotApiError extends TelegramApiClientError {
59
+ readonly errorCode?: number | undefined;
60
+ readonly httpStatus?: number | undefined;
61
+ readonly description: string;
62
+ readonly retryAfterSeconds?: number | undefined;
63
+ readonly migrateToChatId?: number | undefined;
64
+ constructor(method: string, opts: {
65
+ errorCode?: number | undefined;
66
+ httpStatus?: number | undefined;
67
+ description: string;
68
+ retryAfterSeconds?: number | undefined;
69
+ migrateToChatId?: number | undefined;
70
+ });
71
+ }
72
+ export interface RetryDecision {
73
+ /** Whether to retry the request. */
74
+ retry: boolean;
75
+ /** Milliseconds to wait before retrying. 0 when retry is false. */
76
+ delayMs: number;
77
+ }
78
+ /**
79
+ * Classify a caught error and decide whether to retry, and how long to wait.
80
+ * @param err The error thrown by api-client methods.
81
+ * @param attempt 1-based attempt counter.
82
+ * @returns A RetryDecision.
83
+ */
84
+ export declare function classifyRetry(err: unknown, attempt: number): RetryDecision;
85
+ type TelegramFetch = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
86
+ export interface TelegramApiClientOptions {
87
+ token: string;
88
+ /** Override used by deterministic tests or Bot API proxies. */
89
+ apiRoot?: string | undefined;
90
+ /** Optional transport injection. Defaults to globalThis.fetch at call time. */
91
+ fetch?: TelegramFetch | undefined;
92
+ }
93
+ export interface TelegramRequestOptions {
94
+ signal?: AbortSignal | undefined;
95
+ }
96
+ export interface TelegramGetUpdatesOptions extends TelegramRequestOptions {
97
+ deadlineMs?: number | undefined;
98
+ offset: number;
99
+ timeoutSeconds: number;
100
+ }
101
+ /** Build the one canonical, token-bearing Bot API base URL. Never log this value. */
102
+ export declare function buildTelegramBotApiBaseUrl(token: string, apiRoot?: string): string;
103
+ export declare function abortableSleep(ms: number, signal?: AbortSignal | undefined): Promise<void>;
104
+ export declare class TelegramApiClient {
105
+ readonly safeBaseUrl: string;
106
+ private readonly token;
107
+ private readonly baseUrl;
108
+ private readonly fetchOverride?;
109
+ constructor(opts: TelegramApiClientOptions);
110
+ getMe(opts?: TelegramRequestOptions): Promise<TelegramApiUser>;
111
+ getUpdates(opts: TelegramGetUpdatesOptions): Promise<TelegramApiUpdate[]>;
112
+ sendMessage(chatId: string | number, text: string, opts?: TelegramRequestOptions): Promise<TelegramApiMessage>;
113
+ sendMessageWithKeyboard(chatId: string | number, text: string, buttons: readonly TelegramInlineKeyboardButton[], opts?: TelegramRequestOptions): Promise<TelegramApiMessage>;
114
+ answerCallbackQuery(callbackQueryId: string, text: string, showAlert: boolean, opts?: TelegramRequestOptions): Promise<boolean>;
115
+ private request;
116
+ private redact;
117
+ }
118
+ export {};
119
+ //# sourceMappingURL=api-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { Logger } from '@wrongstack/core';
2
+ import type { TelegramApiMessage } from './api-client.js';
3
+ import type { TelegramBot, TelegramBotResponse } from './bot.js';
4
+ export interface BotOutboundOptions {
5
+ readonly bot: TelegramBot;
6
+ /** Pass-through logger (defaults to bot's internal logger via the bot's debug hook). */
7
+ readonly log: Logger;
8
+ /** Optional override; defaults to 32 entries per chat. */
9
+ readonly maxPerChat?: number;
10
+ /** Optional override; defaults to 4 concurrent sends. */
11
+ readonly maxConcurrency?: number;
12
+ }
13
+ export declare class TelegramBotOutbound {
14
+ #private;
15
+ constructor(opts: BotOutboundOptions);
16
+ /** Manual send (telegram_send tool, /telegram:send): never silently dropped. */
17
+ sendManual(chatId: string | number, text: string): Promise<TelegramBotResponse<TelegramApiMessage>>;
18
+ /**
19
+ * Notification send (session ended, long tool, delegate): fire-and-forget.
20
+ * The returned promise resolves as soon as the queue accepts the entry;
21
+ * downstream send failures are logged and counted but not surfaced.
22
+ */
23
+ enqueueNotification(chatId: string | number, text: string): void;
24
+ stats(): import("./outbound-queue.js").OutboundQueueStats;
25
+ stop(): Promise<void>;
26
+ }
27
+ //# sourceMappingURL=bot-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bot-queue.d.ts","sourceRoot":"","sources":["../src/bot-queue.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAC1D,OAAO,KAAK,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAC;AAGjE,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B,wFAAwF;IACxF,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,0DAA0D;IAC1D,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,yDAAyD;IACzD,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,qBAAa,mBAAmB;;IAM9B,YAAY,IAAI,EAAE,kBAAkB,EAenC;IAED,gFAAgF;IAC1E,UAAU,CACd,MAAM,EAAE,MAAM,GAAG,MAAM,EACvB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,mBAAmB,CAAC,kBAAkB,CAAC,CAAC,CASlD;IAED;;;;OAIG;IACH,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAW/D;IAED,KAAK,qDAEJ;IAEK,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAG1B;CACF"}
package/dist/bot.d.ts CHANGED
@@ -1,29 +1,10 @@
1
1
  import type { Logger } from '@wrongstack/core';
2
+ import { type TelegramApiMessage } from './api-client.js';
3
+ import type { OffsetStore } from './offset-store.js';
2
4
  import type { PollLock } from './poll-lock.js';
3
- interface TgUser {
4
- id: number;
5
- is_bot: boolean;
6
- first_name: string;
7
- username?: string | undefined;
8
- }
9
- interface TgChat {
10
- id: number;
11
- type: 'private' | 'group' | 'supergroup' | 'channel';
12
- title?: string | undefined;
13
- username?: string | undefined;
14
- }
15
- interface TgMessage {
16
- message_id: number;
17
- from?: TgUser | undefined;
18
- chat: TgChat;
19
- date: number;
20
- text?: string | undefined;
21
- }
22
- interface TgResponse<T> {
23
- ok: boolean;
24
- result?: T | undefined;
25
- description?: string | undefined;
26
- error_code?: number | undefined;
5
+ export interface TelegramBotResponse<T> {
6
+ ok: true;
7
+ result: T;
27
8
  }
28
9
  export interface TelegramIncomingMessage {
29
10
  messageId: number;
@@ -34,6 +15,22 @@ export interface TelegramIncomingMessage {
34
15
  text: string;
35
16
  timestamp: number;
36
17
  }
18
+ export interface TelegramApprovalResult {
19
+ approved: boolean;
20
+ fromUser: string;
21
+ fromUserId?: number | undefined;
22
+ }
23
+ export interface TelegramApprovalRequestInput {
24
+ requestId: string;
25
+ sessionId: string;
26
+ expectedChatId: string | number;
27
+ expectedUserIds: readonly (string | number)[];
28
+ /** Group/supergroup callbacks are rejected unless this was explicitly enabled. */
29
+ allowGroup: boolean;
30
+ expiresAt: number;
31
+ /** Cancels the request when its owning tool execution is aborted. */
32
+ signal?: AbortSignal | undefined;
33
+ }
37
34
  export interface TelegramBotOptions {
38
35
  token: string;
39
36
  pollIntervalSec: number;
@@ -45,11 +42,11 @@ export interface TelegramBotOptions {
45
42
  /** Called for each incoming message that passes allowlist checks. */
46
43
  onMessage(msg: TelegramIncomingMessage): void;
47
44
  /**
48
- * Optional path to a file that stores the polling offset. When provided,
49
- * the offset is persisted on every successful poll and restored on startup,
50
- * preventing message replay after crashes or restarts.
45
+ * Optional typed offset store. When provided, the polling offset is persisted
46
+ * atomically on every successful poll and restored on startup, preventing
47
+ * message replay after crashes or restarts.
51
48
  */
52
- offsetStoragePath?: string | undefined;
49
+ offsetStore?: OffsetStore | undefined;
53
50
  /**
54
51
  * Optional cross-process single-poller lock. Telegram allows one
55
52
  * `getUpdates` consumer per token; when another wstack instance holds the
@@ -61,9 +58,7 @@ export interface TelegramBotOptions {
61
58
  standbyRetryMs?: number | undefined;
62
59
  }
63
60
  export declare class TelegramBot {
64
- private readonly baseUrl;
65
- /** Base URL with token redacted, safe to use in log calls. */
66
- private readonly safeBaseUrl;
61
+ private readonly api;
67
62
  private readonly pollIntervalMs;
68
63
  private readonly allowedUsers;
69
64
  private readonly allowedChats;
@@ -84,8 +79,8 @@ export declare class TelegramBot {
84
79
  private static readonly CONFLICT_BACKOFF_AFTER;
85
80
  private static readonly CONFLICT_POLL_MS;
86
81
  private _startedAt;
87
- /** If set, the offset is persisted here after each successful poll. */
88
- private readonly offsetStoragePath?;
82
+ /** Typed offset store for atomic polling-cursor persistence. */
83
+ private readonly offsetStore?;
89
84
  /** Single-poller election across wstack instances sharing this token. */
90
85
  private readonly lock?;
91
86
  private readonly standbyRetryMs;
@@ -118,7 +113,7 @@ export declare class TelegramBot {
118
113
  /** Drop messages older than the given message ID from the buffer. */
119
114
  acknowledge(lastMessageId: number): number;
120
115
  get bufferCount(): number;
121
- sendMessage(chatId: string | number, text: string): Promise<TgResponse<TgMessage>>;
116
+ sendMessage(chatId: string | number, text: string, signal?: AbortSignal | undefined): Promise<TelegramBotResponse<TelegramApiMessage>>;
122
117
  /**
123
118
  * Send a message that has up to one row of inline buttons (Telegram's
124
119
  * `inline_keyboard`). Used by `telegram_approve` to present a
@@ -128,28 +123,26 @@ export declare class TelegramBot {
128
123
  sendMessageWithKeyboard(chatId: string | number, text: string, buttons: Array<{
129
124
  text: string;
130
125
  callback_data: string;
131
- }>): Promise<TgResponse<TgMessage>>;
132
- health(): Promise<{
126
+ }>, signal?: AbortSignal | undefined): Promise<TelegramBotResponse<TelegramApiMessage>>;
127
+ health(signal?: AbortSignal | undefined): Promise<{
133
128
  ok: boolean;
134
129
  username?: string | undefined;
135
130
  error?: string | undefined;
136
131
  }>;
137
132
  private schedulePoll;
138
133
  private poll;
139
- private processMessage;
140
134
  /**
141
- * Handle an inbound `callback_query` update: route it to a registered
142
- * waiter (if any), and acknowledge it via `answerCallbackQuery` so the
143
- * client stops spinning. Telegram requires the answer within 10 s.
135
+ * Apply the inbound identity policy to every update type. A non-empty set is
136
+ * a mandatory constraint: missing identity fails closed instead of bypassing
137
+ * the allowlist. An empty set leaves that identity dimension unrestricted.
144
138
  */
139
+ private inboundDenialReason;
140
+ private processMessage;
145
141
  /**
146
- * Resolve any pending waiter for `key` with a `{ approved: false, fromUser }`
147
- * value, regardless of why the callback was rejected (allowlist, shutdown,
148
- * etc.). Returns true if a waiter was found and resolved, false otherwise.
149
- * This helper centralizes the race-safe `delete → resolve` pattern in one
150
- * place so the deny and shutdown paths don't drift out of sync.
142
+ * Resolve a pending approval request exactly once and record its terminal
143
+ * state before removing it from the live registry.
151
144
  */
152
- private rejectWaiter;
145
+ private settleApproval;
153
146
  private dispatchCallback;
154
147
  /**
155
148
  * POST /answerCallbackQuery for a callback. Best-effort: failures are
@@ -159,34 +152,24 @@ export declare class TelegramBot {
159
152
  */
160
153
  private answerCallback;
161
154
  /**
162
- * Register a waiter for a callback_query whose `data` field equals `key`.
163
- * Resolves with `{ approved, fromUser }` when a matching press arrives, or
164
- * with `{ approved: false, fromUser: 'timeout' }` after `timeoutMs`.
165
- *
166
- * Callers are responsible for not registering the same key twice — a
167
- * second `awaitCallback` for an in-flight key is undefined.
155
+ * Register one approval request before its prompt is sent. The returned
156
+ * promise owns the request's only timer and resolves on one terminal event.
168
157
  */
169
- awaitCallback(key: string, timeoutMs: number): Promise<{
170
- approved: boolean;
171
- fromUser: string;
172
- }>;
158
+ awaitApproval(input: TelegramApprovalRequestInput): Promise<TelegramApprovalResult>;
159
+ /**
160
+ * Attach the Bot API response's prompt message ID to an existing request.
161
+ * Any callback that arrived during the send is replayed against the fully
162
+ * bound identity without allocating a second waiter or timer.
163
+ */
164
+ bindApprovalPrompt(requestId: string, promptMessageId: number): boolean;
165
+ /** Cancel a request that cannot reach a valid terminal callback. */
166
+ cancelApproval(requestId: string, fromUser?: string): boolean;
173
167
  private loadOffset;
174
168
  private saveOffset;
175
169
  }
176
- /**
177
- * Truncate text to fit Telegram's 4096-char message limit.
178
- * Preserves semantic boundaries in this priority order:
179
- * 1. Paragraph break (double newline)
180
- * 2. Sentence break (. ! ? followed by space/newline)
181
- * 3. Word break (space)
182
- * 4. Hard cut with ellipsis
183
- *
184
- * When a clean boundary is found, appends "…" to signal intentional truncation.
185
- */
186
170
  export declare function truncateForTelegram(text: string, maxLen?: number): string;
187
171
  /**
188
172
  * Escape HTML special chars for Telegram's HTML parse mode.
189
173
  */
190
174
  export declare function escapeHtml(text: string): string;
191
- export {};
192
175
  //# sourceMappingURL=bot.d.ts.map
package/dist/bot.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bot.d.ts","sourceRoot":"","sources":["../src/bot.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"bot.d.ts","sourceRoot":"","sources":["../src/bot.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ import type { TelegramPluginConfig } from './config.js';
2
+ /**
3
+ * Keys the Telegram plugin can apply live without restarting the bot.
4
+ *
5
+ * Rationale per key:
6
+ * - `inboundMode` / `allowedUsers` / `allowedChats` — `processMessage`
7
+ * and `dispatchCallback` re-read the allowlist on every inbound call,
8
+ * so toggling the mode or editing the lists takes effect immediately.
9
+ * - `notifyOnSessionEnd` / `notifyOnDelegate` — re-evaluated on each
10
+ * event subscription; no in-flight state to restart.
11
+ * - `longToolThresholdMs` — pure threshold read inside the tool-notify
12
+ * handler; the next long-tool event uses the new value.
13
+ * - `maxMessageLength` / `allowedOutboundChats` — re-read on every
14
+ * `sendMessage` / outbound-target resolution, so the next send reflects
15
+ * the change.
16
+ * - `pollIntervalSec` — the poller reads this on each iteration of its
17
+ * wait loop; the next tick uses the new interval.
18
+ * - `outboundQueuePerChat` / `outboundQueueConcurrency` — the outbound
19
+ * queue reads these only at construction. They are documented as
20
+ * hot-reload in spirit (the queue can be re-evaluated) but the live
21
+ * queue owns a stable copy. Marking them HOT would silently mislead,
22
+ * so they are intentionally EXCLUDED until the queue supports
23
+ * mid-flight reconfiguration. See `restart-required` below.
24
+ */
25
+ export declare const HOT_RELOAD_KEYS: Set<keyof TelegramPluginConfig>;
26
+ /**
27
+ * Keys that require restarting the Telegram plugin (or wstack itself)
28
+ * for the new value to take effect.
29
+ *
30
+ * - `botToken` — every Telegram transport object is constructed once with
31
+ * the token baked into the base URL; changing it requires a new
32
+ * `TelegramBot` instance.
33
+ * - `notifyChatId` — bound into the optional-allowedUsers fallback at
34
+ * setup (`inboundAllowlist`) and read by every notification callback;
35
+ * the inbound allowlist does not rebuild without a setup re-run.
36
+ * - `offsetStoragePath` — the `OffsetStore` is opened once at setup;
37
+ * changing the path would orphan any persisted cursor.
38
+ * - `singleInstanceLock` — the cross-process `PollLock` is acquired at
39
+ * startup; flipping this flag requires a fresh setup to acquire /
40
+ * release the right lock file.
41
+ * - `outboundQueuePerChat` / `outboundQueueConcurrency` — the
42
+ * `OutboundQueue` reads these at construction only.
43
+ */
44
+ export declare const RESTART_REQUIRED_KEYS: Set<keyof TelegramPluginConfig>;
45
+ export type ConfigReloadClass = 'hot' | 'restart-required';
46
+ /**
47
+ * Classify a single key. Returns `'hot'` for keys in `HOT_RELOAD_KEYS`,
48
+ * `'restart-required'` for keys in `RESTART_REQUIRED_KEYS`, and
49
+ * `'restart-required'` as a conservative default for keys that are not
50
+ * registered in either set — preventing a new field from silently
51
+ * pretending to be hot-reloadable before the classifier is updated.
52
+ */
53
+ export declare function classifyReload(key: keyof TelegramPluginConfig): ConfigReloadClass;
54
+ /**
55
+ * Diff two configs and return the keys whose values differ, each tagged
56
+ * with its reload classification. Keys present in `previous` but absent in
57
+ * `next` (or vice versa) are reported as `'restart-required'` so a stale
58
+ * removal is never silently applied as a live change.
59
+ */
60
+ export declare function diffConfigKeys(previous: TelegramPluginConfig, next: TelegramPluginConfig): Array<{
61
+ key: keyof TelegramPluginConfig;
62
+ classification: ConfigReloadClass;
63
+ }>;
64
+ //# sourceMappingURL=config-classifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-classifier.d.ts","sourceRoot":"","sources":["../src/config-classifier.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAC;AAExD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,eAAO,MAAM,eAAe,iCAU1B,CAAC;AAEH;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,qBAAqB,iCAOhC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,KAAK,GAAG,kBAAkB,CAAC;AAE3D;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,oBAAoB,GAAG,iBAAiB,CAGjF;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,oBAAoB,EAC9B,IAAI,EAAE,oBAAoB,GACzB,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,oBAAoB,CAAC;IAAC,cAAc,EAAE,iBAAiB,CAAA;CAAE,CAAC,CAc/E"}
package/dist/config.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { PluginAPI } from '@wrongstack/core';
2
2
  export declare const PLUGIN_NAME = "telegram";
3
+ export type TelegramInboundMode = 'disabled' | 'paired' | 'allowlist' | 'public';
3
4
  export interface TelegramPluginConfig {
4
5
  /** Telegram Bot API token (from @BotFather). */
5
6
  botToken: string;
@@ -9,15 +10,17 @@ export interface TelegramPluginConfig {
9
10
  */
10
11
  notifyChatId?: string | number | undefined;
11
12
  /**
12
- * List of user/chat IDs allowed to interact with the bot.
13
- * Empty = allow all. Recommended to set in production.
13
+ * Controls who may send inbound messages to the bot. Defaults to `disabled`
14
+ * for new/unpaired configurations. Legacy configurations are migrated to
15
+ * `allowlist` when IDs exist, or `paired` when `notifyChatId` exists.
14
16
  */
17
+ inboundMode?: TelegramInboundMode | undefined;
18
+ /** List of user IDs accepted when `inboundMode` is `allowlist`. */
15
19
  allowedUsers?: Array<string | number> | undefined;
16
- /**
17
- * List of group/chat IDs the bot is allowed to read from.
18
- * Empty = allow all. Narrow this to prevent noise.
19
- */
20
+ /** List of chat IDs accepted when `inboundMode` is `allowlist`. */
20
21
  allowedChats?: Array<string | number> | undefined;
22
+ /** Additional trusted targets for outbound sends beyond `notifyChatId`. */
23
+ allowedOutboundChats?: Array<string | number> | undefined;
21
24
  /** Polling interval in seconds (default: 2). */
22
25
  pollIntervalSec?: number | undefined;
23
26
  /** Notify on Telegram when a session ends. */
@@ -43,6 +46,14 @@ export interface TelegramPluginConfig {
43
46
  * Set false only if this is guaranteed to be the sole consumer.
44
47
  */
45
48
  singleInstanceLock?: boolean | undefined;
49
+ /**
50
+ * Per-chat pending-message cap for the outbound queue. Older pending
51
+ * notification entries are dropped when this is exceeded; manual
52
+ * telegram_send entries surface the overflow as an error. Default: 32.
53
+ */
54
+ outboundQueuePerChat?: number | undefined;
55
+ /** Maximum concurrent outbound sends across all chats. Default: 4. */
56
+ outboundQueueConcurrency?: number | undefined;
46
57
  }
47
58
  export declare const DEFAULT_CONFIG: Required<Omit<TelegramPluginConfig, 'botToken' | 'notifyChatId' | 'offsetStoragePath'>>;
48
59
  export declare const telegramConfigSchema: {
@@ -58,6 +69,12 @@ export declare const telegramConfigSchema: {
58
69
  }[];
59
70
  description: string;
60
71
  };
72
+ inboundMode: {
73
+ type: string;
74
+ enum: ("allowlist" | "disabled" | "paired" | "public")[];
75
+ default: string;
76
+ description: string;
77
+ };
61
78
  allowedUsers: {
62
79
  type: string;
63
80
  items: {
@@ -76,6 +93,15 @@ export declare const telegramConfigSchema: {
76
93
  };
77
94
  description: string;
78
95
  };
96
+ allowedOutboundChats: {
97
+ type: string;
98
+ items: {
99
+ oneOf: {
100
+ type: string;
101
+ }[];
102
+ };
103
+ description: string;
104
+ };
79
105
  pollIntervalSec: {
80
106
  type: string;
81
107
  minimum: number;
@@ -101,8 +127,20 @@ export declare const telegramConfigSchema: {
101
127
  type: string;
102
128
  description: string;
103
129
  };
130
+ outboundQueuePerChat: {
131
+ type: string;
132
+ minimum: number;
133
+ maximum: number;
134
+ description: string;
135
+ };
136
+ outboundQueueConcurrency: {
137
+ type: string;
138
+ minimum: number;
139
+ maximum: number;
140
+ description: string;
141
+ };
104
142
  };
105
143
  required: string[];
106
144
  };
107
- export declare function readTelegramConfig(api: Pick<PluginAPI, 'config'>): Required<Omit<TelegramPluginConfig, 'notifyChatId' | 'offsetStoragePath'>> & Pick<TelegramPluginConfig, 'notifyChatId' | 'offsetStoragePath'>;
145
+ export declare function readTelegramConfig(api: Pick<PluginAPI, 'config'> & Partial<Pick<PluginAPI, 'log'>>): Required<Omit<TelegramPluginConfig, 'notifyChatId' | 'offsetStoragePath'>> & Pick<TelegramPluginConfig, 'notifyChatId' | 'offsetStoragePath'>;
108
146
  //# sourceMappingURL=config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,eAAO,MAAM,WAAW,aAAa,CAAC;AAEtC,MAAM,WAAW,oBAAoB;IACnC,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAC3C;;;OAGG;IACH,YAAY,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,SAAS,CAAC;IAClD;;;OAGG;IACH,YAAY,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,SAAS,CAAC;IAClD,gDAAgD;IAChD,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,8CAA8C;IAC9C,kBAAkB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACzC,iFAAiF;IACjF,mBAAmB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,mEAAmE;IACnE,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC1C;AAED,eAAO,MAAM,cAAc,EAAE,QAAQ,CAAC,IAAI,CAAC,oBAAoB,EAAE,UAAU,GAAG,cAAc,GAAG,mBAAmB,CAAC,CASlH,CAAC;AAEF,eAAO,MAAM,oBAAoB;;;;YAGjB,IAAI;YAAY,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;YAgBrC,IAAI;YACJ,OAAO;YACP,OAAO;YACP,WAAW;;;YAES,IAAI;;;YACH,IAAI;YAAa,OAAO;;;YAC3B,IAAI;;;YACJ,IAAI;YAAa,OAAO;YAAO,OAAO;;;YAExD,IAAI;YACJ,WAAW;;;;CAIhB,CAAC;AAEF,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,GAC7B,QAAQ,CAAC,IAAI,CAAC,oBAAoB,EAAE,cAAc,GAAG,mBAAmB,CAAC,CAAC,GAC3E,IAAI,CAAC,oBAAoB,EAAE,cAAc,GAAG,mBAAmB,CAAC,CAgBjE"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAElD,eAAO,MAAM,WAAW,aAAa,CAAC;AAEtC,MAAM,MAAM,mBAAmB,GAAG,UAAU,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,CAAC;AAIjF,MAAM,WAAW,oBAAoB;IACnC,gDAAgD;IAChD,QAAQ,EAAE,MAAM,CAAC;IACjB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAC3C;;;;OAIG;IACH,WAAW,CAAC,EAAE,mBAAmB,GAAG,SAAS,CAAC;IAC9C,mEAAmE;IACnE,YAAY,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,SAAS,CAAC;IAClD,mEAAmE;IACnE,YAAY,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,SAAS,CAAC;IAClD,2EAA2E;IAC3E,oBAAoB,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC,GAAG,SAAS,CAAC;IAC1D,gDAAgD;IAChD,eAAe,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACrC,8CAA8C;IAC9C,kBAAkB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACzC,iFAAiF;IACjF,mBAAmB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzC,6EAA6E;IAC7E,gBAAgB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACvC,mEAAmE;IACnE,gBAAgB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACtC;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACvC;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;IACzC;;;;OAIG;IACH,oBAAoB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1C,sEAAsE;IACtE,wBAAwB,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC/C;AAED,eAAO,MAAM,cAAc,EAAE,QAAQ,CACnC,IAAI,CAAC,oBAAoB,EAAE,UAAU,GAAG,cAAc,GAAG,mBAAmB,CAAC,CAc9E,CAAC;AAEF,eAAO,MAAM,oBAAoB;;;;YAGjB,IAAI;YAAY,WAAW;;;;;;;;;YAMrC,IAAI;YACJ,IAAI;YACJ,OAAO;YACP,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;YAmBX,IAAI;YACJ,OAAO;YACP,OAAO;YACP,WAAW;;;YAES,IAAI;;;YACH,IAAI;YAAa,OAAO;;;YAC3B,IAAI;;;YACJ,IAAI;YAAa,OAAO;YAAO,OAAO;;;YAExD,IAAI;YACJ,WAAW;;;YAIX,IAAI;YACJ,OAAO;YACP,OAAO;YACP,WAAW;;;YAGX,IAAI;YACJ,OAAO;YACP,OAAO;YACP,WAAW;;;;CAIhB,CAAC;AAEF,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,IAAI,CAAC,SAAS,EAAE,QAAQ,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,CAAC,CAAC,GAC/D,QAAQ,CAAC,IAAI,CAAC,oBAAoB,EAAE,cAAc,GAAG,mBAAmB,CAAC,CAAC,GAC3E,IAAI,CAAC,oBAAoB,EAAE,cAAc,GAAG,mBAAmB,CAAC,CAuBjE"}