@vellumai/assistant 0.8.7 → 0.8.8-dev.202606052332.17fc8ea
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/Dockerfile +20 -4
- package/bun.lock +2 -2
- package/docker-entrypoint.sh +4 -2
- package/docker-init-apt-root.sh +3 -1
- package/docker-kata-apt-env.sh +3 -1
- package/docker-kata-runtime-family.sh +12 -0
- package/docs/architecture/memory.md +1 -1
- package/examples/plugins/echo/README.md +61 -66
- package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
- package/examples/plugins/echo/hooks/stop.ts +16 -0
- package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
- package/examples/plugins/echo/package.json +1 -2
- package/examples/plugins/echo/src/emit.ts +19 -0
- package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +3 -3
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
- package/openapi.yaml +3378 -335
- package/package.json +2 -2
- package/scripts/generate-openapi.ts +68 -41
- package/src/__tests__/agent-loop-exit-reason.test.ts +35 -93
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +1 -1
- package/src/__tests__/agent-loop.test.ts +37 -87
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
- package/src/__tests__/annotate-risk-options.test.ts +2 -3
- package/src/__tests__/anthropic-provider.test.ts +95 -2
- package/src/__tests__/app-control-flow.test.ts +1 -1
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +4 -1
- package/src/__tests__/assistant-event-hub.test.ts +25 -0
- package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
- package/src/__tests__/{conversation-stream-state.test.ts → assistant-stream-state.test.ts} +252 -91
- package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
- package/src/__tests__/btw-routes.test.ts +62 -3
- package/src/__tests__/build-persisted-content.test.ts +184 -0
- package/src/__tests__/catalog-files.test.ts +1 -1
- package/src/__tests__/channel-approval-routes.test.ts +1 -1
- package/src/__tests__/channel-approvals.test.ts +1 -1
- package/src/__tests__/clawhub-files.test.ts +1 -1
- package/src/__tests__/compaction-circuit.test.ts +258 -0
- package/src/__tests__/compaction-direct.test.ts +132 -0
- package/src/__tests__/compaction.benchmark.test.ts +0 -30
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +57 -19
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +6 -5
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -7
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +316 -1143
- package/src/__tests__/conversation-agent-loop.test.ts +638 -1655
- package/src/__tests__/conversation-analysis-routes.test.ts +6 -0
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-history-web-search.test.ts +11 -1
- package/src/__tests__/conversation-pairing.test.ts +4 -31
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +6 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -10
- package/src/__tests__/conversation-queue.test.ts +2 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +3 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +6 -5
- package/src/__tests__/conversation-runtime-assembly.test.ts +310 -300
- package/src/__tests__/conversation-runtime-workspace.test.ts +105 -45
- package/src/__tests__/conversation-slash-commands.test.ts +8 -42
- package/src/__tests__/conversation-slash-queue.test.ts +6 -1
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +84 -0
- package/src/__tests__/conversation-sync-tags.test.ts +27 -15
- package/src/__tests__/conversation-title-service.test.ts +135 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +17 -16
- package/src/__tests__/conversation-workspace-injection.test.ts +67 -2
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +7 -6
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/cross-provider-web-search.test.ts +214 -1
- package/src/__tests__/db-acp-history.test.ts +101 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +5 -0
- package/src/__tests__/dm-persistence.test.ts +5 -1
- package/src/__tests__/dynamic-page-surface.test.ts +31 -0
- package/src/__tests__/empty-response-hook.test.ts +304 -0
- package/src/__tests__/feature-flag-test-helpers.ts +2 -2
- package/src/__tests__/file-write-tool.test.ts +63 -0
- package/src/__tests__/gateway-only-guard.test.ts +12 -2
- package/src/__tests__/gemini-image-service.test.ts +13 -0
- package/src/__tests__/guardian-grant-minting.test.ts +1 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +1 -0
- package/src/__tests__/helpers/mock-provider.ts +110 -0
- package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
- package/src/__tests__/history-repair-hook.test.ts +1 -0
- package/src/__tests__/host-app-control-routes.test.ts +1 -1
- package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
- package/src/__tests__/identity-intro-cache.test.ts +12 -100
- package/src/__tests__/identity-routes.test.ts +248 -7
- package/src/__tests__/inbound-slack-persistence.test.ts +5 -1
- package/src/__tests__/injector-background-turn.test.ts +3 -9
- package/src/__tests__/injector-chain.test.ts +139 -275
- package/src/__tests__/injector-disk-pressure.test.ts +75 -41
- package/src/__tests__/injector-document-comments.test.ts +3 -3
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
- package/src/__tests__/injector-v3-suppression.test.ts +31 -37
- package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
- package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
- package/src/__tests__/list-messages-page-latest.test.ts +60 -0
- package/src/__tests__/list-messages-tool-merge.test.ts +20 -0
- package/src/__tests__/llm-usage-store.test.ts +223 -1
- package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
- package/src/__tests__/native-web-search.test.ts +191 -0
- package/src/__tests__/onboarding-template-contract.test.ts +2 -0
- package/src/__tests__/openai-image-service.test.ts +17 -0
- package/src/__tests__/openai-provider.test.ts +31 -1
- package/src/__tests__/{overflow-reduce-pipeline.test.ts → overflow-reduction-loop.test.ts} +64 -284
- package/src/__tests__/persist-unsendable-image.test.ts +215 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +1 -0
- package/src/__tests__/pkb-autoinject.test.ts +2 -5
- package/src/__tests__/plugin-api-shim.test.ts +3 -6
- package/src/__tests__/plugin-bootstrap.test.ts +14 -40
- package/src/__tests__/plugin-registry.test.ts +3 -76
- package/src/__tests__/plugin-types.test.ts +0 -193
- package/src/__tests__/process-message-display-content.test.ts +6 -2
- package/src/__tests__/reaction-persistence.test.ts +1 -1
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
- package/src/__tests__/resolve-trust-class.test.ts +4 -4
- package/src/__tests__/runtime-events-sse-reconnect.test.ts +60 -23
- package/src/__tests__/schedule-routes.test.ts +603 -2
- package/src/__tests__/schedule-store.test.ts +41 -0
- package/src/__tests__/schedule-tools.test.ts +35 -0
- package/src/__tests__/send-endpoint-busy.test.ts +4 -1
- package/src/__tests__/server-history-render.test.ts +314 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +33 -0
- package/src/__tests__/skillssh-files.test.ts +1 -1
- package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
- package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
- package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
- package/src/__tests__/subagent-manager-notify.test.ts +1 -3
- package/src/__tests__/subagent-notify-parent.test.ts +1 -3
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
- package/src/__tests__/system-prompt.test.ts +20 -0
- package/src/__tests__/task-scheduler.test.ts +162 -1
- package/src/__tests__/terminal-tools.test.ts +6 -1
- package/src/__tests__/title-generate-hook.test.ts +319 -0
- package/src/__tests__/tool-error-hook.test.ts +278 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +468 -5
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -2
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
- package/src/__tests__/ui-work-result-surface.test.ts +159 -0
- package/src/__tests__/usage-routes.test.ts +285 -1
- package/src/__tests__/user-plugin-loader.test.ts +54 -286
- package/src/__tests__/voice-session-bridge.test.ts +6 -3
- package/src/__tests__/web-search-backend-failure.test.ts +166 -0
- package/src/acp/__tests__/agent-process.test.ts +161 -0
- package/src/acp/__tests__/client-handler.test.ts +40 -0
- package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
- package/src/acp/__tests__/helpers/exec-file-stub.ts +101 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +137 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
- package/src/acp/__tests__/session-manager-resume.test.ts +736 -0
- package/src/acp/agent-process.ts +61 -1
- package/src/acp/auto-install.test.ts +196 -0
- package/src/acp/auto-install.ts +177 -0
- package/src/acp/client-handler.ts +31 -0
- package/src/acp/feature-gate.test.ts +48 -0
- package/src/acp/feature-gate.ts +34 -0
- package/src/acp/prepare-agent-env.ts +83 -29
- package/src/acp/resolve-agent.test.ts +320 -7
- package/src/acp/resolve-agent.ts +182 -18
- package/src/acp/resume-hint.ts +25 -0
- package/src/acp/session-manager.ts +495 -73
- package/src/acp/types.ts +8 -0
- package/src/agent/compaction-circuit.ts +60 -102
- package/src/agent/loop.ts +362 -485
- package/src/api/events/assistant-thinking-delta.ts +33 -0
- package/src/api/events/tool-output-chunk.ts +45 -0
- package/src/api/events/tool-use-preview-start.ts +32 -0
- package/src/api/events/trace-event.ts +69 -0
- package/src/api/index.ts +48 -13
- package/src/api/responses/conversation-message.ts +374 -0
- package/src/approvals/guardian-request-resolvers.ts +1 -1
- package/src/avatar/__tests__/avatar-store.test.ts +34 -29
- package/src/background-wake/next-wake.ts +1 -0
- package/src/cli/commands/__tests__/notifications.test.ts +58 -14
- package/src/cli/commands/notifications.ts +112 -60
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
- package/src/config/acp-defaults.test.ts +10 -0
- package/src/config/acp-defaults.ts +6 -0
- package/src/config/assistant-feature-flags.ts +22 -11
- package/src/config/bundled-skills/acp/SKILL.md +83 -31
- package/src/config/bundled-skills/acp/TOOLS.json +4 -4
- package/src/config/bundled-skills/app-builder/SKILL.md +224 -398
- package/src/config/bundled-skills/app-builder/TOOLS.json +29 -0
- package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
- package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
- package/src/config/bundled-skills/app-builder/references/examples/README.md +17 -0
- package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
- package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
- package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
- package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +0 -7
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/feature-flag-cache.ts +3 -3
- package/src/config/feature-flag-registry.json +48 -7
- package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
- package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/memory-v2.ts +8 -0
- package/src/config/schemas/memory-v3.ts +8 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/seed-inference-profiles.ts +2 -2
- package/src/config/skills.ts +13 -0
- package/src/context/compactor.ts +1 -1
- package/src/context/strip-injections.ts +128 -0
- package/src/context/token-estimator.ts +23 -0
- package/src/context/tool-result-truncation.ts +0 -23
- package/src/context/window-manager.ts +5 -7
- package/src/credential-execution/executable-discovery.ts +16 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
- package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
- package/src/daemon/assistant-attachments.ts +1 -1
- package/src/daemon/config-watcher.ts +2 -2
- package/src/daemon/context-overflow-reducer.ts +0 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +594 -153
- package/src/daemon/conversation-agent-loop.ts +301 -997
- package/src/daemon/conversation-history.ts +5 -4
- package/src/daemon/conversation-lifecycle.ts +3 -4
- package/src/daemon/conversation-messaging.ts +7 -6
- package/src/daemon/conversation-process.ts +11 -16
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +218 -398
- package/src/daemon/conversation-slash.ts +6 -25
- package/src/daemon/conversation-store.ts +9 -90
- package/src/daemon/conversation-surfaces.ts +222 -4
- package/src/daemon/conversation-tool-setup.ts +2 -29
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +32 -20
- package/src/daemon/external-plugins-bootstrap.ts +17 -18
- package/src/daemon/handlers/config-a2a.ts +51 -36
- package/src/daemon/handlers/config-slack-channel.ts +20 -14
- package/src/daemon/handlers/config-telegram.ts +16 -2
- package/src/daemon/handlers/conversations.ts +3 -1
- package/src/daemon/handlers/shared.ts +156 -84
- package/src/daemon/handlers/skills.ts +42 -10
- package/src/daemon/lifecycle.ts +25 -0
- package/src/daemon/message-types/apps.ts +1 -29
- package/src/daemon/message-types/messages.ts +9 -57
- package/src/daemon/message-types/skills.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +136 -3
- package/src/daemon/now-scratchpad.ts +21 -0
- package/src/daemon/orphan-reaper.test.ts +210 -0
- package/src/daemon/orphan-reaper.ts +240 -0
- package/src/daemon/overflow-reduction-loop.ts +230 -0
- package/src/daemon/persist-unsendable-image.ts +117 -0
- package/src/daemon/process-message.ts +1 -3
- package/src/daemon/server.ts +2 -0
- package/src/daemon/trace-emitter.ts +6 -4
- package/src/daemon/trust-context.ts +19 -0
- package/src/daemon/wake-target-adapter.ts +3 -1
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
- package/src/heartbeat/heartbeat-run-store.ts +23 -1
- package/src/heartbeat/heartbeat-service.ts +26 -0
- package/src/home/home-greeting-cache.ts +24 -1
- package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
- package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
- package/src/ipc/gateway-client.test.ts +2 -2
- package/src/ipc/gateway-client.ts +3 -3
- package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
- package/src/ipc/skill-routes/memory.ts +4 -2
- package/src/media/gemini-image-service.ts +15 -0
- package/src/media/openai-image-service.ts +14 -0
- package/src/media/types.ts +34 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +56 -0
- package/src/memory/auth-fallback-events-store.ts +94 -0
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/conversation-title-service.ts +65 -41
- package/src/memory/db-init.ts +6 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
- package/src/memory/graph/conversation-graph-memory.ts +65 -0
- package/src/memory/job-handlers/conversation-starters.ts +13 -2
- package/src/memory/jobs-store.ts +33 -0
- package/src/memory/jobs-worker.ts +32 -5
- package/src/memory/llm-usage-store.ts +224 -50
- package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
- package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
- package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
- package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/pkb/autoinject.ts +61 -0
- package/src/memory/pkb/context.ts +50 -0
- package/src/memory/pkb/types.ts +14 -0
- package/src/memory/schedule-attribution-sql.ts +104 -0
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/schema/infrastructure.ts +16 -0
- package/src/memory/usage-grouped-buckets.ts +6 -1
- package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -4
- package/src/memory/v2/consolidation-job.ts +14 -5
- package/src/notifications/conversation-pairing.ts +8 -15
- package/src/notifications/decision-engine.ts +6 -3
- package/src/notifications/home-feed-side-effect.ts +12 -1
- package/src/permissions/prompter.ts +4 -0
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +7 -5
- package/src/plugin-api/types.ts +151 -1
- package/src/plugins/defaults/compaction/compact.ts +59 -0
- package/src/plugins/defaults/compaction/package.json +1 -1
- package/src/plugins/defaults/compaction/register.ts +8 -19
- package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
- package/src/plugins/defaults/empty-response/register.ts +8 -13
- package/src/plugins/defaults/index.ts +2 -18
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +95 -0
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
- package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +288 -81
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/assign.test.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/health.test.ts +16 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/maintain-job.test.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/orchestrate.test.ts +48 -12
- package/src/plugins/defaults/memory-v3-shadow/__tests__/provider-blocks.test.ts +13 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/reconcile.test.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/render-injection.test.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/router.test.ts +104 -32
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +8 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selector.test.ts +96 -30
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +34 -16
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/health.ts +0 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +75 -0
- package/src/plugins/defaults/memory-v3-shadow/llm-retry.ts +32 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +8 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +26 -14
- package/src/plugins/defaults/{llm-call → memory-v3-shadow}/package.json +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/provider-blocks.ts +26 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +3 -3
- package/src/plugins/defaults/memory-v3-shadow/register.ts +26 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/router.ts +51 -45
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selection-log-store.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selector.ts +61 -46
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +69 -99
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +8 -0
- package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
- package/src/plugins/defaults/title-generate/package.json +1 -1
- package/src/plugins/defaults/title-generate/register.ts +18 -18
- package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
- package/src/plugins/defaults/tool-error/package.json +1 -1
- package/src/plugins/defaults/tool-error/register.ts +9 -21
- package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
- package/src/plugins/defaults/tool-result-truncate/register.ts +10 -21
- package/src/plugins/defaults/tool-result-truncate/terminal.ts +37 -18
- package/src/plugins/external-api.ts +2 -2
- package/src/plugins/pipeline.ts +6 -305
- package/src/plugins/registry.ts +10 -55
- package/src/plugins/types.ts +62 -797
- package/src/plugins/user-loader.ts +30 -127
- package/src/proactive-artifact/aux-message-injector.ts +4 -4
- package/src/proactive-artifact/job.test.ts +8 -13
- package/src/prompts/__tests__/system-prompt.test.ts +42 -0
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +64 -0
- package/src/prompts/templates/BOOTSTRAP.md +2 -2
- package/src/prompts/templates/system-sections.ts +15 -0
- package/src/providers/anthropic/client.ts +37 -29
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -0
- package/src/providers/openai/chat-completions-provider.ts +44 -0
- package/src/providers/openrouter/client.ts +1 -0
- package/src/providers/placeholder-sentinels.ts +35 -0
- package/src/runtime/__tests__/agent-wake.test.ts +10 -6
- package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
- package/src/runtime/agent-wake.ts +2 -5
- package/src/runtime/assistant-event-hub.ts +37 -7
- package/src/runtime/{conversation-stream-state.ts → assistant-stream-state.ts} +132 -58
- package/src/runtime/channel-approvals.ts +1 -1
- package/src/runtime/http-router.ts +16 -21
- package/src/runtime/http-types.ts +16 -70
- package/src/runtime/interactive-ui.ts +1 -1
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/routes/__tests__/acp-routes.test.ts +283 -55
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
- package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
- package/src/runtime/routes/acp-routes.test.ts +89 -25
- package/src/runtime/routes/acp-routes.ts +81 -29
- package/src/runtime/routes/app-management-routes.ts +6 -117
- package/src/runtime/routes/app-routes.ts +13 -15
- package/src/runtime/routes/approval-routes.ts +1 -1
- package/src/runtime/routes/attachment-routes.ts +26 -15
- package/src/runtime/routes/avatar-routes.ts +26 -0
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/browser-tabs-routes.ts +6 -10
- package/src/runtime/routes/btw-routes.ts +29 -23
- package/src/runtime/routes/consolidation-routes.ts +120 -20
- package/src/runtime/routes/conversation-cli-routes.ts +1 -1
- package/src/runtime/routes/conversation-list-routes.ts +1 -1
- package/src/runtime/routes/conversation-query-routes.ts +3 -1
- package/src/runtime/routes/conversation-routes.ts +372 -185
- package/src/runtime/routes/conversation-starter-routes.ts +13 -7
- package/src/runtime/routes/conversations-import-routes.ts +24 -7
- package/src/runtime/routes/documents-routes.ts +4 -0
- package/src/runtime/routes/domain-routes.ts +51 -37
- package/src/runtime/routes/epoch-millis-range.ts +34 -0
- package/src/runtime/routes/events-routes.ts +28 -34
- package/src/runtime/routes/gateway-log-routes.ts +26 -4
- package/src/runtime/routes/heartbeat-routes.ts +32 -12
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-cu-routes.ts +1 -1
- package/src/runtime/routes/identity-intro-cache.ts +11 -34
- package/src/runtime/routes/identity-routes.ts +224 -18
- package/src/runtime/routes/image-generation-routes.ts +40 -2
- package/src/runtime/routes/inbound-message-handler.ts +1 -1
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/a2a.ts +12 -10
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
- package/src/runtime/routes/integrations/slack/channel.ts +4 -0
- package/src/runtime/routes/integrations/slack/share.ts +27 -6
- package/src/runtime/routes/integrations/telegram.ts +6 -0
- package/src/runtime/routes/integrations/twilio.ts +42 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
- package/src/runtime/routes/log-export-routes.ts +8 -0
- package/src/runtime/routes/memory-v2-routes.ts +15 -8
- package/src/runtime/routes/memory-v3-routes.ts +66 -34
- package/src/runtime/routes/oauth-apps.ts +66 -12
- package/src/runtime/routes/oauth-providers.ts +44 -5
- package/src/runtime/routes/platform-routes.ts +81 -5
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +6 -4
- package/src/runtime/routes/playground/force-compact.ts +1 -1
- package/src/runtime/routes/playground/helpers.ts +1 -1
- package/src/runtime/routes/rename-conversation-routes.ts +5 -0
- package/src/runtime/routes/schedule-routes.ts +152 -42
- package/src/runtime/routes/secret-routes.ts +14 -2
- package/src/runtime/routes/skills-routes.ts +43 -14
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
- package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
- package/src/runtime/routes/trust-rules-routes.ts +26 -2
- package/src/runtime/routes/tts-routes.ts +35 -0
- package/src/runtime/routes/types.ts +66 -8
- package/src/runtime/routes/usage-routes.ts +47 -39
- package/src/runtime/routes/webhook-routes.ts +41 -2
- package/src/runtime/routes/work-items-routes.ts +2 -4
- package/src/runtime/routes/workspace-routes.ts +4 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +6 -0
- package/src/runtime/services/analyze-conversation.ts +2 -2
- package/src/runtime/services/conversation-serializer.ts +1 -1
- package/src/schedule/schedule-store.ts +20 -1
- package/src/schedule/schedule-usage-store.ts +83 -0
- package/src/schedule/scheduler.ts +12 -5
- package/src/signals/cancel.ts +2 -4
- package/src/skills/catalog-files.ts +2 -2
- package/src/skills/catalog-install.ts +3 -0
- package/src/skills/categories-cache.ts +118 -0
- package/src/skills/clawhub-files.ts +1 -2
- package/src/skills/skillssh-files.ts +1 -2
- package/src/subagent/manager.ts +17 -5
- package/src/telemetry/types.ts +29 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +112 -3
- package/src/telemetry/usage-telemetry-reporter.ts +57 -2
- package/src/tools/acp/context.ts +20 -0
- package/src/tools/acp/list-agents.test.ts +7 -1
- package/src/tools/acp/spawn.test.ts +158 -55
- package/src/tools/acp/spawn.ts +47 -72
- package/src/tools/acp/steer.test.ts +105 -8
- package/src/tools/acp/steer.ts +48 -17
- package/src/tools/apps/executors.ts +13 -8
- package/src/tools/executor.ts +1 -53
- package/src/tools/filesystem/write.ts +34 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
- package/src/tools/network/__tests__/web-search.test.ts +11 -3
- package/src/tools/network/web-search-error.test.ts +248 -0
- package/src/tools/network/web-search-error.ts +267 -0
- package/src/tools/network/web-search.ts +207 -48
- package/src/tools/schedule/create.ts +2 -0
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/terminal/safe-env.ts +10 -1
- package/src/tools/ui-surface/definitions.ts +34 -5
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
- package/src/tts/provider-catalog.ts +76 -1
- package/src/util/mutex.ts +47 -0
- package/src/workspace/git-service.ts +1 -42
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
- package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
- package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +117 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/docs/plugins.md +0 -836
- package/examples/plugins/echo/register.ts +0 -184
- package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
- package/src/__tests__/compaction-pipeline.test.ts +0 -210
- package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
- package/src/__tests__/empty-response-pipeline.test.ts +0 -423
- package/src/__tests__/llm-call-pipeline.test.ts +0 -287
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
- package/src/__tests__/persistence-pipeline.test.ts +0 -503
- package/src/__tests__/pipeline-runner.test.ts +0 -564
- package/src/__tests__/title-generate-pipeline.test.ts +0 -211
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -479
- package/src/__tests__/tool-error-pipeline.test.ts +0 -241
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -341
- package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
- package/src/gallery/default-gallery.ts +0 -1359
- package/src/gallery/gallery-manifest.ts +0 -28
- package/src/home/feature-gate.ts +0 -22
- package/src/memory/v3/provider-blocks.ts +0 -16
- package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
- package/src/plugins/defaults/circuit-breaker/package.json +0 -15
- package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
- package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
- package/src/plugins/defaults/compaction/terminal.ts +0 -73
- package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +0 -22
- package/src/plugins/defaults/empty-response/terminal.ts +0 -106
- package/src/plugins/defaults/injectors/package.json +0 -15
- package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +0 -17
- package/src/plugins/defaults/llm-call/register.ts +0 -45
- package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +0 -17
- package/src/plugins/defaults/memory-retrieval/package.json +0 -15
- package/src/plugins/defaults/memory-retrieval/register.ts +0 -181
- package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
- package/src/plugins/defaults/overflow-reduce/package.json +0 -15
- package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
- package/src/plugins/defaults/persistence/middlewares/persistence.ts +0 -19
- package/src/plugins/defaults/persistence/package.json +0 -15
- package/src/plugins/defaults/persistence/register.ts +0 -38
- package/src/plugins/defaults/persistence/terminal.ts +0 -83
- package/src/plugins/defaults/title-generate/terminal.ts +0 -31
- package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +0 -23
- package/src/plugins/defaults/token-estimate/package.json +0 -15
- package/src/plugins/defaults/token-estimate/register.ts +0 -34
- package/src/plugins/defaults/token-estimate/terminal.ts +0 -40
- package/src/plugins/defaults/tool-error/middlewares/toolError.ts +0 -21
- package/src/plugins/defaults/tool-error/terminal.ts +0 -47
- package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +0 -23
- package/src/plugins/defaults/tool-execute/package.json +0 -15
- package/src/plugins/defaults/tool-execute/register.ts +0 -49
- package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +0 -23
- package/src/plugins/defaults/tool-result-truncate/types.ts +0 -22
- package/src/skills/category-inference.ts +0 -111
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/capabilities.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/core.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/needle.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/snapshot.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/tree.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/core.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/needle.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/snapshot.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +0 -0
|
@@ -6,6 +6,7 @@ import type {
|
|
|
6
6
|
import { RiskLevel } from "../../permissions/types.js";
|
|
7
7
|
import { getProviderKeyAsync } from "../../security/secure-keys.js";
|
|
8
8
|
import { wrapUntrustedContent } from "../../security/untrusted-content.js";
|
|
9
|
+
import { isAbortReason } from "../../util/abort-reasons.js";
|
|
9
10
|
import { faviconUrlForDomain } from "../../util/favicon.js";
|
|
10
11
|
import { getLogger } from "../../util/logger.js";
|
|
11
12
|
import {
|
|
@@ -22,6 +23,11 @@ import type {
|
|
|
22
23
|
} from "../types.js";
|
|
23
24
|
import { extractDomain } from "./domain-normalize.js";
|
|
24
25
|
import type { ManagedSearchProxyResult } from "./managed-search-proxy.js";
|
|
26
|
+
import {
|
|
27
|
+
classifyWebSearchFailure,
|
|
28
|
+
logWebSearchBackendFailure,
|
|
29
|
+
WEB_SEARCH_BACKEND_FAILURE_MESSAGE,
|
|
30
|
+
} from "./web-search-error.js";
|
|
25
31
|
|
|
26
32
|
const log = getLogger("web-search");
|
|
27
33
|
|
|
@@ -381,6 +387,115 @@ function errorResult(
|
|
|
381
387
|
};
|
|
382
388
|
}
|
|
383
389
|
|
|
390
|
+
/**
|
|
391
|
+
* Wrap an already-read provider response body so {@link backendFailureResult}
|
|
392
|
+
* forwards it into the classifier's internal-only `rawDetail` (telemetry). The
|
|
393
|
+
* classifier reads `error.message`; `buildRawDetail` truncates to ≤500 chars.
|
|
394
|
+
* Returns `undefined` for an empty body so we don't pad `rawDetail` with noise.
|
|
395
|
+
* The body must NEVER reach user-facing `content`/`errorMessage`.
|
|
396
|
+
*/
|
|
397
|
+
function rawBodyDetail(body: unknown): { message: string } | undefined {
|
|
398
|
+
if (body == null) return undefined;
|
|
399
|
+
const text =
|
|
400
|
+
typeof body === "string" ? body : safeStringifyBody(body);
|
|
401
|
+
const trimmed = text.trim();
|
|
402
|
+
return trimmed ? { message: trimmed } : undefined;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function safeStringifyBody(body: unknown): string {
|
|
406
|
+
try {
|
|
407
|
+
return JSON.stringify(body);
|
|
408
|
+
} catch {
|
|
409
|
+
return String(body);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Build a {@link ToolExecutionResult} for a genuine backend/transport failure
|
|
415
|
+
* (5xx, post-retry rate-limit, thrown network/timeout error). Routes the raw
|
|
416
|
+
* detail through {@link classifyWebSearchFailure}: when it is a backend failure
|
|
417
|
+
* we surface the friendly recoverable copy (the bare sentence so the model
|
|
418
|
+
* reads it as guidance — retry / continue-without-search / paste-details —
|
|
419
|
+
* rather than fabricating) in both the model-facing `content` and the client
|
|
420
|
+
* `errorMessage`, and log the raw detail via telemetry. Non-backend categories
|
|
421
|
+
* (e.g. an unexpected 4xx) fall back to {@link errorResult} with `fallback`.
|
|
422
|
+
*
|
|
423
|
+
* Raw provider JSON / status text must never reach `content` or `errorMessage`;
|
|
424
|
+
* only `rawDetail` (internal-only) captures it for the log.
|
|
425
|
+
*/
|
|
426
|
+
function backendFailureResult(
|
|
427
|
+
query: string,
|
|
428
|
+
provider: WebSearchProvider,
|
|
429
|
+
startedAt: number,
|
|
430
|
+
raw: { error?: unknown; statusCode?: number; errorCode?: string },
|
|
431
|
+
fallback: string,
|
|
432
|
+
): ToolExecutionResult {
|
|
433
|
+
const classification = classifyWebSearchFailure({
|
|
434
|
+
isError: true,
|
|
435
|
+
error: raw.error,
|
|
436
|
+
statusCode: raw.statusCode,
|
|
437
|
+
errorCode: raw.errorCode,
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
if (!classification.isBackendFailure) {
|
|
441
|
+
return errorResult(query, provider, startedAt, fallback);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
logWebSearchBackendFailure(log, {
|
|
445
|
+
provider,
|
|
446
|
+
errorCategory: classification.category,
|
|
447
|
+
rawDetail: classification.rawDetail,
|
|
448
|
+
fallbackShown: true,
|
|
449
|
+
queryLength: query.length,
|
|
450
|
+
});
|
|
451
|
+
|
|
452
|
+
return {
|
|
453
|
+
content: WEB_SEARCH_BACKEND_FAILURE_MESSAGE,
|
|
454
|
+
isError: true,
|
|
455
|
+
activityMetadata: {
|
|
456
|
+
webSearch: {
|
|
457
|
+
query,
|
|
458
|
+
provider,
|
|
459
|
+
resultCount: 0,
|
|
460
|
+
durationMs: Date.now() - startedAt,
|
|
461
|
+
results: [],
|
|
462
|
+
errorMessage: WEB_SEARCH_BACKEND_FAILURE_MESSAGE,
|
|
463
|
+
},
|
|
464
|
+
},
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Route a thrown fetch error (network/timeout) through {@link backendFailureResult}
|
|
470
|
+
* as a `backend_unavailable` candidate, falling back to a `Web search failed: …`
|
|
471
|
+
* error for non-backend throws (e.g. a JSON parse error).
|
|
472
|
+
*
|
|
473
|
+
* If the caller aborted the request (`signal.aborted` — the user hit Stop/Esc,
|
|
474
|
+
* or an external caller cancelled), the thrown error is re-thrown so the
|
|
475
|
+
* executor's existing cancellation handling takes over. A user-cancel must NOT
|
|
476
|
+
* surface the friendly backend copy or emit `web_search_backend_failure`
|
|
477
|
+
* telemetry. Internal fetch timeouts (where the caller's signal is not aborted)
|
|
478
|
+
* still route to the friendly backend result.
|
|
479
|
+
*/
|
|
480
|
+
function networkFailureResult(
|
|
481
|
+
query: string,
|
|
482
|
+
provider: WebSearchProvider,
|
|
483
|
+
startedAt: number,
|
|
484
|
+
err: unknown,
|
|
485
|
+
signal?: AbortSignal,
|
|
486
|
+
): ToolExecutionResult {
|
|
487
|
+
if (signal?.aborted || isAbortReason((err as { reason?: unknown })?.reason)) {
|
|
488
|
+
throw err;
|
|
489
|
+
}
|
|
490
|
+
return backendFailureResult(
|
|
491
|
+
query,
|
|
492
|
+
provider,
|
|
493
|
+
startedAt,
|
|
494
|
+
{ error: err },
|
|
495
|
+
`Web search failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
|
|
384
499
|
async function executeBraveSearch(
|
|
385
500
|
query: string,
|
|
386
501
|
count: number,
|
|
@@ -396,21 +511,26 @@ async function executeBraveSearch(
|
|
|
396
511
|
const startedAt = Date.now();
|
|
397
512
|
|
|
398
513
|
for (let attempt = 0; attempt <= DEFAULT_MAX_RETRIES; attempt++) {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
514
|
+
let response: Response;
|
|
515
|
+
try {
|
|
516
|
+
response = await fetch(url, {
|
|
517
|
+
headers: {
|
|
518
|
+
Accept: "application/json",
|
|
519
|
+
"Accept-Encoding": "gzip",
|
|
520
|
+
"X-Subscription-Token": apiKey,
|
|
521
|
+
},
|
|
522
|
+
signal,
|
|
523
|
+
});
|
|
524
|
+
} catch (err) {
|
|
525
|
+
return networkFailureResult(query, "brave", startedAt, err, signal);
|
|
526
|
+
}
|
|
407
527
|
|
|
408
528
|
if (response.ok) {
|
|
409
529
|
const data = (await response.json()) as BraveSearchResponse;
|
|
410
530
|
return successfulBraveResult(data, query, startedAt);
|
|
411
531
|
}
|
|
412
532
|
|
|
413
|
-
await response.text();
|
|
533
|
+
const bodyText = await response.text();
|
|
414
534
|
|
|
415
535
|
if (response.status === 401 || response.status === 403) {
|
|
416
536
|
return errorResult(
|
|
@@ -436,20 +556,22 @@ async function executeBraveSearch(
|
|
|
436
556
|
}
|
|
437
557
|
|
|
438
558
|
log.warn({ status: response.status }, "Brave Search API error");
|
|
439
|
-
return
|
|
559
|
+
return backendFailureResult(
|
|
440
560
|
query,
|
|
441
561
|
"brave",
|
|
442
562
|
startedAt,
|
|
563
|
+
{ statusCode: response.status, error: rawBodyDetail(bodyText) },
|
|
443
564
|
response.status === 429
|
|
444
565
|
? "Brave Search rate limit exceeded after retries. Try again shortly."
|
|
445
566
|
: `Brave Search API returned status ${response.status}`,
|
|
446
567
|
);
|
|
447
568
|
}
|
|
448
569
|
|
|
449
|
-
return
|
|
570
|
+
return backendFailureResult(
|
|
450
571
|
query,
|
|
451
572
|
"brave",
|
|
452
573
|
startedAt,
|
|
574
|
+
{ statusCode: 429 },
|
|
453
575
|
"Brave Search rate limit exceeded after retries. Try again shortly.",
|
|
454
576
|
);
|
|
455
577
|
}
|
|
@@ -478,6 +600,23 @@ async function executeManagedBraveSearch(
|
|
|
478
600
|
);
|
|
479
601
|
|
|
480
602
|
if (!proxyResult.ok) {
|
|
603
|
+
// Keep billing/auth/unavailable mapping as specific copy; route genuine
|
|
604
|
+
// platform 5xx (transport-level failures) to the friendly backend helper.
|
|
605
|
+
if (
|
|
606
|
+
proxyResult.kind === "platform-error" &&
|
|
607
|
+
proxyResult.status >= 500
|
|
608
|
+
) {
|
|
609
|
+
return backendFailureResult(
|
|
610
|
+
query,
|
|
611
|
+
"brave",
|
|
612
|
+
startedAt,
|
|
613
|
+
{
|
|
614
|
+
statusCode: proxyResult.status,
|
|
615
|
+
error: rawBodyDetail(proxyResult.body),
|
|
616
|
+
},
|
|
617
|
+
managedSearchProxyErrorMessage(proxyResult),
|
|
618
|
+
);
|
|
619
|
+
}
|
|
481
620
|
return errorResult(
|
|
482
621
|
query,
|
|
483
622
|
"brave",
|
|
@@ -503,12 +642,18 @@ async function executeManagedBraveSearch(
|
|
|
503
642
|
);
|
|
504
643
|
}
|
|
505
644
|
|
|
506
|
-
if (proxyResult.status === 429) {
|
|
507
|
-
return
|
|
645
|
+
if (proxyResult.status === 429 || proxyResult.status >= 500) {
|
|
646
|
+
return backendFailureResult(
|
|
508
647
|
query,
|
|
509
648
|
"brave",
|
|
510
649
|
startedAt,
|
|
511
|
-
|
|
650
|
+
{
|
|
651
|
+
statusCode: proxyResult.status,
|
|
652
|
+
error: rawBodyDetail(proxyResult.body),
|
|
653
|
+
},
|
|
654
|
+
proxyResult.status === 429
|
|
655
|
+
? "Managed Brave Search rate limit exceeded. Try again shortly."
|
|
656
|
+
: `Managed Brave Search provider returned status ${proxyResult.status}`,
|
|
512
657
|
);
|
|
513
658
|
}
|
|
514
659
|
|
|
@@ -545,18 +690,23 @@ async function executePerplexitySearch(
|
|
|
545
690
|
): Promise<ToolExecutionResult> {
|
|
546
691
|
const startedAt = Date.now();
|
|
547
692
|
for (let attempt = 0; attempt <= DEFAULT_MAX_RETRIES; attempt++) {
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
693
|
+
let response: Response;
|
|
694
|
+
try {
|
|
695
|
+
response = await fetch(PERPLEXITY_API_URL, {
|
|
696
|
+
method: "POST",
|
|
697
|
+
headers: {
|
|
698
|
+
"Content-Type": "application/json",
|
|
699
|
+
Authorization: `Bearer ${apiKey}`,
|
|
700
|
+
},
|
|
701
|
+
body: JSON.stringify({
|
|
702
|
+
model: "sonar",
|
|
703
|
+
messages: [{ role: "user", content: query }],
|
|
704
|
+
}),
|
|
705
|
+
signal,
|
|
706
|
+
});
|
|
707
|
+
} catch (err) {
|
|
708
|
+
return networkFailureResult(query, "perplexity", startedAt, err, signal);
|
|
709
|
+
}
|
|
560
710
|
|
|
561
711
|
if (response.ok) {
|
|
562
712
|
const data = (await response.json()) as PerplexityResponse;
|
|
@@ -574,7 +724,7 @@ async function executePerplexitySearch(
|
|
|
574
724
|
};
|
|
575
725
|
}
|
|
576
726
|
|
|
577
|
-
await response.text();
|
|
727
|
+
const bodyText = await response.text();
|
|
578
728
|
|
|
579
729
|
if (response.status === 401 || response.status === 403) {
|
|
580
730
|
return errorResult(
|
|
@@ -600,20 +750,22 @@ async function executePerplexitySearch(
|
|
|
600
750
|
}
|
|
601
751
|
|
|
602
752
|
log.warn({ status: response.status }, "Perplexity API error");
|
|
603
|
-
return
|
|
753
|
+
return backendFailureResult(
|
|
604
754
|
query,
|
|
605
755
|
"perplexity",
|
|
606
756
|
startedAt,
|
|
757
|
+
{ statusCode: response.status, error: rawBodyDetail(bodyText) },
|
|
607
758
|
response.status === 429
|
|
608
759
|
? "Perplexity rate limit exceeded after retries. Try again shortly."
|
|
609
760
|
: `Perplexity API returned status ${response.status}`,
|
|
610
761
|
);
|
|
611
762
|
}
|
|
612
763
|
|
|
613
|
-
return
|
|
764
|
+
return backendFailureResult(
|
|
614
765
|
query,
|
|
615
766
|
"perplexity",
|
|
616
767
|
startedAt,
|
|
768
|
+
{ statusCode: 429 },
|
|
617
769
|
"Perplexity rate limit exceeded after retries. Try again shortly.",
|
|
618
770
|
);
|
|
619
771
|
}
|
|
@@ -638,16 +790,21 @@ async function executeTavilySearch(
|
|
|
638
790
|
const startedAt = Date.now();
|
|
639
791
|
|
|
640
792
|
for (let attempt = 0; attempt <= DEFAULT_MAX_RETRIES; attempt++) {
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
793
|
+
let response: Response;
|
|
794
|
+
try {
|
|
795
|
+
response = await fetch(TAVILY_API_URL, {
|
|
796
|
+
method: "POST",
|
|
797
|
+
headers: {
|
|
798
|
+
"Content-Type": "application/json",
|
|
799
|
+
Authorization: `Bearer ${apiKey}`,
|
|
800
|
+
"X-Client-Source": "vellum-assistant",
|
|
801
|
+
},
|
|
802
|
+
body: JSON.stringify(body),
|
|
803
|
+
signal,
|
|
804
|
+
});
|
|
805
|
+
} catch (err) {
|
|
806
|
+
return networkFailureResult(query, "tavily", startedAt, err, signal);
|
|
807
|
+
}
|
|
651
808
|
|
|
652
809
|
if (response.ok) {
|
|
653
810
|
const data = (await response.json()) as TavilySearchResponse;
|
|
@@ -665,7 +822,7 @@ async function executeTavilySearch(
|
|
|
665
822
|
};
|
|
666
823
|
}
|
|
667
824
|
|
|
668
|
-
await response.text();
|
|
825
|
+
const bodyText = await response.text();
|
|
669
826
|
|
|
670
827
|
if (response.status === 401 || response.status === 403) {
|
|
671
828
|
return errorResult(
|
|
@@ -691,20 +848,22 @@ async function executeTavilySearch(
|
|
|
691
848
|
}
|
|
692
849
|
|
|
693
850
|
log.warn({ status: response.status }, "Tavily Search API error");
|
|
694
|
-
return
|
|
851
|
+
return backendFailureResult(
|
|
695
852
|
query,
|
|
696
853
|
"tavily",
|
|
697
854
|
startedAt,
|
|
855
|
+
{ statusCode: response.status, error: rawBodyDetail(bodyText) },
|
|
698
856
|
response.status === 429
|
|
699
857
|
? "Tavily Search rate limit exceeded after retries. Try again shortly."
|
|
700
858
|
: `Tavily Search API returned status ${response.status}`,
|
|
701
859
|
);
|
|
702
860
|
}
|
|
703
861
|
|
|
704
|
-
return
|
|
862
|
+
return backendFailureResult(
|
|
705
863
|
query,
|
|
706
864
|
"tavily",
|
|
707
865
|
startedAt,
|
|
866
|
+
{ statusCode: 429 },
|
|
708
867
|
"Tavily Search rate limit exceeded after retries. Try again shortly.",
|
|
709
868
|
);
|
|
710
869
|
}
|
|
@@ -844,13 +1003,13 @@ export const webSearchTool = {
|
|
|
844
1003
|
signal: context.signal,
|
|
845
1004
|
});
|
|
846
1005
|
} catch (err) {
|
|
847
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
848
1006
|
log.error({ err }, "Managed web search failed");
|
|
849
|
-
return
|
|
1007
|
+
return networkFailureResult(
|
|
850
1008
|
query,
|
|
851
1009
|
"brave",
|
|
852
1010
|
startedAt,
|
|
853
|
-
|
|
1011
|
+
err,
|
|
1012
|
+
context.signal,
|
|
854
1013
|
);
|
|
855
1014
|
}
|
|
856
1015
|
}
|
|
@@ -897,13 +1056,13 @@ export const webSearchTool = {
|
|
|
897
1056
|
signal: context.signal,
|
|
898
1057
|
});
|
|
899
1058
|
} catch (err) {
|
|
900
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
901
1059
|
log.error({ err }, "Web search failed");
|
|
902
|
-
return
|
|
1060
|
+
return networkFailureResult(
|
|
903
1061
|
query,
|
|
904
1062
|
provider,
|
|
905
1063
|
startedAt,
|
|
906
|
-
|
|
1064
|
+
err,
|
|
1065
|
+
context.signal,
|
|
907
1066
|
);
|
|
908
1067
|
}
|
|
909
1068
|
},
|
|
@@ -144,6 +144,7 @@ export async function executeScheduleCreate(
|
|
|
144
144
|
maxRetries,
|
|
145
145
|
retryBackoffMs,
|
|
146
146
|
timeoutMs,
|
|
147
|
+
createdFromConversationId: context.conversationId,
|
|
147
148
|
});
|
|
148
149
|
|
|
149
150
|
const fireDate = formatLocalDate(job.nextRunAt);
|
|
@@ -225,6 +226,7 @@ export async function executeScheduleCreate(
|
|
|
225
226
|
maxRetries,
|
|
226
227
|
retryBackoffMs,
|
|
227
228
|
timeoutMs,
|
|
229
|
+
createdFromConversationId: context.conversationId,
|
|
228
230
|
});
|
|
229
231
|
|
|
230
232
|
const scheduleDescription =
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { findConversation } from "../../daemon/conversation-
|
|
1
|
+
import { findConversation } from "../../daemon/conversation-registry.js";
|
|
2
2
|
import { getConversationOverrideProfile } from "../../memory/conversation-crud.js";
|
|
3
3
|
import type { Message } from "../../providers/types.js";
|
|
4
4
|
import { getSubagentManager } from "../../subagent/index.js";
|
|
@@ -102,9 +102,7 @@ export async function executeSubagentSpawn(
|
|
|
102
102
|
...(inheritedOverrideProfile
|
|
103
103
|
? { overrideProfile: inheritedOverrideProfile }
|
|
104
104
|
: {}),
|
|
105
|
-
...(context.toolUseId
|
|
106
|
-
? { parentToolUseId: context.toolUseId }
|
|
107
|
-
: {}),
|
|
105
|
+
...(context.toolUseId ? { parentToolUseId: context.toolUseId } : {}),
|
|
108
106
|
...forkFields,
|
|
109
107
|
},
|
|
110
108
|
sendToClient as (msg: unknown) => void,
|
|
@@ -69,6 +69,15 @@ export const KATA_SAFE_ENV_VARS = [
|
|
|
69
69
|
export const KATA_INJECTED_ENV_VARS = ["LD_LIBRARY_PATH"] as const;
|
|
70
70
|
|
|
71
71
|
const KATA_APT_DATA_ROOT = "/data/system";
|
|
72
|
+
const KATA_FAMILY_SANDBOX_RUNTIMES = new Set([
|
|
73
|
+
"kata",
|
|
74
|
+
"firecracker",
|
|
75
|
+
"cloud-hypervisor",
|
|
76
|
+
]);
|
|
77
|
+
|
|
78
|
+
function isKataFamilyRuntime(runtime: string | undefined): boolean {
|
|
79
|
+
return runtime != null && KATA_FAMILY_SANDBOX_RUNTIMES.has(runtime);
|
|
80
|
+
}
|
|
72
81
|
|
|
73
82
|
function kataAptPaths(dataRoot: string): string[] {
|
|
74
83
|
return [
|
|
@@ -118,7 +127,7 @@ function appendUniquePathEntries(
|
|
|
118
127
|
|
|
119
128
|
export function buildSanitizedEnv(): Record<string, string> {
|
|
120
129
|
const env: Record<string, string> = {};
|
|
121
|
-
const isKataRuntime = process.env.VELLUM_SANDBOX_RUNTIME
|
|
130
|
+
const isKataRuntime = isKataFamilyRuntime(process.env.VELLUM_SANDBOX_RUNTIME);
|
|
122
131
|
const safeEnvVars = isKataRuntime
|
|
123
132
|
? [...SAFE_ENV_VARS, ...KATA_SAFE_ENV_VARS]
|
|
124
133
|
: SAFE_ENV_VARS;
|
|
@@ -19,7 +19,7 @@ import type {
|
|
|
19
19
|
// ---------------------------------------------------------------------------
|
|
20
20
|
|
|
21
21
|
const APP_BUILDER_ARTIFACT_RE =
|
|
22
|
-
/\b(app|apps|application|applications|website|websites|site|sites|dashboard|dashboards|game|games|calculator|calculators|tracker|trackers|visualization|visualizations|visualisation|visualisations|tool|tools|utility|utilities|counter|counters)\b/i;
|
|
22
|
+
/\b(app|apps|application|applications|website|websites|site|sites|dashboard|dashboards|game|games|calculator|calculators|tracker|trackers|visualization|visualizations|visualisation|visualisations|visualize|visualise|artifact|artifacts|chart|charts|graph|graphs|tool|tools|utility|utilities|counter|counters)\b/i;
|
|
23
23
|
const APP_BUILDER_BUILD_RE =
|
|
24
24
|
/\b(build|building|built|create|creating|created|make|making|made|generate|generating|generated)\b/i;
|
|
25
25
|
|
|
@@ -58,11 +58,32 @@ function isDynamicPageAppSubstitute(input: Record<string, unknown>): boolean {
|
|
|
58
58
|
}
|
|
59
59
|
|
|
60
60
|
const text = collectRoutingText(input).join(" ");
|
|
61
|
-
if (
|
|
62
|
-
|
|
61
|
+
if (
|
|
62
|
+
APP_BUILDER_ARTIFACT_RE.test(text) &&
|
|
63
|
+
(APP_BUILDER_BUILD_RE.test(text) || /\b(app|application)\b/i.test(text))
|
|
64
|
+
) {
|
|
65
|
+
return true;
|
|
63
66
|
}
|
|
64
67
|
|
|
65
|
-
|
|
68
|
+
// Second signal: even when the model gives the surface a clean,
|
|
69
|
+
// non-app-sounding title (dodging the text regex above), substantial
|
|
70
|
+
// interactive HTML is an app being smuggled in as a transient surface.
|
|
71
|
+
// A genuinely transient page is small and static; an app has real
|
|
72
|
+
// scripted markup. Keep the bar high so simple snippets still pass.
|
|
73
|
+
return isSubstantialInteractiveHtml(input);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const INTERACTIVE_HTML_RE = /<script\b|on[a-z]+\s*=|addEventListener|new Chart\b|window\.vellum\b/i;
|
|
77
|
+
|
|
78
|
+
function isSubstantialInteractiveHtml(
|
|
79
|
+
input: Record<string, unknown>,
|
|
80
|
+
): boolean {
|
|
81
|
+
const data = asRecord(input.data);
|
|
82
|
+
const html = data?.html;
|
|
83
|
+
if (typeof html !== "string") {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
return html.length > 2000 && INTERACTIVE_HTML_RE.test(html);
|
|
66
87
|
}
|
|
67
88
|
|
|
68
89
|
function collectRoutingText(input: Record<string, unknown>): string[] {
|
|
@@ -105,13 +126,17 @@ export const uiShowTool = {
|
|
|
105
126
|
'Surface structured data or UI in the conversation. For long-form writing use the document skill. For interactive apps, dashboards, games, calculators, or durable tools, call `skill_load` with `skill: "app-builder"` and use the app-builder workflow; do not use `dynamic_page` as a substitute for a persistent app. App-like `dynamic_page` calls are rejected.\n\n' +
|
|
106
127
|
"Surface types (data shapes):\n" +
|
|
107
128
|
'- card: { title, subtitle?, body, metadata?: [{ label, value }], template?, templateData? }. Templates: "weather_forecast" (native weather widget), "task_progress" (live step tracker - update via ui_update on data.templateData; shape: { title, status: "in_progress"|"completed"|"failed", steps: [{ label, status: "pending"|"in_progress"|"completed"|"failed", detail? }] })\n' +
|
|
129
|
+
"- copy_block: { text, label?, language? }. Shows copyable text with a visible copy button; use for prompts, commands, paths, or snippets the user should copy.\n" +
|
|
130
|
+
'- choice: { description?, options: [{ id, title, description?, recommended?, data? }], selectionMode?: "single"|"multiple", commitOnSelect?, submitLabel? }. Single-select choices commit on option click by default. Use for outcome offers and follow-up choices; mark the strongest option with recommended: true.\n' +
|
|
131
|
+
"- oauth_connect: { providerKey, displayName?, description?, logoUrl? }. Shows a managed OAuth connection CTA in chat; use when the current task needs a managed integration account (Google, Linear, GitHub, etc.) instead of asking the user to visit settings or attempting OAuth through shell/tools. The client supplies the CTA label. Do not include OAuth scopes in the surface; managed providers use the platform's configured scopes.\n" +
|
|
108
132
|
'- table: { columns: [{ id, label }], rows: [{ id, cells: Record<id, string | { text, icon?, iconColor?: "success"|"warning"|"error"|"muted" }>, selectable?, selected? }], selectionMode?: "none"|"single"|"multiple", caption? }\n' +
|
|
109
133
|
'- form: { description?, fields: [{ id, type: "text"|"textarea"|"select"|"toggle"|"number"|"password", label, placeholder?, required?, defaultValue?, options?: [{ label, value }] }], submitLabel? }. Multi-page: { pages: [{ id, title, description?, fields }], pageLabels?: { next?, back?, submit? }, submitLabel? }\n' +
|
|
110
134
|
'- list: { items: [{ id, title, subtitle?, icon?, selected? }], selectionMode: "single"|"multiple"|"none" }\n' +
|
|
111
135
|
"- confirmation: { message, detail?, confirmLabel?, confirmedLabel?, cancelLabel?, destructive? }\n" +
|
|
112
136
|
"- dynamic_page: { html, width?, height?, preview?: { title, subtitle?, description?, icon?, metrics?: [{ label, value }] } }\n" +
|
|
113
137
|
"- file_upload: { prompt, acceptedTypes?, maxFiles? }\n" +
|
|
114
|
-
"- task_preferences: {} (no data needed — categories are rendered client-side)\n
|
|
138
|
+
"- task_preferences: {} (no data needed — categories are rendered client-side)\n" +
|
|
139
|
+
'- work_result: { eyebrow?, status?: "completed"|"partial"|"failed"|"in_progress", summary?, metrics?: [{ label, value, detail?, tone?: "neutral"|"positive"|"warning"|"negative" }], sections?: [{ id?, title, description?, type?: "items"|"timeline"|"diff"|"artifacts"|"warnings", items?: [{ id?, title, description?, status?, tone?, metadata?: [{ label, value }], href? }], diffs?: [{ label?, before?, after? }] }] }. Shows a structured receipt after real work: what changed, what was skipped, proof points, and next actions. Keep display-only unless explicit follow-up buttons are needed.\n\n' +
|
|
115
140
|
"Proactively show a task_progress card before multi-step or long-running work (web searches, file operations, research). Show it before your first tool call, then update steps as work progresses.",
|
|
116
141
|
category: "ui-surface",
|
|
117
142
|
defaultRiskLevel: RiskLevel.Low,
|
|
@@ -124,6 +149,9 @@ export const uiShowTool = {
|
|
|
124
149
|
type: "string",
|
|
125
150
|
enum: [
|
|
126
151
|
"card",
|
|
152
|
+
"choice",
|
|
153
|
+
"copy_block",
|
|
154
|
+
"oauth_connect",
|
|
127
155
|
"form",
|
|
128
156
|
"list",
|
|
129
157
|
"table",
|
|
@@ -131,6 +159,7 @@ export const uiShowTool = {
|
|
|
131
159
|
"dynamic_page",
|
|
132
160
|
"file_upload",
|
|
133
161
|
"task_preferences",
|
|
162
|
+
"work_result",
|
|
134
163
|
],
|
|
135
164
|
description: "The type of surface to display",
|
|
136
165
|
},
|
|
@@ -30,8 +30,10 @@ import {
|
|
|
30
30
|
interface ClientCatalogProvider {
|
|
31
31
|
id: string;
|
|
32
32
|
displayName: string;
|
|
33
|
+
subtitle?: string;
|
|
34
|
+
supportsVoiceSelection?: boolean;
|
|
33
35
|
credentialMode?: string;
|
|
34
|
-
credentialsGuide?: { url: string };
|
|
36
|
+
credentialsGuide?: { description: string; url: string; linkLabel: string };
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
interface ClientCatalog {
|
|
@@ -144,6 +146,88 @@ describe("TTS provider catalog / client artifact consistency", () => {
|
|
|
144
146
|
}
|
|
145
147
|
});
|
|
146
148
|
|
|
149
|
+
// -- Display field parity --------------------------------------------------
|
|
150
|
+
|
|
151
|
+
test("subtitle matches between assistant catalog and client artifact", () => {
|
|
152
|
+
const violations: string[] = [];
|
|
153
|
+
for (const clientEntry of clientCatalog.providers) {
|
|
154
|
+
try {
|
|
155
|
+
const assistantEntry = getCatalogProvider(clientEntry.id as any);
|
|
156
|
+
if (clientEntry.subtitle !== assistantEntry.subtitle) {
|
|
157
|
+
violations.push(
|
|
158
|
+
`"${clientEntry.id}": client="${clientEntry.subtitle}" vs assistant="${assistantEntry.subtitle}"`,
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
} catch {
|
|
162
|
+
// Unknown ID — covered by provider ID parity tests above.
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (violations.length > 0) {
|
|
166
|
+
expect(violations, "Subtitle mismatch:\n" + violations.join("\n")).toEqual(
|
|
167
|
+
[],
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("supportsVoiceSelection matches between assistant catalog and client artifact", () => {
|
|
173
|
+
const violations: string[] = [];
|
|
174
|
+
for (const clientEntry of clientCatalog.providers) {
|
|
175
|
+
try {
|
|
176
|
+
const assistantEntry = getCatalogProvider(clientEntry.id as any);
|
|
177
|
+
if (
|
|
178
|
+
clientEntry.supportsVoiceSelection !==
|
|
179
|
+
assistantEntry.supportsVoiceSelection
|
|
180
|
+
) {
|
|
181
|
+
violations.push(
|
|
182
|
+
`"${clientEntry.id}": client=${clientEntry.supportsVoiceSelection} vs assistant=${assistantEntry.supportsVoiceSelection}`,
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
} catch {
|
|
186
|
+
// Unknown ID — covered by provider ID parity tests above.
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
if (violations.length > 0) {
|
|
190
|
+
expect(
|
|
191
|
+
violations,
|
|
192
|
+
"supportsVoiceSelection mismatch:\n" + violations.join("\n"),
|
|
193
|
+
).toEqual([]);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
test("credentialsGuide matches between assistant catalog and client artifact", () => {
|
|
198
|
+
const violations: string[] = [];
|
|
199
|
+
for (const clientEntry of clientCatalog.providers) {
|
|
200
|
+
try {
|
|
201
|
+
const assistantEntry = getCatalogProvider(clientEntry.id as any);
|
|
202
|
+
const cg = clientEntry.credentialsGuide;
|
|
203
|
+
const ag = assistantEntry.credentialsGuide;
|
|
204
|
+
if (cg && ag) {
|
|
205
|
+
if (cg.url !== ag.url) {
|
|
206
|
+
violations.push(`"${clientEntry.id}": credentialsGuide.url mismatch`);
|
|
207
|
+
}
|
|
208
|
+
if (cg.description !== ag.description) {
|
|
209
|
+
violations.push(
|
|
210
|
+
`"${clientEntry.id}": credentialsGuide.description mismatch`,
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
if (cg.linkLabel !== ag.linkLabel) {
|
|
214
|
+
violations.push(
|
|
215
|
+
`"${clientEntry.id}": credentialsGuide.linkLabel mismatch`,
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
} catch {
|
|
220
|
+
// Unknown ID — covered by provider ID parity tests above.
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
if (violations.length > 0) {
|
|
224
|
+
expect(
|
|
225
|
+
violations,
|
|
226
|
+
"credentialsGuide mismatch:\n" + violations.join("\n"),
|
|
227
|
+
).toEqual([]);
|
|
228
|
+
}
|
|
229
|
+
});
|
|
230
|
+
|
|
147
231
|
// -- Structural sanity ----------------------------------------------------
|
|
148
232
|
|
|
149
233
|
test("client artifact version is a positive integer", () => {
|