@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
|
@@ -51,9 +51,18 @@ mock.module("../util/logger.js", () => ({
|
|
|
51
51
|
}),
|
|
52
52
|
}));
|
|
53
53
|
|
|
54
|
+
const mockPublishConversationTitleChanged = mock(
|
|
55
|
+
(_conversationId: string, _title: string) => {},
|
|
56
|
+
);
|
|
57
|
+
mock.module("../runtime/sync/resource-sync-events.js", () => ({
|
|
58
|
+
publishConversationTitleChanged: mockPublishConversationTitleChanged,
|
|
59
|
+
}));
|
|
60
|
+
|
|
54
61
|
import {
|
|
55
62
|
generateAndPersistConversationTitle,
|
|
63
|
+
queueGenerateConversationTitle,
|
|
56
64
|
regenerateConversationTitle,
|
|
65
|
+
titleMutex,
|
|
57
66
|
} from "../memory/conversation-title-service.js";
|
|
58
67
|
|
|
59
68
|
describe("conversation-title-service", () => {
|
|
@@ -63,6 +72,7 @@ describe("conversation-title-service", () => {
|
|
|
63
72
|
mockGetMessages.mockClear();
|
|
64
73
|
mockUpdateConversationTitle.mockClear();
|
|
65
74
|
mockGetConfiguredProvider.mockClear();
|
|
75
|
+
mockPublishConversationTitleChanged.mockClear();
|
|
66
76
|
});
|
|
67
77
|
|
|
68
78
|
test("uses the BTW side-chain helper for initial title generation", async () => {
|
|
@@ -87,7 +97,7 @@ describe("conversation-title-service", () => {
|
|
|
87
97
|
systemPrompt: expect.stringContaining("conversation titles"),
|
|
88
98
|
tools: [],
|
|
89
99
|
callSite: "conversationTitle",
|
|
90
|
-
timeoutMs:
|
|
100
|
+
timeoutMs: 15_000,
|
|
91
101
|
}),
|
|
92
102
|
);
|
|
93
103
|
expect(mockUpdateConversationTitle).toHaveBeenCalledWith(
|
|
@@ -95,6 +105,12 @@ describe("conversation-title-service", () => {
|
|
|
95
105
|
"Project kickoff",
|
|
96
106
|
1,
|
|
97
107
|
);
|
|
108
|
+
// Emit is service-native: persisting a title broadcasts the update so
|
|
109
|
+
// every title origin (agent loop, bootstrap, voice) updates clients live.
|
|
110
|
+
expect(mockPublishConversationTitleChanged).toHaveBeenCalledWith(
|
|
111
|
+
"conv-1",
|
|
112
|
+
"Project kickoff",
|
|
113
|
+
);
|
|
98
114
|
});
|
|
99
115
|
|
|
100
116
|
test("regeneration extracts text from JSON content blocks", async () => {
|
|
@@ -205,7 +221,7 @@ describe("conversation-title-service", () => {
|
|
|
205
221
|
systemPrompt: expect.stringContaining("conversation titles"),
|
|
206
222
|
tools: [],
|
|
207
223
|
callSite: "conversationTitle",
|
|
208
|
-
timeoutMs:
|
|
224
|
+
timeoutMs: 15_000,
|
|
209
225
|
}),
|
|
210
226
|
);
|
|
211
227
|
expect(mockUpdateConversationTitle).toHaveBeenCalledWith(
|
|
@@ -354,4 +370,121 @@ describe("conversation-title-service", () => {
|
|
|
354
370
|
expect(call.content).not.toContain("do NOT respond");
|
|
355
371
|
expect(call.systemPrompt).toContain("Do NOT respond");
|
|
356
372
|
});
|
|
373
|
+
|
|
374
|
+
test("queueGenerateConversationTitle serializes concurrent calls", async () => {
|
|
375
|
+
const callOrder: string[] = [];
|
|
376
|
+
let resolveFirst!: () => void;
|
|
377
|
+
const firstBlocked = new Promise<void>((r) => {
|
|
378
|
+
resolveFirst = r;
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
// First call: blocks until we release it
|
|
382
|
+
mockRunBtwSidechain.mockImplementationOnce(async () => {
|
|
383
|
+
callOrder.push("first:start");
|
|
384
|
+
await firstBlocked;
|
|
385
|
+
callOrder.push("first:end");
|
|
386
|
+
return {
|
|
387
|
+
text: "Title One",
|
|
388
|
+
hadTextDeltas: true,
|
|
389
|
+
response: {
|
|
390
|
+
content: [{ type: "text", text: "Title One" }],
|
|
391
|
+
model: "test-model",
|
|
392
|
+
usage: { inputTokens: 10, outputTokens: 5 },
|
|
393
|
+
stopReason: "end_turn",
|
|
394
|
+
},
|
|
395
|
+
};
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
// Second call: resolves immediately
|
|
399
|
+
mockRunBtwSidechain.mockImplementationOnce(async () => {
|
|
400
|
+
callOrder.push("second:start");
|
|
401
|
+
return {
|
|
402
|
+
text: "Title Two",
|
|
403
|
+
hadTextDeltas: true,
|
|
404
|
+
response: {
|
|
405
|
+
content: [{ type: "text", text: "Title Two" }],
|
|
406
|
+
model: "test-model",
|
|
407
|
+
usage: { inputTokens: 10, outputTokens: 5 },
|
|
408
|
+
stopReason: "end_turn",
|
|
409
|
+
},
|
|
410
|
+
};
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
const provider = {
|
|
414
|
+
name: "test-provider",
|
|
415
|
+
sendMessage: mock(async () => {
|
|
416
|
+
throw new Error("should not call directly");
|
|
417
|
+
}),
|
|
418
|
+
};
|
|
419
|
+
|
|
420
|
+
// Fire both calls — without serialization both would start immediately
|
|
421
|
+
queueGenerateConversationTitle({
|
|
422
|
+
conversationId: "conv-1",
|
|
423
|
+
provider,
|
|
424
|
+
userMessage: "first message",
|
|
425
|
+
});
|
|
426
|
+
queueGenerateConversationTitle({
|
|
427
|
+
conversationId: "conv-2",
|
|
428
|
+
provider,
|
|
429
|
+
userMessage: "second message",
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
// Let microtasks settle — only the first call should have started
|
|
433
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
434
|
+
expect(callOrder).toEqual(["first:start"]);
|
|
435
|
+
|
|
436
|
+
// Release the first call
|
|
437
|
+
resolveFirst();
|
|
438
|
+
await titleMutex.withLock(async () => {});
|
|
439
|
+
|
|
440
|
+
// Second should have started only after first finished
|
|
441
|
+
expect(callOrder).toEqual(["first:start", "first:end", "second:start"]);
|
|
442
|
+
});
|
|
443
|
+
|
|
444
|
+
test("queue continues processing after a failed call", async () => {
|
|
445
|
+
// First call: throws
|
|
446
|
+
mockRunBtwSidechain.mockImplementationOnce(async () => {
|
|
447
|
+
throw new Error("provider timeout");
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
// Second call: succeeds
|
|
451
|
+
mockRunBtwSidechain.mockImplementationOnce(async () => ({
|
|
452
|
+
text: "Recovery Title",
|
|
453
|
+
hadTextDeltas: true,
|
|
454
|
+
response: {
|
|
455
|
+
content: [{ type: "text", text: "Recovery Title" }],
|
|
456
|
+
model: "test-model",
|
|
457
|
+
usage: { inputTokens: 10, outputTokens: 5 },
|
|
458
|
+
stopReason: "end_turn",
|
|
459
|
+
},
|
|
460
|
+
}));
|
|
461
|
+
|
|
462
|
+
const provider = {
|
|
463
|
+
name: "test-provider",
|
|
464
|
+
sendMessage: mock(async () => {
|
|
465
|
+
throw new Error("should not call directly");
|
|
466
|
+
}),
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
queueGenerateConversationTitle({
|
|
470
|
+
conversationId: "conv-1",
|
|
471
|
+
provider,
|
|
472
|
+
userMessage: "will fail",
|
|
473
|
+
});
|
|
474
|
+
queueGenerateConversationTitle({
|
|
475
|
+
conversationId: "conv-2",
|
|
476
|
+
provider,
|
|
477
|
+
userMessage: "will succeed",
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
await titleMutex.withLock(async () => {});
|
|
481
|
+
|
|
482
|
+
// Both calls went through — failure didn't break the chain
|
|
483
|
+
expect(mockRunBtwSidechain).toHaveBeenCalledTimes(2);
|
|
484
|
+
// Second conversation got a proper title
|
|
485
|
+
const secondUpdate = (
|
|
486
|
+
mockUpdateConversationTitle.mock.calls as unknown as string[][]
|
|
487
|
+
).find((c) => c[0] === "conv-2" && c[1] === "Recovery Title");
|
|
488
|
+
expect(secondUpdate).toBeTruthy();
|
|
489
|
+
});
|
|
357
490
|
});
|
|
@@ -198,6 +198,7 @@ mock.module("../agent/loop.js", () => ({
|
|
|
198
198
|
}));
|
|
199
199
|
|
|
200
200
|
import { Conversation } from "../daemon/conversation.js";
|
|
201
|
+
import { refreshWorkspaceTopLevelContextIfNeeded } from "../daemon/conversation-workspace.js";
|
|
201
202
|
|
|
202
203
|
// ---------------------------------------------------------------------------
|
|
203
204
|
// Helpers
|
|
@@ -249,7 +250,7 @@ describe("Conversation workspace cache state", () => {
|
|
|
249
250
|
});
|
|
250
251
|
|
|
251
252
|
test("refreshWorkspaceTopLevelContextIfNeeded populates context and clears dirty", () => {
|
|
252
|
-
|
|
253
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
253
254
|
|
|
254
255
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
255
256
|
expect(conversation.getWorkspaceTopLevelContext()).not.toBeNull();
|
|
@@ -265,10 +266,10 @@ describe("Conversation workspace cache state", () => {
|
|
|
265
266
|
});
|
|
266
267
|
|
|
267
268
|
test("refreshWorkspaceTopLevelContextIfNeeded no-ops when not dirty and cache exists", () => {
|
|
268
|
-
|
|
269
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
269
270
|
const first = conversation.getWorkspaceTopLevelContext();
|
|
270
271
|
|
|
271
|
-
|
|
272
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
272
273
|
const second = conversation.getWorkspaceTopLevelContext();
|
|
273
274
|
|
|
274
275
|
// Same reference — no recomputation
|
|
@@ -276,7 +277,7 @@ describe("Conversation workspace cache state", () => {
|
|
|
276
277
|
});
|
|
277
278
|
|
|
278
279
|
test("markWorkspaceTopLevelDirty sets dirty flag", () => {
|
|
279
|
-
|
|
280
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
280
281
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
281
282
|
|
|
282
283
|
conversation.markWorkspaceTopLevelDirty();
|
|
@@ -284,10 +285,10 @@ describe("Conversation workspace cache state", () => {
|
|
|
284
285
|
});
|
|
285
286
|
|
|
286
287
|
test("refresh after marking dirty produces fresh context", () => {
|
|
287
|
-
|
|
288
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
288
289
|
|
|
289
290
|
conversation.markWorkspaceTopLevelDirty();
|
|
290
|
-
|
|
291
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
291
292
|
|
|
292
293
|
expect(conversation.getWorkspaceTopLevelContext()).not.toBeNull();
|
|
293
294
|
expect(conversation.getWorkspaceTopLevelContext()!).toContain(
|
|
@@ -299,7 +300,7 @@ describe("Conversation workspace cache state", () => {
|
|
|
299
300
|
test("renders client-reported host env when set on the conversation", () => {
|
|
300
301
|
conversation.hostHomeDir = "/Users/alice";
|
|
301
302
|
conversation.hostUsername = "alice";
|
|
302
|
-
|
|
303
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
303
304
|
|
|
304
305
|
const block = conversation.getWorkspaceTopLevelContext();
|
|
305
306
|
expect(block).not.toBeNull();
|
|
@@ -309,7 +310,7 @@ describe("Conversation workspace cache state", () => {
|
|
|
309
310
|
|
|
310
311
|
test("falls back to daemon os info when client host env is absent", async () => {
|
|
311
312
|
const { homedir, userInfo } = await import("node:os");
|
|
312
|
-
|
|
313
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
313
314
|
|
|
314
315
|
const block = conversation.getWorkspaceTopLevelContext();
|
|
315
316
|
expect(block).not.toBeNull();
|
|
@@ -320,7 +321,7 @@ describe("Conversation workspace cache state", () => {
|
|
|
320
321
|
test("re-renders with updated host env after marking dirty", () => {
|
|
321
322
|
conversation.hostHomeDir = "/Users/alice";
|
|
322
323
|
conversation.hostUsername = "alice";
|
|
323
|
-
|
|
324
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
324
325
|
expect(conversation.getWorkspaceTopLevelContext()!).toContain(
|
|
325
326
|
"Host home directory: /Users/alice",
|
|
326
327
|
);
|
|
@@ -328,7 +329,7 @@ describe("Conversation workspace cache state", () => {
|
|
|
328
329
|
conversation.hostHomeDir = "/Users/bob";
|
|
329
330
|
conversation.hostUsername = "bob";
|
|
330
331
|
conversation.markWorkspaceTopLevelDirty();
|
|
331
|
-
|
|
332
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
332
333
|
|
|
333
334
|
const block = conversation.getWorkspaceTopLevelContext();
|
|
334
335
|
expect(block).not.toBeNull();
|
|
@@ -344,7 +345,7 @@ describe("Conversation workspace cache state", () => {
|
|
|
344
345
|
// Simulate a macOS turn populating host env.
|
|
345
346
|
conversation.hostHomeDir = "/Users/alice";
|
|
346
347
|
conversation.hostUsername = "alice";
|
|
347
|
-
|
|
348
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
348
349
|
expect(conversation.getWorkspaceTopLevelContext()!).toContain(
|
|
349
350
|
"Host home directory: /Users/alice",
|
|
350
351
|
);
|
|
@@ -355,7 +356,7 @@ describe("Conversation workspace cache state", () => {
|
|
|
355
356
|
conversation.hostHomeDir = undefined;
|
|
356
357
|
conversation.hostUsername = undefined;
|
|
357
358
|
conversation.markWorkspaceTopLevelDirty();
|
|
358
|
-
|
|
359
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
359
360
|
|
|
360
361
|
const block = conversation.getWorkspaceTopLevelContext();
|
|
361
362
|
expect(block).not.toBeNull();
|
|
@@ -381,7 +382,7 @@ describe("Conversation workspace cache state", () => {
|
|
|
381
382
|
expect(conversation.hostUsername).toBe("alice");
|
|
382
383
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(true);
|
|
383
384
|
|
|
384
|
-
|
|
385
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
385
386
|
const block = conversation.getWorkspaceTopLevelContext();
|
|
386
387
|
expect(block!).toContain("Host home directory: /Users/alice");
|
|
387
388
|
expect(block!).toContain("Host username: alice");
|
|
@@ -455,7 +456,7 @@ describe("Conversation workspace cache state", () => {
|
|
|
455
456
|
hostUsername: "alice",
|
|
456
457
|
});
|
|
457
458
|
// Render once so the dirty flag clears.
|
|
458
|
-
|
|
459
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
459
460
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
460
461
|
|
|
461
462
|
// Re-apply the same values — dirty flag should remain false so we don't
|
|
@@ -476,7 +477,7 @@ describe("Conversation workspace cache state", () => {
|
|
|
476
477
|
hostHomeDir: "/Users/alice",
|
|
477
478
|
hostUsername: "alice",
|
|
478
479
|
});
|
|
479
|
-
|
|
480
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
480
481
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
481
482
|
|
|
482
483
|
// New values — should mark dirty so the next render picks them up.
|
|
@@ -500,7 +501,7 @@ describe("Conversation workspace cache state", () => {
|
|
|
500
501
|
|
|
501
502
|
try {
|
|
502
503
|
const tempConversation = makeConversation(workspaceRoot);
|
|
503
|
-
|
|
504
|
+
refreshWorkspaceTopLevelContextIfNeeded(tempConversation);
|
|
504
505
|
|
|
505
506
|
expect(tempConversation.getWorkspaceTopLevelContext()!).toContain(
|
|
506
507
|
`Current conversation attachments: conversations/${legacyDirName}/attachments/`,
|
|
@@ -265,7 +265,12 @@ mock.module("../agent/loop.js", () => ({
|
|
|
265
265
|
content: [{ type: "text", text: "ok" }],
|
|
266
266
|
};
|
|
267
267
|
onEvent({ type: "message_complete", message: assistantMessage });
|
|
268
|
-
return {
|
|
268
|
+
return {
|
|
269
|
+
history: [...messages, assistantMessage],
|
|
270
|
+
exitReason: null,
|
|
271
|
+
appendedNewMessages: true,
|
|
272
|
+
newMessages: [assistantMessage],
|
|
273
|
+
};
|
|
269
274
|
}
|
|
270
275
|
},
|
|
271
276
|
}));
|
|
@@ -290,6 +295,14 @@ mock.module("../memory/canonical-guardian-store.js", () => ({
|
|
|
290
295
|
}));
|
|
291
296
|
|
|
292
297
|
import { Conversation } from "../daemon/conversation.js";
|
|
298
|
+
import {
|
|
299
|
+
clearConversations,
|
|
300
|
+
findConversation,
|
|
301
|
+
removeSubagentConversation,
|
|
302
|
+
setConversation,
|
|
303
|
+
setSubagentConversation,
|
|
304
|
+
} from "../daemon/conversation-registry.js";
|
|
305
|
+
import { resolveWorkspaceTopLevelContext } from "../daemon/conversation-workspace.js";
|
|
293
306
|
import { resetPluginRegistryAndRegisterDefaults } from "../plugins/defaults/index.js";
|
|
294
307
|
|
|
295
308
|
function makeConversation(): Conversation {
|
|
@@ -304,7 +317,7 @@ function makeConversation(): Conversation {
|
|
|
304
317
|
};
|
|
305
318
|
},
|
|
306
319
|
};
|
|
307
|
-
|
|
320
|
+
const conversation = new Conversation(
|
|
308
321
|
"conv-1",
|
|
309
322
|
provider,
|
|
310
323
|
"system prompt",
|
|
@@ -312,6 +325,10 @@ function makeConversation(): Conversation {
|
|
|
312
325
|
"/tmp",
|
|
313
326
|
{ maxTokens: 4096 },
|
|
314
327
|
);
|
|
328
|
+
// Mirror production: top-level conversations are registered in the store, so
|
|
329
|
+
// the workspace injector can resolve them by id via the conversation registry.
|
|
330
|
+
setConversation(conversation.conversationId, conversation);
|
|
331
|
+
return conversation;
|
|
315
332
|
}
|
|
316
333
|
|
|
317
334
|
function messageText(message: Message): string {
|
|
@@ -330,6 +347,7 @@ describe("Conversation workspace injection", () => {
|
|
|
330
347
|
runCalls = [];
|
|
331
348
|
agentLoopScript = () => {};
|
|
332
349
|
scanCallCount = 0;
|
|
350
|
+
clearConversations();
|
|
333
351
|
resetPluginRegistryAndRegisterDefaults();
|
|
334
352
|
});
|
|
335
353
|
|
|
@@ -412,11 +430,58 @@ describe("Conversation workspace injection", () => {
|
|
|
412
430
|
});
|
|
413
431
|
});
|
|
414
432
|
|
|
433
|
+
describe("Conversation workspace injection — subagents", () => {
|
|
434
|
+
beforeEach(() => {
|
|
435
|
+
scanCallCount = 0;
|
|
436
|
+
clearConversations();
|
|
437
|
+
resetPluginRegistryAndRegisterDefaults();
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
test("workspace context resolves for subagent conversations not in the store", () => {
|
|
441
|
+
const provider = {
|
|
442
|
+
name: "mock",
|
|
443
|
+
async sendMessage(): Promise<ProviderResponse> {
|
|
444
|
+
return {
|
|
445
|
+
content: [],
|
|
446
|
+
model: "mock",
|
|
447
|
+
usage: { inputTokens: 0, outputTokens: 0 },
|
|
448
|
+
stopReason: "end_turn",
|
|
449
|
+
};
|
|
450
|
+
},
|
|
451
|
+
};
|
|
452
|
+
const subagentId = "subagent-conv-1";
|
|
453
|
+
const conversation = new Conversation(
|
|
454
|
+
subagentId,
|
|
455
|
+
provider,
|
|
456
|
+
"system prompt",
|
|
457
|
+
() => {},
|
|
458
|
+
"/tmp",
|
|
459
|
+
{ maxTokens: 4096 },
|
|
460
|
+
);
|
|
461
|
+
setSubagentConversation(subagentId, conversation);
|
|
462
|
+
|
|
463
|
+
try {
|
|
464
|
+
// Subagents live only in the manager's index, never in the
|
|
465
|
+
// eviction-managed store, so the store lookup must not see them.
|
|
466
|
+
expect(findConversation(subagentId)).toBeUndefined();
|
|
467
|
+
|
|
468
|
+
// The per-conversation workspace lookup still resolves them, so subagent
|
|
469
|
+
// turns retain workspace grounding.
|
|
470
|
+
const context = resolveWorkspaceTopLevelContext(subagentId);
|
|
471
|
+
expect(context).not.toBeNull();
|
|
472
|
+
expect(context).toContain("Root: /tmp");
|
|
473
|
+
} finally {
|
|
474
|
+
removeSubagentConversation(subagentId, conversation);
|
|
475
|
+
}
|
|
476
|
+
});
|
|
477
|
+
});
|
|
478
|
+
|
|
415
479
|
describe("Conversation workspace dirty-refresh E2E", () => {
|
|
416
480
|
beforeEach(() => {
|
|
417
481
|
runCalls = [];
|
|
418
482
|
agentLoopScript = () => {};
|
|
419
483
|
scanCallCount = 0;
|
|
484
|
+
clearConversations();
|
|
420
485
|
resetPluginRegistryAndRegisterDefaults();
|
|
421
486
|
});
|
|
422
487
|
|
|
@@ -273,6 +273,7 @@ mock.module("../memory/canonical-guardian-store.js", () => ({
|
|
|
273
273
|
}));
|
|
274
274
|
|
|
275
275
|
import { Conversation } from "../daemon/conversation.js";
|
|
276
|
+
import { refreshWorkspaceTopLevelContextIfNeeded } from "../daemon/conversation-workspace.js";
|
|
276
277
|
|
|
277
278
|
function makeConversation(): Conversation {
|
|
278
279
|
const provider = {
|
|
@@ -310,7 +311,7 @@ describe("Conversation workspace dirty on file mutations", () => {
|
|
|
310
311
|
await conversation.loadFromDb();
|
|
311
312
|
|
|
312
313
|
// Prime the cache so dirty=false
|
|
313
|
-
|
|
314
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
314
315
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
315
316
|
|
|
316
317
|
agentLoopScript = (onEvent) => {
|
|
@@ -339,7 +340,7 @@ describe("Conversation workspace dirty on file mutations", () => {
|
|
|
339
340
|
const conversation = makeConversation();
|
|
340
341
|
await conversation.loadFromDb();
|
|
341
342
|
|
|
342
|
-
|
|
343
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
343
344
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
344
345
|
|
|
345
346
|
agentLoopScript = (onEvent) => {
|
|
@@ -370,7 +371,7 @@ describe("Conversation workspace dirty on file mutations", () => {
|
|
|
370
371
|
const conversation = makeConversation();
|
|
371
372
|
await conversation.loadFromDb();
|
|
372
373
|
|
|
373
|
-
|
|
374
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
374
375
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
375
376
|
|
|
376
377
|
agentLoopScript = (onEvent) => {
|
|
@@ -399,7 +400,7 @@ describe("Conversation workspace dirty on file mutations", () => {
|
|
|
399
400
|
const conversation = makeConversation();
|
|
400
401
|
await conversation.loadFromDb();
|
|
401
402
|
|
|
402
|
-
|
|
403
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
403
404
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
404
405
|
|
|
405
406
|
agentLoopScript = (onEvent) => {
|
|
@@ -428,7 +429,7 @@ describe("Conversation workspace dirty on file mutations", () => {
|
|
|
428
429
|
const conversation = makeConversation();
|
|
429
430
|
await conversation.loadFromDb();
|
|
430
431
|
|
|
431
|
-
|
|
432
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
432
433
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
433
434
|
|
|
434
435
|
agentLoopScript = (onEvent) => {
|
|
@@ -457,7 +458,7 @@ describe("Conversation workspace dirty on file mutations", () => {
|
|
|
457
458
|
const conversation = makeConversation();
|
|
458
459
|
await conversation.loadFromDb();
|
|
459
460
|
|
|
460
|
-
|
|
461
|
+
refreshWorkspaceTopLevelContextIfNeeded(conversation);
|
|
461
462
|
expect(conversation.isWorkspaceTopLevelDirty()).toBe(false);
|
|
462
463
|
|
|
463
464
|
agentLoopScript = (onEvent) => {
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests that the conversations import route never persists non-renderable
|
|
3
|
+
* roles. The messages store is UI-facing (`ConversationMessage`), so an
|
|
4
|
+
* imported export carrying agent-context `system` rows must land only its
|
|
5
|
+
* `user`/`assistant` turns — the `system` rows are dropped, not persisted.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
9
|
+
|
|
10
|
+
mock.module("../util/logger.js", () => ({
|
|
11
|
+
getLogger: () =>
|
|
12
|
+
new Proxy({} as Record<string, unknown>, {
|
|
13
|
+
get: () => () => {},
|
|
14
|
+
}),
|
|
15
|
+
}));
|
|
16
|
+
|
|
17
|
+
mock.module("../config/loader.js", () => ({
|
|
18
|
+
getConfig: () => ({
|
|
19
|
+
ui: {},
|
|
20
|
+
model: "test",
|
|
21
|
+
provider: "test",
|
|
22
|
+
memory: { enabled: false },
|
|
23
|
+
rateLimit: { maxRequestsPerMinute: 0 },
|
|
24
|
+
}),
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
mock.module("../memory/indexer.js", () => ({
|
|
28
|
+
indexMessageNow: async () => {},
|
|
29
|
+
}));
|
|
30
|
+
|
|
31
|
+
import { getMessages } from "../memory/conversation-crud.js";
|
|
32
|
+
import { getDb } from "../memory/db-connection.js";
|
|
33
|
+
import { initializeDb } from "../memory/db-init.js";
|
|
34
|
+
import { conversations, messages } from "../memory/schema.js";
|
|
35
|
+
import { ROUTES } from "../runtime/routes/conversations-import-routes.js";
|
|
36
|
+
import type { RouteHandlerArgs } from "../runtime/routes/types.js";
|
|
37
|
+
|
|
38
|
+
initializeDb();
|
|
39
|
+
|
|
40
|
+
function resetTables() {
|
|
41
|
+
const db = getDb();
|
|
42
|
+
db.run("DELETE FROM message_attachments");
|
|
43
|
+
db.run("DELETE FROM attachments");
|
|
44
|
+
db.run("DELETE FROM messages");
|
|
45
|
+
db.run("DELETE FROM conversation_keys");
|
|
46
|
+
db.run("DELETE FROM conversations");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const importHandler = ROUTES.find(
|
|
50
|
+
(r) => r.operationId === "conversations_import",
|
|
51
|
+
)!.handler;
|
|
52
|
+
|
|
53
|
+
describe("conversations import system-row filtering", () => {
|
|
54
|
+
beforeEach(resetTables);
|
|
55
|
+
|
|
56
|
+
test("imports renderable turns but drops system rows", async () => {
|
|
57
|
+
// GIVEN an export whose conversation sandwiches a system row between two
|
|
58
|
+
// renderable turns (e.g. agent-context scaffolding an export carried)
|
|
59
|
+
const body = {
|
|
60
|
+
conversations: [
|
|
61
|
+
{
|
|
62
|
+
sourceKey: "src-1",
|
|
63
|
+
title: "Imported chat",
|
|
64
|
+
messages: [
|
|
65
|
+
{ role: "user", content: "first visible" },
|
|
66
|
+
{ role: "system", content: "agent-context scaffolding" },
|
|
67
|
+
{ role: "assistant", content: "second visible" },
|
|
68
|
+
],
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// WHEN the conversation is imported
|
|
74
|
+
const result = (await importHandler({
|
|
75
|
+
body,
|
|
76
|
+
} as unknown as RouteHandlerArgs)) as {
|
|
77
|
+
ok: boolean;
|
|
78
|
+
imported: number;
|
|
79
|
+
messages: number;
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// THEN the import succeeds and only counts the renderable turns
|
|
83
|
+
expect(result.ok).toBe(true);
|
|
84
|
+
expect(result.imported).toBe(1);
|
|
85
|
+
expect(result.messages).toBe(2);
|
|
86
|
+
|
|
87
|
+
// AND the persisted rows are exactly the user/assistant turns, never the
|
|
88
|
+
// system scaffolding
|
|
89
|
+
const db = getDb();
|
|
90
|
+
const conv = db.select().from(conversations).all()[0];
|
|
91
|
+
const rows = getMessages(conv.id);
|
|
92
|
+
expect(rows.map((m) => m.role)).toEqual(["user", "assistant"]);
|
|
93
|
+
expect(
|
|
94
|
+
db
|
|
95
|
+
.select()
|
|
96
|
+
.from(messages)
|
|
97
|
+
.all()
|
|
98
|
+
.some((m) => m.role === "system"),
|
|
99
|
+
).toBe(false);
|
|
100
|
+
});
|
|
101
|
+
});
|