@llblab/pi-telegram 0.52.2 → 0.53.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.
Files changed (204) hide show
  1. package/AGENTS.md +9 -7
  2. package/BACKLOG.md +1 -18
  3. package/CHANGELOG.md +15 -0
  4. package/README.md +7 -11
  5. package/api/commands.ts +3 -0
  6. package/dist/api/commands.d.ts +1 -1
  7. package/dist/lib/activity-verbosity.d.ts +1 -5
  8. package/dist/lib/activity-verbosity.js +15 -42
  9. package/dist/lib/activity.d.ts +9 -0
  10. package/dist/lib/activity.js +39 -10
  11. package/dist/lib/agent-messages.js +7 -3
  12. package/dist/lib/bindings.d.ts +43 -5
  13. package/dist/lib/bindings.js +163 -20
  14. package/dist/lib/bus-api.d.ts +12 -0
  15. package/dist/lib/bus-api.js +182 -79
  16. package/dist/lib/bus-follower.d.ts +111 -31
  17. package/dist/lib/bus-follower.js +1324 -359
  18. package/dist/lib/bus-leader.d.ts +41 -91
  19. package/dist/lib/bus-leader.js +969 -637
  20. package/dist/lib/bus-transport.js +16 -9
  21. package/dist/lib/bus.d.ts +215 -18
  22. package/dist/lib/bus.js +1048 -327
  23. package/dist/lib/channel-posts.d.ts +5 -0
  24. package/dist/lib/channel-posts.js +434 -261
  25. package/dist/lib/command-templates.d.ts +26 -5
  26. package/dist/lib/command-templates.js +92 -176
  27. package/dist/lib/commands.d.ts +168 -82
  28. package/dist/lib/commands.js +1409 -291
  29. package/dist/lib/config.d.ts +1 -8
  30. package/dist/lib/config.js +34 -76
  31. package/dist/lib/delivery.d.ts +2 -2
  32. package/dist/lib/delivery.js +16 -16
  33. package/dist/lib/extension.js +639 -171
  34. package/dist/lib/generative-app-worker.mjs +19 -7
  35. package/dist/lib/generative-apps.d.ts +0 -5
  36. package/dist/lib/generative-apps.js +61 -49
  37. package/dist/lib/inbound.d.ts +5 -0
  38. package/dist/lib/inbound.js +35 -59
  39. package/dist/lib/journal.d.ts +33 -3
  40. package/dist/lib/journal.js +2063 -782
  41. package/dist/lib/lifecycle.d.ts +2 -6
  42. package/dist/lib/lifecycle.js +75 -58
  43. package/dist/lib/locks.d.ts +29 -3
  44. package/dist/lib/locks.js +289 -108
  45. package/dist/lib/logging.js +50 -14
  46. package/dist/lib/media.d.ts +22 -1
  47. package/dist/lib/media.js +46 -28
  48. package/dist/lib/menu-model.d.ts +19 -5
  49. package/dist/lib/menu-model.js +53 -11
  50. package/dist/lib/menu-queue.d.ts +5 -4
  51. package/dist/lib/menu-queue.js +45 -63
  52. package/dist/lib/menu-settings.d.ts +5 -4
  53. package/dist/lib/menu-settings.js +114 -78
  54. package/dist/lib/menu-status.d.ts +1 -1
  55. package/dist/lib/menu-status.js +8 -25
  56. package/dist/lib/menu-thinking.d.ts +6 -1
  57. package/dist/lib/menu-thinking.js +14 -21
  58. package/dist/lib/menu.d.ts +8 -61
  59. package/dist/lib/menu.js +92 -164
  60. package/dist/lib/model.d.ts +1 -1
  61. package/dist/lib/model.js +2 -16
  62. package/dist/lib/outbound-attachments.d.ts +6 -27
  63. package/dist/lib/outbound-attachments.js +45 -38
  64. package/dist/lib/outbound-buttons.js +22 -16
  65. package/dist/lib/outbound-markup.d.ts +0 -2
  66. package/dist/lib/outbound-markup.js +51 -64
  67. package/dist/lib/outbound-voice.js +6 -3
  68. package/dist/lib/outbound.d.ts +6 -6
  69. package/dist/lib/outbound.js +65 -51
  70. package/dist/lib/ownership.js +6 -2
  71. package/dist/lib/paths.d.ts +5 -0
  72. package/dist/lib/paths.js +37 -21
  73. package/dist/lib/pi.d.ts +2 -0
  74. package/dist/lib/pi.js +8 -1
  75. package/dist/lib/polling.d.ts +21 -37
  76. package/dist/lib/polling.js +59 -50
  77. package/dist/lib/preview.js +39 -19
  78. package/dist/lib/process-identity.d.ts +24 -0
  79. package/dist/lib/process-identity.js +133 -0
  80. package/dist/lib/prompts.js +2 -7
  81. package/dist/lib/queue.d.ts +14 -16
  82. package/dist/lib/queue.js +252 -127
  83. package/dist/lib/recovery.d.ts +2 -83
  84. package/dist/lib/recovery.js +20 -274
  85. package/dist/lib/replies.d.ts +16 -24
  86. package/dist/lib/replies.js +99 -116
  87. package/dist/lib/routing.d.ts +120 -7
  88. package/dist/lib/routing.js +4667 -1152
  89. package/dist/lib/runtime.d.ts +1 -11
  90. package/dist/lib/sections.d.ts +4 -29
  91. package/dist/lib/sections.js +7 -20
  92. package/dist/lib/skills.js +4 -3
  93. package/dist/lib/status.js +86 -57
  94. package/dist/lib/sync.d.ts +1 -3
  95. package/dist/lib/sync.js +67 -43
  96. package/dist/lib/target.d.ts +4 -0
  97. package/dist/lib/target.js +18 -0
  98. package/dist/lib/telegram-api.d.ts +18 -13
  99. package/dist/lib/telegram-api.js +148 -53
  100. package/dist/lib/text-groups.js +4 -39
  101. package/dist/lib/thread-cleanup-manager.d.ts +6 -27
  102. package/dist/lib/thread-cleanup-manager.js +369 -186
  103. package/dist/lib/thread-display.d.ts +1 -2
  104. package/dist/lib/thread-display.js +58 -38
  105. package/dist/lib/thread-naming.d.ts +28 -8
  106. package/dist/lib/thread-naming.js +175 -66
  107. package/dist/lib/thread-reconciler.d.ts +42 -16
  108. package/dist/lib/thread-reconciler.js +90 -50
  109. package/dist/lib/threads.d.ts +82 -20
  110. package/dist/lib/threads.js +2247 -855
  111. package/dist/lib/time-injection.js +1 -2
  112. package/dist/lib/turns.js +1 -19
  113. package/dist/lib/updates.d.ts +169 -80
  114. package/dist/lib/updates.js +2418 -641
  115. package/dist/lib/voice.d.ts +7 -22
  116. package/dist/lib/voice.js +7 -22
  117. package/dist/lib/wire.js +1 -1
  118. package/dist/lib/workspace-admission.d.ts +3 -1
  119. package/dist/lib/workspace-admission.js +425 -211
  120. package/dist/lib/workspace-identity.js +8 -13
  121. package/dist/lib/workspace-retirement.d.ts +1 -1
  122. package/dist/lib/workspace-retirement.js +328 -140
  123. package/dist/lib/workspace-slots.js +8 -4
  124. package/dist/package.json +1 -1
  125. package/docs/architecture.md +31 -29
  126. package/docs/inbound.md +1 -1
  127. package/docs/multi-instance-bus.md +15 -3
  128. package/docs/outbound.md +1 -1
  129. package/docs/public-api.md +29 -1
  130. package/docs/ui-style.md +25 -8
  131. package/lib/activity-verbosity.ts +59 -82
  132. package/lib/activity.ts +146 -102
  133. package/lib/agent-messages.ts +12 -5
  134. package/lib/bindings.ts +389 -82
  135. package/lib/bus-api.ts +263 -102
  136. package/lib/bus-follower.ts +2440 -601
  137. package/lib/bus-leader.ts +2209 -1290
  138. package/lib/bus-transport.ts +46 -12
  139. package/lib/bus.ts +1673 -444
  140. package/lib/channel-posts.ts +848 -327
  141. package/lib/command-templates.ts +152 -265
  142. package/lib/commands.ts +2398 -555
  143. package/lib/config.ts +177 -170
  144. package/lib/delivery.ts +41 -29
  145. package/lib/extension.ts +1206 -433
  146. package/lib/generative-app-worker.mjs +19 -7
  147. package/lib/generative-apps.ts +324 -211
  148. package/lib/inbound.ts +95 -97
  149. package/lib/journal.ts +5311 -1763
  150. package/lib/keyboard.ts +1 -3
  151. package/lib/lifecycle.ts +128 -78
  152. package/lib/locks.ts +677 -203
  153. package/lib/logging.ts +147 -58
  154. package/lib/media.ts +82 -43
  155. package/lib/menu-model.ts +85 -18
  156. package/lib/menu-queue.ts +88 -129
  157. package/lib/menu-settings.ts +260 -155
  158. package/lib/menu-status.ts +20 -60
  159. package/lib/menu-thinking.ts +41 -56
  160. package/lib/menu.ts +112 -285
  161. package/lib/model.ts +8 -22
  162. package/lib/outbound-attachments.ts +156 -125
  163. package/lib/outbound-buttons.ts +76 -49
  164. package/lib/outbound-markup.ts +96 -94
  165. package/lib/outbound-voice.ts +33 -27
  166. package/lib/outbound.ts +128 -125
  167. package/lib/ownership.ts +19 -10
  168. package/lib/paths.ts +148 -49
  169. package/lib/pi.ts +35 -15
  170. package/lib/polling.ts +144 -147
  171. package/lib/preview.ts +93 -40
  172. package/lib/process-identity.ts +164 -0
  173. package/lib/prompts.ts +4 -11
  174. package/lib/queue.ts +516 -301
  175. package/lib/recovery.ts +50 -454
  176. package/lib/rendering.ts +1 -3
  177. package/lib/replies.ts +248 -209
  178. package/lib/routing.ts +8509 -2186
  179. package/lib/runtime.ts +1 -12
  180. package/lib/sections.ts +24 -89
  181. package/lib/setup.ts +1 -3
  182. package/lib/skills.ts +9 -4
  183. package/lib/status.ts +215 -110
  184. package/lib/sync.ts +448 -361
  185. package/lib/target.ts +22 -3
  186. package/lib/telegram-api.ts +302 -127
  187. package/lib/text-groups.ts +5 -39
  188. package/lib/thread-cleanup-manager.ts +833 -331
  189. package/lib/thread-display.ts +213 -97
  190. package/lib/thread-naming.ts +232 -87
  191. package/lib/thread-reconciler.ts +155 -72
  192. package/lib/threads.ts +5318 -1845
  193. package/lib/time-injection.ts +1 -4
  194. package/lib/turns.ts +15 -28
  195. package/lib/updates.ts +5184 -1612
  196. package/lib/voice.ts +7 -35
  197. package/lib/wire.ts +1 -1
  198. package/lib/workspace-admission.ts +996 -416
  199. package/lib/workspace-identity.ts +14 -17
  200. package/lib/workspace-retirement.ts +878 -376
  201. package/lib/workspace-slots.ts +22 -13
  202. package/package.json +1 -1
  203. package/scripts/check-downgrade.mjs +0 -472
  204. package/scripts/measure-workspace.mjs +0 -108
@@ -22,6 +22,8 @@ import { Worker } from "node:worker_threads";
22
22
  import { Type } from "@sinclair/typebox";
23
23
 
24
24
  import type { ExtensionAPI } from "./pi.ts";
25
+ import { renameTelegramPathWithRetry } from "./locks.ts";
26
+ import { isProcessAlive } from "./process-identity.ts";
25
27
 
26
28
  const GENERATIVE_APP_NAME = /^[a-z][a-z0-9-]{0,31}$/u;
27
29
  const GENERATIVE_APP_METHOD = /^[a-z][a-z0-9_]{0,31}$/u;
@@ -120,10 +122,10 @@ export interface GenerativeAppLiveSurfaceRuntimeDeps<THandle> {
120
122
  result: GenerativeAppInvocationResult,
121
123
  handle: THandle,
122
124
  ) => GenerativeAppLiveSurfaceFrame<THandle>;
123
- edit: (
124
- frame: GenerativeAppLiveSurfaceFrame<THandle>,
125
- ) => Promise<THandle>;
126
- classifyEditError?: (error: unknown) =>
125
+ edit: (frame: GenerativeAppLiveSurfaceFrame<THandle>) => Promise<THandle>;
126
+ classifyEditError?: (
127
+ error: unknown,
128
+ ) =>
127
129
  | { kind: "retry"; retryAfterMs?: number }
128
130
  | { kind: "terminal" | "unavailable" | "unknown" };
129
131
  recordRuntimeEvent?: (
@@ -131,7 +133,10 @@ export interface GenerativeAppLiveSurfaceRuntimeDeps<THandle> {
131
133
  error: unknown,
132
134
  details?: Record<string, unknown>,
133
135
  ) => void;
134
- setTimer?: (callback: () => void, delayMs: number) => ReturnType<typeof setTimeout>;
136
+ setTimer?: (
137
+ callback: () => void,
138
+ delayMs: number,
139
+ ) => ReturnType<typeof setTimeout>;
135
140
  clearTimer?: (timer: ReturnType<typeof setTimeout>) => void;
136
141
  }
137
142
 
@@ -159,7 +164,8 @@ export interface TelegramBindToolRegistrationDeps extends GenerativeAppRuntimeOp
159
164
  liveSurfaceSetTimer?: GenerativeAppLiveSurfaceRuntimeDeps<unknown>["setTimer"];
160
165
  liveSurfaceClearTimer?: GenerativeAppLiveSurfaceRuntimeDeps<unknown>["clearTimer"];
161
166
  setLiveSurfaceRuntime?: (
162
- runtime: GenerativeAppLiveSurfaceRuntime<TelegramBindLiveHandle> | undefined,
167
+ runtime:
168
+ GenerativeAppLiveSurfaceRuntime<TelegramBindLiveHandle> | undefined,
163
169
  ) => void;
164
170
  isDeliveryHandleCurrent?: (handle: TelegramBindDeliveryHandle) => boolean;
165
171
  editView?: (
@@ -211,21 +217,9 @@ function wait(ms: number): Promise<void> {
211
217
  return new Promise((resolveWait) => setTimeout(resolveWait, ms));
212
218
  }
213
219
 
214
- function isProcessAlive(pid: number): boolean {
215
- if (!Number.isInteger(pid) || pid <= 0) return false;
216
- try {
217
- process.kill(pid, 0);
218
- return true;
219
- } catch (error) {
220
- return (error as NodeJS.ErrnoException).code === "EPERM";
221
- }
222
- }
223
-
224
220
  function assertAppName(app: string): void {
225
221
  if (!GENERATIVE_APP_NAME.test(app)) {
226
- throw new Error(
227
- "Generative App name must match /^[a-z][a-z0-9-]{0,31}$/.",
228
- );
222
+ throw new Error("Generative App name must match /^[a-z][a-z0-9-]{0,31}$/.");
229
223
  }
230
224
  }
231
225
 
@@ -237,7 +231,10 @@ function assertMethod(method: string): void {
237
231
  }
238
232
  }
239
233
 
240
- function assertJsonValue(value: unknown, label: string): GenerativeAppJsonValue {
234
+ function assertJsonValue(
235
+ value: unknown,
236
+ label: string,
237
+ ): GenerativeAppJsonValue {
241
238
  let encoded: string;
242
239
  try {
243
240
  encoded = JSON.stringify(value);
@@ -247,7 +244,9 @@ function assertJsonValue(value: unknown, label: string): GenerativeAppJsonValue
247
244
  if (encoded === undefined) throw new Error(`${label} must be a JSON value.`);
248
245
  const parsed = JSON.parse(encoded) as GenerativeAppJsonValue;
249
246
  if (byteLength(encoded) > GENERATIVE_APP_MAX_STATE_BYTES) {
250
- throw new Error(`${label} exceeds ${GENERATIVE_APP_MAX_STATE_BYTES} bytes.`);
247
+ throw new Error(
248
+ `${label} exceeds ${GENERATIVE_APP_MAX_STATE_BYTES} bytes.`,
249
+ );
251
250
  }
252
251
  return parsed;
253
252
  }
@@ -266,14 +265,19 @@ async function ensureManagedAppsRoot(
266
265
  try {
267
266
  const metadata = await lstat(appsRoot);
268
267
  if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
269
- throw new Error("Generative App root must be a managed non-symlink directory.");
268
+ throw new Error(
269
+ "Generative App root must be a managed non-symlink directory.",
270
+ );
270
271
  }
271
272
  } catch (error) {
272
- if ((error as NodeJS.ErrnoException).code !== "ENOENT" || !create) throw error;
273
+ if ((error as NodeJS.ErrnoException).code !== "ENOENT" || !create)
274
+ throw error;
273
275
  await mkdir(appsRoot, { mode: 0o700 });
274
276
  const metadata = await lstat(appsRoot);
275
277
  if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
276
- throw new Error("Generative App root must be a managed non-symlink directory.");
278
+ throw new Error(
279
+ "Generative App root must be a managed non-symlink directory.",
280
+ );
277
281
  }
278
282
  }
279
283
  return appsRoot;
@@ -295,7 +299,11 @@ async function writeFileAtomic(path: string, content: string): Promise<void> {
295
299
  await mkdir(dirname(path), { recursive: true });
296
300
  const temporary = `${path}.${process.pid}.${randomUUID()}.tmp`;
297
301
  await writeFile(temporary, content, { encoding: "utf8", mode: 0o600 });
298
- await rename(temporary, path);
302
+ // Windows may briefly deny replacing a file another reader or scanner holds; share the bounded retry.
303
+ if (!renameTelegramPathWithRetry(temporary, path))
304
+ throw new Error(
305
+ "Generative App staging file disappeared before publication.",
306
+ );
299
307
  }
300
308
 
301
309
  async function acquireGenerativeAppTransitionLock(
@@ -371,13 +379,15 @@ async function acquireGenerativeAppTransitionLock(
371
379
  } catch {
372
380
  // Re-check ownerless staleness below.
373
381
  }
374
- const currentOwnerlessStale = currentOwnerPid === undefined
375
- ? await stat(lockDir)
376
- .then((metadata) => Date.now() - metadata.mtimeMs > 1_000)
377
- .catch(() => false)
378
- : false;
382
+ const currentOwnerlessStale =
383
+ currentOwnerPid === undefined
384
+ ? await stat(lockDir)
385
+ .then((metadata) => Date.now() - metadata.mtimeMs > 1_000)
386
+ .catch(() => false)
387
+ : false;
379
388
  if (
380
- (currentOwnerPid !== undefined && !isProcessAlive(currentOwnerPid)) ||
389
+ (currentOwnerPid !== undefined &&
390
+ !isProcessAlive(currentOwnerPid)) ||
381
391
  currentOwnerlessStale
382
392
  ) {
383
393
  await rm(lockDir, { recursive: true, force: true });
@@ -388,7 +398,9 @@ async function acquireGenerativeAppTransitionLock(
388
398
  continue;
389
399
  }
390
400
  if (Date.now() >= deadline) {
391
- throw new Error("Generative App transition is busy in another process.");
401
+ throw new Error(
402
+ "Generative App transition is busy in another process.",
403
+ );
392
404
  }
393
405
  await wait(25);
394
406
  }
@@ -398,23 +410,31 @@ async function acquireGenerativeAppTransitionLock(
398
410
  async function assertGenerativeAppModule(path: string): Promise<void> {
399
411
  const metadata = await lstat(path);
400
412
  if (!metadata.isFile() || metadata.isSymbolicLink()) {
401
- throw new Error(`Generative App module is not a regular managed file: ${path}`);
413
+ throw new Error(
414
+ `Generative App module is not a regular managed file: ${path}`,
415
+ );
402
416
  }
403
417
  if (metadata.size > GENERATIVE_APP_MAX_MODULE_BYTES) {
404
- throw new Error(`Generative App module exceeds ${GENERATIVE_APP_MAX_MODULE_BYTES} bytes.`);
418
+ throw new Error(
419
+ `Generative App module exceeds ${GENERATIVE_APP_MAX_MODULE_BYTES} bytes.`,
420
+ );
405
421
  }
406
422
  }
407
423
 
408
424
  async function assertManagedAppDir(appDir: string): Promise<void> {
409
425
  const metadata = await lstat(appDir);
410
426
  if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
411
- throw new Error("Generative App path must be a managed non-symlink directory.");
427
+ throw new Error(
428
+ "Generative App path must be a managed non-symlink directory.",
429
+ );
412
430
  }
413
431
  }
414
432
 
415
433
  async function readGenerativeAppGeneration(appDir: string): Promise<string> {
416
434
  await assertManagedAppDir(appDir);
417
- const generation = (await readFile(join(appDir, "generation"), "utf8")).trim();
435
+ const generation = (
436
+ await readFile(join(appDir, "generation"), "utf8")
437
+ ).trim();
418
438
  if (!/^[0-9a-f]{8}-[0-9a-f-]{27}$/u.test(generation)) {
419
439
  throw new Error("Generative App installation generation is invalid.");
420
440
  }
@@ -430,7 +450,9 @@ function normalizeMethodResult(value: unknown): GenerativeAppMethodResult {
430
450
  throw new Error("Generative App method result.output must be a string.");
431
451
  }
432
452
  if (byteLength(result.output) > GENERATIVE_APP_MAX_OUTPUT_BYTES) {
433
- throw new Error(`Generative App output exceeds ${GENERATIVE_APP_MAX_OUTPUT_BYTES} bytes.`);
453
+ throw new Error(
454
+ `Generative App output exceeds ${GENERATIVE_APP_MAX_OUTPUT_BYTES} bytes.`,
455
+ );
434
456
  }
435
457
  const viewMode = result.viewMode ?? "new";
436
458
  if (viewMode !== "new" && viewMode !== "edit") {
@@ -444,7 +466,9 @@ function normalizeMethodResult(value: unknown): GenerativeAppMethodResult {
444
466
  !Number.isInteger(result.refreshAfterMs) ||
445
467
  result.refreshAfterMs <= 0
446
468
  ) {
447
- throw new Error("Generative App refreshAfterMs must be a finite positive integer.");
469
+ throw new Error(
470
+ "Generative App refreshAfterMs must be a finite positive integer.",
471
+ );
448
472
  }
449
473
  refreshAfterMs = Math.min(
450
474
  GENERATIVE_APP_MAX_REFRESH_AFTER_MS,
@@ -461,7 +485,9 @@ function normalizeMethodResult(value: unknown): GenerativeAppMethodResult {
461
485
  };
462
486
  }
463
487
 
464
- async function readStateTimeline(appDir: string): Promise<GenerativeAppStateEnvelope[]> {
488
+ async function readStateTimeline(
489
+ appDir: string,
490
+ ): Promise<GenerativeAppStateEnvelope[]> {
465
491
  const path = join(appDir, "states.jsonl");
466
492
  let content: string;
467
493
  try {
@@ -483,11 +509,17 @@ async function readStateTimeline(appDir: string): Promise<GenerativeAppStateEnve
483
509
  parsed.revision !== envelopes.length ||
484
510
  typeof parsed.method !== "string" ||
485
511
  !Object.hasOwn(parsed, "state")
486
- ) throw new Error("invalid envelope");
487
- parsed.state = assertJsonValue(parsed.state, "Generative App journal state");
512
+ )
513
+ throw new Error("invalid envelope");
514
+ parsed.state = assertJsonValue(
515
+ parsed.state,
516
+ "Generative App journal state",
517
+ );
488
518
  envelopes.push(parsed);
489
519
  } catch {
490
- const hasLaterContent = lines.slice(index + 1).some((entry) => entry.trim());
520
+ const hasLaterContent = lines
521
+ .slice(index + 1)
522
+ .some((entry) => entry.trim());
491
523
  if (!hasLaterContent) {
492
524
  await writeFileAtomic(
493
525
  path,
@@ -496,7 +528,9 @@ async function readStateTimeline(appDir: string): Promise<GenerativeAppStateEnve
496
528
  );
497
529
  break;
498
530
  }
499
- throw new Error(`Generative App state journal is corrupt at line ${index + 1}.`);
531
+ throw new Error(
532
+ `Generative App state journal is corrupt at line ${index + 1}.`,
533
+ );
500
534
  }
501
535
  }
502
536
  return envelopes;
@@ -537,23 +571,26 @@ async function executeGenerativeAppWorker(options: {
537
571
  options.execution?.assertCurrent();
538
572
  await assertGenerativeAppModule(options.modulePath);
539
573
  return await new Promise((resolveResult, rejectResult) => {
540
- const worker = new Worker(new URL("./generative-app-worker.mjs", import.meta.url), {
541
- execArgv: [],
542
- workerData: {
543
- argument: options.argument,
544
- argumentPresent: options.argument !== undefined,
545
- method: options.method,
546
- methodTimeoutMs: options.methodTimeoutMs,
547
- modulePath: options.modulePath,
548
- app: options.app,
549
- revision: options.revision,
550
- runMaxArgs: GENERATIVE_APP_RUN_MAX_ARGS,
551
- runMaxStreamBytes: GENERATIVE_APP_RUN_MAX_STREAM_BYTES,
552
- runMaxTimeoutMs: GENERATIVE_APP_RUN_MAX_TIMEOUT_MS,
553
- state: options.state,
554
- statePresent: options.state !== undefined,
574
+ const worker = new Worker(
575
+ new URL("./generative-app-worker.mjs", import.meta.url),
576
+ {
577
+ execArgv: [],
578
+ workerData: {
579
+ argument: options.argument,
580
+ argumentPresent: options.argument !== undefined,
581
+ method: options.method,
582
+ methodTimeoutMs: options.methodTimeoutMs,
583
+ modulePath: options.modulePath,
584
+ app: options.app,
585
+ revision: options.revision,
586
+ runMaxArgs: GENERATIVE_APP_RUN_MAX_ARGS,
587
+ runMaxStreamBytes: GENERATIVE_APP_RUN_MAX_STREAM_BYTES,
588
+ runMaxTimeoutMs: GENERATIVE_APP_RUN_MAX_TIMEOUT_MS,
589
+ state: options.state,
590
+ statePresent: options.state !== undefined,
591
+ },
555
592
  },
556
- });
593
+ );
557
594
  let settled = false;
558
595
  let terminationError: Error | undefined;
559
596
  let terminationStarted = false;
@@ -594,22 +631,25 @@ async function executeGenerativeAppWorker(options: {
594
631
  }, options.methodTimeoutMs);
595
632
  timeout.unref?.();
596
633
  options.execution?.signal.addEventListener("abort", abort, { once: true });
597
- worker.on("message", (message: {
598
- error?: string;
599
- ok?: boolean;
600
- result?: unknown;
601
- type?: string;
602
- }) => {
603
- if (terminationError) {
604
- if (message?.type === "abort-ack") terminateAndFinish();
605
- } else if (message?.ok === true) {
606
- finish(undefined, message.result);
607
- void worker.terminate();
608
- } else {
609
- finish(new Error(message?.error || "Generative App worker failed."));
610
- void worker.terminate();
611
- }
612
- });
634
+ worker.on(
635
+ "message",
636
+ (message: {
637
+ error?: string;
638
+ ok?: boolean;
639
+ result?: unknown;
640
+ type?: string;
641
+ }) => {
642
+ if (terminationError) {
643
+ if (message?.type === "abort-ack") terminateAndFinish();
644
+ } else if (message?.ok === true) {
645
+ finish(undefined, message.result);
646
+ void worker.terminate();
647
+ } else {
648
+ finish(new Error(message?.error || "Generative App worker failed."));
649
+ void worker.terminate();
650
+ }
651
+ },
652
+ );
613
653
  worker.once("error", (error) => finish(terminationError ?? error));
614
654
  worker.once("exit", (code) => {
615
655
  if (terminationError) finish(terminationError);
@@ -622,7 +662,10 @@ async function executeGenerativeAppWorker(options: {
622
662
 
623
663
  const invocationQueues = new Map<string, Promise<unknown>>();
624
664
 
625
- function serializeInvocation<T>(key: string, operation: () => Promise<T>): Promise<T> {
665
+ function serializeInvocation<T>(
666
+ key: string,
667
+ operation: () => Promise<T>,
668
+ ): Promise<T> {
626
669
  const previous = invocationQueues.get(key) ?? Promise.resolve();
627
670
  const current = previous.catch(() => undefined).then(operation);
628
671
  invocationQueues.set(key, current);
@@ -697,7 +740,10 @@ async function invokeMethod(options: {
697
740
  const encodedEnvelope = `${JSON.stringify(envelope)}\n`;
698
741
  const encodedState = `${JSON.stringify(result.state, null, 2)}\n`;
699
742
  if (options.method === "init") {
700
- await writeFileAtomic(join(options.appDir, "states.jsonl"), encodedEnvelope);
743
+ await writeFileAtomic(
744
+ join(options.appDir, "states.jsonl"),
745
+ encodedEnvelope,
746
+ );
701
747
  } else {
702
748
  await appendFile(join(options.appDir, "states.jsonl"), encodedEnvelope, {
703
749
  encoding: "utf8",
@@ -721,21 +767,27 @@ async function invokeMethod(options: {
721
767
  };
722
768
  }
723
769
 
724
- export async function invokeGenerativeApp(options: GenerativeAppRuntimeOptions & {
725
- argument?: unknown;
726
- expectedGeneration?: string;
727
- expectedRevision?: number;
728
- method: string;
729
- app: string;
730
- }): Promise<GenerativeAppInvocationResult> {
770
+ export async function invokeGenerativeApp(
771
+ options: GenerativeAppRuntimeOptions & {
772
+ argument?: unknown;
773
+ expectedGeneration?: string;
774
+ expectedRevision?: number;
775
+ method: string;
776
+ app: string;
777
+ },
778
+ ): Promise<GenerativeAppInvocationResult> {
731
779
  assertAppName(options.app);
732
780
  assertMethod(options.method);
733
- const argument = options.argument === undefined
734
- ? undefined
735
- : assertJsonValue(options.argument, "Generative App argument");
781
+ const argument =
782
+ options.argument === undefined
783
+ ? undefined
784
+ : assertJsonValue(options.argument, "Generative App argument");
736
785
  await ensureManagedAppsRoot(options.agentDir, false);
737
786
  const appDir = resolveGenerativeAppDir(options.agentDir, options.app);
738
- const modulePath = resolveGenerativeAppModulePath(options.agentDir, options.app);
787
+ const modulePath = resolveGenerativeAppModulePath(
788
+ options.agentDir,
789
+ options.app,
790
+ );
739
791
  return await serializeInvocation(appDir, async () => {
740
792
  const releaseLock = await acquireGenerativeAppTransitionLock(appDir);
741
793
  try {
@@ -750,7 +802,8 @@ export async function invokeGenerativeApp(options: GenerativeAppRuntimeOptions &
750
802
  ? { expectedRevision: options.expectedRevision }
751
803
  : {}),
752
804
  method: options.method,
753
- methodTimeoutMs: options.methodTimeoutMs ?? GENERATIVE_APP_METHOD_TIMEOUT_MS,
805
+ methodTimeoutMs:
806
+ options.methodTimeoutMs ?? GENERATIVE_APP_METHOD_TIMEOUT_MS,
754
807
  modulePath,
755
808
  app: options.app,
756
809
  });
@@ -760,12 +813,14 @@ export async function invokeGenerativeApp(options: GenerativeAppRuntimeOptions &
760
813
  });
761
814
  }
762
815
 
763
- export async function installGenerativeApp(options: GenerativeAppRuntimeOptions & {
764
- argument?: unknown;
765
- app: string;
766
- replace?: boolean;
767
- script: string;
768
- }): Promise<GenerativeAppInvocationResult> {
816
+ export async function installGenerativeApp(
817
+ options: GenerativeAppRuntimeOptions & {
818
+ argument?: unknown;
819
+ app: string;
820
+ replace?: boolean;
821
+ script: string;
822
+ },
823
+ ): Promise<GenerativeAppInvocationResult> {
769
824
  assertAppName(options.app);
770
825
  const sourcePath = resolve(options.script);
771
826
  if (extname(sourcePath) !== ".mjs") {
@@ -776,10 +831,14 @@ export async function installGenerativeApp(options: GenerativeAppRuntimeOptions
776
831
  }
777
832
  const sourceMetadata = await lstat(sourcePath);
778
833
  if (!sourceMetadata.isFile() || sourceMetadata.isSymbolicLink()) {
779
- throw new Error("Generative App source must be a regular non-symlink file.");
834
+ throw new Error(
835
+ "Generative App source must be a regular non-symlink file.",
836
+ );
780
837
  }
781
838
  if (sourceMetadata.size > GENERATIVE_APP_MAX_MODULE_BYTES) {
782
- throw new Error(`Generative App module exceeds ${GENERATIVE_APP_MAX_MODULE_BYTES} bytes.`);
839
+ throw new Error(
840
+ `Generative App module exceeds ${GENERATIVE_APP_MAX_MODULE_BYTES} bytes.`,
841
+ );
783
842
  }
784
843
  const appsRoot = await ensureManagedAppsRoot(options.agentDir, true);
785
844
  const appDir = resolveGenerativeAppDir(options.agentDir, options.app);
@@ -788,17 +847,23 @@ export async function installGenerativeApp(options: GenerativeAppRuntimeOptions
788
847
  try {
789
848
  const metadata = await lstat(appDir);
790
849
  if (!metadata.isDirectory() || metadata.isSymbolicLink()) {
791
- throw new Error(`Generative App ${options.app} path is not a managed directory.`);
850
+ throw new Error(
851
+ `Generative App ${options.app} path is not a managed directory.`,
852
+ );
792
853
  }
793
854
  installed = true;
794
855
  } catch (error) {
795
856
  if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
796
857
  }
797
858
  if (installed && options.replace !== true) {
798
- throw new Error(`Generative App ${options.app} is already installed; set replace to true.`);
859
+ throw new Error(
860
+ `Generative App ${options.app} is already installed; set replace to true.`,
861
+ );
799
862
  }
800
863
  if (!installed && options.replace === true) {
801
- throw new Error(`Generative App ${options.app} is not installed and cannot be replaced.`);
864
+ throw new Error(
865
+ `Generative App ${options.app} is not installed and cannot be replaced.`,
866
+ );
802
867
  }
803
868
  const releaseLock = installed
804
869
  ? await acquireGenerativeAppTransitionLock(appDir)
@@ -815,15 +880,17 @@ export async function installGenerativeApp(options: GenerativeAppRuntimeOptions
815
880
  encoding: "utf8",
816
881
  mode: 0o600,
817
882
  });
818
- const argument = options.argument === undefined
819
- ? undefined
820
- : assertJsonValue(options.argument, "Generative App argument");
883
+ const argument =
884
+ options.argument === undefined
885
+ ? undefined
886
+ : assertJsonValue(options.argument, "Generative App argument");
821
887
  const result = await invokeMethod({
822
888
  appDir: stagingDir,
823
889
  ...(argument !== undefined ? { argument } : {}),
824
890
  ...(options.execution ? { execution: options.execution } : {}),
825
891
  method: "init",
826
- methodTimeoutMs: options.methodTimeoutMs ?? GENERATIVE_APP_METHOD_TIMEOUT_MS,
892
+ methodTimeoutMs:
893
+ options.methodTimeoutMs ?? GENERATIVE_APP_METHOD_TIMEOUT_MS,
827
894
  modulePath: stagingModule,
828
895
  app: options.app,
829
896
  });
@@ -864,18 +931,22 @@ export function parseGenerativeAppBoundAction(
864
931
  prompt: string,
865
932
  ): GenerativeAppBoundAction | undefined {
866
933
  if (!prompt.includes("::")) return undefined;
867
- const match = /^([a-z][a-z0-9-]{0,31})::([a-z][a-z0-9_]{0,31})(?:\(([\s\S]+)\))?$/u.exec(
868
- prompt,
869
- );
934
+ const match =
935
+ /^([a-z][a-z0-9-]{0,31})::([a-z][a-z0-9_]{0,31})(?:\(([\s\S]+)\))?$/u.exec(
936
+ prompt,
937
+ );
870
938
  if (!match) throw new Error("Malformed Generative App bound action.");
871
939
  const [, app, method, encodedArgument] = match;
872
- if (!app || !method) throw new Error("Malformed Generative App bound action.");
940
+ if (!app || !method)
941
+ throw new Error("Malformed Generative App bound action.");
873
942
  if (encodedArgument === undefined) return { method, app };
874
943
  let argument: unknown;
875
944
  try {
876
945
  argument = JSON.parse(encodedArgument);
877
946
  } catch {
878
- throw new Error("Generative App bound action argument must be strict JSON.");
947
+ throw new Error(
948
+ "Generative App bound action argument must be strict JSON.",
949
+ );
879
950
  }
880
951
  return {
881
952
  argument: assertJsonValue(argument, "Generative App bound action argument"),
@@ -884,37 +955,15 @@ export function parseGenerativeAppBoundAction(
884
955
  };
885
956
  }
886
957
 
887
- export async function invokeGenerativeAppBoundAction(
958
+ export async function bindGenerativeApp(
888
959
  options: GenerativeAppRuntimeOptions & {
889
- expectedGeneration?: string;
890
- expectedRevision?: number;
891
- prompt: string;
960
+ argument?: unknown;
961
+ method?: string;
962
+ app: string;
963
+ replace?: boolean;
964
+ script?: string;
892
965
  },
893
- ): Promise<GenerativeAppInvocationResult | undefined> {
894
- const action = parseGenerativeAppBoundAction(options.prompt);
895
- if (!action) return undefined;
896
- return await invokeGenerativeApp({
897
- agentDir: options.agentDir,
898
- ...(action.argument !== undefined ? { argument: action.argument } : {}),
899
- ...(options.expectedGeneration !== undefined
900
- ? { expectedGeneration: options.expectedGeneration }
901
- : {}),
902
- ...(options.expectedRevision !== undefined
903
- ? { expectedRevision: options.expectedRevision }
904
- : {}),
905
- method: action.method,
906
- methodTimeoutMs: options.methodTimeoutMs,
907
- app: action.app,
908
- });
909
- }
910
-
911
- export async function bindGenerativeApp(options: GenerativeAppRuntimeOptions & {
912
- argument?: unknown;
913
- method?: string;
914
- app: string;
915
- replace?: boolean;
916
- script?: string;
917
- }): Promise<GenerativeAppInvocationResult> {
966
+ ): Promise<GenerativeAppInvocationResult> {
918
967
  const hasScript = typeof options.script === "string";
919
968
  const hasMethod = typeof options.method === "string";
920
969
  if (hasScript === hasMethod) {
@@ -926,7 +975,9 @@ export async function bindGenerativeApp(options: GenerativeAppRuntimeOptions & {
926
975
  return hasScript
927
976
  ? await installGenerativeApp({
928
977
  agentDir: options.agentDir,
929
- ...(options.argument !== undefined ? { argument: options.argument } : {}),
978
+ ...(options.argument !== undefined
979
+ ? { argument: options.argument }
980
+ : {}),
930
981
  methodTimeoutMs: options.methodTimeoutMs,
931
982
  app: options.app,
932
983
  replace: options.replace,
@@ -934,7 +985,9 @@ export async function bindGenerativeApp(options: GenerativeAppRuntimeOptions & {
934
985
  })
935
986
  : await invokeGenerativeApp({
936
987
  agentDir: options.agentDir,
937
- ...(options.argument !== undefined ? { argument: options.argument } : {}),
988
+ ...(options.argument !== undefined
989
+ ? { argument: options.argument }
990
+ : {}),
938
991
  method: options.method!,
939
992
  methodTimeoutMs: options.methodTimeoutMs,
940
993
  app: options.app,
@@ -956,10 +1009,12 @@ export function createGenerativeAppLiveSurfaceRuntime<THandle>(
956
1009
  };
957
1010
  }
958
1011
  const records = new Map<string, Record>();
959
- const setTimer: NonNullable<GenerativeAppLiveSurfaceRuntimeDeps<THandle>["setTimer"]> =
960
- deps.setTimer ?? ((callback, delayMs) => setTimeout(callback, delayMs));
961
- const clearTimer: NonNullable<GenerativeAppLiveSurfaceRuntimeDeps<THandle>["clearTimer"]> =
962
- deps.clearTimer ?? ((timer) => clearTimeout(timer));
1012
+ const setTimer: NonNullable<
1013
+ GenerativeAppLiveSurfaceRuntimeDeps<THandle>["setTimer"]
1014
+ > = deps.setTimer ?? ((callback, delayMs) => setTimeout(callback, delayMs));
1015
+ const clearTimer: NonNullable<
1016
+ GenerativeAppLiveSurfaceRuntimeDeps<THandle>["clearTimer"]
1017
+ > = deps.clearTimer ?? ((timer) => clearTimeout(timer));
963
1018
  let active = true;
964
1019
 
965
1020
  const clearRecordTimer = (record: Record): void => {
@@ -1029,7 +1084,9 @@ export function createGenerativeAppLiveSurfaceRuntime<THandle>(
1029
1084
  record.surface.refreshAfterMs = result.refreshAfterMs;
1030
1085
  schedule(record, result.refreshAfterMs);
1031
1086
  } catch (error) {
1032
- const classification = deps.classifyEditError?.(error) ?? { kind: "unknown" as const };
1087
+ const classification = deps.classifyEditError?.(error) ?? {
1088
+ kind: "unknown" as const,
1089
+ };
1033
1090
  if (!ownsRecord(key, record)) return;
1034
1091
  deps.recordRuntimeEvent?.("generative-app", error, {
1035
1092
  phase: "live-surface-refresh",
@@ -1040,9 +1097,13 @@ export function createGenerativeAppLiveSurfaceRuntime<THandle>(
1040
1097
  cancel(key);
1041
1098
  return;
1042
1099
  }
1043
- const delay = classification.retryAfterMs === undefined
1044
- ? record.retryDelayMs
1045
- : Math.max(GENERATIVE_APP_MIN_REFRESH_AFTER_MS, classification.retryAfterMs);
1100
+ const delay =
1101
+ classification.retryAfterMs === undefined
1102
+ ? record.retryDelayMs
1103
+ : Math.max(
1104
+ GENERATIVE_APP_MIN_REFRESH_AFTER_MS,
1105
+ classification.retryAfterMs,
1106
+ );
1046
1107
  record.retryDelayMs = Math.min(60_000, Math.max(4_000, delay * 2));
1047
1108
  schedule(record, delay);
1048
1109
  } finally {
@@ -1068,10 +1129,15 @@ export function createGenerativeAppLiveSurfaceRuntime<THandle>(
1068
1129
  async resume(surface, result) {
1069
1130
  if (!active || !deps.isCurrent(surface)) return;
1070
1131
  const frame = deps.plan(result, surface.handle);
1071
- const handle = frame.digest === surface.initialDigest
1072
- ? surface.handle
1073
- : await deps.edit(frame);
1074
- if (result.refreshAfterMs === undefined || !deps.isCurrent({ ...surface, handle })) return;
1132
+ const handle =
1133
+ frame.digest === surface.initialDigest
1134
+ ? surface.handle
1135
+ : await deps.edit(frame);
1136
+ if (
1137
+ result.refreshAfterMs === undefined ||
1138
+ !deps.isCurrent({ ...surface, handle })
1139
+ )
1140
+ return;
1075
1141
  open({
1076
1142
  ...surface,
1077
1143
  appGeneration: result.generation,
@@ -1100,7 +1166,9 @@ export function formatDisplayedGenerativeAppToolOutput(): string {
1100
1166
 
1101
1167
  export function formatGenerativeAppToolError(error: unknown): Error {
1102
1168
  const message = error instanceof Error ? error.message : String(error);
1103
- return new Error(`\n${message.replace(/^\n+/u, "") || "Generative App operation failed."}`);
1169
+ return new Error(
1170
+ `\n${message.replace(/^\n+/u, "") || "Generative App operation failed."}`,
1171
+ );
1104
1172
  }
1105
1173
 
1106
1174
  // Provider tool APIs reject recursive $ref schemas (OpenAI) and raw TypeBox
@@ -1139,53 +1207,73 @@ export function registerTelegramBindTool(
1139
1207
  const view = {
1140
1208
  text: planned.markdown,
1141
1209
  parseMode: "markdown" as const,
1142
- ...(planned.replyMarkup !== undefined ? { replyMarkup: planned.replyMarkup } : {}),
1210
+ ...(planned.replyMarkup !== undefined
1211
+ ? { replyMarkup: planned.replyMarkup }
1212
+ : {}),
1143
1213
  };
1144
1214
  return {
1145
1215
  digest: createHash("sha256").update(JSON.stringify(view)).digest("hex"),
1146
1216
  handle: { delivery: handle.delivery, view },
1147
1217
  };
1148
1218
  };
1149
- const liveSurfaces = deps.planOutput && deps.editView && deps.isDeliveryHandleCurrent
1150
- ? createGenerativeAppLiveSurfaceRuntime<TelegramBindLiveHandle>({
1151
- agentDir: deps.agentDir,
1152
- isCurrent: (surface) => deps.isDeliveryHandleCurrent!(surface.handle.delivery),
1153
- plan: planFrame,
1154
- async edit(frame) {
1155
- const view = frame.handle.view;
1156
- if (!view) throw new Error("Generative App live frame is missing its planned view.");
1157
- const edited = await deps.editView!(frame.handle.delivery, view);
1158
- if (!edited.ok) throw Object.assign(new Error(edited.message), {
1159
- deliveryFailureReason: edited.reason,
1160
- ...(edited.retryAfterMs === undefined ? {} : { retryAfterMs: edited.retryAfterMs }),
1161
- });
1162
- return { delivery: edited.value, view };
1163
- },
1164
- classifyEditError(error) {
1165
- const failure = error as {
1166
- deliveryFailureReason?: string;
1167
- retryAfterMs?: number;
1168
- };
1169
- if (failure.deliveryFailureReason === "rate-limited") {
1170
- return { kind: "retry", retryAfterMs: failure.retryAfterMs };
1171
- }
1172
- if (failure.deliveryFailureReason === "transport-retryable") {
1173
- return { kind: "retry" };
1174
- }
1175
- if (failure.deliveryFailureReason === "message-unavailable") {
1176
- return { kind: "unavailable" };
1177
- }
1178
- return {
1179
- kind: failure.deliveryFailureReason === "commit-unknown" ? "unknown" : "terminal",
1180
- };
1181
- },
1182
- recordRuntimeEvent: deps.recordRuntimeEvent,
1183
- ...(deps.liveSurfaceSetTimer ? { setTimer: deps.liveSurfaceSetTimer } : {}),
1184
- ...(deps.liveSurfaceClearTimer ? { clearTimer: deps.liveSurfaceClearTimer } : {}),
1185
- })
1186
- : undefined;
1219
+ const liveSurfaces =
1220
+ deps.planOutput && deps.editView && deps.isDeliveryHandleCurrent
1221
+ ? createGenerativeAppLiveSurfaceRuntime<TelegramBindLiveHandle>({
1222
+ agentDir: deps.agentDir,
1223
+ isCurrent: (surface) =>
1224
+ deps.isDeliveryHandleCurrent!(surface.handle.delivery),
1225
+ plan: planFrame,
1226
+ async edit(frame) {
1227
+ const view = frame.handle.view;
1228
+ if (!view)
1229
+ throw new Error(
1230
+ "Generative App live frame is missing its planned view.",
1231
+ );
1232
+ const edited = await deps.editView!(frame.handle.delivery, view);
1233
+ if (!edited.ok)
1234
+ throw Object.assign(new Error(edited.message), {
1235
+ deliveryFailureReason: edited.reason,
1236
+ ...(edited.retryAfterMs === undefined
1237
+ ? {}
1238
+ : { retryAfterMs: edited.retryAfterMs }),
1239
+ });
1240
+ return { delivery: edited.value, view };
1241
+ },
1242
+ classifyEditError(error) {
1243
+ const failure = error as {
1244
+ deliveryFailureReason?: string;
1245
+ retryAfterMs?: number;
1246
+ };
1247
+ if (failure.deliveryFailureReason === "rate-limited") {
1248
+ return { kind: "retry", retryAfterMs: failure.retryAfterMs };
1249
+ }
1250
+ if (failure.deliveryFailureReason === "transport-retryable") {
1251
+ return { kind: "retry" };
1252
+ }
1253
+ if (failure.deliveryFailureReason === "message-unavailable") {
1254
+ return { kind: "unavailable" };
1255
+ }
1256
+ return {
1257
+ kind:
1258
+ failure.deliveryFailureReason === "commit-unknown"
1259
+ ? "unknown"
1260
+ : "terminal",
1261
+ };
1262
+ },
1263
+ recordRuntimeEvent: deps.recordRuntimeEvent,
1264
+ ...(deps.liveSurfaceSetTimer
1265
+ ? { setTimer: deps.liveSurfaceSetTimer }
1266
+ : {}),
1267
+ ...(deps.liveSurfaceClearTimer
1268
+ ? { clearTimer: deps.liveSurfaceClearTimer }
1269
+ : {}),
1270
+ })
1271
+ : undefined;
1187
1272
  deps.setLiveSurfaceRuntime?.(liveSurfaces);
1188
- const surfaceKey = (app: string, handle: TelegramBindDeliveryHandle): string =>
1273
+ const surfaceKey = (
1274
+ app: string,
1275
+ handle: TelegramBindDeliveryHandle,
1276
+ ): string =>
1189
1277
  getTelegramBindLiveSurfaceKey(
1190
1278
  app,
1191
1279
  deps.getActiveProfileName?.() ?? "default",
@@ -1196,21 +1284,28 @@ export function registerTelegramBindTool(
1196
1284
  label: "Telegram Bind",
1197
1285
  description:
1198
1286
  "Install, explicitly replace, or invoke one named method on a managed Generative App; successful output displays directly in the active Telegram turn unless display is false.",
1199
- parameters: Type.Object({
1200
- app: Type.String(),
1201
- script: Type.Optional(Type.String()),
1202
- method: Type.Optional(Type.String()),
1203
- replace: Type.Optional(Type.Boolean()),
1204
- display: Type.Optional(Type.Boolean()),
1205
- argument: Type.Optional(
1206
- createTelegramBindJsonArgumentSchema(TELEGRAM_BIND_JSON_ARGUMENT_MAX_DEPTH),
1207
- ),
1208
- }, { additionalProperties: false }),
1287
+ parameters: Type.Object(
1288
+ {
1289
+ app: Type.String(),
1290
+ script: Type.Optional(Type.String()),
1291
+ method: Type.Optional(Type.String()),
1292
+ replace: Type.Optional(Type.Boolean()),
1293
+ display: Type.Optional(Type.Boolean()),
1294
+ argument: Type.Optional(
1295
+ createTelegramBindJsonArgumentSchema(
1296
+ TELEGRAM_BIND_JSON_ARGUMENT_MAX_DEPTH,
1297
+ ),
1298
+ ),
1299
+ },
1300
+ { additionalProperties: false },
1301
+ ),
1209
1302
  async execute(_toolCallId, params) {
1210
1303
  try {
1211
1304
  const result = await bindGenerativeApp({
1212
1305
  agentDir: deps.agentDir,
1213
- ...(params.argument !== undefined ? { argument: params.argument } : {}),
1306
+ ...(params.argument !== undefined
1307
+ ? { argument: params.argument }
1308
+ : {}),
1214
1309
  ...("method" in params && typeof params.method === "string"
1215
1310
  ? { method: params.method }
1216
1311
  : {}),
@@ -1223,10 +1318,13 @@ export function registerTelegramBindTool(
1223
1318
  : {}),
1224
1319
  methodTimeoutMs: deps.methodTimeoutMs,
1225
1320
  });
1226
- const activeTurn = params.display === false
1227
- ? undefined
1228
- : deps.getActiveTurn?.();
1229
- if (activeTurn && deps.planOutput && (deps.sendView || deps.sendMarkdownReply)) {
1321
+ const activeTurn =
1322
+ params.display === false ? undefined : deps.getActiveTurn?.();
1323
+ if (
1324
+ activeTurn &&
1325
+ deps.planOutput &&
1326
+ (deps.sendView || deps.sendMarkdownReply)
1327
+ ) {
1230
1328
  try {
1231
1329
  const planned = deps.planOutput(result.output, {
1232
1330
  binding: {
@@ -1284,7 +1382,12 @@ export function registerTelegramBindTool(
1284
1382
  );
1285
1383
  }
1286
1384
  return {
1287
- content: [{ type: "text", text: formatDisplayedGenerativeAppToolOutput() }],
1385
+ content: [
1386
+ {
1387
+ type: "text",
1388
+ text: formatDisplayedGenerativeAppToolOutput(),
1389
+ },
1390
+ ],
1288
1391
  details: { ...result, displayed: true, messageId },
1289
1392
  };
1290
1393
  } catch (error) {
@@ -1294,13 +1397,23 @@ export function registerTelegramBindTool(
1294
1397
  method: result.method,
1295
1398
  });
1296
1399
  return {
1297
- content: [{ type: "text", text: formatGenerativeAppToolOutput(result.output) }],
1400
+ content: [
1401
+ {
1402
+ type: "text",
1403
+ text: formatGenerativeAppToolOutput(result.output),
1404
+ },
1405
+ ],
1298
1406
  details: { ...result, displayed: false, displayFailed: true },
1299
1407
  };
1300
1408
  }
1301
1409
  }
1302
1410
  return {
1303
- content: [{ type: "text", text: formatGenerativeAppToolOutput(result.output) }],
1411
+ content: [
1412
+ {
1413
+ type: "text",
1414
+ text: formatGenerativeAppToolOutput(result.output),
1415
+ },
1416
+ ],
1304
1417
  details: { ...result, displayed: false },
1305
1418
  };
1306
1419
  } catch (error) {