@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
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the `voice_picker` ui_show surface's "never blocks a turn"
|
|
3
|
+
* invariant.
|
|
4
|
+
*
|
|
5
|
+
* The card has no terminal action: it settles when the user picks a voice,
|
|
6
|
+
* with no button to click. `actions` and `await_action` are generic ui_show
|
|
7
|
+
* params though, so the model can attach them to any surface, and the web
|
|
8
|
+
* client latches `awaiting_user_input` on the presence of actions alone with
|
|
9
|
+
* nothing to clear it. The daemon resolver therefore strips both, and a
|
|
10
|
+
* pending picker never holds the one-interactive-surface lock.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { describe, expect, test } from "bun:test";
|
|
14
|
+
|
|
15
|
+
import { UISurfaceShowEventSchema } from "../api/events/ui-surface-show.js";
|
|
16
|
+
import type { AssistantEvent } from "../api/index.js";
|
|
17
|
+
import type { Conversation } from "../daemon/conversation.js";
|
|
18
|
+
import {
|
|
19
|
+
createSurfaceMutex,
|
|
20
|
+
surfaceProxyResolver,
|
|
21
|
+
} from "../daemon/conversation-surfaces.js";
|
|
22
|
+
import type { SurfaceType } from "../daemon/message-protocol.js";
|
|
23
|
+
import { INTERACTIVE_SURFACE_TYPES } from "../daemon/message-protocol.js";
|
|
24
|
+
import { asConversation } from "./helpers/mock-conversation.js";
|
|
25
|
+
|
|
26
|
+
function makeContext(sent: AssistantEvent[] = []): Conversation {
|
|
27
|
+
return asConversation({
|
|
28
|
+
conversationId: "session-1",
|
|
29
|
+
emit: (msg) => sent.push(msg),
|
|
30
|
+
pendingSurfaceActions: new Map<string, { surfaceType: SurfaceType }>(),
|
|
31
|
+
lastSurfaceAction: new Map<
|
|
32
|
+
string,
|
|
33
|
+
{ actionId: string; data?: Record<string, unknown> }
|
|
34
|
+
>(),
|
|
35
|
+
surfaceState: new Map(),
|
|
36
|
+
surfaceUndoStacks: new Map<string, string[]>(),
|
|
37
|
+
accumulatedSurfaceState: new Map<string, Record<string, unknown>>(),
|
|
38
|
+
surfaceActionRequestIds: new Set<string>(),
|
|
39
|
+
currentTurnSurfaces: [],
|
|
40
|
+
isProcessing: () => false,
|
|
41
|
+
enqueueMessage: () => ({ queued: false, requestId: "req-1" }),
|
|
42
|
+
getQueueDepth: () => 0,
|
|
43
|
+
processMessage: async () => "ok",
|
|
44
|
+
withSurface: createSurfaceMutex(),
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function showEvent(sent: AssistantEvent[]) {
|
|
49
|
+
return UISurfaceShowEventSchema.parse(
|
|
50
|
+
sent.find((msg) => msg.type === "ui_surface_show"),
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
describe("voice_picker never blocks a turn", () => {
|
|
55
|
+
test("shows with an empty payload and awaits nothing", async () => {
|
|
56
|
+
const sent: AssistantEvent[] = [];
|
|
57
|
+
const ctx = makeContext(sent);
|
|
58
|
+
|
|
59
|
+
const result = await surfaceProxyResolver(ctx, "ui_show", {
|
|
60
|
+
surface_type: "voice_picker",
|
|
61
|
+
data: {},
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
expect(result.isError).toBe(false);
|
|
65
|
+
expect(result.yieldToUser).toBeUndefined();
|
|
66
|
+
|
|
67
|
+
const parsed = showEvent(sent);
|
|
68
|
+
expect(parsed.surfaceType).toBe("voice_picker");
|
|
69
|
+
expect(parsed.actions).toBeUndefined();
|
|
70
|
+
expect(ctx.pendingSurfaceActions.has(parsed.surfaceId)).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("strips actions the model attached", async () => {
|
|
74
|
+
const sent: AssistantEvent[] = [];
|
|
75
|
+
const ctx = makeContext(sent);
|
|
76
|
+
|
|
77
|
+
const result = await surfaceProxyResolver(ctx, "ui_show", {
|
|
78
|
+
surface_type: "voice_picker",
|
|
79
|
+
data: {},
|
|
80
|
+
actions: [{ id: "done", label: "Done", style: "primary" }],
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
expect(result.isError).toBe(false);
|
|
84
|
+
expect(result.yieldToUser).toBeUndefined();
|
|
85
|
+
|
|
86
|
+
const parsed = showEvent(sent);
|
|
87
|
+
expect(parsed.actions).toBeUndefined();
|
|
88
|
+
// The persisted copy must match the emitted one, or a history reseed would
|
|
89
|
+
// resurrect the button the client blocks on.
|
|
90
|
+
expect(ctx.currentTurnSurfaces[0]?.actions).toBeUndefined();
|
|
91
|
+
expect(ctx.pendingSurfaceActions.has(parsed.surfaceId)).toBe(false);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
test("ignores an explicit await_action", async () => {
|
|
95
|
+
const sent: AssistantEvent[] = [];
|
|
96
|
+
const ctx = makeContext(sent);
|
|
97
|
+
|
|
98
|
+
const result = await surfaceProxyResolver(ctx, "ui_show", {
|
|
99
|
+
surface_type: "voice_picker",
|
|
100
|
+
data: {},
|
|
101
|
+
await_action: true,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
expect(result.isError).toBe(false);
|
|
105
|
+
expect(result.yieldToUser).toBeUndefined();
|
|
106
|
+
expect(result.content).not.toContain("awaiting_user_action");
|
|
107
|
+
expect(ctx.pendingSurfaceActions.size).toBe(0);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
test("a pending picker does not hold the one-interactive-surface lock", async () => {
|
|
111
|
+
const ctx = makeContext();
|
|
112
|
+
ctx.pendingSurfaceActions.set("surface-picker", {
|
|
113
|
+
surfaceType: "voice_picker",
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
const result = await surfaceProxyResolver(ctx, "ui_show", {
|
|
117
|
+
surface_type: "choice",
|
|
118
|
+
data: { options: [{ id: "a", title: "Option A" }] },
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
expect(result.isError).toBe(false);
|
|
122
|
+
expect(result.content).not.toContain("already awaiting user input");
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
test("voice_picker is not an interactive surface type", () => {
|
|
126
|
+
expect(INTERACTIVE_SURFACE_TYPES).not.toContain("voice_picker");
|
|
127
|
+
});
|
|
128
|
+
});
|
|
@@ -19,7 +19,7 @@ import { asConversation } from "./helpers/mock-conversation.js";
|
|
|
19
19
|
function makeContext(sent: AssistantEvent[] = []): Conversation {
|
|
20
20
|
return asConversation({
|
|
21
21
|
conversationId: "session-1",
|
|
22
|
-
|
|
22
|
+
emit: (msg) => sent.push(msg),
|
|
23
23
|
pendingSurfaceActions: new Map<string, { surfaceType: SurfaceType }>(),
|
|
24
24
|
lastSurfaceAction: new Map<
|
|
25
25
|
string,
|
|
@@ -23,11 +23,12 @@ import { tmpdir } from "node:os";
|
|
|
23
23
|
import { join } from "node:path";
|
|
24
24
|
import { beforeEach, describe, expect, test } from "bun:test";
|
|
25
25
|
|
|
26
|
+
import { getUserHooksFor } from "../hooks/registry.js";
|
|
26
27
|
import {
|
|
27
28
|
getCachedUserTools,
|
|
28
|
-
getUserHooksFor,
|
|
29
29
|
resetPluginCacheForTests,
|
|
30
30
|
} from "../plugins/mtime-cache.js";
|
|
31
|
+
import { resetPluginRegistryForTests } from "../plugins/registry.js";
|
|
31
32
|
import { loadUserPlugins } from "../plugins/user-loader.js";
|
|
32
33
|
|
|
33
34
|
// Isolate every run under its own tempdir so parallel test files (and
|
|
@@ -71,6 +72,7 @@ function clearPluginsDir(): void {
|
|
|
71
72
|
describe("user plugin loader", () => {
|
|
72
73
|
beforeEach(() => {
|
|
73
74
|
resetPluginCacheForTests();
|
|
75
|
+
resetPluginRegistryForTests();
|
|
74
76
|
clearPluginsDir();
|
|
75
77
|
});
|
|
76
78
|
|
|
@@ -18,9 +18,7 @@ mock.module("../permissions/checker.js", () => ({
|
|
|
18
18
|
isDynamicSkillLoadInvocation: () => false,
|
|
19
19
|
classifyRisk: async () => ({ level: "low" }),
|
|
20
20
|
check: async () => ({ decision: "allow", reason: "allowed" }),
|
|
21
|
-
generateAllowlistOptions: () => [],
|
|
22
21
|
generateScopeOptions: () => [],
|
|
23
|
-
getCachedAssessment: () => undefined,
|
|
24
22
|
}));
|
|
25
23
|
|
|
26
24
|
mock.module("../persistence/conversation-crud.js", () => ({
|
|
@@ -25,6 +25,7 @@ import {
|
|
|
25
25
|
import { registerDefaultPluginInjectors } from "../plugins/defaults/index.js";
|
|
26
26
|
import type { Message } from "../providers/types.js";
|
|
27
27
|
import { getWorkspacePluginsDir } from "../util/platform.js";
|
|
28
|
+
import { asConversation } from "./helpers/mock-conversation.js";
|
|
28
29
|
|
|
29
30
|
// The injector chain is registered by the daemon bootstrap in production; do
|
|
30
31
|
// the same here so `applyRuntimeInjections` walks a non-empty chain.
|
|
@@ -151,15 +152,21 @@ describe("visible_app injection", () => {
|
|
|
151
152
|
];
|
|
152
153
|
|
|
153
154
|
function seedConversation(currentTurnVisibleAppId: string | undefined): void {
|
|
154
|
-
setConversation(
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
155
|
+
setConversation(
|
|
156
|
+
CONVERSATION_ID,
|
|
157
|
+
asConversation({
|
|
158
|
+
conversationId: CONVERSATION_ID,
|
|
159
|
+
workingDir: "/sandbox",
|
|
160
|
+
workspaceTopLevelContext: "",
|
|
161
|
+
workspaceTopLevelDirty: false,
|
|
162
|
+
surfaceState: new Map(),
|
|
163
|
+
currentTurnVisibleAppId,
|
|
164
|
+
currentTurnTemporalSnapshot: {
|
|
165
|
+
clientTimezone: null,
|
|
166
|
+
timeSinceLastMessage: null,
|
|
167
|
+
},
|
|
168
|
+
}),
|
|
169
|
+
);
|
|
163
170
|
}
|
|
164
171
|
|
|
165
172
|
afterEach(() => {
|
|
@@ -276,6 +276,46 @@ describe("voice_config_update — tts_voice_id (managed/vellum active)", () => {
|
|
|
276
276
|
expect(result.content).not.toContain("broadcast");
|
|
277
277
|
});
|
|
278
278
|
|
|
279
|
+
test("hints the inline picker for the next managed voice change", async () => {
|
|
280
|
+
makeVellumActive();
|
|
281
|
+
|
|
282
|
+
const result = await run(
|
|
283
|
+
{ setting: "tts_voice_id", value: "aura-2-zeus-en" },
|
|
284
|
+
makeContext(),
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
expect(result.isError).toBe(false);
|
|
288
|
+
expect(result.content).toStartWith("Vellum managed voice updated to");
|
|
289
|
+
expect(result.content).toContain(
|
|
290
|
+
'ui_show { surface_type: "voice_picker", data: {} }',
|
|
291
|
+
);
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test("does not hint the picker when the voice is not managed", async () => {
|
|
295
|
+
writeConfig({ services: { tts: { provider: "elevenlabs" } } });
|
|
296
|
+
invalidateConfigCache();
|
|
297
|
+
|
|
298
|
+
const result = await run(
|
|
299
|
+
{ setting: "tts_voice_id", value: "abc123" },
|
|
300
|
+
makeContext(),
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
expect(result.isError).toBe(false);
|
|
304
|
+
expect(result.content).not.toContain("voice_picker");
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test("does not hint the picker for a non-voice setting", async () => {
|
|
308
|
+
makeVellumActive();
|
|
309
|
+
|
|
310
|
+
const result = await run(
|
|
311
|
+
{ setting: "conversation_timeout", value: 30 },
|
|
312
|
+
makeContext(),
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
expect(result.isError).toBe(false);
|
|
316
|
+
expect(result.content).not.toContain("voice_picker");
|
|
317
|
+
});
|
|
318
|
+
|
|
279
319
|
test("rejects a value with characters invalid for any voice id", async () => {
|
|
280
320
|
makeVellumActive();
|
|
281
321
|
|
|
@@ -89,7 +89,8 @@ const CALL_SESSION_ID = "call-session-voice-grant-test";
|
|
|
89
89
|
|
|
90
90
|
/**
|
|
91
91
|
* Create a mock session that, when runAgentLoop is called, emits a
|
|
92
|
-
* confirmation_request through the
|
|
92
|
+
* confirmation_request through the registered event observer before
|
|
93
|
+
* completing.
|
|
93
94
|
*/
|
|
94
95
|
function createMockSession(opts?: {
|
|
95
96
|
confirmationRequestId?: string;
|
|
@@ -118,8 +119,9 @@ function createMockSession(opts?: {
|
|
|
118
119
|
currentRequestId: requestId,
|
|
119
120
|
abort: () => {},
|
|
120
121
|
persistUserMessage: async () => ({ id: "msg-1", deduplicated: false }),
|
|
121
|
-
|
|
122
|
+
addEventObserver: (cb: (msg: AssistantEvent) => void) => {
|
|
122
123
|
clientCallback = cb;
|
|
124
|
+
return () => {};
|
|
123
125
|
},
|
|
124
126
|
handleConfirmationResponse: (
|
|
125
127
|
reqId: string,
|
|
@@ -138,7 +140,7 @@ function createMockSession(opts?: {
|
|
|
138
140
|
_messageId: string,
|
|
139
141
|
options?: { onEvent?: (msg: AssistantEvent) => void },
|
|
140
142
|
) => {
|
|
141
|
-
// Emit a confirmation_request through the
|
|
143
|
+
// Emit a confirmation_request through the event observer
|
|
142
144
|
if (clientCallback) {
|
|
143
145
|
clientCallback({
|
|
144
146
|
type: "confirmation_request",
|
|
@@ -61,7 +61,7 @@ function makeStreamingSession(events: AssistantEvent[]): Conversation {
|
|
|
61
61
|
setTurnChannelContext: () => {},
|
|
62
62
|
setTurnInterfaceContext: () => {},
|
|
63
63
|
setVoiceCallControlPrompt: () => {},
|
|
64
|
-
|
|
64
|
+
addEventObserver: () => () => {},
|
|
65
65
|
ensureActorScopedHistory: async () => {},
|
|
66
66
|
runAgentLoop: async (
|
|
67
67
|
_content: string,
|
|
@@ -116,7 +116,7 @@ function makePersistingStreamingSession(
|
|
|
116
116
|
turnInterfaceContext = ctx;
|
|
117
117
|
},
|
|
118
118
|
setVoiceCallControlPrompt: () => {},
|
|
119
|
-
|
|
119
|
+
addEventObserver: () => () => {},
|
|
120
120
|
ensureActorScopedHistory: async () => {},
|
|
121
121
|
runAgentLoop: async (
|
|
122
122
|
_content: string,
|
|
@@ -254,7 +254,7 @@ describe("voice-session-bridge", () => {
|
|
|
254
254
|
setTurnChannelContext: () => {},
|
|
255
255
|
setTurnInterfaceContext: () => {},
|
|
256
256
|
setVoiceCallControlPrompt: () => {},
|
|
257
|
-
|
|
257
|
+
addEventObserver: () => () => {},
|
|
258
258
|
ensureActorScopedHistory: async () => {},
|
|
259
259
|
runAgentLoop: async () => {
|
|
260
260
|
await new Promise((r) => setTimeout(r, 200));
|
|
@@ -321,6 +321,51 @@ describe("voice-session-bridge", () => {
|
|
|
321
321
|
expect(capturedOptions?.callSite).toBe("callAgent");
|
|
322
322
|
});
|
|
323
323
|
|
|
324
|
+
test("startVoiceTurn declares the turn interactive so approval prompts are raised for its observer", async () => {
|
|
325
|
+
// A caller is on the line. If the turn ran non-interactive the permission
|
|
326
|
+
// checker would deny an approval-gated tool before any prompt existed, and
|
|
327
|
+
// the bridge's approval observer (auto-resolve for a non-guardian caller, a
|
|
328
|
+
// real card for the guardian) would never get to decide. Presence is per
|
|
329
|
+
// turn, so the bridge has to say so.
|
|
330
|
+
const conversation = createConversation("voice bridge interactive test");
|
|
331
|
+
const events: AssistantEvent[] = [
|
|
332
|
+
{ type: "message_complete", conversationId: conversation.id },
|
|
333
|
+
];
|
|
334
|
+
|
|
335
|
+
let capturedOptions: Record<string, unknown> | undefined;
|
|
336
|
+
const session = {
|
|
337
|
+
...makeStreamingSession(events),
|
|
338
|
+
runAgentLoop: async (
|
|
339
|
+
_content: string,
|
|
340
|
+
_messageId: string,
|
|
341
|
+
options?: Record<string, unknown>,
|
|
342
|
+
) => {
|
|
343
|
+
capturedOptions = options;
|
|
344
|
+
const onEvent =
|
|
345
|
+
(options as { onEvent?: (msg: AssistantEvent) => void })?.onEvent ??
|
|
346
|
+
(() => {});
|
|
347
|
+
for (const event of events) {
|
|
348
|
+
onEvent(event);
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
} as unknown as Conversation;
|
|
352
|
+
|
|
353
|
+
injectDeps(() => session);
|
|
354
|
+
|
|
355
|
+
await startVoiceTurn({
|
|
356
|
+
conversationId: conversation.id,
|
|
357
|
+
content: "Hello",
|
|
358
|
+
isInbound: true,
|
|
359
|
+
onTextDelta: () => {},
|
|
360
|
+
onComplete: () => {},
|
|
361
|
+
onError: () => {},
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
365
|
+
|
|
366
|
+
expect(capturedOptions?.isInteractive).toBe(true);
|
|
367
|
+
});
|
|
368
|
+
|
|
324
369
|
test("external AbortSignal triggers turn abort", async () => {
|
|
325
370
|
const conversation = createConversation("voice bridge signal test");
|
|
326
371
|
let abortCalled = false;
|
|
@@ -339,7 +384,7 @@ describe("voice-session-bridge", () => {
|
|
|
339
384
|
setTurnChannelContext: () => {},
|
|
340
385
|
setTurnInterfaceContext: () => {},
|
|
341
386
|
setVoiceCallControlPrompt: () => {},
|
|
342
|
-
|
|
387
|
+
addEventObserver: () => () => {},
|
|
343
388
|
ensureActorScopedHistory: async () => {},
|
|
344
389
|
runAgentLoop: async () => {
|
|
345
390
|
await new Promise((r) => setTimeout(r, 200));
|
|
@@ -704,13 +749,14 @@ describe("voice-session-bridge", () => {
|
|
|
704
749
|
setTurnChannelContext: () => {},
|
|
705
750
|
setTurnInterfaceContext: () => {},
|
|
706
751
|
setVoiceCallControlPrompt: () => {},
|
|
707
|
-
|
|
752
|
+
addEventObserver: (handler: (msg: AssistantEvent) => void) => {
|
|
708
753
|
clientHandler = handler;
|
|
754
|
+
return () => {};
|
|
709
755
|
},
|
|
710
756
|
ensureActorScopedHistory: async () => {},
|
|
711
757
|
runAgentLoop: async () => {
|
|
712
|
-
// Simulate the
|
|
713
|
-
//
|
|
758
|
+
// Simulate the conversation emitting a confirmation_request to its
|
|
759
|
+
// event observer (this is how the real conversation fans out).
|
|
714
760
|
clientHandler({
|
|
715
761
|
type: "confirmation_request",
|
|
716
762
|
requestId: "req-voice-1",
|
|
@@ -796,8 +842,9 @@ describe("voice-session-bridge", () => {
|
|
|
796
842
|
setTurnChannelContext: () => {},
|
|
797
843
|
setTurnInterfaceContext: () => {},
|
|
798
844
|
setVoiceCallControlPrompt: () => {},
|
|
799
|
-
|
|
845
|
+
addEventObserver: (handler: (msg: AssistantEvent) => void) => {
|
|
800
846
|
clientHandler = handler;
|
|
847
|
+
return () => {};
|
|
801
848
|
},
|
|
802
849
|
ensureActorScopedHistory: async () => {},
|
|
803
850
|
runAgentLoop: async () => {
|
|
@@ -879,8 +926,9 @@ describe("voice-session-bridge", () => {
|
|
|
879
926
|
setTurnChannelContext: () => {},
|
|
880
927
|
setTurnInterfaceContext: () => {},
|
|
881
928
|
setVoiceCallControlPrompt: () => {},
|
|
882
|
-
|
|
929
|
+
addEventObserver: (handler: (msg: AssistantEvent) => void) => {
|
|
883
930
|
clientHandler = handler;
|
|
931
|
+
return () => {};
|
|
884
932
|
},
|
|
885
933
|
ensureActorScopedHistory: async () => {},
|
|
886
934
|
runAgentLoop: async () => {
|
|
@@ -946,8 +994,9 @@ describe("voice-session-bridge", () => {
|
|
|
946
994
|
setTurnChannelContext: () => {},
|
|
947
995
|
setTurnInterfaceContext: () => {},
|
|
948
996
|
setVoiceCallControlPrompt: () => {},
|
|
949
|
-
|
|
997
|
+
addEventObserver: (handler: (msg: AssistantEvent) => void) => {
|
|
950
998
|
clientHandler = handler;
|
|
999
|
+
return () => {};
|
|
951
1000
|
},
|
|
952
1001
|
ensureActorScopedHistory: async () => {},
|
|
953
1002
|
runAgentLoop: async () => {
|
|
@@ -1009,8 +1058,9 @@ describe("voice-session-bridge", () => {
|
|
|
1009
1058
|
setTurnChannelContext: () => {},
|
|
1010
1059
|
setTurnInterfaceContext: () => {},
|
|
1011
1060
|
setVoiceCallControlPrompt: () => {},
|
|
1012
|
-
|
|
1061
|
+
addEventObserver: (handler: (msg: AssistantEvent) => void) => {
|
|
1013
1062
|
clientHandler = handler;
|
|
1063
|
+
return () => {};
|
|
1014
1064
|
},
|
|
1015
1065
|
ensureActorScopedHistory: async () => {},
|
|
1016
1066
|
runAgentLoop: async () => {
|
|
@@ -1061,13 +1111,20 @@ describe("voice-session-bridge", () => {
|
|
|
1061
1111
|
// broadcast in voice-session-bridge.ts's confirmation_request branch. The
|
|
1062
1112
|
// fake's handleConfirmationResponse mirrors production's synchronous
|
|
1063
1113
|
// `interaction_resolved` broadcast so the wire order is observable through
|
|
1064
|
-
// the event hub, which serializes publishes in call order.
|
|
1114
|
+
// the event hub, which serializes publishes in call order. Its `emit`
|
|
1115
|
+
// mirrors a real conversation's: the sink (the hub) delivers first, then
|
|
1116
|
+
// observers run, so the bridge's resolution lands after the request.
|
|
1065
1117
|
function makeConfirmationOrderingSession(
|
|
1066
1118
|
conversationId: string,
|
|
1067
1119
|
requestId: string,
|
|
1068
1120
|
): Conversation {
|
|
1069
1121
|
let clientHandler: (msg: AssistantEvent) => void = () => {};
|
|
1122
|
+
const emit = (msg: AssistantEvent) => {
|
|
1123
|
+
broadcastMessage(msg);
|
|
1124
|
+
clientHandler(msg);
|
|
1125
|
+
};
|
|
1070
1126
|
return {
|
|
1127
|
+
emit,
|
|
1071
1128
|
isProcessing: () => false,
|
|
1072
1129
|
persistUserMessage: async () => ({
|
|
1073
1130
|
id: "test-msg-id",
|
|
@@ -1080,12 +1137,13 @@ describe("voice-session-bridge", () => {
|
|
|
1080
1137
|
setTurnChannelContext: () => {},
|
|
1081
1138
|
setTurnInterfaceContext: () => {},
|
|
1082
1139
|
setVoiceCallControlPrompt: () => {},
|
|
1083
|
-
|
|
1140
|
+
addEventObserver: (handler: (msg: AssistantEvent) => void) => {
|
|
1084
1141
|
clientHandler = handler;
|
|
1142
|
+
return () => {};
|
|
1085
1143
|
},
|
|
1086
1144
|
ensureActorScopedHistory: async () => {},
|
|
1087
1145
|
runAgentLoop: async () => {
|
|
1088
|
-
|
|
1146
|
+
emit({
|
|
1089
1147
|
type: "confirmation_request",
|
|
1090
1148
|
requestId,
|
|
1091
1149
|
toolName: "host_bash",
|
|
@@ -1222,8 +1280,9 @@ describe("voice-session-bridge", () => {
|
|
|
1222
1280
|
setTurnChannelContext: () => {},
|
|
1223
1281
|
setTurnInterfaceContext: () => {},
|
|
1224
1282
|
setVoiceCallControlPrompt: () => {},
|
|
1225
|
-
|
|
1283
|
+
addEventObserver: (handler: (msg: AssistantEvent) => void) => {
|
|
1226
1284
|
clientHandler = handler;
|
|
1285
|
+
return () => {};
|
|
1227
1286
|
},
|
|
1228
1287
|
ensureActorScopedHistory: async () => {},
|
|
1229
1288
|
runAgentLoop: async () => {
|
|
@@ -1290,7 +1349,7 @@ describe("voice-session-bridge", () => {
|
|
|
1290
1349
|
setTurnChannelContext: () => {},
|
|
1291
1350
|
setTurnInterfaceContext: () => {},
|
|
1292
1351
|
setVoiceCallControlPrompt: () => {},
|
|
1293
|
-
|
|
1352
|
+
addEventObserver: () => () => {},
|
|
1294
1353
|
ensureActorScopedHistory: async () => {},
|
|
1295
1354
|
runAgentLoop: async () => {},
|
|
1296
1355
|
handleConfirmationResponse: () => {},
|
|
@@ -1349,7 +1408,7 @@ describe("voice-session-bridge", () => {
|
|
|
1349
1408
|
setTurnChannelContext: recordLast("setTurnChannelContext"),
|
|
1350
1409
|
setTurnInterfaceContext: recordLast("setTurnInterfaceContext"),
|
|
1351
1410
|
setVoiceCallControlPrompt: recordLast("setVoiceCallControlPrompt"),
|
|
1352
|
-
|
|
1411
|
+
addEventObserver: () => () => {},
|
|
1353
1412
|
ensureActorScopedHistory: async () => {},
|
|
1354
1413
|
runAgentLoop: async () => {},
|
|
1355
1414
|
handleConfirmationResponse: () => {},
|
|
@@ -1391,15 +1450,12 @@ describe("voice-session-bridge", () => {
|
|
|
1391
1450
|
expect(session.forcePromptSideEffects).toBe(false);
|
|
1392
1451
|
});
|
|
1393
1452
|
|
|
1394
|
-
test("cleanup on early persistUserMessage throw does not detach prior
|
|
1453
|
+
test("cleanup on early persistUserMessage throw does not detach a prior turn's observer", async () => {
|
|
1395
1454
|
const conversation = createConversation(
|
|
1396
1455
|
"voice bridge sender detach guard test",
|
|
1397
1456
|
);
|
|
1398
1457
|
|
|
1399
|
-
const
|
|
1400
|
-
callback: unknown;
|
|
1401
|
-
replace: boolean | undefined;
|
|
1402
|
-
}> = [];
|
|
1458
|
+
const addEventObserverCalls: unknown[] = [];
|
|
1403
1459
|
|
|
1404
1460
|
const session = {
|
|
1405
1461
|
isProcessing: () => false,
|
|
@@ -1415,8 +1471,9 @@ describe("voice-session-bridge", () => {
|
|
|
1415
1471
|
setTurnChannelContext: () => {},
|
|
1416
1472
|
setTurnInterfaceContext: () => {},
|
|
1417
1473
|
setVoiceCallControlPrompt: () => {},
|
|
1418
|
-
|
|
1419
|
-
|
|
1474
|
+
addEventObserver: (observer: unknown) => {
|
|
1475
|
+
addEventObserverCalls.push(observer);
|
|
1476
|
+
return () => {};
|
|
1420
1477
|
},
|
|
1421
1478
|
ensureActorScopedHistory: async () => {},
|
|
1422
1479
|
runAgentLoop: async () => {},
|
|
@@ -1451,10 +1508,9 @@ describe("voice-session-bridge", () => {
|
|
|
1451
1508
|
expect(caught?.message).toBe(
|
|
1452
1509
|
"persist failed before bridge installed callback",
|
|
1453
1510
|
);
|
|
1454
|
-
// The bridge never reached its `conversation.
|
|
1455
|
-
// site, so
|
|
1456
|
-
|
|
1457
|
-
expect(updateClientCalls).toEqual([]);
|
|
1511
|
+
// The bridge never reached its `conversation.addEventObserver(...)`
|
|
1512
|
+
// install site, so no observer was registered for cleanup to detach.
|
|
1513
|
+
expect(addEventObserverCalls).toEqual([]);
|
|
1458
1514
|
});
|
|
1459
1515
|
|
|
1460
1516
|
test("pre-aborted signal triggers immediate abort", async () => {
|
|
@@ -1475,7 +1531,7 @@ describe("voice-session-bridge", () => {
|
|
|
1475
1531
|
setTurnChannelContext: () => {},
|
|
1476
1532
|
setTurnInterfaceContext: () => {},
|
|
1477
1533
|
setVoiceCallControlPrompt: () => {},
|
|
1478
|
-
|
|
1534
|
+
addEventObserver: () => () => {},
|
|
1479
1535
|
ensureActorScopedHistory: async () => {},
|
|
1480
1536
|
runAgentLoop: async () => {
|
|
1481
1537
|
await new Promise((r) => setTimeout(r, 200));
|
|
@@ -18,10 +18,18 @@ import { Glob } from "bun";
|
|
|
18
18
|
* - start the PID-file identity guard — the PID file is a worker's sole
|
|
19
19
|
* tracking handle, so a worker the file stops naming can never be
|
|
20
20
|
* stopped externally and must evict itself.
|
|
21
|
+
*
|
|
22
|
+
* The subset that hosts real agent conversations
|
|
23
|
+
* ({@link CONVERSATION_RUNNING_ENTRYPOINTS}) must additionally register the
|
|
24
|
+
* default plugins' hooks and injectors, since hook dispatch is process-global
|
|
25
|
+
* and a worker that skips it runs conversations with no plugin behavior at all
|
|
26
|
+
* (no image captioning or vision-rejection recovery, no tool-result
|
|
27
|
+
* truncation, no runtime injections).
|
|
21
28
|
*/
|
|
22
29
|
|
|
23
30
|
const DISABLE_CALL = "disableStreamSeqStamping()";
|
|
24
31
|
const PID_GUARD_CALL = "startWorkerPidFileGuard(";
|
|
32
|
+
const PLUGIN_SURFACE_CALL = "registerWorkerPluginSurface(";
|
|
25
33
|
|
|
26
34
|
/** Shape of a shutdown that schedules `process.exit` instead of calling it. */
|
|
27
35
|
const DEFERRED_EXIT = ".finally(() => process.exit";
|
|
@@ -44,6 +52,16 @@ const WORK_START_MARKERS: Record<string, string> = {
|
|
|
44
52
|
"routes/worker.ts": 'server.on("connection"',
|
|
45
53
|
};
|
|
46
54
|
|
|
55
|
+
/**
|
|
56
|
+
* The entrypoints whose work wakes real agent conversations. `monitoring` (a
|
|
57
|
+
* resource sampler) and `routes` (an HTTP route host) run no conversations, so
|
|
58
|
+
* they carry no plugin surface.
|
|
59
|
+
*/
|
|
60
|
+
const CONVERSATION_RUNNING_ENTRYPOINTS: ReadonlySet<string> = new Set([
|
|
61
|
+
"schedule/worker.ts",
|
|
62
|
+
"plugins/defaults/memory/worker.ts",
|
|
63
|
+
]);
|
|
64
|
+
|
|
47
65
|
function findWorkerEntrypoints(): string[] {
|
|
48
66
|
const srcRoot = join(process.cwd(), "src");
|
|
49
67
|
const glob = new Glob("**/worker.ts");
|
|
@@ -137,4 +155,40 @@ describe("worker entrypoint guards", () => {
|
|
|
137
155
|
"worker superseded at startup before it runs orphaned work.",
|
|
138
156
|
).toEqual([]);
|
|
139
157
|
});
|
|
158
|
+
|
|
159
|
+
test("the conversation-running entrypoints are all discovered workers", () => {
|
|
160
|
+
const discovered = new Set(findWorkerEntrypoints());
|
|
161
|
+
const unknown = [...CONVERSATION_RUNNING_ENTRYPOINTS].filter(
|
|
162
|
+
(file) => !discovered.has(file),
|
|
163
|
+
);
|
|
164
|
+
expect(
|
|
165
|
+
unknown,
|
|
166
|
+
"CONVERSATION_RUNNING_ENTRYPOINTS names an entrypoint the glob does " +
|
|
167
|
+
"not find — the worker moved or was renamed.",
|
|
168
|
+
).toEqual([]);
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
test("every conversation-running entrypoint registers the default plugin surface before starting work", () => {
|
|
172
|
+
const offenders = [...CONVERSATION_RUNNING_ENTRYPOINTS]
|
|
173
|
+
.sort()
|
|
174
|
+
.filter((file) => {
|
|
175
|
+
const source = readFileSync(join(process.cwd(), "src", file), "utf8");
|
|
176
|
+
const marker = WORK_START_MARKERS[file];
|
|
177
|
+
if (marker == null) {
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
const registerAt = source.indexOf(PLUGIN_SURFACE_CALL);
|
|
181
|
+
const workAt = source.indexOf(marker);
|
|
182
|
+
return registerAt < 0 || workAt < 0 || registerAt > workAt;
|
|
183
|
+
});
|
|
184
|
+
expect(
|
|
185
|
+
offenders,
|
|
186
|
+
`A worker that wakes agent conversations must call ` +
|
|
187
|
+
`${PLUGIN_SURFACE_CALL}) before its work-start call (see ` +
|
|
188
|
+
"WORK_START_MARKERS). Hook dispatch is process-global, so without it " +
|
|
189
|
+
"the conversations this process runs get no default plugin behavior: " +
|
|
190
|
+
"no vision-rejection recovery, no tool-result truncation, no runtime " +
|
|
191
|
+
"injections.",
|
|
192
|
+
).toEqual([]);
|
|
193
|
+
});
|
|
140
194
|
});
|