@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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { and, asc, desc, eq,
|
|
1
|
+
import { and, asc, desc, eq, like, sql } from "drizzle-orm";
|
|
2
2
|
import { v4 as uuid } from "uuid";
|
|
3
3
|
|
|
4
4
|
import type { ChannelId } from "../channels/types.js";
|
|
@@ -96,13 +96,12 @@ function parseContact(row: typeof contacts.$inferSelect): Contact {
|
|
|
96
96
|
id: row.id,
|
|
97
97
|
displayName: row.displayName,
|
|
98
98
|
notes: row.notes,
|
|
99
|
+
role: row.role,
|
|
99
100
|
lastInteraction: null,
|
|
100
101
|
interactionCount: 0,
|
|
101
102
|
createdAt: row.createdAt,
|
|
102
103
|
updatedAt: row.updatedAt,
|
|
103
|
-
|
|
104
|
-
contactType: (row.contactType as Contact["contactType"]) ?? "human",
|
|
105
|
-
principalId: row.principalId,
|
|
104
|
+
contactType: row.contactType,
|
|
106
105
|
userFile: row.userFile ?? null,
|
|
107
106
|
};
|
|
108
107
|
}
|
|
@@ -117,13 +116,7 @@ function parseChannel(
|
|
|
117
116
|
address: row.address,
|
|
118
117
|
isPrimary: row.isPrimary,
|
|
119
118
|
externalChatId: row.externalChatId,
|
|
120
|
-
status: row.status as ContactChannel["status"],
|
|
121
|
-
policy: row.policy as ContactChannel["policy"],
|
|
122
|
-
verifiedAt: row.verifiedAt,
|
|
123
|
-
verifiedVia: row.verifiedVia,
|
|
124
119
|
inviteId: row.inviteId,
|
|
125
|
-
revokedReason: row.revokedReason,
|
|
126
|
-
blockedReason: row.blockedReason,
|
|
127
120
|
lastSeenAt: row.lastSeenAt,
|
|
128
121
|
interactionCount: row.interactionCount,
|
|
129
122
|
lastInteraction: row.lastInteraction,
|
|
@@ -145,6 +138,8 @@ function getChannelsForContact(contactId: string): ContactChannel[] {
|
|
|
145
138
|
|
|
146
139
|
function withChannels(contact: Contact): ContactWithChannels {
|
|
147
140
|
const channels = getChannelsForContact(contact.id);
|
|
141
|
+
// INFO telemetry aggregated from channel rows (not ACL): sum interaction
|
|
142
|
+
// counts, take the most recent interaction across channels.
|
|
148
143
|
const interactionCount = channels.reduce(
|
|
149
144
|
(sum, ch) => sum + ch.interactionCount,
|
|
150
145
|
0,
|
|
@@ -225,7 +220,6 @@ export function upsertContact(params: {
|
|
|
225
220
|
notes?: string | null;
|
|
226
221
|
role?: ContactRole;
|
|
227
222
|
contactType?: ContactType;
|
|
228
|
-
principalId?: string | null;
|
|
229
223
|
userFile?: string | null;
|
|
230
224
|
channels?: SyncChannelData[];
|
|
231
225
|
/** When true, conflicting channels on other contacts are reassigned to this
|
|
@@ -260,11 +254,8 @@ export function upsertContact(params: {
|
|
|
260
254
|
updatedAt: now,
|
|
261
255
|
};
|
|
262
256
|
if (params.notes !== undefined) updateSet.notes = params.notes;
|
|
263
|
-
if (params.role !== undefined) updateSet.role = params.role;
|
|
264
257
|
if (params.contactType !== undefined)
|
|
265
258
|
updateSet.contactType = params.contactType;
|
|
266
|
-
if (params.principalId !== undefined)
|
|
267
|
-
updateSet.principalId = params.principalId;
|
|
268
259
|
if (params.userFile !== undefined) updateSet.userFile = params.userFile;
|
|
269
260
|
|
|
270
261
|
db.update(contacts)
|
|
@@ -298,11 +289,8 @@ export function upsertContact(params: {
|
|
|
298
289
|
updatedAt: now,
|
|
299
290
|
};
|
|
300
291
|
if (params.notes !== undefined) updateSet.notes = params.notes;
|
|
301
|
-
if (params.role !== undefined) updateSet.role = params.role;
|
|
302
292
|
if (params.contactType !== undefined)
|
|
303
293
|
updateSet.contactType = params.contactType;
|
|
304
|
-
if (params.principalId !== undefined)
|
|
305
|
-
updateSet.principalId = params.principalId;
|
|
306
294
|
if (params.userFile !== undefined) updateSet.userFile = params.userFile;
|
|
307
295
|
|
|
308
296
|
db.update(contacts)
|
|
@@ -319,35 +307,16 @@ export function upsertContact(params: {
|
|
|
319
307
|
|
|
320
308
|
// Create new contact
|
|
321
309
|
contactId = contactId ?? uuid();
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
resolvedUserFile = params.userFile;
|
|
327
|
-
} else if (params.principalId) {
|
|
328
|
-
const sibling = db
|
|
329
|
-
.select({ userFile: contacts.userFile })
|
|
330
|
-
.from(contacts)
|
|
331
|
-
.where(
|
|
332
|
-
and(
|
|
333
|
-
eq(contacts.principalId, params.principalId),
|
|
334
|
-
isNotNull(contacts.userFile),
|
|
335
|
-
),
|
|
336
|
-
)
|
|
337
|
-
.get();
|
|
338
|
-
resolvedUserFile =
|
|
339
|
-
sibling?.userFile ?? generateUserFileSlug(params.displayName);
|
|
340
|
-
} else {
|
|
341
|
-
resolvedUserFile = generateUserFileSlug(params.displayName);
|
|
342
|
-
}
|
|
310
|
+
const resolvedUserFile =
|
|
311
|
+
params.userFile !== undefined
|
|
312
|
+
? params.userFile
|
|
313
|
+
: generateUserFileSlug(params.displayName);
|
|
343
314
|
db.insert(contacts)
|
|
344
315
|
.values({
|
|
345
316
|
id: contactId,
|
|
346
317
|
displayName: params.displayName,
|
|
347
318
|
notes: params.notes ?? null,
|
|
348
|
-
role: params.role ?? "contact",
|
|
349
319
|
contactType: params.contactType ?? "human",
|
|
350
|
-
principalId: params.principalId ?? null,
|
|
351
320
|
userFile: resolvedUserFile,
|
|
352
321
|
createdAt: now,
|
|
353
322
|
updatedAt: now,
|
|
@@ -396,27 +365,12 @@ function syncChannels(
|
|
|
396
365
|
.get();
|
|
397
366
|
|
|
398
367
|
if (existing) {
|
|
399
|
-
// Preserve guardian blocks: if the channel is blocked, do not overwrite
|
|
400
|
-
// its status/policy — mirrors the guard in the cross-contact reassignment
|
|
401
|
-
// path so a blocked channel cannot be unblocked via a same-contact sync.
|
|
402
|
-
const isBlocked = existing.status === "blocked";
|
|
403
|
-
|
|
404
368
|
const updateSet: Record<string, unknown> = {};
|
|
405
369
|
// Self-heal legacy lowercased addresses to canonical form.
|
|
406
370
|
if (existing.address !== ch.address) updateSet.address = ch.address;
|
|
407
371
|
if (ch.isPrimary !== undefined) updateSet.isPrimary = ch.isPrimary;
|
|
408
372
|
if (ch.externalChatId !== undefined)
|
|
409
373
|
updateSet.externalChatId = ch.externalChatId;
|
|
410
|
-
if (!isBlocked) {
|
|
411
|
-
if (ch.status !== undefined) updateSet.status = ch.status;
|
|
412
|
-
if (ch.policy !== undefined) updateSet.policy = ch.policy;
|
|
413
|
-
if (ch.revokedReason !== undefined)
|
|
414
|
-
updateSet.revokedReason = ch.revokedReason;
|
|
415
|
-
if (ch.blockedReason !== undefined)
|
|
416
|
-
updateSet.blockedReason = ch.blockedReason;
|
|
417
|
-
}
|
|
418
|
-
if (ch.verifiedAt !== undefined) updateSet.verifiedAt = ch.verifiedAt;
|
|
419
|
-
if (ch.verifiedVia !== undefined) updateSet.verifiedVia = ch.verifiedVia;
|
|
420
374
|
if (ch.inviteId !== undefined) updateSet.inviteId = ch.inviteId;
|
|
421
375
|
|
|
422
376
|
if (Object.keys(updateSet).length > 0) {
|
|
@@ -434,11 +388,6 @@ function syncChannels(
|
|
|
434
388
|
|
|
435
389
|
if (conflicting) {
|
|
436
390
|
if (reassignConflicting) {
|
|
437
|
-
// Preserve guardian blocks: if the existing channel is blocked, do not
|
|
438
|
-
// overwrite its status/policy — a valid invite must not bypass an
|
|
439
|
-
// explicit guardian block on a different contact.
|
|
440
|
-
const isBlocked = conflicting.status === "blocked";
|
|
441
|
-
|
|
442
391
|
// Reassign the channel to the target contact. Used by invite redemption
|
|
443
392
|
// to bind a redeemer's existing channel identity to the invite's target.
|
|
444
393
|
const reassignSet: Record<string, unknown> = {
|
|
@@ -447,17 +396,6 @@ function syncChannels(
|
|
|
447
396
|
};
|
|
448
397
|
if (ch.externalChatId !== undefined)
|
|
449
398
|
reassignSet.externalChatId = ch.externalChatId;
|
|
450
|
-
if (!isBlocked) {
|
|
451
|
-
if (ch.status !== undefined) reassignSet.status = ch.status;
|
|
452
|
-
if (ch.policy !== undefined) reassignSet.policy = ch.policy;
|
|
453
|
-
if (ch.revokedReason !== undefined)
|
|
454
|
-
reassignSet.revokedReason = ch.revokedReason;
|
|
455
|
-
if (ch.blockedReason !== undefined)
|
|
456
|
-
reassignSet.blockedReason = ch.blockedReason;
|
|
457
|
-
}
|
|
458
|
-
if (ch.verifiedAt !== undefined) reassignSet.verifiedAt = ch.verifiedAt;
|
|
459
|
-
if (ch.verifiedVia !== undefined)
|
|
460
|
-
reassignSet.verifiedVia = ch.verifiedVia;
|
|
461
399
|
if (ch.inviteId !== undefined) reassignSet.inviteId = ch.inviteId;
|
|
462
400
|
|
|
463
401
|
db.update(contactChannels)
|
|
@@ -478,10 +416,6 @@ function syncChannels(
|
|
|
478
416
|
address: ch.address,
|
|
479
417
|
isPrimary: ch.isPrimary ?? false,
|
|
480
418
|
externalChatId: ch.externalChatId ?? null,
|
|
481
|
-
status: ch.status ?? "unverified",
|
|
482
|
-
policy: ch.policy ?? "allow",
|
|
483
|
-
verifiedAt: ch.verifiedAt ?? null,
|
|
484
|
-
verifiedVia: ch.verifiedVia ?? null,
|
|
485
419
|
inviteId: ch.inviteId ?? null,
|
|
486
420
|
createdAt: now,
|
|
487
421
|
updatedAt: now,
|
|
@@ -494,7 +428,6 @@ export function searchContacts(params: {
|
|
|
494
428
|
query?: string;
|
|
495
429
|
channelAddress?: string;
|
|
496
430
|
channelType?: string;
|
|
497
|
-
role?: ContactRole;
|
|
498
431
|
contactType?: ContactType;
|
|
499
432
|
limit?: number;
|
|
500
433
|
}): ContactWithChannels[] {
|
|
@@ -534,7 +467,6 @@ export function searchContacts(params: {
|
|
|
534
467
|
const contact = getContactInternal(id);
|
|
535
468
|
if (
|
|
536
469
|
contact &&
|
|
537
|
-
(!params.role || contact.role === params.role) &&
|
|
538
470
|
(!params.contactType || contact.contactType === params.contactType) &&
|
|
539
471
|
(!sanitizedQuery ||
|
|
540
472
|
(contact.displayName &&
|
|
@@ -564,7 +496,6 @@ export function searchContacts(params: {
|
|
|
564
496
|
const contact = getContactInternal(id);
|
|
565
497
|
if (
|
|
566
498
|
contact &&
|
|
567
|
-
(!params.role || contact.role === params.role) &&
|
|
568
499
|
(!params.contactType || contact.contactType === params.contactType)
|
|
569
500
|
) {
|
|
570
501
|
results.push(contact);
|
|
@@ -577,14 +508,11 @@ export function searchContacts(params: {
|
|
|
577
508
|
const conditions = [];
|
|
578
509
|
if (params.query) {
|
|
579
510
|
const sanitized = escapeLike(params.query);
|
|
580
|
-
if (!sanitized && !params.
|
|
511
|
+
if (!sanitized && !params.contactType) return [];
|
|
581
512
|
if (sanitized) {
|
|
582
513
|
conditions.push(like(contacts.displayName, `%${sanitized}%`));
|
|
583
514
|
}
|
|
584
515
|
}
|
|
585
|
-
if (params.role) {
|
|
586
|
-
conditions.push(eq(contacts.role, params.role));
|
|
587
|
-
}
|
|
588
516
|
if (params.contactType) {
|
|
589
517
|
conditions.push(eq(contacts.contactType, params.contactType));
|
|
590
518
|
}
|
|
@@ -633,20 +561,16 @@ export function searchContacts(params: {
|
|
|
633
561
|
|
|
634
562
|
export function listContacts(
|
|
635
563
|
limit = 50,
|
|
636
|
-
role?: ContactRole,
|
|
637
564
|
contactType?: ContactType,
|
|
638
565
|
opts?: { uncapped?: boolean },
|
|
639
566
|
): ContactWithChannels[] {
|
|
640
567
|
const db = getDb();
|
|
641
568
|
const effectiveLimit = opts?.uncapped ? limit : Math.min(limit, 200);
|
|
642
|
-
const conditions = [];
|
|
643
|
-
if (role) conditions.push(eq(contacts.role, role));
|
|
644
|
-
if (contactType) conditions.push(eq(contacts.contactType, contactType));
|
|
645
569
|
const rows = db
|
|
646
570
|
.select()
|
|
647
571
|
.from(contacts)
|
|
648
|
-
.where(
|
|
649
|
-
.orderBy(
|
|
572
|
+
.where(contactType ? eq(contacts.contactType, contactType) : undefined)
|
|
573
|
+
.orderBy(desc(contacts.updatedAt))
|
|
650
574
|
.limit(effectiveLimit)
|
|
651
575
|
.all();
|
|
652
576
|
return rows.map((r) => withChannels(parseContact(r)));
|
|
@@ -758,7 +682,8 @@ export function findContactByAddress(
|
|
|
758
682
|
/**
|
|
759
683
|
* Find a contact by channel external chat ID. Fallback for callers that only
|
|
760
684
|
* have a chat ID (no user-level address) — matches by (type, externalChatId).
|
|
761
|
-
* No unique constraint exists on externalChatId, so ORDER BY is needed
|
|
685
|
+
* No unique constraint exists on externalChatId, so ORDER BY is needed for a
|
|
686
|
+
* deterministic pick; channel ranking (status) is owned by the gateway now.
|
|
762
687
|
*/
|
|
763
688
|
function findContactByChannelExternalChatId(
|
|
764
689
|
channelType: string,
|
|
@@ -774,14 +699,7 @@ function findContactByChannelExternalChatId(
|
|
|
774
699
|
eq(contactChannels.externalChatId, externalChatId),
|
|
775
700
|
),
|
|
776
701
|
)
|
|
777
|
-
.orderBy(
|
|
778
|
-
sql`CASE ${contactChannels.status}
|
|
779
|
-
WHEN 'active' THEN 0
|
|
780
|
-
WHEN 'unverified' THEN 1
|
|
781
|
-
ELSE 2
|
|
782
|
-
END`,
|
|
783
|
-
desc(contactChannels.updatedAt),
|
|
784
|
-
)
|
|
702
|
+
.orderBy(desc(contactChannels.updatedAt), desc(contactChannels.createdAt))
|
|
785
703
|
.get();
|
|
786
704
|
if (!channel) return null;
|
|
787
705
|
return getContactInternal(channel.contactId);
|
|
@@ -830,131 +748,10 @@ export function findContactChannel(params: {
|
|
|
830
748
|
}
|
|
831
749
|
|
|
832
750
|
/**
|
|
833
|
-
*
|
|
834
|
-
*
|
|
835
|
-
*
|
|
836
|
-
|
|
837
|
-
export function findGuardianForChannel(
|
|
838
|
-
channelType: string,
|
|
839
|
-
): { contact: Contact; channel: ContactChannel } | null {
|
|
840
|
-
const db = getDb();
|
|
841
|
-
const conditions = [
|
|
842
|
-
eq(contacts.role, "guardian"),
|
|
843
|
-
eq(contactChannels.type, channelType),
|
|
844
|
-
eq(contactChannels.status, "active"),
|
|
845
|
-
];
|
|
846
|
-
const rows = db
|
|
847
|
-
.select({
|
|
848
|
-
contact: contacts,
|
|
849
|
-
channel: contactChannels,
|
|
850
|
-
})
|
|
851
|
-
.from(contacts)
|
|
852
|
-
.innerJoin(contactChannels, eq(contacts.id, contactChannels.contactId))
|
|
853
|
-
.where(and(...conditions))
|
|
854
|
-
.orderBy(desc(contactChannels.verifiedAt))
|
|
855
|
-
.limit(1)
|
|
856
|
-
.all();
|
|
857
|
-
|
|
858
|
-
if (rows.length === 0) return null;
|
|
859
|
-
const row = rows[0];
|
|
860
|
-
return {
|
|
861
|
-
contact: parseContact(row.contact),
|
|
862
|
-
channel: parseChannel(row.channel),
|
|
863
|
-
};
|
|
864
|
-
}
|
|
865
|
-
|
|
866
|
-
/**
|
|
867
|
-
* List all active channels for guardian contacts.
|
|
868
|
-
* This is the contacts-based equivalent of listActiveBindingsByAssistant(assistantId).
|
|
869
|
-
* Joins contacts+channels with status='active' in a single query so we never
|
|
870
|
-
* pick a guardian that has no active channels.
|
|
871
|
-
* Returns channels ordered by most-recently-verified first.
|
|
872
|
-
*/
|
|
873
|
-
export function listGuardianChannels(): {
|
|
874
|
-
contact: Contact;
|
|
875
|
-
channels: ContactChannel[];
|
|
876
|
-
} | null {
|
|
877
|
-
const db = getDb();
|
|
878
|
-
const rows = db
|
|
879
|
-
.select({
|
|
880
|
-
contact: contacts,
|
|
881
|
-
channel: contactChannels,
|
|
882
|
-
})
|
|
883
|
-
.from(contacts)
|
|
884
|
-
.innerJoin(contactChannels, eq(contacts.id, contactChannels.contactId))
|
|
885
|
-
.where(
|
|
886
|
-
and(eq(contacts.role, "guardian"), eq(contactChannels.status, "active")),
|
|
887
|
-
)
|
|
888
|
-
.orderBy(desc(contactChannels.verifiedAt))
|
|
889
|
-
.all();
|
|
890
|
-
|
|
891
|
-
if (rows.length === 0) return null;
|
|
892
|
-
|
|
893
|
-
// Use the first row's contact (the guardian with the most-recently-verified
|
|
894
|
-
// active channel) and collect all active channels for that contact.
|
|
895
|
-
const guardian = parseContact(rows[0].contact);
|
|
896
|
-
const channels = rows
|
|
897
|
-
.filter((r) => r.contact.id === guardian.id)
|
|
898
|
-
.map((r) => parseChannel(r.channel));
|
|
899
|
-
|
|
900
|
-
return { contact: guardian, channels };
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
/**
|
|
904
|
-
* Update a channel's access-control fields (status, policy, reasons).
|
|
905
|
-
* Returns the updated channel, or null if the channel does not exist.
|
|
906
|
-
*/
|
|
907
|
-
export function updateChannelStatus(
|
|
908
|
-
channelId: string,
|
|
909
|
-
params: {
|
|
910
|
-
status?: ChannelStatus;
|
|
911
|
-
policy?: ChannelPolicy;
|
|
912
|
-
revokedReason?: string | null;
|
|
913
|
-
blockedReason?: string | null;
|
|
914
|
-
},
|
|
915
|
-
): ContactChannel | null {
|
|
916
|
-
const db = getDb();
|
|
917
|
-
const existing = db
|
|
918
|
-
.select()
|
|
919
|
-
.from(contactChannels)
|
|
920
|
-
.where(eq(contactChannels.id, channelId))
|
|
921
|
-
.get();
|
|
922
|
-
|
|
923
|
-
if (!existing) return null;
|
|
924
|
-
|
|
925
|
-
const updateSet: Record<string, unknown> = {};
|
|
926
|
-
if (params.status !== undefined) updateSet.status = params.status;
|
|
927
|
-
if (params.policy !== undefined) updateSet.policy = params.policy;
|
|
928
|
-
if (params.revokedReason !== undefined)
|
|
929
|
-
updateSet.revokedReason = params.revokedReason;
|
|
930
|
-
if (params.blockedReason !== undefined)
|
|
931
|
-
updateSet.blockedReason = params.blockedReason;
|
|
932
|
-
|
|
933
|
-
if (Object.keys(updateSet).length > 0) {
|
|
934
|
-
updateSet.updatedAt = Date.now();
|
|
935
|
-
db.update(contactChannels)
|
|
936
|
-
.set(updateSet)
|
|
937
|
-
.where(eq(contactChannels.id, channelId))
|
|
938
|
-
.run();
|
|
939
|
-
|
|
940
|
-
const updated = db
|
|
941
|
-
.select()
|
|
942
|
-
.from(contactChannels)
|
|
943
|
-
.where(eq(contactChannels.id, channelId))
|
|
944
|
-
.get();
|
|
945
|
-
|
|
946
|
-
const result = updated ? parseChannel(updated) : null;
|
|
947
|
-
emitContactChange();
|
|
948
|
-
return result;
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
return parseChannel(existing);
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
/**
|
|
955
|
-
* Update a guardian contact's principalId and its channel's identity fields.
|
|
956
|
-
* Used for healing guardian binding drift when the JWT principal no longer
|
|
957
|
-
* matches the stored guardian binding after a DB reset.
|
|
751
|
+
* Heal a guardian channel's identity address when the JWT principal no longer
|
|
752
|
+
* matches the stored guardian binding after a DB reset. The principalId ACL
|
|
753
|
+
* column is gateway-owned and no longer written here; only the channel identity
|
|
754
|
+
* address is repaired.
|
|
958
755
|
*
|
|
959
756
|
* Returns false if the update would violate the unique (type, address)
|
|
960
757
|
* constraint on contact_channels — e.g. when the incoming principal already
|
|
@@ -962,7 +759,7 @@ export function updateChannelStatus(
|
|
|
962
759
|
* In that case the heal is skipped and trust stays `unknown`.
|
|
963
760
|
*/
|
|
964
761
|
export function updateContactPrincipalAndChannel(
|
|
965
|
-
|
|
762
|
+
_contactId: string,
|
|
966
763
|
channelId: string,
|
|
967
764
|
newPrincipalId: string,
|
|
968
765
|
): boolean {
|
|
@@ -983,20 +780,13 @@ export function updateContactPrincipalAndChannel(
|
|
|
983
780
|
return false;
|
|
984
781
|
}
|
|
985
782
|
|
|
986
|
-
db.
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
.set({
|
|
994
|
-
address: newPrincipalId,
|
|
995
|
-
updatedAt: now,
|
|
996
|
-
})
|
|
997
|
-
.where(eq(contactChannels.id, channelId))
|
|
998
|
-
.run();
|
|
999
|
-
});
|
|
783
|
+
db.update(contactChannels)
|
|
784
|
+
.set({
|
|
785
|
+
address: newPrincipalId,
|
|
786
|
+
updatedAt: now,
|
|
787
|
+
})
|
|
788
|
+
.where(eq(contactChannels.id, channelId))
|
|
789
|
+
.run();
|
|
1000
790
|
|
|
1001
791
|
emitContactChange();
|
|
1002
792
|
return true;
|
|
@@ -6,47 +6,33 @@
|
|
|
6
6
|
* identity and access-control state.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { emitContactChange } from "./contact-events.js";
|
|
10
9
|
import {
|
|
11
10
|
findContactChannel,
|
|
12
|
-
findGuardianForChannel,
|
|
13
11
|
getChannelById,
|
|
14
12
|
getContact,
|
|
15
13
|
getContactInternal,
|
|
16
|
-
updateChannelStatus,
|
|
17
14
|
upsertContact,
|
|
18
15
|
} from "./contact-store.js";
|
|
19
|
-
import type {
|
|
20
|
-
ChannelPolicy,
|
|
21
|
-
ChannelStatus,
|
|
22
|
-
ContactRole,
|
|
23
|
-
ContactWriteResult,
|
|
24
|
-
} from "./types.js";
|
|
16
|
+
import type { ContactWriteResult } from "./types.js";
|
|
25
17
|
|
|
26
18
|
// ── Guardian operations ──────────────────────────────────────────────
|
|
27
19
|
|
|
28
20
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
21
|
+
* No-op shim: the guardian channel ACL revoke is gateway-owned (relayed via
|
|
22
|
+
* mark_channel_revoked). Retained while callers still invoke it; the return is
|
|
23
|
+
* discarded.
|
|
31
24
|
*/
|
|
32
|
-
export function revokeGuardianBinding(
|
|
33
|
-
|
|
34
|
-
if (!guardian) return false;
|
|
35
|
-
|
|
36
|
-
updateChannelStatus(guardian.channel.id, {
|
|
37
|
-
status: "revoked",
|
|
38
|
-
revokedReason: "binding_revoked",
|
|
39
|
-
});
|
|
40
|
-
emitContactChange();
|
|
41
|
-
return true;
|
|
25
|
+
export function revokeGuardianBinding(_channel: string): boolean {
|
|
26
|
+
return false;
|
|
42
27
|
}
|
|
43
28
|
|
|
44
29
|
// ── Member operations ────────────────────────────────────────────────
|
|
45
30
|
|
|
46
31
|
/**
|
|
47
|
-
* Upsert a contact and channel by writing to the contacts table.
|
|
48
|
-
*
|
|
49
|
-
*
|
|
32
|
+
* Upsert a contact and channel identity by writing to the contacts table.
|
|
33
|
+
* Persists only identity/INFO — the gateway owns the ACL verdict. Returns the
|
|
34
|
+
* native Contact + ContactChannel, or null if no usable identity was provided
|
|
35
|
+
* or the lookup failed after upsert.
|
|
50
36
|
*/
|
|
51
37
|
export function upsertContactChannel(params: {
|
|
52
38
|
sourceChannel: string;
|
|
@@ -54,12 +40,7 @@ export function upsertContactChannel(params: {
|
|
|
54
40
|
externalChatId?: string;
|
|
55
41
|
displayName?: string;
|
|
56
42
|
username?: string;
|
|
57
|
-
policy?: string;
|
|
58
|
-
status?: string;
|
|
59
43
|
inviteId?: string;
|
|
60
|
-
verifiedAt?: number;
|
|
61
|
-
verifiedVia?: string;
|
|
62
|
-
role?: ContactRole;
|
|
63
44
|
contactId?: string;
|
|
64
45
|
}): ContactWriteResult | null {
|
|
65
46
|
let address: string;
|
|
@@ -88,19 +69,12 @@ export function upsertContactChannel(params: {
|
|
|
88
69
|
upsertContact({
|
|
89
70
|
id: params.contactId,
|
|
90
71
|
displayName,
|
|
91
|
-
role: params.role,
|
|
92
72
|
channels: [
|
|
93
73
|
{
|
|
94
74
|
type: params.sourceChannel,
|
|
95
75
|
address,
|
|
96
76
|
externalChatId: params.externalChatId ?? null,
|
|
97
|
-
status: (params.status as ChannelStatus) ?? undefined,
|
|
98
|
-
policy: (params.policy as ChannelPolicy) ?? undefined,
|
|
99
77
|
inviteId: params.inviteId ?? null,
|
|
100
|
-
revokedReason: params.status === "active" ? null : undefined,
|
|
101
|
-
blockedReason: params.status === "active" ? null : undefined,
|
|
102
|
-
verifiedAt: params.verifiedAt ?? undefined,
|
|
103
|
-
verifiedVia: params.verifiedVia ?? undefined,
|
|
104
78
|
},
|
|
105
79
|
],
|
|
106
80
|
// When a specific contactId is provided, reassign conflicting channels from
|
|
@@ -129,32 +103,21 @@ export function upsertContactChannel(params: {
|
|
|
129
103
|
}
|
|
130
104
|
|
|
131
105
|
/**
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
*
|
|
106
|
+
* Resolve the native contact/channel for a member id. The ACL downgrade is
|
|
107
|
+
* gateway-owned (relayed via mark_channel_revoked); this no longer mutates
|
|
108
|
+
* local status. The memberId may be a plain channel ID (internal callers) or a
|
|
109
|
+
* composite contactId:channelId (from the API response format).
|
|
135
110
|
*/
|
|
136
|
-
export function revokeMember(
|
|
137
|
-
memberId: string,
|
|
138
|
-
reason?: string,
|
|
139
|
-
): ContactWriteResult | null {
|
|
111
|
+
export function revokeMember(memberId: string): ContactWriteResult | null {
|
|
140
112
|
const channelId = memberId.includes(":") ? memberId.split(":")[1] : memberId;
|
|
141
113
|
|
|
142
114
|
const channelRow = getChannelById(channelId);
|
|
143
115
|
if (!channelRow) return null;
|
|
144
|
-
if (channelRow.status !== "active" && channelRow.status !== "pending")
|
|
145
|
-
return null;
|
|
146
|
-
|
|
147
|
-
updateChannelStatus(channelId, {
|
|
148
|
-
status: "revoked",
|
|
149
|
-
revokedReason: reason ?? null,
|
|
150
|
-
});
|
|
151
116
|
|
|
152
|
-
// Use unscoped lookup — the contact was already resolved via channel ID
|
|
153
117
|
const contact = getContactInternal(channelRow.contactId);
|
|
154
118
|
if (!contact) return null;
|
|
155
|
-
const
|
|
156
|
-
if (!
|
|
119
|
+
const channel = contact.channels.find((ch) => ch.id === channelId);
|
|
120
|
+
if (!channel) return null;
|
|
157
121
|
|
|
158
|
-
|
|
159
|
-
return { contact, channel: updatedChannel };
|
|
122
|
+
return { contact, channel };
|
|
160
123
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { GetContactIpcResponseSchema } from "@vellumai/gateway-client/gateway-ipc-contracts";
|
|
2
|
+
|
|
3
|
+
import { ipcCallPersistent } from "../ipc/gateway-client.js";
|
|
4
|
+
import { getLogger } from "../util/logger.js";
|
|
5
|
+
import type { ContactChannel } from "./types.js";
|
|
6
|
+
|
|
7
|
+
const log = getLogger("gateway-channel-read");
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Read a contact channel's verified state from the gateway contact-channel read
|
|
11
|
+
* (ACL source of truth). Covers all contacts, not just guardian deliveries.
|
|
12
|
+
*
|
|
13
|
+
* Matches the gateway row by logical identity — `(type, address)`,
|
|
14
|
+
* case-insensitive — not by id, so a reconcile-divergent row that the gateway
|
|
15
|
+
* write helpers re-keyed under a different UUID is still found.
|
|
16
|
+
*
|
|
17
|
+
* Returns `undefined` for unreachable reads (gateway down, IPC timeout, schema
|
|
18
|
+
* mismatch) or when no such channel exists, so callers fail open.
|
|
19
|
+
*/
|
|
20
|
+
export async function gatewayContactChannelState(
|
|
21
|
+
channel: Pick<ContactChannel, "contactId" | "type" | "address">,
|
|
22
|
+
): Promise<{ status: string; verifiedAt: number | null } | undefined> {
|
|
23
|
+
let result: unknown;
|
|
24
|
+
try {
|
|
25
|
+
result = await ipcCallPersistent("contacts_get_rich", {
|
|
26
|
+
contactId: channel.contactId,
|
|
27
|
+
});
|
|
28
|
+
} catch (err) {
|
|
29
|
+
log.warn(
|
|
30
|
+
{ err, contactId: channel.contactId },
|
|
31
|
+
"contacts_get_rich unreachable — failing open",
|
|
32
|
+
);
|
|
33
|
+
return undefined;
|
|
34
|
+
}
|
|
35
|
+
if (!result || (result as { contact?: unknown }).contact == null) {
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
const parsed = GetContactIpcResponseSchema.safeParse(result);
|
|
39
|
+
if (!parsed.success) {
|
|
40
|
+
log.warn(
|
|
41
|
+
{ err: parsed.error, contactId: channel.contactId },
|
|
42
|
+
"contacts_get_rich response failed schema parse — failing open",
|
|
43
|
+
);
|
|
44
|
+
return undefined;
|
|
45
|
+
}
|
|
46
|
+
const address = channel.address.toLowerCase();
|
|
47
|
+
const ch = parsed.data.contact.channels.find(
|
|
48
|
+
(c) => c.type === channel.type && c.address.toLowerCase() === address,
|
|
49
|
+
);
|
|
50
|
+
return ch ? { status: ch.status, verifiedAt: ch.verifiedAt } : undefined;
|
|
51
|
+
}
|