@llblab/pi-kit 0.12.1 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/README.md +2 -2
- package/node_modules/@llblab/pi-state-flow/AGENTS.md +7 -7
- package/node_modules/@llblab/pi-state-flow/BACKLOG.md +8 -9
- package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +26 -0
- package/node_modules/@llblab/pi-state-flow/README.md +1 -3
- package/node_modules/@llblab/pi-state-flow/dist/index.d.ts +21 -0
- package/node_modules/@llblab/pi-state-flow/dist/index.js +20 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/acquisition.d.ts +39 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/acquisition.js +78 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/artifact.d.ts +110 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/artifact.js +334 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/compaction.d.ts +49 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/compaction.js +67 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/config.d.ts +11 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/config.js +53 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/context.d.ts +23 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/context.js +109 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/continuation.d.ts +111 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/continuation.js +189 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/discovery.d.ts +21 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/discovery.js +125 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/durable.d.ts +102 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/durable.js +507 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/episode.d.ts +8 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/episode.js +27 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/extension.d.ts +22 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +1263 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/git.d.ts +72 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/git.js +565 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/history.d.ts +22 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/history.js +79 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/json.d.ts +12 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/json.js +109 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/logging.d.ts +25 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/logging.js +24 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/maintenance.d.ts +36 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/maintenance.js +98 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/memory.d.ts +15 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/memory.js +42 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/migration.d.ts +13 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/migration.js +133 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.d.ts +7 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.js +59 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/publication.d.ts +69 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/publication.js +335 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/query.d.ts +27 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/query.js +35 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/recovery.d.ts +8 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/recovery.js +27 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/rehydration.d.ts +36 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/rehydration.js +38 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.d.ts +142 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/runtime.js +529 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/session.d.ts +21 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/session.js +44 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/skills.d.ts +25 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/skills.js +131 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.d.ts +88 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/snapshot.js +255 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/state.d.ts +55 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/state.js +31 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/status.d.ts +38 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/status.js +79 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/storage.d.ts +46 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/storage.js +217 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.d.ts +98 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/telegram.js +231 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/temporal.d.ts +39 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/temporal.js +203 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/transition.d.ts +25 -0
- package/node_modules/@llblab/pi-state-flow/dist/lib/transition.js +204 -0
- package/node_modules/@llblab/pi-state-flow/dist/package.json +79 -0
- package/node_modules/@llblab/pi-state-flow/dist/pi-state-flow/index.js +1 -0
- package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-memory/SKILL.md +138 -0
- package/node_modules/@llblab/pi-state-flow/docs/architecture.md +11 -5
- package/node_modules/@llblab/pi-state-flow/docs/compatibility.md +5 -1
- package/node_modules/@llblab/pi-state-flow/docs/filesystem-recovery.md +3 -3
- package/node_modules/@llblab/pi-state-flow/docs/usage.md +6 -4
- package/node_modules/@llblab/pi-state-flow/index.ts +1 -0
- package/node_modules/@llblab/pi-state-flow/lib/compaction.ts +17 -1
- package/node_modules/@llblab/pi-state-flow/lib/config.ts +6 -1
- package/node_modules/@llblab/pi-state-flow/lib/durable.ts +88 -96
- package/node_modules/@llblab/pi-state-flow/lib/extension.ts +64 -12
- package/node_modules/@llblab/pi-state-flow/lib/git.ts +32 -188
- package/node_modules/@llblab/pi-state-flow/lib/migration.ts +84 -48
- package/node_modules/@llblab/pi-state-flow/lib/query.ts +40 -0
- package/node_modules/@llblab/pi-state-flow/lib/recovery.ts +7 -30
- package/node_modules/@llblab/pi-state-flow/lib/runtime.ts +48 -50
- package/node_modules/@llblab/pi-state-flow/lib/snapshot.ts +41 -97
- package/node_modules/@llblab/pi-state-flow/lib/storage.ts +17 -12
- package/node_modules/@llblab/pi-state-flow/lib/telegram.ts +4 -4
- package/node_modules/@llblab/pi-state-flow/package.json +23 -6
- package/node_modules/@llblab/pi-state-flow/skills/state-flow-memory/SKILL.md +3 -1
- package/node_modules/@llblab/pi-telegram/AGENTS.md +5 -3
- package/node_modules/@llblab/pi-telegram/BACKLOG.md +0 -6
- package/node_modules/@llblab/pi-telegram/CHANGELOG.md +22 -0
- package/node_modules/@llblab/pi-telegram/README.md +6 -3
- package/node_modules/@llblab/pi-telegram/dist/api/activity.d.ts +6 -0
- package/node_modules/@llblab/pi-telegram/dist/api/activity.js +6 -0
- package/node_modules/@llblab/pi-telegram/dist/api/commands.d.ts +6 -0
- package/node_modules/@llblab/pi-telegram/dist/api/commands.js +6 -0
- package/node_modules/@llblab/pi-telegram/dist/api/delivery.d.ts +6 -0
- package/node_modules/@llblab/pi-telegram/dist/api/delivery.js +6 -0
- package/node_modules/@llblab/pi-telegram/dist/api/inbound.d.ts +6 -0
- package/node_modules/@llblab/pi-telegram/dist/api/inbound.js +6 -0
- package/node_modules/@llblab/pi-telegram/dist/api/keyboard.d.ts +6 -0
- package/node_modules/@llblab/pi-telegram/dist/api/keyboard.js +6 -0
- package/node_modules/@llblab/pi-telegram/dist/api/outbound.d.ts +6 -0
- package/node_modules/@llblab/pi-telegram/dist/api/outbound.js +6 -0
- package/node_modules/@llblab/pi-telegram/dist/api/sections.d.ts +7 -0
- package/node_modules/@llblab/pi-telegram/dist/api/sections.js +6 -0
- package/node_modules/@llblab/pi-telegram/dist/api/status.d.ts +6 -0
- package/node_modules/@llblab/pi-telegram/dist/api/status.js +6 -0
- package/node_modules/@llblab/pi-telegram/dist/api/updates.d.ts +6 -0
- package/node_modules/@llblab/pi-telegram/dist/api/updates.js +6 -0
- package/node_modules/@llblab/pi-telegram/dist/api/voice.d.ts +6 -0
- package/node_modules/@llblab/pi-telegram/dist/api/voice.js +6 -0
- package/node_modules/@llblab/pi-telegram/dist/index.d.ts +6 -0
- package/node_modules/@llblab/pi-telegram/dist/index.js +6 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.d.ts +52 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/activity-verbosity.js +596 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/activity.d.ts +220 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/activity.js +574 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/agent-messages.d.ts +28 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/agent-messages.js +86 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +250 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +936 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.d.ts +18 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-api.js +254 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.d.ts +464 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.js +1689 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.d.ts +292 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-leader.js +2242 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-transport.d.ts +64 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/bus-transport.js +140 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/bus.d.ts +518 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/bus.js +2055 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.d.ts +170 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/channel-posts.js +611 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.d.ts +70 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/command-templates.js +744 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +541 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +1155 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/config.d.ts +252 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/config.js +889 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/delivery.d.ts +163 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/delivery.js +542 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/extension.d.ts +7 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +1604 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/generative-app-worker.mjs +104 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.d.ts +212 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/generative-apps.js +1006 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/inbound.d.ts +91 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/inbound.js +502 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/journal.d.ts +665 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/journal.js +3701 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/keyboard.d.ts +23 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/keyboard.js +37 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.d.ts +157 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.js +395 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/locks.d.ts +164 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/locks.js +1208 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/logging.d.ts +50 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/logging.js +274 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/media.d.ts +181 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/media.js +602 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.d.ts +217 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-model.js +663 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-queue.d.ts +37 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-queue.js +408 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.d.ts +115 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-settings.js +595 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.d.ts +32 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-status.js +112 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.d.ts +29 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/menu-thinking.js +82 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/menu.d.ts +171 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/menu.js +323 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/model.d.ts +127 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/model.js +409 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.d.ts +241 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-attachments.js +639 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-buttons.d.ts +69 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-buttons.js +248 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.d.ts +42 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-markup.js +678 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-voice.d.ts +55 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound-voice.js +152 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound.d.ts +185 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/outbound.js +516 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/ownership.d.ts +77 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/ownership.js +174 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/paths.d.ts +40 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/paths.js +94 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/pi.d.ts +72 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/pi.js +121 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/polling.d.ts +351 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/polling.js +1196 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/preview.d.ts +192 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/preview.js +614 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/prompt-templates.d.ts +24 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/prompt-templates.js +118 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/prompts.d.ts +57 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/prompts.js +167 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/queue.d.ts +766 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/queue.js +1965 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/recovery.d.ts +86 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/recovery.js +285 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/rendering.d.ts +20 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/rendering.js +983 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/replies.d.ts +214 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/replies.js +737 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/routing.d.ts +207 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/routing.js +2282 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/runtime.d.ts +172 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/runtime.js +402 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/sections.d.ts +165 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/sections.js +327 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/setup.d.ts +82 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/setup.js +150 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/skills.d.ts +8 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/skills.js +12 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/status.d.ts +442 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/status.js +1000 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/sync.d.ts +179 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/sync.js +782 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/target.d.ts +20 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/target.js +27 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.d.ts +541 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/telegram-api.js +1159 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/text-groups.d.ts +89 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/text-groups.js +317 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.d.ts +288 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-cleanup-manager.js +560 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.d.ts +46 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-display.js +257 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.d.ts +46 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-naming.js +78 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.d.ts +239 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/thread-reconciler.js +644 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +621 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +3679 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/time-injection.d.ts +15 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/time-injection.js +56 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/turns.d.ts +109 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/turns.js +500 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/updates.d.ts +1290 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/updates.js +3634 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/voice.d.ts +117 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/voice.js +174 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.d.ts +264 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-admission.js +1136 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.d.ts +219 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.js +587 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-slots.d.ts +30 -0
- package/node_modules/@llblab/pi-telegram/dist/lib/workspace-slots.js +54 -0
- package/node_modules/@llblab/pi-telegram/dist/package.json +126 -0
- package/node_modules/@llblab/pi-telegram/dist/pi-telegram/index.js +1 -0
- package/node_modules/@llblab/pi-telegram/dist/skills/generated-control-surface/SKILL.md +107 -0
- package/node_modules/@llblab/pi-telegram/dist/skills/generated-control-surface/references/capability-adapters.md +27 -0
- package/node_modules/@llblab/pi-telegram/dist/skills/generated-control-surface/references/layout-and-state.md +37 -0
- package/node_modules/@llblab/pi-telegram/dist/skills/generative-apps/SKILL.md +115 -0
- package/node_modules/@llblab/pi-telegram/dist/skills/show-me/SKILL.md +166 -0
- package/node_modules/@llblab/pi-telegram/dist/skills/show-me/references/telegram-surfaces.md +43 -0
- package/node_modules/@llblab/pi-telegram/dist/skills/telegram-bridge/SKILL.md +129 -0
- package/node_modules/@llblab/pi-telegram/dist/skills/telegram-bridge/references/configuration.md +15 -0
- package/node_modules/@llblab/pi-telegram/dist/skills/telegram-bridge/references/delivery-and-threads.md +27 -0
- package/node_modules/@llblab/pi-telegram/dist/skills/telegram-bridge/references/diagnosis.md +18 -0
- package/node_modules/@llblab/pi-telegram/docs/README.md +2 -0
- package/node_modules/@llblab/pi-telegram/docs/architecture.md +4 -2
- package/node_modules/@llblab/pi-telegram/docs/compact-matrix-literal.md +10 -3
- package/node_modules/@llblab/pi-telegram/docs/generative-apps.md +15 -13
- package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +6 -4
- package/node_modules/@llblab/pi-telegram/docs/outbound.md +1 -1
- package/node_modules/@llblab/pi-telegram/docs/public-api.md +3 -3
- package/node_modules/@llblab/pi-telegram/lib/bindings.ts +89 -6
- package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +12 -5
- package/node_modules/@llblab/pi-telegram/lib/bus-leader.ts +44 -19
- package/node_modules/@llblab/pi-telegram/lib/bus.ts +4 -1
- package/node_modules/@llblab/pi-telegram/lib/commands.ts +2 -2
- package/node_modules/@llblab/pi-telegram/lib/config.ts +14 -7
- package/node_modules/@llblab/pi-telegram/lib/delivery.ts +38 -6
- package/node_modules/@llblab/pi-telegram/lib/extension.ts +7 -0
- package/node_modules/@llblab/pi-telegram/lib/generative-apps.ts +379 -13
- package/node_modules/@llblab/pi-telegram/lib/menu-settings.ts +20 -6
- package/node_modules/@llblab/pi-telegram/lib/outbound-markup.ts +16 -7
- package/node_modules/@llblab/pi-telegram/lib/status.ts +9 -15
- package/node_modules/@llblab/pi-telegram/lib/telegram-api.ts +12 -1
- package/node_modules/@llblab/pi-telegram/lib/thread-display.ts +96 -18
- package/node_modules/@llblab/pi-telegram/package.json +56 -13
- package/node_modules/@llblab/pi-telegram/scripts/build-dist.mjs +44 -0
- package/node_modules/@llblab/pi-telegram/scripts/measure-bus.mjs +8 -1
- package/node_modules/@llblab/pi-telegram/scripts/measure-workspace.mjs +8 -1
- package/node_modules/@llblab/pi-telegram/skills/generative-apps/SKILL.md +1 -1
- package/node_modules/@llblab/pi-telegram/skills/show-me/SKILL.md +1 -1
- package/node_modules/@llblab/pi-telegram/skills/show-me/references/telegram-surfaces.md +1 -1
- package/node_modules/@llblab/pi-telegram/skills/telegram-bridge/SKILL.md +1 -1
- package/package.json +7 -7
|
@@ -0,0 +1,782 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram synchronization helpers
|
|
3
|
+
* Zones: Telegram bot reality mirror, demand-driven reconciliation, status diagnostics
|
|
4
|
+
* Owns pure contracts for deciding when local Telegram mirror state should be refreshed without querying Telegram on every action
|
|
5
|
+
*/
|
|
6
|
+
import { getTelegramApiErrorRequestTarget, isTelegramStaleTargetHttpError } from "./telegram-api.js";
|
|
7
|
+
import { getTelegramTargetKey } from "./target.js";
|
|
8
|
+
import * as ThreadReconciler from "./thread-reconciler.js";
|
|
9
|
+
import { createTelegramWorkspaceAdmissionOperationId, runWithTelegramWorkspaceAdmissionsAsync, } from "./workspace-admission.js";
|
|
10
|
+
import { createTelegramCleanupTargetProtection, commitTelegramWorkspaceProvisionBinding, getTelegramTargetFromApiBody, getTelegramThreadOwnerKey, isSameTelegramProcessInstance, isTelegramTopicTargetStaleError, normalizeTelegramWorkspacePath, provisionOwnBusTopic, } from "./threads.js";
|
|
11
|
+
export function markTelegramConfigSyncChange(state, action, options) {
|
|
12
|
+
const nowMs = options?.nowMs ?? Date.now();
|
|
13
|
+
let nextState = markTelegramSyncSliceFresh(state, "pairing", {
|
|
14
|
+
nowMs,
|
|
15
|
+
action,
|
|
16
|
+
});
|
|
17
|
+
nextState = markTelegramSyncSliceFresh(nextState, "allowed-user", {
|
|
18
|
+
nowMs,
|
|
19
|
+
action,
|
|
20
|
+
});
|
|
21
|
+
nextState = markTelegramSyncSliceFresh(nextState, "bot-identity", {
|
|
22
|
+
nowMs,
|
|
23
|
+
action,
|
|
24
|
+
});
|
|
25
|
+
return nextState;
|
|
26
|
+
}
|
|
27
|
+
export function createTelegramSessionRestartThreadCleanupHandler(deps) {
|
|
28
|
+
return createTelegramManualThreadDisconnectHandler({
|
|
29
|
+
...deps,
|
|
30
|
+
workspaceOperationKind: "workspace.cleanup-session-restart",
|
|
31
|
+
async stopPolling() {
|
|
32
|
+
await deps.suspendPolling();
|
|
33
|
+
return "Telegram bridge suspended for session restart.";
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
export function createTelegramThreadDisconnectAssembly(deps) {
|
|
38
|
+
return {
|
|
39
|
+
disconnect: createTelegramManualThreadDisconnectHandler({
|
|
40
|
+
...deps,
|
|
41
|
+
workspaceOperationKind: "workspace.disconnect-thread",
|
|
42
|
+
stopPolling: deps.stopPolling,
|
|
43
|
+
}),
|
|
44
|
+
cleanupForSessionRestart: createTelegramSessionRestartThreadCleanupHandler({
|
|
45
|
+
...deps,
|
|
46
|
+
suspendPolling: deps.suspendPolling,
|
|
47
|
+
}),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
export function createTelegramManualThreadDisconnectHandler(deps) {
|
|
51
|
+
const operation = async () => {
|
|
52
|
+
const currentRecord = deps.getCurrentThreadRecord();
|
|
53
|
+
let cleanupPending = false;
|
|
54
|
+
if (currentRecord?.target.threadId) {
|
|
55
|
+
const isManualFollower = currentRecord.owner?.kind === "manual-follower";
|
|
56
|
+
const leaderEpoch = deps.getCurrentLeaderEpoch?.();
|
|
57
|
+
const ownsLeader = deps.getCurrentLeaderEpoch
|
|
58
|
+
? leaderEpoch !== undefined
|
|
59
|
+
: !isManualFollower;
|
|
60
|
+
if (isManualFollower && !ownsLeader) {
|
|
61
|
+
if (deps.disconnectFollowerThread) {
|
|
62
|
+
const disconnected = await deps.disconnectFollowerThread();
|
|
63
|
+
if (!disconnected) {
|
|
64
|
+
throw new Error("Telegram follower thread deletion requires a live leader registration.");
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
const target = currentRecord.target;
|
|
70
|
+
const runtimeGeneration = currentRecord.instanceId ?? deps.instanceId;
|
|
71
|
+
const intent = {
|
|
72
|
+
id: `cleanup:${deps.instanceId}:${runtimeGeneration}:${target.chatId}:${target.threadId}`,
|
|
73
|
+
owner: isManualFollower ? "manual-follower" : "leader",
|
|
74
|
+
instanceId: deps.instanceId,
|
|
75
|
+
runtimeGeneration,
|
|
76
|
+
...(currentRecord.profileKey
|
|
77
|
+
? { profileKey: currentRecord.profileKey }
|
|
78
|
+
: {}),
|
|
79
|
+
target,
|
|
80
|
+
requestedAtMs: (deps.getNowMs ?? Date.now)(),
|
|
81
|
+
};
|
|
82
|
+
const departingRecord = deps.topicTargetStore.list().find((record) => record.instanceId === currentRecord.instanceId &&
|
|
83
|
+
record.target.chatId === target.chatId && record.target.threadId === target.threadId);
|
|
84
|
+
const isCleanupTargetProtected = createTelegramCleanupTargetProtection(deps.topicTargetStore, departingRecord);
|
|
85
|
+
deps.topicTargetStore.upsertPendingCleanup(intent);
|
|
86
|
+
await deps.topicTargetStore.persist();
|
|
87
|
+
const cleanupPlan = ThreadReconciler.planThreadReconciliation({
|
|
88
|
+
nowMs: (deps.getNowMs ?? Date.now)(),
|
|
89
|
+
currentLeaderEpoch: leaderEpoch,
|
|
90
|
+
records: [],
|
|
91
|
+
pendingCleanups: [intent],
|
|
92
|
+
});
|
|
93
|
+
const cleanup = await ThreadReconciler.applyThreadReconciliationPlan(cleanupPlan, {
|
|
94
|
+
isCleanupTargetProtected,
|
|
95
|
+
callApi(method, body) {
|
|
96
|
+
return deps.callApi(method, body);
|
|
97
|
+
},
|
|
98
|
+
markStaleByTarget(targetToMark, syncStatus, lastSyncError) {
|
|
99
|
+
return deps.topicTargetStore.markStaleByTarget(targetToMark, syncStatus, lastSyncError);
|
|
100
|
+
},
|
|
101
|
+
removeCleanupIntentById(id) {
|
|
102
|
+
return deps.topicTargetStore.removePendingCleanup(id);
|
|
103
|
+
},
|
|
104
|
+
persist() {
|
|
105
|
+
return deps.topicTargetStore.persist();
|
|
106
|
+
},
|
|
107
|
+
getCurrentLeaderEpoch: deps.getCurrentLeaderEpoch,
|
|
108
|
+
recordRuntimeEvent: deps.recordRuntimeEvent,
|
|
109
|
+
});
|
|
110
|
+
if (cleanupPlan.actions.some((action) => isCleanupTargetProtected(action.target, action))) {
|
|
111
|
+
return "Thread disconnect superseded by a new binding.";
|
|
112
|
+
}
|
|
113
|
+
cleanupPending = Boolean(cleanup.incompleteActions?.length);
|
|
114
|
+
}
|
|
115
|
+
const leaderTarget = deps.getLeaderTarget();
|
|
116
|
+
if (leaderTarget?.chatId === currentRecord.target.chatId &&
|
|
117
|
+
leaderTarget.threadId === currentRecord.target.threadId) {
|
|
118
|
+
deps.clearLeaderTarget();
|
|
119
|
+
}
|
|
120
|
+
deps.setSyncState(markTelegramSyncSliceFresh(deps.getSyncState(), "target-bindings", {
|
|
121
|
+
nowMs: (deps.getNowMs ?? Date.now)(),
|
|
122
|
+
action: "manual-disconnect",
|
|
123
|
+
}));
|
|
124
|
+
}
|
|
125
|
+
const stopped = await deps.stopPolling();
|
|
126
|
+
return cleanupPending
|
|
127
|
+
? `${stopped} Telegram thread cleanup remains pending for the next leader.`
|
|
128
|
+
: stopped;
|
|
129
|
+
};
|
|
130
|
+
return () => deps.runWorkspaceOperation({
|
|
131
|
+
operationId: createTelegramWorkspaceAdmissionOperationId(),
|
|
132
|
+
operationKind: deps.workspaceOperationKind ?? "workspace.disconnect-thread",
|
|
133
|
+
scopes: [{ kind: "profile" }],
|
|
134
|
+
}, operation);
|
|
135
|
+
}
|
|
136
|
+
export function createTelegramLeaderHealthRuntime(deps) {
|
|
137
|
+
const intervalMs = deps.intervalMs ?? 60_000;
|
|
138
|
+
const getNowMs = deps.getNowMs ?? Date.now;
|
|
139
|
+
let interval;
|
|
140
|
+
let generation = 0;
|
|
141
|
+
let tickPromise;
|
|
142
|
+
const markFresh = () => {
|
|
143
|
+
let state = markTelegramSyncSliceFresh(deps.getSyncState(), "transport-health", { nowMs: getNowMs(), action: "leader-health-tick" });
|
|
144
|
+
state = markTelegramSyncSliceFresh(state, "bot-identity", {
|
|
145
|
+
nowMs: getNowMs(),
|
|
146
|
+
action: "leader-health-tick",
|
|
147
|
+
});
|
|
148
|
+
deps.setSyncState(state);
|
|
149
|
+
};
|
|
150
|
+
const markSuspect = (error) => {
|
|
151
|
+
deps.setSyncState(markTelegramSyncSliceSuspect(deps.getSyncState(), "transport-health", {
|
|
152
|
+
nowMs: getNowMs(),
|
|
153
|
+
reason: String(error),
|
|
154
|
+
action: "leader-health-tick",
|
|
155
|
+
}));
|
|
156
|
+
try {
|
|
157
|
+
deps.recordEvent("telegram", error, { phase: "leader-health-tick" });
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
// Health diagnostics cannot create an unhandled timer rejection.
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
const stop = () => {
|
|
164
|
+
generation += 1;
|
|
165
|
+
if (interval)
|
|
166
|
+
clearInterval(interval);
|
|
167
|
+
interval = undefined;
|
|
168
|
+
tickPromise = undefined;
|
|
169
|
+
};
|
|
170
|
+
const requestTick = () => {
|
|
171
|
+
if (tickPromise)
|
|
172
|
+
return tickPromise;
|
|
173
|
+
const expectedGeneration = generation;
|
|
174
|
+
let tracked;
|
|
175
|
+
tracked = Promise.resolve()
|
|
176
|
+
.then(deps.callGetMe)
|
|
177
|
+
.then(() => {
|
|
178
|
+
if (generation !== expectedGeneration)
|
|
179
|
+
return;
|
|
180
|
+
try {
|
|
181
|
+
markFresh();
|
|
182
|
+
}
|
|
183
|
+
catch (stateError) {
|
|
184
|
+
try {
|
|
185
|
+
deps.recordEvent("telegram", stateError, {
|
|
186
|
+
phase: "leader-health-state",
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
// State and diagnostic failure remain contained by this owner.
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}, (error) => {
|
|
194
|
+
if (generation !== expectedGeneration)
|
|
195
|
+
return;
|
|
196
|
+
try {
|
|
197
|
+
markSuspect(error);
|
|
198
|
+
}
|
|
199
|
+
catch (stateError) {
|
|
200
|
+
try {
|
|
201
|
+
deps.recordEvent("telegram", stateError, {
|
|
202
|
+
phase: "leader-health-state",
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// State and diagnostic failure remain contained by this owner.
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
})
|
|
210
|
+
.finally(() => {
|
|
211
|
+
if (tickPromise === tracked)
|
|
212
|
+
tickPromise = undefined;
|
|
213
|
+
});
|
|
214
|
+
tickPromise = tracked;
|
|
215
|
+
return tracked;
|
|
216
|
+
};
|
|
217
|
+
return {
|
|
218
|
+
start() {
|
|
219
|
+
stop();
|
|
220
|
+
interval = setInterval(() => {
|
|
221
|
+
void requestTick();
|
|
222
|
+
}, intervalMs);
|
|
223
|
+
interval.unref?.();
|
|
224
|
+
},
|
|
225
|
+
stop,
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
export function captureTelegramStaleTargetRequestRecovery(body, deps) {
|
|
229
|
+
const target = getTelegramTargetFromApiBody(body);
|
|
230
|
+
const epoch = deps.getCurrentLeaderEpoch();
|
|
231
|
+
if (!target || epoch === undefined)
|
|
232
|
+
return undefined;
|
|
233
|
+
const key = getTelegramTargetKey(target);
|
|
234
|
+
const record = deps.topicTargetStore.list().find((candidate) => getTelegramTargetKey(candidate.target) === key);
|
|
235
|
+
if (!record)
|
|
236
|
+
return undefined;
|
|
237
|
+
const generation = deps.getSessionGeneration();
|
|
238
|
+
const profile = deps.getProfileName();
|
|
239
|
+
const isAuthorityCurrent = () => deps.getCurrentLeaderEpoch() === epoch &&
|
|
240
|
+
deps.getSessionGeneration() === generation && deps.getProfileName() === profile;
|
|
241
|
+
const isCurrent = () => {
|
|
242
|
+
const current = deps.topicTargetStore.list().find((candidate) => getTelegramTargetKey(candidate.target) === key);
|
|
243
|
+
return isAuthorityCurrent() &&
|
|
244
|
+
current?.instanceId === record.instanceId && current?.profileKey === record.profileKey &&
|
|
245
|
+
current?.updatedAtMs === record.updatedAtMs && current?.createdAtMs === record.createdAtMs;
|
|
246
|
+
};
|
|
247
|
+
return async (error) => {
|
|
248
|
+
const requestTarget = getTelegramApiErrorRequestTarget(error);
|
|
249
|
+
if (!isTelegramStaleTargetHttpError(error) || !requestTarget ||
|
|
250
|
+
getTelegramTargetKey(requestTarget) !== key || !isCurrent())
|
|
251
|
+
return;
|
|
252
|
+
if (await recoverStaleTelegramTopicApiError({ chat_id: target.chatId, message_thread_id: target.threadId }, error, { ...deps, isCurrent, isAuthorityCurrent })) {
|
|
253
|
+
deps.onRecovered();
|
|
254
|
+
}
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
export function createTelegramStaleTopicApiErrorRecoveryRuntime(deps) {
|
|
258
|
+
return (apiBody, error) => recoverStaleTelegramTopicApiError(apiBody, error, deps);
|
|
259
|
+
}
|
|
260
|
+
export async function settleStaleTelegramTopicExecutionFailure(error, deps) {
|
|
261
|
+
const target = getTelegramApiErrorRequestTarget(error);
|
|
262
|
+
if (!target || !isTelegramTopicTargetStaleError(error))
|
|
263
|
+
return false;
|
|
264
|
+
await recoverStaleTelegramTopicApiError({ chat_id: target.chatId, message_thread_id: target.threadId }, error, deps);
|
|
265
|
+
return true;
|
|
266
|
+
}
|
|
267
|
+
export async function recoverStaleTelegramTopicApiError(apiBody, error, deps) {
|
|
268
|
+
const target = getTelegramTargetFromApiBody(apiBody);
|
|
269
|
+
if (!target ||
|
|
270
|
+
!isTelegramTopicTargetStaleError(error) ||
|
|
271
|
+
deps.isCurrent?.() === false)
|
|
272
|
+
return false;
|
|
273
|
+
const recover = async () => {
|
|
274
|
+
if (deps.isCurrent) {
|
|
275
|
+
if (!deps.topicTargetStore.invalidateTarget ||
|
|
276
|
+
!await deps.topicTargetStore.invalidateTarget(target, deps.isCurrent, String(error)))
|
|
277
|
+
return false;
|
|
278
|
+
if (deps.isAuthorityCurrent?.() === false)
|
|
279
|
+
return false;
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
await deps.topicTargetStore.load();
|
|
283
|
+
if (!deps.topicTargetStore.markStaleByTarget(target, "deleted", String(error)))
|
|
284
|
+
return false;
|
|
285
|
+
await deps.topicTargetStore.persist();
|
|
286
|
+
}
|
|
287
|
+
const nowMs = (deps.getNowMs ?? Date.now)();
|
|
288
|
+
let state = markTelegramSyncSliceSuspect(deps.getSyncState(), "topic-state", {
|
|
289
|
+
nowMs,
|
|
290
|
+
reason: "stale-api-error",
|
|
291
|
+
action: "topic-target-stale",
|
|
292
|
+
});
|
|
293
|
+
state = markTelegramSyncSliceSuspect(state, "transport-health", {
|
|
294
|
+
nowMs,
|
|
295
|
+
reason: "stale-api-error",
|
|
296
|
+
action: "topic-target-stale",
|
|
297
|
+
});
|
|
298
|
+
state = markTelegramSyncSliceSuspect(state, "target-bindings", {
|
|
299
|
+
nowMs,
|
|
300
|
+
reason: "stale-api-error",
|
|
301
|
+
action: "topic-target-stale",
|
|
302
|
+
});
|
|
303
|
+
deps.setSyncState(state);
|
|
304
|
+
deps.recordEvent("bus", error, {
|
|
305
|
+
phase: "topic-target-stale",
|
|
306
|
+
chatId: target.chatId,
|
|
307
|
+
threadId: target.threadId,
|
|
308
|
+
});
|
|
309
|
+
return true;
|
|
310
|
+
};
|
|
311
|
+
if (!deps.getWorkspaceAdmission)
|
|
312
|
+
return recover();
|
|
313
|
+
const admission = deps.getWorkspaceAdmission();
|
|
314
|
+
if (!admission) {
|
|
315
|
+
throw new Error("Telegram Workspace admission authority is unavailable.");
|
|
316
|
+
}
|
|
317
|
+
return runWithTelegramWorkspaceAdmissionsAsync({
|
|
318
|
+
ledger: admission,
|
|
319
|
+
operationId: createTelegramWorkspaceAdmissionOperationId(),
|
|
320
|
+
operationKind: "workspace.recover-stale-target",
|
|
321
|
+
scopes: [{ kind: "target", target }],
|
|
322
|
+
operation: recover,
|
|
323
|
+
onReleaseError(releaseError) {
|
|
324
|
+
deps.recordEvent("bus", releaseError, {
|
|
325
|
+
phase: "workspace-admission-release",
|
|
326
|
+
operationKind: "workspace.recover-stale-target",
|
|
327
|
+
chatId: target.chatId,
|
|
328
|
+
threadId: target.threadId,
|
|
329
|
+
});
|
|
330
|
+
},
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
export async function ensureTelegramLeaderThreadBinding(deps) {
|
|
334
|
+
const leaderEpoch = deps.getCurrentLeaderEpoch?.();
|
|
335
|
+
const assertLeaderEpoch = (phase) => {
|
|
336
|
+
if (deps.getCurrentLeaderEpoch &&
|
|
337
|
+
(leaderEpoch === undefined ||
|
|
338
|
+
deps.getCurrentLeaderEpoch() !== leaderEpoch)) {
|
|
339
|
+
throw new Error(`Telegram leader thread binding lost ownership (${phase}).`);
|
|
340
|
+
}
|
|
341
|
+
};
|
|
342
|
+
assertLeaderEpoch("start");
|
|
343
|
+
await deps.topicTargetStore.load();
|
|
344
|
+
assertLeaderEpoch("after-load");
|
|
345
|
+
const normalizedLeaderCwd = deps.cwd
|
|
346
|
+
? normalizeTelegramWorkspacePath(deps.cwd)
|
|
347
|
+
: undefined;
|
|
348
|
+
const leaderOwner = {
|
|
349
|
+
kind: "leader",
|
|
350
|
+
cwd: normalizedLeaderCwd,
|
|
351
|
+
instanceId: deps.instanceId,
|
|
352
|
+
...(deps.telegramProfile ? { telegramProfile: deps.telegramProfile } : {}),
|
|
353
|
+
};
|
|
354
|
+
const leaderProfileKey = getTelegramThreadOwnerKey(leaderOwner);
|
|
355
|
+
const legacyLeaderRecord = deps.topicTargetStore.getByProfileKey(leaderProfileKey);
|
|
356
|
+
const workspaceIdentity = normalizedLeaderCwd
|
|
357
|
+
? deps.topicTargetStore.claimWorkspaceIdentity(normalizedLeaderCwd, deps.instanceId, legacyLeaderRecord?.instanceId, { sessionId: deps.sessionId })
|
|
358
|
+
: undefined;
|
|
359
|
+
if (deps.cwd && !workspaceIdentity) {
|
|
360
|
+
throw new Error("Telegram Workspace identity is already claimed.");
|
|
361
|
+
}
|
|
362
|
+
const commitWorkspaceBinding = async (result) => {
|
|
363
|
+
if (!workspaceIdentity)
|
|
364
|
+
return result;
|
|
365
|
+
const committed = commitTelegramWorkspaceProvisionBinding({
|
|
366
|
+
store: deps.topicTargetStore,
|
|
367
|
+
instanceId: deps.instanceId,
|
|
368
|
+
profileKey: leaderProfileKey,
|
|
369
|
+
displayTitle: result.displayTitle,
|
|
370
|
+
binding: {
|
|
371
|
+
...workspaceIdentity,
|
|
372
|
+
target: { ...result.target },
|
|
373
|
+
...(result.threadName ? { threadName: result.threadName } : {}),
|
|
374
|
+
...(result.slot ? { slot: result.slot } : {}),
|
|
375
|
+
journalBindingKeys: [],
|
|
376
|
+
journalBindingsComplete: true,
|
|
377
|
+
updatedAtMs: deps.getNowMs?.() ?? Date.now(),
|
|
378
|
+
},
|
|
379
|
+
});
|
|
380
|
+
deps.topicTargetStore.markWorkspaceBindingActiveByTarget(result.target);
|
|
381
|
+
assertLeaderEpoch("before-workspace-persist");
|
|
382
|
+
await deps.topicTargetStore.persist();
|
|
383
|
+
assertLeaderEpoch("after-workspace-persist");
|
|
384
|
+
return { ...result, ...(committed.displayTitle ? { displayTitle: committed.displayTitle } : {}) };
|
|
385
|
+
};
|
|
386
|
+
try {
|
|
387
|
+
const unavailableTargetKeys = new Set([
|
|
388
|
+
...deps.topicTargetStore
|
|
389
|
+
.listSyncObservations()
|
|
390
|
+
.filter((observation) => observation.syncStatus === "deleted")
|
|
391
|
+
.map((observation) => getTelegramTargetKey(observation.target)),
|
|
392
|
+
...deps.topicTargetStore
|
|
393
|
+
.listPendingCleanups()
|
|
394
|
+
.map((intent) => getTelegramTargetKey(intent.target)),
|
|
395
|
+
]);
|
|
396
|
+
let invalidatedUnavailableTarget = false;
|
|
397
|
+
for (const record of deps.topicTargetStore.list()) {
|
|
398
|
+
if (record.instanceId !== deps.instanceId ||
|
|
399
|
+
!unavailableTargetKeys.has(getTelegramTargetKey(record.target))) {
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
402
|
+
invalidatedUnavailableTarget =
|
|
403
|
+
deps.topicTargetStore.markStaleByTarget(record.target) ||
|
|
404
|
+
invalidatedUnavailableTarget;
|
|
405
|
+
}
|
|
406
|
+
if (invalidatedUnavailableTarget) {
|
|
407
|
+
assertLeaderEpoch("before-unavailable-persist");
|
|
408
|
+
await deps.topicTargetStore.persist();
|
|
409
|
+
assertLeaderEpoch("after-unavailable-persist");
|
|
410
|
+
}
|
|
411
|
+
const persistedWorkspaceBinding = workspaceIdentity
|
|
412
|
+
? deps.topicTargetStore.getWorkspaceBinding(workspaceIdentity.cwd, workspaceIdentity.instanceSlot, workspaceIdentity.sessionId)
|
|
413
|
+
: undefined;
|
|
414
|
+
const legacyWorkspaceBinding = workspaceIdentity?.instanceSlot === "a" &&
|
|
415
|
+
!persistedWorkspaceBinding &&
|
|
416
|
+
typeof legacyLeaderRecord?.target.threadId === "number"
|
|
417
|
+
? {
|
|
418
|
+
...workspaceIdentity,
|
|
419
|
+
target: { ...legacyLeaderRecord.target },
|
|
420
|
+
...(legacyLeaderRecord.threadName
|
|
421
|
+
? { threadName: legacyLeaderRecord.threadName }
|
|
422
|
+
: {}),
|
|
423
|
+
...(legacyLeaderRecord.slot ? { slot: legacyLeaderRecord.slot } : {}),
|
|
424
|
+
updatedAtMs: legacyLeaderRecord.updatedAtMs,
|
|
425
|
+
}
|
|
426
|
+
: undefined;
|
|
427
|
+
const recoverableWorkspaceBinding = persistedWorkspaceBinding ?? legacyWorkspaceBinding;
|
|
428
|
+
const recoverableTargetKey = recoverableWorkspaceBinding
|
|
429
|
+
? getTelegramTargetKey(recoverableWorkspaceBinding.target)
|
|
430
|
+
: undefined;
|
|
431
|
+
const recoverableRecord = recoverableWorkspaceBinding
|
|
432
|
+
? deps.topicTargetStore
|
|
433
|
+
.list()
|
|
434
|
+
.find((record) => getTelegramTargetKey(record.target) === recoverableTargetKey)
|
|
435
|
+
: undefined;
|
|
436
|
+
const sameProcessWorkspaceBinding = !!recoverableRecord &&
|
|
437
|
+
(recoverableRecord.instanceId === deps.instanceId ||
|
|
438
|
+
isSameTelegramProcessInstance(recoverableRecord.instanceId, deps.instanceId));
|
|
439
|
+
if (recoverableWorkspaceBinding &&
|
|
440
|
+
!unavailableTargetKeys.has(recoverableTargetKey ?? "") &&
|
|
441
|
+
(!deps.forceFreshUnnamed || recoverableWorkspaceBinding.threadName) &&
|
|
442
|
+
(sameProcessWorkspaceBinding || deps.probeWorkspaceBinding)) {
|
|
443
|
+
let workspaceTargetVisible = sameProcessWorkspaceBinding;
|
|
444
|
+
if (!workspaceTargetVisible && deps.probeWorkspaceBinding) {
|
|
445
|
+
assertLeaderEpoch("before-workspace-probe");
|
|
446
|
+
try {
|
|
447
|
+
await deps.probeWorkspaceBinding(recoverableWorkspaceBinding);
|
|
448
|
+
assertLeaderEpoch("after-workspace-probe");
|
|
449
|
+
workspaceTargetVisible = true;
|
|
450
|
+
}
|
|
451
|
+
catch (error) {
|
|
452
|
+
if (!isTelegramTopicTargetStaleError(error))
|
|
453
|
+
throw error;
|
|
454
|
+
deps.topicTargetStore.markStaleByTarget(recoverableWorkspaceBinding.target, "deleted", error instanceof Error ? error.message : String(error));
|
|
455
|
+
await deps.topicTargetStore.persist();
|
|
456
|
+
deps.recordEvent("bus", error, {
|
|
457
|
+
phase: "leader-workspace-target-stale",
|
|
458
|
+
chatId: recoverableWorkspaceBinding.target.chatId,
|
|
459
|
+
threadId: recoverableWorkspaceBinding.target.threadId,
|
|
460
|
+
instanceId: deps.instanceId,
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
if (workspaceTargetVisible) {
|
|
465
|
+
const nowMs = deps.getNowMs?.() ?? Date.now();
|
|
466
|
+
const slot = workspaceIdentity?.slot;
|
|
467
|
+
if (!slot) {
|
|
468
|
+
throw new Error("Telegram Thread slot authority is unavailable.");
|
|
469
|
+
}
|
|
470
|
+
const recovered = await commitWorkspaceBinding({
|
|
471
|
+
target: { ...recoverableWorkspaceBinding.target },
|
|
472
|
+
slot,
|
|
473
|
+
...(recoverableWorkspaceBinding.threadName
|
|
474
|
+
? { threadName: recoverableWorkspaceBinding.threadName }
|
|
475
|
+
: {}),
|
|
476
|
+
reused: true,
|
|
477
|
+
});
|
|
478
|
+
deps.topicTargetStore.upsert({
|
|
479
|
+
profileKey: leaderProfileKey,
|
|
480
|
+
owner: leaderOwner,
|
|
481
|
+
target: { ...recovered.target },
|
|
482
|
+
status: "active",
|
|
483
|
+
createdAtMs: recoverableRecord?.createdAtMs ?? nowMs,
|
|
484
|
+
updatedAtMs: nowMs,
|
|
485
|
+
...(recovered.threadName ? { threadName: recovered.threadName } : {}),
|
|
486
|
+
instanceId: deps.instanceId,
|
|
487
|
+
slot: recovered.slot,
|
|
488
|
+
syncStatus: "open",
|
|
489
|
+
lastSyncObservedAtMs: nowMs,
|
|
490
|
+
lastReconcileAction: "leader-workspace-binding-recovered",
|
|
491
|
+
});
|
|
492
|
+
assertLeaderEpoch("before-recovered-record-persist");
|
|
493
|
+
await deps.topicTargetStore.persist();
|
|
494
|
+
assertLeaderEpoch("after-recovered-record-persist");
|
|
495
|
+
deps.recordEvent("telegram", "Leader thread preserved after Workspace recovery", {
|
|
496
|
+
phase: "leader-thread-reused",
|
|
497
|
+
instanceId: deps.instanceId,
|
|
498
|
+
chatId: recovered.target.chatId,
|
|
499
|
+
threadId: recovered.target.threadId,
|
|
500
|
+
slot: recovered.slot,
|
|
501
|
+
});
|
|
502
|
+
return recovered;
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
const priorTargets = deps.topicTargetStore.list().filter((record) => {
|
|
506
|
+
return (record.instanceId === deps.instanceId &&
|
|
507
|
+
(record.status === "active" || record.status === "starting"));
|
|
508
|
+
});
|
|
509
|
+
// Short-circuit: when the instance already has an active thread and we are not
|
|
510
|
+
// force-freshing, reuse it without re-provisioning. A thread belongs to the
|
|
511
|
+
// live instance binding, not to one transient Pi session lifecycle.
|
|
512
|
+
if (!deps.forceFreshUnnamed && priorTargets.length > 0) {
|
|
513
|
+
const record = priorTargets[0];
|
|
514
|
+
deps.recordEvent("telegram", "Leader thread preserved after session lifecycle change", {
|
|
515
|
+
phase: "leader-thread-reused",
|
|
516
|
+
instanceId: deps.instanceId,
|
|
517
|
+
chatId: record.target.chatId,
|
|
518
|
+
threadId: record.target.threadId,
|
|
519
|
+
slot: record.slot,
|
|
520
|
+
});
|
|
521
|
+
assertLeaderEpoch("before-reuse");
|
|
522
|
+
if (!record.slot) {
|
|
523
|
+
throw new Error("Telegram Thread slot authority is unavailable.");
|
|
524
|
+
}
|
|
525
|
+
return await commitWorkspaceBinding({
|
|
526
|
+
target: record.target,
|
|
527
|
+
slot: record.slot,
|
|
528
|
+
...(record.threadName ? { threadName: record.threadName } : {}),
|
|
529
|
+
reused: true,
|
|
530
|
+
});
|
|
531
|
+
}
|
|
532
|
+
let forcedUnnamedStale = false;
|
|
533
|
+
if (deps.forceFreshUnnamed) {
|
|
534
|
+
for (const record of priorTargets) {
|
|
535
|
+
const isLeaderOwned = record.owner?.kind === "leader" ||
|
|
536
|
+
(!record.owner &&
|
|
537
|
+
(record.profileKey.startsWith("cwd:") ||
|
|
538
|
+
record.profileKey.startsWith("leader:")));
|
|
539
|
+
if (!isLeaderOwned)
|
|
540
|
+
continue;
|
|
541
|
+
if (record.threadName)
|
|
542
|
+
continue;
|
|
543
|
+
forcedUnnamedStale =
|
|
544
|
+
deps.topicTargetStore.markStaleByTarget(record.target) ||
|
|
545
|
+
forcedUnnamedStale;
|
|
546
|
+
deps.recordEvent("telegram", "Unnamed leader thread binding refreshed", {
|
|
547
|
+
phase: "leader-thread-force-fresh-unnamed",
|
|
548
|
+
instanceId: deps.instanceId,
|
|
549
|
+
chatId: record.target.chatId,
|
|
550
|
+
threadId: record.target.threadId,
|
|
551
|
+
slot: record.slot,
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
if (forcedUnnamedStale)
|
|
555
|
+
await deps.topicTargetStore.persist();
|
|
556
|
+
}
|
|
557
|
+
assertLeaderEpoch("before-provision");
|
|
558
|
+
const ownTarget = await provisionOwnBusTopic({
|
|
559
|
+
getAllowedUserId: deps.getAllowedUserId,
|
|
560
|
+
instanceId: deps.instanceId,
|
|
561
|
+
cwd: normalizedLeaderCwd,
|
|
562
|
+
telegramProfile: deps.telegramProfile,
|
|
563
|
+
getCurrentLeaderEpoch: deps.getCurrentLeaderEpoch,
|
|
564
|
+
getThreadReconciliationMachineState: deps.getThreadReconciliationMachineState,
|
|
565
|
+
recordThreadReconciliationPlan: deps.recordThreadReconciliationPlan,
|
|
566
|
+
store: deps.topicTargetStore,
|
|
567
|
+
callApi: deps.callApi,
|
|
568
|
+
getNowMs: deps.getNowMs,
|
|
569
|
+
getRandom: deps.getRandom,
|
|
570
|
+
requestedThreadName: recoverableWorkspaceBinding?.threadName ?? deps.requestedThreadName,
|
|
571
|
+
workspaceBindingKey: workspaceIdentity?.bindingKey,
|
|
572
|
+
preferredSlot: workspaceIdentity?.slot,
|
|
573
|
+
resolveInitialWorkspaceDisplayTitle: deps.resolveInitialWorkspaceDisplayTitle,
|
|
574
|
+
recordEvent: deps.recordEvent,
|
|
575
|
+
});
|
|
576
|
+
assertLeaderEpoch("after-provision");
|
|
577
|
+
if (!ownTarget)
|
|
578
|
+
return undefined;
|
|
579
|
+
const replacementPlan = ThreadReconciler.planThreadReconciliation({
|
|
580
|
+
nowMs: Date.now(),
|
|
581
|
+
currentLeaderEpoch: deps.getCurrentLeaderEpoch?.(),
|
|
582
|
+
previousState: deps.getThreadReconciliationMachineState?.(),
|
|
583
|
+
records: priorTargets,
|
|
584
|
+
pendingProvisions: deps.topicTargetStore.listPendingProvisions(),
|
|
585
|
+
replacedBindings: [
|
|
586
|
+
{
|
|
587
|
+
instanceId: deps.instanceId,
|
|
588
|
+
replacementTarget: ownTarget.target,
|
|
589
|
+
},
|
|
590
|
+
],
|
|
591
|
+
});
|
|
592
|
+
deps.recordThreadReconciliationPlan?.(replacementPlan);
|
|
593
|
+
await ThreadReconciler.applyThreadReconciliationPlan(replacementPlan, {
|
|
594
|
+
isCleanupTargetProtected: createTelegramCleanupTargetProtection(deps.topicTargetStore),
|
|
595
|
+
callApi: deps.callApi,
|
|
596
|
+
markStaleByTarget: (target, syncStatus, lastSyncError) => deps.topicTargetStore.markStaleByTarget(target, syncStatus, lastSyncError),
|
|
597
|
+
persist: () => deps.topicTargetStore.persist(),
|
|
598
|
+
removePendingProvisionById: (id) => deps.topicTargetStore.removePendingProvision(id),
|
|
599
|
+
getCurrentLeaderEpoch: deps.getCurrentLeaderEpoch,
|
|
600
|
+
recordRuntimeEvent: deps.recordEvent,
|
|
601
|
+
});
|
|
602
|
+
assertLeaderEpoch("before-final-persist");
|
|
603
|
+
await deps.topicTargetStore.persist();
|
|
604
|
+
assertLeaderEpoch("after-final-persist");
|
|
605
|
+
return await commitWorkspaceBinding(ownTarget);
|
|
606
|
+
}
|
|
607
|
+
finally {
|
|
608
|
+
deps.topicTargetStore.releaseWorkspaceClaim(deps.instanceId);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
export const TELEGRAM_SYNC_SLICE_TARGET_BINDINGS = "target-bindings";
|
|
612
|
+
export const TELEGRAM_SYNC_SLICES = [
|
|
613
|
+
"bot-identity",
|
|
614
|
+
"bot-capabilities",
|
|
615
|
+
"pairing",
|
|
616
|
+
"allowed-user",
|
|
617
|
+
"topic-capability",
|
|
618
|
+
"topic-state",
|
|
619
|
+
TELEGRAM_SYNC_SLICE_TARGET_BINDINGS,
|
|
620
|
+
"reservations",
|
|
621
|
+
"transport-health",
|
|
622
|
+
];
|
|
623
|
+
export function createUnknownTelegramSyncState() {
|
|
624
|
+
return Object.fromEntries(TELEGRAM_SYNC_SLICES.map((slice) => [slice, { status: "unknown" }]));
|
|
625
|
+
}
|
|
626
|
+
export function createTelegramConfigSyncPersister(deps) {
|
|
627
|
+
return async (config) => {
|
|
628
|
+
await deps.persist(config);
|
|
629
|
+
deps.markConfigChange("config-persist");
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
export function createTelegramSyncStateRuntime(initialState = createUnknownTelegramSyncState()) {
|
|
633
|
+
let state = initialState;
|
|
634
|
+
return {
|
|
635
|
+
getState: () => state,
|
|
636
|
+
setState(nextState) {
|
|
637
|
+
state = nextState;
|
|
638
|
+
},
|
|
639
|
+
markConfigChange(action) {
|
|
640
|
+
state = markTelegramConfigSyncChange(state, action);
|
|
641
|
+
},
|
|
642
|
+
markSliceFresh(slice, options) {
|
|
643
|
+
state = markTelegramSyncSliceFresh(state, slice, options);
|
|
644
|
+
},
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
export function createTelegramProvisioningActivityRuntime() {
|
|
648
|
+
let activeCount = 0;
|
|
649
|
+
return {
|
|
650
|
+
isActive: () => activeCount > 0,
|
|
651
|
+
start() {
|
|
652
|
+
activeCount += 1;
|
|
653
|
+
},
|
|
654
|
+
end() {
|
|
655
|
+
activeCount = Math.max(0, activeCount - 1);
|
|
656
|
+
},
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
const RECONCILE_TRIGGERS = new Set([
|
|
660
|
+
"startup",
|
|
661
|
+
"reload",
|
|
662
|
+
"topic-lifecycle",
|
|
663
|
+
"stale-api-error",
|
|
664
|
+
"setup-change",
|
|
665
|
+
"pairing-change",
|
|
666
|
+
"follower-register",
|
|
667
|
+
"follower-prune",
|
|
668
|
+
"status-request",
|
|
669
|
+
"leader-health-tick",
|
|
670
|
+
]);
|
|
671
|
+
export function shouldReconcileTelegramSync(trigger) {
|
|
672
|
+
return RECONCILE_TRIGGERS.has(trigger);
|
|
673
|
+
}
|
|
674
|
+
export function markTelegramSyncSliceSuspect(state, slice, input) {
|
|
675
|
+
return {
|
|
676
|
+
...state,
|
|
677
|
+
[slice]: {
|
|
678
|
+
...(state[slice] ?? { status: "unknown" }),
|
|
679
|
+
status: "suspect",
|
|
680
|
+
suspectAtMs: input.nowMs,
|
|
681
|
+
reason: input.reason,
|
|
682
|
+
lastReconcileAction: input.action,
|
|
683
|
+
},
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
export function markTelegramSyncSliceFresh(state, slice, input) {
|
|
687
|
+
return {
|
|
688
|
+
...state,
|
|
689
|
+
[slice]: {
|
|
690
|
+
status: "fresh",
|
|
691
|
+
updatedAtMs: input.nowMs,
|
|
692
|
+
lastReconcileAction: input.action,
|
|
693
|
+
},
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
export function createTelegramObservedTopicLifecycleSyncHandler(deps) {
|
|
697
|
+
const syncTopicLifecycle = createTelegramTopicLifecycleSyncHandler(deps);
|
|
698
|
+
const operation = async (lifecycle) => {
|
|
699
|
+
const nowMs = deps.getNowMs ?? Date.now;
|
|
700
|
+
deps.assertExecutionCurrent?.(lifecycle.message);
|
|
701
|
+
deps.setSyncState(markTelegramSyncSliceSuspect(deps.getSyncState(), "topic-state", {
|
|
702
|
+
nowMs: nowMs(),
|
|
703
|
+
reason: `topic-${lifecycle.kind}`,
|
|
704
|
+
action: "topic-lifecycle",
|
|
705
|
+
}));
|
|
706
|
+
await syncTopicLifecycle(lifecycle);
|
|
707
|
+
deps.assertExecutionCurrent?.(lifecycle.message);
|
|
708
|
+
deps.setSyncState(markTelegramSyncSliceFresh(deps.getSyncState(), "topic-state", {
|
|
709
|
+
nowMs: nowMs(),
|
|
710
|
+
action: "topic-lifecycle",
|
|
711
|
+
}));
|
|
712
|
+
};
|
|
713
|
+
return (lifecycle) => deps.runWorkspaceOperation({
|
|
714
|
+
operationId: createTelegramWorkspaceAdmissionOperationId(),
|
|
715
|
+
operationKind: "workspace.sync-topic-lifecycle",
|
|
716
|
+
scopes: [{ kind: "profile" }],
|
|
717
|
+
}, () => operation(lifecycle));
|
|
718
|
+
}
|
|
719
|
+
export function createTelegramTopicLifecycleSyncHandler(deps) {
|
|
720
|
+
return async (lifecycle) => {
|
|
721
|
+
deps.assertExecutionCurrent?.(lifecycle.message);
|
|
722
|
+
await deps.topicTargetStore.load();
|
|
723
|
+
deps.assertExecutionCurrent?.(lifecycle.message);
|
|
724
|
+
const nowMs = Date.now();
|
|
725
|
+
const plan = ThreadReconciler.planThreadReconciliation({
|
|
726
|
+
nowMs,
|
|
727
|
+
currentLeaderEpoch: deps.getCurrentLeaderEpoch?.(),
|
|
728
|
+
previousState: deps.getThreadReconciliationMachineState?.(),
|
|
729
|
+
records: deps.topicTargetStore.list(),
|
|
730
|
+
reservations: deps.topicTargetStore.listReservations(),
|
|
731
|
+
pendingProvisions: deps.topicTargetStore.listPendingProvisions(),
|
|
732
|
+
observations: [
|
|
733
|
+
{
|
|
734
|
+
target: lifecycle.target,
|
|
735
|
+
syncStatus: lifecycle.kind === "closed" ? "closed" : "open",
|
|
736
|
+
observedAtMs: nowMs,
|
|
737
|
+
},
|
|
738
|
+
],
|
|
739
|
+
});
|
|
740
|
+
deps.recordThreadReconciliationPlan?.(plan);
|
|
741
|
+
const result = await ThreadReconciler.applyThreadReconciliationPlan(plan, {
|
|
742
|
+
markActiveByTarget: (target) => deps.topicTargetStore.markActiveByTarget(target),
|
|
743
|
+
markStaleByTarget: (target, syncStatus, lastSyncError) => deps.topicTargetStore.markStaleByTarget(target, syncStatus, lastSyncError),
|
|
744
|
+
persist: () => deps.topicTargetStore.persist(),
|
|
745
|
+
removePendingProvisionById: (id) => deps.topicTargetStore.removePendingProvision(id),
|
|
746
|
+
getCurrentLeaderEpoch: deps.getCurrentLeaderEpoch,
|
|
747
|
+
recordRuntimeEvent: deps.recordEvent,
|
|
748
|
+
});
|
|
749
|
+
const changed = result.changed;
|
|
750
|
+
if (lifecycle.kind === "created" && deps.isBusEnabled()) {
|
|
751
|
+
const target = lifecycle.target;
|
|
752
|
+
const isKnownInRecords = deps.topicTargetStore.list().some((record) => {
|
|
753
|
+
return (record.target.chatId === target.chatId &&
|
|
754
|
+
record.target.threadId === target.threadId);
|
|
755
|
+
});
|
|
756
|
+
const isKnownInReservations = deps.topicTargetStore
|
|
757
|
+
.listReservations()
|
|
758
|
+
.some((reservation) => {
|
|
759
|
+
return (reservation.target.chatId === target.chatId &&
|
|
760
|
+
reservation.target.threadId === target.threadId);
|
|
761
|
+
});
|
|
762
|
+
if (!isKnownInRecords && !isKnownInReservations) {
|
|
763
|
+
deps.recordEvent?.("telegram", deps.isTopicProvisioningActive?.()
|
|
764
|
+
? "Telegram unknown topic creation observed during provisioning"
|
|
765
|
+
: "Telegram unknown topic creation observed", {
|
|
766
|
+
phase: deps.isTopicProvisioningActive?.()
|
|
767
|
+
? "topic-lifecycle-provisioning-skip"
|
|
768
|
+
: "topic-lifecycle-unknown-created-observed",
|
|
769
|
+
chatId: target.chatId,
|
|
770
|
+
threadId: target.threadId,
|
|
771
|
+
});
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
deps.recordEvent?.("telegram", "Telegram topic lifecycle update", {
|
|
775
|
+
phase: "topic-lifecycle",
|
|
776
|
+
lifecycle: lifecycle.kind,
|
|
777
|
+
chatId: lifecycle.target.chatId,
|
|
778
|
+
threadId: lifecycle.target.threadId,
|
|
779
|
+
changed,
|
|
780
|
+
});
|
|
781
|
+
};
|
|
782
|
+
}
|