@vellumai/assistant 0.11.3 → 0.11.4-dev.202608190019.b94dbf2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +9 -3
- package/ARCHITECTURE.md +32 -10
- package/docs/architecture/memory.md +43 -5
- package/docs/architecture/security.md +96 -152
- package/docs/architecture/turn-actor.md +70 -0
- package/docs/browser-use-architecture-phase2.md +128 -56
- package/docs/flux-turn-detection-spike.md +248 -0
- package/docs/guardian-request-flow.md +35 -0
- package/docs/stt-provider-onboarding.md +3 -1
- package/knip.json +3 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +34 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +166 -1
- package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
- package/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/openapi.yaml +289 -113
- package/package.json +1 -1
- package/scripts/voice-ttft-spike.ts +3 -3
- package/scripts/write-plugin-api-shim.ts +10 -0
- package/src/__tests__/app-compiler.test.ts +38 -3
- package/src/__tests__/app-control-flow.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +2 -2
- package/src/__tests__/assistant-feature-flag-guard.test.ts +25 -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__/channel-inbound-disk-pressure.test.ts +4 -4
- package/src/__tests__/channel-setup-panel-ack.test.ts +1 -1
- package/src/__tests__/checker.test.ts +20 -314
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +6 -2
- package/src/__tests__/compaction-events.test.ts +8 -10
- package/src/__tests__/config-loader-backfill.test.ts +3 -3
- package/src/__tests__/config-schema.test.ts +25 -10
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
- 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 +32 -21
- package/src/__tests__/conversation-attachments.test.ts +0 -1
- package/src/__tests__/conversation-attention-store.test.ts +63 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +112 -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-load-history-repair.test.ts +209 -0
- package/src/__tests__/conversation-notifiers-provenance.test.ts +59 -0
- package/src/__tests__/conversation-queue.test.ts +154 -4
- package/src/__tests__/conversation-routes-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-routes-enabled-plugins.test.ts +1 -1
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +9 -9
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +1 -1
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +187 -102
- package/src/__tests__/conversation-runtime-workspace.test.ts +14 -10
- package/src/__tests__/conversation-slash-queue.test.ts +3 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-app-open.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +21 -14
- package/src/__tests__/conversation-surfaces-queued-emit.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-state-update.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-table-action.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -1
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +1 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +1 -1
- package/src/__tests__/credential-prompt-route.test.ts +7 -10
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +1 -0
- package/src/__tests__/custom-profile-ensure.test.ts +5 -1
- package/src/__tests__/default-plugin-names-guard.test.ts +12 -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__/document-sync-tags.test.ts +0 -75
- package/src/__tests__/dynamic-skill-background-guard.test.ts +0 -2
- package/src/__tests__/edit-propagation.test.ts +107 -11
- package/src/__tests__/file-ops-service.test.ts +163 -30
- package/src/__tests__/filesystem-tools.test.ts +23 -24
- package/src/__tests__/gateway-only-guard.test.ts +2 -5
- package/src/__tests__/helpers/gateway-classify-mock.ts +27 -6
- package/src/__tests__/helpers/mock-actor-context.ts +49 -0
- package/src/__tests__/helpers/mock-conversation.ts +13 -1
- package/src/__tests__/host-file-read-tool.test.ts +16 -19
- package/src/__tests__/http-user-message-parity.test.ts +1 -1
- package/src/__tests__/init-feature-flag-overrides.test.ts +49 -0
- package/src/__tests__/injector-chain.test.ts +63 -41
- package/src/__tests__/injector-disk-pressure.test.ts +11 -23
- package/src/__tests__/inline-skill-load-permissions.test.ts +47 -36
- package/src/__tests__/llm-context-resolution.test.ts +73 -1
- package/src/__tests__/llm-schema.test.ts +5 -2
- package/src/__tests__/managed-skill-lifecycle.test.ts +7 -0
- package/src/__tests__/mcp-list-plugin-servers.test.ts +250 -0
- package/src/__tests__/media-generate-image.test.ts +131 -21
- package/src/__tests__/memory-retrieval-hook.test.ts +100 -7
- package/src/__tests__/messages-read-boundary-guard.test.ts +134 -0
- package/src/__tests__/mtime-cache.test.ts +3 -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__/platform-bash-auto-approve.test.ts +0 -4
- package/src/__tests__/plugin-api-store-credential.test.ts +261 -0
- package/src/__tests__/plugin-api-webhook-url.test.ts +10 -7
- package/src/__tests__/plugin-disabled-state.test.ts +2 -0
- package/src/__tests__/plugin-effective-enabled-set.test.ts +55 -3
- package/src/__tests__/plugin-execution-context.test.ts +73 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -1
- package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +7 -4
- 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__/proxy-approval-callback.test.ts +1 -0
- package/src/__tests__/qdrant-manager.test.ts +14 -1
- package/src/__tests__/reaction-persistence.test.ts +200 -7
- package/src/__tests__/require-fresh-approval.test.ts +0 -4
- package/src/__tests__/risk-classification-boundary-guard.test.ts +115 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +460 -105
- package/src/__tests__/run-due-schedules.test.ts +21 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +187 -18
- package/src/__tests__/schedule-routes.test.ts +23 -0
- package/src/__tests__/schedule-store.test.ts +17 -0
- package/src/__tests__/scoped-approval-grants.test.ts +11 -6
- package/src/__tests__/secret-ingress-channel.test.ts +0 -1
- package/src/__tests__/secret-ingress-http.test.ts +1 -1
- package/src/__tests__/send-endpoint-busy.test.ts +3 -3
- package/src/__tests__/skills.test.ts +32 -0
- package/src/__tests__/slack-edit-ordering-characterization.test.ts +0 -1
- package/src/__tests__/starter-task-flow.test.ts +5 -4
- package/src/__tests__/subagent-call-site-routing.test.ts +31 -19
- package/src/__tests__/subagent-fork-prompt-role.test.ts +1 -1
- package/src/__tests__/subagent-spawn-and-await.test.ts +18 -17
- package/src/__tests__/subagent-tool-gate-mode.test.ts +1 -1
- package/src/__tests__/subagent-tools.test.ts +81 -101
- package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +1 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +132 -5
- package/src/__tests__/tool-executor.test.ts +68 -39
- package/src/__tests__/turn-events-store.test.ts +43 -0
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +1 -1
- package/src/__tests__/ui-shape-teaching.test.ts +33 -0
- package/src/__tests__/ui-visual-surface.test.ts +1 -1
- package/src/__tests__/ui-voice-picker-surface.test.ts +128 -0
- package/src/__tests__/ui-work-result-surface.test.ts +1 -1
- package/src/__tests__/user-plugin-loader.test.ts +3 -1
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
- package/src/__tests__/visible-app-context.test.ts +16 -9
- package/src/__tests__/voice-config-update.test.ts +40 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +5 -3
- package/src/__tests__/voice-session-bridge.test.ts +85 -29
- 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 +76 -3
- package/src/api/events/acp-auth-required.ts +55 -0
- package/src/api/events/host-file.ts +2 -2
- package/src/api/index.ts +7 -0
- package/src/api/surfaces.ts +12 -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 +194 -11
- package/src/calls/__tests__/voice-triage-escalate.test.ts +102 -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 +115 -41
- package/src/calls/voice-triage-escalate.ts +105 -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/bundled-modules.ts +29 -0
- package/src/cli/commands/credentials.help.ts +2 -2
- package/src/cli/commands/db/repair.ts +4 -8
- package/src/cli/commands/domain.ts +6 -3
- package/src/cli/commands/email.ts +6 -3
- package/src/cli/commands/inference-providers.ts +1 -1
- package/src/cli/commands/keys.ts +8 -3
- package/src/cli/commands/mcp.help.ts +13 -4
- package/src/cli/commands/mcp.ts +9 -0
- package/src/cli/commands/memory/__tests__/memory-v2.test.ts +57 -7
- package/src/cli/commands/memory/__tests__/memory-v3.test.ts +128 -5
- package/src/cli/commands/memory/index.help.ts +84 -28
- package/src/cli/commands/memory/index.ts +2 -0
- package/src/cli/commands/memory/memory-v2.ts +58 -54
- package/src/cli/commands/memory/memory-v3.ts +64 -0
- package/src/cli/commands/memory/memory-validate.ts +18 -0
- package/src/cli/commands/plugins.ts +85 -36
- package/src/cli/commands/schedules.ts +35 -1
- package/src/cli/commands/stt.help.ts +27 -2
- package/src/cli/commands/trust.ts +3 -14
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +39 -0
- package/src/cli/lib/bundled-marketplace.json +14 -1
- package/src/cli/lib/upgrade-plugin.ts +42 -0
- package/src/config/__tests__/balanced-model-experiment.test.ts +278 -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/assistant-feature-flags.ts +36 -15
- package/src/config/balanced-model-experiment.ts +35 -0
- 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/image-studio/SKILL.md +5 -4
- package/src/config/bundled-skills/image-studio/TOOLS.json +1 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +101 -0
- 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/skill-management/TOOLS.json +9 -3
- package/src/config/bundled-skills/subagent/SKILL.md +17 -12
- package/src/config/bundled-skills/subagent/TOOLS.json +4 -4
- 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 +11 -5
- package/src/config/default-profile-catalog.ts +179 -16
- package/src/config/default-profile-names.ts +4 -1
- package/src/config/default-provider-resolution.ts +4 -0
- package/src/config/feature-flag-registry.json +12 -12
- 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/config/skills.ts +9 -2
- package/src/context/outbound-sanitize.ts +96 -1
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +1 -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 +31 -19
- package/src/daemon/conversation-messaging.ts +5 -1
- package/src/daemon/conversation-notifiers.ts +20 -10
- package/src/daemon/conversation-process.ts +20 -6
- package/src/daemon/conversation-runtime-assembly.ts +12 -6
- package/src/daemon/conversation-store.ts +14 -4
- package/src/daemon/conversation-surfaces.ts +75 -26
- package/src/daemon/conversation-tool-setup.ts +3 -6
- package/src/daemon/conversation.ts +154 -51
- package/src/daemon/doordash-steps.ts +2 -2
- package/src/daemon/lifecycle.ts +14 -1
- package/src/daemon/mcp-reload-service.ts +36 -6
- package/src/daemon/process-message.ts +13 -26
- package/src/daemon/providers-setup.ts +6 -3
- package/src/daemon/trust-context-types.ts +29 -0
- package/src/daemon/trust-context.ts +12 -7
- package/src/daemon/wake-conversation-ops.ts +3 -2
- package/src/daemon/windows-compiled-entry.ts +4 -0
- package/src/documents/document-store.ts +143 -240
- package/src/hooks/hook-loader.ts +3 -3
- package/src/hooks/registry.ts +103 -45
- package/src/hooks/types.ts +5 -0
- package/src/inbound/__tests__/oauth-callback-url.test.ts +83 -0
- package/src/inbound/oauth-callback-url.ts +61 -0
- package/src/ipc/gateway-client.test.ts +1 -1
- package/src/ipc/gateway-client.ts +14 -21
- package/src/ipc/gateway-flag-listener.ts +17 -3
- 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 +1050 -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-manager.ts +16 -3
- 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 +598 -323
- package/src/live-voice/progress-narration.ts +277 -0
- package/src/live-voice/protocol.ts +21 -1
- package/src/live-voice/windows-compiled-live-voice.ts +4 -0
- 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/control.ts +1 -0
- package/src/monitoring/db-integrity-sample.ts +4 -5
- package/src/monitoring/plugin-auto-update.ts +128 -24
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/approval-card-data.ts +33 -0
- package/src/notifications/signal.ts +1 -0
- package/src/permissions/AGENTS.md +16 -0
- package/src/permissions/checker.test.ts +75 -122
- package/src/permissions/checker.ts +44 -560
- package/src/permissions/confirmation-guardian-request.test.ts +15 -11
- package/src/permissions/confirmation-guardian-request.ts +2 -2
- package/src/permissions/prompter.ts +1 -5
- 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 +182 -52
- package/src/persistence/delivery-crud.ts +150 -36
- package/src/persistence/embeddings/qdrant-manager.ts +84 -49
- 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/360-add-document-workspace-path.ts +5 -14
- 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/schema/documents.ts +4 -4
- package/src/persistence/steps.ts +9 -0
- package/src/plugin-api/__tests__/oauth-callback-url-export.test.ts +29 -0
- package/src/plugin-api/constants.ts +8 -0
- package/src/plugin-api/conversation-turn.ts +195 -5
- package/src/plugin-api/index.ts +38 -6
- package/src/plugin-api/resolve-credential.ts +3 -2
- package/src/plugin-api/store-credential.ts +146 -0
- package/src/plugin-api/vision-support.test.ts +1 -1
- package/src/plugin-api/webhook-url.ts +13 -11
- package/src/plugins/__tests__/mcp-servers.test.ts +371 -0
- package/src/plugins/defaults/main.ts +21 -7
- package/src/plugins/defaults/memory/AGENTS.md +15 -3
- 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/__tests__/memory-tier-boundary-guard.test.ts +0 -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/__tests__/conversation-graph-memory-v2-routing.test.ts +77 -0
- package/src/plugins/defaults/memory/graph/conversation-graph-memory.ts +24 -6
- 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 +17 -24
- package/src/plugins/defaults/memory/hooks/post-compact.ts +1 -4
- package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +53 -5
- 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/memory-retrospective-job.ts +4 -4
- package/src/plugins/defaults/memory/src/__tests__/memory-v3-gate-stats.test.ts +281 -0
- package/src/plugins/defaults/memory/src/memory-v2-routes.ts +12 -10
- package/src/plugins/defaults/memory/src/memory-v3-routes.ts +207 -0
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +147 -0
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +8 -0
- package/src/plugins/defaults/memory/substrate/__tests__/edge-index.test.ts +0 -30
- package/src/plugins/defaults/memory/substrate/__tests__/ingest.test.ts +73 -1
- package/src/plugins/defaults/memory/substrate/__tests__/page-index.test.ts +37 -0
- package/src/plugins/defaults/memory/substrate/__tests__/page-links.test.ts +208 -0
- package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +158 -1
- package/src/plugins/defaults/memory/substrate/__tests__/static-context.test.ts +195 -58
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +93 -24
- package/src/plugins/defaults/memory/substrate/edge-index.ts +0 -31
- package/src/plugins/defaults/memory/substrate/ingest.ts +59 -0
- package/src/plugins/defaults/memory/substrate/page-index.ts +35 -8
- package/src/plugins/defaults/memory/substrate/page-links.ts +133 -0
- package/src/plugins/defaults/memory/substrate/page-store.ts +10 -0
- package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +124 -34
- package/src/plugins/defaults/memory/substrate/skill-content.ts +8 -1
- package/src/plugins/defaults/memory/substrate/static-context.ts +160 -33
- 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/__tests__/injection.test.ts +18 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +66 -1
- package/src/plugins/defaults/memory/v3/card.ts +9 -9
- package/src/plugins/defaults/memory/v3/core-set.test.ts +7 -0
- package/src/plugins/defaults/memory/v3/core-set.ts +5 -1
- package/src/plugins/defaults/memory/v3/edge.ts +13 -57
- package/src/plugins/defaults/memory/v3/injector.ts +8 -0
- 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/v3/shadow-plugin.ts +23 -9
- package/src/plugins/defaults/memory/worker-control.ts +1 -0
- package/src/plugins/defaults/memory/worker.ts +6 -3
- package/src/plugins/defaults/worker-entrypoints.ts +3 -0
- package/src/plugins/external-plugin-loader.ts +47 -0
- package/src/plugins/mcp-servers.ts +361 -0
- package/src/plugins/mtime-cache.ts +37 -46
- package/src/plugins/pipeline.ts +14 -7
- package/src/plugins/plugin-execution-context.ts +35 -11
- package/src/plugins/worker-plugin-surface.ts +33 -0
- package/src/prompts/templates/system-sections.ts +0 -7
- package/src/providers/__tests__/connection-model-compat.test.ts +1 -1
- package/src/providers/__tests__/context-overflow-error.test.ts +24 -0
- 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 +25 -2
- package/src/providers/anthropic/__tests__/pause-turn-continuation.test.ts +170 -0
- package/src/providers/anthropic/client.ts +332 -241
- package/src/providers/call-site-routing.ts +30 -3
- package/src/providers/connection-resolution.ts +194 -11
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +4 -0
- package/src/providers/inference/adapter-factory.ts +3 -0
- 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/__tests__/chat-completions-provider-reasoning.test.ts +163 -40
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +125 -2
- package/src/providers/openai/chat-completions-provider.ts +133 -36
- 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 +625 -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 +679 -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/control.ts +1 -0
- package/src/routes/route-host-client.ts +1 -0
- package/src/routes/route-host-protocol.ts +7 -0
- package/src/routes/worker.ts +48 -15
- package/src/runtime/AGENTS.md +16 -17
- package/src/runtime/access-request-helper.ts +9 -12
- package/src/runtime/agent-wake.ts +18 -15
- package/src/runtime/effective-capabilities.test.ts +43 -0
- package/src/runtime/effective-capabilities.ts +27 -0
- package/src/runtime/guardian-reply-router.ts +6 -2
- 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 +522 -2
- 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 +132 -35
- package/src/runtime/routes/__tests__/schedule-routes-disarm-reason.test.ts +215 -0
- package/src/runtime/routes/__tests__/stt-routes.test.ts +25 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +55 -6
- package/src/runtime/routes/__tests__/user-route-dispatcher-host.test.ts +52 -1
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +133 -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 +181 -23
- package/src/runtime/routes/conversation-management-routes.ts +17 -12
- package/src/runtime/routes/conversation-query-routes.ts +40 -27
- package/src/runtime/routes/conversation-routes.ts +43 -29
- package/src/runtime/routes/credential-prompt-routes.ts +4 -7
- package/src/runtime/routes/credential-routes.ts +26 -82
- package/src/runtime/routes/default-provider-routes.ts +15 -0
- package/src/runtime/routes/documents-routes.ts +3 -222
- package/src/runtime/routes/inbound-message-handler.ts +21 -45
- 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 +101 -79
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +12 -7
- package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +91 -8
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +72 -79
- 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/playground/__tests__/inject-failures.test.ts +2 -0
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +3 -0
- package/src/runtime/routes/playground/inject-failures.ts +2 -2
- package/src/runtime/routes/playground/reset-circuit.ts +1 -1
- package/src/runtime/routes/plugins-routes.ts +38 -8
- package/src/runtime/routes/schedule-routes.ts +94 -7
- package/src/runtime/routes/settings-routes.ts +6 -6
- package/src/runtime/routes/stt-routes.ts +31 -25
- package/src/runtime/routes/surface-action-routes.ts +20 -19
- package/src/runtime/routes/surface-conversation-resolver.ts +3 -0
- package/src/runtime/routes/user-route-dispatcher.ts +69 -15
- package/src/runtime/routes/user-route-import.ts +108 -0
- package/src/runtime/routes/user-route-resolution.ts +18 -1
- package/src/runtime/routes/workspace-routes.ts +0 -9
- package/src/runtime/routes/workspace-utils.ts +3 -13
- package/src/runtime/services/conversation-serializer.ts +7 -2
- package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +68 -5
- package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +81 -0
- package/src/schedule/plugin-schedule-availability.ts +58 -0
- package/src/schedule/plugin-schedule-declarations.ts +23 -27
- package/src/schedule/plugin-schedule-reconciler.ts +12 -3
- package/src/schedule/schedule-store.ts +5 -1
- package/src/schedule/scheduler.ts +9 -4
- package/src/schedule/worker-control.ts +1 -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/__tests__/consult-prompt.test.ts +26 -15
- package/src/subagent/consult-context.ts +11 -11
- package/src/subagent/consult-prompt.ts +26 -35
- package/src/subagent/manager.ts +21 -40
- package/src/subagent/notify.ts +7 -1
- package/src/subagent/types.ts +15 -12
- package/src/tools/__tests__/tool-input-schemas.test.ts +7 -7
- package/src/tools/acp/spawn.test.ts +97 -0
- package/src/tools/acp/spawn.ts +38 -4
- package/src/tools/credentials/ref-parse.ts +35 -0
- package/src/tools/credentials/resolve.ts +4 -10
- package/src/tools/credentials/store.ts +168 -0
- package/src/tools/document/document-tool.ts +12 -3
- package/src/tools/executor.ts +53 -15
- package/src/tools/filesystem/read.ts +27 -10
- package/src/tools/host-filesystem/read.ts +27 -15
- package/src/tools/network/url-safety.ts +9 -16
- package/src/tools/permission-checker.ts +43 -52
- package/src/tools/registry.ts +2 -1
- package/src/tools/shared/filesystem/file-ops-service.ts +63 -35
- package/src/tools/shared/filesystem/legacy-read-args.ts +22 -0
- package/src/tools/shared/filesystem/types.ts +5 -5
- package/src/tools/skills/scaffold-managed.ts +25 -7
- package/src/tools/subagent/spawn.ts +28 -88
- package/src/tools/tool-types.ts +26 -13
- package/src/tools/types.ts +6 -6
- package/src/tools/ui-surface/surface-shape-docs.ts +12 -1
- 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/__tests__/worker-process-command.test.ts +37 -0
- package/src/util/logger.ts +16 -0
- package/src/util/think-tag-stream.ts +95 -0
- package/src/util/worker-process.ts +37 -4
- package/src/windows-compiled-cli.ts +32 -0
- package/src/windows-compiled-entry.ts +4 -0
- package/src/windows-compiled-logger.ts +6 -0
- package/src/windows-compiled-worker-entry.ts +29 -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/__tests__/document-workspace-file.test.ts +0 -467
- package/src/live-voice/__tests__/front-decision.test.ts +0 -645
- package/src/live-voice/front-decision.ts +0 -476
- package/src/permissions/ipc-risk-types.ts +0 -143
- package/src/permissions/risk-types.ts +0 -76
- package/src/subagent/__tests__/consult-transcript.test.ts +0 -184
- package/src/subagent/consult-transcript.ts +0 -90
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
* Shared types, constants, and utilities used across channel route modules.
|
|
3
3
|
*/
|
|
4
4
|
import type { ChannelId } from "../../channels/types.js";
|
|
5
|
-
import { DAEMON_INTERNAL_ASSISTANT_ID } from "../assistant-scope.js";
|
|
6
5
|
import {
|
|
7
6
|
type ApprovalAction,
|
|
8
7
|
type ApprovalDecisionResult,
|
|
@@ -10,11 +9,6 @@ import {
|
|
|
10
9
|
isApprovalAction,
|
|
11
10
|
} from "../channel-approval-types.js";
|
|
12
11
|
|
|
13
|
-
/** Canonicalize assistantId for channel ingress paths. */
|
|
14
|
-
export function canonicalChannelAssistantId(_assistantId: string): string {
|
|
15
|
-
return DAEMON_INTERNAL_ASSISTANT_ID;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
12
|
// ---------------------------------------------------------------------------
|
|
19
13
|
// Actor role
|
|
20
14
|
// ---------------------------------------------------------------------------
|
|
@@ -159,13 +159,13 @@ async function handleExchange(args: RouteHandlerArgs) {
|
|
|
159
159
|
|
|
160
160
|
const existing = getConnection(db, CONNECTION_NAME);
|
|
161
161
|
if (existing) {
|
|
162
|
-
// Stamp the provider with the auth: this row
|
|
163
|
-
//
|
|
164
|
-
//
|
|
165
|
-
// otherwise
|
|
162
|
+
// Stamp the provider with the auth: this row IS the subscription route
|
|
163
|
+
// (auth modality = provider identity), and dispatch derives the openai
|
|
164
|
+
// upstream from the identity per-request. Stamping also heals a
|
|
165
|
+
// claiming row whose provider would otherwise misroute the fresh token.
|
|
166
166
|
const updateResult = updateConnection(db, CONNECTION_NAME, {
|
|
167
167
|
auth: authInput,
|
|
168
|
-
provider: "
|
|
168
|
+
provider: "chatgpt",
|
|
169
169
|
});
|
|
170
170
|
if (!updateResult.ok) {
|
|
171
171
|
log.error(
|
|
@@ -177,7 +177,7 @@ async function handleExchange(args: RouteHandlerArgs) {
|
|
|
177
177
|
} else {
|
|
178
178
|
const createResult = createConnection(db, {
|
|
179
179
|
name: CONNECTION_NAME,
|
|
180
|
-
provider: "
|
|
180
|
+
provider: "chatgpt",
|
|
181
181
|
auth: authInput,
|
|
182
182
|
});
|
|
183
183
|
if (!createResult.ok) {
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* GET /v1/conversations — paginated conversation list
|
|
5
5
|
* GET /v1/conversations/unread-count - count of unseen foreground conversations
|
|
6
|
+
* GET /v1/conversations/sections - per-section totals for the sidebar index
|
|
6
7
|
* POST /v1/conversations/seen — record a seen signal (single)
|
|
7
8
|
* POST /v1/conversations/seen/bulk — record seen signals (batch)
|
|
8
9
|
* POST /v1/conversations/unread — mark a conversation unread
|
|
@@ -29,6 +30,7 @@ import { resolveConversationId } from "../../persistence/conversation-key-store.
|
|
|
29
30
|
import {
|
|
30
31
|
type ConversationListFilter,
|
|
31
32
|
countConversations,
|
|
33
|
+
countConversationSections,
|
|
32
34
|
countUnreadConversations,
|
|
33
35
|
listConversations,
|
|
34
36
|
listPinnedConversations,
|
|
@@ -155,6 +157,42 @@ const unreadConversationCountResponseSchema = z.object({
|
|
|
155
157
|
count: z.number(),
|
|
156
158
|
});
|
|
157
159
|
|
|
160
|
+
const sectionCountFields = {
|
|
161
|
+
total: z.number(),
|
|
162
|
+
unread: z.number(),
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* One renderable sidebar section. Discriminated by `kind` so clients get a
|
|
167
|
+
* typed union: group sections carry their metadata inline (one consistent
|
|
168
|
+
* snapshot, no join against `GET /v1/groups` that could disagree with it),
|
|
169
|
+
* channel sections carry only the id (labels are client-side i18n), and
|
|
170
|
+
* `chats` is the ungrouped-native bucket. In a view without channel sections
|
|
171
|
+
* the Chats card holds every ungrouped row: the buckets are disjoint, so
|
|
172
|
+
* that reading is `chats` plus the sum of `channel` entries.
|
|
173
|
+
*/
|
|
174
|
+
const conversationSectionSchema = z.discriminatedUnion("kind", [
|
|
175
|
+
z.object({ kind: z.literal("pinned"), ...sectionCountFields }),
|
|
176
|
+
z.object({
|
|
177
|
+
kind: z.literal("group"),
|
|
178
|
+
groupId: z.string(),
|
|
179
|
+
name: z.string(),
|
|
180
|
+
icon: z.string().nullable(),
|
|
181
|
+
sortPosition: z.number(),
|
|
182
|
+
...sectionCountFields,
|
|
183
|
+
}),
|
|
184
|
+
z.object({
|
|
185
|
+
kind: z.literal("channel"),
|
|
186
|
+
channelId: z.string(),
|
|
187
|
+
...sectionCountFields,
|
|
188
|
+
}),
|
|
189
|
+
z.object({ kind: z.literal("chats"), ...sectionCountFields }),
|
|
190
|
+
]);
|
|
191
|
+
|
|
192
|
+
const conversationSectionsResponseSchema = z.object({
|
|
193
|
+
sections: z.array(conversationSectionSchema),
|
|
194
|
+
});
|
|
195
|
+
|
|
158
196
|
// ---------------------------------------------------------------------------
|
|
159
197
|
// Helpers
|
|
160
198
|
// ---------------------------------------------------------------------------
|
|
@@ -171,28 +209,41 @@ function resolveOrThrow(rawId: string): string {
|
|
|
171
209
|
// Handlers
|
|
172
210
|
// ---------------------------------------------------------------------------
|
|
173
211
|
|
|
212
|
+
/**
|
|
213
|
+
* Read a query parameter that admits a closed set of values. Absent or empty
|
|
214
|
+
* reads as `undefined`; anything outside `accepted` is a 400. The strictness
|
|
215
|
+
* is the point: silently coercing a typo or a newer client's value to "no
|
|
216
|
+
* filter" would hand back the full list where a subset was asked for, and
|
|
217
|
+
* that skew is invisible to the client (it masks version skew too).
|
|
218
|
+
*/
|
|
219
|
+
function parseEnumQueryParam<const T extends readonly string[]>(
|
|
220
|
+
queryParams: Record<string, string | undefined>,
|
|
221
|
+
name: string,
|
|
222
|
+
accepted: T,
|
|
223
|
+
): T[number] | undefined {
|
|
224
|
+
const raw = queryParams[name];
|
|
225
|
+
if (raw === undefined || raw === "") {
|
|
226
|
+
return undefined;
|
|
227
|
+
}
|
|
228
|
+
if ((accepted as readonly string[]).includes(raw)) {
|
|
229
|
+
return raw as T[number];
|
|
230
|
+
}
|
|
231
|
+
throw new BadRequestError(
|
|
232
|
+
`Unknown ${name} "${raw}"; expected ${accepted.map((v) => `"${v}"`).join(" or ")}.`,
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
174
236
|
function handleListConversations({ queryParams = {} }: RouteHandlerArgs) {
|
|
175
237
|
const limit = Number(queryParams.limit ?? 50);
|
|
176
238
|
const offset = Number(queryParams.offset ?? 0);
|
|
177
239
|
// "background" is the back-compat umbrella (background + scheduled); newer
|
|
178
240
|
// clients can pass "scheduled" to load only the Scheduled section. Absent
|
|
179
|
-
// defaults to the standard foreground list.
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if (
|
|
186
|
-
rawConversationType === "background" ||
|
|
187
|
-
rawConversationType === "scheduled"
|
|
188
|
-
) {
|
|
189
|
-
conversationType = rawConversationType;
|
|
190
|
-
} else {
|
|
191
|
-
throw new BadRequestError(
|
|
192
|
-
`Unknown conversationType "${rawConversationType}"; expected "background" or "scheduled".`,
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
}
|
|
241
|
+
// defaults to the standard foreground list.
|
|
242
|
+
const conversationType: ConversationType =
|
|
243
|
+
parseEnumQueryParam(queryParams, "conversationType", [
|
|
244
|
+
"background",
|
|
245
|
+
"scheduled",
|
|
246
|
+
]) ?? "standard";
|
|
196
247
|
// Defaults to `active` so sidebar restores no longer pull archived rows.
|
|
197
248
|
// The Archive page opts into `archived` to render only archived rows
|
|
198
249
|
// without dragging the entire live history through pagination first.
|
|
@@ -213,11 +264,23 @@ function handleListConversations({ queryParams = {} }: RouteHandlerArgs) {
|
|
|
213
264
|
? queryParams.groupId
|
|
214
265
|
: undefined;
|
|
215
266
|
|
|
267
|
+
const needsAttention =
|
|
268
|
+
parseEnumQueryParam(queryParams, "needsAttention", ["true"]) === "true"
|
|
269
|
+
? true
|
|
270
|
+
: undefined;
|
|
271
|
+
|
|
272
|
+
const foregroundOnly =
|
|
273
|
+
parseEnumQueryParam(queryParams, "foregroundOnly", ["true"]) === "true"
|
|
274
|
+
? true
|
|
275
|
+
: undefined;
|
|
276
|
+
|
|
216
277
|
const filter: ConversationListFilter = {
|
|
217
278
|
conversationType,
|
|
218
279
|
archiveStatus,
|
|
219
280
|
originChannel,
|
|
220
281
|
groupId,
|
|
282
|
+
needsAttention,
|
|
283
|
+
foregroundOnly,
|
|
221
284
|
};
|
|
222
285
|
let rows = listConversations({ ...filter, limit, offset });
|
|
223
286
|
const totalCount = countConversations(filter);
|
|
@@ -232,15 +295,20 @@ function handleListConversations({ queryParams = {} }: RouteHandlerArgs) {
|
|
|
232
295
|
// Skipped for group-scoped queries for the same reason: a caller asking
|
|
233
296
|
// for one group gets that group, and a client that fetches the Pinned
|
|
234
297
|
// section via `groupId=system:pinned` has no use for rows appended to
|
|
235
|
-
// some other group's page.
|
|
236
|
-
//
|
|
237
|
-
//
|
|
298
|
+
// some other group's page. Skipped for attention-scoped and
|
|
299
|
+
// foreground-only queries too: the caller asked for a subset, and a
|
|
300
|
+
// pinned row outside it appended to the page would sit there while
|
|
301
|
+
// `hasMore` is computed from the filtered count. This is the
|
|
302
|
+
// compatibility shim for clients that still read Pinned out of the
|
|
303
|
+
// unfiltered list; it goes away once every section fetches its own group.
|
|
238
304
|
if (
|
|
239
305
|
offset === 0 &&
|
|
240
306
|
conversationType === "standard" &&
|
|
241
307
|
archiveStatus === "active" &&
|
|
242
308
|
originChannel === undefined &&
|
|
243
|
-
groupId === undefined
|
|
309
|
+
groupId === undefined &&
|
|
310
|
+
needsAttention === undefined &&
|
|
311
|
+
foregroundOnly === undefined
|
|
244
312
|
) {
|
|
245
313
|
const pinned = listPinnedConversations(archiveStatus);
|
|
246
314
|
const seen = new Set(rows.map((c) => c.id));
|
|
@@ -292,6 +360,64 @@ function handleGetUnreadConversationCount() {
|
|
|
292
360
|
return { count: countUnreadConversations() };
|
|
293
361
|
}
|
|
294
362
|
|
|
363
|
+
/**
|
|
364
|
+
* One row per non-empty sidebar section, no conversation rows. What this
|
|
365
|
+
* exists to answer without a list drain: which sections render at all,
|
|
366
|
+
* and what their rail badges say.
|
|
367
|
+
*
|
|
368
|
+
* A count row whose `group_id` names no existing non-system group is
|
|
369
|
+
* skipped rather than surfaced: those rows are unreachable as a section
|
|
370
|
+
* (the sidebar builds group sections from the groups table), and a
|
|
371
|
+
* dangling id is a transient state around group deletion, not a section.
|
|
372
|
+
*/
|
|
373
|
+
function handleGetConversationSections() {
|
|
374
|
+
const counts = countConversationSections();
|
|
375
|
+
const groupsById = new Map(listGroups().map((g) => [g.id, g]));
|
|
376
|
+
const sections: Array<z.infer<typeof conversationSectionSchema>> = [];
|
|
377
|
+
|
|
378
|
+
for (const row of counts.groups) {
|
|
379
|
+
if (row.groupId === "system:pinned") {
|
|
380
|
+
sections.push({ kind: "pinned", total: row.total, unread: row.unread });
|
|
381
|
+
continue;
|
|
382
|
+
}
|
|
383
|
+
const meta = groupsById.get(row.groupId);
|
|
384
|
+
if (!meta || meta.isSystemGroup) {
|
|
385
|
+
continue;
|
|
386
|
+
}
|
|
387
|
+
sections.push({
|
|
388
|
+
kind: "group",
|
|
389
|
+
groupId: row.groupId,
|
|
390
|
+
name: meta.name,
|
|
391
|
+
icon: meta.icon,
|
|
392
|
+
sortPosition: meta.sortPosition,
|
|
393
|
+
total: row.total,
|
|
394
|
+
unread: row.unread,
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
for (const row of counts.channels) {
|
|
399
|
+
if (row.channel === "vellum") {
|
|
400
|
+
sections.push({ kind: "chats", total: row.total, unread: row.unread });
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
sections.push({
|
|
404
|
+
kind: "channel",
|
|
405
|
+
channelId: row.channel,
|
|
406
|
+
total: row.total,
|
|
407
|
+
unread: row.unread,
|
|
408
|
+
});
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/* Every other section exists only when it has rows; Chats is the leftover
|
|
412
|
+
bucket and renders regardless, so its counts are part of the contract
|
|
413
|
+
even at zero. */
|
|
414
|
+
if (!sections.some((s) => s.kind === "chats")) {
|
|
415
|
+
sections.push({ kind: "chats", total: 0, unread: 0 });
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
return { sections };
|
|
419
|
+
}
|
|
420
|
+
|
|
295
421
|
function handleRecordSeen({ body = {}, headers }: RouteHandlerArgs) {
|
|
296
422
|
const rawConversationId = body.conversationId as string | undefined;
|
|
297
423
|
if (!rawConversationId) {
|
|
@@ -482,7 +608,23 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
482
608
|
type: "string",
|
|
483
609
|
required: false,
|
|
484
610
|
description:
|
|
485
|
-
'Filter to a single group, so each sidebar section can load independently of the paginated list. Pass "system:all" for conversations in no group, "system:pinned" for the Pinned section, or a custom group id. A group-scoped request is ordered
|
|
611
|
+
'Filter to a single group, so each sidebar section can load independently of the paginated list. Pass "system:all" for conversations in no group, "system:pinned" for the Pinned section, or a custom group id. A group-scoped request is recency-ordered like every list read (COALESCE(last_message_at, updated_at) descending) and never has pinned rows appended to it. Omit to span every group.',
|
|
612
|
+
},
|
|
613
|
+
{
|
|
614
|
+
name: "needsAttention",
|
|
615
|
+
type: "string",
|
|
616
|
+
required: false,
|
|
617
|
+
description:
|
|
618
|
+
'Pass "true" to return only conversations whose latest assistant message the user has not seen: the same predicate behind the unread count and the section index, so a client that keeps no complete conversation list can ask for exactly the rows its attention surfaces need. Composes with every other filter. Any value other than "true" is rejected. Omit to span every conversation.',
|
|
619
|
+
schema: { type: "string", enum: ["true"] },
|
|
620
|
+
},
|
|
621
|
+
{
|
|
622
|
+
name: "foregroundOnly",
|
|
623
|
+
type: "string",
|
|
624
|
+
required: false,
|
|
625
|
+
description:
|
|
626
|
+
'Pass "true" to return only user-facing foreground conversations, dropping the automated background and scheduled runs the standard listing admits when they are filed in a custom group (a surfaced run stays). This is the same predicate the unread count and the section index apply, so a client can ask for the newest conversation a user can open in one row instead of paging past runs it would skip. Composes with every other filter; a foreground-only first page never has pinned rows appended to it. Any value other than "true" is rejected. Omit to keep every visible row.',
|
|
627
|
+
schema: { type: "string", enum: ["true"] },
|
|
486
628
|
},
|
|
487
629
|
],
|
|
488
630
|
responseBody: listConversationsResponseSchema,
|
|
@@ -503,6 +645,21 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
503
645
|
responseBody: unreadConversationCountResponseSchema,
|
|
504
646
|
handler: handleGetUnreadConversationCount,
|
|
505
647
|
},
|
|
648
|
+
{
|
|
649
|
+
operationId: "getConversationSections",
|
|
650
|
+
endpoint: "conversations/sections",
|
|
651
|
+
method: "GET",
|
|
652
|
+
policy: {
|
|
653
|
+
requiredScopes: ["chat.read"],
|
|
654
|
+
allowedPrincipalTypes: ACTOR_PRINCIPALS,
|
|
655
|
+
},
|
|
656
|
+
summary: "Sidebar section index",
|
|
657
|
+
description:
|
|
658
|
+
"One row per renderable sidebar section (Pinned, each non-empty custom group, each origin channel with unassigned conversations, Chats) with total and unread counts and no conversation rows. Lets a client know which sections exist, and what their badges say, without fetching any conversation list. Totals follow the standard listing visibility; unread applies the same rules as GET /v1/conversations/unread-count scoped to the section. Chats is always present, even at zero: it is the leftover bucket and renders regardless.",
|
|
659
|
+
tags: ["conversations"],
|
|
660
|
+
responseBody: conversationSectionsResponseSchema,
|
|
661
|
+
handler: handleGetConversationSections,
|
|
662
|
+
},
|
|
506
663
|
{
|
|
507
664
|
operationId: "recordConversationSeen",
|
|
508
665
|
endpoint: "conversations/seen",
|
|
@@ -572,7 +729,8 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
572
729
|
},
|
|
573
730
|
pathParams: [{ name: "id", type: "uuid" }],
|
|
574
731
|
summary: "Get conversation detail",
|
|
575
|
-
description:
|
|
732
|
+
description:
|
|
733
|
+
"Retrieve a single conversation with full metadata. Rows the listing hides by type (legacy private rows) are not found here either.",
|
|
576
734
|
tags: ["conversations"],
|
|
577
735
|
responseBody: conversationDetailResponseSchema,
|
|
578
736
|
handler: handleGetConversation,
|
|
@@ -308,9 +308,8 @@ async function handleSummarizeConversation({ body = {} }: RouteHandlerArgs) {
|
|
|
308
308
|
statusText: "Summarizing conversation",
|
|
309
309
|
});
|
|
310
310
|
// The context-window usage push goes out on the same broadcast path as
|
|
311
|
-
// the result card below
|
|
312
|
-
//
|
|
313
|
-
// sender and a `sendToClient` emit would reach nobody.
|
|
311
|
+
// the result card below, so the two can never be delivered to different
|
|
312
|
+
// places.
|
|
314
313
|
const result = await conversation.summarizeUpToMessage(
|
|
315
314
|
beforeMessageId,
|
|
316
315
|
broadcastMessage,
|
|
@@ -692,17 +691,16 @@ async function handleRetryLastAssistantTurn({
|
|
|
692
691
|
const conversation = await getOrCreateConversationInstance(conversationId);
|
|
693
692
|
touchConversation(conversationId);
|
|
694
693
|
|
|
695
|
-
//
|
|
696
|
-
//
|
|
697
|
-
//
|
|
698
|
-
//
|
|
694
|
+
// Resolved before the busy gate so the awaits stay outside the
|
|
695
|
+
// check-then-claim window below, but bound only once the claim succeeds: a
|
|
696
|
+
// retry that loses the race is rejected, and a rejected request must not
|
|
697
|
+
// repoint the conversation's actor at its requester.
|
|
699
698
|
const actorPrincipalId = headers?.["x-vellum-actor-principal-id"];
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
}),
|
|
699
|
+
const retryTrustContext = await resolveVellumActorTrustContext(
|
|
700
|
+
actorPrincipalId,
|
|
701
|
+
{ healResetDrift: true },
|
|
704
702
|
);
|
|
705
|
-
|
|
703
|
+
const retrySourceActorPrincipalId =
|
|
706
704
|
await resolveActorPrincipalIdForLocalGuardian(actorPrincipalId);
|
|
707
705
|
|
|
708
706
|
// Synchronous check-then-claim (no await between them) so a concurrent
|
|
@@ -715,6 +713,13 @@ async function handleRetryLastAssistantTurn({
|
|
|
715
713
|
}
|
|
716
714
|
conversation.setProcessing(true);
|
|
717
715
|
|
|
716
|
+
// Bind the requesting actor's trust for the turn we just claimed. A freshly
|
|
717
|
+
// hydrated conversation has no trust context, and `loadFromDb` under an
|
|
718
|
+
// unset context filters guardian-provenance history — the re-run would see
|
|
719
|
+
// an empty transcript.
|
|
720
|
+
conversation.setTrustContext(retryTrustContext);
|
|
721
|
+
conversation.currentTurnSourceActorPrincipalId = retrySourceActorPrincipalId;
|
|
722
|
+
|
|
718
723
|
const requestId = uuidv7();
|
|
719
724
|
const abortController = new AbortController();
|
|
720
725
|
let discarded: ReturnType<typeof discardLastAssistantDisplayTurn>;
|
|
@@ -41,7 +41,10 @@ import {
|
|
|
41
41
|
withSuppressedConfigDiskWrites,
|
|
42
42
|
withSuppressedConfigDiskWritesSync,
|
|
43
43
|
} from "../../config/loader.js";
|
|
44
|
-
import {
|
|
44
|
+
import {
|
|
45
|
+
completeCustomProfile,
|
|
46
|
+
mergePreservingUnknownKeys,
|
|
47
|
+
} from "../../config/profile-materialization.js";
|
|
45
48
|
import { AssistantConfigSchema } from "../../config/schema.js";
|
|
46
49
|
import { getSchemaAtPath } from "../../config/schema-utils.js";
|
|
47
50
|
import {
|
|
@@ -50,6 +53,7 @@ import {
|
|
|
50
53
|
LLMConfigFragment,
|
|
51
54
|
ProfileEntry,
|
|
52
55
|
routingIdentityModelIssue,
|
|
56
|
+
unknownLlmProviderIssue,
|
|
53
57
|
} from "../../config/schemas/llm.js";
|
|
54
58
|
import { VALID_MEMORY_EMBEDDING_PROVIDERS } from "../../config/schemas/memory-storage.js";
|
|
55
59
|
import { ServiceModeSchema } from "../../config/schemas/services.js";
|
|
@@ -96,6 +100,7 @@ import { type LogRow } from "../../persistence/llm-request-log-store.js";
|
|
|
96
100
|
import { getMemoryRecallLogByMessageIds } from "../../plugins/defaults/memory/memory-recall-log-store.js";
|
|
97
101
|
import { getMemoryV2ActivationLogByMessageIds } from "../../plugins/defaults/memory/v2/activation-log-store.js";
|
|
98
102
|
import { getMemoryV3SelectionForInspectorByMessageIds } from "../../plugins/defaults/memory/v3/selection-log-store.js";
|
|
103
|
+
import { writableProfileProviderIssue } from "../../providers/connection-resolution.js";
|
|
99
104
|
import { ROUTING_IDENTITY_PROVIDERS } from "../../providers/inference/auth.js";
|
|
100
105
|
import { PROVIDERS_REQUIRING_BASE_URL_AND_MODELS } from "../../providers/inference/auth.js";
|
|
101
106
|
import {
|
|
@@ -1252,30 +1257,6 @@ function assertInvariantProfilesPreserved(
|
|
|
1252
1257
|
}
|
|
1253
1258
|
}
|
|
1254
1259
|
|
|
1255
|
-
/**
|
|
1256
|
-
* `{...raw, ...completed}` recursively: completed (schema-known) values win,
|
|
1257
|
-
* raw keys the schema stripped survive at every depth.
|
|
1258
|
-
*/
|
|
1259
|
-
function mergePreservingUnknownKeys(
|
|
1260
|
-
raw: Record<string, unknown>,
|
|
1261
|
-
completed: Record<string, unknown>,
|
|
1262
|
-
): Record<string, unknown> {
|
|
1263
|
-
const out: Record<string, unknown> = { ...raw, ...completed };
|
|
1264
|
-
for (const [key, value] of Object.entries(completed)) {
|
|
1265
|
-
const rawValue = raw[key];
|
|
1266
|
-
if (
|
|
1267
|
-
readPlainObject(value) !== undefined &&
|
|
1268
|
-
readPlainObject(rawValue) !== undefined
|
|
1269
|
-
) {
|
|
1270
|
-
out[key] = mergePreservingUnknownKeys(
|
|
1271
|
-
rawValue as Record<string, unknown>,
|
|
1272
|
-
value as Record<string, unknown>,
|
|
1273
|
-
);
|
|
1274
|
-
}
|
|
1275
|
-
}
|
|
1276
|
-
return out;
|
|
1277
|
-
}
|
|
1278
|
-
|
|
1279
1260
|
/**
|
|
1280
1261
|
* Custom profiles persist as complete overrides: any user-source, non-mix
|
|
1281
1262
|
* profile entry this write creates or changes is completed against the
|
|
@@ -1343,7 +1324,9 @@ function completeChangedCustomProfiles(
|
|
|
1343
1324
|
* would let the pair reach disk and be stripped on the next read — a silent
|
|
1344
1325
|
* no-op where the user expects a saved override.
|
|
1345
1326
|
*/
|
|
1346
|
-
function
|
|
1327
|
+
function collectProviderEntries(
|
|
1328
|
+
raw: Record<string, unknown>,
|
|
1329
|
+
): [string, { provider?: unknown; model?: unknown }][] {
|
|
1347
1330
|
const llm = raw.llm as
|
|
1348
1331
|
| {
|
|
1349
1332
|
default?: { provider?: unknown; model?: unknown } | null;
|
|
@@ -1371,10 +1354,40 @@ function assertRoutableIdentityEntries(raw: Record<string, unknown>): void {
|
|
|
1371
1354
|
}
|
|
1372
1355
|
}
|
|
1373
1356
|
}
|
|
1357
|
+
return entries;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
function assertRoutableIdentityEntries(
|
|
1361
|
+
preWrite: Record<string, unknown>,
|
|
1362
|
+
raw: Record<string, unknown>,
|
|
1363
|
+
): void {
|
|
1364
|
+
const entries = collectProviderEntries(raw);
|
|
1365
|
+
const priorProviders = new Map(
|
|
1366
|
+
collectProviderEntries(preWrite).flatMap(([label, entry]) =>
|
|
1367
|
+
typeof entry.provider === "string" ? [[label, entry.provider]] : [],
|
|
1368
|
+
),
|
|
1369
|
+
);
|
|
1374
1370
|
for (const [label, entry] of entries) {
|
|
1375
1371
|
if (typeof entry.provider !== "string") {
|
|
1376
1372
|
continue;
|
|
1377
1373
|
}
|
|
1374
|
+
// The provider schema is an open string (a stored value outside the
|
|
1375
|
+
// known set parses instead of stripping its profile), so write-time
|
|
1376
|
+
// membership is enforced here and at the profiles route. Scoped to
|
|
1377
|
+
// providers this write introduces or changes: a stored value outside
|
|
1378
|
+
// the known set is readable and dispatchable by design, and
|
|
1379
|
+
// re-validating it on every write would make all later settings saves
|
|
1380
|
+
// fail with no in-product repair path. Profiles accept entry names;
|
|
1381
|
+
// call-site fragments and the legacy default blob stay vendor-only
|
|
1382
|
+
// (overrides become model-only with the entries demolition).
|
|
1383
|
+
if (entry.provider !== priorProviders.get(label)) {
|
|
1384
|
+
const providerIssue = label.startsWith("llm.profiles.")
|
|
1385
|
+
? writableProfileProviderIssue(entry.provider)
|
|
1386
|
+
: unknownLlmProviderIssue(entry.provider);
|
|
1387
|
+
if (providerIssue) {
|
|
1388
|
+
throw new BadRequestError(`${providerIssue} (${label})`);
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1378
1391
|
const issue = routingIdentityModelIssue(
|
|
1379
1392
|
entry.provider,
|
|
1380
1393
|
typeof entry.model === "string" ? entry.model : undefined,
|
|
@@ -1396,7 +1409,7 @@ export async function commitConfigWrite(
|
|
|
1396
1409
|
const preWrite = loadRawConfig();
|
|
1397
1410
|
completeChangedCustomProfiles(preWrite, raw);
|
|
1398
1411
|
assertInvariantProfilesPreserved(preWrite, raw);
|
|
1399
|
-
assertRoutableIdentityEntries(raw);
|
|
1412
|
+
assertRoutableIdentityEntries(preWrite, raw);
|
|
1400
1413
|
|
|
1401
1414
|
// Suppress the file-watcher callback for the duration of the debounce
|
|
1402
1415
|
// window. Without this, the ConfigWatcher detects the config.json write
|
|
@@ -1849,6 +1849,11 @@ export async function handleSendMessage(
|
|
|
1849
1849
|
|
|
1850
1850
|
// Resolve guardian context from the AuthContext's actorPrincipalId via the
|
|
1851
1851
|
// gateway guardian binding: a vellum principal is the guardian or nobody.
|
|
1852
|
+
//
|
|
1853
|
+
// Resolved into a local; the conversation's slot is stamped only where this
|
|
1854
|
+
// request commits to running a turn, since that write is what supplies the
|
|
1855
|
+
// acting actor for a run and must not fire for a send that merely queues.
|
|
1856
|
+
let resolvedTrustCtx: TrustContext;
|
|
1852
1857
|
if (actorPrincipalId) {
|
|
1853
1858
|
// Dev bypass (HTTP auth disabled): the synthetic "dev-bypass" principal
|
|
1854
1859
|
// won't match any guardian binding. Resolve the real guardian principal and
|
|
@@ -1874,7 +1879,7 @@ export async function handleSendMessage(
|
|
|
1874
1879
|
trustCtx = healed;
|
|
1875
1880
|
}
|
|
1876
1881
|
}
|
|
1877
|
-
|
|
1882
|
+
resolvedTrustCtx = trustCtx;
|
|
1878
1883
|
} else {
|
|
1879
1884
|
let trustCtx = withSourceChannel(
|
|
1880
1885
|
sourceChannel,
|
|
@@ -1907,22 +1912,19 @@ export async function handleSendMessage(
|
|
|
1907
1912
|
);
|
|
1908
1913
|
}
|
|
1909
1914
|
}
|
|
1910
|
-
|
|
1915
|
+
resolvedTrustCtx = trustCtx;
|
|
1911
1916
|
}
|
|
1912
1917
|
} else {
|
|
1913
1918
|
// Service principals (svc_gateway) or tokens without an actor ID
|
|
1914
1919
|
// get a minimal guardian context so downstream code has something.
|
|
1915
|
-
|
|
1920
|
+
resolvedTrustCtx = { trustClass: "guardian", sourceChannel };
|
|
1916
1921
|
}
|
|
1917
1922
|
|
|
1918
|
-
// The trust this request's turn runs under
|
|
1919
|
-
//
|
|
1920
|
-
//
|
|
1921
|
-
//
|
|
1922
|
-
|
|
1923
|
-
// pointer elevation, the voice bridge), and the loop would otherwise
|
|
1924
|
-
// re-read it and run this turn as whoever wrote last.
|
|
1925
|
-
const turnTrustContext = conversation.trustContext;
|
|
1923
|
+
// The trust this request's turn runs under: the sender resolved above,
|
|
1924
|
+
// never a read of the shared slot. Between here and the loop that slot is
|
|
1925
|
+
// writable by paths that do not own this turn (channel ingress for another
|
|
1926
|
+
// actor, live-voice hydration, pointer elevation, the voice bridge).
|
|
1927
|
+
const turnTrustContext = resolvedTrustCtx;
|
|
1926
1928
|
|
|
1927
1929
|
const isInteractive = isInteractiveInterface(sourceInterface);
|
|
1928
1930
|
// Translate the dev-bypass actor principal to the real guardian principal
|
|
@@ -1979,20 +1981,18 @@ export async function handleSendMessage(
|
|
|
1979
1981
|
sourceActorPrincipalId,
|
|
1980
1982
|
);
|
|
1981
1983
|
}
|
|
1982
|
-
//
|
|
1983
|
-
//
|
|
1984
|
-
//
|
|
1985
|
-
//
|
|
1986
|
-
//
|
|
1987
|
-
//
|
|
1988
|
-
//
|
|
1989
|
-
// proxy explicitly below without relaxing `hasNoClient`.
|
|
1990
|
-
conversation.updateClient(broadcastMessage, !isInteractive);
|
|
1984
|
+
// Delivery needs no wiring: the conversation's sink is the SSE hub for its
|
|
1985
|
+
// whole life. Presence travels with the turn (`isInteractive` below), which
|
|
1986
|
+
// is what keeps host_bash/host_file/host_cu gated for non-desktop
|
|
1987
|
+
// interfaces. The chrome-extension interface is non-interactive (no SSE
|
|
1988
|
+
// prompter UI) but still has a connected client that can service
|
|
1989
|
+
// host_browser_request events; that single proxy is restored explicitly
|
|
1990
|
+
// below without claiming a human is present.
|
|
1991
1991
|
if (isInteractive) {
|
|
1992
|
-
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
);
|
|
1992
|
+
// A client that reconnected mid-phase resyncs the current activity state
|
|
1993
|
+
// and any subagent card it left stale while disconnected.
|
|
1994
|
+
conversation.replayActivityState();
|
|
1995
|
+
getSubagentManager().reannounceChildStatuses(mapping.conversationId);
|
|
1996
1996
|
}
|
|
1997
1997
|
|
|
1998
1998
|
// ── URL scan path: rewrite first message for scan onboarding ──
|
|
@@ -2055,12 +2055,15 @@ export async function handleSendMessage(
|
|
|
2055
2055
|
requestId: uuidv7(),
|
|
2056
2056
|
metadata: greetingMeta,
|
|
2057
2057
|
clientMessageId,
|
|
2058
|
+
// This path answers and returns without starting a turn, so it never
|
|
2059
|
+
// reaches the stamp below; name the sender on the row directly.
|
|
2060
|
+
trustContext: resolvedTrustCtx,
|
|
2058
2061
|
...(clientOs ? { requestClientOs: clientOs } : {}),
|
|
2059
2062
|
});
|
|
2060
2063
|
|
|
2061
2064
|
const conversationId = mapping.conversationId;
|
|
2062
2065
|
const channelMeta = buildChannelMetadata(sourceChannel, sourceInterface, {
|
|
2063
|
-
trustContext:
|
|
2066
|
+
trustContext: resolvedTrustCtx,
|
|
2064
2067
|
});
|
|
2065
2068
|
|
|
2066
2069
|
const assistantMsg = createAssistantMessage(cannedGreeting);
|
|
@@ -2167,10 +2170,9 @@ export async function handleSendMessage(
|
|
|
2167
2170
|
|
|
2168
2171
|
// Resolve the verified actor's external user ID and principal for inline
|
|
2169
2172
|
// approval routing from the conversation's guardian context.
|
|
2170
|
-
const verifiedActorExternalUserId =
|
|
2171
|
-
conversation.trustContext?.guardianExternalUserId;
|
|
2173
|
+
const verifiedActorExternalUserId = resolvedTrustCtx.guardianExternalUserId;
|
|
2172
2174
|
const verifiedActorPrincipalId =
|
|
2173
|
-
|
|
2175
|
+
resolvedTrustCtx.guardianPrincipalId ?? undefined;
|
|
2174
2176
|
|
|
2175
2177
|
// Try to consume the message as a guardian approval/rejection reply.
|
|
2176
2178
|
// On failure, degrade to the existing queue/auto-deny path rather than
|
|
@@ -2244,6 +2246,10 @@ export async function handleSendMessage(
|
|
|
2244
2246
|
sourceActorPrincipalId,
|
|
2245
2247
|
transport,
|
|
2246
2248
|
clientMessageId,
|
|
2249
|
+
// The sender's own trust, so the drain runs this message as the actor
|
|
2250
|
+
// who sent it rather than as whoever the slot happens to hold when the
|
|
2251
|
+
// queue is worked.
|
|
2252
|
+
trustContext: resolvedTrustCtx,
|
|
2247
2253
|
});
|
|
2248
2254
|
if (enqueueResult.rejected) {
|
|
2249
2255
|
return new RouteResponse(
|
|
@@ -2342,7 +2348,15 @@ export async function handleSendMessage(
|
|
|
2342
2348
|
// matching in-memory pending interaction (e.g. prompter timeouts).
|
|
2343
2349
|
await expireOrphanedGuardianRequests(mapping.conversationId);
|
|
2344
2350
|
|
|
2345
|
-
// Conversation is idle — persist and fire agent loop immediately
|
|
2351
|
+
// Conversation is idle — persist and fire agent loop immediately.
|
|
2352
|
+
//
|
|
2353
|
+
// Stamping the sender here rather than at resolution is what keeps the two
|
|
2354
|
+
// in step: the slot hydrates and scopes the turn started just below
|
|
2355
|
+
// (`ensureActorScopedHistory`, persisted provenance, the loop's own trust),
|
|
2356
|
+
// so it must name whoever this request is about to run as. A request that
|
|
2357
|
+
// queues instead returns above without stamping — it is not starting a run,
|
|
2358
|
+
// and its actor rides the queue item to the drain.
|
|
2359
|
+
conversation.setTrustContext(resolvedTrustCtx);
|
|
2346
2360
|
conversation.setTurnChannelContext({
|
|
2347
2361
|
userMessageChannel: sourceChannel,
|
|
2348
2362
|
assistantMessageChannel: sourceChannel,
|