@vellumai/assistant 0.10.9 → 0.10.10-dev.202607162206.d08e98e
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 +1 -1
- package/Dockerfile +8 -0
- package/docs/activation-funnel-telemetry.md +13 -7
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/redacted-credential.test.ts +200 -0
- package/node_modules/@vellumai/service-contracts/src/redacted-credential.ts +226 -0
- package/openapi.yaml +530 -107
- package/package.json +1 -1
- package/scripts/generate-openapi.ts +8 -0
- package/src/__tests__/activation-early-marking.test.ts +6 -5
- package/src/__tests__/agent-loop-override-profile.test.ts +22 -25
- package/src/__tests__/agent-wake-override-profile.test.ts +21 -48
- package/src/__tests__/app-builder-tool-scripts.test.ts +0 -1
- package/src/__tests__/app-bundler.test.ts +4 -12
- package/src/__tests__/app-executors.test.ts +2 -49
- package/src/__tests__/app-routes-csp.test.ts +114 -146
- package/src/__tests__/auth-fallback-events-store.test.ts +8 -1
- package/src/__tests__/build-persisted-content.test.ts +96 -0
- package/src/__tests__/bundle-scanner.test.ts +27 -1
- package/src/__tests__/call-controller.test.ts +291 -0
- package/src/__tests__/call-site-routing-connection-auto-resolve.test.ts +165 -0
- package/src/__tests__/chat-credential-redaction.test.ts +1395 -0
- package/src/__tests__/chat-reveal-guard-priming.test.ts +791 -0
- package/src/__tests__/compaction.benchmark.test.ts +2 -1
- package/src/__tests__/compactor-image-manifest-trust.test.ts +50 -0
- package/src/__tests__/config-loader-backfill.test.ts +9 -4
- package/src/__tests__/config-schema-cmd.test.ts +10 -11
- package/src/__tests__/config-schema.test.ts +190 -257
- package/src/__tests__/conversation-agent-loop-fatal-cleanup.test.ts +208 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +5 -19
- package/src/__tests__/conversation-agent-loop.test.ts +22 -9
- package/src/__tests__/conversation-error.test.ts +31 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +110 -0
- package/src/__tests__/conversation-process-callsite.test.ts +12 -19
- package/src/__tests__/conversation-routes-slash-commands.test.ts +7 -21
- package/src/__tests__/conversation-summarize-route.test.ts +36 -44
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +7 -7
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +0 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -1
- package/src/__tests__/conversation-usage.test.ts +4 -12
- package/src/__tests__/credential-routes.test.ts +250 -0
- package/src/__tests__/credential-security-invariants.test.ts +3 -0
- package/src/__tests__/db-migration-rollback.test.ts +22 -0
- package/src/__tests__/empty-response-hook.test.ts +186 -1
- package/src/__tests__/external-plugin-loader.test.ts +11 -5
- package/src/__tests__/heartbeat-service.test.ts +0 -28
- package/src/__tests__/host-shell-tool.test.ts +2 -0
- package/src/__tests__/inactive-tool-error-messages.test.ts +2 -2
- package/src/__tests__/inference-no-mode-boot-e2e.test.ts +52 -8
- package/src/__tests__/internal-telemetry-routes.test.ts +23 -4
- package/src/__tests__/invite-routes-http.test.ts +12 -16
- package/src/__tests__/list-all-apps.test.ts +0 -4
- package/src/__tests__/llm-context-resolution.test.ts +32 -56
- package/src/__tests__/llm-request-log-turn-query.test.ts +109 -0
- package/src/__tests__/llm-resolver-override-or-default.test.ts +3 -52
- package/src/__tests__/llm-resolver.test.ts +342 -602
- package/src/__tests__/llm-schema.test.ts +79 -37
- package/src/__tests__/max-tokens-continue-hook.test.ts +19 -0
- package/src/__tests__/media-stream-output.test.ts +259 -3
- package/src/__tests__/media-stream-server-integration.test.ts +22 -1
- package/src/__tests__/media-stream-stt-session.test.ts +47 -0
- package/src/__tests__/memory-jobs-worker-cleanup-cadence.test.ts +33 -0
- package/src/__tests__/memory-recall-log-store.test.ts +47 -13
- package/src/__tests__/mock-gateway-ipc.ts +46 -1
- package/src/__tests__/mtime-cache.test.ts +61 -0
- package/src/__tests__/navigate-settings-tab.test.ts +2 -0
- package/src/__tests__/normalize-onboarding.test.ts +33 -0
- package/src/__tests__/onboarding-persona-write.test.ts +26 -0
- package/src/__tests__/plugin-api-resolve-credential.test.ts +140 -0
- package/src/__tests__/plugin-app-serve-routes.test.ts +166 -14
- package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +2 -0
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +38 -0
- package/src/__tests__/provider-commit-message-generator.test.ts +27 -20
- package/src/__tests__/provider-connections-backfill.test.ts +138 -0
- package/src/__tests__/provider-platform-proxy-integration.test.ts +10 -31
- package/src/__tests__/provider-registry-ollama.test.ts +8 -18
- package/src/__tests__/provider-send-message-override-profile.test.ts +23 -23
- package/src/__tests__/provider-usage-tracking.test.ts +9 -19
- package/src/__tests__/prune-old-conversations-job.test.ts +12 -0
- package/src/__tests__/published-app-updater.test.ts +22 -16
- package/src/__tests__/registry.test.ts +5 -16
- package/src/__tests__/retry-openrouter-only-normalization.test.ts +22 -23
- package/src/__tests__/retry-thinking-adaptive-only.test.ts +43 -48
- package/src/__tests__/retry-thinking-tool-choice.test.ts +57 -66
- package/src/__tests__/retry-verbosity-normalization.test.ts +24 -23
- package/src/__tests__/reveal-success-registry.test.ts +123 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +130 -0
- package/src/__tests__/secret-fixtures.ts +9 -0
- package/src/__tests__/server-history-render.test.ts +28 -0
- package/src/__tests__/skills.test.ts +9 -4
- package/src/__tests__/slack-share-routes.test.ts +0 -1
- package/src/__tests__/stt-stream-session.test.ts +6 -5
- package/src/__tests__/subagent-call-site-routing.test.ts +69 -95
- package/src/__tests__/subagent-disposal.test.ts +2 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +2 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +2 -0
- package/src/__tests__/subagent-manager-notify.test.ts +2 -0
- package/src/__tests__/subagent-role-registry.test.ts +37 -0
- package/src/__tests__/subagent-spawn-and-await.test.ts +1 -0
- package/src/__tests__/subagent-terminal-message.test.ts +50 -0
- package/src/__tests__/subagent-tool-gate-mode.test.ts +78 -5
- package/src/__tests__/surface-completion-nudge-hook.test.ts +19 -0
- package/src/__tests__/telemetry-routes.test.ts +99 -19
- package/src/__tests__/tool-audit.test.ts +34 -4
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-profiler.test.ts +72 -1
- package/src/__tests__/tool-result-spool.test.ts +49 -4
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -1
- package/src/__tests__/ui-channel-variants.test.ts +108 -0
- package/src/__tests__/ui-shape-teaching.test.ts +255 -0
- package/src/__tests__/usage-attribution.test.ts +18 -41
- package/src/__tests__/user-plugin-loader.test.ts +4 -4
- package/src/__tests__/voice-config-update.test.ts +46 -0
- package/src/__tests__/voice-session-bridge.test.ts +216 -84
- package/src/__tests__/workspace-migration-131-drop-web-fetch-mode.test.ts +120 -0
- package/src/agent/loop.ts +4 -3
- package/src/api/events/open-conversation.test.ts +64 -0
- package/src/api/events/open-conversation.ts +33 -0
- package/src/api/index.ts +6 -0
- package/src/api/responses/conversation-message.ts +5 -0
- package/src/apps/app-store.ts +25 -35
- package/src/bundler/app-bundler.ts +34 -48
- package/src/bundler/app-compiler.ts +39 -4
- package/src/bundler/bundle-scanner.ts +13 -0
- package/src/bundler/manifest.ts +1 -1
- package/src/calls/__tests__/voice-session-bridge.test.ts +47 -0
- package/src/calls/call-constants.ts +5 -0
- package/src/calls/call-controller.ts +100 -32
- package/src/calls/call-transport.ts +9 -0
- package/src/calls/media-stream-output.ts +107 -4
- package/src/calls/media-stream-server.ts +29 -9
- package/src/calls/media-stream-stt-session.ts +7 -1
- package/src/calls/media-turn-detector.ts +11 -1
- package/src/calls/voice-session-bridge.ts +84 -63
- package/src/cli/commands/__tests__/inference-providers.test.ts +270 -33
- package/src/cli/commands/__tests__/notifications.test.ts +24 -3
- package/src/cli/commands/config.help.ts +6 -6
- package/src/cli/commands/credentials.help.ts +13 -0
- package/src/cli/commands/credentials.ts +6 -1
- package/src/cli/commands/email.help.ts +7 -0
- package/src/cli/commands/email.ts +35 -1
- package/src/cli/commands/inference-providers.ts +168 -107
- package/src/cli/commands/inference.help.ts +118 -46
- package/src/cli/commands/memory/index.help.ts +23 -0
- package/src/cli/commands/memory/nodes.ts +146 -0
- package/src/cli/commands/notifications.help.ts +10 -10
- package/src/cli/commands/oauth/connect-surface-guidance.test.ts +40 -0
- package/src/cli/commands/oauth/connect-surface-guidance.ts +54 -0
- package/src/cli/commands/oauth/connect.test.ts +126 -0
- package/src/cli/commands/oauth/connect.ts +29 -6
- package/src/cli/commands/oauth/index.help.ts +7 -1
- package/src/cli/commands/oauth/status.test.ts +69 -3
- package/src/cli/commands/oauth/status.ts +50 -12
- package/src/cli/commands/plugins.help.ts +13 -2
- package/src/cli/commands/plugins.ts +61 -7
- package/src/cli/commands/telemetry.help.ts +13 -0
- package/src/cli/commands/telemetry.ts +45 -5
- package/src/cli/lib/__tests__/plugin-catalog-local.test.ts +8 -2
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +213 -9
- package/src/cli/lib/bundled-marketplace.json +93 -36
- package/src/cli/lib/inspect-plugin.ts +5 -1
- package/src/cli/lib/install-from-github.ts +68 -4
- package/src/cli/lib/plugin-catalog-local.ts +6 -2
- package/src/cli/lib/plugin-fingerprint.ts +3 -3
- package/src/cli/lib/upgrade-plugin.ts +236 -35
- package/src/config/__tests__/default-profile-catalog.test.ts +8 -12
- package/src/config/__tests__/plugin-resident-skill-discovery.test.ts +137 -0
- package/src/config/__tests__/profile-materialization.test.ts +1 -88
- package/src/config/bundled-skills/AGENTS.md +3 -30
- package/src/config/bundled-skills/app-builder/SKILL.md +5 -3
- package/src/config/bundled-skills/app-builder/TOOLS.json +23 -0
- package/src/config/bundled-skills/app-builder/tools/app-open.ts +32 -0
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +7 -7
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +1 -1
- package/src/config/bundled-skills/settings/TOOLS.json +3 -1
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +2 -0
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +18 -10
- package/src/config/bundled-skills/subagent/SKILL.md +2 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +1 -1
- package/src/config/call-site-defaults.ts +3 -3
- package/src/config/feature-flag-registry.json +24 -39
- package/src/config/llm-resolver.ts +88 -475
- package/src/config/profile-materialization.ts +11 -11
- package/src/config/schema.ts +93 -127
- package/src/config/schemas/__tests__/live-voice.test.ts +24 -6
- package/src/config/schemas/__tests__/stt.test.ts +31 -3
- package/src/config/schemas/live-voice.ts +10 -4
- package/src/config/schemas/llm.ts +52 -15
- package/src/config/schemas/memory-lifecycle.ts +1 -1
- package/src/config/schemas/memory-retrospective.ts +8 -0
- package/src/config/schemas/memory-v2.ts +2 -2
- package/src/config/schemas/memory-v3.ts +1 -1
- package/src/config/schemas/services.ts +6 -3
- package/src/config/schemas/stt.ts +38 -21
- package/src/config/schemas/tts.ts +13 -18
- package/src/config/skills.ts +31 -21
- package/src/context/compactor.ts +44 -7
- package/src/context/post-turn-tool-result-truncation.ts +4 -2
- package/src/context/tool-result-spool.ts +26 -5
- package/src/conversations/__tests__/message-consolidation.test.ts +48 -0
- package/src/conversations/message-consolidation.ts +22 -2
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +5 -12
- package/src/daemon/app-source-watcher.ts +17 -23
- package/src/daemon/chat-credential-redaction.ts +1365 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +627 -34
- package/src/daemon/conversation-agent-loop.ts +71 -31
- package/src/daemon/conversation-error.ts +36 -14
- package/src/daemon/conversation-process.ts +22 -0
- package/src/daemon/conversation-store.ts +35 -0
- package/src/daemon/conversation-surfaces.ts +25 -25
- package/src/daemon/conversation-tool-setup.ts +33 -7
- package/src/daemon/conversation.ts +55 -5
- package/src/daemon/handlers/shared.ts +33 -2
- package/src/daemon/lifecycle.ts +4 -4
- package/src/daemon/message-types/conversations.ts +6 -17
- package/src/daemon/providers-setup.ts +8 -0
- package/src/daemon/tool-setup-types.ts +7 -1
- package/src/daemon/wake-conversation-ops.ts +10 -1
- package/src/hooks/types.ts +9 -0
- package/src/ipc/__tests__/email-ipc.test.ts +90 -0
- package/src/ipc/gateway-client.test.ts +59 -0
- package/src/ipc/gateway-client.ts +70 -27
- package/src/live-voice/__tests__/live-voice-events.test.ts +14 -2
- package/src/live-voice/__tests__/live-voice-integration.test.ts +116 -2
- package/src/live-voice/__tests__/live-voice-vad.test.ts +804 -13
- package/src/live-voice/__tests__/protocol.test.ts +122 -0
- package/src/live-voice/live-voice-session.ts +443 -40
- package/src/live-voice/protocol.ts +143 -1
- package/src/monitoring/__tests__/plugin-source-watch.test.ts +3 -1
- package/src/monitoring/plugin-source-watch.ts +3 -62
- package/src/notifications/README.md +1 -1
- package/src/permissions/checker.ts +8 -4
- package/src/persistence/__tests__/db-init-migrations-ok.test.ts +26 -0
- package/src/persistence/conversation-crud.ts +104 -2
- package/src/persistence/db-init.ts +14 -3
- package/src/persistence/job-handlers/cleanup.ts +15 -7
- package/src/persistence/llm-request-log-store.ts +88 -52
- package/src/persistence/migrations/298-move-memory-jobs-to-memory-db.ts +7 -31
- package/src/persistence/migrations/305-drop-contact-acl-columns.ts +3 -2
- package/src/persistence/migrations/326-move-injection-events-to-memory-db.ts +8 -34
- package/src/persistence/migrations/336-move-memory-v2-activation-logs-to-memory-db.ts +90 -0
- package/src/persistence/migrations/337-move-memory-recall-logs-to-memory-db.ts +114 -0
- package/src/persistence/migrations/338-move-memory-v3-selections-to-memory-db.ts +84 -0
- package/src/persistence/migrations/339-move-activation-sessions-to-memory-db.ts +52 -0
- package/src/persistence/migrations/__tests__/run-migrations.test.ts +155 -0
- package/src/persistence/migrations/helpers/relocation.ts +44 -1
- package/src/persistence/migrations/run-migrations.ts +25 -1
- package/src/persistence/schema/infrastructure.ts +9 -0
- package/src/persistence/schema/memory-core.ts +3 -0
- package/src/persistence/schema/memory-injection.ts +2 -0
- package/src/persistence/steps.ts +36 -0
- package/src/platform/client.test.ts +1 -44
- package/src/platform/client.ts +10 -20
- package/src/platform/consent-cache.test.ts +89 -23
- package/src/platform/consent-cache.ts +71 -33
- package/src/plugin-api/constants.ts +12 -0
- package/src/plugin-api/conversation-turn.ts +37 -14
- package/src/plugin-api/index.ts +11 -1
- package/src/plugin-api/resolve-credential.ts +75 -0
- package/src/plugin-api/vision-support.test.ts +8 -19
- package/src/plugin-api/vision-support.ts +26 -17
- package/src/plugins/collect-source-versions.ts +77 -0
- package/src/plugins/defaults/compaction/compact.ts +6 -0
- package/src/plugins/defaults/compaction/window-manager.ts +9 -0
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +18 -24
- package/src/plugins/defaults/empty-response/hooks/user-prompt-submit.ts +38 -0
- package/src/plugins/defaults/empty-response/refusal-quarantine.ts +99 -0
- package/src/plugins/defaults/image-fallback/__tests__/caption-cache-persistence.test.ts +7 -3
- package/src/plugins/defaults/index.ts +8 -1
- package/src/plugins/defaults/max-tokens-continue/hooks/post-model-call.ts +4 -1
- package/src/plugins/defaults/memory/__tests__/activation-session-store.test.ts +48 -6
- package/src/plugins/defaults/memory/__tests__/db-memory-attach.test.ts +28 -22
- package/src/plugins/defaults/memory/__tests__/memory-log-stores-degraded.test.ts +148 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +80 -8
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +202 -0
- package/src/plugins/defaults/memory/__tests__/memory-v2-activation-log-store.test.ts +64 -25
- package/src/plugins/defaults/memory/__tests__/memory-v2-concept-frequency.test.ts +24 -11
- package/src/plugins/defaults/memory/__tests__/prompt-override.test.ts +70 -6
- package/src/plugins/defaults/memory/__tests__/table-relocation.test.ts +278 -0
- package/src/plugins/defaults/memory/activation-session-store.ts +27 -20
- package/src/plugins/defaults/memory/context-search/sources/memory-v2.ts +2 -9
- package/src/plugins/defaults/memory/context-search/sources/workspace.ts +1 -8
- package/src/plugins/defaults/memory/graph/retriever.test.ts +6 -6
- package/src/plugins/defaults/memory/graph/store.ts +114 -0
- package/src/plugins/defaults/memory/graph/tool-handlers.ts +36 -1
- package/src/plugins/defaults/memory/graph/tools.ts +47 -13
- package/src/plugins/defaults/memory/graph-topology/build-memory-graph.ts +16 -35
- package/src/plugins/defaults/memory/jobs-worker.ts +10 -1
- package/src/plugins/defaults/memory/memory-db.ts +3 -2
- package/src/plugins/defaults/memory/memory-recall-log-store.ts +129 -66
- package/src/plugins/defaults/memory/memory-retrospective-constants.ts +8 -0
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +11 -122
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +216 -0
- package/src/plugins/defaults/memory/memory-v2-activation-log-store.ts +84 -46
- package/src/plugins/defaults/memory/memory-v2-concept-frequency.ts +42 -32
- package/src/plugins/defaults/memory/path-containment.ts +21 -0
- package/src/plugins/defaults/memory/prompt-override.ts +51 -13
- package/src/plugins/defaults/memory/tools.test.ts +34 -0
- package/src/plugins/defaults/memory/tools.ts +12 -2
- package/src/plugins/defaults/memory/v2/__tests__/harness-compare.test.ts +19 -15
- package/src/plugins/defaults/memory/v2/__tests__/harness-oracle.test.ts +24 -19
- package/src/plugins/defaults/memory/v2/__tests__/harness-replay-input.test.ts +19 -15
- package/src/plugins/defaults/memory/v2/__tests__/injection.test.ts +22 -2
- package/src/plugins/defaults/memory/v2/__tests__/prompts-consolidation.test.ts +5 -3
- package/src/plugins/defaults/memory/v2/harness/oracle.ts +59 -41
- package/src/plugins/defaults/memory/v2/harness/replay-input.ts +29 -25
- package/src/plugins/defaults/memory/v2/migration.ts +46 -16
- package/src/plugins/defaults/memory/v2/prompts/consolidation.ts +4 -0
- package/src/plugins/defaults/memory/v3/__tests__/carry-integration.test.ts +17 -15
- package/src/plugins/defaults/memory/v3/__tests__/gate.test.ts +24 -22
- package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +9 -5
- package/src/plugins/defaults/memory/v3/__tests__/orchestrate.test.ts +277 -10
- package/src/plugins/defaults/memory/v3/__tests__/selection-log-store.test.ts +57 -5
- package/src/plugins/defaults/memory/v3/__tests__/shadow-integration.test.ts +9 -7
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +38 -7
- package/src/plugins/defaults/memory/v3/hot-set.test.ts +36 -18
- package/src/plugins/defaults/memory/v3/hot-set.ts +12 -14
- package/src/plugins/defaults/memory/v3/learned-edges.test.ts +47 -27
- package/src/plugins/defaults/memory/v3/learned-edges.ts +12 -14
- package/src/plugins/defaults/memory/v3/orchestrate.ts +139 -22
- package/src/plugins/defaults/memory/v3/prune.test.ts +10 -3
- package/src/plugins/defaults/memory/v3/prune.ts +17 -11
- package/src/plugins/defaults/memory/v3/selection-log-store.ts +23 -11
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +70 -53
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +9 -6
- package/src/plugins/external-plugin-loader.ts +15 -15
- package/src/plugins/mtime-cache.ts +47 -0
- package/src/plugins/pipeline.ts +9 -1
- package/src/plugins/plugin-execution-context.ts +44 -0
- package/src/plugins/plugin-tree-walk.ts +31 -24
- package/src/plugins/source-fingerprint.ts +3 -4
- package/src/prompts/normalize-onboarding.ts +12 -0
- package/src/prompts/persona-resolver.ts +8 -0
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +1 -1
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +6 -9
- package/src/providers/__tests__/registry-native-web-search.test.ts +4 -10
- package/src/providers/__tests__/retry-callsite.test.ts +235 -215
- package/src/providers/__tests__/satellite-connection-routing.test.ts +8 -16
- package/src/providers/atlascloud/client.ts +10 -49
- package/src/providers/baseten/client.ts +43 -0
- package/src/providers/call-site-routing.ts +26 -13
- package/src/providers/connection-resolution.ts +9 -11
- package/src/providers/fetch-provider-catalog.ts +4 -2
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +29 -4
- package/src/providers/inference/__tests__/connection-availability-keyless.test.ts +78 -0
- package/src/providers/inference/adapter-factory.ts +27 -6
- package/src/providers/inference/auth.ts +29 -1
- package/src/providers/inference/backfill.ts +61 -10
- package/src/providers/inference/connection-availability.ts +3 -2
- package/src/providers/inference/resolve-auth.ts +9 -1
- package/src/providers/model-catalog.ts +37 -0
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +24 -2
- package/src/providers/openai/api-key-validation.ts +70 -0
- package/src/providers/retry.ts +2 -0
- package/src/providers/types.ts +11 -4
- package/src/providers/vellum-model-routing.test.ts +26 -0
- package/src/providers/vellum-model-routing.ts +30 -0
- package/src/providers/voice-error-copy.ts +47 -0
- package/src/runtime/agent-wake.ts +40 -24
- package/src/runtime/for-chat-mint-registry.ts +118 -0
- package/src/runtime/reveal-nonce.ts +49 -0
- package/src/runtime/reveal-success-registry.ts +306 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +88 -3
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +161 -30
- package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +7 -0
- package/src/runtime/routes/__tests__/schedule-worker-routes.test.ts +15 -0
- package/src/runtime/routes/app-management-routes.ts +152 -45
- package/src/runtime/routes/app-routes.ts +16 -77
- package/src/runtime/routes/canned-message-complete.ts +16 -16
- package/src/runtime/routes/conversation-management-routes.ts +17 -14
- package/src/runtime/routes/conversation-query-routes.ts +54 -6
- package/src/runtime/routes/conversation-routes.ts +48 -2
- package/src/runtime/routes/credential-routes.ts +116 -3
- package/src/runtime/routes/email-routes.ts +17 -1
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +33 -5
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +6 -5
- package/src/runtime/routes/inference-provider-connection-routes.ts +77 -28
- package/src/runtime/routes/inference-send-routes.ts +1 -1
- package/src/runtime/routes/internal-telemetry-routes.ts +7 -6
- package/src/runtime/routes/plugins-routes.ts +37 -6
- package/src/runtime/routes/publish-routes.ts +15 -18
- package/src/runtime/routes/schedule-worker-routes.ts +9 -0
- package/src/runtime/routes/secret-routes.ts +10 -0
- package/src/runtime/routes/telemetry-routes.ts +149 -45
- package/src/schedule/__tests__/schedule-timezone.test.ts +101 -0
- package/src/schedule/__tests__/worker-watchdog.test.ts +209 -0
- package/src/schedule/schedule-store.ts +12 -1
- package/src/schedule/schedule-timezone.ts +63 -0
- package/src/schedule/scheduler.ts +100 -6
- package/src/schedule/worker-control.ts +19 -0
- package/src/security/auth-fallback-events-store.ts +7 -6
- package/src/security/secret-scanner.ts +26 -1
- package/src/services/published-app-updater.ts +6 -11
- package/src/stt/stt-stream-session.ts +36 -16
- package/src/subagent/manager.ts +34 -8
- package/src/telemetry/AGENTS.md +30 -1
- package/src/telemetry/__tests__/config-setting-snapshot.test.ts +18 -0
- package/src/telemetry/__tests__/outbox-test-harness.ts +5 -3
- package/src/telemetry/config-setting-snapshot.ts +44 -10
- package/src/telemetry/telemetry-event-sources.test.ts +124 -30
- package/src/telemetry/telemetry-event-sources.ts +137 -83
- package/src/telemetry/telemetry-events-outbox.test.ts +46 -1
- package/src/telemetry/telemetry-events-outbox.ts +60 -9
- package/src/telemetry/telemetry-wire-source.json +1 -1
- package/src/telemetry/telemetry-wire-validation.ts +39 -2
- package/src/telemetry/telemetry-wire.generated.ts +8 -0
- package/src/telemetry/tool-audit.ts +15 -9
- package/src/telemetry/tool-executed-events-store.test.ts +1 -1
- package/src/telemetry/turn-events-store.ts +20 -0
- package/src/telemetry/types.ts +45 -12
- package/src/telemetry/usage-telemetry-reporter.test.ts +294 -25
- package/src/telemetry/usage-telemetry-reporter.ts +117 -18
- package/src/telemetry/watchdog-direct-emit.test.ts +11 -3
- package/src/telemetry/watchdog-direct-emit.ts +11 -6
- package/src/tools/apps/executors.ts +11 -36
- package/src/tools/executor.ts +14 -1
- package/src/tools/host-terminal/host-shell.ts +6 -2
- package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +1 -1
- package/src/tools/network/__tests__/web-fetch-metadata.test.ts +25 -0
- package/src/tools/network/web-fetch.ts +6 -2
- package/src/tools/skills/sandbox-runner.ts +5 -2
- package/src/tools/subagent/spawn.ts +7 -11
- package/src/tools/terminal/shell.ts +5 -2
- package/src/tools/tool-manifest.ts +0 -2
- package/src/tools/tool-profiler.ts +37 -6
- package/src/tools/ui-surface/channel-variants.ts +101 -0
- package/src/tools/ui-surface/definitions.ts +23 -67
- package/src/tools/ui-surface/surface-shape-docs.ts +229 -0
- package/src/tts/__tests__/provider-adapters.test.ts +18 -0
- package/src/tts/provider-catalog.ts +2 -4
- package/src/tts/providers/deepgram-provider.ts +10 -1
- package/src/types/onboarding-context.ts +8 -0
- package/src/usage/attribution.ts +18 -112
- package/src/{config/bundled-skills/messaging/tools/gmail-mime-helpers.ts → util/mime-type.ts} +4 -1
- package/src/util/provider-error-patterns.ts +7 -1
- package/src/util/worker-process.ts +105 -0
- package/src/watcher/__tests__/telemetry.test.ts +17 -4
- package/src/watcher/telemetry.ts +5 -5
- package/src/workspace/migrations/131-drop-web-fetch-mode.ts +61 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/workspace/provider-commit-message-generator.ts +6 -4
- package/src/__tests__/app-open-proxy.test.ts +0 -67
- package/src/onboarding/onboarding-research-events-store.test.ts +0 -230
- package/src/onboarding/onboarding-research-events-store.ts +0 -104
- package/src/runtime/routes/assets/vellum-design-system.css +0 -2236
- package/src/tools/apps/definitions.ts +0 -73
- package/src/tools/apps/open-proxy.ts +0 -43
|
@@ -33,11 +33,31 @@ export class ToolProfiler {
|
|
|
33
33
|
startRequest(): void {
|
|
34
34
|
this.tools.clear();
|
|
35
35
|
this.requestStartMs = Date.now();
|
|
36
|
-
const rss =
|
|
36
|
+
const rss = this.sampleRssBytes() ?? 0;
|
|
37
37
|
this.rssStartBytes = rss;
|
|
38
38
|
this.peakRssBytes = rss;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Sample the current RSS in bytes, returning `null` if the reading fails.
|
|
43
|
+
*
|
|
44
|
+
* Bun 1.3.11 can throw `SystemError: Failed to get memory usage, errno: 2`
|
|
45
|
+
* from the underlying syscall. Resource metrics are best-effort — a failed
|
|
46
|
+
* sample must never propagate, so it is logged and reported as `null` and the
|
|
47
|
+
* caller keeps its last known peak.
|
|
48
|
+
*/
|
|
49
|
+
private sampleRssBytes(): number | null {
|
|
50
|
+
try {
|
|
51
|
+
return process.memoryUsage().rss;
|
|
52
|
+
} catch (err) {
|
|
53
|
+
log.warn(
|
|
54
|
+
{ err },
|
|
55
|
+
"Failed to sample RSS memory; skipping profiler sample",
|
|
56
|
+
);
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
41
61
|
recordToolCompletion(
|
|
42
62
|
toolName: string,
|
|
43
63
|
durationMs: number,
|
|
@@ -57,8 +77,8 @@ export class ToolProfiler {
|
|
|
57
77
|
stats.errors++;
|
|
58
78
|
}
|
|
59
79
|
|
|
60
|
-
const currentRss =
|
|
61
|
-
if (currentRss > this.peakRssBytes) {
|
|
80
|
+
const currentRss = this.sampleRssBytes();
|
|
81
|
+
if (currentRss !== null && currentRss > this.peakRssBytes) {
|
|
62
82
|
this.peakRssBytes = currentRss;
|
|
63
83
|
}
|
|
64
84
|
}
|
|
@@ -156,6 +176,10 @@ export function startToolProfilingRequest(conversationId: string): void {
|
|
|
156
176
|
/**
|
|
157
177
|
* Record a completed tool invocation into the conversation's profiler.
|
|
158
178
|
* A no-op when no profiling window is active (e.g. standalone tool runs).
|
|
179
|
+
*
|
|
180
|
+
* The tool has already completed by the time this runs, so profiling is pure
|
|
181
|
+
* observation: any failure here is swallowed and logged rather than allowed to
|
|
182
|
+
* propagate into the executor and fail the agent loop.
|
|
159
183
|
*/
|
|
160
184
|
export function recordToolCompletion(
|
|
161
185
|
conversationId: string,
|
|
@@ -163,9 +187,16 @@ export function recordToolCompletion(
|
|
|
163
187
|
durationMs: number,
|
|
164
188
|
isError: boolean,
|
|
165
189
|
): void {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
190
|
+
try {
|
|
191
|
+
profilersByConversation
|
|
192
|
+
.get(conversationId)
|
|
193
|
+
?.recordToolCompletion(toolName, durationMs, isError);
|
|
194
|
+
} catch (err) {
|
|
195
|
+
log.warn(
|
|
196
|
+
{ err, conversationId, toolName },
|
|
197
|
+
"Tool profiler failed to record completion; continuing",
|
|
198
|
+
);
|
|
199
|
+
}
|
|
169
200
|
}
|
|
170
201
|
|
|
171
202
|
/** Emit the end-of-turn profiling summary log for a conversation. */
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-conversation wire projections of the UI surface tool definitions.
|
|
3
|
+
*
|
|
4
|
+
* Slack renders exactly one surface — the task_progress card (everything else
|
|
5
|
+
* is rejected at execution; see `isSlackTaskProgressUiException` in
|
|
6
|
+
* `daemon/conversation-surfaces.ts`) — so Slack conversations get a `ui_show`
|
|
7
|
+
* variant documenting only that shape instead of the full multi-surface docs.
|
|
8
|
+
* Activation-rail conversations additionally get the `activation_moment`
|
|
9
|
+
* telemetry param, which no other conversation pays schema tokens for (the
|
|
10
|
+
* emit path is gated on `isActivationSession` regardless).
|
|
11
|
+
*
|
|
12
|
+
* Projection contract: shared definition objects are never mutated (variants
|
|
13
|
+
* are module-level constants swapped in via spread-copy), and the projected
|
|
14
|
+
* set is a pure function of per-conversation-stable state, so a
|
|
15
|
+
* conversation's tools+system prompt-cache prefix stays stable across turns.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import type { ToolDefinition } from "../../providers/types.js";
|
|
19
|
+
import { ACTIVATION_MOMENT_PARAMS } from "../../telemetry/activation-funnel.js";
|
|
20
|
+
import { TASK_PROGRESS_TEMPLATE_SHAPE } from "./surface-shape-docs.js";
|
|
21
|
+
|
|
22
|
+
const SLACK_UI_SHOW_DESCRIPTION = `Show a live task-progress card for the current work — the only surface this channel renders. data: { template: "task_progress", templateData: ${TASK_PROGRESS_TEMPLATE_SHAPE} }. Show it early on multi-step turns and advance step statuses via ui_update as work progresses.`;
|
|
23
|
+
|
|
24
|
+
const SLACK_UI_SHOW_INPUT_SCHEMA = {
|
|
25
|
+
type: "object",
|
|
26
|
+
properties: {
|
|
27
|
+
surface_type: {
|
|
28
|
+
type: "string",
|
|
29
|
+
enum: ["card"],
|
|
30
|
+
description: "Always card on this channel",
|
|
31
|
+
},
|
|
32
|
+
title: {
|
|
33
|
+
type: "string",
|
|
34
|
+
description: "Optional surface title",
|
|
35
|
+
},
|
|
36
|
+
data: {
|
|
37
|
+
type: "object",
|
|
38
|
+
description:
|
|
39
|
+
'Card data: { template: "task_progress", templateData: { title, status, steps } }',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
required: ["surface_type", "data"],
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Swap UI surface tool definitions for channel-appropriate variants. Returns
|
|
47
|
+
* the input array unchanged (same identity) for channels without a variant.
|
|
48
|
+
*/
|
|
49
|
+
export function projectUiToolsForChannel(
|
|
50
|
+
definitions: ToolDefinition[],
|
|
51
|
+
channel: string | undefined,
|
|
52
|
+
): ToolDefinition[] {
|
|
53
|
+
if (channel !== "slack") {
|
|
54
|
+
return definitions;
|
|
55
|
+
}
|
|
56
|
+
return definitions.map((def) =>
|
|
57
|
+
def.name === "ui_show"
|
|
58
|
+
? {
|
|
59
|
+
...def,
|
|
60
|
+
description: SLACK_UI_SHOW_DESCRIPTION,
|
|
61
|
+
input_schema: SLACK_UI_SHOW_INPUT_SCHEMA,
|
|
62
|
+
}
|
|
63
|
+
: def,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const ACTIVATION_MOMENT_PROPERTY = {
|
|
68
|
+
type: "string",
|
|
69
|
+
enum: ACTIVATION_MOMENT_PARAMS,
|
|
70
|
+
description:
|
|
71
|
+
"Activation-rail telemetry tag. Set this when this surface IS one of the activation funnel moments; the milestone is recorded automatically when the user commits the surface. Omit for all non-activation surfaces.",
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Add the optional `activation_moment` param to ui_show's schema. Applied
|
|
76
|
+
* only for activation-rail conversations (`isActivationSession`) — the rail
|
|
77
|
+
* bootstrap prompt instructs the tagging, and the daemon's emit path reads
|
|
78
|
+
* the tag from tool input independently of this schema.
|
|
79
|
+
*/
|
|
80
|
+
export function injectActivationMomentParam(
|
|
81
|
+
definitions: ToolDefinition[],
|
|
82
|
+
): ToolDefinition[] {
|
|
83
|
+
return definitions.map((def) => {
|
|
84
|
+
if (def.name !== "ui_show") {
|
|
85
|
+
return def;
|
|
86
|
+
}
|
|
87
|
+
const schema = def.input_schema as {
|
|
88
|
+
properties?: Record<string, unknown>;
|
|
89
|
+
};
|
|
90
|
+
return {
|
|
91
|
+
...def,
|
|
92
|
+
input_schema: {
|
|
93
|
+
...schema,
|
|
94
|
+
properties: {
|
|
95
|
+
...schema.properties,
|
|
96
|
+
activation_moment: ACTIVATION_MOMENT_PROPERTY,
|
|
97
|
+
},
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
});
|
|
101
|
+
}
|
|
@@ -2,19 +2,25 @@
|
|
|
2
2
|
* UI surface tool definitions.
|
|
3
3
|
*
|
|
4
4
|
* These tools allow the model to show, update, and dismiss just-in-time UI
|
|
5
|
-
* surfaces (cards,
|
|
5
|
+
* surfaces (cards, tables, forms, confirmations) on a connected macOS client.
|
|
6
6
|
* They are proxy tools -- execution is forwarded to the client and never
|
|
7
7
|
* handled locally by the daemon.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { RiskLevel } from "../../permissions/types.js";
|
|
11
11
|
import { isWeakOpenModel } from "../../providers/weak-open-model.js";
|
|
12
|
-
import { ACTIVATION_MOMENT_PARAMS } from "../../telemetry/activation-funnel.js";
|
|
13
12
|
import type {
|
|
14
13
|
ToolContext,
|
|
15
14
|
ToolDefinition,
|
|
16
15
|
ToolExecutionResult,
|
|
17
16
|
} from "../types.js";
|
|
17
|
+
import {
|
|
18
|
+
asRecord,
|
|
19
|
+
hasContent,
|
|
20
|
+
SURFACE_TYPE_NAMES,
|
|
21
|
+
UI_SHOW_TYPE_DOCS,
|
|
22
|
+
uiShowTeachingError,
|
|
23
|
+
} from "./surface-shape-docs.js";
|
|
18
24
|
|
|
19
25
|
// ---------------------------------------------------------------------------
|
|
20
26
|
// Helpers
|
|
@@ -36,6 +42,13 @@ function proxyExecute(toolName: string) {
|
|
|
36
42
|
input: Record<string, unknown>,
|
|
37
43
|
context: ToolContext,
|
|
38
44
|
): Promise<ToolExecutionResult> => {
|
|
45
|
+
if (toolName === "ui_show") {
|
|
46
|
+
const teachingError = uiShowTeachingError(input);
|
|
47
|
+
if (teachingError !== null) {
|
|
48
|
+
return { content: teachingError, isError: true };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
39
52
|
if (toolName === "ui_show" && isEmptyDynamicPage(input)) {
|
|
40
53
|
return {
|
|
41
54
|
content: isWeakOpenModel(context.attribution?.resolvedModel)
|
|
@@ -135,22 +148,6 @@ function isEmptyUpdate(input: Record<string, unknown>): boolean {
|
|
|
135
148
|
return data === null || !hasContent(data);
|
|
136
149
|
}
|
|
137
150
|
|
|
138
|
-
function hasContent(value: unknown): boolean {
|
|
139
|
-
if (value === null || value === undefined) {
|
|
140
|
-
return false;
|
|
141
|
-
}
|
|
142
|
-
if (Array.isArray(value)) {
|
|
143
|
-
return value.length > 0;
|
|
144
|
-
}
|
|
145
|
-
if (typeof value === "object") {
|
|
146
|
-
return Object.values(value).some(hasContent);
|
|
147
|
-
}
|
|
148
|
-
if (typeof value === "string") {
|
|
149
|
-
return value.trim().length > 0;
|
|
150
|
-
}
|
|
151
|
-
return true;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
151
|
function isEmptyDynamicPage(input: Record<string, unknown>): boolean {
|
|
155
152
|
if (input.surface_type !== "dynamic_page") {
|
|
156
153
|
return false;
|
|
@@ -211,12 +208,6 @@ function collectRoutingText(input: Record<string, unknown>): string[] {
|
|
|
211
208
|
return values;
|
|
212
209
|
}
|
|
213
210
|
|
|
214
|
-
function asRecord(value: unknown): Record<string, unknown> | null {
|
|
215
|
-
return value !== null && typeof value === "object"
|
|
216
|
-
? (value as Record<string, unknown>)
|
|
217
|
-
: null;
|
|
218
|
-
}
|
|
219
|
-
|
|
220
211
|
function addString(values: string[], value: unknown): void {
|
|
221
212
|
if (typeof value === "string") {
|
|
222
213
|
values.push(value);
|
|
@@ -231,21 +222,7 @@ export const uiShowTool = {
|
|
|
231
222
|
name: "ui_show",
|
|
232
223
|
description:
|
|
233
224
|
'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' +
|
|
234
|
-
|
|
235
|
-
'- 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' +
|
|
236
|
-
"- 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" +
|
|
237
|
-
'- 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' +
|
|
238
|
-
"- 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" +
|
|
239
|
-
'- 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' +
|
|
240
|
-
'- 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' +
|
|
241
|
-
'- list: { items: [{ id, title, subtitle?, icon?, selected? }], selectionMode: "single"|"multiple"|"none" }\n' +
|
|
242
|
-
"- confirmation: { message, detail?, confirmLabel?, confirmedLabel?, cancelLabel?, destructive? }\n" +
|
|
243
|
-
"- dynamic_page: { html, width?, height?, preview?: { title, subtitle?, description?, icon?, metrics?: [{ label, value }] } }\n" +
|
|
244
|
-
"- file_upload: { prompt, acceptedTypes?, maxFiles? }\n" +
|
|
245
|
-
"- task_preferences: {} (no data needed — categories are rendered client-side)\n" +
|
|
246
|
-
'- 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' +
|
|
247
|
-
'- channel_setup: { channel: "slack" | "telegram" | "phone" }. Opens the channel setup panel in a side drawer. Returns success only after a connected client confirms the panel rendered (an error means the user does NOT see the panel — never claim it is open after an error). The user then completes credential entry at their own pace. Slack shows a full setup wizard; Telegram and Phone show credential forms (the assistant handles remaining setup steps like webhooks in chat after the user saves credentials).\n\n' +
|
|
248
|
-
"For multi-step or long-running turns (web searches, file operations, research), show a task_progress card early and keep its steps updated as work progresses. Coarse steps are fine, and you can add or revise them as the work takes shape — a rough card beats no signal.",
|
|
225
|
+
UI_SHOW_TYPE_DOCS,
|
|
249
226
|
category: "ui-surface",
|
|
250
227
|
defaultRiskLevel: RiskLevel.Low,
|
|
251
228
|
executionTarget: "host",
|
|
@@ -255,26 +232,12 @@ export const uiShowTool = {
|
|
|
255
232
|
properties: {
|
|
256
233
|
surface_type: {
|
|
257
234
|
type: "string",
|
|
258
|
-
enum:
|
|
259
|
-
"card",
|
|
260
|
-
"channel_setup",
|
|
261
|
-
"choice",
|
|
262
|
-
"copy_block",
|
|
263
|
-
"oauth_connect",
|
|
264
|
-
"form",
|
|
265
|
-
"list",
|
|
266
|
-
"table",
|
|
267
|
-
"confirmation",
|
|
268
|
-
"dynamic_page",
|
|
269
|
-
"file_upload",
|
|
270
|
-
"task_preferences",
|
|
271
|
-
"work_result",
|
|
272
|
-
],
|
|
235
|
+
enum: SURFACE_TYPE_NAMES,
|
|
273
236
|
description: "The type of surface to display",
|
|
274
237
|
},
|
|
275
238
|
title: {
|
|
276
239
|
type: "string",
|
|
277
|
-
description: "Optional
|
|
240
|
+
description: "Optional surface title",
|
|
278
241
|
},
|
|
279
242
|
data: {
|
|
280
243
|
type: "object",
|
|
@@ -286,12 +249,11 @@ export const uiShowTool = {
|
|
|
286
249
|
items: {
|
|
287
250
|
type: "object",
|
|
288
251
|
properties: {
|
|
289
|
-
id: { type: "string"
|
|
290
|
-
label: { type: "string"
|
|
252
|
+
id: { type: "string" },
|
|
253
|
+
label: { type: "string" },
|
|
291
254
|
style: {
|
|
292
255
|
type: "string",
|
|
293
256
|
enum: ["primary", "secondary", "destructive"],
|
|
294
|
-
description: "Visual style of the button",
|
|
295
257
|
},
|
|
296
258
|
},
|
|
297
259
|
required: ["id", "label"],
|
|
@@ -302,23 +264,17 @@ export const uiShowTool = {
|
|
|
302
264
|
type: "string",
|
|
303
265
|
enum: ["inline", "panel"],
|
|
304
266
|
description:
|
|
305
|
-
'Where to render
|
|
267
|
+
'Where to render: "inline" (default, embedded in chat) or "panel" (floating window; only when explicitly requested)',
|
|
306
268
|
},
|
|
307
269
|
await_action: {
|
|
308
270
|
type: "boolean",
|
|
309
271
|
description:
|
|
310
|
-
"
|
|
272
|
+
"Block until an action is selected. Defaults to true when actions are provided.",
|
|
311
273
|
},
|
|
312
274
|
persistent: {
|
|
313
275
|
type: "boolean",
|
|
314
276
|
description:
|
|
315
|
-
"
|
|
316
|
-
},
|
|
317
|
-
activation_moment: {
|
|
318
|
-
type: "string",
|
|
319
|
-
enum: ACTIVATION_MOMENT_PARAMS,
|
|
320
|
-
description:
|
|
321
|
-
"Activation-rail telemetry tag (cohort only). Set this when this surface IS one of the activation funnel moments; the milestone is recorded automatically when the user commits the surface. Omit for all non-activation surfaces.",
|
|
277
|
+
"Keep the surface visible after an action is clicked (clicked actions are marked spent). For launcher/menu cards. Defaults to false.",
|
|
322
278
|
},
|
|
323
279
|
},
|
|
324
280
|
required: ["surface_type", "data"],
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Point-of-use shape documentation and essential-content guards for ui_show
|
|
3
|
+
* surfaces.
|
|
4
|
+
*
|
|
5
|
+
* Each entry documents one surface type: a one-phrase `purpose` (used in the
|
|
6
|
+
* unknown-type index error) and the full data `shape` spec (returned inside a
|
|
7
|
+
* teaching error when a payload is missing its load-bearing content). Keeping
|
|
8
|
+
* the specs here rather than in the always-present tool description lets the
|
|
9
|
+
* model recover the exact shape at the moment it gets one wrong.
|
|
10
|
+
*
|
|
11
|
+
* Guards are advisory and intentionally minimal: they check only the fields
|
|
12
|
+
* without which the surface renders blank or broken (mirroring the tolerant
|
|
13
|
+
* contract in `api/surfaces.ts` — a renderable payload must never be
|
|
14
|
+
* rejected). Normalization the daemon already performs (e.g. top-level
|
|
15
|
+
* `template`/`title` lifted into card data by `normalizeCardShowData`) must
|
|
16
|
+
* stay accepted, so card has no guard here.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export function asRecord(value: unknown): Record<string, unknown> | null {
|
|
20
|
+
return value !== null && typeof value === "object"
|
|
21
|
+
? (value as Record<string, unknown>)
|
|
22
|
+
: null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Whether a value carries any renderable content: a non-empty string, a
|
|
27
|
+
* non-empty array, a non-empty primitive, or an object with at least one
|
|
28
|
+
* content-bearing value (recursively).
|
|
29
|
+
*/
|
|
30
|
+
export function hasContent(value: unknown): boolean {
|
|
31
|
+
if (value === null || value === undefined) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
if (Array.isArray(value)) {
|
|
35
|
+
return value.length > 0;
|
|
36
|
+
}
|
|
37
|
+
if (typeof value === "object") {
|
|
38
|
+
return Object.values(value).some(hasContent);
|
|
39
|
+
}
|
|
40
|
+
if (typeof value === "string") {
|
|
41
|
+
return value.trim().length > 0;
|
|
42
|
+
}
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function isNonEmptyString(value: unknown): boolean {
|
|
47
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isNonEmptyArray(value: unknown): boolean {
|
|
51
|
+
return Array.isArray(value) && value.length > 0;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
interface SurfaceShapeDoc {
|
|
55
|
+
/** One-phrase purpose, shown in the unknown-surface_type index error. */
|
|
56
|
+
purpose: string;
|
|
57
|
+
/** Full `data` shape spec, shown in per-type teaching errors. */
|
|
58
|
+
shape: string;
|
|
59
|
+
/**
|
|
60
|
+
* Returns a description of the missing load-bearing content, or null when
|
|
61
|
+
* the payload has enough to render. Absent for types the daemon normalizes
|
|
62
|
+
* leniently (card), that render fine with defaults (file_upload), or that
|
|
63
|
+
* have bespoke handling (dynamic_page).
|
|
64
|
+
*/
|
|
65
|
+
missingContent?: (data: Record<string, unknown>) => string | null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** templateData shape of a task_progress card (shared with channel variants). */
|
|
69
|
+
export const TASK_PROGRESS_TEMPLATE_SHAPE =
|
|
70
|
+
'{ title, status: "in_progress"|"completed"|"failed", steps: [{ label, status: "pending"|"in_progress"|"completed"|"failed", detail? }] }';
|
|
71
|
+
|
|
72
|
+
export const SURFACE_SHAPE_DOCS: Record<string, SurfaceShapeDoc> = {
|
|
73
|
+
card: {
|
|
74
|
+
purpose: "structured info card, supports templates like task_progress",
|
|
75
|
+
shape: `{ title, subtitle?, body, metadata?: [{ label, value }], template?, templateData? }. Template "task_progress" renders a live step tracker — templateData: ${TASK_PROGRESS_TEMPLATE_SHAPE}; advance it via ui_update as steps finish. Other card templates are documented by the skills that use them`,
|
|
76
|
+
},
|
|
77
|
+
copy_block: {
|
|
78
|
+
purpose: "copyable text with a visible copy button",
|
|
79
|
+
shape:
|
|
80
|
+
"{ text, label?, language? } — shows copyable text with a visible copy button; use for prompts, commands, paths, or snippets the user should copy",
|
|
81
|
+
missingContent: (data) =>
|
|
82
|
+
isNonEmptyString(data.text)
|
|
83
|
+
? null
|
|
84
|
+
: "`data.text` must be a non-empty string",
|
|
85
|
+
},
|
|
86
|
+
choice: {
|
|
87
|
+
purpose: "clickable options for the user to pick from",
|
|
88
|
+
shape:
|
|
89
|
+
'{ description?, options: [{ id, title, description?, recommended?, data? }], selectionMode?: "single"|"multiple", commitOnSelect?, submitLabel? }. Single-select choices commit on option click by default; mark the strongest option with recommended: true',
|
|
90
|
+
missingContent: (data) =>
|
|
91
|
+
isNonEmptyArray(data.options)
|
|
92
|
+
? null
|
|
93
|
+
: "`data.options` must be a non-empty array",
|
|
94
|
+
},
|
|
95
|
+
table: {
|
|
96
|
+
purpose: "tabular data, optionally with selectable rows",
|
|
97
|
+
shape:
|
|
98
|
+
'{ columns: [{ id, label }], rows: [{ id, cells: Record<columnId, string | { text, icon?, iconColor?: "success"|"warning"|"error"|"muted" }>, selectable?, selected? }], selectionMode?: "none"|"single"|"multiple", caption? }',
|
|
99
|
+
missingContent: (data) =>
|
|
100
|
+
isNonEmptyArray(data.columns)
|
|
101
|
+
? null
|
|
102
|
+
: "`data.columns` must be a non-empty array",
|
|
103
|
+
},
|
|
104
|
+
work_result: {
|
|
105
|
+
purpose: "structured receipt after completed work",
|
|
106
|
+
shape:
|
|
107
|
+
'{ 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? }] }] } — structured receipt after real work; keep display-only unless follow-up buttons are needed',
|
|
108
|
+
missingContent: (data) =>
|
|
109
|
+
hasContent(data)
|
|
110
|
+
? null
|
|
111
|
+
: "`data` must carry at least a summary, metrics, or sections",
|
|
112
|
+
},
|
|
113
|
+
oauth_connect: {
|
|
114
|
+
purpose: "managed OAuth connection button for an integration account",
|
|
115
|
+
shape:
|
|
116
|
+
"{ providerKey, displayName?, description?, logoUrl? } — managed OAuth connection CTA; use when the task needs a managed integration account (Google, Linear, GitHub, ...) instead of settings or shell OAuth. Do not include OAuth scopes; managed providers use the platform's configured scopes",
|
|
117
|
+
missingContent: (data) =>
|
|
118
|
+
isNonEmptyString(data.providerKey)
|
|
119
|
+
? null
|
|
120
|
+
: "`data.providerKey` must name the provider to connect",
|
|
121
|
+
},
|
|
122
|
+
form: {
|
|
123
|
+
purpose: "input form, single or multi-page",
|
|
124
|
+
shape:
|
|
125
|
+
'{ 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? }',
|
|
126
|
+
missingContent: (data) =>
|
|
127
|
+
isNonEmptyArray(data.fields) || isNonEmptyArray(data.pages)
|
|
128
|
+
? null
|
|
129
|
+
: "`data.fields` (or `data.pages`) must be a non-empty array",
|
|
130
|
+
},
|
|
131
|
+
confirmation: {
|
|
132
|
+
purpose: "confirm/cancel prompt",
|
|
133
|
+
shape:
|
|
134
|
+
"{ message, detail?, confirmLabel?, confirmedLabel?, cancelLabel?, destructive? }",
|
|
135
|
+
missingContent: (data) =>
|
|
136
|
+
isNonEmptyString(data.message)
|
|
137
|
+
? null
|
|
138
|
+
: "`data.message` must be a non-empty string",
|
|
139
|
+
},
|
|
140
|
+
file_upload: {
|
|
141
|
+
purpose: "prompt the user to upload files",
|
|
142
|
+
shape: "{ prompt, acceptedTypes?, maxFiles? }",
|
|
143
|
+
},
|
|
144
|
+
dynamic_page: {
|
|
145
|
+
purpose: "custom HTML page for transient UI only, never app-like builds",
|
|
146
|
+
shape:
|
|
147
|
+
"{ html, width?, height?, preview?: { title, subtitle?, description?, icon?, metrics?: [{ label, value }] } } — custom visual HTML for transient surfaces only, never app-like builds",
|
|
148
|
+
},
|
|
149
|
+
channel_setup: {
|
|
150
|
+
purpose: "open the Slack/Telegram/Phone setup panel",
|
|
151
|
+
shape: '{ channel: "slack" | "telegram" | "phone" }',
|
|
152
|
+
missingContent: (data) =>
|
|
153
|
+
data.channel === "slack" ||
|
|
154
|
+
data.channel === "telegram" ||
|
|
155
|
+
data.channel === "phone"
|
|
156
|
+
? null
|
|
157
|
+
: '`data.channel` must be one of "slack", "telegram", "phone"',
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/** Model-facing surface_type enum, derived so docs and schema cannot drift. */
|
|
162
|
+
export const SURFACE_TYPE_NAMES = Object.keys(SURFACE_SHAPE_DOCS);
|
|
163
|
+
|
|
164
|
+
const SURFACE_TYPE_INDEX = SURFACE_TYPE_NAMES.map(
|
|
165
|
+
(name) => `${name} (${SURFACE_SHAPE_DOCS[name]!.purpose})`,
|
|
166
|
+
).join("; ");
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Types whose full shape rides in the always-present tool description —
|
|
170
|
+
* together they cover ~95% of fleet ui_show calls. The rest appear there as
|
|
171
|
+
* a one-line index and get their shape from the teaching error on first
|
|
172
|
+
* misuse.
|
|
173
|
+
*/
|
|
174
|
+
const HOT_SURFACE_TYPES = [
|
|
175
|
+
"card",
|
|
176
|
+
"copy_block",
|
|
177
|
+
"choice",
|
|
178
|
+
"table",
|
|
179
|
+
"work_result",
|
|
180
|
+
"oauth_connect",
|
|
181
|
+
"dynamic_page",
|
|
182
|
+
] as const;
|
|
183
|
+
|
|
184
|
+
const COLD_SURFACE_INDEX = SURFACE_TYPE_NAMES.filter(
|
|
185
|
+
(name) => !(HOT_SURFACE_TYPES as readonly string[]).includes(name),
|
|
186
|
+
)
|
|
187
|
+
.map((name) => `${name} (${SURFACE_SHAPE_DOCS[name]!.purpose})`)
|
|
188
|
+
.join(", ");
|
|
189
|
+
|
|
190
|
+
/** The surface-types section of the ui_show tool description. */
|
|
191
|
+
export const UI_SHOW_TYPE_DOCS = [
|
|
192
|
+
"Surface types (data shapes):",
|
|
193
|
+
...HOT_SURFACE_TYPES.map(
|
|
194
|
+
(name) => `- ${name}: ${SURFACE_SHAPE_DOCS[name]!.shape}`,
|
|
195
|
+
),
|
|
196
|
+
`Other types: ${COLD_SURFACE_INDEX}. Send your best-guess data — if required content is missing, the error returns the exact shape.`,
|
|
197
|
+
].join("\n");
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Teaching error for a ui_show payload that would render nothing, or null
|
|
201
|
+
* when the payload is displayable. Unknown/missing surface_type gets the
|
|
202
|
+
* full type index; a known type missing its load-bearing content gets that
|
|
203
|
+
* type's exact shape. dynamic_page emptiness is handled by the bespoke
|
|
204
|
+
* model-aware envelopes in `definitions.ts`, not here.
|
|
205
|
+
*/
|
|
206
|
+
export function uiShowTeachingError(
|
|
207
|
+
input: Record<string, unknown>,
|
|
208
|
+
): string | null {
|
|
209
|
+
const surfaceType = input.surface_type;
|
|
210
|
+
if (
|
|
211
|
+
typeof surfaceType !== "string" ||
|
|
212
|
+
!Object.hasOwn(SURFACE_SHAPE_DOCS, surfaceType)
|
|
213
|
+
) {
|
|
214
|
+
const got =
|
|
215
|
+
typeof surfaceType === "string" && surfaceType.trim().length > 0
|
|
216
|
+
? `"${surfaceType}" is not a surface type`
|
|
217
|
+
: "`surface_type` is missing";
|
|
218
|
+
return `Error: ui_show was not displayed — ${got}. Valid types: ${SURFACE_TYPE_INDEX}. Resend ui_show with one of these surface_type values; if a type's data is missing required content, the error will include its exact shape.`;
|
|
219
|
+
}
|
|
220
|
+
const doc = SURFACE_SHAPE_DOCS[surfaceType]!;
|
|
221
|
+
if (!doc.missingContent) {
|
|
222
|
+
return null;
|
|
223
|
+
}
|
|
224
|
+
const missing = doc.missingContent(asRecord(input.data) ?? {});
|
|
225
|
+
if (missing === null) {
|
|
226
|
+
return null;
|
|
227
|
+
}
|
|
228
|
+
return `Error: ui_show ${surfaceType} was not displayed — ${missing}, so the user saw nothing. Data shape: ${doc.shape}. Resend ui_show with the missing content filled in.`;
|
|
229
|
+
}
|
|
@@ -1520,6 +1520,24 @@ describe("Deepgram TTS provider adapter", () => {
|
|
|
1520
1520
|
}
|
|
1521
1521
|
});
|
|
1522
1522
|
|
|
1523
|
+
test("surfaces a friendly, JSON-free message on a 401 auth failure", async () => {
|
|
1524
|
+
mockFetchError(401, '{"err_code":"INVALID_AUTH","err_msg":"expired"}');
|
|
1525
|
+
|
|
1526
|
+
const provider = createDeepgramProvider();
|
|
1527
|
+
|
|
1528
|
+
try {
|
|
1529
|
+
await provider.synthesize(makeRequest());
|
|
1530
|
+
throw new Error("Expected synthesize to throw");
|
|
1531
|
+
} catch (err) {
|
|
1532
|
+
expect(err).toBeInstanceOf(DeepgramTtsError);
|
|
1533
|
+
const message = (err as DeepgramTtsError).message;
|
|
1534
|
+
expect(message).not.toContain("{");
|
|
1535
|
+
expect(message).not.toContain("INVALID_AUTH");
|
|
1536
|
+
expect(message).toContain("API key");
|
|
1537
|
+
expect(message).toContain("Settings");
|
|
1538
|
+
}
|
|
1539
|
+
});
|
|
1540
|
+
|
|
1523
1541
|
test("throws DEEPGRAM_TTS_EMPTY_RESPONSE on empty audio body", async () => {
|
|
1524
1542
|
mockFetchReturning(new Uint8Array(0));
|
|
1525
1543
|
|
|
@@ -54,11 +54,9 @@ const DEFINITIONS = {
|
|
|
54
54
|
/**
|
|
55
55
|
* Definitions in display order (e.g. settings dropdowns).
|
|
56
56
|
*/
|
|
57
|
-
// vellum is
|
|
58
|
-
// `services.tts.mode`, not the provider picker — today's settings UI writes
|
|
59
|
-
// only `provider` + an API key, which the schema rejects for vellum. The
|
|
60
|
-
// definition stays in DEFINITIONS so managed-mode resolution works.
|
|
57
|
+
// vellum leads: it is the managed option, selected like any other provider.
|
|
61
58
|
const CATALOG: readonly TtsProviderDefinition[] = [
|
|
59
|
+
DEFINITIONS.vellum,
|
|
62
60
|
DEFINITIONS.elevenlabs,
|
|
63
61
|
DEFINITIONS["fish-audio"],
|
|
64
62
|
DEFINITIONS.deepgram,
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
|
|
13
13
|
import { getConfig } from "../../config/loader.js";
|
|
14
14
|
import type { TtsDeepgramProviderConfig } from "../../config/schemas/tts.js";
|
|
15
|
+
import { describeTtsAuthFailure } from "../../providers/voice-error-copy.js";
|
|
15
16
|
import { getProviderKeyAsync } from "../../security/secure-keys.js";
|
|
16
17
|
import { getLogger } from "../../util/logger.js";
|
|
17
18
|
import { resolvePcmOutputSampleRateHz } from "../pcm-sample-rates.js";
|
|
@@ -208,9 +209,17 @@ async function performTtsRequest(
|
|
|
208
209
|
|
|
209
210
|
if (!response.ok) {
|
|
210
211
|
const errorText = await response.text().catch(() => "");
|
|
212
|
+
log.debug(
|
|
213
|
+
{ status: response.status, body: errorText.slice(0, 300) },
|
|
214
|
+
"Deepgram TTS non-200 response",
|
|
215
|
+
);
|
|
216
|
+
const message =
|
|
217
|
+
response.status === 401 || response.status === 403
|
|
218
|
+
? describeTtsAuthFailure("Deepgram")
|
|
219
|
+
: `Deepgram text-to-speech failed (HTTP ${response.status}).`;
|
|
211
220
|
throw new DeepgramTtsError(
|
|
212
221
|
"DEEPGRAM_TTS_HTTP_ERROR",
|
|
213
|
-
|
|
222
|
+
message,
|
|
214
223
|
response.status,
|
|
215
224
|
);
|
|
216
225
|
}
|
|
@@ -18,4 +18,12 @@ export interface OnboardingContext {
|
|
|
18
18
|
initialMessage?: string;
|
|
19
19
|
/** Skills to eagerly load on first turn (e.g. ["geo-writing", "document-editor"]). Informational — the bootstrap template drives actual loading. */
|
|
20
20
|
skills?: string[];
|
|
21
|
+
/**
|
|
22
|
+
* Findings from the pre-chat onboarding research that the user explicitly
|
|
23
|
+
* kept on the results screen (removed/rejected claims are excluded
|
|
24
|
+
* client-side). Rendered into the persona's onboarding section so they're
|
|
25
|
+
* available from the very first turn — the greeting turn is barred from
|
|
26
|
+
* `recall`/file reads and would otherwise be blind to them.
|
|
27
|
+
*/
|
|
28
|
+
researchFindings?: string[];
|
|
21
29
|
}
|