@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
|
@@ -179,6 +179,47 @@ describe("createSchedule (cron)", () => {
|
|
|
179
179
|
expect(retrieved!.cronExpression).toBe("0 * * * *");
|
|
180
180
|
});
|
|
181
181
|
|
|
182
|
+
test("defaults source conversation metadata to null", () => {
|
|
183
|
+
const job = createSchedule({
|
|
184
|
+
name: "No source conversation",
|
|
185
|
+
cronExpression: "0 9 * * *",
|
|
186
|
+
message: "daily check",
|
|
187
|
+
syntax: "cron",
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
expect(job.createdFromConversationId).toBeNull();
|
|
191
|
+
expect(getSchedule(job.id)!.createdFromConversationId).toBeNull();
|
|
192
|
+
|
|
193
|
+
const raw = getRawDb()
|
|
194
|
+
.query("SELECT created_from_conversation_id FROM cron_jobs WHERE id = ?")
|
|
195
|
+
.get(job.id) as { created_from_conversation_id: string | null };
|
|
196
|
+
expect(raw.created_from_conversation_id).toBeNull();
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
test("persists source conversation metadata through create, list, and update", () => {
|
|
200
|
+
const job = createSchedule({
|
|
201
|
+
name: "With source conversation",
|
|
202
|
+
cronExpression: "0 9 * * *",
|
|
203
|
+
message: "daily check",
|
|
204
|
+
syntax: "cron",
|
|
205
|
+
createdFromConversationId: "conv-source",
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
expect(job.createdFromConversationId).toBe("conv-source");
|
|
209
|
+
expect(getSchedule(job.id)!.createdFromConversationId).toBe("conv-source");
|
|
210
|
+
expect(listSchedules()[0].createdFromConversationId).toBe("conv-source");
|
|
211
|
+
|
|
212
|
+
const updated = updateSchedule(job.id, {
|
|
213
|
+
createdFromConversationId: "conv-updated",
|
|
214
|
+
});
|
|
215
|
+
expect(updated!.createdFromConversationId).toBe("conv-updated");
|
|
216
|
+
|
|
217
|
+
const cleared = updateSchedule(job.id, {
|
|
218
|
+
createdFromConversationId: null,
|
|
219
|
+
});
|
|
220
|
+
expect(cleared!.createdFromConversationId).toBeNull();
|
|
221
|
+
});
|
|
222
|
+
|
|
182
223
|
test("stores schedule_syntax in the DB row", () => {
|
|
183
224
|
const job = createSchedule({
|
|
184
225
|
name: "Syntax check",
|
|
@@ -66,6 +66,23 @@ describe("schedule_create tool", () => {
|
|
|
66
66
|
expect(result.content).toContain("Enabled: true");
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
+
test("persists the creating conversation for recurring schedules", async () => {
|
|
70
|
+
const result = await executeScheduleCreate(
|
|
71
|
+
{
|
|
72
|
+
name: "Recurring source",
|
|
73
|
+
expression: "0 9 * * *",
|
|
74
|
+
message: "remember the source",
|
|
75
|
+
},
|
|
76
|
+
ctx,
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
expect(result.isError).toBe(false);
|
|
80
|
+
const row = getRawDb()
|
|
81
|
+
.query("SELECT created_from_conversation_id FROM cron_jobs LIMIT 1")
|
|
82
|
+
.get() as { created_from_conversation_id: string | null };
|
|
83
|
+
expect(row.created_from_conversation_id).toBe(ctx.conversationId);
|
|
84
|
+
});
|
|
85
|
+
|
|
69
86
|
test("creates a disabled schedule", async () => {
|
|
70
87
|
const result = await executeScheduleCreate(
|
|
71
88
|
{
|
|
@@ -192,6 +209,24 @@ describe("schedule_create with fire_at (one-shot)", () => {
|
|
|
192
209
|
expect(result.content).toContain("Status: active");
|
|
193
210
|
});
|
|
194
211
|
|
|
212
|
+
test("persists the creating conversation for one-shot schedules", async () => {
|
|
213
|
+
const futureDate = new Date(Date.now() + 24 * 60 * 60 * 1000).toISOString();
|
|
214
|
+
const result = await executeScheduleCreate(
|
|
215
|
+
{
|
|
216
|
+
name: "One-shot source",
|
|
217
|
+
fire_at: futureDate,
|
|
218
|
+
message: "remember this source too",
|
|
219
|
+
},
|
|
220
|
+
ctx,
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
expect(result.isError).toBe(false);
|
|
224
|
+
const row = getRawDb()
|
|
225
|
+
.query("SELECT created_from_conversation_id FROM cron_jobs LIMIT 1")
|
|
226
|
+
.get() as { created_from_conversation_id: string | null };
|
|
227
|
+
expect(row.created_from_conversation_id).toBe(ctx.conversationId);
|
|
228
|
+
});
|
|
229
|
+
|
|
195
230
|
test("rejects fire_at that is not valid ISO 8601", async () => {
|
|
196
231
|
const result = await executeScheduleCreate(
|
|
197
232
|
{
|
|
@@ -91,11 +91,14 @@ mock.module("../config/loader.js", () => ({
|
|
|
91
91
|
let _conversationFactory: (() => Conversation) | undefined;
|
|
92
92
|
let _approvalGenerator: unknown;
|
|
93
93
|
|
|
94
|
-
mock.module("../daemon/conversation-
|
|
94
|
+
mock.module("../daemon/conversation-registry.js", () => ({
|
|
95
95
|
findConversation: () => {
|
|
96
96
|
if (!_conversationFactory) return undefined;
|
|
97
97
|
return _conversationFactory();
|
|
98
98
|
},
|
|
99
|
+
}));
|
|
100
|
+
|
|
101
|
+
mock.module("../daemon/conversation-store.js", () => ({
|
|
99
102
|
getOrCreateConversation: async (..._args: unknown[]) => {
|
|
100
103
|
if (!_conversationFactory)
|
|
101
104
|
throw new Error("_conversationFactory not set in test");
|
|
@@ -8,6 +8,7 @@ mock.module("../util/logger.js", () => ({
|
|
|
8
8
|
}));
|
|
9
9
|
|
|
10
10
|
import { renderHistoryContent } from "../daemon/handlers/shared.js";
|
|
11
|
+
import type { ToolActivityMetadata } from "../daemon/message-types/web-activity.js";
|
|
11
12
|
import {
|
|
12
13
|
getAttachmentsForMessage,
|
|
13
14
|
linkAttachmentToMessage,
|
|
@@ -226,7 +227,7 @@ describe("renderHistoryContent", () => {
|
|
|
226
227
|
|
|
227
228
|
expect(output.text).toBe("");
|
|
228
229
|
expect(output.toolCalls).toEqual([
|
|
229
|
-
{ name: "web_fetch", input: { url: "https://example.com" } },
|
|
230
|
+
{ id: "tu_1", name: "web_fetch", input: { url: "https://example.com" } },
|
|
230
231
|
]);
|
|
231
232
|
expect(output.toolCallsBeforeText).toBe(true);
|
|
232
233
|
});
|
|
@@ -244,6 +245,7 @@ describe("renderHistoryContent", () => {
|
|
|
244
245
|
|
|
245
246
|
expect(output.toolCalls).toEqual([
|
|
246
247
|
{
|
|
248
|
+
id: "tu_1",
|
|
247
249
|
name: "bash",
|
|
248
250
|
input: { command: "ls" },
|
|
249
251
|
result: "file1.txt\nfile2.txt",
|
|
@@ -265,6 +267,7 @@ describe("renderHistoryContent", () => {
|
|
|
265
267
|
|
|
266
268
|
expect(output.toolCalls).toEqual([
|
|
267
269
|
{
|
|
270
|
+
id: "tu_1",
|
|
268
271
|
name: "bash",
|
|
269
272
|
input: { command: "bad" },
|
|
270
273
|
result: "command not found",
|
|
@@ -273,6 +276,74 @@ describe("renderHistoryContent", () => {
|
|
|
273
276
|
]);
|
|
274
277
|
});
|
|
275
278
|
|
|
279
|
+
test("omits id when the tool_use block carries none", () => {
|
|
280
|
+
const output = renderHistoryContent([
|
|
281
|
+
{ type: "tool_use", name: "bash", input: { command: "ls" } },
|
|
282
|
+
]);
|
|
283
|
+
|
|
284
|
+
// No provider id on the block — emit the entry without an `id` rather than
|
|
285
|
+
// materializing an empty string, so clients fall back to a synthesized id.
|
|
286
|
+
expect(output.toolCalls).toEqual([
|
|
287
|
+
{ name: "bash", input: { command: "ls" } },
|
|
288
|
+
]);
|
|
289
|
+
expect(output.toolCalls[0]).not.toHaveProperty("id");
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
test("carries the provider id for server_tool_use blocks", () => {
|
|
293
|
+
const output = renderHistoryContent([
|
|
294
|
+
{
|
|
295
|
+
type: "server_tool_use",
|
|
296
|
+
id: "srvtu_1",
|
|
297
|
+
name: "web_search",
|
|
298
|
+
input: { query: "vellum" },
|
|
299
|
+
},
|
|
300
|
+
]);
|
|
301
|
+
|
|
302
|
+
expect(output.toolCalls).toEqual([
|
|
303
|
+
{ id: "srvtu_1", name: "web_search", input: { query: "vellum" } },
|
|
304
|
+
]);
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test("synthesizes a positional id when a tool_use lacks a provider id", () => {
|
|
308
|
+
const output = renderHistoryContent(
|
|
309
|
+
[
|
|
310
|
+
{ type: "tool_use", name: "bash", input: { command: "ls" } },
|
|
311
|
+
{ type: "tool_use", name: "bash", input: { command: "pwd" } },
|
|
312
|
+
],
|
|
313
|
+
undefined,
|
|
314
|
+
"msg-1",
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
// Same positional scheme the web client used to synthesize, so snapshot and
|
|
318
|
+
// stream tool calls stay keyed consistently and the client can drop its own
|
|
319
|
+
// fallback once it no longer skews ahead of the daemon.
|
|
320
|
+
expect(output.toolCalls.map((tc) => tc.id)).toEqual([
|
|
321
|
+
"tool-history-msg-1-0",
|
|
322
|
+
"tool-history-msg-1-1",
|
|
323
|
+
]);
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
test("keeps the provider id and only synthesizes for blocks missing one", () => {
|
|
327
|
+
const output = renderHistoryContent(
|
|
328
|
+
[
|
|
329
|
+
{
|
|
330
|
+
type: "tool_use",
|
|
331
|
+
id: "tu_1",
|
|
332
|
+
name: "bash",
|
|
333
|
+
input: { command: "ls" },
|
|
334
|
+
},
|
|
335
|
+
{ type: "tool_use", name: "bash", input: { command: "pwd" } },
|
|
336
|
+
],
|
|
337
|
+
undefined,
|
|
338
|
+
"msg-1",
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
expect(output.toolCalls.map((tc) => tc.id)).toEqual([
|
|
342
|
+
"tu_1",
|
|
343
|
+
"tool-history-msg-1-1",
|
|
344
|
+
]);
|
|
345
|
+
});
|
|
346
|
+
|
|
276
347
|
// ── Persisted risk-option ladders (Phase B of conflation track) ─────────────
|
|
277
348
|
|
|
278
349
|
test("hydrates persisted _risk*Options annotations onto tool calls", () => {
|
|
@@ -315,6 +386,79 @@ describe("renderHistoryContent", () => {
|
|
|
315
386
|
expect(entry.riskDirectoryScopeOptions).toEqual(directoryScopeOptions);
|
|
316
387
|
});
|
|
317
388
|
|
|
389
|
+
// ── Persisted tool activity (web_search / web_fetch) ────────────────────────
|
|
390
|
+
|
|
391
|
+
test("hydrates persisted _activityMetadata onto a tool_use block", () => {
|
|
392
|
+
// Mirrors what `annotatePersistedAssistantMessage` writes so the activity
|
|
393
|
+
// card survives a history reopen instead of degrading to plain text.
|
|
394
|
+
const activityMetadata: ToolActivityMetadata = {
|
|
395
|
+
webSearch: {
|
|
396
|
+
query: "vellum docs",
|
|
397
|
+
provider: "brave",
|
|
398
|
+
resultCount: 1,
|
|
399
|
+
durationMs: 120,
|
|
400
|
+
results: [
|
|
401
|
+
{
|
|
402
|
+
rank: 1,
|
|
403
|
+
title: "Vellum",
|
|
404
|
+
url: "https://vellum.ai",
|
|
405
|
+
domain: "vellum.ai",
|
|
406
|
+
},
|
|
407
|
+
],
|
|
408
|
+
},
|
|
409
|
+
};
|
|
410
|
+
|
|
411
|
+
const output = renderHistoryContent([
|
|
412
|
+
{
|
|
413
|
+
type: "tool_use",
|
|
414
|
+
id: "tu_1",
|
|
415
|
+
name: "web_search",
|
|
416
|
+
input: { query: "vellum docs" },
|
|
417
|
+
_activityMetadata: activityMetadata,
|
|
418
|
+
},
|
|
419
|
+
]);
|
|
420
|
+
|
|
421
|
+
expect(output.toolCalls[0].activityMetadata).toEqual(activityMetadata);
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
test("hydrates persisted _activityMetadata onto a server_tool_use block", () => {
|
|
425
|
+
const activityMetadata: ToolActivityMetadata = {
|
|
426
|
+
webSearch: {
|
|
427
|
+
query: "native search",
|
|
428
|
+
provider: "anthropic-native",
|
|
429
|
+
resultCount: 0,
|
|
430
|
+
durationMs: 80,
|
|
431
|
+
results: [],
|
|
432
|
+
},
|
|
433
|
+
};
|
|
434
|
+
|
|
435
|
+
const output = renderHistoryContent([
|
|
436
|
+
{
|
|
437
|
+
type: "server_tool_use",
|
|
438
|
+
id: "srvtu_1",
|
|
439
|
+
name: "web_search",
|
|
440
|
+
input: { query: "native search" },
|
|
441
|
+
_activityMetadata: activityMetadata,
|
|
442
|
+
},
|
|
443
|
+
]);
|
|
444
|
+
|
|
445
|
+
expect(output.toolCalls[0].activityMetadata).toEqual(activityMetadata);
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
test("ignores non-object _activityMetadata annotations", () => {
|
|
449
|
+
const output = renderHistoryContent([
|
|
450
|
+
{
|
|
451
|
+
type: "tool_use",
|
|
452
|
+
id: "tu_1",
|
|
453
|
+
name: "web_search",
|
|
454
|
+
input: { query: "x" },
|
|
455
|
+
_activityMetadata: "not an object",
|
|
456
|
+
},
|
|
457
|
+
]);
|
|
458
|
+
|
|
459
|
+
expect(output.toolCalls[0].activityMetadata).toBeUndefined();
|
|
460
|
+
});
|
|
461
|
+
|
|
318
462
|
test("ignores non-array _risk*Options annotations", () => {
|
|
319
463
|
// Defensive: a malformed persisted block should not throw or coerce.
|
|
320
464
|
const output = renderHistoryContent([
|
|
@@ -355,6 +499,46 @@ describe("renderHistoryContent", () => {
|
|
|
355
499
|
expect(entry.riskDirectoryScopeOptions).toBeUndefined();
|
|
356
500
|
});
|
|
357
501
|
|
|
502
|
+
test("reads back a persisted confirmation decision from the closed enum", () => {
|
|
503
|
+
// GIVEN a persisted tool_use block stamped with a recorded decision
|
|
504
|
+
const output = renderHistoryContent([
|
|
505
|
+
{
|
|
506
|
+
type: "tool_use",
|
|
507
|
+
id: "tu_1",
|
|
508
|
+
name: "bash",
|
|
509
|
+
input: { command: "rm file" },
|
|
510
|
+
_confirmationDecision: "denied",
|
|
511
|
+
_confirmationLabel: "Run Command",
|
|
512
|
+
},
|
|
513
|
+
]);
|
|
514
|
+
|
|
515
|
+
// WHEN it is rendered into a history tool call
|
|
516
|
+
const [entry] = output.toolCalls;
|
|
517
|
+
|
|
518
|
+
// THEN the decision survives verbatim alongside its label
|
|
519
|
+
expect(entry.confirmationDecision).toBe("denied");
|
|
520
|
+
expect(entry.confirmationLabel).toBe("Run Command");
|
|
521
|
+
});
|
|
522
|
+
|
|
523
|
+
test("drops a _confirmationDecision outside the closed enum", () => {
|
|
524
|
+
// GIVEN a malformed persisted decision the daemon never writes
|
|
525
|
+
const output = renderHistoryContent([
|
|
526
|
+
{
|
|
527
|
+
type: "tool_use",
|
|
528
|
+
id: "tu_1",
|
|
529
|
+
name: "bash",
|
|
530
|
+
input: { command: "ls" },
|
|
531
|
+
_confirmationDecision: "bogus",
|
|
532
|
+
},
|
|
533
|
+
]);
|
|
534
|
+
|
|
535
|
+
// WHEN it is rendered into a history tool call
|
|
536
|
+
const [entry] = output.toolCalls;
|
|
537
|
+
|
|
538
|
+
// THEN the unknown value is not surfaced on the wire
|
|
539
|
+
expect(entry.confirmationDecision).toBeUndefined();
|
|
540
|
+
});
|
|
541
|
+
|
|
358
542
|
test("handles mixed text and tool blocks", () => {
|
|
359
543
|
const output = renderHistoryContent([
|
|
360
544
|
{ type: "text", text: "Let me look that up." },
|
|
@@ -569,6 +753,135 @@ describe("renderHistoryContent", () => {
|
|
|
569
753
|
});
|
|
570
754
|
});
|
|
571
755
|
|
|
756
|
+
describe("renderHistoryContent contentBlocks", () => {
|
|
757
|
+
test("builds an ordered block array while walking interleaved content", () => {
|
|
758
|
+
// GIVEN a turn that interleaves text, reasoning, a tool call, a surface,
|
|
759
|
+
// and trailing text in the raw model content
|
|
760
|
+
const output = renderHistoryContent([
|
|
761
|
+
{ type: "text", text: "before tool" },
|
|
762
|
+
{ type: "thinking", thinking: "reasoning", signature: "sig" },
|
|
763
|
+
{ type: "tool_use", id: "t1", name: "run_command", input: { cmd: "ls" } },
|
|
764
|
+
{ type: "tool_result", tool_use_id: "t1", content: "file.txt" },
|
|
765
|
+
{
|
|
766
|
+
type: "ui_surface",
|
|
767
|
+
surfaceId: "s1",
|
|
768
|
+
surfaceType: "ui_card",
|
|
769
|
+
data: {},
|
|
770
|
+
},
|
|
771
|
+
{ type: "text", text: "after tool" },
|
|
772
|
+
]);
|
|
773
|
+
|
|
774
|
+
// THEN contentBlocks mirrors the walk order
|
|
775
|
+
expect(output.contentBlocks.map((b) => b.type)).toEqual([
|
|
776
|
+
"text",
|
|
777
|
+
"thinking",
|
|
778
|
+
"tool_use",
|
|
779
|
+
"surface",
|
|
780
|
+
"text",
|
|
781
|
+
]);
|
|
782
|
+
expect(output.contentBlocks[0]).toEqual({
|
|
783
|
+
type: "text",
|
|
784
|
+
text: "before tool",
|
|
785
|
+
});
|
|
786
|
+
expect(output.contentBlocks[1]).toEqual({
|
|
787
|
+
type: "thinking",
|
|
788
|
+
thinking: "reasoning",
|
|
789
|
+
});
|
|
790
|
+
expect(output.contentBlocks[4]).toEqual({
|
|
791
|
+
type: "text",
|
|
792
|
+
text: "after tool",
|
|
793
|
+
});
|
|
794
|
+
// AND the tool_use / surface blocks reuse the same objects the positional
|
|
795
|
+
// arrays hold, so the tool result paired in later is reflected here too
|
|
796
|
+
const toolBlock = output.contentBlocks[2];
|
|
797
|
+
expect(toolBlock.type === "tool_use" && toolBlock.toolCall).toBe(
|
|
798
|
+
output.toolCalls[0],
|
|
799
|
+
);
|
|
800
|
+
expect(output.toolCalls[0].result).toBe("file.txt");
|
|
801
|
+
const surfaceBlock = output.contentBlocks[3];
|
|
802
|
+
expect(surfaceBlock.type === "surface" && surfaceBlock.surface).toBe(
|
|
803
|
+
output.surfaces[0],
|
|
804
|
+
);
|
|
805
|
+
// AND no attachment block appears (this turn has no file blocks)
|
|
806
|
+
expect(output.contentBlocks.some((b) => b.type === "attachment")).toBe(
|
|
807
|
+
false,
|
|
808
|
+
);
|
|
809
|
+
});
|
|
810
|
+
|
|
811
|
+
const pdfBlock = {
|
|
812
|
+
type: "file",
|
|
813
|
+
source: {
|
|
814
|
+
type: "base64",
|
|
815
|
+
media_type: "application/pdf",
|
|
816
|
+
filename: "spec.pdf",
|
|
817
|
+
data: Buffer.from("hi").toString("base64"),
|
|
818
|
+
},
|
|
819
|
+
} as const;
|
|
820
|
+
const pdfAttachment = {
|
|
821
|
+
id: "att-1",
|
|
822
|
+
filename: "spec.pdf",
|
|
823
|
+
mimeType: "application/pdf",
|
|
824
|
+
sizeBytes: 2,
|
|
825
|
+
kind: "file",
|
|
826
|
+
} as const;
|
|
827
|
+
|
|
828
|
+
test("inlines an attachment block when hydrated metadata is supplied", () => {
|
|
829
|
+
// GIVEN a turn with text, a file attachment, then more text, and the
|
|
830
|
+
// caller supplies the DB-hydrated metadata for that file block
|
|
831
|
+
const output = renderHistoryContent(
|
|
832
|
+
[
|
|
833
|
+
{ type: "text", text: "see file" },
|
|
834
|
+
pdfBlock,
|
|
835
|
+
{ type: "text", text: "thanks" },
|
|
836
|
+
],
|
|
837
|
+
[pdfAttachment],
|
|
838
|
+
);
|
|
839
|
+
|
|
840
|
+
// THEN the attachment block is placed inline between the two text blocks
|
|
841
|
+
expect(output.contentBlocks).toEqual([
|
|
842
|
+
{ type: "text", text: "see file" },
|
|
843
|
+
{ type: "attachment", attachment: pdfAttachment },
|
|
844
|
+
{ type: "text", text: "thanks" },
|
|
845
|
+
]);
|
|
846
|
+
});
|
|
847
|
+
|
|
848
|
+
test("omits the file block from contentBlocks when no metadata is supplied", () => {
|
|
849
|
+
// GIVEN the same turn but no hydrated metadata (the file still ships via
|
|
850
|
+
// the positional attachments array, just not as an inline block)
|
|
851
|
+
const output = renderHistoryContent([
|
|
852
|
+
{ type: "text", text: "see file" },
|
|
853
|
+
pdfBlock,
|
|
854
|
+
{ type: "text", text: "thanks" },
|
|
855
|
+
]);
|
|
856
|
+
|
|
857
|
+
expect(output.contentBlocks).toEqual([
|
|
858
|
+
{ type: "text", text: "see file" },
|
|
859
|
+
{ type: "text", text: "thanks" },
|
|
860
|
+
]);
|
|
861
|
+
expect(output.attachments.length).toBe(1);
|
|
862
|
+
});
|
|
863
|
+
|
|
864
|
+
test("excludes the trailing attachment-description segment from blocks", () => {
|
|
865
|
+
// GIVEN an attachment-only turn (the legacy text body carries a synthetic
|
|
866
|
+
// attachment description segment for clients without attachment UI)
|
|
867
|
+
const output = renderHistoryContent([pdfBlock], [pdfAttachment]);
|
|
868
|
+
|
|
869
|
+
// THEN the only block is the inlined attachment — the synthetic text
|
|
870
|
+
// segment stays in textSegments but never pollutes contentBlocks
|
|
871
|
+
expect(output.contentBlocks).toEqual([
|
|
872
|
+
{ type: "attachment", attachment: pdfAttachment },
|
|
873
|
+
]);
|
|
874
|
+
expect(output.textSegments.length).toBe(1);
|
|
875
|
+
});
|
|
876
|
+
|
|
877
|
+
test("emits a single text block for the non-array fallback", () => {
|
|
878
|
+
expect(renderHistoryContent("raw string").contentBlocks).toEqual([
|
|
879
|
+
{ type: "text", text: "raw string" },
|
|
880
|
+
]);
|
|
881
|
+
expect(renderHistoryContent(null).contentBlocks).toEqual([]);
|
|
882
|
+
});
|
|
883
|
+
});
|
|
884
|
+
|
|
572
885
|
describe("getAttachmentsForMessage", () => {
|
|
573
886
|
beforeEach(() => {
|
|
574
887
|
const db = getDb();
|
|
@@ -5,6 +5,8 @@
|
|
|
5
5
|
* parses it via the real frontmatter parser, and verifies that `skillFlagKey()`
|
|
6
6
|
* returns the correct key and `resolveSkillStates()` correctly gates the skill.
|
|
7
7
|
*/
|
|
8
|
+
import { readFileSync } from "node:fs";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
8
10
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
9
11
|
|
|
10
12
|
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
@@ -225,3 +227,34 @@ describe("frontmatter feature-flag integration", () => {
|
|
|
225
227
|
expect(resolvedOff.length).toBe(0);
|
|
226
228
|
});
|
|
227
229
|
});
|
|
230
|
+
|
|
231
|
+
// ---------------------------------------------------------------------------
|
|
232
|
+
// Bundled ACP skill: discoverability when ACP is disabled
|
|
233
|
+
// ---------------------------------------------------------------------------
|
|
234
|
+
|
|
235
|
+
describe("bundled acp skill discoverability", () => {
|
|
236
|
+
test("acp skill resolves with the flag off and config.acp disabled (no frontmatter flag gate)", () => {
|
|
237
|
+
// The ACP skill carries its own first-time-setup instructions, so it must
|
|
238
|
+
// stay visible even when the acp flag and config.acp.enabled are both off.
|
|
239
|
+
// Runtime enforcement happens in the ACP tools via isAcpEnabled instead.
|
|
240
|
+
const skillMdPath = fileURLToPath(
|
|
241
|
+
new URL("../config/bundled-skills/acp/SKILL.md", import.meta.url),
|
|
242
|
+
);
|
|
243
|
+
const skillMd = readFileSync(skillMdPath, "utf8");
|
|
244
|
+
|
|
245
|
+
const skill = buildSkillSummary("acp", skillMd);
|
|
246
|
+
expect(skill).not.toBeNull();
|
|
247
|
+
expect(skill!.featureFlag).toBeUndefined();
|
|
248
|
+
expect(skillFlagKey(skill!)).toBeUndefined();
|
|
249
|
+
|
|
250
|
+
// acp flag at its registry default (off) and config.acp disabled.
|
|
251
|
+
const config = makeConfig({
|
|
252
|
+
acp: { enabled: false, maxConcurrentSessions: 4, agents: {} },
|
|
253
|
+
} as Partial<AssistantConfig>);
|
|
254
|
+
|
|
255
|
+
const resolved = resolveSkillStates([skill!], config);
|
|
256
|
+
expect(resolved.length).toBe(1);
|
|
257
|
+
expect(resolved[0].summary.id).toBe("acp");
|
|
258
|
+
expect(resolved[0].state).toBe("enabled");
|
|
259
|
+
});
|
|
260
|
+
});
|
|
@@ -384,7 +384,7 @@ describe("toSlimSkill", () => {
|
|
|
384
384
|
expect(slim!.kind).toBe("catalog");
|
|
385
385
|
expect(slim!.status).toBe("available");
|
|
386
386
|
expect(slim!.origin).toBe("skillssh");
|
|
387
|
-
expect(slim!.category).toBe("
|
|
387
|
+
expect(slim!.category).toBe("integrations");
|
|
388
388
|
expect((slim as any).slug).toBe("owner/repo/my-skill");
|
|
389
389
|
expect((slim as any).sourceRepo).toBe("owner/repo");
|
|
390
390
|
expect((slim as any).installs).toBe(0);
|
|
@@ -174,7 +174,7 @@ import { LLMSchema } from "../config/schemas/llm.js";
|
|
|
174
174
|
import {
|
|
175
175
|
clearConversations,
|
|
176
176
|
setConversation,
|
|
177
|
-
} from "../daemon/conversation-
|
|
177
|
+
} from "../daemon/conversation-registry.js";
|
|
178
178
|
import { CallSiteRoutingProvider } from "../providers/call-site-routing.js";
|
|
179
179
|
import { SubagentManager } from "../subagent/manager.js";
|
|
180
180
|
|
|
@@ -11,7 +11,7 @@ const capturedNotifications: {
|
|
|
11
11
|
message: string;
|
|
12
12
|
}[] = [];
|
|
13
13
|
|
|
14
|
-
mock.module("../daemon/conversation-
|
|
14
|
+
mock.module("../daemon/conversation-registry.js", () => ({
|
|
15
15
|
findConversation: (id: string) => ({
|
|
16
16
|
enqueueMessage: (options: { content: string }) => {
|
|
17
17
|
capturedNotifications.push({
|
|
@@ -23,8 +23,6 @@ mock.module("../daemon/conversation-store.js", () => ({
|
|
|
23
23
|
persistUserMessage: async () => ({ id: "mock-msg", deduplicated: false }),
|
|
24
24
|
runAgentLoop: async () => {},
|
|
25
25
|
}),
|
|
26
|
-
addConversation: () => {},
|
|
27
|
-
removeConversation: () => {},
|
|
28
26
|
}));
|
|
29
27
|
|
|
30
28
|
mock.module("../runtime/assistant-event-hub.js", () => ({
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
clearConversations,
|
|
5
5
|
findConversation,
|
|
6
6
|
setConversation,
|
|
7
|
-
} from "../daemon/conversation-
|
|
7
|
+
} from "../daemon/conversation-registry.js";
|
|
8
8
|
import type { ServerMessage } from "../daemon/message-protocol.js";
|
|
9
9
|
import type { Message } from "../providers/types.js";
|
|
10
10
|
import { SubagentManager } from "../subagent/manager.js";
|
|
@@ -11,7 +11,7 @@ const capturedNotifications: {
|
|
|
11
11
|
message: string;
|
|
12
12
|
}[] = [];
|
|
13
13
|
|
|
14
|
-
mock.module("../daemon/conversation-
|
|
14
|
+
mock.module("../daemon/conversation-registry.js", () => ({
|
|
15
15
|
findConversation: (id: string) => ({
|
|
16
16
|
enqueueMessage: (options: { content: string }) => {
|
|
17
17
|
capturedNotifications.push({
|
|
@@ -23,8 +23,6 @@ mock.module("../daemon/conversation-store.js", () => ({
|
|
|
23
23
|
persistUserMessage: async () => ({ id: "mock-msg", deduplicated: false }),
|
|
24
24
|
runAgentLoop: async () => {},
|
|
25
25
|
}),
|
|
26
|
-
addConversation: () => {},
|
|
27
|
-
removeConversation: () => {},
|
|
28
26
|
}));
|
|
29
27
|
|
|
30
28
|
mock.module("../runtime/assistant-event-hub.js", () => ({
|
|
@@ -36,7 +36,7 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
36
36
|
*/
|
|
37
37
|
const capturedMessages: string[] = [];
|
|
38
38
|
|
|
39
|
-
mock.module("../daemon/conversation-
|
|
39
|
+
mock.module("../daemon/conversation-registry.js", () => ({
|
|
40
40
|
findConversation: (_id: string) => ({
|
|
41
41
|
enqueueMessage: (options: { content: string }) => {
|
|
42
42
|
capturedMessages.push(options.content);
|
|
@@ -45,8 +45,6 @@ mock.module("../daemon/conversation-store.js", () => ({
|
|
|
45
45
|
persistUserMessage: async () => ({ id: "mock-msg", deduplicated: false }),
|
|
46
46
|
runAgentLoop: async () => {},
|
|
47
47
|
}),
|
|
48
|
-
addConversation: () => {},
|
|
49
|
-
removeConversation: () => {},
|
|
50
48
|
}));
|
|
51
49
|
|
|
52
50
|
mock.module("../runtime/assistant-event-hub.js", () => ({
|
|
@@ -32,7 +32,7 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
32
32
|
import {
|
|
33
33
|
clearConversations,
|
|
34
34
|
setConversation,
|
|
35
|
-
} from "../daemon/conversation-
|
|
35
|
+
} from "../daemon/conversation-registry.js";
|
|
36
36
|
import type { Message } from "../providers/types.js";
|
|
37
37
|
import { getSubagentManager } from "../subagent/index.js";
|
|
38
38
|
import { executeSubagentSpawn } from "../tools/subagent/spawn.js";
|
|
@@ -673,6 +673,26 @@ describe("buildSystemPrompt", () => {
|
|
|
673
673
|
expect(result).toContain("Batch independent tool calls");
|
|
674
674
|
});
|
|
675
675
|
|
|
676
|
+
test("bundled communication section renders and sorts before the parallel-tool-calls block", () => {
|
|
677
|
+
// `01-communication` sorts ahead of `01-parallel-tool-calls`, so the
|
|
678
|
+
// communication guidance leads the operational sections.
|
|
679
|
+
const result = buildSystemPrompt();
|
|
680
|
+
expect(result).toContain("## Communication");
|
|
681
|
+
// The core rule: deliberation belongs in private thinking, not text.
|
|
682
|
+
expect(result).toContain(
|
|
683
|
+
"in your private thinking — never in user-facing text",
|
|
684
|
+
);
|
|
685
|
+
// Closes by deferring to the user's established communication preferences.
|
|
686
|
+
expect(result).toContain(
|
|
687
|
+
"Always prioritize communication preferences that you've established",
|
|
688
|
+
);
|
|
689
|
+
const communicationIdx = result.indexOf("## Communication");
|
|
690
|
+
const parallelIdx = result.indexOf("<use_parallel_tool_calls>");
|
|
691
|
+
expect(communicationIdx).toBeGreaterThan(-1);
|
|
692
|
+
expect(parallelIdx).toBeGreaterThan(-1);
|
|
693
|
+
expect(communicationIdx).toBeLessThan(parallelIdx);
|
|
694
|
+
});
|
|
695
|
+
|
|
676
696
|
test("workspace prefix with frontmatter renders body at the very top", () => {
|
|
677
697
|
mkdirSync(SYSTEM_PROMPTS_DIR, { recursive: true });
|
|
678
698
|
writeFileSync(
|