@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
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* record, enforces allow/deny/escalate policies, handles invite token
|
|
4
4
|
* intercepts, and notifies the guardian of denied access requests.
|
|
5
5
|
*/
|
|
6
|
-
import type { SourceMetadata } from "@vellumai/gateway-client";
|
|
6
|
+
import type { AdmissionPolicy, SourceMetadata } from "@vellumai/gateway-client";
|
|
7
7
|
|
|
8
8
|
import { isInviteCodeRedemptionEnabled } from "../../../channels/config.js";
|
|
9
9
|
import type { ChannelId } from "../../../channels/types.js";
|
|
@@ -90,6 +90,18 @@ export interface AclEnforcementParams {
|
|
|
90
90
|
replyCallbackUrl: string | undefined;
|
|
91
91
|
assistantId: string;
|
|
92
92
|
externalMessageId: string;
|
|
93
|
+
/**
|
|
94
|
+
* Effective admission policy for this request (gateway floor resolved with
|
|
95
|
+
* any per-conversation override). When set, ACL skips its hard-deny paths
|
|
96
|
+
* when the policy is permissive enough:
|
|
97
|
+
* - `strangers`: non-members and inactive (non-blocked) members are passed
|
|
98
|
+
* through so the admission floor can emit the final verdict.
|
|
99
|
+
* - `any_contact`: inactive `pending` members are passed through.
|
|
100
|
+
*
|
|
101
|
+
* Passing this in avoids having ACL fire guardian notifications and canned
|
|
102
|
+
* replies for senders who will be admitted by the floor stage anyway.
|
|
103
|
+
*/
|
|
104
|
+
effectiveAdmissionPolicy?: AdmissionPolicy;
|
|
93
105
|
}
|
|
94
106
|
|
|
95
107
|
/** Resolved contact + channel pair from ACL enforcement. */
|
|
@@ -102,6 +114,12 @@ export interface AclResult {
|
|
|
102
114
|
resolvedMember: ResolvedMember | null;
|
|
103
115
|
/** When set, the caller must return this response immediately. */
|
|
104
116
|
earlyResponse?: Record<string, unknown>;
|
|
117
|
+
/**
|
|
118
|
+
* True when a valid `pending_bootstrap` session was resolved during ACL
|
|
119
|
+
* enforcement. The caller must skip the admission policy floor so the
|
|
120
|
+
* bootstrap intercept stage can handle identity binding and emit its reply.
|
|
121
|
+
*/
|
|
122
|
+
isValidatedBootstrap?: boolean;
|
|
105
123
|
}
|
|
106
124
|
|
|
107
125
|
/** Map ChannelStatus to the API-facing member status (excludes "unverified"). */
|
|
@@ -134,8 +152,11 @@ export async function enforceIngressAcl(
|
|
|
134
152
|
replyCallbackUrl,
|
|
135
153
|
assistantId,
|
|
136
154
|
externalMessageId,
|
|
155
|
+
effectiveAdmissionPolicy,
|
|
137
156
|
} = params;
|
|
138
157
|
|
|
158
|
+
let isValidatedBootstrap = false;
|
|
159
|
+
|
|
139
160
|
// Trust signals from Slack users.info, forwarded via sourceMetadata.
|
|
140
161
|
const isStranger = sourceMetadata?.isStranger ?? undefined;
|
|
141
162
|
const isRestricted = sourceMetadata?.isRestricted ?? undefined;
|
|
@@ -166,7 +187,7 @@ export async function enforceIngressAcl(
|
|
|
166
187
|
if (canonicalSenderId) {
|
|
167
188
|
const contactResult = findContactChannel({
|
|
168
189
|
channelType: sourceChannel,
|
|
169
|
-
|
|
190
|
+
address: canonicalSenderId,
|
|
170
191
|
externalChatId: conversationExternalId,
|
|
171
192
|
});
|
|
172
193
|
resolvedMember = contactResult
|
|
@@ -196,6 +217,7 @@ export async function enforceIngressAcl(
|
|
|
196
217
|
bootstrapSessionForAcl.status === "pending_bootstrap"
|
|
197
218
|
) {
|
|
198
219
|
denyNonMember = false;
|
|
220
|
+
isValidatedBootstrap = true;
|
|
199
221
|
} else {
|
|
200
222
|
log.info(
|
|
201
223
|
{ sourceChannel, hasValidBootstrapSession: false },
|
|
@@ -206,8 +228,10 @@ export async function enforceIngressAcl(
|
|
|
206
228
|
|
|
207
229
|
// ── Invite token intercept (non-member) ──
|
|
208
230
|
// /start invite deep links grant access without guardian approval.
|
|
209
|
-
//
|
|
210
|
-
//
|
|
231
|
+
// Runs BEFORE the policy-aware bypass so a valid /start iv_<token>
|
|
232
|
+
// always redeems and creates a member record — even when the
|
|
233
|
+
// admission policy is `strangers` (which would otherwise admit the
|
|
234
|
+
// sender as a non-member before the token is consumed).
|
|
211
235
|
if (inviteToken && denyNonMember) {
|
|
212
236
|
const inviteResult = await handleInviteTokenIntercept({
|
|
213
237
|
rawToken: inviteToken,
|
|
@@ -232,6 +256,8 @@ export async function enforceIngressAcl(
|
|
|
232
256
|
// On channels with codeRedemptionEnabled, a bare 6-digit message may be
|
|
233
257
|
// an invite code. Attempt redemption; on failure (no matching code) fall
|
|
234
258
|
// through to normal processing — the number may be a regular message.
|
|
259
|
+
// Runs before the policy-aware bypass for the same reason as the token
|
|
260
|
+
// intercept above.
|
|
235
261
|
if (denyNonMember && /^\d{6}$/.test(trimmedContent)) {
|
|
236
262
|
const codeInterceptResult = await handleInviteCodeIntercept({
|
|
237
263
|
code: trimmedContent,
|
|
@@ -252,6 +278,28 @@ export async function enforceIngressAcl(
|
|
|
252
278
|
};
|
|
253
279
|
}
|
|
254
280
|
|
|
281
|
+
// ── Policy-aware non-member bypass ──
|
|
282
|
+
// Skip the ACL deny gate so the admission floor stage emits the final
|
|
283
|
+
// verdict instead of the ACL prematurely firing guardian notifications,
|
|
284
|
+
// a canned reply, and (on Slack) a verification challenge.
|
|
285
|
+
// - `strangers` (floor 1): any sender (rank 1) clears the floor, so the
|
|
286
|
+
// stage admits.
|
|
287
|
+
// - `guardian_only` (floor 4): no non-guardian can clear the floor even
|
|
288
|
+
// after verifying (a verified contact is only rank 3), so the upgrade
|
|
289
|
+
// challenge is misleading — the stage denies with `shouldChallenge:
|
|
290
|
+
// false`. `trusted_contacts` is intentionally NOT bypassed here: a
|
|
291
|
+
// stranger there still can't reach the floor, but suppressing its
|
|
292
|
+
// self-verify challenge is a default-onboarding behavior change left
|
|
293
|
+
// for a separate §8.2 decision.
|
|
294
|
+
// Runs AFTER invite intercepts so valid tokens redeem first.
|
|
295
|
+
if (
|
|
296
|
+
denyNonMember &&
|
|
297
|
+
(effectiveAdmissionPolicy === "strangers" ||
|
|
298
|
+
effectiveAdmissionPolicy === "guardian_only")
|
|
299
|
+
) {
|
|
300
|
+
denyNonMember = false;
|
|
301
|
+
}
|
|
302
|
+
|
|
255
303
|
if (denyNonMember) {
|
|
256
304
|
log.info(
|
|
257
305
|
{ sourceChannel, externalUserId: canonicalSenderId },
|
|
@@ -465,6 +513,7 @@ export async function enforceIngressAcl(
|
|
|
465
513
|
bootstrapSessionForAcl.status === "pending_bootstrap"
|
|
466
514
|
) {
|
|
467
515
|
denyInactiveMember = false;
|
|
516
|
+
isValidatedBootstrap = true;
|
|
468
517
|
} else {
|
|
469
518
|
log.info(
|
|
470
519
|
{
|
|
@@ -481,7 +530,9 @@ export async function enforceIngressAcl(
|
|
|
481
530
|
// Invite tokens can reactivate revoked/pending members without
|
|
482
531
|
// requiring guardian approval, but blocked members are excluded so
|
|
483
532
|
// they are short-circuited at the ACL layer rather than entering the
|
|
484
|
-
// redemption path.
|
|
533
|
+
// redemption path. Runs BEFORE the policy-aware bypass so a valid
|
|
534
|
+
// invite always redeems and reactivates the member record, rather
|
|
535
|
+
// than the bypass admitting the sender in their inactive state.
|
|
485
536
|
if (!isBlockedMember && inviteToken && denyInactiveMember) {
|
|
486
537
|
const inviteResult = await handleInviteTokenIntercept({
|
|
487
538
|
rawToken: inviteToken,
|
|
@@ -506,7 +557,8 @@ export async function enforceIngressAcl(
|
|
|
506
557
|
// Codes can reactivate revoked/pending members; non-matching codes
|
|
507
558
|
// fall through. Blocked members are excluded here for consistency —
|
|
508
559
|
// the redemption service would reject them anyway, but early exit
|
|
509
|
-
// avoids unnecessary work.
|
|
560
|
+
// avoids unnecessary work. Runs before the policy-aware bypass for
|
|
561
|
+
// the same reason as the token intercept above.
|
|
510
562
|
if (
|
|
511
563
|
!isBlockedMember &&
|
|
512
564
|
denyInactiveMember &&
|
|
@@ -531,6 +583,36 @@ export async function enforceIngressAcl(
|
|
|
531
583
|
};
|
|
532
584
|
}
|
|
533
585
|
|
|
586
|
+
// ── Policy-aware inactive-member bypass ──
|
|
587
|
+
// `strangers` (floor 1): admit non-blocked, non-revoked senders
|
|
588
|
+
// (pending/unverified bypass the inactive-member deny gate).
|
|
589
|
+
// Revoked is an EXPLICIT governance action and stays denied even
|
|
590
|
+
// under the most permissive policy — it is not the same as an
|
|
591
|
+
// unknown stranger who has never interacted with the assistant.
|
|
592
|
+
// `any_contact` (floor 2): admit `pending` and `unverified` members
|
|
593
|
+
// (both classify as `unverified_contact` — rank 2 ≥ floor 2); deny
|
|
594
|
+
// `revoked` members (unknown rank 1 < floor 2).
|
|
595
|
+
// `guardian_only` (floor 4): route `pending`/`unverified` members to
|
|
596
|
+
// the floor stage for a plain denial. Verifying only lifts them to
|
|
597
|
+
// `trusted_contact` (rank 3 < floor 4), so the ACL's re-verify
|
|
598
|
+
// challenge would be misleading. `trusted_contacts` is NOT included:
|
|
599
|
+
// there, verifying reaches `trusted_contact` (rank 3 ≥ floor 3), so
|
|
600
|
+
// the challenge legitimately upgrades the sender into access.
|
|
601
|
+
// In every case skip the deny gate so the admission stage decides.
|
|
602
|
+
// Runs AFTER invite intercepts so valid tokens redeem first.
|
|
603
|
+
if (!isBlockedMember && denyInactiveMember) {
|
|
604
|
+
if (
|
|
605
|
+
(effectiveAdmissionPolicy === "strangers" &&
|
|
606
|
+
resolvedMember.channel.status !== "revoked") ||
|
|
607
|
+
((effectiveAdmissionPolicy === "any_contact" ||
|
|
608
|
+
effectiveAdmissionPolicy === "guardian_only") &&
|
|
609
|
+
(resolvedMember.channel.status === "pending" ||
|
|
610
|
+
resolvedMember.channel.status === "unverified"))
|
|
611
|
+
) {
|
|
612
|
+
denyInactiveMember = false;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
|
|
534
616
|
if (denyInactiveMember) {
|
|
535
617
|
log.info(
|
|
536
618
|
{
|
|
@@ -734,7 +816,7 @@ export async function enforceIngressAcl(
|
|
|
734
816
|
}
|
|
735
817
|
}
|
|
736
818
|
|
|
737
|
-
return { resolvedMember };
|
|
819
|
+
return { resolvedMember, ...(isValidatedBootstrap && { isValidatedBootstrap }) };
|
|
738
820
|
}
|
|
739
821
|
|
|
740
822
|
// ---------------------------------------------------------------------------
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the `enforceAdmissionPolicy` pure function.
|
|
3
|
+
*
|
|
4
|
+
* Drives the floor-vs-rank logic in isolation — no I/O, no mocks needed.
|
|
5
|
+
* Integration-level coverage lives in the gateway's
|
|
6
|
+
* `handle-inbound-admission.test.ts` and in the full inbound-message
|
|
7
|
+
* handler integration tests.
|
|
8
|
+
*/
|
|
9
|
+
import { describe, expect,test } from "bun:test";
|
|
10
|
+
|
|
11
|
+
import type { AdmissionPolicyInput } from "./admission-policy.js";
|
|
12
|
+
import { enforceAdmissionPolicy } from "./admission-policy.js";
|
|
13
|
+
|
|
14
|
+
function makeInput(overrides: Partial<AdmissionPolicyInput>): AdmissionPolicyInput {
|
|
15
|
+
return {
|
|
16
|
+
sourceChannel: "telegram",
|
|
17
|
+
trustClass: "unknown",
|
|
18
|
+
memberStatus: undefined,
|
|
19
|
+
policy: "trusted_contacts",
|
|
20
|
+
...overrides,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// §8.1 exempt channels — always admit
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
describe("enforceAdmissionPolicy — exempt channels", () => {
|
|
29
|
+
test("a2a short-circuits to admitted regardless of policy", () => {
|
|
30
|
+
const result = enforceAdmissionPolicy(
|
|
31
|
+
makeInput({ sourceChannel: "a2a", trustClass: "unknown", policy: "no_one" }),
|
|
32
|
+
);
|
|
33
|
+
expect(result.admitted).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("phone is enforced (not exempt): the floor applies", () => {
|
|
37
|
+
// Voice ingress is wired, so `phone` is no longer exempt — an `unknown`
|
|
38
|
+
// caller is denied under `no_one`.
|
|
39
|
+
const result = enforceAdmissionPolicy(
|
|
40
|
+
makeInput({ sourceChannel: "phone", trustClass: "unknown", policy: "no_one" }),
|
|
41
|
+
);
|
|
42
|
+
expect(result.admitted).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// ---------------------------------------------------------------------------
|
|
47
|
+
// §8 revoked members — denied regardless of policy
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
|
|
50
|
+
describe("enforceAdmissionPolicy — revoked member denial", () => {
|
|
51
|
+
test("revoked member is denied even under `strangers` (most permissive policy)", () => {
|
|
52
|
+
// Before the §8 fix, a revoked member with trustClass `unknown` (rank 1)
|
|
53
|
+
// would pass the `strangers` floor (floor 1, rank ≥ floor). The defense-
|
|
54
|
+
// in-depth short-circuit in this function now denies them regardless.
|
|
55
|
+
const result = enforceAdmissionPolicy(
|
|
56
|
+
makeInput({
|
|
57
|
+
trustClass: "unknown",
|
|
58
|
+
memberStatus: "revoked",
|
|
59
|
+
policy: "strangers",
|
|
60
|
+
}),
|
|
61
|
+
);
|
|
62
|
+
expect(result.admitted).toBe(false);
|
|
63
|
+
if (!result.admitted) {
|
|
64
|
+
expect(result.reason).toBe("member_revoked");
|
|
65
|
+
expect(result.shouldChallenge).toBe(false);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("revoked member is denied under `any_contact`", () => {
|
|
70
|
+
const result = enforceAdmissionPolicy(
|
|
71
|
+
makeInput({
|
|
72
|
+
trustClass: "unknown",
|
|
73
|
+
memberStatus: "revoked",
|
|
74
|
+
policy: "any_contact",
|
|
75
|
+
}),
|
|
76
|
+
);
|
|
77
|
+
expect(result.admitted).toBe(false);
|
|
78
|
+
if (!result.admitted) expect(result.reason).toBe("member_revoked");
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test("revoked member is denied under `trusted_contacts`", () => {
|
|
82
|
+
const result = enforceAdmissionPolicy(
|
|
83
|
+
makeInput({
|
|
84
|
+
trustClass: "unknown",
|
|
85
|
+
memberStatus: "revoked",
|
|
86
|
+
policy: "trusted_contacts",
|
|
87
|
+
}),
|
|
88
|
+
);
|
|
89
|
+
expect(result.admitted).toBe(false);
|
|
90
|
+
if (!result.admitted) expect(result.reason).toBe("member_revoked");
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// §8 blocked members — denied regardless of policy (existing behavior)
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
describe("enforceAdmissionPolicy — blocked member denial", () => {
|
|
99
|
+
test("blocked member is denied under `strangers`", () => {
|
|
100
|
+
const result = enforceAdmissionPolicy(
|
|
101
|
+
makeInput({ memberStatus: "blocked", policy: "strangers" }),
|
|
102
|
+
);
|
|
103
|
+
expect(result.admitted).toBe(false);
|
|
104
|
+
if (!result.admitted) expect(result.reason).toBe("member_blocked");
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
// Rank-vs-floor: non-revoked, non-blocked members
|
|
110
|
+
// ---------------------------------------------------------------------------
|
|
111
|
+
|
|
112
|
+
describe("enforceAdmissionPolicy — rank vs floor", () => {
|
|
113
|
+
test("guardian admitted under any policy including guardian_only", () => {
|
|
114
|
+
const result = enforceAdmissionPolicy(
|
|
115
|
+
makeInput({ trustClass: "guardian", policy: "guardian_only" }),
|
|
116
|
+
);
|
|
117
|
+
expect(result.admitted).toBe(true);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("trusted_contact admitted under trusted_contacts", () => {
|
|
121
|
+
const result = enforceAdmissionPolicy(
|
|
122
|
+
makeInput({ trustClass: "trusted_contact", policy: "trusted_contacts" }),
|
|
123
|
+
);
|
|
124
|
+
expect(result.admitted).toBe(true);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
test("unverified_contact admitted under any_contact floor", () => {
|
|
128
|
+
const result = enforceAdmissionPolicy(
|
|
129
|
+
makeInput({ trustClass: "unverified_contact", policy: "any_contact" }),
|
|
130
|
+
);
|
|
131
|
+
expect(result.admitted).toBe(true);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
test("unknown (non-member) admitted under strangers floor", () => {
|
|
135
|
+
const result = enforceAdmissionPolicy(
|
|
136
|
+
makeInput({ trustClass: "unknown", memberStatus: undefined, policy: "strangers" }),
|
|
137
|
+
);
|
|
138
|
+
expect(result.admitted).toBe(true);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
test("unknown denied under trusted_contacts floor", () => {
|
|
142
|
+
const result = enforceAdmissionPolicy(
|
|
143
|
+
makeInput({ trustClass: "unknown", policy: "trusted_contacts" }),
|
|
144
|
+
);
|
|
145
|
+
expect(result.admitted).toBe(false);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
test("pending member (unverified_contact) admitted under strangers floor", () => {
|
|
149
|
+
const result = enforceAdmissionPolicy(
|
|
150
|
+
makeInput({ trustClass: "unverified_contact", memberStatus: "pending", policy: "strangers" }),
|
|
151
|
+
);
|
|
152
|
+
expect(result.admitted).toBe(true);
|
|
153
|
+
});
|
|
154
|
+
});
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Admission policy enforcement stage.
|
|
3
|
+
*
|
|
4
|
+
* Sits between `resolveTrustContext()` and the agent-loop dispatch in
|
|
5
|
+
* `inbound-message-handler.ts`. The gateway attaches a per-channel-type
|
|
6
|
+
* floor (`sourceMetadata.admissionPolicy`); this stage compares the floor
|
|
7
|
+
* to the resolved trust class's rank and either admits or denies.
|
|
8
|
+
*
|
|
9
|
+
* Deny semantics — see `wave-b-plan.md` §8.2:
|
|
10
|
+
*
|
|
11
|
+
* - `shouldChallenge: true` when the policy is one that re-verification
|
|
12
|
+
* could lift past (`any_contact`, `strangers`). The caller fires the
|
|
13
|
+
* existing Slack DM / email upgrade UX so the sender knows verification
|
|
14
|
+
* would admit them.
|
|
15
|
+
* - `shouldChallenge: false` for the stricter floors (`guardian_only`,
|
|
16
|
+
* `trusted_contacts`). Denials are silent — sender gets the standard
|
|
17
|
+
* canned reply; guardian still gets the access-request notification.
|
|
18
|
+
*
|
|
19
|
+
* Blocked / revoked members short-circuit to deny regardless of policy.
|
|
20
|
+
* The gateway kill switch (`no_one`) is enforced before forwarding, so
|
|
21
|
+
* this stage never sees a `no_one` policy on the wire; we still handle
|
|
22
|
+
* the value defensively for defense in depth and unit-test reachability.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import {
|
|
26
|
+
ADMISSION_FLOOR,
|
|
27
|
+
type AdmissionPolicy,
|
|
28
|
+
isAdmissionPolicyExemptChannel,
|
|
29
|
+
} from "@vellumai/gateway-client";
|
|
30
|
+
|
|
31
|
+
import type { ChannelId } from "../../../channels/types.js";
|
|
32
|
+
import type { ChannelStatus } from "../../../contacts/types.js";
|
|
33
|
+
import {
|
|
34
|
+
TRUST_CLASS_RANK,
|
|
35
|
+
type TrustClass,
|
|
36
|
+
} from "../../actor-trust-resolver.js";
|
|
37
|
+
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// Types
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
export interface AdmissionPolicyInput {
|
|
43
|
+
sourceChannel: ChannelId;
|
|
44
|
+
trustClass: TrustClass;
|
|
45
|
+
/**
|
|
46
|
+
* Channel record status for the resolved member, when one was found.
|
|
47
|
+
* Blocked/revoked short-circuit to deny regardless of floor.
|
|
48
|
+
*/
|
|
49
|
+
memberStatus: ChannelStatus | undefined;
|
|
50
|
+
/** Per-channel-type floor attached by the gateway. */
|
|
51
|
+
policy: AdmissionPolicy;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export type AdmissionDenyReason =
|
|
55
|
+
| "member_blocked"
|
|
56
|
+
| "member_revoked"
|
|
57
|
+
| `admission_policy_${AdmissionPolicy}`;
|
|
58
|
+
|
|
59
|
+
export type AdmissionPolicyResult =
|
|
60
|
+
| { admitted: true }
|
|
61
|
+
| {
|
|
62
|
+
admitted: false;
|
|
63
|
+
reason: AdmissionDenyReason;
|
|
64
|
+
/**
|
|
65
|
+
* Whether the runtime should fire the re-verification upgrade UX
|
|
66
|
+
* (Slack DM / email guardian forwarder). Only meaningful when the
|
|
67
|
+
* resolved trust class could clear the floor after verification.
|
|
68
|
+
*/
|
|
69
|
+
shouldChallenge: boolean;
|
|
70
|
+
/** Effective policy that produced the deny (after override resolution). */
|
|
71
|
+
effectivePolicy: AdmissionPolicy;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
// Public API
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Policies under which completing verification could lift the sender past
|
|
80
|
+
* the floor. Used to decide whether to fire the upgrade UX on deny.
|
|
81
|
+
* `unverified_contact` (rank 2) reaches `any_contact` (floor 2) and
|
|
82
|
+
* `strangers` (floor 1); below those, verification still leaves the
|
|
83
|
+
* sender short of the floor (§8.2).
|
|
84
|
+
*/
|
|
85
|
+
const POLICIES_THAT_COULD_UPGRADE: ReadonlySet<AdmissionPolicy> = new Set([
|
|
86
|
+
"any_contact",
|
|
87
|
+
"strangers",
|
|
88
|
+
]);
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Enforce the admission policy floor against the resolved trust class.
|
|
92
|
+
*
|
|
93
|
+
* Pure function — all I/O happens in the caller. Returns the canned
|
|
94
|
+
* admit/deny verdict; the caller wires denials into the existing
|
|
95
|
+
* canned-reply / guardian-notify pipeline used by `acl-enforcement.ts` for
|
|
96
|
+
* `not_a_member`.
|
|
97
|
+
*/
|
|
98
|
+
export function enforceAdmissionPolicy(
|
|
99
|
+
input: AdmissionPolicyInput,
|
|
100
|
+
): AdmissionPolicyResult {
|
|
101
|
+
// §8.1: short-circuit on internal exempt channels. The gateway should
|
|
102
|
+
// not have attached a policy for these in the first place; this is
|
|
103
|
+
// defense in depth and keeps the runtime fail-open if the gateway is
|
|
104
|
+
// ever called from a path that bypasses the kill-switch insertion.
|
|
105
|
+
if (isAdmissionPolicyExemptChannel(input.sourceChannel)) {
|
|
106
|
+
return { admitted: true };
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Blocked and revoked members never clear admission regardless of floor.
|
|
110
|
+
// `enforceIngressAcl` already short-circuits on both: blocked always, and
|
|
111
|
+
// revoked under any policy including `strangers` (§8 fix: revoked is an
|
|
112
|
+
// explicit governance action, not the same as an unknown stranger). The
|
|
113
|
+
// checks here are defense-in-depth so unit tests can drive the floor stage
|
|
114
|
+
// in isolation and a future refactor that reorders stages doesn't silently
|
|
115
|
+
// admit a blocked/revoked actor.
|
|
116
|
+
if (input.memberStatus === "blocked" || input.memberStatus === "revoked") {
|
|
117
|
+
return {
|
|
118
|
+
admitted: false,
|
|
119
|
+
reason: input.memberStatus === "blocked" ? "member_blocked" : "member_revoked",
|
|
120
|
+
shouldChallenge: false,
|
|
121
|
+
effectivePolicy: input.policy,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const effectivePolicy = input.policy;
|
|
126
|
+
|
|
127
|
+
const rank = TRUST_CLASS_RANK[input.trustClass];
|
|
128
|
+
const floor = ADMISSION_FLOOR[effectivePolicy];
|
|
129
|
+
|
|
130
|
+
if (rank >= floor) {
|
|
131
|
+
return { admitted: true };
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
admitted: false,
|
|
136
|
+
reason: `admission_policy_${effectivePolicy}` as const,
|
|
137
|
+
shouldChallenge: POLICIES_THAT_COULD_UPGRADE.has(effectivePolicy),
|
|
138
|
+
effectivePolicy,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
@@ -845,7 +845,7 @@ describe("Slack thinking status timing", () => {
|
|
|
845
845
|
channel: channelId,
|
|
846
846
|
threadTs,
|
|
847
847
|
status: expect.any(String),
|
|
848
|
-
loadingMessages: ["
|
|
848
|
+
loadingMessages: ["Thinking\u2026"],
|
|
849
849
|
},
|
|
850
850
|
);
|
|
851
851
|
const threadStatus = deliveredChannelReplies[0]!.payload
|
|
@@ -948,13 +948,13 @@ describe("Slack thinking status timing", () => {
|
|
|
948
948
|
channel: channelId,
|
|
949
949
|
threadTs,
|
|
950
950
|
status: expect.any(String),
|
|
951
|
-
loadingMessages: ["
|
|
951
|
+
loadingMessages: ["Thinking\u2026"],
|
|
952
952
|
},
|
|
953
953
|
{
|
|
954
954
|
channel: channelId,
|
|
955
955
|
threadTs,
|
|
956
956
|
status: expect.any(String),
|
|
957
|
-
loadingMessages: ["
|
|
957
|
+
loadingMessages: ["Thinking\u2026"],
|
|
958
958
|
},
|
|
959
959
|
{
|
|
960
960
|
channel: channelId,
|
|
@@ -577,7 +577,7 @@ function createSlackThinkingStatusController(params: {
|
|
|
577
577
|
}
|
|
578
578
|
|
|
579
579
|
const SLACK_THINKING_MAX_DURATION_MS = 120_000;
|
|
580
|
-
const SLACK_GENERIC_LOADING_MESSAGES = ["
|
|
580
|
+
const SLACK_GENERIC_LOADING_MESSAGES = ["Thinking…"] as const;
|
|
581
581
|
const SLACK_THINKING_STATUSES = ["is on it", "is working hard"] as const;
|
|
582
582
|
|
|
583
583
|
function getRandomSlackThinkingStatus(): string {
|
|
@@ -904,10 +904,12 @@ const globalNotifiedApprovalRequestIds = new Map<string, string>();
|
|
|
904
904
|
|
|
905
905
|
/**
|
|
906
906
|
* Start a poller that sends a one-shot "waiting for guardian approval" message
|
|
907
|
-
* to the trusted contact when a confirmation_request enters guardian
|
|
908
|
-
* wait. Deduplicates by requestId so each request only produces one
|
|
907
|
+
* to the trusted/unverified contact when a confirmation_request enters guardian
|
|
908
|
+
* approval wait. Deduplicates by requestId so each request only produces one
|
|
909
|
+
* message.
|
|
909
910
|
*
|
|
910
|
-
* Only activates for
|
|
911
|
+
* Only activates for trusted_contact and unverified_contact actors with a
|
|
912
|
+
* resolvable guardian route.
|
|
911
913
|
*/
|
|
912
914
|
function startTrustedContactApprovalNotifier(params: {
|
|
913
915
|
conversationId: string;
|
|
@@ -928,8 +930,11 @@ function startTrustedContactApprovalNotifier(params: {
|
|
|
928
930
|
assistantId,
|
|
929
931
|
} = params;
|
|
930
932
|
|
|
931
|
-
// Only notify
|
|
932
|
-
|
|
933
|
+
// Only notify identity-known non-guardian contacts (trusted_contact and
|
|
934
|
+
// unverified_contact) who have a resolvable guardian route.
|
|
935
|
+
const isIdentityKnownNonGuardian =
|
|
936
|
+
trustClass === "trusted_contact" || trustClass === "unverified_contact";
|
|
937
|
+
if (!isIdentityKnownNonGuardian || !guardianExternalUserId) {
|
|
933
938
|
return () => {};
|
|
934
939
|
}
|
|
935
940
|
|
|
@@ -11,7 +11,6 @@ import type { ChannelId, InterfaceId } from "../../../channels/types.js";
|
|
|
11
11
|
import { createCanonicalGuardianRequest } from "../../../memory/canonical-guardian-store.js";
|
|
12
12
|
import { storePayload } from "../../../memory/delivery-crud.js";
|
|
13
13
|
import { emitNotificationSignal } from "../../../notifications/emit-signal.js";
|
|
14
|
-
import type { NotificationSourceChannel } from "../../../notifications/signal.js";
|
|
15
14
|
import { getLogger } from "../../../util/logger.js";
|
|
16
15
|
import { getGuardianBinding } from "../../channel-verification-service.js";
|
|
17
16
|
import { GUARDIAN_APPROVAL_TTL_MS } from "../channel-route-shared.js";
|
|
@@ -133,7 +132,7 @@ export function handleEscalationIntercept(
|
|
|
133
132
|
// channels, supplementing the direct guardian notification below.
|
|
134
133
|
void emitNotificationSignal({
|
|
135
134
|
sourceEventName: "ingress.escalation",
|
|
136
|
-
sourceChannel
|
|
135
|
+
sourceChannel,
|
|
137
136
|
sourceContextId: conversationId,
|
|
138
137
|
attentionHints: {
|
|
139
138
|
requiresAction: true,
|
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
import type { ChannelId } from "../../../channels/types.js";
|
|
12
12
|
import { findGuardianForChannel } from "../../../contacts/contact-store.js";
|
|
13
13
|
import { emitNotificationSignal } from "../../../notifications/emit-signal.js";
|
|
14
|
-
import type { NotificationSourceChannel } from "../../../notifications/signal.js";
|
|
15
14
|
import { getLogger } from "../../../util/logger.js";
|
|
16
15
|
import {
|
|
17
16
|
createOutboundSession,
|
|
@@ -164,7 +163,7 @@ export async function handleGuardianActivationIntercept(
|
|
|
164
163
|
// ── Emit notification signal to deliver code to macOS app ──
|
|
165
164
|
void emitNotificationSignal({
|
|
166
165
|
sourceEventName: "guardian.channel_activation",
|
|
167
|
-
sourceChannel
|
|
166
|
+
sourceChannel,
|
|
168
167
|
sourceContextId: `guardian-activation-${sourceChannel}-${rawSenderId}`,
|
|
169
168
|
attentionHints: {
|
|
170
169
|
requiresAction: true,
|
|
@@ -85,7 +85,7 @@ describe("handleGuardianReplyIntercept", () => {
|
|
|
85
85
|
deliverChannelReplyCalls = [];
|
|
86
86
|
});
|
|
87
87
|
|
|
88
|
-
test("passes
|
|
88
|
+
test("passes a blocked scope when Slack guardian sends message in non-delivery chat", async () => {
|
|
89
89
|
// No delivery-scoped requests for this chat
|
|
90
90
|
mockDeliveryScopedRequests = [];
|
|
91
91
|
// Identity-based lookup would find a pending request in a different chat
|
|
@@ -95,8 +95,8 @@ describe("handleGuardianReplyIntercept", () => {
|
|
|
95
95
|
|
|
96
96
|
expect(routeGuardianReplyCalls).toHaveLength(1);
|
|
97
97
|
const ctx = routeGuardianReplyCalls[0] as Record<string, unknown>;
|
|
98
|
-
// Must be
|
|
99
|
-
expect(ctx.
|
|
98
|
+
// Must be a blocked scope, NOT undefined — blocks identity fallback
|
|
99
|
+
expect(ctx.pendingScope).toEqual({ mode: "blocked" });
|
|
100
100
|
});
|
|
101
101
|
|
|
102
102
|
test("preserves identity fallback for non-Slack channels when no deliveries exist", async () => {
|
|
@@ -110,11 +110,11 @@ describe("handleGuardianReplyIntercept", () => {
|
|
|
110
110
|
|
|
111
111
|
expect(routeGuardianReplyCalls).toHaveLength(1);
|
|
112
112
|
const ctx = routeGuardianReplyCalls[0] as Record<string, unknown>;
|
|
113
|
-
// Must be
|
|
114
|
-
expect(ctx.
|
|
113
|
+
// Must be unset — identity-based fallback stays active
|
|
114
|
+
expect(ctx.pendingScope).toBeUndefined();
|
|
115
115
|
});
|
|
116
116
|
|
|
117
|
-
test("
|
|
117
|
+
test("passes an identity-unioned scoped set when Slack guardian is in delivery chat", async () => {
|
|
118
118
|
mockDeliveryScopedRequests = [{ id: "delivered-req" }];
|
|
119
119
|
mockIdentityRequests = [
|
|
120
120
|
{ id: "delivered-req" },
|
|
@@ -125,7 +125,7 @@ describe("handleGuardianReplyIntercept", () => {
|
|
|
125
125
|
|
|
126
126
|
expect(routeGuardianReplyCalls).toHaveLength(1);
|
|
127
127
|
const ctx = routeGuardianReplyCalls[0] as Record<string, unknown>;
|
|
128
|
-
const ids = ctx.
|
|
128
|
+
const ids = (ctx.pendingScope as { requestIds: string[] }).requestIds;
|
|
129
129
|
expect(ids).toContain("delivered-req");
|
|
130
130
|
expect(ids).toContain("identity-only-req");
|
|
131
131
|
});
|