@ouro.bot/cli 0.1.0-alpha.81 → 0.1.0-alpha.810

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 (623) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +154 -23
  3. package/RepairGuide.ouro/agent.json +5 -0
  4. package/RepairGuide.ouro/psyche/IDENTITY.md +19 -0
  5. package/RepairGuide.ouro/psyche/SOUL.md +55 -0
  6. package/RepairGuide.ouro/skills/diagnose-broken-remote.md +63 -0
  7. package/RepairGuide.ouro/skills/diagnose-stacked-typed-issues.md +35 -0
  8. package/RepairGuide.ouro/skills/diagnose-sync-blocked.md +54 -0
  9. package/RepairGuide.ouro/skills/diagnose-vault-expired.md +60 -0
  10. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +4 -2
  11. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +2 -2
  12. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
  13. package/assets/bluebubbles-host +264 -0
  14. package/changelog.json +5177 -16
  15. package/deploy/unraid/Dockerfile +48 -0
  16. package/deploy/unraid/README.txt +3032 -0
  17. package/deploy/unraid/audit-container-spec.sh +4 -0
  18. package/deploy/unraid/container-runtime.json +1 -0
  19. package/deploy/unraid/docker-man-template-transaction.mjs +526 -0
  20. package/deploy/unraid/docker-man-template-xml.cjs +105 -0
  21. package/deploy/unraid/migrate-sanctuary-bundle.mjs +60 -0
  22. package/deploy/unraid/ouro-events/bootstrap-spool.sh +92 -0
  23. package/deploy/unraid/ouro-events/emit-event.mjs +310 -0
  24. package/deploy/unraid/ouro-events/emit-usenet-event.sh +54 -0
  25. package/deploy/unraid/ouro-events/install-usenet-guard.sh +435 -0
  26. package/deploy/unraid/ouro-events/usenet-health.sh +176 -0
  27. package/deploy/unraid/sanctuary-acceptance-adapter.sh +43 -0
  28. package/deploy/unraid/sanctuary-acceptance-contract.json +198 -0
  29. package/deploy/unraid/sanctuary-acceptance-harness.sh +23 -0
  30. package/deploy/unraid/sanctuary-deployment-target.mjs +709 -0
  31. package/deploy/unraid/sanctuary-safe-rename.py +24 -0
  32. package/deploy/unraid/sanctuary-unit16-host-broker.mjs +2030 -0
  33. package/deploy/unraid/sanctuary-unit16-run.sh +560 -0
  34. package/deploy/unraid/sanctuary-unit18-target-audit.sh +27 -0
  35. package/deploy/unraid/sanctuary.ouro/agent.json +24 -0
  36. package/deploy/unraid/sanctuary.ouro/arc/README.md +3 -0
  37. package/deploy/unraid/sanctuary.ouro/bundle-meta.json +5 -0
  38. package/deploy/unraid/sanctuary.ouro/habits/sanctuary-health.md +8 -0
  39. package/deploy/unraid/sanctuary.ouro/provider-readiness.json +11 -0
  40. package/deploy/unraid/sanctuary.ouro/psyche/ASPIRATIONS.md +5 -0
  41. package/deploy/unraid/sanctuary.ouro/psyche/IDENTITY.md +5 -0
  42. package/deploy/unraid/sanctuary.ouro/psyche/LORE.md +11 -0
  43. package/deploy/unraid/sanctuary.ouro/psyche/SOUL.md +25 -0
  44. package/deploy/unraid/sanctuary.ouro/psyche/TACIT.md +11 -0
  45. package/deploy/unraid/sanctuary.ouro/state/policy/steward.json +7 -0
  46. package/deploy/unraid/sanctuary.ouro/tool-profiles.json +23 -0
  47. package/deploy/unraid/sanctuary.xml +27 -0
  48. package/dist/a2a/card.js +57 -0
  49. package/dist/a2a/client.js +180 -0
  50. package/dist/a2a/config.js +50 -0
  51. package/dist/a2a/delegation-stores.js +56 -0
  52. package/dist/a2a/did-resolution.js +93 -0
  53. package/dist/a2a/identity.js +170 -0
  54. package/dist/a2a/inbound-share.js +107 -0
  55. package/dist/a2a/mission-result-wire.js +196 -0
  56. package/dist/a2a/onboarding.js +115 -0
  57. package/dist/a2a/pin-store.js +132 -0
  58. package/dist/a2a/seen-ledger.js +120 -0
  59. package/dist/a2a/server.js +604 -0
  60. package/dist/a2a/task-store.js +69 -0
  61. package/dist/a2a/transport.js +45 -0
  62. package/dist/a2a/types.js +3 -0
  63. package/dist/arc/attention-types.js +8 -0
  64. package/dist/arc/cares.js +360 -0
  65. package/dist/arc/episodes.js +118 -0
  66. package/dist/arc/evolution.js +490 -0
  67. package/dist/arc/flight-recorder.js +1034 -0
  68. package/dist/arc/intentions.js +134 -0
  69. package/dist/arc/json-store.js +117 -0
  70. package/dist/arc/obligations.js +386 -0
  71. package/dist/arc/packets.js +336 -0
  72. package/dist/arc/presence.js +185 -0
  73. package/dist/arc/task-lifecycle.js +57 -0
  74. package/dist/commerce/store.js +755 -0
  75. package/dist/commerce/types.js +3 -0
  76. package/dist/heart/active-work.js +820 -29
  77. package/dist/heart/agent-entry.js +252 -3
  78. package/dist/heart/approval-files.js +113 -0
  79. package/dist/heart/approval-store.js +950 -0
  80. package/dist/heart/attachments/image-normalize.js +194 -0
  81. package/dist/heart/attachments/materialize.js +97 -0
  82. package/dist/heart/attachments/originals.js +88 -0
  83. package/dist/heart/attachments/render.js +29 -0
  84. package/dist/heart/attachments/sources/bluebubbles.js +156 -0
  85. package/dist/heart/attachments/sources/cli-local-file.js +78 -0
  86. package/dist/heart/attachments/sources/index.js +18 -0
  87. package/dist/heart/attachments/sources/telegram.js +71 -0
  88. package/dist/heart/attachments/store.js +132 -0
  89. package/dist/heart/attachments/types.js +93 -0
  90. package/dist/heart/auth/auth-flow.js +495 -0
  91. package/dist/heart/autonomy-budget.js +478 -0
  92. package/dist/heart/awaiting/await-alert.js +148 -0
  93. package/dist/heart/awaiting/await-expiry.js +143 -0
  94. package/dist/heart/awaiting/await-loader.js +91 -0
  95. package/dist/heart/awaiting/await-parser.js +149 -0
  96. package/dist/heart/awaiting/await-runtime-state.js +100 -0
  97. package/dist/heart/awaiting/await-scheduler.js +382 -0
  98. package/dist/heart/background-operations.js +281 -0
  99. package/dist/heart/bridges/manager.js +137 -17
  100. package/dist/heart/bridges/store.js +14 -2
  101. package/dist/heart/bundle-state.js +168 -0
  102. package/dist/heart/commitments.js +64 -18
  103. package/dist/heart/config-registry.js +331 -0
  104. package/dist/heart/config.js +142 -129
  105. package/dist/heart/context-loss-gauntlet.js +387 -0
  106. package/dist/heart/context-loss-sentinel.js +1157 -0
  107. package/dist/heart/core.js +2105 -574
  108. package/dist/heart/cross-chat-delivery.js +9 -22
  109. package/dist/heart/daemon/agent-config-check.js +451 -0
  110. package/dist/heart/daemon/agent-discovery.js +185 -3
  111. package/dist/heart/daemon/agent-service.js +542 -0
  112. package/dist/heart/daemon/agentic-repair.js +547 -0
  113. package/dist/heart/daemon/await-private-wake.js +59 -0
  114. package/dist/heart/daemon/bluebubbles-cli-integration.js +71 -0
  115. package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
  116. package/dist/heart/daemon/bluebubbles-host-protocol.js +433 -0
  117. package/dist/heart/daemon/bluebubbles-host.js +273 -0
  118. package/dist/heart/daemon/boot-sync-probe.js +197 -0
  119. package/dist/heart/daemon/cadence.js +180 -0
  120. package/dist/heart/daemon/cli-defaults.js +854 -0
  121. package/dist/heart/daemon/cli-desk.js +322 -0
  122. package/dist/heart/daemon/cli-exec.js +8822 -0
  123. package/dist/heart/daemon/cli-help.js +716 -0
  124. package/dist/heart/daemon/cli-parse.js +2771 -0
  125. package/dist/heart/daemon/cli-render-doctor.js +57 -0
  126. package/dist/heart/daemon/cli-render.js +777 -0
  127. package/dist/heart/daemon/cli-types.js +8 -0
  128. package/dist/heart/daemon/connect-bay.js +323 -0
  129. package/dist/heart/daemon/container-credential-bootstrap.js +273 -0
  130. package/dist/heart/daemon/container-healthcheck.js +112 -0
  131. package/dist/heart/daemon/container-runtime.js +97 -0
  132. package/dist/heart/daemon/container-spec-auditor-main.js +170 -0
  133. package/dist/heart/daemon/container-spec-auditor.js +236 -0
  134. package/dist/heart/daemon/daemon-bootstrap-startup.js +115 -0
  135. package/dist/heart/daemon/daemon-cli.js +29 -2317
  136. package/dist/heart/daemon/daemon-entry.js +921 -11
  137. package/dist/heart/daemon/daemon-health.js +186 -0
  138. package/dist/heart/daemon/daemon-rollup.js +57 -0
  139. package/dist/heart/daemon/daemon-runtime-sync.js +88 -13
  140. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  141. package/dist/heart/daemon/daemon.js +2073 -104
  142. package/dist/heart/daemon/dns-workflow.js +394 -0
  143. package/dist/heart/daemon/doctor-types.js +8 -0
  144. package/dist/heart/daemon/doctor.js +1755 -0
  145. package/dist/heart/daemon/freshness.js +345 -0
  146. package/dist/heart/daemon/habit-private-wake.js +61 -0
  147. package/dist/heart/daemon/health-monitor.js +142 -11
  148. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  149. package/dist/heart/daemon/hooks/bundle-meta.js +135 -1
  150. package/dist/heart/daemon/http-health-probe.js +80 -0
  151. package/dist/heart/daemon/human-command-screens.js +234 -0
  152. package/dist/heart/daemon/human-readiness.js +114 -0
  153. package/dist/heart/daemon/inner-status.js +78 -0
  154. package/dist/heart/daemon/interactive-repair.js +394 -0
  155. package/dist/heart/daemon/launchd.js +37 -8
  156. package/dist/heart/daemon/log-tailer.js +79 -10
  157. package/dist/heart/daemon/logs-prune.js +132 -0
  158. package/dist/heart/daemon/mcp-canary.js +524 -0
  159. package/dist/heart/daemon/message-router.js +65 -5
  160. package/dist/heart/daemon/migrate-to-desk.js +848 -0
  161. package/dist/heart/daemon/os-cron-deps.js +135 -0
  162. package/dist/heart/daemon/os-cron.js +22 -14
  163. package/dist/heart/daemon/ouro-bot-entry.js +4 -2
  164. package/dist/heart/daemon/ouro-entry.js +3 -1
  165. package/dist/heart/daemon/plugin-cli.js +432 -0
  166. package/dist/heart/daemon/process-manager.js +733 -61
  167. package/dist/heart/daemon/provider-discovery.js +137 -0
  168. package/dist/heart/daemon/provider-ping-progress.js +83 -0
  169. package/dist/heart/daemon/prunable-bundle.js +144 -0
  170. package/dist/heart/daemon/pulse.js +511 -0
  171. package/dist/heart/daemon/readiness-repair.js +365 -0
  172. package/dist/heart/daemon/run-hooks.js +2 -0
  173. package/dist/heart/daemon/runtime-logging.js +47 -14
  174. package/dist/heart/daemon/runtime-metadata.js +2 -30
  175. package/dist/heart/daemon/runtime-mode.js +13 -2
  176. package/dist/heart/daemon/safe-mode.js +161 -0
  177. package/dist/heart/daemon/sanctuary-acceptance-adapter.js +2609 -0
  178. package/dist/heart/daemon/sanctuary-acceptance-harness.js +1866 -0
  179. package/dist/heart/daemon/sanctuary-acceptance-marker.js +267 -0
  180. package/dist/heart/daemon/sanctuary-acceptance-scenarios.js +976 -0
  181. package/dist/heart/daemon/sanctuary-bundle-migration.js +728 -0
  182. package/dist/heart/daemon/sanctuary-package-management.js +103 -0
  183. package/dist/heart/daemon/sanctuary-scheduler-liveness.js +227 -0
  184. package/dist/heart/daemon/sanctuary-scheduler-origin.js +132 -0
  185. package/dist/heart/daemon/sense-manager.js +689 -48
  186. package/dist/heart/daemon/session-id-resolver.js +131 -0
  187. package/dist/heart/daemon/skill-management-installer.js +1 -1
  188. package/dist/heart/daemon/socket-client.js +180 -12
  189. package/dist/heart/daemon/stale-bundle-prune.js +113 -0
  190. package/dist/heart/daemon/startup-tui.js +339 -0
  191. package/dist/heart/daemon/supercronic-supervisor.js +282 -0
  192. package/dist/heart/daemon/task-scheduler.js +117 -39
  193. package/dist/heart/daemon/terminal-ui.js +499 -0
  194. package/dist/heart/daemon/thoughts.js +138 -54
  195. package/dist/heart/daemon/up-progress.js +366 -0
  196. package/dist/heart/daemon/vault-items.js +56 -0
  197. package/dist/heart/delegation.js +1 -4
  198. package/dist/heart/external-events/router.js +1414 -0
  199. package/dist/heart/frontend-approval-runtime.js +432 -0
  200. package/dist/heart/frontend-journal.js +215 -0
  201. package/dist/heart/frontend-session-service.js +237 -0
  202. package/dist/heart/frontend-socket-client.js +154 -0
  203. package/dist/heart/frontend-socket.js +400 -0
  204. package/dist/heart/habits/habit-cancel.js +810 -0
  205. package/dist/heart/habits/habit-lifecycle.js +1327 -0
  206. package/dist/heart/habits/habit-migration.js +194 -0
  207. package/dist/heart/habits/habit-parser.js +278 -0
  208. package/dist/heart/habits/habit-runtime-state.js +93 -0
  209. package/dist/heart/habits/habit-scheduler.js +653 -0
  210. package/dist/heart/habits/habit-session-summary.js +318 -0
  211. package/dist/heart/habits/habit-session.js +636 -0
  212. package/dist/heart/{daemon → hatch}/hatch-flow.js +55 -62
  213. package/dist/heart/{daemon → hatch}/hatch-specialist.js +6 -8
  214. package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
  215. package/dist/heart/{daemon → hatch}/specialist-tools.js +53 -18
  216. package/dist/heart/identity.js +184 -59
  217. package/dist/heart/kept-notes.js +289 -0
  218. package/dist/heart/kicks.js +1 -1
  219. package/dist/heart/machine-identity.js +161 -0
  220. package/dist/heart/mail-import-discovery.js +356 -0
  221. package/dist/heart/mailbox/mailbox-http-hooks.js +98 -0
  222. package/dist/heart/mailbox/mailbox-http-response.js +7 -0
  223. package/dist/heart/mailbox/mailbox-http-routes.js +398 -0
  224. package/dist/heart/mailbox/mailbox-http-static.js +103 -0
  225. package/dist/heart/mailbox/mailbox-http-transport.js +129 -0
  226. package/dist/heart/mailbox/mailbox-http.js +99 -0
  227. package/dist/heart/mailbox/mailbox-read.js +39 -0
  228. package/dist/heart/mailbox/mailbox-types.js +27 -0
  229. package/dist/heart/mailbox/mailbox-view.js +197 -0
  230. package/dist/heart/mailbox/readers/agent-machine.js +428 -0
  231. package/dist/heart/mailbox/readers/continuity-readers.js +329 -0
  232. package/dist/heart/mailbox/readers/mail.js +375 -0
  233. package/dist/heart/mailbox/readers/runtime-readers.js +823 -0
  234. package/dist/heart/mailbox/readers/sessions.js +231 -0
  235. package/dist/heart/mailbox/readers/shared.js +111 -0
  236. package/dist/heart/mcp/mcp-server.js +696 -0
  237. package/dist/heart/migrate-config.js +100 -0
  238. package/dist/heart/model-capabilities.js +11 -0
  239. package/dist/heart/orientation-frame.js +345 -0
  240. package/dist/heart/platform.js +81 -0
  241. package/dist/heart/primitives.js +1 -1
  242. package/dist/heart/private-runtime/decision-renderer.js +158 -0
  243. package/dist/heart/private-runtime/index.js +18 -0
  244. package/dist/heart/private-runtime/ledger.js +221 -0
  245. package/dist/heart/private-runtime/policy.js +252 -0
  246. package/dist/heart/private-runtime/types.js +2 -0
  247. package/dist/heart/progress-story.js +1 -1
  248. package/dist/heart/provider-attempt.js +137 -0
  249. package/dist/heart/provider-binding-resolver.js +273 -0
  250. package/dist/heart/provider-credentials.js +449 -0
  251. package/dist/heart/provider-failover.js +311 -0
  252. package/dist/heart/provider-models.js +96 -0
  253. package/dist/heart/provider-ping.js +265 -0
  254. package/dist/heart/provider-readiness-cache.js +119 -0
  255. package/dist/heart/provider-visibility.js +188 -0
  256. package/dist/heart/providers/anthropic-token.js +131 -0
  257. package/dist/heart/providers/anthropic.js +173 -60
  258. package/dist/heart/providers/azure.js +23 -11
  259. package/dist/heart/providers/error-classification.js +127 -0
  260. package/dist/heart/providers/github-copilot.js +34 -38
  261. package/dist/heart/providers/minimax-vlm.js +189 -0
  262. package/dist/heart/providers/minimax.js +26 -8
  263. package/dist/heart/providers/openai-codex-token.js +349 -0
  264. package/dist/heart/providers/openai-codex.js +55 -40
  265. package/dist/heart/providers/openai-compatible.js +177 -0
  266. package/dist/heart/run-ledger.js +247 -0
  267. package/dist/heart/runtime-capability-check.js +170 -0
  268. package/dist/heart/runtime-credentials.js +607 -0
  269. package/dist/heart/runtime-cwd.js +87 -0
  270. package/dist/heart/sense-truth.js +22 -4
  271. package/dist/heart/session-activity.js +92 -24
  272. package/dist/heart/session-events.js +1433 -0
  273. package/dist/heart/session-playback-cli-main.js +5 -0
  274. package/dist/heart/session-playback-cli.js +36 -0
  275. package/dist/heart/session-playback.js +231 -0
  276. package/dist/heart/session-redaction-repair-cli-main.js +31 -0
  277. package/dist/heart/session-redaction-repair.js +543 -0
  278. package/dist/heart/session-stats-cli-main.js +5 -0
  279. package/dist/heart/session-stats.js +182 -0
  280. package/dist/heart/session-transcript.js +133 -0
  281. package/dist/heart/start-of-turn-packet.js +372 -0
  282. package/dist/heart/steward-policy.js +302 -0
  283. package/dist/heart/streaming.js +604 -207
  284. package/dist/heart/structured-output.js +196 -0
  285. package/dist/heart/sync-classification.js +176 -0
  286. package/dist/heart/sync.js +449 -0
  287. package/dist/heart/target-resolution.js +11 -7
  288. package/dist/heart/tempo.js +93 -0
  289. package/dist/heart/temporal-view.js +41 -0
  290. package/dist/heart/timeouts.js +101 -0
  291. package/dist/heart/tool-activity-callbacks.js +59 -0
  292. package/dist/heart/tool-approval.js +342 -0
  293. package/dist/heart/tool-description.js +155 -0
  294. package/dist/heart/tool-friction.js +55 -0
  295. package/dist/heart/tool-loop.js +200 -0
  296. package/dist/heart/turn-context.js +482 -0
  297. package/dist/heart/turn-execution-lease.js +30 -0
  298. package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +6 -5
  299. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  300. package/dist/heart/versioning/ouro-path-installer.js +432 -0
  301. package/dist/heart/versioning/ouro-recovery-launcher.js +76 -0
  302. package/dist/heart/versioning/ouro-version-manager.js +409 -0
  303. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  304. package/dist/heart/{daemon → versioning}/update-checker.js +6 -1
  305. package/dist/heart/versioning/update-hooks.js +154 -0
  306. package/dist/heart/versioning/version-intent.js +114 -0
  307. package/dist/heart/work-card.js +380 -0
  308. package/dist/mailbox-ui/assets/index-Du_9G9WO.css +1 -0
  309. package/dist/mailbox-ui/assets/index-J8zDwHmE.js +1 -0
  310. package/dist/mailbox-ui/assets/vendor-CcN1XpQ9.js +61 -0
  311. package/dist/mailbox-ui/index.html +16 -0
  312. package/dist/mailroom/attention.js +167 -0
  313. package/dist/mailroom/autonomy.js +209 -0
  314. package/dist/mailroom/blob-store.js +889 -0
  315. package/dist/mailroom/body-cache.js +61 -0
  316. package/dist/mailroom/cache-sync-cli.js +58 -0
  317. package/dist/mailroom/core.js +803 -0
  318. package/dist/mailroom/entry.js +160 -0
  319. package/dist/mailroom/file-store.js +568 -0
  320. package/dist/mailroom/hosted-cache-sync.js +384 -0
  321. package/dist/mailroom/mbox-import.js +393 -0
  322. package/dist/mailroom/migration.js +164 -0
  323. package/dist/mailroom/outbound.js +380 -0
  324. package/dist/mailroom/policy.js +263 -0
  325. package/dist/mailroom/reader.js +292 -0
  326. package/dist/mailroom/search-cache.js +513 -0
  327. package/dist/mailroom/search-relevance.js +319 -0
  328. package/dist/mailroom/smtp-ingress.js +176 -0
  329. package/dist/mailroom/source-state.js +176 -0
  330. package/dist/mailroom/thread.js +109 -0
  331. package/dist/mailroom/travel-extract.js +89 -0
  332. package/dist/mind/bundle-manifest.js +24 -2
  333. package/dist/mind/context.js +279 -111
  334. package/dist/mind/desk-section.js +362 -0
  335. package/dist/mind/diary-integrity.js +60 -0
  336. package/dist/mind/{memory.js → diary.js} +68 -77
  337. package/dist/mind/embedding-provider.js +60 -0
  338. package/dist/mind/file-state.js +179 -0
  339. package/dist/mind/{associative-recall.js → note-search.js} +47 -58
  340. package/dist/mind/obligation-steering.js +227 -0
  341. package/dist/mind/pending.js +34 -8
  342. package/dist/mind/prompt-budget.js +479 -0
  343. package/dist/mind/prompt-refresh.js +3 -2
  344. package/dist/mind/prompt.js +1059 -187
  345. package/dist/mind/provenance-trust.js +26 -0
  346. package/dist/mind/record-paths.js +312 -0
  347. package/dist/mind/scrutiny.js +173 -0
  348. package/dist/mind/session-transaction.js +564 -0
  349. package/dist/nerves/cli-logging.js +7 -1
  350. package/dist/nerves/coverage/audit-rules.js +15 -6
  351. package/dist/nerves/coverage/audit.js +28 -2
  352. package/dist/nerves/coverage/cli.js +1 -1
  353. package/dist/nerves/coverage/contract.js +5 -5
  354. package/dist/nerves/coverage/file-completeness.js +139 -5
  355. package/dist/nerves/coverage/run-artifacts.js +55 -35
  356. package/dist/nerves/coverage/source-scanner.js +4 -4
  357. package/dist/nerves/event-buffer.js +111 -0
  358. package/dist/nerves/index.js +293 -9
  359. package/dist/nerves/observation.js +20 -0
  360. package/dist/nerves/redact.js +79 -0
  361. package/dist/nerves/review/cli-main.js +5 -0
  362. package/dist/nerves/review/cli.js +156 -0
  363. package/dist/nerves/review/core.js +159 -0
  364. package/dist/nerves/runtime.js +17 -1
  365. package/dist/repertoire/ado-client.js +15 -56
  366. package/dist/repertoire/ado-semantic.js +16 -10
  367. package/dist/repertoire/api-client.js +97 -0
  368. package/dist/repertoire/bitwarden-store.js +1045 -0
  369. package/dist/repertoire/bundle-templates.js +71 -0
  370. package/dist/repertoire/bw-installer.js +180 -0
  371. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  372. package/dist/repertoire/coding/context-pack.js +331 -0
  373. package/dist/repertoire/coding/feedback.js +218 -30
  374. package/dist/repertoire/coding/index.js +15 -3
  375. package/dist/repertoire/coding/manager.js +166 -10
  376. package/dist/repertoire/coding/spawner.js +58 -11
  377. package/dist/repertoire/coding/tools.js +242 -14
  378. package/dist/repertoire/coding/workbench-client.js +272 -0
  379. package/dist/repertoire/coding/workbench-manager.js +346 -0
  380. package/dist/repertoire/commerce-errors.js +109 -0
  381. package/dist/repertoire/commerce-self-test.js +156 -0
  382. package/dist/repertoire/credential-access.js +178 -0
  383. package/dist/repertoire/desk/classifier.js +362 -0
  384. package/dist/repertoire/duffel-client.js +185 -0
  385. package/dist/repertoire/github-client.js +14 -55
  386. package/dist/repertoire/graph-client.js +11 -52
  387. package/dist/repertoire/guardrails.js +811 -18
  388. package/dist/repertoire/mcp-client.js +44 -3
  389. package/dist/repertoire/mcp-manager.js +331 -27
  390. package/dist/repertoire/mcp-tools.js +83 -0
  391. package/dist/repertoire/plugin-mcp.js +175 -0
  392. package/dist/repertoire/plugins.js +253 -0
  393. package/dist/repertoire/relationship-authorization.js +194 -0
  394. package/dist/repertoire/shell-sessions.js +133 -0
  395. package/dist/repertoire/skills.js +48 -4
  396. package/dist/repertoire/stripe-client.js +131 -0
  397. package/dist/repertoire/tool-arguments.js +148 -0
  398. package/dist/repertoire/tool-results.js +29 -0
  399. package/dist/repertoire/tools-a2a.js +699 -0
  400. package/dist/repertoire/tools-attachments.js +322 -0
  401. package/dist/repertoire/tools-awaiting.js +575 -0
  402. package/dist/repertoire/tools-base.js +82 -1139
  403. package/dist/repertoire/tools-bluebubbles.js +2 -0
  404. package/dist/repertoire/tools-bridge.js +144 -0
  405. package/dist/repertoire/tools-bundle.js +993 -0
  406. package/dist/repertoire/tools-commerce.js +253 -0
  407. package/dist/repertoire/tools-config.js +186 -0
  408. package/dist/repertoire/tools-continuity.js +575 -0
  409. package/dist/repertoire/tools-credential.js +383 -0
  410. package/dist/repertoire/tools-evolution.js +527 -0
  411. package/dist/repertoire/tools-files.js +344 -0
  412. package/dist/repertoire/tools-flight.js +290 -0
  413. package/dist/repertoire/tools-flow.js +119 -0
  414. package/dist/repertoire/tools-github.js +3 -8
  415. package/dist/repertoire/tools-habits.js +103 -0
  416. package/dist/repertoire/tools-mail.js +1828 -0
  417. package/dist/repertoire/tools-notes.js +485 -0
  418. package/dist/repertoire/tools-obligations.js +182 -0
  419. package/dist/repertoire/tools-orientation.js +31 -0
  420. package/dist/repertoire/tools-record.js +482 -0
  421. package/dist/repertoire/tools-rsvp.js +139 -0
  422. package/dist/repertoire/tools-runtime.js +150 -0
  423. package/dist/repertoire/tools-session.js +1108 -0
  424. package/dist/repertoire/tools-shell.js +133 -0
  425. package/dist/repertoire/tools-steward-policy.js +95 -0
  426. package/dist/repertoire/tools-stripe.js +224 -0
  427. package/dist/repertoire/tools-surface.js +369 -0
  428. package/dist/repertoire/tools-teams.js +12 -39
  429. package/dist/repertoire/tools-telegram-contacts.js +37 -0
  430. package/dist/repertoire/tools-travel.js +125 -0
  431. package/dist/repertoire/tools-trip.js +982 -0
  432. package/dist/repertoire/tools-unraid.js +484 -0
  433. package/dist/repertoire/tools-user-profile.js +146 -0
  434. package/dist/repertoire/tools-vault.js +40 -0
  435. package/dist/repertoire/tools-voice.js +145 -0
  436. package/dist/repertoire/tools.js +377 -80
  437. package/dist/repertoire/travel-api-client.js +360 -0
  438. package/dist/repertoire/unraid-client.js +155 -0
  439. package/dist/repertoire/unraid-restart.js +212 -0
  440. package/dist/repertoire/user-profile.js +131 -0
  441. package/dist/repertoire/vault-setup.js +246 -0
  442. package/dist/repertoire/vault-unlock.js +628 -0
  443. package/dist/rsvp/aisleplanner-client.js +354 -0
  444. package/dist/rsvp/cli.js +926 -0
  445. package/dist/rsvp/config.js +327 -0
  446. package/dist/rsvp/cutover.js +433 -0
  447. package/dist/rsvp/diagnostics.js +232 -0
  448. package/dist/rsvp/diff-renderer.js +176 -0
  449. package/dist/rsvp/habit-policy.js +110 -0
  450. package/dist/rsvp/habit-stage.js +123 -0
  451. package/dist/rsvp/incident-bundle.js +97 -0
  452. package/dist/rsvp/migration.js +204 -0
  453. package/dist/rsvp/native-habit-runner.js +630 -0
  454. package/dist/rsvp/outbound-state.js +628 -0
  455. package/dist/rsvp/query.js +118 -0
  456. package/dist/rsvp/replay.js +233 -0
  457. package/dist/rsvp/snapshot.js +232 -0
  458. package/dist/rsvp/spend-ledger.js +175 -0
  459. package/dist/scripts/claude-code-hook.js +41 -0
  460. package/dist/scripts/claude-code-stop-hook.js +47 -0
  461. package/dist/senses/a2a-entry.js +115 -0
  462. package/dist/senses/acp-server.js +386 -0
  463. package/dist/senses/attention-queue.js +186 -0
  464. package/dist/senses/await-turn-message.js +58 -0
  465. package/dist/senses/bluebubbles/active-turns.js +216 -0
  466. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  467. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  468. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +355 -40
  469. package/dist/senses/bluebubbles/context-packet.js +207 -0
  470. package/dist/senses/bluebubbles/context-smoke.js +144 -0
  471. package/dist/senses/bluebubbles/entry.js +90 -0
  472. package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +21 -3
  473. package/dist/senses/bluebubbles/index.js +3839 -0
  474. package/dist/senses/bluebubbles/latest-turn.js +311 -0
  475. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -71
  476. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +159 -28
  477. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +24 -13
  478. package/dist/senses/bluebubbles/outbound-state.js +308 -0
  479. package/dist/senses/bluebubbles/processed-log.js +133 -0
  480. package/dist/senses/bluebubbles/reaction-policy.js +47 -0
  481. package/dist/senses/bluebubbles/replay.js +284 -0
  482. package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +30 -2
  483. package/dist/senses/bluebubbles/semantic-receipts.js +1986 -0
  484. package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
  485. package/dist/senses/bluebubbles/webhook-registration.js +228 -0
  486. package/dist/senses/bluebubbles-meta-guard.js +39 -0
  487. package/dist/senses/cli/bracketed-paste.js +82 -0
  488. package/dist/senses/cli/image-paste.js +287 -0
  489. package/dist/senses/cli/image-ref-navigation.js +75 -0
  490. package/dist/senses/cli/ink-app.js +156 -0
  491. package/dist/senses/cli/inline-diff.js +64 -0
  492. package/dist/senses/cli/input-keys.js +174 -0
  493. package/dist/senses/cli/kill-ring.js +86 -0
  494. package/dist/senses/cli/message-list.js +51 -0
  495. package/dist/senses/cli/ouro-tui.js +607 -0
  496. package/dist/senses/cli/spinner-imperative.js +135 -0
  497. package/dist/senses/cli/spinner.js +101 -0
  498. package/dist/senses/cli/status-line.js +60 -0
  499. package/dist/senses/cli/streaming-markdown.js +526 -0
  500. package/dist/senses/cli/tool-display.js +85 -0
  501. package/dist/senses/cli/tool-render.js +85 -0
  502. package/dist/senses/cli/tui-store.js +241 -0
  503. package/dist/senses/cli/virtual-list.js +35 -0
  504. package/dist/senses/cli-entry.js +60 -8
  505. package/dist/senses/cli.js +600 -317
  506. package/dist/senses/commands.js +66 -3
  507. package/dist/senses/context-packet-ledger.js +371 -0
  508. package/dist/senses/context-packets.js +154 -0
  509. package/dist/senses/habit-lifecycle-guard.js +24 -0
  510. package/dist/senses/habit-turn-message.js +160 -0
  511. package/dist/senses/ingress-evidence.js +2 -0
  512. package/dist/senses/mail-entry.js +66 -0
  513. package/dist/senses/mail.js +524 -0
  514. package/dist/senses/pipeline.js +1115 -204
  515. package/dist/senses/private-runtime-worker.js +859 -0
  516. package/dist/senses/private-runtime.js +1496 -0
  517. package/dist/senses/proactive-content-guard.js +51 -0
  518. package/dist/senses/sanctuary-download-credit-presentation.js +31 -0
  519. package/dist/senses/sanctuary-full-visibility-contract.js +302 -0
  520. package/dist/senses/sanctuary-grounding.js +118 -0
  521. package/dist/senses/sanctuary-health-acceptance-probe-entry.js +12 -0
  522. package/dist/senses/sanctuary-health-acceptance-probe.js +774 -0
  523. package/dist/senses/sanctuary-health-runner.js +101 -0
  524. package/dist/senses/sanctuary-health.js +408 -0
  525. package/dist/senses/sanctuary-install-state-contract.js +126 -0
  526. package/dist/senses/sanctuary-interactive-control.js +306 -0
  527. package/dist/senses/sanctuary-media-catalog-contract.js +235 -0
  528. package/dist/senses/sanctuary-media-optimization.js +514 -0
  529. package/dist/senses/sanctuary-runtime.js +338 -0
  530. package/dist/senses/sanctuary-sab.js +85 -0
  531. package/dist/senses/sanctuary-storage-optimization-contract.js +32 -0
  532. package/dist/senses/shared-turn.js +756 -0
  533. package/dist/senses/surface-tool.js +109 -0
  534. package/dist/senses/teams-entry.js +60 -8
  535. package/dist/senses/teams.js +515 -217
  536. package/dist/senses/telegram-admission.js +711 -0
  537. package/dist/senses/telegram-approval-runtime.js +566 -0
  538. package/dist/senses/telegram-attachments.js +182 -0
  539. package/dist/senses/telegram-audit-ledger.js +220 -0
  540. package/dist/senses/telegram-client.js +1677 -0
  541. package/dist/senses/telegram-effect-adapter.js +859 -0
  542. package/dist/senses/telegram-entry.js +83 -0
  543. package/dist/senses/telegram.js +2045 -0
  544. package/dist/senses/trust-gate.js +104 -9
  545. package/dist/senses/voice/audio-playback.js +237 -0
  546. package/dist/senses/voice/audio-routing.js +119 -0
  547. package/dist/senses/voice/elevenlabs.js +202 -0
  548. package/dist/senses/voice/floor-control.js +431 -0
  549. package/dist/senses/voice/floor-controller.js +115 -0
  550. package/dist/senses/voice/golden-path.js +116 -0
  551. package/dist/senses/voice/index.js +29 -0
  552. package/dist/senses/voice/meeting.js +113 -0
  553. package/dist/senses/voice/outbound.js +190 -0
  554. package/dist/senses/voice/phone.js +33 -0
  555. package/dist/senses/voice/playback.js +139 -0
  556. package/dist/senses/voice/realtime-eval.js +496 -0
  557. package/dist/senses/voice/realtime-trace.js +531 -0
  558. package/dist/senses/voice/transcript.js +70 -0
  559. package/dist/senses/voice/turn.js +191 -0
  560. package/dist/senses/voice/twilio-phone-runtime.js +807 -0
  561. package/dist/senses/voice/twilio-phone.js +5081 -0
  562. package/dist/senses/voice/types.js +2 -0
  563. package/dist/senses/voice/whisper.js +161 -0
  564. package/dist/senses/voice-entry.js +81 -0
  565. package/dist/senses/voice-realtime-eval-command.js +99 -0
  566. package/dist/senses/voice-realtime-eval-entry.js +21 -0
  567. package/dist/senses/voice-twilio-entry.js +87 -0
  568. package/dist/trips/core.js +138 -0
  569. package/dist/trips/store.js +265 -0
  570. package/dist/util/frontmatter.js +69 -0
  571. package/npm-shrinkwrap.json +8138 -0
  572. package/package.json +72 -13
  573. package/skills/agent-commerce.md +113 -0
  574. package/skills/browser-navigation.md +117 -0
  575. package/skills/commerce-setup-guide.md +116 -0
  576. package/skills/commerce-setup.md +84 -0
  577. package/skills/configure-dev-tools.md +99 -0
  578. package/skills/travel-planning.md +138 -0
  579. package/dist/heart/daemon/auth-flow.js +0 -430
  580. package/dist/heart/daemon/ouro-path-installer.js +0 -260
  581. package/dist/heart/daemon/ouro-version-manager.js +0 -164
  582. package/dist/heart/daemon/update-hooks.js +0 -138
  583. package/dist/heart/obligations.js +0 -141
  584. package/dist/heart/safe-workspace.js +0 -228
  585. package/dist/heart/session-recall.js +0 -116
  586. package/dist/mind/friends/channel.js +0 -84
  587. package/dist/mind/friends/group-context.js +0 -144
  588. package/dist/mind/friends/resolver.js +0 -84
  589. package/dist/mind/friends/store-file.js +0 -190
  590. package/dist/mind/friends/store.js +0 -4
  591. package/dist/mind/friends/tokens.js +0 -26
  592. package/dist/mind/friends/trust-explanation.js +0 -74
  593. package/dist/mind/friends/types.js +0 -29
  594. package/dist/repertoire/tasks/board.js +0 -134
  595. package/dist/repertoire/tasks/index.js +0 -224
  596. package/dist/repertoire/tasks/lifecycle.js +0 -80
  597. package/dist/repertoire/tasks/middleware.js +0 -65
  598. package/dist/repertoire/tasks/parser.js +0 -173
  599. package/dist/repertoire/tasks/scanner.js +0 -132
  600. package/dist/repertoire/tasks/transitions.js +0 -144
  601. package/dist/senses/bluebubbles-entry.js +0 -13
  602. package/dist/senses/bluebubbles.js +0 -1181
  603. package/dist/senses/debug-activity.js +0 -154
  604. package/dist/senses/inner-dialog-worker.js +0 -90
  605. package/dist/senses/inner-dialog.js +0 -525
  606. package/subagents/README.md +0 -7
  607. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  608. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  609. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  610. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  611. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  612. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  613. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  614. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  615. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  616. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  617. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  618. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  619. /package/dist/{repertoire/tasks/types.js → heart/attachments/sources/adapter.js} +0 -0
  620. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  621. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  622. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  623. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -0,0 +1,859 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.FileTelegramEffectJournal = exports.FIXED_USENET_SYSTEM_FAILSAFE = exports.FIXED_ADMISSION_ACKNOWLEDGEMENT = void 0;
37
+ exports.prepareTelegramEffect = prepareTelegramEffect;
38
+ exports.executeTelegramEffect = executeTelegramEffect;
39
+ exports.createTelegramAuthorizedEffectExecutor = createTelegramAuthorizedEffectExecutor;
40
+ exports.reconcileTelegramSystemFailsafe = reconcileTelegramSystemFailsafe;
41
+ exports.sweepTelegramSystemFailsafes = sweepTelegramSystemFailsafes;
42
+ exports.recoverTelegramEffectOutbox = recoverTelegramEffectOutbox;
43
+ exports.createTelegramApprovalEffectPort = createTelegramApprovalEffectPort;
44
+ exports.recordTelegramEffectInSession = recordTelegramEffectInSession;
45
+ exports.appendTelegramArtifactEvents = appendTelegramArtifactEvents;
46
+ exports.appendTelegramInboundEvent = appendTelegramInboundEvent;
47
+ exports.recordTelegramEffectsInSession = recordTelegramEffectsInSession;
48
+ exports.resolveTelegramReply = resolveTelegramReply;
49
+ exports.resolveTelegramControlArtifact = resolveTelegramControlArtifact;
50
+ const crypto = __importStar(require("node:crypto"));
51
+ const fs = __importStar(require("node:fs"));
52
+ const path = __importStar(require("node:path"));
53
+ const runtime_1 = require("../nerves/runtime");
54
+ const router_1 = require("../heart/external-events/router");
55
+ const session_events_1 = require("../heart/session-events");
56
+ const session_transaction_1 = require("../mind/session-transaction");
57
+ const telegram_client_1 = require("./telegram-client");
58
+ exports.FIXED_ADMISSION_ACKNOWLEDGEMENT = "Thanks — I’ve asked Ari. Your message is still unread and unprocessed; I’ll only read it if Ari welcomes you in.";
59
+ exports.FIXED_USENET_SYSTEM_FAILSAFE = "Quick heads-up: I paused downloads because the server was discarding most of what it fetched. Nothing needs doing this second — I’ll investigate and follow up when I’m back online.";
60
+ const SYSTEM_FAILSAFE_UNAVAILABLE_MS = 2 * 60_000;
61
+ function isTransportOnlyControl(artifact) {
62
+ return artifact.authorClass === "control" && artifact.effect.kind !== "text";
63
+ }
64
+ function artifactId(idempotencyKey) {
65
+ return crypto.createHash("sha256").update(idempotencyKey).digest("hex");
66
+ }
67
+ function requireText(value, label) {
68
+ if (!value.trim())
69
+ throw new Error(`${label} must be nonempty`);
70
+ return value;
71
+ }
72
+ function preparedTexts(effect) {
73
+ if (effect.kind === "text" || effect.kind === "admission_ack")
74
+ return (0, telegram_client_1.splitTelegramText)(requireText(effect.text, "Telegram text"));
75
+ if (effect.kind === "card" || effect.kind === "edit")
76
+ return [requireText(effect.text, "Telegram text")];
77
+ return [effect.text?.trim() || null];
78
+ }
79
+ function renderTelegramButlerHtml(text) {
80
+ let html = "";
81
+ let cursor = 0;
82
+ const styledText = /`([^`\n]+)`|\*\*([\p{L}\p{N}](?:[^*_`\n]*\S)?)\*\*|\*([\p{L}\p{N}](?:[^*_`\n]*\S)?)\*|_([\p{L}\p{N}](?:[^*_`\n]*\S)?)_/gu;
83
+ for (const match of text.matchAll(styledText)) {
84
+ const gap = text.slice(cursor, match.index);
85
+ if (/[*_`]/u.test(gap))
86
+ return (0, telegram_client_1.escapeTelegramHtml)(text);
87
+ html += (0, telegram_client_1.escapeTelegramHtml)(gap);
88
+ if (match[1] !== undefined)
89
+ html += `<code>${(0, telegram_client_1.escapeTelegramHtml)(match[1])}</code>`;
90
+ else if (match[2] !== undefined || match[3] !== undefined)
91
+ html += `<b>${(0, telegram_client_1.escapeTelegramHtml)((match[2] ?? match[3]))}</b>`;
92
+ else
93
+ html += `<i>${(0, telegram_client_1.escapeTelegramHtml)(match[4])}</i>`;
94
+ cursor = match.index + match[0].length;
95
+ }
96
+ const tail = text.slice(cursor);
97
+ return /[*_`]/u.test(tail) ? (0, telegram_client_1.escapeTelegramHtml)(text) : html + (0, telegram_client_1.escapeTelegramHtml)(tail);
98
+ }
99
+ function assertEffectTarget(target, effect, idempotencyKey) {
100
+ if (target.kind === "admission_gate") {
101
+ if (effect.kind !== "admission_ack" || effect.text !== exports.FIXED_ADMISSION_ACKNOWLEDGEMENT || idempotencyKey !== `ack:${target.admissionId}`) {
102
+ throw new Error("admission targets allow only the fixed admission acknowledgement");
103
+ }
104
+ }
105
+ else if (effect.kind === "admission_ack") {
106
+ throw new Error("fixed admission acknowledgement requires an admission target");
107
+ }
108
+ }
109
+ function canonicalTarget(target) {
110
+ if (!target || typeof target !== "object")
111
+ throw new Error("Telegram effect artifact is invalid");
112
+ if (target.kind === "admission_gate")
113
+ return target;
114
+ return {
115
+ kind: "approved_relationship",
116
+ friendId: target.friendId,
117
+ sessionKey: target.sessionKey,
118
+ ...(target.requestId ? { requestId: target.requestId } : {}),
119
+ };
120
+ }
121
+ function boundedText(value, max = 50_000) {
122
+ return typeof value === "string" && value.length > 0 && Buffer.byteLength(value, "utf8") <= max;
123
+ }
124
+ function canonicalTime(value) {
125
+ return typeof value === "string" && Number.isFinite(Date.parse(value)) && new Date(value).toISOString() === value;
126
+ }
127
+ function validateArtifact(artifact, expectedId) {
128
+ const target = artifact?.target;
129
+ const effect = artifact?.effect;
130
+ const targetValid = target?.kind === "approved_relationship"
131
+ ? boundedText(target.friendId, 512) && boundedText(target.sessionKey, 512)
132
+ && (target.requestId === undefined || boundedText(target.requestId, 512))
133
+ : target?.kind === "admission_gate" && boundedText(target.admissionId, 512) && /^[1-9][0-9]*$/u.test(target.botId)
134
+ && /^[1-9][0-9]*$/u.test(target.userId) && /^[1-9][0-9]*$/u.test(target.chatId);
135
+ const effectValid = effect?.kind === "text" || effect?.kind === "admission_ack"
136
+ ? boundedText(effect.text, 150_000)
137
+ : effect?.kind === "card"
138
+ ? boundedText(effect.text) && Array.isArray(effect.buttons) && effect.buttons.length > 0 && effect.buttons.length <= 20
139
+ && effect.buttons.every((row) => Array.isArray(row) && row.length > 0 && row.length <= 8
140
+ && row.every((button) => boundedText(button?.text, 256) && boundedText(button?.callbackData, 64) && Buffer.byteLength(button.callbackData, "utf8") <= 64))
141
+ : effect?.kind === "edit"
142
+ ? Number.isSafeInteger(effect.messageId) && effect.messageId > 0 && boundedText(effect.text)
143
+ : effect?.kind === "callback_ack"
144
+ ? boundedText(effect.callbackQueryId, 512) && (effect.text === undefined || boundedText(effect.text, 512)) && (effect.showAlert === undefined || typeof effect.showAlert === "boolean")
145
+ : false;
146
+ let expectedTexts = [];
147
+ try {
148
+ if (targetValid && effectValid) {
149
+ assertEffectTarget(target, effect, artifact.idempotencyKey);
150
+ expectedTexts = preparedTexts(effect);
151
+ }
152
+ }
153
+ catch {
154
+ expectedTexts = [];
155
+ }
156
+ const partsValid = Array.isArray(artifact.parts) && artifact.parts.length === expectedTexts.length && artifact.parts.length > 0 && artifact.parts.length <= 100
157
+ && artifact.parts.every((part, index) => part.index === index && part.text === expectedTexts[index]
158
+ && ["prepared", "attempting", "accepted", "session_recorded", "indeterminate"].includes(part.state)
159
+ && canonicalTime(part.updatedAt)
160
+ && (part.acceptedAt === undefined || canonicalTime(part.acceptedAt))
161
+ && (part.sessionRecordedAt === undefined || canonicalTime(part.sessionRecordedAt))
162
+ && (part.messageId === undefined || (Number.isSafeInteger(part.messageId) && part.messageId > 0))
163
+ && (part.sessionEventId === undefined || boundedText(part.sessionEventId, 512))
164
+ && (part.attempts === undefined || (Number.isSafeInteger(part.attempts) && part.attempts >= 0 && part.attempts <= 100))
165
+ && (part.state === "session_recorded"
166
+ ? (boundedText(part.sessionEventId, 512) || (isTransportOnlyControl(artifact) && part.sessionEventId === undefined))
167
+ : part.sessionEventId === undefined)
168
+ && ((effect.kind === "callback_ack" || part.state === "prepared" || part.state === "attempting" || part.state === "indeterminate")
169
+ || (Number.isSafeInteger(part.messageId) && part.messageId > 0)));
170
+ if (artifact.schemaVersion !== 1 || artifact.id !== expectedId || artifact.id !== artifactId(artifact.idempotencyKey)
171
+ || !boundedText(artifact.idempotencyKey, 1024) || !targetValid || !effectValid || expectedTexts.length === 0
172
+ || !["butler", "control", "system_failsafe"].includes(artifact.authorClass)
173
+ || !boundedText(artifact.authorizationReceiptId, 1024) || !canonicalTime(artifact.authorizationExpiresAt)
174
+ || (artifact.obligationReturnId !== undefined && !boundedText(artifact.obligationReturnId, 512))
175
+ || !canonicalTime(artifact.createdAt) || !canonicalTime(artifact.updatedAt) || !partsValid) {
176
+ throw new Error("Telegram effect artifact is invalid");
177
+ }
178
+ }
179
+ class FileTelegramEffectJournal {
180
+ root;
181
+ directoryFd;
182
+ descriptorRoot;
183
+ rootDevice;
184
+ rootInode;
185
+ closed = false;
186
+ constructor(root) {
187
+ this.root = root;
188
+ if (fs.existsSync(root)) {
189
+ const stat = fs.lstatSync(root);
190
+ if (stat.isSymbolicLink())
191
+ throw new Error("Telegram effect journal root must not be a symbolic link");
192
+ if (!stat.isDirectory())
193
+ throw new Error("Telegram effect journal root must be a directory");
194
+ }
195
+ else {
196
+ fs.mkdirSync(root, { recursive: true, mode: 0o700 });
197
+ }
198
+ this.directoryFd = fs.openSync(root, fs.constants.O_RDONLY | fs.constants.O_DIRECTORY | fs.constants.O_NOFOLLOW);
199
+ this.descriptorRoot = root;
200
+ fs.fchmodSync(this.directoryFd, 0o700);
201
+ const identity = fs.fstatSync(this.directoryFd);
202
+ this.rootDevice = identity.dev;
203
+ this.rootInode = identity.ino;
204
+ const verified = fs.lstatSync(root);
205
+ if (!verified.isDirectory() || verified.isSymbolicLink() || (verified.mode & 0o777) !== 0o700)
206
+ throw new Error("Telegram effect journal root is unsafe");
207
+ }
208
+ artifactPath(id) {
209
+ if (this.closed)
210
+ throw new Error("Telegram effect journal is closed");
211
+ if (!/^[a-f0-9]{64}$/u.test(id))
212
+ throw new Error("Telegram effect artifact id is invalid");
213
+ return path.join(this.descriptorRoot, `${id}.json`);
214
+ }
215
+ assertRootIdentity() {
216
+ const current = fs.lstatSync(this.root);
217
+ if (current.isSymbolicLink() || !current.isDirectory() || current.dev !== this.rootDevice || current.ino !== this.rootInode) {
218
+ throw new Error("Telegram effect journal root identity changed");
219
+ }
220
+ }
221
+ coordinationPath(id) {
222
+ if (!/^[a-f0-9]{64}$/u.test(id))
223
+ throw new Error("Telegram effect artifact id is invalid");
224
+ this.assertRootIdentity();
225
+ return path.join(path.dirname(this.root), `${path.basename(this.root)}.coordination`, id);
226
+ }
227
+ close() {
228
+ if (this.closed)
229
+ return;
230
+ this.closed = true;
231
+ fs.closeSync(this.directoryFd);
232
+ }
233
+ read(id) {
234
+ this.assertRootIdentity();
235
+ const filePath = this.artifactPath(id);
236
+ const fd = fs.openSync(filePath, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
237
+ try {
238
+ this.assertRootIdentity();
239
+ const stat = fs.fstatSync(fd);
240
+ if (!stat.isFile() || stat.size < 2 || stat.size > 512 * 1024)
241
+ throw new Error("Telegram effect artifact is not a bounded regular file");
242
+ const raw = fs.readFileSync(fd, "utf8");
243
+ const artifact = JSON.parse(raw);
244
+ artifact.target = canonicalTarget(artifact.target);
245
+ validateArtifact(artifact, id);
246
+ if (raw.includes('"chatId"') && artifact.target.kind === "approved_relationship")
247
+ this.write(artifact);
248
+ return artifact;
249
+ }
250
+ finally {
251
+ fs.closeSync(fd);
252
+ }
253
+ }
254
+ readIfExists(id) {
255
+ const filePath = this.artifactPath(id);
256
+ return fs.existsSync(filePath) ? this.read(id) : null;
257
+ }
258
+ quarantineInvalid(id, reason) {
259
+ this.assertRootIdentity();
260
+ const filePath = this.artifactPath(id);
261
+ if (!fs.existsSync(filePath))
262
+ return;
263
+ const quarantineRoot = path.join(path.dirname(this.root), `${path.basename(this.root)}.quarantine`);
264
+ fs.mkdirSync(quarantineRoot, { recursive: true, mode: 0o700 });
265
+ fs.chmodSync(quarantineRoot, 0o700);
266
+ const quarantinePath = path.join(quarantineRoot, `${id}.${Date.now()}.${process.pid}.invalid.json`);
267
+ fs.renameSync(filePath, quarantinePath);
268
+ fs.chmodSync(quarantinePath, 0o600);
269
+ (0, runtime_1.emitNervesEvent)({
270
+ level: "error",
271
+ component: "senses",
272
+ event: "senses.telegram_effect_artifact_quarantined",
273
+ message: "quarantined invalid Telegram effect artifact",
274
+ meta: { artifactId: id, reason },
275
+ });
276
+ }
277
+ write(artifact) {
278
+ this.assertRootIdentity();
279
+ const filePath = this.artifactPath(artifact.id);
280
+ const tempPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
281
+ const fd = fs.openSync(tempPath, "wx", 0o600);
282
+ try {
283
+ this.assertRootIdentity();
284
+ fs.writeFileSync(fd, `${JSON.stringify(artifact, null, 2)}\n`, "utf8");
285
+ fs.fsyncSync(fd);
286
+ }
287
+ finally {
288
+ fs.closeSync(fd);
289
+ }
290
+ this.assertRootIdentity();
291
+ fs.renameSync(tempPath, filePath);
292
+ fs.chmodSync(filePath, 0o600);
293
+ fs.fsyncSync(this.directoryFd);
294
+ this.assertRootIdentity();
295
+ }
296
+ list() {
297
+ this.assertRootIdentity();
298
+ const entries = fs.readdirSync(this.descriptorRoot, { withFileTypes: true });
299
+ this.assertRootIdentity();
300
+ return entries
301
+ .filter((entry) => entry.isFile() && /^[a-f0-9]{64}\.json$/u.test(entry.name))
302
+ .map((entry) => this.read(entry.name.slice(0, -5)));
303
+ }
304
+ listRecoverable() {
305
+ this.assertRootIdentity();
306
+ const entries = fs.readdirSync(this.descriptorRoot, { withFileTypes: true });
307
+ this.assertRootIdentity();
308
+ const artifacts = [];
309
+ for (const entry of entries.filter((candidate) => candidate.isFile() && /^[a-f0-9]{64}\.json$/u.test(candidate.name))) {
310
+ const id = entry.name.slice(0, -5);
311
+ try {
312
+ artifacts.push(this.read(id));
313
+ }
314
+ catch (error) {
315
+ (0, runtime_1.emitNervesEvent)({
316
+ level: "error",
317
+ component: "senses",
318
+ event: "senses.telegram_effect_recovery_artifact_skipped",
319
+ message: "skipped invalid Telegram effect artifact during recovery",
320
+ meta: { artifactId: id, error: error instanceof Error ? error.message : String(error) },
321
+ });
322
+ }
323
+ }
324
+ return artifacts;
325
+ }
326
+ }
327
+ exports.FileTelegramEffectJournal = FileTelegramEffectJournal;
328
+ function prepareTelegramEffect(store, input) {
329
+ if (!input.authorization.allowed)
330
+ throw new Error(`Telegram effect authorization denied: ${input.authorization.reason}`);
331
+ if (!/^[1-9][0-9]*$/u.test(input.authorization.transport.chatId))
332
+ throw new Error("Telegram effect authorization returned an invalid transport route");
333
+ const idempotencyKey = requireText(input.idempotencyKey, "Telegram effect idempotency key");
334
+ const target = canonicalTarget(input.target);
335
+ assertEffectTarget(target, input.effect, idempotencyKey);
336
+ if (target.kind === "admission_gate" && input.authorization.transport.chatId !== target.chatId)
337
+ throw new Error("Telegram admission transport route changed");
338
+ const id = artifactId(idempotencyKey);
339
+ let existing = null;
340
+ try {
341
+ existing = store.readIfExists(id);
342
+ }
343
+ catch (error) {
344
+ if (!isTransportOnlyControl({ authorClass: input.authorClass, effect: input.effect, target }))
345
+ throw error;
346
+ store.quarantineInvalid(id, error instanceof Error ? error.message : String(error));
347
+ }
348
+ if (existing) {
349
+ if (JSON.stringify(existing.target) !== JSON.stringify(target)
350
+ || existing.authorClass !== input.authorClass
351
+ || JSON.stringify(existing.effect) !== JSON.stringify(input.effect)
352
+ || existing.obligationReturnId !== input.obligationReturnId) {
353
+ throw new Error("Telegram effect idempotency key was reused for a different effect");
354
+ }
355
+ return existing;
356
+ }
357
+ const now = input.now ?? new Date().toISOString();
358
+ const artifact = {
359
+ schemaVersion: 1,
360
+ id,
361
+ idempotencyKey,
362
+ target,
363
+ authorClass: input.authorClass,
364
+ effect: input.effect,
365
+ authorizationReceiptId: input.authorization.receiptId,
366
+ authorizationExpiresAt: input.authorization.expiresAt,
367
+ ...(input.obligationReturnId ? { obligationReturnId: input.obligationReturnId } : {}),
368
+ parts: preparedTexts(input.effect).map((text, index) => ({ index, text, state: "prepared", updatedAt: now })),
369
+ createdAt: now,
370
+ updatedAt: now,
371
+ };
372
+ store.write(artifact);
373
+ (0, runtime_1.emitNervesEvent)({ component: "senses", event: "senses.telegram_effect_prepared", message: "prepared Telegram effect", meta: { artifactId: id, effectKind: input.effect.kind, targetKind: input.target.kind, partCount: artifact.parts.length } });
374
+ return artifact;
375
+ }
376
+ function canonicalMessageId(result) {
377
+ const messageId = result && typeof result === "object" && !Array.isArray(result) ? result.message_id : undefined;
378
+ if (!Number.isSafeInteger(messageId) || messageId < 1)
379
+ throw new Error("Telegram effect response omitted message_id");
380
+ return messageId;
381
+ }
382
+ async function executePart(api, artifact, part, chatId, signal) {
383
+ const effect = artifact.effect;
384
+ if (effect.kind === "text" || effect.kind === "admission_ack") {
385
+ const renderHtml = effect.kind === "text" && artifact.authorClass === "butler" ? renderTelegramButlerHtml : undefined;
386
+ const ids = await (0, telegram_client_1.sendTelegramText)(api, chatId, part.text, { ...(signal ? { signal } : {}), ...(renderHtml ? { renderHtml } : {}) });
387
+ if (ids.length !== 1)
388
+ throw new Error("Telegram effect chunk renderer returned an unexpected message count");
389
+ return ids[0];
390
+ }
391
+ if (effect.kind === "card") {
392
+ const reply_markup = { inline_keyboard: effect.buttons.map((row) => row.map((button) => ({ text: button.text, callback_data: button.callbackData }))) };
393
+ let result;
394
+ try {
395
+ result = await api.request("sendMessage", { chat_id: chatId, text: (0, telegram_client_1.escapeTelegramHtml)(effect.text), parse_mode: "HTML", reply_markup }, signal);
396
+ }
397
+ catch (error) {
398
+ if (!(error instanceof telegram_client_1.TelegramApiError) || error.status !== 400)
399
+ throw error;
400
+ result = await api.request("sendMessage", { chat_id: chatId, text: effect.text, reply_markup }, signal);
401
+ }
402
+ return canonicalMessageId(result);
403
+ }
404
+ if (effect.kind === "edit") {
405
+ const base = { chat_id: chatId, message_id: effect.messageId, reply_markup: { inline_keyboard: [] } };
406
+ try {
407
+ await api.request("editMessageText", { ...base, text: (0, telegram_client_1.escapeTelegramHtml)(effect.text), parse_mode: "HTML" }, signal ? AbortSignal.any([signal, AbortSignal.timeout(30_000)]) : AbortSignal.timeout(30_000));
408
+ }
409
+ catch (error) {
410
+ if (error instanceof telegram_client_1.TelegramApiError && error.status === 400 && /message is not modified/iu.test(error.message))
411
+ return effect.messageId;
412
+ if (!(error instanceof telegram_client_1.TelegramApiError) || error.status !== 400)
413
+ throw error;
414
+ try {
415
+ await api.request("editMessageText", { ...base, text: effect.text }, signal ? AbortSignal.any([signal, AbortSignal.timeout(30_000)]) : AbortSignal.timeout(30_000));
416
+ }
417
+ catch (fallbackError) {
418
+ if (!(fallbackError instanceof telegram_client_1.TelegramApiError) || fallbackError.status !== 400 || !/message is not modified/iu.test(fallbackError.message))
419
+ throw fallbackError;
420
+ }
421
+ }
422
+ return effect.messageId;
423
+ }
424
+ await api.request("answerCallbackQuery", { callback_query_id: effect.callbackQueryId, ...(effect.text ? { text: effect.text } : {}), ...(effect.showAlert ? { show_alert: true } : {}) }, signal);
425
+ return undefined;
426
+ }
427
+ async function executeTelegramEffect(store, artifactIdValue, api, reauthorize, signal) {
428
+ return (0, session_transaction_1.withSessionTurnLease)(store.coordinationPath(artifactIdValue), async () => {
429
+ const artifact = store.read(artifactIdValue);
430
+ if (artifact.parts.some((part) => part.state === "indeterminate"))
431
+ throw new Error("Telegram effect has an indeterminate part and cannot be retried blindly");
432
+ const interrupted = artifact.parts.filter((part) => part.state === "attempting");
433
+ if (interrupted.length > 0) {
434
+ const now = new Date().toISOString();
435
+ for (const part of interrupted) {
436
+ part.state = "indeterminate";
437
+ part.updatedAt = now;
438
+ }
439
+ artifact.updatedAt = now;
440
+ store.write(artifact);
441
+ throw new Error("Telegram effect has an indeterminate part after an interrupted send and cannot be retried blindly");
442
+ }
443
+ const authorization = await reauthorize(artifact);
444
+ if (!authorization.allowed)
445
+ throw new Error(`Telegram effect authorization denied: ${authorization.reason}`);
446
+ if (Date.parse(authorization.expiresAt) <= Date.now())
447
+ throw new Error("Telegram effect authorization expired");
448
+ if (!/^[1-9][0-9]*$/u.test(authorization.transport.chatId))
449
+ throw new Error("Telegram effect authorization returned an invalid transport route");
450
+ if (artifact.target.kind === "admission_gate" && authorization.transport.chatId !== artifact.target.chatId)
451
+ throw new Error("Telegram admission transport route changed");
452
+ artifact.authorizationReceiptId = authorization.receiptId;
453
+ artifact.authorizationExpiresAt = authorization.expiresAt;
454
+ for (const part of artifact.parts) {
455
+ if (part.state === "accepted" || part.state === "session_recorded")
456
+ continue;
457
+ if (signal?.aborted)
458
+ throw signal.reason;
459
+ const attemptingAt = new Date().toISOString();
460
+ part.state = "attempting";
461
+ part.attempts = (part.attempts ?? 0) + 1;
462
+ part.updatedAt = attemptingAt;
463
+ artifact.updatedAt = attemptingAt;
464
+ store.write(artifact);
465
+ try {
466
+ const messageId = await executePart(api, artifact, part, authorization.transport.chatId, signal);
467
+ part.state = "accepted";
468
+ if (messageId !== undefined)
469
+ part.messageId = messageId;
470
+ part.acceptedAt = new Date().toISOString();
471
+ part.updatedAt = part.acceptedAt;
472
+ artifact.updatedAt = part.updatedAt;
473
+ store.write(artifact);
474
+ }
475
+ catch (error) {
476
+ const safelyRetryable = artifact.effect.kind === "edit" || artifact.effect.kind === "callback_ack"
477
+ || (error instanceof telegram_client_1.TelegramApiError && error.errorCode !== null);
478
+ part.state = safelyRetryable ? "prepared" : "indeterminate";
479
+ part.updatedAt = new Date().toISOString();
480
+ artifact.updatedAt = part.updatedAt;
481
+ store.write(artifact);
482
+ if (!safelyRetryable)
483
+ (0, runtime_1.emitNervesEvent)({ level: "error", component: "senses", event: "senses.telegram_effect_indeterminate", message: "Telegram effect became indeterminate", meta: { artifactId: artifact.id, partIndex: part.index, effectKind: artifact.effect.kind, error: error instanceof Error ? error.message : String(error) } });
484
+ throw error;
485
+ }
486
+ }
487
+ (0, runtime_1.emitNervesEvent)({ component: "senses", event: "senses.telegram_effect_accepted", message: "Telegram effect accepted", meta: { artifactId: artifact.id, effectKind: artifact.effect.kind, partCount: artifact.parts.length } });
488
+ return artifact;
489
+ });
490
+ }
491
+ function createTelegramAuthorizedEffectExecutor(options) {
492
+ const barrier = options.barrier ?? (() => undefined);
493
+ const getStore = () => typeof options.store === "function" ? options.store() : options.store;
494
+ return async (input) => {
495
+ if (input.signal?.aborted)
496
+ throw input.signal.reason;
497
+ barrier();
498
+ const authorization = await options.authorize({ phase: "prepare", ...input });
499
+ if (!authorization.allowed)
500
+ throw new Error(`Telegram effect authorization denied: ${authorization.reason}`);
501
+ const store = getStore();
502
+ const prepared = prepareTelegramEffect(store, { ...input, authorization });
503
+ try {
504
+ const executed = await executeTelegramEffect(store, prepared.id, options.api, async (artifact) => {
505
+ if (input.signal?.aborted)
506
+ throw input.signal.reason;
507
+ barrier();
508
+ return options.authorize({
509
+ phase: "send",
510
+ idempotencyKey: artifact.idempotencyKey,
511
+ target: artifact.target,
512
+ authorClass: artifact.authorClass,
513
+ effect: artifact.effect,
514
+ artifact,
515
+ });
516
+ }, input.signal);
517
+ barrier();
518
+ for (const part of executed.parts) {
519
+ if (part.messageId !== undefined && part.text !== null)
520
+ input.onMessageDelivered?.(part.messageId, part.text);
521
+ }
522
+ return executed;
523
+ }
524
+ catch (error) {
525
+ const attempted = store.read(prepared.id);
526
+ for (const part of attempted.parts) {
527
+ if ((part.state === "accepted" || part.state === "session_recorded") && part.messageId !== undefined && part.text !== null) {
528
+ input.onMessageDelivered?.(part.messageId, part.text);
529
+ }
530
+ }
531
+ throw error;
532
+ }
533
+ };
534
+ }
535
+ async function reconcileTelegramSystemFailsafe(input) {
536
+ const { record } = input;
537
+ const action = record.privilegedProtectiveAction;
538
+ if (record.privilegedFailsafe)
539
+ return { sent: false, reason: "already_recorded", artifactId: record.privilegedFailsafe.artifactId };
540
+ if (record.agent !== "sanctuary" || record.source !== "sanctuary-usenet" || record.eventType !== "usenet.protective_action"
541
+ || record.priority !== "critical" || !action?.critical)
542
+ return { sent: false, reason: "noncritical" };
543
+ if (!action.verification.verified)
544
+ return { sent: false, reason: "unverified" };
545
+ if (action.action !== "sabnzbd.pause")
546
+ return { sent: false, reason: "policy_blocked" };
547
+ if (record.dispatchEnabled === false)
548
+ return { sent: false, reason: "policy_blocked" };
549
+ if ((record.executionState !== "retry_wait" && record.executionState !== "dead_letter") || !record.lastError || record.attemptCount < 1) {
550
+ return { sent: false, reason: "butler_available" };
551
+ }
552
+ const now = input.now?.() ?? new Date().toISOString();
553
+ const nowMs = Date.parse(now);
554
+ if (!Number.isFinite(nowMs) || nowMs - Date.parse(record.updatedAt) < SYSTEM_FAILSAFE_UNAVAILABLE_MS)
555
+ return { sent: false, reason: "unavailability_window" };
556
+ if (nowMs > Date.parse(action.expiresAt))
557
+ return { sent: false, reason: "expired" };
558
+ const verification = await input.verifyProtectiveState(action);
559
+ if (!verification.verified || !verification.reference.trim() || Buffer.byteLength(verification.reference) > 512)
560
+ return { sent: false, reason: "unverified" };
561
+ const artifact = await input.execute({
562
+ idempotencyKey: `system-failsafe:${action.transitionId}`,
563
+ target: input.target,
564
+ authorClass: "system_failsafe",
565
+ effect: { kind: "text", text: exports.FIXED_USENET_SYSTEM_FAILSAFE },
566
+ });
567
+ await input.recordArtifact(artifact);
568
+ input.bindArtifact(record.recordPath, { artifactId: artifact.id, verificationRef: verification.reference, recordedAt: now });
569
+ (0, runtime_1.emitNervesEvent)({ component: "senses", event: "senses.telegram_system_failsafe_sent", message: "sent verified Sanctuary system failsafe", meta: { artifactId: artifact.id, eventId: record.eventId, transitionId: action.transitionId } });
570
+ return { sent: true, reason: "sent", artifactId: artifact.id };
571
+ }
572
+ async function sweepTelegramSystemFailsafes(input) {
573
+ const records = (0, router_1.listExternalEventStatus)(input.eventRoot).filter((status) => !status.corrupt && status.agent === "sanctuary" && status.source === "sanctuary-usenet").slice(0, 32);
574
+ let sent = 0;
575
+ for (const status of records) {
576
+ const result = await reconcileTelegramSystemFailsafe({
577
+ record: (0, router_1.readExternalEventRecord)(status.recordPath),
578
+ ...(input.now ? { now: input.now } : {}),
579
+ target: input.target,
580
+ verifyProtectiveState: input.verifyProtectiveState,
581
+ execute: input.execute,
582
+ recordArtifact: input.recordArtifact,
583
+ bindArtifact: router_1.bindPrivilegedFailsafeArtifact,
584
+ });
585
+ if (result.sent)
586
+ sent += 1;
587
+ }
588
+ return { inspected: records.length, sent };
589
+ }
590
+ async function recoverTelegramEffectOutbox(input) {
591
+ const limit = Math.min(Math.max(input.maxArtifacts ?? 20, 1), 100);
592
+ const candidates = input.store.listRecoverable()
593
+ .filter((artifact) => (!input.matches || input.matches(artifact))
594
+ && !artifact.parts.some((part) => part.state === "attempting" || part.state === "indeterminate")
595
+ && artifact.parts.some((part) => part.state === "prepared" && (part.attempts ?? 0) < 3))
596
+ .sort((left, right) => left.createdAt.localeCompare(right.createdAt) || left.id.localeCompare(right.id))
597
+ .slice(0, limit);
598
+ let accepted = 0;
599
+ let failed = 0;
600
+ for (const artifact of candidates) {
601
+ try {
602
+ await input.execute({ idempotencyKey: artifact.idempotencyKey, target: artifact.target, authorClass: artifact.authorClass, effect: artifact.effect, ...(artifact.obligationReturnId ? { obligationReturnId: artifact.obligationReturnId } : {}) });
603
+ accepted += 1;
604
+ }
605
+ catch {
606
+ failed += 1;
607
+ }
608
+ }
609
+ return { attempted: candidates.length, accepted, failed };
610
+ }
611
+ function createTelegramApprovalEffectPort(options) {
612
+ const executeAndRecord = async (input, causalEventId) => {
613
+ const artifact = await options.execute({ ...input, target: options.target });
614
+ await options.record(artifact, causalEventId);
615
+ return artifact;
616
+ };
617
+ return {
618
+ async sendText(input) {
619
+ if (input.chatId !== options.chatId)
620
+ throw new Error("Telegram approval text target changed");
621
+ const artifact = await executeAndRecord({ idempotencyKey: input.idempotencyKey, authorClass: input.authorClass, effect: { kind: "text", text: input.text }, ...(input.signal ? { signal: input.signal } : {}) }, input.causalEventId);
622
+ return artifact.parts.flatMap((part) => part.messageId === undefined ? [] : [part.messageId]);
623
+ },
624
+ async sendCard(input) {
625
+ if (input.chatId !== options.chatId)
626
+ throw new Error("Telegram approval card target changed");
627
+ const artifact = await executeAndRecord({ idempotencyKey: input.idempotencyKey, authorClass: "control", effect: { kind: "card", text: input.text, buttons: input.buttons }, ...(input.signal ? { signal: input.signal } : {}) });
628
+ const messageId = artifact.parts[0]?.messageId;
629
+ if (!messageId)
630
+ throw new Error("Telegram approval card omitted its message id");
631
+ return messageId;
632
+ },
633
+ async edit(input) {
634
+ if (input.chatId !== options.chatId)
635
+ throw new Error("Telegram approval edit target changed");
636
+ await executeAndRecord({ idempotencyKey: input.idempotencyKey, authorClass: "control", effect: { kind: "edit", messageId: input.messageId, text: input.text }, ...(input.signal ? { signal: input.signal } : {}) });
637
+ },
638
+ async acknowledge(input) {
639
+ await executeAndRecord({
640
+ idempotencyKey: input.idempotencyKey,
641
+ authorClass: "control",
642
+ effect: { kind: "callback_ack", callbackQueryId: input.callbackQueryId, ...(input.text ? { text: input.text } : {}), ...(input.showAlert ? { showAlert: true } : {}) },
643
+ ...(input.signal ? { signal: input.signal } : {}),
644
+ });
645
+ },
646
+ };
647
+ }
648
+ function recordTelegramEffectInSession(store, artifactIdValue, sessionEventIds) {
649
+ const artifact = store.read(artifactIdValue);
650
+ const accepted = artifact.parts.filter((part) => part.state === "accepted");
651
+ const transportOnlyControl = isTransportOnlyControl(artifact);
652
+ if ((!transportOnlyControl && accepted.length !== sessionEventIds.length) || (transportOnlyControl && sessionEventIds.length !== 0) || sessionEventIds.some((value) => !value.trim())) {
653
+ throw new Error("Telegram session event ids do not match accepted effect parts");
654
+ }
655
+ accepted.forEach((part, index) => {
656
+ part.state = "session_recorded";
657
+ if (!transportOnlyControl)
658
+ part.sessionEventId = sessionEventIds[index];
659
+ part.sessionRecordedAt = new Date().toISOString();
660
+ part.updatedAt = part.sessionRecordedAt;
661
+ });
662
+ artifact.updatedAt = new Date().toISOString();
663
+ store.write(artifact);
664
+ (0, runtime_1.emitNervesEvent)({ component: "senses", event: "senses.telegram_effect_session_recorded", message: "recorded Telegram effect in session continuity", meta: { artifactId: artifact.id, authorClass: artifact.authorClass, partCount: accepted.length } });
665
+ return artifact;
666
+ }
667
+ function bindButlerArtifactToCanonicalEvent(envelope, artifact, causalEventId) {
668
+ if (artifact.authorClass !== "butler" || artifact.effect.kind !== "text" || !causalEventId)
669
+ return null;
670
+ const reference = `telegram-artifact:${artifact.id}`;
671
+ const candidate = envelope.events.find((event) => event.id === causalEventId && event.role === "assistant");
672
+ if (!candidate)
673
+ throw new Error("Telegram effect canonical causal event is unavailable");
674
+ const messageReferences = artifact.parts.flatMap((part) => part.messageId ? [`telegram-message:${part.messageId}`] : []);
675
+ const requestReferences = artifact.target.kind === "approved_relationship" && artifact.target.requestId ? [`request:${artifact.target.requestId}`] : [];
676
+ const events = envelope.events.map((event) => event.id === candidate.id
677
+ ? { ...event, relations: { ...event.relations, references: [...new Set([...event.relations.references, reference, ...messageReferences, ...requestReferences])] } }
678
+ : event);
679
+ return { envelope: { ...envelope, events }, eventIds: artifact.parts.filter((part) => part.state === "accepted").map(() => candidate.id) };
680
+ }
681
+ function artifactEventContent(artifact, part) {
682
+ const text = part.text ?? "";
683
+ if (artifact.authorClass === "butler")
684
+ return text;
685
+ if (artifact.authorClass === "control")
686
+ return `[Telegram control artifact]\n${text}`;
687
+ return `[System failsafe already sent]\n${text}`;
688
+ }
689
+ function appendTelegramArtifactEvents(envelope, artifact, recordedAt) {
690
+ const accepted = artifact.parts.filter((part) => part.state === "accepted");
691
+ if (accepted.length === 0)
692
+ throw new Error("Telegram artifact has no accepted parts to record");
693
+ let sequence = envelope.events.reduce((maximum, event) => Math.max(maximum, event.sequence), 0);
694
+ const events = accepted.map((part) => {
695
+ sequence += 1;
696
+ const id = `evt-${String(sequence).padStart(6, "0")}`;
697
+ const role = artifact.authorClass === "butler" ? "assistant" : "system";
698
+ const name = artifact.authorClass === "butler"
699
+ ? "telegram-butler"
700
+ : artifact.authorClass === "control" ? "telegram-control" : "telegram-system-failsafe";
701
+ return {
702
+ id,
703
+ sequence,
704
+ role,
705
+ content: artifactEventContent(artifact, part),
706
+ name,
707
+ toolCallId: null,
708
+ toolCalls: [],
709
+ attachments: [],
710
+ time: { authoredAt: recordedAt, authoredAtSource: "local", observedAt: recordedAt, observedAtSource: "local", recordedAt, recordedAtSource: "save" },
711
+ relations: {
712
+ replyToEventId: null,
713
+ threadRootEventId: null,
714
+ references: [
715
+ `telegram-artifact:${artifact.id}`,
716
+ ...(part.messageId ? [`telegram-message:${part.messageId}`] : []),
717
+ ...(artifact.target.kind === "approved_relationship" && artifact.target.requestId ? [`request:${artifact.target.requestId}`] : []),
718
+ ],
719
+ toolCallId: null,
720
+ supersedesEventId: null,
721
+ redactsEventId: null,
722
+ },
723
+ provenance: { captureKind: "synthetic", legacyVersion: null, sourceMessageIndex: null },
724
+ };
725
+ });
726
+ const eventIds = events.map((event) => event.id);
727
+ return {
728
+ envelope: {
729
+ ...envelope,
730
+ events: [...envelope.events, ...events],
731
+ projection: { ...envelope.projection, eventIds: [...envelope.projection.eventIds, ...eventIds], projectedAt: recordedAt, trimmed: false },
732
+ },
733
+ eventIds,
734
+ };
735
+ }
736
+ function appendTelegramInboundEvent(envelope, input) {
737
+ if (envelope.events.some((event) => event.relations.references.includes(input.reference)))
738
+ return envelope;
739
+ const sequence = envelope.events.reduce((maximum, event) => Math.max(maximum, event.sequence), 0) + 1;
740
+ const id = `evt-${String(sequence).padStart(6, "0")}`;
741
+ const event = {
742
+ id,
743
+ sequence,
744
+ role: "user",
745
+ content: input.text,
746
+ name: "telegram-user",
747
+ toolCallId: null,
748
+ toolCalls: [],
749
+ attachments: [...new Set(input.attachmentIds ?? [])],
750
+ time: { authoredAt: null, authoredAtSource: "unknown", observedAt: input.recordedAt, observedAtSource: "ingest", recordedAt: input.recordedAt, recordedAtSource: "save" },
751
+ relations: {
752
+ replyToEventId: input.relations?.replyToEventId ?? null,
753
+ threadRootEventId: input.relations?.threadRootEventId ?? null,
754
+ references: [...new Set([input.reference, ...(input.relations?.references ?? [])])],
755
+ toolCallId: null,
756
+ supersedesEventId: null,
757
+ redactsEventId: null,
758
+ },
759
+ provenance: { captureKind: "live", legacyVersion: null, sourceMessageIndex: null },
760
+ };
761
+ return {
762
+ ...envelope,
763
+ events: [...envelope.events, event],
764
+ projection: { ...envelope.projection, eventIds: [...envelope.projection.eventIds, id], projectedAt: input.recordedAt, trimmed: false },
765
+ };
766
+ }
767
+ async function recordTelegramEffectsInSession(input) {
768
+ if (input.artifacts.length === 0 && !input.inbound)
769
+ return null;
770
+ const record = async (lease) => {
771
+ const transaction = (0, session_transaction_1.readSessionTransaction)(input.sessionPath, lease);
772
+ let envelope = (0, session_events_1.loadSessionEnvelopeFile)(input.sessionPath) ?? {
773
+ version: 2,
774
+ events: [],
775
+ projection: { eventIds: [], trimmed: false, maxTokens: null, contextMargin: null, inputTokens: null, projectedAt: null },
776
+ lastUsage: null,
777
+ state: { mustResolveBeforeHandoff: false, lastFriendActivityAt: null },
778
+ };
779
+ let inboundReceipt = null;
780
+ let changed = false;
781
+ if (input.inbound) {
782
+ const existing = envelope.events.filter((event) => event.relations.references.includes(input.inbound.reference));
783
+ const expectedAttachmentIds = [...new Set(input.inbound.attachmentIds ?? [])];
784
+ if (existing.length > 1 || (existing.length === 1 && (existing[0].role !== "user" || existing[0].content !== input.inbound.text
785
+ || JSON.stringify(existing[0].attachments) !== JSON.stringify(expectedAttachmentIds)))) {
786
+ throw new Error("Telegram session has conflicting inbound ingress");
787
+ }
788
+ if (existing.length === 1) {
789
+ inboundReceipt = { eventId: existing[0].id, reference: input.inbound.reference };
790
+ }
791
+ else {
792
+ envelope = appendTelegramInboundEvent(envelope, { ...input.inbound, attachmentIds: expectedAttachmentIds, recordedAt: new Date().toISOString() });
793
+ inboundReceipt = { eventId: envelope.events.at(-1).id, reference: input.inbound.reference };
794
+ changed = true;
795
+ }
796
+ }
797
+ const recordings = [];
798
+ for (const artifact of input.artifacts) {
799
+ const unrecorded = artifact.parts.filter((part) => part.state === "accepted");
800
+ if (unrecorded.length === 0)
801
+ continue;
802
+ if (isTransportOnlyControl(artifact)) {
803
+ recordings.push({ artifact, eventIds: [] });
804
+ continue;
805
+ }
806
+ const reference = `telegram-artifact:${artifact.id}`;
807
+ const existingEvents = envelope.events.filter((event) => event.relations.references.includes(reference));
808
+ if (existingEvents.length > 0) {
809
+ const eventIds = unrecorded.map((part) => existingEvents.find((event) => !part.messageId || event.relations.references.includes(`telegram-message:${part.messageId}`))?.id);
810
+ if (eventIds.every((id) => typeof id === "string")) {
811
+ recordings.push({ artifact, eventIds });
812
+ continue;
813
+ }
814
+ }
815
+ if (existingEvents.length !== 0)
816
+ throw new Error("Telegram effect session reconciliation is partial");
817
+ const appended = bindButlerArtifactToCanonicalEvent(envelope, artifact, input.causalEventIds?.[artifact.id])
818
+ ?? appendTelegramArtifactEvents(envelope, artifact, new Date().toISOString());
819
+ envelope = appended.envelope;
820
+ recordings.push({ artifact, eventIds: appended.eventIds });
821
+ changed = true;
822
+ }
823
+ if (changed)
824
+ (0, session_transaction_1.writeSessionTransaction)(input.sessionPath, envelope, { lease, expectedRevision: transaction.revision });
825
+ for (const recording of recordings)
826
+ recordTelegramEffectInSession(input.store, recording.artifact.id, recording.eventIds);
827
+ return inboundReceipt;
828
+ };
829
+ const lease = (0, session_transaction_1.currentSessionTurnLease)(input.sessionPath);
830
+ return lease ? record(lease) : (0, session_transaction_1.withSessionTurnLease)(input.sessionPath, record);
831
+ }
832
+ function resolveTelegramReply(store, input) {
833
+ for (const artifact of store.list()) {
834
+ if (artifact.target.kind !== "approved_relationship" || artifact.target.friendId !== input.friendId || artifact.target.sessionKey !== input.sessionKey)
835
+ continue;
836
+ const part = artifact.parts.find((candidate) => candidate.messageId === input.messageId && candidate.state === "session_recorded" && candidate.sessionEventId);
837
+ if (!part?.sessionEventId)
838
+ continue;
839
+ return {
840
+ artifactId: artifact.id,
841
+ authorClass: artifact.authorClass,
842
+ sessionEventId: part.sessionEventId,
843
+ requestId: artifact.target.requestId ?? null,
844
+ };
845
+ }
846
+ return null;
847
+ }
848
+ function resolveTelegramControlArtifact(store, input) {
849
+ for (const artifact of store.list()) {
850
+ if (artifact.authorClass !== "control" || artifact.target.kind !== "approved_relationship"
851
+ || artifact.effect.kind !== "card"
852
+ || artifact.target.friendId !== input.friendId || artifact.target.sessionKey !== input.sessionKey)
853
+ continue;
854
+ if (!artifact.parts.some((part) => part.messageId === input.messageId && part.state === "session_recorded"))
855
+ continue;
856
+ return { artifactId: artifact.id, requestId: artifact.target.requestId ?? null };
857
+ }
858
+ return null;
859
+ }