@ouro.bot/cli 0.1.0-alpha.92 → 0.1.0-alpha.920

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 (716) 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/assets/sanctuary-host-launcher.sh +16 -0
  15. package/changelog.json +5819 -16
  16. package/deploy/unraid/Dockerfile +58 -0
  17. package/deploy/unraid/README.txt +2997 -0
  18. package/deploy/unraid/audit-container-spec.sh +4 -0
  19. package/deploy/unraid/container-runtime.json +1 -0
  20. package/deploy/unraid/docker-man-template-transaction.mjs +711 -0
  21. package/deploy/unraid/docker-man-template-xml.cjs +105 -0
  22. package/deploy/unraid/migrate-sanctuary-bundle.mjs +60 -0
  23. package/deploy/unraid/ouro-events/bootstrap-spool.sh +92 -0
  24. package/deploy/unraid/ouro-events/emit-event.mjs +310 -0
  25. package/deploy/unraid/ouro-events/emit-usenet-event.sh +54 -0
  26. package/deploy/unraid/ouro-events/install-usenet-guard.sh +435 -0
  27. package/deploy/unraid/ouro-events/usenet-health.sh +176 -0
  28. package/deploy/unraid/sanctuary-acceptance-adapter.sh +43 -0
  29. package/deploy/unraid/sanctuary-acceptance-contract.json +198 -0
  30. package/deploy/unraid/sanctuary-acceptance-harness.sh +23 -0
  31. package/deploy/unraid/sanctuary-authority-installation.json +36 -0
  32. package/deploy/unraid/sanctuary-authority-service.sh +30 -0
  33. package/deploy/unraid/sanctuary-butler-upgrade.mjs +1254 -0
  34. package/deploy/unraid/sanctuary-deployment-target.mjs +710 -0
  35. package/deploy/unraid/sanctuary-replay-gate.mjs +1564 -0
  36. package/deploy/unraid/sanctuary-safe-rename.py +24 -0
  37. package/deploy/unraid/sanctuary-unit16-host-broker.mjs +2086 -0
  38. package/deploy/unraid/sanctuary-unit16-run.sh +560 -0
  39. package/deploy/unraid/sanctuary-unit18-target-audit.sh +27 -0
  40. package/deploy/unraid/sanctuary.ouro/agent.json +66 -0
  41. package/deploy/unraid/sanctuary.ouro/arc/README.md +3 -0
  42. package/deploy/unraid/sanctuary.ouro/awaiting/house-care-sweep.md +17 -0
  43. package/deploy/unraid/sanctuary.ouro/bundle-meta.json +5 -0
  44. package/deploy/unraid/sanctuary.ouro/habits/sanctuary-health.md +8 -0
  45. package/deploy/unraid/sanctuary.ouro/mcp/media-mcp.mjs +2231 -0
  46. package/deploy/unraid/sanctuary.ouro/provider-readiness.json +11 -0
  47. package/deploy/unraid/sanctuary.ouro/psyche/ASPIRATIONS.md +5 -0
  48. package/deploy/unraid/sanctuary.ouro/psyche/IDENTITY.md +5 -0
  49. package/deploy/unraid/sanctuary.ouro/psyche/LORE.md +33 -0
  50. package/deploy/unraid/sanctuary.ouro/psyche/SOUL.md +27 -0
  51. package/deploy/unraid/sanctuary.ouro/psyche/TACIT.md +22 -0
  52. package/deploy/unraid/sanctuary.ouro/state/policy/steward.json +7 -0
  53. package/deploy/unraid/sanctuary.ouro/tool-profiles.json +232 -0
  54. package/deploy/unraid/sanctuary.xml +30 -0
  55. package/dist/a2a/card.js +57 -0
  56. package/dist/a2a/client.js +250 -0
  57. package/dist/a2a/config.js +50 -0
  58. package/dist/a2a/delegated-command-grants.js +93 -0
  59. package/dist/a2a/delegated-command.js +197 -0
  60. package/dist/a2a/delegation-stores.js +56 -0
  61. package/dist/a2a/did-resolution.js +93 -0
  62. package/dist/a2a/escalation-grants.js +84 -0
  63. package/dist/a2a/escalation-wiring.js +60 -0
  64. package/dist/a2a/identity.js +258 -0
  65. package/dist/a2a/inbound-share.js +110 -0
  66. package/dist/a2a/mission-result-wire.js +196 -0
  67. package/dist/a2a/onboarding.js +133 -0
  68. package/dist/a2a/operator-trust.js +382 -0
  69. package/dist/a2a/outbox-client.js +57 -0
  70. package/dist/a2a/outbox-store.js +188 -0
  71. package/dist/a2a/outbox-wire.js +95 -0
  72. package/dist/a2a/pin-store.js +132 -0
  73. package/dist/a2a/replay-grant-holders.js +33 -0
  74. package/dist/a2a/replay-harness.js +188 -0
  75. package/dist/a2a/resolution-proof.js +53 -0
  76. package/dist/a2a/sealed-chat.js +113 -0
  77. package/dist/a2a/seen-ledger.js +120 -0
  78. package/dist/a2a/server.js +771 -0
  79. package/dist/a2a/task-store.js +69 -0
  80. package/dist/a2a/transport.js +45 -0
  81. package/dist/a2a/trust-commands.js +203 -0
  82. package/dist/a2a/trusted-files.js +185 -0
  83. package/dist/a2a/types.js +3 -0
  84. package/dist/arc/attention-types.js +8 -0
  85. package/dist/arc/cares.js +360 -0
  86. package/dist/arc/episodes.js +118 -0
  87. package/dist/arc/evolution.js +490 -0
  88. package/dist/arc/flight-recorder.js +1034 -0
  89. package/dist/arc/intentions.js +134 -0
  90. package/dist/arc/json-store.js +117 -0
  91. package/dist/arc/obligations.js +386 -0
  92. package/dist/arc/packets.js +336 -0
  93. package/dist/arc/presence.js +185 -0
  94. package/dist/arc/task-lifecycle.js +57 -0
  95. package/dist/commerce/store.js +755 -0
  96. package/dist/commerce/types.js +3 -0
  97. package/dist/heart/active-work.js +798 -40
  98. package/dist/heart/agent-entry.js +252 -3
  99. package/dist/heart/approval-files.js +113 -0
  100. package/dist/heart/approval-store.js +960 -0
  101. package/dist/heart/attachments/image-normalize.js +194 -0
  102. package/dist/heart/attachments/materialize.js +97 -0
  103. package/dist/heart/attachments/originals.js +88 -0
  104. package/dist/heart/attachments/render.js +29 -0
  105. package/dist/heart/attachments/sources/bluebubbles.js +156 -0
  106. package/dist/heart/attachments/sources/cli-local-file.js +78 -0
  107. package/dist/heart/attachments/sources/index.js +18 -0
  108. package/dist/heart/attachments/sources/telegram.js +71 -0
  109. package/dist/heart/attachments/store.js +132 -0
  110. package/dist/heart/attachments/types.js +93 -0
  111. package/dist/heart/auth/auth-flow.js +495 -0
  112. package/dist/heart/autonomy-budget.js +478 -0
  113. package/dist/heart/awaiting/a2a-await-delivery.js +109 -0
  114. package/dist/heart/awaiting/await-alert.js +152 -0
  115. package/dist/heart/awaiting/await-expiry.js +143 -0
  116. package/dist/heart/awaiting/await-loader.js +91 -0
  117. package/dist/heart/awaiting/await-parser.js +152 -0
  118. package/dist/heart/awaiting/await-runtime-state.js +111 -0
  119. package/dist/heart/awaiting/await-scheduler.js +391 -0
  120. package/dist/heart/background-operations.js +281 -0
  121. package/dist/heart/bluebubbles-host-account.js +78 -0
  122. package/dist/heart/bridges/manager.js +137 -17
  123. package/dist/heart/bridges/store.js +14 -2
  124. package/dist/heart/bundle-state.js +168 -0
  125. package/dist/heart/commitments.js +44 -18
  126. package/dist/heart/config-registry.js +331 -0
  127. package/dist/heart/config.js +174 -131
  128. package/dist/heart/context-loss-gauntlet.js +387 -0
  129. package/dist/heart/context-loss-sentinel.js +1159 -0
  130. package/dist/heart/core.js +2364 -578
  131. package/dist/heart/cross-chat-delivery.js +9 -22
  132. package/dist/heart/daemon/agent-config-check.js +451 -0
  133. package/dist/heart/daemon/agent-discovery.js +283 -3
  134. package/dist/heart/daemon/agent-home.js +319 -0
  135. package/dist/heart/daemon/agent-service.js +542 -0
  136. package/dist/heart/daemon/agentic-repair.js +547 -0
  137. package/dist/heart/daemon/await-private-wake.js +59 -0
  138. package/dist/heart/daemon/bluebubbles-cli-integration.js +71 -0
  139. package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
  140. package/dist/heart/daemon/bluebubbles-host-protocol.js +433 -0
  141. package/dist/heart/daemon/bluebubbles-host.js +273 -0
  142. package/dist/heart/daemon/boot-sync-probe.js +197 -0
  143. package/dist/heart/daemon/cadence.js +180 -0
  144. package/dist/heart/daemon/cli-arg-redaction.js +26 -0
  145. package/dist/heart/daemon/cli-defaults.js +876 -0
  146. package/dist/heart/daemon/cli-desk.js +322 -0
  147. package/dist/heart/daemon/cli-exec.js +9199 -0
  148. package/dist/heart/daemon/cli-help.js +735 -0
  149. package/dist/heart/daemon/cli-parse.js +3211 -0
  150. package/dist/heart/daemon/cli-render-doctor.js +57 -0
  151. package/dist/heart/daemon/cli-render.js +819 -0
  152. package/dist/heart/daemon/cli-types.js +8 -0
  153. package/dist/heart/daemon/connect-bay.js +323 -0
  154. package/dist/heart/daemon/container-credential-bootstrap.js +273 -0
  155. package/dist/heart/daemon/container-healthcheck.js +112 -0
  156. package/dist/heart/daemon/container-runtime.js +97 -0
  157. package/dist/heart/daemon/container-spec-auditor-main.js +171 -0
  158. package/dist/heart/daemon/container-spec-auditor.js +235 -0
  159. package/dist/heart/daemon/daemon-bootstrap-startup.js +115 -0
  160. package/dist/heart/daemon/daemon-cli.js +29 -2350
  161. package/dist/heart/daemon/daemon-entry.js +953 -11
  162. package/dist/heart/daemon/daemon-health.js +186 -0
  163. package/dist/heart/daemon/daemon-rollup.js +57 -0
  164. package/dist/heart/daemon/daemon-runtime-sync.js +88 -13
  165. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  166. package/dist/heart/daemon/daemon.js +2084 -105
  167. package/dist/heart/daemon/dns-workflow.js +394 -0
  168. package/dist/heart/daemon/doctor-types.js +8 -0
  169. package/dist/heart/daemon/doctor.js +1766 -0
  170. package/dist/heart/daemon/freshness.js +345 -0
  171. package/dist/heart/daemon/habit-private-wake.js +61 -0
  172. package/dist/heart/daemon/health-monitor.js +154 -11
  173. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  174. package/dist/heart/daemon/hooks/bundle-meta.js +135 -1
  175. package/dist/heart/daemon/http-health-probe.js +80 -0
  176. package/dist/heart/daemon/human-command-screens.js +234 -0
  177. package/dist/heart/daemon/human-readiness.js +114 -0
  178. package/dist/heart/daemon/inner-status.js +78 -0
  179. package/dist/heart/daemon/interactive-repair.js +394 -0
  180. package/dist/heart/daemon/launchd.js +37 -8
  181. package/dist/heart/daemon/log-tailer.js +79 -10
  182. package/dist/heart/daemon/logs-prune.js +132 -0
  183. package/dist/heart/daemon/mcp-canary.js +524 -0
  184. package/dist/heart/daemon/message-router.js +65 -5
  185. package/dist/heart/daemon/migrate-to-desk.js +848 -0
  186. package/dist/heart/daemon/os-cron-deps.js +135 -0
  187. package/dist/heart/daemon/os-cron.js +22 -14
  188. package/dist/heart/daemon/ouro-bot-entry.js +6 -3
  189. package/dist/heart/daemon/ouro-entry.js +5 -2
  190. package/dist/heart/daemon/plugin-cli.js +432 -0
  191. package/dist/heart/daemon/process-manager.js +760 -61
  192. package/dist/heart/daemon/provider-discovery.js +137 -0
  193. package/dist/heart/daemon/provider-ping-progress.js +83 -0
  194. package/dist/heart/daemon/prunable-bundle.js +144 -0
  195. package/dist/heart/daemon/pulse.js +511 -0
  196. package/dist/heart/daemon/readiness-repair.js +365 -0
  197. package/dist/heart/daemon/run-hooks.js +2 -0
  198. package/dist/heart/daemon/runtime-logging.js +47 -14
  199. package/dist/heart/daemon/runtime-metadata.js +2 -30
  200. package/dist/heart/daemon/runtime-mode.js +13 -2
  201. package/dist/heart/daemon/safe-mode.js +161 -0
  202. package/dist/heart/daemon/sanctuary-acceptance-adapter.js +2654 -0
  203. package/dist/heart/daemon/sanctuary-acceptance-harness.js +1830 -0
  204. package/dist/heart/daemon/sanctuary-acceptance-marker.js +267 -0
  205. package/dist/heart/daemon/sanctuary-acceptance-scenarios.js +955 -0
  206. package/dist/heart/daemon/sanctuary-authority-codec.js +86 -0
  207. package/dist/heart/daemon/sanctuary-authority-epoch.js +281 -0
  208. package/dist/heart/daemon/sanctuary-authority-installation.js +161 -0
  209. package/dist/heart/daemon/sanctuary-authority-ledger.js +241 -0
  210. package/dist/heart/daemon/sanctuary-authority-root-lifecycle.js +1267 -0
  211. package/dist/heart/daemon/sanctuary-authority-vault-migration.js +79 -0
  212. package/dist/heart/daemon/sanctuary-bundle-migration.js +730 -0
  213. package/dist/heart/daemon/sanctuary-host-authority.js +1008 -0
  214. package/dist/heart/daemon/sanctuary-host-detached-supervisor.js +493 -0
  215. package/dist/heart/daemon/sanctuary-host-executor.js +670 -0
  216. package/dist/heart/daemon/sanctuary-host-linux-kernel.js +334 -0
  217. package/dist/heart/daemon/sanctuary-host-supervisor-entry.js +207 -0
  218. package/dist/heart/daemon/sanctuary-host-supervisor.js +95 -0
  219. package/dist/heart/daemon/sanctuary-package-management.js +103 -0
  220. package/dist/heart/daemon/sanctuary-scheduler-liveness.js +227 -0
  221. package/dist/heart/daemon/sanctuary-scheduler-origin.js +132 -0
  222. package/dist/heart/daemon/sanctuary-telegram-authority-entry.js +475 -0
  223. package/dist/heart/daemon/sanctuary-telegram-authority-gateway.js +585 -0
  224. package/dist/heart/daemon/sanctuary-telegram-authority-service.js +676 -0
  225. package/dist/heart/daemon/sense-manager.js +733 -48
  226. package/dist/heart/daemon/session-id-resolver.js +131 -0
  227. package/dist/heart/daemon/skill-management-installer.js +1 -1
  228. package/dist/heart/daemon/socket-client.js +180 -12
  229. package/dist/heart/daemon/stale-bundle-prune.js +113 -0
  230. package/dist/heart/daemon/startup-tui.js +339 -0
  231. package/dist/heart/daemon/supercronic-supervisor.js +282 -0
  232. package/dist/heart/daemon/task-scheduler.js +117 -39
  233. package/dist/heart/daemon/terminal-ui.js +503 -0
  234. package/dist/heart/daemon/thoughts.js +138 -54
  235. package/dist/heart/daemon/up-progress.js +366 -0
  236. package/dist/heart/daemon/vault-items.js +56 -0
  237. package/dist/heart/delegation.js +1 -4
  238. package/dist/heart/external-events/router.js +1516 -0
  239. package/dist/heart/failure-reports.js +336 -0
  240. package/dist/heart/file-lock.js +109 -0
  241. package/dist/heart/frontend-approval-runtime.js +506 -0
  242. package/dist/heart/frontend-journal.js +215 -0
  243. package/dist/heart/frontend-session-service.js +237 -0
  244. package/dist/heart/frontend-socket-client.js +164 -0
  245. package/dist/heart/frontend-socket.js +400 -0
  246. package/dist/heart/habits/habit-cancel.js +810 -0
  247. package/dist/heart/habits/habit-lifecycle.js +1327 -0
  248. package/dist/heart/habits/habit-migration.js +194 -0
  249. package/dist/heart/habits/habit-parser.js +278 -0
  250. package/dist/heart/habits/habit-runtime-state.js +93 -0
  251. package/dist/heart/habits/habit-scheduler.js +653 -0
  252. package/dist/heart/habits/habit-session-summary.js +318 -0
  253. package/dist/heart/habits/habit-session.js +636 -0
  254. package/dist/heart/{daemon → hatch}/hatch-flow.js +55 -62
  255. package/dist/heart/{daemon → hatch}/hatch-specialist.js +6 -8
  256. package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
  257. package/dist/heart/{daemon → hatch}/specialist-tools.js +53 -18
  258. package/dist/heart/identity.js +230 -59
  259. package/dist/heart/kept-notes.js +289 -0
  260. package/dist/heart/kicks.js +1 -1
  261. package/dist/heart/machine-identity.js +175 -0
  262. package/dist/heart/mail-import-discovery.js +356 -0
  263. package/dist/heart/mailbox/mailbox-http-hooks.js +98 -0
  264. package/dist/heart/mailbox/mailbox-http-response.js +7 -0
  265. package/dist/heart/mailbox/mailbox-http-routes.js +398 -0
  266. package/dist/heart/mailbox/mailbox-http-static.js +103 -0
  267. package/dist/heart/mailbox/mailbox-http-transport.js +272 -0
  268. package/dist/heart/mailbox/mailbox-http.js +99 -0
  269. package/dist/heart/mailbox/mailbox-read.js +39 -0
  270. package/dist/heart/mailbox/mailbox-types.js +27 -0
  271. package/dist/heart/mailbox/mailbox-view.js +197 -0
  272. package/dist/heart/mailbox/readers/agent-machine.js +428 -0
  273. package/dist/heart/mailbox/readers/continuity-readers.js +329 -0
  274. package/dist/heart/mailbox/readers/mail.js +375 -0
  275. package/dist/heart/mailbox/readers/runtime-readers.js +823 -0
  276. package/dist/heart/mailbox/readers/sessions.js +231 -0
  277. package/dist/heart/mailbox/readers/shared.js +111 -0
  278. package/dist/heart/mcp/mcp-server.js +696 -0
  279. package/dist/heart/migrate-config.js +100 -0
  280. package/dist/heart/model-capabilities.js +83 -16
  281. package/dist/heart/orientation-frame.js +345 -0
  282. package/dist/heart/platform.js +81 -0
  283. package/dist/heart/primitives.js +1 -1
  284. package/dist/heart/private-runtime/decision-renderer.js +158 -0
  285. package/dist/heart/private-runtime/index.js +18 -0
  286. package/dist/heart/private-runtime/ledger.js +221 -0
  287. package/dist/heart/private-runtime/policy.js +260 -0
  288. package/dist/heart/private-runtime/types.js +2 -0
  289. package/dist/heart/progress-story.js +1 -1
  290. package/dist/heart/provider-attempt.js +137 -0
  291. package/dist/heart/provider-binding-resolver.js +273 -0
  292. package/dist/heart/provider-credentials.js +449 -0
  293. package/dist/heart/provider-failover.js +311 -0
  294. package/dist/heart/provider-models.js +96 -0
  295. package/dist/heart/provider-ping.js +265 -0
  296. package/dist/heart/provider-readiness-cache.js +119 -0
  297. package/dist/heart/provider-visibility.js +188 -0
  298. package/dist/heart/providers/anthropic-token.js +131 -0
  299. package/dist/heart/providers/anthropic.js +173 -60
  300. package/dist/heart/providers/azure.js +23 -11
  301. package/dist/heart/providers/error-classification.js +127 -0
  302. package/dist/heart/providers/github-copilot.js +38 -39
  303. package/dist/heart/providers/minimax-vlm.js +189 -0
  304. package/dist/heart/providers/minimax.js +26 -8
  305. package/dist/heart/providers/openai-codex-token.js +349 -0
  306. package/dist/heart/providers/openai-codex.js +55 -40
  307. package/dist/heart/providers/openai-compatible.js +177 -0
  308. package/dist/heart/reply-style.js +52 -0
  309. package/dist/heart/run-ledger.js +247 -0
  310. package/dist/heart/runtime-capability-check.js +170 -0
  311. package/dist/heart/runtime-credentials.js +607 -0
  312. package/dist/heart/runtime-cwd.js +87 -0
  313. package/dist/heart/sense-truth.js +24 -4
  314. package/dist/heart/session-activity.js +92 -24
  315. package/dist/heart/session-events.js +1504 -0
  316. package/dist/heart/session-playback-cli-main.js +5 -0
  317. package/dist/heart/session-playback-cli.js +36 -0
  318. package/dist/heart/session-playback.js +231 -0
  319. package/dist/heart/session-redaction-repair-cli-main.js +31 -0
  320. package/dist/heart/session-redaction-repair.js +552 -0
  321. package/dist/heart/session-stats-cli-main.js +5 -0
  322. package/dist/heart/session-stats.js +183 -0
  323. package/dist/heart/session-transcript.js +133 -0
  324. package/dist/heart/shelf-claims.js +78 -0
  325. package/dist/heart/shepherd-config.js +113 -0
  326. package/dist/heart/source-grounding.js +297 -0
  327. package/dist/heart/start-of-turn-packet.js +372 -0
  328. package/dist/heart/steward-policy.js +656 -0
  329. package/dist/heart/streaming.js +609 -207
  330. package/dist/heart/structured-output.js +196 -0
  331. package/dist/heart/sync-classification.js +176 -0
  332. package/dist/heart/sync-health.js +152 -0
  333. package/dist/heart/sync.js +726 -0
  334. package/dist/heart/target-resolution.js +11 -7
  335. package/dist/heart/tempo.js +93 -0
  336. package/dist/heart/temporal-view.js +41 -0
  337. package/dist/heart/timeouts.js +101 -0
  338. package/dist/heart/tool-activity-callbacks.js +59 -0
  339. package/dist/heart/tool-approval.js +382 -0
  340. package/dist/heart/tool-description.js +155 -0
  341. package/dist/heart/tool-friction.js +55 -0
  342. package/dist/heart/tool-loop.js +206 -0
  343. package/dist/heart/turn-context.js +496 -0
  344. package/dist/heart/turn-execution-lease.js +30 -0
  345. package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +6 -5
  346. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +3 -2
  347. package/dist/heart/versioning/ouro-path-installer.js +432 -0
  348. package/dist/heart/versioning/ouro-recovery-launcher.js +76 -0
  349. package/dist/heart/versioning/ouro-version-manager.js +432 -0
  350. package/dist/heart/versioning/release-sources.js +173 -0
  351. package/dist/heart/versioning/self-update-defaults.js +294 -0
  352. package/dist/heart/versioning/self-update.js +196 -0
  353. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  354. package/dist/heart/{daemon → versioning}/update-checker.js +6 -1
  355. package/dist/heart/versioning/update-hooks.js +154 -0
  356. package/dist/heart/versioning/update-state.js +126 -0
  357. package/dist/heart/versioning/updater-launchd.js +150 -0
  358. package/dist/heart/versioning/version-intent.js +114 -0
  359. package/dist/heart/work-card.js +380 -0
  360. package/dist/mailbox-ui/assets/index-Cyv4mw3Y.js +1 -0
  361. package/dist/mailbox-ui/assets/index-Du_9G9WO.css +1 -0
  362. package/dist/mailbox-ui/assets/vendor-CcN1XpQ9.js +61 -0
  363. package/dist/mailbox-ui/index.html +16 -0
  364. package/dist/mailroom/attention.js +167 -0
  365. package/dist/mailroom/autonomy.js +209 -0
  366. package/dist/mailroom/blob-store.js +889 -0
  367. package/dist/mailroom/body-cache.js +61 -0
  368. package/dist/mailroom/cache-sync-cli.js +58 -0
  369. package/dist/mailroom/core.js +803 -0
  370. package/dist/mailroom/entry.js +160 -0
  371. package/dist/mailroom/file-store.js +568 -0
  372. package/dist/mailroom/hosted-cache-sync.js +384 -0
  373. package/dist/mailroom/mbox-import.js +393 -0
  374. package/dist/mailroom/migration.js +164 -0
  375. package/dist/mailroom/outbound.js +380 -0
  376. package/dist/mailroom/policy.js +263 -0
  377. package/dist/mailroom/reader.js +292 -0
  378. package/dist/mailroom/search-cache.js +513 -0
  379. package/dist/mailroom/search-relevance.js +319 -0
  380. package/dist/mailroom/smtp-ingress.js +176 -0
  381. package/dist/mailroom/source-state.js +176 -0
  382. package/dist/mailroom/thread.js +109 -0
  383. package/dist/mailroom/travel-extract.js +89 -0
  384. package/dist/mind/bundle-manifest.js +24 -2
  385. package/dist/mind/context.js +307 -139
  386. package/dist/mind/desk-section.js +362 -0
  387. package/dist/mind/diary-integrity.js +60 -0
  388. package/dist/mind/{memory.js → diary.js} +68 -77
  389. package/dist/mind/embedding-provider.js +60 -0
  390. package/dist/mind/file-state.js +179 -0
  391. package/dist/mind/friend-link-codes.js +209 -0
  392. package/dist/mind/friend-merge.js +330 -0
  393. package/dist/mind/{associative-recall.js → note-search.js} +47 -58
  394. package/dist/mind/obligation-steering.js +199 -3
  395. package/dist/mind/pending.js +36 -10
  396. package/dist/mind/prompt-budget.js +479 -0
  397. package/dist/mind/prompt-refresh.js +3 -2
  398. package/dist/mind/prompt.js +1085 -198
  399. package/dist/mind/provenance-trust.js +26 -0
  400. package/dist/mind/record-paths.js +312 -0
  401. package/dist/mind/scrutiny.js +173 -0
  402. package/dist/mind/session-transaction.js +564 -0
  403. package/dist/nerves/cli-logging.js +7 -1
  404. package/dist/nerves/coverage/audit-rules.js +15 -6
  405. package/dist/nerves/coverage/audit.js +28 -2
  406. package/dist/nerves/coverage/cli.js +1 -1
  407. package/dist/nerves/coverage/contract.js +5 -5
  408. package/dist/nerves/coverage/file-completeness.js +139 -5
  409. package/dist/nerves/coverage/run-artifacts.js +55 -35
  410. package/dist/nerves/coverage/source-scanner.js +4 -4
  411. package/dist/nerves/event-buffer.js +111 -0
  412. package/dist/nerves/index.js +293 -9
  413. package/dist/nerves/observation.js +20 -0
  414. package/dist/nerves/redact.js +79 -0
  415. package/dist/nerves/review/cli-main.js +5 -0
  416. package/dist/nerves/review/cli.js +156 -0
  417. package/dist/nerves/review/core.js +159 -0
  418. package/dist/nerves/runtime.js +17 -1
  419. package/dist/repertoire/ado-client.js +15 -56
  420. package/dist/repertoire/ado-semantic.js +16 -10
  421. package/dist/repertoire/api-client.js +97 -0
  422. package/dist/repertoire/bitwarden-store.js +1201 -0
  423. package/dist/repertoire/bundle-templates.js +71 -0
  424. package/dist/repertoire/bw-installer.js +231 -0
  425. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  426. package/dist/repertoire/coding/context-pack.js +331 -0
  427. package/dist/repertoire/coding/feedback.js +174 -32
  428. package/dist/repertoire/coding/index.js +15 -3
  429. package/dist/repertoire/coding/manager.js +159 -10
  430. package/dist/repertoire/coding/spawner.js +40 -11
  431. package/dist/repertoire/coding/tools.js +182 -18
  432. package/dist/repertoire/coding/workbench-client.js +272 -0
  433. package/dist/repertoire/coding/workbench-manager.js +346 -0
  434. package/dist/repertoire/commerce-errors.js +109 -0
  435. package/dist/repertoire/commerce-self-test.js +156 -0
  436. package/dist/repertoire/credential-access.js +183 -0
  437. package/dist/repertoire/desk/classifier.js +362 -0
  438. package/dist/repertoire/duffel-client.js +185 -0
  439. package/dist/repertoire/github-client.js +14 -55
  440. package/dist/repertoire/graph-client.js +11 -52
  441. package/dist/repertoire/guardrails.js +968 -20
  442. package/dist/repertoire/house-sweep.js +469 -0
  443. package/dist/repertoire/mcp-client.js +58 -5
  444. package/dist/repertoire/mcp-manager.js +427 -139
  445. package/dist/repertoire/mcp-tools.js +132 -0
  446. package/dist/repertoire/mcp-write-guard.js +46 -0
  447. package/dist/repertoire/plugin-mcp.js +175 -0
  448. package/dist/repertoire/plugins.js +253 -0
  449. package/dist/repertoire/relationship-authorization.js +322 -0
  450. package/dist/repertoire/sanctuary-container-policy.js +46 -0
  451. package/dist/repertoire/shell-sessions.js +134 -0
  452. package/dist/repertoire/skills.js +48 -4
  453. package/dist/repertoire/stripe-client.js +131 -0
  454. package/dist/repertoire/tool-arguments.js +182 -0
  455. package/dist/repertoire/tool-results.js +29 -0
  456. package/dist/repertoire/tools-a2a.js +699 -0
  457. package/dist/repertoire/tools-attachments.js +322 -0
  458. package/dist/repertoire/tools-awaiting.js +872 -0
  459. package/dist/repertoire/tools-base.js +83 -1168
  460. package/dist/repertoire/tools-bluebubbles.js +2 -0
  461. package/dist/repertoire/tools-bridge.js +144 -0
  462. package/dist/repertoire/tools-bundle.js +995 -0
  463. package/dist/repertoire/tools-commerce.js +253 -0
  464. package/dist/repertoire/tools-config.js +186 -0
  465. package/dist/repertoire/tools-continuity.js +575 -0
  466. package/dist/repertoire/tools-credential.js +383 -0
  467. package/dist/repertoire/tools-escalation.js +84 -0
  468. package/dist/repertoire/tools-evolution.js +527 -0
  469. package/dist/repertoire/tools-files.js +344 -0
  470. package/dist/repertoire/tools-flight.js +290 -0
  471. package/dist/repertoire/tools-flow.js +119 -0
  472. package/dist/repertoire/tools-friend-link.js +57 -0
  473. package/dist/repertoire/tools-github.js +3 -8
  474. package/dist/repertoire/tools-habits.js +103 -0
  475. package/dist/repertoire/tools-house-care.js +174 -0
  476. package/dist/repertoire/tools-mail.js +1828 -0
  477. package/dist/repertoire/tools-notes.js +496 -0
  478. package/dist/repertoire/tools-obligations.js +182 -0
  479. package/dist/repertoire/tools-orientation.js +31 -0
  480. package/dist/repertoire/tools-record.js +482 -0
  481. package/dist/repertoire/tools-rsvp.js +139 -0
  482. package/dist/repertoire/tools-runtime.js +238 -0
  483. package/dist/repertoire/tools-sanctuary-host.js +202 -0
  484. package/dist/repertoire/tools-session.js +1133 -0
  485. package/dist/repertoire/tools-shell.js +149 -0
  486. package/dist/repertoire/tools-shepherd.js +262 -0
  487. package/dist/repertoire/tools-steward-policy.js +147 -0
  488. package/dist/repertoire/tools-stripe.js +224 -0
  489. package/dist/repertoire/tools-surface.js +369 -0
  490. package/dist/repertoire/tools-teams.js +12 -39
  491. package/dist/repertoire/tools-telegram-contacts.js +37 -0
  492. package/dist/repertoire/tools-travel.js +125 -0
  493. package/dist/repertoire/tools-trip.js +982 -0
  494. package/dist/repertoire/tools-unraid.js +556 -0
  495. package/dist/repertoire/tools-user-profile.js +146 -0
  496. package/dist/repertoire/tools-vault.js +40 -0
  497. package/dist/repertoire/tools-voice-local.js +198 -0
  498. package/dist/repertoire/tools-voice.js +145 -0
  499. package/dist/repertoire/tools.js +514 -156
  500. package/dist/repertoire/travel-api-client.js +360 -0
  501. package/dist/repertoire/unraid-client.js +155 -0
  502. package/dist/repertoire/unraid-restart.js +339 -0
  503. package/dist/repertoire/user-profile.js +131 -0
  504. package/dist/repertoire/vault-setup.js +246 -0
  505. package/dist/repertoire/vault-unlock.js +686 -0
  506. package/dist/rsvp/aisleplanner-client.js +354 -0
  507. package/dist/rsvp/cli.js +936 -0
  508. package/dist/rsvp/config.js +327 -0
  509. package/dist/rsvp/cutover.js +433 -0
  510. package/dist/rsvp/diagnostics.js +232 -0
  511. package/dist/rsvp/diff-renderer.js +176 -0
  512. package/dist/rsvp/habit-policy.js +110 -0
  513. package/dist/rsvp/habit-stage.js +123 -0
  514. package/dist/rsvp/incident-bundle.js +97 -0
  515. package/dist/rsvp/migration.js +204 -0
  516. package/dist/rsvp/native-habit-runner.js +630 -0
  517. package/dist/rsvp/outbound-state.js +628 -0
  518. package/dist/rsvp/query.js +118 -0
  519. package/dist/rsvp/replay.js +233 -0
  520. package/dist/rsvp/snapshot.js +232 -0
  521. package/dist/rsvp/spend-ledger.js +175 -0
  522. package/dist/scripts/claude-code-hook.js +41 -0
  523. package/dist/scripts/claude-code-stop-hook.js +47 -0
  524. package/dist/senses/a2a-entry.js +132 -0
  525. package/dist/senses/acp-server.js +386 -0
  526. package/dist/senses/attention-queue.js +186 -0
  527. package/dist/senses/await-turn-message.js +65 -0
  528. package/dist/senses/bluebubbles/active-turns.js +216 -0
  529. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  530. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  531. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +379 -41
  532. package/dist/senses/bluebubbles/context-packet.js +207 -0
  533. package/dist/senses/bluebubbles/context-smoke.js +144 -0
  534. package/dist/senses/bluebubbles/entry.js +90 -0
  535. package/dist/senses/bluebubbles/host-account-verifier.js +88 -0
  536. package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +21 -3
  537. package/dist/senses/bluebubbles/index.js +3878 -0
  538. package/dist/senses/bluebubbles/latest-turn.js +311 -0
  539. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -71
  540. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +159 -28
  541. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +24 -13
  542. package/dist/senses/bluebubbles/outbound-state.js +308 -0
  543. package/dist/senses/bluebubbles/processed-log.js +133 -0
  544. package/dist/senses/bluebubbles/reaction-policy.js +47 -0
  545. package/dist/senses/bluebubbles/replay.js +284 -0
  546. package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +30 -2
  547. package/dist/senses/bluebubbles/semantic-receipts.js +1986 -0
  548. package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
  549. package/dist/senses/bluebubbles/webhook-registration.js +257 -0
  550. package/dist/senses/bluebubbles-meta-guard.js +39 -0
  551. package/dist/senses/cli/bracketed-paste.js +82 -0
  552. package/dist/senses/cli/image-paste.js +287 -0
  553. package/dist/senses/cli/image-ref-navigation.js +75 -0
  554. package/dist/senses/cli/ink-app.js +156 -0
  555. package/dist/senses/cli/inline-diff.js +64 -0
  556. package/dist/senses/cli/input-keys.js +174 -0
  557. package/dist/senses/cli/kill-ring.js +86 -0
  558. package/dist/senses/cli/message-list.js +51 -0
  559. package/dist/senses/cli/ouro-tui.js +607 -0
  560. package/dist/senses/cli/spinner-imperative.js +135 -0
  561. package/dist/senses/cli/spinner.js +101 -0
  562. package/dist/senses/cli/status-line.js +60 -0
  563. package/dist/senses/cli/streaming-markdown.js +526 -0
  564. package/dist/senses/cli/tool-display.js +85 -0
  565. package/dist/senses/cli/tool-render.js +85 -0
  566. package/dist/senses/cli/tui-store.js +241 -0
  567. package/dist/senses/cli/virtual-list.js +35 -0
  568. package/dist/senses/cli-entry.js +60 -8
  569. package/dist/senses/cli.js +601 -304
  570. package/dist/senses/commands.js +66 -3
  571. package/dist/senses/context-packet-ledger.js +371 -0
  572. package/dist/senses/context-packets.js +154 -0
  573. package/dist/senses/habit-lifecycle-guard.js +24 -0
  574. package/dist/senses/habit-turn-message.js +160 -0
  575. package/dist/senses/ingress-evidence.js +2 -0
  576. package/dist/senses/mail-entry.js +66 -0
  577. package/dist/senses/mail.js +524 -0
  578. package/dist/senses/pipeline.js +1158 -204
  579. package/dist/senses/private-runtime-worker.js +859 -0
  580. package/dist/senses/private-runtime.js +1627 -0
  581. package/dist/senses/proactive-content-guard.js +51 -0
  582. package/dist/senses/root-host-approval-port.js +345 -0
  583. package/dist/senses/root-host-approval-runtime.js +393 -0
  584. package/dist/senses/sanctuary-authority-resident.js +102 -0
  585. package/dist/senses/sanctuary-download-credit-presentation.js +31 -0
  586. package/dist/senses/sanctuary-full-visibility-contract.js +302 -0
  587. package/dist/senses/sanctuary-grounding.js +118 -0
  588. package/dist/senses/sanctuary-health-acceptance-probe-entry.js +12 -0
  589. package/dist/senses/sanctuary-health-acceptance-probe.js +774 -0
  590. package/dist/senses/sanctuary-health-runner.js +104 -0
  591. package/dist/senses/sanctuary-health.js +422 -0
  592. package/dist/senses/sanctuary-install-state-contract.js +169 -0
  593. package/dist/senses/sanctuary-interactive-control.js +410 -0
  594. package/dist/senses/sanctuary-media-catalog-contract.js +259 -0
  595. package/dist/senses/sanctuary-media-optimization.js +514 -0
  596. package/dist/senses/sanctuary-runtime.js +369 -0
  597. package/dist/senses/sanctuary-sab.js +85 -0
  598. package/dist/senses/sanctuary-storage-optimization-contract.js +32 -0
  599. package/dist/senses/shared-turn.js +793 -0
  600. package/dist/senses/shepherd/client.js +196 -0
  601. package/dist/senses/shepherd/cmux.js +344 -0
  602. package/dist/senses/shepherd/herdr.js +315 -0
  603. package/dist/senses/shepherd/host.js +14 -0
  604. package/dist/senses/shepherd/judge.js +137 -0
  605. package/dist/senses/shepherd/owner-notice.js +245 -0
  606. package/dist/senses/shepherd/redact.js +66 -0
  607. package/dist/senses/shepherd/returns.js +79 -0
  608. package/dist/senses/shepherd/sense.js +309 -0
  609. package/dist/senses/shepherd-entry.js +78 -0
  610. package/dist/senses/surface-tool.js +109 -0
  611. package/dist/senses/teams-entry.js +68 -8
  612. package/dist/senses/teams.js +598 -233
  613. package/dist/senses/telegram-admission.js +718 -0
  614. package/dist/senses/telegram-approval-runtime.js +793 -0
  615. package/dist/senses/telegram-attachments.js +186 -0
  616. package/dist/senses/telegram-audit-ledger.js +220 -0
  617. package/dist/senses/telegram-authority-transport.js +231 -0
  618. package/dist/senses/telegram-client.js +1749 -0
  619. package/dist/senses/telegram-effect-adapter.js +900 -0
  620. package/dist/senses/telegram-entry.js +83 -0
  621. package/dist/senses/telegram-handoff.js +80 -0
  622. package/dist/senses/telegram.js +2405 -0
  623. package/dist/senses/trust-gate.js +104 -9
  624. package/dist/senses/voice/audio-playback.js +237 -0
  625. package/dist/senses/voice/audio-routing.js +119 -0
  626. package/dist/senses/voice/call-auth.js +250 -0
  627. package/dist/senses/voice/elevenlabs.js +202 -0
  628. package/dist/senses/voice/floor-control.js +431 -0
  629. package/dist/senses/voice/floor-controller.js +115 -0
  630. package/dist/senses/voice/golden-path.js +116 -0
  631. package/dist/senses/voice/index.js +29 -0
  632. package/dist/senses/voice/local-audio-control.js +344 -0
  633. package/dist/senses/voice/local-audio-devices.js +516 -0
  634. package/dist/senses/voice/local-audio-disclosure.js +174 -0
  635. package/dist/senses/voice/local-audio-identity.js +103 -0
  636. package/dist/senses/voice/local-audio-join.js +325 -0
  637. package/dist/senses/voice/local-audio-mute.js +199 -0
  638. package/dist/senses/voice/local-audio-notify.js +121 -0
  639. package/dist/senses/voice/local-audio-prompts.js +116 -0
  640. package/dist/senses/voice/local-audio-transport.js +427 -0
  641. package/dist/senses/voice/local-audio.js +281 -0
  642. package/dist/senses/voice/local-media-stream.js +145 -0
  643. package/dist/senses/voice/meeting.js +113 -0
  644. package/dist/senses/voice/outbound.js +190 -0
  645. package/dist/senses/voice/phone.js +33 -0
  646. package/dist/senses/voice/playback.js +139 -0
  647. package/dist/senses/voice/process-guards.js +51 -0
  648. package/dist/senses/voice/realtime-eval.js +496 -0
  649. package/dist/senses/voice/realtime-tool-schema.js +195 -0
  650. package/dist/senses/voice/realtime-trace.js +531 -0
  651. package/dist/senses/voice/transcript.js +70 -0
  652. package/dist/senses/voice/turn.js +191 -0
  653. package/dist/senses/voice/twilio-phone-runtime.js +836 -0
  654. package/dist/senses/voice/twilio-phone.js +5454 -0
  655. package/dist/senses/voice/types.js +2 -0
  656. package/dist/senses/voice/whisper.js +161 -0
  657. package/dist/senses/voice-entry.js +83 -0
  658. package/dist/senses/voice-realtime-eval-command.js +99 -0
  659. package/dist/senses/voice-realtime-eval-entry.js +21 -0
  660. package/dist/senses/voice-twilio-entry.js +87 -0
  661. package/dist/trips/core.js +138 -0
  662. package/dist/trips/store.js +265 -0
  663. package/dist/util/frontmatter.js +69 -0
  664. package/npm-shrinkwrap.json +8100 -0
  665. package/package.json +74 -13
  666. package/skills/agent-commerce.md +113 -0
  667. package/skills/browser-navigation.md +117 -0
  668. package/skills/commerce-setup-guide.md +116 -0
  669. package/skills/commerce-setup.md +84 -0
  670. package/skills/configure-dev-tools.md +99 -0
  671. package/skills/travel-planning.md +138 -0
  672. package/dist/heart/daemon/auth-flow.js +0 -430
  673. package/dist/heart/daemon/ouro-path-installer.js +0 -260
  674. package/dist/heart/daemon/ouro-version-manager.js +0 -171
  675. package/dist/heart/daemon/update-hooks.js +0 -138
  676. package/dist/heart/obligations.js +0 -191
  677. package/dist/heart/safe-workspace.js +0 -362
  678. package/dist/heart/session-recall.js +0 -116
  679. package/dist/mind/friends/channel.js +0 -84
  680. package/dist/mind/friends/group-context.js +0 -144
  681. package/dist/mind/friends/resolver.js +0 -84
  682. package/dist/mind/friends/store-file.js +0 -190
  683. package/dist/mind/friends/store.js +0 -4
  684. package/dist/mind/friends/tokens.js +0 -26
  685. package/dist/mind/friends/trust-explanation.js +0 -74
  686. package/dist/mind/friends/types.js +0 -29
  687. package/dist/repertoire/tasks/board.js +0 -134
  688. package/dist/repertoire/tasks/index.js +0 -224
  689. package/dist/repertoire/tasks/lifecycle.js +0 -80
  690. package/dist/repertoire/tasks/middleware.js +0 -65
  691. package/dist/repertoire/tasks/parser.js +0 -173
  692. package/dist/repertoire/tasks/scanner.js +0 -132
  693. package/dist/repertoire/tasks/transitions.js +0 -144
  694. package/dist/senses/bluebubbles-entry.js +0 -13
  695. package/dist/senses/bluebubbles.js +0 -1181
  696. package/dist/senses/debug-activity.js +0 -154
  697. package/dist/senses/inner-dialog-worker.js +0 -90
  698. package/dist/senses/inner-dialog.js +0 -525
  699. package/subagents/README.md +0 -7
  700. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  701. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  702. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  703. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  704. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  705. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  706. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  707. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  708. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  709. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  710. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  711. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  712. /package/dist/{repertoire/tasks/types.js → heart/attachments/sources/adapter.js} +0 -0
  713. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  714. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  715. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  716. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -0,0 +1,209 @@
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.DEFAULT_LINK_CODE_TTL_MS = void 0;
37
+ exports.parseLinkCode = parseLinkCode;
38
+ exports.formatLinkCode = formatLinkCode;
39
+ exports.issueLinkCode = issueLinkCode;
40
+ exports.redeemLinkCode = redeemLinkCode;
41
+ // Identity link codes: how a person proves that a new channel identity is theirs.
42
+ //
43
+ // A friend on a surface the agent already trusts (the owner's CLI, or a family chat)
44
+ // asks for a short code. When that exact code arrives as a 1:1 message from an
45
+ // identity the agent does not yet trust, that identity's record merges into the
46
+ // friend who asked, and the new identity is treated as them from then on.
47
+ //
48
+ // Codes are single use and expire. Only a SHA-256 digest of each code is stored, under
49
+ // the agent's `state/` directory, which bundle sync does not carry. A code is consumed
50
+ // by its first presentation, whatever the outcome, by an atomic rename, so a replay or a
51
+ // race between two senders finds nothing to redeem.
52
+ const fs = __importStar(require("node:fs"));
53
+ const path = __importStar(require("node:path"));
54
+ const node_crypto_1 = require("node:crypto");
55
+ const runtime_1 = require("../nerves/runtime");
56
+ const friend_merge_1 = require("./friend-merge");
57
+ const CODE_ALPHABET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
58
+ const CODE_BODY_LENGTH = 8;
59
+ exports.DEFAULT_LINK_CODE_TTL_MS = 10 * 60 * 1000;
60
+ const MAX_LINK_CODE_TTL_MS = 24 * 60 * 60 * 1000;
61
+ const CODE_SHAPE = new RegExp(`^OURO-?([${CODE_ALPHABET}]{4})-?([${CODE_ALPHABET}]{4})$`);
62
+ function codesRoot(agentRoot) {
63
+ return path.join(agentRoot, "state", "friend-link-codes");
64
+ }
65
+ function digest(codeBody) {
66
+ return (0, node_crypto_1.createHash)("sha256").update(codeBody).digest("hex");
67
+ }
68
+ /**
69
+ * The code body when `text` is exactly one link code (case, spacing, dashes and simple
70
+ * markup ignored), otherwise null. Anything else in the message means it is not a code.
71
+ */
72
+ function parseLinkCode(text) {
73
+ const plain = text
74
+ .replace(/<at>[^<]*<\/at>/gi, " ")
75
+ .replace(/<[^>]*>/g, " ")
76
+ .replace(/&nbsp;/g, " ")
77
+ .replace(/\s+/g, "")
78
+ .toUpperCase();
79
+ const match = CODE_SHAPE.exec(plain);
80
+ return match ? `${match[1]}${match[2]}` : null;
81
+ }
82
+ function formatLinkCode(codeBody) {
83
+ return `OURO-${codeBody.slice(0, 4)}-${codeBody.slice(4)}`;
84
+ }
85
+ function issueLinkCode(input) {
86
+ const now = (input.now ?? (() => new Date()))();
87
+ const ttlMs = Math.min(Math.max(input.ttlMs ?? exports.DEFAULT_LINK_CODE_TTL_MS, 60 * 1000), MAX_LINK_CODE_TTL_MS);
88
+ let codeBody = "";
89
+ for (let index = 0; index < CODE_BODY_LENGTH; index += 1)
90
+ codeBody += CODE_ALPHABET[(0, node_crypto_1.randomInt)(CODE_ALPHABET.length)];
91
+ const record = {
92
+ schemaVersion: 1,
93
+ friendId: input.friend.id,
94
+ friendName: input.friend.name,
95
+ ...(input.channel ? { channel: input.channel } : {}),
96
+ issuedAt: now.toISOString(),
97
+ expiresAt: new Date(now.getTime() + ttlMs).toISOString(),
98
+ issuedBy: input.issuedBy,
99
+ };
100
+ const pendingDir = path.join(codesRoot(input.agentRoot), "pending");
101
+ fs.mkdirSync(pendingDir, { recursive: true, mode: 0o700 });
102
+ fs.writeFileSync(path.join(pendingDir, `${digest(codeBody)}.json`), `${JSON.stringify(record, null, 2)}\n`, { mode: 0o600, flag: "wx" });
103
+ (0, runtime_1.emitNervesEvent)({
104
+ component: "friends",
105
+ event: "friends.link_code_issued",
106
+ message: "issued an identity link code",
107
+ meta: { friendId: input.friend.id, channel: input.channel ?? "any", expiresAt: record.expiresAt, issuedBy: input.issuedBy },
108
+ });
109
+ return { code: formatLinkCode(codeBody), friendId: record.friendId, friendName: record.friendName, ...(record.channel ? { channel: record.channel } : {}), expiresAt: record.expiresAt };
110
+ }
111
+ const REJECTION_REPLIES = {
112
+ unknown_or_used: "That link code isn't valid. Ask for a new one.",
113
+ expired: "That link code has expired. Ask for a new one.",
114
+ group_chat: "Link codes only work in a 1:1 chat with me. Ask for a new code and send it to me directly.",
115
+ wrong_channel: "That link code was issued for a different channel. Ask for a new one.",
116
+ already_linked: "This account is already linked to you.",
117
+ claimant_trusted: "This account is already known to me as someone I trust, so a code can't relink it. The owner can merge the records instead.",
118
+ friend_missing: "The person this code was issued for no longer exists. Ask for a new code.",
119
+ };
120
+ function reject(reason, meta) {
121
+ (0, runtime_1.emitNervesEvent)({
122
+ level: "warn",
123
+ component: "friends",
124
+ event: "friends.link_code_rejected",
125
+ message: "rejected an identity link code",
126
+ meta: { reason, ...meta },
127
+ });
128
+ return { status: "rejected", reason, reply: REJECTION_REPLIES[reason] };
129
+ }
130
+ /** Atomically take the code out of `pending/`. Null when no pending code has this body. */
131
+ function consumeCode(agentRoot, codeBody, nowIso) {
132
+ const root = codesRoot(agentRoot);
133
+ const name = `${digest(codeBody)}.json`;
134
+ const usedDir = path.join(root, "used");
135
+ fs.mkdirSync(usedDir, { recursive: true, mode: 0o700 });
136
+ const usedPath = path.join(usedDir, name);
137
+ try {
138
+ fs.renameSync(path.join(root, "pending", name), usedPath);
139
+ }
140
+ catch (error) {
141
+ /* v8 ignore next -- only ENOENT is expected; other filesystem failures propagate @preserve */
142
+ if (error.code !== "ENOENT")
143
+ throw error;
144
+ return null;
145
+ }
146
+ const record = JSON.parse(fs.readFileSync(usedPath, "utf-8"));
147
+ fs.writeFileSync(usedPath, `${JSON.stringify({ ...record, consumedAt: nowIso }, null, 2)}\n`, { mode: 0o600 });
148
+ return { record, usedPath };
149
+ }
150
+ function recordOutcome(usedPath, outcome) {
151
+ const current = JSON.parse(fs.readFileSync(usedPath, "utf-8"));
152
+ fs.writeFileSync(usedPath, `${JSON.stringify({ ...current, ...outcome }, null, 2)}\n`, { mode: 0o600 });
153
+ }
154
+ /**
155
+ * Redeem a link code sent by `claimant`. Returns null when `text` is not a link code, so
156
+ * callers can run this on every inbound message and fall through to a normal turn.
157
+ */
158
+ async function redeemLinkCode(input) {
159
+ const codeBody = parseLinkCode(input.text);
160
+ if (!codeBody)
161
+ return null;
162
+ const now = (input.now ?? (() => new Date()))();
163
+ const nowIso = now.toISOString();
164
+ const baseMeta = { claimantId: input.claimant.id, channel: input.channel };
165
+ const consumed = consumeCode(input.agentRoot, codeBody, nowIso);
166
+ if (!consumed)
167
+ return reject("unknown_or_used", baseMeta);
168
+ const { record, usedPath } = consumed;
169
+ const meta = { ...baseMeta, friendId: record.friendId };
170
+ const refuse = (reason) => {
171
+ recordOutcome(usedPath, { outcome: "rejected", reason, claimantId: input.claimant.id, channel: input.channel });
172
+ return reject(reason, meta);
173
+ };
174
+ if (Date.parse(record.expiresAt) <= now.getTime())
175
+ return refuse("expired");
176
+ if (input.isGroupChat)
177
+ return refuse("group_chat");
178
+ if (record.channel && record.channel !== input.channel)
179
+ return refuse("wrong_channel");
180
+ if (input.claimant.id === record.friendId)
181
+ return refuse("already_linked");
182
+ // A code can only bring in an identity the agent does not already trust; trusted
183
+ // records are merged deliberately by the owner, not by whoever holds a code.
184
+ if (input.claimant.trustLevel !== "stranger" && input.claimant.trustLevel !== "acquaintance")
185
+ return refuse("claimant_trusted");
186
+ const merged = await (0, friend_merge_1.mergeFriends)({
187
+ store: input.store,
188
+ friendsDir: input.friendsDir,
189
+ targetId: record.friendId,
190
+ sourceIds: [input.claimant.id],
191
+ actor: `link-code:${record.issuedBy}`,
192
+ reason: `identity link code redeemed on ${input.channel}`,
193
+ now: () => now,
194
+ });
195
+ if (!merged.ok)
196
+ return refuse("friend_missing");
197
+ recordOutcome(usedPath, { outcome: "linked", claimantId: input.claimant.id, channel: input.channel, linkedExternalIds: merged.addedExternalIds.map((externalId) => externalId.provider) });
198
+ (0, runtime_1.emitNervesEvent)({
199
+ component: "friends",
200
+ event: "friends.link_code_redeemed",
201
+ message: "linked a new identity through a link code",
202
+ meta: { ...meta, trustLevel: merged.target.trustLevel, linkedExternalIds: merged.addedExternalIds.map((externalId) => externalId.provider) },
203
+ });
204
+ return {
205
+ status: "linked",
206
+ friend: merged.target,
207
+ reply: `Linked. I'll recognize this ${input.channel} account as ${merged.target.name} from now on.`,
208
+ };
209
+ }
@@ -0,0 +1,330 @@
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.externalIdKey = externalIdKey;
37
+ exports.mergeFriendRecordContent = mergeFriendRecordContent;
38
+ exports.mergeFriends = mergeFriends;
39
+ exports.planFriendDedupe = planFriendDedupe;
40
+ exports.dedupeFriends = dedupeFriends;
41
+ // Merging friend records into one person, and retiring the merged-away records.
42
+ //
43
+ // Two product paths use this: redeeming an identity link code (a new channel identity
44
+ // joins its owner's record) and deduplicating records that name the same local person.
45
+ // The target record stays authoritative: a merge never raises trust, admission,
46
+ // initiative or capability, so merging cannot be used to escalate anyone.
47
+ //
48
+ // A retired record is kept as a tombstone in `friends/_retired/` (outside the store's
49
+ // listing), never deleted silently. External-identity claim journals that pointed at the
50
+ // retired record move beside it, so the store's claim ledger never names a record that
51
+ // no longer exists. The claim journal layout belongs to @ouro.bot/friends; this module
52
+ // only moves whole journal files and never rewrites them.
53
+ const fs = __importStar(require("node:fs"));
54
+ const path = __importStar(require("node:path"));
55
+ const runtime_1 = require("../nerves/runtime");
56
+ const RETIRED_DIR = "_retired";
57
+ const CLAIMS_DIR = "_external-id-claims";
58
+ const CLAIM_FILE = /^[a-f0-9]{64}\.json$/;
59
+ function externalIdKey(externalId) {
60
+ return JSON.stringify([externalId.provider, externalId.externalId, externalId.tenantId ?? null]);
61
+ }
62
+ /** Fold the sources into the target. The target's relationship fields always win. */
63
+ function mergeFriendRecordContent(target, sources, nowIso) {
64
+ const externalIds = [...target.externalIds];
65
+ const seenIds = new Set(externalIds.map(externalIdKey));
66
+ const addedExternalIds = [];
67
+ const notes = { ...target.notes };
68
+ const preservedNoteKeys = [];
69
+ const tenantMemberships = [...target.tenantMemberships];
70
+ const toolPreferences = { ...target.toolPreferences };
71
+ const connections = [...(target.connections ?? [])];
72
+ const importedNotes = { ...(target.importedNotes ?? {}) };
73
+ let totalTokens = target.totalTokens;
74
+ let createdAt = target.createdAt;
75
+ for (const source of sources) {
76
+ for (const externalId of source.externalIds) {
77
+ const key = externalIdKey(externalId);
78
+ if (seenIds.has(key))
79
+ continue;
80
+ seenIds.add(key);
81
+ externalIds.push(externalId);
82
+ addedExternalIds.push(externalId);
83
+ }
84
+ for (const [key, note] of Object.entries(source.notes)) {
85
+ const existing = notes[key];
86
+ if (!existing) {
87
+ notes[key] = note;
88
+ continue;
89
+ }
90
+ if (existing.value === note.value)
91
+ continue;
92
+ const preservedKey = `${key}~${source.id.slice(0, 8)}`;
93
+ if (notes[preservedKey])
94
+ continue;
95
+ notes[preservedKey] = note;
96
+ preservedNoteKeys.push(preservedKey);
97
+ }
98
+ for (const tenant of source.tenantMemberships) {
99
+ if (!tenantMemberships.includes(tenant))
100
+ tenantMemberships.push(tenant);
101
+ }
102
+ for (const [tool, preference] of Object.entries(source.toolPreferences)) {
103
+ if (!(tool in toolPreferences))
104
+ toolPreferences[tool] = preference;
105
+ }
106
+ for (const connection of source.connections ?? []) {
107
+ if (!connections.some((known) => known.name === connection.name && known.relationship === connection.relationship)) {
108
+ connections.push(connection);
109
+ }
110
+ }
111
+ for (const [agentKey, imported] of Object.entries(source.importedNotes ?? {})) {
112
+ importedNotes[agentKey] = { ...imported, ...(importedNotes[agentKey] ?? {}) };
113
+ }
114
+ totalTokens += source.totalTokens;
115
+ if (source.createdAt < createdAt)
116
+ createdAt = source.createdAt;
117
+ }
118
+ const merged = {
119
+ ...target,
120
+ externalIds,
121
+ notes,
122
+ tenantMemberships,
123
+ toolPreferences,
124
+ connections,
125
+ ...(Object.keys(importedNotes).length > 0 ? { importedNotes } : {}),
126
+ totalTokens,
127
+ createdAt,
128
+ updatedAt: nowIso,
129
+ };
130
+ return { merged, addedExternalIds, preservedNoteKeys };
131
+ }
132
+ function writePrivateJson(filePath, value) {
133
+ const temp = `${filePath}.${process.pid}.${Date.now()}.tmp`;
134
+ fs.writeFileSync(temp, `${JSON.stringify(value, null, 2)}\n`, { mode: 0o600 });
135
+ fs.renameSync(temp, filePath);
136
+ }
137
+ function uniqueTombstonePath(retiredDir, friendId, nowIso) {
138
+ const plain = path.join(retiredDir, `${friendId}.json`);
139
+ if (!fs.existsSync(plain))
140
+ return plain;
141
+ return path.join(retiredDir, `${friendId}.${nowIso.replace(/[:.]/g, "-")}.json`);
142
+ }
143
+ /** Move every claim journal that names `friendId` out of the live claim ledger. */
144
+ function moveClaimJournals(friendsDir, friendId) {
145
+ const claimsDir = path.join(friendsDir, CLAIMS_DIR);
146
+ if (!fs.existsSync(claimsDir))
147
+ return [];
148
+ const retiredClaimsDir = path.join(friendsDir, RETIRED_DIR, "external-id-claims");
149
+ const moved = [];
150
+ for (const entry of fs.readdirSync(claimsDir)) {
151
+ if (!CLAIM_FILE.test(entry))
152
+ continue;
153
+ let claim;
154
+ try {
155
+ claim = JSON.parse(fs.readFileSync(path.join(claimsDir, entry), "utf-8"));
156
+ }
157
+ catch {
158
+ continue;
159
+ }
160
+ if (claim.friendId !== friendId)
161
+ continue;
162
+ fs.mkdirSync(retiredClaimsDir, { recursive: true, mode: 0o700 });
163
+ fs.renameSync(path.join(claimsDir, entry), path.join(retiredClaimsDir, entry));
164
+ moved.push(entry);
165
+ }
166
+ return moved;
167
+ }
168
+ async function mergeFriends(input) {
169
+ const nowIso = (input.now ?? (() => new Date()))().toISOString();
170
+ const dryRun = input.dryRun === true;
171
+ const sourceIds = [...new Set(input.sourceIds)];
172
+ if (sourceIds.length === 0 || sourceIds.includes(input.targetId)) {
173
+ return { ok: false, reason: "invalid_sources", message: "name at least one record to merge, other than the target" };
174
+ }
175
+ const target = await input.store.get(input.targetId);
176
+ if (!target || target.id !== input.targetId) {
177
+ return { ok: false, reason: "target_not_found", message: `friend not found: ${input.targetId}` };
178
+ }
179
+ const sources = [];
180
+ for (const sourceId of sourceIds) {
181
+ const source = await input.store.get(sourceId);
182
+ if (!source || source.id !== sourceId) {
183
+ return { ok: false, reason: "source_not_found", message: `friend not found: ${sourceId}` };
184
+ }
185
+ if ((source.kind ?? "human") !== (target.kind ?? "human")) {
186
+ return { ok: false, reason: "kind_mismatch", message: `cannot merge ${sourceId} into ${target.id}: an agent and a human are different friends` };
187
+ }
188
+ sources.push(source);
189
+ }
190
+ const { merged, addedExternalIds, preservedNoteKeys } = mergeFriendRecordContent(target, sources, nowIso);
191
+ const retiredDir = path.join(input.friendsDir, RETIRED_DIR);
192
+ if (dryRun) {
193
+ return {
194
+ ok: true,
195
+ dryRun,
196
+ target: merged,
197
+ addedExternalIds,
198
+ preservedNoteKeys,
199
+ retired: sources.map((source) => ({ friendId: source.id, name: source.name, tombstonePath: path.join(retiredDir, `${source.id}.json`), movedClaims: [] })),
200
+ };
201
+ }
202
+ // Order: tombstones first (nothing is lost if we stop), then the target gains the
203
+ // identities, then the sources leave the store and their claim journals follow them.
204
+ fs.mkdirSync(retiredDir, { recursive: true, mode: 0o700 });
205
+ const tombstones = sources.map((source) => {
206
+ const tombstonePath = uniqueTombstonePath(retiredDir, source.id, nowIso);
207
+ writePrivateJson(tombstonePath, {
208
+ schemaVersion: 1,
209
+ retiredAt: nowIso,
210
+ retiredInto: target.id,
211
+ actor: input.actor,
212
+ reason: input.reason,
213
+ record: source,
214
+ });
215
+ return { source, tombstonePath };
216
+ });
217
+ await input.store.put(target.id, merged);
218
+ const retired = [];
219
+ for (const { source, tombstonePath } of tombstones) {
220
+ await input.store.delete(source.id);
221
+ const movedClaims = moveClaimJournals(input.friendsDir, source.id);
222
+ retired.push({ friendId: source.id, name: source.name, tombstonePath, movedClaims });
223
+ (0, runtime_1.emitNervesEvent)({
224
+ component: "friends",
225
+ event: "friends.record_retired",
226
+ message: "retired a friend record into another record",
227
+ meta: { friendId: source.id, retiredInto: target.id, actor: input.actor, movedClaims: movedClaims.length },
228
+ });
229
+ }
230
+ const stored = (await input.store.get(target.id)) ?? merged;
231
+ (0, runtime_1.emitNervesEvent)({
232
+ component: "friends",
233
+ event: "friends.records_merged",
234
+ message: "merged friend records",
235
+ meta: {
236
+ targetId: target.id,
237
+ sourceIds,
238
+ actor: input.actor,
239
+ reason: input.reason,
240
+ addedExternalIds: addedExternalIds.map((externalId) => externalId.provider),
241
+ preservedNoteKeys: preservedNoteKeys.length,
242
+ },
243
+ });
244
+ return { ok: true, dryRun, target: stored, addedExternalIds, preservedNoteKeys, retired };
245
+ }
246
+ function isLocalOnly(record) {
247
+ return record.externalIds.length > 0 && record.externalIds.every((externalId) => externalId.provider === "local");
248
+ }
249
+ function canonicalOrder(a, b) {
250
+ const notes = Object.keys(b.notes).length - Object.keys(a.notes).length;
251
+ if (notes !== 0)
252
+ return notes;
253
+ if (a.createdAt !== b.createdAt)
254
+ return a.createdAt < b.createdAt ? -1 : 1;
255
+ return a.id < b.id ? -1 : 1;
256
+ }
257
+ /**
258
+ * Records are duplicates when they are human, carry only local identities, share the
259
+ * same name and trust level, and are joined through at least one shared local identity.
260
+ * The canonical record is the one with the most notes, then the oldest.
261
+ */
262
+ function planFriendDedupe(records) {
263
+ const buckets = new Map();
264
+ for (const record of records) {
265
+ if ((record.kind ?? "human") !== "human" || !isLocalOnly(record))
266
+ continue;
267
+ const key = JSON.stringify([record.name.trim().toLowerCase(), record.trustLevel ?? "friend"]);
268
+ buckets.set(key, [...(buckets.get(key) ?? []), record]);
269
+ }
270
+ const groups = [];
271
+ for (const bucket of buckets.values()) {
272
+ // Union records that share any local identity.
273
+ const parent = bucket.map((_, index) => index);
274
+ const find = (index) => (parent[index] === index ? index : (parent[index] = find(parent[index])));
275
+ const owner = new Map();
276
+ bucket.forEach((record, index) => {
277
+ for (const externalId of record.externalIds) {
278
+ const key = externalIdKey(externalId);
279
+ const seen = owner.get(key);
280
+ if (seen === undefined)
281
+ owner.set(key, index);
282
+ else
283
+ parent[find(index)] = find(seen);
284
+ }
285
+ });
286
+ const components = new Map();
287
+ bucket.forEach((record, index) => {
288
+ const root = find(index);
289
+ components.set(root, [...(components.get(root) ?? []), record]);
290
+ });
291
+ for (const members of components.values()) {
292
+ if (members.length < 2)
293
+ continue;
294
+ const [canonical, ...duplicates] = [...members].sort(canonicalOrder);
295
+ groups.push({ canonical: canonical, duplicates });
296
+ }
297
+ }
298
+ return groups.sort((a, b) => canonicalOrder(a.canonical, b.canonical));
299
+ }
300
+ async function dedupeFriends(input) {
301
+ const listAll = input.store.listAll;
302
+ const records = listAll ? await listAll.call(input.store) : [];
303
+ const plan = planFriendDedupe(records);
304
+ const dryRun = input.dryRun !== false;
305
+ const groups = [];
306
+ for (const group of plan) {
307
+ const duplicateIds = group.duplicates.map((record) => record.id);
308
+ const result = await mergeFriends({
309
+ store: input.store,
310
+ friendsDir: input.friendsDir,
311
+ targetId: group.canonical.id,
312
+ sourceIds: duplicateIds,
313
+ actor: input.actor,
314
+ reason: "duplicate local records of the same person",
315
+ dryRun,
316
+ ...(input.now ? { now: input.now } : {}),
317
+ });
318
+ /* v8 ignore next -- the plan only names records it just listed; a concurrent removal is the only way here @preserve */
319
+ if (!result.ok)
320
+ throw new Error(result.message);
321
+ groups.push({ canonical: result.target, duplicateIds, preservedNoteKeys: result.preservedNoteKeys, addedExternalIds: result.addedExternalIds });
322
+ }
323
+ (0, runtime_1.emitNervesEvent)({
324
+ component: "friends",
325
+ event: "friends.dedupe_completed",
326
+ message: dryRun ? "planned friend record dedupe" : "applied friend record dedupe",
327
+ meta: { dryRun, groups: groups.length, duplicates: groups.reduce((sum, group) => sum + group.duplicateIds.length, 0), actor: input.actor },
328
+ });
329
+ return { dryRun, groups };
330
+ }