@ouro.bot/cli 0.1.0-alpha.70 → 0.1.0-alpha.700

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 (501) hide show
  1. package/README.md +133 -23
  2. package/RepairGuide.ouro/agent.json +5 -0
  3. package/RepairGuide.ouro/psyche/IDENTITY.md +19 -0
  4. package/RepairGuide.ouro/psyche/SOUL.md +55 -0
  5. package/RepairGuide.ouro/skills/diagnose-broken-remote.md +63 -0
  6. package/RepairGuide.ouro/skills/diagnose-stacked-typed-issues.md +35 -0
  7. package/RepairGuide.ouro/skills/diagnose-sync-blocked.md +54 -0
  8. package/RepairGuide.ouro/skills/diagnose-vault-expired.md +60 -0
  9. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +4 -2
  10. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +2 -2
  11. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
  12. package/changelog.json +4454 -14
  13. package/dist/a2a/card.js +57 -0
  14. package/dist/a2a/client.js +180 -0
  15. package/dist/a2a/config.js +50 -0
  16. package/dist/a2a/delegation-stores.js +56 -0
  17. package/dist/a2a/did-resolution.js +93 -0
  18. package/dist/a2a/identity.js +170 -0
  19. package/dist/a2a/inbound-share.js +107 -0
  20. package/dist/a2a/mission-result-wire.js +196 -0
  21. package/dist/a2a/onboarding.js +115 -0
  22. package/dist/a2a/pin-store.js +132 -0
  23. package/dist/a2a/seen-ledger.js +120 -0
  24. package/dist/a2a/server.js +604 -0
  25. package/dist/a2a/task-store.js +69 -0
  26. package/dist/a2a/transport.js +45 -0
  27. package/dist/a2a/types.js +3 -0
  28. package/dist/arc/attention-types.js +8 -0
  29. package/dist/arc/cares.js +144 -0
  30. package/dist/arc/episodes.js +118 -0
  31. package/dist/arc/evolution.js +490 -0
  32. package/dist/arc/flight-recorder.js +928 -0
  33. package/dist/arc/intentions.js +134 -0
  34. package/dist/arc/json-store.js +117 -0
  35. package/dist/arc/obligations.js +348 -0
  36. package/dist/arc/packets.js +336 -0
  37. package/dist/arc/presence.js +185 -0
  38. package/dist/arc/task-lifecycle.js +57 -0
  39. package/dist/commerce/store.js +755 -0
  40. package/dist/commerce/types.js +3 -0
  41. package/dist/heart/active-work.js +867 -43
  42. package/dist/heart/agent-entry.js +190 -3
  43. package/dist/heart/attachments/image-normalize.js +194 -0
  44. package/dist/heart/attachments/materialize.js +97 -0
  45. package/dist/heart/attachments/originals.js +88 -0
  46. package/dist/heart/attachments/render.js +29 -0
  47. package/dist/heart/attachments/sources/bluebubbles.js +156 -0
  48. package/dist/heart/attachments/sources/cli-local-file.js +78 -0
  49. package/dist/heart/attachments/sources/index.js +16 -0
  50. package/dist/heart/attachments/store.js +103 -0
  51. package/dist/heart/attachments/types.js +93 -0
  52. package/dist/heart/auth/auth-flow.js +479 -0
  53. package/dist/heart/autonomy-budget.js +434 -0
  54. package/dist/heart/awaiting/await-alert.js +146 -0
  55. package/dist/heart/awaiting/await-expiry.js +108 -0
  56. package/dist/heart/awaiting/await-loader.js +91 -0
  57. package/dist/heart/awaiting/await-parser.js +141 -0
  58. package/dist/heart/awaiting/await-runtime-state.js +100 -0
  59. package/dist/heart/awaiting/await-scheduler.js +378 -0
  60. package/dist/heart/background-operations.js +281 -0
  61. package/dist/heart/bridges/manager.js +137 -17
  62. package/dist/heart/bridges/store.js +14 -2
  63. package/dist/heart/bundle-state.js +168 -0
  64. package/dist/heart/commitments.js +135 -0
  65. package/dist/heart/config-registry.js +331 -0
  66. package/dist/heart/config.js +119 -127
  67. package/dist/heart/context-loss-gauntlet.js +354 -0
  68. package/dist/heart/context-loss-sentinel.js +1155 -0
  69. package/dist/heart/core.js +1306 -278
  70. package/dist/heart/cross-chat-delivery.js +8 -22
  71. package/dist/heart/daemon/agent-config-check.js +451 -0
  72. package/dist/heart/daemon/agent-discovery.js +185 -3
  73. package/dist/heart/daemon/agent-service.js +523 -0
  74. package/dist/heart/daemon/agentic-repair.js +547 -0
  75. package/dist/heart/daemon/await-private-wake.js +59 -0
  76. package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
  77. package/dist/heart/daemon/boot-sync-probe.js +197 -0
  78. package/dist/heart/daemon/cadence.js +180 -0
  79. package/dist/heart/daemon/cli-defaults.js +780 -0
  80. package/dist/heart/daemon/cli-desk.js +322 -0
  81. package/dist/heart/daemon/cli-exec.js +8142 -0
  82. package/dist/heart/daemon/cli-help.js +675 -0
  83. package/dist/heart/daemon/cli-parse.js +2461 -0
  84. package/dist/heart/daemon/cli-render-doctor.js +57 -0
  85. package/dist/heart/daemon/cli-render.js +770 -0
  86. package/dist/heart/daemon/cli-types.js +8 -0
  87. package/dist/heart/daemon/connect-bay.js +323 -0
  88. package/dist/heart/daemon/daemon-cli.js +29 -1930
  89. package/dist/heart/daemon/daemon-entry.js +822 -10
  90. package/dist/heart/daemon/daemon-health.js +176 -0
  91. package/dist/heart/daemon/daemon-rollup.js +57 -0
  92. package/dist/heart/daemon/daemon-runtime-sync.js +88 -13
  93. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  94. package/dist/heart/daemon/daemon.js +1264 -84
  95. package/dist/heart/daemon/dns-workflow.js +394 -0
  96. package/dist/heart/daemon/doctor-types.js +8 -0
  97. package/dist/heart/daemon/doctor.js +1126 -0
  98. package/dist/heart/daemon/habit-private-wake.js +56 -0
  99. package/dist/heart/daemon/health-monitor.js +142 -11
  100. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  101. package/dist/heart/daemon/hooks/bundle-meta.js +135 -1
  102. package/dist/heart/daemon/http-health-probe.js +80 -0
  103. package/dist/heart/daemon/human-command-screens.js +234 -0
  104. package/dist/heart/daemon/human-readiness.js +114 -0
  105. package/dist/heart/daemon/inner-status.js +78 -0
  106. package/dist/heart/daemon/interactive-repair.js +394 -0
  107. package/dist/heart/daemon/launchd.js +37 -8
  108. package/dist/heart/daemon/log-tailer.js +79 -10
  109. package/dist/heart/daemon/logs-prune.js +110 -0
  110. package/dist/heart/daemon/mcp-canary.js +297 -0
  111. package/dist/heart/daemon/message-router.js +45 -5
  112. package/dist/heart/daemon/migrate-to-desk.js +848 -0
  113. package/dist/heart/daemon/os-cron-deps.js +135 -0
  114. package/dist/heart/daemon/os-cron.js +22 -14
  115. package/dist/heart/daemon/ouro-bot-entry.js +4 -2
  116. package/dist/heart/daemon/ouro-entry.js +3 -1
  117. package/dist/heart/daemon/plugin-cli.js +432 -0
  118. package/dist/heart/daemon/process-manager.js +519 -40
  119. package/dist/heart/daemon/provider-discovery.js +137 -0
  120. package/dist/heart/daemon/provider-ping-progress.js +83 -0
  121. package/dist/heart/daemon/pulse.js +511 -0
  122. package/dist/heart/daemon/readiness-repair.js +365 -0
  123. package/dist/heart/daemon/run-hooks.js +2 -0
  124. package/dist/heart/daemon/runtime-logging.js +41 -14
  125. package/dist/heart/daemon/runtime-metadata.js +2 -30
  126. package/dist/heart/daemon/safe-mode.js +161 -0
  127. package/dist/heart/daemon/sense-manager.js +670 -48
  128. package/dist/heart/daemon/session-id-resolver.js +131 -0
  129. package/dist/heart/daemon/skill-management-installer.js +1 -1
  130. package/dist/heart/daemon/socket-client.js +171 -12
  131. package/dist/heart/daemon/stale-bundle-prune.js +113 -0
  132. package/dist/heart/daemon/startup-tui.js +338 -0
  133. package/dist/heart/daemon/task-scheduler.js +117 -39
  134. package/dist/heart/daemon/terminal-ui.js +499 -0
  135. package/dist/heart/daemon/thoughts.js +255 -43
  136. package/dist/heart/daemon/up-progress.js +366 -0
  137. package/dist/heart/daemon/vault-items.js +56 -0
  138. package/dist/heart/delegation.js +1 -4
  139. package/dist/heart/external-events/router.js +121 -0
  140. package/dist/heart/habits/habit-migration.js +189 -0
  141. package/dist/heart/habits/habit-parser.js +214 -0
  142. package/dist/heart/habits/habit-runtime-state.js +114 -0
  143. package/dist/heart/habits/habit-scheduler.js +431 -0
  144. package/dist/heart/habits/habit-session-summary.js +318 -0
  145. package/dist/heart/habits/habit-session.js +617 -0
  146. package/dist/heart/{daemon → hatch}/hatch-flow.js +51 -60
  147. package/dist/heart/{daemon → hatch}/hatch-specialist.js +6 -8
  148. package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
  149. package/dist/heart/{daemon → hatch}/specialist-tools.js +53 -18
  150. package/dist/heart/identity.js +174 -59
  151. package/dist/heart/kept-notes.js +289 -0
  152. package/dist/heart/kicks.js +1 -1
  153. package/dist/heart/machine-identity.js +161 -0
  154. package/dist/heart/mail-import-discovery.js +353 -0
  155. package/dist/heart/mailbox/mailbox-http-hooks.js +96 -0
  156. package/dist/heart/mailbox/mailbox-http-response.js +7 -0
  157. package/dist/heart/mailbox/mailbox-http-routes.js +375 -0
  158. package/dist/heart/mailbox/mailbox-http-static.js +103 -0
  159. package/dist/heart/mailbox/mailbox-http-transport.js +116 -0
  160. package/dist/heart/mailbox/mailbox-http.js +99 -0
  161. package/dist/heart/mailbox/mailbox-read.js +37 -0
  162. package/dist/heart/mailbox/mailbox-types.js +27 -0
  163. package/dist/heart/mailbox/mailbox-view.js +197 -0
  164. package/dist/heart/mailbox/readers/agent-machine.js +428 -0
  165. package/dist/heart/mailbox/readers/continuity-readers.js +329 -0
  166. package/dist/heart/mailbox/readers/mail.js +375 -0
  167. package/dist/heart/mailbox/readers/runtime-readers.js +815 -0
  168. package/dist/heart/mailbox/readers/sessions.js +232 -0
  169. package/dist/heart/mailbox/readers/shared.js +111 -0
  170. package/dist/heart/mcp/mcp-server.js +696 -0
  171. package/dist/heart/migrate-config.js +100 -0
  172. package/dist/heart/model-capabilities.js +11 -0
  173. package/dist/heart/orientation-frame.js +217 -0
  174. package/dist/heart/platform.js +81 -0
  175. package/dist/heart/primitives.js +1 -1
  176. package/dist/heart/private-runtime/decision-renderer.js +156 -0
  177. package/dist/heart/private-runtime/index.js +18 -0
  178. package/dist/heart/private-runtime/ledger.js +218 -0
  179. package/dist/heart/private-runtime/policy.js +214 -0
  180. package/dist/heart/private-runtime/types.js +2 -0
  181. package/dist/heart/progress-story.js +1 -1
  182. package/dist/heart/provider-attempt.js +134 -0
  183. package/dist/heart/provider-binding-resolver.js +273 -0
  184. package/dist/heart/provider-credentials.js +441 -0
  185. package/dist/heart/provider-failover.js +311 -0
  186. package/dist/heart/provider-models.js +81 -0
  187. package/dist/heart/provider-ping.js +262 -0
  188. package/dist/heart/provider-readiness-cache.js +119 -0
  189. package/dist/heart/provider-visibility.js +188 -0
  190. package/dist/heart/providers/anthropic-token.js +131 -0
  191. package/dist/heart/providers/anthropic.js +139 -52
  192. package/dist/heart/providers/azure.js +23 -11
  193. package/dist/heart/providers/error-classification.js +127 -0
  194. package/dist/heart/providers/github-copilot.js +34 -38
  195. package/dist/heart/providers/minimax-vlm.js +189 -0
  196. package/dist/heart/providers/minimax.js +26 -8
  197. package/dist/heart/providers/openai-codex-token.js +349 -0
  198. package/dist/heart/providers/openai-codex.js +55 -40
  199. package/dist/heart/run-ledger.js +247 -0
  200. package/dist/heart/runtime-capability-check.js +170 -0
  201. package/dist/heart/runtime-credentials.js +398 -0
  202. package/dist/heart/runtime-cwd.js +87 -0
  203. package/dist/heart/sense-truth.js +20 -4
  204. package/dist/heart/session-activity.js +90 -24
  205. package/dist/heart/session-events.js +1133 -0
  206. package/dist/heart/session-playback-cli-main.js +5 -0
  207. package/dist/heart/session-playback-cli.js +36 -0
  208. package/dist/heart/session-playback.js +231 -0
  209. package/dist/heart/session-stats-cli-main.js +5 -0
  210. package/dist/heart/session-stats.js +182 -0
  211. package/dist/heart/session-transcript.js +133 -0
  212. package/dist/heart/start-of-turn-packet.js +365 -0
  213. package/dist/heart/streaming.js +44 -27
  214. package/dist/heart/structured-output.js +196 -0
  215. package/dist/heart/sync-classification.js +176 -0
  216. package/dist/heart/sync.js +449 -0
  217. package/dist/heart/target-resolution.js +11 -7
  218. package/dist/heart/tempo.js +93 -0
  219. package/dist/heart/temporal-view.js +41 -0
  220. package/dist/heart/timeouts.js +101 -0
  221. package/dist/heart/tool-activity-callbacks.js +59 -0
  222. package/dist/heart/tool-description.js +155 -0
  223. package/dist/heart/tool-friction.js +55 -0
  224. package/dist/heart/tool-loop.js +200 -0
  225. package/dist/heart/turn-context.js +472 -0
  226. package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +6 -5
  227. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  228. package/dist/heart/versioning/ouro-path-installer.js +426 -0
  229. package/dist/heart/versioning/ouro-version-manager.js +409 -0
  230. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  231. package/dist/heart/{daemon → versioning}/update-checker.js +6 -1
  232. package/dist/heart/versioning/update-hooks.js +154 -0
  233. package/dist/heart/work-card.js +380 -0
  234. package/dist/mailbox-ui/assets/index-Du_9G9WO.css +1 -0
  235. package/dist/mailbox-ui/assets/index-J8zDwHmE.js +1 -0
  236. package/dist/mailbox-ui/assets/vendor-CcN1XpQ9.js +61 -0
  237. package/dist/mailbox-ui/index.html +16 -0
  238. package/dist/mailroom/attention.js +167 -0
  239. package/dist/mailroom/autonomy.js +209 -0
  240. package/dist/mailroom/blob-store.js +715 -0
  241. package/dist/mailroom/body-cache.js +61 -0
  242. package/dist/mailroom/core.js +788 -0
  243. package/dist/mailroom/entry.js +160 -0
  244. package/dist/mailroom/file-store.js +568 -0
  245. package/dist/mailroom/mbox-import.js +393 -0
  246. package/dist/mailroom/migration.js +164 -0
  247. package/dist/mailroom/outbound.js +380 -0
  248. package/dist/mailroom/policy.js +263 -0
  249. package/dist/mailroom/reader.js +233 -0
  250. package/dist/mailroom/search-cache.js +334 -0
  251. package/dist/mailroom/search-relevance.js +319 -0
  252. package/dist/mailroom/smtp-ingress.js +176 -0
  253. package/dist/mailroom/source-state.js +176 -0
  254. package/dist/mailroom/thread.js +109 -0
  255. package/dist/mailroom/travel-extract.js +89 -0
  256. package/dist/mind/bundle-manifest.js +24 -2
  257. package/dist/mind/context.js +250 -101
  258. package/dist/mind/desk-section.js +362 -0
  259. package/dist/mind/diary-integrity.js +60 -0
  260. package/dist/mind/{memory.js → diary.js} +68 -77
  261. package/dist/mind/embedding-provider.js +60 -0
  262. package/dist/mind/file-state.js +179 -0
  263. package/dist/mind/{associative-recall.js → note-search.js} +47 -58
  264. package/dist/mind/obligation-steering.js +221 -0
  265. package/dist/mind/pending.js +13 -8
  266. package/dist/mind/prompt-budget.js +380 -0
  267. package/dist/mind/prompt-refresh.js +3 -2
  268. package/dist/mind/prompt.js +1028 -169
  269. package/dist/mind/provenance-trust.js +26 -0
  270. package/dist/mind/record-paths.js +312 -0
  271. package/dist/mind/scrutiny.js +173 -0
  272. package/dist/nerves/cli-logging.js +7 -1
  273. package/dist/nerves/coverage/audit-rules.js +15 -6
  274. package/dist/nerves/coverage/audit.js +28 -2
  275. package/dist/nerves/coverage/cli.js +1 -1
  276. package/dist/nerves/coverage/contract.js +5 -5
  277. package/dist/nerves/coverage/file-completeness.js +139 -5
  278. package/dist/nerves/event-buffer.js +111 -0
  279. package/dist/nerves/index.js +234 -4
  280. package/dist/nerves/observation.js +20 -0
  281. package/dist/nerves/redact.js +79 -0
  282. package/dist/nerves/review/cli-main.js +5 -0
  283. package/dist/nerves/review/cli.js +156 -0
  284. package/dist/nerves/review/core.js +152 -0
  285. package/dist/nerves/runtime.js +5 -1
  286. package/dist/repertoire/ado-client.js +15 -56
  287. package/dist/repertoire/ado-semantic.js +16 -10
  288. package/dist/repertoire/api-client.js +97 -0
  289. package/dist/repertoire/bitwarden-store.js +1042 -0
  290. package/dist/repertoire/bundle-templates.js +71 -0
  291. package/dist/repertoire/bw-installer.js +180 -0
  292. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  293. package/dist/repertoire/coding/context-pack.js +331 -0
  294. package/dist/repertoire/coding/feedback.js +218 -30
  295. package/dist/repertoire/coding/index.js +15 -3
  296. package/dist/repertoire/coding/manager.js +166 -10
  297. package/dist/repertoire/coding/spawner.js +58 -11
  298. package/dist/repertoire/coding/tools.js +242 -14
  299. package/dist/repertoire/coding/workbench-client.js +272 -0
  300. package/dist/repertoire/coding/workbench-manager.js +346 -0
  301. package/dist/repertoire/commerce-errors.js +109 -0
  302. package/dist/repertoire/commerce-self-test.js +156 -0
  303. package/dist/repertoire/credential-access.js +178 -0
  304. package/dist/repertoire/desk/classifier.js +362 -0
  305. package/dist/repertoire/duffel-client.js +185 -0
  306. package/dist/repertoire/github-client.js +14 -55
  307. package/dist/repertoire/graph-client.js +11 -52
  308. package/dist/repertoire/guardrails.js +156 -27
  309. package/dist/repertoire/mcp-client.js +44 -3
  310. package/dist/repertoire/mcp-manager.js +259 -20
  311. package/dist/repertoire/mcp-tools.js +83 -0
  312. package/dist/repertoire/plugin-mcp.js +175 -0
  313. package/dist/repertoire/plugins.js +253 -0
  314. package/dist/repertoire/shell-sessions.js +133 -0
  315. package/dist/repertoire/skills.js +48 -4
  316. package/dist/repertoire/stripe-client.js +131 -0
  317. package/dist/repertoire/tool-results.js +29 -0
  318. package/dist/repertoire/tools-a2a.js +699 -0
  319. package/dist/repertoire/tools-attachments.js +317 -0
  320. package/dist/repertoire/tools-awaiting.js +372 -0
  321. package/dist/repertoire/tools-base.js +65 -1095
  322. package/dist/repertoire/tools-bluebubbles.js +2 -0
  323. package/dist/repertoire/tools-bridge.js +144 -0
  324. package/dist/repertoire/tools-bundle.js +993 -0
  325. package/dist/repertoire/tools-commerce.js +253 -0
  326. package/dist/repertoire/tools-config.js +186 -0
  327. package/dist/repertoire/tools-continuity.js +252 -0
  328. package/dist/repertoire/tools-credential.js +383 -0
  329. package/dist/repertoire/tools-evolution.js +527 -0
  330. package/dist/repertoire/tools-files.js +344 -0
  331. package/dist/repertoire/tools-flight.js +290 -0
  332. package/dist/repertoire/tools-flow.js +119 -0
  333. package/dist/repertoire/tools-github.js +3 -8
  334. package/dist/repertoire/tools-mail.js +1975 -0
  335. package/dist/repertoire/tools-notes.js +459 -0
  336. package/dist/repertoire/tools-obligations.js +182 -0
  337. package/dist/repertoire/tools-orientation.js +31 -0
  338. package/dist/repertoire/tools-record.js +482 -0
  339. package/dist/repertoire/tools-rsvp.js +139 -0
  340. package/dist/repertoire/tools-runtime.js +150 -0
  341. package/dist/repertoire/tools-session.js +881 -0
  342. package/dist/repertoire/tools-shell.js +120 -0
  343. package/dist/repertoire/tools-stripe.js +224 -0
  344. package/dist/repertoire/tools-surface.js +355 -0
  345. package/dist/repertoire/tools-teams.js +12 -39
  346. package/dist/repertoire/tools-travel.js +125 -0
  347. package/dist/repertoire/tools-trip.js +982 -0
  348. package/dist/repertoire/tools-user-profile.js +146 -0
  349. package/dist/repertoire/tools-vault.js +40 -0
  350. package/dist/repertoire/tools-voice.js +145 -0
  351. package/dist/repertoire/tools.js +250 -80
  352. package/dist/repertoire/travel-api-client.js +360 -0
  353. package/dist/repertoire/user-profile.js +131 -0
  354. package/dist/repertoire/vault-setup.js +246 -0
  355. package/dist/repertoire/vault-unlock.js +594 -0
  356. package/dist/rsvp/aisleplanner-client.js +354 -0
  357. package/dist/rsvp/cli.js +704 -0
  358. package/dist/rsvp/config.js +327 -0
  359. package/dist/rsvp/cutover.js +431 -0
  360. package/dist/rsvp/diagnostics.js +222 -0
  361. package/dist/rsvp/diff-renderer.js +161 -0
  362. package/dist/rsvp/habit-policy.js +85 -0
  363. package/dist/rsvp/habit-stage.js +93 -0
  364. package/dist/rsvp/incident-bundle.js +97 -0
  365. package/dist/rsvp/migration.js +204 -0
  366. package/dist/rsvp/outbound-state.js +254 -0
  367. package/dist/rsvp/query.js +118 -0
  368. package/dist/rsvp/replay.js +233 -0
  369. package/dist/rsvp/snapshot.js +232 -0
  370. package/dist/scripts/claude-code-hook.js +41 -0
  371. package/dist/scripts/claude-code-stop-hook.js +47 -0
  372. package/dist/senses/a2a-entry.js +115 -0
  373. package/dist/senses/attention-queue.js +186 -0
  374. package/dist/senses/await-turn-message.js +58 -0
  375. package/dist/senses/bluebubbles/active-turns.js +216 -0
  376. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  377. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  378. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +226 -19
  379. package/dist/senses/bluebubbles/entry.js +77 -0
  380. package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +20 -3
  381. package/dist/senses/bluebubbles/index.js +2937 -0
  382. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -71
  383. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +33 -12
  384. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +3 -3
  385. package/dist/senses/bluebubbles/outbound-state.js +304 -0
  386. package/dist/senses/bluebubbles/processed-log.js +133 -0
  387. package/dist/senses/bluebubbles/replay.js +284 -0
  388. package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +30 -2
  389. package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
  390. package/dist/senses/bluebubbles-meta-guard.js +39 -0
  391. package/dist/senses/cli/bracketed-paste.js +82 -0
  392. package/dist/senses/cli/image-paste.js +287 -0
  393. package/dist/senses/cli/image-ref-navigation.js +75 -0
  394. package/dist/senses/cli/ink-app.js +156 -0
  395. package/dist/senses/cli/inline-diff.js +64 -0
  396. package/dist/senses/cli/input-keys.js +174 -0
  397. package/dist/senses/cli/kill-ring.js +86 -0
  398. package/dist/senses/cli/message-list.js +51 -0
  399. package/dist/senses/cli/ouro-tui.js +607 -0
  400. package/dist/senses/cli/spinner-imperative.js +135 -0
  401. package/dist/senses/cli/spinner.js +101 -0
  402. package/dist/senses/cli/status-line.js +60 -0
  403. package/dist/senses/cli/streaming-markdown.js +526 -0
  404. package/dist/senses/cli/tool-display.js +85 -0
  405. package/dist/senses/cli/tool-render.js +85 -0
  406. package/dist/senses/cli/tui-store.js +240 -0
  407. package/dist/senses/cli/virtual-list.js +35 -0
  408. package/dist/senses/cli-entry.js +60 -8
  409. package/dist/senses/cli-layout.js +100 -0
  410. package/dist/senses/cli.js +521 -214
  411. package/dist/senses/commands.js +66 -3
  412. package/dist/senses/context-packet-ledger.js +245 -0
  413. package/dist/senses/context-packets.js +154 -0
  414. package/dist/senses/habit-turn-message.js +160 -0
  415. package/dist/senses/mail-entry.js +66 -0
  416. package/dist/senses/mail.js +395 -0
  417. package/dist/senses/pipeline.js +952 -178
  418. package/dist/senses/private-runtime-worker.js +723 -0
  419. package/dist/senses/private-runtime.js +1157 -0
  420. package/dist/senses/proactive-content-guard.js +51 -0
  421. package/dist/senses/shared-turn.js +416 -0
  422. package/dist/senses/surface-tool.js +109 -0
  423. package/dist/senses/teams-entry.js +60 -8
  424. package/dist/senses/teams.js +397 -112
  425. package/dist/senses/trust-gate.js +104 -9
  426. package/dist/senses/voice/audio-playback.js +237 -0
  427. package/dist/senses/voice/audio-routing.js +119 -0
  428. package/dist/senses/voice/elevenlabs.js +202 -0
  429. package/dist/senses/voice/floor-control.js +431 -0
  430. package/dist/senses/voice/floor-controller.js +115 -0
  431. package/dist/senses/voice/golden-path.js +116 -0
  432. package/dist/senses/voice/index.js +29 -0
  433. package/dist/senses/voice/meeting.js +113 -0
  434. package/dist/senses/voice/outbound.js +190 -0
  435. package/dist/senses/voice/phone.js +33 -0
  436. package/dist/senses/voice/playback.js +139 -0
  437. package/dist/senses/voice/realtime-eval.js +496 -0
  438. package/dist/senses/voice/realtime-trace.js +531 -0
  439. package/dist/senses/voice/transcript.js +70 -0
  440. package/dist/senses/voice/turn.js +191 -0
  441. package/dist/senses/voice/twilio-phone-runtime.js +807 -0
  442. package/dist/senses/voice/twilio-phone.js +5081 -0
  443. package/dist/senses/voice/types.js +2 -0
  444. package/dist/senses/voice/whisper.js +161 -0
  445. package/dist/senses/voice-entry.js +81 -0
  446. package/dist/senses/voice-realtime-eval-command.js +99 -0
  447. package/dist/senses/voice-realtime-eval-entry.js +21 -0
  448. package/dist/senses/voice-twilio-entry.js +87 -0
  449. package/dist/trips/core.js +138 -0
  450. package/dist/trips/store.js +265 -0
  451. package/dist/util/frontmatter.js +69 -0
  452. package/package.json +57 -12
  453. package/skills/agent-commerce.md +113 -0
  454. package/skills/browser-navigation.md +117 -0
  455. package/skills/commerce-setup-guide.md +116 -0
  456. package/skills/commerce-setup.md +84 -0
  457. package/skills/configure-dev-tools.md +99 -0
  458. package/skills/travel-planning.md +138 -0
  459. package/dist/heart/daemon/auth-flow.js +0 -409
  460. package/dist/heart/daemon/ouro-path-installer.js +0 -178
  461. package/dist/heart/daemon/update-hooks.js +0 -138
  462. package/dist/heart/safe-workspace.js +0 -228
  463. package/dist/heart/session-recall.js +0 -116
  464. package/dist/mind/friends/channel.js +0 -84
  465. package/dist/mind/friends/group-context.js +0 -144
  466. package/dist/mind/friends/resolver.js +0 -84
  467. package/dist/mind/friends/store-file.js +0 -190
  468. package/dist/mind/friends/store.js +0 -4
  469. package/dist/mind/friends/tokens.js +0 -26
  470. package/dist/mind/friends/trust-explanation.js +0 -74
  471. package/dist/mind/friends/types.js +0 -29
  472. package/dist/repertoire/tasks/board.js +0 -134
  473. package/dist/repertoire/tasks/index.js +0 -224
  474. package/dist/repertoire/tasks/lifecycle.js +0 -80
  475. package/dist/repertoire/tasks/middleware.js +0 -65
  476. package/dist/repertoire/tasks/parser.js +0 -173
  477. package/dist/repertoire/tasks/scanner.js +0 -132
  478. package/dist/repertoire/tasks/transitions.js +0 -144
  479. package/dist/senses/bluebubbles-entry.js +0 -13
  480. package/dist/senses/bluebubbles.js +0 -1180
  481. package/dist/senses/debug-activity.js +0 -148
  482. package/dist/senses/inner-dialog-worker.js +0 -90
  483. package/dist/senses/inner-dialog.js +0 -480
  484. package/subagents/README.md +0 -7
  485. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  486. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  487. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  488. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  489. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  490. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  491. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  492. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  493. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  494. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  495. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  496. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  497. /package/dist/{repertoire/tasks/types.js → heart/attachments/sources/adapter.js} +0 -0
  498. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  499. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  500. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  501. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -33,16 +33,31 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.downloadBlueBubblesAttachment = exports.VLM_TEXT_WRAPPERS = void 0;
36
37
  exports.hydrateBlueBubblesAttachments = hydrateBlueBubblesAttachments;
37
38
  const node_child_process_1 = require("node:child_process");
38
39
  const fs = __importStar(require("node:fs/promises"));
39
40
  const os = __importStar(require("node:os"));
40
41
  const path = __importStar(require("node:path"));
41
- const runtime_1 = require("../nerves/runtime");
42
- const identity_1 = require("../heart/identity");
43
- const MAX_ATTACHMENT_BYTES = 8 * 1024 * 1024;
42
+ const runtime_1 = require("../../nerves/runtime");
43
+ const identity_1 = require("../../heart/identity");
44
+ const model_capabilities_1 = require("../../heart/model-capabilities");
45
+ const image_normalize_1 = require("../../heart/attachments/image-normalize");
46
+ const store_1 = require("../../heart/attachments/store");
47
+ const bluebubbles_1 = require("../../heart/attachments/sources/bluebubbles");
48
+ const attachment_download_1 = require("./attachment-download");
49
+ // Pin the exact wrapper strings so tests and code read from one source.
50
+ exports.VLM_TEXT_WRAPPERS = {
51
+ description: (desc) => `[image description: ${desc}]`,
52
+ failure: (reason) => `[image description failed: ${reason}]`,
53
+ };
54
+ // D3 prompt template. Included here (not templated out) because the contract
55
+ // is load-bearing — see planning doc section D3 and AX-4.
56
+ function buildVlmPrompt(userText) {
57
+ const body = userText ?? "";
58
+ return `User message: "${body}"\n\nDescribe this image in detail, focusing on anything relevant to what the user said above.\nInclude any text visible in the image verbatim.`;
59
+ }
44
60
  const AUDIO_EXTENSIONS = new Set([".mp3", ".wav", ".m4a", ".caf", ".ogg"]);
45
- const IMAGE_EXTENSIONS = new Set([".jpg", ".jpeg", ".png", ".gif", ".webp", ".heic", ".heif"]);
46
61
  const AUDIO_EXTENSION_BY_CONTENT_TYPE = {
47
62
  "audio/wav": ".wav",
48
63
  "audio/x-wav": ".wav",
@@ -66,31 +81,19 @@ const AUDIO_INPUT_FORMAT_BY_EXTENSION = {
66
81
  const WHISPER_CPP_FORMULA = "whisper-cpp";
67
82
  const WHISPER_CPP_MODEL_NAME = "ggml-base.en.bin";
68
83
  const WHISPER_CPP_MODEL_URL = `https://huggingface.co/ggerganov/whisper.cpp/resolve/main/${WHISPER_CPP_MODEL_NAME}`;
69
- const WHISPER_CPP_TOOLS_DIR = path.join((0, identity_1.getAgentToolsRoot)(), "whisper-cpp");
70
- const WHISPER_CPP_MODELS_DIR = path.join(WHISPER_CPP_TOOLS_DIR, "models");
71
- const WHISPER_CPP_MODEL_PATH = path.join(WHISPER_CPP_MODELS_DIR, WHISPER_CPP_MODEL_NAME);
72
- function buildBlueBubblesApiUrl(baseUrl, endpoint, password) {
73
- const root = baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`;
74
- const url = new URL(endpoint.replace(/^\//, ""), root);
75
- url.searchParams.set("password", password);
76
- return url.toString();
84
+ // Lazy — getAgentToolsRoot() requires identity to be resolved, which isn't
85
+ // true at module-load time in most unit test contexts. Tests that only touch
86
+ // the image/VLM path shouldn't break because the audio path asked for a
87
+ // tools root they don't need.
88
+ function whisperCppPaths() {
89
+ const toolsDir = path.join((0, identity_1.getAgentToolsRoot)(), "whisper-cpp");
90
+ const modelsDir = path.join(toolsDir, "models");
91
+ const modelPath = path.join(modelsDir, WHISPER_CPP_MODEL_NAME);
92
+ return { toolsDir, modelsDir, modelPath };
77
93
  }
78
94
  function describeAttachment(attachment) {
79
95
  return attachment.transferName?.trim() || attachment.guid?.trim() || "attachment";
80
96
  }
81
- function inferContentType(attachment, responseType) {
82
- const normalizedResponseType = responseType?.split(";")[0]?.trim().toLowerCase();
83
- if (normalizedResponseType) {
84
- return normalizedResponseType;
85
- }
86
- return attachment.mimeType?.trim().toLowerCase() || undefined;
87
- }
88
- function isImageAttachment(attachment, contentType) {
89
- if (contentType?.startsWith("image/"))
90
- return true;
91
- const extension = path.extname(attachment.transferName ?? "").toLowerCase();
92
- return IMAGE_EXTENSIONS.has(extension);
93
- }
94
97
  function isAudioAttachment(attachment, contentType) {
95
98
  if (contentType?.startsWith("audio/"))
96
99
  return true;
@@ -170,12 +173,13 @@ async function resolveWhisperCppBinary(timeoutMs) {
170
173
  return candidate;
171
174
  }
172
175
  async function ensureWhisperCppModel(timeoutMs, fetchImpl) {
176
+ const { modelsDir, modelPath } = whisperCppPaths();
173
177
  try {
174
- await fs.access(WHISPER_CPP_MODEL_PATH);
175
- return WHISPER_CPP_MODEL_PATH;
178
+ await fs.access(modelPath);
179
+ return modelPath;
176
180
  }
177
181
  catch {
178
- await fs.mkdir(WHISPER_CPP_MODELS_DIR, { recursive: true });
182
+ await fs.mkdir(modelsDir, { recursive: true });
179
183
  const response = await fetchImpl(WHISPER_CPP_MODEL_URL, {
180
184
  method: "GET",
181
185
  signal: AbortSignal.timeout(Math.max(timeoutMs, 300_000)),
@@ -183,8 +187,8 @@ async function ensureWhisperCppModel(timeoutMs, fetchImpl) {
183
187
  if (!response.ok) {
184
188
  throw new Error(`failed to download whisper.cpp model: HTTP ${response.status}`);
185
189
  }
186
- await fs.writeFile(WHISPER_CPP_MODEL_PATH, Buffer.from(await response.arrayBuffer()));
187
- return WHISPER_CPP_MODEL_PATH;
190
+ await fs.writeFile(modelPath, Buffer.from(await response.arrayBuffer()));
191
+ return modelPath;
188
192
  }
189
193
  }
190
194
  async function convertAudioForWhisperCpp(sourcePath, outputPath, timeoutMs) {
@@ -236,59 +240,105 @@ async function transcribeAudioWithWhisperCpp(params, modelFetchImpl = fetch) {
236
240
  await fs.rm(workDir, { recursive: true, force: true }).catch(() => undefined);
237
241
  }
238
242
  }
239
- async function downloadAttachment(attachment, config, channelConfig, fetchImpl) {
240
- const guid = attachment.guid?.trim();
241
- if (!guid) {
242
- throw new Error("attachment guid missing");
243
- }
244
- if (typeof attachment.totalBytes === "number" && attachment.totalBytes > MAX_ATTACHMENT_BYTES) {
245
- throw new Error(`attachment exceeds ${MAX_ATTACHMENT_BYTES} byte limit`);
246
- }
247
- const url = buildBlueBubblesApiUrl(config.serverUrl, `/api/v1/attachment/${encodeURIComponent(guid)}/download`, config.password);
248
- const response = await fetchImpl(url, {
249
- method: "GET",
250
- signal: AbortSignal.timeout(channelConfig.requestTimeoutMs),
251
- });
252
- if (!response.ok) {
253
- throw new Error(`HTTP ${response.status}`);
254
- }
255
- const buffer = Buffer.from(await response.arrayBuffer());
256
- if (buffer.length > MAX_ATTACHMENT_BYTES) {
257
- throw new Error(`attachment exceeds ${MAX_ATTACHMENT_BYTES} byte limit`);
258
- }
259
- return {
260
- buffer,
261
- contentType: inferContentType(attachment, response.headers.get("content-type")),
262
- };
263
- }
243
+ var attachment_download_2 = require("./attachment-download");
244
+ Object.defineProperty(exports, "downloadBlueBubblesAttachment", { enumerable: true, get: function () { return attachment_download_2.downloadBlueBubblesAttachment; } });
264
245
  async function hydrateBlueBubblesAttachments(attachments, config, channelConfig, deps = {}) {
265
- (0, runtime_1.emitNervesEvent)({
266
- component: "senses",
267
- event: "senses.bluebubbles_media_hydrate",
268
- message: "hydrating bluebubbles attachments",
269
- meta: {
270
- attachmentCount: attachments.length,
271
- preferAudioInput: deps.preferAudioInput ?? false,
272
- },
273
- });
274
246
  const fetchImpl = deps.fetchImpl ?? fetch;
275
247
  const modelFetchImpl = deps.modelFetchImpl ?? fetch;
276
248
  const transcribeAudio = deps.transcribeAudio ?? ((params) => transcribeAudioWithWhisperCpp(params, modelFetchImpl));
277
249
  const preferAudioInput = deps.preferAudioInput ?? false;
250
+ const chatModel = deps.chatModel;
251
+ const visionCapable = chatModel ? (0, model_capabilities_1.getModelCapabilities)(chatModel).vision === true : true;
252
+ const agentName = (0, identity_1.getAgentName)();
253
+ const agentRoot = (0, identity_1.getAgentRoot)(agentName);
278
254
  const inputParts = [];
279
255
  const transcriptAdditions = [];
280
256
  const notices = [];
281
257
  for (const attachment of attachments) {
282
258
  const name = describeAttachment(attachment);
259
+ const initialRecord = attachment.guid?.trim()
260
+ ? (0, store_1.cacheRecentAttachment)(agentName, (0, bluebubbles_1.buildBlueBubblesAttachmentRecord)(attachment), agentRoot)
261
+ : null;
283
262
  try {
284
- const downloaded = await downloadAttachment(attachment, config, channelConfig, fetchImpl);
263
+ const downloaded = await (0, attachment_download_1.downloadBlueBubblesAttachment)(attachment, config, channelConfig, fetchImpl);
285
264
  const base64 = downloaded.buffer.toString("base64");
286
- if (isImageAttachment(attachment, downloaded.contentType)) {
287
- inputParts.push({
288
- type: "image_url",
289
- image_url: {
290
- url: `data:${downloaded.contentType ?? "application/octet-stream"};base64,${base64}`,
291
- detail: "auto",
265
+ const byteCount = downloaded.buffer.length;
266
+ const persistedRecord = initialRecord && initialRecord.source === "bluebubbles"
267
+ ? await (0, bluebubbles_1.persistBlueBubblesAttachmentSource)(agentName, initialRecord, {
268
+ buffer: downloaded.buffer,
269
+ mimeType: downloaded.contentType,
270
+ byteCount,
271
+ }, agentRoot)
272
+ : null;
273
+ if ((0, attachment_download_1.isBlueBubblesImageAttachment)(attachment, downloaded.contentType)) {
274
+ const mimeType = downloaded.contentType ?? "application/octet-stream";
275
+ if (visionCapable) {
276
+ inputParts.push({
277
+ type: "image_url",
278
+ image_url: {
279
+ url: `data:${mimeType};base64,${base64}`,
280
+ detail: "auto",
281
+ },
282
+ });
283
+ (0, runtime_1.emitNervesEvent)({
284
+ level: "warn",
285
+ component: "senses",
286
+ event: "senses.bluebubbles_media_hydrate",
287
+ message: "bluebubbles media hydrate",
288
+ meta: {
289
+ attachmentGuid: attachment.guid,
290
+ mimeType,
291
+ byteCount,
292
+ hydrationPath: "native-passthrough",
293
+ },
294
+ });
295
+ continue;
296
+ }
297
+ try {
298
+ if (!deps.vlmDescribe) {
299
+ throw new Error("no VLM describer configured — wire a vlmDescribe dep or configure a vision-capable chat model");
300
+ }
301
+ if (!persistedRecord?.sourceData.localPath) {
302
+ throw new Error("image attachment could not be persisted for normalization");
303
+ }
304
+ const normalized = await (0, image_normalize_1.normalizeImageForVision)({
305
+ attachment: persistedRecord,
306
+ sourcePath: persistedRecord.sourceData.localPath,
307
+ agentName,
308
+ agentRoot,
309
+ });
310
+ const normalizedBuffer = await fs.readFile(normalized.path);
311
+ const normalizedMime = normalized.mimeType ?? persistedRecord.mimeType ?? "image/jpeg";
312
+ const dataUrl = `data:${normalizedMime};base64,${normalizedBuffer.toString("base64")}`;
313
+ const description = await deps.vlmDescribe({
314
+ prompt: buildVlmPrompt(deps.userText),
315
+ imageDataUrl: dataUrl,
316
+ attachmentGuid: attachment.guid,
317
+ mimeType: normalizedMime,
318
+ chatModel,
319
+ });
320
+ inputParts.push({
321
+ type: "text",
322
+ text: exports.VLM_TEXT_WRAPPERS.description(description),
323
+ });
324
+ }
325
+ catch (vlmError) {
326
+ const reason = vlmError instanceof Error ? vlmError.message : String(vlmError);
327
+ inputParts.push({
328
+ type: "text",
329
+ text: exports.VLM_TEXT_WRAPPERS.failure(reason),
330
+ });
331
+ }
332
+ (0, runtime_1.emitNervesEvent)({
333
+ level: "warn",
334
+ component: "senses",
335
+ event: "senses.bluebubbles_media_hydrate",
336
+ message: "bluebubbles media hydrate",
337
+ meta: {
338
+ attachmentGuid: attachment.guid,
339
+ mimeType: persistedRecord?.mimeType ?? mimeType,
340
+ byteCount,
341
+ hydrationPath: "vlm-describe",
292
342
  },
293
343
  });
294
344
  continue;
@@ -1,7 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BlueBubblesIgnoredEventError = void 0;
3
4
  exports.normalizeBlueBubblesEvent = normalizeBlueBubblesEvent;
4
- const runtime_1 = require("../nerves/runtime");
5
+ const runtime_1 = require("../../nerves/runtime");
6
+ const render_1 = require("../../heart/attachments/render");
7
+ const bluebubbles_1 = require("../../heart/attachments/sources/bluebubbles");
8
+ const IGNORABLE_GUIDLESS_EVENT_TYPES = new Set([
9
+ "chat-read-status-changed",
10
+ ]);
11
+ class BlueBubblesIgnoredEventError extends Error {
12
+ eventType;
13
+ constructor(eventType, message) {
14
+ super(message);
15
+ this.name = "BlueBubblesIgnoredEventError";
16
+ this.eventType = eventType;
17
+ }
18
+ }
19
+ exports.BlueBubblesIgnoredEventError = BlueBubblesIgnoredEventError;
5
20
  function asRecord(value) {
6
21
  return value && typeof value === "object" && !Array.isArray(value)
7
22
  ? value
@@ -107,18 +122,14 @@ function extractAttachments(data) {
107
122
  function formatAttachmentText(attachments) {
108
123
  if (attachments.length === 0)
109
124
  return "";
125
+ const renderable = attachments
126
+ .filter((attachment) => typeof attachment.guid === "string" && attachment.guid.trim().length > 0)
127
+ .map((attachment) => (0, bluebubbles_1.buildBlueBubblesAttachmentRecord)(attachment));
128
+ if (renderable.length > 0) {
129
+ return (0, render_1.renderAttachmentBlock)(renderable);
130
+ }
110
131
  const [first] = attachments;
111
- const mime = first.mimeType ?? "";
112
- const label = mime.startsWith("image/")
113
- ? "image attachment"
114
- : mime.startsWith("audio/")
115
- ? "audio attachment"
116
- : "attachment";
117
- const name = first.transferName ? `: ${first.transferName}` : "";
118
- const dimensions = typeof first.width === "number" && typeof first.height === "number" && first.width > 0 && first.height > 0
119
- ? ` (${first.width}x${first.height})`
120
- : "";
121
- return `[${label}${name}${dimensions}]`;
132
+ return `[attachment: ${first.transferName?.trim() || "unknown"}]`;
122
133
  }
123
134
  function formatMessageText(data, attachments) {
124
135
  const text = readString(data, "text")?.trim() ?? "";
@@ -127,6 +138,13 @@ function formatMessageText(data, attachments) {
127
138
  if (balloonBundleId === "com.apple.messages.URLBalloonProvider") {
128
139
  return `${text}\n[link preview attached]`;
129
140
  }
141
+ // B2 fix: when text and attachments both exist, append the attachment
142
+ // marker so downstream senses can see the guid/filename. Previously the
143
+ // marker was dropped whenever text was non-empty, which hid images from
144
+ // the agent when the user captioned a screenshot.
145
+ if (attachments.length > 0) {
146
+ return `${text}\n${formatAttachmentText(attachments)}`;
147
+ }
130
148
  return text;
131
149
  }
132
150
  return formatAttachmentText(attachments);
@@ -198,6 +216,9 @@ function normalizeBlueBubblesEvent(payload) {
198
216
  message: "ignored bluebubbles payload without guid",
199
217
  meta: { eventType },
200
218
  });
219
+ if (IGNORABLE_GUIDLESS_EVENT_TYPES.has(eventType)) {
220
+ throw new BlueBubblesIgnoredEventError(eventType, `Ignored BlueBubbles event '${eventType}' without data.guid`);
221
+ }
201
222
  throw new Error("BlueBubbles payload is missing data.guid");
202
223
  }
203
224
  const threadOriginatorGuid = readString(data, "threadOriginatorGuid")?.trim() || undefined;
@@ -38,9 +38,9 @@ exports.recordBlueBubblesMutation = recordBlueBubblesMutation;
38
38
  exports.listBlueBubblesRecoveryCandidates = listBlueBubblesRecoveryCandidates;
39
39
  const fs = __importStar(require("node:fs"));
40
40
  const path = __importStar(require("node:path"));
41
- const runtime_1 = require("../nerves/runtime");
42
- const identity_1 = require("../heart/identity");
43
- const config_1 = require("../heart/config");
41
+ const runtime_1 = require("../../nerves/runtime");
42
+ const identity_1 = require("../../heart/identity");
43
+ const config_1 = require("../../heart/config");
44
44
  function getBlueBubblesMutationLogPath(agentName, sessionKey) {
45
45
  return path.join((0, identity_1.getAgentRoot)(agentName), "state", "senses", "bluebubbles", "mutations", `${(0, config_1.sanitizeKey)(sessionKey)}.ndjson`);
46
46
  }
@@ -0,0 +1,304 @@
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.BLUEBUBBLES_OUTBOUND_STATE_POLICY_VERSION = void 0;
37
+ exports.blueBubblesOutboundRecordPath = blueBubblesOutboundRecordPath;
38
+ exports.buildBlueBubblesRouteProof = buildBlueBubblesRouteProof;
39
+ exports.buildBlueBubblesAttachmentProof = buildBlueBubblesAttachmentProof;
40
+ exports.readBlueBubblesOutboundRecord = readBlueBubblesOutboundRecord;
41
+ exports.reserveBlueBubblesOutbound = reserveBlueBubblesOutbound;
42
+ exports.markBlueBubblesOutboundAccepted = markBlueBubblesOutboundAccepted;
43
+ exports.markBlueBubblesOutboundEnqueued = markBlueBubblesOutboundEnqueued;
44
+ exports.markBlueBubblesOutboundLocalVisible = markBlueBubblesOutboundLocalVisible;
45
+ exports.markBlueBubblesOutboundDelivered = markBlueBubblesOutboundDelivered;
46
+ exports.markBlueBubblesOutboundFailed = markBlueBubblesOutboundFailed;
47
+ exports.markBlueBubblesOutboundPendingManualVerification = markBlueBubblesOutboundPendingManualVerification;
48
+ const fs = __importStar(require("node:fs"));
49
+ const path = __importStar(require("node:path"));
50
+ const node_crypto_1 = require("node:crypto");
51
+ const runtime_1 = require("../../nerves/runtime");
52
+ exports.BLUEBUBBLES_OUTBOUND_STATE_POLICY_VERSION = "bluebubbles-outbound-state/v1";
53
+ function sha256Hex(input) {
54
+ return (0, node_crypto_1.createHash)("sha256").update(input).digest("hex");
55
+ }
56
+ function hash(input) {
57
+ return `sha256:${sha256Hex(canonicalJson(input))}`;
58
+ }
59
+ function canonicalJson(value) {
60
+ if (Array.isArray(value))
61
+ return `[${value.map((entry) => canonicalJson(entry)).join(",")}]`;
62
+ if (value && typeof value === "object") {
63
+ const record = value;
64
+ return `{${Object.keys(record).sort().map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
65
+ }
66
+ return JSON.stringify(value);
67
+ }
68
+ function nowIso(input) {
69
+ return input ?? new Date().toISOString();
70
+ }
71
+ function recordIdFor(idempotencyKey) {
72
+ return `bbout_${sha256Hex(`bluebubbles-outbound:${idempotencyKey}`).slice(0, 32)}`;
73
+ }
74
+ function normalizeText(value) {
75
+ return value.trim().replace(/\s+/g, " ");
76
+ }
77
+ function endpointOrigin(serverUrl) {
78
+ const parsed = new URL(serverUrl);
79
+ return parsed.origin;
80
+ }
81
+ function blueBubblesOutboundDir(agentRoot) {
82
+ return path.join(agentRoot, "state", "bluebubbles", "outbound");
83
+ }
84
+ function blueBubblesOutboundRecordPath(agentRoot, recordId) {
85
+ return path.join(blueBubblesOutboundDir(agentRoot), `${recordId}.json`);
86
+ }
87
+ function buildBlueBubblesRouteProof(chat) {
88
+ const participantHandles = [...chat.participantHandles].sort();
89
+ const base = {
90
+ chatGuid: chat.chatGuid?.trim() || null,
91
+ chatIdentifier: chat.chatIdentifier?.trim() || null,
92
+ sendTarget: chat.sendTarget,
93
+ sessionKey: chat.sessionKey,
94
+ isGroup: chat.isGroup,
95
+ participantHandles,
96
+ };
97
+ return {
98
+ chatGuid: base.chatGuid,
99
+ chatIdentifier: base.chatIdentifier,
100
+ sendTarget: base.sendTarget,
101
+ sessionKey: base.sessionKey,
102
+ isGroup: base.isGroup,
103
+ participantHandlesHash: hash(participantHandles),
104
+ routeHash: hash(base),
105
+ rawParticipantHandlesStored: false,
106
+ };
107
+ }
108
+ function buildBlueBubblesAttachmentProof(input) {
109
+ const proof = {
110
+ endpointOrigin: endpointOrigin(input.serverUrl),
111
+ accountId: input.accountId?.trim() || null,
112
+ };
113
+ return {
114
+ ...proof,
115
+ identityHash: hash(proof),
116
+ secretStored: false,
117
+ };
118
+ }
119
+ function buildRecord(input) {
120
+ const createdAt = nowIso(input.now);
121
+ const idempotencyKey = input.idempotencyKey.trim();
122
+ const text = normalizeText(input.text);
123
+ return {
124
+ schemaVersion: 1,
125
+ policyVersion: exports.BLUEBUBBLES_OUTBOUND_STATE_POLICY_VERSION,
126
+ recordId: recordIdFor(idempotencyKey),
127
+ idempotencyKey,
128
+ idempotencyHash: hash(idempotencyKey),
129
+ status: "reserved",
130
+ createdAt,
131
+ updatedAt: createdAt,
132
+ tempGuid: input.tempGuid.trim(),
133
+ routeProof: buildBlueBubblesRouteProof(input.chat),
134
+ attachmentProof: buildBlueBubblesAttachmentProof(input.attachment),
135
+ textHash: hash(text),
136
+ textLength: text.length,
137
+ ...(input.replyToMessageGuid?.trim() ? { replyToMessageGuidHash: hash(input.replyToMessageGuid.trim()) } : {}),
138
+ contentStored: false,
139
+ };
140
+ }
141
+ function writeJsonAtomic(filePath, value) {
142
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
143
+ const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;
144
+ fs.writeFileSync(tmp, `${JSON.stringify(value, null, 2)}\n`, "utf-8");
145
+ fs.renameSync(tmp, filePath);
146
+ }
147
+ function readJson(filePath) {
148
+ if (!fs.existsSync(filePath))
149
+ return null;
150
+ return JSON.parse(fs.readFileSync(filePath, "utf-8"));
151
+ }
152
+ function isOutboundRecord(value) {
153
+ const row = value;
154
+ return !!row
155
+ && row.schemaVersion === 1
156
+ && row.policyVersion === exports.BLUEBUBBLES_OUTBOUND_STATE_POLICY_VERSION
157
+ && typeof row.recordId === "string"
158
+ && typeof row.idempotencyKey === "string"
159
+ && typeof row.idempotencyHash === "string"
160
+ && typeof row.status === "string"
161
+ && typeof row.createdAt === "string"
162
+ && typeof row.updatedAt === "string"
163
+ && typeof row.tempGuid === "string"
164
+ && typeof row.textHash === "string"
165
+ && typeof row.textLength === "number"
166
+ && row.contentStored === false;
167
+ }
168
+ function readBlueBubblesOutboundRecord(agentRoot, recordId) {
169
+ try {
170
+ const parsed = readJson(blueBubblesOutboundRecordPath(agentRoot, recordId));
171
+ return isOutboundRecord(parsed) ? parsed : null;
172
+ }
173
+ catch {
174
+ return null;
175
+ }
176
+ }
177
+ function writeBlueBubblesOutboundRecord(agentRoot, record) {
178
+ writeJsonAtomic(blueBubblesOutboundRecordPath(agentRoot, record.recordId), record);
179
+ return record;
180
+ }
181
+ function identityMatches(left, right) {
182
+ return left.routeProof.routeHash === right.routeProof.routeHash
183
+ && left.attachmentProof.identityHash === right.attachmentProof.identityHash
184
+ && left.textHash === right.textHash
185
+ && left.replyToMessageGuidHash === right.replyToMessageGuidHash;
186
+ }
187
+ function emitDuplicate(record) {
188
+ (0, runtime_1.emitNervesEvent)({
189
+ level: "warn",
190
+ component: "senses",
191
+ event: "senses.bluebubbles_outbound_duplicate",
192
+ message: "duplicate bluebubbles outbound reservation suppressed",
193
+ meta: { recordId: record.recordId, status: record.status },
194
+ });
195
+ }
196
+ function markPendingManualVerification(agentRoot, record, decidedAt, reason) {
197
+ const updated = writeBlueBubblesOutboundRecord(agentRoot, {
198
+ ...record,
199
+ status: "pending-manual-verification",
200
+ updatedAt: decidedAt,
201
+ manualVerificationReason: reason,
202
+ });
203
+ (0, runtime_1.emitNervesEvent)({
204
+ level: "warn",
205
+ component: "senses",
206
+ event: "senses.bluebubbles_outbound_pending_manual_verification",
207
+ message: "bluebubbles outbound requires manual verification",
208
+ meta: { recordId: updated.recordId, reason },
209
+ });
210
+ return updated;
211
+ }
212
+ function reserveBlueBubblesOutbound(input) {
213
+ const record = buildRecord(input);
214
+ const filePath = blueBubblesOutboundRecordPath(input.agentRoot, record.recordId);
215
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
216
+ try {
217
+ const fd = fs.openSync(filePath, "wx");
218
+ try {
219
+ fs.writeFileSync(fd, `${JSON.stringify(record, null, 2)}\n`, "utf-8");
220
+ }
221
+ finally {
222
+ fs.closeSync(fd);
223
+ }
224
+ (0, runtime_1.emitNervesEvent)({
225
+ component: "senses",
226
+ event: "senses.bluebubbles_outbound_reserved",
227
+ message: "reserved bluebubbles outbound idempotency key",
228
+ meta: { recordId: record.recordId, textLength: record.textLength },
229
+ });
230
+ return { status: "reserved", record };
231
+ }
232
+ catch (error) {
233
+ /* v8 ignore next -- non-EEXIST filesystem failures are rethrown fail-closed; hard to trigger portably after mkdir succeeds @preserve */
234
+ if (!isNodeFileExistsError(error))
235
+ throw error;
236
+ const existing = readBlueBubblesOutboundRecord(input.agentRoot, record.recordId);
237
+ if (!existing)
238
+ throw new Error(`bluebubbles outbound reservation is unreadable: ${record.recordId}`);
239
+ if (identityMatches(existing, record)) {
240
+ emitDuplicate(existing);
241
+ return { status: "duplicate", record: existing };
242
+ }
243
+ const updated = markPendingManualVerification(input.agentRoot, existing, nowIso(input.now), "identity-proof-mismatch");
244
+ return { status: "pending-manual-verification", reason: "identity-proof-mismatch", record: updated };
245
+ }
246
+ }
247
+ function isNodeFileExistsError(error) {
248
+ return typeof error === "object" && error !== null && error.code === "EEXIST";
249
+ }
250
+ function updateStatus(agentRoot, recordId, status, updatedAt, patch) {
251
+ const existing = readBlueBubblesOutboundRecord(agentRoot, recordId);
252
+ if (!existing)
253
+ throw new Error(`bluebubbles outbound record not found: ${recordId}`);
254
+ const updated = writeBlueBubblesOutboundRecord(agentRoot, {
255
+ ...existing,
256
+ ...patch,
257
+ status,
258
+ updatedAt,
259
+ });
260
+ (0, runtime_1.emitNervesEvent)({
261
+ component: "senses",
262
+ event: "senses.bluebubbles_outbound_state_changed",
263
+ message: "bluebubbles outbound state changed",
264
+ meta: { recordId, status },
265
+ });
266
+ return updated;
267
+ }
268
+ function markBlueBubblesOutboundAccepted(input) {
269
+ return updateStatus(input.agentRoot, input.recordId, "accepted", input.acceptedAt, {
270
+ acceptedAt: input.acceptedAt,
271
+ ...(input.messageGuid?.trim() ? { messageGuid: input.messageGuid.trim() } : {}),
272
+ });
273
+ }
274
+ function markBlueBubblesOutboundEnqueued(input) {
275
+ return updateStatus(input.agentRoot, input.recordId, "enqueued", input.enqueuedAt, {
276
+ enqueuedAt: input.enqueuedAt,
277
+ messageGuid: input.messageGuid.trim(),
278
+ });
279
+ }
280
+ function markBlueBubblesOutboundLocalVisible(input) {
281
+ return updateStatus(input.agentRoot, input.recordId, "local-visible", input.visibleAt, {
282
+ localVisibleAt: input.visibleAt,
283
+ messageGuid: input.messageGuid.trim(),
284
+ ...(input.tempGuid?.trim() ? { tempGuid: input.tempGuid.trim() } : {}),
285
+ });
286
+ }
287
+ function markBlueBubblesOutboundDelivered(input) {
288
+ return updateStatus(input.agentRoot, input.recordId, "delivered", input.deliveredAt, {
289
+ deliveredAt: input.deliveredAt,
290
+ ...(input.messageGuid?.trim() ? { messageGuid: input.messageGuid.trim() } : {}),
291
+ });
292
+ }
293
+ function markBlueBubblesOutboundFailed(input) {
294
+ return updateStatus(input.agentRoot, input.recordId, "failed", input.failedAt, {
295
+ failedAt: input.failedAt,
296
+ failureReason: input.reason.slice(0, 500),
297
+ });
298
+ }
299
+ function markBlueBubblesOutboundPendingManualVerification(input) {
300
+ const existing = readBlueBubblesOutboundRecord(input.agentRoot, input.recordId);
301
+ if (!existing)
302
+ throw new Error(`bluebubbles outbound record not found: ${input.recordId}`);
303
+ return markPendingManualVerification(input.agentRoot, existing, input.decidedAt, input.reason);
304
+ }