@vellumai/assistant 0.8.3 → 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/docker-entrypoint.sh +0 -1
- package/docs/browser-use-architecture-phase2.md +1 -1
- package/knip.json +2 -1
- package/node_modules/@vellumai/gateway-client/src/types.ts +2 -0
- package/openapi.yaml +1492 -100
- package/package.json +1 -1
- package/src/__tests__/agent-loop-exit-reason.test.ts +4 -5
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -1
- package/src/__tests__/agent-loop.test.ts +88 -3
- package/src/__tests__/anthropic-provider.test.ts +302 -33
- package/src/__tests__/approval-cascade.test.ts +1 -1
- 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 +4 -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-delivery-store.test.ts +193 -0
- package/src/__tests__/channel-guardian.test.ts +3 -3
- package/src/__tests__/channel-reply-delivery.test.ts +284 -5
- package/src/__tests__/channel-retry-sweep.test.ts +274 -1
- package/src/__tests__/checker.test.ts +6 -15
- package/src/__tests__/compaction-events.test.ts +2 -1
- package/src/__tests__/compactor-call-site-logging.test.ts +214 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +110 -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__/config-watcher.test.ts +1 -1
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
- package/src/__tests__/context-token-estimator.test.ts +91 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +55 -4
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +228 -8
- package/src/__tests__/conversation-agent-loop.test.ts +188 -129
- package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -5
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -1
- package/src/__tests__/conversation-clean-command.test.ts +137 -0
- package/src/__tests__/conversation-clear-safety.test.ts +25 -25
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -1
- 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 +324 -0
- package/src/__tests__/conversation-lifecycle.test.ts +53 -12
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-load-history-stripped.test.ts +279 -0
- package/src/__tests__/conversation-pairing.test.ts +2 -2
- package/src/__tests__/conversation-process-callsite.test.ts +1 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -1
- package/src/__tests__/conversation-queue.test.ts +1 -1
- 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-runtime-assembly.test.ts +264 -81
- package/src/__tests__/conversation-seed-composer.test.ts +66 -4
- package/src/__tests__/conversation-skill-tools.test.ts +2 -5
- package/src/__tests__/conversation-slash-commands.test.ts +36 -8
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -1
- package/src/__tests__/conversation-speed-override.test.ts +1 -1
- package/src/__tests__/conversation-store.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +220 -0
- package/src/__tests__/conversation-sync-tags.test.ts +99 -32
- package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -1
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -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 +7 -0
- package/src/__tests__/credential-vault-unit.test.ts +2 -2
- package/src/__tests__/cu-unified-flow.test.ts +10 -1
- package/src/__tests__/dm-backfill.test.ts +64 -0
- package/src/__tests__/dm-persistence.test.ts +33 -0
- package/src/__tests__/document-find-replace.test.ts +501 -0
- package/src/__tests__/dynamic-page-surface.test.ts +2 -2
- package/src/__tests__/email-html-renderer.test.ts +12 -0
- package/src/__tests__/first-greeting.test.ts +23 -2
- 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__/headless-browser-navigate.test.ts +172 -0
- package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
- package/src/__tests__/heartbeat-service.test.ts +4 -0
- package/src/__tests__/host-bash-proxy.test.ts +6 -0
- package/src/__tests__/host-browser-proxy.test.ts +10 -0
- package/src/__tests__/host-cu-proxy.test.ts +8 -1
- package/src/__tests__/host-file-proxy.test.ts +8 -1
- package/src/__tests__/host-shell-tool.test.ts +1 -1
- package/src/__tests__/host-transfer-proxy.test.ts +8 -1
- package/src/__tests__/identity-routes.test.ts +57 -0
- package/src/__tests__/inbound-slack-persistence.test.ts +3 -0
- package/src/__tests__/init-feature-flag-overrides.test.ts +5 -6
- package/src/__tests__/injector-chain.test.ts +2 -0
- package/src/__tests__/injector-document-comments.test.ts +378 -0
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +4 -25
- package/src/__tests__/list-messages-attachments.test.ts +21 -17
- package/src/__tests__/list-messages-hidden-metadata.test.ts +217 -0
- package/src/__tests__/list-messages-page-latest.test.ts +130 -14
- package/src/__tests__/list-messages-tool-merge.test.ts +77 -17
- package/src/__tests__/llm-context-normalization.test.ts +0 -2
- 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 +161 -9
- package/src/__tests__/llm-usage-store.test.ts +66 -0
- package/src/__tests__/log-export-routes.test.ts +99 -2
- 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__/message-queue-steer.test.ts +114 -0
- 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 +151 -0
- package/src/__tests__/openai-responses-provider.test.ts +118 -16
- package/src/__tests__/outbound-slack-persistence.test.ts +187 -20
- package/src/__tests__/pending-interactions-resolved-event.test.ts +189 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
- package/src/__tests__/platform.test.ts +2 -5
- package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
- package/src/__tests__/plugin-bootstrap.test.ts +2 -2
- package/src/__tests__/plugin-source-watcher.test.ts +302 -0
- 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__/process-message-background-slack.test.ts +1 -51
- package/src/__tests__/process-message-display-content.test.ts +21 -16
- 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__/server-history-render.test.ts +83 -4
- 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__/steer-tool-repair.test.ts +249 -0
- 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 +161 -124
- package/src/__tests__/terminal-tools.test.ts +12 -2
- package/src/__tests__/thinking-block-replay.test.ts +113 -0
- package/src/__tests__/thread-backfill.test.ts +370 -22
- 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 +89 -53
- package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +167 -0
- 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__/twilio-routes.test.ts +1 -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__/web-fetch.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +94 -10
- package/src/__tests__/workspace-migration-088-deprecate-background-conversation-override.test.ts +158 -0
- 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/attachments.ts +1 -0
- package/src/agent/loop.ts +65 -20
- 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/next-wake.test.ts +289 -0
- package/src/background-wake/next-wake.ts +172 -0
- package/src/background-wake/runtime-registry.ts +24 -0
- package/src/browser/operations.ts +15 -0
- package/src/cli/commands/__tests__/browser.test.ts +23 -5
- package/src/cli/commands/__tests__/conversations-slack.test.ts +572 -0
- 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 +10 -12
- package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
- package/src/cli/commands/browser.ts +247 -0
- package/src/cli/commands/conversations.ts +128 -1
- package/src/cli/commands/domain.ts +91 -41
- package/src/cli/commands/inference-providers.ts +147 -1
- 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 +483 -0
- package/src/cli/commands/memory-v3-render.ts +344 -0
- package/src/cli/commands/memory-v3.ts +316 -0
- package/src/cli/commands/notifications.ts +24 -2
- package/src/cli/program.ts +2 -0
- package/src/cli/utils/conversation-id.ts +17 -5
- package/src/config/assistant-feature-flags.ts +21 -9
- package/src/config/bundled-skills/app-builder/SKILL.md +2 -2
- package/src/config/bundled-skills/document-editor/SKILL.md +124 -0
- package/src/config/bundled-skills/document-editor/TOOLS.json +258 -0
- package/src/config/bundled-skills/document-editor/tools/comment-list.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-reply.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/comment-resolve.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-find.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-open.ts +12 -0
- package/src/config/bundled-skills/document-editor/tools/document-replace-text.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/SKILL.md +8 -0
- 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-skills/schedule/SKILL.md +8 -0
- package/src/config/bundled-tool-registry.ts +24 -12
- package/src/config/call-site-defaults.ts +20 -0
- package/src/config/feature-flag-registry.json +115 -3
- package/src/config/llm-resolver.ts +16 -2
- package/src/config/schemas/__tests__/memory-v2.test.ts +217 -1
- package/src/config/schemas/call-site-catalog.ts +35 -0
- package/src/config/schemas/llm.ts +14 -0
- package/src/config/schemas/memory-v2.ts +294 -1
- package/src/config/schemas/memory.ts +2 -1
- package/src/context/compactor.ts +60 -1
- package/src/context/token-estimator.ts +47 -4
- package/src/context/window-manager.ts +25 -0
- package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
- package/src/conversations/message-consolidation.ts +404 -0
- package/src/credential-health/credential-health-service.ts +34 -19
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -1
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +66 -6
- package/src/daemon/__tests__/meet-manifest-loader.test.ts +1 -1
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +357 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +287 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +155 -36
- package/src/daemon/conversation-agent-loop.ts +307 -88
- package/src/daemon/conversation-error.ts +31 -1
- package/src/daemon/conversation-lifecycle.ts +149 -118
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +273 -0
- package/src/daemon/conversation-queue-manager.ts +14 -0
- package/src/daemon/conversation-runtime-assembly.ts +145 -84
- package/src/daemon/conversation-slash.ts +37 -5
- package/src/daemon/conversation-surfaces.ts +45 -2
- package/src/daemon/conversation-tool-setup.ts +70 -3
- package/src/daemon/conversation-usage.ts +2 -0
- package/src/daemon/conversation.ts +54 -32
- package/src/daemon/disk-pressure-guard.ts +14 -2
- package/src/daemon/first-greeting.ts +10 -0
- package/src/daemon/handlers/__tests__/config-a2a-accept.test.ts +498 -0
- package/src/daemon/handlers/config-a2a.ts +160 -0
- package/src/daemon/handlers/config-model.test.ts +2 -0
- package/src/daemon/handlers/conversations.ts +90 -3
- package/src/daemon/handlers/shared.ts +92 -29
- package/src/daemon/host-bash-proxy.ts +1 -1
- package/src/daemon/host-browser-proxy.ts +5 -5
- package/src/daemon/host-cu-proxy.ts +5 -5
- package/src/daemon/host-file-proxy.ts +5 -5
- package/src/daemon/host-proxy-base.ts +4 -4
- package/src/daemon/host-transfer-proxy.ts +11 -11
- package/src/daemon/lifecycle.ts +40 -23
- package/src/daemon/meet-manifest-loader.ts +1 -7
- package/src/daemon/message-protocol.ts +4 -0
- package/src/daemon/message-types/conversations.ts +14 -9
- package/src/daemon/message-types/document-comments.ts +50 -0
- package/src/daemon/message-types/home.ts +1 -13
- package/src/daemon/message-types/messages.ts +66 -7
- package/src/daemon/message-types/surfaces.ts +3 -1
- package/src/daemon/message-types/sync.ts +14 -0
- package/src/daemon/message-types/web-activity.ts +57 -0
- package/src/daemon/plugin-source-watcher.ts +135 -3
- package/src/daemon/process-message.ts +69 -12
- 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/daemon/trust-context.ts +6 -0
- package/src/documents/document-comments-store.test.ts +338 -0
- package/src/documents/document-comments-store.ts +237 -0
- package/src/documents/document-store.ts +202 -0
- package/src/events/relationship-state-updated.ts +25 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -2
- package/src/heartbeat/heartbeat-service.ts +1 -0
- package/src/home/__tests__/suggested-prompts.test.ts +33 -2
- package/src/home/feed-types.ts +6 -1
- package/src/home/home-content-refresh.ts +52 -0
- package/src/home/home-greeting-cache.ts +69 -0
- package/src/home/home-greeting.ts +85 -0
- package/src/home/suggested-prompts.ts +168 -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__/jobs-worker-v2-schedule.test.ts +135 -2
- package/src/memory/__tests__/memory-retrospective-job.test.ts +327 -6
- package/src/memory/auto-analysis-enqueue.ts +5 -1
- package/src/memory/conversation-crud.ts +191 -100
- 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 +26 -0
- package/src/memory/db-maintenance.ts +30 -21
- package/src/memory/delivery-crud.ts +41 -0
- package/src/memory/delivery-status.ts +141 -15
- package/src/memory/external-conversation-store.ts +32 -1
- 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 +68 -15
- 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-constants.ts +28 -0
- package/src/memory/memory-retrospective-enqueue.ts +11 -3
- package/src/memory/memory-retrospective-job.ts +413 -18
- package/src/memory/memory-retrospective-startup-cleanup.ts +3 -3
- package/src/memory/memory-v2-activation-log-store.ts +41 -14
- package/src/memory/migrations/100-core-tables.ts +1 -0
- package/src/memory/migrations/109-external-conversation-bindings.ts +1 -0
- package/src/memory/migrations/253-conversation-last-notified-profile.ts +15 -0
- package/src/memory/migrations/253-document-comments.ts +47 -0
- package/src/memory/migrations/254-external-conversation-binding-chat-name.ts +43 -0
- package/src/memory/migrations/255-channel-inbound-delivery-attempts.ts +24 -0
- package/src/memory/migrations/256-memory-v2-injection-events.ts +113 -0
- package/src/memory/migrations/257-strip-base-url-non-openai-compatible.ts +22 -0
- package/src/memory/migrations/258-onboarding-events-prior-assistants.ts +13 -0
- package/src/memory/migrations/259-conversation-cleaned-at.ts +33 -0
- 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 +34 -0
- package/src/memory/migrations/registry.ts +58 -0
- package/src/memory/onboarding-events-store.ts +7 -0
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/infrastructure.ts +22 -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-events.test.ts +318 -0
- package/src/memory/v2/__tests__/injection.test.ts +158 -112
- package/src/memory/v2/__tests__/page-index.test.ts +365 -1
- package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
- package/src/memory/v2/__tests__/router.test.ts +660 -4
- package/src/memory/v2/consolidation-job.ts +14 -0
- 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-events.ts +101 -0
- package/src/memory/v2/injection.ts +42 -25
- package/src/memory/v2/page-index.ts +209 -7
- package/src/memory/v2/page-store.ts +18 -0
- package/src/memory/v2/prompts/router.ts +26 -1
- package/src/memory/v2/qdrant.ts +14 -2
- package/src/memory/v2/router.ts +369 -62
- 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/messaging/providers/index.ts +7 -1
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +329 -3
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +34 -1
- package/src/messaging/providers/slack/adapter.ts +178 -25
- package/src/messaging/providers/slack/api.test.ts +54 -0
- package/src/messaging/providers/slack/api.ts +119 -3
- package/src/messaging/providers/slack/client.ts +12 -0
- package/src/messaging/providers/slack/deep-link.ts +20 -1
- package/src/messaging/providers/slack/message-metadata.test.ts +48 -0
- package/src/messaging/providers/slack/message-metadata.ts +156 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +107 -75
- package/src/messaging/providers/slack/render-transcript.ts +176 -49
- package/src/messaging/providers/slack/send.test.ts +77 -0
- package/src/messaging/providers/slack/send.ts +8 -2
- package/src/messaging/providers/slack/types.ts +14 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +4 -1
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +116 -54
- package/src/notifications/adapters/macos.ts +18 -1
- package/src/notifications/adapters/platform.ts +1 -1
- package/src/notifications/conversation-seed-composer.ts +14 -2
- package/src/notifications/decision-engine.ts +1 -4
- package/src/notifications/deferred-emit.ts +135 -0
- package/src/notifications/emit-signal.ts +38 -50
- package/src/notifications/home-feed-side-effect.ts +60 -30
- package/src/oauth/connect-orchestrator.ts +3 -0
- package/src/oauth/credential-token-resolver.ts +2 -0
- package/src/oauth/manual-token-connection.ts +19 -0
- package/src/oauth/oauth-store.ts +12 -0
- package/src/oauth/seed-providers.ts +22 -0
- package/src/permissions/prompter.ts +8 -5
- package/src/permissions/question-prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +6 -3
- 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 +100 -20
- 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__/system-prompt.test.ts +46 -2
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
- package/src/prompts/normalize-onboarding.ts +40 -0
- package/src/prompts/persona-resolver.ts +36 -21
- package/src/prompts/sections.ts +69 -19
- package/src/prompts/system-prompt.ts +118 -216
- package/src/prompts/template-detection.ts +37 -0
- package/src/prompts/templates/BOOTSTRAP-CONTENT-AUTOMATION.md +141 -0
- package/src/prompts/templates/BOOTSTRAP.md +10 -2
- package/src/prompts/templates/VOICE.md +3 -0
- package/src/prompts/templates/system-sections.ts +281 -9
- 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 +159 -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/fireworks/client.ts +20 -2
- package/src/providers/gemini/client.ts +49 -6
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +342 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +189 -0
- package/src/providers/inference/__tests__/codex-token-refresh.test.ts +254 -0
- package/src/providers/inference/adapter-factory.ts +18 -1
- package/src/providers/inference/auth.ts +3 -3
- package/src/providers/inference/codex-token-refresh.ts +128 -0
- package/src/providers/inference/resolve-auth.ts +49 -6
- package/src/providers/minimax/client.ts +106 -0
- package/src/providers/model-catalog.ts +91 -1
- package/src/providers/model-intents.ts +1 -1
- package/src/providers/openai/chat-completions-provider.ts +63 -23
- package/src/providers/openai/codex-models.ts +18 -0
- package/src/providers/openai/responses-provider.ts +86 -23
- package/src/providers/openrouter/client.ts +5 -1
- 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/types.ts +25 -0
- package/src/providers/usage-tracking.ts +2 -0
- package/src/runtime/AGENTS.md +2 -2
- package/src/runtime/__tests__/agent-wake.test.ts +214 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +128 -0
- package/src/runtime/agent-wake.ts +152 -56
- package/src/runtime/assistant-event-hub.ts +76 -6
- package/src/runtime/auth/route-policy.ts +43 -3
- package/src/runtime/background-job-runner.ts +26 -0
- package/src/runtime/btw-sidechain.ts +0 -6
- package/src/runtime/channel-reply-delivery.ts +182 -47
- package/src/runtime/channel-retry-sweep.ts +141 -16
- package/src/runtime/http-types.ts +7 -6
- package/src/runtime/migrations/vbundle-builder.ts +10 -3
- package/src/runtime/pending-interactions.ts +50 -8
- package/src/runtime/routes/__tests__/content-source-routes.test.ts +162 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +161 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +14 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +290 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -0
- package/src/runtime/routes/__tests__/sanity-routes.test.ts +280 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +266 -0
- package/src/runtime/routes/acp-routes.test.ts +255 -6
- package/src/runtime/routes/acp-routes.ts +8 -1
- package/src/runtime/routes/approval-routes.ts +4 -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/chatgpt-subscription-auth-routes.ts +246 -0
- package/src/runtime/routes/content-source-routes.ts +78 -0
- package/src/runtime/routes/conversation-cli-routes.ts +147 -2
- 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 +196 -31
- package/src/runtime/routes/conversation-routes.ts +472 -425
- package/src/runtime/routes/conversation-starter-routes.ts +6 -3
- package/src/runtime/routes/disk-pressure-routes.ts +1 -1
- package/src/runtime/routes/document-comments-routes.ts +287 -0
- package/src/runtime/routes/documents-routes.ts +33 -0
- 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/home-feed-routes.ts +6 -3
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-browser-routes.ts +17 -2
- package/src/runtime/routes/host-cu-routes.ts +2 -2
- package/src/runtime/routes/identity-routes.ts +21 -0
- package/src/runtime/routes/inbound-message-handler.ts +288 -58
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +96 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +365 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +283 -82
- package/src/runtime/routes/index.ts +20 -4
- 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/inference-provider-connection-routes.ts +63 -7
- package/src/runtime/routes/integrations/a2a.ts +60 -1
- package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
- package/src/runtime/routes/log-export-routes.ts +39 -0
- package/src/runtime/routes/memory-item-routes.ts +8 -3
- package/src/runtime/routes/memory-v2-routes.ts +427 -0
- package/src/runtime/routes/memory-v3-routes.ts +316 -0
- package/src/runtime/routes/migration-routes.ts +21 -24
- package/src/runtime/routes/notification-routes.ts +19 -2
- package/src/runtime/routes/plugins-routes.ts +337 -0
- package/src/runtime/routes/question-routes.ts +4 -1
- package/src/runtime/routes/rename-conversation-routes.ts +6 -2
- package/src/runtime/routes/sanity-routes.ts +159 -0
- 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 +188 -0
- package/src/runtime/routes/workspace-routes.ts +25 -10
- package/src/runtime/services/conversation-serializer.ts +30 -4
- 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/schedule/integration-status.ts +3 -1
- package/src/security/__tests__/oauth2-device-code.test.ts +479 -0
- package/src/security/oauth2-device-code.ts +307 -0
- package/src/security/oauth2.ts +26 -9
- package/src/security/secure-keys.ts +5 -0
- package/src/skills/catalog-install.ts +6 -2
- 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 +150 -0
- package/src/tools/browser/browser-execution.ts +106 -0
- package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +28 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +4 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +22 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +42 -2
- package/src/tools/browser/cdp-client/factory.ts +171 -4
- package/src/tools/browser/cdp-client/local-cdp-client.ts +21 -0
- package/src/tools/browser/cdp-client/types.ts +101 -0
- package/src/tools/browser/pinned-tabs.ts +146 -0
- 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-comment-tool.test.ts +379 -0
- package/src/tools/document/document-comment-tool.ts +156 -0
- package/src/tools/document/document-tool.ts +187 -2
- 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/__tests__/web-fetch-metadata.test.ts +229 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +346 -0
- package/src/tools/network/domain-normalize.ts +17 -0
- package/src/tools/network/web-fetch.ts +216 -73
- package/src/tools/network/web-search.ts +216 -98
- 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/safe-env.ts +3 -2
- package/src/tools/terminal/shell.ts +3 -9
- package/src/tools/tool-approval-handler.ts +19 -12
- package/src/tools/tool-defaults.ts +94 -0
- package/src/tools/types.ts +31 -98
- package/src/tools/ui-surface/definitions.ts +9 -23
- package/src/types/onboarding-context.ts +4 -0
- package/src/usage/pricing.ts +23 -0
- package/src/usage/types.ts +12 -0
- package/src/util/__tests__/favicon.test.ts +84 -0
- package/src/util/favicon.ts +40 -0
- package/src/util/logger.ts +16 -7
- package/src/util/platform.ts +7 -7
- package/src/util/sqlite3-runtime.ts +65 -0
- package/src/workspace/git-service.ts +75 -4
- package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
- package/src/workspace/migrations/088-deprecate-background-conversation-override.ts +103 -0
- package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
- package/src/workspace/migrations/registry.ts +4 -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/config/bundled-skills/document/SKILL.md +0 -54
- package/src/config/bundled-skills/document/TOOLS.json +0 -106
- package/src/daemon/seed-files.ts +0 -18
- package/src/prompts/cache-boundary.ts +0 -8
- package/src/runtime/routes/interface-routes.ts +0 -43
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-create.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-delete.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-list.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-read.ts +0 -0
- /package/src/config/bundled-skills/{document → document-editor}/tools/document-update.ts +0 -0
|
@@ -69,50 +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 IDENTITY.md / BOOTSTRAP.md content + the user persona from the
|
|
82
|
-
* dynamic block of the system prompt, stripping configuration, skills
|
|
83
|
-
* catalog, and connected services.
|
|
84
|
-
*
|
|
85
|
-
* SOUL.md no longer flows through this helper — it renders as the
|
|
86
|
-
* `09-soul` workspace-backed section in the static (cached) prefix.
|
|
87
|
-
* Tests that assert on SOUL.md content slice the static block directly.
|
|
88
|
-
*/
|
|
89
|
-
function basePrompt(result: string): string {
|
|
90
|
-
// The workspace files are in the dynamic block after the cache boundary.
|
|
91
|
-
const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
92
|
-
let s =
|
|
93
|
-
boundaryIdx >= 0
|
|
94
|
-
? result.slice(boundaryIdx + SYSTEM_PROMPT_CACHE_BOUNDARY.length)
|
|
95
|
-
: result;
|
|
96
|
-
for (const heading of [
|
|
97
|
-
"## Configuration",
|
|
98
|
-
"## Skills Catalog",
|
|
99
|
-
"## External Communications Identity",
|
|
100
|
-
"# Connected Services",
|
|
101
|
-
"## Dynamic Skill Authoring Workflow",
|
|
102
|
-
]) {
|
|
103
|
-
if (s.startsWith(heading)) {
|
|
104
|
-
s = "";
|
|
105
|
-
break;
|
|
106
|
-
}
|
|
107
|
-
const idx = s.indexOf(`\n\n${heading}`);
|
|
108
|
-
if (idx !== -1) s = s.slice(0, idx);
|
|
109
|
-
}
|
|
110
|
-
return s;
|
|
111
|
-
}
|
|
93
|
+
const { buildSystemPrompt, ensurePromptFiles, stripCommentLines } =
|
|
94
|
+
await import("../prompts/system-prompt.js");
|
|
112
95
|
|
|
113
96
|
describe("buildSystemPrompt", () => {
|
|
114
97
|
beforeEach(() => {
|
|
115
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;
|
|
116
103
|
});
|
|
117
104
|
|
|
118
105
|
afterEach(() => {
|
|
@@ -122,8 +109,10 @@ describe("buildSystemPrompt", () => {
|
|
|
122
109
|
"USER.md",
|
|
123
110
|
"BOOTSTRAP.md",
|
|
124
111
|
"UPDATES.md",
|
|
112
|
+
"VOICE.md",
|
|
125
113
|
"skills",
|
|
126
114
|
"users",
|
|
115
|
+
"channels",
|
|
127
116
|
]) {
|
|
128
117
|
const p = join(TEST_DIR, name);
|
|
129
118
|
if (existsSync(p)) rmSync(p, { recursive: true, force: true });
|
|
@@ -133,19 +122,11 @@ describe("buildSystemPrompt", () => {
|
|
|
133
122
|
}
|
|
134
123
|
});
|
|
135
124
|
|
|
136
|
-
test("returns empty string when no files exist", () => {
|
|
137
|
-
const result = buildSystemPrompt();
|
|
138
|
-
expect(basePrompt(result)).toBe("");
|
|
139
|
-
});
|
|
140
|
-
|
|
141
125
|
test("uses SOUL.md when it exists", () => {
|
|
142
126
|
writeFileSync(join(TEST_DIR, "SOUL.md"), "# My Soul\n\nBe awesome.");
|
|
143
127
|
const result = buildSystemPrompt();
|
|
144
|
-
// SOUL.md renders as the `09-soul` workspace-backed section
|
|
145
|
-
|
|
146
|
-
const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
147
|
-
expect(boundaryIdx).toBeGreaterThan(-1);
|
|
148
|
-
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.");
|
|
149
130
|
});
|
|
150
131
|
|
|
151
132
|
test("uses IDENTITY.md when it exists", () => {
|
|
@@ -154,30 +135,40 @@ describe("buildSystemPrompt", () => {
|
|
|
154
135
|
"# My Identity\n\nI am Vellum.",
|
|
155
136
|
);
|
|
156
137
|
const result = buildSystemPrompt();
|
|
157
|
-
|
|
138
|
+
// IDENTITY.md renders as the `08-identity` workspace-backed section.
|
|
139
|
+
expect(result).toContain("# My Identity\n\nI am Vellum.");
|
|
158
140
|
});
|
|
159
141
|
|
|
160
142
|
test("composes IDENTITY.md + SOUL.md when both exist", () => {
|
|
161
143
|
writeFileSync(join(TEST_DIR, "IDENTITY.md"), "# Identity\n\nI am Vellum.");
|
|
162
144
|
writeFileSync(join(TEST_DIR, "SOUL.md"), "# Soul\n\nBe thoughtful.");
|
|
163
145
|
const result = buildSystemPrompt();
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
|
|
167
|
-
expect(result
|
|
168
|
-
|
|
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.");
|
|
152
|
+
expect(identityIdx).toBeLessThan(soulIdx);
|
|
169
153
|
});
|
|
170
154
|
|
|
171
155
|
test("ignores empty SOUL.md", () => {
|
|
172
156
|
writeFileSync(join(TEST_DIR, "SOUL.md"), " \n \n ");
|
|
157
|
+
writeFileSync(join(TEST_DIR, "IDENTITY.md"), "# Identity\n\nI am Vellum.");
|
|
173
158
|
const result = buildSystemPrompt();
|
|
174
|
-
|
|
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 ");
|
|
175
163
|
});
|
|
176
164
|
|
|
177
165
|
test("ignores empty IDENTITY.md", () => {
|
|
178
166
|
writeFileSync(join(TEST_DIR, "IDENTITY.md"), "");
|
|
167
|
+
writeFileSync(join(TEST_DIR, "SOUL.md"), "# Soul\n\nBe thoughtful.");
|
|
179
168
|
const result = buildSystemPrompt();
|
|
180
|
-
|
|
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.");
|
|
181
172
|
});
|
|
182
173
|
|
|
183
174
|
test("trims whitespace from file content", () => {
|
|
@@ -185,10 +176,8 @@ describe("buildSystemPrompt", () => {
|
|
|
185
176
|
const result = buildSystemPrompt();
|
|
186
177
|
// SOUL.md renders via the `09-soul` workspace-backed section;
|
|
187
178
|
// stripCommentLines + trim run inside the section renderer.
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
expect(staticBlock).toContain("Be kind");
|
|
191
|
-
expect(staticBlock).not.toContain("\n Be kind \n");
|
|
179
|
+
expect(result).toContain("Be kind");
|
|
180
|
+
expect(result).not.toContain("\n Be kind \n");
|
|
192
181
|
});
|
|
193
182
|
|
|
194
183
|
test("does not include skills catalog in system prompt", () => {
|
|
@@ -217,10 +206,9 @@ describe("buildSystemPrompt", () => {
|
|
|
217
206
|
writeFileSync(join(TEST_DIR, "SOUL.md"), "Soul content");
|
|
218
207
|
|
|
219
208
|
const result = buildSystemPrompt();
|
|
220
|
-
//
|
|
221
|
-
//
|
|
222
|
-
//
|
|
223
|
-
// and the skills catalog is still suppressed.
|
|
209
|
+
// Both files render in the static prefix via `08-identity` /
|
|
210
|
+
// `09-soul`. Verify both are present and the skills catalog is
|
|
211
|
+
// still suppressed.
|
|
224
212
|
expect(result).toContain("Identity content");
|
|
225
213
|
expect(result).toContain("Soul content");
|
|
226
214
|
expect(result).not.toContain("## Available Skills");
|
|
@@ -262,11 +250,7 @@ describe("buildSystemPrompt", () => {
|
|
|
262
250
|
writeFileSync(join(TEST_DIR, "IDENTITY.md"), "Identity");
|
|
263
251
|
writeFileSync(join(TEST_DIR, "SOUL.md"), "Soul");
|
|
264
252
|
const result = buildSystemPrompt();
|
|
265
|
-
|
|
266
|
-
// section, so it doesn't flow through basePrompt. Assert that
|
|
267
|
-
// IDENTITY is the only dynamic content and that SOUL is still in the
|
|
268
|
-
// full prompt.
|
|
269
|
-
expect(basePrompt(result)).toBe("Identity");
|
|
253
|
+
expect(result).toContain("Identity");
|
|
270
254
|
expect(result).toContain("Soul");
|
|
271
255
|
});
|
|
272
256
|
|
|
@@ -280,55 +264,118 @@ describe("buildSystemPrompt", () => {
|
|
|
280
264
|
);
|
|
281
265
|
const result = buildSystemPrompt();
|
|
282
266
|
expect(result).not.toContain("stale user content");
|
|
283
|
-
expect(
|
|
267
|
+
expect(result).toContain("Identity");
|
|
284
268
|
});
|
|
285
269
|
|
|
286
|
-
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
|
+
);
|
|
287
280
|
writeFileSync(join(TEST_DIR, "IDENTITY.md"), "Identity");
|
|
288
281
|
writeFileSync(join(TEST_DIR, "SOUL.md"), "Soul");
|
|
289
|
-
const result = buildSystemPrompt(
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
// and is no longer part of the dynamic block sliced by basePrompt.
|
|
294
|
-
expect(basePrompt(result)).toBe(
|
|
295
|
-
"Identity\n\n# User persona\n\nName: Alice",
|
|
296
|
-
);
|
|
282
|
+
const result = buildSystemPrompt();
|
|
283
|
+
// IDENTITY, SOUL, and the user persona all render as workspace-backed
|
|
284
|
+
// bundled sections in the assembled prompt.
|
|
285
|
+
expect(result).toContain("Identity");
|
|
297
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");
|
|
298
342
|
});
|
|
299
343
|
|
|
300
344
|
describe("BOOTSTRAP.md user persona placeholder", () => {
|
|
301
|
-
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";
|
|
302
348
|
writeFileSync(
|
|
303
349
|
join(TEST_DIR, "BOOTSTRAP.md"),
|
|
304
|
-
"# First run\n\nSave facts to users/{{
|
|
350
|
+
"# First run\n\nSave facts to users/{{userSlug}}.md immediately.",
|
|
305
351
|
);
|
|
306
|
-
const result = buildSystemPrompt(
|
|
352
|
+
const result = buildSystemPrompt();
|
|
307
353
|
expect(result).toContain("users/alice.md");
|
|
308
|
-
expect(result).not.toContain("{{
|
|
354
|
+
expect(result).not.toContain("{{userSlug}}");
|
|
309
355
|
});
|
|
310
356
|
|
|
311
|
-
test("falls back to users/default.md when
|
|
357
|
+
test("falls back to users/default.md when no slug is resolvable", () => {
|
|
312
358
|
writeFileSync(
|
|
313
359
|
join(TEST_DIR, "BOOTSTRAP.md"),
|
|
314
|
-
"# First run\n\nSave facts to users/{{
|
|
360
|
+
"# First run\n\nSave facts to users/{{userSlug}}.md immediately.",
|
|
315
361
|
);
|
|
316
362
|
const result = buildSystemPrompt();
|
|
317
363
|
expect(result).toContain("users/default.md");
|
|
318
|
-
expect(result).not.toContain("{{
|
|
364
|
+
expect(result).not.toContain("{{userSlug}}");
|
|
319
365
|
});
|
|
320
366
|
|
|
321
367
|
test("substitutes the unmodified bundled BOOTSTRAP.md template", () => {
|
|
322
368
|
// Copy the real bundled BOOTSTRAP.md into the test workspace so we
|
|
323
369
|
// verify substitution against the actual template the daemon ships.
|
|
370
|
+
mockPersona.userSlug = "alice";
|
|
324
371
|
const bundled = readFileSync(
|
|
325
372
|
join(import.meta.dirname, "..", "prompts", "templates", "BOOTSTRAP.md"),
|
|
326
373
|
"utf-8",
|
|
327
374
|
);
|
|
328
375
|
writeFileSync(join(TEST_DIR, "BOOTSTRAP.md"), bundled);
|
|
329
|
-
const result = buildSystemPrompt(
|
|
376
|
+
const result = buildSystemPrompt();
|
|
330
377
|
expect(result).toContain("users/alice.md");
|
|
331
|
-
expect(result).not.toContain("{{
|
|
378
|
+
expect(result).not.toContain("{{userSlug}}");
|
|
332
379
|
});
|
|
333
380
|
});
|
|
334
381
|
|
|
@@ -480,7 +527,11 @@ describe("buildSystemPrompt", () => {
|
|
|
480
527
|
"# Identity\n_ This is a comment\nI am Vellum.\n_ Another comment",
|
|
481
528
|
);
|
|
482
529
|
const result = buildSystemPrompt();
|
|
483
|
-
|
|
530
|
+
// IDENTITY.md renders in the static prefix via the 08-identity section,
|
|
531
|
+
// so we assert against the full prompt rather than basePrompt.
|
|
532
|
+
expect(result).toContain("# Identity\nI am Vellum.");
|
|
533
|
+
expect(result).not.toContain("_ This is a comment");
|
|
534
|
+
expect(result).not.toContain("_ Another comment");
|
|
484
535
|
});
|
|
485
536
|
|
|
486
537
|
test("collapses whitespace around stripped comment lines", () => {
|
|
@@ -498,8 +549,14 @@ describe("buildSystemPrompt", () => {
|
|
|
498
549
|
|
|
499
550
|
test("file with only comment lines is treated as empty", () => {
|
|
500
551
|
writeFileSync(join(TEST_DIR, "SOUL.md"), "_ All comments\n_ Nothing else");
|
|
552
|
+
writeFileSync(join(TEST_DIR, "IDENTITY.md"), "# Identity\n\nI am Vellum.");
|
|
501
553
|
const result = buildSystemPrompt();
|
|
502
|
-
|
|
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");
|
|
503
560
|
});
|
|
504
561
|
|
|
505
562
|
describe("workspace system prompt sections", () => {
|
|
@@ -532,11 +589,7 @@ describe("buildSystemPrompt", () => {
|
|
|
532
589
|
);
|
|
533
590
|
const result = buildSystemPrompt();
|
|
534
591
|
expect(result.startsWith("You are operating in demo mode.")).toBe(true);
|
|
535
|
-
|
|
536
|
-
const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
537
|
-
expect(boundaryIdx).toBeGreaterThan(-1);
|
|
538
|
-
const staticBlock = result.slice(0, boundaryIdx);
|
|
539
|
-
expect(staticBlock).toContain("You are operating in demo mode.");
|
|
592
|
+
expect(result).toContain("You are operating in demo mode.");
|
|
540
593
|
});
|
|
541
594
|
|
|
542
595
|
test("workspace file without frontmatter is rendered as-is (always-on)", () => {
|
|
@@ -610,7 +663,12 @@ describe("buildSystemPrompt", () => {
|
|
|
610
663
|
writeFileSync(join(TEST_DIR, "IDENTITY.md"), "I am Vellum.");
|
|
611
664
|
const result = buildSystemPrompt();
|
|
612
665
|
expect(result.startsWith("Custom prefix")).toBe(true);
|
|
613
|
-
|
|
666
|
+
// IDENTITY.md renders via 08-identity in the static prefix after
|
|
667
|
+
// the 00-prefix slot.
|
|
668
|
+
const prefixIdx = result.indexOf("Custom prefix");
|
|
669
|
+
const identityIdx = result.indexOf("I am Vellum.");
|
|
670
|
+
expect(prefixIdx).toBeGreaterThan(-1);
|
|
671
|
+
expect(identityIdx).toBeGreaterThan(prefixIdx);
|
|
614
672
|
});
|
|
615
673
|
|
|
616
674
|
test("parallel tool calls section is sourced from workspace when present", () => {
|
|
@@ -786,11 +844,7 @@ describe("buildSystemPrompt", () => {
|
|
|
786
844
|
expect(result).toContain(
|
|
787
845
|
"Run `assistant --help` to discover commands.",
|
|
788
846
|
);
|
|
789
|
-
|
|
790
|
-
const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
791
|
-
expect(boundaryIdx).toBeGreaterThan(-1);
|
|
792
|
-
const staticBlock = result.slice(0, boundaryIdx);
|
|
793
|
-
expect(staticBlock).toContain("## Assistant CLI");
|
|
847
|
+
expect(result).toContain("## Assistant CLI");
|
|
794
848
|
});
|
|
795
849
|
|
|
796
850
|
test("bundled cli-reference default renders when no workspace override", () => {
|
|
@@ -831,11 +885,7 @@ describe("buildSystemPrompt", () => {
|
|
|
831
885
|
// The no-client body (em-dash separator after sandbox `bash`) must
|
|
832
886
|
// not leak when the with-client variant is active.
|
|
833
887
|
expect(result).not.toContain("install tools yourself; (2) browser");
|
|
834
|
-
|
|
835
|
-
const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
836
|
-
expect(boundaryIdx).toBeGreaterThan(-1);
|
|
837
|
-
const staticBlock = result.slice(0, boundaryIdx);
|
|
838
|
-
expect(staticBlock).toContain("## External Service Access");
|
|
888
|
+
expect(result).toContain("## External Service Access");
|
|
839
889
|
});
|
|
840
890
|
|
|
841
891
|
test("hasNoClient=true renders the two-tier (no host_bash) priority list", () => {
|
|
@@ -944,8 +994,7 @@ describe("buildSystemPrompt", () => {
|
|
|
944
994
|
|
|
945
995
|
test("paired {{#flag}} + {{^flag}} acts as if/else", () => {
|
|
946
996
|
// Use long unique markers — single letters collide with substrings
|
|
947
|
-
// in the rest of the system prompt (e.g. "
|
|
948
|
-
// SYSTEM_PROMPT_CACHE_BOUNDARY, "A" inside "API keys").
|
|
997
|
+
// in the rest of the system prompt (e.g. "A" inside "API keys").
|
|
949
998
|
mkdirSync(SYSTEM_PROMPTS_DIR, { recursive: true });
|
|
950
999
|
writeFileSync(
|
|
951
1000
|
SECTION_FILE,
|
|
@@ -1010,11 +1059,7 @@ describe("buildSystemPrompt", () => {
|
|
|
1010
1059
|
const result = buildSystemPrompt();
|
|
1011
1060
|
expect(result).toContain("## Sending Files to the User");
|
|
1012
1061
|
expect(result).toContain("Use the `<vellum-attachment />` tag.");
|
|
1013
|
-
|
|
1014
|
-
const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
1015
|
-
expect(boundaryIdx).toBeGreaterThan(-1);
|
|
1016
|
-
const staticBlock = result.slice(0, boundaryIdx);
|
|
1017
|
-
expect(staticBlock).toContain("## Sending Files to the User");
|
|
1062
|
+
expect(result).toContain("## Sending Files to the User");
|
|
1018
1063
|
});
|
|
1019
1064
|
|
|
1020
1065
|
test("renders after the cli-reference section to preserve original order", () => {
|
|
@@ -1060,11 +1105,7 @@ describe("buildSystemPrompt", () => {
|
|
|
1060
1105
|
const result = buildSystemPrompt();
|
|
1061
1106
|
expect(result).toContain("## Credential Security");
|
|
1062
1107
|
expect(result).toContain("Workspace override marker BRAVO_TANGO_7.");
|
|
1063
|
-
|
|
1064
|
-
const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
1065
|
-
expect(boundaryIdx).toBeGreaterThan(-1);
|
|
1066
|
-
const staticBlock = result.slice(0, boundaryIdx);
|
|
1067
|
-
expect(staticBlock).toContain("## Credential Security");
|
|
1108
|
+
expect(result).toContain("## Credential Security");
|
|
1068
1109
|
});
|
|
1069
1110
|
|
|
1070
1111
|
test("bundled credential-security default renders when no workspace override", () => {
|
|
@@ -1104,11 +1145,7 @@ describe("buildSystemPrompt", () => {
|
|
|
1104
1145
|
const result = buildSystemPrompt();
|
|
1105
1146
|
expect(result).toContain("## External Content");
|
|
1106
1147
|
expect(result).toContain("Workspace override marker NEBULA_9X.");
|
|
1107
|
-
|
|
1108
|
-
const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
1109
|
-
expect(boundaryIdx).toBeGreaterThan(-1);
|
|
1110
|
-
const staticBlock = result.slice(0, boundaryIdx);
|
|
1111
|
-
expect(staticBlock).toContain("## External Content");
|
|
1148
|
+
expect(result).toContain("## External Content");
|
|
1112
1149
|
});
|
|
1113
1150
|
|
|
1114
1151
|
test("bundled external-content default renders when no workspace override", () => {
|
|
@@ -67,6 +67,7 @@ mock.module("../tools/network/script-proxy/index.js", () => ({
|
|
|
67
67
|
import {
|
|
68
68
|
ALWAYS_INJECTED_ENV_VARS,
|
|
69
69
|
buildSanitizedEnv,
|
|
70
|
+
KATA_INJECTED_ENV_VARS,
|
|
70
71
|
KATA_SAFE_ENV_VARS,
|
|
71
72
|
SAFE_ENV_VARS,
|
|
72
73
|
} from "../tools/terminal/safe-env.js";
|
|
@@ -145,7 +146,7 @@ describe("buildSanitizedEnv", () => {
|
|
|
145
146
|
process.env.VELLUM_SANDBOX_RUNTIME = "gvisor";
|
|
146
147
|
process.env.PATH = "/usr/bin";
|
|
147
148
|
process.env.VELLUM_APT_DATA_ROOT = "/data/system";
|
|
148
|
-
process.env.LD_LIBRARY_PATH = "/
|
|
149
|
+
process.env.LD_LIBRARY_PATH = "/host/lib";
|
|
149
150
|
|
|
150
151
|
let env = buildSanitizedEnv();
|
|
151
152
|
expect(env.VELLUM_APT_DATA_ROOT).toBeUndefined();
|
|
@@ -161,6 +162,7 @@ describe("buildSanitizedEnv", () => {
|
|
|
161
162
|
expect(env.LD_LIBRARY_PATH.split(":")).toContain(
|
|
162
163
|
"/data/system/usr/local/lib",
|
|
163
164
|
);
|
|
165
|
+
expect(env.LD_LIBRARY_PATH.split(":")).not.toContain("/host/lib");
|
|
164
166
|
});
|
|
165
167
|
|
|
166
168
|
test("defaults LANG and LC_ALL to UTF-8 when unset", () => {
|
|
@@ -181,12 +183,20 @@ describe("buildSanitizedEnv", () => {
|
|
|
181
183
|
delete process.env.GATEWAY_PORT;
|
|
182
184
|
});
|
|
183
185
|
|
|
186
|
+
test("Kata entrypoint initializes apt root without inheriting apt loader paths", () => {
|
|
187
|
+
const entrypoint = readFileSync("docker-entrypoint.sh", "utf8");
|
|
188
|
+
|
|
189
|
+
expect(entrypoint).toContain("/app/assistant/docker-init-apt-root.sh");
|
|
190
|
+
expect(entrypoint).not.toContain(". /app/assistant/docker-kata-apt-env.sh");
|
|
191
|
+
});
|
|
192
|
+
|
|
184
193
|
test("result is a plain object with no prototype-inherited secrets", () => {
|
|
185
194
|
const env = buildSanitizedEnv();
|
|
186
195
|
const keys = Object.keys(env);
|
|
187
196
|
const safeKeys: string[] = [
|
|
188
197
|
...SAFE_ENV_VARS,
|
|
189
198
|
...KATA_SAFE_ENV_VARS,
|
|
199
|
+
...KATA_INJECTED_ENV_VARS,
|
|
190
200
|
...ALWAYS_INJECTED_ENV_VARS,
|
|
191
201
|
];
|
|
192
202
|
for (const key of keys) {
|
|
@@ -278,7 +288,7 @@ describe("Shell tool input validation", () => {
|
|
|
278
288
|
});
|
|
279
289
|
|
|
280
290
|
test("tool definition includes required schema fields", () => {
|
|
281
|
-
const def = shellTool
|
|
291
|
+
const def = shellTool;
|
|
282
292
|
const schema = def.input_schema as {
|
|
283
293
|
required: string[];
|
|
284
294
|
properties: Record<string, unknown>;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reproduction & regression tests for the thinking-block provider-switch bug.
|
|
3
|
+
*
|
|
4
|
+
* Phase 1 (real API): proves that replaying a thinking block with a tampered
|
|
5
|
+
* signature causes Anthropic to reject the request with a 400.
|
|
6
|
+
* Phase 2 (mocked): verifies the send-time filtering fix strips historical
|
|
7
|
+
* thinking blocks while preserving active tool-use continuation blocks.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { describe, expect, test } from "bun:test";
|
|
11
|
+
|
|
12
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
13
|
+
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Phase 1 — Real API reproduction
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
const apiKey = process.env.ANTHROPIC_API_KEY;
|
|
19
|
+
|
|
20
|
+
describe.skipIf(!apiKey)(
|
|
21
|
+
"Thinking block replay — real API reproduction",
|
|
22
|
+
() => {
|
|
23
|
+
test("Anthropic rejects a tampered thinking signature with 400", async () => {
|
|
24
|
+
const client = new Anthropic({ apiKey });
|
|
25
|
+
|
|
26
|
+
// Step 1: Get a real thinking block from the API
|
|
27
|
+
const initialResponse = await client.messages.create({
|
|
28
|
+
model: "claude-sonnet-4-6",
|
|
29
|
+
max_tokens: 4096,
|
|
30
|
+
thinking: { type: "enabled", budget_tokens: 1024 },
|
|
31
|
+
messages: [{ role: "user", content: "What is 2 + 2?" }],
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const thinkingBlock = initialResponse.content.find(
|
|
35
|
+
(b) => b.type === "thinking",
|
|
36
|
+
) as Anthropic.ThinkingBlock | undefined;
|
|
37
|
+
|
|
38
|
+
expect(thinkingBlock).toBeDefined();
|
|
39
|
+
expect(thinkingBlock!.signature).toBeTruthy();
|
|
40
|
+
|
|
41
|
+
// Step 2: Tamper with the signature to simulate a stale/cross-provider block
|
|
42
|
+
const tamperedSignature =
|
|
43
|
+
thinkingBlock!.signature.slice(0, -4) + "XXXX";
|
|
44
|
+
|
|
45
|
+
// Step 3: Replay the tampered block as historical context
|
|
46
|
+
const historicalAssistantContent: Anthropic.ContentBlockParam[] = [
|
|
47
|
+
{
|
|
48
|
+
type: "thinking",
|
|
49
|
+
thinking: thinkingBlock!.thinking,
|
|
50
|
+
signature: tamperedSignature,
|
|
51
|
+
},
|
|
52
|
+
{ type: "text", text: "4" },
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
// Step 4: Confirm Anthropic rejects with 400
|
|
56
|
+
try {
|
|
57
|
+
await client.messages.create({
|
|
58
|
+
model: "claude-sonnet-4-6",
|
|
59
|
+
max_tokens: 4096,
|
|
60
|
+
thinking: { type: "enabled", budget_tokens: 1024 },
|
|
61
|
+
messages: [
|
|
62
|
+
{ role: "user", content: "What is 2 + 2?" },
|
|
63
|
+
{ role: "assistant", content: historicalAssistantContent },
|
|
64
|
+
{ role: "user", content: "And what is 3 + 3?" },
|
|
65
|
+
],
|
|
66
|
+
});
|
|
67
|
+
expect.unreachable("API should have rejected the tampered signature");
|
|
68
|
+
} catch (err: unknown) {
|
|
69
|
+
const apiErr = err as { status?: number; message?: string };
|
|
70
|
+
expect(apiErr.status).toBe(400);
|
|
71
|
+
expect(apiErr.message).toContain("signature");
|
|
72
|
+
}
|
|
73
|
+
}, 30_000);
|
|
74
|
+
|
|
75
|
+
test("Anthropic accepts the request when thinking blocks are stripped from historical turns", async () => {
|
|
76
|
+
const client = new Anthropic({ apiKey });
|
|
77
|
+
|
|
78
|
+
// Step 1: Get a real thinking block
|
|
79
|
+
const initialResponse = await client.messages.create({
|
|
80
|
+
model: "claude-sonnet-4-6",
|
|
81
|
+
max_tokens: 4096,
|
|
82
|
+
thinking: { type: "enabled", budget_tokens: 1024 },
|
|
83
|
+
messages: [{ role: "user", content: "What is 2 + 2?" }],
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const thinkingBlock = initialResponse.content.find(
|
|
87
|
+
(b) => b.type === "thinking",
|
|
88
|
+
) as Anthropic.ThinkingBlock | undefined;
|
|
89
|
+
|
|
90
|
+
expect(thinkingBlock).toBeDefined();
|
|
91
|
+
|
|
92
|
+
// Step 2: Build history WITHOUT thinking blocks (the fix)
|
|
93
|
+
const cleanAssistantContent: Anthropic.ContentBlockParam[] = [
|
|
94
|
+
{ type: "text", text: "4" },
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
// Step 3: Confirm request succeeds
|
|
98
|
+
const followUp = await client.messages.create({
|
|
99
|
+
model: "claude-sonnet-4-6",
|
|
100
|
+
max_tokens: 4096,
|
|
101
|
+
thinking: { type: "enabled", budget_tokens: 1024 },
|
|
102
|
+
messages: [
|
|
103
|
+
{ role: "user", content: "What is 2 + 2?" },
|
|
104
|
+
{ role: "assistant", content: cleanAssistantContent },
|
|
105
|
+
{ role: "user", content: "And what is 3 + 3?" },
|
|
106
|
+
],
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
expect(followUp.content).toBeDefined();
|
|
110
|
+
expect(followUp.stop_reason).toBe("end_turn");
|
|
111
|
+
}, 30_000);
|
|
112
|
+
},
|
|
113
|
+
);
|