@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
|
@@ -20,6 +20,7 @@ import type { MemoryV3SelectionLog } from "../../../../api/responses/memory-v3-s
|
|
|
20
20
|
import { getConfig } from "../../../../config/loader.js";
|
|
21
21
|
import { isMemoryV3Live } from "../../../../config/memory-v3-gate.js";
|
|
22
22
|
import { getDb, getSqliteFrom } from "../../../../persistence/db-connection.js";
|
|
23
|
+
import { memorySqliteOrNull } from "../memory-db.js";
|
|
23
24
|
import { getWorkspaceDir } from "../paths.js";
|
|
24
25
|
import { readPage } from "../v2/page-store.js";
|
|
25
26
|
import { capabilityOrDiskBody } from "./capabilities.js";
|
|
@@ -46,7 +47,11 @@ interface SelectionRow {
|
|
|
46
47
|
const SELECTION_COLUMNS = `turn, slug, source, pinned, section_ordinal, section_title`;
|
|
47
48
|
|
|
48
49
|
function rowsForTurn(conversationId: string, turn: number): SelectionRow[] {
|
|
49
|
-
|
|
50
|
+
const raw = memorySqliteOrNull("rowsForTurn");
|
|
51
|
+
if (!raw) {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
return raw
|
|
50
55
|
.query(
|
|
51
56
|
/*sql*/ `
|
|
52
57
|
SELECT ${SELECTION_COLUMNS} FROM memory_v3_selections
|
|
@@ -59,8 +64,12 @@ function rowsForTurn(conversationId: string, turn: number): SelectionRow[] {
|
|
|
59
64
|
|
|
60
65
|
function rowsForMessageIds(messageIds: string[]): SelectionRow[] {
|
|
61
66
|
if (messageIds.length === 0) return [];
|
|
67
|
+
const raw = memorySqliteOrNull("rowsForMessageIds");
|
|
68
|
+
if (!raw) {
|
|
69
|
+
return [];
|
|
70
|
+
}
|
|
62
71
|
const placeholders = messageIds.map(() => "?").join(", ");
|
|
63
|
-
return
|
|
72
|
+
return raw
|
|
64
73
|
.query(
|
|
65
74
|
/*sql*/ `
|
|
66
75
|
SELECT ${SELECTION_COLUMNS} FROM memory_v3_selections
|
|
@@ -249,18 +258,21 @@ function isSelectionSource(source: string): source is SelectionSource {
|
|
|
249
258
|
}
|
|
250
259
|
|
|
251
260
|
export function summarizeSelections(conversationId: string): SelectionSummary {
|
|
252
|
-
const
|
|
253
|
-
|
|
254
|
-
|
|
261
|
+
const raw = memorySqliteOrNull("summarizeSelections");
|
|
262
|
+
const rows = raw
|
|
263
|
+
? (raw
|
|
264
|
+
.query(
|
|
265
|
+
/*sql*/ `
|
|
255
266
|
SELECT turn, slug, source FROM memory_v3_selections
|
|
256
267
|
WHERE conversation_id = ?
|
|
257
268
|
`,
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
269
|
+
)
|
|
270
|
+
.all(conversationId) as Array<{
|
|
271
|
+
turn: number;
|
|
272
|
+
slug: string;
|
|
273
|
+
source: string;
|
|
274
|
+
}>)
|
|
275
|
+
: [];
|
|
264
276
|
|
|
265
277
|
const bySource = Object.fromEntries(
|
|
266
278
|
SELECTION_SOURCES.map((source) => [source, 0]),
|
|
@@ -30,9 +30,9 @@ import {
|
|
|
30
30
|
|
|
31
31
|
import { getConfig } from "../../../../config/loader.js";
|
|
32
32
|
import type { AssistantConfig } from "../../../../config/schema.js";
|
|
33
|
-
import { getDb, getSqliteFrom } from "../../../../persistence/db-connection.js";
|
|
34
33
|
import { stripCommentLines } from "../host-utils.js";
|
|
35
34
|
import { getLogger } from "../logging.js";
|
|
35
|
+
import { memorySqliteOrNull } from "../memory-db.js";
|
|
36
36
|
import { getWorkspaceDir, getWorkspacePromptPath } from "../paths.js";
|
|
37
37
|
import { getPageIndex } from "../v2/page-index.js";
|
|
38
38
|
import { readPage, renderPageContent } from "../v2/page-store.js";
|
|
@@ -229,15 +229,12 @@ async function initLanes(config: AssistantConfig): Promise<ShadowLanes> {
|
|
|
229
229
|
const coreSlugs = loadCoreSet(getWorkspaceDir()).filter((slug) =>
|
|
230
230
|
sectionIndex.byArticle.has(slug),
|
|
231
231
|
);
|
|
232
|
-
const hotSlugs = computeHotSet(
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
excludeSlugs: new Set(coreSlugs),
|
|
239
|
-
},
|
|
240
|
-
)
|
|
232
|
+
const hotSlugs = computeHotSet({
|
|
233
|
+
k: tuning.hotSetK,
|
|
234
|
+
halfLifeMs: config.memory.v3.hotSet.halfLifeDays * DAY_MS,
|
|
235
|
+
now: Date.now(),
|
|
236
|
+
excludeSlugs: new Set(coreSlugs),
|
|
237
|
+
})
|
|
241
238
|
.map((entry) => entry.slug)
|
|
242
239
|
.filter((slug) => sectionIndex.byArticle.has(slug));
|
|
243
240
|
|
|
@@ -320,18 +317,15 @@ async function initLanes(config: AssistantConfig): Promise<ShadowLanes> {
|
|
|
320
317
|
const learned = config.memory.v3.learnedEdges;
|
|
321
318
|
const learnedGraph =
|
|
322
319
|
tuning.learnedEdgesCap > 0 && learned.maxPerPage > 0
|
|
323
|
-
? computeLearnedEdgeGraph(
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
knownSlugs: new Set(sectionIndex.byArticle.keys()),
|
|
333
|
-
},
|
|
334
|
-
)
|
|
320
|
+
? computeLearnedEdgeGraph({
|
|
321
|
+
halfLifeMs: learned.halfLifeDays * DAY_MS,
|
|
322
|
+
minCount: learned.minCount,
|
|
323
|
+
npmiFloor: learned.npmiFloor,
|
|
324
|
+
maxPerPage: learned.maxPerPage,
|
|
325
|
+
now: Date.now(),
|
|
326
|
+
windowMs: LEARNED_EDGES_WINDOW_DAYS * DAY_MS,
|
|
327
|
+
knownSlugs: new Set(sectionIndex.byArticle.keys()),
|
|
328
|
+
})
|
|
335
329
|
: undefined;
|
|
336
330
|
// Ensuring the dense collection is best-effort: the needle + edge lanes and
|
|
337
331
|
// the core/hot prefix are in-memory and independent of Qdrant, so a Qdrant outage
|
|
@@ -530,7 +524,11 @@ export function attributeSelections(result: OrchestrateResult): SelectionRow[] {
|
|
|
530
524
|
});
|
|
531
525
|
}
|
|
532
526
|
|
|
533
|
-
/**
|
|
527
|
+
/**
|
|
528
|
+
* Write the attributed selection rows to `memory_v3_selections` over the
|
|
529
|
+
* dedicated memory connection. Best-effort: an unavailable memory database or
|
|
530
|
+
* a failed write drops the turn's log rows rather than affecting the turn.
|
|
531
|
+
*/
|
|
534
532
|
export function writeSelections(
|
|
535
533
|
conversationId: string,
|
|
536
534
|
turn: number,
|
|
@@ -539,30 +537,37 @@ export function writeSelections(
|
|
|
539
537
|
if (rows.length === 0) {
|
|
540
538
|
return;
|
|
541
539
|
}
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
540
|
+
try {
|
|
541
|
+
const raw = memorySqliteOrNull("writeSelections");
|
|
542
|
+
if (!raw) {
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
// PK is (conversation_id, turn, slug); OR REPLACE keeps the write
|
|
546
|
+
// idempotent if the same turn is observed twice (e.g. a retried turn).
|
|
547
|
+
// `message_id` is written NULL here (the assistant message does not exist
|
|
548
|
+
// at injection time) and stamped at turn end by
|
|
549
|
+
// `backfillMemoryV3SelectionMessageId`.
|
|
550
|
+
const stmt = raw.query(/*sql*/ `
|
|
551
|
+
INSERT OR REPLACE INTO memory_v3_selections (
|
|
552
|
+
conversation_id, turn, slug, source, pinned, created_at,
|
|
553
|
+
message_id, section_ordinal, section_title
|
|
554
|
+
) VALUES (?, ?, ?, ?, ?, ?, NULL, ?, ?)
|
|
555
|
+
`);
|
|
556
|
+
const now = Date.now();
|
|
557
|
+
for (const row of rows) {
|
|
558
|
+
stmt.run(
|
|
559
|
+
conversationId,
|
|
560
|
+
turn,
|
|
561
|
+
row.slug,
|
|
562
|
+
row.source,
|
|
563
|
+
row.pinned,
|
|
564
|
+
now,
|
|
565
|
+
row.sectionOrdinal,
|
|
566
|
+
row.sectionTitle,
|
|
567
|
+
);
|
|
568
|
+
}
|
|
569
|
+
} catch (err) {
|
|
570
|
+
log.warn({ err }, "failed to write memory-v3 selections; continuing");
|
|
566
571
|
}
|
|
567
572
|
}
|
|
568
573
|
|
|
@@ -579,12 +584,23 @@ export function backfillMemoryV3SelectionMessageId(
|
|
|
579
584
|
conversationId: string,
|
|
580
585
|
assistantMessageId: string,
|
|
581
586
|
): void {
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
587
|
+
try {
|
|
588
|
+
const raw = memorySqliteOrNull("backfillMemoryV3SelectionMessageId");
|
|
589
|
+
if (!raw) {
|
|
590
|
+
return;
|
|
591
|
+
}
|
|
592
|
+
raw
|
|
593
|
+
.query(
|
|
594
|
+
/*sql*/ `UPDATE memory_v3_selections SET message_id = ?
|
|
595
|
+
WHERE conversation_id = ? AND message_id IS NULL`,
|
|
596
|
+
)
|
|
597
|
+
.run(assistantMessageId, conversationId);
|
|
598
|
+
} catch (err) {
|
|
599
|
+
log.warn(
|
|
600
|
+
{ err },
|
|
601
|
+
"failed to backfill memory-v3 selection messageId; continuing",
|
|
602
|
+
);
|
|
603
|
+
}
|
|
588
604
|
}
|
|
589
605
|
|
|
590
606
|
/**
|
|
@@ -633,6 +649,7 @@ export async function observeTurn(
|
|
|
633
649
|
prefixCards: lanes.prefixCards,
|
|
634
650
|
needleK: tuning.needleK,
|
|
635
651
|
denseK: tuning.denseK,
|
|
652
|
+
realConceptPageCount: lanes.realConceptPageCount,
|
|
636
653
|
entityCap: v3.entity.cap,
|
|
637
654
|
replyQueryK: tuning.replyQueryK,
|
|
638
655
|
edgeSeeds: tuning.edgeSeedCount,
|
|
@@ -45,11 +45,12 @@
|
|
|
45
45
|
* the `post-model-call` chain — later hooks see (and may override) its decision.
|
|
46
46
|
*/
|
|
47
47
|
|
|
48
|
-
import
|
|
49
|
-
ContentBlock,
|
|
50
|
-
HookFunction,
|
|
51
|
-
|
|
52
|
-
|
|
48
|
+
import {
|
|
49
|
+
type ContentBlock,
|
|
50
|
+
type HookFunction,
|
|
51
|
+
INTERNAL_NUDGE_OUTPUT_SUPPRESSION,
|
|
52
|
+
type Message,
|
|
53
|
+
type PostModelCallContext,
|
|
53
54
|
} from "@vellumai/plugin-api";
|
|
54
55
|
|
|
55
56
|
import {
|
|
@@ -64,7 +65,9 @@ import {
|
|
|
64
65
|
* is genuinely still running.
|
|
65
66
|
*/
|
|
66
67
|
export const SURFACE_COMPLETION_NUDGE_TEXT =
|
|
67
|
-
'<system_notice>You showed the user a progress surface this turn (a task_progress card or a work_result) and are about to end the turn with it still marked in_progress. If that work is finished, advance it to a terminal state now — call ui_update to set its status to "completed" (or "failed"), or ui_dismiss it — so the user is not left watching a card spin forever. Do this only if the work it represents is actually done; if it is genuinely still running, leave it. Then give your final reply
|
|
68
|
+
'<system_notice>You showed the user a progress surface this turn (a task_progress card or a work_result) and are about to end the turn with it still marked in_progress. If that work is finished, advance it to a terminal state now — call ui_update to set its status to "completed" (or "failed"), or ui_dismiss it — so the user is not left watching a card spin forever. Do this only if the work it represents is actually done; if it is genuinely still running, leave it. Then give your final reply.' +
|
|
69
|
+
INTERNAL_NUDGE_OUTPUT_SUPPRESSION +
|
|
70
|
+
"</system_notice>";
|
|
68
71
|
|
|
69
72
|
/**
|
|
70
73
|
* Surface statuses that mean the progress surface has reached a terminal state
|
|
@@ -42,7 +42,7 @@
|
|
|
42
42
|
|
|
43
43
|
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
44
44
|
import { readFile } from "node:fs/promises";
|
|
45
|
-
import { join } from "node:path";
|
|
45
|
+
import { basename, join } from "node:path";
|
|
46
46
|
import { pathToFileURL } from "node:url";
|
|
47
47
|
|
|
48
48
|
import semver from "semver";
|
|
@@ -100,15 +100,6 @@ export interface LoadExternalPluginOptions {
|
|
|
100
100
|
readonly importTimeoutMs?: number;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
-
/**
|
|
104
|
-
* Strip the npm scope from a package name. `@vellumai/simple-memory` →
|
|
105
|
-
* `simple-memory`; an unscoped name passes through unchanged.
|
|
106
|
-
*/
|
|
107
|
-
export function stripScope(name: string): string {
|
|
108
|
-
const match = /^@[^/]+\/(.+)$/.exec(name);
|
|
109
|
-
return match ? match[1]! : name;
|
|
110
|
-
}
|
|
111
|
-
|
|
112
103
|
export function toToolNameSegment(value: string): string {
|
|
113
104
|
return value.replace(/[^a-zA-Z0-9]+/g, "_").replace(/^_+|_+$/g, "") || "tool";
|
|
114
105
|
}
|
|
@@ -229,7 +220,15 @@ async function buildPluginFromDir(pluginDir: string): Promise<Plugin> {
|
|
|
229
220
|
);
|
|
230
221
|
}
|
|
231
222
|
const pkg: PluginPackageJson = parsed.data;
|
|
232
|
-
|
|
223
|
+
// A plugin's identity is its install directory name — the slug the plugin
|
|
224
|
+
// was installed under (a marketplace slug or a GitHub path leaf). This is
|
|
225
|
+
// the identity every other surface uses: `plugins list`, enable/disable,
|
|
226
|
+
// per-conversation plugin scoping, and resident-skill `owner.id`. The
|
|
227
|
+
// `package.json` `name` is authored by the plugin and routinely differs
|
|
228
|
+
// from the slug, so keying runtime registration off it would desync the
|
|
229
|
+
// registry from the identity the user toggles and the scope filters match.
|
|
230
|
+
// The manifest is still required (schema-validated above) as the load gate.
|
|
231
|
+
const name = basename(pluginDir);
|
|
233
232
|
const version = pkg.version && pkg.version.length > 0 ? pkg.version : "0.0.0";
|
|
234
233
|
|
|
235
234
|
// Host-compat negotiation: plugins declare their plugin-api version
|
|
@@ -381,11 +380,12 @@ export async function loadExternalPlugin(
|
|
|
381
380
|
|
|
382
381
|
/**
|
|
383
382
|
* Parse a plugin's `package.json` manifest from disk. Returns the plugin
|
|
384
|
-
*
|
|
385
|
-
* missing, unparseable, or fails schema validation.
|
|
383
|
+
* identity (its install directory name) and version, or `undefined` when the
|
|
384
|
+
* `package.json` is missing, unparseable, or fails schema validation.
|
|
386
385
|
*
|
|
387
386
|
* Exported so the mtime cache can discover plugin identity without going
|
|
388
|
-
* through the full `buildExternalPlugin` path.
|
|
387
|
+
* through the full `buildExternalPlugin` path. The identity mirrors
|
|
388
|
+
* {@link buildPluginFromDir}: the directory name, not `package.json` `name`.
|
|
389
389
|
*/
|
|
390
390
|
export async function parsePluginManifest(
|
|
391
391
|
pluginDir: string,
|
|
@@ -411,7 +411,7 @@ export async function parsePluginManifest(
|
|
|
411
411
|
return undefined;
|
|
412
412
|
}
|
|
413
413
|
const pkg: PluginPackageJson = parsed.data;
|
|
414
|
-
const name =
|
|
414
|
+
const name = basename(pluginDir);
|
|
415
415
|
const version = pkg.version && pkg.version.length > 0 ? pkg.version : "0.0.0";
|
|
416
416
|
return { name, version };
|
|
417
417
|
}
|
|
@@ -58,6 +58,7 @@ import {
|
|
|
58
58
|
getWorkspaceHooksDir,
|
|
59
59
|
getWorkspacePluginsDir,
|
|
60
60
|
} from "../util/platform.js";
|
|
61
|
+
import { collectSourceVersions } from "./collect-source-versions.js";
|
|
61
62
|
import {
|
|
62
63
|
deriveToolName,
|
|
63
64
|
listSurfaceDir,
|
|
@@ -308,6 +309,52 @@ async function maybeReconcileFromSentinel(): Promise<void> {
|
|
|
308
309
|
}
|
|
309
310
|
}
|
|
310
311
|
|
|
312
|
+
/**
|
|
313
|
+
* Imperatively reconcile the plugin caches against the current on-disk state
|
|
314
|
+
* *now* — without waiting for the resource monitor to republish the
|
|
315
|
+
* source-versions sentinel or for the next hook dispatch to notice it.
|
|
316
|
+
*
|
|
317
|
+
* An install / uninstall / enable / disable materializes files on disk; the
|
|
318
|
+
* steady-state path picks that up eventually — the monitor republishes the
|
|
319
|
+
* sentinel and the dispatch-path gate applies the diff on the next turn. That
|
|
320
|
+
* is eventually-correct but not immediate: nothing runs a newly installed
|
|
321
|
+
* plugin's `init` until some later turn dispatches a hook. Callers that just
|
|
322
|
+
* changed the plugin set on disk (the install / uninstall routes, and the
|
|
323
|
+
* CLI's best-effort post-install poke) call this to bring the change up
|
|
324
|
+
* deterministically, so a freshly installed plugin's `init` fires as part of
|
|
325
|
+
* the install rather than at the next daemon boot.
|
|
326
|
+
*
|
|
327
|
+
* Reuses the same collector the monitor writes into the sentinel, so the map
|
|
328
|
+
* applied here is identical to the monitor's next publish — which then diffs to
|
|
329
|
+
* a no-op. Idempotent and safe to call redundantly: `applySourceVersions` only
|
|
330
|
+
* redeploys directories whose fingerprint moved, and activation is guarded so a
|
|
331
|
+
* plugin already up is never re-initialized. Never throws — a failure is
|
|
332
|
+
* contained inside `applySourceVersions` and logged there.
|
|
333
|
+
*
|
|
334
|
+
* Coordinates with the sentinel gate through the shared `reconcileInFlight`
|
|
335
|
+
* latch: it waits out any dispatch-driven reconcile already running, then
|
|
336
|
+
* claims the latch for its own apply, so the two paths never overlap.
|
|
337
|
+
*/
|
|
338
|
+
export async function reconcilePluginSourcesNow(): Promise<void> {
|
|
339
|
+
while (reconcileInFlight !== null) {
|
|
340
|
+
await reconcileInFlight;
|
|
341
|
+
}
|
|
342
|
+
// `applySourceVersions` contains its own failures, but the `collectSourceVersions()`
|
|
343
|
+
// walk that feeds it runs outside that guard — wrap the whole thing so an
|
|
344
|
+
// imperative reconcile never rejects into its callers (the install route must
|
|
345
|
+
// still return success for an install whose files already landed on disk).
|
|
346
|
+
reconcileInFlight = (async () => {
|
|
347
|
+
try {
|
|
348
|
+
await applySourceVersions(collectSourceVersions());
|
|
349
|
+
} catch (err) {
|
|
350
|
+
log.error({ err }, "imperative plugin reconcile failed");
|
|
351
|
+
}
|
|
352
|
+
})().finally(() => {
|
|
353
|
+
reconcileInFlight = null;
|
|
354
|
+
});
|
|
355
|
+
await reconcileInFlight;
|
|
356
|
+
}
|
|
357
|
+
|
|
311
358
|
/**
|
|
312
359
|
* Validate that a directory path from the sentinel is an allowed plugin
|
|
313
360
|
* source: either under the workspace plugins directory or the standalone
|
package/src/plugins/pipeline.ts
CHANGED
|
@@ -25,6 +25,7 @@ import { getHookEntriesFor } from "../hooks/registry.js";
|
|
|
25
25
|
import type { BaseHookContext } from "../hooks/types.js";
|
|
26
26
|
import { type HookName, HOOKS } from "../plugin-api/constants.js";
|
|
27
27
|
import { getLogger } from "../util/logger.js";
|
|
28
|
+
import { runInPluginContext } from "./plugin-execution-context.js";
|
|
28
29
|
import type { HookEntry } from "./types.js";
|
|
29
30
|
|
|
30
31
|
// ─── Hook runner ────────────────────────────────────────────────────────────
|
|
@@ -352,8 +353,15 @@ export async function runHook<TInput extends object>(
|
|
|
352
353
|
broadcast: makeHookBroadcast({ conversationId, hookName: name, owner }),
|
|
353
354
|
};
|
|
354
355
|
try {
|
|
356
|
+
// Mark the contributing plugin as in context so host APIs the hook
|
|
357
|
+
// reaches (e.g. resolveCredential) can scope to it. Standalone workspace
|
|
358
|
+
// hooks are not plugins and establish no context.
|
|
359
|
+
const invokeHook =
|
|
360
|
+
owner.kind === "plugin"
|
|
361
|
+
? () => runInPluginContext(owner.id, () => fn(draft))
|
|
362
|
+
: () => fn(draft);
|
|
355
363
|
const result = await callWithTimeout(
|
|
356
|
-
|
|
364
|
+
invokeHook,
|
|
357
365
|
HOOK_TIMEOUT_MS,
|
|
358
366
|
`plugin hook '${name}' (${owner.id}) timed out after ${HOOK_TIMEOUT_MS}ms`,
|
|
359
367
|
);
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin execution context — tracks which plugin's code is currently running.
|
|
3
|
+
*
|
|
4
|
+
* Host APIs exposed to plugins through `@vellumai/plugin-api` sometimes need to
|
|
5
|
+
* know *which* plugin is calling them so they can scope their behavior to that
|
|
6
|
+
* plugin (e.g. {@link ../plugin-api/resolve-credential.resolveCredential} limits
|
|
7
|
+
* a plugin to its own credentials). A plugin's manifest name is not threaded
|
|
8
|
+
* through every host call, so the pipeline that invokes a plugin's hook — and
|
|
9
|
+
* the tool executor that runs a plugin's tool — mark the plugin as "in context"
|
|
10
|
+
* for the duration of that invocation via an {@link AsyncLocalStorage}. Host
|
|
11
|
+
* APIs read {@link getCurrentPluginName} to recover it.
|
|
12
|
+
*
|
|
13
|
+
* The store propagates across `await` boundaries, so a plugin that awaits a
|
|
14
|
+
* host API deep inside its hook/tool body is still seen as in context. When no
|
|
15
|
+
* plugin is in context (host-internal callers, the CLI, tests), the store is
|
|
16
|
+
* empty and scoped APIs fall back to their unscoped behavior.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
20
|
+
|
|
21
|
+
export interface PluginExecutionContext {
|
|
22
|
+
/** Manifest name of the plugin whose hook or tool is currently executing. */
|
|
23
|
+
pluginName: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const storage = new AsyncLocalStorage<PluginExecutionContext>();
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Run `fn` with `pluginName` marked as the plugin currently in context. The
|
|
30
|
+
* returned value (including a promise) carries the context across its async
|
|
31
|
+
* continuations, so callers pass the promise straight to a timeout wrapper
|
|
32
|
+
* without losing the binding.
|
|
33
|
+
*/
|
|
34
|
+
export function runInPluginContext<T>(pluginName: string, fn: () => T): T {
|
|
35
|
+
return storage.run({ pluginName }, fn);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Name of the plugin whose hook or tool is currently executing, or `undefined`
|
|
40
|
+
* when no plugin is in context.
|
|
41
|
+
*/
|
|
42
|
+
export function getCurrentPluginName(): string | undefined {
|
|
43
|
+
return storage.getStore()?.pluginName;
|
|
44
|
+
}
|
|
@@ -41,10 +41,10 @@ export const PRESERVED_ENTRIES = [
|
|
|
41
41
|
] as const;
|
|
42
42
|
|
|
43
43
|
/**
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
* it:
|
|
44
|
+
* Generated app build output: `apps/<app>/dist`. This is compiled output (the
|
|
45
|
+
* plugin source watcher builds each multi-file app's `src/` into its sibling
|
|
46
|
+
* `dist/`), never tracked source, so — like {@link PRESERVED_ENTRIES} — every
|
|
47
|
+
* fingerprint walk excludes it:
|
|
48
48
|
*
|
|
49
49
|
* - the **live-reload** change detector (`./source-fingerprint.ts`), so the
|
|
50
50
|
* watcher's own compile does not read as a source change and re-trigger
|
|
@@ -52,27 +52,24 @@ export const PRESERVED_ENTRIES = [
|
|
|
52
52
|
* - the **install/drift** fingerprint (`../cli/lib/plugin-fingerprint.ts`), so
|
|
53
53
|
* generated output is not reported as drift/added against the pinned commit.
|
|
54
54
|
*
|
|
55
|
-
*
|
|
56
|
-
* (if it ships built code its hooks import)
|
|
55
|
+
* A path pattern rather than a bare name so it stays scoped to `apps/<app>/dist`
|
|
56
|
+
* — a plugin's own top-level `dist/` (if it ships built code its hooks import)
|
|
57
|
+
* is still tracked. Matched against the POSIX path relative to the plugin root.
|
|
57
58
|
*/
|
|
58
|
-
export
|
|
59
|
-
const parts = relDir.split("/");
|
|
60
|
-
return parts.length === 3 && parts[0] === "apps" && parts[2] === "dist";
|
|
61
|
-
}
|
|
59
|
+
export const GENERATED_APP_BUILD_DIR = /^apps\/[^/]+\/dist$/;
|
|
62
60
|
|
|
63
61
|
/** Options controlling which entries a {@link walkPluginTree} visits. */
|
|
64
62
|
export interface PluginTreeWalkOptions {
|
|
65
|
-
/** Top-level entry names to skip (e.g. {@link PRESERVED_ENTRIES}). */
|
|
66
|
-
readonly excludeRootEntries?: Iterable<string>;
|
|
67
|
-
/** Directory names skipped at any depth (e.g. `node_modules`). */
|
|
68
|
-
readonly excludeDirsAnywhere?: ReadonlySet<string>;
|
|
69
63
|
/**
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
* path
|
|
73
|
-
*
|
|
64
|
+
* Entries to exclude. A `string` matches a top-level entry by name (e.g.
|
|
65
|
+
* {@link PRESERVED_ENTRIES}); a `RegExp` matches any entry — at any depth —
|
|
66
|
+
* by its POSIX path relative to the walk root, and when it matches a
|
|
67
|
+
* directory the whole subtree is skipped (e.g. {@link
|
|
68
|
+
* GENERATED_APP_BUILD_DIR}).
|
|
74
69
|
*/
|
|
75
|
-
readonly
|
|
70
|
+
readonly excludeRootEntries?: Iterable<string | RegExp>;
|
|
71
|
+
/** Directory names skipped at any depth (e.g. `node_modules`). */
|
|
72
|
+
readonly excludeDirsAnywhere?: ReadonlySet<string>;
|
|
76
73
|
/** Skip entries whose name starts with `.`, at any depth. */
|
|
77
74
|
readonly excludeDotEntries?: boolean;
|
|
78
75
|
/**
|
|
@@ -94,7 +91,17 @@ export function walkPluginTree(
|
|
|
94
91
|
options: PluginTreeWalkOptions,
|
|
95
92
|
visit: (rel: string, abs: string) => void,
|
|
96
93
|
): void {
|
|
97
|
-
|
|
94
|
+
// Split the exclusion list: bare strings match a top-level entry name;
|
|
95
|
+
// RegExps match an entry's relative path at any depth.
|
|
96
|
+
const excludedRootNames = new Set<string>();
|
|
97
|
+
const excludePatterns: RegExp[] = [];
|
|
98
|
+
for (const entry of options.excludeRootEntries ?? []) {
|
|
99
|
+
if (typeof entry === "string") {
|
|
100
|
+
excludedRootNames.add(entry);
|
|
101
|
+
} else {
|
|
102
|
+
excludePatterns.push(entry);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
98
105
|
|
|
99
106
|
const walk = (relDir: string): void => {
|
|
100
107
|
const absDir = relDir ? join(root, relDir) : root;
|
|
@@ -109,7 +116,7 @@ export function walkPluginTree(
|
|
|
109
116
|
}
|
|
110
117
|
for (const entry of entries) {
|
|
111
118
|
const name = entry.name;
|
|
112
|
-
if (relDir === "" &&
|
|
119
|
+
if (relDir === "" && excludedRootNames.has(name)) {
|
|
113
120
|
continue;
|
|
114
121
|
}
|
|
115
122
|
if (options.excludeDotEntries === true && name.startsWith(".")) {
|
|
@@ -119,13 +126,13 @@ export function walkPluginTree(
|
|
|
119
126
|
continue;
|
|
120
127
|
}
|
|
121
128
|
const rel = relDir ? `${relDir}/${name}` : name;
|
|
129
|
+
if (excludePatterns.some((pattern) => pattern.test(rel))) {
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
122
132
|
if (entry.isDirectory()) {
|
|
123
133
|
if (options.excludeDirsAnywhere?.has(name) === true) {
|
|
124
134
|
continue;
|
|
125
135
|
}
|
|
126
|
-
if (options.excludeDir?.(rel) === true) {
|
|
127
|
-
continue;
|
|
128
|
-
}
|
|
129
136
|
walk(rel);
|
|
130
137
|
} else if (entry.isFile()) {
|
|
131
138
|
visit(rel, join(absDir, name));
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* Exclusions: dot-entries anywhere (`.disabled`, `.git`, …), `node_modules`
|
|
20
20
|
* anywhere (vendored deps change only through install flows, which recycle
|
|
21
21
|
* the plugin directory), generated app build output ({@link
|
|
22
|
-
*
|
|
22
|
+
* GENERATED_APP_BUILD_DIR}, so the watcher's own compile does not re-trigger a
|
|
23
23
|
* pass), and — at the plugin root only — the runtime-owned
|
|
24
24
|
* {@link PRESERVED_ENTRIES} (`data/`, `config.json`, `install-meta.json`),
|
|
25
25
|
* none of which are importable source. Symlinked entries inside the tree are
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
import { realpathSync, statSync } from "node:fs";
|
|
34
34
|
|
|
35
35
|
import {
|
|
36
|
-
|
|
36
|
+
GENERATED_APP_BUILD_DIR,
|
|
37
37
|
PRESERVED_ENTRIES,
|
|
38
38
|
walkPluginTree,
|
|
39
39
|
} from "./plugin-tree-walk.js";
|
|
@@ -82,9 +82,8 @@ export function snapshotPluginSource(pluginDir: string): SourceSnapshot {
|
|
|
82
82
|
walkPluginTree(
|
|
83
83
|
realRoot,
|
|
84
84
|
{
|
|
85
|
-
excludeRootEntries: PRESERVED_ENTRIES,
|
|
85
|
+
excludeRootEntries: [...PRESERVED_ENTRIES, GENERATED_APP_BUILD_DIR],
|
|
86
86
|
excludeDirsAnywhere: EXCLUDED_DIRS_ANYWHERE,
|
|
87
|
-
excludeDir: isGeneratedAppBuildDir,
|
|
88
87
|
excludeDotEntries: true,
|
|
89
88
|
bestEffort: true,
|
|
90
89
|
},
|
|
@@ -85,6 +85,8 @@ export interface NormalizedOnboarding {
|
|
|
85
85
|
occupation?: string;
|
|
86
86
|
commonWork: string[];
|
|
87
87
|
dailyTools: string[];
|
|
88
|
+
/** User-confirmed findings from onboarding research; absent when none. */
|
|
89
|
+
researchFindings?: string[];
|
|
88
90
|
tone?: string;
|
|
89
91
|
assistantName?: string;
|
|
90
92
|
priorAssistants?: string[];
|
|
@@ -127,6 +129,16 @@ export function normalizeOnboardingContext(
|
|
|
127
129
|
occupation: ctx.occupation?.trim() || undefined,
|
|
128
130
|
commonWork: normalizeTasks(ctx.tasks),
|
|
129
131
|
dailyTools: normalizeTools(ctx.tools),
|
|
132
|
+
// Findings are model-extracted from arbitrary web content and get written
|
|
133
|
+
// into persisted persona markdown as single bullets. Collapse ALL runs of
|
|
134
|
+
// whitespace (incl. newlines) so one finding can never mint extra lines —
|
|
135
|
+
// injected bullets, headings, or a `## ` that would corrupt the managed
|
|
136
|
+
// section boundary on later rewrites.
|
|
137
|
+
researchFindings: ctx.researchFindings?.length
|
|
138
|
+
? ctx.researchFindings
|
|
139
|
+
.map((finding) => finding.replace(/\s+/g, " ").trim())
|
|
140
|
+
.filter(Boolean)
|
|
141
|
+
: undefined,
|
|
130
142
|
tone: ctx.tone,
|
|
131
143
|
assistantName: ctx.assistantName,
|
|
132
144
|
googleConnected: ctx.googleConnected,
|
|
@@ -421,6 +421,14 @@ function buildOnboardingSection(normalized: NormalizedOnboarding): string {
|
|
|
421
421
|
if (normalized.dailyTools.length > 0) {
|
|
422
422
|
lines.push(`- **Daily tools:** ${normalized.dailyTools.join(", ")}`);
|
|
423
423
|
}
|
|
424
|
+
if (normalized.researchFindings?.length) {
|
|
425
|
+
lines.push(
|
|
426
|
+
"- **Research findings** (surfaced during onboarding, confirmed by the user):",
|
|
427
|
+
);
|
|
428
|
+
for (const finding of normalized.researchFindings) {
|
|
429
|
+
lines.push(` - ${finding}`);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
424
432
|
|
|
425
433
|
lines.push("");
|
|
426
434
|
return lines.join("\n");
|
|
@@ -83,7 +83,7 @@ These are enforcement rules, not advice.
|
|
|
83
83
|
|
|
84
84
|
Identity writes (IDENTITY.md, SOUL.md), user-profile writes, journal entries: all wait until the rail produces real signal, which is Moment 1 output at the earliest. None of them delay a user-visible response. None of them happen alongside the opening turn.
|
|
85
85
|
|
|
86
|
-
|
|
86
|
+
Your opener is the Port pitch — no other intake surface precedes it.
|
|
87
87
|
|
|
88
88
|
## Telemetry: tag your `ui_show` surfaces
|
|
89
89
|
|