@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
|
@@ -5,6 +5,7 @@ import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
|
5
5
|
|
|
6
6
|
import { getSqliteFrom } from "../memory/db-connection.js";
|
|
7
7
|
import { migrate230AcpSessionHistory } from "../memory/migrations/230-acp-session-history.js";
|
|
8
|
+
import { migrateAcpSessionHistoryCwd } from "../memory/migrations/272-acp-session-history-cwd.js";
|
|
8
9
|
import * as schema from "../memory/schema.js";
|
|
9
10
|
|
|
10
11
|
function createTestDb() {
|
|
@@ -282,3 +283,103 @@ describe("acp_session_history migration", () => {
|
|
|
282
283
|
expect(row?.id).toBe("history-rerun");
|
|
283
284
|
});
|
|
284
285
|
});
|
|
286
|
+
|
|
287
|
+
describe("acp_session_history cwd migration (272)", () => {
|
|
288
|
+
function readColumns(raw: Database): Map<string, ColumnRow> {
|
|
289
|
+
const columns = raw
|
|
290
|
+
.query(`PRAGMA table_info(acp_session_history)`)
|
|
291
|
+
.all() as ColumnRow[];
|
|
292
|
+
return new Map(columns.map((c) => [c.name, c]));
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
test("adds a nullable cwd TEXT column to an upgraded table", () => {
|
|
296
|
+
const db = createTestDb();
|
|
297
|
+
const raw = getSqliteFrom(db);
|
|
298
|
+
bootstrapCheckpointsTable(raw);
|
|
299
|
+
|
|
300
|
+
migrate230AcpSessionHistory(db);
|
|
301
|
+
expect(readColumns(raw).has("cwd")).toBe(false);
|
|
302
|
+
|
|
303
|
+
migrateAcpSessionHistoryCwd(db);
|
|
304
|
+
|
|
305
|
+
const cwd = readColumns(raw).get("cwd");
|
|
306
|
+
expect(cwd).toBeDefined();
|
|
307
|
+
expect(cwd?.type).toBe("TEXT");
|
|
308
|
+
expect(cwd?.notnull).toBe(0);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
test("re-running is a no-op, including with a cleared checkpoint", () => {
|
|
312
|
+
const db = createTestDb();
|
|
313
|
+
const raw = getSqliteFrom(db);
|
|
314
|
+
bootstrapCheckpointsTable(raw);
|
|
315
|
+
|
|
316
|
+
migrate230AcpSessionHistory(db);
|
|
317
|
+
migrateAcpSessionHistoryCwd(db);
|
|
318
|
+
|
|
319
|
+
// Second run short-circuits on the completed checkpoint.
|
|
320
|
+
expect(() => migrateAcpSessionHistoryCwd(db)).not.toThrow();
|
|
321
|
+
|
|
322
|
+
// Even with the checkpoint cleared (simulating crash recovery), the
|
|
323
|
+
// column-existence guard makes the ALTER a no-op.
|
|
324
|
+
raw
|
|
325
|
+
.query(`DELETE FROM memory_checkpoints WHERE key = ?`)
|
|
326
|
+
.run("migration_acp_session_history_cwd_v1");
|
|
327
|
+
expect(() => migrateAcpSessionHistoryCwd(db)).not.toThrow();
|
|
328
|
+
|
|
329
|
+
const columns = raw
|
|
330
|
+
.query(`PRAGMA table_info(acp_session_history)`)
|
|
331
|
+
.all() as ColumnRow[];
|
|
332
|
+
expect(columns.filter((c) => c.name === "cwd")).toHaveLength(1);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
test("legacy rows read back with cwd null; new rows round-trip cwd", () => {
|
|
336
|
+
const db = createTestDb();
|
|
337
|
+
const raw = getSqliteFrom(db);
|
|
338
|
+
bootstrapCheckpointsTable(raw);
|
|
339
|
+
|
|
340
|
+
migrate230AcpSessionHistory(db);
|
|
341
|
+
|
|
342
|
+
// Legacy row inserted before the cwd column existed.
|
|
343
|
+
raw
|
|
344
|
+
.query(
|
|
345
|
+
/*sql*/ `
|
|
346
|
+
INSERT INTO acp_session_history (
|
|
347
|
+
id, agent_id, acp_session_id, parent_conversation_id,
|
|
348
|
+
started_at, status
|
|
349
|
+
) VALUES (?, ?, ?, ?, ?, ?)
|
|
350
|
+
`,
|
|
351
|
+
)
|
|
352
|
+
.run("legacy-1", "agent-old", "acp-old", "conv-old", 100, "completed");
|
|
353
|
+
|
|
354
|
+
migrateAcpSessionHistoryCwd(db);
|
|
355
|
+
|
|
356
|
+
const legacy = raw
|
|
357
|
+
.query(`SELECT cwd FROM acp_session_history WHERE id = 'legacy-1'`)
|
|
358
|
+
.get() as { cwd: string | null } | null;
|
|
359
|
+
expect(legacy?.cwd).toBeNull();
|
|
360
|
+
|
|
361
|
+
raw
|
|
362
|
+
.query(
|
|
363
|
+
/*sql*/ `
|
|
364
|
+
INSERT INTO acp_session_history (
|
|
365
|
+
id, agent_id, acp_session_id, parent_conversation_id,
|
|
366
|
+
started_at, status, cwd
|
|
367
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
368
|
+
`,
|
|
369
|
+
)
|
|
370
|
+
.run(
|
|
371
|
+
"new-1",
|
|
372
|
+
"agent-new",
|
|
373
|
+
"acp-new",
|
|
374
|
+
"conv-new",
|
|
375
|
+
200,
|
|
376
|
+
"completed",
|
|
377
|
+
"/Users/me/project",
|
|
378
|
+
);
|
|
379
|
+
|
|
380
|
+
const fresh = raw
|
|
381
|
+
.query(`SELECT cwd FROM acp_session_history WHERE id = 'new-1'`)
|
|
382
|
+
.get() as { cwd: string | null } | null;
|
|
383
|
+
expect(fresh?.cwd).toBe("/Users/me/project");
|
|
384
|
+
});
|
|
385
|
+
});
|
|
@@ -94,6 +94,37 @@ describe("ui_show dynamic_page app substitute guard", () => {
|
|
|
94
94
|
expect(proxied).toBe(false);
|
|
95
95
|
});
|
|
96
96
|
|
|
97
|
+
test("rejects dynamic_page with a clean title but substantial interactive html", async () => {
|
|
98
|
+
let proxied = false;
|
|
99
|
+
|
|
100
|
+
const result = await uiShowTool.execute(
|
|
101
|
+
{
|
|
102
|
+
surface_type: "dynamic_page",
|
|
103
|
+
title: "Labor Market Stats",
|
|
104
|
+
data: {
|
|
105
|
+
html:
|
|
106
|
+
"<div id='root'></div><script>" +
|
|
107
|
+
"const data=[/*...*/];".padEnd(2100, "/") +
|
|
108
|
+
"new Chart(document.getElementById('root'), {});</script>",
|
|
109
|
+
preview: { title: "Labor Market Stats" },
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
conversationId: "conversation-123",
|
|
114
|
+
workingDir: "/tmp",
|
|
115
|
+
trustClass: "guardian",
|
|
116
|
+
proxyToolResolver: async () => {
|
|
117
|
+
proxied = true;
|
|
118
|
+
return { content: "proxied", isError: false };
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
expect(result.isError).toBe(true);
|
|
124
|
+
expect(result.content).toContain('skill: "app-builder"');
|
|
125
|
+
expect(proxied).toBe(false);
|
|
126
|
+
});
|
|
127
|
+
|
|
97
128
|
test("allows transient non-app dynamic_page surfaces", async () => {
|
|
98
129
|
let proxied = false;
|
|
99
130
|
|
|
@@ -2,8 +2,7 @@
|
|
|
2
2
|
* Tests for managed proxy Gemini embedding backend selection.
|
|
3
3
|
*
|
|
4
4
|
* Verifies that selectEmbeddingBackend correctly routes through the
|
|
5
|
-
* managed proxy when
|
|
6
|
-
* prerequisites are satisfied.
|
|
5
|
+
* managed proxy when managed proxy prerequisites are satisfied.
|
|
7
6
|
*/
|
|
8
7
|
|
|
9
8
|
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
@@ -42,13 +41,9 @@ mock.module("../security/secure-keys.js", () => ({
|
|
|
42
41
|
},
|
|
43
42
|
}));
|
|
44
43
|
|
|
45
|
-
// Feature flag mock
|
|
46
|
-
const mockFeatureFlags: Record<string, boolean> = {};
|
|
47
|
-
|
|
44
|
+
// Feature flag mock — always returns true (flag is GA'ed)
|
|
48
45
|
mock.module("../config/assistant-feature-flags.js", () => ({
|
|
49
|
-
isAssistantFeatureFlagEnabled: (
|
|
50
|
-
return mockFeatureFlags[key] ?? false;
|
|
51
|
-
},
|
|
46
|
+
isAssistantFeatureFlagEnabled: () => true,
|
|
52
47
|
}));
|
|
53
48
|
|
|
54
49
|
import type { AssistantConfig } from "../config/types.js";
|
|
@@ -75,14 +70,6 @@ function disableManagedProxy() {
|
|
|
75
70
|
mockAssistantApiKey = null;
|
|
76
71
|
}
|
|
77
72
|
|
|
78
|
-
function enableFlag() {
|
|
79
|
-
mockFeatureFlags["managed-gemini-embeddings-enabled"] = true;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function disableFlag() {
|
|
83
|
-
mockFeatureFlags["managed-gemini-embeddings-enabled"] = false;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
73
|
function makeConfig(
|
|
87
74
|
overrides: {
|
|
88
75
|
provider?: string;
|
|
@@ -116,7 +103,6 @@ function makeConfig(
|
|
|
116
103
|
|
|
117
104
|
beforeEach(() => {
|
|
118
105
|
disableManagedProxy();
|
|
119
|
-
disableFlag();
|
|
120
106
|
mockProviderKeys = {};
|
|
121
107
|
clearEmbeddingBackendCache();
|
|
122
108
|
});
|
|
@@ -126,9 +112,8 @@ afterEach(() => {
|
|
|
126
112
|
});
|
|
127
113
|
|
|
128
114
|
describe("managed proxy Gemini embedding selection", () => {
|
|
129
|
-
test("selects managed proxy Gemini when
|
|
115
|
+
test("selects managed proxy Gemini when proxy context available", async () => {
|
|
130
116
|
enableManagedProxy();
|
|
131
|
-
enableFlag();
|
|
132
117
|
const config = makeConfig();
|
|
133
118
|
|
|
134
119
|
const { backend, reason } = await selectEmbeddingBackend(config);
|
|
@@ -141,7 +126,6 @@ describe("managed proxy Gemini embedding selection", () => {
|
|
|
141
126
|
|
|
142
127
|
test("managed proxy backend uses default 3072 dimensions when geminiDimensions not set", async () => {
|
|
143
128
|
enableManagedProxy();
|
|
144
|
-
enableFlag();
|
|
145
129
|
const config = makeConfig();
|
|
146
130
|
|
|
147
131
|
const { backend } = await selectEmbeddingBackend(config);
|
|
@@ -155,7 +139,6 @@ describe("managed proxy Gemini embedding selection", () => {
|
|
|
155
139
|
|
|
156
140
|
test("managed proxy backend uses explicit geminiDimensions when set", async () => {
|
|
157
141
|
enableManagedProxy();
|
|
158
|
-
enableFlag();
|
|
159
142
|
const config = makeConfig({ geminiDimensions: 768 });
|
|
160
143
|
|
|
161
144
|
const { backend } = await selectEmbeddingBackend(config);
|
|
@@ -168,7 +151,6 @@ describe("managed proxy Gemini embedding selection", () => {
|
|
|
168
151
|
|
|
169
152
|
test("managed proxy backend uses managedBaseUrl (not direct Google API)", async () => {
|
|
170
153
|
enableManagedProxy();
|
|
171
|
-
enableFlag();
|
|
172
154
|
const config = makeConfig();
|
|
173
155
|
|
|
174
156
|
const { backend } = await selectEmbeddingBackend(config);
|
|
@@ -179,32 +161,19 @@ describe("managed proxy Gemini embedding selection", () => {
|
|
|
179
161
|
expect(managedBaseUrl).toBe(`${PLATFORM_BASE}/v1/runtime-proxy/gemini`);
|
|
180
162
|
});
|
|
181
163
|
|
|
182
|
-
test("falls back to local when flag is disabled (no managed proxy)", async () => {
|
|
183
|
-
enableManagedProxy();
|
|
184
|
-
disableFlag();
|
|
185
|
-
const config = makeConfig();
|
|
186
|
-
|
|
187
|
-
const { backend } = await selectEmbeddingBackend(config);
|
|
188
|
-
|
|
189
|
-
// With auto and no provider keys, falls through to local
|
|
190
|
-
expect(backend).not.toBeNull();
|
|
191
|
-
expect(backend!.provider).toBe("local");
|
|
192
|
-
});
|
|
193
|
-
|
|
194
164
|
test("falls back to local when managed proxy context unavailable", async () => {
|
|
195
165
|
disableManagedProxy();
|
|
196
|
-
enableFlag();
|
|
197
166
|
const config = makeConfig();
|
|
198
167
|
|
|
199
168
|
const { backend } = await selectEmbeddingBackend(config);
|
|
200
169
|
|
|
170
|
+
// With auto and no provider keys or proxy, falls through to local
|
|
201
171
|
expect(backend).not.toBeNull();
|
|
202
172
|
expect(backend!.provider).toBe("local");
|
|
203
173
|
});
|
|
204
174
|
|
|
205
175
|
test("selects managed proxy when provider is explicitly gemini", async () => {
|
|
206
176
|
enableManagedProxy();
|
|
207
|
-
enableFlag();
|
|
208
177
|
const config = makeConfig({ provider: "gemini" });
|
|
209
178
|
|
|
210
179
|
const { backend } = await selectEmbeddingBackend(config);
|
|
@@ -217,7 +186,6 @@ describe("managed proxy Gemini embedding selection", () => {
|
|
|
217
186
|
|
|
218
187
|
test("does not use managed proxy when provider is explicitly local", async () => {
|
|
219
188
|
enableManagedProxy();
|
|
220
|
-
enableFlag();
|
|
221
189
|
const config = makeConfig({ provider: "local" });
|
|
222
190
|
|
|
223
191
|
const { backend } = await selectEmbeddingBackend(config);
|
|
@@ -228,7 +196,6 @@ describe("managed proxy Gemini embedding selection", () => {
|
|
|
228
196
|
|
|
229
197
|
test("does not use managed proxy when provider is explicitly openai", async () => {
|
|
230
198
|
enableManagedProxy();
|
|
231
|
-
enableFlag();
|
|
232
199
|
mockProviderKeys[credentialKey("openai", "api_key")] = "user-openai-key";
|
|
233
200
|
const config = makeConfig({ provider: "openai" });
|
|
234
201
|
|
|
@@ -238,9 +205,8 @@ describe("managed proxy Gemini embedding selection", () => {
|
|
|
238
205
|
expect(backend!.provider).toBe("openai");
|
|
239
206
|
});
|
|
240
207
|
|
|
241
|
-
test("direct Gemini key still works
|
|
208
|
+
test("direct Gemini key still works without managed proxy", async () => {
|
|
242
209
|
disableManagedProxy();
|
|
243
|
-
disableFlag();
|
|
244
210
|
mockProviderKeys[credentialKey("gemini", "api_key")] = "user-gemini-key";
|
|
245
211
|
const config = makeConfig({ provider: "gemini" });
|
|
246
212
|
|
|
@@ -31,7 +31,7 @@ import {
|
|
|
31
31
|
REFUSAL_NUDGE_TEXT,
|
|
32
32
|
} from "../plugins/defaults/empty-response/hooks/stop.js";
|
|
33
33
|
import stop from "../plugins/defaults/empty-response/hooks/stop.js";
|
|
34
|
-
import { defaultEmptyResponsePlugin } from "../plugins/defaults/
|
|
34
|
+
import { defaultEmptyResponsePlugin } from "../plugins/defaults/index.js";
|
|
35
35
|
import { runHook } from "../plugins/pipeline.js";
|
|
36
36
|
import {
|
|
37
37
|
registerPlugin,
|
|
@@ -260,3 +260,66 @@ describe("file_write tool PKB re-index hook", () => {
|
|
|
260
260
|
expect(existsSync(join(workingDir, "pkb", "oops.md"))).toBe(true);
|
|
261
261
|
});
|
|
262
262
|
});
|
|
263
|
+
|
|
264
|
+
describe("file_write artifact-HTML guard", () => {
|
|
265
|
+
test("rejects a self-contained interactive HTML visualization", async () => {
|
|
266
|
+
const dir = makeTempDir();
|
|
267
|
+
const html =
|
|
268
|
+
"<!doctype html><html><head><title>Food Market</title></head>" +
|
|
269
|
+
"<body><canvas id='c'></canvas><script>" +
|
|
270
|
+
("const data=[{x:1,y:2}];").padEnd(4000, "/") +
|
|
271
|
+
"new Chart(document.getElementById('c'), {data});</script></body></html>";
|
|
272
|
+
|
|
273
|
+
const result = await fileWriteTool.execute(
|
|
274
|
+
{ path: "food-market-stats.html", content: html },
|
|
275
|
+
makeContext(dir),
|
|
276
|
+
);
|
|
277
|
+
|
|
278
|
+
expect(result.isError).toBe(true);
|
|
279
|
+
expect(result.content).toContain('skill: "app-builder"');
|
|
280
|
+
expect(existsSync(join(dir, "food-market-stats.html"))).toBe(false);
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test("allows app-builder's thin shell index.html (external module script)", async () => {
|
|
284
|
+
const dir = makeTempDir();
|
|
285
|
+
const shell =
|
|
286
|
+
"<!doctype html><html><head>" +
|
|
287
|
+
"<link rel='stylesheet' href='/src/styles.css'>".padEnd(3200, " ") +
|
|
288
|
+
"</head><body><div id='app'></div>" +
|
|
289
|
+
"<script type='module' src='/src/main.tsx'></script></body></html>";
|
|
290
|
+
|
|
291
|
+
const result = await fileWriteTool.execute(
|
|
292
|
+
{ path: "src/index.html", content: shell },
|
|
293
|
+
makeContext(dir),
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
expect(result.isError).toBe(false);
|
|
297
|
+
expect(existsSync(join(dir, "src", "index.html"))).toBe(true);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
test("allows a small/static HTML snippet", async () => {
|
|
301
|
+
const dir = makeTempDir();
|
|
302
|
+
const result = await fileWriteTool.execute(
|
|
303
|
+
{ path: "note.html", content: "<html><body><h1>Hi</h1></body></html>" },
|
|
304
|
+
makeContext(dir),
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
expect(result.isError).toBe(false);
|
|
308
|
+
expect(existsSync(join(dir, "note.html"))).toBe(true);
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
test("allows a non-HTML file even with inline script-like content", async () => {
|
|
312
|
+
const dir = makeTempDir();
|
|
313
|
+
const result = await fileWriteTool.execute(
|
|
314
|
+
{
|
|
315
|
+
path: "notes.md",
|
|
316
|
+
content:
|
|
317
|
+
"<script>" + "x".padEnd(2000, "x") + "</script>" + "\n# notes\n",
|
|
318
|
+
},
|
|
319
|
+
makeContext(dir),
|
|
320
|
+
);
|
|
321
|
+
|
|
322
|
+
expect(result.isError).toBe(false);
|
|
323
|
+
expect(existsSync(join(dir, "notes.md"))).toBe(true);
|
|
324
|
+
});
|
|
325
|
+
});
|
|
@@ -27,6 +27,7 @@ const ALLOWLIST = new Set([
|
|
|
27
27
|
"clients/shared/App/Auth/PlatformOAuthService.swift", // comment explaining runtimeUrl vs platformUrl
|
|
28
28
|
"clients/macos/vellum-assistant/App/AppDelegate.swift",
|
|
29
29
|
"clients/macos/vellum-assistant/Features/Settings/SettingsConnectTab.swift",
|
|
30
|
+
"apps/macos/src/main/bundle-flow.ts", // Electron main calls the local gateway (gatewayPort) with a Guardian token to scan bundles
|
|
30
31
|
".claude/skills/update/SKILL.md", // daemon health check script
|
|
31
32
|
|
|
32
33
|
// --- Test fixtures that poll the daemon directly (gateway may require auth) ---
|
|
@@ -84,12 +85,26 @@ function isGatewayInternal(filePath: string): boolean {
|
|
|
84
85
|
return filePath.startsWith("gateway/");
|
|
85
86
|
}
|
|
86
87
|
|
|
88
|
+
/** Additional files allowed for the interpolated-port check only (use gateway port, not runtime). */
|
|
89
|
+
const INTERPOLATED_PORT_ALLOWLIST = new Set([
|
|
90
|
+
"apps/macos/src/main/bundle-flow.ts",
|
|
91
|
+
// Electron main bridges the host proxy to a local assistant's gateway
|
|
92
|
+
// (gatewayPort) over loopback with a Guardian-minted gateway token — same
|
|
93
|
+
// class as bundle-flow.ts. Added with #34049 (host proxy for cloud/managed
|
|
94
|
+
// assistants); the allowlist entry was missed there.
|
|
95
|
+
"apps/macos/src/main/host-proxy-router.ts",
|
|
96
|
+
]);
|
|
97
|
+
|
|
87
98
|
/** Shared violation filter: exempt test files, gateway internals, and allowlisted paths. */
|
|
88
|
-
function filterViolations(
|
|
99
|
+
function filterViolations(
|
|
100
|
+
files: string[],
|
|
101
|
+
extraAllowlist?: Set<string>,
|
|
102
|
+
): string[] {
|
|
89
103
|
return files.filter((f) => {
|
|
90
104
|
if (isTestFile(f)) return false;
|
|
91
105
|
if (isGatewayInternal(f)) return false;
|
|
92
106
|
if (ALLOWLIST.has(f)) return false;
|
|
107
|
+
if (extraAllowlist?.has(f)) return false;
|
|
93
108
|
return true;
|
|
94
109
|
});
|
|
95
110
|
}
|
|
@@ -181,7 +196,7 @@ describe("gateway-only API consumption guard", () => {
|
|
|
181
196
|
}
|
|
182
197
|
|
|
183
198
|
const files = grepOutput.split("\n").filter((f) => f.length > 0);
|
|
184
|
-
const violations = filterViolations(files);
|
|
199
|
+
const violations = filterViolations(files, INTERPOLATED_PORT_ALLOWLIST);
|
|
185
200
|
|
|
186
201
|
if (violations.length > 0) {
|
|
187
202
|
const message = [
|
|
@@ -20,7 +20,7 @@ mock.module("../util/logger.js", () => ({
|
|
|
20
20
|
}));
|
|
21
21
|
|
|
22
22
|
const _conversationMocks = new Map<string, unknown>();
|
|
23
|
-
mock.module("../daemon/conversation-
|
|
23
|
+
mock.module("../daemon/conversation-registry.js", () => ({
|
|
24
24
|
findConversation: (id: string) => _conversationMocks.get(id),
|
|
25
25
|
}));
|
|
26
26
|
|
|
@@ -21,7 +21,7 @@ import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
|
21
21
|
mock.module("../config/env.js", () => ({ isHttpAuthDisabled: () => true }));
|
|
22
22
|
|
|
23
23
|
const _conversationMocks = new Map<string, unknown>();
|
|
24
|
-
mock.module("../daemon/conversation-
|
|
24
|
+
mock.module("../daemon/conversation-registry.js", () => ({
|
|
25
25
|
findConversation: (id: string) => _conversationMocks.get(id),
|
|
26
26
|
}));
|
|
27
27
|
|
|
@@ -51,9 +51,7 @@ import {
|
|
|
51
51
|
routeGuardianReply,
|
|
52
52
|
} from "../runtime/guardian-reply-router.js";
|
|
53
53
|
import * as pendingInteractions from "../runtime/pending-interactions.js";
|
|
54
|
-
import {
|
|
55
|
-
listGuardianDecisionPrompts,
|
|
56
|
-
} from "../runtime/routes/guardian-action-routes.js";
|
|
54
|
+
import { listGuardianDecisionPrompts } from "../runtime/routes/guardian-action-routes.js";
|
|
57
55
|
|
|
58
56
|
initializeDb();
|
|
59
57
|
|
|
@@ -1334,6 +1332,97 @@ describe("routing invariant: callback buttons route through canonical primitive"
|
|
|
1334
1332
|
});
|
|
1335
1333
|
});
|
|
1336
1334
|
|
|
1335
|
+
// ===========================================================================
|
|
1336
|
+
// SECTION 8b: route-owned (directResolve) confirmations resolve via the
|
|
1337
|
+
// channel/guardian-action path too.
|
|
1338
|
+
//
|
|
1339
|
+
// The ACP spawn/steer approval gate (acp-routes.ts, ATL-822) registers
|
|
1340
|
+
// pending confirmations with a `directResolve` callback rather than a
|
|
1341
|
+
// Conversation.prompter. The tool_approval resolver must fire that callback
|
|
1342
|
+
// directly; otherwise a guardian approval submitted over a channel never
|
|
1343
|
+
// reaches the waiting route and the spawn/resume hangs until timeout.
|
|
1344
|
+
// ===========================================================================
|
|
1345
|
+
|
|
1346
|
+
describe("routing invariant: directResolve interactions resolve via guardian decisions", () => {
|
|
1347
|
+
beforeEach(() => resetTables());
|
|
1348
|
+
|
|
1349
|
+
function registerDirectResolveInteraction(
|
|
1350
|
+
requestId: string,
|
|
1351
|
+
conversationId: string,
|
|
1352
|
+
): string[] {
|
|
1353
|
+
const decisions: string[] = [];
|
|
1354
|
+
pendingInteractions.register(requestId, {
|
|
1355
|
+
conversationId,
|
|
1356
|
+
kind: "confirmation",
|
|
1357
|
+
confirmationDetails: {
|
|
1358
|
+
toolName: "acp_spawn",
|
|
1359
|
+
input: { agent: "claude", task: "t", cwd: "/work" },
|
|
1360
|
+
riskLevel: "high",
|
|
1361
|
+
executionTarget: "host",
|
|
1362
|
+
allowlistOptions: [],
|
|
1363
|
+
scopeOptions: [],
|
|
1364
|
+
persistentDecisionsAllowed: false,
|
|
1365
|
+
},
|
|
1366
|
+
directResolve: (d) => decisions.push(d),
|
|
1367
|
+
});
|
|
1368
|
+
return decisions;
|
|
1369
|
+
}
|
|
1370
|
+
|
|
1371
|
+
test("channel approval fires directResolve('allow') with no live Conversation", async () => {
|
|
1372
|
+
const req = createCanonicalGuardianRequest({
|
|
1373
|
+
kind: "tool_approval",
|
|
1374
|
+
sourceType: "channel",
|
|
1375
|
+
conversationId: "conv-acp",
|
|
1376
|
+
guardianExternalUserId: "guardian-1",
|
|
1377
|
+
guardianPrincipalId: TEST_PRINCIPAL_ID,
|
|
1378
|
+
toolName: "acp_spawn",
|
|
1379
|
+
expiresAt: Date.now() + 60_000,
|
|
1380
|
+
});
|
|
1381
|
+
// Deliberately no _conversationMocks entry: the prompter path would have
|
|
1382
|
+
// failed with conversation_not_found, proving directResolve is what runs.
|
|
1383
|
+
const decisions = registerDirectResolveInteraction(req.id, "conv-acp");
|
|
1384
|
+
|
|
1385
|
+
const result = await routeGuardianReply(
|
|
1386
|
+
replyCtx({
|
|
1387
|
+
messageText: "",
|
|
1388
|
+
callbackData: `apr:${req.id}:approve_once`,
|
|
1389
|
+
conversationId: "conv-acp",
|
|
1390
|
+
}),
|
|
1391
|
+
);
|
|
1392
|
+
|
|
1393
|
+
expect(result.decisionApplied).toBe(true);
|
|
1394
|
+
expect(decisions).toEqual(["allow"]);
|
|
1395
|
+
expect(pendingInteractions.get(req.id)).toBeUndefined();
|
|
1396
|
+
expect(getCanonicalGuardianRequest(req.id)!.status).toBe("approved");
|
|
1397
|
+
});
|
|
1398
|
+
|
|
1399
|
+
test("channel rejection fires directResolve('deny')", async () => {
|
|
1400
|
+
const req = createCanonicalGuardianRequest({
|
|
1401
|
+
kind: "tool_approval",
|
|
1402
|
+
sourceType: "channel",
|
|
1403
|
+
conversationId: "conv-acp",
|
|
1404
|
+
guardianExternalUserId: "guardian-1",
|
|
1405
|
+
guardianPrincipalId: TEST_PRINCIPAL_ID,
|
|
1406
|
+
toolName: "acp_spawn",
|
|
1407
|
+
expiresAt: Date.now() + 60_000,
|
|
1408
|
+
});
|
|
1409
|
+
const decisions = registerDirectResolveInteraction(req.id, "conv-acp");
|
|
1410
|
+
|
|
1411
|
+
const result = await routeGuardianReply(
|
|
1412
|
+
replyCtx({
|
|
1413
|
+
messageText: "",
|
|
1414
|
+
callbackData: `apr:${req.id}:reject`,
|
|
1415
|
+
conversationId: "conv-acp",
|
|
1416
|
+
}),
|
|
1417
|
+
);
|
|
1418
|
+
|
|
1419
|
+
expect(result.decisionApplied).toBe(true);
|
|
1420
|
+
expect(decisions).toEqual(["deny"]);
|
|
1421
|
+
expect(pendingInteractions.get(req.id)).toBeUndefined();
|
|
1422
|
+
expect(getCanonicalGuardianRequest(req.id)!.status).toBe("denied");
|
|
1423
|
+
});
|
|
1424
|
+
});
|
|
1425
|
+
|
|
1337
1426
|
// ===========================================================================
|
|
1338
1427
|
// SECTION 9: Destination hints do not bypass principal binding for tool_approval
|
|
1339
1428
|
// ===========================================================================
|
|
@@ -143,7 +143,7 @@ mock.module("../util/logger.js", () => ({
|
|
|
143
143
|
import {
|
|
144
144
|
clearConversations,
|
|
145
145
|
setConversation,
|
|
146
|
-
} from "../daemon/conversation-
|
|
146
|
+
} from "../daemon/conversation-registry.js";
|
|
147
147
|
import { handleConfirmationResponse } from "../daemon/handlers/conversations.js";
|
|
148
148
|
|
|
149
149
|
describe("handleConfirmationResponse canonical status sync", () => {
|
|
@@ -56,6 +56,7 @@ mock.module("../heartbeat/heartbeat-run-store.js", () => ({
|
|
|
56
56
|
markStaleRunsAsMissed: mockMarkStaleRunsAsMissed,
|
|
57
57
|
markStaleRunningAsError: mockMarkStaleRunningAsError,
|
|
58
58
|
countCompletedHeartbeatRuns: mock(() => 10),
|
|
59
|
+
countCompletedRunsToday: mock(() => 0),
|
|
59
60
|
countRecentConsecutiveRuns: mock(() => 0),
|
|
60
61
|
}));
|
|
61
62
|
|
|
@@ -32,6 +32,7 @@ mock.module("../heartbeat/heartbeat-run-store.js", () => ({
|
|
|
32
32
|
markStaleRunningAsError: mockMarkStaleRunningAsError,
|
|
33
33
|
listHeartbeatRuns: mockListHeartbeatRuns,
|
|
34
34
|
countCompletedHeartbeatRuns: mockCountCompletedHeartbeatRuns,
|
|
35
|
+
countCompletedRunsToday: mock(() => 0),
|
|
35
36
|
countRecentConsecutiveRuns: mock(() => 0),
|
|
36
37
|
}));
|
|
37
38
|
|
|
@@ -21,8 +21,8 @@ import type {
|
|
|
21
21
|
UserPromptSubmitContext,
|
|
22
22
|
} from "../plugin-api/types.js";
|
|
23
23
|
import userPromptSubmit from "../plugins/defaults/history-repair/hooks/user-prompt-submit.js";
|
|
24
|
-
import { defaultHistoryRepairPlugin } from "../plugins/defaults/history-repair/register.js";
|
|
25
24
|
import { repairHistory } from "../plugins/defaults/history-repair/terminal.js";
|
|
25
|
+
import { defaultHistoryRepairPlugin } from "../plugins/defaults/index.js";
|
|
26
26
|
import { runHook } from "../plugins/pipeline.js";
|
|
27
27
|
import {
|
|
28
28
|
registerPlugin,
|
|
@@ -40,6 +40,10 @@ const noopLogger: PluginLogger = {
|
|
|
40
40
|
function makeCtx(messages: Message[]): UserPromptSubmitContext {
|
|
41
41
|
return {
|
|
42
42
|
conversationId: "conv-test",
|
|
43
|
+
userMessageId: "msg-test",
|
|
44
|
+
requestId: "req-test",
|
|
45
|
+
modelProfileKey: null,
|
|
46
|
+
isNonInteractive: false,
|
|
43
47
|
prompt: "",
|
|
44
48
|
originalMessages: messages,
|
|
45
49
|
latestMessages: messages,
|
|
@@ -372,6 +372,49 @@ describe("HostAppControlProxy", () => {
|
|
|
372
372
|
proxy.dispose();
|
|
373
373
|
});
|
|
374
374
|
|
|
375
|
+
test("non-start tool with mismatched target client is rejected", async () => {
|
|
376
|
+
mockActorMap.set("client-A", "user-1");
|
|
377
|
+
mockActorMap.set("client-B", "user-1");
|
|
378
|
+
const proxy = new HostAppControlProxy("conv-1");
|
|
379
|
+
const startCtrl = new AbortController();
|
|
380
|
+
|
|
381
|
+
const pStart = proxy.request(
|
|
382
|
+
"app_control_start",
|
|
383
|
+
{ tool: "start", app: "com.example.editor" },
|
|
384
|
+
"conv-1",
|
|
385
|
+
startCtrl.signal,
|
|
386
|
+
"user-1",
|
|
387
|
+
"client-A",
|
|
388
|
+
);
|
|
389
|
+
proxy.resolve(
|
|
390
|
+
(sentMessages[0] as Record<string, unknown>).requestId as string,
|
|
391
|
+
payload({ pngBase64: PNG_A }),
|
|
392
|
+
);
|
|
393
|
+
await pStart;
|
|
394
|
+
sentMessages.length = 0;
|
|
395
|
+
|
|
396
|
+
const result = await proxy.request(
|
|
397
|
+
"app_control_type",
|
|
398
|
+
{
|
|
399
|
+
tool: "type",
|
|
400
|
+
app: "com.example.editor",
|
|
401
|
+
text: "hello",
|
|
402
|
+
},
|
|
403
|
+
"conv-1",
|
|
404
|
+
new AbortController().signal,
|
|
405
|
+
"user-1",
|
|
406
|
+
"client-B",
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
expect(result.isError).toBe(true);
|
|
410
|
+
expect(result.content).toContain("client-A");
|
|
411
|
+
expect(result.content).toContain("client-B");
|
|
412
|
+
expect(result.content).toContain("app_control_start");
|
|
413
|
+
expect(sentMessages).toHaveLength(0);
|
|
414
|
+
|
|
415
|
+
proxy.dispose();
|
|
416
|
+
});
|
|
417
|
+
|
|
375
418
|
test("non-start tool with case-different but otherwise matching app is allowed", async () => {
|
|
376
419
|
const proxy = new HostAppControlProxy("conv-1");
|
|
377
420
|
const startCtrl = new AbortController();
|
|
@@ -1297,6 +1340,7 @@ describe("HostAppControlProxy", () => {
|
|
|
1297
1340
|
_setActiveAppControlSession({
|
|
1298
1341
|
conversationId: "conv-1",
|
|
1299
1342
|
app: "com.example.app",
|
|
1343
|
+
targetClientId: "client-A",
|
|
1300
1344
|
});
|
|
1301
1345
|
|
|
1302
1346
|
const resultPromise = proxy.request(
|
|
@@ -1332,6 +1376,7 @@ describe("HostAppControlProxy", () => {
|
|
|
1332
1376
|
_setActiveAppControlSession({
|
|
1333
1377
|
conversationId: "conv-1",
|
|
1334
1378
|
app: "com.example.app",
|
|
1379
|
+
targetClientId: "client-A",
|
|
1335
1380
|
});
|
|
1336
1381
|
|
|
1337
1382
|
const resultPromise = proxy.request(
|
|
@@ -47,7 +47,7 @@ interface FakeConversation {
|
|
|
47
47
|
|
|
48
48
|
const conversations = new Map<string, FakeConversation>();
|
|
49
49
|
|
|
50
|
-
mock.module("../daemon/conversation-
|
|
50
|
+
mock.module("../daemon/conversation-registry.js", () => ({
|
|
51
51
|
findConversation: (id: string) => conversations.get(id),
|
|
52
52
|
}));
|
|
53
53
|
|