@ouro.bot/cli 0.1.0-alpha.66 → 0.1.0-alpha.660

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 (449) hide show
  1. package/README.md +127 -23
  2. package/RepairGuide.ouro/agent.json +5 -0
  3. package/RepairGuide.ouro/psyche/IDENTITY.md +19 -0
  4. package/RepairGuide.ouro/psyche/SOUL.md +55 -0
  5. package/RepairGuide.ouro/skills/diagnose-broken-remote.md +63 -0
  6. package/RepairGuide.ouro/skills/diagnose-stacked-typed-issues.md +35 -0
  7. package/RepairGuide.ouro/skills/diagnose-sync-blocked.md +54 -0
  8. package/RepairGuide.ouro/skills/diagnose-vault-expired.md +60 -0
  9. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +4 -2
  10. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +2 -2
  11. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
  12. package/changelog.json +4209 -13
  13. package/dist/a2a/card.js +56 -0
  14. package/dist/a2a/client.js +143 -0
  15. package/dist/a2a/config.js +50 -0
  16. package/dist/a2a/onboarding.js +111 -0
  17. package/dist/a2a/server.js +498 -0
  18. package/dist/a2a/task-store.js +69 -0
  19. package/dist/a2a/types.js +3 -0
  20. package/dist/arc/attention-types.js +8 -0
  21. package/dist/arc/cares.js +144 -0
  22. package/dist/arc/episodes.js +118 -0
  23. package/dist/arc/evolution.js +487 -0
  24. package/dist/arc/flight-recorder.js +369 -0
  25. package/dist/arc/intentions.js +134 -0
  26. package/dist/arc/json-store.js +117 -0
  27. package/dist/arc/obligations.js +292 -0
  28. package/dist/arc/packets.js +288 -0
  29. package/dist/arc/presence.js +185 -0
  30. package/dist/arc/task-lifecycle.js +57 -0
  31. package/dist/commerce/store.js +755 -0
  32. package/dist/commerce/types.js +3 -0
  33. package/dist/heart/active-work.js +860 -43
  34. package/dist/heart/agent-entry.js +69 -3
  35. package/dist/heart/attachments/image-normalize.js +194 -0
  36. package/dist/heart/attachments/materialize.js +97 -0
  37. package/dist/heart/attachments/originals.js +88 -0
  38. package/dist/heart/attachments/render.js +29 -0
  39. package/dist/heart/attachments/sources/bluebubbles.js +156 -0
  40. package/dist/heart/attachments/sources/cli-local-file.js +78 -0
  41. package/dist/heart/attachments/sources/index.js +16 -0
  42. package/dist/heart/attachments/store.js +103 -0
  43. package/dist/heart/attachments/types.js +93 -0
  44. package/dist/heart/auth/auth-flow.js +479 -0
  45. package/dist/heart/awaiting/await-alert.js +146 -0
  46. package/dist/heart/awaiting/await-expiry.js +108 -0
  47. package/dist/heart/awaiting/await-loader.js +91 -0
  48. package/dist/heart/awaiting/await-parser.js +141 -0
  49. package/dist/heart/awaiting/await-runtime-state.js +100 -0
  50. package/dist/heart/awaiting/await-scheduler.js +377 -0
  51. package/dist/heart/background-operations.js +281 -0
  52. package/dist/heart/bridges/manager.js +137 -17
  53. package/dist/heart/bridges/store.js +14 -2
  54. package/dist/heart/bundle-state.js +168 -0
  55. package/dist/heart/commitments.js +135 -0
  56. package/dist/heart/config-registry.js +331 -0
  57. package/dist/heart/config.js +118 -119
  58. package/dist/heart/context-loss-gauntlet.js +354 -0
  59. package/dist/heart/core.js +1123 -247
  60. package/dist/heart/cross-chat-delivery.js +3 -18
  61. package/dist/heart/daemon/agent-config-check.js +419 -0
  62. package/dist/heart/daemon/agent-discovery.js +102 -3
  63. package/dist/heart/daemon/agent-service.js +523 -0
  64. package/dist/heart/daemon/agentic-repair.js +547 -0
  65. package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
  66. package/dist/heart/daemon/boot-sync-probe.js +197 -0
  67. package/dist/heart/daemon/cadence.js +70 -0
  68. package/dist/heart/daemon/cli-defaults.js +780 -0
  69. package/dist/heart/daemon/cli-desk.js +322 -0
  70. package/dist/heart/daemon/cli-exec.js +7767 -0
  71. package/dist/heart/daemon/cli-help.js +558 -0
  72. package/dist/heart/daemon/cli-parse.js +1688 -0
  73. package/dist/heart/daemon/cli-render-doctor.js +57 -0
  74. package/dist/heart/daemon/cli-render.js +763 -0
  75. package/dist/heart/daemon/cli-types.js +8 -0
  76. package/dist/heart/daemon/connect-bay.js +323 -0
  77. package/dist/heart/daemon/daemon-cli.js +29 -1750
  78. package/dist/heart/daemon/daemon-entry.js +485 -2
  79. package/dist/heart/daemon/daemon-health.js +176 -0
  80. package/dist/heart/daemon/daemon-rollup.js +57 -0
  81. package/dist/heart/daemon/daemon-runtime-sync.js +88 -13
  82. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  83. package/dist/heart/daemon/daemon.js +937 -74
  84. package/dist/heart/daemon/dns-workflow.js +394 -0
  85. package/dist/heart/daemon/doctor-types.js +8 -0
  86. package/dist/heart/daemon/doctor.js +873 -0
  87. package/dist/heart/daemon/health-monitor.js +122 -1
  88. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  89. package/dist/heart/daemon/hooks/bundle-meta.js +135 -1
  90. package/dist/heart/daemon/http-health-probe.js +80 -0
  91. package/dist/heart/daemon/human-command-screens.js +234 -0
  92. package/dist/heart/daemon/human-readiness.js +114 -0
  93. package/dist/heart/daemon/inner-status.js +78 -0
  94. package/dist/heart/daemon/interactive-repair.js +394 -0
  95. package/dist/heart/daemon/launchd.js +37 -8
  96. package/dist/heart/daemon/log-tailer.js +79 -10
  97. package/dist/heart/daemon/logs-prune.js +110 -0
  98. package/dist/heart/daemon/mcp-canary.js +297 -0
  99. package/dist/heart/daemon/message-router.js +6 -2
  100. package/dist/heart/daemon/migrate-to-desk.js +848 -0
  101. package/dist/heart/daemon/os-cron-deps.js +135 -0
  102. package/dist/heart/daemon/os-cron.js +14 -12
  103. package/dist/heart/daemon/ouro-bot-entry.js +4 -2
  104. package/dist/heart/daemon/ouro-entry.js +3 -1
  105. package/dist/heart/daemon/plugin-cli.js +432 -0
  106. package/dist/heart/daemon/process-manager.js +511 -40
  107. package/dist/heart/daemon/provider-discovery.js +137 -0
  108. package/dist/heart/daemon/provider-ping-progress.js +83 -0
  109. package/dist/heart/daemon/pulse.js +475 -0
  110. package/dist/heart/daemon/readiness-repair.js +365 -0
  111. package/dist/heart/daemon/run-hooks.js +2 -0
  112. package/dist/heart/daemon/runtime-logging.js +35 -14
  113. package/dist/heart/daemon/runtime-metadata.js +2 -30
  114. package/dist/heart/daemon/safe-mode.js +161 -0
  115. package/dist/heart/daemon/sense-manager.js +564 -38
  116. package/dist/heart/daemon/session-id-resolver.js +131 -0
  117. package/dist/heart/daemon/skill-management-installer.js +1 -1
  118. package/dist/heart/daemon/socket-client.js +158 -11
  119. package/dist/heart/daemon/stale-bundle-prune.js +96 -0
  120. package/dist/heart/daemon/startup-tui.js +330 -0
  121. package/dist/heart/daemon/task-scheduler.js +117 -39
  122. package/dist/heart/daemon/terminal-ui.js +499 -0
  123. package/dist/heart/daemon/thoughts.js +229 -17
  124. package/dist/heart/daemon/up-progress.js +366 -0
  125. package/dist/heart/daemon/vault-items.js +56 -0
  126. package/dist/heart/delegation.js +1 -4
  127. package/dist/heart/habits/habit-migration.js +189 -0
  128. package/dist/heart/habits/habit-parser.js +203 -0
  129. package/dist/heart/habits/habit-runtime-state.js +100 -0
  130. package/dist/heart/habits/habit-scheduler.js +372 -0
  131. package/dist/heart/{daemon → hatch}/hatch-flow.js +40 -56
  132. package/dist/heart/{daemon → hatch}/hatch-specialist.js +6 -8
  133. package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
  134. package/dist/heart/{daemon → hatch}/specialist-tools.js +45 -18
  135. package/dist/heart/identity.js +174 -57
  136. package/dist/heart/kept-notes.js +289 -0
  137. package/dist/heart/kicks.js +1 -1
  138. package/dist/heart/machine-identity.js +161 -0
  139. package/dist/heart/mail-import-discovery.js +353 -0
  140. package/dist/heart/mailbox/mailbox-http-hooks.js +67 -0
  141. package/dist/heart/mailbox/mailbox-http-response.js +7 -0
  142. package/dist/heart/mailbox/mailbox-http-routes.js +250 -0
  143. package/dist/heart/mailbox/mailbox-http-static.js +103 -0
  144. package/dist/heart/mailbox/mailbox-http-transport.js +116 -0
  145. package/dist/heart/mailbox/mailbox-http.js +99 -0
  146. package/dist/heart/mailbox/mailbox-read.js +32 -0
  147. package/dist/heart/mailbox/mailbox-types.js +27 -0
  148. package/dist/heart/mailbox/mailbox-view.js +197 -0
  149. package/dist/heart/mailbox/readers/agent-machine.js +418 -0
  150. package/dist/heart/mailbox/readers/continuity-readers.js +324 -0
  151. package/dist/heart/mailbox/readers/mail.js +375 -0
  152. package/dist/heart/mailbox/readers/runtime-readers.js +728 -0
  153. package/dist/heart/mailbox/readers/sessions.js +232 -0
  154. package/dist/heart/mailbox/readers/shared.js +111 -0
  155. package/dist/heart/mcp/mcp-server.js +696 -0
  156. package/dist/heart/migrate-config.js +100 -0
  157. package/dist/heart/model-capabilities.js +19 -0
  158. package/dist/heart/orientation-frame.js +217 -0
  159. package/dist/heart/platform.js +81 -0
  160. package/dist/heart/provider-attempt.js +134 -0
  161. package/dist/heart/provider-binding-resolver.js +272 -0
  162. package/dist/heart/provider-credentials.js +425 -0
  163. package/dist/heart/provider-failover.js +311 -0
  164. package/dist/heart/provider-models.js +81 -0
  165. package/dist/heart/provider-ping.js +262 -0
  166. package/dist/heart/provider-readiness-cache.js +40 -0
  167. package/dist/heart/provider-visibility.js +188 -0
  168. package/dist/heart/providers/anthropic-token.js +131 -0
  169. package/dist/heart/providers/anthropic.js +139 -52
  170. package/dist/heart/providers/azure.js +23 -11
  171. package/dist/heart/providers/error-classification.js +127 -0
  172. package/dist/heart/providers/github-copilot.js +145 -0
  173. package/dist/heart/providers/minimax-vlm.js +189 -0
  174. package/dist/heart/providers/minimax.js +26 -8
  175. package/dist/heart/providers/openai-codex-token.js +349 -0
  176. package/dist/heart/providers/openai-codex.js +55 -40
  177. package/dist/heart/runtime-capability-check.js +170 -0
  178. package/dist/heart/runtime-credentials.js +367 -0
  179. package/dist/heart/runtime-cwd.js +87 -0
  180. package/dist/heart/sense-truth.js +17 -4
  181. package/dist/heart/session-activity.js +48 -24
  182. package/dist/heart/session-events.js +1133 -0
  183. package/dist/heart/session-playback-cli-main.js +5 -0
  184. package/dist/heart/session-playback-cli.js +36 -0
  185. package/dist/heart/session-playback.js +231 -0
  186. package/dist/heart/session-stats-cli-main.js +5 -0
  187. package/dist/heart/session-stats.js +182 -0
  188. package/dist/heart/session-transcript.js +133 -0
  189. package/dist/heart/start-of-turn-packet.js +351 -0
  190. package/dist/heart/streaming.js +44 -27
  191. package/dist/heart/structured-output.js +196 -0
  192. package/dist/heart/sync-classification.js +176 -0
  193. package/dist/heart/sync.js +449 -0
  194. package/dist/heart/target-resolution.js +9 -5
  195. package/dist/heart/tempo.js +93 -0
  196. package/dist/heart/temporal-view.js +41 -0
  197. package/dist/heart/timeouts.js +101 -0
  198. package/dist/heart/tool-activity-callbacks.js +59 -0
  199. package/dist/heart/tool-description.js +155 -0
  200. package/dist/heart/tool-friction.js +55 -0
  201. package/dist/heart/tool-loop.js +200 -0
  202. package/dist/heart/turn-context.js +430 -0
  203. package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +6 -5
  204. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  205. package/dist/heart/versioning/ouro-path-installer.js +426 -0
  206. package/dist/heart/versioning/ouro-version-manager.js +409 -0
  207. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  208. package/dist/heart/{daemon → versioning}/update-checker.js +6 -1
  209. package/dist/heart/versioning/update-hooks.js +154 -0
  210. package/dist/heart/work-card.js +386 -0
  211. package/dist/mailbox-ui/assets/index-B-V9vRQ0.js +61 -0
  212. package/dist/mailbox-ui/assets/index-BOZbGbkL.css +1 -0
  213. package/dist/mailbox-ui/index.html +15 -0
  214. package/dist/mailroom/attention.js +167 -0
  215. package/dist/mailroom/autonomy.js +209 -0
  216. package/dist/mailroom/blob-store.js +715 -0
  217. package/dist/mailroom/body-cache.js +61 -0
  218. package/dist/mailroom/core.js +788 -0
  219. package/dist/mailroom/entry.js +160 -0
  220. package/dist/mailroom/file-store.js +568 -0
  221. package/dist/mailroom/mbox-import.js +393 -0
  222. package/dist/mailroom/migration.js +164 -0
  223. package/dist/mailroom/outbound.js +380 -0
  224. package/dist/mailroom/policy.js +263 -0
  225. package/dist/mailroom/reader.js +233 -0
  226. package/dist/mailroom/search-cache.js +334 -0
  227. package/dist/mailroom/search-relevance.js +319 -0
  228. package/dist/mailroom/smtp-ingress.js +176 -0
  229. package/dist/mailroom/source-state.js +176 -0
  230. package/dist/mailroom/thread.js +109 -0
  231. package/dist/mailroom/travel-extract.js +89 -0
  232. package/dist/mind/bundle-manifest.js +21 -2
  233. package/dist/mind/context.js +250 -101
  234. package/dist/mind/desk-section.js +362 -0
  235. package/dist/mind/diary-integrity.js +60 -0
  236. package/dist/mind/{memory.js → diary.js} +68 -77
  237. package/dist/mind/embedding-provider.js +60 -0
  238. package/dist/mind/file-state.js +179 -0
  239. package/dist/mind/friends/channel.js +48 -0
  240. package/dist/mind/friends/resolver.js +67 -4
  241. package/dist/mind/friends/store-file.js +61 -4
  242. package/dist/mind/friends/types.js +2 -2
  243. package/dist/mind/{associative-recall.js → note-search.js} +47 -58
  244. package/dist/mind/obligation-steering.js +221 -0
  245. package/dist/mind/pending.js +6 -1
  246. package/dist/mind/prompt-refresh.js +3 -2
  247. package/dist/mind/prompt.js +1015 -140
  248. package/dist/mind/provenance-trust.js +26 -0
  249. package/dist/mind/record-paths.js +312 -0
  250. package/dist/mind/scrutiny.js +173 -0
  251. package/dist/nerves/cli-logging.js +7 -1
  252. package/dist/nerves/coverage/audit-rules.js +15 -6
  253. package/dist/nerves/coverage/audit.js +28 -2
  254. package/dist/nerves/coverage/cli.js +1 -1
  255. package/dist/nerves/coverage/contract.js +5 -5
  256. package/dist/nerves/coverage/file-completeness.js +139 -5
  257. package/dist/nerves/event-buffer.js +111 -0
  258. package/dist/nerves/index.js +224 -4
  259. package/dist/nerves/observation.js +20 -0
  260. package/dist/nerves/redact.js +79 -0
  261. package/dist/nerves/review/cli-main.js +5 -0
  262. package/dist/nerves/review/cli.js +156 -0
  263. package/dist/nerves/review/core.js +152 -0
  264. package/dist/nerves/runtime.js +5 -1
  265. package/dist/repertoire/ado-client.js +15 -56
  266. package/dist/repertoire/ado-semantic.js +16 -10
  267. package/dist/repertoire/api-client.js +97 -0
  268. package/dist/repertoire/bitwarden-store.js +1041 -0
  269. package/dist/repertoire/bundle-templates.js +71 -0
  270. package/dist/repertoire/bw-installer.js +180 -0
  271. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  272. package/dist/repertoire/coding/context-pack.js +331 -0
  273. package/dist/repertoire/coding/feedback.js +197 -30
  274. package/dist/repertoire/coding/manager.js +166 -10
  275. package/dist/repertoire/coding/spawner.js +55 -9
  276. package/dist/repertoire/coding/tools.js +219 -7
  277. package/dist/repertoire/commerce-errors.js +109 -0
  278. package/dist/repertoire/commerce-self-test.js +156 -0
  279. package/dist/repertoire/credential-access.js +178 -0
  280. package/dist/repertoire/desk/classifier.js +362 -0
  281. package/dist/repertoire/duffel-client.js +185 -0
  282. package/dist/repertoire/github-client.js +14 -55
  283. package/dist/repertoire/graph-client.js +11 -52
  284. package/dist/repertoire/guardrails.js +159 -25
  285. package/dist/repertoire/mcp-client.js +295 -0
  286. package/dist/repertoire/mcp-manager.js +434 -0
  287. package/dist/repertoire/mcp-tools.js +83 -0
  288. package/dist/repertoire/plugin-mcp.js +175 -0
  289. package/dist/repertoire/plugins.js +253 -0
  290. package/dist/repertoire/shell-sessions.js +133 -0
  291. package/dist/repertoire/skills.js +48 -4
  292. package/dist/repertoire/stripe-client.js +131 -0
  293. package/dist/repertoire/tool-results.js +29 -0
  294. package/dist/repertoire/tools-a2a.js +283 -0
  295. package/dist/repertoire/tools-attachments.js +317 -0
  296. package/dist/repertoire/tools-awaiting.js +372 -0
  297. package/dist/repertoire/tools-base.js +63 -1082
  298. package/dist/repertoire/tools-bluebubbles.js +2 -0
  299. package/dist/repertoire/tools-bridge.js +144 -0
  300. package/dist/repertoire/tools-bundle.js +993 -0
  301. package/dist/repertoire/tools-commerce.js +253 -0
  302. package/dist/repertoire/tools-config.js +186 -0
  303. package/dist/repertoire/tools-continuity.js +252 -0
  304. package/dist/repertoire/tools-credential.js +383 -0
  305. package/dist/repertoire/tools-evolution.js +527 -0
  306. package/dist/repertoire/tools-files.js +344 -0
  307. package/dist/repertoire/tools-flight.js +290 -0
  308. package/dist/repertoire/tools-flow.js +119 -0
  309. package/dist/repertoire/tools-github.js +3 -8
  310. package/dist/repertoire/tools-mail.js +1975 -0
  311. package/dist/repertoire/tools-notes.js +418 -0
  312. package/dist/repertoire/tools-obligations.js +143 -0
  313. package/dist/repertoire/tools-orientation.js +31 -0
  314. package/dist/repertoire/tools-record.js +469 -0
  315. package/dist/repertoire/tools-runtime.js +150 -0
  316. package/dist/repertoire/tools-session.js +766 -0
  317. package/dist/repertoire/tools-shell.js +120 -0
  318. package/dist/repertoire/tools-stripe.js +224 -0
  319. package/dist/repertoire/tools-surface.js +344 -0
  320. package/dist/repertoire/tools-teams.js +12 -39
  321. package/dist/repertoire/tools-travel.js +125 -0
  322. package/dist/repertoire/tools-trip.js +982 -0
  323. package/dist/repertoire/tools-user-profile.js +146 -0
  324. package/dist/repertoire/tools-vault.js +40 -0
  325. package/dist/repertoire/tools-voice.js +145 -0
  326. package/dist/repertoire/tools.js +243 -79
  327. package/dist/repertoire/travel-api-client.js +360 -0
  328. package/dist/repertoire/user-profile.js +131 -0
  329. package/dist/repertoire/vault-setup.js +246 -0
  330. package/dist/repertoire/vault-unlock.js +594 -0
  331. package/dist/scripts/claude-code-hook.js +41 -0
  332. package/dist/scripts/claude-code-stop-hook.js +47 -0
  333. package/dist/senses/a2a-entry.js +78 -0
  334. package/dist/senses/attention-queue.js +186 -0
  335. package/dist/senses/await-turn-message.js +58 -0
  336. package/dist/senses/bluebubbles/active-turns.js +216 -0
  337. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  338. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  339. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +219 -18
  340. package/dist/senses/bluebubbles/entry.js +77 -0
  341. package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +20 -3
  342. package/dist/senses/bluebubbles/index.js +2737 -0
  343. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -71
  344. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +33 -12
  345. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +3 -3
  346. package/dist/senses/bluebubbles/processed-log.js +133 -0
  347. package/dist/senses/bluebubbles/replay.js +137 -0
  348. package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +30 -2
  349. package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
  350. package/dist/senses/bluebubbles-meta-guard.js +40 -0
  351. package/dist/senses/cli/bracketed-paste.js +82 -0
  352. package/dist/senses/cli/image-paste.js +287 -0
  353. package/dist/senses/cli/image-ref-navigation.js +75 -0
  354. package/dist/senses/cli/ink-app.js +156 -0
  355. package/dist/senses/cli/inline-diff.js +64 -0
  356. package/dist/senses/cli/input-keys.js +174 -0
  357. package/dist/senses/cli/kill-ring.js +86 -0
  358. package/dist/senses/cli/message-list.js +51 -0
  359. package/dist/senses/cli/ouro-tui.js +607 -0
  360. package/dist/senses/cli/spinner-imperative.js +135 -0
  361. package/dist/senses/cli/spinner.js +101 -0
  362. package/dist/senses/cli/status-line.js +60 -0
  363. package/dist/senses/cli/streaming-markdown.js +526 -0
  364. package/dist/senses/cli/tool-display.js +85 -0
  365. package/dist/senses/cli/tool-render.js +85 -0
  366. package/dist/senses/cli/tui-store.js +240 -0
  367. package/dist/senses/cli/virtual-list.js +35 -0
  368. package/dist/senses/cli-entry.js +60 -8
  369. package/dist/senses/cli-layout.js +100 -0
  370. package/dist/senses/cli.js +517 -204
  371. package/dist/senses/commands.js +66 -3
  372. package/dist/senses/habit-turn-message.js +122 -0
  373. package/dist/senses/inner-dialog-worker.js +303 -22
  374. package/dist/senses/inner-dialog.js +525 -41
  375. package/dist/senses/mail-entry.js +66 -0
  376. package/dist/senses/mail.js +379 -0
  377. package/dist/senses/pipeline.js +857 -180
  378. package/dist/senses/proactive-content-guard.js +51 -0
  379. package/dist/senses/shared-turn.js +419 -0
  380. package/dist/senses/surface-tool.js +108 -0
  381. package/dist/senses/teams-entry.js +60 -8
  382. package/dist/senses/teams.js +390 -98
  383. package/dist/senses/trust-gate.js +100 -5
  384. package/dist/senses/voice/audio-playback.js +237 -0
  385. package/dist/senses/voice/audio-routing.js +119 -0
  386. package/dist/senses/voice/elevenlabs.js +202 -0
  387. package/dist/senses/voice/floor-control.js +431 -0
  388. package/dist/senses/voice/floor-controller.js +115 -0
  389. package/dist/senses/voice/golden-path.js +116 -0
  390. package/dist/senses/voice/index.js +29 -0
  391. package/dist/senses/voice/meeting.js +113 -0
  392. package/dist/senses/voice/outbound.js +190 -0
  393. package/dist/senses/voice/phone.js +33 -0
  394. package/dist/senses/voice/playback.js +139 -0
  395. package/dist/senses/voice/realtime-eval.js +496 -0
  396. package/dist/senses/voice/realtime-trace.js +531 -0
  397. package/dist/senses/voice/transcript.js +70 -0
  398. package/dist/senses/voice/turn.js +191 -0
  399. package/dist/senses/voice/twilio-phone-runtime.js +807 -0
  400. package/dist/senses/voice/twilio-phone.js +5079 -0
  401. package/dist/senses/voice/types.js +2 -0
  402. package/dist/senses/voice/whisper.js +161 -0
  403. package/dist/senses/voice-entry.js +81 -0
  404. package/dist/senses/voice-realtime-eval-command.js +99 -0
  405. package/dist/senses/voice-realtime-eval-entry.js +21 -0
  406. package/dist/senses/voice-twilio-entry.js +87 -0
  407. package/dist/trips/core.js +138 -0
  408. package/dist/trips/store.js +265 -0
  409. package/dist/util/frontmatter.js +69 -0
  410. package/package.json +55 -12
  411. package/skills/agent-commerce.md +113 -0
  412. package/skills/browser-navigation.md +117 -0
  413. package/skills/commerce-setup-guide.md +116 -0
  414. package/skills/commerce-setup.md +84 -0
  415. package/skills/configure-dev-tools.md +99 -0
  416. package/skills/travel-planning.md +138 -0
  417. package/dist/heart/daemon/auth-flow.js +0 -351
  418. package/dist/heart/daemon/ouro-path-installer.js +0 -178
  419. package/dist/heart/daemon/update-hooks.js +0 -138
  420. package/dist/heart/safe-workspace.js +0 -228
  421. package/dist/heart/session-recall.js +0 -116
  422. package/dist/repertoire/tasks/board.js +0 -134
  423. package/dist/repertoire/tasks/index.js +0 -224
  424. package/dist/repertoire/tasks/lifecycle.js +0 -80
  425. package/dist/repertoire/tasks/middleware.js +0 -65
  426. package/dist/repertoire/tasks/parser.js +0 -173
  427. package/dist/repertoire/tasks/scanner.js +0 -132
  428. package/dist/repertoire/tasks/transitions.js +0 -144
  429. package/dist/senses/bluebubbles-entry.js +0 -13
  430. package/dist/senses/bluebubbles.js +0 -1177
  431. package/dist/senses/debug-activity.js +0 -148
  432. package/subagents/README.md +0 -7
  433. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  434. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  435. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  436. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  437. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  438. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  439. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  440. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  441. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  442. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  443. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  444. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  445. /package/dist/{repertoire/tasks/types.js → heart/attachments/sources/adapter.js} +0 -0
  446. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  447. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  448. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  449. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -3,7 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createCommandRegistry = createCommandRegistry;
4
4
  exports.getToolChoiceRequired = getToolChoiceRequired;
5
5
  exports.resetToolChoiceRequired = resetToolChoiceRequired;
6
+ exports.getDebugMode = getDebugMode;
7
+ exports.resetDebugMode = resetDebugMode;
6
8
  exports.registerDefaultCommands = registerDefaultCommands;
9
+ exports.getSharedCommandRegistry = getSharedCommandRegistry;
10
+ exports.resetSharedCommandRegistry = resetSharedCommandRegistry;
7
11
  exports.parseSlashCommand = parseSlashCommand;
8
12
  const identity_1 = require("../heart/identity");
9
13
  const runtime_1 = require("../nerves/runtime");
@@ -21,7 +25,7 @@ function createCommandRegistry() {
21
25
  },
22
26
  dispatch(name, ctx) {
23
27
  const cmd = commands.get(name);
24
- if (!cmd)
28
+ if (!cmd || !cmd.channels.includes(ctx.channel))
25
29
  return { handled: false };
26
30
  return { handled: true, result: cmd.handler(ctx) };
27
31
  },
@@ -35,6 +39,14 @@ function getToolChoiceRequired() {
35
39
  function resetToolChoiceRequired() {
36
40
  _toolChoiceRequired = false;
37
41
  }
42
+ // Module-level toggle for debug mode
43
+ let _debugMode = false;
44
+ function getDebugMode() {
45
+ return _debugMode;
46
+ }
47
+ function resetDebugMode() {
48
+ _debugMode = false;
49
+ }
38
50
  function registerDefaultCommands(registry) {
39
51
  (0, runtime_1.emitNervesEvent)({
40
52
  event: "repertoire.load_start",
@@ -51,7 +63,7 @@ function registerDefaultCommands(registry) {
51
63
  registry.register({
52
64
  name: "new",
53
65
  description: "start a new conversation",
54
- channels: ["cli", "teams"],
66
+ channels: ["teams"],
55
67
  handler: () => ({ action: "new" }),
56
68
  });
57
69
  registry.register({
@@ -60,10 +72,36 @@ function registerDefaultCommands(registry) {
60
72
  channels: ["cli", "teams"],
61
73
  handler: (ctx) => {
62
74
  const cmds = registry.list(ctx.channel);
63
- const lines = cmds.map((c) => `/${c.name} - ${c.description}`);
75
+ const lines = cmds.map((c) => `/${c.name} \u2014 ${c.description}`);
64
76
  return { action: "response", message: lines.join("\n") };
65
77
  },
66
78
  });
79
+ registry.register({
80
+ name: "help",
81
+ description: "show keyboard shortcuts and tips",
82
+ channels: ["cli"],
83
+ /* v8 ignore start -- integration: /help command tested via E2E @preserve */
84
+ handler: () => ({
85
+ action: "response",
86
+ message: [
87
+ "Keyboard shortcuts:",
88
+ " Ctrl-C abort current generation / clear input / exit (twice within 2s)",
89
+ " Escape clear input",
90
+ " ↑ / ↓ browse input history (includes previous sessions)",
91
+ " Option+← / → jump between words",
92
+ " Ctrl+A / E jump to start / end of line",
93
+ " Option+Delete delete word backward",
94
+ " Option+D delete word forward",
95
+ " Alt+Enter insert newline (multi-line input)",
96
+ "",
97
+ "Commands:",
98
+ " /help this help",
99
+ " /commands list all commands",
100
+ " /exit quit",
101
+ ].join("\n"),
102
+ }),
103
+ /* v8 ignore stop */
104
+ });
67
105
  registry.register({
68
106
  name: "tool-required",
69
107
  description: "toggle tool_choice required mode (forces tool calls)",
@@ -73,6 +111,20 @@ function registerDefaultCommands(registry) {
73
111
  return { action: "response", message: `tool-required mode: ${_toolChoiceRequired ? "ON" : "OFF"}` };
74
112
  },
75
113
  });
114
+ registry.register({
115
+ name: "debug",
116
+ description: "toggle debug mode — see more detail about what I'm doing",
117
+ channels: ["cli", "teams", "bluebubbles", "mail", "mcp"],
118
+ handler: () => {
119
+ _debugMode = !_debugMode;
120
+ return {
121
+ action: "response",
122
+ message: _debugMode
123
+ ? "debug mode on — you'll see more detail about what I'm doing"
124
+ : "debug mode off — back to clean output",
125
+ };
126
+ },
127
+ });
76
128
  (0, runtime_1.emitNervesEvent)({
77
129
  event: "repertoire.load_end",
78
130
  component: "repertoire",
@@ -80,6 +132,17 @@ function registerDefaultCommands(registry) {
80
132
  meta: {},
81
133
  });
82
134
  }
135
+ let _sharedRegistry = null;
136
+ function getSharedCommandRegistry() {
137
+ if (!_sharedRegistry) {
138
+ _sharedRegistry = createCommandRegistry();
139
+ registerDefaultCommands(_sharedRegistry);
140
+ }
141
+ return _sharedRegistry;
142
+ }
143
+ function resetSharedCommandRegistry() {
144
+ _sharedRegistry = null;
145
+ }
83
146
  function parseSlashCommand(input) {
84
147
  const trimmed = input.trim();
85
148
  if (!trimmed.startsWith("/"))
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildHabitTurnMessage = buildHabitTurnMessage;
4
+ const runtime_1 = require("../nerves/runtime");
5
+ function formatElapsed(ms) {
6
+ const minutes = Math.floor(ms / 60000);
7
+ if (minutes < 60) {
8
+ return `${minutes} ${minutes === 1 ? "minute" : "minutes"}`;
9
+ }
10
+ const hours = Math.floor(minutes / 60);
11
+ return `${hours} ${hours === 1 ? "hour" : "hours"}`;
12
+ }
13
+ function buildHabitTurnMessage(options) {
14
+ const { habitName, habitTitle, habitBody, lastRun, checkpoint, alsoDue, staleObligations, parseErrors, degradedComponents, arcResume, deskOrientation, surfacePolicy, now, } = options;
15
+ const hasBody = habitBody !== undefined && habitBody !== "";
16
+ const leadingSections = buildLeadingSections(arcResume, deskOrientation, surfacePolicy);
17
+ // First beat: lastRun is null
18
+ if (lastRun === null) {
19
+ // Cold start: no checkpoint, no body — bare awareness
20
+ if (!checkpoint && !hasBody) {
21
+ (0, runtime_1.emitNervesEvent)({
22
+ component: "senses",
23
+ event: "senses.habit_turn_message_built",
24
+ message: "habit turn message built (cold start)",
25
+ meta: { habitName, coldStart: true },
26
+ });
27
+ return joinSections(leadingSections, ["...time passing. anything stirring?"]);
28
+ }
29
+ if (!hasBody) {
30
+ // First beat with no body: nudge
31
+ const sections = [
32
+ `your ${habitTitle} fired but has no instructions \u2014 add a body to \`habits/${habitName}.md\``,
33
+ ];
34
+ appendTrailingExtras(sections, alsoDue, staleObligations, parseErrors, degradedComponents);
35
+ (0, runtime_1.emitNervesEvent)({
36
+ component: "senses",
37
+ event: "senses.habit_turn_message_built",
38
+ message: "habit turn message built (first beat, no body)",
39
+ meta: { habitName, firstBeat: true, hasBody: false },
40
+ });
41
+ return joinSections(leadingSections, sections);
42
+ }
43
+ const sections = [
44
+ `your ${habitTitle} is alive. this is its first breath.`,
45
+ habitBody,
46
+ ];
47
+ appendTrailingExtras(sections, alsoDue, staleObligations, parseErrors, degradedComponents);
48
+ (0, runtime_1.emitNervesEvent)({
49
+ component: "senses",
50
+ event: "senses.habit_turn_message_built",
51
+ message: "habit turn message built (first beat)",
52
+ meta: { habitName, firstBeat: true },
53
+ });
54
+ return joinSections(leadingSections, sections);
55
+ }
56
+ // Normal turn
57
+ const sections = [];
58
+ // 1. Checkpoint
59
+ if (checkpoint) {
60
+ sections.push(`you were thinking about ${checkpoint}.`);
61
+ }
62
+ // 2. Elapsed time
63
+ const nowMs = now().getTime();
64
+ const lastRunMs = new Date(lastRun).getTime();
65
+ const elapsed = nowMs - lastRunMs;
66
+ sections.push(`${formatElapsed(elapsed)} have passed.`);
67
+ // 3. Body or no-body nudge
68
+ if (hasBody) {
69
+ sections.push(habitBody);
70
+ }
71
+ else {
72
+ sections.push(`your ${habitTitle} fired but has no instructions \u2014 add a body to \`habits/${habitName}.md\``);
73
+ }
74
+ // 4-7. Trailing extras
75
+ appendTrailingExtras(sections, alsoDue, staleObligations, parseErrors, degradedComponents);
76
+ (0, runtime_1.emitNervesEvent)({
77
+ component: "senses",
78
+ event: "senses.habit_turn_message_built",
79
+ message: "habit turn message built",
80
+ meta: {
81
+ habitName,
82
+ hasCheckpoint: !!checkpoint,
83
+ hasBody: hasBody,
84
+ staleObligationCount: staleObligations.length,
85
+ },
86
+ });
87
+ return joinSections(leadingSections, sections);
88
+ }
89
+ function buildLeadingSections(arcResume, deskOrientation, surfacePolicy) {
90
+ const sections = [];
91
+ if (arcResume?.trim())
92
+ sections.push(arcResume.trim());
93
+ if (deskOrientation?.trim())
94
+ sections.push(deskOrientation.trim());
95
+ if (surfacePolicy?.trim())
96
+ sections.push(surfacePolicy.trim());
97
+ return sections;
98
+ }
99
+ function joinSections(leadingSections, sections) {
100
+ return [...leadingSections, ...sections].filter((section) => section.trim().length > 0).join("\n\n");
101
+ }
102
+ function appendTrailingExtras(sections, alsoDue, staleObligations, parseErrors, degradedComponents) {
103
+ // 4. Also-due
104
+ if (alsoDue) {
105
+ sections.push(alsoDue);
106
+ }
107
+ // 5. Stale obligations
108
+ if (staleObligations.length > 0) {
109
+ const lines = staleObligations.map((o) => `[internal] obligation: ${o.friendName} — waiting ${formatElapsed(o.stalenessMs)}`);
110
+ sections.push(lines.join("\n"));
111
+ }
112
+ // 6. Parse errors
113
+ if (parseErrors.length > 0) {
114
+ const lines = parseErrors.map((e) => `I noticed my habit file \`${e.file}\` has invalid frontmatter \u2014 I should fix it. (${e.error})`);
115
+ sections.push(lines.join("\n"));
116
+ }
117
+ // 7. Degraded state
118
+ if (degradedComponents.length > 0) {
119
+ const reasons = degradedComponents.map((d) => `${d.component}: ${d.reason}`).join("; ");
120
+ sections.push(`[note: my scheduling is degraded: ${reasons}]`);
121
+ }
122
+ }
@@ -1,35 +1,242 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.HEARTBEAT_OK_REST_SUPPRESSION_MS = exports.HABIT_RECURSION_BURST_THRESHOLD = exports.HABIT_RECURSION_BURST_WINDOW_MS = exports.HABIT_RECURSION_MIN_INTERVAL_MS = exports.MAX_CONSECUTIVE_INSTINCT_TURNS = void 0;
3
37
  exports.createInnerDialogWorker = createInnerDialogWorker;
4
38
  exports.startInnerDialogWorker = startInnerDialogWorker;
39
+ const path = __importStar(require("path"));
5
40
  const inner_dialog_1 = require("./inner-dialog");
6
41
  const runtime_1 = require("../nerves/runtime");
7
42
  const identity_1 = require("../heart/identity");
8
43
  const pending_1 = require("../mind/pending");
9
- function createInnerDialogWorker(runTurn = (options) => (0, inner_dialog_1.runInnerDialogTurn)(options), hasPendingWork = () => (0, pending_1.hasPendingMessages)((0, pending_1.getInnerDialogPendingDir)((0, identity_1.getAgentName)()))) {
44
+ const habit_runtime_state_1 = require("../heart/habits/habit-runtime-state");
45
+ const flight_recorder_1 = require("../arc/flight-recorder");
46
+ /**
47
+ * Cap on consecutive `instinct` follow-on turns triggered by `hasPendingWork()`
48
+ * with no externally-queued work in between. Without this cap, a turn that
49
+ * writes anything back into the inner-dialog pending dir as a side effect of
50
+ * processing (e.g. a surface tool routing a response) puts the worker into
51
+ * a self-sustaining loop where the next turn's drain produces another write,
52
+ * and so on. Real workflows rarely chain more than 2–3 instinct turns; an
53
+ * external trigger (habit, poke, chat) resets the counter so legitimate
54
+ * follow-on work is unaffected.
55
+ *
56
+ * Three feels right: legitimate cascading follow-ups (e.g. processing a
57
+ * batch of delegated returns) get through; a true self-loop caps fast.
58
+ */
59
+ exports.MAX_CONSECUTIVE_INSTINCT_TURNS = 3;
60
+ /**
61
+ * Habit recursion detector thresholds. The instinct cap above protects
62
+ * against pending-dir self-loops; this protects against the *external*
63
+ * IPC self-loop where heartbeat-shaped messages get re-issued faster
64
+ * than their cadence — e.g. a hook misconfigured to repost on every
65
+ * heartbeat, a daemon retry storm, or a stuck timer firing back-to-back.
66
+ *
67
+ * MIN_INTERVAL_MS — two of the same habit within this window is suspect
68
+ * regardless of cadence (no realistic habit fires every few seconds).
69
+ * BURST_THRESHOLD over BURST_WINDOW_MS catches slower runaways that stay
70
+ * just under MIN_INTERVAL_MS.
71
+ *
72
+ * Detection is observation-only: it emits warn-level nerves events, it
73
+ * does not drop the message. An operator (or follow-up auto-recovery)
74
+ * decides what to do with the signal.
75
+ */
76
+ exports.HABIT_RECURSION_MIN_INTERVAL_MS = 5_000;
77
+ exports.HABIT_RECURSION_BURST_WINDOW_MS = 60_000;
78
+ exports.HABIT_RECURSION_BURST_THRESHOLD = 5;
79
+ exports.HEARTBEAT_OK_REST_SUPPRESSION_MS = 20 * 60_000;
80
+ function isHeartbeatOkRestResult(result) {
81
+ if (!result || typeof result !== "object")
82
+ return false;
83
+ const maybeResult = result;
84
+ return maybeResult.turnOutcome === "rested" && maybeResult.restStatus === "HEARTBEAT_OK";
85
+ }
86
+ function createInnerDialogWorker(runTurn = (options) => (0, inner_dialog_1.runInnerDialogTurn)(options), hasPendingWork = () => (0, pending_1.hasPendingMessages)((0, pending_1.getInnerDialogPendingDir)((0, identity_1.getAgentName)())), nowSource = () => Date.now()) {
10
87
  let running = false;
11
- let rerunRequested = false;
12
- let rerunReason = "instinct";
13
- let rerunTaskId;
14
- async function run(reason, taskId) {
15
- if (running) {
16
- rerunRequested = true;
17
- rerunReason = reason;
18
- if (taskId !== undefined) {
19
- rerunTaskId = taskId;
88
+ const queue = [];
89
+ const lastFireByHabit = new Map();
90
+ const recentHabitFires = [];
91
+ let heartbeatOkRestedAt = null;
92
+ function habitOutcomeForTurn(result, errors) {
93
+ if (errors.length > 0)
94
+ return { outcome: "error", producedRefs: [] };
95
+ const toolNames = new Set();
96
+ if (result && typeof result === "object" && Array.isArray(result.messages)) {
97
+ for (const message of result.messages) {
98
+ const toolCalls = message.tool_calls;
99
+ if (!Array.isArray(toolCalls))
100
+ continue;
101
+ for (const call of toolCalls) {
102
+ const functionName = call.function?.name;
103
+ if (typeof functionName === "string")
104
+ toolNames.add(functionName);
105
+ }
20
106
  }
107
+ }
108
+ if (toolNames.has("send_message") || toolNames.has("surface")) {
109
+ return { outcome: "surfaced", producedRefs: [{ kind: "surface", locator: "tool:send_message_or_surface" }] };
110
+ }
111
+ if (toolNames.has("diary_write") || toolNames.has("note")) {
112
+ return { outcome: "wrote_record", producedRefs: [{ kind: "desk_record", locator: "desk/_record" }] };
113
+ }
114
+ if ([...toolNames].some((name) => name.startsWith("mcp__desk__"))) {
115
+ return { outcome: "updated_desk", producedRefs: [{ kind: "desk_task", locator: "desk/" }] };
116
+ }
117
+ return { outcome: "no_change", producedRefs: [] };
118
+ }
119
+ function recordHabitCompletion(habitName, startedAt = new Date(nowSource()).toISOString(), endedAt = startedAt, trigger = "overdue", result, errors = []) {
120
+ try {
121
+ const agentRoot = (0, identity_1.getAgentRoot)();
122
+ (0, habit_runtime_state_1.recordHabitRun)(agentRoot, habitName, endedAt, {
123
+ definitionPath: path.join(agentRoot, "habits", `${habitName}.md`),
124
+ });
125
+ const { outcome, producedRefs } = habitOutcomeForTurn(result, errors);
126
+ (0, flight_recorder_1.writeHabitRunReceipt)(agentRoot, {
127
+ schemaVersion: 1,
128
+ runId: (0, flight_recorder_1.createHabitRunId)(habitName, new Date(startedAt)),
129
+ habitName,
130
+ trigger,
131
+ startedAt,
132
+ endedAt,
133
+ outcome,
134
+ producedRefs,
135
+ surfaceAttempts: [],
136
+ errors,
137
+ });
138
+ }
139
+ catch {
140
+ // Habit file/state may be unavailable during the turn — skip gracefully
141
+ }
142
+ }
143
+ function clearHeartbeatRestShield() {
144
+ heartbeatOkRestedAt = null;
145
+ }
146
+ function shouldReuseHeartbeatOkRest(habitName) {
147
+ if (habitName !== "heartbeat" || heartbeatOkRestedAt === null)
148
+ return false;
149
+ if (nowSource() - heartbeatOkRestedAt > exports.HEARTBEAT_OK_REST_SUPPRESSION_MS)
150
+ return false;
151
+ if (hasPendingWork()) {
152
+ clearHeartbeatRestShield();
153
+ return false;
154
+ }
155
+ return true;
156
+ }
157
+ function reuseHeartbeatOkRest(habitName) {
158
+ const nowIso = new Date(nowSource()).toISOString();
159
+ recordHabitCompletion(habitName, nowIso, nowIso, "overdue", { turnOutcome: "rested", restStatus: "HEARTBEAT_OK" });
160
+ (0, runtime_1.emitNervesEvent)({
161
+ level: "info",
162
+ component: "senses",
163
+ event: "senses.heartbeat_ok_rest_reused",
164
+ message: "heartbeat skipped because previous HEARTBEAT_OK rest is still valid and no pending work exists",
165
+ meta: {
166
+ habitName,
167
+ quietWindowMs: exports.HEARTBEAT_OK_REST_SUPPRESSION_MS,
168
+ restedAgoMs: nowSource() - heartbeatOkRestedAt,
169
+ },
170
+ });
171
+ }
172
+ function recordHabitFireForRecursion(habitName) {
173
+ const now = nowSource();
174
+ const previous = lastFireByHabit.get(habitName);
175
+ if (previous !== undefined) {
176
+ const intervalMs = now - previous;
177
+ if (intervalMs < exports.HABIT_RECURSION_MIN_INTERVAL_MS) {
178
+ (0, runtime_1.emitNervesEvent)({
179
+ level: "warn",
180
+ component: "senses",
181
+ event: "senses.habit_recursion_suspected",
182
+ message: "habit fired suspiciously fast after the previous fire — possible self-recursion or duplicate dispatch",
183
+ meta: {
184
+ habitName,
185
+ intervalMs,
186
+ thresholdMs: exports.HABIT_RECURSION_MIN_INTERVAL_MS,
187
+ },
188
+ });
189
+ }
190
+ }
191
+ lastFireByHabit.set(habitName, now);
192
+ recentHabitFires.push(now);
193
+ while (recentHabitFires.length > 0 && now - recentHabitFires[0] > exports.HABIT_RECURSION_BURST_WINDOW_MS) {
194
+ recentHabitFires.shift();
195
+ }
196
+ if (recentHabitFires.length >= exports.HABIT_RECURSION_BURST_THRESHOLD) {
197
+ (0, runtime_1.emitNervesEvent)({
198
+ level: "warn",
199
+ component: "senses",
200
+ event: "senses.habit_recursion_burst",
201
+ message: "habit messages arriving in a burst — possible runaway loop",
202
+ meta: {
203
+ count: recentHabitFires.length,
204
+ windowMs: exports.HABIT_RECURSION_BURST_WINDOW_MS,
205
+ thresholdCount: exports.HABIT_RECURSION_BURST_THRESHOLD,
206
+ lastHabitName: habitName,
207
+ },
208
+ });
209
+ }
210
+ }
211
+ async function run(reason, taskId, habitName, awaitName, trigger) {
212
+ if (running) {
213
+ queue.push({ reason, taskId, habitName, awaitName, trigger });
21
214
  return;
22
215
  }
23
216
  running = true;
24
217
  try {
25
218
  let nextReason = reason;
26
219
  let nextTaskId = taskId;
27
- do {
28
- rerunRequested = false;
220
+ let nextHabitName = habitName;
221
+ let nextAwaitName = awaitName;
222
+ let nextTrigger = trigger;
223
+ let consecutiveInstinctTurns = reason === "instinct" ? 1 : 0;
224
+ runLoop: do {
225
+ const currentReason = nextReason;
226
+ const currentHabitName = nextHabitName;
227
+ const currentTrigger = nextTrigger ?? "overdue";
228
+ const habitStartedAt = currentReason === "habit" && currentHabitName ? new Date(nowSource()).toISOString() : null;
229
+ const turnErrors = [];
230
+ if (!(currentReason === "habit" && currentHabitName === "heartbeat")) {
231
+ clearHeartbeatRestShield();
232
+ }
233
+ let turnResult;
29
234
  try {
30
- await runTurn({ reason: nextReason, taskId: nextTaskId });
235
+ turnResult = await runTurn({ reason: nextReason, taskId: nextTaskId, habitName: nextHabitName, awaitName: nextAwaitName });
31
236
  }
32
237
  catch (error) {
238
+ clearHeartbeatRestShield();
239
+ turnErrors.push(error instanceof Error ? error.message : String(error));
33
240
  (0, runtime_1.emitNervesEvent)({
34
241
  level: "error",
35
242
  component: "senses",
@@ -41,15 +248,62 @@ function createInnerDialogWorker(runTurn = (options) => (0, inner_dialog_1.runIn
41
248
  },
42
249
  });
43
250
  }
44
- if (!rerunRequested && hasPendingWork()) {
45
- rerunRequested = true;
46
- rerunReason = "instinct";
251
+ if (currentReason === "habit" && currentHabitName === "heartbeat") {
252
+ heartbeatOkRestedAt = isHeartbeatOkRestResult(turnResult) ? nowSource() : null;
253
+ }
254
+ // Record lastRun after a habit turn without dirtying the tracked habit file.
255
+ if (currentReason === "habit" && currentHabitName && habitStartedAt) {
256
+ recordHabitCompletion(currentHabitName, habitStartedAt, new Date(nowSource()).toISOString(), currentTrigger, turnResult, turnErrors);
47
257
  }
48
- nextReason = rerunReason;
49
- nextTaskId = rerunTaskId;
50
- rerunReason = "instinct";
51
- rerunTaskId = undefined;
52
- } while (rerunRequested);
258
+ // Drain queue first. Externally-queued work resets the instinct cap
259
+ // because a real outside trigger arrived between turns.
260
+ while (queue.length > 0) {
261
+ const next = queue.shift();
262
+ if (next.reason === "habit" && next.habitName === "heartbeat" && shouldReuseHeartbeatOkRest(next.habitName)) {
263
+ reuseHeartbeatOkRest(next.habitName);
264
+ continue;
265
+ }
266
+ if (!(next.reason === "habit" && next.habitName === "heartbeat")) {
267
+ clearHeartbeatRestShield();
268
+ }
269
+ nextReason = next.reason;
270
+ nextTaskId = next.taskId;
271
+ nextHabitName = next.habitName;
272
+ nextAwaitName = next.awaitName;
273
+ nextTrigger = next.trigger;
274
+ consecutiveInstinctTurns = nextReason === "instinct" ? consecutiveInstinctTurns + 1 : 0;
275
+ continue runLoop;
276
+ }
277
+ // Then check hasPendingWork fallback. This is the loop site: any
278
+ // tool that writes to the inner-dialog pending dir during a turn
279
+ // would cause hasPendingWork() to be true here, producing a
280
+ // self-sustaining "instinct" loop with no external input. Cap it.
281
+ if (hasPendingWork()) {
282
+ clearHeartbeatRestShield();
283
+ if (consecutiveInstinctTurns >= exports.MAX_CONSECUTIVE_INSTINCT_TURNS) {
284
+ (0, runtime_1.emitNervesEvent)({
285
+ level: "warn",
286
+ component: "senses",
287
+ event: "senses.inner_dialog_worker_instinct_loop_capped",
288
+ message: "inner dialog worker stopped chaining instinct turns; pending work remains for next external trigger",
289
+ meta: {
290
+ consecutiveInstinctTurns,
291
+ cap: exports.MAX_CONSECUTIVE_INSTINCT_TURNS,
292
+ lastReason: nextReason,
293
+ },
294
+ });
295
+ break;
296
+ }
297
+ consecutiveInstinctTurns += 1;
298
+ nextReason = "instinct";
299
+ nextTaskId = undefined;
300
+ nextHabitName = undefined;
301
+ nextAwaitName = undefined;
302
+ nextTrigger = undefined;
303
+ continue;
304
+ }
305
+ break;
306
+ } while (true);
53
307
  }
54
308
  finally {
55
309
  running = false;
@@ -59,16 +313,43 @@ function createInnerDialogWorker(runTurn = (options) => (0, inner_dialog_1.runIn
59
313
  if (!message || typeof message !== "object")
60
314
  return;
61
315
  const maybeMessage = message;
316
+ if (maybeMessage.type === "habit") {
317
+ /* v8 ignore next -- defensive fallback: live habit dispatch always sets habitName @preserve */
318
+ const habitName = maybeMessage.habitName ?? "(unnamed)";
319
+ if (shouldReuseHeartbeatOkRest(habitName)) {
320
+ reuseHeartbeatOkRest(habitName);
321
+ return;
322
+ }
323
+ recordHabitFireForRecursion(habitName);
324
+ await run("habit", undefined, maybeMessage.habitName, undefined, maybeMessage.trigger ?? "overdue");
325
+ return;
326
+ }
327
+ if (maybeMessage.type === "await") {
328
+ clearHeartbeatRestShield();
329
+ /* v8 ignore next -- defensive fallback: live await dispatch always sets awaitName @preserve */
330
+ const awaitName = maybeMessage.awaitName ?? "(unnamed)";
331
+ recordHabitFireForRecursion(`await:${awaitName}`);
332
+ await run("await", undefined, undefined, maybeMessage.awaitName);
333
+ return;
334
+ }
62
335
  if (maybeMessage.type === "heartbeat") {
63
- await run("heartbeat");
336
+ // Backward compatibility: heartbeat -> habit/heartbeat
337
+ if (shouldReuseHeartbeatOkRest("heartbeat")) {
338
+ reuseHeartbeatOkRest("heartbeat");
339
+ return;
340
+ }
341
+ recordHabitFireForRecursion("heartbeat");
342
+ await run("habit", undefined, "heartbeat", undefined, "overdue");
64
343
  return;
65
344
  }
66
345
  if (maybeMessage.type === "poke") {
346
+ clearHeartbeatRestShield();
67
347
  await run("instinct", maybeMessage.taskId);
68
348
  return;
69
349
  }
70
350
  if (maybeMessage.type === "chat" ||
71
351
  maybeMessage.type === "message") {
352
+ clearHeartbeatRestShield();
72
353
  await run("instinct");
73
354
  return;
74
355
  }