@ouro.bot/cli 0.1.0-alpha.50 → 0.1.0-alpha.501
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/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +3 -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 +3184 -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 +867 -35
- 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 +426 -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 +119 -129
- package/dist/heart/core.js +898 -244
- package/dist/heart/cross-chat-delivery.js +131 -0
- package/dist/heart/daemon/agent-config-check.js +490 -0
- package/dist/heart/daemon/agent-discovery.js +79 -3
- package/dist/heart/daemon/agent-service.js +360 -0
- package/dist/heart/daemon/agentic-repair.js +216 -0
- package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
- package/dist/heart/daemon/cadence.js +70 -0
- package/dist/heart/daemon/cli-defaults.js +640 -0
- package/dist/heart/daemon/cli-exec.js +7239 -0
- package/dist/heart/daemon/cli-help.js +493 -0
- package/dist/heart/daemon/cli-parse.js +1533 -0
- package/dist/heart/daemon/cli-render-doctor.js +57 -0
- package/dist/heart/daemon/cli-render.js +561 -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 -1631
- package/dist/heart/daemon/daemon-entry.js +345 -3
- package/dist/heart/daemon/daemon-health.js +141 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +190 -12
- package/dist/heart/daemon/daemon-tombstone.js +236 -0
- package/dist/heart/daemon/daemon.js +677 -58
- package/dist/heart/daemon/dns-workflow.js +394 -0
- package/dist/heart/daemon/doctor-types.js +8 -0
- package/dist/heart/daemon/doctor.js +615 -0
- package/dist/heart/daemon/health-monitor.js +92 -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 +89 -0
- package/dist/heart/daemon/interactive-repair.js +394 -0
- package/dist/heart/daemon/launchd.js +25 -5
- package/dist/heart/daemon/log-tailer.js +82 -12
- package/dist/heart/daemon/logs-prune.js +110 -0
- package/dist/heart/daemon/message-router.js +2 -2
- package/dist/heart/daemon/os-cron-deps.js +134 -0
- 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 +214 -0
- 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 +73 -0
- package/dist/heart/daemon/runtime-mode.js +67 -0
- package/dist/heart/daemon/safe-mode.js +161 -0
- package/dist/heart/daemon/sense-manager.js +178 -37
- 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 +109 -4
- package/dist/heart/daemon/stale-bundle-prune.js +96 -0
- package/dist/heart/daemon/startup-tui.js +264 -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 +3 -3
- 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 +201 -66
- 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/mcp/mcp-server.js +653 -0
- package/dist/heart/migrate-config.js +100 -0
- package/dist/heart/model-capabilities.js +59 -0
- package/dist/heart/outlook/outlook-http-hooks.js +66 -0
- package/dist/heart/outlook/outlook-http-response.js +7 -0
- package/dist/heart/outlook/outlook-http-routes.js +244 -0
- package/dist/heart/outlook/outlook-http-static.js +103 -0
- package/dist/heart/outlook/outlook-http-transport.js +116 -0
- package/dist/heart/outlook/outlook-http.js +99 -0
- package/dist/heart/outlook/outlook-read.js +31 -0
- package/dist/heart/outlook/outlook-types.js +27 -0
- package/dist/heart/outlook/outlook-view.js +195 -0
- package/dist/heart/outlook/readers/agent-machine.js +382 -0
- package/dist/heart/outlook/readers/continuity-readers.js +336 -0
- package/dist/heart/outlook/readers/mail.js +362 -0
- package/dist/heart/outlook/readers/runtime-readers.js +644 -0
- package/dist/heart/outlook/readers/sessions.js +232 -0
- package/dist/heart/outlook/readers/shared.js +111 -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 +424 -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 +193 -55
- package/dist/heart/providers/azure.js +104 -13
- package/dist/heart/providers/error-classification.js +63 -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 +29 -7
- package/dist/heart/providers/openai-codex.js +63 -39
- package/dist/heart/runtime-capability-check.js +170 -0
- package/dist/heart/runtime-credentials.js +260 -0
- package/dist/heart/sense-truth.js +11 -4
- package/dist/heart/session-activity.js +43 -22
- package/dist/heart/session-events.js +1089 -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-transcript.js +167 -0
- package/dist/heart/start-of-turn-packet.js +345 -0
- package/dist/heart/streaming.js +48 -28
- package/dist/heart/sync.js +332 -0
- package/dist/heart/target-resolution.js +127 -0
- package/dist/heart/tempo.js +93 -0
- package/dist/heart/temporal-view.js +41 -0
- package/dist/heart/tool-activity-callbacks.js +36 -0
- package/dist/heart/tool-description.js +135 -0
- package/dist/heart/tool-friction.js +55 -0
- package/dist/heart/tool-loop.js +200 -0
- package/dist/heart/turn-context.js +372 -0
- package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +1 -1
- package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +1 -1
- package/dist/heart/versioning/ouro-path-installer.js +425 -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 +5 -1
- package/dist/heart/{daemon → versioning}/update-hooks.js +63 -59
- package/dist/mailroom/attention.js +167 -0
- package/dist/mailroom/autonomy.js +209 -0
- package/dist/mailroom/blob-store.js +606 -0
- package/dist/mailroom/core.js +672 -0
- package/dist/mailroom/entry.js +160 -0
- package/dist/mailroom/file-store.js +426 -0
- package/dist/mailroom/mbox-import.js +382 -0
- package/dist/mailroom/outbound.js +380 -0
- package/dist/mailroom/policy.js +263 -0
- package/dist/mailroom/reader.js +219 -0
- package/dist/mailroom/search-cache.js +182 -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 +164 -101
- package/dist/mind/diary-integrity.js +60 -0
- package/dist/mind/{memory.js → diary.js} +74 -93
- 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/group-context.js +144 -0
- package/dist/mind/friends/resolver.js +54 -2
- package/dist/mind/friends/store-file.js +39 -3
- package/dist/mind/friends/trust-explanation.js +74 -0
- 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 +948 -110
- 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 +106 -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/outlook-ui/assets/index-BPr5vNuM.css +1 -0
- package/dist/outlook-ui/assets/index-Cm51CY9W.js +61 -0
- package/dist/outlook-ui/index.html +15 -0
- 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 +774 -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 +255 -0
- package/dist/repertoire/mcp-manager.js +305 -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 +46 -921
- package/dist/repertoire/tools-bluebubbles.js +1 -0
- package/dist/repertoire/tools-bridge.js +141 -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 +105 -0
- package/dist/repertoire/tools-github.js +1 -7
- package/dist/repertoire/tools-mail.js +1377 -0
- package/dist/repertoire/tools-notes.js +376 -0
- package/dist/repertoire/tools-session.js +749 -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 +356 -0
- package/dist/repertoire/tools-user-profile.js +144 -0
- package/dist/repertoire/tools-vault.js +40 -0
- package/dist/repertoire/tools.js +144 -115
- 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/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 +73 -0
- package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +20 -3
- package/dist/senses/bluebubbles/index.js +1881 -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 +111 -0
- package/dist/senses/bluebubbles/replay.js +129 -0
- package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +2 -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 +605 -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 +83 -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 +511 -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 +573 -164
- 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 +405 -170
- 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 +39 -8
- 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 -1142
- 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,12 +34,16 @@ 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;
|
|
46
|
+
exports.sendProactiveTeamsMessageToSession = sendProactiveTeamsMessageToSession;
|
|
43
47
|
exports.drainAndSendPendingTeams = drainAndSendPendingTeams;
|
|
44
48
|
exports.startTeamsApp = startTeamsApp;
|
|
45
49
|
const fs = __importStar(require("fs"));
|
|
@@ -57,19 +61,34 @@ const context_1 = require("../mind/context");
|
|
|
57
61
|
const commands_1 = require("./commands");
|
|
58
62
|
const nerves_1 = require("../nerves");
|
|
59
63
|
const runtime_1 = require("../nerves/runtime");
|
|
64
|
+
const proactive_content_guard_1 = require("./proactive-content-guard");
|
|
60
65
|
const store_file_1 = require("../mind/friends/store-file");
|
|
61
66
|
const types_1 = require("../mind/friends/types");
|
|
62
67
|
const resolver_1 = require("../mind/friends/resolver");
|
|
63
68
|
const tokens_1 = require("../mind/friends/tokens");
|
|
64
69
|
const turn_coordinator_1 = require("../heart/turn-coordinator");
|
|
65
70
|
const identity_1 = require("../heart/identity");
|
|
71
|
+
const mcp_manager_1 = require("../repertoire/mcp-manager");
|
|
66
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");
|
|
67
75
|
const http = __importStar(require("http"));
|
|
68
76
|
const path = __importStar(require("path"));
|
|
69
77
|
const trust_gate_1 = require("./trust-gate");
|
|
70
78
|
const pipeline_1 = require("./pipeline");
|
|
79
|
+
const teamsFailoverStates = new Map();
|
|
71
80
|
const pending_1 = require("../mind/pending");
|
|
72
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
|
+
}
|
|
73
92
|
// Strip @mention markup from incoming messages.
|
|
74
93
|
// Removes <at>...</at> tags and trims extra whitespace.
|
|
75
94
|
// Fallback safety net -- the SDK's activity.mentions.stripText should handle
|
|
@@ -124,6 +143,46 @@ function splitMessage(text, maxLen) {
|
|
|
124
143
|
}
|
|
125
144
|
return chunks;
|
|
126
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
|
+
}
|
|
127
186
|
// Create Teams-specific callbacks for the agent loop.
|
|
128
187
|
// The SDK handles cumulative text, debouncing (500ms), and the streaming
|
|
129
188
|
// protocol (streamSequence, streamId, informative/streaming/final types).
|
|
@@ -136,12 +195,16 @@ function splitMessage(text, maxLen) {
|
|
|
136
195
|
// (transient status) or safeSend (terminal errors). Reasoning is accumulated
|
|
137
196
|
// and periodically pushed via safeUpdate on the same flush timer tick.
|
|
138
197
|
function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
198
|
+
const MIN_INITIAL_CHARS = 20;
|
|
139
199
|
let stopped = false; // set when stream signals cancellation (403)
|
|
140
200
|
let hadToolRun = false;
|
|
141
201
|
let hadRealOutput = false; // true once reasoning/tool output shown; suppresses phrases
|
|
142
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
|
|
143
205
|
let textBuffer = ""; // accumulated text output for chunked streaming
|
|
144
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
|
|
145
208
|
let phraseTimer = null;
|
|
146
209
|
let lastPhrase = "";
|
|
147
210
|
let flushTimer = null;
|
|
@@ -191,15 +254,16 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
191
254
|
result.catch(() => markStopped());
|
|
192
255
|
}
|
|
193
256
|
}
|
|
194
|
-
// Safely emit a text delta to the stream.
|
|
257
|
+
// Safely emit a text delta to the stream with AI labels.
|
|
195
258
|
// On error (e.g. 403 from Teams stop button), abort the controller.
|
|
196
259
|
function safeEmit(text) {
|
|
197
260
|
/* v8 ignore next -- defensive guard: stopped set by prior 403; tested via flush abort path @preserve */
|
|
198
261
|
if (stopped)
|
|
199
262
|
return;
|
|
200
263
|
try {
|
|
201
|
-
catchAsync(stream.emit(text));
|
|
264
|
+
catchAsync(stream.emit({ text, entities: aiLabelEntities(), channelData: { feedbackLoopEnabled: true } }));
|
|
202
265
|
streamHasContent = true;
|
|
266
|
+
totalEmitted += text.length;
|
|
203
267
|
}
|
|
204
268
|
catch {
|
|
205
269
|
markStopped();
|
|
@@ -214,7 +278,7 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
214
278
|
try {
|
|
215
279
|
// stream.emit() is typed as void but the Teams SDK returns a Promise
|
|
216
280
|
// internally (async HTTP). Cast to capture the result for awaiting.
|
|
217
|
-
const result = stream.emit(text);
|
|
281
|
+
const result = stream.emit({ text, entities: aiLabelEntities(), channelData: { feedbackLoopEnabled: true } });
|
|
218
282
|
streamHasContent = true;
|
|
219
283
|
if (result && typeof result.then === "function") {
|
|
220
284
|
await result;
|
|
@@ -270,11 +334,49 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
270
334
|
// emitted text into a single streaming message (cumulative), so every
|
|
271
335
|
// periodic flush appends to the same response — not separate messages.
|
|
272
336
|
// No preemptive splitting — sends full text. Error recovery happens in flush().
|
|
337
|
+
// Hybrid MIN_INITIAL_CHARS: hold back until >= MIN_INITIAL_CHARS accumulated
|
|
338
|
+
// before the first content emit, so phrase rotation shows while real content
|
|
339
|
+
// buffers. After first emit, flush normally (no threshold).
|
|
273
340
|
function flushTextBuffer() {
|
|
274
341
|
if (!textBuffer)
|
|
275
342
|
return;
|
|
343
|
+
if (!firstContentEmitted && textBuffer.length < MIN_INITIAL_CHARS)
|
|
344
|
+
return;
|
|
345
|
+
// Proactive >4000 finalization: if cumulative emitted + buffer >= RECOVERY_CHUNK_SIZE,
|
|
346
|
+
// finalize the stream and send overflow via safeSend (follow-up message).
|
|
347
|
+
if (!streamFinalized && totalEmitted + textBuffer.length >= RECOVERY_CHUNK_SIZE) {
|
|
348
|
+
const remaining = RECOVERY_CHUNK_SIZE - totalEmitted;
|
|
349
|
+
/* v8 ignore next 2 -- defensive: remaining always > 0 because finalization runs once @preserve */
|
|
350
|
+
if (remaining > 0)
|
|
351
|
+
safeEmit(textBuffer.slice(0, remaining));
|
|
352
|
+
try {
|
|
353
|
+
stream.close();
|
|
354
|
+
}
|
|
355
|
+
catch { /* stream may already be dead */ }
|
|
356
|
+
streamFinalized = true;
|
|
357
|
+
/* v8 ignore next -- defensive ternary: remaining always > 0 at first finalization @preserve */
|
|
358
|
+
const overflow = textBuffer.slice(remaining > 0 ? remaining : 0);
|
|
359
|
+
textBuffer = "";
|
|
360
|
+
if (overflow)
|
|
361
|
+
safeSend(overflow);
|
|
362
|
+
if (!firstContentEmitted) {
|
|
363
|
+
firstContentEmitted = true;
|
|
364
|
+
stopPhraseRotation();
|
|
365
|
+
}
|
|
366
|
+
return;
|
|
367
|
+
}
|
|
368
|
+
if (streamFinalized) {
|
|
369
|
+
// After finalization, all content goes to safeSend (follow-up messages)
|
|
370
|
+
safeSend(textBuffer);
|
|
371
|
+
textBuffer = "";
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
276
374
|
safeEmit(textBuffer);
|
|
277
375
|
textBuffer = "";
|
|
376
|
+
if (!firstContentEmitted) {
|
|
377
|
+
firstContentEmitted = true;
|
|
378
|
+
stopPhraseRotation();
|
|
379
|
+
}
|
|
278
380
|
}
|
|
279
381
|
function startPhraseRotation(pool) {
|
|
280
382
|
stopPhraseRotation();
|
|
@@ -317,40 +419,45 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
317
419
|
onTextChunk: (text) => {
|
|
318
420
|
if (stopped)
|
|
319
421
|
return;
|
|
320
|
-
|
|
422
|
+
// Don't stop phrase rotation here — let it continue until first content
|
|
423
|
+
// emit (handled in flushTextBuffer when MIN_INITIAL_CHARS threshold met).
|
|
321
424
|
textBuffer += text;
|
|
322
425
|
startFlushTimer();
|
|
323
426
|
},
|
|
324
427
|
onClearText: () => {
|
|
325
428
|
textBuffer = "";
|
|
326
429
|
},
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
430
|
+
...(() => {
|
|
431
|
+
const toolCbs = (0, tool_activity_callbacks_1.createToolActivityCallbacks)({
|
|
432
|
+
onDescription: (text) => safeUpdate(text),
|
|
433
|
+
/* v8 ignore next -- onResult only called in debug mode; tested via tool-activity-callbacks.test.ts @preserve */
|
|
434
|
+
onResult: (text) => safeUpdate(text),
|
|
435
|
+
/* v8 ignore next -- onFailure tested via onToolEnd failure test @preserve */
|
|
436
|
+
onFailure: (text) => safeUpdate(text),
|
|
437
|
+
isDebug: commands_2.getDebugMode,
|
|
438
|
+
});
|
|
439
|
+
return {
|
|
440
|
+
onToolStart: (name, args) => {
|
|
441
|
+
stopPhraseRotation();
|
|
442
|
+
// Force-flush any accumulated text, bypassing MIN_INITIAL_CHARS threshold
|
|
443
|
+
firstContentEmitted = true;
|
|
444
|
+
flushTextBuffer();
|
|
445
|
+
// Emit a placeholder to satisfy the 15s Copilot timeout for initial
|
|
446
|
+
// stream.emit(). Without this, long tool chains (e.g. ADO batch ops)
|
|
447
|
+
// never emit before the timeout and the user sees "this response was
|
|
448
|
+
// stopped". The placeholder is replaced by actual content on next emit.
|
|
449
|
+
// https://learn.microsoft.com/en-us/answers/questions/2288017/m365-custom-engine-agents-timeout-message-after-15
|
|
450
|
+
if (!streamHasContent)
|
|
451
|
+
safeEmit("\u23f3");
|
|
452
|
+
toolCbs.onToolStart(name, args);
|
|
453
|
+
hadToolRun = true;
|
|
454
|
+
},
|
|
455
|
+
onToolEnd: (name, summary, success) => {
|
|
456
|
+
stopPhraseRotation();
|
|
457
|
+
toolCbs.onToolEnd(name, summary, success);
|
|
458
|
+
},
|
|
459
|
+
};
|
|
460
|
+
})(),
|
|
354
461
|
onKick: () => {
|
|
355
462
|
stopPhraseRotation();
|
|
356
463
|
const msg = (0, format_1.formatKick)();
|
|
@@ -372,38 +479,27 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
372
479
|
safeSend(msg);
|
|
373
480
|
}
|
|
374
481
|
},
|
|
375
|
-
onConfirmAction: options?.conversationId
|
|
376
|
-
? async (name, args) => {
|
|
377
|
-
const convId = options.conversationId;
|
|
378
|
-
const argsDesc = Object.entries(args).map(([k, v]) => `${k}: ${v}`).join(", ");
|
|
379
|
-
safeUpdate(`Confirm action: ${name} (${argsDesc}) -- reply "yes" to confirm or "no" to cancel`);
|
|
380
|
-
return new Promise((resolve) => {
|
|
381
|
-
_pendingConfirmations.set(convId, resolve);
|
|
382
|
-
// Auto-deny after 2 minutes to prevent indefinite blocking
|
|
383
|
-
// (e.g. when the stream dies and the user never sees the prompt).
|
|
384
|
-
setTimeout(() => {
|
|
385
|
-
if (_pendingConfirmations.has(convId)) {
|
|
386
|
-
_pendingConfirmations.delete(convId);
|
|
387
|
-
resolve("denied");
|
|
388
|
-
}
|
|
389
|
-
}, 120_000);
|
|
390
|
-
});
|
|
391
|
-
}
|
|
392
|
-
: undefined,
|
|
393
482
|
flush: async () => {
|
|
394
483
|
stopFlushTimer();
|
|
484
|
+
stopPhraseRotation();
|
|
395
485
|
if (textBuffer) {
|
|
486
|
+
// Bypass MIN_INITIAL_CHARS threshold — flush delivers all remaining content
|
|
487
|
+
firstContentEmitted = true;
|
|
396
488
|
const text = textBuffer;
|
|
397
489
|
textBuffer = "";
|
|
398
|
-
if (
|
|
490
|
+
if (streamFinalized && sendMessage) {
|
|
491
|
+
// Stream already finalized (>4000 path) — send remaining content as follow-up
|
|
492
|
+
safeSend(text);
|
|
493
|
+
}
|
|
494
|
+
else if (!stopped) {
|
|
399
495
|
// Stream is alive — await the emit so we can catch async 413/failure
|
|
400
496
|
// and fall through to sendMessage recovery.
|
|
401
497
|
const ok = await tryEmit(text);
|
|
402
498
|
if (!ok)
|
|
403
499
|
markStopped();
|
|
404
500
|
}
|
|
405
|
-
if (stopped && sendMessage) {
|
|
406
|
-
// Stream is dead — fall back to sendMessage; split on failure as recovery.
|
|
501
|
+
if (stopped && !streamFinalized && sendMessage) {
|
|
502
|
+
// Stream is dead (not from finalization) — fall back to sendMessage; split on failure as recovery.
|
|
407
503
|
try {
|
|
408
504
|
await sendMessage(text);
|
|
409
505
|
}
|
|
@@ -414,32 +510,12 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
414
510
|
}
|
|
415
511
|
}
|
|
416
512
|
}
|
|
417
|
-
else if (!streamHasContent) {
|
|
418
|
-
safeEmit("(completed with tool calls only
|
|
513
|
+
else if (!streamHasContent && !options?.suppressEmptyStreamMessage) {
|
|
514
|
+
safeEmit("(completed with tool calls only — no text response)");
|
|
419
515
|
}
|
|
420
516
|
},
|
|
421
517
|
};
|
|
422
518
|
}
|
|
423
|
-
// Per-conversation pending confirmation resolvers.
|
|
424
|
-
// When a mutate tool needs confirmation, the resolver is stored here.
|
|
425
|
-
// The next message from the same conversation resolves it.
|
|
426
|
-
const _pendingConfirmations = new Map();
|
|
427
|
-
// Confirmation response words (case-insensitive)
|
|
428
|
-
const CONFIRM_WORDS = new Set(["yes", "confirm", "go", "y", "ok", "approve", "proceed"]);
|
|
429
|
-
function resolvePendingConfirmation(convId, text) {
|
|
430
|
-
const resolver = _pendingConfirmations.get(convId);
|
|
431
|
-
if (!resolver)
|
|
432
|
-
return false;
|
|
433
|
-
_pendingConfirmations.delete(convId);
|
|
434
|
-
const word = text.trim().toLowerCase();
|
|
435
|
-
if (CONFIRM_WORDS.has(word)) {
|
|
436
|
-
resolver("confirmed");
|
|
437
|
-
}
|
|
438
|
-
else {
|
|
439
|
-
resolver("denied");
|
|
440
|
-
}
|
|
441
|
-
return true;
|
|
442
|
-
}
|
|
443
519
|
const _turnCoordinator = (0, turn_coordinator_1.createTurnCoordinator)();
|
|
444
520
|
function teamsTurnKey(conversationId) {
|
|
445
521
|
return `teams:${conversationId}`;
|
|
@@ -458,6 +534,7 @@ function createTeamsCommandRegistry() {
|
|
|
458
534
|
(0, commands_1.registerDefaultCommands)(registry);
|
|
459
535
|
return registry;
|
|
460
536
|
}
|
|
537
|
+
/* v8 ignore start -- superseding follow-up slash command handler; tested via startTeamsApp integration tests @preserve */
|
|
461
538
|
function handleTeamsSlashCommand(text, registry, friendId, conversationId, stream, emitResponse = true) {
|
|
462
539
|
const parsed = (0, commands_1.parseSlashCommand)(text);
|
|
463
540
|
if (!parsed)
|
|
@@ -481,15 +558,19 @@ function handleTeamsSlashCommand(text, registry, friendId, conversationId, strea
|
|
|
481
558
|
}
|
|
482
559
|
return null;
|
|
483
560
|
}
|
|
561
|
+
/* v8 ignore stop */
|
|
484
562
|
// Handle an incoming Teams message
|
|
485
|
-
async function handleTeamsMessage(text, stream, conversationId, teamsContext, sendMessage) {
|
|
563
|
+
async function handleTeamsMessage(text, stream, conversationId, teamsContext, sendMessage, reactionOverrides) {
|
|
486
564
|
const turnKey = teamsTurnKey(conversationId);
|
|
487
565
|
// NOTE: Confirmation resolution is handled in the app.on("message") handler
|
|
488
566
|
// BEFORE the conversation lock. By the time we get here, any pending
|
|
489
567
|
// confirmation has already been resolved and the reply consumed.
|
|
490
568
|
// Send first thinking phrase immediately so the user sees feedback
|
|
491
569
|
// before sync I/O (session load, trim) blocks the event loop.
|
|
492
|
-
|
|
570
|
+
// Skip for reaction signals — they should be processed quietly.
|
|
571
|
+
if (!reactionOverrides) {
|
|
572
|
+
stream.update((0, phrases_1.pickPhrase)((0, phrases_1.getPhrases)().thinking) + "...");
|
|
573
|
+
}
|
|
493
574
|
await new Promise(r => setImmediate(r));
|
|
494
575
|
// Resolve identity provider early for friend resolution + slash command session path
|
|
495
576
|
const store = getFriendStore();
|
|
@@ -506,15 +587,10 @@ async function handleTeamsMessage(text, stream, conversationId, teamsContext, se
|
|
|
506
587
|
// Pre-resolve friend for session path + slash commands (pipeline will re-use the cached result)
|
|
507
588
|
const resolvedContext = await resolver.resolve();
|
|
508
589
|
const friendId = resolvedContext.friend.id;
|
|
509
|
-
const registry = createTeamsCommandRegistry();
|
|
510
|
-
// Check for slash commands (before pipeline -- these are transport-level concerns)
|
|
511
|
-
if (handleTeamsSlashCommand(text, registry, friendId, conversationId, stream)) {
|
|
512
|
-
return;
|
|
513
|
-
}
|
|
514
590
|
// ── Teams adapter concerns: controller, callbacks, session path ──────────
|
|
515
591
|
const controller = new AbortController();
|
|
516
592
|
const channelConfig = (0, config_2.getTeamsChannelConfig)();
|
|
517
|
-
const callbacks = createTeamsCallbacks(stream, controller, sendMessage, { conversationId, flushIntervalMs: channelConfig.flushIntervalMs });
|
|
593
|
+
const callbacks = createTeamsCallbacks(stream, controller, sendMessage, { conversationId, flushIntervalMs: channelConfig.flushIntervalMs, ...(reactionOverrides?.suppressEmptyStreamMessage ? { suppressEmptyStreamMessage: true } : {}) });
|
|
518
594
|
const traceId = (0, nerves_1.createTraceId)();
|
|
519
595
|
const sessPath = (0, config_2.sessionPath)(friendId, "teams", conversationId);
|
|
520
596
|
const teamsCapabilities = (0, channel_1.getChannelCapabilities)("teams");
|
|
@@ -525,42 +601,68 @@ async function handleTeamsMessage(text, stream, conversationId, teamsContext, se
|
|
|
525
601
|
adoToken: teamsContext.adoToken,
|
|
526
602
|
githubToken: teamsContext.githubToken,
|
|
527
603
|
signin: teamsContext.signin,
|
|
528
|
-
summarize: (0, core_1.createSummarize)(),
|
|
604
|
+
summarize: (0, core_1.createSummarize)("human"),
|
|
529
605
|
tenantId: teamsContext.tenantId,
|
|
530
606
|
botApi: teamsContext.botApi,
|
|
531
607
|
} : {};
|
|
532
608
|
let currentText = text;
|
|
609
|
+
const mcpManager = await (0, mcp_manager_1.getSharedMcpManager)() ?? undefined;
|
|
533
610
|
while (true) {
|
|
534
611
|
let drainedSteeringFollowUps = [];
|
|
535
612
|
// Build runAgentOptions with Teams-specific fields
|
|
536
613
|
const agentOptions = {
|
|
537
614
|
traceId,
|
|
538
615
|
toolContext: teamsToolContext,
|
|
616
|
+
mcpManager,
|
|
539
617
|
drainSteeringFollowUps: () => {
|
|
540
618
|
drainedSteeringFollowUps = _turnCoordinator.drainFollowUps(turnKey)
|
|
541
619
|
.map(({ text: followUpText, effect }) => ({ text: followUpText, effect }));
|
|
542
620
|
return drainedSteeringFollowUps;
|
|
543
621
|
},
|
|
622
|
+
...(reactionOverrides?.isReactionSignal ? { isReactionSignal: true } : {}),
|
|
544
623
|
};
|
|
545
|
-
if (channelConfig.skipConfirmation)
|
|
546
|
-
agentOptions.skipConfirmation = true;
|
|
547
624
|
// ── Call shared pipeline ──────────────────────────────────────────
|
|
625
|
+
// Capture terminal errors — failover message replaces the error card if it triggers
|
|
626
|
+
let capturedTerminalError = null;
|
|
627
|
+
const teamsFailoverState = (() => {
|
|
628
|
+
if (!teamsFailoverStates.has(conversationId)) {
|
|
629
|
+
teamsFailoverStates.set(conversationId, { pending: null });
|
|
630
|
+
}
|
|
631
|
+
return teamsFailoverStates.get(conversationId);
|
|
632
|
+
})();
|
|
633
|
+
/* v8 ignore start -- failover-aware callback wrapper: tested via pipeline integration @preserve */
|
|
634
|
+
const failoverAwareCallbacks = {
|
|
635
|
+
...callbacks,
|
|
636
|
+
onError: (error, severity) => {
|
|
637
|
+
if (severity === "terminal" && teamsFailoverState) {
|
|
638
|
+
capturedTerminalError = error;
|
|
639
|
+
return;
|
|
640
|
+
}
|
|
641
|
+
callbacks.onError(error, severity);
|
|
642
|
+
},
|
|
643
|
+
};
|
|
644
|
+
/* v8 ignore stop */
|
|
548
645
|
const result = await (0, pipeline_1.handleInboundTurn)({
|
|
549
646
|
channel: "teams",
|
|
550
647
|
sessionKey: conversationId,
|
|
551
648
|
capabilities: teamsCapabilities,
|
|
552
649
|
messages: [{ role: "user", content: currentText }],
|
|
553
650
|
continuityIngressTexts: [currentText],
|
|
554
|
-
callbacks,
|
|
651
|
+
callbacks: failoverAwareCallbacks,
|
|
555
652
|
friendResolver: { resolve: () => Promise.resolve(resolvedContext) },
|
|
556
653
|
sessionLoader: {
|
|
557
654
|
loadOrCreate: async () => {
|
|
558
655
|
const existing = (0, context_1.loadSession)(sessPath);
|
|
559
656
|
const messages = existing?.messages && existing.messages.length > 0
|
|
560
657
|
? existing.messages
|
|
561
|
-
: [{ role: "system", content: await (0, prompt_1.buildSystem)("teams",
|
|
658
|
+
: [{ role: "system", content: (0, prompt_1.flattenSystemPrompt)(await (0, prompt_1.buildSystem)("teams", {}, resolvedContext)) }];
|
|
562
659
|
(0, core_1.repairOrphanedToolCalls)(messages);
|
|
563
|
-
return {
|
|
660
|
+
return {
|
|
661
|
+
messages,
|
|
662
|
+
sessionPath: sessPath,
|
|
663
|
+
state: existing?.state,
|
|
664
|
+
events: existing?.events,
|
|
665
|
+
};
|
|
564
666
|
},
|
|
565
667
|
},
|
|
566
668
|
pendingDir,
|
|
@@ -583,11 +685,33 @@ async function handleTeamsMessage(text, stream, conversationId, teamsContext, se
|
|
|
583
685
|
summarize: teamsToolContext.summarize,
|
|
584
686
|
},
|
|
585
687
|
}),
|
|
586
|
-
postTurn:
|
|
688
|
+
postTurn: (turnMessages, sessionPathArg, usage, hooks, state) => {
|
|
689
|
+
const prepared = (0, context_1.postTurnTrim)(turnMessages, usage, hooks);
|
|
690
|
+
(0, context_1.deferPostTurnPersist)(sessionPathArg, prepared, usage, state);
|
|
691
|
+
},
|
|
587
692
|
accumulateFriendTokens: tokens_1.accumulateFriendTokens,
|
|
588
693
|
signal: controller.signal,
|
|
589
694
|
runAgentOptions: agentOptions,
|
|
695
|
+
failoverState: teamsFailoverState,
|
|
590
696
|
});
|
|
697
|
+
// ── Handle pipeline-intercepted commands ────────────────────────
|
|
698
|
+
if (result.turnOutcome === "command") {
|
|
699
|
+
if (result.commandAction === "new") {
|
|
700
|
+
(0, context_1.deleteSession)(sessPath);
|
|
701
|
+
stream.emit("session cleared");
|
|
702
|
+
}
|
|
703
|
+
// For "response" commands: pipeline already emitted the response via onTextChunk
|
|
704
|
+
await callbacks.flush();
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
/* v8 ignore start -- failover display: tested via pipeline integration tests @preserve */
|
|
708
|
+
if (result.failoverMessage) {
|
|
709
|
+
stream.emit(result.failoverMessage);
|
|
710
|
+
}
|
|
711
|
+
else if (capturedTerminalError) {
|
|
712
|
+
callbacks.onError(capturedTerminalError, "terminal");
|
|
713
|
+
}
|
|
714
|
+
/* v8 ignore stop */
|
|
591
715
|
// ── Handle gate result ────────────────────────────────────────
|
|
592
716
|
if (!result.gateResult.allowed) {
|
|
593
717
|
if ("autoReply" in result.gateResult && result.gateResult.autoReply) {
|
|
@@ -627,7 +751,7 @@ async function handleTeamsMessage(text, stream, conversationId, teamsContext, se
|
|
|
627
751
|
currentText = replayTail;
|
|
628
752
|
continue;
|
|
629
753
|
}
|
|
630
|
-
if (handleTeamsSlashCommand(supersedingFollowUp.text,
|
|
754
|
+
if (handleTeamsSlashCommand(supersedingFollowUp.text, createTeamsCommandRegistry(), friendId, conversationId, stream, false)) {
|
|
631
755
|
return;
|
|
632
756
|
}
|
|
633
757
|
currentText = supersedingFollowUp.text;
|
|
@@ -707,7 +831,7 @@ function registerBotHandlers(app, label) {
|
|
|
707
831
|
// (graph + ado + github). The verifyState activity only carries a `state`
|
|
708
832
|
// code with no connectionName, so we try each configured connection until
|
|
709
833
|
// one succeeds.
|
|
710
|
-
app.on("signin.verify-state", async (ctx) => {
|
|
834
|
+
app.on("signin.verify-state", (async (ctx) => {
|
|
711
835
|
const { api, activity } = ctx;
|
|
712
836
|
if (!activity.value?.state)
|
|
713
837
|
return { status: 404 };
|
|
@@ -726,7 +850,73 @@ function registerBotHandlers(app, label) {
|
|
|
726
850
|
}
|
|
727
851
|
(0, runtime_1.emitNervesEvent)({ level: "warn", event: "channel.verify_state", component: "channels", message: `[${label}] verify-state failed for all connections`, meta: {} });
|
|
728
852
|
return { status: 412 };
|
|
853
|
+
}));
|
|
854
|
+
// Handle Teams feedback reactions (thumbs up/down on AI-generated messages).
|
|
855
|
+
// SDK routes message/submitAction with actionName "feedback" to this event.
|
|
856
|
+
/* v8 ignore start -- Teams SDK invoke handler; requires live SDK context @preserve */
|
|
857
|
+
app.on("message.submit.feedback", async (ctx) => {
|
|
858
|
+
const { stream, activity } = ctx;
|
|
859
|
+
const reaction = activity.value?.actionValue?.reaction;
|
|
860
|
+
const comment = activity.value?.actionValue?.feedback;
|
|
861
|
+
const convId = activity.conversation?.id || "unknown";
|
|
862
|
+
const turnKey = teamsTurnKey(convId);
|
|
863
|
+
// Validate payload — graceful no-op for malformed invocations
|
|
864
|
+
if (activity.value?.actionName !== "feedback" || !reaction) {
|
|
865
|
+
return;
|
|
866
|
+
}
|
|
867
|
+
const syntheticText = buildFeedbackSyntheticText(reaction, comment);
|
|
868
|
+
// Turn coordination: if a turn is active, enqueue as steering follow-up
|
|
869
|
+
if (!_turnCoordinator.tryBeginTurn(turnKey)) {
|
|
870
|
+
_turnCoordinator.enqueueFollowUp(turnKey, {
|
|
871
|
+
conversationId: convId,
|
|
872
|
+
text: syntheticText,
|
|
873
|
+
receivedAt: Date.now(),
|
|
874
|
+
effect: (0, continuity_1.classifySteeringFollowUpEffect)(syntheticText),
|
|
875
|
+
});
|
|
876
|
+
return;
|
|
877
|
+
}
|
|
878
|
+
try {
|
|
879
|
+
const teamsContext = {
|
|
880
|
+
signin: async () => undefined,
|
|
881
|
+
aadObjectId: activity.from?.aadObjectId,
|
|
882
|
+
tenantId: activity.conversation?.tenantId,
|
|
883
|
+
displayName: activity.from?.name,
|
|
884
|
+
};
|
|
885
|
+
const ctxSend = async (t) => {
|
|
886
|
+
await ctx.send({ type: "message", text: t, replyToId: activity.replyToId, entities: aiLabelEntities(), channelData: { feedbackLoopEnabled: true } });
|
|
887
|
+
};
|
|
888
|
+
await handleTeamsMessage(syntheticText, stream, convId, teamsContext, ctxSend, { isReactionSignal: true, suppressEmptyStreamMessage: true });
|
|
889
|
+
}
|
|
890
|
+
catch (err) {
|
|
891
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
892
|
+
(0, runtime_1.emitNervesEvent)({ level: "error", event: "channel.feedback_handler_error", component: "channels", message: msg.slice(0, 200), meta: {} });
|
|
893
|
+
}
|
|
894
|
+
finally {
|
|
895
|
+
_turnCoordinator.endTurn(turnKey);
|
|
896
|
+
}
|
|
729
897
|
});
|
|
898
|
+
/* v8 ignore stop */
|
|
899
|
+
// Handle bot install — send welcome Adaptive Card with prompt starters.
|
|
900
|
+
/* v8 ignore start -- Teams SDK install handler; requires live SDK context @preserve */
|
|
901
|
+
app.on("install.add", async (ctx) => {
|
|
902
|
+
try {
|
|
903
|
+
const card = buildWelcomeCard();
|
|
904
|
+
await ctx.send({
|
|
905
|
+
type: "message",
|
|
906
|
+
attachments: [{
|
|
907
|
+
contentType: "application/vnd.microsoft.card.adaptive",
|
|
908
|
+
content: card,
|
|
909
|
+
}],
|
|
910
|
+
entities: aiLabelEntities(),
|
|
911
|
+
channelData: { feedbackLoopEnabled: true },
|
|
912
|
+
});
|
|
913
|
+
}
|
|
914
|
+
catch (err) {
|
|
915
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
916
|
+
(0, runtime_1.emitNervesEvent)({ level: "error", event: "channel.welcome_handler_error", component: "channels", message: msg.slice(0, 200), meta: {} });
|
|
917
|
+
}
|
|
918
|
+
});
|
|
919
|
+
/* v8 ignore stop */
|
|
730
920
|
app.on("message", async (ctx) => {
|
|
731
921
|
const { stream, activity, api, signin } = ctx;
|
|
732
922
|
const text = activity.text || "";
|
|
@@ -735,13 +925,6 @@ function registerBotHandlers(app, label) {
|
|
|
735
925
|
const userId = activity.from?.id || "";
|
|
736
926
|
const channelId = activity.channelId || "msteams";
|
|
737
927
|
(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) } });
|
|
738
|
-
// Resolve pending confirmations IMMEDIATELY — before token fetches or
|
|
739
|
-
// the conversation lock. The original message holds the lock while
|
|
740
|
-
// awaiting confirmation, so acquiring it here would deadlock. Token
|
|
741
|
-
// fetches are also unnecessary (and slow) for a simple yes/no reply.
|
|
742
|
-
if (resolvePendingConfirmation(convId, text)) {
|
|
743
|
-
return;
|
|
744
|
-
}
|
|
745
928
|
const commandRegistry = createTeamsCommandRegistry();
|
|
746
929
|
const parsedSlashCommand = (0, commands_1.parseSlashCommand)(text);
|
|
747
930
|
if (parsedSlashCommand) {
|
|
@@ -842,7 +1025,7 @@ function registerBotHandlers(app, label) {
|
|
|
842
1025
|
const ctxSend = async (t) => {
|
|
843
1026
|
// Use send with replyToId (not reply, which adds a blockquote).
|
|
844
1027
|
// replyToId anchors the message after the user's message in Copilot Chat.
|
|
845
|
-
await ctx.send({ type: "message", text: t, replyToId: activity.id });
|
|
1028
|
+
await ctx.send({ type: "message", text: t, replyToId: activity.id, entities: aiLabelEntities(), channelData: { feedbackLoopEnabled: true } });
|
|
846
1029
|
};
|
|
847
1030
|
await handleTeamsMessage(text, stream, convId, teamsContext, ctxSend);
|
|
848
1031
|
}
|
|
@@ -867,6 +1050,108 @@ function findAadObjectId(friend) {
|
|
|
867
1050
|
}
|
|
868
1051
|
return undefined;
|
|
869
1052
|
}
|
|
1053
|
+
function resolveTeamsFriendStore(deps) {
|
|
1054
|
+
return deps.store
|
|
1055
|
+
?? deps.createFriendStore?.()
|
|
1056
|
+
?? new store_file_1.FileFriendStore(path.join((0, identity_1.getAgentRoot)(), "friends"));
|
|
1057
|
+
}
|
|
1058
|
+
function getTeamsConversations(botApi) {
|
|
1059
|
+
return botApi.conversations;
|
|
1060
|
+
}
|
|
1061
|
+
function hasExplicitCrossChatAuthorization(params) {
|
|
1062
|
+
return params.intent === "explicit_cross_chat"
|
|
1063
|
+
&& types_1.TRUSTED_LEVELS.has(params.authorizingSession?.trustLevel ?? "stranger");
|
|
1064
|
+
}
|
|
1065
|
+
async function sendProactiveTeamsMessageToSession(params, deps) {
|
|
1066
|
+
const store = resolveTeamsFriendStore(deps);
|
|
1067
|
+
const conversations = getTeamsConversations(deps.botApi);
|
|
1068
|
+
let friend;
|
|
1069
|
+
try {
|
|
1070
|
+
friend = await store.get(params.friendId);
|
|
1071
|
+
}
|
|
1072
|
+
catch {
|
|
1073
|
+
friend = null;
|
|
1074
|
+
}
|
|
1075
|
+
if (!friend) {
|
|
1076
|
+
(0, runtime_1.emitNervesEvent)({
|
|
1077
|
+
level: "warn",
|
|
1078
|
+
component: "senses",
|
|
1079
|
+
event: "senses.teams_proactive_no_friend",
|
|
1080
|
+
message: "proactive send skipped: friend not found",
|
|
1081
|
+
meta: { friendId: params.friendId, sessionKey: params.sessionKey },
|
|
1082
|
+
});
|
|
1083
|
+
return { delivered: false, reason: "friend_not_found" };
|
|
1084
|
+
}
|
|
1085
|
+
if (!hasExplicitCrossChatAuthorization(params) && !types_1.TRUSTED_LEVELS.has(friend.trustLevel ?? "stranger")) {
|
|
1086
|
+
(0, runtime_1.emitNervesEvent)({
|
|
1087
|
+
component: "senses",
|
|
1088
|
+
event: "senses.teams_proactive_trust_skip",
|
|
1089
|
+
message: "proactive send skipped: trust level not allowed",
|
|
1090
|
+
meta: {
|
|
1091
|
+
friendId: params.friendId,
|
|
1092
|
+
trustLevel: friend.trustLevel ?? "unknown",
|
|
1093
|
+
intent: params.intent ?? "generic_outreach",
|
|
1094
|
+
authorizingTrustLevel: params.authorizingSession?.trustLevel ?? null,
|
|
1095
|
+
},
|
|
1096
|
+
});
|
|
1097
|
+
return { delivered: false, reason: "trust_skip" };
|
|
1098
|
+
}
|
|
1099
|
+
const aadInfo = findAadObjectId(friend);
|
|
1100
|
+
if (!aadInfo) {
|
|
1101
|
+
(0, runtime_1.emitNervesEvent)({
|
|
1102
|
+
level: "warn",
|
|
1103
|
+
component: "senses",
|
|
1104
|
+
event: "senses.teams_proactive_no_aad_id",
|
|
1105
|
+
message: "proactive send skipped: no AAD object ID found",
|
|
1106
|
+
meta: { friendId: params.friendId, sessionKey: params.sessionKey },
|
|
1107
|
+
});
|
|
1108
|
+
return { delivered: false, reason: "missing_target" };
|
|
1109
|
+
}
|
|
1110
|
+
const internalContentBlockReason = (0, proactive_content_guard_1.getProactiveInternalContentBlockReason)(params.text);
|
|
1111
|
+
if (internalContentBlockReason) {
|
|
1112
|
+
(0, proactive_content_guard_1.emitProactiveInternalContentBlocked)({
|
|
1113
|
+
friendId: params.friendId,
|
|
1114
|
+
sessionKey: params.sessionKey,
|
|
1115
|
+
reason: internalContentBlockReason,
|
|
1116
|
+
source: "session_send",
|
|
1117
|
+
});
|
|
1118
|
+
return { delivered: false, reason: "internal_content_blocked" };
|
|
1119
|
+
}
|
|
1120
|
+
try {
|
|
1121
|
+
const conversation = await conversations.create({
|
|
1122
|
+
bot: { id: deps.botApi.id },
|
|
1123
|
+
members: [{ id: aadInfo.aadObjectId, role: "user", name: friend.name || aadInfo.aadObjectId }],
|
|
1124
|
+
tenantId: aadInfo.tenantId,
|
|
1125
|
+
isGroup: false,
|
|
1126
|
+
});
|
|
1127
|
+
await conversations.activities(conversation.id).create({
|
|
1128
|
+
type: "message",
|
|
1129
|
+
text: params.text,
|
|
1130
|
+
});
|
|
1131
|
+
(0, runtime_1.emitNervesEvent)({
|
|
1132
|
+
component: "senses",
|
|
1133
|
+
event: "senses.teams_proactive_sent",
|
|
1134
|
+
message: "proactive teams message sent",
|
|
1135
|
+
meta: { friendId: params.friendId, aadObjectId: aadInfo.aadObjectId, sessionKey: params.sessionKey },
|
|
1136
|
+
});
|
|
1137
|
+
return { delivered: true };
|
|
1138
|
+
}
|
|
1139
|
+
catch (error) {
|
|
1140
|
+
(0, runtime_1.emitNervesEvent)({
|
|
1141
|
+
level: "error",
|
|
1142
|
+
component: "senses",
|
|
1143
|
+
event: "senses.teams_proactive_send_error",
|
|
1144
|
+
message: "proactive teams send failed",
|
|
1145
|
+
meta: {
|
|
1146
|
+
friendId: params.friendId,
|
|
1147
|
+
aadObjectId: aadInfo.aadObjectId,
|
|
1148
|
+
sessionKey: params.sessionKey,
|
|
1149
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
1150
|
+
},
|
|
1151
|
+
});
|
|
1152
|
+
return { delivered: false, reason: "send_error" };
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
870
1155
|
function scanPendingTeamsFiles(pendingRoot) {
|
|
871
1156
|
const results = [];
|
|
872
1157
|
let friendIds;
|
|
@@ -912,8 +1197,7 @@ async function drainAndSendPendingTeams(store, botApi, pendingRoot) {
|
|
|
912
1197
|
const root = pendingRoot ?? path.join((0, identity_1.getAgentRoot)(), "state", "pending");
|
|
913
1198
|
const pendingFiles = scanPendingTeamsFiles(root);
|
|
914
1199
|
const result = { sent: 0, skipped: 0, failed: 0 };
|
|
915
|
-
const
|
|
916
|
-
for (const { friendId, filePath, content } of pendingFiles) {
|
|
1200
|
+
for (const { friendId, key, filePath, content } of pendingFiles) {
|
|
917
1201
|
let parsed;
|
|
918
1202
|
try {
|
|
919
1203
|
parsed = JSON.parse(content);
|
|
@@ -935,95 +1219,46 @@ async function drainAndSendPendingTeams(store, botApi, pendingRoot) {
|
|
|
935
1219
|
catch { /* ignore */ }
|
|
936
1220
|
continue;
|
|
937
1221
|
}
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
friend = await store.get(friendId);
|
|
941
|
-
}
|
|
942
|
-
catch {
|
|
943
|
-
friend = null;
|
|
944
|
-
}
|
|
945
|
-
if (!friend) {
|
|
1222
|
+
const internalBlockReason = (0, proactive_content_guard_1.getProactiveInternalContentBlockReason)(messageText);
|
|
1223
|
+
if (internalBlockReason) {
|
|
946
1224
|
result.skipped++;
|
|
947
1225
|
try {
|
|
948
1226
|
fs.unlinkSync(filePath);
|
|
949
1227
|
}
|
|
950
1228
|
catch { /* ignore */ }
|
|
951
|
-
(0,
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
message: "proactive send skipped: friend not found",
|
|
956
|
-
meta: { friendId },
|
|
1229
|
+
(0, proactive_content_guard_1.emitProactiveInternalContentBlocked)({
|
|
1230
|
+
friendId,
|
|
1231
|
+
reason: internalBlockReason,
|
|
1232
|
+
source: "pending_drain",
|
|
957
1233
|
});
|
|
958
1234
|
continue;
|
|
959
1235
|
}
|
|
960
|
-
|
|
961
|
-
|
|
1236
|
+
const sendResult = await sendProactiveTeamsMessageToSession({
|
|
1237
|
+
friendId,
|
|
1238
|
+
sessionKey: key,
|
|
1239
|
+
text: messageText,
|
|
1240
|
+
intent: "generic_outreach",
|
|
1241
|
+
}, {
|
|
1242
|
+
botApi,
|
|
1243
|
+
store,
|
|
1244
|
+
});
|
|
1245
|
+
if (sendResult.delivered) {
|
|
1246
|
+
result.sent++;
|
|
962
1247
|
try {
|
|
963
1248
|
fs.unlinkSync(filePath);
|
|
964
1249
|
}
|
|
965
1250
|
catch { /* ignore */ }
|
|
966
|
-
(0, runtime_1.emitNervesEvent)({
|
|
967
|
-
component: "senses",
|
|
968
|
-
event: "senses.teams_proactive_trust_skip",
|
|
969
|
-
message: "proactive send skipped: trust level not allowed",
|
|
970
|
-
meta: { friendId, trustLevel: friend.trustLevel ?? "unknown" },
|
|
971
|
-
});
|
|
972
1251
|
continue;
|
|
973
1252
|
}
|
|
974
|
-
|
|
975
|
-
if (!aadInfo) {
|
|
1253
|
+
if (sendResult.reason === "friend_not_found" || sendResult.reason === "trust_skip" || sendResult.reason === "missing_target") {
|
|
976
1254
|
result.skipped++;
|
|
977
1255
|
try {
|
|
978
1256
|
fs.unlinkSync(filePath);
|
|
979
1257
|
}
|
|
980
1258
|
catch { /* ignore */ }
|
|
981
|
-
(0, runtime_1.emitNervesEvent)({
|
|
982
|
-
level: "warn",
|
|
983
|
-
component: "senses",
|
|
984
|
-
event: "senses.teams_proactive_no_aad_id",
|
|
985
|
-
message: "proactive send skipped: no AAD object ID found",
|
|
986
|
-
meta: { friendId },
|
|
987
|
-
});
|
|
988
1259
|
continue;
|
|
989
1260
|
}
|
|
990
|
-
|
|
991
|
-
const conversation = await conversations.create({
|
|
992
|
-
bot: { id: botApi.id },
|
|
993
|
-
members: [{ id: aadInfo.aadObjectId, role: "user", name: friend.name || aadInfo.aadObjectId }],
|
|
994
|
-
tenantId: aadInfo.tenantId,
|
|
995
|
-
isGroup: false,
|
|
996
|
-
});
|
|
997
|
-
await conversations.activities(conversation.id).create({
|
|
998
|
-
type: "message",
|
|
999
|
-
text: messageText,
|
|
1000
|
-
});
|
|
1001
|
-
result.sent++;
|
|
1002
|
-
try {
|
|
1003
|
-
fs.unlinkSync(filePath);
|
|
1004
|
-
}
|
|
1005
|
-
catch { /* ignore */ }
|
|
1006
|
-
(0, runtime_1.emitNervesEvent)({
|
|
1007
|
-
component: "senses",
|
|
1008
|
-
event: "senses.teams_proactive_sent",
|
|
1009
|
-
message: "proactive teams message sent",
|
|
1010
|
-
meta: { friendId, aadObjectId: aadInfo.aadObjectId },
|
|
1011
|
-
});
|
|
1012
|
-
}
|
|
1013
|
-
catch (error) {
|
|
1014
|
-
result.failed++;
|
|
1015
|
-
(0, runtime_1.emitNervesEvent)({
|
|
1016
|
-
level: "error",
|
|
1017
|
-
component: "senses",
|
|
1018
|
-
event: "senses.teams_proactive_send_error",
|
|
1019
|
-
message: "proactive teams send failed",
|
|
1020
|
-
meta: {
|
|
1021
|
-
friendId,
|
|
1022
|
-
aadObjectId: aadInfo.aadObjectId,
|
|
1023
|
-
reason: error instanceof Error ? error.message : String(error),
|
|
1024
|
-
},
|
|
1025
|
-
});
|
|
1026
|
-
}
|
|
1261
|
+
result.failed++;
|
|
1027
1262
|
}
|
|
1028
1263
|
if (result.sent > 0 || result.skipped > 0 || result.failed > 0) {
|
|
1029
1264
|
(0, runtime_1.emitNervesEvent)({
|