@ouro.bot/cli 0.1.0-alpha.55 → 0.1.0-alpha.550
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 +133 -19
- 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-bootstrap-drift.md +54 -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 +3555 -0
- package/dist/arc/attention-types.js +8 -0
- package/dist/arc/cares.js +140 -0
- package/dist/arc/episodes.js +117 -0
- package/dist/arc/intentions.js +133 -0
- package/dist/arc/json-store.js +117 -0
- package/dist/arc/obligations.js +237 -0
- package/dist/arc/packets.js +193 -0
- package/dist/arc/presence.js +185 -0
- package/dist/arc/task-lifecycle.js +65 -0
- package/dist/heart/active-work.js +837 -26
- package/dist/heart/agent-entry.js +58 -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/adapter.js +2 -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/background-operations.js +281 -0
- package/dist/heart/bundle-state.js +168 -0
- package/dist/heart/commitments.js +111 -0
- package/dist/heart/config-registry.js +304 -0
- package/dist/heart/config.js +114 -118
- package/dist/heart/core.js +925 -246
- package/dist/heart/cross-chat-delivery.js +3 -18
- package/dist/heart/daemon/agent-config-check.js +512 -0
- package/dist/heart/daemon/agent-discovery.js +102 -3
- package/dist/heart/daemon/agent-service.js +522 -0
- package/dist/heart/daemon/agentic-repair.js +554 -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 +665 -0
- package/dist/heart/daemon/cli-exec.js +7565 -0
- package/dist/heart/daemon/cli-help.js +498 -0
- package/dist/heart/daemon/cli-parse.js +1590 -0
- package/dist/heart/daemon/cli-render-doctor.js +57 -0
- package/dist/heart/daemon/cli-render.js +775 -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 -1672
- package/dist/heart/daemon/daemon-entry.js +417 -2
- package/dist/heart/daemon/daemon-health.js +183 -0
- package/dist/heart/daemon/daemon-rollup.js +58 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +87 -13
- package/dist/heart/daemon/daemon-tombstone.js +236 -0
- package/dist/heart/daemon/daemon.js +758 -71
- package/dist/heart/daemon/dns-workflow.js +394 -0
- package/dist/heart/daemon/doctor-types.js +8 -0
- package/dist/heart/daemon/doctor.js +844 -0
- package/dist/heart/daemon/drift-detection.js +146 -0
- package/dist/heart/daemon/health-monitor.js +122 -1
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +115 -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 +102 -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 +82 -12
- 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 +2 -2
- 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/process-manager.js +375 -33
- 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 +67 -16
- package/dist/heart/daemon/runtime-metadata.js +3 -31
- package/dist/heart/daemon/safe-mode.js +161 -0
- package/dist/heart/daemon/sense-manager.js +353 -38
- package/dist/heart/daemon/session-id-resolver.js +131 -0
- package/dist/heart/daemon/skill-management-installer.js +94 -0
- 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 +3 -25
- package/dist/heart/daemon/terminal-ui.js +499 -0
- package/dist/heart/daemon/thoughts.js +162 -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 -1
- package/dist/heart/habits/habit-migration.js +189 -0
- package/dist/heart/habits/habit-parser.js +140 -0
- package/dist/heart/habits/habit-runtime-state.js +100 -0
- package/dist/heart/habits/habit-scheduler.js +372 -0
- package/dist/heart/{daemon → hatch}/hatch-flow.js +52 -117
- 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 +35 -12
- package/dist/heart/identity.js +200 -51
- package/dist/heart/kept-notes.js +357 -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 +66 -0
- package/dist/heart/mailbox/mailbox-http-response.js +7 -0
- package/dist/heart/mailbox/mailbox-http-routes.js +246 -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 +31 -0
- package/dist/heart/mailbox/mailbox-types.js +27 -0
- package/dist/heart/mailbox/mailbox-view.js +195 -0
- package/dist/heart/mailbox/readers/agent-machine.js +382 -0
- package/dist/heart/mailbox/readers/continuity-readers.js +338 -0
- package/dist/heart/mailbox/readers/mail.js +362 -0
- package/dist/heart/mailbox/readers/runtime-readers.js +651 -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 +683 -0
- package/dist/heart/migrate-config.js +100 -0
- package/dist/heart/model-capabilities.js +19 -0
- package/dist/heart/platform.js +81 -0
- package/dist/heart/provider-attempt.js +134 -0
- package/dist/heart/provider-binding-resolver.js +255 -0
- package/dist/heart/provider-credentials.js +425 -0
- package/dist/heart/provider-failover.js +301 -0
- package/dist/heart/provider-models.js +81 -0
- package/dist/heart/provider-ping.js +262 -0
- package/dist/heart/provider-state.js +216 -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 +97 -13
- 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.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 +11 -4
- package/dist/heart/session-activity.js +43 -22
- package/dist/heart/session-events.js +1149 -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 +243 -0
- package/dist/heart/start-of-turn-packet.js +345 -0
- package/dist/heart/streaming.js +44 -27
- package/dist/heart/sync-classification.js +176 -0
- package/dist/heart/sync.js +449 -0
- package/dist/heart/target-resolution.js +9 -5
- 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 +139 -0
- package/dist/heart/tool-friction.js +55 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-context.js +381 -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 +295 -0
- package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
- package/dist/heart/{daemon → versioning}/update-checker.js +6 -1
- package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
- package/dist/mailbox-ui/assets/index-BPr5vNuM.css +1 -0
- package/dist/mailbox-ui/assets/index-Cm51CY9W.js +61 -0
- package/dist/mailbox-ui/index.html +15 -0
- package/dist/mailroom/attention.js +167 -0
- package/dist/mailroom/autonomy.js +209 -0
- package/dist/mailroom/blob-store.js +674 -0
- package/dist/mailroom/body-cache.js +61 -0
- package/dist/mailroom/core.js +720 -0
- package/dist/mailroom/entry.js +160 -0
- package/dist/mailroom/file-store.js +430 -0
- package/dist/mailroom/mbox-import.js +383 -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 +256 -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 +7 -1
- package/dist/mind/context.js +165 -101
- package/dist/mind/diary-integrity.js +60 -0
- package/dist/mind/{memory.js → diary.js} +62 -75
- package/dist/mind/embedding-provider.js +60 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/friends/channel.js +30 -0
- package/dist/mind/friends/resolver.js +54 -2
- package/dist/mind/friends/store-file.js +39 -3
- package/dist/mind/friends/types.js +2 -2
- package/dist/mind/journal-index.js +161 -0
- package/dist/mind/note-search.js +268 -0
- package/dist/mind/obligation-steering.js +221 -0
- package/dist/mind/pending.js +4 -0
- package/dist/mind/prompt-refresh.js +3 -2
- package/dist/mind/prompt.js +995 -123
- package/dist/mind/provenance-trust.js +26 -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/coverage/run-artifacts.js +1 -1
- 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 +11 -10
- package/dist/repertoire/api-client.js +97 -0
- package/dist/repertoire/bitwarden-store.js +816 -0
- package/dist/repertoire/bundle-templates.js +72 -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 +330 -0
- package/dist/repertoire/coding/feedback.js +197 -30
- package/dist/repertoire/coding/manager.js +158 -9
- package/dist/repertoire/coding/spawner.js +55 -9
- package/dist/repertoire/coding/tools.js +170 -7
- package/dist/repertoire/commerce-errors.js +109 -0
- package/dist/repertoire/commerce-self-test.js +156 -0
- package/dist/repertoire/credential-access.js +111 -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 +396 -0
- package/dist/repertoire/mcp-client.js +295 -0
- package/dist/repertoire/mcp-manager.js +362 -0
- package/dist/repertoire/mcp-tools.js +63 -0
- package/dist/repertoire/shell-sessions.js +133 -0
- package/dist/repertoire/skills.js +15 -24
- package/dist/repertoire/stripe-client.js +131 -0
- package/dist/repertoire/tasks/board.js +31 -5
- package/dist/repertoire/tasks/fix.js +182 -0
- package/dist/repertoire/tasks/index.js +16 -4
- package/dist/repertoire/tasks/lifecycle.js +2 -2
- package/dist/repertoire/tasks/parser.js +3 -2
- package/dist/repertoire/tasks/scanner.js +194 -37
- package/dist/repertoire/tasks/transitions.js +16 -78
- package/dist/repertoire/tool-results.js +29 -0
- package/dist/repertoire/tools-attachments.js +317 -0
- package/dist/repertoire/tools-base.js +47 -1075
- package/dist/repertoire/tools-bluebubbles.js +1 -0
- package/dist/repertoire/tools-bridge.js +142 -0
- package/dist/repertoire/tools-bundle.js +984 -0
- package/dist/repertoire/tools-config.js +185 -0
- package/dist/repertoire/tools-continuity.js +248 -0
- package/dist/repertoire/tools-credential.js +381 -0
- package/dist/repertoire/tools-files.js +342 -0
- package/dist/repertoire/tools-flight.js +224 -0
- package/dist/repertoire/tools-flow.js +119 -0
- package/dist/repertoire/tools-github.js +1 -7
- package/dist/repertoire/tools-mail.js +1857 -0
- package/dist/repertoire/tools-notes.js +421 -0
- package/dist/repertoire/tools-session.js +750 -0
- package/dist/repertoire/tools-shell.js +120 -0
- package/dist/repertoire/tools-stripe.js +180 -0
- package/dist/repertoire/tools-surface.js +243 -0
- package/dist/repertoire/tools-teams.js +9 -39
- package/dist/repertoire/tools-travel.js +125 -0
- package/dist/repertoire/tools-trip.js +604 -0
- package/dist/repertoire/tools-user-profile.js +144 -0
- package/dist/repertoire/tools-vault.js +40 -0
- package/dist/repertoire/tools.js +108 -100
- 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 +561 -0
- package/dist/scripts/claude-code-hook.js +41 -0
- package/dist/scripts/claude-code-stop-hook.js +47 -0
- package/dist/senses/attention-queue.js +116 -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 +2305 -0
- package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -70
- 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/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 +187 -0
- package/dist/senses/cli.js +520 -209
- package/dist/senses/commands.js +66 -3
- package/dist/senses/habit-turn-message.js +108 -0
- package/dist/senses/inner-dialog-worker.js +175 -21
- package/dist/senses/inner-dialog.js +330 -27
- package/dist/senses/mail-entry.js +66 -0
- package/dist/senses/mail.js +379 -0
- package/dist/senses/pipeline.js +569 -182
- package/dist/senses/proactive-content-guard.js +51 -0
- package/dist/senses/shared-turn.js +248 -0
- package/dist/senses/surface-tool.js +68 -0
- package/dist/senses/teams-entry.js +60 -8
- package/dist/senses/teams.js +387 -98
- package/dist/senses/trust-gate.js +100 -5
- package/dist/trips/core.js +138 -0
- package/dist/trips/store.js +146 -0
- package/package.json +38 -7
- package/skills/agent-commerce.md +106 -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 +101 -0
- package/skills/travel-planning.md +138 -0
- package/dist/heart/daemon/ouro-path-installer.js +0 -178
- package/dist/heart/daemon/subagent-installer.js +0 -166
- package/dist/heart/session-recall.js +0 -116
- package/dist/mind/associative-recall.js +0 -209
- 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 -86
- package/subagents/work-doer.md +0 -237
- package/subagents/work-merger.md +0 -618
- package/subagents/work-planner.md +0 -390
- /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/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;
|
|
@@ -58,19 +61,34 @@ 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");
|
|
64
|
+
const proactive_content_guard_1 = require("./proactive-content-guard");
|
|
61
65
|
const store_file_1 = require("../mind/friends/store-file");
|
|
62
66
|
const types_1 = require("../mind/friends/types");
|
|
63
67
|
const resolver_1 = require("../mind/friends/resolver");
|
|
64
68
|
const tokens_1 = require("../mind/friends/tokens");
|
|
65
69
|
const turn_coordinator_1 = require("../heart/turn-coordinator");
|
|
66
70
|
const identity_1 = require("../heart/identity");
|
|
71
|
+
const mcp_manager_1 = require("../repertoire/mcp-manager");
|
|
67
72
|
const progress_story_1 = require("../heart/progress-story");
|
|
73
|
+
const tool_activity_callbacks_1 = require("../heart/tool-activity-callbacks");
|
|
74
|
+
const commands_2 = require("./commands");
|
|
68
75
|
const http = __importStar(require("http"));
|
|
69
76
|
const path = __importStar(require("path"));
|
|
70
77
|
const trust_gate_1 = require("./trust-gate");
|
|
71
78
|
const pipeline_1 = require("./pipeline");
|
|
79
|
+
const teamsFailoverStates = new Map();
|
|
72
80
|
const pending_1 = require("../mind/pending");
|
|
73
81
|
const continuity_1 = require("./continuity");
|
|
82
|
+
// AIGeneratedContent entity and feedbackLoopEnabled channelData for all outbound
|
|
83
|
+
// Teams messages. Required by Teams AI UX best practices.
|
|
84
|
+
function aiLabelEntities() {
|
|
85
|
+
return [{
|
|
86
|
+
type: "https://schema.org/Message",
|
|
87
|
+
"@type": "Message",
|
|
88
|
+
"@context": "https://schema.org",
|
|
89
|
+
additionalType: ["AIGeneratedContent"],
|
|
90
|
+
}];
|
|
91
|
+
}
|
|
74
92
|
// Strip @mention markup from incoming messages.
|
|
75
93
|
// Removes <at>...</at> tags and trims extra whitespace.
|
|
76
94
|
// Fallback safety net -- the SDK's activity.mentions.stripText should handle
|
|
@@ -125,6 +143,46 @@ function splitMessage(text, maxLen) {
|
|
|
125
143
|
}
|
|
126
144
|
return chunks;
|
|
127
145
|
}
|
|
146
|
+
// Sanitize user-provided feedback comments: truncate, strip control chars and newlines.
|
|
147
|
+
function sanitizeFeedbackComment(comment) {
|
|
148
|
+
const cleaned = comment.replace(/[\x00-\x1f\n\r]/g, "");
|
|
149
|
+
return cleaned.length > 200 ? cleaned.slice(0, 200) : cleaned;
|
|
150
|
+
}
|
|
151
|
+
// Build synthetic message text from a Teams feedback reaction.
|
|
152
|
+
function buildFeedbackSyntheticText(reaction, comment) {
|
|
153
|
+
const emoji = reaction === "like" ? "thumbs-up" : "thumbs-down";
|
|
154
|
+
if (comment) {
|
|
155
|
+
const sanitized = sanitizeFeedbackComment(comment);
|
|
156
|
+
return `[reacted with ${emoji} to your message: "${sanitized}"]`;
|
|
157
|
+
}
|
|
158
|
+
return `[reacted with ${emoji} to your message]`;
|
|
159
|
+
}
|
|
160
|
+
// Build a welcome Adaptive Card with prompt starters for new bot installs.
|
|
161
|
+
function buildWelcomeCard() {
|
|
162
|
+
const promptStarters = [
|
|
163
|
+
"What can you help me with?",
|
|
164
|
+
"Tell me about yourself",
|
|
165
|
+
"What's on my calendar today?",
|
|
166
|
+
"Summarize my recent emails",
|
|
167
|
+
];
|
|
168
|
+
return {
|
|
169
|
+
type: "AdaptiveCard",
|
|
170
|
+
version: "1.5",
|
|
171
|
+
body: [
|
|
172
|
+
{
|
|
173
|
+
type: "TextBlock",
|
|
174
|
+
text: "Hey! I'm here and ready to help. Try one of these to get started, or just ask me anything.",
|
|
175
|
+
wrap: true,
|
|
176
|
+
size: "Medium",
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
actions: promptStarters.map((prompt) => ({
|
|
180
|
+
type: "Action.Submit",
|
|
181
|
+
title: prompt,
|
|
182
|
+
data: { msteams: { type: "messageBack", text: prompt, displayText: prompt } },
|
|
183
|
+
})),
|
|
184
|
+
};
|
|
185
|
+
}
|
|
128
186
|
// Create Teams-specific callbacks for the agent loop.
|
|
129
187
|
// The SDK handles cumulative text, debouncing (500ms), and the streaming
|
|
130
188
|
// protocol (streamSequence, streamId, informative/streaming/final types).
|
|
@@ -137,12 +195,16 @@ function splitMessage(text, maxLen) {
|
|
|
137
195
|
// (transient status) or safeSend (terminal errors). Reasoning is accumulated
|
|
138
196
|
// and periodically pushed via safeUpdate on the same flush timer tick.
|
|
139
197
|
function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
198
|
+
const MIN_INITIAL_CHARS = 20;
|
|
140
199
|
let stopped = false; // set when stream signals cancellation (403)
|
|
141
200
|
let hadToolRun = false;
|
|
142
201
|
let hadRealOutput = false; // true once reasoning/tool output shown; suppresses phrases
|
|
143
202
|
let reasoningBuf = ""; // accumulated reasoning text for status display
|
|
203
|
+
let totalEmitted = 0; // cumulative chars emitted via safeEmit (for >4000 finalization)
|
|
204
|
+
let streamFinalized = false; // true after stream.close() — subsequent flushes go to safeSend
|
|
144
205
|
let textBuffer = ""; // accumulated text output for chunked streaming
|
|
145
206
|
let streamHasContent = false; // tracks whether primary output has received content
|
|
207
|
+
let firstContentEmitted = false; // true after first content push — disables MIN_INITIAL_CHARS threshold
|
|
146
208
|
let phraseTimer = null;
|
|
147
209
|
let lastPhrase = "";
|
|
148
210
|
let flushTimer = null;
|
|
@@ -192,15 +254,16 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
192
254
|
result.catch(() => markStopped());
|
|
193
255
|
}
|
|
194
256
|
}
|
|
195
|
-
// Safely emit a text delta to the stream.
|
|
257
|
+
// Safely emit a text delta to the stream with AI labels.
|
|
196
258
|
// On error (e.g. 403 from Teams stop button), abort the controller.
|
|
197
259
|
function safeEmit(text) {
|
|
198
260
|
/* v8 ignore next -- defensive guard: stopped set by prior 403; tested via flush abort path @preserve */
|
|
199
261
|
if (stopped)
|
|
200
262
|
return;
|
|
201
263
|
try {
|
|
202
|
-
catchAsync(stream.emit(text));
|
|
264
|
+
catchAsync(stream.emit({ text, entities: aiLabelEntities(), channelData: { feedbackLoopEnabled: true } }));
|
|
203
265
|
streamHasContent = true;
|
|
266
|
+
totalEmitted += text.length;
|
|
204
267
|
}
|
|
205
268
|
catch {
|
|
206
269
|
markStopped();
|
|
@@ -215,7 +278,7 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
215
278
|
try {
|
|
216
279
|
// stream.emit() is typed as void but the Teams SDK returns a Promise
|
|
217
280
|
// internally (async HTTP). Cast to capture the result for awaiting.
|
|
218
|
-
const result = stream.emit(text);
|
|
281
|
+
const result = stream.emit({ text, entities: aiLabelEntities(), channelData: { feedbackLoopEnabled: true } });
|
|
219
282
|
streamHasContent = true;
|
|
220
283
|
if (result && typeof result.then === "function") {
|
|
221
284
|
await result;
|
|
@@ -227,6 +290,26 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
227
290
|
return false;
|
|
228
291
|
}
|
|
229
292
|
}
|
|
293
|
+
// Non-aborting awaitable emit — returns true on success, false on failure WITHOUT
|
|
294
|
+
// calling markStopped() / aborting the controller. Used by flushNow (speak) so a
|
|
295
|
+
// primary-stream failure followed by a successful sendMessage fallback does NOT
|
|
296
|
+
// poison the rest of the turn. tryEmit's abort-on-failure behavior is correct for
|
|
297
|
+
// end-of-turn flush() (no fallback path forward) but wrong for mid-turn speak,
|
|
298
|
+
// which has a sendMessage fallback that may still succeed. Caller (flushNow) is
|
|
299
|
+
// responsible for the `!stopped` precondition; no defensive guard here.
|
|
300
|
+
async function tryEmitNoAbort(text) {
|
|
301
|
+
try {
|
|
302
|
+
const result = stream.emit({ text, entities: aiLabelEntities(), channelData: { feedbackLoopEnabled: true } });
|
|
303
|
+
streamHasContent = true;
|
|
304
|
+
if (result && typeof result.then === "function") {
|
|
305
|
+
await result;
|
|
306
|
+
}
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
catch {
|
|
310
|
+
return false;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
230
313
|
// Safely send a status update to the stream.
|
|
231
314
|
// On error (e.g. 403 from Teams stop button), abort the controller.
|
|
232
315
|
function safeUpdate(text) {
|
|
@@ -271,11 +354,49 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
271
354
|
// emitted text into a single streaming message (cumulative), so every
|
|
272
355
|
// periodic flush appends to the same response — not separate messages.
|
|
273
356
|
// No preemptive splitting — sends full text. Error recovery happens in flush().
|
|
357
|
+
// Hybrid MIN_INITIAL_CHARS: hold back until >= MIN_INITIAL_CHARS accumulated
|
|
358
|
+
// before the first content emit, so phrase rotation shows while real content
|
|
359
|
+
// buffers. After first emit, flush normally (no threshold).
|
|
274
360
|
function flushTextBuffer() {
|
|
275
361
|
if (!textBuffer)
|
|
276
362
|
return;
|
|
363
|
+
if (!firstContentEmitted && textBuffer.length < MIN_INITIAL_CHARS)
|
|
364
|
+
return;
|
|
365
|
+
// Proactive >4000 finalization: if cumulative emitted + buffer >= RECOVERY_CHUNK_SIZE,
|
|
366
|
+
// finalize the stream and send overflow via safeSend (follow-up message).
|
|
367
|
+
if (!streamFinalized && totalEmitted + textBuffer.length >= RECOVERY_CHUNK_SIZE) {
|
|
368
|
+
const remaining = RECOVERY_CHUNK_SIZE - totalEmitted;
|
|
369
|
+
/* v8 ignore next 2 -- defensive: remaining always > 0 because finalization runs once @preserve */
|
|
370
|
+
if (remaining > 0)
|
|
371
|
+
safeEmit(textBuffer.slice(0, remaining));
|
|
372
|
+
try {
|
|
373
|
+
stream.close();
|
|
374
|
+
}
|
|
375
|
+
catch { /* stream may already be dead */ }
|
|
376
|
+
streamFinalized = true;
|
|
377
|
+
/* v8 ignore next -- defensive ternary: remaining always > 0 at first finalization @preserve */
|
|
378
|
+
const overflow = textBuffer.slice(remaining > 0 ? remaining : 0);
|
|
379
|
+
textBuffer = "";
|
|
380
|
+
if (overflow)
|
|
381
|
+
safeSend(overflow);
|
|
382
|
+
if (!firstContentEmitted) {
|
|
383
|
+
firstContentEmitted = true;
|
|
384
|
+
stopPhraseRotation();
|
|
385
|
+
}
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
if (streamFinalized) {
|
|
389
|
+
// After finalization, all content goes to safeSend (follow-up messages)
|
|
390
|
+
safeSend(textBuffer);
|
|
391
|
+
textBuffer = "";
|
|
392
|
+
return;
|
|
393
|
+
}
|
|
277
394
|
safeEmit(textBuffer);
|
|
278
395
|
textBuffer = "";
|
|
396
|
+
if (!firstContentEmitted) {
|
|
397
|
+
firstContentEmitted = true;
|
|
398
|
+
stopPhraseRotation();
|
|
399
|
+
}
|
|
279
400
|
}
|
|
280
401
|
function startPhraseRotation(pool) {
|
|
281
402
|
stopPhraseRotation();
|
|
@@ -318,40 +439,108 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
318
439
|
onTextChunk: (text) => {
|
|
319
440
|
if (stopped)
|
|
320
441
|
return;
|
|
321
|
-
|
|
442
|
+
// Don't stop phrase rotation here — let it continue until first content
|
|
443
|
+
// emit (handled in flushTextBuffer when MIN_INITIAL_CHARS threshold met).
|
|
322
444
|
textBuffer += text;
|
|
323
445
|
startFlushTimer();
|
|
324
446
|
},
|
|
325
447
|
onClearText: () => {
|
|
326
448
|
textBuffer = "";
|
|
327
449
|
},
|
|
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) => {
|
|
450
|
+
flushNow: async () => {
|
|
451
|
+
const trimmed = textBuffer.trim();
|
|
452
|
+
if (!trimmed)
|
|
453
|
+
return;
|
|
454
|
+
// Cancel pending periodic flush — we're delivering now.
|
|
455
|
+
stopFlushTimer();
|
|
456
|
+
// The actual speak message replaces any "thinking..." phrase cycling.
|
|
347
457
|
stopPhraseRotation();
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
458
|
+
// Bypass MIN_INITIAL_CHARS threshold — speak delivers immediately.
|
|
459
|
+
firstContentEmitted = true;
|
|
460
|
+
textBuffer = "";
|
|
461
|
+
// Try the stream first via the NON-ABORTING variant; on failure, fall back
|
|
462
|
+
// to sendMessage. Critical: do NOT call markStopped() / abort the controller
|
|
463
|
+
// when only the primary stream fails — the sendMessage fallback may still
|
|
464
|
+
// deliver the speak, and a successful fallback must not poison the rest of
|
|
465
|
+
// the turn. Only abort when ALL delivery paths fail (handled below).
|
|
466
|
+
// Contract: throws if the message could not be delivered through any available path.
|
|
467
|
+
let delivered = false;
|
|
468
|
+
let lastError = null;
|
|
469
|
+
if (!stopped) {
|
|
470
|
+
const ok = await tryEmitNoAbort(trimmed);
|
|
471
|
+
if (ok)
|
|
472
|
+
delivered = true;
|
|
473
|
+
else
|
|
474
|
+
lastError = new Error("stream emit failed");
|
|
475
|
+
}
|
|
476
|
+
if (!delivered && sendMessage) {
|
|
477
|
+
try {
|
|
478
|
+
await sendMessage(trimmed);
|
|
479
|
+
delivered = true;
|
|
480
|
+
lastError = null;
|
|
481
|
+
}
|
|
482
|
+
catch (err) {
|
|
483
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
(0, runtime_1.emitNervesEvent)({
|
|
487
|
+
component: "senses",
|
|
488
|
+
event: "teams.speak_flush",
|
|
489
|
+
message: "teams flushed mid-turn speak",
|
|
490
|
+
meta: { messageLength: trimmed.length, delivered },
|
|
491
|
+
});
|
|
492
|
+
if (!delivered) {
|
|
493
|
+
// All delivery paths exhausted — now it is correct to abort the turn.
|
|
494
|
+
// markStopped() halts further stream activity and aborts the controller
|
|
495
|
+
// so the engine catches up and ends the turn cleanly.
|
|
496
|
+
markStopped();
|
|
497
|
+
throw new Error(`teams speak delivery failed: ${lastError?.message ?? "no fallback available"}`);
|
|
498
|
+
}
|
|
354
499
|
},
|
|
500
|
+
...(() => {
|
|
501
|
+
const toolCbs = (0, tool_activity_callbacks_1.createToolActivityCallbacks)({
|
|
502
|
+
onDescription: (text) => safeUpdate(text),
|
|
503
|
+
/* v8 ignore next -- onResult only called in debug mode; tested via tool-activity-callbacks.test.ts @preserve */
|
|
504
|
+
onResult: (text) => safeUpdate(text),
|
|
505
|
+
/* v8 ignore next -- onFailure tested via onToolEnd failure test @preserve */
|
|
506
|
+
onFailure: (text) => safeUpdate(text),
|
|
507
|
+
isDebug: commands_2.getDebugMode,
|
|
508
|
+
});
|
|
509
|
+
return {
|
|
510
|
+
onToolStart: (name, args) => {
|
|
511
|
+
// speak is flow-control: its visible output is the message itself
|
|
512
|
+
// (delivered via onTextChunk + flushNow). Do NOT stop phrase rotation
|
|
513
|
+
// here, do NOT emit the \u23f3 placeholder, do NOT post a tool-activity
|
|
514
|
+
// status update \u2014 all of those would create UI churn right before the
|
|
515
|
+
// actual speak content arrives.
|
|
516
|
+
if (name === "speak")
|
|
517
|
+
return;
|
|
518
|
+
stopPhraseRotation();
|
|
519
|
+
// Force-flush any accumulated text, bypassing MIN_INITIAL_CHARS threshold
|
|
520
|
+
firstContentEmitted = true;
|
|
521
|
+
flushTextBuffer();
|
|
522
|
+
// Emit a placeholder to satisfy the 15s Copilot timeout for initial
|
|
523
|
+
// stream.emit(). Without this, long tool chains (e.g. ADO batch ops)
|
|
524
|
+
// never emit before the timeout and the user sees "this response was
|
|
525
|
+
// stopped". The placeholder is replaced by actual content on next emit.
|
|
526
|
+
// https://learn.microsoft.com/en-us/answers/questions/2288017/m365-custom-engine-agents-timeout-message-after-15
|
|
527
|
+
if (!streamHasContent)
|
|
528
|
+
safeEmit("\u23f3");
|
|
529
|
+
toolCbs.onToolStart(name, args);
|
|
530
|
+
hadToolRun = true;
|
|
531
|
+
},
|
|
532
|
+
onToolEnd: (name, summary, success) => {
|
|
533
|
+
// speak is flow-control: skip phrase-rotation stop and tool-activity end
|
|
534
|
+
// callback (no safeUpdate for \u2713/\u2717). The flushNow call inside the engine
|
|
535
|
+
// already emitted the actual message and stopped any rotation as part of
|
|
536
|
+
// tryEmit's first-content-emitted flag.
|
|
537
|
+
if (name === "speak")
|
|
538
|
+
return;
|
|
539
|
+
stopPhraseRotation();
|
|
540
|
+
toolCbs.onToolEnd(name, summary, success);
|
|
541
|
+
},
|
|
542
|
+
};
|
|
543
|
+
})(),
|
|
355
544
|
onKick: () => {
|
|
356
545
|
stopPhraseRotation();
|
|
357
546
|
const msg = (0, format_1.formatKick)();
|
|
@@ -373,38 +562,27 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
373
562
|
safeSend(msg);
|
|
374
563
|
}
|
|
375
564
|
},
|
|
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
565
|
flush: async () => {
|
|
395
566
|
stopFlushTimer();
|
|
567
|
+
stopPhraseRotation();
|
|
396
568
|
if (textBuffer) {
|
|
569
|
+
// Bypass MIN_INITIAL_CHARS threshold — flush delivers all remaining content
|
|
570
|
+
firstContentEmitted = true;
|
|
397
571
|
const text = textBuffer;
|
|
398
572
|
textBuffer = "";
|
|
399
|
-
if (
|
|
573
|
+
if (streamFinalized && sendMessage) {
|
|
574
|
+
// Stream already finalized (>4000 path) — send remaining content as follow-up
|
|
575
|
+
safeSend(text);
|
|
576
|
+
}
|
|
577
|
+
else if (!stopped) {
|
|
400
578
|
// Stream is alive — await the emit so we can catch async 413/failure
|
|
401
579
|
// and fall through to sendMessage recovery.
|
|
402
580
|
const ok = await tryEmit(text);
|
|
403
581
|
if (!ok)
|
|
404
582
|
markStopped();
|
|
405
583
|
}
|
|
406
|
-
if (stopped && sendMessage) {
|
|
407
|
-
// Stream is dead — fall back to sendMessage; split on failure as recovery.
|
|
584
|
+
if (stopped && !streamFinalized && sendMessage) {
|
|
585
|
+
// Stream is dead (not from finalization) — fall back to sendMessage; split on failure as recovery.
|
|
408
586
|
try {
|
|
409
587
|
await sendMessage(text);
|
|
410
588
|
}
|
|
@@ -415,32 +593,12 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
415
593
|
}
|
|
416
594
|
}
|
|
417
595
|
}
|
|
418
|
-
else if (!streamHasContent) {
|
|
419
|
-
safeEmit("(completed with tool calls only
|
|
596
|
+
else if (!streamHasContent && !options?.suppressEmptyStreamMessage) {
|
|
597
|
+
safeEmit("(completed with tool calls only — no text response)");
|
|
420
598
|
}
|
|
421
599
|
},
|
|
422
600
|
};
|
|
423
601
|
}
|
|
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
602
|
const _turnCoordinator = (0, turn_coordinator_1.createTurnCoordinator)();
|
|
445
603
|
function teamsTurnKey(conversationId) {
|
|
446
604
|
return `teams:${conversationId}`;
|
|
@@ -459,6 +617,7 @@ function createTeamsCommandRegistry() {
|
|
|
459
617
|
(0, commands_1.registerDefaultCommands)(registry);
|
|
460
618
|
return registry;
|
|
461
619
|
}
|
|
620
|
+
/* v8 ignore start -- superseding follow-up slash command handler; tested via startTeamsApp integration tests @preserve */
|
|
462
621
|
function handleTeamsSlashCommand(text, registry, friendId, conversationId, stream, emitResponse = true) {
|
|
463
622
|
const parsed = (0, commands_1.parseSlashCommand)(text);
|
|
464
623
|
if (!parsed)
|
|
@@ -482,15 +641,19 @@ function handleTeamsSlashCommand(text, registry, friendId, conversationId, strea
|
|
|
482
641
|
}
|
|
483
642
|
return null;
|
|
484
643
|
}
|
|
644
|
+
/* v8 ignore stop */
|
|
485
645
|
// Handle an incoming Teams message
|
|
486
|
-
async function handleTeamsMessage(text, stream, conversationId, teamsContext, sendMessage) {
|
|
646
|
+
async function handleTeamsMessage(text, stream, conversationId, teamsContext, sendMessage, reactionOverrides) {
|
|
487
647
|
const turnKey = teamsTurnKey(conversationId);
|
|
488
648
|
// NOTE: Confirmation resolution is handled in the app.on("message") handler
|
|
489
649
|
// BEFORE the conversation lock. By the time we get here, any pending
|
|
490
650
|
// confirmation has already been resolved and the reply consumed.
|
|
491
651
|
// Send first thinking phrase immediately so the user sees feedback
|
|
492
652
|
// before sync I/O (session load, trim) blocks the event loop.
|
|
493
|
-
|
|
653
|
+
// Skip for reaction signals — they should be processed quietly.
|
|
654
|
+
if (!reactionOverrides) {
|
|
655
|
+
stream.update((0, phrases_1.pickPhrase)((0, phrases_1.getPhrases)().thinking) + "...");
|
|
656
|
+
}
|
|
494
657
|
await new Promise(r => setImmediate(r));
|
|
495
658
|
// Resolve identity provider early for friend resolution + slash command session path
|
|
496
659
|
const store = getFriendStore();
|
|
@@ -507,15 +670,10 @@ async function handleTeamsMessage(text, stream, conversationId, teamsContext, se
|
|
|
507
670
|
// Pre-resolve friend for session path + slash commands (pipeline will re-use the cached result)
|
|
508
671
|
const resolvedContext = await resolver.resolve();
|
|
509
672
|
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
673
|
// ── Teams adapter concerns: controller, callbacks, session path ──────────
|
|
516
674
|
const controller = new AbortController();
|
|
517
675
|
const channelConfig = (0, config_2.getTeamsChannelConfig)();
|
|
518
|
-
const callbacks = createTeamsCallbacks(stream, controller, sendMessage, { conversationId, flushIntervalMs: channelConfig.flushIntervalMs });
|
|
676
|
+
const callbacks = createTeamsCallbacks(stream, controller, sendMessage, { conversationId, flushIntervalMs: channelConfig.flushIntervalMs, ...(reactionOverrides?.suppressEmptyStreamMessage ? { suppressEmptyStreamMessage: true } : {}) });
|
|
519
677
|
const traceId = (0, nerves_1.createTraceId)();
|
|
520
678
|
const sessPath = (0, config_2.sessionPath)(friendId, "teams", conversationId);
|
|
521
679
|
const teamsCapabilities = (0, channel_1.getChannelCapabilities)("teams");
|
|
@@ -526,42 +684,68 @@ async function handleTeamsMessage(text, stream, conversationId, teamsContext, se
|
|
|
526
684
|
adoToken: teamsContext.adoToken,
|
|
527
685
|
githubToken: teamsContext.githubToken,
|
|
528
686
|
signin: teamsContext.signin,
|
|
529
|
-
summarize: (0, core_1.createSummarize)(),
|
|
687
|
+
summarize: (0, core_1.createSummarize)("human"),
|
|
530
688
|
tenantId: teamsContext.tenantId,
|
|
531
689
|
botApi: teamsContext.botApi,
|
|
532
690
|
} : {};
|
|
533
691
|
let currentText = text;
|
|
692
|
+
const mcpManager = await (0, mcp_manager_1.getSharedMcpManager)() ?? undefined;
|
|
534
693
|
while (true) {
|
|
535
694
|
let drainedSteeringFollowUps = [];
|
|
536
695
|
// Build runAgentOptions with Teams-specific fields
|
|
537
696
|
const agentOptions = {
|
|
538
697
|
traceId,
|
|
539
698
|
toolContext: teamsToolContext,
|
|
699
|
+
mcpManager,
|
|
540
700
|
drainSteeringFollowUps: () => {
|
|
541
701
|
drainedSteeringFollowUps = _turnCoordinator.drainFollowUps(turnKey)
|
|
542
702
|
.map(({ text: followUpText, effect }) => ({ text: followUpText, effect }));
|
|
543
703
|
return drainedSteeringFollowUps;
|
|
544
704
|
},
|
|
705
|
+
...(reactionOverrides?.isReactionSignal ? { isReactionSignal: true } : {}),
|
|
545
706
|
};
|
|
546
|
-
if (channelConfig.skipConfirmation)
|
|
547
|
-
agentOptions.skipConfirmation = true;
|
|
548
707
|
// ── Call shared pipeline ──────────────────────────────────────────
|
|
708
|
+
// Capture terminal errors — failover message replaces the error card if it triggers
|
|
709
|
+
let capturedTerminalError = null;
|
|
710
|
+
const teamsFailoverState = (() => {
|
|
711
|
+
if (!teamsFailoverStates.has(conversationId)) {
|
|
712
|
+
teamsFailoverStates.set(conversationId, { pending: null });
|
|
713
|
+
}
|
|
714
|
+
return teamsFailoverStates.get(conversationId);
|
|
715
|
+
})();
|
|
716
|
+
/* v8 ignore start -- failover-aware callback wrapper: tested via pipeline integration @preserve */
|
|
717
|
+
const failoverAwareCallbacks = {
|
|
718
|
+
...callbacks,
|
|
719
|
+
onError: (error, severity) => {
|
|
720
|
+
if (severity === "terminal" && teamsFailoverState) {
|
|
721
|
+
capturedTerminalError = error;
|
|
722
|
+
return;
|
|
723
|
+
}
|
|
724
|
+
callbacks.onError(error, severity);
|
|
725
|
+
},
|
|
726
|
+
};
|
|
727
|
+
/* v8 ignore stop */
|
|
549
728
|
const result = await (0, pipeline_1.handleInboundTurn)({
|
|
550
729
|
channel: "teams",
|
|
551
730
|
sessionKey: conversationId,
|
|
552
731
|
capabilities: teamsCapabilities,
|
|
553
732
|
messages: [{ role: "user", content: currentText }],
|
|
554
733
|
continuityIngressTexts: [currentText],
|
|
555
|
-
callbacks,
|
|
734
|
+
callbacks: failoverAwareCallbacks,
|
|
556
735
|
friendResolver: { resolve: () => Promise.resolve(resolvedContext) },
|
|
557
736
|
sessionLoader: {
|
|
558
737
|
loadOrCreate: async () => {
|
|
559
738
|
const existing = (0, context_1.loadSession)(sessPath);
|
|
560
739
|
const messages = existing?.messages && existing.messages.length > 0
|
|
561
740
|
? existing.messages
|
|
562
|
-
: [{ role: "system", content: await (0, prompt_1.buildSystem)("teams",
|
|
741
|
+
: [{ role: "system", content: (0, prompt_1.flattenSystemPrompt)(await (0, prompt_1.buildSystem)("teams", {}, resolvedContext)) }];
|
|
563
742
|
(0, core_1.repairOrphanedToolCalls)(messages);
|
|
564
|
-
return {
|
|
743
|
+
return {
|
|
744
|
+
messages,
|
|
745
|
+
sessionPath: sessPath,
|
|
746
|
+
state: existing?.state,
|
|
747
|
+
events: existing?.events,
|
|
748
|
+
};
|
|
565
749
|
},
|
|
566
750
|
},
|
|
567
751
|
pendingDir,
|
|
@@ -584,11 +768,33 @@ async function handleTeamsMessage(text, stream, conversationId, teamsContext, se
|
|
|
584
768
|
summarize: teamsToolContext.summarize,
|
|
585
769
|
},
|
|
586
770
|
}),
|
|
587
|
-
postTurn:
|
|
771
|
+
postTurn: (turnMessages, sessionPathArg, usage, hooks, state) => {
|
|
772
|
+
const prepared = (0, context_1.postTurnTrim)(turnMessages, usage, hooks);
|
|
773
|
+
(0, context_1.deferPostTurnPersist)(sessionPathArg, prepared, usage, state);
|
|
774
|
+
},
|
|
588
775
|
accumulateFriendTokens: tokens_1.accumulateFriendTokens,
|
|
589
776
|
signal: controller.signal,
|
|
590
777
|
runAgentOptions: agentOptions,
|
|
778
|
+
failoverState: teamsFailoverState,
|
|
591
779
|
});
|
|
780
|
+
// ── Handle pipeline-intercepted commands ────────────────────────
|
|
781
|
+
if (result.turnOutcome === "command") {
|
|
782
|
+
if (result.commandAction === "new") {
|
|
783
|
+
(0, context_1.deleteSession)(sessPath);
|
|
784
|
+
stream.emit("session cleared");
|
|
785
|
+
}
|
|
786
|
+
// For "response" commands: pipeline already emitted the response via onTextChunk
|
|
787
|
+
await callbacks.flush();
|
|
788
|
+
return;
|
|
789
|
+
}
|
|
790
|
+
/* v8 ignore start -- failover display: tested via pipeline integration tests @preserve */
|
|
791
|
+
if (result.failoverMessage) {
|
|
792
|
+
stream.emit(result.failoverMessage);
|
|
793
|
+
}
|
|
794
|
+
else if (capturedTerminalError) {
|
|
795
|
+
callbacks.onError(capturedTerminalError, "terminal");
|
|
796
|
+
}
|
|
797
|
+
/* v8 ignore stop */
|
|
592
798
|
// ── Handle gate result ────────────────────────────────────────
|
|
593
799
|
if (!result.gateResult.allowed) {
|
|
594
800
|
if ("autoReply" in result.gateResult && result.gateResult.autoReply) {
|
|
@@ -628,7 +834,7 @@ async function handleTeamsMessage(text, stream, conversationId, teamsContext, se
|
|
|
628
834
|
currentText = replayTail;
|
|
629
835
|
continue;
|
|
630
836
|
}
|
|
631
|
-
if (handleTeamsSlashCommand(supersedingFollowUp.text,
|
|
837
|
+
if (handleTeamsSlashCommand(supersedingFollowUp.text, createTeamsCommandRegistry(), friendId, conversationId, stream, false)) {
|
|
632
838
|
return;
|
|
633
839
|
}
|
|
634
840
|
currentText = supersedingFollowUp.text;
|
|
@@ -708,7 +914,7 @@ function registerBotHandlers(app, label) {
|
|
|
708
914
|
// (graph + ado + github). The verifyState activity only carries a `state`
|
|
709
915
|
// code with no connectionName, so we try each configured connection until
|
|
710
916
|
// one succeeds.
|
|
711
|
-
app.on("signin.verify-state", async (ctx) => {
|
|
917
|
+
app.on("signin.verify-state", (async (ctx) => {
|
|
712
918
|
const { api, activity } = ctx;
|
|
713
919
|
if (!activity.value?.state)
|
|
714
920
|
return { status: 404 };
|
|
@@ -727,7 +933,73 @@ function registerBotHandlers(app, label) {
|
|
|
727
933
|
}
|
|
728
934
|
(0, runtime_1.emitNervesEvent)({ level: "warn", event: "channel.verify_state", component: "channels", message: `[${label}] verify-state failed for all connections`, meta: {} });
|
|
729
935
|
return { status: 412 };
|
|
936
|
+
}));
|
|
937
|
+
// Handle Teams feedback reactions (thumbs up/down on AI-generated messages).
|
|
938
|
+
// SDK routes message/submitAction with actionName "feedback" to this event.
|
|
939
|
+
/* v8 ignore start -- Teams SDK invoke handler; requires live SDK context @preserve */
|
|
940
|
+
app.on("message.submit.feedback", async (ctx) => {
|
|
941
|
+
const { stream, activity } = ctx;
|
|
942
|
+
const reaction = activity.value?.actionValue?.reaction;
|
|
943
|
+
const comment = activity.value?.actionValue?.feedback;
|
|
944
|
+
const convId = activity.conversation?.id || "unknown";
|
|
945
|
+
const turnKey = teamsTurnKey(convId);
|
|
946
|
+
// Validate payload — graceful no-op for malformed invocations
|
|
947
|
+
if (activity.value?.actionName !== "feedback" || !reaction) {
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
const syntheticText = buildFeedbackSyntheticText(reaction, comment);
|
|
951
|
+
// Turn coordination: if a turn is active, enqueue as steering follow-up
|
|
952
|
+
if (!_turnCoordinator.tryBeginTurn(turnKey)) {
|
|
953
|
+
_turnCoordinator.enqueueFollowUp(turnKey, {
|
|
954
|
+
conversationId: convId,
|
|
955
|
+
text: syntheticText,
|
|
956
|
+
receivedAt: Date.now(),
|
|
957
|
+
effect: (0, continuity_1.classifySteeringFollowUpEffect)(syntheticText),
|
|
958
|
+
});
|
|
959
|
+
return;
|
|
960
|
+
}
|
|
961
|
+
try {
|
|
962
|
+
const teamsContext = {
|
|
963
|
+
signin: async () => undefined,
|
|
964
|
+
aadObjectId: activity.from?.aadObjectId,
|
|
965
|
+
tenantId: activity.conversation?.tenantId,
|
|
966
|
+
displayName: activity.from?.name,
|
|
967
|
+
};
|
|
968
|
+
const ctxSend = async (t) => {
|
|
969
|
+
await ctx.send({ type: "message", text: t, replyToId: activity.replyToId, entities: aiLabelEntities(), channelData: { feedbackLoopEnabled: true } });
|
|
970
|
+
};
|
|
971
|
+
await handleTeamsMessage(syntheticText, stream, convId, teamsContext, ctxSend, { isReactionSignal: true, suppressEmptyStreamMessage: true });
|
|
972
|
+
}
|
|
973
|
+
catch (err) {
|
|
974
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
975
|
+
(0, runtime_1.emitNervesEvent)({ level: "error", event: "channel.feedback_handler_error", component: "channels", message: msg.slice(0, 200), meta: {} });
|
|
976
|
+
}
|
|
977
|
+
finally {
|
|
978
|
+
_turnCoordinator.endTurn(turnKey);
|
|
979
|
+
}
|
|
980
|
+
});
|
|
981
|
+
/* v8 ignore stop */
|
|
982
|
+
// Handle bot install — send welcome Adaptive Card with prompt starters.
|
|
983
|
+
/* v8 ignore start -- Teams SDK install handler; requires live SDK context @preserve */
|
|
984
|
+
app.on("install.add", async (ctx) => {
|
|
985
|
+
try {
|
|
986
|
+
const card = buildWelcomeCard();
|
|
987
|
+
await ctx.send({
|
|
988
|
+
type: "message",
|
|
989
|
+
attachments: [{
|
|
990
|
+
contentType: "application/vnd.microsoft.card.adaptive",
|
|
991
|
+
content: card,
|
|
992
|
+
}],
|
|
993
|
+
entities: aiLabelEntities(),
|
|
994
|
+
channelData: { feedbackLoopEnabled: true },
|
|
995
|
+
});
|
|
996
|
+
}
|
|
997
|
+
catch (err) {
|
|
998
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
999
|
+
(0, runtime_1.emitNervesEvent)({ level: "error", event: "channel.welcome_handler_error", component: "channels", message: msg.slice(0, 200), meta: {} });
|
|
1000
|
+
}
|
|
730
1001
|
});
|
|
1002
|
+
/* v8 ignore stop */
|
|
731
1003
|
app.on("message", async (ctx) => {
|
|
732
1004
|
const { stream, activity, api, signin } = ctx;
|
|
733
1005
|
const text = activity.text || "";
|
|
@@ -736,13 +1008,6 @@ function registerBotHandlers(app, label) {
|
|
|
736
1008
|
const userId = activity.from?.id || "";
|
|
737
1009
|
const channelId = activity.channelId || "msteams";
|
|
738
1010
|
(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
1011
|
const commandRegistry = createTeamsCommandRegistry();
|
|
747
1012
|
const parsedSlashCommand = (0, commands_1.parseSlashCommand)(text);
|
|
748
1013
|
if (parsedSlashCommand) {
|
|
@@ -843,7 +1108,7 @@ function registerBotHandlers(app, label) {
|
|
|
843
1108
|
const ctxSend = async (t) => {
|
|
844
1109
|
// Use send with replyToId (not reply, which adds a blockquote).
|
|
845
1110
|
// replyToId anchors the message after the user's message in Copilot Chat.
|
|
846
|
-
await ctx.send({ type: "message", text: t, replyToId: activity.id });
|
|
1111
|
+
await ctx.send({ type: "message", text: t, replyToId: activity.id, entities: aiLabelEntities(), channelData: { feedbackLoopEnabled: true } });
|
|
847
1112
|
};
|
|
848
1113
|
await handleTeamsMessage(text, stream, convId, teamsContext, ctxSend);
|
|
849
1114
|
}
|
|
@@ -925,6 +1190,16 @@ async function sendProactiveTeamsMessageToSession(params, deps) {
|
|
|
925
1190
|
});
|
|
926
1191
|
return { delivered: false, reason: "missing_target" };
|
|
927
1192
|
}
|
|
1193
|
+
const internalContentBlockReason = (0, proactive_content_guard_1.getProactiveInternalContentBlockReason)(params.text);
|
|
1194
|
+
if (internalContentBlockReason) {
|
|
1195
|
+
(0, proactive_content_guard_1.emitProactiveInternalContentBlocked)({
|
|
1196
|
+
friendId: params.friendId,
|
|
1197
|
+
sessionKey: params.sessionKey,
|
|
1198
|
+
reason: internalContentBlockReason,
|
|
1199
|
+
source: "session_send",
|
|
1200
|
+
});
|
|
1201
|
+
return { delivered: false, reason: "internal_content_blocked" };
|
|
1202
|
+
}
|
|
928
1203
|
try {
|
|
929
1204
|
const conversation = await conversations.create({
|
|
930
1205
|
bot: { id: deps.botApi.id },
|
|
@@ -1027,6 +1302,20 @@ async function drainAndSendPendingTeams(store, botApi, pendingRoot) {
|
|
|
1027
1302
|
catch { /* ignore */ }
|
|
1028
1303
|
continue;
|
|
1029
1304
|
}
|
|
1305
|
+
const internalBlockReason = (0, proactive_content_guard_1.getProactiveInternalContentBlockReason)(messageText);
|
|
1306
|
+
if (internalBlockReason) {
|
|
1307
|
+
result.skipped++;
|
|
1308
|
+
try {
|
|
1309
|
+
fs.unlinkSync(filePath);
|
|
1310
|
+
}
|
|
1311
|
+
catch { /* ignore */ }
|
|
1312
|
+
(0, proactive_content_guard_1.emitProactiveInternalContentBlocked)({
|
|
1313
|
+
friendId,
|
|
1314
|
+
reason: internalBlockReason,
|
|
1315
|
+
source: "pending_drain",
|
|
1316
|
+
});
|
|
1317
|
+
continue;
|
|
1318
|
+
}
|
|
1030
1319
|
const sendResult = await sendProactiveTeamsMessageToSession({
|
|
1031
1320
|
friendId,
|
|
1032
1321
|
sessionKey: key,
|