@llblab/pi-kit 0.29.1 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. package/BACKLOG.md +3 -1
  2. package/CHANGELOG.md +10 -0
  3. package/README.md +4 -4
  4. package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +12 -0
  5. package/node_modules/@llblab/pi-state-flow/README.md +2 -2
  6. package/node_modules/@llblab/pi-state-flow/dist/lib/context.d.ts +2 -2
  7. package/node_modules/@llblab/pi-state-flow/dist/lib/context.js +17 -8
  8. package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +7 -3
  9. package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.d.ts +2 -1
  10. package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.js +3 -2
  11. package/node_modules/@llblab/pi-state-flow/dist/package.json +1 -1
  12. package/node_modules/@llblab/pi-state-flow/docs/architecture.md +3 -3
  13. package/node_modules/@llblab/pi-state-flow/docs/usage.md +3 -2
  14. package/node_modules/@llblab/pi-state-flow/lib/context.ts +17 -8
  15. package/node_modules/@llblab/pi-state-flow/lib/extension.ts +6 -3
  16. package/node_modules/@llblab/pi-state-flow/lib/protocol.ts +4 -2
  17. package/node_modules/@llblab/pi-state-flow/package.json +1 -1
  18. package/node_modules/@llblab/pi-telegram/AGENTS.md +13 -10
  19. package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -1
  20. package/node_modules/@llblab/pi-telegram/CHANGELOG.md +23 -0
  21. package/node_modules/@llblab/pi-telegram/README.md +10 -11
  22. package/node_modules/@llblab/pi-telegram/api/commands.ts +3 -0
  23. package/node_modules/@llblab/pi-telegram/dist/api/commands.d.ts +1 -1
  24. package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.d.ts +1 -5
  25. package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.js +15 -42
  26. package/node_modules/@llblab/pi-telegram/dist/lib/activity.d.ts +9 -0
  27. package/node_modules/@llblab/pi-telegram/dist/lib/activity.js +39 -10
  28. package/node_modules/@llblab/pi-telegram/dist/lib/agent-messages.js +7 -3
  29. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +43 -5
  30. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +164 -20
  31. package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.d.ts +12 -0
  32. package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.js +181 -77
  33. package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.d.ts +111 -31
  34. package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.js +1324 -359
  35. package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.d.ts +41 -91
  36. package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.js +969 -637
  37. package/node_modules/@llblab/pi-telegram/dist/lib/bus-transport.js +16 -9
  38. package/node_modules/@llblab/pi-telegram/dist/lib/bus.d.ts +215 -18
  39. package/node_modules/@llblab/pi-telegram/dist/lib/bus.js +1048 -327
  40. package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.d.ts +5 -0
  41. package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.js +434 -261
  42. package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.d.ts +26 -5
  43. package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.js +92 -176
  44. package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +168 -82
  45. package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +1409 -291
  46. package/node_modules/@llblab/pi-telegram/dist/lib/config.d.ts +1 -8
  47. package/node_modules/@llblab/pi-telegram/dist/lib/config.js +34 -76
  48. package/node_modules/@llblab/pi-telegram/dist/lib/delivery.d.ts +2 -2
  49. package/node_modules/@llblab/pi-telegram/dist/lib/delivery.js +16 -16
  50. package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +642 -170
  51. package/node_modules/@llblab/pi-telegram/dist/lib/generative-app-worker.mjs +19 -7
  52. package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.d.ts +0 -5
  53. package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.js +61 -49
  54. package/node_modules/@llblab/pi-telegram/dist/lib/inbound.d.ts +5 -0
  55. package/node_modules/@llblab/pi-telegram/dist/lib/inbound.js +35 -59
  56. package/node_modules/@llblab/pi-telegram/dist/lib/journal.d.ts +40 -4
  57. package/node_modules/@llblab/pi-telegram/dist/lib/journal.js +2102 -760
  58. package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.d.ts +2 -6
  59. package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.js +75 -58
  60. package/node_modules/@llblab/pi-telegram/dist/lib/locks.d.ts +34 -3
  61. package/node_modules/@llblab/pi-telegram/dist/lib/locks.js +318 -119
  62. package/node_modules/@llblab/pi-telegram/dist/lib/logging.js +50 -14
  63. package/node_modules/@llblab/pi-telegram/dist/lib/media.d.ts +22 -1
  64. package/node_modules/@llblab/pi-telegram/dist/lib/media.js +46 -28
  65. package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.d.ts +19 -5
  66. package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.js +53 -11
  67. package/node_modules/@llblab/pi-telegram/dist/lib/menu-queue.d.ts +5 -4
  68. package/node_modules/@llblab/pi-telegram/dist/lib/menu-queue.js +45 -63
  69. package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.d.ts +5 -4
  70. package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.js +114 -78
  71. package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.d.ts +1 -1
  72. package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.js +8 -25
  73. package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.d.ts +6 -1
  74. package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.js +14 -21
  75. package/node_modules/@llblab/pi-telegram/dist/lib/menu.d.ts +8 -61
  76. package/node_modules/@llblab/pi-telegram/dist/lib/menu.js +92 -164
  77. package/node_modules/@llblab/pi-telegram/dist/lib/model.d.ts +1 -1
  78. package/node_modules/@llblab/pi-telegram/dist/lib/model.js +2 -16
  79. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.d.ts +6 -27
  80. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.js +45 -38
  81. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-buttons.js +22 -16
  82. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.d.ts +0 -2
  83. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.js +51 -64
  84. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-voice.js +6 -3
  85. package/node_modules/@llblab/pi-telegram/dist/lib/outbound.d.ts +6 -6
  86. package/node_modules/@llblab/pi-telegram/dist/lib/outbound.js +61 -51
  87. package/node_modules/@llblab/pi-telegram/dist/lib/ownership.js +6 -2
  88. package/node_modules/@llblab/pi-telegram/dist/lib/paths.d.ts +5 -0
  89. package/node_modules/@llblab/pi-telegram/dist/lib/paths.js +37 -21
  90. package/node_modules/@llblab/pi-telegram/dist/lib/pi.d.ts +2 -0
  91. package/node_modules/@llblab/pi-telegram/dist/lib/pi.js +8 -1
  92. package/node_modules/@llblab/pi-telegram/dist/lib/polling.d.ts +23 -37
  93. package/node_modules/@llblab/pi-telegram/dist/lib/polling.js +71 -52
  94. package/node_modules/@llblab/pi-telegram/dist/lib/preview.js +39 -19
  95. package/node_modules/@llblab/pi-telegram/dist/lib/process-identity.d.ts +24 -0
  96. package/node_modules/@llblab/pi-telegram/dist/lib/process-identity.js +133 -0
  97. package/node_modules/@llblab/pi-telegram/dist/lib/prompts.js +2 -7
  98. package/node_modules/@llblab/pi-telegram/dist/lib/queue.d.ts +16 -16
  99. package/node_modules/@llblab/pi-telegram/dist/lib/queue.js +261 -116
  100. package/node_modules/@llblab/pi-telegram/dist/lib/recovery.d.ts +2 -83
  101. package/node_modules/@llblab/pi-telegram/dist/lib/recovery.js +20 -274
  102. package/node_modules/@llblab/pi-telegram/dist/lib/replies.d.ts +16 -24
  103. package/node_modules/@llblab/pi-telegram/dist/lib/replies.js +99 -116
  104. package/node_modules/@llblab/pi-telegram/dist/lib/routing.d.ts +121 -7
  105. package/node_modules/@llblab/pi-telegram/dist/lib/routing.js +4750 -1161
  106. package/node_modules/@llblab/pi-telegram/dist/lib/runtime.d.ts +1 -11
  107. package/node_modules/@llblab/pi-telegram/dist/lib/sections.d.ts +4 -29
  108. package/node_modules/@llblab/pi-telegram/dist/lib/sections.js +7 -20
  109. package/node_modules/@llblab/pi-telegram/dist/lib/skills.js +4 -3
  110. package/node_modules/@llblab/pi-telegram/dist/lib/status.js +86 -57
  111. package/node_modules/@llblab/pi-telegram/dist/lib/sync.d.ts +6 -3
  112. package/node_modules/@llblab/pi-telegram/dist/lib/sync.js +112 -45
  113. package/node_modules/@llblab/pi-telegram/dist/lib/target.d.ts +4 -0
  114. package/node_modules/@llblab/pi-telegram/dist/lib/target.js +18 -0
  115. package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.d.ts +18 -11
  116. package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.js +165 -49
  117. package/node_modules/@llblab/pi-telegram/dist/lib/text-groups.js +4 -39
  118. package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.d.ts +6 -27
  119. package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.js +369 -186
  120. package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.d.ts +1 -2
  121. package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.js +58 -38
  122. package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.d.ts +28 -8
  123. package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.js +175 -66
  124. package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.d.ts +42 -16
  125. package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.js +90 -50
  126. package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +88 -24
  127. package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +2276 -842
  128. package/node_modules/@llblab/pi-telegram/dist/lib/time-injection.js +1 -2
  129. package/node_modules/@llblab/pi-telegram/dist/lib/turns.js +1 -19
  130. package/node_modules/@llblab/pi-telegram/dist/lib/updates.d.ts +178 -82
  131. package/node_modules/@llblab/pi-telegram/dist/lib/updates.js +2438 -645
  132. package/node_modules/@llblab/pi-telegram/dist/lib/voice.d.ts +7 -22
  133. package/node_modules/@llblab/pi-telegram/dist/lib/voice.js +7 -22
  134. package/node_modules/@llblab/pi-telegram/dist/lib/wire.js +1 -1
  135. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.d.ts +3 -1
  136. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.js +425 -211
  137. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-identity.js +8 -13
  138. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.d.ts +1 -1
  139. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.js +328 -140
  140. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-slots.js +8 -4
  141. package/node_modules/@llblab/pi-telegram/dist/package.json +1 -1
  142. package/node_modules/@llblab/pi-telegram/docs/architecture.md +43 -35
  143. package/node_modules/@llblab/pi-telegram/docs/inbound.md +1 -1
  144. package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +15 -3
  145. package/node_modules/@llblab/pi-telegram/docs/outbound.md +1 -1
  146. package/node_modules/@llblab/pi-telegram/docs/public-api.md +29 -1
  147. package/node_modules/@llblab/pi-telegram/docs/ui-style.md +25 -8
  148. package/node_modules/@llblab/pi-telegram/lib/activity-verbosity.ts +59 -82
  149. package/node_modules/@llblab/pi-telegram/lib/activity.ts +146 -102
  150. package/node_modules/@llblab/pi-telegram/lib/agent-messages.ts +12 -5
  151. package/node_modules/@llblab/pi-telegram/lib/bindings.ts +390 -82
  152. package/node_modules/@llblab/pi-telegram/lib/bus-api.ts +262 -99
  153. package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +2440 -601
  154. package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +2209 -1290
  155. package/node_modules/@llblab/pi-telegram/lib/bus-transport.ts +46 -12
  156. package/node_modules/@llblab/pi-telegram/lib/bus.ts +1673 -444
  157. package/node_modules/@llblab/pi-telegram/lib/channel-posts.ts +848 -327
  158. package/node_modules/@llblab/pi-telegram/lib/command-templates.ts +152 -265
  159. package/node_modules/@llblab/pi-telegram/lib/commands.ts +2398 -555
  160. package/node_modules/@llblab/pi-telegram/lib/config.ts +177 -170
  161. package/node_modules/@llblab/pi-telegram/lib/delivery.ts +41 -29
  162. package/node_modules/@llblab/pi-telegram/lib/extension.ts +1209 -431
  163. package/node_modules/@llblab/pi-telegram/lib/generative-app-worker.mjs +19 -7
  164. package/node_modules/@llblab/pi-telegram/lib/generative-apps.ts +324 -211
  165. package/node_modules/@llblab/pi-telegram/lib/inbound.ts +95 -97
  166. package/node_modules/@llblab/pi-telegram/lib/journal.ts +5329 -1712
  167. package/node_modules/@llblab/pi-telegram/lib/keyboard.ts +1 -3
  168. package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +128 -78
  169. package/node_modules/@llblab/pi-telegram/lib/locks.ts +703 -212
  170. package/node_modules/@llblab/pi-telegram/lib/logging.ts +147 -58
  171. package/node_modules/@llblab/pi-telegram/lib/media.ts +82 -43
  172. package/node_modules/@llblab/pi-telegram/lib/menu-model.ts +85 -18
  173. package/node_modules/@llblab/pi-telegram/lib/menu-queue.ts +88 -129
  174. package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +260 -155
  175. package/node_modules/@llblab/pi-telegram/lib/menu-status.ts +20 -60
  176. package/node_modules/@llblab/pi-telegram/lib/menu-thinking.ts +41 -56
  177. package/node_modules/@llblab/pi-telegram/lib/menu.ts +112 -285
  178. package/node_modules/@llblab/pi-telegram/lib/model.ts +8 -22
  179. package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +156 -125
  180. package/node_modules/@llblab/pi-telegram/lib/outbound-buttons.ts +76 -49
  181. package/node_modules/@llblab/pi-telegram/lib/outbound-markup.ts +96 -94
  182. package/node_modules/@llblab/pi-telegram/lib/outbound-voice.ts +33 -27
  183. package/node_modules/@llblab/pi-telegram/lib/outbound.ts +125 -125
  184. package/node_modules/@llblab/pi-telegram/lib/ownership.ts +19 -10
  185. package/node_modules/@llblab/pi-telegram/lib/paths.ts +148 -49
  186. package/node_modules/@llblab/pi-telegram/lib/pi.ts +35 -15
  187. package/node_modules/@llblab/pi-telegram/lib/polling.ts +165 -149
  188. package/node_modules/@llblab/pi-telegram/lib/preview.ts +93 -40
  189. package/node_modules/@llblab/pi-telegram/lib/process-identity.ts +164 -0
  190. package/node_modules/@llblab/pi-telegram/lib/prompts.ts +4 -11
  191. package/node_modules/@llblab/pi-telegram/lib/queue.ts +523 -289
  192. package/node_modules/@llblab/pi-telegram/lib/recovery.ts +50 -454
  193. package/node_modules/@llblab/pi-telegram/lib/rendering.ts +1 -3
  194. package/node_modules/@llblab/pi-telegram/lib/replies.ts +248 -209
  195. package/node_modules/@llblab/pi-telegram/lib/routing.ts +8527 -2149
  196. package/node_modules/@llblab/pi-telegram/lib/runtime.ts +1 -12
  197. package/node_modules/@llblab/pi-telegram/lib/sections.ts +24 -89
  198. package/node_modules/@llblab/pi-telegram/lib/setup.ts +1 -3
  199. package/node_modules/@llblab/pi-telegram/lib/skills.ts +9 -4
  200. package/node_modules/@llblab/pi-telegram/lib/status.ts +215 -110
  201. package/node_modules/@llblab/pi-telegram/lib/sync.ts +488 -359
  202. package/node_modules/@llblab/pi-telegram/lib/target.ts +22 -3
  203. package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +322 -122
  204. package/node_modules/@llblab/pi-telegram/lib/text-groups.ts +5 -39
  205. package/node_modules/@llblab/pi-telegram/lib/thread-cleanup-manager.ts +833 -331
  206. package/node_modules/@llblab/pi-telegram/lib/thread-display.ts +213 -97
  207. package/node_modules/@llblab/pi-telegram/lib/thread-naming.ts +232 -87
  208. package/node_modules/@llblab/pi-telegram/lib/thread-reconciler.ts +155 -72
  209. package/node_modules/@llblab/pi-telegram/lib/threads.ts +5329 -1817
  210. package/node_modules/@llblab/pi-telegram/lib/time-injection.ts +1 -4
  211. package/node_modules/@llblab/pi-telegram/lib/turns.ts +15 -28
  212. package/node_modules/@llblab/pi-telegram/lib/updates.ts +5194 -1600
  213. package/node_modules/@llblab/pi-telegram/lib/voice.ts +7 -35
  214. package/node_modules/@llblab/pi-telegram/lib/wire.ts +1 -1
  215. package/node_modules/@llblab/pi-telegram/lib/workspace-admission.ts +996 -416
  216. package/node_modules/@llblab/pi-telegram/lib/workspace-identity.ts +14 -17
  217. package/node_modules/@llblab/pi-telegram/lib/workspace-retirement.ts +878 -376
  218. package/node_modules/@llblab/pi-telegram/lib/workspace-slots.ts +22 -13
  219. package/node_modules/@llblab/pi-telegram/package.json +1 -1
  220. package/package.json +3 -3
  221. package/node_modules/@llblab/pi-telegram/scripts/check-downgrade.mjs +0 -472
  222. package/node_modules/@llblab/pi-telegram/scripts/measure-workspace.mjs +0 -108
@@ -152,9 +152,7 @@ export function replaceTopLevelHtmlComments(
152
152
  return result + markdown.slice(offset);
153
153
  }
154
154
 
155
- function findTopLevelOpenOrPartialHtmlCommentIndex(
156
- markdown: string,
157
- ): number {
155
+ function findTopLevelOpenOrPartialHtmlCommentIndex(markdown: string): number {
158
156
  const { openCommentStart } = collectTopLevelHtmlComments(markdown);
159
157
  if (openCommentStart !== undefined) return openCommentStart;
160
158
  let offset = 0;
@@ -231,7 +229,9 @@ function parseTelegramJsonObjectCandidate(
231
229
  source: string,
232
230
  ): Record<string, unknown> | undefined {
233
231
  const normalized = removeTelegramJsonTrailingCommas(source);
234
- for (const candidate of normalized === source ? [source] : [source, normalized]) {
232
+ for (const candidate of normalized === source
233
+ ? [source]
234
+ : [source, normalized]) {
235
235
  try {
236
236
  const value: unknown = JSON.parse(candidate);
237
237
  if (isTelegramActionPayload(value)) return value;
@@ -249,41 +249,62 @@ function looksLikeTelegramNamedJsonObject(
249
249
  return /^\{\s*"(?:[^"\\]|\\.)*"\s*:/u.test(source.slice(offset));
250
250
  }
251
251
 
252
- function parseTelegramActionPayload(
252
+ /** Blank one structured span so tolerant attribute parsing never reads keys inside rejected JSON or matrices. */
253
+ function blankTelegramActionSpan(
254
+ text: string,
255
+ start: number,
256
+ end: number,
257
+ ): string {
258
+ return `${text.slice(0, start)}${" ".repeat(end - start)}${text.slice(end)}`;
259
+ }
260
+
261
+ /**
262
+ * Scans one action comment for its first accepted structured payload. Otherwise returns the attribute envelope
263
+ * with every structured span blanked; implausible `[` starts are skipped without a parse attempt.
264
+ */
265
+ function scanTelegramActionComment<T>(
253
266
  comment: TelegramTopLevelHtmlComment,
254
267
  command: string,
255
- ): Record<string, unknown> | undefined {
268
+ parseStructured: (content: string, offset: number) => T | undefined,
269
+ ): { payload: T } | { attributeEnvelope: string } | undefined {
256
270
  let content = comment.content.replace(/^\s+/, "").replace(/^!/, "");
257
271
  if (!content.startsWith(command)) return undefined;
258
272
  content = content.slice(command.length);
259
273
  let attributeEnvelope = content;
260
274
  for (let offset = 0; offset < content.length; offset += 1) {
261
- if (content[offset] !== "{" && content[offset] !== "[") continue;
262
- if (
263
- content[offset] === "[" &&
264
- !isPlausibleTelegramMatrixStart(content, offset)
265
- ) {
266
- const noiseEnd = findTelegramStructuredPayloadEnd(content, offset);
267
- if (noiseEnd !== undefined) {
268
- attributeEnvelope = `${attributeEnvelope.slice(0, offset)}${" ".repeat(noiseEnd - offset)}${attributeEnvelope.slice(noiseEnd)}`;
269
- offset = noiseEnd - 1;
270
- }
271
- continue;
272
- }
273
- if (content[offset] === "{") {
274
- const parsed = parseTelegramAdaptiveActionPayloadRows(
275
- content.slice(offset),
276
- parseTelegramVoiceCompactActionPayload,
277
- { allowTrailing: true },
278
- );
279
- if (parsed) return parsed.rows[0]![0];
275
+ const char = content[offset];
276
+ if (char !== "{" && char !== "[") continue;
277
+ if (char === "{" || isPlausibleTelegramMatrixStart(content, offset)) {
278
+ const payload = parseStructured(content, offset);
279
+ if (payload !== undefined) return { payload };
280
280
  }
281
281
  const end = findTelegramStructuredPayloadEnd(content, offset);
282
282
  if (end === undefined) continue;
283
- attributeEnvelope = `${attributeEnvelope.slice(0, offset)}${" ".repeat(end - offset)}${attributeEnvelope.slice(end)}`;
283
+ attributeEnvelope = blankTelegramActionSpan(attributeEnvelope, offset, end);
284
284
  offset = end - 1;
285
285
  }
286
- return parseTolerantTelegramAttributes(attributeEnvelope, [
286
+ return { attributeEnvelope };
287
+ }
288
+
289
+ function parseTelegramActionPayload(
290
+ comment: TelegramTopLevelHtmlComment,
291
+ command: string,
292
+ ): Record<string, unknown> | undefined {
293
+ const scanned = scanTelegramActionComment(
294
+ comment,
295
+ command,
296
+ (content, offset) =>
297
+ content[offset] === "{"
298
+ ? parseTelegramAdaptiveActionPayloadRows(
299
+ content.slice(offset),
300
+ parseTelegramVoiceCompactActionPayload,
301
+ { allowTrailing: true },
302
+ )?.rows[0]![0]
303
+ : undefined,
304
+ );
305
+ if (!scanned) return undefined;
306
+ if ("payload" in scanned) return scanned.payload;
307
+ return parseTolerantTelegramAttributes(scanned.attributeEnvelope, [
287
308
  "text",
288
309
  "value",
289
310
  "lang",
@@ -311,8 +332,14 @@ function parseTelegramButtonCompactActionPayload(
311
332
  selectedStyle !== "success" &&
312
333
  selectedStyle !== "danger" &&
313
334
  !(atoms.length === 4 && selectedStyle === "")
314
- ) return undefined;
315
- if (atoms.length === 4 && !isDisabled && disabled !== "0" && disabled !== "false") {
335
+ )
336
+ return undefined;
337
+ if (
338
+ atoms.length === 4 &&
339
+ !isDisabled &&
340
+ disabled !== "0" &&
341
+ disabled !== "false"
342
+ ) {
316
343
  return undefined;
317
344
  }
318
345
  return {
@@ -377,16 +404,15 @@ function parseTelegramAdaptiveActionPayloadRows(
377
404
  continue;
378
405
  }
379
406
  if (character === "|") {
380
- if (atomSources.length >= (options.maxCompactAtoms ?? 3)) return undefined;
407
+ if (atomSources.length >= (options.maxCompactAtoms ?? 3))
408
+ return undefined;
381
409
  atomSources.push([]);
382
410
  offset += 1;
383
411
  continue;
384
412
  }
385
413
  if (character === "}") {
386
414
  offset += 1;
387
- const atoms = atomSources.map((atom) =>
388
- normalizeAtom(atom.join("")),
389
- );
415
+ const atoms = atomSources.map((atom) => normalizeAtom(atom.join("")));
390
416
  if (atoms.some((atom) => atom === undefined)) return undefined;
391
417
  return parseCompactPayload(atoms as string[]);
392
418
  }
@@ -422,7 +448,8 @@ function parseTelegramAdaptiveActionPayloadRows(
422
448
  if (
423
449
  (character === "}" && opening !== "{") ||
424
450
  (character === "]" && opening !== "[")
425
- ) return undefined;
451
+ )
452
+ return undefined;
426
453
  if (stack.length > 0) continue;
427
454
  const candidate = source.slice(start, index + 1);
428
455
  const value = parseTelegramJsonObjectCandidate(candidate);
@@ -499,9 +526,7 @@ function parseTelegramAdaptiveActionPayloadRows(
499
526
  };
500
527
 
501
528
  skipWhitespace();
502
- const rows = source[offset] === "{"
503
- ? parseVerticalSequence()
504
- : parseMatrix();
529
+ const rows = source[offset] === "{" ? parseVerticalSequence() : parseMatrix();
505
530
  if (!rows) return undefined;
506
531
  skipWhitespace();
507
532
  if (!options.allowTrailing && offset !== source.length) return undefined;
@@ -546,9 +571,7 @@ function isPlausibleTelegramMatrixStart(
546
571
  let offset = start + 1;
547
572
  while (/\s/u.test(source[offset] ?? "")) offset += 1;
548
573
  return (
549
- source[offset] === "{" ||
550
- source[offset] === "[" ||
551
- source[offset] === "]"
574
+ source[offset] === "{" || source[offset] === "[" || source[offset] === "]"
552
575
  );
553
576
  }
554
577
 
@@ -566,60 +589,41 @@ export function parseTelegramActionPayloadRows(
566
589
  comment: TelegramTopLevelHtmlComment,
567
590
  command: string,
568
591
  ): Record<string, unknown>[][] | undefined {
569
- let content = comment.content.replace(/^\s+/, "").replace(/^!/, "");
570
- if (!content.startsWith(command)) return undefined;
571
- content = content.slice(command.length);
572
- let attributeEnvelope = content;
573
- for (let offset = 0; offset < content.length; offset += 1) {
574
- if (content[offset] !== "[" && content[offset] !== "{") continue;
575
- if (
576
- content[offset] === "[" &&
577
- !isPlausibleTelegramMatrixStart(content, offset)
578
- ) {
579
- const noiseEnd = findTelegramStructuredPayloadEnd(content, offset);
580
- if (noiseEnd !== undefined) {
581
- attributeEnvelope = `${attributeEnvelope.slice(0, offset)}${" ".repeat(noiseEnd - offset)}${attributeEnvelope.slice(noiseEnd)}`;
582
- offset = noiseEnd - 1;
583
- }
584
- continue;
585
- }
586
- const parsed = parseTelegramAdaptiveActionPayloadRows(
587
- content.slice(offset),
588
- parseTelegramButtonCompactActionPayload,
589
- { allowTrailing: true, maxCompactAtoms: 4 },
590
- );
591
- if (parsed) return parsed.rows;
592
- const end = findTelegramStructuredPayloadEnd(content, offset);
593
- if (end === undefined) continue;
594
- attributeEnvelope = `${attributeEnvelope.slice(0, offset)}${" ".repeat(end - offset)}${attributeEnvelope.slice(end)}`;
595
- offset = end - 1;
596
- }
597
- const attributes = parseTolerantTelegramAttributes(attributeEnvelope, [
598
- "label",
599
- "prompt",
600
- "value",
601
- "selected_style",
602
- "disabled",
603
- ]);
592
+ const scanned = scanTelegramActionComment(
593
+ comment,
594
+ command,
595
+ (content, offset) =>
596
+ parseTelegramAdaptiveActionPayloadRows(
597
+ content.slice(offset),
598
+ parseTelegramButtonCompactActionPayload,
599
+ { allowTrailing: true, maxCompactAtoms: 4 },
600
+ )?.rows,
601
+ );
602
+ if (!scanned) return undefined;
603
+ if ("payload" in scanned) return scanned.payload;
604
+ const attributes = parseTolerantTelegramAttributes(
605
+ scanned.attributeEnvelope,
606
+ ["label", "prompt", "value", "selected_style", "disabled"],
607
+ );
604
608
  if (!attributes) return undefined;
605
- return [[{
606
- ...attributes,
607
- ...(attributes.disabled === "true" || attributes.disabled === "false"
608
- ? { disabled: attributes.disabled === "true" }
609
- : {}),
610
- }]];
611
- }
612
-
613
- export function normalizeMarkdownAfterVoiceExtraction(
614
- markdown: string,
615
- ): string {
609
+ return [
610
+ [
611
+ {
612
+ ...attributes,
613
+ ...(attributes.disabled === "true" || attributes.disabled === "false"
614
+ ? { disabled: attributes.disabled === "true" }
615
+ : {}),
616
+ },
617
+ ],
618
+ ];
619
+ }
620
+
621
+ function normalizeMarkdownAfterVoiceExtraction(markdown: string): string {
616
622
  return markdown.replace(/\n{3,}/g, "\n\n").trim();
617
623
  }
618
624
 
619
625
  function isTelegramCommentOnlyLinePrefix(value: string): boolean {
620
- return /^[ \t]*(?:(?:>[ \t]*)+)?(?:(?:[-+*]|\d+[.)])[ \t]+)?$/u.test(
621
- value,
622
- );
626
+ return /^[ \t]*(?:(?:>[ \t]*)+)?(?:(?:[-+*]|\d+[.)])[ \t]+)?$/u.test(value);
623
627
  }
624
628
 
625
629
  function stripTelegramHtmlCommentBlocks(markdown: string): string {
@@ -674,10 +678,6 @@ export function stripTelegramCommentMarkupForDelivery(
674
678
  return normalizeMarkdownAfterVoiceExtraction(deliveryMarkdown);
675
679
  }
676
680
 
677
- export function stripTelegramVoiceMarkupForPreview(markdown: string): string {
678
- return stripTelegramCommentMarkupForPreview(markdown);
679
- }
680
-
681
681
  export interface TelegramVoiceReplyItem {
682
682
  text: string;
683
683
  lang?: string;
@@ -710,7 +710,9 @@ export function planTelegramVoiceReply(
710
710
  let rate: string | undefined;
711
711
  const stripped = replaceTopLevelHtmlComments(markdown, (comment) => {
712
712
  const command = "telegram_voice";
713
- const normalizedContent = comment.content.replace(/^\s+/, "").replace(/^!/, "");
713
+ const normalizedContent = comment.content
714
+ .replace(/^\s+/, "")
715
+ .replace(/^!/, "");
714
716
  if (!normalizedContent.startsWith(command)) return comment.raw;
715
717
  const payload = parseTelegramActionPayload(comment, command);
716
718
  if (!payload) return "";
@@ -13,8 +13,8 @@ import {
13
13
  getTelegramTargetThreadParams,
14
14
  type TelegramTarget,
15
15
  } from "./target.ts";
16
- import { getTelegramVoiceSynthesisProviders } from "./voice.ts";
17
16
  import { isTelegramApiCommitUnknownError } from "./telegram-api.ts";
17
+ import { getTelegramVoiceSynthesisProviders } from "./voice.ts";
18
18
 
19
19
  export interface TelegramVoiceReplyTurnView {
20
20
  chatId: number;
@@ -121,32 +121,38 @@ export function createTelegramVoiceReplySender<THandler = unknown>(
121
121
  await sendVoiceChatAction(deps, turn.chatId);
122
122
  if (deps.isDeliveryActive?.() === false) return;
123
123
  await withTelegramReplyParameters(
124
- turn.chatId, options?.replyToPrompt === false ? undefined : turn.replyToMessageId, turn.target,
125
- (replyParameters) => deps.sendMultipart(
126
- "sendVoice",
127
- {
128
- chat_id: String(turn.chatId),
129
- ...(replyParameters ? { reply_parameters: JSON.stringify(replyParameters) } : {}),
130
- ...(turn.target
131
- ? Object.fromEntries(
132
- Object.entries(getTelegramTargetThreadParams(turn.target)).map(
133
- ([key, value]) => [key, String(value)],
134
- ),
135
- )
136
- : {}),
137
- ...(options?.replyMarkup !== undefined && options.replyMarkup !== null
138
- ? {
139
- reply_markup:
140
- typeof options.replyMarkup === "string"
141
- ? options.replyMarkup
142
- : JSON.stringify(options.replyMarkup),
143
- }
144
- : {}),
145
- },
146
- "voice",
147
- voiceFilePath,
148
- basename(voiceFilePath),
149
- ),
124
+ turn.chatId,
125
+ options?.replyToPrompt === false ? undefined : turn.replyToMessageId,
126
+ turn.target,
127
+ (replyParameters) =>
128
+ deps.sendMultipart(
129
+ "sendVoice",
130
+ {
131
+ chat_id: String(turn.chatId),
132
+ ...(replyParameters
133
+ ? { reply_parameters: JSON.stringify(replyParameters) }
134
+ : {}),
135
+ ...(turn.target
136
+ ? Object.fromEntries(
137
+ Object.entries(
138
+ getTelegramTargetThreadParams(turn.target),
139
+ ).map(([key, value]) => [key, String(value)]),
140
+ )
141
+ : {}),
142
+ ...(options?.replyMarkup !== undefined &&
143
+ options.replyMarkup !== null
144
+ ? {
145
+ reply_markup:
146
+ typeof options.replyMarkup === "string"
147
+ ? options.replyMarkup
148
+ : JSON.stringify(options.replyMarkup),
149
+ }
150
+ : {}),
151
+ },
152
+ "voice",
153
+ voiceFilePath,
154
+ basename(voiceFilePath),
155
+ ),
150
156
  );
151
157
  };
152
158