@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
|
@@ -60,9 +60,11 @@ import {
|
|
|
60
60
|
} from "./conversation-slash.js";
|
|
61
61
|
import { getModelInfo } from "./handlers/config-model.js";
|
|
62
62
|
import { preactivateHostProxySkills } from "./host-proxy-preactivation.js";
|
|
63
|
+
import { bindInteractiveTurnSender } from "./interactive-turn-sender.js";
|
|
63
64
|
import type { UserMessageAttachment } from "./message-protocol.js";
|
|
64
65
|
import { buildTransportHints } from "./transport-hints.js";
|
|
65
66
|
import { sameTrustIdentity, type TrustContext } from "./trust-context-types.js";
|
|
67
|
+
import { turnOrRestingTrust } from "./trust-context-types.js";
|
|
66
68
|
import { resolveVerificationSessionIntent } from "./verification-session-intent.js";
|
|
67
69
|
|
|
68
70
|
const log = getLogger("conversation-process");
|
|
@@ -823,7 +825,7 @@ async function drainSingleMessage(
|
|
|
823
825
|
if (slashResult.kind === "unknown") {
|
|
824
826
|
try {
|
|
825
827
|
const drainProvenance = provenanceFromTrustContext(
|
|
826
|
-
conversation
|
|
828
|
+
turnOrRestingTrust(conversation),
|
|
827
829
|
);
|
|
828
830
|
const drainImageSourcePaths: Record<string, string> = {};
|
|
829
831
|
for (let i = 0; i < next.attachments.length; i++) {
|
|
@@ -938,7 +940,7 @@ async function drainSingleMessage(
|
|
|
938
940
|
let persistedCompactMessage = false;
|
|
939
941
|
try {
|
|
940
942
|
const drainProvenance = provenanceFromTrustContext(
|
|
941
|
-
conversation
|
|
943
|
+
turnOrRestingTrust(conversation),
|
|
942
944
|
);
|
|
943
945
|
const drainChannelMeta = {
|
|
944
946
|
...drainProvenance,
|
|
@@ -1031,7 +1033,7 @@ async function drainSingleMessage(
|
|
|
1031
1033
|
let persistedCleanMessage = false;
|
|
1032
1034
|
try {
|
|
1033
1035
|
const drainProvenance = provenanceFromTrustContext(
|
|
1034
|
-
conversation
|
|
1036
|
+
turnOrRestingTrust(conversation),
|
|
1035
1037
|
);
|
|
1036
1038
|
const drainChannelMeta = {
|
|
1037
1039
|
...drainProvenance,
|
|
@@ -1291,11 +1293,26 @@ async function drainSingleMessage(
|
|
|
1291
1293
|
drainLoopOptions.isHiddenPrompt = true;
|
|
1292
1294
|
}
|
|
1293
1295
|
|
|
1296
|
+
// By the time the drain runs, the conversation-level sender is usually the
|
|
1297
|
+
// no-op the just-finished interactive turn's restore (or conversation
|
|
1298
|
+
// birth) left behind, and the drain bypasses `processMessage`, so without
|
|
1299
|
+
// a fresh bind the PermissionPrompter's confirmation_request (which reads
|
|
1300
|
+
// the conversation-level sender, not the turn's `onEvent`) would be
|
|
1301
|
+
// emitted into nothing and hang until the permission timeout. Same
|
|
1302
|
+
// bind/restore contract as `processMessage`.
|
|
1303
|
+
const restoreSender =
|
|
1304
|
+
next.isInteractive === true
|
|
1305
|
+
? bindInteractiveTurnSender(conversation)
|
|
1306
|
+
: undefined;
|
|
1307
|
+
|
|
1294
1308
|
conversation
|
|
1295
1309
|
.runAgentLoop(agentLoopContent, userMessageId, {
|
|
1296
1310
|
...drainLoopOptions,
|
|
1297
1311
|
onEvent: next.onEvent,
|
|
1298
1312
|
})
|
|
1313
|
+
.finally(() => {
|
|
1314
|
+
restoreSender?.();
|
|
1315
|
+
})
|
|
1299
1316
|
.catch((err) => {
|
|
1300
1317
|
const message = err instanceof Error ? err.message : String(err);
|
|
1301
1318
|
log.error(
|
|
@@ -1778,6 +1795,14 @@ async function drainBatch(
|
|
|
1778
1795
|
drainLoopOptions.isHiddenPrompt = true;
|
|
1779
1796
|
}
|
|
1780
1797
|
|
|
1798
|
+
// Same sender contract as drainSingleMessage: an interactive drained batch
|
|
1799
|
+
// must rebind the conversation-level sender or prompter confirmations
|
|
1800
|
+
// raised during this turn reach no client.
|
|
1801
|
+
const restoreSender =
|
|
1802
|
+
drainLoopOptions.isInteractive === true
|
|
1803
|
+
? bindInteractiveTurnSender(conversation)
|
|
1804
|
+
: undefined;
|
|
1805
|
+
|
|
1781
1806
|
// Fire-and-forget: runAgentLoop's finally block recursively calls drainQueue
|
|
1782
1807
|
// when this run completes. Mirrors drainSingleMessage.
|
|
1783
1808
|
conversation
|
|
@@ -1785,6 +1810,9 @@ async function drainBatch(
|
|
|
1785
1810
|
...drainLoopOptions,
|
|
1786
1811
|
onEvent: fanOutOnEvent,
|
|
1787
1812
|
})
|
|
1813
|
+
.finally(() => {
|
|
1814
|
+
restoreSender?.();
|
|
1815
|
+
})
|
|
1788
1816
|
.catch((err) => {
|
|
1789
1817
|
const message = err instanceof Error ? err.message : String(err);
|
|
1790
1818
|
log.error(
|
|
@@ -2024,7 +2052,9 @@ export async function processMessage(
|
|
|
2024
2052
|
if (slashResult.kind === "unknown") {
|
|
2025
2053
|
const pmTurnCtx = conversation.getTurnChannelContext();
|
|
2026
2054
|
const pmInterfaceCtx = conversation.getTurnInterfaceContext();
|
|
2027
|
-
const pmProvenance = provenanceFromTrustContext(
|
|
2055
|
+
const pmProvenance = provenanceFromTrustContext(
|
|
2056
|
+
turnOrRestingTrust(conversation),
|
|
2057
|
+
);
|
|
2028
2058
|
const pmImageSourcePaths: Record<string, string> = {};
|
|
2029
2059
|
for (let i = 0; i < attachments.length; i++) {
|
|
2030
2060
|
const a = attachments[i];
|
|
@@ -2116,7 +2146,7 @@ export async function processMessage(
|
|
|
2116
2146
|
const pmTurnCtx = conversation.getTurnChannelContext();
|
|
2117
2147
|
const pmInterfaceCtx = conversation.getTurnInterfaceContext();
|
|
2118
2148
|
const pmProvenance = provenanceFromTrustContext(
|
|
2119
|
-
conversation
|
|
2149
|
+
turnOrRestingTrust(conversation),
|
|
2120
2150
|
);
|
|
2121
2151
|
const pmChannelMeta = {
|
|
2122
2152
|
...pmProvenance,
|
|
@@ -2197,7 +2227,7 @@ export async function processMessage(
|
|
|
2197
2227
|
const pmTurnCtx = conversation.getTurnChannelContext();
|
|
2198
2228
|
const pmInterfaceCtx = conversation.getTurnInterfaceContext();
|
|
2199
2229
|
const pmProvenance = provenanceFromTrustContext(
|
|
2200
|
-
conversation
|
|
2230
|
+
turnOrRestingTrust(conversation),
|
|
2201
2231
|
);
|
|
2202
2232
|
const pmChannelMeta = {
|
|
2203
2233
|
...pmProvenance,
|
|
@@ -2319,7 +2319,7 @@ export async function applyRuntimeInjections(
|
|
|
2319
2319
|
// folded into the summary are excluded.
|
|
2320
2320
|
const slackActiveThreadFocusBlock = channelCapabilities
|
|
2321
2321
|
? loadSlackActiveThreadFocusBlock(conversationId, channelCapabilities, {
|
|
2322
|
-
trustClass: liveConversation?.
|
|
2322
|
+
trustClass: liveConversation?.getTrustContext()?.trustClass,
|
|
2323
2323
|
contextCompactedMessageCount:
|
|
2324
2324
|
liveConversation?.contextCompactedMessageCount,
|
|
2325
2325
|
slackContextCompactionWatermarkTs:
|
|
@@ -2337,7 +2337,7 @@ export async function applyRuntimeInjections(
|
|
|
2337
2337
|
// boundary without the orchestrator threading a snapshot in.
|
|
2338
2338
|
const slackChronologicalMessages = channelCapabilities
|
|
2339
2339
|
? loadSlackChronologicalMessages(conversationId, channelCapabilities, {
|
|
2340
|
-
trustClass: liveConversation?.
|
|
2340
|
+
trustClass: liveConversation?.getTrustContext()?.trustClass,
|
|
2341
2341
|
contextSummary: liveConversation?.contextSummary,
|
|
2342
2342
|
contextCompactedMessageCount:
|
|
2343
2343
|
liveConversation?.contextCompactedMessageCount,
|
|
@@ -2380,8 +2380,7 @@ export async function applyRuntimeInjections(
|
|
|
2380
2380
|
turnIndex: options.turnIndex ?? liveConversation?.turnCount ?? 0,
|
|
2381
2381
|
trust:
|
|
2382
2382
|
options.trust ??
|
|
2383
|
-
liveConversation?.
|
|
2384
|
-
liveConversation?.trustContext ??
|
|
2383
|
+
liveConversation?.getTurnOrRestingTrust() ??
|
|
2385
2384
|
fallbackTurnTrust(channelCapabilities),
|
|
2386
2385
|
callSite: options.callSite ?? liveConversation?.currentCallSite,
|
|
2387
2386
|
...injectionInputs,
|
|
@@ -140,14 +140,28 @@ const MAX_UNDO_DEPTH = 10;
|
|
|
140
140
|
|
|
141
141
|
/**
|
|
142
142
|
* Pending surface types that do not hold the one-interactive-surface-at-a-time
|
|
143
|
-
* lock.
|
|
144
|
-
* answer, so a live one must not block the next
|
|
143
|
+
* lock. Each renders content the user reads (or settles on its own) rather
|
|
144
|
+
* than a question they must answer, so a live one must not block the next
|
|
145
|
+
* surface.
|
|
145
146
|
*/
|
|
146
147
|
const NON_BLOCKING_PENDING_SURFACE_TYPES = new Set<SurfaceType>([
|
|
147
148
|
"dynamic_page",
|
|
148
149
|
"visual",
|
|
150
|
+
"voice_picker",
|
|
149
151
|
]);
|
|
150
152
|
|
|
153
|
+
/**
|
|
154
|
+
* Surface types that carry no terminal action: the card settles when the user
|
|
155
|
+
* interacts with it, so no click could ever satisfy an attached `actions`
|
|
156
|
+
* entry or an explicit `await_action`. Both are generic ui_show params though,
|
|
157
|
+
* so nothing stops the model attaching them here, and doing so wedges the
|
|
158
|
+
* turn: the client latches `awaiting_user_input` on the presence of actions
|
|
159
|
+
* alone and no action ever arrives to clear it, leaving the composer disabled
|
|
160
|
+
* and Stop hidden while the daemon is still streaming. Stripping them makes
|
|
161
|
+
* the "never blocks a turn" contract structural rather than advisory.
|
|
162
|
+
*/
|
|
163
|
+
const ACTIONLESS_SURFACE_TYPES = new Set<SurfaceType>(["voice_picker"]);
|
|
164
|
+
|
|
151
165
|
/**
|
|
152
166
|
* Debounce window for persisting `ui_surface_update` data back to the
|
|
153
167
|
* message row. Surfaces typically receive bursts of updates (e.g. a
|
|
@@ -3279,8 +3293,12 @@ export async function surfaceProxyResolver(
|
|
|
3279
3293
|
}
|
|
3280
3294
|
inputActions = valid.length > 0 ? valid : undefined;
|
|
3281
3295
|
}
|
|
3282
|
-
const
|
|
3283
|
-
|
|
3296
|
+
const isActionless = ACTIONLESS_SURFACE_TYPES.has(surfaceType);
|
|
3297
|
+
const actions = isActionless
|
|
3298
|
+
? undefined
|
|
3299
|
+
: choiceData !== undefined
|
|
3300
|
+
? buildChoiceActions(choiceData)
|
|
3301
|
+
: inputActions;
|
|
3284
3302
|
const hasActions = Array.isArray(actions) && actions.length > 0;
|
|
3285
3303
|
if (surfaceType === "choice" && !hasActions) {
|
|
3286
3304
|
return {
|
|
@@ -3332,7 +3350,11 @@ export async function surfaceProxyResolver(
|
|
|
3332
3350
|
: surfaceType === "table"
|
|
3333
3351
|
? hasActions
|
|
3334
3352
|
: INTERACTIVE_SURFACE_TYPES.includes(surfaceType);
|
|
3335
|
-
|
|
3353
|
+
// An explicit `await_action: true` is honored for every other type; an
|
|
3354
|
+
// actionless surface has nothing to await, so it is forced false.
|
|
3355
|
+
const awaitAction = isActionless
|
|
3356
|
+
? false
|
|
3357
|
+
: ((input.await_action as boolean) ?? isInteractive);
|
|
3336
3358
|
|
|
3337
3359
|
// Only one non-persistent interactive surface at a time. If another
|
|
3338
3360
|
// surface is already awaiting user input, reject this one so the LLM
|
|
@@ -389,8 +389,7 @@ export function createToolExecutor(
|
|
|
389
389
|
// Per-turn trust snapshot: prefer the snapshot captured at turn start so
|
|
390
390
|
// a concurrent owner meta command (/status, /clean) that mutates the live
|
|
391
391
|
// trustContext cannot elevate the in-flight turn to guardian.
|
|
392
|
-
const turnTrust =
|
|
393
|
-
ctx.currentTurnTrustContext ?? ctx.trustContext ?? FALLBACK_TURN_TRUST;
|
|
392
|
+
const turnTrust = ctx.getTurnOrRestingTrust() ?? FALLBACK_TURN_TRUST;
|
|
394
393
|
|
|
395
394
|
const toolContext: ToolContext = {
|
|
396
395
|
workingDir: ctx.workingDir,
|
|
@@ -2488,6 +2488,54 @@ export class Conversation {
|
|
|
2488
2488
|
return this.authContext;
|
|
2489
2489
|
}
|
|
2490
2490
|
|
|
2491
|
+
/**
|
|
2492
|
+
* Trust the in-flight turn is executing under.
|
|
2493
|
+
*
|
|
2494
|
+
* Use this for authorization and for routing a reply to the requester:
|
|
2495
|
+
* cases where substituting the conversation's owner would be wrong rather
|
|
2496
|
+
* than approximate. Provenance is not such a case; see
|
|
2497
|
+
* {@link getTurnOrRestingTrust} and `docs/architecture/turn-actor.md`.
|
|
2498
|
+
*
|
|
2499
|
+
* `undefined` when no turn recorded one, which is a gap in the entry point
|
|
2500
|
+
* rather than an answer. Deliberately does not fall back to the
|
|
2501
|
+
* conversation's trust: a caller that can accept the conversation's owner
|
|
2502
|
+
* instead spells `?? getTrustContext()`, so the substitution is visible
|
|
2503
|
+
* where it happens.
|
|
2504
|
+
*/
|
|
2505
|
+
getTurnTrust(): TrustContext | undefined {
|
|
2506
|
+
return this.currentTurnTrustContext;
|
|
2507
|
+
}
|
|
2508
|
+
|
|
2509
|
+
/**
|
|
2510
|
+
* Trust of the actor the conversation belongs to, independent of any turn.
|
|
2511
|
+
*
|
|
2512
|
+
* Use this where there is no turn to speak of: routes reporting on a
|
|
2513
|
+
* conversation, hydration, and persisting conversation-level options. A
|
|
2514
|
+
* caller that wants the conversation's owner *rather than* whoever is
|
|
2515
|
+
* currently acting should be obviously doing so; if it is not obvious,
|
|
2516
|
+
* {@link getTurnTrust} is probably the one meant.
|
|
2517
|
+
*/
|
|
2518
|
+
getTrustContext(): TrustContext | undefined {
|
|
2519
|
+
return this.trustContext;
|
|
2520
|
+
}
|
|
2521
|
+
|
|
2522
|
+
/**
|
|
2523
|
+
* Trust of the in-flight turn, or the conversation's owner when the turn
|
|
2524
|
+
* recorded none. The substitution is in the name: callers that can accept
|
|
2525
|
+
* the owner as a stand-in ask this, including provenance stamping, whose
|
|
2526
|
+
* readers treat an absent trust class as more trusted than `"unknown"`.
|
|
2527
|
+
* Callers for which the owner would be wrong rather than approximate call
|
|
2528
|
+
* {@link getTurnTrust} and handle `undefined`.
|
|
2529
|
+
*
|
|
2530
|
+
* The fallback half is load-bearing, not transitional politeness: a
|
|
2531
|
+
* deferred wake fires with no inbound actor, and refusing it an answer
|
|
2532
|
+
* denies every sensitive tool in the resumed turn (LUM-2929). It becomes
|
|
2533
|
+
* removable in one place when every entry point records a turn actor.
|
|
2534
|
+
*/
|
|
2535
|
+
getTurnOrRestingTrust(): TrustContext | undefined {
|
|
2536
|
+
return this.currentTurnTrustContext ?? this.trustContext;
|
|
2537
|
+
}
|
|
2538
|
+
|
|
2491
2539
|
/**
|
|
2492
2540
|
* The actor principal that owns the current turn, for host-proxy routing.
|
|
2493
2541
|
* Prefers the in-flight turn's actor over the conversation's resting
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The interactive-turn sender contract, in one place.
|
|
3
|
+
*
|
|
4
|
+
* `Conversation.sendToClient` is conversation-level mutable state: born a
|
|
5
|
+
* no-op, bound to the SSE hub for the duration of an interactive turn, and
|
|
6
|
+
* restored when that turn ends. Some subsystems read this conversation-level
|
|
7
|
+
* sender instead of the turn's own `onEvent` sink; the `PermissionPrompter`
|
|
8
|
+
* is the load-bearing one, so a `confirmation_request` raised by a turn that
|
|
9
|
+
* never bound the sender is emitted into the no-op, reaches no client, and
|
|
10
|
+
* hangs until the permission timeout auto-denies. Every path that runs an
|
|
11
|
+
* interactive agent turn MUST therefore bind before the loop and restore
|
|
12
|
+
* after it. The queue drain bypasses `processMessage`, so it uses this
|
|
13
|
+
* helper directly.
|
|
14
|
+
*
|
|
15
|
+
* The restore is a snapshot of the sender state taken at bind time, not an
|
|
16
|
+
* unconditional reset to a no-op: a live binding that predated the turn
|
|
17
|
+
* (e.g. installed by the send route while a non-interactive turn was
|
|
18
|
+
* running) survives the turn, so out-of-turn producers that read the live
|
|
19
|
+
* sender (call transcript and completion notifiers) keep their client. In
|
|
20
|
+
* the ordinary interactive flow the snapshot is the no-op the previous
|
|
21
|
+
* turn's restore left behind, so restoring it is identical to the reset
|
|
22
|
+
* this contract has always performed. The restore is also identity-guarded:
|
|
23
|
+
* it only replaces the hub binding this contract installed, so a sender
|
|
24
|
+
* some other subsystem rebound mid-turn to anything else is left alone.
|
|
25
|
+
*
|
|
26
|
+
* TRANSITIONAL: this helper is the paved road for a pattern we want less
|
|
27
|
+
* of. Bind/restore of shared mutable sender state is why the drain, the
|
|
28
|
+
* compaction card, and the host-browser proxy have each needed their own
|
|
29
|
+
* restore step. The durable design is a per-turn sink threaded to the
|
|
30
|
+
* prompter and the other conversation-level-sender readers, at which point
|
|
31
|
+
* this module and every call site of it should be deleted. Prefer that
|
|
32
|
+
* refactor over adding new bind/restore call sites.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import { broadcastMessage } from "../runtime/assistant-event-hub.js";
|
|
36
|
+
import { getSubagentManager } from "../subagent/index.js";
|
|
37
|
+
import type { Conversation } from "./conversation.js";
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Point the conversation-level sender (and the subagent parent sender) at
|
|
41
|
+
* the SSE hub for the duration of an interactive turn. Returns the restore
|
|
42
|
+
* closure the turn's cleanup must invoke.
|
|
43
|
+
*/
|
|
44
|
+
export function bindInteractiveTurnSender(
|
|
45
|
+
conversation: Conversation,
|
|
46
|
+
): () => void {
|
|
47
|
+
const previousSender = conversation.getCurrentSender();
|
|
48
|
+
const previousHasNoClient = conversation.hasNoClient;
|
|
49
|
+
conversation.updateClient(broadcastMessage, false);
|
|
50
|
+
getSubagentManager().updateParentSender(
|
|
51
|
+
conversation.conversationId,
|
|
52
|
+
broadcastMessage,
|
|
53
|
+
);
|
|
54
|
+
return () => {
|
|
55
|
+
if (conversation.getCurrentSender() === broadcastMessage) {
|
|
56
|
+
conversation.updateClient(previousSender, previousHasNoClient);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { getConfig, invalidateConfigCache } from "../config/loader.js";
|
|
9
|
+
import {
|
|
10
|
+
buildEffectiveMcpConfig,
|
|
11
|
+
pluginMcpServersChangedSinceLastBuild,
|
|
12
|
+
} from "../mcp/effective-config.js";
|
|
9
13
|
import { getMcpServerManager } from "../mcp/manager.js";
|
|
10
14
|
import { migrateLegacyMcpHeaders } from "../mcp/mcp-header-store.js";
|
|
11
15
|
import { createMcpToolsFromServer } from "../tools/mcp/mcp-tool-factory.js";
|
|
@@ -52,6 +56,29 @@ export function reloadMcpServers(): Promise<McpReloadResult> {
|
|
|
52
56
|
return reloadInProgress;
|
|
53
57
|
}
|
|
54
58
|
|
|
59
|
+
/**
|
|
60
|
+
* Reload MCP servers if — and only if — the plugin-declared set moved.
|
|
61
|
+
*
|
|
62
|
+
* Called by the plugin source reconcile, which every install / uninstall /
|
|
63
|
+
* upgrade / enable / disable already funnels through, so a plugin's MCP
|
|
64
|
+
* tools come up and go down with the plugin instead of lingering until an
|
|
65
|
+
* explicit reload, a config edit, or a restart.
|
|
66
|
+
*
|
|
67
|
+
* The guard matters: a reconcile also fires for plugin edits that touch no
|
|
68
|
+
* `mcp.json` at all, and an unconditional reload would tear down every
|
|
69
|
+
* healthy workspace connection to re-establish it for nothing.
|
|
70
|
+
*
|
|
71
|
+
* Never throws — the reconcile is best-effort and must not fail an install
|
|
72
|
+
* whose files already landed on disk.
|
|
73
|
+
*/
|
|
74
|
+
export async function reconcilePluginMcpServers(): Promise<void> {
|
|
75
|
+
if (!pluginMcpServersChangedSinceLastBuild()) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
log.info("Plugin-declared MCP servers changed; reloading");
|
|
79
|
+
await reloadMcpServers();
|
|
80
|
+
}
|
|
81
|
+
|
|
55
82
|
async function doReload(): Promise<McpReloadResult> {
|
|
56
83
|
try {
|
|
57
84
|
const manager = getMcpServerManager();
|
|
@@ -74,17 +101,20 @@ async function doReload(): Promise<McpReloadResult> {
|
|
|
74
101
|
// 2. Stop existing MCP servers + unregister their tools
|
|
75
102
|
await manager.stop();
|
|
76
103
|
unregisterAllMcpTools();
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
104
|
+
|
|
105
|
+
// Plugins are re-read here too: installing or removing one changes the
|
|
106
|
+
// server set exactly like editing config.json does, and both arrive
|
|
107
|
+
// through this same reload.
|
|
108
|
+
const mcpConfig = buildEffectiveMcpConfig(config.mcp);
|
|
109
|
+
const serverIds = Object.keys(mcpConfig.servers);
|
|
80
110
|
|
|
81
111
|
// 3. Restart MCP servers
|
|
82
112
|
let serverCount = 0;
|
|
83
113
|
let toolCount = 0;
|
|
84
114
|
const servers: McpReloadServerResult[] = [];
|
|
85
115
|
|
|
86
|
-
if (
|
|
87
|
-
const serverToolInfos = await manager.start(
|
|
116
|
+
if (serverIds.length > 0) {
|
|
117
|
+
const serverToolInfos = await manager.start(mcpConfig);
|
|
88
118
|
for (const { serverId, serverConfig, tools } of serverToolInfos) {
|
|
89
119
|
const mcpTools = createMcpToolsFromServer(
|
|
90
120
|
tools,
|
|
@@ -105,7 +135,7 @@ async function doReload(): Promise<McpReloadResult> {
|
|
|
105
135
|
// Include servers that were configured but failed to connect or are disabled
|
|
106
136
|
for (const id of serverIds) {
|
|
107
137
|
if (!servers.some((s) => s.id === id)) {
|
|
108
|
-
const serverConfig =
|
|
138
|
+
const serverConfig = mcpConfig.servers[id];
|
|
109
139
|
const isDisabled = serverConfig?.enabled === false;
|
|
110
140
|
servers.push({
|
|
111
141
|
id,
|
|
@@ -34,7 +34,6 @@ import { updateMetaFile } from "../persistence/conversation-disk-view.js";
|
|
|
34
34
|
import { broadcastMessage } from "../runtime/assistant-event-hub.js";
|
|
35
35
|
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../runtime/assistant-scope.js";
|
|
36
36
|
import { publishConversationMessagesChanged } from "../runtime/sync/resource-sync-events.js";
|
|
37
|
-
import { getSubagentManager } from "../subagent/index.js";
|
|
38
37
|
import {
|
|
39
38
|
readTurnFailure,
|
|
40
39
|
type TurnFailure,
|
|
@@ -70,7 +69,9 @@ import {
|
|
|
70
69
|
preactivateHostProxySkills,
|
|
71
70
|
shouldAttachHostProxyForCapability,
|
|
72
71
|
} from "./host-proxy-preactivation.js";
|
|
72
|
+
import { bindInteractiveTurnSender } from "./interactive-turn-sender.js";
|
|
73
73
|
import type { SubagentToolGateMode } from "./tool-setup-types.js";
|
|
74
|
+
import { restingTrust } from "./trust-context-types.js";
|
|
74
75
|
|
|
75
76
|
const log = getLogger("process-message");
|
|
76
77
|
|
|
@@ -455,7 +456,10 @@ export async function processMessage(
|
|
|
455
456
|
if (slashResult.kind === "unknown") {
|
|
456
457
|
const serverTurnCtx = conversation.getTurnChannelContext();
|
|
457
458
|
const serverProvenance = provenanceFromTrustContext(
|
|
458
|
-
|
|
459
|
+
// Ingress persists before any per-turn stamp; the slot was just written
|
|
460
|
+
// by this message's own resolution, and the per-turn field may still
|
|
461
|
+
// hold the previous turn's actor.
|
|
462
|
+
restingTrust(conversation),
|
|
459
463
|
);
|
|
460
464
|
const imageSourcePaths: Record<string, string> = {};
|
|
461
465
|
for (let i = 0; i < attachments.length; i++) {
|
|
@@ -557,7 +561,10 @@ export async function processMessage(
|
|
|
557
561
|
if (slashResult.kind === "compact") {
|
|
558
562
|
const serverTurnCtx = conversation.getTurnChannelContext();
|
|
559
563
|
const serverProvenance = provenanceFromTrustContext(
|
|
560
|
-
|
|
564
|
+
// Ingress persists before any per-turn stamp; the slot was just written
|
|
565
|
+
// by this message's own resolution, and the per-turn field may still
|
|
566
|
+
// hold the previous turn's actor.
|
|
567
|
+
restingTrust(conversation),
|
|
561
568
|
);
|
|
562
569
|
const compactChannelMeta = {
|
|
563
570
|
...serverProvenance,
|
|
@@ -612,7 +619,10 @@ export async function processMessage(
|
|
|
612
619
|
if (slashResult.kind === "clean") {
|
|
613
620
|
const serverTurnCtx = conversation.getTurnChannelContext();
|
|
614
621
|
const serverProvenance = provenanceFromTrustContext(
|
|
615
|
-
|
|
622
|
+
// Ingress persists before any per-turn stamp; the slot was just written
|
|
623
|
+
// by this message's own resolution, and the per-turn field may still
|
|
624
|
+
// hold the previous turn's actor.
|
|
625
|
+
restingTrust(conversation),
|
|
616
626
|
);
|
|
617
627
|
const cleanChannelMeta = {
|
|
618
628
|
...serverProvenance,
|
|
@@ -699,10 +709,10 @@ export async function processMessage(
|
|
|
699
709
|
};
|
|
700
710
|
}
|
|
701
711
|
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
712
|
+
const restoreSender =
|
|
713
|
+
options?.isInteractive === true
|
|
714
|
+
? bindInteractiveTurnSender(conversation)
|
|
715
|
+
: undefined;
|
|
706
716
|
|
|
707
717
|
const restoreToolScope = applyTurnToolAllowlist(conversation, options);
|
|
708
718
|
try {
|
|
@@ -721,12 +731,7 @@ export async function processMessage(
|
|
|
721
731
|
});
|
|
722
732
|
} finally {
|
|
723
733
|
restoreToolScope();
|
|
724
|
-
|
|
725
|
-
options?.isInteractive === true &&
|
|
726
|
-
conversation.getCurrentSender() === broadcastMessage
|
|
727
|
-
) {
|
|
728
|
-
conversation.updateClient(() => {}, true);
|
|
729
|
-
}
|
|
734
|
+
restoreSender?.();
|
|
730
735
|
}
|
|
731
736
|
|
|
732
737
|
// Read the just-finished turn's outcome from the stamp `runAgentLoop`'s
|
|
@@ -785,10 +790,10 @@ export async function processMessageInBackground(
|
|
|
785
790
|
return { messageId };
|
|
786
791
|
}
|
|
787
792
|
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
793
|
+
const restoreSender =
|
|
794
|
+
options?.isInteractive === true
|
|
795
|
+
? bindInteractiveTurnSender(conversation)
|
|
796
|
+
: undefined;
|
|
792
797
|
|
|
793
798
|
const restoreToolScope = applyTurnToolAllowlist(conversation, options);
|
|
794
799
|
conversation
|
|
@@ -804,12 +809,7 @@ export async function processMessageInBackground(
|
|
|
804
809
|
})
|
|
805
810
|
.finally(() => {
|
|
806
811
|
restoreToolScope();
|
|
807
|
-
|
|
808
|
-
options?.isInteractive === true &&
|
|
809
|
-
conversation.getCurrentSender() === broadcastMessage
|
|
810
|
-
) {
|
|
811
|
-
conversation.updateClient(() => {}, true);
|
|
812
|
-
}
|
|
812
|
+
restoreSender?.();
|
|
813
813
|
})
|
|
814
814
|
.catch((err) => {
|
|
815
815
|
log.error({ err, conversationId }, "Background agent loop failed");
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { maybeDefaultSpeechToManaged } from "../config/managed-speech-defaults.js";
|
|
2
2
|
import { rehydratePlatformCredentials } from "../config/platform-rehydration.js";
|
|
3
3
|
import type { AssistantConfig } from "../config/types.js";
|
|
4
|
+
import { buildEffectiveMcpConfig } from "../mcp/effective-config.js";
|
|
4
5
|
import { getMcpServerManager } from "../mcp/manager.js";
|
|
5
6
|
import { gmailMessagingProvider } from "../messaging/providers/gmail/adapter.js";
|
|
6
7
|
import { outlookMessagingProvider } from "../messaging/providers/outlook/adapter.js";
|
|
@@ -61,11 +62,13 @@ export async function initializeProvidersAndTools(
|
|
|
61
62
|
);
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
// Start MCP servers and
|
|
65
|
-
|
|
65
|
+
// Start MCP servers — workspace-configured and plugin-declared alike —
|
|
66
|
+
// and register their tools.
|
|
67
|
+
const mcpConfig = buildEffectiveMcpConfig(config.mcp);
|
|
68
|
+
if (Object.keys(mcpConfig.servers).length > 0) {
|
|
66
69
|
const manager = getMcpServerManager();
|
|
67
70
|
try {
|
|
68
|
-
const serverToolInfos = await manager.start(
|
|
71
|
+
const serverToolInfos = await manager.start(mcpConfig);
|
|
69
72
|
for (const { serverId, serverConfig, tools } of serverToolInfos) {
|
|
70
73
|
const mcpTools = createMcpToolsFromServer(
|
|
71
74
|
tools,
|
|
@@ -106,3 +106,32 @@ export function sameTrustIdentity(
|
|
|
106
106
|
a.guardianPrincipalId === b.guardianPrincipalId
|
|
107
107
|
);
|
|
108
108
|
}
|
|
109
|
+
|
|
110
|
+
/** The two trust fields a conversation-shaped value carries. */
|
|
111
|
+
export interface TrustCarrier {
|
|
112
|
+
currentTurnTrustContext?: TrustContext;
|
|
113
|
+
trustContext?: TrustContext;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* The acting turn's trust, else the owner's. Structural counterpart of
|
|
118
|
+
* `Conversation.getTurnOrRestingTrust` for call sites handed a
|
|
119
|
+
* conversation-shaped context rather than the class, so a partial test double
|
|
120
|
+
* needs only the fields it already has.
|
|
121
|
+
*/
|
|
122
|
+
export function turnOrRestingTrust(
|
|
123
|
+
carrier: TrustCarrier | undefined,
|
|
124
|
+
): TrustContext | undefined {
|
|
125
|
+
return carrier?.currentTurnTrustContext ?? carrier?.trustContext;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The owner's trust, independent of any turn. Structural counterpart of
|
|
130
|
+
* `Conversation.getTrustContext`; see `docs/architecture/turn-actor.md` for
|
|
131
|
+
* when the owner is the right actor.
|
|
132
|
+
*/
|
|
133
|
+
export function restingTrust(
|
|
134
|
+
carrier: Pick<TrustCarrier, "trustContext"> | undefined,
|
|
135
|
+
): TrustContext | undefined {
|
|
136
|
+
return carrier?.trustContext;
|
|
137
|
+
}
|
|
@@ -32,6 +32,7 @@ import type {
|
|
|
32
32
|
SubagentToolGateMode,
|
|
33
33
|
WakeToolContextPin,
|
|
34
34
|
} from "./tool-setup-types.js";
|
|
35
|
+
import { restingTrust } from "./trust-context-types.js";
|
|
35
36
|
|
|
36
37
|
const log = getLogger("wake-conversation-ops");
|
|
37
38
|
|
|
@@ -222,7 +223,7 @@ export async function persistWakeTailMessage(
|
|
|
222
223
|
const turnChannelCtx = conversation.getTurnChannelContext();
|
|
223
224
|
const turnInterfaceCtx = conversation.getTurnInterfaceContext();
|
|
224
225
|
const metadata: Record<string, unknown> = {
|
|
225
|
-
...provenanceFromTrustContext(conversation
|
|
226
|
+
...provenanceFromTrustContext(restingTrust(conversation)),
|
|
226
227
|
userMessageChannel: turnChannelCtx?.userMessageChannel ?? "vellum",
|
|
227
228
|
assistantMessageChannel:
|
|
228
229
|
turnChannelCtx?.assistantMessageChannel ?? "vellum",
|
|
@@ -301,7 +302,7 @@ export async function persistWakeTriggerMessage(
|
|
|
301
302
|
const turnChannelCtx = conversation.getTurnChannelContext();
|
|
302
303
|
const turnInterfaceCtx = conversation.getTurnInterfaceContext();
|
|
303
304
|
const metadata: Record<string, unknown> = {
|
|
304
|
-
...provenanceFromTrustContext(conversation
|
|
305
|
+
...provenanceFromTrustContext(restingTrust(conversation)),
|
|
305
306
|
userMessageChannel: turnChannelCtx?.userMessageChannel ?? "vellum",
|
|
306
307
|
assistantMessageChannel:
|
|
307
308
|
turnChannelCtx?.assistantMessageChannel ?? "vellum",
|