@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.
- package/README.md +127 -23
- package/RepairGuide.ouro/agent.json +5 -0
- package/RepairGuide.ouro/psyche/IDENTITY.md +19 -0
- package/RepairGuide.ouro/psyche/SOUL.md +55 -0
- package/RepairGuide.ouro/skills/diagnose-broken-remote.md +63 -0
- package/RepairGuide.ouro/skills/diagnose-stacked-typed-issues.md +35 -0
- package/RepairGuide.ouro/skills/diagnose-sync-blocked.md +54 -0
- package/RepairGuide.ouro/skills/diagnose-vault-expired.md +60 -0
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +4 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +2 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
- package/changelog.json +4277 -16
- package/dist/a2a/card.js +56 -0
- package/dist/a2a/client.js +143 -0
- package/dist/a2a/config.js +50 -0
- package/dist/a2a/onboarding.js +83 -0
- package/dist/a2a/server.js +498 -0
- package/dist/a2a/task-store.js +69 -0
- package/dist/a2a/types.js +3 -0
- package/dist/arc/attention-types.js +8 -0
- package/dist/arc/cares.js +144 -0
- package/dist/arc/episodes.js +118 -0
- package/dist/arc/evolution.js +487 -0
- package/dist/arc/flight-recorder.js +810 -0
- package/dist/arc/intentions.js +134 -0
- package/dist/arc/json-store.js +117 -0
- package/dist/arc/obligations.js +326 -0
- package/dist/arc/packets.js +288 -0
- package/dist/arc/presence.js +185 -0
- package/dist/arc/task-lifecycle.js +57 -0
- package/dist/commerce/store.js +755 -0
- package/dist/commerce/types.js +3 -0
- package/dist/heart/active-work.js +860 -43
- package/dist/heart/agent-entry.js +69 -3
- package/dist/heart/attachments/image-normalize.js +194 -0
- package/dist/heart/attachments/materialize.js +97 -0
- package/dist/heart/attachments/originals.js +88 -0
- package/dist/heart/attachments/render.js +29 -0
- package/dist/heart/attachments/sources/bluebubbles.js +156 -0
- package/dist/heart/attachments/sources/cli-local-file.js +78 -0
- package/dist/heart/attachments/sources/index.js +16 -0
- package/dist/heart/attachments/store.js +103 -0
- package/dist/heart/attachments/types.js +93 -0
- package/dist/heart/auth/auth-flow.js +479 -0
- package/dist/heart/awaiting/await-alert.js +146 -0
- package/dist/heart/awaiting/await-expiry.js +108 -0
- package/dist/heart/awaiting/await-loader.js +91 -0
- package/dist/heart/awaiting/await-parser.js +141 -0
- package/dist/heart/awaiting/await-runtime-state.js +100 -0
- package/dist/heart/awaiting/await-scheduler.js +378 -0
- package/dist/heart/background-operations.js +281 -0
- package/dist/heart/bridges/manager.js +137 -17
- package/dist/heart/bridges/store.js +14 -2
- package/dist/heart/bundle-state.js +168 -0
- package/dist/heart/commitments.js +135 -0
- package/dist/heart/config-registry.js +331 -0
- package/dist/heart/config.js +118 -119
- package/dist/heart/context-loss-gauntlet.js +354 -0
- package/dist/heart/context-loss-sentinel.js +907 -0
- package/dist/heart/core.js +1288 -249
- package/dist/heart/cross-chat-delivery.js +8 -22
- package/dist/heart/daemon/agent-config-check.js +427 -0
- package/dist/heart/daemon/agent-discovery.js +102 -3
- package/dist/heart/daemon/agent-service.js +523 -0
- package/dist/heart/daemon/agentic-repair.js +547 -0
- package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
- package/dist/heart/daemon/boot-sync-probe.js +197 -0
- package/dist/heart/daemon/cadence.js +70 -0
- package/dist/heart/daemon/cli-defaults.js +780 -0
- package/dist/heart/daemon/cli-desk.js +322 -0
- package/dist/heart/daemon/cli-exec.js +7940 -0
- package/dist/heart/daemon/cli-help.js +576 -0
- package/dist/heart/daemon/cli-parse.js +1850 -0
- package/dist/heart/daemon/cli-render-doctor.js +57 -0
- package/dist/heart/daemon/cli-render.js +763 -0
- package/dist/heart/daemon/cli-types.js +8 -0
- package/dist/heart/daemon/connect-bay.js +323 -0
- package/dist/heart/daemon/daemon-cli.js +29 -1750
- package/dist/heart/daemon/daemon-entry.js +533 -3
- package/dist/heart/daemon/daemon-health.js +176 -0
- package/dist/heart/daemon/daemon-rollup.js +57 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +88 -13
- package/dist/heart/daemon/daemon-tombstone.js +236 -0
- package/dist/heart/daemon/daemon.js +950 -76
- package/dist/heart/daemon/dns-workflow.js +394 -0
- package/dist/heart/daemon/doctor-types.js +8 -0
- package/dist/heart/daemon/doctor.js +873 -0
- package/dist/heart/daemon/health-monitor.js +125 -1
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +135 -1
- package/dist/heart/daemon/http-health-probe.js +80 -0
- package/dist/heart/daemon/human-command-screens.js +234 -0
- package/dist/heart/daemon/human-readiness.js +114 -0
- package/dist/heart/daemon/inner-status.js +78 -0
- package/dist/heart/daemon/interactive-repair.js +394 -0
- package/dist/heart/daemon/launchd.js +37 -8
- package/dist/heart/daemon/log-tailer.js +79 -10
- package/dist/heart/daemon/logs-prune.js +110 -0
- package/dist/heart/daemon/mcp-canary.js +297 -0
- package/dist/heart/daemon/message-router.js +6 -2
- package/dist/heart/daemon/migrate-to-desk.js +848 -0
- package/dist/heart/daemon/os-cron-deps.js +135 -0
- package/dist/heart/daemon/os-cron.js +14 -12
- package/dist/heart/daemon/ouro-bot-entry.js +4 -2
- package/dist/heart/daemon/ouro-entry.js +3 -1
- package/dist/heart/daemon/plugin-cli.js +432 -0
- package/dist/heart/daemon/process-manager.js +511 -40
- package/dist/heart/daemon/provider-discovery.js +137 -0
- package/dist/heart/daemon/provider-ping-progress.js +83 -0
- package/dist/heart/daemon/pulse.js +475 -0
- package/dist/heart/daemon/readiness-repair.js +365 -0
- package/dist/heart/daemon/run-hooks.js +2 -0
- package/dist/heart/daemon/runtime-logging.js +41 -14
- package/dist/heart/daemon/runtime-metadata.js +2 -30
- package/dist/heart/daemon/safe-mode.js +161 -0
- package/dist/heart/daemon/sense-manager.js +564 -38
- package/dist/heart/daemon/session-id-resolver.js +131 -0
- package/dist/heart/daemon/skill-management-installer.js +1 -1
- package/dist/heart/daemon/socket-client.js +158 -11
- package/dist/heart/daemon/stale-bundle-prune.js +96 -0
- package/dist/heart/daemon/startup-tui.js +330 -0
- package/dist/heart/daemon/task-scheduler.js +117 -39
- package/dist/heart/daemon/terminal-ui.js +499 -0
- package/dist/heart/daemon/thoughts.js +229 -17
- package/dist/heart/daemon/up-progress.js +366 -0
- package/dist/heart/daemon/vault-items.js +56 -0
- package/dist/heart/delegation.js +1 -4
- package/dist/heart/habits/habit-migration.js +189 -0
- package/dist/heart/habits/habit-parser.js +211 -0
- package/dist/heart/habits/habit-runtime-state.js +114 -0
- package/dist/heart/habits/habit-scheduler.js +391 -0
- package/dist/heart/habits/habit-session-summary.js +318 -0
- package/dist/heart/habits/habit-session.js +618 -0
- package/dist/heart/{daemon → hatch}/hatch-flow.js +42 -56
- package/dist/heart/{daemon → hatch}/hatch-specialist.js +6 -8
- package/dist/heart/{daemon → hatch}/specialist-prompt.js +12 -9
- package/dist/heart/{daemon → hatch}/specialist-tools.js +45 -18
- package/dist/heart/identity.js +174 -57
- package/dist/heart/kept-notes.js +289 -0
- package/dist/heart/kicks.js +1 -1
- package/dist/heart/machine-identity.js +161 -0
- package/dist/heart/mail-import-discovery.js +353 -0
- package/dist/heart/mailbox/mailbox-http-hooks.js +96 -0
- package/dist/heart/mailbox/mailbox-http-response.js +7 -0
- package/dist/heart/mailbox/mailbox-http-routes.js +375 -0
- package/dist/heart/mailbox/mailbox-http-static.js +103 -0
- package/dist/heart/mailbox/mailbox-http-transport.js +116 -0
- package/dist/heart/mailbox/mailbox-http.js +99 -0
- package/dist/heart/mailbox/mailbox-read.js +37 -0
- package/dist/heart/mailbox/mailbox-types.js +27 -0
- package/dist/heart/mailbox/mailbox-view.js +197 -0
- package/dist/heart/mailbox/readers/agent-machine.js +428 -0
- package/dist/heart/mailbox/readers/continuity-readers.js +329 -0
- package/dist/heart/mailbox/readers/mail.js +375 -0
- package/dist/heart/mailbox/readers/runtime-readers.js +815 -0
- package/dist/heart/mailbox/readers/sessions.js +232 -0
- package/dist/heart/mailbox/readers/shared.js +111 -0
- package/dist/heart/mcp/mcp-server.js +696 -0
- package/dist/heart/migrate-config.js +100 -0
- package/dist/heart/model-capabilities.js +19 -0
- package/dist/heart/orientation-frame.js +217 -0
- package/dist/heart/platform.js +81 -0
- package/dist/heart/provider-attempt.js +134 -0
- package/dist/heart/provider-binding-resolver.js +273 -0
- package/dist/heart/provider-credentials.js +425 -0
- package/dist/heart/provider-failover.js +311 -0
- package/dist/heart/provider-models.js +81 -0
- package/dist/heart/provider-ping.js +262 -0
- package/dist/heart/provider-readiness-cache.js +119 -0
- package/dist/heart/provider-visibility.js +188 -0
- package/dist/heart/providers/anthropic-token.js +131 -0
- package/dist/heart/providers/anthropic.js +139 -52
- package/dist/heart/providers/azure.js +23 -11
- package/dist/heart/providers/error-classification.js +127 -0
- package/dist/heart/providers/github-copilot.js +145 -0
- package/dist/heart/providers/minimax-vlm.js +189 -0
- package/dist/heart/providers/minimax.js +26 -8
- package/dist/heart/providers/openai-codex-token.js +349 -0
- package/dist/heart/providers/openai-codex.js +55 -40
- package/dist/heart/runtime-capability-check.js +170 -0
- package/dist/heart/runtime-credentials.js +367 -0
- package/dist/heart/runtime-cwd.js +87 -0
- package/dist/heart/sense-truth.js +17 -4
- package/dist/heart/session-activity.js +48 -24
- package/dist/heart/session-events.js +1133 -0
- package/dist/heart/session-playback-cli-main.js +5 -0
- package/dist/heart/session-playback-cli.js +36 -0
- package/dist/heart/session-playback.js +231 -0
- package/dist/heart/session-stats-cli-main.js +5 -0
- package/dist/heart/session-stats.js +182 -0
- package/dist/heart/session-transcript.js +133 -0
- package/dist/heart/start-of-turn-packet.js +357 -0
- package/dist/heart/streaming.js +44 -27
- package/dist/heart/structured-output.js +196 -0
- package/dist/heart/sync-classification.js +176 -0
- package/dist/heart/sync.js +449 -0
- package/dist/heart/target-resolution.js +11 -7
- package/dist/heart/tempo.js +93 -0
- package/dist/heart/temporal-view.js +41 -0
- package/dist/heart/timeouts.js +101 -0
- package/dist/heart/tool-activity-callbacks.js +59 -0
- package/dist/heart/tool-description.js +155 -0
- package/dist/heart/tool-friction.js +55 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-context.js +450 -0
- package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +6 -5
- package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
- package/dist/heart/versioning/ouro-path-installer.js +426 -0
- package/dist/heart/versioning/ouro-version-manager.js +409 -0
- package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
- package/dist/heart/{daemon → versioning}/update-checker.js +6 -1
- package/dist/heart/versioning/update-hooks.js +154 -0
- package/dist/heart/work-card.js +390 -0
- package/dist/mailbox-ui/assets/index-CaTIFDmv.js +1 -0
- package/dist/mailbox-ui/assets/index-Du_9G9WO.css +1 -0
- package/dist/mailbox-ui/assets/vendor-CcN1XpQ9.js +61 -0
- package/dist/mailbox-ui/index.html +16 -0
- package/dist/mailroom/attention.js +167 -0
- package/dist/mailroom/autonomy.js +209 -0
- package/dist/mailroom/blob-store.js +715 -0
- package/dist/mailroom/body-cache.js +61 -0
- package/dist/mailroom/core.js +788 -0
- package/dist/mailroom/entry.js +160 -0
- package/dist/mailroom/file-store.js +568 -0
- package/dist/mailroom/mbox-import.js +393 -0
- package/dist/mailroom/migration.js +164 -0
- package/dist/mailroom/outbound.js +380 -0
- package/dist/mailroom/policy.js +263 -0
- package/dist/mailroom/reader.js +233 -0
- package/dist/mailroom/search-cache.js +334 -0
- package/dist/mailroom/search-relevance.js +319 -0
- package/dist/mailroom/smtp-ingress.js +176 -0
- package/dist/mailroom/source-state.js +176 -0
- package/dist/mailroom/thread.js +109 -0
- package/dist/mailroom/travel-extract.js +89 -0
- package/dist/mind/bundle-manifest.js +24 -2
- package/dist/mind/context.js +250 -101
- package/dist/mind/desk-section.js +362 -0
- package/dist/mind/diary-integrity.js +60 -0
- package/dist/mind/{memory.js → diary.js} +68 -77
- package/dist/mind/embedding-provider.js +60 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/{associative-recall.js → note-search.js} +47 -58
- package/dist/mind/obligation-steering.js +221 -0
- package/dist/mind/pending.js +6 -1
- package/dist/mind/prompt-refresh.js +3 -2
- package/dist/mind/prompt.js +1020 -148
- package/dist/mind/provenance-trust.js +26 -0
- package/dist/mind/record-paths.js +312 -0
- package/dist/mind/scrutiny.js +173 -0
- package/dist/nerves/cli-logging.js +7 -1
- package/dist/nerves/coverage/audit-rules.js +15 -6
- package/dist/nerves/coverage/audit.js +28 -2
- package/dist/nerves/coverage/cli.js +1 -1
- package/dist/nerves/coverage/contract.js +5 -5
- package/dist/nerves/coverage/file-completeness.js +139 -5
- package/dist/nerves/event-buffer.js +111 -0
- package/dist/nerves/index.js +224 -4
- package/dist/nerves/observation.js +20 -0
- package/dist/nerves/redact.js +79 -0
- package/dist/nerves/review/cli-main.js +5 -0
- package/dist/nerves/review/cli.js +156 -0
- package/dist/nerves/review/core.js +152 -0
- package/dist/nerves/runtime.js +5 -1
- package/dist/repertoire/ado-client.js +15 -56
- package/dist/repertoire/ado-semantic.js +16 -10
- package/dist/repertoire/api-client.js +97 -0
- package/dist/repertoire/bitwarden-store.js +1042 -0
- package/dist/repertoire/bundle-templates.js +71 -0
- package/dist/repertoire/bw-installer.js +180 -0
- package/dist/repertoire/coding/codex-jsonl.js +64 -0
- package/dist/repertoire/coding/context-pack.js +331 -0
- package/dist/repertoire/coding/feedback.js +197 -30
- package/dist/repertoire/coding/manager.js +166 -10
- package/dist/repertoire/coding/spawner.js +55 -9
- package/dist/repertoire/coding/tools.js +219 -7
- package/dist/repertoire/commerce-errors.js +109 -0
- package/dist/repertoire/commerce-self-test.js +156 -0
- package/dist/repertoire/credential-access.js +178 -0
- package/dist/repertoire/desk/classifier.js +362 -0
- package/dist/repertoire/duffel-client.js +185 -0
- package/dist/repertoire/github-client.js +14 -55
- package/dist/repertoire/graph-client.js +11 -52
- package/dist/repertoire/guardrails.js +161 -27
- package/dist/repertoire/mcp-client.js +295 -0
- package/dist/repertoire/mcp-manager.js +434 -0
- package/dist/repertoire/mcp-tools.js +83 -0
- package/dist/repertoire/plugin-mcp.js +175 -0
- package/dist/repertoire/plugins.js +253 -0
- package/dist/repertoire/shell-sessions.js +133 -0
- package/dist/repertoire/skills.js +48 -4
- package/dist/repertoire/stripe-client.js +131 -0
- package/dist/repertoire/tool-results.js +29 -0
- package/dist/repertoire/tools-a2a.js +282 -0
- package/dist/repertoire/tools-attachments.js +317 -0
- package/dist/repertoire/tools-awaiting.js +372 -0
- package/dist/repertoire/tools-base.js +63 -1082
- package/dist/repertoire/tools-bluebubbles.js +2 -0
- package/dist/repertoire/tools-bridge.js +144 -0
- package/dist/repertoire/tools-bundle.js +993 -0
- package/dist/repertoire/tools-commerce.js +253 -0
- package/dist/repertoire/tools-config.js +186 -0
- package/dist/repertoire/tools-continuity.js +252 -0
- package/dist/repertoire/tools-credential.js +383 -0
- package/dist/repertoire/tools-evolution.js +527 -0
- package/dist/repertoire/tools-files.js +344 -0
- package/dist/repertoire/tools-flight.js +290 -0
- package/dist/repertoire/tools-flow.js +119 -0
- package/dist/repertoire/tools-github.js +3 -8
- package/dist/repertoire/tools-mail.js +1975 -0
- package/dist/repertoire/tools-notes.js +459 -0
- package/dist/repertoire/tools-obligations.js +143 -0
- package/dist/repertoire/tools-orientation.js +31 -0
- package/dist/repertoire/tools-record.js +482 -0
- package/dist/repertoire/tools-runtime.js +150 -0
- package/dist/repertoire/tools-session.js +906 -0
- package/dist/repertoire/tools-shell.js +120 -0
- package/dist/repertoire/tools-stripe.js +224 -0
- package/dist/repertoire/tools-surface.js +355 -0
- package/dist/repertoire/tools-teams.js +12 -39
- package/dist/repertoire/tools-travel.js +125 -0
- package/dist/repertoire/tools-trip.js +982 -0
- package/dist/repertoire/tools-user-profile.js +146 -0
- package/dist/repertoire/tools-vault.js +40 -0
- package/dist/repertoire/tools-voice.js +145 -0
- package/dist/repertoire/tools.js +250 -79
- package/dist/repertoire/travel-api-client.js +360 -0
- package/dist/repertoire/user-profile.js +131 -0
- package/dist/repertoire/vault-setup.js +246 -0
- package/dist/repertoire/vault-unlock.js +594 -0
- package/dist/scripts/claude-code-hook.js +41 -0
- package/dist/scripts/claude-code-stop-hook.js +47 -0
- package/dist/senses/a2a-entry.js +78 -0
- package/dist/senses/attention-queue.js +186 -0
- package/dist/senses/await-turn-message.js +58 -0
- package/dist/senses/bluebubbles/active-turns.js +216 -0
- package/dist/senses/bluebubbles/attachment-cache.js +53 -0
- package/dist/senses/bluebubbles/attachment-download.js +137 -0
- package/dist/senses/{bluebubbles-client.js → bluebubbles/client.js} +219 -18
- package/dist/senses/bluebubbles/entry.js +77 -0
- package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +20 -3
- package/dist/senses/bluebubbles/index.js +2732 -0
- package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -71
- package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +33 -12
- package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +3 -3
- package/dist/senses/bluebubbles/processed-log.js +133 -0
- package/dist/senses/bluebubbles/replay.js +137 -0
- package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +30 -2
- package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
- package/dist/senses/bluebubbles-meta-guard.js +40 -0
- package/dist/senses/cli/bracketed-paste.js +82 -0
- package/dist/senses/cli/image-paste.js +287 -0
- package/dist/senses/cli/image-ref-navigation.js +75 -0
- package/dist/senses/cli/ink-app.js +156 -0
- package/dist/senses/cli/inline-diff.js +64 -0
- package/dist/senses/cli/input-keys.js +174 -0
- package/dist/senses/cli/kill-ring.js +86 -0
- package/dist/senses/cli/message-list.js +51 -0
- package/dist/senses/cli/ouro-tui.js +607 -0
- package/dist/senses/cli/spinner-imperative.js +135 -0
- package/dist/senses/cli/spinner.js +101 -0
- package/dist/senses/cli/status-line.js +60 -0
- package/dist/senses/cli/streaming-markdown.js +526 -0
- package/dist/senses/cli/tool-display.js +85 -0
- package/dist/senses/cli/tool-render.js +85 -0
- package/dist/senses/cli/tui-store.js +240 -0
- package/dist/senses/cli/virtual-list.js +35 -0
- package/dist/senses/cli-entry.js +60 -8
- package/dist/senses/cli-layout.js +100 -0
- package/dist/senses/cli.js +522 -212
- package/dist/senses/commands.js +66 -3
- package/dist/senses/habit-turn-message.js +160 -0
- package/dist/senses/inner-dialog-worker.js +499 -22
- package/dist/senses/inner-dialog.js +544 -47
- package/dist/senses/mail-entry.js +66 -0
- package/dist/senses/mail.js +379 -0
- package/dist/senses/pipeline.js +870 -179
- package/dist/senses/proactive-content-guard.js +51 -0
- package/dist/senses/shared-turn.js +416 -0
- package/dist/senses/surface-tool.js +109 -0
- package/dist/senses/teams-entry.js +60 -8
- package/dist/senses/teams.js +400 -112
- package/dist/senses/trust-gate.js +102 -7
- package/dist/senses/voice/audio-playback.js +237 -0
- package/dist/senses/voice/audio-routing.js +119 -0
- package/dist/senses/voice/elevenlabs.js +202 -0
- package/dist/senses/voice/floor-control.js +431 -0
- package/dist/senses/voice/floor-controller.js +115 -0
- package/dist/senses/voice/golden-path.js +116 -0
- package/dist/senses/voice/index.js +29 -0
- package/dist/senses/voice/meeting.js +113 -0
- package/dist/senses/voice/outbound.js +190 -0
- package/dist/senses/voice/phone.js +33 -0
- package/dist/senses/voice/playback.js +139 -0
- package/dist/senses/voice/realtime-eval.js +496 -0
- package/dist/senses/voice/realtime-trace.js +531 -0
- package/dist/senses/voice/transcript.js +70 -0
- package/dist/senses/voice/turn.js +191 -0
- package/dist/senses/voice/twilio-phone-runtime.js +807 -0
- package/dist/senses/voice/twilio-phone.js +5077 -0
- package/dist/senses/voice/types.js +2 -0
- package/dist/senses/voice/whisper.js +161 -0
- package/dist/senses/voice-entry.js +81 -0
- package/dist/senses/voice-realtime-eval-command.js +99 -0
- package/dist/senses/voice-realtime-eval-entry.js +21 -0
- package/dist/senses/voice-twilio-entry.js +87 -0
- package/dist/trips/core.js +138 -0
- package/dist/trips/store.js +265 -0
- package/dist/util/frontmatter.js +69 -0
- package/package.json +57 -12
- package/skills/agent-commerce.md +113 -0
- package/skills/browser-navigation.md +117 -0
- package/skills/commerce-setup-guide.md +116 -0
- package/skills/commerce-setup.md +84 -0
- package/skills/configure-dev-tools.md +99 -0
- package/skills/travel-planning.md +138 -0
- package/dist/heart/daemon/auth-flow.js +0 -351
- package/dist/heart/daemon/ouro-path-installer.js +0 -178
- package/dist/heart/daemon/update-hooks.js +0 -138
- package/dist/heart/safe-workspace.js +0 -228
- package/dist/heart/session-recall.js +0 -116
- package/dist/mind/friends/channel.js +0 -84
- package/dist/mind/friends/group-context.js +0 -144
- package/dist/mind/friends/resolver.js +0 -84
- package/dist/mind/friends/store-file.js +0 -190
- package/dist/mind/friends/store.js +0 -4
- package/dist/mind/friends/tokens.js +0 -26
- package/dist/mind/friends/trust-explanation.js +0 -74
- package/dist/mind/friends/types.js +0 -29
- package/dist/repertoire/tasks/board.js +0 -134
- package/dist/repertoire/tasks/index.js +0 -224
- package/dist/repertoire/tasks/lifecycle.js +0 -80
- package/dist/repertoire/tasks/middleware.js +0 -65
- package/dist/repertoire/tasks/parser.js +0 -173
- package/dist/repertoire/tasks/scanner.js +0 -132
- package/dist/repertoire/tasks/transitions.js +0 -144
- package/dist/senses/bluebubbles-entry.js +0 -13
- package/dist/senses/bluebubbles.js +0 -1177
- package/dist/senses/debug-activity.js +0 -148
- package/subagents/README.md +0 -7
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
- /package/dist/{repertoire/tasks/types.js → heart/attachments/sources/adapter.js} +0 -0
- /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
- /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
- /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
- /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
package/dist/senses/teams.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
//
|
|
333
|
-
|
|
334
|
-
//
|
|
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
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
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 (
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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,
|
|
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",
|
|
737
|
+
: [{ role: "system", content: (0, prompt_1.flattenSystemPrompt)(await (0, prompt_1.buildSystem)("teams", {}, resolvedContext)) }];
|
|
563
738
|
(0, core_1.repairOrphanedToolCalls)(messages);
|
|
564
|
-
return {
|
|
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:
|
|
588
|
-
|
|
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,
|
|
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
|
|
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
|
|
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
|
-
&&
|
|
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) && !
|
|
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,
|
|
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) ---
|