pi-harness-runtime 1.1.427 → 1.1.429

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.
Files changed (156) hide show
  1. package/AGENTS.md +2 -2
  2. package/RULES.md +2 -3
  3. package/package.json +1 -1
  4. package/packages/a2a-adapter/package.json +1 -1
  5. package/packages/agent-policy/package.json +1 -1
  6. package/packages/architecture-generator/package.json +1 -1
  7. package/packages/auth/package.json +1 -1
  8. package/packages/autonomous-refactor/package.json +1 -1
  9. package/packages/autonomous-runtime/package.json +1 -1
  10. package/packages/cache-strategy/package.json +1 -1
  11. package/packages/capability-registry/package.json +1 -1
  12. package/packages/checkpoint/package.json +1 -1
  13. package/packages/cli-plugin-sdk/package.json +1 -1
  14. package/packages/clipboard/package.json +1 -1
  15. package/packages/clipboard-plugin/dist/index.js +1 -1
  16. package/packages/clipboard-plugin/dist/index.js.map +1 -1
  17. package/packages/clipboard-plugin/package.json +1 -2
  18. package/packages/clipboard-plugin/src/index.ts +1 -1
  19. package/packages/code-generation/package.json +1 -1
  20. package/packages/code-review/package.json +1 -1
  21. package/packages/codex-adapter/package.json +1 -1
  22. package/packages/codex-watcher/package.json +1 -1
  23. package/packages/config-capture/package.json +1 -1
  24. package/packages/connection-runtime/package.json +1 -1
  25. package/packages/context-compiler/package.json +1 -1
  26. package/packages/context-discovery/package.json +1 -1
  27. package/packages/context-manager/package.json +1 -1
  28. package/packages/contracts/package.json +1 -1
  29. package/packages/cookie-sanitizer/package.json +1 -1
  30. package/packages/cost-optimizer/package.json +1 -1
  31. package/packages/dependency-analyzer/package.json +1 -1
  32. package/packages/django-plugin/package.json +1 -1
  33. package/packages/doc-generator/package.json +1 -1
  34. package/packages/durable-review/package.json +1 -1
  35. package/packages/e2e-tester/package.json +1 -1
  36. package/packages/evaluation-engine/package.json +1 -1
  37. package/packages/evaluation-runner/package.json +1 -1
  38. package/packages/event-bus/package.json +1 -1
  39. package/packages/event-sink/package.json +1 -1
  40. package/packages/event-store/package.json +1 -1
  41. package/packages/experience-replay/package.json +1 -1
  42. package/packages/feedback-collector/package.json +1 -1
  43. package/packages/file-copy-helper/package.json +1 -1
  44. package/packages/framework-detector/package.json +1 -1
  45. package/packages/framework-plugin-sdk/package.json +1 -1
  46. package/packages/frappe-plugin/package.json +1 -1
  47. package/packages/generic-web-plugin/package.json +1 -1
  48. package/packages/health-monitor/package.json +1 -1
  49. package/packages/intent-analyzer/package.json +1 -1
  50. package/packages/jev-judge/package.json +1 -1
  51. package/packages/knowledge-graph/package.json +1 -1
  52. package/packages/knowledge-retrieval/package.json +1 -1
  53. package/packages/laravel-plugin/package.json +1 -1
  54. package/packages/learning-engine/package.json +1 -1
  55. package/packages/logging/package.json +1 -1
  56. package/packages/mcp-adapter/package.json +1 -1
  57. package/packages/memory-engine/package.json +1 -1
  58. package/packages/milestone-manager/package.json +1 -1
  59. package/packages/model-registry/package.json +1 -1
  60. package/packages/nextjs-plugin/package.json +1 -1
  61. package/packages/notification/package.json +1 -1
  62. package/packages/observability/package.json +1 -1
  63. package/packages/okf-indexer/package.json +1 -1
  64. package/packages/paperclip-integration/package.json +1 -1
  65. package/packages/performance-optimizer/package.json +1 -1
  66. package/packages/permission-mode/package.json +1 -1
  67. package/packages/privilege-broker/package.json +1 -1
  68. package/packages/project-analyzer/package.json +1 -1
  69. package/packages/project-bootstrap/package.json +1 -1
  70. package/packages/projection-engine/package.json +1 -1
  71. package/packages/prompt-compiler/package.json +1 -1
  72. package/packages/prompt-versioning/package.json +1 -1
  73. package/packages/provider-adapter-sdk/package.json +1 -1
  74. package/packages/provider-router/package.json +1 -1
  75. package/packages/provider-selector/package.json +1 -1
  76. package/packages/providers/package.json +1 -1
  77. package/packages/qdrant-skills/package.json +1 -1
  78. package/packages/quota-manager/package.json +1 -1
  79. package/packages/rate-limiter/package.json +1 -1
  80. package/packages/react-vite-plugin/package.json +1 -1
  81. package/packages/relay/package.json +1 -1
  82. package/packages/release-manager/package.json +1 -1
  83. package/packages/requirement-compiler/package.json +1 -1
  84. package/packages/runtime/package.json +1 -1
  85. package/packages/scheduler/package.json +1 -1
  86. package/packages/scheduler-adapter/package.json +1 -1
  87. package/packages/session/package.json +1 -1
  88. package/packages/session-api/package.json +1 -1
  89. package/packages/session-export/package.json +1 -1
  90. package/packages/shared-context/package.json +1 -1
  91. package/packages/skill-mcp-client/package.json +1 -1
  92. package/packages/skill-registry/package.json +1 -1
  93. package/packages/skills/package.json +1 -1
  94. package/packages/sprint-planner/package.json +1 -1
  95. package/packages/subscription-engine/package.json +1 -1
  96. package/packages/task-compiler/package.json +1 -1
  97. package/packages/tencentdb-memory/package.json +1 -1
  98. package/packages/tencentdb-sync/package.json +1 -1
  99. package/packages/test-data-generator/package.json +1 -1
  100. package/packages/test-generator/package.json +1 -1
  101. package/packages/todo-bd-sync/package.json +1 -1
  102. package/packages/token-estimation/package.json +1 -1
  103. package/packages/token-optimizer/package.json +1 -1
  104. package/packages/tooling-preference/package.json +1 -1
  105. package/packages/trajectory/package.json +1 -1
  106. package/packages/tui/package.json +1 -1
  107. package/packages/two-phase-review/package.json +1 -1
  108. package/packages/types/package.json +1 -1
  109. package/packages/voice-api/package.json +1 -1
  110. package/packages/webmcp-tester/package.json +1 -1
  111. package/packages/wiki/package.json +1 -1
  112. package/packages/workflow-events/package.json +1 -1
  113. package/packages/workspace-scanner/package.json +1 -1
  114. package/packages/worktree/package.json +1 -1
  115. package/packages/write-review/package.json +1 -1
  116. package/scripts/lib/runtime-imports.ts +263 -0
  117. package/scripts/verify-package-contents.ts +112 -18
  118. package/packages/telegram/package.json +0 -22
  119. package/packages/telegram/src/agent-messages.d.ts +0 -28
  120. package/packages/telegram/src/agent-messages.js +0 -90
  121. package/packages/telegram/src/bindings.d.ts +0 -296
  122. package/packages/telegram/src/bindings.js +0 -1086
  123. package/packages/telegram/src/bus.d.ts +0 -759
  124. package/packages/telegram/src/bus.js +0 -2732
  125. package/packages/telegram/src/commands.d.ts +0 -761
  126. package/packages/telegram/src/commands.js +0 -2622
  127. package/packages/telegram/src/config.d.ts +0 -222
  128. package/packages/telegram/src/config.js +0 -844
  129. package/packages/telegram/src/delivery.d.ts +0 -161
  130. package/packages/telegram/src/delivery.js +0 -550
  131. package/packages/telegram/src/extension.d.ts +0 -7
  132. package/packages/telegram/src/extension.js +0 -2444
  133. package/packages/telegram/src/inbound.d.ts +0 -94
  134. package/packages/telegram/src/inbound.js +0 -478
  135. package/packages/telegram/src/index.d.ts +0 -6
  136. package/packages/telegram/src/index.js +0 -6
  137. package/packages/telegram/src/keyboard.d.ts +0 -21
  138. package/packages/telegram/src/keyboard.js +0 -37
  139. package/packages/telegram/src/lifecycle.d.ts +0 -210
  140. package/packages/telegram/src/lifecycle.js +0 -538
  141. package/packages/telegram/src/pi.d.ts +0 -76
  142. package/packages/telegram/src/pi.js +0 -130
  143. package/packages/telegram/src/polling.d.ts +0 -332
  144. package/packages/telegram/src/polling.js +0 -1219
  145. package/packages/telegram/src/replies.d.ts +0 -210
  146. package/packages/telegram/src/replies.js +0 -752
  147. package/packages/telegram/src/routing.d.ts +0 -388
  148. package/packages/telegram/src/routing.js +0 -7886
  149. package/packages/telegram/src/runtime.d.ts +0 -159
  150. package/packages/telegram/src/runtime.js +0 -351
  151. package/packages/telegram/src/telegram-api.d.ts +0 -564
  152. package/packages/telegram/src/telegram-api.js +0 -1332
  153. package/packages/telegram/src/turns.d.ts +0 -108
  154. package/packages/telegram/src/turns.js +0 -482
  155. package/packages/telegram/src/updates.d.ts +0 -1545
  156. package/packages/telegram/src/updates.js +0 -6420
@@ -1,1086 +0,0 @@
1
- /**
2
- * Telegram bridge binding composition
3
- * Zones: telegram, pi agent, orchestration
4
- * Owns pi-facing tool, command, and lifecycle hook registration for the entrypoint
5
- */
6
- import * as ActivityVerbosity from "./activity-verbosity.js";
7
- import * as Activity from "./activity.js";
8
- import * as BusApi from "./bus-api.js";
9
- import * as ChannelPosts from "./channel-posts.js";
10
- import * as CommandTemplates from "./command-templates.js";
11
- import * as Commands from "./commands.js";
12
- import * as Config from "./config.js";
13
- import * as Delivery from "./delivery.js";
14
- import * as GenerativeApps from "./generative-apps.js";
15
- import * as Lifecycle from "./lifecycle.js";
16
- import * as Menu from "./menu.js";
17
- import * as OutboundAttachments from "./outbound-attachments.js";
18
- import * as OutboundHandlers from "./outbound.js";
19
- import * as Pi from "./pi.js";
20
- import * as Prompts from "./prompts.js";
21
- import * as Queue from "./queue.js";
22
- import * as Replies from "./replies.js";
23
- import * as Routing from "./routing.js";
24
- import * as Runtime from "./runtime.js";
25
- import * as Setup from "./setup.js";
26
- import * as TelegramApi from "./telegram-api.js";
27
- /** Compose any registered held command over captured recipient effects; no ordinary follower API fallback. */
28
- export function createTelegramFollowerSelectedCommandBinding(deps) {
29
- const { ctx, operationId, registrationGeneration, name, recipient, command, deliver, recordOwnership, } = deps, target = { ...deps.target };
30
- const prepare = command.prepareHeldCommand, canPrepare = command.canPrepareHeldCommand;
31
- const isCurrent = recipient.isCurrent, assertRecipientCurrent = recipient.assertRecipientCurrent, menu = deps.menu, menuPorts = menu && { ...menu };
32
- if ([
33
- prepare,
34
- canPrepare,
35
- isCurrent,
36
- assertRecipientCurrent,
37
- deliver,
38
- recordOwnership,
39
- ].some((port) => typeof port !== "function") ||
40
- (menuPorts &&
41
- [
42
- menuPorts.getModelMenuState,
43
- menuPorts.storeModelMenuState,
44
- menuPorts.getActiveModel,
45
- menuPorts.getThinkingLevel,
46
- menuPorts.buildStatusHtml,
47
- menuPorts.isIdle,
48
- menuPorts.canOfferInFlightModelSwitch,
49
- ].some((port) => typeof port !== "function")))
50
- return undefined;
51
- const portsCurrent = () => deps.ctx === ctx &&
52
- deps.operationId === operationId &&
53
- deps.registrationGeneration === registrationGeneration &&
54
- deps.name === name &&
55
- deps.recipient === recipient &&
56
- recipient.isCurrent === isCurrent &&
57
- recipient.assertRecipientCurrent === assertRecipientCurrent &&
58
- deps.command === command &&
59
- command.prepareHeldCommand === prepare &&
60
- command.canPrepareHeldCommand === canPrepare &&
61
- deps.deliver === deliver &&
62
- deps.recordOwnership === recordOwnership &&
63
- deps.menu === menu &&
64
- (!menuPorts ||
65
- Object.entries(menuPorts).every(([key, value]) => Reflect.get(menu, key) === value));
66
- const current = () => portsCurrent() && isCurrent.call(recipient) && portsCurrent();
67
- if (!current())
68
- return undefined;
69
- const api = BusApi.createTelegramSelectedMenuTextApi({
70
- operationId,
71
- registrationGeneration,
72
- target,
73
- assertAuthority: assertRecipientCurrent,
74
- deliver,
75
- });
76
- const rendered = Replies.createTelegramRenderedMessageDeliveryRuntime({
77
- sendMessage: api.sendMessage,
78
- editMessage: api.editMessageText,
79
- recordOwnership,
80
- async sendRichMessage() {
81
- throw new Error("Selected command has no rich or ordinary API fallback.");
82
- },
83
- });
84
- const menus = menuPorts &&
85
- Menu.createTelegramMenuActionRuntime({
86
- ...menuPorts,
87
- sendTextReply: rendered.sendTextReply,
88
- sendInteractiveMessage: rendered.sendInteractiveMessage,
89
- editInteractiveMessage: rendered.editInteractiveMessage,
90
- });
91
- const effects = {
92
- sendTextReply: rendered.sendTextReply,
93
- ...(menus ? { showStatus: menus.sendStatusMessage } : {}),
94
- };
95
- if (!canPrepare.call(command, name, effects) || !current())
96
- return undefined;
97
- return {
98
- isCurrent: current,
99
- assertRecipientCurrent,
100
- prepare(readiness, context, input) {
101
- if (!current() ||
102
- context !== ctx ||
103
- input.target.chatId !== target.chatId ||
104
- input.target.threadId !== target.threadId ||
105
- input.assertRecipientCurrent !== assertRecipientCurrent)
106
- return undefined;
107
- const plan = prepare.call(command, name, readiness, context, input, effects);
108
- return current() ? plan : undefined;
109
- },
110
- };
111
- }
112
- export function createTelegramQueueBindingRuntime(deps) {
113
- const settleDiscardedItems = (items, ctx) => {
114
- const durableItems = items.filter((item) => (item.admissionReceipts?.length ?? 0) > 0);
115
- if (durableItems.length === 0)
116
- return true;
117
- const settlement = deps.admission.getSettlement();
118
- if (!settlement)
119
- return false;
120
- return settlement.onItemsDiscarded(durableItems, ctx) === true;
121
- };
122
- const mutation = Queue.createTelegramQueueMutationController({
123
- ...deps.store,
124
- hasPendingDispatch: deps.lifecycle.hasDispatchPending,
125
- allocateLaneOrder: deps.queue.allocateItemOrder,
126
- onItemsDiscarded(items, ctx) {
127
- if (!settleDiscardedItems(items, ctx)) {
128
- throw new Error("Telegram queue items could not be discarded durably.");
129
- }
130
- },
131
- updateStatus: deps.updateStatus,
132
- recordRuntimeEvent: deps.recordRuntimeEvent,
133
- });
134
- const dispatch = Queue.createTelegramQueueDispatchRuntime({
135
- ...deps.store,
136
- isCompactionInProgress: deps.lifecycle.isCompactionInProgress,
137
- hasActiveTurn: deps.activeTurn.has,
138
- hasDispatchPending: deps.lifecycle.hasDispatchPending,
139
- isIdle: deps.isIdle,
140
- hasPendingMessages: deps.hasPendingMessages,
141
- hasDispatchContext: deps.deferredDispatch.isBound,
142
- getDispatchGeneration: deps.deferredDispatch.getGeneration,
143
- isDispatchGenerationActive: deps.deferredDispatch.isGenerationActive,
144
- isQueueItemTransportActive(item) {
145
- return deps.transportStamp.isActive(item.transportStamp);
146
- },
147
- hasPendingInboundQueueMutationForItem(item) {
148
- return deps.admission.hasPendingQueueMutationForItem(item);
149
- },
150
- isQueueItemAdmissionReady(item) {
151
- return (deps.admission.getSettlement()?.isItemReady(item) ??
152
- (item.admissionReceipts?.length ?? 0) === 0);
153
- },
154
- commitPromptDispatch(item, ctx) {
155
- if ((item.admissionReceipts?.length ?? 0) === 0)
156
- return true;
157
- const settlement = deps.admission.getSettlement();
158
- if (!settlement?.onPromptHandedOff)
159
- return false;
160
- return settlement.onPromptHandedOff(item, ctx) === true;
161
- },
162
- onControlSettled(item, ctx) {
163
- deps.admission.getSettlement()?.onControlSettled(item, ctx);
164
- },
165
- onPromptSkipped(item, ctx) {
166
- return settleDiscardedItems([item], ctx);
167
- },
168
- updateStatus: deps.updateStatus,
169
- sendTextReply: deps.sendTextReply,
170
- reconcileNextDispatchAnnouncementReplyOwnership: deps.reconcileNextDispatchAnnouncementReplyOwnership,
171
- recordRuntimeEvent: deps.recordRuntimeEvent,
172
- ...deps.promptDispatch,
173
- sendUserMessage: deps.sendUserMessage,
174
- });
175
- return {
176
- mutation,
177
- dispatchNext: dispatch.dispatchNext,
178
- requestNextDispatchAnnouncement: dispatch.requestNextDispatchAnnouncement,
179
- cancelNextDispatchAnnouncement: dispatch.cancelNextDispatchAnnouncement,
180
- watchdog: Queue.createTelegramQueueDispatchWatchdogRuntime({
181
- hasQueuedItems: deps.store.hasQueuedItems,
182
- dispatchNextQueuedTelegramTurn: dispatch.dispatchNext,
183
- recordRuntimeEvent: deps.recordRuntimeEvent,
184
- }),
185
- };
186
- }
187
- export function createTelegramGenerativeAppLiveSurfaceBinding() {
188
- let current;
189
- return {
190
- get() {
191
- return current;
192
- },
193
- set(runtime) {
194
- current?.shutdown();
195
- current = runtime;
196
- },
197
- shutdown() {
198
- current?.shutdown();
199
- current = undefined;
200
- },
201
- };
202
- }
203
- export function createTelegramGenerativeAppBoundButtonActionInvoker(deps) {
204
- return async (action, query) => {
205
- let boundAction;
206
- let handedOff;
207
- try {
208
- boundAction = GenerativeApps.parseGenerativeAppBoundAction(action.prompt);
209
- if (!boundAction)
210
- return false;
211
- deps.assertExecutionCurrent(query);
212
- const chatId = query.message?.chat?.id;
213
- const messageId = query.message?.message_id;
214
- if (typeof chatId !== "number" || typeof messageId !== "number") {
215
- throw new Error("Generative App callback target is unavailable.");
216
- }
217
- const liveSurfaces = deps.getLiveSurfaceRuntime?.();
218
- handedOff = liveSurfaces?.take(GenerativeApps.getTelegramBindLiveSurfaceKey(boundAction.app, deps.getActiveProfileName?.() ?? "default", {
219
- chatId,
220
- ...(query.message?.message_thread_id !== undefined
221
- ? { threadId: query.message.message_thread_id }
222
- : {}),
223
- }));
224
- const result = await GenerativeApps.invokeGenerativeApp({
225
- agentDir: deps.agentDir,
226
- ...(deps.getExecutionFence(query)
227
- ? { execution: deps.getExecutionFence(query) }
228
- : {}),
229
- ...(boundAction.argument !== undefined
230
- ? { argument: boundAction.argument }
231
- : {}),
232
- ...(action.binding?.app === boundAction.app
233
- ? {
234
- expectedGeneration: action.binding.generation,
235
- expectedRevision: action.binding.revision,
236
- }
237
- : {}),
238
- method: boundAction.method,
239
- app: boundAction.app,
240
- });
241
- deps.assertExecutionCurrent(query);
242
- const reply = deps.planOutput(result.output, {
243
- binding: {
244
- generation: result.generation,
245
- app: result.app,
246
- revision: result.revision,
247
- },
248
- });
249
- if (handedOff && liveSurfaces) {
250
- try {
251
- await liveSurfaces.resume(handedOff, result);
252
- deps.assertExecutionCurrent(query);
253
- return "edit";
254
- }
255
- catch (error) {
256
- deps.recordRuntimeEvent("generative-app", error, {
257
- phase: "bound-action-live-edit-fallback",
258
- app: boundAction.app,
259
- method: boundAction.method,
260
- });
261
- }
262
- }
263
- if (result.viewMode === "edit" && deps.editInteractiveMessage) {
264
- let editFailed = false;
265
- try {
266
- await deps.editInteractiveMessage(chatId, messageId, reply.markdown, "markdown", reply.replyMarkup ?? { inline_keyboard: [] });
267
- }
268
- catch (error) {
269
- editFailed = true;
270
- deps.recordRuntimeEvent("generative-app", error, {
271
- phase: "bound-action-edit-fallback",
272
- app: boundAction.app,
273
- method: boundAction.method,
274
- });
275
- }
276
- deps.assertExecutionCurrent(query);
277
- if (!editFailed)
278
- return "edit";
279
- }
280
- deps.assertExecutionCurrent(query);
281
- await deps.sendMarkdownReply(chatId, messageId, reply.markdown, {
282
- replyMarkup: reply.replyMarkup,
283
- });
284
- deps.assertExecutionCurrent(query);
285
- return "new";
286
- }
287
- catch (error) {
288
- if (handedOff)
289
- deps.getLiveSurfaceRuntime?.()?.open(handedOff);
290
- deps.recordRuntimeEvent("generative-app", error, {
291
- phase: "bound-action",
292
- ...(boundAction
293
- ? { app: boundAction.app, method: boundAction.method }
294
- : {}),
295
- });
296
- throw error;
297
- }
298
- };
299
- }
300
- export function createTelegramAgentMessageToolRoutingRuntime(deps) {
301
- return {
302
- async resolveAgentTarget(selector) {
303
- if (!deps.ownsLeader())
304
- return deps.follower.resolveTarget(selector);
305
- const target = deps.local.resolveTarget(selector, deps.getSourceTarget());
306
- if (!target) {
307
- throw new Error("Telegram agent target is unavailable, ambiguous, or not live.");
308
- }
309
- return target;
310
- },
311
- async routeAgentMessage(message) {
312
- if (!deps.ownsLeader())
313
- return deps.follower.routeMessage(message);
314
- await deps.local.route({
315
- sourceTarget: deps.getSourceTarget(),
316
- sourceThreadName: deps.getSourceThreadName(),
317
- message,
318
- });
319
- },
320
- canSendDirect() {
321
- return deps.ownsDirectDelivery() || deps.isFollowerRegistered();
322
- },
323
- };
324
- }
325
- export function createTelegramAssistantOutputBindingRuntime(deps) {
326
- const authority = Routing.createTelegramAssistantOutputAuthorityRuntime(deps.authority);
327
- const sendOutput = OutboundHandlers.createTelegramAssistantOutputSender(deps.sender);
328
- const runtime = Activity.createTelegramAssistantOutputRuntime({
329
- ...authority,
330
- enqueue: deps.enqueue,
331
- prepareSend: (event) => event.source === "telegram" ? deps.prepareTelegramPreview?.() : undefined,
332
- async send(event, authority, isAuthorityActive) {
333
- await deps.waitForActivityIdle?.();
334
- if (!isAuthorityActive())
335
- return;
336
- await sendOutput(event, authority, isAuthorityActive);
337
- },
338
- recordFailure(event, error) {
339
- deps.recordRuntimeEvent("proactive-push", error, {
340
- activityId: event.activityId,
341
- sequence: event.sequence,
342
- placement: event.placement,
343
- });
344
- },
345
- });
346
- return {
347
- runtime,
348
- authority,
349
- observeEvent(event) {
350
- if (event.type === "assistant-segment")
351
- runtime.accept(event);
352
- },
353
- };
354
- }
355
- /** Compose public activity fanout, verbosity, and assistant output ordering. */
356
- export function createTelegramActivityBindingRuntime(deps) {
357
- const publication = Activity.createTelegramActivityPublicationRuntime();
358
- const assistantOutputBinding = createTelegramAssistantOutputBindingRuntime({
359
- ...deps.assistantOutput,
360
- enqueue: publication.enqueue,
361
- });
362
- const activityVerbosityRuntime = ActivityVerbosity.createTelegramActivityVerbosityRuntime({
363
- ...deps.activityVerbosity,
364
- enqueue: publication.enqueue,
365
- captureAuthority: assistantOutputBinding.authority.captureAuthority,
366
- isAuthorityActive: assistantOutputBinding.authority.isAuthorityActive,
367
- recordFailure(operation, event, error) {
368
- deps.assistantOutput.recordRuntimeEvent("activity", error, {
369
- operation,
370
- eventType: event.type,
371
- activityId: event.activityId,
372
- });
373
- },
374
- });
375
- const activityRuntime = Activity.createTelegramActivityBridgeRuntime({
376
- generation: deps.generation,
377
- observeEvent(event) {
378
- assistantOutputBinding.observeEvent(event);
379
- activityVerbosityRuntime.accept(event);
380
- },
381
- recordFailure(handlerId, event, error) {
382
- deps.assistantOutput.recordRuntimeEvent("activity", error, {
383
- handlerId,
384
- eventType: event.type,
385
- activityId: event.activityId,
386
- });
387
- },
388
- });
389
- return {
390
- activityRuntime: {
391
- ...activityRuntime,
392
- onSessionStart() {
393
- publication.reset();
394
- activityRuntime.onSessionStart?.();
395
- },
396
- onSessionShutdown() {
397
- publication.reset();
398
- activityRuntime.onSessionShutdown();
399
- },
400
- },
401
- activityVerbosityRuntime,
402
- assistantOutputRuntime: assistantOutputBinding.runtime,
403
- publicationRuntime: {
404
- enqueue: publication.enqueue,
405
- reserve: publication.reserve,
406
- hasPending: publication.hasPending,
407
- beginWork: publication.beginWork,
408
- hasUnconfirmed: publication.hasUnconfirmed,
409
- capture() {
410
- const authority = assistantOutputBinding.authority.captureAuthority();
411
- return {
412
- target: authority.target ? { ...authority.target } : undefined,
413
- isCurrent: () => assistantOutputBinding.authority.isAuthorityActive(authority),
414
- };
415
- },
416
- },
417
- };
418
- }
419
- /** Fresh current work projection; a snapshot is not a cleanup grant or target-application ACK. */
420
- export function createTelegramLiveTargetWorkObserver(deps) {
421
- return (target, ctx) => {
422
- const work = Queue.observeTelegramTargetQueueWork(target, deps.activeTurn.get(), deps.queue.getQueuedItems());
423
- const flags = [
424
- deps.isIdle(ctx),
425
- deps.hasPendingMessages(ctx),
426
- deps.lifecycle.hasDispatchPending(),
427
- deps.lifecycle.isCompactionInProgress(),
428
- deps.hasPendingControl(),
429
- ];
430
- const tools = deps.lifecycle.getActiveToolExecutions();
431
- const publication = deps.publication.hasPending(), unconfirmed = deps.publication.hasUnconfirmed?.(), activity = deps.activity.hasPending?.(), delivery = deps.delivery.hasPendingTarget(target), api = deps.api.hasPendingTarget(target);
432
- const unknown = work.unknown ||
433
- flags.some((flag) => typeof flag !== "boolean") ||
434
- !Number.isSafeInteger(tools) ||
435
- tools < 0 ||
436
- unconfirmed !== false ||
437
- [publication, activity, delivery, api].some((flag) => typeof flag !== "boolean");
438
- return {
439
- sessionBusy: flags[0] !== true || flags.slice(1).some(Boolean) || tools > 0,
440
- targetWork: work.protected,
441
- deliveryPending: publication === true ||
442
- activity === true ||
443
- delivery === true ||
444
- api === true,
445
- unknown,
446
- };
447
- };
448
- }
449
- export function registerTelegramCommandsAndTools({ pi, agentDir, configStore, persistConfig, setup, activeTurnRuntime, lockedPollingRuntime, stopPolling, getDisconnectThreadName, onTransportChanged, getStatusLines, buttonActionStore, sendMarkdownReply, sendChannelMarkdownMessage, sendChannelMediaMessage, listChannelPosts, mutateChannelPost, callMultipart, getDefaultChatId, getDefaultTarget, resolveAgentTarget, routeAgentMessage, canSendDirect, setGenerativeAppLiveSurfaceRuntime, recordRuntimeEvent, updateStatus, isContextCurrent, getSessionGeneration, connectionIntent, }) {
450
- GenerativeApps.registerTelegramBindTool(pi, {
451
- agentDir,
452
- getActiveProfileName: configStore.getActiveProfileName,
453
- getActiveTurn: activeTurnRuntime.get,
454
- ...(setGenerativeAppLiveSurfaceRuntime
455
- ? { setLiveSurfaceRuntime: setGenerativeAppLiveSurfaceRuntime }
456
- : {}),
457
- isDeliveryHandleCurrent: Delivery.isTelegramDeliveryHandleCurrent,
458
- editView: (handle, view) => Delivery.editTelegramView(handle, view),
459
- planOutput: OutboundHandlers.createTelegramOutboundReplyPlanner(buttonActionStore, Config.createTelegramConfigControls(configStore)
460
- .getAssistantRenderingMode),
461
- sendMarkdownReply,
462
- sendView: (view, options) => Delivery.sendTelegramView(view, options),
463
- recordRuntimeEvent,
464
- });
465
- ChannelPosts.registerTelegramChannelPostMutationTool(pi, {
466
- mutate: mutateChannelPost,
467
- });
468
- ChannelPosts.registerTelegramChannelPostListTool(pi, {
469
- list: listChannelPosts,
470
- });
471
- OutboundAttachments.registerTelegramOutboundAttachmentTool(pi, {
472
- getActiveTurn: activeTurnRuntime.get,
473
- getDefaultChatId,
474
- getDefaultTarget,
475
- canSendDirect,
476
- sendMultipart: callMultipart,
477
- recordRuntimeEvent,
478
- });
479
- OutboundAttachments.registerTelegramOutboundMessageTool(pi, {
480
- getDefaultChatId,
481
- getDefaultTarget,
482
- getActiveTurn: activeTurnRuntime.get,
483
- resolveAgentTarget,
484
- routeAgentMessage,
485
- canSendDirect,
486
- planMessage: OutboundHandlers.createTelegramOutboundReplyPlanner(buttonActionStore, Config.createTelegramConfigControls(configStore)
487
- .getAssistantRenderingMode),
488
- sendMarkdownMessage: (chatId, markdown, options) => sendMarkdownReply(chatId, undefined, markdown, options),
489
- sendChannelMarkdownMessage,
490
- sendChannelMediaMessage,
491
- recordRuntimeEvent,
492
- });
493
- const queueAgentConnectionContext = (connected) => {
494
- pi.sendMessage({
495
- customType: "telegram-connection-state",
496
- content: connected
497
- ? Prompts.TELEGRAM_CONNECTED_CONTEXT_MESSAGE
498
- : Prompts.TELEGRAM_DISCONNECTED_CONTEXT_MESSAGE,
499
- display: false,
500
- }, { deliverAs: "nextTurn" });
501
- };
502
- Commands.registerTelegramBridgeCommands(pi, {
503
- promptForConfig: async (ctx, profileName) => {
504
- const nextProfileName = profileName ?? undefined;
505
- if (profileName && !Config.isValidTelegramProfileName(profileName)) {
506
- ctx.ui.notify(`Invalid Telegram profile name: ${profileName}`, "error");
507
- return;
508
- }
509
- const previousProfileName = configStore.getActiveProfileName();
510
- let setupConfigStore = configStore;
511
- let persistSetupConfig = persistConfig;
512
- if (!profileName) {
513
- if (previousProfileName !== nextProfileName) {
514
- await (stopPolling ?? lockedPollingRuntime.stop)();
515
- }
516
- configStore.activateProfile(undefined);
517
- await onTransportChanged?.();
518
- }
519
- else {
520
- const storedConfig = configStore.getStoredConfig();
521
- setupConfigStore = Config.createTelegramConfigStore({
522
- initialConfig: {
523
- ...storedConfig,
524
- profiles: {
525
- ...(storedConfig.profiles ?? {}),
526
- [profileName]: storedConfig.profiles?.[profileName] ?? {
527
- botToken: "",
528
- },
529
- },
530
- },
531
- });
532
- setupConfigStore.activateProfile(profileName);
533
- persistSetupConfig = async () => {
534
- try {
535
- if (previousProfileName !== profileName) {
536
- await (stopPolling ?? lockedPollingRuntime.stop)();
537
- }
538
- const profile = Config.getTelegramProfileFields(setupConfigStore.get());
539
- if (!profile) {
540
- throw new Error(`Telegram profile "${profileName}" has no token.`);
541
- }
542
- await configStore.load();
543
- const latestProfile = configStore.getStoredConfig().profiles?.[profileName];
544
- configStore.setProfile(profileName, {
545
- ...profile,
546
- threadDisplayMode: latestProfile?.threadDisplayMode,
547
- });
548
- configStore.activateProfile(profileName);
549
- await onTransportChanged?.();
550
- await persistConfig(configStore.get());
551
- }
552
- catch (error) {
553
- await configStore.load().catch(() => undefined);
554
- configStore.activateProfile(previousProfileName);
555
- await onTransportChanged?.();
556
- throw error;
557
- }
558
- };
559
- }
560
- const runSetup = Setup.createTelegramSetupPromptRuntime({
561
- getConfig: setupConfigStore.get,
562
- setConfig: setupConfigStore.set,
563
- setupGuard: setup,
564
- getMe: TelegramApi.fetchTelegramBotIdentity,
565
- resolveBotToken: (value) => Config.resolveTelegramBotToken(value, process.env),
566
- describeBotToken: (value) => Config.getTelegramBotTokenDiagnostic(value, process.env),
567
- persistConfig: persistSetupConfig,
568
- startPolling: lockedPollingRuntime.start,
569
- updateStatus,
570
- recordRuntimeEvent,
571
- });
572
- const completion = await runSetup(ctx);
573
- if (completion.status === "success") {
574
- queueAgentConnectionContext(true);
575
- if (profileName) {
576
- ctx.ui.notify(`Profile "${profileName}" saved and connected.`, "info");
577
- }
578
- }
579
- },
580
- getStatusLines,
581
- reloadConfig: configStore.load,
582
- hasBotToken: configStore.hasBotToken,
583
- getBotTokenDiagnostic: configStore.getBotTokenDiagnostic,
584
- startPolling: lockedPollingRuntime.start,
585
- recordConnectionEvent: (error, phase) => recordRuntimeEvent("connection", error, { phase }),
586
- stopPolling: stopPolling ?? lockedPollingRuntime.stop,
587
- getDisconnectThreadName,
588
- queueAgentConnectionContext,
589
- updateStatus,
590
- isContextCurrent,
591
- getSessionGeneration,
592
- connectionIntent,
593
- getProfileNames: () => Config.getTelegramProfileNames(configStore.getStoredConfig()),
594
- activateDefaultProfileConfig: async (_ctx, isCurrent) => {
595
- const previousProfileName = configStore.getActiveProfileName();
596
- await configStore.load();
597
- if (!isCurrent())
598
- return;
599
- if (previousProfileName) {
600
- await (stopPolling ?? lockedPollingRuntime.stop)();
601
- if (!isCurrent())
602
- return;
603
- }
604
- configStore.activateProfile(undefined);
605
- await onTransportChanged?.();
606
- },
607
- activateProfileConfig: async (_ctx, profileName, isCurrent) => {
608
- const previousProfileName = configStore.getActiveProfileName();
609
- await configStore.load();
610
- if (!isCurrent())
611
- return false;
612
- if (!Config.isValidTelegramProfileName(profileName))
613
- return false;
614
- const storedConfig = configStore.getStoredConfig();
615
- if (!storedConfig.profiles?.[profileName])
616
- return false;
617
- if (previousProfileName !== profileName) {
618
- await (stopPolling ?? lockedPollingRuntime.stop)();
619
- if (!isCurrent())
620
- return false;
621
- }
622
- if (!configStore.activateProfile(profileName))
623
- return false;
624
- await onTransportChanged?.();
625
- return true;
626
- },
627
- });
628
- }
629
- export function registerTelegramLifecycleRuntimeHooks({ pi, publicationRuntime, activityRuntime, activityVerbosityRuntime, diagnostics, assistantOutputRuntime, sessionLifecycleRuntime, configStore, abort, typing, lifecycle, activeTurnRuntime, telegramQueueStore, modelSwitchController, previewRuntime, promptDispatchRuntime, deferredQueueDispatchRuntime, modelContextAvailabilityRuntime, disconnectOnQuit, onSessionStarted, shutdownGenerativeAppLiveSurfaces, resolveAutomaticThreadCleanupEnabled, buttonActionStore, callMultipart, sendChatAction, sendRecordVoiceAction, sendMarkdownReply, sendTextReply, dispatchNextQueuedTelegramTurn, onPromptHandedOff, answerGuestQuery, deleteMessage, sendGuestReply, editGuestReply, stopGuestPlaceholder, preparePreviewDelivery, finalizeMarkdownPreview, proactivePushTargetGetter, getAssistantRenderingMode, recordMessageOwnership, canSendAgentActivity, isSessionContextActive = () => true, isTurnTransportActive, updateStatus, recordRuntimeEvent, }) {
630
- const agentEndResetter = Runtime.createTelegramAgentEndResetter({
631
- abort,
632
- typing,
633
- clearActiveTurn: activeTurnRuntime.clear,
634
- resetToolExecutions: lifecycle.resetActiveToolExecutions,
635
- clearPendingModelSwitch: modelSwitchController.clearPendingSwitch,
636
- clearDispatchPending: lifecycle.clearDispatchPending,
637
- });
638
- const queuedAttachmentSender = OutboundAttachments.createTelegramQueuedOutboundAttachmentSender({
639
- sendMultipart: callMultipart,
640
- sendTextReply,
641
- recordRuntimeEvent,
642
- });
643
- const richAttachmentSender = OutboundAttachments.createTelegramRichOutboundAttachmentSender({
644
- sendMultipart: callMultipart,
645
- getRenderingMode: getAssistantRenderingMode,
646
- recordOwnership: recordMessageOwnership,
647
- recordRuntimeEvent,
648
- });
649
- const sendGuestAttachment = async (turn, attachment, caption) => {
650
- const stagingTarget = proactivePushTargetGetter();
651
- const stagingChatId = stagingTarget?.chatId;
652
- if (stagingChatId === undefined) {
653
- throw new Error("Guest attachment staging requires a paired Telegram chat");
654
- }
655
- await OutboundAttachments.deliverTelegramGuestCachedAttachment({
656
- guestQueryId: turn.guestQueryId,
657
- stagingChatId,
658
- stagingTarget,
659
- attachment,
660
- caption,
661
- sendMultipart: callMultipart,
662
- answerGuestQuery: (guestQueryId, result) => answerGuestQuery(guestQueryId, undefined, { result }),
663
- answerGuestText: (guestQueryId, text) => answerGuestQuery(guestQueryId, text),
664
- fallbackText: caption ||
665
- "Telegram bridge could not deliver the requested attachment.",
666
- deleteMessage,
667
- recordRuntimeEvent,
668
- });
669
- };
670
- const outboundReplyPlanner = OutboundHandlers.createTelegramOutboundReplyPlanner(buttonActionStore, getAssistantRenderingMode);
671
- const voiceReplySenderDeps = {
672
- execCommand: CommandTemplates.execCommandTemplate,
673
- sendMultipart: callMultipart,
674
- sendTextReply,
675
- sendChatAction,
676
- sendRecordVoiceAction,
677
- getHandlers: configStore.getOutboundHandlers,
678
- recordRuntimeEvent,
679
- };
680
- const outboundReplyArtifactSender = OutboundHandlers.createTelegramOutboundReplyArtifactSender(voiceReplySenderDeps);
681
- const sendGuestVoiceReply = async (turn, plan, caption) => {
682
- const stagingTarget = proactivePushTargetGetter();
683
- const stagingChatId = stagingTarget?.chatId;
684
- if (stagingChatId === undefined) {
685
- throw new Error("Guest voice staging requires a paired Telegram chat");
686
- }
687
- const guestVoiceSender = OutboundHandlers.createTelegramOutboundReplyArtifactSender({
688
- ...voiceReplySenderDeps,
689
- sendChatAction: undefined,
690
- sendRecordVoiceAction: undefined,
691
- sendMultipart: async (_method, _fields, _fileField, filePath, fileName) => {
692
- try {
693
- await OutboundAttachments.deliverTelegramGuestCachedAttachment({
694
- guestQueryId: turn.guestQueryId,
695
- stagingChatId,
696
- stagingTarget,
697
- attachment: { path: filePath, fileName },
698
- caption,
699
- sendMultipart: callMultipart,
700
- answerGuestQuery: (guestQueryId, result) => answerGuestQuery(guestQueryId, undefined, { result }),
701
- answerGuestText: (guestQueryId, text) => answerGuestQuery(guestQueryId, text),
702
- fallbackText: caption || "Telegram bridge could not deliver the voice reply.",
703
- deleteMessage,
704
- recordRuntimeEvent,
705
- });
706
- }
707
- catch (error) {
708
- recordRuntimeEvent("delivery", error, {
709
- phase: "guest-voice-answer",
710
- guestQueryId: turn.guestQueryId,
711
- });
712
- }
713
- return {};
714
- },
715
- });
716
- await guestVoiceSender(turn, {
717
- ...plan,
718
- ...(plan.voiceReplies?.length
719
- ? { voiceReplies: [plan.voiceReplies[0]] }
720
- : {}),
721
- }, { replyToPrompt: false });
722
- };
723
- let pendingFinalPublication;
724
- const cancelPendingFinalPublication = () => {
725
- pendingFinalPublication?.reservation.cancel();
726
- pendingFinalPublication = undefined;
727
- };
728
- const recordPublicationFailure = (error) => {
729
- recordRuntimeEvent("delivery", error, {
730
- phase: "agent-end-background-delivery",
731
- });
732
- };
733
- const scheduleActiveTurnDelivery = (task) => {
734
- void publicationRuntime.enqueue(task).catch(recordPublicationFailure);
735
- };
736
- const agentLifecycleHooks = Queue.createTelegramAgentLifecycleHooks({
737
- setAbortHandler: Runtime.createTelegramContextAbortHandlerSetter(abort),
738
- getQueuedItems: telegramQueueStore.getQueuedItems,
739
- hasPendingDispatch: lifecycle.hasDispatchPending,
740
- hasActiveTurn: activeTurnRuntime.has,
741
- resetToolExecutions: lifecycle.resetActiveToolExecutions,
742
- resetPendingModelSwitch: modelSwitchController.clearPendingSwitch,
743
- setQueuedItems: telegramQueueStore.setQueuedItems,
744
- clearDispatchPending: lifecycle.clearDispatchPending,
745
- setFoldQueuedPromptsIntoHistory: lifecycle.setFoldQueuedPromptsIntoHistory,
746
- setActiveTurn: activeTurnRuntime.set,
747
- onPromptHandedOff: (turn, ctx) => {
748
- assistantOutputRuntime.beginTurn();
749
- onPromptHandedOff?.(turn, ctx);
750
- },
751
- createPreviewState: previewRuntime.resetState,
752
- startTypingLoop: (ctx) => {
753
- const turn = activeTurnRuntime.get();
754
- promptDispatchRuntime.startTypingLoop(ctx, turn?.chatId, {
755
- target: turn?.target,
756
- });
757
- },
758
- updateStatus,
759
- getActiveTurn: activeTurnRuntime.get,
760
- loadConfig: configStore.load,
761
- extractAssistant: Replies.extractRunAssistantMessage,
762
- isAssistantAlreadyPublished: (assistant) => !!assistant.text &&
763
- assistantOutputRuntime.hasAdmittedTelegramIntermediate(assistant.text),
764
- getFoldQueuedPromptsIntoHistory: lifecycle.shouldFoldQueuedPromptsIntoHistory,
765
- resetRuntimeState: agentEndResetter,
766
- isSessionActive: isSessionContextActive,
767
- isTurnTransportActive,
768
- waitForTypingIdle: typing.waitForIdle,
769
- dispatchNextQueuedTelegramTurn,
770
- requestDeferredDispatchNextQueuedTelegramTurn: deferredQueueDispatchRuntime.request,
771
- scheduleActiveTurnDelivery,
772
- reserveActiveTurnDelivery() {
773
- const pending = pendingFinalPublication;
774
- pendingFinalPublication = undefined;
775
- const matches = pending?.turn === activeTurnRuntime.get();
776
- if (!matches)
777
- pending?.reservation.cancel();
778
- const reservation = matches && pending ? pending.reservation : publicationRuntime.reserve();
779
- return {
780
- schedule: (task) => {
781
- void reservation.publish(task).catch(recordPublicationFailure);
782
- },
783
- cancel: reservation.cancel,
784
- };
785
- },
786
- preparePreviewDelivery,
787
- preparePreviewClear: previewRuntime.prepareClear,
788
- clearPreview: previewRuntime.clear,
789
- setPreviewPendingText: previewRuntime.setPendingText,
790
- finalizeMarkdownPreview,
791
- sendMarkdownReply,
792
- sendTextReply,
793
- sendQueuedAttachments: queuedAttachmentSender,
794
- sendRichAttachmentReply: richAttachmentSender,
795
- answerGuestQuery,
796
- sendGuestReply,
797
- editGuestReply,
798
- stopGuestPlaceholder,
799
- sendGuestAttachment,
800
- sendGuestVoiceReply,
801
- planOutboundReply: outboundReplyPlanner,
802
- sendOutboundReplyArtifacts: outboundReplyArtifactSender,
803
- recordRuntimeEvent,
804
- getActiveToolExecutions: lifecycle.getActiveToolExecutions,
805
- setActiveToolExecutions: lifecycle.setActiveToolExecutions,
806
- triggerPendingModelSwitchAbort: modelSwitchController.triggerPendingAbort,
807
- });
808
- const agentStartWithDedupReset = Lifecycle.createAgentStartDedupHook(agentLifecycleHooks.onAgentStart, Replies.resetTransportReplyDedup, scheduleActiveTurnDelivery);
809
- let uiPromptActive = false;
810
- const startAgentActivityTypingLoop = (ctx) => {
811
- if (uiPromptActive || !canSendAgentActivity(ctx))
812
- return false;
813
- const turn = activeTurnRuntime.get();
814
- const target = turn?.target ?? proactivePushTargetGetter();
815
- return (promptDispatchRuntime.startTypingLoop(ctx, turn?.chatId ?? target?.chatId, { target }) !== false);
816
- };
817
- const startActiveTurnTypingLoop = (ctx) => {
818
- if (uiPromptActive)
819
- return;
820
- const turn = activeTurnRuntime.get();
821
- promptDispatchRuntime.startTypingLoop(ctx, turn?.chatId, {
822
- target: turn?.target,
823
- });
824
- };
825
- let observedAutomaticCompaction = false;
826
- let agentWorkActive = false;
827
- const prepareCompactionNotice = (text, ctx) => {
828
- const authority = publicationRuntime.capture();
829
- const turn = activeTurnRuntime.get();
830
- const selectedTarget = turn?.target ?? authority.target;
831
- const target = selectedTarget ? { ...selectedTarget } : undefined;
832
- const replyToMessageId = turn?.replyToMessageId;
833
- return async () => {
834
- if (!target || !isSessionContextActive(ctx) || !authority.isCurrent())
835
- return;
836
- if (turn && isTurnTransportActive?.(turn) === false)
837
- return;
838
- try {
839
- await sendMarkdownReply(target.chatId, replyToMessageId, text, {
840
- target,
841
- });
842
- }
843
- catch (error) {
844
- recordRuntimeEvent("delivery", error, { phase: "compaction-notice" });
845
- }
846
- };
847
- };
848
- const sendCompactionNotice = (text, ctx) => {
849
- scheduleActiveTurnDelivery(prepareCompactionNotice(text, ctx));
850
- };
851
- const compactionObserver = Lifecycle.createTelegramCompactionObserverRuntime({
852
- isContextActive: isSessionContextActive,
853
- setCompactionInProgress: lifecycle.setCompactionInProgress,
854
- updateStatus,
855
- startTypingLoop: startAgentActivityTypingLoop,
856
- stopTypingLoop: typing.stop,
857
- requestDeferredDispatchNextQueuedTelegramTurn: deferredQueueDispatchRuntime.request,
858
- dispatchNextQueuedTelegramTurn,
859
- recordRuntimeEvent,
860
- onCompactionAbandoned: () => {
861
- observedAutomaticCompaction = false;
862
- activityRuntime.onCompactionAbandoned();
863
- },
864
- });
865
- const messageActivityTypingHooks = Lifecycle.createTelegramMessageActivityTypingHooks({
866
- hasActiveTurn: activeTurnRuntime.has,
867
- startTypingLoop: startActiveTurnTypingLoop,
868
- onMessageStart: previewRuntime.onMessageStart,
869
- onMessageUpdate: previewRuntime.onMessageUpdate,
870
- recordRuntimeEvent,
871
- });
872
- const messageActivityHooks = messageActivityTypingHooks;
873
- Lifecycle.registerTelegramLifecycleHooks(pi, {
874
- isSessionActive: isSessionContextActive,
875
- ...sessionLifecycleRuntime,
876
- ...agentLifecycleHooks,
877
- onInput(event) {
878
- activityRuntime.recordInputSource(event.source ?? "unknown");
879
- },
880
- async onSessionStart(event, ctx) {
881
- diagnostics?.onSessionStart();
882
- cancelPendingFinalPublication();
883
- previewRuntime.invalidate();
884
- assistantOutputRuntime.start();
885
- activityRuntime.onSessionStart?.();
886
- activityVerbosityRuntime?.reset();
887
- modelContextAvailabilityRuntime.reconcile();
888
- await sessionLifecycleRuntime.onSessionStart(event, ctx);
889
- onSessionStarted?.(event, ctx);
890
- },
891
- async onSessionShutdown(event, ctx) {
892
- if (!isSessionContextActive(ctx))
893
- return;
894
- const diagnosticsStopped = diagnostics?.onSessionShutdown();
895
- shutdownGenerativeAppLiveSurfaces?.();
896
- agentLifecycleHooks.clearRetainedAgentEnd();
897
- activityRuntime.onSessionShutdown();
898
- activityVerbosityRuntime?.reset();
899
- assistantOutputRuntime.stop();
900
- observedAutomaticCompaction = false;
901
- agentWorkActive = false;
902
- cancelPendingFinalPublication();
903
- uiPromptActive = false;
904
- compactionObserver.onSessionShutdown();
905
- await diagnosticsStopped;
906
- if (!isSessionContextActive(ctx))
907
- return;
908
- if (event.reason === "quit" && disconnectOnQuit) {
909
- try {
910
- const automaticCleanupEnabled = (await resolveAutomaticThreadCleanupEnabled?.()) ?? true;
911
- if (automaticCleanupEnabled)
912
- await disconnectOnQuit();
913
- }
914
- catch (error) {
915
- recordRuntimeEvent("session", error, {
916
- phase: "automatic-disconnect-on-quit",
917
- });
918
- }
919
- }
920
- await sessionLifecycleRuntime.onSessionShutdown(event, ctx);
921
- },
922
- async onSessionBeforeCompact(event, ctx) {
923
- if (!isSessionContextActive(ctx))
924
- return;
925
- const shouldNotify = !(lifecycle.isCompactionInProgress?.() ?? false);
926
- if (shouldNotify)
927
- observedAutomaticCompaction = true;
928
- activityRuntime.onCompactionStart(Pi.getSessionCompactionReason(event));
929
- compactionObserver.onSessionBeforeCompact(event, ctx);
930
- if (shouldNotify)
931
- sendCompactionNotice(Commands.TELEGRAM_COMPACTION_STARTED_MARKDOWN, ctx);
932
- },
933
- async onSessionCompact(event, ctx) {
934
- if (!isSessionContextActive(ctx))
935
- return;
936
- activityRuntime.onCompactionEnd(Pi.getSessionCompactionReason(event));
937
- compactionObserver.onSessionCompact(event, ctx);
938
- if (observedAutomaticCompaction) {
939
- observedAutomaticCompaction = false;
940
- sendCompactionNotice(Commands.TELEGRAM_COMPACTION_COMPLETED_MARKDOWN, ctx);
941
- }
942
- },
943
- async onSessionCompactFailed(event, ctx) {
944
- if (!isSessionContextActive(ctx))
945
- return;
946
- const shouldNotify = observedAutomaticCompaction;
947
- compactionObserver.onSessionCompactFailed(event, ctx);
948
- if (!shouldNotify)
949
- return;
950
- const notice = event.aborted
951
- ? "**⚠️ Compaction cancelled.**"
952
- : "**⚠️ Compaction failed.**";
953
- sendCompactionNotice(notice, ctx);
954
- },
955
- async onAgentStart(event, ctx) {
956
- if (!isSessionContextActive(ctx))
957
- return;
958
- agentWorkActive = true;
959
- cancelPendingFinalPublication();
960
- await agentStartWithDedupReset(event, ctx);
961
- const turn = activeTurnRuntime.get();
962
- activityRuntime.onAgentStart(turn?.target, turn?.replyToMessageId);
963
- startAgentActivityTypingLoop(ctx);
964
- },
965
- async onToolExecutionStart(event, ctx) {
966
- if (!isSessionContextActive(ctx))
967
- return;
968
- agentLifecycleHooks.onToolExecutionStart();
969
- activityRuntime.onToolStart({
970
- toolCallId: event.toolCallId,
971
- toolName: event.toolName,
972
- args: event.args,
973
- });
974
- },
975
- onToolExecutionUpdate(event, ctx) {
976
- if (!isSessionContextActive(ctx))
977
- return;
978
- activityRuntime.onToolUpdate({
979
- toolCallId: event.toolCallId,
980
- toolName: event.toolName,
981
- update: event.partialResult,
982
- });
983
- },
984
- async onToolExecutionEnd(event, ctx) {
985
- if (!isSessionContextActive(ctx))
986
- return;
987
- activityRuntime.onToolEnd({
988
- toolCallId: event.toolCallId,
989
- toolName: event.toolName,
990
- result: event.result,
991
- isError: event.isError,
992
- });
993
- agentLifecycleHooks.onToolExecutionEnd(event, ctx);
994
- },
995
- async onMessageStart(event, ctx) {
996
- if (!isSessionContextActive(ctx))
997
- return;
998
- await messageActivityHooks.onMessageStart(event, ctx);
999
- },
1000
- async onMessageUpdate(event, ctx) {
1001
- if (!isSessionContextActive(ctx))
1002
- return;
1003
- if (event.assistantMessageEvent) {
1004
- activityRuntime.onAssistantEvent(event.assistantMessageEvent);
1005
- }
1006
- await messageActivityHooks.onMessageUpdate(event, ctx);
1007
- },
1008
- onMessageEnd(event, ctx) {
1009
- if (!isSessionContextActive(ctx))
1010
- return;
1011
- if (event.message.role === "assistant") {
1012
- previewRuntime.seal();
1013
- activityRuntime.onAssistantMessageEnd(event.message.stopReason);
1014
- }
1015
- if (event.message.role !== "assistant" ||
1016
- event.message.stopReason === "toolUse" ||
1017
- event.message.stopReason === "aborted")
1018
- return;
1019
- const turn = activeTurnRuntime.get();
1020
- if (!turn || turn.guestQueryId || pendingFinalPublication?.turn === turn)
1021
- return;
1022
- cancelPendingFinalPublication();
1023
- const assistant = Replies.extractLatestAssistantMessageText([
1024
- event.message,
1025
- ]);
1026
- if (!assistant.text &&
1027
- assistant.stopReason !== "error" &&
1028
- turn.queuedAttachments.length === 0)
1029
- return;
1030
- pendingFinalPublication = {
1031
- turn,
1032
- reservation: publicationRuntime.reserve(),
1033
- };
1034
- },
1035
- onUiPromptStart(event, ctx) {
1036
- if (!isSessionContextActive(ctx))
1037
- return;
1038
- uiPromptActive = true;
1039
- typing.stop();
1040
- activityRuntime.onUiPromptStart(event.kind, event.title);
1041
- updateStatus(ctx);
1042
- },
1043
- onUiPromptEnd(_event, ctx) {
1044
- if (!isSessionContextActive(ctx))
1045
- return;
1046
- uiPromptActive = false;
1047
- activityRuntime.onUiPromptEnd();
1048
- if (agentWorkActive || lifecycle.isCompactionInProgress()) {
1049
- startAgentActivityTypingLoop(ctx);
1050
- }
1051
- updateStatus(ctx);
1052
- },
1053
- async onAgentEnd(event, ctx) {
1054
- if (!isSessionContextActive(ctx))
1055
- return;
1056
- if (pendingFinalPublication &&
1057
- pendingFinalPublication.turn !== activeTurnRuntime.get()) {
1058
- cancelPendingFinalPublication();
1059
- return;
1060
- }
1061
- activityRuntime.onAgentEnd();
1062
- await agentLifecycleHooks.onAgentEnd(event, ctx);
1063
- },
1064
- async onAgentSettled(event, ctx) {
1065
- if (!isSessionContextActive(ctx))
1066
- return;
1067
- const pending = pendingFinalPublication;
1068
- try {
1069
- await agentLifecycleHooks.onAgentSettled(event, ctx);
1070
- }
1071
- finally {
1072
- if (pendingFinalPublication === pending)
1073
- cancelPendingFinalPublication();
1074
- }
1075
- if (!isSessionContextActive(ctx))
1076
- return;
1077
- agentWorkActive = false;
1078
- activityRuntime.onAgentSettled();
1079
- modelContextAvailabilityRuntime.reconcile();
1080
- },
1081
- onBeforeAgentStart: Prompts.createTelegramProactiveBeforeAgentStartHook({
1082
- reconcileAvailability: modelContextAvailabilityRuntime.reconcile,
1083
- isAvailable: canSendAgentActivity,
1084
- }),
1085
- });
1086
- }