@vellumai/assistant 0.8.1 → 0.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +2 -7
- package/Dockerfile +75 -1
- package/bun.lock +11 -1
- package/docker-entrypoint.sh +5 -0
- package/docker-init-apt-root.sh +94 -0
- package/docker-kata-apt-env.sh +39 -0
- package/docs/plugins.md +88 -47
- package/docs/skills.md +9 -7
- package/examples/plugins/echo/README.md +27 -27
- package/examples/plugins/echo/package.json +3 -0
- package/examples/plugins/echo/register.ts +31 -31
- package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
- package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
- package/openapi.yaml +325 -3
- package/package.json +3 -1
- package/scripts/generate-openapi.ts +83 -10
- package/scripts/sync-llm-catalog.ts +2 -2
- package/scripts/sync-web-search-catalog.ts +47 -25
- package/src/__tests__/agent-image-optimize.test.ts +11 -3
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
- package/src/__tests__/anthropic-provider.test.ts +45 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
- package/src/__tests__/app-executors.test.ts +220 -4
- package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
- package/src/__tests__/bundled-asset.test.ts +6 -6
- package/src/__tests__/channel-availability-routes.test.ts +206 -0
- package/src/__tests__/channel-delivery-store.test.ts +289 -1
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
- package/src/__tests__/clawhub.test.ts +75 -16
- package/src/__tests__/compactor-tail-resolution.test.ts +41 -0
- package/src/__tests__/config-schema.test.ts +21 -0
- package/src/__tests__/config-set-route.test.ts +80 -0
- package/src/__tests__/config-sounds-sync.test.ts +97 -0
- package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
- package/src/__tests__/context-search-conversations-source.test.ts +117 -2
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
- package/src/__tests__/context-search-workspace-source.test.ts +7 -0
- package/src/__tests__/context-token-estimator.test.ts +1 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
- package/src/__tests__/conversation-agent-loop.test.ts +2 -0
- package/src/__tests__/conversation-error.test.ts +42 -3
- package/src/__tests__/conversation-fork-crud.test.ts +82 -0
- package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
- package/src/__tests__/conversation-lifecycle.test.ts +173 -0
- package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
- package/src/__tests__/conversation-pairing.test.ts +54 -0
- package/src/__tests__/conversation-process-callsite.test.ts +4 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
- package/src/__tests__/conversation-queue.test.ts +4 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +76 -9
- package/src/__tests__/conversation-slash-queue.test.ts +59 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
- package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
- package/src/__tests__/conversation-sync-tags.test.ts +235 -0
- 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-security-invariants.test.ts +3 -2
- package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
- package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
- package/src/__tests__/disk-pressure-tools.test.ts +1 -0
- package/src/__tests__/dm-backfill.test.ts +121 -10
- package/src/__tests__/document-tool-security.test.ts +258 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/edit-propagation.test.ts +33 -0
- package/src/__tests__/empty-response-pipeline.test.ts +0 -4
- package/src/__tests__/external-plugin-loader.test.ts +60 -36
- package/src/__tests__/filing-service.test.ts +140 -0
- package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
- package/src/__tests__/helpers/tar-fixtures.ts +39 -0
- package/src/__tests__/helpers/wait-for.ts +21 -0
- package/src/__tests__/history-repair-pipeline.test.ts +0 -3
- package/src/__tests__/history-repair.test.ts +73 -0
- package/src/__tests__/host-app-control-proxy.test.ts +266 -10
- package/src/__tests__/image-credentials.test.ts +1 -1
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
- package/src/__tests__/inference-profile-reaper.test.ts +4 -2
- package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
- package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
- package/src/__tests__/injector-chain.test.ts +10 -8
- package/src/__tests__/install-skill-routing.test.ts +155 -37
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +92 -3
- package/src/__tests__/list-messages-page-latest.test.ts +55 -0
- package/src/__tests__/llm-call-pipeline.test.ts +0 -3
- package/src/__tests__/llm-catalog-parity.test.ts +55 -13
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +34 -0
- package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
- package/src/__tests__/llm-usage-store.test.ts +114 -0
- package/src/__tests__/managed-profile-guard.test.ts +31 -29
- package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
- package/src/__tests__/managed-store.test.ts +84 -192
- package/src/__tests__/media-generate-image.test.ts +1 -1
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
- package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
- package/src/__tests__/oauth-commands-routes.test.ts +168 -16
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
- package/src/__tests__/openai-provider.test.ts +24 -0
- package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
- package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
- package/src/__tests__/persistence-pipeline.test.ts +0 -2
- package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +1 -1
- package/src/__tests__/platform.test.ts +2 -0
- package/src/__tests__/plugin-api-shim.test.ts +125 -0
- package/src/__tests__/plugin-bootstrap.test.ts +10 -36
- package/src/__tests__/plugin-external-api.test.ts +68 -0
- package/src/__tests__/plugin-registry.test.ts +0 -77
- package/src/__tests__/plugin-route-contribution.test.ts +0 -1
- package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
- package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
- package/src/__tests__/plugin-types.test.ts +3 -13
- package/src/__tests__/process-message-background-slack.test.ts +8 -1
- package/src/__tests__/process-message-display-content.test.ts +421 -0
- package/src/__tests__/provider-catalog-visibility.test.ts +142 -0
- package/src/__tests__/provider-error-scenarios.test.ts +111 -0
- package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +8 -8
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
- package/src/__tests__/schedule-routes.test.ts +50 -3
- package/src/__tests__/schedule-store.test.ts +94 -0
- package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
- package/src/__tests__/schema-transforms.test.ts +20 -0
- package/src/__tests__/search-skills-unified.test.ts +0 -5
- package/src/__tests__/server-history-render.test.ts +43 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
- package/src/__tests__/skill-load-tool.test.ts +27 -89
- package/src/__tests__/skill-memory.test.ts +23 -3
- package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
- package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
- package/src/__tests__/skills-install-extract.test.ts +49 -38
- package/src/__tests__/skills-install-staging.test.ts +159 -0
- package/src/__tests__/skills-uninstall.test.ts +9 -41
- package/src/__tests__/skills.test.ts +51 -58
- package/src/__tests__/slack-channel-config.test.ts +9 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
- package/src/__tests__/system-prompt.test.ts +737 -63
- package/src/__tests__/terminal-tools.test.ts +28 -1
- package/src/__tests__/thread-backfill.test.ts +557 -27
- package/src/__tests__/title-generate-pipeline.test.ts +0 -13
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
- package/src/__tests__/tool-error-pipeline.test.ts +0 -3
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +16 -4
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
- package/src/__tests__/turn-events-store.test.ts +256 -0
- package/src/__tests__/twilio-routes.test.ts +4 -0
- package/src/__tests__/user-plugin-loader.test.ts +0 -7
- package/src/__tests__/voice-session-bridge.test.ts +198 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
- package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
- package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
- package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
- package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
- package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
- package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
- package/src/acp/resolve-agent.ts +1 -1
- package/src/agent/image-optimize.ts +13 -5
- package/src/calls/voice-session-bridge.ts +61 -42
- package/src/channels/types.ts +108 -0
- package/src/cli/__tests__/unknown-command.test.ts +24 -0
- package/src/cli/commands/__tests__/changelog.test.ts +304 -319
- package/src/cli/commands/__tests__/schedules.test.ts +491 -0
- package/src/cli/commands/changelog.ts +106 -42
- package/src/cli/commands/conversations.ts +102 -17
- package/src/cli/commands/default-action.ts +10 -53
- package/src/cli/commands/notifications.ts +329 -317
- package/src/cli/commands/plugins.ts +185 -0
- package/src/cli/commands/schedules.ts +391 -0
- package/src/cli/commands/telemetry.ts +40 -0
- package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
- package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
- package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
- package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
- package/src/cli/lib/cli-colors.ts +12 -0
- package/src/cli/lib/confirm-prompt.ts +79 -0
- package/src/cli/lib/install-from-github.ts +304 -0
- package/src/cli/lib/list-installed-plugins.ts +137 -0
- package/src/cli/lib/uninstall-plugin.ts +82 -0
- package/src/cli/lib/unknown-command.ts +111 -0
- package/src/cli/program.ts +38 -2
- package/src/config/bundled-skills/app-builder/SKILL.md +23 -21
- package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
- package/src/config/bundled-skills/document/SKILL.md +23 -3
- package/src/config/bundled-skills/document/TOOLS.json +53 -0
- package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
- package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
- package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
- package/src/config/bundled-tool-registry.ts +6 -0
- package/src/config/feature-flag-registry.json +41 -1
- package/src/config/loader.ts +64 -38
- package/src/config/schema.ts +7 -10
- package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
- package/src/config/schemas/channels.ts +8 -0
- package/src/config/schemas/compaction.ts +28 -0
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm-request-logs.ts +31 -7
- package/src/config/schemas/llm.ts +3 -0
- package/src/config/schemas/memory-retrieval.ts +18 -0
- package/src/config/schemas/tools.ts +14 -0
- package/src/config/skills.ts +3 -96
- package/src/context/compactor.ts +1047 -0
- package/src/context/token-estimator.ts +2 -2
- package/src/context/window-manager.ts +197 -1520
- package/src/credential-execution/managed-catalog.ts +37 -0
- package/src/credential-health/credential-health-service.ts +280 -19
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +34 -0
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
- package/src/daemon/approval-generators.ts +8 -6
- package/src/daemon/config-watcher.ts +94 -31
- package/src/daemon/conversation-agent-loop.ts +169 -9
- package/src/daemon/conversation-error.ts +171 -37
- package/src/daemon/conversation-lifecycle.ts +53 -40
- package/src/daemon/conversation-messaging.ts +25 -6
- package/src/daemon/conversation-process.ts +49 -12
- package/src/daemon/conversation-runtime-assembly.ts +16 -1
- package/src/daemon/conversation-slash.ts +12 -5
- package/src/daemon/conversation-store.ts +11 -4
- package/src/daemon/conversation-tool-setup.ts +39 -7
- package/src/daemon/conversation.ts +33 -1
- package/src/daemon/external-plugins-bootstrap.ts +217 -181
- package/src/daemon/first-greeting.ts +22 -2
- package/src/daemon/handlers/config-model.ts +6 -5
- package/src/daemon/handlers/config-slack-channel.ts +15 -3
- package/src/daemon/handlers/shared.ts +14 -5
- package/src/daemon/handlers/skills.ts +111 -108
- package/src/daemon/history-repair.ts +28 -1
- package/src/daemon/host-app-control-proxy.ts +98 -23
- package/src/daemon/lifecycle.ts +45 -35
- package/src/daemon/meet-host-supervisor.ts +5 -4
- package/src/daemon/memory-v2-startup.ts +49 -0
- package/src/daemon/message-protocol.ts +1 -0
- package/src/daemon/message-types/conversations.ts +25 -0
- package/src/daemon/message-types/messages.ts +61 -0
- package/src/daemon/message-types/subagents.ts +1 -0
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/pkb-reminder-builder.test.ts +1 -1
- package/src/daemon/pkb-reminder-builder.ts +1 -1
- package/src/daemon/plugin-source-watcher.ts +146 -0
- package/src/daemon/process-message.ts +21 -3
- package/src/daemon/server.ts +11 -2
- package/src/daemon/skill-memory-refresh.ts +29 -0
- package/src/documents/document-store.ts +221 -3
- package/src/embedded/plugin-api.ts +40 -0
- package/src/filing/filing-service.ts +39 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +91 -6
- package/src/heartbeat/heartbeat-run-store.ts +2 -1
- package/src/heartbeat/heartbeat-service.ts +41 -0
- package/src/home/__tests__/feed-types.test.ts +40 -0
- package/src/home/feed-types.ts +22 -0
- package/src/home/post-connect-feed.ts +1 -0
- package/src/index.ts +18 -1
- package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
- package/src/mcp/client.ts +20 -4
- package/src/media/image-credentials.ts +3 -3
- package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
- package/src/memory/__tests__/conversation-queries.test.ts +263 -0
- package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
- package/src/memory/__tests__/message-content.test.ts +35 -0
- package/src/memory/bookmark-crud.ts +42 -10
- package/src/memory/context-search/sources/conversations.ts +62 -2
- package/src/memory/context-search/sources/workspace.ts +4 -0
- package/src/memory/conversation-crud.ts +63 -19
- package/src/memory/conversation-queries.ts +110 -10
- package/src/memory/db-init.ts +6 -0
- package/src/memory/delivery-crud.ts +152 -5
- package/src/memory/embedding-backend.ts +4 -4
- package/src/memory/external-conversation-store.ts +66 -5
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +66 -9
- package/src/memory/graph/conversation-graph-memory.ts +31 -15
- package/src/memory/graph/tools.ts +3 -3
- package/src/memory/indexer.ts +34 -29
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
- package/src/memory/jobs/embed-concept-page.ts +20 -11
- package/src/memory/jobs-worker.ts +6 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +17 -10
- package/src/memory/llm-request-log-source.ts +19 -52
- package/src/memory/llm-usage-store.ts +125 -5
- package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
- package/src/memory/message-content.ts +1 -1
- package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
- package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
- package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
- package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
- package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
- package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/onboarding-events-store.ts +106 -0
- package/src/memory/schema/bookmarks.ts +0 -2
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/inference.ts +1 -3
- package/src/memory/schema/infrastructure.ts +12 -0
- package/src/memory/turn-events-store.ts +127 -2
- package/src/memory/v2/__tests__/activation.test.ts +0 -8
- package/src/memory/v2/__tests__/injection.test.ts +98 -8
- package/src/memory/v2/__tests__/migration.test.ts +87 -0
- package/src/memory/v2/__tests__/page-index.test.ts +83 -0
- package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
- package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
- package/src/memory/v2/__tests__/router.test.ts +15 -0
- package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
- package/src/memory/v2/injection.ts +32 -6
- package/src/memory/v2/migration.ts +49 -19
- package/src/memory/v2/page-index.ts +35 -5
- package/src/memory/v2/prompts/router.ts +11 -8
- package/src/memory/v2/prompts/sweep.ts +2 -2
- package/src/memory/v2/qdrant.ts +135 -7
- package/src/memory/v2/router.ts +9 -8
- package/src/memory/v2/skill-store.ts +120 -35
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
- package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
- package/src/messaging/providers/slack/adapter.ts +43 -5
- package/src/messaging/providers/slack/client.ts +27 -0
- package/src/messaging/providers/slack/deep-link.ts +65 -0
- package/src/messaging/providers/slack/download.ts +104 -0
- package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
- package/src/messaging/providers/slack/message-metadata.ts +27 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
- package/src/messaging/providers/slack/render-transcript.ts +69 -5
- package/src/messaging/providers/slack/types.ts +20 -1
- package/src/notifications/conversation-pairing.ts +2 -1
- package/src/notifications/decision-engine.ts +2 -1
- package/src/notifications/emit-signal.ts +20 -1
- package/src/notifications/home-feed-side-effect.ts +54 -0
- package/src/notifications/signal.ts +3 -1
- package/src/oauth/connection-resolver.ts +8 -4
- package/src/oauth/platform-connection.ts +6 -2
- package/src/oauth/seed-providers.ts +10 -1
- package/src/permissions/checker.ts +2 -0
- package/src/permissions/ipc-risk-types.ts +1 -0
- package/src/permissions/question-prompter.test.ts +416 -0
- package/src/permissions/question-prompter.ts +294 -0
- package/src/platform/client.test.ts +1 -1
- package/src/platform/client.ts +1 -1
- package/src/plugin-api/constants.ts +26 -0
- package/src/plugin-api/index.ts +34 -1
- package/src/plugin-api/types.ts +104 -22
- package/src/plugins/defaults/circuit-breaker.ts +0 -5
- package/src/plugins/defaults/compaction.ts +0 -4
- package/src/plugins/defaults/empty-response.ts +0 -2
- package/src/plugins/defaults/history-repair.ts +0 -2
- package/src/plugins/defaults/injectors.ts +36 -3
- package/src/plugins/defaults/llm-call.ts +0 -2
- package/src/plugins/defaults/memory-retrieval.ts +0 -1
- package/src/plugins/defaults/overflow-reduce.ts +0 -1
- package/src/plugins/defaults/persistence.ts +0 -2
- package/src/plugins/defaults/title-generate.ts +0 -5
- package/src/plugins/defaults/token-estimate.ts +0 -2
- package/src/plugins/defaults/tool-error.ts +0 -7
- package/src/plugins/defaults/tool-execute.ts +0 -2
- package/src/plugins/defaults/tool-result-truncate.ts +0 -4
- package/src/plugins/ensure-plugin-api-shim.ts +96 -0
- package/src/plugins/external-api.ts +104 -0
- package/src/plugins/external-plugin-loader.ts +105 -32
- package/src/plugins/feature-gate.ts +22 -0
- package/src/plugins/pipeline.ts +37 -0
- package/src/plugins/registry.ts +48 -80
- package/src/plugins/types.ts +31 -26
- package/src/plugins/user-loader.ts +21 -2
- package/src/proactive-artifact/aux-message-injector.ts +11 -0
- package/src/proactive-artifact/job.test.ts +37 -5
- package/src/prompts/__tests__/system-prompt.test.ts +12 -0
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +99 -0
- package/src/prompts/normalize-onboarding.ts +27 -0
- package/src/prompts/sections.ts +302 -0
- package/src/prompts/system-prompt.ts +63 -166
- package/src/prompts/templates/BOOTSTRAP.md +17 -1
- package/src/prompts/templates/system-sections.ts +173 -0
- package/src/providers/__tests__/inference.test.ts +22 -7
- package/src/providers/anthropic/client.ts +28 -28
- package/src/providers/connection-resolution.ts +7 -0
- package/src/providers/inference/adapter-factory.ts +41 -4
- package/src/providers/inference/connections.ts +74 -29
- package/src/providers/inference/resolve-auth.ts +12 -4
- package/src/providers/model-catalog.ts +294 -12
- package/src/providers/openai/chat-completions-provider.ts +10 -2
- package/src/providers/openrouter/client.ts +7 -0
- package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -1
- package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
- package/src/providers/provider-availability.ts +17 -2
- package/src/providers/provider-catalog-visibility.ts +36 -0
- package/src/providers/registry.ts +22 -14
- package/src/providers/retry.ts +47 -1
- package/src/runtime/__tests__/agent-wake.test.ts +152 -0
- package/src/runtime/agent-wake.ts +42 -14
- package/src/runtime/auth/route-policy.ts +8 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/http-types.ts +19 -0
- package/src/runtime/migrations/origin-mode.ts +1 -1
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +107 -20
- package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
- package/src/runtime/routes/acp-routes-list.test.ts +143 -0
- package/src/runtime/routes/acp-routes.ts +5 -3
- package/src/runtime/routes/auth-routes.ts +1 -1
- package/src/runtime/routes/bookmark-routes.ts +5 -3
- package/src/runtime/routes/btw-routes.ts +5 -1
- package/src/runtime/routes/channel-availability-routes.ts +121 -0
- package/src/runtime/routes/conversation-cli-routes.ts +44 -3
- package/src/runtime/routes/conversation-list-routes.ts +3 -20
- package/src/runtime/routes/conversation-management-routes.ts +17 -42
- package/src/runtime/routes/conversation-query-routes.ts +40 -35
- package/src/runtime/routes/conversation-routes.ts +90 -11
- package/src/runtime/routes/documents-routes.ts +25 -86
- package/src/runtime/routes/group-routes.ts +5 -0
- package/src/runtime/routes/inbound-conversation.ts +28 -8
- package/src/runtime/routes/inbound-message-handler.ts +236 -41
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
- package/src/runtime/routes/index.ts +6 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
- package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
- package/src/runtime/routes/inference-provider-connection-routes.ts +65 -21
- package/src/runtime/routes/integrations/slack/share.ts +4 -52
- package/src/runtime/routes/integrations/slack/token.ts +43 -0
- package/src/runtime/routes/integrations/twilio.ts +6 -13
- package/src/runtime/routes/notification-routes.ts +1 -1
- package/src/runtime/routes/oauth-commands-routes.ts +105 -15
- package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
- package/src/runtime/routes/question-routes.ts +259 -0
- package/src/runtime/routes/rename-conversation-routes.ts +2 -33
- package/src/runtime/routes/schedule-routes.ts +4 -7
- package/src/runtime/routes/subagents-routes.ts +57 -18
- package/src/runtime/routes/telemetry-routes.ts +27 -0
- package/src/runtime/routes/tts-routes.ts +27 -2
- package/src/runtime/routes/workspace-routes.test.ts +43 -0
- package/src/runtime/routes/workspace-routes.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +39 -7
- package/src/runtime/sync/resource-sync-events.ts +93 -1
- package/src/schedule/schedule-store.ts +27 -2
- package/src/schedule/scheduler.ts +9 -1
- package/src/security/__tests__/untrusted-content.test.ts +86 -0
- package/src/security/untrusted-content.ts +93 -8
- package/src/skills/catalog-files.ts +1 -1
- package/src/skills/catalog-install.ts +233 -116
- package/src/skills/clawhub.ts +70 -13
- package/src/skills/managed-store.ts +4 -119
- package/src/skills/skillssh-registry.ts +27 -48
- package/src/subagent/manager.ts +15 -7
- package/src/telemetry/types.ts +113 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
- package/src/telemetry/usage-telemetry-reporter.ts +113 -7
- package/src/tools/apps/executors.ts +58 -7
- package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
- package/src/tools/ask-question/ask-question-tool.ts +304 -0
- package/src/tools/browser/browser-execution.ts +15 -11
- package/src/tools/computer-use/definitions.ts +3 -3
- package/src/tools/credentials/vault.ts +1 -1
- package/src/tools/document/document-tool.ts +124 -1
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +5 -2
- package/src/tools/host-filesystem/transfer.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +1 -1
- package/src/tools/permission-checker.ts +1 -1
- package/src/tools/registry.ts +17 -7
- package/src/tools/schedule/create.ts +2 -2
- package/src/tools/schema-transforms.ts +7 -2
- package/src/tools/side-effects.ts +1 -0
- package/src/tools/skills/delete-managed.ts +4 -4
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/scaffold-managed.ts +3 -2
- package/src/tools/subagent/notify-parent.ts +1 -1
- package/src/tools/system/request-permission.ts +2 -2
- package/src/tools/terminal/safe-env.ts +60 -1
- package/src/tools/tool-manifest.ts +2 -0
- package/src/tools/types.ts +72 -21
- package/src/tools/ui-surface/definitions.ts +6 -5
- package/src/tts/__tests__/provider-adapters.test.ts +76 -2
- package/src/tts/providers/elevenlabs-provider.ts +75 -1
- package/src/types/onboarding-context.ts +2 -0
- package/src/util/errors.ts +17 -0
- package/src/util/platform.ts +10 -0
- package/src/watcher/__tests__/engine.test.ts +22 -0
- package/src/watcher/engine.ts +6 -2
- package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
- package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
- package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
- package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
- package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
- package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
- package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/workspace/migrations/runner.ts +39 -9
- package/src/workspace/migrations/types.ts +4 -0
- package/examples/plugins/echo/bun.lock +0 -25
- package/src/__tests__/context-window-manager.test.ts +0 -2481
- package/src/context/__tests__/compact-prompt.test.ts +0 -63
- package/src/context/prompts/compact.md +0 -26
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
- /package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +0 -0
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
/**
|
|
3
|
-
* Generate
|
|
4
|
-
* `SEARCH_PROVIDER_CATALOG` in
|
|
3
|
+
* Generate the client-facing web-search-provider catalog JSON from the
|
|
4
|
+
* canonical `SEARCH_PROVIDER_CATALOG` in
|
|
5
5
|
* `assistant/src/providers/search-provider-catalog.ts`.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
7
|
+
* Two byte-identical copies are written:
|
|
8
|
+
* - `meta/web-search-provider-catalog.json` — primary checked-in artifact,
|
|
9
|
+
* consumed by:
|
|
10
|
+
* - `cli/src/__tests__/search-provider-env-var-parity.test.ts`
|
|
11
|
+
* (drift guard for the CLI's hardcoded env-var mirror).
|
|
12
|
+
* - Downstream `vellum-assistant-platform/web/src/lib/generated/
|
|
13
|
+
* web-search-provider-catalog.json` (manually sync'd today; the
|
|
14
|
+
* scheduled sync workflow is a planned follow-up).
|
|
15
|
+
* - `clients/shared/Resources/web-search-provider-catalog.json` — SwiftPM
|
|
16
|
+
* resource bundled into `VellumAssistantShared`. SwiftPM cannot reach
|
|
17
|
+
* files outside a target's source directory, so this mirror is
|
|
18
|
+
* necessary; both files are produced by the same generator and
|
|
19
|
+
* asserted equal by the parity test, making drift impossible.
|
|
9
20
|
*
|
|
10
|
-
*
|
|
11
|
-
* (drift guard for the CLI's hardcoded env-var mirror).
|
|
12
|
-
* - Downstream `vellum-assistant-platform/web/src/lib/generated/
|
|
13
|
-
* web-search-provider-catalog.json` (manually sync'd today; scheduled
|
|
14
|
-
* sync workflow is a planned follow-up).
|
|
21
|
+
* Companion to `sync-llm-catalog.ts`; same dual-write pattern.
|
|
15
22
|
*
|
|
16
23
|
* Usage:
|
|
17
24
|
* cd assistant && bun run scripts/sync-web-search-catalog.ts
|
|
@@ -20,7 +27,7 @@
|
|
|
20
27
|
*/
|
|
21
28
|
|
|
22
29
|
import { readFile, writeFile } from "node:fs/promises";
|
|
23
|
-
import { join, resolve } from "node:path";
|
|
30
|
+
import { join, relative, resolve } from "node:path";
|
|
24
31
|
|
|
25
32
|
import {
|
|
26
33
|
SEARCH_PROVIDER_CATALOG,
|
|
@@ -28,7 +35,10 @@ import {
|
|
|
28
35
|
} from "../src/providers/search-provider-catalog.js";
|
|
29
36
|
|
|
30
37
|
const ROOT = resolve(import.meta.dir, "../..");
|
|
31
|
-
const
|
|
38
|
+
const OUTPUT_PATHS = [
|
|
39
|
+
join(ROOT, "meta/web-search-provider-catalog.json"),
|
|
40
|
+
join(ROOT, "clients/shared/Resources/web-search-provider-catalog.json"),
|
|
41
|
+
] as const;
|
|
32
42
|
|
|
33
43
|
/**
|
|
34
44
|
* Bumped when the *shape* of the client catalog JSON changes in a way
|
|
@@ -84,24 +94,36 @@ async function main(): Promise<void> {
|
|
|
84
94
|
const next = generate();
|
|
85
95
|
|
|
86
96
|
if (checkMode) {
|
|
87
|
-
let
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
let anyStale = false;
|
|
98
|
+
for (const path of OUTPUT_PATHS) {
|
|
99
|
+
const rel = relative(ROOT, path);
|
|
100
|
+
let existing = "";
|
|
101
|
+
try {
|
|
102
|
+
existing = await readFile(path, "utf-8");
|
|
103
|
+
} catch {
|
|
104
|
+
console.error(
|
|
105
|
+
`${rel} does not exist. Run: bun run sync:web-search-catalog`,
|
|
106
|
+
);
|
|
107
|
+
anyStale = true;
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
110
|
+
if (existing !== next) {
|
|
111
|
+
console.error(
|
|
112
|
+
`${rel} is stale. Run: bun run sync:web-search-catalog`,
|
|
113
|
+
);
|
|
114
|
+
anyStale = true;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
console.log(`${rel} is up to date.`);
|
|
99
118
|
}
|
|
119
|
+
if (anyStale) process.exit(1);
|
|
100
120
|
return;
|
|
101
121
|
}
|
|
102
122
|
|
|
103
|
-
|
|
104
|
-
|
|
123
|
+
for (const path of OUTPUT_PATHS) {
|
|
124
|
+
await writeFile(path, next, "utf-8");
|
|
125
|
+
console.log(`Wrote ${relative(ROOT, path)}`);
|
|
126
|
+
}
|
|
105
127
|
}
|
|
106
128
|
|
|
107
129
|
await main();
|
|
@@ -14,10 +14,18 @@ describe("shouldRescaleImage", () => {
|
|
|
14
14
|
expect(shouldRescaleImage({ width: 1200, height: 800 }, 50_000)).toBe(
|
|
15
15
|
false,
|
|
16
16
|
);
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("rescales when raw bytes would inflate past Anthropic's 5 MB base64 cap", () => {
|
|
20
|
+
// Regression: an oversized image retained across compaction was rejected
|
|
21
|
+
// with "image.source.base64: image exceeds 5 MB maximum" even though
|
|
22
|
+
// dimensions were within the 1568 px cap. base64 inflates raw bytes by
|
|
23
|
+
// 4/3, so anything over ~3.5 MB raw risks crossing the 5 MB API limit.
|
|
19
24
|
expect(shouldRescaleImage({ width: 1568, height: 1568 }, 5_000_000)).toBe(
|
|
20
|
-
|
|
25
|
+
true,
|
|
26
|
+
);
|
|
27
|
+
expect(shouldRescaleImage({ width: 1200, height: 800 }, 4_000_000)).toBe(
|
|
28
|
+
true,
|
|
21
29
|
);
|
|
22
30
|
});
|
|
23
31
|
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Regression test: `classifyDiskPressureTurnPolicy` must receive the
|
|
3
|
+
* caller-supplied `callSite` from `wakeAgentForOpportunity`, not a
|
|
4
|
+
* hardcoded `"mainAgent"`.
|
|
5
|
+
*
|
|
6
|
+
* Today the disk-pressure classifier's `isBackgroundTurn` branches on
|
|
7
|
+
* `isDirectWake` before it consults `callSite`, so the hardcoded value
|
|
8
|
+
* did not produce a runtime regression. But the metadata recorded the
|
|
9
|
+
* wrong call site for any wake initiated by a background job (e.g.
|
|
10
|
+
* memory-v2 consolidation), and the inconsistency would bite the moment
|
|
11
|
+
* policy ever branches on `callSite` for wake turns. This test pins the
|
|
12
|
+
* forwarded value so the contract stays honest.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
16
|
+
|
|
17
|
+
import type {
|
|
18
|
+
DiskPressureTurnMetadata,
|
|
19
|
+
DiskPressureTurnPolicyDecision,
|
|
20
|
+
} from "../daemon/disk-pressure-policy.js";
|
|
21
|
+
import type { Message } from "../providers/types.js";
|
|
22
|
+
|
|
23
|
+
mock.module("../memory/conversation-crud.js", () => ({
|
|
24
|
+
getConversationOverrideProfile: () => undefined,
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
mock.module("../config/loader.js", () => ({
|
|
28
|
+
getConfig: () => ({ llm: {} }),
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
mock.module("../config/llm-context-resolution.js", () => ({
|
|
32
|
+
resolveEffectiveContextWindow: () => ({ maxInputTokens: 200_000 }),
|
|
33
|
+
}));
|
|
34
|
+
|
|
35
|
+
const classifyCalls: DiskPressureTurnMetadata[] = [];
|
|
36
|
+
mock.module("../daemon/disk-pressure-policy.js", () => ({
|
|
37
|
+
classifyDiskPressureTurnPolicy: (
|
|
38
|
+
_status: unknown,
|
|
39
|
+
metadata: DiskPressureTurnMetadata,
|
|
40
|
+
): DiskPressureTurnPolicyDecision => {
|
|
41
|
+
classifyCalls.push(metadata);
|
|
42
|
+
return { action: "allow-normal" };
|
|
43
|
+
},
|
|
44
|
+
}));
|
|
45
|
+
|
|
46
|
+
mock.module("../daemon/disk-pressure-guard.js", () => ({
|
|
47
|
+
getDiskPressureStatus: () => ({
|
|
48
|
+
enabled: false,
|
|
49
|
+
state: "disabled",
|
|
50
|
+
locked: false,
|
|
51
|
+
acknowledged: false,
|
|
52
|
+
overrideActive: false,
|
|
53
|
+
effectivelyLocked: false,
|
|
54
|
+
lockId: null,
|
|
55
|
+
usagePercent: null,
|
|
56
|
+
thresholdPercent: 95,
|
|
57
|
+
path: null,
|
|
58
|
+
lastCheckedAt: null,
|
|
59
|
+
blockedCapabilities: [],
|
|
60
|
+
error: null,
|
|
61
|
+
}),
|
|
62
|
+
}));
|
|
63
|
+
|
|
64
|
+
import {
|
|
65
|
+
__resetWakeChainForTests,
|
|
66
|
+
wakeAgentForOpportunity,
|
|
67
|
+
type WakeTarget,
|
|
68
|
+
} from "../runtime/agent-wake.js";
|
|
69
|
+
|
|
70
|
+
function makeTarget(): WakeTarget {
|
|
71
|
+
const history: Message[] = [];
|
|
72
|
+
let processing = false;
|
|
73
|
+
return {
|
|
74
|
+
conversationId: "conv-wake-callsite",
|
|
75
|
+
agentLoop: {
|
|
76
|
+
run: (async (messages: Message[]) =>
|
|
77
|
+
messages) as WakeTarget["agentLoop"]["run"],
|
|
78
|
+
},
|
|
79
|
+
getMessages: () => history,
|
|
80
|
+
pushMessage: (msg) => {
|
|
81
|
+
history.push(msg);
|
|
82
|
+
},
|
|
83
|
+
emitAgentEvent: () => {},
|
|
84
|
+
isProcessing: () => processing,
|
|
85
|
+
markProcessing: (on) => {
|
|
86
|
+
processing = on;
|
|
87
|
+
},
|
|
88
|
+
persistTailMessage: async () => {},
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
beforeEach(() => {
|
|
93
|
+
__resetWakeChainForTests();
|
|
94
|
+
classifyCalls.length = 0;
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe("wakeAgentForOpportunity — disk-pressure callSite forwarding", () => {
|
|
98
|
+
test("forwards opts.callSite to classifyDiskPressureTurnPolicy", async () => {
|
|
99
|
+
const target = makeTarget();
|
|
100
|
+
|
|
101
|
+
await wakeAgentForOpportunity(
|
|
102
|
+
{
|
|
103
|
+
conversationId: target.conversationId,
|
|
104
|
+
hint: "consolidate buffer",
|
|
105
|
+
source: "memory_v2_consolidation",
|
|
106
|
+
callSite: "memoryV2Consolidation",
|
|
107
|
+
},
|
|
108
|
+
{ resolveTarget: async () => target },
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
expect(classifyCalls).toHaveLength(1);
|
|
112
|
+
expect(classifyCalls[0]!.callSite).toBe("memoryV2Consolidation");
|
|
113
|
+
expect(classifyCalls[0]!.isDirectWake).toBe(true);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
test("defaults to mainAgent when opts.callSite is omitted", async () => {
|
|
117
|
+
const target = makeTarget();
|
|
118
|
+
|
|
119
|
+
await wakeAgentForOpportunity(
|
|
120
|
+
{
|
|
121
|
+
conversationId: target.conversationId,
|
|
122
|
+
hint: "resume",
|
|
123
|
+
source: "scheduler",
|
|
124
|
+
},
|
|
125
|
+
{ resolveTarget: async () => target },
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
expect(classifyCalls).toHaveLength(1);
|
|
129
|
+
expect(classifyCalls[0]!.callSite).toBe("mainAgent");
|
|
130
|
+
});
|
|
131
|
+
});
|
|
@@ -223,6 +223,22 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
223
223
|
expect(system[1].cache_control).toEqual({ type: "ephemeral", ttl: "1h" });
|
|
224
224
|
});
|
|
225
225
|
|
|
226
|
+
test("omits empty dynamic system block after cache boundary", async () => {
|
|
227
|
+
const staticBlock = "You are a helpful assistant.";
|
|
228
|
+
const prompt = staticBlock + SYSTEM_PROMPT_CACHE_BOUNDARY;
|
|
229
|
+
|
|
230
|
+
await provider.sendMessage([userMsg("Hi")], undefined, prompt);
|
|
231
|
+
|
|
232
|
+
const system = lastStreamParams!.system as Array<{
|
|
233
|
+
type: string;
|
|
234
|
+
text: string;
|
|
235
|
+
cache_control?: { type: string; ttl?: string };
|
|
236
|
+
}>;
|
|
237
|
+
expect(system).toHaveLength(1);
|
|
238
|
+
expect(system[0].text).toBe(staticBlock);
|
|
239
|
+
expect(system[0].cache_control).toEqual({ type: "ephemeral", ttl: "1h" });
|
|
240
|
+
});
|
|
241
|
+
|
|
226
242
|
test("drops static system block cache_control when total would exceed 4", async () => {
|
|
227
243
|
const staticBlock = "You are a helpful assistant.";
|
|
228
244
|
const dynamicBlock = "User workspace files here.";
|
|
@@ -2346,6 +2362,35 @@ describe("OpenRouterProvider — Anthropic dispatch", () => {
|
|
|
2346
2362
|
expect(lastStreamParams!.reasoning).toBeUndefined();
|
|
2347
2363
|
});
|
|
2348
2364
|
|
|
2365
|
+
test("sends OpenRouter app-attribution headers on Anthropic-compatible requests", async () => {
|
|
2366
|
+
const { OpenRouterProvider } =
|
|
2367
|
+
await import("../providers/openrouter/client.js");
|
|
2368
|
+
const provider = new OpenRouterProvider(
|
|
2369
|
+
"or-key",
|
|
2370
|
+
"anthropic/claude-sonnet-4.6",
|
|
2371
|
+
);
|
|
2372
|
+
await provider.sendMessage([userMsg("hi")], undefined, undefined, {
|
|
2373
|
+
config: {
|
|
2374
|
+
usageAttributionHeaders: {
|
|
2375
|
+
"Vellum-Organization-Id": "org-123",
|
|
2376
|
+
},
|
|
2377
|
+
},
|
|
2378
|
+
});
|
|
2379
|
+
|
|
2380
|
+
expect(_lastStreamOptions?.headers).toEqual(
|
|
2381
|
+
expect.objectContaining({
|
|
2382
|
+
"HTTP-Referer": "https://www.vellum.ai",
|
|
2383
|
+
"X-OpenRouter-Title": "Vellum Assistant",
|
|
2384
|
+
"X-OpenRouter-Categories": "personal-agent,cli-agent",
|
|
2385
|
+
"Vellum-Organization-Id": "org-123",
|
|
2386
|
+
}),
|
|
2387
|
+
);
|
|
2388
|
+
expect(lastStreamParams).not.toHaveProperty("HTTP-Referer");
|
|
2389
|
+
expect(lastStreamParams).not.toHaveProperty("X-OpenRouter-Title");
|
|
2390
|
+
expect(lastStreamParams).not.toHaveProperty("X-OpenRouter-Categories");
|
|
2391
|
+
expect(lastStreamParams).not.toHaveProperty("usageAttributionHeaders");
|
|
2392
|
+
});
|
|
2393
|
+
|
|
2349
2394
|
test("per-request model override routes based on the overridden model", async () => {
|
|
2350
2395
|
const { OpenRouterProvider } =
|
|
2351
2396
|
await import("../providers/openrouter/client.js");
|
|
@@ -22,15 +22,18 @@ function makeApp(overrides: Partial<AppDefinition> = {}): AppDefinition {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
function makeMockStore(overrides: Partial<AppStore> = {}): AppStore {
|
|
25
|
+
const files = new Set<string>();
|
|
25
26
|
return {
|
|
26
27
|
getApp: () => makeApp(),
|
|
27
28
|
listApps: () => [makeApp()],
|
|
28
|
-
appFileExists: () =>
|
|
29
|
+
appFileExists: (_appId: string, path: string) => files.has(path),
|
|
29
30
|
createApp: (params) =>
|
|
30
31
|
makeApp({ name: params.name, description: params.description }),
|
|
31
32
|
updateApp: (id, updates) => makeApp({ id, ...updates }),
|
|
32
33
|
deleteApp: () => {},
|
|
33
|
-
writeAppFile: () => {
|
|
34
|
+
writeAppFile: (_appId: string, path: string) => {
|
|
35
|
+
files.add(path);
|
|
36
|
+
},
|
|
34
37
|
...overrides,
|
|
35
38
|
};
|
|
36
39
|
}
|
|
@@ -107,7 +110,10 @@ describe("app-builder skill tool scripts", () => {
|
|
|
107
110
|
isError: false,
|
|
108
111
|
});
|
|
109
112
|
const result = await appCreateScript.run(
|
|
110
|
-
{
|
|
113
|
+
{
|
|
114
|
+
name: "Auto App",
|
|
115
|
+
source_files: { "src/main.tsx": "// real code" },
|
|
116
|
+
},
|
|
111
117
|
makeContext({ proxyToolResolver: proxy }),
|
|
112
118
|
);
|
|
113
119
|
expect(result.isError).toBe(false);
|
|
@@ -1,13 +1,31 @@
|
|
|
1
|
-
import { describe, expect, mock, test } from "bun:test";
|
|
1
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
type CompileResult = {
|
|
4
|
+
ok: boolean;
|
|
5
|
+
errors: string[];
|
|
6
|
+
warnings: string[];
|
|
7
|
+
durationMs: number;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
let compileResultOverride: CompileResult = {
|
|
11
|
+
ok: true,
|
|
12
|
+
errors: [],
|
|
13
|
+
warnings: [],
|
|
14
|
+
durationMs: 0,
|
|
15
|
+
};
|
|
2
16
|
|
|
3
17
|
mock.module("../bundler/app-compiler.js", () => ({
|
|
4
|
-
compileApp: async () =>
|
|
18
|
+
compileApp: async () => compileResultOverride,
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
compileResultOverride = {
|
|
5
23
|
ok: true,
|
|
6
24
|
errors: [],
|
|
7
25
|
warnings: [],
|
|
8
26
|
durationMs: 0,
|
|
9
|
-
}
|
|
10
|
-
})
|
|
27
|
+
};
|
|
28
|
+
});
|
|
11
29
|
|
|
12
30
|
import type { AppDefinition } from "../memory/app-store.js";
|
|
13
31
|
import type { AppStore } from "../tools/apps/executors.js";
|
|
@@ -102,6 +120,204 @@ describe("executeAppCreate", () => {
|
|
|
102
120
|
expect(files["src/main.tsx"]).toBeDefined();
|
|
103
121
|
expect(files["src/main.tsx"]).toContain("import { render } from 'preact'");
|
|
104
122
|
expect(files["src/main.tsx"]).toContain('{"Hello, New App!"}');
|
|
123
|
+
// next_steps directive must be present so the model keeps writing
|
|
124
|
+
// real source files instead of treating the scaffold as done.
|
|
125
|
+
const parsed = JSON.parse(result.content);
|
|
126
|
+
expect(parsed.next_steps).toContain("placeholder src/main.tsx");
|
|
127
|
+
expect(parsed.next_steps).toContain("app_refresh");
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("skips auto_open on scaffold even when proxy resolver is available", async () => {
|
|
131
|
+
const files: Record<string, string> = {};
|
|
132
|
+
const app = makeMultifileApp({ name: "New App" });
|
|
133
|
+
const store: AppStore = {
|
|
134
|
+
...mockStore(app, files),
|
|
135
|
+
createApp: () => app,
|
|
136
|
+
};
|
|
137
|
+
let proxyCalled = false;
|
|
138
|
+
const proxyResolver = async () => {
|
|
139
|
+
proxyCalled = true;
|
|
140
|
+
return { content: JSON.stringify({ opened: true }), isError: false };
|
|
141
|
+
};
|
|
142
|
+
|
|
143
|
+
const result = await executeAppCreate(
|
|
144
|
+
{ name: "New App" },
|
|
145
|
+
store,
|
|
146
|
+
proxyResolver,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
expect(result.isError).toBe(false);
|
|
150
|
+
expect(proxyCalled).toBe(false);
|
|
151
|
+
const parsed = JSON.parse(result.content);
|
|
152
|
+
expect(parsed.auto_opened).toBeUndefined();
|
|
153
|
+
expect(parsed.next_steps).toContain("placeholder src/main.tsx");
|
|
154
|
+
expect(parsed.next_steps).toContain("app_refresh");
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
test("omits next_steps when main.tsx was pre-written before app_create", async () => {
|
|
158
|
+
// The agent's supported workflow is to write the real source files first
|
|
159
|
+
// and then call app_create. In that case the placeholder directive would
|
|
160
|
+
// be false and risk triggering a destructive rewrite of correct code.
|
|
161
|
+
const files: Record<string, string> = {
|
|
162
|
+
"src/main.tsx": "// real code from the agent",
|
|
163
|
+
};
|
|
164
|
+
const app = makeMultifileApp({ name: "Pre-written App" });
|
|
165
|
+
const store: AppStore = {
|
|
166
|
+
...mockStore(app, files),
|
|
167
|
+
createApp: () => app,
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
const result = await executeAppCreate({ name: "Pre-written App" }, store);
|
|
171
|
+
|
|
172
|
+
expect(result.isError).toBe(false);
|
|
173
|
+
// The pre-written file must be preserved
|
|
174
|
+
expect(files["src/main.tsx"]).toBe("// real code from the agent");
|
|
175
|
+
const parsed = JSON.parse(result.content);
|
|
176
|
+
expect(parsed.next_steps).toBeUndefined();
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test("includes next_steps when compile fails on the scaffold", async () => {
|
|
180
|
+
compileResultOverride = {
|
|
181
|
+
ok: false,
|
|
182
|
+
errors: ["unexpected token"],
|
|
183
|
+
warnings: [],
|
|
184
|
+
durationMs: 3,
|
|
185
|
+
};
|
|
186
|
+
const files: Record<string, string> = {};
|
|
187
|
+
const app = makeMultifileApp({ name: "Broken App" });
|
|
188
|
+
const store: AppStore = {
|
|
189
|
+
...mockStore(app, files),
|
|
190
|
+
createApp: () => app,
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
const result = await executeAppCreate({ name: "Broken App" }, store);
|
|
194
|
+
|
|
195
|
+
expect(result.isError).toBe(false);
|
|
196
|
+
const parsed = JSON.parse(result.content);
|
|
197
|
+
expect(parsed.compile_errors).toEqual(["unexpected token"]);
|
|
198
|
+
expect(parsed.next_steps).toContain("placeholder src/main.tsx");
|
|
199
|
+
expect(parsed.next_steps).toContain("app_refresh");
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
test("omits next_steps when compile fails on pre-written files", async () => {
|
|
203
|
+
compileResultOverride = {
|
|
204
|
+
ok: false,
|
|
205
|
+
errors: ["unexpected token"],
|
|
206
|
+
warnings: [],
|
|
207
|
+
durationMs: 3,
|
|
208
|
+
};
|
|
209
|
+
const files: Record<string, string> = {
|
|
210
|
+
"src/main.tsx": "// agent's real (but broken) code",
|
|
211
|
+
};
|
|
212
|
+
const app = makeMultifileApp({ name: "Broken Pre-written App" });
|
|
213
|
+
const store: AppStore = {
|
|
214
|
+
...mockStore(app, files),
|
|
215
|
+
createApp: () => app,
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const result = await executeAppCreate(
|
|
219
|
+
{ name: "Broken Pre-written App" },
|
|
220
|
+
store,
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
expect(result.isError).toBe(false);
|
|
224
|
+
const parsed = JSON.parse(result.content);
|
|
225
|
+
expect(parsed.compile_errors).toEqual(["unexpected token"]);
|
|
226
|
+
expect(parsed.next_steps).toBeUndefined();
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test("suppresses auto_open when only scaffold exists", async () => {
|
|
230
|
+
const files: Record<string, string> = {};
|
|
231
|
+
const app = makeMultifileApp({ name: "New App" });
|
|
232
|
+
const store: AppStore = {
|
|
233
|
+
...mockStore(app, files),
|
|
234
|
+
createApp: () => app,
|
|
235
|
+
};
|
|
236
|
+
let proxyCalledWith: Record<string, unknown> | undefined;
|
|
237
|
+
const proxyResolver = async (
|
|
238
|
+
toolName: string,
|
|
239
|
+
input: Record<string, unknown>,
|
|
240
|
+
) => {
|
|
241
|
+
proxyCalledWith = { toolName, input };
|
|
242
|
+
return { content: JSON.stringify({ opened: true }), isError: false };
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const result = await executeAppCreate(
|
|
246
|
+
{ name: "New App" },
|
|
247
|
+
store,
|
|
248
|
+
proxyResolver,
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
expect(result.isError).toBe(false);
|
|
252
|
+
const parsed = JSON.parse(result.content);
|
|
253
|
+
expect(proxyCalledWith).toBeUndefined();
|
|
254
|
+
expect(parsed.auto_opened).toBeUndefined();
|
|
255
|
+
expect(parsed.next_steps).toContain("placeholder src/main.tsx");
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
test("fires auto_open when source_files includes main.tsx", async () => {
|
|
259
|
+
const files: Record<string, string> = {};
|
|
260
|
+
const app = makeMultifileApp({ name: "Real App" });
|
|
261
|
+
const store: AppStore = {
|
|
262
|
+
...mockStore(app, files),
|
|
263
|
+
createApp: () => app,
|
|
264
|
+
};
|
|
265
|
+
let proxyCalledWith: Record<string, unknown> | undefined;
|
|
266
|
+
const proxyResolver = async (
|
|
267
|
+
toolName: string,
|
|
268
|
+
input: Record<string, unknown>,
|
|
269
|
+
) => {
|
|
270
|
+
proxyCalledWith = { toolName, input };
|
|
271
|
+
return { content: JSON.stringify({ opened: true }), isError: false };
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const result = await executeAppCreate(
|
|
275
|
+
{
|
|
276
|
+
name: "Real App",
|
|
277
|
+
source_files: {
|
|
278
|
+
"src/main.tsx":
|
|
279
|
+
"import { render } from 'preact';\nrender(<div>Real</div>, document.getElementById('app')!);",
|
|
280
|
+
"src/styles.css": "body { margin: 0; }",
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
store,
|
|
284
|
+
proxyResolver,
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
expect(result.isError).toBe(false);
|
|
288
|
+
const parsed = JSON.parse(result.content);
|
|
289
|
+
expect(proxyCalledWith).toBeDefined();
|
|
290
|
+
expect(parsed.auto_opened).toBe(true);
|
|
291
|
+
expect(parsed.next_steps).toBeUndefined();
|
|
292
|
+
expect(files["src/main.tsx"]).toContain("Real");
|
|
293
|
+
expect(files["src/styles.css"]).toContain("margin");
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
test("source_files are written and prevent scaffold", async () => {
|
|
297
|
+
const files: Record<string, string> = {};
|
|
298
|
+
const app = makeMultifileApp({ name: "Inline App" });
|
|
299
|
+
const store: AppStore = {
|
|
300
|
+
...mockStore(app, files),
|
|
301
|
+
createApp: () => app,
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
const result = await executeAppCreate(
|
|
305
|
+
{
|
|
306
|
+
name: "Inline App",
|
|
307
|
+
source_files: {
|
|
308
|
+
"src/main.tsx": "// custom app code",
|
|
309
|
+
"src/components/App.tsx": "// App component",
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
store,
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
expect(result.isError).toBe(false);
|
|
316
|
+
expect(files["src/main.tsx"]).toBe("// custom app code");
|
|
317
|
+
expect(files["src/components/App.tsx"]).toBe("// App component");
|
|
318
|
+
expect(files["src/index.html"]).toBeDefined();
|
|
319
|
+
const parsed = JSON.parse(result.content);
|
|
320
|
+
expect(parsed.next_steps).toBeUndefined();
|
|
105
321
|
});
|
|
106
322
|
|
|
107
323
|
test("rejects retired html shortcut", async () => {
|
|
@@ -585,3 +585,38 @@ describe("indexer v1/v2 mutual exclusion for graph_extract", () => {
|
|
|
585
585
|
);
|
|
586
586
|
});
|
|
587
587
|
});
|
|
588
|
+
|
|
589
|
+
// ─────────────────────────────────────────────────────────────────
|
|
590
|
+
// Indexer v1/v2 mutual exclusion: build_conversation_summary feeds
|
|
591
|
+
// v1-only readers (fetchRecentSummaries, semantic search). When
|
|
592
|
+
// memory.v2.enabled is on, the summary writes are unread, so the
|
|
593
|
+
// indexer must not enqueue them.
|
|
594
|
+
// ─────────────────────────────────────────────────────────────────
|
|
595
|
+
|
|
596
|
+
describe("indexer v1/v2 mutual exclusion for build_conversation_summary", () => {
|
|
597
|
+
const originalV2Enabled = TEST_CONFIG.memory.v2.enabled;
|
|
598
|
+
|
|
599
|
+
afterEach(() => {
|
|
600
|
+
TEST_CONFIG.memory.v2.enabled = originalV2Enabled;
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
test("v2 active (config on) → build_conversation_summary not enqueued", async () => {
|
|
604
|
+
TEST_CONFIG.memory.v2.enabled = true;
|
|
605
|
+
|
|
606
|
+
const source = createConversation("summary-v2-active");
|
|
607
|
+
await indexMessages(source.id, 2);
|
|
608
|
+
|
|
609
|
+
expect(countJobsOfType("build_conversation_summary", source.id)).toBe(0);
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
test("config gate off → build_conversation_summary enqueued", async () => {
|
|
613
|
+
TEST_CONFIG.memory.v2.enabled = false;
|
|
614
|
+
|
|
615
|
+
const source = createConversation("summary-v2-config-off");
|
|
616
|
+
await indexMessages(source.id, 2);
|
|
617
|
+
|
|
618
|
+
expect(
|
|
619
|
+
countJobsOfType("build_conversation_summary", source.id),
|
|
620
|
+
).toBeGreaterThanOrEqual(1);
|
|
621
|
+
});
|
|
622
|
+
});
|
|
@@ -101,18 +101,18 @@ describe("resolveBundledDir", () => {
|
|
|
101
101
|
const macosDir = join(tempDir, "Contents", "MacOS");
|
|
102
102
|
const resourcesDir = join(tempDir, "Contents", "Resources");
|
|
103
103
|
mkdirSync(macosDir, { recursive: true });
|
|
104
|
-
mkdirSync(join(resourcesDir, "
|
|
104
|
+
mkdirSync(join(resourcesDir, "templates"), { recursive: true });
|
|
105
105
|
// Also create at execDir level
|
|
106
|
-
mkdirSync(join(macosDir, "
|
|
106
|
+
mkdirSync(join(macosDir, "templates"), { recursive: true });
|
|
107
107
|
|
|
108
108
|
process.execPath = join(macosDir, "vellum-daemon");
|
|
109
109
|
|
|
110
110
|
const result = resolveBundledDir(
|
|
111
|
-
"/$bunfs/root/src/
|
|
112
|
-
"
|
|
113
|
-
"
|
|
111
|
+
"/$bunfs/root/src/prompts",
|
|
112
|
+
"templates",
|
|
113
|
+
"templates",
|
|
114
114
|
);
|
|
115
|
-
expect(result).toBe(join(resourcesDir, "
|
|
115
|
+
expect(result).toBe(join(resourcesDir, "templates"));
|
|
116
116
|
});
|
|
117
117
|
|
|
118
118
|
test("works with different bundleName values", () => {
|