@vellumai/assistant 0.8.8 → 0.8.9-dev.202606091926.ebb2d62
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +6 -6
- package/bun.lock +2 -2
- package/docs/activation-funnel-telemetry.md +310 -0
- package/examples/plugins/echo/README.md +61 -60
- package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
- package/examples/plugins/echo/hooks/stop.ts +16 -0
- package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
- package/examples/plugins/echo/package.json +1 -2
- package/examples/plugins/echo/src/emit.ts +19 -0
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
- package/openapi.yaml +273 -121
- package/package.json +2 -2
- package/scripts/sync-llm-catalog.ts +2 -0
- package/src/__tests__/activation-early-marking.test.ts +120 -0
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +69 -14
- package/src/__tests__/agent-loop-exit-reason.test.ts +204 -144
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +50 -35
- package/src/__tests__/agent-loop-output-hooks.test.ts +357 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +25 -6
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +41 -21
- package/src/__tests__/agent-loop-thinking.test.ts +36 -20
- package/src/__tests__/agent-loop.test.ts +441 -96
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +14 -14
- package/src/__tests__/agent-wake-override-profile.test.ts +17 -21
- package/src/__tests__/anthropic-provider.test.ts +23 -8
- package/src/__tests__/app-builder-tool-scripts.test.ts +21 -0
- package/src/__tests__/app-control-flow.test.ts +1 -1
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +132 -0
- package/src/__tests__/approval-cascade.test.ts +6 -5
- package/src/__tests__/approval-routes-http.test.ts +4 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
- package/src/__tests__/build-persisted-content.test.ts +75 -1
- package/src/__tests__/channel-approval-routes.test.ts +1 -1
- package/src/__tests__/channel-approvals.test.ts +1 -1
- package/src/__tests__/compaction-circuit.test.ts +258 -0
- package/src/__tests__/compaction-direct.test.ts +146 -0
- package/src/__tests__/compaction-events.test.ts +7 -7
- package/src/__tests__/compaction.benchmark.test.ts +1 -1
- package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
- package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
- package/src/__tests__/context-overflow-reducer.test.ts +265 -125
- package/src/__tests__/context-window-manager-compact-retry.test.ts +121 -15
- package/src/__tests__/conversation-abort-tool-results.test.ts +7 -5
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +7 -10
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +78 -119
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +158 -234
- package/src/__tests__/conversation-agent-loop.test.ts +342 -623
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +6 -5
- package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
- package/src/__tests__/conversation-error.test.ts +15 -1
- package/src/__tests__/conversation-history-web-search.test.ts +6 -1
- package/src/__tests__/conversation-media-retry.test.ts +1 -1
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +44 -4
- package/src/__tests__/conversation-process-callsite.test.ts +15 -15
- package/src/__tests__/conversation-provider-retry-repair.test.ts +66 -65
- package/src/__tests__/conversation-queue.test.ts +10 -10
- package/src/__tests__/conversation-runtime-assembly.test.ts +1059 -231
- package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
- package/src/__tests__/conversation-slash-queue.test.ts +7 -5
- package/src/__tests__/conversation-slash-unknown.test.ts +7 -5
- package/src/__tests__/conversation-speed-override.test.ts +11 -10
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +395 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +48 -1
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +73 -4
- package/src/__tests__/conversation-undo.test.ts +2 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +24 -21
- package/src/__tests__/conversation-workspace-injection.test.ts +69 -7
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +15 -12
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/corrected-target.test.ts +93 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +5 -4
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +36 -0
- package/src/__tests__/db-acp-history.test.ts +101 -0
- package/src/__tests__/dynamic-page-surface.test.ts +31 -0
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
- package/src/__tests__/empty-response-hook.test.ts +1 -1
- package/src/__tests__/file-write-tool.test.ts +63 -0
- package/src/__tests__/gateway-only-guard.test.ts +17 -2
- package/src/__tests__/guardian-grant-minting.test.ts +1 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +93 -4
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +1 -0
- package/src/__tests__/history-repair-hook.test.ts +5 -1
- package/src/__tests__/host-app-control-proxy.test.ts +45 -0
- package/src/__tests__/host-app-control-routes.test.ts +1 -1
- package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
- package/src/__tests__/inference-profile-reaper.test.ts +62 -0
- package/src/__tests__/inference-profile-session-handler.test.ts +86 -0
- package/src/__tests__/injector-background-turn.test.ts +13 -23
- package/src/__tests__/injector-chain.test.ts +268 -44
- package/src/__tests__/injector-disk-pressure.test.ts +210 -52
- package/src/__tests__/injector-document-comments.test.ts +97 -114
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
- package/src/__tests__/injector-v3-suppression.test.ts +4 -4
- package/src/__tests__/list-messages-client-message-id.test.ts +91 -0
- package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
- package/src/__tests__/llm-catalog-parity.test.ts +4 -0
- package/src/__tests__/llm-resolver.test.ts +73 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +156 -38
- package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
- package/src/__tests__/notification-decision-strategy.test.ts +3 -3
- package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +145 -0
- package/src/__tests__/persist-unsendable-image.test.ts +97 -1
- package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
- package/src/__tests__/plugin-api-shim.test.ts +6 -9
- package/src/__tests__/plugin-bootstrap.test.ts +91 -81
- package/src/__tests__/plugin-registry.test.ts +3 -49
- package/src/__tests__/plugin-skill-contribution.test.ts +15 -14
- package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
- package/src/__tests__/plugin-types.test.ts +0 -70
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +69 -0
- package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
- package/src/__tests__/published-app-updater.test.ts +138 -0
- package/src/__tests__/reaction-persistence.test.ts +1 -1
- package/src/__tests__/retry-thinking-adaptive-only.test.ts +245 -0
- package/src/__tests__/send-endpoint-busy.test.ts +4 -1
- package/src/__tests__/server-history-render.test.ts +39 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +31 -0
- package/src/__tests__/steer-tool-repair.test.ts +1 -1
- package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
- package/src/__tests__/subagent-detail.test.ts +25 -7
- package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
- package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
- package/src/__tests__/subagent-manager-notify.test.ts +1 -3
- package/src/__tests__/subagent-notify-parent.test.ts +1 -3
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
- package/src/__tests__/title-generate-hook.test.ts +5 -1
- package/src/__tests__/tool-error-hook.test.ts +1 -1
- package/src/__tests__/tool-result-truncate-hook.test.ts +1 -1
- package/src/__tests__/tool-start-timestamp.test.ts +218 -0
- package/src/__tests__/user-plugin-loader.test.ts +54 -286
- package/src/__tests__/web-fetch.test.ts +45 -0
- package/src/acp/__tests__/agent-process.test.ts +161 -0
- package/src/acp/__tests__/client-handler.test.ts +40 -0
- package/src/acp/__tests__/helpers/acp-config-stub.ts +0 -2
- package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
- package/src/acp/__tests__/helpers/exec-file-stub.ts +106 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +97 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
- package/src/acp/__tests__/session-manager-resume.test.ts +888 -0
- package/src/acp/agent-process.ts +61 -1
- package/src/acp/auto-install.test.ts +280 -0
- package/src/acp/auto-install.ts +232 -0
- package/src/acp/client-handler.ts +31 -0
- package/src/acp/prepare-agent-env.ts +80 -27
- package/src/acp/resolve-agent.test.ts +188 -28
- package/src/acp/resolve-agent.ts +119 -42
- package/src/acp/resume-hint.ts +23 -0
- package/src/acp/session-manager.ts +507 -73
- package/src/agent/compaction-circuit.ts +60 -102
- package/src/agent/loop.ts +403 -251
- package/src/api/events/tool-result.ts +9 -0
- package/src/api/events/tool-use-start.ts +7 -0
- package/src/api/responses/conversation-message.ts +72 -13
- package/src/api/responses/memory-v3-selection-log.ts +19 -10
- package/src/approvals/guardian-request-resolvers.ts +27 -1
- package/src/background-wake/next-wake.ts +1 -0
- package/src/cli/commands/__tests__/memory-v3.test.ts +191 -210
- package/src/cli/commands/channel-verification-sessions.ts +6 -6
- package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
- package/src/cli/commands/memory-v3.ts +57 -199
- package/src/cli/commands/plugins.ts +43 -37
- package/src/cli/lib/__tests__/install-from-github.test.ts +790 -111
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
- package/src/cli/lib/__tests__/plugin-details.test.ts +381 -0
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +270 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +233 -32
- package/src/cli/lib/install-from-github.ts +797 -60
- package/src/cli/lib/plugin-catalog-cache.ts +84 -0
- package/src/cli/lib/plugin-details.ts +416 -0
- package/src/cli/lib/plugin-marketplace.ts +215 -0
- package/src/cli/lib/search-plugins.ts +201 -29
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
- package/src/config/acp-defaults.test.ts +10 -0
- package/src/config/acp-defaults.ts +9 -3
- package/src/config/acp-schema.ts +1 -7
- package/src/config/bundled-skills/acp/SKILL.md +80 -41
- package/src/config/bundled-skills/acp/TOOLS.json +4 -4
- package/src/config/bundled-skills/app-builder/SKILL.md +224 -381
- package/src/config/bundled-skills/app-builder/TOOLS.json +72 -2
- package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
- package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +18 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
- package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
- package/src/config/bundled-tool-registry.ts +4 -0
- package/src/config/call-site-defaults.ts +0 -3
- package/src/config/feature-flag-registry.json +10 -56
- package/src/config/llm-resolver.ts +39 -7
- package/src/config/loader.ts +5 -22
- package/src/config/schemas/__tests__/memory-v3.test.ts +25 -9
- package/src/config/schemas/call-site-catalog.ts +0 -21
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +17 -3
- package/src/config/schemas/memory-v3.ts +58 -8
- package/src/config/seed-inference-profiles.ts +18 -0
- package/src/context/compactor.ts +22 -4
- package/src/context/post-turn-tool-result-truncation.ts +39 -1
- package/src/context/strip-injections.ts +8 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +120 -42
- package/src/daemon/conversation-agent-loop.ts +308 -1297
- package/src/daemon/conversation-error.ts +31 -4
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +11 -255
- package/src/daemon/conversation-media-retry.ts +19 -6
- package/src/daemon/conversation-messaging.ts +17 -0
- package/src/daemon/conversation-process.ts +22 -141
- package/src/daemon/conversation-queue-manager.ts +8 -0
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +361 -393
- package/src/daemon/conversation-store.ts +9 -90
- package/src/daemon/conversation-surfaces.ts +165 -11
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +444 -62
- package/src/daemon/disk-pressure-policy.ts +0 -1
- package/src/daemon/external-plugins-bootstrap.ts +36 -46
- package/src/daemon/handlers/config-channels.ts +11 -2
- package/src/daemon/handlers/conversations.ts +3 -1
- package/src/daemon/handlers/shared.ts +9 -1
- package/src/daemon/handlers/skills.ts +4 -1
- package/src/daemon/host-app-control-proxy.ts +72 -57
- package/src/daemon/host-proxy-preactivation.ts +1 -3
- package/src/daemon/lifecycle.ts +21 -7
- package/src/daemon/persist-unsendable-image.ts +62 -25
- package/src/daemon/process-message.ts +1 -1
- package/src/daemon/server.ts +2 -0
- package/src/daemon/tool-side-effects.ts +15 -0
- package/src/daemon/wake-conversation-ops.ts +269 -0
- package/src/embedded/plugin-api.ts +2 -2
- package/src/export/__tests__/transcript-formatter.test.ts +5 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
- package/src/heartbeat/heartbeat-run-store.ts +23 -1
- package/src/heartbeat/heartbeat-service.ts +26 -0
- package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
- package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
- package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
- package/src/ipc/skill-routes/memory.ts +4 -2
- package/src/memory/__tests__/activation-session-store.test.ts +41 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +87 -0
- package/src/memory/__tests__/onboarding-events-store.test.ts +80 -0
- package/src/memory/activation-session-store.ts +43 -0
- package/src/memory/conversation-crud.ts +29 -19
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/db-init.ts +6 -0
- package/src/memory/embedding-backend.ts +5 -22
- package/src/memory/job-handlers/conversation-starters.ts +13 -2
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -4
- package/src/memory/jobs-worker.ts +25 -1
- package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
- package/src/memory/migrations/273-onboarding-events-funnel-columns.ts +46 -0
- package/src/memory/migrations/274-create-activation-sessions.ts +15 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/onboarding-events-store.ts +66 -18
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/schema/infrastructure.ts +13 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -97
- package/src/memory/v2/__tests__/page-store.test.ts +22 -0
- package/src/memory/v2/consolidation-job.ts +2 -63
- package/src/memory/v2/types.ts +5 -0
- package/src/messaging/providers/telegram-bot/api.ts +14 -5
- package/src/notifications/__tests__/copy-composer.test.ts +244 -0
- package/src/notifications/access-request-copy.ts +298 -0
- package/src/notifications/adapters/slack.ts +3 -3
- package/src/notifications/adapters/telegram.ts +9 -2
- package/src/notifications/copy-composer.ts +49 -267
- package/src/notifications/decision-engine.ts +16 -35
- package/src/notifications/home-feed-side-effect.ts +1 -6
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +40 -11
- package/src/plugin-api/types.ts +108 -0
- package/src/plugins/defaults/compaction/compact.ts +106 -0
- package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +250 -42
- package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
- package/src/plugins/defaults/compaction/manager-store.ts +57 -0
- package/src/plugins/defaults/compaction/package.json +1 -2
- package/src/{context → plugins/defaults/compaction}/window-manager.ts +95 -25
- package/src/plugins/defaults/empty-response/package.json +0 -1
- package/src/plugins/defaults/history-repair/package.json +0 -1
- package/src/plugins/defaults/index.ts +156 -26
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +285 -0
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +2 -2
- package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +148 -73
- package/src/plugins/defaults/memory-retrieval/package.json +14 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +223 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/capabilities.test.ts +71 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/dense.test.ts +181 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/edge.test.ts +247 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +147 -134
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +456 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +662 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +306 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +91 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +402 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-needle.test.ts +135 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/sections.test.ts +125 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +74 -19
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +446 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +592 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -24
- package/src/plugins/defaults/memory-v3-shadow/capabilities.ts +91 -0
- package/src/plugins/defaults/memory-v3-shadow/dense.ts +97 -0
- package/src/plugins/defaults/memory-v3-shadow/edge.ts +252 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +76 -0
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +555 -0
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +266 -0
- package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
- package/src/plugins/defaults/memory-v3-shadow/page-content.ts +71 -0
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +204 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +16 -8
- package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +236 -0
- package/src/plugins/defaults/memory-v3-shadow/section-needle.ts +200 -0
- package/src/plugins/defaults/memory-v3-shadow/sections.ts +115 -0
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +156 -0
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +382 -0
- package/src/plugins/defaults/memory-v3-shadow/types.ts +92 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +4 -1
- package/src/plugins/defaults/title-generate/package.json +0 -1
- package/src/plugins/defaults/tool-error/package.json +0 -1
- package/src/plugins/defaults/tool-result-truncate/package.json +0 -1
- package/src/plugins/external-api.ts +12 -2
- package/src/plugins/pipeline.ts +6 -293
- package/src/plugins/registry.ts +9 -37
- package/src/plugins/types.ts +76 -381
- package/src/plugins/user-loader.ts +30 -127
- package/src/prompts/__tests__/system-prompt.test.ts +6 -0
- package/src/prompts/system-prompt.ts +61 -10
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +71 -4
- package/src/providers/anthropic/client.ts +9 -10
- package/src/providers/inference/kimi-cjk-token-ids.ts +493 -0
- package/src/providers/inference/logit-bias.ts +55 -0
- package/src/providers/model-catalog.ts +57 -0
- package/src/providers/openai/__tests__/vision-not-supported.test.ts +75 -0
- package/src/providers/openai/chat-completions-provider.ts +44 -1
- package/src/providers/retry.ts +57 -7
- package/src/providers/types.ts +6 -0
- package/src/runtime/__tests__/agent-wake.test.ts +555 -691
- package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
- package/src/runtime/agent-wake.ts +108 -209
- package/src/runtime/assistant-event-hub.ts +1 -1
- package/src/runtime/channel-approvals.ts +1 -1
- package/src/runtime/interactive-ui.ts +1 -1
- package/src/runtime/routes/__tests__/acp-routes.test.ts +466 -55
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +423 -73
- package/src/runtime/routes/__tests__/stt-routes.test.ts +112 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
- package/src/runtime/routes/acp-routes.test.ts +185 -32
- package/src/runtime/routes/acp-routes.ts +328 -30
- package/src/runtime/routes/app-management-routes.ts +3 -0
- package/src/runtime/routes/approval-routes.ts +1 -1
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/browser-tabs-routes.ts +6 -10
- package/src/runtime/routes/channel-verification-routes.ts +14 -5
- package/src/runtime/routes/conversation-cli-routes.ts +1 -1
- package/src/runtime/routes/conversation-list-routes.ts +1 -1
- package/src/runtime/routes/conversation-query-routes.ts +1 -1
- package/src/runtime/routes/conversation-routes.ts +30 -2
- package/src/runtime/routes/conversation-starter-routes.ts +13 -7
- package/src/runtime/routes/conversations-import-routes.ts +24 -7
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-cu-routes.ts +1 -1
- package/src/runtime/routes/identity-routes.ts +18 -3
- package/src/runtime/routes/inbound-message-handler.ts +1 -1
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
- package/src/runtime/routes/inference-profile-session-handler.ts +11 -0
- package/src/runtime/routes/inference-profile-session-reaper.ts +6 -0
- package/src/runtime/routes/memory-v3-routes.ts +65 -305
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +1 -1
- package/src/runtime/routes/playground/helpers.ts +1 -1
- package/src/runtime/routes/plugins-routes.ts +337 -35
- package/src/runtime/routes/stt-routes.ts +45 -12
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/work-items-routes.ts +2 -4
- package/src/runtime/routes/workspace-routes.ts +50 -15
- package/src/runtime/services/conversation-serializer.ts +1 -1
- package/src/runtime/verification-outbound-actions.ts +147 -2
- package/src/runtime/verification-templates.ts +29 -3
- package/src/services/published-app-updater.ts +30 -8
- package/src/signals/cancel.ts +2 -4
- package/src/subagent/manager.ts +21 -5
- package/src/telemetry/__tests__/activation-funnel.test.ts +95 -0
- package/src/telemetry/activation-funnel.ts +167 -0
- package/src/telemetry/types.ts +13 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +154 -0
- package/src/telemetry/usage-telemetry-reporter.ts +26 -1
- package/src/tools/acp/context.ts +20 -0
- package/src/tools/acp/list-agents.test.ts +9 -19
- package/src/tools/acp/list-agents.ts +3 -15
- package/src/tools/acp/spawn.test.ts +173 -202
- package/src/tools/acp/spawn.ts +37 -172
- package/src/tools/acp/steer.test.ts +105 -8
- package/src/tools/acp/steer.ts +48 -17
- package/src/tools/apps/executors.ts +166 -50
- package/src/tools/browser/browser-execution.ts +12 -2
- package/src/tools/filesystem/write.ts +34 -0
- package/src/tools/network/web-fetch.ts +65 -24
- package/src/tools/skills/load.ts +1 -1
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/ui-surface/definitions.ts +32 -5
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +69 -45
- package/docs/plugins.md +0 -832
- package/examples/plugins/echo/register.ts +0 -143
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
- package/src/__tests__/compaction-pipeline.test.ts +0 -210
- package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
- package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -667
- package/src/__tests__/pipeline-runner.test.ts +0 -554
- package/src/daemon/wake-target-adapter.ts +0 -253
- package/src/memory/v3/__tests__/assign.test.ts +0 -242
- package/src/memory/v3/__tests__/capabilities.test.ts +0 -118
- package/src/memory/v3/__tests__/core.test.ts +0 -39
- package/src/memory/v3/__tests__/health.test.ts +0 -219
- package/src/memory/v3/__tests__/maintain-job.test.ts +0 -288
- package/src/memory/v3/__tests__/needle.test.ts +0 -107
- package/src/memory/v3/__tests__/orchestrate.test.ts +0 -436
- package/src/memory/v3/__tests__/provider-blocks.test.ts +0 -13
- package/src/memory/v3/__tests__/reconcile.test.ts +0 -274
- package/src/memory/v3/__tests__/render-injection.test.ts +0 -61
- package/src/memory/v3/__tests__/router.test.ts +0 -332
- package/src/memory/v3/__tests__/selector.test.ts +0 -470
- package/src/memory/v3/__tests__/shadow-plugin.test.ts +0 -432
- package/src/memory/v3/__tests__/snapshot.test.ts +0 -168
- package/src/memory/v3/__tests__/tree.test.ts +0 -192
- package/src/memory/v3/assign.ts +0 -268
- package/src/memory/v3/capabilities.ts +0 -124
- package/src/memory/v3/core.ts +0 -26
- package/src/memory/v3/health.ts +0 -0
- package/src/memory/v3/maintain-job.ts +0 -314
- package/src/memory/v3/needle.ts +0 -115
- package/src/memory/v3/orchestrate.ts +0 -126
- package/src/memory/v3/page-content.ts +0 -34
- package/src/memory/v3/provider-blocks.ts +0 -26
- package/src/memory/v3/reconcile.ts +0 -523
- package/src/memory/v3/router.ts +0 -190
- package/src/memory/v3/selection-log-store.ts +0 -84
- package/src/memory/v3/selector.ts +0 -226
- package/src/memory/v3/shadow-plugin.ts +0 -411
- package/src/memory/v3/snapshot.ts +0 -209
- package/src/memory/v3/tree.ts +0 -174
- package/src/memory/v3/types.ts +0 -59
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
- package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
- package/src/plugins/defaults/circuit-breaker/package.json +0 -15
- package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
- package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
- package/src/plugins/defaults/compaction/register.ts +0 -35
- package/src/plugins/defaults/compaction/terminal.ts +0 -73
- package/src/plugins/defaults/empty-response/register.ts +0 -23
- package/src/plugins/defaults/history-repair/register.ts +0 -24
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +0 -216
- package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
- package/src/plugins/defaults/overflow-reduce/package.json +0 -15
- package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
- package/src/plugins/defaults/title-generate/register.ts +0 -35
- package/src/plugins/defaults/tool-error/register.ts +0 -23
- package/src/plugins/defaults/tool-result-truncate/register.ts +0 -24
- package/src/proactive-artifact/aux-message-injector.ts +0 -97
- package/src/proactive-artifact/decision.test.ts +0 -226
- package/src/proactive-artifact/decision.ts +0 -165
- package/src/proactive-artifact/index.ts +0 -7
- package/src/proactive-artifact/job.test.ts +0 -962
- package/src/proactive-artifact/job.ts +0 -372
- package/src/proactive-artifact/message-copy.ts +0 -58
- package/src/proactive-artifact/trigger-state.test.ts +0 -286
- package/src/proactive-artifact/trigger-state.ts +0 -123
- package/src/util/map-limit.ts +0 -27
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/llm-retry.ts +0 -0
|
@@ -137,9 +137,10 @@ mock.module("../memory/retrieval-budget.js", () => ({
|
|
|
137
137
|
computeRecallBudget: () => 0,
|
|
138
138
|
}));
|
|
139
139
|
|
|
140
|
-
mock.module("../
|
|
140
|
+
mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
|
|
141
141
|
ContextWindowManager: class {
|
|
142
142
|
constructor() {}
|
|
143
|
+
updateConfig() {}
|
|
143
144
|
shouldCompact() {
|
|
144
145
|
return { needed: false, estimatedTokens: 0 };
|
|
145
146
|
}
|
|
@@ -176,10 +177,11 @@ mock.module("../agent/loop.js", () => ({
|
|
|
176
177
|
getActiveModel() {
|
|
177
178
|
return undefined;
|
|
178
179
|
}
|
|
179
|
-
async run(
|
|
180
|
-
messages: Message[]
|
|
181
|
-
onEvent: (event: AgentEvent) => void
|
|
182
|
-
): Promise<Message[]> {
|
|
180
|
+
async run(options: {
|
|
181
|
+
messages: Message[];
|
|
182
|
+
onEvent: (event: AgentEvent) => void;
|
|
183
|
+
}): Promise<Message[]> {
|
|
184
|
+
const { messages, onEvent } = options;
|
|
183
185
|
const assistantMessage: Message = {
|
|
184
186
|
role: "assistant",
|
|
185
187
|
content: [{ type: "text", text: "ok" }],
|
|
@@ -198,6 +200,7 @@ mock.module("../agent/loop.js", () => ({
|
|
|
198
200
|
}));
|
|
199
201
|
|
|
200
202
|
import { Conversation } from "../daemon/conversation.js";
|
|
203
|
+
import { refreshWorkspaceTopLevelContextIfNeeded } from "../daemon/conversation-workspace.js";
|
|
201
204
|
|
|
202
205
|
// ---------------------------------------------------------------------------
|
|
203
206
|
// Helpers
|
|
@@ -249,7 +252,7 @@ describe("Conversation workspace cache state", () => {
|
|
|
249
252
|
});
|
|
250
253
|
|
|
251
254
|
test("refreshWorkspaceTopLevelContextIfNeeded populates context and clears dirty", () => {
|
|
252
|
-
|
|
255
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
253
256
|
|
|
254
257
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
255
258
|
expect(conversation.getWorkspaceTopLevelContext()).not.toBeNull();
|
|
@@ -265,10 +268,10 @@ describe("Conversation workspace cache state", () => {
|
|
|
265
268
|
});
|
|
266
269
|
|
|
267
270
|
test("refreshWorkspaceTopLevelContextIfNeeded no-ops when not dirty and cache exists", () => {
|
|
268
|
-
|
|
271
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
269
272
|
const first = conversation.getWorkspaceTopLevelContext();
|
|
270
273
|
|
|
271
|
-
|
|
274
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
272
275
|
const second = conversation.getWorkspaceTopLevelContext();
|
|
273
276
|
|
|
274
277
|
// Same reference — no recomputation
|
|
@@ -276,7 +279,7 @@ describe("Conversation workspace cache state", () => {
|
|
|
276
279
|
});
|
|
277
280
|
|
|
278
281
|
test("markWorkspaceTopLevelDirty sets dirty flag", () => {
|
|
279
|
-
|
|
282
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
280
283
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
281
284
|
|
|
282
285
|
conversation.markWorkspaceTopLevelDirty();
|
|
@@ -284,10 +287,10 @@ describe("Conversation workspace cache state", () => {
|
|
|
284
287
|
});
|
|
285
288
|
|
|
286
289
|
test("refresh after marking dirty produces fresh context", () => {
|
|
287
|
-
|
|
290
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
288
291
|
|
|
289
292
|
conversation.markWorkspaceTopLevelDirty();
|
|
290
|
-
|
|
293
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
291
294
|
|
|
292
295
|
expect(conversation.getWorkspaceTopLevelContext()).not.toBeNull();
|
|
293
296
|
expect(conversation.getWorkspaceTopLevelContext()!).toContain(
|
|
@@ -299,7 +302,7 @@ describe("Conversation workspace cache state", () => {
|
|
|
299
302
|
test("renders client-reported host env when set on the conversation", () => {
|
|
300
303
|
conversation.hostHomeDir = "/Users/alice";
|
|
301
304
|
conversation.hostUsername = "alice";
|
|
302
|
-
|
|
305
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
303
306
|
|
|
304
307
|
const block = conversation.getWorkspaceTopLevelContext();
|
|
305
308
|
expect(block).not.toBeNull();
|
|
@@ -309,7 +312,7 @@ describe("Conversation workspace cache state", () => {
|
|
|
309
312
|
|
|
310
313
|
test("falls back to daemon os info when client host env is absent", async () => {
|
|
311
314
|
const { homedir, userInfo } = await import("node:os");
|
|
312
|
-
|
|
315
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
313
316
|
|
|
314
317
|
const block = conversation.getWorkspaceTopLevelContext();
|
|
315
318
|
expect(block).not.toBeNull();
|
|
@@ -320,7 +323,7 @@ describe("Conversation workspace cache state", () => {
|
|
|
320
323
|
test("re-renders with updated host env after marking dirty", () => {
|
|
321
324
|
conversation.hostHomeDir = "/Users/alice";
|
|
322
325
|
conversation.hostUsername = "alice";
|
|
323
|
-
|
|
326
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
324
327
|
expect(conversation.getWorkspaceTopLevelContext()!).toContain(
|
|
325
328
|
"Host home directory: /Users/alice",
|
|
326
329
|
);
|
|
@@ -328,7 +331,7 @@ describe("Conversation workspace cache state", () => {
|
|
|
328
331
|
conversation.hostHomeDir = "/Users/bob";
|
|
329
332
|
conversation.hostUsername = "bob";
|
|
330
333
|
conversation.markWorkspaceTopLevelDirty();
|
|
331
|
-
|
|
334
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
332
335
|
|
|
333
336
|
const block = conversation.getWorkspaceTopLevelContext();
|
|
334
337
|
expect(block).not.toBeNull();
|
|
@@ -344,7 +347,7 @@ describe("Conversation workspace cache state", () => {
|
|
|
344
347
|
// Simulate a macOS turn populating host env.
|
|
345
348
|
conversation.hostHomeDir = "/Users/alice";
|
|
346
349
|
conversation.hostUsername = "alice";
|
|
347
|
-
|
|
350
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
348
351
|
expect(conversation.getWorkspaceTopLevelContext()!).toContain(
|
|
349
352
|
"Host home directory: /Users/alice",
|
|
350
353
|
);
|
|
@@ -355,7 +358,7 @@ describe("Conversation workspace cache state", () => {
|
|
|
355
358
|
conversation.hostHomeDir = undefined;
|
|
356
359
|
conversation.hostUsername = undefined;
|
|
357
360
|
conversation.markWorkspaceTopLevelDirty();
|
|
358
|
-
|
|
361
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
359
362
|
|
|
360
363
|
const block = conversation.getWorkspaceTopLevelContext();
|
|
361
364
|
expect(block).not.toBeNull();
|
|
@@ -381,7 +384,7 @@ describe("Conversation workspace cache state", () => {
|
|
|
381
384
|
expect(conversation.hostUsername).toBe("alice");
|
|
382
385
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(true);
|
|
383
386
|
|
|
384
|
-
|
|
387
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
385
388
|
const block = conversation.getWorkspaceTopLevelContext();
|
|
386
389
|
expect(block!).toContain("Host home directory: /Users/alice");
|
|
387
390
|
expect(block!).toContain("Host username: alice");
|
|
@@ -455,7 +458,7 @@ describe("Conversation workspace cache state", () => {
|
|
|
455
458
|
hostUsername: "alice",
|
|
456
459
|
});
|
|
457
460
|
// Render once so the dirty flag clears.
|
|
458
|
-
|
|
461
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
459
462
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
460
463
|
|
|
461
464
|
// Re-apply the same values — dirty flag should remain false so we don't
|
|
@@ -476,7 +479,7 @@ describe("Conversation workspace cache state", () => {
|
|
|
476
479
|
hostHomeDir: "/Users/alice",
|
|
477
480
|
hostUsername: "alice",
|
|
478
481
|
});
|
|
479
|
-
|
|
482
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
480
483
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
481
484
|
|
|
482
485
|
// New values — should mark dirty so the next render picks them up.
|
|
@@ -500,7 +503,7 @@ describe("Conversation workspace cache state", () => {
|
|
|
500
503
|
|
|
501
504
|
try {
|
|
502
505
|
const tempConversation = makeConversation(workspaceRoot);
|
|
503
|
-
|
|
506
|
+
refreshWorkspaceTopLevelContextIfNeeded(tempConversation);
|
|
504
507
|
|
|
505
508
|
expect(tempConversation.getWorkspaceTopLevelContext()!).toContain(
|
|
506
509
|
`Current conversation attachments: conversations/${legacyDirName}/attachments/`,
|
|
@@ -144,7 +144,7 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
144
144
|
getMessageById: () => null,
|
|
145
145
|
getLastUserTimestampBefore: () => 0,
|
|
146
146
|
setLastNotifiedInferenceProfile: () => {},
|
|
147
|
-
|
|
147
|
+
resolveOverrideProfile: () => undefined,
|
|
148
148
|
updateMessageMetadata: () => {},
|
|
149
149
|
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
150
150
|
updateMessageContent: mock(() => {}),
|
|
@@ -181,8 +181,9 @@ mock.module("../memory/query-builder.js", () => ({
|
|
|
181
181
|
mock.module("../memory/retrieval-budget.js", () => ({
|
|
182
182
|
computeRecallBudget: () => 0,
|
|
183
183
|
}));
|
|
184
|
-
mock.module("../
|
|
184
|
+
mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
|
|
185
185
|
ContextWindowManager: class {
|
|
186
|
+
updateConfig() {}
|
|
186
187
|
shouldCompact() {
|
|
187
188
|
return { needed: false, estimatedTokens: 0 };
|
|
188
189
|
}
|
|
@@ -244,10 +245,11 @@ mock.module("../agent/loop.js", () => ({
|
|
|
244
245
|
getActiveModel() {
|
|
245
246
|
return undefined;
|
|
246
247
|
}
|
|
247
|
-
async run(
|
|
248
|
-
messages: Message[]
|
|
249
|
-
onEvent: (event: AgentEvent) => void
|
|
250
|
-
): Promise<AgentLoopRunResult> {
|
|
248
|
+
async run(options: {
|
|
249
|
+
messages: Message[];
|
|
250
|
+
onEvent: (event: AgentEvent) => void;
|
|
251
|
+
}): Promise<AgentLoopRunResult> {
|
|
252
|
+
const { messages, onEvent } = options;
|
|
251
253
|
// Prime the assistant row anchor — production code emits this from
|
|
252
254
|
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
253
255
|
await onEvent({ type: "llm_call_started" });
|
|
@@ -295,6 +297,14 @@ mock.module("../memory/canonical-guardian-store.js", () => ({
|
|
|
295
297
|
}));
|
|
296
298
|
|
|
297
299
|
import { Conversation } from "../daemon/conversation.js";
|
|
300
|
+
import {
|
|
301
|
+
clearConversations,
|
|
302
|
+
findConversation,
|
|
303
|
+
removeSubagentConversation,
|
|
304
|
+
setConversation,
|
|
305
|
+
setSubagentConversation,
|
|
306
|
+
} from "../daemon/conversation-registry.js";
|
|
307
|
+
import { resolveWorkspaceTopLevelContext } from "../daemon/conversation-workspace.js";
|
|
298
308
|
import { resetPluginRegistryAndRegisterDefaults } from "../plugins/defaults/index.js";
|
|
299
309
|
|
|
300
310
|
function makeConversation(): Conversation {
|
|
@@ -309,7 +319,7 @@ function makeConversation(): Conversation {
|
|
|
309
319
|
};
|
|
310
320
|
},
|
|
311
321
|
};
|
|
312
|
-
|
|
322
|
+
const conversation = new Conversation(
|
|
313
323
|
"conv-1",
|
|
314
324
|
provider,
|
|
315
325
|
"system prompt",
|
|
@@ -317,6 +327,10 @@ function makeConversation(): Conversation {
|
|
|
317
327
|
"/tmp",
|
|
318
328
|
{ maxTokens: 4096 },
|
|
319
329
|
);
|
|
330
|
+
// Mirror production: top-level conversations are registered in the store, so
|
|
331
|
+
// the workspace injector can resolve them by id via the conversation registry.
|
|
332
|
+
setConversation(conversation.conversationId, conversation);
|
|
333
|
+
return conversation;
|
|
320
334
|
}
|
|
321
335
|
|
|
322
336
|
function messageText(message: Message): string {
|
|
@@ -335,6 +349,7 @@ describe("Conversation workspace injection", () => {
|
|
|
335
349
|
runCalls = [];
|
|
336
350
|
agentLoopScript = () => {};
|
|
337
351
|
scanCallCount = 0;
|
|
352
|
+
clearConversations();
|
|
338
353
|
resetPluginRegistryAndRegisterDefaults();
|
|
339
354
|
});
|
|
340
355
|
|
|
@@ -417,11 +432,58 @@ describe("Conversation workspace injection", () => {
|
|
|
417
432
|
});
|
|
418
433
|
});
|
|
419
434
|
|
|
435
|
+
describe("Conversation workspace injection — subagents", () => {
|
|
436
|
+
beforeEach(() => {
|
|
437
|
+
scanCallCount = 0;
|
|
438
|
+
clearConversations();
|
|
439
|
+
resetPluginRegistryAndRegisterDefaults();
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
test("workspace context resolves for subagent conversations not in the store", () => {
|
|
443
|
+
const provider = {
|
|
444
|
+
name: "mock",
|
|
445
|
+
async sendMessage(): Promise<ProviderResponse> {
|
|
446
|
+
return {
|
|
447
|
+
content: [],
|
|
448
|
+
model: "mock",
|
|
449
|
+
usage: { inputTokens: 0, outputTokens: 0 },
|
|
450
|
+
stopReason: "end_turn",
|
|
451
|
+
};
|
|
452
|
+
},
|
|
453
|
+
};
|
|
454
|
+
const subagentId = "subagent-conv-1";
|
|
455
|
+
const conversation = new Conversation(
|
|
456
|
+
subagentId,
|
|
457
|
+
provider,
|
|
458
|
+
"system prompt",
|
|
459
|
+
() => {},
|
|
460
|
+
"/tmp",
|
|
461
|
+
{ maxTokens: 4096 },
|
|
462
|
+
);
|
|
463
|
+
setSubagentConversation(subagentId, conversation);
|
|
464
|
+
|
|
465
|
+
try {
|
|
466
|
+
// Subagents live only in the manager's index, never in the
|
|
467
|
+
// eviction-managed store, so the store lookup must not see them.
|
|
468
|
+
expect(findConversation(subagentId)).toBeUndefined();
|
|
469
|
+
|
|
470
|
+
// The per-conversation workspace lookup still resolves them, so subagent
|
|
471
|
+
// turns retain workspace grounding.
|
|
472
|
+
const context = resolveWorkspaceTopLevelContext(subagentId);
|
|
473
|
+
expect(context).not.toBeNull();
|
|
474
|
+
expect(context).toContain("Root: /tmp");
|
|
475
|
+
} finally {
|
|
476
|
+
removeSubagentConversation(subagentId, conversation);
|
|
477
|
+
}
|
|
478
|
+
});
|
|
479
|
+
});
|
|
480
|
+
|
|
420
481
|
describe("Conversation workspace dirty-refresh E2E", () => {
|
|
421
482
|
beforeEach(() => {
|
|
422
483
|
runCalls = [];
|
|
423
484
|
agentLoopScript = () => {};
|
|
424
485
|
scanCallCount = 0;
|
|
486
|
+
clearConversations();
|
|
425
487
|
resetPluginRegistryAndRegisterDefaults();
|
|
426
488
|
});
|
|
427
489
|
|
|
@@ -141,7 +141,7 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
141
141
|
getMessageById: () => null,
|
|
142
142
|
getLastUserTimestampBefore: () => 0,
|
|
143
143
|
setLastNotifiedInferenceProfile: () => {},
|
|
144
|
-
|
|
144
|
+
resolveOverrideProfile: () => undefined,
|
|
145
145
|
updateMessageMetadata: () => {},
|
|
146
146
|
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
147
147
|
updateMessageContent: mock(() => {}),
|
|
@@ -178,8 +178,9 @@ mock.module("../memory/query-builder.js", () => ({
|
|
|
178
178
|
mock.module("../memory/retrieval-budget.js", () => ({
|
|
179
179
|
computeRecallBudget: () => 0,
|
|
180
180
|
}));
|
|
181
|
-
mock.module("../
|
|
181
|
+
mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
|
|
182
182
|
ContextWindowManager: class {
|
|
183
|
+
updateConfig() {}
|
|
183
184
|
shouldCompact() {
|
|
184
185
|
return { needed: false, estimatedTokens: 0 };
|
|
185
186
|
}
|
|
@@ -228,10 +229,11 @@ mock.module("../agent/loop.js", () => ({
|
|
|
228
229
|
getActiveModel() {
|
|
229
230
|
return undefined;
|
|
230
231
|
}
|
|
231
|
-
async run(
|
|
232
|
-
messages: Message[]
|
|
233
|
-
onEvent: (event: AgentEvent) => void
|
|
234
|
-
): Promise<Message[]> {
|
|
232
|
+
async run(options: {
|
|
233
|
+
messages: Message[];
|
|
234
|
+
onEvent: (event: AgentEvent) => void;
|
|
235
|
+
}): Promise<Message[]> {
|
|
236
|
+
const { messages, onEvent } = options;
|
|
235
237
|
// Prime the assistant row anchor — production code emits this from
|
|
236
238
|
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
237
239
|
await onEvent({ type: "llm_call_started" });
|
|
@@ -273,6 +275,7 @@ mock.module("../memory/canonical-guardian-store.js", () => ({
|
|
|
273
275
|
}));
|
|
274
276
|
|
|
275
277
|
import { Conversation } from "../daemon/conversation.js";
|
|
278
|
+
import { refreshWorkspaceTopLevelContextIfNeeded } from "../daemon/conversation-workspace.js";
|
|
276
279
|
|
|
277
280
|
function makeConversation(): Conversation {
|
|
278
281
|
const provider = {
|
|
@@ -310,7 +313,7 @@ describe("Conversation workspace dirty on file mutations", () => {
|
|
|
310
313
|
await conversation.loadFromDb();
|
|
311
314
|
|
|
312
315
|
// Prime the cache so dirty=false
|
|
313
|
-
|
|
316
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
314
317
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
315
318
|
|
|
316
319
|
agentLoopScript = (onEvent) => {
|
|
@@ -339,7 +342,7 @@ describe("Conversation workspace dirty on file mutations", () => {
|
|
|
339
342
|
const conversation = makeConversation();
|
|
340
343
|
await conversation.loadFromDb();
|
|
341
344
|
|
|
342
|
-
|
|
345
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
343
346
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
344
347
|
|
|
345
348
|
agentLoopScript = (onEvent) => {
|
|
@@ -370,7 +373,7 @@ describe("Conversation workspace dirty on file mutations", () => {
|
|
|
370
373
|
const conversation = makeConversation();
|
|
371
374
|
await conversation.loadFromDb();
|
|
372
375
|
|
|
373
|
-
|
|
376
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
374
377
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
375
378
|
|
|
376
379
|
agentLoopScript = (onEvent) => {
|
|
@@ -399,7 +402,7 @@ describe("Conversation workspace dirty on file mutations", () => {
|
|
|
399
402
|
const conversation = makeConversation();
|
|
400
403
|
await conversation.loadFromDb();
|
|
401
404
|
|
|
402
|
-
|
|
405
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
403
406
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
404
407
|
|
|
405
408
|
agentLoopScript = (onEvent) => {
|
|
@@ -428,7 +431,7 @@ describe("Conversation workspace dirty on file mutations", () => {
|
|
|
428
431
|
const conversation = makeConversation();
|
|
429
432
|
await conversation.loadFromDb();
|
|
430
433
|
|
|
431
|
-
|
|
434
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
432
435
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
433
436
|
|
|
434
437
|
agentLoopScript = (onEvent) => {
|
|
@@ -457,7 +460,7 @@ describe("Conversation workspace dirty on file mutations", () => {
|
|
|
457
460
|
const conversation = makeConversation();
|
|
458
461
|
await conversation.loadFromDb();
|
|
459
462
|
|
|
460
|
-
|
|
463
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
461
464
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
462
465
|
|
|
463
466
|
agentLoopScript = (onEvent) => {
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests that the conversations import route never persists non-renderable
|
|
3
|
+
* roles. The messages store is UI-facing (`ConversationMessage`), so an
|
|
4
|
+
* imported export carrying agent-context `system` rows must land only its
|
|
5
|
+
* `user`/`assistant` turns — the `system` rows are dropped, not persisted.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
9
|
+
|
|
10
|
+
mock.module("../util/logger.js", () => ({
|
|
11
|
+
getLogger: () =>
|
|
12
|
+
new Proxy({} as Record<string, unknown>, {
|
|
13
|
+
get: () => () => {},
|
|
14
|
+
}),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
mock.module("../config/loader.js", () => ({
|
|
18
|
+
getConfig: () => ({
|
|
19
|
+
ui: {},
|
|
20
|
+
model: "test",
|
|
21
|
+
provider: "test",
|
|
22
|
+
memory: { enabled: false },
|
|
23
|
+
rateLimit: { maxRequestsPerMinute: 0 },
|
|
24
|
+
}),
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
mock.module("../memory/indexer.js", () => ({
|
|
28
|
+
indexMessageNow: async () => {},
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
import { getMessages } from "../memory/conversation-crud.js";
|
|
32
|
+
import { getDb } from "../memory/db-connection.js";
|
|
33
|
+
import { initializeDb } from "../memory/db-init.js";
|
|
34
|
+
import { conversations, messages } from "../memory/schema.js";
|
|
35
|
+
import { ROUTES } from "../runtime/routes/conversations-import-routes.js";
|
|
36
|
+
import type { RouteHandlerArgs } from "../runtime/routes/types.js";
|
|
37
|
+
|
|
38
|
+
initializeDb();
|
|
39
|
+
|
|
40
|
+
function resetTables() {
|
|
41
|
+
const db = getDb();
|
|
42
|
+
db.run("DELETE FROM message_attachments");
|
|
43
|
+
db.run("DELETE FROM attachments");
|
|
44
|
+
db.run("DELETE FROM messages");
|
|
45
|
+
db.run("DELETE FROM conversation_keys");
|
|
46
|
+
db.run("DELETE FROM conversations");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const importHandler = ROUTES.find(
|
|
50
|
+
(r) => r.operationId === "conversations_import",
|
|
51
|
+
)!.handler;
|
|
52
|
+
|
|
53
|
+
describe("conversations import system-row filtering", () => {
|
|
54
|
+
beforeEach(resetTables);
|
|
55
|
+
|
|
56
|
+
test("imports renderable turns but drops system rows", async () => {
|
|
57
|
+
// GIVEN an export whose conversation sandwiches a system row between two
|
|
58
|
+
// renderable turns (e.g. agent-context scaffolding an export carried)
|
|
59
|
+
const body = {
|
|
60
|
+
conversations: [
|
|
61
|
+
{
|
|
62
|
+
sourceKey: "src-1",
|
|
63
|
+
title: "Imported chat",
|
|
64
|
+
messages: [
|
|
65
|
+
{ role: "user", content: "first visible" },
|
|
66
|
+
{ role: "system", content: "agent-context scaffolding" },
|
|
67
|
+
{ role: "assistant", content: "second visible" },
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// WHEN the conversation is imported
|
|
74
|
+
const result = (await importHandler({
|
|
75
|
+
body,
|
|
76
|
+
} as unknown as RouteHandlerArgs)) as {
|
|
77
|
+
ok: boolean;
|
|
78
|
+
imported: number;
|
|
79
|
+
messages: number;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// THEN the import succeeds and only counts the renderable turns
|
|
83
|
+
expect(result.ok).toBe(true);
|
|
84
|
+
expect(result.imported).toBe(1);
|
|
85
|
+
expect(result.messages).toBe(2);
|
|
86
|
+
|
|
87
|
+
// AND the persisted rows are exactly the user/assistant turns, never the
|
|
88
|
+
// system scaffolding
|
|
89
|
+
const db = getDb();
|
|
90
|
+
const conv = db.select().from(conversations).all()[0];
|
|
91
|
+
const rows = getMessages(conv.id);
|
|
92
|
+
expect(rows.map((m) => m.role)).toEqual(["user", "assistant"]);
|
|
93
|
+
expect(
|
|
94
|
+
db
|
|
95
|
+
.select()
|
|
96
|
+
.from(messages)
|
|
97
|
+
.all()
|
|
98
|
+
.some((m) => m.role === "system"),
|
|
99
|
+
).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { computeCorrectedOverflowTarget } from "../plugins/defaults/compaction/corrected-target.js";
|
|
4
|
+
|
|
5
|
+
describe("computeCorrectedOverflowTarget", () => {
|
|
6
|
+
test("lowers the target proportionally when the estimator under-counted", () => {
|
|
7
|
+
/**
|
|
8
|
+
* When the provider's actual token count exceeds the estimator's count,
|
|
9
|
+
* the target is scaled down by the estimation-error ratio so a re-estimate
|
|
10
|
+
* of the reduced history lands under the provider's real ceiling.
|
|
11
|
+
*/
|
|
12
|
+
// GIVEN a preflight budget and an estimator that under-counted (185k vs 242k actual)
|
|
13
|
+
const preflightBudget = 162_000;
|
|
14
|
+
const actualTokens = 242_000;
|
|
15
|
+
const estimatedTokens = 185_000;
|
|
16
|
+
|
|
17
|
+
// WHEN computing the corrected target
|
|
18
|
+
const result = computeCorrectedOverflowTarget({
|
|
19
|
+
preflightBudget,
|
|
20
|
+
actualTokens,
|
|
21
|
+
estimatedTokens,
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// THEN the target is the budget divided by the error ratio
|
|
25
|
+
const expectedRatio = actualTokens / estimatedTokens;
|
|
26
|
+
expect(result.targetTokens).toBe(
|
|
27
|
+
Math.floor(preflightBudget / expectedRatio),
|
|
28
|
+
);
|
|
29
|
+
// AND the ratio is surfaced for logging
|
|
30
|
+
expect(result.estimationErrorRatio).toBeCloseTo(expectedRatio, 10);
|
|
31
|
+
// AND the corrected target is below the nominal budget
|
|
32
|
+
expect(result.targetTokens).toBeLessThan(preflightBudget);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("returns the preflight budget unchanged when the estimator did not under-count", () => {
|
|
36
|
+
/**
|
|
37
|
+
* If the estimator matched or over-counted the provider, no correction is
|
|
38
|
+
* applied and the nominal preflight budget is the target.
|
|
39
|
+
*/
|
|
40
|
+
// GIVEN an actual count at or below the estimate
|
|
41
|
+
const preflightBudget = 162_000;
|
|
42
|
+
|
|
43
|
+
// WHEN computing the corrected target
|
|
44
|
+
const result = computeCorrectedOverflowTarget({
|
|
45
|
+
preflightBudget,
|
|
46
|
+
actualTokens: 180_000,
|
|
47
|
+
estimatedTokens: 185_000,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// THEN the target is the unchanged preflight budget and no ratio is reported
|
|
51
|
+
expect(result.targetTokens).toBe(preflightBudget);
|
|
52
|
+
expect(result.estimationErrorRatio).toBeNull();
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("falls back to the preflight budget when the actual count is unavailable", () => {
|
|
56
|
+
/**
|
|
57
|
+
* Proxy-rewrapped or unparseable rejections yield a null actual count; the
|
|
58
|
+
* correction is skipped rather than guessed.
|
|
59
|
+
*/
|
|
60
|
+
// GIVEN a null actual token count
|
|
61
|
+
const preflightBudget = 162_000;
|
|
62
|
+
|
|
63
|
+
// WHEN computing the corrected target
|
|
64
|
+
const result = computeCorrectedOverflowTarget({
|
|
65
|
+
preflightBudget,
|
|
66
|
+
actualTokens: null,
|
|
67
|
+
estimatedTokens: 185_000,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// THEN the nominal budget is used with no ratio
|
|
71
|
+
expect(result.targetTokens).toBe(preflightBudget);
|
|
72
|
+
expect(result.estimationErrorRatio).toBeNull();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
test("falls back to the preflight budget when the estimate is zero", () => {
|
|
76
|
+
/**
|
|
77
|
+
* A zero estimate would make the ratio undefined; guard against it.
|
|
78
|
+
*/
|
|
79
|
+
// GIVEN a zero estimate
|
|
80
|
+
const preflightBudget = 162_000;
|
|
81
|
+
|
|
82
|
+
// WHEN computing the corrected target
|
|
83
|
+
const result = computeCorrectedOverflowTarget({
|
|
84
|
+
preflightBudget,
|
|
85
|
+
actualTokens: 242_000,
|
|
86
|
+
estimatedTokens: 0,
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
// THEN the nominal budget is used with no ratio
|
|
90
|
+
expect(result.targetTokens).toBe(preflightBudget);
|
|
91
|
+
expect(result.estimationErrorRatio).toBeNull();
|
|
92
|
+
});
|
|
93
|
+
});
|
|
@@ -165,10 +165,11 @@ describe("CES flags do not affect unrelated flags", () => {
|
|
|
165
165
|
setOverridesForTesting(overrides);
|
|
166
166
|
const config = makeConfig();
|
|
167
167
|
|
|
168
|
-
//
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
168
|
+
// Flags with defaultEnabled: true in the registry should stay true
|
|
169
|
+
// regardless of CES overrides.
|
|
170
|
+
expect(
|
|
171
|
+
isAssistantFeatureFlagEnabled("platform-features-in-local-mode", config),
|
|
172
|
+
).toBe(true);
|
|
172
173
|
});
|
|
173
174
|
|
|
174
175
|
test("enabling all CES flags does not change unrelated fail-closed flags", () => {
|
|
@@ -231,6 +231,7 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
|
|
|
231
231
|
"ipc/skill-routes/providers.ts", // host.providers.secureKeys.getProviderKey IPC route (out-of-process SkillHost companion)
|
|
232
232
|
"daemon/external-plugins-bootstrap.ts", // reads credentials at plugin init (manifest.requiresCredential) via the CES-mediated getSecureKeyAsync path
|
|
233
233
|
"plugins/external-api.ts", // globalThis runtime bridge that exposes getSecureKeyAsync to dynamically-imported workspace plugins (compiled-binary plugin loading)
|
|
234
|
+
"plugin-api/index.ts", // public @vellumai/plugin-api surface re-exports getSecureKeyAsync to workspace plugins via the boot-time shim (same exposure as the external-api bridge, routed through the typed public package)
|
|
234
235
|
"inbound/platform-callback-registration.ts", // managed credential lookup for platform base URL, assistant ID, and API key
|
|
235
236
|
"tts/providers/elevenlabs-provider.ts", // ElevenLabs TTS API key lookup
|
|
236
237
|
"tts/providers/deepgram-provider.ts", // Deepgram TTS API key lookup
|
|
@@ -69,6 +69,7 @@ type SurfaceConversationContext =
|
|
|
69
69
|
function buildMockContext(
|
|
70
70
|
hostCuProxy?: InstanceType<typeof HostCuProxy>,
|
|
71
71
|
trustGuardianPrincipalId: string | null = DEFAULT_PRINCIPAL,
|
|
72
|
+
actorPrincipalId: string | null = trustGuardianPrincipalId,
|
|
72
73
|
): SurfaceConversationContext {
|
|
73
74
|
return {
|
|
74
75
|
conversationId: "test-session",
|
|
@@ -80,6 +81,16 @@ function buildMockContext(
|
|
|
80
81
|
guardianPrincipalId: trustGuardianPrincipalId,
|
|
81
82
|
}
|
|
82
83
|
: undefined,
|
|
84
|
+
authContext:
|
|
85
|
+
actorPrincipalId != null
|
|
86
|
+
? ({ actorPrincipalId } as SurfaceConversationContext["authContext"])
|
|
87
|
+
: undefined,
|
|
88
|
+
currentTurnAuthContext:
|
|
89
|
+
actorPrincipalId != null
|
|
90
|
+
? ({
|
|
91
|
+
actorPrincipalId,
|
|
92
|
+
} as SurfaceConversationContext["currentTurnAuthContext"])
|
|
93
|
+
: undefined,
|
|
83
94
|
traceEmitter: { emit: () => {} },
|
|
84
95
|
sendToClient: () => {},
|
|
85
96
|
pendingSurfaceActions: new Map(),
|
|
@@ -665,6 +676,31 @@ describe("surfaceProxyResolver — CU tool routing", () => {
|
|
|
665
676
|
expect(sentMessages).toHaveLength(0);
|
|
666
677
|
});
|
|
667
678
|
|
|
679
|
+
test("rejects untargeted CU dispatch to owner client when current requester is a trusted contact", async () => {
|
|
680
|
+
sentMessages.length = 0;
|
|
681
|
+
mockHasClient = true;
|
|
682
|
+
mockCuClients = [
|
|
683
|
+
{
|
|
684
|
+
clientId: "owner-cu-client",
|
|
685
|
+
capabilities: ["host_cu"],
|
|
686
|
+
actorPrincipalId: "owner-user",
|
|
687
|
+
},
|
|
688
|
+
];
|
|
689
|
+
proxy = new HostCuProxy();
|
|
690
|
+
const ctx = buildMockContext(proxy, "owner-user", "trusted-contact-user");
|
|
691
|
+
|
|
692
|
+
const result = await surfaceProxyResolver(ctx, "computer_use_click", {
|
|
693
|
+
element_id: 1,
|
|
694
|
+
reasoning: "click",
|
|
695
|
+
});
|
|
696
|
+
|
|
697
|
+
expect(result.isError).toBe(true);
|
|
698
|
+
expect(result.content).toContain("current actor");
|
|
699
|
+
expect(proxy.stepCount).toBe(0);
|
|
700
|
+
expect(proxy.actionHistory).toHaveLength(0);
|
|
701
|
+
expect(sentMessages).toHaveLength(0);
|
|
702
|
+
});
|
|
703
|
+
|
|
668
704
|
test("auto-resolves to the unique same-user CU client when cross-user clients are present", async () => {
|
|
669
705
|
// Regression: previously the dispatch counted only same-user clients
|
|
670
706
|
// for the multi-client guard, so 1 same-user + 1 cross-user passed the
|