@ouro.bot/cli 0.1.0-alpha.67 → 0.1.0-alpha.671

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 (457) hide show
  1. package/README.md +127 -23
  2. package/RepairGuide.ouro/agent.json +5 -0
  3. package/RepairGuide.ouro/psyche/IDENTITY.md +19 -0
  4. package/RepairGuide.ouro/psyche/SOUL.md +55 -0
  5. package/RepairGuide.ouro/skills/diagnose-broken-remote.md +63 -0
  6. package/RepairGuide.ouro/skills/diagnose-stacked-typed-issues.md +35 -0
  7. package/RepairGuide.ouro/skills/diagnose-sync-blocked.md +54 -0
  8. package/RepairGuide.ouro/skills/diagnose-vault-expired.md +60 -0
  9. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +4 -2
  10. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +2 -2
  11. package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
  12. package/changelog.json +4277 -16
  13. package/dist/a2a/card.js +56 -0
  14. package/dist/a2a/client.js +143 -0
  15. package/dist/a2a/config.js +50 -0
  16. package/dist/a2a/onboarding.js +83 -0
  17. package/dist/a2a/server.js +498 -0
  18. package/dist/a2a/task-store.js +69 -0
  19. package/dist/a2a/types.js +3 -0
  20. package/dist/arc/attention-types.js +8 -0
  21. package/dist/arc/cares.js +144 -0
  22. package/dist/arc/episodes.js +118 -0
  23. package/dist/arc/evolution.js +487 -0
  24. package/dist/arc/flight-recorder.js +810 -0
  25. package/dist/arc/intentions.js +134 -0
  26. package/dist/arc/json-store.js +117 -0
  27. package/dist/arc/obligations.js +326 -0
  28. package/dist/arc/packets.js +288 -0
  29. package/dist/arc/presence.js +185 -0
  30. package/dist/arc/task-lifecycle.js +57 -0
  31. package/dist/commerce/store.js +755 -0
  32. package/dist/commerce/types.js +3 -0
  33. package/dist/heart/active-work.js +860 -43
  34. package/dist/heart/agent-entry.js +69 -3
  35. package/dist/heart/attachments/image-normalize.js +194 -0
  36. package/dist/heart/attachments/materialize.js +97 -0
  37. package/dist/heart/attachments/originals.js +88 -0
  38. package/dist/heart/attachments/render.js +29 -0
  39. package/dist/heart/attachments/sources/bluebubbles.js +156 -0
  40. package/dist/heart/attachments/sources/cli-local-file.js +78 -0
  41. package/dist/heart/attachments/sources/index.js +16 -0
  42. package/dist/heart/attachments/store.js +103 -0
  43. package/dist/heart/attachments/types.js +93 -0
  44. package/dist/heart/auth/auth-flow.js +479 -0
  45. package/dist/heart/awaiting/await-alert.js +146 -0
  46. package/dist/heart/awaiting/await-expiry.js +108 -0
  47. package/dist/heart/awaiting/await-loader.js +91 -0
  48. package/dist/heart/awaiting/await-parser.js +141 -0
  49. package/dist/heart/awaiting/await-runtime-state.js +100 -0
  50. package/dist/heart/awaiting/await-scheduler.js +378 -0
  51. package/dist/heart/background-operations.js +281 -0
  52. package/dist/heart/bridges/manager.js +137 -17
  53. package/dist/heart/bridges/store.js +14 -2
  54. package/dist/heart/bundle-state.js +168 -0
  55. package/dist/heart/commitments.js +135 -0
  56. package/dist/heart/config-registry.js +331 -0
  57. package/dist/heart/config.js +118 -119
  58. package/dist/heart/context-loss-gauntlet.js +354 -0
  59. package/dist/heart/context-loss-sentinel.js +907 -0
  60. package/dist/heart/core.js +1288 -249
  61. package/dist/heart/cross-chat-delivery.js +8 -22
  62. package/dist/heart/daemon/agent-config-check.js +427 -0
  63. package/dist/heart/daemon/agent-discovery.js +102 -3
  64. package/dist/heart/daemon/agent-service.js +523 -0
  65. package/dist/heart/daemon/agentic-repair.js +547 -0
  66. package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
  67. package/dist/heart/daemon/boot-sync-probe.js +197 -0
  68. package/dist/heart/daemon/cadence.js +70 -0
  69. package/dist/heart/daemon/cli-defaults.js +780 -0
  70. package/dist/heart/daemon/cli-desk.js +322 -0
  71. package/dist/heart/daemon/cli-exec.js +7940 -0
  72. package/dist/heart/daemon/cli-help.js +576 -0
  73. package/dist/heart/daemon/cli-parse.js +1850 -0
  74. package/dist/heart/daemon/cli-render-doctor.js +57 -0
  75. package/dist/heart/daemon/cli-render.js +763 -0
  76. package/dist/heart/daemon/cli-types.js +8 -0
  77. package/dist/heart/daemon/connect-bay.js +323 -0
  78. package/dist/heart/daemon/daemon-cli.js +29 -1750
  79. package/dist/heart/daemon/daemon-entry.js +533 -3
  80. package/dist/heart/daemon/daemon-health.js +176 -0
  81. package/dist/heart/daemon/daemon-rollup.js +57 -0
  82. package/dist/heart/daemon/daemon-runtime-sync.js +88 -13
  83. package/dist/heart/daemon/daemon-tombstone.js +236 -0
  84. package/dist/heart/daemon/daemon.js +950 -76
  85. package/dist/heart/daemon/dns-workflow.js +394 -0
  86. package/dist/heart/daemon/doctor-types.js +8 -0
  87. package/dist/heart/daemon/doctor.js +873 -0
  88. package/dist/heart/daemon/health-monitor.js +125 -1
  89. package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
  90. package/dist/heart/daemon/hooks/bundle-meta.js +135 -1
  91. package/dist/heart/daemon/http-health-probe.js +80 -0
  92. package/dist/heart/daemon/human-command-screens.js +234 -0
  93. package/dist/heart/daemon/human-readiness.js +114 -0
  94. package/dist/heart/daemon/inner-status.js +78 -0
  95. package/dist/heart/daemon/interactive-repair.js +394 -0
  96. package/dist/heart/daemon/launchd.js +37 -8
  97. package/dist/heart/daemon/log-tailer.js +79 -10
  98. package/dist/heart/daemon/logs-prune.js +110 -0
  99. package/dist/heart/daemon/mcp-canary.js +297 -0
  100. package/dist/heart/daemon/message-router.js +6 -2
  101. package/dist/heart/daemon/migrate-to-desk.js +848 -0
  102. package/dist/heart/daemon/os-cron-deps.js +135 -0
  103. package/dist/heart/daemon/os-cron.js +14 -12
  104. package/dist/heart/daemon/ouro-bot-entry.js +4 -2
  105. package/dist/heart/daemon/ouro-entry.js +3 -1
  106. package/dist/heart/daemon/plugin-cli.js +432 -0
  107. package/dist/heart/daemon/process-manager.js +511 -40
  108. package/dist/heart/daemon/provider-discovery.js +137 -0
  109. package/dist/heart/daemon/provider-ping-progress.js +83 -0
  110. package/dist/heart/daemon/pulse.js +475 -0
  111. package/dist/heart/daemon/readiness-repair.js +365 -0
  112. package/dist/heart/daemon/run-hooks.js +2 -0
  113. package/dist/heart/daemon/runtime-logging.js +41 -14
  114. package/dist/heart/daemon/runtime-metadata.js +2 -30
  115. package/dist/heart/daemon/safe-mode.js +161 -0
  116. package/dist/heart/daemon/sense-manager.js +564 -38
  117. package/dist/heart/daemon/session-id-resolver.js +131 -0
  118. package/dist/heart/daemon/skill-management-installer.js +1 -1
  119. package/dist/heart/daemon/socket-client.js +158 -11
  120. package/dist/heart/daemon/stale-bundle-prune.js +96 -0
  121. package/dist/heart/daemon/startup-tui.js +330 -0
  122. package/dist/heart/daemon/task-scheduler.js +117 -39
  123. package/dist/heart/daemon/terminal-ui.js +499 -0
  124. package/dist/heart/daemon/thoughts.js +229 -17
  125. package/dist/heart/daemon/up-progress.js +366 -0
  126. package/dist/heart/daemon/vault-items.js +56 -0
  127. package/dist/heart/delegation.js +1 -4
  128. package/dist/heart/habits/habit-migration.js +189 -0
  129. package/dist/heart/habits/habit-parser.js +211 -0
  130. package/dist/heart/habits/habit-runtime-state.js +114 -0
  131. package/dist/heart/habits/habit-scheduler.js +391 -0
  132. package/dist/heart/habits/habit-session-summary.js +318 -0
  133. package/dist/heart/habits/habit-session.js +618 -0
  134. package/dist/heart/{daemon → hatch}/hatch-flow.js +42 -56
  135. package/dist/heart/{daemon → hatch}/hatch-specialist.js +6 -8
  136. package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
  137. package/dist/heart/{daemon → hatch}/specialist-tools.js +45 -18
  138. package/dist/heart/identity.js +174 -57
  139. package/dist/heart/kept-notes.js +289 -0
  140. package/dist/heart/kicks.js +1 -1
  141. package/dist/heart/machine-identity.js +161 -0
  142. package/dist/heart/mail-import-discovery.js +353 -0
  143. package/dist/heart/mailbox/mailbox-http-hooks.js +96 -0
  144. package/dist/heart/mailbox/mailbox-http-response.js +7 -0
  145. package/dist/heart/mailbox/mailbox-http-routes.js +375 -0
  146. package/dist/heart/mailbox/mailbox-http-static.js +103 -0
  147. package/dist/heart/mailbox/mailbox-http-transport.js +116 -0
  148. package/dist/heart/mailbox/mailbox-http.js +99 -0
  149. package/dist/heart/mailbox/mailbox-read.js +37 -0
  150. package/dist/heart/mailbox/mailbox-types.js +27 -0
  151. package/dist/heart/mailbox/mailbox-view.js +197 -0
  152. package/dist/heart/mailbox/readers/agent-machine.js +428 -0
  153. package/dist/heart/mailbox/readers/continuity-readers.js +329 -0
  154. package/dist/heart/mailbox/readers/mail.js +375 -0
  155. package/dist/heart/mailbox/readers/runtime-readers.js +815 -0
  156. package/dist/heart/mailbox/readers/sessions.js +232 -0
  157. package/dist/heart/mailbox/readers/shared.js +111 -0
  158. package/dist/heart/mcp/mcp-server.js +696 -0
  159. package/dist/heart/migrate-config.js +100 -0
  160. package/dist/heart/model-capabilities.js +19 -0
  161. package/dist/heart/orientation-frame.js +217 -0
  162. package/dist/heart/platform.js +81 -0
  163. package/dist/heart/provider-attempt.js +134 -0
  164. package/dist/heart/provider-binding-resolver.js +273 -0
  165. package/dist/heart/provider-credentials.js +425 -0
  166. package/dist/heart/provider-failover.js +311 -0
  167. package/dist/heart/provider-models.js +81 -0
  168. package/dist/heart/provider-ping.js +262 -0
  169. package/dist/heart/provider-readiness-cache.js +119 -0
  170. package/dist/heart/provider-visibility.js +188 -0
  171. package/dist/heart/providers/anthropic-token.js +131 -0
  172. package/dist/heart/providers/anthropic.js +139 -52
  173. package/dist/heart/providers/azure.js +23 -11
  174. package/dist/heart/providers/error-classification.js +127 -0
  175. package/dist/heart/providers/github-copilot.js +145 -0
  176. package/dist/heart/providers/minimax-vlm.js +189 -0
  177. package/dist/heart/providers/minimax.js +26 -8
  178. package/dist/heart/providers/openai-codex-token.js +349 -0
  179. package/dist/heart/providers/openai-codex.js +55 -40
  180. package/dist/heart/runtime-capability-check.js +170 -0
  181. package/dist/heart/runtime-credentials.js +367 -0
  182. package/dist/heart/runtime-cwd.js +87 -0
  183. package/dist/heart/sense-truth.js +17 -4
  184. package/dist/heart/session-activity.js +48 -24
  185. package/dist/heart/session-events.js +1133 -0
  186. package/dist/heart/session-playback-cli-main.js +5 -0
  187. package/dist/heart/session-playback-cli.js +36 -0
  188. package/dist/heart/session-playback.js +231 -0
  189. package/dist/heart/session-stats-cli-main.js +5 -0
  190. package/dist/heart/session-stats.js +182 -0
  191. package/dist/heart/session-transcript.js +133 -0
  192. package/dist/heart/start-of-turn-packet.js +357 -0
  193. package/dist/heart/streaming.js +44 -27
  194. package/dist/heart/structured-output.js +196 -0
  195. package/dist/heart/sync-classification.js +176 -0
  196. package/dist/heart/sync.js +449 -0
  197. package/dist/heart/target-resolution.js +11 -7
  198. package/dist/heart/tempo.js +93 -0
  199. package/dist/heart/temporal-view.js +41 -0
  200. package/dist/heart/timeouts.js +101 -0
  201. package/dist/heart/tool-activity-callbacks.js +59 -0
  202. package/dist/heart/tool-description.js +155 -0
  203. package/dist/heart/tool-friction.js +55 -0
  204. package/dist/heart/tool-loop.js +200 -0
  205. package/dist/heart/turn-context.js +450 -0
  206. package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +6 -5
  207. package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
  208. package/dist/heart/versioning/ouro-path-installer.js +426 -0
  209. package/dist/heart/versioning/ouro-version-manager.js +409 -0
  210. package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
  211. package/dist/heart/{daemon → versioning}/update-checker.js +6 -1
  212. package/dist/heart/versioning/update-hooks.js +154 -0
  213. package/dist/heart/work-card.js +390 -0
  214. package/dist/mailbox-ui/assets/index-CaTIFDmv.js +1 -0
  215. package/dist/mailbox-ui/assets/index-Du_9G9WO.css +1 -0
  216. package/dist/mailbox-ui/assets/vendor-CcN1XpQ9.js +61 -0
  217. package/dist/mailbox-ui/index.html +16 -0
  218. package/dist/mailroom/attention.js +167 -0
  219. package/dist/mailroom/autonomy.js +209 -0
  220. package/dist/mailroom/blob-store.js +715 -0
  221. package/dist/mailroom/body-cache.js +61 -0
  222. package/dist/mailroom/core.js +788 -0
  223. package/dist/mailroom/entry.js +160 -0
  224. package/dist/mailroom/file-store.js +568 -0
  225. package/dist/mailroom/mbox-import.js +393 -0
  226. package/dist/mailroom/migration.js +164 -0
  227. package/dist/mailroom/outbound.js +380 -0
  228. package/dist/mailroom/policy.js +263 -0
  229. package/dist/mailroom/reader.js +233 -0
  230. package/dist/mailroom/search-cache.js +334 -0
  231. package/dist/mailroom/search-relevance.js +319 -0
  232. package/dist/mailroom/smtp-ingress.js +176 -0
  233. package/dist/mailroom/source-state.js +176 -0
  234. package/dist/mailroom/thread.js +109 -0
  235. package/dist/mailroom/travel-extract.js +89 -0
  236. package/dist/mind/bundle-manifest.js +24 -2
  237. package/dist/mind/context.js +250 -101
  238. package/dist/mind/desk-section.js +362 -0
  239. package/dist/mind/diary-integrity.js +60 -0
  240. package/dist/mind/{memory.js → diary.js} +68 -77
  241. package/dist/mind/embedding-provider.js +60 -0
  242. package/dist/mind/file-state.js +179 -0
  243. package/dist/mind/{associative-recall.js → note-search.js} +47 -58
  244. package/dist/mind/obligation-steering.js +221 -0
  245. package/dist/mind/pending.js +6 -1
  246. package/dist/mind/prompt-refresh.js +3 -2
  247. package/dist/mind/prompt.js +1020 -148
  248. package/dist/mind/provenance-trust.js +26 -0
  249. package/dist/mind/record-paths.js +312 -0
  250. package/dist/mind/scrutiny.js +173 -0
  251. package/dist/nerves/cli-logging.js +7 -1
  252. package/dist/nerves/coverage/audit-rules.js +15 -6
  253. package/dist/nerves/coverage/audit.js +28 -2
  254. package/dist/nerves/coverage/cli.js +1 -1
  255. package/dist/nerves/coverage/contract.js +5 -5
  256. package/dist/nerves/coverage/file-completeness.js +139 -5
  257. package/dist/nerves/event-buffer.js +111 -0
  258. package/dist/nerves/index.js +224 -4
  259. package/dist/nerves/observation.js +20 -0
  260. package/dist/nerves/redact.js +79 -0
  261. package/dist/nerves/review/cli-main.js +5 -0
  262. package/dist/nerves/review/cli.js +156 -0
  263. package/dist/nerves/review/core.js +152 -0
  264. package/dist/nerves/runtime.js +5 -1
  265. package/dist/repertoire/ado-client.js +15 -56
  266. package/dist/repertoire/ado-semantic.js +16 -10
  267. package/dist/repertoire/api-client.js +97 -0
  268. package/dist/repertoire/bitwarden-store.js +1042 -0
  269. package/dist/repertoire/bundle-templates.js +71 -0
  270. package/dist/repertoire/bw-installer.js +180 -0
  271. package/dist/repertoire/coding/codex-jsonl.js +64 -0
  272. package/dist/repertoire/coding/context-pack.js +331 -0
  273. package/dist/repertoire/coding/feedback.js +197 -30
  274. package/dist/repertoire/coding/manager.js +166 -10
  275. package/dist/repertoire/coding/spawner.js +55 -9
  276. package/dist/repertoire/coding/tools.js +219 -7
  277. package/dist/repertoire/commerce-errors.js +109 -0
  278. package/dist/repertoire/commerce-self-test.js +156 -0
  279. package/dist/repertoire/credential-access.js +178 -0
  280. package/dist/repertoire/desk/classifier.js +362 -0
  281. package/dist/repertoire/duffel-client.js +185 -0
  282. package/dist/repertoire/github-client.js +14 -55
  283. package/dist/repertoire/graph-client.js +11 -52
  284. package/dist/repertoire/guardrails.js +161 -27
  285. package/dist/repertoire/mcp-client.js +295 -0
  286. package/dist/repertoire/mcp-manager.js +434 -0
  287. package/dist/repertoire/mcp-tools.js +83 -0
  288. package/dist/repertoire/plugin-mcp.js +175 -0
  289. package/dist/repertoire/plugins.js +253 -0
  290. package/dist/repertoire/shell-sessions.js +133 -0
  291. package/dist/repertoire/skills.js +48 -4
  292. package/dist/repertoire/stripe-client.js +131 -0
  293. package/dist/repertoire/tool-results.js +29 -0
  294. package/dist/repertoire/tools-a2a.js +282 -0
  295. package/dist/repertoire/tools-attachments.js +317 -0
  296. package/dist/repertoire/tools-awaiting.js +372 -0
  297. package/dist/repertoire/tools-base.js +63 -1082
  298. package/dist/repertoire/tools-bluebubbles.js +2 -0
  299. package/dist/repertoire/tools-bridge.js +144 -0
  300. package/dist/repertoire/tools-bundle.js +993 -0
  301. package/dist/repertoire/tools-commerce.js +253 -0
  302. package/dist/repertoire/tools-config.js +186 -0
  303. package/dist/repertoire/tools-continuity.js +252 -0
  304. package/dist/repertoire/tools-credential.js +383 -0
  305. package/dist/repertoire/tools-evolution.js +527 -0
  306. package/dist/repertoire/tools-files.js +344 -0
  307. package/dist/repertoire/tools-flight.js +290 -0
  308. package/dist/repertoire/tools-flow.js +119 -0
  309. package/dist/repertoire/tools-github.js +3 -8
  310. package/dist/repertoire/tools-mail.js +1975 -0
  311. package/dist/repertoire/tools-notes.js +459 -0
  312. package/dist/repertoire/tools-obligations.js +143 -0
  313. package/dist/repertoire/tools-orientation.js +31 -0
  314. package/dist/repertoire/tools-record.js +482 -0
  315. package/dist/repertoire/tools-runtime.js +150 -0
  316. package/dist/repertoire/tools-session.js +906 -0
  317. package/dist/repertoire/tools-shell.js +120 -0
  318. package/dist/repertoire/tools-stripe.js +224 -0
  319. package/dist/repertoire/tools-surface.js +355 -0
  320. package/dist/repertoire/tools-teams.js +12 -39
  321. package/dist/repertoire/tools-travel.js +125 -0
  322. package/dist/repertoire/tools-trip.js +982 -0
  323. package/dist/repertoire/tools-user-profile.js +146 -0
  324. package/dist/repertoire/tools-vault.js +40 -0
  325. package/dist/repertoire/tools-voice.js +145 -0
  326. package/dist/repertoire/tools.js +250 -79
  327. package/dist/repertoire/travel-api-client.js +360 -0
  328. package/dist/repertoire/user-profile.js +131 -0
  329. package/dist/repertoire/vault-setup.js +246 -0
  330. package/dist/repertoire/vault-unlock.js +594 -0
  331. package/dist/scripts/claude-code-hook.js +41 -0
  332. package/dist/scripts/claude-code-stop-hook.js +47 -0
  333. package/dist/senses/a2a-entry.js +78 -0
  334. package/dist/senses/attention-queue.js +186 -0
  335. package/dist/senses/await-turn-message.js +58 -0
  336. package/dist/senses/bluebubbles/active-turns.js +216 -0
  337. package/dist/senses/bluebubbles/attachment-cache.js +53 -0
  338. package/dist/senses/bluebubbles/attachment-download.js +137 -0
  339. package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +219 -18
  340. package/dist/senses/bluebubbles/entry.js +77 -0
  341. package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +20 -3
  342. package/dist/senses/bluebubbles/index.js +2732 -0
  343. package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -71
  344. package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +33 -12
  345. package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +3 -3
  346. package/dist/senses/bluebubbles/processed-log.js +133 -0
  347. package/dist/senses/bluebubbles/replay.js +137 -0
  348. package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +30 -2
  349. package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
  350. package/dist/senses/bluebubbles-meta-guard.js +40 -0
  351. package/dist/senses/cli/bracketed-paste.js +82 -0
  352. package/dist/senses/cli/image-paste.js +287 -0
  353. package/dist/senses/cli/image-ref-navigation.js +75 -0
  354. package/dist/senses/cli/ink-app.js +156 -0
  355. package/dist/senses/cli/inline-diff.js +64 -0
  356. package/dist/senses/cli/input-keys.js +174 -0
  357. package/dist/senses/cli/kill-ring.js +86 -0
  358. package/dist/senses/cli/message-list.js +51 -0
  359. package/dist/senses/cli/ouro-tui.js +607 -0
  360. package/dist/senses/cli/spinner-imperative.js +135 -0
  361. package/dist/senses/cli/spinner.js +101 -0
  362. package/dist/senses/cli/status-line.js +60 -0
  363. package/dist/senses/cli/streaming-markdown.js +526 -0
  364. package/dist/senses/cli/tool-display.js +85 -0
  365. package/dist/senses/cli/tool-render.js +85 -0
  366. package/dist/senses/cli/tui-store.js +240 -0
  367. package/dist/senses/cli/virtual-list.js +35 -0
  368. package/dist/senses/cli-entry.js +60 -8
  369. package/dist/senses/cli-layout.js +100 -0
  370. package/dist/senses/cli.js +522 -212
  371. package/dist/senses/commands.js +66 -3
  372. package/dist/senses/habit-turn-message.js +160 -0
  373. package/dist/senses/inner-dialog-worker.js +499 -22
  374. package/dist/senses/inner-dialog.js +544 -47
  375. package/dist/senses/mail-entry.js +66 -0
  376. package/dist/senses/mail.js +379 -0
  377. package/dist/senses/pipeline.js +870 -179
  378. package/dist/senses/proactive-content-guard.js +51 -0
  379. package/dist/senses/shared-turn.js +416 -0
  380. package/dist/senses/surface-tool.js +109 -0
  381. package/dist/senses/teams-entry.js +60 -8
  382. package/dist/senses/teams.js +400 -112
  383. package/dist/senses/trust-gate.js +102 -7
  384. package/dist/senses/voice/audio-playback.js +237 -0
  385. package/dist/senses/voice/audio-routing.js +119 -0
  386. package/dist/senses/voice/elevenlabs.js +202 -0
  387. package/dist/senses/voice/floor-control.js +431 -0
  388. package/dist/senses/voice/floor-controller.js +115 -0
  389. package/dist/senses/voice/golden-path.js +116 -0
  390. package/dist/senses/voice/index.js +29 -0
  391. package/dist/senses/voice/meeting.js +113 -0
  392. package/dist/senses/voice/outbound.js +190 -0
  393. package/dist/senses/voice/phone.js +33 -0
  394. package/dist/senses/voice/playback.js +139 -0
  395. package/dist/senses/voice/realtime-eval.js +496 -0
  396. package/dist/senses/voice/realtime-trace.js +531 -0
  397. package/dist/senses/voice/transcript.js +70 -0
  398. package/dist/senses/voice/turn.js +191 -0
  399. package/dist/senses/voice/twilio-phone-runtime.js +807 -0
  400. package/dist/senses/voice/twilio-phone.js +5077 -0
  401. package/dist/senses/voice/types.js +2 -0
  402. package/dist/senses/voice/whisper.js +161 -0
  403. package/dist/senses/voice-entry.js +81 -0
  404. package/dist/senses/voice-realtime-eval-command.js +99 -0
  405. package/dist/senses/voice-realtime-eval-entry.js +21 -0
  406. package/dist/senses/voice-twilio-entry.js +87 -0
  407. package/dist/trips/core.js +138 -0
  408. package/dist/trips/store.js +265 -0
  409. package/dist/util/frontmatter.js +69 -0
  410. package/package.json +57 -12
  411. package/skills/agent-commerce.md +113 -0
  412. package/skills/browser-navigation.md +117 -0
  413. package/skills/commerce-setup-guide.md +116 -0
  414. package/skills/commerce-setup.md +84 -0
  415. package/skills/configure-dev-tools.md +99 -0
  416. package/skills/travel-planning.md +138 -0
  417. package/dist/heart/daemon/auth-flow.js +0 -351
  418. package/dist/heart/daemon/ouro-path-installer.js +0 -178
  419. package/dist/heart/daemon/update-hooks.js +0 -138
  420. package/dist/heart/safe-workspace.js +0 -228
  421. package/dist/heart/session-recall.js +0 -116
  422. package/dist/mind/friends/channel.js +0 -84
  423. package/dist/mind/friends/group-context.js +0 -144
  424. package/dist/mind/friends/resolver.js +0 -84
  425. package/dist/mind/friends/store-file.js +0 -190
  426. package/dist/mind/friends/store.js +0 -4
  427. package/dist/mind/friends/tokens.js +0 -26
  428. package/dist/mind/friends/trust-explanation.js +0 -74
  429. package/dist/mind/friends/types.js +0 -29
  430. package/dist/repertoire/tasks/board.js +0 -134
  431. package/dist/repertoire/tasks/index.js +0 -224
  432. package/dist/repertoire/tasks/lifecycle.js +0 -80
  433. package/dist/repertoire/tasks/middleware.js +0 -65
  434. package/dist/repertoire/tasks/parser.js +0 -173
  435. package/dist/repertoire/tasks/scanner.js +0 -132
  436. package/dist/repertoire/tasks/transitions.js +0 -144
  437. package/dist/senses/bluebubbles-entry.js +0 -13
  438. package/dist/senses/bluebubbles.js +0 -1177
  439. package/dist/senses/debug-activity.js +0 -148
  440. package/subagents/README.md +0 -7
  441. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
  442. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
  443. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
  444. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
  445. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
  446. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
  447. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
  448. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
  449. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
  450. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
  451. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
  452. /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
  453. /package/dist/{repertoire/tasks/types.js → heart/attachments/sources/adapter.js} +0 -0
  454. /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
  455. /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
  456. /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
  457. /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,19 +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
+ const mcp_manager_1 = require("../repertoire/mcp-manager");
67
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");
68
71
  const http = __importStar(require("http"));
69
72
  const path = __importStar(require("path"));
70
73
  const trust_gate_1 = require("./trust-gate");
71
74
  const pipeline_1 = require("./pipeline");
75
+ const teamsFailoverStates = new Map();
72
76
  const pending_1 = require("../mind/pending");
73
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
+ }
74
88
  // Strip @mention markup from incoming messages.
75
89
  // Removes <at>...</at> tags and trims extra whitespace.
76
90
  // Fallback safety net -- the SDK's activity.mentions.stripText should handle
@@ -125,6 +139,46 @@ function splitMessage(text, maxLen) {
125
139
  }
126
140
  return chunks;
127
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
+ }
128
182
  // Create Teams-specific callbacks for the agent loop.
129
183
  // The SDK handles cumulative text, debouncing (500ms), and the streaming
130
184
  // protocol (streamSequence, streamId, informative/streaming/final types).
@@ -137,12 +191,16 @@ function splitMessage(text, maxLen) {
137
191
  // (transient status) or safeSend (terminal errors). Reasoning is accumulated
138
192
  // and periodically pushed via safeUpdate on the same flush timer tick.
139
193
  function createTeamsCallbacks(stream, controller, sendMessage, options) {
194
+ const MIN_INITIAL_CHARS = 20;
140
195
  let stopped = false; // set when stream signals cancellation (403)
141
196
  let hadToolRun = false;
142
197
  let hadRealOutput = false; // true once reasoning/tool output shown; suppresses phrases
143
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
144
201
  let textBuffer = ""; // accumulated text output for chunked streaming
145
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
146
204
  let phraseTimer = null;
147
205
  let lastPhrase = "";
148
206
  let flushTimer = null;
@@ -192,15 +250,16 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
192
250
  result.catch(() => markStopped());
193
251
  }
194
252
  }
195
- // Safely emit a text delta to the stream.
253
+ // Safely emit a text delta to the stream with AI labels.
196
254
  // On error (e.g. 403 from Teams stop button), abort the controller.
197
255
  function safeEmit(text) {
198
256
  /* v8 ignore next -- defensive guard: stopped set by prior 403; tested via flush abort path @preserve */
199
257
  if (stopped)
200
258
  return;
201
259
  try {
202
- catchAsync(stream.emit(text));
260
+ catchAsync(stream.emit({ text, entities: aiLabelEntities(), channelData: { feedbackLoopEnabled: true } }));
203
261
  streamHasContent = true;
262
+ totalEmitted += text.length;
204
263
  }
205
264
  catch {
206
265
  markStopped();
@@ -215,7 +274,7 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
215
274
  try {
216
275
  // stream.emit() is typed as void but the Teams SDK returns a Promise
217
276
  // internally (async HTTP). Cast to capture the result for awaiting.
218
- const result = stream.emit(text);
277
+ const result = stream.emit({ text, entities: aiLabelEntities(), channelData: { feedbackLoopEnabled: true } });
219
278
  streamHasContent = true;
220
279
  if (result && typeof result.then === "function") {
221
280
  await result;
@@ -227,6 +286,26 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
227
286
  return false;
228
287
  }
229
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
+ }
230
309
  // Safely send a status update to the stream.
231
310
  // On error (e.g. 403 from Teams stop button), abort the controller.
232
311
  function safeUpdate(text) {
@@ -271,11 +350,49 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
271
350
  // emitted text into a single streaming message (cumulative), so every
272
351
  // periodic flush appends to the same response — not separate messages.
273
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).
274
356
  function flushTextBuffer() {
275
357
  if (!textBuffer)
276
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
+ }
277
390
  safeEmit(textBuffer);
278
391
  textBuffer = "";
392
+ if (!firstContentEmitted) {
393
+ firstContentEmitted = true;
394
+ stopPhraseRotation();
395
+ }
279
396
  }
280
397
  function startPhraseRotation(pool) {
281
398
  stopPhraseRotation();
@@ -318,40 +435,108 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
318
435
  onTextChunk: (text) => {
319
436
  if (stopped)
320
437
  return;
321
- 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).
322
440
  textBuffer += text;
323
441
  startFlushTimer();
324
442
  },
325
443
  onClearText: () => {
326
444
  textBuffer = "";
327
445
  },
328
- onToolStart: (name, args) => {
329
- stopPhraseRotation();
330
- flushTextBuffer();
331
- // Emit a placeholder to satisfy the 15s Copilot timeout for initial
332
- // stream.emit(). Without this, long tool chains (e.g. ADO batch ops)
333
- // never emit before the timeout and the user sees "this response was
334
- // stopped". The placeholder is replaced by actual content on next emit.
335
- // https://learn.microsoft.com/en-us/answers/questions/2288017/m365-custom-engine-agents-timeout-message-after-15
336
- if (!streamHasContent)
337
- safeEmit("⏳");
338
- const argSummary = (0, tools_1.summarizeArgs)(name, args) || Object.keys(args).join(", ");
339
- safeUpdate((0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
340
- scope: "shared-work",
341
- phase: "processing",
342
- objective: `running ${name} (${argSummary})...`,
343
- })));
344
- hadToolRun = true;
345
- },
346
- 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.
347
453
  stopPhraseRotation();
348
- const msg = (0, format_1.formatToolResult)(name, summary, success);
349
- safeUpdate((0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
350
- scope: "shared-work",
351
- phase: "processing",
352
- objective: msg,
353
- })));
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
+ }
354
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
+ })(),
355
540
  onKick: () => {
356
541
  stopPhraseRotation();
357
542
  const msg = (0, format_1.formatKick)();
@@ -373,38 +558,27 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
373
558
  safeSend(msg);
374
559
  }
375
560
  },
376
- onConfirmAction: options?.conversationId
377
- ? async (name, args) => {
378
- const convId = options.conversationId;
379
- const argsDesc = Object.entries(args).map(([k, v]) => `${k}: ${v}`).join(", ");
380
- safeUpdate(`Confirm action: ${name} (${argsDesc}) -- reply "yes" to confirm or "no" to cancel`);
381
- return new Promise((resolve) => {
382
- _pendingConfirmations.set(convId, resolve);
383
- // Auto-deny after 2 minutes to prevent indefinite blocking
384
- // (e.g. when the stream dies and the user never sees the prompt).
385
- setTimeout(() => {
386
- if (_pendingConfirmations.has(convId)) {
387
- _pendingConfirmations.delete(convId);
388
- resolve("denied");
389
- }
390
- }, 120_000);
391
- });
392
- }
393
- : undefined,
394
561
  flush: async () => {
395
562
  stopFlushTimer();
563
+ stopPhraseRotation();
396
564
  if (textBuffer) {
565
+ // Bypass MIN_INITIAL_CHARS threshold — flush delivers all remaining content
566
+ firstContentEmitted = true;
397
567
  const text = textBuffer;
398
568
  textBuffer = "";
399
- 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) {
400
574
  // Stream is alive — await the emit so we can catch async 413/failure
401
575
  // and fall through to sendMessage recovery.
402
576
  const ok = await tryEmit(text);
403
577
  if (!ok)
404
578
  markStopped();
405
579
  }
406
- if (stopped && sendMessage) {
407
- // 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.
408
582
  try {
409
583
  await sendMessage(text);
410
584
  }
@@ -415,32 +589,12 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
415
589
  }
416
590
  }
417
591
  }
418
- else if (!streamHasContent) {
419
- 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)");
420
594
  }
421
595
  },
422
596
  };
423
597
  }
424
- // Per-conversation pending confirmation resolvers.
425
- // When a mutate tool needs confirmation, the resolver is stored here.
426
- // The next message from the same conversation resolves it.
427
- const _pendingConfirmations = new Map();
428
- // Confirmation response words (case-insensitive)
429
- const CONFIRM_WORDS = new Set(["yes", "confirm", "go", "y", "ok", "approve", "proceed"]);
430
- function resolvePendingConfirmation(convId, text) {
431
- const resolver = _pendingConfirmations.get(convId);
432
- if (!resolver)
433
- return false;
434
- _pendingConfirmations.delete(convId);
435
- const word = text.trim().toLowerCase();
436
- if (CONFIRM_WORDS.has(word)) {
437
- resolver("confirmed");
438
- }
439
- else {
440
- resolver("denied");
441
- }
442
- return true;
443
- }
444
598
  const _turnCoordinator = (0, turn_coordinator_1.createTurnCoordinator)();
445
599
  function teamsTurnKey(conversationId) {
446
600
  return `teams:${conversationId}`;
@@ -452,13 +606,14 @@ async function withConversationLock(convId, fn) {
452
606
  // are deleted while the process is alive.
453
607
  function getFriendStore() {
454
608
  const friendsPath = path.join((0, identity_1.getAgentRoot)(), "friends");
455
- return new store_file_1.FileFriendStore(friendsPath);
609
+ return new friends_1.FileFriendStore(friendsPath);
456
610
  }
457
611
  function createTeamsCommandRegistry() {
458
612
  const registry = (0, commands_1.createCommandRegistry)();
459
613
  (0, commands_1.registerDefaultCommands)(registry);
460
614
  return registry;
461
615
  }
616
+ /* v8 ignore start -- superseding follow-up slash command handler; tested via startTeamsApp integration tests @preserve */
462
617
  function handleTeamsSlashCommand(text, registry, friendId, conversationId, stream, emitResponse = true) {
463
618
  const parsed = (0, commands_1.parseSlashCommand)(text);
464
619
  if (!parsed)
@@ -482,22 +637,26 @@ function handleTeamsSlashCommand(text, registry, friendId, conversationId, strea
482
637
  }
483
638
  return null;
484
639
  }
640
+ /* v8 ignore stop */
485
641
  // Handle an incoming Teams message
486
- async function handleTeamsMessage(text, stream, conversationId, teamsContext, sendMessage) {
642
+ async function handleTeamsMessage(text, stream, conversationId, teamsContext, sendMessage, reactionOverrides) {
487
643
  const turnKey = teamsTurnKey(conversationId);
488
644
  // NOTE: Confirmation resolution is handled in the app.on("message") handler
489
645
  // BEFORE the conversation lock. By the time we get here, any pending
490
646
  // confirmation has already been resolved and the reply consumed.
491
647
  // Send first thinking phrase immediately so the user sees feedback
492
648
  // before sync I/O (session load, trim) blocks the event loop.
493
- 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
+ }
494
653
  await new Promise(r => setImmediate(r));
495
654
  // Resolve identity provider early for friend resolution + slash command session path
496
655
  const store = getFriendStore();
497
656
  const provider = teamsContext?.aadObjectId ? "aad" : "teams-conversation";
498
657
  const externalId = teamsContext?.aadObjectId || conversationId;
499
658
  // Build FriendResolver for the pipeline
500
- const resolver = new resolver_1.FriendResolver(store, {
659
+ const resolver = new friends_1.FriendResolver(store, {
501
660
  provider,
502
661
  externalId,
503
662
  tenantId: teamsContext?.tenantId,
@@ -507,18 +666,13 @@ async function handleTeamsMessage(text, stream, conversationId, teamsContext, se
507
666
  // Pre-resolve friend for session path + slash commands (pipeline will re-use the cached result)
508
667
  const resolvedContext = await resolver.resolve();
509
668
  const friendId = resolvedContext.friend.id;
510
- const registry = createTeamsCommandRegistry();
511
- // Check for slash commands (before pipeline -- these are transport-level concerns)
512
- if (handleTeamsSlashCommand(text, registry, friendId, conversationId, stream)) {
513
- return;
514
- }
515
669
  // ── Teams adapter concerns: controller, callbacks, session path ──────────
516
670
  const controller = new AbortController();
517
671
  const channelConfig = (0, config_2.getTeamsChannelConfig)();
518
- 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 } : {}) });
519
673
  const traceId = (0, nerves_1.createTraceId)();
520
674
  const sessPath = (0, config_2.sessionPath)(friendId, "teams", conversationId);
521
- const teamsCapabilities = (0, channel_1.getChannelCapabilities)("teams");
675
+ const teamsCapabilities = (0, friends_1.getChannelCapabilities)("teams");
522
676
  const pendingDir = (0, pending_1.getPendingDir)((0, identity_1.getAgentName)(), friendId, "teams", conversationId);
523
677
  // Build Teams-specific toolContext fields for injection into the pipeline
524
678
  const teamsToolContext = teamsContext ? {
@@ -526,42 +680,69 @@ async function handleTeamsMessage(text, stream, conversationId, teamsContext, se
526
680
  adoToken: teamsContext.adoToken,
527
681
  githubToken: teamsContext.githubToken,
528
682
  signin: teamsContext.signin,
529
- summarize: (0, core_1.createSummarize)(),
683
+ summarize: (0, core_1.createSummarize)("human"),
530
684
  tenantId: teamsContext.tenantId,
531
685
  botApi: teamsContext.botApi,
532
686
  } : {};
533
687
  let currentText = text;
688
+ const mcpManager = await (0, mcp_manager_1.getSharedMcpManager)() ?? undefined;
534
689
  while (true) {
535
690
  let drainedSteeringFollowUps = [];
536
691
  // Build runAgentOptions with Teams-specific fields
537
692
  const agentOptions = {
538
693
  traceId,
539
694
  toolContext: teamsToolContext,
695
+ mcpManager,
540
696
  drainSteeringFollowUps: () => {
541
697
  drainedSteeringFollowUps = _turnCoordinator.drainFollowUps(turnKey)
542
698
  .map(({ text: followUpText, effect }) => ({ text: followUpText, effect }));
543
699
  return drainedSteeringFollowUps;
544
700
  },
701
+ ...(reactionOverrides?.isReactionSignal ? { isReactionSignal: true } : {}),
545
702
  };
546
- if (channelConfig.skipConfirmation)
547
- agentOptions.skipConfirmation = true;
548
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 */
549
724
  const result = await (0, pipeline_1.handleInboundTurn)({
550
725
  channel: "teams",
551
726
  sessionKey: conversationId,
552
727
  capabilities: teamsCapabilities,
553
728
  messages: [{ role: "user", content: currentText }],
554
729
  continuityIngressTexts: [currentText],
555
- callbacks,
730
+ callbacks: failoverAwareCallbacks,
556
731
  friendResolver: { resolve: () => Promise.resolve(resolvedContext) },
557
732
  sessionLoader: {
558
733
  loadOrCreate: async () => {
559
734
  const existing = (0, context_1.loadSession)(sessPath);
560
735
  const messages = existing?.messages && existing.messages.length > 0
561
736
  ? existing.messages
562
- : [{ role: "system", content: await (0, prompt_1.buildSystem)("teams", undefined, resolvedContext) }];
737
+ : [{ role: "system", content: (0, prompt_1.flattenSystemPrompt)(await (0, prompt_1.buildSystem)("teams", {}, resolvedContext)) }];
563
738
  (0, core_1.repairOrphanedToolCalls)(messages);
564
- 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
+ };
565
746
  },
566
747
  },
567
748
  pendingDir,
@@ -584,11 +765,33 @@ async function handleTeamsMessage(text, stream, conversationId, teamsContext, se
584
765
  summarize: teamsToolContext.summarize,
585
766
  },
586
767
  }),
587
- postTurn: context_1.postTurn,
588
- 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,
589
773
  signal: controller.signal,
590
774
  runAgentOptions: agentOptions,
775
+ failoverState: teamsFailoverState,
591
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 */
592
795
  // ── Handle gate result ────────────────────────────────────────
593
796
  if (!result.gateResult.allowed) {
594
797
  if ("autoReply" in result.gateResult && result.gateResult.autoReply) {
@@ -628,7 +831,7 @@ async function handleTeamsMessage(text, stream, conversationId, teamsContext, se
628
831
  currentText = replayTail;
629
832
  continue;
630
833
  }
631
- if (handleTeamsSlashCommand(supersedingFollowUp.text, registry, friendId, conversationId, stream, false)) {
834
+ if (handleTeamsSlashCommand(supersedingFollowUp.text, createTeamsCommandRegistry(), friendId, conversationId, stream, false)) {
632
835
  return;
633
836
  }
634
837
  currentText = supersedingFollowUp.text;
@@ -708,7 +911,7 @@ function registerBotHandlers(app, label) {
708
911
  // (graph + ado + github). The verifyState activity only carries a `state`
709
912
  // code with no connectionName, so we try each configured connection until
710
913
  // one succeeds.
711
- app.on("signin.verify-state", async (ctx) => {
914
+ app.on("signin.verify-state", (async (ctx) => {
712
915
  const { api, activity } = ctx;
713
916
  if (!activity.value?.state)
714
917
  return { status: 404 };
@@ -727,7 +930,75 @@ function registerBotHandlers(app, label) {
727
930
  }
728
931
  (0, runtime_1.emitNervesEvent)({ level: "warn", event: "channel.verify_state", component: "channels", message: `[${label}] verify-state failed for all connections`, meta: {} });
729
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;
730
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 */
731
1002
  app.on("message", async (ctx) => {
732
1003
  const { stream, activity, api, signin } = ctx;
733
1004
  const text = activity.text || "";
@@ -736,13 +1007,6 @@ function registerBotHandlers(app, label) {
736
1007
  const userId = activity.from?.id || "";
737
1008
  const channelId = activity.channelId || "msteams";
738
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) } });
739
- // Resolve pending confirmations IMMEDIATELY — before token fetches or
740
- // the conversation lock. The original message holds the lock while
741
- // awaiting confirmation, so acquiring it here would deadlock. Token
742
- // fetches are also unnecessary (and slow) for a simple yes/no reply.
743
- if (resolvePendingConfirmation(convId, text)) {
744
- return;
745
- }
746
1010
  const commandRegistry = createTeamsCommandRegistry();
747
1011
  const parsedSlashCommand = (0, commands_1.parseSlashCommand)(text);
748
1012
  if (parsedSlashCommand) {
@@ -756,7 +1020,7 @@ function registerBotHandlers(app, label) {
756
1020
  const commandStore = getFriendStore();
757
1021
  const commandProvider = activity.from?.aadObjectId ? "aad" : "teams-conversation";
758
1022
  const commandExternalId = activity.from?.aadObjectId || convId;
759
- const commandResolver = new resolver_1.FriendResolver(commandStore, {
1023
+ const commandResolver = new friends_1.FriendResolver(commandStore, {
760
1024
  provider: commandProvider,
761
1025
  externalId: commandExternalId,
762
1026
  tenantId: activity.conversation?.tenantId,
@@ -843,7 +1107,7 @@ function registerBotHandlers(app, label) {
843
1107
  const ctxSend = async (t) => {
844
1108
  // Use send with replyToId (not reply, which adds a blockquote).
845
1109
  // replyToId anchors the message after the user's message in Copilot Chat.
846
- 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 } });
847
1111
  };
848
1112
  await handleTeamsMessage(text, stream, convId, teamsContext, ctxSend);
849
1113
  }
@@ -871,14 +1135,14 @@ function findAadObjectId(friend) {
871
1135
  function resolveTeamsFriendStore(deps) {
872
1136
  return deps.store
873
1137
  ?? deps.createFriendStore?.()
874
- ?? new store_file_1.FileFriendStore(path.join((0, identity_1.getAgentRoot)(), "friends"));
1138
+ ?? new friends_1.FileFriendStore(path.join((0, identity_1.getAgentRoot)(), "friends"));
875
1139
  }
876
1140
  function getTeamsConversations(botApi) {
877
1141
  return botApi.conversations;
878
1142
  }
879
1143
  function hasExplicitCrossChatAuthorization(params) {
880
1144
  return params.intent === "explicit_cross_chat"
881
- && types_1.TRUSTED_LEVELS.has(params.authorizingSession?.trustLevel ?? "stranger");
1145
+ && friends_1.TRUSTED_LEVELS.has(params.authorizingSession?.trustLevel ?? "stranger");
882
1146
  }
883
1147
  async function sendProactiveTeamsMessageToSession(params, deps) {
884
1148
  const store = resolveTeamsFriendStore(deps);
@@ -900,7 +1164,7 @@ async function sendProactiveTeamsMessageToSession(params, deps) {
900
1164
  });
901
1165
  return { delivered: false, reason: "friend_not_found" };
902
1166
  }
903
- if (!hasExplicitCrossChatAuthorization(params) && !types_1.TRUSTED_LEVELS.has(friend.trustLevel ?? "stranger")) {
1167
+ if (!hasExplicitCrossChatAuthorization(params) && !friends_1.TRUSTED_LEVELS.has(friend.trustLevel ?? "stranger")) {
904
1168
  (0, runtime_1.emitNervesEvent)({
905
1169
  component: "senses",
906
1170
  event: "senses.teams_proactive_trust_skip",
@@ -925,6 +1189,16 @@ async function sendProactiveTeamsMessageToSession(params, deps) {
925
1189
  });
926
1190
  return { delivered: false, reason: "missing_target" };
927
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
+ }
928
1202
  try {
929
1203
  const conversation = await conversations.create({
930
1204
  bot: { id: deps.botApi.id },
@@ -1027,6 +1301,20 @@ async function drainAndSendPendingTeams(store, botApi, pendingRoot) {
1027
1301
  catch { /* ignore */ }
1028
1302
  continue;
1029
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
+ }
1030
1318
  const sendResult = await sendProactiveTeamsMessageToSession({
1031
1319
  friendId,
1032
1320
  sessionKey: key,
@@ -1088,7 +1376,7 @@ function startTeamsApp() {
1088
1376
  const port = process.env.PORT ? Number(process.env.PORT) : (0, config_2.getTeamsChannelConfig)().port;
1089
1377
  app.start(port);
1090
1378
  // Diagnostic: log tool count at startup to verify deploy
1091
- const startupTools = (0, tools_1.getToolsForChannel)((0, channel_1.getChannelCapabilities)("teams"));
1379
+ const startupTools = (0, tools_1.getToolsForChannel)((0, friends_1.getChannelCapabilities)("teams"));
1092
1380
  const toolNames = startupTools.map((t) => t.function.name);
1093
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") } });
1094
1382
  // --- Secondary bot (dual-bot support) ---