@vellumai/assistant 0.8.7 → 0.8.8-dev.202606052332.17fc8ea
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/Dockerfile +20 -4
- package/bun.lock +2 -2
- package/docker-entrypoint.sh +4 -2
- package/docker-init-apt-root.sh +3 -1
- package/docker-kata-apt-env.sh +3 -1
- package/docker-kata-runtime-family.sh +12 -0
- package/docs/architecture/memory.md +1 -1
- package/examples/plugins/echo/README.md +61 -66
- 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/server-message.ts +3 -3
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
- package/openapi.yaml +3378 -335
- package/package.json +2 -2
- package/scripts/generate-openapi.ts +68 -41
- package/src/__tests__/agent-loop-exit-reason.test.ts +35 -93
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +1 -1
- package/src/__tests__/agent-loop.test.ts +37 -87
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
- package/src/__tests__/annotate-risk-options.test.ts +2 -3
- package/src/__tests__/anthropic-provider.test.ts +95 -2
- package/src/__tests__/app-control-flow.test.ts +1 -1
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +4 -1
- package/src/__tests__/assistant-event-hub.test.ts +25 -0
- package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
- package/src/__tests__/{conversation-stream-state.test.ts → assistant-stream-state.test.ts} +252 -91
- package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
- package/src/__tests__/btw-routes.test.ts +62 -3
- package/src/__tests__/build-persisted-content.test.ts +184 -0
- package/src/__tests__/catalog-files.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__/clawhub-files.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.benchmark.test.ts +0 -30
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +57 -19
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +6 -5
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -7
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +316 -1143
- package/src/__tests__/conversation-agent-loop.test.ts +638 -1655
- package/src/__tests__/conversation-analysis-routes.test.ts +6 -0
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-history-web-search.test.ts +11 -1
- package/src/__tests__/conversation-pairing.test.ts +4 -31
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +6 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -10
- package/src/__tests__/conversation-queue.test.ts +2 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +3 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +6 -5
- package/src/__tests__/conversation-runtime-assembly.test.ts +310 -300
- package/src/__tests__/conversation-runtime-workspace.test.ts +105 -45
- package/src/__tests__/conversation-slash-commands.test.ts +8 -42
- package/src/__tests__/conversation-slash-queue.test.ts +6 -1
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +84 -0
- package/src/__tests__/conversation-sync-tags.test.ts +27 -15
- package/src/__tests__/conversation-title-service.test.ts +135 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +17 -16
- package/src/__tests__/conversation-workspace-injection.test.ts +67 -2
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +7 -6
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/cross-provider-web-search.test.ts +214 -1
- package/src/__tests__/db-acp-history.test.ts +101 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +5 -0
- package/src/__tests__/dm-persistence.test.ts +5 -1
- package/src/__tests__/dynamic-page-surface.test.ts +31 -0
- package/src/__tests__/empty-response-hook.test.ts +304 -0
- package/src/__tests__/feature-flag-test-helpers.ts +2 -2
- package/src/__tests__/file-write-tool.test.ts +63 -0
- package/src/__tests__/gateway-only-guard.test.ts +12 -2
- package/src/__tests__/gemini-image-service.test.ts +13 -0
- 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__/helpers/mock-provider.ts +110 -0
- package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
- package/src/__tests__/history-repair-hook.test.ts +1 -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__/identity-intro-cache.test.ts +12 -100
- package/src/__tests__/identity-routes.test.ts +248 -7
- package/src/__tests__/inbound-slack-persistence.test.ts +5 -1
- package/src/__tests__/injector-background-turn.test.ts +3 -9
- package/src/__tests__/injector-chain.test.ts +139 -275
- package/src/__tests__/injector-disk-pressure.test.ts +75 -41
- package/src/__tests__/injector-document-comments.test.ts +3 -3
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
- package/src/__tests__/injector-v3-suppression.test.ts +31 -37
- package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
- package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
- package/src/__tests__/list-messages-page-latest.test.ts +60 -0
- package/src/__tests__/list-messages-tool-merge.test.ts +20 -0
- package/src/__tests__/llm-usage-store.test.ts +223 -1
- package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
- package/src/__tests__/native-web-search.test.ts +191 -0
- package/src/__tests__/onboarding-template-contract.test.ts +2 -0
- package/src/__tests__/openai-image-service.test.ts +17 -0
- package/src/__tests__/openai-provider.test.ts +31 -1
- package/src/__tests__/{overflow-reduce-pipeline.test.ts → overflow-reduction-loop.test.ts} +64 -284
- package/src/__tests__/persist-unsendable-image.test.ts +215 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +1 -0
- package/src/__tests__/pkb-autoinject.test.ts +2 -5
- package/src/__tests__/plugin-api-shim.test.ts +3 -6
- package/src/__tests__/plugin-bootstrap.test.ts +14 -40
- package/src/__tests__/plugin-registry.test.ts +3 -76
- package/src/__tests__/plugin-types.test.ts +0 -193
- package/src/__tests__/process-message-display-content.test.ts +6 -2
- package/src/__tests__/reaction-persistence.test.ts +1 -1
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
- package/src/__tests__/resolve-trust-class.test.ts +4 -4
- package/src/__tests__/runtime-events-sse-reconnect.test.ts +60 -23
- package/src/__tests__/schedule-routes.test.ts +603 -2
- package/src/__tests__/schedule-store.test.ts +41 -0
- package/src/__tests__/schedule-tools.test.ts +35 -0
- package/src/__tests__/send-endpoint-busy.test.ts +4 -1
- package/src/__tests__/server-history-render.test.ts +314 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +33 -0
- package/src/__tests__/skillssh-files.test.ts +1 -1
- package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
- 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__/system-prompt.test.ts +20 -0
- package/src/__tests__/task-scheduler.test.ts +162 -1
- package/src/__tests__/terminal-tools.test.ts +6 -1
- package/src/__tests__/title-generate-hook.test.ts +319 -0
- package/src/__tests__/tool-error-hook.test.ts +278 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +468 -5
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -2
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
- package/src/__tests__/ui-work-result-surface.test.ts +159 -0
- package/src/__tests__/usage-routes.test.ts +285 -1
- package/src/__tests__/user-plugin-loader.test.ts +54 -286
- package/src/__tests__/voice-session-bridge.test.ts +6 -3
- package/src/__tests__/web-search-backend-failure.test.ts +166 -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-history-db.ts +82 -0
- package/src/acp/__tests__/helpers/exec-file-stub.ts +101 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +137 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
- package/src/acp/__tests__/session-manager-resume.test.ts +736 -0
- package/src/acp/agent-process.ts +61 -1
- package/src/acp/auto-install.test.ts +196 -0
- package/src/acp/auto-install.ts +177 -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 +83 -29
- package/src/acp/resolve-agent.test.ts +320 -7
- package/src/acp/resolve-agent.ts +182 -18
- package/src/acp/resume-hint.ts +25 -0
- package/src/acp/session-manager.ts +495 -73
- package/src/acp/types.ts +8 -0
- package/src/agent/compaction-circuit.ts +60 -102
- package/src/agent/loop.ts +362 -485
- package/src/api/events/assistant-thinking-delta.ts +33 -0
- package/src/api/events/tool-output-chunk.ts +45 -0
- package/src/api/events/tool-use-preview-start.ts +32 -0
- package/src/api/events/trace-event.ts +69 -0
- package/src/api/index.ts +48 -13
- package/src/api/responses/conversation-message.ts +374 -0
- package/src/approvals/guardian-request-resolvers.ts +1 -1
- package/src/avatar/__tests__/avatar-store.test.ts +34 -29
- package/src/background-wake/next-wake.ts +1 -0
- package/src/cli/commands/__tests__/notifications.test.ts +58 -14
- package/src/cli/commands/notifications.ts +112 -60
- 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/assistant-feature-flags.ts +22 -11
- package/src/config/bundled-skills/acp/SKILL.md +83 -31
- package/src/config/bundled-skills/acp/TOOLS.json +4 -4
- package/src/config/bundled-skills/app-builder/SKILL.md +224 -398
- package/src/config/bundled-skills/app-builder/TOOLS.json +29 -0
- 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/references/examples/README.md +17 -0
- package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
- package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
- package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -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-skills/messaging/SKILL.md +0 -7
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/feature-flag-cache.ts +3 -3
- package/src/config/feature-flag-registry.json +48 -7
- package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
- package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/memory-v2.ts +8 -0
- package/src/config/schemas/memory-v3.ts +8 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/seed-inference-profiles.ts +2 -2
- package/src/config/skills.ts +13 -0
- package/src/context/compactor.ts +1 -1
- package/src/context/strip-injections.ts +128 -0
- package/src/context/token-estimator.ts +23 -0
- package/src/context/tool-result-truncation.ts +0 -23
- package/src/context/window-manager.ts +5 -7
- package/src/credential-execution/executable-discovery.ts +16 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
- package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
- package/src/daemon/assistant-attachments.ts +1 -1
- package/src/daemon/config-watcher.ts +2 -2
- package/src/daemon/context-overflow-reducer.ts +0 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +594 -153
- package/src/daemon/conversation-agent-loop.ts +301 -997
- package/src/daemon/conversation-history.ts +5 -4
- package/src/daemon/conversation-lifecycle.ts +3 -4
- package/src/daemon/conversation-messaging.ts +7 -6
- package/src/daemon/conversation-process.ts +11 -16
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +218 -398
- package/src/daemon/conversation-slash.ts +6 -25
- package/src/daemon/conversation-store.ts +9 -90
- package/src/daemon/conversation-surfaces.ts +222 -4
- package/src/daemon/conversation-tool-setup.ts +2 -29
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +32 -20
- package/src/daemon/external-plugins-bootstrap.ts +17 -18
- package/src/daemon/handlers/config-a2a.ts +51 -36
- package/src/daemon/handlers/config-slack-channel.ts +20 -14
- package/src/daemon/handlers/config-telegram.ts +16 -2
- package/src/daemon/handlers/conversations.ts +3 -1
- package/src/daemon/handlers/shared.ts +156 -84
- package/src/daemon/handlers/skills.ts +42 -10
- package/src/daemon/lifecycle.ts +25 -0
- package/src/daemon/message-types/apps.ts +1 -29
- package/src/daemon/message-types/messages.ts +9 -57
- package/src/daemon/message-types/skills.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +136 -3
- package/src/daemon/now-scratchpad.ts +21 -0
- package/src/daemon/orphan-reaper.test.ts +210 -0
- package/src/daemon/orphan-reaper.ts +240 -0
- package/src/daemon/overflow-reduction-loop.ts +230 -0
- package/src/daemon/persist-unsendable-image.ts +117 -0
- package/src/daemon/process-message.ts +1 -3
- package/src/daemon/server.ts +2 -0
- package/src/daemon/trace-emitter.ts +6 -4
- package/src/daemon/trust-context.ts +19 -0
- package/src/daemon/wake-target-adapter.ts +3 -1
- 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/home/home-greeting-cache.ts +24 -1
- package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
- package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
- package/src/ipc/gateway-client.test.ts +2 -2
- package/src/ipc/gateway-client.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/media/gemini-image-service.ts +15 -0
- package/src/media/openai-image-service.ts +14 -0
- package/src/media/types.ts +34 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +56 -0
- package/src/memory/auth-fallback-events-store.ts +94 -0
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/conversation-title-service.ts +65 -41
- package/src/memory/db-init.ts +6 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
- package/src/memory/graph/conversation-graph-memory.ts +65 -0
- package/src/memory/job-handlers/conversation-starters.ts +13 -2
- package/src/memory/jobs-store.ts +33 -0
- package/src/memory/jobs-worker.ts +32 -5
- package/src/memory/llm-usage-store.ts +224 -50
- package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
- package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
- package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
- package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/pkb/autoinject.ts +61 -0
- package/src/memory/pkb/context.ts +50 -0
- package/src/memory/pkb/types.ts +14 -0
- package/src/memory/schedule-attribution-sql.ts +104 -0
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/schema/infrastructure.ts +16 -0
- package/src/memory/usage-grouped-buckets.ts +6 -1
- package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -4
- package/src/memory/v2/consolidation-job.ts +14 -5
- package/src/notifications/conversation-pairing.ts +8 -15
- package/src/notifications/decision-engine.ts +6 -3
- package/src/notifications/home-feed-side-effect.ts +12 -1
- package/src/permissions/prompter.ts +4 -0
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +7 -5
- package/src/plugin-api/types.ts +151 -1
- package/src/plugins/defaults/compaction/compact.ts +59 -0
- package/src/plugins/defaults/compaction/package.json +1 -1
- package/src/plugins/defaults/compaction/register.ts +8 -19
- package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
- package/src/plugins/defaults/empty-response/register.ts +8 -13
- package/src/plugins/defaults/index.ts +2 -18
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +95 -0
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
- package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +288 -81
- 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__/health.test.ts +16 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +4 -4
- 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 +48 -12
- package/src/plugins/defaults/memory-v3-shadow/__tests__/provider-blocks.test.ts +13 -0
- 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 +104 -32
- 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 +96 -30
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +34 -16
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/health.ts +0 -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 +75 -0
- package/src/plugins/defaults/memory-v3-shadow/llm-retry.ts +32 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +8 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +26 -14
- package/src/plugins/defaults/{llm-call → memory-v3-shadow}/package.json +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/provider-blocks.ts +26 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +3 -3
- package/src/plugins/defaults/memory-v3-shadow/register.ts +26 -0
- 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 +51 -45
- 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 +61 -46
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +69 -99
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +8 -0
- package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
- package/src/plugins/defaults/title-generate/package.json +1 -1
- package/src/plugins/defaults/title-generate/register.ts +18 -18
- package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
- package/src/plugins/defaults/tool-error/package.json +1 -1
- package/src/plugins/defaults/tool-error/register.ts +9 -21
- package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
- package/src/plugins/defaults/tool-result-truncate/register.ts +10 -21
- package/src/plugins/defaults/tool-result-truncate/terminal.ts +37 -18
- package/src/plugins/external-api.ts +2 -2
- package/src/plugins/pipeline.ts +6 -305
- package/src/plugins/registry.ts +10 -55
- package/src/plugins/types.ts +62 -797
- package/src/plugins/user-loader.ts +30 -127
- package/src/proactive-artifact/aux-message-injector.ts +4 -4
- package/src/proactive-artifact/job.test.ts +8 -13
- package/src/prompts/__tests__/system-prompt.test.ts +42 -0
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +64 -0
- package/src/prompts/templates/BOOTSTRAP.md +2 -2
- package/src/prompts/templates/system-sections.ts +15 -0
- package/src/providers/anthropic/client.ts +37 -29
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -0
- package/src/providers/openai/chat-completions-provider.ts +44 -0
- package/src/providers/openrouter/client.ts +1 -0
- package/src/providers/placeholder-sentinels.ts +35 -0
- package/src/runtime/__tests__/agent-wake.test.ts +10 -6
- package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
- package/src/runtime/agent-wake.ts +2 -5
- package/src/runtime/assistant-event-hub.ts +37 -7
- package/src/runtime/{conversation-stream-state.ts → assistant-stream-state.ts} +132 -58
- package/src/runtime/channel-approvals.ts +1 -1
- package/src/runtime/http-router.ts +16 -21
- package/src/runtime/http-types.ts +16 -70
- package/src/runtime/interactive-ui.ts +1 -1
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/routes/__tests__/acp-routes.test.ts +283 -55
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
- 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/__tests__/tts-routes.test.ts +6 -2
- package/src/runtime/routes/acp-routes.test.ts +89 -25
- package/src/runtime/routes/acp-routes.ts +81 -29
- package/src/runtime/routes/app-management-routes.ts +6 -117
- package/src/runtime/routes/app-routes.ts +13 -15
- package/src/runtime/routes/approval-routes.ts +1 -1
- package/src/runtime/routes/attachment-routes.ts +26 -15
- package/src/runtime/routes/avatar-routes.ts +26 -0
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/browser-tabs-routes.ts +6 -10
- package/src/runtime/routes/btw-routes.ts +29 -23
- package/src/runtime/routes/consolidation-routes.ts +120 -20
- 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 +3 -1
- package/src/runtime/routes/conversation-routes.ts +372 -185
- package/src/runtime/routes/conversation-starter-routes.ts +13 -7
- package/src/runtime/routes/conversations-import-routes.ts +24 -7
- package/src/runtime/routes/documents-routes.ts +4 -0
- package/src/runtime/routes/domain-routes.ts +51 -37
- package/src/runtime/routes/epoch-millis-range.ts +34 -0
- package/src/runtime/routes/events-routes.ts +28 -34
- package/src/runtime/routes/gateway-log-routes.ts +26 -4
- package/src/runtime/routes/heartbeat-routes.ts +32 -12
- 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-intro-cache.ts +11 -34
- package/src/runtime/routes/identity-routes.ts +224 -18
- package/src/runtime/routes/image-generation-routes.ts +40 -2
- package/src/runtime/routes/inbound-message-handler.ts +1 -1
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/a2a.ts +12 -10
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
- package/src/runtime/routes/integrations/slack/channel.ts +4 -0
- package/src/runtime/routes/integrations/slack/share.ts +27 -6
- package/src/runtime/routes/integrations/telegram.ts +6 -0
- package/src/runtime/routes/integrations/twilio.ts +42 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
- package/src/runtime/routes/log-export-routes.ts +8 -0
- package/src/runtime/routes/memory-v2-routes.ts +15 -8
- package/src/runtime/routes/memory-v3-routes.ts +66 -34
- package/src/runtime/routes/oauth-apps.ts +66 -12
- package/src/runtime/routes/oauth-providers.ts +44 -5
- package/src/runtime/routes/platform-routes.ts +81 -5
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +6 -4
- package/src/runtime/routes/playground/force-compact.ts +1 -1
- package/src/runtime/routes/playground/helpers.ts +1 -1
- package/src/runtime/routes/rename-conversation-routes.ts +5 -0
- package/src/runtime/routes/schedule-routes.ts +152 -42
- package/src/runtime/routes/secret-routes.ts +14 -2
- package/src/runtime/routes/skills-routes.ts +43 -14
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
- package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
- package/src/runtime/routes/trust-rules-routes.ts +26 -2
- package/src/runtime/routes/tts-routes.ts +35 -0
- package/src/runtime/routes/types.ts +66 -8
- package/src/runtime/routes/usage-routes.ts +47 -39
- package/src/runtime/routes/webhook-routes.ts +41 -2
- package/src/runtime/routes/work-items-routes.ts +2 -4
- package/src/runtime/routes/workspace-routes.ts +4 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +6 -0
- package/src/runtime/services/analyze-conversation.ts +2 -2
- package/src/runtime/services/conversation-serializer.ts +1 -1
- package/src/schedule/schedule-store.ts +20 -1
- package/src/schedule/schedule-usage-store.ts +83 -0
- package/src/schedule/scheduler.ts +12 -5
- package/src/signals/cancel.ts +2 -4
- package/src/skills/catalog-files.ts +2 -2
- package/src/skills/catalog-install.ts +3 -0
- package/src/skills/categories-cache.ts +118 -0
- package/src/skills/clawhub-files.ts +1 -2
- package/src/skills/skillssh-files.ts +1 -2
- package/src/subagent/manager.ts +17 -5
- package/src/telemetry/types.ts +29 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +112 -3
- package/src/telemetry/usage-telemetry-reporter.ts +57 -2
- package/src/tools/acp/context.ts +20 -0
- package/src/tools/acp/list-agents.test.ts +7 -1
- package/src/tools/acp/spawn.test.ts +158 -55
- package/src/tools/acp/spawn.ts +47 -72
- package/src/tools/acp/steer.test.ts +105 -8
- package/src/tools/acp/steer.ts +48 -17
- package/src/tools/apps/executors.ts +13 -8
- package/src/tools/executor.ts +1 -53
- package/src/tools/filesystem/write.ts +34 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
- package/src/tools/network/__tests__/web-search.test.ts +11 -3
- package/src/tools/network/web-search-error.test.ts +248 -0
- package/src/tools/network/web-search-error.ts +267 -0
- package/src/tools/network/web-search.ts +207 -48
- package/src/tools/schedule/create.ts +2 -0
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/terminal/safe-env.ts +10 -1
- package/src/tools/ui-surface/definitions.ts +34 -5
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
- package/src/tts/provider-catalog.ts +76 -1
- package/src/util/mutex.ts +47 -0
- package/src/workspace/git-service.ts +1 -42
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
- package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
- package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +117 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/docs/plugins.md +0 -836
- package/examples/plugins/echo/register.ts +0 -184
- package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
- 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__/empty-response-pipeline.test.ts +0 -423
- package/src/__tests__/llm-call-pipeline.test.ts +0 -287
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
- package/src/__tests__/persistence-pipeline.test.ts +0 -503
- package/src/__tests__/pipeline-runner.test.ts +0 -564
- package/src/__tests__/title-generate-pipeline.test.ts +0 -211
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -479
- package/src/__tests__/tool-error-pipeline.test.ts +0 -241
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -341
- package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
- package/src/gallery/default-gallery.ts +0 -1359
- package/src/gallery/gallery-manifest.ts +0 -28
- package/src/home/feature-gate.ts +0 -22
- package/src/memory/v3/provider-blocks.ts +0 -16
- 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/terminal.ts +0 -73
- package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +0 -22
- package/src/plugins/defaults/empty-response/terminal.ts +0 -106
- package/src/plugins/defaults/injectors/package.json +0 -15
- package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +0 -17
- package/src/plugins/defaults/llm-call/register.ts +0 -45
- package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +0 -17
- package/src/plugins/defaults/memory-retrieval/package.json +0 -15
- package/src/plugins/defaults/memory-retrieval/register.ts +0 -181
- 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/persistence/middlewares/persistence.ts +0 -19
- package/src/plugins/defaults/persistence/package.json +0 -15
- package/src/plugins/defaults/persistence/register.ts +0 -38
- package/src/plugins/defaults/persistence/terminal.ts +0 -83
- package/src/plugins/defaults/title-generate/terminal.ts +0 -31
- package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +0 -23
- package/src/plugins/defaults/token-estimate/package.json +0 -15
- package/src/plugins/defaults/token-estimate/register.ts +0 -34
- package/src/plugins/defaults/token-estimate/terminal.ts +0 -40
- package/src/plugins/defaults/tool-error/middlewares/toolError.ts +0 -21
- package/src/plugins/defaults/tool-error/terminal.ts +0 -47
- package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +0 -23
- package/src/plugins/defaults/tool-execute/package.json +0 -15
- package/src/plugins/defaults/tool-execute/register.ts +0 -49
- package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +0 -23
- package/src/plugins/defaults/tool-result-truncate/types.ts +0 -22
- package/src/skills/category-inference.ts +0 -111
- /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__/needle.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__/types.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}/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
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unit tests for AcpAgentProcess capability getters.
|
|
3
|
+
*
|
|
4
|
+
* The getters reflect the InitializeResponse captured during initialize();
|
|
5
|
+
* before that resolves (or after kill()) they must report false. The
|
|
6
|
+
* connection is stubbed directly so no child process is spawned.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { describe, expect, test } from "bun:test";
|
|
10
|
+
|
|
11
|
+
import type { InitializeResponse } from "@agentclientprotocol/sdk";
|
|
12
|
+
|
|
13
|
+
import { AcpAgentProcess } from "../agent-process.js";
|
|
14
|
+
|
|
15
|
+
function makeProcess(): AcpAgentProcess {
|
|
16
|
+
return new AcpAgentProcess(
|
|
17
|
+
"test-agent",
|
|
18
|
+
{ command: "echo", args: [] },
|
|
19
|
+
() => {
|
|
20
|
+
throw new Error("client factory should not be called in this test");
|
|
21
|
+
},
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Injects a stub connection whose initialize() resolves with `response`. */
|
|
26
|
+
function stubConnection(
|
|
27
|
+
proc: AcpAgentProcess,
|
|
28
|
+
response: InitializeResponse,
|
|
29
|
+
): void {
|
|
30
|
+
(
|
|
31
|
+
proc as unknown as {
|
|
32
|
+
connection: { initialize: () => Promise<InitializeResponse> };
|
|
33
|
+
}
|
|
34
|
+
).connection = {
|
|
35
|
+
initialize: () => Promise.resolve(response),
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
describe("AcpAgentProcess capability getters", () => {
|
|
40
|
+
test("both getters return false before initialize() resolves", () => {
|
|
41
|
+
const proc = makeProcess();
|
|
42
|
+
expect(proc.supportsLoadSession).toBe(false);
|
|
43
|
+
expect(proc.supportsSessionResume).toBe(false);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("supportsLoadSession reflects agentCapabilities.loadSession", async () => {
|
|
47
|
+
const proc = makeProcess();
|
|
48
|
+
stubConnection(proc, {
|
|
49
|
+
protocolVersion: 1,
|
|
50
|
+
agentCapabilities: { loadSession: true },
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
await proc.initialize();
|
|
54
|
+
|
|
55
|
+
expect(proc.supportsLoadSession).toBe(true);
|
|
56
|
+
expect(proc.supportsSessionResume).toBe(false);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("supportsSessionResume reflects agentCapabilities.sessionCapabilities.resume", async () => {
|
|
60
|
+
const proc = makeProcess();
|
|
61
|
+
stubConnection(proc, {
|
|
62
|
+
protocolVersion: 1,
|
|
63
|
+
agentCapabilities: { sessionCapabilities: { resume: {} } },
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
await proc.initialize();
|
|
67
|
+
|
|
68
|
+
expect(proc.supportsSessionResume).toBe(true);
|
|
69
|
+
expect(proc.supportsLoadSession).toBe(false);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test("both getters return false when the agent advertises no capabilities", async () => {
|
|
73
|
+
const proc = makeProcess();
|
|
74
|
+
stubConnection(proc, { protocolVersion: 1 });
|
|
75
|
+
|
|
76
|
+
await proc.initialize();
|
|
77
|
+
|
|
78
|
+
expect(proc.supportsLoadSession).toBe(false);
|
|
79
|
+
expect(proc.supportsSessionResume).toBe(false);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test("kill() clears the captured initialize response", async () => {
|
|
83
|
+
const proc = makeProcess();
|
|
84
|
+
stubConnection(proc, {
|
|
85
|
+
protocolVersion: 1,
|
|
86
|
+
agentCapabilities: {
|
|
87
|
+
loadSession: true,
|
|
88
|
+
sessionCapabilities: { resume: {} },
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
await proc.initialize();
|
|
93
|
+
expect(proc.supportsLoadSession).toBe(true);
|
|
94
|
+
expect(proc.supportsSessionResume).toBe(true);
|
|
95
|
+
|
|
96
|
+
proc.kill();
|
|
97
|
+
|
|
98
|
+
expect(proc.supportsLoadSession).toBe(false);
|
|
99
|
+
expect(proc.supportsSessionResume).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe("AcpAgentProcess loadSession/resumeSession", () => {
|
|
104
|
+
/** Injects a stub connection that records loadSession/resumeSession params. */
|
|
105
|
+
function stubSessionConnection(proc: AcpAgentProcess): {
|
|
106
|
+
loadCalls: unknown[];
|
|
107
|
+
resumeCalls: unknown[];
|
|
108
|
+
} {
|
|
109
|
+
const loadCalls: unknown[] = [];
|
|
110
|
+
const resumeCalls: unknown[] = [];
|
|
111
|
+
(proc as unknown as { connection: unknown }).connection = {
|
|
112
|
+
loadSession: (params: unknown) => {
|
|
113
|
+
loadCalls.push(params);
|
|
114
|
+
return Promise.resolve({});
|
|
115
|
+
},
|
|
116
|
+
resumeSession: (params: unknown) => {
|
|
117
|
+
resumeCalls.push(params);
|
|
118
|
+
return Promise.resolve({});
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
return { loadCalls, resumeCalls };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
test("loadSession forwards { sessionId, cwd, mcpServers: [] } to the connection", async () => {
|
|
125
|
+
const proc = makeProcess();
|
|
126
|
+
const { loadCalls } = stubSessionConnection(proc);
|
|
127
|
+
|
|
128
|
+
await proc.loadSession("session-1", "/tmp/project");
|
|
129
|
+
|
|
130
|
+
expect(loadCalls).toEqual([
|
|
131
|
+
{ sessionId: "session-1", cwd: "/tmp/project", mcpServers: [] },
|
|
132
|
+
]);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
test("resumeSession forwards { sessionId, cwd, mcpServers: [] } to the connection", async () => {
|
|
136
|
+
const proc = makeProcess();
|
|
137
|
+
const { resumeCalls } = stubSessionConnection(proc);
|
|
138
|
+
|
|
139
|
+
await proc.resumeSession("session-2", "/tmp/project");
|
|
140
|
+
|
|
141
|
+
expect(resumeCalls).toEqual([
|
|
142
|
+
{ sessionId: "session-2", cwd: "/tmp/project", mcpServers: [] },
|
|
143
|
+
]);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test("loadSession throws when the process is not spawned", async () => {
|
|
147
|
+
const proc = makeProcess();
|
|
148
|
+
|
|
149
|
+
await expect(proc.loadSession("session-1", "/tmp/project")).rejects.toThrow(
|
|
150
|
+
'ACP agent "test-agent" is not spawned',
|
|
151
|
+
);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
test("resumeSession throws when the process is not spawned", async () => {
|
|
155
|
+
const proc = makeProcess();
|
|
156
|
+
|
|
157
|
+
await expect(
|
|
158
|
+
proc.resumeSession("session-1", "/tmp/project"),
|
|
159
|
+
).rejects.toThrow('ACP agent "test-agent" is not spawned');
|
|
160
|
+
});
|
|
161
|
+
});
|
|
@@ -62,3 +62,43 @@ describe("VellumAcpClientHandler.sessionUpdate", () => {
|
|
|
62
62
|
expect(handler.responseText).toBe("");
|
|
63
63
|
});
|
|
64
64
|
});
|
|
65
|
+
|
|
66
|
+
describe("VellumAcpClientHandler replay suppression", () => {
|
|
67
|
+
function messageChunk(text: string): SessionNotification {
|
|
68
|
+
return {
|
|
69
|
+
sessionId: ACP_SESSION_ID,
|
|
70
|
+
update: {
|
|
71
|
+
sessionUpdate: "agent_message_chunk",
|
|
72
|
+
content: { type: "text", text },
|
|
73
|
+
},
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
test("updates received while suppressed are dropped", async () => {
|
|
78
|
+
const { handler, sent } = makeHandler();
|
|
79
|
+
|
|
80
|
+
handler.beginReplaySuppression();
|
|
81
|
+
await handler.sessionUpdate(messageChunk("replayed history"));
|
|
82
|
+
|
|
83
|
+
expect(sent).toHaveLength(0);
|
|
84
|
+
expect(handler.responseText).toBe("");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("updates after endReplaySuppression() flow normally", async () => {
|
|
88
|
+
const { handler, sent } = makeHandler();
|
|
89
|
+
|
|
90
|
+
handler.beginReplaySuppression();
|
|
91
|
+
await handler.sessionUpdate(messageChunk("replayed history"));
|
|
92
|
+
handler.endReplaySuppression();
|
|
93
|
+
await handler.sessionUpdate(messageChunk("live response"));
|
|
94
|
+
|
|
95
|
+
expect(sent).toHaveLength(1);
|
|
96
|
+
expect(sent[0]).toEqual({
|
|
97
|
+
type: "acp_session_update",
|
|
98
|
+
acpSessionId: ACP_SESSION_ID,
|
|
99
|
+
updateType: "agent_message_chunk",
|
|
100
|
+
content: "live response",
|
|
101
|
+
});
|
|
102
|
+
expect(handler.responseText).toBe("live response");
|
|
103
|
+
});
|
|
104
|
+
});
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared test helpers for the `acp_session_history` table.
|
|
3
|
+
*
|
|
4
|
+
* Several suites (session-manager resume/persistence, the ACP route
|
|
5
|
+
* handlers) seed and read history rows directly via SQL; this module
|
|
6
|
+
* consolidates the copy-pasted row shape + insert/read/clear helpers.
|
|
7
|
+
* Tests must run `initializeDb()` themselves before calling these.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { getSqlite } from "../../../memory/db-connection.js";
|
|
11
|
+
|
|
12
|
+
/** Raw column snapshot of an `acp_session_history` row. */
|
|
13
|
+
export interface HistoryRow {
|
|
14
|
+
id: string;
|
|
15
|
+
agent_id: string;
|
|
16
|
+
acp_session_id: string;
|
|
17
|
+
parent_conversation_id: string;
|
|
18
|
+
started_at: number;
|
|
19
|
+
completed_at: number | null;
|
|
20
|
+
status: string;
|
|
21
|
+
stop_reason: string | null;
|
|
22
|
+
error: string | null;
|
|
23
|
+
event_log_json: string;
|
|
24
|
+
cwd: string | null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function clearHistory(): void {
|
|
28
|
+
getSqlite().run("DELETE FROM acp_session_history");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Inserts a history row. Every field except `id` defaults to the values
|
|
33
|
+
* the resume suites were built around (a completed claude run in
|
|
34
|
+
* /tmp/proj); pass explicit values to override, including `null` where the
|
|
35
|
+
* column is nullable (e.g. `cwd: null` for legacy rows).
|
|
36
|
+
*/
|
|
37
|
+
export function insertHistoryRow(row: {
|
|
38
|
+
id: string;
|
|
39
|
+
agentId?: string;
|
|
40
|
+
acpSessionId?: string;
|
|
41
|
+
parentConversationId?: string;
|
|
42
|
+
startedAt?: number;
|
|
43
|
+
completedAt?: number | null;
|
|
44
|
+
status?: string;
|
|
45
|
+
stopReason?: string | null;
|
|
46
|
+
error?: string | null;
|
|
47
|
+
eventLogJson?: string;
|
|
48
|
+
cwd?: string | null;
|
|
49
|
+
}): void {
|
|
50
|
+
getSqlite()
|
|
51
|
+
.query(
|
|
52
|
+
`INSERT INTO acp_session_history (
|
|
53
|
+
id, agent_id, acp_session_id, parent_conversation_id,
|
|
54
|
+
started_at, completed_at, status, stop_reason, error,
|
|
55
|
+
event_log_json, cwd
|
|
56
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
57
|
+
)
|
|
58
|
+
.run(
|
|
59
|
+
row.id,
|
|
60
|
+
row.agentId ?? "claude",
|
|
61
|
+
row.acpSessionId ?? "proto-old",
|
|
62
|
+
row.parentConversationId ?? "conv-1",
|
|
63
|
+
row.startedAt ?? 1234,
|
|
64
|
+
row.completedAt === undefined ? 5678 : row.completedAt,
|
|
65
|
+
row.status ?? "completed",
|
|
66
|
+
row.stopReason === undefined ? "end_turn" : row.stopReason,
|
|
67
|
+
row.error ?? null,
|
|
68
|
+
row.eventLogJson ?? "[]",
|
|
69
|
+
row.cwd === undefined ? "/tmp/proj" : row.cwd,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function readHistoryRow(id: string): HistoryRow | null {
|
|
74
|
+
return getSqlite()
|
|
75
|
+
.query(
|
|
76
|
+
`SELECT id, agent_id, acp_session_id, parent_conversation_id,
|
|
77
|
+
started_at, completed_at, status, stop_reason, error,
|
|
78
|
+
event_log_json, cwd
|
|
79
|
+
FROM acp_session_history WHERE id = ?`,
|
|
80
|
+
)
|
|
81
|
+
.get(id) as HistoryRow | null;
|
|
82
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared test helper: stub `execFile` from `node:child_process` for ACP tests.
|
|
3
|
+
*
|
|
4
|
+
* Several ACP suites (the `acp_spawn` tool's version probes, the adapter
|
|
5
|
+
* auto-installer, and the `/v1/acp/spawn` route) shell out via
|
|
6
|
+
* `execFileWithTimeout`. Each test file used to duplicate the same
|
|
7
|
+
* `mock.module("node:child_process", ...)` + scripted-responses boilerplate;
|
|
8
|
+
* this helper consolidates it, mirroring `which-stub.ts`.
|
|
9
|
+
*
|
|
10
|
+
* Like the other helpers here, the hook is process-global by design (Bun's
|
|
11
|
+
* `mock.module` is process-global). Each test file should call
|
|
12
|
+
* `installExecFileStub()` once at the top level, script calls per test via
|
|
13
|
+
* `execScripts`, and clear state in `beforeEach` via `reset()`.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import * as realChildProcess from "node:child_process";
|
|
17
|
+
import { mock } from "bun:test";
|
|
18
|
+
|
|
19
|
+
type ExecCallback = (
|
|
20
|
+
err: Error | null,
|
|
21
|
+
stdout: string | Buffer,
|
|
22
|
+
stderr: string | Buffer,
|
|
23
|
+
) => void;
|
|
24
|
+
|
|
25
|
+
export interface ExecScript {
|
|
26
|
+
/** When set, the call rejects with this error. */
|
|
27
|
+
error?: Error;
|
|
28
|
+
/** When set, the call resolves with this stdout. */
|
|
29
|
+
stdout?: string;
|
|
30
|
+
/** When set, runs as the call executes (e.g. flip a which-stub flag). */
|
|
31
|
+
onCall?: () => void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
type ExecFileMock = ReturnType<
|
|
35
|
+
typeof mock<
|
|
36
|
+
(
|
|
37
|
+
command: string,
|
|
38
|
+
args: string[],
|
|
39
|
+
options: unknown,
|
|
40
|
+
callback?: ExecCallback,
|
|
41
|
+
) => ReturnType<typeof realChildProcess.execFile>
|
|
42
|
+
>
|
|
43
|
+
>;
|
|
44
|
+
|
|
45
|
+
export interface ExecFileStubHandle {
|
|
46
|
+
/**
|
|
47
|
+
* Per-call scripted responses, keyed by `${command} ${args[0]}` so tests
|
|
48
|
+
* can target `npm ls`, `npm view`, and `npm i` independently. Calls with
|
|
49
|
+
* no script reject with a recognizable "No script for <key>" error.
|
|
50
|
+
*/
|
|
51
|
+
execScripts: Map<string, ExecScript>;
|
|
52
|
+
execFileMock: ExecFileMock;
|
|
53
|
+
/** Clears scripts and recorded calls. Call from `beforeEach`. */
|
|
54
|
+
reset(): void;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Installs a process-global `execFile` mock. Returns handles to drive it. */
|
|
58
|
+
export function installExecFileStub(): ExecFileStubHandle {
|
|
59
|
+
const execScripts: Map<string, ExecScript> = new Map();
|
|
60
|
+
|
|
61
|
+
const execFileMock: ExecFileMock = mock(
|
|
62
|
+
(
|
|
63
|
+
command: string,
|
|
64
|
+
args: string[],
|
|
65
|
+
_options: unknown,
|
|
66
|
+
callback?: ExecCallback,
|
|
67
|
+
) => {
|
|
68
|
+
const key = `${command} ${args[0]}`;
|
|
69
|
+
const script = execScripts.get(key);
|
|
70
|
+
queueMicrotask(() => {
|
|
71
|
+
if (!callback) return;
|
|
72
|
+
if (!script) {
|
|
73
|
+
callback(new Error(`No script for ${key}`), "", "");
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
script.onCall?.();
|
|
77
|
+
if (script.error) {
|
|
78
|
+
callback(script.error, "", "");
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
callback(null, script.stdout ?? "", "");
|
|
82
|
+
});
|
|
83
|
+
// Return value is not used by execFileWithTimeout.
|
|
84
|
+
return {} as ReturnType<typeof realChildProcess.execFile>;
|
|
85
|
+
},
|
|
86
|
+
);
|
|
87
|
+
|
|
88
|
+
mock.module("node:child_process", () => ({
|
|
89
|
+
...realChildProcess,
|
|
90
|
+
execFile: execFileMock,
|
|
91
|
+
}));
|
|
92
|
+
|
|
93
|
+
return {
|
|
94
|
+
execScripts,
|
|
95
|
+
execFileMock,
|
|
96
|
+
reset(): void {
|
|
97
|
+
execScripts.clear();
|
|
98
|
+
execFileMock.mockClear();
|
|
99
|
+
},
|
|
100
|
+
};
|
|
101
|
+
}
|
|
@@ -216,6 +216,22 @@ describe("prepareAgentEnv — claude-agent-acp gating", () => {
|
|
|
216
216
|
expect(prepared.env?.CLAUDE_CODE_OAUTH_TOKEN).toBe("vault-FFF");
|
|
217
217
|
});
|
|
218
218
|
|
|
219
|
+
test("injects the token for the bunx-rewritten claude adapter (adapterCommand gate)", async () => {
|
|
220
|
+
// The resolver rewrites a missing claude-agent-acp binary to run via
|
|
221
|
+
// `bun x --bun <pkg>` and preserves the canonical identity on
|
|
222
|
+
// `adapterCommand`. Without this gate, bunx-resolved spawns would start
|
|
223
|
+
// with no auth and die as zombies on the first prompt.
|
|
224
|
+
seedVaultToken("vault-bunx");
|
|
225
|
+
|
|
226
|
+
const prepared = await prepareAgentEnv({
|
|
227
|
+
command: "bun",
|
|
228
|
+
args: ["x", "--bun", "@agentclientprotocol/claude-agent-acp"],
|
|
229
|
+
adapterCommand: "claude-agent-acp",
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
expect(prepared.env?.CLAUDE_CODE_OAUTH_TOKEN).toBe("vault-bunx");
|
|
233
|
+
});
|
|
234
|
+
|
|
219
235
|
test("does NOT mutate the caller's agentConfig", async () => {
|
|
220
236
|
seedVaultToken("vault-GGG");
|
|
221
237
|
const original = {
|
|
@@ -234,6 +250,115 @@ describe("prepareAgentEnv — claude-agent-acp gating", () => {
|
|
|
234
250
|
});
|
|
235
251
|
});
|
|
236
252
|
|
|
253
|
+
describe("prepareAgentEnv - gemini gating", () => {
|
|
254
|
+
function seedVaultGeminiKey(key: string): void {
|
|
255
|
+
vaultStore.set("acp/gemini_api_key", key);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
test("injects GEMINI_API_KEY from the vault via the broker when agent.env has no override", async () => {
|
|
259
|
+
seedVaultGeminiKey("vault-gem-AAA");
|
|
260
|
+
|
|
261
|
+
const prepared = await prepareAgentEnv({
|
|
262
|
+
command: "gemini",
|
|
263
|
+
args: ["--acp"],
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
expect(prepared.env?.GEMINI_API_KEY).toBe("vault-gem-AAA");
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test("auto-registers metadata with acp_spawn in allowedTools when none exists", async () => {
|
|
270
|
+
seedVaultGeminiKey("vault-gem-auto-meta");
|
|
271
|
+
|
|
272
|
+
await prepareAgentEnv({ command: "gemini", args: ["--acp"] });
|
|
273
|
+
|
|
274
|
+
const meta = metadataStore.get("acp/gemini_api_key");
|
|
275
|
+
expect(meta).toBeDefined();
|
|
276
|
+
expect(meta!.allowedTools).toContain("acp_spawn");
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
test("injects the key for the bunx-rewritten gemini CLI (adapterCommand gate)", async () => {
|
|
280
|
+
seedVaultGeminiKey("vault-gem-bunx");
|
|
281
|
+
|
|
282
|
+
const prepared = await prepareAgentEnv({
|
|
283
|
+
command: "bun",
|
|
284
|
+
args: ["x", "--bun", "@google/gemini-cli", "--acp"],
|
|
285
|
+
adapterCommand: "gemini",
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
expect(prepared.env?.GEMINI_API_KEY).toBe("vault-gem-bunx");
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
test("a vault miss does NOT throw and spawns without GEMINI_API_KEY (key is optional)", async () => {
|
|
292
|
+
const prepared = await prepareAgentEnv({
|
|
293
|
+
command: "gemini",
|
|
294
|
+
args: ["--acp"],
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
expect(prepared.env).not.toHaveProperty("GEMINI_API_KEY");
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
test("respects explicit tool policy that excludes acp_spawn without failing the spawn", async () => {
|
|
301
|
+
metadataStore.set("acp/gemini_api_key", {
|
|
302
|
+
allowedTools: ["other_tool"],
|
|
303
|
+
});
|
|
304
|
+
seedVaultGeminiKey("vault-gem-restricted");
|
|
305
|
+
|
|
306
|
+
const prepared = await prepareAgentEnv({
|
|
307
|
+
command: "gemini",
|
|
308
|
+
args: ["--acp"],
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
expect(prepared.env).not.toHaveProperty("GEMINI_API_KEY");
|
|
312
|
+
const meta = metadataStore.get("acp/gemini_api_key");
|
|
313
|
+
expect(meta!.allowedTools).toEqual(["other_tool"]);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
test("agent.env override wins over the vault entry and skips the broker (precedence pin)", async () => {
|
|
317
|
+
// Seed a vault value but no metadata: if the override path consulted the
|
|
318
|
+
// broker anyway, ensureAcpCredentialPolicy would create metadata here.
|
|
319
|
+
seedVaultGeminiKey("vault-gem-CCC");
|
|
320
|
+
|
|
321
|
+
const prepared = await prepareAgentEnv({
|
|
322
|
+
command: "gemini",
|
|
323
|
+
args: ["--acp"],
|
|
324
|
+
env: { GEMINI_API_KEY: "config-gem-DDD" },
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
expect(prepared.env?.GEMINI_API_KEY).toBe("config-gem-DDD");
|
|
328
|
+
expect(metadataStore.has("acp/gemini_api_key")).toBe(false);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
test("preserves unrelated env vars on agent.env when injecting from the vault", async () => {
|
|
332
|
+
seedVaultGeminiKey("vault-gem-EEE");
|
|
333
|
+
|
|
334
|
+
const prepared = await prepareAgentEnv({
|
|
335
|
+
command: "gemini",
|
|
336
|
+
args: ["--acp"],
|
|
337
|
+
env: { NO_COLOR: "1" },
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
expect(prepared.env?.GEMINI_API_KEY).toBe("vault-gem-EEE");
|
|
341
|
+
expect(prepared.env?.NO_COLOR).toBe("1");
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
test("does NOT mutate the caller's agentConfig", async () => {
|
|
345
|
+
seedVaultGeminiKey("vault-gem-GGG");
|
|
346
|
+
const original = {
|
|
347
|
+
command: "gemini",
|
|
348
|
+
args: ["--acp"],
|
|
349
|
+
env: { OTHER: "keep" },
|
|
350
|
+
};
|
|
351
|
+
const beforeEnv = { ...original.env };
|
|
352
|
+
|
|
353
|
+
const prepared = await prepareAgentEnv(original);
|
|
354
|
+
|
|
355
|
+
expect(prepared).not.toBe(original);
|
|
356
|
+
expect(prepared.env).not.toBe(original.env);
|
|
357
|
+
expect(original.env).toEqual(beforeEnv);
|
|
358
|
+
expect(original.env).not.toHaveProperty("GEMINI_API_KEY");
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
|
|
237
362
|
describe("prepareAgentEnv — non-claude commands", () => {
|
|
238
363
|
test("returns the config unchanged for codex-acp (no required env vars today)", async () => {
|
|
239
364
|
const prepared = await prepareAgentEnv({
|
|
@@ -244,6 +369,18 @@ describe("prepareAgentEnv — non-claude commands", () => {
|
|
|
244
369
|
expect(prepared.env).toEqual({});
|
|
245
370
|
});
|
|
246
371
|
|
|
372
|
+
test("no injection for a bunx-rewritten non-claude adapter", async () => {
|
|
373
|
+
seedVaultToken("vault-should-not-leak");
|
|
374
|
+
|
|
375
|
+
const prepared = await prepareAgentEnv({
|
|
376
|
+
command: "bun",
|
|
377
|
+
args: ["x", "--bun", "@zed-industries/codex-acp"],
|
|
378
|
+
adapterCommand: "codex-acp",
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
expect(prepared.env).toEqual({});
|
|
382
|
+
});
|
|
383
|
+
|
|
247
384
|
test("returns the config unchanged for an unrecognized command basename", async () => {
|
|
248
385
|
seedVaultToken("vault-HHH");
|
|
249
386
|
|
|
@@ -23,35 +23,13 @@ import type { ServerMessage } from "../../daemon/message-protocol.js";
|
|
|
23
23
|
import type { AcpSessionUpdate } from "../../daemon/message-types/acp.js";
|
|
24
24
|
import { getSqlite } from "../../memory/db-connection.js";
|
|
25
25
|
import { initializeDb } from "../../memory/db-init.js";
|
|
26
|
+
import {
|
|
27
|
+
clearHistory,
|
|
28
|
+
insertHistoryRow,
|
|
29
|
+
readHistoryRow,
|
|
30
|
+
} from "./helpers/acp-history-db.js";
|
|
26
31
|
initializeDb();
|
|
27
32
|
|
|
28
|
-
function clearHistory() {
|
|
29
|
-
getSqlite().run("DELETE FROM acp_session_history");
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
interface HistoryRow {
|
|
33
|
-
id: string;
|
|
34
|
-
agent_id: string;
|
|
35
|
-
acp_session_id: string;
|
|
36
|
-
parent_conversation_id: string;
|
|
37
|
-
started_at: number;
|
|
38
|
-
completed_at: number | null;
|
|
39
|
-
status: string;
|
|
40
|
-
stop_reason: string | null;
|
|
41
|
-
error: string | null;
|
|
42
|
-
event_log_json: string;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function readHistoryRow(id: string): HistoryRow | null {
|
|
46
|
-
return getSqlite()
|
|
47
|
-
.query(
|
|
48
|
-
`SELECT id, agent_id, acp_session_id, parent_conversation_id,
|
|
49
|
-
started_at, completed_at, status, stop_reason, error, event_log_json
|
|
50
|
-
FROM acp_session_history WHERE id = ?`,
|
|
51
|
-
)
|
|
52
|
-
.get(id) as HistoryRow | null;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
33
|
/**
|
|
56
34
|
* Builds a manager with a fake session pre-injected and returns the handles
|
|
57
35
|
* needed to drive terminal transitions in tests.
|
|
@@ -61,6 +39,7 @@ function buildSessionWithFakeProcess(opts: {
|
|
|
61
39
|
agentId: string;
|
|
62
40
|
protocolSessionId: string;
|
|
63
41
|
parentConversationId: string;
|
|
42
|
+
cwd?: string;
|
|
64
43
|
}): {
|
|
65
44
|
manager: AcpSessionManager;
|
|
66
45
|
resolvePrompt: (v: { stopReason: string }) => void;
|
|
@@ -126,7 +105,7 @@ function buildSessionWithFakeProcess(opts: {
|
|
|
126
105
|
sendToVellum: wrappedSend,
|
|
127
106
|
currentPrompt: null as Promise<unknown> | null,
|
|
128
107
|
parentConversationId: opts.parentConversationId,
|
|
129
|
-
cwd: "/tmp",
|
|
108
|
+
cwd: opts.cwd ?? "/tmp",
|
|
130
109
|
command: "claude-agent-acp",
|
|
131
110
|
};
|
|
132
111
|
sessions.set(opts.id, entry);
|
|
@@ -345,6 +324,94 @@ describe("AcpSessionManager — terminal persistence", () => {
|
|
|
345
324
|
expect(row!.event_log_json.length).toBeLessThan(256 * 1024 + 1024);
|
|
346
325
|
});
|
|
347
326
|
|
|
327
|
+
test("persists the spawn cwd on terminal transition", async () => {
|
|
328
|
+
const id = "session-cwd-1";
|
|
329
|
+
const handles = buildSessionWithFakeProcess({
|
|
330
|
+
id,
|
|
331
|
+
agentId: "agent-cwd",
|
|
332
|
+
protocolSessionId: "proto-cwd",
|
|
333
|
+
parentConversationId: "conv-cwd",
|
|
334
|
+
cwd: "/Users/me/projects/widget",
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
handles.resolvePrompt({ stopReason: "end_turn" });
|
|
338
|
+
await Promise.resolve();
|
|
339
|
+
await Promise.resolve();
|
|
340
|
+
|
|
341
|
+
const row = readHistoryRow(id);
|
|
342
|
+
expect(row).not.toBeNull();
|
|
343
|
+
expect(row!.cwd).toBe("/Users/me/projects/widget");
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
test("legacy rows without a cwd read back as null", () => {
|
|
347
|
+
insertHistoryRow({
|
|
348
|
+
id: "legacy-row-1",
|
|
349
|
+
agentId: "agent-legacy",
|
|
350
|
+
acpSessionId: "proto-legacy",
|
|
351
|
+
parentConversationId: "conv-legacy",
|
|
352
|
+
startedAt: 1000,
|
|
353
|
+
completedAt: null,
|
|
354
|
+
status: "completed",
|
|
355
|
+
stopReason: null,
|
|
356
|
+
cwd: null,
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
const row = readHistoryRow("legacy-row-1");
|
|
360
|
+
expect(row).not.toBeNull();
|
|
361
|
+
expect(row!.cwd).toBeNull();
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
test("upserts over a pre-existing row with the same id (resumed runs)", async () => {
|
|
365
|
+
const id = "session-upsert-1";
|
|
366
|
+
// Simulate the row left behind by the original run that a resumed run
|
|
367
|
+
// reuses the id of.
|
|
368
|
+
insertHistoryRow({
|
|
369
|
+
id,
|
|
370
|
+
agentId: "agent-up",
|
|
371
|
+
acpSessionId: "proto-up",
|
|
372
|
+
parentConversationId: "conv-up",
|
|
373
|
+
startedAt: 1000,
|
|
374
|
+
completedAt: 2000,
|
|
375
|
+
status: "cancelled",
|
|
376
|
+
stopReason: "daemon_restarted",
|
|
377
|
+
eventLogJson: '[{"old":true}]',
|
|
378
|
+
cwd: "/old/cwd",
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
const handles = buildSessionWithFakeProcess({
|
|
382
|
+
id,
|
|
383
|
+
agentId: "agent-up",
|
|
384
|
+
protocolSessionId: "proto-up",
|
|
385
|
+
parentConversationId: "conv-up",
|
|
386
|
+
cwd: "/new/cwd",
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
handles.emitUpdate({
|
|
390
|
+
type: "acp_session_update",
|
|
391
|
+
acpSessionId: id,
|
|
392
|
+
updateType: "agent_message_chunk",
|
|
393
|
+
content: "from the resumed run",
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
handles.resolvePrompt({ stopReason: "end_turn" });
|
|
397
|
+
await Promise.resolve();
|
|
398
|
+
await Promise.resolve();
|
|
399
|
+
|
|
400
|
+
const count = getSqlite()
|
|
401
|
+
.query(`SELECT COUNT(*) AS n FROM acp_session_history WHERE id = ?`)
|
|
402
|
+
.get(id) as { n: number };
|
|
403
|
+
expect(count.n).toBe(1);
|
|
404
|
+
|
|
405
|
+
const row = readHistoryRow(id);
|
|
406
|
+
expect(row).not.toBeNull();
|
|
407
|
+
expect(row!.status).toBe("completed");
|
|
408
|
+
expect(row!.stop_reason).toBe("end_turn");
|
|
409
|
+
expect(row!.cwd).toBe("/new/cwd");
|
|
410
|
+
const log = JSON.parse(row!.event_log_json) as AcpSessionUpdate[];
|
|
411
|
+
expect(log).toHaveLength(1);
|
|
412
|
+
expect(log[0]).toMatchObject({ content: "from the resumed run" });
|
|
413
|
+
});
|
|
414
|
+
|
|
348
415
|
test("persists empty event log when no updates were emitted", async () => {
|
|
349
416
|
const id = "session-empty-1";
|
|
350
417
|
const handles = buildSessionWithFakeProcess({
|