@vellumai/assistant 0.11.3 → 0.11.4-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 +11 -6
- package/docs/architecture/memory.md +11 -0
- package/docs/architecture/turn-actor.md +70 -0
- package/docs/flux-turn-detection-spike.md +243 -0
- package/docs/stt-provider-onboarding.md +3 -1
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +34 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/openapi.yaml +139 -37
- package/package.json +1 -1
- package/scripts/voice-ttft-spike.ts +3 -3
- package/src/__tests__/app-compiler.test.ts +38 -3
- package/src/__tests__/attachments-store.test.ts +21 -12
- package/src/__tests__/byok-default-profile-ensure.test.ts +17 -0
- package/src/__tests__/call-setup-flow-name-capture.test.ts +0 -1
- package/src/__tests__/call-site-routing-provider.test.ts +1 -1
- package/src/__tests__/channel-availability-routes.test.ts +14 -1
- package/src/__tests__/channel-capabilities-dedupe.test.ts +214 -0
- package/src/__tests__/channel-delivery-store.test.ts +14 -14
- package/src/__tests__/config-loader-backfill.test.ts +3 -3
- package/src/__tests__/config-schema.test.ts +25 -10
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +8 -11
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +8 -11
- package/src/__tests__/conversation-agent-loop.test.ts +28 -20
- package/src/__tests__/conversation-attention-store.test.ts +63 -0
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -4
- package/src/__tests__/conversation-fork-crud.test.ts +69 -0
- package/src/__tests__/conversation-fork-referential.test.ts +67 -0
- package/src/__tests__/conversation-fork-retrospective.test.ts +24 -0
- package/src/__tests__/conversation-notifiers-provenance.test.ts +59 -0
- package/src/__tests__/conversation-queue.test.ts +177 -4
- package/src/__tests__/conversation-runtime-assembly.test.ts +134 -102
- package/src/__tests__/conversation-runtime-workspace.test.ts +14 -10
- package/src/__tests__/credential-prompt-route.test.ts +7 -10
- package/src/__tests__/custom-profile-ensure.test.ts +5 -1
- package/src/__tests__/discord-access-request-privacy.test.ts +5 -1
- package/src/__tests__/discord-requester-notice-privacy.test.ts +3 -3
- package/src/__tests__/document-append-idempotency.test.ts +233 -0
- package/src/__tests__/edit-propagation.test.ts +0 -7
- package/src/__tests__/helpers/mock-actor-context.ts +49 -0
- package/src/__tests__/helpers/mock-conversation.ts +13 -1
- package/src/__tests__/injector-chain.test.ts +63 -41
- package/src/__tests__/injector-disk-pressure.test.ts +11 -23
- package/src/__tests__/llm-context-resolution.test.ts +73 -1
- package/src/__tests__/llm-schema.test.ts +5 -2
- package/src/__tests__/mcp-list-plugin-servers.test.ts +250 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +6 -5
- package/src/__tests__/messages-read-boundary-guard.test.ts +134 -0
- package/src/__tests__/mtime-cache.test.ts +1 -1
- package/src/__tests__/non-member-access-request.test.ts +0 -20
- package/src/__tests__/outbound-slack-persistence.test.ts +40 -1
- package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -0
- package/src/__tests__/plugin-secret-pattern-contribution.test.ts +1 -1
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +14 -7
- package/src/__tests__/provider-commit-message-generator.test.ts +20 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +434 -105
- package/src/__tests__/scoped-approval-grants.test.ts +11 -6
- package/src/__tests__/secret-ingress-channel.test.ts +0 -1
- package/src/__tests__/skills.test.ts +32 -0
- package/src/__tests__/slack-edit-ordering-characterization.test.ts +0 -1
- package/src/__tests__/subagent-call-site-routing.test.ts +31 -19
- package/src/__tests__/subagent-spawn-and-await.test.ts +14 -10
- package/src/__tests__/turn-events-store.test.ts +43 -0
- package/src/__tests__/ui-shape-teaching.test.ts +33 -0
- package/src/__tests__/ui-voice-picker-surface.test.ts +128 -0
- package/src/__tests__/user-plugin-loader.test.ts +1 -1
- package/src/__tests__/visible-app-context.test.ts +16 -9
- package/src/__tests__/voice-config-update.test.ts +40 -0
- package/src/__tests__/worker-entrypoint-guards.test.ts +54 -0
- package/src/__tests__/worker-plugin-surface.test.ts +77 -0
- package/src/__tests__/workspace-migration-142-consolidate-voice-front-door.test.ts +158 -0
- package/src/__tests__/workspace-migration-143-repair-deprecated-codex-model-id.test.ts +133 -0
- package/src/__tests__/workspace-migration-144-convert-stranded-subscription-openai-profiles.test.ts +316 -0
- package/src/__tests__/workspace-migration-145-collapse-profile-bindings-to-entries.test.ts +325 -0
- package/src/__tests__/workspace-migration-146-repair-retired-fireworks-deepseek-flash-model-id.test.ts +235 -0
- package/src/acp/__tests__/acp-claude-oauth.test.ts +10 -2
- package/src/acp/__tests__/auth-required.test.ts +161 -0
- package/src/acp/acp-claude-oauth.ts +19 -2
- package/src/acp/agent-process.test.ts +100 -0
- package/src/acp/agent-process.ts +29 -26
- package/src/acp/auth-required.ts +102 -0
- package/src/acp/session-manager.test.ts +119 -0
- package/src/acp/session-manager.ts +68 -2
- package/src/api/events/acp-auth-required.ts +55 -0
- package/src/api/index.ts +7 -0
- package/src/api/surfaces.ts +7 -3
- package/src/apps/app-store.ts +3 -0
- package/src/bundler/package-resolver.ts +2 -30
- package/src/calls/__tests__/voice-session-bridge.test.ts +173 -1
- package/src/calls/__tests__/voice-triage-escalate.test.ts +94 -0
- package/src/calls/call-controller.ts +19 -3
- package/src/calls/call-setup-flow.ts +0 -1
- package/src/calls/media-stream-stt-session.ts +15 -0
- package/src/calls/voice-session-bridge.ts +71 -16
- package/src/calls/voice-triage-escalate.ts +104 -2
- package/src/channels/__tests__/plugin-channel-declarations.test.ts +161 -0
- package/src/channels/config.ts +13 -0
- package/src/channels/plugin-channel-declarations.ts +108 -0
- package/src/channels/types.ts +30 -0
- package/src/cli/AGENTS.md +5 -2
- package/src/cli/commands/credentials.help.ts +2 -2
- package/src/cli/commands/inference-providers.ts +1 -1
- package/src/cli/commands/mcp.help.ts +13 -4
- package/src/cli/commands/mcp.ts +9 -0
- package/src/cli/commands/memory/__tests__/memory-v3.test.ts +128 -5
- package/src/cli/commands/memory/index.help.ts +43 -1
- package/src/cli/commands/memory/memory-v3.ts +64 -0
- package/src/cli/commands/stt.help.ts +27 -2
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +39 -0
- package/src/cli/lib/bundled-marketplace.json +13 -0
- package/src/cli/lib/upgrade-plugin.ts +42 -0
- package/src/config/__tests__/default-profile-catalog.test.ts +34 -2
- package/src/config/__tests__/default-provider.test.ts +6 -1
- package/src/config/__tests__/profile-materialization.test.ts +75 -19
- package/src/config/bundled-skills/acp/SKILL.md +6 -7
- package/src/config/bundled-skills/document-editor/SKILL.md +2 -2
- package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +14 -4
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.test.ts +65 -0
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +7 -1
- package/src/config/bundled-skills/settings/tools/shared.ts +16 -0
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -1
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +22 -1
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +9 -2
- package/src/config/call-site-defaults.ts +4 -5
- package/src/config/default-profile-catalog.ts +83 -12
- package/src/config/default-profile-names.ts +4 -1
- package/src/config/default-provider-resolution.ts +4 -0
- package/src/config/llm-context-resolution.ts +11 -3
- package/src/config/llm-resolver.ts +28 -1
- package/src/config/profile-materialization.ts +70 -22
- package/src/config/schemas/__tests__/live-voice.test.ts +107 -4
- package/src/config/schemas/call-site-catalog.ts +4 -4
- package/src/config/schemas/live-voice.ts +57 -23
- package/src/config/schemas/llm.ts +59 -32
- package/src/config/schemas/mcp.ts +23 -0
- package/src/config/schemas/plugin-updates.ts +6 -2
- package/src/config/schemas/stt.ts +1 -0
- package/src/context/outbound-sanitize.ts +96 -1
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +82 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +15 -10
- package/src/daemon/conversation-agent-loop.ts +17 -6
- package/src/daemon/conversation-messaging.ts +5 -1
- package/src/daemon/conversation-notifiers.ts +9 -1
- package/src/daemon/conversation-process.ts +36 -6
- package/src/daemon/conversation-runtime-assembly.ts +3 -4
- package/src/daemon/conversation-surfaces.ts +27 -5
- package/src/daemon/conversation-tool-setup.ts +1 -2
- package/src/daemon/conversation.ts +48 -0
- package/src/daemon/interactive-turn-sender.ts +59 -0
- package/src/daemon/mcp-reload-service.ts +36 -6
- package/src/daemon/process-message.ts +24 -24
- package/src/daemon/providers-setup.ts +6 -3
- package/src/daemon/trust-context-types.ts +29 -0
- package/src/daemon/wake-conversation-ops.ts +3 -2
- package/src/documents/document-store.ts +138 -5
- package/src/hooks/hook-loader.ts +3 -3
- package/src/hooks/registry.ts +50 -6
- package/src/inbound/__tests__/oauth-callback-url.test.ts +83 -0
- package/src/inbound/oauth-callback-url.ts +61 -0
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +1 -104
- package/src/live-voice/__tests__/live-voice-events.test.ts +7 -8
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +932 -0
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +115 -8
- package/src/live-voice/__tests__/live-voice-photo.test.ts +100 -0
- package/src/live-voice/__tests__/live-voice-progress.test.ts +60 -194
- package/src/live-voice/__tests__/live-voice-stt.test.ts +14 -0
- package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +29 -0
- package/src/live-voice/__tests__/live-voice-tts-session.test.ts +0 -483
- package/src/live-voice/__tests__/live-voice-vad.test.ts +0 -16
- package/src/live-voice/__tests__/progress-narration.test.ts +214 -0
- package/src/live-voice/live-voice-archive.ts +2 -0
- package/src/live-voice/live-voice-metrics.ts +57 -32
- package/src/live-voice/live-voice-photo.ts +1 -2
- package/src/live-voice/live-voice-session.ts +535 -314
- package/src/live-voice/progress-narration.ts +277 -0
- package/src/live-voice/protocol.ts +21 -1
- package/src/mcp/__tests__/effective-config.test.ts +238 -0
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
- package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +200 -0
- package/src/mcp/__tests__/mcp-oauth-provider.test.ts +9 -9
- package/src/mcp/__tests__/plugin-server-credential-isolation.test.ts +95 -0
- package/src/mcp/client.ts +16 -11
- package/src/mcp/effective-config.ts +113 -0
- package/src/mcp/manager.ts +11 -6
- package/src/mcp/mcp-auth-orchestrator.ts +13 -22
- package/src/mcp/mcp-oauth-provider.ts +205 -240
- package/src/monitoring/__tests__/plugin-auto-update.test.ts +166 -3
- package/src/monitoring/plugin-auto-update.ts +128 -24
- package/src/notifications/signal.ts +1 -0
- package/src/permissions/confirmation-guardian-request.test.ts +15 -11
- package/src/permissions/confirmation-guardian-request.ts +2 -2
- package/src/permissions/question-guardian-request.test.ts +14 -6
- package/src/permissions/question-guardian-request.ts +1 -2
- package/src/persistence/attachments-store.ts +8 -1
- package/src/persistence/bookmark-crud.ts +3 -7
- package/src/persistence/conversation-attention-store.ts +16 -45
- package/src/persistence/conversation-crud.ts +33 -4
- package/src/persistence/conversation-lineage.ts +9 -0
- package/src/persistence/conversation-queries.ts +108 -41
- package/src/persistence/delivery-crud.ts +38 -29
- package/src/persistence/external-conversation-store.ts +32 -4
- package/src/persistence/llm-request-log-store.ts +4 -10
- package/src/persistence/llm-usage-store.ts +8 -3
- package/src/persistence/message-reads.test.ts +197 -0
- package/src/persistence/message-reads.ts +211 -0
- package/src/persistence/migrations/366-chatgpt-subscription-row-identity.test.ts +120 -0
- package/src/persistence/migrations/366-chatgpt-subscription-row-identity.ts +62 -0
- package/src/persistence/real-user-turn-filter.ts +27 -3
- package/src/persistence/steps.ts +9 -0
- package/src/plugin-api/__tests__/oauth-callback-url-export.test.ts +29 -0
- package/src/plugin-api/conversation-turn.ts +168 -5
- package/src/plugin-api/index.ts +21 -5
- package/src/plugin-api/vision-support.test.ts +1 -1
- package/src/plugins/__tests__/mcp-servers.test.ts +371 -0
- package/src/plugins/defaults/memory/AGENTS.md +4 -0
- package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +204 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +72 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +4 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +4 -1
- package/src/plugins/defaults/memory/buffer-format.ts +165 -0
- package/src/plugins/defaults/memory/context-search/sources/conversations.ts +6 -0
- package/src/plugins/defaults/memory/graph/image-ref-utils.ts +3 -0
- package/src/plugins/defaults/memory/graph/tool-handlers.ts +1 -30
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.test.ts +34 -0
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +8 -12
- package/src/plugins/defaults/memory/hooks/post-compact.ts +1 -4
- package/src/plugins/defaults/memory/indexer.ts +3 -1
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +19 -7
- package/src/plugins/defaults/memory/src/__tests__/memory-v3-gate-stats.test.ts +281 -0
- package/src/plugins/defaults/memory/src/memory-v3-routes.ts +207 -0
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +33 -0
- package/src/plugins/defaults/memory/substrate/__tests__/static-context.test.ts +199 -2
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +25 -18
- package/src/plugins/defaults/memory/substrate/skill-content.ts +8 -1
- package/src/plugins/defaults/memory/substrate/static-context.ts +160 -4
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +2 -4
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +3 -1
- package/src/plugins/defaults/memory/v3/prune.ts +2 -0
- package/src/plugins/defaults/memory/v3/selection-log-store.ts +2 -0
- package/src/plugins/defaults/memory/worker.ts +6 -3
- package/src/plugins/external-plugin-loader.ts +47 -0
- package/src/plugins/mcp-servers.ts +361 -0
- package/src/plugins/mtime-cache.ts +23 -49
- package/src/plugins/worker-plugin-surface.ts +33 -0
- package/src/providers/__tests__/connection-model-compat.test.ts +1 -1
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +214 -2
- package/src/providers/__tests__/preflight-resolved-config.test.ts +57 -0
- package/src/providers/__tests__/retry-callsite.test.ts +5 -2
- package/src/providers/call-site-routing.ts +30 -3
- package/src/providers/connection-resolution.ts +194 -11
- package/src/providers/inference/auth.ts +6 -0
- package/src/providers/inference/connection-availability.ts +24 -2
- package/src/providers/inference/connections.ts +2 -0
- package/src/providers/model-catalog.ts +3 -3
- package/src/providers/model-intents.ts +28 -8
- package/src/providers/openai/chat-completions-provider.ts +5 -6
- package/src/providers/openai/codex-models.ts +2 -1
- package/src/providers/provider-send-message.ts +32 -3
- package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +433 -0
- package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +620 -0
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +34 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +285 -6
- package/src/providers/speech-to-text/deepgram-flux-frames.ts +395 -0
- package/src/providers/speech-to-text/deepgram-flux-realtime.ts +719 -0
- package/src/providers/speech-to-text/provider-catalog.ts +99 -8
- package/src/providers/speech-to-text/resolve.ts +25 -2
- package/src/routes/worker.ts +17 -5
- package/src/runtime/access-request-helper.ts +9 -12
- package/src/runtime/agent-wake.ts +3 -3
- package/src/runtime/pre-first-message-gate.ts +4 -0
- package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +12 -4
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +219 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +52 -0
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +61 -0
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +44 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +102 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +44 -0
- package/src/runtime/routes/__tests__/stt-routes.test.ts +25 -0
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +62 -1
- package/src/runtime/routes/channel-availability-routes.ts +32 -14
- package/src/runtime/routes/channel-route-shared.ts +0 -6
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +6 -6
- package/src/runtime/routes/conversation-list-routes.ts +112 -1
- package/src/runtime/routes/conversation-query-routes.ts +40 -27
- package/src/runtime/routes/credential-prompt-routes.ts +4 -7
- package/src/runtime/routes/default-provider-routes.ts +15 -0
- package/src/runtime/routes/inbound-message-handler.ts +17 -41
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -9
- package/src/runtime/routes/inbound-stages/admission-policy.ts +1 -17
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -3
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +1 -3
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +3 -4
- package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +11 -20
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +2 -3
- package/src/runtime/routes/inference-profiles-routes.ts +20 -11
- package/src/runtime/routes/inference-provider-connection-routes.ts +77 -15
- package/src/runtime/routes/log-export-routes.ts +3 -0
- package/src/runtime/routes/mcp-auth-routes.ts +148 -57
- package/src/runtime/routes/plugins-routes.ts +21 -3
- package/src/runtime/routes/stt-routes.ts +31 -25
- package/src/runtime/routes/surface-conversation-resolver.ts +3 -0
- package/src/runtime/routes/user-route-dispatcher.ts +39 -14
- package/src/runtime/routes/user-route-import.ts +108 -0
- package/src/schedule/worker.ts +6 -0
- package/src/security/oauth2.ts +6 -22
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +22 -0
- package/src/stt/__tests__/types.test.ts +94 -0
- package/src/stt/daemon-batch-transcriber.ts +10 -0
- package/src/stt/stt-stream-session.ts +8 -4
- package/src/stt/types.ts +103 -0
- package/src/subagent/manager.ts +1 -3
- package/src/subagent/types.ts +7 -6
- package/src/tools/acp/spawn.test.ts +97 -0
- package/src/tools/acp/spawn.ts +32 -0
- package/src/tools/document/document-tool.ts +12 -3
- package/src/tools/registry.ts +2 -1
- package/src/tools/ui-surface/surface-shape-docs.ts +11 -0
- package/src/tools/workflows/run-workflow.ts +1 -2
- package/src/tts/__tests__/reasoning-tag-filter.test.ts +78 -0
- package/src/tts/reasoning-tag-filter.ts +89 -0
- package/src/util/think-tag-stream.ts +95 -0
- package/src/workspace/byok-default-profile-ensure.ts +76 -24
- package/src/workspace/custom-profile-ensure.ts +4 -24
- package/src/workspace/migrations/142-consolidate-voice-front-door.ts +70 -0
- package/src/workspace/migrations/143-repair-deprecated-codex-model-id.ts +134 -0
- package/src/workspace/migrations/144-convert-stranded-subscription-openai-profiles.ts +265 -0
- package/src/workspace/migrations/145-collapse-profile-bindings-to-entries.ts +328 -0
- package/src/workspace/migrations/146-repair-retired-fireworks-deepseek-flash-model-id.ts +195 -0
- package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +0 -10
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/provider-commit-message-generator.ts +7 -5
- package/src/live-voice/__tests__/front-decision.test.ts +0 -645
- package/src/live-voice/front-decision.ts +0 -476
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
PROVIDERS_REQUIRING_BASE_URL_AND_MODELS,
|
|
5
|
+
ROUTING_IDENTITY_PROVIDERS,
|
|
6
|
+
} from "../../providers/inference/auth.js";
|
|
4
7
|
import { PROVIDER_CATALOG } from "../../providers/model-catalog.js";
|
|
5
8
|
import { isCodexSubscriptionModel } from "../../providers/openai/codex-models.js";
|
|
6
9
|
import {
|
|
@@ -25,32 +28,54 @@ import {
|
|
|
25
28
|
// Provider enum
|
|
26
29
|
// ---------------------------------------------------------------------------
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
31
|
+
/**
|
|
32
|
+
* The provider values the write surfaces accept today: adapter-backed
|
|
33
|
+
* catalog providers plus the two routing identities. The schema itself is
|
|
34
|
+
* an open string so a stored provider outside this list parses instead of
|
|
35
|
+
* stripping its profile; dispatch resolves such a label as a connection
|
|
36
|
+
* entry name and fails with an explainable resolution error when no row
|
|
37
|
+
* carries it. Write-time membership is enforced at the profiles route and
|
|
38
|
+
* the `commitConfigWrite` choke point (`unknownLlmProviderIssue`), which
|
|
39
|
+
* is where entry names unlock when the entries model enables them.
|
|
40
|
+
*/
|
|
41
|
+
export const KNOWN_LLM_PROVIDERS = [
|
|
42
|
+
"anthropic",
|
|
43
|
+
"openai",
|
|
44
|
+
"gemini",
|
|
45
|
+
"ollama",
|
|
46
|
+
"fireworks",
|
|
47
|
+
"openrouter",
|
|
48
|
+
"vercel-ai-gateway",
|
|
49
|
+
"openai-compatible",
|
|
50
|
+
"minimax",
|
|
51
|
+
"atlascloud",
|
|
52
|
+
"together",
|
|
53
|
+
"litellm",
|
|
54
|
+
"baseten",
|
|
55
|
+
"poolside",
|
|
56
|
+
// Routing identities rather than adapters: "vellum" = the platform-managed
|
|
57
|
+
// route (upstream derived from the model at dispatch), "chatgpt" = the
|
|
58
|
+
// subscription route to OpenAI. Neither has a PROVIDER_CATALOG entry;
|
|
59
|
+
// dispatch substitutes the real upstream before any adapter lookup.
|
|
60
|
+
"vellum",
|
|
61
|
+
"chatgpt",
|
|
62
|
+
] as const;
|
|
63
|
+
|
|
64
|
+
export const LLMProvider = z.string().min(1).meta({ id: "LLMProvider" });
|
|
52
65
|
export type LLMProvider = z.infer<typeof LLMProvider>;
|
|
53
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Write-surface membership check for a provider value. Returns a message
|
|
69
|
+
* when the value is outside {@link KNOWN_LLM_PROVIDERS}, null when it is
|
|
70
|
+
* allowed. Pure and sync so the profiles route and the config-write choke
|
|
71
|
+
* point share one rule.
|
|
72
|
+
*/
|
|
73
|
+
export function unknownLlmProviderIssue(provider: string): string | null {
|
|
74
|
+
return (KNOWN_LLM_PROVIDERS as readonly string[]).includes(provider)
|
|
75
|
+
? null
|
|
76
|
+
: `Invalid provider "${provider}". Valid providers: ${KNOWN_LLM_PROVIDERS.join(", ")}.`;
|
|
77
|
+
}
|
|
78
|
+
|
|
54
79
|
/**
|
|
55
80
|
* Providers that can back `llm.defaultProvider`: the named columns of the
|
|
56
81
|
* default-profile matrix plus every API-key catalog provider whose personal
|
|
@@ -70,10 +95,10 @@ export const DEFAULT_PROVIDER_CHOICES: readonly LLMProvider[] = [
|
|
|
70
95
|
entry.defaultModel !== "",
|
|
71
96
|
)
|
|
72
97
|
.map((entry) => entry.id)
|
|
73
|
-
// A catalog provider
|
|
74
|
-
// any profile, so it cannot back the defaults either.
|
|
98
|
+
// A catalog provider outside the known provider set cannot be
|
|
99
|
+
// referenced by any profile, so it cannot back the defaults either.
|
|
75
100
|
.filter((id): id is LLMProvider =>
|
|
76
|
-
(
|
|
101
|
+
(KNOWN_LLM_PROVIDERS as readonly string[]).includes(id),
|
|
77
102
|
),
|
|
78
103
|
]),
|
|
79
104
|
];
|
|
@@ -84,13 +109,15 @@ export function isDefaultProviderChoice(value: string): value is LLMProvider {
|
|
|
84
109
|
|
|
85
110
|
/**
|
|
86
111
|
* Default-provider choices whose profiles materialize from the shared BYOK
|
|
87
|
-
* templates: every choice except the
|
|
88
|
-
* defaults are pinned
|
|
112
|
+
* templates: every choice except the routing identities (`vellum`,
|
|
113
|
+
* `chatgpt`), whose defaults are pinned models on code-owned columns.
|
|
89
114
|
*/
|
|
90
115
|
export function isByokDefaultProviderChoice(
|
|
91
116
|
value: string,
|
|
92
117
|
): value is LLMProvider {
|
|
93
|
-
return
|
|
118
|
+
return (
|
|
119
|
+
!ROUTING_IDENTITY_PROVIDERS.has(value) && isDefaultProviderChoice(value)
|
|
120
|
+
);
|
|
94
121
|
}
|
|
95
122
|
|
|
96
123
|
const DefaultProviderEnum = z.enum(
|
|
@@ -188,7 +215,7 @@ export const LLMCallSiteEnum = z.enum([
|
|
|
188
215
|
"skillCategoryInference",
|
|
189
216
|
"inference",
|
|
190
217
|
"vision",
|
|
191
|
-
"
|
|
218
|
+
"voiceProgressNarration",
|
|
192
219
|
"voiceFrontDoor",
|
|
193
220
|
"trustRuleSuggestion",
|
|
194
221
|
"homeGreeting",
|
|
@@ -109,3 +109,26 @@ export const McpConfigSchema = z
|
|
|
109
109
|
export type McpTransport = z.infer<typeof McpTransportSchema>;
|
|
110
110
|
export type McpServerConfig = z.infer<typeof McpServerConfigSchema>;
|
|
111
111
|
export type McpConfig = z.infer<typeof McpConfigSchema>;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Who declared a server: the workspace `config.json`, which the user owns,
|
|
115
|
+
* or a plugin's `mcp.json`, which its author owns.
|
|
116
|
+
*
|
|
117
|
+
* Deliberately not a schema field. It is resolved from where the entry was
|
|
118
|
+
* read, never parsed from a file, so nothing on disk can claim to be
|
|
119
|
+
* workspace-owned. What it gates is credential access: only a workspace
|
|
120
|
+
* server resolves `mcp:<serverId>:*` from the credential store, because a
|
|
121
|
+
* plugin controls both its server key and its URL and would otherwise
|
|
122
|
+
* receive a workspace credential at an endpoint it chose.
|
|
123
|
+
*/
|
|
124
|
+
export type McpServerSource = "workspace" | "plugin";
|
|
125
|
+
|
|
126
|
+
/** A server config with its origin resolved. */
|
|
127
|
+
export interface ResolvedMcpServerConfig extends McpServerConfig {
|
|
128
|
+
readonly source: McpServerSource;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** The MCP config the daemon runs: both origins, every server attributed. */
|
|
132
|
+
export interface ResolvedMcpConfig extends Omit<McpConfig, "servers"> {
|
|
133
|
+
readonly servers: Record<string, ResolvedMcpServerConfig>;
|
|
134
|
+
}
|
|
@@ -5,11 +5,15 @@ import { z } from "zod";
|
|
|
5
5
|
*
|
|
6
6
|
* The resource monitor process runs a periodic sweep (see
|
|
7
7
|
* `monitoring/plugin-auto-update.ts`) that moves every installed, enabled
|
|
8
|
-
* plugin to
|
|
8
|
+
* marketplace plugin to the catalog's current pin. The sweep is off by default:
|
|
9
9
|
* `mode: "manual"` means nothing upgrades unless a human runs
|
|
10
10
|
* `assistant plugins upgrade <name>` (or the equivalent route), which is the
|
|
11
11
|
* behavior every workspace had before this block existed. Setting
|
|
12
12
|
* `mode: "auto"` opts the workspace into the hourly sweep.
|
|
13
|
+
*
|
|
14
|
+
* Opting in never covers plugins installed straight from a GitHub URL. Those
|
|
15
|
+
* track a mutable ref rather than a reviewed pin, so the sweep leaves them to
|
|
16
|
+
* an explicit `assistant plugins upgrade`.
|
|
13
17
|
*/
|
|
14
18
|
export const PluginUpdatesConfigSchema = z
|
|
15
19
|
.object({
|
|
@@ -19,7 +23,7 @@ export const PluginUpdatesConfigSchema = z
|
|
|
19
23
|
})
|
|
20
24
|
.default("manual")
|
|
21
25
|
.describe(
|
|
22
|
-
'How installed plugins move to newer revisions. "manual" (default) never upgrades on its own
|
|
26
|
+
'How installed plugins move to newer revisions. "manual" (default) never upgrades on its own: upgrades happen only when a human asks for one. "auto" lets the resource monitor upgrade every installed, enabled marketplace plugin on the `checkIntervalMs` cadence. Plugins installed directly from a GitHub URL are never upgraded automatically, since they track a mutable ref instead of a reviewed pin.',
|
|
23
27
|
),
|
|
24
28
|
strategy: z
|
|
25
29
|
// Deliberately narrower than the full `PluginUpgradeStrategy` union: the
|
|
@@ -102,6 +102,96 @@ export function compactAxTreeHistory(messages: Message[]): Message[] {
|
|
|
102
102
|
});
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Full-wrapper matcher for the `<channel_capabilities>` block. Both ends are
|
|
107
|
+
* required so a user message that merely opens with the tag (someone pasting
|
|
108
|
+
* prompt markup into chat) is never mistaken for an injected block, the same
|
|
109
|
+
* discipline `stripUserTextBlocksByPrefix` uses in `strip-injections.ts`.
|
|
110
|
+
*/
|
|
111
|
+
const CHANNEL_CAPABILITIES_OPEN = "<channel_capabilities>\n";
|
|
112
|
+
const CHANNEL_CAPABILITIES_CLOSE = "\n</channel_capabilities>";
|
|
113
|
+
|
|
114
|
+
/** Whether a text block is a complete `<channel_capabilities>…` block. */
|
|
115
|
+
function isChannelCapabilitiesBlock(text: string): boolean {
|
|
116
|
+
return (
|
|
117
|
+
text.startsWith(CHANNEL_CAPABILITIES_OPEN) &&
|
|
118
|
+
text.endsWith(CHANNEL_CAPABILITIES_CLOSE)
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Drop `<channel_capabilities>` blocks that repeat the last one still standing,
|
|
124
|
+
* so a conversation carries one copy per distinct set of capabilities instead
|
|
125
|
+
* of one per turn.
|
|
126
|
+
*
|
|
127
|
+
* Runtime injection prepends this block to the turn-starting user message and
|
|
128
|
+
* then freezes it into history: live, because injection splices into the
|
|
129
|
+
* conversation's own message array, and across restarts, because `loadFromDb`
|
|
130
|
+
* rehydrates it from message metadata. Nothing ever removes it, so an N-turn
|
|
131
|
+
* channel conversation ships N copies. Unlike `<turn_context>`, which at least
|
|
132
|
+
* carries a fresh timestamp, this block is a pure function of the channel
|
|
133
|
+
* (`buildChannelCapabilityBlock`): every copy after the first is byte-identical
|
|
134
|
+
* to the one above it and tells the model nothing it has not already read.
|
|
135
|
+
*
|
|
136
|
+
* ## Why "same as the last retained copy" and not "not the current turn"
|
|
137
|
+
*
|
|
138
|
+
* Prompt caching only pays off when the bytes a provider marked in turn N are
|
|
139
|
+
* still at the same position in turn N+1, an invariant this repo enforces end
|
|
140
|
+
* to end in `prompt-cache-cross-turn-stability.test.ts`. Any rule of the form
|
|
141
|
+
* "keep it on the current turn, collapse it above" necessarily re-renders one
|
|
142
|
+
* message every turn (the turn that just ended), which is a full cache miss on
|
|
143
|
+
* the whole message prefix, every turn, for every conversation.
|
|
144
|
+
*
|
|
145
|
+
* Comparing against the last RETAINED copy avoids that entirely: whether a
|
|
146
|
+
* given occurrence survives depends only on the messages above it, never on
|
|
147
|
+
* how many turns come later. A message's rendering is therefore fixed the
|
|
148
|
+
* moment it is written, and the prefix stays byte-stable as the conversation
|
|
149
|
+
* grows. Capabilities that genuinely change mid-conversation (the same
|
|
150
|
+
* conversation resumed from a different client) still differ from the retained
|
|
151
|
+
* copy, so the new block is kept and the model sees the change.
|
|
152
|
+
*
|
|
153
|
+
* The cost is placement: the surviving copy sits at the top of the conversation
|
|
154
|
+
* rather than next to the newest message. It is the same bytes either way.
|
|
155
|
+
*
|
|
156
|
+
* Idempotent, like every other transform in this module: re-running it over an
|
|
157
|
+
* already-deduplicated history retains exactly the same occurrences.
|
|
158
|
+
*/
|
|
159
|
+
export function dedupeChannelCapabilityBlocks(history: Message[]): Message[] {
|
|
160
|
+
let lastRetained: string | null = null;
|
|
161
|
+
let changed = false;
|
|
162
|
+
|
|
163
|
+
const next = history.map((message) => {
|
|
164
|
+
if (message.role !== "user") {
|
|
165
|
+
return message;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
let messageChanged = false;
|
|
169
|
+
const content = message.content.filter((block) => {
|
|
170
|
+
if (block.type !== "text" || !isChannelCapabilitiesBlock(block.text)) {
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
if (block.text === lastRetained) {
|
|
174
|
+
messageChanged = true;
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
lastRetained = block.text;
|
|
178
|
+
return true;
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// A user row is never only injections in practice, but guard anyway: an
|
|
182
|
+
// empty content array is not a message any provider will accept, and
|
|
183
|
+
// dropping the row would break tool_use/tool_result pairing and the
|
|
184
|
+
// row-to-history index mapping `summarizeUpToMessage` relies on.
|
|
185
|
+
if (!messageChanged || content.length === 0) {
|
|
186
|
+
return message;
|
|
187
|
+
}
|
|
188
|
+
changed = true;
|
|
189
|
+
return { ...message, content };
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
return changed ? next : history;
|
|
193
|
+
}
|
|
194
|
+
|
|
105
195
|
/**
|
|
106
196
|
* Index of the last user message carrying `tool_result` blocks — the
|
|
107
197
|
* "current turn" boundary {@link stripOldMediaBlocks} keeps intact while
|
|
@@ -194,6 +284,9 @@ function stripOldMediaBlocks(history: Message[]): Message[] {
|
|
|
194
284
|
* `web_search_tool_result` blocks to text summaries; Anthropic's opaque
|
|
195
285
|
* `encrypted_content` tokens expire / are route-scoped, and replaying a stale
|
|
196
286
|
* one is rejected with `Invalid encrypted_content in search_result block`.
|
|
287
|
+
* - {@link dedupeChannelCapabilityBlocks} keeps one `<channel_capabilities>`
|
|
288
|
+
* block per distinct set of capabilities instead of the one-per-turn history
|
|
289
|
+
* accumulates; every repeat is byte-identical to the copy above it.
|
|
197
290
|
*
|
|
198
291
|
* Transforms the outbound copy only — the durable history keeps the rich
|
|
199
292
|
* originals and each send re-derives the sanitized projection (every transform
|
|
@@ -206,5 +299,7 @@ function stripOldMediaBlocks(history: Message[]): Message[] {
|
|
|
206
299
|
export function preModelCallSanitize(history: Message[]): Message[] {
|
|
207
300
|
const mediaStripped = stripOldMediaBlocks(history);
|
|
208
301
|
const axCompacted = compactAxTreeHistory(mediaStripped);
|
|
209
|
-
|
|
302
|
+
const webSearchStripped =
|
|
303
|
+
stripHistoricalWebSearchResults(axCompacted).messages;
|
|
304
|
+
return dedupeChannelCapabilityBlocks(webSearchStripped);
|
|
210
305
|
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The plugin source reconcile drives MCP reload.
|
|
3
|
+
*
|
|
4
|
+
* Without this, installing a plugin leaves its `mcp.json` servers
|
|
5
|
+
* unconnected and its tools unregistered until an explicit MCP reload, a
|
|
6
|
+
* config edit, or a restart — and, worse in the other direction, an
|
|
7
|
+
* uninstalled or disabled plugin's connected client and registered tools
|
|
8
|
+
* stay callable. The reconcile is the seam every install / uninstall /
|
|
9
|
+
* upgrade / enable / disable already funnels through.
|
|
10
|
+
*
|
|
11
|
+
* The guard is equally load-bearing: a reconcile also fires for plugin
|
|
12
|
+
* edits that touch no `mcp.json`, and reloading on those would tear down
|
|
13
|
+
* every healthy workspace connection to rebuild it for nothing.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { beforeEach, describe, expect, jest, mock, test } from "bun:test";
|
|
17
|
+
|
|
18
|
+
let pluginServersChanged = true;
|
|
19
|
+
|
|
20
|
+
const managerStop = jest.fn(async () => {});
|
|
21
|
+
const managerStart = jest.fn(async () => []);
|
|
22
|
+
|
|
23
|
+
mock.module("../../mcp/effective-config.js", () => ({
|
|
24
|
+
buildEffectiveMcpConfig: () => ({
|
|
25
|
+
servers: {
|
|
26
|
+
unabyss: {
|
|
27
|
+
transport: { type: "streamable-http", url: "https://mcp.example/x" },
|
|
28
|
+
enabled: true,
|
|
29
|
+
defaultRiskLevel: "low",
|
|
30
|
+
maxTools: 20,
|
|
31
|
+
source: "plugin",
|
|
32
|
+
},
|
|
33
|
+
},
|
|
34
|
+
globalMaxTools: 50,
|
|
35
|
+
}),
|
|
36
|
+
pluginMcpServersChangedSinceLastBuild: () => pluginServersChanged,
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
mock.module("../../mcp/manager.js", () => ({
|
|
40
|
+
getMcpServerManager: () => ({ stop: managerStop, start: managerStart }),
|
|
41
|
+
}));
|
|
42
|
+
|
|
43
|
+
mock.module("../../mcp/mcp-header-store.js", () => ({
|
|
44
|
+
migrateLegacyMcpHeaders: async () => {},
|
|
45
|
+
}));
|
|
46
|
+
|
|
47
|
+
mock.module("../../config/loader.js", () => ({
|
|
48
|
+
getConfig: () => ({ mcp: { servers: {}, globalMaxTools: 50 } }),
|
|
49
|
+
invalidateConfigCache: () => {},
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
mock.module("../../tools/registry.js", () => ({
|
|
53
|
+
registerMcpTools: () => [],
|
|
54
|
+
unregisterAllMcpTools: () => {},
|
|
55
|
+
}));
|
|
56
|
+
|
|
57
|
+
const { reconcilePluginMcpServers } = await import("../mcp-reload-service.js");
|
|
58
|
+
|
|
59
|
+
describe("reconcilePluginMcpServers", () => {
|
|
60
|
+
beforeEach(() => {
|
|
61
|
+
managerStop.mockClear();
|
|
62
|
+
managerStart.mockClear();
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test("reloads when the plugin-declared set changed", async () => {
|
|
66
|
+
pluginServersChanged = true;
|
|
67
|
+
|
|
68
|
+
await reconcilePluginMcpServers();
|
|
69
|
+
|
|
70
|
+
expect(managerStop).toHaveBeenCalledTimes(1);
|
|
71
|
+
expect(managerStart).toHaveBeenCalledTimes(1);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("does nothing when no plugin changed the set", async () => {
|
|
75
|
+
pluginServersChanged = false;
|
|
76
|
+
|
|
77
|
+
await reconcilePluginMcpServers();
|
|
78
|
+
|
|
79
|
+
expect(managerStop).not.toHaveBeenCalled();
|
|
80
|
+
expect(managerStart).not.toHaveBeenCalled();
|
|
81
|
+
});
|
|
82
|
+
});
|
|
@@ -143,6 +143,7 @@ import type {
|
|
|
143
143
|
WebSearchResultItem,
|
|
144
144
|
} from "./message-types/web-activity.js";
|
|
145
145
|
import { referenceMediaBlocksForPersist } from "./persist-media-references.js";
|
|
146
|
+
import { turnOrRestingTrust } from "./trust-context-types.js";
|
|
146
147
|
import type { TurnLatencyTracker } from "./turn-latency-tracker.js";
|
|
147
148
|
|
|
148
149
|
const log = getLogger("agent-loop-handlers");
|
|
@@ -1330,7 +1331,7 @@ function buildAssistantChannelMetadata(
|
|
|
1330
1331
|
deps: EventHandlerDeps,
|
|
1331
1332
|
): Record<string, unknown> {
|
|
1332
1333
|
const metadata: Record<string, unknown> = {
|
|
1333
|
-
...provenanceFromTrustContext(deps.ctx
|
|
1334
|
+
...provenanceFromTrustContext(turnOrRestingTrust(deps.ctx)),
|
|
1334
1335
|
userMessageChannel: deps.turnChannelContext.userMessageChannel,
|
|
1335
1336
|
assistantMessageChannel: deps.turnChannelContext.assistantMessageChannel,
|
|
1336
1337
|
userMessageInterface: deps.turnInterfaceContext.userMessageInterface,
|
|
@@ -1340,15 +1341,19 @@ function buildAssistantChannelMetadata(
|
|
|
1340
1341
|
};
|
|
1341
1342
|
|
|
1342
1343
|
if (deps.turnChannelContext.assistantMessageChannel === "slack") {
|
|
1343
|
-
|
|
1344
|
+
// The whole envelope resolves from one turn-local snapshot: the actor the
|
|
1345
|
+
// provenance above names is the actor whose channel and thread the row
|
|
1346
|
+
// routes to. Reading the live slot here instead would let a concurrent
|
|
1347
|
+
// inbound repoint the reply mid-turn, and could stamp one actor's class
|
|
1348
|
+
// beside another actor's routing identity.
|
|
1349
|
+
const rowTrust = turnOrRestingTrust(deps.ctx);
|
|
1350
|
+
const channelId = rowTrust?.requesterChatId;
|
|
1344
1351
|
if (channelId) {
|
|
1345
|
-
//
|
|
1346
|
-
//
|
|
1347
|
-
//
|
|
1348
|
-
//
|
|
1349
|
-
|
|
1350
|
-
// externalThreadId mid-turn, whereas the trust context is per-turn.
|
|
1351
|
-
const turnThreadTs = deps.ctx.trustContext?.sourceThreadId;
|
|
1352
|
+
// This turn's own inbound thread id (the same field guardian-approval
|
|
1353
|
+
// cards read). Deliberately not the shared conversation binding: on a
|
|
1354
|
+
// legacy flat→thread aliased Slack conversation a concurrent inbound
|
|
1355
|
+
// can rewrite the binding's externalThreadId mid-turn.
|
|
1356
|
+
const turnThreadTs = rowTrust?.sourceThreadId;
|
|
1352
1357
|
const threadTs = isSlackTs(turnThreadTs) ? turnThreadTs : undefined;
|
|
1353
1358
|
const timestampTimezone = resolveAssistantReplyTimestampTimezone(
|
|
1354
1359
|
deps.ctx,
|
|
@@ -1896,7 +1901,7 @@ function buildToolResultMetadata(
|
|
|
1896
1901
|
deps: EventHandlerDeps,
|
|
1897
1902
|
): Record<string, unknown> {
|
|
1898
1903
|
return {
|
|
1899
|
-
...provenanceFromTrustContext(deps.ctx
|
|
1904
|
+
...provenanceFromTrustContext(turnOrRestingTrust(deps.ctx)),
|
|
1900
1905
|
userMessageChannel: deps.turnChannelContext.userMessageChannel,
|
|
1901
1906
|
assistantMessageChannel: deps.turnChannelContext.assistantMessageChannel,
|
|
1902
1907
|
userMessageInterface: deps.turnInterfaceContext.userMessageInterface,
|
|
@@ -61,7 +61,11 @@ import { HOOKS } from "../plugin-api/constants.js";
|
|
|
61
61
|
import type { ConversationGraphMemory } from "../plugins/defaults/memory/graph/conversation-graph-memory.js";
|
|
62
62
|
import { enqueueMemoryRetrospectiveOnCompaction } from "../plugins/defaults/memory/memory-retrospective-enqueue.js";
|
|
63
63
|
import { runHook } from "../plugins/pipeline.js";
|
|
64
|
-
import {
|
|
64
|
+
import {
|
|
65
|
+
dispatchProviderResolvable,
|
|
66
|
+
isManagedConnectionRoute,
|
|
67
|
+
resolveEntryConnectionName,
|
|
68
|
+
} from "../providers/connection-resolution.js";
|
|
65
69
|
import {
|
|
66
70
|
ConnectionResolutionError,
|
|
67
71
|
resolveRoutingIdentity,
|
|
@@ -130,6 +134,7 @@ import {
|
|
|
130
134
|
} from "./inflight-turn-registry.js";
|
|
131
135
|
import type { UsageStats } from "./message-protocol.js";
|
|
132
136
|
import type { TrustContext } from "./trust-context-types.js";
|
|
137
|
+
import { turnOrRestingTrust } from "./trust-context-types.js";
|
|
133
138
|
import { resolveTurnCallSite } from "./turn-call-site.js";
|
|
134
139
|
import { runWithLatencySubSpans } from "./turn-latency-sub-spans.js";
|
|
135
140
|
import {
|
|
@@ -491,10 +496,13 @@ export async function runAgentLoopImpl(
|
|
|
491
496
|
const turnErrorAttribution = (): ConversationErrorAttribution => {
|
|
492
497
|
try {
|
|
493
498
|
const overrideProfile = readCurrentOverrideProfile();
|
|
499
|
+
// Mirrors dispatch's selection (including the resolvable-provider
|
|
500
|
+
// healing) so attribution names the profile that actually ran.
|
|
494
501
|
const resolveOpts = {
|
|
495
502
|
overrideProfile,
|
|
496
503
|
forceOverrideProfile,
|
|
497
504
|
selectionSeed: ctx.conversationId,
|
|
505
|
+
isResolvableProvider: dispatchProviderResolvable,
|
|
498
506
|
};
|
|
499
507
|
const { config: resolved, profileName } =
|
|
500
508
|
resolveCallSiteConfigWithProfile(turnCallSite, config.llm, resolveOpts);
|
|
@@ -509,6 +517,10 @@ export async function runAgentLoopImpl(
|
|
|
509
517
|
}
|
|
510
518
|
connectionName = error.connectionName;
|
|
511
519
|
}
|
|
520
|
+
// An entry-name provider IS the connection name, matching the
|
|
521
|
+
// dispatch-side translation.
|
|
522
|
+
connectionName ??=
|
|
523
|
+
resolveEntryConnectionName(resolved.provider) ?? undefined;
|
|
512
524
|
// Managed-ness comes from the connection row, matching what dispatch
|
|
513
525
|
// decides. The profile's own provider can't stand in: a concrete
|
|
514
526
|
// provider tweak over a managed winner keeps the managed connection
|
|
@@ -1283,8 +1295,7 @@ export async function runAgentLoopImpl(
|
|
|
1283
1295
|
// context, then the fallback — matching the trust the runtime injection
|
|
1284
1296
|
// assembly resolves for the same turn. The loop's other turn-identity
|
|
1285
1297
|
// fields self-resolve from its own conversation id.
|
|
1286
|
-
const loopTrust =
|
|
1287
|
-
ctx.currentTurnTrustContext ?? ctx.trustContext ?? FALLBACK_TURN_TRUST;
|
|
1298
|
+
const loopTrust = ctx.getTurnOrRestingTrust() ?? FALLBACK_TURN_TRUST;
|
|
1288
1299
|
|
|
1289
1300
|
/**
|
|
1290
1301
|
* Shared closure: runs the agent loop with the wrapper's turn context and
|
|
@@ -1404,7 +1415,7 @@ export async function runAgentLoopImpl(
|
|
|
1404
1415
|
// row here rather than writing a duplicate.
|
|
1405
1416
|
if (state.pendingToolResults.size > 0) {
|
|
1406
1417
|
const toolResultMetadata = {
|
|
1407
|
-
...provenanceFromTrustContext(ctx
|
|
1418
|
+
...provenanceFromTrustContext(turnOrRestingTrust(ctx)),
|
|
1408
1419
|
userMessageChannel: capturedTurnChannelContext.userMessageChannel,
|
|
1409
1420
|
assistantMessageChannel:
|
|
1410
1421
|
capturedTurnChannelContext.assistantMessageChannel,
|
|
@@ -1431,7 +1442,7 @@ export async function runAgentLoopImpl(
|
|
|
1431
1442
|
budgetYieldClassification.userMessage,
|
|
1432
1443
|
);
|
|
1433
1444
|
const yieldNoticeMetadata = {
|
|
1434
|
-
...provenanceFromTrustContext(ctx
|
|
1445
|
+
...provenanceFromTrustContext(turnOrRestingTrust(ctx)),
|
|
1435
1446
|
userMessageChannel: capturedTurnChannelContext.userMessageChannel,
|
|
1436
1447
|
assistantMessageChannel:
|
|
1437
1448
|
capturedTurnChannelContext.assistantMessageChannel,
|
|
@@ -1565,7 +1576,7 @@ export async function runAgentLoopImpl(
|
|
|
1565
1576
|
state.lastAssistantMessageId = undefined;
|
|
1566
1577
|
} else {
|
|
1567
1578
|
const errChannelMeta = {
|
|
1568
|
-
...provenanceFromTrustContext(ctx
|
|
1579
|
+
...provenanceFromTrustContext(turnOrRestingTrust(ctx)),
|
|
1569
1580
|
userMessageChannel: capturedTurnChannelContext.userMessageChannel,
|
|
1570
1581
|
assistantMessageChannel:
|
|
1571
1582
|
capturedTurnChannelContext.assistantMessageChannel,
|
|
@@ -68,6 +68,7 @@ import type { SlackInboundMessageMetadata } from "./handlers/shared.js";
|
|
|
68
68
|
import type { UserMessageAttachment } from "./message-protocol.js";
|
|
69
69
|
import type { ConversationTransportMetadata } from "./message-types/conversations.js";
|
|
70
70
|
import type { TrustContext } from "./trust-context-types.js";
|
|
71
|
+
import { restingTrust } from "./trust-context-types.js";
|
|
71
72
|
|
|
72
73
|
const log = getLogger("conversation-messaging");
|
|
73
74
|
|
|
@@ -863,7 +864,10 @@ export async function persistQueuedMessageBody(
|
|
|
863
864
|
const turnIfCtx =
|
|
864
865
|
extractTurnInterfaceContext(metadata) ?? ctx.getTurnInterfaceContext();
|
|
865
866
|
const provenance = provenanceFromTrustContext(
|
|
866
|
-
|
|
867
|
+
// Callers that own a turn pass the sender's trust; the fallback serves
|
|
868
|
+
// ingress paths that persist before any per-turn stamp exists, where
|
|
869
|
+
// the slot their own resolution just wrote is the right actor.
|
|
870
|
+
options.trustContext ?? restingTrust(ctx),
|
|
867
871
|
);
|
|
868
872
|
const imageSourcePaths = extractImageSourcePaths(attachments);
|
|
869
873
|
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
} from "../persistence/conversation-crud.js";
|
|
26
26
|
import type { Message } from "../providers/types.js";
|
|
27
27
|
import type { TrustContext } from "./trust-context-types.js";
|
|
28
|
+
import { restingTrust } from "./trust-context-types.js";
|
|
28
29
|
|
|
29
30
|
/**
|
|
30
31
|
* Subset of Conversation state that notifier callbacks need to read at
|
|
@@ -58,7 +59,14 @@ export function registerConversationNotifiers(
|
|
|
58
59
|
JSON.stringify([{ type: "text", text: questionText }]),
|
|
59
60
|
{
|
|
60
61
|
metadata: {
|
|
61
|
-
...provenanceFromTrustContext(
|
|
62
|
+
...provenanceFromTrustContext(
|
|
63
|
+
// This callback fires after the voice turn has settled: the
|
|
64
|
+
// per-turn field may still hold that turn's actor (nothing
|
|
65
|
+
// clears it at release), while voice cleanup has restored the
|
|
66
|
+
// conversation's resting trust. The owner is the right actor
|
|
67
|
+
// for a row persisted outside any turn.
|
|
68
|
+
restingTrust(ctx),
|
|
69
|
+
),
|
|
62
70
|
userMessageChannel: "phone",
|
|
63
71
|
assistantMessageChannel: "phone",
|
|
64
72
|
userMessageInterface: "phone",
|