@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
|
@@ -13,15 +13,16 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import type { TrustContext } from "../daemon/trust-context.js";
|
|
16
|
+
import type { CanonicalGuardianRequest } from "../memory/canonical-guardian-store.js";
|
|
16
17
|
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
} from "../
|
|
18
|
+
recordApprovalCardDelivery,
|
|
19
|
+
recordGuardianRequestDeliveries,
|
|
20
|
+
} from "../notifications/canonical-delivery-recorder.js";
|
|
20
21
|
import { emitNotificationSignal } from "../notifications/emit-signal.js";
|
|
21
|
-
import type { NotificationSourceChannel } from "../notifications/signal.js";
|
|
22
22
|
import { canonicalizeInboundIdentity } from "../util/canonicalize-identity.js";
|
|
23
23
|
import { getLogger } from "../util/logger.js";
|
|
24
24
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from "./assistant-scope.js";
|
|
25
|
+
import { resolveCapabilities } from "./capabilities.js";
|
|
25
26
|
import { getGuardianBinding } from "./channel-verification-service.js";
|
|
26
27
|
|
|
27
28
|
const log = getLogger("confirmation-request-guardian-bridge");
|
|
@@ -48,7 +49,7 @@ export type BridgeConfirmationRequestResult =
|
|
|
48
49
|
| {
|
|
49
50
|
skipped: true;
|
|
50
51
|
reason:
|
|
51
|
-
| "
|
|
52
|
+
| "not_bridgeable_trust_class"
|
|
52
53
|
| "no_guardian_binding"
|
|
53
54
|
| "missing_guardian_identity"
|
|
54
55
|
| "binding_identity_mismatch";
|
|
@@ -59,11 +60,13 @@ export type BridgeConfirmationRequestResult =
|
|
|
59
60
|
// ---------------------------------------------------------------------------
|
|
60
61
|
|
|
61
62
|
/**
|
|
62
|
-
* Bridge a
|
|
63
|
+
* Bridge a non-guardian contact confirmation_request to a guardian.question
|
|
64
|
+
* notification.
|
|
63
65
|
*
|
|
64
|
-
* Only emits when the session belongs to a
|
|
65
|
-
* resolvable guardian binding. Guardian and unknown actors are
|
|
66
|
-
* self-approve, and unknown actors are already fail-closed
|
|
66
|
+
* Only emits when the session belongs to a trusted_contact or unverified_contact
|
|
67
|
+
* actor with a resolvable guardian binding. Guardian and unknown actors are
|
|
68
|
+
* skipped — guardians self-approve, and unknown actors are already fail-closed
|
|
69
|
+
* by the routing layer.
|
|
67
70
|
*
|
|
68
71
|
* Fire-and-forget safe: notification emission errors are logged but not propagated.
|
|
69
72
|
*/
|
|
@@ -78,10 +81,14 @@ export function bridgeConfirmationRequestToGuardian(
|
|
|
78
81
|
assistantId = DAEMON_INTERNAL_ASSISTANT_ID,
|
|
79
82
|
} = params;
|
|
80
83
|
|
|
81
|
-
// Only bridge for
|
|
82
|
-
// unknown actors are fail-closed by the routing
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
// Only bridge for actors whose sensitive tool approval escalates-and-waits.
|
|
85
|
+
// Guardians self-approve and unknown actors are fail-closed by the routing
|
|
86
|
+
// layer, so neither needs a guardian bridge.
|
|
87
|
+
if (
|
|
88
|
+
resolveCapabilities(trustContext.trustClass).sensitiveToolApproval !==
|
|
89
|
+
"escalate-and-wait"
|
|
90
|
+
) {
|
|
91
|
+
return { skipped: true, reason: "not_bridgeable_trust_class" };
|
|
85
92
|
}
|
|
86
93
|
|
|
87
94
|
if (!trustContext.guardianExternalUserId) {
|
|
@@ -144,10 +151,14 @@ export function bridgeConfirmationRequestToGuardian(
|
|
|
144
151
|
? `Approve tool: ${toolName} — ${canonicalRequest.activityText}`
|
|
145
152
|
: `Approve tool: ${toolName}`;
|
|
146
153
|
|
|
154
|
+
// The vellum delivery row is created up front in onConversationCreated so the
|
|
155
|
+
// in-app client sees it immediately; the post-resolve recorder reuses it.
|
|
156
|
+
let vellumDeliveryId: string | undefined;
|
|
157
|
+
|
|
147
158
|
// Emit guardian.question notification so the guardian is alerted.
|
|
148
159
|
const signalPromise = emitNotificationSignal({
|
|
149
160
|
sourceEventName: "guardian.question",
|
|
150
|
-
sourceChannel
|
|
161
|
+
sourceChannel,
|
|
151
162
|
sourceContextId: conversationId,
|
|
152
163
|
requiresConversation: true,
|
|
153
164
|
attentionHints: {
|
|
@@ -157,7 +168,7 @@ export function bridgeConfirmationRequestToGuardian(
|
|
|
157
168
|
visibleInSourceNow: false,
|
|
158
169
|
},
|
|
159
170
|
contextPayload: {
|
|
160
|
-
requestKind: "tool_approval"
|
|
171
|
+
requestKind: "tool_approval",
|
|
161
172
|
requestId: canonicalRequest.id,
|
|
162
173
|
requestCode:
|
|
163
174
|
canonicalRequest.requestCode ??
|
|
@@ -168,29 +179,27 @@ export function bridgeConfirmationRequestToGuardian(
|
|
|
168
179
|
requesterIdentifier: senderLabel,
|
|
169
180
|
toolName,
|
|
170
181
|
questionText,
|
|
182
|
+
riskLevel: canonicalRequest.riskLevel ?? undefined,
|
|
183
|
+
commandPreview: canonicalRequest.commandPreview ?? undefined,
|
|
171
184
|
},
|
|
172
185
|
dedupeKey: `tc-confirmation-request:${canonicalRequest.id}`,
|
|
173
186
|
onConversationCreated: (info) => {
|
|
174
|
-
|
|
187
|
+
vellumDeliveryId = recordApprovalCardDelivery({
|
|
175
188
|
requestId: canonicalRequest.id,
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
});
|
|
189
|
+
channel: "vellum",
|
|
190
|
+
conversationId: info.conversationId,
|
|
191
|
+
})?.id;
|
|
179
192
|
},
|
|
180
193
|
});
|
|
181
194
|
|
|
182
|
-
// Record
|
|
195
|
+
// Record deliveries from the notification pipeline (fire-and-forget).
|
|
183
196
|
void signalPromise
|
|
184
197
|
.then((signalResult) => {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
destinationChatId:
|
|
191
|
-
result.destination.length > 0 ? result.destination : undefined,
|
|
192
|
-
});
|
|
193
|
-
}
|
|
198
|
+
recordGuardianRequestDeliveries({
|
|
199
|
+
requestId: canonicalRequest.id,
|
|
200
|
+
deliveryResults: signalResult.deliveryResults,
|
|
201
|
+
vellumDeliveryId,
|
|
202
|
+
});
|
|
194
203
|
})
|
|
195
204
|
.catch((err) => {
|
|
196
205
|
log.warn(
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
canActOnPrivilegedDocuments,
|
|
5
|
+
isArchiveBySenderAuthorized,
|
|
6
|
+
isUntrustedShellLockdownActive,
|
|
7
|
+
} from "./effective-capabilities.js";
|
|
8
|
+
|
|
9
|
+
describe("canActOnPrivilegedDocuments", () => {
|
|
10
|
+
test("guardian is privileged regardless of channel", () => {
|
|
11
|
+
expect(canActOnPrivilegedDocuments({ trustClass: "guardian" })).toBe(true);
|
|
12
|
+
expect(
|
|
13
|
+
canActOnPrivilegedDocuments({
|
|
14
|
+
trustClass: "guardian",
|
|
15
|
+
executionChannel: "telegram",
|
|
16
|
+
}),
|
|
17
|
+
).toBe(true);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("non-guardian is privileged only on a privileged channel", () => {
|
|
21
|
+
expect(
|
|
22
|
+
canActOnPrivilegedDocuments({
|
|
23
|
+
trustClass: "trusted_contact",
|
|
24
|
+
executionChannel: "telegram",
|
|
25
|
+
}),
|
|
26
|
+
).toBe(false);
|
|
27
|
+
expect(
|
|
28
|
+
canActOnPrivilegedDocuments({
|
|
29
|
+
trustClass: "trusted_contact",
|
|
30
|
+
executionChannel: "vellum",
|
|
31
|
+
}),
|
|
32
|
+
).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("the vellum channel grants access even to unknown actors", () => {
|
|
36
|
+
expect(
|
|
37
|
+
canActOnPrivilegedDocuments({
|
|
38
|
+
trustClass: "unknown",
|
|
39
|
+
executionChannel: "vellum",
|
|
40
|
+
}),
|
|
41
|
+
).toBe(true);
|
|
42
|
+
expect(canActOnPrivilegedDocuments({ trustClass: "unknown" })).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe("isUntrustedShellLockdownActive", () => {
|
|
47
|
+
test("inactive when the lockdown flag is off", () => {
|
|
48
|
+
expect(
|
|
49
|
+
isUntrustedShellLockdownActive({
|
|
50
|
+
trustClass: "unknown",
|
|
51
|
+
lockdownEnabled: false,
|
|
52
|
+
}),
|
|
53
|
+
).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("inactive for guardians even when the flag is on", () => {
|
|
57
|
+
expect(
|
|
58
|
+
isUntrustedShellLockdownActive({
|
|
59
|
+
trustClass: "guardian",
|
|
60
|
+
lockdownEnabled: true,
|
|
61
|
+
}),
|
|
62
|
+
).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("active for non-unsandboxed actors when the flag is on", () => {
|
|
66
|
+
expect(
|
|
67
|
+
isUntrustedShellLockdownActive({
|
|
68
|
+
trustClass: "trusted_contact",
|
|
69
|
+
lockdownEnabled: true,
|
|
70
|
+
}),
|
|
71
|
+
).toBe(true);
|
|
72
|
+
expect(
|
|
73
|
+
isUntrustedShellLockdownActive({
|
|
74
|
+
trustClass: "unknown",
|
|
75
|
+
lockdownEnabled: true,
|
|
76
|
+
}),
|
|
77
|
+
).toBe(true);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe("isArchiveBySenderAuthorized", () => {
|
|
82
|
+
test("any non-self authorization path suffices", () => {
|
|
83
|
+
expect(
|
|
84
|
+
isArchiveBySenderAuthorized({
|
|
85
|
+
trustClass: "unknown",
|
|
86
|
+
triggeredBySurfaceAction: true,
|
|
87
|
+
}),
|
|
88
|
+
).toBe(true);
|
|
89
|
+
expect(
|
|
90
|
+
isArchiveBySenderAuthorized({
|
|
91
|
+
trustClass: "unknown",
|
|
92
|
+
batchAuthorizedByTask: true,
|
|
93
|
+
}),
|
|
94
|
+
).toBe(true);
|
|
95
|
+
expect(
|
|
96
|
+
isArchiveBySenderAuthorized({
|
|
97
|
+
trustClass: "unknown",
|
|
98
|
+
approvedViaPrompt: true,
|
|
99
|
+
}),
|
|
100
|
+
).toBe(true);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("user_approved self-authorizes only for a permitted trust class", () => {
|
|
104
|
+
expect(
|
|
105
|
+
isArchiveBySenderAuthorized({
|
|
106
|
+
trustClass: "guardian",
|
|
107
|
+
userApproved: true,
|
|
108
|
+
}),
|
|
109
|
+
).toBe(true);
|
|
110
|
+
expect(
|
|
111
|
+
isArchiveBySenderAuthorized({
|
|
112
|
+
trustClass: "trusted_contact",
|
|
113
|
+
userApproved: true,
|
|
114
|
+
}),
|
|
115
|
+
).toBe(false);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("unauthorized when no path applies", () => {
|
|
119
|
+
expect(isArchiveBySenderAuthorized({ trustClass: "guardian" })).toBe(false);
|
|
120
|
+
expect(
|
|
121
|
+
isArchiveBySenderAuthorized({
|
|
122
|
+
trustClass: "trusted_contact",
|
|
123
|
+
triggeredBySurfaceAction: false,
|
|
124
|
+
userApproved: false,
|
|
125
|
+
}),
|
|
126
|
+
).toBe(false);
|
|
127
|
+
});
|
|
128
|
+
});
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Effective capabilities — trust-class capabilities COMPOSED with runtime context.
|
|
3
|
+
*
|
|
4
|
+
* `resolveCapabilities` (`capabilities.ts`) answers "what may this trust class
|
|
5
|
+
* do" from the actor's class alone, and stays deliberately pure (no context
|
|
6
|
+
* dependencies) so it remains the single fail-closed trust boundary. Some real
|
|
7
|
+
* decisions additionally depend on runtime context — the channel a request
|
|
8
|
+
* arrived on, surface actions, task authorization, a feature flag. Those
|
|
9
|
+
* compositions belong here, in named/testable helpers, rather than re-derived
|
|
10
|
+
* inline at each call site (which scatters a single policy across the codebase).
|
|
11
|
+
*
|
|
12
|
+
* Scope: this module composes capabilities with *actor/request* context. It does
|
|
13
|
+
* not read global config or feature flags itself — callers resolve those and
|
|
14
|
+
* pass the result in (e.g. `lockdownEnabled`), keeping this layer focused on the
|
|
15
|
+
* capability composition. `resolveRoutingState` in `trust-context-resolver.ts`
|
|
16
|
+
* is the same shape (capability + guardian-route context → `promptWaitingAllowed`)
|
|
17
|
+
* and predates this module; it stays where it is.
|
|
18
|
+
*/
|
|
19
|
+
import type { TrustClass } from "./actor-trust-resolver.js";
|
|
20
|
+
import { resolveCapabilities } from "./capabilities.js";
|
|
21
|
+
|
|
22
|
+
type RawTrustClass = TrustClass | (string & {}) | undefined;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Channels that are themselves privileged document surfaces. Actors on these get
|
|
26
|
+
* privileged document access regardless of trust class — the `vellum` first-party
|
|
27
|
+
* console is the operator's own surface, not an external contact channel.
|
|
28
|
+
*/
|
|
29
|
+
const PRIVILEGED_DOCUMENT_CHANNELS = new Set<string>(["vellum"]);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Whether an actor may perform privileged (non-conversation-scoped) document
|
|
33
|
+
* operations. True when the trust class grants it OR the request arrived on a
|
|
34
|
+
* privileged channel.
|
|
35
|
+
*/
|
|
36
|
+
export function canActOnPrivilegedDocuments(actor: {
|
|
37
|
+
trustClass: RawTrustClass;
|
|
38
|
+
executionChannel?: string;
|
|
39
|
+
}): boolean {
|
|
40
|
+
return (
|
|
41
|
+
resolveCapabilities(actor.trustClass).canAccessPrivilegedDocuments ||
|
|
42
|
+
(actor.executionChannel != null &&
|
|
43
|
+
PRIVILEGED_DOCUMENT_CHANNELS.has(actor.executionChannel))
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Whether the CES shell lockdown applies to this actor. Active when the lockdown
|
|
49
|
+
* flag is enabled AND the actor's trust class cannot run an unsandboxed shell.
|
|
50
|
+
* Callers resolve the flag (`isCesShellLockdownEnabled(config)`) and pass it in.
|
|
51
|
+
*/
|
|
52
|
+
export function isUntrustedShellLockdownActive(actor: {
|
|
53
|
+
trustClass: RawTrustClass;
|
|
54
|
+
lockdownEnabled: boolean;
|
|
55
|
+
}): boolean {
|
|
56
|
+
return (
|
|
57
|
+
actor.lockdownEnabled &&
|
|
58
|
+
!resolveCapabilities(actor.trustClass).canRunUnsandboxedShell
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Whether an archive-by-sender invocation is authorized. Any one of a surface
|
|
64
|
+
* action, a task-batch authorization, or an explicit prompt approval suffices.
|
|
65
|
+
* Absent those, the actor's own `user_approved` flag only counts when its trust
|
|
66
|
+
* class may self-authorize archive-by-sender.
|
|
67
|
+
*/
|
|
68
|
+
export function isArchiveBySenderAuthorized(args: {
|
|
69
|
+
trustClass: RawTrustClass;
|
|
70
|
+
triggeredBySurfaceAction?: boolean;
|
|
71
|
+
batchAuthorizedByTask?: boolean;
|
|
72
|
+
approvedViaPrompt?: boolean;
|
|
73
|
+
userApproved?: boolean;
|
|
74
|
+
}): boolean {
|
|
75
|
+
const selfAuthorized =
|
|
76
|
+
args.userApproved === true &&
|
|
77
|
+
resolveCapabilities(args.trustClass).canSelfAuthorizeArchiveBySender;
|
|
78
|
+
return (
|
|
79
|
+
args.triggeredBySurfaceAction === true ||
|
|
80
|
+
args.batchAuthorizedByTask === true ||
|
|
81
|
+
args.approvedViaPrompt === true ||
|
|
82
|
+
selfAuthorized
|
|
83
|
+
);
|
|
84
|
+
}
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
type CanonicalGuardianRequest,
|
|
31
31
|
getCanonicalGuardianRequest,
|
|
32
32
|
getCanonicalGuardianRequestByCode,
|
|
33
|
+
getPendingCanonicalRequestByDestinationMessage,
|
|
33
34
|
isRequestExpired,
|
|
34
35
|
listCanonicalGuardianRequests,
|
|
35
36
|
} from "../memory/canonical-guardian-store.js";
|
|
@@ -47,6 +48,7 @@ import type {
|
|
|
47
48
|
ApprovalConversationContext,
|
|
48
49
|
ApprovalConversationGenerator,
|
|
49
50
|
} from "./http-types.js";
|
|
51
|
+
import { parseReactionCallbackData } from "./routes/channel-route-shared.js";
|
|
50
52
|
|
|
51
53
|
const log = getLogger("guardian-reply-router");
|
|
52
54
|
|
|
@@ -54,6 +56,26 @@ const log = getLogger("guardian-reply-router");
|
|
|
54
56
|
// Types
|
|
55
57
|
// ---------------------------------------------------------------------------
|
|
56
58
|
|
|
59
|
+
/**
|
|
60
|
+
* How to scope a guardian's pending requests when resolving an inbound reply.
|
|
61
|
+
* The three states are mutually exclusive and named so the security-critical
|
|
62
|
+
* `blocked` cannot be confused with the absence of a hint:
|
|
63
|
+
*
|
|
64
|
+
* - `scoped`: resolve only these request ids, and constrain request-code
|
|
65
|
+
* routing to this set (delivery-/conversation-scoped hints).
|
|
66
|
+
* - `blocked`: fail closed — no pending requests and no identity fallback.
|
|
67
|
+
* The Slack cross-chat guard: a guardian's unrelated message in a chat
|
|
68
|
+
* where no card was delivered must not resolve a request delivered
|
|
69
|
+
* elsewhere. Explicit callbacks and request codes still work (they carry
|
|
70
|
+
* their own request id).
|
|
71
|
+
* - `identity-fallback`: discover pending requests by guardian identity,
|
|
72
|
+
* conversation, or principal. The default when no scope is supplied.
|
|
73
|
+
*/
|
|
74
|
+
export type GuardianPendingScope =
|
|
75
|
+
| { mode: "scoped"; requestIds: string[] }
|
|
76
|
+
| { mode: "blocked" }
|
|
77
|
+
| { mode: "identity-fallback" };
|
|
78
|
+
|
|
57
79
|
/** Context for an inbound message that may be a guardian reply. */
|
|
58
80
|
export interface GuardianReplyContext {
|
|
59
81
|
/** The raw message text (trimmed). */
|
|
@@ -66,8 +88,18 @@ export interface GuardianReplyContext {
|
|
|
66
88
|
conversationId: string;
|
|
67
89
|
/** Callback data from button presses (e.g. `apr:<requestId>:<action>`). */
|
|
68
90
|
callbackData?: string;
|
|
69
|
-
/**
|
|
70
|
-
|
|
91
|
+
/**
|
|
92
|
+
* For emoji-reaction decisions (`callbackData` of `reaction:<emoji>`): the
|
|
93
|
+
* channel-native id (e.g. Slack `ts`) of the message the reaction was
|
|
94
|
+
* attached to. Used to recover the target request from its delivery record.
|
|
95
|
+
*/
|
|
96
|
+
reactedMessageTs?: string;
|
|
97
|
+
/**
|
|
98
|
+
* How to scope this guardian's pending requests (see {@link
|
|
99
|
+
* GuardianPendingScope}). Omitted is equivalent to
|
|
100
|
+
* `{ mode: "identity-fallback" }`.
|
|
101
|
+
*/
|
|
102
|
+
pendingScope?: GuardianPendingScope;
|
|
71
103
|
/** Conversation generator for NL classification (injected by daemon). */
|
|
72
104
|
approvalConversationGenerator?: ApprovalConversationGenerator;
|
|
73
105
|
/** Optional channel delivery context for resolver-driven side effects. */
|
|
@@ -204,30 +236,33 @@ function parseRequestCode(
|
|
|
204
236
|
/** Find all pending canonical requests for a guardian actor. */
|
|
205
237
|
function findPendingCanonicalRequests(
|
|
206
238
|
actor: ActorContext,
|
|
207
|
-
|
|
239
|
+
scope: GuardianPendingScope,
|
|
208
240
|
conversationId?: string,
|
|
209
241
|
): CanonicalGuardianRequest[] {
|
|
242
|
+
// `blocked` fails closed: no pending requests and no identity fallback — the
|
|
243
|
+
// Slack cross-chat hijack guard.
|
|
244
|
+
if (scope.mode === "blocked") {
|
|
245
|
+
return [];
|
|
246
|
+
}
|
|
247
|
+
|
|
210
248
|
let results: CanonicalGuardianRequest[];
|
|
211
249
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
return [];
|
|
216
|
-
}
|
|
217
|
-
results = pendingRequestIds
|
|
250
|
+
if (scope.mode === "scoped") {
|
|
251
|
+
// Resolve exactly the supplied ids.
|
|
252
|
+
results = scope.requestIds
|
|
218
253
|
.map(getCanonicalGuardianRequest)
|
|
219
254
|
.filter((r): r is CanonicalGuardianRequest => r?.status === "pending");
|
|
220
255
|
} else if (actor.actorExternalUserId) {
|
|
221
|
-
//
|
|
256
|
+
// identity-fallback: query by guardian identity when available
|
|
222
257
|
results = listCanonicalGuardianRequests({
|
|
223
258
|
status: "pending",
|
|
224
259
|
guardianExternalUserId: actor.actorExternalUserId,
|
|
225
260
|
});
|
|
226
261
|
} else if (conversationId) {
|
|
227
|
-
//
|
|
228
|
-
// path can discover pending requests bound to this conversation.
|
|
229
|
-
// Include guardianPrincipalId
|
|
230
|
-
//
|
|
262
|
+
// identity-fallback without an actorExternalUserId: scope by conversationId
|
|
263
|
+
// so the NL path can discover pending requests bound to this conversation.
|
|
264
|
+
// Include guardianPrincipalId when available so the guardian only sees
|
|
265
|
+
// requests they are authorized to act on.
|
|
231
266
|
results = listCanonicalGuardianRequests({
|
|
232
267
|
status: "pending",
|
|
233
268
|
conversationId,
|
|
@@ -236,9 +271,8 @@ function findPendingCanonicalRequests(
|
|
|
236
271
|
: {}),
|
|
237
272
|
});
|
|
238
273
|
} else if (actor.guardianPrincipalId) {
|
|
239
|
-
//
|
|
240
|
-
//
|
|
241
|
-
// discover pending guardian work via their bound principal.
|
|
274
|
+
// identity-fallback by principal: desktop sessions discover pending
|
|
275
|
+
// guardian work via their bound principal.
|
|
242
276
|
results = listCanonicalGuardianRequests({
|
|
243
277
|
status: "pending",
|
|
244
278
|
guardianPrincipalId: actor.guardianPrincipalId,
|
|
@@ -284,22 +318,68 @@ export async function routeGuardianReply(
|
|
|
284
318
|
): Promise<GuardianReplyResult> {
|
|
285
319
|
const {
|
|
286
320
|
messageText,
|
|
321
|
+
channel,
|
|
287
322
|
actor,
|
|
288
323
|
conversationId,
|
|
289
324
|
callbackData,
|
|
325
|
+
reactedMessageTs,
|
|
290
326
|
approvalConversationGenerator,
|
|
291
327
|
channelDeliveryContext,
|
|
292
328
|
emissionContext,
|
|
293
329
|
} = ctx;
|
|
330
|
+
|
|
331
|
+
// ── 0. Reaction decisions (emoji on a delivered approval card) ──
|
|
332
|
+
// A reaction carries an emoji plus the message it is attached to. Map the
|
|
333
|
+
// emoji to an action and recover the target request from that card's
|
|
334
|
+
// delivery record. Addressing by the reacted message disambiguates precisely
|
|
335
|
+
// even when several cards are pending in the same chat, so — unlike the
|
|
336
|
+
// text/NL paths — no clarification prompt is ever needed. `reaction_removed`
|
|
337
|
+
// never expresses intent and is filtered out before reaching the router.
|
|
338
|
+
if (
|
|
339
|
+
callbackData?.startsWith("reaction:") &&
|
|
340
|
+
!callbackData.startsWith("reaction_removed:")
|
|
341
|
+
) {
|
|
342
|
+
const reaction = parseReactionCallbackData(callbackData);
|
|
343
|
+
const guardianChatId = channelDeliveryContext?.guardianChatId;
|
|
344
|
+
if (!reaction || !reactedMessageTs || !guardianChatId) {
|
|
345
|
+
// Unknown emoji, or missing addressing context — not an actionable
|
|
346
|
+
// approval reaction. Leave it for the caller to persist as a transcript
|
|
347
|
+
// signal (it must not trigger an agent turn).
|
|
348
|
+
return notConsumed();
|
|
349
|
+
}
|
|
350
|
+
const request = getPendingCanonicalRequestByDestinationMessage(
|
|
351
|
+
channel,
|
|
352
|
+
guardianChatId,
|
|
353
|
+
reactedMessageTs,
|
|
354
|
+
);
|
|
355
|
+
if (!request) {
|
|
356
|
+
// The reacted message is not a known pending approval card (a stray
|
|
357
|
+
// reaction, or one whose request was already resolved). Never approve
|
|
358
|
+
// off an unrecognized message.
|
|
359
|
+
return notConsumed();
|
|
360
|
+
}
|
|
361
|
+
return applyDecision(
|
|
362
|
+
request.id,
|
|
363
|
+
reaction.action,
|
|
364
|
+
actor,
|
|
365
|
+
undefined,
|
|
366
|
+
channelDeliveryContext,
|
|
367
|
+
emissionContext,
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const pendingScope: GuardianPendingScope = ctx.pendingScope ?? {
|
|
372
|
+
mode: "identity-fallback",
|
|
373
|
+
};
|
|
294
374
|
const pendingRequests = findPendingCanonicalRequests(
|
|
295
375
|
actor,
|
|
296
|
-
|
|
376
|
+
pendingScope,
|
|
297
377
|
conversationId,
|
|
298
378
|
);
|
|
379
|
+
// Request codes carry their own id, so constrain them only under an explicit
|
|
380
|
+
// scope; under blocked/identity-fallback they still resolve cross-chat.
|
|
299
381
|
const scopedPendingRequestIds =
|
|
300
|
-
|
|
301
|
-
? new Set(ctx.pendingRequestIds)
|
|
302
|
-
: null;
|
|
382
|
+
pendingScope.mode === "scoped" ? new Set(pendingScope.requestIds) : null;
|
|
303
383
|
|
|
304
384
|
// ── 1. Deterministic callback parsing (button presses) ──
|
|
305
385
|
// No conversationId scoping here — the guardian's reply comes from a
|
|
@@ -800,6 +880,7 @@ function resolveRequestInstructionMode(
|
|
|
800
880
|
type CanonicalFailureReason =
|
|
801
881
|
| "already_resolved"
|
|
802
882
|
| "identity_mismatch"
|
|
883
|
+
| "request_misconfigured"
|
|
803
884
|
| "invalid_action"
|
|
804
885
|
| "expired";
|
|
805
886
|
|
|
@@ -819,6 +900,10 @@ function failureReplyText(
|
|
|
819
900
|
return "This request has expired.";
|
|
820
901
|
case "identity_mismatch":
|
|
821
902
|
return "You don't have permission to decide on this request.";
|
|
903
|
+
case "request_misconfigured":
|
|
904
|
+
// The actor is authorized; the request record itself is incomplete
|
|
905
|
+
// (e.g. missing its bound principal). Do not imply a permission problem.
|
|
906
|
+
return "Something went wrong with this request on our end, so I couldn't apply your decision.";
|
|
822
907
|
case "invalid_action":
|
|
823
908
|
return buildGuardianInvalidActionReply(
|
|
824
909
|
resolveRequestInstructionMode(request),
|
|
@@ -129,7 +129,7 @@ export function redeemInvite(params: {
|
|
|
129
129
|
: undefined;
|
|
130
130
|
const contactResult = findContactChannel({
|
|
131
131
|
channelType: sourceChannel,
|
|
132
|
-
|
|
132
|
+
address: canonicalUserId,
|
|
133
133
|
externalChatId: externalChatId,
|
|
134
134
|
});
|
|
135
135
|
const existingChannel = contactResult?.channel ?? null;
|
|
@@ -164,20 +164,12 @@ export function redeemInvite(params: {
|
|
|
164
164
|
// Sentinel error used to trigger a transaction rollback when the invite
|
|
165
165
|
// was concurrently revoked/expired between pre-validation and write time.
|
|
166
166
|
const STALE_INVITE = Symbol("stale_invite");
|
|
167
|
-
const canonicalMemberId = existingChannel.
|
|
168
|
-
? canonicalizeInboundIdentity(
|
|
169
|
-
sourceChannel as ChannelId,
|
|
170
|
-
existingChannel.externalUserId,
|
|
171
|
-
)
|
|
172
|
-
: null;
|
|
167
|
+
const canonicalMemberId = existingChannel.address;
|
|
173
168
|
const canonicalCallerId = externalUserId
|
|
174
169
|
? canonicalizeInboundIdentity(sourceChannel as ChannelId, externalUserId)
|
|
175
170
|
: null;
|
|
176
|
-
const memberMatchesSender =
|
|
177
|
-
canonicalMemberId
|
|
178
|
-
canonicalCallerId &&
|
|
179
|
-
canonicalMemberId === canonicalCallerId
|
|
180
|
-
);
|
|
171
|
+
const memberMatchesSender =
|
|
172
|
+
!!canonicalCallerId && canonicalMemberId === canonicalCallerId;
|
|
181
173
|
const preservedDisplayName =
|
|
182
174
|
memberMatchesSender && existingContact?.displayName?.trim().length
|
|
183
175
|
? existingContact.displayName
|
|
@@ -359,7 +351,7 @@ export function redeemVoiceInviteCode(params: {
|
|
|
359
351
|
callerExternalUserId;
|
|
360
352
|
const voiceContactResult = findContactChannel({
|
|
361
353
|
channelType: "phone",
|
|
362
|
-
|
|
354
|
+
address: canonicalCallerId,
|
|
363
355
|
});
|
|
364
356
|
const existingVoiceChannel = voiceContactResult?.channel ?? null;
|
|
365
357
|
const voiceContact = voiceContactResult?.contact ?? null;
|
|
@@ -511,7 +503,7 @@ export function redeemInviteByCode(params: {
|
|
|
511
503
|
: undefined;
|
|
512
504
|
const contactResult = findContactChannel({
|
|
513
505
|
channelType: sourceChannel,
|
|
514
|
-
|
|
506
|
+
address: canonicalUserId,
|
|
515
507
|
externalChatId: externalChatId,
|
|
516
508
|
});
|
|
517
509
|
const existingChannel = contactResult?.channel ?? null;
|
|
@@ -542,20 +534,12 @@ export function redeemInviteByCode(params: {
|
|
|
542
534
|
// an invite use atomically.
|
|
543
535
|
if (existingChannel && !targetMismatch) {
|
|
544
536
|
const STALE_INVITE_REACTIVATE = Symbol("stale_invite_reactivate");
|
|
545
|
-
const canonicalMemberId = existingChannel.
|
|
546
|
-
? canonicalizeInboundIdentity(
|
|
547
|
-
sourceChannel as ChannelId,
|
|
548
|
-
existingChannel.externalUserId,
|
|
549
|
-
)
|
|
550
|
-
: null;
|
|
537
|
+
const canonicalMemberId = existingChannel.address;
|
|
551
538
|
const canonicalCallerId = externalUserId
|
|
552
539
|
? canonicalizeInboundIdentity(sourceChannel as ChannelId, externalUserId)
|
|
553
540
|
: null;
|
|
554
|
-
const memberMatchesSender =
|
|
555
|
-
canonicalMemberId
|
|
556
|
-
canonicalCallerId &&
|
|
557
|
-
canonicalMemberId === canonicalCallerId
|
|
558
|
-
);
|
|
541
|
+
const memberMatchesSender =
|
|
542
|
+
!!canonicalCallerId && canonicalMemberId === canonicalCallerId;
|
|
559
543
|
const preservedDisplayName =
|
|
560
544
|
memberMatchesSender && existingContact?.displayName?.trim().length
|
|
561
545
|
? existingContact.displayName
|