@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,6 +2,7 @@ import { describe, expect, test } from "bun:test";
|
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
LiveVoiceConfigSchema,
|
|
5
|
+
LiveVoiceFluxConfigSchema,
|
|
5
6
|
LiveVoiceFrontModelConfigSchema,
|
|
6
7
|
LiveVoiceVadConfigSchema,
|
|
7
8
|
VALID_LIVE_VOICE_MODES,
|
|
@@ -21,11 +22,18 @@ const FRONT_MODEL_DEFAULTS = {
|
|
|
21
22
|
endpointDecisionTimeoutMs: 1200,
|
|
22
23
|
endpointExtensionMs: 1500,
|
|
23
24
|
endpointMaxExtensions: 2,
|
|
24
|
-
ackFirstDeltaTimeoutMs: 2500,
|
|
25
|
-
ackGenerationTimeoutMs: 600,
|
|
26
25
|
progress: PROGRESS_DEFAULTS,
|
|
27
26
|
};
|
|
28
27
|
|
|
28
|
+
// `eagerEotThreshold` is deliberately absent: it has no default, and leaving it
|
|
29
|
+
// unset is what keeps Deepgram from emitting speculative turn events.
|
|
30
|
+
const FLUX_DEFAULTS = {
|
|
31
|
+
turnEnd: { enabled: false },
|
|
32
|
+
model: "flux-general-en",
|
|
33
|
+
eotThreshold: 0.7,
|
|
34
|
+
eotTimeoutMs: 5_000,
|
|
35
|
+
};
|
|
36
|
+
|
|
29
37
|
describe("LiveVoiceVadConfigSchema", () => {
|
|
30
38
|
test("empty object parses to defaults", () => {
|
|
31
39
|
const parsed = LiveVoiceVadConfigSchema.parse({});
|
|
@@ -124,8 +132,15 @@ describe("LiveVoiceFrontModelConfigSchema", () => {
|
|
|
124
132
|
expect(parsed.endpointMaxExtensions).toBe(0);
|
|
125
133
|
// Unspecified fields still get defaults
|
|
126
134
|
expect(parsed.endpointExtensionMs).toBe(1500);
|
|
127
|
-
|
|
128
|
-
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("strips retired generated-ack settings", () => {
|
|
138
|
+
const parsed = LiveVoiceFrontModelConfigSchema.parse({
|
|
139
|
+
ackFirstDeltaTimeoutMs: 2500,
|
|
140
|
+
ackGenerationTimeoutMs: 600,
|
|
141
|
+
});
|
|
142
|
+
expect(parsed).not.toHaveProperty("ackFirstDeltaTimeoutMs");
|
|
143
|
+
expect(parsed).not.toHaveProperty("ackGenerationTimeoutMs");
|
|
129
144
|
});
|
|
130
145
|
|
|
131
146
|
test("rejects non-positive endpointDecisionTimeoutMs", () => {
|
|
@@ -213,6 +228,87 @@ describe("LiveVoiceFrontModelConfigSchema", () => {
|
|
|
213
228
|
});
|
|
214
229
|
});
|
|
215
230
|
|
|
231
|
+
describe("LiveVoiceFluxConfigSchema", () => {
|
|
232
|
+
test("empty object parses to defaults, with turn-end off", () => {
|
|
233
|
+
expect(LiveVoiceFluxConfigSchema.parse({})).toEqual(FLUX_DEFAULTS);
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
test("an unset eagerEotThreshold is absent, not zero", () => {
|
|
237
|
+
expect("eagerEotThreshold" in LiveVoiceFluxConfigSchema.parse({})).toBe(
|
|
238
|
+
false,
|
|
239
|
+
);
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
test("accepts overrides", () => {
|
|
243
|
+
const parsed = LiveVoiceFluxConfigSchema.parse({
|
|
244
|
+
turnEnd: { enabled: true },
|
|
245
|
+
model: "flux-general-multi",
|
|
246
|
+
eotThreshold: 0.85,
|
|
247
|
+
eagerEotThreshold: 0.45,
|
|
248
|
+
eotTimeoutMs: 12_000,
|
|
249
|
+
});
|
|
250
|
+
expect(parsed.turnEnd.enabled).toBe(true);
|
|
251
|
+
expect(parsed.model).toBe("flux-general-multi");
|
|
252
|
+
expect(parsed.eotThreshold).toBe(0.85);
|
|
253
|
+
expect(parsed.eagerEotThreshold).toBe(0.45);
|
|
254
|
+
expect(parsed.eotTimeoutMs).toBe(12_000);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
test("partial overrides merge with defaults", () => {
|
|
258
|
+
const parsed = LiveVoiceFluxConfigSchema.parse({ eotThreshold: 0.6 });
|
|
259
|
+
expect(parsed.eotThreshold).toBe(0.6);
|
|
260
|
+
expect(parsed.turnEnd.enabled).toBe(false);
|
|
261
|
+
expect(parsed.model).toBe("flux-general-en");
|
|
262
|
+
expect(parsed.eotTimeoutMs).toBe(5_000);
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
test("rejects an eotThreshold outside 0.5..0.9", () => {
|
|
266
|
+
const above = LiveVoiceFluxConfigSchema.safeParse({ eotThreshold: 0.95 });
|
|
267
|
+
expect(above.success).toBe(false);
|
|
268
|
+
expect(above.error?.issues.map((i) => i.message)).toEqual([
|
|
269
|
+
"liveVoice.flux.eotThreshold must be <= 0.9",
|
|
270
|
+
]);
|
|
271
|
+
|
|
272
|
+
const below = LiveVoiceFluxConfigSchema.safeParse({ eotThreshold: 0.4 });
|
|
273
|
+
expect(below.success).toBe(false);
|
|
274
|
+
expect(below.error?.issues.map((i) => i.message)).toEqual([
|
|
275
|
+
"liveVoice.flux.eotThreshold must be >= 0.5",
|
|
276
|
+
]);
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
test("rejects an eagerEotThreshold outside 0.3..0.9", () => {
|
|
280
|
+
expect(
|
|
281
|
+
LiveVoiceFluxConfigSchema.safeParse({ eagerEotThreshold: 0.2 }).success,
|
|
282
|
+
).toBe(false);
|
|
283
|
+
expect(
|
|
284
|
+
LiveVoiceFluxConfigSchema.safeParse({ eagerEotThreshold: 0.95 }).success,
|
|
285
|
+
).toBe(false);
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
test("rejects an eotTimeoutMs outside 500..60000, and non-integers", () => {
|
|
289
|
+
expect(
|
|
290
|
+
LiveVoiceFluxConfigSchema.safeParse({ eotTimeoutMs: 499 }).success,
|
|
291
|
+
).toBe(false);
|
|
292
|
+
expect(
|
|
293
|
+
LiveVoiceFluxConfigSchema.safeParse({ eotTimeoutMs: 60_001 }).success,
|
|
294
|
+
).toBe(false);
|
|
295
|
+
expect(
|
|
296
|
+
LiveVoiceFluxConfigSchema.safeParse({ eotTimeoutMs: 1_500.5 }).success,
|
|
297
|
+
).toBe(false);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
test("rejects a non-boolean turnEnd.enabled", () => {
|
|
301
|
+
const result = LiveVoiceFluxConfigSchema.safeParse({
|
|
302
|
+
turnEnd: { enabled: "yes" },
|
|
303
|
+
});
|
|
304
|
+
expect(result.success).toBe(false);
|
|
305
|
+
const msgs = result.error?.issues.map((i) => i.message) ?? [];
|
|
306
|
+
expect(msgs.some((m) => m.includes("liveVoice.flux.turnEnd.enabled"))).toBe(
|
|
307
|
+
true,
|
|
308
|
+
);
|
|
309
|
+
});
|
|
310
|
+
});
|
|
311
|
+
|
|
216
312
|
describe("LiveVoiceConfigSchema", () => {
|
|
217
313
|
test("empty object parses to defaults", () => {
|
|
218
314
|
const parsed = LiveVoiceConfigSchema.parse({});
|
|
@@ -228,6 +324,9 @@ describe("LiveVoiceConfigSchema", () => {
|
|
|
228
324
|
echoDrainSlackMs: 300,
|
|
229
325
|
},
|
|
230
326
|
frontModel: FRONT_MODEL_DEFAULTS,
|
|
327
|
+
// Off by default: Flux turn detection is opt-in, so the front-door hold
|
|
328
|
+
// verdict keeps committing turns until it is enabled.
|
|
329
|
+
flux: FLUX_DEFAULTS,
|
|
231
330
|
maxSessionDurationSeconds: 1800,
|
|
232
331
|
// Off by default: voice turns carry only their transcript, no audio
|
|
233
332
|
// artifacts on the conversation messages (JARVIS-1283).
|
|
@@ -255,6 +354,7 @@ describe("LiveVoiceConfigSchema", () => {
|
|
|
255
354
|
mode: "ptt",
|
|
256
355
|
vad: { silenceThresholdMs: 900 },
|
|
257
356
|
frontModel: { endpointDecisionTimeoutMs: 300 },
|
|
357
|
+
flux: { turnEnd: { enabled: true } },
|
|
258
358
|
maxSessionDurationSeconds: 600,
|
|
259
359
|
});
|
|
260
360
|
expect(parsed.mode).toBe("ptt");
|
|
@@ -265,6 +365,9 @@ describe("LiveVoiceConfigSchema", () => {
|
|
|
265
365
|
// Partial frontModel overrides merge with defaults
|
|
266
366
|
expect(parsed.frontModel.endpointDecisionTimeoutMs).toBe(300);
|
|
267
367
|
expect(parsed.frontModel.endpointExtensionMs).toBe(1500);
|
|
368
|
+
// Partial flux overrides merge with defaults
|
|
369
|
+
expect(parsed.flux.turnEnd.enabled).toBe(true);
|
|
370
|
+
expect(parsed.flux.eotThreshold).toBe(0.7);
|
|
268
371
|
expect(parsed.maxSessionDurationSeconds).toBe(600);
|
|
269
372
|
});
|
|
270
373
|
|
|
@@ -289,11 +289,11 @@ const CATALOG_RECORD: CatalogRecord = {
|
|
|
289
289
|
"Captions images via a vision-capable profile for text-only model fallback.",
|
|
290
290
|
domain: "skills",
|
|
291
291
|
},
|
|
292
|
-
|
|
293
|
-
id: "
|
|
294
|
-
displayName: "Voice
|
|
292
|
+
voiceProgressNarration: {
|
|
293
|
+
id: "voiceProgressNarration",
|
|
294
|
+
displayName: "Voice Progress Narration",
|
|
295
295
|
description:
|
|
296
|
-
"
|
|
296
|
+
"Phrases short spoken progress updates during long-running live voice turns.",
|
|
297
297
|
domain: "agentLoop",
|
|
298
298
|
},
|
|
299
299
|
voiceFrontDoor: {
|
|
@@ -207,34 +207,64 @@ export const LiveVoiceFrontModelConfigSchema = z
|
|
|
207
207
|
)
|
|
208
208
|
.default(2)
|
|
209
209
|
.describe("Cap on consecutive 'hold' extensions per utterance"),
|
|
210
|
-
ackFirstDeltaTimeoutMs: z
|
|
211
|
-
.number({
|
|
212
|
-
error: "liveVoice.frontModel.ackFirstDeltaTimeoutMs must be a number",
|
|
213
|
-
})
|
|
214
|
-
.int("liveVoice.frontModel.ackFirstDeltaTimeoutMs must be an integer")
|
|
215
|
-
.positive(
|
|
216
|
-
"liveVoice.frontModel.ackFirstDeltaTimeoutMs must be a positive integer",
|
|
217
|
-
)
|
|
218
|
-
.default(2500)
|
|
219
|
-
.describe(
|
|
220
|
-
"Keyword-delay budget (ms): a spoken ack fires if no first assistant delta has arrived by then",
|
|
221
|
-
),
|
|
222
|
-
ackGenerationTimeoutMs: z
|
|
223
|
-
.number({
|
|
224
|
-
error: "liveVoice.frontModel.ackGenerationTimeoutMs must be a number",
|
|
225
|
-
})
|
|
226
|
-
.int("liveVoice.frontModel.ackGenerationTimeoutMs must be an integer")
|
|
227
|
-
.positive(
|
|
228
|
-
"liveVoice.frontModel.ackGenerationTimeoutMs must be a positive integer",
|
|
229
|
-
)
|
|
230
|
-
.default(600)
|
|
231
|
-
.describe("Budget (ms) for LLM-generated ack text"),
|
|
232
210
|
progress: LiveVoiceProgressConfigSchema.default(
|
|
233
211
|
LiveVoiceProgressConfigSchema.parse({}),
|
|
234
212
|
),
|
|
235
213
|
})
|
|
236
214
|
.describe(
|
|
237
|
-
"
|
|
215
|
+
"Voice front-door endpointing and long-turn progress narration tuning",
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
const LiveVoiceFluxTurnEndConfigSchema = z
|
|
219
|
+
.object({
|
|
220
|
+
enabled: z
|
|
221
|
+
.boolean({ error: "liveVoice.flux.turnEnd.enabled must be a boolean" })
|
|
222
|
+
.default(false)
|
|
223
|
+
.describe(
|
|
224
|
+
"Commit the live-voice turn on Flux's EndOfTurn instead of the front-door [0] hold verdict. Requires services.stt.provider to be deepgram-flux; ignored otherwise.",
|
|
225
|
+
),
|
|
226
|
+
})
|
|
227
|
+
.describe(
|
|
228
|
+
"Which signal commits a live voice turn when Deepgram Flux is the STT provider",
|
|
229
|
+
);
|
|
230
|
+
|
|
231
|
+
export const LiveVoiceFluxConfigSchema = z
|
|
232
|
+
.object({
|
|
233
|
+
turnEnd: LiveVoiceFluxTurnEndConfigSchema.default(
|
|
234
|
+
LiveVoiceFluxTurnEndConfigSchema.parse({}),
|
|
235
|
+
),
|
|
236
|
+
model: z
|
|
237
|
+
.string({ error: "liveVoice.flux.model must be a string" })
|
|
238
|
+
.default("flux-general-en")
|
|
239
|
+
.describe("Deepgram Flux model requested when opening the STT stream"),
|
|
240
|
+
eotThreshold: z
|
|
241
|
+
.number({ error: "liveVoice.flux.eotThreshold must be a number" })
|
|
242
|
+
.min(0.5, "liveVoice.flux.eotThreshold must be >= 0.5")
|
|
243
|
+
.max(0.9, "liveVoice.flux.eotThreshold must be <= 0.9")
|
|
244
|
+
.default(0.7)
|
|
245
|
+
.describe(
|
|
246
|
+
"End-of-turn confidence Flux must reach before it emits EndOfTurn. Lower values commit sooner and cut speakers off more often; higher values wait longer and add end-of-turn latency.",
|
|
247
|
+
),
|
|
248
|
+
eagerEotThreshold: z
|
|
249
|
+
.number({ error: "liveVoice.flux.eagerEotThreshold must be a number" })
|
|
250
|
+
.min(0.3, "liveVoice.flux.eagerEotThreshold must be >= 0.3")
|
|
251
|
+
.max(0.9, "liveVoice.flux.eagerEotThreshold must be <= 0.9")
|
|
252
|
+
.optional()
|
|
253
|
+
.describe(
|
|
254
|
+
"Confidence at which Flux starts speculating that the turn has ended. Leaving it unset disables Deepgram's EagerEndOfTurn / TurnResumed events; enabling it raises LLM calls 50-70% because speculative turns that resume are thrown away.",
|
|
255
|
+
),
|
|
256
|
+
eotTimeoutMs: z
|
|
257
|
+
.number({ error: "liveVoice.flux.eotTimeoutMs must be a number" })
|
|
258
|
+
.int("liveVoice.flux.eotTimeoutMs must be an integer")
|
|
259
|
+
.min(500, "liveVoice.flux.eotTimeoutMs must be >= 500")
|
|
260
|
+
.max(60_000, "liveVoice.flux.eotTimeoutMs must be <= 60000")
|
|
261
|
+
.default(5_000)
|
|
262
|
+
.describe(
|
|
263
|
+
"Silence (ms) after which Flux force-ends the turn even though its end-of-turn confidence never reached eotThreshold",
|
|
264
|
+
),
|
|
265
|
+
})
|
|
266
|
+
.describe(
|
|
267
|
+
"Deepgram Flux turn-detection tuning for live voice sessions (model-integrated end-of-turn)",
|
|
238
268
|
);
|
|
239
269
|
|
|
240
270
|
export const LiveVoiceConfigSchema = z
|
|
@@ -251,6 +281,9 @@ export const LiveVoiceConfigSchema = z
|
|
|
251
281
|
frontModel: LiveVoiceFrontModelConfigSchema.default(
|
|
252
282
|
LiveVoiceFrontModelConfigSchema.parse({}),
|
|
253
283
|
),
|
|
284
|
+
flux: LiveVoiceFluxConfigSchema.default(
|
|
285
|
+
LiveVoiceFluxConfigSchema.parse({}),
|
|
286
|
+
),
|
|
254
287
|
maxSessionDurationSeconds: z
|
|
255
288
|
.number({
|
|
256
289
|
error: "liveVoice.maxSessionDurationSeconds must be a number",
|
|
@@ -280,3 +313,4 @@ export type LiveVoiceFrontModelConfig = z.infer<
|
|
|
280
313
|
export type LiveVoiceProgressConfig = z.infer<
|
|
281
314
|
typeof LiveVoiceProgressConfigSchema
|
|
282
315
|
>;
|
|
316
|
+
export type LiveVoiceFluxConfig = z.infer<typeof LiveVoiceFluxConfigSchema>;
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
PROVIDERS_REQUIRING_BASE_URL_AND_MODELS,
|
|
5
|
+
ROUTING_IDENTITY_PROVIDERS,
|
|
6
|
+
} from "../../providers/inference/auth.js";
|
|
4
7
|
import { PROVIDER_CATALOG } from "../../providers/model-catalog.js";
|
|
5
8
|
import { isCodexSubscriptionModel } from "../../providers/openai/codex-models.js";
|
|
6
9
|
import {
|
|
@@ -25,32 +28,54 @@ import {
|
|
|
25
28
|
// Provider enum
|
|
26
29
|
// ---------------------------------------------------------------------------
|
|
27
30
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
31
|
+
/**
|
|
32
|
+
* The provider values the write surfaces accept today: adapter-backed
|
|
33
|
+
* catalog providers plus the two routing identities. The schema itself is
|
|
34
|
+
* an open string so a stored provider outside this list parses instead of
|
|
35
|
+
* stripping its profile; dispatch resolves such a label as a connection
|
|
36
|
+
* entry name and fails with an explainable resolution error when no row
|
|
37
|
+
* carries it. Write-time membership is enforced at the profiles route and
|
|
38
|
+
* the `commitConfigWrite` choke point (`unknownLlmProviderIssue`), which
|
|
39
|
+
* is where entry names unlock when the entries model enables them.
|
|
40
|
+
*/
|
|
41
|
+
export const KNOWN_LLM_PROVIDERS = [
|
|
42
|
+
"anthropic",
|
|
43
|
+
"openai",
|
|
44
|
+
"gemini",
|
|
45
|
+
"ollama",
|
|
46
|
+
"fireworks",
|
|
47
|
+
"openrouter",
|
|
48
|
+
"vercel-ai-gateway",
|
|
49
|
+
"openai-compatible",
|
|
50
|
+
"minimax",
|
|
51
|
+
"atlascloud",
|
|
52
|
+
"together",
|
|
53
|
+
"litellm",
|
|
54
|
+
"baseten",
|
|
55
|
+
"poolside",
|
|
56
|
+
// Routing identities rather than adapters: "vellum" = the platform-managed
|
|
57
|
+
// route (upstream derived from the model at dispatch), "chatgpt" = the
|
|
58
|
+
// subscription route to OpenAI. Neither has a PROVIDER_CATALOG entry;
|
|
59
|
+
// dispatch substitutes the real upstream before any adapter lookup.
|
|
60
|
+
"vellum",
|
|
61
|
+
"chatgpt",
|
|
62
|
+
] as const;
|
|
63
|
+
|
|
64
|
+
export const LLMProvider = z.string().min(1).meta({ id: "LLMProvider" });
|
|
52
65
|
export type LLMProvider = z.infer<typeof LLMProvider>;
|
|
53
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Write-surface membership check for a provider value. Returns a message
|
|
69
|
+
* when the value is outside {@link KNOWN_LLM_PROVIDERS}, null when it is
|
|
70
|
+
* allowed. Pure and sync so the profiles route and the config-write choke
|
|
71
|
+
* point share one rule.
|
|
72
|
+
*/
|
|
73
|
+
export function unknownLlmProviderIssue(provider: string): string | null {
|
|
74
|
+
return (KNOWN_LLM_PROVIDERS as readonly string[]).includes(provider)
|
|
75
|
+
? null
|
|
76
|
+
: `Invalid provider "${provider}". Valid providers: ${KNOWN_LLM_PROVIDERS.join(", ")}.`;
|
|
77
|
+
}
|
|
78
|
+
|
|
54
79
|
/**
|
|
55
80
|
* Providers that can back `llm.defaultProvider`: the named columns of the
|
|
56
81
|
* default-profile matrix plus every API-key catalog provider whose personal
|
|
@@ -70,10 +95,10 @@ export const DEFAULT_PROVIDER_CHOICES: readonly LLMProvider[] = [
|
|
|
70
95
|
entry.defaultModel !== "",
|
|
71
96
|
)
|
|
72
97
|
.map((entry) => entry.id)
|
|
73
|
-
// A catalog provider
|
|
74
|
-
// any profile, so it cannot back the defaults either.
|
|
98
|
+
// A catalog provider outside the known provider set cannot be
|
|
99
|
+
// referenced by any profile, so it cannot back the defaults either.
|
|
75
100
|
.filter((id): id is LLMProvider =>
|
|
76
|
-
(
|
|
101
|
+
(KNOWN_LLM_PROVIDERS as readonly string[]).includes(id),
|
|
77
102
|
),
|
|
78
103
|
]),
|
|
79
104
|
];
|
|
@@ -84,13 +109,15 @@ export function isDefaultProviderChoice(value: string): value is LLMProvider {
|
|
|
84
109
|
|
|
85
110
|
/**
|
|
86
111
|
* Default-provider choices whose profiles materialize from the shared BYOK
|
|
87
|
-
* templates: every choice except the
|
|
88
|
-
* defaults are pinned
|
|
112
|
+
* templates: every choice except the routing identities (`vellum`,
|
|
113
|
+
* `chatgpt`), whose defaults are pinned models on code-owned columns.
|
|
89
114
|
*/
|
|
90
115
|
export function isByokDefaultProviderChoice(
|
|
91
116
|
value: string,
|
|
92
117
|
): value is LLMProvider {
|
|
93
|
-
return
|
|
118
|
+
return (
|
|
119
|
+
!ROUTING_IDENTITY_PROVIDERS.has(value) && isDefaultProviderChoice(value)
|
|
120
|
+
);
|
|
94
121
|
}
|
|
95
122
|
|
|
96
123
|
const DefaultProviderEnum = z.enum(
|
|
@@ -188,7 +215,7 @@ export const LLMCallSiteEnum = z.enum([
|
|
|
188
215
|
"skillCategoryInference",
|
|
189
216
|
"inference",
|
|
190
217
|
"vision",
|
|
191
|
-
"
|
|
218
|
+
"voiceProgressNarration",
|
|
192
219
|
"voiceFrontDoor",
|
|
193
220
|
"trustRuleSuggestion",
|
|
194
221
|
"homeGreeting",
|
|
@@ -109,3 +109,26 @@ export const McpConfigSchema = z
|
|
|
109
109
|
export type McpTransport = z.infer<typeof McpTransportSchema>;
|
|
110
110
|
export type McpServerConfig = z.infer<typeof McpServerConfigSchema>;
|
|
111
111
|
export type McpConfig = z.infer<typeof McpConfigSchema>;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Who declared a server: the workspace `config.json`, which the user owns,
|
|
115
|
+
* or a plugin's `mcp.json`, which its author owns.
|
|
116
|
+
*
|
|
117
|
+
* Deliberately not a schema field. It is resolved from where the entry was
|
|
118
|
+
* read, never parsed from a file, so nothing on disk can claim to be
|
|
119
|
+
* workspace-owned. What it gates is credential access: only a workspace
|
|
120
|
+
* server resolves `mcp:<serverId>:*` from the credential store, because a
|
|
121
|
+
* plugin controls both its server key and its URL and would otherwise
|
|
122
|
+
* receive a workspace credential at an endpoint it chose.
|
|
123
|
+
*/
|
|
124
|
+
export type McpServerSource = "workspace" | "plugin";
|
|
125
|
+
|
|
126
|
+
/** A server config with its origin resolved. */
|
|
127
|
+
export interface ResolvedMcpServerConfig extends McpServerConfig {
|
|
128
|
+
readonly source: McpServerSource;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** The MCP config the daemon runs: both origins, every server attributed. */
|
|
132
|
+
export interface ResolvedMcpConfig extends Omit<McpConfig, "servers"> {
|
|
133
|
+
readonly servers: Record<string, ResolvedMcpServerConfig>;
|
|
134
|
+
}
|
|
@@ -5,11 +5,15 @@ import { z } from "zod";
|
|
|
5
5
|
*
|
|
6
6
|
* The resource monitor process runs a periodic sweep (see
|
|
7
7
|
* `monitoring/plugin-auto-update.ts`) that moves every installed, enabled
|
|
8
|
-
* plugin to
|
|
8
|
+
* marketplace plugin to the catalog's current pin. The sweep is off by default:
|
|
9
9
|
* `mode: "manual"` means nothing upgrades unless a human runs
|
|
10
10
|
* `assistant plugins upgrade <name>` (or the equivalent route), which is the
|
|
11
11
|
* behavior every workspace had before this block existed. Setting
|
|
12
12
|
* `mode: "auto"` opts the workspace into the hourly sweep.
|
|
13
|
+
*
|
|
14
|
+
* Opting in never covers plugins installed straight from a GitHub URL. Those
|
|
15
|
+
* track a mutable ref rather than a reviewed pin, so the sweep leaves them to
|
|
16
|
+
* an explicit `assistant plugins upgrade`.
|
|
13
17
|
*/
|
|
14
18
|
export const PluginUpdatesConfigSchema = z
|
|
15
19
|
.object({
|
|
@@ -19,7 +23,7 @@ export const PluginUpdatesConfigSchema = z
|
|
|
19
23
|
})
|
|
20
24
|
.default("manual")
|
|
21
25
|
.describe(
|
|
22
|
-
'How installed plugins move to newer revisions. "manual" (default) never upgrades on its own
|
|
26
|
+
'How installed plugins move to newer revisions. "manual" (default) never upgrades on its own: upgrades happen only when a human asks for one. "auto" lets the resource monitor upgrade every installed, enabled marketplace plugin on the `checkIntervalMs` cadence. Plugins installed directly from a GitHub URL are never upgraded automatically, since they track a mutable ref instead of a reviewed pin.',
|
|
23
27
|
),
|
|
24
28
|
strategy: z
|
|
25
29
|
// Deliberately narrower than the full `PluginUpgradeStrategy` union: the
|
package/src/config/skills.ts
CHANGED
|
@@ -104,11 +104,18 @@ export interface SkillSummary {
|
|
|
104
104
|
owner?: OwnerInfo;
|
|
105
105
|
/** Parsed tool manifest metadata, if the skill has a valid TOOLS.json. */
|
|
106
106
|
toolManifest?: SkillToolManifestMeta;
|
|
107
|
-
/**
|
|
107
|
+
/**
|
|
108
|
+
* IDs of child skills this skill composes. `skill_load` inlines each child's
|
|
109
|
+
* body and projects its tools when the parent loads (tools/skills/load.ts);
|
|
110
|
+
* they play no part in which skills get selected for a turn.
|
|
111
|
+
*/
|
|
108
112
|
includes?: string[];
|
|
109
113
|
/** Feature flag ID declared in frontmatter. Only skills with this field are subject to feature flag gating. */
|
|
110
114
|
featureFlag?: string;
|
|
111
|
-
/**
|
|
115
|
+
/**
|
|
116
|
+
* Intent phrases rendered into the skill's capability card as "Use when: ..."
|
|
117
|
+
* (memory substrate/skill-content.ts), which is the text skill routing scores.
|
|
118
|
+
*/
|
|
112
119
|
activationHints?: string[];
|
|
113
120
|
/** Conditions under which this skill should NOT be loaded. */
|
|
114
121
|
avoidWhen?: string[];
|
|
@@ -102,6 +102,96 @@ export function compactAxTreeHistory(messages: Message[]): Message[] {
|
|
|
102
102
|
});
|
|
103
103
|
}
|
|
104
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Full-wrapper matcher for the `<channel_capabilities>` block. Both ends are
|
|
107
|
+
* required so a user message that merely opens with the tag (someone pasting
|
|
108
|
+
* prompt markup into chat) is never mistaken for an injected block, the same
|
|
109
|
+
* discipline `stripUserTextBlocksByPrefix` uses in `strip-injections.ts`.
|
|
110
|
+
*/
|
|
111
|
+
const CHANNEL_CAPABILITIES_OPEN = "<channel_capabilities>\n";
|
|
112
|
+
const CHANNEL_CAPABILITIES_CLOSE = "\n</channel_capabilities>";
|
|
113
|
+
|
|
114
|
+
/** Whether a text block is a complete `<channel_capabilities>…` block. */
|
|
115
|
+
function isChannelCapabilitiesBlock(text: string): boolean {
|
|
116
|
+
return (
|
|
117
|
+
text.startsWith(CHANNEL_CAPABILITIES_OPEN) &&
|
|
118
|
+
text.endsWith(CHANNEL_CAPABILITIES_CLOSE)
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Drop `<channel_capabilities>` blocks that repeat the last one still standing,
|
|
124
|
+
* so a conversation carries one copy per distinct set of capabilities instead
|
|
125
|
+
* of one per turn.
|
|
126
|
+
*
|
|
127
|
+
* Runtime injection prepends this block to the turn-starting user message and
|
|
128
|
+
* then freezes it into history: live, because injection splices into the
|
|
129
|
+
* conversation's own message array, and across restarts, because `loadFromDb`
|
|
130
|
+
* rehydrates it from message metadata. Nothing ever removes it, so an N-turn
|
|
131
|
+
* channel conversation ships N copies. Unlike `<turn_context>`, which at least
|
|
132
|
+
* carries a fresh timestamp, this block is a pure function of the channel
|
|
133
|
+
* (`buildChannelCapabilityBlock`): every copy after the first is byte-identical
|
|
134
|
+
* to the one above it and tells the model nothing it has not already read.
|
|
135
|
+
*
|
|
136
|
+
* ## Why "same as the last retained copy" and not "not the current turn"
|
|
137
|
+
*
|
|
138
|
+
* Prompt caching only pays off when the bytes a provider marked in turn N are
|
|
139
|
+
* still at the same position in turn N+1, an invariant this repo enforces end
|
|
140
|
+
* to end in `prompt-cache-cross-turn-stability.test.ts`. Any rule of the form
|
|
141
|
+
* "keep it on the current turn, collapse it above" necessarily re-renders one
|
|
142
|
+
* message every turn (the turn that just ended), which is a full cache miss on
|
|
143
|
+
* the whole message prefix, every turn, for every conversation.
|
|
144
|
+
*
|
|
145
|
+
* Comparing against the last RETAINED copy avoids that entirely: whether a
|
|
146
|
+
* given occurrence survives depends only on the messages above it, never on
|
|
147
|
+
* how many turns come later. A message's rendering is therefore fixed the
|
|
148
|
+
* moment it is written, and the prefix stays byte-stable as the conversation
|
|
149
|
+
* grows. Capabilities that genuinely change mid-conversation (the same
|
|
150
|
+
* conversation resumed from a different client) still differ from the retained
|
|
151
|
+
* copy, so the new block is kept and the model sees the change.
|
|
152
|
+
*
|
|
153
|
+
* The cost is placement: the surviving copy sits at the top of the conversation
|
|
154
|
+
* rather than next to the newest message. It is the same bytes either way.
|
|
155
|
+
*
|
|
156
|
+
* Idempotent, like every other transform in this module: re-running it over an
|
|
157
|
+
* already-deduplicated history retains exactly the same occurrences.
|
|
158
|
+
*/
|
|
159
|
+
export function dedupeChannelCapabilityBlocks(history: Message[]): Message[] {
|
|
160
|
+
let lastRetained: string | null = null;
|
|
161
|
+
let changed = false;
|
|
162
|
+
|
|
163
|
+
const next = history.map((message) => {
|
|
164
|
+
if (message.role !== "user") {
|
|
165
|
+
return message;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
let messageChanged = false;
|
|
169
|
+
const content = message.content.filter((block) => {
|
|
170
|
+
if (block.type !== "text" || !isChannelCapabilitiesBlock(block.text)) {
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
173
|
+
if (block.text === lastRetained) {
|
|
174
|
+
messageChanged = true;
|
|
175
|
+
return false;
|
|
176
|
+
}
|
|
177
|
+
lastRetained = block.text;
|
|
178
|
+
return true;
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// A user row is never only injections in practice, but guard anyway: an
|
|
182
|
+
// empty content array is not a message any provider will accept, and
|
|
183
|
+
// dropping the row would break tool_use/tool_result pairing and the
|
|
184
|
+
// row-to-history index mapping `summarizeUpToMessage` relies on.
|
|
185
|
+
if (!messageChanged || content.length === 0) {
|
|
186
|
+
return message;
|
|
187
|
+
}
|
|
188
|
+
changed = true;
|
|
189
|
+
return { ...message, content };
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
return changed ? next : history;
|
|
193
|
+
}
|
|
194
|
+
|
|
105
195
|
/**
|
|
106
196
|
* Index of the last user message carrying `tool_result` blocks — the
|
|
107
197
|
* "current turn" boundary {@link stripOldMediaBlocks} keeps intact while
|
|
@@ -194,6 +284,9 @@ function stripOldMediaBlocks(history: Message[]): Message[] {
|
|
|
194
284
|
* `web_search_tool_result` blocks to text summaries; Anthropic's opaque
|
|
195
285
|
* `encrypted_content` tokens expire / are route-scoped, and replaying a stale
|
|
196
286
|
* one is rejected with `Invalid encrypted_content in search_result block`.
|
|
287
|
+
* - {@link dedupeChannelCapabilityBlocks} keeps one `<channel_capabilities>`
|
|
288
|
+
* block per distinct set of capabilities instead of the one-per-turn history
|
|
289
|
+
* accumulates; every repeat is byte-identical to the copy above it.
|
|
197
290
|
*
|
|
198
291
|
* Transforms the outbound copy only — the durable history keeps the rich
|
|
199
292
|
* originals and each send re-derives the sanitized projection (every transform
|
|
@@ -206,5 +299,7 @@ function stripOldMediaBlocks(history: Message[]): Message[] {
|
|
|
206
299
|
export function preModelCallSanitize(history: Message[]): Message[] {
|
|
207
300
|
const mediaStripped = stripOldMediaBlocks(history);
|
|
208
301
|
const axCompacted = compactAxTreeHistory(mediaStripped);
|
|
209
|
-
|
|
302
|
+
const webSearchStripped =
|
|
303
|
+
stripHistoricalWebSearchResults(axCompacted).messages;
|
|
304
|
+
return dedupeChannelCapabilityBlocks(webSearchStripped);
|
|
210
305
|
}
|
|
@@ -158,7 +158,7 @@ function makeContext(overrides?: Partial<Conversation>): HarnessContext {
|
|
|
158
158
|
|
|
159
159
|
const base = asConversation({
|
|
160
160
|
conversationId: "origin-conv-id",
|
|
161
|
-
|
|
161
|
+
emit: (msg) => sent.push(msg),
|
|
162
162
|
pendingSurfaceActions: new Map<string, { surfaceType: SurfaceType }>(),
|
|
163
163
|
lastSurfaceAction: new Map<
|
|
164
164
|
string,
|