@llblab/pi-kit 0.27.6 → 0.28.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 (182) hide show
  1. package/BACKLOG.md +2 -5
  2. package/CHANGELOG.md +5 -0
  3. package/README.md +2 -2
  4. package/node_modules/@llblab/pi-telegram/AGENTS.md +10 -8
  5. package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -9
  6. package/node_modules/@llblab/pi-telegram/CHANGELOG.md +13 -0
  7. package/node_modules/@llblab/pi-telegram/LICENSE +21 -0
  8. package/node_modules/@llblab/pi-telegram/README.md +15 -5
  9. package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.d.ts +1 -5
  10. package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.js +5 -7
  11. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +5 -1
  12. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +6 -1
  13. package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.js +4 -2
  14. package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.d.ts +54 -56
  15. package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.js +206 -139
  16. package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.d.ts +21 -2
  17. package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.js +181 -25
  18. package/node_modules/@llblab/pi-telegram/dist/lib/bus-transport.d.ts +4 -1
  19. package/node_modules/@llblab/pi-telegram/dist/lib/bus-transport.js +28 -4
  20. package/node_modules/@llblab/pi-telegram/dist/lib/bus.d.ts +52 -56
  21. package/node_modules/@llblab/pi-telegram/dist/lib/bus.js +78 -246
  22. package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.d.ts +1 -3
  23. package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.js +23 -26
  24. package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.d.ts +0 -5
  25. package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.js +5 -5
  26. package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +0 -23
  27. package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +15 -15
  28. package/node_modules/@llblab/pi-telegram/dist/lib/config.d.ts +0 -17
  29. package/node_modules/@llblab/pi-telegram/dist/lib/config.js +12 -14
  30. package/node_modules/@llblab/pi-telegram/dist/lib/delivery.d.ts +0 -4
  31. package/node_modules/@llblab/pi-telegram/dist/lib/delivery.js +2 -5
  32. package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +255 -61
  33. package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.d.ts +0 -19
  34. package/node_modules/@llblab/pi-telegram/dist/lib/inbound.d.ts +0 -2
  35. package/node_modules/@llblab/pi-telegram/dist/lib/inbound.js +2 -2
  36. package/node_modules/@llblab/pi-telegram/dist/lib/journal.d.ts +177 -16
  37. package/node_modules/@llblab/pi-telegram/dist/lib/journal.js +1115 -249
  38. package/node_modules/@llblab/pi-telegram/dist/lib/keyboard.d.ts +0 -2
  39. package/node_modules/@llblab/pi-telegram/dist/lib/keyboard.js +2 -2
  40. package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.js +1 -1
  41. package/node_modules/@llblab/pi-telegram/dist/lib/locks.d.ts +100 -12
  42. package/node_modules/@llblab/pi-telegram/dist/lib/locks.js +380 -14
  43. package/node_modules/@llblab/pi-telegram/dist/lib/logging.d.ts +30 -12
  44. package/node_modules/@llblab/pi-telegram/dist/lib/logging.js +129 -72
  45. package/node_modules/@llblab/pi-telegram/dist/lib/media.d.ts +28 -5
  46. package/node_modules/@llblab/pi-telegram/dist/lib/media.js +26 -8
  47. package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.d.ts +0 -11
  48. package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.js +8 -8
  49. package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.d.ts +0 -18
  50. package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.js +18 -18
  51. package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.d.ts +4 -4
  52. package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.js +12 -7
  53. package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.d.ts +1 -1
  54. package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.js +1 -1
  55. package/node_modules/@llblab/pi-telegram/dist/lib/menu.d.ts +1 -0
  56. package/node_modules/@llblab/pi-telegram/dist/lib/menu.js +4 -3
  57. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.d.ts +0 -1
  58. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.js +1 -1
  59. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-buttons.js +1 -8
  60. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.d.ts +1 -6
  61. package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.js +4 -21
  62. package/node_modules/@llblab/pi-telegram/dist/lib/outbound.d.ts +0 -8
  63. package/node_modules/@llblab/pi-telegram/dist/lib/outbound.js +5 -5
  64. package/node_modules/@llblab/pi-telegram/dist/lib/paths.d.ts +50 -7
  65. package/node_modules/@llblab/pi-telegram/dist/lib/paths.js +155 -17
  66. package/node_modules/@llblab/pi-telegram/dist/lib/pi.d.ts +1 -0
  67. package/node_modules/@llblab/pi-telegram/dist/lib/pi.js +2 -1
  68. package/node_modules/@llblab/pi-telegram/dist/lib/polling.d.ts +0 -5
  69. package/node_modules/@llblab/pi-telegram/dist/lib/polling.js +5 -5
  70. package/node_modules/@llblab/pi-telegram/dist/lib/preview.d.ts +0 -3
  71. package/node_modules/@llblab/pi-telegram/dist/lib/preview.js +3 -3
  72. package/node_modules/@llblab/pi-telegram/dist/lib/prompt-templates.d.ts +0 -1
  73. package/node_modules/@llblab/pi-telegram/dist/lib/prompt-templates.js +1 -1
  74. package/node_modules/@llblab/pi-telegram/dist/lib/queue.d.ts +3 -15
  75. package/node_modules/@llblab/pi-telegram/dist/lib/queue.js +69 -8
  76. package/node_modules/@llblab/pi-telegram/dist/lib/recovery.d.ts +29 -9
  77. package/node_modules/@llblab/pi-telegram/dist/lib/recovery.js +104 -33
  78. package/node_modules/@llblab/pi-telegram/dist/lib/replies.d.ts +0 -4
  79. package/node_modules/@llblab/pi-telegram/dist/lib/replies.js +1 -1
  80. package/node_modules/@llblab/pi-telegram/dist/lib/routing.d.ts +73 -8
  81. package/node_modules/@llblab/pi-telegram/dist/lib/routing.js +2206 -307
  82. package/node_modules/@llblab/pi-telegram/dist/lib/sections.d.ts +0 -2
  83. package/node_modules/@llblab/pi-telegram/dist/lib/sections.js +1 -1
  84. package/node_modules/@llblab/pi-telegram/dist/lib/status.d.ts +44 -9
  85. package/node_modules/@llblab/pi-telegram/dist/lib/status.js +149 -27
  86. package/node_modules/@llblab/pi-telegram/dist/lib/sync.d.ts +0 -5
  87. package/node_modules/@llblab/pi-telegram/dist/lib/sync.js +5 -4
  88. package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.d.ts +11 -3
  89. package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.js +34 -23
  90. package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.d.ts +1 -0
  91. package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.js +18 -21
  92. package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.d.ts +32 -5
  93. package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.js +191 -7
  94. package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.d.ts +2 -0
  95. package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.js +1 -1
  96. package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +291 -59
  97. package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +1647 -369
  98. package/node_modules/@llblab/pi-telegram/dist/lib/turns.d.ts +0 -1
  99. package/node_modules/@llblab/pi-telegram/dist/lib/turns.js +1 -1
  100. package/node_modules/@llblab/pi-telegram/dist/lib/updates.d.ts +184 -28
  101. package/node_modules/@llblab/pi-telegram/dist/lib/updates.js +1032 -114
  102. package/node_modules/@llblab/pi-telegram/dist/lib/wire.d.ts +12 -0
  103. package/node_modules/@llblab/pi-telegram/dist/lib/wire.js +21 -0
  104. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.d.ts +25 -17
  105. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.js +95 -20
  106. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-identity.d.ts +20 -0
  107. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-identity.js +103 -0
  108. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.d.ts +43 -8
  109. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.js +148 -52
  110. package/node_modules/@llblab/pi-telegram/dist/package.json +6 -6
  111. package/node_modules/@llblab/pi-telegram/dist/skills/generated-control-surface/SKILL.md +3 -3
  112. package/node_modules/@llblab/pi-telegram/dist/skills/telegram-bridge/references/diagnosis.md +3 -3
  113. package/node_modules/@llblab/pi-telegram/docs/README.md +1 -1
  114. package/node_modules/@llblab/pi-telegram/docs/activity.md +1 -1
  115. package/node_modules/@llblab/pi-telegram/docs/architecture.md +152 -34
  116. package/node_modules/@llblab/pi-telegram/docs/callback-namespaces.md +1 -1
  117. package/node_modules/@llblab/pi-telegram/docs/delivery.md +1 -1
  118. package/node_modules/@llblab/pi-telegram/docs/generative-apps.md +2 -2
  119. package/node_modules/@llblab/pi-telegram/docs/inbound.md +1 -1
  120. package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +125 -19
  121. package/node_modules/@llblab/pi-telegram/docs/public-api.md +3 -1
  122. package/node_modules/@llblab/pi-telegram/docs/ui-style.md +22 -8
  123. package/node_modules/@llblab/pi-telegram/docs/updates.md +1 -1
  124. package/node_modules/@llblab/pi-telegram/lib/activity-verbosity.ts +5 -9
  125. package/node_modules/@llblab/pi-telegram/lib/bindings.ts +6 -0
  126. package/node_modules/@llblab/pi-telegram/lib/bus-api.ts +5 -2
  127. package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +243 -224
  128. package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +178 -31
  129. package/node_modules/@llblab/pi-telegram/lib/bus-transport.ts +30 -8
  130. package/node_modules/@llblab/pi-telegram/lib/bus.ts +110 -334
  131. package/node_modules/@llblab/pi-telegram/lib/channel-posts.ts +29 -29
  132. package/node_modules/@llblab/pi-telegram/lib/command-templates.ts +5 -5
  133. package/node_modules/@llblab/pi-telegram/lib/commands.ts +15 -15
  134. package/node_modules/@llblab/pi-telegram/lib/config.ts +12 -17
  135. package/node_modules/@llblab/pi-telegram/lib/delivery.ts +2 -8
  136. package/node_modules/@llblab/pi-telegram/lib/extension.ts +257 -72
  137. package/node_modules/@llblab/pi-telegram/lib/generative-apps.ts +0 -22
  138. package/node_modules/@llblab/pi-telegram/lib/inbound.ts +2 -2
  139. package/node_modules/@llblab/pi-telegram/lib/journal.ts +1182 -326
  140. package/node_modules/@llblab/pi-telegram/lib/keyboard.ts +2 -2
  141. package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +1 -1
  142. package/node_modules/@llblab/pi-telegram/lib/locks.ts +402 -23
  143. package/node_modules/@llblab/pi-telegram/lib/logging.ts +151 -101
  144. package/node_modules/@llblab/pi-telegram/lib/media.ts +51 -9
  145. package/node_modules/@llblab/pi-telegram/lib/menu-model.ts +8 -8
  146. package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +18 -18
  147. package/node_modules/@llblab/pi-telegram/lib/menu-status.ts +11 -0
  148. package/node_modules/@llblab/pi-telegram/lib/menu-thinking.ts +2 -2
  149. package/node_modules/@llblab/pi-telegram/lib/menu.ts +5 -1
  150. package/node_modules/@llblab/pi-telegram/lib/outbound-attachments.ts +1 -1
  151. package/node_modules/@llblab/pi-telegram/lib/outbound-buttons.ts +1 -10
  152. package/node_modules/@llblab/pi-telegram/lib/outbound-markup.ts +5 -24
  153. package/node_modules/@llblab/pi-telegram/lib/outbound.ts +5 -5
  154. package/node_modules/@llblab/pi-telegram/lib/paths.ts +177 -17
  155. package/node_modules/@llblab/pi-telegram/lib/pi.ts +5 -2
  156. package/node_modules/@llblab/pi-telegram/lib/polling.ts +5 -5
  157. package/node_modules/@llblab/pi-telegram/lib/preview.ts +3 -3
  158. package/node_modules/@llblab/pi-telegram/lib/prompt-templates.ts +1 -1
  159. package/node_modules/@llblab/pi-telegram/lib/queue.ts +67 -9
  160. package/node_modules/@llblab/pi-telegram/lib/recovery.ts +104 -44
  161. package/node_modules/@llblab/pi-telegram/lib/replies.ts +1 -1
  162. package/node_modules/@llblab/pi-telegram/lib/routing.ts +1955 -375
  163. package/node_modules/@llblab/pi-telegram/lib/sections.ts +1 -1
  164. package/node_modules/@llblab/pi-telegram/lib/status.ts +156 -36
  165. package/node_modules/@llblab/pi-telegram/lib/sync.ts +4 -4
  166. package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +48 -33
  167. package/node_modules/@llblab/pi-telegram/lib/thread-cleanup-manager.ts +24 -21
  168. package/node_modules/@llblab/pi-telegram/lib/thread-naming.ts +267 -9
  169. package/node_modules/@llblab/pi-telegram/lib/thread-reconciler.ts +3 -1
  170. package/node_modules/@llblab/pi-telegram/lib/threads.ts +1697 -488
  171. package/node_modules/@llblab/pi-telegram/lib/turns.ts +1 -1
  172. package/node_modules/@llblab/pi-telegram/lib/updates.ts +1072 -151
  173. package/node_modules/@llblab/pi-telegram/lib/wire.ts +28 -0
  174. package/node_modules/@llblab/pi-telegram/lib/workspace-admission.ts +97 -47
  175. package/node_modules/@llblab/pi-telegram/lib/workspace-identity.ts +147 -0
  176. package/node_modules/@llblab/pi-telegram/lib/workspace-retirement.ts +188 -89
  177. package/node_modules/@llblab/pi-telegram/package.json +6 -6
  178. package/node_modules/@llblab/pi-telegram/scripts/audit-exports.mjs +100 -0
  179. package/node_modules/@llblab/pi-telegram/scripts/check-downgrade.mjs +80 -43
  180. package/node_modules/@llblab/pi-telegram/skills/generated-control-surface/SKILL.md +3 -3
  181. package/node_modules/@llblab/pi-telegram/skills/telegram-bridge/references/diagnosis.md +3 -3
  182. package/package.json +2 -2
@@ -8,12 +8,18 @@ import { chmodSync, closeSync, constants, createReadStream, fstatSync, lstatSync
8
8
  readFileSync, unlinkSync, writeFileSync } from "node:fs";
9
9
  import { lstat } from "node:fs/promises";
10
10
  import { basename, dirname } from "node:path";
11
+ import { getTelegramJournalPublicationPaths } from "./paths.ts";
11
12
  import { createHash, randomUUID } from "node:crypto";
12
13
 
13
14
  import { Type } from "@sinclair/typebox";
14
15
 
15
16
  import { renameTelegramPathWithRetry, withTelegramFileTransaction } from "./locks.ts";
16
17
  import type { ExtensionAPI } from "./pi.ts";
18
+ import {
19
+ isWireRecord as isRecord,
20
+ hasOnlyWireKeys as hasOnlyKeys,
21
+ isNonNegativeWireInteger as isSafeTime,
22
+ } from "./wire.ts";
17
23
 
18
24
  const CHANNEL_POST_JOURNAL_VERSION = 1;
19
25
  const DEFAULT_MAX_RECORDS = 256;
@@ -31,12 +37,12 @@ export interface TelegramChannelPostMediaIntent {
31
37
  sha256: string;
32
38
  }
33
39
 
34
- export const TELEGRAM_CHANNEL_POST_MEDIA_MAX_BYTES: Record<TelegramChannelPostMediaKind, number> = {
40
+ const TELEGRAM_CHANNEL_POST_MEDIA_MAX_BYTES: Record<TelegramChannelPostMediaKind, number> = {
35
41
  photo: 10 * 1024 * 1024,
36
42
  video: 50 * 1024 * 1024,
37
43
  };
38
- export const TELEGRAM_CHANNEL_POST_CAPTION_MAX_LENGTH = 1024;
39
- export const TELEGRAM_CHANNEL_POST_MEDIA_FILE_NAME_MAX_LENGTH = 255;
44
+ const TELEGRAM_CHANNEL_POST_CAPTION_MAX_LENGTH = 1024;
45
+ const TELEGRAM_CHANNEL_POST_MEDIA_FILE_NAME_MAX_LENGTH = 255;
40
46
 
41
47
  /** Safe, content-free local validation failure for channel media publication intent. */
42
48
  export class TelegramChannelPostValidationError extends Error {
@@ -195,6 +201,7 @@ interface TelegramChannelPostJournalFile {
195
201
 
196
202
  export interface TelegramChannelPostJournalStoreOptions {
197
203
  path: string;
204
+ runtimeDir?: string;
198
205
  profileName: string;
199
206
  tokenSha256: string;
200
207
  maxRecords?: number;
@@ -329,19 +336,6 @@ export function registerTelegramChannelPostListTool(
329
336
  });
330
337
  }
331
338
 
332
- function isRecord(value: unknown): value is Record<string, unknown> {
333
- return !!value && typeof value === "object" && !Array.isArray(value);
334
- }
335
-
336
- function hasOnlyKeys(value: Record<string, unknown>, keys: readonly string[]): boolean {
337
- const allowed = new Set(keys);
338
- return Object.keys(value).every(key => allowed.has(key));
339
- }
340
-
341
- function isSafeTime(value: unknown): value is number {
342
- return Number.isSafeInteger(value) && (value as number) >= 0;
343
- }
344
-
345
339
  function normalizeChannel(value: unknown): TelegramChannelPostAddress {
346
340
  if (Number.isSafeInteger(value) && (value as number) < 0) return value as number;
347
341
  if (typeof value === "string" && /^@[A-Za-z0-9_]{5,32}$/u.test(value)) {
@@ -454,9 +448,24 @@ function validateRecord(value: unknown): TelegramChannelPostRecord {
454
448
  throw new TelegramChannelPostJournalError("invalid", "Telegram channel post journal contains an invalid state.");
455
449
  }
456
450
 
451
+ function parseTelegramChannelPostJournalFile(value: unknown, profileName: string, tokenSha256: string): TelegramChannelPostJournalFile {
452
+ if (!isRecord(value) || !hasOnlyKeys(value, ["version", "profile", "tokenSha256", "records"]) ||
453
+ value.version !== CHANNEL_POST_JOURNAL_VERSION || value.profile !== profileName ||
454
+ value.tokenSha256 !== tokenSha256 || !Array.isArray(value.records)) {
455
+ throw new TelegramChannelPostJournalError("conflict", "Telegram channel post journal identity or schema does not match.");
456
+ }
457
+ const records = value.records.map(validateRecord);
458
+ if (new Set(records.map(record => record.operationId)).size !== records.length) {
459
+ throw new TelegramChannelPostJournalError("invalid", "Telegram channel post journal contains duplicate operation IDs.");
460
+ }
461
+ return { version: CHANNEL_POST_JOURNAL_VERSION, profile: profileName, tokenSha256, records };
462
+ }
463
+
457
464
  export function createTelegramChannelPostJournalStore(
458
465
  options: TelegramChannelPostJournalStoreOptions,
459
466
  ): TelegramChannelPostJournalStore {
467
+ options = { ...options };
468
+ const publicationPaths = getTelegramJournalPublicationPaths(options.path, options.runtimeDir);
460
469
  const maxRecords = options.maxRecords ?? DEFAULT_MAX_RECORDS;
461
470
  const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
462
471
  const now = options.getNowMs ?? Date.now;
@@ -491,17 +500,7 @@ export function createTelegramChannelPostJournalStore(
491
500
  throw new TelegramChannelPostJournalError("conflict", "Telegram channel post journal changed during inspection.");
492
501
  }
493
502
  const value = JSON.parse(readFileSync(fd, "utf8")) as unknown;
494
- if (!isRecord(value) || !hasOnlyKeys(value, ["version", "profile", "tokenSha256", "records"]) ||
495
- value.version !== CHANNEL_POST_JOURNAL_VERSION || value.profile !== options.profileName ||
496
- value.tokenSha256 !== options.tokenSha256 || !Array.isArray(value.records)) {
497
- throw new TelegramChannelPostJournalError("conflict", "Telegram channel post journal identity or schema does not match.");
498
- }
499
- const records = value.records.map(validateRecord);
500
- if (new Set(records.map(record => record.operationId)).size !== records.length) {
501
- throw new TelegramChannelPostJournalError("invalid", "Telegram channel post journal contains duplicate operation IDs.");
502
- }
503
- return { version: CHANNEL_POST_JOURNAL_VERSION, profile: options.profileName,
504
- tokenSha256: options.tokenSha256, records };
503
+ return parseTelegramChannelPostJournalFile(value, options.profileName, options.tokenSha256);
505
504
  } catch (error) {
506
505
  if (error instanceof TelegramChannelPostJournalError) throw error;
507
506
  throw new TelegramChannelPostJournalError("io", "Could not read Telegram channel post journal.", error);
@@ -517,8 +516,9 @@ export function createTelegramChannelPostJournalStore(
517
516
  if (Buffer.byteLength(serialized) > maxBytes) {
518
517
  throw new TelegramChannelPostJournalError("capacity", "Telegram channel post journal byte limit reached.");
519
518
  }
520
- const temporaryPath = `${options.path}.${process.pid}.${randomUUID()}.tmp`;
519
+ const temporaryPath = `${publicationPaths.temporaryBasePath}.${process.pid}.${randomUUID()}.tmp`;
521
520
  mkdirSync(dirname(options.path), { recursive: true, mode: 0o700 });
521
+ mkdirSync(dirname(temporaryPath), { recursive: true, mode: 0o700 });
522
522
  try {
523
523
  writeFileSync(temporaryPath, serialized, { encoding: "utf8", mode: 0o600 });
524
524
  chmodSync(temporaryPath, 0o600);
@@ -532,7 +532,7 @@ export function createTelegramChannelPostJournalStore(
532
532
  };
533
533
  const mutate = <T>(operation: (file: TelegramChannelPostJournalFile) => T): T => {
534
534
  try {
535
- return withTelegramFileTransaction(`${options.path}.transaction`, () => operation(read()));
535
+ return withTelegramFileTransaction(publicationPaths.transactionPath, () => operation(read()));
536
536
  } catch (error) {
537
537
  if (error instanceof TelegramChannelPostJournalError) throw error;
538
538
  throw new TelegramChannelPostJournalError("io", "Telegram channel post journal mutation failed.", error);
@@ -121,7 +121,7 @@ function normalizeCommandTemplateDefaults(
121
121
  return normalized;
122
122
  }
123
123
 
124
- export function resolveInheritedDefaultReferences(
124
+ function resolveInheritedDefaultReferences(
125
125
  ownDefaults: Record<string, unknown> | undefined,
126
126
  inheritedDefaults: Record<string, unknown> | undefined,
127
127
  runtimeValues: Record<string, unknown> = {},
@@ -142,7 +142,7 @@ export function resolveInheritedDefaultReferences(
142
142
  return resolved;
143
143
  }
144
144
 
145
- export function resolveCommandTemplateRepeat(
145
+ function resolveCommandTemplateRepeat(
146
146
  value: number | string | undefined,
147
147
  values: Record<string, unknown> = {},
148
148
  ): number | undefined {
@@ -367,7 +367,7 @@ function pad(value: number, width: number): string {
367
367
  return String(value).padStart(width, "0");
368
368
  }
369
369
 
370
- export function getCommandTemplateRepeatDefaults(
370
+ function getCommandTemplateRepeatDefaults(
371
371
  index: number,
372
372
  repeat: number,
373
373
  ): Record<string, string> {
@@ -516,7 +516,7 @@ function parseCommandTemplatePlaceholderContent(
516
516
  };
517
517
  }
518
518
 
519
- export function getCommandTemplateDefaults(
519
+ function getCommandTemplateDefaults(
520
520
  config: CommandTemplateConfig | undefined,
521
521
  ): Record<string, string> {
522
522
  const normalizedConfig = config
@@ -588,7 +588,7 @@ export function splitCommandTemplate(input: string): string[] {
588
588
  return words;
589
589
  }
590
590
 
591
- export function expandCommandTemplateExecutable(
591
+ function expandCommandTemplateExecutable(
592
592
  command: string,
593
593
  cwd: string,
594
594
  ): string {
@@ -91,11 +91,11 @@ function getOrCreateTelegramCommandRegistry(): TelegramExtensionCommandRegistry
91
91
  return registry;
92
92
  }
93
93
 
94
- export function normalizeTelegramExtensionCommandName(name: string): string {
94
+ function normalizeTelegramExtensionCommandName(name: string): string {
95
95
  return name.trim().replace(/^\/+/, "").toLowerCase();
96
96
  }
97
97
 
98
- export function isTelegramExtensionCommandName(name: string): boolean {
98
+ function isTelegramExtensionCommandName(name: string): boolean {
99
99
  return TELEGRAM_BOT_COMMAND_NAME_PATTERN.test(name);
100
100
  }
101
101
 
@@ -144,7 +144,7 @@ export function registerTelegramCommand(
144
144
  };
145
145
  }
146
146
 
147
- export function getTelegramExtensionCommands(): RegisteredTelegramExtensionCommand[] {
147
+ function getTelegramExtensionCommands(): RegisteredTelegramExtensionCommand[] {
148
148
  return Array.from(
149
149
  getOrCreateTelegramCommandRegistry().commands.values(),
150
150
  ).sort((a, b) => a.order - b.order || a.name.localeCompare(b.name));
@@ -181,7 +181,7 @@ export const TELEGRAM_COMMAND_EMOJI = {
181
181
 
182
182
  export type TelegramCommandEmojiName = keyof typeof TELEGRAM_COMMAND_EMOJI;
183
183
 
184
- export function getTelegramCommandEmoji(
184
+ function getTelegramCommandEmoji(
185
185
  command: TelegramCommandEmojiName,
186
186
  ): string {
187
187
  return TELEGRAM_COMMAND_EMOJI[command];
@@ -235,12 +235,12 @@ export function formatTelegramAutomaticThreadDisplayNameRestoredHeading(
235
235
  return `<b>✅ Automatic Thread display name restored as <i>${escapeHtml(name)}</i>.</b>`;
236
236
  }
237
237
 
238
- export const TELEGRAM_COMPACTION_STARTED_TEXT =
238
+ const TELEGRAM_COMPACTION_STARTED_TEXT =
239
239
  formatTelegramInformationHeading(
240
240
  getTelegramCommandEmoji("compact"),
241
241
  "Compaction started.",
242
242
  );
243
- export const TELEGRAM_COMPACTION_COMPLETED_TEXT =
243
+ const TELEGRAM_COMPACTION_COMPLETED_TEXT =
244
244
  formatTelegramInformationHeading("✅", "Compaction completed.");
245
245
  export const TELEGRAM_COMPACTION_STARTED_MARKDOWN =
246
246
  `**${formatTelegramCommandEmojiPrefix("compact")}Compaction started.**`;
@@ -254,7 +254,7 @@ function formatTelegramBotCommandDescription(
254
254
  return `${formatTelegramCommandEmojiPrefix(command)}${description}`;
255
255
  }
256
256
 
257
- export const TELEGRAM_BUILTIN_BOT_COMMANDS: readonly TelegramBotCommandDefinition[] =
257
+ const TELEGRAM_BUILTIN_BOT_COMMANDS: readonly TelegramBotCommandDefinition[] =
258
258
  [
259
259
  {
260
260
  command: "start",
@@ -1504,7 +1504,7 @@ export async function handleTelegramNextCommand(deps: {
1504
1504
  deps.updateStatus();
1505
1505
  }
1506
1506
 
1507
- export async function handleTelegramContinueCommand<TMessage, TContext>(
1507
+ async function handleTelegramContinueCommand<TMessage, TContext>(
1508
1508
  message: TMessage,
1509
1509
  ctx: TContext,
1510
1510
  deps: {
@@ -1530,7 +1530,7 @@ function dispatchNextQueuedTelegramTurnAfterCompact(
1530
1530
  deps.dispatchNextQueuedTelegramTurn();
1531
1531
  }
1532
1532
 
1533
- export function buildTelegramNewConfirmationReplyMarkup(): TelegramCompactConfirmationReplyMarkup {
1533
+ function buildTelegramNewConfirmationReplyMarkup(): TelegramCompactConfirmationReplyMarkup {
1534
1534
  return {
1535
1535
  inline_keyboard: [
1536
1536
  [
@@ -1541,7 +1541,7 @@ export function buildTelegramNewConfirmationReplyMarkup(): TelegramCompactConfir
1541
1541
  };
1542
1542
  }
1543
1543
 
1544
- export function getTelegramNewConfirmationHtml(): string {
1544
+ function getTelegramNewConfirmationHtml(): string {
1545
1545
  return "<b>Start a new session?</b>";
1546
1546
  }
1547
1547
 
@@ -1588,7 +1588,7 @@ export async function handleTelegramNewConfirmationCallback<TContext>(
1588
1588
  return true;
1589
1589
  }
1590
1590
 
1591
- export function buildTelegramCompactConfirmationReplyMarkup(): TelegramCompactConfirmationReplyMarkup {
1591
+ function buildTelegramCompactConfirmationReplyMarkup(): TelegramCompactConfirmationReplyMarkup {
1592
1592
  return {
1593
1593
  inline_keyboard: [
1594
1594
  [
@@ -1599,11 +1599,11 @@ export function buildTelegramCompactConfirmationReplyMarkup(): TelegramCompactCo
1599
1599
  };
1600
1600
  }
1601
1601
 
1602
- export function getTelegramCompactConfirmationHtml(): string {
1602
+ function getTelegramCompactConfirmationHtml(): string {
1603
1603
  return "<b>Compact session?</b>";
1604
1604
  }
1605
1605
 
1606
- export async function openTelegramCompactConfirmation(
1606
+ async function openTelegramCompactConfirmation(
1607
1607
  target: TelegramCommandMessageTarget,
1608
1608
  deps: TelegramCompactConfirmationDeps,
1609
1609
  ): Promise<void> {
@@ -2373,7 +2373,7 @@ export const TELEGRAM_INTERNAL_COMMAND_DESCRIPTION =
2373
2373
  export const TELEGRAM_INTERNAL_MANUAL_USE_MESSAGE =
2374
2374
  "This internal Telegram command cannot be run manually.";
2375
2375
 
2376
- export function delayTelegramSessionAction(delayMs: number): Promise<void> {
2376
+ function delayTelegramSessionAction(delayMs: number): Promise<void> {
2377
2377
  return new Promise<void>((resolve) => setTimeout(resolve, delayMs));
2378
2378
  }
2379
2379
 
@@ -2433,7 +2433,7 @@ export async function settleTelegramSessionReplacement(
2433
2433
  return "stale";
2434
2434
  }
2435
2435
 
2436
- export function createTelegramSessionReplacementSettlementRuntime<TContext>(deps: {
2436
+ function createTelegramSessionReplacementSettlementRuntime<TContext>(deps: {
2437
2437
  resolve: (ctx: TContext) => TelegramSessionReplacementSettlementDeps | undefined;
2438
2438
  onResult?: (result: "none" | "settled" | "expired" | "failed" | "stale") => void;
2439
2439
  onError?: (error: unknown) => void;
@@ -24,6 +24,7 @@ import {
24
24
  export { TELEGRAM_DEFAULT_PROFILE_NAME } from "./paths.ts";
25
25
 
26
26
  import type { CommandTemplateObjectConfig } from "./command-templates.ts";
27
+ import { isWireRecord as isPlainConfigRecord } from "./wire.ts";
27
28
  import type { TelegramInboundHandlerConfig } from "./inbound.ts";
28
29
  import { withTelegramFileTransaction } from "./locks.ts";
29
30
 
@@ -75,7 +76,7 @@ export type TelegramBotTokenReference =
75
76
  * environment-variable references. Any other `$`-prefixed value is malformed
76
77
  * rather than a literal secret so a broken reference fails closed.
77
78
  */
78
- export function getTelegramBotTokenReference(
79
+ function getTelegramBotTokenReference(
79
80
  value: string | undefined,
80
81
  ): TelegramBotTokenReference | undefined {
81
82
  const trimmed = value?.trim();
@@ -115,8 +116,6 @@ export function getTelegramBotTokenDiagnostic(
115
116
  return `Telegram bot token environment variable ${reference.variable} is not set.`;
116
117
  }
117
118
 
118
- export type TelegramOutboundCommandTemplateConfig =
119
- string | CommandTemplateObjectConfig;
120
119
  export interface TelegramOutboundHandlerConfig extends CommandTemplateObjectConfig {
121
120
  type?: string;
122
121
  match?: string | string[];
@@ -465,10 +464,6 @@ export async function writeTelegramConfig(
465
464
  await chmod(configPath, 0o600);
466
465
  }
467
466
 
468
- function isPlainConfigRecord(value: unknown): value is Record<string, unknown> {
469
- return !!value && typeof value === "object" && !Array.isArray(value);
470
- }
471
-
472
467
  function cloneTelegramConfig<T>(value: T): T {
473
468
  return structuredClone(value);
474
469
  }
@@ -963,7 +958,7 @@ export function createTelegramConfigStore(
963
958
  };
964
959
  }
965
960
 
966
- export function createTelegramDraftPreviewsChecker(
961
+ function createTelegramDraftPreviewsChecker(
967
962
  configStore: Pick<TelegramConfigStore, "get">,
968
963
  ): () => boolean {
969
964
  return () => {
@@ -977,7 +972,7 @@ export function createTelegramDraftPreviewsChecker(
977
972
  };
978
973
  }
979
974
 
980
- export function createTelegramDraftPreviewsSetter(
975
+ function createTelegramDraftPreviewsSetter(
981
976
  configStore: TelegramMutableConfigStore,
982
977
  ): (enabled: boolean) => Promise<void> {
983
978
  return async (enabled) => {
@@ -996,7 +991,7 @@ export function createTelegramDraftPreviewsSetter(
996
991
  };
997
992
  }
998
993
 
999
- export function createTelegramAssistantRenderingModeGetter(
994
+ function createTelegramAssistantRenderingModeGetter(
1000
995
  configStore: Pick<TelegramConfigStore, "get">,
1001
996
  ): () => TelegramAssistantRenderingMode {
1002
997
  return () => {
@@ -1006,7 +1001,7 @@ export function createTelegramAssistantRenderingModeGetter(
1006
1001
  };
1007
1002
  }
1008
1003
 
1009
- export function createTelegramAssistantRenderingModeSetter(
1004
+ function createTelegramAssistantRenderingModeSetter(
1010
1005
  configStore: TelegramMutableConfigStore,
1011
1006
  ): (mode: TelegramAssistantRenderingMode) => Promise<void> {
1012
1007
  return async (mode) => {
@@ -1022,7 +1017,7 @@ export function createTelegramAssistantRenderingModeSetter(
1022
1017
  };
1023
1018
  }
1024
1019
 
1025
- export function createTelegramActivityVerbosityGetter(
1020
+ function createTelegramActivityVerbosityGetter(
1026
1021
  configStore: Pick<TelegramConfigStore, "get">,
1027
1022
  ): () => TelegramActivityVerbosity {
1028
1023
  return () => {
@@ -1044,13 +1039,13 @@ export function createTelegramActivityVerbosityGetter(
1044
1039
  };
1045
1040
  }
1046
1041
 
1047
- export function createTelegramActivityVerbosityRefresher(
1042
+ function createTelegramActivityVerbosityRefresher(
1048
1043
  configStore: TelegramMutableConfigStore,
1049
1044
  ): () => Promise<void> {
1050
1045
  return () => loadLatestTelegramConfig(configStore);
1051
1046
  }
1052
1047
 
1053
- export function createTelegramActivityVerbositySetter(
1048
+ function createTelegramActivityVerbositySetter(
1054
1049
  configStore: TelegramMutableConfigStore,
1055
1050
  ): (verbosity: TelegramActivityVerbosity) => Promise<void> {
1056
1051
  return async (verbosity) => {
@@ -1210,13 +1205,13 @@ export function createTelegramProactivePushTargetGetter(deps: {
1210
1205
  };
1211
1206
  }
1212
1207
 
1213
- export function createTelegramAutomaticThreadCleanupChecker(
1208
+ function createTelegramAutomaticThreadCleanupChecker(
1214
1209
  configStore: Pick<TelegramConfigStore, "get">,
1215
1210
  ): () => boolean {
1216
1211
  return () => configStore.get().threads?.automaticCleanup ?? true;
1217
1212
  }
1218
1213
 
1219
- export function createTelegramAutomaticThreadCleanupResolver(
1214
+ function createTelegramAutomaticThreadCleanupResolver(
1220
1215
  configStore: TelegramMutableConfigStore,
1221
1216
  ): () => Promise<boolean> {
1222
1217
  return async () => {
@@ -1230,7 +1225,7 @@ export function createTelegramAutomaticThreadCleanupResolver(
1230
1225
  };
1231
1226
  }
1232
1227
 
1233
- export function createTelegramAutomaticThreadCleanupSetter(
1228
+ function createTelegramAutomaticThreadCleanupSetter(
1234
1229
  configStore: TelegramMutableConfigStore,
1235
1230
  ): (enabled: boolean) => Promise<void> {
1236
1231
  return async (enabled) => {
@@ -14,6 +14,7 @@ import {
14
14
  renderTelegramMessage,
15
15
  } from "./replies.ts";
16
16
  import {
17
+ areTelegramTargetsEqual as areDeliveryTargetsEqual,
17
18
  getTelegramTargetThreadParams,
18
19
  type TelegramTarget,
19
20
  } from "./target.ts";
@@ -262,7 +263,7 @@ function failure<T>(
262
263
  : { ok: false, reason, message, partial, ...retry };
263
264
  }
264
265
 
265
- export function classifyTelegramDeliveryTransportError(error: unknown): {
266
+ function classifyTelegramDeliveryTransportError(error: unknown): {
266
267
  reason: Extract<TelegramDeliveryFailureReason,
267
268
  "commit-unknown" | "message-unavailable" | "rate-limited" |
268
269
  "transport-retryable" | "transport-failed">;
@@ -374,13 +375,6 @@ export function isTelegramDeliveryExplicitTargetAuthorized(
374
375
  });
375
376
  }
376
377
 
377
- function areDeliveryTargetsEqual(
378
- left: TelegramDeliveryTarget,
379
- right: TelegramDeliveryTarget,
380
- ): boolean {
381
- return left.chatId === right.chatId && left.threadId === right.threadId;
382
- }
383
-
384
378
  function cloneTarget(target: TelegramDeliveryTarget): TelegramDeliveryTarget {
385
379
  return target.threadId === undefined
386
380
  ? { chatId: target.chatId }