@vellumai/assistant 0.8.1 → 0.8.3
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 +13 -19
- package/Dockerfile +75 -1
- package/bun.lock +11 -1
- package/docker-entrypoint.sh +17 -0
- package/docker-init-apt-root.sh +167 -0
- package/docker-kata-apt-env.sh +39 -0
- package/docs/plugins.md +88 -47
- package/docs/skills.md +9 -7
- package/examples/plugins/echo/README.md +27 -27
- package/examples/plugins/echo/package.json +3 -0
- package/examples/plugins/echo/register.ts +31 -31
- package/node_modules/@vellumai/slack-text/src/index.test.ts +114 -14
- package/node_modules/@vellumai/slack-text/src/index.ts +82 -18
- package/openapi.yaml +642 -5
- package/package.json +3 -1
- package/scripts/generate-openapi.ts +83 -10
- package/scripts/sync-llm-catalog.ts +2 -2
- package/scripts/sync-web-search-catalog.ts +47 -25
- package/src/__tests__/agent-image-optimize.test.ts +11 -3
- package/src/__tests__/agent-loop-exit-reason.test.ts +272 -0
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +195 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +131 -0
- package/src/__tests__/anthropic-provider.test.ts +45 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +9 -3
- package/src/__tests__/app-executors.test.ts +220 -4
- package/src/__tests__/auto-analysis-end-to-end.test.ts +35 -0
- package/src/__tests__/bundled-asset.test.ts +6 -6
- package/src/__tests__/channel-availability-routes.test.ts +206 -0
- package/src/__tests__/channel-delivery-store.test.ts +289 -1
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -1
- package/src/__tests__/clawhub.test.ts +75 -16
- package/src/__tests__/compactor-tail-resolution.test.ts +147 -0
- package/src/__tests__/config-get-vision-flag.test.ts +136 -0
- package/src/__tests__/config-loader-backfill.test.ts +115 -18
- package/src/__tests__/config-schema.test.ts +21 -0
- package/src/__tests__/config-set-route.test.ts +80 -0
- package/src/__tests__/config-sounds-sync.test.ts +97 -0
- package/src/__tests__/config-watcher-skill-reseed.test.ts +453 -0
- package/src/__tests__/context-search-conversations-source.test.ts +117 -2
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
- package/src/__tests__/context-search-workspace-source.test.ts +7 -0
- package/src/__tests__/context-token-estimator.test.ts +31 -65
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +92 -92
- package/src/__tests__/conversation-agent-loop.test.ts +59 -1
- package/src/__tests__/conversation-error.test.ts +42 -3
- package/src/__tests__/conversation-fork-crud.test.ts +82 -0
- package/src/__tests__/conversation-inference-profile-route.test.ts +40 -4
- package/src/__tests__/conversation-lifecycle.test.ts +173 -0
- package/src/__tests__/conversation-media-retry.test.ts +19 -8
- package/src/__tests__/conversation-message-sync-tags.test.ts +97 -0
- package/src/__tests__/conversation-pairing.test.ts +54 -0
- package/src/__tests__/conversation-process-callsite.test.ts +4 -1
- package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -1
- package/src/__tests__/conversation-queue.test.ts +4 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +102 -13
- package/src/__tests__/conversation-slash-queue.test.ts +59 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +4 -1
- package/src/__tests__/conversation-surfaces-table-action.test.ts +360 -0
- package/src/__tests__/conversation-sync-tags.test.ts +235 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -1
- package/src/__tests__/credential-security-invariants.test.ts +3 -2
- package/src/__tests__/date-context.test.ts +45 -0
- package/src/__tests__/db-slack-external-content-normalization.test.ts +301 -0
- package/src/__tests__/delete-managed-skill-tool.test.ts +55 -13
- package/src/__tests__/disk-pressure-tools.test.ts +1 -0
- package/src/__tests__/dm-backfill.test.ts +121 -10
- package/src/__tests__/document-tool-security.test.ts +258 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/edit-propagation.test.ts +33 -0
- package/src/__tests__/empty-response-pipeline.test.ts +0 -4
- package/src/__tests__/external-plugin-loader.test.ts +151 -55
- package/src/__tests__/filing-service.test.ts +140 -0
- package/src/__tests__/get-skill-detail-audit.test.ts +0 -4
- package/src/__tests__/guardian-action-no-hardcoded-copy.test.ts +0 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -0
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +43 -62
- package/src/__tests__/heartbeat-service.test.ts +24 -164
- package/src/__tests__/helpers/channel-test-adapter.ts +0 -2
- package/src/__tests__/helpers/tar-fixtures.ts +39 -0
- package/src/__tests__/helpers/wait-for.ts +21 -0
- package/src/__tests__/history-repair-pipeline.test.ts +0 -3
- package/src/__tests__/history-repair.test.ts +73 -0
- package/src/__tests__/host-app-control-proxy.test.ts +507 -10
- package/src/__tests__/host-proxy-preactivation.test.ts +200 -13
- package/src/__tests__/image-credentials.test.ts +1 -1
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/inference-no-mode-boot-e2e.test.ts +1 -1
- package/src/__tests__/inference-profile-reaper.test.ts +4 -2
- package/src/__tests__/inference-profile-session-handler.test.ts +18 -6
- package/src/__tests__/inference-profile-session-ipc.test.ts +17 -5
- package/src/__tests__/injector-background-turn.test.ts +153 -0
- package/src/__tests__/injector-chain.test.ts +15 -8
- package/src/__tests__/install-skill-routing.test.ts +155 -37
- package/src/__tests__/lifecycle-memory-v2-seed.test.ts +99 -3
- package/src/__tests__/list-messages-page-latest.test.ts +55 -0
- package/src/__tests__/llm-call-pipeline.test.ts +0 -3
- package/src/__tests__/llm-callsite-catalog.test.ts +25 -0
- package/src/__tests__/llm-catalog-parity.test.ts +58 -13
- package/src/__tests__/llm-request-log-agent-loop-exit-reason.test.ts +116 -0
- package/src/__tests__/llm-request-log-error-payload.test.ts +138 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +36 -0
- package/src/__tests__/llm-request-log-source-factory.test.ts +29 -53
- package/src/__tests__/llm-resolver.test.ts +255 -2
- package/src/__tests__/llm-usage-store.test.ts +114 -0
- package/src/__tests__/managed-profile-guard.test.ts +41 -29
- package/src/__tests__/managed-skill-lifecycle.test.ts +109 -18
- package/src/__tests__/managed-store.test.ts +84 -192
- package/src/__tests__/media-generate-image.test.ts +1 -1
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -2
- package/src/__tests__/messages-after-tiebreaker.test.ts +122 -0
- package/src/__tests__/notification-decision-fallback.test.ts +0 -91
- package/src/__tests__/notification-decision-strategy.test.ts +14 -31
- package/src/__tests__/notification-deep-link.test.ts +15 -0
- package/src/__tests__/notification-guardian-path.test.ts +1 -2
- package/src/__tests__/notification-platform-adapter.test.ts +5 -4
- package/src/__tests__/notification-telegram-adapter.test.ts +1 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +113 -0
- package/src/__tests__/oauth-commands-routes.test.ts +168 -16
- package/src/__tests__/oauth-provider-profiles.test.ts +9 -0
- package/src/__tests__/openai-provider.test.ts +242 -3
- package/src/__tests__/openai-responses-cutover-guard.test.ts +17 -9
- package/src/__tests__/openrouter-provider-only.test.ts +51 -3
- package/src/__tests__/openrouter-token-estimation.test.ts +34 -25
- package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -2
- package/src/__tests__/persistence-pipeline.test.ts +0 -2
- package/src/__tests__/{managed-proxy-context.test.ts → platform-proxy-context.test.ts} +7 -2
- package/src/__tests__/platform.test.ts +2 -0
- package/src/__tests__/plugin-api-shim.test.ts +125 -0
- package/src/__tests__/plugin-bootstrap.test.ts +10 -36
- package/src/__tests__/plugin-external-api.test.ts +68 -0
- package/src/__tests__/plugin-registry.test.ts +0 -77
- package/src/__tests__/plugin-route-contribution.test.ts +0 -1
- package/src/__tests__/plugin-skill-contribution.test.ts +0 -2
- package/src/__tests__/plugin-tool-contribution.test.ts +16 -15
- package/src/__tests__/plugin-types.test.ts +3 -13
- package/src/__tests__/process-message-background-slack.test.ts +8 -1
- package/src/__tests__/process-message-display-content.test.ts +421 -0
- package/src/__tests__/provider-catalog-visibility.test.ts +158 -0
- package/src/__tests__/provider-error-scenarios.test.ts +111 -0
- package/src/__tests__/{provider-managed-proxy-integration.test.ts → provider-platform-proxy-integration.test.ts} +33 -31
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +65 -13
- package/src/__tests__/schedule-routes.test.ts +50 -3
- package/src/__tests__/schedule-store.test.ts +94 -0
- package/src/__tests__/scheduler-reuse-conversation.test.ts +54 -7
- package/src/__tests__/schema-transforms.test.ts +20 -0
- package/src/__tests__/search-skills-unified.test.ts +0 -5
- package/src/__tests__/{secret-routes-managed-proxy.test.ts → secret-routes-platform-proxy.test.ts} +1 -1
- package/src/__tests__/server-history-render.test.ts +43 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +0 -12
- package/src/__tests__/skill-load-tool.test.ts +27 -89
- package/src/__tests__/skill-memory.test.ts +23 -3
- package/src/__tests__/skills-file-content-endpoint.test.ts +9 -38
- package/src/__tests__/skills-files-catalog-fallback.test.ts +0 -3
- package/src/__tests__/skills-install-extract.test.ts +49 -38
- package/src/__tests__/skills-install-staging.test.ts +159 -0
- package/src/__tests__/skills-uninstall.test.ts +9 -41
- package/src/__tests__/skills.test.ts +51 -58
- package/src/__tests__/slack-channel-config.test.ts +9 -0
- package/src/__tests__/subagent-tool-filtering.test.ts +50 -0
- package/src/__tests__/system-prompt.test.ts +670 -63
- package/src/__tests__/terminal-tools.test.ts +28 -1
- package/src/__tests__/thread-backfill.test.ts +557 -27
- package/src/__tests__/title-generate-pipeline.test.ts +0 -13
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -3
- package/src/__tests__/tool-error-pipeline.test.ts +0 -3
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -5
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +16 -4
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -12
- package/src/__tests__/turn-events-store.test.ts +256 -0
- package/src/__tests__/twilio-routes.test.ts +4 -0
- package/src/__tests__/user-plugin-loader.test.ts +0 -7
- package/src/__tests__/voice-session-bridge.test.ts +198 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +32 -10
- package/src/__tests__/workspace-migration-057-repair-stale-gemini-model-ids.test.ts +115 -3
- package/src/__tests__/workspace-migration-072-seed-reply-suggestion-callsite.test.ts +50 -0
- package/src/__tests__/workspace-migration-073-repair-recall-callsite-empty-profile.test.ts +153 -0
- package/src/__tests__/workspace-migration-085-memory-v2-bm25-b-reembed-disabled-v2-pages.test.ts +220 -0
- package/src/__tests__/workspace-migration-086-revert-stale-gemini-mis-rewrites.test.ts +269 -0
- package/src/__tests__/workspace-migration-087-memory-router-balanced-profile.test.ts +228 -0
- package/src/__tests__/workspace-migration-remove-legacy-skills-index.test.ts +309 -0
- package/src/__tests__/workspace-migrations-runner.test.ts +111 -3
- package/src/a2a/__tests__/agent-card.test.ts +98 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +597 -0
- package/src/a2a/__tests__/protocol-helpers.test.ts +113 -0
- package/src/a2a/__tests__/task-store.test.ts +246 -0
- package/src/a2a/agent-card.ts +58 -0
- package/src/a2a/feature-gate.ts +8 -0
- package/src/a2a/protocol-constants.ts +21 -0
- package/src/a2a/protocol-errors.ts +50 -0
- package/src/a2a/protocol-types.ts +162 -0
- package/src/a2a/task-store.ts +168 -0
- package/src/acp/resolve-agent.ts +1 -1
- package/src/agent/image-optimize.ts +13 -5
- package/src/agent/loop.ts +167 -18
- package/src/calls/voice-session-bridge.ts +61 -42
- package/src/channels/config.ts +9 -0
- package/src/channels/types.ts +122 -0
- package/src/cli/__tests__/unknown-command.test.ts +24 -0
- package/src/cli/commands/__tests__/changelog.test.ts +304 -319
- package/src/cli/{__tests__ → commands/__tests__}/notifications.test.ts +201 -28
- package/src/cli/commands/__tests__/schedules.test.ts +960 -0
- package/src/cli/commands/changelog.ts +106 -42
- package/src/cli/commands/conversations.ts +102 -17
- package/src/cli/commands/default-action.ts +10 -53
- package/src/cli/commands/notifications.ts +388 -346
- package/src/cli/commands/plugins.ts +252 -0
- package/src/cli/commands/schedules.ts +683 -0
- package/src/cli/commands/telemetry.ts +40 -0
- package/src/cli/lib/__tests__/cli-colors.test.ts +48 -0
- package/src/cli/lib/__tests__/confirm-prompt.test.ts +159 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +355 -0
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +154 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +261 -0
- package/src/cli/lib/__tests__/uninstall-plugin.test.ts +124 -0
- package/src/cli/lib/__tests__/unknown-command.test.ts +106 -0
- package/src/cli/lib/cli-colors.ts +12 -0
- package/src/cli/lib/confirm-prompt.ts +79 -0
- package/src/cli/lib/install-from-github.ts +303 -0
- package/src/cli/lib/list-installed-plugins.ts +137 -0
- package/src/cli/lib/search-plugins.ts +163 -0
- package/src/cli/lib/uninstall-plugin.ts +82 -0
- package/src/cli/lib/unknown-command.ts +111 -0
- package/src/cli/program.ts +52 -2
- package/src/config/assistant-feature-flags.ts +24 -54
- package/src/config/bundled-skills/app-builder/SKILL.md +140 -22
- package/src/config/bundled-skills/app-builder/TOOLS.json +7 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +15 -52
- package/src/config/bundled-skills/document/SKILL.md +23 -3
- package/src/config/bundled-skills/document/TOOLS.json +53 -0
- package/src/config/bundled-skills/document/tools/document-delete.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-list.ts +12 -0
- package/src/config/bundled-skills/document/tools/document-read.ts +12 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -1
- package/src/config/bundled-skills/skill-management/SKILL.md +2 -2
- package/src/config/bundled-skills/skill-management/TOOLS.json +7 -7
- package/src/config/bundled-tool-registry.ts +6 -0
- package/src/config/call-site-defaults.ts +105 -0
- package/src/config/feature-flag-registry.json +41 -9
- package/src/config/llm-resolver.ts +52 -1
- package/src/config/loader.ts +64 -38
- package/src/config/schema.ts +9 -10
- package/src/config/schemas/__tests__/llm-request-logs.test.ts +36 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +3 -3
- package/src/config/schemas/channels.ts +17 -0
- package/src/config/schemas/compaction.ts +28 -0
- package/src/config/schemas/conversations.ts +10 -0
- package/src/config/schemas/heartbeat.ts +23 -0
- package/src/config/schemas/llm-request-logs.ts +31 -7
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/memory-retrieval.ts +18 -0
- package/src/config/schemas/memory-retrospective.ts +1 -1
- package/src/config/schemas/memory-v2.ts +4 -4
- package/src/config/schemas/memory.ts +3 -1
- package/src/config/schemas/tools.ts +14 -0
- package/src/config/seed-inference-profiles.ts +99 -29
- package/src/config/skills.ts +3 -96
- package/src/context/compactor.ts +1107 -0
- package/src/context/token-estimator.ts +34 -36
- package/src/context/window-manager.ts +197 -1520
- package/src/credential-execution/managed-catalog.ts +37 -0
- package/src/credential-health/credential-health-service.ts +280 -19
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +33 -18
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +138 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +74 -0
- package/src/daemon/approval-generators.ts +8 -6
- package/src/daemon/config-watcher.ts +94 -31
- package/src/daemon/conversation-agent-loop-handlers.ts +78 -0
- package/src/daemon/conversation-agent-loop.ts +198 -11
- package/src/daemon/conversation-error.ts +171 -37
- package/src/daemon/conversation-lifecycle.ts +53 -40
- package/src/daemon/conversation-messaging.ts +25 -6
- package/src/daemon/conversation-process.ts +49 -12
- package/src/daemon/conversation-runtime-assembly.ts +25 -1
- package/src/daemon/conversation-slash.ts +12 -5
- package/src/daemon/conversation-store.ts +11 -4
- package/src/daemon/conversation-tool-setup.ts +39 -7
- package/src/daemon/conversation.ts +33 -8
- package/src/daemon/date-context.ts +40 -0
- package/src/daemon/external-plugins-bootstrap.ts +217 -181
- package/src/daemon/first-greeting.ts +22 -2
- package/src/daemon/guardian-action-generators.ts +1 -125
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +248 -0
- package/src/daemon/handlers/__tests__/config-a2a-invite.test.ts +154 -0
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +133 -0
- package/src/daemon/handlers/__tests__/config-a2a.test.ts +95 -0
- package/src/daemon/handlers/config-a2a.ts +289 -0
- package/src/daemon/handlers/config-model.ts +6 -5
- package/src/daemon/handlers/config-slack-channel.ts +15 -3
- package/src/daemon/handlers/conversations.ts +1 -0
- package/src/daemon/handlers/shared.ts +14 -5
- package/src/daemon/handlers/skills.ts +111 -108
- package/src/daemon/history-repair.ts +28 -1
- package/src/daemon/host-app-control-proxy.ts +153 -27
- package/src/daemon/host-proxy-preactivation.ts +85 -18
- package/src/daemon/lifecycle.ts +89 -91
- package/src/daemon/meet-host-supervisor.ts +5 -4
- package/src/daemon/memory-v2-startup.ts +85 -0
- package/src/daemon/message-protocol.ts +1 -0
- package/src/daemon/message-types/conversations.ts +25 -0
- package/src/daemon/message-types/messages.ts +61 -0
- package/src/daemon/message-types/notifications.ts +21 -0
- package/src/daemon/message-types/subagents.ts +1 -0
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/pkb-reminder-builder.test.ts +11 -54
- package/src/daemon/pkb-reminder-builder.ts +5 -20
- package/src/daemon/plugin-source-watcher.ts +146 -0
- package/src/daemon/process-message.ts +24 -3
- package/src/daemon/server.ts +11 -2
- package/src/daemon/skill-memory-refresh.ts +33 -0
- package/src/daemon/wake-target-adapter.ts +2 -0
- package/src/documents/document-store.ts +221 -3
- package/src/embedded/plugin-api.ts +40 -0
- package/src/export/__tests__/transcript-formatter.test.ts +121 -0
- package/src/export/transcript-formatter.ts +54 -20
- package/src/filing/filing-service.ts +39 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +135 -6
- package/src/heartbeat/heartbeat-run-store.ts +2 -1
- package/src/heartbeat/heartbeat-service.ts +73 -189
- package/src/home/__tests__/feed-types.test.ts +80 -0
- package/src/home/feed-types.ts +36 -2
- package/src/home/post-connect-feed.ts +1 -0
- package/src/index.ts +18 -1
- package/src/ipc/cli-client.ts +147 -45
- package/src/live-voice/__tests__/live-voice-stt.test.ts +57 -0
- package/src/mcp/client.ts +20 -4
- package/src/media/image-credentials.ts +3 -3
- package/src/memory/__tests__/bookmark-crud.test.ts +33 -27
- package/src/memory/__tests__/conversation-queries.test.ts +483 -0
- package/src/memory/__tests__/jobs-worker-v2-graph-trigger-embed.test.ts +113 -0
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +2 -50
- package/src/memory/__tests__/memory-retrospective-job.test.ts +87 -4
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +119 -14
- package/src/memory/__tests__/message-content.test.ts +35 -0
- package/src/memory/bookmark-crud.ts +42 -10
- package/src/memory/context-search/sources/conversations.ts +62 -2
- package/src/memory/context-search/sources/workspace.ts +4 -0
- package/src/memory/conversation-crud.ts +63 -19
- package/src/memory/conversation-queries.ts +197 -11
- package/src/memory/conversation-title-service.ts +26 -4
- package/src/memory/db-init.ts +12 -0
- package/src/memory/delivery-crud.ts +152 -5
- package/src/memory/embedding-backend.ts +4 -4
- package/src/memory/external-conversation-store.ts +66 -5
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +150 -12
- package/src/memory/graph/conversation-graph-memory.ts +49 -21
- package/src/memory/graph/tools.ts +9 -40
- package/src/memory/indexer.ts +34 -29
- package/src/memory/invite-store.ts +53 -0
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +73 -0
- package/src/memory/jobs/embed-concept-page.ts +20 -11
- package/src/memory/jobs-worker.ts +6 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +24 -12
- package/src/memory/llm-request-log-source.ts +19 -52
- package/src/memory/llm-request-log-store.ts +92 -1
- package/src/memory/llm-usage-store.ts +125 -5
- package/src/memory/memory-retrospective-enqueue.ts +1 -20
- package/src/memory/memory-retrospective-job.ts +33 -6
- package/src/memory/memory-retrospective-startup-cleanup.ts +72 -5
- package/src/memory/message-content.ts +1 -1
- package/src/memory/migrations/109-external-conversation-bindings.ts +15 -4
- package/src/memory/migrations/229-delete-private-conversations.test.ts +38 -1
- package/src/memory/migrations/229-delete-private-conversations.ts +7 -0
- package/src/memory/migrations/247-external-conversation-binding-thread-id.ts +78 -0
- package/src/memory/migrations/248-create-onboarding-events.ts +21 -0
- package/src/memory/migrations/249-normalize-slack-external-content.ts +240 -0
- package/src/memory/migrations/250-provider-connection-base-url-and-models.ts +28 -0
- package/src/memory/migrations/251-a2a-tasks.ts +49 -0
- package/src/memory/migrations/252-llm-request-log-agent-loop-exit-reason.ts +32 -0
- package/src/memory/migrations/index.ts +9 -0
- package/src/memory/migrations/registry.ts +16 -0
- package/src/memory/onboarding-events-store.ts +106 -0
- package/src/memory/schema/a2a.ts +15 -0
- package/src/memory/schema/bookmarks.ts +0 -2
- package/src/memory/schema/calls.ts +1 -0
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/inference.ts +3 -3
- package/src/memory/schema/infrastructure.ts +13 -0
- package/src/memory/turn-events-store.ts +127 -2
- package/src/memory/v2/__tests__/activation-store.test.ts +25 -23
- package/src/memory/v2/__tests__/activation.test.ts +0 -8
- package/src/memory/v2/__tests__/cli-command-store.test.ts +404 -0
- package/src/memory/v2/__tests__/frontmatter-sweep.test.ts +25 -4
- package/src/memory/v2/__tests__/injection.test.ts +288 -11
- package/src/memory/v2/__tests__/migration.test.ts +87 -0
- package/src/memory/v2/__tests__/page-index.test.ts +83 -0
- package/src/memory/v2/__tests__/prompts-router.test.ts +58 -6
- package/src/memory/v2/__tests__/qdrant.test.ts +66 -3
- package/src/memory/v2/__tests__/router.test.ts +15 -0
- package/src/memory/v2/__tests__/skill-store.test.ts +387 -8
- package/src/memory/v2/__tests__/static-context.test.ts +12 -1
- package/src/memory/v2/activation-store.ts +14 -16
- package/src/memory/v2/cli-command-content.ts +19 -0
- package/src/memory/v2/cli-command-store.ts +304 -0
- package/src/memory/v2/frontmatter-sweep.ts +7 -1
- package/src/memory/v2/injection.ts +81 -26
- package/src/memory/v2/migration.ts +49 -19
- package/src/memory/v2/page-index.ts +63 -8
- package/src/memory/v2/prompts/router.ts +11 -8
- package/src/memory/v2/prompts/sweep.ts +2 -2
- package/src/memory/v2/qdrant.ts +135 -7
- package/src/memory/v2/router.ts +9 -8
- package/src/memory/v2/skill-store.ts +120 -35
- package/src/memory/v2/static-context.ts +4 -4
- package/src/memory/v2/types.ts +23 -0
- package/src/messaging/providers/a2a/__tests__/deliver.test.ts +274 -0
- package/src/messaging/providers/a2a/deliver.ts +156 -0
- package/src/messaging/providers/gmail/client.ts +9 -2
- package/src/messaging/providers/index.ts +11 -2
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +45 -5
- package/src/messaging/providers/slack/__tests__/download.test.ts +231 -0
- package/src/messaging/providers/slack/adapter.ts +43 -5
- package/src/messaging/providers/slack/client.ts +27 -0
- package/src/messaging/providers/slack/deep-link.ts +65 -0
- package/src/messaging/providers/slack/download.ts +104 -0
- package/src/messaging/providers/slack/message-metadata.test.ts +32 -0
- package/src/messaging/providers/slack/message-metadata.ts +27 -0
- package/src/messaging/providers/slack/render-transcript.test.ts +134 -0
- package/src/messaging/providers/slack/render-transcript.ts +69 -5
- package/src/messaging/providers/slack/types.ts +20 -1
- package/src/notifications/__tests__/broadcaster.test.ts +203 -0
- package/src/notifications/__tests__/decision-engine.test.ts +283 -0
- package/src/notifications/__tests__/deterministic-checks.test.ts +286 -0
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +430 -7
- package/src/notifications/adapters/macos.ts +12 -2
- package/src/notifications/broadcaster.ts +29 -4
- package/src/notifications/conversation-pairing.ts +2 -1
- package/src/notifications/copy-composer.ts +17 -64
- package/src/notifications/decision-engine.ts +113 -45
- package/src/notifications/deterministic-checks.ts +96 -0
- package/src/notifications/emit-signal.ts +21 -1
- package/src/notifications/home-feed-side-effect.ts +138 -5
- package/src/notifications/signal.ts +3 -5
- package/src/notifications/types.ts +8 -0
- package/src/oauth/connection-resolver.ts +8 -4
- package/src/oauth/platform-connection.test.ts +43 -3
- package/src/oauth/platform-connection.ts +19 -6
- package/src/oauth/seed-providers.ts +10 -1
- package/src/permissions/checker.ts +2 -0
- package/src/permissions/ipc-risk-types.ts +1 -0
- package/src/permissions/question-prompter.test.ts +416 -0
- package/src/permissions/question-prompter.ts +294 -0
- package/src/platform/client.test.ts +1 -1
- package/src/platform/client.ts +1 -1
- package/src/plugin-api/constants.ts +26 -0
- package/src/plugin-api/index.ts +34 -1
- package/src/plugin-api/types.ts +104 -22
- package/src/plugins/defaults/circuit-breaker.ts +0 -5
- package/src/plugins/defaults/compaction.ts +0 -4
- package/src/plugins/defaults/empty-response.ts +0 -2
- package/src/plugins/defaults/history-repair.ts +0 -2
- package/src/plugins/defaults/injectors.ts +74 -22
- package/src/plugins/defaults/llm-call.ts +0 -2
- package/src/plugins/defaults/memory-retrieval.ts +0 -1
- package/src/plugins/defaults/overflow-reduce.ts +0 -1
- package/src/plugins/defaults/persistence.ts +0 -2
- package/src/plugins/defaults/title-generate.ts +0 -5
- package/src/plugins/defaults/token-estimate.ts +0 -2
- package/src/plugins/defaults/tool-error.ts +0 -7
- package/src/plugins/defaults/tool-execute.ts +0 -2
- package/src/plugins/defaults/tool-result-truncate.ts +0 -4
- package/src/plugins/ensure-plugin-api-shim.ts +96 -0
- package/src/plugins/external-api.ts +104 -0
- package/src/plugins/external-plugin-loader.ts +187 -42
- package/src/plugins/feature-gate.ts +22 -0
- package/src/plugins/pipeline.ts +37 -0
- package/src/plugins/registry.ts +48 -80
- package/src/plugins/types.ts +40 -26
- package/src/plugins/user-loader.ts +21 -2
- package/src/proactive-artifact/aux-message-injector.ts +11 -0
- package/src/proactive-artifact/job.test.ts +37 -5
- package/src/prompts/__tests__/system-prompt.test.ts +10 -43
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +95 -0
- package/src/prompts/normalize-onboarding.ts +27 -0
- package/src/prompts/sections.ts +302 -0
- package/src/prompts/system-prompt.ts +63 -174
- package/src/prompts/templates/BOOTSTRAP.md +17 -1
- package/src/prompts/templates/system-sections.ts +164 -0
- package/src/providers/__tests__/inference.test.ts +24 -7
- package/src/providers/anthropic/client.ts +28 -28
- package/src/providers/call-site-routing.ts +24 -6
- package/src/providers/connection-resolution.ts +68 -11
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +74 -0
- package/src/providers/inference/__tests__/connections-openai-compatible.test.ts +175 -0
- package/src/providers/inference/__tests__/connections-status-label.test.ts +15 -0
- package/src/providers/inference/adapter-factory.ts +32 -6
- package/src/providers/inference/auth.ts +12 -0
- package/src/providers/inference/backfill.ts +14 -1
- package/src/providers/inference/connections.ts +159 -34
- package/src/providers/inference/resolve-auth.ts +14 -4
- package/src/providers/model-catalog.ts +249 -12
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +235 -0
- package/src/providers/openai/chat-completions-provider.ts +169 -8
- package/src/providers/openrouter/client.ts +49 -4
- package/src/providers/{managed-proxy → platform-proxy}/constants.ts +4 -2
- package/src/providers/{managed-proxy → platform-proxy}/context.ts +3 -3
- package/src/providers/provider-availability.ts +17 -2
- package/src/providers/provider-catalog-visibility.ts +38 -0
- package/src/providers/provider-send-message.ts +27 -12
- package/src/providers/registry.ts +52 -15
- package/src/providers/retry.ts +47 -1
- package/src/runtime/__tests__/agent-wake.test.ts +152 -0
- package/src/runtime/agent-wake.ts +103 -15
- package/src/runtime/auth/route-policy.ts +21 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/http-server.ts +7 -16
- package/src/runtime/http-types.ts +19 -47
- package/src/runtime/migrations/origin-mode.ts +1 -1
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +17 -0
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +258 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +5 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +172 -23
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +275 -44
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +12 -0
- package/src/runtime/routes/__tests__/question-routes.test.ts +395 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +64 -1
- package/src/runtime/routes/acp-routes-list.test.ts +143 -0
- package/src/runtime/routes/acp-routes.ts +5 -3
- package/src/runtime/routes/auth-routes.ts +1 -1
- package/src/runtime/routes/bookmark-routes.ts +5 -3
- package/src/runtime/routes/btw-routes.ts +5 -1
- package/src/runtime/routes/channel-availability-routes.ts +126 -0
- package/src/runtime/routes/consolidation-routes.ts +100 -0
- package/src/runtime/routes/conversation-cli-routes.ts +44 -3
- package/src/runtime/routes/conversation-list-routes.ts +3 -20
- package/src/runtime/routes/conversation-management-routes.ts +17 -42
- package/src/runtime/routes/conversation-query-routes.ts +99 -35
- package/src/runtime/routes/conversation-routes.ts +97 -11
- package/src/runtime/routes/documents-routes.ts +25 -86
- package/src/runtime/routes/group-routes.ts +5 -0
- package/src/runtime/routes/inbound-conversation.ts +28 -8
- package/src/runtime/routes/inbound-message-handler.ts +236 -41
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +111 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +32 -1
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +17 -4
- package/src/runtime/routes/index.ts +8 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +17 -44
- package/src/runtime/routes/inference-profile-session-reaper.ts +7 -21
- package/src/runtime/routes/inference-provider-connection-routes.ts +199 -22
- package/src/runtime/routes/integrations/a2a.ts +235 -0
- package/src/runtime/routes/integrations/slack/share.ts +4 -52
- package/src/runtime/routes/integrations/slack/token.ts +43 -0
- package/src/runtime/routes/integrations/twilio.ts +6 -13
- package/src/runtime/routes/llm-call-sites-routes.ts +11 -1
- package/src/runtime/routes/notification-routes.ts +1 -1
- package/src/runtime/routes/oauth-commands-routes.ts +105 -15
- package/src/runtime/routes/oauth-lifecycle-routes.ts +43 -0
- package/src/runtime/routes/question-routes.ts +259 -0
- package/src/runtime/routes/rename-conversation-routes.ts +2 -33
- package/src/runtime/routes/schedule-routes.ts +4 -7
- package/src/runtime/routes/subagents-routes.ts +98 -18
- package/src/runtime/routes/telemetry-routes.ts +27 -0
- package/src/runtime/routes/tts-routes.ts +27 -2
- package/src/runtime/routes/workspace-routes.test.ts +43 -0
- package/src/runtime/routes/workspace-routes.ts +28 -0
- package/src/runtime/services/conversation-serializer.ts +39 -7
- package/src/runtime/sync/resource-sync-events.ts +93 -1
- package/src/schedule/schedule-store.ts +27 -2
- package/src/schedule/scheduler.ts +9 -1
- package/src/security/__tests__/untrusted-content.test.ts +86 -0
- package/src/security/untrusted-content.ts +93 -8
- package/src/skills/catalog-files.ts +1 -1
- package/src/skills/catalog-install.ts +233 -116
- package/src/skills/clawhub.ts +70 -13
- package/src/skills/managed-store.ts +4 -119
- package/src/skills/skillssh-registry.ts +27 -48
- package/src/subagent/manager.ts +17 -7
- package/src/telemetry/types.ts +113 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +312 -5
- package/src/telemetry/usage-telemetry-reporter.ts +113 -7
- package/src/tools/apps/executors.ts +58 -7
- package/src/tools/ask-question/ask-question-tool.test.ts +509 -0
- package/src/tools/ask-question/ask-question-tool.ts +304 -0
- package/src/tools/browser/browser-execution.ts +15 -11
- package/src/tools/computer-use/definitions.ts +3 -3
- package/src/tools/credentials/vault.ts +1 -1
- package/src/tools/document/document-tool.ts +124 -1
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +5 -2
- package/src/tools/host-filesystem/transfer.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +1 -1
- package/src/tools/memory/register.ts +1 -9
- package/src/tools/permission-checker.ts +1 -1
- package/src/tools/registry.ts +17 -7
- package/src/tools/schedule/create.ts +2 -2
- package/src/tools/schema-transforms.ts +7 -2
- package/src/tools/side-effects.ts +1 -0
- package/src/tools/skills/delete-managed.ts +4 -4
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/scaffold-managed.ts +3 -2
- package/src/tools/subagent/notify-parent.ts +1 -1
- package/src/tools/system/request-permission.ts +2 -2
- package/src/tools/terminal/safe-env.ts +60 -1
- package/src/tools/tool-manifest.ts +2 -0
- package/src/tools/types.ts +107 -21
- package/src/tools/ui-surface/definitions.ts +6 -5
- package/src/tts/__tests__/provider-adapters.test.ts +76 -2
- package/src/tts/providers/elevenlabs-provider.ts +75 -1
- package/src/types/onboarding-context.ts +2 -0
- package/src/util/errors.ts +17 -0
- package/src/util/platform.ts +10 -0
- package/src/watcher/__tests__/engine.test.ts +22 -0
- package/src/watcher/engine.ts +6 -2
- package/src/workspace/migrations/057-repair-stale-gemini-model-ids.ts +80 -15
- package/src/workspace/migrations/072-seed-reply-suggestion-callsite.ts +35 -22
- package/src/workspace/migrations/073-repair-recall-callsite-empty-profile.ts +3 -1
- package/src/workspace/migrations/083-system-prompt-prefix-to-file.ts +191 -0
- package/src/workspace/migrations/084-remove-legacy-skills-index.ts +276 -0
- package/src/workspace/migrations/085-memory-v2-bm25-b-reembed-disabled-v2-pages.ts +137 -0
- package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +198 -0
- package/src/workspace/migrations/087-memory-router-balanced-profile.ts +91 -0
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/migrations/runner.ts +39 -9
- package/src/workspace/migrations/types.ts +4 -0
- package/examples/plugins/echo/bun.lock +0 -25
- package/src/__tests__/context-window-manager.test.ts +0 -2481
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -441
- package/src/context/__tests__/compact-prompt.test.ts +0 -63
- package/src/context/prompts/compact.md +0 -26
- package/src/memory/graph/__tests__/remember-description.test.ts +0 -55
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +0 -37
- package/src/runtime/guardian-action-conversation-turn.ts +0 -99
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import {
|
|
3
|
+
basename,
|
|
4
|
+
dirname,
|
|
5
|
+
isAbsolute,
|
|
6
|
+
join,
|
|
7
|
+
normalize,
|
|
8
|
+
relative,
|
|
9
|
+
resolve,
|
|
10
|
+
} from "node:path";
|
|
11
|
+
|
|
12
|
+
import { getLogger } from "../../util/logger.js";
|
|
13
|
+
import type { WorkspaceMigration } from "./types.js";
|
|
14
|
+
|
|
15
|
+
const log = getLogger("workspace-migration-084-remove-legacy-skills-index");
|
|
16
|
+
|
|
17
|
+
function isNotFoundError(err: unknown): boolean {
|
|
18
|
+
return (
|
|
19
|
+
typeof err === "object" &&
|
|
20
|
+
err !== null &&
|
|
21
|
+
"code" in err &&
|
|
22
|
+
err.code === "ENOENT"
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function isInsideDirectory(rootDir: string, candidatePath: string): boolean {
|
|
27
|
+
const rootRealPath = fs.realpathSync(rootDir);
|
|
28
|
+
const candidateRealPath = fs.realpathSync(candidatePath);
|
|
29
|
+
const relativePath = relative(rootRealPath, candidateRealPath);
|
|
30
|
+
return (
|
|
31
|
+
relativePath === "" ||
|
|
32
|
+
(!relativePath.startsWith("..") && !isAbsolute(relativePath))
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function parseLegacySkillIndexEntry(line: string): string | null {
|
|
37
|
+
const match = line.match(/^\s*[-*]\s+(.+?)\s*$/);
|
|
38
|
+
if (!match) return null;
|
|
39
|
+
|
|
40
|
+
let entry = match[1].trim();
|
|
41
|
+
const markdownLink = entry.match(/^\[.+?\]\((.+?)\)$/);
|
|
42
|
+
if (markdownLink) {
|
|
43
|
+
entry = markdownLink[1].trim();
|
|
44
|
+
} else {
|
|
45
|
+
entry = entry.split(/\s+/)[0]?.trim() ?? "";
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
entry = entry.replace(/^`|`$/g, "");
|
|
49
|
+
if (!entry || entry.includes("\0") || isAbsolute(entry)) return null;
|
|
50
|
+
|
|
51
|
+
const normalized = normalize(entry);
|
|
52
|
+
if (
|
|
53
|
+
normalized === "." ||
|
|
54
|
+
normalized.startsWith("..") ||
|
|
55
|
+
isAbsolute(normalized)
|
|
56
|
+
) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (basename(normalized).toLowerCase() === "skill.md") {
|
|
61
|
+
const skillDir = dirname(normalized);
|
|
62
|
+
return skillDir === "." ? null : skillDir;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return normalized;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function parseLegacySkillIndexEntries(contents: string): string[] {
|
|
69
|
+
const entries = new Set<string>();
|
|
70
|
+
for (const line of contents.split(/\r?\n/)) {
|
|
71
|
+
const entry = parseLegacySkillIndexEntry(line);
|
|
72
|
+
if (entry) entries.add(entry);
|
|
73
|
+
}
|
|
74
|
+
return [...entries];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function skillFileContentsMatch(
|
|
78
|
+
sourceDir: string,
|
|
79
|
+
destinationDir: string,
|
|
80
|
+
): boolean {
|
|
81
|
+
try {
|
|
82
|
+
return (
|
|
83
|
+
fs.readFileSync(join(sourceDir, "SKILL.md"), "utf-8") ===
|
|
84
|
+
fs.readFileSync(join(destinationDir, "SKILL.md"), "utf-8")
|
|
85
|
+
);
|
|
86
|
+
} catch {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function topLevelPreservationName(relativeSkillDir: string): string {
|
|
92
|
+
return relativeSkillDir
|
|
93
|
+
.split(/[\\/]+/)
|
|
94
|
+
.filter(Boolean)
|
|
95
|
+
.join("__");
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function getPreservationDestinationDir(
|
|
99
|
+
skillsDir: string,
|
|
100
|
+
sourceDir: string,
|
|
101
|
+
relativeSkillDir: string,
|
|
102
|
+
): string | null {
|
|
103
|
+
const skillName = basename(relativeSkillDir);
|
|
104
|
+
if (!skillName || skillName === relativeSkillDir) return null;
|
|
105
|
+
|
|
106
|
+
const alternateName = topLevelPreservationName(relativeSkillDir);
|
|
107
|
+
const candidateNames = [
|
|
108
|
+
skillName,
|
|
109
|
+
...(alternateName && alternateName !== skillName ? [alternateName] : []),
|
|
110
|
+
];
|
|
111
|
+
|
|
112
|
+
for (const candidateName of candidateNames) {
|
|
113
|
+
const destinationDir = join(skillsDir, candidateName);
|
|
114
|
+
if (!fs.existsSync(destinationDir)) return destinationDir;
|
|
115
|
+
if (skillFileContentsMatch(sourceDir, destinationDir)) {
|
|
116
|
+
log.info(
|
|
117
|
+
{ destinationDir, sourceDir },
|
|
118
|
+
"Nested indexed skill already preserved at top-level skills directory",
|
|
119
|
+
);
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const baseName =
|
|
125
|
+
alternateName && alternateName !== skillName
|
|
126
|
+
? alternateName
|
|
127
|
+
: `legacy__${skillName}`;
|
|
128
|
+
for (let suffix = 2; ; suffix += 1) {
|
|
129
|
+
const destinationDir = join(skillsDir, `${baseName}-${suffix}`);
|
|
130
|
+
if (!fs.existsSync(destinationDir)) return destinationDir;
|
|
131
|
+
if (skillFileContentsMatch(sourceDir, destinationDir)) {
|
|
132
|
+
log.info(
|
|
133
|
+
{ destinationDir, sourceDir },
|
|
134
|
+
"Nested indexed skill already preserved at top-level skills directory",
|
|
135
|
+
);
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function preserveNestedIndexedSkill(
|
|
142
|
+
tempRootDir: string,
|
|
143
|
+
skillsDir: string,
|
|
144
|
+
relativeSkillDir: string,
|
|
145
|
+
): void {
|
|
146
|
+
const sourceDir = resolve(skillsDir, relativeSkillDir);
|
|
147
|
+
let destinationDir: string | null = null;
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
if (!fs.existsSync(sourceDir)) return;
|
|
151
|
+
if (!isInsideDirectory(skillsDir, sourceDir)) {
|
|
152
|
+
log.warn(
|
|
153
|
+
{ relativeSkillDir, sourceDir },
|
|
154
|
+
"Skipping nested indexed skill that resolves outside skills root",
|
|
155
|
+
);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const sourceStat = fs.lstatSync(sourceDir);
|
|
160
|
+
if (!sourceStat.isDirectory()) return;
|
|
161
|
+
|
|
162
|
+
const skillFilePath = join(sourceDir, "SKILL.md");
|
|
163
|
+
if (!fs.existsSync(skillFilePath)) return;
|
|
164
|
+
if (!isInsideDirectory(sourceDir, skillFilePath)) {
|
|
165
|
+
log.warn(
|
|
166
|
+
{ skillFilePath, sourceDir },
|
|
167
|
+
"Skipping nested indexed skill with SKILL.md outside skill directory",
|
|
168
|
+
);
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
const skillFileStat = fs.lstatSync(skillFilePath);
|
|
173
|
+
if (!skillFileStat.isFile()) return;
|
|
174
|
+
|
|
175
|
+
destinationDir = getPreservationDestinationDir(
|
|
176
|
+
skillsDir,
|
|
177
|
+
sourceDir,
|
|
178
|
+
relativeSkillDir,
|
|
179
|
+
);
|
|
180
|
+
if (!destinationDir) return;
|
|
181
|
+
|
|
182
|
+
fs.mkdirSync(tempRootDir, { recursive: true });
|
|
183
|
+
const tempDir = join(tempRootDir, basename(destinationDir));
|
|
184
|
+
if (fs.existsSync(tempDir)) {
|
|
185
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
fs.cpSync(sourceDir, tempDir, {
|
|
189
|
+
dereference: false,
|
|
190
|
+
errorOnExist: true,
|
|
191
|
+
force: false,
|
|
192
|
+
recursive: true,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
if (fs.existsSync(destinationDir)) {
|
|
196
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
197
|
+
if (skillFileContentsMatch(sourceDir, destinationDir)) return;
|
|
198
|
+
log.warn(
|
|
199
|
+
{ destinationDir, sourceDir },
|
|
200
|
+
"Skipping nested indexed skill preservation because destination appeared during copy",
|
|
201
|
+
);
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
fs.renameSync(tempDir, destinationDir);
|
|
206
|
+
log.info(
|
|
207
|
+
{ destinationDir, sourceDir },
|
|
208
|
+
"Preserved nested indexed skill at top-level skills directory",
|
|
209
|
+
);
|
|
210
|
+
} catch (err) {
|
|
211
|
+
if (isNotFoundError(err)) return;
|
|
212
|
+
log.warn(
|
|
213
|
+
{ err, relativeSkillDir, sourceDir, destinationDir },
|
|
214
|
+
"Failed to preserve nested indexed skill",
|
|
215
|
+
);
|
|
216
|
+
throw err;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function preserveNestedIndexedSkills(
|
|
221
|
+
workspaceDir: string,
|
|
222
|
+
skillsDir: string,
|
|
223
|
+
indexPath: string,
|
|
224
|
+
): void {
|
|
225
|
+
const contents = fs.readFileSync(indexPath, "utf-8");
|
|
226
|
+
const tempRootDir = join(
|
|
227
|
+
workspaceDir,
|
|
228
|
+
".workspace-migration-084-remove-legacy-skills-index",
|
|
229
|
+
);
|
|
230
|
+
for (const relativeSkillDir of parseLegacySkillIndexEntries(contents)) {
|
|
231
|
+
preserveNestedIndexedSkill(tempRootDir, skillsDir, relativeSkillDir);
|
|
232
|
+
}
|
|
233
|
+
if (fs.existsSync(tempRootDir)) {
|
|
234
|
+
fs.rmSync(tempRootDir, { recursive: true, force: true });
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export const removeLegacySkillsIndexMigration: WorkspaceMigration = {
|
|
239
|
+
id: "084-remove-legacy-skills-index",
|
|
240
|
+
description: "Remove legacy workspace skills/SKILLS.md index file",
|
|
241
|
+
retryFailedCheckpoint: true,
|
|
242
|
+
|
|
243
|
+
run(workspaceDir: string): void {
|
|
244
|
+
const skillsDir = join(workspaceDir, "skills");
|
|
245
|
+
const indexPath = join(skillsDir, "SKILLS.md");
|
|
246
|
+
|
|
247
|
+
try {
|
|
248
|
+
const stat = fs.lstatSync(indexPath);
|
|
249
|
+
if (!stat.isFile() && !stat.isSymbolicLink()) {
|
|
250
|
+
log.warn(
|
|
251
|
+
{ path: indexPath },
|
|
252
|
+
"Legacy SKILLS.md path is not a file; leaving it in place",
|
|
253
|
+
);
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
if (stat.isFile()) {
|
|
258
|
+
preserveNestedIndexedSkills(workspaceDir, skillsDir, indexPath);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
fs.unlinkSync(indexPath);
|
|
262
|
+
log.info({ path: indexPath }, "Removed legacy skills index file");
|
|
263
|
+
} catch (err) {
|
|
264
|
+
if (isNotFoundError(err)) return;
|
|
265
|
+
log.warn(
|
|
266
|
+
{ err, path: indexPath },
|
|
267
|
+
"Failed to remove legacy skills index file",
|
|
268
|
+
);
|
|
269
|
+
throw err;
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
|
|
273
|
+
down(_workspaceDir: string): void {
|
|
274
|
+
// Forward-only: SKILLS.md is no longer a supported skill catalog format.
|
|
275
|
+
},
|
|
276
|
+
};
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { Database } from "bun:sqlite";
|
|
5
|
+
|
|
6
|
+
import type { WorkspaceMigration } from "./types.js";
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Follow-up to `075-memory-v2-bm25-b-default-reembed`. Migration 075 shipped
|
|
10
|
+
* in v0.8.1 with no gating beyond "db exists", so workspaces with no v2
|
|
11
|
+
* pages still recorded a checkpoint entry and a queued reembed job. We
|
|
12
|
+
* cannot edit 075 to add gating retroactively — the runner skips any
|
|
13
|
+
* already-checkpointed id — so this migration re-runs the enqueue with the
|
|
14
|
+
* gating we want now.
|
|
15
|
+
*
|
|
16
|
+
* Two gates:
|
|
17
|
+
*
|
|
18
|
+
* 1. `hasConceptPages` — only enqueue if `memory/concepts/` actually has a
|
|
19
|
+
* `.md` page. Workspaces that never wrote a v2 page have nothing to
|
|
20
|
+
* reembed.
|
|
21
|
+
* 2. `isMemoryV2Disabled` — skip when `memory.v2.enabled` is explicitly
|
|
22
|
+
* `false`. The worker does not currently gate `memory_v2_reembed`
|
|
23
|
+
* dispatch on the config flag, so enqueueing for a workspace that
|
|
24
|
+
* intentionally disabled v2 would immediately re-embed pages and hit
|
|
25
|
+
* the embedding backend against the user's intent.
|
|
26
|
+
*
|
|
27
|
+
* When either gate fails, we also DELETE any pending `memory_v2_reembed`
|
|
28
|
+
* job that 075 may have enqueued in the same startup sweep. For workspaces
|
|
29
|
+
* that never checkpointed 075 (upgrades from pre-v0.8.1, first-boot sweeps),
|
|
30
|
+
* 075 runs first and unconditionally enqueues a job; without the explicit
|
|
31
|
+
* cancellation here, 075's job would survive 085's gate and execute against
|
|
32
|
+
* the user's intent.
|
|
33
|
+
*/
|
|
34
|
+
export const memoryV2Bm25BReembedDisabledV2PagesMigration: WorkspaceMigration =
|
|
35
|
+
{
|
|
36
|
+
id: "085-memory-v2-bm25-b-reembed-disabled-v2-pages",
|
|
37
|
+
description:
|
|
38
|
+
"Re-enqueue memory_v2_reembed for workspaces with v2 pages, gated on v2 not being explicitly disabled",
|
|
39
|
+
|
|
40
|
+
run(workspaceDir: string): void {
|
|
41
|
+
const dbPath = join(workspaceDir, "data", "db", "assistant.db");
|
|
42
|
+
if (!existsSync(dbPath)) return;
|
|
43
|
+
|
|
44
|
+
let db: Database;
|
|
45
|
+
try {
|
|
46
|
+
db = new Database(dbPath);
|
|
47
|
+
} catch {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
const tableRow = db
|
|
53
|
+
.query(
|
|
54
|
+
`SELECT name FROM sqlite_master WHERE type='table' AND name='memory_jobs'`,
|
|
55
|
+
)
|
|
56
|
+
.get();
|
|
57
|
+
if (!tableRow) return;
|
|
58
|
+
|
|
59
|
+
// If either gate fails, cancel any pending reembed job that 075 may
|
|
60
|
+
// have enqueued in this same startup sweep. Leave 'running' jobs
|
|
61
|
+
// alone — the worker is already mid-flight and canceling would orphan
|
|
62
|
+
// its state. Only 'pending' jobs are safe to delete here.
|
|
63
|
+
if (
|
|
64
|
+
isMemoryV2Disabled(workspaceDir) ||
|
|
65
|
+
!hasConceptPages(workspaceDir)
|
|
66
|
+
) {
|
|
67
|
+
db.query(
|
|
68
|
+
`DELETE FROM memory_jobs WHERE type='memory_v2_reembed' AND status='pending'`,
|
|
69
|
+
).run();
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const existing = db
|
|
74
|
+
.query(
|
|
75
|
+
`SELECT id FROM memory_jobs WHERE type='memory_v2_reembed' AND status IN ('pending','running') LIMIT 1`,
|
|
76
|
+
)
|
|
77
|
+
.get();
|
|
78
|
+
if (existing) return;
|
|
79
|
+
|
|
80
|
+
const now = Date.now();
|
|
81
|
+
db.query(
|
|
82
|
+
`INSERT INTO memory_jobs
|
|
83
|
+
(id, type, payload, status, attempts, deferrals, run_after, last_error, created_at, updated_at)
|
|
84
|
+
VALUES (?, 'memory_v2_reembed', '{}', 'pending', 0, 0, ?, NULL, ?, ?)`,
|
|
85
|
+
).run(randomUUID(), now, now, now);
|
|
86
|
+
} finally {
|
|
87
|
+
db.close();
|
|
88
|
+
}
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
down(_workspaceDir: string): void {
|
|
92
|
+
// Forward-only: the reembed is a one-shot data refresh.
|
|
93
|
+
},
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Returns true only when `memory.v2.enabled` is explicitly set to `false`
|
|
98
|
+
* in the workspace `config.json`. Missing/unparseable config falls through
|
|
99
|
+
* to the schema default (enabled), matching `MemoryV2ConfigSchema`.
|
|
100
|
+
*/
|
|
101
|
+
function isMemoryV2Disabled(workspaceDir: string): boolean {
|
|
102
|
+
const configPath = join(workspaceDir, "config.json");
|
|
103
|
+
if (!existsSync(configPath)) return false;
|
|
104
|
+
try {
|
|
105
|
+
const raw = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
106
|
+
const memory = (raw as { memory?: { v2?: { enabled?: unknown } } })?.memory;
|
|
107
|
+
return memory?.v2?.enabled === false;
|
|
108
|
+
} catch {
|
|
109
|
+
return false;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Returns true when `memory/concepts/` contains any `.md` file. Walks the
|
|
115
|
+
* tree iteratively so we bail on the first hit — pages can be nested in
|
|
116
|
+
* subdirectories (e.g. `memory/concepts/people/alice.md`).
|
|
117
|
+
*/
|
|
118
|
+
function hasConceptPages(workspaceDir: string): boolean {
|
|
119
|
+
const stack = [join(workspaceDir, "memory", "concepts")];
|
|
120
|
+
while (stack.length > 0) {
|
|
121
|
+
const dir = stack.pop()!;
|
|
122
|
+
let entries;
|
|
123
|
+
try {
|
|
124
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
125
|
+
} catch {
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
for (const entry of entries) {
|
|
129
|
+
if (entry.isDirectory()) {
|
|
130
|
+
stack.push(join(dir, entry.name));
|
|
131
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
132
|
+
return true;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
@@ -0,0 +1,198 @@
|
|
|
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
|
+
* Revert mis-rewrites by migration 057 where a non-Gemini fragment was
|
|
8
|
+
* incorrectly treated as Gemini.
|
|
9
|
+
*
|
|
10
|
+
* Migration 057's `inferProvider` helper hardcodes `model === "gemini-3-flash"`
|
|
11
|
+
* as Gemini even when no `provider` is set, but `resolveCallSiteConfig` only
|
|
12
|
+
* infers providers via the catalog and `gemini-3-flash` is not a catalog
|
|
13
|
+
* entry. The runtime resolver therefore leaves such fragments inheriting from
|
|
14
|
+
* lower layers, so a workspace like `llm.default = {provider: "ollama"}` with
|
|
15
|
+
* `llm.callSites.recall = {model: "gemini-3-flash"}` (a user-named local
|
|
16
|
+
* model) resolves as Ollama at runtime — but 057 rewrites that recall model
|
|
17
|
+
* to `gemini-3-flash-preview`, flipping the catalog-based provider inference
|
|
18
|
+
* to Gemini and corrupting the user's intent.
|
|
19
|
+
*
|
|
20
|
+
* For each call-site or profile fragment that currently looks like a 057
|
|
21
|
+
* rewrite output (model in the replacement set, no explicit `provider`), this
|
|
22
|
+
* migration recomputes the effective provider via proper catalog-based
|
|
23
|
+
* inference using only the other layers. If that effective provider is
|
|
24
|
+
* explicitly non-Gemini, the fragment's model is reverted to
|
|
25
|
+
* `gemini-3-flash`, restoring the user's pre-057 state.
|
|
26
|
+
*/
|
|
27
|
+
export const revertStaleGeminiMisRewritesMigration: WorkspaceMigration = {
|
|
28
|
+
id: "086-revert-stale-gemini-mis-rewrites",
|
|
29
|
+
description:
|
|
30
|
+
"Revert 057 mis-rewrites of gemini-3-flash in non-Gemini fragment contexts",
|
|
31
|
+
run(workspaceDir: string): void {
|
|
32
|
+
const configPath = join(workspaceDir, "config.json");
|
|
33
|
+
if (!existsSync(configPath)) return;
|
|
34
|
+
|
|
35
|
+
let config: Record<string, unknown>;
|
|
36
|
+
try {
|
|
37
|
+
const raw = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
38
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
|
|
39
|
+
config = raw as Record<string, unknown>;
|
|
40
|
+
} catch {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const llm = readObject(config.llm);
|
|
45
|
+
if (llm === null) return;
|
|
46
|
+
|
|
47
|
+
const defaultBlock = readObject(llm.default);
|
|
48
|
+
const defaultProvider = inferLayerProvider(defaultBlock);
|
|
49
|
+
const profiles = readObject(llm.profiles);
|
|
50
|
+
const activeProfileName =
|
|
51
|
+
typeof llm.activeProfile === "string" ? llm.activeProfile : undefined;
|
|
52
|
+
|
|
53
|
+
let changed = false;
|
|
54
|
+
|
|
55
|
+
// Pass 1: revert profile candidates first so that pass 2's call-site
|
|
56
|
+
// evaluation sees the reverted profile state. A call-site that references
|
|
57
|
+
// a profile candidate would otherwise infer `siteProfileProvider = gemini`
|
|
58
|
+
// from the profile's pre-revert rewritten model, masking the call-site's
|
|
59
|
+
// own need to revert.
|
|
60
|
+
if (profiles !== null) {
|
|
61
|
+
for (const rawProfile of Object.values(profiles)) {
|
|
62
|
+
const profile = readObject(rawProfile);
|
|
63
|
+
if (profile === null) continue;
|
|
64
|
+
if (!isRevertCandidate(profile)) continue;
|
|
65
|
+
if (isExplicitlyNonGemini(defaultProvider)) {
|
|
66
|
+
profile.model = STALE_MODEL;
|
|
67
|
+
changed = true;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Compute the active-profile provider after pass 1 so it reflects any
|
|
73
|
+
// reversion of the active profile itself.
|
|
74
|
+
const activeProfileBlock =
|
|
75
|
+
profiles !== null && activeProfileName !== undefined
|
|
76
|
+
? readObject(profiles[activeProfileName])
|
|
77
|
+
: null;
|
|
78
|
+
const activeProfileProvider = inferLayerProvider(activeProfileBlock);
|
|
79
|
+
|
|
80
|
+
// Pass 2: call-site candidates. `inferLayerProvider` is re-read from
|
|
81
|
+
// `profiles` per call site, so it observes pass 1's reversions.
|
|
82
|
+
const callSites = readObject(llm.callSites);
|
|
83
|
+
if (callSites !== null) {
|
|
84
|
+
for (const [site, rawConfig] of Object.entries(callSites)) {
|
|
85
|
+
const callSiteConfig = readObject(rawConfig);
|
|
86
|
+
if (callSiteConfig === null) continue;
|
|
87
|
+
if (!isRevertCandidate(callSiteConfig)) continue;
|
|
88
|
+
const siteProfileName =
|
|
89
|
+
typeof callSiteConfig.profile === "string"
|
|
90
|
+
? callSiteConfig.profile
|
|
91
|
+
: undefined;
|
|
92
|
+
const siteProfileBlock =
|
|
93
|
+
profiles !== null && siteProfileName !== undefined
|
|
94
|
+
? readObject(profiles[siteProfileName])
|
|
95
|
+
: null;
|
|
96
|
+
const siteProfileProvider = inferLayerProvider(siteProfileBlock);
|
|
97
|
+
|
|
98
|
+
const effective = effectiveProviderExcludingSite({
|
|
99
|
+
callSite: site,
|
|
100
|
+
siteProfileProvider,
|
|
101
|
+
activeProfileProvider,
|
|
102
|
+
defaultProvider,
|
|
103
|
+
});
|
|
104
|
+
if (isExplicitlyNonGemini(effective)) {
|
|
105
|
+
callSiteConfig.model = STALE_MODEL;
|
|
106
|
+
changed = true;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (!changed) return;
|
|
112
|
+
|
|
113
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
114
|
+
},
|
|
115
|
+
down(_workspaceDir: string): void {
|
|
116
|
+
// Forward-only: re-applying the broken rewrite would reintroduce the bug.
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// ---------------------------------------------------------------------------
|
|
121
|
+
// Helpers — self-contained per workspace migrations AGENTS.md
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
|
|
124
|
+
const STALE_MODEL = "gemini-3-flash";
|
|
125
|
+
|
|
126
|
+
// Models 057 writes when rewriting. Any fragment now holding one of these
|
|
127
|
+
// values without an explicit provider is a potential mis-rewrite candidate.
|
|
128
|
+
const REPLACEMENT_MODELS = new Set<string>([
|
|
129
|
+
"gemini-3-flash-preview",
|
|
130
|
+
"gemini-3.1-flash-lite-preview",
|
|
131
|
+
]);
|
|
132
|
+
|
|
133
|
+
// Subset of the Gemini provider catalog used for per-layer catalog inference.
|
|
134
|
+
// Mirrors `getCatalogProviderForModel` for Gemini entries only — for other
|
|
135
|
+
// providers we treat a bare model as "provider unknown" and fall through to
|
|
136
|
+
// lower layers, which is safe because the revert only fires when at least one
|
|
137
|
+
// layer below carries an explicit non-Gemini provider.
|
|
138
|
+
const GEMINI_CATALOG_MODELS = new Set<string>([
|
|
139
|
+
"gemini-3.1-pro-preview",
|
|
140
|
+
"gemini-3.1-pro-preview-customtools",
|
|
141
|
+
"gemini-3-flash-preview",
|
|
142
|
+
"gemini-3.1-flash-lite-preview",
|
|
143
|
+
"gemini-2.5-flash",
|
|
144
|
+
"gemini-2.5-flash-lite",
|
|
145
|
+
"gemini-2.5-pro",
|
|
146
|
+
]);
|
|
147
|
+
|
|
148
|
+
function isRevertCandidate(block: Record<string, unknown>): boolean {
|
|
149
|
+
if (typeof block.provider === "string") return false;
|
|
150
|
+
return typeof block.model === "string" && REPLACEMENT_MODELS.has(block.model);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function inferLayerProvider(
|
|
154
|
+
block: Record<string, unknown> | null,
|
|
155
|
+
): string | undefined {
|
|
156
|
+
if (block === null) return undefined;
|
|
157
|
+
if (typeof block.provider === "string") return block.provider;
|
|
158
|
+
if (
|
|
159
|
+
typeof block.model === "string" &&
|
|
160
|
+
GEMINI_CATALOG_MODELS.has(block.model)
|
|
161
|
+
) {
|
|
162
|
+
return "gemini";
|
|
163
|
+
}
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function isExplicitlyNonGemini(provider: string | undefined): boolean {
|
|
168
|
+
return provider !== undefined && provider !== "gemini";
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Mirrors `resolveCallSiteConfig`'s layered provider resolution, omitting the
|
|
172
|
+
// candidate call-site fragment itself (its provider is undefined and its
|
|
173
|
+
// model is the replacement value, so including it would always say "gemini"
|
|
174
|
+
// via catalog inference — defeating the revert check).
|
|
175
|
+
function effectiveProviderExcludingSite(args: {
|
|
176
|
+
callSite: string;
|
|
177
|
+
siteProfileProvider: string | undefined;
|
|
178
|
+
activeProfileProvider: string | undefined;
|
|
179
|
+
defaultProvider: string | undefined;
|
|
180
|
+
}): string | undefined {
|
|
181
|
+
const {
|
|
182
|
+
callSite,
|
|
183
|
+
siteProfileProvider,
|
|
184
|
+
activeProfileProvider,
|
|
185
|
+
defaultProvider,
|
|
186
|
+
} = args;
|
|
187
|
+
if (callSite === "mainAgent") {
|
|
188
|
+
return activeProfileProvider ?? siteProfileProvider ?? defaultProvider;
|
|
189
|
+
}
|
|
190
|
+
return siteProfileProvider ?? activeProfileProvider ?? defaultProvider;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function readObject(value: unknown): Record<string, unknown> | null {
|
|
194
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
195
|
+
return null;
|
|
196
|
+
}
|
|
197
|
+
return value as Record<string, unknown>;
|
|
198
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
|
|
4
|
+
import type { WorkspaceMigration } from "./types.js";
|
|
5
|
+
|
|
6
|
+
// Upgrade callSites.memoryRouter from the 077-seeded
|
|
7
|
+
// {model: "claude-sonnet-4-6", contextWindow: {maxInputTokens: 1_000_000}}
|
|
8
|
+
// shape to {profile: "balanced"} so the router rides the workspace's active
|
|
9
|
+
// inference profile (with thinking enabled, higher effort, etc.) instead of a
|
|
10
|
+
// bare model pin.
|
|
11
|
+
//
|
|
12
|
+
// Two skip conditions guard against runtime regressions:
|
|
13
|
+
//
|
|
14
|
+
// 1. BYOK / non-Anthropic workspaces. `balanced` resolves to the managed
|
|
15
|
+
// Anthropic connection (see seedInferenceProfiles), which off-platform
|
|
16
|
+
// installs explicitly disable. Forcing `balanced` there would make
|
|
17
|
+
// getConfiguredProvider("memoryRouter") return null and silently
|
|
18
|
+
// disable memory injection. Detect this by inspecting llm.default.provider
|
|
19
|
+
// — same heuristic migration 077 used to gate its seed.
|
|
20
|
+
//
|
|
21
|
+
// 2. User-customized memoryRouter config. If the existing entry isn't the
|
|
22
|
+
// exact 077-seeded shape (and isn't already {profile: "balanced"}), the
|
|
23
|
+
// user — or a platform overlay — chose those values deliberately. Match
|
|
24
|
+
// 077's pattern of preserving any prior config.
|
|
25
|
+
export const memoryRouterBalancedProfileMigration: WorkspaceMigration = {
|
|
26
|
+
id: "087-memory-router-balanced-profile",
|
|
27
|
+
description:
|
|
28
|
+
"Set callSites.memoryRouter to { profile: 'balanced' }, dropping the seeded model and contextWindow override",
|
|
29
|
+
run(workspaceDir: string): void {
|
|
30
|
+
if (process.env.VELLUM_DEFAULT_WORKSPACE_CONFIG_PATH) return;
|
|
31
|
+
|
|
32
|
+
const configPath = join(workspaceDir, "config.json");
|
|
33
|
+
const configExisted = existsSync(configPath);
|
|
34
|
+
|
|
35
|
+
let config: Record<string, unknown> = {};
|
|
36
|
+
if (configExisted) {
|
|
37
|
+
try {
|
|
38
|
+
const raw = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
39
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return;
|
|
40
|
+
config = raw as Record<string, unknown>;
|
|
41
|
+
} catch {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const llm = readObject(config.llm) ?? {};
|
|
47
|
+
|
|
48
|
+
const explicitProvider = readString(readObject(llm.default)?.provider);
|
|
49
|
+
if (explicitProvider !== undefined && explicitProvider !== "anthropic") {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const callSites = readObject(llm.callSites) ?? {};
|
|
54
|
+
const existing = readObject(callSites.memoryRouter);
|
|
55
|
+
|
|
56
|
+
if (existing !== null && !isSeededBy077(existing)) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
callSites.memoryRouter = { profile: "balanced" };
|
|
61
|
+
llm.callSites = callSites;
|
|
62
|
+
config.llm = llm;
|
|
63
|
+
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
64
|
+
},
|
|
65
|
+
down(_workspaceDir: string): void {
|
|
66
|
+
// Forward-only.
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// True when the entry looks exactly like what migration 077 wrote: a model pin
|
|
71
|
+
// of claude-sonnet-4-6 plus the 1M-token context window, and nothing else.
|
|
72
|
+
function isSeededBy077(entry: Record<string, unknown>): boolean {
|
|
73
|
+
const keys = Object.keys(entry);
|
|
74
|
+
if (keys.length !== 2) return false;
|
|
75
|
+
if (entry.model !== "claude-sonnet-4-6") return false;
|
|
76
|
+
const contextWindow = readObject(entry.contextWindow);
|
|
77
|
+
if (contextWindow === null) return false;
|
|
78
|
+
const cwKeys = Object.keys(contextWindow);
|
|
79
|
+
return cwKeys.length === 1 && contextWindow.maxInputTokens === 1_000_000;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function readObject(value: unknown): Record<string, unknown> | null {
|
|
83
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
return value as Record<string, unknown>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function readString(value: unknown): string | undefined {
|
|
90
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
91
|
+
}
|