@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
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import type { AssistantEvent } from "../api/index.js";
|
|
17
|
+
import type { ChannelId } from "../channels/types.js";
|
|
17
18
|
import type { LLMCallSite } from "../config/schemas/llm.js";
|
|
18
19
|
import type { UserMessageAttachment } from "../daemon/message-types/shared.js";
|
|
19
20
|
import type { ContentBlock, MediaSource } from "../providers/types.js";
|
|
@@ -22,12 +23,62 @@ import type { ContentBlock, MediaSource } from "../providers/types.js";
|
|
|
22
23
|
// Public types
|
|
23
24
|
// ---------------------------------------------------------------------------
|
|
24
25
|
|
|
26
|
+
/**
|
|
27
|
+
* The chat a turn belongs to, in the channel's own terms.
|
|
28
|
+
*
|
|
29
|
+
* Supplied instead of a `conversationId` when the caller knows where the
|
|
30
|
+
* message came from but not which conversation that is. The pair resolves to
|
|
31
|
+
* the same conversation an inbound message on those coordinates would land
|
|
32
|
+
* in, because it resolves through the same binding.
|
|
33
|
+
*
|
|
34
|
+
* That sameness is the reason this exists at all. A caller could keep its own
|
|
35
|
+
* `externalChatId -> conversationId` map and pass the id, and turns would run
|
|
36
|
+
* in the right place. What it could not do is make the rest of the assistant
|
|
37
|
+
* agree: conversation reset is addressed by channel coordinates
|
|
38
|
+
* (`handleDeleteConversation`), the deny lanes attach an access-request card
|
|
39
|
+
* by them (`findInboundConversationId`), and the conversation and session
|
|
40
|
+
* APIs read the channel, chat name and sender off the external binding. A
|
|
41
|
+
* private map is a second name for the same conversation, and everything
|
|
42
|
+
* keyed on the public one quietly misses.
|
|
43
|
+
*/
|
|
44
|
+
export interface ConversationChannelAddress {
|
|
45
|
+
/** Channel the message arrived on. */
|
|
46
|
+
sourceChannel: ChannelId;
|
|
47
|
+
/** The chat, in the channel's own id space. */
|
|
48
|
+
externalChatId: string;
|
|
49
|
+
/**
|
|
50
|
+
* Thread within the chat, where the channel has threads. Only Slack and
|
|
51
|
+
* Telegram scope a conversation by thread; elsewhere this is carried as
|
|
52
|
+
* binding metadata and does not split the conversation.
|
|
53
|
+
*/
|
|
54
|
+
externalThreadId?: string | null;
|
|
55
|
+
/** Human-readable chat name, for the conversation list. */
|
|
56
|
+
externalChatName?: string | null;
|
|
57
|
+
/** Who sent it, in the channel's id space. */
|
|
58
|
+
externalUserId?: string | null;
|
|
59
|
+
displayName?: string | null;
|
|
60
|
+
username?: string | null;
|
|
61
|
+
}
|
|
62
|
+
|
|
25
63
|
export interface RunConversationTurnOptions {
|
|
26
64
|
/**
|
|
27
65
|
* Conversation to run the turn in. If omitted, a new conversation is
|
|
28
|
-
* created (its ID is generated with `uuidv7` and returned in the result)
|
|
66
|
+
* created (its ID is generated with `uuidv7` and returned in the result),
|
|
67
|
+
* unless {@link RunConversationTurnOptions.channel} says which chat this
|
|
68
|
+
* belongs to, in which case that chat's conversation is used.
|
|
29
69
|
*/
|
|
30
70
|
conversationId?: string;
|
|
71
|
+
/**
|
|
72
|
+
* The chat this turn belongs to, resolved to a conversation and bound to
|
|
73
|
+
* the channel. See {@link ConversationChannelAddress}.
|
|
74
|
+
*
|
|
75
|
+
* Ignored when `conversationId` is given: an explicit conversation is the
|
|
76
|
+
* caller saying which one, and re-resolving would overrule it. A caller
|
|
77
|
+
* that wants both the binding and a conversation of its own choosing is
|
|
78
|
+
* describing two different conversations, which is a bug worth surfacing
|
|
79
|
+
* as one rather than silently picking a winner.
|
|
80
|
+
*/
|
|
81
|
+
channel?: ConversationChannelAddress;
|
|
31
82
|
/**
|
|
32
83
|
* User message content blocks for this turn. Text blocks become the
|
|
33
84
|
* user message body; image/file blocks are resolved to inline
|
|
@@ -121,6 +172,77 @@ function extractContentAndAttachments(
|
|
|
121
172
|
return { text: textParts.join("\n"), attachments };
|
|
122
173
|
}
|
|
123
174
|
|
|
175
|
+
/**
|
|
176
|
+
* Resolve a chat to its conversation, binding the two if they are not already.
|
|
177
|
+
*
|
|
178
|
+
* Two writes, the same two `handleChannelInbound` makes for an arriving
|
|
179
|
+
* message, and for the same reasons. The conversation key is what makes the
|
|
180
|
+
* chat resolve to this conversation next time and what conversation reset
|
|
181
|
+
* addresses; the external binding is what the conversation and session APIs
|
|
182
|
+
* read to show which channel a conversation came from and who is on the other
|
|
183
|
+
* end. One without the other is a conversation that is half-registered, and
|
|
184
|
+
* which half is missing decides which feature quietly stops working.
|
|
185
|
+
*
|
|
186
|
+
* Deliberately not `recordInbound`, which is the same resolution plus an
|
|
187
|
+
* inbound event row. That row exists to dedup a vendor's redelivery and to
|
|
188
|
+
* correlate later edits, and a caller driving a turn on its own schedule has
|
|
189
|
+
* neither a vendor nor a message id to correlate. Plugin deliveries are
|
|
190
|
+
* deduped upstream in the gateway before the message ever gets here.
|
|
191
|
+
*/
|
|
192
|
+
async function resolveChannelConversation(
|
|
193
|
+
channel: ConversationChannelAddress,
|
|
194
|
+
conversationType?: "standard" | "background",
|
|
195
|
+
): Promise<{ conversationId: string; created: boolean }> {
|
|
196
|
+
const { findInboundConversationId, resolveInboundConversation } =
|
|
197
|
+
await import("../persistence/delivery-crud.js");
|
|
198
|
+
const { upsertBinding } =
|
|
199
|
+
await import("../persistence/external-conversation-store.js");
|
|
200
|
+
|
|
201
|
+
// Asked before resolving, because resolving is what creates it: once the id
|
|
202
|
+
// is in hand a new conversation is indistinguishable from an existing one,
|
|
203
|
+
// and the caller announces only the new. `findInboundConversationId` is the
|
|
204
|
+
// read-only mirror of the resolution below, so the two agree on what
|
|
205
|
+
// "already bound" means, including the Slack flat-key alias.
|
|
206
|
+
const created =
|
|
207
|
+
findInboundConversationId(
|
|
208
|
+
channel.sourceChannel,
|
|
209
|
+
channel.externalChatId,
|
|
210
|
+
channel.externalThreadId,
|
|
211
|
+
) === null;
|
|
212
|
+
|
|
213
|
+
const { conversationId } = resolveInboundConversation(
|
|
214
|
+
channel.sourceChannel,
|
|
215
|
+
channel.externalChatId,
|
|
216
|
+
channel.externalThreadId,
|
|
217
|
+
// Both apply only if this call is what mints the conversation. `origin` is
|
|
218
|
+
// first-message attribution, which a chat's first turn is the only chance
|
|
219
|
+
// to record.
|
|
220
|
+
{
|
|
221
|
+
origin: channel.sourceChannel,
|
|
222
|
+
...(conversationType ? { conversationType } : {}),
|
|
223
|
+
},
|
|
224
|
+
);
|
|
225
|
+
|
|
226
|
+
// Refreshed on every turn rather than only on creation, because the sender's
|
|
227
|
+
// display name and the chat's name are the vendor's to change and the
|
|
228
|
+
// conversation list shows whichever we last heard. The optional fields pass
|
|
229
|
+
// through as given: a caller that omits one on a later turn knows only the
|
|
230
|
+
// chat coordinates this time, which `upsertBinding` reads as silence rather
|
|
231
|
+
// than as the sender having no name.
|
|
232
|
+
upsertBinding({
|
|
233
|
+
conversationId,
|
|
234
|
+
sourceChannel: channel.sourceChannel,
|
|
235
|
+
externalChatId: channel.externalChatId,
|
|
236
|
+
externalChatName: channel.externalChatName,
|
|
237
|
+
externalThreadId: channel.externalThreadId ?? null,
|
|
238
|
+
externalUserId: channel.externalUserId,
|
|
239
|
+
displayName: channel.displayName,
|
|
240
|
+
username: channel.username,
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
return { conversationId, created };
|
|
244
|
+
}
|
|
245
|
+
|
|
124
246
|
// ---------------------------------------------------------------------------
|
|
125
247
|
// Public API
|
|
126
248
|
// ---------------------------------------------------------------------------
|
|
@@ -158,6 +280,9 @@ export async function runConversationTurn(
|
|
|
158
280
|
await import("../persistence/conversation-crud.js");
|
|
159
281
|
const { publishConversationListAndMetadataChanged } =
|
|
160
282
|
await import("../runtime/sync/resource-sync-events.js");
|
|
283
|
+
const { parseInterfaceId } = await import("../channels/types.js");
|
|
284
|
+
const { resolveChannelCapabilities } =
|
|
285
|
+
await import("../daemon/conversation-runtime-assembly.js");
|
|
161
286
|
|
|
162
287
|
// Plugin-driven turns run as the guardian: plugins are installed by the
|
|
163
288
|
// guardian, so their conversations inherit guardian trust. This lets the
|
|
@@ -166,8 +291,25 @@ export async function runConversationTurn(
|
|
|
166
291
|
// below) without requiring a client to approve prompts.
|
|
167
292
|
const { INTERNAL_GUARDIAN_TRUST_CONTEXT } =
|
|
168
293
|
await import("../daemon/trust-context.js");
|
|
169
|
-
|
|
170
|
-
|
|
294
|
+
|
|
295
|
+
// A channel address resolves through the same binding an inbound message
|
|
296
|
+
// uses, so a turn addressed by chat lands in that chat's conversation
|
|
297
|
+
// rather than a private one only this caller can find.
|
|
298
|
+
const channelConversation =
|
|
299
|
+
!options.conversationId && options.channel
|
|
300
|
+
? await resolveChannelConversation(
|
|
301
|
+
options.channel,
|
|
302
|
+
options.conversationType,
|
|
303
|
+
)
|
|
304
|
+
: undefined;
|
|
305
|
+
const conversationId =
|
|
306
|
+
options.conversationId ?? channelConversation?.conversationId ?? uuidv7();
|
|
307
|
+
// A channel address reports its own novelty, since resolving it creates the
|
|
308
|
+
// row: asking the DB here answers yes for a conversation minted a line ago
|
|
309
|
+
// and swallows the announcement it is owed.
|
|
310
|
+
const rowExisted = channelConversation
|
|
311
|
+
? !channelConversation.created
|
|
312
|
+
: getConversation(conversationId) != null;
|
|
171
313
|
const conversation = await getOrCreateConversation(conversationId, {
|
|
172
314
|
trustContext: INTERNAL_GUARDIAN_TRUST_CONTEXT,
|
|
173
315
|
...(options.conversationType
|
|
@@ -199,6 +341,51 @@ export async function runConversationTurn(
|
|
|
199
341
|
resolveMediaSourceData,
|
|
200
342
|
);
|
|
201
343
|
|
|
344
|
+
// The channel this turn speaks on. Runtime assembly reads the per-turn
|
|
345
|
+
// context first and falls back to the conversation's `originChannel`, then
|
|
346
|
+
// to `vellum`, so a turn into a conversation carrying no origin runs as
|
|
347
|
+
// `vellum` without this: the wrong channel for the message row and for the
|
|
348
|
+
// channel-permission cascade the tools are approved against. Null when the
|
|
349
|
+
// caller names no channel, which both restores that fallback and clears any
|
|
350
|
+
// context left by a previous turn.
|
|
351
|
+
const turnChannelContext = options.channel
|
|
352
|
+
? {
|
|
353
|
+
userMessageChannel: options.channel.sourceChannel,
|
|
354
|
+
assistantMessageChannel: options.channel.sourceChannel,
|
|
355
|
+
}
|
|
356
|
+
: null;
|
|
357
|
+
// The interface the message arrived on. `plugin` is in both the channel
|
|
358
|
+
// and interface unions, matching gateway inbound. Tool gating reads this
|
|
359
|
+
// as `transportInterface`; unset, the loop falls back to `web`.
|
|
360
|
+
const turnInterfaceContext = options.channel
|
|
361
|
+
? (() => {
|
|
362
|
+
const iface = parseInterfaceId(options.channel.sourceChannel);
|
|
363
|
+
return iface
|
|
364
|
+
? {
|
|
365
|
+
userMessageInterface: iface,
|
|
366
|
+
assistantMessageInterface: iface,
|
|
367
|
+
}
|
|
368
|
+
: null;
|
|
369
|
+
})()
|
|
370
|
+
: null;
|
|
371
|
+
if (options.channel) {
|
|
372
|
+
conversation.setChannelCapabilities(
|
|
373
|
+
resolveChannelCapabilities(
|
|
374
|
+
options.channel.sourceChannel,
|
|
375
|
+
turnInterfaceContext?.userMessageInterface,
|
|
376
|
+
),
|
|
377
|
+
);
|
|
378
|
+
}
|
|
379
|
+
// Carried on the metadata as well, because a queued turn is drained after
|
|
380
|
+
// this call returns and reads its channel from there. Without it the drain
|
|
381
|
+
// inherits whichever turn was in flight, which on a shared conversation is
|
|
382
|
+
// some other channel entirely.
|
|
383
|
+
const metadata = {
|
|
384
|
+
...PLUGIN_TURN_MESSAGE_METADATA,
|
|
385
|
+
...(turnChannelContext ?? {}),
|
|
386
|
+
...(turnInterfaceContext ?? {}),
|
|
387
|
+
};
|
|
388
|
+
|
|
202
389
|
// Build the event emitter: fan out to SSE/event-hub subscribers via
|
|
203
390
|
// broadcastMessage, then invoke the collector callback. This mirrors the
|
|
204
391
|
// buildEventEmitter pattern from process-message.ts so plugin-driven
|
|
@@ -221,7 +408,7 @@ export async function runConversationTurn(
|
|
|
221
408
|
onEvent,
|
|
222
409
|
requestId,
|
|
223
410
|
isInteractive: false,
|
|
224
|
-
metadata
|
|
411
|
+
metadata,
|
|
225
412
|
});
|
|
226
413
|
if (enqueueResult.rejected) {
|
|
227
414
|
throw new Error(
|
|
@@ -236,12 +423,15 @@ export async function runConversationTurn(
|
|
|
236
423
|
};
|
|
237
424
|
}
|
|
238
425
|
|
|
426
|
+
conversation.setTurnChannelContext(turnChannelContext);
|
|
427
|
+
conversation.setTurnInterfaceContext(turnInterfaceContext);
|
|
428
|
+
|
|
239
429
|
const userMessageId = await conversation.processMessage({
|
|
240
430
|
content: text,
|
|
241
431
|
attachments,
|
|
242
432
|
onEvent,
|
|
243
433
|
isInteractive: false,
|
|
244
|
-
metadata
|
|
434
|
+
metadata,
|
|
245
435
|
...(options.callSite ? { callSite: options.callSite } : {}),
|
|
246
436
|
});
|
|
247
437
|
|
package/src/plugin-api/index.ts
CHANGED
|
@@ -67,7 +67,11 @@
|
|
|
67
67
|
*/
|
|
68
68
|
|
|
69
69
|
export type { HookName } from "./constants.js";
|
|
70
|
-
export {
|
|
70
|
+
export {
|
|
71
|
+
HOOKS,
|
|
72
|
+
INTERNAL_NUDGE_OUTPUT_SUPPRESSION,
|
|
73
|
+
VOICE_ESCALATION_CONTINUATION_MESSAGE_KIND,
|
|
74
|
+
} from "./constants.js";
|
|
71
75
|
// Conversation message/content shapes. A hook receives the live message
|
|
72
76
|
// history (e.g. `PostToolUseContext.latestMessages: Message[]`), so plugins
|
|
73
77
|
// that inspect or narrow content blocks — reading a `tool_use` block's input,
|
|
@@ -173,6 +177,18 @@ export {
|
|
|
173
177
|
CredentialResolutionError,
|
|
174
178
|
resolveCredential,
|
|
175
179
|
} from "./resolve-credential.js";
|
|
180
|
+
// Store a credential's plaintext value (the same write `assistant credentials
|
|
181
|
+
// set` performs), creating it or replacing an existing one, named by UUID or a
|
|
182
|
+
// "service/field" reference. A plugin may only write credentials whose `field`
|
|
183
|
+
// matches its manifest name, and the write fails closed with no plugin in
|
|
184
|
+
// context. Throws CredentialStoreError when there is no calling plugin, the ref
|
|
185
|
+
// is malformed, the value is invalid, the store rejects the write, or the
|
|
186
|
+
// credential is out of the plugin's scope.
|
|
187
|
+
export type {
|
|
188
|
+
StoreCredentialOptions,
|
|
189
|
+
StoredCredentialRef,
|
|
190
|
+
} from "./store-credential.js";
|
|
191
|
+
export { CredentialStoreError, storeCredential } from "./store-credential.js";
|
|
176
192
|
// Resolve the public URL a third party should deliver to for one of the
|
|
177
193
|
// plugin's own ingress routes. Which URL is correct depends on how the
|
|
178
194
|
// assistant is reachable (a managed platform callback route, or a configured
|
|
@@ -180,6 +196,16 @@ export {
|
|
|
180
196
|
// the same resolution as `webhooks register`, and registers the callback route
|
|
181
197
|
// on the managed branches. The plugin defaults to the one in context.
|
|
182
198
|
export { resolveWebhookUrl, type WebhookUrlOptions } from "./webhook-url.js";
|
|
199
|
+
// Resolve the redirect URI an authorization server should send a user back
|
|
200
|
+
// to after consent. One shared route serves every OAuth flow in the
|
|
201
|
+
// assistant, demultiplexed by OAuth `state`, so this takes no arguments and
|
|
202
|
+
// returns the same URL for every caller and every attempt. A plugin needs it
|
|
203
|
+
// when it publishes something that has to name the redirect URI ahead of the
|
|
204
|
+
// flow, such as a Client ID Metadata Document, whose `redirect_uris` an
|
|
205
|
+
// authorization server matches exactly. Throws when no public ingress is
|
|
206
|
+
// configured and the assistant is not connected to the platform, which is the
|
|
207
|
+
// case where no URL would work.
|
|
208
|
+
export { resolveOauthCallbackUrl } from "../inbound/oauth-callback-url.js";
|
|
183
209
|
// Resolve a provider for a call site (optionally overriding the profile) so a
|
|
184
210
|
// plugin can run inference through the workspace's configured profiles and
|
|
185
211
|
// credentials — managed-proxy or BYOK — without supplying its own API key.
|
|
@@ -345,21 +371,26 @@ export { synthesizeText, TtsSynthesisError } from "../tts/synthesize-text.js";
|
|
|
345
371
|
export type { TtsSynthesisResult } from "../tts/types.js";
|
|
346
372
|
// Streaming speech-to-text — open a live transcription session against the
|
|
347
373
|
// assistant's globally configured STT provider stack. The plugin feeds audio
|
|
348
|
-
// chunks via `sendAudio` and receives
|
|
349
|
-
// the `start(onEvent)` callback, closing with `stop`.
|
|
350
|
-
//
|
|
351
|
-
//
|
|
352
|
-
//
|
|
374
|
+
// chunks via `sendAudio` and receives transcript and turn-boundary events
|
|
375
|
+
// through the `start(onEvent)` callback, closing with `stop`.
|
|
376
|
+
// `SttStreamServerEvent` types the events handed to `onEvent`; every member of
|
|
377
|
+
// that union is exported alongside it so a plugin can name each variant in an
|
|
378
|
+
// exhaustive switch. `SttErrorCategory` classifies `error` events;
|
|
379
|
+
// `SttProviderId` names the resolved session's provider.
|
|
353
380
|
export type {
|
|
354
381
|
StreamingTranscriber,
|
|
355
382
|
SttErrorCategory,
|
|
356
383
|
SttProviderId,
|
|
357
384
|
SttStreamServerClosedEvent,
|
|
385
|
+
SttStreamServerEagerTurnEndEvent,
|
|
358
386
|
SttStreamServerErrorEvent,
|
|
359
387
|
SttStreamServerEvent,
|
|
360
388
|
SttStreamServerFinalEvent,
|
|
361
389
|
SttStreamServerFinalizedEvent,
|
|
362
390
|
SttStreamServerPartialEvent,
|
|
391
|
+
SttStreamServerTurnEndEvent,
|
|
392
|
+
SttStreamServerTurnResumedEvent,
|
|
393
|
+
SttStreamServerTurnStartEvent,
|
|
363
394
|
} from "../stt/types.js";
|
|
364
395
|
export { openTranscriptionSession } from "./transcription-session.js";
|
|
365
396
|
// Conversation agent-loop turn — run a full conversation turn (persist user
|
|
@@ -370,6 +401,7 @@ export { openTranscriptionSession } from "./transcription-session.js";
|
|
|
370
401
|
// (e.g. meeting-bot flushing a transcript excerpt) should prefer this over the
|
|
371
402
|
// stateless `provider.sendMessage()` call.
|
|
372
403
|
export type {
|
|
404
|
+
ConversationChannelAddress,
|
|
373
405
|
RunConversationTurnOptions,
|
|
374
406
|
RunConversationTurnResult,
|
|
375
407
|
} from "./conversation-turn.js";
|
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
*
|
|
10
10
|
* ## Plugin scoping
|
|
11
11
|
*
|
|
12
|
-
* When a plugin is in context
|
|
13
|
-
*
|
|
12
|
+
* When a plugin is in context (its hook, tool, or one of its own
|
|
13
|
+
* `/x/plugins/<name>/` routes is executing, tracked by
|
|
14
|
+
* {@link ../plugins/plugin-execution-context.getCurrentPluginName}), resolution
|
|
14
15
|
* is restricted: the plugin may only resolve credentials whose `field` equals
|
|
15
16
|
* the plugin's own manifest name. A plugin named `acme` can therefore read
|
|
16
17
|
* `openai/acme` or `stripe/acme` but not `openai/api_key`. Outside any plugin
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin-facing credential storage.
|
|
3
|
+
*
|
|
4
|
+
* {@link storeCredential} writes a credential's plaintext to the secure store,
|
|
5
|
+
* the same write `assistant credentials set` performs, naming it either by an
|
|
6
|
+
* existing credential's UUID or by a `"service/field"` string, the same
|
|
7
|
+
* vocabulary {@link ./resolve-credential.resolveCredential} reads back. A
|
|
8
|
+
* plugin that obtains a token for itself (an OAuth exchange it drives, a key a
|
|
9
|
+
* user hands it through its own route) persists it here instead of inventing
|
|
10
|
+
* its own on-disk secret file.
|
|
11
|
+
*
|
|
12
|
+
* ## Plugin scoping
|
|
13
|
+
*
|
|
14
|
+
* A plugin may only write credentials whose `field` equals its manifest name,
|
|
15
|
+
* so `acme` writes `openai/acme` or `stripe/acme` but never `openai/api_key`.
|
|
16
|
+
* The name comes from the execution context
|
|
17
|
+
* ({@link ../plugins/plugin-execution-context.getCurrentPluginName}), which the
|
|
18
|
+
* host establishes around a plugin's hook, tool, and route invocations.
|
|
19
|
+
*
|
|
20
|
+
* Unlike the read path, this **fails closed**: with no plugin in context there
|
|
21
|
+
* is nobody to scope the write to, so it is refused rather than treated as an
|
|
22
|
+
* unscoped host write. A plugin's module body is evaluated by the loader
|
|
23
|
+
* outside any context, and an unscoped branch there would let top-level code
|
|
24
|
+
* overwrite the user's own credentials. Host-internal callers that legitimately
|
|
25
|
+
* write unscoped (the `credentials/set` route, the CLI behind it) compose
|
|
26
|
+
* {@link ../tools/credentials/store.storeCredentialValue} directly. This
|
|
27
|
+
* matches the plugin-owned index (`indexDocument`), which likewise requires a
|
|
28
|
+
* context because a write with no owner has nothing to attribute.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import { getCurrentPluginName } from "../plugins/plugin-execution-context.js";
|
|
32
|
+
import { parseServiceFieldRef } from "../tools/credentials/ref-parse.js";
|
|
33
|
+
import { resolveCredentialRef } from "../tools/credentials/resolve.js";
|
|
34
|
+
import { storeCredentialValue } from "../tools/credentials/store.js";
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Raised when a credential cannot be stored: the reference is malformed, the
|
|
38
|
+
* value is empty or invalid for its service, the secure backend rejected the
|
|
39
|
+
* write, or the calling plugin is not permitted to write the credential.
|
|
40
|
+
*/
|
|
41
|
+
export class CredentialStoreError extends Error {
|
|
42
|
+
constructor(message: string) {
|
|
43
|
+
super(message);
|
|
44
|
+
this.name = "CredentialStoreError";
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface StoreCredentialOptions {
|
|
49
|
+
/** Human-friendly alias shown in credential listings. */
|
|
50
|
+
label?: string;
|
|
51
|
+
/** What the credential is for, surfaced to the agent. */
|
|
52
|
+
description?: string;
|
|
53
|
+
/**
|
|
54
|
+
* Skip the retroactive sweep that removes the value from recent transcripts.
|
|
55
|
+
* The sweep exists because a secret is often pasted into chat before it is
|
|
56
|
+
* stored; pass `true` only for a value that provably never transited a
|
|
57
|
+
* conversation, such as a token minted by an OAuth refresh.
|
|
58
|
+
*/
|
|
59
|
+
skipTranscriptScrub?: boolean;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Identity of the credential that was written. */
|
|
63
|
+
export interface StoredCredentialRef {
|
|
64
|
+
credentialId: string;
|
|
65
|
+
service: string;
|
|
66
|
+
field: string;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Store a credential's plaintext value, creating it or replacing the value of
|
|
71
|
+
* an existing one.
|
|
72
|
+
*
|
|
73
|
+
* @param ref A credential UUID (an existing credential) or a `"service/field"`
|
|
74
|
+
* string (created when no such credential exists).
|
|
75
|
+
* @param value The plaintext value. Edge whitespace is trimmed.
|
|
76
|
+
* @returns The identity of the stored credential, which
|
|
77
|
+
* {@link ./resolve-credential.resolveCredential} accepts as a ref.
|
|
78
|
+
* @throws {CredentialStoreError} when no plugin is in context, the ref is
|
|
79
|
+
* malformed or names no credential, the value is empty or invalid for its
|
|
80
|
+
* service, the store rejects the write, or the calling plugin is not scoped
|
|
81
|
+
* to the credential.
|
|
82
|
+
*/
|
|
83
|
+
export async function storeCredential(
|
|
84
|
+
ref: string,
|
|
85
|
+
value: string,
|
|
86
|
+
options: StoreCredentialOptions = {},
|
|
87
|
+
): Promise<StoredCredentialRef> {
|
|
88
|
+
// Both gates run before anything is written, so a refused write never
|
|
89
|
+
// touches the secure backend. Fail closed when there is no plugin to scope
|
|
90
|
+
// to: a plugin's module body is evaluated outside any context, and an
|
|
91
|
+
// unscoped branch there would be a way to overwrite the user's credentials.
|
|
92
|
+
const pluginName = getCurrentPluginName();
|
|
93
|
+
if (pluginName === undefined) {
|
|
94
|
+
throw new CredentialStoreError(
|
|
95
|
+
"storeCredential requires an active plugin execution context (no calling plugin found). " +
|
|
96
|
+
"Call it from a hook, tool, or route handler rather than at module scope.",
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const target = resolveStoreTarget(ref);
|
|
101
|
+
if (target.field !== pluginName) {
|
|
102
|
+
throw new CredentialStoreError(
|
|
103
|
+
`Plugin "${pluginName}" may only store credentials whose field matches its name; ` +
|
|
104
|
+
`"${target.service}/${target.field}" is out of scope.`,
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
return await storeCredentialValue({
|
|
110
|
+
service: target.service,
|
|
111
|
+
field: target.field,
|
|
112
|
+
value,
|
|
113
|
+
alias: options.label,
|
|
114
|
+
usageDescription: options.description,
|
|
115
|
+
skipTranscriptScrub: options.skipTranscriptScrub,
|
|
116
|
+
});
|
|
117
|
+
} catch (err) {
|
|
118
|
+
throw new CredentialStoreError(
|
|
119
|
+
err instanceof Error ? err.message : String(err),
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Resolve the `ref` a write targets. An existing credential (by UUID or by
|
|
126
|
+
* `"service/field"`) keeps its identity; otherwise the ref must name the
|
|
127
|
+
* service and field of the credential to create, which a bare UUID cannot.
|
|
128
|
+
*/
|
|
129
|
+
function resolveStoreTarget(ref: string): { service: string; field: string } {
|
|
130
|
+
if (!ref || ref.trim().length === 0) {
|
|
131
|
+
throw new CredentialStoreError("Credential reference is required");
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const existing = resolveCredentialRef(ref);
|
|
135
|
+
if (existing) {
|
|
136
|
+
return { service: existing.service, field: existing.field };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const parsed = parseServiceFieldRef(ref);
|
|
140
|
+
if (!parsed) {
|
|
141
|
+
throw new CredentialStoreError(
|
|
142
|
+
`Cannot store credential: "${ref}" matches no stored credential and is not a "service/field" reference`,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
return parsed;
|
|
146
|
+
}
|
|
@@ -107,7 +107,7 @@ describe("doesSupportVision", () => {
|
|
|
107
107
|
managed: { provider: "vellum", model: "claude-fable-5" },
|
|
108
108
|
"managed-text": {
|
|
109
109
|
provider: "vellum",
|
|
110
|
-
model: "accounts/fireworks/models/deepseek-v4-flash",
|
|
110
|
+
model: "accounts/fireworks/models/deepseek-v4-flash-0731",
|
|
111
111
|
},
|
|
112
112
|
});
|
|
113
113
|
expect(doesSupportVision(profile("managed"))).toBe(true);
|
|
@@ -90,22 +90,24 @@ function registrationType(plugin: string, route: string): string {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
/**
|
|
93
|
-
*
|
|
93
|
+
* Keep a trailing slash on a resolved callback URL.
|
|
94
94
|
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* than at each plugin that registers a URL.
|
|
95
|
+
* Django in front of managed callbacks canonicalizes onto `/`. A vendor given
|
|
96
|
+
* the slashless spelling is 301'd, and clients that follow a 301 on POST
|
|
97
|
+
* typically retry as GET and drop the body. The gateway serves both spellings
|
|
98
|
+
* of a plugin webhook, so the slashed URL is the one to hand out.
|
|
100
99
|
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
100
|
+
* Query-bearing URLs are left alone: this resolver passes no query
|
|
101
|
+
* parameters, and appending there would cut into the query rather than the
|
|
102
|
+
* path. The callback path registered with the platform stays slashless. The
|
|
103
|
+
* platform strips slashes on store, and Django's path converter typically
|
|
104
|
+
* does not include the final `/` in the lookup key.
|
|
103
105
|
*/
|
|
104
|
-
function
|
|
106
|
+
function withTrailingSlash(url: string): string {
|
|
105
107
|
if (url.includes("?") || url.includes("#")) {
|
|
106
108
|
return url;
|
|
107
109
|
}
|
|
108
|
-
return url.
|
|
110
|
+
return url.endsWith("/") ? url : `${url}/`;
|
|
109
111
|
}
|
|
110
112
|
|
|
111
113
|
/**
|
|
@@ -147,5 +149,5 @@ export async function resolveWebhookUrl(
|
|
|
147
149
|
undefined,
|
|
148
150
|
sourceIdentifier,
|
|
149
151
|
);
|
|
150
|
-
return
|
|
152
|
+
return withTrailingSlash(resolved);
|
|
151
153
|
}
|