@vellumai/assistant 0.9.0 → 0.10.0-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +18 -34
- package/bun.lock +7 -8
- package/docs/activation-funnel-telemetry.md +28 -22
- package/docs/architecture/security.md +29 -28
- package/docs/stt-provider-onboarding.md +3 -5
- package/docs/workflows-testing.md +13 -44
- package/docs/workflows.md +3 -5
- package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +47 -0
- package/node_modules/@vellumai/ces-client/src/rpc-client.ts +28 -5
- package/node_modules/@vellumai/environments/src/seeds.ts +2 -5
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +32 -6
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +119 -0
- package/node_modules/@vellumai/gateway-client/src/types.ts +15 -84
- package/openapi.yaml +976 -63
- package/package.json +2 -1
- package/scripts/sync-llm-catalog.ts +6 -15
- package/scripts/sync-web-search-catalog.ts +3 -11
- package/src/__tests__/access-request-card-view.test.ts +98 -0
- package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +72 -32
- package/src/__tests__/agent-loop-compaction-strip.test.ts +241 -0
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
- package/src/__tests__/agent-loop-output-hooks.test.ts +69 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +25 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -3
- package/src/__tests__/app-compiler.test.ts +15 -1
- package/src/__tests__/app-dir-path-guard.test.ts +0 -1
- package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
- package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
- package/src/__tests__/avatar-identity-sync.test.ts +2 -27
- package/src/__tests__/btw-routes.test.ts +6 -8
- package/src/__tests__/call-pointer-messages.test.ts +28 -0
- package/src/__tests__/cancel-clears-processing.test.ts +89 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -4
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
- package/src/__tests__/checker.test.ts +0 -3
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
- package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
- package/src/__tests__/config-loader-backfill.test.ts +268 -27
- package/src/__tests__/config-schema.test.ts +35 -0
- package/src/__tests__/config-watcher.test.ts +0 -18
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +0 -6
- package/src/__tests__/contacts-tools.test.ts +29 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +58 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-lifecycle.test.ts +7 -9
- package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
- package/src/__tests__/conversation-title-service.test.ts +62 -0
- package/src/__tests__/credential-broker.test.ts +449 -1
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
- package/src/__tests__/credential-execution-tools.test.ts +0 -1
- package/src/__tests__/credential-prompt-route.test.ts +4 -4
- package/src/__tests__/credential-routes.test.ts +360 -0
- package/src/__tests__/credential-security-invariants.test.ts +4 -13
- package/src/__tests__/disk-pressure-policy.test.ts +12 -0
- package/src/__tests__/disk-usage.test.ts +65 -0
- package/src/__tests__/dynamic-page-surface.test.ts +152 -1
- package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
- package/src/__tests__/gateway-flag-listener.test.ts +110 -1
- package/src/__tests__/gateway-only-guard.test.ts +3 -7
- package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
- package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
- package/src/__tests__/guardian-grant-minting.test.ts +3 -35
- package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
- package/src/__tests__/guardian-routing-state.test.ts +0 -1
- package/src/__tests__/headless-browser-mode.test.ts +10 -0
- package/src/__tests__/headless-browser-navigate.test.ts +8 -3
- package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
- package/src/__tests__/host-browser-proxy.test.ts +87 -0
- package/src/__tests__/identity-routes.test.ts +0 -189
- package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
- package/src/__tests__/injector-v3-suppression.test.ts +27 -20
- package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
- package/src/__tests__/invite-redemption-service.test.ts +4 -7
- package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
- package/src/__tests__/llm-catalog-parity.test.ts +30 -23
- package/src/__tests__/llm-resolver.test.ts +70 -24
- package/src/__tests__/llm-schema.test.ts +1 -0
- package/src/__tests__/managed-profile-guard.test.ts +163 -4
- package/src/__tests__/mcp-health-check.test.ts +6 -7
- package/src/__tests__/media-stream-server-integration.test.ts +317 -13
- package/src/__tests__/oauth-provider-seed-logos.test.ts +4 -6
- package/src/__tests__/onboarding-persona-write.test.ts +1 -1
- package/src/__tests__/path-policy.test.ts +34 -0
- package/src/__tests__/persona-resolver.test.ts +49 -14
- package/src/__tests__/plugin-api-model-profiles.test.ts +178 -0
- package/src/__tests__/plugin-api-provider.test.ts +24 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
- package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
- package/src/__tests__/reaction-persistence.test.ts +150 -29
- package/src/__tests__/registry.test.ts +2 -7
- package/src/__tests__/relay-server.test.ts +285 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
- package/src/__tests__/schedule-routes.test.ts +0 -30
- package/src/__tests__/schedule-tools.test.ts +2 -18
- package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
- package/src/__tests__/skill-execute-input.test.ts +51 -1
- package/src/__tests__/skill-runtime-path.test.ts +2 -3
- package/src/__tests__/skills.test.ts +51 -0
- package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
- package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
- package/src/__tests__/subagent-tools.test.ts +266 -0
- package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -0
- package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
- package/src/__tests__/title-generate-hook.test.ts +100 -3
- package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -29
- package/src/__tests__/token-manager.test.ts +519 -0
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
- package/src/__tests__/tool-audit-listener.test.ts +7 -7
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
- package/src/__tests__/tool-executor.test.ts +0 -79
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
- package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
- package/src/__tests__/trusted-contact-verification.test.ts +8 -10
- package/src/__tests__/twilio-routes.test.ts +81 -1
- package/src/__tests__/voice-invite-redemption.test.ts +2 -3
- package/src/__tests__/weak-open-model.test.ts +30 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
- package/src/__tests__/workspace-greetings.test.ts +152 -0
- package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
- package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
- package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
- package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
- package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
- package/src/agent/loop.ts +49 -29
- package/src/api/README.md +6 -6
- package/src/api/events/tool-result.ts +6 -0
- package/src/api/events/workflow-completed.ts +53 -0
- package/src/api/events/workflow-leaf-finished.ts +38 -0
- package/src/api/events/workflow-leaf-started.ts +35 -0
- package/src/api/events/workflow-progress.ts +32 -0
- package/src/api/events/workflow-started.ts +31 -0
- package/src/api/index.ts +40 -0
- package/src/api/responses/conversation-message.ts +28 -4
- package/src/api/responses/home.ts +26 -4
- package/src/api/responses/workflow-journal.ts +53 -0
- package/src/approvals/guardian-card-withdrawal.ts +145 -0
- package/src/approvals/guardian-decision-primitive.ts +26 -3
- package/src/approvals/guardian-request-resolvers.ts +183 -80
- package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
- package/src/calls/call-pointer-messages.ts +10 -4
- package/src/calls/channel-admission-reader.ts +104 -0
- package/src/calls/guardian-dispatch.ts +17 -45
- package/src/calls/media-stream-server.ts +84 -2
- package/src/calls/relay-access-wait.ts +1 -1
- package/src/calls/relay-server.ts +66 -0
- package/src/calls/relay-setup-router.ts +82 -1
- package/src/calls/twilio-routes.ts +17 -8
- package/src/calls/voice-session-bridge.ts +2 -2
- package/src/cli/commands/clients.ts +3 -0
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
- package/src/cli/commands/memory/index.ts +30 -0
- package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
- package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
- package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
- package/src/cli/commands/oauth/status.test.ts +36 -0
- package/src/cli/commands/oauth/status.ts +23 -3
- package/src/cli/commands/plugins.ts +197 -4
- package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +443 -0
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +295 -2
- package/src/cli/lib/diff-plugin.ts +346 -0
- package/src/cli/lib/inspect-plugin.ts +12 -1
- package/src/cli/lib/install-from-github.ts +105 -17
- package/src/cli/lib/merge-plugin-tree.ts +328 -0
- package/src/cli/lib/plugin-fingerprint.ts +14 -0
- package/src/cli/lib/plugin-surfaces.ts +104 -0
- package/src/cli/lib/upgrade-plugin.ts +298 -10
- package/src/cli/program.ts +2 -6
- package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
- package/src/config/assistant-feature-flags.ts +22 -7
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
- package/src/config/bundled-skills/messaging/SKILL.md +6 -4
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
- package/src/config/bundled-skills/subagent/SKILL.md +4 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
- package/src/config/bundled-skills/workflows/SKILL.md +14 -8
- package/src/config/bundled-tool-registry.ts +2 -7
- package/src/config/call-site-defaults.ts +15 -2
- package/src/config/feature-flag-registry.json +46 -31
- package/src/config/inference-profile-validation.ts +26 -0
- package/src/config/llm-resolver.ts +3 -0
- package/src/config/loader.ts +4 -0
- package/src/config/memory-v3-gate.ts +11 -0
- package/src/config/profile-order.ts +28 -0
- package/src/config/schema.ts +8 -6
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/channels.ts +11 -0
- package/src/config/schemas/elevenlabs.ts +0 -1
- package/src/config/schemas/llm.ts +31 -0
- package/src/config/schemas/memory-lifecycle.ts +3 -7
- package/src/config/schemas/memory-v3.ts +6 -0
- package/src/config/schemas/platform.ts +0 -8
- package/src/config/schemas/services.ts +18 -0
- package/src/config/seed-inference-profiles.ts +109 -44
- package/src/config/skills.ts +21 -0
- package/src/config/sync-gated-profiles.ts +220 -0
- package/src/contacts/contact-store.ts +89 -106
- package/src/contacts/contacts-write.ts +5 -22
- package/src/contacts/types.ts +0 -1
- package/src/context/compactor.ts +88 -54
- package/src/context/strip-injections.ts +58 -10
- package/src/context/token-estimator.ts +1 -1
- package/src/credential-execution/process-manager.ts +55 -14
- package/src/credential-execution/prompted-credential.ts +2 -3
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
- package/src/daemon/config-watcher.ts +0 -4
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
- package/src/daemon/conversation-agent-loop.ts +114 -22
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +3 -5
- package/src/daemon/conversation-process.ts +13 -5
- package/src/daemon/conversation-runtime-assembly.ts +13 -15
- package/src/daemon/conversation-slash.ts +2 -23
- package/src/daemon/conversation-surfaces.ts +26 -0
- package/src/daemon/conversation-tool-setup.ts +27 -14
- package/src/daemon/conversation.ts +66 -14
- package/src/daemon/disk-pressure-policy.ts +5 -3
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
- package/src/daemon/handlers/config-a2a.ts +0 -2
- package/src/daemon/handlers/config-channels.ts +15 -16
- package/src/daemon/handlers/config-slack-channel.ts +22 -3
- package/src/daemon/handlers/conversations.ts +107 -0
- package/src/daemon/host-browser-proxy.ts +41 -0
- package/src/daemon/lifecycle.ts +55 -27
- package/src/daemon/message-provenance.ts +2 -0
- package/src/daemon/message-types/contacts.ts +0 -1
- package/src/daemon/message-types/conversations.ts +3 -3
- package/src/daemon/message-types/sync.ts +0 -1
- package/src/daemon/message-types/web-activity.ts +7 -1
- package/src/daemon/message-types/workflows.ts +83 -1
- package/src/daemon/orphan-reaper.test.ts +0 -19
- package/src/daemon/orphan-reaper.ts +2 -24
- package/src/daemon/server.ts +0 -10
- package/src/daemon/tool-setup-types.ts +4 -0
- package/src/daemon/trust-context.ts +1 -1
- package/src/events/tool-audit-listener.ts +2 -2
- package/src/home/feed-source-enrichment.test.ts +151 -0
- package/src/home/feed-source-enrichment.ts +176 -0
- package/src/home/relationship-state.ts +2 -4
- package/src/instrument.ts +18 -6
- package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
- package/src/ipc/assistant-server.ts +37 -4
- package/src/ipc/gateway-flag-listener.ts +18 -2
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
- package/src/memory/__tests__/memory-retrospective-job.test.ts +229 -401
- package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
- package/src/memory/auth-fallback-events-store.ts +2 -2
- package/src/memory/auto-analysis-enqueue.ts +3 -5
- package/src/memory/bookmark-crud.ts +1 -2
- package/src/memory/canonical-guardian-store.ts +39 -1
- package/src/memory/conversation-crud.ts +9 -4
- package/src/memory/conversation-key-store.ts +17 -2
- package/src/memory/conversation-title-service.ts +64 -7
- package/src/memory/db-init.ts +17 -17
- package/src/memory/embedding-backend.ts +38 -1
- package/src/memory/embedding-billing-breaker.ts +96 -0
- package/src/memory/jobs-store.ts +25 -13
- package/src/memory/jobs-worker.ts +54 -1
- package/src/memory/lifecycle-events-store.ts +2 -2
- package/src/memory/memory-retrospective-constants.ts +4 -4
- package/src/memory/memory-retrospective-enqueue.ts +31 -6
- package/src/memory/memory-retrospective-job.ts +28 -227
- package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
- package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
- package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +72 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
- package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
- package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
- package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
- package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
- package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +341 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/run-migrations.ts +41 -0
- package/src/memory/migrations/validate-migration-state.ts +1 -1
- package/src/memory/onboarding-events-store.ts +3 -3
- package/src/memory/schema/contacts.ts +0 -5
- package/src/memory/skill-loaded-events-store.test.ts +7 -15
- package/src/memory/skill-loaded-events-store.ts +2 -2
- package/src/memory/tool-executed-events-store.test.ts +7 -7
- package/src/memory/turn-trace-store.test.ts +736 -0
- package/src/memory/turn-trace-store.ts +364 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
- package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
- package/src/memory/v2/consolidation-job.ts +2 -2
- package/src/memory/v2/skill-content.ts +25 -7
- package/src/memory/v2/skill-store.ts +7 -1
- package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
- package/src/memory/v3-eval/eval-packets.ts +546 -0
- package/src/messaging/providers/slack/adapter.ts +1 -1
- package/src/messaging/providers/slack/api.ts +31 -0
- package/src/messaging/providers/slack/send.test.ts +114 -2
- package/src/messaging/providers/slack/send.ts +30 -7
- package/src/messaging/providers/slack/withdraw.test.ts +200 -0
- package/src/messaging/providers/slack/withdraw.ts +161 -0
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/access-request-copy.ts +72 -59
- package/src/notifications/adapters/shared.ts +29 -0
- package/src/notifications/adapters/slack.ts +58 -103
- package/src/notifications/adapters/telegram.ts +2 -20
- package/src/notifications/approval-card-data.ts +333 -0
- package/src/notifications/broadcaster.ts +16 -3
- package/src/notifications/canonical-delivery-recorder.ts +139 -0
- package/src/notifications/copy-composer.ts +3 -3
- package/src/notifications/decision-engine.ts +4 -2
- package/src/notifications/destination-resolver.ts +4 -6
- package/src/notifications/guardian-question-mode.ts +10 -0
- package/src/notifications/home-feed-side-effect.ts +7 -16
- package/src/notifications/notification-utils.ts +19 -20
- package/src/notifications/signal.ts +79 -43
- package/src/notifications/types.ts +98 -121
- package/src/oauth/AGENTS.md +5 -24
- package/src/permissions/checker.test.ts +51 -0
- package/src/permissions/checker.ts +185 -26
- package/src/permissions/ipc-risk-types.ts +24 -0
- package/src/permissions/question-prompter.test.ts +27 -0
- package/src/permissions/question-prompter.ts +4 -0
- package/src/platform/client.test.ts +119 -0
- package/src/platform/client.ts +66 -0
- package/src/platform/consent-cache.test.ts +267 -0
- package/src/platform/consent-cache.ts +174 -0
- package/src/plugin-api/constants.ts +1 -1
- package/src/plugin-api/index.ts +33 -1
- package/src/plugin-api/model-profiles.ts +33 -0
- package/src/plugin-api/types.ts +50 -2
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
- package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
- package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
- package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
- package/src/plugins/defaults/advisor/config.ts +21 -0
- package/src/plugins/defaults/advisor/consult.ts +93 -0
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/advisor/package.json +14 -0
- package/src/plugins/defaults/advisor/steering.ts +67 -0
- package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
- package/src/plugins/defaults/advisor/transcript.ts +76 -0
- package/src/plugins/defaults/index.ts +60 -0
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +129 -9
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +144 -11
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +276 -0
- package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
- package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
- package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +3 -13
- package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
- package/src/prompts/persona-resolver.ts +14 -4
- package/src/prompts/templates/system-sections.ts +7 -2
- package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
- package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
- package/src/providers/__tests__/retry-callsite.test.ts +176 -0
- package/src/providers/atlascloud/client.ts +85 -0
- package/src/providers/fetch-provider-catalog.ts +85 -0
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/model-catalog.ts +58 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
- package/src/providers/openai/chat-completions-provider.ts +7 -0
- package/src/providers/openai/responses-provider.ts +10 -0
- package/src/providers/provider-send-message.ts +11 -3
- package/src/providers/retry.ts +53 -12
- package/src/providers/search-provider-catalog.ts +10 -0
- package/src/providers/weak-open-model.ts +22 -0
- package/src/runtime/AGENTS.md +0 -1
- package/src/runtime/__tests__/agent-wake.test.ts +181 -0
- package/src/runtime/__tests__/client-health.test.ts +44 -0
- package/src/runtime/access-request-helper.ts +21 -53
- package/src/runtime/actor-trust-resolver.ts +59 -63
- package/src/runtime/agent-wake.ts +52 -0
- package/src/runtime/assistant-event-hub.ts +18 -4
- package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
- package/src/runtime/auth/require-bound-guardian.ts +1 -4
- package/src/runtime/btw-sidechain.ts +3 -6
- package/src/runtime/capabilities.test.ts +120 -0
- package/src/runtime/capabilities.ts +197 -0
- package/src/runtime/channel-approval-types.ts +22 -45
- package/src/runtime/channel-invite-transports/telegram.ts +4 -4
- package/src/runtime/channel-retry-sweep.ts +1 -0
- package/src/runtime/channel-verification-service.ts +3 -3
- package/src/runtime/client-health.ts +26 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
- package/src/runtime/effective-capabilities.test.ts +128 -0
- package/src/runtime/effective-capabilities.ts +84 -0
- package/src/runtime/guardian-reply-router.ts +106 -21
- package/src/runtime/invite-redemption-service.ts +9 -25
- package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
- package/src/runtime/migrations/vbundle-builder.ts +49 -20
- package/src/runtime/pending-interactions.ts +15 -0
- package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +240 -1
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
- package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
- package/src/runtime/routes/browser-tabs-routes.ts +9 -0
- package/src/runtime/routes/btw-routes.ts +1 -27
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
- package/src/runtime/routes/client-routes.ts +10 -0
- package/src/runtime/routes/contact-routes.ts +31 -8
- package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
- package/src/runtime/routes/conversation-management-routes.ts +80 -1
- package/src/runtime/routes/conversation-query-routes.ts +68 -22
- package/src/runtime/routes/conversation-routes.ts +39 -14
- package/src/runtime/routes/credential-routes.ts +40 -16
- package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
- package/src/runtime/routes/events-routes.ts +1 -3
- package/src/runtime/routes/guardian-approval-interception.ts +14 -73
- package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
- package/src/runtime/routes/home-feed-routes.ts +8 -3
- package/src/runtime/routes/identity-routes.ts +1 -296
- package/src/runtime/routes/inbound-message-handler.ts +214 -228
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +89 -7
- package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
- package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
- package/src/runtime/routes/integrations/slack/channel.ts +36 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
- package/src/runtime/routes/mcp-auth-routes.ts +233 -41
- package/src/runtime/routes/memory-eval-routes.ts +87 -0
- package/src/runtime/routes/notification-routes.ts +122 -133
- package/src/runtime/routes/platform-routes.ts +2 -2
- package/src/runtime/routes/plugins-routes.ts +202 -3
- package/src/runtime/routes/schedule-routes.ts +0 -22
- package/src/runtime/routes/secret-routes.ts +10 -0
- package/src/runtime/routes/surface-action-routes.ts +2 -1
- package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
- package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
- package/src/runtime/routes/workflow-routes.test.ts +229 -44
- package/src/runtime/routes/workflow-routes.ts +131 -29
- package/src/runtime/routes/workspace-greetings.ts +55 -0
- package/src/runtime/sync/resource-sync-events.ts +1 -11
- package/src/runtime/tool-grant-request-helper.ts +18 -16
- package/src/runtime/trust-context-resolver.ts +8 -5
- package/src/schedule/inference-profile.ts +2 -14
- package/src/schedule/schedule-store.ts +1 -1
- package/src/schedule/scheduler-types.ts +5 -1
- package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
- package/src/security/secret-patterns.ts +3 -0
- package/src/subagent/manager.ts +17 -4
- package/src/subagent/types.ts +6 -0
- package/src/telemetry/trace-collection-policy.test.ts +28 -0
- package/src/telemetry/trace-collection-policy.ts +30 -0
- package/src/telemetry/types.ts +89 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
- package/src/telemetry/usage-telemetry-reporter.ts +148 -41
- package/src/tools/AGENTS.md +3 -3
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
- package/src/tools/browser/browser-execution.ts +30 -19
- package/src/tools/document/document-tool.ts +2 -3
- package/src/tools/executor.ts +5 -3
- package/src/tools/host-terminal/host-shell.ts +5 -4
- package/src/tools/memory/register.ts +2 -2
- package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
- package/src/tools/network/__tests__/web-search.test.ts +143 -0
- package/src/tools/network/web-fetch.ts +372 -1
- package/src/tools/network/web-search-error.ts +1 -1
- package/src/tools/network/web-search.ts +213 -10
- package/src/tools/permission-checker.ts +4 -3
- package/src/tools/registry.ts +20 -0
- package/src/tools/schedule/create.ts +7 -12
- package/src/tools/schedule/update.ts +4 -11
- package/src/tools/shared/filesystem/path-policy.ts +39 -13
- package/src/tools/side-effects.ts +2 -17
- package/src/tools/skills/execute.ts +33 -0
- package/src/tools/subagent/spawn.ts +61 -12
- package/src/tools/terminal/shell.ts +10 -4
- package/src/tools/tool-approval-handler.ts +18 -13
- package/src/tools/tool-manifest.ts +0 -2
- package/src/tools/types.ts +9 -0
- package/src/tools/ui-surface/definitions.ts +64 -3
- package/src/tools/verification-control-plane-policy.ts +3 -1
- package/src/tools/workflows/run-workflow.test.ts +8 -18
- package/src/tools/workflows/run-workflow.ts +1 -0
- package/src/util/disk-usage.ts +78 -23
- package/src/util/platform.ts +10 -3
- package/src/watcher/telemetry.ts +2 -2
- package/src/workflows/capabilities.ts +2 -3
- package/src/workflows/engine.test.ts +175 -1
- package/src/workflows/engine.ts +82 -0
- package/src/workflows/journal-store.test.ts +70 -0
- package/src/workflows/journal-store.ts +18 -3
- package/src/workflows/run-manager.test.ts +171 -28
- package/src/workflows/run-manager.ts +66 -24
- package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
- package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
- package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
- package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/__tests__/app-control-no-global-cgevent.test.ts +0 -98
- package/src/__tests__/credential-security-e2e.test.ts +0 -362
- package/src/__tests__/credential-vault-unit.test.ts +0 -1528
- package/src/__tests__/credential-vault.test.ts +0 -1706
- package/src/__tests__/identity-intro-cache.test.ts +0 -315
- package/src/__tests__/secret-onetime-send.test.ts +0 -182
- package/src/cli/commands/__tests__/task.test.ts +0 -914
- package/src/cli/commands/task.ts +0 -771
- package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
- package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
- package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
- package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
- package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
- package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
- package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
- package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
- package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
- package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
- package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
- package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
- package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
- package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
- package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
- package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
- package/src/memory/preloaded-apps.ts +0 -116
- package/src/notifications/tool-approval-copy.ts +0 -142
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
- package/src/runtime/routes/identity-intro-cache.ts +0 -172
- package/src/tools/credentials/vault.ts +0 -712
|
@@ -105,6 +105,16 @@ export const SEARCH_PROVIDER_CATALOG: readonly SearchProviderCatalogEntry[] = [
|
|
|
105
105
|
fallbackOrder: 3,
|
|
106
106
|
privacyPolicyUrl: "https://tavily.com/privacy",
|
|
107
107
|
},
|
|
108
|
+
{
|
|
109
|
+
id: "firecrawl",
|
|
110
|
+
displayName: "Firecrawl",
|
|
111
|
+
kind: "byok",
|
|
112
|
+
apiKeyPrefix: "fc-...",
|
|
113
|
+
envVar: "FIRECRAWL_API_KEY",
|
|
114
|
+
secretKey: "firecrawl",
|
|
115
|
+
fallbackOrder: 4,
|
|
116
|
+
privacyPolicyUrl: "https://www.firecrawl.dev/privacy-policy",
|
|
117
|
+
},
|
|
108
118
|
];
|
|
109
119
|
|
|
110
120
|
/** Provider ids accepted by the web-search config schema. */
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Family-level classification of "weak open models" — open-weight models
|
|
3
|
+
* (Kimi, DeepSeek, MiniMax, GLM) that disregard static instructions and have
|
|
4
|
+
* capability gaps that capable models (Claude, GPT) do not. Used by harness
|
|
5
|
+
* levers that coach or redirect these models without touching capable-model
|
|
6
|
+
* behavior: the task-progress-nudge plugin and the empty-dynamic_page surface
|
|
7
|
+
* redirect.
|
|
8
|
+
*
|
|
9
|
+
* Family-level matching spans provider naming conventions: OpenRouter
|
|
10
|
+
* `moonshotai/kimi-k2.6`, `deepseek/deepseek-chat`, `minimax/minimax-m3`;
|
|
11
|
+
* Fireworks `accounts/fireworks/models/minimax-m3`, `kimi-k2p6`. Extend the
|
|
12
|
+
* pattern as other open models show the same gaps.
|
|
13
|
+
*
|
|
14
|
+
* Distinct from exploration-drift's narrower `LOOP_PRONE_MODEL_PATTERN`, which
|
|
15
|
+
* targets specific loop-prone versions rather than the whole capability family.
|
|
16
|
+
*/
|
|
17
|
+
export const WEAK_OPEN_MODEL_PATTERN = /kimi|deepseek|minimax|glm/i;
|
|
18
|
+
|
|
19
|
+
/** True when `model` is a weak open model (see {@link WEAK_OPEN_MODEL_PATTERN}). */
|
|
20
|
+
export function isWeakOpenModel(model: string | null | undefined): boolean {
|
|
21
|
+
return typeof model === "string" && WEAK_OPEN_MODEL_PATTERN.test(model);
|
|
22
|
+
}
|
package/src/runtime/AGENTS.md
CHANGED
|
@@ -22,7 +22,6 @@ GET handlers must be safe and side-effect-free — they must not enqueue backgro
|
|
|
22
22
|
|
|
23
23
|
Accepted exceptions (stale-while-revalidate caches): a GET handler may kick off a bounded, fire-and-forget background refresh of a generated-content cache when no fresh cache exists, provided the handler itself stays read-only and returns immediately with cached/fallback copy, the refresh is single-flight (concurrent GETs share one regeneration), and a TTL bounds regeneration frequency. Current instances:
|
|
24
24
|
|
|
25
|
-
- `GET /v1/identity/intro` — refreshes the generated greeting cache; the background prompt may only depend on static identity/soul context plus caller-supplied local hour/minute.
|
|
26
25
|
- `GET /v1/home/feed` — refreshes the personalized home greeting and suggested-prompt caches via `revalidateHomeContentInBackground()`, which publishes `home_feed_updated` when fresh content lands so clients refetch. This is intentional: home content is generated on demand (when a user actually views Home), never at daemon startup or on a timer.
|
|
27
26
|
- `GET /v1/conversation-starters` — enqueues a `generate_conversation_starters` memory job when the starter set is stale, cooldown-gated and deduped against in-flight jobs.
|
|
28
27
|
|
|
@@ -244,6 +244,55 @@ const recordRequestLogCalls: Array<{
|
|
|
244
244
|
provider?: string;
|
|
245
245
|
callSite?: string;
|
|
246
246
|
}> = [];
|
|
247
|
+
const recordUsageCalls: Array<{
|
|
248
|
+
conversationId: string;
|
|
249
|
+
inputTokens: number;
|
|
250
|
+
outputTokens: number;
|
|
251
|
+
model: string;
|
|
252
|
+
actor: string;
|
|
253
|
+
cacheCreationInputTokens: number;
|
|
254
|
+
cacheReadInputTokens: number;
|
|
255
|
+
callSite: unknown;
|
|
256
|
+
overrideProfile: unknown;
|
|
257
|
+
forceOverrideProfile: unknown;
|
|
258
|
+
selectionSeed: unknown;
|
|
259
|
+
}> = [];
|
|
260
|
+
mock.module("../../daemon/conversation-usage.js", () => ({
|
|
261
|
+
recordUsage: (
|
|
262
|
+
ctx: { conversationId: string },
|
|
263
|
+
inputTokens: number,
|
|
264
|
+
outputTokens: number,
|
|
265
|
+
model: string,
|
|
266
|
+
_onEvent: unknown,
|
|
267
|
+
actor: string,
|
|
268
|
+
_requestId: unknown,
|
|
269
|
+
cacheCreationInputTokens = 0,
|
|
270
|
+
cacheReadInputTokens = 0,
|
|
271
|
+
_rawResponse?: unknown,
|
|
272
|
+
_llmCallCount?: number,
|
|
273
|
+
_contextWindow?: unknown,
|
|
274
|
+
attribution?: {
|
|
275
|
+
callSite?: unknown;
|
|
276
|
+
overrideProfile?: unknown;
|
|
277
|
+
forceOverrideProfile?: unknown;
|
|
278
|
+
selectionSeed?: unknown;
|
|
279
|
+
},
|
|
280
|
+
) => {
|
|
281
|
+
recordUsageCalls.push({
|
|
282
|
+
conversationId: ctx.conversationId,
|
|
283
|
+
inputTokens,
|
|
284
|
+
outputTokens,
|
|
285
|
+
model,
|
|
286
|
+
actor,
|
|
287
|
+
cacheCreationInputTokens,
|
|
288
|
+
cacheReadInputTokens,
|
|
289
|
+
callSite: attribution?.callSite ?? null,
|
|
290
|
+
overrideProfile: attribution?.overrideProfile ?? null,
|
|
291
|
+
forceOverrideProfile: attribution?.forceOverrideProfile,
|
|
292
|
+
selectionSeed: attribution?.selectionSeed,
|
|
293
|
+
});
|
|
294
|
+
},
|
|
295
|
+
}));
|
|
247
296
|
mock.module("../../memory/llm-request-log-store.js", () => ({
|
|
248
297
|
recordRequestLog: (
|
|
249
298
|
conversationId: string,
|
|
@@ -464,6 +513,7 @@ beforeEach(() => {
|
|
|
464
513
|
__resetWakeChainForTests();
|
|
465
514
|
wakeConvRegistry.clear();
|
|
466
515
|
recordRequestLogCalls.length = 0;
|
|
516
|
+
recordUsageCalls.length = 0;
|
|
467
517
|
mockGetOrCreateConversationCalls.length = 0;
|
|
468
518
|
mockResolverTarget = null;
|
|
469
519
|
mockGetConversationOverrideProfile = () => undefined;
|
|
@@ -1896,6 +1946,137 @@ describe("wakeAgentForOpportunity", () => {
|
|
|
1896
1946
|
expect(recordRequestLogCalls[0]?.callSite).toBe("memoryRetrospective");
|
|
1897
1947
|
});
|
|
1898
1948
|
|
|
1949
|
+
test("wake records LLM usage to the cost ledger, attributed to its call site", async () => {
|
|
1950
|
+
const usageEvent: AgentEvent = {
|
|
1951
|
+
type: "usage",
|
|
1952
|
+
inputTokens: 100,
|
|
1953
|
+
outputTokens: 5,
|
|
1954
|
+
model: "test-model",
|
|
1955
|
+
actualProvider: "test-provider",
|
|
1956
|
+
providerDurationMs: 10,
|
|
1957
|
+
cacheCreationInputTokens: 7,
|
|
1958
|
+
cacheReadInputTokens: 11,
|
|
1959
|
+
rawRequest: { request: "retrospective wake" },
|
|
1960
|
+
rawResponse: { response: "real reply" },
|
|
1961
|
+
};
|
|
1962
|
+
const conversation = makeWakeConversation({
|
|
1963
|
+
baseline: [{ role: "user", content: [{ type: "text", text: "hi" }] }],
|
|
1964
|
+
scriptedEvents: [usageEvent],
|
|
1965
|
+
scriptedAssistant: {
|
|
1966
|
+
role: "assistant",
|
|
1967
|
+
content: [{ type: "text", text: "real reply" }],
|
|
1968
|
+
},
|
|
1969
|
+
});
|
|
1970
|
+
|
|
1971
|
+
await wakeAgentForOpportunity(
|
|
1972
|
+
{
|
|
1973
|
+
conversationId: conversation.conversationId,
|
|
1974
|
+
hint: "do reply",
|
|
1975
|
+
source: "unit-test",
|
|
1976
|
+
callSite: "memoryRetrospective",
|
|
1977
|
+
},
|
|
1978
|
+
{ resolveTarget: async () => conversation },
|
|
1979
|
+
);
|
|
1980
|
+
|
|
1981
|
+
// A wake-driven LLM call records a usage row attributed to its
|
|
1982
|
+
// conversation, so its cost reaches the ledger.
|
|
1983
|
+
expect(recordUsageCalls).toHaveLength(1);
|
|
1984
|
+
expect(recordUsageCalls[0]).toMatchObject({
|
|
1985
|
+
conversationId: conversation.conversationId,
|
|
1986
|
+
inputTokens: 100,
|
|
1987
|
+
outputTokens: 5,
|
|
1988
|
+
model: "test-model",
|
|
1989
|
+
actor: "main_agent",
|
|
1990
|
+
cacheCreationInputTokens: 7,
|
|
1991
|
+
cacheReadInputTokens: 11,
|
|
1992
|
+
callSite: "memoryRetrospective",
|
|
1993
|
+
// Seed the dispatch path used for mix-arm resolution.
|
|
1994
|
+
selectionSeed: conversation.conversationId,
|
|
1995
|
+
});
|
|
1996
|
+
});
|
|
1997
|
+
|
|
1998
|
+
test("forced-profile wake records usage under the forced profile, not the call site", async () => {
|
|
1999
|
+
const usageEvent: AgentEvent = {
|
|
2000
|
+
type: "usage",
|
|
2001
|
+
inputTokens: 100,
|
|
2002
|
+
outputTokens: 5,
|
|
2003
|
+
model: "test-model",
|
|
2004
|
+
actualProvider: "test-provider",
|
|
2005
|
+
providerDurationMs: 10,
|
|
2006
|
+
rawRequest: { request: "forced wake" },
|
|
2007
|
+
rawResponse: { response: "real reply" },
|
|
2008
|
+
};
|
|
2009
|
+
const conversation = makeWakeConversation({
|
|
2010
|
+
baseline: [{ role: "user", content: [{ type: "text", text: "hi" }] }],
|
|
2011
|
+
scriptedEvents: [usageEvent],
|
|
2012
|
+
scriptedAssistant: {
|
|
2013
|
+
role: "assistant",
|
|
2014
|
+
content: [{ type: "text", text: "real reply" }],
|
|
2015
|
+
},
|
|
2016
|
+
});
|
|
2017
|
+
|
|
2018
|
+
await wakeAgentForOpportunity(
|
|
2019
|
+
{
|
|
2020
|
+
conversationId: conversation.conversationId,
|
|
2021
|
+
hint: "do reply",
|
|
2022
|
+
source: "unit-test",
|
|
2023
|
+
callSite: "memoryRetrospective",
|
|
2024
|
+
// Stand-in for the source conversation's profile, floated above the
|
|
2025
|
+
// call-site profile (fork retrospectives). `recordUsage` is mocked, so
|
|
2026
|
+
// this value is only threaded through and asserted — not resolved.
|
|
2027
|
+
forceOverrideProfile: "source-profile",
|
|
2028
|
+
},
|
|
2029
|
+
{ resolveTarget: async () => conversation },
|
|
2030
|
+
);
|
|
2031
|
+
|
|
2032
|
+
expect(recordUsageCalls).toHaveLength(1);
|
|
2033
|
+
expect(recordUsageCalls[0]).toMatchObject({
|
|
2034
|
+
overrideProfile: "source-profile",
|
|
2035
|
+
forceOverrideProfile: true,
|
|
2036
|
+
selectionSeed: conversation.conversationId,
|
|
2037
|
+
});
|
|
2038
|
+
});
|
|
2039
|
+
|
|
2040
|
+
test("silent no-op wake still records usage even though its request log is dropped", async () => {
|
|
2041
|
+
const usageEvent: AgentEvent = {
|
|
2042
|
+
type: "usage",
|
|
2043
|
+
inputTokens: 100,
|
|
2044
|
+
outputTokens: 5,
|
|
2045
|
+
model: "test-model",
|
|
2046
|
+
actualProvider: "test-provider",
|
|
2047
|
+
providerDurationMs: 10,
|
|
2048
|
+
rawRequest: { request: "no-op wake" },
|
|
2049
|
+
rawResponse: { response: "no output" },
|
|
2050
|
+
};
|
|
2051
|
+
const conversation = makeWakeConversation({
|
|
2052
|
+
baseline: [{ role: "user", content: [{ type: "text", text: "hi" }] }],
|
|
2053
|
+
scriptedEvents: [usageEvent],
|
|
2054
|
+
// Empty assistant text → silent no-op, so the request log is dropped.
|
|
2055
|
+
scriptedAssistant: {
|
|
2056
|
+
role: "assistant",
|
|
2057
|
+
content: [{ type: "text", text: "" }],
|
|
2058
|
+
},
|
|
2059
|
+
});
|
|
2060
|
+
|
|
2061
|
+
await wakeAgentForOpportunity(
|
|
2062
|
+
{
|
|
2063
|
+
conversationId: conversation.conversationId,
|
|
2064
|
+
hint: "consider doing nothing",
|
|
2065
|
+
source: "unit-test",
|
|
2066
|
+
},
|
|
2067
|
+
{ resolveTarget: async () => conversation },
|
|
2068
|
+
);
|
|
2069
|
+
|
|
2070
|
+
// Silent wake drops its request log, but the call still cost money.
|
|
2071
|
+
expect(recordRequestLogCalls).toHaveLength(0);
|
|
2072
|
+
expect(recordUsageCalls).toHaveLength(1);
|
|
2073
|
+
expect(recordUsageCalls[0]).toMatchObject({
|
|
2074
|
+
conversationId: conversation.conversationId,
|
|
2075
|
+
inputTokens: 100,
|
|
2076
|
+
outputTokens: 5,
|
|
2077
|
+
});
|
|
2078
|
+
});
|
|
2079
|
+
|
|
1899
2080
|
test("non-serializable usage payload does not abort the wake", async () => {
|
|
1900
2081
|
// Circular reference in rawRequest — JSON.stringify throws on this.
|
|
1901
2082
|
// Serialization must happen inside persistLog's try/catch so the
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { isClientDegraded } from "../client-health.js";
|
|
4
|
+
|
|
5
|
+
const HEARTBEAT_MS = 7_000;
|
|
6
|
+
|
|
7
|
+
describe("isClientDegraded", () => {
|
|
8
|
+
test("fresh connection is not degraded", () => {
|
|
9
|
+
const now = new Date(1_000_000);
|
|
10
|
+
const lastActiveAt = now; // just connected
|
|
11
|
+
expect(isClientDegraded(lastActiveAt, now, HEARTBEAT_MS)).toBe(false);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
test("actively heartbeating connection is not degraded", () => {
|
|
15
|
+
const now = new Date(1_000_000);
|
|
16
|
+
const lastActiveAt = new Date(now.getTime() - 2_000); // heartbeat 2s ago
|
|
17
|
+
expect(isClientDegraded(lastActiveAt, now, HEARTBEAT_MS)).toBe(false);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test("never-heartbeating connection that has gone stale is degraded", () => {
|
|
21
|
+
const now = new Date(1_000_000);
|
|
22
|
+
const lastActiveAt = new Date(now.getTime() - 30_000); // 30s since any activity
|
|
23
|
+
expect(isClientDegraded(lastActiveAt, now, HEARTBEAT_MS)).toBe(true);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test("heartbeated then froze is degraded (stale relative to now, not connectedAt)", () => {
|
|
27
|
+
const now = new Date(1_000_000);
|
|
28
|
+
// Connected long ago, heartbeated for a while, then stopped an hour ago.
|
|
29
|
+
const lastActiveAt = new Date(now.getTime() - 3_600_000);
|
|
30
|
+
expect(isClientDegraded(lastActiveAt, now, HEARTBEAT_MS)).toBe(true);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
test("boundary: just under 2 heartbeat intervals stale is not degraded", () => {
|
|
34
|
+
const now = new Date(1_000_000);
|
|
35
|
+
const lastActiveAt = new Date(now.getTime() - (2 * HEARTBEAT_MS - 1));
|
|
36
|
+
expect(isClientDegraded(lastActiveAt, now, HEARTBEAT_MS)).toBe(false);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("boundary: just over 2 heartbeat intervals stale is degraded", () => {
|
|
40
|
+
const now = new Date(1_000_000);
|
|
41
|
+
const lastActiveAt = new Date(now.getTime() - (2 * HEARTBEAT_MS + 1));
|
|
42
|
+
expect(isClientDegraded(lastActiveAt, now, HEARTBEAT_MS)).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
@@ -15,33 +15,20 @@ import type { ChannelId } from "../channels/types.js";
|
|
|
15
15
|
import { findGuardianForChannel } from "../contacts/contact-store.js";
|
|
16
16
|
import type { ChannelStatus } from "../contacts/types.js";
|
|
17
17
|
import {
|
|
18
|
-
createCanonicalGuardianDelivery,
|
|
19
18
|
createCanonicalGuardianRequest,
|
|
20
19
|
listCanonicalGuardianRequests,
|
|
21
|
-
updateCanonicalGuardianDelivery,
|
|
22
20
|
} from "../memory/canonical-guardian-store.js";
|
|
21
|
+
import {
|
|
22
|
+
recordApprovalCardDelivery,
|
|
23
|
+
recordGuardianRequestDeliveries,
|
|
24
|
+
} from "../notifications/canonical-delivery-recorder.js";
|
|
23
25
|
import { emitNotificationSignal } from "../notifications/emit-signal.js";
|
|
24
|
-
import type {
|
|
25
|
-
GuardianResolutionSource,
|
|
26
|
-
NotificationSourceChannel,
|
|
27
|
-
} from "../notifications/signal.js";
|
|
28
|
-
import type { NotificationDeliveryResult } from "../notifications/types.js";
|
|
26
|
+
import type { GuardianResolutionSource } from "../notifications/signal.js";
|
|
29
27
|
import { getLogger } from "../util/logger.js";
|
|
30
28
|
import { GUARDIAN_APPROVAL_TTL_MS } from "./routes/channel-route-shared.js";
|
|
31
29
|
|
|
32
30
|
const log = getLogger("access-request-helper");
|
|
33
31
|
|
|
34
|
-
function applyDeliveryStatus(
|
|
35
|
-
deliveryId: string,
|
|
36
|
-
result: NotificationDeliveryResult,
|
|
37
|
-
): void {
|
|
38
|
-
if (result.status === "sent") {
|
|
39
|
-
updateCanonicalGuardianDelivery(deliveryId, { status: "sent" });
|
|
40
|
-
return;
|
|
41
|
-
}
|
|
42
|
-
updateCanonicalGuardianDelivery(deliveryId, { status: "failed" });
|
|
43
|
-
}
|
|
44
|
-
|
|
45
32
|
// ---------------------------------------------------------------------------
|
|
46
33
|
// Types
|
|
47
34
|
// ---------------------------------------------------------------------------
|
|
@@ -127,7 +114,7 @@ export function notifyGuardianOfAccessRequest(
|
|
|
127
114
|
sourceGuardian &&
|
|
128
115
|
sourceGuardian.contact.principalId === assistantGuardianPrincipalId
|
|
129
116
|
) {
|
|
130
|
-
guardianExternalUserId = sourceGuardian.channel.
|
|
117
|
+
guardianExternalUserId = sourceGuardian.channel.address;
|
|
131
118
|
guardianPrincipalId = sourceGuardian.contact.principalId;
|
|
132
119
|
guardianBindingChannel = sourceGuardian.channel.type;
|
|
133
120
|
guardianResolutionSource = "source-channel-contact";
|
|
@@ -136,8 +123,7 @@ export function notifyGuardianOfAccessRequest(
|
|
|
136
123
|
// Access requests always require a principal. If source-channel resolution
|
|
137
124
|
// did not match the assistant anchor, use the anchored vellum identity.
|
|
138
125
|
if (!guardianPrincipalId && vellumGuardian) {
|
|
139
|
-
guardianExternalUserId =
|
|
140
|
-
vellumGuardian.channel.externalUserId ?? guardianExternalUserId;
|
|
126
|
+
guardianExternalUserId = vellumGuardian.channel.address;
|
|
141
127
|
guardianPrincipalId = assistantGuardianPrincipalId ?? null;
|
|
142
128
|
guardianBindingChannel = guardianBindingChannel ?? "vellum";
|
|
143
129
|
guardianResolutionSource = "vellum-anchor";
|
|
@@ -200,7 +186,7 @@ export function notifyGuardianOfAccessRequest(
|
|
|
200
186
|
expiresAt: Date.now() + GUARDIAN_APPROVAL_TTL_MS,
|
|
201
187
|
});
|
|
202
188
|
|
|
203
|
-
let vellumDeliveryId: string |
|
|
189
|
+
let vellumDeliveryId: string | undefined;
|
|
204
190
|
// When the access request originates from a text channel with
|
|
205
191
|
// notification delivery support (Slack, Telegram) and the guardian was
|
|
206
192
|
// resolved via a verified same-channel contact, route the notification
|
|
@@ -219,7 +205,7 @@ export function notifyGuardianOfAccessRequest(
|
|
|
219
205
|
|
|
220
206
|
void emitNotificationSignal({
|
|
221
207
|
sourceEventName: "ingress.access_request",
|
|
222
|
-
sourceChannel
|
|
208
|
+
sourceChannel,
|
|
223
209
|
sourceContextId: `access-req-${sourceChannel}-${actorExternalId}`,
|
|
224
210
|
requiresConversation: true,
|
|
225
211
|
...(sameChannelOnly ? { routingIntent: "single_channel" as const } : {}),
|
|
@@ -250,44 +236,26 @@ export function notifyGuardianOfAccessRequest(
|
|
|
250
236
|
onConversationCreated: (info) => {
|
|
251
237
|
if (info.sourceEventName !== "ingress.access_request" || vellumDeliveryId)
|
|
252
238
|
return;
|
|
253
|
-
|
|
239
|
+
vellumDeliveryId = recordApprovalCardDelivery({
|
|
254
240
|
requestId: canonicalRequest.id,
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
});
|
|
258
|
-
vellumDeliveryId = delivery.id;
|
|
241
|
+
channel: "vellum",
|
|
242
|
+
conversationId: info.conversationId,
|
|
243
|
+
})?.id;
|
|
259
244
|
},
|
|
260
245
|
})
|
|
261
246
|
.then((signalResult) => {
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
destinationChannel: "vellum",
|
|
268
|
-
destinationConversationId: result.conversationId,
|
|
269
|
-
});
|
|
270
|
-
vellumDeliveryId = delivery.id;
|
|
271
|
-
}
|
|
272
|
-
applyDeliveryStatus(vellumDeliveryId, result);
|
|
273
|
-
continue;
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
const delivery = createCanonicalGuardianDelivery({
|
|
277
|
-
requestId: canonicalRequest.id,
|
|
278
|
-
destinationChannel: result.channel,
|
|
279
|
-
destinationChatId:
|
|
280
|
-
result.destination.length > 0 ? result.destination : undefined,
|
|
281
|
-
});
|
|
282
|
-
applyDeliveryStatus(delivery.id, result);
|
|
283
|
-
}
|
|
247
|
+
vellumDeliveryId = recordGuardianRequestDeliveries({
|
|
248
|
+
requestId: canonicalRequest.id,
|
|
249
|
+
deliveryResults: signalResult.deliveryResults,
|
|
250
|
+
vellumDeliveryId,
|
|
251
|
+
});
|
|
284
252
|
|
|
285
253
|
if (!vellumDeliveryId && !sameChannelOnly) {
|
|
286
|
-
|
|
254
|
+
recordApprovalCardDelivery({
|
|
287
255
|
requestId: canonicalRequest.id,
|
|
288
|
-
|
|
256
|
+
channel: "vellum",
|
|
257
|
+
status: "failed",
|
|
289
258
|
});
|
|
290
|
-
updateCanonicalGuardianDelivery(fallback.id, { status: "failed" });
|
|
291
259
|
log.warn(
|
|
292
260
|
{ requestId: canonicalRequest.id, reason: signalResult.reason },
|
|
293
261
|
"Notification pipeline did not produce a vellum delivery result for access request",
|
|
@@ -8,13 +8,17 @@
|
|
|
8
8
|
* Trust classifications:
|
|
9
9
|
* - `guardian`: sender matches the guardian contact's channel for this channel type.
|
|
10
10
|
* - `trusted_contact`: sender is an active contact channel (not the guardian).
|
|
11
|
-
* - `
|
|
11
|
+
* - `unverified_contact`: sender matches a contact channel that is pending or
|
|
12
|
+
* unverified — known to the guardian but not yet through verification.
|
|
13
|
+
* Treated identically to `trusted_contact` downstream; the distinction only
|
|
14
|
+
* matters at the admission floor (see channel admission policy).
|
|
15
|
+
* - `unknown`: sender has no matching contact, no identity could be
|
|
16
|
+
* established, or the contact's channel is blocked/revoked.
|
|
12
17
|
*/
|
|
13
18
|
|
|
14
19
|
import type { ChannelId } from "../channels/types.js";
|
|
15
20
|
import {
|
|
16
21
|
findContactByAddress,
|
|
17
|
-
findContactByChannelExternalId,
|
|
18
22
|
findGuardianForChannel,
|
|
19
23
|
} from "../contacts/contact-store.js";
|
|
20
24
|
import type { ContactChannel, ContactWithChannels } from "../contacts/types.js";
|
|
@@ -39,22 +43,35 @@ export type { TrustContext } from "../daemon/trust-context.js";
|
|
|
39
43
|
* - `'trusted_contact'`: The sender is an active contact with a channel
|
|
40
44
|
* (not the guardian). Trusted contacts can invoke tools but require
|
|
41
45
|
* guardian approval for sensitive operations.
|
|
46
|
+
* - `'unverified_contact'`: The sender matches a contact channel whose
|
|
47
|
+
* status is `pending` or `unverified` — known to the guardian but not yet
|
|
48
|
+
* verified. Treated identically to `trusted_contact` for every downstream
|
|
49
|
+
* capability/tool/approval decision; the distinction is admission-only.
|
|
42
50
|
* - `'unknown'`: The sender has no contact record, no identity could be
|
|
43
|
-
* established, or the sender is
|
|
51
|
+
* established, or the sender is a blocked/revoked contact. Unknown
|
|
44
52
|
* actors are fail-closed with no escalation path.
|
|
45
53
|
*/
|
|
46
|
-
export type TrustClass =
|
|
54
|
+
export type TrustClass =
|
|
55
|
+
| "guardian"
|
|
56
|
+
| "trusted_contact"
|
|
57
|
+
| "unverified_contact"
|
|
58
|
+
| "unknown";
|
|
47
59
|
|
|
48
|
-
/**
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
60
|
+
/**
|
|
61
|
+
* Trust-class ordinal used by the per-channel admission policy floor check.
|
|
62
|
+
* Higher rank = more trusted. Blocked/revoked never reach classification —
|
|
63
|
+
* their effective rank is 0 and is enforced by the inbound ACL stage's
|
|
64
|
+
* member-status short-circuit, not via this table.
|
|
65
|
+
*
|
|
66
|
+
* See `wave-b-plan.md` §2.4. Paired with `ADMISSION_FLOOR` from
|
|
67
|
+
* `@vellumai/gateway-client` — both tables move together.
|
|
68
|
+
*/
|
|
69
|
+
export const TRUST_CLASS_RANK: Record<TrustClass, number> = {
|
|
70
|
+
guardian: 4,
|
|
71
|
+
trusted_contact: 3,
|
|
72
|
+
unverified_contact: 2,
|
|
73
|
+
unknown: 1,
|
|
74
|
+
};
|
|
58
75
|
|
|
59
76
|
/**
|
|
60
77
|
* Fully resolved trust context from the actor trust resolver.
|
|
@@ -181,19 +198,13 @@ export function resolveActorTrust(
|
|
|
181
198
|
if (guardianResult) {
|
|
182
199
|
const { contact: guardianContact, channel: guardianChannel } =
|
|
183
200
|
guardianResult;
|
|
184
|
-
const canonicalGuardianId = guardianChannel.externalUserId
|
|
185
|
-
? canonicalizeInboundIdentity(
|
|
186
|
-
input.sourceChannel,
|
|
187
|
-
guardianChannel.externalUserId,
|
|
188
|
-
)
|
|
189
|
-
: null;
|
|
190
201
|
guardianBindingMatch = {
|
|
191
|
-
guardianExternalUserId: guardianChannel.
|
|
202
|
+
guardianExternalUserId: guardianChannel.address,
|
|
192
203
|
guardianDeliveryChatId: guardianChannel.externalChatId,
|
|
193
204
|
};
|
|
194
205
|
guardianPrincipalId = guardianContact.principalId ?? undefined;
|
|
195
206
|
isGuardian =
|
|
196
|
-
|
|
207
|
+
guardianChannel.address.toLowerCase() === canonicalSenderId.toLowerCase();
|
|
197
208
|
}
|
|
198
209
|
|
|
199
210
|
log.debug(
|
|
@@ -206,37 +217,18 @@ export function resolveActorTrust(
|
|
|
206
217
|
);
|
|
207
218
|
|
|
208
219
|
// --- Member lookup via contacts ---
|
|
209
|
-
// Primary path: match by externalUserId (populated after channel verification
|
|
210
|
-
// completes, or for channels registered via the verification upsert path).
|
|
211
|
-
// Fallback path: match by address (covers channels registered by the inbound
|
|
212
|
-
// name-capture flow, where address is set but externalUserId remains NULL
|
|
213
|
-
// until the DTMF challenge succeeds). Mirrors the gateway's OR-based lookup
|
|
214
|
-
// in ContactStore.getContactByPhoneNumber so the runtime's unverified-caller
|
|
215
|
-
// guard fires for pre-verification channels the gateway passes through.
|
|
216
220
|
let memberRecord: ActorTrustContext["memberRecord"] = null;
|
|
217
|
-
const
|
|
221
|
+
const byAddress = findContactByAddress(
|
|
218
222
|
input.sourceChannel,
|
|
219
223
|
canonicalSenderId,
|
|
220
224
|
);
|
|
221
|
-
const
|
|
225
|
+
const byAddressChannel = byAddress?.channels.find(
|
|
222
226
|
(ch) =>
|
|
223
227
|
ch.type === input.sourceChannel &&
|
|
224
|
-
ch.
|
|
228
|
+
ch.address.toLowerCase() === canonicalSenderId.toLowerCase(),
|
|
225
229
|
);
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
memberRecord = { contact: byExternalId, channel: byExternalIdChannel };
|
|
229
|
-
} else {
|
|
230
|
-
// Address fallback: catches channels where externalUserId is not yet set.
|
|
231
|
-
const byAddress = findContactByAddress(input.sourceChannel, canonicalSenderId);
|
|
232
|
-
const byAddressChannel = byAddress?.channels.find(
|
|
233
|
-
(ch) =>
|
|
234
|
-
ch.type === input.sourceChannel &&
|
|
235
|
-
ch.address?.toLowerCase() === canonicalSenderId.toLowerCase(),
|
|
236
|
-
);
|
|
237
|
-
if (byAddress && byAddressChannel) {
|
|
238
|
-
memberRecord = { contact: byAddress, channel: byAddressChannel };
|
|
239
|
-
}
|
|
230
|
+
if (byAddress && byAddressChannel) {
|
|
231
|
+
memberRecord = { contact: byAddress, channel: byAddressChannel };
|
|
240
232
|
}
|
|
241
233
|
|
|
242
234
|
log.debug(
|
|
@@ -244,23 +236,16 @@ export function resolveActorTrust(
|
|
|
244
236
|
channel: input.sourceChannel,
|
|
245
237
|
canonicalSenderId,
|
|
246
238
|
found: !!memberRecord,
|
|
247
|
-
via: memberRecord
|
|
239
|
+
via: memberRecord ? "address" : "none",
|
|
248
240
|
},
|
|
249
241
|
"trust-resolver member lookup",
|
|
250
242
|
);
|
|
251
243
|
|
|
252
244
|
// Only use member metadata when the record's channel identity matches the
|
|
253
245
|
// current sender to avoid misidentification in group chats.
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
const memberMatchesSender = memberRecord?.channel.externalUserId
|
|
258
|
-
? canonicalizeInboundIdentity(
|
|
259
|
-
input.sourceChannel,
|
|
260
|
-
memberRecord.channel.externalUserId,
|
|
261
|
-
) === canonicalSenderId
|
|
262
|
-
: (memberRecord?.channel.address?.toLowerCase() ===
|
|
263
|
-
canonicalSenderId.toLowerCase());
|
|
246
|
+
const memberMatchesSender =
|
|
247
|
+
memberRecord?.channel.address.toLowerCase() ===
|
|
248
|
+
canonicalSenderId.toLowerCase();
|
|
264
249
|
|
|
265
250
|
const memberDisplayName =
|
|
266
251
|
memberMatchesSender &&
|
|
@@ -281,12 +266,23 @@ export function resolveActorTrust(
|
|
|
281
266
|
let trustClass: TrustClass;
|
|
282
267
|
if (isGuardian) {
|
|
283
268
|
trustClass = "guardian";
|
|
284
|
-
} else if (
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
269
|
+
} else if (memberMatchesSender && memberRecord) {
|
|
270
|
+
const status = memberRecord.channel.status;
|
|
271
|
+
if (status === "active") {
|
|
272
|
+
trustClass = "trusted_contact";
|
|
273
|
+
} else if (status === "unverified" || status === "pending") {
|
|
274
|
+
// Pre-verification / awaiting-verification contacts get their own
|
|
275
|
+
// admission tier. Treated identically to trusted_contact for ALL
|
|
276
|
+
// downstream capability/tool/approval decisions; the distinction
|
|
277
|
+
// only matters at the channel admission floor.
|
|
278
|
+
trustClass = "unverified_contact";
|
|
279
|
+
} else {
|
|
280
|
+
// status === "blocked" or "revoked" → unknown. acl-enforcement
|
|
281
|
+
// re-checks resolvedMember.channel.status and emits the appropriate
|
|
282
|
+
// member_blocked / member_revoked reasons, so hard-deny semantics
|
|
283
|
+
// for these statuses are preserved end-to-end.
|
|
284
|
+
trustClass = "unknown";
|
|
285
|
+
}
|
|
290
286
|
} else {
|
|
291
287
|
trustClass = "unknown";
|
|
292
288
|
}
|