@llblab/pi-telegram 0.3.0 → 0.5.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 +41 -10
- package/docs/README.md +4 -3
- package/docs/architecture.md +43 -38
- package/docs/attachment-handlers.md +60 -0
- package/docs/command-templates.md +75 -0
- package/docs/locks.md +136 -0
- package/index.ts +80 -142
- package/lib/attachments.ts +70 -2
- package/lib/commands.ts +116 -48
- package/lib/config.ts +17 -5
- package/lib/handlers.ts +400 -0
- package/lib/lifecycle.ts +140 -0
- package/lib/locks.ts +336 -0
- package/lib/media.ts +50 -6
- package/lib/menu.ts +0 -4
- package/lib/pi.ts +11 -1
- package/lib/prompts.ts +44 -0
- package/lib/queue.ts +12 -6
- package/lib/routing.ts +219 -0
- package/lib/runtime.ts +9 -6
- package/lib/setup.ts +21 -3
- package/lib/status.ts +33 -4
- package/lib/turns.ts +103 -21
- package/package.json +1 -1
- package/lib/registration.ts +0 -262
package/lib/handlers.ts
ADDED
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram inbound attachment handler pipeline
|
|
3
|
+
* Owns MIME/type matching, command-template execution, fallback handling, and prompt injection before prompt enqueueing
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { homedir } from "node:os";
|
|
7
|
+
import { basename, isAbsolute, resolve } from "node:path";
|
|
8
|
+
|
|
9
|
+
const DEFAULT_ATTACHMENT_HANDLER_TIMEOUT_MS = 120_000;
|
|
10
|
+
|
|
11
|
+
export interface TelegramAttachmentHandlerConfig {
|
|
12
|
+
match?: string | string[];
|
|
13
|
+
mime?: string | string[];
|
|
14
|
+
type?: string | string[];
|
|
15
|
+
template?: string;
|
|
16
|
+
args?: string | string[];
|
|
17
|
+
defaults?: Record<string, unknown>;
|
|
18
|
+
timeoutMs?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface TelegramAttachmentHandlerFile {
|
|
22
|
+
path: string;
|
|
23
|
+
fileName?: string;
|
|
24
|
+
mimeType?: string;
|
|
25
|
+
kind?: string;
|
|
26
|
+
isImage?: boolean;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface TelegramAttachmentHandlerOutput {
|
|
30
|
+
file: TelegramAttachmentHandlerFile;
|
|
31
|
+
output: string;
|
|
32
|
+
handler: TelegramAttachmentHandlerConfig;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface TelegramAttachmentHandlerProcessResult<
|
|
36
|
+
TFile extends TelegramAttachmentHandlerFile = TelegramAttachmentHandlerFile,
|
|
37
|
+
> {
|
|
38
|
+
rawText: string;
|
|
39
|
+
promptFiles: TFile[];
|
|
40
|
+
handlerOutputs: string[];
|
|
41
|
+
handledFiles: TelegramAttachmentHandlerOutput[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export interface TelegramAttachmentHandlerExecOptions {
|
|
45
|
+
cwd?: string;
|
|
46
|
+
timeout?: number;
|
|
47
|
+
signal?: AbortSignal;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface TelegramAttachmentHandlerExecResult {
|
|
51
|
+
stdout: string;
|
|
52
|
+
stderr: string;
|
|
53
|
+
code: number;
|
|
54
|
+
killed: boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface TelegramAttachmentHandlerRuntimeContext {
|
|
58
|
+
cwd: string;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface TelegramAttachmentHandlerRuntimeDeps<TContext> {
|
|
62
|
+
getHandlers: () => TelegramAttachmentHandlerConfig[] | undefined;
|
|
63
|
+
execCommand: (
|
|
64
|
+
command: string,
|
|
65
|
+
args: string[],
|
|
66
|
+
options?: TelegramAttachmentHandlerExecOptions,
|
|
67
|
+
) => Promise<TelegramAttachmentHandlerExecResult>;
|
|
68
|
+
getCwd: (ctx: TContext) => string;
|
|
69
|
+
recordRuntimeEvent?: (
|
|
70
|
+
category: string,
|
|
71
|
+
error: unknown,
|
|
72
|
+
details?: Record<string, unknown>,
|
|
73
|
+
) => void;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export interface TelegramAttachmentHandlerRuntime<TContext> {
|
|
77
|
+
process: <TFile extends TelegramAttachmentHandlerFile>(
|
|
78
|
+
files: TFile[],
|
|
79
|
+
rawText: string,
|
|
80
|
+
ctx: TContext,
|
|
81
|
+
) => Promise<TelegramAttachmentHandlerProcessResult<TFile>>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
interface AttachmentHandlerInvocation {
|
|
85
|
+
command: string;
|
|
86
|
+
args: string[];
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function normalizeStringList(value: string | string[] | undefined): string[] {
|
|
90
|
+
if (Array.isArray(value)) {
|
|
91
|
+
return value
|
|
92
|
+
.map(String)
|
|
93
|
+
.map((item) => item.trim())
|
|
94
|
+
.filter(Boolean);
|
|
95
|
+
}
|
|
96
|
+
if (typeof value === "string" && value.trim()) return [value.trim()];
|
|
97
|
+
return [];
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function matchesWildcard(pattern: string, value: string | undefined): boolean {
|
|
101
|
+
if (!value) return false;
|
|
102
|
+
const normalizedPattern = pattern.toLowerCase();
|
|
103
|
+
const normalizedValue = value.toLowerCase();
|
|
104
|
+
if (normalizedPattern === "*") return true;
|
|
105
|
+
const escaped = normalizedPattern
|
|
106
|
+
.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
|
107
|
+
.replace(/\\\*/g, ".*");
|
|
108
|
+
return new RegExp(`^${escaped}$`).test(normalizedValue);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function handlerHasSelectors(
|
|
112
|
+
handler: TelegramAttachmentHandlerConfig,
|
|
113
|
+
): boolean {
|
|
114
|
+
return (
|
|
115
|
+
normalizeStringList(handler.match).length > 0 ||
|
|
116
|
+
normalizeStringList(handler.mime).length > 0 ||
|
|
117
|
+
normalizeStringList(handler.type).length > 0
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function matchesAnyPattern(
|
|
122
|
+
patterns: string[],
|
|
123
|
+
value: string | undefined,
|
|
124
|
+
): boolean {
|
|
125
|
+
return patterns.some((pattern) => matchesWildcard(pattern, value));
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export function telegramAttachmentHandlerMatchesFile(
|
|
129
|
+
handler: TelegramAttachmentHandlerConfig,
|
|
130
|
+
file: TelegramAttachmentHandlerFile,
|
|
131
|
+
): boolean {
|
|
132
|
+
if (!handlerHasSelectors(handler)) return true;
|
|
133
|
+
const matchPatterns = normalizeStringList(handler.match);
|
|
134
|
+
const mimePatterns = normalizeStringList(handler.mime);
|
|
135
|
+
const typePatterns = normalizeStringList(handler.type);
|
|
136
|
+
if (matchesAnyPattern(mimePatterns, file.mimeType)) return true;
|
|
137
|
+
if (matchesAnyPattern(typePatterns, file.kind)) return true;
|
|
138
|
+
if (matchesAnyPattern(matchPatterns, file.mimeType)) return true;
|
|
139
|
+
return matchesAnyPattern(matchPatterns, file.kind);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function findTelegramAttachmentHandlers(
|
|
143
|
+
handlers: TelegramAttachmentHandlerConfig[] | undefined,
|
|
144
|
+
file: TelegramAttachmentHandlerFile,
|
|
145
|
+
): TelegramAttachmentHandlerConfig[] {
|
|
146
|
+
if (!Array.isArray(handlers)) return [];
|
|
147
|
+
return handlers.filter(
|
|
148
|
+
(handler) =>
|
|
149
|
+
!!handler &&
|
|
150
|
+
typeof handler === "object" &&
|
|
151
|
+
telegramAttachmentHandlerMatchesFile(handler, file),
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export function findTelegramAttachmentHandler(
|
|
156
|
+
handlers: TelegramAttachmentHandlerConfig[] | undefined,
|
|
157
|
+
file: TelegramAttachmentHandlerFile,
|
|
158
|
+
): TelegramAttachmentHandlerConfig | undefined {
|
|
159
|
+
return findTelegramAttachmentHandlers(handlers, file)[0];
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function hasAttachmentFilePlaceholder(value: string): boolean {
|
|
163
|
+
return /\{file\}/.test(value);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function normalizeTelegramAttachmentHandlerArgs(
|
|
167
|
+
value: string | string[] | undefined,
|
|
168
|
+
): string[] {
|
|
169
|
+
if (Array.isArray(value)) return value.map(String).map((item) => item.trim());
|
|
170
|
+
if (typeof value !== "string") return [];
|
|
171
|
+
return value.split(",").map((item) => item.trim());
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function getTelegramAttachmentHandlerArgDefaults(
|
|
175
|
+
handler: TelegramAttachmentHandlerConfig,
|
|
176
|
+
): Record<string, string> {
|
|
177
|
+
const defaults: Record<string, string> = {};
|
|
178
|
+
for (const item of normalizeTelegramAttachmentHandlerArgs(handler.args)) {
|
|
179
|
+
if (!item) continue;
|
|
180
|
+
const [name, ...defaultParts] = item.split("=");
|
|
181
|
+
if (!name || defaultParts.length === 0) continue;
|
|
182
|
+
defaults[name.trim()] = defaultParts.join("=").trim();
|
|
183
|
+
}
|
|
184
|
+
for (const [key, value] of Object.entries(handler.defaults ?? {})) {
|
|
185
|
+
defaults[key] = value === undefined || value === null ? "" : String(value);
|
|
186
|
+
}
|
|
187
|
+
return defaults;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function getTelegramAttachmentHandlerTemplateValues(
|
|
191
|
+
handler: TelegramAttachmentHandlerConfig,
|
|
192
|
+
file: TelegramAttachmentHandlerFile,
|
|
193
|
+
): Record<string, string> {
|
|
194
|
+
return {
|
|
195
|
+
...getTelegramAttachmentHandlerArgDefaults(handler),
|
|
196
|
+
file: file.path,
|
|
197
|
+
mime: file.mimeType ?? "",
|
|
198
|
+
type: file.kind ?? "",
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function substituteTelegramAttachmentHandlerTemplateToken(
|
|
203
|
+
token: string,
|
|
204
|
+
values: Record<string, string>,
|
|
205
|
+
): string {
|
|
206
|
+
return token.replace(/\{([A-Za-z_][A-Za-z0-9_-]*)\}/g, (_match, name) => {
|
|
207
|
+
if (Object.hasOwn(values, name)) return values[name] ?? "";
|
|
208
|
+
throw new Error(`Missing attachment handler template value: ${name}`);
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function splitTelegramAttachmentHandlerTemplate(input: string): string[] {
|
|
213
|
+
const words: string[] = [];
|
|
214
|
+
let current = "";
|
|
215
|
+
let quote: "'" | '"' | undefined;
|
|
216
|
+
let escaped = false;
|
|
217
|
+
let active = false;
|
|
218
|
+
for (const char of input) {
|
|
219
|
+
if (escaped) {
|
|
220
|
+
current += char;
|
|
221
|
+
escaped = false;
|
|
222
|
+
active = true;
|
|
223
|
+
continue;
|
|
224
|
+
}
|
|
225
|
+
if (char === "\\" && quote !== "'") {
|
|
226
|
+
escaped = true;
|
|
227
|
+
active = true;
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (quote) {
|
|
231
|
+
if (char === quote) quote = undefined;
|
|
232
|
+
else current += char;
|
|
233
|
+
active = true;
|
|
234
|
+
continue;
|
|
235
|
+
}
|
|
236
|
+
if (char === "'" || char === '"') {
|
|
237
|
+
quote = char;
|
|
238
|
+
active = true;
|
|
239
|
+
continue;
|
|
240
|
+
}
|
|
241
|
+
if (/\s/.test(char)) {
|
|
242
|
+
if (active) words.push(current);
|
|
243
|
+
if (active) current = "";
|
|
244
|
+
active = false;
|
|
245
|
+
continue;
|
|
246
|
+
}
|
|
247
|
+
current += char;
|
|
248
|
+
active = true;
|
|
249
|
+
}
|
|
250
|
+
if (escaped) current += "\\";
|
|
251
|
+
if (active || current) words.push(current);
|
|
252
|
+
return words;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function expandExecutablePath(command: string, cwd: string): string {
|
|
256
|
+
if (command === "~") return homedir();
|
|
257
|
+
if (command.startsWith("~/")) return resolve(homedir(), command.slice(2));
|
|
258
|
+
if (command.includes("/") && !isAbsolute(command)) {
|
|
259
|
+
return resolve(cwd, command);
|
|
260
|
+
}
|
|
261
|
+
return command;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function buildTelegramAttachmentTemplateInvocation(
|
|
265
|
+
template: string,
|
|
266
|
+
handler: TelegramAttachmentHandlerConfig,
|
|
267
|
+
file: TelegramAttachmentHandlerFile,
|
|
268
|
+
cwd: string,
|
|
269
|
+
): AttachmentHandlerInvocation {
|
|
270
|
+
const parts = splitTelegramAttachmentHandlerTemplate(template);
|
|
271
|
+
const commandPart = parts[0];
|
|
272
|
+
if (!commandPart) throw new Error("Attachment handler template is empty");
|
|
273
|
+
const values = getTelegramAttachmentHandlerTemplateValues(handler, file);
|
|
274
|
+
const hadFilePlaceholder = parts.some(hasAttachmentFilePlaceholder);
|
|
275
|
+
const command = expandExecutablePath(
|
|
276
|
+
substituteTelegramAttachmentHandlerTemplateToken(commandPart, values),
|
|
277
|
+
cwd,
|
|
278
|
+
);
|
|
279
|
+
const args = parts
|
|
280
|
+
.slice(1)
|
|
281
|
+
.map((part) =>
|
|
282
|
+
substituteTelegramAttachmentHandlerTemplateToken(part, values),
|
|
283
|
+
);
|
|
284
|
+
if (!hadFilePlaceholder) args.push(file.path);
|
|
285
|
+
return { command, args };
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export function buildTelegramAttachmentHandlerInvocation(
|
|
289
|
+
handler: TelegramAttachmentHandlerConfig,
|
|
290
|
+
file: TelegramAttachmentHandlerFile,
|
|
291
|
+
cwd: string,
|
|
292
|
+
): AttachmentHandlerInvocation {
|
|
293
|
+
const { template } = handler;
|
|
294
|
+
if (!template) throw new Error("Attachment handler template is required");
|
|
295
|
+
return buildTelegramAttachmentTemplateInvocation(template, handler, file, cwd);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
function getTelegramAttachmentHandlerTimeout(
|
|
299
|
+
handler: TelegramAttachmentHandlerConfig,
|
|
300
|
+
): number {
|
|
301
|
+
return typeof handler.timeoutMs === "number" &&
|
|
302
|
+
Number.isFinite(handler.timeoutMs) &&
|
|
303
|
+
handler.timeoutMs > 0
|
|
304
|
+
? handler.timeoutMs
|
|
305
|
+
: DEFAULT_ATTACHMENT_HANDLER_TIMEOUT_MS;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function getTelegramAttachmentHandlerKind(
|
|
309
|
+
handler: TelegramAttachmentHandlerConfig,
|
|
310
|
+
): string {
|
|
311
|
+
if (handler.template) return "template";
|
|
312
|
+
return "unknown";
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function formatTelegramAttachmentHandlerFailure(
|
|
316
|
+
result: TelegramAttachmentHandlerExecResult,
|
|
317
|
+
): string {
|
|
318
|
+
const parts = [
|
|
319
|
+
`Attachment handler exited with code ${result.code}${result.killed ? " (killed)" : ""}`,
|
|
320
|
+
];
|
|
321
|
+
if (result.stderr.trim()) parts.push(`stderr:\n${result.stderr.trimEnd()}`);
|
|
322
|
+
if (result.stdout.trim()) parts.push(`stdout:\n${result.stdout.trimEnd()}`);
|
|
323
|
+
return parts.join("\n\n");
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
async function executeTelegramAttachmentHandler(
|
|
327
|
+
handler: TelegramAttachmentHandlerConfig,
|
|
328
|
+
file: TelegramAttachmentHandlerFile,
|
|
329
|
+
cwd: string,
|
|
330
|
+
deps: Pick<TelegramAttachmentHandlerRuntimeDeps<unknown>, "execCommand">,
|
|
331
|
+
): Promise<string> {
|
|
332
|
+
const invocation = buildTelegramAttachmentHandlerInvocation(
|
|
333
|
+
handler,
|
|
334
|
+
file,
|
|
335
|
+
cwd,
|
|
336
|
+
);
|
|
337
|
+
const result = await deps.execCommand(invocation.command, invocation.args, {
|
|
338
|
+
cwd,
|
|
339
|
+
timeout: getTelegramAttachmentHandlerTimeout(handler),
|
|
340
|
+
});
|
|
341
|
+
if (result.code !== 0)
|
|
342
|
+
throw new Error(formatTelegramAttachmentHandlerFailure(result));
|
|
343
|
+
return result.stdout.trim();
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export async function processTelegramAttachmentHandlers<
|
|
347
|
+
TFile extends TelegramAttachmentHandlerFile,
|
|
348
|
+
>(options: {
|
|
349
|
+
files: TFile[];
|
|
350
|
+
rawText: string;
|
|
351
|
+
handlers?: TelegramAttachmentHandlerConfig[];
|
|
352
|
+
cwd: string;
|
|
353
|
+
execCommand: TelegramAttachmentHandlerRuntimeDeps<unknown>["execCommand"];
|
|
354
|
+
recordRuntimeEvent?: TelegramAttachmentHandlerRuntimeDeps<unknown>["recordRuntimeEvent"];
|
|
355
|
+
}): Promise<TelegramAttachmentHandlerProcessResult<TFile>> {
|
|
356
|
+
const promptFiles: TFile[] = [...options.files];
|
|
357
|
+
const outputs: TelegramAttachmentHandlerOutput[] = [];
|
|
358
|
+
for (const file of options.files) {
|
|
359
|
+
const handlers = findTelegramAttachmentHandlers(options.handlers, file);
|
|
360
|
+
for (const handler of handlers) {
|
|
361
|
+
try {
|
|
362
|
+
const output = await executeTelegramAttachmentHandler(
|
|
363
|
+
handler,
|
|
364
|
+
file,
|
|
365
|
+
options.cwd,
|
|
366
|
+
options,
|
|
367
|
+
);
|
|
368
|
+
if (output) outputs.push({ file, output, handler });
|
|
369
|
+
break;
|
|
370
|
+
} catch (error) {
|
|
371
|
+
options.recordRuntimeEvent?.("attachment-handler", error, {
|
|
372
|
+
fileName: file.fileName || basename(file.path),
|
|
373
|
+
handler: getTelegramAttachmentHandlerKind(handler),
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
return {
|
|
379
|
+
rawText: options.rawText,
|
|
380
|
+
promptFiles,
|
|
381
|
+
handlerOutputs: outputs.map((output) => output.output),
|
|
382
|
+
handledFiles: outputs,
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
export function createTelegramAttachmentHandlerRuntime<TContext>(
|
|
387
|
+
deps: TelegramAttachmentHandlerRuntimeDeps<TContext>,
|
|
388
|
+
): TelegramAttachmentHandlerRuntime<TContext> {
|
|
389
|
+
return {
|
|
390
|
+
process: (files, rawText, ctx) =>
|
|
391
|
+
processTelegramAttachmentHandlers({
|
|
392
|
+
files,
|
|
393
|
+
rawText,
|
|
394
|
+
handlers: deps.getHandlers(),
|
|
395
|
+
cwd: deps.getCwd(ctx),
|
|
396
|
+
execCommand: deps.execCommand,
|
|
397
|
+
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
398
|
+
}),
|
|
399
|
+
};
|
|
400
|
+
}
|
package/lib/lifecycle.ts
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram lifecycle hook registration helpers
|
|
3
|
+
* Owns binding prepared Telegram lifecycle runtimes to pi extension lifecycle events
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
AgentEndEvent,
|
|
8
|
+
AgentStartEvent,
|
|
9
|
+
BeforeAgentStartEvent,
|
|
10
|
+
ExtensionAPI,
|
|
11
|
+
ExtensionContext,
|
|
12
|
+
SessionShutdownEvent,
|
|
13
|
+
SessionStartEvent,
|
|
14
|
+
} from "./pi.ts";
|
|
15
|
+
|
|
16
|
+
export interface TelegramBeforeAgentStartResult {
|
|
17
|
+
systemPrompt?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type TelegramBeforeAgentStartReturn =
|
|
21
|
+
| Promise<TelegramBeforeAgentStartResult | undefined>
|
|
22
|
+
| TelegramBeforeAgentStartResult
|
|
23
|
+
| undefined;
|
|
24
|
+
|
|
25
|
+
type TelegramLifecycleModel = ExtensionContext["model"];
|
|
26
|
+
type TelegramLifecycleMessage = AgentEndEvent["messages"][number];
|
|
27
|
+
|
|
28
|
+
export interface TelegramLifecycleRegistrationDeps {
|
|
29
|
+
onSessionStart: (
|
|
30
|
+
event: SessionStartEvent,
|
|
31
|
+
ctx: ExtensionContext,
|
|
32
|
+
) => Promise<void>;
|
|
33
|
+
onSessionShutdown: (
|
|
34
|
+
event: SessionShutdownEvent,
|
|
35
|
+
ctx: ExtensionContext,
|
|
36
|
+
) => Promise<void>;
|
|
37
|
+
onBeforeAgentStart: (
|
|
38
|
+
event: BeforeAgentStartEvent,
|
|
39
|
+
ctx: ExtensionContext,
|
|
40
|
+
) => TelegramBeforeAgentStartReturn;
|
|
41
|
+
onModelSelect: (
|
|
42
|
+
event: { model: TelegramLifecycleModel },
|
|
43
|
+
ctx: ExtensionContext,
|
|
44
|
+
) => Promise<void> | void;
|
|
45
|
+
onAgentStart: (
|
|
46
|
+
event: AgentStartEvent,
|
|
47
|
+
ctx: ExtensionContext,
|
|
48
|
+
) => Promise<void>;
|
|
49
|
+
onToolExecutionStart: (
|
|
50
|
+
event: unknown,
|
|
51
|
+
ctx: ExtensionContext,
|
|
52
|
+
) => Promise<void> | void;
|
|
53
|
+
onToolExecutionEnd: (
|
|
54
|
+
event: unknown,
|
|
55
|
+
ctx: ExtensionContext,
|
|
56
|
+
) => Promise<void> | void;
|
|
57
|
+
onMessageStart: (
|
|
58
|
+
event: { message: TelegramLifecycleMessage },
|
|
59
|
+
ctx: ExtensionContext,
|
|
60
|
+
) => Promise<void>;
|
|
61
|
+
onMessageUpdate: (
|
|
62
|
+
event: { message: TelegramLifecycleMessage },
|
|
63
|
+
ctx: ExtensionContext,
|
|
64
|
+
) => Promise<void>;
|
|
65
|
+
onAgentEnd: (event: AgentEndEvent, ctx: ExtensionContext) => Promise<void>;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface TelegramSessionLifecycleHooks {
|
|
69
|
+
onSessionStart: (
|
|
70
|
+
event: SessionStartEvent,
|
|
71
|
+
ctx: ExtensionContext,
|
|
72
|
+
) => Promise<void>;
|
|
73
|
+
onSessionShutdown: (
|
|
74
|
+
event: SessionShutdownEvent,
|
|
75
|
+
ctx: ExtensionContext,
|
|
76
|
+
) => Promise<void>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface TelegramExtraLifecycleHooks {
|
|
80
|
+
onSessionStart?: (
|
|
81
|
+
event: SessionStartEvent,
|
|
82
|
+
ctx: ExtensionContext,
|
|
83
|
+
) => Promise<void>;
|
|
84
|
+
onSessionShutdown?: (
|
|
85
|
+
event: SessionShutdownEvent,
|
|
86
|
+
ctx: ExtensionContext,
|
|
87
|
+
) => Promise<void>;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export function appendTelegramLifecycleHooks(
|
|
91
|
+
base: TelegramSessionLifecycleHooks,
|
|
92
|
+
extra: TelegramExtraLifecycleHooks,
|
|
93
|
+
): TelegramSessionLifecycleHooks {
|
|
94
|
+
return {
|
|
95
|
+
onSessionStart: async (event, ctx) => {
|
|
96
|
+
await base.onSessionStart(event, ctx);
|
|
97
|
+
await extra.onSessionStart?.(event, ctx);
|
|
98
|
+
},
|
|
99
|
+
onSessionShutdown: async (event, ctx) => {
|
|
100
|
+
await base.onSessionShutdown(event, ctx);
|
|
101
|
+
await extra.onSessionShutdown?.(event, ctx);
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function registerTelegramLifecycleHooks(
|
|
107
|
+
pi: ExtensionAPI,
|
|
108
|
+
deps: TelegramLifecycleRegistrationDeps,
|
|
109
|
+
): void {
|
|
110
|
+
pi.on("session_start", async (event, ctx) => {
|
|
111
|
+
await deps.onSessionStart(event, ctx);
|
|
112
|
+
});
|
|
113
|
+
pi.on("session_shutdown", async (event, ctx) => {
|
|
114
|
+
await deps.onSessionShutdown(event, ctx);
|
|
115
|
+
});
|
|
116
|
+
pi.on("before_agent_start", async (event, ctx) => {
|
|
117
|
+
return deps.onBeforeAgentStart(event, ctx);
|
|
118
|
+
});
|
|
119
|
+
pi.on("model_select", async (event, ctx) => {
|
|
120
|
+
await deps.onModelSelect(event, ctx);
|
|
121
|
+
});
|
|
122
|
+
pi.on("agent_start", async (event, ctx) => {
|
|
123
|
+
await deps.onAgentStart(event, ctx);
|
|
124
|
+
});
|
|
125
|
+
pi.on("tool_execution_start", async (event, ctx) => {
|
|
126
|
+
await deps.onToolExecutionStart(event, ctx);
|
|
127
|
+
});
|
|
128
|
+
pi.on("tool_execution_end", async (event, ctx) => {
|
|
129
|
+
await deps.onToolExecutionEnd(event, ctx);
|
|
130
|
+
});
|
|
131
|
+
pi.on("message_start", async (event, ctx) => {
|
|
132
|
+
await deps.onMessageStart(event, ctx);
|
|
133
|
+
});
|
|
134
|
+
pi.on("message_update", async (event, ctx) => {
|
|
135
|
+
await deps.onMessageUpdate(event, ctx);
|
|
136
|
+
});
|
|
137
|
+
pi.on("agent_end", async (event, ctx) => {
|
|
138
|
+
await deps.onAgentEnd(event, ctx);
|
|
139
|
+
});
|
|
140
|
+
}
|