@llblab/pi-telegram 0.6.2 → 0.7.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 +152 -0
- package/BACKLOG.md +5 -0
- package/CHANGELOG.md +142 -0
- package/README.md +49 -42
- package/docs/architecture.md +49 -40
- package/docs/attachment-handlers.md +4 -6
- package/docs/command-templates.md +53 -9
- package/docs/locks.md +4 -0
- package/docs/outbound-handlers.md +20 -14
- package/index.ts +217 -153
- package/lib/api.ts +1 -0
- package/lib/attachment-handlers.ts +16 -9
- package/lib/attachments.ts +1 -0
- package/lib/command-templates.ts +32 -3
- package/lib/commands.ts +367 -80
- package/lib/config.ts +10 -8
- package/lib/keyboard.ts +14 -0
- package/lib/lifecycle.ts +26 -0
- package/lib/locks.ts +3 -2
- package/lib/media.ts +1 -0
- package/lib/menu-model.ts +881 -0
- package/lib/menu-queue.ts +608 -0
- package/lib/menu-status.ts +226 -0
- package/lib/menu-thinking.ts +171 -0
- package/lib/menu.ts +143 -1019
- package/lib/model.ts +1 -0
- package/lib/outbound-handlers.ts +120 -45
- package/lib/pi.ts +8 -0
- package/lib/polling.ts +1 -0
- package/lib/preview.ts +97 -50
- package/lib/prompt-templates.ts +150 -0
- package/lib/prompts.ts +17 -9
- package/lib/queue.ts +51 -15
- package/lib/rendering.ts +1 -0
- package/lib/replies.ts +86 -2
- package/lib/routing.ts +76 -14
- package/lib/runtime.ts +2 -0
- package/lib/setup.ts +1 -0
- package/lib/status.ts +15 -6
- package/lib/turns.ts +1 -0
- package/lib/updates.ts +36 -6
- package/package.json +4 -1
package/index.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Telegram bridge extension entrypoint and orchestration layer
|
|
3
|
+
* Zones: telegram, pi agent, orchestration
|
|
3
4
|
* Keeps the runtime wiring in one place while delegating reusable domain logic to /lib modules
|
|
4
5
|
*/
|
|
5
6
|
|
|
@@ -9,14 +10,17 @@ import * as Attachments from "./lib/attachments.ts";
|
|
|
9
10
|
import * as Commands from "./lib/commands.ts";
|
|
10
11
|
import * as CommandTemplates from "./lib/command-templates.ts";
|
|
11
12
|
import * as Config from "./lib/config.ts";
|
|
13
|
+
import * as Keyboard from "./lib/keyboard.ts";
|
|
12
14
|
import * as Lifecycle from "./lib/lifecycle.ts";
|
|
13
15
|
import * as Locks from "./lib/locks.ts";
|
|
14
16
|
import * as Media from "./lib/media.ts";
|
|
15
17
|
import * as Menu from "./lib/menu.ts";
|
|
18
|
+
import * as MenuQueue from "./lib/menu-queue.ts";
|
|
16
19
|
import * as Model from "./lib/model.ts";
|
|
17
20
|
import * as Pi from "./lib/pi.ts";
|
|
18
21
|
import * as Polling from "./lib/polling.ts";
|
|
19
22
|
import * as Preview from "./lib/preview.ts";
|
|
23
|
+
import * as PromptTemplates from "./lib/prompt-templates.ts";
|
|
20
24
|
import * as Prompts from "./lib/prompts.ts";
|
|
21
25
|
import * as Queue from "./lib/queue.ts";
|
|
22
26
|
import * as Replies from "./lib/replies.ts";
|
|
@@ -33,7 +37,15 @@ type RuntimeTelegramQueueItem = Queue.TelegramQueueItem<Pi.ExtensionContext>;
|
|
|
33
37
|
|
|
34
38
|
export default function (pi: Pi.ExtensionAPI) {
|
|
35
39
|
const piRuntime = Pi.createExtensionApiRuntimePorts(pi);
|
|
40
|
+
const {
|
|
41
|
+
getCommands,
|
|
42
|
+
getThinkingLevel,
|
|
43
|
+
sendUserMessage,
|
|
44
|
+
setModel,
|
|
45
|
+
setThinkingLevel,
|
|
46
|
+
} = piRuntime;
|
|
36
47
|
const bridgeRuntime = Runtime.createTelegramBridgeRuntime();
|
|
48
|
+
const { abort, lifecycle, queue, setup, typing } = bridgeRuntime;
|
|
37
49
|
const configStore = Config.createTelegramConfigStore();
|
|
38
50
|
const lockRuntime = Locks.createTelegramLockRuntime<Pi.ExtensionContext>();
|
|
39
51
|
const activeTurnRuntime = Queue.createTelegramActiveTurnStore();
|
|
@@ -46,6 +58,11 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
46
58
|
const runtimeEvents = Status.createTelegramRuntimeEventRecorder({
|
|
47
59
|
getBotToken: configStore.getBotToken,
|
|
48
60
|
});
|
|
61
|
+
const recordRuntimeEvent = runtimeEvents.record;
|
|
62
|
+
const getContextModel = Pi.getExtensionContextModel;
|
|
63
|
+
const isIdle = Pi.isExtensionContextIdle;
|
|
64
|
+
const hasPendingMessages = Pi.hasExtensionContextPendingMessages;
|
|
65
|
+
const compact = Pi.compactExtensionContext;
|
|
49
66
|
const mediaGroupRuntime = Media.createTelegramMediaGroupController<
|
|
50
67
|
Api.TelegramMessage,
|
|
51
68
|
Pi.ExtensionContext
|
|
@@ -54,7 +71,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
54
71
|
Queue.createTelegramQueueStore<Pi.ExtensionContext>();
|
|
55
72
|
const deferredQueueDispatchRuntime =
|
|
56
73
|
Queue.createTelegramDeferredQueueDispatchRuntime<Pi.ExtensionContext>({
|
|
57
|
-
recordRuntimeEvent
|
|
74
|
+
recordRuntimeEvent,
|
|
58
75
|
});
|
|
59
76
|
const pollingControllerState = Polling.createTelegramPollingControllerState();
|
|
60
77
|
const { getStatusLines, updateStatus } =
|
|
@@ -68,9 +85,9 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
68
85
|
),
|
|
69
86
|
getActiveSourceMessageIds: activeTurnRuntime.getSourceMessageIds,
|
|
70
87
|
hasActiveTurn: activeTurnRuntime.has,
|
|
71
|
-
hasDispatchPending:
|
|
72
|
-
isCompactionInProgress:
|
|
73
|
-
getActiveToolExecutions:
|
|
88
|
+
hasDispatchPending: lifecycle.hasDispatchPending,
|
|
89
|
+
isCompactionInProgress: lifecycle.isCompactionInProgress,
|
|
90
|
+
getActiveToolExecutions: lifecycle.getActiveToolExecutions,
|
|
74
91
|
hasPendingModelSwitch: pendingModelSwitchStore.has,
|
|
75
92
|
getQueuedItems: telegramQueueStore.getQueuedItems,
|
|
76
93
|
formatQueuedStatus: Queue.formatQueuedTelegramItemsStatus,
|
|
@@ -81,16 +98,15 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
81
98
|
Pi.ExtensionContext,
|
|
82
99
|
ActivePiModel
|
|
83
100
|
>({
|
|
84
|
-
getContextModel
|
|
101
|
+
getContextModel,
|
|
85
102
|
updateStatus,
|
|
86
103
|
});
|
|
87
104
|
const queueMutationRuntime =
|
|
88
105
|
Queue.createTelegramQueueMutationController<Pi.ExtensionContext>({
|
|
89
106
|
...telegramQueueStore,
|
|
90
|
-
getNextPriorityReactionOrder:
|
|
91
|
-
bridgeRuntime.queue.getNextPriorityReactionOrder,
|
|
107
|
+
getNextPriorityReactionOrder: queue.getNextPriorityReactionOrder,
|
|
92
108
|
incrementNextPriorityReactionOrder:
|
|
93
|
-
|
|
109
|
+
queue.incrementNextPriorityReactionOrder,
|
|
94
110
|
updateStatus,
|
|
95
111
|
});
|
|
96
112
|
const attachmentHandlerRuntime =
|
|
@@ -99,7 +115,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
99
115
|
getHandlers: configStore.getAttachmentHandlers,
|
|
100
116
|
execCommand: CommandTemplates.execCommandTemplate,
|
|
101
117
|
getCwd: Pi.getExtensionContextCwd,
|
|
102
|
-
recordRuntimeEvent
|
|
118
|
+
recordRuntimeEvent,
|
|
103
119
|
},
|
|
104
120
|
);
|
|
105
121
|
|
|
@@ -119,19 +135,19 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
119
135
|
prepareTempDir,
|
|
120
136
|
} = Api.createDefaultTelegramBridgeApiRuntime({
|
|
121
137
|
getBotToken: configStore.getBotToken,
|
|
122
|
-
recordRuntimeEvent
|
|
138
|
+
recordRuntimeEvent,
|
|
123
139
|
});
|
|
124
140
|
|
|
125
141
|
// --- Message Delivery & Preview ---
|
|
126
142
|
|
|
127
143
|
const promptDispatchRuntime =
|
|
128
144
|
Runtime.createTelegramPromptDispatchRuntime<Pi.ExtensionContext>({
|
|
129
|
-
lifecycle
|
|
130
|
-
typing
|
|
145
|
+
lifecycle,
|
|
146
|
+
typing,
|
|
131
147
|
getDefaultChatId: activeTurnRuntime.getChatId,
|
|
132
148
|
sendTypingAction,
|
|
133
149
|
updateStatus,
|
|
134
|
-
recordRuntimeEvent
|
|
150
|
+
recordRuntimeEvent,
|
|
135
151
|
});
|
|
136
152
|
|
|
137
153
|
// --- Reply Runtime Wiring ---
|
|
@@ -143,7 +159,7 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
143
159
|
editInteractiveMessage,
|
|
144
160
|
sendInteractiveMessage,
|
|
145
161
|
} =
|
|
146
|
-
Replies.createTelegramRenderedMessageDeliveryRuntime<
|
|
162
|
+
Replies.createTelegramRenderedMessageDeliveryRuntime<Keyboard.TelegramInlineKeyboardMarkup>(
|
|
147
163
|
{
|
|
148
164
|
sendMessage,
|
|
149
165
|
editMessage: editTelegramMessageText,
|
|
@@ -152,19 +168,22 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
152
168
|
const dispatchNextQueuedTelegramTurn =
|
|
153
169
|
Queue.createTelegramQueueDispatchRuntime<Pi.ExtensionContext>({
|
|
154
170
|
...telegramQueueStore,
|
|
155
|
-
isCompactionInProgress:
|
|
171
|
+
isCompactionInProgress: lifecycle.isCompactionInProgress,
|
|
156
172
|
hasActiveTurn: activeTurnRuntime.has,
|
|
157
|
-
hasDispatchPending:
|
|
158
|
-
isIdle
|
|
159
|
-
hasPendingMessages
|
|
173
|
+
hasDispatchPending: lifecycle.hasDispatchPending,
|
|
174
|
+
isIdle,
|
|
175
|
+
hasPendingMessages,
|
|
160
176
|
hasDispatchContext: deferredQueueDispatchRuntime.isBound,
|
|
161
177
|
updateStatus,
|
|
162
178
|
sendTextReply,
|
|
163
|
-
recordRuntimeEvent
|
|
179
|
+
recordRuntimeEvent,
|
|
164
180
|
...promptDispatchRuntime,
|
|
165
|
-
sendUserMessage
|
|
181
|
+
sendUserMessage,
|
|
166
182
|
}).dispatchNext;
|
|
167
|
-
const previewRuntime = Preview.createTelegramAssistantPreviewRuntime
|
|
183
|
+
const previewRuntime = Preview.createTelegramAssistantPreviewRuntime<
|
|
184
|
+
unknown,
|
|
185
|
+
Keyboard.TelegramInlineKeyboardMarkup
|
|
186
|
+
>({
|
|
168
187
|
getActiveTurn: activeTurnRuntime.get,
|
|
169
188
|
isAssistantMessage: Replies.isAssistantAgentMessage,
|
|
170
189
|
getMessageText: Replies.getAgentMessageText,
|
|
@@ -182,18 +201,26 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
182
201
|
Pi.ExtensionContext,
|
|
183
202
|
Model.ScopedTelegramModel<ActivePiModel>
|
|
184
203
|
>({
|
|
185
|
-
isIdle
|
|
204
|
+
isIdle,
|
|
186
205
|
getPendingModelSwitch: pendingModelSwitchStore.get,
|
|
187
206
|
setPendingModelSwitch: pendingModelSwitchStore.set,
|
|
188
207
|
getActiveTurn: activeTurnRuntime.get,
|
|
189
|
-
getAbortHandler:
|
|
190
|
-
hasAbortHandler:
|
|
191
|
-
getActiveToolExecutions:
|
|
192
|
-
allocateItemOrder:
|
|
193
|
-
allocateControlOrder:
|
|
208
|
+
getAbortHandler: abort.getHandler,
|
|
209
|
+
hasAbortHandler: abort.hasHandler,
|
|
210
|
+
getActiveToolExecutions: lifecycle.getActiveToolExecutions,
|
|
211
|
+
allocateItemOrder: queue.allocateItemOrder,
|
|
212
|
+
allocateControlOrder: queue.allocateControlOrder,
|
|
194
213
|
appendQueuedItem: queueMutationRuntime.append,
|
|
195
214
|
updateStatus,
|
|
196
215
|
});
|
|
216
|
+
const getQueueItemCount = Queue.createTelegramQueueItemCountGetter(
|
|
217
|
+
telegramQueueStore,
|
|
218
|
+
);
|
|
219
|
+
const getPromptTemplateCommands =
|
|
220
|
+
PromptTemplates.createTelegramPromptTemplateCommandGetter({
|
|
221
|
+
getCommands,
|
|
222
|
+
reservedCommandNames: Commands.TELEGRAM_RESERVED_COMMAND_NAMES,
|
|
223
|
+
});
|
|
197
224
|
const menuActions = Menu.createTelegramMenuActionRuntimeWithStateBuilder<
|
|
198
225
|
ActivePiModel,
|
|
199
226
|
Pi.ExtensionContext
|
|
@@ -201,20 +228,80 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
201
228
|
runtime: modelMenuRuntime,
|
|
202
229
|
createSettingsManager: Pi.createSettingsManager,
|
|
203
230
|
getActiveModel: currentModelRuntime.get,
|
|
204
|
-
getThinkingLevel
|
|
205
|
-
|
|
206
|
-
|
|
231
|
+
getThinkingLevel,
|
|
232
|
+
getQueueItemCount,
|
|
233
|
+
buildStatusHtml: Commands.createTelegramAppMenuHtmlBuilder({
|
|
234
|
+
buildStatusHtml: Status.createTelegramStatusHtmlBuilder({
|
|
235
|
+
getActiveModel: currentModelRuntime.get,
|
|
236
|
+
}),
|
|
237
|
+
getPromptTemplateCommands,
|
|
207
238
|
}),
|
|
208
239
|
storeModelMenuState: modelMenuRuntime.storeState,
|
|
209
|
-
isIdle
|
|
240
|
+
isIdle,
|
|
210
241
|
canOfferInFlightModelSwitch: modelSwitchController.canOfferInFlightSwitch,
|
|
211
242
|
sendTextReply,
|
|
212
243
|
editInteractiveMessage,
|
|
213
244
|
sendInteractiveMessage,
|
|
214
245
|
});
|
|
215
246
|
|
|
247
|
+
// --- Queue Menu ---
|
|
248
|
+
|
|
249
|
+
const getQueueMenuState = Menu.createTelegramModelMenuStateBuilder({
|
|
250
|
+
runtime: modelMenuRuntime,
|
|
251
|
+
createSettingsManager: Pi.createSettingsManager,
|
|
252
|
+
getActiveModel: currentModelRuntime.get,
|
|
253
|
+
});
|
|
254
|
+
const queueMenuRuntime = MenuQueue.createTelegramQueueMenuRuntime({
|
|
255
|
+
telegramQueueStore,
|
|
256
|
+
queueMutationRuntime,
|
|
257
|
+
sendInteractiveMessage,
|
|
258
|
+
editInteractiveMessage,
|
|
259
|
+
answerCallbackQuery,
|
|
260
|
+
getModelMenuState: getQueueMenuState,
|
|
261
|
+
getStoredModelMenuState: modelMenuRuntime.getState,
|
|
262
|
+
storeModelMenuState: modelMenuRuntime.storeState,
|
|
263
|
+
updateStatusMessage: menuActions.updateStatusMessage,
|
|
264
|
+
updateStatus,
|
|
265
|
+
});
|
|
266
|
+
|
|
216
267
|
// --- Polling ---
|
|
217
268
|
|
|
269
|
+
const inboundRouteRuntime = Routing.createTelegramInboundRouteRuntime<
|
|
270
|
+
Api.TelegramUpdate,
|
|
271
|
+
Api.TelegramMessage,
|
|
272
|
+
Api.TelegramCallbackQuery,
|
|
273
|
+
Pi.ExtensionContext,
|
|
274
|
+
ActivePiModel
|
|
275
|
+
>({
|
|
276
|
+
configStore,
|
|
277
|
+
bridgeRuntime,
|
|
278
|
+
activeTurnRuntime,
|
|
279
|
+
mediaGroupRuntime,
|
|
280
|
+
telegramQueueStore,
|
|
281
|
+
queueMutationRuntime,
|
|
282
|
+
modelMenuRuntime,
|
|
283
|
+
currentModelRuntime,
|
|
284
|
+
modelSwitchController,
|
|
285
|
+
menuActions,
|
|
286
|
+
openQueueMenu: queueMenuRuntime.openQueueMenu,
|
|
287
|
+
queueMenuCallbackHandler: queueMenuRuntime.handleCallbackQuery,
|
|
288
|
+
buttonActionStore,
|
|
289
|
+
attachmentHandlerRuntime,
|
|
290
|
+
updateStatus,
|
|
291
|
+
dispatchNextQueuedTelegramTurn,
|
|
292
|
+
answerCallbackQuery,
|
|
293
|
+
sendTextReply,
|
|
294
|
+
setMyCommands,
|
|
295
|
+
getCommands,
|
|
296
|
+
downloadFile: downloadTelegramBridgeFile,
|
|
297
|
+
getThinkingLevel,
|
|
298
|
+
setThinkingLevel,
|
|
299
|
+
setModel,
|
|
300
|
+
isIdle,
|
|
301
|
+
hasPendingMessages,
|
|
302
|
+
compact,
|
|
303
|
+
recordRuntimeEvent,
|
|
304
|
+
});
|
|
218
305
|
const pollingRuntime = Polling.createTelegramPollingControllerRuntime<
|
|
219
306
|
Api.TelegramUpdate,
|
|
220
307
|
Pi.ExtensionContext
|
|
@@ -225,42 +312,10 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
225
312
|
deleteWebhook,
|
|
226
313
|
getUpdates,
|
|
227
314
|
persistConfig: configStore.persist,
|
|
228
|
-
handleUpdate:
|
|
229
|
-
|
|
230
|
-
Api.TelegramMessage,
|
|
231
|
-
Api.TelegramCallbackQuery,
|
|
232
|
-
Pi.ExtensionContext,
|
|
233
|
-
ActivePiModel
|
|
234
|
-
>({
|
|
235
|
-
configStore,
|
|
236
|
-
bridgeRuntime,
|
|
237
|
-
activeTurnRuntime,
|
|
238
|
-
mediaGroupRuntime,
|
|
239
|
-
telegramQueueStore,
|
|
240
|
-
queueMutationRuntime,
|
|
241
|
-
modelMenuRuntime,
|
|
242
|
-
currentModelRuntime,
|
|
243
|
-
modelSwitchController,
|
|
244
|
-
menuActions,
|
|
245
|
-
buttonActionStore,
|
|
246
|
-
attachmentHandlerRuntime,
|
|
247
|
-
updateStatus,
|
|
248
|
-
dispatchNextQueuedTelegramTurn,
|
|
249
|
-
answerCallbackQuery,
|
|
250
|
-
sendTextReply,
|
|
251
|
-
setMyCommands,
|
|
252
|
-
downloadFile: downloadTelegramBridgeFile,
|
|
253
|
-
getThinkingLevel: piRuntime.getThinkingLevel,
|
|
254
|
-
setThinkingLevel: piRuntime.setThinkingLevel,
|
|
255
|
-
setModel: piRuntime.setModel,
|
|
256
|
-
isIdle: Pi.isExtensionContextIdle,
|
|
257
|
-
hasPendingMessages: Pi.hasExtensionContextPendingMessages,
|
|
258
|
-
compact: Pi.compactExtensionContext,
|
|
259
|
-
recordRuntimeEvent: runtimeEvents.record,
|
|
260
|
-
}).handleUpdate,
|
|
261
|
-
stopTypingLoop: bridgeRuntime.typing.stop,
|
|
315
|
+
handleUpdate: inboundRouteRuntime.handleUpdate,
|
|
316
|
+
stopTypingLoop: typing.stop,
|
|
262
317
|
updateStatus,
|
|
263
|
-
recordRuntimeEvent
|
|
318
|
+
recordRuntimeEvent,
|
|
264
319
|
});
|
|
265
320
|
const lockedPollingRuntime = Locks.createTelegramLockedPollingRuntime({
|
|
266
321
|
lock: lockRuntime,
|
|
@@ -268,34 +323,35 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
268
323
|
startPolling: pollingRuntime.start,
|
|
269
324
|
stopPolling: pollingRuntime.stop,
|
|
270
325
|
updateStatus,
|
|
271
|
-
recordRuntimeEvent
|
|
326
|
+
recordRuntimeEvent,
|
|
327
|
+
});
|
|
328
|
+
const queueSessionLifecycle = Queue.createTelegramSessionLifecycleRuntime<
|
|
329
|
+
Pi.ExtensionContext,
|
|
330
|
+
RuntimeTelegramQueueItem,
|
|
331
|
+
ActivePiModel
|
|
332
|
+
>({
|
|
333
|
+
getCurrentModel: getContextModel,
|
|
334
|
+
loadConfig: configStore.load,
|
|
335
|
+
setQueuedItems: telegramQueueStore.setQueuedItems,
|
|
336
|
+
setCurrentModel: currentModelRuntime.set,
|
|
337
|
+
setPendingModelSwitch: pendingModelSwitchStore.set,
|
|
338
|
+
syncCounters: queue.syncCounters,
|
|
339
|
+
syncFlags: lifecycle.syncFlags,
|
|
340
|
+
bindDeferredDispatchContext: deferredQueueDispatchRuntime.bind,
|
|
341
|
+
prepareTempDir,
|
|
342
|
+
updateStatus,
|
|
343
|
+
unbindDeferredDispatchContext: deferredQueueDispatchRuntime.unbind,
|
|
344
|
+
clearPendingMediaGroups: mediaGroupRuntime.clear,
|
|
345
|
+
clearModelMenuState: modelMenuRuntime.clear,
|
|
346
|
+
getActiveTurnChatId: activeTurnRuntime.getChatId,
|
|
347
|
+
clearPreview: previewRuntime.clear,
|
|
348
|
+
clearActiveTurn: activeTurnRuntime.clear,
|
|
349
|
+
clearAbort: abort.clearHandler,
|
|
350
|
+
stopPolling: lockedPollingRuntime.suspend,
|
|
351
|
+
recordRuntimeEvent,
|
|
272
352
|
});
|
|
273
353
|
const sessionLifecycleRuntime = Lifecycle.appendTelegramLifecycleHooks(
|
|
274
|
-
|
|
275
|
-
Pi.ExtensionContext,
|
|
276
|
-
RuntimeTelegramQueueItem,
|
|
277
|
-
ActivePiModel
|
|
278
|
-
>({
|
|
279
|
-
getCurrentModel: Pi.getExtensionContextModel,
|
|
280
|
-
loadConfig: configStore.load,
|
|
281
|
-
setQueuedItems: telegramQueueStore.setQueuedItems,
|
|
282
|
-
setCurrentModel: currentModelRuntime.set,
|
|
283
|
-
setPendingModelSwitch: pendingModelSwitchStore.set,
|
|
284
|
-
syncCounters: bridgeRuntime.queue.syncCounters,
|
|
285
|
-
syncFlags: bridgeRuntime.lifecycle.syncFlags,
|
|
286
|
-
bindDeferredDispatchContext: deferredQueueDispatchRuntime.bind,
|
|
287
|
-
prepareTempDir,
|
|
288
|
-
updateStatus,
|
|
289
|
-
unbindDeferredDispatchContext: deferredQueueDispatchRuntime.unbind,
|
|
290
|
-
clearPendingMediaGroups: mediaGroupRuntime.clear,
|
|
291
|
-
clearModelMenuState: modelMenuRuntime.clear,
|
|
292
|
-
getActiveTurnChatId: activeTurnRuntime.getChatId,
|
|
293
|
-
clearPreview: previewRuntime.clear,
|
|
294
|
-
clearActiveTurn: activeTurnRuntime.clear,
|
|
295
|
-
clearAbort: bridgeRuntime.abort.clearHandler,
|
|
296
|
-
stopPolling: lockedPollingRuntime.suspend,
|
|
297
|
-
recordRuntimeEvent: runtimeEvents.record,
|
|
298
|
-
}),
|
|
354
|
+
queueSessionLifecycle,
|
|
299
355
|
{ onSessionStart: lockedPollingRuntime.onSessionStart },
|
|
300
356
|
);
|
|
301
357
|
|
|
@@ -303,19 +359,19 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
303
359
|
|
|
304
360
|
Attachments.registerTelegramAttachmentTool(pi, {
|
|
305
361
|
getActiveTurn: activeTurnRuntime.get,
|
|
306
|
-
recordRuntimeEvent
|
|
362
|
+
recordRuntimeEvent,
|
|
307
363
|
});
|
|
308
364
|
|
|
309
365
|
Commands.registerTelegramBridgeCommands(pi, {
|
|
310
366
|
promptForConfig: Setup.createTelegramSetupPromptRuntime({
|
|
311
367
|
getConfig: configStore.get,
|
|
312
368
|
setConfig: configStore.set,
|
|
313
|
-
setupGuard:
|
|
369
|
+
setupGuard: setup,
|
|
314
370
|
getMe: Api.fetchTelegramBotIdentity,
|
|
315
371
|
persistConfig: configStore.persist,
|
|
316
372
|
startPolling: lockedPollingRuntime.start,
|
|
317
373
|
updateStatus,
|
|
318
|
-
recordRuntimeEvent
|
|
374
|
+
recordRuntimeEvent,
|
|
319
375
|
}),
|
|
320
376
|
getStatusLines,
|
|
321
377
|
reloadConfig: configStore.load,
|
|
@@ -327,68 +383,76 @@ export default function (pi: Pi.ExtensionAPI) {
|
|
|
327
383
|
|
|
328
384
|
// --- Lifecycle Hooks ---
|
|
329
385
|
|
|
386
|
+
const agentEndResetter = Runtime.createTelegramAgentEndResetter({
|
|
387
|
+
abort,
|
|
388
|
+
typing,
|
|
389
|
+
clearActiveTurn: activeTurnRuntime.clear,
|
|
390
|
+
resetToolExecutions: lifecycle.resetActiveToolExecutions,
|
|
391
|
+
clearPendingModelSwitch: modelSwitchController.clearPendingSwitch,
|
|
392
|
+
clearDispatchPending: lifecycle.clearDispatchPending,
|
|
393
|
+
});
|
|
394
|
+
const queuedAttachmentSender =
|
|
395
|
+
Attachments.createTelegramQueuedAttachmentSender({
|
|
396
|
+
sendMultipart: callMultipart,
|
|
397
|
+
sendTextReply,
|
|
398
|
+
recordRuntimeEvent,
|
|
399
|
+
});
|
|
400
|
+
const outboundReplyPlanner =
|
|
401
|
+
OutboundHandlers.createTelegramOutboundReplyPlanner(buttonActionStore);
|
|
402
|
+
const outboundReplyArtifactSender =
|
|
403
|
+
OutboundHandlers.createTelegramOutboundReplyArtifactSender({
|
|
404
|
+
execCommand: CommandTemplates.execCommandTemplate,
|
|
405
|
+
sendMultipart: callMultipart,
|
|
406
|
+
sendTextReply,
|
|
407
|
+
getHandlers: configStore.getOutboundHandlers,
|
|
408
|
+
recordRuntimeEvent,
|
|
409
|
+
});
|
|
410
|
+
const agentLifecycleHooks = Queue.createTelegramAgentLifecycleHooks<
|
|
411
|
+
Queue.PendingTelegramTurn,
|
|
412
|
+
Pi.ExtensionContext,
|
|
413
|
+
unknown,
|
|
414
|
+
Keyboard.TelegramInlineKeyboardMarkup
|
|
415
|
+
>({
|
|
416
|
+
setAbortHandler: Runtime.createTelegramContextAbortHandlerSetter(abort),
|
|
417
|
+
getQueuedItems: telegramQueueStore.getQueuedItems,
|
|
418
|
+
hasPendingDispatch: lifecycle.hasDispatchPending,
|
|
419
|
+
hasActiveTurn: activeTurnRuntime.has,
|
|
420
|
+
resetToolExecutions: lifecycle.resetActiveToolExecutions,
|
|
421
|
+
resetPendingModelSwitch: modelSwitchController.clearPendingSwitch,
|
|
422
|
+
setQueuedItems: telegramQueueStore.setQueuedItems,
|
|
423
|
+
clearDispatchPending: lifecycle.clearDispatchPending,
|
|
424
|
+
setActiveTurn: activeTurnRuntime.set,
|
|
425
|
+
createPreviewState: previewRuntime.resetState,
|
|
426
|
+
startTypingLoop: promptDispatchRuntime.startTypingLoop,
|
|
427
|
+
updateStatus,
|
|
428
|
+
getActiveTurn: activeTurnRuntime.get,
|
|
429
|
+
extractAssistant: Replies.extractLatestAssistantMessageText,
|
|
430
|
+
getPreserveQueuedTurnsAsHistory: lifecycle.shouldPreserveQueuedTurnsAsHistory,
|
|
431
|
+
resetRuntimeState: agentEndResetter,
|
|
432
|
+
dispatchNextQueuedTelegramTurn,
|
|
433
|
+
requestDeferredDispatchNextQueuedTelegramTurn:
|
|
434
|
+
deferredQueueDispatchRuntime.request,
|
|
435
|
+
clearPreview: previewRuntime.clear,
|
|
436
|
+
setPreviewPendingText: previewRuntime.setPendingText,
|
|
437
|
+
finalizeMarkdownPreview: previewRuntime.finalizeMarkdown,
|
|
438
|
+
sendMarkdownReply,
|
|
439
|
+
sendTextReply,
|
|
440
|
+
sendQueuedAttachments: queuedAttachmentSender,
|
|
441
|
+
planOutboundReply: outboundReplyPlanner,
|
|
442
|
+
sendOutboundReplyArtifacts: outboundReplyArtifactSender,
|
|
443
|
+
getActiveToolExecutions: lifecycle.getActiveToolExecutions,
|
|
444
|
+
setActiveToolExecutions: lifecycle.setActiveToolExecutions,
|
|
445
|
+
triggerPendingModelSwitchAbort: modelSwitchController.triggerPendingAbort,
|
|
446
|
+
});
|
|
447
|
+
// Wire transport-level reply dedup reset via lifecycle
|
|
448
|
+
Lifecycle.setResetTransportReplyDedup(Replies.resetTransportReplyDedup);
|
|
449
|
+
const agentStartWithDedupReset = Lifecycle.createAgentStartDedupHook(agentLifecycleHooks.onAgentStart);
|
|
330
450
|
Lifecycle.registerTelegramLifecycleHooks(pi, {
|
|
331
451
|
...sessionLifecycleRuntime,
|
|
452
|
+
...agentLifecycleHooks,
|
|
453
|
+
onAgentStart: agentStartWithDedupReset,
|
|
332
454
|
onBeforeAgentStart: Prompts.createTelegramBeforeAgentStartHook(),
|
|
333
455
|
onModelSelect: currentModelRuntime.onModelSelect,
|
|
334
|
-
...Queue.createTelegramAgentLifecycleHooks<
|
|
335
|
-
Queue.PendingTelegramTurn,
|
|
336
|
-
Pi.ExtensionContext,
|
|
337
|
-
unknown
|
|
338
|
-
>({
|
|
339
|
-
setAbortHandler: Runtime.createTelegramContextAbortHandlerSetter(
|
|
340
|
-
bridgeRuntime.abort,
|
|
341
|
-
),
|
|
342
|
-
getQueuedItems: telegramQueueStore.getQueuedItems,
|
|
343
|
-
hasPendingDispatch: bridgeRuntime.lifecycle.hasDispatchPending,
|
|
344
|
-
hasActiveTurn: activeTurnRuntime.has,
|
|
345
|
-
resetToolExecutions: bridgeRuntime.lifecycle.resetActiveToolExecutions,
|
|
346
|
-
resetPendingModelSwitch: modelSwitchController.clearPendingSwitch,
|
|
347
|
-
setQueuedItems: telegramQueueStore.setQueuedItems,
|
|
348
|
-
clearDispatchPending: bridgeRuntime.lifecycle.clearDispatchPending,
|
|
349
|
-
setActiveTurn: activeTurnRuntime.set,
|
|
350
|
-
createPreviewState: previewRuntime.resetState,
|
|
351
|
-
startTypingLoop: promptDispatchRuntime.startTypingLoop,
|
|
352
|
-
updateStatus,
|
|
353
|
-
getActiveTurn: activeTurnRuntime.get,
|
|
354
|
-
extractAssistant: Replies.extractLatestAssistantMessageText,
|
|
355
|
-
getPreserveQueuedTurnsAsHistory:
|
|
356
|
-
bridgeRuntime.lifecycle.shouldPreserveQueuedTurnsAsHistory,
|
|
357
|
-
resetRuntimeState: Runtime.createTelegramAgentEndResetter({
|
|
358
|
-
abort: bridgeRuntime.abort,
|
|
359
|
-
typing: bridgeRuntime.typing,
|
|
360
|
-
clearActiveTurn: activeTurnRuntime.clear,
|
|
361
|
-
resetToolExecutions: bridgeRuntime.lifecycle.resetActiveToolExecutions,
|
|
362
|
-
clearPendingModelSwitch: modelSwitchController.clearPendingSwitch,
|
|
363
|
-
clearDispatchPending: bridgeRuntime.lifecycle.clearDispatchPending,
|
|
364
|
-
}),
|
|
365
|
-
dispatchNextQueuedTelegramTurn,
|
|
366
|
-
requestDeferredDispatchNextQueuedTelegramTurn:
|
|
367
|
-
deferredQueueDispatchRuntime.request,
|
|
368
|
-
clearPreview: previewRuntime.clear,
|
|
369
|
-
setPreviewPendingText: previewRuntime.setPendingText,
|
|
370
|
-
finalizeMarkdownPreview: previewRuntime.finalizeMarkdown,
|
|
371
|
-
sendMarkdownReply,
|
|
372
|
-
sendTextReply,
|
|
373
|
-
sendQueuedAttachments: Attachments.createTelegramQueuedAttachmentSender({
|
|
374
|
-
sendMultipart: callMultipart,
|
|
375
|
-
sendTextReply,
|
|
376
|
-
recordRuntimeEvent: runtimeEvents.record,
|
|
377
|
-
}),
|
|
378
|
-
planOutboundReply:
|
|
379
|
-
OutboundHandlers.createTelegramOutboundReplyPlanner(buttonActionStore),
|
|
380
|
-
sendOutboundReplyArtifacts:
|
|
381
|
-
OutboundHandlers.createTelegramOutboundReplyArtifactSender({
|
|
382
|
-
execCommand: CommandTemplates.execCommandTemplate,
|
|
383
|
-
sendMultipart: callMultipart,
|
|
384
|
-
sendTextReply,
|
|
385
|
-
getHandlers: configStore.getOutboundHandlers,
|
|
386
|
-
recordRuntimeEvent: runtimeEvents.record,
|
|
387
|
-
}),
|
|
388
|
-
getActiveToolExecutions: bridgeRuntime.lifecycle.getActiveToolExecutions,
|
|
389
|
-
setActiveToolExecutions: bridgeRuntime.lifecycle.setActiveToolExecutions,
|
|
390
|
-
triggerPendingModelSwitchAbort: modelSwitchController.triggerPendingAbort,
|
|
391
|
-
}),
|
|
392
456
|
onMessageStart: previewRuntime.onMessageStart,
|
|
393
457
|
onMessageUpdate: previewRuntime.onMessageUpdate,
|
|
394
458
|
});
|
package/lib/api.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Telegram inbound attachment handler pipeline
|
|
3
|
+
* Zones: telegram inbound, command templates, prompt preparation
|
|
3
4
|
* Owns MIME/type matching, command-template execution, fallback handling, and prompt injection before prompt enqueueing
|
|
4
5
|
*/
|
|
5
6
|
|
|
@@ -299,6 +300,7 @@ async function executeTelegramAttachmentHandlerInvocation(
|
|
|
299
300
|
const result = await deps.execCommand(invocation.command, invocation.args, {
|
|
300
301
|
cwd,
|
|
301
302
|
timeout,
|
|
303
|
+
...(typeof handler === "object" && handler.retry !== undefined ? { retry: handler.retry } : {}),
|
|
302
304
|
...(stdin !== undefined ? { stdin } : {}),
|
|
303
305
|
});
|
|
304
306
|
if (result.code !== 0)
|
|
@@ -342,15 +344,20 @@ async function executeTelegramAttachmentHandler(
|
|
|
342
344
|
const startedAt = Date.now();
|
|
343
345
|
let output = "";
|
|
344
346
|
for (const [index, step] of steps.entries()) {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
347
|
+
try {
|
|
348
|
+
output = await executeTelegramAttachmentHandlerInvocation(
|
|
349
|
+
step,
|
|
350
|
+
file,
|
|
351
|
+
cwd,
|
|
352
|
+
deps,
|
|
353
|
+
false,
|
|
354
|
+
getTelegramAttachmentCompositionStepTimeout(handler, step, startedAt),
|
|
355
|
+
index === 0 ? undefined : output,
|
|
356
|
+
);
|
|
357
|
+
} catch (error) {
|
|
358
|
+
if (typeof step === "object" && step.critical) throw error;
|
|
359
|
+
output = "";
|
|
360
|
+
}
|
|
354
361
|
}
|
|
355
362
|
return output.trim();
|
|
356
363
|
}
|
package/lib/attachments.ts
CHANGED
package/lib/command-templates.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Command-template standard helpers
|
|
3
|
+
* Zones: shared utils, local process execution, automation standard
|
|
3
4
|
* Owns shell-free command-template splitting, placeholder defaults, composition expansion, executable path expansion, and direct execution
|
|
4
5
|
*/
|
|
5
6
|
|
|
@@ -7,12 +8,16 @@ import { spawn } from "node:child_process";
|
|
|
7
8
|
import { homedir } from "node:os";
|
|
8
9
|
import { isAbsolute, resolve } from "node:path";
|
|
9
10
|
|
|
11
|
+
export const DEFAULT_COMMAND_TIMEOUT_MS = 30_000;
|
|
12
|
+
|
|
10
13
|
export interface CommandTemplateObjectConfig {
|
|
11
14
|
template?: CommandTemplateValue;
|
|
12
15
|
args?: string[];
|
|
13
16
|
defaults?: Record<string, unknown>;
|
|
14
17
|
timeout?: number;
|
|
15
18
|
output?: string;
|
|
19
|
+
retry?: number;
|
|
20
|
+
critical?: boolean;
|
|
16
21
|
}
|
|
17
22
|
|
|
18
23
|
export type CommandTemplateValue = string | CommandTemplateConfig[];
|
|
@@ -34,6 +39,7 @@ export interface CommandTemplateExecOptions {
|
|
|
34
39
|
signal?: AbortSignal;
|
|
35
40
|
stdin?: string;
|
|
36
41
|
killGrace?: number;
|
|
42
|
+
retry?: number;
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
export interface CommandTemplateExecResult {
|
|
@@ -100,7 +106,13 @@ export function expandCommandTemplateConfigs(
|
|
|
100
106
|
}
|
|
101
107
|
if (typeof normalizedConfig.template !== "string") return [];
|
|
102
108
|
return [
|
|
103
|
-
{
|
|
109
|
+
{
|
|
110
|
+
...normalizedConfig,
|
|
111
|
+
...context,
|
|
112
|
+
template: normalizedConfig.template,
|
|
113
|
+
retry: normalizedConfig.retry,
|
|
114
|
+
critical: normalizedConfig.critical,
|
|
115
|
+
},
|
|
104
116
|
];
|
|
105
117
|
}
|
|
106
118
|
|
|
@@ -192,7 +204,22 @@ export function substituteCommandTemplateToken(
|
|
|
192
204
|
);
|
|
193
205
|
}
|
|
194
206
|
|
|
195
|
-
export function execCommandTemplate(
|
|
207
|
+
export async function execCommandTemplate(
|
|
208
|
+
command: string,
|
|
209
|
+
args: string[],
|
|
210
|
+
options: CommandTemplateExecOptions = {},
|
|
211
|
+
): Promise<CommandTemplateExecResult> {
|
|
212
|
+
const maxAttempts = options.retry ?? 1;
|
|
213
|
+
let lastResult: CommandTemplateExecResult = { stdout: "", stderr: "", code: 1, killed: false };
|
|
214
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
215
|
+
const result = await execCommandTemplateOnce(command, args, options);
|
|
216
|
+
if (result.code === 0) return result;
|
|
217
|
+
lastResult = result;
|
|
218
|
+
}
|
|
219
|
+
return lastResult;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function execCommandTemplateOnce(
|
|
196
223
|
command: string,
|
|
197
224
|
args: string[],
|
|
198
225
|
options: CommandTemplateExecOptions = {},
|
|
@@ -231,8 +258,10 @@ export function execCommandTemplate(
|
|
|
231
258
|
else
|
|
232
259
|
options.signal.addEventListener("abort", killProcess, { once: true });
|
|
233
260
|
}
|
|
234
|
-
if (options.timeout && options.timeout > 0)
|
|
261
|
+
if (options.timeout !== undefined && options.timeout > 0)
|
|
235
262
|
timeoutId = setTimeout(killProcess, options.timeout);
|
|
263
|
+
else if (options.timeout === undefined)
|
|
264
|
+
timeoutId = setTimeout(killProcess, DEFAULT_COMMAND_TIMEOUT_MS);
|
|
236
265
|
proc.stdout?.on("data", (data) => {
|
|
237
266
|
stdout += data.toString();
|
|
238
267
|
});
|