@vellumai/assistant 0.6.4 → 0.6.5
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/.prettierignore +5 -0
- package/ARCHITECTURE.md +32 -36
- package/Dockerfile +12 -0
- package/README.md +3 -4
- package/bun.lock +8 -3
- package/docs/architecture/integrations.md +1 -20
- package/docs/architecture/security.md +16 -16
- package/docs/error-handling.md +111 -0
- package/docs/skills.md +10 -10
- package/docs/stt-provider-onboarding.md +2 -1
- package/knip.json +9 -2
- package/node_modules/@vellumai/ces-contracts/package.json +2 -1
- package/node_modules/@vellumai/ces-contracts/src/__tests__/trust-rules.test.ts +471 -0
- package/node_modules/@vellumai/ces-contracts/src/trust-rules.ts +398 -4
- package/node_modules/@vellumai/credential-storage/bun.lock +2 -2
- package/node_modules/@vellumai/credential-storage/package.json +2 -2
- package/node_modules/@vellumai/credential-storage/src/oauth-runtime.ts +20 -2
- package/node_modules/@vellumai/egress-proxy/bun.lock +2 -2
- package/node_modules/@vellumai/egress-proxy/package.json +2 -2
- package/openapi.yaml +123 -11
- package/package.json +6 -3
- package/scripts/generate-openapi.ts +50 -11
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +318 -0
- package/src/__tests__/agent-loop-sentry-hygiene.test.ts +137 -0
- package/src/__tests__/agent-loop.test.ts +112 -1
- package/src/__tests__/anthropic-error-formatting.test.ts +98 -0
- package/src/__tests__/anthropic-provider.test.ts +171 -2
- package/src/__tests__/approval-cascade.test.ts +31 -10
- package/src/__tests__/approval-routes-http.test.ts +134 -10
- package/src/__tests__/assistant-attachments.test.ts +44 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +29 -0
- package/src/__tests__/browser-fill-credential.test.ts +1 -1
- package/src/__tests__/browser-identifier-parity-guard.test.ts +53 -0
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +23 -33
- package/src/__tests__/browser-skill-endstate.test.ts +51 -182
- package/src/__tests__/btw-routes.test.ts +47 -1
- package/src/__tests__/call-controller.test.ts +1 -2
- package/src/__tests__/call-site-routing-provider.test.ts +214 -0
- package/src/__tests__/catalog-cache.test.ts +27 -4
- package/src/__tests__/channel-approval-routes.test.ts +4 -4
- package/src/__tests__/channel-reply-delivery.test.ts +300 -2
- package/src/__tests__/checker.test.ts +428 -501
- package/src/__tests__/cli-command-risk-guard.test.ts +30 -33
- package/src/__tests__/compaction-circuit-breaker.test.ts +336 -0
- package/src/__tests__/compaction.benchmark.test.ts +1 -1
- package/src/__tests__/config-analysis.test.ts +11 -28
- package/src/__tests__/config-loader-backfill.test.ts +174 -0
- package/src/__tests__/config-loader-corrupt.test.ts +183 -0
- package/src/__tests__/config-loader-quarantine-bulletin.test.ts +202 -0
- package/src/__tests__/config-schema-cmd.test.ts +11 -5
- package/src/__tests__/config-schema.test.ts +427 -114
- package/src/__tests__/config-watcher.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +72 -73
- package/src/__tests__/contacts-write.test.ts +4 -4
- package/src/__tests__/context-token-estimator.test.ts +191 -1
- package/src/__tests__/context-window-manager.test.ts +530 -2
- package/src/__tests__/conversation-abort-tool-results.test.ts +30 -16
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +61 -17
- package/src/__tests__/conversation-agent-loop.test.ts +412 -82
- package/src/__tests__/conversation-attachments.test.ts +1 -1
- package/src/__tests__/conversation-confirmation-signals.test.ts +30 -9
- package/src/__tests__/conversation-error.test.ts +37 -6
- package/src/__tests__/conversation-history-web-search.test.ts +6 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +36 -0
- package/src/__tests__/conversation-lifecycle.test.ts +336 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +27 -10
- package/src/__tests__/conversation-pre-run-repair.test.ts +30 -16
- package/src/__tests__/conversation-process-callsite.test.ts +306 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -16
- package/src/__tests__/conversation-queue.test.ts +41 -26
- package/src/__tests__/conversation-routes-disk-view.test.ts +29 -1
- package/src/__tests__/conversation-routes-slash-commands.test.ts +31 -3
- package/src/__tests__/conversation-runtime-assembly.test.ts +2735 -55
- package/src/__tests__/conversation-runtime-workspace.test.ts +12 -12
- package/src/__tests__/conversation-skill-tools.test.ts +12 -146
- package/src/__tests__/conversation-slash-queue.test.ts +34 -19
- package/src/__tests__/conversation-slash-unknown.test.ts +30 -16
- package/src/__tests__/conversation-speed-override.test.ts +30 -11
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1035 -0
- package/src/__tests__/conversation-surfaces-standalone.test.ts +630 -0
- package/src/__tests__/conversation-title-service.test.ts +2 -2
- package/src/__tests__/conversation-tool-setup-batch-authorized.test.ts +1 -1
- package/src/__tests__/conversation-unread-route.test.ts +2 -2
- package/src/__tests__/conversation-usage.test.ts +3 -1
- package/src/__tests__/conversation-workspace-cache-state.test.ts +31 -10
- package/src/__tests__/conversation-workspace-injection.test.ts +43 -15
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +44 -16
- package/src/__tests__/credential-broker-browser-fill.test.ts +110 -0
- package/src/__tests__/credential-security-invariants.test.ts +3 -0
- package/src/__tests__/credential-storage-oauth-compat.test.ts +18 -0
- package/src/__tests__/credential-storage-static-compat.test.ts +28 -0
- package/src/__tests__/credential-vault-unit.test.ts +135 -19
- package/src/__tests__/credentials-cli.test.ts +1 -9
- package/src/__tests__/cross-provider-web-search.test.ts +84 -0
- package/src/__tests__/daemon-server-persist-and-process-callsite.test.ts +92 -0
- package/src/__tests__/delete-propagation.test.ts +437 -0
- package/src/__tests__/dm-backfill.test.ts +417 -0
- package/src/__tests__/dm-persistence.test.ts +227 -0
- package/src/__tests__/edit-propagation.test.ts +280 -0
- package/src/__tests__/ephemeral-permissions.test.ts +93 -3
- package/src/__tests__/estimator-calibration-integration.test.ts +208 -0
- package/src/__tests__/estimator-calibration.test.ts +213 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +26 -7
- package/src/__tests__/file-write-tool.test.ts +151 -1
- package/src/__tests__/filing-service.test.ts +255 -0
- package/src/__tests__/gemini-provider.test.ts +0 -3
- package/src/__tests__/guardian-grant-minting.test.ts +8 -0
- package/src/__tests__/headless-browser-interactions.test.ts +1 -1
- package/src/__tests__/heartbeat-service.test.ts +96 -15
- package/src/__tests__/host-shell-tool.test.ts +124 -18
- package/src/__tests__/http-user-message-parity.test.ts +29 -1
- package/src/__tests__/inbound-slack-persistence.test.ts +340 -0
- package/src/__tests__/intent-routing.test.ts +1 -40
- package/src/__tests__/llm-catalog-parity.test.ts +174 -0
- package/src/__tests__/llm-context-normalization.test.ts +121 -0
- package/src/__tests__/llm-resolver.test.ts +214 -0
- package/src/__tests__/llm-schema.test.ts +223 -0
- package/src/__tests__/managed-proxy-context.test.ts +6 -2
- package/src/__tests__/messaging-skill-split.test.ts +3 -34
- package/src/__tests__/migration-import-from-url.test.ts +684 -0
- package/src/__tests__/model-intents.test.ts +9 -83
- package/src/__tests__/notification-decision-fallback.test.ts +0 -10
- package/src/__tests__/notification-decision-identity.test.ts +0 -9
- package/src/__tests__/notification-decision-recipient-context.test.ts +0 -9
- package/src/__tests__/oauth-store.test.ts +10 -7
- package/src/__tests__/oauth2-gateway-transport.test.ts +8 -3
- package/src/__tests__/oauth2-refresh-retry.test.ts +279 -0
- package/src/__tests__/openai-provider.test.ts +7 -0
- package/src/__tests__/openai-responses-provider.test.ts +396 -0
- package/src/__tests__/openrouter-provider-only.test.ts +135 -0
- package/src/__tests__/outbound-slack-persistence.test.ts +293 -0
- package/src/__tests__/permission-checker-host-gate.test.ts +1 -1
- package/src/__tests__/permission-mode.test.ts +16 -0
- package/src/__tests__/permission-types.test.ts +0 -1
- package/src/__tests__/persona-resolver.test.ts +13 -13
- package/src/__tests__/pkb-autoinject.test.ts +37 -1
- package/src/__tests__/platform-bash-auto-approve.test.ts +1 -1
- package/src/__tests__/pricing.test.ts +50 -3
- package/src/__tests__/profiler-routes.test.ts +1 -1
- package/src/__tests__/provider-commit-message-generator.test.ts +14 -84
- package/src/__tests__/provider-env-vars-scope.test.ts +52 -0
- package/src/__tests__/provider-error-scenarios.test.ts +135 -6
- package/src/__tests__/provider-managed-proxy-integration.test.ts +42 -11
- package/src/__tests__/provider-registry-ollama.test.ts +1 -2
- package/src/__tests__/proxy-approval-callback.test.ts +0 -1
- package/src/__tests__/reaction-persistence.test.ts +560 -0
- package/src/__tests__/relay-server.test.ts +1 -1
- package/src/__tests__/require-fresh-approval.test.ts +1 -1
- package/src/__tests__/retry-openrouter-only-normalization.test.ts +136 -0
- package/src/__tests__/retry-thinking-tool-choice.test.ts +226 -0
- package/src/__tests__/risk-classifier-parity.test.ts +230 -0
- package/src/__tests__/sanitize-config-for-transfer.test.ts +78 -1
- package/src/__tests__/secret-ingress-http.test.ts +28 -0
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +125 -0
- package/src/__tests__/secret-routes-managed-proxy.test.ts +2 -3
- package/src/__tests__/secret-scanner-executor.test.ts +1 -1
- package/src/__tests__/send-endpoint-busy.test.ts +29 -1
- package/src/__tests__/server-history-render.test.ts +31 -0
- package/src/__tests__/shell-parser-property.test.ts +13 -13
- package/src/__tests__/skill-cache-store.test.ts +182 -0
- package/src/__tests__/skills.test.ts +19 -33
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
- package/src/__tests__/slack-skill.test.ts +3 -8
- package/src/__tests__/starter-bundle.test.ts +35 -0
- package/src/__tests__/subagent-call-site-routing.test.ts +280 -0
- package/src/__tests__/suggestion-routes.test.ts +160 -3
- package/src/__tests__/system-prompt.test.ts +22 -35
- package/src/__tests__/task-runner.test.ts +3 -1
- package/src/__tests__/tcc-sandbox-deny.test.ts +198 -0
- package/src/__tests__/terminal-tools.test.ts +8 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +2 -52
- package/src/__tests__/thread-backfill.test.ts +941 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -2
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -2
- package/src/__tests__/tool-executor.test.ts +60 -94
- package/src/__tests__/trust-store.test.ts +442 -109
- package/src/__tests__/update-bulletin-job.test.ts +389 -0
- package/src/__tests__/usage-cache-backfill-migration.test.ts +3 -1
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -22
- package/src/__tests__/voice-session-bridge.test.ts +39 -0
- package/src/__tests__/volume-security-guard.test.ts +3 -2
- package/src/__tests__/web-search-history.test.ts +337 -0
- package/src/__tests__/workspace-migration-039-drop-legacy-llm-keys.test.ts +343 -0
- package/src/__tests__/workspace-migration-043-release-notes-latex-rendering.test.ts +202 -0
- package/src/__tests__/workspace-migration-045-release-notes-meet-avatar.test.ts +210 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +11 -11
- package/src/__tests__/workspace-migration-unify-llm-callsite-configs.test.ts +841 -0
- package/src/__tests__/workspace-policy.test.ts +1 -13
- package/src/acp/client-handler.ts +1 -2
- package/src/agent/loop.ts +209 -17
- package/src/avatar/resvg-lazy.test.ts +136 -0
- package/src/avatar/resvg-lazy.ts +82 -9
- package/src/avatar/traits-png-sync.ts +21 -1
- package/src/browser/__tests__/operations.test.ts +163 -0
- package/src/browser/identifiers.ts +51 -0
- package/src/browser/operations.ts +660 -0
- package/src/browser/types.ts +81 -0
- package/src/calls/guardian-question-copy.ts +2 -2
- package/src/calls/telephony-stt-routing.ts +1 -1
- package/src/calls/voice-session-bridge.ts +1 -0
- package/src/cli/AGENTS.md +1 -1
- package/src/cli/commands/__tests__/attachment.test.ts +438 -0
- package/src/cli/commands/__tests__/browser.test.ts +554 -0
- package/src/cli/commands/__tests__/cache.test.ts +623 -0
- package/src/cli/commands/__tests__/email-list.test.ts +6 -0
- package/src/cli/commands/__tests__/email-send.test.ts +93 -1
- package/src/cli/commands/__tests__/image-generation.test.ts +666 -0
- package/src/cli/commands/__tests__/inference-send.test.ts +451 -0
- package/src/cli/commands/__tests__/stt-transcribe.test.ts +454 -0
- package/src/cli/commands/__tests__/task.test.ts +913 -0
- package/src/cli/commands/__tests__/tts-synthesize.test.ts +594 -0
- package/src/cli/commands/__tests__/ui-confirm.test.ts +650 -0
- package/src/cli/commands/__tests__/ui.test.ts +1215 -0
- package/src/cli/commands/__tests__/watchers.test.ts +716 -0
- package/src/cli/commands/attachment.ts +182 -0
- package/src/cli/commands/browser.ts +350 -0
- package/src/cli/commands/cache.ts +341 -0
- package/src/cli/commands/completions.ts +0 -3
- package/src/cli/commands/config.ts +6 -6
- package/src/cli/commands/conversations-import.ts +347 -0
- package/src/cli/commands/conversations.ts +14 -1
- package/src/cli/commands/email.ts +234 -194
- package/src/cli/commands/image-generation.ts +300 -0
- package/src/cli/commands/inference.ts +200 -0
- package/src/cli/commands/memory.ts +127 -17
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/connect.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +0 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +0 -1
- package/src/cli/commands/stt.ts +339 -0
- package/src/cli/commands/task.ts +795 -0
- package/src/cli/commands/trust.ts +50 -19
- package/src/cli/commands/tts.ts +273 -0
- package/src/cli/commands/ui.ts +670 -0
- package/src/cli/commands/watchers.ts +509 -0
- package/src/cli/lib/daemon-credential-client.ts +0 -19
- package/src/cli/program.ts +23 -4
- package/src/cli.ts +0 -37
- package/src/config/bundled-skills/conversations/tools/rename-conversation.ts +23 -1
- package/src/config/bundled-skills/media-processing/services/reduce.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +2 -2
- package/src/config/bundled-skills/messaging/TOOLS.json +4 -0
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +8 -1
- package/src/config/bundled-skills/messaging/tools/messaging-read.ts +15 -1
- package/src/config/bundled-skills/messaging/tools/messaging-search.ts +21 -1
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +11 -12
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +9 -8
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-tool-registry.ts +0 -175
- package/src/config/env.ts +7 -2
- package/src/config/feature-flag-registry.json +25 -9
- package/src/config/llm-resolver.ts +128 -0
- package/src/config/loader.ts +194 -10
- package/src/config/raw-config-utils.ts +30 -2
- package/src/config/sanitize-for-transfer.ts +35 -0
- package/src/config/schema.ts +30 -41
- package/src/config/schemas/analysis.ts +3 -22
- package/src/config/schemas/calls.ts +0 -4
- package/src/config/schemas/filing.ts +2 -7
- package/src/config/schemas/heartbeat.ts +0 -5
- package/src/config/schemas/inference.ts +3 -23
- package/src/config/schemas/llm.ts +318 -0
- package/src/config/schemas/memory-processing.ts +1 -9
- package/src/config/schemas/notifications.ts +4 -11
- package/src/config/schemas/platform.ts +3 -9
- package/src/config/schemas/security.ts +33 -0
- package/src/config/schemas/services.ts +9 -4
- package/src/config/schemas/stt.ts +1 -0
- package/src/config/schemas/tts.ts +53 -0
- package/src/config/schemas/updates.ts +1 -1
- package/src/config/schemas/workspace-git.ts +3 -40
- package/src/config/skills.ts +2 -2
- package/src/context/__tests__/compact-prompt.test.ts +45 -0
- package/src/context/__tests__/microcompact.test.ts +805 -0
- package/src/context/estimator-calibration.ts +136 -0
- package/src/context/microcompact.ts +443 -0
- package/src/context/prompts/compact.md +12 -0
- package/src/context/token-estimator.ts +61 -3
- package/src/context/window-manager.ts +229 -25
- package/src/credential-execution/approval-bridge.ts +0 -1
- package/src/credential-execution/executable-discovery.ts +19 -8
- package/src/credential-execution/process-manager.test.ts +109 -0
- package/src/credential-execution/process-manager.ts +65 -2
- package/src/daemon/approval-generators.ts +29 -4
- package/src/daemon/assistant-attachments.ts +24 -13
- package/src/daemon/classifier.ts +2 -2
- package/src/daemon/config-watcher.ts +0 -1
- package/src/daemon/context-overflow-reducer.ts +4 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +79 -12
- package/src/daemon/conversation-agent-loop.ts +462 -80
- package/src/daemon/conversation-attachments.ts +2 -6
- package/src/daemon/conversation-error.ts +36 -1
- package/src/daemon/conversation-lifecycle.ts +30 -6
- package/src/daemon/conversation-messaging.ts +73 -4
- package/src/daemon/conversation-process.ts +10 -4
- package/src/daemon/conversation-queue-manager.ts +3 -0
- package/src/daemon/conversation-runtime-assembly.ts +760 -29
- package/src/daemon/conversation-slash.ts +2 -2
- package/src/daemon/conversation-surfaces.ts +389 -1
- package/src/daemon/conversation-tool-setup.ts +10 -5
- package/src/daemon/conversation-usage.ts +1 -1
- package/src/daemon/conversation.ts +118 -30
- package/src/daemon/external-skills-bootstrap.ts +41 -0
- package/src/daemon/guardian-action-generators.ts +34 -14
- package/src/daemon/handlers/config-model.test.ts +86 -0
- package/src/daemon/handlers/config-model.ts +54 -12
- package/src/daemon/handlers/conversations.ts +9 -2
- package/src/daemon/handlers/shared.ts +39 -11
- package/src/daemon/handlers/skills.ts +2 -2
- package/src/daemon/handlers/slack-channel-oauth-install.ts +197 -0
- package/src/daemon/lifecycle.ts +76 -14
- package/src/daemon/message-types/conversations.ts +14 -0
- package/src/daemon/message-types/messages.ts +9 -1
- package/src/daemon/message-types/trust.ts +0 -2
- package/src/daemon/parse-actual-tokens-from-error.test.ts +57 -1
- package/src/daemon/parse-actual-tokens-from-error.ts +66 -0
- package/src/daemon/pkb-context-tracker.test.ts +169 -0
- package/src/daemon/pkb-context-tracker.ts +125 -0
- package/src/daemon/pkb-reminder-builder.test.ts +70 -0
- package/src/daemon/pkb-reminder-builder.ts +31 -0
- package/src/daemon/providers-setup.ts +6 -0
- package/src/daemon/server.ts +117 -9
- package/src/daemon/tool-side-effects.ts +0 -9
- package/src/daemon/watch-handler.ts +4 -4
- package/src/daemon/web-search-history.ts +126 -0
- package/src/events/domain-events.ts +0 -1
- package/src/filing/filing-service.ts +9 -10
- package/src/heartbeat/heartbeat-service.ts +76 -28
- package/src/home/__tests__/feed-scheduler.test.ts +39 -11
- package/src/home/__tests__/rollup-producer.test.ts +44 -0
- package/src/home/assistant-feed-authoring.ts +4 -0
- package/src/home/emit-feed-event.ts +4 -0
- package/src/home/feed-scheduler.ts +20 -4
- package/src/home/feed-types.ts +56 -2
- package/src/home/relationship-state-writer.ts +2 -2
- package/src/home/rollup-producer.ts +34 -5
- package/src/home/suggested-prompts.ts +101 -0
- package/src/ipc/__tests__/attachment-ipc.test.ts +213 -0
- package/src/ipc/__tests__/browser-ipc.test.ts +339 -0
- package/src/ipc/__tests__/cache-ipc.test.ts +266 -0
- package/src/ipc/__tests__/socket-path.test.ts +73 -0
- package/src/ipc/__tests__/task-ipc.test.ts +577 -0
- package/src/ipc/__tests__/ui-request-route.test.ts +495 -0
- package/src/ipc/__tests__/watcher-ipc.test.ts +295 -0
- package/src/ipc/cli-client.ts +2 -1
- package/src/ipc/cli-server.ts +26 -8
- package/src/ipc/gateway-client.ts +4 -4
- package/src/ipc/routes/attachment.ts +114 -0
- package/src/ipc/routes/browser-context.ts +61 -0
- package/src/ipc/routes/browser.ts +96 -0
- package/src/ipc/routes/cache.ts +96 -0
- package/src/ipc/routes/index.ts +17 -1
- package/src/ipc/routes/task-queue.ts +226 -0
- package/src/ipc/routes/task.ts +173 -0
- package/src/ipc/routes/ui-request.ts +50 -0
- package/src/ipc/routes/watcher.ts +203 -0
- package/src/ipc/socket-path.ts +100 -0
- package/src/memory/__tests__/conversation-analyze-job.test.ts +9 -8
- package/src/memory/__tests__/conversation-group-migration.test.ts +99 -0
- package/src/memory/admin.ts +18 -0
- package/src/memory/conversation-analyze-job.ts +14 -13
- package/src/memory/conversation-attention-store.ts +13 -6
- package/src/memory/conversation-crud.ts +103 -3
- package/src/memory/conversation-group-migration.ts +38 -6
- package/src/memory/conversation-title-service.ts +7 -4
- package/src/memory/db-init.ts +2 -0
- package/src/memory/embedding-backend.ts +1 -1
- package/src/memory/graph/compaction.ts +299 -0
- package/src/memory/graph/consolidation.ts +4 -4
- package/src/memory/graph/conversation-graph-memory.ts +89 -29
- package/src/memory/graph/extraction.test.ts +272 -2
- package/src/memory/graph/extraction.ts +173 -51
- package/src/memory/graph/graph-search.test.ts +92 -0
- package/src/memory/graph/graph-search.ts +4 -1
- package/src/memory/graph/narrative.ts +2 -2
- package/src/memory/graph/pattern-scan.ts +2 -2
- package/src/memory/graph/retriever.test.ts +459 -0
- package/src/memory/graph/retriever.ts +230 -48
- package/src/memory/graph/store.ts +41 -0
- package/src/memory/graph/tool-handlers.ts +27 -0
- package/src/memory/graph/tools.ts +6 -1
- package/src/memory/indexer.ts +5 -5
- package/src/memory/job-handlers/conversation-starters.ts +23 -20
- package/src/memory/job-handlers/summarization.ts +2 -2
- package/src/memory/job-utils.ts +7 -1
- package/src/memory/jobs/embed-pkb-file.test.ts +168 -0
- package/src/memory/jobs/embed-pkb-file.ts +54 -0
- package/src/memory/jobs-store.ts +44 -3
- package/src/memory/jobs-worker.ts +4 -0
- package/src/memory/migrations/140-backfill-usage-cache-accounting.ts +1 -1
- package/src/memory/migrations/220-normalize-user-file-by-principal.ts +2 -2
- package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +82 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/pkb/pkb-index.test.ts +368 -0
- package/src/memory/pkb/pkb-index.ts +255 -0
- package/src/memory/pkb/pkb-reconcile.test.ts +251 -0
- package/src/memory/pkb/pkb-reconcile.ts +148 -0
- package/src/memory/pkb/pkb-search.test.ts +438 -0
- package/src/memory/pkb/pkb-search.ts +137 -0
- package/src/memory/pkb/types.ts +53 -0
- package/src/memory/qdrant-client.ts +122 -1
- package/src/memory/slack-thread-store.ts +37 -0
- package/src/messaging/providers/gmail/adapter.ts +6 -16
- package/src/messaging/providers/gmail/client.ts +22 -0
- package/src/messaging/providers/gmail/types.ts +7 -0
- package/src/messaging/providers/slack/adapter.ts +14 -2
- package/src/messaging/providers/slack/backfill.test.ts +257 -0
- package/src/messaging/providers/slack/backfill.ts +101 -0
- package/src/messaging/providers/slack/message-metadata.test.ts +316 -0
- package/src/messaging/providers/slack/message-metadata.ts +123 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +1373 -0
- package/src/messaging/providers/slack/render-transcript.ts +443 -0
- package/src/messaging/style-analyzer.ts +5 -2
- package/src/notifications/README.md +9 -5
- package/src/notifications/decision-engine.ts +3 -9
- package/src/notifications/preference-extractor.ts +2 -6
- package/src/oauth/oauth-store.ts +1 -0
- package/src/oauth/platform-connection.test.ts +47 -0
- package/src/oauth/platform-connection.ts +15 -5
- package/src/oauth/seed-providers.ts +4 -2
- package/src/permissions/approval-policy.test.ts +948 -0
- package/src/permissions/approval-policy.ts +257 -0
- package/src/permissions/bash-risk-classifier.test.ts +1208 -0
- package/src/permissions/bash-risk-classifier.ts +707 -0
- package/src/permissions/checker.ts +217 -708
- package/src/permissions/command-registry.test.ts +535 -0
- package/src/permissions/command-registry.ts +825 -0
- package/src/permissions/defaults.ts +26 -78
- package/src/permissions/file-risk-classifier.test.ts +535 -0
- package/src/permissions/file-risk-classifier.ts +274 -0
- package/src/permissions/risk-types.ts +205 -0
- package/src/permissions/secret-prompter.ts +53 -2
- package/src/permissions/skill-risk-classifier.test.ts +311 -0
- package/src/permissions/skill-risk-classifier.ts +214 -0
- package/src/permissions/trust-client.ts +52 -25
- package/src/permissions/trust-store-interface.ts +1 -6
- package/src/permissions/trust-store.ts +161 -62
- package/src/permissions/types.ts +23 -14
- package/src/permissions/web-risk-classifier.test.ts +170 -0
- package/src/permissions/web-risk-classifier.ts +89 -0
- package/src/permissions/workspace-policy.ts +1 -16
- package/src/platform/client.ts +19 -1
- package/src/prompts/persona-resolver.ts +3 -3
- package/src/prompts/system-prompt.ts +19 -20
- package/src/prompts/templates/SOUL.md +2 -2
- package/src/prompts/update-bulletin-job.ts +190 -0
- package/src/providers/__tests__/context-overflow-error.test.ts +328 -0
- package/src/providers/__tests__/provider-env-vars.test.ts +102 -0
- package/src/providers/__tests__/retry-callsite.test.ts +424 -0
- package/src/providers/anthropic/client.ts +183 -14
- package/src/providers/call-site-routing.ts +71 -0
- package/src/providers/gemini/client.ts +65 -2
- package/src/providers/managed-proxy/constants.ts +2 -1
- package/src/providers/model-catalog.ts +501 -33
- package/src/providers/model-intents.ts +4 -4
- package/src/providers/openai/chat-completions-provider.ts +57 -1
- package/src/providers/openai/responses-provider.ts +86 -9
- package/src/providers/openrouter/client.ts +76 -9
- package/src/providers/provider-env-vars.ts +56 -0
- package/src/providers/provider-send-message.ts +22 -5
- package/src/providers/ratelimit.ts +4 -0
- package/src/providers/registry.ts +19 -8
- package/src/providers/retry.ts +174 -39
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +55 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.ts +4 -4
- package/src/providers/speech-to-text/provider-catalog.ts +17 -0
- package/src/providers/speech-to-text/resolve.ts +7 -0
- package/src/providers/speech-to-text/xai-realtime.test.ts +578 -0
- package/src/providers/speech-to-text/xai-realtime.ts +796 -0
- package/src/providers/speech-to-text/xai.test.ts +155 -0
- package/src/providers/speech-to-text/xai.ts +97 -0
- package/src/providers/types.ts +93 -3
- package/src/runtime/AGENTS.md +2 -2
- package/src/runtime/__tests__/agent-wake.test.ts +43 -2
- package/src/runtime/__tests__/interactive-ui.test.ts +673 -0
- package/src/runtime/agent-wake.ts +63 -22
- package/src/runtime/auth/route-policy.ts +4 -0
- package/src/runtime/btw-sidechain.ts +13 -3
- package/src/runtime/channel-reply-delivery.ts +106 -2
- package/src/runtime/decision-token.ts +116 -0
- package/src/runtime/gateway-client.ts +2 -2
- package/src/runtime/http-router.ts +32 -0
- package/src/runtime/http-server.ts +52 -1
- package/src/runtime/http-types.ts +23 -1
- package/src/runtime/interactive-ui.ts +362 -0
- package/src/runtime/invite-instruction-generator.ts +2 -2
- package/src/runtime/migrations/__tests__/gcs-signed-url.test.ts +176 -0
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +390 -0
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge.test.ts +221 -0
- package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +1540 -0
- package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +453 -0
- package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +222 -0
- package/src/runtime/migrations/gcs-signed-url.ts +162 -0
- package/src/runtime/migrations/vbundle-importer.ts +154 -9
- package/src/runtime/migrations/vbundle-metadata-merge.ts +124 -0
- package/src/runtime/migrations/vbundle-streaming-importer.ts +2522 -0
- package/src/runtime/migrations/vbundle-streaming-validator.ts +244 -0
- package/src/runtime/migrations/vbundle-tar-stream.ts +217 -0
- package/src/runtime/migrations/vbundle-validator.ts +15 -6
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +111 -0
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +114 -75
- package/src/runtime/routes/__tests__/migration-vellum-metadata-reconcile.test.ts +246 -0
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +58 -0
- package/src/runtime/routes/approval-routes.ts +12 -17
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +9 -0
- package/src/runtime/routes/avatar-routes.ts +20 -4
- package/src/runtime/routes/btw-routes.ts +1 -4
- package/src/runtime/routes/conversation-management-routes.ts +20 -2
- package/src/runtime/routes/conversation-routes.ts +133 -27
- package/src/runtime/routes/debug-routes.ts +1 -1
- package/src/runtime/routes/diagnostics-routes.ts +6 -4
- package/src/runtime/routes/events-routes.ts +16 -0
- package/src/runtime/routes/guardian-approval-interception.ts +33 -3
- package/src/runtime/routes/guardian-approval-prompt.ts +13 -3
- package/src/runtime/routes/home-feed-routes.ts +120 -2
- package/src/runtime/routes/inbound-message-handler.ts +912 -2
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +113 -2
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +61 -3
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +129 -6
- package/src/runtime/routes/integrations/slack/channel.ts +25 -3
- package/src/runtime/routes/llm-context-normalization.ts +23 -1
- package/src/runtime/routes/migration-routes.ts +720 -124
- package/src/runtime/routes/settings-routes.ts +4 -2
- package/src/runtime/routes/trust-rules-routes.ts +30 -14
- package/src/runtime/routes/work-items-routes.test.ts +1 -1
- package/src/runtime/routes/work-items-routes.ts +3 -2
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +25 -43
- package/src/runtime/services/analyze-conversation.ts +12 -16
- package/src/runtime/skill-route-registry.ts +28 -6
- package/src/schedule/scheduler.ts +8 -0
- package/src/security/__tests__/provider-key-env-fallback.test.ts +119 -0
- package/src/security/__tests__/untrusted-content.test.ts +109 -0
- package/src/security/oauth2.ts +98 -35
- package/src/security/secure-keys.ts +7 -8
- package/src/security/token-manager.ts +27 -13
- package/src/security/untrusted-content.ts +102 -0
- package/src/skills/catalog-cache.ts +26 -7
- package/src/skills/catalog-install.ts +31 -3
- package/src/skills/skill-cache-store.ts +97 -0
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +76 -0
- package/src/stt/daemon-batch-transcriber.ts +33 -0
- package/src/stt/stt-stream-session.ts +8 -1
- package/src/stt/types.ts +5 -1
- package/src/subagent/manager.ts +41 -13
- package/src/tasks/ephemeral-permissions.ts +9 -4
- package/src/telemetry/usage-telemetry-reporter.ts +27 -5
- package/src/tools/browser/__tests__/browser-status.test.ts +45 -2
- package/src/tools/browser/browser-execution.ts +65 -38
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +22 -0
- package/src/tools/credentials/tool-policy.ts +39 -5
- package/src/tools/credentials/vault.ts +9 -4
- package/src/tools/executor.ts +4 -0
- package/src/tools/filesystem/write.ts +52 -0
- package/src/tools/host-terminal/host-shell.ts +45 -5
- package/src/tools/memory/register.test.ts +185 -0
- package/src/tools/memory/register.ts +3 -1
- package/src/tools/network/web-fetch.ts +20 -10
- package/src/tools/network/web-search.ts +19 -4
- package/src/tools/permission-checker.ts +36 -15
- package/src/tools/policy-context.ts +25 -8
- package/src/tools/registry.ts +55 -3
- package/src/tools/side-effects.ts +0 -11
- package/src/tools/skills/execute.ts +2 -2
- package/src/tools/skills/sandbox-runner.ts +5 -2
- package/src/tools/terminal/backends/native.ts +51 -2
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/terminal/shell.ts +1 -0
- package/src/tools/tool-manifest.ts +6 -21
- package/src/tools/types.ts +12 -3
- package/src/tools/verification-control-plane-policy.ts +1 -1
- package/src/tts/__tests__/provider-adapters.test.ts +240 -13
- package/src/tts/provider-catalog.ts +18 -0
- package/src/tts/providers/index.ts +2 -0
- package/src/tts/providers/xai-provider.ts +224 -0
- package/src/tts/types.ts +46 -0
- package/src/types/tar-stream.d.ts +66 -0
- package/src/util/json.ts +17 -0
- package/src/util/platform.ts +2 -2
- package/src/util/pricing.ts +15 -5
- package/src/watcher/engine.ts +1 -1
- package/src/watcher/providers/google-calendar.ts +134 -8
- package/src/watcher/providers/outlook-calendar.ts +42 -2
- package/src/workspace/git-service.ts +23 -4
- package/src/workspace/migrations/038-unify-llm-callsite-configs.ts +516 -0
- package/src/workspace/migrations/039-drop-legacy-llm-keys.ts +171 -0
- package/src/workspace/migrations/040-seed-latency-callsite-defaults.ts +154 -0
- package/src/workspace/migrations/041-backfill-google-gmail-settings-scope.ts +57 -0
- package/src/workspace/migrations/042-fix-backfill-google-gmail-settings-scope.ts +70 -0
- package/src/workspace/migrations/043-release-notes-latex-rendering.ts +75 -0
- package/src/workspace/migrations/044-bump-stale-provider-stream-timeout.ts +51 -0
- package/src/workspace/migrations/045-release-notes-meet-avatar.ts +130 -0
- package/src/workspace/migrations/AGENTS.md +1 -1
- package/src/workspace/migrations/registry.ts +16 -0
- package/src/workspace/provider-commit-message-generator.ts +19 -38
- package/src/__tests__/gmail-archive-fallback.test.ts +0 -193
- package/src/__tests__/gmail-archive-gate.test.ts +0 -246
- package/src/__tests__/gmail-preferences.test.ts +0 -117
- package/src/__tests__/outlook-attachments.test.ts +0 -301
- package/src/__tests__/outlook-automation-tools.test.ts +0 -425
- package/src/__tests__/outlook-categories.test.ts +0 -212
- package/src/__tests__/outlook-compose-tools.test.ts +0 -325
- package/src/__tests__/outlook-declutter-tools.test.ts +0 -585
- package/src/__tests__/outlook-follow-up.test.ts +0 -196
- package/src/__tests__/outlook-trash.test.ts +0 -77
- package/src/__tests__/outlook-unsubscribe.test.ts +0 -279
- package/src/__tests__/update-bulletin-format.test.ts +0 -181
- package/src/__tests__/update-bulletin-state.test.ts +0 -135
- package/src/__tests__/update-bulletin.test.ts +0 -478
- package/src/__tests__/update-template-contract.test.ts +0 -29
- package/src/cli/commands/doctor.ts +0 -341
- package/src/config/bundled-skills/browser/SKILL.md +0 -88
- package/src/config/bundled-skills/browser/TOOLS.json +0 -516
- package/src/config/bundled-skills/browser/tools/browser-attach.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-click.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-close.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-detach.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-extract.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-fill-credential.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-hover.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-navigate.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-press-key.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-screenshot.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-scroll.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-select-option.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-snapshot.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-status.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-type.ts +0 -12
- package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +0 -49
- package/src/config/bundled-skills/browser/tools/browser-wait-for.ts +0 -12
- package/src/config/bundled-skills/chatgpt-import/SKILL.md +0 -27
- package/src/config/bundled-skills/chatgpt-import/TOOLS.json +0 -27
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +0 -378
- package/src/config/bundled-skills/gmail/SKILL.md +0 -221
- package/src/config/bundled-skills/gmail/TOOLS.json +0 -588
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +0 -256
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +0 -112
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +0 -44
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +0 -81
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +0 -108
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +0 -146
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +0 -53
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +0 -347
- package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +0 -59
- package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +0 -82
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +0 -26
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +0 -347
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +0 -29
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +0 -122
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +0 -67
- package/src/config/bundled-skills/gmail/tools/scan-result-store.ts +0 -100
- package/src/config/bundled-skills/gmail/tools/shared.ts +0 -47
- package/src/config/bundled-skills/google-calendar/SKILL.md +0 -51
- package/src/config/bundled-skills/google-calendar/TOOLS.json +0 -226
- package/src/config/bundled-skills/google-calendar/calendar-client.ts +0 -223
- package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +0 -27
- package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +0 -48
- package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +0 -19
- package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +0 -36
- package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +0 -58
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +0 -17
- package/src/config/bundled-skills/google-calendar/types.ts +0 -97
- package/src/config/bundled-skills/outlook/SKILL.md +0 -196
- package/src/config/bundled-skills/outlook/TOOLS.json +0 -530
- package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +0 -85
- package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +0 -77
- package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +0 -84
- package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +0 -94
- package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +0 -49
- package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +0 -237
- package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +0 -161
- package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +0 -32
- package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +0 -272
- package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +0 -29
- package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +0 -129
- package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +0 -87
- package/src/config/bundled-skills/outlook/tools/shared.ts +0 -20
- package/src/config/bundled-skills/outlook-calendar/SKILL.md +0 -51
- package/src/config/bundled-skills/outlook-calendar/TOOLS.json +0 -221
- package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +0 -252
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +0 -53
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +0 -74
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +0 -18
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +0 -46
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +0 -36
- package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +0 -17
- package/src/config/bundled-skills/outlook-calendar/types.ts +0 -120
- package/src/config/bundled-skills/slack/SKILL.md +0 -108
- package/src/config/bundled-skills/tasks/SKILL.md +0 -37
- package/src/config/bundled-skills/tasks/TOOLS.json +0 -353
- package/src/config/bundled-skills/tasks/icon.svg +0 -34
- package/src/config/bundled-skills/tasks/tools/task-delete.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-add.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-remove.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-show.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list-update.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-list.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-queue-run.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-run.ts +0 -12
- package/src/config/bundled-skills/tasks/tools/task-save.ts +0 -12
- package/src/config/bundled-skills/watcher/SKILL.md +0 -31
- package/src/config/bundled-skills/watcher/TOOLS.json +0 -167
- package/src/config/bundled-skills/watcher/tools/watcher-create.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-delete.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-digest.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-list.ts +0 -12
- package/src/config/bundled-skills/watcher/tools/watcher-update.ts +0 -12
- package/src/prompts/templates/UPDATES.md +0 -50
- package/src/prompts/update-bulletin-format.ts +0 -85
- package/src/prompts/update-bulletin-state.ts +0 -58
- package/src/prompts/update-bulletin-template-path.ts +0 -13
- package/src/prompts/update-bulletin.ts +0 -139
- package/src/shared/provider-env-vars.ts +0 -19
- package/src/tools/watcher/create.ts +0 -86
- package/src/tools/watcher/delete.ts +0 -36
- package/src/tools/watcher/digest.ts +0 -54
- package/src/tools/watcher/list.ts +0 -83
- package/src/tools/watcher/update.ts +0 -71
|
@@ -0,0 +1,516 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import type { WorkspaceMigration } from "./types.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Consolidate scattered LLM-related config keys into the unified `llm` block
|
|
8
|
+
* introduced in PR 1 of the LLM call-site unification plan.
|
|
9
|
+
*
|
|
10
|
+
* What this migration writes (under `llm.*`):
|
|
11
|
+
* - `llm.default` — provider/model/maxTokens/effort/speed/thinking/contextWindow
|
|
12
|
+
* pulled from `services.inference.{provider,model}` and the legacy
|
|
13
|
+
* top-level `maxTokens`/`effort`/`speed`/`thinking`/`contextWindow` keys.
|
|
14
|
+
* `temperature` is seeded as `null` because no current config source maps to
|
|
15
|
+
* it.
|
|
16
|
+
* - `llm.callSites.<id>` — per-call-site overrides derived from the existing
|
|
17
|
+
* scattered config (`heartbeat.speed`, `filing.speed`,
|
|
18
|
+
* `analysis.modelIntent`/`modelOverride`,
|
|
19
|
+
* `memory.summarization.modelIntent`,
|
|
20
|
+
* `workspaceGit.commitMessageLLM.{maxTokens,temperature}`,
|
|
21
|
+
* `ui.greetingModelIntent`, `notifications.decisionModelIntent` (which
|
|
22
|
+
* drives both `notificationDecision` and `preferenceExtraction`),
|
|
23
|
+
* `calls.model`).
|
|
24
|
+
* - `llm.pricingOverrides` — copied from the top-level `pricingOverrides`.
|
|
25
|
+
*
|
|
26
|
+
* What this migration does NOT do:
|
|
27
|
+
* - Delete any of the source keys. The legacy keys remain on disk so
|
|
28
|
+
* existing readers continue to work. PR 19 of the plan removes them from
|
|
29
|
+
* the schema once every call site has been switched over to read through
|
|
30
|
+
* the resolver.
|
|
31
|
+
*
|
|
32
|
+
* Idempotency:
|
|
33
|
+
* - Early-returns when `config.llm.default` is already present, so re-runs
|
|
34
|
+
* and runs against an already-migrated workspace are no-ops.
|
|
35
|
+
* - Early-returns on missing/malformed `config.json`.
|
|
36
|
+
*
|
|
37
|
+
* Rollback (`down`):
|
|
38
|
+
* - Reverses the mapping best-effort by extracting `llm.default.*` back to
|
|
39
|
+
* top-level + `services.inference`, extracting `llm.callSites.*` back to
|
|
40
|
+
* scattered keys, copying `llm.pricingOverrides` back to top-level
|
|
41
|
+
* `pricingOverrides`, and finally removing `llm`. After PRs 7-18 land and
|
|
42
|
+
* callers stop reading the old keys, rollback fidelity will degrade
|
|
43
|
+
* (callers will only see what `down` writes back), which is acceptable
|
|
44
|
+
* for a development rollback path.
|
|
45
|
+
*/
|
|
46
|
+
export const unifyLlmCallSiteConfigsMigration: WorkspaceMigration = {
|
|
47
|
+
id: "038-unify-llm-callsite-configs",
|
|
48
|
+
description:
|
|
49
|
+
"Consolidate scattered LLM config keys into unified llm.{default,profiles,callSites} structure",
|
|
50
|
+
run(workspaceDir: string): void {
|
|
51
|
+
const configPath = join(workspaceDir, "config.json");
|
|
52
|
+
if (!existsSync(configPath)) return;
|
|
53
|
+
|
|
54
|
+
let config: Record<string, unknown>;
|
|
55
|
+
try {
|
|
56
|
+
const raw = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
57
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
|
|
58
|
+
config = raw as Record<string, unknown>;
|
|
59
|
+
} catch {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Idempotency: skip only when `llm.default` is present AND no legacy
|
|
64
|
+
// source key remains on disk. The `&& !hasLegacyData` clause matters
|
|
65
|
+
// because `AssistantConfigSchema` wires `llm: LLMSchema.default(LLMSchema
|
|
66
|
+
// .parse({}))`, which makes `loadConfig()` inject a full schema-default
|
|
67
|
+
// `llm.default` block to disk on first daemon boot under the new schema.
|
|
68
|
+
// If that boot happened before this migration was added (or before the
|
|
69
|
+
// daemon binary that includes it landed on the user's machine),
|
|
70
|
+
// `llm.default` will be present BUT will hold schema defaults rather
|
|
71
|
+
// than the user's actual `services.inference.{provider, model}` values.
|
|
72
|
+
// Returning early here would let migration 039 strip the legacy keys
|
|
73
|
+
// silently, dropping the user's real configuration.
|
|
74
|
+
const existingLlm = readObject(config.llm);
|
|
75
|
+
const existingDefault = existingLlm
|
|
76
|
+
? readObject(existingLlm.default)
|
|
77
|
+
: null;
|
|
78
|
+
const hasLegacyData =
|
|
79
|
+
hasLegacyLlmDefaultSource(config) || hasLegacyCallSiteSource(config);
|
|
80
|
+
if (existingDefault !== null && !hasLegacyData) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ── Build llm.default ──────────────────────────────────────────────
|
|
85
|
+
// Precedence (highest wins): legacy source key → existing `llm.default`
|
|
86
|
+
// value → migration fallback. Existing values come second so that on a
|
|
87
|
+
// re-run AFTER legacy keys are stripped, user-set `llm.default.*` values
|
|
88
|
+
// survive untouched (the early-return above handles the common no-op
|
|
89
|
+
// path; this ordering covers the partial-state case).
|
|
90
|
+
const services = readObject(config.services) ?? {};
|
|
91
|
+
const inference = readObject(services.inference) ?? {};
|
|
92
|
+
|
|
93
|
+
const defaultBlock: Record<string, unknown> = {
|
|
94
|
+
provider:
|
|
95
|
+
readString(inference.provider) ??
|
|
96
|
+
readString(config.provider) ??
|
|
97
|
+
readString(existingDefault?.provider) ??
|
|
98
|
+
"anthropic",
|
|
99
|
+
model:
|
|
100
|
+
readString(inference.model) ??
|
|
101
|
+
readString(config.model) ??
|
|
102
|
+
readString(existingDefault?.model) ??
|
|
103
|
+
"claude-opus-4-6",
|
|
104
|
+
maxTokens:
|
|
105
|
+
readPositiveInt(config.maxTokens) ??
|
|
106
|
+
readPositiveInt(existingDefault?.maxTokens) ??
|
|
107
|
+
64000,
|
|
108
|
+
effort:
|
|
109
|
+
readEnum(config.effort, EFFORT_VALUES) ??
|
|
110
|
+
readEnum(existingDefault?.effort, EFFORT_VALUES) ??
|
|
111
|
+
"max",
|
|
112
|
+
speed:
|
|
113
|
+
readEnum(config.speed, SPEED_VALUES) ??
|
|
114
|
+
readEnum(existingDefault?.speed, SPEED_VALUES) ??
|
|
115
|
+
"standard",
|
|
116
|
+
// No current legacy key maps to temperature; preserve existing
|
|
117
|
+
// `llm.default.temperature` if a user set one, else seed null to
|
|
118
|
+
// match `LLMConfigBase` defaults.
|
|
119
|
+
temperature:
|
|
120
|
+
existingDefault && "temperature" in existingDefault
|
|
121
|
+
? (existingDefault.temperature as number | null)
|
|
122
|
+
: null,
|
|
123
|
+
};
|
|
124
|
+
const thinking =
|
|
125
|
+
readObject(config.thinking) ??
|
|
126
|
+
(existingDefault ? readObject(existingDefault.thinking) : null);
|
|
127
|
+
if (thinking !== null) {
|
|
128
|
+
defaultBlock.thinking = thinking;
|
|
129
|
+
}
|
|
130
|
+
const contextWindow =
|
|
131
|
+
readObject(config.contextWindow) ??
|
|
132
|
+
(existingDefault ? readObject(existingDefault.contextWindow) : null);
|
|
133
|
+
if (contextWindow !== null) {
|
|
134
|
+
defaultBlock.contextWindow = contextWindow;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ── Build llm.callSites ────────────────────────────────────────────
|
|
138
|
+
const callSites: Record<string, Record<string, unknown>> = {};
|
|
139
|
+
|
|
140
|
+
const heartbeat = readObject(config.heartbeat);
|
|
141
|
+
const heartbeatSpeed = heartbeat
|
|
142
|
+
? readEnum(heartbeat.speed, SPEED_VALUES)
|
|
143
|
+
: undefined;
|
|
144
|
+
if (heartbeatSpeed !== undefined && heartbeatSpeed !== defaultBlock.speed) {
|
|
145
|
+
callSites.heartbeatAgent = { speed: heartbeatSpeed };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const filing = readObject(config.filing);
|
|
149
|
+
const filingSpeed = filing
|
|
150
|
+
? readEnum(filing.speed, SPEED_VALUES)
|
|
151
|
+
: undefined;
|
|
152
|
+
if (filingSpeed !== undefined && filingSpeed !== defaultBlock.speed) {
|
|
153
|
+
callSites.filingAgent = { speed: filingSpeed };
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const analysis = readObject(config.analysis);
|
|
157
|
+
if (analysis !== null) {
|
|
158
|
+
const analysisOverride = readString(analysis.modelOverride);
|
|
159
|
+
const analysisIntent = readModelIntent(analysis.modelIntent);
|
|
160
|
+
const analysisCallSite: Record<string, unknown> = {};
|
|
161
|
+
// `modelOverride` is shaped as `"provider/model"` — explode into the
|
|
162
|
+
// resolver's separate provider/model fields. If the string lacks a
|
|
163
|
+
// slash, treat the whole value as the model and inherit the active
|
|
164
|
+
// provider implicitly via the resolver's default merge.
|
|
165
|
+
if (analysisOverride !== undefined) {
|
|
166
|
+
const [providerPart, ...modelParts] = analysisOverride.split("/");
|
|
167
|
+
if (modelParts.length > 0 && providerPart.length > 0) {
|
|
168
|
+
analysisCallSite.provider = providerPart;
|
|
169
|
+
analysisCallSite.model = modelParts.join("/");
|
|
170
|
+
} else {
|
|
171
|
+
analysisCallSite.model = analysisOverride;
|
|
172
|
+
}
|
|
173
|
+
} else if (analysisIntent !== undefined) {
|
|
174
|
+
// Resolve intent to provider/model using the same lookup the runtime
|
|
175
|
+
// uses (mirrors `providers/model-intents.ts` PROVIDER_MODEL_INTENTS).
|
|
176
|
+
const provider = String(defaultBlock.provider);
|
|
177
|
+
const resolvedModel = resolveModelIntentForProvider(
|
|
178
|
+
provider,
|
|
179
|
+
analysisIntent,
|
|
180
|
+
);
|
|
181
|
+
if (resolvedModel !== undefined) {
|
|
182
|
+
analysisCallSite.model = resolvedModel;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (Object.keys(analysisCallSite).length > 0) {
|
|
186
|
+
callSites.analyzeConversation = analysisCallSite;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const memory = readObject(config.memory);
|
|
191
|
+
const summarization = memory ? readObject(memory.summarization) : null;
|
|
192
|
+
const summarizationIntent = summarization
|
|
193
|
+
? readModelIntent(summarization.modelIntent)
|
|
194
|
+
: undefined;
|
|
195
|
+
if (summarizationIntent !== undefined) {
|
|
196
|
+
const provider = String(defaultBlock.provider);
|
|
197
|
+
const resolvedModel = resolveModelIntentForProvider(
|
|
198
|
+
provider,
|
|
199
|
+
summarizationIntent,
|
|
200
|
+
);
|
|
201
|
+
if (resolvedModel !== undefined) {
|
|
202
|
+
callSites.conversationSummarization = { model: resolvedModel };
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const workspaceGit = readObject(config.workspaceGit);
|
|
207
|
+
const commitMessageLLM = workspaceGit
|
|
208
|
+
? readObject(workspaceGit.commitMessageLLM)
|
|
209
|
+
: null;
|
|
210
|
+
if (commitMessageLLM !== null) {
|
|
211
|
+
const commitOverride: Record<string, unknown> = {};
|
|
212
|
+
const cmMaxTokens = readPositiveInt(commitMessageLLM.maxTokens);
|
|
213
|
+
if (cmMaxTokens !== undefined) {
|
|
214
|
+
commitOverride.maxTokens = cmMaxTokens;
|
|
215
|
+
}
|
|
216
|
+
const cmTemperature = readTemperature(commitMessageLLM.temperature);
|
|
217
|
+
if (cmTemperature !== undefined) {
|
|
218
|
+
commitOverride.temperature = cmTemperature;
|
|
219
|
+
}
|
|
220
|
+
if (Object.keys(commitOverride).length > 0) {
|
|
221
|
+
callSites.commitMessage = commitOverride;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const ui = readObject(config.ui);
|
|
226
|
+
const greetingIntent = ui
|
|
227
|
+
? readModelIntent(ui.greetingModelIntent)
|
|
228
|
+
: undefined;
|
|
229
|
+
if (greetingIntent !== undefined) {
|
|
230
|
+
const provider = String(defaultBlock.provider);
|
|
231
|
+
const resolvedModel = resolveModelIntentForProvider(
|
|
232
|
+
provider,
|
|
233
|
+
greetingIntent,
|
|
234
|
+
);
|
|
235
|
+
if (resolvedModel !== undefined) {
|
|
236
|
+
callSites.emptyStateGreeting = { model: resolvedModel };
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const notifications = readObject(config.notifications);
|
|
241
|
+
const notificationIntent = notifications
|
|
242
|
+
? readModelIntent(notifications.decisionModelIntent)
|
|
243
|
+
: undefined;
|
|
244
|
+
if (notificationIntent !== undefined) {
|
|
245
|
+
const provider = String(defaultBlock.provider);
|
|
246
|
+
const resolvedModel = resolveModelIntentForProvider(
|
|
247
|
+
provider,
|
|
248
|
+
notificationIntent,
|
|
249
|
+
);
|
|
250
|
+
if (resolvedModel !== undefined) {
|
|
251
|
+
// `notifications.decisionModelIntent` drives BOTH the notification
|
|
252
|
+
// decision engine (`notifications/decision-engine.ts`) AND the
|
|
253
|
+
// preference extractor (`notifications/preference-extractor.ts`), so
|
|
254
|
+
// seed both call sites from the same source intent. Confirmed via
|
|
255
|
+
// grep — those are the only two readers of the legacy key.
|
|
256
|
+
callSites.notificationDecision = { model: resolvedModel };
|
|
257
|
+
callSites.preferenceExtraction = { model: resolvedModel };
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const calls = readObject(config.calls);
|
|
262
|
+
const callsModel = calls ? readString(calls.model) : undefined;
|
|
263
|
+
if (callsModel !== undefined) {
|
|
264
|
+
callSites.callAgent = { model: callsModel };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// ── Build llm block ────────────────────────────────────────────────
|
|
268
|
+
//
|
|
269
|
+
// Preserve any pre-existing `llm` subtree. Reaching this point means
|
|
270
|
+
// `llm.default` was absent (idempotency check at the top), but a user
|
|
271
|
+
// may still have defined `llm.callSites`, `llm.profiles`, or
|
|
272
|
+
// `llm.pricingOverrides` directly. Wholesale-replacing `config.llm`
|
|
273
|
+
// would silently drop those user overrides, so deep-merge instead:
|
|
274
|
+
// - `default`: always taken from this migration (we just synthesized
|
|
275
|
+
// it from legacy keys).
|
|
276
|
+
// - `callSites`: per-key merge, with migration-derived entries
|
|
277
|
+
// overwriting pre-existing entries that share the same key.
|
|
278
|
+
// - `profiles`: preserved verbatim from existing `llm.profiles`.
|
|
279
|
+
// - `pricingOverrides`: prefer the migration-derived value (legacy
|
|
280
|
+
// top-level `pricingOverrides`); fall back to existing
|
|
281
|
+
// `llm.pricingOverrides` if the legacy key is absent.
|
|
282
|
+
const llmBlock: Record<string, unknown> = {
|
|
283
|
+
default: defaultBlock,
|
|
284
|
+
};
|
|
285
|
+
const existingProfiles = existingLlm
|
|
286
|
+
? readObject(existingLlm.profiles)
|
|
287
|
+
: null;
|
|
288
|
+
if (existingProfiles !== null) {
|
|
289
|
+
llmBlock.profiles = existingProfiles;
|
|
290
|
+
}
|
|
291
|
+
const existingCallSites = existingLlm
|
|
292
|
+
? readObject(existingLlm.callSites)
|
|
293
|
+
: null;
|
|
294
|
+
const mergedCallSites: Record<string, Record<string, unknown>> = {};
|
|
295
|
+
if (existingCallSites !== null) {
|
|
296
|
+
for (const [key, value] of Object.entries(existingCallSites)) {
|
|
297
|
+
const obj = readObject(value);
|
|
298
|
+
if (obj !== null) {
|
|
299
|
+
mergedCallSites[key] = obj;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
for (const [key, value] of Object.entries(callSites)) {
|
|
304
|
+
mergedCallSites[key] = value;
|
|
305
|
+
}
|
|
306
|
+
if (Object.keys(mergedCallSites).length > 0) {
|
|
307
|
+
llmBlock.callSites = mergedCallSites;
|
|
308
|
+
}
|
|
309
|
+
const pricingOverrides = config.pricingOverrides;
|
|
310
|
+
if (Array.isArray(pricingOverrides)) {
|
|
311
|
+
llmBlock.pricingOverrides = pricingOverrides;
|
|
312
|
+
} else if (existingLlm && Array.isArray(existingLlm.pricingOverrides)) {
|
|
313
|
+
llmBlock.pricingOverrides = existingLlm.pricingOverrides;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
config.llm = llmBlock;
|
|
317
|
+
|
|
318
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
319
|
+
},
|
|
320
|
+
/**
|
|
321
|
+
* Documented no-op since PR 19 of the unify-llm-callsites plan.
|
|
322
|
+
*
|
|
323
|
+
* The legacy keys that this migration consolidates (`services.inference.
|
|
324
|
+
* {provider,model}`, top-level `maxTokens`/`effort`/`speed`/`thinking`/
|
|
325
|
+
* `contextWindow`/`pricingOverrides`, `heartbeat.speed`, `filing.speed`,
|
|
326
|
+
* `analysis.modelIntent`/`modelOverride`, `memory.summarization.modelIntent`,
|
|
327
|
+
* `notifications.decisionModelIntent`, `ui.greetingModelIntent`,
|
|
328
|
+
* `calls.model`, and `workspaceGit.commitMessageLLM.{maxTokens,temperature}`)
|
|
329
|
+
* were removed from `AssistantConfigSchema` in PR 19. Re-creating them in
|
|
330
|
+
* `down()` would have no effect on the running daemon (no code reads them
|
|
331
|
+
* any more), so a rollback that needs to undo this migration must instead
|
|
332
|
+
* roll back the application binary to a build that predates PR 19.
|
|
333
|
+
*/
|
|
334
|
+
down(_workspaceDir: string): void {
|
|
335
|
+
// Forward-only after PR 19. See comment above.
|
|
336
|
+
},
|
|
337
|
+
};
|
|
338
|
+
|
|
339
|
+
// ---------------------------------------------------------------------------
|
|
340
|
+
// Helpers — self-contained per workspace migrations AGENTS.md
|
|
341
|
+
// ---------------------------------------------------------------------------
|
|
342
|
+
|
|
343
|
+
// `xhigh` was added to the runtime enum in main PR #26117 after this
|
|
344
|
+
// migration was authored. Widening the validation set is safe (it can only
|
|
345
|
+
// admit MORE legitimate values, never narrow them) and prevents an `effort:
|
|
346
|
+
// "xhigh"` legacy value from being silently downgraded to "max" during the
|
|
347
|
+
// consolidation below.
|
|
348
|
+
const EFFORT_VALUES = new Set(["low", "medium", "high", "xhigh", "max"]);
|
|
349
|
+
const SPEED_VALUES = new Set(["standard", "fast"]);
|
|
350
|
+
const MODEL_INTENT_VALUES = new Set([
|
|
351
|
+
"latency-optimized",
|
|
352
|
+
"quality-optimized",
|
|
353
|
+
"vision-optimized",
|
|
354
|
+
]);
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Mirror of `providers/model-intents.ts:PROVIDER_MODEL_INTENTS` snapshotted at
|
|
358
|
+
* the time this migration was authored. Migrations are write-once and must be
|
|
359
|
+
* self-contained — duplicating the table here means the migration's behavior
|
|
360
|
+
* is frozen against the catalog as it existed when users upgraded across this
|
|
361
|
+
* boundary, even if the runtime catalog evolves later.
|
|
362
|
+
*/
|
|
363
|
+
const PROVIDER_MODEL_INTENTS_SNAPSHOT: Record<
|
|
364
|
+
string,
|
|
365
|
+
Record<string, string>
|
|
366
|
+
> = {
|
|
367
|
+
anthropic: {
|
|
368
|
+
"latency-optimized": "claude-haiku-4-5-20251001",
|
|
369
|
+
"quality-optimized": "claude-opus-4-7",
|
|
370
|
+
"vision-optimized": "claude-opus-4-6",
|
|
371
|
+
},
|
|
372
|
+
openai: {
|
|
373
|
+
"latency-optimized": "gpt-5.4-nano",
|
|
374
|
+
"quality-optimized": "gpt-5.4",
|
|
375
|
+
"vision-optimized": "gpt-5.4",
|
|
376
|
+
},
|
|
377
|
+
gemini: {
|
|
378
|
+
"latency-optimized": "gemini-3-flash",
|
|
379
|
+
"quality-optimized": "gemini-3-flash",
|
|
380
|
+
"vision-optimized": "gemini-3-flash",
|
|
381
|
+
},
|
|
382
|
+
ollama: {
|
|
383
|
+
"latency-optimized": "llama3.2",
|
|
384
|
+
"quality-optimized": "llama3.2",
|
|
385
|
+
"vision-optimized": "llama3.2",
|
|
386
|
+
},
|
|
387
|
+
fireworks: {
|
|
388
|
+
"latency-optimized": "accounts/fireworks/models/kimi-k2p5",
|
|
389
|
+
"quality-optimized": "accounts/fireworks/models/kimi-k2p5",
|
|
390
|
+
"vision-optimized": "accounts/fireworks/models/kimi-k2p5",
|
|
391
|
+
},
|
|
392
|
+
openrouter: {
|
|
393
|
+
"latency-optimized": "anthropic/claude-haiku-4.5",
|
|
394
|
+
"quality-optimized": "anthropic/claude-opus-4.7",
|
|
395
|
+
"vision-optimized": "anthropic/claude-opus-4.6",
|
|
396
|
+
},
|
|
397
|
+
};
|
|
398
|
+
|
|
399
|
+
function resolveModelIntentForProvider(
|
|
400
|
+
provider: string,
|
|
401
|
+
intent: string,
|
|
402
|
+
): string | undefined {
|
|
403
|
+
return PROVIDER_MODEL_INTENTS_SNAPSHOT[provider]?.[intent];
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
function readObject(value: unknown): Record<string, unknown> | null {
|
|
407
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
return value as Record<string, unknown>;
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
function readString(value: unknown): string | undefined {
|
|
414
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function readPositiveInt(value: unknown): number | undefined {
|
|
418
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0
|
|
419
|
+
? value
|
|
420
|
+
: undefined;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
function readEnum<T extends string>(
|
|
424
|
+
value: unknown,
|
|
425
|
+
allowed: Set<string>,
|
|
426
|
+
): T | undefined {
|
|
427
|
+
return typeof value === "string" && allowed.has(value)
|
|
428
|
+
? (value as T)
|
|
429
|
+
: undefined;
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function readModelIntent(value: unknown): string | undefined {
|
|
433
|
+
return typeof value === "string" && MODEL_INTENT_VALUES.has(value)
|
|
434
|
+
? value
|
|
435
|
+
: undefined;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function readTemperature(value: unknown): number | undefined {
|
|
439
|
+
return typeof value === "number" &&
|
|
440
|
+
Number.isFinite(value) &&
|
|
441
|
+
value >= 0 &&
|
|
442
|
+
value <= 2
|
|
443
|
+
? value
|
|
444
|
+
: undefined;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Returns true if any source key that this migration consolidates into
|
|
449
|
+
* `llm.default` is still present on disk. The `provider`/`model`/etc. checks
|
|
450
|
+
* cover the `services.inference` and top-level legacy locations; their
|
|
451
|
+
* presence proves migration 039 hasn't yet stripped them, which means we
|
|
452
|
+
* MUST re-process even if `llm.default` already exists (it may have been
|
|
453
|
+
* injected by `loadConfig()`'s schema-default backfill rather than by a
|
|
454
|
+
* previous run of this migration).
|
|
455
|
+
*/
|
|
456
|
+
function hasLegacyLlmDefaultSource(config: Record<string, unknown>): boolean {
|
|
457
|
+
const services = readObject(config.services);
|
|
458
|
+
const inference = services ? readObject(services.inference) : null;
|
|
459
|
+
if (
|
|
460
|
+
inference &&
|
|
461
|
+
(readString(inference.provider) !== undefined ||
|
|
462
|
+
readString(inference.model) !== undefined)
|
|
463
|
+
) {
|
|
464
|
+
return true;
|
|
465
|
+
}
|
|
466
|
+
for (const key of [
|
|
467
|
+
"provider",
|
|
468
|
+
"model",
|
|
469
|
+
"maxTokens",
|
|
470
|
+
"effort",
|
|
471
|
+
"speed",
|
|
472
|
+
"thinking",
|
|
473
|
+
"contextWindow",
|
|
474
|
+
"pricingOverrides",
|
|
475
|
+
]) {
|
|
476
|
+
if (key in config) return true;
|
|
477
|
+
}
|
|
478
|
+
return false;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Returns true if any source key that this migration consolidates into
|
|
483
|
+
* `llm.callSites` is still present on disk. Same idempotency justification
|
|
484
|
+
* as `hasLegacyLlmDefaultSource`: their presence proves migration 039
|
|
485
|
+
* hasn't yet stripped them and re-processing is required.
|
|
486
|
+
*/
|
|
487
|
+
function hasLegacyCallSiteSource(config: Record<string, unknown>): boolean {
|
|
488
|
+
const heartbeat = readObject(config.heartbeat);
|
|
489
|
+
if (heartbeat && "speed" in heartbeat) return true;
|
|
490
|
+
const filing = readObject(config.filing);
|
|
491
|
+
if (filing && "speed" in filing) return true;
|
|
492
|
+
const analysis = readObject(config.analysis);
|
|
493
|
+
if (analysis && ("modelIntent" in analysis || "modelOverride" in analysis)) {
|
|
494
|
+
return true;
|
|
495
|
+
}
|
|
496
|
+
const memory = readObject(config.memory);
|
|
497
|
+
const summarization = memory ? readObject(memory.summarization) : null;
|
|
498
|
+
if (summarization && "modelIntent" in summarization) return true;
|
|
499
|
+
const notifications = readObject(config.notifications);
|
|
500
|
+
if (notifications && "decisionModelIntent" in notifications) return true;
|
|
501
|
+
const ui = readObject(config.ui);
|
|
502
|
+
if (ui && "greetingModelIntent" in ui) return true;
|
|
503
|
+
const calls = readObject(config.calls);
|
|
504
|
+
if (calls && "model" in calls) return true;
|
|
505
|
+
const workspaceGit = readObject(config.workspaceGit);
|
|
506
|
+
const commitMessageLLM = workspaceGit
|
|
507
|
+
? readObject(workspaceGit.commitMessageLLM)
|
|
508
|
+
: null;
|
|
509
|
+
if (
|
|
510
|
+
commitMessageLLM &&
|
|
511
|
+
("maxTokens" in commitMessageLLM || "temperature" in commitMessageLLM)
|
|
512
|
+
) {
|
|
513
|
+
return true;
|
|
514
|
+
}
|
|
515
|
+
return false;
|
|
516
|
+
}
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import type { WorkspaceMigration } from "./types.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Strip the now-removed legacy LLM-related keys from existing `config.json`
|
|
8
|
+
* files. PR 19 of the unify-llm-callsites plan removed these keys from
|
|
9
|
+
* `AssistantConfigSchema`; Zod silently strips unknown fields when re-parsing,
|
|
10
|
+
* but the keys would otherwise persist on disk forever and re-appear in any
|
|
11
|
+
* exported config snapshot. Erasing them keeps `config.json` lean and matches
|
|
12
|
+
* the schema that the in-memory loader sees.
|
|
13
|
+
*
|
|
14
|
+
* Keys removed:
|
|
15
|
+
* - Top level: `maxTokens`, `effort`, `speed`, `thinking`, `contextWindow`,
|
|
16
|
+
* `pricingOverrides`.
|
|
17
|
+
* - `services.inference.{provider, model}` (the `mode` field stays — it
|
|
18
|
+
* governs `managed` vs `your-own` routing, which is orthogonal to LLM
|
|
19
|
+
* model selection).
|
|
20
|
+
* - `heartbeat.speed`, `filing.speed`.
|
|
21
|
+
* - `analysis.modelIntent`, `analysis.modelOverride`.
|
|
22
|
+
* - `memory.summarization.modelIntent`.
|
|
23
|
+
* - `notifications.decisionModelIntent`.
|
|
24
|
+
* - `ui.greetingModelIntent`.
|
|
25
|
+
* - `calls.model`.
|
|
26
|
+
* - `workspaceGit.commitMessageLLM.{maxTokens, temperature,
|
|
27
|
+
* useConfiguredProvider, providerFastModelOverrides}`.
|
|
28
|
+
*
|
|
29
|
+
* Preconditions: this migration depends on
|
|
30
|
+
* `038-unify-llm-callsite-configs` having already populated `llm.default` /
|
|
31
|
+
* `llm.callSites` / `llm.pricingOverrides` from these legacy keys. The
|
|
32
|
+
* registry guarantees ordering.
|
|
33
|
+
*
|
|
34
|
+
* Idempotency: each delete is wrapped in a key-exists check so re-runs are
|
|
35
|
+
* no-ops. Empty objects are left in place rather than recursively pruned —
|
|
36
|
+
* that matches Zod's default behavior of treating an absent value the same
|
|
37
|
+
* as an empty `{}` for nested schemas.
|
|
38
|
+
*/
|
|
39
|
+
export const dropLegacyLlmKeysMigration: WorkspaceMigration = {
|
|
40
|
+
id: "039-drop-legacy-llm-keys",
|
|
41
|
+
description:
|
|
42
|
+
"Strip deprecated scattered LLM-related keys from config.json (post-PR-19 cleanup)",
|
|
43
|
+
run(workspaceDir: string): void {
|
|
44
|
+
const configPath = join(workspaceDir, "config.json");
|
|
45
|
+
if (!existsSync(configPath)) return;
|
|
46
|
+
|
|
47
|
+
let config: Record<string, unknown>;
|
|
48
|
+
try {
|
|
49
|
+
const raw = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
50
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
|
|
51
|
+
config = raw as Record<string, unknown>;
|
|
52
|
+
} catch {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
let mutated = false;
|
|
57
|
+
|
|
58
|
+
for (const key of [
|
|
59
|
+
"maxTokens",
|
|
60
|
+
"effort",
|
|
61
|
+
"speed",
|
|
62
|
+
"thinking",
|
|
63
|
+
"contextWindow",
|
|
64
|
+
"pricingOverrides",
|
|
65
|
+
]) {
|
|
66
|
+
if (key in config) {
|
|
67
|
+
delete config[key];
|
|
68
|
+
mutated = true;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const services = readObject(config.services);
|
|
73
|
+
if (services !== null) {
|
|
74
|
+
const inference = readObject(services.inference);
|
|
75
|
+
if (inference !== null) {
|
|
76
|
+
for (const key of ["provider", "model"]) {
|
|
77
|
+
if (key in inference) {
|
|
78
|
+
delete inference[key];
|
|
79
|
+
mutated = true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const heartbeat = readObject(config.heartbeat);
|
|
86
|
+
if (heartbeat !== null && "speed" in heartbeat) {
|
|
87
|
+
delete heartbeat.speed;
|
|
88
|
+
mutated = true;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const filing = readObject(config.filing);
|
|
92
|
+
if (filing !== null && "speed" in filing) {
|
|
93
|
+
delete filing.speed;
|
|
94
|
+
mutated = true;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const analysis = readObject(config.analysis);
|
|
98
|
+
if (analysis !== null) {
|
|
99
|
+
for (const key of ["modelIntent", "modelOverride"]) {
|
|
100
|
+
if (key in analysis) {
|
|
101
|
+
delete analysis[key];
|
|
102
|
+
mutated = true;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const memory = readObject(config.memory);
|
|
108
|
+
if (memory !== null) {
|
|
109
|
+
const summarization = readObject(memory.summarization);
|
|
110
|
+
if (summarization !== null && "modelIntent" in summarization) {
|
|
111
|
+
delete summarization.modelIntent;
|
|
112
|
+
mutated = true;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const notifications = readObject(config.notifications);
|
|
117
|
+
if (notifications !== null && "decisionModelIntent" in notifications) {
|
|
118
|
+
delete notifications.decisionModelIntent;
|
|
119
|
+
mutated = true;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const ui = readObject(config.ui);
|
|
123
|
+
if (ui !== null && "greetingModelIntent" in ui) {
|
|
124
|
+
delete ui.greetingModelIntent;
|
|
125
|
+
mutated = true;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const calls = readObject(config.calls);
|
|
129
|
+
if (calls !== null && "model" in calls) {
|
|
130
|
+
delete calls.model;
|
|
131
|
+
mutated = true;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const workspaceGit = readObject(config.workspaceGit);
|
|
135
|
+
if (workspaceGit !== null) {
|
|
136
|
+
const commitMessageLLM = readObject(workspaceGit.commitMessageLLM);
|
|
137
|
+
if (commitMessageLLM !== null) {
|
|
138
|
+
for (const key of [
|
|
139
|
+
"maxTokens",
|
|
140
|
+
"temperature",
|
|
141
|
+
"useConfiguredProvider",
|
|
142
|
+
"providerFastModelOverrides",
|
|
143
|
+
]) {
|
|
144
|
+
if (key in commitMessageLLM) {
|
|
145
|
+
delete commitMessageLLM[key];
|
|
146
|
+
mutated = true;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (!mutated) return;
|
|
153
|
+
|
|
154
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
155
|
+
},
|
|
156
|
+
/**
|
|
157
|
+
* Forward-only. Restoring the deleted keys would re-introduce schema-validation
|
|
158
|
+
* warnings and have no runtime effect — every reader migrated to `llm.default`
|
|
159
|
+
* / `llm.callSites` in PR 19.
|
|
160
|
+
*/
|
|
161
|
+
down(_workspaceDir: string): void {
|
|
162
|
+
// no-op
|
|
163
|
+
},
|
|
164
|
+
};
|
|
165
|
+
|
|
166
|
+
function readObject(value: unknown): Record<string, unknown> | null {
|
|
167
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
return value as Record<string, unknown>;
|
|
171
|
+
}
|