@ouro.bot/cli 0.1.0-alpha.72 → 0.1.0-alpha.721

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