@vellumai/assistant 0.9.0 → 0.10.0-staging.2
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/ARCHITECTURE.md +18 -34
- package/bun.lock +7 -8
- package/docs/activation-funnel-telemetry.md +28 -22
- package/docs/architecture/security.md +29 -28
- package/docs/stt-provider-onboarding.md +3 -5
- package/docs/workflows-testing.md +13 -44
- package/docs/workflows.md +3 -5
- package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +47 -0
- package/node_modules/@vellumai/ces-client/src/rpc-client.ts +28 -5
- package/node_modules/@vellumai/environments/src/seeds.ts +2 -5
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +32 -6
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +119 -0
- package/node_modules/@vellumai/gateway-client/src/types.ts +15 -84
- package/openapi.yaml +976 -63
- package/package.json +2 -1
- package/scripts/sync-llm-catalog.ts +6 -15
- package/scripts/sync-web-search-catalog.ts +3 -11
- package/src/__tests__/access-request-card-view.test.ts +98 -0
- package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +72 -32
- package/src/__tests__/agent-loop-compaction-strip.test.ts +241 -0
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
- package/src/__tests__/agent-loop-output-hooks.test.ts +69 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +25 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -3
- package/src/__tests__/app-compiler.test.ts +15 -1
- package/src/__tests__/app-dir-path-guard.test.ts +0 -1
- package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
- package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
- package/src/__tests__/avatar-identity-sync.test.ts +2 -27
- package/src/__tests__/btw-routes.test.ts +6 -8
- package/src/__tests__/call-pointer-messages.test.ts +28 -0
- package/src/__tests__/cancel-clears-processing.test.ts +89 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -4
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
- package/src/__tests__/checker.test.ts +0 -3
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
- package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
- package/src/__tests__/config-loader-backfill.test.ts +268 -27
- package/src/__tests__/config-schema.test.ts +35 -0
- package/src/__tests__/config-watcher.test.ts +0 -18
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +0 -6
- package/src/__tests__/contacts-tools.test.ts +29 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +58 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-lifecycle.test.ts +7 -9
- package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
- package/src/__tests__/conversation-title-service.test.ts +62 -0
- package/src/__tests__/credential-broker.test.ts +449 -1
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
- package/src/__tests__/credential-execution-tools.test.ts +0 -1
- package/src/__tests__/credential-prompt-route.test.ts +4 -4
- package/src/__tests__/credential-routes.test.ts +360 -0
- package/src/__tests__/credential-security-invariants.test.ts +4 -13
- package/src/__tests__/disk-pressure-policy.test.ts +12 -0
- package/src/__tests__/disk-usage.test.ts +65 -0
- package/src/__tests__/dynamic-page-surface.test.ts +152 -1
- package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
- package/src/__tests__/gateway-flag-listener.test.ts +110 -1
- package/src/__tests__/gateway-only-guard.test.ts +3 -7
- package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
- package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
- package/src/__tests__/guardian-grant-minting.test.ts +3 -35
- package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
- package/src/__tests__/guardian-routing-state.test.ts +0 -1
- package/src/__tests__/headless-browser-mode.test.ts +10 -0
- package/src/__tests__/headless-browser-navigate.test.ts +8 -3
- package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
- package/src/__tests__/host-browser-proxy.test.ts +87 -0
- package/src/__tests__/identity-routes.test.ts +0 -189
- package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
- package/src/__tests__/injector-v3-suppression.test.ts +27 -20
- package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
- package/src/__tests__/invite-redemption-service.test.ts +4 -7
- package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
- package/src/__tests__/llm-catalog-parity.test.ts +30 -23
- package/src/__tests__/llm-resolver.test.ts +70 -24
- package/src/__tests__/llm-schema.test.ts +1 -0
- package/src/__tests__/managed-profile-guard.test.ts +163 -4
- package/src/__tests__/mcp-health-check.test.ts +6 -7
- package/src/__tests__/media-stream-server-integration.test.ts +317 -13
- package/src/__tests__/oauth-provider-seed-logos.test.ts +4 -6
- package/src/__tests__/onboarding-persona-write.test.ts +1 -1
- package/src/__tests__/path-policy.test.ts +34 -0
- package/src/__tests__/persona-resolver.test.ts +49 -14
- package/src/__tests__/plugin-api-model-profiles.test.ts +178 -0
- package/src/__tests__/plugin-api-provider.test.ts +24 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
- package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
- package/src/__tests__/reaction-persistence.test.ts +150 -29
- package/src/__tests__/registry.test.ts +2 -7
- package/src/__tests__/relay-server.test.ts +285 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
- package/src/__tests__/schedule-routes.test.ts +0 -30
- package/src/__tests__/schedule-tools.test.ts +2 -18
- package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
- package/src/__tests__/skill-execute-input.test.ts +51 -1
- package/src/__tests__/skill-runtime-path.test.ts +2 -3
- package/src/__tests__/skills.test.ts +51 -0
- package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
- package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
- package/src/__tests__/subagent-tools.test.ts +266 -0
- package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -0
- package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
- package/src/__tests__/title-generate-hook.test.ts +100 -3
- package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -29
- package/src/__tests__/token-manager.test.ts +519 -0
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
- package/src/__tests__/tool-audit-listener.test.ts +7 -7
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
- package/src/__tests__/tool-executor.test.ts +0 -79
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
- package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
- package/src/__tests__/trusted-contact-verification.test.ts +8 -10
- package/src/__tests__/twilio-routes.test.ts +81 -1
- package/src/__tests__/voice-invite-redemption.test.ts +2 -3
- package/src/__tests__/weak-open-model.test.ts +30 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
- package/src/__tests__/workspace-greetings.test.ts +152 -0
- package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
- package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
- package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
- package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
- package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
- package/src/agent/loop.ts +49 -29
- package/src/api/README.md +6 -6
- package/src/api/events/tool-result.ts +6 -0
- package/src/api/events/workflow-completed.ts +53 -0
- package/src/api/events/workflow-leaf-finished.ts +38 -0
- package/src/api/events/workflow-leaf-started.ts +35 -0
- package/src/api/events/workflow-progress.ts +32 -0
- package/src/api/events/workflow-started.ts +31 -0
- package/src/api/index.ts +40 -0
- package/src/api/responses/conversation-message.ts +28 -4
- package/src/api/responses/home.ts +26 -4
- package/src/api/responses/workflow-journal.ts +53 -0
- package/src/approvals/guardian-card-withdrawal.ts +145 -0
- package/src/approvals/guardian-decision-primitive.ts +26 -3
- package/src/approvals/guardian-request-resolvers.ts +183 -80
- package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
- package/src/calls/call-pointer-messages.ts +10 -4
- package/src/calls/channel-admission-reader.ts +104 -0
- package/src/calls/guardian-dispatch.ts +17 -45
- package/src/calls/media-stream-server.ts +84 -2
- package/src/calls/relay-access-wait.ts +1 -1
- package/src/calls/relay-server.ts +66 -0
- package/src/calls/relay-setup-router.ts +82 -1
- package/src/calls/twilio-routes.ts +17 -8
- package/src/calls/voice-session-bridge.ts +2 -2
- package/src/cli/commands/clients.ts +3 -0
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
- package/src/cli/commands/memory/index.ts +30 -0
- package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
- package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
- package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
- package/src/cli/commands/oauth/status.test.ts +36 -0
- package/src/cli/commands/oauth/status.ts +23 -3
- package/src/cli/commands/plugins.ts +197 -4
- package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +443 -0
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +295 -2
- package/src/cli/lib/diff-plugin.ts +346 -0
- package/src/cli/lib/inspect-plugin.ts +12 -1
- package/src/cli/lib/install-from-github.ts +105 -17
- package/src/cli/lib/merge-plugin-tree.ts +328 -0
- package/src/cli/lib/plugin-fingerprint.ts +14 -0
- package/src/cli/lib/plugin-surfaces.ts +104 -0
- package/src/cli/lib/upgrade-plugin.ts +298 -10
- package/src/cli/program.ts +2 -6
- package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
- package/src/config/assistant-feature-flags.ts +22 -7
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
- package/src/config/bundled-skills/messaging/SKILL.md +6 -4
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
- package/src/config/bundled-skills/subagent/SKILL.md +4 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
- package/src/config/bundled-skills/workflows/SKILL.md +14 -8
- package/src/config/bundled-tool-registry.ts +2 -7
- package/src/config/call-site-defaults.ts +15 -2
- package/src/config/feature-flag-registry.json +46 -31
- package/src/config/inference-profile-validation.ts +26 -0
- package/src/config/llm-resolver.ts +3 -0
- package/src/config/loader.ts +4 -0
- package/src/config/memory-v3-gate.ts +11 -0
- package/src/config/profile-order.ts +28 -0
- package/src/config/schema.ts +8 -6
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/channels.ts +11 -0
- package/src/config/schemas/elevenlabs.ts +0 -1
- package/src/config/schemas/llm.ts +31 -0
- package/src/config/schemas/memory-lifecycle.ts +3 -7
- package/src/config/schemas/memory-v3.ts +6 -0
- package/src/config/schemas/platform.ts +0 -8
- package/src/config/schemas/services.ts +18 -0
- package/src/config/seed-inference-profiles.ts +109 -44
- package/src/config/skills.ts +21 -0
- package/src/config/sync-gated-profiles.ts +220 -0
- package/src/contacts/contact-store.ts +89 -106
- package/src/contacts/contacts-write.ts +5 -22
- package/src/contacts/types.ts +0 -1
- package/src/context/compactor.ts +88 -54
- package/src/context/strip-injections.ts +58 -10
- package/src/context/token-estimator.ts +1 -1
- package/src/credential-execution/process-manager.ts +55 -14
- package/src/credential-execution/prompted-credential.ts +2 -3
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
- package/src/daemon/config-watcher.ts +0 -4
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
- package/src/daemon/conversation-agent-loop.ts +114 -22
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +3 -5
- package/src/daemon/conversation-process.ts +13 -5
- package/src/daemon/conversation-runtime-assembly.ts +13 -15
- package/src/daemon/conversation-slash.ts +2 -23
- package/src/daemon/conversation-surfaces.ts +26 -0
- package/src/daemon/conversation-tool-setup.ts +27 -14
- package/src/daemon/conversation.ts +66 -14
- package/src/daemon/disk-pressure-policy.ts +5 -3
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
- package/src/daemon/handlers/config-a2a.ts +0 -2
- package/src/daemon/handlers/config-channels.ts +15 -16
- package/src/daemon/handlers/config-slack-channel.ts +22 -3
- package/src/daemon/handlers/conversations.ts +107 -0
- package/src/daemon/host-browser-proxy.ts +41 -0
- package/src/daemon/lifecycle.ts +55 -27
- package/src/daemon/message-provenance.ts +2 -0
- package/src/daemon/message-types/contacts.ts +0 -1
- package/src/daemon/message-types/conversations.ts +3 -3
- package/src/daemon/message-types/sync.ts +0 -1
- package/src/daemon/message-types/web-activity.ts +7 -1
- package/src/daemon/message-types/workflows.ts +83 -1
- package/src/daemon/orphan-reaper.test.ts +0 -19
- package/src/daemon/orphan-reaper.ts +2 -24
- package/src/daemon/server.ts +0 -10
- package/src/daemon/tool-setup-types.ts +4 -0
- package/src/daemon/trust-context.ts +1 -1
- package/src/events/tool-audit-listener.ts +2 -2
- package/src/home/feed-source-enrichment.test.ts +151 -0
- package/src/home/feed-source-enrichment.ts +176 -0
- package/src/home/relationship-state.ts +2 -4
- package/src/instrument.ts +18 -6
- package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
- package/src/ipc/assistant-server.ts +37 -4
- package/src/ipc/gateway-flag-listener.ts +18 -2
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
- package/src/memory/__tests__/memory-retrospective-job.test.ts +229 -401
- package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
- package/src/memory/auth-fallback-events-store.ts +2 -2
- package/src/memory/auto-analysis-enqueue.ts +3 -5
- package/src/memory/bookmark-crud.ts +1 -2
- package/src/memory/canonical-guardian-store.ts +39 -1
- package/src/memory/conversation-crud.ts +9 -4
- package/src/memory/conversation-key-store.ts +17 -2
- package/src/memory/conversation-title-service.ts +64 -7
- package/src/memory/db-init.ts +17 -17
- package/src/memory/embedding-backend.ts +38 -1
- package/src/memory/embedding-billing-breaker.ts +96 -0
- package/src/memory/jobs-store.ts +25 -13
- package/src/memory/jobs-worker.ts +54 -1
- package/src/memory/lifecycle-events-store.ts +2 -2
- package/src/memory/memory-retrospective-constants.ts +4 -4
- package/src/memory/memory-retrospective-enqueue.ts +31 -6
- package/src/memory/memory-retrospective-job.ts +28 -227
- package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
- package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
- package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +72 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
- package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
- package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
- package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
- package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
- package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +341 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/run-migrations.ts +41 -0
- package/src/memory/migrations/validate-migration-state.ts +1 -1
- package/src/memory/onboarding-events-store.ts +3 -3
- package/src/memory/schema/contacts.ts +0 -5
- package/src/memory/skill-loaded-events-store.test.ts +7 -15
- package/src/memory/skill-loaded-events-store.ts +2 -2
- package/src/memory/tool-executed-events-store.test.ts +7 -7
- package/src/memory/turn-trace-store.test.ts +736 -0
- package/src/memory/turn-trace-store.ts +364 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
- package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
- package/src/memory/v2/consolidation-job.ts +2 -2
- package/src/memory/v2/skill-content.ts +25 -7
- package/src/memory/v2/skill-store.ts +7 -1
- package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
- package/src/memory/v3-eval/eval-packets.ts +546 -0
- package/src/messaging/providers/slack/adapter.ts +1 -1
- package/src/messaging/providers/slack/api.ts +31 -0
- package/src/messaging/providers/slack/send.test.ts +114 -2
- package/src/messaging/providers/slack/send.ts +30 -7
- package/src/messaging/providers/slack/withdraw.test.ts +200 -0
- package/src/messaging/providers/slack/withdraw.ts +161 -0
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/access-request-copy.ts +72 -59
- package/src/notifications/adapters/shared.ts +29 -0
- package/src/notifications/adapters/slack.ts +58 -103
- package/src/notifications/adapters/telegram.ts +2 -20
- package/src/notifications/approval-card-data.ts +333 -0
- package/src/notifications/broadcaster.ts +16 -3
- package/src/notifications/canonical-delivery-recorder.ts +139 -0
- package/src/notifications/copy-composer.ts +3 -3
- package/src/notifications/decision-engine.ts +4 -2
- package/src/notifications/destination-resolver.ts +4 -6
- package/src/notifications/guardian-question-mode.ts +10 -0
- package/src/notifications/home-feed-side-effect.ts +7 -16
- package/src/notifications/notification-utils.ts +19 -20
- package/src/notifications/signal.ts +79 -43
- package/src/notifications/types.ts +98 -121
- package/src/oauth/AGENTS.md +5 -24
- package/src/permissions/checker.test.ts +51 -0
- package/src/permissions/checker.ts +185 -26
- package/src/permissions/ipc-risk-types.ts +24 -0
- package/src/permissions/question-prompter.test.ts +27 -0
- package/src/permissions/question-prompter.ts +4 -0
- package/src/platform/client.test.ts +119 -0
- package/src/platform/client.ts +66 -0
- package/src/platform/consent-cache.test.ts +267 -0
- package/src/platform/consent-cache.ts +174 -0
- package/src/plugin-api/constants.ts +1 -1
- package/src/plugin-api/index.ts +33 -1
- package/src/plugin-api/model-profiles.ts +33 -0
- package/src/plugin-api/types.ts +50 -2
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
- package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
- package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
- package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
- package/src/plugins/defaults/advisor/config.ts +21 -0
- package/src/plugins/defaults/advisor/consult.ts +93 -0
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/advisor/package.json +14 -0
- package/src/plugins/defaults/advisor/steering.ts +67 -0
- package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
- package/src/plugins/defaults/advisor/transcript.ts +76 -0
- package/src/plugins/defaults/index.ts +60 -0
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +129 -9
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +144 -11
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +276 -0
- package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
- package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
- package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +3 -13
- package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
- package/src/prompts/persona-resolver.ts +14 -4
- package/src/prompts/templates/system-sections.ts +7 -2
- package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
- package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
- package/src/providers/__tests__/retry-callsite.test.ts +176 -0
- package/src/providers/atlascloud/client.ts +85 -0
- package/src/providers/fetch-provider-catalog.ts +85 -0
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/model-catalog.ts +58 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
- package/src/providers/openai/chat-completions-provider.ts +7 -0
- package/src/providers/openai/responses-provider.ts +10 -0
- package/src/providers/provider-send-message.ts +11 -3
- package/src/providers/retry.ts +53 -12
- package/src/providers/search-provider-catalog.ts +10 -0
- package/src/providers/weak-open-model.ts +22 -0
- package/src/runtime/AGENTS.md +0 -1
- package/src/runtime/__tests__/agent-wake.test.ts +181 -0
- package/src/runtime/__tests__/client-health.test.ts +44 -0
- package/src/runtime/access-request-helper.ts +21 -53
- package/src/runtime/actor-trust-resolver.ts +59 -63
- package/src/runtime/agent-wake.ts +52 -0
- package/src/runtime/assistant-event-hub.ts +18 -4
- package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
- package/src/runtime/auth/require-bound-guardian.ts +1 -4
- package/src/runtime/btw-sidechain.ts +3 -6
- package/src/runtime/capabilities.test.ts +120 -0
- package/src/runtime/capabilities.ts +197 -0
- package/src/runtime/channel-approval-types.ts +22 -45
- package/src/runtime/channel-invite-transports/telegram.ts +4 -4
- package/src/runtime/channel-retry-sweep.ts +1 -0
- package/src/runtime/channel-verification-service.ts +3 -3
- package/src/runtime/client-health.ts +26 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
- package/src/runtime/effective-capabilities.test.ts +128 -0
- package/src/runtime/effective-capabilities.ts +84 -0
- package/src/runtime/guardian-reply-router.ts +106 -21
- package/src/runtime/invite-redemption-service.ts +9 -25
- package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
- package/src/runtime/migrations/vbundle-builder.ts +49 -20
- package/src/runtime/pending-interactions.ts +15 -0
- package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +240 -1
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
- package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
- package/src/runtime/routes/browser-tabs-routes.ts +9 -0
- package/src/runtime/routes/btw-routes.ts +1 -27
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
- package/src/runtime/routes/client-routes.ts +10 -0
- package/src/runtime/routes/contact-routes.ts +31 -8
- package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
- package/src/runtime/routes/conversation-management-routes.ts +80 -1
- package/src/runtime/routes/conversation-query-routes.ts +68 -22
- package/src/runtime/routes/conversation-routes.ts +39 -14
- package/src/runtime/routes/credential-routes.ts +40 -16
- package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
- package/src/runtime/routes/events-routes.ts +1 -3
- package/src/runtime/routes/guardian-approval-interception.ts +14 -73
- package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
- package/src/runtime/routes/home-feed-routes.ts +8 -3
- package/src/runtime/routes/identity-routes.ts +1 -296
- package/src/runtime/routes/inbound-message-handler.ts +214 -228
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +89 -7
- package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
- package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
- package/src/runtime/routes/integrations/slack/channel.ts +36 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
- package/src/runtime/routes/mcp-auth-routes.ts +233 -41
- package/src/runtime/routes/memory-eval-routes.ts +87 -0
- package/src/runtime/routes/notification-routes.ts +122 -133
- package/src/runtime/routes/platform-routes.ts +2 -2
- package/src/runtime/routes/plugins-routes.ts +202 -3
- package/src/runtime/routes/schedule-routes.ts +0 -22
- package/src/runtime/routes/secret-routes.ts +10 -0
- package/src/runtime/routes/surface-action-routes.ts +2 -1
- package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
- package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
- package/src/runtime/routes/workflow-routes.test.ts +229 -44
- package/src/runtime/routes/workflow-routes.ts +131 -29
- package/src/runtime/routes/workspace-greetings.ts +55 -0
- package/src/runtime/sync/resource-sync-events.ts +1 -11
- package/src/runtime/tool-grant-request-helper.ts +18 -16
- package/src/runtime/trust-context-resolver.ts +8 -5
- package/src/schedule/inference-profile.ts +2 -14
- package/src/schedule/schedule-store.ts +1 -1
- package/src/schedule/scheduler-types.ts +5 -1
- package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
- package/src/security/secret-patterns.ts +3 -0
- package/src/subagent/manager.ts +17 -4
- package/src/subagent/types.ts +6 -0
- package/src/telemetry/trace-collection-policy.test.ts +28 -0
- package/src/telemetry/trace-collection-policy.ts +30 -0
- package/src/telemetry/types.ts +89 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
- package/src/telemetry/usage-telemetry-reporter.ts +148 -41
- package/src/tools/AGENTS.md +3 -3
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
- package/src/tools/browser/browser-execution.ts +30 -19
- package/src/tools/document/document-tool.ts +2 -3
- package/src/tools/executor.ts +5 -3
- package/src/tools/host-terminal/host-shell.ts +5 -4
- package/src/tools/memory/register.ts +2 -2
- package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
- package/src/tools/network/__tests__/web-search.test.ts +143 -0
- package/src/tools/network/web-fetch.ts +372 -1
- package/src/tools/network/web-search-error.ts +1 -1
- package/src/tools/network/web-search.ts +213 -10
- package/src/tools/permission-checker.ts +4 -3
- package/src/tools/registry.ts +20 -0
- package/src/tools/schedule/create.ts +7 -12
- package/src/tools/schedule/update.ts +4 -11
- package/src/tools/shared/filesystem/path-policy.ts +39 -13
- package/src/tools/side-effects.ts +2 -17
- package/src/tools/skills/execute.ts +33 -0
- package/src/tools/subagent/spawn.ts +61 -12
- package/src/tools/terminal/shell.ts +10 -4
- package/src/tools/tool-approval-handler.ts +18 -13
- package/src/tools/tool-manifest.ts +0 -2
- package/src/tools/types.ts +9 -0
- package/src/tools/ui-surface/definitions.ts +64 -3
- package/src/tools/verification-control-plane-policy.ts +3 -1
- package/src/tools/workflows/run-workflow.test.ts +8 -18
- package/src/tools/workflows/run-workflow.ts +1 -0
- package/src/util/disk-usage.ts +78 -23
- package/src/util/platform.ts +10 -3
- package/src/watcher/telemetry.ts +2 -2
- package/src/workflows/capabilities.ts +2 -3
- package/src/workflows/engine.test.ts +175 -1
- package/src/workflows/engine.ts +82 -0
- package/src/workflows/journal-store.test.ts +70 -0
- package/src/workflows/journal-store.ts +18 -3
- package/src/workflows/run-manager.test.ts +171 -28
- package/src/workflows/run-manager.ts +66 -24
- package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
- package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
- package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
- package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/__tests__/app-control-no-global-cgevent.test.ts +0 -98
- package/src/__tests__/credential-security-e2e.test.ts +0 -362
- package/src/__tests__/credential-vault-unit.test.ts +0 -1528
- package/src/__tests__/credential-vault.test.ts +0 -1706
- package/src/__tests__/identity-intro-cache.test.ts +0 -315
- package/src/__tests__/secret-onetime-send.test.ts +0 -182
- package/src/cli/commands/__tests__/task.test.ts +0 -914
- package/src/cli/commands/task.ts +0 -771
- package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
- package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
- package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
- package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
- package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
- package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
- package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
- package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
- package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
- package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
- package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
- package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
- package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
- package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
- package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
- package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
- package/src/memory/preloaded-apps.ts +0 -116
- package/src/notifications/tool-approval-copy.ts +0 -142
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
- package/src/runtime/routes/identity-intro-cache.ts +0 -172
- package/src/tools/credentials/vault.ts +0 -712
|
@@ -16,7 +16,10 @@ import {
|
|
|
16
16
|
} from "../../../memory/canonical-guardian-store.js";
|
|
17
17
|
import { getLogger } from "../../../util/logger.js";
|
|
18
18
|
import { deliverChannelReply } from "../../gateway-client.js";
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
type GuardianPendingScope,
|
|
21
|
+
routeGuardianReply,
|
|
22
|
+
} from "../../guardian-reply-router.js";
|
|
20
23
|
import type { ApprovalConversationGenerator } from "../../http-types.js";
|
|
21
24
|
|
|
22
25
|
const log = getLogger("runtime-http");
|
|
@@ -30,6 +33,12 @@ export interface GuardianReplyInterceptParams {
|
|
|
30
33
|
trimmedContent: string;
|
|
31
34
|
hasCallbackData: boolean;
|
|
32
35
|
callbackData: string | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* For emoji-reaction decisions: the channel-native id (Slack `ts`) of the
|
|
38
|
+
* message the reaction was attached to. Threaded to the router so it can
|
|
39
|
+
* recover the target request from the reacted card's delivery record.
|
|
40
|
+
*/
|
|
41
|
+
reactedMessageTs?: string;
|
|
33
42
|
rawSenderId: string | undefined;
|
|
34
43
|
canonicalSenderId: string | null;
|
|
35
44
|
canonicalAssistantId: string;
|
|
@@ -65,6 +74,7 @@ export async function handleGuardianReplyIntercept(
|
|
|
65
74
|
trimmedContent,
|
|
66
75
|
hasCallbackData,
|
|
67
76
|
callbackData,
|
|
77
|
+
reactedMessageTs,
|
|
68
78
|
rawSenderId,
|
|
69
79
|
canonicalSenderId,
|
|
70
80
|
canonicalAssistantId,
|
|
@@ -102,43 +112,51 @@ export async function handleGuardianReplyIntercept(
|
|
|
102
112
|
// based pending requests so that requests without delivery rows (e.g.
|
|
103
113
|
// tool_approval requests created inline) are not silently excluded.
|
|
104
114
|
//
|
|
105
|
-
// On Slack, when no delivery-scoped results exist for the current
|
|
106
|
-
//
|
|
107
|
-
// router's identity-based fallback from intercepting unrelated
|
|
115
|
+
// On Slack, when no delivery-scoped results exist for the current chat,
|
|
116
|
+
// use `{ mode: "blocked" }` rather than identity-fallback. This prevents
|
|
117
|
+
// the router's identity-based fallback from intercepting unrelated
|
|
108
118
|
// messages in other channels/threads — a cross-chat hijacking vector
|
|
109
119
|
// unique to Slack where a single guardian is active in many threaded
|
|
110
120
|
// contexts. Explicit callbacks (apr:<id>:<action>) and request codes
|
|
111
121
|
// still work cross-chat because they carry specific request
|
|
112
|
-
// identifiers and bypass the
|
|
122
|
+
// identifiers and bypass the pending-request scope.
|
|
113
123
|
//
|
|
114
|
-
// Non-Slack channels (Telegram, WhatsApp)
|
|
124
|
+
// Non-Slack channels (Telegram, WhatsApp) leave the scope unset so the
|
|
115
125
|
// identity-based fallback stays active. On those channels, delivery
|
|
116
126
|
// rows are created asynchronously (fire-and-forget .then()) so the
|
|
117
127
|
// guardian can reply before the row is persisted. Cross-chat
|
|
118
128
|
// contamination is unlikely there because each chat is a distinct
|
|
119
129
|
// conversation with no thread concept.
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
130
|
+
// Reactions address one specific request by the reacted card's message id,
|
|
131
|
+
// so they bypass the pending-request list scoping that the text/NL paths
|
|
132
|
+
// need — the router's reaction branch resolves the target directly.
|
|
133
|
+
const isReaction = callbackData?.startsWith("reaction:") === true;
|
|
134
|
+
let pendingScope: GuardianPendingScope | undefined;
|
|
135
|
+
if (!isReaction) {
|
|
136
|
+
const deliveryScopedPendingRequests =
|
|
137
|
+
listPendingCanonicalGuardianRequestsByDestinationChat(
|
|
138
|
+
sourceChannel,
|
|
139
|
+
conversationExternalId,
|
|
140
|
+
);
|
|
141
|
+
if (deliveryScopedPendingRequests.length > 0) {
|
|
142
|
+
const deliveryIds = new Set(
|
|
143
|
+
deliveryScopedPendingRequests.map((r) => r.id),
|
|
144
|
+
);
|
|
145
|
+
// Also include identity-based pending requests so we don't hide them
|
|
146
|
+
const identityId = canonicalSenderId ?? rawSenderId!;
|
|
147
|
+
const identityPending = listCanonicalGuardianRequests({
|
|
148
|
+
status: "pending",
|
|
149
|
+
guardianExternalUserId: identityId,
|
|
150
|
+
});
|
|
151
|
+
for (const r of identityPending) {
|
|
152
|
+
deliveryIds.add(r.id);
|
|
153
|
+
}
|
|
154
|
+
pendingScope = { mode: "scoped", requestIds: [...deliveryIds] };
|
|
155
|
+
} else if (sourceChannel === "slack") {
|
|
156
|
+
// Block identity-based fallback on Slack to prevent cross-chat
|
|
157
|
+
// NL/free-text interception. See comment above for rationale.
|
|
158
|
+
pendingScope = { mode: "blocked" };
|
|
136
159
|
}
|
|
137
|
-
pendingRequestIds = [...deliveryIds];
|
|
138
|
-
} else if (sourceChannel === "slack") {
|
|
139
|
-
// Block identity-based fallback on Slack to prevent cross-chat
|
|
140
|
-
// NL/free-text interception. See comment above for rationale.
|
|
141
|
-
pendingRequestIds = [];
|
|
142
160
|
}
|
|
143
161
|
|
|
144
162
|
const routerResult = await routeGuardianReply({
|
|
@@ -152,7 +170,8 @@ export async function handleGuardianReplyIntercept(
|
|
|
152
170
|
},
|
|
153
171
|
conversationId,
|
|
154
172
|
callbackData,
|
|
155
|
-
|
|
173
|
+
reactedMessageTs,
|
|
174
|
+
pendingScope,
|
|
156
175
|
approvalConversationGenerator,
|
|
157
176
|
channelDeliveryContext: {
|
|
158
177
|
replyCallbackUrl,
|
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Slack reaction intercept stage.
|
|
3
|
+
*
|
|
4
|
+
* Reactions are passive channel signals — not messages, and not access
|
|
5
|
+
* attempts. They are dispatched here *before* the message pipeline (ACL,
|
|
6
|
+
* admission floor, disk-pressure block, conversation binding) so that:
|
|
7
|
+
*
|
|
8
|
+
* - a 👍 never triggers an ingress access challenge / verification handshake
|
|
9
|
+
* or an access-request notification (LUM-2489),
|
|
10
|
+
* - a stranger's reaction creates no conversation, binding, or transcript
|
|
11
|
+
* row — it is dropped as channel noise,
|
|
12
|
+
* - a known contact's reaction is recorded as an inline transcript signal,
|
|
13
|
+
* - a guardian's reaction on an approval card is routed through the canonical
|
|
14
|
+
* guardian decision pipeline (the same path as buttons and text replies).
|
|
15
|
+
*
|
|
16
|
+
* Reactions never drive an agent turn.
|
|
17
|
+
*/
|
|
18
|
+
import type { SourceMetadata } from "@vellumai/gateway-client";
|
|
19
|
+
|
|
20
|
+
import type { ChannelId, InterfaceId } from "../../../channels/types.js";
|
|
21
|
+
import { getDiskPressureStatus } from "../../../daemon/disk-pressure-guard.js";
|
|
22
|
+
import { classifyDiskPressureTurnPolicy } from "../../../daemon/disk-pressure-policy.js";
|
|
23
|
+
import { addMessage } from "../../../memory/conversation-crud.js";
|
|
24
|
+
import {
|
|
25
|
+
clearPayload,
|
|
26
|
+
linkMessage,
|
|
27
|
+
recordInbound,
|
|
28
|
+
} from "../../../memory/delivery-crud.js";
|
|
29
|
+
import { markProcessed } from "../../../memory/delivery-status.js";
|
|
30
|
+
import { upsertBinding } from "../../../memory/external-conversation-store.js";
|
|
31
|
+
import {
|
|
32
|
+
type SlackMessageMetadata,
|
|
33
|
+
writeSlackMetadata,
|
|
34
|
+
} from "../../../messaging/providers/slack/message-metadata.js";
|
|
35
|
+
import { getLogger } from "../../../util/logger.js";
|
|
36
|
+
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../../assistant-scope.js";
|
|
37
|
+
import type { ApprovalConversationGenerator } from "../../http-types.js";
|
|
38
|
+
import { resolveTrustContext } from "../../trust-context-resolver.js";
|
|
39
|
+
import { handleGuardianReplyIntercept } from "./guardian-reply-intercept.js";
|
|
40
|
+
|
|
41
|
+
const log = getLogger("runtime-http");
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Detect a Slack reaction event by inspecting the inbound payload's
|
|
45
|
+
* `callbackData` prefix. The gateway encodes reactions as a unified
|
|
46
|
+
* `SlackInboundEvent` with `callbackData` of the form `reaction:<emoji>`
|
|
47
|
+
* (added) or `reaction_removed:<emoji>` (removed) — see
|
|
48
|
+
* `gateway/src/slack/normalize.ts`. This helper centralizes that convention
|
|
49
|
+
* so the daemon can route reactions to this dedicated stage instead of the
|
|
50
|
+
* agent-response pipeline.
|
|
51
|
+
*/
|
|
52
|
+
export function isSlackReactionEvent(body: {
|
|
53
|
+
sourceChannel?: string;
|
|
54
|
+
callbackData?: string;
|
|
55
|
+
}): boolean {
|
|
56
|
+
if (body.sourceChannel !== "slack") return false;
|
|
57
|
+
const cb = body.callbackData;
|
|
58
|
+
if (typeof cb !== "string") return false;
|
|
59
|
+
return cb.startsWith("reaction:") || cb.startsWith("reaction_removed:");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Parse a reaction `callbackData` string into its op (added/removed) and
|
|
64
|
+
* emoji name. Returns `null` when the input is not a reaction prefix or
|
|
65
|
+
* when the emoji portion is empty.
|
|
66
|
+
*/
|
|
67
|
+
export function parseSlackReactionCallbackData(
|
|
68
|
+
callbackData: string,
|
|
69
|
+
): { op: "added" | "removed"; emoji: string } | null {
|
|
70
|
+
let op: "added" | "removed";
|
|
71
|
+
let emoji: string;
|
|
72
|
+
if (callbackData.startsWith("reaction_removed:")) {
|
|
73
|
+
op = "removed";
|
|
74
|
+
emoji = callbackData.slice("reaction_removed:".length);
|
|
75
|
+
} else if (callbackData.startsWith("reaction:")) {
|
|
76
|
+
op = "added";
|
|
77
|
+
emoji = callbackData.slice("reaction:".length);
|
|
78
|
+
} else {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
if (emoji.length === 0) return null;
|
|
82
|
+
return { op, emoji };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export interface ReactionInterceptParams {
|
|
86
|
+
/** The reaction callbackData (`reaction:<emoji>` / `reaction_removed:<emoji>`). */
|
|
87
|
+
callbackData: string;
|
|
88
|
+
sourceChannel: ChannelId;
|
|
89
|
+
sourceInterface: InterfaceId | undefined;
|
|
90
|
+
conversationExternalId: string;
|
|
91
|
+
externalMessageId: string;
|
|
92
|
+
canonicalAssistantId: string;
|
|
93
|
+
rawSenderId: string | undefined;
|
|
94
|
+
canonicalSenderId: string | null;
|
|
95
|
+
actorDisplayName: string | undefined;
|
|
96
|
+
actorUsername: string | undefined;
|
|
97
|
+
replyCallbackUrl: string | undefined;
|
|
98
|
+
sourceMetadata: SourceMetadata | undefined;
|
|
99
|
+
/** Slack channel display name, for the conversation binding. */
|
|
100
|
+
slackChannelName: string | null;
|
|
101
|
+
approvalConversationGenerator: ApprovalConversationGenerator | undefined;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Handle a Slack reaction event end to end. Always consumes the event (the
|
|
106
|
+
* caller dispatches here only for `isSlackReactionEvent`), returning the
|
|
107
|
+
* response the top-level handler should short-circuit with.
|
|
108
|
+
*/
|
|
109
|
+
export async function handleSlackReactionIntercept(
|
|
110
|
+
params: ReactionInterceptParams,
|
|
111
|
+
): Promise<Record<string, unknown>> {
|
|
112
|
+
const {
|
|
113
|
+
callbackData,
|
|
114
|
+
sourceChannel,
|
|
115
|
+
sourceInterface,
|
|
116
|
+
conversationExternalId,
|
|
117
|
+
externalMessageId,
|
|
118
|
+
canonicalAssistantId,
|
|
119
|
+
rawSenderId,
|
|
120
|
+
canonicalSenderId,
|
|
121
|
+
actorDisplayName,
|
|
122
|
+
actorUsername,
|
|
123
|
+
replyCallbackUrl,
|
|
124
|
+
sourceMetadata,
|
|
125
|
+
slackChannelName,
|
|
126
|
+
approvalConversationGenerator,
|
|
127
|
+
} = params;
|
|
128
|
+
|
|
129
|
+
// Classify the reactor. No timezone enrichment — reactions never drive an
|
|
130
|
+
// agent turn, so only the trust class / guardian principal matter.
|
|
131
|
+
const trustCtx = resolveTrustContext({
|
|
132
|
+
assistantId: canonicalAssistantId,
|
|
133
|
+
sourceChannel,
|
|
134
|
+
conversationExternalId,
|
|
135
|
+
actorExternalId: rawSenderId,
|
|
136
|
+
actorUsername,
|
|
137
|
+
actorDisplayName,
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Drop strangers before any write. `unknown` covers no contact record and
|
|
141
|
+
// blocked/revoked contacts — a reaction from them is channel noise. Dropping
|
|
142
|
+
// here (before recordInbound/upsertBinding) means no empty conversation or
|
|
143
|
+
// binding is created on their behalf.
|
|
144
|
+
if (trustCtx.trustClass === "unknown") {
|
|
145
|
+
log.debug(
|
|
146
|
+
{ sourceChannel, conversationExternalId },
|
|
147
|
+
"Dropping reaction from unknown actor",
|
|
148
|
+
);
|
|
149
|
+
return { accepted: true, reaction: "dropped_unknown_actor" };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const reactedMessageTs =
|
|
153
|
+
typeof sourceMetadata?.messageId === "string"
|
|
154
|
+
? sourceMetadata.messageId
|
|
155
|
+
: undefined;
|
|
156
|
+
const threadTs =
|
|
157
|
+
typeof sourceMetadata?.threadId === "string" &&
|
|
158
|
+
sourceMetadata.threadId.trim().length > 0
|
|
159
|
+
? sourceMetadata.threadId.trim()
|
|
160
|
+
: undefined;
|
|
161
|
+
|
|
162
|
+
// Record for dedup + conversation resolution (known contacts only — strangers
|
|
163
|
+
// were dropped above).
|
|
164
|
+
const result = recordInbound(
|
|
165
|
+
sourceChannel,
|
|
166
|
+
conversationExternalId,
|
|
167
|
+
externalMessageId,
|
|
168
|
+
{
|
|
169
|
+
sourceMessageId: reactedMessageTs,
|
|
170
|
+
assistantId: canonicalAssistantId,
|
|
171
|
+
sourceThreadId: threadTs,
|
|
172
|
+
},
|
|
173
|
+
);
|
|
174
|
+
|
|
175
|
+
// Respect disk-pressure cleanup so reactions don't bypass storage
|
|
176
|
+
// protection. Guardians resolve to `allow-cleanup-mode` (not `block`), so a
|
|
177
|
+
// guardian's approval-by-reaction still flows.
|
|
178
|
+
const diskPressure = classifyDiskPressureTurnPolicy(getDiskPressureStatus(), {
|
|
179
|
+
sourceChannel,
|
|
180
|
+
sourceInterface,
|
|
181
|
+
trustContext: {
|
|
182
|
+
sourceChannel: trustCtx.sourceChannel,
|
|
183
|
+
trustClass: trustCtx.trustClass,
|
|
184
|
+
},
|
|
185
|
+
});
|
|
186
|
+
if (diskPressure.action === "block") {
|
|
187
|
+
// Block silently: a reaction is a passive signal, so the message
|
|
188
|
+
// pipeline's "storage is low, try again" notice is meaningless for an
|
|
189
|
+
// emoji — there is nothing to retry. Mark the event processed and stop
|
|
190
|
+
// before binding/persistence.
|
|
191
|
+
if (!result.duplicate) {
|
|
192
|
+
clearPayload(result.eventId);
|
|
193
|
+
markProcessed(result.eventId);
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
accepted: true,
|
|
197
|
+
duplicate: result.duplicate,
|
|
198
|
+
eventId: result.eventId,
|
|
199
|
+
diskPressure: "blocked",
|
|
200
|
+
reason: diskPressure.reason,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Maintain the conversation binding, matching the message pipeline. Scoped to
|
|
205
|
+
// the daemon's own assistant so assistant-scoped legacy routes don't clobber
|
|
206
|
+
// each other's binding metadata.
|
|
207
|
+
if (canonicalAssistantId === DAEMON_INTERNAL_ASSISTANT_ID) {
|
|
208
|
+
upsertBinding({
|
|
209
|
+
conversationId: result.conversationId,
|
|
210
|
+
sourceChannel,
|
|
211
|
+
externalChatId: conversationExternalId,
|
|
212
|
+
externalChatName: slackChannelName,
|
|
213
|
+
externalThreadId: threadTs ?? null,
|
|
214
|
+
externalUserId: canonicalSenderId ?? rawSenderId ?? null,
|
|
215
|
+
displayName: actorDisplayName ?? null,
|
|
216
|
+
username: actorUsername ?? null,
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Guardian approval-by-reaction → canonical decision pipeline, exactly like
|
|
221
|
+
// buttons and text replies. Only `reaction:` (added) expresses intent;
|
|
222
|
+
// `reaction_removed:` never does. `handleGuardianReplyIntercept` self-gates
|
|
223
|
+
// on `trustClass === "guardian"`, so a contact's reaction returns no response
|
|
224
|
+
// and falls through to persistence.
|
|
225
|
+
const isReactionAdded = callbackData.startsWith("reaction:");
|
|
226
|
+
if (isReactionAdded && replyCallbackUrl && !result.duplicate) {
|
|
227
|
+
const reactionIntercept = await handleGuardianReplyIntercept({
|
|
228
|
+
isDuplicate: result.duplicate,
|
|
229
|
+
trimmedContent: "",
|
|
230
|
+
hasCallbackData: true,
|
|
231
|
+
callbackData,
|
|
232
|
+
reactedMessageTs,
|
|
233
|
+
rawSenderId,
|
|
234
|
+
canonicalSenderId,
|
|
235
|
+
canonicalAssistantId,
|
|
236
|
+
sourceChannel,
|
|
237
|
+
conversationExternalId,
|
|
238
|
+
conversationId: result.conversationId,
|
|
239
|
+
eventId: result.eventId,
|
|
240
|
+
replyCallbackUrl,
|
|
241
|
+
trustClass: trustCtx.trustClass,
|
|
242
|
+
guardianPrincipalId: trustCtx.guardianPrincipalId,
|
|
243
|
+
approvalConversationGenerator,
|
|
244
|
+
});
|
|
245
|
+
// Consumed as a guardian decision (applied, or a surfaced failure delivered
|
|
246
|
+
// as an ephemeral reply). Short-circuit so we do not also persist a
|
|
247
|
+
// transcript row.
|
|
248
|
+
if (reactionIntercept.response) {
|
|
249
|
+
return reactionIntercept.response;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Record the reaction as an inline transcript signal. Requires the reacted
|
|
254
|
+
// message ts to anchor the rendering.
|
|
255
|
+
if (!reactedMessageTs) {
|
|
256
|
+
log.debug(
|
|
257
|
+
{ conversationId: result.conversationId, eventId: result.eventId },
|
|
258
|
+
"Skipping reaction persistence: missing sourceMetadata.messageId",
|
|
259
|
+
);
|
|
260
|
+
return {
|
|
261
|
+
accepted: result.accepted,
|
|
262
|
+
duplicate: result.duplicate,
|
|
263
|
+
eventId: result.eventId,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
try {
|
|
268
|
+
await persistSlackReactionAsMessage({
|
|
269
|
+
conversationId: result.conversationId,
|
|
270
|
+
conversationExternalId,
|
|
271
|
+
eventId: result.eventId,
|
|
272
|
+
callbackData,
|
|
273
|
+
actorDisplayName,
|
|
274
|
+
threadTs,
|
|
275
|
+
reactedMessageTs,
|
|
276
|
+
duplicate: result.duplicate,
|
|
277
|
+
});
|
|
278
|
+
} catch (err) {
|
|
279
|
+
log.error(
|
|
280
|
+
{ err, conversationId: result.conversationId, eventId: result.eventId },
|
|
281
|
+
"Failed to persist Slack reaction event",
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return {
|
|
286
|
+
accepted: result.accepted,
|
|
287
|
+
duplicate: result.duplicate,
|
|
288
|
+
eventId: result.eventId,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Persist a Slack reaction event as a `messages` row with a `slackMeta`
|
|
294
|
+
* envelope so the renderer can surface it inline in the chronological
|
|
295
|
+
* transcript. Reactions do not trigger an agent response — the row is written
|
|
296
|
+
* and the inbound event is linked, but the agent loop is not dispatched.
|
|
297
|
+
*
|
|
298
|
+
* The caller is expected to have run `recordInbound` already so that
|
|
299
|
+
* deduplication and conversation resolution have happened. Duplicate inbound
|
|
300
|
+
* events are skipped here to keep persistence idempotent.
|
|
301
|
+
*/
|
|
302
|
+
async function persistSlackReactionAsMessage(params: {
|
|
303
|
+
conversationId: string;
|
|
304
|
+
conversationExternalId: string;
|
|
305
|
+
eventId: string;
|
|
306
|
+
callbackData: string;
|
|
307
|
+
actorDisplayName?: string;
|
|
308
|
+
threadTs?: string;
|
|
309
|
+
reactedMessageTs: string;
|
|
310
|
+
duplicate: boolean;
|
|
311
|
+
}): Promise<void> {
|
|
312
|
+
if (params.duplicate) return;
|
|
313
|
+
|
|
314
|
+
const parsed = parseSlackReactionCallbackData(params.callbackData);
|
|
315
|
+
if (!parsed) {
|
|
316
|
+
log.debug(
|
|
317
|
+
{
|
|
318
|
+
conversationId: params.conversationId,
|
|
319
|
+
callbackData: params.callbackData,
|
|
320
|
+
},
|
|
321
|
+
"Skipping reaction persistence: unparseable callbackData",
|
|
322
|
+
);
|
|
323
|
+
return;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const slackMeta: SlackMessageMetadata = {
|
|
327
|
+
source: "slack",
|
|
328
|
+
channelId: params.conversationExternalId,
|
|
329
|
+
channelTs: params.reactedMessageTs,
|
|
330
|
+
eventKind: "reaction",
|
|
331
|
+
...(params.threadTs ? { threadTs: params.threadTs } : {}),
|
|
332
|
+
...(params.actorDisplayName
|
|
333
|
+
? { displayName: params.actorDisplayName }
|
|
334
|
+
: {}),
|
|
335
|
+
reaction: {
|
|
336
|
+
emoji: parsed.emoji,
|
|
337
|
+
targetChannelTs: params.reactedMessageTs,
|
|
338
|
+
op: parsed.op,
|
|
339
|
+
...(params.actorDisplayName
|
|
340
|
+
? { actorDisplayName: params.actorDisplayName }
|
|
341
|
+
: {}),
|
|
342
|
+
},
|
|
343
|
+
};
|
|
344
|
+
|
|
345
|
+
// Sentinel content — Slack transcript renderers read `slackMeta` to format
|
|
346
|
+
// the reaction line; the literal text is never displayed to the model.
|
|
347
|
+
const persisted = await addMessage(
|
|
348
|
+
params.conversationId,
|
|
349
|
+
"user",
|
|
350
|
+
"[reaction]",
|
|
351
|
+
{
|
|
352
|
+
metadata: { slackMeta: writeSlackMetadata(slackMeta) },
|
|
353
|
+
skipIndexing: true,
|
|
354
|
+
},
|
|
355
|
+
);
|
|
356
|
+
linkMessage(params.eventId, persisted.id);
|
|
357
|
+
markProcessed(params.eventId);
|
|
358
|
+
}
|
|
@@ -90,6 +90,7 @@ import { ROUTES as INTERNAL_TWILIO_ROUTES } from "./internal-twilio-routes.js";
|
|
|
90
90
|
import { ROUTES as LLM_CALL_SITES_ROUTES } from "./llm-call-sites-routes.js";
|
|
91
91
|
import { ROUTES as LOG_EXPORT_ROUTES } from "./log-export-routes.js";
|
|
92
92
|
import { ROUTES as MCP_AUTH_ROUTES } from "./mcp-auth-routes.js";
|
|
93
|
+
import { ROUTES as MEMORY_EVAL_ROUTES } from "./memory-eval-routes.js";
|
|
93
94
|
import { ROUTES as MEMORY_ITEM_ROUTES } from "./memory-item-routes.js";
|
|
94
95
|
import { ROUTES as MEMORY_V2_ROUTES } from "./memory-v2-routes.js";
|
|
95
96
|
import { ROUTES as MEMORY_V3_ROUTES } from "./memory-v3-routes.js";
|
|
@@ -222,6 +223,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
222
223
|
...INTERNAL_TWILIO_ROUTES,
|
|
223
224
|
...LOG_EXPORT_ROUTES,
|
|
224
225
|
...LLM_CALL_SITES_ROUTES,
|
|
226
|
+
...MEMORY_EVAL_ROUTES,
|
|
225
227
|
...MEMORY_ITEM_ROUTES,
|
|
226
228
|
...MEMORY_V2_ROUTES,
|
|
227
229
|
...MEMORY_V3_ROUTES,
|
|
@@ -28,6 +28,7 @@ let mockSetConfigResult: SlackChannelConfigResult = {
|
|
|
28
28
|
hasAppToken: false,
|
|
29
29
|
hasUserToken: false,
|
|
30
30
|
connected: false,
|
|
31
|
+
threadMode: "mention_only",
|
|
31
32
|
};
|
|
32
33
|
|
|
33
34
|
mock.module("../../../../../daemon/handlers/config-slack-channel.js", () => ({
|
|
@@ -37,6 +38,7 @@ mock.module("../../../../../daemon/handlers/config-slack-channel.js", () => ({
|
|
|
37
38
|
hasAppToken: z.boolean(),
|
|
38
39
|
hasUserToken: z.boolean(),
|
|
39
40
|
connected: z.boolean(),
|
|
41
|
+
threadMode: z.enum(["mention_only", "mention_then_thread"]),
|
|
40
42
|
teamId: z.string().optional(),
|
|
41
43
|
teamName: z.string().optional(),
|
|
42
44
|
teamUrl: z.string().optional(),
|
|
@@ -45,6 +47,7 @@ mock.module("../../../../../daemon/handlers/config-slack-channel.js", () => ({
|
|
|
45
47
|
error: z.string().optional(),
|
|
46
48
|
warning: z.string().optional(),
|
|
47
49
|
}),
|
|
50
|
+
SlackThreadMode: z.enum(["mention_only", "mention_then_thread"]),
|
|
48
51
|
setSlackChannelConfig: async (
|
|
49
52
|
botToken?: string,
|
|
50
53
|
appToken?: string,
|
|
@@ -59,6 +62,7 @@ mock.module("../../../../../daemon/handlers/config-slack-channel.js", () => ({
|
|
|
59
62
|
hasAppToken: false,
|
|
60
63
|
hasUserToken: false,
|
|
61
64
|
connected: false,
|
|
65
|
+
threadMode: "mention_only",
|
|
62
66
|
}),
|
|
63
67
|
clearSlackChannelConfig: async (): Promise<SlackChannelConfigResult> => ({
|
|
64
68
|
success: true,
|
|
@@ -66,7 +70,9 @@ mock.module("../../../../../daemon/handlers/config-slack-channel.js", () => ({
|
|
|
66
70
|
hasAppToken: false,
|
|
67
71
|
hasUserToken: false,
|
|
68
72
|
connected: false,
|
|
73
|
+
threadMode: "mention_only",
|
|
69
74
|
}),
|
|
75
|
+
patchSlackChannelConfig: () => {},
|
|
70
76
|
}));
|
|
71
77
|
|
|
72
78
|
import { BadRequestError } from "../../../errors.js";
|
|
@@ -81,6 +87,7 @@ describe("POST /v1/integrations/slack/channel/config", () => {
|
|
|
81
87
|
hasAppToken: false,
|
|
82
88
|
hasUserToken: false,
|
|
83
89
|
connected: false,
|
|
90
|
+
threadMode: "mention_only",
|
|
84
91
|
};
|
|
85
92
|
});
|
|
86
93
|
|
|
@@ -129,6 +136,7 @@ describe("POST /v1/integrations/slack/channel/config", () => {
|
|
|
129
136
|
hasAppToken: false,
|
|
130
137
|
hasUserToken: false,
|
|
131
138
|
connected: false,
|
|
139
|
+
threadMode: "mention_only",
|
|
132
140
|
error: 'Invalid user token: must start with "xoxp-"',
|
|
133
141
|
};
|
|
134
142
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* GET /v1/integrations/slack/channel/config — get current config status
|
|
5
5
|
* POST /v1/integrations/slack/channel/config — validate and store credentials
|
|
6
|
+
* PATCH /v1/integrations/slack/channel/config — update channel settings
|
|
6
7
|
* DELETE /v1/integrations/slack/channel/config — clear credentials
|
|
7
8
|
*/
|
|
8
9
|
|
|
@@ -11,8 +12,10 @@ import { z } from "zod";
|
|
|
11
12
|
import {
|
|
12
13
|
clearSlackChannelConfig,
|
|
13
14
|
getSlackChannelConfig,
|
|
15
|
+
patchSlackChannelConfig,
|
|
14
16
|
setSlackChannelConfig,
|
|
15
17
|
SlackChannelConfigResultSchema,
|
|
18
|
+
SlackThreadMode,
|
|
16
19
|
} from "../../../../daemon/handlers/config-slack-channel.js";
|
|
17
20
|
import { ACTOR_PRINCIPALS } from "../../../auth/route-policy.js";
|
|
18
21
|
import { BadRequestError } from "../../errors.js";
|
|
@@ -43,6 +46,20 @@ export async function handleSetSlackChannelConfig({
|
|
|
43
46
|
return result;
|
|
44
47
|
}
|
|
45
48
|
|
|
49
|
+
async function handlePatchSlackChannelConfig({ body = {} }: RouteHandlerArgs) {
|
|
50
|
+
const { threadMode } = body as { threadMode?: string };
|
|
51
|
+
if (threadMode !== undefined) {
|
|
52
|
+
const parsed = SlackThreadMode.safeParse(threadMode);
|
|
53
|
+
if (!parsed.success) {
|
|
54
|
+
throw new BadRequestError(
|
|
55
|
+
"threadMode must be 'mention_only' or 'mention_then_thread'",
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
patchSlackChannelConfig(parsed.data);
|
|
59
|
+
}
|
|
60
|
+
return getSlackChannelConfig();
|
|
61
|
+
}
|
|
62
|
+
|
|
46
63
|
async function handleClearSlackChannelConfig() {
|
|
47
64
|
return clearSlackChannelConfig();
|
|
48
65
|
}
|
|
@@ -84,6 +101,25 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
84
101
|
}),
|
|
85
102
|
responseBody: SlackChannelConfigResultSchema,
|
|
86
103
|
},
|
|
104
|
+
{
|
|
105
|
+
operationId: "integrations_slack_channel_config_patch",
|
|
106
|
+
endpoint: "integrations/slack/channel/config",
|
|
107
|
+
method: "PATCH",
|
|
108
|
+
policy: {
|
|
109
|
+
requiredScopes: ["settings.write"],
|
|
110
|
+
allowedPrincipalTypes: ACTOR_PRINCIPALS,
|
|
111
|
+
},
|
|
112
|
+
summary: "Update Slack channel settings",
|
|
113
|
+
description: "Update Slack channel behavior settings (e.g. thread mode).",
|
|
114
|
+
tags: ["integrations"],
|
|
115
|
+
handler: handlePatchSlackChannelConfig,
|
|
116
|
+
requestBody: z.object({
|
|
117
|
+
threadMode: SlackThreadMode.describe(
|
|
118
|
+
"Controls whether the bot follows threads after an initial @mention",
|
|
119
|
+
).optional(),
|
|
120
|
+
}),
|
|
121
|
+
responseBody: SlackChannelConfigResultSchema,
|
|
122
|
+
},
|
|
87
123
|
{
|
|
88
124
|
operationId: "integrations_slack_channel_config_delete",
|
|
89
125
|
endpoint: "integrations/slack/channel/config",
|
|
@@ -53,7 +53,7 @@ function handleRecordAuthFallback({ body }: RouteHandlerArgs) {
|
|
|
53
53
|
|
|
54
54
|
const recorded = recordAuthFallbackCounts(window_start, window_end, mapped);
|
|
55
55
|
if (recorded === 0) {
|
|
56
|
-
//
|
|
56
|
+
// share_analytics consent off — counts dropped to honor the opt-out.
|
|
57
57
|
return { skipped: true };
|
|
58
58
|
}
|
|
59
59
|
log.debug({ recorded }, "Recorded auth-fallback counts");
|