@ouro.bot/cli 0.1.0-alpha.99 → 0.1.0-alpha.990

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 (772) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +171 -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 +6251 -16
  16. package/deploy/unraid/Dockerfile +58 -0
  17. package/deploy/unraid/INSTALL.md +64 -0
  18. package/deploy/unraid/README.txt +2997 -0
  19. package/deploy/unraid/audit-container-spec.sh +4 -0
  20. package/deploy/unraid/container-runtime.json +1 -0
  21. package/deploy/unraid/credit-gateway.md +95 -0
  22. package/deploy/unraid/docker-man-template-transaction.mjs +727 -0
  23. package/deploy/unraid/docker-man-template-xml.cjs +105 -0
  24. package/deploy/unraid/install.sh +274 -0
  25. package/deploy/unraid/migrate-sanctuary-bundle.mjs +63 -0
  26. package/deploy/unraid/ouro-events/bootstrap-spool.sh +92 -0
  27. package/deploy/unraid/ouro-events/emit-event.mjs +310 -0
  28. package/deploy/unraid/ouro-events/emit-usenet-event.sh +54 -0
  29. package/deploy/unraid/ouro-events/install-usenet-guard.sh +435 -0
  30. package/deploy/unraid/ouro-events/usenet-health.sh +176 -0
  31. package/deploy/unraid/sanctuary-acceptance-adapter.sh +43 -0
  32. package/deploy/unraid/sanctuary-acceptance-contract.json +198 -0
  33. package/deploy/unraid/sanctuary-acceptance-harness.sh +23 -0
  34. package/deploy/unraid/sanctuary-authority-installation.json +36 -0
  35. package/deploy/unraid/sanctuary-authority-service.sh +30 -0
  36. package/deploy/unraid/sanctuary-butler-upgrade.mjs +1499 -0
  37. package/deploy/unraid/sanctuary-deployment-target.mjs +710 -0
  38. package/deploy/unraid/sanctuary-replay-gate.mjs +2114 -0
  39. package/deploy/unraid/sanctuary-safe-rename.py +24 -0
  40. package/deploy/unraid/sanctuary-unit16-host-broker.mjs +2088 -0
  41. package/deploy/unraid/sanctuary-unit16-run.sh +570 -0
  42. package/deploy/unraid/sanctuary-unit18-target-audit.sh +27 -0
  43. package/deploy/unraid/sanctuary.ouro/agent.json +66 -0
  44. package/deploy/unraid/sanctuary.ouro/arc/README.md +3 -0
  45. package/deploy/unraid/sanctuary.ouro/awaiting/house-care-sweep.md +17 -0
  46. package/deploy/unraid/sanctuary.ouro/bundle-meta.json +5 -0
  47. package/deploy/unraid/sanctuary.ouro/habits/sanctuary-health.md +8 -0
  48. package/deploy/unraid/sanctuary.ouro/mcp/media-mcp.mjs +2255 -0
  49. package/deploy/unraid/sanctuary.ouro/product/PRODUCT.md +13 -0
  50. package/deploy/unraid/sanctuary.ouro/provider-readiness.json +11 -0
  51. package/deploy/unraid/sanctuary.ouro/psyche/ASPIRATIONS.md +5 -0
  52. package/deploy/unraid/sanctuary.ouro/psyche/IDENTITY.md +7 -0
  53. package/deploy/unraid/sanctuary.ouro/psyche/LORE.md +7 -0
  54. package/deploy/unraid/sanctuary.ouro/psyche/SOUL.md +15 -0
  55. package/deploy/unraid/sanctuary.ouro/psyche/TACIT.md +3 -0
  56. package/deploy/unraid/sanctuary.ouro/state/policy/steward.json +7 -0
  57. package/deploy/unraid/sanctuary.ouro/tool-profiles.json +232 -0
  58. package/deploy/unraid/sanctuary.xml +29 -0
  59. package/dist/a2a/card.js +57 -0
  60. package/dist/a2a/client.js +290 -0
  61. package/dist/a2a/config.js +50 -0
  62. package/dist/a2a/delegated-command-grants.js +93 -0
  63. package/dist/a2a/delegated-command.js +197 -0
  64. package/dist/a2a/delegation-stores.js +56 -0
  65. package/dist/a2a/did-resolution.js +205 -0
  66. package/dist/a2a/escalation-grants.js +84 -0
  67. package/dist/a2a/escalation-wiring.js +60 -0
  68. package/dist/a2a/identity.js +258 -0
  69. package/dist/a2a/inbound-share.js +131 -0
  70. package/dist/a2a/mission-result-wire.js +265 -0
  71. package/dist/a2a/onboarding.js +133 -0
  72. package/dist/a2a/operator-trust.js +399 -0
  73. package/dist/a2a/outbox-client.js +59 -0
  74. package/dist/a2a/outbox-store.js +188 -0
  75. package/dist/a2a/outbox-wire.js +106 -0
  76. package/dist/a2a/pin-store.js +167 -0
  77. package/dist/a2a/refusals.js +53 -0
  78. package/dist/a2a/relay-client.js +106 -0
  79. package/dist/a2a/relay-config-vault.js +64 -0
  80. package/dist/a2a/relay-contacts.js +120 -0
  81. package/dist/a2a/relay-inbox.js +360 -0
  82. package/dist/a2a/relay-retirements.js +89 -0
  83. package/dist/a2a/replay-grant-holders.js +33 -0
  84. package/dist/a2a/replay-harness.js +188 -0
  85. package/dist/a2a/resolution-proof.js +53 -0
  86. package/dist/a2a/sealed-chat.js +114 -0
  87. package/dist/a2a/seen-ledger.js +214 -0
  88. package/dist/a2a/server.js +808 -0
  89. package/dist/a2a/task-store.js +69 -0
  90. package/dist/a2a/transport.js +100 -0
  91. package/dist/a2a/trust-commands.js +203 -0
  92. package/dist/a2a/trusted-files.js +185 -0
  93. package/dist/a2a/types.js +3 -0
  94. package/dist/arc/attention-types.js +8 -0
  95. package/dist/arc/cares.js +360 -0
  96. package/dist/arc/episodes.js +118 -0
  97. package/dist/arc/evolution.js +490 -0
  98. package/dist/arc/flight-recorder.js +1034 -0
  99. package/dist/arc/intentions.js +134 -0
  100. package/dist/arc/json-store.js +117 -0
  101. package/dist/arc/obligations.js +386 -0
  102. package/dist/arc/packets.js +336 -0
  103. package/dist/arc/presence.js +185 -0
  104. package/dist/arc/task-lifecycle.js +57 -0
  105. package/dist/commerce/store.js +755 -0
  106. package/dist/commerce/types.js +3 -0
  107. package/dist/gateway/keys.js +117 -0
  108. package/dist/gateway/server.js +429 -0
  109. package/dist/heart/active-work.js +798 -40
  110. package/dist/heart/agent-entry.js +252 -3
  111. package/dist/heart/approval-files.js +113 -0
  112. package/dist/heart/approval-store.js +960 -0
  113. package/dist/heart/attachments/image-normalize.js +201 -0
  114. package/dist/heart/attachments/materialize.js +97 -0
  115. package/dist/heart/attachments/originals.js +88 -0
  116. package/dist/heart/attachments/render.js +29 -0
  117. package/dist/heart/attachments/sources/bluebubbles.js +156 -0
  118. package/dist/heart/attachments/sources/cli-local-file.js +78 -0
  119. package/dist/heart/attachments/sources/index.js +18 -0
  120. package/dist/heart/attachments/sources/telegram.js +71 -0
  121. package/dist/heart/attachments/store.js +132 -0
  122. package/dist/heart/attachments/types.js +93 -0
  123. package/dist/heart/auth/auth-flow.js +511 -0
  124. package/dist/heart/autonomy-budget.js +478 -0
  125. package/dist/heart/awaiting/a2a-await-delivery.js +109 -0
  126. package/dist/heart/awaiting/await-alert.js +152 -0
  127. package/dist/heart/awaiting/await-expiry.js +143 -0
  128. package/dist/heart/awaiting/await-loader.js +91 -0
  129. package/dist/heart/awaiting/await-parser.js +152 -0
  130. package/dist/heart/awaiting/await-runtime-state.js +130 -0
  131. package/dist/heart/awaiting/await-scheduler.js +391 -0
  132. package/dist/heart/background-operations.js +281 -0
  133. package/dist/heart/bluebubbles-host-account.js +78 -0
  134. package/dist/heart/bridges/manager.js +137 -17
  135. package/dist/heart/bridges/store.js +14 -2
  136. package/dist/heart/bundle-state.js +168 -0
  137. package/dist/heart/commitments.js +44 -18
  138. package/dist/heart/config-registry.js +331 -0
  139. package/dist/heart/config.js +178 -131
  140. package/dist/heart/context-loss-gauntlet.js +387 -0
  141. package/dist/heart/context-loss-sentinel.js +1204 -0
  142. package/dist/heart/core.js +2378 -586
  143. package/dist/heart/cross-chat-delivery.js +9 -22
  144. package/dist/heart/daemon/agent-config-check.js +474 -0
  145. package/dist/heart/daemon/agent-discovery.js +283 -3
  146. package/dist/heart/daemon/agent-home.js +319 -0
  147. package/dist/heart/daemon/agent-service.js +542 -0
  148. package/dist/heart/daemon/agentic-repair.js +547 -0
  149. package/dist/heart/daemon/await-private-wake.js +59 -0
  150. package/dist/heart/daemon/bluebubbles-cli-integration.js +71 -0
  151. package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
  152. package/dist/heart/daemon/bluebubbles-host-protocol.js +433 -0
  153. package/dist/heart/daemon/bluebubbles-host.js +273 -0
  154. package/dist/heart/daemon/boot-sync-probe.js +197 -0
  155. package/dist/heart/daemon/cadence.js +180 -0
  156. package/dist/heart/daemon/cli-arg-redaction.js +26 -0
  157. package/dist/heart/daemon/cli-defaults.js +881 -0
  158. package/dist/heart/daemon/cli-desk.js +331 -0
  159. package/dist/heart/daemon/cli-exec.js +9280 -0
  160. package/dist/heart/daemon/cli-help.js +754 -0
  161. package/dist/heart/daemon/cli-parse.js +3329 -0
  162. package/dist/heart/daemon/cli-render-doctor.js +57 -0
  163. package/dist/heart/daemon/cli-render.js +819 -0
  164. package/dist/heart/daemon/cli-types.js +8 -0
  165. package/dist/heart/daemon/connect-bay.js +323 -0
  166. package/dist/heart/daemon/container-credential-bootstrap.js +273 -0
  167. package/dist/heart/daemon/container-healthcheck.js +112 -0
  168. package/dist/heart/daemon/container-runtime.js +97 -0
  169. package/dist/heart/daemon/container-spec-auditor-main.js +171 -0
  170. package/dist/heart/daemon/container-spec-auditor.js +241 -0
  171. package/dist/heart/daemon/daemon-bootstrap-startup.js +115 -0
  172. package/dist/heart/daemon/daemon-cli.js +29 -2373
  173. package/dist/heart/daemon/daemon-entry.js +953 -11
  174. package/dist/heart/daemon/daemon-health.js +186 -0
  175. package/dist/heart/daemon/daemon-rollup.js +57 -0
  176. package/dist/heart/daemon/daemon-runtime-sync.js +88 -13
  177. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  178. package/dist/heart/daemon/daemon.js +2110 -104
  179. package/dist/heart/daemon/desk-admin.js +420 -0
  180. package/dist/heart/daemon/dns-workflow.js +394 -0
  181. package/dist/heart/daemon/doctor-types.js +8 -0
  182. package/dist/heart/daemon/doctor.js +1766 -0
  183. package/dist/heart/daemon/freshness.js +345 -0
  184. package/dist/heart/daemon/habit-private-wake.js +61 -0
  185. package/dist/heart/daemon/health-monitor.js +154 -11
  186. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  187. package/dist/heart/daemon/hooks/bundle-meta.js +135 -1
  188. package/dist/heart/daemon/http-health-probe.js +80 -0
  189. package/dist/heart/daemon/human-command-screens.js +234 -0
  190. package/dist/heart/daemon/human-readiness.js +114 -0
  191. package/dist/heart/daemon/inner-status.js +78 -0
  192. package/dist/heart/daemon/interactive-repair.js +394 -0
  193. package/dist/heart/daemon/launchd.js +37 -8
  194. package/dist/heart/daemon/log-tailer.js +79 -10
  195. package/dist/heart/daemon/logs-prune.js +132 -0
  196. package/dist/heart/daemon/mcp-canary.js +524 -0
  197. package/dist/heart/daemon/message-router.js +65 -5
  198. package/dist/heart/daemon/migrate-to-desk.js +848 -0
  199. package/dist/heart/daemon/os-cron-deps.js +135 -0
  200. package/dist/heart/daemon/os-cron.js +22 -14
  201. package/dist/heart/daemon/ouro-bot-entry.js +6 -3
  202. package/dist/heart/daemon/ouro-entry.js +9 -3
  203. package/dist/heart/daemon/plugin-cli.js +432 -0
  204. package/dist/heart/daemon/process-manager.js +760 -61
  205. package/dist/heart/daemon/provider-discovery.js +137 -0
  206. package/dist/heart/daemon/provider-ping-progress.js +83 -0
  207. package/dist/heart/daemon/prunable-bundle.js +144 -0
  208. package/dist/heart/daemon/pulse.js +511 -0
  209. package/dist/heart/daemon/readiness-repair.js +365 -0
  210. package/dist/heart/daemon/run-hooks.js +2 -0
  211. package/dist/heart/daemon/runtime-logging.js +47 -14
  212. package/dist/heart/daemon/runtime-metadata.js +2 -30
  213. package/dist/heart/daemon/runtime-mode.js +13 -2
  214. package/dist/heart/daemon/safe-mode.js +161 -0
  215. package/dist/heart/daemon/sanctuary-acceptance-adapter.js +2662 -0
  216. package/dist/heart/daemon/sanctuary-acceptance-harness.js +1832 -0
  217. package/dist/heart/daemon/sanctuary-acceptance-marker.js +267 -0
  218. package/dist/heart/daemon/sanctuary-acceptance-scenarios.js +956 -0
  219. package/dist/heart/daemon/sanctuary-authority-codec.js +86 -0
  220. package/dist/heart/daemon/sanctuary-authority-epoch.js +291 -0
  221. package/dist/heart/daemon/sanctuary-authority-installation.js +161 -0
  222. package/dist/heart/daemon/sanctuary-authority-ledger.js +241 -0
  223. package/dist/heart/daemon/sanctuary-authority-root-lifecycle.js +1332 -0
  224. package/dist/heart/daemon/sanctuary-authority-vault-migration.js +79 -0
  225. package/dist/heart/daemon/sanctuary-bundle-migration.js +878 -0
  226. package/dist/heart/daemon/sanctuary-host-authority.js +1008 -0
  227. package/dist/heart/daemon/sanctuary-host-detached-supervisor.js +493 -0
  228. package/dist/heart/daemon/sanctuary-host-executor.js +670 -0
  229. package/dist/heart/daemon/sanctuary-host-linux-kernel.js +334 -0
  230. package/dist/heart/daemon/sanctuary-host-supervisor-entry.js +207 -0
  231. package/dist/heart/daemon/sanctuary-host-supervisor.js +95 -0
  232. package/dist/heart/daemon/sanctuary-package-management.js +103 -0
  233. package/dist/heart/daemon/sanctuary-scheduler-liveness.js +227 -0
  234. package/dist/heart/daemon/sanctuary-scheduler-origin.js +132 -0
  235. package/dist/heart/daemon/sanctuary-telegram-authority-entry.js +475 -0
  236. package/dist/heart/daemon/sanctuary-telegram-authority-gateway.js +585 -0
  237. package/dist/heart/daemon/sanctuary-telegram-authority-service.js +676 -0
  238. package/dist/heart/daemon/sense-manager.js +786 -48
  239. package/dist/heart/daemon/session-id-resolver.js +131 -0
  240. package/dist/heart/daemon/skill-management-installer.js +1 -1
  241. package/dist/heart/daemon/socket-client.js +180 -12
  242. package/dist/heart/daemon/stale-bundle-prune.js +113 -0
  243. package/dist/heart/daemon/startup-tui.js +339 -0
  244. package/dist/heart/daemon/supercronic-supervisor.js +282 -0
  245. package/dist/heart/daemon/task-scheduler.js +117 -39
  246. package/dist/heart/daemon/terminal-ui.js +503 -0
  247. package/dist/heart/daemon/thoughts.js +138 -54
  248. package/dist/heart/daemon/up-progress.js +366 -0
  249. package/dist/heart/daemon/vault-items.js +56 -0
  250. package/dist/heart/delegation.js +1 -4
  251. package/dist/heart/external-events/router.js +1531 -0
  252. package/dist/heart/failure-reports.js +336 -0
  253. package/dist/heart/file-lock.js +109 -0
  254. package/dist/heart/frontend-approval-runtime.js +506 -0
  255. package/dist/heart/frontend-journal.js +215 -0
  256. package/dist/heart/frontend-session-service.js +271 -0
  257. package/dist/heart/frontend-socket-client.js +173 -0
  258. package/dist/heart/frontend-socket.js +434 -0
  259. package/dist/heart/frontend-turn-errors.js +20 -0
  260. package/dist/heart/habits/habit-cancel.js +810 -0
  261. package/dist/heart/habits/habit-lifecycle.js +1327 -0
  262. package/dist/heart/habits/habit-migration.js +194 -0
  263. package/dist/heart/habits/habit-parser.js +278 -0
  264. package/dist/heart/habits/habit-runtime-state.js +93 -0
  265. package/dist/heart/habits/habit-scheduler.js +653 -0
  266. package/dist/heart/habits/habit-session-summary.js +318 -0
  267. package/dist/heart/habits/habit-session.js +636 -0
  268. package/dist/heart/{daemon → hatch}/hatch-flow.js +55 -62
  269. package/dist/heart/{daemon → hatch}/hatch-specialist.js +6 -8
  270. package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
  271. package/dist/heart/{daemon → hatch}/specialist-tools.js +53 -18
  272. package/dist/heart/identity.js +239 -59
  273. package/dist/heart/kept-notes.js +289 -0
  274. package/dist/heart/kicks.js +1 -1
  275. package/dist/heart/machine-identity.js +175 -0
  276. package/dist/heart/mail-import-discovery.js +356 -0
  277. package/dist/heart/mailbox/mailbox-http-hooks.js +99 -0
  278. package/dist/heart/mailbox/mailbox-http-response.js +7 -0
  279. package/dist/heart/mailbox/mailbox-http-routes.js +406 -0
  280. package/dist/heart/mailbox/mailbox-http-static.js +103 -0
  281. package/dist/heart/mailbox/mailbox-http-transport.js +272 -0
  282. package/dist/heart/mailbox/mailbox-http.js +99 -0
  283. package/dist/heart/mailbox/mailbox-read.js +41 -0
  284. package/dist/heart/mailbox/mailbox-supervision.js +131 -0
  285. package/dist/heart/mailbox/mailbox-types.js +27 -0
  286. package/dist/heart/mailbox/mailbox-view.js +197 -0
  287. package/dist/heart/mailbox/readers/agent-machine.js +428 -0
  288. package/dist/heart/mailbox/readers/continuity-readers.js +329 -0
  289. package/dist/heart/mailbox/readers/mail.js +375 -0
  290. package/dist/heart/mailbox/readers/runtime-readers.js +823 -0
  291. package/dist/heart/mailbox/readers/sessions.js +231 -0
  292. package/dist/heart/mailbox/readers/shared.js +111 -0
  293. package/dist/heart/mcp/mcp-server.js +760 -0
  294. package/dist/heart/migrate-config.js +100 -0
  295. package/dist/heart/model-capabilities.js +83 -16
  296. package/dist/heart/orientation-frame.js +376 -0
  297. package/dist/heart/platform.js +81 -0
  298. package/dist/heart/primitives.js +1 -1
  299. package/dist/heart/private-runtime/decision-renderer.js +158 -0
  300. package/dist/heart/private-runtime/index.js +18 -0
  301. package/dist/heart/private-runtime/ledger.js +221 -0
  302. package/dist/heart/private-runtime/policy.js +260 -0
  303. package/dist/heart/private-runtime/types.js +2 -0
  304. package/dist/heart/progress-story.js +1 -1
  305. package/dist/heart/provider-attempt.js +137 -0
  306. package/dist/heart/provider-binding-resolver.js +273 -0
  307. package/dist/heart/provider-credentials.js +449 -0
  308. package/dist/heart/provider-failover.js +311 -0
  309. package/dist/heart/provider-models.js +96 -0
  310. package/dist/heart/provider-ping.js +266 -0
  311. package/dist/heart/provider-readiness-cache.js +119 -0
  312. package/dist/heart/provider-visibility.js +188 -0
  313. package/dist/heart/providers/anthropic-token.js +131 -0
  314. package/dist/heart/providers/anthropic.js +248 -67
  315. package/dist/heart/providers/azure.js +23 -11
  316. package/dist/heart/providers/error-classification.js +129 -0
  317. package/dist/heart/providers/github-copilot.js +38 -39
  318. package/dist/heart/providers/minimax-vlm.js +189 -0
  319. package/dist/heart/providers/minimax.js +26 -8
  320. package/dist/heart/providers/openai-codex-token.js +349 -0
  321. package/dist/heart/providers/openai-codex.js +55 -40
  322. package/dist/heart/providers/openai-compatible.js +177 -0
  323. package/dist/heart/reach-channel.js +85 -0
  324. package/dist/heart/reply-style.js +52 -0
  325. package/dist/heart/run-ledger.js +247 -0
  326. package/dist/heart/runtime-capability-check.js +170 -0
  327. package/dist/heart/runtime-credentials.js +608 -0
  328. package/dist/heart/runtime-cwd.js +87 -0
  329. package/dist/heart/sense-truth.js +24 -4
  330. package/dist/heart/session-activity.js +94 -24
  331. package/dist/heart/session-events.js +1521 -0
  332. package/dist/heart/session-playback-cli-main.js +5 -0
  333. package/dist/heart/session-playback-cli.js +36 -0
  334. package/dist/heart/session-playback.js +231 -0
  335. package/dist/heart/session-redaction-repair-cli-main.js +31 -0
  336. package/dist/heart/session-redaction-repair.js +552 -0
  337. package/dist/heart/session-stats-cli-main.js +5 -0
  338. package/dist/heart/session-stats.js +183 -0
  339. package/dist/heart/session-transcript.js +133 -0
  340. package/dist/heart/shelf-claims.js +78 -0
  341. package/dist/heart/shepherd/custody-types.js +2 -0
  342. package/dist/heart/shepherd/mandates.js +352 -0
  343. package/dist/heart/shepherd/operation-commands.js +19 -0
  344. package/dist/heart/shepherd/operations.js +135 -0
  345. package/dist/heart/shepherd/supervision.js +114 -0
  346. package/dist/heart/shepherd/types.js +2 -0
  347. package/dist/heart/shepherd-config.js +123 -0
  348. package/dist/heart/source-grounding.js +313 -0
  349. package/dist/heart/start-of-turn-packet.js +372 -0
  350. package/dist/heart/steward-policy.js +656 -0
  351. package/dist/heart/streaming.js +631 -207
  352. package/dist/heart/structured-output.js +196 -0
  353. package/dist/heart/sync-classification.js +176 -0
  354. package/dist/heart/sync-health.js +152 -0
  355. package/dist/heart/sync.js +726 -0
  356. package/dist/heart/target-resolution.js +11 -7
  357. package/dist/heart/tempo.js +93 -0
  358. package/dist/heart/temporal-view.js +41 -0
  359. package/dist/heart/timeouts.js +101 -0
  360. package/dist/heart/tool-activity-callbacks.js +59 -0
  361. package/dist/heart/tool-approval.js +382 -0
  362. package/dist/heart/tool-description.js +155 -0
  363. package/dist/heart/tool-friction.js +55 -0
  364. package/dist/heart/tool-loop.js +206 -0
  365. package/dist/heart/tool-output-budget.js +41 -0
  366. package/dist/heart/turn-context.js +496 -0
  367. package/dist/heart/turn-execution-lease.js +30 -0
  368. package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +6 -5
  369. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +3 -2
  370. package/dist/heart/versioning/ouro-path-installer.js +432 -0
  371. package/dist/heart/versioning/ouro-recovery-launcher.js +76 -0
  372. package/dist/heart/versioning/ouro-version-manager.js +432 -0
  373. package/dist/heart/versioning/release-sources.js +173 -0
  374. package/dist/heart/versioning/self-update-defaults.js +294 -0
  375. package/dist/heart/versioning/self-update.js +196 -0
  376. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  377. package/dist/heart/{daemon → versioning}/update-checker.js +6 -1
  378. package/dist/heart/versioning/update-hooks.js +154 -0
  379. package/dist/heart/versioning/update-state.js +126 -0
  380. package/dist/heart/versioning/updater-launchd.js +150 -0
  381. package/dist/heart/versioning/version-intent.js +114 -0
  382. package/dist/heart/work-card.js +380 -0
  383. package/dist/mailbox-ui/assets/index-Dbska1tZ.js +1 -0
  384. package/dist/mailbox-ui/assets/index-DsiKaPyH.css +1 -0
  385. package/dist/mailbox-ui/assets/vendor-CcN1XpQ9.js +61 -0
  386. package/dist/mailbox-ui/index.html +16 -0
  387. package/dist/mailroom/attention.js +167 -0
  388. package/dist/mailroom/autonomy.js +209 -0
  389. package/dist/mailroom/blob-store.js +889 -0
  390. package/dist/mailroom/body-cache.js +61 -0
  391. package/dist/mailroom/cache-sync-cli.js +58 -0
  392. package/dist/mailroom/core.js +803 -0
  393. package/dist/mailroom/entry.js +160 -0
  394. package/dist/mailroom/file-store.js +568 -0
  395. package/dist/mailroom/hosted-cache-sync.js +384 -0
  396. package/dist/mailroom/mbox-import.js +393 -0
  397. package/dist/mailroom/migration.js +164 -0
  398. package/dist/mailroom/outbound.js +380 -0
  399. package/dist/mailroom/policy.js +263 -0
  400. package/dist/mailroom/reader.js +292 -0
  401. package/dist/mailroom/search-cache.js +513 -0
  402. package/dist/mailroom/search-relevance.js +319 -0
  403. package/dist/mailroom/smtp-ingress.js +176 -0
  404. package/dist/mailroom/source-state.js +176 -0
  405. package/dist/mailroom/thread.js +109 -0
  406. package/dist/mailroom/travel-extract.js +89 -0
  407. package/dist/mind/bundle-manifest.js +25 -2
  408. package/dist/mind/context.js +328 -139
  409. package/dist/mind/desk-section.js +362 -0
  410. package/dist/mind/diary-integrity.js +60 -0
  411. package/dist/mind/{memory.js → diary.js} +68 -77
  412. package/dist/mind/embedding-provider.js +60 -0
  413. package/dist/mind/file-state.js +179 -0
  414. package/dist/mind/friend-link-codes.js +209 -0
  415. package/dist/mind/friend-merge.js +336 -0
  416. package/dist/mind/house-names.js +43 -0
  417. package/dist/mind/{associative-recall.js → note-search.js} +47 -58
  418. package/dist/mind/obligation-steering.js +199 -3
  419. package/dist/mind/outbound-record.js +168 -0
  420. package/dist/mind/pending.js +36 -10
  421. package/dist/mind/prompt-budget.js +479 -0
  422. package/dist/mind/prompt-refresh.js +3 -2
  423. package/dist/mind/prompt.js +1152 -206
  424. package/dist/mind/provenance-trust.js +26 -0
  425. package/dist/mind/record-paths.js +312 -0
  426. package/dist/mind/scrutiny.js +173 -0
  427. package/dist/mind/session-transaction.js +622 -0
  428. package/dist/nerves/cli-logging.js +7 -1
  429. package/dist/nerves/coverage/audit-rules.js +15 -6
  430. package/dist/nerves/coverage/audit.js +28 -2
  431. package/dist/nerves/coverage/cli.js +1 -1
  432. package/dist/nerves/coverage/contract.js +5 -5
  433. package/dist/nerves/coverage/file-completeness.js +143 -5
  434. package/dist/nerves/coverage/run-artifacts.js +55 -35
  435. package/dist/nerves/coverage/source-scanner.js +4 -4
  436. package/dist/nerves/event-buffer.js +111 -0
  437. package/dist/nerves/index.js +293 -9
  438. package/dist/nerves/observation.js +20 -0
  439. package/dist/nerves/redact.js +79 -0
  440. package/dist/nerves/review/cli-main.js +5 -0
  441. package/dist/nerves/review/cli.js +156 -0
  442. package/dist/nerves/review/core.js +159 -0
  443. package/dist/nerves/runtime.js +17 -1
  444. package/dist/repertoire/ado-client.js +15 -56
  445. package/dist/repertoire/ado-semantic.js +16 -10
  446. package/dist/repertoire/api-client.js +97 -0
  447. package/dist/repertoire/bitwarden-store.js +1199 -0
  448. package/dist/repertoire/bundle-templates.js +71 -0
  449. package/dist/repertoire/bw-installer.js +231 -0
  450. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  451. package/dist/repertoire/coding/context-pack.js +331 -0
  452. package/dist/repertoire/coding/feedback.js +146 -34
  453. package/dist/repertoire/coding/index.js +15 -3
  454. package/dist/repertoire/coding/manager.js +159 -10
  455. package/dist/repertoire/coding/spawner.js +40 -11
  456. package/dist/repertoire/coding/tools.js +182 -18
  457. package/dist/repertoire/coding/workbench-client.js +272 -0
  458. package/dist/repertoire/coding/workbench-manager.js +346 -0
  459. package/dist/repertoire/commerce-errors.js +109 -0
  460. package/dist/repertoire/commerce-self-test.js +156 -0
  461. package/dist/repertoire/credential-access.js +183 -0
  462. package/dist/repertoire/desk/classifier.js +362 -0
  463. package/dist/repertoire/duffel-client.js +185 -0
  464. package/dist/repertoire/github-client.js +14 -55
  465. package/dist/repertoire/graph-client.js +11 -52
  466. package/dist/repertoire/guardrails.js +1196 -20
  467. package/dist/repertoire/house-sweep.js +471 -0
  468. package/dist/repertoire/mcp-client.js +58 -5
  469. package/dist/repertoire/mcp-manager.js +427 -139
  470. package/dist/repertoire/mcp-tools.js +132 -0
  471. package/dist/repertoire/mcp-write-guard.js +46 -0
  472. package/dist/repertoire/plugin-mcp.js +175 -0
  473. package/dist/repertoire/plugins.js +253 -0
  474. package/dist/repertoire/relationship-authorization.js +322 -0
  475. package/dist/repertoire/sanctuary-container-policy.js +50 -0
  476. package/dist/repertoire/shell-sessions.js +134 -0
  477. package/dist/repertoire/skills.js +48 -4
  478. package/dist/repertoire/stripe-client.js +131 -0
  479. package/dist/repertoire/tool-arguments.js +182 -0
  480. package/dist/repertoire/tool-results.js +29 -0
  481. package/dist/repertoire/tools-a2a.js +742 -0
  482. package/dist/repertoire/tools-attachments.js +322 -0
  483. package/dist/repertoire/tools-awaiting.js +897 -0
  484. package/dist/repertoire/tools-base.js +83 -1168
  485. package/dist/repertoire/tools-bluebubbles.js +2 -0
  486. package/dist/repertoire/tools-bridge.js +144 -0
  487. package/dist/repertoire/tools-bundle.js +995 -0
  488. package/dist/repertoire/tools-commerce.js +253 -0
  489. package/dist/repertoire/tools-config.js +186 -0
  490. package/dist/repertoire/tools-continuity.js +588 -0
  491. package/dist/repertoire/tools-credential.js +383 -0
  492. package/dist/repertoire/tools-escalation.js +84 -0
  493. package/dist/repertoire/tools-evolution.js +527 -0
  494. package/dist/repertoire/tools-files.js +362 -0
  495. package/dist/repertoire/tools-flight.js +290 -0
  496. package/dist/repertoire/tools-flow.js +119 -0
  497. package/dist/repertoire/tools-friend-link.js +57 -0
  498. package/dist/repertoire/tools-github.js +3 -8
  499. package/dist/repertoire/tools-habits.js +103 -0
  500. package/dist/repertoire/tools-house-care.js +174 -0
  501. package/dist/repertoire/tools-mail.js +1828 -0
  502. package/dist/repertoire/tools-notes.js +501 -0
  503. package/dist/repertoire/tools-obligations.js +182 -0
  504. package/dist/repertoire/tools-orientation.js +31 -0
  505. package/dist/repertoire/tools-record.js +482 -0
  506. package/dist/repertoire/tools-rsvp.js +139 -0
  507. package/dist/repertoire/tools-runtime.js +238 -0
  508. package/dist/repertoire/tools-sanctuary-host.js +202 -0
  509. package/dist/repertoire/tools-session.js +1141 -0
  510. package/dist/repertoire/tools-shell.js +154 -0
  511. package/dist/repertoire/tools-shepherd-guidance.js +171 -0
  512. package/dist/repertoire/tools-shepherd.js +325 -0
  513. package/dist/repertoire/tools-steward-policy.js +147 -0
  514. package/dist/repertoire/tools-stripe.js +224 -0
  515. package/dist/repertoire/tools-surface.js +369 -0
  516. package/dist/repertoire/tools-teams.js +50 -39
  517. package/dist/repertoire/tools-telegram-contacts.js +37 -0
  518. package/dist/repertoire/tools-travel.js +125 -0
  519. package/dist/repertoire/tools-trip.js +982 -0
  520. package/dist/repertoire/tools-unraid.js +556 -0
  521. package/dist/repertoire/tools-user-profile.js +146 -0
  522. package/dist/repertoire/tools-vault.js +40 -0
  523. package/dist/repertoire/tools-voice-local.js +198 -0
  524. package/dist/repertoire/tools-voice.js +145 -0
  525. package/dist/repertoire/tools.js +519 -156
  526. package/dist/repertoire/travel-api-client.js +360 -0
  527. package/dist/repertoire/unraid-client.js +155 -0
  528. package/dist/repertoire/unraid-restart.js +339 -0
  529. package/dist/repertoire/user-profile.js +131 -0
  530. package/dist/repertoire/vault-setup.js +246 -0
  531. package/dist/repertoire/vault-unlock.js +686 -0
  532. package/dist/rsvp/aisleplanner-client.js +354 -0
  533. package/dist/rsvp/cli.js +936 -0
  534. package/dist/rsvp/config.js +327 -0
  535. package/dist/rsvp/cutover.js +453 -0
  536. package/dist/rsvp/diagnostics.js +232 -0
  537. package/dist/rsvp/diff-renderer.js +176 -0
  538. package/dist/rsvp/habit-policy.js +110 -0
  539. package/dist/rsvp/habit-stage.js +123 -0
  540. package/dist/rsvp/incident-bundle.js +97 -0
  541. package/dist/rsvp/migration.js +204 -0
  542. package/dist/rsvp/native-habit-runner.js +630 -0
  543. package/dist/rsvp/outbound-state.js +628 -0
  544. package/dist/rsvp/query.js +118 -0
  545. package/dist/rsvp/replay.js +233 -0
  546. package/dist/rsvp/snapshot.js +232 -0
  547. package/dist/rsvp/spend-ledger.js +175 -0
  548. package/dist/scripts/claude-code-hook.js +41 -0
  549. package/dist/scripts/claude-code-stop-hook.js +47 -0
  550. package/dist/senses/a2a-entry.js +140 -0
  551. package/dist/senses/acp-server.js +386 -0
  552. package/dist/senses/attention-queue.js +186 -0
  553. package/dist/senses/await-turn-message.js +65 -0
  554. package/dist/senses/bluebubbles/active-turns.js +216 -0
  555. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  556. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  557. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +393 -43
  558. package/dist/senses/bluebubbles/context-packet.js +207 -0
  559. package/dist/senses/bluebubbles/context-smoke.js +144 -0
  560. package/dist/senses/bluebubbles/entry.js +90 -0
  561. package/dist/senses/bluebubbles/host-account-verifier.js +88 -0
  562. package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +21 -3
  563. package/dist/senses/bluebubbles/index.js +4054 -0
  564. package/dist/senses/bluebubbles/latest-turn.js +311 -0
  565. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -71
  566. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +159 -28
  567. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +24 -13
  568. package/dist/senses/bluebubbles/outbound-state.js +335 -0
  569. package/dist/senses/bluebubbles/processed-log.js +133 -0
  570. package/dist/senses/bluebubbles/reaction-policy.js +47 -0
  571. package/dist/senses/bluebubbles/replay.js +284 -0
  572. package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +30 -2
  573. package/dist/senses/bluebubbles/semantic-receipts.js +1986 -0
  574. package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
  575. package/dist/senses/bluebubbles/webhook-registration.js +257 -0
  576. package/dist/senses/bluebubbles-meta-guard.js +39 -0
  577. package/dist/senses/cli/bracketed-paste.js +82 -0
  578. package/dist/senses/cli/image-paste.js +287 -0
  579. package/dist/senses/cli/image-ref-navigation.js +75 -0
  580. package/dist/senses/cli/ink-app.js +156 -0
  581. package/dist/senses/cli/inline-diff.js +64 -0
  582. package/dist/senses/cli/input-keys.js +174 -0
  583. package/dist/senses/cli/kill-ring.js +86 -0
  584. package/dist/senses/cli/message-list.js +51 -0
  585. package/dist/senses/cli/ouro-tui.js +607 -0
  586. package/dist/senses/cli/spinner-imperative.js +135 -0
  587. package/dist/senses/cli/spinner.js +101 -0
  588. package/dist/senses/cli/status-line.js +60 -0
  589. package/dist/senses/cli/streaming-markdown.js +526 -0
  590. package/dist/senses/cli/tool-display.js +85 -0
  591. package/dist/senses/cli/tool-render.js +85 -0
  592. package/dist/senses/cli/tui-store.js +241 -0
  593. package/dist/senses/cli/virtual-list.js +35 -0
  594. package/dist/senses/cli-entry.js +60 -8
  595. package/dist/senses/cli.js +570 -309
  596. package/dist/senses/commands.js +66 -3
  597. package/dist/senses/context-packet-ledger.js +371 -0
  598. package/dist/senses/context-packets.js +154 -0
  599. package/dist/senses/habit-lifecycle-guard.js +24 -0
  600. package/dist/senses/habit-turn-message.js +160 -0
  601. package/dist/senses/ingress-evidence.js +2 -0
  602. package/dist/senses/mail-entry.js +66 -0
  603. package/dist/senses/mail.js +524 -0
  604. package/dist/senses/pipeline.js +1158 -204
  605. package/dist/senses/private-runtime-worker.js +857 -0
  606. package/dist/senses/private-runtime.js +1627 -0
  607. package/dist/senses/proactive-content-guard.js +51 -0
  608. package/dist/senses/root-host-approval-port.js +345 -0
  609. package/dist/senses/root-host-approval-runtime.js +393 -0
  610. package/dist/senses/sanctuary-authority-resident.js +102 -0
  611. package/dist/senses/sanctuary-download-credit-presentation.js +31 -0
  612. package/dist/senses/sanctuary-full-visibility-contract.js +302 -0
  613. package/dist/senses/sanctuary-grounding.js +118 -0
  614. package/dist/senses/sanctuary-health-acceptance-probe-entry.js +12 -0
  615. package/dist/senses/sanctuary-health-acceptance-probe.js +786 -0
  616. package/dist/senses/sanctuary-health-runner.js +106 -0
  617. package/dist/senses/sanctuary-health.js +442 -0
  618. package/dist/senses/sanctuary-house.js +97 -0
  619. package/dist/senses/sanctuary-install-state-contract.js +169 -0
  620. package/dist/senses/sanctuary-interactive-control.js +410 -0
  621. package/dist/senses/sanctuary-media-catalog-contract.js +268 -0
  622. package/dist/senses/sanctuary-media-optimization.js +517 -0
  623. package/dist/senses/sanctuary-runtime.js +390 -0
  624. package/dist/senses/sanctuary-sab.js +85 -0
  625. package/dist/senses/sanctuary-storage-optimization-contract.js +32 -0
  626. package/dist/senses/shared-turn.js +832 -0
  627. package/dist/senses/shepherd/agent-presentation.js +88 -0
  628. package/dist/senses/shepherd/attention-page.js +166 -0
  629. package/dist/senses/shepherd/attention-sidebar.js +76 -0
  630. package/dist/senses/shepherd/attention.js +295 -0
  631. package/dist/senses/shepherd/checkin-config.js +76 -0
  632. package/dist/senses/shepherd/checkin-store.js +386 -0
  633. package/dist/senses/shepherd/client.js +196 -0
  634. package/dist/senses/shepherd/cmux.js +483 -0
  635. package/dist/senses/shepherd/composer.js +336 -0
  636. package/dist/senses/shepherd/copilot-app.js +648 -0
  637. package/dist/senses/shepherd/feedback-reply.js +108 -0
  638. package/dist/senses/shepherd/feedback-source.js +20 -0
  639. package/dist/senses/shepherd/feedback-store.js +331 -0
  640. package/dist/senses/shepherd/feedback-submit.js +182 -0
  641. package/dist/senses/shepherd/feedback-worker.js +208 -0
  642. package/dist/senses/shepherd/guidance.js +273 -0
  643. package/dist/senses/shepherd/herdr.js +427 -0
  644. package/dist/senses/shepherd/host.js +17 -0
  645. package/dist/senses/shepherd/human-decision-guard.js +29 -0
  646. package/dist/senses/shepherd/judge-evaluation-command.js +183 -0
  647. package/dist/senses/shepherd/judge-evaluation-entry.js +13 -0
  648. package/dist/senses/shepherd/judge-evaluation.js +155 -0
  649. package/dist/senses/shepherd/judge.js +210 -0
  650. package/dist/senses/shepherd/notice-outbox.js +262 -0
  651. package/dist/senses/shepherd/observation-publisher.js +320 -0
  652. package/dist/senses/shepherd/operations-defaults.js +86 -0
  653. package/dist/senses/shepherd/operations.js +159 -0
  654. package/dist/senses/shepherd/owner-notice.js +489 -0
  655. package/dist/senses/shepherd/prompt-state.js +60 -0
  656. package/dist/senses/shepherd/recovery.js +543 -0
  657. package/dist/senses/shepherd/redact.js +66 -0
  658. package/dist/senses/shepherd/returns.js +79 -0
  659. package/dist/senses/shepherd/sense.js +849 -0
  660. package/dist/senses/shepherd/shepherd-judge-corpus-v1.json +170 -0
  661. package/dist/senses/shepherd/surface.js +123 -0
  662. package/dist/senses/shepherd/target-binding.js +13 -0
  663. package/dist/senses/shepherd/terminal-process.js +147 -0
  664. package/dist/senses/shepherd-entry.js +98 -0
  665. package/dist/senses/surface-tool.js +109 -0
  666. package/dist/senses/teams-entry.js +68 -8
  667. package/dist/senses/teams-image.js +119 -0
  668. package/dist/senses/teams.js +998 -337
  669. package/dist/senses/telegram-admission.js +718 -0
  670. package/dist/senses/telegram-approval-runtime.js +793 -0
  671. package/dist/senses/telegram-attachments.js +186 -0
  672. package/dist/senses/telegram-audit-ledger.js +220 -0
  673. package/dist/senses/telegram-authority-transport.js +231 -0
  674. package/dist/senses/telegram-client.js +1755 -0
  675. package/dist/senses/telegram-effect-adapter.js +904 -0
  676. package/dist/senses/telegram-entry.js +83 -0
  677. package/dist/senses/telegram-handoff.js +154 -0
  678. package/dist/senses/telegram.js +2474 -0
  679. package/dist/senses/trust-gate.js +104 -9
  680. package/dist/senses/voice/audio-playback.js +237 -0
  681. package/dist/senses/voice/audio-routing.js +119 -0
  682. package/dist/senses/voice/call-auth.js +250 -0
  683. package/dist/senses/voice/elevenlabs.js +202 -0
  684. package/dist/senses/voice/floor-control.js +431 -0
  685. package/dist/senses/voice/floor-controller.js +115 -0
  686. package/dist/senses/voice/golden-path.js +116 -0
  687. package/dist/senses/voice/index.js +29 -0
  688. package/dist/senses/voice/local-audio-control.js +344 -0
  689. package/dist/senses/voice/local-audio-devices.js +516 -0
  690. package/dist/senses/voice/local-audio-disclosure.js +174 -0
  691. package/dist/senses/voice/local-audio-identity.js +103 -0
  692. package/dist/senses/voice/local-audio-join.js +325 -0
  693. package/dist/senses/voice/local-audio-mute.js +199 -0
  694. package/dist/senses/voice/local-audio-notify.js +124 -0
  695. package/dist/senses/voice/local-audio-prompts.js +116 -0
  696. package/dist/senses/voice/local-audio-transport.js +427 -0
  697. package/dist/senses/voice/local-audio.js +281 -0
  698. package/dist/senses/voice/local-media-stream.js +145 -0
  699. package/dist/senses/voice/meeting.js +113 -0
  700. package/dist/senses/voice/outbound.js +190 -0
  701. package/dist/senses/voice/phone.js +33 -0
  702. package/dist/senses/voice/playback.js +139 -0
  703. package/dist/senses/voice/process-guards.js +51 -0
  704. package/dist/senses/voice/realtime-eval.js +496 -0
  705. package/dist/senses/voice/realtime-tool-schema.js +195 -0
  706. package/dist/senses/voice/realtime-trace.js +531 -0
  707. package/dist/senses/voice/transcript.js +70 -0
  708. package/dist/senses/voice/turn.js +191 -0
  709. package/dist/senses/voice/twilio-phone-runtime.js +836 -0
  710. package/dist/senses/voice/twilio-phone.js +5454 -0
  711. package/dist/senses/voice/types.js +2 -0
  712. package/dist/senses/voice/whisper.js +161 -0
  713. package/dist/senses/voice-entry.js +83 -0
  714. package/dist/senses/voice-realtime-eval-command.js +99 -0
  715. package/dist/senses/voice-realtime-eval-entry.js +21 -0
  716. package/dist/senses/voice-twilio-entry.js +87 -0
  717. package/dist/trips/core.js +138 -0
  718. package/dist/trips/store.js +265 -0
  719. package/dist/util/frontmatter.js +69 -0
  720. package/npm-shrinkwrap.json +8342 -0
  721. package/package.json +76 -13
  722. package/skills/agent-commerce.md +113 -0
  723. package/skills/browser-navigation.md +117 -0
  724. package/skills/commerce-setup-guide.md +116 -0
  725. package/skills/commerce-setup.md +84 -0
  726. package/skills/configure-dev-tools.md +99 -0
  727. package/skills/travel-planning.md +138 -0
  728. package/dist/heart/daemon/auth-flow.js +0 -430
  729. package/dist/heart/daemon/ouro-path-installer.js +0 -260
  730. package/dist/heart/daemon/ouro-version-manager.js +0 -171
  731. package/dist/heart/daemon/update-hooks.js +0 -138
  732. package/dist/heart/obligations.js +0 -191
  733. package/dist/heart/safe-workspace.js +0 -381
  734. package/dist/heart/session-recall.js +0 -116
  735. package/dist/mind/friends/channel.js +0 -84
  736. package/dist/mind/friends/group-context.js +0 -144
  737. package/dist/mind/friends/resolver.js +0 -84
  738. package/dist/mind/friends/store-file.js +0 -190
  739. package/dist/mind/friends/store.js +0 -4
  740. package/dist/mind/friends/tokens.js +0 -26
  741. package/dist/mind/friends/trust-explanation.js +0 -74
  742. package/dist/mind/friends/types.js +0 -29
  743. package/dist/repertoire/tasks/board.js +0 -134
  744. package/dist/repertoire/tasks/index.js +0 -224
  745. package/dist/repertoire/tasks/lifecycle.js +0 -80
  746. package/dist/repertoire/tasks/middleware.js +0 -65
  747. package/dist/repertoire/tasks/parser.js +0 -173
  748. package/dist/repertoire/tasks/scanner.js +0 -132
  749. package/dist/repertoire/tasks/transitions.js +0 -144
  750. package/dist/senses/bluebubbles-entry.js +0 -13
  751. package/dist/senses/bluebubbles.js +0 -1181
  752. package/dist/senses/debug-activity.js +0 -154
  753. package/dist/senses/inner-dialog-worker.js +0 -90
  754. package/dist/senses/inner-dialog.js +0 -525
  755. package/subagents/README.md +0 -7
  756. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  757. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  758. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  759. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  760. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  761. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  762. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  763. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  764. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  765. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  766. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  767. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  768. /package/dist/{repertoire/tasks/types.js → heart/attachments/sources/adapter.js} +0 -0
  769. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  770. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  771. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  772. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -33,12 +33,21 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.OURO_CLI_TRUST_MANIFEST = void 0;
36
+ exports.OURO_CLI_TRUST_MANIFEST = exports.PSYCHE_REFUSAL = void 0;
37
+ exports.canonicalTarget = canonicalTarget;
38
+ exports.insideAgentFolder = insideAgentFolder;
39
+ exports.psycheFileClass = psycheFileClass;
40
+ exports.psycheWriteAllowed = psycheWriteAllowed;
41
+ exports.psycheEditNote = psycheEditNote;
37
42
  exports.guardInvocation = guardInvocation;
38
43
  const fs = __importStar(require("node:fs"));
39
44
  const os = __importStar(require("node:os"));
40
- const types_1 = require("../mind/friends/types");
45
+ const path = __importStar(require("node:path"));
46
+ const friends_1 = require("@ouro.bot/friends");
47
+ const shell_quote_1 = require("shell-quote");
41
48
  const runtime_1 = require("../nerves/runtime");
49
+ const store_1 = require("../commerce/store");
50
+ const identity_1 = require("../heart/identity");
42
51
  const deny = (reason) => ({ allowed: false, reason });
43
52
  const allow = { allowed: true };
44
53
  // --- reason templates ---
@@ -55,17 +64,165 @@ const REASONS = {
55
64
  // --- read-only tools that never need guardrails ---
56
65
  const READ_ONLY_TOOLS = new Set(["read_file", "glob", "grep"]);
57
66
  // --- protected path detection ---
58
- const PROTECTED_PATH_SEGMENTS = [".git/"];
59
- function getProtectedAbsolutePrefixes() {
60
- return [`${os.homedir()}/.agentsecrets/`];
67
+ const PROTECTED_PATH_SEGMENTS = [
68
+ ".git/",
69
+ ".ouro-cli/vault-unlock/",
70
+ ".ouro-cli/vault-unlock-dpapi/",
71
+ "state/policy/",
72
+ // The pre-trust-directory home of the escalation grants; the file is no longer read, and a model must not recreate it.
73
+ "state/a2a/escalation-grants.json",
74
+ // Where to queue mail for offline peers; a model that rewrote a contact could send a peer's mail to its own relay.
75
+ "state/a2a/relay-contacts/",
76
+ // Relay retirements awaiting their vault write; a forged marker would skip draining an old relay inbox.
77
+ "state/a2a/relay-retired/",
78
+ "state/outbox/",
79
+ "state/reports/",
80
+ ];
81
+ const PROTECTED_FILENAMES = ["agent.json", "tool-profiles.json"];
82
+ /** How many symlinks a dangling-link walk follows before refusing (the Linux kernel's own limit is 40). */
83
+ const MAX_SYMLINK_HOPS = 40;
84
+ /** A lookup that failed only because part of the path does not exist yet; any other failure leaves the target unknown. */
85
+ function rethrowUnlessMissing(error) {
86
+ const code = error.code;
87
+ if (code !== "ENOENT" && code !== "ENOTDIR")
88
+ throw error;
89
+ }
90
+ /**
91
+ * The real location of `lexical`: `realpathSync.native` of the deepest part that exists, the final file included, with the
92
+ * missing remainder appended. A dangling symlink is followed, from its real parent, to where a write through it would land.
93
+ * Throws when the filesystem cannot say where the path lands (a denied lookup, a symlink loop, too many links), so the
94
+ * guard can refuse rather than fall back to the lexical path.
95
+ */
96
+ function canonicalize(lexical) {
97
+ let current = lexical;
98
+ const missing = [];
99
+ let hops = 0;
100
+ for (;;) {
101
+ try {
102
+ return path.join(fs.realpathSync.native(current), ...missing);
103
+ }
104
+ catch (error) {
105
+ rethrowUnlessMissing(error);
106
+ }
107
+ let stat;
108
+ try {
109
+ stat = fs.lstatSync(current);
110
+ }
111
+ catch (error) {
112
+ rethrowUnlessMissing(error);
113
+ }
114
+ if (stat?.isSymbolicLink()) {
115
+ if (hops >= MAX_SYMLINK_HOPS)
116
+ throw new Error(`too many symbolic links resolving ${lexical}`);
117
+ hops += 1;
118
+ current = path.resolve(fs.realpathSync.native(path.dirname(current)), fs.readlinkSync(current));
119
+ continue;
120
+ }
121
+ const parent = path.dirname(current);
122
+ if (parent === current)
123
+ return path.join(current, ...missing);
124
+ missing.unshift(path.basename(current));
125
+ current = parent;
126
+ }
127
+ }
128
+ /**
129
+ * Every place a file tool could write `p`: resolved against the agent root and against the repo root (which is what
130
+ * `write_file` and `edit_file` use), each in its lexical and canonical form. An absolute `p` is resolved only as itself.
131
+ */
132
+ function canonicalTarget(p, agentRoot) {
133
+ const lexicals = path.isAbsolute(p) ? [p] : [path.resolve(agentRoot, p), path.resolve((0, identity_1.getRepoRoot)(), p)];
134
+ return lexicals.flatMap((lexical) => [lexical, canonicalize(lexical)]);
135
+ }
136
+ /** True when any form of `p` is the agent's `folder` (in either its lexical or its canonical form) or under it. */
137
+ function insideAgentFolder(p, agentRoot, folder) {
138
+ const lexicalFolder = path.resolve(agentRoot, folder);
139
+ const folders = [lexicalFolder, canonicalize(lexicalFolder)];
140
+ return canonicalTarget(p, agentRoot).some((target) => folders.some((dir) => target === dir || target.startsWith(`${dir}${path.sep}`)));
141
+ }
142
+ // --- the psyche belongs to the agent ---
143
+ // Every agent writes its own TACIT.md: what it learns about how its house works. The rest of its psyche (who it is) changes only
144
+ // when a family human asks it directly, one to one, on a channel that proves who they are.
145
+ exports.PSYCHE_REFUSAL = "that file is part of me; I change it when my owner asks me in our own chat.";
146
+ const PSYCHE_IDENTITY_CHANNELS = new Set(["telegram", "bluebubbles", "teams"]);
147
+ /** `tacit` for the agent's psyche/TACIT.md, `identity` for anything else in psyche/ (the folder and new files included), `null` outside it. */
148
+ function psycheFileClass(filePath, agentRoot) {
149
+ if (!filePath || !agentRoot || !insideAgentFolder(filePath, agentRoot, "psyche"))
150
+ return null;
151
+ const lexicalFolder = path.resolve(agentRoot, "psyche");
152
+ const folders = [lexicalFolder, canonicalize(lexicalFolder)];
153
+ const tacits = folders.map((folder) => path.join(folder, "TACIT.md"));
154
+ // Every form of the path that lands in psyche/ must be TACIT: a link that sends one form of it to SOUL makes it identity.
155
+ const inPsyche = canonicalTarget(filePath, agentRoot).filter((target) => folders.some((dir) => target === dir || target.startsWith(`${dir}${path.sep}`)));
156
+ return inPsyche.every((target) => tacits.includes(target)) ? "tacit" : "identity";
157
+ }
158
+ /** The lstat of `p`, or null when it does not exist; throws on any other lookup failure. */
159
+ function lstatIfPresent(p) {
160
+ try {
161
+ return fs.lstatSync(p);
162
+ }
163
+ catch (error) {
164
+ if (error.code === "ENOENT")
165
+ return null;
166
+ throw error;
167
+ }
168
+ }
169
+ /** Whether the person in this conversation may have the agent write this psyche file. */
170
+ function psycheWriteAllowed(fileClass, _filePath, context) {
171
+ const agentRoot = context.agentRoot;
172
+ /* v8 ignore next -- a file is only classed as psyche when there is an agent root @preserve */
173
+ if (!agentRoot)
174
+ return false;
175
+ try {
176
+ if (lstatIfPresent(path.join(agentRoot, "psyche"))?.isSymbolicLink())
177
+ return false;
178
+ if (fileClass === "tacit") {
179
+ // A symlink or a hard link would carry the write to some other file.
180
+ const tacit = lstatIfPresent(path.join(agentRoot, "psyche", "TACIT.md"));
181
+ if (tacit && (tacit.isSymbolicLink() || tacit.nlink > 1))
182
+ return false;
183
+ if (context.friendId === undefined)
184
+ return true;
185
+ return context.trustLevel === undefined || context.trustLevel === "friend" || context.trustLevel === "family";
186
+ }
187
+ }
188
+ catch {
189
+ return false;
190
+ }
191
+ return context.friendKind === "human"
192
+ && context.trustLevel === "family"
193
+ && context.channel !== undefined && PSYCHE_IDENTITY_CHANNELS.has(context.channel)
194
+ && context.isGroupChat !== true
195
+ // Teams does not yet tell the turn whether it is a group chat, so there only an explicit one-to-one counts.
196
+ && (context.channel !== "teams" || context.isGroupChat === false);
197
+ }
198
+ /** The line a successful write to an identity file carries back to the agent. */
199
+ function psycheEditNote(file) {
200
+ return `psyche edit: I changed psyche/${file}. I tell my owner in one line which file changed and why.`;
201
+ }
202
+ /** The agent's psyche folder, in the lexical form the shell check uses; the file tools classify with `psycheFileClass`. */
203
+ function isPsychePath(filePath, agentRoot) {
204
+ const psyche = path.resolve(agentRoot, "psyche");
205
+ const target = path.resolve(agentRoot, filePath);
206
+ return target === psyche || target.startsWith(`${psyche}${path.sep}`);
207
+ }
208
+ /**
209
+ * The friends store (`friends/` under the agent root) is written by the harness's own code, which the model's file tools and
210
+ * shell redirects must not reach: a model talked into editing a record could rewrite trust, admission or a legacy grant.
211
+ * Scoped to this agent's root, so a project directory that happens to be named `friends` stays editable. Defence in depth
212
+ * only: a shell can still write anywhere its uid can, which is why delegated-command authority lives outside the bundle.
213
+ */
214
+ function isFriendsStorePath(filePath, agentRoot) {
215
+ const store = path.resolve(agentRoot, "friends");
216
+ const target = path.resolve(agentRoot, filePath);
217
+ return target === store || target.startsWith(`${store}${path.sep}`);
61
218
  }
62
219
  function isProtectedPath(filePath) {
63
220
  for (const segment of PROTECTED_PATH_SEGMENTS) {
64
221
  if (filePath.includes(`/${segment}`) || filePath.startsWith(segment))
65
222
  return true;
66
223
  }
67
- for (const prefix of getProtectedAbsolutePrefixes()) {
68
- if (filePath.startsWith(prefix))
224
+ for (const name of PROTECTED_FILENAMES) {
225
+ if (path.basename(filePath) === name)
69
226
  return true;
70
227
  }
71
228
  return false;
@@ -99,6 +256,196 @@ function shellWritesToProtectedPath(command) {
99
256
  return true;
100
257
  return false;
101
258
  }
259
+ // --- the psyche folder in shell commands ---
260
+ // The agent changes its own psyche through write_file and edit_file, where the trust gate below decides who may ask for it.
261
+ // The shell never writes there, for anyone: a shell command that mentions the psyche folder at all runs only if every part of
262
+ // it is a read-only command, because enumerating the ways to write a file (>, >>, tee, cp, mv, sed -i, node -e, python, cd then
263
+ // redirect) is a list the model can always step around.
264
+ const PSYCHE_READ_ONLY_COMMANDS = new Set(["cat", "ls", "head", "tail", "grep", "wc", "sha256sum", "stat", "cd", "pwd"]);
265
+ const SEGMENT_OPERATORS = new Set(["&&", "||", ";", "|"]);
266
+ // --- globs and expansions that could land inside a protected folder ---
267
+ // A word such as /bundle/fr*nds/x.json, /bundle/friend?/x.json or ${F}ends/x.json names no protected folder literally, but the
268
+ // shell could resolve it into one. A word like that counts as mentioning the folder when its directory part can match the folder's
269
+ // path, so the read-only rule below applies to the command.
270
+ const EXPANSION = "\u0001";
271
+ const STABLE_VARIABLES = ["HOME", "TMPDIR", "USER"];
272
+ function neutraliseExpansions(command) {
273
+ return command.replace(/\$\([^)]*\)|`[^`]*`|\$\{([A-Za-z_]\w*)\}|\$([A-Za-z_]\w*)|\$\{[^}]*\}/g, (_match, braced, plain) => {
274
+ const name = braced ?? plain;
275
+ if (name !== undefined && STABLE_VARIABLES.includes(name) && !new RegExp(`\\b${name}=`).test(command) && process.env[name])
276
+ return process.env[name];
277
+ return EXPANSION;
278
+ });
279
+ }
280
+ function wordPattern(word) {
281
+ const generalised = word.replace(/\{[^}]*\}/g, EXPANSION).replace(/\[[^\]]*\]/g, "?");
282
+ let source = "";
283
+ for (const char of generalised)
284
+ source += char === EXPANSION ? ".*" : char === "*" ? "[^/]*" : char === "?" ? "[^/]" : char.replace(/[.+^$()|\\\]{}[]/g, "\\$&");
285
+ return new RegExp(`^${source}$`);
286
+ }
287
+ function globOrExpansionCouldReach(command, folder, agentRoot) {
288
+ const tokens = neutraliseExpansions(command).split(/[\s<>|;&()'"]+/).filter((token) => /[*?[{\u0001]/.test(token));
289
+ return tokens.some((token) => {
290
+ const word = token.startsWith("~") ? expandHome(token) : token;
291
+ const absolute = word.startsWith("/") || word.startsWith(EXPANSION) ? word : `${agentRoot}/${word}`;
292
+ const dir = absolute.slice(0, Math.max(absolute.lastIndexOf("/"), 0));
293
+ return wordPattern(absolute).test(folder) || wordPattern(dir).test(folder);
294
+ });
295
+ }
296
+ function commandMentionsPsyche(command, words, agentRoot) {
297
+ const psyche = path.resolve(agentRoot, "psyche");
298
+ if (globOrExpansionCouldReach(command, psyche, agentRoot))
299
+ return true;
300
+ if (command.includes(psyche) || words.some((word) => word.includes(psyche)))
301
+ return true;
302
+ if (words.some((word) => word === "psyche" || isPsychePath(word, agentRoot)))
303
+ return true;
304
+ // A relative "psyche/..." inside code or a quoted argument, when this agent has such a folder.
305
+ if (/(?:^|[\s'"=(])psyche\//.test(command) && fs.existsSync(psyche))
306
+ return true;
307
+ // ~/..., $HOME/... and paths relative to a cd or -C target that land in this agent's psyche/.
308
+ return commandMentionsAgentFolder(command, words, agentRoot, "psyche");
309
+ }
310
+ /**
311
+ * The shared rule: when a shell command mentions a protected folder at all, it runs only if every part of it is a read-only
312
+ * command. The mention test is the only thing that differs between folders.
313
+ */
314
+ function protectedFolderShellWriteRefused(command, mentions) {
315
+ const entries = (0, shell_quote_1.parse)(command);
316
+ const words = entries.filter((entry) => typeof entry === "string");
317
+ if (!mentions(words))
318
+ return false;
319
+ // Substitution, heredocs and any $VAR or ${VAR} expansion: the shell could build a protected path or a command word we cannot see.
320
+ if (/\$[({A-Za-z_]|`|<\(|<</.test(command))
321
+ return true;
322
+ let expectCommandWord = true;
323
+ for (const entry of entries) {
324
+ if (typeof entry === "string") {
325
+ if (expectCommandWord && !/^\w+=/.test(entry)) {
326
+ if (!PSYCHE_READ_ONLY_COMMANDS.has(entry))
327
+ return true;
328
+ expectCommandWord = false;
329
+ }
330
+ continue;
331
+ }
332
+ const op = "op" in entry ? entry.op : "comment";
333
+ if (SEGMENT_OPERATORS.has(op))
334
+ expectCommandWord = true;
335
+ else if (op !== "glob")
336
+ return true;
337
+ }
338
+ return false;
339
+ }
340
+ function psycheShellWriteRefused(command, agentRoot) {
341
+ if (!agentRoot)
342
+ return false;
343
+ try {
344
+ return protectedFolderShellWriteRefused(command, (words) => commandMentionsPsyche(command, words, agentRoot));
345
+ }
346
+ catch {
347
+ // The filesystem could not say where a path that names psyche/ lands: refuse rather than guess.
348
+ return true;
349
+ }
350
+ }
351
+ // --- the friends store in shell commands ---
352
+ // Same stance as the psyche folder: a command that names this agent's friends store runs only if it is read-only, because a list of
353
+ // the ways to write a file (>, >>, tee, cp, mv, sed -i, node -e, python, cd then redirect, ~ paths) is one the model can step around.
354
+ function expandHome(word) {
355
+ return word === "~" || word.startsWith("~/") ? path.join(os.homedir(), word.slice(1)) : word;
356
+ }
357
+ function commandMentionsFriendsStore(command, words, agentRoot) {
358
+ if (globOrExpansionCouldReach(command, path.resolve(agentRoot, "friends"), agentRoot))
359
+ return true;
360
+ // Only path-shaped words resolve against the store: an ordinary word such as "friends" in prose is not a path.
361
+ if (words.some((word) => word.includes("/") && isFriendsStorePath(expandHome(word), agentRoot)))
362
+ return true;
363
+ // A bare `friends` names the store only as the target of a cd; elsewhere it is an ordinary word ("my friends are kind").
364
+ if (words.some((word, index) => word.startsWith("friends/") || (word === "friends" && words[index - 1] === "cd")))
365
+ return true;
366
+ // A variable or substitution in front of the path ($BUNDLE/friends/x.json): the folder name is still in the command.
367
+ if (/\$[({A-Za-z_]|`/.test(command) && /(?:^|[\/\s'"=])friends(?:$|[\/\s'"])/.test(command))
368
+ return true;
369
+ // An absolute or relative path inside code or a quoted argument, such as node -e "...'/bundle/friends/x.json'...".
370
+ const store = path.resolve(agentRoot, "friends");
371
+ if (command.includes(store) || /(?:^|[\s'"=(])friends\//.test(command))
372
+ return true;
373
+ // Paths relative to a cd or -C target that land in this agent's friends/.
374
+ return commandMentionsAgentFolder(command, words, agentRoot, "friends");
375
+ }
376
+ function friendsShellWriteRefused(command, agentRoot) {
377
+ if (!agentRoot)
378
+ return false;
379
+ try {
380
+ return protectedFolderShellWriteRefused(command, (words) => commandMentionsFriendsStore(command, words, agentRoot));
381
+ }
382
+ catch {
383
+ // The filesystem could not say where a path that names friends/ lands: refuse rather than guess.
384
+ return true;
385
+ }
386
+ }
387
+ // --- where a path in a shell command lands ---
388
+ // A command can name one of this agent's folders by an absolute path, by ~/ or $HOME/, or relative to wherever it cd's (or
389
+ // git -C's) first. Each word whose path has a part exactly `<name>` is resolved against every directory the command could be
390
+ // working in, lexically and canonically, and counts when it lands in this agent's <name>/.
391
+ const UNFOLLOWABLE_WORKING_DIRECTORY = /(?:^|[\s;&|(`])(?:pushd|popd)(?:\s|$)|(?:^|[\s;&|(`])cd\s+(?:--|["']?\$)|\s-C\s*["']?\$|(?:^|[\s;&|(`/])(?:sh|bash|zsh|dash|ksh)\s+(?:-\w+\s+)*-\w*c\b/;
392
+ /** Words of a shell command that name a path whose part is exactly `name`, wherever they sit (code and quotes included). */
393
+ function namedFolderWords(command, name) {
394
+ const part = new RegExp(`(?:^|/)${name}(?:/|$)`);
395
+ return command.split(/[\s'"=();|&<>`]+/).filter((token) => part.test(token));
396
+ }
397
+ function commandMentionsAgentFolder(command, words, agentRoot, name) {
398
+ const lexicalFolder = path.resolve(agentRoot, name);
399
+ if (globOrExpansionCouldReach(command, lexicalFolder, agentRoot))
400
+ return true;
401
+ const candidates = namedFolderWords(command, name);
402
+ if (candidates.length === 0)
403
+ return false;
404
+ // A variable in front of the path ($HOME/.../<name>/x): where it lands is the shell's to say, so it counts.
405
+ if (candidates.some((candidate) => candidate.includes("$")))
406
+ return true;
407
+ // A working directory this guard cannot follow (pushd, cd --, a cd or -C to a variable, or a nested shell's -c string):
408
+ // a relative path could land anywhere, so it counts.
409
+ if (UNFOLLOWABLE_WORKING_DIRECTORY.test(command))
410
+ return true;
411
+ // A path that names this bundle's own folder (x.ouro/psyche) or a glob just above the folder (*/psyche) is this agent's
412
+ // wherever the command works from, so eval, a piped shell, env --chdir, find -path and code that chdir's all count.
413
+ const bundle = path.basename(path.resolve(agentRoot)).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
414
+ const anchored = new RegExp(`(?:^|/)(?:${bundle}|[^/]*[*?[][^/]*)/${name}(?:/|$)`);
415
+ if (candidates.some((candidate) => anchored.test(candidate)))
416
+ return true;
417
+ const folders = [lexicalFolder, path.join(canonicalize(path.resolve(agentRoot)), name)];
418
+ // Every directory the command could be working in: where the shell starts, and each cd target, from each earlier one.
419
+ const cwds = [process.cwd()];
420
+ for (const [index, word] of words.entries()) {
421
+ // `git -C <dir>` works in <dir> just as `cd <dir>` does.
422
+ if (words[index - 1] !== "cd" && words[index - 1] !== "-C")
423
+ continue;
424
+ const target = expandHome(word);
425
+ cwds.push(...(path.isAbsolute(target) ? [target] : cwds.map((cwd) => path.resolve(cwd, target))));
426
+ }
427
+ return candidates.some((candidate) => {
428
+ const word = expandHome(candidate);
429
+ const lexicals = path.isAbsolute(word) ? [path.resolve(word)] : cwds.map((cwd) => path.resolve(cwd, word));
430
+ return lexicals.flatMap((lexical) => [lexical, canonicalize(lexical)])
431
+ .some((target) => folders.some((dir) => target === dir || target.startsWith(`${dir}${path.sep}`)));
432
+ });
433
+ }
434
+ // --- the product rules in shell commands ---
435
+ // product/PRODUCT.md is the package's standing rules for this agent; releases replace it, and nothing in a conversation does.
436
+ // Unlike the friends store, a bare relative "product/" is an ordinary project folder almost everywhere, so it counts as this
437
+ // agent's only when the command is plainly working in the agent root.
438
+ function productShellWriteRefused(command, agentRoot) {
439
+ if (!agentRoot)
440
+ return false;
441
+ try {
442
+ return protectedFolderShellWriteRefused(command, (words) => commandMentionsAgentFolder(command, words, agentRoot, "product"));
443
+ }
444
+ catch {
445
+ // The filesystem could not say where a path that names product/ lands: refuse rather than guess.
446
+ return true;
447
+ }
448
+ }
102
449
  // --- structural guardrail checks (always on, all trust levels) ---
103
450
  function checkReadBeforeWrite(toolName, args, context) {
104
451
  if (toolName === "edit_file") {
@@ -127,21 +474,47 @@ function checkDestructiveShellPatterns(toolName, args) {
127
474
  }
128
475
  return allow;
129
476
  }
130
- function checkProtectedPaths(toolName, args) {
477
+ function checkProtectedPaths(toolName, args, context) {
131
478
  if (toolName === "write_file" || toolName === "edit_file") {
132
479
  const filePath = args.path || "";
480
+ // The file tools resolve a relative path against the repo root and follow symlinks, so the guard checks every form.
481
+ // A path whose landing place the filesystem cannot report is refused rather than checked lexically.
482
+ const agentRoot = filePath ? context.agentRoot : undefined;
483
+ if (agentRoot) {
484
+ try {
485
+ const psycheClass = psycheFileClass(filePath, agentRoot);
486
+ if (psycheClass && !psycheWriteAllowed(psycheClass, filePath, context))
487
+ return deny(exports.PSYCHE_REFUSAL);
488
+ if (insideAgentFolder(filePath, agentRoot, "friends"))
489
+ return deny(REASONS.protectedPath);
490
+ if (insideAgentFolder(filePath, agentRoot, "product"))
491
+ return deny(REASONS.protectedPath);
492
+ // A link can carry a write that reads as harmless to a protected file (state/policy, agent.json).
493
+ if (canonicalTarget(filePath, agentRoot).some(isProtectedPath))
494
+ return deny(REASONS.protectedPath);
495
+ }
496
+ catch {
497
+ return deny(REASONS.protectedPath);
498
+ }
499
+ }
133
500
  if (isProtectedPath(filePath))
134
501
  return deny(REASONS.protectedPath);
135
502
  }
136
503
  if (toolName === "shell") {
137
504
  const command = args.command || "";
505
+ if (psycheShellWriteRefused(command, context.agentRoot))
506
+ return deny(exports.PSYCHE_REFUSAL);
507
+ if (friendsShellWriteRefused(command, context.agentRoot))
508
+ return deny(REASONS.protectedPath);
509
+ if (productShellWriteRefused(command, context.agentRoot))
510
+ return deny(REASONS.protectedPath);
138
511
  if (shellWritesToProtectedPath(command))
139
512
  return deny(REASONS.protectedPath);
140
513
  }
141
514
  return allow;
142
515
  }
143
516
  function checkStructuralGuardrails(toolName, args, context) {
144
- const protectedResult = checkProtectedPaths(toolName, args);
517
+ const protectedResult = checkProtectedPaths(toolName, args, context);
145
518
  if (!protectedResult.allowed)
146
519
  return protectedResult;
147
520
  const destructiveResult = checkDestructiveShellPatterns(toolName, args);
@@ -155,18 +528,15 @@ exports.OURO_CLI_TRUST_MANIFEST = {
155
528
  whoami: "acquaintance",
156
529
  changelog: "acquaintance",
157
530
  "session list": "acquaintance",
158
- "task board": "friend",
159
- "task create": "friend",
160
- "task update": "friend",
161
- "task show": "friend",
162
- "task actionable": "friend",
163
- "task deps": "friend",
164
- "task sessions": "friend",
165
531
  "friend list": "friend",
166
532
  "friend show": "friend",
167
533
  "friend create": "friend",
168
534
  "friend update": "family",
169
- "reminder create": "friend",
535
+ // Linking and merging change who the agent believes someone is: family only.
536
+ "friend link": "family",
537
+ "friend unlink": "family",
538
+ "friend merge": "family",
539
+ "friend dedupe": "family",
170
540
  "config model": "friend",
171
541
  "config models": "friend",
172
542
  "mcp list": "acquaintance",
@@ -201,7 +571,8 @@ function resolveOuroSubcommand(command) {
201
571
  /* v8 ignore next -- bare "ouro" is caught upstream by checkShellTrustGuardrails @preserve */
202
572
  if (!afterOuro)
203
573
  return null;
204
- const tokens = afterOuro.split(/\s+/);
574
+ // `--agent <name>` may come before the subcommand; it never changes which subcommand runs.
575
+ const tokens = afterOuro.split(/\s+/).filter((token, index, all) => token !== "--agent" && all[index - 1] !== "--agent");
205
576
  const twoWord = tokens.length >= 2 ? `${tokens[0]} ${tokens[1]}` : null;
206
577
  // Two-word match first (e.g. "task board"), then one-word (e.g. "whoami")
207
578
  if (twoWord && exports.OURO_CLI_TRUST_MANIFEST[twoWord])
@@ -210,6 +581,26 @@ function resolveOuroSubcommand(command) {
210
581
  return tokens[0];
211
582
  return null;
212
583
  }
584
+ // --- MCP server-specific trust rules ---
585
+ const MCP_SERVER_TRUST = {
586
+ browser: { minTrust: "friend", blockGroupChat: true },
587
+ };
588
+ function checkMcpServerTrust(command, context) {
589
+ const match = command.match(/^ouro\s+mcp\s+call\s+(\S+)/);
590
+ if (!match)
591
+ return allow;
592
+ const serverName = match[1];
593
+ const rules = MCP_SERVER_TRUST[serverName];
594
+ if (!rules)
595
+ return allow; // no special rules for this server
596
+ if (!trustLevelSatisfied(rules.minTrust, context.trustLevel ?? "friend")) {
597
+ return deny(REASONS.needsTrust);
598
+ }
599
+ if (rules.blockGroupChat && context.isGroupChat) {
600
+ return deny("browser tools are only available in 1:1 conversations, not group chats.");
601
+ }
602
+ return allow;
603
+ }
213
604
  function checkSingleShellCommandTrust(command, trustLevel) {
214
605
  const trimmed = command.trim();
215
606
  const tokens = trimmed.split(/\s+/);
@@ -261,9 +652,794 @@ function checkWriteTrustGuardrails(toolName, args, context) {
261
652
  return allow;
262
653
  return deny(REASONS.needsTrustForWrite);
263
654
  }
655
+ // --- credential tool trust gating ---
656
+ // Credential write tools: family only
657
+ const CREDENTIAL_FAMILY_TOOLS = new Set([
658
+ "credential_generate_password", "credential_store", "credential_delete", "vault_setup",
659
+ // User profile tools: family only
660
+ "user_profile_store", "user_profile_get", "user_profile_delete",
661
+ // Payment tools: family only
662
+ "stripe_create_card", "stripe_deactivate_card", "stripe_list_cards",
663
+ // Booking tools that involve payment: family only
664
+ "flight_book", "flight_hold", "flight_cancel",
665
+ ]);
666
+ // Credential read tools: friend+
667
+ const CREDENTIAL_TRUSTED_TOOLS = new Set(["credential_get", "credential_list"]);
668
+ // Travel tools: friend+ (weather_lookup accesses vault credentials indirectly;
669
+ // advisory and geocode are public APIs but gated for consistency)
670
+ // Flight search is also friend+ (read-only, no payment)
671
+ const TRAVEL_TRUSTED_TOOLS = new Set(["weather_lookup", "travel_advisory", "geocode_search", "flight_search"]);
672
+ const A2A_TRUSTED_TOOLS = new Set(["a2a_list_peers", "a2a_send_message", "a2a_get_task"]);
673
+ const COMMERCE_FAMILY_TOOLS = new Set(["commerce_checkout_preview", "commerce_checkout_commit", "commerce_receipt_get", "commerce_access_log"]);
674
+ const COMMERCE_AUTHORITY_TOOLS = new Set(["stripe_create_card", "flight_hold", "flight_book"]);
675
+ // The Shepherd tools read the owner's terminals and speak to them through their desktop, so only family may use them.
676
+ // A call with no known trust level fails closed.
677
+ const SHEPHERD_FAMILY_TOOLS = new Set(["shepherd_overview", "shepherd_read", "shepherd_signal", "shepherd_send"]);
678
+ const MAIL_FAMILY_TOOLS = new Set(["mail_screener", "mail_decide", "mail_access_log", "mail_send", "mail_index_refresh"]);
679
+ const MAIL_DELEGATED_READ_TOOLS = new Set(["mail_recent", "mail_search"]);
680
+ function opaqueAnsiShellAtoms(command) {
681
+ const atoms = [];
682
+ let inAnsiQuote = false;
683
+ for (let index = 0; index < command.length;) {
684
+ if (!inAnsiQuote && command.startsWith("$'", index)) {
685
+ inAnsiQuote = true;
686
+ index += 2;
687
+ continue;
688
+ }
689
+ const character = command[index];
690
+ if (inAnsiQuote && character === "'") {
691
+ inAnsiQuote = false;
692
+ index += 1;
693
+ continue;
694
+ }
695
+ if (inAnsiQuote && character === "\\") {
696
+ const escape = command.slice(index).match(/^\\(?:x[0-9a-fA-F]{1,2}|u[0-9a-fA-F]{1,4}|U[0-9a-fA-F]{1,8}|[0-7]{1,3}|c.|.)?/s)[0];
697
+ atoms.push(null);
698
+ index += escape.length;
699
+ continue;
700
+ }
701
+ if (!inAnsiQuote && (character === "'" || character === '"')) {
702
+ index += 1;
703
+ continue;
704
+ }
705
+ if (!inAnsiQuote && character === "\\" && index + 1 < command.length) {
706
+ atoms.push(command[index + 1]);
707
+ index += 2;
708
+ continue;
709
+ }
710
+ atoms.push(character);
711
+ index += 1;
712
+ }
713
+ return atoms;
714
+ }
715
+ function shellContainsAmbiguousAnsiMailCacheSync(command) {
716
+ if (!command.includes("$'"))
717
+ return false;
718
+ const expected = ["ouro", "mail", "sync-cache"];
719
+ let states = new Set(["0:0"]);
720
+ const crossTokenBoundary = (current) => {
721
+ const next = new Set(["0:0"]);
722
+ for (const state of current) {
723
+ const [tokenIndex, characterIndex] = state.split(":").map(Number);
724
+ if (characterIndex !== expected[tokenIndex].length)
725
+ continue;
726
+ if (tokenIndex === expected.length - 1)
727
+ return { matched: true, states: next };
728
+ next.add(`${tokenIndex + 1}:0`);
729
+ }
730
+ return { matched: false, states: next };
731
+ };
732
+ for (const atom of opaqueAnsiShellAtoms(command)) {
733
+ if (atom === null) {
734
+ const next = new Set(states);
735
+ for (const state of states) {
736
+ const [tokenIndex, characterIndex] = state.split(":").map(Number);
737
+ if (characterIndex >= 0 && characterIndex < expected[tokenIndex].length) {
738
+ next.add(`${tokenIndex}:${characterIndex + 1}`);
739
+ }
740
+ }
741
+ const boundary = crossTokenBoundary(states);
742
+ if (boundary.matched)
743
+ return true;
744
+ states = new Set([...next, ...boundary.states]);
745
+ continue;
746
+ }
747
+ if (/\s|[;&|()`]/.test(atom)) {
748
+ const boundary = crossTokenBoundary(states);
749
+ if (boundary.matched)
750
+ return true;
751
+ states = boundary.states;
752
+ continue;
753
+ }
754
+ const next = new Set();
755
+ for (const state of states) {
756
+ const [tokenIndex, characterIndex] = state.split(":").map(Number);
757
+ const wanted = expected[tokenIndex]?.[characterIndex];
758
+ next.add(wanted?.toLowerCase() === atom.toLowerCase() ? `${tokenIndex}:${characterIndex + 1}` : `${tokenIndex}:-1`);
759
+ }
760
+ states = next;
761
+ }
762
+ return crossTokenBoundary(states).matched;
763
+ }
764
+ const SHELL_COMMAND_BOUNDARIES = new Set(["||", "&&", ";;", "|&", "&", ";", "(", ")", "|", "<("]);
765
+ const SHELL_INTERPRETERS = new Set(["sh", "bash", "zsh"]);
766
+ const SHELL_TOKEN_BOUNDARIES = new Set(["{", "}", "then", "elif", "else", "do"]);
767
+ const SHELL_GRAMMAR_PREFIXES = new Set(["!", "if", "until", "while"]);
768
+ const SHELL_PARAMETER_MARKER = "__OURO_SHELL_PARAMETER_";
769
+ const SHELL_ARRAY_EXPANSION = "__OURO_ARRAY_EXPANSION__";
770
+ function hasUnescapedShellSubstitution(value) {
771
+ for (let index = 0; index < value.length; index += 1) {
772
+ if (value[index] === "\\") {
773
+ index += 1;
774
+ continue;
775
+ }
776
+ if (value.startsWith("$(", index) && !value.startsWith("$((", index))
777
+ return true;
778
+ if (value[index] === "`")
779
+ return true;
780
+ }
781
+ return false;
782
+ }
783
+ function stripHereDocBodies(command) {
784
+ const pending = [];
785
+ const lines = command.split("\n");
786
+ return lines.map((sourceLine) => {
787
+ if (pending.length > 0) {
788
+ const active = pending[0];
789
+ const candidate = active.stripTabs ? sourceLine.replace(/^\t+/, "") : sourceLine;
790
+ if (candidate === active.delimiter) {
791
+ pending.shift();
792
+ return "";
793
+ }
794
+ if (active.quoted || !hasUnescapedShellSubstitution(sourceLine))
795
+ return "";
796
+ return exposeShellSubstitutions(sourceLine).split("\n").slice(1).join("\n");
797
+ }
798
+ const pattern = /<<(?!<)(-)?\s*(?:'([^']*)'|"([^"]*)"|\\([^\s;&|]+)|([^\s;&|]+))/g;
799
+ for (const match of stripShellComments(sourceLine).matchAll(pattern)) {
800
+ pending.push({
801
+ stripTabs: match[1] !== undefined,
802
+ delimiter: match[2] ?? match[3] ?? match[4] ?? match[5],
803
+ quoted: match[2] !== undefined || match[3] !== undefined || match[4] !== undefined,
804
+ });
805
+ }
806
+ return sourceLine;
807
+ }).join("\n");
808
+ }
809
+ function stripShellComments(command) {
810
+ let result = "";
811
+ let quote = null;
812
+ for (let index = 0; index < command.length; index += 1) {
813
+ const character = command[index];
814
+ if (character === "\\" && quote !== "single") {
815
+ result += character + (command[index + 1] ?? "");
816
+ index += 1;
817
+ continue;
818
+ }
819
+ if (character === "'" && quote !== "double")
820
+ quote = quote === "single" ? null : "single";
821
+ else if (character === '"' && quote !== "single")
822
+ quote = quote === "double" ? null : "double";
823
+ if (character === "#" && quote === null && (index === 0 || /[\s;&|()]/.test(command[index - 1]))) {
824
+ const newline = command.indexOf("\n", index);
825
+ if (newline < 0)
826
+ break;
827
+ result += " ".repeat(newline - index) + "\n";
828
+ index = newline;
829
+ continue;
830
+ }
831
+ result += character;
832
+ }
833
+ return result;
834
+ }
835
+ const SHELL_ARRAY_ASSIGNMENT = /([A-Za-z_][A-Za-z0-9_]*)(\+?=)\(([^()\n]*)\)/y;
836
+ const SHELL_INDEXED_ASSIGNMENT = /([A-Za-z_][A-Za-z0-9_]*)\[(\d+)\]=("[^"]*"|'[^']*'|[^\s;&|]+)/y;
837
+ const SHELL_SCALAR_ASSIGNMENT = /([A-Za-z_][A-Za-z0-9_]*)=("[^"]*"|'[^']*'|[^\s;&|()]+)/y;
838
+ const SHELL_ARRAY_PARAMETER = /\$\{([A-Za-z_][A-Za-z0-9_]*)\[(@|\*)\]\}/y;
839
+ const SHELL_SCALAR_PARAMETER = /\$(?:\{([A-Za-z_][A-Za-z0-9_]*)\}|([A-Za-z_][A-Za-z0-9_]*))/y;
840
+ function shellMatchAt(pattern, command, index) {
841
+ pattern.lastIndex = index;
842
+ return pattern.exec(command);
843
+ }
844
+ function shellAssignmentPosition(command, index) {
845
+ let previous = index - 1;
846
+ while (previous >= 0 && /[ \t]/.test(command[previous]))
847
+ previous -= 1;
848
+ return previous < 0 || /[;&|()\n]/.test(command[previous]);
849
+ }
850
+ function parseShellWords(value) {
851
+ if (hasUnescapedShellSubstitution(value) || /(^|[^\\])\$(?!['"])/.test(value))
852
+ return undefined;
853
+ try {
854
+ const entries = (0, shell_quote_1.parse)(value);
855
+ if (entries.some((entry) => typeof entry !== "string"))
856
+ return undefined;
857
+ return entries;
858
+ }
859
+ catch {
860
+ return undefined;
861
+ }
862
+ }
863
+ function shellAssignmentPersists(command, endIndex) {
864
+ let next = endIndex;
865
+ while (next < command.length && /[ \t]/.test(command[next]))
866
+ next += 1;
867
+ return next === command.length || /[;&|\n]/.test(command[next]);
868
+ }
869
+ function shellUnquotedFields(values) {
870
+ const fields = values.flatMap((value) => value.split(/\s+/).filter(Boolean));
871
+ return fields.some((field) => /[*?[]/.test(field)) ? SHELL_ARRAY_EXPANSION : (0, shell_quote_1.quote)(fields);
872
+ }
873
+ function doubleQuotedFragment(value) {
874
+ if (hasUnescapedShellSubstitution(value))
875
+ return undefined;
876
+ try {
877
+ const parsed = (0, shell_quote_1.parse)(`"${value}"`);
878
+ return parsed.length === 1 && typeof parsed[0] === "string" ? parsed[0] : undefined;
879
+ }
880
+ catch {
881
+ return undefined;
882
+ }
883
+ }
884
+ function closingDoubleQuote(command, startIndex) {
885
+ for (let index = startIndex; index < command.length; index += 1) {
886
+ if (command[index] === "\\")
887
+ index += 1;
888
+ else if (command[index] === '"')
889
+ return index;
890
+ }
891
+ return -1;
892
+ }
893
+ function transformOrderedShellVariables(command) {
894
+ const variables = new Map();
895
+ const result = [];
896
+ let quote = null;
897
+ let doubleQuoteStart = -1;
898
+ let doubleQuoteResultIndex = -1;
899
+ for (let index = 0; index < command.length; index += 1) {
900
+ const character = command[index];
901
+ if (character === "\\" && quote !== "single") {
902
+ result.push(character + (command[index + 1] ?? ""));
903
+ index += 1;
904
+ continue;
905
+ }
906
+ if (character === "'") {
907
+ if (quote !== "double")
908
+ quote = quote === "single" ? null : "single";
909
+ result.push(character);
910
+ continue;
911
+ }
912
+ if (character === '"') {
913
+ if (quote !== "single") {
914
+ if (quote === "double") {
915
+ quote = null;
916
+ doubleQuoteStart = -1;
917
+ doubleQuoteResultIndex = -1;
918
+ }
919
+ else {
920
+ quote = "double";
921
+ doubleQuoteStart = index;
922
+ doubleQuoteResultIndex = result.length;
923
+ }
924
+ }
925
+ result.push(character);
926
+ continue;
927
+ }
928
+ if (quote === null && /[A-Za-z_]/.test(character) && shellAssignmentPosition(command, index)) {
929
+ const arrayAssignment = shellMatchAt(SHELL_ARRAY_ASSIGNMENT, command, index);
930
+ if (arrayAssignment) {
931
+ const [, name, operator, body] = arrayAssignment;
932
+ const elements = parseShellWords(body);
933
+ if (elements === undefined)
934
+ variables.delete(name);
935
+ else
936
+ variables.set(name, { array: operator === "+=" ? [...(variables.get(name)?.array ?? []), ...elements] : elements });
937
+ result.push(`${name}=__OURO_ARRAY_LITERAL`);
938
+ index += arrayAssignment[0].length - 1;
939
+ continue;
940
+ }
941
+ const indexedAssignment = shellMatchAt(SHELL_INDEXED_ASSIGNMENT, command, index);
942
+ if (indexedAssignment) {
943
+ const [, name, elementIndex, rawValue] = indexedAssignment;
944
+ const element = parseShellWords(rawValue);
945
+ if (element?.length !== 1)
946
+ variables.delete(name);
947
+ else {
948
+ const array = [...(variables.get(name)?.array ?? [])];
949
+ array[Number(elementIndex)] = element[0];
950
+ variables.set(name, { array });
951
+ }
952
+ result.push(`${name}=__OURO_ARRAY_LITERAL`);
953
+ index += indexedAssignment[0].length - 1;
954
+ continue;
955
+ }
956
+ const scalarAssignment = shellMatchAt(SHELL_SCALAR_ASSIGNMENT, command, index);
957
+ if (scalarAssignment) {
958
+ const [, name, rawValue] = scalarAssignment;
959
+ const scalar = parseShellWords(rawValue);
960
+ if (shellAssignmentPersists(command, index + scalarAssignment[0].length)) {
961
+ if (scalar?.length === 1)
962
+ variables.set(name, { scalar: scalar[0] });
963
+ else
964
+ variables.delete(name);
965
+ }
966
+ result.push(scalarAssignment[0]);
967
+ index += scalarAssignment[0].length - 1;
968
+ continue;
969
+ }
970
+ }
971
+ if (quote !== "single" && character === "$") {
972
+ const arrayExpansion = shellMatchAt(SHELL_ARRAY_PARAMETER, command, index);
973
+ if (arrayExpansion) {
974
+ const [, name, mode] = arrayExpansion;
975
+ const elements = variables.get(name)?.array;
976
+ if (elements === undefined)
977
+ result.push(SHELL_ARRAY_EXPANSION);
978
+ else if (quote === "double" && mode === "@") {
979
+ const closing = closingDoubleQuote(command, index + arrayExpansion[0].length);
980
+ const prefix = doubleQuotedFragment(command.slice(doubleQuoteStart + 1, index));
981
+ const suffix = closing < 0 ? undefined : doubleQuotedFragment(command.slice(index + arrayExpansion[0].length, closing));
982
+ if (closing < 0 || prefix === undefined || suffix === undefined)
983
+ result.push(SHELL_ARRAY_EXPANSION);
984
+ else {
985
+ const presentElements = elements.filter((element) => element !== undefined);
986
+ const expanded = presentElements.length === 0
987
+ ? (prefix.length + suffix.length === 0 ? [] : [`${prefix}${suffix}`])
988
+ : presentElements.map((element, elementIndex) => `${elementIndex === 0 ? prefix : ""}${element}${elementIndex === presentElements.length - 1 ? suffix : ""}`);
989
+ result.splice(doubleQuoteResultIndex);
990
+ result.push((0, shell_quote_1.quote)(expanded));
991
+ quote = null;
992
+ doubleQuoteStart = -1;
993
+ doubleQuoteResultIndex = -1;
994
+ index = closing;
995
+ continue;
996
+ }
997
+ }
998
+ else if (quote === "double")
999
+ result.push(elements.join(" "));
1000
+ else
1001
+ result.push(shellUnquotedFields(elements));
1002
+ index += arrayExpansion[0].length - 1;
1003
+ continue;
1004
+ }
1005
+ const scalarExpansion = shellMatchAt(SHELL_SCALAR_PARAMETER, command, index);
1006
+ if (scalarExpansion) {
1007
+ const name = scalarExpansion[1] ?? scalarExpansion[2];
1008
+ const scalar = variables.get(name)?.scalar;
1009
+ if (scalar !== undefined) {
1010
+ if (quote === "double")
1011
+ result.push(scalar.replace(/["\\$`]/g, "\\$&"));
1012
+ else
1013
+ result.push(shellUnquotedFields([scalar]));
1014
+ index += scalarExpansion[0].length - 1;
1015
+ continue;
1016
+ }
1017
+ if (quote === null) {
1018
+ result.push(SHELL_ARRAY_EXPANSION);
1019
+ index += scalarExpansion[0].length - 1;
1020
+ continue;
1021
+ }
1022
+ }
1023
+ }
1024
+ result.push(character);
1025
+ }
1026
+ return result.join("");
1027
+ }
1028
+ function findCommandSubstitutionEnd(command, start) {
1029
+ let depth = 1;
1030
+ let quote = null;
1031
+ for (let index = start; index < command.length; index += 1) {
1032
+ const character = command[index];
1033
+ if (character === "\\" && quote !== "single") {
1034
+ index += 1;
1035
+ continue;
1036
+ }
1037
+ if (character === "'" && quote !== "double") {
1038
+ quote = quote === "single" ? null : "single";
1039
+ continue;
1040
+ }
1041
+ if (character === '"' && quote !== "single") {
1042
+ quote = quote === "double" ? null : "double";
1043
+ continue;
1044
+ }
1045
+ if (quote !== null)
1046
+ continue;
1047
+ if (character === "(")
1048
+ depth += 1;
1049
+ else if (character === ")") {
1050
+ depth -= 1;
1051
+ if (depth === 0)
1052
+ return index;
1053
+ }
1054
+ }
1055
+ return -1;
1056
+ }
1057
+ function exposeShellSubstitutions(command, depth = 0) {
1058
+ if (depth >= 32)
1059
+ return "ouro mail sync-cache";
1060
+ let outer = "";
1061
+ const nested = [];
1062
+ let quote = null;
1063
+ for (let index = 0; index < command.length; index += 1) {
1064
+ const character = command[index];
1065
+ if (character === "\\" && quote !== "single") {
1066
+ outer += character + (command[index + 1] ?? "");
1067
+ index += 1;
1068
+ continue;
1069
+ }
1070
+ if (character === "'" && quote !== "double") {
1071
+ quote = quote === "single" ? null : "single";
1072
+ outer += character;
1073
+ continue;
1074
+ }
1075
+ if (character === '"' && quote !== "single") {
1076
+ quote = quote === "double" ? null : "double";
1077
+ outer += character;
1078
+ continue;
1079
+ }
1080
+ if (quote !== "single" && command.startsWith("$(", index) && !command.startsWith("$((", index)) {
1081
+ const end = findCommandSubstitutionEnd(command, index + 2);
1082
+ if (end >= 0) {
1083
+ nested.push(exposeShellSubstitutions(command.slice(index + 2, end), depth + 1));
1084
+ outer += "${__OURO_COMMAND_SUBSTITUTION}";
1085
+ index = end;
1086
+ continue;
1087
+ }
1088
+ }
1089
+ if (quote !== "single" && character === "`") {
1090
+ let end = index + 1;
1091
+ while (end < command.length && command[end] !== "`") {
1092
+ if (command[end] === "\\")
1093
+ end += 1;
1094
+ end += 1;
1095
+ }
1096
+ if (end < command.length) {
1097
+ nested.push(exposeShellSubstitutions(command.slice(index + 1, end), depth + 1));
1098
+ outer += "${__OURO_COMMAND_SUBSTITUTION}";
1099
+ index = end;
1100
+ continue;
1101
+ }
1102
+ }
1103
+ outer += character;
1104
+ }
1105
+ return [outer, ...nested].join("\n");
1106
+ }
1107
+ function normalizeShellNewlines(command) {
1108
+ let result = "";
1109
+ let quote = null;
1110
+ for (let index = 0; index < command.length; index += 1) {
1111
+ const character = command[index];
1112
+ if (character === "\\" && quote !== "single") {
1113
+ result += character + (command[index + 1] ?? "");
1114
+ index += 1;
1115
+ continue;
1116
+ }
1117
+ if (character === "'" && quote !== "double")
1118
+ quote = quote === "single" ? null : "single";
1119
+ else if (character === '"' && quote !== "single")
1120
+ quote = quote === "double" ? null : "double";
1121
+ result += character === "\n" && quote === null ? " ; " : character;
1122
+ }
1123
+ return result;
1124
+ }
1125
+ function executableShellView(command) {
1126
+ const withoutData = stripShellComments(stripHereDocBodies(command));
1127
+ return normalizeShellNewlines(transformOrderedShellVariables(exposeShellSubstitutions(withoutData)));
1128
+ }
1129
+ function parseShellCommands(command) {
1130
+ let parameterIndex = 0;
1131
+ const parameterNames = new Map();
1132
+ const entries = (0, shell_quote_1.parse)(command, (name) => {
1133
+ const marker = `${SHELL_PARAMETER_MARKER}${parameterIndex}__`;
1134
+ parameterIndex += 1;
1135
+ parameterNames.set(marker, name);
1136
+ return marker;
1137
+ });
1138
+ const commands = [];
1139
+ let current = [];
1140
+ let skipRedirectionTarget = false;
1141
+ const flush = () => {
1142
+ if (current.length > 0)
1143
+ commands.push(current);
1144
+ current = [];
1145
+ skipRedirectionTarget = false;
1146
+ };
1147
+ for (const entry of entries) {
1148
+ if (typeof entry === "string") {
1149
+ if (skipRedirectionTarget)
1150
+ skipRedirectionTarget = false;
1151
+ else if (SHELL_TOKEN_BOUNDARIES.has(entry.toLowerCase()))
1152
+ flush();
1153
+ else
1154
+ current.push(entry);
1155
+ continue;
1156
+ }
1157
+ const operation = entry;
1158
+ if (operation.op === "glob") {
1159
+ current.push(operation.pattern);
1160
+ continue;
1161
+ }
1162
+ if (SHELL_COMMAND_BOUNDARIES.has(operation.op)) {
1163
+ flush();
1164
+ continue;
1165
+ }
1166
+ // Every remaining control operator is a redirection. Its next string is
1167
+ // a path or descriptor, not an argv token.
1168
+ skipRedirectionTarget = true;
1169
+ }
1170
+ flush();
1171
+ return { commands, parameterNames };
1172
+ }
1173
+ function resolveKnownShellParameters(token, knownVariables, parameterNames) {
1174
+ let resolved = token;
1175
+ for (const [marker, name] of parameterNames) {
1176
+ const known = knownVariables.get(name);
1177
+ if (known !== undefined)
1178
+ resolved = resolved.replaceAll(marker, known);
1179
+ }
1180
+ return resolved;
1181
+ }
1182
+ function shellTokenCanEqual(token, expected) {
1183
+ const escaped = token
1184
+ .split(new RegExp(`(?:${SHELL_PARAMETER_MARKER}\\d+__|${SHELL_ARRAY_EXPANSION})`, "g"))
1185
+ .map((literal) => literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"))
1186
+ .join(".*");
1187
+ return new RegExp(`^${escaped}$`, "i").test(expected);
1188
+ }
1189
+ function shellExecutableCanEqual(token, expected) {
1190
+ const basenameToken = token.slice(token.lastIndexOf("/") + 1);
1191
+ return shellTokenCanEqual(basenameToken, expected);
1192
+ }
1193
+ function exactShellExecutable(token) {
1194
+ if (token === undefined || token.includes(SHELL_PARAMETER_MARKER))
1195
+ return undefined;
1196
+ return token.slice(token.lastIndexOf("/") + 1).toLowerCase();
1197
+ }
1198
+ const SUDO_SHORT_OPERANDS = new Set(["-C", "-D", "-g", "-h", "-p", "-R", "-r", "-T", "-t", "-U", "-u"]);
1199
+ const SUDO_LONG_OPERANDS = new Set(["--chdir", "--chroot", "--close-from", "--command-timeout", "--group", "--host", "--other-user", "--prompt", "--role", "--type", "--user"]);
1200
+ const TIME_SHORT_OPERANDS = new Set(["-f", "-o"]);
1201
+ const TIME_LONG_OPERANDS = new Set(["--format", "--output"]);
1202
+ function wrapperOptionAdvance(option, shortOperands, longOperands) {
1203
+ if (shortOperands.has(option) || longOperands.has(option))
1204
+ return 2;
1205
+ if ([...shortOperands].some((candidate) => option.startsWith(candidate) && option.length > candidate.length))
1206
+ return 1;
1207
+ if ([...longOperands].some((candidate) => option.startsWith(`${candidate}=`)))
1208
+ return 1;
1209
+ return 0;
1210
+ }
1211
+ function resolveShellCommand(tokens, startIndex) {
1212
+ let index = startIndex;
1213
+ while (index < tokens.length) {
1214
+ const executable = exactShellExecutable(tokens[index]);
1215
+ if (SHELL_GRAMMAR_PREFIXES.has(executable ?? "")) {
1216
+ index += 1;
1217
+ continue;
1218
+ }
1219
+ if (executable === "coproc") {
1220
+ index += 1;
1221
+ const candidate = tokens[index] ?? "";
1222
+ if (/^[A-Za-z_][A-Za-z0-9_]*$/.test(candidate) && !shellExecutableCanEqual(candidate, "ouro") && tokens[index + 1] !== undefined)
1223
+ index += 1;
1224
+ continue;
1225
+ }
1226
+ if (executable === "command" || executable === "nohup") {
1227
+ index += 1;
1228
+ if (executable === "command" && (tokens[index] === "-v" || tokens[index] === "-V"))
1229
+ return { index: tokens.length };
1230
+ while (tokens[index]?.startsWith("-"))
1231
+ index += 1;
1232
+ continue;
1233
+ }
1234
+ if (executable === "env") {
1235
+ index += 1;
1236
+ while (index < tokens.length) {
1237
+ const option = tokens[index];
1238
+ if (option === "-S" || option === "--split-string")
1239
+ return { index, script: `env ${tokens.slice(index + 1).join(" ")}` };
1240
+ if (option.startsWith("--split-string="))
1241
+ return { index, script: `env ${[option.slice("--split-string=".length), ...tokens.slice(index + 1)].join(" ")}` };
1242
+ if (["-u", "-C", "--unset", "--chdir"].includes(option))
1243
+ index += 2;
1244
+ else if (option.startsWith("-") || /^[A-Za-z_][A-Za-z0-9_]*=/.test(option))
1245
+ index += 1;
1246
+ else
1247
+ break;
1248
+ }
1249
+ continue;
1250
+ }
1251
+ if (executable === "sudo") {
1252
+ index += 1;
1253
+ while (tokens[index]?.startsWith("-")) {
1254
+ const advance = wrapperOptionAdvance(tokens[index], SUDO_SHORT_OPERANDS, SUDO_LONG_OPERANDS);
1255
+ index += advance === 0 ? 1 : advance;
1256
+ }
1257
+ continue;
1258
+ }
1259
+ if (executable === "time") {
1260
+ index += 1;
1261
+ while (tokens[index]?.startsWith("-")) {
1262
+ const advance = wrapperOptionAdvance(tokens[index], TIME_SHORT_OPERANDS, TIME_LONG_OPERANDS);
1263
+ index += advance === 0 ? 1 : advance;
1264
+ }
1265
+ continue;
1266
+ }
1267
+ if (executable === "exec") {
1268
+ index += 1;
1269
+ while (tokens[index]?.startsWith("-"))
1270
+ index += tokens[index] === "-a" ? 2 : 1;
1271
+ continue;
1272
+ }
1273
+ break;
1274
+ }
1275
+ return { index };
1276
+ }
1277
+ function shellScriptRequiresFamily(script, variables, depth) {
1278
+ if (script.includes(SHELL_PARAMETER_MARKER) || script.includes(SHELL_ARRAY_EXPANSION))
1279
+ return true;
1280
+ return shellCommandsContainMailCacheSync(script, variables, depth + 1);
1281
+ }
1282
+ function shellTokensContainMailCacheSync(tokens, startIndex, variables, depth) {
1283
+ const resolved = resolveShellCommand(tokens, startIndex);
1284
+ if (resolved.script !== undefined)
1285
+ return shellScriptRequiresFamily(resolved.script, variables, depth);
1286
+ const executableToken = tokens[resolved.index] ?? "";
1287
+ const executable = exactShellExecutable(executableToken);
1288
+ if (executableToken.includes(SHELL_ARRAY_EXPANSION))
1289
+ return true;
1290
+ if (SHELL_INTERPRETERS.has(executable ?? "")) {
1291
+ const commandOption = tokens.findIndex((token, index) => index > resolved.index && /^-[^-]*c/.test(token));
1292
+ return commandOption >= 0
1293
+ && shellScriptRequiresFamily(tokens[commandOption + 1] ?? "", variables, depth);
1294
+ }
1295
+ if (executable === "eval")
1296
+ return shellScriptRequiresFamily(tokens.slice(resolved.index + 1).join(" "), variables, depth);
1297
+ return shellExecutableCanEqual(executableToken, "ouro")
1298
+ && shellTokenCanEqual(tokens[resolved.index + 1] ?? "", "mail")
1299
+ && shellTokenCanEqual(tokens[resolved.index + 2] ?? "", "sync-cache");
1300
+ }
1301
+ function shellCommandsContainMailCacheSync(command, inheritedVariables = new Map(), depth = 0) {
1302
+ if (depth >= 32)
1303
+ return true;
1304
+ let parsed;
1305
+ try {
1306
+ parsed = parseShellCommands(command);
1307
+ }
1308
+ catch {
1309
+ return false;
1310
+ }
1311
+ const knownVariables = new Map(inheritedVariables);
1312
+ for (const rawTokens of parsed.commands) {
1313
+ const tokens = rawTokens.map((token) => resolveKnownShellParameters(token, knownVariables, parsed.parameterNames));
1314
+ const invocationVariables = new Map(knownVariables);
1315
+ let commandIndex = 0;
1316
+ while (commandIndex < tokens.length) {
1317
+ const assignment = tokens[commandIndex].match(/^([A-Za-z_][A-Za-z0-9_]*)=(.*)$/s);
1318
+ if (!assignment)
1319
+ break;
1320
+ if (assignment[2].includes(SHELL_PARAMETER_MARKER))
1321
+ invocationVariables.delete(assignment[1]);
1322
+ else
1323
+ invocationVariables.set(assignment[1], assignment[2]);
1324
+ commandIndex += 1;
1325
+ }
1326
+ if (commandIndex === tokens.length) {
1327
+ knownVariables.clear();
1328
+ for (const [name, value] of invocationVariables)
1329
+ knownVariables.set(name, value);
1330
+ continue;
1331
+ }
1332
+ if (shellTokensContainMailCacheSync(tokens, commandIndex, invocationVariables, depth))
1333
+ return true;
1334
+ }
1335
+ return false;
1336
+ }
1337
+ function shellContainsMailCacheSync(command) {
1338
+ const executableCommand = executableShellView(command);
1339
+ if (shellContainsAmbiguousAnsiMailCacheSync(executableCommand))
1340
+ return true;
1341
+ return shellCommandsContainMailCacheSync(executableCommand);
1342
+ }
1343
+ function mailCacheSyncShellGuardrail(toolName, args, context) {
1344
+ if (toolName !== "shell" || !shellContainsMailCacheSync(args.command ?? ""))
1345
+ return allow;
1346
+ if (context.trustLevel === undefined || context.trustLevel === "family")
1347
+ return allow;
1348
+ return deny("hosted mail cache convergence requires family trust.");
1349
+ }
1350
+ function mailTrustGuardrail(toolName, args, context) {
1351
+ if (MAIL_FAMILY_TOOLS.has(toolName)) {
1352
+ if (context.trustLevel === undefined || context.trustLevel === "family")
1353
+ return allow;
1354
+ if (toolName === "mail_send")
1355
+ return deny("outbound mail sends require family trust.");
1356
+ return deny(toolName === "mail_decide"
1357
+ ? "mail screener decisions require family trust."
1358
+ : "delegated human mail requires family trust.");
1359
+ }
1360
+ if (MAIL_DELEGATED_READ_TOOLS.has(toolName)) {
1361
+ const scope = (args.scope ?? "").trim().toLowerCase();
1362
+ if (scope === "delegated" || scope === "all") {
1363
+ if (context.trustLevel === undefined || context.trustLevel === "family")
1364
+ return allow;
1365
+ return deny("delegated human mail requires family trust.");
1366
+ }
1367
+ }
1368
+ return allow;
1369
+ }
1370
+ function checkCredentialTrustGuardrails(toolName, context) {
1371
+ if (CREDENTIAL_FAMILY_TOOLS.has(toolName) || COMMERCE_FAMILY_TOOLS.has(toolName)) {
1372
+ if (context.trustLevel === "family")
1373
+ return allow;
1374
+ return deny(REASONS.needsTrust);
1375
+ }
1376
+ if (CREDENTIAL_TRUSTED_TOOLS.has(toolName) || TRAVEL_TRUSTED_TOOLS.has(toolName) || A2A_TRUSTED_TOOLS.has(toolName)) {
1377
+ if ((0, friends_1.isTrustedLevel)(context.trustLevel))
1378
+ return allow;
1379
+ return deny(REASONS.needsTrust);
1380
+ }
1381
+ return allow;
1382
+ }
1383
+ function checkCommerceAuthorityGuardrails(toolName, args, context) {
1384
+ if (!COMMERCE_AUTHORITY_TOOLS.has(toolName))
1385
+ return allow;
1386
+ if (!context.agentRoot)
1387
+ return deny("commerce authority unavailable: agent root could not be resolved.");
1388
+ const result = (0, store_1.validateCommerceAuthority)({
1389
+ agentRoot: context.agentRoot,
1390
+ token: args.commerce_authority,
1391
+ toolName,
1392
+ args,
1393
+ friendId: context.friendId,
1394
+ });
1395
+ if (result.ok)
1396
+ return allow;
1397
+ return deny(`commerce authority required: ${result.reason}`);
1398
+ }
1399
+ function checkFirstClassMcpTrust(context) {
1400
+ if (!context.mcpServerName)
1401
+ return allow;
1402
+ const rules = MCP_SERVER_TRUST[context.mcpServerName] ?? { minTrust: "friend", blockGroupChat: false };
1403
+ if (!trustLevelSatisfied(rules.minTrust, context.trustLevel ?? "friend")) {
1404
+ return deny(REASONS.needsTrust);
1405
+ }
1406
+ if (rules.blockGroupChat && context.isGroupChat) {
1407
+ return deny("browser tools are only available in 1:1 conversations, not group chats.");
1408
+ }
1409
+ return allow;
1410
+ }
264
1411
  function checkTrustLevelGuardrails(toolName, args, context) {
265
- // Trusted levels (family/friend) — no trust guardrails. Undefined defaults to friend.
266
- if ((0, types_1.isTrustedLevel)(context.trustLevel))
1412
+ if (toolName === "teams_send_image" && context.trustLevel !== "family") {
1413
+ return deny("sending local images in Teams requires family trust.");
1414
+ }
1415
+ const cacheSyncResult = mailCacheSyncShellGuardrail(toolName, args, context);
1416
+ if (!cacheSyncResult.allowed)
1417
+ return cacheSyncResult;
1418
+ const mailResult = mailTrustGuardrail(toolName, args, context);
1419
+ if (!mailResult.allowed)
1420
+ return mailResult;
1421
+ if (SHEPHERD_FAMILY_TOOLS.has(toolName) && context.trustLevel !== "family") {
1422
+ return deny("the owner's terminals require family trust.");
1423
+ }
1424
+ // Credential tools have their own trust rules that apply at all levels
1425
+ const credentialResult = checkCredentialTrustGuardrails(toolName, context);
1426
+ if (!credentialResult.allowed)
1427
+ return credentialResult;
1428
+ const commerceAuthorityResult = checkCommerceAuthorityGuardrails(toolName, args, context);
1429
+ if (!commerceAuthorityResult.allowed)
1430
+ return commerceAuthorityResult;
1431
+ // First-class MCP tool trust (e.g. browser_navigate) — applies at all trust levels
1432
+ const firstClassMcpResult = checkFirstClassMcpTrust(context);
1433
+ if (!firstClassMcpResult.allowed)
1434
+ return firstClassMcpResult;
1435
+ // MCP server-specific trust via shell (e.g. ouro mcp call browser) — applies at all trust levels
1436
+ if (toolName === "shell") {
1437
+ const mcpResult = checkMcpServerTrust(args.command || "", context);
1438
+ if (!mcpResult.allowed)
1439
+ return mcpResult;
1440
+ }
1441
+ // Trusted levels (family/friend) — no further trust guardrails. Undefined defaults to friend.
1442
+ if ((0, friends_1.isTrustedLevel)(context.trustLevel))
267
1443
  return allow;
268
1444
  if (toolName === "shell") {
269
1445
  return checkShellTrustGuardrails(args.command || "", context.trustLevel);