@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
@@ -4,79 +4,675 @@
4
4
  // resolve friend -> trust gate -> load session -> drain pending -> runAgent -> postTurn -> token accumulation.
5
5
  //
6
6
  // Transport-level concerns (BB API calls, Teams cards, readline) stay in sense adapters.
7
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
+ if (k2 === undefined) k2 = k;
9
+ var desc = Object.getOwnPropertyDescriptor(m, k);
10
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
+ desc = { enumerable: true, get: function() { return m[k]; } };
12
+ }
13
+ Object.defineProperty(o, k2, desc);
14
+ }) : (function(o, m, k, k2) {
15
+ if (k2 === undefined) k2 = k;
16
+ o[k2] = m[k];
17
+ }));
18
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
20
+ }) : function(o, v) {
21
+ o["default"] = v;
22
+ });
23
+ var __importStar = (this && this.__importStar) || (function () {
24
+ var ownKeys = function(o) {
25
+ ownKeys = Object.getOwnPropertyNames || function (o) {
26
+ var ar = [];
27
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
+ return ar;
29
+ };
30
+ return ownKeys(o);
31
+ };
32
+ return function (mod) {
33
+ if (mod && mod.__esModule) return mod;
34
+ var result = {};
35
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
+ __setModuleDefault(result, mod);
37
+ return result;
38
+ };
39
+ })();
7
40
  Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.emitObligationTransitionEpisodes = emitObligationTransitionEpisodes;
42
+ exports.selectCheckpointCurrentAsk = selectCheckpointCurrentAsk;
8
43
  exports.handleInboundTurn = handleInboundTurn;
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
9
46
  const runtime_1 = require("../nerves/runtime");
47
+ const commands_1 = require("./commands");
10
48
  const continuity_1 = require("./continuity");
11
49
  const manager_1 = require("../heart/bridges/manager");
12
50
  const identity_1 = require("../heart/identity");
13
- const tasks_1 = require("../repertoire/tasks");
14
- const session_activity_1 = require("../heart/session-activity");
51
+ const auth_flow_1 = require("../heart/auth/auth-flow");
15
52
  const active_work_1 = require("../heart/active-work");
16
53
  const delegation_1 = require("../heart/delegation");
17
- const target_resolution_1 = require("../heart/target-resolution");
18
- const thoughts_1 = require("../heart/daemon/thoughts");
19
- const pending_1 = require("../mind/pending");
20
- const obligations_1 = require("../heart/obligations");
21
- function emptyTaskBoard() {
54
+ const obligations_1 = require("../arc/obligations");
55
+ const packets_1 = require("../arc/packets");
56
+ const evolution_1 = require("../arc/evolution");
57
+ const provider_failover_1 = require("../heart/provider-failover");
58
+ const openai_codex_token_1 = require("../heart/providers/openai-codex-token");
59
+ const run_ledger_1 = require("../heart/run-ledger");
60
+ const tempo_1 = require("../heart/tempo");
61
+ const temporal_view_1 = require("../heart/temporal-view");
62
+ const start_of_turn_packet_1 = require("../heart/start-of-turn-packet");
63
+ const bundle_state_1 = require("../heart/bundle-state");
64
+ const sync_1 = require("../heart/sync");
65
+ const agent_discovery_1 = require("../heart/daemon/agent-discovery");
66
+ const sync_health_1 = require("../heart/sync-health");
67
+ const config_1 = require("../heart/config");
68
+ const session_events_1 = require("../heart/session-events");
69
+ const session_activity_1 = require("../heart/session-activity");
70
+ const presence_1 = require("../arc/presence");
71
+ const episodes_1 = require("../arc/episodes");
72
+ const turn_context_1 = require("../heart/turn-context");
73
+ const provider_visibility_1 = require("../heart/provider-visibility");
74
+ const orientation_frame_1 = require("../heart/orientation-frame");
75
+ const flight_recorder_1 = require("../arc/flight-recorder");
76
+ const context_loss_sentinel_1 = require("../heart/context-loss-sentinel");
77
+ const cares_1 = require("../arc/cares");
78
+ const turn_execution_lease_1 = require("../heart/turn-execution-lease");
79
+ const friend_link_codes_1 = require("../mind/friend-link-codes");
80
+ const VOICE_PENDING_MAX_AGE_MS = 15 * 60 * 1_000;
81
+ const TELEGRAM_GENERIC_OUTREACH_PENDING_MAX_AGE_MS = 15 * 60 * 1_000;
82
+ function pendingExpirationReason(channel, message, now, agentName) {
83
+ /* v8 ignore start -- pending expiry edge permutations are covered by the stale voice queue tests; this helper keeps defensive non-voice fallbacks @preserve */
84
+ if (Number.isFinite(message.expiresAt) && Number(message.expiresAt) <= now)
85
+ return "explicit_expiry";
86
+ if (channel === "telegram"
87
+ && agentName
88
+ && message.from === agentName
89
+ && !message.delegatedFrom
90
+ && !message.obligationId
91
+ && !message.requestId
92
+ && Number.isFinite(message.timestamp)
93
+ && now - message.timestamp > TELEGRAM_GENERIC_OUTREACH_PENDING_MAX_AGE_MS) {
94
+ return "telegram_generic_outreach_freshness_window";
95
+ }
96
+ if (channel !== "voice")
97
+ return null;
98
+ if (!Number.isFinite(message.timestamp))
99
+ return null;
100
+ return now - message.timestamp > VOICE_PENDING_MAX_AGE_MS ? "voice_freshness_window" : null;
101
+ /* v8 ignore stop */
102
+ }
103
+ function archiveExpiredPendingMessages(input) {
104
+ /* v8 ignore start -- archive path edge cases are defensive filesystem hygiene; stale voice expiry tests cover the normal archive path @preserve */
105
+ if (input.expired.length === 0)
106
+ return;
107
+ const pendingRoot = path.join(input.agentRoot, "state", "pending");
108
+ const relativePendingDir = path.relative(pendingRoot, input.pendingDir);
109
+ const archiveDir = relativePendingDir && !relativePendingDir.startsWith("..") && !path.isAbsolute(relativePendingDir)
110
+ ? path.join(input.agentRoot, "state", "pending-expired", relativePendingDir)
111
+ : path.join(input.agentRoot, "state", "pending-expired", input.channel);
112
+ try {
113
+ fs.mkdirSync(archiveDir, { recursive: true });
114
+ input.expired.forEach(({ message, reason }, index) => {
115
+ const stamp = Number.isFinite(message.timestamp) ? message.timestamp : input.now;
116
+ const filePath = path.join(archiveDir, `${stamp}-${index}.json`);
117
+ fs.writeFileSync(filePath, `${JSON.stringify({
118
+ ...message,
119
+ expiredAt: input.now,
120
+ expirationReason: reason,
121
+ originalPendingDir: input.pendingDir,
122
+ }, null, 2)}\n`, "utf-8");
123
+ });
124
+ /* v8 ignore stop */
125
+ (0, runtime_1.emitNervesEvent)({
126
+ component: "senses",
127
+ event: "senses.pending_expired",
128
+ message: "expired stale pending sense messages before model injection",
129
+ meta: {
130
+ channel: input.channel,
131
+ pendingDir: input.pendingDir,
132
+ archiveDir,
133
+ count: String(input.expired.length),
134
+ },
135
+ });
136
+ }
137
+ catch (error) {
138
+ /* v8 ignore next -- filesystem archive failures are defensive; stale voice expiry success path is covered @preserve */
139
+ (0, runtime_1.emitNervesEvent)({
140
+ level: "warn",
141
+ component: "senses",
142
+ event: "senses.pending_expire_archive_error",
143
+ message: "failed to archive expired pending sense messages",
144
+ meta: {
145
+ channel: input.channel,
146
+ pendingDir: input.pendingDir,
147
+ count: String(input.expired.length),
148
+ error: error instanceof Error ? error.message : String(error),
149
+ },
150
+ });
151
+ }
152
+ }
153
+ function filterDeliverablePendingMessages(input) {
154
+ if (input.messages.length === 0)
155
+ return input.messages;
156
+ const now = input.now ?? Date.now();
157
+ const agentName = (() => {
158
+ try {
159
+ return (0, identity_1.getAgentName)();
160
+ }
161
+ catch {
162
+ return undefined;
163
+ }
164
+ })();
165
+ const deliverable = [];
166
+ const expired = [];
167
+ for (const message of input.messages) {
168
+ const reason = pendingExpirationReason(input.channel, message, now, agentName);
169
+ if (reason) {
170
+ expired.push({ message, reason });
171
+ }
172
+ else {
173
+ deliverable.push(message);
174
+ }
175
+ }
176
+ if (expired.length > 0) {
177
+ try {
178
+ archiveExpiredPendingMessages({ ...input, agentRoot: (0, identity_1.getAgentRoot)(), expired, now });
179
+ }
180
+ catch (error) {
181
+ /* v8 ignore next -- getAgentRoot failure is only reachable under broken runtime identity state @preserve */
182
+ (0, runtime_1.emitNervesEvent)({
183
+ level: "warn",
184
+ component: "senses",
185
+ event: "senses.pending_expire_archive_error",
186
+ message: "failed to resolve agent root for expired pending sense message archive",
187
+ meta: {
188
+ channel: input.channel,
189
+ pendingDir: input.pendingDir,
190
+ count: String(expired.length),
191
+ error: error instanceof Error ? error.message : String(error),
192
+ },
193
+ });
194
+ }
195
+ }
196
+ return deliverable;
197
+ }
198
+ /**
199
+ * Emit episodes for obligation state transitions detected during a turn.
200
+ * Exported for direct testability (avoids v8 coverage merge issues in multi-file test suites).
201
+ */
202
+ function emitObligationTransitionEpisodes(agentRoot, preTurnObligationIds, postTurnObligations, preTurnObligations) {
203
+ const postTurnObligationIds = new Set(postTurnObligations.map((ob) => `${ob.id}:${ob.status}`));
204
+ for (const key of preTurnObligationIds) {
205
+ if (!postTurnObligationIds.has(key)) {
206
+ const [obId] = key.split(":");
207
+ const matchedOb = postTurnObligations.find((ob) => ob.id === obId) ?? preTurnObligations.find((ob) => ob.id === obId);
208
+ (0, episodes_1.emitEpisode)(agentRoot, {
209
+ kind: "obligation_shift",
210
+ summary: `obligation "${matchedOb?.content ?? obId}" status changed`,
211
+ whyItMattered: "obligation state transition detected during turn",
212
+ relatedEntities: [`obligation:${obId}`],
213
+ salience: "medium",
214
+ });
215
+ }
216
+ }
217
+ }
218
+ function providerLaneForChannel(channel) {
219
+ return channel === "inner" ? "inner" : "outward";
220
+ }
221
+ function latestUserAuthoredText(messages, continuityIngressTexts) {
222
+ const ingress = continuityIngressTexts?.map((entry) => entry.trim()).filter(Boolean);
223
+ if (ingress?.length)
224
+ return ingress[ingress.length - 1];
225
+ const userMessages = messages
226
+ .filter((message) => message.role === "user")
227
+ .map((message) => typeof message.content === "string" ? message.content.trim() : "")
228
+ .filter(Boolean);
229
+ return userMessages[userMessages.length - 1];
230
+ }
231
+ function selectCheckpointCurrentAsk(input) {
232
+ const frameSpeech = input.orientationFrame?.currentUserSpeech
233
+ .map((speech) => speech.trim())
234
+ .filter(Boolean)
235
+ .at(-1) ?? null;
236
+ if (input.orientationFrame?.speechKind === "reaction")
237
+ return frameSpeech;
238
+ const currentUtterance = input.currentUserMessage?.trim();
239
+ return currentUtterance || frameSpeech;
240
+ }
241
+ function sessionRefForFlightRecorder(session) {
242
+ return `${session.friendId}/${session.channel}/${session.key}`;
243
+ }
244
+ function isTerminalWithoutContinuation(outcome, hasActiveContinuation) {
245
+ return !hasActiveContinuation && (outcome === "settled"
246
+ || outcome === "observed"
247
+ || outcome === "rested"
248
+ || outcome === "superseded");
249
+ }
250
+ function selectPostTurnContinuityState(input) {
251
+ const continuingState = {
252
+ ...(input.mustResolveBeforeHandoff ? { mustResolveBeforeHandoff: true } : {}),
253
+ ...(typeof input.lastFriendActivityAt === "string" ? { lastFriendActivityAt: input.lastFriendActivityAt } : {}),
254
+ };
255
+ const clearsMustResolveBeforeHandoff = input.outcome === "settled"
256
+ || input.outcome === "blocked"
257
+ || input.outcome === "superseded"
258
+ || input.outcome === "observed";
259
+ if (!clearsMustResolveBeforeHandoff) {
260
+ return Object.keys(continuingState).length > 0 ? continuingState : undefined;
261
+ }
262
+ return typeof input.lastFriendActivityAt === "string"
263
+ ? { lastFriendActivityAt: input.lastFriendActivityAt }
264
+ : undefined;
265
+ }
266
+ function readPostTurnFlightRecorderArcSnapshot(agentRoot) {
267
+ const latest = (0, flight_recorder_1.readFlightRecorderResume)(agentRoot);
22
268
  return {
23
- compact: "",
24
- full: "",
25
- byStatus: {
26
- drafting: [],
27
- processing: [],
28
- validating: [],
29
- collaborating: [],
30
- paused: [],
31
- blocked: [],
32
- done: [],
269
+ activeObligationIds: (0, obligations_1.readPendingObligations)(agentRoot).map((obligation) => obligation.id),
270
+ activeReturnObligationIds: (0, obligations_1.listActiveReturnObligationsForRoot)(agentRoot).map((obligation) => obligation.id),
271
+ activePacketIds: (0, packets_1.listActivePonderPackets)(agentRoot).map((packet) => packet.id),
272
+ openEvolutionCaseIds: (0, evolution_1.listOpenEvolutionCases)(agentRoot).map((evolutionCase) => evolutionCase.id),
273
+ recentClaimIds: latest.recentClaimIds,
274
+ unverifiedClaimIds: latest.unverifiedClaimIds,
275
+ };
276
+ }
277
+ function recordTurnFlightRecorderCheckpoint(input) {
278
+ const hasActiveContinuation = input.mustResolveBeforeHandoff
279
+ || input.activeObligationIds.length > 0
280
+ || input.activeReturnObligationIds.length > 0
281
+ || input.activePacketIds.length > 0
282
+ || input.openEvolutionCaseIds.length > 0;
283
+ const blockedBecause = [
284
+ ...(input.outcome === "blocked" ? ["agent reported a blocker in this turn"] : []),
285
+ ...(input.outcome === "errored" ? ["turn errored before a safe continuation was reached"] : []),
286
+ ...(input.outcome === "aborted" ? ["turn aborted before a safe continuation was reached"] : []),
287
+ ...(isTerminalWithoutContinuation(input.outcome, hasActiveContinuation) ? [`turn outcome ${input.outcome}; wait for new input before acting`] : []),
288
+ ];
289
+ const nextSafeAction = input.nextSafeAction
290
+ ?? (blockedBecause.length > 0
291
+ ? "inspect the latest session and wait for new input before acting"
292
+ : "continue the current held work and update Arc/Desk with the next checkpoint");
293
+ (0, flight_recorder_1.recordFlightRecorderEvent)(input.agentRoot, {
294
+ kind: input.sessionPersisted ? "post_turn_persisted" : "blocker_detected",
295
+ sessionRef: sessionRefForFlightRecorder(input.currentSession),
296
+ summary: input.sessionPersisted
297
+ ? `persisted ${input.currentSession.channel}/${input.currentSession.key} turn with outcome ${input.outcome}`
298
+ : `runAgent threw before session persistence for ${input.currentSession.channel}/${input.currentSession.key}`,
299
+ currentAsk: input.currentAsk,
300
+ nextSafeAction,
301
+ blockedBecause,
302
+ stopBefore: blockedBecause.length > 0 ? ["acting on stale context"] : [],
303
+ activeObligationIds: input.activeObligationIds,
304
+ activeReturnObligationIds: input.activeReturnObligationIds,
305
+ activePacketIds: input.activePacketIds,
306
+ openEvolutionCaseIds: input.openEvolutionCaseIds,
307
+ recentClaimIds: input.recentClaimIds,
308
+ unverifiedClaimIds: input.unverifiedClaimIds,
309
+ meta: {
310
+ outcome: input.outcome,
311
+ sessionPersisted: input.sessionPersisted,
312
+ mustResolveBeforeHandoff: input.mustResolveBeforeHandoff,
313
+ sessionPath: input.currentSession.sessionPath,
314
+ },
315
+ });
316
+ }
317
+ async function refreshContextLossSentinelNonFatal(input) {
318
+ try {
319
+ await (0, context_loss_sentinel_1.refreshContextLossSentinel)(input.agentName, input.agentRoot, { trigger: input.trigger });
320
+ }
321
+ catch (error) {
322
+ (0, runtime_1.emitNervesEvent)({
323
+ level: "warn",
324
+ component: "senses",
325
+ event: "senses.context_loss_sentinel_refresh_error",
326
+ message: `failed to refresh context-loss Sentinel for ${input.trigger}`,
327
+ meta: {
328
+ agentName: input.agentName,
329
+ trigger: input.trigger,
330
+ error: error instanceof Error ? error.message : String(error),
331
+ },
332
+ });
333
+ }
334
+ }
335
+ function resolveCurrentFailoverBinding(agentName, lane) {
336
+ const agentRoot = (0, identity_1.getAgentRoot)();
337
+ const { config: agentConfig } = (0, auth_flow_1.readAgentConfigForAgent)(agentName, path.dirname(agentRoot));
338
+ const fallback = lane === "inner" ? agentConfig.agentFacing : agentConfig.humanFacing;
339
+ return { provider: fallback.provider, model: fallback.model };
340
+ }
341
+ /**
342
+ * Apply an agent-driven failover switch to agent.json, but only after
343
+ * re-pinging the candidate. The inventory ping that produced the candidate
344
+ * may be stale by the time the agent replies — without this preflight, a
345
+ * "switch to <provider>" reply can move the lane onto an unreachable provider.
346
+ *
347
+ * Returns:
348
+ * { ok: true } — preflight passed, state mutated
349
+ * { ok: false, refused } — preflight failed, state untouched, caller should
350
+ * surface the refusal to the agent
351
+ * Throws on disk errors only (caught by caller as before).
352
+ */
353
+ async function writeFailoverAgentConfigSwitch(agentName, action) {
354
+ const validation = await (0, provider_failover_1.validateFailoverSwitchCandidate)(agentName, { provider: action.provider, model: action.model });
355
+ if (!validation.ok) {
356
+ return { ok: false, refused: true, classification: validation.classification, message: validation.message };
357
+ }
358
+ const agentRoot = (0, identity_1.getAgentRoot)();
359
+ const { configPath, config } = (0, auth_flow_1.readAgentConfigForAgent)(agentName, path.dirname(agentRoot));
360
+ const facingKey = action.lane === "inner" ? "agentFacing" : "humanFacing";
361
+ const nextConfig = {
362
+ ...config,
363
+ [facingKey]: {
364
+ ...config[facingKey],
365
+ provider: action.provider,
366
+ model: action.model,
33
367
  },
34
- actionRequired: [],
35
- unresolvedDependencies: [],
36
- activeSessions: [],
37
- activeBridges: [],
38
368
  };
369
+ fs.writeFileSync(configPath, `${JSON.stringify(nextConfig, null, 2)}\n`, "utf-8");
370
+ return { ok: true };
371
+ }
372
+ function formatFailoverSwitchLabel(action) {
373
+ const provenance = (0, provider_failover_1.formatCredentialProvenanceLabel)(action);
374
+ return `${action.provider} (${action.model}${provenance ? `; ${provenance}` : ""})`;
375
+ }
376
+ /**
377
+ * Build the operational refusal context message handed back to the agent when
378
+ * a failover switch is rejected by the preflight ping. Field-tested format:
379
+ * lead with the refusal + reason, restate the lane that's still standing, then
380
+ * list remaining ready alternatives so the next turn doesn't have to re-enter
381
+ * discovery mode.
382
+ */
383
+ function buildFailoverSwitchRefusedMessage(pendingContext, refusedAction, refusal) {
384
+ const refusedLabel = formatFailoverSwitchLabel(refusedAction);
385
+ const remaining = pendingContext.readyProviders.filter((candidate) => candidate.provider !== refusedAction.provider);
386
+ const alternativesLine = remaining.length > 0
387
+ ? `available verified alternatives right now: ${remaining.map((c) => `${c.provider} (${c.model})`).join(", ")}.`
388
+ : "no other verified alternatives are ready right now.";
389
+ const nextMove = remaining.length > 0
390
+ ? `next move: reply "switch to <provider>" picking one of the alternatives above, or tell the user you cannot continue and why.`
391
+ : `next move: ask the operator to repair credentials for ${refusedAction.provider} (or another provider), or tell the user you cannot continue and why.`;
392
+ return [
393
+ `[provider switch refused: tried to switch ${refusedAction.lane} lane to ${refusedLabel}.`,
394
+ `reason: preflight ping failed (${refusal.classification}: ${refusal.message}).`,
395
+ `current lane unchanged: ${pendingContext.currentProvider} / ${pendingContext.currentModel} on the ${pendingContext.currentLane} lane.`,
396
+ alternativesLine,
397
+ nextMove + "]",
398
+ ].join(" ");
399
+ }
400
+ function prependTurnSections(message, sections) {
401
+ /* v8 ignore next -- defensive: only user messages with non-empty sections reach here @preserve */
402
+ if (message.role !== "user" || sections.length === 0)
403
+ return message;
404
+ const prefix = sections.join("\n\n");
405
+ /* v8 ignore start -- defensive: multipart content branch; non-string user messages are rare @preserve */
406
+ if (typeof message.content === "string") {
407
+ return {
408
+ ...message,
409
+ content: `${prefix}\n\n${message.content}`,
410
+ };
411
+ }
412
+ return {
413
+ ...message,
414
+ content: [
415
+ { type: "text", text: `${prefix}\n\n` },
416
+ ...message.content,
417
+ ],
418
+ };
419
+ /* v8 ignore stop */
420
+ }
421
+ // ── Pipeline ──────────────────────────────────────────────────────
422
+ let _lastSessionKey = null;
423
+ function runAgentOptionContextPacketIds(options) {
424
+ return Array.isArray(options.contextPacketIds) ? options.contextPacketIds : [];
425
+ }
426
+ function mergeRunAgentContextPacketIds(left, right) {
427
+ const merged = [
428
+ ...(Array.isArray(left) ? left : []),
429
+ ...(Array.isArray(right) ? right : []),
430
+ ].filter((value) => typeof value === "string" && value.trim().length > 0);
431
+ return merged.length > 0 ? [...new Set(merged)] : undefined;
432
+ }
433
+ function mergePreparedRunAgentOptions(base, prepared) {
434
+ if (!prepared)
435
+ return base;
436
+ const contextPacketIds = mergeRunAgentContextPacketIds(base.contextPacketIds, prepared.contextPacketIds);
437
+ const { contextPacketIds: _baseContextPacketIds, ...baseOptions } = base;
438
+ const { contextPacketIds: _preparedContextPacketIds, ...preparedOptions } = prepared;
439
+ const merged = {
440
+ ...baseOptions,
441
+ ...preparedOptions,
442
+ ...(contextPacketIds ? { contextPacketIds } : {}),
443
+ };
444
+ if (prepared.toolContext) {
445
+ const toolContext = {
446
+ ...base.toolContext,
447
+ ...prepared.toolContext,
448
+ };
449
+ merged.toolContext = {
450
+ ...toolContext,
451
+ signin: toolContext.signin ?? (async () => undefined),
452
+ };
453
+ }
454
+ return merged;
39
455
  }
40
- function readInnerWorkState() {
41
- const defaultJob = {
42
- status: "idle",
43
- content: null,
44
- origin: null,
45
- mode: "reflect",
46
- obligationStatus: null,
47
- surfacedResult: null,
48
- queuedAt: null,
49
- startedAt: null,
50
- surfacedAt: null,
456
+ function buildInboundRunLedgerInput(input) {
457
+ const target = {
458
+ channel: input.channel,
459
+ senseType: input.senseType,
460
+ sessionKey: input.sessionKey,
461
+ sessionPath: input.sessionPath,
462
+ friendId: input.friendId,
463
+ currentUserHash: input.currentUserMessage ? (0, run_ledger_1.runLedgerHash)(input.currentUserMessage) : null,
464
+ traceId: input.runAgentOptions.traceId ?? null,
465
+ };
466
+ return {
467
+ agent: input.agentName,
468
+ triggerType: "inbound",
469
+ sourceKind: "sense",
470
+ senseOrHabit: String(input.channel),
471
+ startedAt: input.startedAt,
472
+ target,
473
+ idempotencyScope: {
474
+ channel: input.channel,
475
+ sessionKey: input.sessionKey,
476
+ friendId: input.friendId,
477
+ currentUserHash: target.currentUserHash,
478
+ traceId: input.runAgentOptions.traceId ?? null,
479
+ },
480
+ provider: "unknown",
481
+ model: "unknown",
482
+ sessionRef: {
483
+ channel: String(input.channel),
484
+ keyHash: (0, run_ledger_1.runLedgerHash)(input.sessionKey),
485
+ },
486
+ contextPacketIds: runAgentOptionContextPacketIds(input.runAgentOptions),
51
487
  };
488
+ }
489
+ function prepareInboundRunLedger(input) {
52
490
  try {
53
- const agentRoot = (0, identity_1.getAgentRoot)();
54
- const pendingDir = (0, pending_1.getInnerDialogPendingDir)((0, identity_1.getAgentName)());
55
- const sessionPath = (0, thoughts_1.getInnerDialogSessionPath)(agentRoot);
56
- const { pendingMessages, turns, runtimeState } = (0, thoughts_1.readInnerDialogRawData)(sessionPath, pendingDir);
57
- const dialogStatus = (0, thoughts_1.deriveInnerDialogStatus)(pendingMessages, turns, runtimeState);
58
- const job = (0, thoughts_1.deriveInnerJob)(pendingMessages, turns, runtimeState);
59
- // Derive obligationPending from both the pending message field and the obligation store
60
- const storeObligationPending = (0, obligations_1.readPendingObligations)(agentRoot).length > 0;
61
491
  return {
62
- status: dialogStatus.processing === "started" ? "running" : "idle",
63
- hasPending: dialogStatus.queue !== "clear",
64
- origin: dialogStatus.origin,
65
- contentSnippet: dialogStatus.contentSnippet,
66
- obligationPending: dialogStatus.obligationPending || storeObligationPending,
67
- job,
492
+ agentRoot: (0, identity_1.getAgentRoot)(),
493
+ recordInput: buildInboundRunLedgerInput({
494
+ ...input,
495
+ agentName: (0, identity_1.getAgentName)(),
496
+ }),
68
497
  };
69
498
  }
70
499
  catch {
71
- return {
72
- status: "idle",
73
- hasPending: false,
74
- job: defaultJob,
75
- };
500
+ return null;
76
501
  }
77
502
  }
78
- // ── Pipeline ──────────────────────────────────────────────────────
79
503
  async function handleInboundTurn(input) {
504
+ return (0, turn_execution_lease_1.withTurnExecutionLease)(() => handleInboundTurnExclusive(input));
505
+ }
506
+ async function handleInboundTurnExclusive(input) {
507
+ // Continuity resumption is structured caller intent. Never infer it from the
508
+ // current message or from similarity to prior obligations.
509
+ const resumePriorWork = input.runAgentOptions?.resumePriorWork === true;
510
+ // Reset session-scoped state when the session changes
511
+ const sessionKey = `${input.channel}/${input.sessionKey ?? "session"}`;
512
+ if (sessionKey !== _lastSessionKey) {
513
+ _lastSessionKey = sessionKey;
514
+ // Reset file-state cache and scrutiny tracking for the new session
515
+ const { fileStateCache } = await Promise.resolve().then(() => __importStar(require("../mind/file-state")));
516
+ const { resetSessionModifiedFiles } = await Promise.resolve().then(() => __importStar(require("../mind/scrutiny")));
517
+ fileStateCache.clear();
518
+ resetSessionModifiedFiles();
519
+ }
520
+ // Step 0: Check for pending failover reply
521
+ if (input.failoverState?.pending) {
522
+ const userText = input.messages
523
+ .filter((m) => m.role === "user")
524
+ .map((m) => typeof m.content === "string" ? m.content : /* v8 ignore next -- defensive: multipart content fallback @preserve */ "")
525
+ .join(" ")
526
+ .trim();
527
+ const pendingContext = input.failoverState.pending;
528
+ const failoverAction = (0, provider_failover_1.handleFailoverReply)(userText, pendingContext);
529
+ const failoverAgentName = pendingContext.agentName;
530
+ input.failoverState.pending = null; // always clear before acting
531
+ if (failoverAction.action === "switch") {
532
+ let switchOutcome = null;
533
+ try {
534
+ switchOutcome = await writeFailoverAgentConfigSwitch(failoverAgentName, failoverAction);
535
+ /* v8 ignore start -- defensive: write failure during provider switch @preserve */
536
+ }
537
+ catch (switchError) {
538
+ (0, runtime_1.emitNervesEvent)({
539
+ level: "error",
540
+ component: "senses",
541
+ event: "senses.failover_switch_error",
542
+ message: `failed to switch ${failoverAction.lane} provider lane to ${failoverAction.provider}`,
543
+ meta: { agentName: failoverAgentName, lane: failoverAction.lane, provider: failoverAction.provider, model: failoverAction.model, error: switchError instanceof Error ? switchError.message : String(switchError) },
544
+ });
545
+ }
546
+ /* v8 ignore stop */
547
+ if (switchOutcome?.ok) {
548
+ (0, runtime_1.emitNervesEvent)({
549
+ component: "senses",
550
+ event: "senses.failover_switch",
551
+ message: `switched ${failoverAction.lane} provider lane to ${failoverAction.provider} via failover`,
552
+ meta: {
553
+ agentName: failoverAgentName,
554
+ lane: failoverAction.lane,
555
+ provider: failoverAction.provider,
556
+ model: failoverAction.model,
557
+ credentialRevision: failoverAction.credentialRevision,
558
+ source: failoverAction.source,
559
+ },
560
+ });
561
+ // Replace "switch to <provider>" with a context message for the agent.
562
+ // The session already has the user's original question from the failed turn.
563
+ // The agent needs to know what happened so it can respond appropriately.
564
+ const newProviderLabel = formatFailoverSwitchLabel(failoverAction);
565
+ input.messages = [{
566
+ role: "user",
567
+ content: `[provider switch: ${pendingContext.errorSummary}. switched ${failoverAction.lane} lane to ${newProviderLabel}. your conversation history is intact — respond to the user's last message.]`,
568
+ }];
569
+ input.switchedProvider = failoverAction.provider;
570
+ }
571
+ else if (switchOutcome && !switchOutcome.ok) {
572
+ // Preflight refused the switch — the candidate provider is not actually
573
+ // reachable right now. Keep the existing lane intact and tell the agent
574
+ // what happened so it can pick something else next turn.
575
+ (0, runtime_1.emitNervesEvent)({
576
+ level: "warn",
577
+ component: "senses",
578
+ event: "senses.failover_switch_refused",
579
+ message: `refused failover switch of ${failoverAction.lane} lane to ${failoverAction.provider}: ${switchOutcome.message}`,
580
+ meta: {
581
+ agentName: failoverAgentName,
582
+ lane: failoverAction.lane,
583
+ provider: failoverAction.provider,
584
+ model: failoverAction.model,
585
+ classification: switchOutcome.classification,
586
+ error: switchOutcome.message,
587
+ },
588
+ });
589
+ input.messages = [{
590
+ role: "user",
591
+ content: buildFailoverSwitchRefusedMessage(pendingContext, failoverAction, switchOutcome),
592
+ }];
593
+ }
594
+ // Switch failed OR succeeded — either way, fall through to normal processing.
595
+ }
596
+ else if (failoverAction.action === "refresh") {
597
+ const refresh = failoverAction.provider === "openai-codex"
598
+ ? await (0, openai_codex_token_1.refreshOpenAICodexProviderCredentials)(failoverAgentName, {
599
+ force: true,
600
+ reason: "failover-reply",
601
+ })
602
+ : { ok: false, actor: "human-required", message: `provider ${failoverAction.provider} does not support chat-driven refresh` };
603
+ if (refresh.ok) {
604
+ (0, runtime_1.emitNervesEvent)({
605
+ component: "senses",
606
+ event: "senses.failover_refresh",
607
+ message: `refreshed ${failoverAction.provider} provider credentials via failover`,
608
+ meta: {
609
+ agentName: failoverAgentName,
610
+ lane: failoverAction.lane,
611
+ provider: failoverAction.provider,
612
+ refreshed: refresh.refreshed,
613
+ },
614
+ });
615
+ input.messages = [{
616
+ role: "user",
617
+ content: `[provider refresh: ${pendingContext.errorSummary}. refreshed ${failoverAction.provider} credentials for the ${failoverAction.lane} lane. your conversation history is intact — respond to the user's last message.]`,
618
+ }];
619
+ }
620
+ else {
621
+ (0, runtime_1.emitNervesEvent)({
622
+ level: refresh.actor === "human-required" ? "warn" : "error",
623
+ component: "senses",
624
+ event: "senses.failover_refresh_error",
625
+ message: `failed to refresh ${failoverAction.provider} provider credentials via failover`,
626
+ meta: {
627
+ agentName: failoverAgentName,
628
+ lane: failoverAction.lane,
629
+ provider: failoverAction.provider,
630
+ actor: refresh.actor,
631
+ error: refresh.message,
632
+ },
633
+ });
634
+ input.messages = [{
635
+ role: "user",
636
+ content: `[provider refresh failed: tried to refresh ${failoverAction.provider} for the ${failoverAction.lane} lane. actor: ${refresh.actor}. reason: ${refresh.message}. current lane unchanged: ${pendingContext.currentProvider} / ${pendingContext.currentModel}. If ready alternatives are listed in the prior failover message, switch to one; otherwise tell the user the concrete human-required auth step.]`,
637
+ }];
638
+ }
639
+ }
640
+ }
641
+ const firstUserMsg = input.messages.find((m) => m.role === "user");
642
+ const userText = firstUserMsg
643
+ ? (typeof firstUserMsg.content === "string"
644
+ ? firstUserMsg.content
645
+ : Array.isArray(firstUserMsg.content)
646
+ ? firstUserMsg.content.find((p) => p.type === "text")?.text ?? ""
647
+ : /* v8 ignore next -- defensive: content is always string or array @preserve */ "")
648
+ : "";
649
+ // Step 0b: Slash command interception (before friend resolution / agent turn)
650
+ {
651
+ const parsed = (0, commands_1.parseSlashCommand)(userText);
652
+ if (parsed) {
653
+ const registry = (0, commands_1.getSharedCommandRegistry)();
654
+ const dispatchResult = registry.dispatch(parsed.command, { channel: input.channel });
655
+ if (dispatchResult.handled && dispatchResult.result) {
656
+ (0, runtime_1.emitNervesEvent)({
657
+ component: "senses",
658
+ event: "senses.pipeline_command",
659
+ message: `slash command intercepted: /${parsed.command}`,
660
+ meta: { command: parsed.command, channel: input.channel },
661
+ });
662
+ if (dispatchResult.result.message) {
663
+ input.callbacks.onTextChunk(dispatchResult.result.message);
664
+ }
665
+ // Return a minimal result — no agent turn, no session write
666
+ const resolvedContext = await input.friendResolver.resolve();
667
+ return {
668
+ resolvedContext,
669
+ gateResult: { allowed: true },
670
+ turnOutcome: "command",
671
+ commandAction: dispatchResult.result.action,
672
+ };
673
+ }
674
+ }
675
+ }
80
676
  // Step 1: Resolve friend
81
677
  const resolvedContext = await input.friendResolver.resolve();
82
678
  (0, runtime_1.emitNervesEvent)({
@@ -89,6 +685,27 @@ async function handleInboundTurn(input) {
89
685
  senseType: input.capabilities.senseType,
90
686
  },
91
687
  });
688
+ // Step 1b: Identity link code. Runs before the trust gate so an identity the agent
689
+ // does not trust yet can prove it belongs to someone it does. Never a model turn.
690
+ if (input.channel !== "inner" && (0, friend_link_codes_1.parseLinkCode)(userText)) {
691
+ const agentRoot = (0, identity_1.getAgentRoot)();
692
+ const redemption = (await (0, friend_link_codes_1.redeemLinkCode)({
693
+ agentRoot,
694
+ friendsDir: path.join(agentRoot, "friends"),
695
+ store: input.friendStore,
696
+ text: userText,
697
+ claimant: resolvedContext.friend,
698
+ channel: input.channel,
699
+ isGroupChat: input.isGroupChat ?? false,
700
+ }));
701
+ input.callbacks.onTextChunk(redemption.reply);
702
+ return {
703
+ resolvedContext: redemption.status === "linked" ? { ...resolvedContext, friend: redemption.friend } : resolvedContext,
704
+ gateResult: { allowed: true },
705
+ turnOutcome: "command",
706
+ commandAction: "response",
707
+ };
708
+ }
92
709
  // Step 2: Trust gate
93
710
  const gateInput = {
94
711
  friend: resolvedContext.friend,
@@ -121,7 +738,11 @@ async function handleInboundTurn(input) {
121
738
  }
122
739
  // Step 3: Load/create session
123
740
  const session = await input.sessionLoader.loadOrCreate();
741
+ if (input.sessionTurnLease && path.resolve(session.sessionPath) !== input.sessionTurnLease.sessionPath) {
742
+ throw new Error(`leased session path mismatch: expected ${input.sessionTurnLease.sessionPath}, loaded ${path.resolve(session.sessionPath)}`);
743
+ }
124
744
  const sessionMessages = session.messages;
745
+ const sessionEvents = session.events ?? [];
125
746
  let mustResolveBeforeHandoff = (0, continuity_1.resolveMustResolveBeforeHandoff)(session.state?.mustResolveBeforeHandoff === true, input.continuityIngressTexts);
126
747
  const lastFriendActivityAt = input.channel === "inner"
127
748
  ? session.state?.lastFriendActivityAt
@@ -136,172 +757,505 @@ async function handleInboundTurn(input) {
136
757
  key: input.sessionKey ?? "session",
137
758
  sessionPath: session.sessionPath,
138
759
  };
139
- const activeBridges = (0, manager_1.createBridgeManager)().findBridgesForSession({
140
- friendId: currentSession.friendId,
141
- channel: currentSession.channel,
142
- key: currentSession.key,
143
- });
144
- const bridgeContext = (0, manager_1.formatBridgeContext)(activeBridges) || undefined;
145
- let sessionActivity = [];
760
+ const currentSessionTiming = (0, session_events_1.describeCurrentSessionTiming)(sessionEvents);
761
+ // Step 3b: Pre-turn sync pull (opt-in) — MUST happen before any continuity state reads
762
+ // so that obligations, episodes, cares, etc. reflect the latest remote state.
763
+ let syncFailure;
764
+ let syncConfig = { enabled: false, remote: "origin" };
765
+ const liveLatencyMode = input.latencyMode === "live";
766
+ let declinedForHome = false;
146
767
  try {
147
- const agentRoot = (0, identity_1.getAgentRoot)();
148
- sessionActivity = (0, session_activity_1.listSessionActivity)({
149
- sessionsDir: `${agentRoot}/state/sessions`,
150
- friendsDir: `${agentRoot}/friends`,
151
- agentName: (0, identity_1.getAgentName)(),
152
- currentSession: {
153
- friendId: currentSession.friendId,
154
- channel: currentSession.channel,
155
- key: currentSession.key,
156
- },
157
- });
158
- }
159
- catch {
160
- sessionActivity = [];
768
+ syncConfig = (0, config_1.getSyncConfig)();
161
769
  }
162
- let targetCandidates = [];
770
+ catch { /* config not available */ }
771
+ // Wrap the turn body in try/finally so postTurnPush always runs — even on
772
+ // error or early-return failover paths.
163
773
  try {
164
- if (input.channel !== "inner") {
774
+ /* v8 ignore start -- sync-enabled branches tested in sync.test.ts, pipeline tests mock at module boundary @preserve */
775
+ if (syncConfig.enabled && !liveLatencyMode) {
776
+ const pullResult = (0, sync_1.preTurnPull)((0, identity_1.getAgentRoot)(), syncConfig);
777
+ if (!pullResult.ok) {
778
+ syncFailure = pullResult.error;
779
+ }
780
+ // Check for pending-sync from a prior failed push. The record stays
781
+ // until a sync succeeds, so every turn sees a failure that persists.
782
+ if (!syncFailure) {
783
+ const pendingSyncPath = path.join((0, identity_1.getAgentRoot)(), "state", "pending-sync.json");
784
+ try {
785
+ if (fs.existsSync(pendingSyncPath)) {
786
+ const failure = (0, sync_health_1.parseBundleSyncFailure)(JSON.parse(fs.readFileSync(pendingSyncPath, "utf-8")));
787
+ syncFailure = failure
788
+ ? `bundle ${(0, sync_health_1.describeSyncFailure)(failure)}`
789
+ : "bundle sync failed earlier; state/pending-sync.json is unreadable";
790
+ }
791
+ }
792
+ catch {
793
+ // Ignore read errors for pending-sync
794
+ }
795
+ }
796
+ }
797
+ /* v8 ignore stop */
798
+ // Each agent runs on one machine. If the (just pulled) lease names another
799
+ // machine, this machine must not act for the agent, even before its daemon
800
+ // has noticed and stopped it. Nothing is written, so nothing is pushed.
801
+ // Leases travel only through bundle sync, so only synced bundles can move.
802
+ const homeElsewhere = syncConfig.enabled ? (0, agent_discovery_1.readAgentHomeElsewhere)((0, identity_1.getAgentRoot)()) : null;
803
+ if (homeElsewhere) {
804
+ declinedForHome = true;
805
+ (0, runtime_1.emitNervesEvent)({
806
+ level: "warn",
807
+ component: "senses",
808
+ event: "senses.turn_declined_homed_elsewhere",
809
+ message: "declined a turn for an agent homed on another machine",
810
+ meta: { channel: input.channel, homeMachineId: homeElsewhere.machineId, homeMachineName: homeElsewhere.machineName },
811
+ });
812
+ return { resolvedContext, gateResult };
813
+ }
814
+ // Build the turn context snapshot — centralizes all state reads
815
+ const ctx = await (0, turn_context_1.buildTurnContext)({
816
+ currentSession,
817
+ channel: input.channel,
818
+ friendStore: input.friendStore,
819
+ includeAmbientBackgroundDiscovery: !liveLatencyMode,
820
+ });
821
+ // Propagate sync failure from pre-turn pull
822
+ ctx.syncFailure = syncFailure;
823
+ const { activeBridges, sessionActivity, pendingObligations, codingSessions, otherCodingSessions, backgroundOperations } = ctx;
824
+ const bridgeContext = (0, manager_1.formatBridgeContext)(activeBridges) || undefined;
825
+ const activeWorkFrame = (0, active_work_1.buildActiveWorkFrame)({
826
+ currentSession,
827
+ currentObligation,
828
+ mustResolveBeforeHandoff,
829
+ inner: ctx.innerWorkState,
830
+ bridges: activeBridges,
831
+ codingSessions,
832
+ backgroundOperations,
833
+ otherCodingSessions,
834
+ pendingObligations,
835
+ friendActivity: sessionActivity,
836
+ targetCandidates: ctx.targetCandidates,
837
+ innerReturnObligations: ctx.returnObligations,
838
+ });
839
+ const friendContactTiming = (0, session_activity_1.describeSessionActivityTiming)((0, session_activity_1.selectFreshestFriendFacingActivity)(sessionActivity), { prefix: "freshest friend-facing contact" });
840
+ const delegationDecision = (0, delegation_1.decideDelegation)({
841
+ channel: input.channel,
842
+ ingressTexts: input.continuityIngressTexts ?? [],
843
+ activeWork: activeWorkFrame,
844
+ mustResolveBeforeHandoff,
845
+ });
846
+ // Step 4: Drain deferred friend returns, then ordinary per-session pending.
847
+ const deferredReturns = input.channel === "inner"
848
+ ? []
849
+ : (input.drainDeferredReturns?.(resolvedContext.friend.id) ?? []);
850
+ const sessionPending = input.drainPending(input.pendingDir);
851
+ const pending = filterDeliverablePendingMessages({
852
+ pendingDir: input.pendingDir,
853
+ channel: input.channel,
854
+ messages: [...deferredReturns, ...sessionPending],
855
+ });
856
+ const promotedPendingUserMessages = input.channel === "inner"
857
+ ? pending
858
+ .filter((message) => message.from === "ouro-cli" && message.content.trim().length > 0)
859
+ .map((message) => ({ role: "user", content: message.content }))
860
+ : [];
861
+ const backgroundPendingMessages = promotedPendingUserMessages.length > 0
862
+ ? pending.filter((message) => message.from !== "ouro-cli")
863
+ : pending;
864
+ // Assemble messages: session messages + pending + inbound user messages
865
+ // NOTE: live world-state checkpoint and pending messages are rendered via buildSystem (system prompt sections)
866
+ const extraPrefixSections = input.onPendingDrained?.(pending) ?? [];
867
+ // extraPrefixSections from onPendingDrained still prepend to user message (e.g., private-runtime wakes)
868
+ if (extraPrefixSections.length > 0 && input.messages.length > 0) {
869
+ input.messages[0] = prependTurnSections(input.messages[0], extraPrefixSections);
870
+ }
871
+ // Append user messages from the inbound turn
872
+ for (const msg of [...input.messages, ...promotedPendingUserMessages]) {
873
+ (0, session_events_1.stampIngressTime)(msg);
874
+ sessionMessages.push(msg);
875
+ }
876
+ const currentUserMessages = [...input.messages, ...promotedPendingUserMessages].filter((message) => message.role === "user");
877
+ const orientationFrame = input.runAgentOptions?.orientationFrame
878
+ ?? (currentUserMessages.length > 0
879
+ ? (0, orientation_frame_1.buildOrientationFrame)({
880
+ channel: input.channel,
881
+ messages: sessionMessages,
882
+ currentUserMessages,
883
+ structuredOutputs: session.structuredOutputs ?? [],
884
+ })
885
+ : undefined);
886
+ // Step 4b: Continuity pipeline — derive tempo, build start-of-turn packet, snapshot obligations
887
+ let renderedStartOfTurnPacket;
888
+ const careEvidenceNow = Date.now();
889
+ const activeCaresSnapshot = ctx.activeCares;
890
+ const preTurnObligationIds = new Set(pendingObligations.map((ob) => `${ob.id}:${ob.status}`));
891
+ try {
165
892
  const agentRoot = (0, identity_1.getAgentRoot)();
166
- targetCandidates = await (0, target_resolution_1.listTargetSessionCandidates)({
167
- sessionsDir: `${agentRoot}/state/sessions`,
168
- friendsDir: `${agentRoot}/friends`,
169
- agentName: (0, identity_1.getAgentName)(),
170
- currentSession: {
171
- friendId: currentSession.friendId,
172
- channel: currentSession.channel,
173
- key: currentSession.key,
893
+ const agentName = (0, identity_1.getAgentName)();
894
+ const { recentEpisodes } = ctx;
895
+ const projectedCares = activeCaresSnapshot.map((care) => (0, cares_1.projectCareEvidence)(care, careEvidenceNow));
896
+ const tempoState = (0, tempo_1.deriveTempo)({
897
+ activeSessions: sessionActivity.length + 1,
898
+ openObligations: pendingObligations.length,
899
+ recentEpisodeCount: recentEpisodes.length,
900
+ lastActivityAgeMs: sessionActivity.length > 0
901
+ ? Date.now() - new Date(sessionActivity[0].lastActivityAt).getTime()
902
+ : 0,
903
+ hasBlockers: false, // obligations use specific statuses, not "blocked"
904
+ highSalienceEpisodes: recentEpisodes.filter((ep) => ep.salience === "high" || ep.salience === "critical").length,
905
+ activeCareCount: activeCaresSnapshot.length,
906
+ atRiskCareCount: projectedCares.filter((care) => !("recheckRequired" in care) && care.currentRisk != null).length,
907
+ });
908
+ const temporalView = (0, temporal_view_1.buildTemporalView)(agentRoot, {
909
+ tempo: tempoState.mode,
910
+ preloaded: {
911
+ recentEpisodes,
912
+ activeObligations: pendingObligations,
913
+ activeCares: activeCaresSnapshot,
174
914
  },
175
- friendStore: input.friendStore,
176
915
  });
916
+ const startOfTurnPacket = (0, start_of_turn_packet_1.buildStartOfTurnPacket)(temporalView, {
917
+ canonicalObligations: {
918
+ primary: activeWorkFrame.primaryObligation,
919
+ all: activeWorkFrame.pendingObligations,
920
+ },
921
+ currentSessionTiming,
922
+ friendContactTiming,
923
+ flightRecorderResume: ctx.flightRecorderResume,
924
+ recoverySentinel: ctx.recoverySentinel,
925
+ careEvidenceNow,
926
+ });
927
+ /* v8 ignore next 3 -- syncFailure propagation tested in sync.test.ts @preserve */
928
+ if (syncFailure) {
929
+ startOfTurnPacket.syncFailure = syncFailure;
930
+ }
931
+ if (ctx.providerVisibility) {
932
+ startOfTurnPacket.providerSelection = (0, provider_visibility_1.formatAgentProviderVisibilityForStartOfTurn)(ctx.providerVisibility);
933
+ }
934
+ // Structured bundle state detection — surfaces discrete issues the
935
+ // agent can remediate via the bundle_* tools. Runs independently of
936
+ // syncFailure so the two signals coexist during the transition away
937
+ // from the legacy free-form syncFailure string. Always assigned; the
938
+ // packet renderer's empty-filter handles the empty-array case without
939
+ // a separate branch here.
940
+ startOfTurnPacket.bundleState = (0, bundle_state_1.detectBundleState)(agentRoot);
941
+ const capabilities = (0, start_of_turn_packet_1.buildCapabilitiesSection)(agentRoot);
942
+ if (capabilities) {
943
+ startOfTurnPacket.capabilities = capabilities;
944
+ }
945
+ renderedStartOfTurnPacket = (0, start_of_turn_packet_1.renderStartOfTurnPacket)(startOfTurnPacket, { resumePriorWork });
946
+ if (!renderedStartOfTurnPacket)
947
+ renderedStartOfTurnPacket = undefined;
948
+ // Update self-presence
949
+ const presence = (0, presence_1.derivePresence)(agentRoot, agentName, {
950
+ activeSessions: sessionActivity.length + 1,
951
+ openObligations: pendingObligations.length,
952
+ activeBridges: activeBridges.length,
953
+ codingLanes: codingSessions.length,
954
+ currentTempo: tempoState.mode,
955
+ });
956
+ (0, presence_1.writePresence)(agentRoot, agentName, presence);
177
957
  }
178
- }
179
- catch {
180
- targetCandidates = [];
181
- }
182
- let pendingObligations = [];
183
- try {
184
- pendingObligations = (0, obligations_1.readPendingObligations)((0, identity_1.getAgentRoot)());
185
- }
186
- catch {
187
- pendingObligations = [];
188
- }
189
- const activeWorkFrame = (0, active_work_1.buildActiveWorkFrame)({
190
- currentSession,
191
- currentObligation,
192
- mustResolveBeforeHandoff,
193
- inner: readInnerWorkState(),
194
- bridges: activeBridges,
195
- pendingObligations,
196
- taskBoard: (() => {
958
+ catch (continuityError) {
959
+ (0, runtime_1.emitNervesEvent)({
960
+ level: "warn",
961
+ component: "senses",
962
+ event: "senses.continuity_error",
963
+ message: "continuity pipeline failed, continuing without start-of-turn packet",
964
+ meta: { error: continuityError instanceof Error ? continuityError.message : String(continuityError) },
965
+ });
966
+ }
967
+ // Step 5: runAgent
968
+ const existingToolContext = input.runAgentOptions?.toolContext;
969
+ const promotedCurrentUserMessage = latestUserAuthoredText(promotedPendingUserMessages, undefined);
970
+ const currentUserMessage = promotedCurrentUserMessage
971
+ ?? existingToolContext?.currentUserMessage
972
+ ?? latestUserAuthoredText([...input.messages, ...promotedPendingUserMessages], input.continuityIngressTexts);
973
+ let runAgentOptions = {
974
+ ...input.runAgentOptions,
975
+ resumePriorWork,
976
+ ...(orientationFrame ? { orientationFrame } : {}),
977
+ ...(liveLatencyMode ? { skipKeptNotes: true } : {}),
978
+ bridgeContext,
979
+ activeWorkFrame,
980
+ delegationDecision,
981
+ startOfTurnPacket: renderedStartOfTurnPacket,
982
+ pendingMessages: backgroundPendingMessages.length > 0 ? backgroundPendingMessages.map((msg) => ({ from: msg.from, content: msg.content })) : undefined,
983
+ currentSessionKey: currentSession.key,
984
+ currentObligation,
985
+ mustResolveBeforeHandoff,
986
+ setMustResolveBeforeHandoff: (value) => {
987
+ mustResolveBeforeHandoff = value;
988
+ },
989
+ // Pre-read state from TurnContext for prompt assembly
990
+ daemonRunning: ctx.daemonRunning,
991
+ senseStatusLines: ctx.senseStatusLines,
992
+ bundleMeta: ctx.bundleMeta,
993
+ daemonHealth: ctx.daemonHealth,
994
+ flightRecorderResume: ctx.flightRecorderResume,
995
+ ...(ctx.providerVisibility ? { providerVisibility: ctx.providerVisibility } : {}),
996
+ toolContext: {
997
+ /* v8 ignore next -- default no-op signin satisfies interface; real signin injected by sense adapter @preserve */
998
+ signin: async () => undefined,
999
+ ...existingToolContext,
1000
+ ...(currentUserMessage ? { currentUserMessage } : {}),
1001
+ context: resolvedContext,
1002
+ friendStore: input.friendStore,
1003
+ currentSession,
1004
+ activeBridges,
1005
+ ...(orientationFrame ? { orientationFrame } : {}),
1006
+ },
1007
+ };
1008
+ runAgentOptions = mergePreparedRunAgentOptions(runAgentOptions, await input.prepareRunAgentOptions?.({
1009
+ messages: sessionMessages,
1010
+ currentUserMessages,
1011
+ resolvedContext,
1012
+ runAgentOptions,
1013
+ activeCares: activeCaresSnapshot,
1014
+ careEvidenceNow,
1015
+ }));
1016
+ const checkpointCurrentAsk = selectCheckpointCurrentAsk({
1017
+ currentUserMessage: runAgentOptions.toolContext?.currentUserMessage ?? currentUserMessage,
1018
+ orientationFrame: runAgentOptions.orientationFrame,
1019
+ });
1020
+ const runLedger = prepareInboundRunLedger({
1021
+ channel: input.channel,
1022
+ senseType: input.capabilities.senseType,
1023
+ sessionKey: currentSession.key,
1024
+ sessionPath: session.sessionPath,
1025
+ friendId: resolvedContext.friend.id,
1026
+ currentUserMessage,
1027
+ startedAt: new Date().toISOString(),
1028
+ runAgentOptions,
1029
+ });
1030
+ if (runLedger)
1031
+ (0, run_ledger_1.appendRunLedgerRecordNonFatal)(runLedger.agentRoot, (0, run_ledger_1.createRunLedgerRecord)({
1032
+ ...runLedger.recordInput,
1033
+ lifecycle: "started",
1034
+ }));
1035
+ let result;
1036
+ try {
1037
+ result = await input.runAgent(sessionMessages, input.callbacks, input.channel, input.signal, runAgentOptions);
1038
+ }
1039
+ catch (error) {
1040
+ if (runLedger)
1041
+ (0, run_ledger_1.appendRunLedgerRecordNonFatal)(runLedger.agentRoot, (0, run_ledger_1.createRunLedgerRecord)({
1042
+ ...runLedger.recordInput,
1043
+ lifecycle: "error",
1044
+ endedAt: new Date().toISOString(),
1045
+ usage: (0, run_ledger_1.usageMetadataFromUsageData)(undefined, "reported-unavailable"),
1046
+ errorName: error instanceof Error ? error.name : "NonErrorThrown",
1047
+ }));
197
1048
  try {
198
- return (0, tasks_1.getTaskModule)().getBoard();
1049
+ const agentRoot = (0, identity_1.getAgentRoot)();
1050
+ const postTurnArc = readPostTurnFlightRecorderArcSnapshot(agentRoot);
1051
+ recordTurnFlightRecorderCheckpoint({
1052
+ agentRoot,
1053
+ currentSession,
1054
+ currentAsk: checkpointCurrentAsk,
1055
+ nextSafeAction: activeWorkFrame.resumeHandle?.nextAction
1056
+ ?? activeWorkFrame.primaryObligation?.nextAction?.trim()
1057
+ ?? null,
1058
+ outcome: "errored",
1059
+ sessionPersisted: false,
1060
+ mustResolveBeforeHandoff,
1061
+ activeObligationIds: postTurnArc.activeObligationIds,
1062
+ activeReturnObligationIds: postTurnArc.activeReturnObligationIds,
1063
+ activePacketIds: postTurnArc.activePacketIds,
1064
+ openEvolutionCaseIds: postTurnArc.openEvolutionCaseIds,
1065
+ recentClaimIds: postTurnArc.recentClaimIds,
1066
+ unverifiedClaimIds: postTurnArc.unverifiedClaimIds,
1067
+ });
1068
+ await refreshContextLossSentinelNonFatal({ agentName: (0, identity_1.getAgentName)(), agentRoot, trigger: "post_turn" });
199
1069
  }
200
- catch {
201
- return emptyTaskBoard();
1070
+ catch (checkpointError) {
1071
+ (0, runtime_1.emitNervesEvent)({
1072
+ level: "warn",
1073
+ component: "senses",
1074
+ event: "senses.flight_recorder_checkpoint_error",
1075
+ message: "failed to record thrown-turn flight recorder checkpoint",
1076
+ meta: { error: checkpointError instanceof Error ? checkpointError.message : String(checkpointError) },
1077
+ });
202
1078
  }
203
- })(),
204
- friendActivity: sessionActivity,
205
- targetCandidates,
206
- });
207
- const delegationDecision = (0, delegation_1.decideDelegation)({
208
- channel: input.channel,
209
- ingressTexts: input.continuityIngressTexts ?? [],
210
- activeWork: activeWorkFrame,
211
- mustResolveBeforeHandoff,
212
- });
213
- // Step 4: Drain deferred friend returns, then ordinary per-session pending.
214
- const deferredReturns = input.channel === "inner"
215
- ? []
216
- : (input.drainDeferredReturns?.(resolvedContext.friend.id) ?? []);
217
- const sessionPending = input.drainPending(input.pendingDir);
218
- const pending = [...deferredReturns, ...sessionPending];
219
- // Assemble messages: session messages + pending (formatted) + inbound user messages
220
- if (pending.length > 0) {
221
- // Format pending messages and prepend to the user content
222
- const pendingSection = pending
223
- .map((msg) => `[pending from ${msg.from}]: ${msg.content}`)
224
- .join("\n");
225
- // If there are inbound user messages, prepend pending to the first one
226
- if (input.messages.length > 0) {
227
- const firstMsg = input.messages[0];
228
- if (firstMsg.role === "user") {
229
- if (typeof firstMsg.content === "string") {
230
- input.messages[0] = {
231
- ...firstMsg,
232
- content: `## pending messages\n${pendingSection}\n\n${firstMsg.content}`,
233
- };
1079
+ throw error;
1080
+ }
1081
+ const runLedgerLifecycle = result.outcome === "errored" ? "error" : "completed";
1082
+ if (runLedger)
1083
+ (0, run_ledger_1.appendRunLedgerRecordNonFatal)(runLedger.agentRoot, (0, run_ledger_1.createRunLedgerRecord)({
1084
+ ...runLedger.recordInput,
1085
+ lifecycle: runLedgerLifecycle,
1086
+ endedAt: new Date().toISOString(),
1087
+ usage: (0, run_ledger_1.usageMetadataFromUsageData)(result.usage, result.usage ? "provider" : "none"),
1088
+ ...(result.error ? { errorName: result.error.name } : {}),
1089
+ ...(result.errorClassification ? { errorCode: result.errorClassification } : {}),
1090
+ }));
1091
+ if (result.outcome === "suspended") {
1092
+ if (!result.suspension)
1093
+ throw new Error("suspended agent turn omitted durable approval suspension");
1094
+ (0, runtime_1.emitNervesEvent)({
1095
+ component: "senses",
1096
+ event: "senses.pipeline_approval_suspended",
1097
+ message: "pipeline returned a durably checkpointed approval suspension without duplicate post-turn persistence",
1098
+ meta: { approvalId: result.suspension.approvalId, sessionPath: session.sessionPath },
1099
+ });
1100
+ return {
1101
+ resolvedContext,
1102
+ gateResult,
1103
+ usage: result.usage,
1104
+ turnOutcome: result.outcome,
1105
+ suspension: result.suspension,
1106
+ sessionPath: session.sessionPath,
1107
+ messages: sessionMessages,
1108
+ drainedPending: pending,
1109
+ };
1110
+ }
1111
+ // Step 5b: Failover on terminal error
1112
+ if (result.outcome === "errored" && input.failoverState) {
1113
+ try {
1114
+ const agentName = (0, identity_1.getAgentName)();
1115
+ const currentLane = providerLaneForChannel(input.channel);
1116
+ const currentBinding = resolveCurrentFailoverBinding(agentName, currentLane);
1117
+ const currentProvider = currentBinding.provider;
1118
+ /* v8 ignore next -- defensive: errorClassification always set when errored @preserve */
1119
+ const classification = result.errorClassification ?? "unknown";
1120
+ const inventory = await (0, provider_failover_1.runMachineProviderFailoverInventory)(agentName, currentProvider);
1121
+ const failoverContext = (0, provider_failover_1.buildFailoverContext)(
1122
+ /* v8 ignore next -- defensive: error always set when errored @preserve */
1123
+ result.error?.message ?? "unknown error", classification, currentProvider, currentBinding.model, agentName, inventory, {}, { currentLane });
1124
+ input.failoverState.pending = failoverContext;
1125
+ const failoverNextState = selectPostTurnContinuityState({
1126
+ outcome: "errored",
1127
+ mustResolveBeforeHandoff,
1128
+ lastFriendActivityAt,
1129
+ });
1130
+ await input.postTurn(sessionMessages, session.sessionPath, result.usage, undefined, failoverNextState);
1131
+ try {
1132
+ const agentRoot = (0, identity_1.getAgentRoot)();
1133
+ const postTurnArc = readPostTurnFlightRecorderArcSnapshot(agentRoot);
1134
+ recordTurnFlightRecorderCheckpoint({
1135
+ agentRoot,
1136
+ currentSession,
1137
+ currentAsk: checkpointCurrentAsk,
1138
+ nextSafeAction: failoverContext.userMessage,
1139
+ outcome: "errored",
1140
+ sessionPersisted: true,
1141
+ mustResolveBeforeHandoff,
1142
+ activeObligationIds: postTurnArc.activeObligationIds,
1143
+ activeReturnObligationIds: postTurnArc.activeReturnObligationIds,
1144
+ activePacketIds: postTurnArc.activePacketIds,
1145
+ openEvolutionCaseIds: postTurnArc.openEvolutionCaseIds,
1146
+ recentClaimIds: postTurnArc.recentClaimIds,
1147
+ unverifiedClaimIds: postTurnArc.unverifiedClaimIds,
1148
+ });
1149
+ await refreshContextLossSentinelNonFatal({ agentName, agentRoot, trigger: "provider_failover" });
234
1150
  }
235
- else {
236
- input.messages[0] = {
237
- ...firstMsg,
238
- content: [
239
- { type: "text", text: `## pending messages\n${pendingSection}\n\n` },
240
- ...firstMsg.content,
241
- ],
242
- };
1151
+ catch (checkpointError) {
1152
+ /* v8 ignore next -- best-effort recorder write must not hide provider-failover guidance @preserve */
1153
+ (0, runtime_1.emitNervesEvent)({
1154
+ level: "warn",
1155
+ component: "senses",
1156
+ event: "senses.flight_recorder_checkpoint_error",
1157
+ message: "failed to record provider-failover flight recorder checkpoint",
1158
+ meta: { error: checkpointError instanceof Error ? checkpointError.message : String(checkpointError) },
1159
+ });
243
1160
  }
1161
+ return {
1162
+ resolvedContext,
1163
+ gateResult,
1164
+ usage: result.usage,
1165
+ turnOutcome: result.outcome,
1166
+ sessionPath: session.sessionPath,
1167
+ messages: sessionMessages,
1168
+ drainedPending: pending,
1169
+ failoverMessage: failoverContext.userMessage,
1170
+ };
1171
+ /* v8 ignore start -- failover catch: tested via pipeline failover sequence throws test but v8 under-reports catch coverage @preserve */
1172
+ }
1173
+ catch (failoverError) {
1174
+ (0, runtime_1.emitNervesEvent)({
1175
+ level: "warn",
1176
+ component: "senses",
1177
+ event: "senses.failover_error",
1178
+ message: "failover sequence failed, falling through",
1179
+ meta: { error: failoverError instanceof Error ? failoverError.message : String(failoverError) },
1180
+ });
244
1181
  }
1182
+ /* v8 ignore stop */
1183
+ }
1184
+ // Step 5c: Emit episodes for obligation state transitions
1185
+ try {
1186
+ const agentRoot = (0, identity_1.getAgentRoot)();
1187
+ const postTurnObligations = (0, obligations_1.readPendingObligations)(agentRoot);
1188
+ emitObligationTransitionEpisodes(agentRoot, preTurnObligationIds, postTurnObligations, pendingObligations);
245
1189
  }
1190
+ catch {
1191
+ // Episode emission is non-fatal
1192
+ }
1193
+ // Step 6: postTurn
1194
+ const nextState = selectPostTurnContinuityState({
1195
+ outcome: result.outcome,
1196
+ mustResolveBeforeHandoff,
1197
+ lastFriendActivityAt,
1198
+ });
1199
+ await input.postTurn(sessionMessages, session.sessionPath, result.usage, undefined, nextState);
1200
+ try {
1201
+ const agentRoot = (0, identity_1.getAgentRoot)();
1202
+ const postTurnArc = readPostTurnFlightRecorderArcSnapshot(agentRoot);
1203
+ recordTurnFlightRecorderCheckpoint({
1204
+ agentRoot,
1205
+ currentSession,
1206
+ currentAsk: checkpointCurrentAsk,
1207
+ nextSafeAction: activeWorkFrame.resumeHandle?.nextAction
1208
+ ?? activeWorkFrame.primaryObligation?.nextAction?.trim()
1209
+ ?? null,
1210
+ outcome: result.outcome ?? "observed",
1211
+ sessionPersisted: true,
1212
+ mustResolveBeforeHandoff,
1213
+ activeObligationIds: postTurnArc.activeObligationIds,
1214
+ activeReturnObligationIds: postTurnArc.activeReturnObligationIds,
1215
+ activePacketIds: postTurnArc.activePacketIds,
1216
+ openEvolutionCaseIds: postTurnArc.openEvolutionCaseIds,
1217
+ recentClaimIds: postTurnArc.recentClaimIds,
1218
+ unverifiedClaimIds: postTurnArc.unverifiedClaimIds,
1219
+ });
1220
+ await refreshContextLossSentinelNonFatal({ agentName: (0, identity_1.getAgentName)(), agentRoot, trigger: "post_turn" });
1221
+ }
1222
+ catch (error) {
1223
+ /* v8 ignore next -- defensive recorder failures are non-fatal to already-persisted user turns @preserve */
1224
+ (0, runtime_1.emitNervesEvent)({
1225
+ level: "warn",
1226
+ component: "senses",
1227
+ event: "senses.flight_recorder_checkpoint_error",
1228
+ message: "failed to record post-turn flight recorder checkpoint",
1229
+ meta: { error: error instanceof Error ? error.message : String(error) },
1230
+ });
1231
+ }
1232
+ // Step 7: Token accumulation
1233
+ await input.accumulateFriendTokens(input.friendStore, resolvedContext.friend.id, result.usage);
1234
+ (0, runtime_1.emitNervesEvent)({
1235
+ component: "senses",
1236
+ event: "senses.pipeline_end",
1237
+ message: "inbound turn pipeline completed",
1238
+ meta: {
1239
+ channel: input.channel,
1240
+ friendId: resolvedContext.friend.id,
1241
+ },
1242
+ });
1243
+ return {
1244
+ resolvedContext,
1245
+ gateResult,
1246
+ usage: result.usage,
1247
+ turnOutcome: result.outcome,
1248
+ completion: result.completion,
1249
+ sessionPath: session.sessionPath,
1250
+ messages: sessionMessages,
1251
+ drainedPending: pending,
1252
+ ...(input.switchedProvider ? { switchedProvider: input.switchedProvider } : {}),
1253
+ };
246
1254
  }
247
- // Append user messages from the inbound turn
248
- for (const msg of input.messages) {
249
- sessionMessages.push(msg);
1255
+ finally {
1256
+ // Step 6b: Post-turn sync push (opt-in, git-status-based discovery).
1257
+ if (syncConfig.enabled && !liveLatencyMode && !declinedForHome) {
1258
+ (0, sync_1.postTurnPush)((0, identity_1.getAgentRoot)(), syncConfig);
1259
+ }
250
1260
  }
251
- // Step 5: runAgent
252
- const existingToolContext = input.runAgentOptions?.toolContext;
253
- const runAgentOptions = {
254
- ...input.runAgentOptions,
255
- bridgeContext,
256
- activeWorkFrame,
257
- delegationDecision,
258
- currentSessionKey: currentSession.key,
259
- currentObligation,
260
- mustResolveBeforeHandoff,
261
- setMustResolveBeforeHandoff: (value) => {
262
- mustResolveBeforeHandoff = value;
263
- },
264
- toolContext: {
265
- /* v8 ignore next -- default no-op signin satisfies interface; real signin injected by sense adapter @preserve */
266
- signin: async () => undefined,
267
- ...existingToolContext,
268
- context: resolvedContext,
269
- friendStore: input.friendStore,
270
- currentSession,
271
- activeBridges,
272
- },
273
- };
274
- const result = await input.runAgent(sessionMessages, input.callbacks, input.channel, input.signal, runAgentOptions);
275
- // Step 6: postTurn
276
- const continuingState = {
277
- ...(mustResolveBeforeHandoff ? { mustResolveBeforeHandoff: true } : {}),
278
- ...(typeof lastFriendActivityAt === "string" ? { lastFriendActivityAt } : {}),
279
- };
280
- const nextState = result.outcome === "complete" || result.outcome === "blocked" || result.outcome === "superseded" || result.outcome === "no_response"
281
- ? (typeof lastFriendActivityAt === "string"
282
- ? { lastFriendActivityAt }
283
- : undefined)
284
- : (Object.keys(continuingState).length > 0 ? continuingState : undefined);
285
- input.postTurn(sessionMessages, session.sessionPath, result.usage, undefined, nextState);
286
- // Step 7: Token accumulation
287
- await input.accumulateFriendTokens(input.friendStore, resolvedContext.friend.id, result.usage);
288
- (0, runtime_1.emitNervesEvent)({
289
- component: "senses",
290
- event: "senses.pipeline_end",
291
- message: "inbound turn pipeline completed",
292
- meta: {
293
- channel: input.channel,
294
- friendId: resolvedContext.friend.id,
295
- },
296
- });
297
- return {
298
- resolvedContext,
299
- gateResult,
300
- usage: result.usage,
301
- turnOutcome: result.outcome,
302
- completion: result.completion,
303
- sessionPath: session.sessionPath,
304
- messages: sessionMessages,
305
- drainedPending: pending,
306
- };
307
1261
  }