@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
@@ -33,9 +33,11 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.MarkdownStreamer = exports.InputController = exports.Spinner = exports.StreamingWordWrapper = exports.wrapCliText = exports.formatEchoedInputSummary = void 0;
36
+ exports.InputQueue = exports.MarkdownStreamer = exports.InputController = exports.Spinner = exports.StreamingWordWrapper = exports.wrapCliText = exports.formatEchoedInputSummary = void 0;
37
37
  exports.formatPendingPrefix = formatPendingPrefix;
38
38
  exports.getCliContinuityIngressTexts = getCliContinuityIngressTexts;
39
+ exports.formatTimeAgo = formatTimeAgo;
40
+ exports.writeCliAsyncAssistantMessage = writeCliAsyncAssistantMessage;
39
41
  exports.pauseActiveSpinner = pauseActiveSpinner;
40
42
  exports.resumeActiveSpinner = resumeActiveSpinner;
41
43
  exports.setActiveSpinner = setActiveSpinner;
@@ -54,25 +56,27 @@ const prompt_1 = require("../mind/prompt");
54
56
  const phrases_1 = require("../mind/phrases");
55
57
  const format_1 = require("../mind/format");
56
58
  const config_1 = require("../heart/config");
59
+ const session_events_1 = require("../heart/session-events");
57
60
  const context_1 = require("../mind/context");
58
61
  const pending_1 = require("../mind/pending");
59
62
  const commands_1 = require("./commands");
60
63
  const identity_1 = require("../heart/identity");
61
64
  const mcp_manager_1 = require("../repertoire/mcp-manager");
62
65
  const nerves_1 = require("../nerves");
63
- const store_file_1 = require("../mind/friends/store-file");
64
- const resolver_1 = require("../mind/friends/resolver");
65
- const tokens_1 = require("../mind/friends/tokens");
66
+ const friends_1 = require("@ouro.bot/friends");
66
67
  const cli_logging_1 = require("../nerves/cli-logging");
67
68
  const runtime_1 = require("../nerves/runtime");
68
69
  const trust_gate_1 = require("./trust-gate");
69
70
  const pipeline_1 = require("./pipeline");
70
- const channel_1 = require("../mind/friends/channel");
71
- const session_lock_1 = require("./session-lock");
72
- const update_hooks_1 = require("../heart/daemon/update-hooks");
71
+ const session_transaction_1 = require("../mind/session-transaction");
72
+ const update_hooks_1 = require("../heart/versioning/update-hooks");
73
73
  const bundle_meta_1 = require("../heart/daemon/hooks/bundle-meta");
74
+ const agent_config_v2_1 = require("../heart/daemon/hooks/agent-config-v2");
74
75
  const bundle_manifest_1 = require("../mind/bundle-manifest");
75
76
  const cli_layout_1 = require("./cli-layout");
77
+ const image_paste_1 = require("./cli/image-paste");
78
+ const spinner_imperative_1 = require("./cli/spinner-imperative");
79
+ const tool_display_1 = require("./cli/tool-display");
76
80
  var cli_layout_2 = require("./cli-layout");
77
81
  Object.defineProperty(exports, "formatEchoedInputSummary", { enumerable: true, get: function () { return cli_layout_2.formatEchoedInputSummary; } });
78
82
  Object.defineProperty(exports, "wrapCliText", { enumerable: true, get: function () { return cli_layout_2.wrapCliText; } });
@@ -80,13 +84,13 @@ Object.defineProperty(exports, "StreamingWordWrapper", { enumerable: true, get:
80
84
  /**
81
85
  * Format pending messages as content-prefix strings for injection into
82
86
  * the next user message. Self-messages (from === agentName) become
83
- * `[inner thought: {content}]`, inter-agent messages become
87
+ * `[private-runtime note: {content}]`, inter-agent messages become
84
88
  * `[message from {name}: {content}]`.
85
89
  */
86
90
  function formatPendingPrefix(messages, agentName) {
87
91
  return messages
88
92
  .map((msg) => msg.from === agentName
89
- ? `[inner thought: ${msg.content}]`
93
+ ? `[private-runtime note: ${msg.content}]`
90
94
  : `[message from ${msg.from}: ${msg.content}]`)
91
95
  .join("\n");
92
96
  }
@@ -94,6 +98,35 @@ function getCliContinuityIngressTexts(input) {
94
98
  const trimmed = input.trim();
95
99
  return trimmed ? [trimmed] : [];
96
100
  }
101
+ /* v8 ignore start -- cosmetic time formatting @preserve */
102
+ function formatTimeAgo(date) {
103
+ const seconds = Math.floor((Date.now() - date.getTime()) / 1000);
104
+ if (seconds < 60)
105
+ return "just now";
106
+ const minutes = Math.floor(seconds / 60);
107
+ if (minutes < 60)
108
+ return `${minutes}m ago`;
109
+ const hours = Math.floor(minutes / 60);
110
+ if (hours < 24)
111
+ return `${hours}h ago`;
112
+ const days = Math.floor(hours / 24);
113
+ return `${days}d ago`;
114
+ }
115
+ const CLI_PROMPT = "\x1b[36m) \x1b[0m";
116
+ function writeCliAsyncAssistantMessage(rl, message, stdout = process.stdout) {
117
+ const rlInt = rl;
118
+ const currentLine = rlInt.line ?? "";
119
+ const currentCursor = rlInt.cursor ?? currentLine.length;
120
+ stdout.write("\r\x1b[K");
121
+ stdout.write(`${renderMarkdown(message)}\n`);
122
+ stdout.write(CLI_PROMPT);
123
+ if (!currentLine)
124
+ return;
125
+ stdout.write(currentLine);
126
+ if (currentCursor < currentLine.length) {
127
+ readline.cursorTo(process.stdout, 2 + currentCursor);
128
+ }
129
+ }
97
130
  // Module-level active spinner for log coordination.
98
131
  // The terminal log sink calls these to avoid interleaving with spinner output.
99
132
  let _activeSpinner = null;
@@ -102,82 +135,9 @@ function pauseActiveSpinner() { _activeSpinner?.pause(); }
102
135
  function resumeActiveSpinner() { _activeSpinner?.resume(); }
103
136
  /* v8 ignore stop */
104
137
  function setActiveSpinner(s) { _activeSpinner = s; }
105
- // spinner that only touches stderr, cleans up after itself
106
- // exported for direct testability (stop-without-start branch)
107
- class Spinner {
108
- frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
109
- i = 0;
110
- iv = null;
111
- piv = null;
112
- msg = "";
113
- phrases = null;
114
- lastPhrase = "";
115
- stopped = false;
116
- constructor(m = "working", phrases) {
117
- this.msg = m;
118
- if (phrases && phrases.length > 0)
119
- this.phrases = phrases;
120
- }
121
- start() {
122
- this.stopped = false;
123
- process.stderr.write("\r\x1b[K");
124
- this.spin();
125
- this.iv = setInterval(() => this.spin(), 80);
126
- if (this.phrases) {
127
- this.piv = setInterval(() => this.rotatePhrase(), 1500);
128
- }
129
- }
130
- spin() {
131
- // Guard: clearInterval can't prevent already-dequeued callbacks
132
- /* v8 ignore next -- race guard: timer callback fires after stop() @preserve */
133
- if (this.stopped)
134
- return;
135
- process.stderr.write(`\r\x1b[K${this.frames[this.i]} ${this.msg}... `);
136
- this.i = (this.i + 1) % this.frames.length;
137
- }
138
- rotatePhrase() {
139
- /* v8 ignore next -- race guard: timer callback fires after stop() @preserve */
140
- if (this.stopped)
141
- return;
142
- const next = (0, phrases_1.pickPhrase)(this.phrases, this.lastPhrase);
143
- this.lastPhrase = next;
144
- this.msg = next;
145
- }
146
- /* v8 ignore start -- pause/resume: exercised at runtime via log sink coordination @preserve */
147
- /** Clear the spinner line temporarily so other output can print cleanly. */
148
- pause() {
149
- if (this.stopped)
150
- return;
151
- process.stderr.write("\r\x1b[K");
152
- }
153
- /** Restore the spinner line after a pause. */
154
- resume() {
155
- if (this.stopped)
156
- return;
157
- this.spin();
158
- }
159
- /* v8 ignore stop */
160
- stop(ok) {
161
- this.stopped = true;
162
- if (this.iv) {
163
- clearInterval(this.iv);
164
- this.iv = null;
165
- }
166
- if (this.piv) {
167
- clearInterval(this.piv);
168
- this.piv = null;
169
- }
170
- process.stderr.write("\r\x1b[K");
171
- /* v8 ignore next -- ok parameter currently unused by callers @preserve */
172
- if (ok)
173
- process.stderr.write(`\x1b[32m\u2713\x1b[0m ${ok}\n`);
174
- }
175
- fail(msg) {
176
- this.stop();
177
- process.stderr.write(`\x1b[31m\u2717\x1b[0m ${msg}\n`);
178
- }
179
- }
180
- exports.Spinner = Spinner;
138
+ // Re-export ImperativeSpinner as Spinner for backward compatibility
139
+ var spinner_imperative_2 = require("./cli/spinner-imperative");
140
+ Object.defineProperty(exports, "Spinner", { enumerable: true, get: function () { return spinner_imperative_2.ImperativeSpinner; } });
181
141
  // Input controller: pauses readline during model/tool execution.
182
142
  // Does NOT touch raw mode — readline with terminal:true manages raw mode
183
143
  // internally. Touching it causes ^C to be echoed by the terminal driver.
@@ -351,6 +311,7 @@ function createCliCallbacks() {
351
311
  const streamer = new MarkdownStreamer();
352
312
  const wrapper = new cli_layout_1.StreamingWordWrapper();
353
313
  return {
314
+ settleOutputMode: "final_only",
354
315
  onModelStart: () => {
355
316
  currentSpinner?.stop();
356
317
  setSpinner(null);
@@ -360,20 +321,20 @@ function createCliCallbacks() {
360
321
  const phrases = (0, phrases_1.getPhrases)();
361
322
  const pool = hadToolRun ? phrases.followup : phrases.thinking;
362
323
  const first = (0, phrases_1.pickPhrase)(pool);
363
- setSpinner(new Spinner(first, pool));
324
+ setSpinner(new spinner_imperative_1.ImperativeSpinner(first, pool));
364
325
  currentSpinner.start();
365
326
  },
366
327
  onModelStreamStart: () => {
367
328
  // No-op: content callbacks (onTextChunk, onReasoningChunk) handle
368
329
  // stopping the spinner. onModelStreamStart fires too early and
369
- // doesn't fire at all for final_answer tool streaming.
330
+ // doesn't fire at all for settle tool streaming.
370
331
  },
371
332
  onClearText: () => {
372
333
  streamer.reset();
373
334
  wrapper.reset();
374
335
  },
375
336
  onTextChunk: (text) => {
376
- // Stop spinner if still running — final_answer streaming and Anthropic
337
+ // Stop spinner if still running — settle streaming and Anthropic
377
338
  // tool-only responses bypass onModelStreamStart, so the spinner would
378
339
  // otherwise keep running (and its \r writes overwrite response text).
379
340
  if (currentSpinner) {
@@ -390,11 +351,20 @@ function createCliCallbacks() {
390
351
  /* v8 ignore stop */
391
352
  textDirty = text.length > 0 && !text.endsWith("\n");
392
353
  },
354
+ flushNow: () => {
355
+ // CLI flushes immediately on each onTextChunk; nothing buffered to push.
356
+ },
393
357
  onReasoningChunk: (_text) => {
394
358
  // Keep reasoning private in the CLI surface. The spinner continues to
395
359
  // represent active thinking until actual tool or answer output arrives.
396
360
  },
397
361
  onToolStart: (_name, _args) => {
362
+ // speak is flow-control: its visible output is the message itself (delivered
363
+ // via onTextChunk/flushNow). Do NOT start a tool spinner — that would write
364
+ // a "running speak..." phrase to stderr right before the actual message
365
+ // arrives, which is the visual churn the user explicitly does not want.
366
+ if (_name === "speak")
367
+ return;
398
368
  // Stop the model-start spinner: when the model returns only tool calls
399
369
  // (no content/reasoning), onModelStreamStart never fires, so the old
400
370
  // spinner's intervals would leak.
@@ -407,16 +377,14 @@ function createCliCallbacks() {
407
377
  }
408
378
  const toolPhrases = (0, phrases_1.getPhrases)().tool;
409
379
  const first = (0, phrases_1.pickPhrase)(toolPhrases);
410
- setSpinner(new Spinner(first, toolPhrases));
380
+ setSpinner(new spinner_imperative_1.ImperativeSpinner(first, toolPhrases));
411
381
  currentSpinner.start();
412
382
  hadToolRun = true;
413
383
  },
414
384
  onToolEnd: (name, argSummary, success) => {
415
385
  currentSpinner?.stop();
416
386
  setSpinner(null);
417
- const msg = (0, format_1.formatToolResult)(name, argSummary, success);
418
- const color = success ? "\x1b[32m" : "\x1b[31m";
419
- process.stderr.write(`${color}${msg}\x1b[0m\n`);
387
+ (0, tool_display_1.writeToolEnd)(name, argSummary, success);
420
388
  },
421
389
  onError: (error, severity) => {
422
390
  if (severity === "transient") {
@@ -492,29 +460,243 @@ async function* createDebouncedLines(source, debounceMs) {
492
460
  yield lines.join("\n");
493
461
  }
494
462
  }
463
+ /**
464
+ * Async queue that bridges push-based Ink input to pull-based async iteration.
465
+ * Input from Ink's onSubmit callback is pushed; the business logic loop awaits via for-await.
466
+ */
467
+ class InputQueue {
468
+ queue = [];
469
+ resolve = null;
470
+ done = false;
471
+ push(input) {
472
+ if (this.done)
473
+ return;
474
+ if (this.resolve) {
475
+ const r = this.resolve;
476
+ this.resolve = null;
477
+ r({ value: input, done: false });
478
+ }
479
+ else {
480
+ this.queue.push(input);
481
+ }
482
+ }
483
+ close() {
484
+ this.done = true;
485
+ if (this.resolve) {
486
+ const r = this.resolve;
487
+ this.resolve = null;
488
+ r({ value: undefined, done: true });
489
+ }
490
+ }
491
+ /** Drain all buffered items, leaving any pending async awaiter untouched. */
492
+ drainAll() {
493
+ const items = [...this.queue];
494
+ this.queue = [];
495
+ return items;
496
+ }
497
+ [Symbol.asyncIterator]() {
498
+ return {
499
+ next: () => {
500
+ if (this.queue.length > 0) {
501
+ return Promise.resolve({ value: this.queue.shift(), done: false });
502
+ }
503
+ if (this.done) {
504
+ return Promise.resolve({ value: undefined, done: true });
505
+ }
506
+ return new Promise((resolve) => {
507
+ this.resolve = resolve;
508
+ });
509
+ },
510
+ };
511
+ }
512
+ }
513
+ exports.InputQueue = InputQueue;
495
514
  async function runCliSession(options) {
496
515
  /* v8 ignore start -- integration: runCliSession is interactive, tested via E2E @preserve */
497
- const pasteDebounceMs = options.pasteDebounceMs ?? 50;
498
516
  const registry = (0, commands_1.createCommandRegistry)();
499
517
  if (!options.disableCommands) {
500
518
  (0, commands_1.registerDefaultCommands)(registry);
501
519
  }
502
520
  const messages = options.messages
503
- ?? [{ role: "system", content: await (0, prompt_1.buildSystem)("cli") }];
504
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: true });
505
- const ctrl = new InputController(rl);
521
+ ?? [{ role: "system", content: (0, prompt_1.flattenSystemPrompt)(await (0, prompt_1.buildSystem)("cli")) }];
522
+ // ─── Rendering: TUI (Ink + Static) for TTY, imperative for tests/pipes ───
523
+ const useTui = !options._testInputSource && process.stdin.isTTY === true;
506
524
  let currentAbort = null;
507
- const history = [];
508
525
  let closed = false;
509
- rl.on("close", () => { closed = true; });
510
- if (options.banner !== false) {
511
- const bannerText = typeof options.banner === "string"
512
- ? options.banner
513
- : `${options.agentName} (type /commands for help)`;
514
- // eslint-disable-next-line no-console -- terminal UX: startup banner
515
- console.log(`\n${bannerText}\n`);
526
+ // eslint-disable-next-line prefer-const -- set by onImageMap callback during input
527
+ let pendingImages = null;
528
+ let cliCallbacks;
529
+ let tuiStore = null;
530
+ let inkRef = null;
531
+ const inputQueue = useTui ? new InputQueue() : null;
532
+ let rl = null;
533
+ if (useTui) {
534
+ try {
535
+ const [ink, React, tuiMod, storeMod] = await Promise.all([
536
+ Promise.resolve().then(() => __importStar(require("ink"))),
537
+ Promise.resolve().then(() => __importStar(require("react"))),
538
+ Promise.resolve().then(() => __importStar(require("./cli/ouro-tui"))),
539
+ Promise.resolve().then(() => __importStar(require("./cli/tui-store"))),
540
+ ]);
541
+ const { OuroTui } = tuiMod;
542
+ const { TuiStore, createTuiCallbacks } = storeMod;
543
+ tuiStore = new TuiStore();
544
+ cliCallbacks = createTuiCallbacks(tuiStore);
545
+ // Seed input history from previous session (for up/down arrows) — NOT display
546
+ const prevUserMsgs = messages
547
+ .filter((msg) => msg.role === "user" && typeof msg.content === "string")
548
+ .map(msg => msg.content);
549
+ tuiStore.seedHistory(prevUserMsgs);
550
+ // Show session resume context: last 2 exchanges as normal messages
551
+ if (messages.length > 1) {
552
+ const userAssistantMsgs = messages.filter((m) => (m.role === "user" || m.role === "assistant") && typeof m.content === "string" && m.content.trim().length > 0);
553
+ // Extract last 2 exchanges (up to 4 messages)
554
+ const lastExchanges = [];
555
+ for (let i = userAssistantMsgs.length - 1; i >= 0 && lastExchanges.length < 4; i--) {
556
+ lastExchanges.unshift({ role: userAssistantMsgs[i].role, content: userAssistantMsgs[i].content });
557
+ }
558
+ tuiStore.addResumeMessages(lastExchanges);
559
+ }
560
+ // Compute resumeInfo for header banner
561
+ const resumeInfo = messages.length > 1
562
+ ? {
563
+ messageCount: messages.filter(m => m.role === "user" || m.role === "assistant").length,
564
+ timeAgo: options.lastActivityAt ? formatTimeAgo(new Date(options.lastActivityAt)) : "unknown",
565
+ }
566
+ : undefined;
567
+ // Ctrl-C state machine (Claude Code behavior):
568
+ // During generation: abort current request
569
+ // Idle with text: clear input
570
+ // Idle empty (first): warn
571
+ // Idle empty (second): exit
572
+ let ctrlCWarned = false;
573
+ let ctrlCTimer = null;
574
+ const handleCtrlC = (hasInput) => {
575
+ if (currentAbort) {
576
+ currentAbort.abort();
577
+ ctrlCWarned = false;
578
+ return "abort";
579
+ }
580
+ if (hasInput) {
581
+ ctrlCWarned = false;
582
+ return "clear";
583
+ }
584
+ if (ctrlCWarned) {
585
+ ctrlCWarned = false;
586
+ if (ctrlCTimer) {
587
+ clearTimeout(ctrlCTimer);
588
+ ctrlCTimer = null;
589
+ }
590
+ closed = true;
591
+ inputQueue.close();
592
+ return "exit";
593
+ }
594
+ ctrlCWarned = true;
595
+ // Reset after 2 seconds — must press twice within window
596
+ ctrlCTimer = setTimeout(() => { ctrlCWarned = false; }, 2000);
597
+ return "warn";
598
+ };
599
+ // TUI root: subscribes to store, passes props to OuroTui
600
+ // Elapsed timer is local React state (no store.notify overhead)
601
+ const storeRef = tuiStore;
602
+ function TuiRoot() {
603
+ const [, forceUpdate] = React.useState(0);
604
+ const [elapsed, setElapsed] = React.useState(0);
605
+ React.useEffect(() => storeRef.subscribe(() => {
606
+ forceUpdate((n) => n + 1);
607
+ // Reset ctrlC warning on any state change (new turn, etc.)
608
+ }), []);
609
+ React.useEffect(() => {
610
+ const iv = setInterval(() => setElapsed(storeRef.getElapsed()), 1000);
611
+ return () => clearInterval(iv);
612
+ }, []);
613
+ return React.createElement(OuroTui, {
614
+ agentName: options.agentName,
615
+ model: (0, identity_1.loadAgentConfig)().humanFacing?.model ?? "",
616
+ completedMessages: storeRef.completedMessages,
617
+ inputHistory: storeRef.inputHistory,
618
+ queuedInputs: storeRef.queuedInputs,
619
+ live: storeRef.live,
620
+ elapsedSeconds: elapsed,
621
+ contextPercent: 0,
622
+ onSubmit: (text) => { ctrlCWarned = false; inputQueue.push(text); storeRef.enqueueInput(text); },
623
+ onCtrlC: handleCtrlC,
624
+ onPopQueue: () => { const items = storeRef.popAllQueuedForEditing(); inputQueue.drainAll(); return items; },
625
+ headerShown: storeRef.headerShown,
626
+ cwd: process.cwd().replace(process.env.HOME ?? "", "~"),
627
+ resumeInfo,
628
+ onImageMap: (images) => { pendingImages = images; },
629
+ onHistoryAdd: (text) => { storeRef.addToHistoryOnly(text); },
630
+ });
631
+ }
632
+ inkRef = ink.render(React.createElement(TuiRoot), { exitOnCtrlC: false, patchConsole: false });
633
+ }
634
+ catch (err) {
635
+ // Ink failed to load (CJS compat, missing deps, etc.) — fall through to imperative
636
+ (0, runtime_1.emitNervesEvent)({
637
+ component: "senses",
638
+ event: "senses.tui_fallback",
639
+ message: `TUI failed to load, falling back to imperative: ${err instanceof Error ? err.message : String(err)}`,
640
+ meta: {},
641
+ });
642
+ }
643
+ }
644
+ // Fallback to imperative callbacks if TUI didn't initialize
645
+ if (!tuiStore) {
646
+ cliCallbacks = createCliCallbacks();
647
+ if (options.banner !== false) {
648
+ const bannerText = typeof options.banner === "string"
649
+ ? options.banner
650
+ : `${options.agentName} (type /commands for help)`;
651
+ // eslint-disable-next-line no-console -- terminal UX: startup banner
652
+ console.log(`\n${bannerText}\n`);
653
+ }
516
654
  }
517
- const cliCallbacks = createCliCallbacks();
655
+ // Display helpers: route to TUI store or imperative stderr/stdout
656
+ const display = {
657
+ error: (msg) => {
658
+ if (tuiStore)
659
+ tuiStore.setError(msg);
660
+ else
661
+ process.stderr.write(`\x1b[31m${msg}\x1b[0m\n`);
662
+ },
663
+ warn: (msg) => {
664
+ if (tuiStore)
665
+ tuiStore.setError(msg); // TUI shows warnings as errors (amber color handled in component)
666
+ else
667
+ process.stderr.write(`\x1b[33m${msg}\x1b[0m\n`);
668
+ },
669
+ text: (msg) => {
670
+ if (tuiStore)
671
+ tuiStore.appendText(msg);
672
+ else
673
+ process.stdout.write(`${msg}\n`);
674
+ },
675
+ suppressInput: () => {
676
+ if (tuiStore)
677
+ tuiStore.suppressInput();
678
+ },
679
+ restoreInput: () => {
680
+ if (tuiStore)
681
+ tuiStore.restoreInput();
682
+ },
683
+ };
684
+ const effectiveToolContext = {
685
+ signin: options.toolContext?.signin ?? (async () => undefined),
686
+ ...options.toolContext,
687
+ codingFeedback: {
688
+ send: async (message) => {
689
+ const assistantMessage = {
690
+ role: "assistant",
691
+ content: message,
692
+ };
693
+ messages.push(assistantMessage);
694
+ await options.onAsyncAssistantMessage?.(messages, assistantMessage);
695
+ display.text(message);
696
+ await options.toolContext?.codingFeedback?.send(message);
697
+ },
698
+ },
699
+ };
518
700
  // exitOnToolCall machinery: wrap execTool to detect target tool
519
701
  let exitToolResult;
520
702
  let exitToolFired = false;
@@ -525,8 +707,6 @@ async function runCliSession(options) {
525
707
  if (name === options.exitOnToolCall) {
526
708
  exitToolResult = result;
527
709
  exitToolFired = true;
528
- // Abort immediately so the model doesn't generate more output
529
- // (e.g. reasoning about calling final_answer after complete_adoption)
530
710
  currentAbort?.abort();
531
711
  }
532
712
  return result;
@@ -534,28 +714,6 @@ async function runCliSession(options) {
534
714
  : resolvedExecTool;
535
715
  // Resolve toolChoiceRequired: use explicit option if set, else fall back to toggle
536
716
  const getEffectiveToolChoiceRequired = () => options.toolChoiceRequired !== undefined ? options.toolChoiceRequired : (0, commands_1.getToolChoiceRequired)();
537
- // Ctrl-C at the input prompt: clear line or warn/exit
538
- rl.on("SIGINT", () => {
539
- const rlInt = rl;
540
- const currentLine = rlInt.line || "";
541
- const result = handleSigint(rl, currentLine);
542
- if (result === "clear") {
543
- rlInt.line = "";
544
- rlInt.cursor = 0;
545
- process.stdout.write("\r\x1b[K\x1b[36m> \x1b[0m");
546
- }
547
- else if (result === "warn") {
548
- rlInt.line = "";
549
- rlInt.cursor = 0;
550
- process.stdout.write("\r\x1b[K");
551
- process.stderr.write("press Ctrl-C again to exit\n");
552
- process.stdout.write("\x1b[36m> \x1b[0m");
553
- }
554
- else {
555
- rl.close();
556
- }
557
- });
558
- const debouncedLines = (source) => createDebouncedLines(source, pasteDebounceMs);
559
717
  (0, runtime_1.emitNervesEvent)({
560
718
  component: "senses",
561
719
  event: "senses.cli_session_start",
@@ -568,7 +726,7 @@ async function runCliSession(options) {
568
726
  if (options.autoFirstTurn && messages.length > 0 && messages[messages.length - 1]?.role === "user") {
569
727
  currentAbort = new AbortController();
570
728
  const traceId = (0, nerves_1.createTraceId)();
571
- ctrl.suppress(() => currentAbort.abort());
729
+ display.suppressInput();
572
730
  let result;
573
731
  try {
574
732
  result = await (0, core_1.runAgent)(messages, cliCallbacks, options.skipSystemPromptRefresh ? undefined : "cli", currentAbort.signal, {
@@ -576,142 +734,189 @@ async function runCliSession(options) {
576
734
  traceId,
577
735
  tools: options.tools,
578
736
  execTool: wrappedExecTool,
579
- toolContext: options.toolContext,
737
+ toolContext: effectiveToolContext,
580
738
  });
581
739
  }
582
740
  catch (err) {
583
- // AbortError (Ctrl-C) -- silently continue to prompt
584
- // All other errors: show the user what happened
585
741
  if (!(err instanceof DOMException && err.name === "AbortError")) {
586
- process.stderr.write(`\x1b[31m${err instanceof Error ? err.message : String(err)}\x1b[0m\n`);
742
+ display.error(err instanceof Error ? err.message : String(err));
587
743
  }
588
744
  }
589
745
  cliCallbacks.flushMarkdown();
590
- ctrl.restore();
746
+ display.restoreInput();
591
747
  currentAbort = null;
592
748
  if (exitToolFired) {
593
749
  exitReason = "tool_exit";
594
- rl.close();
750
+ closed = true;
595
751
  }
596
752
  else {
597
753
  const lastMsg = messages[messages.length - 1];
598
754
  if (lastMsg?.role === "assistant" && !(typeof lastMsg.content === "string" ? lastMsg.content : "").trim()) {
599
- process.stderr.write("\x1b[33m(empty response)\x1b[0m\n");
755
+ display.warn("(empty response)");
600
756
  }
601
- process.stdout.write("\n\n");
602
757
  if (options.onTurnEnd) {
603
758
  await options.onTurnEnd(messages, result ?? { usage: undefined });
604
759
  }
605
760
  }
606
761
  }
607
- if (!exitToolFired) {
608
- process.stdout.write("\x1b[36m> \x1b[0m");
609
- }
610
762
  try {
611
- for await (const input of debouncedLines(rl)) {
763
+ // Input source: TUI queue for Ink, test source for tests, readline fallback
764
+ let inputSource;
765
+ let inputCtrl = null;
766
+ if (tuiStore && inputQueue) {
767
+ // TUI path: input comes from Ink's onSubmit → InputQueue
768
+ inputSource = inputQueue;
769
+ }
770
+ else if (options._testInputSource) {
771
+ inputSource = options._testInputSource;
772
+ }
773
+ else {
774
+ // Imperative fallback: readline
775
+ const isTTY = process.stdin.isTTY === true;
776
+ rl = readline.createInterface({ input: process.stdin, output: process.stdout, terminal: isTTY });
777
+ inputCtrl = new InputController(rl);
778
+ inputSource = rl;
779
+ process.stdout.write(CLI_PROMPT);
780
+ }
781
+ for await (const input of inputSource) {
612
782
  if (closed)
613
783
  break;
614
- if (!input.trim()) {
615
- process.stdout.write("\x1b[36m> \x1b[0m");
784
+ if (!input.trim())
616
785
  continue;
617
- }
786
+ // Remove from TUI queue display as the agent picks up this message
787
+ tuiStore?.dequeueInput(input);
618
788
  // Optional input gate (e.g. trust gate in main)
619
789
  if (options.onInput) {
620
790
  const gate = options.onInput(input);
621
791
  if (!gate.allowed) {
622
792
  if (gate.reply) {
623
- process.stdout.write(`${gate.reply}\n`);
793
+ display.text(gate.reply);
624
794
  }
625
795
  if (closed)
626
796
  break;
627
- process.stdout.write("\x1b[36m> \x1b[0m");
628
797
  continue;
629
798
  }
630
799
  }
631
- // Check for slash commands
632
- const parsed = (0, commands_1.parseSlashCommand)(input);
633
- if (parsed) {
634
- const dispatchResult = registry.dispatch(parsed.command, { channel: "cli" });
635
- if (dispatchResult.handled && dispatchResult.result) {
636
- if (dispatchResult.result.action === "exit") {
637
- break;
800
+ await options.onTurnStart?.(messages);
801
+ try {
802
+ // Check for slash commands (legacy path only — pipeline handles commands for runTurn path)
803
+ if (!options.runTurn) {
804
+ const parsed = (0, commands_1.parseSlashCommand)(input);
805
+ if (parsed) {
806
+ const dispatchResult = registry.dispatch(parsed.command, { channel: "cli" });
807
+ if (dispatchResult.handled && dispatchResult.result) {
808
+ if (dispatchResult.result.action === "exit") {
809
+ break;
810
+ }
811
+ else if (dispatchResult.result.action === "response") {
812
+ display.text(dispatchResult.result.message || "");
813
+ continue;
814
+ }
815
+ }
638
816
  }
639
- else if (dispatchResult.result.action === "new") {
640
- messages.length = 0;
641
- messages.push({ role: "system", content: await (0, prompt_1.buildSystem)("cli") });
642
- await options.onNewSession?.();
643
- // eslint-disable-next-line no-console -- terminal UX: session cleared
644
- console.log("session cleared");
645
- process.stdout.write("\x1b[36m> \x1b[0m");
646
- continue;
817
+ }
818
+ // Track user message in TUI for display
819
+ if (tuiStore)
820
+ tuiStore.addUserMessage(input);
821
+ currentAbort = new AbortController();
822
+ if (tuiStore)
823
+ tuiStore.suppressInput();
824
+ inputCtrl?.suppress(() => { currentAbort?.abort(); });
825
+ // Resolve pending image content before the turn executes
826
+ let contentParts = null;
827
+ const currentImages = pendingImages;
828
+ if (currentImages && currentImages.size > 0) {
829
+ contentParts = await (0, image_paste_1.resolveImageContent)(input, currentImages);
830
+ pendingImages = null;
831
+ }
832
+ let result;
833
+ try {
834
+ if (options.runTurn) {
835
+ // Pipeline-based turn: the runTurn callback handles user message assembly,
836
+ // pending drain, trust gate, runAgent, postTurn, and token accumulation.
837
+ result = await options.runTurn(messages, input, cliCallbacks, currentAbort.signal, effectiveToolContext, contentParts ?? undefined);
838
+ // Handle pipeline-intercepted commands with loop-control side effects
839
+ if (result?.turnOutcome === "command") {
840
+ if (result.commandAction === "exit") {
841
+ break;
842
+ }
843
+ // For "response" commands: the pipeline already emitted the response via onTextChunk
844
+ cliCallbacks.flushMarkdown();
845
+ continue;
846
+ }
647
847
  }
648
- else if (dispatchResult.result.action === "response") {
649
- // eslint-disable-next-line no-console -- terminal UX: command dispatch result
650
- console.log(dispatchResult.result.message || "");
651
- process.stdout.write("\x1b[36m> \x1b[0m");
652
- continue;
848
+ else {
849
+ // Legacy path: inline runAgent (used by serpent guide and tests)
850
+ const prefix = options.getContentPrefix?.();
851
+ const userContent = contentParts
852
+ ? contentParts
853
+ : (prefix ? `${prefix}\n\n${input}` : input);
854
+ const userMsg = { role: "user", content: userContent };
855
+ (0, session_events_1.stampIngressTime)(userMsg);
856
+ messages.push(userMsg);
857
+ const traceId = (0, nerves_1.createTraceId)();
858
+ result = await (0, core_1.runAgent)(messages, cliCallbacks, options.skipSystemPromptRefresh ? undefined : "cli", currentAbort.signal, {
859
+ toolChoiceRequired: getEffectiveToolChoiceRequired(),
860
+ traceId,
861
+ tools: options.tools,
862
+ execTool: wrappedExecTool,
863
+ toolContext: effectiveToolContext,
864
+ });
653
865
  }
654
866
  }
655
- }
656
- // Re-style the echoed input lines without leaving wrapped paste remnants behind.
657
- const cols = process.stdout.columns || 80;
658
- process.stdout.write((0, cli_layout_1.formatEchoedInputSummary)(input, cols));
659
- addHistory(history, input);
660
- currentAbort = new AbortController();
661
- ctrl.suppress(() => currentAbort.abort());
662
- let result;
663
- try {
664
- if (options.runTurn) {
665
- // Pipeline-based turn: the runTurn callback handles user message assembly,
666
- // pending drain, trust gate, runAgent, postTurn, and token accumulation.
667
- result = await options.runTurn(messages, input, cliCallbacks, currentAbort.signal);
867
+ catch (err) {
868
+ if (!(err instanceof DOMException && err.name === "AbortError")) {
869
+ display.error(err instanceof Error ? err.message : String(err));
870
+ }
668
871
  }
669
- else {
670
- // Legacy path: inline runAgent (used by adoption specialist and tests)
671
- const prefix = options.getContentPrefix?.();
672
- messages.push({ role: "user", content: prefix ? `${prefix}\n\n${input}` : input });
673
- const traceId = (0, nerves_1.createTraceId)();
674
- result = await (0, core_1.runAgent)(messages, cliCallbacks, options.skipSystemPromptRefresh ? undefined : "cli", currentAbort.signal, {
675
- toolChoiceRequired: getEffectiveToolChoiceRequired(),
676
- traceId,
677
- tools: options.tools,
678
- execTool: wrappedExecTool,
679
- toolContext: options.toolContext,
680
- });
872
+ cliCallbacks.flushMarkdown();
873
+ if (!tuiStore)
874
+ process.stdout.write("\n"); // ensure response ends with newline before prompt (imperative only)
875
+ if (tuiStore)
876
+ tuiStore.restoreInput();
877
+ inputCtrl?.restore();
878
+ currentAbort = null;
879
+ // Check if exit tool was fired during this turn
880
+ if (exitToolFired) {
881
+ exitReason = "tool_exit";
882
+ break;
681
883
  }
682
- }
683
- catch (err) {
684
- // AbortError (Ctrl-C) -- silently return to prompt
685
- // All other errors: show the user what happened
686
- if (!(err instanceof DOMException && err.name === "AbortError")) {
687
- process.stderr.write(`\x1b[31m${err instanceof Error ? err.message : String(err)}\x1b[0m\n`);
884
+ // Safety net: never silently swallow an empty response
885
+ const lastMsg = messages[messages.length - 1];
886
+ if (lastMsg?.role === "assistant" && !(typeof lastMsg.content === "string" ? lastMsg.content : "").trim()) {
887
+ display.warn("(empty response)");
888
+ }
889
+ // Post-turn hook (session persistence, pending drain, prompt refresh, etc.)
890
+ if (options.onTurnEnd) {
891
+ await options.onTurnEnd(messages, result ?? { usage: undefined });
688
892
  }
689
893
  }
690
- cliCallbacks.flushMarkdown();
691
- ctrl.restore();
692
- currentAbort = null;
693
- // Check if exit tool was fired during this turn
694
- if (exitToolFired) {
695
- exitReason = "tool_exit";
696
- break;
697
- }
698
- // Safety net: never silently swallow an empty response
699
- const lastMsg = messages[messages.length - 1];
700
- if (lastMsg?.role === "assistant" && !(typeof lastMsg.content === "string" ? lastMsg.content : "").trim()) {
701
- process.stderr.write("\x1b[33m(empty response)\x1b[0m\n");
702
- }
703
- process.stdout.write("\n\n");
704
- // Post-turn hook (session persistence, pending drain, prompt refresh, etc.)
705
- if (options.onTurnEnd) {
706
- await options.onTurnEnd(messages, result ?? { usage: undefined });
894
+ finally {
895
+ await options.onTurnFinish?.();
707
896
  }
708
897
  if (closed)
709
898
  break;
710
- process.stdout.write("\x1b[36m> \x1b[0m");
711
899
  }
712
900
  }
713
901
  finally {
714
- rl.close();
902
+ rl?.close();
903
+ if (inkRef) {
904
+ // Suppress React "state update on unmounted component" warnings during cleanup.
905
+ // Ink's useInput hook fires after unmount — this is harmless but noisy.
906
+ // This also covers the SerpentGuide exitOnToolCall path, which aborts the
907
+ // current request and breaks out of the loop into this finally block.
908
+ // eslint-disable-next-line no-console -- intentional console.warn/error override for cleanup
909
+ const origWarn = console.warn;
910
+ const origError = console.error; // eslint-disable-line no-console
911
+ // eslint-disable-next-line no-console -- suppress React unmount warnings
912
+ console.warn = (...args) => { if (typeof args[0] === "string" && args[0].includes("Can't perform a React state update"))
913
+ return; origWarn.apply(console, args); };
914
+ // eslint-disable-next-line no-console -- suppress React unmount warnings
915
+ console.error = (...args) => { if (typeof args[0] === "string" && args[0].includes("Can't perform a React state update"))
916
+ return; origError.apply(console, args); };
917
+ inkRef.unmount();
918
+ setTimeout(() => { console.warn = origWarn; console.error = origError; }, 100); // eslint-disable-line no-console
919
+ }
715
920
  if (options.banner !== false) {
716
921
  // eslint-disable-next-line no-console -- terminal UX: goodbye
717
922
  console.log("bye");
@@ -724,20 +929,35 @@ async function main(agentName, options) {
724
929
  if (agentName)
725
930
  (0, identity_1.setAgentName)(agentName);
726
931
  const pasteDebounceMs = options?.pasteDebounceMs ?? 50;
932
+ // Safety net: process-level SIGINT handler ensures Ctrl+C always exits,
933
+ // even when Ink's event loop is blocked by expensive renders.
934
+ /* v8 ignore start -- process signal handler @preserve */
935
+ let sigintCount = 0;
936
+ const sigintHandler = () => {
937
+ sigintCount++;
938
+ if (sigintCount >= 2)
939
+ process.exit(1);
940
+ };
941
+ if (!options?._testInputSource) {
942
+ process.on("SIGINT", sigintHandler);
943
+ }
944
+ /* v8 ignore stop */
727
945
  // Register spinner hooks so log output clears the spinner before printing
728
946
  (0, nerves_1.registerSpinnerHooks)(pauseActiveSpinner, resumeActiveSpinner);
729
947
  // Fallback: apply pending updates for daemon-less direct CLI usage
730
948
  (0, update_hooks_1.registerUpdateHook)(bundle_meta_1.bundleMetaHook);
949
+ (0, update_hooks_1.registerUpdateHook)(agent_config_v2_1.agentConfigV2Hook);
731
950
  await (0, update_hooks_1.applyPendingUpdates)((0, identity_1.getAgentBundlesRoot)(), (0, bundle_manifest_1.getPackageVersion)());
732
951
  // Fail fast if provider is misconfigured (triggers human-readable error + exit)
733
- (0, core_1.getProvider)();
952
+ (0, core_1.getProvider)("human");
734
953
  // Resolve context kernel (identity + channel) for CLI
735
- const friendsPath = path.join((0, identity_1.getAgentRoot)(), "friends");
736
- const friendStore = new store_file_1.FileFriendStore(friendsPath);
954
+ const currentAgentName = (0, identity_1.getAgentName)();
955
+ const owner = { agentName: currentAgentName, agentRoot: (0, identity_1.getAgentRoot)(currentAgentName) };
956
+ const friendsPath = path.join(owner.agentRoot, "friends");
957
+ const friendStore = new friends_1.FileFriendStore(friendsPath);
737
958
  const username = os.userInfo().username;
738
- const hostname = os.hostname();
739
- const localExternalId = `${username}@${hostname}`;
740
- const resolver = new resolver_1.FriendResolver(friendStore, {
959
+ const localExternalId = username;
960
+ const resolver = new friends_1.FriendResolver(friendStore, {
741
961
  provider: "local",
742
962
  externalId: localExternalId,
743
963
  displayName: username,
@@ -751,90 +971,167 @@ async function main(agentName, options) {
751
971
  sinks: agentConfig.logging?.sinks,
752
972
  });
753
973
  const sessPath = (0, config_1.sessionPath)(friendId, "cli", "session");
754
- let sessionLock = null;
755
- try {
756
- sessionLock = (0, session_lock_1.acquireSessionLock)(`${sessPath}.lock`, (0, identity_1.getAgentName)());
757
- }
758
- catch (error) {
759
- /* v8 ignore start -- integration: main() is interactive, lock tested in session-lock.test.ts @preserve */
760
- if (error instanceof session_lock_1.SessionLockError) {
761
- process.stderr.write(`${error.message}\n`);
762
- return;
763
- }
764
- throw error;
765
- /* v8 ignore stop */
766
- }
767
- // Load existing session or start fresh
768
- const existing = (0, context_1.loadSession)(sessPath);
769
- let sessionState = existing?.state;
770
- const mcpManager = await (0, mcp_manager_1.getSharedMcpManager)() ?? undefined;
771
- const sessionMessages = existing?.messages && existing.messages.length > 0
772
- ? existing.messages
773
- : [{ role: "system", content: await (0, prompt_1.buildSystem)("cli", { mcpManager }, resolvedContext) }];
974
+ const acquireTurnLease = options?._acquireSessionTurnLease ?? session_transaction_1.acquireSessionTurnLease;
975
+ let sessionTurnLease = null;
976
+ let pendingPersists = [];
977
+ let existing = null;
978
+ let sessionState;
979
+ let sessionEvents = [];
980
+ const sessionMessages = [
981
+ { role: "system", content: (0, prompt_1.flattenSystemPrompt)(await (0, prompt_1.buildSystem)("cli", {}, resolvedContext)) },
982
+ ];
774
983
  // Per-turn pipeline input: CLI capabilities and pending dir
775
- const cliCapabilities = (0, channel_1.getChannelCapabilities)("cli");
776
- const currentAgentName = (0, identity_1.getAgentName)();
984
+ const cliCapabilities = (0, friends_1.getChannelCapabilities)("cli");
777
985
  const pendingDir = (0, pending_1.getPendingDir)(currentAgentName, friendId, "cli", "session");
778
- const summarize = (0, core_1.createSummarize)();
779
- try {
780
- await runCliSession({
781
- agentName: currentAgentName,
782
- pasteDebounceMs,
783
- messages: sessionMessages,
784
- runTurn: async (messages, userInput, callbacks, signal) => {
785
- // Run the full per-turn pipeline: resolve -> gate -> session -> drain -> runAgent -> postTurn -> tokens
786
- // User message passed via input.messages so the pipeline can prepend pending messages to it.
787
- const result = await (0, pipeline_1.handleInboundTurn)({
788
- channel: "cli",
789
- sessionKey: "session",
790
- capabilities: cliCapabilities,
791
- messages: [{ role: "user", content: userInput }],
792
- continuityIngressTexts: getCliContinuityIngressTexts(userInput),
793
- callbacks,
794
- friendResolver: { resolve: () => Promise.resolve(resolvedContext) },
795
- sessionLoader: { loadOrCreate: () => Promise.resolve({ messages, sessionPath: sessPath, state: sessionState }) },
796
- pendingDir,
797
- friendStore,
798
- provider: "local",
799
- externalId: localExternalId,
800
- enforceTrustGate: trust_gate_1.enforceTrustGate,
801
- drainPending: pending_1.drainPending,
802
- drainDeferredReturns: (deferredFriendId) => (0, pending_1.drainDeferredReturns)(currentAgentName, deferredFriendId),
803
- runAgent: (msgs, cb, channel, sig, opts) => (0, core_1.runAgent)(msgs, cb, channel, sig, {
804
- ...opts,
805
- toolContext: {
806
- /* v8 ignore next -- default no-op signin; pipeline provides the real one @preserve */
807
- signin: async () => undefined,
808
- ...opts?.toolContext,
809
- summarize,
810
- },
986
+ const summarize = (0, core_1.createSummarize)("human");
987
+ const cliFailoverState = { pending: null };
988
+ await runCliSession({
989
+ agentName: currentAgentName,
990
+ pasteDebounceMs,
991
+ messages: sessionMessages,
992
+ lastActivityAt: sessionState?.lastFriendActivityAt,
993
+ _testInputSource: options?._testInputSource,
994
+ onTurnStart: async (messages) => {
995
+ sessionTurnLease = await acquireTurnLease(sessPath);
996
+ try {
997
+ pendingPersists = [];
998
+ existing = (0, context_1.loadSession)(sessPath);
999
+ sessionState = existing?.state;
1000
+ sessionEvents = existing?.events ?? [];
1001
+ const loadedMessages = existing?.messages && existing.messages.length > 0
1002
+ ? existing.messages
1003
+ : [{ role: "system", content: (0, prompt_1.flattenSystemPrompt)(await (0, prompt_1.buildSystem)("cli", {}, resolvedContext)) }];
1004
+ (0, core_1.repairOrphanedToolCalls)(loadedMessages);
1005
+ messages.splice(0, messages.length, ...loadedMessages);
1006
+ }
1007
+ catch (error) {
1008
+ const lease = sessionTurnLease;
1009
+ sessionTurnLease = null;
1010
+ await lease.release();
1011
+ throw error;
1012
+ }
1013
+ },
1014
+ onTurnFinish: async () => {
1015
+ try {
1016
+ await Promise.all(pendingPersists);
1017
+ }
1018
+ finally {
1019
+ const lease = sessionTurnLease;
1020
+ sessionTurnLease = null;
1021
+ await lease.release();
1022
+ }
1023
+ },
1024
+ onAsyncAssistantMessage: async (messages, _assistantMessage) => {
1025
+ const prepared = (0, context_1.postTurnTrim)(messages);
1026
+ const events = (0, context_1.postTurnPersist)(sessPath, prepared, undefined, sessionState, sessionTurnLease ?? undefined);
1027
+ /* v8 ignore next -- defensive: postTurnPersist always returns events in practice @preserve */
1028
+ sessionEvents = events.length > 0 ? events : sessionEvents;
1029
+ },
1030
+ runTurn: async (messages, userInput, callbacks, signal, toolContext, userContent) => {
1031
+ // Run the full per-turn pipeline: resolve -> gate -> session -> drain -> runAgent -> postTurn -> tokens
1032
+ // User message passed via input.messages so the pipeline can prepend pending messages to it.
1033
+ const failoverState = cliFailoverState;
1034
+ // Capture terminal errors instead of displaying immediately — the failover
1035
+ // message replaces the raw error if failover triggers successfully.
1036
+ let capturedTerminalError = null;
1037
+ /* v8 ignore start -- failover-aware callback wrapper: tested via pipeline integration @preserve */
1038
+ const failoverAwareCallbacks = {
1039
+ ...callbacks,
1040
+ // Save session after each tool result for crash recovery (deferred to avoid blocking)
1041
+ onToolResult: (turnMessages) => {
1042
+ const prepared = (0, context_1.postTurnTrim)(turnMessages);
1043
+ pendingPersists.push((0, context_1.deferPostTurnPersist)(sessPath, prepared, undefined, sessionState, sessionTurnLease ?? undefined));
1044
+ },
1045
+ onError: (error, severity) => {
1046
+ if (severity === "terminal" && failoverState) {
1047
+ capturedTerminalError = error;
1048
+ callbacks.onError(new Error(""), "transient");
1049
+ return;
1050
+ }
1051
+ callbacks.onError(error, severity);
1052
+ },
1053
+ };
1054
+ /* v8 ignore stop */
1055
+ const result = await (0, pipeline_1.handleInboundTurn)({
1056
+ channel: "cli",
1057
+ sessionKey: "session",
1058
+ capabilities: cliCapabilities,
1059
+ messages: [{ role: "user", content: userContent ?? userInput }],
1060
+ continuityIngressTexts: getCliContinuityIngressTexts(userInput),
1061
+ callbacks: failoverAwareCallbacks,
1062
+ sessionTurnLease: sessionTurnLease,
1063
+ friendResolver: { resolve: () => Promise.resolve(resolvedContext) },
1064
+ sessionLoader: {
1065
+ loadOrCreate: () => Promise.resolve({
1066
+ messages,
1067
+ sessionPath: sessPath,
1068
+ state: sessionState,
1069
+ events: sessionEvents,
1070
+ structuredOutputs: existing?.structuredOutputs,
811
1071
  }),
812
- postTurn: (turnMessages, sessionPathArg, usage, hooks, state) => {
813
- (0, context_1.postTurn)(turnMessages, sessionPathArg, usage, hooks, state);
814
- sessionState = state;
1072
+ },
1073
+ pendingDir,
1074
+ friendStore,
1075
+ provider: "local",
1076
+ externalId: localExternalId,
1077
+ enforceTrustGate: trust_gate_1.enforceTrustGate,
1078
+ drainPending: pending_1.drainPending,
1079
+ drainDeferredReturns: (deferredFriendId) => (0, pending_1.drainDeferredReturns)(currentAgentName, deferredFriendId),
1080
+ runAgent: async (msgs, cb, channel, sig, opts) => (0, core_1.runAgent)(msgs, cb, channel, sig, {
1081
+ ...opts,
1082
+ mcpManager: opts?.hardDisableTools ? undefined : await (0, mcp_manager_1.getSharedMcpManager)(owner) ?? undefined,
1083
+ toolContext: {
1084
+ /* v8 ignore next -- default no-op signin; pipeline provides the real one @preserve */
1085
+ signin: async () => undefined,
1086
+ ...opts?.toolContext,
1087
+ ...owner,
1088
+ summarize,
815
1089
  },
816
- accumulateFriendTokens: tokens_1.accumulateFriendTokens,
817
- signal,
818
- runAgentOptions: {
819
- toolChoiceRequired: (0, commands_1.getToolChoiceRequired)(),
820
- traceId: (0, nerves_1.createTraceId)(),
821
- mcpManager,
822
- },
823
- });
824
- // Handle gate rejection: display auto-reply if present
825
- if (!result.gateResult.allowed) {
826
- if ("autoReply" in result.gateResult && result.gateResult.autoReply) {
827
- process.stdout.write(`${result.gateResult.autoReply}\n`);
828
- }
1090
+ }),
1091
+ postTurn: (turnMessages, sessionPathArg, usage, hooks, state) => {
1092
+ // Trim synchronously (mutates turnMessages for next turn),
1093
+ // then defer envelope build + disk I/O to avoid blocking the TUI.
1094
+ const prepared = (0, context_1.postTurnTrim)(turnMessages, usage, hooks);
1095
+ sessionState = state;
1096
+ const persist = (0, context_1.deferPostTurnPersist)(sessionPathArg, prepared, usage, state, sessionTurnLease ?? undefined).then((events) => {
1097
+ /* v8 ignore next -- defensive: deferPostTurnPersist always resolves events in practice @preserve */
1098
+ sessionEvents = events.length > 0 ? events : sessionEvents;
1099
+ });
1100
+ pendingPersists.push(persist);
1101
+ },
1102
+ accumulateFriendTokens: friends_1.accumulateFriendTokens,
1103
+ signal,
1104
+ runAgentOptions: {
1105
+ toolChoiceRequired: (0, commands_1.getToolChoiceRequired)(),
1106
+ traceId: (0, nerves_1.createTraceId)(),
1107
+ toolContext,
1108
+ },
1109
+ failoverState,
1110
+ });
1111
+ /* v8 ignore start -- failover display: tested via pipeline integration tests @preserve */
1112
+ if (result.failoverMessage) {
1113
+ // Failover handled it — show the actionable message instead of the raw error
1114
+ process.stdout.write(`\x1b[33m${result.failoverMessage}\x1b[0m\n`);
1115
+ }
1116
+ else if (capturedTerminalError) {
1117
+ // Failover didn't trigger (no failoverState, or sequence failed) — show the raw error
1118
+ process.stderr.write(`\x1b[31m${(0, format_1.formatError)(capturedTerminalError)}\x1b[0m\n`);
1119
+ }
1120
+ /* v8 ignore stop */
1121
+ // Handle gate rejection: display auto-reply if present
1122
+ if (!result.gateResult.allowed) {
1123
+ if ("autoReply" in result.gateResult && result.gateResult.autoReply) {
1124
+ process.stdout.write(`${result.gateResult.autoReply}\n`);
829
1125
  }
830
- return { usage: result.usage };
831
- },
832
- onNewSession: () => {
833
- (0, context_1.deleteSession)(sessPath);
834
- },
835
- });
836
- }
837
- finally {
838
- sessionLock?.release();
839
- }
1126
+ }
1127
+ return { usage: result.usage, turnOutcome: result.turnOutcome, commandAction: result.commandAction };
1128
+ },
1129
+ });
1130
+ // Force exit: lingering handles (Ink cleanup timers, MCP connections) keep the
1131
+ // event loop alive after the interactive session ends. This is safe because all
1132
+ // session persistence has already completed in the finally block above.
1133
+ /* v8 ignore next -- process.exit not callable in vitest @preserve */
1134
+ if (!options?._testInputSource)
1135
+ process.exit(0);
840
1136
  }
1137
+ // CI trigger