@ouro.bot/cli 0.1.0-alpha.62 → 0.1.0-alpha.637

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 (432) hide show
  1. package/README.md +127 -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 +4087 -13
  13. package/dist/arc/attention-types.js +8 -0
  14. package/dist/arc/cares.js +144 -0
  15. package/dist/arc/episodes.js +118 -0
  16. package/dist/arc/evolution.js +487 -0
  17. package/dist/arc/intentions.js +134 -0
  18. package/dist/arc/json-store.js +117 -0
  19. package/dist/arc/obligations.js +270 -0
  20. package/dist/arc/packets.js +288 -0
  21. package/dist/arc/presence.js +185 -0
  22. package/dist/arc/task-lifecycle.js +57 -0
  23. package/dist/heart/active-work.js +860 -43
  24. package/dist/heart/agent-entry.js +69 -3
  25. package/dist/heart/attachments/image-normalize.js +194 -0
  26. package/dist/heart/attachments/materialize.js +97 -0
  27. package/dist/heart/attachments/originals.js +88 -0
  28. package/dist/heart/attachments/render.js +29 -0
  29. package/dist/heart/attachments/sources/bluebubbles.js +156 -0
  30. package/dist/heart/attachments/sources/cli-local-file.js +78 -0
  31. package/dist/heart/attachments/sources/index.js +16 -0
  32. package/dist/heart/attachments/store.js +103 -0
  33. package/dist/heart/attachments/types.js +93 -0
  34. package/dist/heart/auth/auth-flow.js +479 -0
  35. package/dist/heart/awaiting/await-alert.js +146 -0
  36. package/dist/heart/awaiting/await-expiry.js +108 -0
  37. package/dist/heart/awaiting/await-loader.js +91 -0
  38. package/dist/heart/awaiting/await-parser.js +141 -0
  39. package/dist/heart/awaiting/await-runtime-state.js +100 -0
  40. package/dist/heart/awaiting/await-scheduler.js +377 -0
  41. package/dist/heart/background-operations.js +281 -0
  42. package/dist/heart/bridges/manager.js +137 -17
  43. package/dist/heart/bridges/store.js +14 -2
  44. package/dist/heart/bundle-state.js +168 -0
  45. package/dist/heart/commitments.js +135 -0
  46. package/dist/heart/config-registry.js +322 -0
  47. package/dist/heart/config.js +114 -119
  48. package/dist/heart/core.js +1028 -248
  49. package/dist/heart/cross-chat-delivery.js +3 -18
  50. package/dist/heart/daemon/agent-config-check.js +419 -0
  51. package/dist/heart/daemon/agent-discovery.js +102 -3
  52. package/dist/heart/daemon/agent-service.js +522 -0
  53. package/dist/heart/daemon/agentic-repair.js +547 -0
  54. package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
  55. package/dist/heart/daemon/boot-sync-probe.js +197 -0
  56. package/dist/heart/daemon/cadence.js +70 -0
  57. package/dist/heart/daemon/cli-defaults.js +776 -0
  58. package/dist/heart/daemon/cli-desk.js +322 -0
  59. package/dist/heart/daemon/cli-exec.js +7468 -0
  60. package/dist/heart/daemon/cli-help.js +505 -0
  61. package/dist/heart/daemon/cli-parse.js +1554 -0
  62. package/dist/heart/daemon/cli-render-doctor.js +57 -0
  63. package/dist/heart/daemon/cli-render.js +763 -0
  64. package/dist/heart/daemon/cli-types.js +8 -0
  65. package/dist/heart/daemon/connect-bay.js +323 -0
  66. package/dist/heart/daemon/daemon-cli.js +29 -1700
  67. package/dist/heart/daemon/daemon-entry.js +485 -2
  68. package/dist/heart/daemon/daemon-health.js +176 -0
  69. package/dist/heart/daemon/daemon-rollup.js +57 -0
  70. package/dist/heart/daemon/daemon-runtime-sync.js +88 -13
  71. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  72. package/dist/heart/daemon/daemon.js +906 -71
  73. package/dist/heart/daemon/dns-workflow.js +394 -0
  74. package/dist/heart/daemon/doctor-types.js +8 -0
  75. package/dist/heart/daemon/doctor.js +873 -0
  76. package/dist/heart/daemon/health-monitor.js +122 -1
  77. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  78. package/dist/heart/daemon/hooks/bundle-meta.js +115 -1
  79. package/dist/heart/daemon/http-health-probe.js +80 -0
  80. package/dist/heart/daemon/human-command-screens.js +234 -0
  81. package/dist/heart/daemon/human-readiness.js +114 -0
  82. package/dist/heart/daemon/inner-status.js +89 -0
  83. package/dist/heart/daemon/interactive-repair.js +394 -0
  84. package/dist/heart/daemon/launchd.js +37 -8
  85. package/dist/heart/daemon/log-tailer.js +79 -10
  86. package/dist/heart/daemon/logs-prune.js +110 -0
  87. package/dist/heart/daemon/mcp-canary.js +297 -0
  88. package/dist/heart/daemon/migrate-to-desk.js +848 -0
  89. package/dist/heart/daemon/os-cron-deps.js +135 -0
  90. package/dist/heart/daemon/os-cron.js +14 -12
  91. package/dist/heart/daemon/ouro-bot-entry.js +4 -2
  92. package/dist/heart/daemon/ouro-entry.js +3 -1
  93. package/dist/heart/daemon/plugin-cli.js +432 -0
  94. package/dist/heart/daemon/process-manager.js +501 -35
  95. package/dist/heart/daemon/provider-discovery.js +137 -0
  96. package/dist/heart/daemon/provider-ping-progress.js +83 -0
  97. package/dist/heart/daemon/pulse.js +475 -0
  98. package/dist/heart/daemon/readiness-repair.js +365 -0
  99. package/dist/heart/daemon/run-hooks.js +2 -0
  100. package/dist/heart/daemon/runtime-logging.js +11 -3
  101. package/dist/heart/daemon/runtime-metadata.js +2 -30
  102. package/dist/heart/daemon/safe-mode.js +161 -0
  103. package/dist/heart/daemon/sense-manager.js +493 -38
  104. package/dist/heart/daemon/session-id-resolver.js +131 -0
  105. package/dist/heart/daemon/skill-management-installer.js +22 -9
  106. package/dist/heart/daemon/socket-client.js +158 -11
  107. package/dist/heart/daemon/stale-bundle-prune.js +96 -0
  108. package/dist/heart/daemon/startup-tui.js +330 -0
  109. package/dist/heart/daemon/task-scheduler.js +117 -39
  110. package/dist/heart/daemon/terminal-ui.js +499 -0
  111. package/dist/heart/daemon/thoughts.js +229 -17
  112. package/dist/heart/daemon/up-progress.js +366 -0
  113. package/dist/heart/daemon/vault-items.js +56 -0
  114. package/dist/heart/delegation.js +1 -4
  115. package/dist/heart/habits/habit-migration.js +189 -0
  116. package/dist/heart/habits/habit-parser.js +140 -0
  117. package/dist/heart/habits/habit-runtime-state.js +100 -0
  118. package/dist/heart/habits/habit-scheduler.js +372 -0
  119. package/dist/heart/{daemon → hatch}/hatch-flow.js +32 -56
  120. package/dist/heart/{daemon → hatch}/hatch-specialist.js +6 -8
  121. package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
  122. package/dist/heart/{daemon → hatch}/specialist-tools.js +37 -14
  123. package/dist/heart/identity.js +168 -57
  124. package/dist/heart/kept-notes.js +357 -0
  125. package/dist/heart/kicks.js +1 -1
  126. package/dist/heart/machine-identity.js +161 -0
  127. package/dist/heart/mail-import-discovery.js +353 -0
  128. package/dist/heart/mailbox/mailbox-http-hooks.js +66 -0
  129. package/dist/heart/mailbox/mailbox-http-response.js +7 -0
  130. package/dist/heart/mailbox/mailbox-http-routes.js +246 -0
  131. package/dist/heart/mailbox/mailbox-http-static.js +103 -0
  132. package/dist/heart/mailbox/mailbox-http-transport.js +116 -0
  133. package/dist/heart/mailbox/mailbox-http.js +99 -0
  134. package/dist/heart/mailbox/mailbox-read.js +31 -0
  135. package/dist/heart/mailbox/mailbox-types.js +27 -0
  136. package/dist/heart/mailbox/mailbox-view.js +197 -0
  137. package/dist/heart/mailbox/readers/agent-machine.js +418 -0
  138. package/dist/heart/mailbox/readers/continuity-readers.js +319 -0
  139. package/dist/heart/mailbox/readers/mail.js +375 -0
  140. package/dist/heart/mailbox/readers/runtime-readers.js +756 -0
  141. package/dist/heart/mailbox/readers/sessions.js +232 -0
  142. package/dist/heart/mailbox/readers/shared.js +111 -0
  143. package/dist/heart/mcp/mcp-server.js +692 -0
  144. package/dist/heart/migrate-config.js +100 -0
  145. package/dist/heart/model-capabilities.js +19 -0
  146. package/dist/heart/orientation-frame.js +217 -0
  147. package/dist/heart/platform.js +81 -0
  148. package/dist/heart/provider-attempt.js +134 -0
  149. package/dist/heart/provider-binding-resolver.js +272 -0
  150. package/dist/heart/provider-credentials.js +425 -0
  151. package/dist/heart/provider-failover.js +301 -0
  152. package/dist/heart/provider-models.js +81 -0
  153. package/dist/heart/provider-ping.js +262 -0
  154. package/dist/heart/provider-readiness-cache.js +40 -0
  155. package/dist/heart/provider-visibility.js +188 -0
  156. package/dist/heart/providers/anthropic-token.js +131 -0
  157. package/dist/heart/providers/anthropic.js +139 -52
  158. package/dist/heart/providers/azure.js +23 -11
  159. package/dist/heart/providers/error-classification.js +127 -0
  160. package/dist/heart/providers/github-copilot.js +145 -0
  161. package/dist/heart/providers/minimax-vlm.js +189 -0
  162. package/dist/heart/providers/minimax.js +26 -8
  163. package/dist/heart/providers/openai-codex.js +55 -40
  164. package/dist/heart/runtime-capability-check.js +170 -0
  165. package/dist/heart/runtime-credentials.js +367 -0
  166. package/dist/heart/runtime-cwd.js +87 -0
  167. package/dist/heart/sense-truth.js +13 -4
  168. package/dist/heart/session-activity.js +48 -24
  169. package/dist/heart/session-events.js +1163 -0
  170. package/dist/heart/session-playback-cli-main.js +5 -0
  171. package/dist/heart/session-playback-cli.js +36 -0
  172. package/dist/heart/session-playback.js +231 -0
  173. package/dist/heart/session-stats-cli-main.js +5 -0
  174. package/dist/heart/session-stats.js +182 -0
  175. package/dist/heart/session-transcript.js +133 -0
  176. package/dist/heart/start-of-turn-packet.js +345 -0
  177. package/dist/heart/streaming.js +44 -27
  178. package/dist/heart/structured-output.js +196 -0
  179. package/dist/heart/sync-classification.js +176 -0
  180. package/dist/heart/sync.js +449 -0
  181. package/dist/heart/target-resolution.js +9 -5
  182. package/dist/heart/tempo.js +93 -0
  183. package/dist/heart/temporal-view.js +41 -0
  184. package/dist/heart/timeouts.js +101 -0
  185. package/dist/heart/tool-activity-callbacks.js +59 -0
  186. package/dist/heart/tool-description.js +143 -0
  187. package/dist/heart/tool-friction.js +55 -0
  188. package/dist/heart/tool-loop.js +200 -0
  189. package/dist/heart/turn-context.js +389 -0
  190. package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +6 -5
  191. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  192. package/dist/heart/versioning/ouro-path-installer.js +426 -0
  193. package/dist/heart/versioning/ouro-version-manager.js +295 -0
  194. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  195. package/dist/heart/{daemon → versioning}/update-checker.js +6 -1
  196. package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
  197. package/dist/mailbox-ui/assets/index-9-AxCxuB.js +61 -0
  198. package/dist/mailbox-ui/assets/index-CWzt267f.css +1 -0
  199. package/dist/mailbox-ui/index.html +15 -0
  200. package/dist/mailroom/attention.js +167 -0
  201. package/dist/mailroom/autonomy.js +209 -0
  202. package/dist/mailroom/blob-store.js +715 -0
  203. package/dist/mailroom/body-cache.js +61 -0
  204. package/dist/mailroom/core.js +788 -0
  205. package/dist/mailroom/entry.js +160 -0
  206. package/dist/mailroom/file-store.js +568 -0
  207. package/dist/mailroom/mbox-import.js +393 -0
  208. package/dist/mailroom/migration.js +164 -0
  209. package/dist/mailroom/outbound.js +380 -0
  210. package/dist/mailroom/policy.js +263 -0
  211. package/dist/mailroom/reader.js +233 -0
  212. package/dist/mailroom/search-cache.js +334 -0
  213. package/dist/mailroom/search-relevance.js +319 -0
  214. package/dist/mailroom/smtp-ingress.js +176 -0
  215. package/dist/mailroom/source-state.js +176 -0
  216. package/dist/mailroom/thread.js +109 -0
  217. package/dist/mailroom/travel-extract.js +89 -0
  218. package/dist/mind/bundle-manifest.js +14 -1
  219. package/dist/mind/context.js +251 -101
  220. package/dist/mind/desk-section.js +310 -0
  221. package/dist/mind/diary-integrity.js +60 -0
  222. package/dist/mind/{memory.js → diary.js} +68 -76
  223. package/dist/mind/embedding-provider.js +60 -0
  224. package/dist/mind/file-state.js +179 -0
  225. package/dist/mind/friends/channel.js +39 -0
  226. package/dist/mind/friends/resolver.js +54 -2
  227. package/dist/mind/friends/store-file.js +48 -4
  228. package/dist/mind/friends/types.js +2 -2
  229. package/dist/mind/journal-index.js +162 -0
  230. package/dist/mind/note-search.js +268 -0
  231. package/dist/mind/obligation-steering.js +221 -0
  232. package/dist/mind/pending.js +6 -1
  233. package/dist/mind/prompt-refresh.js +3 -2
  234. package/dist/mind/prompt.js +1075 -146
  235. package/dist/mind/provenance-trust.js +26 -0
  236. package/dist/mind/scrutiny.js +173 -0
  237. package/dist/nerves/cli-logging.js +7 -1
  238. package/dist/nerves/coverage/audit-rules.js +15 -6
  239. package/dist/nerves/coverage/audit.js +28 -2
  240. package/dist/nerves/coverage/cli.js +1 -1
  241. package/dist/nerves/coverage/contract.js +5 -5
  242. package/dist/nerves/coverage/file-completeness.js +139 -5
  243. package/dist/nerves/event-buffer.js +111 -0
  244. package/dist/nerves/index.js +224 -4
  245. package/dist/nerves/observation.js +20 -0
  246. package/dist/nerves/redact.js +79 -0
  247. package/dist/nerves/review/cli-main.js +5 -0
  248. package/dist/nerves/review/cli.js +156 -0
  249. package/dist/nerves/review/core.js +152 -0
  250. package/dist/nerves/runtime.js +5 -1
  251. package/dist/repertoire/ado-client.js +15 -56
  252. package/dist/repertoire/ado-semantic.js +16 -10
  253. package/dist/repertoire/api-client.js +97 -0
  254. package/dist/repertoire/bitwarden-store.js +1040 -0
  255. package/dist/repertoire/bundle-templates.js +72 -0
  256. package/dist/repertoire/bw-installer.js +180 -0
  257. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  258. package/dist/repertoire/coding/context-pack.js +331 -0
  259. package/dist/repertoire/coding/feedback.js +197 -30
  260. package/dist/repertoire/coding/manager.js +166 -10
  261. package/dist/repertoire/coding/spawner.js +55 -9
  262. package/dist/repertoire/coding/tools.js +219 -7
  263. package/dist/repertoire/commerce-errors.js +109 -0
  264. package/dist/repertoire/commerce-self-test.js +156 -0
  265. package/dist/repertoire/credential-access.js +178 -0
  266. package/dist/repertoire/desk/classifier.js +362 -0
  267. package/dist/repertoire/duffel-client.js +185 -0
  268. package/dist/repertoire/github-client.js +14 -55
  269. package/dist/repertoire/graph-client.js +11 -52
  270. package/dist/repertoire/guardrails.js +385 -0
  271. package/dist/repertoire/mcp-client.js +295 -0
  272. package/dist/repertoire/mcp-manager.js +403 -0
  273. package/dist/repertoire/mcp-tools.js +83 -0
  274. package/dist/repertoire/plugin-mcp.js +175 -0
  275. package/dist/repertoire/plugins.js +253 -0
  276. package/dist/repertoire/shell-sessions.js +133 -0
  277. package/dist/repertoire/skills.js +48 -4
  278. package/dist/repertoire/stripe-client.js +131 -0
  279. package/dist/repertoire/tool-results.js +29 -0
  280. package/dist/repertoire/tools-attachments.js +317 -0
  281. package/dist/repertoire/tools-awaiting.js +372 -0
  282. package/dist/repertoire/tools-base.js +59 -1082
  283. package/dist/repertoire/tools-bluebubbles.js +2 -0
  284. package/dist/repertoire/tools-bridge.js +144 -0
  285. package/dist/repertoire/tools-bundle.js +993 -0
  286. package/dist/repertoire/tools-config.js +186 -0
  287. package/dist/repertoire/tools-continuity.js +252 -0
  288. package/dist/repertoire/tools-credential.js +383 -0
  289. package/dist/repertoire/tools-evolution.js +527 -0
  290. package/dist/repertoire/tools-files.js +344 -0
  291. package/dist/repertoire/tools-flight.js +227 -0
  292. package/dist/repertoire/tools-flow.js +119 -0
  293. package/dist/repertoire/tools-github.js +3 -8
  294. package/dist/repertoire/tools-mail.js +1975 -0
  295. package/dist/repertoire/tools-notes.js +438 -0
  296. package/dist/repertoire/tools-obligations.js +143 -0
  297. package/dist/repertoire/tools-orientation.js +31 -0
  298. package/dist/repertoire/tools-record.js +464 -0
  299. package/dist/repertoire/tools-runtime.js +150 -0
  300. package/dist/repertoire/tools-session.js +766 -0
  301. package/dist/repertoire/tools-shell.js +120 -0
  302. package/dist/repertoire/tools-stripe.js +182 -0
  303. package/dist/repertoire/tools-surface.js +344 -0
  304. package/dist/repertoire/tools-teams.js +12 -39
  305. package/dist/repertoire/tools-travel.js +125 -0
  306. package/dist/repertoire/tools-trip.js +982 -0
  307. package/dist/repertoire/tools-user-profile.js +146 -0
  308. package/dist/repertoire/tools-vault.js +40 -0
  309. package/dist/repertoire/tools-voice.js +145 -0
  310. package/dist/repertoire/tools.js +215 -103
  311. package/dist/repertoire/travel-api-client.js +360 -0
  312. package/dist/repertoire/user-profile.js +131 -0
  313. package/dist/repertoire/vault-setup.js +246 -0
  314. package/dist/repertoire/vault-unlock.js +594 -0
  315. package/dist/scripts/claude-code-hook.js +41 -0
  316. package/dist/scripts/claude-code-stop-hook.js +47 -0
  317. package/dist/senses/attention-queue.js +186 -0
  318. package/dist/senses/await-turn-message.js +58 -0
  319. package/dist/senses/bluebubbles/active-turns.js +216 -0
  320. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  321. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  322. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +219 -18
  323. package/dist/senses/bluebubbles/entry.js +77 -0
  324. package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +20 -3
  325. package/dist/senses/bluebubbles/index.js +2737 -0
  326. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -71
  327. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +33 -12
  328. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +3 -3
  329. package/dist/senses/bluebubbles/processed-log.js +133 -0
  330. package/dist/senses/bluebubbles/replay.js +137 -0
  331. package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +30 -2
  332. package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
  333. package/dist/senses/bluebubbles-meta-guard.js +40 -0
  334. package/dist/senses/cli/bracketed-paste.js +82 -0
  335. package/dist/senses/cli/image-paste.js +287 -0
  336. package/dist/senses/cli/image-ref-navigation.js +75 -0
  337. package/dist/senses/cli/ink-app.js +156 -0
  338. package/dist/senses/cli/inline-diff.js +64 -0
  339. package/dist/senses/cli/input-keys.js +174 -0
  340. package/dist/senses/cli/kill-ring.js +86 -0
  341. package/dist/senses/cli/message-list.js +51 -0
  342. package/dist/senses/cli/ouro-tui.js +607 -0
  343. package/dist/senses/cli/spinner-imperative.js +135 -0
  344. package/dist/senses/cli/spinner.js +101 -0
  345. package/dist/senses/cli/status-line.js +60 -0
  346. package/dist/senses/cli/streaming-markdown.js +526 -0
  347. package/dist/senses/cli/tool-display.js +85 -0
  348. package/dist/senses/cli/tool-render.js +85 -0
  349. package/dist/senses/cli/tui-store.js +240 -0
  350. package/dist/senses/cli/virtual-list.js +35 -0
  351. package/dist/senses/cli-entry.js +60 -8
  352. package/dist/senses/cli-layout.js +100 -0
  353. package/dist/senses/cli.js +517 -204
  354. package/dist/senses/commands.js +66 -3
  355. package/dist/senses/habit-turn-message.js +108 -0
  356. package/dist/senses/inner-dialog-worker.js +254 -22
  357. package/dist/senses/inner-dialog.js +505 -40
  358. package/dist/senses/mail-entry.js +66 -0
  359. package/dist/senses/mail.js +379 -0
  360. package/dist/senses/pipeline.js +666 -181
  361. package/dist/senses/proactive-content-guard.js +51 -0
  362. package/dist/senses/shared-turn.js +393 -0
  363. package/dist/senses/surface-tool.js +108 -0
  364. package/dist/senses/teams-entry.js +60 -8
  365. package/dist/senses/teams.js +388 -98
  366. package/dist/senses/trust-gate.js +100 -5
  367. package/dist/senses/voice/audio-playback.js +237 -0
  368. package/dist/senses/voice/audio-routing.js +119 -0
  369. package/dist/senses/voice/elevenlabs.js +202 -0
  370. package/dist/senses/voice/floor-control.js +431 -0
  371. package/dist/senses/voice/floor-controller.js +115 -0
  372. package/dist/senses/voice/golden-path.js +116 -0
  373. package/dist/senses/voice/index.js +29 -0
  374. package/dist/senses/voice/meeting.js +113 -0
  375. package/dist/senses/voice/outbound.js +190 -0
  376. package/dist/senses/voice/phone.js +33 -0
  377. package/dist/senses/voice/playback.js +139 -0
  378. package/dist/senses/voice/realtime-eval.js +496 -0
  379. package/dist/senses/voice/realtime-trace.js +531 -0
  380. package/dist/senses/voice/transcript.js +70 -0
  381. package/dist/senses/voice/turn.js +191 -0
  382. package/dist/senses/voice/twilio-phone-runtime.js +807 -0
  383. package/dist/senses/voice/twilio-phone.js +5079 -0
  384. package/dist/senses/voice/types.js +2 -0
  385. package/dist/senses/voice/whisper.js +161 -0
  386. package/dist/senses/voice-entry.js +81 -0
  387. package/dist/senses/voice-realtime-eval-command.js +99 -0
  388. package/dist/senses/voice-realtime-eval-entry.js +21 -0
  389. package/dist/senses/voice-twilio-entry.js +87 -0
  390. package/dist/trips/core.js +138 -0
  391. package/dist/trips/store.js +265 -0
  392. package/dist/util/frontmatter.js +53 -0
  393. package/package.json +48 -8
  394. package/skills/agent-commerce.md +106 -0
  395. package/skills/browser-navigation.md +117 -0
  396. package/skills/commerce-setup-guide.md +116 -0
  397. package/skills/commerce-setup.md +84 -0
  398. package/skills/configure-dev-tools.md +99 -0
  399. package/skills/travel-planning.md +138 -0
  400. package/dist/heart/daemon/auth-flow.js +0 -351
  401. package/dist/heart/daemon/ouro-path-installer.js +0 -178
  402. package/dist/heart/safe-workspace.js +0 -228
  403. package/dist/heart/session-recall.js +0 -116
  404. package/dist/mind/associative-recall.js +0 -209
  405. package/dist/repertoire/tasks/board.js +0 -134
  406. package/dist/repertoire/tasks/index.js +0 -224
  407. package/dist/repertoire/tasks/lifecycle.js +0 -80
  408. package/dist/repertoire/tasks/middleware.js +0 -65
  409. package/dist/repertoire/tasks/parser.js +0 -173
  410. package/dist/repertoire/tasks/scanner.js +0 -132
  411. package/dist/repertoire/tasks/transitions.js +0 -144
  412. package/dist/senses/bluebubbles-entry.js +0 -13
  413. package/dist/senses/bluebubbles.js +0 -1177
  414. package/dist/senses/debug-activity.js +0 -148
  415. package/subagents/README.md +0 -7
  416. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  417. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  418. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  419. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  420. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  421. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  422. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  423. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  424. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  425. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  426. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  427. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  428. /package/dist/{repertoire/tasks/types.js → heart/attachments/sources/adapter.js} +0 -0
  429. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  430. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  431. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  432. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -0,0 +1,848 @@
1
+ "use strict";
2
+ /**
3
+ * `ouro migrate-to-desk` migrator.
4
+ *
5
+ * Reads `<bundle>/tasks/**` recursively, classifies each file per the
6
+ * shared triage rules (see `src/repertoire/desk/classifier.ts`), and writes
7
+ * the result into `<bundle>/desk/` in the new desk shape:
8
+ *
9
+ * - terminal / stale_live / ambiguous → `desk/_archive/<original-relative-path>`
10
+ * - live_clear → `desk/legacy/<slug>/{task.md, iterations/...}`
11
+ * - special_europe_trip → `desk/summer-2026-europe-trip/` (full lift)
12
+ * - track-level `track.md` for `legacy`
13
+ * - migration log at `desk/_meta/migration-2026-05-22.log`
14
+ *
15
+ * **COPY semantics, not move.** The source `tasks/` tree is left intact for
16
+ * dual-read safety. The operator triggers final deletion in a later step
17
+ * (out of scope here).
18
+ *
19
+ * **Idempotency.** A second run aborts cleanly unless `--force` is passed.
20
+ * With `--force`, destructive scope is bounded to migrator-owned dirs:
21
+ * `desk/_archive/`, `desk/legacy/`, `desk/summer-2026-europe-trip/`,
22
+ * `desk/_meta/featured.md`, `desk/_meta/migration-2026-05-22.log`.
23
+ * Nothing else under `desk/` is touched.
24
+ *
25
+ * **Dry-run.** `--dry-run` writes the summary to stdout and modifies nothing.
26
+ *
27
+ * **Slug derivation + pairing.** Paired planning/doing/ideation files
28
+ * sharing a slug collapse into a single task with iterations. The migrator
29
+ * groups by `deriveTaskSlug()` (which strips date prefixes and role infix).
30
+ * If any sibling is terminal → all siblings terminal. Else if any is
31
+ * live_clear → all live_clear.
32
+ *
33
+ * **Europe-trip fallback.** If a `europe-trip-lift-plan.md` is not embedded
34
+ * here, we use minimal stub content for the two tasks and the track
35
+ * `track.md`. The operator can flesh out the content in a follow-up.
36
+ *
37
+ * The CLI surface: `ouro migrate-to-desk --agent <name> [--root <path>]
38
+ * [--force] [--dry-run]`. The `--root` flag overrides the bundle root
39
+ * (defaults to `<bundlesRoot>/<agent>.ouro`). Used by Units 12 + 13 to
40
+ * point at `/tmp/<bundle>-latest/`.
41
+ */
42
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
43
+ if (k2 === undefined) k2 = k;
44
+ var desc = Object.getOwnPropertyDescriptor(m, k);
45
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
46
+ desc = { enumerable: true, get: function() { return m[k]; } };
47
+ }
48
+ Object.defineProperty(o, k2, desc);
49
+ }) : (function(o, m, k, k2) {
50
+ if (k2 === undefined) k2 = k;
51
+ o[k2] = m[k];
52
+ }));
53
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
54
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
55
+ }) : function(o, v) {
56
+ o["default"] = v;
57
+ });
58
+ var __importStar = (this && this.__importStar) || (function () {
59
+ var ownKeys = function(o) {
60
+ ownKeys = Object.getOwnPropertyNames || function (o) {
61
+ var ar = [];
62
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
63
+ return ar;
64
+ };
65
+ return ownKeys(o);
66
+ };
67
+ return function (mod) {
68
+ if (mod && mod.__esModule) return mod;
69
+ var result = {};
70
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
71
+ __setModuleDefault(result, mod);
72
+ return result;
73
+ };
74
+ })();
75
+ Object.defineProperty(exports, "__esModule", { value: true });
76
+ exports.runMigrateToDesk = runMigrateToDesk;
77
+ exports.ensureSchemaVersion = ensureSchemaVersion;
78
+ const fs = __importStar(require("fs"));
79
+ const path = __importStar(require("path"));
80
+ const runtime_1 = require("../../nerves/runtime");
81
+ const classifier_1 = require("../../repertoire/desk/classifier");
82
+ // ── Constants ──
83
+ const LEGACY_TRACK_NAME = "legacy";
84
+ const EUROPE_TRIP_TRACK_NAME = "summer-2026-europe-trip";
85
+ const EUROPE_TRIP_TASK_OUTBOUND = "book-replacement-outbound";
86
+ const EUROPE_TRIP_TASK_WEEKLY = "weekly-trip-check";
87
+ const MIGRATION_LOG_BASENAME = "migration-2026-05-22.log";
88
+ const SCHEMA_VERSION = 1;
89
+ const DEFAULT_CUTOFF_DAYS = 30;
90
+ const MIGRATOR_OWNED_DIRS = ["_archive", LEGACY_TRACK_NAME, EUROPE_TRIP_TRACK_NAME];
91
+ const MIGRATOR_OWNED_META_FILES = ["featured.md", MIGRATION_LOG_BASENAME];
92
+ // ── Public entry point ──
93
+ async function runMigrateToDesk(opts) {
94
+ const bundleRoot = opts.root ?? path.join(opts.bundlesRoot, `${opts.agent}.ouro`);
95
+ const tasksRoot = path.join(bundleRoot, "tasks");
96
+ const deskRoot = path.join(bundleRoot, "desk");
97
+ const metaDir = path.join(deskRoot, "_meta");
98
+ const logPath = path.join(metaDir, MIGRATION_LOG_BASENAME);
99
+ const today = opts.today ?? new Date(Date.UTC(2026, 4, 22));
100
+ const cutoffMs = opts.cutoffMs ?? today.getTime() - DEFAULT_CUTOFF_DAYS * 24 * 3600 * 1000;
101
+ (0, runtime_1.emitNervesEvent)({
102
+ component: "daemon",
103
+ event: "daemon.migrate_to_desk_start",
104
+ message: `migrate-to-desk started for ${opts.agent}`,
105
+ meta: {
106
+ agent: opts.agent,
107
+ root: bundleRoot,
108
+ force: Boolean(opts.force),
109
+ dryRun: Boolean(opts.dryRun),
110
+ },
111
+ });
112
+ // Missing-bundle handling.
113
+ if (!fs.existsSync(tasksRoot)) {
114
+ const summary = `migrate-to-desk: no tasks/ directory at ${tasksRoot} — nothing to migrate`;
115
+ (0, runtime_1.emitNervesEvent)({
116
+ component: "daemon",
117
+ event: "daemon.migrate_to_desk_no_source",
118
+ message: summary,
119
+ meta: { agent: opts.agent, root: bundleRoot },
120
+ });
121
+ return {
122
+ performed: false,
123
+ summary,
124
+ counts: emptyCounts(),
125
+ logPath,
126
+ };
127
+ }
128
+ // Idempotency check.
129
+ if (fs.existsSync(logPath) && !opts.force) {
130
+ const summary = `migrate-to-desk: migration log already exists at ${logPath}; ` +
131
+ `pass --force to re-run (scope limited to migrator-owned dirs).`;
132
+ (0, runtime_1.emitNervesEvent)({
133
+ component: "daemon",
134
+ event: "daemon.migrate_to_desk_aborted_existing",
135
+ message: summary,
136
+ meta: { agent: opts.agent, logPath },
137
+ });
138
+ return {
139
+ performed: false,
140
+ summary,
141
+ counts: emptyCounts(),
142
+ logPath,
143
+ abortedExisting: true,
144
+ };
145
+ }
146
+ // Discover + classify.
147
+ const allFiles = walkTasks(tasksRoot);
148
+ const classifications = classifyAll(allFiles, tasksRoot, cutoffMs);
149
+ const grouped = applyPairingRules(classifications);
150
+ // Build the migration plan.
151
+ const plan = buildPlan(grouped, deskRoot);
152
+ const counts = countBuckets(grouped);
153
+ if (opts.dryRun) {
154
+ const summary = renderDryRunSummary(plan, counts, deskRoot, logPath);
155
+ (0, runtime_1.emitNervesEvent)({
156
+ component: "daemon",
157
+ event: "daemon.migrate_to_desk_dry_run",
158
+ message: "migrate-to-desk dry-run complete",
159
+ meta: { agent: opts.agent, counts },
160
+ });
161
+ return { performed: false, summary, counts, logPath };
162
+ }
163
+ // Force scope: clean migrator-owned dirs only.
164
+ if (opts.force) {
165
+ clearMigratorOwnedDirs(deskRoot);
166
+ }
167
+ // Ensure desk dirs exist.
168
+ fs.mkdirSync(metaDir, { recursive: true });
169
+ // Apply the plan.
170
+ for (const action of plan) {
171
+ applyPlanAction(action);
172
+ }
173
+ // Write the track.md for the legacy track if any live_clear tasks landed.
174
+ const hasLegacyTasks = plan.some((a) => a.kind === "write_task" && a.outputPath.startsWith(path.join(deskRoot, LEGACY_TRACK_NAME) + path.sep));
175
+ if (hasLegacyTasks) {
176
+ const legacyTrackMd = path.join(deskRoot, LEGACY_TRACK_NAME, "track.md");
177
+ fs.mkdirSync(path.dirname(legacyTrackMd), { recursive: true });
178
+ fs.writeFileSync(legacyTrackMd, renderLegacyTrackMd(today), "utf-8");
179
+ }
180
+ // Europe-trip lift: write the track + tasks + featured pointer.
181
+ const hasEuropeTrip = plan.some((a) => a.kind === "europe_trip");
182
+ if (hasEuropeTrip) {
183
+ writeEuropeTripScaffold(deskRoot, today);
184
+ fs.writeFileSync(path.join(metaDir, "featured.md"), `${EUROPE_TRIP_TRACK_NAME}\n`, "utf-8");
185
+ }
186
+ // Write the migration log.
187
+ const logLines = renderLogLines(grouped, plan, today);
188
+ fs.writeFileSync(logPath, logLines.join("\n") + "\n", "utf-8");
189
+ const summary = renderPerformedSummary(counts, deskRoot, logPath);
190
+ (0, runtime_1.emitNervesEvent)({
191
+ component: "daemon",
192
+ event: "daemon.migrate_to_desk_complete",
193
+ message: "migrate-to-desk complete",
194
+ meta: { agent: opts.agent, counts, logPath },
195
+ });
196
+ return { performed: true, summary, counts, logPath };
197
+ }
198
+ function walkTasks(tasksRoot) {
199
+ const out = [];
200
+ walk(tasksRoot, "", out);
201
+ return out;
202
+ }
203
+ function walk(root, rel, out) {
204
+ const dir = rel ? path.join(root, rel) : root;
205
+ let entries;
206
+ try {
207
+ entries = fs.readdirSync(dir, { withFileTypes: true });
208
+ }
209
+ catch {
210
+ /* v8 ignore start -- defensive: walk into a missing/permission-denied subdir; tasksRoot existence checked at entry @preserve */
211
+ return;
212
+ /* v8 ignore stop */
213
+ }
214
+ for (const entry of entries) {
215
+ const childRel = rel ? path.join(rel, entry.name) : entry.name;
216
+ const childAbs = path.join(dir, entry.name);
217
+ if (entry.isDirectory()) {
218
+ walk(root, childRel, out);
219
+ continue;
220
+ }
221
+ /* v8 ignore start -- defensive: entry is neither directory nor file (socket, FIFO, etc.); skip silently. Not reachable from synthetic fixture bundles, but defends against pathological filesystems. @preserve */
222
+ if (!entry.isFile())
223
+ continue;
224
+ /* v8 ignore stop */
225
+ let mtimeMs = Date.now();
226
+ try {
227
+ mtimeMs = fs.statSync(childAbs).mtimeMs;
228
+ }
229
+ catch {
230
+ /* v8 ignore start -- defensive: stat failure on a file we just listed; fallback to now() @preserve */
231
+ mtimeMs = Date.now();
232
+ /* v8 ignore stop */
233
+ }
234
+ out.push({ relPath: normalizeRelPath(childRel), absPath: childAbs, mtimeMs });
235
+ }
236
+ }
237
+ function normalizeRelPath(p) {
238
+ return p.split(path.sep).join("/");
239
+ }
240
+ function classifyAll(files, tasksRoot, cutoffMs) {
241
+ const out = [];
242
+ for (const file of files) {
243
+ let content = "";
244
+ try {
245
+ content = fs.readFileSync(file.absPath, "utf-8");
246
+ }
247
+ catch {
248
+ /* v8 ignore start -- defensive: read failure on a file walk just enumerated; treated as ambiguous @preserve */
249
+ content = "";
250
+ /* v8 ignore stop */
251
+ }
252
+ const result = (0, classifier_1.classifyFile)({
253
+ relPath: file.relPath,
254
+ content,
255
+ mtimeMs: file.mtimeMs,
256
+ cutoffMs,
257
+ });
258
+ out.push({ ...file, initial: result });
259
+ }
260
+ // Silence unused-tasksRoot lint; included for symmetry/debugging clarity.
261
+ void tasksRoot;
262
+ return out;
263
+ }
264
+ /**
265
+ * Apply pairing rules:
266
+ * 1. Sub-files under `<taskdir>/` inherit from their parent task file.
267
+ * 2. Sibling planning/doing/ideation/audit files sharing a slug group together.
268
+ * 3. If any sibling is terminal → all terminal. Else if any is live_clear → all live_clear.
269
+ */
270
+ function applyPairingRules(entries) {
271
+ // First pass: identify task-file siblings by directory + slug.
272
+ // We only group top-level markdown files (those without a parent task dir).
273
+ const slugGroups = new Map();
274
+ const childrenByParent = new Map();
275
+ const standalones = [];
276
+ for (const entry of entries) {
277
+ const parentDir = (0, classifier_1.extractParentTaskDir)(entry.relPath);
278
+ if (parentDir) {
279
+ const list = childrenByParent.get(parentDir) ?? [];
280
+ list.push(entry);
281
+ childrenByParent.set(parentDir, list);
282
+ continue;
283
+ }
284
+ if (entry.relPath.endsWith(".md") && !entry.initial.bucket.startsWith("special")) {
285
+ // Group by slug + parent path (e.g. one-shots/foo)
286
+ const slug = (0, classifier_1.deriveTaskSlug)(entry.relPath);
287
+ const dir = path.posix.dirname(entry.relPath);
288
+ const key = `${dir}::${slug}`;
289
+ const list = slugGroups.get(key) ?? [];
290
+ list.push(entry);
291
+ slugGroups.set(key, list);
292
+ }
293
+ else {
294
+ standalones.push(entry);
295
+ }
296
+ }
297
+ // Determine the group's final bucket: any terminal → terminal; else any
298
+ // live_clear → live_clear; else any stale_live → stale_live; else ambiguous.
299
+ // Special_europe_trip is preserved as-is (it lands in standalones, not
300
+ // slugGroups, so the special-trip branch below is defensive).
301
+ const finalBySlug = new Map();
302
+ for (const [key, members] of slugGroups.entries()) {
303
+ /* v8 ignore start -- special_europe_trip files are routed to `standalones` (not slugGroups) by the slug-grouping check above. The branch is defensive in case the classifier ever changes. @preserve */
304
+ if (members.some((m) => m.initial.bucket === "special_europe_trip")) {
305
+ finalBySlug.set(key, "special_europe_trip");
306
+ continue;
307
+ }
308
+ /* v8 ignore stop */
309
+ if (members.some((m) => m.initial.bucket === "terminal")) {
310
+ finalBySlug.set(key, "terminal");
311
+ continue;
312
+ }
313
+ if (members.some((m) => m.initial.bucket === "live_clear")) {
314
+ finalBySlug.set(key, "live_clear");
315
+ continue;
316
+ }
317
+ if (members.some((m) => m.initial.bucket === "stale_live")) {
318
+ finalBySlug.set(key, "stale_live");
319
+ continue;
320
+ }
321
+ finalBySlug.set(key, "ambiguous");
322
+ }
323
+ // Build the result.
324
+ const result = [];
325
+ for (const [key, members] of slugGroups.entries()) {
326
+ // finalBySlug.set(key, ...) was called for every key in slugGroups above,
327
+ // so the lookup is guaranteed non-undefined.
328
+ const finalBucket = finalBySlug.get(key);
329
+ for (const m of members) {
330
+ result.push({ ...m, finalBucket, pairingSlug: key });
331
+ }
332
+ }
333
+ // Children inherit from the parent task dir's slug group.
334
+ for (const [parentDir, kids] of childrenByParent.entries()) {
335
+ const slug = (0, classifier_1.deriveTaskSlug)(parentDir + ".md");
336
+ const dir = path.posix.dirname(parentDir + ".md");
337
+ const key = `${dir}::${slug}`;
338
+ // Parent might not exist (orphan child); default to ambiguous.
339
+ const finalBucket = finalBySlug.get(key) ?? "ambiguous";
340
+ for (const kid of kids) {
341
+ result.push({ ...kid, finalBucket, pairingSlug: key });
342
+ }
343
+ }
344
+ // Standalones (special_europe_trip, archive subdir non-md, etc.) pass through.
345
+ for (const s of standalones) {
346
+ result.push({ ...s, finalBucket: s.initial.bucket });
347
+ }
348
+ return result;
349
+ }
350
+ function buildPlan(grouped, deskRoot) {
351
+ const plan = [];
352
+ const archiveRoot = path.join(deskRoot, "_archive");
353
+ const legacyRoot = path.join(deskRoot, LEGACY_TRACK_NAME);
354
+ // Group live_clear entries by their pairing slug for task assembly.
355
+ // Live_clear entries only come from slugGroups or childrenByParent — both
356
+ // assign a pairingSlug — so we can safely use a non-null assertion when
357
+ // looking it up. Standalones never produce live_clear (they're only special
358
+ // or non-markdown ambiguous files).
359
+ const liveTasksBySlug = new Map();
360
+ for (const entry of grouped) {
361
+ if (entry.finalBucket !== "live_clear")
362
+ continue;
363
+ const slug = entry.pairingSlug;
364
+ const list = liveTasksBySlug.get(slug) ?? [];
365
+ list.push(entry);
366
+ liveTasksBySlug.set(slug, list);
367
+ }
368
+ for (const entry of grouped) {
369
+ switch (entry.finalBucket) {
370
+ case "terminal":
371
+ case "stale_live":
372
+ case "ambiguous": {
373
+ const outputPath = path.join(archiveRoot, entry.relPath);
374
+ plan.push({
375
+ kind: "copy_to_archive",
376
+ src: entry.absPath,
377
+ outputPath,
378
+ relPath: entry.relPath,
379
+ markdown: entry.relPath.endsWith(".md"),
380
+ });
381
+ break;
382
+ }
383
+ case "special_europe_trip": {
384
+ plan.push({ kind: "europe_trip", relPath: entry.relPath });
385
+ break;
386
+ }
387
+ case "live_clear": {
388
+ // Live clear entries become tasks under `legacy/<slug>/`. Use the
389
+ // pairing slug's slug-portion (after `::`) for the directory.
390
+ const pairingSlug = entry.pairingSlug;
391
+ const taskSlug = derivePairingSlugDir(pairingSlug, entry.relPath);
392
+ const taskDir = path.join(legacyRoot, taskSlug);
393
+ // If this is the canonical task file (parent .md in the same dir),
394
+ // it becomes `task.md`. Otherwise it's an iteration artifact.
395
+ const group = liveTasksBySlug.get(pairingSlug);
396
+ const isCanonical = isCanonicalTaskFile(entry, group);
397
+ if (isCanonical) {
398
+ plan.push({
399
+ kind: "write_task",
400
+ outputPath: path.join(taskDir, "task.md"),
401
+ sources: group,
402
+ relPath: entry.relPath,
403
+ });
404
+ }
405
+ else {
406
+ // Iteration artifact — preserve filename under iterations/.
407
+ const iterBase = path.basename(entry.relPath);
408
+ plan.push({
409
+ kind: "copy_to_iteration",
410
+ src: entry.absPath,
411
+ outputPath: path.join(taskDir, "iterations", iterBase),
412
+ relPath: entry.relPath,
413
+ markdown: iterBase.endsWith(".md"),
414
+ });
415
+ }
416
+ break;
417
+ }
418
+ }
419
+ }
420
+ return plan;
421
+ }
422
+ /**
423
+ * Extract the slug-portion of a pairing key. Pairing keys look like
424
+ * `<dir>::<slug>`. The caller (buildPlan) only invokes this with a confirmed
425
+ * non-empty pairingSlug created by `applyPairingRules`, which always inserts
426
+ * a `::` separator. The relPath fallback exists as a defensive guard.
427
+ */
428
+ function derivePairingSlugDir(pairingSlug, relPath) {
429
+ const sep = pairingSlug.indexOf("::");
430
+ /* v8 ignore start -- defensive: pairing keys are constructed as `${dir}::${slug}` in applyPairingRules; the `::` separator always exists. @preserve */
431
+ if (sep === -1)
432
+ return (0, classifier_1.deriveTaskSlug)(relPath);
433
+ /* v8 ignore stop */
434
+ return pairingSlug.slice(sep + 2);
435
+ }
436
+ /**
437
+ * Determine which entry in a pairing group is the canonical task file.
438
+ * Preference: file with `doing-` infix (the executable doc) > `planning-` >
439
+ * earliest by filename. Sub-artifacts (those under a `<task>/` subdir) never
440
+ * win.
441
+ */
442
+ function isCanonicalTaskFile(entry, group) {
443
+ // Children (with a pairing-derived parent dir) are never canonical.
444
+ if ((0, classifier_1.extractParentTaskDir)(entry.relPath))
445
+ return false;
446
+ // From within the group, pick the canonical winner.
447
+ const topLevels = group.filter((g) => !(0, classifier_1.extractParentTaskDir)(g.relPath));
448
+ /* v8 ignore start -- defensive: if entry has no parent task dir (above), it IS a top-level file and is in the group, so topLevels always contains at least entry itself. The empty-array branch is unreachable. @preserve */
449
+ if (topLevels.length === 0)
450
+ return false;
451
+ /* v8 ignore stop */
452
+ const winner = pickCanonical(topLevels);
453
+ return winner.relPath === entry.relPath;
454
+ }
455
+ function pickCanonical(candidates) {
456
+ // Prefer doing- > planning- > others, then earliest filename.
457
+ const role = (rel) => {
458
+ const base = path.basename(rel);
459
+ if (/\b-doing-/.test(base) || /^doing-/.test(base.replace(/^\d{4}-\d{2}-\d{2}(?:-\d{4})?-/, "")))
460
+ return 0;
461
+ /* v8 ignore start -- the `^planning-` post-strip alternative and the `return 2` other-role fallthrough only fire for undated filenames (no `YYYY-MM-DD-` prefix). Real bundles use the dated convention exclusively. @preserve */
462
+ if (/\b-planning-/.test(base) || /^planning-/.test(base.replace(/^\d{4}-\d{2}-\d{2}(?:-\d{4})?-/, "")))
463
+ return 1;
464
+ return 2;
465
+ /* v8 ignore stop */
466
+ };
467
+ const sorted = [...candidates].sort((a, b) => {
468
+ const ra = role(a.relPath);
469
+ const rb = role(b.relPath);
470
+ /* v8 ignore start -- the false branch of `ra !== rb` (same-role tiebreaker) is only reachable when a group has two files with the same role (e.g. two `-doing-` siblings). Legacy bundles use paired doing+planning, not duplicate roles. @preserve */
471
+ if (ra === rb)
472
+ return a.relPath.localeCompare(b.relPath);
473
+ /* v8 ignore stop */
474
+ return ra - rb;
475
+ });
476
+ return sorted[0];
477
+ }
478
+ // ── Plan application ──
479
+ function applyPlanAction(action) {
480
+ switch (action.kind) {
481
+ case "copy_to_archive": {
482
+ fs.mkdirSync(path.dirname(action.outputPath), { recursive: true });
483
+ if (action.markdown) {
484
+ const raw = fs.readFileSync(action.src, "utf-8");
485
+ const withSchema = ensureSchemaVersion(raw);
486
+ fs.writeFileSync(action.outputPath, withSchema, "utf-8");
487
+ }
488
+ else {
489
+ fs.copyFileSync(action.src, action.outputPath);
490
+ }
491
+ break;
492
+ }
493
+ case "copy_to_iteration": {
494
+ fs.mkdirSync(path.dirname(action.outputPath), { recursive: true });
495
+ if (action.markdown) {
496
+ const raw = fs.readFileSync(action.src, "utf-8");
497
+ const withSchema = ensureSchemaVersion(raw);
498
+ fs.writeFileSync(action.outputPath, withSchema, "utf-8");
499
+ }
500
+ else {
501
+ fs.copyFileSync(action.src, action.outputPath);
502
+ }
503
+ break;
504
+ }
505
+ case "write_task": {
506
+ fs.mkdirSync(path.dirname(action.outputPath), { recursive: true });
507
+ const taskMd = renderTaskMd(action.sources, action.relPath);
508
+ fs.writeFileSync(action.outputPath, taskMd, "utf-8");
509
+ break;
510
+ }
511
+ case "europe_trip": {
512
+ // Handled by writeEuropeTripScaffold after plan execution.
513
+ break;
514
+ }
515
+ }
516
+ }
517
+ /**
518
+ * If the markdown file has YAML frontmatter, ensure it carries
519
+ * `schema_version: 1`. If it has no frontmatter, prepend a minimal block.
520
+ */
521
+ function ensureSchemaVersion(raw) {
522
+ const trimmed = raw.trimStart();
523
+ if (trimmed.startsWith("---")) {
524
+ const afterFirst = trimmed.slice(3);
525
+ const closeIdx = afterFirst.indexOf("\n---");
526
+ if (closeIdx !== -1) {
527
+ const fmBlock = afterFirst.slice(0, closeIdx);
528
+ if (/\bschema_version\s*:/.test(fmBlock)) {
529
+ return raw;
530
+ }
531
+ // Insert schema_version line at the start of the frontmatter block.
532
+ const head = trimmed.slice(0, 3); // "---"
533
+ const newFmBlock = `schema_version: ${SCHEMA_VERSION}${fmBlock}`;
534
+ const rest = afterFirst.slice(closeIdx);
535
+ return `${head}\n${newFmBlock}${rest}`;
536
+ }
537
+ }
538
+ // No frontmatter — prepend a minimal block.
539
+ return `---\nschema_version: ${SCHEMA_VERSION}\n---\n${raw}`;
540
+ }
541
+ // ── Task / track rendering ──
542
+ function renderTaskMd(sources, primaryRelPath) {
543
+ const winner = pickCanonical(sources.filter((s) => !(0, classifier_1.extractParentTaskDir)(s.relPath)));
544
+ const winnerContent = readSafe(winner.absPath);
545
+ const withSchema = ensureSchemaVersion(winnerContent);
546
+ // Footer noting the migration provenance.
547
+ const trailer = [
548
+ "",
549
+ "<!-- migrated from `tasks/" + primaryRelPath + "` on 2026-05-22 -->",
550
+ "",
551
+ ].join("\n");
552
+ return withSchema.endsWith("\n") ? withSchema + trailer : withSchema + "\n" + trailer;
553
+ }
554
+ function renderLegacyTrackMd(today) {
555
+ const dateStr = today.toISOString().slice(0, 10);
556
+ return [
557
+ "---",
558
+ "schema_version: 1",
559
+ "track: legacy",
560
+ "status: collaborating",
561
+ `created: ${dateStr}`,
562
+ "---",
563
+ "",
564
+ "# legacy",
565
+ "",
566
+ `Migrated from \`tasks/\` on ${dateStr} by the legacy-tasks-to-desk migrator.`,
567
+ "",
568
+ "## triage rules",
569
+ "",
570
+ "- TERMINAL (done / approved / complete / cancelled / fixed / etc.) → archived under `_archive/`",
571
+ "- STALE_LIVE (live status but updated >30 days ago, cutoff 2026-04-22) → archived under `_archive/`",
572
+ "- AMBIGUOUS (no status, junk format, .md.bak, empty) → archived under `_archive/`",
573
+ "- LIVE_CLEAR (live status, updated within last 30 days, coherent) → migrated here, as one task per paired slug, with iterations under `iterations/`",
574
+ "",
575
+ "Tasks under this track are the recovered live work-in-progress. Audit and either move to a proper track or archive.",
576
+ "",
577
+ ].join("\n");
578
+ }
579
+ // ── Europe-trip scaffold (minimal stubs per Unit 11 fallback) ──
580
+ function writeEuropeTripScaffold(deskRoot, today) {
581
+ const trackDir = path.join(deskRoot, EUROPE_TRIP_TRACK_NAME);
582
+ const planningDir = path.join(trackDir, "_planning");
583
+ const outboundDir = path.join(trackDir, EUROPE_TRIP_TASK_OUTBOUND);
584
+ const weeklyDir = path.join(trackDir, EUROPE_TRIP_TASK_WEEKLY);
585
+ for (const d of [trackDir, planningDir, outboundDir, weeklyDir]) {
586
+ fs.mkdirSync(d, { recursive: true });
587
+ }
588
+ fs.writeFileSync(path.join(trackDir, "track.md"), renderEuropeTripTrackMd(today), "utf-8");
589
+ fs.writeFileSync(path.join(planningDir, "overview.md"), renderEuropeTripOverview(), "utf-8");
590
+ fs.writeFileSync(path.join(planningDir, "next-actions.md"), renderEuropeTripNextActions(), "utf-8");
591
+ fs.writeFileSync(path.join(outboundDir, "task.md"), renderEuropeTripOutboundTask(today), "utf-8");
592
+ fs.writeFileSync(path.join(weeklyDir, "task.md"), renderEuropeTripWeeklyTask(today), "utf-8");
593
+ }
594
+ function renderEuropeTripTrackMd(today) {
595
+ const dateStr = today.toISOString().slice(0, 10);
596
+ return [
597
+ "---",
598
+ "schema_version: 1",
599
+ `track: ${EUROPE_TRIP_TRACK_NAME}`,
600
+ "featured: true",
601
+ "status: active",
602
+ "urgency: high",
603
+ "target_date: 2026-08-01",
604
+ "trip_record: trips/records/trip_summer-2026-europe-trip_82bdea2a9d088cbe.json",
605
+ "travel_docs: travel/2026-summer-trip/",
606
+ `created: ${dateStr}`,
607
+ "---",
608
+ "",
609
+ "# summer-2026-europe-trip",
610
+ "",
611
+ "Project-management overlay for the summer 2026 Europe trip. Trip-tools own the canonical state (itinerary, bookings, ledger, records); this desk track owns the recurring pulse and the open project-management chase.",
612
+ "",
613
+ "## status",
614
+ "",
615
+ "Migrated by the legacy-tasks-to-desk migrator from `tasks/ongoing/2026-03-09-1410-summer-2026-europe-trip.md`. Flesh out from original sources + `travel/2026-summer-trip/` state.",
616
+ "",
617
+ "## links into trip-tools",
618
+ "",
619
+ "- `travel/2026-summer-trip/itinerary.md`",
620
+ "- `travel/2026-summer-trip/bookings.md`",
621
+ "- `travel/2026-summer-trip/budget.md`",
622
+ "- `travel/2026-summer-trip/packing.md`",
623
+ "- `travel/2026-summer-trip/ideas.md`",
624
+ "- `travel/2026-summer-trip/basel.md`",
625
+ "- `travel/2026-summer-trip/research-log.md`",
626
+ "- `travel/2026-summer-trip/gap-fill-2026-04-30.md`",
627
+ "- `trips/records/trip_summer-2026-europe-trip_82bdea2a9d088cbe.json`",
628
+ "",
629
+ "## active tasks",
630
+ "",
631
+ `- \`${EUROPE_TRIP_TASK_OUTBOUND}\` — replacement flight chase (Lufthansa 9FLJTF cancelled 2026-04-16; refund issued)`,
632
+ `- \`${EUROPE_TRIP_TASK_WEEKLY}\` — recurring weekly→daily project-level pulse`,
633
+ "",
634
+ ].join("\n");
635
+ }
636
+ function renderEuropeTripOverview() {
637
+ return [
638
+ "---",
639
+ "schema_version: 1",
640
+ "---",
641
+ "",
642
+ "# overview — desk vs. trip-tools",
643
+ "",
644
+ "Trip-tools own the canonical trip state: `trips/` (ledger + records) and `travel/2026-summer-trip/` (itinerary, bookings, budget, packing, ideas, basel, research-log, gap-fill).",
645
+ "",
646
+ "This desk track owns only the project-management overlay: the weekly pulse and the open chase items. It LINKS to trip-tools files; it does not duplicate them.",
647
+ "",
648
+ "Migrated as a minimal stub by the legacy-tasks-to-desk migrator. Flesh out content from `tasks/ongoing/2026-03-09-1410-summer-2026-europe-trip.md` + the latest gap-fill.",
649
+ "",
650
+ ].join("\n");
651
+ }
652
+ function renderEuropeTripNextActions() {
653
+ return [
654
+ "---",
655
+ "schema_version: 1",
656
+ "---",
657
+ "",
658
+ "# next actions",
659
+ "",
660
+ "Project-management actions that trip-tools don't naturally model:",
661
+ "",
662
+ "- (placeholder) Surface 3-5 replacement-flight candidates",
663
+ "- (placeholder) Confirm Italy/wedding lodging artifact from the operator",
664
+ "",
665
+ "Stub left by the migrator — operator can hand-edit content in a follow-up.",
666
+ "",
667
+ ].join("\n");
668
+ }
669
+ function renderEuropeTripOutboundTask(today) {
670
+ const dateStr = today.toISOString().slice(0, 10);
671
+ return [
672
+ "---",
673
+ "schema_version: 1",
674
+ `track: ${EUROPE_TRIP_TRACK_NAME}`,
675
+ "status: processing",
676
+ "target_date: 2026-08-01",
677
+ `created: ${dateStr}`,
678
+ "---",
679
+ "",
680
+ `# ${EUROPE_TRIP_TASK_OUTBOUND}`,
681
+ "",
682
+ "Replacement outbound flight to Europe — Lufthansa 9FLJTF (LX8007 SEA→ZRH) was cancelled 2026-04-16; refund issued 2026-04-17 ($1,269 + $298 services).",
683
+ "",
684
+ "Search order per `travel/2026-summer-trip/gap-fill-2026-04-30.md#1-replacement-flight-to-europe`:",
685
+ "1. SEA→BSL one-stops first",
686
+ "2. SEA→ZRH + train fallback",
687
+ "",
688
+ "Hotel Märthof check-in is 15:00 on Aug 2.",
689
+ "",
690
+ "Stub left by the migrator — operator can hand-edit content in a follow-up.",
691
+ "",
692
+ ].join("\n");
693
+ }
694
+ function renderEuropeTripWeeklyTask(today) {
695
+ const dateStr = today.toISOString().slice(0, 10);
696
+ return [
697
+ "---",
698
+ "schema_version: 1",
699
+ `track: ${EUROPE_TRIP_TRACK_NAME}`,
700
+ "status: drafting",
701
+ `created: ${dateStr}`,
702
+ "cadence:",
703
+ " - until: 2026-07-25",
704
+ " every: weekly",
705
+ " - until: 2026-08-01",
706
+ " every: daily",
707
+ "---",
708
+ "",
709
+ `# ${EUROPE_TRIP_TASK_WEEKLY}`,
710
+ "",
711
+ "Recurring project-level pulse. Each tick: walk `travel/2026-summer-trip/bookings.md`, list open slots and any `target_date` inside the next interval, append a one-line entry to `_planning/next-actions.md`. Post-departure: auto-archive.",
712
+ "",
713
+ "Stub left by the migrator — operator can hand-edit content in a follow-up.",
714
+ "",
715
+ ].join("\n");
716
+ }
717
+ // ── Force-mode scope ──
718
+ function clearMigratorOwnedDirs(deskRoot) {
719
+ for (const d of MIGRATOR_OWNED_DIRS) {
720
+ const p = path.join(deskRoot, d);
721
+ if (fs.existsSync(p))
722
+ removeTreeEnumerated(p);
723
+ }
724
+ const metaDir = path.join(deskRoot, "_meta");
725
+ for (const f of MIGRATOR_OWNED_META_FILES) {
726
+ const p = path.join(metaDir, f);
727
+ if (fs.existsSync(p))
728
+ fs.rmSync(p, { force: true });
729
+ }
730
+ }
731
+ /**
732
+ * Remove a directory tree by enumerating its contents — readdir, recurse into
733
+ * subdirs, rmSync each file, then rmdirSync bottom-up. Avoids `recursive:
734
+ * true` per the harness's "agent-callable code should enumerate" rule
735
+ * (see test-isolation.contract.test.ts Directive A).
736
+ */
737
+ function removeTreeEnumerated(root) {
738
+ let stat;
739
+ try {
740
+ stat = fs.lstatSync(root);
741
+ }
742
+ catch {
743
+ /* v8 ignore start -- defensive: removed underneath us; nothing to do @preserve */
744
+ return;
745
+ /* v8 ignore stop */
746
+ }
747
+ /* v8 ignore start -- defensive: MIGRATOR_OWNED_DIRS is a fixed list of dir names; this branch only fires if one of those names happens to be a file (impossible by construction) @preserve */
748
+ if (!stat.isDirectory()) {
749
+ fs.rmSync(root, { force: true });
750
+ return;
751
+ }
752
+ /* v8 ignore stop */
753
+ const entries = fs.readdirSync(root, { withFileTypes: true });
754
+ for (const entry of entries) {
755
+ const child = path.join(root, entry.name);
756
+ if (entry.isDirectory()) {
757
+ removeTreeEnumerated(child);
758
+ }
759
+ else {
760
+ fs.rmSync(child, { force: true });
761
+ }
762
+ }
763
+ fs.rmdirSync(root);
764
+ }
765
+ // ── Summary + log rendering ──
766
+ function emptyCounts() {
767
+ return {
768
+ terminal: 0,
769
+ stale_live: 0,
770
+ ambiguous: 0,
771
+ live_clear: 0,
772
+ special_europe_trip: 0,
773
+ };
774
+ }
775
+ function countBuckets(grouped) {
776
+ const counts = emptyCounts();
777
+ for (const g of grouped) {
778
+ counts[g.finalBucket] += 1;
779
+ }
780
+ return counts;
781
+ }
782
+ function renderDryRunSummary(plan, counts, deskRoot, logPath) {
783
+ const lines = [];
784
+ lines.push("[dry-run] migrate-to-desk plan:");
785
+ lines.push(` desk root: ${deskRoot}`);
786
+ lines.push(` counts:`);
787
+ lines.push(` terminal: ${counts.terminal}`);
788
+ lines.push(` stale_live: ${counts.stale_live}`);
789
+ lines.push(` ambiguous: ${counts.ambiguous}`);
790
+ lines.push(` live_clear: ${counts.live_clear}`);
791
+ lines.push(` special_europe_trip: ${counts.special_europe_trip}`);
792
+ lines.push(` planned actions: ${plan.length}`);
793
+ lines.push(` would write log: ${logPath}`);
794
+ lines.push("[dry-run] no files modified.");
795
+ return lines.join("\n");
796
+ }
797
+ function renderPerformedSummary(counts, deskRoot, logPath) {
798
+ const lines = [];
799
+ lines.push("migrate-to-desk: complete.");
800
+ lines.push(` desk root: ${deskRoot}`);
801
+ lines.push(` archived (terminal): ${counts.terminal}`);
802
+ lines.push(` archived (stale_live): ${counts.stale_live}`);
803
+ lines.push(` archived (ambiguous): ${counts.ambiguous}`);
804
+ lines.push(` migrated to legacy track: ${counts.live_clear}`);
805
+ lines.push(` special_europe_trip: ${counts.special_europe_trip}`);
806
+ lines.push(` log: ${logPath}`);
807
+ return lines.join("\n");
808
+ }
809
+ function renderLogLines(grouped, plan, today) {
810
+ const lines = [];
811
+ const dateStr = today.toISOString().slice(0, 10);
812
+ lines.push(`# migration log — ${dateStr}`);
813
+ lines.push("# format: <bucket>\t<source-relpath>\t<dest-relpath>\t<reason>");
814
+ // Map relPath → destination for log output.
815
+ const destByRelPath = new Map();
816
+ for (const action of plan) {
817
+ switch (action.kind) {
818
+ case "copy_to_archive":
819
+ case "copy_to_iteration":
820
+ case "write_task":
821
+ destByRelPath.set(action.relPath, action.outputPath);
822
+ break;
823
+ case "europe_trip":
824
+ destByRelPath.set(action.relPath, "<europe-trip-scaffold>");
825
+ break;
826
+ }
827
+ }
828
+ // Sort by relPath for deterministic output.
829
+ const sorted = [...grouped].sort((a, b) => a.relPath.localeCompare(b.relPath));
830
+ for (const g of sorted) {
831
+ /* v8 ignore start -- defensive: every grouped entry produces a plan action that registers in destByRelPath above. The `<unrouted>` fallback is unreachable in practice. @preserve */
832
+ const dest = destByRelPath.get(g.relPath) ?? "<unrouted>";
833
+ /* v8 ignore stop */
834
+ lines.push([g.finalBucket, g.relPath, dest, g.initial.reason].join("\t"));
835
+ }
836
+ return lines;
837
+ }
838
+ // ── Small helpers ──
839
+ function readSafe(p) {
840
+ try {
841
+ return fs.readFileSync(p, "utf-8");
842
+ }
843
+ catch {
844
+ /* v8 ignore start -- defensive: re-read of a file we already accessed in classifyAll; treated as empty @preserve */
845
+ return "";
846
+ /* v8 ignore stop */
847
+ }
848
+ }