@llblab/pi-telegram 0.20.5 → 0.21.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/AGENTS.md +7 -5
- package/BACKLOG.md +3 -65
- package/CHANGELOG.md +24 -0
- package/README.md +7 -1
- 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 +7 -1
- package/docs/delivery.md +224 -0
- package/docs/public-api.md +92 -1
- 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/outbound-markup.ts +39 -4
- package/lib/pi.ts +21 -5
- package/lib/prompts.ts +3 -2
- package/lib/routing.ts +106 -13
- 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/outbound-markup.ts
CHANGED
|
@@ -64,6 +64,36 @@ function isTopLevelClosingFence(
|
|
|
64
64
|
);
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
function collectPairedTelegramVoiceActionBody(
|
|
68
|
+
markdown: string,
|
|
69
|
+
bodyStart: number,
|
|
70
|
+
commentContent: string,
|
|
71
|
+
): { content: string; end: number } | undefined {
|
|
72
|
+
const normalizedContent = commentContent.trim();
|
|
73
|
+
if (
|
|
74
|
+
!normalizedContent.startsWith("telegram_voice") ||
|
|
75
|
+
!isTelegramActionCommentContent(commentContent)
|
|
76
|
+
) {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
let offset = bodyStart;
|
|
80
|
+
while (offset < markdown.length) {
|
|
81
|
+
const lineEnd = getMarkdownLineEnd(markdown, offset);
|
|
82
|
+
const line = getMarkdownLineText(markdown, offset, lineEnd);
|
|
83
|
+
if (line === "<!-- /telegram_voice -->") {
|
|
84
|
+
const body = markdown.slice(bodyStart, offset).trim();
|
|
85
|
+
if (!body) return undefined;
|
|
86
|
+
return {
|
|
87
|
+
content: `${commentContent.trimEnd()}\n${body}`,
|
|
88
|
+
end: lineEnd,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (line.startsWith("<!--")) return undefined;
|
|
92
|
+
offset = lineEnd;
|
|
93
|
+
}
|
|
94
|
+
return undefined;
|
|
95
|
+
}
|
|
96
|
+
|
|
67
97
|
function collectInlineClosedTelegramActionBody(
|
|
68
98
|
markdown: string,
|
|
69
99
|
bodyStart: number,
|
|
@@ -117,14 +147,19 @@ export function collectTopLevelHtmlComments(markdown: string): {
|
|
|
117
147
|
const closesOnOpeningLine = closeIndex < lineEnd;
|
|
118
148
|
const hasOnlyWhitespaceAfterClose =
|
|
119
149
|
line.slice(closeColumn + 3).trim() === "";
|
|
120
|
-
const
|
|
150
|
+
const pairedVoiceBody =
|
|
121
151
|
closesOnOpeningLine && hasOnlyWhitespaceAfterClose
|
|
152
|
+
? collectPairedTelegramVoiceActionBody(markdown, lineEnd, content)
|
|
153
|
+
: undefined;
|
|
154
|
+
const inlineBody =
|
|
155
|
+
!pairedVoiceBody && closesOnOpeningLine && hasOnlyWhitespaceAfterClose
|
|
122
156
|
? collectInlineClosedTelegramActionBody(markdown, lineEnd, content)
|
|
123
157
|
: undefined;
|
|
124
|
-
|
|
125
|
-
|
|
158
|
+
const recoveredBody = pairedVoiceBody ?? inlineBody;
|
|
159
|
+
if (recoveredBody) {
|
|
160
|
+
end = recoveredBody.end;
|
|
126
161
|
raw = markdown.slice(offset, end);
|
|
127
|
-
content =
|
|
162
|
+
content = recoveredBody.content;
|
|
128
163
|
}
|
|
129
164
|
comments.push({ raw, content, start: offset, end });
|
|
130
165
|
offset = getMarkdownLineEnd(markdown, end);
|
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/prompts.ts
CHANGED
|
@@ -16,7 +16,7 @@ Telegram bridge available. Do not use it from local/TUI prompts unless explicitl
|
|
|
16
16
|
|
|
17
17
|
const TELEGRAM_TURN_SYSTEM_PROMPT_SUFFIX = `
|
|
18
18
|
|
|
19
|
-
Telegram turn note: If context was compacted or you need the pi-telegram bridge contract, call tool \`telegram_help\`; hidden comments are valid only for explicit \`telegram_voice\` or \`telegram_button\` actions with payload
|
|
19
|
+
Telegram turn note: If context was compacted or you need the pi-telegram bridge contract, call tool \`telegram_help\`; hidden comments are valid only for explicit \`telegram_voice\` or \`telegram_button\` actions with payload. For voice use a top-level HTML action: \`<!-- telegram_voice: Speak this. -->\`, multiline \`<!-- telegram_voice lang=ru\nSpeak this.\n-->\`, or paired \`<!-- telegram_voice lang=ru -->\nSpeak this.\n<!-- /telegram_voice -->\`.`;
|
|
20
20
|
|
|
21
21
|
function buildTelegramHelpText(profileName?: string): string {
|
|
22
22
|
const diagnosticsPaths = getTelegramDiagnosticsDisplayPaths(profileName);
|
|
@@ -37,7 +37,8 @@ How to answer Telegram turns:
|
|
|
37
37
|
Assistant-authored Telegram actions:
|
|
38
38
|
- \`telegram_voice\` and \`telegram_button\` are hidden top-level HTML comments, not Pi tools.
|
|
39
39
|
- Put action comments at column zero, outside code, quotes, lists, and indented examples.
|
|
40
|
-
- Voice forms: \`<!-- telegram_voice text="Short summary"
|
|
40
|
+
- Voice forms: \`<!-- telegram_voice text="Short summary" -->\`, \`<!-- telegram_voice: Short summary -->\`, multiline \`<!-- telegram_voice lang=ru\nShort summary.\n-->\`, or paired \`<!-- telegram_voice lang=ru -->\nShort summary.\n<!-- /telegram_voice -->\`.
|
|
41
|
+
- Keep the complete action at top level and include a non-empty voice payload.
|
|
41
42
|
- Keep voice text TTS-friendly; avoid raw Markdown, code, and tables in voice text.
|
|
42
43
|
- Voice delivery generates and attaches OGG automatically; do not also call \`telegram_attach\` for the same audio.
|
|
43
44
|
- Button forms: \`<!-- telegram_button: OK -->\`, \`<!-- telegram_button label=Continue prompt="Continue with the current plan." -->\`, or multiline \`<!-- telegram_button label="Show risks"\nList the main risks first.\n-->\`.
|
package/lib/routing.ts
CHANGED
|
@@ -21,10 +21,82 @@ import * as TextGroups from "./text-groups.ts";
|
|
|
21
21
|
import * as ThreadReconciler from "./thread-reconciler.ts";
|
|
22
22
|
import * as Turns from "./turns.ts";
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
interface TelegramPromptPeerView {
|
|
25
|
+
id?: unknown;
|
|
26
|
+
is_bot?: unknown;
|
|
27
|
+
username?: unknown;
|
|
28
|
+
first_name?: unknown;
|
|
29
|
+
last_name?: unknown;
|
|
30
|
+
title?: unknown;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function formatTelegramPromptPeer(
|
|
34
|
+
peer: TelegramPromptPeerView | undefined,
|
|
35
|
+
): string | undefined {
|
|
36
|
+
if (!peer) return undefined;
|
|
37
|
+
if (typeof peer.username === "string" && peer.username.length > 0) {
|
|
38
|
+
return peer.username;
|
|
39
|
+
}
|
|
40
|
+
const displayName = [peer.first_name, peer.last_name]
|
|
41
|
+
.filter(
|
|
42
|
+
(part): part is string =>
|
|
43
|
+
typeof part === "string" && part.length > 0,
|
|
44
|
+
)
|
|
45
|
+
.join(" ");
|
|
46
|
+
if (displayName) return displayName;
|
|
47
|
+
if (typeof peer.title === "string" && peer.title.length > 0) {
|
|
48
|
+
return peer.title;
|
|
49
|
+
}
|
|
50
|
+
return typeof peer.id === "number" ? String(peer.id) : undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function isTelegramPromptOwnerPeer(
|
|
54
|
+
peer: TelegramPromptPeerView | undefined,
|
|
55
|
+
ownerUserId: number | undefined,
|
|
56
|
+
): boolean {
|
|
57
|
+
return ownerUserId !== undefined && peer?.id === ownerUserId;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function isTelegramPromptBotPeer(
|
|
61
|
+
peer: TelegramPromptPeerView | undefined,
|
|
62
|
+
): boolean {
|
|
63
|
+
return peer?.is_bot === true;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function resolveTelegramGuestPromptPeer(input: {
|
|
67
|
+
chatType?: string;
|
|
68
|
+
chat?: TelegramPromptPeerView;
|
|
69
|
+
from?: TelegramPromptPeerView;
|
|
70
|
+
replyFrom?: TelegramPromptPeerView;
|
|
71
|
+
guestBotCallerUser?: TelegramPromptPeerView;
|
|
72
|
+
guestBotCallerChat?: TelegramPromptPeerView;
|
|
73
|
+
ownerUserId?: number;
|
|
74
|
+
}): string | undefined {
|
|
75
|
+
if (input.chatType !== "private") {
|
|
76
|
+
return formatTelegramPromptPeer(input.chat);
|
|
77
|
+
}
|
|
78
|
+
if (
|
|
79
|
+
!isTelegramPromptOwnerPeer(input.from, input.ownerUserId) &&
|
|
80
|
+
!isTelegramPromptBotPeer(input.from)
|
|
81
|
+
) {
|
|
82
|
+
return formatTelegramPromptPeer(input.from);
|
|
83
|
+
}
|
|
84
|
+
for (const candidate of [
|
|
85
|
+
input.chat,
|
|
86
|
+
input.guestBotCallerUser,
|
|
87
|
+
input.guestBotCallerChat,
|
|
88
|
+
input.replyFrom,
|
|
89
|
+
]) {
|
|
90
|
+
if (
|
|
91
|
+
isTelegramPromptOwnerPeer(candidate, input.ownerUserId) ||
|
|
92
|
+
isTelegramPromptBotPeer(candidate)
|
|
93
|
+
) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
const peer = formatTelegramPromptPeer(candidate);
|
|
97
|
+
if (peer) return peer;
|
|
98
|
+
}
|
|
99
|
+
return undefined;
|
|
28
100
|
}
|
|
29
101
|
|
|
30
102
|
function appendTelegramSourceAttachmentSection(
|
|
@@ -1812,22 +1884,43 @@ export function createTelegramInboundRouteRuntime<
|
|
|
1812
1884
|
const gm = guestMessage as unknown as Record<string, unknown>;
|
|
1813
1885
|
// Build telegram prefix with guest context
|
|
1814
1886
|
const chatRaw = gm.chat as Record<string, unknown>;
|
|
1815
|
-
const chatTitle = chatRaw?.title as string | undefined;
|
|
1816
1887
|
const chatType = chatRaw?.type as string;
|
|
1817
1888
|
const fromRaw = gm.from as Record<string, unknown> | undefined;
|
|
1818
1889
|
const replyMsg = gm.reply_to_message as Record<string, unknown> | undefined;
|
|
1819
1890
|
const replyFromRaw = replyMsg?.from as Record<string, unknown> | undefined;
|
|
1820
|
-
const
|
|
1891
|
+
const guestBotCallerUser = gm.guest_bot_caller_user as
|
|
1892
|
+
| Record<string, unknown>
|
|
1893
|
+
| undefined;
|
|
1894
|
+
const guestBotCallerChat = gm.guest_bot_caller_chat as
|
|
1895
|
+
| Record<string, unknown>
|
|
1896
|
+
| undefined;
|
|
1897
|
+
const ownerUserId = deps.configStore.getAllowedUserId();
|
|
1821
1898
|
const replyPeer = formatTelegramPromptPeer(replyFromRaw);
|
|
1822
|
-
const
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
:
|
|
1899
|
+
const guestPeer = resolveTelegramGuestPromptPeer({
|
|
1900
|
+
chatType,
|
|
1901
|
+
chat: chatRaw,
|
|
1902
|
+
from: fromRaw,
|
|
1903
|
+
replyFrom: replyFromRaw,
|
|
1904
|
+
guestBotCallerUser,
|
|
1905
|
+
guestBotCallerChat,
|
|
1906
|
+
ownerUserId,
|
|
1907
|
+
});
|
|
1826
1908
|
const prefixParts = ["telegram"];
|
|
1827
|
-
if (
|
|
1828
|
-
prefixParts.push(`guest:${chatTitle}`);
|
|
1829
|
-
} else if (chatType === "private" && guestPeer) {
|
|
1909
|
+
if (guestPeer) {
|
|
1830
1910
|
prefixParts.push(`guest:${guestPeer}`);
|
|
1911
|
+
} else if (chatType === "private") {
|
|
1912
|
+
deps.recordRuntimeEvent?.(
|
|
1913
|
+
"guest",
|
|
1914
|
+
new Error("Private Guest Mode remote peer could not be resolved"),
|
|
1915
|
+
{
|
|
1916
|
+
phase: "peer-attribution",
|
|
1917
|
+
chatId: typeof chatRaw?.id === "number" ? chatRaw.id : undefined,
|
|
1918
|
+
fromId: typeof fromRaw?.id === "number" ? fromRaw.id : undefined,
|
|
1919
|
+
hasReplyFrom: !!replyFromRaw,
|
|
1920
|
+
hasCallerUser: !!guestBotCallerUser,
|
|
1921
|
+
hasCallerChat: !!guestBotCallerChat,
|
|
1922
|
+
},
|
|
1923
|
+
);
|
|
1831
1924
|
}
|
|
1832
1925
|
const telegramPrefix = `[${prefixParts.join("|")}]`;
|
|
1833
1926
|
// Extract reply context
|
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.0",
|
|
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": {
|