@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
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
|
|
29
29
|
import { getPlatformOrganizationId, getPlatformUserId } from "../config/env.js";
|
|
30
30
|
import { VellumPlatformClient } from "../platform/client.js";
|
|
31
|
-
import {
|
|
31
|
+
import { getRawShareAnalytics } from "../platform/consent-cache.js";
|
|
32
32
|
import { arePlatformFeaturesEnabled } from "../platform/feature-gate.js";
|
|
33
33
|
import { getDeviceId } from "../util/device-id.js";
|
|
34
34
|
import { getLogger } from "../util/logger.js";
|
|
@@ -45,6 +45,10 @@ import {
|
|
|
45
45
|
TOOL_EXECUTED_SOURCE_ID,
|
|
46
46
|
watermarkKeysForSource,
|
|
47
47
|
} from "./telemetry-event-sources.js";
|
|
48
|
+
import {
|
|
49
|
+
deleteTelemetryOutboxEventsBefore,
|
|
50
|
+
OUTBOX_MAX_ROW_AGE_MS,
|
|
51
|
+
} from "./telemetry-events-outbox.js";
|
|
48
52
|
import { useReadOnlyMainDbForTelemetry } from "./telemetry-main-db.js";
|
|
49
53
|
import { validateWireEvents } from "./telemetry-wire-validation.js";
|
|
50
54
|
|
|
@@ -65,6 +69,43 @@ const BATCH_SIZE = 500;
|
|
|
65
69
|
const MAX_CONSECUTIVE_BATCHES = 10;
|
|
66
70
|
const TELEMETRY_PATH = "/v1/telemetry/ingest/";
|
|
67
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Outcome of a {@link UsageTelemetryReporter.flush} call. `flushed: true` means
|
|
74
|
+
* at least one batch reached the platform (2xx). `sent` is the count the daemon
|
|
75
|
+
* POSTed; `persisted` is the count the platform confirmed written; `dropped` is
|
|
76
|
+
* `sent - persisted` — folding server-side drops (opt-out, unauthenticated) and
|
|
77
|
+
* events silently skipped at ingest validation (unknown/invalid type) into one
|
|
78
|
+
* "did not land" number. `flushed: false` carries a kebab-case skip reason.
|
|
79
|
+
*/
|
|
80
|
+
export type TelemetryFlushSummary =
|
|
81
|
+
| { flushed: true; sent: number; persisted: number; dropped: number }
|
|
82
|
+
| { flushed: false; reason: string };
|
|
83
|
+
|
|
84
|
+
interface FlushAccumulator {
|
|
85
|
+
sent: number;
|
|
86
|
+
persisted: number;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Read `persisted` from an ingest response body. Falls back to `fallback`
|
|
91
|
+
* (the POSTed count — assume all landed) on a malformed body or an older
|
|
92
|
+
* platform that omits the field. Never throws.
|
|
93
|
+
*/
|
|
94
|
+
function parsePersistedCount(body: string, fallback: number): number {
|
|
95
|
+
try {
|
|
96
|
+
const parsed: unknown = JSON.parse(body);
|
|
97
|
+
if (parsed && typeof parsed === "object") {
|
|
98
|
+
const p = (parsed as { persisted?: unknown }).persisted;
|
|
99
|
+
if (typeof p === "number" && Number.isFinite(p) && p >= 0) {
|
|
100
|
+
return p;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
} catch {
|
|
104
|
+
// Malformed/empty body — assume everything persisted.
|
|
105
|
+
}
|
|
106
|
+
return fallback;
|
|
107
|
+
}
|
|
108
|
+
|
|
68
109
|
// ---------------------------------------------------------------------------
|
|
69
110
|
// Singleton access
|
|
70
111
|
// ---------------------------------------------------------------------------
|
|
@@ -193,7 +234,7 @@ export function initToolExecutedWatermarkIfAbsent(
|
|
|
193
234
|
export class UsageTelemetryReporter {
|
|
194
235
|
private initialFlushTimer: ReturnType<typeof setTimeout> | null = null;
|
|
195
236
|
private timer: ReturnType<typeof setInterval> | null = null;
|
|
196
|
-
private activeFlush: Promise<
|
|
237
|
+
private activeFlush: Promise<TelemetryFlushSummary> | null = null;
|
|
197
238
|
private readonly sources: readonly TelemetryEventSource[];
|
|
198
239
|
private readonly checkpoints: FlushCheckpointStore;
|
|
199
240
|
|
|
@@ -248,22 +289,53 @@ export class UsageTelemetryReporter {
|
|
|
248
289
|
await this.flush();
|
|
249
290
|
}
|
|
250
291
|
|
|
251
|
-
async flush(): Promise<
|
|
292
|
+
async flush(): Promise<TelemetryFlushSummary> {
|
|
252
293
|
if (this.activeFlush) {
|
|
253
|
-
return; // overlap guard
|
|
294
|
+
return this.activeFlush; // overlap guard — share the in-flight result
|
|
254
295
|
}
|
|
255
296
|
this.activeFlush = this._doFlush();
|
|
256
297
|
try {
|
|
257
|
-
await this.activeFlush;
|
|
298
|
+
return await this.activeFlush;
|
|
258
299
|
} finally {
|
|
259
300
|
this.activeFlush = null;
|
|
260
301
|
}
|
|
261
302
|
}
|
|
262
303
|
|
|
263
|
-
private async _doFlush(
|
|
304
|
+
private async _doFlush(
|
|
305
|
+
batchCount = 0,
|
|
306
|
+
acc: FlushAccumulator = { sent: 0, persisted: 0 },
|
|
307
|
+
): Promise<TelemetryFlushSummary> {
|
|
308
|
+
// Once any batch has shipped (acc.sent > 0), a later skip/defer/error is
|
|
309
|
+
// just the natural end of the flush — report what landed, not a failure.
|
|
310
|
+
const flushed = (): TelemetryFlushSummary => ({
|
|
311
|
+
flushed: true,
|
|
312
|
+
sent: acc.sent,
|
|
313
|
+
persisted: acc.persisted,
|
|
314
|
+
dropped: Math.max(0, acc.sent - acc.persisted),
|
|
315
|
+
});
|
|
316
|
+
const settle = (reason: string): TelemetryFlushSummary =>
|
|
317
|
+
acc.sent > 0 ? flushed() : { flushed: false, reason };
|
|
318
|
+
|
|
264
319
|
try {
|
|
265
320
|
if (batchCount >= MAX_CONSECUTIVE_BATCHES) {
|
|
266
|
-
return;
|
|
321
|
+
return flushed();
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// Age-bound the ack-mode outbox before any skip gate (platform,
|
|
325
|
+
// checkpoint store, consent three-way), so the bound holds in every
|
|
326
|
+
// state — including the unknown-consent deferral below. Only the
|
|
327
|
+
// reporter partition that flushes the outbox (ack-mode sources)
|
|
328
|
+
// prunes it.
|
|
329
|
+
if (batchCount === 0 && this.sources.some((source) => source.ack)) {
|
|
330
|
+
const prunedCount = deleteTelemetryOutboxEventsBefore(
|
|
331
|
+
Date.now() - OUTBOX_MAX_ROW_AGE_MS,
|
|
332
|
+
);
|
|
333
|
+
if (prunedCount > 0) {
|
|
334
|
+
log.debug(
|
|
335
|
+
{ prunedCount },
|
|
336
|
+
"Telemetry flush: pruned expired outbox rows",
|
|
337
|
+
);
|
|
338
|
+
}
|
|
267
339
|
}
|
|
268
340
|
|
|
269
341
|
// Skip when platform features are disabled (VELLUM_DISABLE_PLATFORM in
|
|
@@ -272,7 +344,7 @@ export class UsageTelemetryReporter {
|
|
|
272
344
|
// is a deployment/local-mode toggle, not a privacy opt-out, so the unsent
|
|
273
345
|
// backlog ships once the flag is cleared.
|
|
274
346
|
if (!arePlatformFeaturesEnabled()) {
|
|
275
|
-
return;
|
|
347
|
+
return settle("disabled");
|
|
276
348
|
}
|
|
277
349
|
|
|
278
350
|
// Skip when the flush-checkpoint store (telemetry DB) is unreachable.
|
|
@@ -283,10 +355,29 @@ export class UsageTelemetryReporter {
|
|
|
283
355
|
log.warn(
|
|
284
356
|
"Telemetry flush: flush-checkpoint store unavailable — skipping, will retry next cycle",
|
|
285
357
|
);
|
|
286
|
-
return;
|
|
358
|
+
return settle("checkpoint-unavailable");
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
// Tri-state consent gate: `true` ships, `false` purges, `"unknown"`
|
|
362
|
+
// defers.
|
|
363
|
+
const shareAnalytics = getRawShareAnalytics();
|
|
364
|
+
|
|
365
|
+
// `"unknown"` (boot before the first successful consent refresh, no
|
|
366
|
+
// platform session, no resolvable owner) is not an opt-out: nothing is
|
|
367
|
+
// sent, acked, discarded, or advanced — the same shape as the
|
|
368
|
+
// checkpoint-unavailable skip above. Purging here would silently
|
|
369
|
+
// destroy events recorded before the cache warmed up; instead the
|
|
370
|
+
// backlog ships (or is purged) on a later cycle once the 5-minute
|
|
371
|
+
// refresh loop resolves the cache to a confirmed value. Bounded by
|
|
372
|
+
// the age prune above.
|
|
373
|
+
if (shareAnalytics === "unknown") {
|
|
374
|
+
log.debug(
|
|
375
|
+
"Telemetry flush: share_analytics consent unknown — skipping, will retry next cycle",
|
|
376
|
+
);
|
|
377
|
+
return settle("unknown-consent");
|
|
287
378
|
}
|
|
288
379
|
|
|
289
|
-
// Respect opt-out: if the platform owner
|
|
380
|
+
// Respect a confirmed opt-out: if the platform owner declined
|
|
290
381
|
// `share_analytics` consent, skip the flush and advance watermarks so
|
|
291
382
|
// events recorded during the opt-out window are never sent
|
|
292
383
|
// retroactively. The daemon runs the reporter even when opted out
|
|
@@ -302,7 +393,7 @@ export class UsageTelemetryReporter {
|
|
|
302
393
|
// under builds predating the audit listener's write-time gate — new
|
|
303
394
|
// opted-out tool_invocations rows persist NULL telemetry columns and
|
|
304
395
|
// are unreportable by construction regardless of watermark timing.
|
|
305
|
-
if (
|
|
396
|
+
if (shareAnalytics === false) {
|
|
306
397
|
// Ack-mode sources drop their pending rows outright. Watermark-mode
|
|
307
398
|
// sources advance the timestamp watermarks and pin the ID watermarks
|
|
308
399
|
// to a sentinel that sorts above any real UUID. The sentinel (rather
|
|
@@ -322,7 +413,7 @@ export class UsageTelemetryReporter {
|
|
|
322
413
|
this.checkpoints.set(keys.at, now);
|
|
323
414
|
this.checkpoints.set(keys.id, OPT_OUT_WATERMARK_ID_SENTINEL);
|
|
324
415
|
}
|
|
325
|
-
return;
|
|
416
|
+
return settle("opted-out");
|
|
326
417
|
}
|
|
327
418
|
|
|
328
419
|
// Read each source's watermark (compound cursor: createdAt + id) and
|
|
@@ -371,7 +462,7 @@ export class UsageTelemetryReporter {
|
|
|
371
462
|
}
|
|
372
463
|
|
|
373
464
|
if (batches.every(({ batch }) => batch.events.length === 0)) {
|
|
374
|
-
return;
|
|
465
|
+
return settle("nothing-pending");
|
|
375
466
|
}
|
|
376
467
|
|
|
377
468
|
// Resolve auth context. We send authenticated-only: if no platform
|
|
@@ -388,7 +479,7 @@ export class UsageTelemetryReporter {
|
|
|
388
479
|
},
|
|
389
480
|
"Telemetry flush: no platform credentials — skipping, will retry next cycle",
|
|
390
481
|
);
|
|
391
|
-
return;
|
|
482
|
+
return settle("no-credentials");
|
|
392
483
|
}
|
|
393
484
|
log.debug(
|
|
394
485
|
{
|
|
@@ -437,9 +528,14 @@ export class UsageTelemetryReporter {
|
|
|
437
528
|
{ status: resp.status, body },
|
|
438
529
|
"Usage telemetry POST failed — will retry next cycle",
|
|
439
530
|
);
|
|
440
|
-
return;
|
|
531
|
+
return settle("post-failed");
|
|
441
532
|
}
|
|
442
|
-
|
|
533
|
+
|
|
534
|
+
// Read the ingest response so `persisted` reflects what actually landed;
|
|
535
|
+
// consuming the body also releases the connection.
|
|
536
|
+
const body = await resp.text();
|
|
537
|
+
acc.sent += typedEvents.length;
|
|
538
|
+
acc.persisted += parsePersistedCount(body, typedEvents.length);
|
|
443
539
|
|
|
444
540
|
// Acknowledge each source's shipped rows: ack-mode sources delete them
|
|
445
541
|
// (never touching flush_checkpoints), watermark-mode sources advance
|
|
@@ -456,12 +552,15 @@ export class UsageTelemetryReporter {
|
|
|
456
552
|
}
|
|
457
553
|
}
|
|
458
554
|
|
|
459
|
-
// If any source produced a full batch, there may be more — recurse
|
|
555
|
+
// If any source produced a full batch, there may be more — recurse,
|
|
556
|
+
// threading the accumulator so counts span every batch in this flush.
|
|
460
557
|
if (batches.some(({ batch }) => batch.fullBatch)) {
|
|
461
|
-
|
|
558
|
+
return this._doFlush(batchCount + 1, acc);
|
|
462
559
|
}
|
|
560
|
+
return flushed();
|
|
463
561
|
} catch (err) {
|
|
464
562
|
log.warn({ err }, "Usage telemetry flush error — non-fatal, will retry");
|
|
563
|
+
return settle("error");
|
|
465
564
|
}
|
|
466
565
|
}
|
|
467
566
|
}
|
|
@@ -8,9 +8,11 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
import type { ConsentState } from "../platform/consent-cache.js";
|
|
12
|
+
|
|
13
|
+
let shareAnalytics: ConsentState = true;
|
|
12
14
|
mock.module("../platform/consent-cache.js", () => ({
|
|
13
|
-
|
|
15
|
+
getRawShareAnalytics: () => shareAnalytics,
|
|
14
16
|
}));
|
|
15
17
|
|
|
16
18
|
let platformEnabled = true;
|
|
@@ -84,12 +86,18 @@ describe("emitWatchdogEventDirect", () => {
|
|
|
84
86
|
});
|
|
85
87
|
});
|
|
86
88
|
|
|
87
|
-
test("sends nothing when share_analytics is
|
|
89
|
+
test("sends nothing when share_analytics is a confirmed opt-out", async () => {
|
|
88
90
|
shareAnalytics = false;
|
|
89
91
|
await emitWatchdogEventDirect("sqlite_corrupted", { database: "main" });
|
|
90
92
|
expect(fetchCalls).toHaveLength(0);
|
|
91
93
|
});
|
|
92
94
|
|
|
95
|
+
test("emits while consent is unknown (no buffer to defer into)", async () => {
|
|
96
|
+
shareAnalytics = "unknown";
|
|
97
|
+
await emitWatchdogEventDirect("sqlite_corrupted", { database: "main" });
|
|
98
|
+
expect(fetchCalls).toHaveLength(1);
|
|
99
|
+
});
|
|
100
|
+
|
|
93
101
|
test("sends nothing when platform features are disabled", async () => {
|
|
94
102
|
platformEnabled = false;
|
|
95
103
|
await emitWatchdogEventDirect("sqlite_corrupted", { database: "main" });
|
|
@@ -20,7 +20,7 @@ import { v4 as uuid } from "uuid";
|
|
|
20
20
|
|
|
21
21
|
import { getPlatformOrganizationId, getPlatformUserId } from "../config/env.js";
|
|
22
22
|
import { VellumPlatformClient } from "../platform/client.js";
|
|
23
|
-
import {
|
|
23
|
+
import { getRawShareAnalytics } from "../platform/consent-cache.js";
|
|
24
24
|
import { arePlatformFeaturesEnabled } from "../platform/feature-gate.js";
|
|
25
25
|
import { getDeviceId } from "../util/device-id.js";
|
|
26
26
|
import { getLogger } from "../util/logger.js";
|
|
@@ -35,9 +35,11 @@ const TELEMETRY_INGEST_PATH = "/v1/telemetry/ingest/";
|
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
37
|
* POST one `watchdog` event directly to the platform, bypassing the SQLite
|
|
38
|
-
* buffer. Honors
|
|
39
|
-
*
|
|
40
|
-
*
|
|
38
|
+
* buffer. Honors a confirmed `share_analytics` opt-out and the
|
|
39
|
+
* platform-features gate. An unknown consent state emits: this path reports
|
|
40
|
+
* SQLite corruption and has no outbox to defer into, and platform ingest
|
|
41
|
+
* re-gates on consent authoritatively server-side. Never throws — the caller
|
|
42
|
+
* is on a query hot path.
|
|
41
43
|
*/
|
|
42
44
|
export async function emitWatchdogEventDirect(
|
|
43
45
|
checkName: string,
|
|
@@ -45,8 +47,11 @@ export async function emitWatchdogEventDirect(
|
|
|
45
47
|
value: number | null = null,
|
|
46
48
|
): Promise<void> {
|
|
47
49
|
try {
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
+
// Drop only on a confirmed opt-out; unknown emits (no buffer to defer
|
|
51
|
+
// into, and platform ingest re-gates on consent server-side).
|
|
52
|
+
if (getRawShareAnalytics() === false) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
50
55
|
if (!arePlatformFeaturesEnabled()) return;
|
|
51
56
|
|
|
52
57
|
// Authenticated-only. Null before the CES handshake resolves credentials;
|
|
@@ -43,7 +43,6 @@ export interface AppStoreWriter {
|
|
|
43
43
|
icon?: string;
|
|
44
44
|
schemaJson: string;
|
|
45
45
|
htmlDefinition: string;
|
|
46
|
-
formatVersion?: number;
|
|
47
46
|
}): AppDefinition;
|
|
48
47
|
updateApp(
|
|
49
48
|
id: string,
|
|
@@ -217,7 +216,6 @@ export async function executeAppCreate(
|
|
|
217
216
|
icon,
|
|
218
217
|
schemaJson,
|
|
219
218
|
htmlDefinition: "",
|
|
220
|
-
formatVersion: 2,
|
|
221
219
|
});
|
|
222
220
|
|
|
223
221
|
// Associate the app with its conversation at creation so subsequent
|
|
@@ -407,28 +405,17 @@ export async function executeAppRefresh(
|
|
|
407
405
|
// the client side.
|
|
408
406
|
const updated = store.updateApp(input.app_id, {});
|
|
409
407
|
|
|
410
|
-
//
|
|
411
|
-
//
|
|
412
|
-
//
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
const compileResult = await compileApp(appDir);
|
|
416
|
-
return {
|
|
417
|
-
content: JSON.stringify({
|
|
418
|
-
refreshed: true,
|
|
419
|
-
appId: updated.id,
|
|
420
|
-
name: updated.name,
|
|
421
|
-
...compileResultPayload(compileResult),
|
|
422
|
-
}),
|
|
423
|
-
isError: false,
|
|
424
|
-
};
|
|
425
|
-
}
|
|
426
|
-
|
|
408
|
+
// Compile explicitly so the LLM sees any errors (bad imports, syntax
|
|
409
|
+
// issues, etc.) instead of silently serving the stale scaffold placeholder
|
|
410
|
+
// from the initial app_create.
|
|
411
|
+
const appDir = getAppDirPath(input.app_id);
|
|
412
|
+
const compileResult = await compileApp(appDir);
|
|
427
413
|
return {
|
|
428
414
|
content: JSON.stringify({
|
|
429
415
|
refreshed: true,
|
|
430
416
|
appId: updated.id,
|
|
431
417
|
name: updated.name,
|
|
418
|
+
...compileResultPayload(compileResult),
|
|
432
419
|
}),
|
|
433
420
|
isError: false,
|
|
434
421
|
};
|
|
@@ -485,29 +472,17 @@ export async function executeAppUpdate(
|
|
|
485
472
|
// An empty update still bumps updatedAt, triggering a client surface refresh.
|
|
486
473
|
const updated = store.updateApp(input.app_id, updates);
|
|
487
474
|
|
|
488
|
-
//
|
|
489
|
-
//
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
const compileResult = await compileApp(appDir);
|
|
493
|
-
return {
|
|
494
|
-
content: JSON.stringify({
|
|
495
|
-
updated: true,
|
|
496
|
-
appId: updated.id,
|
|
497
|
-
name: updated.name,
|
|
498
|
-
description: updated.description,
|
|
499
|
-
...compileResultPayload(compileResult),
|
|
500
|
-
}),
|
|
501
|
-
isError: false,
|
|
502
|
-
};
|
|
503
|
-
}
|
|
504
|
-
|
|
475
|
+
// Recompile so the agent sees any errors from the edited source instead of
|
|
476
|
+
// serving a stale dist (mirrors app_refresh).
|
|
477
|
+
const appDir = getAppDirPath(input.app_id);
|
|
478
|
+
const compileResult = await compileApp(appDir);
|
|
505
479
|
return {
|
|
506
480
|
content: JSON.stringify({
|
|
507
481
|
updated: true,
|
|
508
482
|
appId: updated.id,
|
|
509
483
|
name: updated.name,
|
|
510
484
|
description: updated.description,
|
|
485
|
+
...compileResultPayload(compileResult),
|
|
511
486
|
}),
|
|
512
487
|
isError: false,
|
|
513
488
|
};
|
package/src/tools/executor.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { readFileSync } from "node:fs";
|
|
|
3
3
|
import { getConfig } from "../config/loader.js";
|
|
4
4
|
import { PermissionPrompter } from "../permissions/prompter.js";
|
|
5
5
|
import { RiskLevel } from "../permissions/types.js";
|
|
6
|
+
import { runInPluginContext } from "../plugins/plugin-execution-context.js";
|
|
6
7
|
import { TokenExpiredError } from "../security/token-manager.js";
|
|
7
8
|
import {
|
|
8
9
|
recordToolError,
|
|
@@ -19,6 +20,7 @@ import {
|
|
|
19
20
|
import { getWorkflowRunManager } from "../workflows/run-manager.js";
|
|
20
21
|
import { executeWithTimeout, safeTimeoutMs } from "./execution-timeout.js";
|
|
21
22
|
import { PermissionChecker } from "./permission-checker.js";
|
|
23
|
+
import { getToolOwner } from "./registry.js";
|
|
22
24
|
import { extractAndSanitize } from "./sensitive-output-placeholders.js";
|
|
23
25
|
import { applyEdit } from "./shared/filesystem/edit-engine.js";
|
|
24
26
|
import { sandboxPolicy } from "./shared/filesystem/path-policy.js";
|
|
@@ -223,8 +225,19 @@ export class ToolExecutor {
|
|
|
223
225
|
const toolTimeoutMs = computePerToolTimeoutMs(name, input);
|
|
224
226
|
const execContext = context;
|
|
225
227
|
|
|
228
|
+
// Mark the owning plugin as in context (via AsyncLocalStorage) so host
|
|
229
|
+
// APIs the tool reaches — e.g. resolveCredential — can scope to it. The
|
|
230
|
+
// context must be established around the `execute()` call itself so the
|
|
231
|
+
// returned promise carries the binding across its awaits. Non-plugin
|
|
232
|
+
// tools (default/skill/mcp/workspace) establish no context.
|
|
233
|
+
const owner = getToolOwner(name);
|
|
234
|
+
const execPromise =
|
|
235
|
+
owner?.kind === "plugin"
|
|
236
|
+
? runInPluginContext(owner.id, () => tool.execute(input, execContext))
|
|
237
|
+
: tool.execute(input, execContext);
|
|
238
|
+
|
|
226
239
|
let execResult: ToolExecutionResult = await executeWithTimeout(
|
|
227
|
-
|
|
240
|
+
execPromise,
|
|
228
241
|
toolTimeoutMs,
|
|
229
242
|
name,
|
|
230
243
|
);
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
assistantEventHub,
|
|
21
21
|
broadcastMessage,
|
|
22
22
|
} from "../../runtime/assistant-event-hub.js";
|
|
23
|
+
import { conversationRevealNonce } from "../../runtime/reveal-nonce.js";
|
|
23
24
|
import { redactSecrets } from "../../security/secret-scanner.js";
|
|
24
25
|
import { getLogger } from "../../util/logger.js";
|
|
25
26
|
import type { CompletedBackgroundTool } from "../background-tool-registry.js";
|
|
@@ -84,6 +85,8 @@ function buildHostBashProxyEnv(conversationId: string): Record<string, string> {
|
|
|
84
85
|
// Keep nested `assistant` CLI calls in host_bash aligned with the
|
|
85
86
|
// originating conversation so browser IPC can resolve live proxy context.
|
|
86
87
|
env.__CONVERSATION_ID = conversationId;
|
|
88
|
+
// Secret binding for reveal-derived chat authority — see reveal-nonce.ts.
|
|
89
|
+
env.__REVEAL_NONCE = conversationRevealNonce(conversationId);
|
|
87
90
|
return env;
|
|
88
91
|
}
|
|
89
92
|
|
|
@@ -438,6 +441,7 @@ export const hostShellTool = {
|
|
|
438
441
|
// Match `bash` tool behavior so nested assistant CLI calls can bind to
|
|
439
442
|
// the active conversation when running through host_bash.
|
|
440
443
|
hostEnv.__CONVERSATION_ID = context.conversationId;
|
|
444
|
+
hostEnv.__REVEAL_NONCE = conversationRevealNonce(context.conversationId);
|
|
441
445
|
|
|
442
446
|
if (background) {
|
|
443
447
|
// Check the registry limit BEFORE spawning so we never leak an
|
|
@@ -496,7 +500,7 @@ export const hostShellTool = {
|
|
|
496
500
|
let completed = false;
|
|
497
501
|
|
|
498
502
|
child.on("close", (code) => {
|
|
499
|
-
if (completed) return;
|
|
503
|
+
if (completed) {return;}
|
|
500
504
|
completed = true;
|
|
501
505
|
clearTimeout(timer);
|
|
502
506
|
const stdout = Buffer.concat(stdoutChunks).toString();
|
|
@@ -568,7 +572,7 @@ export const hostShellTool = {
|
|
|
568
572
|
});
|
|
569
573
|
|
|
570
574
|
child.on("error", (err) => {
|
|
571
|
-
if (completed) return;
|
|
575
|
+
if (completed) {return;}
|
|
572
576
|
completed = true;
|
|
573
577
|
clearTimeout(timer);
|
|
574
578
|
const status = aborted ? "cancelled" : "failed";
|
|
@@ -7,7 +7,7 @@ let mockFirecrawlSecureKey: string | undefined;
|
|
|
7
7
|
|
|
8
8
|
/** Seed the active web-fetch provider into the workspace config for real. */
|
|
9
9
|
function seedWebFetch(provider: string): void {
|
|
10
|
-
setConfig("services", { "web-fetch": {
|
|
10
|
+
setConfig("services", { "web-fetch": { provider } });
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
mock.module("../../../security/secure-keys.js", () => ({
|
|
@@ -212,6 +212,31 @@ describe("web_fetch activityMetadata", () => {
|
|
|
212
212
|
expect(result.status ?? "").not.toContain("JavaScript-rendered");
|
|
213
213
|
});
|
|
214
214
|
|
|
215
|
+
test("does not flag mayRequireJavaScript for markup-heavy pages that still yield substantial text", async () => {
|
|
216
|
+
// Mirrors a server-rendered GitHub PR page: <5% text-to-byte ratio, yet
|
|
217
|
+
// the extraction is complete — thousands of chars of real content.
|
|
218
|
+
const paragraph =
|
|
219
|
+
"<p>" +
|
|
220
|
+
"The quick brown fox jumps over the lazy dog. ".repeat(140) +
|
|
221
|
+
"</p>";
|
|
222
|
+
const scriptPayload = `var x = ${JSON.stringify("a".repeat(200_000))};`;
|
|
223
|
+
const body =
|
|
224
|
+
"<!doctype html><html><head><title>Article</title></head>" +
|
|
225
|
+
`<body>${paragraph}<script>${scriptPayload}</script></body></html>`;
|
|
226
|
+
globalThis.fetch = (async () =>
|
|
227
|
+
new Response(body, {
|
|
228
|
+
status: 200,
|
|
229
|
+
headers: { "content-type": "text/html; charset=utf-8" },
|
|
230
|
+
})) as unknown as typeof globalThis.fetch;
|
|
231
|
+
|
|
232
|
+
const result = await executeWithMockFetch({ url: "https://example.com/" });
|
|
233
|
+
expect(result.isError).toBe(false);
|
|
234
|
+
|
|
235
|
+
const meta = result.activityMetadata?.webFetch;
|
|
236
|
+
expect(meta?.mayRequireJavaScript).toBeUndefined();
|
|
237
|
+
expect(result.status ?? "").not.toContain("JavaScript-rendered");
|
|
238
|
+
});
|
|
239
|
+
|
|
215
240
|
test("populates metadata for blocked private-network targets", async () => {
|
|
216
241
|
const result = await executeWithMockFetch({
|
|
217
242
|
url: "http://127.0.0.1/admin",
|
|
@@ -945,10 +945,14 @@ export async function executeWebFetch(
|
|
|
945
945
|
}
|
|
946
946
|
// Detect likely JS-rendered SPAs: text is absolutely tiny, or a non-trivial
|
|
947
947
|
// HTML payload compresses to almost nothing (a shell page whose meaningful
|
|
948
|
-
// content is painted after fetch + document.body rewrite).
|
|
948
|
+
// content is painted after fetch + document.body rewrite). The ratio arm
|
|
949
|
+
// also requires a small absolute yield — markup-heavy pages (e.g. GitHub)
|
|
950
|
+
// extract at <5% while still producing thousands of chars of complete text.
|
|
949
951
|
const lowAbsolute = processed.length < 200;
|
|
950
952
|
const lowRatio =
|
|
951
|
-
|
|
953
|
+
processed.length < 5_000 &&
|
|
954
|
+
body.bytesRead >= 10_000 &&
|
|
955
|
+
processed.length / body.bytesRead < 0.05;
|
|
952
956
|
const mayRequireJavaScript = html && !rawMode && (lowAbsolute || lowRatio);
|
|
953
957
|
if (mayRequireJavaScript) {
|
|
954
958
|
const pct =
|
|
@@ -3,6 +3,7 @@ import { randomUUID } from "node:crypto";
|
|
|
3
3
|
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
5
|
|
|
6
|
+
import { conversationRevealNonce } from "../../runtime/reveal-nonce.js";
|
|
6
7
|
import { computeSkillVersionHash } from "../../skills/version-hash.js";
|
|
7
8
|
import { safeStringSlice } from "../../util/unicode.js";
|
|
8
9
|
import { buildSanitizedEnv } from "../terminal/safe-env.js";
|
|
@@ -148,6 +149,8 @@ function spawnRunner(
|
|
|
148
149
|
conversationId: context.conversationId,
|
|
149
150
|
});
|
|
150
151
|
env.__CONVERSATION_ID = context.conversationId;
|
|
152
|
+
// Secret binding for reveal-derived chat authority — see reveal-nonce.ts.
|
|
153
|
+
env.__REVEAL_NONCE = conversationRevealNonce(context.conversationId);
|
|
151
154
|
|
|
152
155
|
const child = spawn(wrapped.command, wrapped.args, {
|
|
153
156
|
cwd: runDir,
|
|
@@ -255,7 +258,7 @@ function parseSkillResult(
|
|
|
255
258
|
let searchFrom = stdout.length;
|
|
256
259
|
while (searchFrom > 0) {
|
|
257
260
|
const markerIdx = stdout.lastIndexOf("__skill_result", searchFrom - 1);
|
|
258
|
-
if (markerIdx === -1) break;
|
|
261
|
+
if (markerIdx === -1) {break;}
|
|
259
262
|
|
|
260
263
|
const lineStart = stdout.lastIndexOf("\n", markerIdx) + 1;
|
|
261
264
|
const lineEnd = stdout.indexOf("\n", markerIdx);
|
|
@@ -279,7 +282,7 @@ function parseSkillResult(
|
|
|
279
282
|
searchFrom = stdout.length;
|
|
280
283
|
while (searchFrom > 0) {
|
|
281
284
|
const markerIdx = stdout.lastIndexOf("__skill_error", searchFrom - 1);
|
|
282
|
-
if (markerIdx === -1) break;
|
|
285
|
+
if (markerIdx === -1) {break;}
|
|
283
286
|
|
|
284
287
|
const lineStart = stdout.lastIndexOf("\n", markerIdx) + 1;
|
|
285
288
|
const lineEnd = stdout.indexOf("\n", markerIdx);
|
|
@@ -153,20 +153,16 @@ export async function executeSubagentSpawn(
|
|
|
153
153
|
// profile of the call site that invoked us. That last fallback is what makes
|
|
154
154
|
// a subagent match its invoker when the invoking turn ran purely on its
|
|
155
155
|
// call-site default — the workspace `activeProfile` for `mainAgent`, or the
|
|
156
|
-
// call site's own
|
|
157
|
-
//
|
|
158
|
-
//
|
|
159
|
-
//
|
|
160
|
-
// that only surfaces with no `activeProfile` set and a hand-tuned call-site
|
|
161
|
-
// profile.
|
|
156
|
+
// call site's own winning default (a `llm.callSites.<callSite>` pin or the
|
|
157
|
+
// catalog intent, e.g. `cost-optimized`) for a heartbeat/background
|
|
158
|
+
// invoker (`resolveDefaultProfileKey` runs the same winner selection as
|
|
159
|
+
// dispatch, minus a per-turn override).
|
|
162
160
|
//
|
|
163
161
|
// An explicit `llm.callSites.subagentSpawn` profile must still win over
|
|
164
162
|
// the invoker-default tier: that tier is a matching heuristic, not a user
|
|
165
|
-
// choice, and any override outranks the call-site pin under
|
|
166
|
-
//
|
|
167
|
-
//
|
|
168
|
-
// override already lost to the pin, so this guard is behavior-identical
|
|
169
|
-
// there.) An explicit `inference_profile` argument keeps
|
|
163
|
+
// choice, and any override outranks the call-site pin under single-winner
|
|
164
|
+
// resolution — so the heuristic is only forwarded when no explicit pin
|
|
165
|
+
// exists. An explicit `inference_profile` argument keeps
|
|
170
166
|
// `forceOverrideProfile` and wins outright; the row read short-circuits
|
|
171
167
|
// to `undefined` for the background subagent conversation and for tool
|
|
172
168
|
// calls outside an agent-loop turn.
|
|
@@ -6,6 +6,7 @@ import type { BackgroundToolCompleted } from "../../daemon/message-types/backgro
|
|
|
6
6
|
import { RiskLevel } from "../../permissions/types.js";
|
|
7
7
|
import { wakeAgentForOpportunity } from "../../runtime/agent-wake.js";
|
|
8
8
|
import { broadcastMessage } from "../../runtime/assistant-event-hub.js";
|
|
9
|
+
import { conversationRevealNonce } from "../../runtime/reveal-nonce.js";
|
|
9
10
|
import { redactSecrets } from "../../security/secret-scanner.js";
|
|
10
11
|
import { getLogger } from "../../util/logger.js";
|
|
11
12
|
import { getDataDir } from "../../util/platform.js";
|
|
@@ -271,6 +272,8 @@ export const shellTool = {
|
|
|
271
272
|
|
|
272
273
|
const env = buildSanitizedEnv();
|
|
273
274
|
env.__CONVERSATION_ID = context.conversationId;
|
|
275
|
+
// Secret binding for reveal-derived chat authority — see reveal-nonce.ts.
|
|
276
|
+
env.__REVEAL_NONCE = conversationRevealNonce(context.conversationId);
|
|
274
277
|
// Surface the resolving model to assistant CLI commands so they can tailor
|
|
275
278
|
// remediation guidance for weak open models (see isWeakOpenModel).
|
|
276
279
|
if (context.attribution?.resolvedModel) {
|
|
@@ -337,7 +340,7 @@ export const shellTool = {
|
|
|
337
340
|
let completed = false;
|
|
338
341
|
|
|
339
342
|
child.on("close", (code, signal) => {
|
|
340
|
-
if (completed) return;
|
|
343
|
+
if (completed) {return;}
|
|
341
344
|
completed = true;
|
|
342
345
|
clearTimeout(timer);
|
|
343
346
|
removeBackgroundTool(bgId);
|
|
@@ -427,7 +430,7 @@ export const shellTool = {
|
|
|
427
430
|
});
|
|
428
431
|
|
|
429
432
|
child.on("error", (err) => {
|
|
430
|
-
if (completed) return;
|
|
433
|
+
if (completed) {return;}
|
|
431
434
|
completed = true;
|
|
432
435
|
clearTimeout(timer);
|
|
433
436
|
removeBackgroundTool(bgId);
|
|
@@ -7,7 +7,6 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { recallTool, rememberTool } from "../plugins/defaults/memory/tools.js";
|
|
10
|
-
import { appOpenTool } from "./apps/definitions.js";
|
|
11
10
|
import { askQuestionTool } from "./ask-question/ask-question-tool.js";
|
|
12
11
|
import { fileEditTool } from "./filesystem/edit.js";
|
|
13
12
|
import { fileListTool } from "./filesystem/list.js";
|
|
@@ -69,5 +68,4 @@ export const explicitTools: ToolDefinition[] = [
|
|
|
69
68
|
uiShowTool,
|
|
70
69
|
uiUpdateTool,
|
|
71
70
|
uiDismissTool,
|
|
72
|
-
appOpenTool,
|
|
73
71
|
];
|