@ouro.bot/cli 0.1.0-alpha.74 → 0.1.0-alpha.741

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 (587) 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 +4730 -16
  15. package/deploy/unraid/Dockerfile +43 -0
  16. package/deploy/unraid/README.txt +1623 -0
  17. package/deploy/unraid/audit-container-spec.sh +4 -0
  18. package/deploy/unraid/container-runtime.json +1 -0
  19. package/deploy/unraid/sanctuary-acceptance-adapter.sh +37 -0
  20. package/deploy/unraid/sanctuary-acceptance-contract.json +200 -0
  21. package/deploy/unraid/sanctuary-acceptance-harness.sh +23 -0
  22. package/deploy/unraid/sanctuary-deployment-target.mjs +709 -0
  23. package/deploy/unraid/sanctuary-safe-rename.py +24 -0
  24. package/deploy/unraid/sanctuary-unit16-host-broker.mjs +1796 -0
  25. package/deploy/unraid/sanctuary-unit16-run.sh +520 -0
  26. package/deploy/unraid/sanctuary-unit18-target-audit.sh +27 -0
  27. package/deploy/unraid/sanctuary.ouro/agent.json +24 -0
  28. package/deploy/unraid/sanctuary.ouro/arc/README.md +3 -0
  29. package/deploy/unraid/sanctuary.ouro/bundle-meta.json +5 -0
  30. package/deploy/unraid/sanctuary.ouro/habits/sanctuary-health.md +8 -0
  31. package/deploy/unraid/sanctuary.ouro/provider-readiness.json +18 -0
  32. package/deploy/unraid/sanctuary.ouro/psyche/ASPIRATIONS.md +3 -0
  33. package/deploy/unraid/sanctuary.ouro/psyche/IDENTITY.md +3 -0
  34. package/deploy/unraid/sanctuary.ouro/psyche/LORE.md +3 -0
  35. package/deploy/unraid/sanctuary.ouro/psyche/SOUL.md +5 -0
  36. package/deploy/unraid/sanctuary.ouro/psyche/TACIT.md +7 -0
  37. package/deploy/unraid/sanctuary.ouro/tool-profiles.json +21 -0
  38. package/deploy/unraid/sanctuary.xml +25 -0
  39. package/dist/a2a/card.js +57 -0
  40. package/dist/a2a/client.js +180 -0
  41. package/dist/a2a/config.js +50 -0
  42. package/dist/a2a/delegation-stores.js +56 -0
  43. package/dist/a2a/did-resolution.js +93 -0
  44. package/dist/a2a/identity.js +170 -0
  45. package/dist/a2a/inbound-share.js +107 -0
  46. package/dist/a2a/mission-result-wire.js +196 -0
  47. package/dist/a2a/onboarding.js +115 -0
  48. package/dist/a2a/pin-store.js +132 -0
  49. package/dist/a2a/seen-ledger.js +120 -0
  50. package/dist/a2a/server.js +604 -0
  51. package/dist/a2a/task-store.js +69 -0
  52. package/dist/a2a/transport.js +45 -0
  53. package/dist/a2a/types.js +3 -0
  54. package/dist/arc/attention-types.js +8 -0
  55. package/dist/arc/cares.js +144 -0
  56. package/dist/arc/episodes.js +118 -0
  57. package/dist/arc/evolution.js +490 -0
  58. package/dist/arc/flight-recorder.js +1034 -0
  59. package/dist/arc/intentions.js +134 -0
  60. package/dist/arc/json-store.js +117 -0
  61. package/dist/arc/obligations.js +348 -0
  62. package/dist/arc/packets.js +336 -0
  63. package/dist/arc/presence.js +185 -0
  64. package/dist/arc/task-lifecycle.js +57 -0
  65. package/dist/commerce/store.js +755 -0
  66. package/dist/commerce/types.js +3 -0
  67. package/dist/heart/active-work.js +874 -43
  68. package/dist/heart/agent-entry.js +190 -3
  69. package/dist/heart/approval-files.js +113 -0
  70. package/dist/heart/approval-store.js +950 -0
  71. package/dist/heart/attachments/image-normalize.js +194 -0
  72. package/dist/heart/attachments/materialize.js +97 -0
  73. package/dist/heart/attachments/originals.js +88 -0
  74. package/dist/heart/attachments/render.js +29 -0
  75. package/dist/heart/attachments/sources/bluebubbles.js +156 -0
  76. package/dist/heart/attachments/sources/cli-local-file.js +78 -0
  77. package/dist/heart/attachments/sources/index.js +16 -0
  78. package/dist/heart/attachments/store.js +103 -0
  79. package/dist/heart/attachments/types.js +93 -0
  80. package/dist/heart/auth/auth-flow.js +495 -0
  81. package/dist/heart/autonomy-budget.js +477 -0
  82. package/dist/heart/awaiting/await-alert.js +146 -0
  83. package/dist/heart/awaiting/await-expiry.js +108 -0
  84. package/dist/heart/awaiting/await-loader.js +91 -0
  85. package/dist/heart/awaiting/await-parser.js +141 -0
  86. package/dist/heart/awaiting/await-runtime-state.js +100 -0
  87. package/dist/heart/awaiting/await-scheduler.js +382 -0
  88. package/dist/heart/background-operations.js +281 -0
  89. package/dist/heart/bridges/manager.js +137 -17
  90. package/dist/heart/bridges/store.js +14 -2
  91. package/dist/heart/bundle-state.js +168 -0
  92. package/dist/heart/commitments.js +135 -0
  93. package/dist/heart/config-registry.js +331 -0
  94. package/dist/heart/config.js +128 -129
  95. package/dist/heart/context-loss-gauntlet.js +354 -0
  96. package/dist/heart/context-loss-sentinel.js +1155 -0
  97. package/dist/heart/core.js +1759 -331
  98. package/dist/heart/cross-chat-delivery.js +8 -22
  99. package/dist/heart/daemon/agent-config-check.js +451 -0
  100. package/dist/heart/daemon/agent-discovery.js +185 -3
  101. package/dist/heart/daemon/agent-service.js +542 -0
  102. package/dist/heart/daemon/agentic-repair.js +547 -0
  103. package/dist/heart/daemon/await-private-wake.js +59 -0
  104. package/dist/heart/daemon/bluebubbles-cli-integration.js +71 -0
  105. package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
  106. package/dist/heart/daemon/bluebubbles-host-protocol.js +433 -0
  107. package/dist/heart/daemon/bluebubbles-host.js +273 -0
  108. package/dist/heart/daemon/boot-sync-probe.js +197 -0
  109. package/dist/heart/daemon/cadence.js +180 -0
  110. package/dist/heart/daemon/cli-defaults.js +832 -0
  111. package/dist/heart/daemon/cli-desk.js +322 -0
  112. package/dist/heart/daemon/cli-exec.js +8692 -0
  113. package/dist/heart/daemon/cli-help.js +710 -0
  114. package/dist/heart/daemon/cli-parse.js +2714 -0
  115. package/dist/heart/daemon/cli-render-doctor.js +57 -0
  116. package/dist/heart/daemon/cli-render.js +777 -0
  117. package/dist/heart/daemon/cli-types.js +8 -0
  118. package/dist/heart/daemon/connect-bay.js +323 -0
  119. package/dist/heart/daemon/container-credential-bootstrap.js +273 -0
  120. package/dist/heart/daemon/container-healthcheck.js +94 -0
  121. package/dist/heart/daemon/container-runtime.js +84 -0
  122. package/dist/heart/daemon/container-spec-auditor-main.js +156 -0
  123. package/dist/heart/daemon/container-spec-auditor.js +156 -0
  124. package/dist/heart/daemon/daemon-bootstrap-startup.js +41 -0
  125. package/dist/heart/daemon/daemon-cli.js +29 -2144
  126. package/dist/heart/daemon/daemon-entry.js +930 -10
  127. package/dist/heart/daemon/daemon-health.js +186 -0
  128. package/dist/heart/daemon/daemon-rollup.js +57 -0
  129. package/dist/heart/daemon/daemon-runtime-sync.js +88 -13
  130. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  131. package/dist/heart/daemon/daemon.js +1698 -88
  132. package/dist/heart/daemon/dns-workflow.js +394 -0
  133. package/dist/heart/daemon/doctor-types.js +8 -0
  134. package/dist/heart/daemon/doctor.js +1755 -0
  135. package/dist/heart/daemon/freshness.js +345 -0
  136. package/dist/heart/daemon/habit-private-wake.js +61 -0
  137. package/dist/heart/daemon/health-monitor.js +142 -11
  138. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  139. package/dist/heart/daemon/hooks/bundle-meta.js +135 -1
  140. package/dist/heart/daemon/http-health-probe.js +80 -0
  141. package/dist/heart/daemon/human-command-screens.js +234 -0
  142. package/dist/heart/daemon/human-readiness.js +114 -0
  143. package/dist/heart/daemon/inner-status.js +78 -0
  144. package/dist/heart/daemon/interactive-repair.js +394 -0
  145. package/dist/heart/daemon/launchd.js +37 -8
  146. package/dist/heart/daemon/log-tailer.js +79 -10
  147. package/dist/heart/daemon/logs-prune.js +132 -0
  148. package/dist/heart/daemon/mcp-canary.js +524 -0
  149. package/dist/heart/daemon/message-router.js +45 -5
  150. package/dist/heart/daemon/migrate-to-desk.js +848 -0
  151. package/dist/heart/daemon/os-cron-deps.js +135 -0
  152. package/dist/heart/daemon/os-cron.js +22 -14
  153. package/dist/heart/daemon/ouro-bot-entry.js +4 -2
  154. package/dist/heart/daemon/ouro-entry.js +3 -1
  155. package/dist/heart/daemon/plugin-cli.js +432 -0
  156. package/dist/heart/daemon/process-manager.js +624 -61
  157. package/dist/heart/daemon/provider-discovery.js +137 -0
  158. package/dist/heart/daemon/provider-ping-progress.js +83 -0
  159. package/dist/heart/daemon/prunable-bundle.js +144 -0
  160. package/dist/heart/daemon/pulse.js +511 -0
  161. package/dist/heart/daemon/readiness-repair.js +365 -0
  162. package/dist/heart/daemon/run-hooks.js +2 -0
  163. package/dist/heart/daemon/runtime-logging.js +47 -14
  164. package/dist/heart/daemon/runtime-metadata.js +2 -30
  165. package/dist/heart/daemon/runtime-mode.js +13 -2
  166. package/dist/heart/daemon/safe-mode.js +161 -0
  167. package/dist/heart/daemon/sanctuary-acceptance-adapter.js +2365 -0
  168. package/dist/heart/daemon/sanctuary-acceptance-harness.js +1624 -0
  169. package/dist/heart/daemon/sanctuary-acceptance-marker.js +267 -0
  170. package/dist/heart/daemon/sanctuary-acceptance-scenarios.js +985 -0
  171. package/dist/heart/daemon/sanctuary-scheduler-liveness.js +227 -0
  172. package/dist/heart/daemon/sanctuary-scheduler-origin.js +126 -0
  173. package/dist/heart/daemon/sense-manager.js +689 -48
  174. package/dist/heart/daemon/session-id-resolver.js +131 -0
  175. package/dist/heart/daemon/skill-management-installer.js +1 -1
  176. package/dist/heart/daemon/socket-client.js +180 -12
  177. package/dist/heart/daemon/stale-bundle-prune.js +113 -0
  178. package/dist/heart/daemon/startup-tui.js +338 -0
  179. package/dist/heart/daemon/supercronic-supervisor.js +267 -0
  180. package/dist/heart/daemon/task-scheduler.js +117 -39
  181. package/dist/heart/daemon/terminal-ui.js +499 -0
  182. package/dist/heart/daemon/thoughts.js +255 -43
  183. package/dist/heart/daemon/up-progress.js +366 -0
  184. package/dist/heart/daemon/vault-items.js +56 -0
  185. package/dist/heart/delegation.js +1 -4
  186. package/dist/heart/external-events/router.js +121 -0
  187. package/dist/heart/habits/habit-cancel.js +810 -0
  188. package/dist/heart/habits/habit-lifecycle.js +1327 -0
  189. package/dist/heart/habits/habit-migration.js +194 -0
  190. package/dist/heart/habits/habit-parser.js +278 -0
  191. package/dist/heart/habits/habit-runtime-state.js +93 -0
  192. package/dist/heart/habits/habit-scheduler.js +653 -0
  193. package/dist/heart/habits/habit-session-summary.js +318 -0
  194. package/dist/heart/habits/habit-session.js +636 -0
  195. package/dist/heart/{daemon → hatch}/hatch-flow.js +55 -62
  196. package/dist/heart/{daemon → hatch}/hatch-specialist.js +6 -8
  197. package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
  198. package/dist/heart/{daemon → hatch}/specialist-tools.js +53 -18
  199. package/dist/heart/identity.js +184 -59
  200. package/dist/heart/kept-notes.js +289 -0
  201. package/dist/heart/kicks.js +1 -1
  202. package/dist/heart/machine-identity.js +161 -0
  203. package/dist/heart/mail-import-discovery.js +353 -0
  204. package/dist/heart/mailbox/mailbox-http-hooks.js +98 -0
  205. package/dist/heart/mailbox/mailbox-http-response.js +7 -0
  206. package/dist/heart/mailbox/mailbox-http-routes.js +398 -0
  207. package/dist/heart/mailbox/mailbox-http-static.js +103 -0
  208. package/dist/heart/mailbox/mailbox-http-transport.js +116 -0
  209. package/dist/heart/mailbox/mailbox-http.js +99 -0
  210. package/dist/heart/mailbox/mailbox-read.js +39 -0
  211. package/dist/heart/mailbox/mailbox-types.js +27 -0
  212. package/dist/heart/mailbox/mailbox-view.js +197 -0
  213. package/dist/heart/mailbox/readers/agent-machine.js +428 -0
  214. package/dist/heart/mailbox/readers/continuity-readers.js +329 -0
  215. package/dist/heart/mailbox/readers/mail.js +375 -0
  216. package/dist/heart/mailbox/readers/runtime-readers.js +823 -0
  217. package/dist/heart/mailbox/readers/sessions.js +232 -0
  218. package/dist/heart/mailbox/readers/shared.js +111 -0
  219. package/dist/heart/mcp/mcp-server.js +696 -0
  220. package/dist/heart/migrate-config.js +100 -0
  221. package/dist/heart/model-capabilities.js +11 -0
  222. package/dist/heart/orientation-frame.js +345 -0
  223. package/dist/heart/platform.js +81 -0
  224. package/dist/heart/primitives.js +1 -1
  225. package/dist/heart/private-runtime/decision-renderer.js +156 -0
  226. package/dist/heart/private-runtime/index.js +18 -0
  227. package/dist/heart/private-runtime/ledger.js +218 -0
  228. package/dist/heart/private-runtime/policy.js +214 -0
  229. package/dist/heart/private-runtime/types.js +2 -0
  230. package/dist/heart/progress-story.js +1 -1
  231. package/dist/heart/provider-attempt.js +137 -0
  232. package/dist/heart/provider-binding-resolver.js +273 -0
  233. package/dist/heart/provider-credentials.js +449 -0
  234. package/dist/heart/provider-failover.js +311 -0
  235. package/dist/heart/provider-models.js +89 -0
  236. package/dist/heart/provider-ping.js +265 -0
  237. package/dist/heart/provider-readiness-cache.js +119 -0
  238. package/dist/heart/provider-visibility.js +188 -0
  239. package/dist/heart/providers/anthropic-token.js +131 -0
  240. package/dist/heart/providers/anthropic.js +173 -60
  241. package/dist/heart/providers/azure.js +23 -11
  242. package/dist/heart/providers/error-classification.js +127 -0
  243. package/dist/heart/providers/github-copilot.js +34 -38
  244. package/dist/heart/providers/minimax-vlm.js +189 -0
  245. package/dist/heart/providers/minimax.js +26 -8
  246. package/dist/heart/providers/openai-codex-token.js +349 -0
  247. package/dist/heart/providers/openai-codex.js +55 -40
  248. package/dist/heart/providers/openai-compatible.js +177 -0
  249. package/dist/heart/run-ledger.js +247 -0
  250. package/dist/heart/runtime-capability-check.js +170 -0
  251. package/dist/heart/runtime-credentials.js +607 -0
  252. package/dist/heart/runtime-cwd.js +87 -0
  253. package/dist/heart/sense-truth.js +22 -4
  254. package/dist/heart/session-activity.js +90 -24
  255. package/dist/heart/session-events.js +1300 -0
  256. package/dist/heart/session-playback-cli-main.js +5 -0
  257. package/dist/heart/session-playback-cli.js +36 -0
  258. package/dist/heart/session-playback.js +231 -0
  259. package/dist/heart/session-stats-cli-main.js +5 -0
  260. package/dist/heart/session-stats.js +182 -0
  261. package/dist/heart/session-transcript.js +133 -0
  262. package/dist/heart/start-of-turn-packet.js +368 -0
  263. package/dist/heart/streaming.js +604 -207
  264. package/dist/heart/structured-output.js +196 -0
  265. package/dist/heart/sync-classification.js +176 -0
  266. package/dist/heart/sync.js +449 -0
  267. package/dist/heart/target-resolution.js +11 -7
  268. package/dist/heart/tempo.js +93 -0
  269. package/dist/heart/temporal-view.js +41 -0
  270. package/dist/heart/timeouts.js +101 -0
  271. package/dist/heart/tool-activity-callbacks.js +59 -0
  272. package/dist/heart/tool-approval.js +340 -0
  273. package/dist/heart/tool-description.js +155 -0
  274. package/dist/heart/tool-friction.js +55 -0
  275. package/dist/heart/tool-loop.js +200 -0
  276. package/dist/heart/turn-context.js +480 -0
  277. package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +6 -5
  278. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  279. package/dist/heart/versioning/ouro-path-installer.js +432 -0
  280. package/dist/heart/versioning/ouro-recovery-launcher.js +76 -0
  281. package/dist/heart/versioning/ouro-version-manager.js +409 -0
  282. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  283. package/dist/heart/{daemon → versioning}/update-checker.js +6 -1
  284. package/dist/heart/versioning/update-hooks.js +154 -0
  285. package/dist/heart/versioning/version-intent.js +114 -0
  286. package/dist/heart/work-card.js +380 -0
  287. package/dist/mailbox-ui/assets/index-Du_9G9WO.css +1 -0
  288. package/dist/mailbox-ui/assets/index-J8zDwHmE.js +1 -0
  289. package/dist/mailbox-ui/assets/vendor-CcN1XpQ9.js +61 -0
  290. package/dist/mailbox-ui/index.html +16 -0
  291. package/dist/mailroom/attention.js +167 -0
  292. package/dist/mailroom/autonomy.js +209 -0
  293. package/dist/mailroom/blob-store.js +889 -0
  294. package/dist/mailroom/body-cache.js +61 -0
  295. package/dist/mailroom/cache-sync-cli.js +58 -0
  296. package/dist/mailroom/core.js +803 -0
  297. package/dist/mailroom/entry.js +160 -0
  298. package/dist/mailroom/file-store.js +568 -0
  299. package/dist/mailroom/hosted-cache-sync.js +384 -0
  300. package/dist/mailroom/mbox-import.js +393 -0
  301. package/dist/mailroom/migration.js +164 -0
  302. package/dist/mailroom/outbound.js +380 -0
  303. package/dist/mailroom/policy.js +263 -0
  304. package/dist/mailroom/reader.js +292 -0
  305. package/dist/mailroom/search-cache.js +513 -0
  306. package/dist/mailroom/search-relevance.js +319 -0
  307. package/dist/mailroom/smtp-ingress.js +176 -0
  308. package/dist/mailroom/source-state.js +176 -0
  309. package/dist/mailroom/thread.js +109 -0
  310. package/dist/mailroom/travel-extract.js +89 -0
  311. package/dist/mind/bundle-manifest.js +24 -2
  312. package/dist/mind/context.js +273 -111
  313. package/dist/mind/desk-section.js +362 -0
  314. package/dist/mind/diary-integrity.js +60 -0
  315. package/dist/mind/{memory.js → diary.js} +68 -77
  316. package/dist/mind/embedding-provider.js +60 -0
  317. package/dist/mind/file-state.js +179 -0
  318. package/dist/mind/{associative-recall.js → note-search.js} +47 -58
  319. package/dist/mind/obligation-steering.js +227 -0
  320. package/dist/mind/pending.js +13 -8
  321. package/dist/mind/prompt-budget.js +479 -0
  322. package/dist/mind/prompt-refresh.js +3 -2
  323. package/dist/mind/prompt.js +1067 -172
  324. package/dist/mind/provenance-trust.js +26 -0
  325. package/dist/mind/record-paths.js +312 -0
  326. package/dist/mind/scrutiny.js +173 -0
  327. package/dist/mind/session-transaction.js +379 -0
  328. package/dist/nerves/cli-logging.js +7 -1
  329. package/dist/nerves/coverage/audit-rules.js +15 -6
  330. package/dist/nerves/coverage/audit.js +28 -2
  331. package/dist/nerves/coverage/cli.js +1 -1
  332. package/dist/nerves/coverage/contract.js +5 -5
  333. package/dist/nerves/coverage/file-completeness.js +139 -5
  334. package/dist/nerves/coverage/run-artifacts.js +55 -35
  335. package/dist/nerves/coverage/source-scanner.js +4 -4
  336. package/dist/nerves/event-buffer.js +111 -0
  337. package/dist/nerves/index.js +293 -9
  338. package/dist/nerves/observation.js +20 -0
  339. package/dist/nerves/redact.js +79 -0
  340. package/dist/nerves/review/cli-main.js +5 -0
  341. package/dist/nerves/review/cli.js +156 -0
  342. package/dist/nerves/review/core.js +159 -0
  343. package/dist/nerves/runtime.js +17 -1
  344. package/dist/repertoire/ado-client.js +15 -56
  345. package/dist/repertoire/ado-semantic.js +16 -10
  346. package/dist/repertoire/api-client.js +97 -0
  347. package/dist/repertoire/bitwarden-store.js +1045 -0
  348. package/dist/repertoire/bundle-templates.js +71 -0
  349. package/dist/repertoire/bw-installer.js +180 -0
  350. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  351. package/dist/repertoire/coding/context-pack.js +331 -0
  352. package/dist/repertoire/coding/feedback.js +218 -30
  353. package/dist/repertoire/coding/index.js +15 -3
  354. package/dist/repertoire/coding/manager.js +166 -10
  355. package/dist/repertoire/coding/spawner.js +58 -11
  356. package/dist/repertoire/coding/tools.js +242 -14
  357. package/dist/repertoire/coding/workbench-client.js +272 -0
  358. package/dist/repertoire/coding/workbench-manager.js +346 -0
  359. package/dist/repertoire/commerce-errors.js +109 -0
  360. package/dist/repertoire/commerce-self-test.js +156 -0
  361. package/dist/repertoire/credential-access.js +178 -0
  362. package/dist/repertoire/desk/classifier.js +362 -0
  363. package/dist/repertoire/duffel-client.js +185 -0
  364. package/dist/repertoire/github-client.js +14 -55
  365. package/dist/repertoire/graph-client.js +11 -52
  366. package/dist/repertoire/guardrails.js +812 -18
  367. package/dist/repertoire/mcp-client.js +44 -3
  368. package/dist/repertoire/mcp-manager.js +259 -20
  369. package/dist/repertoire/mcp-tools.js +83 -0
  370. package/dist/repertoire/plugin-mcp.js +175 -0
  371. package/dist/repertoire/plugins.js +253 -0
  372. package/dist/repertoire/shell-sessions.js +133 -0
  373. package/dist/repertoire/skills.js +48 -4
  374. package/dist/repertoire/stripe-client.js +131 -0
  375. package/dist/repertoire/tool-arguments.js +143 -0
  376. package/dist/repertoire/tool-results.js +29 -0
  377. package/dist/repertoire/tools-a2a.js +699 -0
  378. package/dist/repertoire/tools-attachments.js +317 -0
  379. package/dist/repertoire/tools-awaiting.js +372 -0
  380. package/dist/repertoire/tools-base.js +67 -1095
  381. package/dist/repertoire/tools-bluebubbles.js +2 -0
  382. package/dist/repertoire/tools-bridge.js +144 -0
  383. package/dist/repertoire/tools-bundle.js +993 -0
  384. package/dist/repertoire/tools-commerce.js +253 -0
  385. package/dist/repertoire/tools-config.js +186 -0
  386. package/dist/repertoire/tools-continuity.js +252 -0
  387. package/dist/repertoire/tools-credential.js +383 -0
  388. package/dist/repertoire/tools-evolution.js +527 -0
  389. package/dist/repertoire/tools-files.js +344 -0
  390. package/dist/repertoire/tools-flight.js +290 -0
  391. package/dist/repertoire/tools-flow.js +119 -0
  392. package/dist/repertoire/tools-github.js +3 -8
  393. package/dist/repertoire/tools-habits.js +103 -0
  394. package/dist/repertoire/tools-mail.js +1828 -0
  395. package/dist/repertoire/tools-notes.js +459 -0
  396. package/dist/repertoire/tools-obligations.js +182 -0
  397. package/dist/repertoire/tools-orientation.js +31 -0
  398. package/dist/repertoire/tools-record.js +482 -0
  399. package/dist/repertoire/tools-rsvp.js +139 -0
  400. package/dist/repertoire/tools-runtime.js +150 -0
  401. package/dist/repertoire/tools-session.js +881 -0
  402. package/dist/repertoire/tools-shell.js +133 -0
  403. package/dist/repertoire/tools-stripe.js +224 -0
  404. package/dist/repertoire/tools-surface.js +355 -0
  405. package/dist/repertoire/tools-teams.js +12 -39
  406. package/dist/repertoire/tools-travel.js +125 -0
  407. package/dist/repertoire/tools-trip.js +982 -0
  408. package/dist/repertoire/tools-unraid.js +353 -0
  409. package/dist/repertoire/tools-user-profile.js +146 -0
  410. package/dist/repertoire/tools-vault.js +40 -0
  411. package/dist/repertoire/tools-voice.js +145 -0
  412. package/dist/repertoire/tools.js +276 -79
  413. package/dist/repertoire/travel-api-client.js +360 -0
  414. package/dist/repertoire/unraid-client.js +155 -0
  415. package/dist/repertoire/unraid-restart.js +130 -0
  416. package/dist/repertoire/user-profile.js +131 -0
  417. package/dist/repertoire/vault-setup.js +246 -0
  418. package/dist/repertoire/vault-unlock.js +628 -0
  419. package/dist/rsvp/aisleplanner-client.js +354 -0
  420. package/dist/rsvp/cli.js +926 -0
  421. package/dist/rsvp/config.js +327 -0
  422. package/dist/rsvp/cutover.js +433 -0
  423. package/dist/rsvp/diagnostics.js +232 -0
  424. package/dist/rsvp/diff-renderer.js +176 -0
  425. package/dist/rsvp/habit-policy.js +110 -0
  426. package/dist/rsvp/habit-stage.js +123 -0
  427. package/dist/rsvp/incident-bundle.js +97 -0
  428. package/dist/rsvp/migration.js +204 -0
  429. package/dist/rsvp/native-habit-runner.js +630 -0
  430. package/dist/rsvp/outbound-state.js +628 -0
  431. package/dist/rsvp/query.js +118 -0
  432. package/dist/rsvp/replay.js +233 -0
  433. package/dist/rsvp/snapshot.js +232 -0
  434. package/dist/rsvp/spend-ledger.js +175 -0
  435. package/dist/scripts/claude-code-hook.js +41 -0
  436. package/dist/scripts/claude-code-stop-hook.js +47 -0
  437. package/dist/senses/a2a-entry.js +115 -0
  438. package/dist/senses/attention-queue.js +186 -0
  439. package/dist/senses/await-turn-message.js +58 -0
  440. package/dist/senses/bluebubbles/active-turns.js +216 -0
  441. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  442. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  443. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +355 -40
  444. package/dist/senses/bluebubbles/context-packet.js +207 -0
  445. package/dist/senses/bluebubbles/context-smoke.js +144 -0
  446. package/dist/senses/bluebubbles/entry.js +90 -0
  447. package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +21 -3
  448. package/dist/senses/bluebubbles/index.js +3839 -0
  449. package/dist/senses/bluebubbles/latest-turn.js +311 -0
  450. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -71
  451. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +159 -28
  452. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +24 -13
  453. package/dist/senses/bluebubbles/outbound-state.js +308 -0
  454. package/dist/senses/bluebubbles/processed-log.js +133 -0
  455. package/dist/senses/bluebubbles/reaction-policy.js +47 -0
  456. package/dist/senses/bluebubbles/replay.js +284 -0
  457. package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +30 -2
  458. package/dist/senses/bluebubbles/semantic-receipts.js +1986 -0
  459. package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
  460. package/dist/senses/bluebubbles/webhook-registration.js +228 -0
  461. package/dist/senses/bluebubbles-meta-guard.js +39 -0
  462. package/dist/senses/cli/bracketed-paste.js +82 -0
  463. package/dist/senses/cli/image-paste.js +287 -0
  464. package/dist/senses/cli/image-ref-navigation.js +75 -0
  465. package/dist/senses/cli/ink-app.js +156 -0
  466. package/dist/senses/cli/inline-diff.js +64 -0
  467. package/dist/senses/cli/input-keys.js +174 -0
  468. package/dist/senses/cli/kill-ring.js +86 -0
  469. package/dist/senses/cli/message-list.js +51 -0
  470. package/dist/senses/cli/ouro-tui.js +607 -0
  471. package/dist/senses/cli/spinner-imperative.js +135 -0
  472. package/dist/senses/cli/spinner.js +101 -0
  473. package/dist/senses/cli/status-line.js +60 -0
  474. package/dist/senses/cli/streaming-markdown.js +526 -0
  475. package/dist/senses/cli/tool-display.js +85 -0
  476. package/dist/senses/cli/tool-render.js +85 -0
  477. package/dist/senses/cli/tui-store.js +241 -0
  478. package/dist/senses/cli/virtual-list.js +35 -0
  479. package/dist/senses/cli-entry.js +60 -8
  480. package/dist/senses/cli-layout.js +100 -0
  481. package/dist/senses/cli.js +621 -321
  482. package/dist/senses/commands.js +66 -3
  483. package/dist/senses/context-packet-ledger.js +371 -0
  484. package/dist/senses/context-packets.js +154 -0
  485. package/dist/senses/habit-lifecycle-guard.js +24 -0
  486. package/dist/senses/habit-turn-message.js +160 -0
  487. package/dist/senses/ingress-evidence.js +2 -0
  488. package/dist/senses/mail-entry.js +66 -0
  489. package/dist/senses/mail.js +524 -0
  490. package/dist/senses/pipeline.js +1075 -176
  491. package/dist/senses/private-runtime-worker.js +741 -0
  492. package/dist/senses/private-runtime.js +1212 -0
  493. package/dist/senses/proactive-content-guard.js +51 -0
  494. package/dist/senses/sanctuary-grounding.js +118 -0
  495. package/dist/senses/sanctuary-health-acceptance-probe-entry.js +12 -0
  496. package/dist/senses/sanctuary-health-acceptance-probe.js +772 -0
  497. package/dist/senses/sanctuary-health-runner.js +146 -0
  498. package/dist/senses/sanctuary-health.js +447 -0
  499. package/dist/senses/sanctuary-interactive-control.js +306 -0
  500. package/dist/senses/sanctuary-runtime.js +206 -0
  501. package/dist/senses/shared-turn.js +432 -0
  502. package/dist/senses/surface-tool.js +109 -0
  503. package/dist/senses/teams-entry.js +60 -8
  504. package/dist/senses/teams.js +515 -217
  505. package/dist/senses/telegram-approval-runtime.js +526 -0
  506. package/dist/senses/telegram-audit-ledger.js +220 -0
  507. package/dist/senses/telegram-client.js +1471 -0
  508. package/dist/senses/telegram-entry.js +80 -0
  509. package/dist/senses/telegram.js +1232 -0
  510. package/dist/senses/trust-gate.js +104 -9
  511. package/dist/senses/voice/audio-playback.js +237 -0
  512. package/dist/senses/voice/audio-routing.js +119 -0
  513. package/dist/senses/voice/elevenlabs.js +202 -0
  514. package/dist/senses/voice/floor-control.js +431 -0
  515. package/dist/senses/voice/floor-controller.js +115 -0
  516. package/dist/senses/voice/golden-path.js +116 -0
  517. package/dist/senses/voice/index.js +29 -0
  518. package/dist/senses/voice/meeting.js +113 -0
  519. package/dist/senses/voice/outbound.js +190 -0
  520. package/dist/senses/voice/phone.js +33 -0
  521. package/dist/senses/voice/playback.js +139 -0
  522. package/dist/senses/voice/realtime-eval.js +496 -0
  523. package/dist/senses/voice/realtime-trace.js +531 -0
  524. package/dist/senses/voice/transcript.js +70 -0
  525. package/dist/senses/voice/turn.js +191 -0
  526. package/dist/senses/voice/twilio-phone-runtime.js +807 -0
  527. package/dist/senses/voice/twilio-phone.js +5081 -0
  528. package/dist/senses/voice/types.js +2 -0
  529. package/dist/senses/voice/whisper.js +161 -0
  530. package/dist/senses/voice-entry.js +81 -0
  531. package/dist/senses/voice-realtime-eval-command.js +99 -0
  532. package/dist/senses/voice-realtime-eval-entry.js +21 -0
  533. package/dist/senses/voice-twilio-entry.js +87 -0
  534. package/dist/trips/core.js +138 -0
  535. package/dist/trips/store.js +265 -0
  536. package/dist/util/frontmatter.js +69 -0
  537. package/npm-shrinkwrap.json +8129 -0
  538. package/package.json +71 -12
  539. package/skills/agent-commerce.md +113 -0
  540. package/skills/browser-navigation.md +117 -0
  541. package/skills/commerce-setup-guide.md +116 -0
  542. package/skills/commerce-setup.md +84 -0
  543. package/skills/configure-dev-tools.md +99 -0
  544. package/skills/travel-planning.md +138 -0
  545. package/dist/heart/daemon/auth-flow.js +0 -430
  546. package/dist/heart/daemon/ouro-path-installer.js +0 -178
  547. package/dist/heart/daemon/update-hooks.js +0 -138
  548. package/dist/heart/safe-workspace.js +0 -228
  549. package/dist/heart/session-recall.js +0 -116
  550. package/dist/mind/friends/channel.js +0 -84
  551. package/dist/mind/friends/group-context.js +0 -144
  552. package/dist/mind/friends/resolver.js +0 -84
  553. package/dist/mind/friends/store-file.js +0 -190
  554. package/dist/mind/friends/store.js +0 -4
  555. package/dist/mind/friends/tokens.js +0 -26
  556. package/dist/mind/friends/trust-explanation.js +0 -74
  557. package/dist/mind/friends/types.js +0 -29
  558. package/dist/repertoire/tasks/board.js +0 -134
  559. package/dist/repertoire/tasks/index.js +0 -224
  560. package/dist/repertoire/tasks/lifecycle.js +0 -80
  561. package/dist/repertoire/tasks/middleware.js +0 -65
  562. package/dist/repertoire/tasks/parser.js +0 -173
  563. package/dist/repertoire/tasks/scanner.js +0 -132
  564. package/dist/repertoire/tasks/transitions.js +0 -144
  565. package/dist/senses/bluebubbles-entry.js +0 -13
  566. package/dist/senses/bluebubbles.js +0 -1180
  567. package/dist/senses/debug-activity.js +0 -148
  568. package/dist/senses/inner-dialog-worker.js +0 -90
  569. package/dist/senses/inner-dialog.js +0 -480
  570. package/subagents/README.md +0 -7
  571. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  572. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  573. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  574. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  575. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  576. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  577. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  578. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  579. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  580. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  581. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  582. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  583. /package/dist/{repertoire/tasks/types.js → heart/attachments/sources/adapter.js} +0 -0
  584. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  585. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  586. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  587. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -0,0 +1,1232 @@
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.telegramAcceptanceAuditOwnerDigest = telegramAcceptanceAuditOwnerDigest;
37
+ exports.sanctuaryTelegramTurnReceiptDigest = sanctuaryTelegramTurnReceiptDigest;
38
+ exports.sanctuaryTelegramTurnReceiptMac = sanctuaryTelegramTurnReceiptMac;
39
+ exports.sanctuaryTelegramAuditLifecycleMac = sanctuaryTelegramAuditLifecycleMac;
40
+ exports.sanctuaryTelegramUnauthorizedDropMac = sanctuaryTelegramUnauthorizedDropMac;
41
+ exports.sanctuaryTelegramApprovalEvidenceMac = sanctuaryTelegramApprovalEvidenceMac;
42
+ exports.readOrCreateTelegramIdentityKey = readOrCreateTelegramIdentityKey;
43
+ exports.opaqueTelegramSubject = opaqueTelegramSubject;
44
+ exports.migrateTelegramFriendIdentity = migrateTelegramFriendIdentity;
45
+ exports.migrateTelegramSessionIdentity = migrateTelegramSessionIdentity;
46
+ exports.parseTelegramSenseCredentials = parseTelegramSenseCredentials;
47
+ exports.createTelegramSenseApp = createTelegramSenseApp;
48
+ exports.loadTelegramSenseCredentials = loadTelegramSenseCredentials;
49
+ exports.startTelegramSenseApp = startTelegramSenseApp;
50
+ const node_crypto_1 = require("node:crypto");
51
+ const node_async_hooks_1 = require("node:async_hooks");
52
+ const node_fs_1 = require("node:fs");
53
+ const path = __importStar(require("node:path"));
54
+ const friends_1 = require("@ouro.bot/friends");
55
+ const identity_1 = require("../heart/identity");
56
+ const sanctuary_acceptance_marker_1 = require("../heart/daemon/sanctuary-acceptance-marker");
57
+ const runtime_credentials_1 = require("../heart/runtime-credentials");
58
+ const runtime_1 = require("../nerves/runtime");
59
+ const nerves_1 = require("../nerves");
60
+ const sanctuary_interactive_control_1 = require("./sanctuary-interactive-control");
61
+ const shared_turn_1 = require("./shared-turn");
62
+ const telegram_client_1 = require("./telegram-client");
63
+ const sanctuary_runtime_1 = require("./sanctuary-runtime");
64
+ const sanctuary_grounding_1 = require("./sanctuary-grounding");
65
+ const telegram_approval_runtime_1 = require("./telegram-approval-runtime");
66
+ const telegram_audit_ledger_1 = require("./telegram-audit-ledger");
67
+ const APPROVAL_EXPIRY_RECONCILE_INTERVAL_MS = 1_000;
68
+ const TELEGRAM_SUBJECT_DOMAIN = "ouroboros.telegram.subject.v1";
69
+ const TELEGRAM_IDENTITY_KEY = /^[A-Za-z0-9_-]{43}$/u;
70
+ const TELEGRAM_SUBJECT = /^tg_[A-Za-z0-9_-]{43}$/u;
71
+ const TELEGRAM_SUBJECT_INDEX = "identity-subjects.json";
72
+ const TELEGRAM_ACCEPTANCE_AUDIT_EVENTS = new Set([
73
+ "approval.acceptance_continuation_transition",
74
+ "approval.acceptance_transition",
75
+ "senses.sanctuary_health_delivered",
76
+ "senses.sanctuary_read_receipt",
77
+ "senses.telegram_approved_restart_end",
78
+ "senses.telegram_approved_restart_error",
79
+ "senses.telegram_approved_restart_start",
80
+ "senses.telegram_approval_continuation_delivered",
81
+ "senses.telegram_approval_prompt_bound",
82
+ "senses.telegram_turn_end",
83
+ "senses.telegram_turn_error",
84
+ "senses.telegram_turn_start",
85
+ "telegram.approval_prompt_terminalized",
86
+ "telegram.approval_expiry_observed",
87
+ "telegram.approval_stale_callback_settled",
88
+ "telegram.callback_recovery_settled",
89
+ "telegram.callback_settled",
90
+ "telegram.update_dropped",
91
+ ]);
92
+ const telegramAcceptanceAuditOwner = new node_async_hooks_1.AsyncLocalStorage();
93
+ const telegramAcceptanceAuditScenario = new node_async_hooks_1.AsyncLocalStorage();
94
+ const telegramAcceptanceAuditExplicitCommit = new node_async_hooks_1.AsyncLocalStorage();
95
+ const telegramAcceptanceAudits = new Map();
96
+ const TELEGRAM_TURN_RECEIPT_DOMAINS = {
97
+ "sanctuary-telegram-turn-receipt-v3": "ouroboros.telegram.turn-receipt.v3",
98
+ "sanctuary-telegram-turn-receipt-v4": "ouroboros.telegram.turn-receipt.v4",
99
+ };
100
+ const TELEGRAM_TURN_LEDGER_MAX_BYTES = 4 * 1024 * 1024;
101
+ const TELEGRAM_TURN_LEDGER_MAX_ROWS = 500;
102
+ const TELEGRAM_TURN_LEDGER_MAX_ROW_BYTES = 16 * 1024;
103
+ const telegramTurnLedgerTails = new Map();
104
+ function telegramAcceptanceAuditOwnerDigest(identityKey, agentName, root) {
105
+ return (0, node_crypto_1.createHmac)("sha256", identityKey)
106
+ .update(`ouroboros.telegram.acceptance-audit-owner.v1\0${agentName}\0${root}`, "utf8")
107
+ .digest("hex");
108
+ }
109
+ function acquireTelegramAcceptanceAudit(root, agentName, identityKey, privateValues, scenarioHandleDigest, releaseHook, maxBytes) {
110
+ const existing = telegramAcceptanceAudits.get(root);
111
+ if (existing) {
112
+ if (existing.identityKey !== identityKey)
113
+ throw new Error("Telegram acceptance audit identity changed while active");
114
+ if (existing.scenarioHandleDigest !== scenarioHandleDigest)
115
+ throw new Error("Telegram acceptance audit scenario changed while active");
116
+ existing.ledger.assertHealthy();
117
+ existing.references += 1;
118
+ return { ledger: existing.ledger, ownerDigest: existing.ownerDigest, scenarioHandleDigest: existing.scenarioHandleDigest, release: () => {
119
+ existing.references -= 1;
120
+ if (existing.references === 0) {
121
+ try {
122
+ existing.unregister();
123
+ }
124
+ finally {
125
+ telegramAcceptanceAudits.delete(root);
126
+ }
127
+ releaseHook?.();
128
+ }
129
+ } };
130
+ }
131
+ const ledger = (0, telegram_audit_ledger_1.createTelegramAuditLedger)({ root, identityKey, privateValues, _maxBytes: maxBytes });
132
+ const ownerDigest = telegramAcceptanceAuditOwnerDigest(identityKey, agentName, root);
133
+ let record;
134
+ const unregister = (0, nerves_1.registerGlobalLogSink)((event) => {
135
+ if (!TELEGRAM_ACCEPTANCE_AUDIT_EVENTS.has(event.event))
136
+ return;
137
+ if (telegramAcceptanceAuditExplicitCommit.getStore())
138
+ return;
139
+ const explicitOwner = event.meta.acceptanceAuditOwnerDigest;
140
+ const contextualOwner = telegramAcceptanceAuditOwner.getStore();
141
+ if (explicitOwner !== ownerDigest && contextualOwner !== ownerDigest)
142
+ return;
143
+ const scenario = event.meta.scenarioHandleDigest;
144
+ if (typeof scenario !== "string" || !/^[0-9a-f]{64}$/u.test(scenario) || record.scenarioHandleDigest !== scenario) {
145
+ ledger.poison(new Error("Telegram acceptance audit scenario ownership drift"));
146
+ }
147
+ ledger.append(event);
148
+ });
149
+ record = { agentName, identityKey, ledger, ownerDigest, references: 1, scenarioHandleDigest, unregister };
150
+ telegramAcceptanceAudits.set(root, record);
151
+ return { ledger, ownerDigest, scenarioHandleDigest, release: () => {
152
+ record.references -= 1;
153
+ if (record.references === 0) {
154
+ try {
155
+ record.unregister();
156
+ }
157
+ finally {
158
+ telegramAcceptanceAudits.delete(root);
159
+ }
160
+ releaseHook?.();
161
+ }
162
+ } };
163
+ }
164
+ function sanctuaryTelegramTurnReceiptDigest(identityKey, schemaVersion, purpose, value) {
165
+ return (0, node_crypto_1.createHmac)("sha256", identityKey).update(`${TELEGRAM_TURN_RECEIPT_DOMAINS[schemaVersion]}\0${purpose}\0${value}`, "utf8").digest("hex");
166
+ }
167
+ function canonicalReceiptJson(value) {
168
+ if (Array.isArray(value))
169
+ return `[${value.map(canonicalReceiptJson).join(",")}]`;
170
+ if (value && typeof value === "object") {
171
+ const record = value;
172
+ return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${canonicalReceiptJson(record[key])}`).join(",")}}`;
173
+ }
174
+ const rendered = JSON.stringify(value);
175
+ if (rendered === undefined)
176
+ throw new Error("Telegram receipt contains an unsupported value");
177
+ return rendered;
178
+ }
179
+ function sanctuaryTelegramTurnReceiptMac(identityKey, value) {
180
+ const unsigned = Object.fromEntries(Object.entries(value).filter(([key]) => key !== "receiptMac"));
181
+ return sanctuaryTelegramTurnReceiptDigest(identityKey, "sanctuary-telegram-turn-receipt-v4", "receipt", canonicalReceiptJson(unsigned));
182
+ }
183
+ function sanctuaryTelegramAuditLifecycleMac(identityKey, schemaVersion, event, meta) {
184
+ const unsigned = Object.fromEntries(Object.entries(meta).filter(([key]) => key !== "lifecycleMac"));
185
+ return sanctuaryTelegramTurnReceiptDigest(identityKey, schemaVersion, "audit-lifecycle", canonicalReceiptJson({ event, meta: unsigned }));
186
+ }
187
+ function sanctuaryTelegramUnauthorizedDropMac(identityKey, schemaVersion, value) {
188
+ return sanctuaryTelegramTurnReceiptDigest(identityKey, schemaVersion, "unauthorized-drop", canonicalReceiptJson(value));
189
+ }
190
+ function sanctuaryTelegramApprovalEvidenceMac(identityKey, event, meta) {
191
+ const unsigned = Object.fromEntries(Object.entries(meta).filter(([key]) => key !== "evidenceMac"));
192
+ return sanctuaryTelegramTurnReceiptDigest(identityKey, "sanctuary-telegram-turn-receipt-v3", "approval-evidence", canonicalReceiptJson({ event, meta: unsigned }));
193
+ }
194
+ function sameTelegramLedgerMetadata(left, right) {
195
+ return left.dev === right.dev
196
+ && left.ino === right.ino
197
+ && left.mode === right.mode
198
+ && left.nlink === right.nlink
199
+ && left.uid === right.uid
200
+ && left.gid === right.gid
201
+ && left.size === right.size
202
+ && left.mtimeNs === right.mtimeNs
203
+ && left.ctimeNs === right.ctimeNs;
204
+ }
205
+ function readStableBoundedTelegramLedger(filePath, afterPreReadStat) {
206
+ let handle;
207
+ try {
208
+ handle = (0, node_fs_1.openSync)(filePath, node_fs_1.constants.O_RDONLY | node_fs_1.constants.O_NOFOLLOW);
209
+ }
210
+ catch (error) {
211
+ if (error.code === "ENOENT")
212
+ return null;
213
+ throw error;
214
+ }
215
+ try {
216
+ const before = (0, node_fs_1.fstatSync)(handle, { bigint: true });
217
+ if (!before.isFile())
218
+ throw new Error("Telegram turn receipt ledger must be a regular file");
219
+ if (before.size > BigInt(TELEGRAM_TURN_LEDGER_MAX_BYTES))
220
+ throw new Error("Telegram turn receipt ledger exceeds its bound");
221
+ afterPreReadStat?.(filePath);
222
+ const expectedBytes = Number(before.size);
223
+ const content = Buffer.allocUnsafe(expectedBytes);
224
+ let offset = 0;
225
+ while (offset < expectedBytes) {
226
+ const bytesRead = (0, node_fs_1.readSync)(handle, content, offset, expectedBytes - offset, offset);
227
+ if (bytesRead === 0)
228
+ throw new Error("Telegram turn receipt ledger changed during read");
229
+ offset += bytesRead;
230
+ }
231
+ const overflow = Buffer.allocUnsafe(1);
232
+ const overflowBytes = (0, node_fs_1.readSync)(handle, overflow, 0, 1, expectedBytes);
233
+ const after = (0, node_fs_1.fstatSync)(handle, { bigint: true });
234
+ const pathAfter = (0, node_fs_1.lstatSync)(filePath, { bigint: true });
235
+ if (overflowBytes !== 0 || !sameTelegramLedgerMetadata(before, after)
236
+ || !pathAfter.isFile() || pathAfter.dev !== before.dev || pathAfter.ino !== before.ino) {
237
+ throw new Error("Telegram turn receipt ledger changed during read");
238
+ }
239
+ return content.toString("utf8");
240
+ }
241
+ finally {
242
+ (0, node_fs_1.closeSync)(handle);
243
+ }
244
+ }
245
+ function validateSanctuaryTurnReceipt(value) {
246
+ const grounded = value.schemaVersion === "sanctuary-telegram-turn-receipt-v4";
247
+ const exactKeys = ["completedAt", "deliveries", "deliveryCount", "errorCategory", "providerInvocationCount", "responseDigest", "scenarioHandleDigest", "schemaVersion", "sequenceDigest", "status", "toolInvocationCount", "toolResultDigests", "updateDigest", ...(grounded ? ["receiptMac", "toolGroundings"] : [])].sort();
248
+ const deliveries = value.deliveries;
249
+ if (JSON.stringify(Object.keys(value).sort()) !== JSON.stringify(exactKeys)
250
+ || (!grounded && value.schemaVersion !== "sanctuary-telegram-turn-receipt-v3")
251
+ || typeof value.scenarioHandleDigest !== "string" || !/^[0-9a-f]{64}$/u.test(value.scenarioHandleDigest)
252
+ || (value.status !== "success" && value.status !== "error")
253
+ || (value.status === "success"
254
+ ? value.errorCategory !== null
255
+ : typeof value.errorCategory !== "string" || value.errorCategory.length < 1 || value.errorCategory.length > 128)
256
+ || ![value.updateDigest, value.sequenceDigest, value.responseDigest].every((digest) => typeof digest === "string" && /^[0-9a-f]{64}$/u.test(digest))
257
+ || !Array.isArray(value.toolResultDigests) || value.toolResultDigests.length > 100 || !value.toolResultDigests.every((digest) => typeof digest === "string" && /^[0-9a-f]{64}$/u.test(digest))
258
+ || !Array.isArray(deliveries) || deliveries.length > 100 || !deliveries.every((delivery) => {
259
+ if (!delivery || typeof delivery !== "object" || Array.isArray(delivery))
260
+ return false;
261
+ const record = delivery;
262
+ return JSON.stringify(Object.keys(record).sort()) === JSON.stringify(grounded ? ["chunkDigest", "messageIdDigest", "redactedText", "utf16Units"] : ["chunkDigest", "messageIdDigest"])
263
+ && typeof record.chunkDigest === "string" && /^[0-9a-f]{64}$/u.test(record.chunkDigest)
264
+ && typeof record.messageIdDigest === "string" && /^[0-9a-f]{64}$/u.test(record.messageIdDigest)
265
+ && (!grounded || (typeof record.redactedText === "string" && record.redactedText.length === record.utf16Units && record.utf16Units <= 1_200));
266
+ })
267
+ || (grounded && (!Array.isArray(value.toolGroundings) || value.toolGroundings.length !== 1 || !value.toolGroundings.every((raw) => {
268
+ if (!raw || typeof raw !== "object" || Array.isArray(raw))
269
+ return false;
270
+ const record = raw;
271
+ if (JSON.stringify(Object.keys(record).sort()) !== JSON.stringify(["facts", "groundingDigest", "observedAt", "resultDigest", "sourceIdentityDigest", "toolName"]) || (record.toolName !== "unraid_get_system" && record.toolName !== "unraid_get_storage")
272
+ || typeof record.resultDigest !== "string" || !/^[0-9a-f]{64}$/u.test(record.resultDigest) || typeof record.groundingDigest !== "string" || !/^[0-9a-f]{64}$/u.test(record.groundingDigest)
273
+ || typeof record.sourceIdentityDigest !== "string" || !/^[0-9a-f]{64}$/u.test(record.sourceIdentityDigest) || typeof record.observedAt !== "string" || !Number.isFinite(Date.parse(record.observedAt)) || new Date(Date.parse(record.observedAt)).toISOString() !== record.observedAt
274
+ || !record.facts || typeof record.facts !== "object" || Array.isArray(record.facts))
275
+ return false;
276
+ return (0, sanctuary_grounding_1.sanctuaryGroundingDigest)(record.facts) === record.groundingDigest && value.toolResultDigests.includes(record.resultDigest);
277
+ })))
278
+ || (grounded && (typeof value.receiptMac !== "string" || !/^[0-9a-f]{64}$/u.test(value.receiptMac)))
279
+ || ![value.providerInvocationCount, value.toolInvocationCount].every((count) => Number.isSafeInteger(count) && Number(count) >= 0 && Number(count) <= 1_000)
280
+ || !Number.isSafeInteger(value.deliveryCount) || value.deliveryCount !== deliveries.length
281
+ || typeof value.completedAt !== "string" || value.completedAt.length > 30 || !Number.isFinite(Date.parse(value.completedAt)) || new Date(Date.parse(value.completedAt)).toISOString() !== value.completedAt)
282
+ throw new Error("Telegram turn receipt ledger row is invalid");
283
+ }
284
+ async function appendSanctuaryTurnReceipt(agentRoot, receipt, afterPreReadStat) {
285
+ const filePath = path.join(agentRoot, "state", "acceptance", "telegram-turns.ndjson");
286
+ const previous = telegramTurnLedgerTails.get(filePath) ?? Promise.resolve();
287
+ const current = previous.catch(() => undefined).then(() => {
288
+ (0, node_fs_1.mkdirSync)(path.dirname(filePath), { recursive: true, mode: 0o700 });
289
+ let existing = [];
290
+ const raw = readStableBoundedTelegramLedger(filePath, afterPreReadStat);
291
+ if (raw !== null) {
292
+ existing = raw.split("\n").filter(Boolean);
293
+ if (existing.length > TELEGRAM_TURN_LEDGER_MAX_ROWS || existing.some((line) => Buffer.byteLength(line) > TELEGRAM_TURN_LEDGER_MAX_ROW_BYTES))
294
+ throw new Error("Telegram turn receipt ledger is invalid");
295
+ for (const line of existing) {
296
+ validateSanctuaryTurnReceipt(JSON.parse(line));
297
+ }
298
+ }
299
+ validateSanctuaryTurnReceipt(receipt);
300
+ const serialized = JSON.stringify(receipt);
301
+ if (Buffer.byteLength(serialized) > TELEGRAM_TURN_LEDGER_MAX_ROW_BYTES)
302
+ throw new Error("Telegram turn receipt exceeds its bound");
303
+ const lines = [...existing, serialized].slice(-TELEGRAM_TURN_LEDGER_MAX_ROWS);
304
+ let aggregateBytes = lines.reduce((total, line) => total + Buffer.byteLength(line) + 1, 0);
305
+ while (aggregateBytes > TELEGRAM_TURN_LEDGER_MAX_BYTES && lines.length > 1) {
306
+ aggregateBytes -= Buffer.byteLength(lines.shift()) + 1;
307
+ }
308
+ const temporary = `${filePath}.tmp-${process.pid}-${(0, node_crypto_1.randomUUID)()}`;
309
+ (0, node_fs_1.writeFileSync)(temporary, `${lines.join("\n")}\n`, { mode: 0o600, flag: "wx" });
310
+ const handle = (0, node_fs_1.openSync)(temporary, "r");
311
+ try {
312
+ (0, node_fs_1.fsyncSync)(handle);
313
+ }
314
+ finally {
315
+ (0, node_fs_1.closeSync)(handle);
316
+ }
317
+ (0, node_fs_1.renameSync)(temporary, filePath);
318
+ const directory = (0, node_fs_1.openSync)(path.dirname(filePath), "r");
319
+ try {
320
+ (0, node_fs_1.fsyncSync)(directory);
321
+ }
322
+ finally {
323
+ (0, node_fs_1.closeSync)(directory);
324
+ }
325
+ });
326
+ telegramTurnLedgerTails.set(filePath, current);
327
+ try {
328
+ await current;
329
+ }
330
+ finally {
331
+ if (telegramTurnLedgerTails.get(filePath) === current)
332
+ telegramTurnLedgerTails.delete(filePath);
333
+ }
334
+ }
335
+ function requiredText(value, label) {
336
+ if (typeof value !== "string" || value.trim().length === 0)
337
+ throw new Error(`Telegram ${label} is missing`);
338
+ return value.trim();
339
+ }
340
+ function canonicalTelegramId(value, label) {
341
+ const text = requiredText(value, label);
342
+ if (!/^[1-9][0-9]*$/u.test(text))
343
+ throw new Error(`Telegram ${label} must be a canonical positive decimal string`);
344
+ return text;
345
+ }
346
+ function canonicalTelegramIdentityKey(value) {
347
+ const key = value.trim();
348
+ if (!TELEGRAM_IDENTITY_KEY.test(key) || Buffer.from(key, "base64url").length !== 32) {
349
+ throw new Error("Telegram opaque identity key is invalid");
350
+ }
351
+ return key;
352
+ }
353
+ function errorCode(error) {
354
+ return error && typeof error === "object" && "code" in error && typeof error.code === "string" ? error.code : undefined;
355
+ }
356
+ function openSecureTelegramDirectory(directory, afterOpen) {
357
+ try {
358
+ const existing = (0, node_fs_1.lstatSync)(directory);
359
+ if (existing.isSymbolicLink())
360
+ throw new Error("Telegram state directory must not be a symbolic link");
361
+ if (!existing.isDirectory())
362
+ throw new Error("Telegram state directory is not a directory");
363
+ }
364
+ catch (error) {
365
+ if (errorCode(error) !== "ENOENT")
366
+ throw error;
367
+ (0, node_fs_1.mkdirSync)(directory, { recursive: true, mode: 0o700 });
368
+ }
369
+ const handle = (0, node_fs_1.openSync)(directory, node_fs_1.constants.O_RDONLY | node_fs_1.constants.O_DIRECTORY | node_fs_1.constants.O_NOFOLLOW);
370
+ try {
371
+ (0, node_fs_1.fchmodSync)(handle, 0o700);
372
+ afterOpen?.(handle);
373
+ const metadata = (0, node_fs_1.fstatSync)(handle);
374
+ if (!metadata.isDirectory() || (metadata.mode & 0o777) !== 0o700) {
375
+ throw new Error("Telegram state directory permissions are invalid");
376
+ }
377
+ return handle;
378
+ }
379
+ catch (error) {
380
+ (0, node_fs_1.closeSync)(handle);
381
+ throw error;
382
+ }
383
+ }
384
+ function readOrCreateTelegramIdentityKey(agentRoot, hooks = {}) {
385
+ const directory = path.join(agentRoot, "state", "senses", "telegram");
386
+ const keyPath = path.join(directory, "identity.key");
387
+ const directoryHandle = openSecureTelegramDirectory(directory, hooks.afterOpenDirectory);
388
+ const readKey = () => {
389
+ const handle = (0, node_fs_1.openSync)(keyPath, node_fs_1.constants.O_RDONLY | node_fs_1.constants.O_NOFOLLOW);
390
+ try {
391
+ const metadata = (0, node_fs_1.fstatSync)(handle);
392
+ if (!metadata.isFile() || (metadata.mode & 0o777) !== 0o600)
393
+ throw new Error("Telegram opaque identity key permissions are invalid");
394
+ return canonicalTelegramIdentityKey((0, node_fs_1.readFileSync)(handle, "utf8"));
395
+ }
396
+ finally {
397
+ (0, node_fs_1.closeSync)(handle);
398
+ }
399
+ };
400
+ try {
401
+ try {
402
+ return readKey();
403
+ }
404
+ catch (error) {
405
+ if (errorCode(error) !== "ENOENT")
406
+ throw error;
407
+ }
408
+ const key = (0, node_crypto_1.randomBytes)(32).toString("base64url");
409
+ hooks.beforeCreate?.(keyPath);
410
+ const temporaryPath = path.join(directory, `.identity.key.${(0, node_crypto_1.randomBytes)(12).toString("base64url")}.tmp`);
411
+ let temporaryHandle;
412
+ try {
413
+ temporaryHandle = (0, node_fs_1.openSync)(temporaryPath, node_fs_1.constants.O_CREAT | node_fs_1.constants.O_EXCL | node_fs_1.constants.O_WRONLY | node_fs_1.constants.O_NOFOLLOW, 0o600);
414
+ hooks.afterCreateTemporary?.(temporaryPath, keyPath);
415
+ (0, node_fs_1.writeFileSync)(temporaryHandle, `${key}\n`);
416
+ (0, node_fs_1.fsyncSync)(temporaryHandle);
417
+ (0, node_fs_1.closeSync)(temporaryHandle);
418
+ temporaryHandle = undefined;
419
+ hooks.beforePublish?.(temporaryPath, keyPath);
420
+ try {
421
+ (0, node_fs_1.linkSync)(temporaryPath, keyPath);
422
+ }
423
+ catch (error) {
424
+ if (errorCode(error) !== "EEXIST")
425
+ throw error;
426
+ return readKey();
427
+ }
428
+ (0, node_fs_1.fsyncSync)(directoryHandle);
429
+ return key;
430
+ }
431
+ finally {
432
+ if (temporaryHandle !== undefined)
433
+ (0, node_fs_1.closeSync)(temporaryHandle);
434
+ if ((0, node_fs_1.existsSync)(temporaryPath))
435
+ (0, node_fs_1.unlinkSync)(temporaryPath);
436
+ (0, node_fs_1.fsyncSync)(directoryHandle);
437
+ }
438
+ }
439
+ finally {
440
+ (0, node_fs_1.closeSync)(directoryHandle);
441
+ }
442
+ }
443
+ function opaqueTelegramSubject(identityKey, botToken, authorizedUserId, authorizedChatId) {
444
+ const payload = [
445
+ TELEGRAM_SUBJECT_DOMAIN,
446
+ `bot:${botToken.length}:${botToken}`,
447
+ `user:${authorizedUserId.length}:${authorizedUserId}`,
448
+ `chat:${authorizedChatId.length}:${authorizedChatId}`,
449
+ ].join("\0");
450
+ return `tg_${(0, node_crypto_1.createHmac)("sha256", identityKey).update(payload, "utf8").digest("base64url")}`;
451
+ }
452
+ function readTelegramSubjectIndex(agentRoot, subject) {
453
+ const indexPath = path.join(agentRoot, "state", "senses", "telegram", TELEGRAM_SUBJECT_INDEX);
454
+ let text;
455
+ let handle;
456
+ try {
457
+ handle = (0, node_fs_1.openSync)(indexPath, node_fs_1.constants.O_RDONLY | node_fs_1.constants.O_NOFOLLOW);
458
+ const metadata = (0, node_fs_1.fstatSync)(handle);
459
+ if (!metadata.isFile() || (metadata.mode & 0o777) !== 0o600)
460
+ throw new Error("Telegram identity subject index permissions are invalid");
461
+ text = (0, node_fs_1.readFileSync)(handle, "utf8");
462
+ }
463
+ catch (error) {
464
+ if (errorCode(error) === "ENOENT")
465
+ return [];
466
+ throw error;
467
+ }
468
+ finally {
469
+ if (handle !== undefined)
470
+ (0, node_fs_1.closeSync)(handle);
471
+ }
472
+ const parsed = JSON.parse(text);
473
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed))
474
+ throw new Error("Telegram identity subject index is invalid");
475
+ const record = parsed;
476
+ if (record.version !== 1 || typeof record.subject !== "string" || !TELEGRAM_SUBJECT.test(record.subject) || !Array.isArray(record.legacySubjects)
477
+ || Object.keys(record).sort().join(",") !== "legacySubjects,subject,version") {
478
+ throw new Error("Telegram identity subject index is invalid");
479
+ }
480
+ const legacySubjects = record.legacySubjects;
481
+ if (!legacySubjects.every((candidate) => typeof candidate === "string" && TELEGRAM_SUBJECT.test(candidate))) {
482
+ throw new Error("Telegram identity subject index is invalid");
483
+ }
484
+ return record.subject === subject ? [] : [record.subject];
485
+ }
486
+ function discoverTelegramFilesystemSubjects(agentRoot) {
487
+ const subjects = new Set();
488
+ const addFriendDirectorySubjects = (root) => {
489
+ let entries;
490
+ try {
491
+ entries = (0, node_fs_1.readdirSync)(root, { withFileTypes: true });
492
+ }
493
+ catch (error) {
494
+ if (errorCode(error) === "ENOENT")
495
+ return;
496
+ throw error;
497
+ }
498
+ for (const entry of entries) {
499
+ if (!entry.isDirectory() || !entry.name.startsWith("telegram-user:"))
500
+ continue;
501
+ const candidate = entry.name.slice("telegram-user:".length);
502
+ if (TELEGRAM_SUBJECT.test(candidate))
503
+ subjects.add(candidate);
504
+ }
505
+ };
506
+ addFriendDirectorySubjects(path.join(agentRoot, "state", "sessions"));
507
+ addFriendDirectorySubjects(path.join(agentRoot, "state", "pending"));
508
+ addFriendDirectorySubjects(path.join(agentRoot, "state", "pending-returns"));
509
+ const friendsRoot = path.join(agentRoot, "friends");
510
+ let friendFiles;
511
+ try {
512
+ friendFiles = (0, node_fs_1.readdirSync)(friendsRoot, { withFileTypes: true });
513
+ }
514
+ catch (error) {
515
+ if (errorCode(error) === "ENOENT")
516
+ return [...subjects];
517
+ throw error;
518
+ }
519
+ for (const entry of friendFiles) {
520
+ if (!entry.isFile() || !entry.name.endsWith(".json"))
521
+ continue;
522
+ const friend = JSON.parse((0, node_fs_1.readFileSync)(path.join(friendsRoot, entry.name), "utf8"));
523
+ if (!friend || typeof friend !== "object" || !Array.isArray(friend.externalIds)) {
524
+ throw new Error("Telegram Friend identity record is invalid");
525
+ }
526
+ for (const external of friend.externalIds) {
527
+ if (!external || typeof external !== "object")
528
+ throw new Error("Telegram Friend external identity is invalid");
529
+ const identity = external;
530
+ if (identity.provider === "telegram-user" && typeof identity.externalId === "string" && TELEGRAM_SUBJECT.test(identity.externalId)) {
531
+ subjects.add(identity.externalId);
532
+ }
533
+ }
534
+ }
535
+ return [...subjects];
536
+ }
537
+ function writeTelegramSubjectIndex(agentRoot, subject, legacySubjects, hooks = {}) {
538
+ const directory = path.join(agentRoot, "state", "senses", "telegram");
539
+ const indexPath = path.join(directory, TELEGRAM_SUBJECT_INDEX);
540
+ const directoryHandle = openSecureTelegramDirectory(directory);
541
+ const temporaryPath = path.join(directory, `.identity-subjects.${(0, node_crypto_1.randomBytes)(12).toString("base64url")}.tmp`);
542
+ let temporaryHandle;
543
+ try {
544
+ temporaryHandle = (0, node_fs_1.openSync)(temporaryPath, node_fs_1.constants.O_CREAT | node_fs_1.constants.O_EXCL | node_fs_1.constants.O_WRONLY | node_fs_1.constants.O_NOFOLLOW, 0o600);
545
+ hooks.afterCreateTemporary?.(temporaryPath);
546
+ const contents = `${JSON.stringify({ version: 1, subject, legacySubjects: [...legacySubjects] }, null, 2)}\n`;
547
+ (0, node_fs_1.writeFileSync)(temporaryHandle, contents);
548
+ (0, node_fs_1.fsyncSync)(temporaryHandle);
549
+ (0, node_fs_1.closeSync)(temporaryHandle);
550
+ temporaryHandle = undefined;
551
+ (0, node_fs_1.renameSync)(temporaryPath, indexPath);
552
+ (0, node_fs_1.fsyncSync)(directoryHandle);
553
+ }
554
+ finally {
555
+ if (temporaryHandle !== undefined)
556
+ (0, node_fs_1.closeSync)(temporaryHandle);
557
+ if ((0, node_fs_1.existsSync)(temporaryPath))
558
+ (0, node_fs_1.unlinkSync)(temporaryPath);
559
+ (0, node_fs_1.closeSync)(directoryHandle);
560
+ }
561
+ }
562
+ async function migrateTelegramFriendIdentity(agentRoot, legacyUserId, subject) {
563
+ const store = new friends_1.FileFriendStore(path.join(agentRoot, "friends"));
564
+ const legacy = await store.findByExternalId("telegram-user", legacyUserId);
565
+ if (!legacy)
566
+ return;
567
+ const current = await store.findByExternalId("telegram-user", subject);
568
+ if (current && current.id !== legacy.id)
569
+ throw new Error("Telegram friend identity migration is ambiguous");
570
+ legacy.externalIds = legacy.externalIds.map((external) => external.provider === "telegram-user" && external.externalId === legacyUserId
571
+ ? { ...external, externalId: subject }
572
+ : external);
573
+ if (legacy.name === `Telegram user ${legacyUserId}`)
574
+ legacy.name = `Telegram user ${subject}`;
575
+ legacy.updatedAt = new Date().toISOString();
576
+ await store.put(legacy.id, legacy);
577
+ }
578
+ function migrateTelegramSessionIdentity(agentRoot, legacyUserId, legacyChatId, subject) {
579
+ const legacyFriendId = `telegram-user:${legacyUserId}`;
580
+ const opaqueFriendId = `telegram-user:${subject}`;
581
+ const migrateDirectory = (root) => {
582
+ const legacy = path.join(root, legacyFriendId);
583
+ const opaque = path.join(root, opaqueFriendId);
584
+ if (!(0, node_fs_1.existsSync)(legacy))
585
+ return (0, node_fs_1.existsSync)(opaque) ? opaque : null;
586
+ if ((0, node_fs_1.existsSync)(opaque))
587
+ throw new Error("Telegram session identity migration is ambiguous");
588
+ (0, node_fs_1.mkdirSync)(root, { recursive: true });
589
+ (0, node_fs_1.renameSync)(legacy, opaque);
590
+ return opaque;
591
+ };
592
+ const sessions = migrateDirectory(path.join(agentRoot, "state", "sessions"));
593
+ if (sessions) {
594
+ const legacySession = path.join(sessions, "telegram", `telegram_${legacyChatId}.json`);
595
+ const opaqueSession = path.join(sessions, "telegram", `telegram_${subject}.json`);
596
+ if ((0, node_fs_1.existsSync)(legacySession)) {
597
+ if ((0, node_fs_1.existsSync)(opaqueSession))
598
+ throw new Error("Telegram session file migration is ambiguous");
599
+ (0, node_fs_1.renameSync)(legacySession, opaqueSession);
600
+ }
601
+ }
602
+ const pending = migrateDirectory(path.join(agentRoot, "state", "pending"));
603
+ if (pending) {
604
+ const legacyPending = path.join(pending, "telegram", `telegram:${legacyChatId}`);
605
+ const opaquePending = path.join(pending, "telegram", `telegram:${subject}`);
606
+ if ((0, node_fs_1.existsSync)(legacyPending)) {
607
+ if ((0, node_fs_1.existsSync)(opaquePending))
608
+ throw new Error("Telegram pending identity migration is ambiguous");
609
+ (0, node_fs_1.renameSync)(legacyPending, opaquePending);
610
+ }
611
+ }
612
+ migrateDirectory(path.join(agentRoot, "state", "pending-returns"));
613
+ }
614
+ function redactTelegramPrivateValues(error, privateValues) {
615
+ let message = error instanceof Error ? error.message : String(error);
616
+ for (const privateValue of privateValues)
617
+ message = message.split(privateValue).join("[redacted]");
618
+ return message;
619
+ }
620
+ function parseTelegramSenseCredentials(value) {
621
+ return {
622
+ botToken: requiredText(value.telegramBotToken, "bot token"),
623
+ authorizedUserId: canonicalTelegramId(value.telegramAuthorizedUserId, "authorized user id"),
624
+ authorizedChatId: canonicalTelegramId(value.telegramAuthorizedChatId, "authorized chat id"),
625
+ };
626
+ }
627
+ function createTelegramSenseApp(options) {
628
+ const botToken = requiredText(options.credentials.botToken, "bot token");
629
+ const authorizedUserId = canonicalTelegramId(options.credentials.authorizedUserId, "authorized user id");
630
+ const authorizedChatId = canonicalTelegramId(options.credentials.authorizedChatId, "authorized chat id");
631
+ const agentRoot = options._agentRoot ?? (0, identity_1.getAgentRoot)(options.agentName);
632
+ const identityKey = options.identityKey === undefined
633
+ ? readOrCreateTelegramIdentityKey(agentRoot)
634
+ : canonicalTelegramIdentityKey(options.identityKey);
635
+ const subject = opaqueTelegramSubject(identityKey, botToken, authorizedUserId, authorizedChatId);
636
+ const transportError = (error) => redactTelegramPrivateValues(error, [botToken, authorizedUserId, authorizedChatId]);
637
+ const api = options.api ?? (0, telegram_client_1.createTelegramBotApi)({ token: botToken });
638
+ const offsetStore = options.offsetStore ?? new telegram_client_1.FileTelegramOffsetStore(path.join(agentRoot, "state", "senses", "telegram", "offset.json"));
639
+ const inboxStore = options.inboxStore ?? new telegram_client_1.FileTelegramUpdateInboxStore(path.join(agentRoot, "state", "senses", "telegram", "inbox.json"));
640
+ const runTurn = options.runTurn ?? options._runTurn ?? shared_turn_1.runSenseTurn;
641
+ const collectToolReceipts = options._runWithToolReceiptCollection ?? sanctuary_runtime_1.runWithSanctuaryToolReceiptCollection;
642
+ const useSanctuaryRuntime = options.agentName === "sanctuary" && !options.runTurn;
643
+ const readScenarioHandleDigest = () => (options.acceptanceMarker
644
+ ? options.acceptanceMarker()
645
+ : (0, sanctuary_acceptance_marker_1.readSanctuaryAcceptanceMarker)(options.agentName))?.scenarioHandleDigest;
646
+ let acceptanceAuditLease;
647
+ const retireAcceptanceAudit = () => {
648
+ const lease = acceptanceAuditLease;
649
+ if (!lease)
650
+ return;
651
+ const errors = [];
652
+ try {
653
+ lease.ledger.assertHealthy();
654
+ }
655
+ catch (error) {
656
+ errors.push(error);
657
+ }
658
+ try {
659
+ lease.release();
660
+ }
661
+ catch (error) {
662
+ errors.push(error);
663
+ }
664
+ finally {
665
+ acceptanceAuditLease = undefined;
666
+ }
667
+ if (errors.length === 1)
668
+ throw errors[0];
669
+ if (errors.length > 1)
670
+ throw new AggregateError(errors, "Telegram acceptance audit retirement failed");
671
+ };
672
+ const ensureAcceptanceAudit = () => {
673
+ if (!useSanctuaryRuntime)
674
+ return undefined;
675
+ const scenarioHandleDigest = readScenarioHandleDigest();
676
+ const effectScenarioHandleDigest = telegramAcceptanceAuditScenario.getStore();
677
+ if (effectScenarioHandleDigest !== undefined && (scenarioHandleDigest ?? null) !== effectScenarioHandleDigest) {
678
+ const error = new Error("Telegram acceptance audit scenario ownership drift");
679
+ acceptanceAuditLease?.ledger.poison(error);
680
+ throw error;
681
+ }
682
+ if (scenarioHandleDigest === undefined) {
683
+ if (acceptanceAuditLease) {
684
+ retireAcceptanceAudit();
685
+ }
686
+ return undefined;
687
+ }
688
+ if (!/^[0-9a-f]{64}$/u.test(scenarioHandleDigest))
689
+ throw new Error("Telegram acceptance audit scenario handle is invalid");
690
+ if (!acceptanceAuditLease) {
691
+ acceptanceAuditLease = acquireTelegramAcceptanceAudit(options.acceptanceReceiptRoot ?? agentRoot, options.agentName, identityKey, [botToken, authorizedUserId, authorizedChatId], scenarioHandleDigest, options._acceptanceAuditReleaseHook, options._acceptanceAuditMaxBytes);
692
+ }
693
+ else if (scenarioHandleDigest !== acceptanceAuditLease.scenarioHandleDigest) {
694
+ retireAcceptanceAudit();
695
+ acceptanceAuditLease = acquireTelegramAcceptanceAudit(options.acceptanceReceiptRoot ?? agentRoot, options.agentName, identityKey, [botToken, authorizedUserId, authorizedChatId], scenarioHandleDigest, options._acceptanceAuditReleaseHook, options._acceptanceAuditMaxBytes);
696
+ }
697
+ return acceptanceAuditLease;
698
+ };
699
+ ensureAcceptanceAudit();
700
+ const acceptanceAuditBarrier = () => {
701
+ const lease = ensureAcceptanceAudit();
702
+ if (!lease)
703
+ return;
704
+ lease.ledger.assertHealthy();
705
+ lease.ledger.assertCapacity(8);
706
+ };
707
+ const runWithAcceptanceAuditOwner = async (operation) => {
708
+ const scenarioHandleDigest = readScenarioHandleDigest() ?? null;
709
+ return telegramAcceptanceAuditScenario.run(scenarioHandleDigest, async () => {
710
+ acceptanceAuditBarrier();
711
+ const ownerDigest = acceptanceAuditLease?.ownerDigest ?? "";
712
+ return telegramAcceptanceAuditOwner.run(ownerDigest, async () => {
713
+ try {
714
+ const result = await operation();
715
+ acceptanceAuditBarrier();
716
+ return result;
717
+ }
718
+ catch (error) {
719
+ try {
720
+ acceptanceAuditBarrier();
721
+ }
722
+ catch (auditError) {
723
+ if (error === auditError)
724
+ throw auditError;
725
+ throw new AggregateError([error, auditError], "Telegram effect and acceptance audit verification failed");
726
+ }
727
+ throw error;
728
+ }
729
+ });
730
+ });
731
+ };
732
+ const emitDurableSettlementEvidence = async (event, meta) => {
733
+ if (event === "telegram.callback_settled") {
734
+ await (0, runtime_1.emitNervesEventDurable)({
735
+ component: "senses",
736
+ event: "telegram.callback_settled",
737
+ message: "Telegram approval acceptance evidence durably recorded",
738
+ meta,
739
+ });
740
+ }
741
+ else if (event === "telegram.callback_recovery_settled") {
742
+ await (0, runtime_1.emitNervesEventDurable)({
743
+ component: "senses",
744
+ event: "telegram.callback_recovery_settled",
745
+ message: "Telegram approval acceptance evidence durably recorded",
746
+ meta,
747
+ });
748
+ }
749
+ else {
750
+ throw new Error("Telegram durable acceptance settlement event is unsupported");
751
+ }
752
+ };
753
+ const commitAcceptanceEvidence = useSanctuaryRuntime ? async (event, meta) => {
754
+ acceptanceAuditBarrier();
755
+ const lease = acceptanceAuditLease;
756
+ if (!lease) {
757
+ await emitDurableSettlementEvidence(event, meta);
758
+ return;
759
+ }
760
+ await telegramAcceptanceAuditExplicitCommit.run(true, async () => {
761
+ await emitDurableSettlementEvidence(event, meta);
762
+ });
763
+ acceptanceAuditBarrier();
764
+ lease.ledger.append({
765
+ ts: new Date().toISOString(),
766
+ level: "info",
767
+ event,
768
+ trace_id: (0, node_crypto_1.randomUUID)(),
769
+ component: "senses",
770
+ message: "Telegram approval acceptance evidence durably recorded",
771
+ meta,
772
+ });
773
+ lease.ledger.assertHealthy();
774
+ } : undefined;
775
+ const releaseAcceptanceAudit = (primaryError) => {
776
+ try {
777
+ acceptanceAuditLease?.release();
778
+ }
779
+ catch (releaseError) {
780
+ throw new AggregateError([primaryError, releaseError], "Telegram sense construction and audit release failed");
781
+ }
782
+ throw primaryError;
783
+ };
784
+ let toolContext;
785
+ let approvalRuntime;
786
+ let approvalTransport;
787
+ let interactiveControl;
788
+ try {
789
+ toolContext = useSanctuaryRuntime ? (options._toolContext ?? (0, sanctuary_runtime_1.createSanctuaryToolContext)(options.agentName)) : undefined;
790
+ approvalRuntime = options.approvalRuntime ?? (useSanctuaryRuntime ? (options._createApprovalRuntime ?? telegram_approval_runtime_1.createTelegramApprovalRuntime)({
791
+ agentName: options.agentName,
792
+ api,
793
+ authorizedUserId,
794
+ authorizedChatId,
795
+ subject,
796
+ identityKey,
797
+ toolContext: toolContext ?? {},
798
+ effectBarrier: acceptanceAuditBarrier,
799
+ dependencies: {
800
+ acceptanceMarker: () => {
801
+ const scenarioHandleDigest = readScenarioHandleDigest();
802
+ return scenarioHandleDigest ? { scenarioHandleDigest } : null;
803
+ },
804
+ commitAcceptanceEvidence,
805
+ },
806
+ }) : undefined);
807
+ approvalTransport = options.approvalTransport ?? approvalRuntime?.transport;
808
+ interactiveControl = useSanctuaryRuntime && approvalTransport
809
+ ? (options._createInteractiveControl ?? sanctuary_interactive_control_1.createSanctuaryInteractiveControl)({
810
+ agentRoot,
811
+ transport: approvalTransport,
812
+ authorizedUserId,
813
+ authorizedChatId,
814
+ runRequest: runWithAcceptanceAuditOwner,
815
+ })
816
+ : undefined;
817
+ }
818
+ catch (error) {
819
+ releaseAcceptanceAudit(error);
820
+ }
821
+ const healthSweep = options.healthSweep;
822
+ const migrateIdentity = options.migrateIdentity ?? (async () => {
823
+ const legacySubjects = new Set([
824
+ ...readTelegramSubjectIndex(agentRoot, subject),
825
+ ...discoverTelegramFilesystemSubjects(agentRoot),
826
+ ...(approvalRuntime?.legacySubjects?.() ?? []),
827
+ ]);
828
+ legacySubjects.delete(subject);
829
+ if (legacySubjects.size > 1)
830
+ throw new Error("Telegram legacy identity subject migration is ambiguous");
831
+ const migrationSubjects = [...legacySubjects];
832
+ for (const legacySubject of migrationSubjects) {
833
+ acceptanceAuditBarrier();
834
+ migrateTelegramSessionIdentity(agentRoot, legacySubject, legacySubject, subject);
835
+ acceptanceAuditBarrier();
836
+ await migrateTelegramFriendIdentity(agentRoot, legacySubject, subject);
837
+ }
838
+ acceptanceAuditBarrier();
839
+ migrateTelegramSessionIdentity(agentRoot, authorizedUserId, authorizedChatId, subject);
840
+ acceptanceAuditBarrier();
841
+ await migrateTelegramFriendIdentity(agentRoot, authorizedUserId, subject);
842
+ acceptanceAuditBarrier();
843
+ approvalRuntime?.migrateIdentity?.(migrationSubjects);
844
+ acceptanceAuditBarrier();
845
+ writeTelegramSubjectIndex(agentRoot, subject, migrationSubjects, options.subjectIndexHooks);
846
+ });
847
+ let approvalReconcileTimer;
848
+ let approvalReconciliationActive = false;
849
+ const approvalReconciliationsInFlight = new Set();
850
+ let nextApprovalReconcileDeadline;
851
+ let stopPromise;
852
+ let runPromise;
853
+ const clearApprovalReconcileTimer = () => {
854
+ if (approvalReconcileTimer)
855
+ clearTimeout(approvalReconcileTimer);
856
+ approvalReconcileTimer = undefined;
857
+ };
858
+ const scheduleApprovalReconcile = () => {
859
+ if (!approvalTransport || !approvalReconciliationActive)
860
+ return;
861
+ const scheduledAt = nextApprovalReconcileDeadline ?? (Date.now() + APPROVAL_EXPIRY_RECONCILE_INTERVAL_MS);
862
+ nextApprovalReconcileDeadline = scheduledAt + APPROVAL_EXPIRY_RECONCILE_INTERVAL_MS;
863
+ const delay = Math.max(0, scheduledAt - Date.now());
864
+ approvalReconcileTimer = setTimeout(() => {
865
+ approvalReconcileTimer = undefined;
866
+ scheduleApprovalReconcile();
867
+ const reconciliation = runWithAcceptanceAuditOwner(() => approvalTransport.reconcileExpired()).catch((error) => {
868
+ acceptanceAuditBarrier();
869
+ (0, runtime_1.emitNervesEvent)({
870
+ level: "error",
871
+ component: "senses",
872
+ event: "senses.telegram_approval_reconcile_error",
873
+ message: "Telegram approval expiry reconciliation failed",
874
+ meta: { agentName: options.agentName, subject, error: transportError(error) },
875
+ });
876
+ }).finally(() => {
877
+ approvalReconciliationsInFlight.delete(reconciliation);
878
+ });
879
+ approvalReconciliationsInFlight.add(reconciliation);
880
+ }, delay);
881
+ };
882
+ const runHealthSweep = async () => {
883
+ if (!healthSweep)
884
+ return;
885
+ try {
886
+ acceptanceAuditBarrier();
887
+ const result = await healthSweep();
888
+ if (result.message) {
889
+ acceptanceAuditBarrier();
890
+ if (result.deliveryId)
891
+ await healthSweep.markDeliveryAttempting?.(result.deliveryId);
892
+ const messageIds = await deliver(result.message);
893
+ acceptanceAuditBarrier();
894
+ if (result.deliveryId)
895
+ await healthSweep.markDelivered?.(result.deliveryId, messageIds);
896
+ (0, runtime_1.emitNervesEvent)({
897
+ component: "senses",
898
+ event: "senses.sanctuary_health_delivered",
899
+ message: "Sanctuary health notification was delivered",
900
+ meta: { agentName: options.agentName, deliveryCount: messageIds.length, ...(0, sanctuary_acceptance_marker_1.sanctuaryAcceptanceEventMeta)(options.agentName) },
901
+ });
902
+ }
903
+ }
904
+ catch (error) {
905
+ acceptanceAuditBarrier();
906
+ (0, runtime_1.emitNervesEvent)({
907
+ level: "error",
908
+ component: "senses",
909
+ event: "senses.sanctuary_health_error",
910
+ message: "Sanctuary deterministic health sweep failed",
911
+ meta: { agentName: options.agentName, subject, error: transportError(error) },
912
+ });
913
+ }
914
+ };
915
+ const deliver = async (text, signal, onMessageDelivered) => {
916
+ acceptanceAuditBarrier();
917
+ return (0, telegram_client_1.sendTelegramText)(api, authorizedChatId, text, signal, onMessageDelivered);
918
+ };
919
+ const onMessageBody = async (message) => {
920
+ const acceptanceMarker = options.acceptanceMarker ? options.acceptanceMarker() : (0, sanctuary_acceptance_marker_1.readSanctuaryAcceptanceMarker)(options.agentName);
921
+ const acceptanceMeta = (0, sanctuary_acceptance_marker_1.sanctuaryAcceptanceEventMeta)(options.agentName);
922
+ const groundedAcceptance = acceptanceMarker?.label === "unit-16d-whats-up" || acceptanceMarker?.label === "unit-16d-1-space";
923
+ const acceptanceSchema = groundedAcceptance ? "sanctuary-telegram-turn-receipt-v4" : "sanctuary-telegram-turn-receipt-v3";
924
+ const auditDigest = (purpose, value) => sanctuaryTelegramTurnReceiptDigest(identityKey, acceptanceSchema, purpose, value);
925
+ const lifecycleCoordinates = acceptanceMarker ? {
926
+ scenarioHandleDigest: acceptanceMarker.scenarioHandleDigest,
927
+ turnDigest: auditDigest("turn", `${message.updateId}\0${message.messageId}`),
928
+ updateDigest: auditDigest("update", `${message.updateId}\0${message.messageId}`),
929
+ subject,
930
+ identityDigest: auditDigest("identity", subject),
931
+ sessionDigest: auditDigest("session", `telegram:${subject}`),
932
+ argumentDigest: auditDigest("argument", message.text),
933
+ } : {};
934
+ const lifecycleStartedAt = Date.now();
935
+ const lifecycleMeta = (event, meta, lifecycleAt) => acceptanceMarker
936
+ ? { ...meta, lifecycleAt, lifecycleMac: sanctuaryTelegramAuditLifecycleMac(identityKey, acceptanceSchema, event, { ...meta, lifecycleAt }) }
937
+ : meta;
938
+ (0, runtime_1.emitNervesEvent)({
939
+ component: "senses",
940
+ event: "senses.telegram_turn_start",
941
+ message: "Telegram authorized turn started",
942
+ meta: lifecycleMeta("senses.telegram_turn_start", { agentName: options.agentName, subject, ...acceptanceMeta, ...lifecycleCoordinates }, lifecycleStartedAt),
943
+ });
944
+ acceptanceAuditBarrier();
945
+ let deliveryCount = 0;
946
+ const deliveredMessageIds = [];
947
+ const deliveredChunks = [];
948
+ let receiptStatus = "success";
949
+ let errorCategory = null;
950
+ const turnMetricsObserver = { providerInvocationCount: 0, toolInvocationCount: 0 };
951
+ const toolReceiptObserver = { toolResultDigests: [], toolGroundings: [] };
952
+ const normalizedRequest = message.text.normalize("NFKC").trim().toLocaleLowerCase("en-US");
953
+ const groundingIntentTool = /^(?:what['’]?s up|status)\??$/u.test(normalizedRequest) ? "unraid_get_system"
954
+ : /^how much (?:space|storage) is left\??$/u.test(normalizedRequest) ? "unraid_get_storage"
955
+ : null;
956
+ const bufferedGroundedDeliveries = [];
957
+ try {
958
+ acceptanceAuditBarrier();
959
+ const collected = await collectToolReceipts(() => runTurn({
960
+ agentName: options.agentName,
961
+ channel: "telegram",
962
+ sessionKey: `telegram:${subject}`,
963
+ friendId: `telegram-user:${subject}`,
964
+ identity: {
965
+ provider: "telegram-user",
966
+ externalId: subject,
967
+ displayName: `Telegram user ${subject}`,
968
+ },
969
+ userMessage: message.text,
970
+ turnMetricsObserver,
971
+ deliverySink: {
972
+ onDelivery: async (delivery) => {
973
+ if (groundingIntentTool) {
974
+ bufferedGroundedDeliveries.push(delivery.kind);
975
+ }
976
+ else {
977
+ await deliver(delivery.text, undefined, (messageId, chunk) => { deliveredMessageIds.push(messageId); deliveredChunks.push(chunk); });
978
+ deliveryCount += 1;
979
+ }
980
+ },
981
+ },
982
+ ...(toolContext ? { toolContext } : {}),
983
+ ...(approvalRuntime ? { approvalCoordinatorFactory: approvalRuntime.coordinator } : {}),
984
+ }), toolReceiptObserver);
985
+ const result = collected.result;
986
+ turnMetricsObserver.providerInvocationCount = Math.max(turnMetricsObserver.providerInvocationCount, result.providerInvocationCount ?? 0);
987
+ turnMetricsObserver.toolInvocationCount = Math.max(turnMetricsObserver.toolInvocationCount, result.toolInvocationCount ?? 0);
988
+ if (groundingIntentTool) {
989
+ const grounding = toolReceiptObserver.toolGroundings?.length === 1 ? toolReceiptObserver.toolGroundings[0] : undefined;
990
+ if (!grounding || grounding.toolName !== groundingIntentTool || bufferedGroundedDeliveries.length > 1
991
+ || (bufferedGroundedDeliveries.length === 1 && bufferedGroundedDeliveries[0] !== "settle"))
992
+ throw new Error("Canonical Sanctuary query did not produce exactly one matching grounded settle");
993
+ const canonical = (0, sanctuary_grounding_1.renderSanctuaryGroundedResponse)(grounding.toolName, grounding.facts);
994
+ await deliver(canonical, undefined, (messageId, chunk) => { deliveredMessageIds.push(messageId); deliveredChunks.push(chunk); });
995
+ deliveryCount = 1;
996
+ }
997
+ else if (deliveryCount === 0 && result.response.trim()) {
998
+ await deliver(result.response, undefined, (messageId, chunk) => { deliveredMessageIds.push(messageId); deliveredChunks.push(chunk); });
999
+ }
1000
+ (0, runtime_1.emitNervesEvent)({
1001
+ component: "senses",
1002
+ event: "senses.telegram_turn_end",
1003
+ message: "Telegram authorized turn completed",
1004
+ meta: lifecycleMeta("senses.telegram_turn_end", { agentName: options.agentName, subject, deliveryCount: Math.max(deliveryCount, result.response.trim() ? 1 : 0), ...acceptanceMeta, ...lifecycleCoordinates, ...(acceptanceMarker ? { outcome: "success", errorDigest: null } : {}) }, Math.max(Date.now(), lifecycleStartedAt + 1)),
1005
+ });
1006
+ }
1007
+ catch (error) {
1008
+ receiptStatus = "error";
1009
+ errorCategory = (error instanceof Error && error.name.trim() ? error.name : "unknown").slice(0, 128);
1010
+ (0, runtime_1.emitNervesEvent)({
1011
+ level: "error",
1012
+ component: "senses",
1013
+ event: "senses.telegram_turn_error",
1014
+ message: "Telegram authorized turn failed",
1015
+ meta: lifecycleMeta("senses.telegram_turn_error", {
1016
+ agentName: options.agentName,
1017
+ subject,
1018
+ ...acceptanceMeta,
1019
+ ...lifecycleCoordinates,
1020
+ ...(acceptanceMarker ? {
1021
+ outcome: "error",
1022
+ errorDigest: auditDigest("error", redactTelegramPrivateValues(error, [botToken, authorizedUserId, authorizedChatId, String(message.updateId), message.messageId])),
1023
+ deliveryCount: deliveredMessageIds.length,
1024
+ } : { error: redactTelegramPrivateValues(error, [botToken, authorizedUserId, authorizedChatId, String(message.updateId), message.messageId]) }),
1025
+ }, Math.max(Date.now(), lifecycleStartedAt + 1)),
1026
+ });
1027
+ const fallback = "I couldn't complete that turn. The failure was recorded; please try again.";
1028
+ if (deliveredMessageIds.length === 0)
1029
+ await deliver(fallback, undefined, (messageId, chunk) => { deliveredMessageIds.push(messageId); deliveredChunks.push(chunk); });
1030
+ }
1031
+ finally {
1032
+ if (acceptanceMarker) {
1033
+ acceptanceAuditBarrier();
1034
+ const grounded = groundedAcceptance;
1035
+ const schemaVersion = grounded ? "sanctuary-telegram-turn-receipt-v4" : "sanctuary-telegram-turn-receipt-v3";
1036
+ const hmac = (purpose, value) => sanctuaryTelegramTurnReceiptDigest(identityKey, schemaVersion, purpose, value);
1037
+ const redact = (value) => [botToken, authorizedUserId, authorizedChatId, String(message.updateId), message.messageId]
1038
+ .reduce((text, privateValue) => privateValue.length >= 5 ? text.replaceAll(privateValue, "[REDACTED]") : text, value);
1039
+ const deliveries = deliveredMessageIds.map((messageId, index) => {
1040
+ const chunk = deliveredChunks[index];
1041
+ const redactedText = redact(chunk);
1042
+ return { messageIdDigest: hmac("delivery", String(messageId)), chunkDigest: hmac("chunk", grounded ? redactedText : chunk), ...(grounded ? { redactedText, utf16Units: redactedText.length } : {}) };
1043
+ });
1044
+ try {
1045
+ const receipt = {
1046
+ schemaVersion,
1047
+ scenarioHandleDigest: acceptanceMarker.scenarioHandleDigest,
1048
+ status: receiptStatus,
1049
+ errorCategory,
1050
+ updateDigest: hmac("update", `${message.updateId}\0${message.messageId}`),
1051
+ sequenceDigest: hmac("sequence", String(message.updateId)),
1052
+ responseDigest: hmac("response", JSON.stringify(deliveries)),
1053
+ toolResultDigests: toolReceiptObserver.toolResultDigests,
1054
+ ...(grounded ? { toolGroundings: toolReceiptObserver.toolGroundings } : {}),
1055
+ providerInvocationCount: turnMetricsObserver.providerInvocationCount,
1056
+ toolInvocationCount: turnMetricsObserver.toolInvocationCount,
1057
+ deliveryCount: deliveries.length,
1058
+ deliveries,
1059
+ completedAt: new Date().toISOString(),
1060
+ };
1061
+ if (grounded)
1062
+ receipt.receiptMac = sanctuaryTelegramTurnReceiptMac(identityKey, receipt);
1063
+ await appendSanctuaryTurnReceipt(options.acceptanceReceiptRoot ?? agentRoot, receipt, options._afterAcceptanceLedgerPreReadStat);
1064
+ }
1065
+ catch (error) {
1066
+ (0, runtime_1.emitNervesEvent)({ level: "error", component: "senses", event: "senses.telegram_acceptance_receipt_error", message: "Telegram acceptance receipt persistence failed", meta: { agentName: options.agentName, scenarioHandleDigest: acceptanceMarker.scenarioHandleDigest, category: error instanceof Error ? error.name : "unknown" } });
1067
+ }
1068
+ }
1069
+ }
1070
+ };
1071
+ const onMessage = (message) => runWithAcceptanceAuditOwner(() => onMessageBody(message));
1072
+ const onUpdate = async (update) => {
1073
+ if (!update.callback_query || !approvalTransport)
1074
+ return false;
1075
+ return runWithAcceptanceAuditOwner(async () => (await approvalTransport.handleUpdate(update)).handled);
1076
+ };
1077
+ let poll;
1078
+ try {
1079
+ poll = (options.createLongPoll ?? telegram_client_1.createTelegramLongPoll)({
1080
+ api,
1081
+ expectedUserId: authorizedUserId,
1082
+ expectedChatId: authorizedChatId,
1083
+ offsetStore,
1084
+ inboxStore,
1085
+ onMessage,
1086
+ onUpdate,
1087
+ acceptanceEventMeta: (update, distinctAccount) => {
1088
+ const marker = options.acceptanceMarker ? options.acceptanceMarker() : (0, sanctuary_acceptance_marker_1.readSanctuaryAcceptanceMarker)(options.agentName);
1089
+ if (!marker)
1090
+ return {};
1091
+ const auditOwnerMeta = acceptanceAuditLease ? { acceptanceAuditOwnerDigest: acceptanceAuditLease.ownerDigest } : {};
1092
+ const messageId = update?.message?.message_id ?? update?.callback_query?.message?.message_id;
1093
+ const senderId = update?.message?.from?.id;
1094
+ if (!update || messageId === undefined || senderId === undefined)
1095
+ return { scenarioHandleDigest: marker.scenarioHandleDigest, ...auditOwnerMeta };
1096
+ const schemaVersion = marker.label === "unit-16d-whats-up" || marker.label === "unit-16d-1-space"
1097
+ ? "sanctuary-telegram-turn-receipt-v4" : "sanctuary-telegram-turn-receipt-v3";
1098
+ const digest = (purpose, value) => sanctuaryTelegramTurnReceiptDigest(identityKey, schemaVersion, purpose, value);
1099
+ const senderDistinct = String(senderId) !== authorizedUserId;
1100
+ if (distinctAccount !== senderDistinct)
1101
+ throw new Error("Telegram dropped-update identity classification mismatch");
1102
+ const binding = {
1103
+ scenarioHandleDigest: marker.scenarioHandleDigest,
1104
+ updateDigest: digest("update", `${update.update_id}\0${messageId}`),
1105
+ senderIdentityDigest: digest("sender-identity", String(senderId)),
1106
+ authorizedIdentityDigest: digest("sender-identity", authorizedUserId),
1107
+ senderDistinct,
1108
+ nextOffsetDigest: digest("next-update-id", String(update.update_id + 1)),
1109
+ };
1110
+ return { ...binding, ...auditOwnerMeta, dropMac: sanctuaryTelegramUnauthorizedDropMac(identityKey, schemaVersion, binding) };
1111
+ },
1112
+ onBeforeDispatch: acceptanceAuditBarrier,
1113
+ onDispatchSettled: acceptanceAuditBarrier,
1114
+ });
1115
+ }
1116
+ catch (error) {
1117
+ releaseAcceptanceAudit(error);
1118
+ }
1119
+ return {
1120
+ run(signal) {
1121
+ if (runPromise)
1122
+ return runPromise;
1123
+ runPromise = (async () => {
1124
+ await runWithAcceptanceAuditOwner(migrateIdentity);
1125
+ await runWithAcceptanceAuditOwner(async () => { await approvalRuntime?.recover(); });
1126
+ await runWithAcceptanceAuditOwner(async () => { await interactiveControl?.start(); });
1127
+ try {
1128
+ await runWithAcceptanceAuditOwner(async () => { await approvalTransport?.reconcileExpired(); });
1129
+ }
1130
+ catch (error) {
1131
+ acceptanceAuditBarrier();
1132
+ (0, runtime_1.emitNervesEvent)({
1133
+ level: "error",
1134
+ component: "senses",
1135
+ event: "senses.telegram_approval_reconcile_error",
1136
+ message: "Telegram approval expiry reconciliation failed",
1137
+ meta: { agentName: options.agentName, subject, error: transportError(error) },
1138
+ });
1139
+ }
1140
+ await runWithAcceptanceAuditOwner(runHealthSweep);
1141
+ (0, runtime_1.emitNervesEvent)({
1142
+ component: "senses",
1143
+ event: "senses.telegram_poll_start",
1144
+ message: "Telegram long poll started",
1145
+ meta: { agentName: options.agentName, subject },
1146
+ });
1147
+ approvalReconciliationActive = true;
1148
+ nextApprovalReconcileDeadline = undefined;
1149
+ scheduleApprovalReconcile();
1150
+ try {
1151
+ await poll.run(signal);
1152
+ }
1153
+ finally {
1154
+ approvalReconciliationActive = false;
1155
+ nextApprovalReconcileDeadline = undefined;
1156
+ clearApprovalReconcileTimer();
1157
+ await Promise.all([...approvalReconciliationsInFlight]);
1158
+ try {
1159
+ await runWithAcceptanceAuditOwner(async () => { await approvalTransport?.reconcileExpired(); });
1160
+ }
1161
+ catch (error) {
1162
+ acceptanceAuditBarrier();
1163
+ (0, runtime_1.emitNervesEvent)({
1164
+ level: "error",
1165
+ component: "senses",
1166
+ event: "senses.telegram_approval_reconcile_error",
1167
+ message: "Telegram approval expiry reconciliation failed",
1168
+ meta: { agentName: options.agentName, subject, error: transportError(error) },
1169
+ });
1170
+ }
1171
+ await interactiveControl?.stop();
1172
+ }
1173
+ })();
1174
+ return runPromise;
1175
+ },
1176
+ async sendProactive(text, signal) {
1177
+ await runWithAcceptanceAuditOwner(() => deliver(requiredText(text, "proactive message"), signal));
1178
+ },
1179
+ stop() {
1180
+ if (stopPromise)
1181
+ return stopPromise;
1182
+ stopPromise = (async () => {
1183
+ const errors = [];
1184
+ const attempt = async (operation) => {
1185
+ try {
1186
+ await operation();
1187
+ }
1188
+ catch (error) {
1189
+ errors.push(error);
1190
+ }
1191
+ };
1192
+ await attempt(() => poll.stop());
1193
+ await attempt(async () => { await runPromise?.catch(() => undefined); });
1194
+ await attempt(async () => { await Promise.all([...approvalReconciliationsInFlight]); });
1195
+ await attempt(async () => { await interactiveControl?.stop(); });
1196
+ await attempt(() => api.stop());
1197
+ await attempt(() => approvalRuntime?.close());
1198
+ await attempt(() => runWithAcceptanceAuditOwner(() => {
1199
+ (0, runtime_1.emitNervesEvent)({
1200
+ component: "senses",
1201
+ event: "senses.telegram_poll_end",
1202
+ message: "Telegram long poll stopped",
1203
+ meta: { agentName: options.agentName, subject },
1204
+ });
1205
+ }));
1206
+ await attempt(retireAcceptanceAudit);
1207
+ if (errors.length === 1)
1208
+ throw errors[0];
1209
+ if (errors.length > 1)
1210
+ throw new AggregateError(errors, "Telegram sense cleanup failed");
1211
+ })();
1212
+ return stopPromise;
1213
+ },
1214
+ };
1215
+ }
1216
+ function loadTelegramSenseCredentials(agentName) {
1217
+ const runtime = (0, runtime_credentials_1.readRuntimeCredentialConfig)(agentName);
1218
+ if (!runtime.ok) {
1219
+ throw new Error(`Telegram runtime config is ${runtime.reason}; actor: agent-runnable; run ouro connect telegram --agent ${agentName}`);
1220
+ }
1221
+ return parseTelegramSenseCredentials(runtime.config);
1222
+ }
1223
+ async function startTelegramSenseApp(agentName) {
1224
+ const app = createTelegramSenseApp({ agentName, credentials: loadTelegramSenseCredentials(agentName) });
1225
+ (0, runtime_1.emitNervesEvent)({
1226
+ component: "senses",
1227
+ event: "senses.telegram_app_ready",
1228
+ message: "Telegram sense app is ready",
1229
+ meta: { agentName },
1230
+ });
1231
+ return app;
1232
+ }