@vellumai/assistant 0.9.1-staging.1 → 0.10.0-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/activation-funnel-telemetry.md +24 -18
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +15 -0
- package/openapi.yaml +852 -15
- package/package.json +1 -1
- package/src/__tests__/access-request-card-view.test.ts +98 -0
- package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +59 -7
- package/src/__tests__/agent-loop-compaction-strip.test.ts +17 -16
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
- package/src/__tests__/app-compiler.test.ts +15 -1
- package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
- package/src/__tests__/call-pointer-messages.test.ts +28 -0
- package/src/__tests__/cancel-clears-processing.test.ts +89 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -4
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
- package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
- package/src/__tests__/config-loader-backfill.test.ts +174 -30
- package/src/__tests__/config-schema.test.ts +35 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +0 -6
- package/src/__tests__/contacts-tools.test.ts +29 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +58 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-lifecycle.test.ts +7 -9
- package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
- package/src/__tests__/conversation-title-service.test.ts +62 -0
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
- package/src/__tests__/credential-prompt-route.test.ts +1 -0
- package/src/__tests__/credential-security-invariants.test.ts +2 -0
- package/src/__tests__/disk-pressure-policy.test.ts +12 -0
- package/src/__tests__/disk-usage.test.ts +65 -0
- package/src/__tests__/dynamic-page-surface.test.ts +51 -0
- package/src/__tests__/gateway-flag-listener.test.ts +110 -1
- package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
- package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
- package/src/__tests__/guardian-grant-minting.test.ts +3 -35
- package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
- package/src/__tests__/guardian-routing-state.test.ts +0 -1
- package/src/__tests__/headless-browser-mode.test.ts +10 -0
- package/src/__tests__/headless-browser-navigate.test.ts +8 -3
- package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
- package/src/__tests__/host-browser-proxy.test.ts +87 -0
- package/src/__tests__/injector-v3-suppression.test.ts +27 -20
- package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
- package/src/__tests__/invite-redemption-service.test.ts +0 -3
- package/src/__tests__/llm-catalog-parity.test.ts +30 -1
- package/src/__tests__/llm-resolver.test.ts +21 -0
- package/src/__tests__/llm-schema.test.ts +1 -0
- package/src/__tests__/managed-profile-guard.test.ts +163 -4
- package/src/__tests__/mcp-health-check.test.ts +6 -7
- package/src/__tests__/media-stream-server-integration.test.ts +317 -13
- package/src/__tests__/path-policy.test.ts +34 -0
- package/src/__tests__/persona-resolver.test.ts +38 -0
- package/src/__tests__/plugin-api-provider.test.ts +24 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
- package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
- package/src/__tests__/reaction-persistence.test.ts +150 -29
- package/src/__tests__/relay-server.test.ts +285 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
- package/src/__tests__/skill-execute-input.test.ts +5 -0
- package/src/__tests__/skills.test.ts +51 -0
- package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
- package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
- package/src/__tests__/subagent-tools.test.ts +150 -0
- package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
- package/src/__tests__/title-generate-hook.test.ts +100 -3
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
- package/src/__tests__/tool-audit-listener.test.ts +7 -7
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
- package/src/__tests__/trusted-contact-verification.test.ts +2 -4
- package/src/__tests__/twilio-routes.test.ts +81 -1
- package/src/__tests__/voice-invite-redemption.test.ts +0 -1
- package/src/__tests__/weak-open-model.test.ts +30 -0
- package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
- package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
- package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
- package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
- package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
- package/src/agent/loop.ts +33 -33
- package/src/api/events/tool-result.ts +6 -0
- package/src/api/events/workflow-completed.ts +53 -0
- package/src/api/events/workflow-leaf-finished.ts +38 -0
- package/src/api/events/workflow-leaf-started.ts +35 -0
- package/src/api/events/workflow-progress.ts +32 -0
- package/src/api/events/workflow-started.ts +31 -0
- package/src/api/index.ts +40 -0
- package/src/api/responses/conversation-message.ts +26 -0
- package/src/api/responses/home.ts +26 -0
- package/src/api/responses/workflow-journal.ts +53 -0
- package/src/approvals/guardian-card-withdrawal.ts +145 -0
- package/src/approvals/guardian-decision-primitive.ts +26 -3
- package/src/approvals/guardian-request-resolvers.ts +181 -78
- package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
- package/src/calls/call-pointer-messages.ts +10 -4
- package/src/calls/channel-admission-reader.ts +104 -0
- package/src/calls/guardian-dispatch.ts +17 -45
- package/src/calls/media-stream-server.ts +84 -2
- package/src/calls/relay-server.ts +66 -0
- package/src/calls/relay-setup-router.ts +82 -1
- package/src/calls/twilio-routes.ts +17 -8
- package/src/cli/commands/clients.ts +3 -0
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
- package/src/cli/commands/memory/index.ts +30 -0
- package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
- package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
- package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
- package/src/cli/commands/oauth/status.test.ts +36 -0
- package/src/cli/commands/oauth/status.ts +23 -3
- package/src/cli/commands/plugins.ts +57 -5
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +134 -4
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +53 -11
- package/src/cli/lib/inspect-plugin.ts +12 -1
- package/src/cli/lib/merge-plugin-tree.ts +149 -49
- package/src/cli/lib/plugin-surfaces.ts +104 -0
- package/src/cli/lib/upgrade-plugin.ts +64 -36
- package/src/cli/program.ts +2 -4
- package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
- package/src/config/assistant-feature-flags.ts +22 -7
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
- package/src/config/bundled-skills/messaging/SKILL.md +6 -4
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
- package/src/config/bundled-skills/workflows/SKILL.md +14 -7
- package/src/config/call-site-defaults.ts +3 -0
- package/src/config/feature-flag-registry.json +49 -18
- package/src/config/llm-resolver.ts +3 -0
- package/src/config/memory-v3-gate.ts +11 -0
- package/src/config/schema.ts +8 -6
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/channels.ts +11 -0
- package/src/config/schemas/llm.ts +31 -0
- package/src/config/schemas/memory-lifecycle.ts +3 -7
- package/src/config/schemas/memory-v3.ts +6 -0
- package/src/config/schemas/services.ts +18 -0
- package/src/config/seed-inference-profiles.ts +94 -34
- package/src/config/skills.ts +21 -0
- package/src/config/sync-gated-profiles.ts +220 -0
- package/src/contacts/contact-store.ts +2 -10
- package/src/contacts/contacts-write.ts +1 -2
- package/src/contacts/types.ts +0 -1
- package/src/context/compactor.ts +86 -52
- package/src/context/strip-injections.ts +58 -10
- package/src/context/token-estimator.ts +1 -1
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
- package/src/daemon/conversation-agent-loop.ts +100 -19
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +3 -5
- package/src/daemon/conversation-process.ts +13 -5
- package/src/daemon/conversation-runtime-assembly.ts +13 -15
- package/src/daemon/conversation-surfaces.ts +26 -0
- package/src/daemon/conversation-tool-setup.ts +16 -11
- package/src/daemon/conversation.ts +64 -14
- package/src/daemon/disk-pressure-policy.ts +5 -3
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
- package/src/daemon/handlers/config-a2a.ts +0 -2
- package/src/daemon/handlers/config-channels.ts +5 -10
- package/src/daemon/handlers/config-slack-channel.ts +20 -0
- package/src/daemon/handlers/conversations.ts +107 -0
- package/src/daemon/host-browser-proxy.ts +41 -0
- package/src/daemon/lifecycle.ts +55 -20
- package/src/daemon/message-provenance.ts +2 -0
- package/src/daemon/message-types/contacts.ts +0 -1
- package/src/daemon/message-types/web-activity.ts +7 -1
- package/src/daemon/message-types/workflows.ts +83 -1
- package/src/daemon/tool-setup-types.ts +4 -0
- package/src/daemon/trust-context.ts +1 -1
- package/src/events/tool-audit-listener.ts +2 -2
- package/src/home/feed-source-enrichment.test.ts +151 -0
- package/src/home/feed-source-enrichment.ts +176 -0
- package/src/instrument.ts +18 -6
- package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
- package/src/ipc/assistant-server.ts +37 -4
- package/src/ipc/gateway-flag-listener.ts +18 -2
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
- package/src/memory/__tests__/memory-retrospective-job.test.ts +34 -0
- package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
- package/src/memory/auth-fallback-events-store.ts +2 -2
- package/src/memory/auto-analysis-enqueue.ts +3 -5
- package/src/memory/canonical-guardian-store.ts +39 -1
- package/src/memory/conversation-crud.ts +9 -4
- package/src/memory/conversation-key-store.ts +17 -2
- package/src/memory/conversation-title-service.ts +64 -7
- package/src/memory/db-init.ts +10 -0
- package/src/memory/embedding-backend.ts +15 -1
- package/src/memory/jobs-worker.ts +2 -1
- package/src/memory/lifecycle-events-store.ts +2 -2
- package/src/memory/memory-retrospective-enqueue.ts +31 -6
- package/src/memory/memory-retrospective-job.ts +9 -0
- package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
- package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
- package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +10 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
- package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
- package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
- package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
- package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
- package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +30 -0
- package/src/memory/migrations/index.ts +5 -0
- package/src/memory/onboarding-events-store.ts +3 -3
- package/src/memory/schema/contacts.ts +0 -1
- package/src/memory/skill-loaded-events-store.test.ts +7 -15
- package/src/memory/skill-loaded-events-store.ts +2 -2
- package/src/memory/tool-executed-events-store.test.ts +7 -7
- package/src/memory/turn-trace-store.test.ts +736 -0
- package/src/memory/turn-trace-store.ts +364 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
- package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
- package/src/memory/v2/consolidation-job.ts +2 -2
- package/src/memory/v2/skill-content.ts +25 -7
- package/src/memory/v2/skill-store.ts +7 -1
- package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
- package/src/memory/v3-eval/eval-packets.ts +546 -0
- package/src/messaging/providers/slack/api.ts +31 -0
- package/src/messaging/providers/slack/send.test.ts +114 -2
- package/src/messaging/providers/slack/send.ts +30 -7
- package/src/messaging/providers/slack/withdraw.test.ts +200 -0
- package/src/messaging/providers/slack/withdraw.ts +161 -0
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/access-request-copy.ts +72 -59
- package/src/notifications/adapters/slack.ts +55 -73
- package/src/notifications/approval-card-data.ts +333 -0
- package/src/notifications/broadcaster.ts +6 -2
- package/src/notifications/canonical-delivery-recorder.ts +139 -0
- package/src/notifications/copy-composer.ts +3 -3
- package/src/notifications/decision-engine.ts +4 -2
- package/src/notifications/destination-resolver.ts +4 -6
- package/src/notifications/guardian-question-mode.ts +10 -0
- package/src/notifications/home-feed-side-effect.ts +3 -13
- package/src/notifications/notification-utils.ts +2 -1
- package/src/notifications/signal.ts +79 -43
- package/src/notifications/types.ts +98 -128
- package/src/permissions/checker.test.ts +51 -0
- package/src/permissions/checker.ts +185 -26
- package/src/permissions/ipc-risk-types.ts +24 -0
- package/src/permissions/question-prompter.test.ts +27 -0
- package/src/permissions/question-prompter.ts +4 -0
- package/src/platform/client.test.ts +119 -0
- package/src/platform/client.ts +66 -0
- package/src/platform/consent-cache.test.ts +267 -0
- package/src/platform/consent-cache.ts +174 -0
- package/src/plugin-api/index.ts +27 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
- package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
- package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
- package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
- package/src/plugins/defaults/advisor/config.ts +21 -0
- package/src/plugins/defaults/advisor/consult.ts +93 -0
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/advisor/package.json +14 -0
- package/src/plugins/defaults/advisor/steering.ts +67 -0
- package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
- package/src/plugins/defaults/advisor/transcript.ts +76 -0
- package/src/plugins/defaults/index.ts +35 -0
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +75 -7
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +37 -4
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +2 -12
- package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
- package/src/prompts/persona-resolver.ts +12 -2
- package/src/prompts/templates/system-sections.ts +7 -2
- package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
- package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
- package/src/providers/__tests__/retry-callsite.test.ts +176 -0
- package/src/providers/atlascloud/client.ts +85 -0
- package/src/providers/fetch-provider-catalog.ts +85 -0
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/model-catalog.ts +58 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
- package/src/providers/openai/chat-completions-provider.ts +7 -0
- package/src/providers/openai/responses-provider.ts +10 -0
- package/src/providers/provider-send-message.ts +11 -3
- package/src/providers/retry.ts +53 -12
- package/src/providers/search-provider-catalog.ts +10 -0
- package/src/providers/weak-open-model.ts +22 -0
- package/src/runtime/__tests__/agent-wake.test.ts +181 -0
- package/src/runtime/__tests__/client-health.test.ts +44 -0
- package/src/runtime/access-request-helper.ts +21 -53
- package/src/runtime/actor-trust-resolver.ts +49 -21
- package/src/runtime/agent-wake.ts +52 -0
- package/src/runtime/assistant-event-hub.ts +18 -4
- package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
- package/src/runtime/auth/require-bound-guardian.ts +1 -4
- package/src/runtime/capabilities.test.ts +120 -0
- package/src/runtime/capabilities.ts +197 -0
- package/src/runtime/channel-approval-types.ts +5 -1
- package/src/runtime/channel-retry-sweep.ts +1 -0
- package/src/runtime/channel-verification-service.ts +1 -2
- package/src/runtime/client-health.ts +26 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
- package/src/runtime/effective-capabilities.test.ts +128 -0
- package/src/runtime/effective-capabilities.ts +84 -0
- package/src/runtime/guardian-reply-router.ts +106 -21
- package/src/runtime/invite-redemption-service.ts +6 -22
- package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
- package/src/runtime/migrations/vbundle-builder.ts +49 -20
- package/src/runtime/pending-interactions.ts +15 -0
- package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +35 -13
- package/src/runtime/routes/browser-tabs-routes.ts +9 -0
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
- package/src/runtime/routes/client-routes.ts +10 -0
- package/src/runtime/routes/contact-routes.ts +31 -8
- package/src/runtime/routes/conversation-management-routes.ts +80 -1
- package/src/runtime/routes/conversation-query-routes.ts +68 -22
- package/src/runtime/routes/conversation-routes.ts +37 -12
- package/src/runtime/routes/events-routes.ts +1 -3
- package/src/runtime/routes/guardian-approval-interception.ts +14 -73
- package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
- package/src/runtime/routes/home-feed-routes.ts +8 -3
- package/src/runtime/routes/inbound-message-handler.ts +214 -228
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +88 -6
- package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
- package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
- package/src/runtime/routes/integrations/slack/channel.ts +36 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
- package/src/runtime/routes/mcp-auth-routes.ts +233 -41
- package/src/runtime/routes/memory-eval-routes.ts +87 -0
- package/src/runtime/routes/notification-routes.ts +122 -133
- package/src/runtime/routes/platform-routes.ts +2 -2
- package/src/runtime/routes/plugins-routes.ts +40 -7
- package/src/runtime/routes/secret-routes.ts +10 -0
- package/src/runtime/routes/surface-action-routes.ts +2 -1
- package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
- package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
- package/src/runtime/routes/workflow-routes.test.ts +225 -1
- package/src/runtime/routes/workflow-routes.ts +131 -1
- package/src/runtime/tool-grant-request-helper.ts +18 -16
- package/src/runtime/trust-context-resolver.ts +8 -5
- package/src/schedule/schedule-store.ts +1 -1
- package/src/schedule/scheduler-types.ts +5 -1
- package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
- package/src/security/secret-patterns.ts +3 -0
- package/src/subagent/manager.ts +11 -4
- package/src/telemetry/trace-collection-policy.test.ts +28 -0
- package/src/telemetry/trace-collection-policy.ts +30 -0
- package/src/telemetry/types.ts +89 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
- package/src/telemetry/usage-telemetry-reporter.ts +148 -41
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
- package/src/tools/browser/browser-execution.ts +29 -18
- package/src/tools/document/document-tool.ts +2 -3
- package/src/tools/executor.ts +5 -3
- package/src/tools/host-terminal/host-shell.ts +5 -4
- package/src/tools/memory/register.ts +2 -2
- package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
- package/src/tools/network/__tests__/web-search.test.ts +143 -0
- package/src/tools/network/web-fetch.ts +372 -1
- package/src/tools/network/web-search.ts +213 -10
- package/src/tools/permission-checker.ts +3 -2
- package/src/tools/registry.ts +20 -0
- package/src/tools/schedule/create.ts +4 -3
- package/src/tools/schedule/update.ts +2 -1
- package/src/tools/shared/filesystem/path-policy.ts +39 -13
- package/src/tools/skills/execute.ts +1 -2
- package/src/tools/subagent/spawn.ts +37 -13
- package/src/tools/terminal/shell.ts +10 -4
- package/src/tools/tool-approval-handler.ts +17 -10
- package/src/tools/types.ts +9 -0
- package/src/tools/ui-surface/definitions.ts +25 -2
- package/src/tools/verification-control-plane-policy.ts +3 -1
- package/src/tools/workflows/run-workflow.ts +1 -0
- package/src/util/disk-usage.ts +78 -23
- package/src/util/platform.ts +8 -1
- package/src/watcher/telemetry.ts +2 -2
- package/src/workflows/engine.test.ts +175 -1
- package/src/workflows/engine.ts +82 -0
- package/src/workflows/journal-store.test.ts +70 -0
- package/src/workflows/journal-store.ts +18 -3
- package/src/workflows/run-manager.test.ts +171 -3
- package/src/workflows/run-manager.ts +64 -0
- package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
- package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
- package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
- package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/notifications/tool-approval-copy.ts +0 -142
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import type { TrustClass } from "./actor-trust-resolver.js";
|
|
4
|
+
import { type CapabilitySet, resolveCapabilities } from "./capabilities.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The capability matrix — the single source of truth for what each trust class
|
|
8
|
+
* may do. If a call site's behavior changes during migration, it changes here
|
|
9
|
+
* (reviewed in one diff) instead of across the ~40 inline conditionals.
|
|
10
|
+
*/
|
|
11
|
+
const MATRIX: Record<TrustClass, CapabilitySet> = {
|
|
12
|
+
guardian: {
|
|
13
|
+
canSelfApproveTools: true,
|
|
14
|
+
sensitiveToolApproval: "self",
|
|
15
|
+
canManageSchedules: true,
|
|
16
|
+
canUseVerificationControlPlane: true,
|
|
17
|
+
canSelfAuthorizeArchiveBySender: true,
|
|
18
|
+
canAccessMemory: true,
|
|
19
|
+
canAccessPrivilegedDocuments: true,
|
|
20
|
+
canRunUnsandboxedShell: true,
|
|
21
|
+
mayBeInteractive: true,
|
|
22
|
+
canActUnderDiskPressureCleanup: true,
|
|
23
|
+
promptTrustGuidance: "none",
|
|
24
|
+
},
|
|
25
|
+
trusted_contact: {
|
|
26
|
+
canSelfApproveTools: false,
|
|
27
|
+
sensitiveToolApproval: "escalate-and-wait",
|
|
28
|
+
canManageSchedules: false,
|
|
29
|
+
canUseVerificationControlPlane: false,
|
|
30
|
+
canSelfAuthorizeArchiveBySender: false,
|
|
31
|
+
canAccessMemory: false,
|
|
32
|
+
canAccessPrivilegedDocuments: false,
|
|
33
|
+
canRunUnsandboxedShell: false,
|
|
34
|
+
mayBeInteractive: true,
|
|
35
|
+
canActUnderDiskPressureCleanup: false,
|
|
36
|
+
promptTrustGuidance: "social-engineering-defense",
|
|
37
|
+
},
|
|
38
|
+
unverified_contact: {
|
|
39
|
+
canSelfApproveTools: false,
|
|
40
|
+
sensitiveToolApproval: "escalate-and-wait",
|
|
41
|
+
canManageSchedules: false,
|
|
42
|
+
canUseVerificationControlPlane: false,
|
|
43
|
+
canSelfAuthorizeArchiveBySender: false,
|
|
44
|
+
canAccessMemory: false,
|
|
45
|
+
canAccessPrivilegedDocuments: false,
|
|
46
|
+
canRunUnsandboxedShell: false,
|
|
47
|
+
mayBeInteractive: true,
|
|
48
|
+
canActUnderDiskPressureCleanup: false,
|
|
49
|
+
promptTrustGuidance: "social-engineering-defense",
|
|
50
|
+
},
|
|
51
|
+
unknown: {
|
|
52
|
+
canSelfApproveTools: false,
|
|
53
|
+
sensitiveToolApproval: "deny",
|
|
54
|
+
canManageSchedules: false,
|
|
55
|
+
canUseVerificationControlPlane: false,
|
|
56
|
+
canSelfAuthorizeArchiveBySender: false,
|
|
57
|
+
canAccessMemory: false,
|
|
58
|
+
canAccessPrivilegedDocuments: false,
|
|
59
|
+
canRunUnsandboxedShell: false,
|
|
60
|
+
mayBeInteractive: false,
|
|
61
|
+
canActUnderDiskPressureCleanup: false,
|
|
62
|
+
promptTrustGuidance: "stranger-warning",
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
describe("resolveCapabilities", () => {
|
|
67
|
+
for (const trustClass of Object.keys(MATRIX) as TrustClass[]) {
|
|
68
|
+
test(`resolves the full capability set for "${trustClass}"`, () => {
|
|
69
|
+
expect(resolveCapabilities(trustClass)).toEqual(MATRIX[trustClass]);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
test("undefined fail-closes to the `unknown` capability set", () => {
|
|
74
|
+
expect(resolveCapabilities(undefined)).toEqual(MATRIX.unknown);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("an unrecognized/legacy string fail-closes to the `unknown` capability set", () => {
|
|
78
|
+
expect(resolveCapabilities("non_guardian")).toEqual(MATRIX.unknown);
|
|
79
|
+
expect(resolveCapabilities("some_future_role")).toEqual(MATRIX.unknown);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("inherited Object.prototype keys fail-closed (no prototype read)", () => {
|
|
83
|
+
for (const key of [
|
|
84
|
+
"__proto__",
|
|
85
|
+
"constructor",
|
|
86
|
+
"toString",
|
|
87
|
+
"hasOwnProperty",
|
|
88
|
+
]) {
|
|
89
|
+
expect(resolveCapabilities(key)).toEqual(MATRIX.unknown);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("unverified_contact is byte-for-byte identical to trusted_contact (admission-only distinction)", () => {
|
|
94
|
+
expect(resolveCapabilities("unverified_contact")).toEqual(
|
|
95
|
+
resolveCapabilities("trusted_contact"),
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("only guardian self-approves; only guardian/contacts may be interactive", () => {
|
|
100
|
+
expect(resolveCapabilities("guardian").canSelfApproveTools).toBe(true);
|
|
101
|
+
expect(resolveCapabilities("trusted_contact").canSelfApproveTools).toBe(
|
|
102
|
+
false,
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
expect(resolveCapabilities("guardian").mayBeInteractive).toBe(true);
|
|
106
|
+
expect(resolveCapabilities("trusted_contact").mayBeInteractive).toBe(true);
|
|
107
|
+
expect(resolveCapabilities("unverified_contact").mayBeInteractive).toBe(
|
|
108
|
+
true,
|
|
109
|
+
);
|
|
110
|
+
expect(resolveCapabilities("unknown").mayBeInteractive).toBe(false);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("sensitive tool approval is graded across the three tiers", () => {
|
|
114
|
+
expect(resolveCapabilities("guardian").sensitiveToolApproval).toBe("self");
|
|
115
|
+
expect(resolveCapabilities("trusted_contact").sensitiveToolApproval).toBe(
|
|
116
|
+
"escalate-and-wait",
|
|
117
|
+
);
|
|
118
|
+
expect(resolveCapabilities("unknown").sensitiveToolApproval).toBe("deny");
|
|
119
|
+
});
|
|
120
|
+
});
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Trust capabilities — what an actor may do once admitted.
|
|
3
|
+
*
|
|
4
|
+
* Separates *what an actor can do* (capabilities/permissions) from *who the
|
|
5
|
+
* actor is* (`TrustClass`, their role/level). The ~40+ decision sites
|
|
6
|
+
* read a named capability instead of re-deriving permissions inline from the
|
|
7
|
+
* This resolves the class to a named capability set in one place so call sites
|
|
8
|
+
* read a capability instead of re-deriving it.
|
|
9
|
+
*
|
|
10
|
+
* Stateless / derive-on-read: capabilities are derived from the already-resolved
|
|
11
|
+
* (and already-persisted) `trustClass` at point of use. Nothing here is stored;
|
|
12
|
+
* `trustClass` remains the persisted field in retry payloads, the journal store,
|
|
13
|
+
* and conversation CRUD.
|
|
14
|
+
*
|
|
15
|
+
* Admission is a SEPARATE axis: `TRUST_CLASS_RANK` vs `ADMISSION_FLOOR`
|
|
16
|
+
* ("who gets in the door") is intentionally not modeled here. `CapabilitySet`
|
|
17
|
+
* is the "what they may do once inside" axis.
|
|
18
|
+
*
|
|
19
|
+
* Context-dependent decisions (interactivity routing, self-approval races,
|
|
20
|
+
* channel-specific overrides) COMPOSE these primitives with runtime context.
|
|
21
|
+
* They are not encoded in the table; named composition helpers live in
|
|
22
|
+
* `effective-capabilities.ts` (and `resolveRoutingState` in
|
|
23
|
+
* `trust-context-resolver.ts`).
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import type { TrustClass } from "./actor-trust-resolver.js";
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Outcome when an actor invokes a tool that requires guardian approval.
|
|
30
|
+
* - `self`: the actor self-approves (guardian).
|
|
31
|
+
* - `escalate-and-wait`: route to the guardian and wait inline for a grant.
|
|
32
|
+
* - `deny`: fail-closed, no escalation or wait.
|
|
33
|
+
*/
|
|
34
|
+
export type SensitiveToolApproval = "self" | "escalate-and-wait" | "deny";
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Which trust-guidance block to inject into the model prompt. The capability
|
|
38
|
+
* layer owns the *selector*; the prompt layer owns the copy for each value.
|
|
39
|
+
*/
|
|
40
|
+
export type PromptTrustGuidance =
|
|
41
|
+
| "none"
|
|
42
|
+
| "social-engineering-defense"
|
|
43
|
+
| "stranger-warning";
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* What an actor may do once admitted, derived purely from their trust class.
|
|
47
|
+
*/
|
|
48
|
+
export interface CapabilitySet {
|
|
49
|
+
// --- Tool approval mechanism ---
|
|
50
|
+
/**
|
|
51
|
+
* Auto-approves *ordinary* tool calls (e.g. background/platform-hosted bash)
|
|
52
|
+
* and honors the actor's own pending-confirmation callback. The
|
|
53
|
+
* sensitive/guardian-required tool path is governed separately by
|
|
54
|
+
* `sensitiveToolApproval`; the two are independent levers.
|
|
55
|
+
*/
|
|
56
|
+
canSelfApproveTools: boolean;
|
|
57
|
+
/** Outcome when a guardian-approval-required (sensitive) tool is invoked. */
|
|
58
|
+
sensitiveToolApproval: SensitiveToolApproval;
|
|
59
|
+
|
|
60
|
+
// --- Privileged tool gates ---
|
|
61
|
+
/** May create/update schedules. */
|
|
62
|
+
canManageSchedules: boolean;
|
|
63
|
+
/** May use verification control-plane tools. */
|
|
64
|
+
canUseVerificationControlPlane: boolean;
|
|
65
|
+
/**
|
|
66
|
+
* May treat its own `user_approved` flag as sufficient authorization to
|
|
67
|
+
* archive-by-sender. Non-guardians can still archive by sender, but must be
|
|
68
|
+
* authorized via surface action, task, or explicit prompt approval instead.
|
|
69
|
+
*/
|
|
70
|
+
canSelfAuthorizeArchiveBySender: boolean;
|
|
71
|
+
|
|
72
|
+
// --- Data & memory ---
|
|
73
|
+
/**
|
|
74
|
+
* May access long-term / cross-conversation memory — both the memory *tools*
|
|
75
|
+
* (recall, auto-analysis, retrospection, graph extraction) and *visibility*
|
|
76
|
+
* of cross-conversation history in assembled context. Untrusted actors are
|
|
77
|
+
* walled off from both.
|
|
78
|
+
*/
|
|
79
|
+
canAccessMemory: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* May perform privileged (non-conversation-scoped) document operations from
|
|
82
|
+
* trust class alone. The effective decision also honors privileged channels —
|
|
83
|
+
* see `canActOnPrivilegedDocuments` in `effective-capabilities.ts`.
|
|
84
|
+
*/
|
|
85
|
+
canAccessPrivilegedDocuments: boolean;
|
|
86
|
+
|
|
87
|
+
// --- Execution environment ---
|
|
88
|
+
/**
|
|
89
|
+
* May run the shell WITHOUT the untrusted sandbox / CES lockdown (no
|
|
90
|
+
* `VELLUM_UNTRUSTED_SHELL`, no credential-secrecy confinement).
|
|
91
|
+
*/
|
|
92
|
+
canRunUnsandboxedShell: boolean;
|
|
93
|
+
|
|
94
|
+
// --- Interactivity & resource ---
|
|
95
|
+
/**
|
|
96
|
+
* Trust class alone permits interactive guardian-approval waits. Composed
|
|
97
|
+
* downstream with `guardianRouteResolvable` to derive `promptWaitingAllowed`
|
|
98
|
+
* (see `resolveRoutingState`).
|
|
99
|
+
*/
|
|
100
|
+
mayBeInteractive: boolean;
|
|
101
|
+
/** May trigger cleanup-mode operations under disk pressure. */
|
|
102
|
+
canActUnderDiskPressureCleanup: boolean;
|
|
103
|
+
|
|
104
|
+
// --- Prompt shaping ---
|
|
105
|
+
/** Which trust-guidance block to inject into the model prompt. */
|
|
106
|
+
promptTrustGuidance: PromptTrustGuidance;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Guardian: full control-plane access, self-approves tools.
|
|
111
|
+
*/
|
|
112
|
+
const GUARDIAN_CAPABILITIES: CapabilitySet = {
|
|
113
|
+
canSelfApproveTools: true,
|
|
114
|
+
sensitiveToolApproval: "self",
|
|
115
|
+
canManageSchedules: true,
|
|
116
|
+
canUseVerificationControlPlane: true,
|
|
117
|
+
canSelfAuthorizeArchiveBySender: true,
|
|
118
|
+
canAccessMemory: true,
|
|
119
|
+
canAccessPrivilegedDocuments: true,
|
|
120
|
+
canRunUnsandboxedShell: true,
|
|
121
|
+
mayBeInteractive: true,
|
|
122
|
+
canActUnderDiskPressureCleanup: true,
|
|
123
|
+
promptTrustGuidance: "none",
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Trusted / unverified contacts: may invoke tools but escalate sensitive ones
|
|
128
|
+
* to the guardian; no privileged data access; sandboxed execution.
|
|
129
|
+
*
|
|
130
|
+
* `trusted_contact` and `unverified_contact` are deliberately identical here —
|
|
131
|
+
* the distinction is admission-only (see `actor-trust-resolver.ts`). The matrix
|
|
132
|
+
* test pins this invariant.
|
|
133
|
+
*/
|
|
134
|
+
const CONTACT_CAPABILITIES: CapabilitySet = {
|
|
135
|
+
canSelfApproveTools: false,
|
|
136
|
+
sensitiveToolApproval: "escalate-and-wait",
|
|
137
|
+
canManageSchedules: false,
|
|
138
|
+
canUseVerificationControlPlane: false,
|
|
139
|
+
canSelfAuthorizeArchiveBySender: false,
|
|
140
|
+
canAccessMemory: false,
|
|
141
|
+
canAccessPrivilegedDocuments: false,
|
|
142
|
+
canRunUnsandboxedShell: false,
|
|
143
|
+
mayBeInteractive: true,
|
|
144
|
+
canActUnderDiskPressureCleanup: false,
|
|
145
|
+
promptTrustGuidance: "social-engineering-defense",
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
/**
|
|
149
|
+
* Unknown actors: fail-closed. No escalation, no interactivity, treated as a
|
|
150
|
+
* potential stranger in the prompt.
|
|
151
|
+
*/
|
|
152
|
+
const UNKNOWN_CAPABILITIES: CapabilitySet = {
|
|
153
|
+
canSelfApproveTools: false,
|
|
154
|
+
sensitiveToolApproval: "deny",
|
|
155
|
+
canManageSchedules: false,
|
|
156
|
+
canUseVerificationControlPlane: false,
|
|
157
|
+
canSelfAuthorizeArchiveBySender: false,
|
|
158
|
+
canAccessMemory: false,
|
|
159
|
+
canAccessPrivilegedDocuments: false,
|
|
160
|
+
canRunUnsandboxedShell: false,
|
|
161
|
+
mayBeInteractive: false,
|
|
162
|
+
canActUnderDiskPressureCleanup: false,
|
|
163
|
+
promptTrustGuidance: "stranger-warning",
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
const CAPABILITIES_BY_CLASS: Record<TrustClass, CapabilitySet> = {
|
|
167
|
+
guardian: GUARDIAN_CAPABILITIES,
|
|
168
|
+
trusted_contact: CONTACT_CAPABILITIES,
|
|
169
|
+
unverified_contact: CONTACT_CAPABILITIES,
|
|
170
|
+
unknown: UNKNOWN_CAPABILITIES,
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Resolve the capability set for a trust class. Pure and stateless.
|
|
175
|
+
*
|
|
176
|
+
* This is the single fail-closed trust boundary: any value that is not a
|
|
177
|
+
* recognized `TrustClass` — including `undefined` and legacy/persisted strings
|
|
178
|
+
* (e.g. `"non_guardian"`) — resolves to the `unknown` capability set. Callers
|
|
179
|
+
* pass their raw trust value directly; they never re-derive "is this a known
|
|
180
|
+
* class?" at the call site. The `(string & {})` member keeps autocomplete for
|
|
181
|
+
* the known classes while still accepting an arbitrary string.
|
|
182
|
+
*
|
|
183
|
+
* The lookup uses an own-property check so raw values that name inherited
|
|
184
|
+
* members (`"__proto__"`, `"constructor"`, `"toString"`) fail closed to the
|
|
185
|
+
* `unknown` set rather than reading off `Object.prototype`.
|
|
186
|
+
*/
|
|
187
|
+
export function resolveCapabilities(
|
|
188
|
+
trustClass: TrustClass | (string & {}) | undefined,
|
|
189
|
+
): CapabilitySet {
|
|
190
|
+
if (
|
|
191
|
+
trustClass != null &&
|
|
192
|
+
Object.prototype.hasOwnProperty.call(CAPABILITIES_BY_CLASS, trustClass)
|
|
193
|
+
) {
|
|
194
|
+
return CAPABILITIES_BY_CLASS[trustClass as TrustClass];
|
|
195
|
+
}
|
|
196
|
+
return UNKNOWN_CAPABILITIES;
|
|
197
|
+
}
|
|
@@ -12,12 +12,16 @@ import type { ApprovalActionOption } from "@vellumai/gateway-client";
|
|
|
12
12
|
|
|
13
13
|
import type { GuardianDecisionAction } from "./guardian-decision-types.js";
|
|
14
14
|
|
|
15
|
-
// Re-export shared wire types so existing daemon imports keep working.
|
|
16
15
|
export type {
|
|
17
16
|
ApprovalActionOption,
|
|
18
17
|
ApprovalUIMetadata,
|
|
19
18
|
PermissionRequestDetails,
|
|
20
19
|
} from "@vellumai/gateway-client";
|
|
20
|
+
// Re-export shared wire types + schemas so existing daemon imports keep working.
|
|
21
|
+
export {
|
|
22
|
+
ApprovalUIMetadataSchema,
|
|
23
|
+
PermissionRequestDetailsSchema,
|
|
24
|
+
} from "@vellumai/gateway-client";
|
|
21
25
|
|
|
22
26
|
// ---------------------------------------------------------------------------
|
|
23
27
|
// Approval actions (daemon-internal)
|
|
@@ -333,8 +333,7 @@ export function getGuardianBinding(
|
|
|
333
333
|
id: result.channel.id,
|
|
334
334
|
assistantId,
|
|
335
335
|
channel,
|
|
336
|
-
guardianExternalUserId:
|
|
337
|
-
result.channel.externalUserId ?? result.channel.address ?? "",
|
|
336
|
+
guardianExternalUserId: result.channel.address,
|
|
338
337
|
guardianDeliveryChatId: result.channel.externalChatId ?? "",
|
|
339
338
|
guardianPrincipalId: result.contact.principalId ?? "",
|
|
340
339
|
status: "active" as const,
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health heuristics for connected SSE clients.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
/** Keep-alive comment sent to idle clients every 7 s by default. */
|
|
6
|
+
export const DEFAULT_HEARTBEAT_INTERVAL_MS = 7_000;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Whether a client looks degraded: its last heartbeat (`lastActiveAt`) is
|
|
10
|
+
* stale by more than two heartbeat intervals. This surfaces a
|
|
11
|
+
* registered-but-not-heartbeating connection — the symptom of a flapping
|
|
12
|
+
* extension SSE stream — whether it never heartbeated or heartbeated and
|
|
13
|
+
* then froze. A healthy client (fresh or actively heartbeating) has a
|
|
14
|
+
* recent `lastActiveAt` and is not flagged.
|
|
15
|
+
*
|
|
16
|
+
* Limitation: a just-reconnected flapping client has a fresh
|
|
17
|
+
* `lastActiveAt` and looks healthy in a single snapshot — this is
|
|
18
|
+
* best-effort visibility, not a liveness guarantee.
|
|
19
|
+
*/
|
|
20
|
+
export function isClientDegraded(
|
|
21
|
+
lastActiveAt: Date,
|
|
22
|
+
now: Date,
|
|
23
|
+
heartbeatIntervalMs: number,
|
|
24
|
+
): boolean {
|
|
25
|
+
return now.getTime() - lastActiveAt.getTime() > 2 * heartbeatIntervalMs;
|
|
26
|
+
}
|
|
@@ -13,15 +13,16 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import type { TrustContext } from "../daemon/trust-context.js";
|
|
16
|
+
import type { CanonicalGuardianRequest } from "../memory/canonical-guardian-store.js";
|
|
16
17
|
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
} from "../
|
|
18
|
+
recordApprovalCardDelivery,
|
|
19
|
+
recordGuardianRequestDeliveries,
|
|
20
|
+
} from "../notifications/canonical-delivery-recorder.js";
|
|
20
21
|
import { emitNotificationSignal } from "../notifications/emit-signal.js";
|
|
21
|
-
import type { NotificationSourceChannel } from "../notifications/signal.js";
|
|
22
22
|
import { canonicalizeInboundIdentity } from "../util/canonicalize-identity.js";
|
|
23
23
|
import { getLogger } from "../util/logger.js";
|
|
24
24
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from "./assistant-scope.js";
|
|
25
|
+
import { resolveCapabilities } from "./capabilities.js";
|
|
25
26
|
import { getGuardianBinding } from "./channel-verification-service.js";
|
|
26
27
|
|
|
27
28
|
const log = getLogger("confirmation-request-guardian-bridge");
|
|
@@ -48,7 +49,7 @@ export type BridgeConfirmationRequestResult =
|
|
|
48
49
|
| {
|
|
49
50
|
skipped: true;
|
|
50
51
|
reason:
|
|
51
|
-
| "
|
|
52
|
+
| "not_bridgeable_trust_class"
|
|
52
53
|
| "no_guardian_binding"
|
|
53
54
|
| "missing_guardian_identity"
|
|
54
55
|
| "binding_identity_mismatch";
|
|
@@ -59,11 +60,13 @@ export type BridgeConfirmationRequestResult =
|
|
|
59
60
|
// ---------------------------------------------------------------------------
|
|
60
61
|
|
|
61
62
|
/**
|
|
62
|
-
* Bridge a
|
|
63
|
+
* Bridge a non-guardian contact confirmation_request to a guardian.question
|
|
64
|
+
* notification.
|
|
63
65
|
*
|
|
64
|
-
* Only emits when the session belongs to a
|
|
65
|
-
* resolvable guardian binding. Guardian and unknown actors are
|
|
66
|
-
* self-approve, and unknown actors are already fail-closed
|
|
66
|
+
* Only emits when the session belongs to a trusted_contact or unverified_contact
|
|
67
|
+
* actor with a resolvable guardian binding. Guardian and unknown actors are
|
|
68
|
+
* skipped — guardians self-approve, and unknown actors are already fail-closed
|
|
69
|
+
* by the routing layer.
|
|
67
70
|
*
|
|
68
71
|
* Fire-and-forget safe: notification emission errors are logged but not propagated.
|
|
69
72
|
*/
|
|
@@ -78,10 +81,14 @@ export function bridgeConfirmationRequestToGuardian(
|
|
|
78
81
|
assistantId = DAEMON_INTERNAL_ASSISTANT_ID,
|
|
79
82
|
} = params;
|
|
80
83
|
|
|
81
|
-
// Only bridge for
|
|
82
|
-
// unknown actors are fail-closed by the routing
|
|
83
|
-
|
|
84
|
-
|
|
84
|
+
// Only bridge for actors whose sensitive tool approval escalates-and-waits.
|
|
85
|
+
// Guardians self-approve and unknown actors are fail-closed by the routing
|
|
86
|
+
// layer, so neither needs a guardian bridge.
|
|
87
|
+
if (
|
|
88
|
+
resolveCapabilities(trustContext.trustClass).sensitiveToolApproval !==
|
|
89
|
+
"escalate-and-wait"
|
|
90
|
+
) {
|
|
91
|
+
return { skipped: true, reason: "not_bridgeable_trust_class" };
|
|
85
92
|
}
|
|
86
93
|
|
|
87
94
|
if (!trustContext.guardianExternalUserId) {
|
|
@@ -144,10 +151,14 @@ export function bridgeConfirmationRequestToGuardian(
|
|
|
144
151
|
? `Approve tool: ${toolName} — ${canonicalRequest.activityText}`
|
|
145
152
|
: `Approve tool: ${toolName}`;
|
|
146
153
|
|
|
154
|
+
// The vellum delivery row is created up front in onConversationCreated so the
|
|
155
|
+
// in-app client sees it immediately; the post-resolve recorder reuses it.
|
|
156
|
+
let vellumDeliveryId: string | undefined;
|
|
157
|
+
|
|
147
158
|
// Emit guardian.question notification so the guardian is alerted.
|
|
148
159
|
const signalPromise = emitNotificationSignal({
|
|
149
160
|
sourceEventName: "guardian.question",
|
|
150
|
-
sourceChannel
|
|
161
|
+
sourceChannel,
|
|
151
162
|
sourceContextId: conversationId,
|
|
152
163
|
requiresConversation: true,
|
|
153
164
|
attentionHints: {
|
|
@@ -157,7 +168,7 @@ export function bridgeConfirmationRequestToGuardian(
|
|
|
157
168
|
visibleInSourceNow: false,
|
|
158
169
|
},
|
|
159
170
|
contextPayload: {
|
|
160
|
-
requestKind: "tool_approval"
|
|
171
|
+
requestKind: "tool_approval",
|
|
161
172
|
requestId: canonicalRequest.id,
|
|
162
173
|
requestCode:
|
|
163
174
|
canonicalRequest.requestCode ??
|
|
@@ -168,29 +179,27 @@ export function bridgeConfirmationRequestToGuardian(
|
|
|
168
179
|
requesterIdentifier: senderLabel,
|
|
169
180
|
toolName,
|
|
170
181
|
questionText,
|
|
182
|
+
riskLevel: canonicalRequest.riskLevel ?? undefined,
|
|
183
|
+
commandPreview: canonicalRequest.commandPreview ?? undefined,
|
|
171
184
|
},
|
|
172
185
|
dedupeKey: `tc-confirmation-request:${canonicalRequest.id}`,
|
|
173
186
|
onConversationCreated: (info) => {
|
|
174
|
-
|
|
187
|
+
vellumDeliveryId = recordApprovalCardDelivery({
|
|
175
188
|
requestId: canonicalRequest.id,
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
});
|
|
189
|
+
channel: "vellum",
|
|
190
|
+
conversationId: info.conversationId,
|
|
191
|
+
})?.id;
|
|
179
192
|
},
|
|
180
193
|
});
|
|
181
194
|
|
|
182
|
-
// Record
|
|
195
|
+
// Record deliveries from the notification pipeline (fire-and-forget).
|
|
183
196
|
void signalPromise
|
|
184
197
|
.then((signalResult) => {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
destinationChatId:
|
|
191
|
-
result.destination.length > 0 ? result.destination : undefined,
|
|
192
|
-
});
|
|
193
|
-
}
|
|
198
|
+
recordGuardianRequestDeliveries({
|
|
199
|
+
requestId: canonicalRequest.id,
|
|
200
|
+
deliveryResults: signalResult.deliveryResults,
|
|
201
|
+
vellumDeliveryId,
|
|
202
|
+
});
|
|
194
203
|
})
|
|
195
204
|
.catch((err) => {
|
|
196
205
|
log.warn(
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
canActOnPrivilegedDocuments,
|
|
5
|
+
isArchiveBySenderAuthorized,
|
|
6
|
+
isUntrustedShellLockdownActive,
|
|
7
|
+
} from "./effective-capabilities.js";
|
|
8
|
+
|
|
9
|
+
describe("canActOnPrivilegedDocuments", () => {
|
|
10
|
+
test("guardian is privileged regardless of channel", () => {
|
|
11
|
+
expect(canActOnPrivilegedDocuments({ trustClass: "guardian" })).toBe(true);
|
|
12
|
+
expect(
|
|
13
|
+
canActOnPrivilegedDocuments({
|
|
14
|
+
trustClass: "guardian",
|
|
15
|
+
executionChannel: "telegram",
|
|
16
|
+
}),
|
|
17
|
+
).toBe(true);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("non-guardian is privileged only on a privileged channel", () => {
|
|
21
|
+
expect(
|
|
22
|
+
canActOnPrivilegedDocuments({
|
|
23
|
+
trustClass: "trusted_contact",
|
|
24
|
+
executionChannel: "telegram",
|
|
25
|
+
}),
|
|
26
|
+
).toBe(false);
|
|
27
|
+
expect(
|
|
28
|
+
canActOnPrivilegedDocuments({
|
|
29
|
+
trustClass: "trusted_contact",
|
|
30
|
+
executionChannel: "vellum",
|
|
31
|
+
}),
|
|
32
|
+
).toBe(true);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("the vellum channel grants access even to unknown actors", () => {
|
|
36
|
+
expect(
|
|
37
|
+
canActOnPrivilegedDocuments({
|
|
38
|
+
trustClass: "unknown",
|
|
39
|
+
executionChannel: "vellum",
|
|
40
|
+
}),
|
|
41
|
+
).toBe(true);
|
|
42
|
+
expect(canActOnPrivilegedDocuments({ trustClass: "unknown" })).toBe(false);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe("isUntrustedShellLockdownActive", () => {
|
|
47
|
+
test("inactive when the lockdown flag is off", () => {
|
|
48
|
+
expect(
|
|
49
|
+
isUntrustedShellLockdownActive({
|
|
50
|
+
trustClass: "unknown",
|
|
51
|
+
lockdownEnabled: false,
|
|
52
|
+
}),
|
|
53
|
+
).toBe(false);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("inactive for guardians even when the flag is on", () => {
|
|
57
|
+
expect(
|
|
58
|
+
isUntrustedShellLockdownActive({
|
|
59
|
+
trustClass: "guardian",
|
|
60
|
+
lockdownEnabled: true,
|
|
61
|
+
}),
|
|
62
|
+
).toBe(false);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("active for non-unsandboxed actors when the flag is on", () => {
|
|
66
|
+
expect(
|
|
67
|
+
isUntrustedShellLockdownActive({
|
|
68
|
+
trustClass: "trusted_contact",
|
|
69
|
+
lockdownEnabled: true,
|
|
70
|
+
}),
|
|
71
|
+
).toBe(true);
|
|
72
|
+
expect(
|
|
73
|
+
isUntrustedShellLockdownActive({
|
|
74
|
+
trustClass: "unknown",
|
|
75
|
+
lockdownEnabled: true,
|
|
76
|
+
}),
|
|
77
|
+
).toBe(true);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe("isArchiveBySenderAuthorized", () => {
|
|
82
|
+
test("any non-self authorization path suffices", () => {
|
|
83
|
+
expect(
|
|
84
|
+
isArchiveBySenderAuthorized({
|
|
85
|
+
trustClass: "unknown",
|
|
86
|
+
triggeredBySurfaceAction: true,
|
|
87
|
+
}),
|
|
88
|
+
).toBe(true);
|
|
89
|
+
expect(
|
|
90
|
+
isArchiveBySenderAuthorized({
|
|
91
|
+
trustClass: "unknown",
|
|
92
|
+
batchAuthorizedByTask: true,
|
|
93
|
+
}),
|
|
94
|
+
).toBe(true);
|
|
95
|
+
expect(
|
|
96
|
+
isArchiveBySenderAuthorized({
|
|
97
|
+
trustClass: "unknown",
|
|
98
|
+
approvedViaPrompt: true,
|
|
99
|
+
}),
|
|
100
|
+
).toBe(true);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("user_approved self-authorizes only for a permitted trust class", () => {
|
|
104
|
+
expect(
|
|
105
|
+
isArchiveBySenderAuthorized({
|
|
106
|
+
trustClass: "guardian",
|
|
107
|
+
userApproved: true,
|
|
108
|
+
}),
|
|
109
|
+
).toBe(true);
|
|
110
|
+
expect(
|
|
111
|
+
isArchiveBySenderAuthorized({
|
|
112
|
+
trustClass: "trusted_contact",
|
|
113
|
+
userApproved: true,
|
|
114
|
+
}),
|
|
115
|
+
).toBe(false);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("unauthorized when no path applies", () => {
|
|
119
|
+
expect(isArchiveBySenderAuthorized({ trustClass: "guardian" })).toBe(false);
|
|
120
|
+
expect(
|
|
121
|
+
isArchiveBySenderAuthorized({
|
|
122
|
+
trustClass: "trusted_contact",
|
|
123
|
+
triggeredBySurfaceAction: false,
|
|
124
|
+
userApproved: false,
|
|
125
|
+
}),
|
|
126
|
+
).toBe(false);
|
|
127
|
+
});
|
|
128
|
+
});
|