@vellumai/assistant 0.11.2 → 0.11.3-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +58 -3
- package/docs/trusted-contact-access.md +9 -5
- package/docs/vellum-doctor.md +195 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/ces-client/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/src/rpc-client.ts +19 -2
- package/node_modules/@vellumai/environments/package.json +2 -1
- package/node_modules/@vellumai/environments/src/__tests__/cloud-assistant-hub-url.test.ts +38 -0
- package/node_modules/@vellumai/environments/src/__tests__/install-layout.test.ts +98 -0
- package/node_modules/@vellumai/environments/src/__tests__/package-boundary.test.ts +5 -5
- package/node_modules/@vellumai/environments/src/index.ts +17 -6
- package/node_modules/@vellumai/environments/src/install-layout.ts +49 -0
- package/node_modules/@vellumai/environments/src/seeds.ts +29 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +4 -0
- package/node_modules/@vellumai/gateway-client/src/verification-session-contract.ts +91 -6
- package/node_modules/@vellumai/ipc-server-utils/src/endpoint.test.ts +36 -0
- package/node_modules/@vellumai/ipc-server-utils/src/endpoint.ts +142 -0
- package/node_modules/@vellumai/ipc-server-utils/src/index.ts +2 -0
- package/node_modules/@vellumai/ipc-server-utils/src/listen-options.ts +3 -0
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.test.ts +15 -1
- package/node_modules/@vellumai/ipc-server-utils/src/socket-watchdog.ts +17 -2
- package/node_modules/@vellumai/service-contracts/src/channels.ts +39 -0
- package/node_modules/@vellumai/service-contracts/src/ingress.ts +10 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +6 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +16 -2
- package/openapi.yaml +589 -30
- package/package.json +1 -1
- package/src/__tests__/agent-image-optimize.test.ts +4 -4
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +113 -66
- package/src/__tests__/agent-loop-override-profile.test.ts +38 -48
- package/src/__tests__/agent-loop-subagent-billing-headers.test.ts +166 -0
- 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__/answered-question-persistence.test.ts +240 -0
- package/src/__tests__/anthropic-provider.test.ts +165 -30
- package/src/__tests__/app-control-flow.test.ts +9 -8
- package/src/__tests__/assistant-event-hub.test.ts +48 -0
- package/src/__tests__/attachments-store-heic-normalize.test.ts +16 -12
- package/src/__tests__/attachments-store.test.ts +130 -68
- package/src/__tests__/attachments.test.ts +36 -36
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
- package/src/__tests__/call-start-guardian-guard.test.ts +18 -4
- package/src/__tests__/canned-reply-release.test.ts +86 -0
- package/src/__tests__/channel-guardian.test.ts +59 -29
- package/src/__tests__/channel-readiness-telegram-remote.test.ts +154 -0
- package/src/__tests__/channel-setup-panel-ack.test.ts +6 -5
- package/src/__tests__/client-os-metadata-persistence.test.ts +103 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +1 -1
- package/src/__tests__/consult-deadline.test.ts +65 -0
- package/src/__tests__/conversation-agent-loop.test.ts +438 -2
- package/src/__tests__/conversation-disk-view-integration.test.ts +5 -1
- package/src/__tests__/conversation-disk-view.test.ts +4 -4
- package/src/__tests__/conversation-error.test.ts +22 -3
- package/src/__tests__/conversation-fork-crud.test.ts +5 -1
- package/src/__tests__/conversation-fork-referential.test.ts +352 -0
- package/src/__tests__/conversation-fork-retrospective.test.ts +5 -1
- package/src/__tests__/conversation-group-tools.test.ts +32 -2
- package/src/__tests__/conversation-inference-profile-route.test.ts +27 -1
- package/src/__tests__/conversation-initial-prompt.test.ts +51 -22
- package/src/__tests__/conversation-key-store-bootstrap-cleanup.test.ts +119 -0
- package/src/__tests__/conversation-key-store-origin.test.ts +105 -0
- package/src/__tests__/conversation-list-recency-ordering.test.ts +104 -0
- package/src/__tests__/conversation-origin-at-creation.test.ts +153 -0
- package/src/__tests__/conversation-origin-channel-filter.test.ts +95 -0
- package/src/__tests__/conversation-placement-promotion.test.ts +172 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +123 -0
- package/src/__tests__/conversation-queue.test.ts +433 -1
- package/src/__tests__/conversation-retry-route.test.ts +3 -1
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +64 -1
- package/src/__tests__/conversation-store-ephemeral.test.ts +4 -0
- package/src/__tests__/conversation-store.test.ts +26 -10
- package/src/__tests__/conversation-summarize-route.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +15 -6
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -5
- package/src/__tests__/conversation-surfaces-app-control.test.ts +12 -11
- package/src/__tests__/conversation-surfaces-app-open.test.ts +11 -5
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +5 -4
- package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +967 -0
- package/src/__tests__/conversation-surfaces-persisted-info.test.ts +312 -0
- package/src/__tests__/conversation-surfaces-queued-emit.test.ts +6 -5
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -8
- package/src/__tests__/conversation-surfaces-standalone.test.ts +21 -11
- package/src/__tests__/conversation-surfaces-state-update.test.ts +7 -11
- package/src/__tests__/conversation-surfaces-table-action.test.ts +7 -6
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +11 -6
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +9 -6
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +70 -7
- package/src/__tests__/conversation-wait-for-idle.test.ts +6 -11
- package/src/__tests__/credential-prompt-route.test.ts +57 -7
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +89 -28
- package/src/__tests__/custom-group-listing-visibility.test.ts +1 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +4 -1
- package/src/__tests__/discord-access-request-privacy.test.ts +366 -0
- package/src/__tests__/discord-requester-notice-privacy.test.ts +87 -0
- package/src/__tests__/disk-pressure-guard.test.ts +55 -55
- package/src/__tests__/disk-pressure-routes.test.ts +10 -10
- package/src/__tests__/disk-pressure-tools.test.ts +2 -2
- package/src/__tests__/disk-usage.test.ts +35 -20
- package/src/__tests__/document-sync-tags.test.ts +379 -0
- package/src/__tests__/document-update-default-surface.test.ts +28 -0
- package/src/__tests__/dynamic-skill-background-guard.test.ts +2 -2
- package/src/__tests__/emit-signal-routing-intent.test.ts +3 -4
- package/src/__tests__/ensure-conversation-exists.test.ts +8 -5
- package/src/__tests__/events-dev-bypass-actor.test.ts +183 -1
- package/src/__tests__/evict-conversations-for-reload.test.ts +90 -0
- package/src/__tests__/file-list-tool.test.ts +12 -10
- package/src/__tests__/file-ops-service.test.ts +151 -44
- package/src/__tests__/file-read-container-boundary.test.ts +137 -0
- package/src/__tests__/file-read-tool.test.ts +21 -9
- package/src/__tests__/file-url-path-guard.test.ts +83 -0
- package/src/__tests__/filesystem-tools.test.ts +82 -66
- package/src/__tests__/guardian-outbound-http.test.ts +8 -8
- package/src/__tests__/helpers/gateway-verification-sessions-stub.ts +14 -1
- package/src/__tests__/helpers/hub-clients.ts +42 -0
- package/src/__tests__/helpers/mock-conversation.ts +46 -0
- package/src/__tests__/helpers/verification-sessions-ipc-sim.ts +61 -57
- package/src/__tests__/history-repair.test.ts +112 -12
- package/src/__tests__/host-bash-proxy.test.ts +10 -5
- package/src/__tests__/host-bash-routes.test.ts +67 -0
- package/src/__tests__/host-browser-proxy.test.ts +2 -2
- package/src/__tests__/host-cu-proxy.test.ts +3 -7
- package/src/__tests__/host-file-proxy.test.ts +3 -7
- package/src/__tests__/host-file-read-tool.test.ts +5 -1
- package/src/__tests__/host-transfer-proxy-targeted.test.ts +6 -4
- package/src/__tests__/http-conversation-lineage.test.ts +2 -5
- package/src/__tests__/http-user-message-parity.test.ts +44 -0
- package/src/__tests__/identity-routes.test.ts +13 -13
- package/src/__tests__/image-conversion.test.ts +29 -26
- package/src/__tests__/image-source-path-reinject.test.ts +2 -2
- package/src/__tests__/inference-profile-session-handler.test.ts +95 -0
- package/src/__tests__/inference-profile-session-ipc.test.ts +25 -0
- package/src/__tests__/introduction-card-resolver.test.ts +6 -1
- package/src/__tests__/list-messages-attachments.test.ts +56 -32
- package/src/__tests__/list-messages-background-tool-completion.test.ts +4 -4
- package/src/__tests__/list-messages-client-message-id.test.ts +2 -2
- package/src/__tests__/list-messages-hidden-metadata.test.ts +16 -16
- package/src/__tests__/list-messages-page-latest.test.ts +61 -61
- package/src/__tests__/list-messages-provider-error.test.ts +6 -6
- package/src/__tests__/list-messages-queued.test.ts +49 -6
- package/src/__tests__/list-messages-system-card.test.ts +4 -4
- package/src/__tests__/list-messages-tool-merge.test.ts +9 -9
- package/src/__tests__/llm-resolver-override-or-default.test.ts +141 -0
- package/src/__tests__/llm-usage-store.test.ts +55 -0
- package/src/__tests__/manual-token-reconciliation.test.ts +45 -0
- package/src/__tests__/mcp-tool-annotations-risk.test.ts +133 -0
- package/src/__tests__/media-stream-server-integration.test.ts +36 -0
- package/src/__tests__/migration-export-http.test.ts +1 -1
- package/src/__tests__/migration-import-commit-http.test.ts +1 -1
- package/src/__tests__/migration-import-preflight-http.test.ts +1 -1
- package/src/__tests__/migration-validate-http.test.ts +1 -1
- package/src/__tests__/mtime-cache.test.ts +65 -3
- package/src/__tests__/notification-platform-adapter.test.ts +104 -0
- package/src/__tests__/notification-schedule-notify-dedup.test.ts +1 -1
- package/src/__tests__/oauth-provider-profiles.test.ts +4 -2
- package/src/__tests__/oauth-provider-seed-logos.test.ts +65 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +17 -0
- package/src/__tests__/oauth-providers-routes.test.ts +1 -0
- package/src/__tests__/openai-responses-prompt-cache.test.ts +5 -2
- package/src/__tests__/path-policy.test.ts +164 -1
- package/src/__tests__/persist-media-references.test.ts +2 -2
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +5 -5
- package/src/__tests__/persist-unsendable-image.test.ts +23 -23
- package/src/__tests__/persona-resolver.test.ts +154 -0
- package/src/__tests__/plugin-api-webhook-url.test.ts +170 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +6 -0
- package/src/__tests__/processing-flag-persist-failure.test.ts +15 -10
- package/src/__tests__/profile-availability-incomplete.test.ts +113 -0
- package/src/__tests__/profiler-routes.test.ts +13 -13
- package/src/__tests__/prompt-cache-cross-turn-stability.test.ts +593 -0
- package/src/__tests__/proxy-approval-callback.test.ts +3 -3
- package/src/__tests__/queued-message-cancel-and-steer.test.ts +189 -0
- package/src/__tests__/queued-message-steer-actor-scoping.test.ts +164 -0
- package/src/__tests__/resolve-trust-class.test.ts +20 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +1 -1
- package/src/__tests__/run-due-schedules.test.ts +174 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +15 -7
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +166 -0
- package/src/__tests__/schedule-routes.test.ts +730 -8
- package/src/__tests__/schedule-store.test.ts +540 -0
- package/src/__tests__/schedule-tools.test.ts +103 -0
- package/src/__tests__/scheduler-wake.test.ts +5 -0
- package/src/__tests__/secret-routes-platform-proxy.test.ts +29 -1
- package/src/__tests__/secure-keys.test.ts +91 -0
- package/src/__tests__/server-history-render.test.ts +97 -7
- package/src/__tests__/size-guard.test.ts +8 -8
- package/src/__tests__/skill-docs-sync-guard.test.ts +3 -2
- package/src/__tests__/skill-tool-factory.test.ts +247 -0
- package/src/__tests__/skills-install-staging.test.ts +15 -6
- package/src/__tests__/slack-edit-ordering-characterization.test.ts +103 -0
- package/src/__tests__/slack-inbound-verification.test.ts +4 -1
- package/src/__tests__/slack-mention-binding-invariance.test.ts +103 -0
- package/src/__tests__/slack-mention-provider-leak-audit.test.ts +135 -0
- package/src/__tests__/starter-task-flow.test.ts +5 -4
- package/src/__tests__/steer-on-enqueue-question.test.ts +3 -2
- package/src/__tests__/stt-language-catalog-parity.test.ts +38 -0
- package/src/__tests__/subagent-allowlist-validation.test.ts +26 -4
- package/src/__tests__/subagent-cron-run-attribution.test.ts +301 -0
- package/src/__tests__/subagent-disposal.test.ts +10 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +10 -0
- package/src/__tests__/subagent-fork-prompt-role.test.ts +85 -11
- package/src/__tests__/subagent-fork-spawn.test.ts +15 -7
- package/src/__tests__/subagent-manager-notify.test.ts +148 -0
- package/src/__tests__/subagent-notify-parent.test.ts +1 -1
- package/src/__tests__/subagent-persistence.test.ts +141 -0
- package/src/__tests__/subagent-role-registry.test.ts +172 -63
- package/src/__tests__/subagent-role-resolution.test.ts +88 -0
- package/src/__tests__/subagent-spawn-and-await.test.ts +48 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +111 -10
- package/src/__tests__/subagent-terminal-message.test.ts +88 -1
- package/src/__tests__/subagent-tool-gate-mode.test.ts +225 -10
- package/src/__tests__/subagent-tools.test.ts +1901 -108
- package/src/__tests__/surface-completion-compaction-boundary.test.ts +124 -0
- package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +298 -0
- package/src/__tests__/system-prompt.test.ts +82 -7
- package/src/__tests__/tool-grant-request-escalation.test.ts +40 -1
- package/src/__tests__/tool-result-spool.test.ts +74 -18
- package/src/__tests__/tool-side-effects-documents.test.ts +174 -0
- package/src/__tests__/tool-side-effects-hook-failures.test.ts +129 -0
- package/src/__tests__/tools-audio-read.test.ts +10 -10
- package/src/__tests__/tools-get-route.test.ts +85 -12
- package/src/__tests__/tools-list-cli.test.ts +171 -0
- package/src/__tests__/top-level-renderer.test.ts +54 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +68 -1
- package/src/__tests__/trusted-contact-multichannel.test.ts +4 -1
- package/src/__tests__/turn-tail-chain.test.ts +135 -0
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +6 -4
- package/src/__tests__/ui-visual-surface.test.ts +5 -4
- package/src/__tests__/ui-work-result-surface.test.ts +5 -4
- package/src/__tests__/unified-turn-context-visible-app.test.ts +7 -8
- package/src/__tests__/usage-routes.test.ts +62 -0
- package/src/__tests__/verification-outbound-delivery.test.ts +176 -0
- package/src/__tests__/visible-app-context.test.ts +20 -4
- package/src/__tests__/web-search-history.test.ts +96 -0
- package/src/__tests__/workspace-migration-139-clear-renamed-cost-profile-label.test.ts +6 -6
- package/src/__tests__/workspace-migration-140-repair-seed-pinned-memory-v3-live.test.ts +330 -0
- package/src/acp/failure-error.ts +3 -5
- package/src/agent/attachments.ts +26 -24
- package/src/agent/history-repair/history-repair.ts +27 -26
- package/src/agent/image-optimize.ts +6 -6
- package/src/agent/loop.ts +40 -22
- package/src/agent/message-types.ts +3 -3
- package/src/api/constants/document-tools.ts +55 -0
- package/src/api/events/question-answered.ts +53 -0
- package/src/api/events/tool-result.ts +7 -0
- package/src/api/index.ts +13 -0
- package/src/api/responses/conversation-message.ts +11 -0
- package/src/api/surfaces-oauth-connect.test.ts +96 -0
- package/src/api/surfaces.ts +36 -8
- package/src/approvals/guardian-channel-delivery.ts +83 -0
- package/src/approvals/guardian-expiry-notifier.ts +13 -11
- package/src/approvals/guardian-request-resolvers.ts +222 -135
- package/src/calls/__tests__/voice-session-bridge.test.ts +50 -0
- package/src/calls/media-stream-server.ts +9 -1
- package/src/calls/voice-session-bridge.ts +38 -0
- package/src/channels/__tests__/gateway-verification-sessions.test.ts +3 -3
- package/src/channels/config.ts +7 -2
- package/src/channels/gateway-verification-sessions.ts +7 -2
- package/src/channels/types.ts +7 -1
- package/src/cli/commands/__tests__/cache.test.ts +7 -4
- package/src/cli/commands/__tests__/cli-test-harness.ts +82 -0
- package/src/cli/commands/__tests__/conversations-wake.test.ts +51 -6
- package/src/cli/commands/__tests__/inference-models.test.ts +14 -0
- package/src/cli/commands/__tests__/inference-profiles.test.ts +44 -1
- package/src/cli/commands/__tests__/inference-providers.test.ts +7 -0
- package/src/cli/commands/__tests__/plugins.test.ts +692 -0
- package/src/cli/commands/__tests__/schedules.test.ts +136 -37
- package/src/cli/commands/conversations.ts +7 -3
- package/src/cli/commands/credentials.help.ts +7 -0
- package/src/cli/commands/inference-profiles.ts +9 -3
- package/src/cli/commands/inference.help.ts +16 -2
- package/src/cli/commands/mcp.help.ts +4 -2
- package/src/cli/commands/memory/memory-retrospective.ts +18 -0
- package/src/cli/commands/notifications.ts +3 -6
- package/src/cli/commands/oauth/connect-surface-guidance.test.ts +16 -0
- package/src/cli/commands/oauth/connect-surface-guidance.ts +25 -7
- package/src/cli/commands/oauth/connect.test.ts +26 -1
- package/src/cli/commands/oauth/connect.ts +4 -1
- package/src/cli/commands/oauth/index.help.ts +3 -1
- package/src/cli/commands/oauth/providers.ts +3 -0
- package/src/cli/commands/plugins.help.ts +25 -2
- package/src/cli/commands/plugins.ts +289 -10
- package/src/cli/commands/schedules.help.ts +14 -12
- package/src/cli/commands/schedules.ts +19 -5
- package/src/cli/commands/tools.help.ts +4 -2
- package/src/cli/commands/tools.ts +38 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +1 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +79 -0
- package/src/cli/lib/__tests__/install-from-platform.test.ts +47 -1
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +86 -7
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +122 -0
- package/src/cli/lib/install-from-github.ts +61 -0
- package/src/cli/lib/install-from-platform.ts +13 -2
- package/src/cli/lib/plugin-surfaces.ts +124 -4
- package/src/cli/lib/upgrade-plugin.ts +18 -0
- package/src/cli.ts +1 -1
- package/src/config/__tests__/deployment-context-defaults.test.ts +73 -0
- package/src/config/__tests__/memory-retrospective-schema.test.ts +40 -0
- package/src/config/__tests__/plugin-updates-schema.test.ts +43 -0
- package/src/config/__tests__/profile-tool-support.test.ts +131 -0
- package/src/config/bundled-skills/AGENTS.md +1 -1
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -1
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +3 -3
- package/src/config/bundled-skills/schedule/SKILL.md +5 -1
- package/src/config/bundled-skills/schedule/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/TOOLS.json +2 -2
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +1 -0
- package/src/config/bundled-skills/subagent/SKILL.md +53 -22
- package/src/config/bundled-skills/subagent/TOOLS.json +14 -13
- package/src/config/bundled-skills/visualize/SKILL.md +1 -1
- package/src/config/default-profile-catalog.ts +6 -2
- package/src/config/feature-flag-registry.json +25 -17
- package/src/config/llm-resolver.ts +45 -15
- package/src/config/loader.ts +70 -1
- package/src/config/profile-tool-support.ts +110 -0
- package/src/config/sanitize-for-transfer.ts +11 -0
- package/src/config/schema.ts +4 -0
- package/src/config/schemas/__tests__/stt.test.ts +20 -5
- package/src/config/schemas/channels.ts +13 -0
- package/src/config/schemas/memory-retrospective.ts +25 -0
- package/src/config/schemas/plugin-updates.ts +55 -0
- package/src/config/schemas/services.ts +8 -4
- package/src/config/schemas/stt.ts +17 -5
- package/src/config/schemas/timeouts.ts +1 -1
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +16 -0
- package/src/contacts/guardian-delivery-reader.ts +29 -2
- package/src/context/compactor.ts +4 -4
- package/src/context/post-turn-tool-result-truncation.ts +32 -16
- package/src/context/strip-injections.ts +42 -0
- package/src/context/tool-result-spool.ts +32 -18
- package/src/credential-execution/ces-runtime.ts +3 -0
- package/src/credential-execution/client.ts +22 -1
- package/src/credential-execution/process-manager.test.ts +99 -0
- package/src/credential-execution/process-manager.ts +62 -16
- package/src/daemon/__tests__/abort-null-controller.test.ts +141 -5
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +8 -12
- package/src/daemon/__tests__/install-assistant-command.test.ts +234 -0
- package/src/daemon/__tests__/turn-tail-assistant-reply-notify.test.ts +2 -4
- package/src/daemon/__tests__/turn-tail-deleted-conversation.test.ts +20 -29
- package/src/daemon/channel-ui-capability.ts +4 -4
- package/src/daemon/conversation-agent-loop-handlers.ts +117 -61
- package/src/daemon/conversation-agent-loop.ts +318 -148
- package/src/daemon/conversation-attachments.ts +1 -1
- package/src/daemon/conversation-error.ts +8 -1
- package/src/daemon/conversation-initial-prompt.ts +16 -38
- package/src/daemon/conversation-lifecycle.ts +154 -32
- package/src/daemon/conversation-messaging.ts +106 -43
- package/src/daemon/conversation-process.ts +131 -33
- package/src/daemon/conversation-queue-manager.ts +10 -0
- package/src/daemon/conversation-runtime-assembly.ts +9 -4
- package/src/daemon/conversation-store.ts +52 -3
- package/src/daemon/conversation-surface-state.ts +9 -0
- package/src/daemon/conversation-surfaces.ts +403 -210
- package/src/daemon/conversation-tool-setup.ts +56 -7
- package/src/daemon/conversation-turn-finalize.ts +134 -89
- package/src/daemon/conversation.ts +111 -27
- package/src/daemon/disk-pressure-guard-lifecycle.ts +2 -2
- package/src/daemon/disk-pressure-guard.ts +3 -3
- package/src/daemon/doordash-steps.ts +5 -5
- package/src/daemon/external-plugins-bootstrap.ts +7 -1
- package/src/daemon/handlers/config-channels.ts +48 -5
- package/src/daemon/handlers/conversation-history.ts +1 -1
- package/src/daemon/handlers/conversations.ts +62 -17
- package/src/daemon/handlers/shared.ts +12 -0
- package/src/daemon/handlers/skills.ts +11 -1
- package/src/daemon/identity-helpers.ts +38 -10
- package/src/daemon/install-assistant-command.ts +322 -0
- package/src/daemon/lifecycle.ts +14 -3
- package/src/daemon/message-types/conversations.ts +6 -7
- package/src/daemon/message-types/sync.ts +1 -0
- package/src/daemon/persist-media-references.ts +23 -14
- package/src/daemon/process-message.ts +6 -6
- package/src/daemon/tool-setup-types.ts +19 -122
- package/src/daemon/tool-side-effects.ts +17 -94
- package/src/daemon/trust-context-types.ts +39 -0
- package/src/daemon/trust-context.ts +14 -6
- package/src/daemon/turn-tail-chain.ts +73 -0
- package/src/daemon/web-search-history.ts +47 -33
- package/src/heartbeat/heartbeat-service.ts +6 -6
- package/src/ipc/__tests__/socket-path.test.ts +21 -0
- package/src/ipc/assistant-server.ts +17 -10
- package/src/ipc/cli-client.ts +10 -1
- package/src/ipc/routes/__tests__/documents-sync-ipc-routes.test.ts +56 -0
- package/src/ipc/routes/documents-sync-ipc-routes.ts +42 -0
- package/src/ipc/socket-cleanup.ts +9 -7
- package/src/ipc/socket-path.ts +4 -96
- package/src/live-voice/__tests__/live-voice-archive.test.ts +16 -16
- package/src/live-voice/__tests__/live-voice-attach-image.test.ts +209 -0
- package/src/live-voice/__tests__/live-voice-connection.test.ts +20 -0
- package/src/live-voice/__tests__/live-voice-integration.test.ts +7 -5
- package/src/live-voice/__tests__/live-voice-session-telemetry.test.ts +265 -0
- package/src/live-voice/__tests__/live-voice-stt.test.ts +6 -1
- package/src/live-voice/__tests__/live-voice-vad.test.ts +35 -15
- package/src/live-voice/__tests__/protocol.test.ts +52 -0
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +3 -0
- package/src/live-voice/live-voice-archive.ts +13 -13
- package/src/live-voice/live-voice-connection.ts +7 -1
- package/src/live-voice/live-voice-photo.ts +169 -0
- package/src/live-voice/live-voice-session.ts +108 -4
- package/src/live-voice/protocol.ts +89 -1
- package/src/mcp/client.ts +10 -0
- package/src/messaging/providers/__tests__/callback-routing.test.ts +5 -1
- package/src/messaging/providers/__tests__/transport-dispatch.test.ts +66 -4
- package/src/messaging/providers/callback-routing.ts +1 -0
- package/src/messaging/providers/discord/api.ts +312 -0
- package/src/messaging/providers/discord/dm-delivery.test.ts +204 -0
- package/src/messaging/providers/discord/render.test.ts +204 -0
- package/src/messaging/providers/discord/render.ts +195 -0
- package/src/messaging/providers/discord/send.test.ts +179 -0
- package/src/messaging/providers/discord/send.ts +212 -0
- package/src/messaging/providers/discord/transport.ts +81 -0
- package/src/messaging/providers/index.ts +3 -1
- package/src/messaging/providers/retry-policy.test.ts +247 -0
- package/src/messaging/providers/retry-policy.ts +206 -0
- package/src/messaging/providers/slack/mention-source.test.ts +285 -0
- package/src/messaging/providers/slack/mention-source.ts +287 -0
- package/src/messaging/providers/telegram-bot/api.ts +47 -130
- package/src/messaging/providers/whatsapp/api.ts +47 -136
- package/src/monitoring/__tests__/db-integrity-sample.test.ts +4 -1
- package/src/monitoring/__tests__/plugin-auto-update.test.ts +332 -0
- package/src/monitoring/db-integrity-sample.ts +6 -1
- package/src/monitoring/plugin-auto-update.ts +359 -0
- package/src/monitoring/resource-sampler.ts +21 -5
- package/src/monitoring/worker.ts +13 -0
- package/src/notifications/__tests__/assistant-reply-producer.test.ts +375 -2
- package/src/notifications/__tests__/broadcaster.test.ts +37 -1
- package/src/notifications/__tests__/copy-composer.test.ts +165 -0
- package/src/notifications/__tests__/emit-signal-pipeline-failure.test.ts +205 -0
- package/src/notifications/__tests__/notification-utils.test.ts +249 -0
- package/src/notifications/adapters/platform.ts +31 -2
- package/src/notifications/assistant-reply-producer.ts +79 -32
- package/src/notifications/broadcaster.ts +30 -10
- package/src/notifications/copy-composer.ts +69 -0
- package/src/notifications/emit-signal.ts +82 -15
- package/src/notifications/events-store.ts +10 -2
- package/src/notifications/home-feed-side-effect.ts +6 -3
- package/src/notifications/notification-utils.ts +173 -5
- package/src/notifications/signal.ts +12 -0
- package/src/oauth/AGENTS.md +4 -2
- package/src/oauth/byo-connection.ts +6 -1
- package/src/oauth/credential-token-resolver.ts +3 -10
- package/src/oauth/manual-token-connection.ts +29 -64
- package/src/oauth/manual-token-providers.ts +62 -0
- package/src/oauth/provider-serializer.ts +13 -0
- package/src/oauth/seed-providers.ts +62 -11
- package/src/onboarding/onboarding-events-store.ts +91 -1
- package/src/permissions/question-prompter.test.ts +4 -0
- package/src/permissions/question-prompter.ts +21 -4
- package/src/persistence/__tests__/conversation-lineage.test.ts +248 -0
- package/src/persistence/attachments-store.ts +15 -15
- package/src/persistence/conversation-attention-store.ts +30 -10
- package/src/persistence/conversation-bootstrap.ts +17 -0
- package/src/persistence/conversation-crud.ts +363 -97
- package/src/persistence/conversation-group-migration.ts +1 -1
- package/src/persistence/conversation-key-store.ts +38 -10
- package/src/persistence/conversation-lineage.ts +226 -0
- package/src/persistence/conversation-plugin-facade.ts +28 -3
- package/src/persistence/conversation-queries.ts +207 -33
- package/src/persistence/conversation-types.ts +107 -1
- package/src/persistence/jobs-store.ts +32 -8
- package/src/persistence/llm-usage-store.ts +22 -0
- package/src/persistence/migrations/361-normalize-managed-connection-rows.test.ts +168 -0
- package/src/persistence/migrations/361-normalize-managed-connection-rows.ts +76 -0
- package/src/persistence/migrations/362-add-conversation-subagent-kind.test.ts +187 -0
- package/src/persistence/migrations/362-add-conversation-subagent-kind.ts +93 -0
- package/src/persistence/migrations/363-backfill-schedule-inference-profile.test.ts +229 -0
- package/src/persistence/migrations/363-backfill-schedule-inference-profile.ts +127 -0
- package/src/persistence/migrations/364-add-schedule-source-key.test.ts +120 -0
- package/src/persistence/migrations/364-add-schedule-source-key.ts +43 -0
- package/src/persistence/migrations/365-add-conversation-fork-strategy.ts +27 -0
- package/src/persistence/schema/conversations.ts +32 -0
- package/src/persistence/schema/infrastructure.ts +3 -0
- package/src/persistence/steps.ts +30 -0
- package/src/persistence/subagent-store.ts +148 -6
- package/src/platform/managed-speech.test.ts +20 -0
- package/src/platform/managed-speech.ts +9 -0
- package/src/plugin-api/index.ts +8 -0
- package/src/plugin-api/webhook-url.ts +151 -0
- package/src/plugins/__tests__/installed-plugin-dirs.test.ts +82 -0
- package/src/plugins/__tests__/source-fingerprint.test.ts +5 -1
- package/src/plugins/collect-source-versions.ts +4 -27
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +5 -5
- package/src/plugins/defaults/exploration-drift/package.json +1 -1
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +4 -2
- package/src/plugins/defaults/image-recovery/recover.ts +67 -57
- package/src/plugins/defaults/index.ts +1 -1
- package/src/plugins/defaults/memory/__tests__/conversation-queries.test.ts +64 -18
- package/src/plugins/defaults/memory/__tests__/jobs-worker-outcome-contract.test.ts +242 -0
- package/src/plugins/defaults/memory/__tests__/jobs-worker-retrospective-sweep.test.ts +24 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-enqueue.test.ts +48 -3
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +511 -19
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-prompt.test.ts +39 -3
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +493 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-wake-chain.test.ts +5 -4
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +5 -1
- package/src/plugins/defaults/memory/job-handlers.ts +87 -5
- package/src/plugins/defaults/memory/jobs-worker.ts +60 -6
- package/src/plugins/defaults/memory/memory-retrospective-constants.ts +11 -0
- package/src/plugins/defaults/memory/memory-retrospective-enqueue.ts +29 -0
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +342 -60
- package/src/plugins/defaults/memory/memory-retrospective-prompt.ts +23 -3
- package/src/plugins/defaults/memory/substrate/consolidation-lock.ts +9 -4
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +7 -0
- package/src/plugins/defaults/memory/v1/graph/injection.ts +1 -1
- package/src/plugins/defaults/memory/v2/router.ts +8 -0
- package/src/plugins/defaults/turn-context/unified-turn-context.ts +1 -2
- package/src/plugins/external-plugin-loader.ts +17 -8
- package/src/plugins/installed-plugin-dirs.ts +101 -0
- package/src/plugins/mtime-cache.ts +34 -22
- package/src/plugins/types.ts +69 -8
- package/src/prompts/persona-resolver.ts +6 -2
- package/src/prompts/system-prompt.ts +72 -17
- package/src/providers/__tests__/inference.test.ts +40 -0
- package/src/providers/__tests__/preflight-resolved-config.test.ts +58 -1
- package/src/providers/__tests__/retry-callsite.test.ts +67 -0
- package/src/providers/__tests__/vellum-connection-routing.test.ts +42 -11
- package/src/providers/anthropic/client.ts +112 -95
- package/src/providers/connection-resolution.ts +35 -1
- package/src/providers/gemini/client.ts +5 -5
- package/src/providers/inference/adapter-factory.ts +16 -10
- package/src/providers/inference/auth.ts +16 -0
- package/src/providers/inference/connection-availability.ts +164 -12
- package/src/providers/inference/connections.ts +25 -1
- package/src/providers/media-resolve.ts +21 -11
- package/src/providers/model-catalog.ts +15 -0
- package/src/providers/openai/chat-completions-provider.ts +4 -4
- package/src/providers/openai/responses-provider.ts +12 -25
- package/src/providers/registry.ts +9 -4
- package/src/providers/retry.ts +25 -0
- package/src/providers/server-tool-pairing.ts +134 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +187 -6
- package/src/providers/speech-to-text/deepgram.ts +1 -0
- package/src/providers/speech-to-text/resolve.ts +80 -10
- package/src/providers/speech-to-text/vellum-managed.ts +7 -1
- package/src/providers/speech-to-text/vellum-speech-relay-connection.ts +3 -1
- package/src/providers/types.ts +26 -6
- package/src/providers/vellum-model-routing.ts +6 -7
- package/src/runtime/AGENTS.md +2 -0
- package/src/runtime/__tests__/agent-wake.test.ts +16 -16
- package/src/runtime/__tests__/background-job-runner.test.ts +0 -144
- package/src/runtime/__tests__/desktop-presence.test.ts +162 -0
- package/src/runtime/agent-wake.ts +63 -24
- package/src/runtime/assistant-event-hub.ts +87 -12
- package/src/runtime/auth/__tests__/guard-tests.test.ts +2 -0
- package/src/runtime/auth/__tests__/scopes.test.ts +20 -0
- package/src/runtime/auth/same-actor.ts +56 -8
- package/src/runtime/auth/scopes.ts +12 -0
- package/src/runtime/auth/types.ts +2 -0
- package/src/runtime/background-job-runner.ts +5 -27
- package/src/runtime/channel-readiness-service.ts +59 -3
- package/src/runtime/channel-readiness-types.ts +18 -0
- package/src/runtime/desktop-presence.ts +69 -0
- package/src/runtime/http-server.ts +12 -0
- package/src/runtime/local-actor-identity.ts +11 -9
- package/src/runtime/migrations/__tests__/vbundle-symlink-streaming-importer.test.ts +0 -0
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/client-routes.test.ts +186 -14
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +547 -1
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +162 -7
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +14 -10
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +340 -3
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +28 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +173 -1
- package/src/runtime/routes/__tests__/question-routes.test.ts +64 -0
- package/src/runtime/routes/__tests__/retrospective-routes.test.ts +20 -0
- package/src/runtime/routes/__tests__/skill-history-route.test.ts +116 -0
- package/src/runtime/routes/__tests__/wake-conversation-routes.test.ts +60 -2
- package/src/runtime/routes/archive-utils.ts +16 -12
- package/src/runtime/routes/attachment-routes.ts +13 -6
- package/src/runtime/routes/canned-reply-release.ts +54 -0
- package/src/runtime/routes/channel-verification-routes.ts +2 -27
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +5 -0
- package/src/runtime/routes/client-routes.ts +114 -2
- package/src/runtime/routes/conversation-cli-routes.ts +10 -12
- package/src/runtime/routes/conversation-list-routes.ts +59 -10
- package/src/runtime/routes/conversation-management-routes.ts +47 -18
- package/src/runtime/routes/conversation-query-routes.ts +32 -53
- package/src/runtime/routes/conversation-routes.ts +360 -293
- package/src/runtime/routes/credential-prompt-routes.ts +23 -2
- package/src/runtime/routes/debug-routes.ts +4 -1
- package/src/runtime/routes/default-provider-routes.ts +7 -11
- package/src/runtime/routes/documents-routes.ts +44 -1
- package/src/runtime/routes/events-routes.ts +28 -1
- package/src/runtime/routes/host-app-control-routes.ts +2 -0
- package/src/runtime/routes/host-bash-routes.ts +2 -0
- package/src/runtime/routes/host-browser-routes.ts +2 -0
- package/src/runtime/routes/host-cu-routes.ts +2 -0
- package/src/runtime/routes/host-file-routes.ts +2 -0
- package/src/runtime/routes/host-transfer-routes.ts +4 -0
- package/src/runtime/routes/identity-routes.ts +6 -6
- package/src/runtime/routes/inbound-message-handler.ts +5 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +7 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +14 -0
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +14 -11
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -17
- package/src/runtime/routes/inference-profile-availability-guard.ts +171 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +26 -2
- package/src/runtime/routes/inference-profile-session-routes.ts +2 -1
- package/src/runtime/routes/inference-profiles-routes.ts +152 -73
- package/src/runtime/routes/inference-provider-connection-routes.ts +0 -16
- package/src/runtime/routes/llm-call-sites-routes.ts +13 -0
- package/src/runtime/routes/log-export-routes.ts +1 -1
- package/src/runtime/routes/notification-routes.ts +0 -14
- package/src/runtime/routes/oauth-providers.ts +1 -0
- package/src/runtime/routes/plugins-routes.ts +106 -1
- package/src/runtime/routes/profiler-routes.ts +4 -2
- package/src/runtime/routes/question-routes.ts +28 -10
- package/src/runtime/routes/retrospective-routes.ts +9 -3
- package/src/runtime/routes/schedule-routes.ts +210 -12
- package/src/runtime/routes/secret-routes.ts +40 -23
- package/src/runtime/routes/settings-routes.ts +41 -13
- package/src/runtime/routes/skills-routes.ts +79 -0
- package/src/runtime/routes/ui-snapshot-routes.ts +1 -0
- package/src/runtime/routes/wake-conversation-routes.ts +23 -0
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
- package/src/runtime/services/conversation-serializer.ts +24 -9
- package/src/runtime/sync/documents-sidecar-publish.test.ts +101 -0
- package/src/runtime/sync/resource-sync-events.ts +79 -1
- package/src/runtime/sync/worker-daemon-notify.test.ts +34 -0
- package/src/runtime/sync/worker-daemon-notify.ts +44 -6
- package/src/runtime/verification-outbound-actions.ts +250 -222
- package/src/runtime/verification-templates.ts +41 -30
- package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +614 -0
- package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +1066 -0
- package/src/schedule/inference-profile.ts +64 -3
- package/src/schedule/plugin-schedule-declarations.ts +518 -0
- package/src/schedule/plugin-schedule-reconciler.ts +605 -0
- package/src/schedule/plugin-schedules-gate.ts +23 -0
- package/src/schedule/schedule-store.ts +520 -26
- package/src/schedule/scheduler.ts +38 -0
- package/src/security/credential-backend.ts +51 -4
- package/src/security/encrypted-store.ts +64 -42
- package/src/security/secure-keys.ts +55 -7
- package/src/skills/catalog-install.ts +20 -7
- package/src/skills/inline-command-runner.ts +2 -7
- package/src/skills/skill-history.test.ts +269 -0
- package/src/skills/skill-history.ts +250 -0
- package/src/skills/skillssh-registry.ts +1 -1
- package/src/stt/daemon-batch-transcriber.ts +8 -6
- package/src/subagent/__tests__/consult-prompt.test.ts +25 -0
- package/src/subagent/consult-prompt.ts +3 -1
- package/src/subagent/consult-transcript.ts +1 -1
- package/src/subagent/manager.ts +304 -44
- package/src/subagent/progress-events.ts +35 -0
- package/src/subagent/role-resolution.ts +88 -0
- package/src/subagent/types.ts +302 -67
- package/src/subagent/validate-allowlists.ts +3 -4
- package/src/telegram/__tests__/webhook-health.test.ts +73 -1
- package/src/telegram/webhook-health.ts +101 -3
- package/src/telemetry/__tests__/telemetry-event-fixtures.ts +2 -0
- package/src/telemetry/activation-funnel.ts +6 -1
- package/src/telemetry/live-voice-funnel.ts +75 -0
- package/src/telemetry/telemetry-event-sources.ts +6 -0
- package/src/telemetry/telemetry-wire-source.json +1 -1
- package/src/telemetry/telemetry-wire.generated.ts +2 -0
- package/src/telemetry/types.ts +23 -4
- package/src/telemetry/usage-telemetry-reporter.test.ts +4 -0
- package/src/tools/__tests__/subagent-workflow-tool-input-schemas.test.ts +1 -1
- package/src/tools/ask-question/ask-question-tool.test.ts +118 -3
- package/src/tools/ask-question/ask-question-tool.ts +56 -2
- package/src/tools/browser/browser-execution.ts +1 -1
- package/src/tools/calls/call-start.ts +8 -5
- package/src/tools/conversation-groups/move_to_group.ts +10 -12
- package/src/tools/document/document-tool.ts +11 -4
- package/src/tools/execution-timeout.ts +9 -2
- package/src/tools/executor.ts +7 -6
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +7 -13
- package/src/tools/filesystem/search.ts +23 -21
- package/src/tools/filesystem/write.ts +4 -1
- package/src/tools/host-filesystem/edit.ts +7 -2
- package/src/tools/host-filesystem/read.ts +21 -8
- package/src/tools/host-filesystem/write.ts +10 -2
- package/src/tools/host-terminal/host-shell.ts +2 -2
- package/src/tools/mcp/mcp-tool-factory.ts +25 -1
- package/src/tools/permission-checker.ts +8 -3
- package/src/tools/schedule/delete.ts +13 -1
- package/src/tools/schedule/list.ts +14 -3
- package/src/tools/schedule/update.ts +56 -33
- package/src/tools/shared/filesystem/audio-read.ts +6 -4
- package/src/tools/shared/filesystem/file-ops-service.ts +176 -116
- package/src/tools/shared/filesystem/image-read.ts +12 -12
- package/src/tools/shared/filesystem/path-policy.ts +70 -5
- package/src/tools/shared/filesystem/size-guard.ts +6 -6
- package/src/tools/shared/input-misuse.ts +72 -0
- package/src/tools/skills/scaffold-managed.ts +82 -0
- package/src/tools/skills/skill-tool-factory.ts +14 -1
- package/src/tools/subagent/consult-deadline.ts +12 -7
- package/src/tools/subagent/message.ts +3 -1
- package/src/tools/subagent/read.ts +66 -4
- package/src/tools/subagent/spawn.ts +500 -78
- package/src/tools/tool-approval-handler.ts +45 -4
- package/src/tools/types.ts +22 -3
- package/src/tools/ui-surface/surface-shape-docs.ts +1 -1
- package/src/tools/ui-surface/visual-validation.ts +2 -0
- package/src/usage/__tests__/subagent-attribution.test.ts +109 -0
- package/src/usage/subagent-attribution.ts +108 -0
- package/src/util/__tests__/sqlite-retry.test.ts +112 -0
- package/src/util/abort-reasons.ts +16 -0
- package/src/util/ansi.ts +44 -0
- package/src/util/disk-usage.ts +17 -13
- package/src/util/image-conversion.ts +20 -13
- package/src/util/schedule-source-key.ts +23 -0
- package/src/util/worker-process.ts +7 -1
- package/src/workspace/git-service.ts +33 -9
- package/src/workspace/migrations/140-repair-seed-pinned-memory-v3-live.ts +260 -0
- package/src/workspace/migrations/141-stt-english-default-to-multilingual.ts +111 -0
- package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +126 -0
- package/src/workspace/migrations/registry.ts +4 -0
- package/src/workspace/top-level-renderer.ts +15 -2
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +0 -357
- package/src/daemon/install-symlink.ts +0 -207
- package/src/notifications/deferred-emit.ts +0 -147
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Write-time availability guard for inference-profile selections.
|
|
3
|
+
*
|
|
4
|
+
* The profile write routes (create, update, set-active, conversation-scoped
|
|
5
|
+
* pin) refuse to persist a selection that provably cannot dispatch. A profile
|
|
6
|
+
* naming a valid provider id with no connection behind it parses fine and
|
|
7
|
+
* writes fine, but the next chat turn fails with "No provider connection is
|
|
8
|
+
* configured" — a lockout the user has to unwind by hand. Judging the entry
|
|
9
|
+
* the way dispatch will, at write time, turns that into a rejected write with
|
|
10
|
+
* a repair path.
|
|
11
|
+
*
|
|
12
|
+
* This module owns the guard's copy so the four write sites word it
|
|
13
|
+
* identically.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { ROUTING_IDENTITY_PROVIDERS } from "../../providers/inference/auth.js";
|
|
17
|
+
import type { ConnectionAvailability } from "../../providers/inference/connection-availability.js";
|
|
18
|
+
import { vellumConnectionAvailability } from "../../providers/inference/connection-availability.js";
|
|
19
|
+
import { getManagedUpstream } from "../../providers/vellum-model-routing.js";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* How the caller should repair the selection. `create`/`update` describe an
|
|
23
|
+
* in-flight write; `repoint` describes an already-persisted profile that some
|
|
24
|
+
* other write (set-active, session pin) is trying to select.
|
|
25
|
+
*/
|
|
26
|
+
export type ProfileRepairHint =
|
|
27
|
+
| { kind: "create" }
|
|
28
|
+
| { kind: "update" }
|
|
29
|
+
| { kind: "repoint"; profileName: string };
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Shell-quote a value interpolated into a generated command. Profile names
|
|
33
|
+
* (and custom-endpoint model ids) are arbitrary strings, and the generated
|
|
34
|
+
* commands are explicitly meant to be run — an unquoted name with whitespace
|
|
35
|
+
* or metacharacters would break, or worse, execute as extra shell words.
|
|
36
|
+
* Values made only of conventional id characters pass through unquoted so the
|
|
37
|
+
* common case stays readable.
|
|
38
|
+
*/
|
|
39
|
+
function shellQuote(value: string): string {
|
|
40
|
+
if (/^[A-Za-z0-9./:_-]+$/.test(value)) {
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
return `'${value.replaceAll("'", `'\\''`)}'`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Command that proves a profile actually dispatches. Surfaced in write results
|
|
48
|
+
* and in guard errors — `--json` callers (agents) never see the CLI's
|
|
49
|
+
* human-mode hint, so the nudge has to travel in the payload.
|
|
50
|
+
*/
|
|
51
|
+
export function verifyProfileCommand(name: string): string {
|
|
52
|
+
return `assistant inference send --profile ${shellQuote(name)} "Reply with OK"`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function managedAlternative(model: string, repair: ProfileRepairHint): string {
|
|
56
|
+
const served =
|
|
57
|
+
"this model is served by your signed-in Vellum-managed route (no API key needed)";
|
|
58
|
+
const quotedModel = shellQuote(model);
|
|
59
|
+
switch (repair.kind) {
|
|
60
|
+
case "create":
|
|
61
|
+
return `Recreate with --provider vellum --model ${quotedModel} — ${served}.`;
|
|
62
|
+
case "update":
|
|
63
|
+
return `Update it with --provider vellum --model ${quotedModel} — ${served}.`;
|
|
64
|
+
case "repoint":
|
|
65
|
+
return `Repoint it first with "assistant inference profiles update ${shellQuote(repair.profileName)} --provider vellum --model ${quotedModel}" — ${served}.`;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const PROMPT_FOR_KEY = (provider: string): string =>
|
|
70
|
+
`"assistant credentials prompt --service ${provider} --field api_key" ` +
|
|
71
|
+
`(secure inline collection — never ask for the key in chat)`;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Repair for a connection that exists but has no stored key: collecting the
|
|
75
|
+
* key is the whole fix — suggesting a `providers create` here would collide
|
|
76
|
+
* with the existing row.
|
|
77
|
+
*/
|
|
78
|
+
function storeMissingKey(provider: string): string {
|
|
79
|
+
return `To store the missing key, run ${PROMPT_FOR_KEY(provider)}, then retry.`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function byoSequence(provider: string): string {
|
|
83
|
+
return (
|
|
84
|
+
`To use "${provider}" directly, run ` +
|
|
85
|
+
`${PROMPT_FOR_KEY(provider)}, then ` +
|
|
86
|
+
`"assistant inference providers create ${provider}-personal --provider ${provider} --credential credential/${provider}/api_key", then retry.`
|
|
87
|
+
);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Build the repair guidance for an unavailable selection: the exact
|
|
92
|
+
* Vellum-managed alternative when the model is managed-routable and the
|
|
93
|
+
* managed route is usable; otherwise key collection alone when a connection
|
|
94
|
+
* already exists and only its credential is missing, or the full
|
|
95
|
+
* bring-your-own-key sequence when there is no connection at all.
|
|
96
|
+
*/
|
|
97
|
+
async function repairGuidance(
|
|
98
|
+
provider: string,
|
|
99
|
+
model: string | undefined,
|
|
100
|
+
repair: ProfileRepairHint,
|
|
101
|
+
status: ConnectionAvailability["status"],
|
|
102
|
+
): Promise<string> {
|
|
103
|
+
// An incomplete profile has no connection to diagnose: the repair is
|
|
104
|
+
// finishing the profile itself. Its `provider` may also be absent, which
|
|
105
|
+
// the branches below would interpolate as the string "undefined".
|
|
106
|
+
if (status === "incomplete") {
|
|
107
|
+
return "Set both a provider and a model on the profile in Settings → Models & Services, then retry.";
|
|
108
|
+
}
|
|
109
|
+
if (ROUTING_IDENTITY_PROVIDERS.has(provider)) {
|
|
110
|
+
return `Sign in to the "${provider}" route in Settings → Models & Services, or pick a profile backed by a provider API key, then retry.`;
|
|
111
|
+
}
|
|
112
|
+
if (model && getManagedUpstream(model) !== null) {
|
|
113
|
+
const managed = await vellumConnectionAvailability();
|
|
114
|
+
if (managed.status === "ok") {
|
|
115
|
+
return managedAlternative(model, repair);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
if (status === "missing_credential") {
|
|
119
|
+
return storeMissingKey(provider);
|
|
120
|
+
}
|
|
121
|
+
return byoSequence(provider);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Agent-legible explanation of why a profile cannot serve requests, followed
|
|
126
|
+
* by the repair path and (for in-flight writes) the escape hatch.
|
|
127
|
+
*/
|
|
128
|
+
export async function describeUnavailableProfile({
|
|
129
|
+
availability,
|
|
130
|
+
provider,
|
|
131
|
+
model,
|
|
132
|
+
repair,
|
|
133
|
+
escapeHatch,
|
|
134
|
+
}: {
|
|
135
|
+
availability: ConnectionAvailability;
|
|
136
|
+
provider: string;
|
|
137
|
+
model?: string;
|
|
138
|
+
repair: ProfileRepairHint;
|
|
139
|
+
/** Whether the caller accepts `allowUnavailable` to force the write. */
|
|
140
|
+
escapeHatch: boolean;
|
|
141
|
+
}): Promise<string> {
|
|
142
|
+
const parts = [
|
|
143
|
+
availability.message ??
|
|
144
|
+
`Provider "${provider}" cannot serve requests (${availability.status}).`,
|
|
145
|
+
await repairGuidance(provider, model, repair, availability.status),
|
|
146
|
+
];
|
|
147
|
+
if (escapeHatch) {
|
|
148
|
+
parts.push(
|
|
149
|
+
"Pass --allow-unavailable (allowUnavailable over HTTP) to write it anyway — only do that when intentionally pre-staging config you will finish later.",
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
if (repair.kind === "repoint") {
|
|
153
|
+
parts.push(`Then verify with: ${verifyProfileCommand(repair.profileName)}`);
|
|
154
|
+
}
|
|
155
|
+
return parts.join(" ");
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* Warning recorded on a forced (`allowUnavailable`) write so the response still
|
|
160
|
+
* names the breakage the guard would have blocked.
|
|
161
|
+
*/
|
|
162
|
+
export function unavailableProfileWarning(
|
|
163
|
+
name: string,
|
|
164
|
+
availability: ConnectionAvailability,
|
|
165
|
+
): string {
|
|
166
|
+
return (
|
|
167
|
+
`Profile "${name}" cannot serve requests yet (${availability.status})` +
|
|
168
|
+
`${availability.message ? `: ${availability.message}` : "."} ` +
|
|
169
|
+
`Written anyway (allowUnavailable). Verify with: ${verifyProfileCommand(name)}`
|
|
170
|
+
);
|
|
171
|
+
}
|
|
@@ -23,8 +23,14 @@ import {
|
|
|
23
23
|
setConversationInferenceProfileSession,
|
|
24
24
|
} from "../../persistence/conversation-crud.js";
|
|
25
25
|
import { resolveConversationId } from "../../persistence/conversation-key-store.js";
|
|
26
|
+
import type { ConnectionAvailability } from "../../providers/inference/connection-availability.js";
|
|
27
|
+
import {
|
|
28
|
+
computeProfileAvailability,
|
|
29
|
+
isUnavailable,
|
|
30
|
+
} from "../../providers/inference/connection-availability.js";
|
|
26
31
|
import { publishConversationInferenceProfileChanged } from "../sync/resource-sync-events.js";
|
|
27
32
|
import { BadRequestError, NotFoundError } from "./errors.js";
|
|
33
|
+
import { describeUnavailableProfile } from "./inference-profile-availability-guard.js";
|
|
28
34
|
|
|
29
35
|
// ---------------------------------------------------------------------------
|
|
30
36
|
// Types
|
|
@@ -52,8 +58,9 @@ export interface InferenceProfileSessionResult {
|
|
|
52
58
|
* conversation.
|
|
53
59
|
*
|
|
54
60
|
* - `profile === null`: clears any existing override (session or non-session).
|
|
55
|
-
* - `profile` is a string: validates it against `llm.profiles
|
|
56
|
-
*
|
|
61
|
+
* - `profile` is a string: validates it against `llm.profiles` and against the
|
|
62
|
+
* dispatch-availability guard (a profile that cannot serve requests is
|
|
63
|
+
* refused rather than pinned), then applies it. TTL handling:
|
|
57
64
|
* - `ttlSeconds` is a positive number → clamp to
|
|
58
65
|
* `[1, llm.profileSession.maxTtlSeconds]`, mint a sessionId, compute
|
|
59
66
|
* expiresAt.
|
|
@@ -156,6 +163,23 @@ export async function setInferenceProfileSession({
|
|
|
156
163
|
);
|
|
157
164
|
}
|
|
158
165
|
|
|
166
|
+
// Pinning a profile that provably cannot dispatch turns the next turn in
|
|
167
|
+
// this conversation into a hard failure, so refuse the pin the same way the
|
|
168
|
+
// active-profile setter does.
|
|
169
|
+
const entry = profiles[profile] as Record<string, unknown>;
|
|
170
|
+
const availability = await computeProfileAvailability(entry);
|
|
171
|
+
if (isUnavailable(availability)) {
|
|
172
|
+
throw new BadRequestError(
|
|
173
|
+
await describeUnavailableProfile({
|
|
174
|
+
availability: availability as ConnectionAvailability,
|
|
175
|
+
provider: String(entry.provider),
|
|
176
|
+
model: typeof entry.model === "string" ? entry.model : undefined,
|
|
177
|
+
repair: { kind: "repoint", profileName: profile },
|
|
178
|
+
escapeHatch: false,
|
|
179
|
+
}),
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
|
|
159
183
|
// --- Compute session fields based on ttlSeconds ---
|
|
160
184
|
let newSessionId: string | null;
|
|
161
185
|
let newExpiresAt: number | null;
|
|
@@ -73,7 +73,8 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
73
73
|
summary: "Open an inference-profile session",
|
|
74
74
|
description:
|
|
75
75
|
"Open (or replace) a session-backed inference-profile override for a conversation. " +
|
|
76
|
-
"Supports an optional TTL — omit for a sticky (non-expiring) override."
|
|
76
|
+
"Supports an optional TTL — omit for a sticky (non-expiring) override. " +
|
|
77
|
+
"A profile that provably cannot dispatch is rejected rather than pinned.",
|
|
77
78
|
tags: ["conversations"],
|
|
78
79
|
requestBody: z.object({
|
|
79
80
|
conversationId: z.string().min(1),
|
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Unlike the generic `config set llm.profiles.<name> '<json>'` path, these
|
|
11
11
|
* routes validate at write time: the provider must be a known `LLMProvider`,
|
|
12
|
-
* the model must be in the catalog (unless `allowUnlisted`),
|
|
13
|
-
* connection must exist
|
|
12
|
+
* the model must be in the catalog (unless `allowUnlisted`), a referenced
|
|
13
|
+
* connection must exist, and the resulting profile must be able to dispatch
|
|
14
|
+
* (unless `allowUnavailable`). Writes reuse the shared config-write plumbing
|
|
14
15
|
* (`commitConfigWrite` + the managed-profile guards), so a CLI-created profile
|
|
15
16
|
* is completed/materialized identically to a UI-created one.
|
|
16
17
|
*/
|
|
@@ -33,14 +34,18 @@ import {
|
|
|
33
34
|
routingIdentityModelIssue,
|
|
34
35
|
} from "../../config/schemas/llm.js";
|
|
35
36
|
import { getDb } from "../../persistence/db-connection.js";
|
|
36
|
-
import {
|
|
37
|
-
ConnectionResolutionError,
|
|
38
|
-
resolveRoutingIdentity,
|
|
39
|
-
} from "../../providers/connection-resolution.js";
|
|
40
37
|
import { ROUTING_IDENTITY_PROVIDERS } from "../../providers/inference/auth.js";
|
|
41
|
-
import {
|
|
38
|
+
import type { ConnectionAvailability } from "../../providers/inference/connection-availability.js";
|
|
39
|
+
import {
|
|
40
|
+
computeProfileAvailability,
|
|
41
|
+
CONNECTION_AVAILABILITY_STATUSES,
|
|
42
|
+
isUnavailable,
|
|
43
|
+
} from "../../providers/inference/connection-availability.js";
|
|
42
44
|
import { getConnection } from "../../providers/inference/connections.js";
|
|
43
|
-
import {
|
|
45
|
+
import {
|
|
46
|
+
getModelDisplayName,
|
|
47
|
+
isModelInCatalog,
|
|
48
|
+
} from "../../providers/model-catalog.js";
|
|
44
49
|
import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
|
|
45
50
|
import {
|
|
46
51
|
commitConfigWrite,
|
|
@@ -48,6 +53,12 @@ import {
|
|
|
48
53
|
rejectManagedProfileDeletion,
|
|
49
54
|
} from "./conversation-query-routes.js";
|
|
50
55
|
import { BadRequestError, ConflictError, NotFoundError } from "./errors.js";
|
|
56
|
+
import {
|
|
57
|
+
describeUnavailableProfile,
|
|
58
|
+
type ProfileRepairHint,
|
|
59
|
+
unavailableProfileWarning,
|
|
60
|
+
verifyProfileCommand,
|
|
61
|
+
} from "./inference-profile-availability-guard.js";
|
|
51
62
|
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
52
63
|
|
|
53
64
|
// Prototype-pollution guards — a profile name may never be one of these.
|
|
@@ -63,15 +74,7 @@ const RESERVED_PROFILE_NAMES = new Set([
|
|
|
63
74
|
|
|
64
75
|
const availabilitySchema = z
|
|
65
76
|
.object({
|
|
66
|
-
status: z.enum(
|
|
67
|
-
"ok",
|
|
68
|
-
"missing_connection",
|
|
69
|
-
"missing_credential",
|
|
70
|
-
"provider_mismatch",
|
|
71
|
-
"unsupported_auth",
|
|
72
|
-
"vellum_unauthenticated",
|
|
73
|
-
"unknown",
|
|
74
|
-
]),
|
|
77
|
+
status: z.enum(CONNECTION_AVAILABILITY_STATUSES),
|
|
75
78
|
message: z.string().optional(),
|
|
76
79
|
})
|
|
77
80
|
.meta({ id: "ProfileConnectionAvailability" });
|
|
@@ -85,7 +88,7 @@ const profileSummarySchema = z
|
|
|
85
88
|
status: z.enum(["active", "disabled"]),
|
|
86
89
|
source: z.enum(["managed", "user"]),
|
|
87
90
|
provider_connection: z.string().optional(),
|
|
88
|
-
/** Null when the profile has no
|
|
91
|
+
/** Null when the profile has no provider to judge (e.g. mix profiles). */
|
|
89
92
|
availability: availabilitySchema.nullable(),
|
|
90
93
|
})
|
|
91
94
|
.meta({ id: "InferenceProfileSummary" });
|
|
@@ -104,6 +107,12 @@ const profileWriteResultSchema = z
|
|
|
104
107
|
name: z.string(),
|
|
105
108
|
entry: z.record(z.string(), z.unknown()),
|
|
106
109
|
warnings: z.array(z.string()),
|
|
110
|
+
/**
|
|
111
|
+
* One-shot command that proves the profile dispatches. The CLI suppresses
|
|
112
|
+
* its human-mode hint under `--json`, which is the mode agents use, so the
|
|
113
|
+
* nudge travels in the payload.
|
|
114
|
+
*/
|
|
115
|
+
verify: z.string(),
|
|
107
116
|
})
|
|
108
117
|
.meta({ id: "InferenceProfileWriteResult" });
|
|
109
118
|
|
|
@@ -119,6 +128,7 @@ const createRequestSchema = z.object({
|
|
|
119
128
|
thinking: z.boolean().optional(),
|
|
120
129
|
description: z.string().optional(),
|
|
121
130
|
allowUnlisted: z.boolean().optional(),
|
|
131
|
+
allowUnavailable: z.boolean().optional(),
|
|
122
132
|
});
|
|
123
133
|
|
|
124
134
|
const updateRequestSchema = z.object({
|
|
@@ -132,6 +142,7 @@ const updateRequestSchema = z.object({
|
|
|
132
142
|
thinking: z.boolean().optional(),
|
|
133
143
|
description: z.string().optional(),
|
|
134
144
|
allowUnlisted: z.boolean().optional(),
|
|
145
|
+
allowUnavailable: z.boolean().optional(),
|
|
135
146
|
});
|
|
136
147
|
|
|
137
148
|
// ---------------------------------------------------------------------------
|
|
@@ -155,6 +166,7 @@ function validateModel(
|
|
|
155
166
|
provider: string,
|
|
156
167
|
model: string,
|
|
157
168
|
allowUnlisted: boolean,
|
|
169
|
+
connectionName?: string,
|
|
158
170
|
): string[] {
|
|
159
171
|
// Routing identities key no catalog entries; they validate against their
|
|
160
172
|
// route's actual reach — the same checks dispatch applies per-request.
|
|
@@ -173,11 +185,24 @@ function validateModel(
|
|
|
173
185
|
if (isModelInCatalog(provider, model)) {
|
|
174
186
|
return [];
|
|
175
187
|
}
|
|
188
|
+
// The named connection's advertised model list is authoritative for models
|
|
189
|
+
// the code-owned catalog doesn't know — a custom (openai-compatible)
|
|
190
|
+
// endpoint declares its own models at connection-create time.
|
|
191
|
+
if (connectionName) {
|
|
192
|
+
const connection = getConnection(getDb(), connectionName);
|
|
193
|
+
if (connection?.models?.some((m) => m.id === model)) {
|
|
194
|
+
return [];
|
|
195
|
+
}
|
|
196
|
+
}
|
|
176
197
|
if (!allowUnlisted) {
|
|
198
|
+
const remedy =
|
|
199
|
+
provider === "openai-compatible"
|
|
200
|
+
? `Pass allowUnlisted to create it anyway, or declare the model on the connection ` +
|
|
201
|
+
`("assistant inference providers update <name> --model ${model}").`
|
|
202
|
+
: `Pass allowUnlisted to create it anyway, or run ` +
|
|
203
|
+
`"assistant inference models list --provider ${provider}" to see valid ids.`;
|
|
177
204
|
throw new BadRequestError(
|
|
178
|
-
`Model "${model}" is not in the catalog for provider "${provider}".
|
|
179
|
-
`Pass allowUnlisted to create it anyway, or run ` +
|
|
180
|
-
`"assistant inference models list --provider ${provider}" to see valid ids.`,
|
|
205
|
+
`Model "${model}" is not in the catalog for provider "${provider}". ${remedy}`,
|
|
181
206
|
);
|
|
182
207
|
}
|
|
183
208
|
return [
|
|
@@ -202,46 +227,38 @@ function asPlainObject(value: unknown): Record<string, unknown> | null {
|
|
|
202
227
|
}
|
|
203
228
|
|
|
204
229
|
/**
|
|
205
|
-
*
|
|
206
|
-
*
|
|
230
|
+
* Refuse a write that would persist a profile which provably cannot dispatch.
|
|
231
|
+
* Returns the offending verdict when the caller forced the write with
|
|
232
|
+
* `allowUnavailable`, so the handler can warn instead; returns null when the
|
|
233
|
+
* profile is fine or indeterminate.
|
|
207
234
|
*/
|
|
208
|
-
async function
|
|
209
|
-
entry
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
235
|
+
async function guardProfileAvailability({
|
|
236
|
+
entry,
|
|
237
|
+
repair,
|
|
238
|
+
allowUnavailable,
|
|
239
|
+
}: {
|
|
240
|
+
entry: Record<string, unknown>;
|
|
241
|
+
repair: ProfileRepairHint;
|
|
242
|
+
allowUnavailable: boolean;
|
|
243
|
+
}): Promise<ConnectionAvailability | null> {
|
|
244
|
+
const availability = await computeProfileAvailability(entry);
|
|
245
|
+
if (!isUnavailable(availability)) {
|
|
214
246
|
return null;
|
|
215
247
|
}
|
|
216
|
-
//
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
);
|
|
231
|
-
} catch (err) {
|
|
232
|
-
return {
|
|
233
|
-
status: "provider_mismatch",
|
|
234
|
-
message:
|
|
235
|
-
err instanceof ConnectionResolutionError
|
|
236
|
-
? err.message
|
|
237
|
-
: `Model "${model ?? "<unset>"}" cannot be routed by provider "${provider}".`,
|
|
238
|
-
};
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
if (typeof connection !== "string") {
|
|
242
|
-
return null;
|
|
243
|
-
}
|
|
244
|
-
return computeConnectionAvailability(provider, connection);
|
|
248
|
+
// isUnavailable() is only true for a non-null verdict.
|
|
249
|
+
const verdict = availability as ConnectionAvailability;
|
|
250
|
+
if (allowUnavailable) {
|
|
251
|
+
return verdict;
|
|
252
|
+
}
|
|
253
|
+
throw new BadRequestError(
|
|
254
|
+
await describeUnavailableProfile({
|
|
255
|
+
availability: verdict,
|
|
256
|
+
provider: String(entry.provider),
|
|
257
|
+
model: typeof entry.model === "string" ? entry.model : undefined,
|
|
258
|
+
repair,
|
|
259
|
+
escapeHatch: repair.kind !== "repoint",
|
|
260
|
+
}),
|
|
261
|
+
);
|
|
245
262
|
}
|
|
246
263
|
|
|
247
264
|
function ensureRawProfiles(
|
|
@@ -370,7 +387,7 @@ async function handleListProfiles() {
|
|
|
370
387
|
...(typeof record.provider_connection === "string"
|
|
371
388
|
? { provider_connection: record.provider_connection }
|
|
372
389
|
: {}),
|
|
373
|
-
availability: await
|
|
390
|
+
availability: await computeProfileAvailability(record),
|
|
374
391
|
};
|
|
375
392
|
}),
|
|
376
393
|
);
|
|
@@ -395,7 +412,7 @@ async function handleGetProfile({ pathParams = {} }: RouteHandlerArgs) {
|
|
|
395
412
|
return {
|
|
396
413
|
name,
|
|
397
414
|
entry: record,
|
|
398
|
-
availability: await
|
|
415
|
+
availability: await computeProfileAvailability(record),
|
|
399
416
|
};
|
|
400
417
|
}
|
|
401
418
|
|
|
@@ -421,14 +438,15 @@ async function handleCreateProfile({ body = {} }: RouteHandlerArgs) {
|
|
|
421
438
|
}
|
|
422
439
|
|
|
423
440
|
assertValidProvider(input.provider);
|
|
441
|
+
if (input.connection) {
|
|
442
|
+
assertConnectionExists(input.connection);
|
|
443
|
+
}
|
|
424
444
|
const warnings = validateModel(
|
|
425
445
|
input.provider,
|
|
426
446
|
input.model,
|
|
427
447
|
input.allowUnlisted ?? false,
|
|
448
|
+
input.connection,
|
|
428
449
|
);
|
|
429
|
-
if (input.connection) {
|
|
430
|
-
assertConnectionExists(input.connection);
|
|
431
|
-
}
|
|
432
450
|
|
|
433
451
|
const entry: Record<string, unknown> = {
|
|
434
452
|
...fragmentFromBody(body as Record<string, unknown>),
|
|
@@ -436,6 +454,22 @@ async function handleCreateProfile({ body = {} }: RouteHandlerArgs) {
|
|
|
436
454
|
model: input.model,
|
|
437
455
|
source: "user",
|
|
438
456
|
};
|
|
457
|
+
// Pickers render the label, so a label-less profile shows its raw config
|
|
458
|
+
// key (e.g. "gemini-latest"). Default it to the model's human-readable
|
|
459
|
+
// display name — the catalog's, or the named connection's for custom
|
|
460
|
+
// endpoints — so an unlabeled create still reads well in the UI.
|
|
461
|
+
if (entry.label === undefined) {
|
|
462
|
+
const displayName =
|
|
463
|
+
getModelDisplayName(input.model) ??
|
|
464
|
+
(input.connection
|
|
465
|
+
? getConnection(getDb(), input.connection)?.models?.find(
|
|
466
|
+
(m) => m.id === input.model,
|
|
467
|
+
)?.displayName
|
|
468
|
+
: undefined);
|
|
469
|
+
if (displayName) {
|
|
470
|
+
entry.label = displayName;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
439
473
|
validateProfileEntry(entry);
|
|
440
474
|
|
|
441
475
|
const raw = loadRawConfig();
|
|
@@ -445,6 +479,16 @@ async function handleCreateProfile({ body = {} }: RouteHandlerArgs) {
|
|
|
445
479
|
`Profile "${name}" already exists. Use update to modify it.`,
|
|
446
480
|
);
|
|
447
481
|
}
|
|
482
|
+
|
|
483
|
+
const forced = await guardProfileAvailability({
|
|
484
|
+
entry,
|
|
485
|
+
repair: { kind: "create" },
|
|
486
|
+
allowUnavailable: input.allowUnavailable ?? false,
|
|
487
|
+
});
|
|
488
|
+
if (forced) {
|
|
489
|
+
warnings.push(unavailableProfileWarning(name, forced));
|
|
490
|
+
}
|
|
491
|
+
|
|
448
492
|
profiles[name] = entry;
|
|
449
493
|
// Defensive: for a user-owned name this is a no-op; it re-asserts the
|
|
450
494
|
// managed-name protection at the shared write choke point.
|
|
@@ -461,6 +505,7 @@ async function handleCreateProfile({ body = {} }: RouteHandlerArgs) {
|
|
|
461
505
|
getConfig().llm.defaultProvider ?? null,
|
|
462
506
|
) ?? entry) as Record<string, unknown>,
|
|
463
507
|
warnings,
|
|
508
|
+
verify: verifyProfileCommand(name),
|
|
464
509
|
};
|
|
465
510
|
}
|
|
466
511
|
|
|
@@ -511,6 +556,14 @@ async function handleUpdateProfile({
|
|
|
511
556
|
assertValidProvider(input.provider);
|
|
512
557
|
}
|
|
513
558
|
let warnings: string[] = [];
|
|
559
|
+
if (input.connection) {
|
|
560
|
+
assertConnectionExists(input.connection);
|
|
561
|
+
}
|
|
562
|
+
const nextConnection =
|
|
563
|
+
input.connection ??
|
|
564
|
+
(typeof existing.provider_connection === "string"
|
|
565
|
+
? existing.provider_connection
|
|
566
|
+
: undefined);
|
|
514
567
|
if (
|
|
515
568
|
(input.provider !== undefined || input.model !== undefined) &&
|
|
516
569
|
typeof nextProvider === "string" &&
|
|
@@ -520,11 +573,9 @@ async function handleUpdateProfile({
|
|
|
520
573
|
nextProvider,
|
|
521
574
|
nextModel,
|
|
522
575
|
input.allowUnlisted ?? false,
|
|
576
|
+
nextConnection,
|
|
523
577
|
);
|
|
524
578
|
}
|
|
525
|
-
if (input.connection) {
|
|
526
|
-
assertConnectionExists(input.connection);
|
|
527
|
-
}
|
|
528
579
|
|
|
529
580
|
const merged: Record<string, unknown> = {
|
|
530
581
|
...existing,
|
|
@@ -536,6 +587,24 @@ async function handleUpdateProfile({
|
|
|
536
587
|
};
|
|
537
588
|
validateProfileEntry(merged);
|
|
538
589
|
|
|
590
|
+
// The availability guard runs only when the write touches the fields that
|
|
591
|
+
// determine dispatchability (provider/model/connection) — metadata-only
|
|
592
|
+
// edits to a pre-staged profile must not require the escape hatch.
|
|
593
|
+
if (
|
|
594
|
+
input.provider !== undefined ||
|
|
595
|
+
input.model !== undefined ||
|
|
596
|
+
input.connection !== undefined
|
|
597
|
+
) {
|
|
598
|
+
const forced = await guardProfileAvailability({
|
|
599
|
+
entry: merged,
|
|
600
|
+
repair: { kind: "update" },
|
|
601
|
+
allowUnavailable: input.allowUnavailable ?? false,
|
|
602
|
+
});
|
|
603
|
+
if (forced) {
|
|
604
|
+
warnings.push(unavailableProfileWarning(name, forced));
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
|
|
539
608
|
profiles[name] = merged;
|
|
540
609
|
normalizeManagedProfileWrites({ llm: { profiles: { [name]: merged } } });
|
|
541
610
|
|
|
@@ -550,6 +619,7 @@ async function handleUpdateProfile({
|
|
|
550
619
|
getConfig().llm.defaultProvider ?? null,
|
|
551
620
|
) ?? merged) as Record<string, unknown>,
|
|
552
621
|
warnings,
|
|
622
|
+
verify: verifyProfileCommand(name),
|
|
553
623
|
};
|
|
554
624
|
}
|
|
555
625
|
|
|
@@ -624,6 +694,13 @@ async function handleSetActiveProfile({ body = {} }: RouteHandlerArgs) {
|
|
|
624
694
|
`Profile "${name}" is disabled and cannot be set as the active profile. Enable it first, or pick another.`,
|
|
625
695
|
);
|
|
626
696
|
}
|
|
697
|
+
// No escape hatch here: an active profile that cannot dispatch locks the
|
|
698
|
+
// user out of chat entirely, and nothing about the write signals that.
|
|
699
|
+
await guardProfileAvailability({
|
|
700
|
+
entry,
|
|
701
|
+
repair: { kind: "repoint", profileName: name },
|
|
702
|
+
allowUnavailable: false,
|
|
703
|
+
});
|
|
627
704
|
|
|
628
705
|
const raw = loadRawConfig();
|
|
629
706
|
const existingLlm = asPlainObject(raw.llm);
|
|
@@ -685,7 +762,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
685
762
|
},
|
|
686
763
|
summary: "Create an inference profile",
|
|
687
764
|
description:
|
|
688
|
-
"Create a validated custom profile. The provider must be a known LLM provider, the model must be in the catalog (unless allowUnlisted),
|
|
765
|
+
"Create a validated custom profile. The provider must be a known LLM provider, the model must be in the catalog (unless allowUnlisted), a referenced connection must exist, and the profile must be able to dispatch — a valid provider id with no credentialed connection behind it is rejected unless allowUnavailable is set, in which case it is created with a warning.",
|
|
689
766
|
tags: ["inference"],
|
|
690
767
|
requestBody: createRequestSchema,
|
|
691
768
|
responseBody: profileWriteResultSchema,
|
|
@@ -693,7 +770,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
693
770
|
additionalResponses: {
|
|
694
771
|
"400": {
|
|
695
772
|
description:
|
|
696
|
-
"Invalid provider, uncataloged model, or
|
|
773
|
+
"Invalid provider, uncataloged model, missing connection, or a profile that cannot serve requests",
|
|
697
774
|
},
|
|
698
775
|
"409": { description: "A profile with this name already exists" },
|
|
699
776
|
},
|
|
@@ -709,14 +786,15 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
709
786
|
},
|
|
710
787
|
summary: "Update an inference profile",
|
|
711
788
|
description:
|
|
712
|
-
"Partial update of a custom profile with the same write-time validation as create. Managed default profiles are read-only.",
|
|
789
|
+
"Partial update of a custom profile with the same write-time validation as create. The dispatch-availability guard (and its allowUnavailable escape hatch) applies when the update changes provider, model, or connection; metadata-only edits skip it. Managed default profiles are read-only.",
|
|
713
790
|
tags: ["inference"],
|
|
714
791
|
pathParams: [{ name: "name", description: "Profile name" }],
|
|
715
792
|
requestBody: updateRequestSchema,
|
|
716
793
|
responseBody: profileWriteResultSchema,
|
|
717
794
|
additionalResponses: {
|
|
718
795
|
"400": {
|
|
719
|
-
description:
|
|
796
|
+
description:
|
|
797
|
+
"Invalid fields, a profile that cannot serve requests, or attempt to edit a managed profile",
|
|
720
798
|
},
|
|
721
799
|
"404": { description: "Profile not found" },
|
|
722
800
|
},
|
|
@@ -741,7 +819,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
741
819
|
"404": { description: "Profile not found" },
|
|
742
820
|
"409": {
|
|
743
821
|
description:
|
|
744
|
-
"Profile is still referenced by activeProfile, advisorProfile, a call site, or a mix arm",
|
|
822
|
+
"Profile is still referenced by activeProfile, advisorProfile, a call site, a default-tier override, or a mix arm",
|
|
745
823
|
},
|
|
746
824
|
},
|
|
747
825
|
handler: handleDeleteProfile,
|
|
@@ -756,7 +834,7 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
756
834
|
},
|
|
757
835
|
summary: "Set the active (chat) inference profile",
|
|
758
836
|
description:
|
|
759
|
-
"Set llm.activeProfile after validating the name against the effective profile catalog (provider-aware default expansion included). Unknown or disabled profiles are rejected so the chat-model selection cannot be silently stripped on the next config load.",
|
|
837
|
+
"Set llm.activeProfile after validating the name against the effective profile catalog (provider-aware default expansion included). Unknown or disabled profiles are rejected so the chat-model selection cannot be silently stripped on the next config load. A profile that provably cannot dispatch is also rejected — there is no escape hatch, because an unusable active profile locks the user out of chat.",
|
|
760
838
|
tags: ["inference"],
|
|
761
839
|
requestBody: setActiveRequestSchema,
|
|
762
840
|
responseBody: z.object({
|
|
@@ -765,7 +843,8 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
765
843
|
}),
|
|
766
844
|
additionalResponses: {
|
|
767
845
|
"400": {
|
|
768
|
-
description:
|
|
846
|
+
description:
|
|
847
|
+
"Unknown or disabled profile name, or a profile that cannot serve requests",
|
|
769
848
|
},
|
|
770
849
|
},
|
|
771
850
|
handler: handleSetActiveProfile,
|