@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/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: { message: TelegramLifecycleMessage },
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: Parameters<TelegramLifecycleRegistrationDeps["onMessageStart"]>[0],
271
+ event: TEvent,
259
272
  ctx: ExtensionContext,
260
- inner: TelegramLifecycleRegistrationDeps["onMessageStart"],
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) => api.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 (!isTelegramPromptOwnerPeer(input.from, input.ownerUserId)) {
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 (isTelegramPromptOwnerPeer(candidate, input.ownerUserId)) continue;
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 companion section content.
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 companion-extension line for the Telegram status menu.
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 companion
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; companion extensions should call
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. Companion extensions should not call this; use
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 companion extensions that share
1332
- * the same bot and pi process with pi-telegram.
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.20.6",
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": {