grok-telegram-bot 2.5.0 → 2.6.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/CHANGELOG.md +71 -0
- package/README.md +5 -3
- package/docs/GROUP.md +39 -4
- package/package.json +4 -4
- package/src/app/types.ts +27 -1
- package/src/bot/auth.ts +5 -1
- package/src/bot/bot.ts +73 -4
- package/src/bot/chat-controller.ts +129 -0
- package/src/bot/group-memory.ts +192 -12
- package/src/bot/handlers/forum.ts +16 -6
- package/src/bot/handlers/message.ts +165 -25
- package/src/bot/handlers/photo.ts +4 -1
- package/src/bot/image-return.ts +4 -1
- package/src/bot/manager-context.ts +208 -0
- package/src/bot/manager-jobs.ts +142 -0
- package/src/bot/menu/ephemeral.ts +4 -1
- package/src/bot/prompt-anchor.ts +2 -3
- package/src/bot/prompt-content.ts +5 -0
- package/src/bot/scope.ts +9 -8
- package/src/bot/session-runtime.ts +663 -55
- package/src/bot/telegram-actions.ts +728 -38
- package/src/bot/telegram-bots.ts +2 -1
- package/src/bot/telegram-io.ts +4 -1
- package/src/forum/manager.ts +2 -1
- package/src/forum/thread.ts +33 -0
- package/src/render/manager-directive.ts +137 -0
- package/src/render/session-comment.ts +10 -0
- package/src/render/telegram-bridge.ts +118 -14
- package/src/sessions/history.ts +18 -0
- package/src/stream/streamer.ts +46 -10
package/src/stream/streamer.ts
CHANGED
|
@@ -18,6 +18,7 @@ import { estimateProgress } from "../render/progress-estimate.js";
|
|
|
18
18
|
import { stripTelegramActionFences } from "../render/telegram-bridge.js";
|
|
19
19
|
import { truncateMiddle } from "../render/truncate.js";
|
|
20
20
|
import { safeEdit, safeSend } from "../bot/telegram-io.js";
|
|
21
|
+
import { outboundThreadExtra } from "../forum/thread.js";
|
|
21
22
|
|
|
22
23
|
const SOFT_LIMIT = 3500;
|
|
23
24
|
/** Display budget for a thinking block (middle-truncated; session context keeps all). */
|
|
@@ -31,6 +32,18 @@ interface Seg {
|
|
|
31
32
|
toolId?: string;
|
|
32
33
|
}
|
|
33
34
|
|
|
35
|
+
export interface StreamerOptions {
|
|
36
|
+
/** Chat-like mode: drop thoughts/tools/plan; only stream agent prose. */
|
|
37
|
+
proseOnly?: boolean;
|
|
38
|
+
/** When false, never render a progress bar (manager chat). Default true. */
|
|
39
|
+
showProgressBar?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Pre-posted message id to edit in place (e.g. General "Thinking…" placeholder).
|
|
42
|
+
* Avoids a separate bubble when the first real tokens arrive.
|
|
43
|
+
*/
|
|
44
|
+
seedMessageId?: number;
|
|
45
|
+
}
|
|
46
|
+
|
|
34
47
|
export class ResponseStreamer {
|
|
35
48
|
private readonly segs: Seg[] = [];
|
|
36
49
|
private sealedIdx = 0;
|
|
@@ -54,6 +67,8 @@ export class ResponseStreamer {
|
|
|
54
67
|
* the progress bar when set — done / in-progress / pending steps.
|
|
55
68
|
*/
|
|
56
69
|
private planMarkdown: string | undefined;
|
|
70
|
+
private readonly proseOnly: boolean;
|
|
71
|
+
private readonly showProgressBar: boolean;
|
|
57
72
|
|
|
58
73
|
constructor(
|
|
59
74
|
private readonly api: Api,
|
|
@@ -68,7 +83,12 @@ export class ResponseStreamer {
|
|
|
68
83
|
private readonly turnStartedAt = Date.now(),
|
|
69
84
|
/** Forum topic thread — required so stream edits land in the right topic. */
|
|
70
85
|
private readonly messageThreadId?: number,
|
|
71
|
-
|
|
86
|
+
opts?: StreamerOptions,
|
|
87
|
+
) {
|
|
88
|
+
this.proseOnly = !!opts?.proseOnly;
|
|
89
|
+
this.showProgressBar = opts?.showProgressBar !== false;
|
|
90
|
+
if (opts?.seedMessageId !== undefined) this.liveId = opts.seedMessageId;
|
|
91
|
+
}
|
|
72
92
|
|
|
73
93
|
/** Replace the hashtag footer (used after a logical fork swaps the session id
|
|
74
94
|
* mid-turn, so the streamed response carries the NEW session's tags). */
|
|
@@ -76,6 +96,16 @@ export class ResponseStreamer {
|
|
|
76
96
|
this.footer = footer;
|
|
77
97
|
}
|
|
78
98
|
|
|
99
|
+
/** Seed/replace the live bubble id (General Thinking… placeholder). */
|
|
100
|
+
seedLiveMessage(messageId: number): void {
|
|
101
|
+
this.liveId = messageId;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Current live Telegram message id (for attaching suggestions after finalize). */
|
|
105
|
+
get liveMessageId(): number | undefined {
|
|
106
|
+
return this.liveId;
|
|
107
|
+
}
|
|
108
|
+
|
|
79
109
|
/** "\n\n<footer>" appended to every finished message bubble (e.g. hashtags). */
|
|
80
110
|
private footerSuffix(): string {
|
|
81
111
|
return this.footer ? `\n\n${this.footer}` : "";
|
|
@@ -94,6 +124,7 @@ export class ResponseStreamer {
|
|
|
94
124
|
* and notifying the owner on change. Agent markers are authoritative: once
|
|
95
125
|
* one arrives, the bot fallback stops contributing. */
|
|
96
126
|
private setProgressValue(pct: number, fromAgent: boolean): void {
|
|
127
|
+
if (!this.showProgressBar) return;
|
|
97
128
|
if (fromAgent) this.agentReported = true;
|
|
98
129
|
const next = Math.max(this.progress ?? 0, Math.round(pct));
|
|
99
130
|
if (next === this.progress) return;
|
|
@@ -108,7 +139,7 @@ export class ResponseStreamer {
|
|
|
108
139
|
/** Advance the fallback estimate from real activity signals, but only while
|
|
109
140
|
* the agent itself hasn't reported a value. No-op when fallback is off. */
|
|
110
141
|
private applyFallback(): void {
|
|
111
|
-
if (!this.fallbackEnabled || this.agentReported) return;
|
|
142
|
+
if (!this.showProgressBar || !this.fallbackEnabled || this.agentReported) return;
|
|
112
143
|
const est = estimateProgress({
|
|
113
144
|
toolCalls: this.toolCalls,
|
|
114
145
|
outputChars: this.outChars,
|
|
@@ -121,12 +152,13 @@ export class ResponseStreamer {
|
|
|
121
152
|
/** Called when the turn finishes successfully: if the agent never reported
|
|
122
153
|
* its own progress, fill the fallback bar to 100. No-op otherwise. */
|
|
123
154
|
completeFallback(): void {
|
|
124
|
-
if (!this.fallbackEnabled || this.agentReported) return;
|
|
155
|
+
if (!this.showProgressBar || !this.fallbackEnabled || this.agentReported) return;
|
|
125
156
|
this.setProgressValue(100, false);
|
|
126
157
|
}
|
|
127
158
|
|
|
128
159
|
private threadExtra(): Record<string, unknown> {
|
|
129
|
-
|
|
160
|
+
// Never send message_thread_id=1 (General) — Telegram rejects it.
|
|
161
|
+
return outboundThreadExtra(this.messageThreadId);
|
|
130
162
|
}
|
|
131
163
|
|
|
132
164
|
/** reply_parameters threading EVERY message of the turn to the user's prompt,
|
|
@@ -148,7 +180,7 @@ export class ResponseStreamer {
|
|
|
148
180
|
}
|
|
149
181
|
|
|
150
182
|
appendThought(text: string): void {
|
|
151
|
-
if (!text) return;
|
|
183
|
+
if (!text || this.proseOnly) return;
|
|
152
184
|
this.thoughtChars += text.length;
|
|
153
185
|
this.merge("think", text);
|
|
154
186
|
this.schedule();
|
|
@@ -159,7 +191,7 @@ export class ResponseStreamer {
|
|
|
159
191
|
* for ACP tool calls that stream progress/output under a stable toolCallId.
|
|
160
192
|
*/
|
|
161
193
|
addTool(rawMarkdown: string): void {
|
|
162
|
-
if (!rawMarkdown) return;
|
|
194
|
+
if (!rawMarkdown || this.proseOnly) return;
|
|
163
195
|
this.toolCalls += 1;
|
|
164
196
|
this.segs.push({ kind: "tool", text: rawMarkdown });
|
|
165
197
|
this.schedule();
|
|
@@ -172,6 +204,7 @@ export class ResponseStreamer {
|
|
|
172
204
|
*/
|
|
173
205
|
/** Replace the live plan board (or clear with empty/undefined). */
|
|
174
206
|
setPlan(markdown: string | undefined): void {
|
|
207
|
+
if (this.proseOnly) return;
|
|
175
208
|
const next = markdown?.trim() ? markdown.trim() : undefined;
|
|
176
209
|
if (next === this.planMarkdown) return;
|
|
177
210
|
this.planMarkdown = next;
|
|
@@ -179,7 +212,7 @@ export class ResponseStreamer {
|
|
|
179
212
|
}
|
|
180
213
|
|
|
181
214
|
upsertTool(toolId: string | undefined, rawMarkdown: string): void {
|
|
182
|
-
if (!rawMarkdown) return;
|
|
215
|
+
if (!rawMarkdown || this.proseOnly) return;
|
|
183
216
|
const id = (toolId || "").trim();
|
|
184
217
|
if (id) {
|
|
185
218
|
// Replace any existing segment with this id (newest first; includes rare
|
|
@@ -204,8 +237,9 @@ export class ResponseStreamer {
|
|
|
204
237
|
this.schedule();
|
|
205
238
|
}
|
|
206
239
|
|
|
240
|
+
/** True when agent prose/tools/thoughts were appended (not just a seed bubble). */
|
|
207
241
|
get hasOutput(): boolean {
|
|
208
|
-
return this.
|
|
242
|
+
return this.segs.some((s) => s.text.trim().length > 0);
|
|
209
243
|
}
|
|
210
244
|
|
|
211
245
|
async finalize(): Promise<void> {
|
|
@@ -213,6 +247,8 @@ export class ResponseStreamer {
|
|
|
213
247
|
if (this.timer) clearTimeout(this.timer);
|
|
214
248
|
this.timer = undefined;
|
|
215
249
|
await this.flush(true);
|
|
250
|
+
// Seeded Thinking… with zero agent text: leave the placeholder for the
|
|
251
|
+
// owner to replace (fallback reply) or keep while bridge results chain.
|
|
216
252
|
}
|
|
217
253
|
|
|
218
254
|
// ── internals ──────────────────────────────────────────────────────────────
|
|
@@ -251,8 +287,8 @@ export class ResponseStreamer {
|
|
|
251
287
|
// Live bubble: body → plan (always above progress) → progress bar → footer.
|
|
252
288
|
const parts: string[] = [];
|
|
253
289
|
if (base.trim()) parts.push(base);
|
|
254
|
-
if (this.planMarkdown) parts.push(this.planMarkdown);
|
|
255
|
-
if (this.progress !== undefined) parts.push(progressBar(this.progress));
|
|
290
|
+
if (!this.proseOnly && this.planMarkdown) parts.push(this.planMarkdown);
|
|
291
|
+
if (this.showProgressBar && this.progress !== undefined) parts.push(progressBar(this.progress));
|
|
256
292
|
if (parts.length === 0) return;
|
|
257
293
|
const src = `${parts.join("\n\n")}${this.footerSuffix()}`;
|
|
258
294
|
const rendered = toTelegramMarkdown(src);
|