@llblab/pi-telegram 0.2.10 → 0.4.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.
Files changed (47) hide show
  1. package/README.md +52 -19
  2. package/docs/README.md +2 -3
  3. package/docs/architecture.md +62 -31
  4. package/docs/locks.md +136 -0
  5. package/index.ts +323 -1880
  6. package/lib/api.ts +396 -60
  7. package/lib/attachments.ts +128 -16
  8. package/lib/commands.ts +648 -14
  9. package/lib/config.ts +169 -0
  10. package/lib/handlers.ts +474 -0
  11. package/lib/locks.ts +306 -0
  12. package/lib/media.ts +196 -46
  13. package/lib/menu.ts +920 -338
  14. package/lib/model.ts +647 -0
  15. package/lib/pi.ts +90 -0
  16. package/lib/polling.ts +240 -14
  17. package/lib/preview.ts +420 -25
  18. package/lib/queue.ts +1137 -110
  19. package/lib/registration.ts +214 -31
  20. package/lib/rendering.ts +560 -366
  21. package/lib/replies.ts +198 -8
  22. package/lib/routing.ts +217 -0
  23. package/lib/runtime.ts +475 -0
  24. package/lib/setup.ts +143 -1
  25. package/lib/status.ts +432 -13
  26. package/lib/turns.ts +217 -36
  27. package/lib/updates.ts +340 -109
  28. package/package.json +18 -3
  29. package/AGENTS.md +0 -91
  30. package/BACKLOG.md +0 -5
  31. package/CHANGELOG.md +0 -34
  32. package/lib/model-switch.ts +0 -62
  33. package/lib/types.ts +0 -137
  34. package/tests/api.test.ts +0 -331
  35. package/tests/attachments.test.ts +0 -132
  36. package/tests/commands.test.ts +0 -85
  37. package/tests/config.test.ts +0 -80
  38. package/tests/media.test.ts +0 -166
  39. package/tests/menu.test.ts +0 -676
  40. package/tests/polling.test.ts +0 -202
  41. package/tests/preview.test.ts +0 -480
  42. package/tests/queue.test.ts +0 -3245
  43. package/tests/registration.test.ts +0 -268
  44. package/tests/rendering.test.ts +0 -526
  45. package/tests/replies.test.ts +0 -142
  46. package/tests/turns.test.ts +0 -247
  47. package/tests/updates.test.ts +0 -416
@@ -1,268 +0,0 @@
1
- /**
2
- * Regression tests for the Telegram registration domain
3
- * Covers tool registration and command registration behavior without exercising the full extension runtime
4
- */
5
-
6
- import assert from "node:assert/strict";
7
- import test from "node:test";
8
-
9
- import telegramExtension from "../index.ts";
10
- import {
11
- registerTelegramAttachmentTool,
12
- registerTelegramCommands,
13
- registerTelegramLifecycleHooks,
14
- } from "../lib/registration.ts";
15
-
16
- function createRegistrationApiHarness() {
17
- let tool: any;
18
- const commands = new Map<string, any>();
19
- const handlers = new Map<string, any>();
20
- return {
21
- tool: () => tool,
22
- commands,
23
- handlers,
24
- api: {
25
- on: (event: string, handler: unknown) => {
26
- handlers.set(event, handler);
27
- },
28
- registerTool: (definition: unknown) => {
29
- tool = definition;
30
- },
31
- registerCommand: (name: string, definition: unknown) => {
32
- commands.set(name, definition);
33
- },
34
- } as never,
35
- };
36
- }
37
-
38
- test("Registration registers the attachment tool and delegates queueing", async () => {
39
- const harness = createRegistrationApiHarness();
40
- const activeTurn = {
41
- queuedAttachments: [],
42
- } as unknown as {
43
- queuedAttachments: Array<{ path: string; fileName: string }>;
44
- } & ReturnType<
45
- Parameters<typeof registerTelegramAttachmentTool>[1]["getActiveTurn"]
46
- >;
47
- registerTelegramAttachmentTool(harness.api, {
48
- maxAttachmentsPerTurn: 2,
49
- getActiveTurn: () => activeTurn,
50
- statPath: async () => ({ isFile: () => true }),
51
- });
52
- const tool = harness.tool();
53
- assert.equal(tool?.name, "telegram_attach");
54
- const result = await tool.execute("tool-call", { paths: ["/tmp/report.md"] });
55
- assert.deepEqual(activeTurn.queuedAttachments, [
56
- { path: "/tmp/report.md", fileName: "report.md" },
57
- ]);
58
- assert.deepEqual(result.details.paths, ["/tmp/report.md"]);
59
- });
60
-
61
- test("Registration commands expose setup and status behaviors", async () => {
62
- const harness = createRegistrationApiHarness();
63
- const events: string[] = [];
64
- registerTelegramCommands(harness.api, {
65
- promptForConfig: async () => {
66
- events.push("setup");
67
- },
68
- getStatusLines: () => ["bot: @demo", "polling: stopped"],
69
- reloadConfig: async () => {
70
- events.push("reload");
71
- },
72
- hasBotToken: () => false,
73
- startPolling: async () => {
74
- events.push("start");
75
- },
76
- stopPolling: async () => {
77
- events.push("stop");
78
- },
79
- updateStatus: () => {
80
- events.push("update-status");
81
- },
82
- });
83
- const setupCommand = harness.commands.get("telegram-setup");
84
- const statusCommand = harness.commands.get("telegram-status");
85
- const notifications: string[] = [];
86
- const ctx = {
87
- ui: {
88
- notify: (message: string) => {
89
- notifications.push(message);
90
- },
91
- },
92
- } as never;
93
- await setupCommand.handler("", ctx);
94
- await statusCommand.handler("", ctx);
95
- assert.deepEqual(events, ["setup"]);
96
- assert.deepEqual(notifications, ["bot: @demo | polling: stopped"]);
97
- });
98
-
99
- test("Registration connect and disconnect commands reload config and control polling", async () => {
100
- const harness = createRegistrationApiHarness();
101
- const events: string[] = [];
102
- let hasToken = false;
103
- registerTelegramCommands(harness.api, {
104
- promptForConfig: async () => {
105
- events.push("setup");
106
- },
107
- getStatusLines: () => [],
108
- reloadConfig: async () => {
109
- events.push("reload");
110
- },
111
- hasBotToken: () => hasToken,
112
- startPolling: async () => {
113
- events.push("start");
114
- },
115
- stopPolling: async () => {
116
- events.push("stop");
117
- },
118
- updateStatus: () => {
119
- events.push("update-status");
120
- },
121
- });
122
- const connectCommand = harness.commands.get("telegram-connect");
123
- const disconnectCommand = harness.commands.get("telegram-disconnect");
124
- const ctx = { ui: { notify: () => {} } } as never;
125
- await connectCommand.handler("", ctx);
126
- hasToken = true;
127
- await connectCommand.handler("", ctx);
128
- await disconnectCommand.handler("", ctx);
129
- assert.deepEqual(events, [
130
- "reload",
131
- "setup",
132
- "reload",
133
- "start",
134
- "update-status",
135
- "stop",
136
- "update-status",
137
- ]);
138
- });
139
-
140
- test("Registration lifecycle hooks are registered and delegate to the provided handlers", async () => {
141
- const harness = createRegistrationApiHarness();
142
- const events: string[] = [];
143
- registerTelegramLifecycleHooks(harness.api, {
144
- onSessionStart: async () => {
145
- events.push("session-start");
146
- },
147
- onSessionShutdown: async () => {
148
- events.push("session-shutdown");
149
- },
150
- onBeforeAgentStart: () => {
151
- events.push("before-agent-start");
152
- return { systemPrompt: "prompt" };
153
- },
154
- onModelSelect: () => {
155
- events.push("model-select");
156
- },
157
- onAgentStart: async () => {
158
- events.push("agent-start");
159
- },
160
- onToolExecutionStart: () => {
161
- events.push("tool-start");
162
- },
163
- onToolExecutionEnd: () => {
164
- events.push("tool-end");
165
- },
166
- onMessageStart: async () => {
167
- events.push("message-start");
168
- },
169
- onMessageUpdate: async () => {
170
- events.push("message-update");
171
- },
172
- onAgentEnd: async () => {
173
- events.push("agent-end");
174
- },
175
- });
176
- assert.deepEqual(
177
- [...harness.handlers.keys()],
178
- [
179
- "session_start",
180
- "session_shutdown",
181
- "before_agent_start",
182
- "model_select",
183
- "agent_start",
184
- "tool_execution_start",
185
- "tool_execution_end",
186
- "message_start",
187
- "message_update",
188
- "agent_end",
189
- ],
190
- );
191
- const ctx = {} as never;
192
- await harness.handlers.get("session_start")({}, ctx);
193
- await harness.handlers.get("session_shutdown")({}, ctx);
194
- const beforeAgentStartResult = await harness.handlers.get(
195
- "before_agent_start",
196
- )({}, ctx);
197
- await harness.handlers.get("model_select")({}, ctx);
198
- await harness.handlers.get("agent_start")({}, ctx);
199
- await harness.handlers.get("tool_execution_start")({}, ctx);
200
- await harness.handlers.get("tool_execution_end")({}, ctx);
201
- await harness.handlers.get("message_start")({}, ctx);
202
- await harness.handlers.get("message_update")({}, ctx);
203
- await harness.handlers.get("agent_end")({}, ctx);
204
- assert.deepEqual(beforeAgentStartResult, { systemPrompt: "prompt" });
205
- assert.deepEqual(events, [
206
- "session-start",
207
- "session-shutdown",
208
- "before-agent-start",
209
- "model-select",
210
- "agent-start",
211
- "tool-start",
212
- "tool-end",
213
- "message-start",
214
- "message-update",
215
- "agent-end",
216
- ]);
217
- });
218
-
219
- test("Extension entrypoint wires registration domains into the pi API", () => {
220
- const harness = createRegistrationApiHarness();
221
- telegramExtension(harness.api);
222
- assert.equal(harness.tool()?.name, "telegram_attach");
223
- assert.deepEqual(
224
- [...harness.commands.keys()],
225
- [
226
- "telegram-setup",
227
- "telegram-status",
228
- "telegram-connect",
229
- "telegram-disconnect",
230
- ],
231
- );
232
- assert.deepEqual(
233
- [...harness.handlers.keys()],
234
- [
235
- "session_start",
236
- "session_shutdown",
237
- "before_agent_start",
238
- "model_select",
239
- "agent_start",
240
- "tool_execution_start",
241
- "tool_execution_end",
242
- "message_start",
243
- "message_update",
244
- "agent_end",
245
- ],
246
- );
247
- });
248
-
249
- test("Extension before-agent-start hook appends Telegram-specific system prompt guidance", async () => {
250
- const harness = createRegistrationApiHarness();
251
- telegramExtension(harness.api);
252
- const handler = harness.handlers.get("before_agent_start");
253
- const basePrompt = "System base";
254
- const telegramResult = await handler(
255
- { systemPrompt: basePrompt, prompt: "[telegram] hello" },
256
- {} as never,
257
- );
258
- const localResult = await handler(
259
- { systemPrompt: basePrompt, prompt: "hello" },
260
- {} as never,
261
- );
262
- assert.match(
263
- telegramResult.systemPrompt,
264
- /current user message came from Telegram/,
265
- );
266
- assert.match(telegramResult.systemPrompt, /telegram_attach/);
267
- assert.equal(localResult.systemPrompt.includes("came from Telegram"), false);
268
- });