@ouro.bot/cli 0.1.0-alpha.92 → 0.1.0-alpha.920
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/LICENSE +21 -0
- package/README.md +154 -23
- package/RepairGuide.ouro/agent.json +5 -0
- package/RepairGuide.ouro/psyche/IDENTITY.md +19 -0
- package/RepairGuide.ouro/psyche/SOUL.md +55 -0
- package/RepairGuide.ouro/skills/diagnose-broken-remote.md +63 -0
- package/RepairGuide.ouro/skills/diagnose-stacked-typed-issues.md +35 -0
- package/RepairGuide.ouro/skills/diagnose-sync-blocked.md +54 -0
- package/RepairGuide.ouro/skills/diagnose-vault-expired.md +60 -0
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/agent.json +4 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/SOUL.md +2 -2
- package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-serpent.md +1 -1
- package/assets/bluebubbles-host +264 -0
- package/assets/sanctuary-host-launcher.sh +16 -0
- package/changelog.json +5819 -16
- package/deploy/unraid/Dockerfile +58 -0
- package/deploy/unraid/README.txt +2997 -0
- package/deploy/unraid/audit-container-spec.sh +4 -0
- package/deploy/unraid/container-runtime.json +1 -0
- package/deploy/unraid/docker-man-template-transaction.mjs +711 -0
- package/deploy/unraid/docker-man-template-xml.cjs +105 -0
- package/deploy/unraid/migrate-sanctuary-bundle.mjs +60 -0
- package/deploy/unraid/ouro-events/bootstrap-spool.sh +92 -0
- package/deploy/unraid/ouro-events/emit-event.mjs +310 -0
- package/deploy/unraid/ouro-events/emit-usenet-event.sh +54 -0
- package/deploy/unraid/ouro-events/install-usenet-guard.sh +435 -0
- package/deploy/unraid/ouro-events/usenet-health.sh +176 -0
- package/deploy/unraid/sanctuary-acceptance-adapter.sh +43 -0
- package/deploy/unraid/sanctuary-acceptance-contract.json +198 -0
- package/deploy/unraid/sanctuary-acceptance-harness.sh +23 -0
- package/deploy/unraid/sanctuary-authority-installation.json +36 -0
- package/deploy/unraid/sanctuary-authority-service.sh +30 -0
- package/deploy/unraid/sanctuary-butler-upgrade.mjs +1254 -0
- package/deploy/unraid/sanctuary-deployment-target.mjs +710 -0
- package/deploy/unraid/sanctuary-replay-gate.mjs +1564 -0
- package/deploy/unraid/sanctuary-safe-rename.py +24 -0
- package/deploy/unraid/sanctuary-unit16-host-broker.mjs +2086 -0
- package/deploy/unraid/sanctuary-unit16-run.sh +560 -0
- package/deploy/unraid/sanctuary-unit18-target-audit.sh +27 -0
- package/deploy/unraid/sanctuary.ouro/agent.json +66 -0
- package/deploy/unraid/sanctuary.ouro/arc/README.md +3 -0
- package/deploy/unraid/sanctuary.ouro/awaiting/house-care-sweep.md +17 -0
- package/deploy/unraid/sanctuary.ouro/bundle-meta.json +5 -0
- package/deploy/unraid/sanctuary.ouro/habits/sanctuary-health.md +8 -0
- package/deploy/unraid/sanctuary.ouro/mcp/media-mcp.mjs +2231 -0
- package/deploy/unraid/sanctuary.ouro/provider-readiness.json +11 -0
- package/deploy/unraid/sanctuary.ouro/psyche/ASPIRATIONS.md +5 -0
- package/deploy/unraid/sanctuary.ouro/psyche/IDENTITY.md +5 -0
- package/deploy/unraid/sanctuary.ouro/psyche/LORE.md +33 -0
- package/deploy/unraid/sanctuary.ouro/psyche/SOUL.md +27 -0
- package/deploy/unraid/sanctuary.ouro/psyche/TACIT.md +22 -0
- package/deploy/unraid/sanctuary.ouro/state/policy/steward.json +7 -0
- package/deploy/unraid/sanctuary.ouro/tool-profiles.json +232 -0
- package/deploy/unraid/sanctuary.xml +30 -0
- package/dist/a2a/card.js +57 -0
- package/dist/a2a/client.js +250 -0
- package/dist/a2a/config.js +50 -0
- package/dist/a2a/delegated-command-grants.js +93 -0
- package/dist/a2a/delegated-command.js +197 -0
- package/dist/a2a/delegation-stores.js +56 -0
- package/dist/a2a/did-resolution.js +93 -0
- package/dist/a2a/escalation-grants.js +84 -0
- package/dist/a2a/escalation-wiring.js +60 -0
- package/dist/a2a/identity.js +258 -0
- package/dist/a2a/inbound-share.js +110 -0
- package/dist/a2a/mission-result-wire.js +196 -0
- package/dist/a2a/onboarding.js +133 -0
- package/dist/a2a/operator-trust.js +382 -0
- package/dist/a2a/outbox-client.js +57 -0
- package/dist/a2a/outbox-store.js +188 -0
- package/dist/a2a/outbox-wire.js +95 -0
- package/dist/a2a/pin-store.js +132 -0
- package/dist/a2a/replay-grant-holders.js +33 -0
- package/dist/a2a/replay-harness.js +188 -0
- package/dist/a2a/resolution-proof.js +53 -0
- package/dist/a2a/sealed-chat.js +113 -0
- package/dist/a2a/seen-ledger.js +120 -0
- package/dist/a2a/server.js +771 -0
- package/dist/a2a/task-store.js +69 -0
- package/dist/a2a/transport.js +45 -0
- package/dist/a2a/trust-commands.js +203 -0
- package/dist/a2a/trusted-files.js +185 -0
- package/dist/a2a/types.js +3 -0
- package/dist/arc/attention-types.js +8 -0
- package/dist/arc/cares.js +360 -0
- package/dist/arc/episodes.js +118 -0
- package/dist/arc/evolution.js +490 -0
- package/dist/arc/flight-recorder.js +1034 -0
- package/dist/arc/intentions.js +134 -0
- package/dist/arc/json-store.js +117 -0
- package/dist/arc/obligations.js +386 -0
- package/dist/arc/packets.js +336 -0
- package/dist/arc/presence.js +185 -0
- package/dist/arc/task-lifecycle.js +57 -0
- package/dist/commerce/store.js +755 -0
- package/dist/commerce/types.js +3 -0
- package/dist/heart/active-work.js +798 -40
- package/dist/heart/agent-entry.js +252 -3
- package/dist/heart/approval-files.js +113 -0
- package/dist/heart/approval-store.js +960 -0
- package/dist/heart/attachments/image-normalize.js +194 -0
- package/dist/heart/attachments/materialize.js +97 -0
- package/dist/heart/attachments/originals.js +88 -0
- package/dist/heart/attachments/render.js +29 -0
- package/dist/heart/attachments/sources/bluebubbles.js +156 -0
- package/dist/heart/attachments/sources/cli-local-file.js +78 -0
- package/dist/heart/attachments/sources/index.js +18 -0
- package/dist/heart/attachments/sources/telegram.js +71 -0
- package/dist/heart/attachments/store.js +132 -0
- package/dist/heart/attachments/types.js +93 -0
- package/dist/heart/auth/auth-flow.js +495 -0
- package/dist/heart/autonomy-budget.js +478 -0
- package/dist/heart/awaiting/a2a-await-delivery.js +109 -0
- package/dist/heart/awaiting/await-alert.js +152 -0
- package/dist/heart/awaiting/await-expiry.js +143 -0
- package/dist/heart/awaiting/await-loader.js +91 -0
- package/dist/heart/awaiting/await-parser.js +152 -0
- package/dist/heart/awaiting/await-runtime-state.js +111 -0
- package/dist/heart/awaiting/await-scheduler.js +391 -0
- package/dist/heart/background-operations.js +281 -0
- package/dist/heart/bluebubbles-host-account.js +78 -0
- package/dist/heart/bridges/manager.js +137 -17
- package/dist/heart/bridges/store.js +14 -2
- package/dist/heart/bundle-state.js +168 -0
- package/dist/heart/commitments.js +44 -18
- package/dist/heart/config-registry.js +331 -0
- package/dist/heart/config.js +174 -131
- package/dist/heart/context-loss-gauntlet.js +387 -0
- package/dist/heart/context-loss-sentinel.js +1159 -0
- package/dist/heart/core.js +2364 -578
- package/dist/heart/cross-chat-delivery.js +9 -22
- package/dist/heart/daemon/agent-config-check.js +451 -0
- package/dist/heart/daemon/agent-discovery.js +283 -3
- package/dist/heart/daemon/agent-home.js +319 -0
- package/dist/heart/daemon/agent-service.js +542 -0
- package/dist/heart/daemon/agentic-repair.js +547 -0
- package/dist/heart/daemon/await-private-wake.js +59 -0
- package/dist/heart/daemon/bluebubbles-cli-integration.js +71 -0
- package/dist/heart/daemon/bluebubbles-health-diagnostics.js +122 -0
- package/dist/heart/daemon/bluebubbles-host-protocol.js +433 -0
- package/dist/heart/daemon/bluebubbles-host.js +273 -0
- package/dist/heart/daemon/boot-sync-probe.js +197 -0
- package/dist/heart/daemon/cadence.js +180 -0
- package/dist/heart/daemon/cli-arg-redaction.js +26 -0
- package/dist/heart/daemon/cli-defaults.js +876 -0
- package/dist/heart/daemon/cli-desk.js +322 -0
- package/dist/heart/daemon/cli-exec.js +9199 -0
- package/dist/heart/daemon/cli-help.js +735 -0
- package/dist/heart/daemon/cli-parse.js +3211 -0
- package/dist/heart/daemon/cli-render-doctor.js +57 -0
- package/dist/heart/daemon/cli-render.js +819 -0
- package/dist/heart/daemon/cli-types.js +8 -0
- package/dist/heart/daemon/connect-bay.js +323 -0
- package/dist/heart/daemon/container-credential-bootstrap.js +273 -0
- package/dist/heart/daemon/container-healthcheck.js +112 -0
- package/dist/heart/daemon/container-runtime.js +97 -0
- package/dist/heart/daemon/container-spec-auditor-main.js +171 -0
- package/dist/heart/daemon/container-spec-auditor.js +235 -0
- package/dist/heart/daemon/daemon-bootstrap-startup.js +115 -0
- package/dist/heart/daemon/daemon-cli.js +29 -2350
- package/dist/heart/daemon/daemon-entry.js +953 -11
- package/dist/heart/daemon/daemon-health.js +186 -0
- package/dist/heart/daemon/daemon-rollup.js +57 -0
- package/dist/heart/daemon/daemon-runtime-sync.js +88 -13
- package/dist/heart/daemon/daemon-tombstone.js +236 -0
- package/dist/heart/daemon/daemon.js +2084 -105
- package/dist/heart/daemon/dns-workflow.js +394 -0
- package/dist/heart/daemon/doctor-types.js +8 -0
- package/dist/heart/daemon/doctor.js +1766 -0
- package/dist/heart/daemon/freshness.js +345 -0
- package/dist/heart/daemon/habit-private-wake.js +61 -0
- package/dist/heart/daemon/health-monitor.js +154 -11
- package/dist/heart/daemon/hooks/agent-config-v2.js +33 -0
- package/dist/heart/daemon/hooks/bundle-meta.js +135 -1
- package/dist/heart/daemon/http-health-probe.js +80 -0
- package/dist/heart/daemon/human-command-screens.js +234 -0
- package/dist/heart/daemon/human-readiness.js +114 -0
- package/dist/heart/daemon/inner-status.js +78 -0
- package/dist/heart/daemon/interactive-repair.js +394 -0
- package/dist/heart/daemon/launchd.js +37 -8
- package/dist/heart/daemon/log-tailer.js +79 -10
- package/dist/heart/daemon/logs-prune.js +132 -0
- package/dist/heart/daemon/mcp-canary.js +524 -0
- package/dist/heart/daemon/message-router.js +65 -5
- package/dist/heart/daemon/migrate-to-desk.js +848 -0
- package/dist/heart/daemon/os-cron-deps.js +135 -0
- package/dist/heart/daemon/os-cron.js +22 -14
- package/dist/heart/daemon/ouro-bot-entry.js +6 -3
- package/dist/heart/daemon/ouro-entry.js +5 -2
- package/dist/heart/daemon/plugin-cli.js +432 -0
- package/dist/heart/daemon/process-manager.js +760 -61
- package/dist/heart/daemon/provider-discovery.js +137 -0
- package/dist/heart/daemon/provider-ping-progress.js +83 -0
- package/dist/heart/daemon/prunable-bundle.js +144 -0
- package/dist/heart/daemon/pulse.js +511 -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 +47 -14
- package/dist/heart/daemon/runtime-metadata.js +2 -30
- package/dist/heart/daemon/runtime-mode.js +13 -2
- package/dist/heart/daemon/safe-mode.js +161 -0
- package/dist/heart/daemon/sanctuary-acceptance-adapter.js +2654 -0
- package/dist/heart/daemon/sanctuary-acceptance-harness.js +1830 -0
- package/dist/heart/daemon/sanctuary-acceptance-marker.js +267 -0
- package/dist/heart/daemon/sanctuary-acceptance-scenarios.js +955 -0
- package/dist/heart/daemon/sanctuary-authority-codec.js +86 -0
- package/dist/heart/daemon/sanctuary-authority-epoch.js +281 -0
- package/dist/heart/daemon/sanctuary-authority-installation.js +161 -0
- package/dist/heart/daemon/sanctuary-authority-ledger.js +241 -0
- package/dist/heart/daemon/sanctuary-authority-root-lifecycle.js +1267 -0
- package/dist/heart/daemon/sanctuary-authority-vault-migration.js +79 -0
- package/dist/heart/daemon/sanctuary-bundle-migration.js +730 -0
- package/dist/heart/daemon/sanctuary-host-authority.js +1008 -0
- package/dist/heart/daemon/sanctuary-host-detached-supervisor.js +493 -0
- package/dist/heart/daemon/sanctuary-host-executor.js +670 -0
- package/dist/heart/daemon/sanctuary-host-linux-kernel.js +334 -0
- package/dist/heart/daemon/sanctuary-host-supervisor-entry.js +207 -0
- package/dist/heart/daemon/sanctuary-host-supervisor.js +95 -0
- package/dist/heart/daemon/sanctuary-package-management.js +103 -0
- package/dist/heart/daemon/sanctuary-scheduler-liveness.js +227 -0
- package/dist/heart/daemon/sanctuary-scheduler-origin.js +132 -0
- package/dist/heart/daemon/sanctuary-telegram-authority-entry.js +475 -0
- package/dist/heart/daemon/sanctuary-telegram-authority-gateway.js +585 -0
- package/dist/heart/daemon/sanctuary-telegram-authority-service.js +676 -0
- package/dist/heart/daemon/sense-manager.js +733 -48
- package/dist/heart/daemon/session-id-resolver.js +131 -0
- package/dist/heart/daemon/skill-management-installer.js +1 -1
- package/dist/heart/daemon/socket-client.js +180 -12
- package/dist/heart/daemon/stale-bundle-prune.js +113 -0
- package/dist/heart/daemon/startup-tui.js +339 -0
- package/dist/heart/daemon/supercronic-supervisor.js +282 -0
- package/dist/heart/daemon/task-scheduler.js +117 -39
- package/dist/heart/daemon/terminal-ui.js +503 -0
- package/dist/heart/daemon/thoughts.js +138 -54
- package/dist/heart/daemon/up-progress.js +366 -0
- package/dist/heart/daemon/vault-items.js +56 -0
- package/dist/heart/delegation.js +1 -4
- package/dist/heart/external-events/router.js +1516 -0
- package/dist/heart/failure-reports.js +336 -0
- package/dist/heart/file-lock.js +109 -0
- package/dist/heart/frontend-approval-runtime.js +506 -0
- package/dist/heart/frontend-journal.js +215 -0
- package/dist/heart/frontend-session-service.js +237 -0
- package/dist/heart/frontend-socket-client.js +164 -0
- package/dist/heart/frontend-socket.js +400 -0
- package/dist/heart/habits/habit-cancel.js +810 -0
- package/dist/heart/habits/habit-lifecycle.js +1327 -0
- package/dist/heart/habits/habit-migration.js +194 -0
- package/dist/heart/habits/habit-parser.js +278 -0
- package/dist/heart/habits/habit-runtime-state.js +93 -0
- package/dist/heart/habits/habit-scheduler.js +653 -0
- package/dist/heart/habits/habit-session-summary.js +318 -0
- package/dist/heart/habits/habit-session.js +636 -0
- package/dist/heart/{daemon → hatch}/hatch-flow.js +55 -62
- 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 +53 -18
- package/dist/heart/identity.js +230 -59
- package/dist/heart/kept-notes.js +289 -0
- package/dist/heart/kicks.js +1 -1
- package/dist/heart/machine-identity.js +175 -0
- package/dist/heart/mail-import-discovery.js +356 -0
- package/dist/heart/mailbox/mailbox-http-hooks.js +98 -0
- package/dist/heart/mailbox/mailbox-http-response.js +7 -0
- package/dist/heart/mailbox/mailbox-http-routes.js +398 -0
- package/dist/heart/mailbox/mailbox-http-static.js +103 -0
- package/dist/heart/mailbox/mailbox-http-transport.js +272 -0
- package/dist/heart/mailbox/mailbox-http.js +99 -0
- package/dist/heart/mailbox/mailbox-read.js +39 -0
- package/dist/heart/mailbox/mailbox-types.js +27 -0
- package/dist/heart/mailbox/mailbox-view.js +197 -0
- package/dist/heart/mailbox/readers/agent-machine.js +428 -0
- package/dist/heart/mailbox/readers/continuity-readers.js +329 -0
- package/dist/heart/mailbox/readers/mail.js +375 -0
- package/dist/heart/mailbox/readers/runtime-readers.js +823 -0
- package/dist/heart/mailbox/readers/sessions.js +231 -0
- package/dist/heart/mailbox/readers/shared.js +111 -0
- package/dist/heart/mcp/mcp-server.js +696 -0
- package/dist/heart/migrate-config.js +100 -0
- package/dist/heart/model-capabilities.js +83 -16
- package/dist/heart/orientation-frame.js +345 -0
- package/dist/heart/platform.js +81 -0
- package/dist/heart/primitives.js +1 -1
- package/dist/heart/private-runtime/decision-renderer.js +158 -0
- package/dist/heart/private-runtime/index.js +18 -0
- package/dist/heart/private-runtime/ledger.js +221 -0
- package/dist/heart/private-runtime/policy.js +260 -0
- package/dist/heart/private-runtime/types.js +2 -0
- package/dist/heart/progress-story.js +1 -1
- package/dist/heart/provider-attempt.js +137 -0
- package/dist/heart/provider-binding-resolver.js +273 -0
- package/dist/heart/provider-credentials.js +449 -0
- package/dist/heart/provider-failover.js +311 -0
- package/dist/heart/provider-models.js +96 -0
- package/dist/heart/provider-ping.js +265 -0
- package/dist/heart/provider-readiness-cache.js +119 -0
- package/dist/heart/provider-visibility.js +188 -0
- package/dist/heart/providers/anthropic-token.js +131 -0
- package/dist/heart/providers/anthropic.js +173 -60
- package/dist/heart/providers/azure.js +23 -11
- package/dist/heart/providers/error-classification.js +127 -0
- package/dist/heart/providers/github-copilot.js +38 -39
- package/dist/heart/providers/minimax-vlm.js +189 -0
- package/dist/heart/providers/minimax.js +26 -8
- package/dist/heart/providers/openai-codex-token.js +349 -0
- package/dist/heart/providers/openai-codex.js +55 -40
- package/dist/heart/providers/openai-compatible.js +177 -0
- package/dist/heart/reply-style.js +52 -0
- package/dist/heart/run-ledger.js +247 -0
- package/dist/heart/runtime-capability-check.js +170 -0
- package/dist/heart/runtime-credentials.js +607 -0
- package/dist/heart/runtime-cwd.js +87 -0
- package/dist/heart/sense-truth.js +24 -4
- package/dist/heart/session-activity.js +92 -24
- package/dist/heart/session-events.js +1504 -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-redaction-repair-cli-main.js +31 -0
- package/dist/heart/session-redaction-repair.js +552 -0
- package/dist/heart/session-stats-cli-main.js +5 -0
- package/dist/heart/session-stats.js +183 -0
- package/dist/heart/session-transcript.js +133 -0
- package/dist/heart/shelf-claims.js +78 -0
- package/dist/heart/shepherd-config.js +113 -0
- package/dist/heart/source-grounding.js +297 -0
- package/dist/heart/start-of-turn-packet.js +372 -0
- package/dist/heart/steward-policy.js +656 -0
- package/dist/heart/streaming.js +609 -207
- package/dist/heart/structured-output.js +196 -0
- package/dist/heart/sync-classification.js +176 -0
- package/dist/heart/sync-health.js +152 -0
- package/dist/heart/sync.js +726 -0
- package/dist/heart/target-resolution.js +11 -7
- package/dist/heart/tempo.js +93 -0
- package/dist/heart/temporal-view.js +41 -0
- package/dist/heart/timeouts.js +101 -0
- package/dist/heart/tool-activity-callbacks.js +59 -0
- package/dist/heart/tool-approval.js +382 -0
- package/dist/heart/tool-description.js +155 -0
- package/dist/heart/tool-friction.js +55 -0
- package/dist/heart/tool-loop.js +206 -0
- package/dist/heart/turn-context.js +496 -0
- package/dist/heart/turn-execution-lease.js +30 -0
- package/dist/heart/{daemon → versioning}/ouro-bot-global-installer.js +6 -5
- package/dist/heart/{daemon → versioning}/ouro-bot-wrapper.js +3 -2
- package/dist/heart/versioning/ouro-path-installer.js +432 -0
- package/dist/heart/versioning/ouro-recovery-launcher.js +76 -0
- package/dist/heart/versioning/ouro-version-manager.js +432 -0
- package/dist/heart/versioning/release-sources.js +173 -0
- package/dist/heart/versioning/self-update-defaults.js +294 -0
- package/dist/heart/versioning/self-update.js +196 -0
- package/dist/heart/{daemon → versioning}/staged-restart.js +40 -8
- package/dist/heart/{daemon → versioning}/update-checker.js +6 -1
- package/dist/heart/versioning/update-hooks.js +154 -0
- package/dist/heart/versioning/update-state.js +126 -0
- package/dist/heart/versioning/updater-launchd.js +150 -0
- package/dist/heart/versioning/version-intent.js +114 -0
- package/dist/heart/work-card.js +380 -0
- package/dist/mailbox-ui/assets/index-Cyv4mw3Y.js +1 -0
- package/dist/mailbox-ui/assets/index-Du_9G9WO.css +1 -0
- package/dist/mailbox-ui/assets/vendor-CcN1XpQ9.js +61 -0
- package/dist/mailbox-ui/index.html +16 -0
- package/dist/mailroom/attention.js +167 -0
- package/dist/mailroom/autonomy.js +209 -0
- package/dist/mailroom/blob-store.js +889 -0
- package/dist/mailroom/body-cache.js +61 -0
- package/dist/mailroom/cache-sync-cli.js +58 -0
- package/dist/mailroom/core.js +803 -0
- package/dist/mailroom/entry.js +160 -0
- package/dist/mailroom/file-store.js +568 -0
- package/dist/mailroom/hosted-cache-sync.js +384 -0
- package/dist/mailroom/mbox-import.js +393 -0
- package/dist/mailroom/migration.js +164 -0
- package/dist/mailroom/outbound.js +380 -0
- package/dist/mailroom/policy.js +263 -0
- package/dist/mailroom/reader.js +292 -0
- package/dist/mailroom/search-cache.js +513 -0
- package/dist/mailroom/search-relevance.js +319 -0
- package/dist/mailroom/smtp-ingress.js +176 -0
- package/dist/mailroom/source-state.js +176 -0
- package/dist/mailroom/thread.js +109 -0
- package/dist/mailroom/travel-extract.js +89 -0
- package/dist/mind/bundle-manifest.js +24 -2
- package/dist/mind/context.js +307 -139
- package/dist/mind/desk-section.js +362 -0
- package/dist/mind/diary-integrity.js +60 -0
- package/dist/mind/{memory.js → diary.js} +68 -77
- package/dist/mind/embedding-provider.js +60 -0
- package/dist/mind/file-state.js +179 -0
- package/dist/mind/friend-link-codes.js +209 -0
- package/dist/mind/friend-merge.js +330 -0
- package/dist/mind/{associative-recall.js → note-search.js} +47 -58
- package/dist/mind/obligation-steering.js +199 -3
- package/dist/mind/pending.js +36 -10
- package/dist/mind/prompt-budget.js +479 -0
- package/dist/mind/prompt-refresh.js +3 -2
- package/dist/mind/prompt.js +1085 -198
- package/dist/mind/provenance-trust.js +26 -0
- package/dist/mind/record-paths.js +312 -0
- package/dist/mind/scrutiny.js +173 -0
- package/dist/mind/session-transaction.js +564 -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 +55 -35
- package/dist/nerves/coverage/source-scanner.js +4 -4
- package/dist/nerves/event-buffer.js +111 -0
- package/dist/nerves/index.js +293 -9
- 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 +159 -0
- package/dist/nerves/runtime.js +17 -1
- package/dist/repertoire/ado-client.js +15 -56
- package/dist/repertoire/ado-semantic.js +16 -10
- package/dist/repertoire/api-client.js +97 -0
- package/dist/repertoire/bitwarden-store.js +1201 -0
- package/dist/repertoire/bundle-templates.js +71 -0
- package/dist/repertoire/bw-installer.js +231 -0
- package/dist/repertoire/coding/codex-jsonl.js +64 -0
- package/dist/repertoire/coding/context-pack.js +331 -0
- package/dist/repertoire/coding/feedback.js +174 -32
- package/dist/repertoire/coding/index.js +15 -3
- package/dist/repertoire/coding/manager.js +159 -10
- package/dist/repertoire/coding/spawner.js +40 -11
- package/dist/repertoire/coding/tools.js +182 -18
- package/dist/repertoire/coding/workbench-client.js +272 -0
- package/dist/repertoire/coding/workbench-manager.js +346 -0
- package/dist/repertoire/commerce-errors.js +109 -0
- package/dist/repertoire/commerce-self-test.js +156 -0
- package/dist/repertoire/credential-access.js +183 -0
- package/dist/repertoire/desk/classifier.js +362 -0
- package/dist/repertoire/duffel-client.js +185 -0
- package/dist/repertoire/github-client.js +14 -55
- package/dist/repertoire/graph-client.js +11 -52
- package/dist/repertoire/guardrails.js +968 -20
- package/dist/repertoire/house-sweep.js +469 -0
- package/dist/repertoire/mcp-client.js +58 -5
- package/dist/repertoire/mcp-manager.js +427 -139
- package/dist/repertoire/mcp-tools.js +132 -0
- package/dist/repertoire/mcp-write-guard.js +46 -0
- package/dist/repertoire/plugin-mcp.js +175 -0
- package/dist/repertoire/plugins.js +253 -0
- package/dist/repertoire/relationship-authorization.js +322 -0
- package/dist/repertoire/sanctuary-container-policy.js +46 -0
- package/dist/repertoire/shell-sessions.js +134 -0
- package/dist/repertoire/skills.js +48 -4
- package/dist/repertoire/stripe-client.js +131 -0
- package/dist/repertoire/tool-arguments.js +182 -0
- package/dist/repertoire/tool-results.js +29 -0
- package/dist/repertoire/tools-a2a.js +699 -0
- package/dist/repertoire/tools-attachments.js +322 -0
- package/dist/repertoire/tools-awaiting.js +872 -0
- package/dist/repertoire/tools-base.js +83 -1168
- package/dist/repertoire/tools-bluebubbles.js +2 -0
- package/dist/repertoire/tools-bridge.js +144 -0
- package/dist/repertoire/tools-bundle.js +995 -0
- package/dist/repertoire/tools-commerce.js +253 -0
- package/dist/repertoire/tools-config.js +186 -0
- package/dist/repertoire/tools-continuity.js +575 -0
- package/dist/repertoire/tools-credential.js +383 -0
- package/dist/repertoire/tools-escalation.js +84 -0
- package/dist/repertoire/tools-evolution.js +527 -0
- package/dist/repertoire/tools-files.js +344 -0
- package/dist/repertoire/tools-flight.js +290 -0
- package/dist/repertoire/tools-flow.js +119 -0
- package/dist/repertoire/tools-friend-link.js +57 -0
- package/dist/repertoire/tools-github.js +3 -8
- package/dist/repertoire/tools-habits.js +103 -0
- package/dist/repertoire/tools-house-care.js +174 -0
- package/dist/repertoire/tools-mail.js +1828 -0
- package/dist/repertoire/tools-notes.js +496 -0
- package/dist/repertoire/tools-obligations.js +182 -0
- package/dist/repertoire/tools-orientation.js +31 -0
- package/dist/repertoire/tools-record.js +482 -0
- package/dist/repertoire/tools-rsvp.js +139 -0
- package/dist/repertoire/tools-runtime.js +238 -0
- package/dist/repertoire/tools-sanctuary-host.js +202 -0
- package/dist/repertoire/tools-session.js +1133 -0
- package/dist/repertoire/tools-shell.js +149 -0
- package/dist/repertoire/tools-shepherd.js +262 -0
- package/dist/repertoire/tools-steward-policy.js +147 -0
- package/dist/repertoire/tools-stripe.js +224 -0
- package/dist/repertoire/tools-surface.js +369 -0
- package/dist/repertoire/tools-teams.js +12 -39
- package/dist/repertoire/tools-telegram-contacts.js +37 -0
- package/dist/repertoire/tools-travel.js +125 -0
- package/dist/repertoire/tools-trip.js +982 -0
- package/dist/repertoire/tools-unraid.js +556 -0
- package/dist/repertoire/tools-user-profile.js +146 -0
- package/dist/repertoire/tools-vault.js +40 -0
- package/dist/repertoire/tools-voice-local.js +198 -0
- package/dist/repertoire/tools-voice.js +145 -0
- package/dist/repertoire/tools.js +514 -156
- package/dist/repertoire/travel-api-client.js +360 -0
- package/dist/repertoire/unraid-client.js +155 -0
- package/dist/repertoire/unraid-restart.js +339 -0
- package/dist/repertoire/user-profile.js +131 -0
- package/dist/repertoire/vault-setup.js +246 -0
- package/dist/repertoire/vault-unlock.js +686 -0
- package/dist/rsvp/aisleplanner-client.js +354 -0
- package/dist/rsvp/cli.js +936 -0
- package/dist/rsvp/config.js +327 -0
- package/dist/rsvp/cutover.js +433 -0
- package/dist/rsvp/diagnostics.js +232 -0
- package/dist/rsvp/diff-renderer.js +176 -0
- package/dist/rsvp/habit-policy.js +110 -0
- package/dist/rsvp/habit-stage.js +123 -0
- package/dist/rsvp/incident-bundle.js +97 -0
- package/dist/rsvp/migration.js +204 -0
- package/dist/rsvp/native-habit-runner.js +630 -0
- package/dist/rsvp/outbound-state.js +628 -0
- package/dist/rsvp/query.js +118 -0
- package/dist/rsvp/replay.js +233 -0
- package/dist/rsvp/snapshot.js +232 -0
- package/dist/rsvp/spend-ledger.js +175 -0
- package/dist/scripts/claude-code-hook.js +41 -0
- package/dist/scripts/claude-code-stop-hook.js +47 -0
- package/dist/senses/a2a-entry.js +132 -0
- package/dist/senses/acp-server.js +386 -0
- package/dist/senses/attention-queue.js +186 -0
- package/dist/senses/await-turn-message.js +65 -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} +379 -41
- package/dist/senses/bluebubbles/context-packet.js +207 -0
- package/dist/senses/bluebubbles/context-smoke.js +144 -0
- package/dist/senses/bluebubbles/entry.js +90 -0
- package/dist/senses/bluebubbles/host-account-verifier.js +88 -0
- package/dist/senses/{bluebubbles-inbound-log.js → bluebubbles/inbound-log.js} +21 -3
- package/dist/senses/bluebubbles/index.js +3878 -0
- package/dist/senses/bluebubbles/latest-turn.js +311 -0
- package/dist/senses/{bluebubbles-media.js → bluebubbles/media.js} +121 -71
- package/dist/senses/{bluebubbles-model.js → bluebubbles/model.js} +159 -28
- package/dist/senses/{bluebubbles-mutation-log.js → bluebubbles/mutation-log.js} +24 -13
- package/dist/senses/bluebubbles/outbound-state.js +308 -0
- package/dist/senses/bluebubbles/processed-log.js +133 -0
- package/dist/senses/bluebubbles/reaction-policy.js +47 -0
- package/dist/senses/bluebubbles/replay.js +284 -0
- package/dist/senses/{bluebubbles-runtime-state.js → bluebubbles/runtime-state.js} +30 -2
- package/dist/senses/bluebubbles/semantic-receipts.js +1986 -0
- package/dist/senses/{bluebubbles-session-cleanup.js → bluebubbles/session-cleanup.js} +1 -1
- package/dist/senses/bluebubbles/webhook-registration.js +257 -0
- package/dist/senses/bluebubbles-meta-guard.js +39 -0
- package/dist/senses/cli/bracketed-paste.js +82 -0
- package/dist/senses/cli/image-paste.js +287 -0
- package/dist/senses/cli/image-ref-navigation.js +75 -0
- package/dist/senses/cli/ink-app.js +156 -0
- package/dist/senses/cli/inline-diff.js +64 -0
- package/dist/senses/cli/input-keys.js +174 -0
- package/dist/senses/cli/kill-ring.js +86 -0
- package/dist/senses/cli/message-list.js +51 -0
- package/dist/senses/cli/ouro-tui.js +607 -0
- package/dist/senses/cli/spinner-imperative.js +135 -0
- package/dist/senses/cli/spinner.js +101 -0
- package/dist/senses/cli/status-line.js +60 -0
- package/dist/senses/cli/streaming-markdown.js +526 -0
- package/dist/senses/cli/tool-display.js +85 -0
- package/dist/senses/cli/tool-render.js +85 -0
- package/dist/senses/cli/tui-store.js +241 -0
- package/dist/senses/cli/virtual-list.js +35 -0
- package/dist/senses/cli-entry.js +60 -8
- package/dist/senses/cli.js +601 -304
- package/dist/senses/commands.js +66 -3
- package/dist/senses/context-packet-ledger.js +371 -0
- package/dist/senses/context-packets.js +154 -0
- package/dist/senses/habit-lifecycle-guard.js +24 -0
- package/dist/senses/habit-turn-message.js +160 -0
- package/dist/senses/ingress-evidence.js +2 -0
- package/dist/senses/mail-entry.js +66 -0
- package/dist/senses/mail.js +524 -0
- package/dist/senses/pipeline.js +1158 -204
- package/dist/senses/private-runtime-worker.js +859 -0
- package/dist/senses/private-runtime.js +1627 -0
- package/dist/senses/proactive-content-guard.js +51 -0
- package/dist/senses/root-host-approval-port.js +345 -0
- package/dist/senses/root-host-approval-runtime.js +393 -0
- package/dist/senses/sanctuary-authority-resident.js +102 -0
- package/dist/senses/sanctuary-download-credit-presentation.js +31 -0
- package/dist/senses/sanctuary-full-visibility-contract.js +302 -0
- package/dist/senses/sanctuary-grounding.js +118 -0
- package/dist/senses/sanctuary-health-acceptance-probe-entry.js +12 -0
- package/dist/senses/sanctuary-health-acceptance-probe.js +774 -0
- package/dist/senses/sanctuary-health-runner.js +104 -0
- package/dist/senses/sanctuary-health.js +422 -0
- package/dist/senses/sanctuary-install-state-contract.js +169 -0
- package/dist/senses/sanctuary-interactive-control.js +410 -0
- package/dist/senses/sanctuary-media-catalog-contract.js +259 -0
- package/dist/senses/sanctuary-media-optimization.js +514 -0
- package/dist/senses/sanctuary-runtime.js +369 -0
- package/dist/senses/sanctuary-sab.js +85 -0
- package/dist/senses/sanctuary-storage-optimization-contract.js +32 -0
- package/dist/senses/shared-turn.js +793 -0
- package/dist/senses/shepherd/client.js +196 -0
- package/dist/senses/shepherd/cmux.js +344 -0
- package/dist/senses/shepherd/herdr.js +315 -0
- package/dist/senses/shepherd/host.js +14 -0
- package/dist/senses/shepherd/judge.js +137 -0
- package/dist/senses/shepherd/owner-notice.js +245 -0
- package/dist/senses/shepherd/redact.js +66 -0
- package/dist/senses/shepherd/returns.js +79 -0
- package/dist/senses/shepherd/sense.js +309 -0
- package/dist/senses/shepherd-entry.js +78 -0
- package/dist/senses/surface-tool.js +109 -0
- package/dist/senses/teams-entry.js +68 -8
- package/dist/senses/teams.js +598 -233
- package/dist/senses/telegram-admission.js +718 -0
- package/dist/senses/telegram-approval-runtime.js +793 -0
- package/dist/senses/telegram-attachments.js +186 -0
- package/dist/senses/telegram-audit-ledger.js +220 -0
- package/dist/senses/telegram-authority-transport.js +231 -0
- package/dist/senses/telegram-client.js +1749 -0
- package/dist/senses/telegram-effect-adapter.js +900 -0
- package/dist/senses/telegram-entry.js +83 -0
- package/dist/senses/telegram-handoff.js +80 -0
- package/dist/senses/telegram.js +2405 -0
- package/dist/senses/trust-gate.js +104 -9
- package/dist/senses/voice/audio-playback.js +237 -0
- package/dist/senses/voice/audio-routing.js +119 -0
- package/dist/senses/voice/call-auth.js +250 -0
- package/dist/senses/voice/elevenlabs.js +202 -0
- package/dist/senses/voice/floor-control.js +431 -0
- package/dist/senses/voice/floor-controller.js +115 -0
- package/dist/senses/voice/golden-path.js +116 -0
- package/dist/senses/voice/index.js +29 -0
- package/dist/senses/voice/local-audio-control.js +344 -0
- package/dist/senses/voice/local-audio-devices.js +516 -0
- package/dist/senses/voice/local-audio-disclosure.js +174 -0
- package/dist/senses/voice/local-audio-identity.js +103 -0
- package/dist/senses/voice/local-audio-join.js +325 -0
- package/dist/senses/voice/local-audio-mute.js +199 -0
- package/dist/senses/voice/local-audio-notify.js +121 -0
- package/dist/senses/voice/local-audio-prompts.js +116 -0
- package/dist/senses/voice/local-audio-transport.js +427 -0
- package/dist/senses/voice/local-audio.js +281 -0
- package/dist/senses/voice/local-media-stream.js +145 -0
- package/dist/senses/voice/meeting.js +113 -0
- package/dist/senses/voice/outbound.js +190 -0
- package/dist/senses/voice/phone.js +33 -0
- package/dist/senses/voice/playback.js +139 -0
- package/dist/senses/voice/process-guards.js +51 -0
- package/dist/senses/voice/realtime-eval.js +496 -0
- package/dist/senses/voice/realtime-tool-schema.js +195 -0
- package/dist/senses/voice/realtime-trace.js +531 -0
- package/dist/senses/voice/transcript.js +70 -0
- package/dist/senses/voice/turn.js +191 -0
- package/dist/senses/voice/twilio-phone-runtime.js +836 -0
- package/dist/senses/voice/twilio-phone.js +5454 -0
- package/dist/senses/voice/types.js +2 -0
- package/dist/senses/voice/whisper.js +161 -0
- package/dist/senses/voice-entry.js +83 -0
- package/dist/senses/voice-realtime-eval-command.js +99 -0
- package/dist/senses/voice-realtime-eval-entry.js +21 -0
- package/dist/senses/voice-twilio-entry.js +87 -0
- package/dist/trips/core.js +138 -0
- package/dist/trips/store.js +265 -0
- package/dist/util/frontmatter.js +69 -0
- package/npm-shrinkwrap.json +8100 -0
- package/package.json +74 -13
- package/skills/agent-commerce.md +113 -0
- package/skills/browser-navigation.md +117 -0
- package/skills/commerce-setup-guide.md +116 -0
- package/skills/commerce-setup.md +84 -0
- package/skills/configure-dev-tools.md +99 -0
- package/skills/travel-planning.md +138 -0
- package/dist/heart/daemon/auth-flow.js +0 -430
- package/dist/heart/daemon/ouro-path-installer.js +0 -260
- package/dist/heart/daemon/ouro-version-manager.js +0 -171
- package/dist/heart/daemon/update-hooks.js +0 -138
- package/dist/heart/obligations.js +0 -191
- package/dist/heart/safe-workspace.js +0 -362
- package/dist/heart/session-recall.js +0 -116
- package/dist/mind/friends/channel.js +0 -84
- package/dist/mind/friends/group-context.js +0 -144
- package/dist/mind/friends/resolver.js +0 -84
- package/dist/mind/friends/store-file.js +0 -190
- package/dist/mind/friends/store.js +0 -4
- package/dist/mind/friends/tokens.js +0 -26
- package/dist/mind/friends/trust-explanation.js +0 -74
- package/dist/mind/friends/types.js +0 -29
- package/dist/repertoire/tasks/board.js +0 -134
- package/dist/repertoire/tasks/index.js +0 -224
- package/dist/repertoire/tasks/lifecycle.js +0 -80
- package/dist/repertoire/tasks/middleware.js +0 -65
- package/dist/repertoire/tasks/parser.js +0 -173
- package/dist/repertoire/tasks/scanner.js +0 -132
- package/dist/repertoire/tasks/transitions.js +0 -144
- package/dist/senses/bluebubbles-entry.js +0 -13
- package/dist/senses/bluebubbles.js +0 -1181
- package/dist/senses/debug-activity.js +0 -154
- package/dist/senses/inner-dialog-worker.js +0 -90
- package/dist/senses/inner-dialog.js +0 -525
- package/subagents/README.md +0 -7
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/basilisk.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jafar.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/jormungandr.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/kaa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/medusa.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/monty.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/nagini.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/ouroboros.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/python.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/quetzalcoatl.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/sir-hiss.md +0 -0
- /package/{AdoptionSpecialist.ouro → SerpentGuide.ouro}/psyche/identities/the-snake.md +0 -0
- /package/dist/{repertoire/tasks/types.js → heart/attachments/sources/adapter.js} +0 -0
- /package/dist/heart/{daemon → hatch}/hatch-animation.js +0 -0
- /package/dist/heart/{daemon → hatch}/specialist-orchestrator.js +0 -0
- /package/dist/heart/{daemon → versioning}/ouro-uti.js +0 -0
- /package/dist/heart/{daemon → versioning}/wrapper-publish-guard.js +0 -0
package/dist/senses/teams.js
CHANGED
|
@@ -34,12 +34,17 @@ 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.
|
|
44
|
+
exports.reasoningStatus = reasoningStatus;
|
|
41
45
|
exports.withConversationLock = withConversationLock;
|
|
42
46
|
exports.handleTeamsMessage = handleTeamsMessage;
|
|
47
|
+
exports.createTeamsSdkLogger = createTeamsSdkLogger;
|
|
43
48
|
exports.sendProactiveTeamsMessageToSession = sendProactiveTeamsMessageToSession;
|
|
44
49
|
exports.drainAndSendPendingTeams = drainAndSendPendingTeams;
|
|
45
50
|
exports.startTeamsApp = startTeamsApp;
|
|
@@ -48,7 +53,7 @@ const teams_apps_1 = require("@microsoft/teams.apps");
|
|
|
48
53
|
const teams_dev_1 = require("@microsoft/teams.dev");
|
|
49
54
|
const core_1 = require("../heart/core");
|
|
50
55
|
const tools_1 = require("../repertoire/tools");
|
|
51
|
-
const
|
|
56
|
+
const friends_1 = require("@ouro.bot/friends");
|
|
52
57
|
const config_1 = require("../heart/config");
|
|
53
58
|
const prompt_1 = require("../mind/prompt");
|
|
54
59
|
const phrases_1 = require("../mind/phrases");
|
|
@@ -58,20 +63,31 @@ const context_1 = require("../mind/context");
|
|
|
58
63
|
const commands_1 = require("./commands");
|
|
59
64
|
const nerves_1 = require("../nerves");
|
|
60
65
|
const runtime_1 = require("../nerves/runtime");
|
|
61
|
-
const
|
|
62
|
-
const types_1 = require("../mind/friends/types");
|
|
63
|
-
const resolver_1 = require("../mind/friends/resolver");
|
|
64
|
-
const tokens_1 = require("../mind/friends/tokens");
|
|
66
|
+
const proactive_content_guard_1 = require("./proactive-content-guard");
|
|
65
67
|
const turn_coordinator_1 = require("../heart/turn-coordinator");
|
|
66
68
|
const identity_1 = require("../heart/identity");
|
|
67
69
|
const mcp_manager_1 = require("../repertoire/mcp-manager");
|
|
68
70
|
const progress_story_1 = require("../heart/progress-story");
|
|
71
|
+
const tool_activity_callbacks_1 = require("../heart/tool-activity-callbacks");
|
|
72
|
+
const commands_2 = require("./commands");
|
|
69
73
|
const http = __importStar(require("http"));
|
|
70
74
|
const path = __importStar(require("path"));
|
|
71
75
|
const trust_gate_1 = require("./trust-gate");
|
|
72
76
|
const pipeline_1 = require("./pipeline");
|
|
77
|
+
const session_transaction_1 = require("../mind/session-transaction");
|
|
78
|
+
const teamsFailoverStates = new Map();
|
|
73
79
|
const pending_1 = require("../mind/pending");
|
|
74
80
|
const continuity_1 = require("./continuity");
|
|
81
|
+
// AIGeneratedContent entity and feedbackLoopEnabled channelData for all outbound
|
|
82
|
+
// Teams messages. Required by Teams AI UX best practices.
|
|
83
|
+
function aiLabelEntities() {
|
|
84
|
+
return [{
|
|
85
|
+
type: "https://schema.org/Message",
|
|
86
|
+
"@type": "Message",
|
|
87
|
+
"@context": "https://schema.org",
|
|
88
|
+
additionalType: ["AIGeneratedContent"],
|
|
89
|
+
}];
|
|
90
|
+
}
|
|
75
91
|
// Strip @mention markup from incoming messages.
|
|
76
92
|
// Removes <at>...</at> tags and trims extra whitespace.
|
|
77
93
|
// Fallback safety net -- the SDK's activity.mentions.stripText should handle
|
|
@@ -126,6 +142,46 @@ function splitMessage(text, maxLen) {
|
|
|
126
142
|
}
|
|
127
143
|
return chunks;
|
|
128
144
|
}
|
|
145
|
+
// Sanitize user-provided feedback comments: truncate, strip control chars and newlines.
|
|
146
|
+
function sanitizeFeedbackComment(comment) {
|
|
147
|
+
const cleaned = comment.replace(/[\x00-\x1f\n\r]/g, "");
|
|
148
|
+
return cleaned.length > 200 ? cleaned.slice(0, 200) : cleaned;
|
|
149
|
+
}
|
|
150
|
+
// Build synthetic message text from a Teams feedback reaction.
|
|
151
|
+
function buildFeedbackSyntheticText(reaction, comment) {
|
|
152
|
+
const emoji = reaction === "like" ? "thumbs-up" : "thumbs-down";
|
|
153
|
+
if (comment) {
|
|
154
|
+
const sanitized = sanitizeFeedbackComment(comment);
|
|
155
|
+
return `[reacted with ${emoji} to your message: "${sanitized}"]`;
|
|
156
|
+
}
|
|
157
|
+
return `[reacted with ${emoji} to your message]`;
|
|
158
|
+
}
|
|
159
|
+
// Build a welcome Adaptive Card with prompt starters for new bot installs.
|
|
160
|
+
function buildWelcomeCard() {
|
|
161
|
+
const promptStarters = [
|
|
162
|
+
"What can you help me with?",
|
|
163
|
+
"Tell me about yourself",
|
|
164
|
+
"What's on my calendar today?",
|
|
165
|
+
"Summarize my recent emails",
|
|
166
|
+
];
|
|
167
|
+
return {
|
|
168
|
+
type: "AdaptiveCard",
|
|
169
|
+
version: "1.5",
|
|
170
|
+
body: [
|
|
171
|
+
{
|
|
172
|
+
type: "TextBlock",
|
|
173
|
+
text: "Hey! I'm here and ready to help. Try one of these to get started, or just ask me anything.",
|
|
174
|
+
wrap: true,
|
|
175
|
+
size: "Medium",
|
|
176
|
+
},
|
|
177
|
+
],
|
|
178
|
+
actions: promptStarters.map((prompt) => ({
|
|
179
|
+
type: "Action.Submit",
|
|
180
|
+
title: prompt,
|
|
181
|
+
data: { msteams: { type: "messageBack", text: prompt, displayText: prompt } },
|
|
182
|
+
})),
|
|
183
|
+
};
|
|
184
|
+
}
|
|
129
185
|
// Create Teams-specific callbacks for the agent loop.
|
|
130
186
|
// The SDK handles cumulative text, debouncing (500ms), and the streaming
|
|
131
187
|
// protocol (streamSequence, streamId, informative/streaming/final types).
|
|
@@ -138,24 +194,43 @@ function splitMessage(text, maxLen) {
|
|
|
138
194
|
// (transient status) or safeSend (terminal errors). Reasoning is accumulated
|
|
139
195
|
// and periodically pushed via safeUpdate on the same flush timer tick.
|
|
140
196
|
function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
197
|
+
const MIN_INITIAL_CHARS = 20;
|
|
141
198
|
let stopped = false; // set when stream signals cancellation (403)
|
|
142
199
|
let hadToolRun = false;
|
|
143
200
|
let hadRealOutput = false; // true once reasoning/tool output shown; suppresses phrases
|
|
144
201
|
let reasoningBuf = ""; // accumulated reasoning text for status display
|
|
202
|
+
let totalEmitted = 0; // cumulative chars emitted via safeEmit (for >4000 finalization)
|
|
203
|
+
let streamFinalized = false; // true after stream.close() — subsequent flushes go to safeSend
|
|
145
204
|
let textBuffer = ""; // accumulated text output for chunked streaming
|
|
146
205
|
let streamHasContent = false; // tracks whether primary output has received content
|
|
206
|
+
let firstContentEmitted = false; // true after first content push — disables MIN_INITIAL_CHARS threshold
|
|
147
207
|
let phraseTimer = null;
|
|
148
208
|
let lastPhrase = "";
|
|
149
209
|
let flushTimer = null;
|
|
150
210
|
const flushInterval = options?.flushIntervalMs ?? exports.DEFAULT_FLUSH_INTERVAL_MS;
|
|
211
|
+
// The SDK merges every chunk's entities into the final message, and Teams rejects a final
|
|
212
|
+
// message with more than one root message entity (400 BadSyntax), so only the first chunk
|
|
213
|
+
// carries the AI label.
|
|
214
|
+
let aiLabelAttached = false;
|
|
215
|
+
function streamChunk(text) {
|
|
216
|
+
if (aiLabelAttached)
|
|
217
|
+
return { type: "message", text, channelData: { feedbackLoopEnabled: true } };
|
|
218
|
+
aiLabelAttached = true;
|
|
219
|
+
return { type: "message", text, entities: aiLabelEntities(), channelData: { feedbackLoopEnabled: true } };
|
|
220
|
+
}
|
|
151
221
|
// Track whether reasoning has changed since last flush (avoid redundant updates).
|
|
152
222
|
let lastFlushedReasoningLen = 0;
|
|
223
|
+
let lastReasoningStatus = "";
|
|
153
224
|
// Periodic tick: flush text buffer and push reasoning updates.
|
|
154
225
|
function flushTick() {
|
|
155
226
|
flushTextBuffer();
|
|
156
227
|
if (reasoningBuf.length > lastFlushedReasoningLen) {
|
|
157
|
-
safeUpdate(reasoningBuf);
|
|
158
228
|
lastFlushedReasoningLen = reasoningBuf.length;
|
|
229
|
+
const status = reasoningStatus(reasoningBuf);
|
|
230
|
+
if (status && status !== lastReasoningStatus) {
|
|
231
|
+
lastReasoningStatus = status;
|
|
232
|
+
safeUpdate(status);
|
|
233
|
+
}
|
|
159
234
|
}
|
|
160
235
|
}
|
|
161
236
|
// Start the periodic flush timer. Idempotent -- no-op if already running.
|
|
@@ -172,9 +247,16 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
172
247
|
}
|
|
173
248
|
}
|
|
174
249
|
// Mark stream as broken and abort the agent loop.
|
|
175
|
-
function markStopped() {
|
|
250
|
+
function markStopped(where, err) {
|
|
176
251
|
if (stopped)
|
|
177
252
|
return;
|
|
253
|
+
(0, runtime_1.emitNervesEvent)({
|
|
254
|
+
level: "warn",
|
|
255
|
+
component: "senses",
|
|
256
|
+
event: "teams.stream_stopped",
|
|
257
|
+
message: `teams stream stopped at ${where}`,
|
|
258
|
+
meta: { where, error: err instanceof Error ? err.message : err === undefined ? null : String(err), conversationId: options?.conversationId ?? null },
|
|
259
|
+
});
|
|
178
260
|
stopped = true;
|
|
179
261
|
stopPhraseRotation();
|
|
180
262
|
stopFlushTimer();
|
|
@@ -190,21 +272,22 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
190
272
|
// catch its rejection so we detect a dead stream and abort the agent loop.
|
|
191
273
|
function catchAsync(result) {
|
|
192
274
|
if (result && typeof result.catch === "function") {
|
|
193
|
-
result.catch(() => markStopped());
|
|
275
|
+
result.catch((err) => markStopped("stream-async", err));
|
|
194
276
|
}
|
|
195
277
|
}
|
|
196
|
-
// Safely emit a text delta to the stream.
|
|
278
|
+
// Safely emit a text delta to the stream with AI labels.
|
|
197
279
|
// On error (e.g. 403 from Teams stop button), abort the controller.
|
|
198
280
|
function safeEmit(text) {
|
|
199
281
|
/* v8 ignore next -- defensive guard: stopped set by prior 403; tested via flush abort path @preserve */
|
|
200
282
|
if (stopped)
|
|
201
283
|
return;
|
|
202
284
|
try {
|
|
203
|
-
catchAsync(stream.emit(text));
|
|
285
|
+
catchAsync(stream.emit(streamChunk(text)));
|
|
204
286
|
streamHasContent = true;
|
|
287
|
+
totalEmitted += text.length;
|
|
205
288
|
}
|
|
206
|
-
catch {
|
|
207
|
-
markStopped();
|
|
289
|
+
catch (err) {
|
|
290
|
+
markStopped("safeEmit", err);
|
|
208
291
|
}
|
|
209
292
|
}
|
|
210
293
|
// Awaitable emit — returns true if the emit succeeded, false if it failed.
|
|
@@ -216,7 +299,28 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
216
299
|
try {
|
|
217
300
|
// stream.emit() is typed as void but the Teams SDK returns a Promise
|
|
218
301
|
// internally (async HTTP). Cast to capture the result for awaiting.
|
|
219
|
-
const result = stream.emit(text);
|
|
302
|
+
const result = stream.emit(streamChunk(text));
|
|
303
|
+
streamHasContent = true;
|
|
304
|
+
if (result && typeof result.then === "function") {
|
|
305
|
+
await result;
|
|
306
|
+
}
|
|
307
|
+
return true;
|
|
308
|
+
}
|
|
309
|
+
catch (err) {
|
|
310
|
+
markStopped("tryEmit", err);
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
// Non-aborting awaitable emit — returns true on success, false on failure WITHOUT
|
|
315
|
+
// calling markStopped() / aborting the controller. Used by flushNow (speak) so a
|
|
316
|
+
// primary-stream failure followed by a successful sendMessage fallback does NOT
|
|
317
|
+
// poison the rest of the turn. tryEmit's abort-on-failure behavior is correct for
|
|
318
|
+
// end-of-turn flush() (no fallback path forward) but wrong for mid-turn speak,
|
|
319
|
+
// which has a sendMessage fallback that may still succeed. Caller (flushNow) is
|
|
320
|
+
// responsible for the `!stopped` precondition; no defensive guard here.
|
|
321
|
+
async function tryEmitNoAbort(text) {
|
|
322
|
+
try {
|
|
323
|
+
const result = stream.emit(streamChunk(text));
|
|
220
324
|
streamHasContent = true;
|
|
221
325
|
if (result && typeof result.then === "function") {
|
|
222
326
|
await result;
|
|
@@ -224,7 +328,6 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
224
328
|
return true;
|
|
225
329
|
}
|
|
226
330
|
catch {
|
|
227
|
-
markStopped();
|
|
228
331
|
return false;
|
|
229
332
|
}
|
|
230
333
|
}
|
|
@@ -236,8 +339,8 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
236
339
|
try {
|
|
237
340
|
catchAsync(stream.update(text));
|
|
238
341
|
}
|
|
239
|
-
catch {
|
|
240
|
-
markStopped();
|
|
342
|
+
catch (err) {
|
|
343
|
+
markStopped("safeUpdate", err);
|
|
241
344
|
}
|
|
242
345
|
}
|
|
243
346
|
// Safely send a separate message via sendMessage (for content after first emit).
|
|
@@ -252,11 +355,11 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
252
355
|
// Chain is idle -- start the send synchronously and mark busy
|
|
253
356
|
sendChainBusy = true;
|
|
254
357
|
try {
|
|
255
|
-
sendChain = sendMessage(text).catch(() => markStopped()).finally(() => { sendChainBusy = false; });
|
|
358
|
+
sendChain = sendMessage(text).catch((err) => markStopped("send", err)).finally(() => { sendChainBusy = false; });
|
|
256
359
|
}
|
|
257
|
-
catch {
|
|
360
|
+
catch (err) {
|
|
258
361
|
sendChainBusy = false;
|
|
259
|
-
markStopped();
|
|
362
|
+
markStopped("send", err);
|
|
260
363
|
}
|
|
261
364
|
}
|
|
262
365
|
else {
|
|
@@ -265,18 +368,56 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
265
368
|
if (stopped)
|
|
266
369
|
return;
|
|
267
370
|
return sendMessage(text);
|
|
268
|
-
}).catch(() => markStopped());
|
|
371
|
+
}).catch((err) => markStopped("send-queued", err));
|
|
269
372
|
}
|
|
270
373
|
}
|
|
271
374
|
// Flush accumulated text buffer via safeEmit. The Teams SDK accumulates
|
|
272
375
|
// emitted text into a single streaming message (cumulative), so every
|
|
273
376
|
// periodic flush appends to the same response — not separate messages.
|
|
274
377
|
// No preemptive splitting — sends full text. Error recovery happens in flush().
|
|
378
|
+
// Hybrid MIN_INITIAL_CHARS: hold back until >= MIN_INITIAL_CHARS accumulated
|
|
379
|
+
// before the first content emit, so phrase rotation shows while real content
|
|
380
|
+
// buffers. After first emit, flush normally (no threshold).
|
|
275
381
|
function flushTextBuffer() {
|
|
276
382
|
if (!textBuffer)
|
|
277
383
|
return;
|
|
384
|
+
if (!firstContentEmitted && textBuffer.length < MIN_INITIAL_CHARS)
|
|
385
|
+
return;
|
|
386
|
+
// Proactive >4000 finalization: if cumulative emitted + buffer >= RECOVERY_CHUNK_SIZE,
|
|
387
|
+
// finalize the stream and send overflow via safeSend (follow-up message).
|
|
388
|
+
if (!streamFinalized && totalEmitted + textBuffer.length >= RECOVERY_CHUNK_SIZE) {
|
|
389
|
+
const remaining = RECOVERY_CHUNK_SIZE - totalEmitted;
|
|
390
|
+
/* v8 ignore next 2 -- defensive: remaining always > 0 because finalization runs once @preserve */
|
|
391
|
+
if (remaining > 0)
|
|
392
|
+
safeEmit(textBuffer.slice(0, remaining));
|
|
393
|
+
try {
|
|
394
|
+
stream.close();
|
|
395
|
+
}
|
|
396
|
+
catch { /* stream may already be dead */ }
|
|
397
|
+
streamFinalized = true;
|
|
398
|
+
/* v8 ignore next -- defensive ternary: remaining always > 0 at first finalization @preserve */
|
|
399
|
+
const overflow = textBuffer.slice(remaining > 0 ? remaining : 0);
|
|
400
|
+
textBuffer = "";
|
|
401
|
+
if (overflow)
|
|
402
|
+
safeSend(overflow);
|
|
403
|
+
if (!firstContentEmitted) {
|
|
404
|
+
firstContentEmitted = true;
|
|
405
|
+
stopPhraseRotation();
|
|
406
|
+
}
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
if (streamFinalized) {
|
|
410
|
+
// After finalization, all content goes to safeSend (follow-up messages)
|
|
411
|
+
safeSend(textBuffer);
|
|
412
|
+
textBuffer = "";
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
278
415
|
safeEmit(textBuffer);
|
|
279
416
|
textBuffer = "";
|
|
417
|
+
if (!firstContentEmitted) {
|
|
418
|
+
firstContentEmitted = true;
|
|
419
|
+
stopPhraseRotation();
|
|
420
|
+
}
|
|
280
421
|
}
|
|
281
422
|
function startPhraseRotation(pool) {
|
|
282
423
|
stopPhraseRotation();
|
|
@@ -293,6 +434,7 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
293
434
|
}
|
|
294
435
|
}
|
|
295
436
|
return {
|
|
437
|
+
settleOutputMode: "final_only",
|
|
296
438
|
onModelStart: () => {
|
|
297
439
|
if (hadRealOutput)
|
|
298
440
|
return; // real output already shown; don't overwrite with phrases
|
|
@@ -311,48 +453,112 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
311
453
|
/* v8 ignore next -- defensive guard: stopped set by prior error @preserve */
|
|
312
454
|
if (stopped)
|
|
313
455
|
return;
|
|
314
|
-
stopPhraseRotation();
|
|
315
|
-
hadRealOutput = true;
|
|
316
456
|
reasoningBuf += text;
|
|
457
|
+
// Keep the friendly phrases until a reasoning heading can replace them.
|
|
458
|
+
if (reasoningStatus(reasoningBuf)) {
|
|
459
|
+
stopPhraseRotation();
|
|
460
|
+
hadRealOutput = true;
|
|
461
|
+
}
|
|
317
462
|
startFlushTimer();
|
|
318
463
|
},
|
|
319
464
|
onTextChunk: (text) => {
|
|
320
465
|
if (stopped)
|
|
321
466
|
return;
|
|
322
|
-
|
|
467
|
+
// Don't stop phrase rotation here — let it continue until first content
|
|
468
|
+
// emit (handled in flushTextBuffer when MIN_INITIAL_CHARS threshold met).
|
|
323
469
|
textBuffer += text;
|
|
324
470
|
startFlushTimer();
|
|
325
471
|
},
|
|
326
472
|
onClearText: () => {
|
|
327
473
|
textBuffer = "";
|
|
328
474
|
},
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
//
|
|
334
|
-
|
|
335
|
-
//
|
|
336
|
-
// https://learn.microsoft.com/en-us/answers/questions/2288017/m365-custom-engine-agents-timeout-message-after-15
|
|
337
|
-
if (!streamHasContent)
|
|
338
|
-
safeEmit("⏳");
|
|
339
|
-
const argSummary = (0, tools_1.summarizeArgs)(name, args) || Object.keys(args).join(", ");
|
|
340
|
-
safeUpdate((0, progress_story_1.renderProgressStory)((0, progress_story_1.buildProgressStory)({
|
|
341
|
-
scope: "shared-work",
|
|
342
|
-
phase: "processing",
|
|
343
|
-
objective: `running ${name} (${argSummary})...`,
|
|
344
|
-
})));
|
|
345
|
-
hadToolRun = true;
|
|
346
|
-
},
|
|
347
|
-
onToolEnd: (name, summary, success) => {
|
|
475
|
+
flushNow: async () => {
|
|
476
|
+
const trimmed = textBuffer.trim();
|
|
477
|
+
if (!trimmed)
|
|
478
|
+
return;
|
|
479
|
+
// Cancel pending periodic flush — we're delivering now.
|
|
480
|
+
stopFlushTimer();
|
|
481
|
+
// The actual speak message replaces any "thinking..." phrase cycling.
|
|
348
482
|
stopPhraseRotation();
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
483
|
+
// Bypass MIN_INITIAL_CHARS threshold — speak delivers immediately.
|
|
484
|
+
firstContentEmitted = true;
|
|
485
|
+
textBuffer = "";
|
|
486
|
+
// Try the stream first via the NON-ABORTING variant; on failure, fall back
|
|
487
|
+
// to sendMessage. Critical: do NOT call markStopped() / abort the controller
|
|
488
|
+
// when only the primary stream fails — the sendMessage fallback may still
|
|
489
|
+
// deliver the speak, and a successful fallback must not poison the rest of
|
|
490
|
+
// the turn. Only abort when ALL delivery paths fail (handled below).
|
|
491
|
+
// Contract: throws if the message could not be delivered through any available path.
|
|
492
|
+
let delivered = false;
|
|
493
|
+
let lastError = null;
|
|
494
|
+
if (!stopped) {
|
|
495
|
+
const ok = await tryEmitNoAbort(trimmed);
|
|
496
|
+
if (ok)
|
|
497
|
+
delivered = true;
|
|
498
|
+
else
|
|
499
|
+
lastError = new Error("stream emit failed");
|
|
500
|
+
}
|
|
501
|
+
if (!delivered && sendMessage) {
|
|
502
|
+
try {
|
|
503
|
+
await sendMessage(trimmed);
|
|
504
|
+
delivered = true;
|
|
505
|
+
lastError = null;
|
|
506
|
+
}
|
|
507
|
+
catch (err) {
|
|
508
|
+
lastError = err instanceof Error ? err : new Error(String(err));
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
(0, runtime_1.emitNervesEvent)({
|
|
512
|
+
component: "senses",
|
|
513
|
+
event: "teams.speak_flush",
|
|
514
|
+
message: "teams flushed mid-turn speak",
|
|
515
|
+
meta: { messageLength: trimmed.length, delivered },
|
|
516
|
+
});
|
|
517
|
+
if (!delivered) {
|
|
518
|
+
// All delivery paths exhausted — now it is correct to abort the turn.
|
|
519
|
+
// markStopped() halts further stream activity and aborts the controller
|
|
520
|
+
// so the engine catches up and ends the turn cleanly.
|
|
521
|
+
markStopped("speak", lastError);
|
|
522
|
+
throw new Error(`teams speak delivery failed: ${lastError?.message ?? "no fallback available"}`);
|
|
523
|
+
}
|
|
355
524
|
},
|
|
525
|
+
...(() => {
|
|
526
|
+
const toolCbs = (0, tool_activity_callbacks_1.createToolActivityCallbacks)({
|
|
527
|
+
onDescription: (text) => safeUpdate(text),
|
|
528
|
+
/* v8 ignore next -- onResult only called in debug mode; tested via tool-activity-callbacks.test.ts @preserve */
|
|
529
|
+
onResult: (text) => safeUpdate(text),
|
|
530
|
+
/* v8 ignore next -- onFailure tested via onToolEnd failure test @preserve */
|
|
531
|
+
onFailure: (text) => safeUpdate(text),
|
|
532
|
+
isDebug: commands_2.getDebugMode,
|
|
533
|
+
});
|
|
534
|
+
return {
|
|
535
|
+
onToolStart: (name, args) => {
|
|
536
|
+
// speak is flow-control: its visible output is the message itself
|
|
537
|
+
// (delivered via onTextChunk + flushNow). Do NOT stop phrase rotation
|
|
538
|
+
// here, do NOT post a tool-activity
|
|
539
|
+
// status update \u2014 all of those would create UI churn right before the
|
|
540
|
+
// actual speak content arrives.
|
|
541
|
+
if (name === "speak")
|
|
542
|
+
return;
|
|
543
|
+
stopPhraseRotation();
|
|
544
|
+
// Force-flush any accumulated text, bypassing MIN_INITIAL_CHARS threshold
|
|
545
|
+
firstContentEmitted = true;
|
|
546
|
+
flushTextBuffer();
|
|
547
|
+
toolCbs.onToolStart(name, args);
|
|
548
|
+
hadToolRun = true;
|
|
549
|
+
},
|
|
550
|
+
onToolEnd: (name, summary, success) => {
|
|
551
|
+
// speak is flow-control: skip phrase-rotation stop and tool-activity end
|
|
552
|
+
// callback (no safeUpdate for \u2713/\u2717). The flushNow call inside the engine
|
|
553
|
+
// already emitted the actual message and stopped any rotation as part of
|
|
554
|
+
// tryEmit's first-content-emitted flag.
|
|
555
|
+
if (name === "speak")
|
|
556
|
+
return;
|
|
557
|
+
stopPhraseRotation();
|
|
558
|
+
toolCbs.onToolEnd(name, summary, success);
|
|
559
|
+
},
|
|
560
|
+
};
|
|
561
|
+
})(),
|
|
356
562
|
onKick: () => {
|
|
357
563
|
stopPhraseRotation();
|
|
358
564
|
const msg = (0, format_1.formatKick)();
|
|
@@ -374,38 +580,27 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
374
580
|
safeSend(msg);
|
|
375
581
|
}
|
|
376
582
|
},
|
|
377
|
-
onConfirmAction: options?.conversationId
|
|
378
|
-
? async (name, args) => {
|
|
379
|
-
const convId = options.conversationId;
|
|
380
|
-
const argsDesc = Object.entries(args).map(([k, v]) => `${k}: ${v}`).join(", ");
|
|
381
|
-
safeUpdate(`Confirm action: ${name} (${argsDesc}) -- reply "yes" to confirm or "no" to cancel`);
|
|
382
|
-
return new Promise((resolve) => {
|
|
383
|
-
_pendingConfirmations.set(convId, resolve);
|
|
384
|
-
// Auto-deny after 2 minutes to prevent indefinite blocking
|
|
385
|
-
// (e.g. when the stream dies and the user never sees the prompt).
|
|
386
|
-
setTimeout(() => {
|
|
387
|
-
if (_pendingConfirmations.has(convId)) {
|
|
388
|
-
_pendingConfirmations.delete(convId);
|
|
389
|
-
resolve("denied");
|
|
390
|
-
}
|
|
391
|
-
}, 120_000);
|
|
392
|
-
});
|
|
393
|
-
}
|
|
394
|
-
: undefined,
|
|
395
583
|
flush: async () => {
|
|
396
584
|
stopFlushTimer();
|
|
585
|
+
stopPhraseRotation();
|
|
397
586
|
if (textBuffer) {
|
|
587
|
+
// Bypass MIN_INITIAL_CHARS threshold — flush delivers all remaining content
|
|
588
|
+
firstContentEmitted = true;
|
|
398
589
|
const text = textBuffer;
|
|
399
590
|
textBuffer = "";
|
|
400
|
-
if (
|
|
591
|
+
if (streamFinalized && sendMessage) {
|
|
592
|
+
// Stream already finalized (>4000 path) — send remaining content as follow-up
|
|
593
|
+
safeSend(text);
|
|
594
|
+
}
|
|
595
|
+
else if (!stopped) {
|
|
401
596
|
// Stream is alive — await the emit so we can catch async 413/failure
|
|
402
597
|
// and fall through to sendMessage recovery.
|
|
403
598
|
const ok = await tryEmit(text);
|
|
404
599
|
if (!ok)
|
|
405
|
-
markStopped();
|
|
600
|
+
markStopped("flush");
|
|
406
601
|
}
|
|
407
|
-
if (stopped && sendMessage) {
|
|
408
|
-
// Stream is dead — fall back to sendMessage; split on failure as recovery.
|
|
602
|
+
if (stopped && !streamFinalized && sendMessage) {
|
|
603
|
+
// Stream is dead (not from finalization) — fall back to sendMessage; split on failure as recovery.
|
|
409
604
|
try {
|
|
410
605
|
await sendMessage(text);
|
|
411
606
|
}
|
|
@@ -416,31 +611,24 @@ function createTeamsCallbacks(stream, controller, sendMessage, options) {
|
|
|
416
611
|
}
|
|
417
612
|
}
|
|
418
613
|
}
|
|
419
|
-
else if (!streamHasContent) {
|
|
420
|
-
safeEmit("(completed with tool calls only
|
|
614
|
+
else if (!streamHasContent && !options?.suppressEmptyStreamMessage) {
|
|
615
|
+
safeEmit("(completed with tool calls only — no text response)");
|
|
421
616
|
}
|
|
617
|
+
(0, runtime_1.emitNervesEvent)({
|
|
618
|
+
component: "senses",
|
|
619
|
+
event: "teams.flush_done",
|
|
620
|
+
message: "teams end-of-turn flush finished",
|
|
621
|
+
meta: { stopped, streamHasContent, streamFinalized, conversationId: options?.conversationId ?? null },
|
|
622
|
+
});
|
|
422
623
|
},
|
|
423
624
|
};
|
|
424
625
|
}
|
|
425
|
-
//
|
|
426
|
-
//
|
|
427
|
-
|
|
428
|
-
const
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
function resolvePendingConfirmation(convId, text) {
|
|
432
|
-
const resolver = _pendingConfirmations.get(convId);
|
|
433
|
-
if (!resolver)
|
|
434
|
-
return false;
|
|
435
|
-
_pendingConfirmations.delete(convId);
|
|
436
|
-
const word = text.trim().toLowerCase();
|
|
437
|
-
if (CONFIRM_WORDS.has(word)) {
|
|
438
|
-
resolver("confirmed");
|
|
439
|
-
}
|
|
440
|
-
else {
|
|
441
|
-
resolver("denied");
|
|
442
|
-
}
|
|
443
|
-
return true;
|
|
626
|
+
// The status bubble shows the latest reasoning heading (models write "**Heading**"
|
|
627
|
+
// before each reasoning step), never the raw reasoning text itself.
|
|
628
|
+
function reasoningStatus(reasoning) {
|
|
629
|
+
const headings = [...reasoning.matchAll(/\*\*([^*\n]+)\*\*/g)];
|
|
630
|
+
const last = headings.at(-1)?.[1]?.trim();
|
|
631
|
+
return last ? `${last}…` : null;
|
|
444
632
|
}
|
|
445
633
|
const _turnCoordinator = (0, turn_coordinator_1.createTurnCoordinator)();
|
|
446
634
|
function teamsTurnKey(conversationId) {
|
|
@@ -453,13 +641,14 @@ async function withConversationLock(convId, fn) {
|
|
|
453
641
|
// are deleted while the process is alive.
|
|
454
642
|
function getFriendStore() {
|
|
455
643
|
const friendsPath = path.join((0, identity_1.getAgentRoot)(), "friends");
|
|
456
|
-
return new
|
|
644
|
+
return new friends_1.FileFriendStore(friendsPath);
|
|
457
645
|
}
|
|
458
646
|
function createTeamsCommandRegistry() {
|
|
459
647
|
const registry = (0, commands_1.createCommandRegistry)();
|
|
460
648
|
(0, commands_1.registerDefaultCommands)(registry);
|
|
461
649
|
return registry;
|
|
462
650
|
}
|
|
651
|
+
/* v8 ignore start -- superseding follow-up slash command handler; tested via startTeamsApp integration tests @preserve */
|
|
463
652
|
function handleTeamsSlashCommand(text, registry, friendId, conversationId, stream, emitResponse = true) {
|
|
464
653
|
const parsed = (0, commands_1.parseSlashCommand)(text);
|
|
465
654
|
if (!parsed)
|
|
@@ -483,22 +672,21 @@ function handleTeamsSlashCommand(text, registry, friendId, conversationId, strea
|
|
|
483
672
|
}
|
|
484
673
|
return null;
|
|
485
674
|
}
|
|
675
|
+
/* v8 ignore stop */
|
|
486
676
|
// Handle an incoming Teams message
|
|
487
|
-
async function handleTeamsMessage(text, stream, conversationId, teamsContext, sendMessage) {
|
|
677
|
+
async function handleTeamsMessage(text, stream, conversationId, teamsContext, sendMessage, reactionOverrides, runtimeOverrides) {
|
|
488
678
|
const turnKey = teamsTurnKey(conversationId);
|
|
679
|
+
const agentName = (0, identity_1.getAgentName)();
|
|
680
|
+
const owner = { agentName, agentRoot: (0, identity_1.getAgentRoot)(agentName) };
|
|
489
681
|
// NOTE: Confirmation resolution is handled in the app.on("message") handler
|
|
490
682
|
// BEFORE the conversation lock. By the time we get here, any pending
|
|
491
683
|
// confirmation has already been resolved and the reply consumed.
|
|
492
|
-
// Send first thinking phrase immediately so the user sees feedback
|
|
493
|
-
// before sync I/O (session load, trim) blocks the event loop.
|
|
494
|
-
stream.update((0, phrases_1.pickPhrase)((0, phrases_1.getPhrases)().thinking) + "...");
|
|
495
|
-
await new Promise(r => setImmediate(r));
|
|
496
684
|
// Resolve identity provider early for friend resolution + slash command session path
|
|
497
685
|
const store = getFriendStore();
|
|
498
686
|
const provider = teamsContext?.aadObjectId ? "aad" : "teams-conversation";
|
|
499
687
|
const externalId = teamsContext?.aadObjectId || conversationId;
|
|
500
688
|
// Build FriendResolver for the pipeline
|
|
501
|
-
const resolver = new
|
|
689
|
+
const resolver = new friends_1.FriendResolver(store, {
|
|
502
690
|
provider,
|
|
503
691
|
externalId,
|
|
504
692
|
tenantId: teamsContext?.tenantId,
|
|
@@ -508,133 +696,194 @@ async function handleTeamsMessage(text, stream, conversationId, teamsContext, se
|
|
|
508
696
|
// Pre-resolve friend for session path + slash commands (pipeline will re-use the cached result)
|
|
509
697
|
const resolvedContext = await resolver.resolve();
|
|
510
698
|
const friendId = resolvedContext.friend.id;
|
|
511
|
-
const registry = createTeamsCommandRegistry();
|
|
512
|
-
// Check for slash commands (before pipeline -- these are transport-level concerns)
|
|
513
|
-
if (handleTeamsSlashCommand(text, registry, friendId, conversationId, stream)) {
|
|
514
|
-
return;
|
|
515
|
-
}
|
|
516
699
|
// ── Teams adapter concerns: controller, callbacks, session path ──────────
|
|
517
700
|
const controller = new AbortController();
|
|
518
701
|
const channelConfig = (0, config_2.getTeamsChannelConfig)();
|
|
519
|
-
const callbacks = createTeamsCallbacks(stream, controller, sendMessage, { conversationId, flushIntervalMs: channelConfig.flushIntervalMs });
|
|
702
|
+
const callbacks = createTeamsCallbacks(stream, controller, sendMessage, { conversationId, flushIntervalMs: channelConfig.flushIntervalMs, ...(reactionOverrides?.suppressEmptyStreamMessage ? { suppressEmptyStreamMessage: true } : {}) });
|
|
520
703
|
const traceId = (0, nerves_1.createTraceId)();
|
|
521
704
|
const sessPath = (0, config_2.sessionPath)(friendId, "teams", conversationId);
|
|
522
|
-
const teamsCapabilities = (0,
|
|
523
|
-
const pendingDir = (0, pending_1.getPendingDir)(
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
summarize: (0, core_1.createSummarize)(),
|
|
531
|
-
tenantId: teamsContext.tenantId,
|
|
532
|
-
botApi: teamsContext.botApi,
|
|
533
|
-
} : {};
|
|
534
|
-
let currentText = text;
|
|
535
|
-
const mcpManager = await (0, mcp_manager_1.getSharedMcpManager)() ?? undefined;
|
|
536
|
-
while (true) {
|
|
537
|
-
let drainedSteeringFollowUps = [];
|
|
538
|
-
// Build runAgentOptions with Teams-specific fields
|
|
539
|
-
const agentOptions = {
|
|
540
|
-
traceId,
|
|
541
|
-
toolContext: teamsToolContext,
|
|
542
|
-
mcpManager,
|
|
543
|
-
drainSteeringFollowUps: () => {
|
|
544
|
-
drainedSteeringFollowUps = _turnCoordinator.drainFollowUps(turnKey)
|
|
545
|
-
.map(({ text: followUpText, effect }) => ({ text: followUpText, effect }));
|
|
546
|
-
return drainedSteeringFollowUps;
|
|
547
|
-
},
|
|
548
|
-
};
|
|
549
|
-
if (channelConfig.skipConfirmation)
|
|
550
|
-
agentOptions.skipConfirmation = true;
|
|
551
|
-
// ── Call shared pipeline ──────────────────────────────────────────
|
|
552
|
-
const result = await (0, pipeline_1.handleInboundTurn)({
|
|
553
|
-
channel: "teams",
|
|
554
|
-
sessionKey: conversationId,
|
|
555
|
-
capabilities: teamsCapabilities,
|
|
556
|
-
messages: [{ role: "user", content: currentText }],
|
|
557
|
-
continuityIngressTexts: [currentText],
|
|
558
|
-
callbacks,
|
|
559
|
-
friendResolver: { resolve: () => Promise.resolve(resolvedContext) },
|
|
560
|
-
sessionLoader: {
|
|
561
|
-
loadOrCreate: async () => {
|
|
562
|
-
const existing = (0, context_1.loadSession)(sessPath);
|
|
563
|
-
const messages = existing?.messages && existing.messages.length > 0
|
|
564
|
-
? existing.messages
|
|
565
|
-
: [{ role: "system", content: await (0, prompt_1.buildSystem)("teams", { mcpManager }, resolvedContext) }];
|
|
566
|
-
(0, core_1.repairOrphanedToolCalls)(messages);
|
|
567
|
-
return { messages, sessionPath: sessPath, state: existing?.state };
|
|
568
|
-
},
|
|
569
|
-
},
|
|
570
|
-
pendingDir,
|
|
571
|
-
friendStore: store,
|
|
572
|
-
provider,
|
|
573
|
-
externalId,
|
|
574
|
-
tenantId: teamsContext?.tenantId,
|
|
575
|
-
isGroupChat: false,
|
|
576
|
-
groupHasFamilyMember: false,
|
|
577
|
-
hasExistingGroupWithFamily: false,
|
|
578
|
-
enforceTrustGate: trust_gate_1.enforceTrustGate,
|
|
579
|
-
drainPending: pending_1.drainPending,
|
|
580
|
-
drainDeferredReturns: (deferredFriendId) => (0, pending_1.drainDeferredReturns)((0, identity_1.getAgentName)(), deferredFriendId),
|
|
581
|
-
runAgent: (msgs, cb, channel, sig, opts) => (0, core_1.runAgent)(msgs, cb, channel, sig, {
|
|
582
|
-
...opts,
|
|
583
|
-
toolContext: {
|
|
584
|
-
/* v8 ignore next -- default no-op signin; pipeline provides the real one @preserve */
|
|
585
|
-
signin: async () => undefined,
|
|
586
|
-
...opts?.toolContext,
|
|
587
|
-
summarize: teamsToolContext.summarize,
|
|
588
|
-
},
|
|
589
|
-
}),
|
|
590
|
-
postTurn: context_1.postTurn,
|
|
591
|
-
accumulateFriendTokens: tokens_1.accumulateFriendTokens,
|
|
592
|
-
signal: controller.signal,
|
|
593
|
-
runAgentOptions: agentOptions,
|
|
594
|
-
});
|
|
595
|
-
// ── Handle gate result ────────────────────────────────────────
|
|
596
|
-
if (!result.gateResult.allowed) {
|
|
597
|
-
if ("autoReply" in result.gateResult && result.gateResult.autoReply) {
|
|
598
|
-
stream.emit(result.gateResult.autoReply);
|
|
705
|
+
const teamsCapabilities = (0, friends_1.getChannelCapabilities)("teams");
|
|
706
|
+
const pendingDir = (0, pending_1.getPendingDir)(agentName, friendId, "teams", conversationId);
|
|
707
|
+
const runWithLease = runtimeOverrides?._withSessionTurnLease ?? session_transaction_1.withSessionTurnLease;
|
|
708
|
+
try {
|
|
709
|
+
await runWithLease(sessPath, async (sessionTurnLease) => {
|
|
710
|
+
// Do not emit any activity until this turn owns the durable session.
|
|
711
|
+
if (!reactionOverrides) {
|
|
712
|
+
stream.update((0, phrases_1.pickPhrase)((0, phrases_1.getPhrases)().thinking) + "...");
|
|
599
713
|
}
|
|
714
|
+
await new Promise(r => setImmediate(r));
|
|
715
|
+
// Build Teams-specific toolContext fields for injection into the pipeline
|
|
716
|
+
const teamsToolContext = { ...owner, ...(teamsContext ? {
|
|
717
|
+
graphToken: teamsContext.graphToken,
|
|
718
|
+
adoToken: teamsContext.adoToken,
|
|
719
|
+
githubToken: teamsContext.githubToken,
|
|
720
|
+
signin: teamsContext.signin,
|
|
721
|
+
summarize: (0, core_1.createSummarize)("human"),
|
|
722
|
+
tenantId: teamsContext.tenantId,
|
|
723
|
+
botApi: teamsContext.botApi,
|
|
724
|
+
} : {}) };
|
|
725
|
+
let currentText = text;
|
|
726
|
+
const mcpManager = await (0, mcp_manager_1.getSharedMcpManager)(owner) ?? undefined;
|
|
727
|
+
while (true) {
|
|
728
|
+
let drainedSteeringFollowUps = [];
|
|
729
|
+
// Build runAgentOptions with Teams-specific fields
|
|
730
|
+
const agentOptions = {
|
|
731
|
+
traceId,
|
|
732
|
+
toolContext: teamsToolContext,
|
|
733
|
+
mcpManager,
|
|
734
|
+
drainSteeringFollowUps: () => {
|
|
735
|
+
drainedSteeringFollowUps = _turnCoordinator.drainFollowUps(turnKey)
|
|
736
|
+
.map(({ text: followUpText, effect }) => ({ text: followUpText, effect }));
|
|
737
|
+
return drainedSteeringFollowUps;
|
|
738
|
+
},
|
|
739
|
+
...(reactionOverrides?.isReactionSignal ? { isReactionSignal: true } : {}),
|
|
740
|
+
};
|
|
741
|
+
// ── Call shared pipeline ──────────────────────────────────────────
|
|
742
|
+
// Capture terminal errors — failover message replaces the error card if it triggers
|
|
743
|
+
let capturedTerminalError = null;
|
|
744
|
+
const teamsFailoverState = (() => {
|
|
745
|
+
if (!teamsFailoverStates.has(conversationId)) {
|
|
746
|
+
teamsFailoverStates.set(conversationId, { pending: null });
|
|
747
|
+
}
|
|
748
|
+
return teamsFailoverStates.get(conversationId);
|
|
749
|
+
})();
|
|
750
|
+
/* v8 ignore start -- failover-aware callback wrapper: tested via pipeline integration @preserve */
|
|
751
|
+
const failoverAwareCallbacks = {
|
|
752
|
+
...callbacks,
|
|
753
|
+
onError: (error, severity) => {
|
|
754
|
+
if (severity === "terminal" && teamsFailoverState) {
|
|
755
|
+
capturedTerminalError = error;
|
|
756
|
+
return;
|
|
757
|
+
}
|
|
758
|
+
callbacks.onError(error, severity);
|
|
759
|
+
},
|
|
760
|
+
};
|
|
761
|
+
/* v8 ignore stop */
|
|
762
|
+
const result = await (0, pipeline_1.handleInboundTurn)({
|
|
763
|
+
channel: "teams",
|
|
764
|
+
sessionKey: conversationId,
|
|
765
|
+
capabilities: teamsCapabilities,
|
|
766
|
+
messages: [{ role: "user", content: currentText }],
|
|
767
|
+
continuityIngressTexts: [currentText],
|
|
768
|
+
callbacks: failoverAwareCallbacks,
|
|
769
|
+
sessionTurnLease,
|
|
770
|
+
friendResolver: { resolve: () => Promise.resolve(resolvedContext) },
|
|
771
|
+
sessionLoader: {
|
|
772
|
+
loadOrCreate: async () => {
|
|
773
|
+
const existing = (0, context_1.loadSession)(sessPath);
|
|
774
|
+
const messages = existing?.messages && existing.messages.length > 0
|
|
775
|
+
? existing.messages
|
|
776
|
+
: [{ role: "system", content: (0, prompt_1.flattenSystemPrompt)(await (0, prompt_1.buildSystem)("teams", {}, resolvedContext)) }];
|
|
777
|
+
(0, core_1.repairOrphanedToolCalls)(messages);
|
|
778
|
+
return {
|
|
779
|
+
messages,
|
|
780
|
+
sessionPath: sessPath,
|
|
781
|
+
state: existing?.state,
|
|
782
|
+
events: existing?.events,
|
|
783
|
+
structuredOutputs: existing?.structuredOutputs,
|
|
784
|
+
};
|
|
785
|
+
},
|
|
786
|
+
},
|
|
787
|
+
pendingDir,
|
|
788
|
+
friendStore: store,
|
|
789
|
+
provider,
|
|
790
|
+
externalId,
|
|
791
|
+
tenantId: teamsContext?.tenantId,
|
|
792
|
+
isGroupChat: false,
|
|
793
|
+
groupHasFamilyMember: false,
|
|
794
|
+
hasExistingGroupWithFamily: false,
|
|
795
|
+
enforceTrustGate: trust_gate_1.enforceTrustGate,
|
|
796
|
+
drainPending: pending_1.drainPending,
|
|
797
|
+
drainDeferredReturns: (deferredFriendId) => (0, pending_1.drainDeferredReturns)(agentName, deferredFriendId),
|
|
798
|
+
runAgent: (msgs, cb, channel, sig, opts) => (0, core_1.runAgent)(msgs, cb, channel, sig, {
|
|
799
|
+
...opts,
|
|
800
|
+
toolContext: {
|
|
801
|
+
/* v8 ignore next -- default no-op signin; pipeline provides the real one @preserve */
|
|
802
|
+
signin: async () => undefined,
|
|
803
|
+
...opts?.toolContext,
|
|
804
|
+
...owner,
|
|
805
|
+
summarize: teamsToolContext.summarize,
|
|
806
|
+
},
|
|
807
|
+
}),
|
|
808
|
+
postTurn: (turnMessages, sessionPathArg, usage, hooks, state) => {
|
|
809
|
+
const prepared = (0, context_1.postTurnTrim)(turnMessages, usage, hooks);
|
|
810
|
+
return (0, context_1.deferPostTurnPersist)(sessionPathArg, prepared, usage, state);
|
|
811
|
+
},
|
|
812
|
+
accumulateFriendTokens: friends_1.accumulateFriendTokens,
|
|
813
|
+
signal: controller.signal,
|
|
814
|
+
runAgentOptions: agentOptions,
|
|
815
|
+
failoverState: teamsFailoverState,
|
|
816
|
+
});
|
|
817
|
+
// ── Handle pipeline-intercepted commands ────────────────────────
|
|
818
|
+
if (result.turnOutcome === "command") {
|
|
819
|
+
if (result.commandAction === "new") {
|
|
820
|
+
(0, context_1.deleteSession)(sessPath);
|
|
821
|
+
stream.emit("session cleared");
|
|
822
|
+
}
|
|
823
|
+
// For "response" commands: pipeline already emitted the response via onTextChunk
|
|
824
|
+
await callbacks.flush();
|
|
825
|
+
return;
|
|
826
|
+
}
|
|
827
|
+
/* v8 ignore start -- failover display: tested via pipeline integration tests @preserve */
|
|
828
|
+
if (result.failoverMessage) {
|
|
829
|
+
stream.emit(result.failoverMessage);
|
|
830
|
+
}
|
|
831
|
+
else if (capturedTerminalError) {
|
|
832
|
+
callbacks.onError(capturedTerminalError, "terminal");
|
|
833
|
+
}
|
|
834
|
+
/* v8 ignore stop */
|
|
835
|
+
// ── Handle gate result ────────────────────────────────────────
|
|
836
|
+
if (!result.gateResult.allowed) {
|
|
837
|
+
if ("autoReply" in result.gateResult && result.gateResult.autoReply) {
|
|
838
|
+
stream.emit(result.gateResult.autoReply);
|
|
839
|
+
}
|
|
840
|
+
return;
|
|
841
|
+
}
|
|
842
|
+
// Flush any remaining accumulated text at end of turn
|
|
843
|
+
await callbacks.flush();
|
|
844
|
+
// After the agent loop, check if any tool returned AUTH_REQUIRED and trigger signin.
|
|
845
|
+
// This must happen after the stream is done so the OAuth card renders properly.
|
|
846
|
+
if (teamsContext && result.messages) {
|
|
847
|
+
const allContent = result.messages.map(m => typeof m.content === "string" ? m.content : "").join("\n");
|
|
848
|
+
if (allContent.includes("AUTH_REQUIRED:graph") && teamsContext.graphConnectionName)
|
|
849
|
+
await teamsContext.signin(teamsContext.graphConnectionName);
|
|
850
|
+
if (allContent.includes("AUTH_REQUIRED:ado") && teamsContext.adoConnectionName)
|
|
851
|
+
await teamsContext.signin(teamsContext.adoConnectionName);
|
|
852
|
+
if (allContent.includes("AUTH_REQUIRED:github") && teamsContext.githubConnectionName)
|
|
853
|
+
await teamsContext.signin(teamsContext.githubConnectionName);
|
|
854
|
+
}
|
|
855
|
+
if (result.turnOutcome !== "superseded") {
|
|
856
|
+
return;
|
|
857
|
+
}
|
|
858
|
+
const supersedingIndex = drainedSteeringFollowUps
|
|
859
|
+
.map((followUp) => followUp.effect)
|
|
860
|
+
.lastIndexOf("clear_and_supersede");
|
|
861
|
+
if (supersedingIndex < 0) {
|
|
862
|
+
return;
|
|
863
|
+
}
|
|
864
|
+
const supersedingFollowUp = drainedSteeringFollowUps[supersedingIndex];
|
|
865
|
+
const replayTail = drainedSteeringFollowUps
|
|
866
|
+
.slice(supersedingIndex + 1)
|
|
867
|
+
.map((followUp) => followUp.text.trim())
|
|
868
|
+
.filter((followUpText) => followUpText.length > 0)
|
|
869
|
+
.join("\n");
|
|
870
|
+
if (replayTail) {
|
|
871
|
+
currentText = replayTail;
|
|
872
|
+
continue;
|
|
873
|
+
}
|
|
874
|
+
if (handleTeamsSlashCommand(supersedingFollowUp.text, createTeamsCommandRegistry(), friendId, conversationId, stream, false)) {
|
|
875
|
+
return;
|
|
876
|
+
}
|
|
877
|
+
currentText = supersedingFollowUp.text;
|
|
878
|
+
}
|
|
879
|
+
});
|
|
880
|
+
}
|
|
881
|
+
catch (error) {
|
|
882
|
+
if (error instanceof session_transaction_1.SessionTurnBusyError || (error instanceof Error && error.name === "SessionTurnBusyError")) {
|
|
883
|
+
stream.emit("this conversation is busy finishing another turn; please try again");
|
|
600
884
|
return;
|
|
601
885
|
}
|
|
602
|
-
|
|
603
|
-
await callbacks.flush();
|
|
604
|
-
// After the agent loop, check if any tool returned AUTH_REQUIRED and trigger signin.
|
|
605
|
-
// This must happen after the stream is done so the OAuth card renders properly.
|
|
606
|
-
if (teamsContext && result.messages) {
|
|
607
|
-
const allContent = result.messages.map(m => typeof m.content === "string" ? m.content : "").join("\n");
|
|
608
|
-
if (allContent.includes("AUTH_REQUIRED:graph") && teamsContext.graphConnectionName)
|
|
609
|
-
await teamsContext.signin(teamsContext.graphConnectionName);
|
|
610
|
-
if (allContent.includes("AUTH_REQUIRED:ado") && teamsContext.adoConnectionName)
|
|
611
|
-
await teamsContext.signin(teamsContext.adoConnectionName);
|
|
612
|
-
if (allContent.includes("AUTH_REQUIRED:github") && teamsContext.githubConnectionName)
|
|
613
|
-
await teamsContext.signin(teamsContext.githubConnectionName);
|
|
614
|
-
}
|
|
615
|
-
if (result.turnOutcome !== "superseded") {
|
|
616
|
-
return;
|
|
617
|
-
}
|
|
618
|
-
const supersedingIndex = drainedSteeringFollowUps
|
|
619
|
-
.map((followUp) => followUp.effect)
|
|
620
|
-
.lastIndexOf("clear_and_supersede");
|
|
621
|
-
if (supersedingIndex < 0) {
|
|
622
|
-
return;
|
|
623
|
-
}
|
|
624
|
-
const supersedingFollowUp = drainedSteeringFollowUps[supersedingIndex];
|
|
625
|
-
const replayTail = drainedSteeringFollowUps
|
|
626
|
-
.slice(supersedingIndex + 1)
|
|
627
|
-
.map((followUp) => followUp.text.trim())
|
|
628
|
-
.filter((followUpText) => followUpText.length > 0)
|
|
629
|
-
.join("\n");
|
|
630
|
-
if (replayTail) {
|
|
631
|
-
currentText = replayTail;
|
|
632
|
-
continue;
|
|
633
|
-
}
|
|
634
|
-
if (handleTeamsSlashCommand(supersedingFollowUp.text, registry, friendId, conversationId, stream, false)) {
|
|
635
|
-
return;
|
|
636
|
-
}
|
|
637
|
-
currentText = supersedingFollowUp.text;
|
|
886
|
+
throw error;
|
|
638
887
|
}
|
|
639
888
|
}
|
|
640
889
|
// Internal port for the secondary bot App (not exposed externally).
|
|
@@ -675,6 +924,7 @@ function createBotApp(teamsConfig) {
|
|
|
675
924
|
tenantId: teamsConfig.tenantId,
|
|
676
925
|
oauth: { defaultConnectionName: oauthConfig.graphConnectionName },
|
|
677
926
|
...mentionStripping,
|
|
927
|
+
logger: createTeamsSdkLogger(),
|
|
678
928
|
}),
|
|
679
929
|
mode: "Bot Service (client secret)",
|
|
680
930
|
};
|
|
@@ -687,21 +937,50 @@ function createBotApp(teamsConfig) {
|
|
|
687
937
|
...(teamsConfig.managedIdentityClientId ? { managedIdentityClientId: teamsConfig.managedIdentityClientId } : {}),
|
|
688
938
|
oauth: { defaultConnectionName: oauthConfig.graphConnectionName },
|
|
689
939
|
...mentionStripping,
|
|
940
|
+
logger: createTeamsSdkLogger(),
|
|
690
941
|
}),
|
|
691
942
|
mode: "Bot Service (managed identity)",
|
|
692
943
|
};
|
|
693
944
|
}
|
|
694
945
|
else {
|
|
946
|
+
// With no credentials the Teams SDK validates no sender, so anyone who can reach the port can
|
|
947
|
+
// talk to the agent. That is only acceptable when a developer asks for it on their own machine.
|
|
948
|
+
if (process.env.OURO_TEAMS_DEVTOOLS !== "1") {
|
|
949
|
+
throw new Error("Teams bot credentials are missing (teams.clientId), so the Teams sense will not start: without them it would accept messages from anyone. Run `ouro connect teams`, or set OURO_TEAMS_DEVTOOLS=1 for local development with no real bot.");
|
|
950
|
+
}
|
|
695
951
|
return {
|
|
696
952
|
app: new teams_apps_1.App({
|
|
697
953
|
plugins: [new teams_dev_1.DevtoolsPlugin()],
|
|
698
954
|
...mentionStripping,
|
|
955
|
+
logger: createTeamsSdkLogger(),
|
|
699
956
|
}),
|
|
700
957
|
mode: "DevtoolsPlugin",
|
|
701
958
|
};
|
|
702
959
|
}
|
|
703
960
|
}
|
|
704
|
-
|
|
961
|
+
function createTeamsSdkLogger(name = "teams-sdk") {
|
|
962
|
+
const verbose = /stream/i.test(name);
|
|
963
|
+
const write = (level, msg) => {
|
|
964
|
+
if ((level === "debug" || level === "trace" || level === "info") && !verbose)
|
|
965
|
+
return;
|
|
966
|
+
(0, runtime_1.emitNervesEvent)({
|
|
967
|
+
level: level === "error" ? "error" : level === "warn" ? "warn" : "info",
|
|
968
|
+
component: "senses",
|
|
969
|
+
event: "teams.sdk_log",
|
|
970
|
+
message: `[${name}] ${msg.map((m) => (m instanceof Error ? m.message : typeof m === "string" ? m : JSON.stringify(m))).join(" ").slice(0, 300)}`,
|
|
971
|
+
meta: { logger: name, sdkLevel: level },
|
|
972
|
+
});
|
|
973
|
+
};
|
|
974
|
+
return {
|
|
975
|
+
debug: (...m) => write("debug", m),
|
|
976
|
+
info: (...m) => write("info", m),
|
|
977
|
+
warn: (...m) => write("warn", m),
|
|
978
|
+
error: (...m) => write("error", m),
|
|
979
|
+
trace: (...m) => write("trace", m),
|
|
980
|
+
log: (level, ...m) => write(level, m),
|
|
981
|
+
child: (child) => createTeamsSdkLogger(`${name}/${child}`),
|
|
982
|
+
};
|
|
983
|
+
}
|
|
705
984
|
// Register message, verify-state, and error handlers on an App instance.
|
|
706
985
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
707
986
|
function registerBotHandlers(app, label) {
|
|
@@ -711,7 +990,7 @@ function registerBotHandlers(app, label) {
|
|
|
711
990
|
// (graph + ado + github). The verifyState activity only carries a `state`
|
|
712
991
|
// code with no connectionName, so we try each configured connection until
|
|
713
992
|
// one succeeds.
|
|
714
|
-
app.on("signin.verify-state", async (ctx) => {
|
|
993
|
+
app.on("signin.verify-state", (async (ctx) => {
|
|
715
994
|
const { api, activity } = ctx;
|
|
716
995
|
if (!activity.value?.state)
|
|
717
996
|
return { status: 404 };
|
|
@@ -730,7 +1009,75 @@ function registerBotHandlers(app, label) {
|
|
|
730
1009
|
}
|
|
731
1010
|
(0, runtime_1.emitNervesEvent)({ level: "warn", event: "channel.verify_state", component: "channels", message: `[${label}] verify-state failed for all connections`, meta: {} });
|
|
732
1011
|
return { status: 412 };
|
|
1012
|
+
}));
|
|
1013
|
+
// Handle Teams feedback reactions (thumbs up/down on AI-generated messages).
|
|
1014
|
+
// SDK routes message/submitAction with actionName "feedback" to this event.
|
|
1015
|
+
/* v8 ignore start -- Teams SDK invoke handler; requires live SDK context @preserve */
|
|
1016
|
+
app.on("message.submit.feedback", async (ctx) => {
|
|
1017
|
+
const { stream, activity } = ctx;
|
|
1018
|
+
const reaction = activity.value?.actionValue?.reaction;
|
|
1019
|
+
const comment = activity.value?.actionValue?.feedback;
|
|
1020
|
+
const convId = activity.conversation?.id || "unknown";
|
|
1021
|
+
const turnKey = teamsTurnKey(convId);
|
|
1022
|
+
// Validate payload — graceful no-op for malformed invocations
|
|
1023
|
+
if (activity.value?.actionName !== "feedback" || !reaction) {
|
|
1024
|
+
return undefined;
|
|
1025
|
+
}
|
|
1026
|
+
const syntheticText = buildFeedbackSyntheticText(reaction, comment);
|
|
1027
|
+
// Turn coordination: if a turn is active, enqueue as steering follow-up
|
|
1028
|
+
if (!_turnCoordinator.tryBeginTurn(turnKey)) {
|
|
1029
|
+
_turnCoordinator.enqueueFollowUp(turnKey, {
|
|
1030
|
+
conversationId: convId,
|
|
1031
|
+
text: syntheticText,
|
|
1032
|
+
receivedAt: Date.now(),
|
|
1033
|
+
effect: (0, continuity_1.classifySteeringFollowUpEffect)(syntheticText),
|
|
1034
|
+
});
|
|
1035
|
+
return undefined;
|
|
1036
|
+
}
|
|
1037
|
+
try {
|
|
1038
|
+
const teamsContext = {
|
|
1039
|
+
signin: async () => undefined,
|
|
1040
|
+
aadObjectId: activity.from?.aadObjectId,
|
|
1041
|
+
tenantId: activity.conversation?.tenantId,
|
|
1042
|
+
displayName: activity.from?.name,
|
|
1043
|
+
};
|
|
1044
|
+
const ctxSend = async (t) => {
|
|
1045
|
+
await ctx.send({ type: "message", text: t, replyToId: activity.replyToId, entities: aiLabelEntities(), channelData: { feedbackLoopEnabled: true } });
|
|
1046
|
+
};
|
|
1047
|
+
await handleTeamsMessage(syntheticText, stream, convId, teamsContext, ctxSend, { isReactionSignal: true, suppressEmptyStreamMessage: true });
|
|
1048
|
+
}
|
|
1049
|
+
catch (err) {
|
|
1050
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1051
|
+
(0, runtime_1.emitNervesEvent)({ level: "error", event: "channel.feedback_handler_error", component: "channels", message: msg.slice(0, 200), meta: {} });
|
|
1052
|
+
}
|
|
1053
|
+
finally {
|
|
1054
|
+
_turnCoordinator.endTurn(turnKey);
|
|
1055
|
+
}
|
|
1056
|
+
return undefined;
|
|
733
1057
|
});
|
|
1058
|
+
/* v8 ignore stop */
|
|
1059
|
+
// Handle bot install — send welcome Adaptive Card with prompt starters.
|
|
1060
|
+
/* v8 ignore start -- Teams SDK install handler; requires live SDK context @preserve */
|
|
1061
|
+
app.on("install.add", async (ctx) => {
|
|
1062
|
+
try {
|
|
1063
|
+
const card = buildWelcomeCard();
|
|
1064
|
+
await ctx.send({
|
|
1065
|
+
type: "message",
|
|
1066
|
+
attachments: [{
|
|
1067
|
+
contentType: "application/vnd.microsoft.card.adaptive",
|
|
1068
|
+
content: card,
|
|
1069
|
+
}],
|
|
1070
|
+
entities: aiLabelEntities(),
|
|
1071
|
+
channelData: { feedbackLoopEnabled: true },
|
|
1072
|
+
});
|
|
1073
|
+
}
|
|
1074
|
+
catch (err) {
|
|
1075
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1076
|
+
(0, runtime_1.emitNervesEvent)({ level: "error", event: "channel.welcome_handler_error", component: "channels", message: msg.slice(0, 200), meta: {} });
|
|
1077
|
+
}
|
|
1078
|
+
return undefined;
|
|
1079
|
+
});
|
|
1080
|
+
/* v8 ignore stop */
|
|
734
1081
|
app.on("message", async (ctx) => {
|
|
735
1082
|
const { stream, activity, api, signin } = ctx;
|
|
736
1083
|
const text = activity.text || "";
|
|
@@ -739,13 +1086,6 @@ function registerBotHandlers(app, label) {
|
|
|
739
1086
|
const userId = activity.from?.id || "";
|
|
740
1087
|
const channelId = activity.channelId || "msteams";
|
|
741
1088
|
(0, runtime_1.emitNervesEvent)({ level: "info", event: "channel.message_received", component: "channels", message: `[${label}] incoming teams message`, meta: { userId: userId.slice(0, 12), conversationId: convId.slice(0, 20) } });
|
|
742
|
-
// Resolve pending confirmations IMMEDIATELY — before token fetches or
|
|
743
|
-
// the conversation lock. The original message holds the lock while
|
|
744
|
-
// awaiting confirmation, so acquiring it here would deadlock. Token
|
|
745
|
-
// fetches are also unnecessary (and slow) for a simple yes/no reply.
|
|
746
|
-
if (resolvePendingConfirmation(convId, text)) {
|
|
747
|
-
return;
|
|
748
|
-
}
|
|
749
1089
|
const commandRegistry = createTeamsCommandRegistry();
|
|
750
1090
|
const parsedSlashCommand = (0, commands_1.parseSlashCommand)(text);
|
|
751
1091
|
if (parsedSlashCommand) {
|
|
@@ -759,7 +1099,7 @@ function registerBotHandlers(app, label) {
|
|
|
759
1099
|
const commandStore = getFriendStore();
|
|
760
1100
|
const commandProvider = activity.from?.aadObjectId ? "aad" : "teams-conversation";
|
|
761
1101
|
const commandExternalId = activity.from?.aadObjectId || convId;
|
|
762
|
-
const commandResolver = new
|
|
1102
|
+
const commandResolver = new friends_1.FriendResolver(commandStore, {
|
|
763
1103
|
provider: commandProvider,
|
|
764
1104
|
externalId: commandExternalId,
|
|
765
1105
|
tenantId: activity.conversation?.tenantId,
|
|
@@ -846,7 +1186,7 @@ function registerBotHandlers(app, label) {
|
|
|
846
1186
|
const ctxSend = async (t) => {
|
|
847
1187
|
// Use send with replyToId (not reply, which adds a blockquote).
|
|
848
1188
|
// replyToId anchors the message after the user's message in Copilot Chat.
|
|
849
|
-
await ctx.send({ type: "message", text: t, replyToId: activity.id });
|
|
1189
|
+
await ctx.send({ type: "message", text: t, replyToId: activity.id, entities: aiLabelEntities(), channelData: { feedbackLoopEnabled: true } });
|
|
850
1190
|
};
|
|
851
1191
|
await handleTeamsMessage(text, stream, convId, teamsContext, ctxSend);
|
|
852
1192
|
}
|
|
@@ -874,14 +1214,14 @@ function findAadObjectId(friend) {
|
|
|
874
1214
|
function resolveTeamsFriendStore(deps) {
|
|
875
1215
|
return deps.store
|
|
876
1216
|
?? deps.createFriendStore?.()
|
|
877
|
-
?? new
|
|
1217
|
+
?? new friends_1.FileFriendStore(path.join((0, identity_1.getAgentRoot)(), "friends"));
|
|
878
1218
|
}
|
|
879
1219
|
function getTeamsConversations(botApi) {
|
|
880
1220
|
return botApi.conversations;
|
|
881
1221
|
}
|
|
882
1222
|
function hasExplicitCrossChatAuthorization(params) {
|
|
883
1223
|
return params.intent === "explicit_cross_chat"
|
|
884
|
-
&&
|
|
1224
|
+
&& friends_1.TRUSTED_LEVELS.has(params.authorizingSession?.trustLevel ?? "stranger");
|
|
885
1225
|
}
|
|
886
1226
|
async function sendProactiveTeamsMessageToSession(params, deps) {
|
|
887
1227
|
const store = resolveTeamsFriendStore(deps);
|
|
@@ -903,7 +1243,7 @@ async function sendProactiveTeamsMessageToSession(params, deps) {
|
|
|
903
1243
|
});
|
|
904
1244
|
return { delivered: false, reason: "friend_not_found" };
|
|
905
1245
|
}
|
|
906
|
-
if (!hasExplicitCrossChatAuthorization(params) && !
|
|
1246
|
+
if (!hasExplicitCrossChatAuthorization(params) && !friends_1.TRUSTED_LEVELS.has(friend.trustLevel ?? "stranger")) {
|
|
907
1247
|
(0, runtime_1.emitNervesEvent)({
|
|
908
1248
|
component: "senses",
|
|
909
1249
|
event: "senses.teams_proactive_trust_skip",
|
|
@@ -928,6 +1268,16 @@ async function sendProactiveTeamsMessageToSession(params, deps) {
|
|
|
928
1268
|
});
|
|
929
1269
|
return { delivered: false, reason: "missing_target" };
|
|
930
1270
|
}
|
|
1271
|
+
const internalContentBlockReason = (0, proactive_content_guard_1.getProactiveInternalContentBlockReason)(params.text);
|
|
1272
|
+
if (internalContentBlockReason) {
|
|
1273
|
+
(0, proactive_content_guard_1.emitProactiveInternalContentBlocked)({
|
|
1274
|
+
friendId: params.friendId,
|
|
1275
|
+
sessionKey: params.sessionKey,
|
|
1276
|
+
reason: internalContentBlockReason,
|
|
1277
|
+
source: "session_send",
|
|
1278
|
+
});
|
|
1279
|
+
return { delivered: false, reason: "internal_content_blocked" };
|
|
1280
|
+
}
|
|
931
1281
|
try {
|
|
932
1282
|
const conversation = await conversations.create({
|
|
933
1283
|
bot: { id: deps.botApi.id },
|
|
@@ -938,6 +1288,7 @@ async function sendProactiveTeamsMessageToSession(params, deps) {
|
|
|
938
1288
|
await conversations.activities(conversation.id).create({
|
|
939
1289
|
type: "message",
|
|
940
1290
|
text: params.text,
|
|
1291
|
+
...(params.textFormat ? { textFormat: params.textFormat } : {}),
|
|
941
1292
|
});
|
|
942
1293
|
(0, runtime_1.emitNervesEvent)({
|
|
943
1294
|
component: "senses",
|
|
@@ -1030,6 +1381,20 @@ async function drainAndSendPendingTeams(store, botApi, pendingRoot) {
|
|
|
1030
1381
|
catch { /* ignore */ }
|
|
1031
1382
|
continue;
|
|
1032
1383
|
}
|
|
1384
|
+
const internalBlockReason = (0, proactive_content_guard_1.getProactiveInternalContentBlockReason)(messageText);
|
|
1385
|
+
if (internalBlockReason) {
|
|
1386
|
+
result.skipped++;
|
|
1387
|
+
try {
|
|
1388
|
+
fs.unlinkSync(filePath);
|
|
1389
|
+
}
|
|
1390
|
+
catch { /* ignore */ }
|
|
1391
|
+
(0, proactive_content_guard_1.emitProactiveInternalContentBlocked)({
|
|
1392
|
+
friendId,
|
|
1393
|
+
reason: internalBlockReason,
|
|
1394
|
+
source: "pending_drain",
|
|
1395
|
+
});
|
|
1396
|
+
continue;
|
|
1397
|
+
}
|
|
1033
1398
|
const sendResult = await sendProactiveTeamsMessageToSession({
|
|
1034
1399
|
friendId,
|
|
1035
1400
|
sessionKey: key,
|
|
@@ -1091,7 +1456,7 @@ function startTeamsApp() {
|
|
|
1091
1456
|
const port = process.env.PORT ? Number(process.env.PORT) : (0, config_2.getTeamsChannelConfig)().port;
|
|
1092
1457
|
app.start(port);
|
|
1093
1458
|
// Diagnostic: log tool count at startup to verify deploy
|
|
1094
|
-
const startupTools = (0, tools_1.getToolsForChannel)((0,
|
|
1459
|
+
const startupTools = (0, tools_1.getToolsForChannel)((0, friends_1.getChannelCapabilities)("teams"));
|
|
1095
1460
|
const toolNames = startupTools.map((t) => t.function.name);
|
|
1096
1461
|
(0, runtime_1.emitNervesEvent)({ level: "info", event: "channel.app_started", component: "channels", message: `Teams bot started on port ${port} with ${mode} (chunked streaming)`, meta: { port, mode, toolCount: toolNames.length, hasProactive: toolNames.includes("teams_send_message") } });
|
|
1097
1462
|
// --- Secondary bot (dual-bot support) ---
|