@vellumai/assistant 0.8.5 → 0.8.6
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/AGENTS.md +33 -1
- package/ARCHITECTURE.md +1 -1
- package/bunfig.toml +6 -1
- package/docs/credential-execution-service.md +6 -6
- package/docs/plugins.md +4 -3
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +12 -13
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +4 -1
- package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +16 -14
- package/openapi.yaml +1900 -166
- package/package.json +1 -1
- package/src/__tests__/actor-token-service.test.ts +3 -2
- package/src/__tests__/agent-loop-exit-reason.test.ts +102 -9
- package/src/__tests__/agent-loop-override-profile.test.ts +2 -1
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +1 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +1 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
- package/src/__tests__/annotate-risk-options.test.ts +1 -0
- package/src/__tests__/approval-cascade.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +9 -13
- package/src/__tests__/assert-not-live-db.ts +79 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +9 -25
- package/src/__tests__/audit-log-rotation.test.ts +2 -2
- package/src/__tests__/auto-analysis-end-to-end.test.ts +6 -6
- package/src/__tests__/background-workers-disk-pressure.test.ts +5 -8
- package/src/__tests__/browser-skill-endstate.test.ts +3 -3
- package/src/__tests__/btw-routes.test.ts +3 -2
- package/src/__tests__/call-controller.test.ts +3 -2
- package/src/__tests__/channel-approval-routes.test.ts +3 -2
- package/src/__tests__/channel-guardian.test.ts +3 -2
- package/src/__tests__/channel-readiness-slack-remote.test.ts +175 -0
- package/src/__tests__/channel-reply-delivery.test.ts +35 -0
- package/src/__tests__/channel-retry-sweep.test.ts +320 -3
- package/src/__tests__/checker.test.ts +12 -12
- package/src/__tests__/compaction-events.test.ts +1 -0
- package/src/__tests__/compaction-trail-store.test.ts +264 -0
- package/src/__tests__/compactor-call-site-logging.test.ts +1 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +1 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +7 -5
- package/src/__tests__/computer-use-tools.test.ts +12 -14
- package/src/__tests__/config-loader-backfill.test.ts +13 -28
- package/src/__tests__/config-loader-corrupt.test.ts +5 -5
- package/src/__tests__/config-loader-platform-defaults.test.ts +93 -26
- package/src/__tests__/config-loader-quarantine-bulletin.test.ts +3 -3
- package/src/__tests__/config-managed-gemini-defaults.test.ts +3 -4
- package/src/__tests__/config-schema.test.ts +10 -10
- package/src/__tests__/connection-model-compat.test.ts +83 -0
- package/src/__tests__/contacts-tools.test.ts +3 -2
- package/src/__tests__/context-token-estimator.test.ts +22 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +5 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-handlers-max-tokens.test.ts +55 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +34 -0
- package/src/__tests__/conversation-agent-loop.test.ts +488 -2
- package/src/__tests__/conversation-analysis-routes.test.ts +1 -0
- package/src/__tests__/conversation-app-control-instantiation.test.ts +29 -19
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +1 -0
- package/src/__tests__/conversation-attention-store.test.ts +101 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +3 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
- package/src/__tests__/conversation-error.test.ts +30 -0
- package/src/__tests__/conversation-fork-crud.test.ts +69 -8
- package/src/__tests__/conversation-fork-route.test.ts +3 -2
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-inference-profile-list.test.ts +3 -2
- package/src/__tests__/conversation-inference-profile-route.test.ts +3 -2
- package/src/__tests__/conversation-lifecycle.test.ts +1 -0
- package/src/__tests__/conversation-list-source.test.ts +3 -2
- package/src/__tests__/conversation-load-history-repair.test.ts +2 -1
- package/src/__tests__/conversation-load-history-stripped.test.ts +1 -0
- package/src/__tests__/conversation-pairing.test.ts +53 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +26 -7
- package/src/__tests__/conversation-process-callsite.test.ts +1 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +5 -0
- package/src/__tests__/conversation-queue.test.ts +333 -291
- package/src/__tests__/conversation-routes-disk-view.test.ts +3 -18
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +33 -8
- package/src/__tests__/conversation-routes-slash-commands.test.ts +33 -2
- package/src/__tests__/conversation-runtime-assembly.test.ts +78 -0
- package/src/__tests__/conversation-skill-tools.test.ts +38 -142
- package/src/__tests__/conversation-slash-queue.test.ts +84 -32
- package/src/__tests__/conversation-slash-unknown.test.ts +5 -0
- package/src/__tests__/conversation-speed-override.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +46 -0
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +6 -3
- package/src/__tests__/conversation-surfaces-standalone.test.ts +6 -3
- package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -3
- package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -17
- package/src/__tests__/conversation-sync-tags.test.ts +128 -12
- package/src/__tests__/conversation-title-service.test.ts +1 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +30 -0
- package/src/__tests__/conversation-usage.test.ts +1 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +5 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +5 -0
- package/src/__tests__/credential-broker-browser-fill.test.ts +3 -3
- package/src/__tests__/credential-broker-server-use.test.ts +5 -5
- package/src/__tests__/credential-execution-client.test.ts +72 -1
- package/src/__tests__/credential-execution-feature-gates.test.ts +10 -12
- package/src/__tests__/credential-health-service.test.ts +252 -3
- package/src/__tests__/credential-security-invariants.test.ts +5 -5
- package/src/__tests__/credential-vault-unit.test.ts +19 -19
- package/src/__tests__/credential-vault.test.ts +5 -5
- package/src/__tests__/cross-provider-web-search.test.ts +56 -2
- package/src/__tests__/db-connection-isolation.test.ts +7 -6
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +8 -10
- package/src/__tests__/db-conversation-inference-profile-migration.test.ts +7 -10
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +9 -15
- package/src/__tests__/db-test-helpers.ts +58 -0
- package/src/__tests__/disk-pressure-guard.test.ts +58 -41
- package/src/__tests__/disk-pressure-lifecycle.test.ts +13 -10
- package/src/__tests__/disk-pressure-routes.test.ts +0 -33
- package/src/__tests__/disk-pressure-tools.test.ts +0 -4
- package/src/__tests__/dm-persistence.test.ts +26 -40
- package/src/__tests__/document-create-dedupe.test.ts +189 -0
- package/src/__tests__/document-find-replace.test.ts +3 -2
- package/src/__tests__/document-tool-security.test.ts +81 -2
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +5 -4
- package/src/__tests__/encrypted-store-test-helpers.ts +56 -0
- package/src/__tests__/encrypted-store.test.ts +11 -9
- package/src/__tests__/feature-flag-test-helpers.ts +53 -0
- package/src/__tests__/filing-service.test.ts +1 -0
- package/src/__tests__/first-greeting.test.ts +62 -12
- package/src/__tests__/gateway-flag-listener.test.ts +0 -1
- package/src/__tests__/gemini-provider.test.ts +26 -0
- package/src/__tests__/guardian-action-sweep.test.ts +3 -2
- package/src/__tests__/guardian-outbound-http.test.ts +3 -2
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +48 -3
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -0
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +1 -0
- package/src/__tests__/helpers/mock-logger.ts +26 -0
- package/src/__tests__/host-bash-routes.test.ts +1 -0
- package/src/__tests__/host-cu-routes-targeted.test.ts +1 -0
- package/src/__tests__/host-file-routes-targeted.test.ts +1 -0
- package/src/__tests__/host-shell-tool.test.ts +5 -4
- package/src/__tests__/host-transfer-routes-targeted.test.ts +1 -0
- package/src/__tests__/http-conversation-lineage.test.ts +3 -2
- package/src/__tests__/http-user-message-parity.test.ts +29 -7
- package/src/__tests__/identity-intro-cache.test.ts +133 -22
- package/src/__tests__/inbound-slack-persistence.test.ts +44 -72
- package/src/__tests__/inference-profile-reaper.test.ts +3 -2
- package/src/__tests__/inference-profile-session-ipc.test.ts +3 -2
- package/src/__tests__/injector-disk-pressure.test.ts +3 -17
- package/src/__tests__/inline-skill-load-permissions.test.ts +4 -4
- package/src/__tests__/list-messages-hidden-metadata.test.ts +80 -0
- package/src/__tests__/llm-context-normalization.test.ts +42 -0
- package/src/__tests__/llm-resolver.test.ts +331 -0
- package/src/__tests__/llm-schema.test.ts +1 -1
- package/src/__tests__/manual-token-reconciliation.test.ts +76 -1
- package/src/__tests__/mcp-abort-signal.test.ts +14 -0
- package/src/__tests__/mcp-client-auth.test.ts +14 -0
- package/src/__tests__/messaging-send-tool.test.ts +1 -0
- package/src/__tests__/migration-import-from-url.test.ts +3 -3
- package/src/__tests__/mock-gateway-ipc.ts +18 -2
- package/src/__tests__/model-intents.test.ts +3 -3
- package/src/__tests__/native-web-search.test.ts +30 -2
- package/src/__tests__/notification-deep-link.test.ts +62 -0
- package/src/__tests__/oauth-commands-routes.test.ts +37 -0
- package/src/__tests__/oauth-provider-visibility.test.ts +8 -8
- package/src/__tests__/oauth-store.test.ts +3 -2
- package/src/__tests__/onboarding-template-contract.test.ts +3 -2
- package/src/__tests__/openai-provider.test.ts +8 -9
- package/src/__tests__/openai-responses-provider.test.ts +70 -10
- package/src/__tests__/openrouter-provider-only.test.ts +27 -5
- package/src/__tests__/outbound-slack-persistence.test.ts +46 -1
- package/src/__tests__/persistence-pipeline.test.ts +139 -1
- package/src/__tests__/persistence-secret-redaction.test.ts +83 -12
- package/src/__tests__/plugin-bootstrap.test.ts +9 -11
- package/src/__tests__/plugin-tool-contribution.test.ts +41 -38
- package/src/__tests__/process-message-background-slack.test.ts +21 -16
- package/src/__tests__/process-message-display-content.test.ts +19 -22
- package/src/__tests__/provider-catalog-visibility.test.ts +9 -9
- package/src/__tests__/provider-platform-proxy-integration.test.ts +216 -4
- package/src/__tests__/provider-registry-ollama.test.ts +45 -22
- package/src/__tests__/recording-handler.test.ts +1 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +1 -0
- package/src/__tests__/registry.test.ts +82 -76
- package/src/__tests__/relay-server.test.ts +10 -10
- package/src/__tests__/runtime-attachment-metadata.test.ts +3 -2
- package/src/__tests__/schedule-store.test.ts +16 -1
- package/src/__tests__/scheduler-reuse-conversation.test.ts +48 -3
- package/src/__tests__/secret-ingress-http.test.ts +5 -1
- package/src/__tests__/secure-keys.test.ts +3 -3
- package/src/__tests__/send-endpoint-busy.test.ts +81 -42
- package/src/__tests__/server-history-render.test.ts +4 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +8 -10
- package/src/__tests__/skill-feature-flags.test.ts +14 -16
- package/src/__tests__/skill-load-feature-flag.test.ts +5 -5
- package/src/__tests__/skill-projection-feature-flag.test.ts +44 -30
- package/src/__tests__/skill-projection.benchmark.test.ts +5 -7
- package/src/__tests__/skill-tool-factory.test.ts +96 -95
- package/src/__tests__/slack-channel-config.test.ts +3 -3
- package/src/__tests__/subagent-call-site-routing.test.ts +11 -3
- package/src/__tests__/subagent-disposal.test.ts +27 -8
- package/src/__tests__/subagent-fork-notifications.test.ts +24 -9
- package/src/__tests__/subagent-fork-spawn.test.ts +13 -4
- package/src/__tests__/subagent-manager-notify.test.ts +20 -8
- package/src/__tests__/subagent-notify-parent.test.ts +5 -4
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +58 -0
- package/src/__tests__/subagent-tools.test.ts +2 -1
- package/src/__tests__/suggestion-routes.test.ts +1 -0
- package/src/__tests__/system-prompt.test.ts +38 -0
- package/src/__tests__/test-preload-verifier.ts +68 -0
- package/src/__tests__/test-preload.ts +32 -39
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +20 -7
- package/src/__tests__/tool-executor.test.ts +55 -10
- package/src/__tests__/tool-preview-lifecycle.test.ts +1 -0
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
- package/src/__tests__/twilio-routes.test.ts +3 -2
- package/src/__tests__/validate-input.test.ts +381 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -1
- package/src/__tests__/voice-session-bridge.test.ts +37 -28
- package/src/__tests__/workspace-migration-090-memory-router-cost-optimized-profile.test.ts +326 -0
- package/src/__tests__/workspace-migration-091-retighten-migration-onboarding-thread.test.ts +166 -0
- package/src/acp/session-manager.ts +5 -6
- package/src/agent/loop.ts +80 -0
- package/src/api/README.md +124 -2
- package/src/api/constants/call-sites.ts +27 -0
- package/src/api/events/assistant-outbound-attachment.ts +51 -0
- package/src/api/events/assistant-text-delta.ts +32 -0
- package/src/api/events/assistant-turn-start.ts +33 -0
- package/src/api/events/document-comment-created.ts +48 -0
- package/src/api/events/document-comment-deleted.ts +24 -0
- package/src/api/events/document-comment-reopened.ts +25 -0
- package/src/api/events/document-comment-resolved.ts +27 -0
- package/src/api/events/generation-cancelled.ts +24 -0
- package/src/api/events/generation-handoff.ts +41 -0
- package/src/api/events/message-complete.ts +42 -0
- package/src/api/events/open-url.ts +30 -0
- package/src/{events → api/events}/relationship-state-updated.ts +3 -3
- package/src/api/events/tool-use-start.ts +32 -0
- package/src/api/index.ts +128 -3
- package/src/api/responses/llm-context-response.ts +39 -0
- package/src/api/responses/llm-request-log-entry.ts +93 -0
- package/src/api/responses/memory-recall-log.ts +65 -0
- package/src/api/responses/memory-v2-activation-log.ts +78 -0
- package/src/background-wake/background-wake-routes.test.ts +687 -52
- package/src/background-wake/platform-client.test.ts +308 -0
- package/src/background-wake/platform-client.ts +167 -0
- package/src/background-wake/publisher.ts +91 -0
- package/src/background-wake/runtime-registry.ts +2 -2
- package/src/background-wake/wake-intent-hooks.test.ts +282 -0
- package/src/calls/guardian-dispatch.ts +1 -0
- package/src/calls/voice-session-bridge.ts +4 -4
- package/src/cli/commands/__tests__/conversations-slack.test.ts +16 -0
- package/src/cli/commands/__tests__/notifications.test.ts +184 -40
- package/src/cli/commands/channels/__tests__/channels.test.ts +143 -0
- package/src/cli/commands/channels/index.ts +229 -0
- package/src/cli/commands/memory-v3-render.ts +147 -0
- package/src/cli/commands/memory-v3.ts +255 -4
- package/src/cli/commands/notifications.ts +365 -55
- package/src/cli/lib/open-browser.ts +7 -2
- package/src/cli/program.ts +2 -0
- package/src/config/assistant-feature-flags.ts +23 -42
- package/src/config/bundled-skills/document-editor/SKILL.md +5 -1
- package/src/config/bundled-skills/schedule/SKILL.md +1 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -0
- package/src/config/call-site-defaults.ts +1 -1
- package/src/config/feature-flag-cache.ts +86 -0
- package/src/config/feature-flag-registry.json +17 -17
- package/src/config/llm-context-resolution.ts +10 -1
- package/src/config/llm-resolver.ts +121 -15
- package/src/config/loader.ts +4 -5
- package/src/config/schemas/__tests__/memory-v2.test.ts +15 -0
- package/src/config/schemas/heartbeat.ts +1 -1
- package/src/config/schemas/llm.ts +90 -1
- package/src/config/schemas/memory-v2.ts +26 -0
- package/src/config/schemas/services.ts +6 -2
- package/src/config/seed-inference-profiles.ts +36 -16
- package/src/context/token-estimator.ts +10 -5
- package/src/credential-execution/executable-discovery.ts +40 -0
- package/src/credential-execution/process-manager.ts +6 -2
- package/src/credential-health/credential-health-service.ts +125 -40
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -6
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +13 -15
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -2
- package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -0
- package/src/daemon/__tests__/meet-manifest-loader.test.ts +25 -12
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +1 -0
- package/src/daemon/__tests__/switch-inference-profile-tool.test.ts +107 -0
- package/src/daemon/__tests__/web-search-status-text.test.ts +1 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +389 -68
- package/src/daemon/conversation-agent-loop.ts +132 -28
- package/src/daemon/conversation-error.ts +33 -5
- package/src/daemon/conversation-messaging.ts +84 -43
- package/src/daemon/conversation-process.ts +74 -37
- package/src/daemon/conversation-runtime-assembly.ts +29 -9
- package/src/daemon/conversation-skill-tools.ts +14 -30
- package/src/daemon/conversation-surfaces.ts +69 -34
- package/src/daemon/conversation-tool-setup.ts +33 -48
- package/src/daemon/conversation.ts +26 -46
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/daemon-skill-host.ts +9 -2
- package/src/daemon/disk-pressure-guard.ts +27 -29
- package/src/daemon/first-greeting.ts +31 -13
- package/src/daemon/handlers/shared.ts +6 -1
- package/src/daemon/lifecycle.ts +12 -12
- package/src/daemon/mcp-reload-service.ts +1 -1
- package/src/daemon/meet-manifest-loader.ts +10 -17
- package/src/daemon/message-types/conversations.ts +20 -22
- package/src/daemon/message-types/document-comments.ts +8 -44
- package/src/daemon/message-types/home.ts +2 -2
- package/src/daemon/message-types/integrations.ts +2 -7
- package/src/daemon/message-types/messages.ts +23 -38
- package/src/daemon/message-types/subagents.ts +6 -0
- package/src/daemon/process-message.ts +9 -9
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/server.ts +16 -0
- package/src/daemon/switch-inference-profile-tool.ts +13 -3
- package/src/daemon/tool-setup-types.ts +0 -6
- package/src/daemon/wake-target-adapter.ts +10 -0
- package/src/documents/document-store.ts +38 -0
- package/src/export/__tests__/transcript-formatter.test.ts +1 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +29 -0
- package/src/heartbeat/heartbeat-service.ts +63 -0
- package/src/home/__tests__/feed-writer.test.ts +161 -0
- package/src/home/__tests__/post-connect-feed.test.ts +1 -0
- package/src/home/__tests__/suggested-prompts.test.ts +55 -59
- package/src/home/feed-writer.ts +146 -7
- package/src/home/suggested-prompts.ts +27 -145
- package/src/ipc/__tests__/cli-ipc.test.ts +1 -0
- package/src/ipc/gateway-client.test.ts +4 -1
- package/src/ipc/skill-routes/__tests__/memory.test.ts +1 -0
- package/src/ipc/skill-routes/__tests__/registries.test.ts +36 -7
- package/src/ipc/skill-routes/memory.ts +4 -3
- package/src/ipc/skill-routes/registries.ts +28 -29
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +1 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +26 -5
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +1 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +1 -0
- package/src/memory/__tests__/memory-retrospective-startup-cleanup.test.ts +1 -0
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +31 -0
- package/src/memory/conversation-attention-store.ts +17 -3
- package/src/memory/conversation-crud.ts +352 -112
- package/src/memory/db-connection.ts +29 -19
- package/src/memory/db-init.ts +4 -0
- package/src/memory/db-singleton.ts +77 -0
- package/src/memory/delivery-channels.ts +82 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +2 -4
- package/src/memory/graph/retriever.test.ts +3 -3
- package/src/memory/job-handlers/embedding.test.ts +3 -2
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -2
- package/src/memory/jobs-worker.ts +12 -1
- package/src/memory/llm-request-log-source-clickhouse.ts +80 -0
- package/src/memory/llm-request-log-source-local.ts +24 -0
- package/src/memory/llm-request-log-source.ts +31 -0
- package/src/memory/llm-request-log-store.ts +188 -3
- package/src/memory/memory-v2-activation-log-store.ts +95 -1
- package/src/memory/migrations/265-drop-provider-connection-status.ts +26 -0
- package/src/memory/migrations/266-messages-client-message-id.ts +43 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/schema/conversations.ts +9 -1
- package/src/memory/schema/inference.ts +0 -1
- package/src/memory/v2/__tests__/backfill-jobs.test.ts +5 -2
- package/src/memory/v2/__tests__/harness-metrics.test.ts +9 -0
- package/src/memory/v2/__tests__/harness-replay-input.test.ts +9 -4
- package/src/memory/v2/__tests__/harness-runner.test.ts +26 -0
- package/src/memory/v2/__tests__/sweep-job.test.ts +6 -3
- package/src/memory/v2/harness/metrics.ts +5 -1
- package/src/memory/v2/harness/replay-input.ts +19 -3
- package/src/memory/v2/harness/runner.ts +6 -0
- package/src/memory/v2/harness/trace.ts +6 -0
- package/src/memory/v3/__tests__/consolidation-job.test.ts +2 -4
- package/src/memory/v3/__tests__/coretrieval-seed.test.ts +270 -0
- package/src/memory/v3/__tests__/edges.test.ts +144 -1
- package/src/memory/v3/__tests__/filter.test.ts +48 -0
- package/src/memory/v3/__tests__/gate.test.ts +96 -33
- package/src/memory/v3/__tests__/index-composition.test.ts +58 -0
- package/src/memory/v3/__tests__/loop.test.ts +250 -5
- package/src/memory/v3/__tests__/scouts.test.ts +49 -0
- package/src/memory/v3/__tests__/shadow-diff.test.ts +225 -0
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +88 -2
- package/src/memory/v3/__tests__/traversal.test.ts +39 -0
- package/src/memory/v3/__tests__/tree-walk.test.ts +77 -0
- package/src/memory/v3/__tests__/validate.test.ts +32 -0
- package/src/memory/v3/coretrieval-seed.ts +240 -0
- package/src/memory/v3/edges.ts +58 -21
- package/src/memory/v3/filter.ts +27 -22
- package/src/memory/v3/gate.ts +51 -36
- package/src/memory/v3/index-composition.ts +18 -5
- package/src/memory/v3/loop.ts +65 -17
- package/src/memory/v3/scouts.ts +15 -4
- package/src/memory/v3/shadow-diff.ts +287 -0
- package/src/memory/v3/shadow-middleware.ts +44 -2
- package/src/memory/v3/traversal.ts +6 -1
- package/src/memory/v3/tree-walk.ts +6 -1
- package/src/memory/v3/validate.ts +56 -33
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +1 -0
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +1 -0
- package/src/notifications/adapters/slack.ts +45 -11
- package/src/notifications/broadcaster.ts +114 -63
- package/src/notifications/conversation-pairing.ts +23 -3
- package/src/notifications/decisions-store.ts +32 -1
- package/src/notifications/deliveries-store.ts +45 -0
- package/src/notifications/edit-notification.ts +201 -0
- package/src/notifications/emit-signal.ts +11 -1
- package/src/notifications/signal.ts +10 -0
- package/src/notifications/types.ts +37 -0
- package/src/oauth/byo-connection.test.ts +67 -3
- package/src/oauth/byo-connection.ts +32 -5
- package/src/oauth/connect-orchestrator.ts +9 -0
- package/src/oauth/connection-resolver.test.ts +76 -0
- package/src/oauth/connection-resolver.ts +49 -10
- package/src/oauth/manual-token-connection.ts +51 -3
- package/src/oauth/seed-providers.ts +3 -0
- package/src/permissions/approval-policy.test.ts +19 -5
- package/src/permissions/approval-policy.ts +14 -3
- package/src/permissions/checker.ts +21 -8
- package/src/platform/client.test.ts +24 -1
- package/src/platform/client.ts +8 -0
- package/src/platform/feature-gate.ts +15 -0
- package/src/plugins/defaults/injectors.ts +2 -8
- package/src/plugins/defaults/persistence.ts +25 -6
- package/src/plugins/types.ts +57 -13
- package/src/proactive-artifact/job.test.ts +1 -0
- package/src/prompts/__tests__/system-prompt.test.ts +4 -4
- package/src/prompts/system-prompt.ts +38 -40
- package/src/prompts/template-detection.ts +10 -4
- package/src/prompts/templates/BOOTSTRAP.md +7 -11
- package/src/prompts/templates/IDENTITY.md +0 -2
- package/src/providers/__tests__/connection-model-compat.test.ts +3 -4
- package/src/providers/__tests__/registry-native-web-search.test.ts +122 -0
- package/src/providers/call-site-routing.ts +33 -9
- package/src/providers/connection-model-compat.ts +23 -0
- package/src/providers/connection-resolution.ts +39 -20
- package/src/providers/fireworks/client.ts +1 -0
- package/src/providers/gemini/client.ts +24 -3
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +0 -2
- package/src/providers/inference/__tests__/base-url-security.test.ts +2 -3
- package/src/providers/inference/__tests__/{connections-status-label.test.ts → connections-label.test.ts} +12 -111
- package/src/providers/inference/auth.ts +0 -8
- package/src/providers/inference/connections.ts +3 -66
- package/src/providers/inference/resolve-auth.ts +2 -3
- package/src/providers/model-catalog.ts +35 -1
- package/src/providers/model-intents.ts +3 -3
- package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +157 -5
- package/src/providers/openai/chat-completions-provider.ts +110 -12
- package/src/providers/openai/codex-models.ts +2 -0
- package/src/providers/openai/responses-provider.ts +53 -53
- package/src/providers/openrouter/client.ts +13 -8
- package/src/providers/provider-send-message.ts +18 -9
- package/src/providers/registry.ts +48 -8
- package/src/providers/retry.ts +16 -4
- package/src/providers/search-provider-catalog.ts +17 -9
- package/src/providers/types.ts +9 -0
- package/src/runtime/__tests__/agent-wake.test.ts +1 -0
- package/src/runtime/__tests__/background-job-runner.test.ts +1 -0
- package/src/runtime/access-request-helper.ts +1 -0
- package/src/runtime/auth/route-policy.ts +10 -0
- package/src/runtime/channel-readiness-service.ts +68 -0
- package/src/runtime/channel-reply-delivery.ts +23 -0
- package/src/runtime/channel-retry-sweep.ts +47 -14
- package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
- package/src/runtime/migrations/vbundle-builder.ts +3 -2
- package/src/runtime/routes/__tests__/bookmark-routes.test.ts +1 -0
- package/src/runtime/routes/__tests__/conversation-compaction-routes.test.ts +406 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +98 -0
- package/src/runtime/routes/__tests__/heartbeat-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +209 -1
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +13 -50
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +51 -3
- package/src/runtime/routes/__tests__/memory-v3-simulate-params.test.ts +35 -0
- package/src/runtime/routes/__tests__/slack-channel-routes.test.ts +3 -2
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +294 -0
- package/src/runtime/routes/__tests__/task-routes.test.ts +48 -3
- package/src/runtime/routes/acp-routes-list.test.ts +3 -0
- package/src/runtime/routes/app-management-routes.ts +111 -4
- package/src/runtime/routes/background-wake-routes.ts +188 -20
- package/src/runtime/routes/btw-routes.ts +4 -4
- package/src/runtime/routes/conversation-analysis-routes.ts +6 -0
- package/src/runtime/routes/conversation-compaction-routes.ts +263 -0
- package/src/runtime/routes/conversation-list-routes.ts +147 -0
- package/src/runtime/routes/conversation-management-routes.ts +39 -14
- package/src/runtime/routes/conversation-query-routes.ts +60 -10
- package/src/runtime/routes/conversation-routes.ts +186 -140
- package/src/runtime/routes/conversations-import-routes.ts +19 -6
- package/src/runtime/routes/documents-routes.ts +10 -1
- package/src/runtime/routes/group-routes.ts +11 -0
- package/src/runtime/routes/home-feed-routes.ts +129 -0
- package/src/runtime/routes/identity-intro-cache.ts +61 -16
- package/src/runtime/routes/identity-routes.ts +30 -9
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +530 -6
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +57 -8
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/inference-provider-connection-routes.ts +5 -26
- package/src/runtime/routes/integrations/vercel.ts +15 -0
- package/src/runtime/routes/llm-context-normalization.ts +7 -2
- package/src/runtime/routes/memory-v3-routes.ts +160 -2
- package/src/runtime/routes/migration-routes.ts +20 -13
- package/src/runtime/routes/notification-routes.ts +63 -1
- package/src/runtime/routes/oauth-commands-routes.ts +6 -1
- package/src/runtime/routes/surface-action-routes.ts +1 -38
- package/src/runtime/routes/surface-content-routes.ts +12 -5
- package/src/runtime/routes/surface-conversation-resolver.ts +65 -0
- package/src/runtime/routes/wipe-conversation-routes.ts +3 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -0
- package/src/runtime/slack-dm-text-delivery.ts +177 -0
- package/src/runtime/sync/resource-sync-events.ts +1 -1
- package/src/runtime/tool-grant-request-helper.ts +1 -0
- package/src/schedule/schedule-store.ts +8 -1
- package/src/schedule/scheduler.ts +111 -15
- package/src/security/__tests__/provider-key-env-fallback.test.ts +3 -3
- package/src/security/encrypted-store.ts +7 -16
- package/src/security/store-path-override.ts +61 -0
- package/src/signals/user-message.ts +5 -8
- package/src/skills/validate-input.ts +177 -0
- package/src/subagent/manager.ts +13 -13
- package/src/subagent/types.ts +6 -0
- package/src/tasks/tool-sanitizer.ts +2 -2
- package/src/tools/apps/definitions.ts +35 -21
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +2 -8
- package/src/tools/computer-use/definitions.ts +268 -266
- package/src/tools/document/document-tool.ts +131 -8
- package/src/tools/execution-target.ts +2 -5
- package/src/tools/executor.ts +18 -55
- package/src/tools/host-filesystem/edit.test.ts +1 -0
- package/src/tools/host-filesystem/read.test.ts +1 -0
- package/src/tools/host-filesystem/transfer.test.ts +31 -6
- package/src/tools/host-filesystem/write.test.ts +1 -0
- package/src/tools/mcp/mcp-tool-factory.ts +0 -2
- package/src/tools/network/__tests__/managed-search-proxy.test.ts +282 -0
- package/src/tools/network/__tests__/web-search.test.ts +211 -3
- package/src/tools/network/managed-search-proxy.ts +183 -0
- package/src/tools/network/web-search.ts +199 -44
- package/src/tools/policy-context.ts +3 -1
- package/src/tools/registry.ts +146 -103
- package/src/tools/schedule/create.ts +1 -1
- package/src/tools/skills/skill-tool-factory.ts +17 -36
- package/src/tools/subagent/spawn.ts +3 -0
- package/src/tools/tool-approval-handler.ts +10 -4
- package/src/tools/tool-name-aliases.ts +72 -14
- package/src/tools/types.ts +17 -15
- package/src/tools/ui-surface/definitions.ts +98 -86
- package/src/types/onboarding-context.ts +6 -0
- package/src/usage/attribution.ts +32 -1
- package/src/util/browser.ts +7 -2
- package/src/workspace/migrations/090-memory-router-cost-optimized-profile.ts +109 -0
- package/src/workspace/migrations/091-retighten-migration-onboarding-thread.ts +41 -0
- package/src/workspace/migrations/registry.ts +4 -0
|
@@ -21,25 +21,12 @@ import {
|
|
|
21
21
|
} from "../util/platform.js";
|
|
22
22
|
import { stripCommentLines } from "../util/strip-comment-lines.js";
|
|
23
23
|
import { cleanupBootstrapFiles } from "./bootstrap-cleanup.js";
|
|
24
|
-
import {
|
|
25
|
-
resolveGuardianPersona,
|
|
26
|
-
resolveUserSlug,
|
|
27
|
-
} from "./persona-resolver.js";
|
|
24
|
+
import { resolveGuardianPersona, resolveUserSlug } from "./persona-resolver.js";
|
|
28
25
|
import { renderWorkspaceSections } from "./sections.js";
|
|
29
26
|
import { isTemplateContent } from "./template-detection.js";
|
|
30
27
|
|
|
31
28
|
export { isTemplateContent };
|
|
32
29
|
|
|
33
|
-
/**
|
|
34
|
-
* Maps onboarding cohort identifiers to their cohort-specific bootstrap
|
|
35
|
-
* template filenames. When a cohort key is present in OnboardingContext,
|
|
36
|
-
* `maybeReseedBootstrapForCohort` swaps the generic BOOTSTRAP.md with the
|
|
37
|
-
* cohort-specific variant — but only if the workspace file is still pristine.
|
|
38
|
-
*/
|
|
39
|
-
const COHORT_BOOTSTRAP_TEMPLATES: Record<string, string> = {
|
|
40
|
-
"content-automation": "BOOTSTRAP-CONTENT-AUTOMATION.md",
|
|
41
|
-
};
|
|
42
|
-
|
|
43
30
|
const log = getLogger("system-prompt");
|
|
44
31
|
|
|
45
32
|
const PROMPT_FILES = ["IDENTITY.md", "SOUL.md"] as const;
|
|
@@ -215,23 +202,34 @@ export function ensurePromptFiles(): void {
|
|
|
215
202
|
|
|
216
203
|
/**
|
|
217
204
|
* One-shot swap: if the workspace BOOTSTRAP.md is still the unmodified generic
|
|
218
|
-
* template AND a
|
|
219
|
-
* with the
|
|
220
|
-
* or the
|
|
205
|
+
* template AND a matching template file exists in the bundled templates dir,
|
|
206
|
+
* overwrite the workspace file with the specified variant. No-op when
|
|
207
|
+
* BOOTSTRAP.md has been deleted, modified, or the template file is missing.
|
|
221
208
|
*/
|
|
222
|
-
export function
|
|
223
|
-
|
|
224
|
-
|
|
209
|
+
export function maybeReseedBootstrap(templateFileName: string): void {
|
|
210
|
+
// Path traversal guard: reject filenames containing directory separators or
|
|
211
|
+
// parent-directory references, and require a `.md` extension.
|
|
212
|
+
if (
|
|
213
|
+
templateFileName.includes("/") ||
|
|
214
|
+
templateFileName.includes("..") ||
|
|
215
|
+
!templateFileName.endsWith(".md")
|
|
216
|
+
) {
|
|
217
|
+
log.warn(
|
|
218
|
+
{ templateFileName },
|
|
219
|
+
"Rejected bootstrap template filename: invalid characters or extension",
|
|
220
|
+
);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
225
223
|
|
|
226
224
|
const bootstrapPath = getWorkspacePromptPath("BOOTSTRAP.md");
|
|
227
225
|
if (!existsSync(bootstrapPath)) return;
|
|
228
226
|
|
|
229
227
|
const currentContent = readPromptFile(bootstrapPath);
|
|
230
|
-
// Compare against the GENERIC "BOOTSTRAP.md" template, not the
|
|
231
|
-
//
|
|
232
|
-
//
|
|
233
|
-
//
|
|
234
|
-
//
|
|
228
|
+
// Compare against the GENERIC "BOOTSTRAP.md" template, not the specified
|
|
229
|
+
// one. After the swap, the workspace content no longer matches the generic
|
|
230
|
+
// template, so this guard returns false on subsequent calls — making the
|
|
231
|
+
// swap idempotent. Do NOT change the comparison target to the provided
|
|
232
|
+
// template filename; that would re-swap on every prompt build.
|
|
235
233
|
if (!isTemplateContent(currentContent, "BOOTSTRAP.md")) return;
|
|
236
234
|
|
|
237
235
|
const templatesDir = resolveBundledDir(
|
|
@@ -239,26 +237,26 @@ export function maybeReseedBootstrapForCohort(cohort: string): void {
|
|
|
239
237
|
"templates",
|
|
240
238
|
"templates",
|
|
241
239
|
);
|
|
242
|
-
const
|
|
243
|
-
if (!existsSync(
|
|
240
|
+
const templatePath = join(templatesDir, templateFileName);
|
|
241
|
+
if (!existsSync(templatePath)) {
|
|
244
242
|
log.warn(
|
|
245
|
-
{
|
|
246
|
-
"
|
|
243
|
+
{ templateFileName },
|
|
244
|
+
"Bootstrap template not found, keeping generic BOOTSTRAP.md",
|
|
247
245
|
);
|
|
248
246
|
return;
|
|
249
247
|
}
|
|
250
248
|
|
|
251
249
|
try {
|
|
252
|
-
const
|
|
253
|
-
writeFileSync(bootstrapPath,
|
|
250
|
+
const templateContent = readFileSync(templatePath, "utf-8");
|
|
251
|
+
writeFileSync(bootstrapPath, templateContent, "utf-8");
|
|
254
252
|
log.info(
|
|
255
|
-
{
|
|
256
|
-
"Replaced generic BOOTSTRAP.md with
|
|
253
|
+
{ templateFileName },
|
|
254
|
+
"Replaced generic BOOTSTRAP.md with specified template",
|
|
257
255
|
);
|
|
258
256
|
} catch (err) {
|
|
259
257
|
log.warn(
|
|
260
|
-
{ err,
|
|
261
|
-
"Failed to reseed BOOTSTRAP.md
|
|
258
|
+
{ err, templateFileName },
|
|
259
|
+
"Failed to reseed BOOTSTRAP.md with template",
|
|
262
260
|
);
|
|
263
261
|
}
|
|
264
262
|
}
|
|
@@ -280,11 +278,11 @@ export interface BuildSystemPromptOptions {
|
|
|
280
278
|
* file-backed bodies, and runtime-computed transforms.
|
|
281
279
|
*/
|
|
282
280
|
export function buildSystemPrompt(options?: BuildSystemPromptOptions): string {
|
|
283
|
-
// One-shot
|
|
284
|
-
// the generic template, replace it with
|
|
285
|
-
// the prompt reads the file.
|
|
286
|
-
if (options?.onboardingContext?.
|
|
287
|
-
|
|
281
|
+
// One-shot bootstrap swap: if the onboarding context specifies a bootstrap
|
|
282
|
+
// template and BOOTSTRAP.md is still the generic template, replace it with
|
|
283
|
+
// the specified variant before the prompt reads the file.
|
|
284
|
+
if (options?.onboardingContext?.bootstrapTemplate) {
|
|
285
|
+
maybeReseedBootstrap(options.onboardingContext.bootstrapTemplate);
|
|
288
286
|
}
|
|
289
287
|
|
|
290
288
|
// Slugs used by the persona sections (`10-user-persona`,
|
|
@@ -6,9 +6,15 @@ import { stripCommentLines } from "../util/strip-comment-lines.js";
|
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Returns true when the prompt file content is still the unmodified template
|
|
9
|
-
* shipped with the daemon.
|
|
10
|
-
*
|
|
11
|
-
* templates are edited in future releases
|
|
9
|
+
* shipped with the daemon. Comment-strips BOTH the workspace content and the
|
|
10
|
+
* bundled template source before comparing, so the check stays accurate even
|
|
11
|
+
* if templates are edited in future releases — and regardless of whether the
|
|
12
|
+
* caller passes raw or already-stripped content. Most callers pre-strip via
|
|
13
|
+
* `readPromptFile`/`stripCommentLines`, but the `08-identity` section
|
|
14
|
+
* transform receives the raw workspace body (comment-stripping happens after
|
|
15
|
+
* the transform in `renderSection`); stripping here keeps detection correct
|
|
16
|
+
* for both. `stripCommentLines` is idempotent, so pre-stripped callers are
|
|
17
|
+
* unaffected.
|
|
12
18
|
*
|
|
13
19
|
* Kept in a leaf module so the bundled section registry can depend on it
|
|
14
20
|
* without forming a cycle through `system-prompt.ts → sections.ts →
|
|
@@ -30,7 +36,7 @@ export function isTemplateContent(
|
|
|
30
36
|
const templateContent = stripCommentLines(
|
|
31
37
|
readFileSync(templatePath, "utf-8"),
|
|
32
38
|
);
|
|
33
|
-
return content === templateContent;
|
|
39
|
+
return stripCommentLines(content) === templateContent;
|
|
34
40
|
} catch {
|
|
35
41
|
return false;
|
|
36
42
|
}
|
|
@@ -36,19 +36,17 @@ Match their energy, not just their format. Lowercase and terse gets lowercase an
|
|
|
36
36
|
|
|
37
37
|
Don't present options and ask what they'd prefer. That reads as hedging. Given what you know, pick the most useful path and say why. Wrong is recoverable. Vague isn't.
|
|
38
38
|
|
|
39
|
-
###
|
|
39
|
+
### Gmail scan (when connected)
|
|
40
40
|
|
|
41
|
-
If the First-Run User Context says "Google connected: yes" and the user asks you to scan, you MUST actually call the `subagent_spawn` tool
|
|
41
|
+
If the First-Run User Context says "Google connected: yes" and lists Gmail access, and the user asks you to scan Gmail, you MUST actually call the `subagent_spawn` tool. Do not simulate, summarize, or render progress components without making a real tool call. The scan requires live API access; you cannot know the results without executing the tool.
|
|
42
42
|
|
|
43
|
-
Call `subagent_spawn`
|
|
43
|
+
Call `subagent_spawn` with `label: "gmail-scan"` and this objective. The subagent must produce two clearly separated sections — `## Profile Signals` (structured facts for user modeling) and `## Action Briefing` (narrative, prioritized, noise-filtered):
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
2. `label: "calendar-scan"`, `objective: "Scan my Google Calendar — 7 days back and 7 days forward. Produce two sections:\n\n## Profile Signals\nStructured facts about the user extracted from calendar patterns:\n- Recurring meeting rhythm (daily standups, weekly 1:1s, bi-weekly syncs, etc.)\n- Meeting type ratio: 1:1 vs group vs external\n- Most-frequent attendees (top 5-10 people)\n- Role signals from meeting patterns (e.g. has direct reports if lots of 1:1s, cross-functional if diverse attendee pools, manager if in skip-levels)\n\n## Action Briefing\nNext 72 hours: prep-worthy meetings (what to prepare, who's attending, context from recent related meetings), scheduling conflicts, and back-to-backs worth noting. Past 7 days: recent meetings with likely pending follow-ups or unresolved action items. Prioritize — don't just list every event."`
|
|
47
|
-
3. `label: "drive-scan"`, `objective: "Scan my Google Drive — focus on shared-with-me activity and folder structure rather than just recently modified files. Produce two sections:\n\n## Profile Signals\nStructured facts about the user extracted from Drive patterns:\n- Top-level folder organization (what categories/projects exist)\n- File type distribution (docs, sheets, slides, etc.)\n- Shared drives and team folders the user belongs to\n- Files shared by others in the last 30 days (who shared them and what types)\n\n## Action Briefing\nFiles shared with me in the last 7 days I haven't opened yet, docs with outstanding comments or suggestions directed at me, and any docs where I'm tagged but haven't responded. If Drive activity is low, say so explicitly — 'not much Drive activity this period' is a valid and useful signal, not something to pad with filler."`
|
|
45
|
+
`objective: "Scan my Gmail from the last 7 days. Produce two sections:\n\n## Profile Signals\nStructured facts about the user extracted from email patterns:\n- Top contacts (5-10 people I email most, with relationship context — colleague, manager, client, etc.)\n- Dominant domains/companies appearing in my inbox\n- Initiate-vs-respond ratio (do I start threads or mostly reply?)\n- Recurring topics or threads\n- Role indicators (e.g. manages people, IC, external-facing, sales, engineering)\n\n## Action Briefing\nEmails that need a human response from me, ordered by urgency. Skip marketing, automated notifications, and newsletters entirely. For each actionable email: who sent it, subject, why it needs my attention, and how urgent it is. If nothing needs action, say so — an empty inbox is a valid signal."`
|
|
48
46
|
|
|
49
|
-
After spawning, tell the user the
|
|
47
|
+
After spawning, tell the user the scan is running in the background and continue the conversation normally. Do not wait or poll — you will be notified automatically when the subagent completes.
|
|
50
48
|
|
|
51
|
-
When subagent completion
|
|
49
|
+
When the subagent completion notification arrives, use `subagent_read` to get results, then synthesize — don't just list. First, use `## Profile Signals` to form an initial picture of the user: their role, key people, work patterns, and communication style. Use this to calibrate your tone and what you reference going forward. Then lead with 1–3 actionable insights from `## Action Briefing` and offer to do something concrete about each one. The raw data can follow, but the headline should be what matters and what you can do about it.
|
|
52
50
|
|
|
53
51
|
If the user doesn't ask for a scan, don't offer it again. The greeting already mentioned it.
|
|
54
52
|
|
|
@@ -120,9 +118,7 @@ If nothing comes up, don't force it.
|
|
|
120
118
|
|
|
121
119
|
## Assistant migration
|
|
122
120
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
If no prior assistants are listed, skip this entirely.
|
|
121
|
+
Help first: do the user's actual first task before pivoting to this — the migration offer must never derail or precede real work. Then, at the first natural opening, offer it. This applies to every new assistant, whether onboarding was full, condensed, or absent: land it early in the first conversation, at the first lull or seam, even when the user came in task-first. Keep it to one light offer, a useful shortcut and not an upsell: "If you've already built context, prompts, skills, or workflows somewhere else, I can help port them over." Prior context may live in ChatGPT, Claude, OpenClaw, Hermes, or another assistant or harness — name those when it helps. If they decline, drop it and don't re-offer. Only if they opt in, use the `assistant-migration` skill to inventory and port that context; do not load or activate migration-related skills preemptively.
|
|
126
122
|
|
|
127
123
|
## Wrap up
|
|
128
124
|
|
|
@@ -2,8 +2,6 @@ _ Lines starting with _ are comments - they won't appear in the system prompt
|
|
|
2
2
|
|
|
3
3
|
# IDENTITY.md
|
|
4
4
|
|
|
5
|
-
This file is yours. Add sections, restructure it, make it reflect who you are. Name, Emoji, Role, Personality are parsed by the app - keep their `- **Label:**` format. Everything else is freeform.
|
|
6
|
-
|
|
7
5
|
- **Name:** _(not yet chosen)_
|
|
8
6
|
- **Emoji:** _(not yet chosen)_
|
|
9
7
|
- **Nature:** _(not yet established)_
|
|
@@ -46,13 +46,15 @@ describe("isConnectionCompatibleWithModel", () => {
|
|
|
46
46
|
test("oauth_subscription connection is incompatible with a non-Codex model", () => {
|
|
47
47
|
const conn = { auth: oauthAuth };
|
|
48
48
|
expect(isConnectionCompatibleWithModel(conn, "gpt-5")).toBe(false);
|
|
49
|
-
expect(isConnectionCompatibleWithModel(conn, "gpt-5.5")).toBe(false);
|
|
50
49
|
expect(isConnectionCompatibleWithModel(conn, "gpt-5.4-nano")).toBe(false);
|
|
50
|
+
expect(isConnectionCompatibleWithModel(conn, "gpt-5.5-pro")).toBe(false);
|
|
51
51
|
});
|
|
52
52
|
|
|
53
53
|
test("oauth_subscription connection is compatible with a Codex model", () => {
|
|
54
54
|
const conn = { auth: oauthAuth };
|
|
55
|
+
expect(isConnectionCompatibleWithModel(conn, "gpt-5.5")).toBe(true);
|
|
55
56
|
expect(isConnectionCompatibleWithModel(conn, "gpt-5.4")).toBe(true);
|
|
57
|
+
expect(isConnectionCompatibleWithModel(conn, "gpt-5.4-mini")).toBe(true);
|
|
56
58
|
expect(isConnectionCompatibleWithModel(conn, "gpt-5.3-codex")).toBe(true);
|
|
57
59
|
});
|
|
58
60
|
|
|
@@ -89,7 +91,6 @@ mock.module("../../memory/db-connection.js", () => ({
|
|
|
89
91
|
type Connection = {
|
|
90
92
|
name: string;
|
|
91
93
|
provider: string;
|
|
92
|
-
status: string;
|
|
93
94
|
auth: { type: string; credential?: string };
|
|
94
95
|
};
|
|
95
96
|
|
|
@@ -140,13 +141,11 @@ function reset(): void {
|
|
|
140
141
|
const OPENAI_KEY: Connection = {
|
|
141
142
|
name: "openai-key",
|
|
142
143
|
provider: "openai",
|
|
143
|
-
status: "active",
|
|
144
144
|
auth: { type: "api_key", credential: "credential/openai" },
|
|
145
145
|
};
|
|
146
146
|
const OPENAI_CODEX: Connection = {
|
|
147
147
|
name: "openai-codex",
|
|
148
148
|
provider: "openai",
|
|
149
|
-
status: "active",
|
|
150
149
|
auth: {
|
|
151
150
|
type: "oauth_subscription",
|
|
152
151
|
credential: "credential/openai-codex/access_token",
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { type LLMConfigBase, LLMSchema } from "../../config/schemas/llm.js";
|
|
4
|
+
import type { ProviderConnection } from "../inference/auth.js";
|
|
5
|
+
import type { ProvidersConfig } from "../registry.js";
|
|
6
|
+
|
|
7
|
+
const adapterCalls: Array<{
|
|
8
|
+
connection: ProviderConnection;
|
|
9
|
+
opts: { model: string; useNativeWebSearch?: boolean };
|
|
10
|
+
}> = [];
|
|
11
|
+
|
|
12
|
+
mock.module("../inference/resolve-auth.js", () => ({
|
|
13
|
+
resolveAuth: async () => ({
|
|
14
|
+
ok: true,
|
|
15
|
+
resolved: {
|
|
16
|
+
kind: "header",
|
|
17
|
+
headers: { Authorization: "Bearer test-provider-key" },
|
|
18
|
+
},
|
|
19
|
+
}),
|
|
20
|
+
}));
|
|
21
|
+
|
|
22
|
+
mock.module("../inference/adapter-factory.js", () => ({
|
|
23
|
+
buildProviderAdapter: () => null,
|
|
24
|
+
createAdapterFromConnection: (
|
|
25
|
+
connection: ProviderConnection,
|
|
26
|
+
_resolvedAuth: unknown,
|
|
27
|
+
opts: { model: string; useNativeWebSearch?: boolean },
|
|
28
|
+
) => {
|
|
29
|
+
adapterCalls.push({ connection, opts });
|
|
30
|
+
return {
|
|
31
|
+
name: connection.provider,
|
|
32
|
+
sendMessage: async () => ({
|
|
33
|
+
content: [],
|
|
34
|
+
model: opts.model,
|
|
35
|
+
usage: { inputTokens: 0, outputTokens: 0 },
|
|
36
|
+
stopReason: "stop",
|
|
37
|
+
}),
|
|
38
|
+
};
|
|
39
|
+
},
|
|
40
|
+
}));
|
|
41
|
+
|
|
42
|
+
import {
|
|
43
|
+
clearConnectionProviderCache,
|
|
44
|
+
resolveProviderFromConnection,
|
|
45
|
+
} from "../registry.js";
|
|
46
|
+
|
|
47
|
+
function makeConfig(): ProvidersConfig {
|
|
48
|
+
const baseLlm = LLMSchema.parse({});
|
|
49
|
+
return {
|
|
50
|
+
services: {
|
|
51
|
+
inference: {},
|
|
52
|
+
"image-generation": {
|
|
53
|
+
mode: "your-own",
|
|
54
|
+
provider: "gemini",
|
|
55
|
+
model: "gemini-3.1-flash-image-preview",
|
|
56
|
+
},
|
|
57
|
+
"web-search": {
|
|
58
|
+
mode: "managed",
|
|
59
|
+
provider: "inference-provider-native",
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
llm: {
|
|
63
|
+
...baseLlm,
|
|
64
|
+
default: {
|
|
65
|
+
...baseLlm.default,
|
|
66
|
+
provider: "openrouter" as LLMConfigBase["provider"],
|
|
67
|
+
model: "x-ai/grok-4.20-beta",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const openRouterConnection: ProviderConnection = {
|
|
74
|
+
name: "openrouter-personal",
|
|
75
|
+
provider: "openrouter",
|
|
76
|
+
auth: { type: "api_key", credential: "credential/openrouter/api_key" },
|
|
77
|
+
label: "OpenRouter",
|
|
78
|
+
baseUrl: null,
|
|
79
|
+
models: null,
|
|
80
|
+
createdAt: 1,
|
|
81
|
+
updatedAt: 1,
|
|
82
|
+
isManaged: false,
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
describe("resolveProviderFromConnection native web search selection", () => {
|
|
86
|
+
beforeEach(() => {
|
|
87
|
+
adapterCalls.length = 0;
|
|
88
|
+
clearConnectionProviderCache();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("uses the routed OpenRouter Anthropic model when enabling native web search", async () => {
|
|
92
|
+
await resolveProviderFromConnection(openRouterConnection, makeConfig(), {
|
|
93
|
+
model: "anthropic/claude-opus-4-7",
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
expect(adapterCalls).toHaveLength(1);
|
|
97
|
+
expect(adapterCalls[0].opts).toMatchObject({
|
|
98
|
+
model: "anthropic/claude-opus-4-7",
|
|
99
|
+
useNativeWebSearch: true,
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
test("keeps OpenRouter native web search model-specific across cached connections", async () => {
|
|
104
|
+
await resolveProviderFromConnection(openRouterConnection, makeConfig(), {
|
|
105
|
+
model: "x-ai/grok-4.20-beta",
|
|
106
|
+
});
|
|
107
|
+
await resolveProviderFromConnection(openRouterConnection, makeConfig(), {
|
|
108
|
+
model: "anthropic/claude-opus-4-7",
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
expect(adapterCalls.map((call) => call.opts)).toEqual([
|
|
112
|
+
expect.objectContaining({
|
|
113
|
+
model: "x-ai/grok-4.20-beta",
|
|
114
|
+
useNativeWebSearch: false,
|
|
115
|
+
}),
|
|
116
|
+
expect.objectContaining({
|
|
117
|
+
model: "anthropic/claude-opus-4-7",
|
|
118
|
+
useNativeWebSearch: true,
|
|
119
|
+
}),
|
|
120
|
+
]);
|
|
121
|
+
});
|
|
122
|
+
});
|
|
@@ -24,7 +24,10 @@ import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
24
24
|
import { resolveCallSiteConfig } from "../config/llm-resolver.js";
|
|
25
25
|
import { getConfig } from "../config/loader.js";
|
|
26
26
|
import { getDb } from "../memory/db-connection.js";
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
describeSubscriptionModelIncompatibility,
|
|
29
|
+
isConnectionCompatibleWithModel,
|
|
30
|
+
} from "./connection-model-compat.js";
|
|
28
31
|
import {
|
|
29
32
|
ConnectionResolutionError,
|
|
30
33
|
tryResolveProviderForConnectionName,
|
|
@@ -146,25 +149,32 @@ export class CallSiteRoutingProvider implements Provider {
|
|
|
146
149
|
if (!callSite) return this.defaultProvider;
|
|
147
150
|
|
|
148
151
|
const overrideProfile = options?.config?.overrideProfile;
|
|
152
|
+
// Forward the per-conversation mix seed so transport selection picks the
|
|
153
|
+
// same mix arm as wire-param normalization in `retry.ts` — otherwise a mix
|
|
154
|
+
// spanning providers could route the transport to a different arm than the
|
|
155
|
+
// request params.
|
|
156
|
+
const selectionSeed = options?.config?.selectionSeed;
|
|
149
157
|
const resolved = resolveCallSiteConfig(callSite, getConfig().llm, {
|
|
150
158
|
overrideProfile,
|
|
159
|
+
selectionSeed,
|
|
151
160
|
});
|
|
152
161
|
|
|
153
162
|
let connectionName = resolved.provider_connection;
|
|
154
163
|
|
|
155
164
|
// When no connection is set and the provider differs from the default,
|
|
156
|
-
// auto-resolve
|
|
157
|
-
//
|
|
158
|
-
//
|
|
165
|
+
// auto-resolve a connection for the provider (handles the case where the
|
|
166
|
+
// profile set provider but not provider_connection, and the merge didn't
|
|
167
|
+
// inherit one).
|
|
168
|
+
let autoResolveCandidates:
|
|
169
|
+
| import("./inference/auth.js").ProviderConnection[]
|
|
170
|
+
| undefined;
|
|
159
171
|
if (!connectionName && resolved.provider !== this.defaultProvider.name) {
|
|
160
172
|
try {
|
|
161
|
-
|
|
173
|
+
autoResolveCandidates = listConnections(getDb(), {
|
|
162
174
|
provider: resolved.provider,
|
|
163
175
|
});
|
|
164
|
-
const active =
|
|
165
|
-
(c)
|
|
166
|
-
c.status === "active" &&
|
|
167
|
-
isConnectionCompatibleWithModel(c, resolved.model),
|
|
176
|
+
const active = autoResolveCandidates.find((c) =>
|
|
177
|
+
isConnectionCompatibleWithModel(c, resolved.model),
|
|
168
178
|
);
|
|
169
179
|
if (active) {
|
|
170
180
|
connectionName = active.name;
|
|
@@ -188,6 +198,20 @@ export class CallSiteRoutingProvider implements Provider {
|
|
|
188
198
|
return this.defaultProvider;
|
|
189
199
|
}
|
|
190
200
|
|
|
201
|
+
if (autoResolveCandidates) {
|
|
202
|
+
const incompatMsg = describeSubscriptionModelIncompatibility(
|
|
203
|
+
autoResolveCandidates,
|
|
204
|
+
resolved.model,
|
|
205
|
+
);
|
|
206
|
+
if (incompatMsg) {
|
|
207
|
+
throw new ConnectionResolutionError(
|
|
208
|
+
"<resolved-callsite>",
|
|
209
|
+
"model_incompatible",
|
|
210
|
+
incompatMsg,
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
191
215
|
throw new ConnectionResolutionError(
|
|
192
216
|
"<resolved-callsite>",
|
|
193
217
|
"missing_connection",
|
|
@@ -36,3 +36,26 @@ export function isConnectionCompatibleWithModel(
|
|
|
36
36
|
if (!model) return true;
|
|
37
37
|
return isCodexSubscriptionModel(model);
|
|
38
38
|
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* When auto-resolution found candidates but none were model-compatible,
|
|
42
|
+
* return a user-facing explanation if the incompatibility is specifically
|
|
43
|
+
* due to all candidates being `oauth_subscription` (ChatGPT) connections.
|
|
44
|
+
*
|
|
45
|
+
* Returns `null` when the incompatibility has a different cause (callers
|
|
46
|
+
* should fall through to their existing generic error).
|
|
47
|
+
*/
|
|
48
|
+
export function describeSubscriptionModelIncompatibility(
|
|
49
|
+
candidates: Pick<ProviderConnection, "auth">[],
|
|
50
|
+
model: string | undefined,
|
|
51
|
+
): string | null {
|
|
52
|
+
if (!model || candidates.length === 0) return null;
|
|
53
|
+
if (candidates.some((c) => isConnectionCompatibleWithModel(c, model))) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const allSubscription = candidates.every(
|
|
57
|
+
(c) => c.auth.type === "oauth_subscription",
|
|
58
|
+
);
|
|
59
|
+
if (!allSubscription) return null;
|
|
60
|
+
return `Model "${model}" isn't available through your ChatGPT subscription. Select a supported model or add an OpenAI API key connection.`;
|
|
61
|
+
}
|
|
@@ -30,7 +30,10 @@
|
|
|
30
30
|
import { resolveCallSiteConfig } from "../config/llm-resolver.js";
|
|
31
31
|
import { getDb } from "../memory/db-connection.js";
|
|
32
32
|
import { getLogger } from "../util/logger.js";
|
|
33
|
-
import {
|
|
33
|
+
import {
|
|
34
|
+
describeSubscriptionModelIncompatibility,
|
|
35
|
+
isConnectionCompatibleWithModel,
|
|
36
|
+
} from "./connection-model-compat.js";
|
|
34
37
|
import { getConnection, listConnections } from "./inference/connections.js";
|
|
35
38
|
import type { ProvidersConfig } from "./registry.js";
|
|
36
39
|
import { resolveProviderFromConnection } from "./registry.js";
|
|
@@ -52,7 +55,8 @@ export class ConnectionResolutionError extends Error {
|
|
|
52
55
|
| "lookup_failed"
|
|
53
56
|
| "not_found"
|
|
54
57
|
| "provider_mismatch"
|
|
55
|
-
| "missing_connection"
|
|
58
|
+
| "missing_connection"
|
|
59
|
+
| "model_incompatible",
|
|
56
60
|
message: string,
|
|
57
61
|
public readonly cause?: unknown,
|
|
58
62
|
) {
|
|
@@ -116,12 +120,12 @@ export async function tryResolveProviderForConnectionName(
|
|
|
116
120
|
// "anthropic-managed" leaked through). Try to find an active connection
|
|
117
121
|
// for the expected provider before giving up.
|
|
118
122
|
let resolved = false;
|
|
123
|
+
let mismatchCandidates: import("./inference/auth.js").ProviderConnection[] | undefined;
|
|
119
124
|
try {
|
|
120
125
|
const db = getDb();
|
|
121
|
-
|
|
122
|
-
const active =
|
|
123
|
-
(c)
|
|
124
|
-
c.status === "active" && isConnectionCompatibleWithModel(c, model),
|
|
126
|
+
mismatchCandidates = listConnections(db, { provider: expectedProvider });
|
|
127
|
+
const active = mismatchCandidates.find((c) =>
|
|
128
|
+
isConnectionCompatibleWithModel(c, model),
|
|
125
129
|
);
|
|
126
130
|
if (active) {
|
|
127
131
|
log.info(
|
|
@@ -130,7 +134,7 @@ export async function tryResolveProviderForConnectionName(
|
|
|
130
134
|
resolvedConnection: active.name,
|
|
131
135
|
expectedProvider,
|
|
132
136
|
},
|
|
133
|
-
"Auto-resolved stale provider_connection to matching
|
|
137
|
+
"Auto-resolved stale provider_connection to matching connection",
|
|
134
138
|
);
|
|
135
139
|
connection = active;
|
|
136
140
|
resolved = true;
|
|
@@ -139,6 +143,16 @@ export async function tryResolveProviderForConnectionName(
|
|
|
139
143
|
// DB not available — fall through to the original error.
|
|
140
144
|
}
|
|
141
145
|
if (!resolved) {
|
|
146
|
+
const incompatMsg = mismatchCandidates
|
|
147
|
+
? describeSubscriptionModelIncompatibility(mismatchCandidates, model)
|
|
148
|
+
: null;
|
|
149
|
+
if (incompatMsg) {
|
|
150
|
+
throw new ConnectionResolutionError(
|
|
151
|
+
connectionName,
|
|
152
|
+
"model_incompatible",
|
|
153
|
+
incompatMsg,
|
|
154
|
+
);
|
|
155
|
+
}
|
|
142
156
|
throw new ConnectionResolutionError(
|
|
143
157
|
connectionName,
|
|
144
158
|
"provider_mismatch",
|
|
@@ -146,13 +160,6 @@ export async function tryResolveProviderForConnectionName(
|
|
|
146
160
|
);
|
|
147
161
|
}
|
|
148
162
|
}
|
|
149
|
-
if (connection.status === "disabled") {
|
|
150
|
-
log.debug(
|
|
151
|
-
{ connectionName, provider: connection.provider },
|
|
152
|
-
"provider_connection is disabled — returning null",
|
|
153
|
-
);
|
|
154
|
-
return null;
|
|
155
|
-
}
|
|
156
163
|
// `resolveProviderFromConnection` reaches into auth resolution (credential
|
|
157
164
|
// reads, managed-proxy context). A transient failure there is a soft
|
|
158
165
|
// miss — log and return null so the caller can treat it the same as
|
|
@@ -160,7 +167,7 @@ export async function tryResolveProviderForConnectionName(
|
|
|
160
167
|
// catch is specifically for in-flight failures that should not take
|
|
161
168
|
// dispatch offline.
|
|
162
169
|
try {
|
|
163
|
-
return await resolveProviderFromConnection(connection, config);
|
|
170
|
+
return await resolveProviderFromConnection(connection, config, { model });
|
|
164
171
|
} catch (err) {
|
|
165
172
|
log.warn(
|
|
166
173
|
{ err, connectionName },
|
|
@@ -196,15 +203,14 @@ export async function resolveDefaultProvider(
|
|
|
196
203
|
// provider without a connection ("Any active" selection), and the merge
|
|
197
204
|
// cleared or failed to inherit one. Try to find an active connection
|
|
198
205
|
// for the provider before giving up.
|
|
206
|
+
let autoResolveCandidates: import("./inference/auth.js").ProviderConnection[] | undefined;
|
|
199
207
|
if (resolved.provider) {
|
|
200
208
|
try {
|
|
201
|
-
|
|
209
|
+
autoResolveCandidates = listConnections(getDb(), {
|
|
202
210
|
provider: resolved.provider,
|
|
203
211
|
});
|
|
204
|
-
const active =
|
|
205
|
-
(c)
|
|
206
|
-
c.status === "active" &&
|
|
207
|
-
isConnectionCompatibleWithModel(c, resolved.model),
|
|
212
|
+
const active = autoResolveCandidates.find((c) =>
|
|
213
|
+
isConnectionCompatibleWithModel(c, resolved.model),
|
|
208
214
|
);
|
|
209
215
|
if (active) {
|
|
210
216
|
log.info(
|
|
@@ -218,6 +224,19 @@ export async function resolveDefaultProvider(
|
|
|
218
224
|
}
|
|
219
225
|
}
|
|
220
226
|
if (!connectionName) {
|
|
227
|
+
const incompatMsg = autoResolveCandidates
|
|
228
|
+
? describeSubscriptionModelIncompatibility(
|
|
229
|
+
autoResolveCandidates,
|
|
230
|
+
resolved.model,
|
|
231
|
+
)
|
|
232
|
+
: null;
|
|
233
|
+
if (incompatMsg) {
|
|
234
|
+
throw new ConnectionResolutionError(
|
|
235
|
+
"<llm.default>",
|
|
236
|
+
"model_incompatible",
|
|
237
|
+
incompatMsg,
|
|
238
|
+
);
|
|
239
|
+
}
|
|
221
240
|
throw new ConnectionResolutionError(
|
|
222
241
|
"<llm.default>",
|
|
223
242
|
"missing_connection",
|
|
@@ -4,6 +4,7 @@ import { ApiError, GoogleGenAI, ThinkingLevel } from "@google/genai";
|
|
|
4
4
|
import { isAbortReason } from "../../util/abort-reasons.js";
|
|
5
5
|
import { ProviderError } from "../../util/errors.js";
|
|
6
6
|
import { getLogger } from "../../util/logger.js";
|
|
7
|
+
import { PROVIDER_CATALOG } from "../model-catalog.js";
|
|
7
8
|
import { createStreamTimeout } from "../stream-timeout.js";
|
|
8
9
|
import type {
|
|
9
10
|
ContentBlock,
|
|
@@ -74,6 +75,24 @@ function buildThinkingConfig(
|
|
|
74
75
|
return Object.keys(result).length > 0 ? result : undefined;
|
|
75
76
|
}
|
|
76
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Whether the active Gemini model accepts a `thinkingConfig`. Non-thinking
|
|
80
|
+
* models (e.g. `gemini-2.5-flash-lite`) reject thinking params, and gemini is
|
|
81
|
+
* in `THINKING_AWARE_PROVIDERS` so `providers/retry.ts` no longer strips them —
|
|
82
|
+
* so we gate on the catalog's `supportsThinking` capability here. Unknown or
|
|
83
|
+
* uncatalogued models default to allowing thinking config (preserving prior
|
|
84
|
+
* behavior); only an explicit `supportsThinking: false` suppresses it.
|
|
85
|
+
*/
|
|
86
|
+
function geminiModelSupportsThinking(model: string): boolean {
|
|
87
|
+
const normalized = model.startsWith("models/")
|
|
88
|
+
? model.slice("models/".length)
|
|
89
|
+
: model;
|
|
90
|
+
const catalogModel = PROVIDER_CATALOG.find(
|
|
91
|
+
(provider) => provider.id === "gemini",
|
|
92
|
+
)?.models.find((m) => m.id === normalized);
|
|
93
|
+
return catalogModel?.supportsThinking !== false;
|
|
94
|
+
}
|
|
95
|
+
|
|
77
96
|
function stripGeminiHttpOptions(
|
|
78
97
|
config: genai.GenerateContentConfig,
|
|
79
98
|
): genai.GenerateContentConfig {
|
|
@@ -215,10 +234,12 @@ export class GeminiProvider implements Provider {
|
|
|
215
234
|
const usageAttributionHeaders = configObj?.usageAttributionHeaders as
|
|
216
235
|
| Record<string, string>
|
|
217
236
|
| undefined;
|
|
218
|
-
const thinkingConfig = buildThinkingConfig(
|
|
219
|
-
configObj?.thinking as Record<string, unknown> | undefined,
|
|
220
|
-
);
|
|
221
237
|
const activeModel = modelOverride ?? this.model;
|
|
238
|
+
const thinkingConfig = geminiModelSupportsThinking(activeModel)
|
|
239
|
+
? buildThinkingConfig(
|
|
240
|
+
configObj?.thinking as Record<string, unknown> | undefined,
|
|
241
|
+
)
|
|
242
|
+
: undefined;
|
|
222
243
|
|
|
223
244
|
try {
|
|
224
245
|
const geminiContents = this.toGeminiContents(messages, activeModel);
|