@llblab/pi-telegram 0.2.9 → 0.3.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 +40 -26
- package/docs/architecture.md +62 -35
- package/index.ts +388 -1936
- package/lib/api.ts +647 -76
- package/lib/attachments.ts +128 -16
- package/lib/commands.ts +721 -0
- package/lib/config.ts +157 -0
- package/lib/media.ts +211 -36
- package/lib/menu.ts +920 -338
- package/lib/model.ts +647 -0
- package/lib/pi.ts +80 -0
- package/lib/polling.ts +264 -18
- package/lib/preview.ts +451 -29
- package/lib/queue.ts +1134 -110
- package/lib/registration.ts +127 -28
- package/lib/rendering.ts +575 -281
- package/lib/replies.ts +198 -8
- package/lib/runtime.ts +475 -0
- package/lib/setup.ts +129 -1
- package/lib/status.ts +428 -13
- package/lib/turns.ts +207 -17
- package/lib/updates.ts +392 -99
- package/package.json +18 -3
- package/AGENTS.md +0 -91
- package/BACKLOG.md +0 -5
- package/CHANGELOG.md +0 -23
- package/lib/model-switch.ts +0 -62
- package/tests/api.test.ts +0 -89
- package/tests/attachments.test.ts +0 -132
- package/tests/config.test.ts +0 -80
- package/tests/media.test.ts +0 -77
- package/tests/menu.test.ts +0 -676
- package/tests/polling.test.ts +0 -129
- package/tests/preview.test.ts +0 -441
- package/tests/queue.test.ts +0 -3245
- package/tests/registration.test.ts +0 -268
- package/tests/rendering.test.ts +0 -475
- package/tests/replies.test.ts +0 -142
- package/tests/turns.test.ts +0 -132
- package/tests/updates.test.ts +0 -357
package/lib/registration.ts
CHANGED
|
@@ -3,28 +3,62 @@
|
|
|
3
3
|
* Owns tool, command, and lifecycle-hook registration so index.ts can stay focused on runtime orchestration state and side effects
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { Type } from "@sinclair/typebox";
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
queueTelegramAttachments,
|
|
10
|
+
TELEGRAM_OUTBOUND_ATTACHMENT_MAX_BYTES,
|
|
11
|
+
type TelegramAttachmentQueueTargetView,
|
|
12
|
+
} from "./attachments.ts";
|
|
6
13
|
import type {
|
|
14
|
+
AgentEndEvent,
|
|
15
|
+
AgentStartEvent,
|
|
16
|
+
BeforeAgentStartEvent,
|
|
7
17
|
ExtensionAPI,
|
|
8
18
|
ExtensionCommandContext,
|
|
9
19
|
ExtensionContext,
|
|
10
|
-
|
|
11
|
-
|
|
20
|
+
SessionShutdownEvent,
|
|
21
|
+
SessionStartEvent,
|
|
22
|
+
} from "./pi.ts";
|
|
23
|
+
import { TELEGRAM_PREFIX } from "./turns.ts";
|
|
12
24
|
|
|
13
|
-
|
|
14
|
-
|
|
25
|
+
const MAX_ATTACHMENTS_PER_TURN = 10;
|
|
26
|
+
|
|
27
|
+
const SYSTEM_PROMPT_SUFFIX = `
|
|
28
|
+
|
|
29
|
+
Telegram bridge extension is active.
|
|
30
|
+
- Messages forwarded from Telegram are prefixed with "[telegram]".
|
|
31
|
+
- [telegram] messages may include local temp file paths for Telegram attachments. Read those files as needed.
|
|
32
|
+
- Telegram is often read on narrow phone screens, so prefer narrow table columns when presenting tabular data; wide monospace tables can become unreadable.
|
|
33
|
+
- If a [telegram] user asked for a file or generated artifact, use the telegram_attach tool with the local file path so the extension can send it with your next final reply.
|
|
34
|
+
- Do not assume mentioning a local file path in plain text will send it to Telegram. Use telegram_attach.`;
|
|
15
35
|
|
|
16
36
|
// --- Tool Registration ---
|
|
17
37
|
|
|
18
|
-
export interface
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
38
|
+
export interface TelegramRuntimeEventRecorderPort {
|
|
39
|
+
recordRuntimeEvent?: (
|
|
40
|
+
category: string,
|
|
41
|
+
error: unknown,
|
|
42
|
+
details?: Record<string, unknown>,
|
|
43
|
+
) => void;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface TelegramAttachmentToolRegistrationDeps
|
|
47
|
+
extends TelegramRuntimeEventRecorderPort {
|
|
48
|
+
maxAttachmentsPerTurn?: number;
|
|
49
|
+
maxAttachmentSizeBytes?: number;
|
|
50
|
+
getActiveTurn: () => TelegramAttachmentQueueTargetView | undefined;
|
|
51
|
+
statPath?: (path: string) => Promise<{ isFile(): boolean; size?: number }>;
|
|
22
52
|
}
|
|
23
53
|
|
|
24
54
|
export function registerTelegramAttachmentTool(
|
|
25
55
|
pi: ExtensionAPI,
|
|
26
56
|
deps: TelegramAttachmentToolRegistrationDeps,
|
|
27
57
|
): void {
|
|
58
|
+
const maxAttachmentsPerTurn =
|
|
59
|
+
deps.maxAttachmentsPerTurn ?? MAX_ATTACHMENTS_PER_TURN;
|
|
60
|
+
const maxAttachmentSizeBytes =
|
|
61
|
+
deps.maxAttachmentSizeBytes ?? TELEGRAM_OUTBOUND_ATTACHMENT_MAX_BYTES;
|
|
28
62
|
pi.registerTool({
|
|
29
63
|
name: "telegram_attach",
|
|
30
64
|
label: "Telegram Attach",
|
|
@@ -37,16 +71,25 @@ export function registerTelegramAttachmentTool(
|
|
|
37
71
|
parameters: Type.Object({
|
|
38
72
|
paths: Type.Array(
|
|
39
73
|
Type.String({ description: "Local file path to attach" }),
|
|
40
|
-
{ minItems: 1, maxItems:
|
|
74
|
+
{ minItems: 1, maxItems: maxAttachmentsPerTurn },
|
|
41
75
|
),
|
|
42
76
|
}),
|
|
43
77
|
async execute(_toolCallId, params) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
78
|
+
try {
|
|
79
|
+
return await queueTelegramAttachments({
|
|
80
|
+
activeTurn: deps.getActiveTurn(),
|
|
81
|
+
paths: params.paths,
|
|
82
|
+
maxAttachmentsPerTurn,
|
|
83
|
+
maxAttachmentSizeBytes,
|
|
84
|
+
statPath: deps.statPath,
|
|
85
|
+
});
|
|
86
|
+
} catch (error) {
|
|
87
|
+
deps.recordRuntimeEvent?.("attachment", error, {
|
|
88
|
+
phase: "queue",
|
|
89
|
+
count: params.paths.length,
|
|
90
|
+
});
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
50
93
|
},
|
|
51
94
|
});
|
|
52
95
|
}
|
|
@@ -58,7 +101,7 @@ export interface TelegramCommandRegistrationDeps {
|
|
|
58
101
|
getStatusLines: () => string[];
|
|
59
102
|
reloadConfig: () => Promise<void>;
|
|
60
103
|
hasBotToken: () => boolean;
|
|
61
|
-
startPolling: (ctx: ExtensionCommandContext) => Promise<void>;
|
|
104
|
+
startPolling: (ctx: ExtensionCommandContext) => void | Promise<void>;
|
|
62
105
|
stopPolling: () => Promise<void>;
|
|
63
106
|
updateStatus: (ctx: ExtensionCommandContext) => void;
|
|
64
107
|
}
|
|
@@ -76,7 +119,7 @@ export function registerTelegramCommands(
|
|
|
76
119
|
pi.registerCommand("telegram-status", {
|
|
77
120
|
description: "Show Telegram bridge status",
|
|
78
121
|
handler: async (_args, ctx) => {
|
|
79
|
-
ctx.ui.notify(deps.getStatusLines().join("
|
|
122
|
+
ctx.ui.notify(deps.getStatusLines().join("\n"), "info");
|
|
80
123
|
},
|
|
81
124
|
});
|
|
82
125
|
pi.registerCommand("telegram-connect", {
|
|
@@ -102,18 +145,67 @@ export function registerTelegramCommands(
|
|
|
102
145
|
|
|
103
146
|
// --- Lifecycle Hook Registration ---
|
|
104
147
|
|
|
148
|
+
export function buildTelegramBridgeSystemPrompt(options: {
|
|
149
|
+
prompt: string;
|
|
150
|
+
systemPrompt: string;
|
|
151
|
+
telegramPrefix?: string;
|
|
152
|
+
systemPromptSuffix: string;
|
|
153
|
+
}): { systemPrompt: string } {
|
|
154
|
+
const telegramPrefix = options.telegramPrefix ?? TELEGRAM_PREFIX;
|
|
155
|
+
const suffix = options.prompt.trimStart().startsWith(telegramPrefix)
|
|
156
|
+
? `${options.systemPromptSuffix}\n- The current user message came from Telegram.`
|
|
157
|
+
: options.systemPromptSuffix;
|
|
158
|
+
return { systemPrompt: options.systemPrompt + suffix };
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function createTelegramBeforeAgentStartHook(
|
|
162
|
+
options: {
|
|
163
|
+
telegramPrefix?: string;
|
|
164
|
+
systemPromptSuffix?: string;
|
|
165
|
+
} = {},
|
|
166
|
+
): (event: BeforeAgentStartEvent) => { systemPrompt: string } {
|
|
167
|
+
return (event) =>
|
|
168
|
+
buildTelegramBridgeSystemPrompt({
|
|
169
|
+
prompt: event.prompt,
|
|
170
|
+
systemPrompt: event.systemPrompt,
|
|
171
|
+
telegramPrefix: options.telegramPrefix,
|
|
172
|
+
systemPromptSuffix: options.systemPromptSuffix ?? SYSTEM_PROMPT_SUFFIX,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export interface TelegramBeforeAgentStartResult {
|
|
177
|
+
systemPrompt?: string;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
type TelegramBeforeAgentStartReturn =
|
|
181
|
+
| Promise<TelegramBeforeAgentStartResult | undefined>
|
|
182
|
+
| TelegramBeforeAgentStartResult
|
|
183
|
+
| undefined;
|
|
184
|
+
|
|
185
|
+
type TelegramLifecycleModel = ExtensionContext["model"];
|
|
186
|
+
type TelegramLifecycleMessage = AgentEndEvent["messages"][number];
|
|
187
|
+
|
|
105
188
|
export interface TelegramLifecycleRegistrationDeps {
|
|
106
|
-
onSessionStart: (
|
|
107
|
-
|
|
189
|
+
onSessionStart: (
|
|
190
|
+
event: SessionStartEvent,
|
|
191
|
+
ctx: ExtensionContext,
|
|
192
|
+
) => Promise<void>;
|
|
193
|
+
onSessionShutdown: (
|
|
194
|
+
event: SessionShutdownEvent,
|
|
195
|
+
ctx: ExtensionContext,
|
|
196
|
+
) => Promise<void>;
|
|
108
197
|
onBeforeAgentStart: (
|
|
109
|
-
event:
|
|
198
|
+
event: BeforeAgentStartEvent,
|
|
110
199
|
ctx: ExtensionContext,
|
|
111
|
-
) =>
|
|
200
|
+
) => TelegramBeforeAgentStartReturn;
|
|
112
201
|
onModelSelect: (
|
|
113
|
-
event:
|
|
202
|
+
event: { model: TelegramLifecycleModel },
|
|
114
203
|
ctx: ExtensionContext,
|
|
115
204
|
) => Promise<void> | void;
|
|
116
|
-
onAgentStart: (
|
|
205
|
+
onAgentStart: (
|
|
206
|
+
event: AgentStartEvent,
|
|
207
|
+
ctx: ExtensionContext,
|
|
208
|
+
) => Promise<void>;
|
|
117
209
|
onToolExecutionStart: (
|
|
118
210
|
event: unknown,
|
|
119
211
|
ctx: ExtensionContext,
|
|
@@ -122,9 +214,15 @@ export interface TelegramLifecycleRegistrationDeps {
|
|
|
122
214
|
event: unknown,
|
|
123
215
|
ctx: ExtensionContext,
|
|
124
216
|
) => Promise<void> | void;
|
|
125
|
-
onMessageStart: (
|
|
126
|
-
|
|
127
|
-
|
|
217
|
+
onMessageStart: (
|
|
218
|
+
event: { message: TelegramLifecycleMessage },
|
|
219
|
+
ctx: ExtensionContext,
|
|
220
|
+
) => Promise<void>;
|
|
221
|
+
onMessageUpdate: (
|
|
222
|
+
event: { message: TelegramLifecycleMessage },
|
|
223
|
+
ctx: ExtensionContext,
|
|
224
|
+
) => Promise<void>;
|
|
225
|
+
onAgentEnd: (event: AgentEndEvent, ctx: ExtensionContext) => Promise<void>;
|
|
128
226
|
}
|
|
129
227
|
|
|
130
228
|
export function registerTelegramLifecycleHooks(
|
|
@@ -137,8 +235,9 @@ export function registerTelegramLifecycleHooks(
|
|
|
137
235
|
pi.on("session_shutdown", async (event, ctx) => {
|
|
138
236
|
await deps.onSessionShutdown(event, ctx);
|
|
139
237
|
});
|
|
140
|
-
pi.on("before_agent_start",
|
|
141
|
-
deps.onBeforeAgentStart(event, ctx)
|
|
238
|
+
pi.on("before_agent_start", async (event, ctx) => {
|
|
239
|
+
return deps.onBeforeAgentStart(event, ctx);
|
|
240
|
+
});
|
|
142
241
|
pi.on("model_select", async (event, ctx) => {
|
|
143
242
|
await deps.onModelSelect(event, ctx);
|
|
144
243
|
});
|