@llblab/pi-telegram 0.20.6 → 0.21.1
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/AGENTS.md +7 -5
- package/BACKLOG.md +3 -45
- package/CHANGELOG.md +24 -0
- package/README.md +16 -8
- package/api/activity.ts +15 -0
- package/api/commands.ts +1 -1
- package/api/delivery.ts +21 -0
- package/api/inbound.ts +1 -1
- package/api/keyboard.ts +1 -1
- package/api/outbound.ts +1 -1
- package/api/sections.ts +1 -1
- package/api/status.ts +2 -2
- package/api/updates.ts +1 -1
- package/api/voice.ts +1 -1
- package/docs/README.md +2 -0
- package/docs/activity.md +294 -0
- package/docs/architecture.md +21 -5
- package/docs/delivery.md +224 -0
- package/docs/public-api.md +93 -2
- package/docs/sections.md +6 -5
- package/index.ts +53 -0
- package/lib/activity.ts +683 -0
- package/lib/bindings.ts +60 -8
- package/lib/bus-api.ts +2 -1
- package/lib/bus-leader.ts +4 -1
- package/lib/bus.ts +38 -0
- package/lib/delivery.ts +800 -0
- package/lib/lifecycle.ts +23 -4
- package/lib/model.ts +3 -1
- package/lib/pi.ts +21 -5
- package/lib/routing.ts +18 -3
- package/lib/sections.ts +1 -1
- package/lib/status.ts +2 -2
- package/lib/updates.ts +4 -4
- package/package.json +6 -4
package/lib/lifecycle.ts
CHANGED
|
@@ -6,10 +6,13 @@
|
|
|
6
6
|
|
|
7
7
|
import type {
|
|
8
8
|
AgentEndEvent,
|
|
9
|
+
AgentSettledEvent,
|
|
9
10
|
AgentStartEvent,
|
|
11
|
+
AssistantMessageEvent,
|
|
10
12
|
BeforeAgentStartEvent,
|
|
11
13
|
ExtensionAPI,
|
|
12
14
|
ExtensionContext,
|
|
15
|
+
InputEvent,
|
|
13
16
|
SessionBeforeCompactEvent,
|
|
14
17
|
SessionCompactEvent,
|
|
15
18
|
SessionShutdownEvent,
|
|
@@ -47,6 +50,7 @@ type TelegramLifecycleModel = ExtensionContext["model"];
|
|
|
47
50
|
type TelegramLifecycleMessage = AgentEndEvent["messages"][number];
|
|
48
51
|
|
|
49
52
|
export interface TelegramLifecycleRegistrationDeps {
|
|
53
|
+
onInput?: (event: InputEvent, ctx: ExtensionContext) => Promise<void> | void;
|
|
50
54
|
onSessionStart: (
|
|
51
55
|
event: SessionStartEvent,
|
|
52
56
|
ctx: ExtensionContext,
|
|
@@ -92,10 +96,17 @@ export interface TelegramLifecycleRegistrationDeps {
|
|
|
92
96
|
ctx: ExtensionContext,
|
|
93
97
|
) => Promise<void>;
|
|
94
98
|
onMessageUpdate: (
|
|
95
|
-
event: {
|
|
99
|
+
event: {
|
|
100
|
+
message: TelegramLifecycleMessage;
|
|
101
|
+
assistantMessageEvent?: AssistantMessageEvent;
|
|
102
|
+
},
|
|
96
103
|
ctx: ExtensionContext,
|
|
97
104
|
) => Promise<void>;
|
|
98
105
|
onAgentEnd: (event: AgentEndEvent, ctx: ExtensionContext) => Promise<void>;
|
|
106
|
+
onAgentSettled?: (
|
|
107
|
+
event: AgentSettledEvent,
|
|
108
|
+
ctx: ExtensionContext,
|
|
109
|
+
) => Promise<void> | void;
|
|
99
110
|
}
|
|
100
111
|
|
|
101
112
|
export interface TelegramSessionLifecycleHooks {
|
|
@@ -160,6 +171,7 @@ export interface TelegramCompactionObserverRuntimeDeps<TContext> {
|
|
|
160
171
|
) => void;
|
|
161
172
|
dispatchNextQueuedTelegramTurn: (ctx: TContext) => void;
|
|
162
173
|
recordRuntimeEvent?: (category: string, error: unknown) => void;
|
|
174
|
+
onCompactionAbandoned?: () => void;
|
|
163
175
|
timeoutMs?: number;
|
|
164
176
|
setTimer?: (callback: () => void, ms: number) => TelegramLifecycleTimer;
|
|
165
177
|
clearTimer?: (timer: TelegramLifecycleTimer) => void;
|
|
@@ -210,6 +222,7 @@ export function createTelegramCompactionObserverRuntime<TContext>(
|
|
|
210
222
|
"compact",
|
|
211
223
|
new Error("Compaction observer timed out"),
|
|
212
224
|
);
|
|
225
|
+
deps.onCompactionAbandoned?.();
|
|
213
226
|
requestDispatch();
|
|
214
227
|
}, timeoutMs);
|
|
215
228
|
unrefTelegramLifecycleTimer(fallbackTimer);
|
|
@@ -253,11 +266,11 @@ export function createTelegramMessageActivityTypingHooks<
|
|
|
253
266
|
const ensureTyping = (ctx: TContext): void => {
|
|
254
267
|
if (deps.hasActiveTurn()) deps.startTypingLoop(ctx);
|
|
255
268
|
};
|
|
256
|
-
const handleMessageActivity = async (
|
|
269
|
+
const handleMessageActivity = async <TEvent>(
|
|
257
270
|
phase: "start" | "update",
|
|
258
|
-
event:
|
|
271
|
+
event: TEvent,
|
|
259
272
|
ctx: ExtensionContext,
|
|
260
|
-
inner:
|
|
273
|
+
inner: (event: TEvent, ctx: ExtensionContext) => Promise<void>,
|
|
261
274
|
): Promise<void> => {
|
|
262
275
|
const typedCtx = ctx as TContext;
|
|
263
276
|
ensureTyping(typedCtx);
|
|
@@ -318,6 +331,9 @@ export function registerTelegramLifecycleHooks(
|
|
|
318
331
|
pi: ExtensionAPI,
|
|
319
332
|
deps: TelegramLifecycleRegistrationDeps,
|
|
320
333
|
): void {
|
|
334
|
+
pi.on("input", async (event, ctx) => {
|
|
335
|
+
await deps.onInput?.(event, ctx);
|
|
336
|
+
});
|
|
321
337
|
pi.on("session_start", async (event, ctx) => {
|
|
322
338
|
await deps.onSessionStart(event, ctx);
|
|
323
339
|
});
|
|
@@ -357,4 +373,7 @@ export function registerTelegramLifecycleHooks(
|
|
|
357
373
|
pi.on("agent_end", async (event, ctx) => {
|
|
358
374
|
await deps.onAgentEnd(event, ctx);
|
|
359
375
|
});
|
|
376
|
+
pi.on("agent_settled", async (event, ctx) => {
|
|
377
|
+
await deps.onAgentSettled?.(event, ctx);
|
|
378
|
+
});
|
|
360
379
|
}
|
package/lib/model.ts
CHANGED
|
@@ -20,7 +20,8 @@ export type ThinkingLevel =
|
|
|
20
20
|
| "low"
|
|
21
21
|
| "medium"
|
|
22
22
|
| "high"
|
|
23
|
-
| "xhigh"
|
|
23
|
+
| "xhigh"
|
|
24
|
+
| "max";
|
|
24
25
|
|
|
25
26
|
export interface ScopedTelegramModel<TModel extends MenuModel = MenuModel> {
|
|
26
27
|
model: TModel;
|
|
@@ -34,6 +35,7 @@ export const THINKING_LEVELS: readonly ThinkingLevel[] = [
|
|
|
34
35
|
"medium",
|
|
35
36
|
"high",
|
|
36
37
|
"xhigh",
|
|
38
|
+
"max",
|
|
37
39
|
];
|
|
38
40
|
|
|
39
41
|
export interface CurrentModelStore<
|
package/lib/pi.ts
CHANGED
|
@@ -4,13 +4,16 @@
|
|
|
4
4
|
* Owns direct pi SDK imports and exposes narrow bridge-facing helpers/types for the extension composition layer
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import type { AssistantMessageEvent } from "@earendil-works/pi-ai";
|
|
7
8
|
import {
|
|
8
9
|
type AgentEndEvent,
|
|
10
|
+
type AgentSettledEvent,
|
|
9
11
|
type AgentStartEvent,
|
|
10
12
|
type BeforeAgentStartEvent,
|
|
11
13
|
type ExtensionAPI,
|
|
12
14
|
type ExtensionCommandContext,
|
|
13
15
|
type ExtensionContext,
|
|
16
|
+
type InputEvent,
|
|
14
17
|
type SessionBeforeCompactEvent,
|
|
15
18
|
type SessionCompactEvent,
|
|
16
19
|
type SessionShutdownEvent,
|
|
@@ -21,11 +24,14 @@ import {
|
|
|
21
24
|
|
|
22
25
|
export type {
|
|
23
26
|
AgentEndEvent,
|
|
27
|
+
AgentSettledEvent,
|
|
24
28
|
AgentStartEvent,
|
|
29
|
+
AssistantMessageEvent,
|
|
25
30
|
BeforeAgentStartEvent,
|
|
26
31
|
ExtensionAPI,
|
|
27
32
|
ExtensionCommandContext,
|
|
28
33
|
ExtensionContext,
|
|
34
|
+
InputEvent,
|
|
29
35
|
SessionBeforeCompactEvent,
|
|
30
36
|
SessionCompactEvent,
|
|
31
37
|
SessionShutdownEvent,
|
|
@@ -68,10 +74,7 @@ export type PiRunMode = "tui" | "rpc" | "json" | "print";
|
|
|
68
74
|
|
|
69
75
|
function isPiRunMode(value: unknown): value is PiRunMode {
|
|
70
76
|
return (
|
|
71
|
-
value === "tui" ||
|
|
72
|
-
value === "rpc" ||
|
|
73
|
-
value === "json" ||
|
|
74
|
-
value === "print"
|
|
77
|
+
value === "tui" || value === "rpc" || value === "json" || value === "print"
|
|
75
78
|
);
|
|
76
79
|
}
|
|
77
80
|
|
|
@@ -99,6 +102,18 @@ export function formatPollingStartBlockedByRunMode(ctx: unknown): string {
|
|
|
99
102
|
: "Telegram polling is unavailable in this Pi run mode.";
|
|
100
103
|
}
|
|
101
104
|
|
|
105
|
+
export function getSessionCompactionReason(
|
|
106
|
+
event: unknown,
|
|
107
|
+
): "manual" | "threshold" | "overflow" | "unknown" {
|
|
108
|
+
const reason =
|
|
109
|
+
event && typeof event === "object" && "reason" in event
|
|
110
|
+
? (event as { reason?: unknown }).reason
|
|
111
|
+
: undefined;
|
|
112
|
+
return reason === "manual" || reason === "threshold" || reason === "overflow"
|
|
113
|
+
? reason
|
|
114
|
+
: "unknown";
|
|
115
|
+
}
|
|
116
|
+
|
|
102
117
|
export type PiSendUserMessageOptions = NonNullable<
|
|
103
118
|
Parameters<ExtensionAPI["sendUserMessage"]>[1]
|
|
104
119
|
>;
|
|
@@ -124,7 +139,8 @@ export function createExtensionApiRuntimePorts(
|
|
|
124
139
|
>,
|
|
125
140
|
): PiExtensionApiRuntimePorts {
|
|
126
141
|
return {
|
|
127
|
-
sendUserMessage: (content, options) =>
|
|
142
|
+
sendUserMessage: (content, options) =>
|
|
143
|
+
api.sendUserMessage(content, options),
|
|
128
144
|
exec: (command, args, options) => api.exec(command, args, options),
|
|
129
145
|
getCommands: () => api.getCommands(),
|
|
130
146
|
getThinkingLevel: () => api.getThinkingLevel(),
|
package/lib/routing.ts
CHANGED
|
@@ -23,6 +23,7 @@ import * as Turns from "./turns.ts";
|
|
|
23
23
|
|
|
24
24
|
interface TelegramPromptPeerView {
|
|
25
25
|
id?: unknown;
|
|
26
|
+
is_bot?: unknown;
|
|
26
27
|
username?: unknown;
|
|
27
28
|
first_name?: unknown;
|
|
28
29
|
last_name?: unknown;
|
|
@@ -56,6 +57,12 @@ function isTelegramPromptOwnerPeer(
|
|
|
56
57
|
return ownerUserId !== undefined && peer?.id === ownerUserId;
|
|
57
58
|
}
|
|
58
59
|
|
|
60
|
+
function isTelegramPromptBotPeer(
|
|
61
|
+
peer: TelegramPromptPeerView | undefined,
|
|
62
|
+
): boolean {
|
|
63
|
+
return peer?.is_bot === true;
|
|
64
|
+
}
|
|
65
|
+
|
|
59
66
|
export function resolveTelegramGuestPromptPeer(input: {
|
|
60
67
|
chatType?: string;
|
|
61
68
|
chat?: TelegramPromptPeerView;
|
|
@@ -68,16 +75,24 @@ export function resolveTelegramGuestPromptPeer(input: {
|
|
|
68
75
|
if (input.chatType !== "private") {
|
|
69
76
|
return formatTelegramPromptPeer(input.chat);
|
|
70
77
|
}
|
|
71
|
-
if (
|
|
78
|
+
if (
|
|
79
|
+
!isTelegramPromptOwnerPeer(input.from, input.ownerUserId) &&
|
|
80
|
+
!isTelegramPromptBotPeer(input.from)
|
|
81
|
+
) {
|
|
72
82
|
return formatTelegramPromptPeer(input.from);
|
|
73
83
|
}
|
|
74
84
|
for (const candidate of [
|
|
75
|
-
input.replyFrom,
|
|
76
85
|
input.chat,
|
|
77
86
|
input.guestBotCallerUser,
|
|
78
87
|
input.guestBotCallerChat,
|
|
88
|
+
input.replyFrom,
|
|
79
89
|
]) {
|
|
80
|
-
if (
|
|
90
|
+
if (
|
|
91
|
+
isTelegramPromptOwnerPeer(candidate, input.ownerUserId) ||
|
|
92
|
+
isTelegramPromptBotPeer(candidate)
|
|
93
|
+
) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
81
96
|
const peer = formatTelegramPromptPeer(candidate);
|
|
82
97
|
if (peer) return peer;
|
|
83
98
|
}
|
package/lib/sections.ts
CHANGED
|
@@ -25,7 +25,7 @@ export type TelegramSectionCallbackResult = "handled" | "pass";
|
|
|
25
25
|
export interface TelegramSectionView {
|
|
26
26
|
text: string;
|
|
27
27
|
/**
|
|
28
|
-
* Source format for
|
|
28
|
+
* Source format for extension-provided section content.
|
|
29
29
|
* Defaults to "html" for explicit Telegram UI markup; use "markdown"
|
|
30
30
|
* when a section naturally owns Markdown content, or "plain" for text.
|
|
31
31
|
*/
|
package/lib/status.ts
CHANGED
|
@@ -428,10 +428,10 @@ function getOrCreateTelegramStatusLineProviderRegistry(): Map<
|
|
|
428
428
|
}
|
|
429
429
|
|
|
430
430
|
/**
|
|
431
|
-
* Register a compact
|
|
431
|
+
* Register a compact extension-provided line for the Telegram status menu.
|
|
432
432
|
*
|
|
433
433
|
* Providers are synchronous and should return undefined when their line is not
|
|
434
|
-
* relevant for the active model. Errors are isolated so optional
|
|
434
|
+
* relevant for the active model. Errors are isolated so optional extension
|
|
435
435
|
* status cannot break the core Telegram menu.
|
|
436
436
|
*/
|
|
437
437
|
export function registerTelegramStatusLineProvider(
|
package/lib/updates.ts
CHANGED
|
@@ -1248,7 +1248,7 @@ export interface TelegramUpdateHandlerRegistry {
|
|
|
1248
1248
|
/**
|
|
1249
1249
|
* Run all registered handlers against an update.
|
|
1250
1250
|
*
|
|
1251
|
-
* Used by pi-telegram's polling runtime;
|
|
1251
|
+
* Used by pi-telegram's polling runtime; extension consumers should call
|
|
1252
1252
|
* {@link registerTelegramUpdateHandler} or `add` instead of dispatching directly.
|
|
1253
1253
|
*/
|
|
1254
1254
|
dispatch: (update: unknown) => Promise<TelegramUpdateHandlerVerdict>;
|
|
@@ -1297,7 +1297,7 @@ function getOrCreateUpdateHandlerRegistry(): TelegramUpdateHandlerRegistry {
|
|
|
1297
1297
|
|
|
1298
1298
|
/**
|
|
1299
1299
|
* Called by pi-telegram's own runtime to obtain the registry it dispatches
|
|
1300
|
-
* through.
|
|
1300
|
+
* through. Extension consumers should not call this; use
|
|
1301
1301
|
* {@link registerTelegramUpdateHandler} instead.
|
|
1302
1302
|
*/
|
|
1303
1303
|
export function getTelegramUpdateHandlerRegistry(): TelegramUpdateHandlerRegistry {
|
|
@@ -1328,8 +1328,8 @@ export function createTelegramUpdateHandle<TUpdate, TContext>(
|
|
|
1328
1328
|
* Register a handler that runs before pi-telegram routes a Telegram update
|
|
1329
1329
|
* through its built-in handlers.
|
|
1330
1330
|
*
|
|
1331
|
-
* This is the low-level public surface for
|
|
1332
|
-
*
|
|
1331
|
+
* This is the low-level public surface for extensions that share the same bot
|
|
1332
|
+
* and Pi process with pi-telegram.
|
|
1333
1333
|
*/
|
|
1334
1334
|
export function registerTelegramUpdateHandler(
|
|
1335
1335
|
handler: TelegramUpdateHandler,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@llblab/pi-telegram",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -48,6 +48,8 @@
|
|
|
48
48
|
".": "./index.ts",
|
|
49
49
|
"./inbound": "./api/inbound.ts",
|
|
50
50
|
"./outbound": "./api/outbound.ts",
|
|
51
|
+
"./delivery": "./api/delivery.ts",
|
|
52
|
+
"./activity": "./api/activity.ts",
|
|
51
53
|
"./updates": "./api/updates.ts",
|
|
52
54
|
"./commands": "./api/commands.ts",
|
|
53
55
|
"./sections": "./api/sections.ts",
|
|
@@ -62,9 +64,9 @@
|
|
|
62
64
|
"image": "https://github.com/llblab/pi-telegram/raw/main/screenshot.png"
|
|
63
65
|
},
|
|
64
66
|
"peerDependencies": {
|
|
65
|
-
"@earendil-works/pi-agent-core": "
|
|
66
|
-
"@earendil-works/pi-ai": "
|
|
67
|
-
"@earendil-works/pi-coding-agent": "
|
|
67
|
+
"@earendil-works/pi-agent-core": ">=0.80.6",
|
|
68
|
+
"@earendil-works/pi-ai": ">=0.80.6",
|
|
69
|
+
"@earendil-works/pi-coding-agent": ">=0.80.6",
|
|
68
70
|
"@sinclair/typebox": "*"
|
|
69
71
|
},
|
|
70
72
|
"devDependencies": {
|