@vellumai/assistant 0.8.4 → 0.8.5
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 +2 -2
- package/docs/browser-use-architecture-phase2.md +1 -1
- package/knip.json +2 -1
- package/openapi.yaml +809 -11
- package/package.json +1 -1
- package/src/__tests__/anthropic-provider.test.ts +34 -37
- package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +293 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +3 -3
- package/src/__tests__/audit-log-rotation.test.ts +70 -16
- package/src/__tests__/background-workers-disk-pressure.test.ts +3 -3
- package/src/__tests__/btw-routes.test.ts +2 -3
- package/src/__tests__/call-controller.test.ts +0 -1
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
- package/src/__tests__/channel-guardian.test.ts +3 -3
- package/src/__tests__/checker.test.ts +6 -15
- package/src/__tests__/compaction-events.test.ts +1 -0
- package/src/__tests__/compactor-call-site-logging.test.ts +214 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +5 -11
- package/src/__tests__/computer-use-tools.test.ts +2 -4
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +197 -2
- package/src/__tests__/conversation-agent-loop.test.ts +163 -122
- package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -5
- package/src/__tests__/conversation-clear-safety.test.ts +25 -25
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +1 -1
- package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
- package/src/__tests__/conversation-error.test.ts +31 -0
- package/src/__tests__/conversation-fork-crud.test.ts +178 -15
- package/src/__tests__/conversation-lifecycle.test.ts +52 -11
- package/src/__tests__/{conversation-load-cleaned-at.test.ts → conversation-load-history-stripped.test.ts} +13 -13
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +109 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +35 -0
- package/src/__tests__/conversation-skill-tools.test.ts +2 -5
- package/src/__tests__/conversation-store.test.ts +1 -1
- package/src/__tests__/conversation-sync-tags.test.ts +99 -32
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
- package/src/__tests__/credential-execution-feature-gates.test.ts +9 -7
- package/src/__tests__/credential-execution-tools.test.ts +6 -6
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault-unit.test.ts +2 -2
- package/src/__tests__/dynamic-page-surface.test.ts +2 -2
- package/src/__tests__/email-html-renderer.test.ts +12 -0
- package/src/__tests__/gateway-flag-listener.test.ts +237 -0
- package/src/__tests__/gemini-provider.test.ts +78 -0
- package/src/__tests__/guardian-dispatch.test.ts +0 -1
- package/src/__tests__/guardian-outbound-http.test.ts +7 -5
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
- package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
- package/src/__tests__/heartbeat-service.test.ts +4 -0
- package/src/__tests__/host-shell-tool.test.ts +1 -1
- package/src/__tests__/init-feature-flag-overrides.test.ts +5 -6
- package/src/__tests__/list-messages-tool-merge.test.ts +70 -11
- package/src/__tests__/llm-request-log-call-site.test.ts +136 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +26 -0
- package/src/__tests__/llm-resolver.test.ts +77 -9
- package/src/__tests__/llm-usage-store.test.ts +66 -0
- package/src/__tests__/logger.test.ts +89 -0
- package/src/__tests__/mcp-abort-signal.test.ts +2 -2
- package/src/__tests__/media-generate-image.test.ts +31 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +7 -7
- package/src/__tests__/model-intents.test.ts +2 -4
- package/src/__tests__/notification-guardian-path.test.ts +0 -1
- package/src/__tests__/onboarding-template-contract.test.ts +1 -1
- package/src/__tests__/openai-provider.test.ts +46 -0
- package/src/__tests__/openai-responses-provider.test.ts +114 -12
- package/src/__tests__/pending-interactions-resolved-event.test.ts +0 -1
- package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
- package/src/__tests__/platform.test.ts +2 -2
- package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
- package/src/__tests__/plugin-bootstrap.test.ts +2 -2
- package/src/__tests__/plugin-tool-contribution.test.ts +13 -6
- package/src/__tests__/plugin-types.test.ts +3 -2
- package/src/__tests__/prechat-onboarding-contract.test.ts +131 -98
- package/src/__tests__/pricing.test.ts +12 -0
- package/src/__tests__/prune-jobs-changes-parser.test.ts +61 -0
- package/src/__tests__/registry.test.ts +2 -8
- package/src/__tests__/require-fresh-approval.test.ts +2 -2
- package/src/__tests__/runtime-events-sse-bilingual.test.ts +154 -0
- package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/skill-projection-feature-flag.test.ts +4 -7
- package/src/__tests__/skill-projection.benchmark.test.ts +2 -6
- package/src/__tests__/skill-tool-factory.test.ts +1 -1
- package/src/__tests__/subagent-notify-parent.test.ts +1 -1
- package/src/__tests__/suggestion-routes.test.ts +1 -0
- package/src/__tests__/sync-message-contract.test.ts +59 -0
- package/src/__tests__/system-prompt.test.ts +145 -131
- package/src/__tests__/terminal-tools.test.ts +1 -1
- package/src/__tests__/tool-approval-handler.test.ts +1 -5
- package/src/__tests__/tool-execute-pipeline.test.ts +2 -2
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -5
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +15 -5
- package/src/__tests__/tool-executor.test.ts +9 -62
- package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -6
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
- package/src/__tests__/ui-file-upload-surface.test.ts +2 -2
- package/src/__tests__/usage-routes.test.ts +3 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +6 -5
- package/src/__tests__/workspace-migration-089-move-memory-tree-out-of-v3.test.ts +86 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +146 -0
- package/src/acp/prepare-agent-env.ts +78 -0
- package/src/acp/session-manager.ts +1 -1
- package/src/agent/loop.ts +8 -0
- package/src/api/README.md +5 -0
- package/src/api/index.ts +4 -0
- package/src/api/package.json +10 -0
- package/src/background-wake/background-wake-routes.test.ts +233 -0
- package/src/background-wake/runtime-registry.ts +24 -0
- package/src/cli/commands/__tests__/browser.test.ts +23 -5
- package/src/cli/commands/__tests__/domain-register.test.ts +110 -0
- package/src/cli/commands/__tests__/domain-status.test.ts +33 -33
- package/src/cli/commands/__tests__/inference-send.test.ts +108 -5
- package/src/cli/commands/__tests__/memory-v2-compare-render.test.ts +98 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +1 -0
- package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
- package/src/cli/commands/browser.ts +247 -0
- package/src/cli/commands/domain.ts +91 -41
- package/src/cli/commands/inference.ts +93 -40
- package/src/cli/commands/memory-v2-compare-render.ts +115 -0
- package/src/cli/commands/memory-v2.ts +176 -1
- package/src/cli/commands/memory-v3-render.ts +344 -0
- package/src/cli/commands/memory-v3.ts +316 -0
- package/src/cli/program.ts +2 -0
- package/src/config/assistant-feature-flags.ts +21 -9
- package/src/config/bundled-skills/document-editor/SKILL.md +11 -2
- package/src/config/bundled-skills/document-editor/TOOLS.json +18 -0
- package/src/config/bundled-skills/document-editor/tools/document-open.ts +12 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +13 -8
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +10 -3
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +16 -14
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +7 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +7 -2
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/call-site-defaults.ts +7 -6
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +213 -1
- package/src/config/schemas/call-site-catalog.ts +21 -7
- package/src/config/schemas/llm.ts +12 -1
- package/src/config/schemas/memory-v2.ts +246 -0
- package/src/config/schemas/memory.ts +2 -1
- package/src/context/compactor.ts +52 -0
- package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
- package/src/conversations/message-consolidation.ts +404 -0
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -1
- package/src/daemon/__tests__/meet-manifest-loader.test.ts +1 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -13
- package/src/daemon/conversation-agent-loop.ts +126 -76
- package/src/daemon/conversation-error.ts +31 -1
- package/src/daemon/conversation-lifecycle.ts +27 -22
- package/src/daemon/conversation-runtime-assembly.ts +10 -9
- package/src/daemon/conversation-tool-setup.ts +63 -3
- package/src/daemon/conversation-usage.ts +2 -0
- package/src/daemon/conversation.ts +14 -29
- package/src/daemon/disk-pressure-guard.ts +14 -2
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +11 -3
- package/src/daemon/host-browser-proxy.ts +5 -5
- package/src/daemon/host-cu-proxy.ts +4 -4
- package/src/daemon/host-file-proxy.ts +4 -4
- package/src/daemon/host-proxy-base.ts +4 -4
- package/src/daemon/host-transfer-proxy.ts +10 -10
- package/src/daemon/lifecycle.ts +23 -20
- package/src/daemon/meet-manifest-loader.ts +1 -7
- package/src/daemon/message-types/conversations.ts +6 -9
- package/src/daemon/message-types/home.ts +1 -13
- package/src/daemon/message-types/messages.ts +6 -14
- package/src/daemon/message-types/sync.ts +14 -0
- package/src/daemon/shutdown-handlers.ts +24 -5
- package/src/daemon/switch-inference-profile-tool.ts +52 -0
- package/src/daemon/tool-setup-types.ts +13 -0
- package/src/events/relationship-state-updated.ts +25 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -1
- package/src/home/home-greeting.ts +0 -9
- package/src/home/suggested-prompts.ts +0 -9
- package/src/ipc/gateway-flag-listener.ts +123 -0
- package/src/ipc/skill-routes/registries.ts +8 -12
- package/src/memory/__tests__/db-async-query.test.ts +165 -0
- package/src/memory/__tests__/db-maintenance.test.ts +115 -0
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +241 -0
- package/src/memory/__tests__/jobs-store-job-classes.test.ts +28 -1
- package/src/memory/__tests__/memory-retrospective-job.test.ts +7 -0
- package/src/memory/auto-analysis-enqueue.ts +5 -1
- package/src/memory/conversation-crud.ts +71 -70
- package/src/memory/conversation-starters-cadence.ts +3 -1
- package/src/memory/conversation-title-service.ts +19 -3
- package/src/memory/db-async-query.ts +214 -0
- package/src/memory/db-init.ts +10 -0
- package/src/memory/db-maintenance.ts +30 -21
- package/src/memory/graph/bootstrap.ts +8 -1
- package/src/memory/graph/capability-seed.ts +7 -3
- package/src/memory/graph/conversation-graph-memory.ts +100 -17
- package/src/memory/graph/extraction.ts +1 -5
- package/src/memory/graph/graph-search.ts +7 -1
- package/src/memory/indexer.ts +28 -18
- package/src/memory/job-handlers/cleanup.ts +76 -18
- package/src/memory/job-handlers/conversation-starters.ts +1 -4
- package/src/memory/jobs/embed-pkb-file.ts +6 -1
- package/src/memory/jobs-store.ts +14 -0
- package/src/memory/jobs-worker.ts +55 -22
- package/src/memory/llm-request-log-source-clickhouse.ts +42 -2
- package/src/memory/llm-request-log-source-local.ts +7 -0
- package/src/memory/llm-request-log-source.ts +9 -2
- package/src/memory/llm-request-log-store.ts +43 -1
- package/src/memory/llm-usage-store.ts +24 -0
- package/src/memory/memory-retrospective-enqueue.ts +8 -1
- package/src/memory/memory-retrospective-job.ts +5 -0
- package/src/memory/memory-v2-activation-log-store.ts +15 -6
- package/src/memory/migrations/260-rename-cleaned-at.ts +44 -0
- package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +36 -0
- package/src/memory/migrations/262-memory-v3-coactivation.ts +57 -0
- package/src/memory/migrations/263-memory-v3-auto-edges.ts +50 -0
- package/src/memory/migrations/264-llm-request-log-call-site.ts +29 -0
- package/src/memory/migrations/index.ts +17 -0
- package/src/memory/migrations/registry.ts +33 -0
- package/src/memory/schema/conversations.ts +1 -1
- package/src/memory/schema/infrastructure.ts +21 -0
- package/src/memory/tool-usage-store.ts +36 -8
- package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -0
- package/src/memory/v2/__tests__/harness-compare.test.ts +186 -0
- package/src/memory/v2/__tests__/harness-metrics.test.ts +74 -0
- package/src/memory/v2/__tests__/harness-oracle.test.ts +257 -0
- package/src/memory/v2/__tests__/harness-replay-input.test.ts +225 -0
- package/src/memory/v2/__tests__/harness-runner.test.ts +109 -0
- package/src/memory/v2/__tests__/injection.test.ts +127 -98
- package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
- package/src/memory/v2/__tests__/router.test.ts +171 -3
- package/src/memory/v2/harness/compare.ts +57 -0
- package/src/memory/v2/harness/metrics.ts +124 -0
- package/src/memory/v2/harness/oracle.ts +145 -0
- package/src/memory/v2/harness/replay-input.ts +224 -0
- package/src/memory/v2/harness/retriever.ts +74 -0
- package/src/memory/v2/harness/router-retriever.ts +43 -0
- package/src/memory/v2/harness/runner.ts +106 -0
- package/src/memory/v2/harness/trace.ts +58 -0
- package/src/memory/v2/injection.ts +21 -15
- package/src/memory/v2/prompts/router.ts +26 -1
- package/src/memory/v2/qdrant.ts +14 -2
- package/src/memory/v2/router.ts +171 -18
- package/src/memory/v3/__tests__/coactivation-store.test.ts +422 -0
- package/src/memory/v3/__tests__/consolidation-job.test.ts +468 -0
- package/src/memory/v3/__tests__/edge-learning-job.test.ts +324 -0
- package/src/memory/v3/__tests__/edges.test.ts +563 -0
- package/src/memory/v3/__tests__/filter.test.ts +512 -0
- package/src/memory/v3/__tests__/gate.test.ts +574 -0
- package/src/memory/v3/__tests__/index-composition.test.ts +233 -0
- package/src/memory/v3/__tests__/loop.test.ts +530 -0
- package/src/memory/v3/__tests__/retriever.test.ts +226 -0
- package/src/memory/v3/__tests__/scouts.test.ts +440 -0
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +312 -0
- package/src/memory/v3/__tests__/system-prompts.test.ts +154 -0
- package/src/memory/v3/__tests__/traversal.test.ts +469 -0
- package/src/memory/v3/__tests__/tree-index.test.ts +280 -0
- package/src/memory/v3/__tests__/tree-store.test.ts +529 -0
- package/src/memory/v3/__tests__/tree-walk.test.ts +707 -0
- package/src/memory/v3/__tests__/validate.test.ts +245 -0
- package/src/memory/v3/auto-edges.ts +223 -0
- package/src/memory/v3/coactivation-store.ts +124 -0
- package/src/memory/v3/consolidation-job.ts +323 -0
- package/src/memory/v3/edge-learning-job.ts +160 -0
- package/src/memory/v3/edges.ts +249 -0
- package/src/memory/v3/filter.ts +281 -0
- package/src/memory/v3/gate.ts +334 -0
- package/src/memory/v3/index-composition.ts +113 -0
- package/src/memory/v3/llm-capture.ts +46 -0
- package/src/memory/v3/loop.ts +382 -0
- package/src/memory/v3/maintenance.ts +144 -0
- package/src/memory/v3/prompt-context.ts +33 -0
- package/src/memory/v3/prompts/consolidation.ts +458 -0
- package/src/memory/v3/prompts/system-prompts.ts +196 -0
- package/src/memory/v3/retriever.ts +33 -0
- package/src/memory/v3/scouts.ts +420 -0
- package/src/memory/v3/shadow-middleware.ts +305 -0
- package/src/memory/v3/traversal.ts +206 -0
- package/src/memory/v3/tree-index.ts +237 -0
- package/src/memory/v3/tree-store.ts +394 -0
- package/src/memory/v3/tree-walk.ts +351 -0
- package/src/memory/v3/types.ts +65 -0
- package/src/memory/v3/validate.ts +300 -0
- package/src/notifications/adapters/macos.ts +18 -1
- package/src/notifications/adapters/platform.ts +1 -1
- package/src/notifications/decision-engine.ts +1 -4
- package/src/notifications/emit-signal.ts +29 -49
- package/src/permissions/prompter.ts +3 -3
- package/src/permissions/question-prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +2 -2
- package/src/plugin-api/index.ts +4 -0
- package/src/plugin-api/types.ts +7 -33
- package/src/plugins/defaults/index.ts +6 -0
- package/src/plugins/defaults/injectors.ts +18 -11
- package/src/plugins/external-plugin-loader.ts +5 -68
- package/src/plugins/types.ts +11 -16
- package/src/proactive-artifact/aux-message-injector.ts +17 -4
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
- package/src/prompts/persona-resolver.ts +36 -21
- package/src/prompts/sections.ts +39 -7
- package/src/prompts/system-prompt.ts +50 -185
- package/src/prompts/templates/BOOTSTRAP.md +2 -2
- package/src/prompts/templates/system-sections.ts +230 -8
- package/src/providers/__tests__/connection-model-compat.test.ts +234 -0
- package/src/providers/__tests__/retry-callsite.test.ts +85 -5
- package/src/providers/anthropic/client.ts +32 -66
- package/src/providers/call-site-routing.ts +14 -2
- package/src/providers/connection-model-compat.ts +38 -0
- package/src/providers/connection-resolution.ts +16 -2
- package/src/providers/gemini/client.ts +49 -6
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/minimax/client.ts +106 -0
- package/src/providers/model-catalog.ts +43 -0
- package/src/providers/model-intents.ts +1 -1
- package/src/providers/openai/chat-completions-provider.ts +6 -3
- package/src/providers/openai/codex-models.ts +18 -0
- package/src/providers/openai/responses-provider.ts +78 -21
- package/src/providers/provider-send-message.ts +7 -1
- package/src/providers/retry.ts +34 -3
- package/src/providers/thinking-config.ts +26 -1
- package/src/providers/usage-tracking.ts +2 -0
- package/src/runtime/AGENTS.md +2 -2
- package/src/runtime/agent-wake.ts +1 -0
- package/src/runtime/assistant-event-hub.ts +76 -6
- package/src/runtime/auth/route-policy.ts +36 -0
- package/src/runtime/btw-sidechain.ts +0 -6
- package/src/runtime/http-types.ts +0 -2
- package/src/runtime/migrations/vbundle-builder.ts +10 -3
- package/src/runtime/pending-interactions.ts +0 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +106 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +25 -6
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -0
- package/src/runtime/routes/acp-routes.test.ts +255 -6
- package/src/runtime/routes/acp-routes.ts +8 -1
- package/src/runtime/routes/avatar-routes.ts +10 -10
- package/src/runtime/routes/background-wake-routes.ts +188 -0
- package/src/runtime/routes/browser-tabs-routes.ts +200 -0
- package/src/runtime/routes/btw-routes.ts +0 -6
- package/src/runtime/routes/conversation-cli-routes.ts +1 -1
- package/src/runtime/routes/conversation-list-routes.ts +12 -4
- package/src/runtime/routes/conversation-management-routes.ts +77 -20
- package/src/runtime/routes/conversation-query-routes.ts +142 -36
- package/src/runtime/routes/conversation-routes.ts +252 -410
- package/src/runtime/routes/conversation-starter-routes.ts +6 -3
- package/src/runtime/routes/disk-pressure-routes.ts +1 -1
- package/src/runtime/routes/domain-routes.ts +60 -10
- package/src/runtime/routes/email-routes.ts +5 -2
- package/src/runtime/routes/events-routes.ts +54 -10
- package/src/runtime/routes/group-routes.ts +24 -8
- package/src/runtime/routes/host-browser-routes.ts +10 -2
- package/src/runtime/routes/host-cu-routes.ts +2 -2
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +96 -3
- package/src/runtime/routes/index.ts +8 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +22 -12
- package/src/runtime/routes/inference-profile-session-routes.ts +7 -1
- package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
- package/src/runtime/routes/memory-item-routes.ts +8 -3
- package/src/runtime/routes/memory-v2-routes.ts +215 -5
- package/src/runtime/routes/memory-v3-routes.ts +316 -0
- package/src/runtime/routes/migration-routes.ts +21 -24
- package/src/runtime/routes/plugins-routes.ts +337 -0
- package/src/runtime/routes/rename-conversation-routes.ts +6 -2
- package/src/runtime/routes/secret-routes.ts +25 -5
- package/src/runtime/routes/settings-routes.ts +12 -11
- package/src/runtime/routes/slack-channel-routes.ts +5 -4
- package/src/runtime/routes/workspace-routes.ts +25 -10
- package/src/runtime/sync/resource-sync-events.ts +106 -38
- package/src/runtime/sync/sync-publisher.test.ts +49 -0
- package/src/runtime/sync/sync-publisher.ts +2 -1
- package/src/runtime/verification-outbound-actions.ts +73 -1
- package/src/telemetry/types.ts +12 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
- package/src/telemetry/usage-telemetry-reporter.ts +1 -0
- package/src/tools/acp/spawn.test.ts +119 -0
- package/src/tools/acp/spawn.ts +15 -2
- package/src/tools/apps/definitions.ts +2 -8
- package/src/tools/ask-question/ask-question-tool.test.ts +3 -3
- package/src/tools/ask-question/ask-question-tool.ts +38 -45
- package/src/tools/browser/__tests__/pinned-tabs.test.ts +70 -0
- package/src/tools/browser/browser-execution.ts +16 -3
- package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +3 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +12 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +27 -1
- package/src/tools/browser/cdp-client/factory.ts +100 -17
- package/src/tools/browser/cdp-client/local-cdp-client.ts +12 -0
- package/src/tools/browser/cdp-client/types.ts +65 -0
- package/src/tools/browser/pinned-tabs.ts +96 -40
- package/src/tools/computer-use/definitions.ts +22 -78
- package/src/tools/credential-execution/make-authenticated-request.ts +3 -9
- package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -9
- package/src/tools/credential-execution/run-authenticated-command.ts +3 -9
- package/src/tools/credentials/vault.ts +3 -9
- package/src/tools/document/document-tool.ts +59 -0
- package/src/tools/execution-target.ts +21 -23
- package/src/tools/executor.ts +6 -1
- package/src/tools/filesystem/edit.ts +3 -9
- package/src/tools/filesystem/list.ts +3 -9
- package/src/tools/filesystem/read.ts +3 -9
- package/src/tools/filesystem/write.ts +3 -9
- package/src/tools/host-filesystem/edit.ts +3 -9
- package/src/tools/host-filesystem/read.ts +3 -9
- package/src/tools/host-filesystem/transfer.ts +3 -9
- package/src/tools/host-filesystem/write.ts +3 -9
- package/src/tools/host-terminal/host-shell.ts +3 -9
- package/src/tools/mcp/mcp-tool-factory.ts +1 -8
- package/src/tools/memory/register.test.ts +1 -1
- package/src/tools/memory/register.ts +4 -9
- package/src/tools/network/web-fetch.ts +3 -9
- package/src/tools/network/web-search.ts +25 -32
- package/src/tools/registry.ts +7 -23
- package/src/tools/schema-transforms.ts +1 -1
- package/src/tools/skills/execute.ts +3 -9
- package/src/tools/skills/load.ts +3 -9
- package/src/tools/skills/skill-tool-factory.ts +1 -8
- package/src/tools/subagent/notify-parent.ts +3 -9
- package/src/tools/system/request-permission.ts +3 -9
- package/src/tools/terminal/shell.ts +3 -9
- package/src/tools/tool-defaults.ts +94 -0
- package/src/tools/types.ts +27 -98
- package/src/tools/ui-surface/definitions.ts +6 -22
- package/src/usage/pricing.ts +23 -0
- package/src/usage/types.ts +12 -0
- package/src/util/logger.ts +16 -7
- package/src/util/platform.ts +7 -2
- package/src/util/sqlite3-runtime.ts +65 -0
- package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
- package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/__tests__/compaction-strip-metadata-clear.test.ts +0 -206
- package/src/__tests__/message-complete-display-id.test.ts +0 -175
- package/src/daemon/query-complexity-router.ts +0 -75
- package/src/prompts/cache-boundary.ts +0 -8
|
@@ -287,7 +287,7 @@ describe("shell tool proxy mode", () => {
|
|
|
287
287
|
});
|
|
288
288
|
|
|
289
289
|
test("schema includes network_mode and credential_ids", () => {
|
|
290
|
-
const def = shellTool
|
|
290
|
+
const def = shellTool;
|
|
291
291
|
const props = (def.input_schema as { properties: Record<string, unknown> })
|
|
292
292
|
.properties;
|
|
293
293
|
expect(props.network_mode).toBeDefined();
|
|
@@ -130,9 +130,9 @@ describe("isAssistantFeatureFlagEnabled with skillFlagKey", () => {
|
|
|
130
130
|
// ---------------------------------------------------------------------------
|
|
131
131
|
|
|
132
132
|
describe("isAssistantFeatureFlagEnabled", () => {
|
|
133
|
-
test("returns
|
|
133
|
+
test("returns false for unknown flags (closed by default)", () => {
|
|
134
134
|
const config = makeConfig();
|
|
135
|
-
expect(isAssistantFeatureFlagEnabled("unknown", config)).toBe(
|
|
135
|
+
expect(isAssistantFeatureFlagEnabled("unknown", config)).toBe(false);
|
|
136
136
|
});
|
|
137
137
|
|
|
138
138
|
test("file-based override overrides registry default", () => {
|
|
@@ -50,7 +50,7 @@ mock.module("../config/assistant-feature-flags.js", () => ({
|
|
|
50
50
|
isAssistantFeatureFlagEnabled: (key: string, _config: unknown): boolean => {
|
|
51
51
|
const explicit = _mockOverrides[key];
|
|
52
52
|
if (typeof explicit === "boolean") return explicit;
|
|
53
|
-
return
|
|
53
|
+
return false; // undeclared flags default to disabled
|
|
54
54
|
},
|
|
55
55
|
clearFeatureFlagOverridesCache: () => {
|
|
56
56
|
_mockOverrides = {};
|
|
@@ -121,15 +121,12 @@ mock.module("../tools/skills/skill-tool-factory.js", () => ({
|
|
|
121
121
|
description: entry.description,
|
|
122
122
|
category: entry.category,
|
|
123
123
|
defaultRiskLevel: RiskLevel.Medium,
|
|
124
|
+
executionTarget: "sandbox" as const,
|
|
124
125
|
origin: "skill" as const,
|
|
125
126
|
ownerSkillId: skillId,
|
|
126
127
|
ownerSkillVersionHash: versionHash,
|
|
127
128
|
ownerSkillBundled: bundled ?? undefined,
|
|
128
|
-
|
|
129
|
-
name: entry.name,
|
|
130
|
-
description: entry.description,
|
|
131
|
-
input_schema: entry.input_schema as object,
|
|
132
|
-
}),
|
|
129
|
+
input_schema: entry.input_schema as object,
|
|
133
130
|
execute: async () => ({ content: "", isError: false }),
|
|
134
131
|
}));
|
|
135
132
|
},
|
|
@@ -422,7 +419,7 @@ describe("projectSkillTools feature flag enforcement", () => {
|
|
|
422
419
|
];
|
|
423
420
|
const prevActive = new Map<string, string>();
|
|
424
421
|
|
|
425
|
-
// Declared skill is OFF
|
|
422
|
+
// Declared skill is OFF; plain-skill has no featureFlag so remains ON.
|
|
426
423
|
_setOverridesForTesting({ [DECLARED_FLAG_KEY]: false });
|
|
427
424
|
|
|
428
425
|
const result = projectSkillTools(history, {
|
|
@@ -23,7 +23,7 @@ mock.module("../util/logger.js", () => ({
|
|
|
23
23
|
}));
|
|
24
24
|
|
|
25
25
|
// Mock config loader and feature flags to avoid filesystem reads on CI.
|
|
26
|
-
//
|
|
26
|
+
// The benchmark fixture treats every feature flag as enabled.
|
|
27
27
|
mock.module("../config/loader.js", () => ({
|
|
28
28
|
getConfig: () => ({}),
|
|
29
29
|
loadConfig: () => ({}),
|
|
@@ -114,11 +114,7 @@ mock.module("../tools/skills/skill-tool-factory.js", () => ({
|
|
|
114
114
|
ownerSkillId: skillId,
|
|
115
115
|
ownerSkillVersionHash: versionHash,
|
|
116
116
|
ownerSkillBundled: bundled,
|
|
117
|
-
|
|
118
|
-
name: e.name,
|
|
119
|
-
description: e.description,
|
|
120
|
-
input_schema: e.input_schema,
|
|
121
|
-
}),
|
|
117
|
+
input_schema: e.input_schema,
|
|
122
118
|
execute: async () => ({ content: "", isError: false }),
|
|
123
119
|
})),
|
|
124
120
|
}));
|
|
@@ -146,7 +146,7 @@ function clearCaptured(): void {
|
|
|
146
146
|
|
|
147
147
|
describe("notify_parent tool definition", () => {
|
|
148
148
|
test("has correct core tool definition", () => {
|
|
149
|
-
const def = notifyParentTool
|
|
149
|
+
const def = notifyParentTool;
|
|
150
150
|
const schema = def.input_schema as Record<string, unknown>;
|
|
151
151
|
expect(def.name).toBe("notify_parent");
|
|
152
152
|
expect(schema.required).toContain("message");
|
|
@@ -51,6 +51,7 @@ const mockGetMessages = mock((_conversationId: string) => [
|
|
|
51
51
|
|
|
52
52
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
53
53
|
getMessages: mockGetMessages,
|
|
54
|
+
getConversation: (_id: string) => null,
|
|
54
55
|
}));
|
|
55
56
|
|
|
56
57
|
const mockGetConfiguredProvider = mock(async () => ({
|
|
@@ -60,4 +60,63 @@ describe("sync message contract", () => {
|
|
|
60
60
|
}),
|
|
61
61
|
).toThrow();
|
|
62
62
|
});
|
|
63
|
+
|
|
64
|
+
test("buildSyncChangedMessage includes originClientId when provided", () => {
|
|
65
|
+
const message = buildSyncChangedMessage(
|
|
66
|
+
[SYNC_TAGS.assistantAvatar],
|
|
67
|
+
"client-abc",
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
expect(message).toEqual({
|
|
71
|
+
type: "sync_changed",
|
|
72
|
+
tags: [SYNC_TAGS.assistantAvatar],
|
|
73
|
+
originClientId: "client-abc",
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test("buildSyncChangedMessage omits originClientId when undefined", () => {
|
|
78
|
+
const message = buildSyncChangedMessage([SYNC_TAGS.assistantAvatar]);
|
|
79
|
+
|
|
80
|
+
expect(message).toEqual({
|
|
81
|
+
type: "sync_changed",
|
|
82
|
+
tags: [SYNC_TAGS.assistantAvatar],
|
|
83
|
+
});
|
|
84
|
+
expect("originClientId" in message).toBe(false);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("buildSyncChangedMessage trims and drops blank originClientId", () => {
|
|
88
|
+
const blank = buildSyncChangedMessage(
|
|
89
|
+
[SYNC_TAGS.assistantAvatar],
|
|
90
|
+
" ",
|
|
91
|
+
);
|
|
92
|
+
expect("originClientId" in blank).toBe(false);
|
|
93
|
+
|
|
94
|
+
const trimmed = buildSyncChangedMessage(
|
|
95
|
+
[SYNC_TAGS.assistantAvatar],
|
|
96
|
+
" client-xyz ",
|
|
97
|
+
);
|
|
98
|
+
expect(trimmed).toEqual({
|
|
99
|
+
type: "sync_changed",
|
|
100
|
+
tags: [SYNC_TAGS.assistantAvatar],
|
|
101
|
+
originClientId: "client-xyz",
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
test("schema accepts a string originClientId and rejects non-string types", () => {
|
|
106
|
+
expect(() =>
|
|
107
|
+
SyncChangedMessageSchema.parse({
|
|
108
|
+
type: "sync_changed",
|
|
109
|
+
tags: [SYNC_TAGS.assistantAvatar],
|
|
110
|
+
originClientId: "client-abc",
|
|
111
|
+
}),
|
|
112
|
+
).not.toThrow();
|
|
113
|
+
|
|
114
|
+
expect(() =>
|
|
115
|
+
SyncChangedMessageSchema.parse({
|
|
116
|
+
type: "sync_changed",
|
|
117
|
+
tags: [SYNC_TAGS.assistantAvatar],
|
|
118
|
+
originClientId: 42,
|
|
119
|
+
}),
|
|
120
|
+
).toThrow();
|
|
121
|
+
});
|
|
63
122
|
});
|
|
@@ -69,51 +69,37 @@ mock.module("../prompts/user-reference.js", () => ({
|
|
|
69
69
|
resolveUserPronouns: () => null,
|
|
70
70
|
}));
|
|
71
71
|
|
|
72
|
+
// Stub persona-resolver so tests can dictate the slug `buildSystemPrompt`
|
|
73
|
+
// sees without needing to write contact rows to the test DB. The user
|
|
74
|
+
// and channel persona files themselves now flow through bundled sections
|
|
75
|
+
// (`10-user-persona` / `11-channel-persona`) that read from disk, so
|
|
76
|
+
// persona *content* is exercised by writing the file under TEST_DIR
|
|
77
|
+
// rather than mocking it here. Tests mutate `mockPersona` in place;
|
|
78
|
+
// the default (all-null) matches a fresh workspace with no contacts
|
|
79
|
+
// and no `users/default.md`.
|
|
80
|
+
const mockPersona: {
|
|
81
|
+
userSlug: string | null;
|
|
82
|
+
guardianPersona: string | null;
|
|
83
|
+
} = { userSlug: null, guardianPersona: null };
|
|
84
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
85
|
+
const realPersonaResolver = require("../prompts/persona-resolver.js");
|
|
86
|
+
mock.module("../prompts/persona-resolver.js", () => ({
|
|
87
|
+
...realPersonaResolver,
|
|
88
|
+
resolveUserSlug: () => mockPersona.userSlug,
|
|
89
|
+
resolveGuardianPersona: () => mockPersona.guardianPersona,
|
|
90
|
+
}));
|
|
91
|
+
|
|
72
92
|
// Import after mock
|
|
73
|
-
const {
|
|
74
|
-
|
|
75
|
-
ensurePromptFiles,
|
|
76
|
-
stripCommentLines,
|
|
77
|
-
SYSTEM_PROMPT_CACHE_BOUNDARY,
|
|
78
|
-
} = await import("../prompts/system-prompt.js");
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Extract BOOTSTRAP.md content + the user persona from the dynamic block
|
|
82
|
-
* of the system prompt, stripping configuration, skills catalog, and
|
|
83
|
-
* connected services.
|
|
84
|
-
*
|
|
85
|
-
* Neither SOUL.md nor IDENTITY.md flows through this helper — they
|
|
86
|
-
* render as the `09-soul` and `08-identity` workspace-backed sections in
|
|
87
|
-
* the static (cached) prefix. Tests that assert on their content slice
|
|
88
|
-
* the static block directly.
|
|
89
|
-
*/
|
|
90
|
-
function basePrompt(result: string): string {
|
|
91
|
-
// The workspace files are in the dynamic block after the cache boundary.
|
|
92
|
-
const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
93
|
-
let s =
|
|
94
|
-
boundaryIdx >= 0
|
|
95
|
-
? result.slice(boundaryIdx + SYSTEM_PROMPT_CACHE_BOUNDARY.length)
|
|
96
|
-
: result;
|
|
97
|
-
for (const heading of [
|
|
98
|
-
"## Configuration",
|
|
99
|
-
"## Skills Catalog",
|
|
100
|
-
"## External Communications Identity",
|
|
101
|
-
"# Connected Services",
|
|
102
|
-
"## Dynamic Skill Authoring Workflow",
|
|
103
|
-
]) {
|
|
104
|
-
if (s.startsWith(heading)) {
|
|
105
|
-
s = "";
|
|
106
|
-
break;
|
|
107
|
-
}
|
|
108
|
-
const idx = s.indexOf(`\n\n${heading}`);
|
|
109
|
-
if (idx !== -1) s = s.slice(0, idx);
|
|
110
|
-
}
|
|
111
|
-
return s;
|
|
112
|
-
}
|
|
93
|
+
const { buildSystemPrompt, ensurePromptFiles, stripCommentLines } =
|
|
94
|
+
await import("../prompts/system-prompt.js");
|
|
113
95
|
|
|
114
96
|
describe("buildSystemPrompt", () => {
|
|
115
97
|
beforeEach(() => {
|
|
116
98
|
mkdirSync(TEST_DIR, { recursive: true });
|
|
99
|
+
// Reset persona stub so each test starts from a fresh
|
|
100
|
+
// no-guardian baseline.
|
|
101
|
+
mockPersona.userSlug = null;
|
|
102
|
+
mockPersona.guardianPersona = null;
|
|
117
103
|
});
|
|
118
104
|
|
|
119
105
|
afterEach(() => {
|
|
@@ -123,8 +109,10 @@ describe("buildSystemPrompt", () => {
|
|
|
123
109
|
"USER.md",
|
|
124
110
|
"BOOTSTRAP.md",
|
|
125
111
|
"UPDATES.md",
|
|
112
|
+
"VOICE.md",
|
|
126
113
|
"skills",
|
|
127
114
|
"users",
|
|
115
|
+
"channels",
|
|
128
116
|
]) {
|
|
129
117
|
const p = join(TEST_DIR, name);
|
|
130
118
|
if (existsSync(p)) rmSync(p, { recursive: true, force: true });
|
|
@@ -134,19 +122,11 @@ describe("buildSystemPrompt", () => {
|
|
|
134
122
|
}
|
|
135
123
|
});
|
|
136
124
|
|
|
137
|
-
test("returns empty string when no files exist", () => {
|
|
138
|
-
const result = buildSystemPrompt();
|
|
139
|
-
expect(basePrompt(result)).toBe("");
|
|
140
|
-
});
|
|
141
|
-
|
|
142
125
|
test("uses SOUL.md when it exists", () => {
|
|
143
126
|
writeFileSync(join(TEST_DIR, "SOUL.md"), "# My Soul\n\nBe awesome.");
|
|
144
127
|
const result = buildSystemPrompt();
|
|
145
|
-
// SOUL.md renders as the `09-soul` workspace-backed section
|
|
146
|
-
|
|
147
|
-
const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
148
|
-
expect(boundaryIdx).toBeGreaterThan(-1);
|
|
149
|
-
expect(result.slice(0, boundaryIdx)).toContain("# My Soul\n\nBe awesome.");
|
|
128
|
+
// SOUL.md renders as the `09-soul` workspace-backed section.
|
|
129
|
+
expect(result).toContain("# My Soul\n\nBe awesome.");
|
|
150
130
|
});
|
|
151
131
|
|
|
152
132
|
test("uses IDENTITY.md when it exists", () => {
|
|
@@ -155,42 +135,40 @@ describe("buildSystemPrompt", () => {
|
|
|
155
135
|
"# My Identity\n\nI am Vellum.",
|
|
156
136
|
);
|
|
157
137
|
const result = buildSystemPrompt();
|
|
158
|
-
// IDENTITY.md renders as the `08-identity` workspace-backed section
|
|
159
|
-
|
|
160
|
-
const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
161
|
-
expect(boundaryIdx).toBeGreaterThan(-1);
|
|
162
|
-
expect(result.slice(0, boundaryIdx)).toContain(
|
|
163
|
-
"# My Identity\n\nI am Vellum.",
|
|
164
|
-
);
|
|
138
|
+
// IDENTITY.md renders as the `08-identity` workspace-backed section.
|
|
139
|
+
expect(result).toContain("# My Identity\n\nI am Vellum.");
|
|
165
140
|
});
|
|
166
141
|
|
|
167
142
|
test("composes IDENTITY.md + SOUL.md when both exist", () => {
|
|
168
143
|
writeFileSync(join(TEST_DIR, "IDENTITY.md"), "# Identity\n\nI am Vellum.");
|
|
169
144
|
writeFileSync(join(TEST_DIR, "SOUL.md"), "# Soul\n\nBe thoughtful.");
|
|
170
145
|
const result = buildSystemPrompt();
|
|
171
|
-
//
|
|
172
|
-
//
|
|
173
|
-
|
|
174
|
-
expect(
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
expect(staticBlock).toContain("# Soul\n\nBe thoughtful.");
|
|
178
|
-
const identityIdx = staticBlock.indexOf("# Identity\n\nI am Vellum.");
|
|
179
|
-
const soulIdx = staticBlock.indexOf("# Soul\n\nBe thoughtful.");
|
|
146
|
+
// IDENTITY renders before SOUL (sections `08-identity` then
|
|
147
|
+
// `09-soul`).
|
|
148
|
+
expect(result).toContain("# Identity\n\nI am Vellum.");
|
|
149
|
+
expect(result).toContain("# Soul\n\nBe thoughtful.");
|
|
150
|
+
const identityIdx = result.indexOf("# Identity\n\nI am Vellum.");
|
|
151
|
+
const soulIdx = result.indexOf("# Soul\n\nBe thoughtful.");
|
|
180
152
|
expect(identityIdx).toBeLessThan(soulIdx);
|
|
181
|
-
expect(basePrompt(result)).toBe("");
|
|
182
153
|
});
|
|
183
154
|
|
|
184
155
|
test("ignores empty SOUL.md", () => {
|
|
185
156
|
writeFileSync(join(TEST_DIR, "SOUL.md"), " \n \n ");
|
|
157
|
+
writeFileSync(join(TEST_DIR, "IDENTITY.md"), "# Identity\n\nI am Vellum.");
|
|
186
158
|
const result = buildSystemPrompt();
|
|
187
|
-
|
|
159
|
+
// IDENTITY renders but SOUL is gated off by the renderer's
|
|
160
|
+
// empty-body check; no SOUL content should appear.
|
|
161
|
+
expect(result).toContain("# Identity\n\nI am Vellum.");
|
|
162
|
+
expect(result).not.toContain(" \n \n ");
|
|
188
163
|
});
|
|
189
164
|
|
|
190
165
|
test("ignores empty IDENTITY.md", () => {
|
|
191
166
|
writeFileSync(join(TEST_DIR, "IDENTITY.md"), "");
|
|
167
|
+
writeFileSync(join(TEST_DIR, "SOUL.md"), "# Soul\n\nBe thoughtful.");
|
|
192
168
|
const result = buildSystemPrompt();
|
|
193
|
-
|
|
169
|
+
// SOUL renders but IDENTITY's empty file is gated off by the
|
|
170
|
+
// renderer's empty-body check.
|
|
171
|
+
expect(result).toContain("# Soul\n\nBe thoughtful.");
|
|
194
172
|
});
|
|
195
173
|
|
|
196
174
|
test("trims whitespace from file content", () => {
|
|
@@ -198,10 +176,8 @@ describe("buildSystemPrompt", () => {
|
|
|
198
176
|
const result = buildSystemPrompt();
|
|
199
177
|
// SOUL.md renders via the `09-soul` workspace-backed section;
|
|
200
178
|
// stripCommentLines + trim run inside the section renderer.
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
expect(staticBlock).toContain("Be kind");
|
|
204
|
-
expect(staticBlock).not.toContain("\n Be kind \n");
|
|
179
|
+
expect(result).toContain("Be kind");
|
|
180
|
+
expect(result).not.toContain("\n Be kind \n");
|
|
205
181
|
});
|
|
206
182
|
|
|
207
183
|
test("does not include skills catalog in system prompt", () => {
|
|
@@ -274,9 +250,6 @@ describe("buildSystemPrompt", () => {
|
|
|
274
250
|
writeFileSync(join(TEST_DIR, "IDENTITY.md"), "Identity");
|
|
275
251
|
writeFileSync(join(TEST_DIR, "SOUL.md"), "Soul");
|
|
276
252
|
const result = buildSystemPrompt();
|
|
277
|
-
// Both IDENTITY and SOUL render in the static (cached) prefix; the
|
|
278
|
-
// dynamic block sliced by basePrompt is empty here.
|
|
279
|
-
expect(basePrompt(result)).toBe("");
|
|
280
253
|
expect(result).toContain("Identity");
|
|
281
254
|
expect(result).toContain("Soul");
|
|
282
255
|
});
|
|
@@ -292,54 +265,117 @@ describe("buildSystemPrompt", () => {
|
|
|
292
265
|
const result = buildSystemPrompt();
|
|
293
266
|
expect(result).not.toContain("stale user content");
|
|
294
267
|
expect(result).toContain("Identity");
|
|
295
|
-
expect(basePrompt(result)).toBe("");
|
|
296
268
|
});
|
|
297
269
|
|
|
298
|
-
test("
|
|
270
|
+
test("includes resolved user persona in the static prefix", () => {
|
|
271
|
+
// User persona flows through the `10-user-persona` bundled section,
|
|
272
|
+
// which reads from `users/<userSlug>.md` (or `users/default.md` as
|
|
273
|
+
// a fallback). Set the slug + write the file to exercise both.
|
|
274
|
+
mockPersona.userSlug = "alice";
|
|
275
|
+
mkdirSync(join(TEST_DIR, "users"), { recursive: true });
|
|
276
|
+
writeFileSync(
|
|
277
|
+
join(TEST_DIR, "users", "alice.md"),
|
|
278
|
+
"# User persona\n\nName: Alice",
|
|
279
|
+
);
|
|
299
280
|
writeFileSync(join(TEST_DIR, "IDENTITY.md"), "Identity");
|
|
300
281
|
writeFileSync(join(TEST_DIR, "SOUL.md"), "Soul");
|
|
301
|
-
const result = buildSystemPrompt(
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
// IDENTITY and SOUL both render in the static (cached) prefix; only
|
|
305
|
-
// the user persona ends up in the dynamic block.
|
|
306
|
-
expect(basePrompt(result)).toBe("# User persona\n\nName: Alice");
|
|
282
|
+
const result = buildSystemPrompt();
|
|
283
|
+
// IDENTITY, SOUL, and the user persona all render as workspace-backed
|
|
284
|
+
// bundled sections in the assembled prompt.
|
|
307
285
|
expect(result).toContain("Identity");
|
|
308
286
|
expect(result).toContain("Soul");
|
|
287
|
+
expect(result).toContain("# User persona");
|
|
288
|
+
expect(result).toContain("Name: Alice");
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
test("user persona falls back to users/default.md when the slug's file is missing", () => {
|
|
292
|
+
// The `10-user-persona` section's workspacePath is
|
|
293
|
+
// `["users/{{userSlug}}.md", "users/default.md"]` — when the
|
|
294
|
+
// primary file doesn't exist the renderer falls through to default.
|
|
295
|
+
mockPersona.userSlug = "alice";
|
|
296
|
+
mkdirSync(join(TEST_DIR, "users"), { recursive: true });
|
|
297
|
+
writeFileSync(
|
|
298
|
+
join(TEST_DIR, "users", "default.md"),
|
|
299
|
+
"# Default persona\n\nNo contact bound.",
|
|
300
|
+
);
|
|
301
|
+
const result = buildSystemPrompt();
|
|
302
|
+
expect(result).toContain("# Default persona");
|
|
303
|
+
expect(result).toContain("No contact bound.");
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
test("includes channel persona from channels/<channelSlug>.md", () => {
|
|
307
|
+
// Channel persona flows through the `11-channel-persona` section.
|
|
308
|
+
// Default channel is "vellum" when no channelCapabilities passed.
|
|
309
|
+
mkdirSync(join(TEST_DIR, "channels"), { recursive: true });
|
|
310
|
+
writeFileSync(
|
|
311
|
+
join(TEST_DIR, "channels", "vellum.md"),
|
|
312
|
+
"# Channel persona\n\nThis is the Vellum channel.",
|
|
313
|
+
);
|
|
314
|
+
const result = buildSystemPrompt();
|
|
315
|
+
expect(result).toContain("# Channel persona");
|
|
316
|
+
expect(result).toContain("This is the Vellum channel.");
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
test("includes VOICE.md as the 12-voice section with prepended heading", () => {
|
|
320
|
+
// VOICE.md flows through the `12-voice` bundled section. The
|
|
321
|
+
// section transform prepends `# Voice Profile` so the file itself
|
|
322
|
+
// stays heading-free; the model writes voice markers as plain
|
|
323
|
+
// bullets / lines.
|
|
324
|
+
writeFileSync(
|
|
325
|
+
join(TEST_DIR, "VOICE.md"),
|
|
326
|
+
"- Prefers lowercase. Replies tightly. Skips greetings.",
|
|
327
|
+
);
|
|
328
|
+
const result = buildSystemPrompt();
|
|
329
|
+
expect(result).toContain("# Voice Profile");
|
|
330
|
+
expect(result).toContain("Prefers lowercase");
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
test("omits the 12-voice section when VOICE.md is missing", () => {
|
|
334
|
+
const result = buildSystemPrompt();
|
|
335
|
+
expect(result).not.toContain("# Voice Profile");
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
test("omits the 12-voice section when VOICE.md is empty / whitespace-only", () => {
|
|
339
|
+
writeFileSync(join(TEST_DIR, "VOICE.md"), " \n\n \n");
|
|
340
|
+
const result = buildSystemPrompt();
|
|
341
|
+
expect(result).not.toContain("# Voice Profile");
|
|
309
342
|
});
|
|
310
343
|
|
|
311
344
|
describe("BOOTSTRAP.md user persona placeholder", () => {
|
|
312
|
-
test("substitutes {{
|
|
345
|
+
test("substitutes {{userSlug}} with the resolved slug when a guardian slug is resolvable", () => {
|
|
346
|
+
// Simulate a guardian contact whose userFile resolves to alice.md.
|
|
347
|
+
mockPersona.userSlug = "alice";
|
|
313
348
|
writeFileSync(
|
|
314
349
|
join(TEST_DIR, "BOOTSTRAP.md"),
|
|
315
|
-
"# First run\n\nSave facts to users/{{
|
|
350
|
+
"# First run\n\nSave facts to users/{{userSlug}}.md immediately.",
|
|
316
351
|
);
|
|
317
|
-
const result = buildSystemPrompt(
|
|
352
|
+
const result = buildSystemPrompt();
|
|
318
353
|
expect(result).toContain("users/alice.md");
|
|
319
|
-
expect(result).not.toContain("{{
|
|
354
|
+
expect(result).not.toContain("{{userSlug}}");
|
|
320
355
|
});
|
|
321
356
|
|
|
322
|
-
test("falls back to users/default.md when
|
|
357
|
+
test("falls back to users/default.md when no slug is resolvable", () => {
|
|
323
358
|
writeFileSync(
|
|
324
359
|
join(TEST_DIR, "BOOTSTRAP.md"),
|
|
325
|
-
"# First run\n\nSave facts to users/{{
|
|
360
|
+
"# First run\n\nSave facts to users/{{userSlug}}.md immediately.",
|
|
326
361
|
);
|
|
327
362
|
const result = buildSystemPrompt();
|
|
328
363
|
expect(result).toContain("users/default.md");
|
|
329
|
-
expect(result).not.toContain("{{
|
|
364
|
+
expect(result).not.toContain("{{userSlug}}");
|
|
330
365
|
});
|
|
331
366
|
|
|
332
367
|
test("substitutes the unmodified bundled BOOTSTRAP.md template", () => {
|
|
333
368
|
// Copy the real bundled BOOTSTRAP.md into the test workspace so we
|
|
334
369
|
// verify substitution against the actual template the daemon ships.
|
|
370
|
+
mockPersona.userSlug = "alice";
|
|
335
371
|
const bundled = readFileSync(
|
|
336
372
|
join(import.meta.dirname, "..", "prompts", "templates", "BOOTSTRAP.md"),
|
|
337
373
|
"utf-8",
|
|
338
374
|
);
|
|
339
375
|
writeFileSync(join(TEST_DIR, "BOOTSTRAP.md"), bundled);
|
|
340
|
-
const result = buildSystemPrompt(
|
|
376
|
+
const result = buildSystemPrompt();
|
|
341
377
|
expect(result).toContain("users/alice.md");
|
|
342
|
-
expect(result).not.toContain("{{
|
|
378
|
+
expect(result).not.toContain("{{userSlug}}");
|
|
343
379
|
});
|
|
344
380
|
});
|
|
345
381
|
|
|
@@ -513,8 +549,14 @@ describe("buildSystemPrompt", () => {
|
|
|
513
549
|
|
|
514
550
|
test("file with only comment lines is treated as empty", () => {
|
|
515
551
|
writeFileSync(join(TEST_DIR, "SOUL.md"), "_ All comments\n_ Nothing else");
|
|
552
|
+
writeFileSync(join(TEST_DIR, "IDENTITY.md"), "# Identity\n\nI am Vellum.");
|
|
516
553
|
const result = buildSystemPrompt();
|
|
517
|
-
|
|
554
|
+
// Comment-only SOUL.md gets stripped down to "" by
|
|
555
|
+
// `stripCommentLines` and is then gated off by the renderer's
|
|
556
|
+
// empty-body check; only IDENTITY contributes content here.
|
|
557
|
+
expect(result).toContain("# Identity\n\nI am Vellum.");
|
|
558
|
+
expect(result).not.toContain("_ All comments");
|
|
559
|
+
expect(result).not.toContain("_ Nothing else");
|
|
518
560
|
});
|
|
519
561
|
|
|
520
562
|
describe("workspace system prompt sections", () => {
|
|
@@ -547,11 +589,7 @@ describe("buildSystemPrompt", () => {
|
|
|
547
589
|
);
|
|
548
590
|
const result = buildSystemPrompt();
|
|
549
591
|
expect(result.startsWith("You are operating in demo mode.")).toBe(true);
|
|
550
|
-
|
|
551
|
-
const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
552
|
-
expect(boundaryIdx).toBeGreaterThan(-1);
|
|
553
|
-
const staticBlock = result.slice(0, boundaryIdx);
|
|
554
|
-
expect(staticBlock).toContain("You are operating in demo mode.");
|
|
592
|
+
expect(result).toContain("You are operating in demo mode.");
|
|
555
593
|
});
|
|
556
594
|
|
|
557
595
|
test("workspace file without frontmatter is rendered as-is (always-on)", () => {
|
|
@@ -627,13 +665,10 @@ describe("buildSystemPrompt", () => {
|
|
|
627
665
|
expect(result.startsWith("Custom prefix")).toBe(true);
|
|
628
666
|
// IDENTITY.md renders via 08-identity in the static prefix after
|
|
629
667
|
// the 00-prefix slot.
|
|
630
|
-
const
|
|
631
|
-
const
|
|
632
|
-
const prefixIdx = staticBlock.indexOf("Custom prefix");
|
|
633
|
-
const identityIdx = staticBlock.indexOf("I am Vellum.");
|
|
668
|
+
const prefixIdx = result.indexOf("Custom prefix");
|
|
669
|
+
const identityIdx = result.indexOf("I am Vellum.");
|
|
634
670
|
expect(prefixIdx).toBeGreaterThan(-1);
|
|
635
671
|
expect(identityIdx).toBeGreaterThan(prefixIdx);
|
|
636
|
-
expect(basePrompt(result)).toBe("");
|
|
637
672
|
});
|
|
638
673
|
|
|
639
674
|
test("parallel tool calls section is sourced from workspace when present", () => {
|
|
@@ -809,11 +844,7 @@ describe("buildSystemPrompt", () => {
|
|
|
809
844
|
expect(result).toContain(
|
|
810
845
|
"Run `assistant --help` to discover commands.",
|
|
811
846
|
);
|
|
812
|
-
|
|
813
|
-
const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
814
|
-
expect(boundaryIdx).toBeGreaterThan(-1);
|
|
815
|
-
const staticBlock = result.slice(0, boundaryIdx);
|
|
816
|
-
expect(staticBlock).toContain("## Assistant CLI");
|
|
847
|
+
expect(result).toContain("## Assistant CLI");
|
|
817
848
|
});
|
|
818
849
|
|
|
819
850
|
test("bundled cli-reference default renders when no workspace override", () => {
|
|
@@ -854,11 +885,7 @@ describe("buildSystemPrompt", () => {
|
|
|
854
885
|
// The no-client body (em-dash separator after sandbox `bash`) must
|
|
855
886
|
// not leak when the with-client variant is active.
|
|
856
887
|
expect(result).not.toContain("install tools yourself; (2) browser");
|
|
857
|
-
|
|
858
|
-
const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
859
|
-
expect(boundaryIdx).toBeGreaterThan(-1);
|
|
860
|
-
const staticBlock = result.slice(0, boundaryIdx);
|
|
861
|
-
expect(staticBlock).toContain("## External Service Access");
|
|
888
|
+
expect(result).toContain("## External Service Access");
|
|
862
889
|
});
|
|
863
890
|
|
|
864
891
|
test("hasNoClient=true renders the two-tier (no host_bash) priority list", () => {
|
|
@@ -967,8 +994,7 @@ describe("buildSystemPrompt", () => {
|
|
|
967
994
|
|
|
968
995
|
test("paired {{#flag}} + {{^flag}} acts as if/else", () => {
|
|
969
996
|
// Use long unique markers — single letters collide with substrings
|
|
970
|
-
// in the rest of the system prompt (e.g. "
|
|
971
|
-
// SYSTEM_PROMPT_CACHE_BOUNDARY, "A" inside "API keys").
|
|
997
|
+
// in the rest of the system prompt (e.g. "A" inside "API keys").
|
|
972
998
|
mkdirSync(SYSTEM_PROMPTS_DIR, { recursive: true });
|
|
973
999
|
writeFileSync(
|
|
974
1000
|
SECTION_FILE,
|
|
@@ -1033,11 +1059,7 @@ describe("buildSystemPrompt", () => {
|
|
|
1033
1059
|
const result = buildSystemPrompt();
|
|
1034
1060
|
expect(result).toContain("## Sending Files to the User");
|
|
1035
1061
|
expect(result).toContain("Use the `<vellum-attachment />` tag.");
|
|
1036
|
-
|
|
1037
|
-
const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
1038
|
-
expect(boundaryIdx).toBeGreaterThan(-1);
|
|
1039
|
-
const staticBlock = result.slice(0, boundaryIdx);
|
|
1040
|
-
expect(staticBlock).toContain("## Sending Files to the User");
|
|
1062
|
+
expect(result).toContain("## Sending Files to the User");
|
|
1041
1063
|
});
|
|
1042
1064
|
|
|
1043
1065
|
test("renders after the cli-reference section to preserve original order", () => {
|
|
@@ -1083,11 +1105,7 @@ describe("buildSystemPrompt", () => {
|
|
|
1083
1105
|
const result = buildSystemPrompt();
|
|
1084
1106
|
expect(result).toContain("## Credential Security");
|
|
1085
1107
|
expect(result).toContain("Workspace override marker BRAVO_TANGO_7.");
|
|
1086
|
-
|
|
1087
|
-
const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
1088
|
-
expect(boundaryIdx).toBeGreaterThan(-1);
|
|
1089
|
-
const staticBlock = result.slice(0, boundaryIdx);
|
|
1090
|
-
expect(staticBlock).toContain("## Credential Security");
|
|
1108
|
+
expect(result).toContain("## Credential Security");
|
|
1091
1109
|
});
|
|
1092
1110
|
|
|
1093
1111
|
test("bundled credential-security default renders when no workspace override", () => {
|
|
@@ -1127,11 +1145,7 @@ describe("buildSystemPrompt", () => {
|
|
|
1127
1145
|
const result = buildSystemPrompt();
|
|
1128
1146
|
expect(result).toContain("## External Content");
|
|
1129
1147
|
expect(result).toContain("Workspace override marker NEBULA_9X.");
|
|
1130
|
-
|
|
1131
|
-
const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
1132
|
-
expect(boundaryIdx).toBeGreaterThan(-1);
|
|
1133
|
-
const staticBlock = result.slice(0, boundaryIdx);
|
|
1134
|
-
expect(staticBlock).toContain("## External Content");
|
|
1148
|
+
expect(result).toContain("## External Content");
|
|
1135
1149
|
});
|
|
1136
1150
|
|
|
1137
1151
|
test("bundled external-content default renders when no workspace override", () => {
|
|
@@ -288,7 +288,7 @@ describe("Shell tool input validation", () => {
|
|
|
288
288
|
});
|
|
289
289
|
|
|
290
290
|
test("tool definition includes required schema fields", () => {
|
|
291
|
-
const def = shellTool
|
|
291
|
+
const def = shellTool;
|
|
292
292
|
const schema = def.input_schema as {
|
|
293
293
|
required: string[];
|
|
294
294
|
properties: Record<string, unknown>;
|
|
@@ -26,11 +26,7 @@ const fakeTool = {
|
|
|
26
26
|
description: "Run a shell command",
|
|
27
27
|
category: "shell",
|
|
28
28
|
defaultRiskLevel: "high",
|
|
29
|
-
|
|
30
|
-
name: "bash",
|
|
31
|
-
description: "Run a shell command",
|
|
32
|
-
input_schema: {},
|
|
33
|
-
}),
|
|
29
|
+
input_schema: {},
|
|
34
30
|
execute: async () => ({ content: "ok", isError: false }),
|
|
35
31
|
};
|
|
36
32
|
|
|
@@ -95,7 +95,7 @@ mock.module("../permissions/checker.js", () => ({
|
|
|
95
95
|
mock.module("../memory/tool-usage-store.js", () => ({
|
|
96
96
|
recordToolInvocation: () => {},
|
|
97
97
|
getRecentInvocations: () => [],
|
|
98
|
-
rotateToolInvocations: () => 0,
|
|
98
|
+
rotateToolInvocations: async () => 0,
|
|
99
99
|
}));
|
|
100
100
|
|
|
101
101
|
// ── Tool registry: return a stub tool whose execute records the call ─
|
|
@@ -113,7 +113,7 @@ mock.module("../tools/registry.js", () => ({
|
|
|
113
113
|
description: "test tool",
|
|
114
114
|
category: "test",
|
|
115
115
|
defaultRiskLevel: "low",
|
|
116
|
-
|
|
116
|
+
input_schema: {},
|
|
117
117
|
execute: async (input: Record<string, unknown>) => {
|
|
118
118
|
lastToolCall = { name, input };
|
|
119
119
|
return fakeToolResult;
|