@ouro.bot/cli 0.1.0-alpha.83 → 0.1.0-alpha.831

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 (647) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +154 -23
  3. package/RepairGuide.ouro/agent.json +5 -0
  4. package/RepairGuide.ouro/psyche/IDENTITY.md +19 -0
  5. package/RepairGuide.ouro/psyche/SOUL.md +55 -0
  6. package/RepairGuide.ouro/skills/diagnose-broken-remote.md +63 -0
  7. package/RepairGuide.ouro/skills/diagnose-stacked-typed-issues.md +35 -0
  8. package/RepairGuide.ouro/skills/diagnose-sync-blocked.md +54 -0
  9. package/RepairGuide.ouro/skills/diagnose-vault-expired.md +60 -0
  10. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +4 -2
  11. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +2 -2
  12. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
  13. package/assets/bluebubbles-host +264 -0
  14. package/assets/sanctuary-host-launcher.sh +16 -0
  15. package/changelog.json +5308 -16
  16. package/deploy/unraid/Dockerfile +54 -0
  17. package/deploy/unraid/README.txt +2983 -0
  18. package/deploy/unraid/audit-container-spec.sh +4 -0
  19. package/deploy/unraid/container-runtime.json +1 -0
  20. package/deploy/unraid/docker-man-template-transaction.mjs +711 -0
  21. package/deploy/unraid/docker-man-template-xml.cjs +105 -0
  22. package/deploy/unraid/migrate-sanctuary-bundle.mjs +60 -0
  23. package/deploy/unraid/ouro-events/bootstrap-spool.sh +92 -0
  24. package/deploy/unraid/ouro-events/emit-event.mjs +310 -0
  25. package/deploy/unraid/ouro-events/emit-usenet-event.sh +54 -0
  26. package/deploy/unraid/ouro-events/install-usenet-guard.sh +435 -0
  27. package/deploy/unraid/ouro-events/usenet-health.sh +176 -0
  28. package/deploy/unraid/sanctuary-acceptance-adapter.sh +43 -0
  29. package/deploy/unraid/sanctuary-acceptance-contract.json +198 -0
  30. package/deploy/unraid/sanctuary-acceptance-harness.sh +23 -0
  31. package/deploy/unraid/sanctuary-authority-installation.json +36 -0
  32. package/deploy/unraid/sanctuary-authority-service.sh +30 -0
  33. package/deploy/unraid/sanctuary-deployment-target.mjs +710 -0
  34. package/deploy/unraid/sanctuary-safe-rename.py +24 -0
  35. package/deploy/unraid/sanctuary-unit16-host-broker.mjs +2084 -0
  36. package/deploy/unraid/sanctuary-unit16-run.sh +560 -0
  37. package/deploy/unraid/sanctuary-unit18-target-audit.sh +27 -0
  38. package/deploy/unraid/sanctuary.ouro/agent.json +66 -0
  39. package/deploy/unraid/sanctuary.ouro/arc/README.md +3 -0
  40. package/deploy/unraid/sanctuary.ouro/bundle-meta.json +5 -0
  41. package/deploy/unraid/sanctuary.ouro/habits/sanctuary-health.md +8 -0
  42. package/deploy/unraid/sanctuary.ouro/mcp/media-mcp.mjs +795 -0
  43. package/deploy/unraid/sanctuary.ouro/provider-readiness.json +11 -0
  44. package/deploy/unraid/sanctuary.ouro/psyche/ASPIRATIONS.md +5 -0
  45. package/deploy/unraid/sanctuary.ouro/psyche/IDENTITY.md +5 -0
  46. package/deploy/unraid/sanctuary.ouro/psyche/LORE.md +15 -0
  47. package/deploy/unraid/sanctuary.ouro/psyche/SOUL.md +25 -0
  48. package/deploy/unraid/sanctuary.ouro/psyche/TACIT.md +15 -0
  49. package/deploy/unraid/sanctuary.ouro/state/policy/steward.json +7 -0
  50. package/deploy/unraid/sanctuary.ouro/tool-profiles.json +135 -0
  51. package/deploy/unraid/sanctuary.xml +28 -0
  52. package/dist/a2a/card.js +57 -0
  53. package/dist/a2a/client.js +180 -0
  54. package/dist/a2a/config.js +50 -0
  55. package/dist/a2a/delegation-stores.js +56 -0
  56. package/dist/a2a/did-resolution.js +93 -0
  57. package/dist/a2a/identity.js +170 -0
  58. package/dist/a2a/inbound-share.js +107 -0
  59. package/dist/a2a/mission-result-wire.js +196 -0
  60. package/dist/a2a/onboarding.js +115 -0
  61. package/dist/a2a/pin-store.js +132 -0
  62. package/dist/a2a/seen-ledger.js +120 -0
  63. package/dist/a2a/server.js +604 -0
  64. package/dist/a2a/task-store.js +69 -0
  65. package/dist/a2a/transport.js +45 -0
  66. package/dist/a2a/types.js +3 -0
  67. package/dist/arc/attention-types.js +8 -0
  68. package/dist/arc/cares.js +360 -0
  69. package/dist/arc/episodes.js +118 -0
  70. package/dist/arc/evolution.js +490 -0
  71. package/dist/arc/flight-recorder.js +1034 -0
  72. package/dist/arc/intentions.js +134 -0
  73. package/dist/arc/json-store.js +117 -0
  74. package/dist/arc/obligations.js +386 -0
  75. package/dist/arc/packets.js +336 -0
  76. package/dist/arc/presence.js +185 -0
  77. package/dist/arc/task-lifecycle.js +57 -0
  78. package/dist/commerce/store.js +755 -0
  79. package/dist/commerce/types.js +3 -0
  80. package/dist/heart/active-work.js +798 -40
  81. package/dist/heart/agent-entry.js +252 -3
  82. package/dist/heart/approval-files.js +113 -0
  83. package/dist/heart/approval-store.js +960 -0
  84. package/dist/heart/attachments/image-normalize.js +194 -0
  85. package/dist/heart/attachments/materialize.js +97 -0
  86. package/dist/heart/attachments/originals.js +88 -0
  87. package/dist/heart/attachments/render.js +29 -0
  88. package/dist/heart/attachments/sources/bluebubbles.js +156 -0
  89. package/dist/heart/attachments/sources/cli-local-file.js +78 -0
  90. package/dist/heart/attachments/sources/index.js +18 -0
  91. package/dist/heart/attachments/sources/telegram.js +71 -0
  92. package/dist/heart/attachments/store.js +132 -0
  93. package/dist/heart/attachments/types.js +93 -0
  94. package/dist/heart/auth/auth-flow.js +495 -0
  95. package/dist/heart/autonomy-budget.js +478 -0
  96. package/dist/heart/awaiting/await-alert.js +148 -0
  97. package/dist/heart/awaiting/await-expiry.js +143 -0
  98. package/dist/heart/awaiting/await-loader.js +91 -0
  99. package/dist/heart/awaiting/await-parser.js +149 -0
  100. package/dist/heart/awaiting/await-runtime-state.js +111 -0
  101. package/dist/heart/awaiting/await-scheduler.js +382 -0
  102. package/dist/heart/background-operations.js +281 -0
  103. package/dist/heart/bridges/manager.js +137 -17
  104. package/dist/heart/bridges/store.js +14 -2
  105. package/dist/heart/bundle-state.js +168 -0
  106. package/dist/heart/commitments.js +44 -18
  107. package/dist/heart/config-registry.js +331 -0
  108. package/dist/heart/config.js +174 -131
  109. package/dist/heart/context-loss-gauntlet.js +387 -0
  110. package/dist/heart/context-loss-sentinel.js +1157 -0
  111. package/dist/heart/core.js +2169 -581
  112. package/dist/heart/cross-chat-delivery.js +9 -22
  113. package/dist/heart/daemon/agent-config-check.js +451 -0
  114. package/dist/heart/daemon/agent-discovery.js +185 -3
  115. package/dist/heart/daemon/agent-service.js +542 -0
  116. package/dist/heart/daemon/agentic-repair.js +547 -0
  117. package/dist/heart/daemon/await-private-wake.js +59 -0
  118. package/dist/heart/daemon/bluebubbles-cli-integration.js +71 -0
  119. package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
  120. package/dist/heart/daemon/bluebubbles-host-protocol.js +433 -0
  121. package/dist/heart/daemon/bluebubbles-host.js +273 -0
  122. package/dist/heart/daemon/boot-sync-probe.js +197 -0
  123. package/dist/heart/daemon/cadence.js +180 -0
  124. package/dist/heart/daemon/cli-defaults.js +854 -0
  125. package/dist/heart/daemon/cli-desk.js +322 -0
  126. package/dist/heart/daemon/cli-exec.js +8822 -0
  127. package/dist/heart/daemon/cli-help.js +716 -0
  128. package/dist/heart/daemon/cli-parse.js +2771 -0
  129. package/dist/heart/daemon/cli-render-doctor.js +57 -0
  130. package/dist/heart/daemon/cli-render.js +777 -0
  131. package/dist/heart/daemon/cli-types.js +8 -0
  132. package/dist/heart/daemon/connect-bay.js +323 -0
  133. package/dist/heart/daemon/container-credential-bootstrap.js +273 -0
  134. package/dist/heart/daemon/container-healthcheck.js +112 -0
  135. package/dist/heart/daemon/container-runtime.js +97 -0
  136. package/dist/heart/daemon/container-spec-auditor-main.js +171 -0
  137. package/dist/heart/daemon/container-spec-auditor.js +229 -0
  138. package/dist/heart/daemon/daemon-bootstrap-startup.js +115 -0
  139. package/dist/heart/daemon/daemon-cli.js +29 -2349
  140. package/dist/heart/daemon/daemon-entry.js +930 -11
  141. package/dist/heart/daemon/daemon-health.js +186 -0
  142. package/dist/heart/daemon/daemon-rollup.js +57 -0
  143. package/dist/heart/daemon/daemon-runtime-sync.js +88 -13
  144. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  145. package/dist/heart/daemon/daemon.js +2084 -105
  146. package/dist/heart/daemon/dns-workflow.js +394 -0
  147. package/dist/heart/daemon/doctor-types.js +8 -0
  148. package/dist/heart/daemon/doctor.js +1755 -0
  149. package/dist/heart/daemon/freshness.js +345 -0
  150. package/dist/heart/daemon/habit-private-wake.js +61 -0
  151. package/dist/heart/daemon/health-monitor.js +142 -11
  152. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  153. package/dist/heart/daemon/hooks/bundle-meta.js +135 -1
  154. package/dist/heart/daemon/http-health-probe.js +80 -0
  155. package/dist/heart/daemon/human-command-screens.js +234 -0
  156. package/dist/heart/daemon/human-readiness.js +114 -0
  157. package/dist/heart/daemon/inner-status.js +78 -0
  158. package/dist/heart/daemon/interactive-repair.js +394 -0
  159. package/dist/heart/daemon/launchd.js +37 -8
  160. package/dist/heart/daemon/log-tailer.js +79 -10
  161. package/dist/heart/daemon/logs-prune.js +132 -0
  162. package/dist/heart/daemon/mcp-canary.js +524 -0
  163. package/dist/heart/daemon/message-router.js +65 -5
  164. package/dist/heart/daemon/migrate-to-desk.js +848 -0
  165. package/dist/heart/daemon/os-cron-deps.js +135 -0
  166. package/dist/heart/daemon/os-cron.js +22 -14
  167. package/dist/heart/daemon/ouro-bot-entry.js +4 -2
  168. package/dist/heart/daemon/ouro-entry.js +3 -1
  169. package/dist/heart/daemon/plugin-cli.js +432 -0
  170. package/dist/heart/daemon/process-manager.js +733 -61
  171. package/dist/heart/daemon/provider-discovery.js +137 -0
  172. package/dist/heart/daemon/provider-ping-progress.js +83 -0
  173. package/dist/heart/daemon/prunable-bundle.js +144 -0
  174. package/dist/heart/daemon/pulse.js +511 -0
  175. package/dist/heart/daemon/readiness-repair.js +365 -0
  176. package/dist/heart/daemon/run-hooks.js +2 -0
  177. package/dist/heart/daemon/runtime-logging.js +47 -14
  178. package/dist/heart/daemon/runtime-metadata.js +2 -30
  179. package/dist/heart/daemon/runtime-mode.js +13 -2
  180. package/dist/heart/daemon/safe-mode.js +161 -0
  181. package/dist/heart/daemon/sanctuary-acceptance-adapter.js +2654 -0
  182. package/dist/heart/daemon/sanctuary-acceptance-harness.js +1830 -0
  183. package/dist/heart/daemon/sanctuary-acceptance-marker.js +267 -0
  184. package/dist/heart/daemon/sanctuary-acceptance-scenarios.js +955 -0
  185. package/dist/heart/daemon/sanctuary-authority-codec.js +86 -0
  186. package/dist/heart/daemon/sanctuary-authority-epoch.js +256 -0
  187. package/dist/heart/daemon/sanctuary-authority-installation.js +140 -0
  188. package/dist/heart/daemon/sanctuary-authority-ledger.js +241 -0
  189. package/dist/heart/daemon/sanctuary-authority-root-lifecycle.js +819 -0
  190. package/dist/heart/daemon/sanctuary-authority-vault-migration.js +79 -0
  191. package/dist/heart/daemon/sanctuary-bundle-migration.js +729 -0
  192. package/dist/heart/daemon/sanctuary-host-authority.js +1008 -0
  193. package/dist/heart/daemon/sanctuary-host-detached-supervisor.js +494 -0
  194. package/dist/heart/daemon/sanctuary-host-executor.js +670 -0
  195. package/dist/heart/daemon/sanctuary-host-linux-kernel.js +334 -0
  196. package/dist/heart/daemon/sanctuary-host-supervisor-entry.js +207 -0
  197. package/dist/heart/daemon/sanctuary-host-supervisor.js +95 -0
  198. package/dist/heart/daemon/sanctuary-package-management.js +103 -0
  199. package/dist/heart/daemon/sanctuary-scheduler-liveness.js +227 -0
  200. package/dist/heart/daemon/sanctuary-scheduler-origin.js +132 -0
  201. package/dist/heart/daemon/sanctuary-telegram-authority-entry.js +445 -0
  202. package/dist/heart/daemon/sanctuary-telegram-authority-gateway.js +585 -0
  203. package/dist/heart/daemon/sanctuary-telegram-authority-service.js +630 -0
  204. package/dist/heart/daemon/sense-manager.js +699 -48
  205. package/dist/heart/daemon/session-id-resolver.js +131 -0
  206. package/dist/heart/daemon/skill-management-installer.js +1 -1
  207. package/dist/heart/daemon/socket-client.js +180 -12
  208. package/dist/heart/daemon/stale-bundle-prune.js +113 -0
  209. package/dist/heart/daemon/startup-tui.js +339 -0
  210. package/dist/heart/daemon/supercronic-supervisor.js +282 -0
  211. package/dist/heart/daemon/task-scheduler.js +117 -39
  212. package/dist/heart/daemon/terminal-ui.js +499 -0
  213. package/dist/heart/daemon/thoughts.js +138 -54
  214. package/dist/heart/daemon/up-progress.js +366 -0
  215. package/dist/heart/daemon/vault-items.js +56 -0
  216. package/dist/heart/delegation.js +1 -4
  217. package/dist/heart/external-events/router.js +1430 -0
  218. package/dist/heart/frontend-approval-runtime.js +506 -0
  219. package/dist/heart/frontend-journal.js +215 -0
  220. package/dist/heart/frontend-session-service.js +237 -0
  221. package/dist/heart/frontend-socket-client.js +164 -0
  222. package/dist/heart/frontend-socket.js +400 -0
  223. package/dist/heart/habits/habit-cancel.js +810 -0
  224. package/dist/heart/habits/habit-lifecycle.js +1327 -0
  225. package/dist/heart/habits/habit-migration.js +194 -0
  226. package/dist/heart/habits/habit-parser.js +278 -0
  227. package/dist/heart/habits/habit-runtime-state.js +93 -0
  228. package/dist/heart/habits/habit-scheduler.js +653 -0
  229. package/dist/heart/habits/habit-session-summary.js +318 -0
  230. package/dist/heart/habits/habit-session.js +636 -0
  231. package/dist/heart/{daemon → hatch}/hatch-flow.js +55 -62
  232. package/dist/heart/{daemon → hatch}/hatch-specialist.js +6 -8
  233. package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
  234. package/dist/heart/{daemon → hatch}/specialist-tools.js +53 -18
  235. package/dist/heart/identity.js +196 -59
  236. package/dist/heart/kept-notes.js +289 -0
  237. package/dist/heart/kicks.js +1 -1
  238. package/dist/heart/machine-identity.js +161 -0
  239. package/dist/heart/mail-import-discovery.js +356 -0
  240. package/dist/heart/mailbox/mailbox-http-hooks.js +98 -0
  241. package/dist/heart/mailbox/mailbox-http-response.js +7 -0
  242. package/dist/heart/mailbox/mailbox-http-routes.js +398 -0
  243. package/dist/heart/mailbox/mailbox-http-static.js +103 -0
  244. package/dist/heart/mailbox/mailbox-http-transport.js +129 -0
  245. package/dist/heart/mailbox/mailbox-http.js +99 -0
  246. package/dist/heart/mailbox/mailbox-read.js +39 -0
  247. package/dist/heart/mailbox/mailbox-types.js +27 -0
  248. package/dist/heart/mailbox/mailbox-view.js +197 -0
  249. package/dist/heart/mailbox/readers/agent-machine.js +428 -0
  250. package/dist/heart/mailbox/readers/continuity-readers.js +329 -0
  251. package/dist/heart/mailbox/readers/mail.js +375 -0
  252. package/dist/heart/mailbox/readers/runtime-readers.js +823 -0
  253. package/dist/heart/mailbox/readers/sessions.js +231 -0
  254. package/dist/heart/mailbox/readers/shared.js +111 -0
  255. package/dist/heart/mcp/mcp-server.js +696 -0
  256. package/dist/heart/migrate-config.js +100 -0
  257. package/dist/heart/model-capabilities.js +11 -0
  258. package/dist/heart/orientation-frame.js +345 -0
  259. package/dist/heart/platform.js +81 -0
  260. package/dist/heart/primitives.js +1 -1
  261. package/dist/heart/private-runtime/decision-renderer.js +158 -0
  262. package/dist/heart/private-runtime/index.js +18 -0
  263. package/dist/heart/private-runtime/ledger.js +221 -0
  264. package/dist/heart/private-runtime/policy.js +252 -0
  265. package/dist/heart/private-runtime/types.js +2 -0
  266. package/dist/heart/progress-story.js +1 -1
  267. package/dist/heart/provider-attempt.js +137 -0
  268. package/dist/heart/provider-binding-resolver.js +273 -0
  269. package/dist/heart/provider-credentials.js +449 -0
  270. package/dist/heart/provider-failover.js +311 -0
  271. package/dist/heart/provider-models.js +96 -0
  272. package/dist/heart/provider-ping.js +265 -0
  273. package/dist/heart/provider-readiness-cache.js +119 -0
  274. package/dist/heart/provider-visibility.js +188 -0
  275. package/dist/heart/providers/anthropic-token.js +131 -0
  276. package/dist/heart/providers/anthropic.js +173 -60
  277. package/dist/heart/providers/azure.js +23 -11
  278. package/dist/heart/providers/error-classification.js +127 -0
  279. package/dist/heart/providers/github-copilot.js +34 -38
  280. package/dist/heart/providers/minimax-vlm.js +189 -0
  281. package/dist/heart/providers/minimax.js +26 -8
  282. package/dist/heart/providers/openai-codex-token.js +349 -0
  283. package/dist/heart/providers/openai-codex.js +55 -40
  284. package/dist/heart/providers/openai-compatible.js +177 -0
  285. package/dist/heart/run-ledger.js +247 -0
  286. package/dist/heart/runtime-capability-check.js +170 -0
  287. package/dist/heart/runtime-credentials.js +607 -0
  288. package/dist/heart/runtime-cwd.js +87 -0
  289. package/dist/heart/sense-truth.js +22 -4
  290. package/dist/heart/session-activity.js +92 -24
  291. package/dist/heart/session-events.js +1504 -0
  292. package/dist/heart/session-playback-cli-main.js +5 -0
  293. package/dist/heart/session-playback-cli.js +36 -0
  294. package/dist/heart/session-playback.js +231 -0
  295. package/dist/heart/session-redaction-repair-cli-main.js +31 -0
  296. package/dist/heart/session-redaction-repair.js +552 -0
  297. package/dist/heart/session-stats-cli-main.js +5 -0
  298. package/dist/heart/session-stats.js +183 -0
  299. package/dist/heart/session-transcript.js +133 -0
  300. package/dist/heart/start-of-turn-packet.js +372 -0
  301. package/dist/heart/steward-policy.js +618 -0
  302. package/dist/heart/streaming.js +604 -207
  303. package/dist/heart/structured-output.js +196 -0
  304. package/dist/heart/sync-classification.js +176 -0
  305. package/dist/heart/sync.js +449 -0
  306. package/dist/heart/target-resolution.js +11 -7
  307. package/dist/heart/tempo.js +93 -0
  308. package/dist/heart/temporal-view.js +41 -0
  309. package/dist/heart/timeouts.js +101 -0
  310. package/dist/heart/tool-activity-callbacks.js +59 -0
  311. package/dist/heart/tool-approval.js +382 -0
  312. package/dist/heart/tool-description.js +155 -0
  313. package/dist/heart/tool-friction.js +55 -0
  314. package/dist/heart/tool-loop.js +200 -0
  315. package/dist/heart/turn-context.js +482 -0
  316. package/dist/heart/turn-execution-lease.js +30 -0
  317. package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +6 -5
  318. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  319. package/dist/heart/versioning/ouro-path-installer.js +432 -0
  320. package/dist/heart/versioning/ouro-recovery-launcher.js +76 -0
  321. package/dist/heart/versioning/ouro-version-manager.js +409 -0
  322. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  323. package/dist/heart/{daemon → versioning}/update-checker.js +6 -1
  324. package/dist/heart/versioning/update-hooks.js +154 -0
  325. package/dist/heart/versioning/version-intent.js +114 -0
  326. package/dist/heart/work-card.js +380 -0
  327. package/dist/mailbox-ui/assets/index-Cyv4mw3Y.js +1 -0
  328. package/dist/mailbox-ui/assets/index-Du_9G9WO.css +1 -0
  329. package/dist/mailbox-ui/assets/vendor-CcN1XpQ9.js +61 -0
  330. package/dist/mailbox-ui/index.html +16 -0
  331. package/dist/mailroom/attention.js +167 -0
  332. package/dist/mailroom/autonomy.js +209 -0
  333. package/dist/mailroom/blob-store.js +889 -0
  334. package/dist/mailroom/body-cache.js +61 -0
  335. package/dist/mailroom/cache-sync-cli.js +58 -0
  336. package/dist/mailroom/core.js +803 -0
  337. package/dist/mailroom/entry.js +160 -0
  338. package/dist/mailroom/file-store.js +568 -0
  339. package/dist/mailroom/hosted-cache-sync.js +384 -0
  340. package/dist/mailroom/mbox-import.js +393 -0
  341. package/dist/mailroom/migration.js +164 -0
  342. package/dist/mailroom/outbound.js +380 -0
  343. package/dist/mailroom/policy.js +263 -0
  344. package/dist/mailroom/reader.js +292 -0
  345. package/dist/mailroom/search-cache.js +513 -0
  346. package/dist/mailroom/search-relevance.js +319 -0
  347. package/dist/mailroom/smtp-ingress.js +176 -0
  348. package/dist/mailroom/source-state.js +176 -0
  349. package/dist/mailroom/thread.js +109 -0
  350. package/dist/mailroom/travel-extract.js +89 -0
  351. package/dist/mind/bundle-manifest.js +24 -2
  352. package/dist/mind/context.js +309 -111
  353. package/dist/mind/desk-section.js +362 -0
  354. package/dist/mind/diary-integrity.js +60 -0
  355. package/dist/mind/{memory.js → diary.js} +68 -77
  356. package/dist/mind/embedding-provider.js +60 -0
  357. package/dist/mind/file-state.js +179 -0
  358. package/dist/mind/{associative-recall.js → note-search.js} +47 -58
  359. package/dist/mind/obligation-steering.js +199 -3
  360. package/dist/mind/pending.js +36 -10
  361. package/dist/mind/prompt-budget.js +479 -0
  362. package/dist/mind/prompt-refresh.js +3 -2
  363. package/dist/mind/prompt.js +1050 -189
  364. package/dist/mind/provenance-trust.js +26 -0
  365. package/dist/mind/record-paths.js +312 -0
  366. package/dist/mind/scrutiny.js +173 -0
  367. package/dist/mind/session-transaction.js +564 -0
  368. package/dist/nerves/cli-logging.js +7 -1
  369. package/dist/nerves/coverage/audit-rules.js +15 -6
  370. package/dist/nerves/coverage/audit.js +28 -2
  371. package/dist/nerves/coverage/cli.js +1 -1
  372. package/dist/nerves/coverage/contract.js +5 -5
  373. package/dist/nerves/coverage/file-completeness.js +139 -5
  374. package/dist/nerves/coverage/run-artifacts.js +55 -35
  375. package/dist/nerves/coverage/source-scanner.js +4 -4
  376. package/dist/nerves/event-buffer.js +111 -0
  377. package/dist/nerves/index.js +293 -9
  378. package/dist/nerves/observation.js +20 -0
  379. package/dist/nerves/redact.js +79 -0
  380. package/dist/nerves/review/cli-main.js +5 -0
  381. package/dist/nerves/review/cli.js +156 -0
  382. package/dist/nerves/review/core.js +159 -0
  383. package/dist/nerves/runtime.js +17 -1
  384. package/dist/repertoire/ado-client.js +15 -56
  385. package/dist/repertoire/ado-semantic.js +16 -10
  386. package/dist/repertoire/api-client.js +97 -0
  387. package/dist/repertoire/bitwarden-store.js +1045 -0
  388. package/dist/repertoire/bundle-templates.js +71 -0
  389. package/dist/repertoire/bw-installer.js +180 -0
  390. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  391. package/dist/repertoire/coding/context-pack.js +331 -0
  392. package/dist/repertoire/coding/feedback.js +174 -32
  393. package/dist/repertoire/coding/index.js +15 -3
  394. package/dist/repertoire/coding/manager.js +159 -10
  395. package/dist/repertoire/coding/spawner.js +58 -11
  396. package/dist/repertoire/coding/tools.js +221 -15
  397. package/dist/repertoire/coding/workbench-client.js +272 -0
  398. package/dist/repertoire/coding/workbench-manager.js +346 -0
  399. package/dist/repertoire/commerce-errors.js +109 -0
  400. package/dist/repertoire/commerce-self-test.js +156 -0
  401. package/dist/repertoire/credential-access.js +178 -0
  402. package/dist/repertoire/desk/classifier.js +362 -0
  403. package/dist/repertoire/duffel-client.js +185 -0
  404. package/dist/repertoire/github-client.js +14 -55
  405. package/dist/repertoire/graph-client.js +11 -52
  406. package/dist/repertoire/guardrails.js +811 -18
  407. package/dist/repertoire/mcp-client.js +44 -3
  408. package/dist/repertoire/mcp-manager.js +427 -139
  409. package/dist/repertoire/mcp-tools.js +124 -0
  410. package/dist/repertoire/plugin-mcp.js +175 -0
  411. package/dist/repertoire/plugins.js +253 -0
  412. package/dist/repertoire/relationship-authorization.js +322 -0
  413. package/dist/repertoire/shell-sessions.js +134 -0
  414. package/dist/repertoire/skills.js +48 -4
  415. package/dist/repertoire/stripe-client.js +131 -0
  416. package/dist/repertoire/tool-arguments.js +170 -0
  417. package/dist/repertoire/tool-results.js +29 -0
  418. package/dist/repertoire/tools-a2a.js +699 -0
  419. package/dist/repertoire/tools-attachments.js +322 -0
  420. package/dist/repertoire/tools-awaiting.js +575 -0
  421. package/dist/repertoire/tools-base.js +77 -1138
  422. package/dist/repertoire/tools-bluebubbles.js +2 -0
  423. package/dist/repertoire/tools-bridge.js +144 -0
  424. package/dist/repertoire/tools-bundle.js +993 -0
  425. package/dist/repertoire/tools-commerce.js +253 -0
  426. package/dist/repertoire/tools-config.js +186 -0
  427. package/dist/repertoire/tools-continuity.js +575 -0
  428. package/dist/repertoire/tools-credential.js +383 -0
  429. package/dist/repertoire/tools-evolution.js +527 -0
  430. package/dist/repertoire/tools-files.js +344 -0
  431. package/dist/repertoire/tools-flight.js +290 -0
  432. package/dist/repertoire/tools-flow.js +119 -0
  433. package/dist/repertoire/tools-github.js +3 -8
  434. package/dist/repertoire/tools-habits.js +103 -0
  435. package/dist/repertoire/tools-mail.js +1828 -0
  436. package/dist/repertoire/tools-notes.js +485 -0
  437. package/dist/repertoire/tools-obligations.js +182 -0
  438. package/dist/repertoire/tools-orientation.js +31 -0
  439. package/dist/repertoire/tools-record.js +482 -0
  440. package/dist/repertoire/tools-rsvp.js +139 -0
  441. package/dist/repertoire/tools-runtime.js +150 -0
  442. package/dist/repertoire/tools-sanctuary-host.js +202 -0
  443. package/dist/repertoire/tools-session.js +1133 -0
  444. package/dist/repertoire/tools-shell.js +149 -0
  445. package/dist/repertoire/tools-steward-policy.js +119 -0
  446. package/dist/repertoire/tools-stripe.js +224 -0
  447. package/dist/repertoire/tools-surface.js +369 -0
  448. package/dist/repertoire/tools-teams.js +12 -39
  449. package/dist/repertoire/tools-telegram-contacts.js +37 -0
  450. package/dist/repertoire/tools-travel.js +125 -0
  451. package/dist/repertoire/tools-trip.js +982 -0
  452. package/dist/repertoire/tools-unraid.js +531 -0
  453. package/dist/repertoire/tools-user-profile.js +146 -0
  454. package/dist/repertoire/tools-vault.js +40 -0
  455. package/dist/repertoire/tools-voice.js +145 -0
  456. package/dist/repertoire/tools.js +494 -148
  457. package/dist/repertoire/travel-api-client.js +360 -0
  458. package/dist/repertoire/unraid-client.js +155 -0
  459. package/dist/repertoire/unraid-restart.js +339 -0
  460. package/dist/repertoire/user-profile.js +131 -0
  461. package/dist/repertoire/vault-setup.js +246 -0
  462. package/dist/repertoire/vault-unlock.js +628 -0
  463. package/dist/rsvp/aisleplanner-client.js +354 -0
  464. package/dist/rsvp/cli.js +926 -0
  465. package/dist/rsvp/config.js +327 -0
  466. package/dist/rsvp/cutover.js +433 -0
  467. package/dist/rsvp/diagnostics.js +232 -0
  468. package/dist/rsvp/diff-renderer.js +176 -0
  469. package/dist/rsvp/habit-policy.js +110 -0
  470. package/dist/rsvp/habit-stage.js +123 -0
  471. package/dist/rsvp/incident-bundle.js +97 -0
  472. package/dist/rsvp/migration.js +204 -0
  473. package/dist/rsvp/native-habit-runner.js +630 -0
  474. package/dist/rsvp/outbound-state.js +628 -0
  475. package/dist/rsvp/query.js +118 -0
  476. package/dist/rsvp/replay.js +233 -0
  477. package/dist/rsvp/snapshot.js +232 -0
  478. package/dist/rsvp/spend-ledger.js +175 -0
  479. package/dist/scripts/claude-code-hook.js +41 -0
  480. package/dist/scripts/claude-code-stop-hook.js +47 -0
  481. package/dist/senses/a2a-entry.js +115 -0
  482. package/dist/senses/acp-server.js +386 -0
  483. package/dist/senses/attention-queue.js +186 -0
  484. package/dist/senses/await-turn-message.js +58 -0
  485. package/dist/senses/bluebubbles/active-turns.js +216 -0
  486. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  487. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  488. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +355 -40
  489. package/dist/senses/bluebubbles/context-packet.js +207 -0
  490. package/dist/senses/bluebubbles/context-smoke.js +144 -0
  491. package/dist/senses/bluebubbles/entry.js +90 -0
  492. package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +21 -3
  493. package/dist/senses/bluebubbles/index.js +3841 -0
  494. package/dist/senses/bluebubbles/latest-turn.js +311 -0
  495. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -71
  496. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +159 -28
  497. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +24 -13
  498. package/dist/senses/bluebubbles/outbound-state.js +308 -0
  499. package/dist/senses/bluebubbles/processed-log.js +133 -0
  500. package/dist/senses/bluebubbles/reaction-policy.js +47 -0
  501. package/dist/senses/bluebubbles/replay.js +284 -0
  502. package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +30 -2
  503. package/dist/senses/bluebubbles/semantic-receipts.js +1986 -0
  504. package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
  505. package/dist/senses/bluebubbles/webhook-registration.js +228 -0
  506. package/dist/senses/bluebubbles-meta-guard.js +39 -0
  507. package/dist/senses/cli/bracketed-paste.js +82 -0
  508. package/dist/senses/cli/image-paste.js +287 -0
  509. package/dist/senses/cli/image-ref-navigation.js +75 -0
  510. package/dist/senses/cli/ink-app.js +156 -0
  511. package/dist/senses/cli/inline-diff.js +64 -0
  512. package/dist/senses/cli/input-keys.js +174 -0
  513. package/dist/senses/cli/kill-ring.js +86 -0
  514. package/dist/senses/cli/message-list.js +51 -0
  515. package/dist/senses/cli/ouro-tui.js +607 -0
  516. package/dist/senses/cli/spinner-imperative.js +135 -0
  517. package/dist/senses/cli/spinner.js +101 -0
  518. package/dist/senses/cli/status-line.js +60 -0
  519. package/dist/senses/cli/streaming-markdown.js +526 -0
  520. package/dist/senses/cli/tool-display.js +85 -0
  521. package/dist/senses/cli/tool-render.js +85 -0
  522. package/dist/senses/cli/tui-store.js +241 -0
  523. package/dist/senses/cli/virtual-list.js +35 -0
  524. package/dist/senses/cli-entry.js +60 -8
  525. package/dist/senses/cli.js +604 -320
  526. package/dist/senses/commands.js +66 -3
  527. package/dist/senses/context-packet-ledger.js +371 -0
  528. package/dist/senses/context-packets.js +154 -0
  529. package/dist/senses/habit-lifecycle-guard.js +24 -0
  530. package/dist/senses/habit-turn-message.js +160 -0
  531. package/dist/senses/ingress-evidence.js +2 -0
  532. package/dist/senses/mail-entry.js +66 -0
  533. package/dist/senses/mail.js +524 -0
  534. package/dist/senses/pipeline.js +1115 -204
  535. package/dist/senses/private-runtime-worker.js +859 -0
  536. package/dist/senses/private-runtime.js +1517 -0
  537. package/dist/senses/proactive-content-guard.js +51 -0
  538. package/dist/senses/root-host-approval-port.js +345 -0
  539. package/dist/senses/root-host-approval-runtime.js +393 -0
  540. package/dist/senses/sanctuary-authority-resident.js +102 -0
  541. package/dist/senses/sanctuary-download-credit-presentation.js +31 -0
  542. package/dist/senses/sanctuary-full-visibility-contract.js +302 -0
  543. package/dist/senses/sanctuary-grounding.js +118 -0
  544. package/dist/senses/sanctuary-health-acceptance-probe-entry.js +12 -0
  545. package/dist/senses/sanctuary-health-acceptance-probe.js +774 -0
  546. package/dist/senses/sanctuary-health-runner.js +100 -0
  547. package/dist/senses/sanctuary-health.js +408 -0
  548. package/dist/senses/sanctuary-install-state-contract.js +126 -0
  549. package/dist/senses/sanctuary-interactive-control.js +410 -0
  550. package/dist/senses/sanctuary-media-catalog-contract.js +297 -0
  551. package/dist/senses/sanctuary-media-optimization.js +514 -0
  552. package/dist/senses/sanctuary-runtime.js +340 -0
  553. package/dist/senses/sanctuary-sab.js +85 -0
  554. package/dist/senses/sanctuary-storage-optimization-contract.js +32 -0
  555. package/dist/senses/shared-turn.js +772 -0
  556. package/dist/senses/surface-tool.js +109 -0
  557. package/dist/senses/teams-entry.js +60 -8
  558. package/dist/senses/teams.js +519 -218
  559. package/dist/senses/telegram-admission.js +718 -0
  560. package/dist/senses/telegram-approval-runtime.js +793 -0
  561. package/dist/senses/telegram-attachments.js +186 -0
  562. package/dist/senses/telegram-audit-ledger.js +220 -0
  563. package/dist/senses/telegram-authority-transport.js +231 -0
  564. package/dist/senses/telegram-client.js +1749 -0
  565. package/dist/senses/telegram-effect-adapter.js +896 -0
  566. package/dist/senses/telegram-entry.js +83 -0
  567. package/dist/senses/telegram.js +2324 -0
  568. package/dist/senses/trust-gate.js +104 -9
  569. package/dist/senses/voice/audio-playback.js +237 -0
  570. package/dist/senses/voice/audio-routing.js +119 -0
  571. package/dist/senses/voice/elevenlabs.js +202 -0
  572. package/dist/senses/voice/floor-control.js +431 -0
  573. package/dist/senses/voice/floor-controller.js +115 -0
  574. package/dist/senses/voice/golden-path.js +116 -0
  575. package/dist/senses/voice/index.js +29 -0
  576. package/dist/senses/voice/meeting.js +113 -0
  577. package/dist/senses/voice/outbound.js +190 -0
  578. package/dist/senses/voice/phone.js +33 -0
  579. package/dist/senses/voice/playback.js +139 -0
  580. package/dist/senses/voice/realtime-eval.js +496 -0
  581. package/dist/senses/voice/realtime-trace.js +531 -0
  582. package/dist/senses/voice/transcript.js +70 -0
  583. package/dist/senses/voice/turn.js +191 -0
  584. package/dist/senses/voice/twilio-phone-runtime.js +807 -0
  585. package/dist/senses/voice/twilio-phone.js +5031 -0
  586. package/dist/senses/voice/types.js +2 -0
  587. package/dist/senses/voice/whisper.js +161 -0
  588. package/dist/senses/voice-entry.js +81 -0
  589. package/dist/senses/voice-realtime-eval-command.js +99 -0
  590. package/dist/senses/voice-realtime-eval-entry.js +21 -0
  591. package/dist/senses/voice-twilio-entry.js +87 -0
  592. package/dist/trips/core.js +138 -0
  593. package/dist/trips/store.js +265 -0
  594. package/dist/util/frontmatter.js +69 -0
  595. package/npm-shrinkwrap.json +8138 -0
  596. package/package.json +72 -13
  597. package/skills/agent-commerce.md +113 -0
  598. package/skills/browser-navigation.md +117 -0
  599. package/skills/commerce-setup-guide.md +116 -0
  600. package/skills/commerce-setup.md +84 -0
  601. package/skills/configure-dev-tools.md +99 -0
  602. package/skills/travel-planning.md +138 -0
  603. package/dist/heart/daemon/auth-flow.js +0 -430
  604. package/dist/heart/daemon/ouro-path-installer.js +0 -260
  605. package/dist/heart/daemon/ouro-version-manager.js +0 -171
  606. package/dist/heart/daemon/update-hooks.js +0 -138
  607. package/dist/heart/obligations.js +0 -191
  608. package/dist/heart/safe-workspace.js +0 -228
  609. package/dist/heart/session-recall.js +0 -116
  610. package/dist/mind/friends/channel.js +0 -84
  611. package/dist/mind/friends/group-context.js +0 -144
  612. package/dist/mind/friends/resolver.js +0 -84
  613. package/dist/mind/friends/store-file.js +0 -190
  614. package/dist/mind/friends/store.js +0 -4
  615. package/dist/mind/friends/tokens.js +0 -26
  616. package/dist/mind/friends/trust-explanation.js +0 -74
  617. package/dist/mind/friends/types.js +0 -29
  618. package/dist/repertoire/tasks/board.js +0 -134
  619. package/dist/repertoire/tasks/index.js +0 -224
  620. package/dist/repertoire/tasks/lifecycle.js +0 -80
  621. package/dist/repertoire/tasks/middleware.js +0 -65
  622. package/dist/repertoire/tasks/parser.js +0 -173
  623. package/dist/repertoire/tasks/scanner.js +0 -132
  624. package/dist/repertoire/tasks/transitions.js +0 -144
  625. package/dist/senses/bluebubbles-entry.js +0 -13
  626. package/dist/senses/bluebubbles.js +0 -1181
  627. package/dist/senses/debug-activity.js +0 -154
  628. package/dist/senses/inner-dialog-worker.js +0 -90
  629. package/dist/senses/inner-dialog.js +0 -525
  630. package/subagents/README.md +0 -7
  631. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  632. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  633. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  634. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  635. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  636. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  637. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  638. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  639. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  640. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  641. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  642. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  643. /package/dist/{repertoire/tasks/types.js → heart/attachments/sources/adapter.js} +0 -0
  644. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  645. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  646. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  647. /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
@@ -34,10 +34,13 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.DEFAULT_FLUSH_INTERVAL_MS = void 0;
37
+ exports.aiLabelEntities = aiLabelEntities;
37
38
  exports.stripMentions = stripMentions;
38
39
  exports.splitMessage = splitMessage;
40
+ exports.sanitizeFeedbackComment = sanitizeFeedbackComment;
41
+ exports.buildFeedbackSyntheticText = buildFeedbackSyntheticText;
42
+ exports.buildWelcomeCard = buildWelcomeCard;
39
43
  exports.createTeamsCallbacks = createTeamsCallbacks;
40
- exports.resolvePendingConfirmation = resolvePendingConfirmation;
41
44
  exports.withConversationLock = withConversationLock;
42
45
  exports.handleTeamsMessage = handleTeamsMessage;
43
46
  exports.sendProactiveTeamsMessageToSession = sendProactiveTeamsMessageToSession;
@@ -48,7 +51,7 @@ const teams_apps_1 = require("@microsoft/teams.apps");
48
51
  const teams_dev_1 = require("@microsoft/teams.dev");
49
52
  const core_1 = require("../heart/core");
50
53
  const tools_1 = require("../repertoire/tools");
51
- const channel_1 = require("../mind/friends/channel");
54
+ const friends_1 = require("@ouro.bot/friends");
52
55
  const config_1 = require("../heart/config");
53
56
  const prompt_1 = require("../mind/prompt");
54
57
  const phrases_1 = require("../mind/phrases");
@@ -58,20 +61,31 @@ const context_1 = require("../mind/context");
58
61
  const commands_1 = require("./commands");
59
62
  const nerves_1 = require("../nerves");
60
63
  const runtime_1 = require("../nerves/runtime");
61
- const store_file_1 = require("../mind/friends/store-file");
62
- const types_1 = require("../mind/friends/types");
63
- const resolver_1 = require("../mind/friends/resolver");
64
- const tokens_1 = require("../mind/friends/tokens");
64
+ const proactive_content_guard_1 = require("./proactive-content-guard");
65
65
  const turn_coordinator_1 = require("../heart/turn-coordinator");
66
66
  const identity_1 = require("../heart/identity");
67
67
  const mcp_manager_1 = require("../repertoire/mcp-manager");
68
68
  const progress_story_1 = require("../heart/progress-story");
69
+ const tool_activity_callbacks_1 = require("../heart/tool-activity-callbacks");
70
+ const commands_2 = require("./commands");
69
71
  const http = __importStar(require("http"));
70
72
  const path = __importStar(require("path"));
71
73
  const trust_gate_1 = require("./trust-gate");
72
74
  const pipeline_1 = require("./pipeline");
75
+ const session_transaction_1 = require("../mind/session-transaction");
76
+ const teamsFailoverStates = new Map();
73
77
  const pending_1 = require("../mind/pending");
74
78
  const continuity_1 = require("./continuity");
79
+ // AIGeneratedContent entity and feedbackLoopEnabled channelData for all outbound
80
+ // Teams messages. Required by Teams AI UX best practices.
81
+ function aiLabelEntities() {
82
+ return [{
83
+ type: "https://schema.org/Message",
84
+ "@type": "Message",
85
+ "@context": "https://schema.org",
86
+ additionalType: ["AIGeneratedContent"],
87
+ }];
88
+ }
75
89
  // Strip @mention markup from incoming messages.
76
90
  // Removes <at>...</at> tags and trims extra whitespace.
77
91
  // Fallback safety net -- the SDK's activity.mentions.stripText should handle
@@ -126,6 +140,46 @@ function splitMessage(text, maxLen) {
126
140
  }
127
141
  return chunks;
128
142
  }
143
+ // Sanitize user-provided feedback comments: truncate, strip control chars and newlines.
144
+ function sanitizeFeedbackComment(comment) {
145
+ const cleaned = comment.replace(/[\x00-\x1f\n\r]/g, "");
146
+ return cleaned.length > 200 ? cleaned.slice(0, 200) : cleaned;
147
+ }
148
+ // Build synthetic message text from a Teams feedback reaction.
149
+ function buildFeedbackSyntheticText(reaction, comment) {
150
+ const emoji = reaction === "like" ? "thumbs-up" : "thumbs-down";
151
+ if (comment) {
152
+ const sanitized = sanitizeFeedbackComment(comment);
153
+ return `[reacted with ${emoji} to your message: "${sanitized}"]`;
154
+ }
155
+ return `[reacted with ${emoji} to your message]`;
156
+ }
157
+ // Build a welcome Adaptive Card with prompt starters for new bot installs.
158
+ function buildWelcomeCard() {
159
+ const promptStarters = [
160
+ "What can you help me with?",
161
+ "Tell me about yourself",
162
+ "What's on my calendar today?",
163
+ "Summarize my recent emails",
164
+ ];
165
+ return {
166
+ type: "AdaptiveCard",
167
+ version: "1.5",
168
+ body: [
169
+ {
170
+ type: "TextBlock",
171
+ text: "Hey! I'm here and ready to help. Try one of these to get started, or just ask me anything.",
172
+ wrap: true,
173
+ size: "Medium",
174
+ },
175
+ ],
176
+ actions: promptStarters.map((prompt) => ({
177
+ type: "Action.Submit",
178
+ title: prompt,
179
+ data: { msteams: { type: "messageBack", text: prompt, displayText: prompt } },
180
+ })),
181
+ };
182
+ }
129
183
  // Create Teams-specific callbacks for the agent loop.
130
184
  // The SDK handles cumulative text, debouncing (500ms), and the streaming
131
185
  // protocol (streamSequence, streamId, informative/streaming/final types).
@@ -138,12 +192,16 @@ function splitMessage(text, maxLen) {
138
192
  // (transient status) or safeSend (terminal errors). Reasoning is accumulated
139
193
  // and periodically pushed via safeUpdate on the same flush timer tick.
140
194
  function createTeamsCallbacks(stream, controller, sendMessage, options) {
195
+ const MIN_INITIAL_CHARS = 20;
141
196
  let stopped = false; // set when stream signals cancellation (403)
142
197
  let hadToolRun = false;
143
198
  let hadRealOutput = false; // true once reasoning/tool output shown; suppresses phrases
144
199
  let reasoningBuf = ""; // accumulated reasoning text for status display
200
+ let totalEmitted = 0; // cumulative chars emitted via safeEmit (for >4000 finalization)
201
+ let streamFinalized = false; // true after stream.close() — subsequent flushes go to safeSend
145
202
  let textBuffer = ""; // accumulated text output for chunked streaming
146
203
  let streamHasContent = false; // tracks whether primary output has received content
204
+ let firstContentEmitted = false; // true after first content push — disables MIN_INITIAL_CHARS threshold
147
205
  let phraseTimer = null;
148
206
  let lastPhrase = "";
149
207
  let flushTimer = null;
@@ -193,15 +251,16 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
193
251
  result.catch(() => markStopped());
194
252
  }
195
253
  }
196
- // Safely emit a text delta to the stream.
254
+ // Safely emit a text delta to the stream with AI labels.
197
255
  // On error (e.g. 403 from Teams stop button), abort the controller.
198
256
  function safeEmit(text) {
199
257
  /* v8 ignore next -- defensive guard: stopped set by prior 403; tested via flush abort path @preserve */
200
258
  if (stopped)
201
259
  return;
202
260
  try {
203
- catchAsync(stream.emit(text));
261
+ catchAsync(stream.emit({ text, entities: aiLabelEntities(), channelData: { feedbackLoopEnabled: true } }));
204
262
  streamHasContent = true;
263
+ totalEmitted += text.length;
205
264
  }
206
265
  catch {
207
266
  markStopped();
@@ -216,7 +275,7 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
216
275
  try {
217
276
  // stream.emit() is typed as void but the Teams SDK returns a Promise
218
277
  // internally (async HTTP). Cast to capture the result for awaiting.
219
- const result = stream.emit(text);
278
+ const result = stream.emit({ text, entities: aiLabelEntities(), channelData: { feedbackLoopEnabled: true } });
220
279
  streamHasContent = true;
221
280
  if (result && typeof result.then === "function") {
222
281
  await result;
@@ -228,6 +287,26 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
228
287
  return false;
229
288
  }
230
289
  }
290
+ // Non-aborting awaitable emit — returns true on success, false on failure WITHOUT
291
+ // calling markStopped() / aborting the controller. Used by flushNow (speak) so a
292
+ // primary-stream failure followed by a successful sendMessage fallback does NOT
293
+ // poison the rest of the turn. tryEmit's abort-on-failure behavior is correct for
294
+ // end-of-turn flush() (no fallback path forward) but wrong for mid-turn speak,
295
+ // which has a sendMessage fallback that may still succeed. Caller (flushNow) is
296
+ // responsible for the `!stopped` precondition; no defensive guard here.
297
+ async function tryEmitNoAbort(text) {
298
+ try {
299
+ const result = stream.emit({ text, entities: aiLabelEntities(), channelData: { feedbackLoopEnabled: true } });
300
+ streamHasContent = true;
301
+ if (result && typeof result.then === "function") {
302
+ await result;
303
+ }
304
+ return true;
305
+ }
306
+ catch {
307
+ return false;
308
+ }
309
+ }
231
310
  // Safely send a status update to the stream.
232
311
  // On error (e.g. 403 from Teams stop button), abort the controller.
233
312
  function safeUpdate(text) {
@@ -272,11 +351,49 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
272
351
  // emitted text into a single streaming message (cumulative), so every
273
352
  // periodic flush appends to the same response — not separate messages.
274
353
  // No preemptive splitting — sends full text. Error recovery happens in flush().
354
+ // Hybrid MIN_INITIAL_CHARS: hold back until >= MIN_INITIAL_CHARS accumulated
355
+ // before the first content emit, so phrase rotation shows while real content
356
+ // buffers. After first emit, flush normally (no threshold).
275
357
  function flushTextBuffer() {
276
358
  if (!textBuffer)
277
359
  return;
360
+ if (!firstContentEmitted && textBuffer.length < MIN_INITIAL_CHARS)
361
+ return;
362
+ // Proactive >4000 finalization: if cumulative emitted + buffer >= RECOVERY_CHUNK_SIZE,
363
+ // finalize the stream and send overflow via safeSend (follow-up message).
364
+ if (!streamFinalized && totalEmitted + textBuffer.length >= RECOVERY_CHUNK_SIZE) {
365
+ const remaining = RECOVERY_CHUNK_SIZE - totalEmitted;
366
+ /* v8 ignore next 2 -- defensive: remaining always > 0 because finalization runs once @preserve */
367
+ if (remaining > 0)
368
+ safeEmit(textBuffer.slice(0, remaining));
369
+ try {
370
+ stream.close();
371
+ }
372
+ catch { /* stream may already be dead */ }
373
+ streamFinalized = true;
374
+ /* v8 ignore next -- defensive ternary: remaining always > 0 at first finalization @preserve */
375
+ const overflow = textBuffer.slice(remaining > 0 ? remaining : 0);
376
+ textBuffer = "";
377
+ if (overflow)
378
+ safeSend(overflow);
379
+ if (!firstContentEmitted) {
380
+ firstContentEmitted = true;
381
+ stopPhraseRotation();
382
+ }
383
+ return;
384
+ }
385
+ if (streamFinalized) {
386
+ // After finalization, all content goes to safeSend (follow-up messages)
387
+ safeSend(textBuffer);
388
+ textBuffer = "";
389
+ return;
390
+ }
278
391
  safeEmit(textBuffer);
279
392
  textBuffer = "";
393
+ if (!firstContentEmitted) {
394
+ firstContentEmitted = true;
395
+ stopPhraseRotation();
396
+ }
280
397
  }
281
398
  function startPhraseRotation(pool) {
282
399
  stopPhraseRotation();
@@ -293,6 +410,7 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
293
410
  }
294
411
  }
295
412
  return {
413
+ settleOutputMode: "final_only",
296
414
  onModelStart: () => {
297
415
  if (hadRealOutput)
298
416
  return; // real output already shown; don't overwrite with phrases
@@ -319,40 +437,108 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
319
437
  onTextChunk: (text) => {
320
438
  if (stopped)
321
439
  return;
322
- stopPhraseRotation();
440
+ // Don't stop phrase rotation here — let it continue until first content
441
+ // emit (handled in flushTextBuffer when MIN_INITIAL_CHARS threshold met).
323
442
  textBuffer += text;
324
443
  startFlushTimer();
325
444
  },
326
445
  onClearText: () => {
327
446
  textBuffer = "";
328
447
  },
329
- onToolStart: (name, args) => {
330
- stopPhraseRotation();
331
- flushTextBuffer();
332
- // Emit a placeholder to satisfy the 15s Copilot timeout for initial
333
- // stream.emit(). Without this, long tool chains (e.g. ADO batch ops)
334
- // never emit before the timeout and the user sees "this response was
335
- // stopped". The placeholder is replaced by actual content on next emit.
336
- // https://learn.microsoft.com/en-us/answers/questions/2288017/m365-custom-engine-agents-timeout-message-after-15
337
- if (!streamHasContent)
338
- safeEmit("⏳");
339
- const argSummary = (0, tools_1.summarizeArgs)(name, args) || Object.keys(args).join(", ");
340
- safeUpdate((0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
341
- scope: "shared-work",
342
- phase: "processing",
343
- objective: `running ${name} (${argSummary})...`,
344
- })));
345
- hadToolRun = true;
346
- },
347
- onToolEnd: (name, summary, success) => {
448
+ flushNow: async () => {
449
+ const trimmed = textBuffer.trim();
450
+ if (!trimmed)
451
+ return;
452
+ // Cancel pending periodic flush — we're delivering now.
453
+ stopFlushTimer();
454
+ // The actual speak message replaces any "thinking..." phrase cycling.
348
455
  stopPhraseRotation();
349
- const msg = (0, format_1.formatToolResult)(name, summary, success);
350
- safeUpdate((0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
351
- scope: "shared-work",
352
- phase: "processing",
353
- objective: msg,
354
- })));
456
+ // Bypass MIN_INITIAL_CHARS threshold — speak delivers immediately.
457
+ firstContentEmitted = true;
458
+ textBuffer = "";
459
+ // Try the stream first via the NON-ABORTING variant; on failure, fall back
460
+ // to sendMessage. Critical: do NOT call markStopped() / abort the controller
461
+ // when only the primary stream fails — the sendMessage fallback may still
462
+ // deliver the speak, and a successful fallback must not poison the rest of
463
+ // the turn. Only abort when ALL delivery paths fail (handled below).
464
+ // Contract: throws if the message could not be delivered through any available path.
465
+ let delivered = false;
466
+ let lastError = null;
467
+ if (!stopped) {
468
+ const ok = await tryEmitNoAbort(trimmed);
469
+ if (ok)
470
+ delivered = true;
471
+ else
472
+ lastError = new Error("stream emit failed");
473
+ }
474
+ if (!delivered && sendMessage) {
475
+ try {
476
+ await sendMessage(trimmed);
477
+ delivered = true;
478
+ lastError = null;
479
+ }
480
+ catch (err) {
481
+ lastError = err instanceof Error ? err : new Error(String(err));
482
+ }
483
+ }
484
+ (0, runtime_1.emitNervesEvent)({
485
+ component: "senses",
486
+ event: "teams.speak_flush",
487
+ message: "teams flushed mid-turn speak",
488
+ meta: { messageLength: trimmed.length, delivered },
489
+ });
490
+ if (!delivered) {
491
+ // All delivery paths exhausted — now it is correct to abort the turn.
492
+ // markStopped() halts further stream activity and aborts the controller
493
+ // so the engine catches up and ends the turn cleanly.
494
+ markStopped();
495
+ throw new Error(`teams speak delivery failed: ${lastError?.message ?? "no fallback available"}`);
496
+ }
355
497
  },
498
+ ...(() => {
499
+ const toolCbs = (0, tool_activity_callbacks_1.createToolActivityCallbacks)({
500
+ onDescription: (text) => safeUpdate(text),
501
+ /* v8 ignore next -- onResult only called in debug mode; tested via tool-activity-callbacks.test.ts @preserve */
502
+ onResult: (text) => safeUpdate(text),
503
+ /* v8 ignore next -- onFailure tested via onToolEnd failure test @preserve */
504
+ onFailure: (text) => safeUpdate(text),
505
+ isDebug: commands_2.getDebugMode,
506
+ });
507
+ return {
508
+ onToolStart: (name, args) => {
509
+ // speak is flow-control: its visible output is the message itself
510
+ // (delivered via onTextChunk + flushNow). Do NOT stop phrase rotation
511
+ // here, do NOT emit the \u23f3 placeholder, do NOT post a tool-activity
512
+ // status update \u2014 all of those would create UI churn right before the
513
+ // actual speak content arrives.
514
+ if (name === "speak")
515
+ return;
516
+ stopPhraseRotation();
517
+ // Force-flush any accumulated text, bypassing MIN_INITIAL_CHARS threshold
518
+ firstContentEmitted = true;
519
+ flushTextBuffer();
520
+ // Emit a placeholder to satisfy the 15s Copilot timeout for initial
521
+ // stream.emit(). Without this, long tool chains (e.g. ADO batch ops)
522
+ // never emit before the timeout and the user sees "this response was
523
+ // stopped". The placeholder is replaced by actual content on next emit.
524
+ // https://learn.microsoft.com/en-us/answers/questions/2288017/m365-custom-engine-agents-timeout-message-after-15
525
+ if (!streamHasContent)
526
+ safeEmit("\u23f3");
527
+ toolCbs.onToolStart(name, args);
528
+ hadToolRun = true;
529
+ },
530
+ onToolEnd: (name, summary, success) => {
531
+ // speak is flow-control: skip phrase-rotation stop and tool-activity end
532
+ // callback (no safeUpdate for \u2713/\u2717). The flushNow call inside the engine
533
+ // already emitted the actual message and stopped any rotation as part of
534
+ // tryEmit's first-content-emitted flag.
535
+ if (name === "speak")
536
+ return;
537
+ stopPhraseRotation();
538
+ toolCbs.onToolEnd(name, summary, success);
539
+ },
540
+ };
541
+ })(),
356
542
  onKick: () => {
357
543
  stopPhraseRotation();
358
544
  const msg = (0, format_1.formatKick)();
@@ -374,38 +560,27 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
374
560
  safeSend(msg);
375
561
  }
376
562
  },
377
- onConfirmAction: options?.conversationId
378
- ? async (name, args) => {
379
- const convId = options.conversationId;
380
- const argsDesc = Object.entries(args).map(([k, v]) => `${k}: ${v}`).join(", ");
381
- safeUpdate(`Confirm action: ${name} (${argsDesc}) -- reply "yes" to confirm or "no" to cancel`);
382
- return new Promise((resolve) => {
383
- _pendingConfirmations.set(convId, resolve);
384
- // Auto-deny after 2 minutes to prevent indefinite blocking
385
- // (e.g. when the stream dies and the user never sees the prompt).
386
- setTimeout(() => {
387
- if (_pendingConfirmations.has(convId)) {
388
- _pendingConfirmations.delete(convId);
389
- resolve("denied");
390
- }
391
- }, 120_000);
392
- });
393
- }
394
- : undefined,
395
563
  flush: async () => {
396
564
  stopFlushTimer();
565
+ stopPhraseRotation();
397
566
  if (textBuffer) {
567
+ // Bypass MIN_INITIAL_CHARS threshold — flush delivers all remaining content
568
+ firstContentEmitted = true;
398
569
  const text = textBuffer;
399
570
  textBuffer = "";
400
- if (!stopped) {
571
+ if (streamFinalized && sendMessage) {
572
+ // Stream already finalized (>4000 path) — send remaining content as follow-up
573
+ safeSend(text);
574
+ }
575
+ else if (!stopped) {
401
576
  // Stream is alive — await the emit so we can catch async 413/failure
402
577
  // and fall through to sendMessage recovery.
403
578
  const ok = await tryEmit(text);
404
579
  if (!ok)
405
580
  markStopped();
406
581
  }
407
- if (stopped && sendMessage) {
408
- // Stream is dead — fall back to sendMessage; split on failure as recovery.
582
+ if (stopped && !streamFinalized && sendMessage) {
583
+ // Stream is dead (not from finalization) — fall back to sendMessage; split on failure as recovery.
409
584
  try {
410
585
  await sendMessage(text);
411
586
  }
@@ -416,32 +591,12 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
416
591
  }
417
592
  }
418
593
  }
419
- else if (!streamHasContent) {
420
- safeEmit("(completed with tool calls only \u2014 no text response)");
594
+ else if (!streamHasContent && !options?.suppressEmptyStreamMessage) {
595
+ safeEmit("(completed with tool calls only — no text response)");
421
596
  }
422
597
  },
423
598
  };
424
599
  }
425
- // Per-conversation pending confirmation resolvers.
426
- // When a mutate tool needs confirmation, the resolver is stored here.
427
- // The next message from the same conversation resolves it.
428
- const _pendingConfirmations = new Map();
429
- // Confirmation response words (case-insensitive)
430
- const CONFIRM_WORDS = new Set(["yes", "confirm", "go", "y", "ok", "approve", "proceed"]);
431
- function resolvePendingConfirmation(convId, text) {
432
- const resolver = _pendingConfirmations.get(convId);
433
- if (!resolver)
434
- return false;
435
- _pendingConfirmations.delete(convId);
436
- const word = text.trim().toLowerCase();
437
- if (CONFIRM_WORDS.has(word)) {
438
- resolver("confirmed");
439
- }
440
- else {
441
- resolver("denied");
442
- }
443
- return true;
444
- }
445
600
  const _turnCoordinator = (0, turn_coordinator_1.createTurnCoordinator)();
446
601
  function teamsTurnKey(conversationId) {
447
602
  return `teams:${conversationId}`;
@@ -453,13 +608,14 @@ async function withConversationLock(convId, fn) {
453
608
  // are deleted while the process is alive.
454
609
  function getFriendStore() {
455
610
  const friendsPath = path.join((0, identity_1.getAgentRoot)(), "friends");
456
- return new store_file_1.FileFriendStore(friendsPath);
611
+ return new friends_1.FileFriendStore(friendsPath);
457
612
  }
458
613
  function createTeamsCommandRegistry() {
459
614
  const registry = (0, commands_1.createCommandRegistry)();
460
615
  (0, commands_1.registerDefaultCommands)(registry);
461
616
  return registry;
462
617
  }
618
+ /* v8 ignore start -- superseding follow-up slash command handler; tested via startTeamsApp integration tests @preserve */
463
619
  function handleTeamsSlashCommand(text, registry, friendId, conversationId, stream, emitResponse = true) {
464
620
  const parsed = (0, commands_1.parseSlashCommand)(text);
465
621
  if (!parsed)
@@ -483,22 +639,21 @@ function handleTeamsSlashCommand(text, registry, friendId, conversationId, strea
483
639
  }
484
640
  return null;
485
641
  }
642
+ /* v8 ignore stop */
486
643
  // Handle an incoming Teams message
487
- async function handleTeamsMessage(text, stream, conversationId, teamsContext, sendMessage) {
644
+ async function handleTeamsMessage(text, stream, conversationId, teamsContext, sendMessage, reactionOverrides, runtimeOverrides) {
488
645
  const turnKey = teamsTurnKey(conversationId);
646
+ const agentName = (0, identity_1.getAgentName)();
647
+ const owner = { agentName, agentRoot: (0, identity_1.getAgentRoot)(agentName) };
489
648
  // NOTE: Confirmation resolution is handled in the app.on("message") handler
490
649
  // BEFORE the conversation lock. By the time we get here, any pending
491
650
  // confirmation has already been resolved and the reply consumed.
492
- // Send first thinking phrase immediately so the user sees feedback
493
- // before sync I/O (session load, trim) blocks the event loop.
494
- stream.update((0, phrases_1.pickPhrase)((0, phrases_1.getPhrases)().thinking) + "...");
495
- await new Promise(r => setImmediate(r));
496
651
  // Resolve identity provider early for friend resolution + slash command session path
497
652
  const store = getFriendStore();
498
653
  const provider = teamsContext?.aadObjectId ? "aad" : "teams-conversation";
499
654
  const externalId = teamsContext?.aadObjectId || conversationId;
500
655
  // Build FriendResolver for the pipeline
501
- const resolver = new resolver_1.FriendResolver(store, {
656
+ const resolver = new friends_1.FriendResolver(store, {
502
657
  provider,
503
658
  externalId,
504
659
  tenantId: teamsContext?.tenantId,
@@ -508,133 +663,194 @@ async function handleTeamsMessage(text, stream, conversationId, teamsContext, se
508
663
  // Pre-resolve friend for session path + slash commands (pipeline will re-use the cached result)
509
664
  const resolvedContext = await resolver.resolve();
510
665
  const friendId = resolvedContext.friend.id;
511
- const registry = createTeamsCommandRegistry();
512
- // Check for slash commands (before pipeline -- these are transport-level concerns)
513
- if (handleTeamsSlashCommand(text, registry, friendId, conversationId, stream)) {
514
- return;
515
- }
516
666
  // ── Teams adapter concerns: controller, callbacks, session path ──────────
517
667
  const controller = new AbortController();
518
668
  const channelConfig = (0, config_2.getTeamsChannelConfig)();
519
- const callbacks = createTeamsCallbacks(stream, controller, sendMessage, { conversationId, flushIntervalMs: channelConfig.flushIntervalMs });
669
+ const callbacks = createTeamsCallbacks(stream, controller, sendMessage, { conversationId, flushIntervalMs: channelConfig.flushIntervalMs, ...(reactionOverrides?.suppressEmptyStreamMessage ? { suppressEmptyStreamMessage: true } : {}) });
520
670
  const traceId = (0, nerves_1.createTraceId)();
521
671
  const sessPath = (0, config_2.sessionPath)(friendId, "teams", conversationId);
522
- const teamsCapabilities = (0, channel_1.getChannelCapabilities)("teams");
523
- const pendingDir = (0, pending_1.getPendingDir)((0, identity_1.getAgentName)(), friendId, "teams", conversationId);
524
- // Build Teams-specific toolContext fields for injection into the pipeline
525
- const teamsToolContext = teamsContext ? {
526
- graphToken: teamsContext.graphToken,
527
- adoToken: teamsContext.adoToken,
528
- githubToken: teamsContext.githubToken,
529
- signin: teamsContext.signin,
530
- summarize: (0, core_1.createSummarize)(),
531
- tenantId: teamsContext.tenantId,
532
- botApi: teamsContext.botApi,
533
- } : {};
534
- let currentText = text;
535
- const mcpManager = await (0, mcp_manager_1.getSharedMcpManager)() ?? undefined;
536
- while (true) {
537
- let drainedSteeringFollowUps = [];
538
- // Build runAgentOptions with Teams-specific fields
539
- const agentOptions = {
540
- traceId,
541
- toolContext: teamsToolContext,
542
- mcpManager,
543
- drainSteeringFollowUps: () => {
544
- drainedSteeringFollowUps = _turnCoordinator.drainFollowUps(turnKey)
545
- .map(({ text: followUpText, effect }) => ({ text: followUpText, effect }));
546
- return drainedSteeringFollowUps;
547
- },
548
- };
549
- if (channelConfig.skipConfirmation)
550
- agentOptions.skipConfirmation = true;
551
- // ── Call shared pipeline ──────────────────────────────────────────
552
- const result = await (0, pipeline_1.handleInboundTurn)({
553
- channel: "teams",
554
- sessionKey: conversationId,
555
- capabilities: teamsCapabilities,
556
- messages: [{ role: "user", content: currentText }],
557
- continuityIngressTexts: [currentText],
558
- callbacks,
559
- friendResolver: { resolve: () => Promise.resolve(resolvedContext) },
560
- sessionLoader: {
561
- loadOrCreate: async () => {
562
- const existing = (0, context_1.loadSession)(sessPath);
563
- const messages = existing?.messages && existing.messages.length > 0
564
- ? existing.messages
565
- : [{ role: "system", content: await (0, prompt_1.buildSystem)("teams", { mcpManager }, resolvedContext) }];
566
- (0, core_1.repairOrphanedToolCalls)(messages);
567
- return { messages, sessionPath: sessPath, state: existing?.state };
568
- },
569
- },
570
- pendingDir,
571
- friendStore: store,
572
- provider,
573
- externalId,
574
- tenantId: teamsContext?.tenantId,
575
- isGroupChat: false,
576
- groupHasFamilyMember: false,
577
- hasExistingGroupWithFamily: false,
578
- enforceTrustGate: trust_gate_1.enforceTrustGate,
579
- drainPending: pending_1.drainPending,
580
- drainDeferredReturns: (deferredFriendId) => (0, pending_1.drainDeferredReturns)((0, identity_1.getAgentName)(), deferredFriendId),
581
- runAgent: (msgs, cb, channel, sig, opts) => (0, core_1.runAgent)(msgs, cb, channel, sig, {
582
- ...opts,
583
- toolContext: {
584
- /* v8 ignore next -- default no-op signin; pipeline provides the real one @preserve */
585
- signin: async () => undefined,
586
- ...opts?.toolContext,
587
- summarize: teamsToolContext.summarize,
588
- },
589
- }),
590
- postTurn: context_1.postTurn,
591
- accumulateFriendTokens: tokens_1.accumulateFriendTokens,
592
- signal: controller.signal,
593
- runAgentOptions: agentOptions,
594
- });
595
- // ── Handle gate result ────────────────────────────────────────
596
- if (!result.gateResult.allowed) {
597
- if ("autoReply" in result.gateResult && result.gateResult.autoReply) {
598
- stream.emit(result.gateResult.autoReply);
672
+ const teamsCapabilities = (0, friends_1.getChannelCapabilities)("teams");
673
+ const pendingDir = (0, pending_1.getPendingDir)(agentName, friendId, "teams", conversationId);
674
+ const runWithLease = runtimeOverrides?._withSessionTurnLease ?? session_transaction_1.withSessionTurnLease;
675
+ try {
676
+ await runWithLease(sessPath, async (sessionTurnLease) => {
677
+ // Do not emit any activity until this turn owns the durable session.
678
+ if (!reactionOverrides) {
679
+ stream.update((0, phrases_1.pickPhrase)((0, phrases_1.getPhrases)().thinking) + "...");
599
680
  }
681
+ await new Promise(r => setImmediate(r));
682
+ // Build Teams-specific toolContext fields for injection into the pipeline
683
+ const teamsToolContext = { ...owner, ...(teamsContext ? {
684
+ graphToken: teamsContext.graphToken,
685
+ adoToken: teamsContext.adoToken,
686
+ githubToken: teamsContext.githubToken,
687
+ signin: teamsContext.signin,
688
+ summarize: (0, core_1.createSummarize)("human"),
689
+ tenantId: teamsContext.tenantId,
690
+ botApi: teamsContext.botApi,
691
+ } : {}) };
692
+ let currentText = text;
693
+ const mcpManager = await (0, mcp_manager_1.getSharedMcpManager)(owner) ?? undefined;
694
+ while (true) {
695
+ let drainedSteeringFollowUps = [];
696
+ // Build runAgentOptions with Teams-specific fields
697
+ const agentOptions = {
698
+ traceId,
699
+ toolContext: teamsToolContext,
700
+ mcpManager,
701
+ drainSteeringFollowUps: () => {
702
+ drainedSteeringFollowUps = _turnCoordinator.drainFollowUps(turnKey)
703
+ .map(({ text: followUpText, effect }) => ({ text: followUpText, effect }));
704
+ return drainedSteeringFollowUps;
705
+ },
706
+ ...(reactionOverrides?.isReactionSignal ? { isReactionSignal: true } : {}),
707
+ };
708
+ // ── Call shared pipeline ──────────────────────────────────────────
709
+ // Capture terminal errors — failover message replaces the error card if it triggers
710
+ let capturedTerminalError = null;
711
+ const teamsFailoverState = (() => {
712
+ if (!teamsFailoverStates.has(conversationId)) {
713
+ teamsFailoverStates.set(conversationId, { pending: null });
714
+ }
715
+ return teamsFailoverStates.get(conversationId);
716
+ })();
717
+ /* v8 ignore start -- failover-aware callback wrapper: tested via pipeline integration @preserve */
718
+ const failoverAwareCallbacks = {
719
+ ...callbacks,
720
+ onError: (error, severity) => {
721
+ if (severity === "terminal" && teamsFailoverState) {
722
+ capturedTerminalError = error;
723
+ return;
724
+ }
725
+ callbacks.onError(error, severity);
726
+ },
727
+ };
728
+ /* v8 ignore stop */
729
+ const result = await (0, pipeline_1.handleInboundTurn)({
730
+ channel: "teams",
731
+ sessionKey: conversationId,
732
+ capabilities: teamsCapabilities,
733
+ messages: [{ role: "user", content: currentText }],
734
+ continuityIngressTexts: [currentText],
735
+ callbacks: failoverAwareCallbacks,
736
+ sessionTurnLease,
737
+ friendResolver: { resolve: () => Promise.resolve(resolvedContext) },
738
+ sessionLoader: {
739
+ loadOrCreate: async () => {
740
+ const existing = (0, context_1.loadSession)(sessPath);
741
+ const messages = existing?.messages && existing.messages.length > 0
742
+ ? existing.messages
743
+ : [{ role: "system", content: (0, prompt_1.flattenSystemPrompt)(await (0, prompt_1.buildSystem)("teams", {}, resolvedContext)) }];
744
+ (0, core_1.repairOrphanedToolCalls)(messages);
745
+ return {
746
+ messages,
747
+ sessionPath: sessPath,
748
+ state: existing?.state,
749
+ events: existing?.events,
750
+ structuredOutputs: existing?.structuredOutputs,
751
+ };
752
+ },
753
+ },
754
+ pendingDir,
755
+ friendStore: store,
756
+ provider,
757
+ externalId,
758
+ tenantId: teamsContext?.tenantId,
759
+ isGroupChat: false,
760
+ groupHasFamilyMember: false,
761
+ hasExistingGroupWithFamily: false,
762
+ enforceTrustGate: trust_gate_1.enforceTrustGate,
763
+ drainPending: pending_1.drainPending,
764
+ drainDeferredReturns: (deferredFriendId) => (0, pending_1.drainDeferredReturns)(agentName, deferredFriendId),
765
+ runAgent: (msgs, cb, channel, sig, opts) => (0, core_1.runAgent)(msgs, cb, channel, sig, {
766
+ ...opts,
767
+ toolContext: {
768
+ /* v8 ignore next -- default no-op signin; pipeline provides the real one @preserve */
769
+ signin: async () => undefined,
770
+ ...opts?.toolContext,
771
+ ...owner,
772
+ summarize: teamsToolContext.summarize,
773
+ },
774
+ }),
775
+ postTurn: (turnMessages, sessionPathArg, usage, hooks, state) => {
776
+ const prepared = (0, context_1.postTurnTrim)(turnMessages, usage, hooks);
777
+ return (0, context_1.deferPostTurnPersist)(sessionPathArg, prepared, usage, state);
778
+ },
779
+ accumulateFriendTokens: friends_1.accumulateFriendTokens,
780
+ signal: controller.signal,
781
+ runAgentOptions: agentOptions,
782
+ failoverState: teamsFailoverState,
783
+ });
784
+ // ── Handle pipeline-intercepted commands ────────────────────────
785
+ if (result.turnOutcome === "command") {
786
+ if (result.commandAction === "new") {
787
+ (0, context_1.deleteSession)(sessPath);
788
+ stream.emit("session cleared");
789
+ }
790
+ // For "response" commands: pipeline already emitted the response via onTextChunk
791
+ await callbacks.flush();
792
+ return;
793
+ }
794
+ /* v8 ignore start -- failover display: tested via pipeline integration tests @preserve */
795
+ if (result.failoverMessage) {
796
+ stream.emit(result.failoverMessage);
797
+ }
798
+ else if (capturedTerminalError) {
799
+ callbacks.onError(capturedTerminalError, "terminal");
800
+ }
801
+ /* v8 ignore stop */
802
+ // ── Handle gate result ────────────────────────────────────────
803
+ if (!result.gateResult.allowed) {
804
+ if ("autoReply" in result.gateResult && result.gateResult.autoReply) {
805
+ stream.emit(result.gateResult.autoReply);
806
+ }
807
+ return;
808
+ }
809
+ // Flush any remaining accumulated text at end of turn
810
+ await callbacks.flush();
811
+ // After the agent loop, check if any tool returned AUTH_REQUIRED and trigger signin.
812
+ // This must happen after the stream is done so the OAuth card renders properly.
813
+ if (teamsContext && result.messages) {
814
+ const allContent = result.messages.map(m => typeof m.content === "string" ? m.content : "").join("\n");
815
+ if (allContent.includes("AUTH_REQUIRED:graph") && teamsContext.graphConnectionName)
816
+ await teamsContext.signin(teamsContext.graphConnectionName);
817
+ if (allContent.includes("AUTH_REQUIRED:ado") && teamsContext.adoConnectionName)
818
+ await teamsContext.signin(teamsContext.adoConnectionName);
819
+ if (allContent.includes("AUTH_REQUIRED:github") && teamsContext.githubConnectionName)
820
+ await teamsContext.signin(teamsContext.githubConnectionName);
821
+ }
822
+ if (result.turnOutcome !== "superseded") {
823
+ return;
824
+ }
825
+ const supersedingIndex = drainedSteeringFollowUps
826
+ .map((followUp) => followUp.effect)
827
+ .lastIndexOf("clear_and_supersede");
828
+ if (supersedingIndex < 0) {
829
+ return;
830
+ }
831
+ const supersedingFollowUp = drainedSteeringFollowUps[supersedingIndex];
832
+ const replayTail = drainedSteeringFollowUps
833
+ .slice(supersedingIndex + 1)
834
+ .map((followUp) => followUp.text.trim())
835
+ .filter((followUpText) => followUpText.length > 0)
836
+ .join("\n");
837
+ if (replayTail) {
838
+ currentText = replayTail;
839
+ continue;
840
+ }
841
+ if (handleTeamsSlashCommand(supersedingFollowUp.text, createTeamsCommandRegistry(), friendId, conversationId, stream, false)) {
842
+ return;
843
+ }
844
+ currentText = supersedingFollowUp.text;
845
+ }
846
+ });
847
+ }
848
+ catch (error) {
849
+ if (error instanceof session_transaction_1.SessionTurnBusyError || (error instanceof Error && error.name === "SessionTurnBusyError")) {
850
+ stream.emit("this conversation is busy finishing another turn; please try again");
600
851
  return;
601
852
  }
602
- // Flush any remaining accumulated text at end of turn
603
- await callbacks.flush();
604
- // After the agent loop, check if any tool returned AUTH_REQUIRED and trigger signin.
605
- // This must happen after the stream is done so the OAuth card renders properly.
606
- if (teamsContext && result.messages) {
607
- const allContent = result.messages.map(m => typeof m.content === "string" ? m.content : "").join("\n");
608
- if (allContent.includes("AUTH_REQUIRED:graph") && teamsContext.graphConnectionName)
609
- await teamsContext.signin(teamsContext.graphConnectionName);
610
- if (allContent.includes("AUTH_REQUIRED:ado") && teamsContext.adoConnectionName)
611
- await teamsContext.signin(teamsContext.adoConnectionName);
612
- if (allContent.includes("AUTH_REQUIRED:github") && teamsContext.githubConnectionName)
613
- await teamsContext.signin(teamsContext.githubConnectionName);
614
- }
615
- if (result.turnOutcome !== "superseded") {
616
- return;
617
- }
618
- const supersedingIndex = drainedSteeringFollowUps
619
- .map((followUp) => followUp.effect)
620
- .lastIndexOf("clear_and_supersede");
621
- if (supersedingIndex < 0) {
622
- return;
623
- }
624
- const supersedingFollowUp = drainedSteeringFollowUps[supersedingIndex];
625
- const replayTail = drainedSteeringFollowUps
626
- .slice(supersedingIndex + 1)
627
- .map((followUp) => followUp.text.trim())
628
- .filter((followUpText) => followUpText.length > 0)
629
- .join("\n");
630
- if (replayTail) {
631
- currentText = replayTail;
632
- continue;
633
- }
634
- if (handleTeamsSlashCommand(supersedingFollowUp.text, registry, friendId, conversationId, stream, false)) {
635
- return;
636
- }
637
- currentText = supersedingFollowUp.text;
853
+ throw error;
638
854
  }
639
855
  }
640
856
  // Internal port for the secondary bot App (not exposed externally).
@@ -711,7 +927,7 @@ function registerBotHandlers(app, label) {
711
927
  // (graph + ado + github). The verifyState activity only carries a `state`
712
928
  // code with no connectionName, so we try each configured connection until
713
929
  // one succeeds.
714
- app.on("signin.verify-state", async (ctx) => {
930
+ app.on("signin.verify-state", (async (ctx) => {
715
931
  const { api, activity } = ctx;
716
932
  if (!activity.value?.state)
717
933
  return { status: 404 };
@@ -730,7 +946,75 @@ function registerBotHandlers(app, label) {
730
946
  }
731
947
  (0, runtime_1.emitNervesEvent)({ level: "warn", event: "channel.verify_state", component: "channels", message: `[${label}] verify-state failed for all connections`, meta: {} });
732
948
  return { status: 412 };
949
+ }));
950
+ // Handle Teams feedback reactions (thumbs up/down on AI-generated messages).
951
+ // SDK routes message/submitAction with actionName "feedback" to this event.
952
+ /* v8 ignore start -- Teams SDK invoke handler; requires live SDK context @preserve */
953
+ app.on("message.submit.feedback", async (ctx) => {
954
+ const { stream, activity } = ctx;
955
+ const reaction = activity.value?.actionValue?.reaction;
956
+ const comment = activity.value?.actionValue?.feedback;
957
+ const convId = activity.conversation?.id || "unknown";
958
+ const turnKey = teamsTurnKey(convId);
959
+ // Validate payload — graceful no-op for malformed invocations
960
+ if (activity.value?.actionName !== "feedback" || !reaction) {
961
+ return undefined;
962
+ }
963
+ const syntheticText = buildFeedbackSyntheticText(reaction, comment);
964
+ // Turn coordination: if a turn is active, enqueue as steering follow-up
965
+ if (!_turnCoordinator.tryBeginTurn(turnKey)) {
966
+ _turnCoordinator.enqueueFollowUp(turnKey, {
967
+ conversationId: convId,
968
+ text: syntheticText,
969
+ receivedAt: Date.now(),
970
+ effect: (0, continuity_1.classifySteeringFollowUpEffect)(syntheticText),
971
+ });
972
+ return undefined;
973
+ }
974
+ try {
975
+ const teamsContext = {
976
+ signin: async () => undefined,
977
+ aadObjectId: activity.from?.aadObjectId,
978
+ tenantId: activity.conversation?.tenantId,
979
+ displayName: activity.from?.name,
980
+ };
981
+ const ctxSend = async (t) => {
982
+ await ctx.send({ type: "message", text: t, replyToId: activity.replyToId, entities: aiLabelEntities(), channelData: { feedbackLoopEnabled: true } });
983
+ };
984
+ await handleTeamsMessage(syntheticText, stream, convId, teamsContext, ctxSend, { isReactionSignal: true, suppressEmptyStreamMessage: true });
985
+ }
986
+ catch (err) {
987
+ const msg = err instanceof Error ? err.message : String(err);
988
+ (0, runtime_1.emitNervesEvent)({ level: "error", event: "channel.feedback_handler_error", component: "channels", message: msg.slice(0, 200), meta: {} });
989
+ }
990
+ finally {
991
+ _turnCoordinator.endTurn(turnKey);
992
+ }
993
+ return undefined;
994
+ });
995
+ /* v8 ignore stop */
996
+ // Handle bot install — send welcome Adaptive Card with prompt starters.
997
+ /* v8 ignore start -- Teams SDK install handler; requires live SDK context @preserve */
998
+ app.on("install.add", async (ctx) => {
999
+ try {
1000
+ const card = buildWelcomeCard();
1001
+ await ctx.send({
1002
+ type: "message",
1003
+ attachments: [{
1004
+ contentType: "application/vnd.microsoft.card.adaptive",
1005
+ content: card,
1006
+ }],
1007
+ entities: aiLabelEntities(),
1008
+ channelData: { feedbackLoopEnabled: true },
1009
+ });
1010
+ }
1011
+ catch (err) {
1012
+ const msg = err instanceof Error ? err.message : String(err);
1013
+ (0, runtime_1.emitNervesEvent)({ level: "error", event: "channel.welcome_handler_error", component: "channels", message: msg.slice(0, 200), meta: {} });
1014
+ }
1015
+ return undefined;
733
1016
  });
1017
+ /* v8 ignore stop */
734
1018
  app.on("message", async (ctx) => {
735
1019
  const { stream, activity, api, signin } = ctx;
736
1020
  const text = activity.text || "";
@@ -739,13 +1023,6 @@ function registerBotHandlers(app, label) {
739
1023
  const userId = activity.from?.id || "";
740
1024
  const channelId = activity.channelId || "msteams";
741
1025
  (0, runtime_1.emitNervesEvent)({ level: "info", event: "channel.message_received", component: "channels", message: `[${label}] incoming teams message`, meta: { userId: userId.slice(0, 12), conversationId: convId.slice(0, 20) } });
742
- // Resolve pending confirmations IMMEDIATELY — before token fetches or
743
- // the conversation lock. The original message holds the lock while
744
- // awaiting confirmation, so acquiring it here would deadlock. Token
745
- // fetches are also unnecessary (and slow) for a simple yes/no reply.
746
- if (resolvePendingConfirmation(convId, text)) {
747
- return;
748
- }
749
1026
  const commandRegistry = createTeamsCommandRegistry();
750
1027
  const parsedSlashCommand = (0, commands_1.parseSlashCommand)(text);
751
1028
  if (parsedSlashCommand) {
@@ -759,7 +1036,7 @@ function registerBotHandlers(app, label) {
759
1036
  const commandStore = getFriendStore();
760
1037
  const commandProvider = activity.from?.aadObjectId ? "aad" : "teams-conversation";
761
1038
  const commandExternalId = activity.from?.aadObjectId || convId;
762
- const commandResolver = new resolver_1.FriendResolver(commandStore, {
1039
+ const commandResolver = new friends_1.FriendResolver(commandStore, {
763
1040
  provider: commandProvider,
764
1041
  externalId: commandExternalId,
765
1042
  tenantId: activity.conversation?.tenantId,
@@ -846,7 +1123,7 @@ function registerBotHandlers(app, label) {
846
1123
  const ctxSend = async (t) => {
847
1124
  // Use send with replyToId (not reply, which adds a blockquote).
848
1125
  // replyToId anchors the message after the user's message in Copilot Chat.
849
- await ctx.send({ type: "message", text: t, replyToId: activity.id });
1126
+ await ctx.send({ type: "message", text: t, replyToId: activity.id, entities: aiLabelEntities(), channelData: { feedbackLoopEnabled: true } });
850
1127
  };
851
1128
  await handleTeamsMessage(text, stream, convId, teamsContext, ctxSend);
852
1129
  }
@@ -874,14 +1151,14 @@ function findAadObjectId(friend) {
874
1151
  function resolveTeamsFriendStore(deps) {
875
1152
  return deps.store
876
1153
  ?? deps.createFriendStore?.()
877
- ?? new store_file_1.FileFriendStore(path.join((0, identity_1.getAgentRoot)(), "friends"));
1154
+ ?? new friends_1.FileFriendStore(path.join((0, identity_1.getAgentRoot)(), "friends"));
878
1155
  }
879
1156
  function getTeamsConversations(botApi) {
880
1157
  return botApi.conversations;
881
1158
  }
882
1159
  function hasExplicitCrossChatAuthorization(params) {
883
1160
  return params.intent === "explicit_cross_chat"
884
- && types_1.TRUSTED_LEVELS.has(params.authorizingSession?.trustLevel ?? "stranger");
1161
+ && friends_1.TRUSTED_LEVELS.has(params.authorizingSession?.trustLevel ?? "stranger");
885
1162
  }
886
1163
  async function sendProactiveTeamsMessageToSession(params, deps) {
887
1164
  const store = resolveTeamsFriendStore(deps);
@@ -903,7 +1180,7 @@ async function sendProactiveTeamsMessageToSession(params, deps) {
903
1180
  });
904
1181
  return { delivered: false, reason: "friend_not_found" };
905
1182
  }
906
- if (!hasExplicitCrossChatAuthorization(params) && !types_1.TRUSTED_LEVELS.has(friend.trustLevel ?? "stranger")) {
1183
+ if (!hasExplicitCrossChatAuthorization(params) && !friends_1.TRUSTED_LEVELS.has(friend.trustLevel ?? "stranger")) {
907
1184
  (0, runtime_1.emitNervesEvent)({
908
1185
  component: "senses",
909
1186
  event: "senses.teams_proactive_trust_skip",
@@ -928,6 +1205,16 @@ async function sendProactiveTeamsMessageToSession(params, deps) {
928
1205
  });
929
1206
  return { delivered: false, reason: "missing_target" };
930
1207
  }
1208
+ const internalContentBlockReason = (0, proactive_content_guard_1.getProactiveInternalContentBlockReason)(params.text);
1209
+ if (internalContentBlockReason) {
1210
+ (0, proactive_content_guard_1.emitProactiveInternalContentBlocked)({
1211
+ friendId: params.friendId,
1212
+ sessionKey: params.sessionKey,
1213
+ reason: internalContentBlockReason,
1214
+ source: "session_send",
1215
+ });
1216
+ return { delivered: false, reason: "internal_content_blocked" };
1217
+ }
931
1218
  try {
932
1219
  const conversation = await conversations.create({
933
1220
  bot: { id: deps.botApi.id },
@@ -1030,6 +1317,20 @@ async function drainAndSendPendingTeams(store, botApi, pendingRoot) {
1030
1317
  catch { /* ignore */ }
1031
1318
  continue;
1032
1319
  }
1320
+ const internalBlockReason = (0, proactive_content_guard_1.getProactiveInternalContentBlockReason)(messageText);
1321
+ if (internalBlockReason) {
1322
+ result.skipped++;
1323
+ try {
1324
+ fs.unlinkSync(filePath);
1325
+ }
1326
+ catch { /* ignore */ }
1327
+ (0, proactive_content_guard_1.emitProactiveInternalContentBlocked)({
1328
+ friendId,
1329
+ reason: internalBlockReason,
1330
+ source: "pending_drain",
1331
+ });
1332
+ continue;
1333
+ }
1033
1334
  const sendResult = await sendProactiveTeamsMessageToSession({
1034
1335
  friendId,
1035
1336
  sessionKey: key,
@@ -1091,7 +1392,7 @@ function startTeamsApp() {
1091
1392
  const port = process.env.PORT ? Number(process.env.PORT) : (0, config_2.getTeamsChannelConfig)().port;
1092
1393
  app.start(port);
1093
1394
  // Diagnostic: log tool count at startup to verify deploy
1094
- const startupTools = (0, tools_1.getToolsForChannel)((0, channel_1.getChannelCapabilities)("teams"));
1395
+ const startupTools = (0, tools_1.getToolsForChannel)((0, friends_1.getChannelCapabilities)("teams"));
1095
1396
  const toolNames = startupTools.map((t) => t.function.name);
1096
1397
  (0, runtime_1.emitNervesEvent)({ level: "info", event: "channel.app_started", component: "channels", message: `Teams bot started on port ${port} with ${mode} (chunked streaming)`, meta: { port, mode, toolCount: toolNames.length, hasProactive: toolNames.includes("teams_send_message") } });
1097
1398
  // --- Secondary bot (dual-bot support) ---