@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
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import type { ChannelId } from "../channels/types.js";
|
|
15
|
-
import {
|
|
15
|
+
import { getGuardianDelivery } from "../contacts/guardian-delivery-reader.js";
|
|
16
16
|
import type { ChannelStatus } from "../contacts/types.js";
|
|
17
17
|
import {
|
|
18
18
|
createCanonicalGuardianRequest,
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
import { emitNotificationSignal } from "../notifications/emit-signal.js";
|
|
26
26
|
import type { GuardianResolutionSource } from "../notifications/signal.js";
|
|
27
27
|
import { getLogger } from "../util/logger.js";
|
|
28
|
+
import { resolveAnchoredGuardian } from "./anchored-guardian.js";
|
|
28
29
|
import { GUARDIAN_APPROVAL_TTL_MS } from "./routes/channel-route-shared.js";
|
|
29
30
|
|
|
30
31
|
const log = getLogger("access-request-helper");
|
|
@@ -70,12 +71,12 @@ export type AccessRequestResult =
|
|
|
70
71
|
* trust anchor and only accepts source-channel contacts that match it. This
|
|
71
72
|
* prevents stale or cross-assistant contacts from being bound to the request.
|
|
72
73
|
*
|
|
73
|
-
*
|
|
74
|
-
*
|
|
74
|
+
* The canonical store writes complete before this resolves; the notification
|
|
75
|
+
* signal emission is fire-and-forget.
|
|
75
76
|
*/
|
|
76
|
-
export function notifyGuardianOfAccessRequest(
|
|
77
|
+
export async function notifyGuardianOfAccessRequest(
|
|
77
78
|
params: AccessRequestParams,
|
|
78
|
-
): AccessRequestResult {
|
|
79
|
+
): Promise<AccessRequestResult> {
|
|
79
80
|
const {
|
|
80
81
|
canonicalAssistantId,
|
|
81
82
|
sourceChannel,
|
|
@@ -94,40 +95,18 @@ export function notifyGuardianOfAccessRequest(
|
|
|
94
95
|
return { notified: false, reason: "no_sender_id" };
|
|
95
96
|
}
|
|
96
97
|
|
|
97
|
-
// Resolve guardian identity with assistant-anchored strategy
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
const
|
|
108
|
-
|
|
109
|
-
// Try source-channel guardian, but only if it maps to the assistant's
|
|
110
|
-
// anchored principal. This blocks cross-assistant/stale contact selection.
|
|
111
|
-
const sourceGuardian = findGuardianForChannel(sourceChannel);
|
|
112
|
-
if (
|
|
113
|
-
assistantGuardianPrincipalId &&
|
|
114
|
-
sourceGuardian &&
|
|
115
|
-
sourceGuardian.contact.principalId === assistantGuardianPrincipalId
|
|
116
|
-
) {
|
|
117
|
-
guardianExternalUserId = sourceGuardian.channel.address;
|
|
118
|
-
guardianPrincipalId = sourceGuardian.contact.principalId;
|
|
119
|
-
guardianBindingChannel = sourceGuardian.channel.type;
|
|
120
|
-
guardianResolutionSource = "source-channel-contact";
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Access requests always require a principal. If source-channel resolution
|
|
124
|
-
// did not match the assistant anchor, use the anchored vellum identity.
|
|
125
|
-
if (!guardianPrincipalId && vellumGuardian) {
|
|
126
|
-
guardianExternalUserId = vellumGuardian.channel.address;
|
|
127
|
-
guardianPrincipalId = assistantGuardianPrincipalId ?? null;
|
|
128
|
-
guardianBindingChannel = guardianBindingChannel ?? "vellum";
|
|
129
|
-
guardianResolutionSource = "vellum-anchor";
|
|
130
|
-
}
|
|
98
|
+
// Resolve guardian identity with the assistant-anchored strategy (gateway
|
|
99
|
+
// source-channel match validated against the vellum anchor, else the vellum
|
|
100
|
+
// anchor).
|
|
101
|
+
const anchored = resolveAnchoredGuardian({
|
|
102
|
+
guardians: await getGuardianDelivery(),
|
|
103
|
+
sourceChannel,
|
|
104
|
+
});
|
|
105
|
+
const guardianExternalUserId = anchored?.address ?? null;
|
|
106
|
+
const guardianPrincipalId = anchored?.principalId ?? null;
|
|
107
|
+
const guardianBindingChannel = anchored?.channelType ?? null;
|
|
108
|
+
const guardianResolutionSource: GuardianResolutionSource =
|
|
109
|
+
anchored?.source ?? "none";
|
|
131
110
|
|
|
132
111
|
log.debug(
|
|
133
112
|
{
|
|
@@ -17,15 +17,23 @@
|
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
19
|
import type { ChannelId } from "../channels/types.js";
|
|
20
|
+
import { findContactByAddress } from "../contacts/contact-store.js";
|
|
20
21
|
import {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
} from "../contacts/
|
|
22
|
+
guardianForChannel,
|
|
23
|
+
peekCachedGuardianDelivery,
|
|
24
|
+
} from "../contacts/guardian-delivery-reader.js";
|
|
24
25
|
import { channelStatusToMemberStatus } from "../contacts/member-status.js";
|
|
25
|
-
import type {
|
|
26
|
+
import type {
|
|
27
|
+
ChannelPolicy,
|
|
28
|
+
ChannelStatus,
|
|
29
|
+
ContactChannel,
|
|
30
|
+
ContactRole,
|
|
31
|
+
ContactWithChannels,
|
|
32
|
+
} from "../contacts/types.js";
|
|
26
33
|
import type { TrustContext } from "../daemon/trust-context.js";
|
|
27
34
|
import { canonicalizeInboundIdentity } from "../util/canonicalize-identity.js";
|
|
28
35
|
import { getLogger } from "../util/logger.js";
|
|
36
|
+
import { getCachedMemberAcl } from "./member-verdict-cache.js";
|
|
29
37
|
|
|
30
38
|
const log = getLogger("actor-trust-resolver");
|
|
31
39
|
|
|
@@ -78,10 +86,18 @@ export interface ActorTrustContext {
|
|
|
78
86
|
} | null;
|
|
79
87
|
/** Canonical principal ID from the guardian binding. */
|
|
80
88
|
guardianPrincipalId?: string;
|
|
81
|
-
/**
|
|
89
|
+
/**
|
|
90
|
+
* Resolved contact + channel for this sender, if any. The ACL view
|
|
91
|
+
* (status/policy/role) is carried here rather than on the contact/channel
|
|
92
|
+
* objects, sourced from the gateway verdict — the verdict path reads it
|
|
93
|
+
* inline, the sync fallback from the in-memory member-verdict cache.
|
|
94
|
+
*/
|
|
82
95
|
memberRecord: {
|
|
83
96
|
contact: ContactWithChannels;
|
|
84
97
|
channel: ContactChannel;
|
|
98
|
+
status: ChannelStatus;
|
|
99
|
+
policy: ChannelPolicy;
|
|
100
|
+
role: ContactRole;
|
|
85
101
|
} | null;
|
|
86
102
|
/** Trust classification. */
|
|
87
103
|
trustClass: TrustClass;
|
|
@@ -175,21 +191,27 @@ export function resolveActorTrust(
|
|
|
175
191
|
}
|
|
176
192
|
|
|
177
193
|
// --- Guardian lookup ---
|
|
178
|
-
|
|
194
|
+
// Sync read of the gateway guardian delivery from the IO-free cache snapshot
|
|
195
|
+
// (kept warm by the async hot paths + daemon-startup warm). A cold cache
|
|
196
|
+
// yields no guardian match, the same outcome as no binding.
|
|
197
|
+
const cachedGuardians = peekCachedGuardianDelivery({
|
|
198
|
+
channelTypes: [input.sourceChannel],
|
|
199
|
+
});
|
|
200
|
+
const guardianDelivery = cachedGuardians
|
|
201
|
+
? guardianForChannel(cachedGuardians, input.sourceChannel)
|
|
202
|
+
: undefined;
|
|
179
203
|
let guardianBindingMatch: ActorTrustContext["guardianBindingMatch"] = null;
|
|
180
204
|
let guardianPrincipalId: string | undefined;
|
|
181
205
|
let isGuardian = false;
|
|
182
206
|
|
|
183
|
-
if (
|
|
184
|
-
const { contact: guardianContact, channel: guardianChannel } =
|
|
185
|
-
guardianResult;
|
|
207
|
+
if (guardianDelivery) {
|
|
186
208
|
guardianBindingMatch = {
|
|
187
|
-
guardianExternalUserId:
|
|
188
|
-
guardianDeliveryChatId:
|
|
209
|
+
guardianExternalUserId: guardianDelivery.address,
|
|
210
|
+
guardianDeliveryChatId: guardianDelivery.externalChatId ?? null,
|
|
189
211
|
};
|
|
190
|
-
guardianPrincipalId =
|
|
212
|
+
guardianPrincipalId = guardianDelivery.principalId ?? undefined;
|
|
191
213
|
isGuardian =
|
|
192
|
-
|
|
214
|
+
guardianDelivery.address.toLowerCase() === canonicalSenderId.toLowerCase();
|
|
193
215
|
}
|
|
194
216
|
|
|
195
217
|
log.debug(
|
|
@@ -213,7 +235,12 @@ export function resolveActorTrust(
|
|
|
213
235
|
ch.address.toLowerCase() === canonicalSenderId.toLowerCase(),
|
|
214
236
|
);
|
|
215
237
|
if (byAddress && byAddressChannel) {
|
|
216
|
-
|
|
238
|
+
const acl = getCachedMemberAcl(input.sourceChannel, canonicalSenderId);
|
|
239
|
+
if (acl) {
|
|
240
|
+
memberRecord = { contact: byAddress, channel: byAddressChannel, ...acl };
|
|
241
|
+
}
|
|
242
|
+
// Fail-closed: already in the sync fallback (no live verdict) and no cached
|
|
243
|
+
// verdict → leave memberRecord null so trustClass resolves to unknown.
|
|
217
244
|
}
|
|
218
245
|
|
|
219
246
|
log.debug(
|
|
@@ -252,7 +279,7 @@ export function resolveActorTrust(
|
|
|
252
279
|
if (isGuardian) {
|
|
253
280
|
trustClass = "guardian";
|
|
254
281
|
} else if (memberMatchesSender && memberRecord) {
|
|
255
|
-
const status = memberRecord.
|
|
282
|
+
const status = memberRecord.status;
|
|
256
283
|
if (status === "active") {
|
|
257
284
|
trustClass = "trusted_contact";
|
|
258
285
|
} else if (status === "unverified" || status === "pending") {
|
|
@@ -323,12 +350,12 @@ export function toTrustContext(
|
|
|
323
350
|
requesterMemberDisplayName: ctx.actorMetadata.memberDisplayName,
|
|
324
351
|
requesterExternalUserId: ctx.canonicalSenderId ?? undefined,
|
|
325
352
|
requesterChatId: conversationExternalId,
|
|
326
|
-
// Member grounding from
|
|
327
|
-
//
|
|
353
|
+
// Member grounding from the resolved memberRecord (voice + verdict paths
|
|
354
|
+
// both populate it).
|
|
328
355
|
requesterContactId: ctx.memberRecord?.contact.id,
|
|
329
356
|
memberStatus: ctx.memberRecord
|
|
330
|
-
? channelStatusToMemberStatus(ctx.memberRecord.
|
|
357
|
+
? channelStatusToMemberStatus(ctx.memberRecord.status)
|
|
331
358
|
: undefined,
|
|
332
|
-
memberPolicy: ctx.memberRecord?.
|
|
359
|
+
memberPolicy: ctx.memberRecord?.policy,
|
|
333
360
|
};
|
|
334
361
|
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for `resolveAnchoredGuardian`.
|
|
3
|
+
*
|
|
4
|
+
* Covers the gateway arms (source-channel match validated against the vellum
|
|
5
|
+
* anchor, vellum-anchor fallback) and the cosmetic `requireAnchorPrincipal`
|
|
6
|
+
* guard.
|
|
7
|
+
*/
|
|
8
|
+
import { describe, expect, test } from "bun:test";
|
|
9
|
+
|
|
10
|
+
import type { GuardianDelivery } from "@vellumai/gateway-client";
|
|
11
|
+
|
|
12
|
+
import { resolveAnchoredGuardian } from "./anchored-guardian.js";
|
|
13
|
+
|
|
14
|
+
function gw(g: Partial<GuardianDelivery> & { channelType: string; address: string }): GuardianDelivery {
|
|
15
|
+
return {
|
|
16
|
+
contactId: `c-${g.channelType}`,
|
|
17
|
+
status: "active",
|
|
18
|
+
...g,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe("resolveAnchoredGuardian — gateway arm", () => {
|
|
23
|
+
test("source-channel guardian matching the anchor wins", () => {
|
|
24
|
+
const result = resolveAnchoredGuardian({
|
|
25
|
+
guardians: [
|
|
26
|
+
gw({ channelType: "vellum", address: "v-addr", principalId: "p-anchor", displayName: "Vellum" }),
|
|
27
|
+
gw({ channelType: "telegram", address: "tg-addr", principalId: "p-anchor", displayName: "Alice" }),
|
|
28
|
+
],
|
|
29
|
+
sourceChannel: "telegram",
|
|
30
|
+
});
|
|
31
|
+
expect(result).toEqual({
|
|
32
|
+
principalId: "p-anchor",
|
|
33
|
+
address: "tg-addr",
|
|
34
|
+
displayName: "Alice",
|
|
35
|
+
channelType: "telegram",
|
|
36
|
+
source: "source-channel-contact",
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("source-channel guardian NOT matching the anchor falls back to vellum-anchor", () => {
|
|
41
|
+
const result = resolveAnchoredGuardian({
|
|
42
|
+
guardians: [
|
|
43
|
+
gw({ channelType: "vellum", address: "v-addr", principalId: "p-anchor", displayName: "Vellum" }),
|
|
44
|
+
gw({ channelType: "telegram", address: "tg-addr", principalId: "p-other", displayName: "Stale" }),
|
|
45
|
+
],
|
|
46
|
+
sourceChannel: "telegram",
|
|
47
|
+
});
|
|
48
|
+
expect(result).toEqual({
|
|
49
|
+
principalId: "p-anchor",
|
|
50
|
+
address: "v-addr",
|
|
51
|
+
displayName: "Vellum",
|
|
52
|
+
channelType: "vellum",
|
|
53
|
+
source: "vellum-anchor",
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("no source-channel guardian falls back to vellum-anchor", () => {
|
|
58
|
+
const result = resolveAnchoredGuardian({
|
|
59
|
+
guardians: [
|
|
60
|
+
gw({ channelType: "vellum", address: "v-addr", principalId: "p-anchor", displayName: "Vellum" }),
|
|
61
|
+
],
|
|
62
|
+
sourceChannel: "telegram",
|
|
63
|
+
});
|
|
64
|
+
expect(result?.source).toBe("vellum-anchor");
|
|
65
|
+
expect(result?.principalId).toBe("p-anchor");
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe("resolveAnchoredGuardian — gateway empty", () => {
|
|
70
|
+
test("null gateway list returns null", () => {
|
|
71
|
+
const result = resolveAnchoredGuardian({
|
|
72
|
+
guardians: null,
|
|
73
|
+
sourceChannel: "telegram",
|
|
74
|
+
});
|
|
75
|
+
expect(result).toBeNull();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("empty gateway list returns null", () => {
|
|
79
|
+
const result = resolveAnchoredGuardian({
|
|
80
|
+
guardians: [],
|
|
81
|
+
sourceChannel: "telegram",
|
|
82
|
+
});
|
|
83
|
+
expect(result).toBeNull();
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe("resolveAnchoredGuardian — requireAnchorPrincipal (cosmetic label)", () => {
|
|
88
|
+
test("vellum guardian with a null principal degrades to null", () => {
|
|
89
|
+
const result = resolveAnchoredGuardian({
|
|
90
|
+
guardians: [
|
|
91
|
+
gw({ channelType: "vellum", address: "v-addr", principalId: null, displayName: "Vellum" }),
|
|
92
|
+
],
|
|
93
|
+
sourceChannel: "telegram",
|
|
94
|
+
requireAnchorPrincipal: true,
|
|
95
|
+
});
|
|
96
|
+
expect(result).toBeNull();
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("without requireAnchorPrincipal a null-principal vellum still resolves", () => {
|
|
100
|
+
const result = resolveAnchoredGuardian({
|
|
101
|
+
guardians: [
|
|
102
|
+
gw({ channelType: "vellum", address: "v-addr", principalId: null, displayName: "Vellum" }),
|
|
103
|
+
],
|
|
104
|
+
sourceChannel: "telegram",
|
|
105
|
+
});
|
|
106
|
+
expect(result?.source).toBe("vellum-anchor");
|
|
107
|
+
expect(result?.principalId).toBeNull();
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared anchored-guardian resolution.
|
|
3
|
+
*
|
|
4
|
+
* Resolves the guardian identity for an inbound access request using the
|
|
5
|
+
* assistant's vellum principal as the trust anchor: a source-channel guardian
|
|
6
|
+
* is only accepted when its principal matches the anchor, otherwise the vellum
|
|
7
|
+
* anchor identity is used. This blocks stale or cross-assistant contacts from
|
|
8
|
+
* being bound to a request.
|
|
9
|
+
*
|
|
10
|
+
* Resolves from the gateway delivery list, the source of truth for guardian
|
|
11
|
+
* principals.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import type { GuardianDelivery } from "@vellumai/gateway-client";
|
|
15
|
+
|
|
16
|
+
import type { ChannelId } from "../channels/types.js";
|
|
17
|
+
import { guardianForChannel } from "../contacts/guardian-delivery-reader.js";
|
|
18
|
+
import type { GuardianResolutionSource } from "../notifications/signal.js";
|
|
19
|
+
|
|
20
|
+
/** Resolved guardian identity, anchored on the assistant's vellum principal. */
|
|
21
|
+
export interface AnchoredGuardian {
|
|
22
|
+
principalId: string | null;
|
|
23
|
+
address: string;
|
|
24
|
+
displayName: string | null;
|
|
25
|
+
channelType: string;
|
|
26
|
+
source: GuardianResolutionSource;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface ResolveAnchoredGuardianInput {
|
|
30
|
+
/** Gateway delivery list; `null` when the gateway read failed/was empty. */
|
|
31
|
+
guardians: GuardianDelivery[] | null;
|
|
32
|
+
sourceChannel: ChannelId;
|
|
33
|
+
/**
|
|
34
|
+
* Require a non-null anchor principal for the vellum-anchor arm. When the
|
|
35
|
+
* vellum guardian has no principal, return `null` instead of a vellum-anchor
|
|
36
|
+
* record. Matches the cosmetic label path, which degrades to the default
|
|
37
|
+
* reference when the anchor principal is absent.
|
|
38
|
+
*/
|
|
39
|
+
requireAnchorPrincipal?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Resolve the anchored guardian for `sourceChannel`, or `null` when none can be
|
|
44
|
+
* resolved. Gateway source-channel match → that record; gateway anchor-only →
|
|
45
|
+
* vellum-anchor.
|
|
46
|
+
*/
|
|
47
|
+
export function resolveAnchoredGuardian(
|
|
48
|
+
input: ResolveAnchoredGuardianInput,
|
|
49
|
+
): AnchoredGuardian | null {
|
|
50
|
+
const { sourceChannel, requireAnchorPrincipal } = input;
|
|
51
|
+
const guardians = input.guardians ?? [];
|
|
52
|
+
|
|
53
|
+
const vellumGuardian = guardianForChannel(guardians, "vellum");
|
|
54
|
+
const anchorPrincipalId = vellumGuardian?.principalId;
|
|
55
|
+
|
|
56
|
+
let resolved: AnchoredGuardian | null = null;
|
|
57
|
+
|
|
58
|
+
// Source-channel guardian, but only when it maps to the assistant's anchored
|
|
59
|
+
// principal. This blocks cross-assistant/stale binding selection.
|
|
60
|
+
const sourceGuardian = guardianForChannel(guardians, sourceChannel);
|
|
61
|
+
if (
|
|
62
|
+
anchorPrincipalId &&
|
|
63
|
+
sourceGuardian &&
|
|
64
|
+
sourceGuardian.principalId === anchorPrincipalId
|
|
65
|
+
) {
|
|
66
|
+
resolved = {
|
|
67
|
+
principalId: sourceGuardian.principalId,
|
|
68
|
+
address: sourceGuardian.address,
|
|
69
|
+
displayName: sourceGuardian.displayName ?? null,
|
|
70
|
+
channelType: sourceGuardian.channelType,
|
|
71
|
+
source: "source-channel-contact",
|
|
72
|
+
};
|
|
73
|
+
} else if (vellumGuardian && !(requireAnchorPrincipal && !anchorPrincipalId)) {
|
|
74
|
+
// Source-channel resolution did not match the anchor → use the anchored
|
|
75
|
+
// vellum identity.
|
|
76
|
+
resolved = {
|
|
77
|
+
principalId: anchorPrincipalId ?? null,
|
|
78
|
+
address: vellumGuardian.address,
|
|
79
|
+
displayName: vellumGuardian.displayName ?? null,
|
|
80
|
+
channelType: "vellum",
|
|
81
|
+
source: "vellum-anchor",
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return resolved;
|
|
86
|
+
}
|
|
@@ -749,7 +749,7 @@ async function createCanonicalRequestForConfirmation(
|
|
|
749
749
|
});
|
|
750
750
|
|
|
751
751
|
if (trustContext && conversation) {
|
|
752
|
-
bridgeConfirmationRequestToGuardian({
|
|
752
|
+
await bridgeConfirmationRequestToGuardian({
|
|
753
753
|
canonicalRequest,
|
|
754
754
|
trustContext,
|
|
755
755
|
conversationId,
|
|
@@ -50,14 +50,21 @@
|
|
|
50
50
|
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
51
51
|
import { dirname, join } from "node:path";
|
|
52
52
|
|
|
53
|
+
import {
|
|
54
|
+
SSE_REPLAY_RING_AGE_LIMIT_MS,
|
|
55
|
+
SSE_REPLAY_RING_COUNT_LIMIT,
|
|
56
|
+
} from "../api/constants/sse-replay.js";
|
|
53
57
|
import { getWorkspaceDir } from "../util/platform.js";
|
|
54
58
|
import type { AssistantEvent } from "./assistant-event.js";
|
|
55
59
|
|
|
56
60
|
// ── Tunables ─────────────────────────────────────────────────────────
|
|
57
61
|
|
|
58
|
-
|
|
62
|
+
// Count and age bounds on the replay ring. Shared with the web client
|
|
63
|
+
// (via `@vellumai/assistant-api`) so its seq-gap tolerance is sized
|
|
64
|
+
// against the same numbers the daemon buffers against.
|
|
65
|
+
const RING_COUNT_LIMIT = SSE_REPLAY_RING_COUNT_LIMIT;
|
|
59
66
|
const RING_SIZE_LIMIT_BYTES = 256 * 1024;
|
|
60
|
-
const RING_AGE_LIMIT_MS =
|
|
67
|
+
const RING_AGE_LIMIT_MS = SSE_REPLAY_RING_AGE_LIMIT_MS;
|
|
61
68
|
|
|
62
69
|
/**
|
|
63
70
|
* Cap on how many conversations retain a persisted-seq entry. Unlike the
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import type { GuardianDelivery } from "@vellumai/gateway-client";
|
|
4
|
+
|
|
5
|
+
let mockGuardians: GuardianDelivery[] | null = null;
|
|
6
|
+
let authDisabled = false;
|
|
7
|
+
|
|
8
|
+
mock.module("../../../contacts/guardian-delivery-reader.js", () => ({
|
|
9
|
+
getGuardianDelivery: async () => mockGuardians,
|
|
10
|
+
// Real active-status selector so the auth gate enforces status==="active".
|
|
11
|
+
guardianForChannel: (list: GuardianDelivery[], channelType: string) =>
|
|
12
|
+
list.find((g) => g.channelType === channelType && g.status === "active"),
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
mock.module("../../../config/env.js", () => ({
|
|
16
|
+
isHttpAuthDisabled: () => authDisabled,
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
import { requireBoundGuardian } from "../require-bound-guardian.js";
|
|
20
|
+
import type { AuthContext } from "../types.js";
|
|
21
|
+
|
|
22
|
+
function ctx(actorPrincipalId?: string): AuthContext {
|
|
23
|
+
return {
|
|
24
|
+
subject: "sub",
|
|
25
|
+
principalType: "actor",
|
|
26
|
+
assistantId: "self",
|
|
27
|
+
actorPrincipalId,
|
|
28
|
+
scopeProfile: "actor_client_v1",
|
|
29
|
+
scopes: new Set(),
|
|
30
|
+
policyEpoch: 0,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function guardian(principalId: string): GuardianDelivery {
|
|
35
|
+
return {
|
|
36
|
+
channelType: "vellum",
|
|
37
|
+
contactId: "guardian-contact",
|
|
38
|
+
principalId,
|
|
39
|
+
address: principalId,
|
|
40
|
+
status: "active",
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
describe("requireBoundGuardian", () => {
|
|
45
|
+
beforeEach(() => {
|
|
46
|
+
mockGuardians = null;
|
|
47
|
+
authDisabled = false;
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test("admits the bound guardian", async () => {
|
|
51
|
+
mockGuardians = [guardian("vellum-principal-abc")];
|
|
52
|
+
const result = await requireBoundGuardian(ctx("vellum-principal-abc"));
|
|
53
|
+
expect(result).toBeNull();
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("denies a non-guardian actor", async () => {
|
|
57
|
+
mockGuardians = [guardian("vellum-principal-abc")];
|
|
58
|
+
const result = await requireBoundGuardian(ctx("vellum-principal-other"));
|
|
59
|
+
expect(result).not.toBeNull();
|
|
60
|
+
expect(result!.status).toBe(403);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("denies when actor principal is missing", async () => {
|
|
64
|
+
mockGuardians = [guardian("vellum-principal-abc")];
|
|
65
|
+
const result = await requireBoundGuardian(ctx(undefined));
|
|
66
|
+
expect(result).not.toBeNull();
|
|
67
|
+
expect(result!.status).toBe(403);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("fails closed on a null list (gateway unreachable)", async () => {
|
|
71
|
+
mockGuardians = null;
|
|
72
|
+
const result = await requireBoundGuardian(ctx("vellum-principal-abc"));
|
|
73
|
+
expect(result).not.toBeNull();
|
|
74
|
+
expect(result!.status).toBe(403);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("denies when no vellum guardian is bound", async () => {
|
|
78
|
+
mockGuardians = [];
|
|
79
|
+
const result = await requireBoundGuardian(ctx("vellum-principal-abc"));
|
|
80
|
+
expect(result).not.toBeNull();
|
|
81
|
+
expect(result!.status).toBe(403);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test("denies a non-active (revoked) vellum row matching the actor", async () => {
|
|
85
|
+
mockGuardians = [
|
|
86
|
+
{ ...guardian("vellum-principal-abc"), status: "revoked" },
|
|
87
|
+
];
|
|
88
|
+
const result = await requireBoundGuardian(ctx("vellum-principal-abc"));
|
|
89
|
+
expect(result).not.toBeNull();
|
|
90
|
+
expect(result!.status).toBe(403);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test("dev bypass admits when auth is disabled", async () => {
|
|
94
|
+
authDisabled = true;
|
|
95
|
+
mockGuardians = null;
|
|
96
|
+
const result = await requireBoundGuardian(ctx(undefined));
|
|
97
|
+
expect(result).toBeNull();
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
import { isHttpAuthDisabled } from "../../config/env.js";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
getGuardianDelivery,
|
|
4
|
+
guardianForChannel,
|
|
5
|
+
} from "../../contacts/guardian-delivery-reader.js";
|
|
3
6
|
import { httpError } from "../http-errors.js";
|
|
4
7
|
import type { AuthContext } from "./types.js";
|
|
5
8
|
|
|
6
9
|
/**
|
|
7
10
|
* Verify the actor from AuthContext is the bound guardian for the vellum channel.
|
|
8
|
-
*
|
|
11
|
+
* Sources the guardian from the gateway binding and fails closed when the
|
|
12
|
+
* gateway is unreachable (null list). Returns an error Response if not
|
|
13
|
+
* authorized, or null if allowed.
|
|
9
14
|
*/
|
|
10
|
-
export function requireBoundGuardian(
|
|
15
|
+
export async function requireBoundGuardian(
|
|
11
16
|
authContext: AuthContext,
|
|
12
|
-
): Response | null {
|
|
17
|
+
): Promise<Response | null> {
|
|
13
18
|
// Dev bypass: when auth is disabled, skip guardian binding check
|
|
14
19
|
// (mirrors enforcePolicy dev bypass in route-policy.ts)
|
|
15
20
|
if (isHttpAuthDisabled()) {
|
|
@@ -22,17 +27,22 @@ export function requireBoundGuardian(
|
|
|
22
27
|
403,
|
|
23
28
|
);
|
|
24
29
|
}
|
|
25
|
-
const
|
|
26
|
-
if (!
|
|
27
|
-
//
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
if (guardianResult.channel.address !== authContext.actorPrincipalId) {
|
|
30
|
+
const guardians = await getGuardianDelivery({ channelTypes: ["vellum"] });
|
|
31
|
+
if (!guardians) {
|
|
32
|
+
// Gateway unreachable — fail closed.
|
|
31
33
|
return httpError(
|
|
32
34
|
"FORBIDDEN",
|
|
33
35
|
"Actor is not the bound guardian for this channel",
|
|
34
36
|
403,
|
|
35
37
|
);
|
|
36
38
|
}
|
|
37
|
-
|
|
39
|
+
const guardian = guardianForChannel(guardians, "vellum");
|
|
40
|
+
if (guardian && guardian.principalId === authContext.actorPrincipalId) {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
return httpError(
|
|
44
|
+
"FORBIDDEN",
|
|
45
|
+
"Actor is not the bound guardian for this channel",
|
|
46
|
+
403,
|
|
47
|
+
);
|
|
38
48
|
}
|