@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
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
OpenAIChatCompletionsProvider,
|
|
5
|
+
type OpenAIChatCompletionsProviderOptions,
|
|
6
|
+
} from "../chat-completions-provider.js";
|
|
4
7
|
|
|
5
8
|
type ReasoningDetail = {
|
|
6
9
|
type?: string;
|
|
@@ -32,23 +35,35 @@ function makeStream(chunks: MockChunk[]): AsyncIterable<MockChunk> {
|
|
|
32
35
|
};
|
|
33
36
|
}
|
|
34
37
|
|
|
35
|
-
function stubProvider(
|
|
38
|
+
function stubProvider(
|
|
39
|
+
chunks: MockChunk[],
|
|
40
|
+
options?: OpenAIChatCompletionsProviderOptions,
|
|
41
|
+
): {
|
|
36
42
|
provider: OpenAIChatCompletionsProvider;
|
|
37
43
|
events: Array<{ type: string; thinking?: string; text?: string }>;
|
|
44
|
+
requests: unknown[];
|
|
38
45
|
} {
|
|
39
|
-
const provider = new OpenAIChatCompletionsProvider(
|
|
46
|
+
const provider = new OpenAIChatCompletionsProvider(
|
|
47
|
+
"test-key",
|
|
48
|
+
"test-model",
|
|
49
|
+
options,
|
|
50
|
+
);
|
|
51
|
+
const requests: unknown[] = [];
|
|
40
52
|
// Swap the SDK client for a stub whose chat.completions.create returns our
|
|
41
53
|
// canned async iterable.
|
|
42
54
|
(provider as unknown as { client: unknown }).client = {
|
|
43
55
|
chat: {
|
|
44
56
|
completions: {
|
|
45
|
-
create: async () =>
|
|
57
|
+
create: async (params: unknown) => {
|
|
58
|
+
requests.push(params);
|
|
59
|
+
return makeStream(chunks);
|
|
60
|
+
},
|
|
46
61
|
},
|
|
47
62
|
},
|
|
48
63
|
};
|
|
49
64
|
const events: Array<{ type: string; thinking?: string; text?: string }> = [];
|
|
50
65
|
(provider as unknown as { __events: typeof events }).__events = events;
|
|
51
|
-
return { provider, events };
|
|
66
|
+
return { provider, events, requests };
|
|
52
67
|
}
|
|
53
68
|
|
|
54
69
|
async function runStream(
|
|
@@ -232,4 +247,141 @@ describe("OpenAIChatCompletionsProvider reasoning parsing", () => {
|
|
|
232
247
|
expect(deltas.map((d) => d.thinking)).toEqual(["it ", "worked", "!"]);
|
|
233
248
|
expect(thinking).toBe("it worked!");
|
|
234
249
|
});
|
|
250
|
+
|
|
251
|
+
test("round-trips prior assistant thinking as reasoning_content when field is set", async () => {
|
|
252
|
+
const { provider, requests } = stubProvider(
|
|
253
|
+
[
|
|
254
|
+
{
|
|
255
|
+
choices: [{ delta: { content: "continued" }, finish_reason: "stop" }],
|
|
256
|
+
usage: { prompt_tokens: 4, completion_tokens: 2 },
|
|
257
|
+
},
|
|
258
|
+
],
|
|
259
|
+
{ assistantReasoningField: "reasoning_content" },
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
await provider.sendMessage([
|
|
263
|
+
{ role: "user", content: [{ type: "text", text: "first question" }] },
|
|
264
|
+
{
|
|
265
|
+
role: "assistant",
|
|
266
|
+
content: [
|
|
267
|
+
{ type: "thinking", thinking: "hidden chain state", signature: "" },
|
|
268
|
+
{ type: "text", text: "first answer" },
|
|
269
|
+
],
|
|
270
|
+
},
|
|
271
|
+
{ role: "user", content: [{ type: "text", text: "follow up" }] },
|
|
272
|
+
]);
|
|
273
|
+
|
|
274
|
+
const params = requests[0] as {
|
|
275
|
+
messages: Array<{
|
|
276
|
+
role: string;
|
|
277
|
+
content: string | null;
|
|
278
|
+
reasoning_content?: string;
|
|
279
|
+
}>;
|
|
280
|
+
};
|
|
281
|
+
const assistantMsg = params.messages.find((m) => m.role === "assistant");
|
|
282
|
+
expect(assistantMsg).toEqual({
|
|
283
|
+
role: "assistant",
|
|
284
|
+
content: "first answer",
|
|
285
|
+
reasoning_content: "hidden chain state",
|
|
286
|
+
});
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
test("uses reasoning field for OpenRouter-style round-trip", async () => {
|
|
290
|
+
const { provider, requests } = stubProvider(
|
|
291
|
+
[
|
|
292
|
+
{
|
|
293
|
+
choices: [{ delta: { content: "ok" }, finish_reason: "stop" }],
|
|
294
|
+
usage: { prompt_tokens: 2, completion_tokens: 1 },
|
|
295
|
+
},
|
|
296
|
+
],
|
|
297
|
+
{ assistantReasoningField: "reasoning" },
|
|
298
|
+
);
|
|
299
|
+
|
|
300
|
+
await provider.sendMessage([
|
|
301
|
+
{
|
|
302
|
+
role: "assistant",
|
|
303
|
+
content: [
|
|
304
|
+
{ type: "thinking", thinking: "visible summary", signature: "" },
|
|
305
|
+
{ type: "text", text: "answer" },
|
|
306
|
+
],
|
|
307
|
+
},
|
|
308
|
+
]);
|
|
309
|
+
|
|
310
|
+
const params = requests[0] as {
|
|
311
|
+
messages: Array<{
|
|
312
|
+
role: string;
|
|
313
|
+
reasoning?: string;
|
|
314
|
+
reasoning_content?: string;
|
|
315
|
+
}>;
|
|
316
|
+
};
|
|
317
|
+
expect(params.messages[0].reasoning).toBe("visible summary");
|
|
318
|
+
expect(params.messages[0].reasoning_content).toBeUndefined();
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
test("drops thinking blocks when assistantReasoningField is unset", async () => {
|
|
322
|
+
const { provider, requests } = stubProvider([
|
|
323
|
+
{
|
|
324
|
+
choices: [{ delta: { content: "reply" }, finish_reason: "stop" }],
|
|
325
|
+
usage: { prompt_tokens: 2, completion_tokens: 1 },
|
|
326
|
+
},
|
|
327
|
+
]);
|
|
328
|
+
|
|
329
|
+
await provider.sendMessage([
|
|
330
|
+
{
|
|
331
|
+
role: "assistant",
|
|
332
|
+
content: [
|
|
333
|
+
{ type: "thinking", thinking: "should be dropped", signature: "" },
|
|
334
|
+
{ type: "text", text: "visible" },
|
|
335
|
+
],
|
|
336
|
+
},
|
|
337
|
+
]);
|
|
338
|
+
|
|
339
|
+
const params = requests[0] as {
|
|
340
|
+
messages: Array<{
|
|
341
|
+
role: string;
|
|
342
|
+
content: string | null;
|
|
343
|
+
reasoning?: string;
|
|
344
|
+
reasoning_content?: string;
|
|
345
|
+
}>;
|
|
346
|
+
};
|
|
347
|
+
const assistantMsg = params.messages[0];
|
|
348
|
+
expect(assistantMsg.content).toBe("visible");
|
|
349
|
+
expect(assistantMsg.reasoning).toBeUndefined();
|
|
350
|
+
expect(assistantMsg.reasoning_content).toBeUndefined();
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
test("skips Anthropic-originated thinking blocks (with signatures)", async () => {
|
|
354
|
+
const { provider, requests } = stubProvider(
|
|
355
|
+
[
|
|
356
|
+
{
|
|
357
|
+
choices: [{ delta: { content: "ok" }, finish_reason: "stop" }],
|
|
358
|
+
usage: { prompt_tokens: 2, completion_tokens: 1 },
|
|
359
|
+
},
|
|
360
|
+
],
|
|
361
|
+
{ assistantReasoningField: "reasoning_content" },
|
|
362
|
+
);
|
|
363
|
+
|
|
364
|
+
await provider.sendMessage([
|
|
365
|
+
{
|
|
366
|
+
role: "assistant",
|
|
367
|
+
content: [
|
|
368
|
+
{
|
|
369
|
+
type: "thinking",
|
|
370
|
+
thinking: "anthropic thinking",
|
|
371
|
+
signature: "sig-abc",
|
|
372
|
+
},
|
|
373
|
+
{ type: "thinking", thinking: "deepseek thinking", signature: "" },
|
|
374
|
+
{ type: "text", text: "answer" },
|
|
375
|
+
],
|
|
376
|
+
},
|
|
377
|
+
]);
|
|
378
|
+
|
|
379
|
+
const params = requests[0] as {
|
|
380
|
+
messages: Array<{
|
|
381
|
+
role: string;
|
|
382
|
+
reasoning_content?: string;
|
|
383
|
+
}>;
|
|
384
|
+
};
|
|
385
|
+
expect(params.messages[0].reasoning_content).toBe("deepseek thinking");
|
|
386
|
+
});
|
|
235
387
|
});
|
|
@@ -54,6 +54,81 @@ export function detectOpenAICompatibleContextOverflow(
|
|
|
54
54
|
return extractOverflowTokensFromMessage(message);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Build the human-readable error string surfaced from an
|
|
59
|
+
* `OpenAI.APIError`. The SDK's `error.message` is typically a one-line
|
|
60
|
+
* summary like `"400 Provider returned error"` — useless when the
|
|
61
|
+
* upstream is OpenRouter (which wraps the real downstream error on
|
|
62
|
+
* `error.error.metadata.raw`) or any provider that returns nested
|
|
63
|
+
* structured details.
|
|
64
|
+
*
|
|
65
|
+
* Returns `{ detail, requestId }` where `detail` includes the JSON body
|
|
66
|
+
* (truncated) and `requestId` is the upstream correlation header when
|
|
67
|
+
* present. Callers fold these into the thrown `ProviderError` so log
|
|
68
|
+
* lines actually identify the failure without re-running the request.
|
|
69
|
+
*
|
|
70
|
+
* Exported for tests.
|
|
71
|
+
*/
|
|
72
|
+
export function extractApiErrorDetail(
|
|
73
|
+
error: InstanceType<typeof OpenAI.APIError>,
|
|
74
|
+
): { detail: string; requestId: string | undefined } {
|
|
75
|
+
const body = (error as { error?: unknown }).error;
|
|
76
|
+
let detail = "";
|
|
77
|
+
if (body !== undefined && body !== null) {
|
|
78
|
+
try {
|
|
79
|
+
const serialized = typeof body === "string" ? body : JSON.stringify(body);
|
|
80
|
+
if (serialized && serialized !== "{}") {
|
|
81
|
+
detail =
|
|
82
|
+
serialized.length > MAX_API_ERROR_DETAIL_CHARS
|
|
83
|
+
? `${serialized.slice(0, MAX_API_ERROR_DETAIL_CHARS)}…`
|
|
84
|
+
: serialized;
|
|
85
|
+
}
|
|
86
|
+
} catch {
|
|
87
|
+
// Body had a cycle or threw on toJSON — fall through with empty detail.
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
const requestId = readHeader(error.headers, [
|
|
91
|
+
"x-request-id",
|
|
92
|
+
"x-openrouter-request-id",
|
|
93
|
+
"openai-request-id",
|
|
94
|
+
"x-amzn-requestid",
|
|
95
|
+
]);
|
|
96
|
+
return { detail, requestId };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/** Cap on the inline-rendered body to keep log lines readable while still
|
|
100
|
+
* surfacing the meaningful upstream payload. Tuned to comfortably hold
|
|
101
|
+
* OpenRouter's `error.metadata.raw` strings, which are typically <1KB. */
|
|
102
|
+
const MAX_API_ERROR_DETAIL_CHARS = 2000;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Read the first matching header from an SDK error's headers object,
|
|
106
|
+
* tolerating both Map-like (`Headers.get()`) and plain-object shapes.
|
|
107
|
+
* Mirrors the shape-tolerance already in `extractRetryAfterMs`.
|
|
108
|
+
*/
|
|
109
|
+
function readHeader(
|
|
110
|
+
headers: unknown,
|
|
111
|
+
names: readonly string[],
|
|
112
|
+
): string | undefined {
|
|
113
|
+
if (!headers) return undefined;
|
|
114
|
+
const getter =
|
|
115
|
+
typeof (headers as { get?: unknown }).get === "function"
|
|
116
|
+
? (headers as { get(name: string): string | null }).get.bind(headers)
|
|
117
|
+
: undefined;
|
|
118
|
+
for (const name of names) {
|
|
119
|
+
let value: string | null | undefined;
|
|
120
|
+
if (getter) {
|
|
121
|
+
value = getter(name);
|
|
122
|
+
} else if (typeof headers === "object") {
|
|
123
|
+
const record = headers as Record<string, unknown>;
|
|
124
|
+
const raw = record[name] ?? record[name.toLowerCase()];
|
|
125
|
+
value = typeof raw === "string" ? raw : undefined;
|
|
126
|
+
}
|
|
127
|
+
if (typeof value === "string" && value.length > 0) return value;
|
|
128
|
+
}
|
|
129
|
+
return undefined;
|
|
130
|
+
}
|
|
131
|
+
|
|
57
132
|
export interface OpenAIChatCompletionsProviderOptions {
|
|
58
133
|
baseURL?: string;
|
|
59
134
|
providerName?: string;
|
|
@@ -75,6 +150,10 @@ export interface OpenAIChatCompletionsProviderOptions {
|
|
|
75
150
|
* blocks. MiniMax and similar providers embed reasoning inside XML-style
|
|
76
151
|
* tags in the regular content field rather than using `reasoning_content`. */
|
|
77
152
|
parseThinkTags?: boolean;
|
|
153
|
+
/** Wire field used to replay prior assistant thinking on multi-turn requests.
|
|
154
|
+
* DeepSeek/Fireworks use `"reasoning_content"`; OpenRouter uses `"reasoning"`.
|
|
155
|
+
* When unset, thinking blocks are dropped from outbound assistant messages. */
|
|
156
|
+
assistantReasoningField?: "reasoning" | "reasoning_content";
|
|
78
157
|
}
|
|
79
158
|
|
|
80
159
|
/** Wire-level reasoning_effort values. The OpenAI SDK type doesn't include
|
|
@@ -146,6 +225,10 @@ export class OpenAIChatCompletionsProvider implements Provider {
|
|
|
146
225
|
private maxReasoningEffort: "high" | "xhigh" | "max";
|
|
147
226
|
private requestHeaders: Record<string, string>;
|
|
148
227
|
private parseThinkTags: boolean;
|
|
228
|
+
private assistantReasoningField:
|
|
229
|
+
| "reasoning"
|
|
230
|
+
| "reasoning_content"
|
|
231
|
+
| undefined;
|
|
149
232
|
|
|
150
233
|
constructor(
|
|
151
234
|
apiKey: string,
|
|
@@ -168,6 +251,7 @@ export class OpenAIChatCompletionsProvider implements Provider {
|
|
|
168
251
|
this.maxReasoningEffort = options.maxReasoningEffort ?? "xhigh";
|
|
169
252
|
this.requestHeaders = options.requestHeaders ?? {};
|
|
170
253
|
this.parseThinkTags = options.parseThinkTags ?? false;
|
|
254
|
+
this.assistantReasoningField = options.assistantReasoningField;
|
|
171
255
|
}
|
|
172
256
|
|
|
173
257
|
async sendMessage(
|
|
@@ -524,18 +608,18 @@ export class OpenAIChatCompletionsProvider implements Provider {
|
|
|
524
608
|
? signal.reason
|
|
525
609
|
: undefined;
|
|
526
610
|
if (error instanceof OpenAI.APIError) {
|
|
611
|
+
const { detail, requestId } = extractApiErrorDetail(error);
|
|
612
|
+
const detailSuffix = detail ? ` ${detail}` : "";
|
|
613
|
+
const requestIdSuffix = requestId ? ` [request_id=${requestId}]` : "";
|
|
614
|
+
const formattedMessage = `${this.providerLabel} API error (${error.status}): ${error.message}${detailSuffix}${requestIdSuffix}`;
|
|
527
615
|
const overflow = detectOpenAICompatibleContextOverflow(error);
|
|
528
616
|
if (overflow) {
|
|
529
|
-
throw new ContextOverflowError(
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
statusCode: error.status,
|
|
536
|
-
cause: error,
|
|
537
|
-
},
|
|
538
|
-
);
|
|
617
|
+
throw new ContextOverflowError(formattedMessage, this.name, {
|
|
618
|
+
actualTokens: overflow.actualTokens,
|
|
619
|
+
maxTokens: overflow.maxTokens,
|
|
620
|
+
statusCode: error.status,
|
|
621
|
+
cause: error,
|
|
622
|
+
});
|
|
539
623
|
}
|
|
540
624
|
const retryAfterMs = extractRetryAfterMs(error.headers);
|
|
541
625
|
const errorOptions: {
|
|
@@ -546,7 +630,7 @@ export class OpenAIChatCompletionsProvider implements Provider {
|
|
|
546
630
|
errorOptions.retryAfterMs = retryAfterMs;
|
|
547
631
|
if (abortReason) errorOptions.abortReason = abortReason;
|
|
548
632
|
throw new ProviderError(
|
|
549
|
-
|
|
633
|
+
formattedMessage,
|
|
550
634
|
this.name,
|
|
551
635
|
error.status,
|
|
552
636
|
Object.keys(errorOptions).length > 0 ? errorOptions : undefined,
|
|
@@ -661,6 +745,7 @@ export class OpenAIChatCompletionsProvider implements Provider {
|
|
|
661
745
|
msg: Message,
|
|
662
746
|
): OpenAI.Chat.Completions.ChatCompletionAssistantMessageParam {
|
|
663
747
|
const textParts: string[] = [];
|
|
748
|
+
const reasoningParts: string[] = [];
|
|
664
749
|
const toolCalls: OpenAI.Chat.Completions.ChatCompletionMessageToolCall[] =
|
|
665
750
|
[];
|
|
666
751
|
|
|
@@ -669,6 +754,10 @@ export class OpenAIChatCompletionsProvider implements Provider {
|
|
|
669
754
|
case "text":
|
|
670
755
|
textParts.push(block.text);
|
|
671
756
|
break;
|
|
757
|
+
case "thinking":
|
|
758
|
+
// Anthropic thinking blocks carry signatures — skip those.
|
|
759
|
+
if (!block.signature) reasoningParts.push(block.thinking);
|
|
760
|
+
break;
|
|
672
761
|
case "tool_use":
|
|
673
762
|
toolCalls.push({
|
|
674
763
|
id: block.id,
|
|
@@ -685,7 +774,7 @@ export class OpenAIChatCompletionsProvider implements Provider {
|
|
|
685
774
|
case "web_search_tool_result":
|
|
686
775
|
textParts.push("[Web search results]");
|
|
687
776
|
break;
|
|
688
|
-
//
|
|
777
|
+
// redacted_thinking, image — not applicable for OpenAI assistant messages
|
|
689
778
|
}
|
|
690
779
|
}
|
|
691
780
|
|
|
@@ -695,6 +784,15 @@ export class OpenAIChatCompletionsProvider implements Provider {
|
|
|
695
784
|
content: textParts.length > 0 ? textParts.join("") : null,
|
|
696
785
|
};
|
|
697
786
|
|
|
787
|
+
if (reasoningParts.length > 0 && this.assistantReasoningField) {
|
|
788
|
+
(
|
|
789
|
+
result as OpenAI.Chat.Completions.ChatCompletionAssistantMessageParam & {
|
|
790
|
+
reasoning?: string;
|
|
791
|
+
reasoning_content?: string;
|
|
792
|
+
}
|
|
793
|
+
)[this.assistantReasoningField] = reasoningParts.join("");
|
|
794
|
+
}
|
|
795
|
+
|
|
698
796
|
if (toolCalls.length > 0) {
|
|
699
797
|
result.tool_calls = toolCalls;
|
|
700
798
|
}
|
|
@@ -26,7 +26,7 @@ export interface OpenAIResponsesProviderOptions {
|
|
|
26
26
|
streamTimeoutMs?: number;
|
|
27
27
|
useNativeWebSearch?: boolean;
|
|
28
28
|
/** When true, target the Codex subscription endpoint and strip fields it
|
|
29
|
-
* rejects (`max_output_tokens
|
|
29
|
+
* rejects (`max_output_tokens`). */
|
|
30
30
|
codexSubscription?: boolean;
|
|
31
31
|
}
|
|
32
32
|
|
|
@@ -74,6 +74,9 @@ interface ResponsesStreamEvent {
|
|
|
74
74
|
response?: {
|
|
75
75
|
model?: string;
|
|
76
76
|
status?: string;
|
|
77
|
+
incomplete_details?: {
|
|
78
|
+
reason?: "max_output_tokens" | "content_filter";
|
|
79
|
+
} | null;
|
|
77
80
|
/** Full output items array — preserved as part of rawResponse for the
|
|
78
81
|
* LLM context normalizer, which uses its presence to detect Responses
|
|
79
82
|
* API payloads in stored diagnostics. */
|
|
@@ -188,24 +191,22 @@ export class OpenAIResponsesProvider implements Provider {
|
|
|
188
191
|
params.max_output_tokens = maxTokens;
|
|
189
192
|
}
|
|
190
193
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
}
|
|
194
|
+
const reasoningEffort = effort
|
|
195
|
+
? EFFORT_TO_REASONING_EFFORT[effort]
|
|
196
|
+
: undefined;
|
|
197
|
+
if (reasoningEffort) {
|
|
198
|
+
params.reasoning = { effort: reasoningEffort };
|
|
199
|
+
}
|
|
198
200
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
}
|
|
201
|
+
if (
|
|
202
|
+
verbosity &&
|
|
203
|
+
VALID_VERBOSITIES.has(verbosity) &&
|
|
204
|
+
modelSupportsVerbosity(modelOverride ?? this.model)
|
|
205
|
+
) {
|
|
206
|
+
params.text = { verbosity };
|
|
206
207
|
}
|
|
207
208
|
|
|
208
|
-
if (tools && tools.length > 0
|
|
209
|
+
if (tools && tools.length > 0) {
|
|
209
210
|
if (
|
|
210
211
|
this.useNativeWebSearch &&
|
|
211
212
|
tools.some((t) => t.name === "web_search")
|
|
@@ -218,9 +219,9 @@ export class OpenAIResponsesProvider implements Provider {
|
|
|
218
219
|
parameters: t.input_schema,
|
|
219
220
|
strict: null,
|
|
220
221
|
}));
|
|
221
|
-
const webSearchTool =
|
|
222
|
-
type: "
|
|
223
|
-
|
|
222
|
+
const webSearchTool = this.codexSubscription
|
|
223
|
+
? { type: "web_search" as const, external_web_access: false }
|
|
224
|
+
: { type: "web_search_preview" as const };
|
|
224
225
|
params.tools = [...mappedOther, webSearchTool];
|
|
225
226
|
} else {
|
|
226
227
|
params.tools = tools.map((t) => ({
|
|
@@ -256,20 +257,26 @@ export class OpenAIResponsesProvider implements Provider {
|
|
|
256
257
|
let rawFinalResponse: unknown = undefined;
|
|
257
258
|
|
|
258
259
|
try {
|
|
259
|
-
//
|
|
260
|
-
// `
|
|
260
|
+
// Use `create()` with `stream: true` instead of the higher-level
|
|
261
|
+
// `stream()` helper. The `stream()` helper wraps the response in a
|
|
262
|
+
// `ResponseStream` that runs `maybeParseResponse()` after iteration,
|
|
263
|
+
// which crashes when the Codex subscription endpoint omits `output`
|
|
264
|
+
// from the `response.completed` event payload.
|
|
261
265
|
const responsesApi = this.client.responses as unknown as {
|
|
262
|
-
|
|
266
|
+
create(
|
|
263
267
|
p: Record<string, unknown>,
|
|
264
|
-
o
|
|
265
|
-
): AsyncIterable<ResponsesStreamEvent
|
|
268
|
+
o?: { signal?: AbortSignal; headers?: Record<string, string> },
|
|
269
|
+
): Promise<AsyncIterable<ResponsesStreamEvent>>;
|
|
266
270
|
};
|
|
267
|
-
const stream = responsesApi.
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
271
|
+
const stream = await responsesApi.create(
|
|
272
|
+
{ ...params, stream: true },
|
|
273
|
+
{
|
|
274
|
+
signal: timeoutSignal,
|
|
275
|
+
...(usageAttributionHeaders
|
|
276
|
+
? { headers: usageAttributionHeaders }
|
|
277
|
+
: {}),
|
|
278
|
+
},
|
|
279
|
+
);
|
|
273
280
|
|
|
274
281
|
for await (const event of stream) {
|
|
275
282
|
switch (event.type) {
|
|
@@ -341,7 +348,8 @@ export class OpenAIResponsesProvider implements Provider {
|
|
|
341
348
|
break;
|
|
342
349
|
}
|
|
343
350
|
|
|
344
|
-
case "response.completed":
|
|
351
|
+
case "response.completed":
|
|
352
|
+
case "response.incomplete": {
|
|
345
353
|
const response = event.response;
|
|
346
354
|
if (response) {
|
|
347
355
|
rawFinalResponse = response;
|
|
@@ -356,15 +364,22 @@ export class OpenAIResponsesProvider implements Provider {
|
|
|
356
364
|
cachedInputTokens =
|
|
357
365
|
response.usage.input_tokens_details?.cached_tokens ?? 0;
|
|
358
366
|
}
|
|
359
|
-
finishReason =
|
|
367
|
+
finishReason =
|
|
368
|
+
response.incomplete_details?.reason ??
|
|
369
|
+
response.status ??
|
|
370
|
+
(event.type === "response.incomplete"
|
|
371
|
+
? "incomplete"
|
|
372
|
+
: "completed");
|
|
360
373
|
}
|
|
361
374
|
// Emit server_tool_complete for any web search calls that were started.
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
375
|
+
if (event.type === "response.completed") {
|
|
376
|
+
for (const toolUseId of webSearchCallIds) {
|
|
377
|
+
onEvent?.({
|
|
378
|
+
type: "server_tool_complete",
|
|
379
|
+
toolUseId,
|
|
380
|
+
isError: false,
|
|
381
|
+
});
|
|
382
|
+
}
|
|
368
383
|
}
|
|
369
384
|
break;
|
|
370
385
|
}
|
|
@@ -438,21 +453,6 @@ export class OpenAIResponsesProvider implements Provider {
|
|
|
438
453
|
? signal.reason
|
|
439
454
|
: undefined;
|
|
440
455
|
if (error instanceof OpenAI.APIError) {
|
|
441
|
-
// Temporary diagnostic: log the raw error shape for Codex 400 debugging
|
|
442
|
-
if (this.codexSubscription) {
|
|
443
|
-
log.warn(
|
|
444
|
-
{
|
|
445
|
-
status: error.status,
|
|
446
|
-
message: error.message,
|
|
447
|
-
code: error.code,
|
|
448
|
-
type: error.type,
|
|
449
|
-
param: error.param,
|
|
450
|
-
errorBody: error.error,
|
|
451
|
-
headers: Object.fromEntries(error.headers?.entries?.() ?? []),
|
|
452
|
-
},
|
|
453
|
-
"Codex subscription API error — raw details",
|
|
454
|
-
);
|
|
455
|
-
}
|
|
456
456
|
const overflow = detectOpenAICompatibleContextOverflow(error);
|
|
457
457
|
if (overflow) {
|
|
458
458
|
throw new ContextOverflowError(
|
|
@@ -122,6 +122,7 @@ export class OpenRouterProvider extends OpenAIChatCompletionsProvider {
|
|
|
122
122
|
providerLabel: "OpenRouter",
|
|
123
123
|
streamTimeoutMs: options.streamTimeoutMs,
|
|
124
124
|
requestHeaders: OPENROUTER_APP_ATTRIBUTION_HEADERS,
|
|
125
|
+
assistantReasoningField: "reasoning",
|
|
125
126
|
});
|
|
126
127
|
this.openRouterApiKey = apiKey;
|
|
127
128
|
this.defaultModel = model;
|
|
@@ -199,17 +200,21 @@ export class OpenRouterProvider extends OpenAIChatCompletionsProvider {
|
|
|
199
200
|
? EFFORT_TO_REASONING_EFFORT[effort]
|
|
200
201
|
: undefined;
|
|
201
202
|
const summaryOverride = extractReasoningSummaryOverride(config);
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
this.resolveMaxReasoningEffort(this.resolveEffectiveModel(options)),
|
|
207
|
-
);
|
|
208
|
-
}
|
|
203
|
+
// Only send `reasoning` when explicitly enabling thinking. Omitting the
|
|
204
|
+
// field lets OpenRouter use the model's natural default, which avoids 400s
|
|
205
|
+
// from reasoning-only models (e.g. DeepSeek R1) that reject `enabled: false`.
|
|
206
|
+
const extras: Record<string, unknown> = {};
|
|
209
207
|
if (thinkingEnabled) {
|
|
208
|
+
const reasoning: Record<string, unknown> = { enabled: true };
|
|
209
|
+
if (mappedEffort) {
|
|
210
|
+
reasoning.effort = clampReasoningEffort(
|
|
211
|
+
mappedEffort,
|
|
212
|
+
this.resolveMaxReasoningEffort(this.resolveEffectiveModel(options)),
|
|
213
|
+
);
|
|
214
|
+
}
|
|
210
215
|
reasoning.summary = summaryOverride ?? "detailed";
|
|
216
|
+
extras.reasoning = reasoning;
|
|
211
217
|
}
|
|
212
|
-
const extras: Record<string, unknown> = { reasoning };
|
|
213
218
|
const only = extractOnlyList(config);
|
|
214
219
|
if (only.length > 0) {
|
|
215
220
|
const existingProvider = (config?.provider ?? {}) as Record<
|
|
@@ -9,7 +9,10 @@ import { getConfig } from "../config/loader.js";
|
|
|
9
9
|
import type { LLMCallSite } from "../config/schemas/llm.js";
|
|
10
10
|
import { getDb } from "../memory/db-connection.js";
|
|
11
11
|
import { getLogger } from "../util/logger.js";
|
|
12
|
-
import {
|
|
12
|
+
import {
|
|
13
|
+
describeSubscriptionModelIncompatibility,
|
|
14
|
+
isConnectionCompatibleWithModel,
|
|
15
|
+
} from "./connection-model-compat.js";
|
|
13
16
|
import { tryResolveProviderForConnectionName } from "./connection-resolution.js";
|
|
14
17
|
import { listConnections } from "./inference/connections.js";
|
|
15
18
|
import { initializeProviders, listProviders } from "./registry.js";
|
|
@@ -117,23 +120,29 @@ export async function resolveConfiguredProvider(
|
|
|
117
120
|
|
|
118
121
|
// Connection-aware path: every dispatch goes through `provider_connection`.
|
|
119
122
|
// The boot-time backfill ensures every profile has one in production.
|
|
120
|
-
// When unset (profile set provider
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
//
|
|
123
|
+
// When unset (profile set provider without a connection, test envs that
|
|
124
|
+
// skip backfill, freshly-installed configs not yet backfilled, or users
|
|
125
|
+
// who manually cleared the field), try to auto-resolve from the provider
|
|
126
|
+
// before falling back to null.
|
|
124
127
|
if (!connectionName) {
|
|
125
128
|
if (inferenceProvider) {
|
|
126
129
|
try {
|
|
127
130
|
const candidates = listConnections(getDb(), {
|
|
128
131
|
provider: inferenceProvider,
|
|
129
132
|
});
|
|
130
|
-
const active = candidates.find(
|
|
131
|
-
(c)
|
|
132
|
-
c.status === "active" &&
|
|
133
|
-
isConnectionCompatibleWithModel(c, resolved.model),
|
|
133
|
+
const active = candidates.find((c) =>
|
|
134
|
+
isConnectionCompatibleWithModel(c, resolved.model),
|
|
134
135
|
);
|
|
135
136
|
if (active) {
|
|
136
137
|
connectionName = active.name;
|
|
138
|
+
} else {
|
|
139
|
+
const incompatMsg = describeSubscriptionModelIncompatibility(
|
|
140
|
+
candidates,
|
|
141
|
+
resolved.model,
|
|
142
|
+
);
|
|
143
|
+
if (incompatMsg) {
|
|
144
|
+
log.warn({ callSite, inferenceProvider, model: resolved.model }, incompatMsg);
|
|
145
|
+
}
|
|
137
146
|
}
|
|
138
147
|
} catch {
|
|
139
148
|
// DB not available — fall through to the existing null-return path.
|