@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
|
@@ -4,6 +4,7 @@ const LIVE_VOICE_CLIENT_FRAME_TYPES = [
|
|
|
4
4
|
"ptt_release",
|
|
5
5
|
"interrupt",
|
|
6
6
|
"end",
|
|
7
|
+
"update_config",
|
|
7
8
|
] as const;
|
|
8
9
|
|
|
9
10
|
type LiveVoiceClientFrameType = (typeof LIVE_VOICE_CLIENT_FRAME_TYPES)[number];
|
|
@@ -78,8 +79,36 @@ export interface LiveVoiceClientStartFrame {
|
|
|
78
79
|
* utterance boundaries and runs repeated utterance→turn cycles.
|
|
79
80
|
*/
|
|
80
81
|
readonly turnDetection?: LiveVoiceTurnDetectionMode;
|
|
82
|
+
/**
|
|
83
|
+
* Per-session override for the trailing-silence duration (ms) that ends the
|
|
84
|
+
* user's turn — the "pause before reply" the client exposes as a setting.
|
|
85
|
+
* Absent falls back to the daemon `liveVoice.vad.silenceThresholdMs` config.
|
|
86
|
+
* Only meaningful for `turnDetection: "server_vad"`. Bounded to
|
|
87
|
+
* [{@link MIN_SILENCE_THRESHOLD_MS}, {@link MAX_SILENCE_THRESHOLD_MS}].
|
|
88
|
+
*/
|
|
89
|
+
readonly silenceThresholdMs?: number;
|
|
90
|
+
/**
|
|
91
|
+
* Per-session override for the sustained speech (ms) required before the
|
|
92
|
+
* user's speech interrupts the assistant mid-reply — the "interrupt
|
|
93
|
+
* sensitivity" the client exposes as a setting (higher = harder to
|
|
94
|
+
* interrupt; 0 disables the guard so barge-in is immediate). Absent falls
|
|
95
|
+
* back to the daemon `liveVoice.vad.bargeInMinSpeechMs` config. Bounded to
|
|
96
|
+
* [{@link MIN_BARGE_IN_MIN_SPEECH_MS}, {@link MAX_BARGE_IN_MIN_SPEECH_MS}].
|
|
97
|
+
*/
|
|
98
|
+
readonly bargeInMinSpeechMs?: number;
|
|
81
99
|
}
|
|
82
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Bounds for the per-session turn-detection overrides carried on the start
|
|
103
|
+
* frame. Kept deliberately wide — they only reject nonsensical values (a
|
|
104
|
+
* sub-100 ms pause would end turns mid-word; a 10 s barge-in guard would make
|
|
105
|
+
* the assistant uninterruptible). The daemon config defaults sit inside these.
|
|
106
|
+
*/
|
|
107
|
+
export const MIN_SILENCE_THRESHOLD_MS = 100;
|
|
108
|
+
export const MAX_SILENCE_THRESHOLD_MS = 5_000;
|
|
109
|
+
export const MIN_BARGE_IN_MIN_SPEECH_MS = 0;
|
|
110
|
+
export const MAX_BARGE_IN_MIN_SPEECH_MS = 3_000;
|
|
111
|
+
|
|
83
112
|
export interface LiveVoiceClientAudioFrame {
|
|
84
113
|
readonly type: "audio";
|
|
85
114
|
readonly dataBase64: string;
|
|
@@ -97,12 +126,26 @@ export interface LiveVoiceClientEndFrame {
|
|
|
97
126
|
readonly type: "end";
|
|
98
127
|
}
|
|
99
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Mid-session tuning update: applies the same turn-detection knobs the start
|
|
131
|
+
* frame carries to the *running* session, so the client can retune "pause
|
|
132
|
+
* before reply" / "interrupt sensitivity" without reconnecting. Each field is
|
|
133
|
+
* optional and independently applied; the same bounds as the start frame apply.
|
|
134
|
+
* Only meaningful for `server_vad` sessions.
|
|
135
|
+
*/
|
|
136
|
+
export interface LiveVoiceClientUpdateConfigFrame {
|
|
137
|
+
readonly type: "update_config";
|
|
138
|
+
readonly silenceThresholdMs?: number;
|
|
139
|
+
readonly bargeInMinSpeechMs?: number;
|
|
140
|
+
}
|
|
141
|
+
|
|
100
142
|
export type LiveVoiceClientFrame =
|
|
101
143
|
| LiveVoiceClientStartFrame
|
|
102
144
|
| LiveVoiceClientAudioFrame
|
|
103
145
|
| LiveVoiceClientPttReleaseFrame
|
|
104
146
|
| LiveVoiceClientInterruptFrame
|
|
105
|
-
| LiveVoiceClientEndFrame
|
|
147
|
+
| LiveVoiceClientEndFrame
|
|
148
|
+
| LiveVoiceClientUpdateConfigFrame;
|
|
106
149
|
|
|
107
150
|
interface LiveVoiceBinaryAudioFrame {
|
|
108
151
|
readonly type: "binary_audio";
|
|
@@ -358,9 +401,60 @@ export function validateLiveVoiceClientFrame(
|
|
|
358
401
|
return { ok: true, frame: { type: "interrupt" } };
|
|
359
402
|
case "end":
|
|
360
403
|
return { ok: true, frame: { type: "end" } };
|
|
404
|
+
case "update_config":
|
|
405
|
+
return validateUpdateConfigFrame(value);
|
|
361
406
|
}
|
|
362
407
|
}
|
|
363
408
|
|
|
409
|
+
function validateUpdateConfigFrame(
|
|
410
|
+
value: Record<string, unknown>,
|
|
411
|
+
): LiveVoiceParseResult<LiveVoiceClientUpdateConfigFrame> {
|
|
412
|
+
if (
|
|
413
|
+
"silenceThresholdMs" in value &&
|
|
414
|
+
!isIntInRange(
|
|
415
|
+
value.silenceThresholdMs,
|
|
416
|
+
MIN_SILENCE_THRESHOLD_MS,
|
|
417
|
+
MAX_SILENCE_THRESHOLD_MS,
|
|
418
|
+
)
|
|
419
|
+
) {
|
|
420
|
+
return protocolError(
|
|
421
|
+
"invalid_field",
|
|
422
|
+
`update_config field silenceThresholdMs must be an integer in [${MIN_SILENCE_THRESHOLD_MS}, ${MAX_SILENCE_THRESHOLD_MS}]`,
|
|
423
|
+
"silenceThresholdMs",
|
|
424
|
+
"update_config",
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
if (
|
|
429
|
+
"bargeInMinSpeechMs" in value &&
|
|
430
|
+
!isIntInRange(
|
|
431
|
+
value.bargeInMinSpeechMs,
|
|
432
|
+
MIN_BARGE_IN_MIN_SPEECH_MS,
|
|
433
|
+
MAX_BARGE_IN_MIN_SPEECH_MS,
|
|
434
|
+
)
|
|
435
|
+
) {
|
|
436
|
+
return protocolError(
|
|
437
|
+
"invalid_field",
|
|
438
|
+
`update_config field bargeInMinSpeechMs must be an integer in [${MIN_BARGE_IN_MIN_SPEECH_MS}, ${MAX_BARGE_IN_MIN_SPEECH_MS}]`,
|
|
439
|
+
"bargeInMinSpeechMs",
|
|
440
|
+
"update_config",
|
|
441
|
+
);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
return {
|
|
445
|
+
ok: true,
|
|
446
|
+
frame: {
|
|
447
|
+
type: "update_config",
|
|
448
|
+
...(typeof value.silenceThresholdMs === "number"
|
|
449
|
+
? { silenceThresholdMs: value.silenceThresholdMs }
|
|
450
|
+
: {}),
|
|
451
|
+
...(typeof value.bargeInMinSpeechMs === "number"
|
|
452
|
+
? { bargeInMinSpeechMs: value.bargeInMinSpeechMs }
|
|
453
|
+
: {}),
|
|
454
|
+
},
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
364
458
|
export function parseLiveVoiceBinaryAudioFrame(
|
|
365
459
|
data: unknown,
|
|
366
460
|
): LiveVoiceParseResult<LiveVoiceBinaryAudioFrame> {
|
|
@@ -448,6 +542,38 @@ function validateStartFrame(
|
|
|
448
542
|
);
|
|
449
543
|
}
|
|
450
544
|
|
|
545
|
+
if (
|
|
546
|
+
"silenceThresholdMs" in value &&
|
|
547
|
+
!isIntInRange(
|
|
548
|
+
value.silenceThresholdMs,
|
|
549
|
+
MIN_SILENCE_THRESHOLD_MS,
|
|
550
|
+
MAX_SILENCE_THRESHOLD_MS,
|
|
551
|
+
)
|
|
552
|
+
) {
|
|
553
|
+
return protocolError(
|
|
554
|
+
"invalid_field",
|
|
555
|
+
`start frame field silenceThresholdMs must be an integer in [${MIN_SILENCE_THRESHOLD_MS}, ${MAX_SILENCE_THRESHOLD_MS}]`,
|
|
556
|
+
"silenceThresholdMs",
|
|
557
|
+
"start",
|
|
558
|
+
);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
if (
|
|
562
|
+
"bargeInMinSpeechMs" in value &&
|
|
563
|
+
!isIntInRange(
|
|
564
|
+
value.bargeInMinSpeechMs,
|
|
565
|
+
MIN_BARGE_IN_MIN_SPEECH_MS,
|
|
566
|
+
MAX_BARGE_IN_MIN_SPEECH_MS,
|
|
567
|
+
)
|
|
568
|
+
) {
|
|
569
|
+
return protocolError(
|
|
570
|
+
"invalid_field",
|
|
571
|
+
`start frame field bargeInMinSpeechMs must be an integer in [${MIN_BARGE_IN_MIN_SPEECH_MS}, ${MAX_BARGE_IN_MIN_SPEECH_MS}]`,
|
|
572
|
+
"bargeInMinSpeechMs",
|
|
573
|
+
"start",
|
|
574
|
+
);
|
|
575
|
+
}
|
|
576
|
+
|
|
451
577
|
return {
|
|
452
578
|
ok: true,
|
|
453
579
|
frame: {
|
|
@@ -459,10 +585,26 @@ function validateStartFrame(
|
|
|
459
585
|
...(isLiveVoiceTurnDetectionMode(value.turnDetection)
|
|
460
586
|
? { turnDetection: value.turnDetection }
|
|
461
587
|
: {}),
|
|
588
|
+
...(typeof value.silenceThresholdMs === "number"
|
|
589
|
+
? { silenceThresholdMs: value.silenceThresholdMs }
|
|
590
|
+
: {}),
|
|
591
|
+
...(typeof value.bargeInMinSpeechMs === "number"
|
|
592
|
+
? { bargeInMinSpeechMs: value.bargeInMinSpeechMs }
|
|
593
|
+
: {}),
|
|
462
594
|
},
|
|
463
595
|
};
|
|
464
596
|
}
|
|
465
597
|
|
|
598
|
+
/** Whether `value` is an integer within the inclusive `[min, max]` range. */
|
|
599
|
+
function isIntInRange(value: unknown, min: number, max: number): boolean {
|
|
600
|
+
return (
|
|
601
|
+
typeof value === "number" &&
|
|
602
|
+
Number.isInteger(value) &&
|
|
603
|
+
value >= min &&
|
|
604
|
+
value <= max
|
|
605
|
+
);
|
|
606
|
+
}
|
|
607
|
+
|
|
466
608
|
function validateAudioFrame(
|
|
467
609
|
value: Record<string, unknown>,
|
|
468
610
|
): LiveVoiceParseResult<LiveVoiceClientAudioFrame> {
|
|
@@ -46,8 +46,10 @@ afterAll(() => {
|
|
|
46
46
|
rmSync(ROOT, { recursive: true, force: true });
|
|
47
47
|
});
|
|
48
48
|
|
|
49
|
-
const {
|
|
49
|
+
const { createSourceWatchState, runSourceWatchPass } =
|
|
50
50
|
await import("../plugin-source-watch.js");
|
|
51
|
+
const { collectSourceVersions } =
|
|
52
|
+
await import("../../plugins/collect-source-versions.js");
|
|
51
53
|
const { getSourceVersionsPath, readSourceVersions } =
|
|
52
54
|
await import("../../plugins/source-versions.js");
|
|
53
55
|
|
|
@@ -25,14 +25,14 @@
|
|
|
25
25
|
import {
|
|
26
26
|
existsSync,
|
|
27
27
|
mkdirSync,
|
|
28
|
+
readdirSync,
|
|
28
29
|
renameSync,
|
|
29
30
|
statSync,
|
|
30
31
|
writeFileSync,
|
|
31
32
|
} from "node:fs";
|
|
32
|
-
import { readdirSync } from "node:fs";
|
|
33
33
|
import { join } from "node:path";
|
|
34
34
|
|
|
35
|
-
import {
|
|
35
|
+
import { collectSourceVersions } from "../plugins/collect-source-versions.js";
|
|
36
36
|
import type {
|
|
37
37
|
PluginSourceVersion,
|
|
38
38
|
SourceVersionsDocument,
|
|
@@ -43,69 +43,10 @@ import {
|
|
|
43
43
|
SOURCE_VERSIONS_FORMAT,
|
|
44
44
|
} from "../plugins/source-versions.js";
|
|
45
45
|
import { getLogger } from "../util/logger.js";
|
|
46
|
-
import {
|
|
47
|
-
getMonitoringDataDir,
|
|
48
|
-
getWorkspaceHooksDir,
|
|
49
|
-
getWorkspacePluginsDir,
|
|
50
|
-
} from "../util/platform.js";
|
|
46
|
+
import { getMonitoringDataDir } from "../util/platform.js";
|
|
51
47
|
|
|
52
48
|
const log = getLogger("plugin-source-watch");
|
|
53
49
|
|
|
54
|
-
/**
|
|
55
|
-
* Collect the current source version of every watched directory: each
|
|
56
|
-
* plugin directory under `<workspace>/plugins/` (same discovery rule as the
|
|
57
|
-
* daemon's plugin scan — a directory with a `package.json`), plus the
|
|
58
|
-
* standalone workspace hooks directory when it exists. Disabled plugins are
|
|
59
|
-
* fingerprinted too: consumers need fresh state the moment a plugin is
|
|
60
|
-
* re-enabled, and the `disabled` field is how they observe the toggle
|
|
61
|
-
* itself (dotfiles are excluded from the fingerprint).
|
|
62
|
-
*/
|
|
63
|
-
export function collectSourceVersions(): Record<string, PluginSourceVersion> {
|
|
64
|
-
const out: Record<string, PluginSourceVersion> = {};
|
|
65
|
-
|
|
66
|
-
const pluginsDir = getWorkspacePluginsDir();
|
|
67
|
-
let entries: string[] = [];
|
|
68
|
-
try {
|
|
69
|
-
entries = readdirSync(pluginsDir);
|
|
70
|
-
} catch {
|
|
71
|
-
// No plugins directory yet — nothing to watch there.
|
|
72
|
-
}
|
|
73
|
-
for (const entry of entries) {
|
|
74
|
-
if (entry.startsWith(".")) {
|
|
75
|
-
continue;
|
|
76
|
-
}
|
|
77
|
-
const dir = join(pluginsDir, entry);
|
|
78
|
-
try {
|
|
79
|
-
if (!statSync(dir).isDirectory()) {
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
} catch {
|
|
83
|
-
continue;
|
|
84
|
-
}
|
|
85
|
-
if (!existsSync(join(dir, "package.json"))) {
|
|
86
|
-
continue;
|
|
87
|
-
}
|
|
88
|
-
const snapshot = snapshotPluginSource(dir);
|
|
89
|
-
out[dir] = {
|
|
90
|
-
fingerprint: snapshot.fingerprint,
|
|
91
|
-
evictionPaths: snapshot.evictionPaths,
|
|
92
|
-
disabled: existsSync(join(dir, ".disabled")),
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const workspaceHooksDir = getWorkspaceHooksDir();
|
|
97
|
-
if (existsSync(workspaceHooksDir)) {
|
|
98
|
-
const snapshot = snapshotPluginSource(workspaceHooksDir);
|
|
99
|
-
out[workspaceHooksDir] = {
|
|
100
|
-
fingerprint: snapshot.fingerprint,
|
|
101
|
-
evictionPaths: snapshot.evictionPaths,
|
|
102
|
-
disabled: false,
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return out;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
50
|
/**
|
|
110
51
|
* Whether two version maps describe the same source state. Compares key
|
|
111
52
|
* sets, fingerprints, and disabled flags; eviction paths are derived from
|
|
@@ -550,6 +550,6 @@ from the unified `llm` block. Override defaults by setting either of:
|
|
|
550
550
|
| `llm.callSites.notificationDecision` | object | _(unset)_ | Provider/model/effort/etc. override for the decision engine call site |
|
|
551
551
|
| `llm.callSites.preferenceExtraction` | object | _(unset)_ | Provider/model/effort/etc. override for the preference extractor call site |
|
|
552
552
|
|
|
553
|
-
When a call site override is unset, the resolver falls back to
|
|
553
|
+
When a call site override is unset, the resolver falls back to the shipped call-site default profile.
|
|
554
554
|
|
|
555
555
|
The notification pipeline is always active -- signals are processed and dispatched as soon as the daemon is running. The audit trail (events, decisions, deliveries) is written for every signal.
|
|
@@ -6,7 +6,10 @@ import { getIsContainerized } from "../config/env-registry.js";
|
|
|
6
6
|
import { getConfig } from "../config/loader.js";
|
|
7
7
|
import { loadSkillCatalog, resolveSkillSelector } from "../config/skills.js";
|
|
8
8
|
import { ipcClassifyRisk } from "../ipc/gateway-client.js";
|
|
9
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
MEMORY_RETROSPECTIVE_ORIGIN,
|
|
11
|
+
SKILL_MANAGEMENT_SKILL_ID,
|
|
12
|
+
} from "../plugins/defaults/memory/memory-retrospective-constants.js";
|
|
10
13
|
import { indexCatalogById } from "../skills/include-graph.js";
|
|
11
14
|
import { getSkillRoots } from "../skills/path-classifier.js";
|
|
12
15
|
import { computeTransitiveSkillVersionHash } from "../skills/transitive-version-hash.js";
|
|
@@ -718,7 +721,10 @@ export async function classifyRisk(
|
|
|
718
721
|
workingDir,
|
|
719
722
|
manifestOverride,
|
|
720
723
|
);
|
|
721
|
-
const gatewayResult = await ipcClassifyRisk(ipcParams);
|
|
724
|
+
const gatewayResult = await ipcClassifyRisk(ipcParams, signal);
|
|
725
|
+
// A mid-retry cancellation should surface as an AbortError, not the
|
|
726
|
+
// misleading fail-closed "gateway unreachable" message.
|
|
727
|
+
signal?.throwIfAborted();
|
|
722
728
|
|
|
723
729
|
if (!gatewayResult) {
|
|
724
730
|
throw new Error(
|
|
@@ -799,8 +805,6 @@ export async function classifyRisk(
|
|
|
799
805
|
// The grant is intentionally narrow: it matches exactly these tools AND the
|
|
800
806
|
// retrospective origin on a v3-live assistant, so no interactive session, other
|
|
801
807
|
// origin, or non-v3-live install is affected.
|
|
802
|
-
const SKILL_MANAGEMENT_SKILL_ID = "skill-management";
|
|
803
|
-
|
|
804
808
|
function isRetrospectiveSkillAuthoringGrant(
|
|
805
809
|
toolName: string,
|
|
806
810
|
input: Record<string, unknown>,
|
|
@@ -11,10 +11,36 @@
|
|
|
11
11
|
import { describe, expect, test } from "bun:test";
|
|
12
12
|
|
|
13
13
|
import { initializeDb } from "../db-init.js";
|
|
14
|
+
import { migrationSteps } from "../steps.js";
|
|
14
15
|
|
|
15
16
|
describe("initializeDb — migrationsOk return contract", () => {
|
|
16
17
|
test("resolves to { migrationsOk: true } on a clean init", async () => {
|
|
17
18
|
const result = await initializeDb();
|
|
18
19
|
expect(result).toEqual({ migrationsOk: true });
|
|
19
20
|
});
|
|
21
|
+
|
|
22
|
+
test("resolves to { migrationsOk: false } when a step is deferred", async () => {
|
|
23
|
+
// initializeDb reads the shared migrationSteps array, so appending a step
|
|
24
|
+
// whose dependsOn names a checkpoint that can never exist exercises the
|
|
25
|
+
// real deferral path end-to-end. The step is deferred (never run, never
|
|
26
|
+
// checkpointed), so removing it in finally leaves the ledger untouched.
|
|
27
|
+
const syntheticStep = {
|
|
28
|
+
name: "dbInitTestSyntheticDeferredStep",
|
|
29
|
+
run: () => {
|
|
30
|
+
throw new Error("deferred step must not run");
|
|
31
|
+
},
|
|
32
|
+
dependsOn: ["dbInitTestMissingPrerequisite"],
|
|
33
|
+
};
|
|
34
|
+
migrationSteps.push(syntheticStep);
|
|
35
|
+
try {
|
|
36
|
+
const result = await initializeDb();
|
|
37
|
+
expect(result).toEqual({ migrationsOk: false });
|
|
38
|
+
} finally {
|
|
39
|
+
migrationSteps.splice(migrationSteps.indexOf(syntheticStep), 1);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// The deferred step wrote nothing to the ledger, so a clean re-init
|
|
43
|
+
// reports ready again.
|
|
44
|
+
expect(await initializeDb()).toEqual({ migrationsOk: true });
|
|
45
|
+
});
|
|
20
46
|
});
|
|
@@ -236,6 +236,25 @@ export const messageMetadataSchema = z
|
|
|
236
236
|
* treat message text as organic user input.
|
|
237
237
|
*/
|
|
238
238
|
hidden: z.boolean().optional(),
|
|
239
|
+
/**
|
|
240
|
+
* How the send was initiated when the user message was sent on the
|
|
241
|
+
* user's behalf by the UI rather than typed by hand (`body.source` on
|
|
242
|
+
* `POST /v1/messages`). Current value: `"nav_redirect"` — a navigation
|
|
243
|
+
* shortcut redirected the user into chat with a pre-filled message.
|
|
244
|
+
* Read back by turn telemetry (`turn-events-store`) so analytics can
|
|
245
|
+
* separate UI-initiated turns from hand-typed ones, and by the queue
|
|
246
|
+
* drain to steer the turn. Kept as a plain string so new sources never
|
|
247
|
+
* fail metadata validation. Absent on hand-typed messages.
|
|
248
|
+
*/
|
|
249
|
+
userMessageSource: z.string().optional(),
|
|
250
|
+
/**
|
|
251
|
+
* Discriminates daemon-authored rows from ordinary turns.
|
|
252
|
+
* `"system_card"` marks pre-composed status cards (the /compact, /clean,
|
|
253
|
+
* and summarize-up-to results); see {@link SYSTEM_CARD_MESSAGE_KIND}.
|
|
254
|
+
* Kept as a plain string so unknown future kinds never fail metadata
|
|
255
|
+
* validation.
|
|
256
|
+
*/
|
|
257
|
+
messageKind: z.string().optional(),
|
|
239
258
|
/**
|
|
240
259
|
* Structured terminal record stamped onto a `<background_event
|
|
241
260
|
* source="background-tool">` wake so the web can rebuild the inline
|
|
@@ -291,6 +310,61 @@ export function isHiddenMessageMetadata(
|
|
|
291
310
|
return metadata?.hidden === true;
|
|
292
311
|
}
|
|
293
312
|
|
|
313
|
+
/**
|
|
314
|
+
* Shared accessor for the send-source tag on user-message metadata (see the
|
|
315
|
+
* `userMessageSource` field on {@link messageMetadataSchema}). Returns the
|
|
316
|
+
* tag (e.g. `"nav_redirect"`) or undefined when the message was typed by
|
|
317
|
+
* hand or predates the field.
|
|
318
|
+
*/
|
|
319
|
+
export function getUserMessageSourceMetadata(
|
|
320
|
+
metadata: Record<string, unknown> | null | undefined,
|
|
321
|
+
): string | undefined {
|
|
322
|
+
const source = metadata?.userMessageSource;
|
|
323
|
+
return typeof source === "string" && source.length > 0 ? source : undefined;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* `messageKind` value marking a daemon-authored system card — a pre-composed
|
|
328
|
+
* status reply (the /compact, /clean, and summarize-up-to result cards) that
|
|
329
|
+
* bypasses the agent loop. Cards render as standalone system notices, never
|
|
330
|
+
* as the assistant persona speaking, and never merge into adjacent assistant
|
|
331
|
+
* display turns.
|
|
332
|
+
*/
|
|
333
|
+
export const SYSTEM_CARD_MESSAGE_KIND = "system_card";
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Shared predicate for the system-card marker on assistant-message metadata
|
|
337
|
+
* (see the `messageKind` field on {@link messageMetadataSchema}). One
|
|
338
|
+
* definition so display merging, transcript rendering, and turn grouping
|
|
339
|
+
* cannot drift.
|
|
340
|
+
*/
|
|
341
|
+
export function isSystemCardMetadata(
|
|
342
|
+
metadata: Record<string, unknown> | null | undefined,
|
|
343
|
+
): boolean {
|
|
344
|
+
return metadata?.messageKind === SYSTEM_CARD_MESSAGE_KIND;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/**
|
|
348
|
+
* Row-level variant of {@link isSystemCardMetadata} for callers holding the
|
|
349
|
+
* raw persisted `metadata` JSON string. The single place the parse lives so
|
|
350
|
+
* display merging and turn grouping agree on what a card is.
|
|
351
|
+
*/
|
|
352
|
+
export function isSystemCardMessage(
|
|
353
|
+
role: string,
|
|
354
|
+
metadata: string | null,
|
|
355
|
+
): boolean {
|
|
356
|
+
if (role !== "assistant" || !metadata) {
|
|
357
|
+
return false;
|
|
358
|
+
}
|
|
359
|
+
try {
|
|
360
|
+
return isSystemCardMetadata(
|
|
361
|
+
JSON.parse(metadata) as Record<string, unknown>,
|
|
362
|
+
);
|
|
363
|
+
} catch {
|
|
364
|
+
return false;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
294
368
|
/**
|
|
295
369
|
* Parse a persisted message's metadata JSON against {@link messageMetadataSchema}
|
|
296
370
|
* — the single source of truth for its shape — returning the validated fields,
|
|
@@ -829,7 +903,7 @@ export function createConversation(
|
|
|
829
903
|
* web client's `crypto.randomUUID()` / `draft-<ts>-<hex>` drafts while
|
|
830
904
|
* rejecting anything with path separators, `..`, or other traversal vectors.
|
|
831
905
|
*/
|
|
832
|
-
const ADOPTABLE_CONVERSATION_ID_RE = /^[A-Za-z0-9_-]{1,128}$/;
|
|
906
|
+
export const ADOPTABLE_CONVERSATION_ID_RE = /^[A-Za-z0-9_-]{1,128}$/;
|
|
833
907
|
|
|
834
908
|
/**
|
|
835
909
|
* Ensure a `conversations` row exists for `id`, creating one with default
|
|
@@ -3830,6 +3904,12 @@ export function getAssistantMessageIdsInTurn(messageId: string): string[] {
|
|
|
3830
3904
|
return [messageId];
|
|
3831
3905
|
}
|
|
3832
3906
|
|
|
3907
|
+
// A system card is its own single-row group — its linked calls (e.g. the
|
|
3908
|
+
// summarize-up-to compaction call) never mix into a neighbouring turn.
|
|
3909
|
+
if (isSystemCardMessage(target.role, target.metadata)) {
|
|
3910
|
+
return [target.id];
|
|
3911
|
+
}
|
|
3912
|
+
|
|
3833
3913
|
// Walk backward from the target message to find the turn boundary.
|
|
3834
3914
|
// Limit to 50 rows — sufficient for even aggressive tool-use loops.
|
|
3835
3915
|
const backwardRows = db
|
|
@@ -3838,6 +3918,7 @@ export function getAssistantMessageIdsInTurn(messageId: string): string[] {
|
|
|
3838
3918
|
role: messages.role,
|
|
3839
3919
|
content: messages.content,
|
|
3840
3920
|
createdAt: messages.createdAt,
|
|
3921
|
+
metadata: messages.metadata,
|
|
3841
3922
|
})
|
|
3842
3923
|
.from(messages)
|
|
3843
3924
|
.where(
|
|
@@ -3855,6 +3936,12 @@ export function getAssistantMessageIdsInTurn(messageId: string): string[] {
|
|
|
3855
3936
|
|
|
3856
3937
|
for (const row of backwardRows) {
|
|
3857
3938
|
if (row.role === "assistant") {
|
|
3939
|
+
if (isSystemCardMessage(row.role, row.metadata)) {
|
|
3940
|
+
// A system card closes the groups on either side of it — rows
|
|
3941
|
+
// before the card belong to an earlier display group.
|
|
3942
|
+
boundaryCreatedAt = row.createdAt;
|
|
3943
|
+
break;
|
|
3944
|
+
}
|
|
3858
3945
|
assistantIds.push(row.id);
|
|
3859
3946
|
} else if (row.role === "user") {
|
|
3860
3947
|
if (isToolResultMessage(row.role, row.content)) {
|
|
@@ -3876,6 +3963,7 @@ export function getAssistantMessageIdsInTurn(messageId: string): string[] {
|
|
|
3876
3963
|
role: messages.role,
|
|
3877
3964
|
content: messages.content,
|
|
3878
3965
|
createdAt: messages.createdAt,
|
|
3966
|
+
metadata: messages.metadata,
|
|
3879
3967
|
})
|
|
3880
3968
|
.from(messages)
|
|
3881
3969
|
.where(
|
|
@@ -3890,6 +3978,15 @@ export function getAssistantMessageIdsInTurn(messageId: string): string[] {
|
|
|
3890
3978
|
|
|
3891
3979
|
for (const row of forwardRows) {
|
|
3892
3980
|
if (row.role === "assistant") {
|
|
3981
|
+
if (isSystemCardMessage(row.role, row.metadata)) {
|
|
3982
|
+
// A card that is the queried user message's only reply (e.g. the
|
|
3983
|
+
// /compact result) IS the turn's response; otherwise the card
|
|
3984
|
+
// closes the group.
|
|
3985
|
+
if (assistantIds.length === 0) {
|
|
3986
|
+
assistantIds.push(row.id);
|
|
3987
|
+
}
|
|
3988
|
+
break;
|
|
3989
|
+
}
|
|
3893
3990
|
if (!assistantIds.includes(row.id)) {
|
|
3894
3991
|
assistantIds.push(row.id);
|
|
3895
3992
|
}
|
|
@@ -3912,6 +4009,7 @@ export function getAssistantMessageIdsInTurn(messageId: string): string[] {
|
|
|
3912
4009
|
id: messages.id,
|
|
3913
4010
|
role: messages.role,
|
|
3914
4011
|
createdAt: messages.createdAt,
|
|
4012
|
+
metadata: messages.metadata,
|
|
3915
4013
|
})
|
|
3916
4014
|
.from(messages)
|
|
3917
4015
|
.where(
|
|
@@ -3925,7 +4023,11 @@ export function getAssistantMessageIdsInTurn(messageId: string): string[] {
|
|
|
3925
4023
|
.all();
|
|
3926
4024
|
|
|
3927
4025
|
for (const row of gapRows) {
|
|
3928
|
-
if (
|
|
4026
|
+
if (
|
|
4027
|
+
row.role === "assistant" &&
|
|
4028
|
+
!isSystemCardMessage(row.role, row.metadata) &&
|
|
4029
|
+
!assistantIds.includes(row.id)
|
|
4030
|
+
) {
|
|
3929
4031
|
assistantIds.push(row.id);
|
|
3930
4032
|
}
|
|
3931
4033
|
}
|
|
@@ -87,7 +87,7 @@ export async function initializeDb(): Promise<{ migrationsOk: boolean }> {
|
|
|
87
87
|
// broken migration doesn't prevent independent later ones from succeeding.
|
|
88
88
|
// The runner creates the checkpoint ledger, recovers crashed migrations, then
|
|
89
89
|
// records each step so an already-migrated database skips it on later boots.
|
|
90
|
-
const { applied, failed, skipped } = await runMigrationSteps(
|
|
90
|
+
const { applied, failed, skipped, deferred } = await runMigrationSteps(
|
|
91
91
|
database,
|
|
92
92
|
migrationSteps,
|
|
93
93
|
);
|
|
@@ -108,6 +108,13 @@ export async function initializeDb(): Promise<{ migrationsOk: boolean }> {
|
|
|
108
108
|
);
|
|
109
109
|
}
|
|
110
110
|
|
|
111
|
+
if (deferred.length > 0) {
|
|
112
|
+
log.error(
|
|
113
|
+
{ deferredMigrations: deferred, count: deferred.length },
|
|
114
|
+
`DB initialization completed with ${deferred.length} deferred migration(s) whose prerequisites are not applied`,
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
111
118
|
// A passing post-run validation is part of readiness: validateMigrationState
|
|
112
119
|
// flags schema inconsistencies (e.g. a completed step missing a declared
|
|
113
120
|
// dependsOn checkpoint) that no individual step body surfaces as a failure.
|
|
@@ -119,7 +126,11 @@ export async function initializeDb(): Promise<{ migrationsOk: boolean }> {
|
|
|
119
126
|
log.error({ err }, "validateMigrationState failed");
|
|
120
127
|
}
|
|
121
128
|
|
|
122
|
-
// migrationsOk
|
|
129
|
+
// migrationsOk requires no failed steps, no deferred steps, and a passing
|
|
123
130
|
// post-run validation, so an inconsistent schema keeps /readyz at 503.
|
|
124
|
-
|
|
131
|
+
// Deferrals count because a mis-declared dependsOn would otherwise leave a
|
|
132
|
+
// step silently never running while the daemon reports ready.
|
|
133
|
+
return {
|
|
134
|
+
migrationsOk: failed.length === 0 && deferred.length === 0 && validationOk,
|
|
135
|
+
};
|
|
125
136
|
}
|
|
@@ -38,8 +38,13 @@ export async function pruneOldLlmRequestLogsJob(
|
|
|
38
38
|
? rawRetention
|
|
39
39
|
: config.memory.cleanup.llmRequestLogRetentionMs;
|
|
40
40
|
|
|
41
|
-
// null means "keep forever" — skip pruning entirely
|
|
42
|
-
|
|
41
|
+
// null/0 means "keep forever" — skip pruning entirely. 0 is excluded (not
|
|
42
|
+
// just null) to match the scheduler in maybeEnqueueScheduledCleanupJobs,
|
|
43
|
+
// which no longer enqueues a retention-0 prune; guarding here too ensures a
|
|
44
|
+
// job left pending from before that fix does not wipe every log on its next
|
|
45
|
+
// run.
|
|
46
|
+
if (retentionMs === null || retentionMs === undefined || retentionMs <= 0)
|
|
47
|
+
return;
|
|
43
48
|
|
|
44
49
|
const cutoffMs = Math.floor(Date.now() - retentionMs);
|
|
45
50
|
if (!Number.isFinite(cutoffMs)) return;
|
|
@@ -104,9 +109,10 @@ export async function pruneOldToolInvocationsJob(
|
|
|
104
109
|
? rawRetention
|
|
105
110
|
: config.auditLog.retentionDays;
|
|
106
111
|
|
|
107
|
-
// 0 means disabled
|
|
108
|
-
//
|
|
109
|
-
|
|
112
|
+
// 0 (or any non-positive window) means disabled — keep forever. Guarding
|
|
113
|
+
// <= 0 mirrors the LLM-log prune and short-circuits before dispatching a
|
|
114
|
+
// pointless async query (rotateToolInvocations no-ops on this too).
|
|
115
|
+
if (retentionDays <= 0) return;
|
|
110
116
|
|
|
111
117
|
await rotateToolInvocations(retentionDays);
|
|
112
118
|
}
|
|
@@ -158,8 +164,10 @@ export function pruneOldConversationsJob(
|
|
|
158
164
|
? job.payload.retentionDays
|
|
159
165
|
: config.memory.cleanup.conversationRetentionDays;
|
|
160
166
|
|
|
161
|
-
// 0 means disabled
|
|
162
|
-
|
|
167
|
+
// 0 (or any non-positive window) means disabled — keep forever. Guarding
|
|
168
|
+
// <= 0 mirrors the LLM-log prune so a stray non-positive window can never
|
|
169
|
+
// produce a cutoff at/after `now` that deletes live conversations.
|
|
170
|
+
if (retentionDays <= 0) return;
|
|
163
171
|
|
|
164
172
|
const cutoffMs = Date.now() - retentionDays * 86_400_000;
|
|
165
173
|
|