@vellumai/assistant 0.8.8 → 0.8.9-staging.2
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/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 +235 -6
- package/package.json +2 -2
- 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 +1 -1
- 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 +5 -4
- 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 +132 -0
- package/src/__tests__/compaction-events.test.ts +5 -5
- package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
- package/src/__tests__/context-overflow-reducer.test.ts +1 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +6 -4
- 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 +142 -218
- package/src/__tests__/conversation-agent-loop.test.ts +297 -586
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +5 -4
- package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -4
- package/src/__tests__/conversation-process-callsite.test.ts +14 -14
- package/src/__tests__/conversation-provider-retry-repair.test.ts +70 -65
- package/src/__tests__/conversation-queue.test.ts +9 -9
- package/src/__tests__/conversation-runtime-assembly.test.ts +923 -231
- package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
- package/src/__tests__/conversation-slash-queue.test.ts +6 -4
- package/src/__tests__/conversation-slash-unknown.test.ts +6 -4
- package/src/__tests__/conversation-speed-override.test.ts +10 -9
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-workspace-cache-state.test.ts +23 -20
- package/src/__tests__/conversation-workspace-injection.test.ts +68 -6
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +14 -11
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/credential-security-invariants.test.ts +0 -1
- 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 +12 -2
- package/src/__tests__/guardian-grant-minting.test.ts +1 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +2 -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 +1 -1
- 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__/memory-retrieval-hook.test.ts +131 -26
- package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
- package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
- package/src/__tests__/plugin-api-shim.test.ts +6 -9
- package/src/__tests__/plugin-bootstrap.test.ts +12 -23
- package/src/__tests__/plugin-registry.test.ts +3 -49
- package/src/__tests__/plugin-types.test.ts +0 -70
- package/src/__tests__/pre-model-call-sanitize.test.ts +109 -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 +33 -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 +1 -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/acp/__tests__/agent-process.test.ts +161 -0
- package/src/acp/__tests__/client-handler.test.ts +40 -0
- 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/feature-gate.test.ts +48 -0
- package/src/acp/feature-gate.ts +34 -0
- package/src/acp/prepare-agent-env.ts +80 -27
- package/src/acp/resolve-agent.test.ts +225 -9
- package/src/acp/resolve-agent.ts +122 -17
- 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 +414 -248
- package/src/api/responses/conversation-message.ts +14 -1
- package/src/approvals/guardian-request-resolvers.ts +1 -1
- package/src/background-wake/next-wake.ts +1 -0
- package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
- package/src/cli/commands/plugins.ts +43 -37
- package/src/cli/lib/__tests__/install-from-github.test.ts +429 -111
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
- package/src/cli/lib/__tests__/plugin-details.test.ts +372 -0
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +220 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +226 -32
- package/src/cli/lib/install-from-github.ts +464 -55
- package/src/cli/lib/plugin-catalog-cache.ts +84 -0
- package/src/cli/lib/plugin-details.ts +409 -0
- package/src/cli/lib/plugin-marketplace.ts +197 -0
- package/src/cli/lib/search-plugins.ts +195 -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 +6 -0
- package/src/config/bundled-skills/acp/SKILL.md +85 -33
- 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 -2
- package/src/config/feature-flag-registry.json +15 -6
- package/src/config/schemas/call-site-catalog.ts +0 -14
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +0 -2
- package/src/context/compactor.ts +22 -4
- package/src/context/strip-injections.ts +8 -2
- package/src/context/window-manager.ts +27 -13
- package/src/daemon/conversation-agent-loop-handlers.ts +10 -35
- package/src/daemon/conversation-agent-loop.ts +175 -1055
- package/src/daemon/conversation-lifecycle.ts +11 -255
- package/src/daemon/conversation-process.ts +8 -136
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +293 -392
- package/src/daemon/conversation-store.ts +9 -90
- package/src/daemon/conversation-surfaces.ts +24 -8
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +404 -57
- package/src/daemon/disk-pressure-policy.ts +0 -1
- package/src/daemon/external-plugins-bootstrap.ts +14 -19
- package/src/daemon/handlers/conversations.ts +3 -1
- package/src/daemon/handlers/skills.ts +4 -1
- package/src/daemon/host-proxy-preactivation.ts +1 -3
- package/src/daemon/lifecycle.ts +21 -7
- package/src/daemon/server.ts +2 -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__/jobs-worker-v2-schedule.test.ts +87 -0
- package/src/memory/conversation-crud.ts +29 -19
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/db-init.ts +2 -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/index.ts +1 -0
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +3 -3
- package/src/memory/v2/consolidation-job.ts +13 -4
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +6 -5
- package/src/plugin-api/types.ts +75 -0
- package/src/plugins/defaults/compaction/compact.ts +59 -0
- package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +7 -7
- package/src/plugins/defaults/compaction/manager-store.ts +57 -0
- package/src/plugins/defaults/compaction/package.json +1 -2
- 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 +135 -26
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +184 -74
- 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/unified-turn-context.ts +223 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/assign.test.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +9 -6
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/maintain-job.test.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/orchestrate.test.ts +8 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/reconcile.test.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/render-injection.test.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/router.test.ts +10 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +8 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selector.test.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +16 -17
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +9 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +5 -2
- 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 +75 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +15 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/provider-blocks.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +7 -3
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/router.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selection-log-store.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selector.ts +7 -7
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +32 -94
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +1 -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/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/templates/BOOTSTRAP-ACTIVATION-RAIL.md +35 -3
- 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 +315 -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__/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 +89 -25
- package/src/runtime/routes/acp-routes.ts +81 -29
- 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/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 +28 -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/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 +16 -6
- package/src/runtime/routes/playground/helpers.ts +1 -1
- package/src/runtime/routes/plugins-routes.ts +337 -35
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/work-items-routes.ts +2 -4
- package/src/runtime/services/conversation-serializer.ts +1 -1
- package/src/signals/cancel.ts +2 -4
- package/src/subagent/manager.ts +21 -5
- package/src/tools/acp/context.ts +20 -0
- package/src/tools/acp/list-agents.test.ts +8 -2
- package/src/tools/acp/spawn.test.ts +176 -195
- 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/filesystem/write.ts +34 -0
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/ui-surface/definitions.ts +25 -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/__tests__/plugin-external-api.test.ts +0 -68
- package/src/daemon/wake-target-adapter.ts +0 -253
- 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/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/plugins/external-api.ts +0 -104
- 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/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/capabilities.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/core.test.ts +0 -0
- /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__/health.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/needle.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/provider-blocks.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/snapshot.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/tree.test.ts +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}/core.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}/health.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/llm-retry.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/needle.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/snapshot.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +0 -0
|
@@ -141,7 +141,7 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
141
141
|
getMessageById: () => null,
|
|
142
142
|
getLastUserTimestampBefore: () => 0,
|
|
143
143
|
setLastNotifiedInferenceProfile: () => {},
|
|
144
|
-
|
|
144
|
+
resolveOverrideProfile: () => undefined,
|
|
145
145
|
updateMessageMetadata: () => {},
|
|
146
146
|
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
147
147
|
updateMessageContent: mock(() => {}),
|
|
@@ -180,6 +180,7 @@ mock.module("../memory/retrieval-budget.js", () => ({
|
|
|
180
180
|
}));
|
|
181
181
|
mock.module("../context/window-manager.js", () => ({
|
|
182
182
|
ContextWindowManager: class {
|
|
183
|
+
updateConfig() {}
|
|
183
184
|
shouldCompact() {
|
|
184
185
|
return { needed: false, estimatedTokens: 0 };
|
|
185
186
|
}
|
|
@@ -228,10 +229,11 @@ mock.module("../agent/loop.js", () => ({
|
|
|
228
229
|
getActiveModel() {
|
|
229
230
|
return undefined;
|
|
230
231
|
}
|
|
231
|
-
async run(
|
|
232
|
-
messages: Message[]
|
|
233
|
-
onEvent: (event: AgentEvent) => void
|
|
234
|
-
): Promise<Message[]> {
|
|
232
|
+
async run(options: {
|
|
233
|
+
messages: Message[];
|
|
234
|
+
onEvent: (event: AgentEvent) => void;
|
|
235
|
+
}): Promise<Message[]> {
|
|
236
|
+
const { messages, onEvent } = options;
|
|
235
237
|
// Prime the assistant row anchor — production code emits this from
|
|
236
238
|
// `AgentLoop.run` just before `provider.sendMessage`.
|
|
237
239
|
await onEvent({ type: "llm_call_started" });
|
|
@@ -273,6 +275,7 @@ mock.module("../memory/canonical-guardian-store.js", () => ({
|
|
|
273
275
|
}));
|
|
274
276
|
|
|
275
277
|
import { Conversation } from "../daemon/conversation.js";
|
|
278
|
+
import { refreshWorkspaceTopLevelContextIfNeeded } from "../daemon/conversation-workspace.js";
|
|
276
279
|
|
|
277
280
|
function makeConversation(): Conversation {
|
|
278
281
|
const provider = {
|
|
@@ -310,7 +313,7 @@ describe("Conversation workspace dirty on file mutations", () => {
|
|
|
310
313
|
await conversation.loadFromDb();
|
|
311
314
|
|
|
312
315
|
// Prime the cache so dirty=false
|
|
313
|
-
|
|
316
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
314
317
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
315
318
|
|
|
316
319
|
agentLoopScript = (onEvent) => {
|
|
@@ -339,7 +342,7 @@ describe("Conversation workspace dirty on file mutations", () => {
|
|
|
339
342
|
const conversation = makeConversation();
|
|
340
343
|
await conversation.loadFromDb();
|
|
341
344
|
|
|
342
|
-
|
|
345
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
343
346
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
344
347
|
|
|
345
348
|
agentLoopScript = (onEvent) => {
|
|
@@ -370,7 +373,7 @@ describe("Conversation workspace dirty on file mutations", () => {
|
|
|
370
373
|
const conversation = makeConversation();
|
|
371
374
|
await conversation.loadFromDb();
|
|
372
375
|
|
|
373
|
-
|
|
376
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
374
377
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
375
378
|
|
|
376
379
|
agentLoopScript = (onEvent) => {
|
|
@@ -399,7 +402,7 @@ describe("Conversation workspace dirty on file mutations", () => {
|
|
|
399
402
|
const conversation = makeConversation();
|
|
400
403
|
await conversation.loadFromDb();
|
|
401
404
|
|
|
402
|
-
|
|
405
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
403
406
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
404
407
|
|
|
405
408
|
agentLoopScript = (onEvent) => {
|
|
@@ -428,7 +431,7 @@ describe("Conversation workspace dirty on file mutations", () => {
|
|
|
428
431
|
const conversation = makeConversation();
|
|
429
432
|
await conversation.loadFromDb();
|
|
430
433
|
|
|
431
|
-
|
|
434
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
432
435
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
433
436
|
|
|
434
437
|
agentLoopScript = (onEvent) => {
|
|
@@ -457,7 +460,7 @@ describe("Conversation workspace dirty on file mutations", () => {
|
|
|
457
460
|
const conversation = makeConversation();
|
|
458
461
|
await conversation.loadFromDb();
|
|
459
462
|
|
|
460
|
-
|
|
463
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
461
464
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
462
465
|
|
|
463
466
|
agentLoopScript = (onEvent) => {
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests that the conversations import route never persists non-renderable
|
|
3
|
+
* roles. The messages store is UI-facing (`ConversationMessage`), so an
|
|
4
|
+
* imported export carrying agent-context `system` rows must land only its
|
|
5
|
+
* `user`/`assistant` turns — the `system` rows are dropped, not persisted.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
9
|
+
|
|
10
|
+
mock.module("../util/logger.js", () => ({
|
|
11
|
+
getLogger: () =>
|
|
12
|
+
new Proxy({} as Record<string, unknown>, {
|
|
13
|
+
get: () => () => {},
|
|
14
|
+
}),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
mock.module("../config/loader.js", () => ({
|
|
18
|
+
getConfig: () => ({
|
|
19
|
+
ui: {},
|
|
20
|
+
model: "test",
|
|
21
|
+
provider: "test",
|
|
22
|
+
memory: { enabled: false },
|
|
23
|
+
rateLimit: { maxRequestsPerMinute: 0 },
|
|
24
|
+
}),
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
mock.module("../memory/indexer.js", () => ({
|
|
28
|
+
indexMessageNow: async () => {},
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
import { getMessages } from "../memory/conversation-crud.js";
|
|
32
|
+
import { getDb } from "../memory/db-connection.js";
|
|
33
|
+
import { initializeDb } from "../memory/db-init.js";
|
|
34
|
+
import { conversations, messages } from "../memory/schema.js";
|
|
35
|
+
import { ROUTES } from "../runtime/routes/conversations-import-routes.js";
|
|
36
|
+
import type { RouteHandlerArgs } from "../runtime/routes/types.js";
|
|
37
|
+
|
|
38
|
+
initializeDb();
|
|
39
|
+
|
|
40
|
+
function resetTables() {
|
|
41
|
+
const db = getDb();
|
|
42
|
+
db.run("DELETE FROM message_attachments");
|
|
43
|
+
db.run("DELETE FROM attachments");
|
|
44
|
+
db.run("DELETE FROM messages");
|
|
45
|
+
db.run("DELETE FROM conversation_keys");
|
|
46
|
+
db.run("DELETE FROM conversations");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const importHandler = ROUTES.find(
|
|
50
|
+
(r) => r.operationId === "conversations_import",
|
|
51
|
+
)!.handler;
|
|
52
|
+
|
|
53
|
+
describe("conversations import system-row filtering", () => {
|
|
54
|
+
beforeEach(resetTables);
|
|
55
|
+
|
|
56
|
+
test("imports renderable turns but drops system rows", async () => {
|
|
57
|
+
// GIVEN an export whose conversation sandwiches a system row between two
|
|
58
|
+
// renderable turns (e.g. agent-context scaffolding an export carried)
|
|
59
|
+
const body = {
|
|
60
|
+
conversations: [
|
|
61
|
+
{
|
|
62
|
+
sourceKey: "src-1",
|
|
63
|
+
title: "Imported chat",
|
|
64
|
+
messages: [
|
|
65
|
+
{ role: "user", content: "first visible" },
|
|
66
|
+
{ role: "system", content: "agent-context scaffolding" },
|
|
67
|
+
{ role: "assistant", content: "second visible" },
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// WHEN the conversation is imported
|
|
74
|
+
const result = (await importHandler({
|
|
75
|
+
body,
|
|
76
|
+
} as unknown as RouteHandlerArgs)) as {
|
|
77
|
+
ok: boolean;
|
|
78
|
+
imported: number;
|
|
79
|
+
messages: number;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// THEN the import succeeds and only counts the renderable turns
|
|
83
|
+
expect(result.ok).toBe(true);
|
|
84
|
+
expect(result.imported).toBe(1);
|
|
85
|
+
expect(result.messages).toBe(2);
|
|
86
|
+
|
|
87
|
+
// AND the persisted rows are exactly the user/assistant turns, never the
|
|
88
|
+
// system scaffolding
|
|
89
|
+
const db = getDb();
|
|
90
|
+
const conv = db.select().from(conversations).all()[0];
|
|
91
|
+
const rows = getMessages(conv.id);
|
|
92
|
+
expect(rows.map((m) => m.role)).toEqual(["user", "assistant"]);
|
|
93
|
+
expect(
|
|
94
|
+
db
|
|
95
|
+
.select()
|
|
96
|
+
.from(messages)
|
|
97
|
+
.all()
|
|
98
|
+
.some((m) => m.role === "system"),
|
|
99
|
+
).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -230,7 +230,6 @@ describe("Invariant 2: no generic plaintext secret read API", () => {
|
|
|
230
230
|
"runtime/routes/platform-routes.ts", // CLI platform connect/disconnect/status routes (CLI-migrated to IPC)
|
|
231
231
|
"ipc/skill-routes/providers.ts", // host.providers.secureKeys.getProviderKey IPC route (out-of-process SkillHost companion)
|
|
232
232
|
"daemon/external-plugins-bootstrap.ts", // reads credentials at plugin init (manifest.requiresCredential) via the CES-mediated getSecureKeyAsync path
|
|
233
|
-
"plugins/external-api.ts", // globalThis runtime bridge that exposes getSecureKeyAsync to dynamically-imported workspace plugins (compiled-binary plugin loading)
|
|
234
233
|
"inbound/platform-callback-registration.ts", // managed credential lookup for platform base URL, assistant ID, and API key
|
|
235
234
|
"tts/providers/elevenlabs-provider.ts", // ElevenLabs TTS API key lookup
|
|
236
235
|
"tts/providers/deepgram-provider.ts", // Deepgram TTS API key lookup
|
|
@@ -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,21 @@ 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
|
+
]);
|
|
92
|
+
|
|
87
93
|
/** Shared violation filter: exempt test files, gateway internals, and allowlisted paths. */
|
|
88
|
-
function filterViolations(
|
|
94
|
+
function filterViolations(
|
|
95
|
+
files: string[],
|
|
96
|
+
extraAllowlist?: Set<string>,
|
|
97
|
+
): string[] {
|
|
89
98
|
return files.filter((f) => {
|
|
90
99
|
if (isTestFile(f)) return false;
|
|
91
100
|
if (isGatewayInternal(f)) return false;
|
|
92
101
|
if (ALLOWLIST.has(f)) return false;
|
|
102
|
+
if (extraAllowlist?.has(f)) return false;
|
|
93
103
|
return true;
|
|
94
104
|
});
|
|
95
105
|
}
|
|
@@ -181,7 +191,7 @@ describe("gateway-only API consumption guard", () => {
|
|
|
181
191
|
}
|
|
182
192
|
|
|
183
193
|
const files = grepOutput.split("\n").filter((f) => f.length > 0);
|
|
184
|
-
const violations = filterViolations(files);
|
|
194
|
+
const violations = filterViolations(files, INTERPOLATED_PORT_ALLOWLIST);
|
|
185
195
|
|
|
186
196
|
if (violations.length > 0) {
|
|
187
197
|
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
|
|
|
@@ -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,
|
|
@@ -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
|
|