@llblab/pi-telegram 0.4.0 → 0.5.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/README.md +22 -13
- package/docs/README.md +2 -0
- package/docs/architecture.md +42 -45
- package/docs/attachment-handlers.md +60 -0
- package/docs/command-templates.md +75 -0
- package/index.ts +9 -7
- package/lib/attachments.ts +70 -2
- package/lib/commands.ts +144 -58
- package/lib/handlers.ts +118 -192
- package/lib/lifecycle.ts +140 -0
- package/lib/locks.ts +43 -13
- package/lib/menu.ts +0 -4
- package/lib/prompts.ts +44 -0
- package/lib/queue.ts +21 -6
- package/lib/routing.ts +5 -2
- package/lib/runtime.ts +9 -6
- package/lib/setup.ts +5 -1
- package/lib/status.ts +29 -4
- package/lib/turns.ts +12 -7
- package/package.json +1 -1
- package/lib/registration.ts +0 -346
package/lib/locks.ts
CHANGED
|
@@ -3,7 +3,14 @@
|
|
|
3
3
|
* Owns shared locks.json access and Telegram bridge ownership semantics
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
existsSync,
|
|
8
|
+
mkdirSync,
|
|
9
|
+
readFileSync,
|
|
10
|
+
renameSync,
|
|
11
|
+
unlinkSync,
|
|
12
|
+
writeFileSync,
|
|
13
|
+
} from "node:fs";
|
|
7
14
|
import { homedir } from "node:os";
|
|
8
15
|
import { dirname, join, resolve } from "node:path";
|
|
9
16
|
|
|
@@ -68,7 +75,9 @@ export type TelegramLockedPollingStartResult =
|
|
|
68
75
|
| { ok: true; message: string; canTakeover?: false }
|
|
69
76
|
| { ok: false; message: string; canTakeover?: boolean; owner?: string };
|
|
70
77
|
|
|
71
|
-
export interface TelegramLockedPollingRuntime<
|
|
78
|
+
export interface TelegramLockedPollingRuntime<
|
|
79
|
+
TContext extends TelegramLockContext,
|
|
80
|
+
> {
|
|
72
81
|
start: (
|
|
73
82
|
ctx: TContext,
|
|
74
83
|
options?: TelegramLockedPollingStartOptions,
|
|
@@ -78,13 +87,19 @@ export interface TelegramLockedPollingRuntime<TContext extends TelegramLockConte
|
|
|
78
87
|
onSessionStart: (_event: unknown, ctx: TContext) => Promise<void>;
|
|
79
88
|
}
|
|
80
89
|
|
|
81
|
-
export interface TelegramLockedPollingRuntimeDeps<
|
|
90
|
+
export interface TelegramLockedPollingRuntimeDeps<
|
|
91
|
+
TContext extends TelegramLockContext,
|
|
92
|
+
> {
|
|
82
93
|
lock: TelegramLockRuntime<TContext>;
|
|
83
94
|
hasBotToken: () => boolean;
|
|
84
95
|
startPolling: (ctx: TContext) => void | Promise<void>;
|
|
85
96
|
stopPolling: () => Promise<void>;
|
|
86
97
|
updateStatus: (ctx: TContext) => void;
|
|
87
|
-
recordRuntimeEvent?: (
|
|
98
|
+
recordRuntimeEvent?: (
|
|
99
|
+
category: string,
|
|
100
|
+
error: unknown,
|
|
101
|
+
details?: Record<string, unknown>,
|
|
102
|
+
) => void;
|
|
88
103
|
ownershipCheckMs?: number;
|
|
89
104
|
}
|
|
90
105
|
|
|
@@ -116,8 +131,11 @@ export function writeLocks(path: string, locks: Record<string, unknown>): void {
|
|
|
116
131
|
}
|
|
117
132
|
}
|
|
118
133
|
|
|
119
|
-
export function parseTelegramLockEntry(
|
|
120
|
-
|
|
134
|
+
export function parseTelegramLockEntry(
|
|
135
|
+
value: unknown,
|
|
136
|
+
): TelegramLockEntry | undefined {
|
|
137
|
+
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
138
|
+
return undefined;
|
|
121
139
|
const record = value as Record<string, unknown>;
|
|
122
140
|
if (typeof record.pid !== "number") return undefined;
|
|
123
141
|
return {
|
|
@@ -140,7 +158,11 @@ function formatLock(lock: TelegramLockEntry): string {
|
|
|
140
158
|
return lock.cwd ? `pid ${lock.pid}, cwd ${lock.cwd}` : `pid ${lock.pid}`;
|
|
141
159
|
}
|
|
142
160
|
|
|
143
|
-
function getLockState(
|
|
161
|
+
function getLockState(
|
|
162
|
+
lock: TelegramLockEntry | undefined,
|
|
163
|
+
pid: number,
|
|
164
|
+
isAlive: (pid: number) => boolean,
|
|
165
|
+
): TelegramLockState {
|
|
144
166
|
if (!lock) return { kind: "inactive" };
|
|
145
167
|
if (lock.pid === pid) return { kind: "active-here", lock };
|
|
146
168
|
if (isAlive(lock.pid)) return { kind: "active-elsewhere", lock };
|
|
@@ -205,12 +227,15 @@ export function createTelegramLockRuntime<TContext extends TelegramLockContext>(
|
|
|
205
227
|
return state;
|
|
206
228
|
},
|
|
207
229
|
getState: () => getLockState(readLock(), pid, isAlive),
|
|
208
|
-
getStatusLabel: () =>
|
|
230
|
+
getStatusLabel: () =>
|
|
231
|
+
formatLockState(getLockState(readLock(), pid, isAlive)),
|
|
209
232
|
owns: (ctx) => ownsLockContext(readLock(), pid, ctx),
|
|
210
233
|
};
|
|
211
234
|
}
|
|
212
235
|
|
|
213
|
-
export function createTelegramLockedPollingRuntime<
|
|
236
|
+
export function createTelegramLockedPollingRuntime<
|
|
237
|
+
TContext extends TelegramLockContext,
|
|
238
|
+
>(
|
|
214
239
|
deps: TelegramLockedPollingRuntimeDeps<TContext>,
|
|
215
240
|
): TelegramLockedPollingRuntime<TContext> {
|
|
216
241
|
let ownershipInterval: ReturnType<typeof setInterval> | undefined;
|
|
@@ -239,8 +264,11 @@ export function createTelegramLockedPollingRuntime<TContext extends TelegramLock
|
|
|
239
264
|
const stopAfterOwnershipLoss = (ctx: TContext) => {
|
|
240
265
|
if (ownershipStop) return;
|
|
241
266
|
stopOwnershipWatcher();
|
|
242
|
-
ownershipStop = deps
|
|
243
|
-
.
|
|
267
|
+
ownershipStop = deps
|
|
268
|
+
.stopPolling()
|
|
269
|
+
.catch((error) =>
|
|
270
|
+
deps.recordRuntimeEvent?.("lock", error, { phase: "ownership-loss" }),
|
|
271
|
+
)
|
|
244
272
|
.finally(() => {
|
|
245
273
|
ownershipStop = undefined;
|
|
246
274
|
updateStatusSafely(ctx, "ownership-loss-status");
|
|
@@ -257,7 +285,8 @@ export function createTelegramLockedPollingRuntime<TContext extends TelegramLock
|
|
|
257
285
|
};
|
|
258
286
|
return {
|
|
259
287
|
start: async (ctx, options = {}) => {
|
|
260
|
-
if (!deps.hasBotToken())
|
|
288
|
+
if (!deps.hasBotToken())
|
|
289
|
+
return { ok: false, message: "Telegram bot is not configured." };
|
|
261
290
|
const acquired = deps.lock.acquire(ctx, options);
|
|
262
291
|
if (!acquired.ok) {
|
|
263
292
|
return {
|
|
@@ -279,7 +308,8 @@ export function createTelegramLockedPollingRuntime<TContext extends TelegramLock
|
|
|
279
308
|
if (state.kind === "active-elsewhere") {
|
|
280
309
|
return `Telegram bridge is active in another pi instance (${formatLock(state.lock)}).`;
|
|
281
310
|
}
|
|
282
|
-
if (state.kind === "stale")
|
|
311
|
+
if (state.kind === "stale")
|
|
312
|
+
return `Removed stale Telegram bridge lock (${formatLock(state.lock)}).`;
|
|
283
313
|
return "Telegram bridge disconnected.";
|
|
284
314
|
},
|
|
285
315
|
suspend: suspendPolling,
|
package/lib/menu.ts
CHANGED
|
@@ -807,10 +807,6 @@ export function buildTelegramModelCallbackPlan<
|
|
|
807
807
|
export async function openTelegramStatusMenu<
|
|
808
808
|
TModel extends MenuModel = MenuModel,
|
|
809
809
|
>(deps: TelegramStatusMenuOpenDeps<TModel>): Promise<void> {
|
|
810
|
-
if (!deps.isIdle()) {
|
|
811
|
-
await deps.sendBusyMessage();
|
|
812
|
-
return;
|
|
813
|
-
}
|
|
814
810
|
const state = await deps.getModelMenuState();
|
|
815
811
|
const messageId = await deps.sendStatusMenu(
|
|
816
812
|
state,
|
package/lib/prompts.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram prompt injection helpers
|
|
3
|
+
* Owns Telegram-specific system prompt suffixes injected into pi agent turns
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import type { BeforeAgentStartEvent } from "./pi.ts";
|
|
7
|
+
import { TELEGRAM_PREFIX } from "./turns.ts";
|
|
8
|
+
|
|
9
|
+
const SYSTEM_PROMPT_SUFFIX = `
|
|
10
|
+
|
|
11
|
+
Telegram bridge extension is active.
|
|
12
|
+
- Messages forwarded from Telegram are prefixed with "[telegram]".
|
|
13
|
+
- [telegram] messages may include [attachments] sections with a base directory plus relative local file entries. Resolve and read those files as needed.
|
|
14
|
+
- Telegram is often read on narrow phone screens, so prefer narrow table columns when presenting tabular data; wide monospace tables can become unreadable.
|
|
15
|
+
- If a [telegram] user asked for a file or generated artifact, use telegram_attach with the local path instead of only mentioning the path in text.
|
|
16
|
+
- Do not assume mentioning a local file path in plain text will send it to Telegram. Use telegram_attach.`;
|
|
17
|
+
|
|
18
|
+
export function buildTelegramBridgeSystemPrompt(options: {
|
|
19
|
+
prompt: string;
|
|
20
|
+
systemPrompt: string;
|
|
21
|
+
telegramPrefix?: string;
|
|
22
|
+
systemPromptSuffix: string;
|
|
23
|
+
}): { systemPrompt: string } {
|
|
24
|
+
const telegramPrefix = options.telegramPrefix ?? TELEGRAM_PREFIX;
|
|
25
|
+
const suffix = options.prompt.trimStart().startsWith(telegramPrefix)
|
|
26
|
+
? `${options.systemPromptSuffix}\n- The current user message came from Telegram.`
|
|
27
|
+
: options.systemPromptSuffix;
|
|
28
|
+
return { systemPrompt: options.systemPrompt + suffix };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function createTelegramBeforeAgentStartHook(
|
|
32
|
+
options: {
|
|
33
|
+
telegramPrefix?: string;
|
|
34
|
+
systemPromptSuffix?: string;
|
|
35
|
+
} = {},
|
|
36
|
+
): (event: BeforeAgentStartEvent) => { systemPrompt: string } {
|
|
37
|
+
return (event) =>
|
|
38
|
+
buildTelegramBridgeSystemPrompt({
|
|
39
|
+
prompt: event.prompt,
|
|
40
|
+
systemPrompt: event.systemPrompt,
|
|
41
|
+
telegramPrefix: options.telegramPrefix,
|
|
42
|
+
systemPromptSuffix: options.systemPromptSuffix ?? SYSTEM_PROMPT_SUFFIX,
|
|
43
|
+
});
|
|
44
|
+
}
|
package/lib/queue.ts
CHANGED
|
@@ -856,8 +856,9 @@ export function createTelegramAgentEndHook<
|
|
|
856
856
|
preserveQueuedTurnsAsHistory: deps.getPreserveQueuedTurnsAsHistory(),
|
|
857
857
|
resetRuntimeState: deps.resetRuntimeState,
|
|
858
858
|
updateStatus: () => deps.updateStatus(ctx),
|
|
859
|
-
dispatchNextQueuedTelegramTurn: () =>
|
|
860
|
-
deps.dispatchNextQueuedTelegramTurn(ctx),
|
|
859
|
+
dispatchNextQueuedTelegramTurn: () => {
|
|
860
|
+
setTimeout(() => deps.dispatchNextQueuedTelegramTurn(ctx), 0);
|
|
861
|
+
},
|
|
861
862
|
clearPreview: deps.clearPreview,
|
|
862
863
|
setPreviewPendingText: deps.setPreviewPendingText,
|
|
863
864
|
finalizeMarkdownPreview: deps.finalizeMarkdownPreview,
|
|
@@ -1060,6 +1061,7 @@ export interface TelegramQueueMutationControllerDeps<
|
|
|
1060
1061
|
export interface TelegramQueueMutationController<TContext> {
|
|
1061
1062
|
append: (item: TelegramQueueItem<TContext>, ctx: TContext) => void;
|
|
1062
1063
|
reorder: (ctx: TContext) => void;
|
|
1064
|
+
clear: (ctx: TContext) => number;
|
|
1063
1065
|
removeByMessageIds: (messageIds: number[], ctx: TContext) => number;
|
|
1064
1066
|
clearPriorityByMessageId: (messageId: number, ctx: TContext) => boolean;
|
|
1065
1067
|
prioritizeByMessageId: (messageId: number, ctx: TContext) => boolean;
|
|
@@ -1261,6 +1263,7 @@ export function createTelegramQueueMutationController<TContext>(
|
|
|
1261
1263
|
append: (item, ctx) =>
|
|
1262
1264
|
appendTelegramQueueItemRuntime(item, buildRuntimeDeps(ctx)),
|
|
1263
1265
|
reorder: (ctx) => reorderTelegramQueueItemsRuntime(buildRuntimeDeps(ctx)),
|
|
1266
|
+
clear: (ctx) => clearTelegramQueueItemsRuntime(buildRuntimeDeps(ctx)),
|
|
1264
1267
|
removeByMessageIds: (messageIds, ctx) =>
|
|
1265
1268
|
removeTelegramQueueItemsByMessageIdsRuntime(
|
|
1266
1269
|
messageIds,
|
|
@@ -1290,6 +1293,16 @@ export function reorderTelegramQueueItemsRuntime<TContext>(
|
|
|
1290
1293
|
deps.updateStatus(deps.ctx);
|
|
1291
1294
|
}
|
|
1292
1295
|
|
|
1296
|
+
export function clearTelegramQueueItemsRuntime<TContext>(
|
|
1297
|
+
deps: TelegramQueueMutationRuntimeDeps<TContext>,
|
|
1298
|
+
): number {
|
|
1299
|
+
const removedCount = deps.getQueuedItems().length;
|
|
1300
|
+
if (removedCount === 0) return 0;
|
|
1301
|
+
deps.setQueuedItems([]);
|
|
1302
|
+
deps.updateStatus(deps.ctx);
|
|
1303
|
+
return removedCount;
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1293
1306
|
export function removeTelegramQueueItemsByMessageIdsRuntime<TContext>(
|
|
1294
1307
|
messageIds: number[],
|
|
1295
1308
|
deps: TelegramQueueMutationRuntimeDeps<TContext>,
|
|
@@ -1400,8 +1413,9 @@ export interface TelegramRuntimeEventRecorderPort {
|
|
|
1400
1413
|
) => void;
|
|
1401
1414
|
}
|
|
1402
1415
|
|
|
1403
|
-
export interface TelegramControlRuntimeDeps<
|
|
1404
|
-
|
|
1416
|
+
export interface TelegramControlRuntimeDeps<
|
|
1417
|
+
TContext,
|
|
1418
|
+
> extends TelegramRuntimeEventRecorderPort {
|
|
1405
1419
|
ctx: TContext;
|
|
1406
1420
|
sendTextReply: (
|
|
1407
1421
|
chatId: number,
|
|
@@ -1454,8 +1468,9 @@ export interface TelegramDispatchRuntimeDeps<TContext = unknown> {
|
|
|
1454
1468
|
onIdle: () => void;
|
|
1455
1469
|
}
|
|
1456
1470
|
|
|
1457
|
-
export interface TelegramQueueDispatchControllerDeps<
|
|
1458
|
-
|
|
1471
|
+
export interface TelegramQueueDispatchControllerDeps<
|
|
1472
|
+
TContext = unknown,
|
|
1473
|
+
> extends TelegramRuntimeEventRecorderPort {
|
|
1459
1474
|
getQueuedItems: () => TelegramQueueItem<TContext>[];
|
|
1460
1475
|
setQueuedItems: (items: TelegramQueueItem<TContext>[]) => void;
|
|
1461
1476
|
canDispatch: (ctx: TContext) => boolean;
|
package/lib/routing.ts
CHANGED
|
@@ -116,7 +116,8 @@ export function createTelegramInboundRouteRuntime<
|
|
|
116
116
|
isIdle: deps.isIdle,
|
|
117
117
|
hasActiveTelegramTurn: deps.activeTurnRuntime.has,
|
|
118
118
|
hasAbortHandler: deps.bridgeRuntime.abort.hasHandler,
|
|
119
|
-
getActiveToolExecutions:
|
|
119
|
+
getActiveToolExecutions:
|
|
120
|
+
deps.bridgeRuntime.lifecycle.getActiveToolExecutions,
|
|
120
121
|
setModel: deps.setModel,
|
|
121
122
|
setCurrentModel: deps.currentModelRuntime.setCurrentModel,
|
|
122
123
|
stagePendingModelSwitch: deps.modelSwitchController.stagePendingSwitch,
|
|
@@ -130,6 +131,7 @@ export function createTelegramInboundRouteRuntime<
|
|
|
130
131
|
hasAbortHandler: deps.bridgeRuntime.abort.hasHandler,
|
|
131
132
|
clearPendingModelSwitch: deps.modelSwitchController.clearPendingSwitch,
|
|
132
133
|
hasQueuedTelegramItems: deps.telegramQueueStore.hasQueuedItems,
|
|
134
|
+
clearQueuedTelegramItems: deps.queueMutationRuntime.clear,
|
|
133
135
|
setPreserveQueuedTurnsAsHistory:
|
|
134
136
|
deps.bridgeRuntime.lifecycle.setPreserveQueuedTurnsAsHistory,
|
|
135
137
|
abortCurrentTurn: deps.bridgeRuntime.abort.abortTurn,
|
|
@@ -138,7 +140,8 @@ export function createTelegramInboundRouteRuntime<
|
|
|
138
140
|
hasActiveTelegramTurn: deps.activeTurnRuntime.has,
|
|
139
141
|
hasDispatchPending: deps.bridgeRuntime.lifecycle.hasDispatchPending,
|
|
140
142
|
isCompactionInProgress: deps.bridgeRuntime.lifecycle.isCompactionInProgress,
|
|
141
|
-
setCompactionInProgress:
|
|
143
|
+
setCompactionInProgress:
|
|
144
|
+
deps.bridgeRuntime.lifecycle.setCompactionInProgress,
|
|
142
145
|
updateStatus: deps.updateStatus,
|
|
143
146
|
dispatchNextQueuedTelegramTurn: deps.dispatchNextQueuedTelegramTurn,
|
|
144
147
|
compact: deps.compact,
|
package/lib/runtime.ts
CHANGED
|
@@ -327,8 +327,9 @@ export interface TelegramRuntimeEventRecorderPort {
|
|
|
327
327
|
) => void;
|
|
328
328
|
}
|
|
329
329
|
|
|
330
|
-
export interface TelegramTypingLoopStarterDeps<
|
|
331
|
-
|
|
330
|
+
export interface TelegramTypingLoopStarterDeps<
|
|
331
|
+
TContext,
|
|
332
|
+
> extends TelegramRuntimeEventRecorderPort {
|
|
332
333
|
typing: TelegramRuntimeTypingPort;
|
|
333
334
|
getDefaultChatId: () => number | undefined;
|
|
334
335
|
sendTypingAction: (chatId: number) => Promise<unknown>;
|
|
@@ -413,8 +414,9 @@ export function createTelegramAgentEndResetter(
|
|
|
413
414
|
};
|
|
414
415
|
}
|
|
415
416
|
|
|
416
|
-
export interface TelegramPromptDispatchLifecycleDeps<
|
|
417
|
-
|
|
417
|
+
export interface TelegramPromptDispatchLifecycleDeps<
|
|
418
|
+
TContext,
|
|
419
|
+
> extends TelegramRuntimeEventRecorderPort {
|
|
418
420
|
lifecycle: Pick<
|
|
419
421
|
TelegramRuntimeLifecyclePort,
|
|
420
422
|
"setDispatchPending" | "clearDispatchPending"
|
|
@@ -424,8 +426,9 @@ export interface TelegramPromptDispatchLifecycleDeps<TContext>
|
|
|
424
426
|
updateStatus: (ctx: TContext, error?: string) => void;
|
|
425
427
|
}
|
|
426
428
|
|
|
427
|
-
export interface TelegramPromptDispatchRuntimeDeps<
|
|
428
|
-
|
|
429
|
+
export interface TelegramPromptDispatchRuntimeDeps<
|
|
430
|
+
TContext,
|
|
431
|
+
> extends TelegramRuntimeEventRecorderPort {
|
|
429
432
|
lifecycle: TelegramPromptDispatchLifecycleDeps<TContext>["lifecycle"];
|
|
430
433
|
typing: TelegramRuntimeTypingPort;
|
|
431
434
|
getDefaultChatId: () => number | undefined;
|
package/lib/setup.ts
CHANGED
|
@@ -86,7 +86,11 @@ const TELEGRAM_BOT_TOKEN_ENV_VARS = [
|
|
|
86
86
|
function isTelegramPollingStartResult(
|
|
87
87
|
value: unknown,
|
|
88
88
|
): value is TelegramPollingStartResult {
|
|
89
|
-
return
|
|
89
|
+
return (
|
|
90
|
+
!!value &&
|
|
91
|
+
typeof value === "object" &&
|
|
92
|
+
typeof (value as { ok?: unknown }).ok === "boolean"
|
|
93
|
+
);
|
|
90
94
|
}
|
|
91
95
|
|
|
92
96
|
export function getTelegramBotTokenInputDefault(
|
package/lib/status.ts
CHANGED
|
@@ -108,6 +108,7 @@ export interface TelegramStatusBarState {
|
|
|
108
108
|
paired: boolean;
|
|
109
109
|
compactionInProgress: boolean;
|
|
110
110
|
processing: boolean;
|
|
111
|
+
processingStatus?: string;
|
|
111
112
|
queuedStatus: string;
|
|
112
113
|
error?: string;
|
|
113
114
|
}
|
|
@@ -333,6 +334,10 @@ export function createTelegramBridgeStatusRuntime<
|
|
|
333
334
|
getStatusBarState: (_ctx, error) => {
|
|
334
335
|
const config = deps.getConfig();
|
|
335
336
|
const queuedItems = deps.getQueuedItems();
|
|
337
|
+
const hasActiveTurn = deps.hasActiveTurn();
|
|
338
|
+
const hasPendingDispatch = deps.hasDispatchPending();
|
|
339
|
+
const hasPendingModelSwitch = deps.hasPendingModelSwitch();
|
|
340
|
+
const activeToolExecutions = deps.getActiveToolExecutions();
|
|
336
341
|
const compactionInProgress = deps.isCompactionInProgress();
|
|
337
342
|
return {
|
|
338
343
|
hasBotToken: !!config.botToken,
|
|
@@ -340,9 +345,14 @@ export function createTelegramBridgeStatusRuntime<
|
|
|
340
345
|
paired: !!config.allowedUserId,
|
|
341
346
|
compactionInProgress,
|
|
342
347
|
processing:
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
348
|
+
hasActiveTurn || hasPendingDispatch || queuedItems.length > 0,
|
|
349
|
+
processingStatus: getTelegramStatusBarProcessingStatus({
|
|
350
|
+
hasActiveTurn,
|
|
351
|
+
hasPendingDispatch,
|
|
352
|
+
hasPendingModelSwitch,
|
|
353
|
+
activeToolExecutions,
|
|
354
|
+
queuedItems: queuedItems.length,
|
|
355
|
+
}),
|
|
346
356
|
queuedStatus: deps.formatQueuedStatus(queuedItems),
|
|
347
357
|
error,
|
|
348
358
|
};
|
|
@@ -367,6 +377,21 @@ export function createTelegramBridgeStatusRuntime<
|
|
|
367
377
|
});
|
|
368
378
|
}
|
|
369
379
|
|
|
380
|
+
export function getTelegramStatusBarProcessingStatus(state: {
|
|
381
|
+
hasActiveTurn: boolean;
|
|
382
|
+
hasPendingDispatch: boolean;
|
|
383
|
+
hasPendingModelSwitch: boolean;
|
|
384
|
+
activeToolExecutions: number;
|
|
385
|
+
queuedItems: number;
|
|
386
|
+
}): string | undefined {
|
|
387
|
+
if (state.hasPendingModelSwitch) return "model";
|
|
388
|
+
if (state.activeToolExecutions > 0) return "tool running";
|
|
389
|
+
if (state.hasActiveTurn) return "active";
|
|
390
|
+
if (state.hasPendingDispatch) return "dispatching";
|
|
391
|
+
if (state.queuedItems > 0) return "queued";
|
|
392
|
+
return undefined;
|
|
393
|
+
}
|
|
394
|
+
|
|
370
395
|
export function buildTelegramStatusBarText(
|
|
371
396
|
theme: TelegramStatusBarTheme,
|
|
372
397
|
state: TelegramStatusBarState,
|
|
@@ -386,7 +411,7 @@ export function buildTelegramStatusBarText(
|
|
|
386
411
|
return `${label} ${theme.fg("accent", "compacting")}${queued}`;
|
|
387
412
|
}
|
|
388
413
|
if (state.processing) {
|
|
389
|
-
return `${label} ${theme.fg("accent", "processing")}${queued}`;
|
|
414
|
+
return `${label} ${theme.fg("accent", state.processingStatus ?? "processing")}${queued}`;
|
|
390
415
|
}
|
|
391
416
|
return `${label} ${theme.fg("success", "connected")}`;
|
|
392
417
|
}
|
package/lib/turns.ts
CHANGED
|
@@ -57,9 +57,7 @@ export function formatTelegramTurnStatusSummary(
|
|
|
57
57
|
): string {
|
|
58
58
|
const textSummary = truncateTelegramQueueSummary(rawText);
|
|
59
59
|
if (textSummary) return textSummary;
|
|
60
|
-
const handlerSummary = truncateTelegramQueueSummary(
|
|
61
|
-
handlerOutputs.join(" "),
|
|
62
|
-
);
|
|
60
|
+
const handlerSummary = truncateTelegramQueueSummary(handlerOutputs.join(" "));
|
|
63
61
|
if (handlerSummary) return handlerSummary;
|
|
64
62
|
if (files.length === 1) {
|
|
65
63
|
const fileName = basename(
|
|
@@ -121,7 +119,11 @@ export function buildTelegramTurnPrompt(options: {
|
|
|
121
119
|
}
|
|
122
120
|
const promptFiles = options.promptFiles ?? options.files;
|
|
123
121
|
prompt = appendTelegramAttachmentSection(prompt, promptFiles);
|
|
124
|
-
prompt = appendTelegramListSection(
|
|
122
|
+
prompt = appendTelegramListSection(
|
|
123
|
+
prompt,
|
|
124
|
+
"outputs",
|
|
125
|
+
options.handlerOutputs ?? [],
|
|
126
|
+
);
|
|
125
127
|
return prompt;
|
|
126
128
|
}
|
|
127
129
|
|
|
@@ -158,7 +160,9 @@ function splitTelegramPromptAttachmentSuffix(prompt: string): {
|
|
|
158
160
|
const attachmentFiles = attachmentLines
|
|
159
161
|
.map((line) => line.match(/^- (.+)$/)?.[1]?.trim())
|
|
160
162
|
.filter((path): path is string => !!path)
|
|
161
|
-
.map((path) =>
|
|
163
|
+
.map((path) =>
|
|
164
|
+
attachmentDir ? join(attachmentDir, path.replace(/^\/+/, "")) : path,
|
|
165
|
+
)
|
|
162
166
|
.map((path) => ({
|
|
163
167
|
path,
|
|
164
168
|
fileName: basename(path),
|
|
@@ -301,8 +305,9 @@ export type BuildTelegramPromptTurnRuntimeOptions = Omit<
|
|
|
301
305
|
"readBinaryFile"
|
|
302
306
|
>;
|
|
303
307
|
|
|
304
|
-
export interface TelegramPromptTurnRuntimeBuilderDeps<
|
|
305
|
-
|
|
308
|
+
export interface TelegramPromptTurnRuntimeBuilderDeps<
|
|
309
|
+
TContext = unknown,
|
|
310
|
+
> extends DownloadTelegramMessageFilesDeps {
|
|
306
311
|
allocateQueueOrder: () => number;
|
|
307
312
|
processAttachments?: (
|
|
308
313
|
files: DownloadedTelegramTurnFile[],
|