@vellumai/assistant 0.10.2 → 0.10.3-dev.202606252046.9075fd5
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/bun.lock +20 -0
- 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/gateway-ipc-contracts.ts +44 -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/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +28 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +41 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/openapi.yaml +196 -56
- package/package.json +4 -1
- package/scripts/test.sh +36 -15
- package/src/__tests__/actor-token-service.test.ts +36 -14
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +83 -31
- 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__/assistant-attachments.test.ts +42 -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-approval-routes.test.ts +21 -26
- package/src/__tests__/channel-delivery-store.test.ts +28 -0
- package/src/__tests__/channel-guardian.test.ts +164 -78
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +11 -19
- package/src/__tests__/channel-reply-delivery.test.ts +2 -0
- package/src/__tests__/code-search-tool.test.ts +585 -0
- package/src/__tests__/compaction-events.test.ts +2 -0
- package/src/__tests__/compaction-ledger-store.test.ts +128 -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__/config-loader-backfill.test.ts +123 -10
- package/src/__tests__/config-schema.test.ts +1 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +31 -29
- package/src/__tests__/consult-deadline.test.ts +60 -0
- package/src/__tests__/contact-store-interaction-info.test.ts +156 -0
- package/src/__tests__/contact-store-user-file.test.ts +7 -10
- package/src/__tests__/contacts-relay-reads.test.ts +19 -24
- package/src/__tests__/contacts-write.test.ts +0 -2
- 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 +134 -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-attention-telegram.test.ts +9 -11
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -0
- package/src/__tests__/conversation-fork-crud.test.ts +354 -24
- 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-task-progress.test.ts +29 -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 +1 -1
- package/src/__tests__/db-compaction-events-migration.test.ts +129 -0
- package/src/__tests__/db-migration-rollback.test.ts +205 -171
- package/src/__tests__/db-test-helpers.ts +5 -4
- package/src/__tests__/delete-propagation.test.ts +5 -3
- 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-backfill.test.ts +6 -4
- package/src/__tests__/dm-persistence.test.ts +2 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +7 -6
- package/src/__tests__/events-dev-bypass-actor.test.ts +7 -1
- package/src/__tests__/exploration-drift-hook.test.ts +3 -2
- package/src/__tests__/filing-service.test.ts +2 -0
- package/src/__tests__/guardian-binding-drift-heal.test.ts +104 -19
- package/src/__tests__/guardian-dispatch.test.ts +140 -1
- package/src/__tests__/guardian-expiry-notifier.test.ts +282 -0
- package/src/__tests__/guardian-outbound-http.test.ts +13 -0
- package/src/__tests__/guardian-routing-state.test.ts +6 -10
- 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 +46 -19
- package/src/__tests__/helpers/create-guardian-binding.ts +15 -23
- package/src/__tests__/helpers/mock-logger.ts +1 -0
- package/src/__tests__/helpers/seed-contact-channel.ts +96 -0
- 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-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 +290 -8
- package/src/__tests__/inbound-invite-redemption.test.ts +115 -10
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/invite-redemption-service.test.ts +471 -53
- package/src/__tests__/invite-routes-http.test.ts +34 -0
- package/src/__tests__/invite-service-ipc.test.ts +65 -2
- package/src/__tests__/llm-context-normalization.test.ts +105 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +71 -9
- package/src/__tests__/llm-usage-store.test.ts +25 -0
- package/src/__tests__/mcp-config-secret-boundary.test.ts +392 -0
- package/src/__tests__/mcp-health-check.test.ts +2 -1
- 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__/mtime-cache.test.ts +275 -5
- package/src/__tests__/native-web-search.test.ts +2 -0
- package/src/__tests__/non-member-access-request.test.ts +191 -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__/onboarding-persona-write.test.ts +52 -22
- package/src/__tests__/openai-provider.test.ts +22 -12
- package/src/__tests__/openai-responses-provider.test.ts +12 -2
- package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/pending-interactions-resolved-event.test.ts +7 -4
- package/src/__tests__/persist-onboarding-artifacts.test.ts +1 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
- package/src/__tests__/persona-resolver.test.ts +75 -45
- package/src/__tests__/plugin-bootstrap.test.ts +21 -83
- package/src/__tests__/plugin-disabled-state.test.ts +190 -0
- package/src/__tests__/plugin-pipeline.test.ts +96 -0
- package/src/__tests__/plugin-route-contribution.test.ts +6 -19
- package/src/__tests__/plugin-tool-contribution.test.ts +5 -20
- package/src/__tests__/plugin-types.test.ts +2 -4
- 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-error-scenarios.test.ts +5 -4
- package/src/__tests__/provider-usage-tracking.test.ts +39 -0
- package/src/__tests__/reaction-intercept-cold-cache-warm.test.ts +135 -0
- package/src/__tests__/reaction-intercept-member-verdict-warm.test.ts +158 -0
- package/src/__tests__/reaction-persistence.test.ts +51 -4
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +2 -0
- package/src/__tests__/registry.test.ts +4 -1
- package/src/__tests__/relay-server.test.ts +758 -32
- package/src/__tests__/runtime-attachment-metadata.test.ts +9 -12
- package/src/__tests__/secret-ingress-http.test.ts +14 -0
- package/src/__tests__/send-endpoint-busy.test.ts +30 -8
- package/src/__tests__/settings-routes.test.ts +32 -0
- 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 +79 -0
- package/src/__tests__/steer-on-enqueue-question.test.ts +181 -0
- package/src/__tests__/stt-hints.test.ts +49 -15
- package/src/__tests__/subagent-detail.test.ts +27 -0
- package/src/__tests__/subagent-disposal.test.ts +65 -0
- package/src/__tests__/subagent-fork-prompt-role.test.ts +195 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +6 -7
- package/src/__tests__/subagent-notify-parent.test.ts +2 -0
- package/src/__tests__/subagent-role-registry.test.ts +24 -6
- package/src/__tests__/subagent-spawn-and-await.test.ts +546 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +2 -0
- package/src/__tests__/subagent-tools.test.ts +398 -1
- package/src/__tests__/suggestion-routes.test.ts +2 -0
- package/src/__tests__/thread-backfill.test.ts +3 -3
- 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-approval-notifier.test.ts +37 -51
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +12 -12
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +9 -7
- package/src/__tests__/trusted-contact-multichannel.test.ts +16 -7
- package/src/__tests__/trusted-contact-verification.test.ts +79 -54
- package/src/__tests__/twilio-routes.test.ts +96 -0
- package/src/__tests__/ui-file-upload-surface.test.ts +86 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -0
- package/src/__tests__/voice-guardian-cold-cache-warm.test.ts +137 -0
- package/src/__tests__/voice-invite-redemption.test.ts +216 -20
- package/src/__tests__/web-search-backend-failure.test.ts +2 -0
- package/src/__tests__/workspace-migration-102-preserve-heartbeat-enabled-for-existing-workspaces.test.ts +3 -3
- package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +208 -0
- package/src/__tests__/workspace-migration-112-remove-advisor-callsite-override.test.ts +170 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +196 -238
- package/src/__tests__/workspace-migration-remove-hooks.test.ts +14 -35
- package/src/__tests__/workspace-tool-loader.test.ts +195 -2
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +35 -47
- package/src/agent/loop-exclusive-tool.test.ts +154 -0
- package/src/agent/loop-native-web-search.test.ts +200 -0
- package/src/agent/loop.ts +164 -1
- package/src/api/constants/sse-replay.ts +41 -0
- package/src/api/events/conversation-notice.ts +26 -0
- package/src/api/index.ts +19 -1
- package/src/api/responses/llm-request-log-entry.ts +29 -0
- package/src/api/responses/subagent-detail.ts +17 -0
- package/src/api/surfaces.ts +39 -3
- package/src/approvals/guardian-channel-delivery.ts +30 -0
- package/src/approvals/guardian-expiry-notifier.ts +148 -0
- package/src/approvals/guardian-request-resolvers.ts +17 -15
- package/src/calls/__tests__/relay-setup-router.test.ts +267 -17
- package/src/calls/call-domain.ts +3 -3
- package/src/calls/guardian-dispatch.ts +14 -9
- package/src/calls/inbound-trust-reader.ts +24 -2
- package/src/calls/media-stream-server.ts +21 -0
- package/src/calls/relay-access-wait.ts +6 -6
- package/src/calls/relay-server.ts +188 -51
- package/src/calls/relay-setup-router.ts +47 -17
- 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/channels/types.ts +10 -20
- package/src/cli/commands/__tests__/cache.test.ts +8 -1
- package/src/cli/commands/__tests__/conversations-slack.test.ts +1 -0
- package/src/cli/commands/cache.ts +194 -181
- package/src/cli/commands/contacts.ts +10 -7
- 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 +432 -0
- package/src/cli/commands/memory/index.ts +2 -0
- package/src/cli/commands/memory/worker.ts +242 -0
- package/src/cli/commands/plugins.ts +125 -13
- 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/__tests__/publish-plugin.test.ts +404 -0
- package/src/cli/lib/list-installed-plugins.ts +179 -1
- package/src/cli/lib/publish-plugin.ts +633 -0
- package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +143 -0
- package/src/config/__tests__/sync-gated-profiles.test.ts +16 -10
- 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/bundled-skills/subagent/SKILL.md +17 -2
- package/src/config/bundled-skills/subagent/TOOLS.json +5 -4
- package/src/config/call-site-defaults.ts +0 -6
- package/src/config/feature-flag-registry.json +5 -13
- package/src/config/llm-resolver.ts +0 -3
- package/src/config/loader.ts +38 -5
- package/src/config/prune-seeded-callsite-defaults.ts +110 -0
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +0 -7
- package/src/config/schemas/heartbeat.ts +2 -5
- package/src/config/schemas/llm.ts +3 -12
- 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 +30 -34
- package/src/config/skills.ts +27 -5
- package/src/config/sync-gated-profiles.ts +12 -16
- package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +128 -0
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +312 -0
- package/src/contacts/__tests__/member-write-relay.test.ts +226 -0
- package/src/contacts/contact-store.ts +27 -237
- package/src/contacts/contacts-write.ts +18 -55
- package/src/contacts/gateway-channel-read.ts +51 -0
- package/src/contacts/guardian-delivery-reader.ts +223 -0
- package/src/contacts/member-write-relay.ts +183 -0
- package/src/contacts/types.ts +3 -15
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +35 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +0 -44
- package/src/daemon/assistant-attachments.ts +27 -4
- package/src/daemon/conversation-agent-loop.ts +28 -0
- package/src/daemon/conversation-notices.ts +60 -0
- package/src/daemon/conversation-process.ts +35 -16
- package/src/daemon/conversation-surfaces.ts +111 -38
- package/src/daemon/conversation-tool-setup.ts +71 -30
- package/src/daemon/conversation.ts +23 -1
- 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 +17 -41
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -2
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +25 -2
- package/src/daemon/handlers/__tests__/config-channels.test.ts +220 -0
- package/src/daemon/handlers/config-a2a.ts +6 -14
- package/src/daemon/handlers/config-channels.ts +67 -26
- package/src/daemon/handlers/conversations.ts +77 -0
- package/src/daemon/lifecycle.ts +16 -0
- package/src/daemon/mcp-reload-service.ts +10 -0
- package/src/daemon/memory-v2-startup.test.ts +72 -0
- package/src/daemon/memory-v2-startup.ts +87 -19
- package/src/daemon/message-types/conversations.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +12 -12
- 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/heartbeat/heartbeat-service.ts +5 -0
- package/src/home/relationship-state-writer.ts +5 -0
- package/src/hooks/hook-loader.ts +341 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +1 -1
- package/src/ipc/assistant-server.ts +2 -2
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +1 -0
- package/src/mcp/client.ts +15 -1
- package/src/mcp/mcp-auth-orchestrator.ts +6 -1
- package/src/mcp/mcp-header-store.ts +134 -0
- package/src/mcp/mcp-oauth-provider.ts +19 -8
- 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/compaction-ledger-store.ts +107 -0
- package/src/memory/conversation-crud.ts +129 -61
- package/src/memory/db-connection.ts +22 -3
- package/src/memory/db-init.ts +37 -503
- package/src/memory/db-singleton.ts +6 -4
- package/src/memory/jobs-worker.ts +104 -0
- package/src/memory/llm-request-log-store.ts +26 -1
- package/src/memory/llm-usage-store.ts +48 -20
- package/src/memory/memory-retrospective-job.ts +14 -8
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +152 -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/302-create-compaction-events.ts +107 -0
- package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +297 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
- package/src/memory/migrations/run-migrations.ts +106 -10
- package/src/memory/migrations/validate-migration-state.ts +105 -67
- package/src/memory/prompt-override.ts +129 -0
- package/src/memory/schema/contacts.ts +6 -2
- package/src/memory/schema/conversations.ts +37 -0
- package/src/memory/schema/infrastructure.ts +20 -0
- package/src/memory/steps.ts +1294 -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 +204 -0
- package/src/memory/worker-process.ts +89 -0
- package/src/messaging/channel-binding-metadata.ts +31 -0
- package/src/messaging/provider-types.ts +8 -0
- package/src/messaging/providers/slack/binding-metadata.ts +60 -0
- package/src/notifications/__tests__/broadcaster.test.ts +8 -8
- package/src/notifications/__tests__/connected-channels.test.ts +86 -0
- package/src/notifications/__tests__/decision-engine.test.ts +78 -9
- package/src/notifications/__tests__/destination-resolver.test.ts +151 -0
- package/src/notifications/broadcaster.ts +8 -1
- package/src/notifications/decision-engine.ts +15 -7
- package/src/notifications/destination-resolver.ts +53 -25
- package/src/notifications/emit-signal.ts +34 -15
- package/src/onboarding/checkin-event.test.ts +222 -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 +12 -12
- package/src/plugin-api/types.ts +12 -14
- package/src/plugin-api/vision-support.test.ts +28 -4
- package/src/plugin-api/vision-support.ts +66 -31
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/empty-response/hooks/stop.ts +2 -2
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +4 -3
- package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/history-repair/hooks/stop.ts +2 -2
- package/src/plugins/defaults/history-repair/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +47 -7
- package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +12 -13
- package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +14 -22
- package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +42 -11
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/image-recovery/hooks/stop.ts +2 -2
- package/src/plugins/defaults/index.ts +0 -35
- package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/max-tokens-continue/hooks/stop.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +33 -3
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +48 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +4 -8
- package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +43 -15
- 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 +77 -13
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +12 -11
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +2 -2
- package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +2 -2
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +2 -2
- package/src/plugins/defaults/title-generate/hooks/stop.ts +2 -2
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +2 -2
- package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +2 -2
- package/src/plugins/disabled-state.ts +31 -0
- package/src/plugins/external-plugin-loader.ts +2 -2
- package/src/plugins/mtime-cache.ts +186 -330
- package/src/plugins/pipeline.ts +111 -13
- package/src/plugins/registry.ts +59 -16
- package/src/plugins/surface-import.ts +121 -0
- package/src/plugins/types.ts +7 -9
- package/src/prompts/persona-resolver.ts +43 -11
- package/src/providers/anthropic/client.ts +5 -0
- package/src/providers/call-site-routing.ts +45 -0
- package/src/providers/model-catalog.ts +16 -0
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +321 -0
- package/src/providers/openai/api-error-normalization.ts +270 -0
- package/src/providers/openai/chat-completions-provider.ts +37 -83
- package/src/providers/openai/responses-provider.ts +50 -46
- package/src/providers/openrouter/client.ts +5 -0
- package/src/providers/provider-send-message.ts +10 -0
- package/src/providers/ratelimit.ts +10 -0
- package/src/providers/registry.ts +1 -1
- package/src/providers/retry.ts +10 -0
- package/src/providers/types.ts +22 -0
- package/src/providers/usage-tracking.ts +10 -0
- package/src/runtime/__tests__/channel-verification-service.test.ts +133 -0
- package/src/runtime/__tests__/guardian-vellum-migration.test.ts +184 -0
- package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +66 -0
- package/src/runtime/__tests__/local-principal-trust.test.ts +162 -0
- package/src/runtime/__tests__/member-verdict-cache.test.ts +119 -0
- package/src/runtime/__tests__/trust-verdict-consumer.test.ts +402 -123
- package/src/runtime/access-request-helper.ts +18 -39
- package/src/runtime/actor-trust-resolver.ts +46 -19
- package/src/runtime/anchored-guardian.test.ts +109 -0
- package/src/runtime/anchored-guardian.ts +86 -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 +69 -8
- package/src/runtime/invite-redemption-service.ts +213 -187
- package/src/runtime/local-actor-identity.test.ts +108 -0
- package/src/runtime/local-actor-identity.ts +85 -13
- package/src/runtime/local-principal-trust.ts +52 -0
- package/src/runtime/member-verdict-cache.ts +0 -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 +305 -0
- package/src/runtime/routes/__tests__/global-search-routes.test.ts +92 -0
- package/src/runtime/routes/__tests__/http-adapter-actor-header.test.ts +129 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +188 -0
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +13 -5
- package/src/runtime/routes/channel-verification-routes.ts +3 -3
- package/src/runtime/routes/contact-routes.ts +48 -57
- 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-query-routes.ts +72 -0
- package/src/runtime/routes/conversation-routes.ts +84 -85
- 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 +16 -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 -59
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +13 -5
- 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/inbound-stages/reaction-intercept.ts +19 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/llm-context-normalization.ts +83 -0
- package/src/runtime/routes/mcp-auth-routes.ts +171 -19
- 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/settings-routes.ts +8 -3
- package/src/runtime/routes/subagents-routes.ts +5 -0
- package/src/runtime/routes/surface-action-routes.ts +42 -56
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
- package/src/runtime/services/conversation-serializer.ts +13 -58
- package/src/runtime/tool-grant-request-helper.ts +3 -3
- package/src/runtime/trust-verdict-consumer.ts +111 -40
- package/src/runtime/verification-outbound-actions.ts +18 -18
- package/src/signals/user-message.ts +16 -0
- package/src/subagent/__tests__/consult-prompt.test.ts +35 -0
- package/src/{plugins/defaults/advisor/__tests__/transcript.test.ts → subagent/__tests__/consult-transcript.test.ts} +47 -10
- package/src/{plugins/defaults/advisor/steering.ts → subagent/consult-prompt.ts} +22 -32
- package/src/{plugins/defaults/advisor/transcript.ts → subagent/consult-transcript.ts} +18 -8
- package/src/subagent/index.ts +1 -1
- package/src/subagent/manager.ts +254 -33
- package/src/subagent/types.ts +11 -4
- 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/executor.ts +4 -4
- package/src/tools/filesystem/search.ts +543 -0
- package/src/tools/registry.ts +38 -0
- package/src/tools/shared/filesystem/path-policy.ts +12 -5
- package/src/tools/subagent/consult-deadline.ts +49 -0
- package/src/tools/subagent/spawn.ts +234 -5
- package/src/tools/tool-approval-handler.ts +1 -1
- package/src/tools/tool-defaults.ts +9 -2
- package/src/tools/tool-manifest.ts +3 -0
- package/src/tools/types.ts +131 -25
- package/src/tools/workspace-tools/loader.ts +348 -244
- package/src/util/errors.ts +26 -1
- package/src/util/logger.ts +9 -0
- package/src/util/platform.ts +19 -0
- package/src/util/telemetry-db-path.ts +24 -0
- package/src/workflows/library.test.ts +140 -0
- package/src/workflows/library.ts +82 -28
- 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/workspace/migrations/031-drop-user-md.ts +232 -148
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +14 -66
- package/src/workspace/migrations/111-prune-seeded-callsite-defaults.ts +134 -0
- package/src/workspace/migrations/112-remove-advisor-callsite-override.ts +64 -0
- package/src/workspace/migrations/registry.ts +4 -0
- 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
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +0 -56
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +0 -43
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +0 -137
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +0 -153
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +0 -138
- package/src/plugins/defaults/advisor/advisor-gate.ts +0 -29
- package/src/plugins/defaults/advisor/advisor-state-store.ts +0 -94
- package/src/plugins/defaults/advisor/config.ts +0 -21
- package/src/plugins/defaults/advisor/consult.ts +0 -93
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +0 -34
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +0 -30
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +0 -19
- package/src/plugins/defaults/advisor/package.json +0 -14
- package/src/plugins/defaults/advisor/tools/advisor.ts +0 -65
- package/src/providers/openai/__tests__/api-error-detail.test.ts +0 -120
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for the global-search contacts ordering source.
|
|
3
|
+
*
|
|
4
|
+
* Recency ordering for contacts surfaces `contacts.updatedAt`, never the
|
|
5
|
+
* channel-derived `lastInteraction` column. Daemon-native contact search
|
|
6
|
+
* carries no gateway-relayed ContactRead, so `updatedAt` is the deterministic
|
|
7
|
+
* ordering key.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { afterEach, describe, expect, mock, test } from "bun:test";
|
|
11
|
+
|
|
12
|
+
import type { ContactWithChannels } from "../../../contacts/types.js";
|
|
13
|
+
|
|
14
|
+
let searchContactsResult: ContactWithChannels[] = [];
|
|
15
|
+
|
|
16
|
+
const searchContactsMock = mock(() => searchContactsResult);
|
|
17
|
+
|
|
18
|
+
const actualContactStore = await import("../../../contacts/contact-store.js");
|
|
19
|
+
|
|
20
|
+
mock.module("../../../contacts/contact-store.js", () => ({
|
|
21
|
+
...actualContactStore,
|
|
22
|
+
searchContacts: searchContactsMock,
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
const { ROUTES } = await import("../global-search-routes.js");
|
|
26
|
+
|
|
27
|
+
const handler = ROUTES.find((r) => r.operationId === "search_global")!.handler;
|
|
28
|
+
|
|
29
|
+
function makeContact(
|
|
30
|
+
overrides: Partial<ContactWithChannels>,
|
|
31
|
+
): ContactWithChannels {
|
|
32
|
+
return {
|
|
33
|
+
id: "ct_1",
|
|
34
|
+
displayName: "Alice",
|
|
35
|
+
notes: null,
|
|
36
|
+
role: "contact",
|
|
37
|
+
lastInteraction: null,
|
|
38
|
+
interactionCount: 0,
|
|
39
|
+
createdAt: 1,
|
|
40
|
+
updatedAt: 1,
|
|
41
|
+
contactType: "human",
|
|
42
|
+
userFile: null,
|
|
43
|
+
channels: [],
|
|
44
|
+
...overrides,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
afterEach(() => {
|
|
49
|
+
searchContactsResult = [];
|
|
50
|
+
searchContactsMock.mockClear();
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe("global-search contacts recency source", () => {
|
|
54
|
+
test("surfaces contacts.updatedAt as lastInteraction, not the channel column", async () => {
|
|
55
|
+
searchContactsResult = [
|
|
56
|
+
makeContact({
|
|
57
|
+
id: "ct_1",
|
|
58
|
+
displayName: "Alice",
|
|
59
|
+
updatedAt: 5000,
|
|
60
|
+
lastInteraction: 9999,
|
|
61
|
+
}),
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
const result = (await handler({
|
|
65
|
+
queryParams: { q: "ali", categories: "contacts" },
|
|
66
|
+
})) as { results: { contacts: { id: string; lastInteraction: number }[] } };
|
|
67
|
+
|
|
68
|
+
expect(result.results.contacts).toHaveLength(1);
|
|
69
|
+
expect(result.results.contacts[0].lastInteraction).toBe(5000);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("preserves searchContacts ordering deterministically", async () => {
|
|
73
|
+
searchContactsResult = [
|
|
74
|
+
makeContact({ id: "ct_a", displayName: "A", updatedAt: 300 }),
|
|
75
|
+
makeContact({ id: "ct_b", displayName: "B", updatedAt: 200 }),
|
|
76
|
+
makeContact({ id: "ct_c", displayName: "C", updatedAt: 100 }),
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
const result = (await handler({
|
|
80
|
+
queryParams: { q: "x", categories: "contacts" },
|
|
81
|
+
})) as { results: { contacts: { id: string; lastInteraction: number }[] } };
|
|
82
|
+
|
|
83
|
+
expect(result.results.contacts.map((c) => c.id)).toEqual([
|
|
84
|
+
"ct_a",
|
|
85
|
+
"ct_b",
|
|
86
|
+
"ct_c",
|
|
87
|
+
]);
|
|
88
|
+
expect(result.results.contacts.map((c) => c.lastInteraction)).toEqual([
|
|
89
|
+
300, 200, 100,
|
|
90
|
+
]);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression tests for actor/principal header sanitization in the HTTP
|
|
3
|
+
* adapter.
|
|
4
|
+
*
|
|
5
|
+
* The HTTP adapter must derive the actor identity headers
|
|
6
|
+
* (`x-vellum-actor-principal-id`, `x-vellum-principal-type`) exclusively from
|
|
7
|
+
* the verified AuthContext, never from caller-supplied request headers. A
|
|
8
|
+
* caller whose token carries no actorPrincipalId (svc_gateway / svc_daemon /
|
|
9
|
+
* local principals) must not be able to spoof another principal — e.g. the
|
|
10
|
+
* guardian — by setting the header explicitly. This protects principal-gated
|
|
11
|
+
* handlers (surface action `apr:*` guardian decisions, guardian actions, host
|
|
12
|
+
* proxies) from approval-token bypass via a forged actor principal.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { describe, expect, test } from "bun:test";
|
|
16
|
+
|
|
17
|
+
import { resolveScopeProfile } from "../../auth/scopes.js";
|
|
18
|
+
import type { AuthContext } from "../../auth/types.js";
|
|
19
|
+
import { routeDefinitionsToHTTPRoutes } from "../http-adapter.js";
|
|
20
|
+
import type { RouteDefinition } from "../types.js";
|
|
21
|
+
|
|
22
|
+
// Echo route: returns the identity headers the handler actually observed.
|
|
23
|
+
const ECHO_ROUTE: RouteDefinition = {
|
|
24
|
+
operationId: "echo_identity_headers",
|
|
25
|
+
endpoint: "test/echo-identity",
|
|
26
|
+
method: "POST",
|
|
27
|
+
policy: null,
|
|
28
|
+
handler: ({ headers }) => ({
|
|
29
|
+
actorPrincipalId: headers?.["x-vellum-actor-principal-id"] ?? null,
|
|
30
|
+
principalType: headers?.["x-vellum-principal-type"] ?? null,
|
|
31
|
+
}),
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
function buildAuthContext(overrides: Partial<AuthContext>): AuthContext {
|
|
35
|
+
return {
|
|
36
|
+
subject: "svc:gateway:self",
|
|
37
|
+
principalType: "svc_gateway",
|
|
38
|
+
assistantId: "self",
|
|
39
|
+
scopeProfile: "gateway_service_v1",
|
|
40
|
+
scopes: resolveScopeProfile("gateway_service_v1"),
|
|
41
|
+
policyEpoch: 0,
|
|
42
|
+
...overrides,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async function invokeEcho(params: {
|
|
47
|
+
spoofedPrincipalId?: string;
|
|
48
|
+
spoofedPrincipalType?: string;
|
|
49
|
+
authContext: AuthContext;
|
|
50
|
+
}): Promise<{ actorPrincipalId: string | null; principalType: string | null }> {
|
|
51
|
+
const [httpRoute] = routeDefinitionsToHTTPRoutes([ECHO_ROUTE]);
|
|
52
|
+
|
|
53
|
+
const reqHeaders: Record<string, string> = {
|
|
54
|
+
"content-type": "application/json",
|
|
55
|
+
};
|
|
56
|
+
if (params.spoofedPrincipalId !== undefined) {
|
|
57
|
+
reqHeaders["x-vellum-actor-principal-id"] = params.spoofedPrincipalId;
|
|
58
|
+
}
|
|
59
|
+
if (params.spoofedPrincipalType !== undefined) {
|
|
60
|
+
reqHeaders["x-vellum-principal-type"] = params.spoofedPrincipalType;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const req = new Request("http://daemon.local/v1/test/echo-identity", {
|
|
64
|
+
method: "POST",
|
|
65
|
+
headers: reqHeaders,
|
|
66
|
+
body: JSON.stringify({}),
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
const response = await httpRoute.handler({
|
|
70
|
+
req,
|
|
71
|
+
url: new URL(req.url),
|
|
72
|
+
// The echo handler doesn't touch `server`; a stub is fine.
|
|
73
|
+
server: undefined as never,
|
|
74
|
+
authContext: params.authContext,
|
|
75
|
+
params: {},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
return (await response.json()) as {
|
|
79
|
+
actorPrincipalId: string | null;
|
|
80
|
+
principalType: string | null;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
describe("http-adapter actor/principal header sanitization", () => {
|
|
85
|
+
test("drops a spoofed actor-principal header when the context has no actorPrincipalId", async () => {
|
|
86
|
+
// A service token (svc_gateway) has no actorPrincipalId. The forged header
|
|
87
|
+
// must be stripped, not passed through to the handler.
|
|
88
|
+
const result = await invokeEcho({
|
|
89
|
+
spoofedPrincipalId: "guardian-principal-victim",
|
|
90
|
+
authContext: buildAuthContext({ actorPrincipalId: undefined }),
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
expect(result.actorPrincipalId).toBeNull();
|
|
94
|
+
// principalType is always re-derived from the verified context.
|
|
95
|
+
expect(result.principalType).toBe("svc_gateway");
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("overrides a spoofed actor-principal header with the verified context value", async () => {
|
|
99
|
+
const result = await invokeEcho({
|
|
100
|
+
spoofedPrincipalId: "guardian-principal-victim",
|
|
101
|
+
spoofedPrincipalType: "actor",
|
|
102
|
+
authContext: buildAuthContext({
|
|
103
|
+
subject: "actor:self:real-actor",
|
|
104
|
+
principalType: "actor",
|
|
105
|
+
scopeProfile: "actor_client_v1",
|
|
106
|
+
scopes: resolveScopeProfile("actor_client_v1"),
|
|
107
|
+
actorPrincipalId: "real-actor",
|
|
108
|
+
}),
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
expect(result.actorPrincipalId).toBe("real-actor");
|
|
112
|
+
expect(result.principalType).toBe("actor");
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("passes through the genuine actor identity when no spoof is present", async () => {
|
|
116
|
+
const result = await invokeEcho({
|
|
117
|
+
authContext: buildAuthContext({
|
|
118
|
+
subject: "actor:self:real-actor",
|
|
119
|
+
principalType: "actor",
|
|
120
|
+
scopeProfile: "actor_client_v1",
|
|
121
|
+
scopes: resolveScopeProfile("actor_client_v1"),
|
|
122
|
+
actorPrincipalId: "real-actor",
|
|
123
|
+
}),
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
expect(result.actorPrincipalId).toBe("real-actor");
|
|
127
|
+
expect(result.principalType).toBe("actor");
|
|
128
|
+
});
|
|
129
|
+
});
|
|
@@ -25,6 +25,7 @@ interface StubConversation {
|
|
|
25
25
|
handleSurfaceActionThrows?: Error;
|
|
26
26
|
handleSurfaceUndoCalled?: boolean;
|
|
27
27
|
handleSurfaceUndoThrows?: Error;
|
|
28
|
+
trustContext?: { trustClass: string; sourceChannel: string };
|
|
28
29
|
surfaceActionCalls: Array<{
|
|
29
30
|
surfaceId: string;
|
|
30
31
|
actionId: string;
|
|
@@ -43,6 +44,48 @@ const findBySurfaceCalls: string[] = [];
|
|
|
43
44
|
const getOrCreateCalls: string[] = [];
|
|
44
45
|
const rawGetCalls: Array<{ sql: string; params: unknown[] }> = [];
|
|
45
46
|
|
|
47
|
+
// Gateway guardian-delivery list (shared by the route's dev-bypass lookup and
|
|
48
|
+
// the local-principal-trust mapper): null = unreachable, [] = no guardian.
|
|
49
|
+
let mockGuardianList: Array<Record<string, unknown>> | null = [];
|
|
50
|
+
let httpAuthDisabled = false;
|
|
51
|
+
|
|
52
|
+
// Stub for the shared reset-drift helper. The route under test only consumes
|
|
53
|
+
// its result (a guardian TrustContext or null); the gate itself is covered in
|
|
54
|
+
// runtime/__tests__/guardian-vellum-migration.test.ts. Tests set
|
|
55
|
+
// `mockReResolve` per case and read `reResolveCalls` to assert routing.
|
|
56
|
+
const reResolveCalls: string[] = [];
|
|
57
|
+
let mockReResolve: { trustClass: string; sourceChannel: string } | null = null;
|
|
58
|
+
|
|
59
|
+
mock.module("../../../contacts/guardian-delivery-reader.js", () => ({
|
|
60
|
+
getGuardianDelivery: (_input?: { channelTypes?: string[] }) =>
|
|
61
|
+
Promise.resolve(mockGuardianList),
|
|
62
|
+
peekCachedGuardianDelivery: () => mockGuardianList ?? undefined,
|
|
63
|
+
guardianForChannel: (
|
|
64
|
+
list: Array<Record<string, unknown>>,
|
|
65
|
+
channelType: string,
|
|
66
|
+
) => list.find((g) => g.channelType === channelType && g.status === "active"),
|
|
67
|
+
}));
|
|
68
|
+
|
|
69
|
+
// Member ACL rides on memberRecord via the member-verdict cache; no local
|
|
70
|
+
// contact here.
|
|
71
|
+
mock.module("../../../contacts/contact-store.js", () => ({
|
|
72
|
+
findContactByAddress: () => null,
|
|
73
|
+
}));
|
|
74
|
+
|
|
75
|
+
mock.module("../../../config/env.js", () => ({
|
|
76
|
+
isHttpAuthDisabled: () => httpAuthDisabled,
|
|
77
|
+
}));
|
|
78
|
+
|
|
79
|
+
mock.module("../../guardian-vellum-migration.js", () => ({
|
|
80
|
+
reResolveTrustOnResetDrift: async (
|
|
81
|
+
incomingPrincipalId: string,
|
|
82
|
+
_sourceChannel: string,
|
|
83
|
+
) => {
|
|
84
|
+
reResolveCalls.push(incomingPrincipalId);
|
|
85
|
+
return mockReResolve;
|
|
86
|
+
},
|
|
87
|
+
}));
|
|
88
|
+
|
|
46
89
|
mock.module("../../../daemon/conversation-registry.js", () => ({
|
|
47
90
|
findConversation: (id: string) => {
|
|
48
91
|
findConvCalls.push(id);
|
|
@@ -122,6 +165,9 @@ function makeStub(id: string): StubConversation {
|
|
|
122
165
|
stub.handleSurfaceUndoCalled = true;
|
|
123
166
|
if (stub.handleSurfaceUndoThrows) throw stub.handleSurfaceUndoThrows;
|
|
124
167
|
},
|
|
168
|
+
setTrustContext: (ctx: { trustClass: string; sourceChannel: string }) => {
|
|
169
|
+
stub.trustContext = ctx;
|
|
170
|
+
},
|
|
125
171
|
});
|
|
126
172
|
return stub;
|
|
127
173
|
}
|
|
@@ -139,6 +185,10 @@ beforeEach(() => {
|
|
|
139
185
|
findBySurfaceCalls.length = 0;
|
|
140
186
|
getOrCreateCalls.length = 0;
|
|
141
187
|
rawGetCalls.length = 0;
|
|
188
|
+
mockGuardianList = [];
|
|
189
|
+
httpAuthDisabled = false;
|
|
190
|
+
reResolveCalls.length = 0;
|
|
191
|
+
mockReResolve = null;
|
|
142
192
|
});
|
|
143
193
|
|
|
144
194
|
// ---------------------------------------------------------------------------
|
|
@@ -337,6 +387,30 @@ describe("triggerSurfaceAction handler", () => {
|
|
|
337
387
|
]);
|
|
338
388
|
});
|
|
339
389
|
|
|
390
|
+
test("resolves dev-bypass to the guardian principal before threading the turn", async () => {
|
|
391
|
+
httpAuthDisabled = true;
|
|
392
|
+
mockGuardianList = [guardianDelivery(GUARDIAN_PRINCIPAL)];
|
|
393
|
+
const live = makeStub("conv-dev-thread");
|
|
394
|
+
memoryBySurface = live;
|
|
395
|
+
|
|
396
|
+
const handler = findHandler("triggerSurfaceAction");
|
|
397
|
+
await handler({
|
|
398
|
+
body: { surfaceId: "surf-dt", actionId: "act-dt" },
|
|
399
|
+
headers: { "x-vellum-actor-principal-id": "dev-bypass" },
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
// dev-bypass is translated so the surface turn matches the SSE host-proxy
|
|
403
|
+
// client's registered guardian principal (CU/app-control same-actor check).
|
|
404
|
+
expect(live.surfaceActionCalls).toEqual([
|
|
405
|
+
{
|
|
406
|
+
surfaceId: "surf-dt",
|
|
407
|
+
actionId: "act-dt",
|
|
408
|
+
data: undefined,
|
|
409
|
+
sourceActorPrincipalId: GUARDIAN_PRINCIPAL,
|
|
410
|
+
},
|
|
411
|
+
]);
|
|
412
|
+
});
|
|
413
|
+
|
|
340
414
|
test("propagates accepted=false rejection as BadRequestError", async () => {
|
|
341
415
|
const live = makeStub("conv-reject");
|
|
342
416
|
live.handleSurfaceActionResult = {
|
|
@@ -362,6 +436,120 @@ describe("triggerSurfaceAction handler", () => {
|
|
|
362
436
|
});
|
|
363
437
|
});
|
|
364
438
|
|
|
439
|
+
// ---------------------------------------------------------------------------
|
|
440
|
+
// Trust context resolution
|
|
441
|
+
// ---------------------------------------------------------------------------
|
|
442
|
+
|
|
443
|
+
const GUARDIAN_PRINCIPAL = "principal-guardian";
|
|
444
|
+
// Daemon-minted vellum-principal-* ids: the DB-reset drift signature. The
|
|
445
|
+
// gateway rebinds to a fresh id while the client still holds a JWT for the old.
|
|
446
|
+
const VELLUM_PRINCIPAL_OLD = "vellum-principal-old";
|
|
447
|
+
const VELLUM_PRINCIPAL_NEW = "vellum-principal-new";
|
|
448
|
+
|
|
449
|
+
function guardianDelivery(principalId: string): Record<string, unknown> {
|
|
450
|
+
return {
|
|
451
|
+
channelType: "vellum",
|
|
452
|
+
contactId: "contact-1",
|
|
453
|
+
principalId,
|
|
454
|
+
address: "guardian-address",
|
|
455
|
+
externalChatId: "guardian-chat",
|
|
456
|
+
status: "active",
|
|
457
|
+
};
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// A guardian TrustContext the stubbed helper hands back on a recovered drift.
|
|
461
|
+
function guardianCtx(): { trustClass: string; sourceChannel: string } {
|
|
462
|
+
return { trustClass: "guardian", sourceChannel: "vellum" };
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
describe("triggerSurfaceAction trust context", () => {
|
|
466
|
+
test("guardian principal → guardian from the gateway binding, helper not called", async () => {
|
|
467
|
+
mockGuardianList = [guardianDelivery(GUARDIAN_PRINCIPAL)];
|
|
468
|
+
const live = makeStub("conv-guardian");
|
|
469
|
+
memoryBySurface = live;
|
|
470
|
+
|
|
471
|
+
const handler = findHandler("triggerSurfaceAction");
|
|
472
|
+
await handler({
|
|
473
|
+
body: { surfaceId: "surf-g", actionId: "act-g" },
|
|
474
|
+
headers: { "x-vellum-actor-principal-id": GUARDIAN_PRINCIPAL },
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
expect(live.trustContext?.trustClass).toBe("guardian");
|
|
478
|
+
expect(live.trustContext?.sourceChannel).toBe("vellum");
|
|
479
|
+
// First-pass resolve already granted guardian, so the drift helper is skipped.
|
|
480
|
+
expect(reResolveCalls).toEqual([]);
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
test("unknown principal: helper consulted, null result stays unknown (fail closed)", async () => {
|
|
484
|
+
mockGuardianList = [guardianDelivery(GUARDIAN_PRINCIPAL)];
|
|
485
|
+
mockReResolve = null;
|
|
486
|
+
const live = makeStub("conv-unknown");
|
|
487
|
+
memoryBySurface = live;
|
|
488
|
+
|
|
489
|
+
const handler = findHandler("triggerSurfaceAction");
|
|
490
|
+
await handler({
|
|
491
|
+
body: { surfaceId: "surf-u", actionId: "act-u" },
|
|
492
|
+
headers: { "x-vellum-actor-principal-id": VELLUM_PRINCIPAL_OLD },
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
expect(reResolveCalls).toEqual([VELLUM_PRINCIPAL_OLD]);
|
|
496
|
+
expect(live.trustContext?.trustClass).toBe("unknown");
|
|
497
|
+
});
|
|
498
|
+
|
|
499
|
+
test("reset drift: helper returns guardian → route adopts it", async () => {
|
|
500
|
+
mockGuardianList = [guardianDelivery(VELLUM_PRINCIPAL_NEW)];
|
|
501
|
+
mockReResolve = guardianCtx();
|
|
502
|
+
const live = makeStub("conv-drift");
|
|
503
|
+
memoryBySurface = live;
|
|
504
|
+
|
|
505
|
+
const handler = findHandler("triggerSurfaceAction");
|
|
506
|
+
await handler({
|
|
507
|
+
body: { surfaceId: "surf-d", actionId: "act-d" },
|
|
508
|
+
headers: { "x-vellum-actor-principal-id": VELLUM_PRINCIPAL_OLD },
|
|
509
|
+
});
|
|
510
|
+
|
|
511
|
+
expect(reResolveCalls).toEqual([VELLUM_PRINCIPAL_OLD]);
|
|
512
|
+
expect(live.trustContext?.trustClass).toBe("guardian");
|
|
513
|
+
});
|
|
514
|
+
|
|
515
|
+
test("dev-bypass resolves the real guardian principal from the gateway, helper not called", async () => {
|
|
516
|
+
httpAuthDisabled = true;
|
|
517
|
+
mockGuardianList = [guardianDelivery(GUARDIAN_PRINCIPAL)];
|
|
518
|
+
const live = makeStub("conv-dev");
|
|
519
|
+
memoryBySurface = live;
|
|
520
|
+
|
|
521
|
+
const handler = findHandler("triggerSurfaceAction");
|
|
522
|
+
await handler({
|
|
523
|
+
body: { surfaceId: "surf-dev", actionId: "act-dev" },
|
|
524
|
+
headers: { "x-vellum-actor-principal-id": "dev-bypass" },
|
|
525
|
+
});
|
|
526
|
+
|
|
527
|
+
// The synthetic dev-bypass principal is translated to the real guardian,
|
|
528
|
+
// yielding a guardian trust context without consulting the drift helper.
|
|
529
|
+
expect(live.trustContext?.trustClass).toBe("guardian");
|
|
530
|
+
expect(reResolveCalls).toEqual([]);
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
test("dev-bypass with an empty gateway: helper null result → unknown (fail closed)", async () => {
|
|
534
|
+
httpAuthDisabled = true;
|
|
535
|
+
// The gateway has no active binding, so dev-bypass cannot translate to a
|
|
536
|
+
// real guardian; the first-pass resolve is unknown and the helper, returning
|
|
537
|
+
// null, leaves trust unknown.
|
|
538
|
+
mockGuardianList = [];
|
|
539
|
+
mockReResolve = null;
|
|
540
|
+
const live = makeStub("conv-dev-fallback");
|
|
541
|
+
memoryBySurface = live;
|
|
542
|
+
|
|
543
|
+
const handler = findHandler("triggerSurfaceAction");
|
|
544
|
+
await handler({
|
|
545
|
+
body: { surfaceId: "surf-devf", actionId: "act-devf" },
|
|
546
|
+
headers: { "x-vellum-actor-principal-id": "dev-bypass" },
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
expect(live.trustContext?.trustClass).toBe("unknown");
|
|
550
|
+
});
|
|
551
|
+
});
|
|
552
|
+
|
|
365
553
|
// ---------------------------------------------------------------------------
|
|
366
554
|
// undoSurfaceAction
|
|
367
555
|
// ---------------------------------------------------------------------------
|
|
@@ -92,7 +92,7 @@ async function handleBrowserExecute({
|
|
|
92
92
|
// register with (dev-bypass otherwise mismatches).
|
|
93
93
|
const headerActor =
|
|
94
94
|
headers["x-vellum-actor-principal-id"]?.trim() || undefined;
|
|
95
|
-
const sourceActorPrincipalId = resolveActorPrincipalIdForLocalGuardian(
|
|
95
|
+
const sourceActorPrincipalId = await resolveActorPrincipalIdForLocalGuardian(
|
|
96
96
|
conversation?.getTurnActorPrincipalId() ?? headerActor,
|
|
97
97
|
);
|
|
98
98
|
|
|
@@ -8,13 +8,16 @@
|
|
|
8
8
|
* requester.
|
|
9
9
|
*
|
|
10
10
|
* This sweep operates on the unified canonical domain
|
|
11
|
-
* (`canonical_guardian_requests`)
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* (`canonical_guardian_requests`). On expiry it transitions the request status
|
|
12
|
+
* (the single source of truth), withdraws the approval cards on every surface,
|
|
13
|
+
* notifies the requester that their request expired, and releases any in-memory
|
|
14
|
+
* pending interaction. Requester notices are delivered straight to the
|
|
15
|
+
* requester's channel — not the guardian-facing notification pipeline — and the
|
|
16
|
+
* guardian stays passive, since the withdrawn card already reflects expiry.
|
|
15
17
|
*/
|
|
16
18
|
|
|
17
19
|
import { withdrawGuardianRequestCards } from "../../approvals/guardian-card-withdrawal.js";
|
|
20
|
+
import { notifyExpiredGuardianRequest } from "../../approvals/guardian-expiry-notifier.js";
|
|
18
21
|
import {
|
|
19
22
|
listCanonicalGuardianRequests,
|
|
20
23
|
resolveCanonicalGuardianRequest,
|
|
@@ -39,7 +42,7 @@ let sweepInProgress = false;
|
|
|
39
42
|
* concurrent decision that wins the race is never overwritten by the
|
|
40
43
|
* sweep. Returns the count of requests transitioned to expired.
|
|
41
44
|
*/
|
|
42
|
-
async function sweepExpiredCanonicalGuardianRequests(): Promise<number> {
|
|
45
|
+
export async function sweepExpiredCanonicalGuardianRequests(): Promise<number> {
|
|
43
46
|
const pending = listCanonicalGuardianRequests({ status: "pending" });
|
|
44
47
|
const now = Date.now();
|
|
45
48
|
let expiredCount = 0;
|
|
@@ -76,6 +79,11 @@ async function sweepExpiredCanonicalGuardianRequests(): Promise<number> {
|
|
|
76
79
|
request: resolved,
|
|
77
80
|
status: "expired",
|
|
78
81
|
});
|
|
82
|
+
|
|
83
|
+
// Notify the requester their request expired and release any in-memory
|
|
84
|
+
// pending interaction. Best-effort and non-throwing, like the card
|
|
85
|
+
// withdrawal above.
|
|
86
|
+
await notifyExpiredGuardianRequest(resolved);
|
|
79
87
|
}
|
|
80
88
|
}
|
|
81
89
|
|
|
@@ -179,7 +179,7 @@ export async function handleCreateVerificationSession({
|
|
|
179
179
|
}
|
|
180
180
|
|
|
181
181
|
// Inbound challenge path
|
|
182
|
-
const result = createInboundChallenge(channel, rebind, conversationId);
|
|
182
|
+
const result = await createInboundChallenge(channel, rebind, conversationId);
|
|
183
183
|
if (!result.success) {
|
|
184
184
|
throw new BadRequestError(
|
|
185
185
|
(result as { message?: string }).message ??
|
|
@@ -192,12 +192,12 @@ export async function handleCreateVerificationSession({
|
|
|
192
192
|
/**
|
|
193
193
|
* GET /v1/channel-verification-sessions/status
|
|
194
194
|
*/
|
|
195
|
-
function handleGetVerificationStatus({
|
|
195
|
+
async function handleGetVerificationStatus({
|
|
196
196
|
queryParams = {},
|
|
197
197
|
body = {},
|
|
198
198
|
}: RouteHandlerArgs) {
|
|
199
199
|
const channel = (queryParams.channel ?? (body as Record<string, unknown>).channel) as ChannelId | undefined;
|
|
200
|
-
return getVerificationStatus(channel);
|
|
200
|
+
return await getVerificationStatus(channel);
|
|
201
201
|
}
|
|
202
202
|
|
|
203
203
|
/**
|