@vellumai/assistant 0.7.1 → 0.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +32 -49
- package/Dockerfile +1 -0
- package/README.md +1 -2
- package/__tests__/permissions/gateway-threshold-reader.test.ts +9 -3
- package/bun.lock +26 -26
- package/docs/architecture/security.md +20 -0
- package/docs/plugins.md +7 -9
- package/knip.json +1 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/src/ipc-client.ts +39 -1
- package/node_modules/@vellumai/gateway-client/src/types.ts +11 -0
- package/node_modules/@vellumai/service-contracts/package.json +2 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/contracts.test.ts +4 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +107 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +5 -1
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +24 -0
- package/node_modules/@vellumai/service-contracts/src/twilio-ingress.ts +84 -0
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +9 -0
- package/node_modules/@vellumai/twilio-client/bun.lock +24 -0
- package/node_modules/@vellumai/twilio-client/package.json +18 -0
- package/node_modules/@vellumai/twilio-client/src/__tests__/twilio-client.test.ts +128 -0
- package/node_modules/@vellumai/twilio-client/src/index.ts +179 -0
- package/node_modules/@vellumai/twilio-client/tsconfig.json +20 -0
- package/openapi.yaml +565 -12
- package/package.json +6 -3
- package/src/__tests__/app-builder-tool-scripts.test.ts +3 -3
- package/src/__tests__/app-bundler.test.ts +170 -1
- package/src/__tests__/app-control-flow.test.ts +374 -0
- package/src/__tests__/app-control-no-global-cgevent.test.ts +98 -0
- package/src/__tests__/app-control-tool-schemas.test.ts +621 -0
- package/src/__tests__/app-executors.test.ts +30 -43
- package/src/__tests__/approval-routes-http.test.ts +23 -6
- package/src/__tests__/assistant-event-hub-machine-name.test.ts +146 -0
- package/src/__tests__/assistant-event-hub-targeted.test.ts +257 -0
- package/src/__tests__/assistant-event-hub.test.ts +109 -2
- package/src/__tests__/assistant-event.test.ts +10 -0
- package/src/__tests__/assistant-events-sse-hardening.test.ts +7 -2
- package/src/__tests__/assistant-feature-flags-integration.test.ts +11 -7
- package/src/__tests__/background-shell-host-bash.test.ts +14 -15
- package/src/__tests__/bootstrap-turn-cleanup.test.ts +44 -0
- package/src/__tests__/btw-routes.test.ts +13 -4
- package/src/__tests__/call-controller.test.ts +49 -1
- package/src/__tests__/call-domain.test.ts +0 -2
- package/src/__tests__/call-routes-http.test.ts +0 -2
- package/src/__tests__/channel-readiness-service.test.ts +59 -1
- package/src/__tests__/checker.test.ts +3 -4
- package/src/__tests__/config-loader-backfill.test.ts +90 -155
- package/src/__tests__/config-loader-platform-defaults.test.ts +196 -0
- package/src/__tests__/config-schema-cmd.test.ts +0 -1
- package/src/__tests__/config-set-platform-guard.test.ts +48 -4
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +2 -2
- package/src/__tests__/config-watcher.test.ts +2 -2
- package/src/__tests__/conversation-app-control-instantiation.test.ts +392 -0
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +237 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +0 -2
- package/src/__tests__/conversation-lifecycle.test.ts +36 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +283 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +6 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +120 -72
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/conversation-slash-commands.test.ts +0 -4
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +202 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +317 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +5 -12
- package/src/__tests__/credential-execution-managed-contract.test.ts +3 -131
- package/src/__tests__/credentials-cli.test.ts +5 -12
- package/src/__tests__/cu-unified-flow.test.ts +185 -23
- package/src/__tests__/daemon-credential-client.test.ts +101 -19
- package/src/__tests__/db-schedule-syntax-migration.test.ts +2 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -2
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +0 -2
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +0 -1
- package/src/__tests__/heartbeat-service.test.ts +718 -1
- package/src/__tests__/helpers/call-route-handler.ts +7 -1
- package/src/__tests__/host-app-control-proxy.test.ts +602 -0
- package/src/__tests__/host-app-control-routes.test.ts +263 -0
- package/src/__tests__/host-bash-proxy.test.ts +246 -47
- package/src/__tests__/host-bash-routes.test.ts +294 -0
- package/src/__tests__/host-browser-proxy.test.ts +24 -22
- package/src/__tests__/host-browser-routes.test.ts +39 -13
- package/src/__tests__/host-cu-proxy.test.ts +41 -52
- package/src/__tests__/host-cu-routes-targeted.test.ts +300 -0
- package/src/__tests__/host-file-edit-tool.test.ts +47 -1
- package/src/__tests__/host-file-proxy-targeted.test.ts +339 -0
- package/src/__tests__/host-file-proxy.test.ts +37 -43
- package/src/__tests__/host-file-read-tool.test.ts +17 -0
- package/src/__tests__/host-file-routes-targeted.test.ts +262 -0
- package/src/__tests__/host-file-write-tool.test.ts +42 -1
- package/src/__tests__/host-proxy-base.test.ts +312 -0
- package/src/__tests__/host-shell-tool.test.ts +22 -4
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +583 -0
- package/src/__tests__/host-transfer-proxy.test.ts +121 -22
- package/src/__tests__/host-transfer-routes-targeted.test.ts +447 -0
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/identity-intro-cache.test.ts +29 -0
- package/src/__tests__/identity-routes.test.ts +103 -1
- package/src/__tests__/init-feature-flag-overrides.test.ts +26 -3
- package/src/__tests__/inline-command-runner.test.ts +0 -1
- package/src/__tests__/inline-skill-load-permissions.test.ts +5 -11
- package/src/__tests__/integration-status.test.ts +85 -5
- package/src/__tests__/intent-routing.test.ts +0 -1
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +95 -5
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +17 -0
- package/src/__tests__/managed-skill-lifecycle.test.ts +0 -1
- package/src/__tests__/mcp-auth-routes.test.ts +197 -0
- package/src/__tests__/mcp-cli.test.ts +338 -2
- package/src/__tests__/memory-jobs-worker-lanes.test.ts +188 -0
- package/src/__tests__/migration-import-commit-http.test.ts +108 -2
- package/src/__tests__/mock-gateway-ipc.ts +1 -0
- package/src/__tests__/oauth-cli.test.ts +0 -2
- package/src/__tests__/oauth2-gateway-transport.test.ts +0 -1
- package/src/__tests__/persistence-secret-redaction.test.ts +299 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +5 -9
- package/src/__tests__/prechat-onboarding-contract.test.ts +3 -1
- package/src/__tests__/process-message-background-slack.test.ts +2 -0
- package/src/__tests__/provider-commit-message-generator.test.ts +0 -1
- package/src/__tests__/public-ingress-urls.test.ts +97 -0
- package/src/__tests__/require-fresh-approval.test.ts +0 -1
- package/src/__tests__/retry-backoff.test.ts +87 -0
- package/src/__tests__/runtime-events-sse.test.ts +10 -6
- package/src/__tests__/sanitize-config-for-transfer.test.ts +24 -2
- package/src/__tests__/schedule-retry.test.ts +715 -0
- package/src/__tests__/script-proxy-mitm-handler.test.ts +1 -1
- package/src/__tests__/secret-ingress-http.test.ts +1 -0
- package/src/__tests__/send-endpoint-busy.test.ts +3 -0
- package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -1
- package/src/__tests__/skill-feature-flags.test.ts +43 -41
- package/src/__tests__/skill-load-feature-flag.test.ts +13 -14
- package/src/__tests__/skill-load-inline-command.test.ts +0 -51
- package/src/__tests__/skill-load-inline-includes.test.ts +0 -43
- package/src/__tests__/skill-projection.benchmark.test.ts +0 -1
- package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -1
- package/src/__tests__/slack-channel-config.test.ts +9 -14
- package/src/__tests__/system-prompt-ask-mode.test.ts +0 -1
- package/src/__tests__/system-prompt.test.ts +0 -1
- package/src/__tests__/telegram-config.test.ts +0 -1
- package/src/__tests__/test-preload.ts +8 -0
- package/src/__tests__/tool-approval-handler.test.ts +3 -4
- package/src/__tests__/tool-audit-listener.test.ts +48 -0
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -1
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -1
- package/src/__tests__/tool-executor.test.ts +0 -1
- package/src/__tests__/twilio-config.test.ts +3 -16
- package/src/__tests__/twilio-routes.test.ts +3 -5
- package/src/__tests__/twilio-validation.test.ts +93 -0
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -4
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -4
- package/src/__tests__/voice-ingress-preflight.test.ts +19 -0
- package/src/__tests__/workspace-migration-006-services-config.test.ts +3 -2
- package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +1 -5
- package/src/__tests__/workspace-migration-down-functions.test.ts +8 -8
- package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +10 -6
- package/src/backup/__tests__/paths.test.ts +0 -22
- package/src/backup/__tests__/restore.test.ts +51 -151
- package/src/backup/paths.ts +2 -18
- package/src/backup/restore.ts +107 -231
- package/src/bundler/app-bundler.ts +51 -3
- package/src/calls/relay-server.ts +4 -44
- package/src/calls/twilio-config.ts +2 -17
- package/src/calls/twilio-rest.ts +33 -105
- package/src/calls/twilio-routes.ts +11 -12
- package/src/channels/types.ts +8 -7
- package/src/cli/commands/__tests__/backup.test.ts +6 -277
- package/src/cli/commands/__tests__/gateway.test.ts +288 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +4 -0
- package/src/cli/commands/__tests__/webhooks.test.ts +0 -1
- package/src/cli/commands/backup.ts +6 -331
- package/src/cli/commands/clients.ts +36 -37
- package/src/cli/commands/contacts.ts +73 -0
- package/src/cli/commands/conversations.ts +2 -5
- package/src/cli/commands/credentials.ts +15 -7
- package/src/cli/commands/domain.ts +66 -15
- package/src/cli/commands/gateway.ts +183 -0
- package/src/cli/commands/keys.ts +9 -6
- package/src/cli/commands/mcp.ts +116 -156
- package/src/cli/commands/memory-v2.ts +296 -1
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/connect.test.ts +0 -2
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -2
- package/src/cli/commands/platform/__tests__/status.test.ts +13 -15
- package/src/cli/commands/platform/disconnect.ts +5 -4
- package/src/cli/commands/platform/index.ts +0 -18
- package/src/cli/lib/daemon-credential-client.ts +110 -28
- package/src/cli/program.ts +2 -0
- package/src/config/assistant-feature-flags.ts +67 -10
- package/src/config/bundled-skills/acp/SKILL.md +6 -0
- package/src/config/bundled-skills/acp/TOOLS.json +1 -22
- package/src/config/bundled-skills/app-builder/SKILL.md +14 -109
- package/src/config/bundled-skills/app-builder/TOOLS.json +1 -28
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +1 -10
- package/src/config/bundled-skills/app-control/SKILL.md +75 -0
- package/src/config/bundled-skills/app-control/TOOLS.json +299 -0
- package/src/config/bundled-skills/app-control/tools/app-control-click.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-combo.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-drag.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-observe.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-press.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-sequence.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-start.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-stop.ts +12 -0
- package/src/config/bundled-skills/app-control/tools/app-control-type.ts +12 -0
- package/src/config/bundled-skills/computer-use/SKILL.md +6 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +67 -43
- package/src/config/bundled-skills/contacts/TOOLS.json +0 -16
- package/src/config/bundled-skills/document/TOOLS.json +0 -8
- package/src/config/bundled-skills/followups/TOOLS.json +0 -12
- package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
- package/src/config/bundled-skills/image-studio/TOOLS.json +0 -4
- package/src/config/bundled-skills/media-processing/TOOLS.json +0 -24
- package/src/config/bundled-skills/messaging/TOOLS.json +0 -40
- package/src/config/bundled-skills/phone-calls/TOOLS.json +0 -12
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +19 -4
- package/src/config/bundled-skills/playbooks/TOOLS.json +0 -16
- package/src/config/bundled-skills/schedule/TOOLS.json +14 -14
- package/src/config/bundled-skills/sequences/TOOLS.json +0 -36
- package/src/config/bundled-skills/settings/SKILL.md +4 -0
- package/src/config/bundled-skills/settings/TOOLS.json +0 -12
- package/src/config/bundled-skills/skill-management/SKILL.md +6 -0
- package/src/config/bundled-skills/skill-management/TOOLS.json +0 -8
- package/src/config/bundled-skills/subagent/SKILL.md +6 -2
- package/src/config/bundled-skills/subagent/TOOLS.json +0 -20
- package/src/config/bundled-skills/transcribe/SKILL.md +4 -0
- package/src/config/bundled-skills/transcribe/TOOLS.json +0 -4
- package/src/config/bundled-tool-registry.ts +21 -0
- package/src/config/env-registry.ts +0 -2
- package/src/config/env.ts +19 -12
- package/src/config/feature-flag-registry.json +21 -133
- package/src/config/loader.ts +73 -99
- package/src/config/sanitize-for-transfer.ts +2 -0
- package/src/config/schemas/__tests__/memory-lifecycle.test.ts +80 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +7 -4
- package/src/config/schemas/calls.ts +0 -9
- package/src/config/schemas/heartbeat.ts +63 -0
- package/src/config/schemas/ingress.ts +10 -6
- package/src/config/schemas/llm.ts +5 -10
- package/src/config/schemas/memory-lifecycle.ts +77 -24
- package/src/config/schemas/memory-v2.ts +48 -4
- package/src/config/schemas/platform.ts +6 -0
- package/src/config/schemas/services.ts +1 -15
- package/src/config/schemas/skills.ts +0 -6
- package/src/config/seed-inference-profiles.ts +1 -1
- package/src/contacts/contact-store.ts +0 -30
- package/src/contacts/contacts-write.ts +0 -27
- package/src/context/window-manager.ts +1 -2
- package/src/credential-execution/feature-gates.ts +10 -10
- package/src/credential-execution/process-manager.ts +12 -41
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +126 -5
- package/src/daemon/bootstrap-turn-cleanup.ts +45 -0
- package/src/daemon/config-watcher.ts +4 -3
- package/src/daemon/conversation-agent-loop-handlers.ts +21 -3
- package/src/daemon/conversation-agent-loop.ts +32 -28
- package/src/daemon/conversation-lifecycle.ts +8 -1
- package/src/daemon/conversation-process.ts +16 -11
- package/src/daemon/conversation-runtime-assembly.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +125 -4
- package/src/daemon/conversation-tool-setup.ts +16 -55
- package/src/daemon/conversation.ts +21 -2
- package/src/daemon/doordash-steps.ts +1 -1
- package/src/daemon/handlers/shared.ts +4 -1
- package/src/daemon/host-app-control-proxy.ts +293 -0
- package/src/daemon/host-bash-proxy.ts +84 -74
- package/src/daemon/host-browser-proxy.ts +67 -82
- package/src/daemon/host-cu-proxy.ts +81 -86
- package/src/daemon/host-file-proxy.ts +93 -69
- package/src/daemon/host-proxy-base.ts +294 -0
- package/src/daemon/host-proxy-preactivation.ts +82 -0
- package/src/daemon/host-transfer-proxy.ts +247 -129
- package/src/daemon/lifecycle.ts +115 -117
- package/src/daemon/message-protocol.ts +3 -8
- package/src/daemon/message-types/contacts.ts +23 -1
- package/src/daemon/message-types/conversations.ts +11 -8
- package/src/daemon/message-types/host-app-control.ts +150 -0
- package/src/daemon/message-types/host-bash.ts +4 -0
- package/src/daemon/message-types/host-cu.ts +2 -0
- package/src/daemon/message-types/host-file.ts +4 -0
- package/src/daemon/message-types/host-transfer.ts +3 -0
- package/src/daemon/message-types/schedules.ts +8 -3
- package/src/daemon/message-types/skills.ts +2 -2
- package/src/daemon/process-message.ts +18 -1
- package/src/daemon/shutdown-handlers.ts +0 -3
- package/src/daemon/tool-setup-types.ts +51 -0
- package/src/daemon/tool-side-effects.ts +1 -1
- package/src/events/tool-audit-listener.ts +2 -1
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +15 -7
- package/src/heartbeat/__tests__/heartbeat-run-store.test.ts +216 -0
- package/src/heartbeat/heartbeat-run-store.ts +236 -0
- package/src/heartbeat/heartbeat-service.ts +280 -49
- package/src/home/__tests__/post-connect-feed.test.ts +99 -0
- package/src/home/__tests__/relationship-state-writer.test.ts +11 -9
- package/src/home/__tests__/suggested-prompts.test.ts +89 -0
- package/src/home/post-connect-feed.ts +68 -0
- package/src/home/relationship-state-writer.ts +17 -92
- package/src/home/suggested-prompts.ts +46 -10
- package/src/inbound/public-ingress-urls.ts +32 -34
- package/src/ipc/__tests__/route-error-envelope.test.ts +80 -0
- package/src/ipc/assistant-server.ts +14 -1
- package/src/ipc/cli-client.ts +32 -1
- package/src/live-voice/live-voice-metrics.ts +10 -10
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +304 -0
- package/src/mcp/mcp-auth-orchestrator.ts +213 -0
- package/src/mcp/mcp-auth-state.ts +133 -0
- package/src/mcp/mcp-oauth-provider.ts +19 -0
- package/src/memory/__tests__/jobs-store-job-classes.test.ts +24 -0
- package/src/memory/__tests__/qdrant-client-sentinel.test.ts +49 -0
- package/src/memory/__tests__/sparse-tokenize.test.ts +66 -0
- package/src/memory/anisotropy.test.ts +247 -0
- package/src/memory/anisotropy.ts +443 -0
- package/src/memory/auto-analysis-constants.ts +17 -0
- package/src/memory/auto-analysis-guard.ts +5 -15
- package/src/memory/canonical-guardian-store.ts +7 -7
- package/src/memory/context-search/__tests__/agent-runner-redaction.test.ts +122 -0
- package/src/memory/context-search/agent-protocol.ts +6 -6
- package/src/memory/context-search/agent-runner.ts +32 -7
- package/src/memory/context-search/sources/memory-v2.ts +17 -5
- package/src/memory/conversation-crud.ts +1 -1
- package/src/memory/conversation-key-store.ts +2 -15
- package/src/memory/db-init.ts +4 -0
- package/src/memory/embedding-backend.ts +9 -21
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +49 -4
- package/src/memory/graph/conversation-graph-memory.ts +1 -24
- package/src/memory/graph/graph-search.ts +8 -0
- package/src/memory/graph/retriever.ts +28 -0
- package/src/memory/graph/tools.ts +1 -1
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +8 -2
- package/src/memory/jobs/embed-concept-page.ts +28 -2
- package/src/memory/jobs/embed-pkb-file.test.ts +2 -2
- package/src/memory/jobs-store.ts +66 -22
- package/src/memory/jobs-worker.ts +112 -63
- package/src/memory/memory-v2-activation-log-store.ts +1 -1
- package/src/memory/migrations/237-heartbeat-runs.ts +45 -0
- package/src/memory/migrations/238-schedule-retry-policy.ts +20 -0
- package/src/memory/migrations/index.ts +5 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/pkb/pkb-search.ts +7 -0
- package/src/memory/qdrant-client.ts +50 -20
- package/src/memory/schema/infrastructure.ts +15 -0
- package/src/memory/search/semantic.ts +7 -0
- package/src/memory/sparse-tokenize.ts +49 -0
- package/src/memory/v2/__tests__/activation.test.ts +77 -95
- package/src/memory/v2/__tests__/injection.test.ts +43 -21
- package/src/memory/v2/__tests__/sim.test.ts +166 -6
- package/src/memory/v2/__tests__/sparse-bm25.test.ts +292 -0
- package/src/memory/v2/__tests__/static-context.test.ts +0 -1
- package/src/memory/v2/activation.ts +69 -88
- package/src/memory/v2/consolidation-job.ts +3 -5
- package/src/memory/v2/constants.ts +7 -0
- package/src/memory/v2/injection.ts +86 -53
- package/src/memory/v2/prompts/consolidation.ts +312 -91
- package/src/memory/v2/qdrant.ts +99 -1
- package/src/memory/v2/sim.ts +126 -16
- package/src/memory/v2/skill-qdrant.ts +12 -3
- package/src/memory/v2/skill-store.ts +16 -1
- package/src/memory/v2/sparse-bm25.ts +245 -0
- package/src/memory/v2/static-context.ts +6 -5
- package/src/messaging/providers/gmail/types.ts +0 -49
- package/src/messaging/providers/slack/adapter.ts +1 -31
- package/src/messaging/providers/slack/types.ts +0 -32
- package/src/notifications/README.md +10 -10
- package/src/notifications/broadcaster.ts +1 -1
- package/src/notifications/guardian-question-mode.ts +5 -5
- package/src/oauth/connect-orchestrator.ts +4 -0
- package/src/oauth/credential-token-resolver.ts +1 -3
- package/src/oauth/manual-token-connection.ts +0 -4
- package/src/outbound-proxy/index.ts +1 -37
- package/src/outbound-proxy/logging.ts +1 -1
- package/src/outbound-proxy/policy.ts +6 -5
- package/src/outbound-proxy/router.ts +2 -1
- package/src/permissions/approval-policy.test.ts +6 -275
- package/src/permissions/approval-policy.ts +0 -51
- package/src/permissions/checker.test.ts +0 -1
- package/src/permissions/checker.ts +3 -17
- package/src/permissions/gateway-threshold-reader.ts +2 -0
- package/src/permissions/prompter.ts +34 -1
- package/src/permissions/secret-prompter.ts +6 -2
- package/src/prompts/bootstrap-cleanup.ts +27 -0
- package/src/prompts/system-prompt.ts +3 -18
- package/src/prompts/templates/SOUL.md +13 -1
- package/src/providers/speech-to-text/provider-catalog.ts +7 -8
- package/src/runtime/assistant-event-hub.ts +118 -96
- package/src/runtime/assistant-event.ts +1 -0
- package/src/runtime/auth/__tests__/middleware.test.ts +11 -56
- package/src/runtime/auth/middleware.ts +0 -96
- package/src/runtime/auth/route-policy.ts +19 -0
- package/src/runtime/btw-sidechain.ts +2 -3
- package/src/runtime/channel-invite-transport.ts +2 -48
- package/src/runtime/channel-invite-transports/email.ts +1 -1
- package/src/runtime/channel-invite-transports/slack.ts +1 -1
- package/src/runtime/channel-invite-transports/telegram.ts +1 -1
- package/src/runtime/channel-invite-transports/voice.ts +1 -1
- package/src/runtime/channel-invite-transports/whatsapp.ts +1 -1
- package/src/runtime/channel-invite-types.ts +54 -0
- package/src/runtime/channel-readiness-service.ts +32 -13
- package/src/runtime/http-server.ts +3 -329
- package/src/runtime/http-types.ts +0 -5
- package/src/runtime/migrations/__tests__/vbundle-import-parity.test.ts +413 -0
- package/src/runtime/migrations/__tests__/vbundle-import-policy.test.ts +260 -0
- package/src/runtime/migrations/__tests__/vbundle-import-version-compat.test.ts +189 -0
- package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +153 -1
- package/src/runtime/migrations/__tests__/vbundle-symlink-importer.test.ts +451 -0
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming.test.ts +515 -0
- package/src/runtime/migrations/__tests__/vbundle-symlink-tar.test.ts +437 -0
- package/src/runtime/migrations/__tests__/vbundle-symlink-walker.test.ts +319 -0
- package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +51 -1
- package/src/runtime/migrations/migration-transport.ts +7 -7
- package/src/runtime/migrations/vbundle-builder.ts +327 -60
- package/src/runtime/migrations/vbundle-import-analyzer.ts +4 -4
- package/src/runtime/migrations/vbundle-import-policy.ts +172 -0
- package/src/runtime/migrations/vbundle-importer.ts +245 -68
- package/src/runtime/migrations/vbundle-streaming-importer.ts +326 -35
- package/src/runtime/migrations/vbundle-streaming-validator.ts +157 -4
- package/src/runtime/migrations/vbundle-tar-stream.ts +15 -6
- package/src/runtime/migrations/vbundle-validator.ts +114 -0
- package/src/runtime/pending-interactions.ts +35 -9
- package/src/runtime/routes/__tests__/backup-routes.test.ts +22 -150
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
- package/src/runtime/routes/__tests__/gateway-log-routes.test.ts +242 -0
- package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +112 -0
- package/src/runtime/routes/approval-interception-types.ts +13 -0
- package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +1 -1
- package/src/runtime/routes/backup-routes.ts +15 -38
- package/src/runtime/routes/btw-routes.ts +14 -37
- package/src/runtime/routes/client-routes.ts +1 -0
- package/src/runtime/routes/contact-prompt-routes.ts +183 -0
- package/src/runtime/routes/conversation-query-routes.ts +36 -1
- package/src/runtime/routes/conversation-routes.ts +30 -13
- package/src/runtime/routes/document-pdf-renderer.ts +165 -0
- package/src/runtime/routes/documents-routes.ts +30 -0
- package/src/runtime/routes/errors.ts +19 -4
- package/src/runtime/routes/events-routes.ts +12 -6
- package/src/runtime/routes/gateway-log-routes.ts +79 -0
- package/src/runtime/routes/guardian-approval-interception.ts +2 -8
- package/src/runtime/routes/heartbeat-routes.ts +103 -38
- package/src/runtime/routes/host-app-control-routes.ts +134 -0
- package/src/runtime/routes/host-bash-routes.ts +36 -6
- package/src/runtime/routes/host-browser-routes.ts +108 -13
- package/src/runtime/routes/host-cu-routes.ts +44 -14
- package/src/runtime/routes/host-file-routes.ts +33 -10
- package/src/runtime/routes/host-transfer-routes.ts +64 -24
- package/src/runtime/routes/http-adapter.ts +1 -0
- package/src/runtime/routes/identity-intro-cache.ts +30 -0
- package/src/runtime/routes/identity-routes.ts +15 -43
- package/src/runtime/routes/inbound-message-handler.ts +1 -9
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -7
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +0 -8
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +0 -20
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +5 -13
- package/src/runtime/routes/index.ts +8 -0
- package/src/runtime/routes/mcp-auth-routes.ts +132 -0
- package/src/runtime/routes/memory-item-routes.ts +10 -12
- package/src/runtime/routes/memory-v2-routes.ts +441 -1
- package/src/runtime/routes/migration-routes.ts +96 -0
- package/src/runtime/routes/schedule-routes.ts +7 -0
- package/src/runtime/verification-templates.ts +4 -7
- package/src/schedule/integration-status.ts +66 -2
- package/src/schedule/recurrence-engine.ts +4 -1
- package/src/schedule/retry-backoff.ts +18 -0
- package/src/schedule/retry-policy.ts +82 -0
- package/src/schedule/schedule-recovery.ts +64 -0
- package/src/schedule/schedule-store.ts +106 -2
- package/src/schedule/scheduler-types.ts +25 -0
- package/src/schedule/scheduler.ts +63 -38
- package/src/security/oauth-callback-registry.ts +8 -0
- package/src/sequence/analytics.ts +5 -5
- package/src/sequence/engine.ts +1 -1
- package/src/skills/catalog-files.ts +2 -8
- package/src/skills/include-graph.ts +5 -5
- package/src/skills/remote-skill-policy.ts +5 -5
- package/src/skills/skill-file-provider.ts +1 -1
- package/src/skills/skill-file-types.ts +13 -0
- package/src/skills/skillssh-audit-types.ts +28 -0
- package/src/skills/skillssh-registry.ts +8 -21
- package/src/telemetry/types.ts +2 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +21 -0
- package/src/telemetry/usage-telemetry-reporter.ts +1 -0
- package/src/tools/app-control/skill-proxy-bridge.ts +28 -0
- package/src/tools/apps/executors.ts +56 -69
- package/src/tools/browser/__tests__/browser-status.test.ts +21 -18
- package/src/tools/browser/browser-execution.ts +2 -2
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +55 -4
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +12 -6
- package/src/tools/browser/cdp-client/factory.ts +23 -24
- package/src/tools/browser/cdp-client/index.ts +1 -14
- package/src/tools/computer-use/definitions.ts +42 -20
- package/src/tools/executor.ts +2 -0
- package/src/tools/host-filesystem/edit.ts +26 -0
- package/src/tools/host-filesystem/read.ts +26 -0
- package/src/tools/host-filesystem/transfer.ts +31 -1
- package/src/tools/host-filesystem/write.ts +26 -0
- package/src/tools/host-terminal/host-shell.ts +58 -0
- package/src/tools/schedule/create.ts +6 -0
- package/src/tools/schedule/list.ts +2 -0
- package/src/tools/schedule/update.ts +10 -0
- package/src/tools/shared/filesystem/file-ops-service.ts +2 -0
- package/src/tools/shared/filesystem/path-policy.ts +25 -1
- package/src/tools/skills/load.ts +0 -32
- package/src/tools/tool-approval-handler.ts +1 -5
- package/src/tools/types.ts +4 -0
- package/src/usage/pricing.ts +1 -1
- package/src/workspace/hatched-date.ts +86 -0
- package/src/workspace/migrations/003-seed-device-id.ts +1 -1
- package/src/workspace/migrations/006-services-config.ts +8 -5
- package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +3 -9
- package/src/workspace/migrations/021-move-signals-to-workspace.ts +4 -10
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +4 -10
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +4 -11
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +3 -10
- package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +3 -2
- package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +2 -1
- package/src/workspace/migrations/059-move-pid-to-workspace.ts +3 -8
- package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +3 -8
- package/src/workspace/migrations/AGENTS.md +1 -1
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +4 -10
- package/src/workspace/migrations/utils.ts +21 -0
- package/src/__tests__/host-browser-e2e-cloud.test.ts +0 -443
- package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +0 -226
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +0 -427
- package/src/__tests__/twilio-rest.test.ts +0 -34
- package/src/backup/__tests__/backup-key.test.ts +0 -152
- package/src/backup/__tests__/backup-worker.test.ts +0 -782
- package/src/backup/__tests__/offsite-writer.test.ts +0 -641
- package/src/backup/__tests__/stream-crypt.test.ts +0 -228
- package/src/backup/backup-key.ts +0 -137
- package/src/backup/backup-worker.ts +0 -472
- package/src/backup/offsite-writer.ts +0 -222
- package/src/backup/stream-crypt.ts +0 -263
- package/src/daemon/message-types/pairing.ts +0 -58
- package/src/outbound-proxy/config.ts +0 -20
- package/src/outbound-proxy/health.ts +0 -18
- package/src/outbound-proxy/types.ts +0 -150
- package/src/runtime/capability-tokens.ts +0 -190
- package/src/signals/mcp-reload.ts +0 -18
|
@@ -60,6 +60,7 @@ mock.module("../prompts/persona-resolver.js", () => ({
|
|
|
60
60
|
import {
|
|
61
61
|
computeIdentityContentHash,
|
|
62
62
|
getCachedIntro,
|
|
63
|
+
readWorkspaceIdentityIntro,
|
|
63
64
|
setCachedIntro,
|
|
64
65
|
} from "../runtime/routes/identity-intro-cache.js";
|
|
65
66
|
|
|
@@ -80,6 +81,34 @@ afterEach(() => {
|
|
|
80
81
|
// ---------------------------------------------------------------------------
|
|
81
82
|
|
|
82
83
|
describe("identity intro cache", () => {
|
|
84
|
+
test("reads explicit intro from IDENTITY.md before SOUL.md", () => {
|
|
85
|
+
workspaceFiles["IDENTITY.md"] = [
|
|
86
|
+
"# Identity",
|
|
87
|
+
"",
|
|
88
|
+
"## Identity Intro",
|
|
89
|
+
"Nova here.",
|
|
90
|
+
].join("\n");
|
|
91
|
+
workspaceFiles["SOUL.md"] = [
|
|
92
|
+
"# Soul",
|
|
93
|
+
"",
|
|
94
|
+
"## Identity Intro",
|
|
95
|
+
"Soul fallback.",
|
|
96
|
+
].join("\n");
|
|
97
|
+
|
|
98
|
+
expect(readWorkspaceIdentityIntro()).toBe("Nova here.");
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("falls back to SOUL.md identity intro for legacy workspaces", () => {
|
|
102
|
+
workspaceFiles["SOUL.md"] = [
|
|
103
|
+
"# Soul",
|
|
104
|
+
"",
|
|
105
|
+
"## Identity Intro",
|
|
106
|
+
"Soul fallback.",
|
|
107
|
+
].join("\n");
|
|
108
|
+
|
|
109
|
+
expect(readWorkspaceIdentityIntro()).toBe("Soul fallback.");
|
|
110
|
+
});
|
|
111
|
+
|
|
83
112
|
test("returns null when cache is empty", () => {
|
|
84
113
|
expect(getCachedIntro()).toBeNull();
|
|
85
114
|
});
|
|
@@ -22,8 +22,16 @@ mock.module("../util/logger.js", () => ({
|
|
|
22
22
|
}),
|
|
23
23
|
}));
|
|
24
24
|
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
handleDetailedHealth,
|
|
27
|
+
ROUTES,
|
|
28
|
+
} from "../runtime/routes/identity-routes.js";
|
|
26
29
|
import { getWorkspaceDir } from "../util/platform.js";
|
|
30
|
+
import {
|
|
31
|
+
getHatchedSidecarPath,
|
|
32
|
+
resolveHatchedAtReadOnly,
|
|
33
|
+
selectHatchedAtFromStats,
|
|
34
|
+
} from "../workspace/hatched-date.js";
|
|
27
35
|
|
|
28
36
|
// ── Env helpers ─────────────────────────────────────────────────────────
|
|
29
37
|
|
|
@@ -118,6 +126,9 @@ beforeEach(() => {
|
|
|
118
126
|
if (existsSync(profilerRunsDir)) {
|
|
119
127
|
rmSync(profilerRunsDir, { recursive: true, force: true });
|
|
120
128
|
}
|
|
129
|
+
|
|
130
|
+
rmSync(getHatchedSidecarPath(), { force: true });
|
|
131
|
+
rmSync(join(getWorkspaceDir(), "IDENTITY.md"), { force: true });
|
|
121
132
|
});
|
|
122
133
|
|
|
123
134
|
afterEach(() => {
|
|
@@ -326,3 +337,94 @@ describe("identity routes — health endpoint", () => {
|
|
|
326
337
|
});
|
|
327
338
|
});
|
|
328
339
|
});
|
|
340
|
+
|
|
341
|
+
describe("identity routes — createdAt selection", () => {
|
|
342
|
+
test("falls back to mtime when birthtime is the Unix epoch", () => {
|
|
343
|
+
const mtime = new Date("2026-05-01T14:49:47.519Z");
|
|
344
|
+
|
|
345
|
+
expect(
|
|
346
|
+
selectHatchedAtFromStats({
|
|
347
|
+
birthtime: new Date(0),
|
|
348
|
+
mtime,
|
|
349
|
+
})?.toISOString(),
|
|
350
|
+
).toBe(mtime.toISOString());
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
test("prefers birthtime when it is valid", () => {
|
|
354
|
+
const birthtime = new Date("2026-04-30T12:00:00.000Z");
|
|
355
|
+
const mtime = new Date("2026-05-01T14:49:47.519Z");
|
|
356
|
+
|
|
357
|
+
expect(
|
|
358
|
+
selectHatchedAtFromStats({
|
|
359
|
+
birthtime,
|
|
360
|
+
mtime,
|
|
361
|
+
})?.toISOString(),
|
|
362
|
+
).toBe(birthtime.toISOString());
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
test("returns undefined when both birthtime and mtime are the Unix epoch", () => {
|
|
366
|
+
expect(
|
|
367
|
+
selectHatchedAtFromStats({
|
|
368
|
+
birthtime: new Date(0),
|
|
369
|
+
mtime: new Date(0),
|
|
370
|
+
}),
|
|
371
|
+
).toBeUndefined();
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
test("read-only resolver falls back to current time without writing sidecar", () => {
|
|
375
|
+
const now = new Date("2026-05-04T12:00:00.000Z");
|
|
376
|
+
|
|
377
|
+
expect(
|
|
378
|
+
resolveHatchedAtReadOnly(
|
|
379
|
+
join(getWorkspaceDir(), "missing-identity.md"),
|
|
380
|
+
now,
|
|
381
|
+
),
|
|
382
|
+
).toBe(now.toISOString());
|
|
383
|
+
expect(existsSync(getHatchedSidecarPath())).toBe(false);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
test("/identity uses persisted hatched sidecar instead of live file metadata", () => {
|
|
387
|
+
const workspaceDir = getWorkspaceDir();
|
|
388
|
+
const dataDir = join(workspaceDir, "data");
|
|
389
|
+
const identityPath = join(workspaceDir, "IDENTITY.md");
|
|
390
|
+
const persistedHatchedAt = "2026-05-01T14:49:47.519Z";
|
|
391
|
+
|
|
392
|
+
mkdirSync(dataDir, { recursive: true });
|
|
393
|
+
writeFileSync(
|
|
394
|
+
identityPath,
|
|
395
|
+
"# Identity\n\n- **Name:** Example Assistant\n",
|
|
396
|
+
"utf-8",
|
|
397
|
+
);
|
|
398
|
+
writeFileSync(
|
|
399
|
+
getHatchedSidecarPath(),
|
|
400
|
+
JSON.stringify({ hatchedAt: persistedHatchedAt }),
|
|
401
|
+
"utf-8",
|
|
402
|
+
);
|
|
403
|
+
|
|
404
|
+
const route = ROUTES.find(
|
|
405
|
+
(candidate) => candidate.operationId === "identity",
|
|
406
|
+
);
|
|
407
|
+
expect(route).toBeDefined();
|
|
408
|
+
|
|
409
|
+
const body = route!.handler({}) as { createdAt?: string };
|
|
410
|
+
expect(body.createdAt).toBe(persistedHatchedAt);
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
test("/identity does not write hatched sidecar on read", () => {
|
|
414
|
+
const identityPath = join(getWorkspaceDir(), "IDENTITY.md");
|
|
415
|
+
writeFileSync(
|
|
416
|
+
identityPath,
|
|
417
|
+
"# Identity\n\n- **Name:** Example Assistant\n",
|
|
418
|
+
"utf-8",
|
|
419
|
+
);
|
|
420
|
+
|
|
421
|
+
const route = ROUTES.find(
|
|
422
|
+
(candidate) => candidate.operationId === "identity",
|
|
423
|
+
);
|
|
424
|
+
expect(route).toBeDefined();
|
|
425
|
+
|
|
426
|
+
const body = route!.handler({}) as { createdAt?: string };
|
|
427
|
+
expect(Date.parse(body.createdAt ?? "")).toBeGreaterThan(0);
|
|
428
|
+
expect(existsSync(getHatchedSidecarPath())).toBe(false);
|
|
429
|
+
});
|
|
430
|
+
});
|
|
@@ -41,8 +41,11 @@ describe("initFeatureFlagOverrides", () => {
|
|
|
41
41
|
it("falls back gracefully when gateway socket is unavailable", async () => {
|
|
42
42
|
mockGatewayIpc(null, { error: true });
|
|
43
43
|
|
|
44
|
-
//
|
|
45
|
-
|
|
44
|
+
// Disable retries — production retries the IPC fetch on failure to
|
|
45
|
+
// dodge the daemon-vs-gateway startup race, but here we're explicitly
|
|
46
|
+
// testing the no-gateway fallback and don't want the test to wait
|
|
47
|
+
// through the backoff schedule.
|
|
48
|
+
await initFeatureFlagOverrides({ retryBackoffsMs: [] });
|
|
46
49
|
|
|
47
50
|
// Without gateway data or file, undeclared flags default to true
|
|
48
51
|
const config = {} as any;
|
|
@@ -64,7 +67,10 @@ describe("initFeatureFlagOverrides", () => {
|
|
|
64
67
|
it("does not cache empty gateway response", async () => {
|
|
65
68
|
mockGatewayIpc({});
|
|
66
69
|
|
|
67
|
-
|
|
70
|
+
// Disable retries — this test explicitly simulates a sustained empty
|
|
71
|
+
// response (gateway up but reporting zero flags) and should not wait
|
|
72
|
+
// through the production backoff schedule.
|
|
73
|
+
await initFeatureFlagOverrides({ retryBackoffsMs: [] });
|
|
68
74
|
|
|
69
75
|
// Undeclared flags without overrides default to true (not false from
|
|
70
76
|
// a cached empty map)
|
|
@@ -72,6 +78,23 @@ describe("initFeatureFlagOverrides", () => {
|
|
|
72
78
|
expect(isAssistantFeatureFlagEnabled("foo-enabled", config)).toBe(true);
|
|
73
79
|
});
|
|
74
80
|
|
|
81
|
+
it("retries empty gateway responses and picks up flags once they become available", async () => {
|
|
82
|
+
// Simulate the daemon-vs-gateway startup race: the first IPC call
|
|
83
|
+
// returns empty (gateway not yet ready), but a later attempt sees
|
|
84
|
+
// the populated flag map. The retry loop in init should bridge this
|
|
85
|
+
// gap without losing the flag.
|
|
86
|
+
mockGatewayIpc({});
|
|
87
|
+
setTimeout(() => {
|
|
88
|
+
resetMockGatewayIpc();
|
|
89
|
+
mockGatewayIpc({ "retry-test-flag": true });
|
|
90
|
+
}, 50);
|
|
91
|
+
|
|
92
|
+
await initFeatureFlagOverrides({ retryBackoffsMs: [200] });
|
|
93
|
+
|
|
94
|
+
const config = {} as any;
|
|
95
|
+
expect(isAssistantFeatureFlagEnabled("retry-test-flag", config)).toBe(true);
|
|
96
|
+
});
|
|
97
|
+
|
|
75
98
|
it("does not re-fetch when cache is already populated", async () => {
|
|
76
99
|
mockGatewayIpc({ "first-call": true });
|
|
77
100
|
|
|
@@ -27,7 +27,6 @@ mock.module("../config/loader.js", () => ({
|
|
|
27
27
|
getConfig: () => mockConfig,
|
|
28
28
|
loadConfig: () => mockConfig,
|
|
29
29
|
invalidateConfigCache: () => {},
|
|
30
|
-
saveConfig: () => {},
|
|
31
30
|
loadRawConfig: () => ({}),
|
|
32
31
|
saveRawConfig: () => {},
|
|
33
32
|
getNestedValue: () => undefined,
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tests for inline-command skill load permission handling.
|
|
3
3
|
*
|
|
4
|
-
* When a skill contains inline command expansions (!\`...\`)
|
|
5
|
-
* inline-skill-commands flag is on, the permission
|
|
4
|
+
* When a skill contains inline command expansions (!\`...\`), the permission
|
|
6
5
|
* system must:
|
|
7
6
|
*
|
|
8
7
|
* 1. Emit skill_load_dynamic:<id>@<hash> / skill_load_dynamic:<id> candidates
|
|
@@ -17,8 +16,6 @@ import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
|
17
16
|
import { join } from "node:path";
|
|
18
17
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
19
18
|
|
|
20
|
-
import { _setOverridesForTesting } from "../config/assistant-feature-flags.js";
|
|
21
|
-
|
|
22
19
|
// ── Mock setup (must be before any imports from the project) ──────────────
|
|
23
20
|
|
|
24
21
|
const testDir = process.env.VELLUM_WORKSPACE_DIR!;
|
|
@@ -48,7 +45,6 @@ mock.module("../config/loader.js", () => ({
|
|
|
48
45
|
getConfig: () => testConfig,
|
|
49
46
|
loadConfig: () => testConfig,
|
|
50
47
|
invalidateConfigCache: () => {},
|
|
51
|
-
saveConfig: () => {},
|
|
52
48
|
loadRawConfig: () => ({}),
|
|
53
49
|
saveRawConfig: () => {},
|
|
54
50
|
getNestedValue: () => undefined,
|
|
@@ -67,12 +63,13 @@ mockIpcResponse("classify_risk", {
|
|
|
67
63
|
});
|
|
68
64
|
mockIpcResponse("get_global_thresholds", {
|
|
69
65
|
interactive: "low",
|
|
70
|
-
|
|
66
|
+
autonomous: "medium",
|
|
71
67
|
headless: "none",
|
|
72
68
|
});
|
|
73
69
|
|
|
74
70
|
// ── Imports (after mocks) ─────────────────────────────────────────────────
|
|
75
71
|
|
|
72
|
+
import { _setOverridesForTesting } from "../config/assistant-feature-flags.js";
|
|
76
73
|
import { check, generateAllowlistOptions } from "../permissions/checker.js";
|
|
77
74
|
import { clearRiskCache } from "../permissions/checker.js";
|
|
78
75
|
import { _clearGlobalCacheForTesting } from "../permissions/gateway-threshold-reader.js";
|
|
@@ -120,13 +117,10 @@ describe("inline-command skill_load permissions", () => {
|
|
|
120
117
|
_clearGlobalCacheForTesting();
|
|
121
118
|
mockIpcResponse("get_global_thresholds", {
|
|
122
119
|
interactive: "low",
|
|
123
|
-
|
|
120
|
+
autonomous: "medium",
|
|
124
121
|
headless: "none",
|
|
125
122
|
});
|
|
126
123
|
testConfig.skills = { load: { extraDirs: [] } };
|
|
127
|
-
_setOverridesForTesting({
|
|
128
|
-
"inline-skill-commands": true,
|
|
129
|
-
});
|
|
130
124
|
try {
|
|
131
125
|
rmSync(join(testDir, "protected", "trust.json"));
|
|
132
126
|
} catch {
|
|
@@ -159,7 +153,7 @@ describe("inline-command skill_load permissions", () => {
|
|
|
159
153
|
writeDynamicSkill("dynamic-strict", "Dynamic Strict Skill");
|
|
160
154
|
mockIpcResponse("get_global_thresholds", {
|
|
161
155
|
interactive: "none",
|
|
162
|
-
|
|
156
|
+
autonomous: "none",
|
|
163
157
|
headless: "none",
|
|
164
158
|
});
|
|
165
159
|
_clearGlobalCacheForTesting();
|
|
@@ -5,8 +5,9 @@ import { credentialKey } from "../security/credential-key.js";
|
|
|
5
5
|
const secureKeyValues = new Map<string, string>();
|
|
6
6
|
let mockTwilioAccountSid: string | undefined;
|
|
7
7
|
|
|
8
|
-
/** Set of providers that should report as connected via isProviderConnected(). */
|
|
9
8
|
const connectedProviders = new Set<string>();
|
|
9
|
+
const managedProviders = new Set<string>();
|
|
10
|
+
const platformConnectedProviders = new Set<string>();
|
|
10
11
|
|
|
11
12
|
mock.module("../security/secure-keys.js", () => ({
|
|
12
13
|
getSecureKeyAsync: async (account: string) => secureKeyValues.get(account),
|
|
@@ -18,21 +19,60 @@ mock.module("../config/loader.js", () => ({
|
|
|
18
19
|
? { accountSid: mockTwilioAccountSid }
|
|
19
20
|
: undefined,
|
|
20
21
|
}),
|
|
22
|
+
getConfig: () => ({ services: {} }),
|
|
23
|
+
}));
|
|
24
|
+
|
|
25
|
+
mock.module("../config/schemas/services.js", () => ({
|
|
26
|
+
getServiceMode: (_services: unknown, key: string) =>
|
|
27
|
+
managedProviders.has(key) ? "managed" : "your-own",
|
|
28
|
+
ServicesSchema: { shape: { google: true, slack: true } },
|
|
21
29
|
}));
|
|
22
30
|
|
|
23
31
|
mock.module("../oauth/oauth-store.js", () => ({
|
|
24
|
-
isProviderConnected: (provider: string) =>
|
|
32
|
+
isProviderConnected: (provider: string) =>
|
|
33
|
+
Promise.resolve(connectedProviders.has(provider)),
|
|
25
34
|
getConnectionByProvider: (provider: string) =>
|
|
26
35
|
connectedProviders.has(provider)
|
|
27
36
|
? { id: `conn-${provider}`, status: "active" }
|
|
28
37
|
: undefined,
|
|
38
|
+
getProvider: (provider: string) => {
|
|
39
|
+
const managedKeys: Record<string, string> = {
|
|
40
|
+
google: "google",
|
|
41
|
+
slack: "slack",
|
|
42
|
+
};
|
|
43
|
+
return managedKeys[provider]
|
|
44
|
+
? { managedServiceConfigKey: managedKeys[provider] }
|
|
45
|
+
: undefined;
|
|
46
|
+
},
|
|
47
|
+
}));
|
|
48
|
+
|
|
49
|
+
mock.module("../platform/client.js", () => ({
|
|
50
|
+
VellumPlatformClient: {
|
|
51
|
+
create: async () => ({
|
|
52
|
+
platformAssistantId: "test-assistant",
|
|
53
|
+
fetch: async (path: string) => {
|
|
54
|
+
const url = new URL(`http://localhost${path}`);
|
|
55
|
+
const provider = url.searchParams.get("provider");
|
|
56
|
+
const hasConnections =
|
|
57
|
+
provider && platformConnectedProviders.has(provider);
|
|
58
|
+
return {
|
|
59
|
+
ok: true,
|
|
60
|
+
json: async () => (hasConnections ? [{ id: "conn-1" }] : []),
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
}),
|
|
64
|
+
},
|
|
29
65
|
}));
|
|
30
66
|
|
|
31
|
-
/** Mark a provider as fully connected (active row + access token). */
|
|
32
67
|
function setOAuthConnected(provider: string): void {
|
|
33
68
|
connectedProviders.add(provider);
|
|
34
69
|
}
|
|
35
70
|
|
|
71
|
+
function setPlatformConnected(provider: string, configKey: string): void {
|
|
72
|
+
managedProviders.add(configKey);
|
|
73
|
+
platformConnectedProviders.add(provider);
|
|
74
|
+
}
|
|
75
|
+
|
|
36
76
|
const { getIntegrationSummary, formatIntegrationSummary, hasCapability } =
|
|
37
77
|
await import("../schedule/integration-status.js");
|
|
38
78
|
|
|
@@ -40,6 +80,8 @@ describe("integration-status", () => {
|
|
|
40
80
|
beforeEach(() => {
|
|
41
81
|
secureKeyValues.clear();
|
|
42
82
|
connectedProviders.clear();
|
|
83
|
+
managedProviders.clear();
|
|
84
|
+
platformConnectedProviders.clear();
|
|
43
85
|
mockTwilioAccountSid = undefined;
|
|
44
86
|
});
|
|
45
87
|
|
|
@@ -99,7 +141,6 @@ describe("integration-status", () => {
|
|
|
99
141
|
});
|
|
100
142
|
|
|
101
143
|
test("Telegram disconnected when no connection record exists", async () => {
|
|
102
|
-
// No oauth_connection record for telegram — should be disconnected
|
|
103
144
|
const summary = await getIntegrationSummary();
|
|
104
145
|
const telegram = summary.find(
|
|
105
146
|
(s: { name: string }) => s.name === "Telegram",
|
|
@@ -153,7 +194,6 @@ describe("integration-status", () => {
|
|
|
153
194
|
});
|
|
154
195
|
|
|
155
196
|
test("returns false when no connection record exists for category integrations", async () => {
|
|
156
|
-
// No oauth_connection record for telegram — should not count as connected
|
|
157
197
|
expect(await hasCapability("messaging")).toBe(false);
|
|
158
198
|
});
|
|
159
199
|
|
|
@@ -166,4 +206,44 @@ describe("integration-status", () => {
|
|
|
166
206
|
expect(await hasCapability("email")).toBe(true);
|
|
167
207
|
});
|
|
168
208
|
});
|
|
209
|
+
|
|
210
|
+
describe("managed mode", () => {
|
|
211
|
+
test("Gmail shows connected when platform has active connection", async () => {
|
|
212
|
+
setPlatformConnected("google", "google");
|
|
213
|
+
|
|
214
|
+
const summary = await getIntegrationSummary();
|
|
215
|
+
const gmail = summary.find((s: { name: string }) => s.name === "Gmail");
|
|
216
|
+
expect(gmail?.connected).toBe(true);
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
test("Gmail shows disconnected when managed but no platform connection", async () => {
|
|
220
|
+
managedProviders.add("google");
|
|
221
|
+
|
|
222
|
+
const summary = await getIntegrationSummary();
|
|
223
|
+
const gmail = summary.find((s: { name: string }) => s.name === "Gmail");
|
|
224
|
+
expect(gmail?.connected).toBe(false);
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
test("Slack shows connected when platform has active connection", async () => {
|
|
228
|
+
setPlatformConnected("slack", "slack");
|
|
229
|
+
|
|
230
|
+
const summary = await getIntegrationSummary();
|
|
231
|
+
const slack = summary.find((s: { name: string }) => s.name === "Slack");
|
|
232
|
+
expect(slack?.connected).toBe(true);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
test("formatIntegrationSummary reflects managed connections", async () => {
|
|
236
|
+
setPlatformConnected("google", "google");
|
|
237
|
+
setPlatformConnected("slack", "slack");
|
|
238
|
+
|
|
239
|
+
const result = await formatIntegrationSummary();
|
|
240
|
+
expect(result).toContain("Gmail \u2713");
|
|
241
|
+
expect(result).toContain("Slack \u2713");
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test("hasCapability returns true for managed email connection", async () => {
|
|
245
|
+
setPlatformConnected("google", "google");
|
|
246
|
+
expect(await hasCapability("email")).toBe(true);
|
|
247
|
+
});
|
|
248
|
+
});
|
|
169
249
|
});
|
|
@@ -13,6 +13,8 @@ mock.module("../config/loader.js", () => ({
|
|
|
13
13
|
invalidateConfigCache: () => {},
|
|
14
14
|
}));
|
|
15
15
|
|
|
16
|
+
import { eq } from "drizzle-orm";
|
|
17
|
+
|
|
16
18
|
import { getDb } from "../memory/db-connection.js";
|
|
17
19
|
import { initializeDb } from "../memory/db-init.js";
|
|
18
20
|
import {
|
|
@@ -24,6 +26,7 @@ import {
|
|
|
24
26
|
_resetQdrantBreaker,
|
|
25
27
|
withQdrantBreaker,
|
|
26
28
|
} from "../memory/qdrant-circuit-breaker.js";
|
|
29
|
+
import { memoryJobs } from "../memory/schema.js";
|
|
27
30
|
|
|
28
31
|
describe("claimMemoryJobs with Qdrant circuit breaker", () => {
|
|
29
32
|
beforeAll(() => {
|
|
@@ -41,7 +44,7 @@ describe("claimMemoryJobs with Qdrant circuit breaker", () => {
|
|
|
41
44
|
enqueueMemoryJob("embed_graph_node", { nodeId: "node-1" });
|
|
42
45
|
enqueueMemoryJob("graph_extract", { conversationId: "conv-1" });
|
|
43
46
|
|
|
44
|
-
const claimed = claimMemoryJobs(10);
|
|
47
|
+
const claimed = claimMemoryJobs({ slowLlm: 10, fast: 10, embed: 10 });
|
|
45
48
|
const types = claimed.map((j) => j.type);
|
|
46
49
|
|
|
47
50
|
expect(types).toContain("embed_segment");
|
|
@@ -70,7 +73,7 @@ describe("claimMemoryJobs with Qdrant circuit breaker", () => {
|
|
|
70
73
|
conversationId: "conv-1",
|
|
71
74
|
});
|
|
72
75
|
|
|
73
|
-
const claimed = claimMemoryJobs(10);
|
|
76
|
+
const claimed = claimMemoryJobs({ slowLlm: 10, fast: 10, embed: 10 });
|
|
74
77
|
const types = claimed.map((j) => j.type);
|
|
75
78
|
|
|
76
79
|
// Only non-embed jobs should be claimed
|
|
@@ -98,7 +101,11 @@ describe("claimMemoryJobs with Qdrant circuit breaker", () => {
|
|
|
98
101
|
enqueueMemoryJob("embed_segment", { segmentId: "seg-1" });
|
|
99
102
|
enqueueMemoryJob("graph_extract", { conversationId: "conv-1" });
|
|
100
103
|
|
|
101
|
-
const claimedWhileOpen = claimMemoryJobs(
|
|
104
|
+
const claimedWhileOpen = claimMemoryJobs({
|
|
105
|
+
slowLlm: 10,
|
|
106
|
+
fast: 10,
|
|
107
|
+
embed: 10,
|
|
108
|
+
});
|
|
102
109
|
expect(claimedWhileOpen.map((j) => j.type)).not.toContain("embed_segment");
|
|
103
110
|
|
|
104
111
|
// Reset breaker (simulates successful probe closing the circuit)
|
|
@@ -107,12 +114,95 @@ describe("claimMemoryJobs with Qdrant circuit breaker", () => {
|
|
|
107
114
|
// Re-enqueue an embed job (the previous one is now "running")
|
|
108
115
|
enqueueMemoryJob("embed_graph_node", { nodeId: "node-2" });
|
|
109
116
|
|
|
110
|
-
const claimedAfterClose = claimMemoryJobs(
|
|
117
|
+
const claimedAfterClose = claimMemoryJobs({
|
|
118
|
+
slowLlm: 10,
|
|
119
|
+
fast: 10,
|
|
120
|
+
embed: 10,
|
|
121
|
+
});
|
|
111
122
|
const types = claimedAfterClose.map((j) => j.type);
|
|
112
123
|
|
|
113
124
|
expect(types).toContain("embed_graph_node");
|
|
114
125
|
});
|
|
115
126
|
|
|
127
|
+
test("lane budgets are honored when called with explicit budgets", () => {
|
|
128
|
+
// 5 slow-lane jobs
|
|
129
|
+
for (let i = 0; i < 5; i++) {
|
|
130
|
+
enqueueMemoryJob("graph_extract", { conversationId: `slow-${i}` });
|
|
131
|
+
}
|
|
132
|
+
// 5 fast-lane jobs (rebuild_index is neither slow-LLM nor embed)
|
|
133
|
+
for (let i = 0; i < 5; i++) {
|
|
134
|
+
enqueueMemoryJob("rebuild_index", { id: `fast-${i}` });
|
|
135
|
+
}
|
|
136
|
+
// 5 embed-lane jobs
|
|
137
|
+
for (let i = 0; i < 5; i++) {
|
|
138
|
+
enqueueMemoryJob("embed_segment", { segmentId: `embed-${i}` });
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const claimed = claimMemoryJobs({ slowLlm: 1, fast: 2, embed: 2 });
|
|
142
|
+
const slowClaimed = claimed.filter((j) => j.type === "graph_extract");
|
|
143
|
+
const fastClaimed = claimed.filter((j) => j.type === "rebuild_index");
|
|
144
|
+
const embedClaimed = claimed.filter((j) => j.type === "embed_segment");
|
|
145
|
+
|
|
146
|
+
expect(claimed).toHaveLength(5);
|
|
147
|
+
expect(slowClaimed).toHaveLength(1);
|
|
148
|
+
expect(fastClaimed).toHaveLength(2);
|
|
149
|
+
expect(embedClaimed).toHaveLength(2);
|
|
150
|
+
|
|
151
|
+
// Remaining 10 jobs should still be pending.
|
|
152
|
+
const db = getDb();
|
|
153
|
+
const pendingRows = db
|
|
154
|
+
.select()
|
|
155
|
+
.from(memoryJobs)
|
|
156
|
+
.where(eq(memoryJobs.status, "pending"))
|
|
157
|
+
.all();
|
|
158
|
+
expect(pendingRows).toHaveLength(10);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("Qdrant breaker gates only the embed lane in lane-aware mode", async () => {
|
|
162
|
+
// 5 slow + 5 fast + 5 embed pending jobs
|
|
163
|
+
for (let i = 0; i < 5; i++) {
|
|
164
|
+
enqueueMemoryJob("graph_extract", { conversationId: `slow-${i}` });
|
|
165
|
+
}
|
|
166
|
+
for (let i = 0; i < 5; i++) {
|
|
167
|
+
enqueueMemoryJob("rebuild_index", { id: `fast-${i}` });
|
|
168
|
+
}
|
|
169
|
+
for (let i = 0; i < 5; i++) {
|
|
170
|
+
enqueueMemoryJob("embed_segment", { segmentId: `embed-${i}` });
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Trip the breaker
|
|
174
|
+
for (let i = 0; i < 5; i++) {
|
|
175
|
+
try {
|
|
176
|
+
await withQdrantBreaker(async () => {
|
|
177
|
+
throw new Error("simulated qdrant failure");
|
|
178
|
+
});
|
|
179
|
+
} catch {
|
|
180
|
+
// expected
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const claimed = claimMemoryJobs({ slowLlm: 1, fast: 2, embed: 2 });
|
|
185
|
+
const slowClaimed = claimed.filter((j) => j.type === "graph_extract");
|
|
186
|
+
const fastClaimed = claimed.filter((j) => j.type === "rebuild_index");
|
|
187
|
+
const embedClaimed = claimed.filter((j) => j.type === "embed_segment");
|
|
188
|
+
|
|
189
|
+
expect(slowClaimed).toHaveLength(1);
|
|
190
|
+
expect(fastClaimed).toHaveLength(2);
|
|
191
|
+
// Breaker is open and probe window has not elapsed → no embed jobs claimed.
|
|
192
|
+
expect(embedClaimed).toHaveLength(0);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
test("FIFO order within a lane is preserved by runAfter ascending", () => {
|
|
196
|
+
const t0 = Date.now() - 30_000;
|
|
197
|
+
enqueueMemoryJob("graph_extract", { conversationId: "third" }, t0 + 200);
|
|
198
|
+
enqueueMemoryJob("graph_extract", { conversationId: "first" }, t0);
|
|
199
|
+
enqueueMemoryJob("graph_extract", { conversationId: "second" }, t0 + 100);
|
|
200
|
+
|
|
201
|
+
const claimed = claimMemoryJobs({ slowLlm: 3, fast: 0, embed: 0 });
|
|
202
|
+
const order = claimed.map((j) => j.payload.conversationId);
|
|
203
|
+
expect(order).toEqual(["first", "second", "third"]);
|
|
204
|
+
});
|
|
205
|
+
|
|
116
206
|
test("all embed job types are skipped when breaker is open", async () => {
|
|
117
207
|
const embedTypes: MemoryJobType[] = [
|
|
118
208
|
"embed_segment",
|
|
@@ -141,7 +231,7 @@ describe("claimMemoryJobs with Qdrant circuit breaker", () => {
|
|
|
141
231
|
// Also enqueue a non-embed job
|
|
142
232
|
enqueueMemoryJob("graph_consolidate", { conversationId: "conv-1" });
|
|
143
233
|
|
|
144
|
-
const claimed = claimMemoryJobs(20);
|
|
234
|
+
const claimed = claimMemoryJobs({ slowLlm: 20, fast: 20, embed: 20 });
|
|
145
235
|
const types = claimed.map((j) => j.type);
|
|
146
236
|
|
|
147
237
|
// Only the non-embed job should be claimed
|
|
@@ -139,6 +139,23 @@ describe("maybeSeedMemoryV2Skills (daemon startup gate)", () => {
|
|
|
139
139
|
expect(state.warnCalls).toHaveLength(0);
|
|
140
140
|
});
|
|
141
141
|
|
|
142
|
+
test("re-invocation seeds after flag flips on (deferred-init race recovery)", async () => {
|
|
143
|
+
// Models the lifecycle-startup race: the synchronous seed call evaluates
|
|
144
|
+
// the flag while the gateway IPC override fetch is still in flight, falls
|
|
145
|
+
// through to the registry default (`false`), and skips. Once
|
|
146
|
+
// `initFeatureFlagOverrides()` resolves, the chained `.then` re-invokes
|
|
147
|
+
// the seed with the now-populated cache and the flag flips to `true`.
|
|
148
|
+
state.flagOverrides = { "memory-v2-enabled": false };
|
|
149
|
+
maybeSeedMemoryV2Skills(makeConfig(true));
|
|
150
|
+
await flushMicrotasks();
|
|
151
|
+
expect(state.seedCallCount).toBe(0);
|
|
152
|
+
|
|
153
|
+
state.flagOverrides = { "memory-v2-enabled": true };
|
|
154
|
+
maybeSeedMemoryV2Skills(makeConfig(true));
|
|
155
|
+
await flushMicrotasks();
|
|
156
|
+
expect(state.seedCallCount).toBe(1);
|
|
157
|
+
});
|
|
158
|
+
|
|
142
159
|
test("swallows seedV2SkillEntries rejections and logs a warning", async () => {
|
|
143
160
|
state.flagOverrides = { "memory-v2-enabled": true };
|
|
144
161
|
state.seedShouldReject = new Error("seed failed");
|
|
@@ -45,7 +45,6 @@ mock.module("../config/loader.js", () => ({
|
|
|
45
45
|
getConfig: () => mockConfig,
|
|
46
46
|
loadConfig: () => mockConfig,
|
|
47
47
|
invalidateConfigCache: () => {},
|
|
48
|
-
saveConfig: () => {},
|
|
49
48
|
loadRawConfig: () => ({}),
|
|
50
49
|
saveRawConfig: () => {},
|
|
51
50
|
getNestedValue: () => undefined,
|