@vellumai/assistant 0.10.1 → 0.10.2-dev.202606241651.2d2b40d
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/docs/workspace-tools.md +42 -33
- package/eslint-rules/cli-no-daemon-internals.js +6 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-delivery-contract.test.ts +91 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +31 -0
- package/node_modules/@vellumai/gateway-client/src/guardian-delivery-contract.ts +48 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +14 -0
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +17 -0
- package/openapi.yaml +74 -1
- package/package.json +1 -1
- package/scripts/test.sh +36 -15
- package/src/__tests__/actor-token-service.test.ts +36 -14
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +2 -0
- package/src/__tests__/annotate-activity-metadata.test.ts +2 -0
- package/src/__tests__/annotate-risk-options.test.ts +2 -0
- package/src/__tests__/approval-cascade.test.ts +2 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -0
- package/src/__tests__/btw-routes.test.ts +2 -0
- package/src/__tests__/build-persisted-content.test.ts +2 -0
- package/src/__tests__/call-controller.test.ts +19 -0
- package/src/__tests__/channel-guardian.test.ts +94 -58
- package/src/__tests__/channel-reply-delivery.test.ts +2 -0
- package/src/__tests__/compaction-events.test.ts +2 -0
- package/src/__tests__/compaction.benchmark.test.ts +2 -0
- package/src/__tests__/compactor-call-site-logging.test.ts +2 -0
- package/src/__tests__/compactor-low-watermark-cut.test.ts +2 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +2 -0
- package/src/__tests__/compactor-summary-call-truncation.test.ts +2 -0
- package/src/__tests__/compactor-web-search-strip.test.ts +2 -0
- package/src/__tests__/computer-use-tools.test.ts +13 -0
- package/src/__tests__/config-loader-backfill.test.ts +5 -1
- package/src/__tests__/config-schema.test.ts +1 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +31 -29
- package/src/__tests__/contacts-relay-reads.test.ts +13 -15
- package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop.test.ts +7 -0
- package/src/__tests__/conversation-analysis-routes.test.ts +2 -0
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +2 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -0
- package/src/__tests__/conversation-history-web-search.test.ts +2 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +2 -0
- package/src/__tests__/conversation-load-history-stripped.test.ts +2 -0
- package/src/__tests__/conversation-pairing.test.ts +2 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
- package/src/__tests__/conversation-process-callsite.test.ts +2 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
- package/src/__tests__/conversation-queue.test.ts +91 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +14 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +14 -0
- package/src/__tests__/conversation-slash-queue.test.ts +2 -0
- package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
- package/src/__tests__/conversation-speed-override.test.ts +2 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +65 -0
- package/src/__tests__/conversation-title-service.test.ts +2 -0
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +47 -0
- package/src/__tests__/conversation-usage.test.ts +2 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
- package/src/__tests__/credential-security-invariants.test.ts +0 -1
- package/src/__tests__/db-migration-rollback.test.ts +205 -171
- package/src/__tests__/db-test-helpers.ts +5 -4
- package/src/__tests__/deterministic-verification-control-plane.test.ts +4 -2
- package/src/__tests__/disk-pressure-guard.test.ts +41 -0
- package/src/__tests__/dm-persistence.test.ts +2 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +10 -5
- package/src/__tests__/events-dev-bypass-actor.test.ts +7 -1
- package/src/__tests__/filing-service.test.ts +2 -0
- package/src/__tests__/guardian-binding-drift-heal.test.ts +75 -10
- package/src/__tests__/guardian-dispatch.test.ts +95 -1
- package/src/__tests__/guardian-outbound-http.test.ts +13 -0
- package/src/__tests__/heartbeat-disk-pressure.test.ts +2 -0
- package/src/__tests__/heartbeat-service.test.ts +2 -0
- package/src/__tests__/helpers/channel-test-adapter.ts +1 -7
- package/src/__tests__/host-app-control-routes.test.ts +24 -30
- package/src/__tests__/host-bash-routes.test.ts +31 -41
- package/src/__tests__/host-browser-routes.test.ts +26 -32
- package/src/__tests__/host-cu-proxy.test.ts +299 -0
- package/src/__tests__/host-cu-routes-targeted.test.ts +25 -33
- package/src/__tests__/host-file-routes-targeted.test.ts +40 -52
- package/src/__tests__/host-transfer-routes-targeted.test.ts +31 -43
- package/src/__tests__/http-user-message-parity.test.ts +167 -8
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/invite-redemption-service.test.ts +43 -0
- package/src/__tests__/llm-context-normalization.test.ts +105 -0
- package/src/__tests__/llm-usage-store.test.ts +25 -0
- package/src/__tests__/media-stream-server-integration.test.ts +127 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +2 -0
- package/src/__tests__/messaging-send-tool.test.ts +2 -0
- package/src/__tests__/migration-import-from-url.test.ts +2 -2
- package/src/__tests__/native-web-search.test.ts +2 -0
- package/src/__tests__/non-member-access-request.test.ts +189 -17
- package/src/__tests__/notification-broadcaster.test.ts +4 -0
- package/src/__tests__/notification-decision-recipient-context.test.ts +33 -32
- package/src/__tests__/notification-deep-link.test.ts +6 -0
- package/src/__tests__/notification-guardian-path.test.ts +19 -0
- package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/pending-interactions-resolved-event.test.ts +7 -4
- package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
- package/src/__tests__/plugin-bootstrap.test.ts +3 -73
- package/src/__tests__/plugin-route-contribution.test.ts +4 -17
- package/src/__tests__/plugin-tool-contribution.test.ts +3 -18
- package/src/__tests__/plugin-types.test.ts +0 -2
- package/src/__tests__/process-message-background-slack.test.ts +2 -0
- package/src/__tests__/process-message-display-content.test.ts +2 -0
- package/src/__tests__/provider-usage-tracking.test.ts +39 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +2 -0
- package/src/__tests__/registry.test.ts +3 -0
- package/src/__tests__/relay-server.test.ts +694 -25
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/secret-ingress-http.test.ts +14 -0
- package/src/__tests__/send-endpoint-busy.test.ts +30 -8
- package/src/__tests__/skills.test.ts +44 -0
- package/src/__tests__/slack-inbound-verification.test.ts +47 -2
- package/src/__tests__/sse-actor-principal-guardian-source.test.ts +102 -0
- package/src/__tests__/steer-on-enqueue-question.test.ts +181 -0
- package/src/__tests__/stt-hints.test.ts +44 -13
- package/src/__tests__/subagent-detail.test.ts +27 -0
- package/src/__tests__/subagent-disposal.test.ts +65 -0
- package/src/__tests__/subagent-notify-parent.test.ts +2 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +2 -0
- package/src/__tests__/subagent-tools.test.ts +2 -0
- package/src/__tests__/suggestion-routes.test.ts +2 -0
- package/src/__tests__/title-generate-hook.test.ts +2 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -0
- package/src/__tests__/tool-executor.test.ts +16 -11
- package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +2 -0
- package/src/__tests__/tool-start-timestamp.test.ts +2 -0
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +10 -10
- package/src/__tests__/twilio-routes.test.ts +96 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -0
- package/src/__tests__/web-search-backend-failure.test.ts +2 -0
- package/src/__tests__/workspace-tool-loader.test.ts +195 -2
- package/src/agent/loop-exclusive-tool.test.ts +150 -0
- package/src/agent/loop.ts +56 -0
- package/src/api/constants/sse-replay.ts +41 -0
- package/src/api/index.ts +6 -0
- package/src/api/responses/llm-request-log-entry.ts +25 -0
- package/src/api/responses/subagent-detail.ts +17 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +262 -4
- package/src/calls/call-domain.ts +3 -3
- package/src/calls/guardian-dispatch.ts +10 -8
- package/src/calls/inbound-trust-reader.ts +17 -1
- package/src/calls/media-stream-server.ts +21 -0
- package/src/calls/relay-server.ts +167 -50
- package/src/calls/relay-setup-router.ts +37 -7
- package/src/calls/relay-verification.ts +4 -4
- package/src/calls/stt-hints.ts +9 -12
- package/src/calls/twilio-routes.ts +14 -4
- package/src/cli/commands/__tests__/cache.test.ts +8 -1
- package/src/cli/commands/cache.ts +194 -181
- package/src/cli/commands/db/__tests__/repair.test.ts +6 -5
- package/src/cli/commands/db/status.ts +37 -1
- package/src/cli/commands/mcp.ts +252 -218
- package/src/cli/commands/memory/__tests__/worker.test.ts +302 -0
- package/src/cli/commands/memory/index.ts +2 -0
- package/src/cli/commands/memory/worker.ts +175 -0
- package/src/cli/commands/plugins.ts +75 -3
- package/src/cli/lib/__tests__/install-from-github.test.ts +102 -0
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +160 -1
- package/src/cli/lib/list-installed-plugins.ts +179 -1
- package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +143 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +6 -1
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +27 -17
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +13 -3
- package/src/config/feature-flag-registry.json +0 -8
- package/src/config/loader.ts +36 -5
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/memory-lifecycle.ts +12 -0
- package/src/config/schemas/memory-v3.ts +7 -0
- package/src/config/schemas/memory.ts +4 -0
- package/src/config/schemas/timeouts.ts +8 -0
- package/src/config/seed-inference-profiles.ts +14 -5
- package/src/config/skills.ts +27 -5
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +312 -0
- package/src/contacts/contacts-write.ts +3 -0
- package/src/contacts/guardian-delivery-reader.ts +223 -0
- package/src/daemon/conversation-agent-loop.ts +9 -0
- package/src/daemon/conversation-process.ts +39 -17
- package/src/daemon/conversation-surfaces.ts +8 -0
- package/src/daemon/conversation-tool-setup.ts +49 -16
- package/src/daemon/conversation.ts +21 -2
- package/src/daemon/disk-pressure-guard.ts +12 -2
- package/src/daemon/event-loop-watchdog.ts +28 -1
- package/src/daemon/external-plugins-bootstrap.ts +4 -34
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +25 -0
- package/src/daemon/handlers/__tests__/config-channels.test.ts +225 -0
- package/src/daemon/handlers/config-a2a.ts +6 -14
- package/src/daemon/handlers/config-channels.ts +78 -22
- package/src/daemon/handlers/conversations.ts +77 -0
- package/src/daemon/host-cu-proxy.ts +102 -11
- package/src/daemon/lifecycle.ts +4 -0
- package/src/daemon/memory-v2-startup.test.ts +72 -0
- package/src/daemon/memory-v2-startup.ts +87 -19
- package/src/daemon/server.ts +0 -4
- package/src/daemon/shutdown-handlers.ts +20 -0
- package/src/daemon/tool-setup-types.ts +9 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +1 -1
- package/src/ipc/assistant-server.ts +2 -2
- package/src/memory/__tests__/301-create-watchdog-events.test.ts +110 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +8 -0
- package/src/memory/__tests__/prompt-override.test.ts +192 -0
- package/src/memory/__tests__/watchdog-events-store.test.ts +161 -0
- package/src/memory/conversation-crud.ts +38 -0
- package/src/memory/db-connection.ts +22 -3
- package/src/memory/db-init.ts +36 -502
- package/src/memory/db-singleton.ts +6 -4
- package/src/memory/jobs-worker.ts +58 -0
- package/src/memory/llm-usage-store.ts +48 -20
- package/src/memory/memory-retrospective-job.ts +9 -8
- package/src/memory/migrations/014-backfill-inbox-thread-state.ts +13 -3
- package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -27
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +130 -56
- package/src/memory/migrations/300-add-processing-started-at.ts +30 -0
- package/src/memory/migrations/301-create-watchdog-events.ts +45 -0
- package/src/memory/migrations/__tests__/014-backfill-inbox-thread-state.test.ts +108 -0
- package/src/memory/migrations/__tests__/136-drop-assistant-id-columns.test.ts +82 -0
- package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +224 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
- package/src/memory/migrations/run-migrations.ts +90 -6
- package/src/memory/migrations/schema-introspection.ts +14 -0
- package/src/memory/migrations/validate-migration-state.ts +101 -66
- package/src/memory/prompt-override.ts +129 -0
- package/src/memory/schema/conversations.ts +9 -0
- package/src/memory/schema/infrastructure.ts +20 -0
- package/src/memory/steps.ts +573 -0
- package/src/memory/v2/__tests__/cli-command-store.test.ts +25 -0
- package/src/memory/v2/__tests__/skill-store.test.ts +80 -0
- package/src/memory/v2/cli-command-store.ts +75 -38
- package/src/memory/v2/prompts/consolidation.ts +13 -82
- package/src/memory/v2/prompts/router.ts +21 -93
- package/src/memory/v2/skill-store.ts +68 -31
- package/src/memory/watchdog-events-store.ts +87 -0
- package/src/memory/worker-control.ts +118 -0
- package/src/memory/worker-process.ts +72 -0
- package/src/notifications/__tests__/broadcaster.test.ts +16 -8
- package/src/notifications/__tests__/connected-channels.test.ts +114 -0
- package/src/notifications/__tests__/decision-engine.test.ts +78 -9
- package/src/notifications/__tests__/destination-resolver.test.ts +256 -0
- package/src/notifications/broadcaster.ts +8 -1
- package/src/notifications/decision-engine.ts +15 -7
- package/src/notifications/destination-resolver.ts +68 -24
- package/src/notifications/emit-signal.ts +39 -14
- package/src/onboarding/checkin-event.test.ts +220 -0
- package/src/onboarding/checkin-event.ts +321 -0
- package/src/onboarding/schedule-checkin.ts +190 -0
- package/src/permissions/question-prompter.test.ts +1 -1
- package/src/permissions/question-prompter.ts +7 -4
- package/src/plugin-api/index.ts +6 -6
- package/src/plugin-api/types.ts +3 -5
- package/src/plugin-api/vision-support.test.ts +28 -4
- package/src/plugin-api/vision-support.ts +66 -31
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +161 -0
- package/src/plugins/defaults/advisor/__tests__/context-pack-gating.test.ts +106 -0
- package/src/plugins/defaults/advisor/__tests__/context-pack.test.ts +60 -0
- package/src/plugins/defaults/advisor/consult.ts +110 -6
- package/src/plugins/defaults/advisor/context-pack.ts +288 -0
- package/src/plugins/defaults/advisor/steering.ts +14 -2
- package/src/plugins/defaults/advisor/tools/advisor.ts +32 -5
- package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +47 -7
- package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +10 -11
- package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +12 -20
- package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +42 -11
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +11 -2
- package/src/plugins/defaults/memory-v3-shadow/pool-select.test.ts +146 -0
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +29 -1
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +8 -1
- package/src/plugins/mtime-cache.ts +7 -2
- package/src/plugins/types.ts +0 -2
- package/src/providers/anthropic/client.ts +5 -0
- package/src/providers/call-site-routing.ts +4 -0
- package/src/providers/model-catalog.ts +16 -0
- package/src/providers/openai/responses-provider.ts +5 -0
- package/src/providers/openrouter/client.ts +5 -0
- package/src/providers/provider-send-message.ts +4 -0
- package/src/providers/ratelimit.ts +4 -0
- package/src/providers/retry.ts +4 -0
- package/src/providers/types.ts +9 -0
- package/src/providers/usage-tracking.ts +4 -0
- package/src/runtime/__tests__/channel-verification-service.test.ts +133 -0
- package/src/runtime/__tests__/guardian-vellum-migration.test.ts +181 -0
- package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +66 -0
- package/src/runtime/__tests__/local-principal-trust.test.ts +164 -0
- package/src/runtime/__tests__/trust-verdict-consumer.test.ts +335 -3
- package/src/runtime/access-request-helper.ts +19 -39
- package/src/runtime/actor-trust-resolver.ts +2 -2
- package/src/runtime/anchored-guardian.test.ts +156 -0
- package/src/runtime/anchored-guardian.ts +135 -0
- package/src/runtime/assistant-event-hub.ts +1 -1
- package/src/runtime/assistant-stream-state.ts +9 -2
- package/src/runtime/auth/__tests__/require-bound-guardian.test.ts +99 -0
- package/src/runtime/auth/require-bound-guardian.ts +21 -11
- package/src/runtime/channel-verification-service.ts +56 -31
- package/src/runtime/confirmation-request-guardian-bridge.ts +3 -3
- package/src/runtime/guardian-vellum-migration.ts +66 -7
- package/src/runtime/invite-redemption-service.ts +50 -18
- package/src/runtime/local-actor-identity.ts +76 -11
- package/src/runtime/local-principal-trust.ts +52 -0
- package/src/runtime/pending-interactions.ts +11 -1
- package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +56 -5
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/contact-routes.test.ts +212 -0
- package/src/runtime/routes/__tests__/global-search-routes.test.ts +93 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +215 -1
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/channel-verification-routes.ts +3 -3
- package/src/runtime/routes/contact-routes.ts +8 -32
- package/src/runtime/routes/conversation-cli-routes.ts +4 -5
- package/src/runtime/routes/conversation-list-routes.ts +4 -7
- package/src/runtime/routes/conversation-routes.ts +74 -81
- package/src/runtime/routes/events-routes.ts +2 -2
- package/src/runtime/routes/global-search-routes.ts +3 -1
- package/src/runtime/routes/guardian-action-routes.ts +4 -5
- package/src/runtime/routes/host-app-control-routes.ts +5 -4
- package/src/runtime/routes/host-bash-routes.ts +5 -4
- package/src/runtime/routes/host-browser-routes.ts +9 -11
- package/src/runtime/routes/host-cu-routes.ts +5 -4
- package/src/runtime/routes/host-file-routes.ts +5 -4
- package/src/runtime/routes/host-transfer-routes.ts +6 -6
- package/src/runtime/routes/http-adapter.ts +1 -1
- package/src/runtime/routes/identity-routes.ts +3 -2
- package/src/runtime/routes/inbound-message-handler.ts +5 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +97 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +61 -49
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +16 -4
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +21 -8
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -3
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/llm-context-normalization.ts +71 -0
- package/src/runtime/routes/mcp-auth-routes.ts +38 -15
- package/src/runtime/routes/migration-rollback-routes.ts +4 -3
- package/src/runtime/routes/migration-routes.ts +4 -1
- package/src/runtime/routes/onboarding-checkin-routes.ts +86 -0
- package/src/runtime/routes/subagents-routes.ts +5 -0
- package/src/runtime/routes/surface-action-routes.ts +51 -55
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
- package/src/runtime/services/conversation-serializer.ts +7 -9
- package/src/runtime/tool-grant-request-helper.ts +3 -3
- package/src/runtime/trust-verdict-consumer.ts +85 -9
- package/src/runtime/verification-outbound-actions.ts +18 -18
- package/src/signals/user-message.ts +16 -0
- package/src/subagent/manager.ts +9 -0
- package/src/telemetry/types.ts +34 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
- package/src/telemetry/usage-telemetry-reporter.ts +87 -3
- package/src/tools/ask-question/ask-question-tool.test.ts +29 -0
- package/src/tools/ask-question/ask-question-tool.ts +13 -0
- package/src/tools/computer-use/definitions.ts +8 -2
- package/src/tools/executor.ts +4 -4
- package/src/tools/registry.ts +18 -0
- package/src/tools/tool-approval-handler.ts +1 -1
- package/src/tools/tool-defaults.ts +9 -2
- package/src/tools/types.ts +17 -2
- package/src/tools/workspace-tools/loader.ts +348 -244
- package/src/util/platform.ts +5 -0
- package/src/util/telemetry-db-path.ts +24 -0
- package/src/workspace/migrations/017-seed-persona-dirs.ts +3 -34
- package/src/workspace/migrations/019-scope-journal-to-guardian.ts +3 -24
- package/src/__tests__/workspace-tools-watcher-flag.test.ts +0 -70
- package/src/daemon/workspace-tools-watcher.ts +0 -328
- package/src/memory/migrations/registry.ts +0 -573
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Verifies resolveDestinations resolves guardian delivery endpoints from the
|
|
3
|
+
* gateway-provided guardian list, with shapes identical to the local read, and
|
|
4
|
+
* falls back to the local contacts read when the list is null.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
8
|
+
|
|
9
|
+
import type { GuardianDelivery } from "@vellumai/gateway-client";
|
|
10
|
+
|
|
11
|
+
mock.module("../../util/logger.js", () => ({
|
|
12
|
+
getLogger: () =>
|
|
13
|
+
new Proxy({} as Record<string, unknown>, {
|
|
14
|
+
get: () => () => {},
|
|
15
|
+
}),
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
// Local fallback read; mocked so the null-list path is deterministic.
|
|
19
|
+
let localGuardian:
|
|
20
|
+
| { contact: { principalId?: string }; channel: { address: string; externalChatId?: string } }
|
|
21
|
+
| null = null;
|
|
22
|
+
|
|
23
|
+
mock.module("../../contacts/contact-store.js", () => ({
|
|
24
|
+
findGuardianForChannel: (_channelType: string) => localGuardian,
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
const { resolveDestinations } = await import("../destination-resolver.js");
|
|
28
|
+
|
|
29
|
+
function guardian(
|
|
30
|
+
overrides: Partial<GuardianDelivery> & Pick<GuardianDelivery, "channelType" | "address">,
|
|
31
|
+
): GuardianDelivery {
|
|
32
|
+
return {
|
|
33
|
+
contactId: "contact-1",
|
|
34
|
+
status: "active",
|
|
35
|
+
...overrides,
|
|
36
|
+
} as GuardianDelivery;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe("resolveDestinations — gateway guardian list", () => {
|
|
40
|
+
beforeEach(() => {
|
|
41
|
+
localGuardian = null;
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("vellum carries guardianPrincipalId from the gateway list", () => {
|
|
45
|
+
const list = [
|
|
46
|
+
guardian({ channelType: "vellum", address: "user@example.com", principalId: "prin-1" }),
|
|
47
|
+
];
|
|
48
|
+
const result = resolveDestinations(["vellum"], list);
|
|
49
|
+
expect(result.get("vellum")).toEqual({
|
|
50
|
+
channel: "vellum",
|
|
51
|
+
metadata: { guardianPrincipalId: "prin-1" },
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("platform carries guardianPrincipalId from the vellum guardian", () => {
|
|
56
|
+
const list = [
|
|
57
|
+
guardian({ channelType: "vellum", address: "user@example.com", principalId: "prin-1" }),
|
|
58
|
+
];
|
|
59
|
+
const result = resolveDestinations(["platform"], list);
|
|
60
|
+
expect(result.get("platform")).toEqual({
|
|
61
|
+
channel: "platform",
|
|
62
|
+
metadata: { guardianPrincipalId: "prin-1" },
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("telegram resolves endpoint and binding context", () => {
|
|
67
|
+
const list = [
|
|
68
|
+
guardian({
|
|
69
|
+
channelType: "telegram",
|
|
70
|
+
address: "tg-user",
|
|
71
|
+
externalChatId: "12345",
|
|
72
|
+
}),
|
|
73
|
+
];
|
|
74
|
+
const result = resolveDestinations(["telegram"], list);
|
|
75
|
+
expect(result.get("telegram")).toEqual({
|
|
76
|
+
channel: "telegram",
|
|
77
|
+
endpoint: "12345",
|
|
78
|
+
metadata: { externalUserId: "tg-user" },
|
|
79
|
+
bindingContext: {
|
|
80
|
+
sourceChannel: "telegram",
|
|
81
|
+
externalChatId: "12345",
|
|
82
|
+
externalUserId: "tg-user",
|
|
83
|
+
},
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("telegram without externalChatId is omitted", () => {
|
|
88
|
+
const list = [guardian({ channelType: "telegram", address: "tg-user" })];
|
|
89
|
+
const result = resolveDestinations(["telegram"], list);
|
|
90
|
+
expect(result.has("telegram")).toBe(false);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("slack resolves DM endpoint and binding context", () => {
|
|
94
|
+
const list = [
|
|
95
|
+
guardian({
|
|
96
|
+
channelType: "slack",
|
|
97
|
+
address: "slack-user",
|
|
98
|
+
externalChatId: "D123",
|
|
99
|
+
}),
|
|
100
|
+
];
|
|
101
|
+
const result = resolveDestinations(["slack"], list);
|
|
102
|
+
expect(result.get("slack")).toEqual({
|
|
103
|
+
channel: "slack",
|
|
104
|
+
endpoint: "D123",
|
|
105
|
+
metadata: { externalUserId: "slack-user" },
|
|
106
|
+
bindingContext: {
|
|
107
|
+
sourceChannel: "slack",
|
|
108
|
+
externalChatId: "D123",
|
|
109
|
+
externalUserId: "slack-user",
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
test("slack non-DM channel is dropped", () => {
|
|
115
|
+
const list = [
|
|
116
|
+
guardian({
|
|
117
|
+
channelType: "slack",
|
|
118
|
+
address: "slack-user",
|
|
119
|
+
externalChatId: "C123",
|
|
120
|
+
}),
|
|
121
|
+
];
|
|
122
|
+
const result = resolveDestinations(["slack"], list);
|
|
123
|
+
expect(result.has("slack")).toBe(false);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("inactive guardian is ignored", () => {
|
|
127
|
+
const list = [
|
|
128
|
+
guardian({
|
|
129
|
+
channelType: "telegram",
|
|
130
|
+
address: "tg-user",
|
|
131
|
+
externalChatId: "12345",
|
|
132
|
+
status: "revoked",
|
|
133
|
+
}),
|
|
134
|
+
];
|
|
135
|
+
const result = resolveDestinations(["telegram"], list);
|
|
136
|
+
expect(result.has("telegram")).toBe(false);
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
describe("resolveDestinations — null list falls back to local read", () => {
|
|
141
|
+
beforeEach(() => {
|
|
142
|
+
localGuardian = null;
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
test("telegram resolves from the local contacts read", () => {
|
|
146
|
+
localGuardian = {
|
|
147
|
+
contact: {},
|
|
148
|
+
channel: { address: "tg-user", externalChatId: "12345" },
|
|
149
|
+
};
|
|
150
|
+
const result = resolveDestinations(["telegram"], null);
|
|
151
|
+
expect(result.get("telegram")).toEqual({
|
|
152
|
+
channel: "telegram",
|
|
153
|
+
endpoint: "12345",
|
|
154
|
+
metadata: { externalUserId: "tg-user" },
|
|
155
|
+
bindingContext: {
|
|
156
|
+
sourceChannel: "telegram",
|
|
157
|
+
externalChatId: "12345",
|
|
158
|
+
externalUserId: "tg-user",
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("slack DM resolves from the local contacts read", () => {
|
|
164
|
+
localGuardian = {
|
|
165
|
+
contact: {},
|
|
166
|
+
channel: { address: "slack-user", externalChatId: "D123" },
|
|
167
|
+
};
|
|
168
|
+
const result = resolveDestinations(["slack"], null);
|
|
169
|
+
expect(result.get("slack")).toEqual({
|
|
170
|
+
channel: "slack",
|
|
171
|
+
endpoint: "D123",
|
|
172
|
+
metadata: { externalUserId: "slack-user" },
|
|
173
|
+
bindingContext: {
|
|
174
|
+
sourceChannel: "slack",
|
|
175
|
+
externalChatId: "D123",
|
|
176
|
+
externalUserId: "slack-user",
|
|
177
|
+
},
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test("vellum carries principalId from the local contacts read", () => {
|
|
182
|
+
localGuardian = {
|
|
183
|
+
contact: { principalId: "prin-1" },
|
|
184
|
+
channel: { address: "user@example.com" },
|
|
185
|
+
};
|
|
186
|
+
const result = resolveDestinations(["vellum"], null);
|
|
187
|
+
expect(result.get("vellum")).toEqual({
|
|
188
|
+
channel: "vellum",
|
|
189
|
+
metadata: { guardianPrincipalId: "prin-1" },
|
|
190
|
+
});
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
describe("resolveDestinations — gateway yields no channel match falls back to local", () => {
|
|
195
|
+
beforeEach(() => {
|
|
196
|
+
localGuardian = null;
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test("empty gateway list falls back to local telegram binding", () => {
|
|
200
|
+
localGuardian = {
|
|
201
|
+
contact: {},
|
|
202
|
+
channel: { address: "tg-user", externalChatId: "12345" },
|
|
203
|
+
};
|
|
204
|
+
const result = resolveDestinations(["telegram"], []);
|
|
205
|
+
expect(result.get("telegram")).toEqual({
|
|
206
|
+
channel: "telegram",
|
|
207
|
+
endpoint: "12345",
|
|
208
|
+
metadata: { externalUserId: "tg-user" },
|
|
209
|
+
bindingContext: {
|
|
210
|
+
sourceChannel: "telegram",
|
|
211
|
+
externalChatId: "12345",
|
|
212
|
+
externalUserId: "tg-user",
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
test("gateway list missing the channel falls back to local slack DM", () => {
|
|
218
|
+
localGuardian = {
|
|
219
|
+
contact: {},
|
|
220
|
+
channel: { address: "slack-user", externalChatId: "D123" },
|
|
221
|
+
};
|
|
222
|
+
// Gateway returns a telegram guardian but no slack entry.
|
|
223
|
+
const list = [
|
|
224
|
+
guardian({ channelType: "telegram", address: "tg", externalChatId: "999" }),
|
|
225
|
+
];
|
|
226
|
+
const result = resolveDestinations(["slack"], list);
|
|
227
|
+
expect(result.get("slack")).toEqual({
|
|
228
|
+
channel: "slack",
|
|
229
|
+
endpoint: "D123",
|
|
230
|
+
metadata: { externalUserId: "slack-user" },
|
|
231
|
+
bindingContext: {
|
|
232
|
+
sourceChannel: "slack",
|
|
233
|
+
externalChatId: "D123",
|
|
234
|
+
externalUserId: "slack-user",
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test("empty gateway list falls back to local vellum principalId", () => {
|
|
240
|
+
localGuardian = {
|
|
241
|
+
contact: { principalId: "prin-1" },
|
|
242
|
+
channel: { address: "user@example.com" },
|
|
243
|
+
};
|
|
244
|
+
const result = resolveDestinations(["vellum"], []);
|
|
245
|
+
expect(result.get("vellum")).toEqual({
|
|
246
|
+
channel: "vellum",
|
|
247
|
+
metadata: { guardianPrincipalId: "prin-1" },
|
|
248
|
+
});
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test("empty gateway list with no local binding omits telegram", () => {
|
|
252
|
+
localGuardian = null;
|
|
253
|
+
const result = resolveDestinations(["telegram"], []);
|
|
254
|
+
expect(result.has("telegram")).toBe(false);
|
|
255
|
+
});
|
|
256
|
+
});
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
import { v4 as uuid } from "uuid";
|
|
13
13
|
|
|
14
|
+
import { getGuardianDelivery } from "../contacts/guardian-delivery-reader.js";
|
|
14
15
|
import { getConversation } from "../memory/conversation-crud.js";
|
|
15
16
|
import type { ApprovalUIMetadata } from "../runtime/channel-approval-types.js";
|
|
16
17
|
import { getLogger } from "../util/logger.js";
|
|
@@ -171,7 +172,13 @@ export class NotificationBroadcaster {
|
|
|
171
172
|
decision: NotificationDecision,
|
|
172
173
|
options?: BroadcastDecisionOptions,
|
|
173
174
|
): Promise<NotificationDeliveryResult[]> {
|
|
174
|
-
|
|
175
|
+
// Pull the guardian list once so the resolver stays pure. A null list
|
|
176
|
+
// (gateway unreachable) falls back to the local contacts read.
|
|
177
|
+
const guardians = await getGuardianDelivery();
|
|
178
|
+
const destinations = resolveDestinations(
|
|
179
|
+
decision.selectedChannels,
|
|
180
|
+
guardians,
|
|
181
|
+
);
|
|
175
182
|
|
|
176
183
|
// Ensure vellum is processed first so the notification_conversation_created
|
|
177
184
|
// event fires immediately, before slower channel sends (e.g. Telegram 30s
|
|
@@ -12,7 +12,11 @@
|
|
|
12
12
|
import { v4 as uuid } from "uuid";
|
|
13
13
|
|
|
14
14
|
import { getDeliverableChannels } from "../channels/config.js";
|
|
15
|
-
import {
|
|
15
|
+
import { findContactInfoById } from "../contacts/contact-store.js";
|
|
16
|
+
import {
|
|
17
|
+
anyGuardian,
|
|
18
|
+
getGuardianDelivery,
|
|
19
|
+
} from "../contacts/guardian-delivery-reader.js";
|
|
16
20
|
import { buildCoreIdentityContext } from "../prompts/system-prompt.js";
|
|
17
21
|
import {
|
|
18
22
|
createTimeout,
|
|
@@ -963,15 +967,19 @@ async function classifyWithLLM(
|
|
|
963
967
|
? truncate(rawIdentityContext, MAX_IDENTITY_CONTEXT_CHARS, "\n…[truncated]")
|
|
964
968
|
: undefined;
|
|
965
969
|
|
|
966
|
-
// Resolve guardian contact notes for recipient context.
|
|
967
|
-
//
|
|
968
|
-
// deliverable channel is "vellum" (
|
|
970
|
+
// Resolve guardian contact notes for recipient context. The guardian's
|
|
971
|
+
// identity (ACL) comes from the gateway pull, channel-agnostic so notes are
|
|
972
|
+
// available even when the only deliverable channel is "vellum". Notes (INFO)
|
|
973
|
+
// stay local and are joined by contactId.
|
|
969
974
|
let recipientNotes: string | undefined;
|
|
970
975
|
try {
|
|
971
|
-
const
|
|
972
|
-
|
|
976
|
+
const guardian = anyGuardian((await getGuardianDelivery()) ?? []);
|
|
977
|
+
const notes = guardian
|
|
978
|
+
? findContactInfoById(guardian.contactId)?.notes
|
|
979
|
+
: undefined;
|
|
980
|
+
if (notes) {
|
|
973
981
|
recipientNotes = truncate(
|
|
974
|
-
|
|
982
|
+
notes,
|
|
975
983
|
MAX_IDENTITY_CONTEXT_CHARS,
|
|
976
984
|
"\n…[truncated]",
|
|
977
985
|
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Resolves per-channel destination endpoints for notification delivery.
|
|
3
3
|
*
|
|
4
|
-
* Reads guardian delivery info from the
|
|
4
|
+
* Reads guardian delivery info from the gateway-backed guardian list.
|
|
5
5
|
*
|
|
6
6
|
* - Vellum: no external endpoint needed — delivery goes through the event
|
|
7
7
|
* broadcast mechanism to connected desktop/mobile clients. The
|
|
@@ -11,14 +11,54 @@
|
|
|
11
11
|
* sourced from the guardian contact's channel record.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
import type { GuardianDelivery } from "@vellumai/gateway-client";
|
|
15
|
+
|
|
14
16
|
import { isNotificationDeliverable } from "../channels/config.js";
|
|
15
17
|
import type { ChannelId } from "../channels/types.js";
|
|
16
18
|
import { findGuardianForChannel } from "../contacts/contact-store.js";
|
|
19
|
+
import { guardianForChannel } from "../contacts/guardian-delivery-reader.js";
|
|
17
20
|
import { getLogger } from "../util/logger.js";
|
|
18
21
|
import type { ChannelDestination, NotificationChannel } from "./types.js";
|
|
19
22
|
|
|
20
23
|
const log = getLogger("destination-resolver");
|
|
21
24
|
|
|
25
|
+
/** Guardian delivery endpoint for a channel, flattened from either source. */
|
|
26
|
+
interface ResolvedGuardian {
|
|
27
|
+
principalId?: string;
|
|
28
|
+
address: string;
|
|
29
|
+
externalChatId?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Resolve the guardian delivery endpoint for a channel: gateway list first,
|
|
34
|
+
* else the local contacts read. The local read is the transitional
|
|
35
|
+
* dual-written mirror and covers a transient gateway failure (null list) or a
|
|
36
|
+
* gateway list missing this channel, so a soft-failed gateway read does not
|
|
37
|
+
* drop a binding the local store still holds. Removed in Combo 11.
|
|
38
|
+
*/
|
|
39
|
+
function resolveGuardian(
|
|
40
|
+
guardians: GuardianDelivery[] | null,
|
|
41
|
+
channelType: string,
|
|
42
|
+
): ResolvedGuardian | undefined {
|
|
43
|
+
const g = guardians
|
|
44
|
+
? guardianForChannel(guardians, channelType)
|
|
45
|
+
: undefined;
|
|
46
|
+
if (g) {
|
|
47
|
+
return {
|
|
48
|
+
principalId: g.principalId ?? undefined,
|
|
49
|
+
address: g.address,
|
|
50
|
+
externalChatId: g.externalChatId ?? undefined,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const local = findGuardianForChannel(channelType);
|
|
54
|
+
if (!local) return undefined;
|
|
55
|
+
return {
|
|
56
|
+
principalId: local.contact.principalId ?? undefined,
|
|
57
|
+
address: local.channel.address,
|
|
58
|
+
externalChatId: local.channel.externalChatId ?? undefined,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
22
62
|
/**
|
|
23
63
|
* Resolve destination information for each requested channel.
|
|
24
64
|
*
|
|
@@ -26,9 +66,14 @@ const log = getLogger("destination-resolver");
|
|
|
26
66
|
* the function skips non-deliverable channels via `isNotificationDeliverable`.
|
|
27
67
|
* Returns a map keyed by `NotificationChannel`. Channels that cannot be
|
|
28
68
|
* resolved (e.g. no Telegram binding configured) are omitted from the result.
|
|
69
|
+
*
|
|
70
|
+
* `guardians` is the gateway-resolved guardian list; per channel, a missing
|
|
71
|
+
* match (null list or no entry for the channel) falls back to the local
|
|
72
|
+
* contacts read for this release.
|
|
29
73
|
*/
|
|
30
74
|
export function resolveDestinations(
|
|
31
75
|
channels: readonly (ChannelId | NotificationChannel)[],
|
|
76
|
+
guardians: GuardianDelivery[] | null,
|
|
32
77
|
): Map<NotificationChannel, ChannelDestination> {
|
|
33
78
|
const result = new Map<NotificationChannel, ChannelDestination>();
|
|
34
79
|
|
|
@@ -43,10 +88,10 @@ export function resolveDestinations(
|
|
|
43
88
|
// Vellum delivery is local — no external endpoint required.
|
|
44
89
|
// Include the guardianPrincipalId so the adapter can annotate
|
|
45
90
|
// guardian-sensitive notifications for scoped delivery.
|
|
46
|
-
const
|
|
91
|
+
const guardian = resolveGuardian(guardians, "vellum");
|
|
47
92
|
const metadata: Record<string, unknown> = {};
|
|
48
|
-
if (
|
|
49
|
-
metadata.guardianPrincipalId =
|
|
93
|
+
if (guardian?.principalId) {
|
|
94
|
+
metadata.guardianPrincipalId = guardian.principalId;
|
|
50
95
|
}
|
|
51
96
|
result.set("vellum", {
|
|
52
97
|
channel: "vellum",
|
|
@@ -55,7 +100,7 @@ export function resolveDestinations(
|
|
|
55
100
|
log.debug(
|
|
56
101
|
{
|
|
57
102
|
channel: "vellum",
|
|
58
|
-
source: "
|
|
103
|
+
source: "guardian-delivery",
|
|
59
104
|
hasEndpoint: false,
|
|
60
105
|
},
|
|
61
106
|
"destination resolved",
|
|
@@ -63,52 +108,52 @@ export function resolveDestinations(
|
|
|
63
108
|
break;
|
|
64
109
|
}
|
|
65
110
|
case "telegram": {
|
|
66
|
-
const
|
|
67
|
-
if (
|
|
68
|
-
const externalChatId =
|
|
111
|
+
const guardian = resolveGuardian(guardians, channel);
|
|
112
|
+
if (guardian?.externalChatId) {
|
|
113
|
+
const externalChatId = guardian.externalChatId;
|
|
69
114
|
result.set(channel as NotificationChannel, {
|
|
70
115
|
channel: channel as NotificationChannel,
|
|
71
116
|
endpoint: externalChatId,
|
|
72
117
|
metadata: {
|
|
73
|
-
externalUserId:
|
|
118
|
+
externalUserId: guardian.address,
|
|
74
119
|
},
|
|
75
120
|
bindingContext: {
|
|
76
121
|
sourceChannel: channel as NotificationChannel,
|
|
77
122
|
externalChatId,
|
|
78
|
-
externalUserId:
|
|
123
|
+
externalUserId: guardian.address,
|
|
79
124
|
},
|
|
80
125
|
});
|
|
81
126
|
}
|
|
82
127
|
log.debug(
|
|
83
128
|
{
|
|
84
129
|
channel,
|
|
85
|
-
source: "
|
|
86
|
-
hasEndpoint: !!
|
|
130
|
+
source: "guardian-delivery",
|
|
131
|
+
hasEndpoint: !!guardian?.externalChatId,
|
|
87
132
|
},
|
|
88
133
|
"destination resolved",
|
|
89
134
|
);
|
|
90
135
|
break;
|
|
91
136
|
}
|
|
92
137
|
case "slack": {
|
|
93
|
-
const
|
|
94
|
-
const chatId =
|
|
138
|
+
const guardian = resolveGuardian(guardians, "slack");
|
|
139
|
+
const chatId = guardian?.externalChatId;
|
|
95
140
|
// Slack bindings can originate from app_mention in shared channels.
|
|
96
141
|
// Only route notifications to DM channels (IDs starting with "D")
|
|
97
142
|
// to prevent leaking notifications into shared workspaces.
|
|
98
|
-
if (
|
|
143
|
+
if (guardian && chatId && isSlackDmChannel(chatId)) {
|
|
99
144
|
result.set("slack", {
|
|
100
145
|
channel: "slack",
|
|
101
146
|
endpoint: chatId,
|
|
102
147
|
metadata: {
|
|
103
|
-
externalUserId:
|
|
148
|
+
externalUserId: guardian.address,
|
|
104
149
|
},
|
|
105
150
|
bindingContext: {
|
|
106
151
|
sourceChannel: "slack",
|
|
107
152
|
externalChatId: chatId,
|
|
108
|
-
externalUserId:
|
|
153
|
+
externalUserId: guardian.address,
|
|
109
154
|
},
|
|
110
155
|
});
|
|
111
|
-
} else if (
|
|
156
|
+
} else if (guardian && chatId) {
|
|
112
157
|
log.warn(
|
|
113
158
|
{ channel: "slack", chatId },
|
|
114
159
|
"skipping non-DM Slack channel for notification delivery",
|
|
@@ -117,7 +162,7 @@ export function resolveDestinations(
|
|
|
117
162
|
log.debug(
|
|
118
163
|
{
|
|
119
164
|
channel: "slack",
|
|
120
|
-
source: "
|
|
165
|
+
source: "guardian-delivery",
|
|
121
166
|
hasEndpoint: !!(chatId && isSlackDmChannel(chatId)),
|
|
122
167
|
},
|
|
123
168
|
"destination resolved",
|
|
@@ -128,11 +173,10 @@ export function resolveDestinations(
|
|
|
128
173
|
// Platform delivery goes through the daemon's VellumPlatformClient —
|
|
129
174
|
// no external binding needed. Include guardianPrincipalId so the
|
|
130
175
|
// adapter can scope guardian-sensitive notifications.
|
|
131
|
-
const platformGuardian =
|
|
176
|
+
const platformGuardian = resolveGuardian(guardians, "vellum");
|
|
132
177
|
const platformMeta: Record<string, unknown> = {};
|
|
133
|
-
if (platformGuardian) {
|
|
134
|
-
platformMeta.guardianPrincipalId =
|
|
135
|
-
platformGuardian.contact.principalId;
|
|
178
|
+
if (platformGuardian?.principalId) {
|
|
179
|
+
platformMeta.guardianPrincipalId = platformGuardian.principalId;
|
|
136
180
|
}
|
|
137
181
|
result.set("platform", {
|
|
138
182
|
channel: "platform",
|
|
@@ -140,7 +184,7 @@ export function resolveDestinations(
|
|
|
140
184
|
Object.keys(platformMeta).length > 0 ? platformMeta : undefined,
|
|
141
185
|
});
|
|
142
186
|
log.debug(
|
|
143
|
-
{ channel: "platform", source: "
|
|
187
|
+
{ channel: "platform", source: "guardian-delivery", hasEndpoint: false },
|
|
144
188
|
"destination resolved",
|
|
145
189
|
);
|
|
146
190
|
break;
|
|
@@ -9,10 +9,15 @@
|
|
|
9
9
|
* propagated unless `throwOnError` is enabled.
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
import type { GuardianDelivery } from "@vellumai/gateway-client";
|
|
12
13
|
import { v4 as uuid } from "uuid";
|
|
13
14
|
|
|
14
15
|
import { getDeliverableChannels } from "../channels/config.js";
|
|
15
16
|
import { findGuardianForChannel } from "../contacts/contact-store.js";
|
|
17
|
+
import {
|
|
18
|
+
getGuardianDelivery,
|
|
19
|
+
guardianForChannel,
|
|
20
|
+
} from "../contacts/guardian-delivery-reader.js";
|
|
16
21
|
import type { ConversationCreateType } from "../memory/conversation-crud.js";
|
|
17
22
|
import { broadcastMessage } from "../runtime/assistant-event-hub.js";
|
|
18
23
|
import { getLogger } from "../util/logger.js";
|
|
@@ -89,9 +94,33 @@ export function getBroadcaster(): NotificationBroadcaster {
|
|
|
89
94
|
|
|
90
95
|
// ── Connected channels resolution ──────────────────────────────────────
|
|
91
96
|
|
|
92
|
-
|
|
97
|
+
/**
|
|
98
|
+
* Resolve a binding-based channel's delivery endpoint (externalChatId) the
|
|
99
|
+
* SAME way destination-resolver's `resolveGuardian` does: gateway match first,
|
|
100
|
+
* falling back to the LOCAL contacts read on ANY per-channel no-match — gateway
|
|
101
|
+
* list null (unreachable) OR no active gateway entry for this channel. The
|
|
102
|
+
* local read is the transitional dual-written mirror, removed in Combo 11.
|
|
103
|
+
* Keeping connectivity aligned with delivery prevents a channel being marked
|
|
104
|
+
* connected but then skipped with no destination (or vice-versa).
|
|
105
|
+
*/
|
|
106
|
+
function resolveChannelChatId(
|
|
107
|
+
guardians: GuardianDelivery[] | null,
|
|
108
|
+
channelType: string,
|
|
109
|
+
): string | undefined {
|
|
110
|
+
const g = guardians ? guardianForChannel(guardians, channelType) : undefined;
|
|
111
|
+
if (g) {
|
|
112
|
+
return g.externalChatId ?? undefined;
|
|
113
|
+
}
|
|
114
|
+
return findGuardianForChannel(channelType)?.channel.externalChatId ?? undefined;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export async function getConnectedChannels(): Promise<NotificationChannel[]> {
|
|
93
118
|
const channels: NotificationChannel[] = [];
|
|
94
119
|
|
|
120
|
+
// Guardian bindings (ACL) come from the gateway pull; null ⇒ gateway
|
|
121
|
+
// unreachable, so binding-based connectivity falls back to the local read.
|
|
122
|
+
const guardians = await getGuardianDelivery();
|
|
123
|
+
|
|
95
124
|
// getDeliverableChannels() returns ChannelId[] but every returned channel
|
|
96
125
|
// has deliveryEnabled: true, making it a valid NotificationChannel at
|
|
97
126
|
// runtime. We iterate over the broad type and narrow via the switch.
|
|
@@ -110,24 +139,20 @@ function getConnectedChannels(): NotificationChannel[] {
|
|
|
110
139
|
channels.push(channel);
|
|
111
140
|
break;
|
|
112
141
|
case "telegram": {
|
|
113
|
-
//
|
|
114
|
-
//
|
|
115
|
-
//
|
|
116
|
-
|
|
117
|
-
// delivery address the destination-resolver needs.
|
|
118
|
-
const guardian = findGuardianForChannel(channel);
|
|
119
|
-
if (guardian && guardian.channel.externalChatId) {
|
|
142
|
+
// Connected when the resolved guardian has a delivery endpoint —
|
|
143
|
+
// mirroring destination-resolver so we never mark connected what
|
|
144
|
+
// can't be delivered.
|
|
145
|
+
if (resolveChannelChatId(guardians, channel)) {
|
|
120
146
|
channels.push(channel);
|
|
121
147
|
}
|
|
122
148
|
break;
|
|
123
149
|
}
|
|
124
150
|
case "slack": {
|
|
125
151
|
// Slack bindings can originate from shared channels (app_mention).
|
|
126
|
-
// Only consider Slack connected when the
|
|
127
|
-
// channel (D-prefixed)
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
if (slackGuardian && chatId && chatId.startsWith("D")) {
|
|
152
|
+
// Only consider Slack connected when the resolved chat ID is a DM
|
|
153
|
+
// channel (D-prefixed), matching destination-resolver's DM gate.
|
|
154
|
+
const chatId = resolveChannelChatId(guardians, "slack");
|
|
155
|
+
if (chatId && chatId.startsWith("D")) {
|
|
131
156
|
channels.push(channel);
|
|
132
157
|
}
|
|
133
158
|
break;
|
|
@@ -290,7 +315,7 @@ export async function emitNotificationSignal<TEventName extends string>(
|
|
|
290
315
|
}
|
|
291
316
|
|
|
292
317
|
// Step 2: Evaluate the signal through the decision engine
|
|
293
|
-
const connectedChannels = getConnectedChannels();
|
|
318
|
+
const connectedChannels = await getConnectedChannels();
|
|
294
319
|
|
|
295
320
|
log.debug(
|
|
296
321
|
{
|