@vellumai/assistant 0.10.1 → 0.10.2-dev.202606241651.2d2b40d
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/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/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/openapi.yaml +74 -1
- package/package.json +1 -1
- package/scripts/test.sh +36 -15
- package/src/__tests__/actor-token-service.test.ts +36 -14
- 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__/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-guardian.test.ts +94 -58
- package/src/__tests__/channel-reply-delivery.test.ts +2 -0
- package/src/__tests__/compaction-events.test.ts +2 -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__/computer-use-tools.test.ts +13 -0
- package/src/__tests__/config-loader-backfill.test.ts +5 -1
- package/src/__tests__/config-schema.test.ts +1 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +31 -29
- package/src/__tests__/contacts-relay-reads.test.ts +13 -15
- 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 +7 -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-confirmation-signals.test.ts +2 -0
- 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-action-delivery.test.ts +65 -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 +0 -1
- package/src/__tests__/db-migration-rollback.test.ts +205 -171
- package/src/__tests__/db-test-helpers.ts +5 -4
- 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-persistence.test.ts +2 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +10 -5
- package/src/__tests__/events-dev-bypass-actor.test.ts +7 -1
- package/src/__tests__/filing-service.test.ts +2 -0
- package/src/__tests__/guardian-binding-drift-heal.test.ts +75 -10
- package/src/__tests__/guardian-dispatch.test.ts +95 -1
- package/src/__tests__/guardian-outbound-http.test.ts +13 -0
- 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 +1 -7
- 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-proxy.test.ts +299 -0
- 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 +167 -8
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/invite-redemption-service.test.ts +43 -0
- package/src/__tests__/llm-context-normalization.test.ts +105 -0
- package/src/__tests__/llm-usage-store.test.ts +25 -0
- 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__/native-web-search.test.ts +2 -0
- package/src/__tests__/non-member-access-request.test.ts +189 -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__/outbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/pending-interactions-resolved-event.test.ts +7 -4
- package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
- package/src/__tests__/plugin-bootstrap.test.ts +3 -73
- package/src/__tests__/plugin-route-contribution.test.ts +4 -17
- package/src/__tests__/plugin-tool-contribution.test.ts +3 -18
- package/src/__tests__/plugin-types.test.ts +0 -2
- 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-usage-tracking.test.ts +39 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +2 -0
- package/src/__tests__/registry.test.ts +3 -0
- package/src/__tests__/relay-server.test.ts +694 -25
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/secret-ingress-http.test.ts +14 -0
- package/src/__tests__/send-endpoint-busy.test.ts +30 -8
- 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 +102 -0
- package/src/__tests__/steer-on-enqueue-question.test.ts +181 -0
- package/src/__tests__/stt-hints.test.ts +44 -13
- package/src/__tests__/subagent-detail.test.ts +27 -0
- package/src/__tests__/subagent-disposal.test.ts +65 -0
- package/src/__tests__/subagent-notify-parent.test.ts +2 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +2 -0
- package/src/__tests__/subagent-tools.test.ts +2 -0
- package/src/__tests__/suggestion-routes.test.ts +2 -0
- 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-inline-approval-integration.test.ts +10 -10
- package/src/__tests__/twilio-routes.test.ts +96 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -0
- package/src/__tests__/web-search-backend-failure.test.ts +2 -0
- package/src/__tests__/workspace-tool-loader.test.ts +195 -2
- package/src/agent/loop-exclusive-tool.test.ts +150 -0
- package/src/agent/loop.ts +56 -0
- package/src/api/constants/sse-replay.ts +41 -0
- package/src/api/index.ts +6 -0
- package/src/api/responses/llm-request-log-entry.ts +25 -0
- package/src/api/responses/subagent-detail.ts +17 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +262 -4
- package/src/calls/call-domain.ts +3 -3
- package/src/calls/guardian-dispatch.ts +10 -8
- package/src/calls/inbound-trust-reader.ts +17 -1
- package/src/calls/media-stream-server.ts +21 -0
- package/src/calls/relay-server.ts +167 -50
- package/src/calls/relay-setup-router.ts +37 -7
- 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/cli/commands/__tests__/cache.test.ts +8 -1
- package/src/cli/commands/cache.ts +194 -181
- 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 +302 -0
- package/src/cli/commands/memory/index.ts +2 -0
- package/src/cli/commands/memory/worker.ts +175 -0
- package/src/cli/commands/plugins.ts +75 -3
- 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/list-installed-plugins.ts +179 -1
- package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +143 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +6 -1
- 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/feature-flag-registry.json +0 -8
- package/src/config/loader.ts +36 -5
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- 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 +14 -5
- package/src/config/skills.ts +27 -5
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +312 -0
- package/src/contacts/contacts-write.ts +3 -0
- package/src/contacts/guardian-delivery-reader.ts +223 -0
- package/src/daemon/conversation-agent-loop.ts +9 -0
- package/src/daemon/conversation-process.ts +39 -17
- package/src/daemon/conversation-surfaces.ts +8 -0
- package/src/daemon/conversation-tool-setup.ts +49 -16
- package/src/daemon/conversation.ts +21 -2
- 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 +4 -34
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +25 -0
- package/src/daemon/handlers/__tests__/config-channels.test.ts +225 -0
- package/src/daemon/handlers/config-a2a.ts +6 -14
- package/src/daemon/handlers/config-channels.ts +78 -22
- package/src/daemon/handlers/conversations.ts +77 -0
- package/src/daemon/host-cu-proxy.ts +102 -11
- package/src/daemon/lifecycle.ts +4 -0
- package/src/daemon/memory-v2-startup.test.ts +72 -0
- package/src/daemon/memory-v2-startup.ts +87 -19
- 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/ipc/__tests__/clients-list-ipc.test.ts +1 -1
- package/src/ipc/assistant-server.ts +2 -2
- 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/conversation-crud.ts +38 -0
- package/src/memory/db-connection.ts +22 -3
- package/src/memory/db-init.ts +36 -502
- package/src/memory/db-singleton.ts +6 -4
- package/src/memory/jobs-worker.ts +58 -0
- package/src/memory/llm-usage-store.ts +48 -20
- package/src/memory/memory-retrospective-job.ts +9 -8
- package/src/memory/migrations/014-backfill-inbox-thread-state.ts +13 -3
- package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -27
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +130 -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/__tests__/014-backfill-inbox-thread-state.test.ts +108 -0
- package/src/memory/migrations/__tests__/136-drop-assistant-id-columns.test.ts +82 -0
- package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +224 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
- package/src/memory/migrations/run-migrations.ts +90 -6
- package/src/memory/migrations/schema-introspection.ts +14 -0
- package/src/memory/migrations/validate-migration-state.ts +101 -66
- package/src/memory/prompt-override.ts +129 -0
- package/src/memory/schema/conversations.ts +9 -0
- package/src/memory/schema/infrastructure.ts +20 -0
- package/src/memory/steps.ts +573 -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 +118 -0
- package/src/memory/worker-process.ts +72 -0
- package/src/notifications/__tests__/broadcaster.test.ts +16 -8
- package/src/notifications/__tests__/connected-channels.test.ts +114 -0
- package/src/notifications/__tests__/decision-engine.test.ts +78 -9
- package/src/notifications/__tests__/destination-resolver.test.ts +256 -0
- package/src/notifications/broadcaster.ts +8 -1
- package/src/notifications/decision-engine.ts +15 -7
- package/src/notifications/destination-resolver.ts +68 -24
- package/src/notifications/emit-signal.ts +39 -14
- package/src/onboarding/checkin-event.test.ts +220 -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 +6 -6
- package/src/plugin-api/types.ts +3 -5
- package/src/plugin-api/vision-support.test.ts +28 -4
- package/src/plugin-api/vision-support.ts +66 -31
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +161 -0
- package/src/plugins/defaults/advisor/__tests__/context-pack-gating.test.ts +106 -0
- package/src/plugins/defaults/advisor/__tests__/context-pack.test.ts +60 -0
- package/src/plugins/defaults/advisor/consult.ts +110 -6
- package/src/plugins/defaults/advisor/context-pack.ts +288 -0
- package/src/plugins/defaults/advisor/steering.ts +14 -2
- package/src/plugins/defaults/advisor/tools/advisor.ts +32 -5
- package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +47 -7
- package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +10 -11
- package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +12 -20
- package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +42 -11
- 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 +29 -1
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +8 -1
- package/src/plugins/mtime-cache.ts +7 -2
- package/src/plugins/types.ts +0 -2
- package/src/providers/anthropic/client.ts +5 -0
- package/src/providers/call-site-routing.ts +4 -0
- package/src/providers/model-catalog.ts +16 -0
- package/src/providers/openai/responses-provider.ts +5 -0
- package/src/providers/openrouter/client.ts +5 -0
- package/src/providers/provider-send-message.ts +4 -0
- package/src/providers/ratelimit.ts +4 -0
- package/src/providers/retry.ts +4 -0
- package/src/providers/types.ts +9 -0
- package/src/providers/usage-tracking.ts +4 -0
- package/src/runtime/__tests__/channel-verification-service.test.ts +133 -0
- package/src/runtime/__tests__/guardian-vellum-migration.test.ts +181 -0
- package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +66 -0
- package/src/runtime/__tests__/local-principal-trust.test.ts +164 -0
- package/src/runtime/__tests__/trust-verdict-consumer.test.ts +335 -3
- package/src/runtime/access-request-helper.ts +19 -39
- package/src/runtime/actor-trust-resolver.ts +2 -2
- package/src/runtime/anchored-guardian.test.ts +156 -0
- package/src/runtime/anchored-guardian.ts +135 -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 +66 -7
- package/src/runtime/invite-redemption-service.ts +50 -18
- package/src/runtime/local-actor-identity.ts +76 -11
- package/src/runtime/local-principal-trust.ts +52 -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 +212 -0
- package/src/runtime/routes/__tests__/global-search-routes.test.ts +93 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +215 -1
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/channel-verification-routes.ts +3 -3
- package/src/runtime/routes/contact-routes.ts +8 -32
- 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-routes.ts +74 -81
- 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 +1 -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 -49
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +16 -4
- 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/index.ts +2 -0
- package/src/runtime/routes/llm-context-normalization.ts +71 -0
- package/src/runtime/routes/mcp-auth-routes.ts +38 -15
- 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/subagents-routes.ts +5 -0
- package/src/runtime/routes/surface-action-routes.ts +51 -55
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
- package/src/runtime/services/conversation-serializer.ts +7 -9
- package/src/runtime/tool-grant-request-helper.ts +3 -3
- package/src/runtime/trust-verdict-consumer.ts +85 -9
- package/src/runtime/verification-outbound-actions.ts +18 -18
- package/src/signals/user-message.ts +16 -0
- package/src/subagent/manager.ts +9 -0
- 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/computer-use/definitions.ts +8 -2
- package/src/tools/executor.ts +4 -4
- package/src/tools/registry.ts +18 -0
- package/src/tools/tool-approval-handler.ts +1 -1
- package/src/tools/tool-defaults.ts +9 -2
- package/src/tools/types.ts +17 -2
- package/src/tools/workspace-tools/loader.ts +348 -244
- package/src/util/platform.ts +5 -0
- package/src/util/telemetry-db-path.ts +24 -0
- 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/__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
|
@@ -29,6 +29,17 @@ mock.module("../config/env.js", () => ({
|
|
|
29
29
|
checkUnrecognizedEnvVars: () => {},
|
|
30
30
|
}));
|
|
31
31
|
|
|
32
|
+
// No gateway in tests: force the reader to miss so resolution exercises the
|
|
33
|
+
// local-store bootstrap fallback deterministically.
|
|
34
|
+
let fakeGuardianDelivery: { principalId?: string | null } | null = null;
|
|
35
|
+
mock.module("../contacts/guardian-delivery-reader.js", () => ({
|
|
36
|
+
getGuardianDelivery: async () =>
|
|
37
|
+
fakeGuardianDelivery ? [fakeGuardianDelivery] : null,
|
|
38
|
+
guardianForChannel: (list: { principalId?: string | null }[]) => list[0],
|
|
39
|
+
invalidateGuardianDeliveryCache: () => {},
|
|
40
|
+
onContactChange: () => {},
|
|
41
|
+
}));
|
|
42
|
+
|
|
32
43
|
import { getDb } from "../memory/db-connection.js";
|
|
33
44
|
import { initializeDb } from "../memory/db-init.js";
|
|
34
45
|
import { resetExternalAssistantIdCache } from "../runtime/auth/external-assistant-id.js";
|
|
@@ -51,6 +62,7 @@ await initializeDb();
|
|
|
51
62
|
beforeEach(async () => {
|
|
52
63
|
initAuthSigningKey(TEST_KEY);
|
|
53
64
|
resetExternalAssistantIdCache();
|
|
65
|
+
fakeGuardianDelivery = null;
|
|
54
66
|
resetDbForTesting();
|
|
55
67
|
await initializeDb();
|
|
56
68
|
});
|
|
@@ -60,8 +72,8 @@ beforeEach(async () => {
|
|
|
60
72
|
// ---------------------------------------------------------------------------
|
|
61
73
|
|
|
62
74
|
describe("resolveLocalTrustContext", () => {
|
|
63
|
-
test("falls back to minimal trust context when no vellum binding exists", () => {
|
|
64
|
-
const ctx = resolveLocalTrustContext();
|
|
75
|
+
test("falls back to minimal trust context when no vellum binding exists", async () => {
|
|
76
|
+
const ctx = await resolveLocalTrustContext();
|
|
65
77
|
expect(ctx.sourceChannel).toBe("vellum");
|
|
66
78
|
});
|
|
67
79
|
});
|
|
@@ -71,38 +83,48 @@ describe("resolveLocalTrustContext", () => {
|
|
|
71
83
|
// ---------------------------------------------------------------------------
|
|
72
84
|
|
|
73
85
|
describe("resolveLocalAuthContext", () => {
|
|
74
|
-
test("returns AuthContext with local principal type", () => {
|
|
75
|
-
const ctx = resolveLocalAuthContext("session-123");
|
|
86
|
+
test("returns AuthContext with local principal type", async () => {
|
|
87
|
+
const ctx = await resolveLocalAuthContext("session-123");
|
|
76
88
|
expect(ctx.principalType).toBe("local");
|
|
77
89
|
});
|
|
78
90
|
|
|
79
|
-
test("subject follows local:self:<conversationId> pattern", () => {
|
|
80
|
-
const ctx = resolveLocalAuthContext("session-abc");
|
|
91
|
+
test("subject follows local:self:<conversationId> pattern", async () => {
|
|
92
|
+
const ctx = await resolveLocalAuthContext("session-abc");
|
|
81
93
|
expect(ctx.subject).toBe("local:self:session-abc");
|
|
82
94
|
});
|
|
83
95
|
|
|
84
|
-
test("assistantId is always self", () => {
|
|
85
|
-
const ctx = resolveLocalAuthContext("session-123");
|
|
96
|
+
test("assistantId is always self", async () => {
|
|
97
|
+
const ctx = await resolveLocalAuthContext("session-123");
|
|
86
98
|
expect(ctx.assistantId).toBe("self");
|
|
87
99
|
});
|
|
88
100
|
|
|
89
|
-
test("uses local_v1 scope profile with local.all scope", () => {
|
|
90
|
-
const ctx = resolveLocalAuthContext("session-123");
|
|
101
|
+
test("uses local_v1 scope profile with local.all scope", async () => {
|
|
102
|
+
const ctx = await resolveLocalAuthContext("session-123");
|
|
91
103
|
expect(ctx.scopeProfile).toBe("local_v1");
|
|
92
104
|
expect(ctx.scopes.has("local.all")).toBe(true);
|
|
93
105
|
});
|
|
94
106
|
|
|
95
|
-
test("actorPrincipalId is undefined when no vellum binding exists", () => {
|
|
107
|
+
test("actorPrincipalId is undefined when no vellum binding exists", async () => {
|
|
96
108
|
const db = getDb();
|
|
97
109
|
db.run("DELETE FROM contact_channels");
|
|
98
110
|
db.run("DELETE FROM contacts");
|
|
99
111
|
|
|
100
|
-
const ctx = resolveLocalAuthContext("session-123");
|
|
112
|
+
const ctx = await resolveLocalAuthContext("session-123");
|
|
101
113
|
expect(ctx.actorPrincipalId).toBeUndefined();
|
|
102
114
|
});
|
|
103
115
|
|
|
104
|
-
test("
|
|
105
|
-
const
|
|
116
|
+
test("resolves the guardian principal from the gateway when available", async () => {
|
|
117
|
+
const db = getDb();
|
|
118
|
+
db.run("DELETE FROM contact_channels");
|
|
119
|
+
db.run("DELETE FROM contacts");
|
|
120
|
+
fakeGuardianDelivery = { principalId: "gateway-guardian-id" };
|
|
121
|
+
|
|
122
|
+
const ctx = await resolveLocalAuthContext("session-123");
|
|
123
|
+
expect(ctx.actorPrincipalId).toBe("gateway-guardian-id");
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test("conversationId matches the provided argument", async () => {
|
|
127
|
+
const ctx = await resolveLocalAuthContext("my-session");
|
|
106
128
|
expect(ctx.conversationId).toBe("my-session");
|
|
107
129
|
});
|
|
108
130
|
});
|
|
@@ -384,6 +384,7 @@ describe("SubagentManager.spawn — overrideProfile inheritance", () => {
|
|
|
384
384
|
// inheritance chain breaks at the second nesting level.
|
|
385
385
|
|
|
386
386
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
387
|
+
setConversationProcessingStartedAt: () => {},
|
|
387
388
|
// Always return undefined for the row read so the test fails fast unless
|
|
388
389
|
// executeSubagentSpawn reads from context.overrideProfile first.
|
|
389
390
|
getConversationOverrideProfile: () => undefined,
|
|
@@ -21,6 +21,8 @@ import type {
|
|
|
21
21
|
import type { Message } from "../providers/types.js";
|
|
22
22
|
|
|
23
23
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
24
|
+
setConversationProcessingStartedAt: () => {},
|
|
25
|
+
isConversationProcessing: () => false,
|
|
24
26
|
getConversationOverrideProfile: () => undefined,
|
|
25
27
|
}));
|
|
26
28
|
|
|
@@ -31,6 +31,8 @@ import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
|
31
31
|
let mockOverrideProfile: string | undefined = undefined;
|
|
32
32
|
|
|
33
33
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
34
|
+
setConversationProcessingStartedAt: () => {},
|
|
35
|
+
isConversationProcessing: () => false,
|
|
34
36
|
getConversationOverrideProfile: (_id: string) => mockOverrideProfile,
|
|
35
37
|
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
36
38
|
}));
|
|
@@ -55,6 +55,8 @@ let mockedRowContent = "";
|
|
|
55
55
|
const updates: Array<{ id: string; content: string }> = [];
|
|
56
56
|
|
|
57
57
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
58
|
+
setConversationProcessingStartedAt: () => {},
|
|
59
|
+
isConversationProcessing: () => false,
|
|
58
60
|
addMessage: () => ({ id: "mock-msg-id" }),
|
|
59
61
|
getMessageById: (id: string) =>
|
|
60
62
|
mockedRowContent ? { id, content: mockedRowContent } : null,
|
|
@@ -50,6 +50,8 @@ let mockedRowContent = "";
|
|
|
50
50
|
const updates: Array<{ id: string; content: string }> = [];
|
|
51
51
|
|
|
52
52
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
53
|
+
setConversationProcessingStartedAt: () => {},
|
|
54
|
+
isConversationProcessing: () => false,
|
|
53
55
|
addMessage: () => ({ id: "mock-msg-id" }),
|
|
54
56
|
getMessageById: (id: string) =>
|
|
55
57
|
mockedRowContent ? { id, content: mockedRowContent } : null,
|
|
@@ -121,6 +121,8 @@ mock.module("../security/secret-allowlist.js", () => ({
|
|
|
121
121
|
}));
|
|
122
122
|
|
|
123
123
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
124
|
+
setConversationProcessingStartedAt: () => {},
|
|
125
|
+
isConversationProcessing: () => false,
|
|
124
126
|
setConversationOriginChannelIfUnset: () => {},
|
|
125
127
|
updateConversationContextWindow: () => {},
|
|
126
128
|
deleteMessageById: () => {},
|
|
@@ -100,6 +100,8 @@ mock.module("../daemon/process-message.js", () => ({
|
|
|
100
100
|
|
|
101
101
|
const createdConversations: Array<{ conversationType: string }> = [];
|
|
102
102
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
103
|
+
setConversationProcessingStartedAt: () => {},
|
|
104
|
+
isConversationProcessing: () => false,
|
|
103
105
|
addMessage: mock(() => ({ id: "msg-1" })),
|
|
104
106
|
archiveConversation: mock(() => true),
|
|
105
107
|
batchSetDisplayOrders: mock(() => {}),
|
|
@@ -43,6 +43,8 @@ mock.module("../memory/conversation-key-store.js", () => ({
|
|
|
43
43
|
const mockAddMessage = mock(() => {});
|
|
44
44
|
|
|
45
45
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
46
|
+
setConversationProcessingStartedAt: () => {},
|
|
47
|
+
isConversationProcessing: () => false,
|
|
46
48
|
addMessage: mockAddMessage,
|
|
47
49
|
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
48
50
|
}));
|
|
@@ -41,6 +41,8 @@ mock.module("../config/loader.js", () => ({
|
|
|
41
41
|
}));
|
|
42
42
|
|
|
43
43
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
44
|
+
setConversationProcessingStartedAt: () => {},
|
|
45
|
+
isConversationProcessing: () => false,
|
|
44
46
|
addMessage: () => ({ id: "mock-msg-id" }),
|
|
45
47
|
getMessageById: () => null,
|
|
46
48
|
updateMessageContent: () => {},
|
|
@@ -168,6 +168,25 @@ mock.module("../notifications/emit-signal.js", () => ({
|
|
|
168
168
|
}),
|
|
169
169
|
}));
|
|
170
170
|
|
|
171
|
+
// Guardian principalId is resolved from the gateway binding reader. Mirror the
|
|
172
|
+
// vellum binding seeded by resetTables so guardian dispatch can resolve it.
|
|
173
|
+
mock.module("../contacts/guardian-delivery-reader.js", () => ({
|
|
174
|
+
getGuardianDelivery: async () => [
|
|
175
|
+
{
|
|
176
|
+
channelType: "vellum",
|
|
177
|
+
contactId: "guardian-vellum",
|
|
178
|
+
principalId: "test-principal-id",
|
|
179
|
+
address: "local",
|
|
180
|
+
status: "active",
|
|
181
|
+
},
|
|
182
|
+
],
|
|
183
|
+
guardianForChannel: (
|
|
184
|
+
list: Array<{ channelType: string; status: string }>,
|
|
185
|
+
channelType: string,
|
|
186
|
+
) => list.find((g) => g.channelType === channelType && g.status === "active"),
|
|
187
|
+
anyGuardian: (list: unknown[]) => list[0],
|
|
188
|
+
}));
|
|
189
|
+
|
|
171
190
|
mock.module("../calls/voice-session-bridge.js", () => {
|
|
172
191
|
mockStartVoiceTurn = mock(createMockVoiceTurn(["Hello", " there"]));
|
|
173
192
|
return {
|
|
@@ -83,6 +83,42 @@ mock.module("../ipc/gateway-client.js", () => ({
|
|
|
83
83
|
}),
|
|
84
84
|
}));
|
|
85
85
|
|
|
86
|
+
// Guardian-delivery reader mock — the inbound challenge guard reads guardian
|
|
87
|
+
// existence from the gateway. Derive the list from the local binding state so
|
|
88
|
+
// the gateway-backed presence guard mirrors the DB the rest of the test sets up.
|
|
89
|
+
const resolveGuardianList = async (input?: { channelTypes?: string[] }) => {
|
|
90
|
+
const { findGuardianForChannel } = await import(
|
|
91
|
+
"../contacts/contact-store.js"
|
|
92
|
+
);
|
|
93
|
+
const channels = input?.channelTypes ?? [];
|
|
94
|
+
return channels
|
|
95
|
+
.map((channelType) => {
|
|
96
|
+
const found = findGuardianForChannel(channelType);
|
|
97
|
+
if (!found) return null;
|
|
98
|
+
return {
|
|
99
|
+
channelType,
|
|
100
|
+
contactId: found.contact.id,
|
|
101
|
+
principalId: found.contact.principalId ?? null,
|
|
102
|
+
displayName: found.contact.displayName ?? null,
|
|
103
|
+
address: found.channel.address,
|
|
104
|
+
externalChatId: found.channel.externalChatId ?? null,
|
|
105
|
+
status: "active",
|
|
106
|
+
verifiedAt: found.channel.verifiedAt ?? null,
|
|
107
|
+
};
|
|
108
|
+
})
|
|
109
|
+
.filter((g) => g !== null);
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
mock.module("../contacts/guardian-delivery-reader.js", () => ({
|
|
113
|
+
getGuardianDelivery: resolveGuardianList,
|
|
114
|
+
getGuardianDeliveryFresh: resolveGuardianList,
|
|
115
|
+
guardianForChannel: (
|
|
116
|
+
list: Array<{ channelType: string; status: string }>,
|
|
117
|
+
channelType: string,
|
|
118
|
+
) =>
|
|
119
|
+
list.find((g) => g.channelType === channelType && g.status === "active"),
|
|
120
|
+
}));
|
|
121
|
+
|
|
86
122
|
import { handleChannelVerificationSession } from "../daemon/handlers/config-channels.js";
|
|
87
123
|
import type {
|
|
88
124
|
ChannelVerificationSessionRequest,
|
|
@@ -319,12 +355,12 @@ describe("guardian service challenge validation", () => {
|
|
|
319
355
|
}
|
|
320
356
|
});
|
|
321
357
|
|
|
322
|
-
test("validateAndConsumeVerification does not create a guardian binding (caller responsibility)", () => {
|
|
358
|
+
test("validateAndConsumeVerification does not create a guardian binding (caller responsibility)", async () => {
|
|
323
359
|
const { secret } = createInboundVerificationSession("telegram");
|
|
324
360
|
|
|
325
361
|
validateAndConsumeVerification("telegram", secret, "user-42", "chat-42");
|
|
326
362
|
|
|
327
|
-
const binding = getGuardianBinding("asst-1", "telegram");
|
|
363
|
+
const binding = await getGuardianBinding("asst-1", "telegram");
|
|
328
364
|
expect(binding).toBeNull();
|
|
329
365
|
});
|
|
330
366
|
|
|
@@ -396,7 +432,7 @@ describe("guardian service challenge validation", () => {
|
|
|
396
432
|
expect(result2.success).toBe(false);
|
|
397
433
|
});
|
|
398
434
|
|
|
399
|
-
test("validateAndConsumeVerification succeeds with voice channel", () => {
|
|
435
|
+
test("validateAndConsumeVerification succeeds with voice channel", async () => {
|
|
400
436
|
const { secret } = createInboundVerificationSession("phone");
|
|
401
437
|
|
|
402
438
|
const result = validateAndConsumeVerification(
|
|
@@ -413,7 +449,7 @@ describe("guardian service challenge validation", () => {
|
|
|
413
449
|
|
|
414
450
|
// validateAndConsumeVerification no longer creates bindings — that is
|
|
415
451
|
// now handled by the gateway's verification intercepts.
|
|
416
|
-
const binding = getGuardianBinding("asst-1", "phone");
|
|
452
|
+
const binding = await getGuardianBinding("asst-1", "phone");
|
|
417
453
|
expect(binding).toBeNull();
|
|
418
454
|
});
|
|
419
455
|
|
|
@@ -449,7 +485,7 @@ describe("guardian service challenge validation", () => {
|
|
|
449
485
|
expect(telegramResult.success).toBe(true);
|
|
450
486
|
});
|
|
451
487
|
|
|
452
|
-
test("validateAndConsumeVerification succeeds even with existing binding (conflict check is caller responsibility)", () => {
|
|
488
|
+
test("validateAndConsumeVerification succeeds even with existing binding (conflict check is caller responsibility)", async () => {
|
|
453
489
|
// Create initial guardian binding
|
|
454
490
|
createGuardianBinding({
|
|
455
491
|
channel: "telegram",
|
|
@@ -458,7 +494,7 @@ describe("guardian service challenge validation", () => {
|
|
|
458
494
|
guardianDeliveryChatId: "old-chat",
|
|
459
495
|
});
|
|
460
496
|
|
|
461
|
-
const oldBinding = getGuardianBinding("asst-1", "telegram");
|
|
497
|
+
const oldBinding = await getGuardianBinding("asst-1", "telegram");
|
|
462
498
|
expect(oldBinding).not.toBeNull();
|
|
463
499
|
expect(oldBinding!.guardianExternalUserId).toBe("old-user");
|
|
464
500
|
|
|
@@ -473,7 +509,7 @@ describe("guardian service challenge validation", () => {
|
|
|
473
509
|
// Challenge validation succeeds — the caller decides how to handle binding conflicts
|
|
474
510
|
expect(result.success).toBe(true);
|
|
475
511
|
|
|
476
|
-
const binding = getGuardianBinding("asst-1", "telegram");
|
|
512
|
+
const binding = await getGuardianBinding("asst-1", "telegram");
|
|
477
513
|
expect(binding).not.toBeNull();
|
|
478
514
|
expect(binding!.guardianExternalUserId).toBe("old-user");
|
|
479
515
|
});
|
|
@@ -488,7 +524,7 @@ describe("guardian identity check", () => {
|
|
|
488
524
|
resetTables();
|
|
489
525
|
});
|
|
490
526
|
|
|
491
|
-
test("isGuardian returns true for matching user", () => {
|
|
527
|
+
test("isGuardian returns true for matching user", async () => {
|
|
492
528
|
createGuardianBinding({
|
|
493
529
|
channel: "telegram",
|
|
494
530
|
guardianExternalUserId: "user-42",
|
|
@@ -496,10 +532,10 @@ describe("guardian identity check", () => {
|
|
|
496
532
|
guardianDeliveryChatId: "chat-42",
|
|
497
533
|
});
|
|
498
534
|
|
|
499
|
-
expect(isGuardian("asst-1", "telegram", "user-42")).toBe(true);
|
|
535
|
+
expect(await isGuardian("asst-1", "telegram", "user-42")).toBe(true);
|
|
500
536
|
});
|
|
501
537
|
|
|
502
|
-
test("isGuardian returns false for non-matching user", () => {
|
|
538
|
+
test("isGuardian returns false for non-matching user", async () => {
|
|
503
539
|
createGuardianBinding({
|
|
504
540
|
channel: "telegram",
|
|
505
541
|
guardianExternalUserId: "user-42",
|
|
@@ -507,14 +543,14 @@ describe("guardian identity check", () => {
|
|
|
507
543
|
guardianDeliveryChatId: "chat-42",
|
|
508
544
|
});
|
|
509
545
|
|
|
510
|
-
expect(isGuardian("asst-1", "telegram", "user-99")).toBe(false);
|
|
546
|
+
expect(await isGuardian("asst-1", "telegram", "user-99")).toBe(false);
|
|
511
547
|
});
|
|
512
548
|
|
|
513
|
-
test("isGuardian returns false when no binding exists", () => {
|
|
514
|
-
expect(isGuardian("asst-1", "telegram", "user-42")).toBe(false);
|
|
549
|
+
test("isGuardian returns false when no binding exists", async () => {
|
|
550
|
+
expect(await isGuardian("asst-1", "telegram", "user-42")).toBe(false);
|
|
515
551
|
});
|
|
516
552
|
|
|
517
|
-
test("isGuardian returns false after binding is revoked", () => {
|
|
553
|
+
test("isGuardian returns false after binding is revoked", async () => {
|
|
518
554
|
createGuardianBinding({
|
|
519
555
|
channel: "telegram",
|
|
520
556
|
guardianExternalUserId: "user-42",
|
|
@@ -524,10 +560,10 @@ describe("guardian identity check", () => {
|
|
|
524
560
|
|
|
525
561
|
serviceRevokeBinding("asst-1", "telegram");
|
|
526
562
|
|
|
527
|
-
expect(isGuardian("asst-1", "telegram", "user-42")).toBe(false);
|
|
563
|
+
expect(await isGuardian("asst-1", "telegram", "user-42")).toBe(false);
|
|
528
564
|
});
|
|
529
565
|
|
|
530
|
-
test("getGuardianBinding returns the active binding", () => {
|
|
566
|
+
test("getGuardianBinding returns the active binding", async () => {
|
|
531
567
|
createGuardianBinding({
|
|
532
568
|
channel: "telegram",
|
|
533
569
|
guardianExternalUserId: "user-42",
|
|
@@ -535,17 +571,17 @@ describe("guardian identity check", () => {
|
|
|
535
571
|
guardianDeliveryChatId: "chat-42",
|
|
536
572
|
});
|
|
537
573
|
|
|
538
|
-
const binding = getGuardianBinding("asst-1", "telegram");
|
|
574
|
+
const binding = await getGuardianBinding("asst-1", "telegram");
|
|
539
575
|
expect(binding).not.toBeNull();
|
|
540
576
|
expect(binding!.guardianExternalUserId).toBe("user-42");
|
|
541
577
|
});
|
|
542
578
|
|
|
543
|
-
test("getGuardianBinding returns null when no binding exists", () => {
|
|
544
|
-
const binding = getGuardianBinding("asst-1", "telegram");
|
|
579
|
+
test("getGuardianBinding returns null when no binding exists", async () => {
|
|
580
|
+
const binding = await getGuardianBinding("asst-1", "telegram");
|
|
545
581
|
expect(binding).toBeNull();
|
|
546
582
|
});
|
|
547
583
|
|
|
548
|
-
test("isGuardian works for voice channel", () => {
|
|
584
|
+
test("isGuardian works for voice channel", async () => {
|
|
549
585
|
createGuardianBinding({
|
|
550
586
|
channel: "phone",
|
|
551
587
|
guardianExternalUserId: "phone-user-1",
|
|
@@ -553,13 +589,13 @@ describe("guardian identity check", () => {
|
|
|
553
589
|
guardianDeliveryChatId: "voice-chat-1",
|
|
554
590
|
});
|
|
555
591
|
|
|
556
|
-
expect(isGuardian("asst-1", "phone", "phone-user-1")).toBe(true);
|
|
557
|
-
expect(isGuardian("asst-1", "phone", "phone-user-2")).toBe(false);
|
|
592
|
+
expect(await isGuardian("asst-1", "phone", "phone-user-1")).toBe(true);
|
|
593
|
+
expect(await isGuardian("asst-1", "phone", "phone-user-2")).toBe(false);
|
|
558
594
|
// Telegram guardian should not match voice channel
|
|
559
|
-
expect(isGuardian("asst-1", "telegram", "phone-user-1")).toBe(false);
|
|
595
|
+
expect(await isGuardian("asst-1", "telegram", "phone-user-1")).toBe(false);
|
|
560
596
|
});
|
|
561
597
|
|
|
562
|
-
test("serviceRevokeBinding revokes the active binding", () => {
|
|
598
|
+
test("serviceRevokeBinding revokes the active binding", async () => {
|
|
563
599
|
createGuardianBinding({
|
|
564
600
|
channel: "telegram",
|
|
565
601
|
guardianExternalUserId: "user-42",
|
|
@@ -569,7 +605,7 @@ describe("guardian identity check", () => {
|
|
|
569
605
|
|
|
570
606
|
const result = serviceRevokeBinding("asst-1", "telegram");
|
|
571
607
|
expect(result).toBe(true);
|
|
572
|
-
expect(getGuardianBinding("asst-1", "telegram")).toBeNull();
|
|
608
|
+
expect(await getGuardianBinding("asst-1", "telegram")).toBeNull();
|
|
573
609
|
});
|
|
574
610
|
});
|
|
575
611
|
|
|
@@ -860,7 +896,7 @@ describe("channel-scoped guardian resolution", () => {
|
|
|
860
896
|
resetTables();
|
|
861
897
|
});
|
|
862
898
|
|
|
863
|
-
test("isGuardian resolves independently per channel", () => {
|
|
899
|
+
test("isGuardian resolves independently per channel", async () => {
|
|
864
900
|
// Create guardian binding on telegram
|
|
865
901
|
createGuardianBinding({
|
|
866
902
|
channel: "telegram",
|
|
@@ -877,15 +913,15 @@ describe("channel-scoped guardian resolution", () => {
|
|
|
877
913
|
});
|
|
878
914
|
|
|
879
915
|
// user-alpha is guardian for telegram but not voice
|
|
880
|
-
expect(isGuardian("self", "telegram", "user-alpha")).toBe(true);
|
|
881
|
-
expect(isGuardian("self", "phone", "user-alpha")).toBe(false);
|
|
916
|
+
expect(await isGuardian("self", "telegram", "user-alpha")).toBe(true);
|
|
917
|
+
expect(await isGuardian("self", "phone", "user-alpha")).toBe(false);
|
|
882
918
|
|
|
883
919
|
// user-beta is guardian for voice but not telegram
|
|
884
|
-
expect(isGuardian("self", "phone", "user-beta")).toBe(true);
|
|
885
|
-
expect(isGuardian("self", "telegram", "user-beta")).toBe(false);
|
|
920
|
+
expect(await isGuardian("self", "phone", "user-beta")).toBe(true);
|
|
921
|
+
expect(await isGuardian("self", "telegram", "user-beta")).toBe(false);
|
|
886
922
|
});
|
|
887
923
|
|
|
888
|
-
test("getGuardianBinding returns different bindings for different channels", () => {
|
|
924
|
+
test("getGuardianBinding returns different bindings for different channels", async () => {
|
|
889
925
|
createGuardianBinding({
|
|
890
926
|
channel: "telegram",
|
|
891
927
|
guardianExternalUserId: "user-alpha",
|
|
@@ -899,8 +935,8 @@ describe("channel-scoped guardian resolution", () => {
|
|
|
899
935
|
guardianDeliveryChatId: "chat-beta",
|
|
900
936
|
});
|
|
901
937
|
|
|
902
|
-
const bindingTelegram = getGuardianBinding("self", "telegram");
|
|
903
|
-
const bindingVoice = getGuardianBinding("self", "phone");
|
|
938
|
+
const bindingTelegram = await getGuardianBinding("self", "telegram");
|
|
939
|
+
const bindingVoice = await getGuardianBinding("self", "phone");
|
|
904
940
|
|
|
905
941
|
expect(bindingTelegram).not.toBeNull();
|
|
906
942
|
expect(bindingVoice).not.toBeNull();
|
|
@@ -908,7 +944,7 @@ describe("channel-scoped guardian resolution", () => {
|
|
|
908
944
|
expect(bindingVoice!.guardianExternalUserId).toBe("user-beta");
|
|
909
945
|
});
|
|
910
946
|
|
|
911
|
-
test("revoking binding for one channel does not affect another", () => {
|
|
947
|
+
test("revoking binding for one channel does not affect another", async () => {
|
|
912
948
|
createGuardianBinding({
|
|
913
949
|
channel: "telegram",
|
|
914
950
|
guardianExternalUserId: "user-alpha",
|
|
@@ -924,11 +960,11 @@ describe("channel-scoped guardian resolution", () => {
|
|
|
924
960
|
|
|
925
961
|
serviceRevokeBinding("self", "telegram");
|
|
926
962
|
|
|
927
|
-
expect(getGuardianBinding("self", "telegram")).toBeNull();
|
|
928
|
-
expect(getGuardianBinding("self", "phone")).not.toBeNull();
|
|
963
|
+
expect(await getGuardianBinding("self", "telegram")).toBeNull();
|
|
964
|
+
expect(await getGuardianBinding("self", "phone")).not.toBeNull();
|
|
929
965
|
});
|
|
930
966
|
|
|
931
|
-
test("validateAndConsumeVerification scoped to channel", () => {
|
|
967
|
+
test("validateAndConsumeVerification scoped to channel", async () => {
|
|
932
968
|
// Create challenge on telegram
|
|
933
969
|
const { secret: secretTelegram } =
|
|
934
970
|
createInboundVerificationSession("telegram");
|
|
@@ -961,8 +997,8 @@ describe("channel-scoped guardian resolution", () => {
|
|
|
961
997
|
);
|
|
962
998
|
expect(resultVoice.success).toBe(true);
|
|
963
999
|
|
|
964
|
-
const bindingTelegram = getGuardianBinding("self", "telegram");
|
|
965
|
-
const bindingVoice = getGuardianBinding("self", "phone");
|
|
1000
|
+
const bindingTelegram = await getGuardianBinding("self", "telegram");
|
|
1001
|
+
const bindingVoice = await getGuardianBinding("self", "phone");
|
|
966
1002
|
expect(bindingTelegram).toBeNull();
|
|
967
1003
|
expect(bindingVoice).toBeNull();
|
|
968
1004
|
});
|
|
@@ -1277,7 +1313,7 @@ describe("voice guardian challenge validation", () => {
|
|
|
1277
1313
|
}
|
|
1278
1314
|
});
|
|
1279
1315
|
|
|
1280
|
-
test("validateAndConsumeVerification does not create a guardian binding for voice (caller responsibility)", () => {
|
|
1316
|
+
test("validateAndConsumeVerification does not create a guardian binding for voice (caller responsibility)", async () => {
|
|
1281
1317
|
const { secret } = createInboundVerificationSession("phone");
|
|
1282
1318
|
|
|
1283
1319
|
validateAndConsumeVerification(
|
|
@@ -1287,7 +1323,7 @@ describe("voice guardian challenge validation", () => {
|
|
|
1287
1323
|
"voice-chat-1",
|
|
1288
1324
|
);
|
|
1289
1325
|
|
|
1290
|
-
const binding = getGuardianBinding("asst-1", "phone");
|
|
1326
|
+
const binding = await getGuardianBinding("asst-1", "phone");
|
|
1291
1327
|
expect(binding).toBeNull();
|
|
1292
1328
|
});
|
|
1293
1329
|
|
|
@@ -1361,7 +1397,7 @@ describe("voice guardian challenge validation", () => {
|
|
|
1361
1397
|
expect(result2.success).toBe(false);
|
|
1362
1398
|
});
|
|
1363
1399
|
|
|
1364
|
-
test("validateAndConsumeVerification succeeds even with existing voice binding (conflict check is caller responsibility)", () => {
|
|
1400
|
+
test("validateAndConsumeVerification succeeds even with existing voice binding (conflict check is caller responsibility)", async () => {
|
|
1365
1401
|
createGuardianBinding({
|
|
1366
1402
|
channel: "phone",
|
|
1367
1403
|
guardianExternalUserId: "old-voice-user",
|
|
@@ -1369,7 +1405,7 @@ describe("voice guardian challenge validation", () => {
|
|
|
1369
1405
|
guardianDeliveryChatId: "old-voice-chat",
|
|
1370
1406
|
});
|
|
1371
1407
|
|
|
1372
|
-
const oldBinding = getGuardianBinding("asst-1", "phone");
|
|
1408
|
+
const oldBinding = await getGuardianBinding("asst-1", "phone");
|
|
1373
1409
|
expect(oldBinding).not.toBeNull();
|
|
1374
1410
|
expect(oldBinding!.guardianExternalUserId).toBe("old-voice-user");
|
|
1375
1411
|
|
|
@@ -1385,7 +1421,7 @@ describe("voice guardian challenge validation", () => {
|
|
|
1385
1421
|
expect(result.success).toBe(true);
|
|
1386
1422
|
|
|
1387
1423
|
// The original binding is untouched (no side effects)
|
|
1388
|
-
const binding = getGuardianBinding("asst-1", "phone");
|
|
1424
|
+
const binding = await getGuardianBinding("asst-1", "phone");
|
|
1389
1425
|
expect(binding).not.toBeNull();
|
|
1390
1426
|
expect(binding!.guardianExternalUserId).toBe("old-voice-user");
|
|
1391
1427
|
});
|
|
@@ -1400,7 +1436,7 @@ describe("voice guardian identity and revocation", () => {
|
|
|
1400
1436
|
resetTables();
|
|
1401
1437
|
});
|
|
1402
1438
|
|
|
1403
|
-
test("isGuardian works for voice channel", () => {
|
|
1439
|
+
test("isGuardian works for voice channel", async () => {
|
|
1404
1440
|
createGuardianBinding({
|
|
1405
1441
|
channel: "phone",
|
|
1406
1442
|
guardianExternalUserId: "voice-user-1",
|
|
@@ -1408,13 +1444,13 @@ describe("voice guardian identity and revocation", () => {
|
|
|
1408
1444
|
guardianDeliveryChatId: "voice-chat-1",
|
|
1409
1445
|
});
|
|
1410
1446
|
|
|
1411
|
-
expect(isGuardian("asst-1", "phone", "voice-user-1")).toBe(true);
|
|
1412
|
-
expect(isGuardian("asst-1", "phone", "voice-user-2")).toBe(false);
|
|
1447
|
+
expect(await isGuardian("asst-1", "phone", "voice-user-1")).toBe(true);
|
|
1448
|
+
expect(await isGuardian("asst-1", "phone", "voice-user-2")).toBe(false);
|
|
1413
1449
|
// Voice guardian should not match telegram channel
|
|
1414
|
-
expect(isGuardian("asst-1", "telegram", "voice-user-1")).toBe(false);
|
|
1450
|
+
expect(await isGuardian("asst-1", "telegram", "voice-user-1")).toBe(false);
|
|
1415
1451
|
});
|
|
1416
1452
|
|
|
1417
|
-
test("getGuardianBinding returns voice binding", () => {
|
|
1453
|
+
test("getGuardianBinding returns voice binding", async () => {
|
|
1418
1454
|
createGuardianBinding({
|
|
1419
1455
|
channel: "phone",
|
|
1420
1456
|
guardianExternalUserId: "voice-user-1",
|
|
@@ -1422,13 +1458,13 @@ describe("voice guardian identity and revocation", () => {
|
|
|
1422
1458
|
guardianDeliveryChatId: "voice-chat-1",
|
|
1423
1459
|
});
|
|
1424
1460
|
|
|
1425
|
-
const binding = getGuardianBinding("asst-1", "phone");
|
|
1461
|
+
const binding = await getGuardianBinding("asst-1", "phone");
|
|
1426
1462
|
expect(binding).not.toBeNull();
|
|
1427
1463
|
expect(binding!.channel).toBe("phone");
|
|
1428
1464
|
expect(binding!.guardianExternalUserId).toBe("voice-user-1");
|
|
1429
1465
|
});
|
|
1430
1466
|
|
|
1431
|
-
test("revokeBinding clears active voice guardian binding", () => {
|
|
1467
|
+
test("revokeBinding clears active voice guardian binding", async () => {
|
|
1432
1468
|
createGuardianBinding({
|
|
1433
1469
|
channel: "phone",
|
|
1434
1470
|
guardianExternalUserId: "voice-user-1",
|
|
@@ -1438,10 +1474,10 @@ describe("voice guardian identity and revocation", () => {
|
|
|
1438
1474
|
|
|
1439
1475
|
const result = serviceRevokeBinding("asst-1", "phone");
|
|
1440
1476
|
expect(result).toBe(true);
|
|
1441
|
-
expect(getGuardianBinding("asst-1", "phone")).toBeNull();
|
|
1477
|
+
expect(await getGuardianBinding("asst-1", "phone")).toBeNull();
|
|
1442
1478
|
});
|
|
1443
1479
|
|
|
1444
|
-
test("revokeBinding for voice does not affect telegram binding", () => {
|
|
1480
|
+
test("revokeBinding for voice does not affect telegram binding", async () => {
|
|
1445
1481
|
createGuardianBinding({
|
|
1446
1482
|
channel: "phone",
|
|
1447
1483
|
guardianExternalUserId: "voice-user-1",
|
|
@@ -1457,8 +1493,8 @@ describe("voice guardian identity and revocation", () => {
|
|
|
1457
1493
|
|
|
1458
1494
|
serviceRevokeBinding("asst-1", "phone");
|
|
1459
1495
|
|
|
1460
|
-
expect(getGuardianBinding("asst-1", "phone")).toBeNull();
|
|
1461
|
-
expect(getGuardianBinding("asst-1", "telegram")).not.toBeNull();
|
|
1496
|
+
expect(await getGuardianBinding("asst-1", "phone")).toBeNull();
|
|
1497
|
+
expect(await getGuardianBinding("asst-1", "telegram")).not.toBeNull();
|
|
1462
1498
|
});
|
|
1463
1499
|
});
|
|
1464
1500
|
|
|
@@ -1716,7 +1752,7 @@ describe("HTTP handler voice guardian verification", () => {
|
|
|
1716
1752
|
expect(resp!.bound).toBe(false);
|
|
1717
1753
|
|
|
1718
1754
|
// Verify binding is actually revoked
|
|
1719
|
-
expect(getGuardianBinding("self", "phone")).toBeNull();
|
|
1755
|
+
expect(await getGuardianBinding("self", "phone")).toBeNull();
|
|
1720
1756
|
});
|
|
1721
1757
|
|
|
1722
1758
|
test("revoke for voice does not affect telegram binding", async () => {
|
|
@@ -1742,8 +1778,8 @@ describe("HTTP handler voice guardian verification", () => {
|
|
|
1742
1778
|
|
|
1743
1779
|
await handleChannelVerificationSession(msg);
|
|
1744
1780
|
|
|
1745
|
-
expect(getGuardianBinding("self", "phone")).toBeNull();
|
|
1746
|
-
expect(getGuardianBinding("self", "telegram")).not.toBeNull();
|
|
1781
|
+
expect(await getGuardianBinding("self", "phone")).toBeNull();
|
|
1782
|
+
expect(await getGuardianBinding("self", "telegram")).not.toBeNull();
|
|
1747
1783
|
});
|
|
1748
1784
|
});
|
|
1749
1785
|
|
|
@@ -81,6 +81,8 @@ mock.module("../runtime/gateway-client.js", () => ({
|
|
|
81
81
|
}));
|
|
82
82
|
|
|
83
83
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
84
|
+
setConversationProcessingStartedAt: () => {},
|
|
85
|
+
isConversationProcessing: () => false,
|
|
84
86
|
setConversationOriginChannelIfUnset: () => {},
|
|
85
87
|
updateConversationContextWindow: () => {},
|
|
86
88
|
deleteMessageById: () => {},
|
|
@@ -125,6 +125,8 @@ mock.module("../security/secret-allowlist.js", () => ({
|
|
|
125
125
|
}));
|
|
126
126
|
|
|
127
127
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
128
|
+
setConversationProcessingStartedAt: () => {},
|
|
129
|
+
isConversationProcessing: () => false,
|
|
128
130
|
setConversationOriginChannelIfUnset: () => {},
|
|
129
131
|
updateConversationContextWindow: () => {},
|
|
130
132
|
setConversationHistoryStrippedAt: () => {},
|
|
@@ -17,6 +17,8 @@ mock.module("../util/logger.js", () => ({
|
|
|
17
17
|
// The compactor reads the conversation's image attachments from the DB to
|
|
18
18
|
// build its manifest; with no images these return empty.
|
|
19
19
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
20
|
+
setConversationProcessingStartedAt: () => {},
|
|
21
|
+
isConversationProcessing: () => false,
|
|
20
22
|
getMessages: () => [],
|
|
21
23
|
}));
|
|
22
24
|
mock.module("../memory/attachments-store.js", () => ({
|
|
@@ -30,6 +30,8 @@ mock.module("../util/logger.js", () => ({
|
|
|
30
30
|
}));
|
|
31
31
|
|
|
32
32
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
33
|
+
setConversationProcessingStartedAt: () => {},
|
|
34
|
+
isConversationProcessing: () => false,
|
|
33
35
|
getMessages: () => [],
|
|
34
36
|
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
35
37
|
}));
|