@vellumai/assistant 0.11.4 → 0.11.5-dev.202608220019.a778cf0
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 +6 -2
- package/ARCHITECTURE.md +29 -4
- package/README.md +1 -1
- package/docs/architecture/memory.md +17 -5
- package/docs/architecture/security.md +96 -152
- package/docs/guardian-request-flow.md +13 -2
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +118 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +103 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +118 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +103 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +166 -1
- package/node_modules/@vellumai/gateway-client/src/http-delivery.ts +7 -14
- package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +30 -32
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/ingress.test.ts +118 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +103 -0
- package/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/openapi.yaml +487 -11
- package/package.json +1 -1
- package/scripts/sync-web-search-catalog.ts +6 -0
- package/src/__tests__/anthropic-provider.test.ts +10 -3
- package/src/__tests__/approval-interception-trust-gates.test.ts +40 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
- package/src/__tests__/channel-approval-routes.test.ts +4 -0
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -6
- package/src/__tests__/channel-readiness-service.test.ts +126 -0
- package/src/__tests__/channel-reply-delivery.test.ts +30 -17
- package/src/__tests__/checker.test.ts +20 -314
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +6 -2
- package/src/__tests__/client-os-metadata-persistence.test.ts +16 -0
- package/src/__tests__/compactor-retained-image-validation.test.ts +142 -0
- package/src/__tests__/config-loader-backfill.test.ts +19 -10
- package/src/__tests__/container-cpu-sampler.test.ts +153 -0
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
- package/src/__tests__/conversation-attachments.test.ts +0 -1
- package/src/__tests__/conversation-delete-watch-timeline.test.ts +231 -0
- package/src/__tests__/conversation-error.test.ts +17 -0
- package/src/__tests__/conversation-lifecycle.test.ts +20 -26
- package/src/__tests__/conversation-pairing.test.ts +186 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +21 -2
- package/src/__tests__/credential-broker-server-use.test.ts +24 -5
- package/src/__tests__/credential-routes.test.ts +22 -3
- package/src/__tests__/credential-security-invariants.test.ts +2 -1
- package/src/__tests__/daemon-credential-client.test.ts +88 -0
- package/src/__tests__/default-plugin-names-guard.test.ts +12 -1
- package/src/__tests__/dm-backfill.test.ts +63 -0
- package/src/__tests__/dynamic-skill-background-guard.test.ts +0 -2
- package/src/__tests__/edit-propagation.test.ts +107 -4
- package/src/__tests__/events-client-registration.test.ts +28 -0
- package/src/__tests__/file-write-tool.test.ts +4 -2
- package/src/__tests__/guardian-prompt-notice-privacy.test.ts +133 -0
- package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +143 -97
- package/src/__tests__/helpers/gateway-classify-mock.ts +27 -6
- package/src/__tests__/host-proxy-interface.test.ts +11 -1
- package/src/__tests__/host-shell-tool.test.ts +23 -4
- package/src/__tests__/image-conversion.test.ts +143 -1
- package/src/__tests__/inline-skill-load-permissions.test.ts +47 -36
- package/src/__tests__/input-repairs.test.ts +207 -0
- package/src/__tests__/live-workspace-guard.test.ts +75 -0
- package/src/__tests__/managed-profile-guard.test.ts +4 -2
- package/src/__tests__/mcp-abort-signal.test.ts +1 -1
- package/src/__tests__/mcp-client-auth.test.ts +1 -1
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +1 -1
- package/src/__tests__/media-resolve-image-validation.test.ts +310 -0
- package/src/__tests__/mtime-cache.test.ts +2 -0
- package/src/__tests__/notification-vellum-adapter.test.ts +124 -1
- package/src/__tests__/openai-provider.test.ts +40 -0
- package/src/__tests__/openai-responses-provider.test.ts +18 -0
- package/src/__tests__/personal-memory-auth-bypass.test.ts +22 -6
- package/src/__tests__/platform-bash-auto-approve.test.ts +0 -4
- package/src/__tests__/platform.test.ts +16 -1
- package/src/__tests__/plugin-api-resolve-credential.test.ts +22 -13
- package/src/__tests__/plugin-api-shim.test.ts +5 -0
- package/src/__tests__/plugin-api-store-credential.test.ts +268 -0
- package/src/__tests__/plugin-config-data-migration.test.ts +8 -1
- package/src/__tests__/plugin-disabled-state.test.ts +2 -0
- package/src/__tests__/plugin-effective-enabled-set.test.ts +55 -3
- package/src/__tests__/plugin-execution-context.test.ts +73 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +0 -1
- package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +0 -1
- package/src/__tests__/reaction-persistence.test.ts +200 -7
- package/src/__tests__/require-fresh-approval.test.ts +0 -4
- package/src/__tests__/resource-pressure-guard.test.ts +428 -0
- package/src/__tests__/resource-pressure-routes.test.ts +113 -0
- package/src/__tests__/risk-classification-boundary-guard.test.ts +115 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +27 -1
- package/src/__tests__/sanitize-config-for-transfer.test.ts +28 -0
- package/src/__tests__/secret-routes-platform-proxy.test.ts +7 -0
- package/src/__tests__/settings-routes.test.ts +85 -3
- package/src/__tests__/skill-tool-factory.test.ts +161 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +132 -5
- package/src/__tests__/tool-executor.test.ts +63 -38
- package/src/__tests__/tool-policy.test.ts +97 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +7 -4
- package/src/__tests__/user-plugin-loader.test.ts +2 -0
- package/src/__tests__/validate-input.test.ts +243 -7
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -0
- package/src/__tests__/voice-session-bridge.test.ts +42 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +8 -0
- package/src/acp/__tests__/acp-claude-oauth.test.ts +97 -29
- package/src/acp/__tests__/prepare-agent-env.test.ts +376 -153
- package/src/acp/acp-claude-oauth.ts +38 -21
- package/src/acp/prepare-agent-env.ts +146 -55
- package/src/agent/loop-tool-dedup.test.ts +161 -0
- package/src/agent/loop.ts +68 -22
- package/src/api/constants/app-tools.ts +34 -0
- package/src/api/constants/profile-config-validation.ts +60 -0
- package/src/api/events/resource-pressure-status-changed.ts +53 -0
- package/src/api/events/tool-result.ts +6 -1
- package/src/api/events/watch-retro-completed.ts +52 -0
- package/src/api/index.ts +30 -0
- package/src/api/responses/resource-pressure-status.ts +25 -0
- package/src/approvals/guardian-channel-delivery.ts +70 -6
- package/src/approvals/guardian-request-resolvers.ts +32 -66
- package/src/calls/__tests__/voice-session-bridge.test.ts +114 -0
- package/src/calls/voice-session-bridge.ts +60 -6
- package/src/channels/__tests__/message-audience.test.ts +49 -0
- package/src/channels/__tests__/types.test.ts +17 -3
- package/src/channels/message-audience.ts +40 -0
- package/src/channels/types.ts +16 -12
- package/src/cli/__tests__/catalog-search-help.test.ts +50 -0
- package/src/cli/commands/__tests__/channel-verification-sessions.test.ts +31 -0
- package/src/cli/commands/__tests__/conversations-search.test.ts +289 -0
- package/src/cli/commands/__tests__/conversations-slack.test.ts +2 -1
- package/src/cli/commands/__tests__/inference-profiles.test.ts +16 -4
- package/src/cli/commands/__tests__/inference-providers.test.ts +135 -2
- package/src/cli/commands/__tests__/keys.test.ts +89 -8
- package/src/cli/commands/__tests__/plugins.test.ts +57 -1
- package/src/cli/commands/channel-verification-sessions.help.ts +11 -9
- package/src/cli/commands/channel-verification-sessions.ts +11 -21
- package/src/cli/commands/conversations.help.ts +34 -0
- package/src/cli/commands/conversations.ts +125 -0
- package/src/cli/commands/inference-profiles.ts +56 -3
- package/src/cli/commands/inference-providers.ts +37 -6
- package/src/cli/commands/inference.help.ts +9 -4
- package/src/cli/commands/keys.help.ts +13 -1
- package/src/cli/commands/keys.ts +26 -15
- package/src/cli/commands/memory/__tests__/memory-v2.test.ts +57 -7
- package/src/cli/commands/memory/index.help.ts +41 -27
- package/src/cli/commands/memory/index.ts +2 -0
- package/src/cli/commands/memory/memory-v2.ts +58 -54
- package/src/cli/commands/memory/memory-validate.ts +18 -0
- package/src/cli/commands/monitoring.ts +1 -0
- package/src/cli/commands/oauth/request.test.ts +93 -0
- package/src/cli/commands/oauth/request.ts +23 -9
- package/src/cli/commands/plugins.help.ts +28 -31
- package/src/cli/commands/plugins.ts +10 -0
- package/src/cli/commands/skills.help.ts +13 -16
- package/src/cli/commands/trust.ts +3 -14
- package/src/cli/lib/__tests__/install-from-github.test.ts +38 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +34 -0
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +32 -1
- package/src/cli/lib/__tests__/toggle-plugin.test.ts +2 -1
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +64 -0
- package/src/cli/lib/bundled-marketplace.json +26 -0
- package/src/cli/lib/daemon-credential-client.ts +25 -3
- package/src/cli/lib/install-from-github.ts +35 -24
- package/src/cli/lib/merge-plugin-tree.ts +22 -4
- package/src/cli/lib/open-browser.test.ts +67 -0
- package/src/cli/lib/open-browser.ts +24 -5
- package/src/cli/lib/plugin-surfaces.ts +27 -0
- package/src/config/__tests__/balanced-model-experiment.test.ts +7 -7
- package/src/config/__tests__/default-profile-catalog.test.ts +3 -3
- package/src/config/__tests__/profile-materialization.test.ts +26 -0
- package/src/config/default-profile-catalog.ts +5 -6
- package/src/config/feature-flag-registry.json +36 -28
- package/src/config/profile-materialization.ts +29 -0
- package/src/config/sanitize-for-transfer.ts +16 -0
- package/src/config/schemas/llm.ts +7 -0
- package/src/config/schemas/services.ts +6 -0
- package/src/config/webhook-routing.ts +8 -0
- package/src/context/compactor.ts +31 -2
- package/src/context/outbound-sanitize.ts +6 -0
- package/src/daemon/__tests__/lifecycle-watch-timeline-sweep.test.ts +98 -0
- package/src/daemon/__tests__/provider-rejection-log-fields.test.ts +272 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +4 -1
- package/src/daemon/conversation-error.ts +19 -2
- package/src/daemon/conversation-messaging.ts +22 -2
- package/src/daemon/conversation-process.ts +11 -0
- package/src/daemon/conversation-store.ts +10 -0
- package/src/daemon/conversation-surfaces.ts +21 -8
- package/src/daemon/conversation.ts +12 -10
- package/src/daemon/daemon-control.ts +1 -0
- package/src/daemon/handlers/__tests__/config-ingress-tunnel-records.test.ts +208 -0
- package/src/daemon/handlers/config-ingress.ts +115 -5
- package/src/daemon/lifecycle.ts +30 -2
- package/src/daemon/message-types/conversations.ts +5 -7
- package/src/daemon/message-types/web-activity.ts +3 -2
- package/src/daemon/provider-rejection-log-fields.ts +124 -0
- package/src/daemon/resource-pressure-guard-lifecycle.ts +66 -0
- package/src/daemon/resource-pressure-guard.ts +387 -0
- package/src/daemon/shutdown-handlers.ts +2 -0
- package/src/daemon/startup-error.ts +16 -0
- package/src/daemon/trust-context.ts +14 -10
- package/src/daemon/unsendable-image-notice.ts +76 -0
- package/src/hooks/registry.ts +65 -51
- package/src/inbound/__tests__/tunnel-probe.test.ts +448 -0
- package/src/inbound/tunnel-probe.ts +261 -0
- package/src/ipc/__tests__/socket-path.test.ts +15 -7
- package/src/ipc/gateway-client.test.ts +1 -1
- package/src/ipc/gateway-client.ts +14 -21
- package/src/ipc/socket-cleanup.ts +2 -11
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +1 -1
- package/src/messaging/provider-message-metadata.ts +128 -0
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +138 -73
- package/src/messaging/providers/channel-transport.ts +100 -16
- package/src/messaging/providers/discord/send.ts +16 -0
- package/src/messaging/providers/discord/transport.ts +23 -1
- package/src/messaging/providers/index.ts +84 -16
- package/src/messaging/providers/slack/message-metadata.ts +45 -0
- package/src/messaging/providers/slack/render-transcript.ts +1 -4
- package/src/messaging/providers/slack/send.test.ts +88 -34
- package/src/messaging/providers/slack/send.ts +99 -50
- package/src/messaging/providers/slack/transport.ts +35 -37
- package/src/messaging/providers/telegram-bot/send.test.ts +88 -18
- package/src/messaging/providers/telegram-bot/send.ts +36 -0
- package/src/messaging/providers/telegram-bot/transport.ts +22 -8
- package/src/messaging/read-provider-metadata.test.ts +157 -0
- package/src/messaging/read-provider-metadata.ts +51 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +94 -0
- package/src/notifications/__tests__/edit-notification.test.ts +174 -2
- package/src/notifications/__tests__/home-feed-side-effect.test.ts +536 -4
- package/src/notifications/adapters/macos.ts +55 -0
- package/src/notifications/adapters/slack.ts +10 -5
- package/src/notifications/assistant-reply-producer.ts +37 -4
- package/src/notifications/conversation-pairing.ts +111 -21
- package/src/notifications/edit-notification.ts +38 -8
- package/src/notifications/emit-signal.ts +12 -14
- package/src/notifications/home-feed-side-effect.ts +218 -15
- package/src/notifications/types.ts +5 -0
- package/src/permissions/AGENTS.md +16 -0
- package/src/permissions/checker.test.ts +75 -122
- package/src/permissions/checker.ts +44 -560
- package/src/permissions/confirmation-guardian-request.test.ts +28 -3
- package/src/permissions/confirmation-guardian-request.ts +5 -1
- package/src/persistence/conversation-crud.ts +69 -20
- package/src/persistence/conversation-queries.ts +14 -1
- package/src/persistence/conversation-types.ts +22 -0
- package/src/persistence/db-async-query.ts +2 -1
- package/src/persistence/db-maintenance.ts +15 -0
- package/src/persistence/delivery-crud.ts +122 -15
- package/src/persistence/embeddings/qdrant-manager.ts +1 -0
- package/src/persistence/migrations/367-create-watch-timeline-entries.ts +46 -0
- package/src/persistence/migrations/368-watch-timeline-screenshot-blob.ts +33 -0
- package/src/persistence/migrations/__tests__/367-create-watch-timeline-entries.test.ts +98 -0
- package/src/persistence/migrations/__tests__/368-watch-timeline-screenshot-blob.test.ts +98 -0
- package/src/persistence/schema/index.ts +1 -0
- package/src/persistence/schema/watch.ts +29 -0
- package/src/persistence/steps.ts +4 -0
- package/src/plugin-api/__tests__/import-graph-partial-mock.test.ts +67 -0
- package/src/plugin-api/conversation-turn.ts +27 -0
- package/src/plugin-api/credential-scope.test.ts +15 -0
- package/src/plugin-api/credential-scope.ts +14 -0
- package/src/plugin-api/index.ts +19 -1
- package/src/plugin-api/resolve-credential.ts +15 -10
- package/src/plugin-api/store-credential.ts +148 -0
- package/src/plugin-api/system-card.ts +36 -0
- package/src/plugin-api/vision-support.test.ts +82 -0
- package/src/plugin-api/vision-support.ts +14 -2
- package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +361 -110
- package/src/plugins/defaults/image-fallback/__tests__/vision-recovery.test.ts +4 -0
- package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +58 -0
- package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +64 -23
- package/src/plugins/defaults/image-recovery/detect.ts +24 -1
- package/src/plugins/defaults/main.ts +15 -0
- package/src/plugins/defaults/memory/AGENTS.md +11 -3
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +0 -1
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +9 -12
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +1 -1
- package/src/plugins/defaults/memory/src/memory-v2-routes.ts +12 -10
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +114 -0
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +8 -0
- package/src/plugins/defaults/memory/substrate/__tests__/edge-index.test.ts +0 -30
- package/src/plugins/defaults/memory/substrate/__tests__/ingest.test.ts +73 -1
- package/src/plugins/defaults/memory/substrate/__tests__/page-index.test.ts +37 -0
- package/src/plugins/defaults/memory/substrate/__tests__/page-links.test.ts +208 -0
- package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +158 -1
- package/src/plugins/defaults/memory/substrate/__tests__/static-context.test.ts +1 -61
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +68 -6
- package/src/plugins/defaults/memory/substrate/edge-index.ts +0 -31
- package/src/plugins/defaults/memory/substrate/ingest.ts +59 -0
- package/src/plugins/defaults/memory/substrate/page-index.ts +35 -8
- package/src/plugins/defaults/memory/substrate/page-links.ts +133 -0
- package/src/plugins/defaults/memory/substrate/page-store.ts +10 -0
- package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +124 -34
- package/src/plugins/defaults/memory/substrate/static-context.ts +0 -29
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +3 -1
- package/src/plugins/defaults/memory/v3/card.ts +9 -9
- package/src/plugins/defaults/memory/v3/core-set.test.ts +7 -0
- package/src/plugins/defaults/memory/v3/core-set.ts +5 -1
- package/src/plugins/defaults/memory/v3/edge.ts +13 -57
- package/src/plugins/mtime-cache.ts +1 -1
- package/src/plugins/pipeline.ts +14 -7
- package/src/plugins/plugin-execution-context.ts +35 -11
- package/src/plugins/plugin-tree-walk.ts +63 -4
- package/src/providers/__tests__/retry-network-error.test.ts +84 -0
- package/src/providers/anthropic/__tests__/pause-turn-continuation.test.ts +170 -0
- package/src/providers/anthropic/client.ts +332 -241
- package/src/providers/connection-resolution.ts +25 -2
- package/src/providers/content-blocks.ts +22 -0
- package/src/providers/fetch-provider-catalog.ts +19 -0
- package/src/providers/gemini/client.ts +5 -2
- package/src/providers/inference/__tests__/adapter-factory-litellm.test.ts +47 -1
- package/src/providers/inference/__tests__/adapter-factory-ollama.test.ts +83 -0
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +98 -1
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +38 -0
- package/src/providers/inference/__tests__/base-url-security.test.ts +10 -0
- package/src/providers/inference/__tests__/connections-ollama.test.ts +90 -0
- package/src/providers/inference/__tests__/endpoint-probe.test.ts +92 -0
- package/src/providers/inference/__tests__/missing-credential-guard.test.ts +117 -0
- package/src/providers/inference/__tests__/profile-config-validation.test.ts +39 -0
- package/src/providers/inference/__tests__/profile-probe-classify.test.ts +66 -0
- package/src/providers/inference/adapter-factory.ts +33 -2
- package/src/providers/inference/auth.ts +13 -2
- package/src/providers/inference/credential-usage.ts +37 -0
- package/src/providers/inference/endpoint-probe.ts +115 -0
- package/src/providers/inference/missing-credential-guard.ts +110 -0
- package/src/providers/inference/profile-probe.ts +256 -0
- package/src/providers/inference/resolve-auth.ts +7 -7
- package/src/providers/media-resolve.ts +176 -15
- package/src/providers/model-catalog.ts +10 -0
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +17 -1
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +576 -46
- package/src/providers/openai/__tests__/connection-error-wrap.test.ts +44 -0
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +125 -2
- package/src/providers/openai/api-error-normalization.ts +16 -2
- package/src/providers/openai/chat-completions-provider.ts +326 -44
- package/src/providers/openai/responses-provider.ts +5 -2
- package/src/providers/provider-send-message.ts +11 -0
- package/src/providers/retry.ts +6 -0
- package/src/providers/search-provider-catalog.ts +20 -0
- package/src/routes/route-host-protocol.ts +7 -0
- package/src/routes/worker.ts +31 -10
- package/src/runtime/AGENTS.md +36 -0
- package/src/runtime/__tests__/host-observe.test.ts +302 -0
- package/src/runtime/__tests__/runtime-http-port-collision.test.ts +131 -0
- package/src/runtime/__tests__/web-presence.test.ts +234 -0
- package/src/runtime/assistant-event-hub.ts +38 -0
- package/src/runtime/channel-readiness-service.ts +39 -13
- package/src/runtime/channel-readiness-types.ts +49 -2
- package/src/runtime/channel-reply-delivery.ts +55 -32
- package/src/runtime/channel-retry-sweep.ts +6 -4
- package/src/runtime/effective-capabilities.test.ts +19 -0
- package/src/runtime/effective-capabilities.ts +25 -0
- package/src/runtime/guardian-reply-router.ts +6 -2
- package/src/runtime/host-observe.ts +246 -0
- package/src/runtime/http-errors.ts +1 -0
- package/src/runtime/http-server.ts +185 -6
- package/src/runtime/routes/__tests__/client-routes.test.ts +150 -0
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +133 -0
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +80 -0
- package/src/runtime/routes/__tests__/credential-delete-in-use.test.ts +155 -0
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +118 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +72 -1
- package/src/runtime/routes/__tests__/ingress-status-routes.test.ts +508 -0
- package/src/runtime/routes/__tests__/monitoring-routes.test.ts +1 -0
- package/src/runtime/routes/__tests__/pending-interactions-route.test.ts +175 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +103 -71
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +55 -6
- package/src/runtime/routes/__tests__/system-card-turn-termination.test.ts +107 -0
- package/src/runtime/routes/__tests__/user-route-dispatcher-host.test.ts +52 -1
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +75 -4
- package/src/runtime/routes/__tests__/watch-routes-guardian-cache.test.ts +139 -0
- package/src/runtime/routes/__tests__/watch-routes.test.ts +598 -0
- package/src/runtime/routes/approval-routes.ts +37 -4
- package/src/runtime/routes/approval-strategies/guardian-text-engine-strategy.ts +16 -22
- package/src/runtime/routes/canned-message-complete.ts +68 -29
- package/src/runtime/routes/channel-readiness-routes.ts +14 -2
- package/src/runtime/routes/client-routes.ts +113 -0
- package/src/runtime/routes/conversation-list-routes.ts +22 -7
- package/src/runtime/routes/conversation-management-routes.ts +15 -9
- package/src/runtime/routes/conversation-query-routes.ts +10 -0
- package/src/runtime/routes/conversation-routes.ts +36 -22
- package/src/runtime/routes/credential-in-use.ts +85 -0
- package/src/runtime/routes/credential-routes.ts +55 -85
- package/src/runtime/routes/guardian-approval-interception.ts +44 -53
- package/src/runtime/routes/guardian-approval-reply-helpers.ts +14 -12
- package/src/runtime/routes/host-cu-routes.ts +18 -0
- package/src/runtime/routes/identity-routes.ts +5 -121
- package/src/runtime/routes/inbound-message-handler.ts +74 -30
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +16 -17
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +182 -297
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +173 -386
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +101 -77
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +17 -8
- package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +91 -7
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +72 -70
- package/src/runtime/routes/index.ts +4 -0
- package/src/runtime/routes/inference-profiles-routes.ts +232 -31
- package/src/runtime/routes/inference-provider-connection-routes.ts +33 -11
- package/src/runtime/routes/ingress-status-routes.ts +180 -0
- package/src/runtime/routes/monitoring-routes.ts +9 -1
- package/src/runtime/routes/oauth-commands-routes.ts +1 -1
- package/src/runtime/routes/plugins-routes.ts +28 -32
- package/src/runtime/routes/resource-pressure-routes.ts +22 -0
- package/src/runtime/routes/secret-routes.ts +39 -8
- package/src/runtime/routes/settings-routes.ts +15 -12
- package/src/runtime/routes/surface-action-routes.ts +20 -19
- package/src/runtime/routes/user-route-dispatcher.ts +31 -2
- package/src/runtime/routes/user-route-resolution.ts +18 -1
- package/src/runtime/routes/watch-routes.ts +807 -0
- package/src/runtime/slack-reply-session.test.ts +23 -13
- package/src/runtime/slack-reply-session.ts +38 -55
- package/src/runtime/web-presence.ts +90 -0
- package/src/security/__tests__/untrusted-content.test.ts +42 -0
- package/src/security/untrusted-content.ts +28 -9
- package/src/skills/validate-input.ts +177 -40
- package/src/subagent/__tests__/consult-context-gating.test.ts +6 -10
- package/src/subagent/consult-context.ts +7 -23
- package/src/tools/credentials/broker.ts +24 -79
- package/src/tools/credentials/ref-parse.ts +35 -0
- package/src/tools/credentials/resolve.ts +4 -10
- package/src/tools/credentials/store.ts +168 -0
- package/src/tools/credentials/tool-policy.ts +67 -0
- package/src/tools/executor.ts +53 -15
- package/src/tools/network/__tests__/firecrawl-compat.test.ts +77 -0
- package/src/tools/network/__tests__/web-fetch-fastcrw.test.ts +169 -0
- package/src/tools/network/__tests__/web-search.test.ts +138 -3
- package/src/tools/network/firecrawl-compat.ts +90 -0
- package/src/tools/network/url-safety.ts +9 -16
- package/src/tools/network/web-fetch.ts +142 -62
- package/src/tools/network/web-search.ts +162 -58
- package/src/tools/permission-checker.ts +43 -52
- package/src/tools/schema-transforms.ts +40 -5
- package/src/tools/shared/input-repairs.ts +160 -0
- package/src/tools/skills/skill-tool-factory.ts +18 -4
- package/src/tools/subagent/spawn.ts +0 -1
- package/src/tools/tool-approval-handler.ts +16 -10
- package/src/tools/tool-types.ts +26 -13
- package/src/tools/types.ts +6 -6
- package/src/util/__tests__/cgroup-memory.test.ts +3 -0
- package/src/util/cgroup-memory.ts +3 -0
- package/src/util/container-cpu-sampler.ts +250 -0
- package/src/util/image-conversion.ts +178 -14
- package/src/util/platform.ts +93 -10
- package/src/util/worker-process.ts +1 -0
- package/src/watch/__tests__/watch-retro.test.ts +665 -0
- package/src/watch/__tests__/watch-session-manager.test.ts +566 -0
- package/src/watch/__tests__/watch-timeline.test.ts +670 -0
- package/src/watch/watch-retro.ts +480 -0
- package/src/watch/watch-session-manager.ts +575 -0
- package/src/watch/watch-timeline.ts +848 -0
- package/src/permissions/ipc-risk-types.ts +0 -143
- package/src/permissions/risk-types.ts +0 -76
|
@@ -91,6 +91,7 @@ import { ROUTES as INFERENCE_PROFILE_SESSION_ROUTES } from "./inference-profile-
|
|
|
91
91
|
import { ROUTES as INFERENCE_PROFILES_ROUTES } from "./inference-profiles-routes.js";
|
|
92
92
|
import { ROUTES as INFERENCE_PROVIDER_CONNECTION_ROUTES } from "./inference-provider-connection-routes.js";
|
|
93
93
|
import { ROUTES as INFERENCE_SEND_ROUTES } from "./inference-send-routes.js";
|
|
94
|
+
import { ROUTES as INGRESS_STATUS_ROUTES } from "./ingress-status-routes.js";
|
|
94
95
|
import { ROUTES as A2A_ROUTES } from "./integrations/a2a.js";
|
|
95
96
|
import { ROUTES as SLACK_CHANNEL_CONFIG_ROUTES } from "./integrations/slack/channel.js";
|
|
96
97
|
import { ROUTES as SLACK_CHANNELS_ROUTES } from "./integrations/slack/channels.js";
|
|
@@ -127,6 +128,7 @@ import { ROUTES as PUBLISH_ROUTES } from "./publish-routes.js";
|
|
|
127
128
|
import { ROUTES as QUESTION_ROUTES } from "./question-routes.js";
|
|
128
129
|
import { ROUTES as RECORDING_ROUTES } from "./recording-routes.js";
|
|
129
130
|
import { ROUTES as RENAME_CONVERSATION_ROUTES } from "./rename-conversation-routes.js";
|
|
131
|
+
import { ROUTES as RESOURCE_PRESSURE_ROUTES } from "./resource-pressure-routes.js";
|
|
130
132
|
import { ROUTES as RETROSPECTIVE_ROUTES } from "./retrospective-routes.js";
|
|
131
133
|
import { ROUTES as ROUTE_HOST_WORKER_ROUTES } from "./route-host-worker-routes.js";
|
|
132
134
|
import { ROUTES as SANITY_ROUTES } from "./sanity-routes.js";
|
|
@@ -210,6 +212,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
210
212
|
...DEFAULT_PROVIDER_ROUTES,
|
|
211
213
|
...DIAGNOSTICS_ROUTES,
|
|
212
214
|
...DISK_PRESSURE_ROUTES,
|
|
215
|
+
...RESOURCE_PRESSURE_ROUTES,
|
|
213
216
|
...DOMAIN_ROUTES,
|
|
214
217
|
...DOCUMENT_COMMENT_ROUTES,
|
|
215
218
|
...DOCUMENT_ROUTES,
|
|
@@ -238,6 +241,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
238
241
|
...INFERENCE_PROFILES_ROUTES,
|
|
239
242
|
...INFERENCE_PROVIDER_CONNECTION_ROUTES,
|
|
240
243
|
...INFERENCE_SEND_ROUTES,
|
|
244
|
+
...INGRESS_STATUS_ROUTES,
|
|
241
245
|
...INTERNAL_OAUTH_ROUTES,
|
|
242
246
|
...INTERNAL_TELEMETRY_ROUTES,
|
|
243
247
|
...MCP_AUTH_ROUTES,
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
import { z } from "zod";
|
|
20
20
|
|
|
21
|
+
import { validateInferenceProfileConfig } from "../../api/constants/profile-config-validation.js";
|
|
21
22
|
import {
|
|
22
23
|
getEffectiveProfilesForProvider,
|
|
23
24
|
MANAGED_PROFILE_NAMES,
|
|
@@ -34,6 +35,7 @@ import {
|
|
|
34
35
|
} from "../../config/schemas/llm.js";
|
|
35
36
|
import { getDb } from "../../persistence/db-connection.js";
|
|
36
37
|
import {
|
|
38
|
+
catalogProviderForProfile,
|
|
37
39
|
resolveEntryProviderKind,
|
|
38
40
|
writableProfileProviderIssue,
|
|
39
41
|
} from "../../providers/connection-resolution.js";
|
|
@@ -45,7 +47,10 @@ import {
|
|
|
45
47
|
isUnavailable,
|
|
46
48
|
} from "../../providers/inference/connection-availability.js";
|
|
47
49
|
import { getConnection } from "../../providers/inference/connections.js";
|
|
50
|
+
import { probeInferenceProfile } from "../../providers/inference/profile-probe.js";
|
|
48
51
|
import {
|
|
52
|
+
catalogContextWindowTokens,
|
|
53
|
+
catalogMaxOutputTokens,
|
|
49
54
|
getModelDisplayName,
|
|
50
55
|
isModelInCatalog,
|
|
51
56
|
} from "../../providers/model-catalog.js";
|
|
@@ -82,6 +87,18 @@ const availabilitySchema = z
|
|
|
82
87
|
})
|
|
83
88
|
.meta({ id: "ProfileConnectionAvailability" });
|
|
84
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Static config problem with the stored entry itself (unknown model,
|
|
92
|
+
* impossible token budget), distinct from `availability` which judges the
|
|
93
|
+
* connection and credential behind it. Absent when the config checks out.
|
|
94
|
+
*/
|
|
95
|
+
const profileConfigIssueSchema = z
|
|
96
|
+
.object({
|
|
97
|
+
code: z.enum(["model_unknown", "over_output_cap", "no_input_room"]),
|
|
98
|
+
message: z.string(),
|
|
99
|
+
})
|
|
100
|
+
.meta({ id: "InferenceProfileConfigIssue" });
|
|
101
|
+
|
|
85
102
|
const profileSummarySchema = z
|
|
86
103
|
.object({
|
|
87
104
|
name: z.string(),
|
|
@@ -93,6 +110,7 @@ const profileSummarySchema = z
|
|
|
93
110
|
provider_connection: z.string().optional(),
|
|
94
111
|
/** Null when the profile has no provider to judge (e.g. mix profiles). */
|
|
95
112
|
availability: availabilitySchema.nullable(),
|
|
113
|
+
config_issue: profileConfigIssueSchema.optional(),
|
|
96
114
|
})
|
|
97
115
|
.meta({ id: "InferenceProfileSummary" });
|
|
98
116
|
|
|
@@ -101,6 +119,7 @@ const profileDetailSchema = z
|
|
|
101
119
|
name: z.string(),
|
|
102
120
|
entry: z.record(z.string(), z.unknown()),
|
|
103
121
|
availability: availabilitySchema.nullable(),
|
|
122
|
+
config_issue: profileConfigIssueSchema.optional(),
|
|
104
123
|
})
|
|
105
124
|
.meta({ id: "InferenceProfileDetail" });
|
|
106
125
|
|
|
@@ -119,6 +138,17 @@ const profileWriteResultSchema = z
|
|
|
119
138
|
})
|
|
120
139
|
.meta({ id: "InferenceProfileWriteResult" });
|
|
121
140
|
|
|
141
|
+
const profileCheckSchema = z
|
|
142
|
+
.object({
|
|
143
|
+
ok: z.boolean(),
|
|
144
|
+
blame: z.enum(["profile", "provider", "transient", "unknown"]).optional(),
|
|
145
|
+
reason: z.string().optional(),
|
|
146
|
+
detail: z.string().optional(),
|
|
147
|
+
connection: z.string().optional(),
|
|
148
|
+
message: z.string().optional(),
|
|
149
|
+
})
|
|
150
|
+
.meta({ id: "InferenceProfileCheck" });
|
|
151
|
+
|
|
122
152
|
const createRequestSchema = z.object({
|
|
123
153
|
name: z.string().min(1),
|
|
124
154
|
provider: z.string().min(1),
|
|
@@ -160,63 +190,147 @@ function assertValidProvider(provider: string): void {
|
|
|
160
190
|
}
|
|
161
191
|
|
|
162
192
|
/**
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
193
|
+
* Why a (provider, model, connection) triple cannot be vouched for, or null.
|
|
194
|
+
* The same reach checks dispatch applies: routing identities against their
|
|
195
|
+
* routing table, entry names against their row's dispatchable kind, vendor
|
|
196
|
+
* ids against the catalog, with the named connection's advertised model list
|
|
197
|
+
* authoritative for models the code-owned catalog doesn't know (a custom
|
|
198
|
+
* endpoint declares its own models at connection-create time).
|
|
166
199
|
*/
|
|
167
|
-
function
|
|
200
|
+
function modelReachIssue(
|
|
168
201
|
provider: string,
|
|
169
202
|
model: string,
|
|
170
|
-
allowUnlisted: boolean,
|
|
171
203
|
connectionName?: string,
|
|
172
|
-
): string
|
|
173
|
-
// Routing identities key no catalog entries; they validate against their
|
|
174
|
-
// route's actual reach — the same checks dispatch applies per-request.
|
|
175
|
-
// allowUnlisted deliberately does not apply: the routing table ships in
|
|
176
|
-
// this build, so an unroutable pair fails every request, and the schema
|
|
177
|
-
// strips it on the next config read.
|
|
204
|
+
): { identity: boolean; catalogProvider: string; message: string } | null {
|
|
178
205
|
if (ROUTING_IDENTITY_PROVIDERS.has(provider)) {
|
|
179
206
|
const issue = routingIdentityModelIssue(provider, model);
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
);
|
|
184
|
-
}
|
|
185
|
-
return [];
|
|
207
|
+
return issue
|
|
208
|
+
? { identity: true, catalogProvider: provider, message: issue }
|
|
209
|
+
: null;
|
|
186
210
|
}
|
|
187
|
-
// An entry-name provider validates against its row's dispatchable kind,
|
|
188
|
-
// the same translation dispatch uses; the entry itself also serves as the
|
|
189
|
-
// model-list connection for custom endpoints below.
|
|
190
211
|
const entryKind = resolveEntryProviderKind(provider, model);
|
|
191
212
|
const catalogProvider = entryKind ?? provider;
|
|
192
213
|
if (isModelInCatalog(catalogProvider, model)) {
|
|
193
|
-
return
|
|
214
|
+
return null;
|
|
194
215
|
}
|
|
195
|
-
// The named connection's advertised model list is authoritative for models
|
|
196
|
-
// the code-owned catalog doesn't know — a custom (openai-compatible)
|
|
197
|
-
// endpoint declares its own models at connection-create time.
|
|
198
216
|
const modelListConnection =
|
|
199
217
|
connectionName ?? (entryKind !== null ? provider : undefined);
|
|
200
218
|
if (modelListConnection) {
|
|
201
219
|
const connection = getConnection(getDb(), modelListConnection);
|
|
202
220
|
if (connection?.models?.some((m) => m.id === model)) {
|
|
203
|
-
return
|
|
221
|
+
return null;
|
|
204
222
|
}
|
|
205
223
|
}
|
|
224
|
+
return {
|
|
225
|
+
identity: false,
|
|
226
|
+
catalogProvider,
|
|
227
|
+
message: `Model "${model}" is not in the catalog for provider "${catalogProvider}".`,
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Validate a (provider, model) pair against the catalog. Returns warnings
|
|
233
|
+
* (never throws) when `allowUnlisted`; throws otherwise for an uncataloged
|
|
234
|
+
* model. An uncataloged model always warns, whether or not it is allowed.
|
|
235
|
+
*/
|
|
236
|
+
function validateModel(
|
|
237
|
+
provider: string,
|
|
238
|
+
model: string,
|
|
239
|
+
allowUnlisted: boolean,
|
|
240
|
+
connectionName?: string,
|
|
241
|
+
): string[] {
|
|
242
|
+
const issue = modelReachIssue(provider, model, connectionName);
|
|
243
|
+
if (!issue) {
|
|
244
|
+
return [];
|
|
245
|
+
}
|
|
246
|
+
// allowUnlisted deliberately does not apply to routing identities: the
|
|
247
|
+
// routing table ships in this build, so an unroutable pair fails every
|
|
248
|
+
// request, and the schema strips it on the next config read.
|
|
249
|
+
if (issue.identity) {
|
|
250
|
+
throw new BadRequestError(
|
|
251
|
+
`${issue.message} Pick a model this route serves, or a concrete provider.`,
|
|
252
|
+
);
|
|
253
|
+
}
|
|
206
254
|
if (!allowUnlisted) {
|
|
207
255
|
const remedy =
|
|
208
|
-
catalogProvider === "openai-compatible"
|
|
256
|
+
issue.catalogProvider === "openai-compatible"
|
|
209
257
|
? `Pass allowUnlisted to create it anyway, or declare the model on the connection ` +
|
|
210
258
|
`("assistant inference providers update <name> --model ${model}").`
|
|
211
259
|
: `Pass allowUnlisted to create it anyway, or run ` +
|
|
212
260
|
`"assistant inference models list --provider ${provider}" to see valid ids.`;
|
|
213
|
-
throw new BadRequestError(
|
|
214
|
-
|
|
261
|
+
throw new BadRequestError(`${issue.message} ${remedy}`);
|
|
262
|
+
}
|
|
263
|
+
return [`${issue.message} Created anyway (allowUnlisted).`];
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Static config verdict for a stored profile: the model-reach and
|
|
268
|
+
* token-budget judgments the write routes enforce, recomputed over the
|
|
269
|
+
* entry so rows that predate validation (or were written through the
|
|
270
|
+
* generic config escape hatch) surface their problem in listings. Cheap
|
|
271
|
+
* and offline; the live probe covers what only a request can prove. Mix
|
|
272
|
+
* arms are judged on their own rows, and managed bodies are code-owned.
|
|
273
|
+
*/
|
|
274
|
+
function profileConfigIssue(record: Record<string, unknown>): {
|
|
275
|
+
code: "model_unknown" | "over_output_cap" | "no_input_room";
|
|
276
|
+
message: string;
|
|
277
|
+
} | null {
|
|
278
|
+
if (record.mix != null || record.source === "managed") {
|
|
279
|
+
return null;
|
|
280
|
+
}
|
|
281
|
+
const provider =
|
|
282
|
+
typeof record.provider === "string" ? record.provider : undefined;
|
|
283
|
+
const model = typeof record.model === "string" ? record.model : undefined;
|
|
284
|
+
if (!provider || !model) {
|
|
285
|
+
// A missing provider/model is availability's `incomplete` verdict.
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
// A stored allowUnlisted marker records that catalog absence was accepted
|
|
289
|
+
// deliberately at write time; the live probe is the check for those.
|
|
290
|
+
if (record.allowUnlisted !== true) {
|
|
291
|
+
const reach = modelReachIssue(
|
|
292
|
+
provider,
|
|
293
|
+
model,
|
|
294
|
+
typeof record.provider_connection === "string"
|
|
295
|
+
? record.provider_connection
|
|
296
|
+
: undefined,
|
|
215
297
|
);
|
|
298
|
+
if (reach) {
|
|
299
|
+
return { code: "model_unknown", message: reach.message };
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
if (typeof record.maxTokens === "number") {
|
|
303
|
+
const budget = maxTokensBudgetIssue(provider, model, record.maxTokens);
|
|
304
|
+
if (budget) {
|
|
305
|
+
return { code: budget.code, message: budget.message };
|
|
306
|
+
}
|
|
216
307
|
}
|
|
217
|
-
return
|
|
218
|
-
|
|
219
|
-
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* The shared token-budget judgment over a (provider, model, maxTokens)
|
|
313
|
+
* triple with the catalog-provider translation applied. Null when the budget
|
|
314
|
+
* passes or the catalog knows nothing to judge against. One implementation
|
|
315
|
+
* for the write routes and the listing verdict so the two cannot drift.
|
|
316
|
+
*/
|
|
317
|
+
function maxTokensBudgetIssue(
|
|
318
|
+
provider: string,
|
|
319
|
+
model: string,
|
|
320
|
+
maxTokens: number,
|
|
321
|
+
): ReturnType<typeof validateInferenceProfileConfig> {
|
|
322
|
+
const catalogProvider = catalogProviderForProfile(provider, model);
|
|
323
|
+
if (catalogProvider === null) {
|
|
324
|
+
return null;
|
|
325
|
+
}
|
|
326
|
+
return validateInferenceProfileConfig({
|
|
327
|
+
maxTokens,
|
|
328
|
+
modelMaxOutputTokens: catalogMaxOutputTokens(catalogProvider, model),
|
|
329
|
+
modelContextWindowTokens: catalogContextWindowTokens(
|
|
330
|
+
catalogProvider,
|
|
331
|
+
model,
|
|
332
|
+
),
|
|
333
|
+
});
|
|
220
334
|
}
|
|
221
335
|
|
|
222
336
|
function assertConnectionExists(name: string): void {
|
|
@@ -373,6 +487,27 @@ function validateProfileEntry(entry: Record<string, unknown>): void {
|
|
|
373
487
|
}
|
|
374
488
|
}
|
|
375
489
|
|
|
490
|
+
/**
|
|
491
|
+
* Reject an explicit `maxTokens` the catalog can prove impossible for the
|
|
492
|
+
* model (over its output cap, or reserving the whole context window so no
|
|
493
|
+
* input fits). Judged against the same catalog-provider translation
|
|
494
|
+
* `validateModel` uses; models the catalog does not know are left to the
|
|
495
|
+
* live probe.
|
|
496
|
+
*/
|
|
497
|
+
function assertSaneMaxTokens(
|
|
498
|
+
provider: string,
|
|
499
|
+
model: string,
|
|
500
|
+
maxTokens: number | undefined,
|
|
501
|
+
): void {
|
|
502
|
+
if (maxTokens === undefined) {
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
const issue = maxTokensBudgetIssue(provider, model, maxTokens);
|
|
506
|
+
if (issue) {
|
|
507
|
+
throw new BadRequestError(issue.message);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
376
511
|
// ---------------------------------------------------------------------------
|
|
377
512
|
// Handlers
|
|
378
513
|
// ---------------------------------------------------------------------------
|
|
@@ -386,6 +521,7 @@ async function handleListProfiles() {
|
|
|
386
521
|
const profiles = await Promise.all(
|
|
387
522
|
Object.entries(effective).map(async ([name, entry]) => {
|
|
388
523
|
const record = entry as Record<string, unknown>;
|
|
524
|
+
const configIssue = profileConfigIssue(record);
|
|
389
525
|
return {
|
|
390
526
|
name,
|
|
391
527
|
label: typeof record.label === "string" ? record.label : null,
|
|
@@ -397,6 +533,7 @@ async function handleListProfiles() {
|
|
|
397
533
|
? { provider_connection: record.provider_connection }
|
|
398
534
|
: {}),
|
|
399
535
|
availability: await computeProfileAvailability(record),
|
|
536
|
+
...(configIssue ? { config_issue: configIssue } : {}),
|
|
400
537
|
};
|
|
401
538
|
}),
|
|
402
539
|
);
|
|
@@ -418,10 +555,12 @@ async function handleGetProfile({ pathParams = {} }: RouteHandlerArgs) {
|
|
|
418
555
|
throw new NotFoundError(`Profile "${name}" not found.`);
|
|
419
556
|
}
|
|
420
557
|
const record = entry as Record<string, unknown>;
|
|
558
|
+
const configIssue = profileConfigIssue(record);
|
|
421
559
|
return {
|
|
422
560
|
name,
|
|
423
561
|
entry: record,
|
|
424
562
|
availability: await computeProfileAvailability(record),
|
|
563
|
+
...(configIssue ? { config_issue: configIssue } : {}),
|
|
425
564
|
};
|
|
426
565
|
}
|
|
427
566
|
|
|
@@ -456,12 +595,19 @@ async function handleCreateProfile({ body = {} }: RouteHandlerArgs) {
|
|
|
456
595
|
input.allowUnlisted ?? false,
|
|
457
596
|
input.connection,
|
|
458
597
|
);
|
|
598
|
+
assertSaneMaxTokens(input.provider, input.model, input.maxTokens);
|
|
459
599
|
|
|
460
600
|
const entry: Record<string, unknown> = {
|
|
461
601
|
...fragmentFromBody(body as Record<string, unknown>),
|
|
462
602
|
provider: input.provider,
|
|
463
603
|
model: input.model,
|
|
464
604
|
source: "user",
|
|
605
|
+
// `warnings` here can only be validateModel's unlisted verdict (the
|
|
606
|
+
// availability warning pushes later), so its presence is exactly the
|
|
607
|
+
// deliberate allowUnlisted acceptance the listing verdict must honor.
|
|
608
|
+
...(input.allowUnlisted && warnings.length > 0
|
|
609
|
+
? { allowUnlisted: true }
|
|
610
|
+
: {}),
|
|
465
611
|
};
|
|
466
612
|
// Pickers render the label, so a label-less profile shows its raw config
|
|
467
613
|
// key (e.g. "gemini-latest"). Default it to the model's human-readable
|
|
@@ -585,6 +731,24 @@ async function handleUpdateProfile({
|
|
|
585
731
|
nextConnection,
|
|
586
732
|
);
|
|
587
733
|
}
|
|
734
|
+
// Gated on the fields that feed the judgment, mirroring the availability
|
|
735
|
+
// guard: metadata-only edits must not start rejecting a stored budget.
|
|
736
|
+
if (
|
|
737
|
+
(input.maxTokens !== undefined ||
|
|
738
|
+
input.model !== undefined ||
|
|
739
|
+
input.provider !== undefined) &&
|
|
740
|
+
typeof nextProvider === "string" &&
|
|
741
|
+
typeof nextModel === "string"
|
|
742
|
+
) {
|
|
743
|
+
assertSaneMaxTokens(
|
|
744
|
+
nextProvider,
|
|
745
|
+
nextModel,
|
|
746
|
+
input.maxTokens ??
|
|
747
|
+
(typeof existing.maxTokens === "number"
|
|
748
|
+
? existing.maxTokens
|
|
749
|
+
: undefined),
|
|
750
|
+
);
|
|
751
|
+
}
|
|
588
752
|
|
|
589
753
|
const merged: Record<string, unknown> = {
|
|
590
754
|
...existing,
|
|
@@ -594,6 +758,17 @@ async function handleUpdateProfile({
|
|
|
594
758
|
source:
|
|
595
759
|
existing.source === "managed" ? "user" : (existing.source ?? "user"),
|
|
596
760
|
};
|
|
761
|
+
// Keep the allowUnlisted marker faithful to the pair being stored: stamp
|
|
762
|
+
// it on a deliberate unlisted acceptance (warnings can only be the
|
|
763
|
+
// unlisted verdict here), and drop a stale one when the pair is now
|
|
764
|
+
// vouched for. Untouched pairs keep their stored marker.
|
|
765
|
+
if (input.provider !== undefined || input.model !== undefined) {
|
|
766
|
+
if (input.allowUnlisted && warnings.length > 0) {
|
|
767
|
+
merged.allowUnlisted = true;
|
|
768
|
+
} else if (warnings.length === 0) {
|
|
769
|
+
delete merged.allowUnlisted;
|
|
770
|
+
}
|
|
771
|
+
}
|
|
597
772
|
validateProfileEntry(merged);
|
|
598
773
|
|
|
599
774
|
// The availability guard runs only when the write touches the fields that
|
|
@@ -632,6 +807,14 @@ async function handleUpdateProfile({
|
|
|
632
807
|
};
|
|
633
808
|
}
|
|
634
809
|
|
|
810
|
+
async function handleValidateProfile({ pathParams = {} }: RouteHandlerArgs) {
|
|
811
|
+
const name = (pathParams.name ?? "").trim();
|
|
812
|
+
if (!name) {
|
|
813
|
+
throw new BadRequestError("Profile name must be a non-empty string");
|
|
814
|
+
}
|
|
815
|
+
return { check: await probeInferenceProfile(name) };
|
|
816
|
+
}
|
|
817
|
+
|
|
635
818
|
async function handleDeleteProfile({ pathParams = {} }: RouteHandlerArgs) {
|
|
636
819
|
const name = (pathParams.name ?? "").trim();
|
|
637
820
|
if (!name) {
|
|
@@ -858,4 +1041,22 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
858
1041
|
},
|
|
859
1042
|
handler: handleSetActiveProfile,
|
|
860
1043
|
},
|
|
1044
|
+
{
|
|
1045
|
+
operationId: "inference_profiles_validate",
|
|
1046
|
+
endpoint: "inference/profiles/:name/validate",
|
|
1047
|
+
method: "POST",
|
|
1048
|
+
policy: {
|
|
1049
|
+
requiredScopes: ["settings.write"],
|
|
1050
|
+
allowedPrincipalTypes: ACTOR_PRINCIPALS,
|
|
1051
|
+
},
|
|
1052
|
+
summary: "Probe a saved profile with one minimal request",
|
|
1053
|
+
description:
|
|
1054
|
+
"Dispatch one minimal test request through the named profile's resolved model and connection, and classify any failure by which object the user should fix (the profile vs its provider connection). Advisory: the probe never mutates the profile. Returns a null check when there is no verdict to give (missing, disabled, managed, or routing-identity profiles, or a probe timeout). The probe spends one tiny request on the profile's own key.",
|
|
1055
|
+
tags: ["inference"],
|
|
1056
|
+
pathParams: [{ name: "name", description: "Profile name" }],
|
|
1057
|
+
responseBody: z.object({
|
|
1058
|
+
check: profileCheckSchema.nullable(),
|
|
1059
|
+
}),
|
|
1060
|
+
handler: handleValidateProfile,
|
|
1061
|
+
},
|
|
861
1062
|
];
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
ConnectionProviderSchema,
|
|
28
28
|
deriveAuthForProvider,
|
|
29
29
|
ProviderConnectionSchema,
|
|
30
|
-
|
|
30
|
+
PROVIDERS_ALLOWING_CUSTOM_BASE_URL,
|
|
31
31
|
VALID_CONNECTION_PROVIDERS,
|
|
32
32
|
} from "../../providers/inference/auth.js";
|
|
33
33
|
import {
|
|
@@ -39,6 +39,10 @@ import {
|
|
|
39
39
|
MANAGED_CONNECTION_NAMES,
|
|
40
40
|
updateConnection,
|
|
41
41
|
} from "../../providers/inference/connections.js";
|
|
42
|
+
import {
|
|
43
|
+
EndpointCheckSchema,
|
|
44
|
+
testInferenceConnection,
|
|
45
|
+
} from "../../providers/inference/endpoint-probe.js";
|
|
42
46
|
import { PROVIDER_CATALOG } from "../../providers/model-catalog.js";
|
|
43
47
|
import {
|
|
44
48
|
isVellumManagedConnection,
|
|
@@ -64,6 +68,16 @@ const log = getLogger("routes/inference-provider-connections");
|
|
|
64
68
|
|
|
65
69
|
const providerConnectionResponseSchema = ProviderConnectionSchema;
|
|
66
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Create/update responses carry the save-time endpoint probe result for
|
|
73
|
+
* connections with a custom base URL. Advisory only: a failed probe never
|
|
74
|
+
* fails the save (some endpoints legitimately reject unauthenticated or
|
|
75
|
+
* minimal requests); clients render `hint` as a warning.
|
|
76
|
+
*/
|
|
77
|
+
const savedConnectionResponseSchema = ProviderConnectionSchema.extend({
|
|
78
|
+
endpoint_check: EndpointCheckSchema.optional(),
|
|
79
|
+
}).meta({ id: "SavedProviderConnection" });
|
|
80
|
+
|
|
67
81
|
// ---------------------------------------------------------------------------
|
|
68
82
|
// Custom provider field parsing (openai-compatible base_url + models)
|
|
69
83
|
// ---------------------------------------------------------------------------
|
|
@@ -72,14 +86,16 @@ const providerConnectionResponseSchema = ProviderConnectionSchema;
|
|
|
72
86
|
* Parse and validate `base_url` and `models` from the request body.
|
|
73
87
|
*
|
|
74
88
|
* `base_url` is only accepted for providers in
|
|
75
|
-
* `
|
|
89
|
+
* `PROVIDERS_ALLOWING_CUSTOM_BASE_URL` (openai-compatible and ollama).
|
|
76
90
|
* For all other providers, supplying `base_url` returns a 400. This prevents
|
|
77
91
|
* API-key exfiltration: an attacker cannot create an `anthropic` connection
|
|
78
92
|
* with a `base_url` pointing to their own server, which would redirect all
|
|
79
93
|
* LLM calls (and the API key) to the attacker.
|
|
80
94
|
*
|
|
81
|
-
* Even for
|
|
82
|
-
* networks or cloud metadata endpoints
|
|
95
|
+
* Even for allowed providers, a platform-hosted daemon rejects `base_url`
|
|
96
|
+
* values that point to private networks or cloud metadata endpoints
|
|
97
|
+
* (SSRF protection). Self-hosted daemons allow those addresses because
|
|
98
|
+
* localhost and LAN hosts are the expected target.
|
|
83
99
|
*/
|
|
84
100
|
async function parseCustomProviderFields(
|
|
85
101
|
body: Record<string, unknown>,
|
|
@@ -96,14 +112,14 @@ async function parseCustomProviderFields(
|
|
|
96
112
|
if ("base_url" in body) {
|
|
97
113
|
const raw = body.base_url;
|
|
98
114
|
|
|
99
|
-
// Gate: base_url is only valid for
|
|
115
|
+
// Gate: base_url is only valid for providers that persist a custom endpoint.
|
|
100
116
|
if (
|
|
101
117
|
raw !== null &&
|
|
102
118
|
raw !== undefined &&
|
|
103
|
-
!
|
|
119
|
+
!PROVIDERS_ALLOWING_CUSTOM_BASE_URL.has(provider)
|
|
104
120
|
) {
|
|
105
121
|
throw new BadRequestError(
|
|
106
|
-
`base_url is only valid for openai-compatible providers. Remove base_url or use
|
|
122
|
+
`base_url is only valid for openai-compatible and ollama providers. Remove base_url or use a provider that accepts a custom endpoint.`,
|
|
107
123
|
);
|
|
108
124
|
}
|
|
109
125
|
|
|
@@ -426,7 +442,10 @@ async function handleCreateConnection({ body = {} }: RouteHandlerArgs) {
|
|
|
426
442
|
throw new BadRequestError("Invalid auth configuration.");
|
|
427
443
|
}
|
|
428
444
|
|
|
429
|
-
|
|
445
|
+
const endpointCheck = await testInferenceConnection(result.connection);
|
|
446
|
+
return endpointCheck
|
|
447
|
+
? { ...result.connection, endpoint_check: endpointCheck }
|
|
448
|
+
: result.connection;
|
|
430
449
|
}
|
|
431
450
|
|
|
432
451
|
async function handleUpdateConnection({
|
|
@@ -545,7 +564,10 @@ async function handleUpdateConnection({
|
|
|
545
564
|
throw new BadRequestError("Invalid auth configuration.");
|
|
546
565
|
}
|
|
547
566
|
|
|
548
|
-
|
|
567
|
+
const endpointCheck = await testInferenceConnection(result.connection);
|
|
568
|
+
return endpointCheck
|
|
569
|
+
? { ...result.connection, endpoint_check: endpointCheck }
|
|
570
|
+
: result.connection;
|
|
549
571
|
}
|
|
550
572
|
|
|
551
573
|
async function handleDeleteConnection({ pathParams = {} }: RouteHandlerArgs) {
|
|
@@ -749,7 +771,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
749
771
|
base_url: z.string().url().nullable().optional(),
|
|
750
772
|
models: z.array(ConnectionModelSchema).nullable().optional(),
|
|
751
773
|
}),
|
|
752
|
-
responseBody:
|
|
774
|
+
responseBody: savedConnectionResponseSchema,
|
|
753
775
|
responseStatus: "201",
|
|
754
776
|
additionalResponses: {
|
|
755
777
|
"400": { description: "Invalid provider or auth schema" },
|
|
@@ -777,7 +799,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
777
799
|
base_url: z.string().url().nullable().optional(),
|
|
778
800
|
models: z.array(ConnectionModelSchema).nullable().optional(),
|
|
779
801
|
}),
|
|
780
|
-
responseBody:
|
|
802
|
+
responseBody: savedConnectionResponseSchema,
|
|
781
803
|
additionalResponses: {
|
|
782
804
|
"400": {
|
|
783
805
|
description:
|