@vellumai/assistant 0.8.8 → 0.8.9-dev.202606091853.fbaa2ae
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 +263 -121
- package/package.json +2 -2
- 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__/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__/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-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__/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-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__/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__/send-endpoint-busy.test.ts +4 -1
- 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__/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/responses/conversation-message.ts +22 -2
- 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 -16
- package/src/config/llm-resolver.ts +39 -7
- 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 +18 -36
- 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/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/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 +34 -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 +22 -0
- 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
|
|
|
@@ -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
|
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* 4. Untargeted (no targetClientId, no header) → 200 accepted (regression)
|
|
9
9
|
*
|
|
10
10
|
* Resolution goes through conversation.hostCuProxy?.resolve(...). The
|
|
11
|
-
* conversation
|
|
11
|
+
* conversation registry is mocked to return a controlled conversation object.
|
|
12
12
|
*
|
|
13
|
-
* Note: host-cu-routes.ts has a deep import chain (conversation-
|
|
13
|
+
* Note: host-cu-routes.ts has a deep import chain (conversation-registry →
|
|
14
14
|
* conversation.ts → ces-client → service-contracts) that requires mocking
|
|
15
15
|
* before the module loads. We use dynamic imports to ensure all mocks are
|
|
16
16
|
* registered before the route module is evaluated.
|
|
@@ -75,7 +75,7 @@ const conversationStore = new Map<
|
|
|
75
75
|
{ hostCuProxy?: { processObservation: (...args: unknown[]) => void } }
|
|
76
76
|
>();
|
|
77
77
|
|
|
78
|
-
mock.module("../daemon/conversation-
|
|
78
|
+
mock.module("../daemon/conversation-registry.js", () => ({
|
|
79
79
|
findConversation: (conversationId: string) =>
|
|
80
80
|
conversationStore.get(conversationId),
|
|
81
81
|
}));
|
|
@@ -7,6 +7,11 @@ mock.module("../util/logger.js", () => ({
|
|
|
7
7
|
getLogger: () => makeMockLogger(),
|
|
8
8
|
}));
|
|
9
9
|
|
|
10
|
+
import type { Conversation } from "../daemon/conversation.js";
|
|
11
|
+
import {
|
|
12
|
+
deleteConversation,
|
|
13
|
+
setConversation,
|
|
14
|
+
} from "../daemon/conversation-registry.js";
|
|
10
15
|
import {
|
|
11
16
|
createConversation,
|
|
12
17
|
getConversation,
|
|
@@ -113,6 +118,63 @@ describe("inference-profile-session-reaper", () => {
|
|
|
113
118
|
subscription.dispose();
|
|
114
119
|
});
|
|
115
120
|
|
|
121
|
+
test("syncs the live in-memory Conversation instance for each cleared session", async () => {
|
|
122
|
+
// GIVEN an expired session-backed override persisted on a conversation row
|
|
123
|
+
const conv = createConversation("reaper-live-sync");
|
|
124
|
+
setConversationInferenceProfileSession(
|
|
125
|
+
conv.id,
|
|
126
|
+
"balanced",
|
|
127
|
+
"session-live",
|
|
128
|
+
Date.now() - 1,
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
// AND a live Conversation instance registered for that conversation,
|
|
132
|
+
// seeded with the same override state the reaper is about to expire
|
|
133
|
+
const appliedStates: Array<{
|
|
134
|
+
profile: string | null;
|
|
135
|
+
sessionId: string | null;
|
|
136
|
+
expiresAt: number | null;
|
|
137
|
+
}> = [];
|
|
138
|
+
const liveConversation = {
|
|
139
|
+
conversationId: conv.id,
|
|
140
|
+
inferenceProfile: "balanced",
|
|
141
|
+
inferenceProfileSessionId: "session-live",
|
|
142
|
+
inferenceProfileExpiresAt: Date.now() - 1,
|
|
143
|
+
applyInferenceProfileState(
|
|
144
|
+
this: {
|
|
145
|
+
inferenceProfile: string | null;
|
|
146
|
+
inferenceProfileSessionId: string | null;
|
|
147
|
+
inferenceProfileExpiresAt: number | null;
|
|
148
|
+
},
|
|
149
|
+
state: {
|
|
150
|
+
profile: string | null;
|
|
151
|
+
sessionId: string | null;
|
|
152
|
+
expiresAt: number | null;
|
|
153
|
+
},
|
|
154
|
+
) {
|
|
155
|
+
appliedStates.push(state);
|
|
156
|
+
this.inferenceProfile = state.profile;
|
|
157
|
+
this.inferenceProfileSessionId = state.sessionId;
|
|
158
|
+
this.inferenceProfileExpiresAt = state.expiresAt;
|
|
159
|
+
},
|
|
160
|
+
} as unknown as Conversation;
|
|
161
|
+
setConversation(conv.id, liveConversation);
|
|
162
|
+
|
|
163
|
+
// WHEN the reaper sweeps the expired session
|
|
164
|
+
tickInferenceProfileReaper();
|
|
165
|
+
|
|
166
|
+
// THEN the live instance is cleared in lock-step with the DB row, so the
|
|
167
|
+
// per-turn override derivation reads the cleared state without re-fetching
|
|
168
|
+
expect(appliedStates).toEqual([
|
|
169
|
+
{ profile: null, sessionId: null, expiresAt: null },
|
|
170
|
+
]);
|
|
171
|
+
expect(liveConversation.inferenceProfile).toBeNull();
|
|
172
|
+
expect(liveConversation.inferenceProfileSessionId).toBeNull();
|
|
173
|
+
expect(liveConversation.inferenceProfileExpiresAt).toBeNull();
|
|
174
|
+
|
|
175
|
+
deleteConversation(conv.id);
|
|
176
|
+
});
|
|
177
|
+
|
|
116
178
|
test("CAS protection: row with NULL expiresAt (sticky override) is not touched", async () => {
|
|
117
179
|
const conv = createConversation("reaper-cas-conv");
|
|
118
180
|
|