@vellumai/assistant 0.8.7 → 0.8.8-dev.202606052332.17fc8ea
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Dockerfile +20 -4
- package/bun.lock +2 -2
- package/docker-entrypoint.sh +4 -2
- package/docker-init-apt-root.sh +3 -1
- package/docker-kata-apt-env.sh +3 -1
- package/docker-kata-runtime-family.sh +12 -0
- package/docs/architecture/memory.md +1 -1
- package/examples/plugins/echo/README.md +61 -66
- package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
- package/examples/plugins/echo/hooks/stop.ts +16 -0
- package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
- package/examples/plugins/echo/package.json +1 -2
- package/examples/plugins/echo/src/emit.ts +19 -0
- package/node_modules/@vellumai/skill-host-contracts/src/server-message.ts +3 -3
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
- package/openapi.yaml +3378 -335
- package/package.json +2 -2
- package/scripts/generate-openapi.ts +68 -41
- package/src/__tests__/agent-loop-exit-reason.test.ts +35 -93
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +1 -1
- package/src/__tests__/agent-loop.test.ts +37 -87
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
- package/src/__tests__/annotate-risk-options.test.ts +2 -3
- package/src/__tests__/anthropic-provider.test.ts +95 -2
- package/src/__tests__/app-control-flow.test.ts +1 -1
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +4 -1
- package/src/__tests__/assistant-event-hub.test.ts +25 -0
- package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
- package/src/__tests__/{conversation-stream-state.test.ts → assistant-stream-state.test.ts} +252 -91
- package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
- package/src/__tests__/btw-routes.test.ts +62 -3
- package/src/__tests__/build-persisted-content.test.ts +184 -0
- package/src/__tests__/catalog-files.test.ts +1 -1
- package/src/__tests__/channel-approval-routes.test.ts +1 -1
- package/src/__tests__/channel-approvals.test.ts +1 -1
- package/src/__tests__/clawhub-files.test.ts +1 -1
- package/src/__tests__/compaction-circuit.test.ts +258 -0
- package/src/__tests__/compaction-direct.test.ts +132 -0
- package/src/__tests__/compaction.benchmark.test.ts +0 -30
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +57 -19
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +6 -5
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -7
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +316 -1143
- package/src/__tests__/conversation-agent-loop.test.ts +638 -1655
- package/src/__tests__/conversation-analysis-routes.test.ts +6 -0
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-history-web-search.test.ts +11 -1
- package/src/__tests__/conversation-pairing.test.ts +4 -31
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +6 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -10
- package/src/__tests__/conversation-queue.test.ts +2 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +3 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +6 -5
- package/src/__tests__/conversation-runtime-assembly.test.ts +310 -300
- package/src/__tests__/conversation-runtime-workspace.test.ts +105 -45
- package/src/__tests__/conversation-slash-commands.test.ts +8 -42
- package/src/__tests__/conversation-slash-queue.test.ts +6 -1
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +84 -0
- package/src/__tests__/conversation-sync-tags.test.ts +27 -15
- package/src/__tests__/conversation-title-service.test.ts +135 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +17 -16
- package/src/__tests__/conversation-workspace-injection.test.ts +67 -2
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +7 -6
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/cross-provider-web-search.test.ts +214 -1
- package/src/__tests__/db-acp-history.test.ts +101 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +5 -0
- package/src/__tests__/dm-persistence.test.ts +5 -1
- package/src/__tests__/dynamic-page-surface.test.ts +31 -0
- package/src/__tests__/empty-response-hook.test.ts +304 -0
- package/src/__tests__/feature-flag-test-helpers.ts +2 -2
- package/src/__tests__/file-write-tool.test.ts +63 -0
- package/src/__tests__/gateway-only-guard.test.ts +12 -2
- package/src/__tests__/gemini-image-service.test.ts +13 -0
- package/src/__tests__/guardian-grant-minting.test.ts +1 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +2 -4
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +1 -0
- package/src/__tests__/helpers/mock-provider.ts +110 -0
- package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
- package/src/__tests__/history-repair-hook.test.ts +1 -0
- package/src/__tests__/host-app-control-routes.test.ts +1 -1
- package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
- package/src/__tests__/identity-intro-cache.test.ts +12 -100
- package/src/__tests__/identity-routes.test.ts +248 -7
- package/src/__tests__/inbound-slack-persistence.test.ts +5 -1
- package/src/__tests__/injector-background-turn.test.ts +3 -9
- package/src/__tests__/injector-chain.test.ts +139 -275
- package/src/__tests__/injector-disk-pressure.test.ts +75 -41
- package/src/__tests__/injector-document-comments.test.ts +3 -3
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
- package/src/__tests__/injector-v3-suppression.test.ts +31 -37
- package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
- package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
- package/src/__tests__/list-messages-page-latest.test.ts +60 -0
- package/src/__tests__/list-messages-tool-merge.test.ts +20 -0
- package/src/__tests__/llm-usage-store.test.ts +223 -1
- package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
- package/src/__tests__/native-web-search.test.ts +191 -0
- package/src/__tests__/onboarding-template-contract.test.ts +2 -0
- package/src/__tests__/openai-image-service.test.ts +17 -0
- package/src/__tests__/openai-provider.test.ts +31 -1
- package/src/__tests__/{overflow-reduce-pipeline.test.ts → overflow-reduction-loop.test.ts} +64 -284
- package/src/__tests__/persist-unsendable-image.test.ts +215 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +1 -0
- package/src/__tests__/pkb-autoinject.test.ts +2 -5
- package/src/__tests__/plugin-api-shim.test.ts +3 -6
- package/src/__tests__/plugin-bootstrap.test.ts +14 -40
- package/src/__tests__/plugin-registry.test.ts +3 -76
- package/src/__tests__/plugin-types.test.ts +0 -193
- package/src/__tests__/process-message-display-content.test.ts +6 -2
- package/src/__tests__/reaction-persistence.test.ts +1 -1
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
- package/src/__tests__/resolve-trust-class.test.ts +4 -4
- package/src/__tests__/runtime-events-sse-reconnect.test.ts +60 -23
- package/src/__tests__/schedule-routes.test.ts +603 -2
- package/src/__tests__/schedule-store.test.ts +41 -0
- package/src/__tests__/schedule-tools.test.ts +35 -0
- package/src/__tests__/send-endpoint-busy.test.ts +4 -1
- package/src/__tests__/server-history-render.test.ts +314 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +33 -0
- package/src/__tests__/skillssh-files.test.ts +1 -1
- package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
- package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
- package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
- package/src/__tests__/subagent-manager-notify.test.ts +1 -3
- package/src/__tests__/subagent-notify-parent.test.ts +1 -3
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
- package/src/__tests__/system-prompt.test.ts +20 -0
- package/src/__tests__/task-scheduler.test.ts +162 -1
- package/src/__tests__/terminal-tools.test.ts +6 -1
- package/src/__tests__/title-generate-hook.test.ts +319 -0
- package/src/__tests__/tool-error-hook.test.ts +278 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +468 -5
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -2
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
- package/src/__tests__/ui-work-result-surface.test.ts +159 -0
- package/src/__tests__/usage-routes.test.ts +285 -1
- package/src/__tests__/user-plugin-loader.test.ts +54 -286
- package/src/__tests__/voice-session-bridge.test.ts +6 -3
- package/src/__tests__/web-search-backend-failure.test.ts +166 -0
- package/src/acp/__tests__/agent-process.test.ts +161 -0
- package/src/acp/__tests__/client-handler.test.ts +40 -0
- package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
- package/src/acp/__tests__/helpers/exec-file-stub.ts +101 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +137 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
- package/src/acp/__tests__/session-manager-resume.test.ts +736 -0
- package/src/acp/agent-process.ts +61 -1
- package/src/acp/auto-install.test.ts +196 -0
- package/src/acp/auto-install.ts +177 -0
- package/src/acp/client-handler.ts +31 -0
- package/src/acp/feature-gate.test.ts +48 -0
- package/src/acp/feature-gate.ts +34 -0
- package/src/acp/prepare-agent-env.ts +83 -29
- package/src/acp/resolve-agent.test.ts +320 -7
- package/src/acp/resolve-agent.ts +182 -18
- package/src/acp/resume-hint.ts +25 -0
- package/src/acp/session-manager.ts +495 -73
- package/src/acp/types.ts +8 -0
- package/src/agent/compaction-circuit.ts +60 -102
- package/src/agent/loop.ts +362 -485
- package/src/api/events/assistant-thinking-delta.ts +33 -0
- package/src/api/events/tool-output-chunk.ts +45 -0
- package/src/api/events/tool-use-preview-start.ts +32 -0
- package/src/api/events/trace-event.ts +69 -0
- package/src/api/index.ts +48 -13
- package/src/api/responses/conversation-message.ts +374 -0
- package/src/approvals/guardian-request-resolvers.ts +1 -1
- package/src/avatar/__tests__/avatar-store.test.ts +34 -29
- package/src/background-wake/next-wake.ts +1 -0
- package/src/cli/commands/__tests__/notifications.test.ts +58 -14
- package/src/cli/commands/notifications.ts +112 -60
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
- package/src/config/acp-defaults.test.ts +10 -0
- package/src/config/acp-defaults.ts +6 -0
- package/src/config/assistant-feature-flags.ts +22 -11
- package/src/config/bundled-skills/acp/SKILL.md +83 -31
- package/src/config/bundled-skills/acp/TOOLS.json +4 -4
- package/src/config/bundled-skills/app-builder/SKILL.md +224 -398
- package/src/config/bundled-skills/app-builder/TOOLS.json +29 -0
- package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
- package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
- package/src/config/bundled-skills/app-builder/references/examples/README.md +17 -0
- package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
- package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
- package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
- package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +0 -7
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/feature-flag-cache.ts +3 -3
- package/src/config/feature-flag-registry.json +48 -7
- package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
- package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/memory-v2.ts +8 -0
- package/src/config/schemas/memory-v3.ts +8 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/seed-inference-profiles.ts +2 -2
- package/src/config/skills.ts +13 -0
- package/src/context/compactor.ts +1 -1
- package/src/context/strip-injections.ts +128 -0
- package/src/context/token-estimator.ts +23 -0
- package/src/context/tool-result-truncation.ts +0 -23
- package/src/context/window-manager.ts +5 -7
- package/src/credential-execution/executable-discovery.ts +16 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
- package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
- package/src/daemon/assistant-attachments.ts +1 -1
- package/src/daemon/config-watcher.ts +2 -2
- package/src/daemon/context-overflow-reducer.ts +0 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +594 -153
- package/src/daemon/conversation-agent-loop.ts +301 -997
- package/src/daemon/conversation-history.ts +5 -4
- package/src/daemon/conversation-lifecycle.ts +3 -4
- package/src/daemon/conversation-messaging.ts +7 -6
- package/src/daemon/conversation-process.ts +11 -16
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +218 -398
- package/src/daemon/conversation-slash.ts +6 -25
- package/src/daemon/conversation-store.ts +9 -90
- package/src/daemon/conversation-surfaces.ts +222 -4
- package/src/daemon/conversation-tool-setup.ts +2 -29
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +32 -20
- package/src/daemon/external-plugins-bootstrap.ts +17 -18
- package/src/daemon/handlers/config-a2a.ts +51 -36
- package/src/daemon/handlers/config-slack-channel.ts +20 -14
- package/src/daemon/handlers/config-telegram.ts +16 -2
- package/src/daemon/handlers/conversations.ts +3 -1
- package/src/daemon/handlers/shared.ts +156 -84
- package/src/daemon/handlers/skills.ts +42 -10
- package/src/daemon/lifecycle.ts +25 -0
- package/src/daemon/message-types/apps.ts +1 -29
- package/src/daemon/message-types/messages.ts +9 -57
- package/src/daemon/message-types/skills.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +136 -3
- package/src/daemon/now-scratchpad.ts +21 -0
- package/src/daemon/orphan-reaper.test.ts +210 -0
- package/src/daemon/orphan-reaper.ts +240 -0
- package/src/daemon/overflow-reduction-loop.ts +230 -0
- package/src/daemon/persist-unsendable-image.ts +117 -0
- package/src/daemon/process-message.ts +1 -3
- package/src/daemon/server.ts +2 -0
- package/src/daemon/trace-emitter.ts +6 -4
- package/src/daemon/trust-context.ts +19 -0
- package/src/daemon/wake-target-adapter.ts +3 -1
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
- package/src/heartbeat/heartbeat-run-store.ts +23 -1
- package/src/heartbeat/heartbeat-service.ts +26 -0
- package/src/home/home-greeting-cache.ts +24 -1
- package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
- package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
- package/src/ipc/gateway-client.test.ts +2 -2
- package/src/ipc/gateway-client.ts +3 -3
- package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
- package/src/ipc/skill-routes/memory.ts +4 -2
- package/src/media/gemini-image-service.ts +15 -0
- package/src/media/openai-image-service.ts +14 -0
- package/src/media/types.ts +34 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +56 -0
- package/src/memory/auth-fallback-events-store.ts +94 -0
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/conversation-title-service.ts +65 -41
- package/src/memory/db-init.ts +6 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
- package/src/memory/graph/conversation-graph-memory.ts +65 -0
- package/src/memory/job-handlers/conversation-starters.ts +13 -2
- package/src/memory/jobs-store.ts +33 -0
- package/src/memory/jobs-worker.ts +32 -5
- package/src/memory/llm-usage-store.ts +224 -50
- package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
- package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
- package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
- package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/pkb/autoinject.ts +61 -0
- package/src/memory/pkb/context.ts +50 -0
- package/src/memory/pkb/types.ts +14 -0
- package/src/memory/schedule-attribution-sql.ts +104 -0
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/schema/infrastructure.ts +16 -0
- package/src/memory/usage-grouped-buckets.ts +6 -1
- package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -4
- package/src/memory/v2/consolidation-job.ts +14 -5
- package/src/notifications/conversation-pairing.ts +8 -15
- package/src/notifications/decision-engine.ts +6 -3
- package/src/notifications/home-feed-side-effect.ts +12 -1
- package/src/permissions/prompter.ts +4 -0
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +7 -5
- package/src/plugin-api/types.ts +151 -1
- package/src/plugins/defaults/compaction/compact.ts +59 -0
- package/src/plugins/defaults/compaction/package.json +1 -1
- package/src/plugins/defaults/compaction/register.ts +8 -19
- package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
- package/src/plugins/defaults/empty-response/register.ts +8 -13
- package/src/plugins/defaults/index.ts +2 -18
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +95 -0
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
- package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +288 -81
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/assign.test.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/health.test.ts +16 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/maintain-job.test.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/orchestrate.test.ts +48 -12
- package/src/plugins/defaults/memory-v3-shadow/__tests__/provider-blocks.test.ts +13 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/reconcile.test.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/render-injection.test.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/router.test.ts +104 -32
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +8 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selector.test.ts +96 -30
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +34 -16
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/health.ts +0 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +75 -0
- package/src/plugins/defaults/memory-v3-shadow/llm-retry.ts +32 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +8 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +26 -14
- package/src/plugins/defaults/{llm-call → memory-v3-shadow}/package.json +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/provider-blocks.ts +26 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +3 -3
- package/src/plugins/defaults/memory-v3-shadow/register.ts +26 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/router.ts +51 -45
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selection-log-store.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selector.ts +61 -46
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +69 -99
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +8 -0
- package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
- package/src/plugins/defaults/title-generate/package.json +1 -1
- package/src/plugins/defaults/title-generate/register.ts +18 -18
- package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
- package/src/plugins/defaults/tool-error/package.json +1 -1
- package/src/plugins/defaults/tool-error/register.ts +9 -21
- package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
- package/src/plugins/defaults/tool-result-truncate/register.ts +10 -21
- package/src/plugins/defaults/tool-result-truncate/terminal.ts +37 -18
- package/src/plugins/external-api.ts +2 -2
- package/src/plugins/pipeline.ts +6 -305
- package/src/plugins/registry.ts +10 -55
- package/src/plugins/types.ts +62 -797
- package/src/plugins/user-loader.ts +30 -127
- package/src/proactive-artifact/aux-message-injector.ts +4 -4
- package/src/proactive-artifact/job.test.ts +8 -13
- package/src/prompts/__tests__/system-prompt.test.ts +42 -0
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +64 -0
- package/src/prompts/templates/BOOTSTRAP.md +2 -2
- package/src/prompts/templates/system-sections.ts +15 -0
- package/src/providers/anthropic/client.ts +37 -29
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -0
- package/src/providers/openai/chat-completions-provider.ts +44 -0
- package/src/providers/openrouter/client.ts +1 -0
- package/src/providers/placeholder-sentinels.ts +35 -0
- package/src/runtime/__tests__/agent-wake.test.ts +10 -6
- package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
- package/src/runtime/agent-wake.ts +2 -5
- package/src/runtime/assistant-event-hub.ts +37 -7
- package/src/runtime/{conversation-stream-state.ts → assistant-stream-state.ts} +132 -58
- package/src/runtime/channel-approvals.ts +1 -1
- package/src/runtime/http-router.ts +16 -21
- package/src/runtime/http-types.ts +16 -70
- package/src/runtime/interactive-ui.ts +1 -1
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/routes/__tests__/acp-routes.test.ts +283 -55
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
- package/src/runtime/routes/__tests__/tts-routes.test.ts +6 -2
- package/src/runtime/routes/acp-routes.test.ts +89 -25
- package/src/runtime/routes/acp-routes.ts +81 -29
- package/src/runtime/routes/app-management-routes.ts +6 -117
- package/src/runtime/routes/app-routes.ts +13 -15
- package/src/runtime/routes/approval-routes.ts +1 -1
- package/src/runtime/routes/attachment-routes.ts +26 -15
- package/src/runtime/routes/avatar-routes.ts +26 -0
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/browser-tabs-routes.ts +6 -10
- package/src/runtime/routes/btw-routes.ts +29 -23
- package/src/runtime/routes/consolidation-routes.ts +120 -20
- package/src/runtime/routes/conversation-cli-routes.ts +1 -1
- package/src/runtime/routes/conversation-list-routes.ts +1 -1
- package/src/runtime/routes/conversation-query-routes.ts +3 -1
- package/src/runtime/routes/conversation-routes.ts +372 -185
- package/src/runtime/routes/conversation-starter-routes.ts +13 -7
- package/src/runtime/routes/conversations-import-routes.ts +24 -7
- package/src/runtime/routes/documents-routes.ts +4 -0
- package/src/runtime/routes/domain-routes.ts +51 -37
- package/src/runtime/routes/epoch-millis-range.ts +34 -0
- package/src/runtime/routes/events-routes.ts +28 -34
- package/src/runtime/routes/gateway-log-routes.ts +26 -4
- package/src/runtime/routes/heartbeat-routes.ts +32 -12
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-cu-routes.ts +1 -1
- package/src/runtime/routes/identity-intro-cache.ts +11 -34
- package/src/runtime/routes/identity-routes.ts +224 -18
- package/src/runtime/routes/image-generation-routes.ts +40 -2
- package/src/runtime/routes/inbound-message-handler.ts +1 -1
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/a2a.ts +12 -10
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
- package/src/runtime/routes/integrations/slack/channel.ts +4 -0
- package/src/runtime/routes/integrations/slack/share.ts +27 -6
- package/src/runtime/routes/integrations/telegram.ts +6 -0
- package/src/runtime/routes/integrations/twilio.ts +42 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
- package/src/runtime/routes/log-export-routes.ts +8 -0
- package/src/runtime/routes/memory-v2-routes.ts +15 -8
- package/src/runtime/routes/memory-v3-routes.ts +66 -34
- package/src/runtime/routes/oauth-apps.ts +66 -12
- package/src/runtime/routes/oauth-providers.ts +44 -5
- package/src/runtime/routes/platform-routes.ts +81 -5
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +6 -4
- package/src/runtime/routes/playground/force-compact.ts +1 -1
- package/src/runtime/routes/playground/helpers.ts +1 -1
- package/src/runtime/routes/rename-conversation-routes.ts +5 -0
- package/src/runtime/routes/schedule-routes.ts +152 -42
- package/src/runtime/routes/secret-routes.ts +14 -2
- package/src/runtime/routes/skills-routes.ts +43 -14
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
- package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
- package/src/runtime/routes/trust-rules-routes.ts +26 -2
- package/src/runtime/routes/tts-routes.ts +35 -0
- package/src/runtime/routes/types.ts +66 -8
- package/src/runtime/routes/usage-routes.ts +47 -39
- package/src/runtime/routes/webhook-routes.ts +41 -2
- package/src/runtime/routes/work-items-routes.ts +2 -4
- package/src/runtime/routes/workspace-routes.ts +4 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +6 -0
- package/src/runtime/services/analyze-conversation.ts +2 -2
- package/src/runtime/services/conversation-serializer.ts +1 -1
- package/src/schedule/schedule-store.ts +20 -1
- package/src/schedule/schedule-usage-store.ts +83 -0
- package/src/schedule/scheduler.ts +12 -5
- package/src/signals/cancel.ts +2 -4
- package/src/skills/catalog-files.ts +2 -2
- package/src/skills/catalog-install.ts +3 -0
- package/src/skills/categories-cache.ts +118 -0
- package/src/skills/clawhub-files.ts +1 -2
- package/src/skills/skillssh-files.ts +1 -2
- package/src/subagent/manager.ts +17 -5
- package/src/telemetry/types.ts +29 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +112 -3
- package/src/telemetry/usage-telemetry-reporter.ts +57 -2
- package/src/tools/acp/context.ts +20 -0
- package/src/tools/acp/list-agents.test.ts +7 -1
- package/src/tools/acp/spawn.test.ts +158 -55
- package/src/tools/acp/spawn.ts +47 -72
- package/src/tools/acp/steer.test.ts +105 -8
- package/src/tools/acp/steer.ts +48 -17
- package/src/tools/apps/executors.ts +13 -8
- package/src/tools/executor.ts +1 -53
- package/src/tools/filesystem/write.ts +34 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
- package/src/tools/network/__tests__/web-search.test.ts +11 -3
- package/src/tools/network/web-search-error.test.ts +248 -0
- package/src/tools/network/web-search-error.ts +267 -0
- package/src/tools/network/web-search.ts +207 -48
- package/src/tools/schedule/create.ts +2 -0
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/terminal/safe-env.ts +10 -1
- package/src/tools/ui-surface/definitions.ts +34 -5
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
- package/src/tts/provider-catalog.ts +76 -1
- package/src/util/mutex.ts +47 -0
- package/src/workspace/git-service.ts +1 -42
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
- package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
- package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +117 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/docs/plugins.md +0 -836
- package/examples/plugins/echo/register.ts +0 -184
- package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
- package/src/__tests__/compaction-pipeline.test.ts +0 -210
- package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
- package/src/__tests__/empty-response-pipeline.test.ts +0 -423
- package/src/__tests__/llm-call-pipeline.test.ts +0 -287
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
- package/src/__tests__/persistence-pipeline.test.ts +0 -503
- package/src/__tests__/pipeline-runner.test.ts +0 -564
- package/src/__tests__/title-generate-pipeline.test.ts +0 -211
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -479
- package/src/__tests__/tool-error-pipeline.test.ts +0 -241
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -341
- package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
- package/src/gallery/default-gallery.ts +0 -1359
- package/src/gallery/gallery-manifest.ts +0 -28
- package/src/home/feature-gate.ts +0 -22
- package/src/memory/v3/provider-blocks.ts +0 -16
- package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
- package/src/plugins/defaults/circuit-breaker/package.json +0 -15
- package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
- package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
- package/src/plugins/defaults/compaction/terminal.ts +0 -73
- package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +0 -22
- package/src/plugins/defaults/empty-response/terminal.ts +0 -106
- package/src/plugins/defaults/injectors/package.json +0 -15
- package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +0 -17
- package/src/plugins/defaults/llm-call/register.ts +0 -45
- package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +0 -17
- package/src/plugins/defaults/memory-retrieval/package.json +0 -15
- package/src/plugins/defaults/memory-retrieval/register.ts +0 -181
- package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
- package/src/plugins/defaults/overflow-reduce/package.json +0 -15
- package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
- package/src/plugins/defaults/persistence/middlewares/persistence.ts +0 -19
- package/src/plugins/defaults/persistence/package.json +0 -15
- package/src/plugins/defaults/persistence/register.ts +0 -38
- package/src/plugins/defaults/persistence/terminal.ts +0 -83
- package/src/plugins/defaults/title-generate/terminal.ts +0 -31
- package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +0 -23
- package/src/plugins/defaults/token-estimate/package.json +0 -15
- package/src/plugins/defaults/token-estimate/register.ts +0 -34
- package/src/plugins/defaults/token-estimate/terminal.ts +0 -40
- package/src/plugins/defaults/tool-error/middlewares/toolError.ts +0 -21
- package/src/plugins/defaults/tool-error/terminal.ts +0 -47
- package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +0 -23
- package/src/plugins/defaults/tool-execute/package.json +0 -15
- package/src/plugins/defaults/tool-execute/register.ts +0 -49
- package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +0 -23
- package/src/plugins/defaults/tool-result-truncate/types.ts +0 -22
- package/src/skills/category-inference.ts +0 -111
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/capabilities.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/core.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/needle.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/snapshot.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/tree.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/core.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/needle.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/snapshot.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +0 -0
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared test harness for driving the daemon's native `server_tool_complete`
|
|
3
|
+
* web_search handler in isolation (ATL-727).
|
|
4
|
+
*
|
|
5
|
+
* Both `native-web-search.test.ts` and `web-search-backend-failure.test.ts`
|
|
6
|
+
* exercise the same handler the same way: build a set of mocked
|
|
7
|
+
* `EventHandlerDeps` (capturing emitted `ServerMessage`s and `rlog.warn`
|
|
8
|
+
* records), then drive a `server_tool_start` → `server_tool_complete` pair.
|
|
9
|
+
* This module is the single source of truth for that harness so the two suites
|
|
10
|
+
* cannot drift apart.
|
|
11
|
+
*
|
|
12
|
+
* Note: each consuming test file must still install its own
|
|
13
|
+
* `mock.module(...)` stubs for the daemon collaborators the handler imports at
|
|
14
|
+
* load time (config loader, conversation-crud, llm-request-log-store), because
|
|
15
|
+
* Bun's `mock.module()` is scoped to the file that registers it.
|
|
16
|
+
*/
|
|
17
|
+
import type {
|
|
18
|
+
EventHandlerDeps,
|
|
19
|
+
EventHandlerState,
|
|
20
|
+
} from "../../daemon/conversation-agent-loop-handlers.js";
|
|
21
|
+
import { dispatchAgentEvent } from "../../daemon/conversation-agent-loop-handlers.js";
|
|
22
|
+
import type { ServerMessage } from "../../daemon/message-protocol.js";
|
|
23
|
+
|
|
24
|
+
/** A `tool_result` `ServerMessage` emitted by the handler. */
|
|
25
|
+
export type ToolResultEvent = Extract<ServerMessage, { type: "tool_result" }>;
|
|
26
|
+
|
|
27
|
+
/** A captured `rlog.warn(obj, msg)` call. */
|
|
28
|
+
export interface LogRecord {
|
|
29
|
+
obj: Record<string, unknown>;
|
|
30
|
+
msg?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface HandlerHarness {
|
|
34
|
+
deps: EventHandlerDeps;
|
|
35
|
+
/** Every `ServerMessage` the handler emitted via `onEvent`. */
|
|
36
|
+
events: ServerMessage[];
|
|
37
|
+
/** Every `rlog.warn(obj, msg)` call the handler made. */
|
|
38
|
+
warnings: LogRecord[];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Build mocked handler deps that capture emitted events and warn logs. */
|
|
42
|
+
export function createHandlerDeps(reqId = "req-web-search"): HandlerHarness {
|
|
43
|
+
const events: ServerMessage[] = [];
|
|
44
|
+
const warnings: LogRecord[] = [];
|
|
45
|
+
const rlog = {
|
|
46
|
+
warn: (obj: Record<string, unknown>, msg?: string) =>
|
|
47
|
+
warnings.push({ obj, msg }),
|
|
48
|
+
info: () => {},
|
|
49
|
+
error: () => {},
|
|
50
|
+
debug: () => {},
|
|
51
|
+
trace: () => {},
|
|
52
|
+
fatal: () => {},
|
|
53
|
+
};
|
|
54
|
+
const deps = {
|
|
55
|
+
ctx: {
|
|
56
|
+
conversationId: "conv-web-search",
|
|
57
|
+
provider: { name: "anthropic" },
|
|
58
|
+
traceEmitter: { emit: () => {} },
|
|
59
|
+
streamThinking: false,
|
|
60
|
+
emitActivityState: () => {},
|
|
61
|
+
markWorkspaceTopLevelDirty: () => {},
|
|
62
|
+
currentTurnSurfaces: [],
|
|
63
|
+
} as unknown as EventHandlerDeps["ctx"],
|
|
64
|
+
onEvent: (msg: ServerMessage) => events.push(msg),
|
|
65
|
+
reqId,
|
|
66
|
+
isFirstMessage: false,
|
|
67
|
+
shouldGenerateTitle: false,
|
|
68
|
+
rlog: rlog as unknown as EventHandlerDeps["rlog"],
|
|
69
|
+
turnChannelContext: {
|
|
70
|
+
userMessageChannel: "vellum",
|
|
71
|
+
assistantMessageChannel: "vellum",
|
|
72
|
+
} as EventHandlerDeps["turnChannelContext"],
|
|
73
|
+
turnInterfaceContext: {
|
|
74
|
+
userMessageInterface: "macos",
|
|
75
|
+
assistantMessageInterface: "macos",
|
|
76
|
+
} as EventHandlerDeps["turnInterfaceContext"],
|
|
77
|
+
applyCompaction: async () => {},
|
|
78
|
+
} as EventHandlerDeps;
|
|
79
|
+
return { deps, events, warnings };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/** The `server_tool_complete` payload shape a test supplies. */
|
|
83
|
+
export interface WebSearchCompleteEvent {
|
|
84
|
+
isError: boolean;
|
|
85
|
+
errorCode?: string;
|
|
86
|
+
errorMessage?: string;
|
|
87
|
+
content?: unknown[];
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** Drive one native (Anthropic) web_search start → complete pair. */
|
|
91
|
+
export async function completeNativeWebSearch(
|
|
92
|
+
state: EventHandlerState,
|
|
93
|
+
deps: EventHandlerDeps,
|
|
94
|
+
toolUseId: string,
|
|
95
|
+
event: WebSearchCompleteEvent,
|
|
96
|
+
): Promise<void> {
|
|
97
|
+
await dispatchAgentEvent(state, deps, {
|
|
98
|
+
type: "server_tool_start",
|
|
99
|
+
name: "web_search",
|
|
100
|
+
toolUseId,
|
|
101
|
+
input: { query: "what is the weather" },
|
|
102
|
+
});
|
|
103
|
+
await dispatchAgentEvent(state, deps, {
|
|
104
|
+
type: "server_tool_complete",
|
|
105
|
+
toolUseId,
|
|
106
|
+
isError: event.isError,
|
|
107
|
+
...(event.errorCode ? { errorCode: event.errorCode } : {}),
|
|
108
|
+
...(event.errorMessage ? { errorMessage: event.errorMessage } : {}),
|
|
109
|
+
content: event.content ?? [],
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** All `tool_result` events emitted so far, in order. */
|
|
114
|
+
export function toolResults(events: ServerMessage[]): ToolResultEvent[] {
|
|
115
|
+
return events.filter((e): e is ToolResultEvent => e.type === "tool_result");
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** The most recent `tool_result` event, if any. */
|
|
119
|
+
export function lastToolResult(
|
|
120
|
+
events: ServerMessage[],
|
|
121
|
+
): ToolResultEvent | undefined {
|
|
122
|
+
const results = toolResults(events);
|
|
123
|
+
return results[results.length - 1];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** The captured `web_search_backend_failure` telemetry warn records. */
|
|
127
|
+
export function backendFailureLogs(warnings: LogRecord[]): LogRecord[] {
|
|
128
|
+
return warnings.filter((w) => w.obj.event === "web_search_backend_failure");
|
|
129
|
+
}
|
|
@@ -47,7 +47,7 @@ interface FakeConversation {
|
|
|
47
47
|
|
|
48
48
|
const conversations = new Map<string, FakeConversation>();
|
|
49
49
|
|
|
50
|
-
mock.module("../daemon/conversation-
|
|
50
|
+
mock.module("../daemon/conversation-registry.js", () => ({
|
|
51
51
|
findConversation: (id: string) => conversations.get(id),
|
|
52
52
|
}));
|
|
53
53
|
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* 4. Untargeted (no targetClientId, no header) → 200 accepted (regression)
|
|
9
9
|
*
|
|
10
10
|
* Resolution goes through conversation.hostCuProxy?.resolve(...). The
|
|
11
|
-
* conversation
|
|
11
|
+
* conversation registry is mocked to return a controlled conversation object.
|
|
12
12
|
*
|
|
13
|
-
* Note: host-cu-routes.ts has a deep import chain (conversation-
|
|
13
|
+
* Note: host-cu-routes.ts has a deep import chain (conversation-registry →
|
|
14
14
|
* conversation.ts → ces-client → service-contracts) that requires mocking
|
|
15
15
|
* before the module loads. We use dynamic imports to ensure all mocks are
|
|
16
16
|
* registered before the route module is evaluated.
|
|
@@ -75,7 +75,7 @@ const conversationStore = new Map<
|
|
|
75
75
|
{ hostCuProxy?: { processObservation: (...args: unknown[]) => void } }
|
|
76
76
|
>();
|
|
77
77
|
|
|
78
|
-
mock.module("../daemon/conversation-
|
|
78
|
+
mock.module("../daemon/conversation-registry.js", () => ({
|
|
79
79
|
findConversation: (conversationId: string) =>
|
|
80
80
|
conversationStore.get(conversationId),
|
|
81
81
|
}));
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Unit tests for the identity intro cache (identity-intro-cache.ts).
|
|
3
3
|
*
|
|
4
|
-
* Validates TTL-based expiration,
|
|
5
|
-
* workspace
|
|
6
|
-
* round-trip get/set behavior.
|
|
4
|
+
* Validates TTL-based expiration, round-trip get/set behavior, and parsing
|
|
5
|
+
* workspace-authored greeting sections.
|
|
7
6
|
*/
|
|
8
7
|
|
|
9
8
|
import { afterEach, describe, expect, mock, test } from "bun:test";
|
|
@@ -45,20 +44,11 @@ mock.module("node:fs", () => ({
|
|
|
45
44
|
},
|
|
46
45
|
}));
|
|
47
46
|
|
|
48
|
-
// Mocked guardian persona — mutable so tests can change it and verify cache
|
|
49
|
-
// invalidation based on the per-user persona file content.
|
|
50
|
-
let guardianPersonaContent: string | null = null;
|
|
51
|
-
|
|
52
|
-
mock.module("../prompts/persona-resolver.js", () => ({
|
|
53
|
-
resolveGuardianPersona: () => guardianPersonaContent,
|
|
54
|
-
}));
|
|
55
|
-
|
|
56
47
|
// ---------------------------------------------------------------------------
|
|
57
48
|
// Imports (after mocks)
|
|
58
49
|
// ---------------------------------------------------------------------------
|
|
59
50
|
|
|
60
51
|
import {
|
|
61
|
-
computeIdentityContentHash,
|
|
62
52
|
getCachedIntro,
|
|
63
53
|
parseGreetingsSection,
|
|
64
54
|
readWorkspaceGreetings,
|
|
@@ -75,7 +65,6 @@ afterEach(() => {
|
|
|
75
65
|
for (const key of Object.keys(workspaceFiles)) {
|
|
76
66
|
delete workspaceFiles[key];
|
|
77
67
|
}
|
|
78
|
-
guardianPersonaContent = null;
|
|
79
68
|
});
|
|
80
69
|
|
|
81
70
|
// ---------------------------------------------------------------------------
|
|
@@ -118,7 +107,6 @@ describe("identity intro cache", () => {
|
|
|
118
107
|
test("round-trip: set then get returns cached greetings array", () => {
|
|
119
108
|
workspaceFiles["IDENTITY.md"] = "- **Name:** Atlas";
|
|
120
109
|
workspaceFiles["SOUL.md"] = "Be playful.";
|
|
121
|
-
guardianPersonaContent = "The user likes coffee.";
|
|
122
110
|
|
|
123
111
|
setCachedIntro(["Hey, I'm Atlas.", "What's up?"]);
|
|
124
112
|
const cached = getCachedIntro();
|
|
@@ -131,111 +119,45 @@ describe("identity intro cache", () => {
|
|
|
131
119
|
|
|
132
120
|
setCachedIntro(["Hello!"]);
|
|
133
121
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
checkpointStore.set("identity:intro:cached_at", fiveHoursAgo);
|
|
122
|
+
const fortyNineHoursAgo = String(Date.now() - 49 * 60 * 60 * 1000);
|
|
123
|
+
checkpointStore.set("identity:intro:cached_at", fortyNineHoursAgo);
|
|
137
124
|
|
|
138
125
|
expect(getCachedIntro()).toBeNull();
|
|
139
126
|
});
|
|
140
127
|
|
|
141
|
-
test("returns cached greetings when within TTL (
|
|
128
|
+
test("returns cached greetings when within TTL (47 hours ago)", () => {
|
|
142
129
|
workspaceFiles["IDENTITY.md"] = "- **Name:** Atlas";
|
|
143
130
|
|
|
144
131
|
setCachedIntro(["Hello!"]);
|
|
145
132
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
checkpointStore.set("identity:intro:cached_at", threeHoursAgo);
|
|
133
|
+
const fortySevenHoursAgo = String(Date.now() - 47 * 60 * 60 * 1000);
|
|
134
|
+
checkpointStore.set("identity:intro:cached_at", fortySevenHoursAgo);
|
|
149
135
|
|
|
150
136
|
const cached = getCachedIntro();
|
|
151
137
|
expect(cached).not.toBeNull();
|
|
152
138
|
expect(cached!.greetings).toEqual(["Hello!"]);
|
|
153
139
|
});
|
|
154
140
|
|
|
155
|
-
test("
|
|
141
|
+
test("keeps cached greetings when IDENTITY.md changes", () => {
|
|
156
142
|
workspaceFiles["IDENTITY.md"] = "- **Name:** Atlas";
|
|
157
143
|
setCachedIntro(["I'm Atlas!"]);
|
|
158
144
|
|
|
159
|
-
// Change IDENTITY.md
|
|
160
145
|
workspaceFiles["IDENTITY.md"] = "- **Name:** Nova";
|
|
161
146
|
|
|
162
|
-
expect(getCachedIntro()).
|
|
147
|
+
expect(getCachedIntro()?.greetings).toEqual(["I'm Atlas!"]);
|
|
163
148
|
});
|
|
164
149
|
|
|
165
|
-
test("
|
|
150
|
+
test("keeps cached greetings when SOUL.md changes", () => {
|
|
166
151
|
workspaceFiles["SOUL.md"] = "Be playful.";
|
|
167
152
|
setCachedIntro(["Hey there!"]);
|
|
168
153
|
|
|
169
|
-
// Change SOUL.md
|
|
170
154
|
workspaceFiles["SOUL.md"] = "Be serious and formal.";
|
|
171
155
|
|
|
172
|
-
expect(getCachedIntro()).
|
|
173
|
-
});
|
|
174
|
-
|
|
175
|
-
test("busts cache when guardian persona content changes", () => {
|
|
176
|
-
guardianPersonaContent = "Likes coffee.";
|
|
177
|
-
setCachedIntro(["Good morning!"]);
|
|
178
|
-
|
|
179
|
-
// Change guardian persona (e.g. user edited users/<slug>.md)
|
|
180
|
-
guardianPersonaContent = "Likes tea.";
|
|
181
|
-
|
|
182
|
-
expect(getCachedIntro()).toBeNull();
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
test("cache remains valid when guardian persona is unchanged", () => {
|
|
186
|
-
workspaceFiles["IDENTITY.md"] = "- **Name:** Atlas";
|
|
187
|
-
workspaceFiles["SOUL.md"] = "Be chill.";
|
|
188
|
-
guardianPersonaContent = "Likes sunsets.";
|
|
189
|
-
|
|
190
|
-
setCachedIntro(["Atlas here.", "Hey friend."]);
|
|
191
|
-
|
|
192
|
-
expect(getCachedIntro()?.greetings).toEqual(["Atlas here.", "Hey friend."]);
|
|
193
|
-
expect(getCachedIntro()?.greetings).toEqual(["Atlas here.", "Hey friend."]);
|
|
194
|
-
});
|
|
195
|
-
|
|
196
|
-
test("computeIdentityContentHash is deterministic", () => {
|
|
197
|
-
workspaceFiles["IDENTITY.md"] = "test";
|
|
198
|
-
workspaceFiles["SOUL.md"] = "test2";
|
|
199
|
-
guardianPersonaContent = "test3";
|
|
200
|
-
|
|
201
|
-
const hash1 = computeIdentityContentHash();
|
|
202
|
-
const hash2 = computeIdentityContentHash();
|
|
203
|
-
expect(hash1).toBe(hash2);
|
|
204
|
-
expect(hash1).toMatch(/^[a-f0-9]{64}$/); // SHA-256 hex
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
test("computeIdentityContentHash changes when guardian persona changes", () => {
|
|
208
|
-
workspaceFiles["IDENTITY.md"] = "- **Name:** Atlas";
|
|
209
|
-
workspaceFiles["SOUL.md"] = "Be playful.";
|
|
210
|
-
guardianPersonaContent = "Likes coffee.";
|
|
211
|
-
const hash1 = computeIdentityContentHash();
|
|
212
|
-
|
|
213
|
-
guardianPersonaContent = "Likes tea.";
|
|
214
|
-
const hash2 = computeIdentityContentHash();
|
|
215
|
-
|
|
216
|
-
expect(hash1).not.toBe(hash2);
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
test("computeIdentityContentHash handles null guardian persona", () => {
|
|
220
|
-
workspaceFiles["IDENTITY.md"] = "- **Name:** Atlas";
|
|
221
|
-
guardianPersonaContent = null;
|
|
222
|
-
|
|
223
|
-
const hash = computeIdentityContentHash();
|
|
224
|
-
expect(hash).toMatch(/^[a-f0-9]{64}$/);
|
|
225
|
-
});
|
|
226
|
-
|
|
227
|
-
test("computeIdentityContentHash changes when file content changes", () => {
|
|
228
|
-
workspaceFiles["IDENTITY.md"] = "v1";
|
|
229
|
-
const hash1 = computeIdentityContentHash();
|
|
230
|
-
|
|
231
|
-
workspaceFiles["IDENTITY.md"] = "v2";
|
|
232
|
-
const hash2 = computeIdentityContentHash();
|
|
233
|
-
|
|
234
|
-
expect(hash1).not.toBe(hash2);
|
|
156
|
+
expect(getCachedIntro()?.greetings).toEqual(["Hey there!"]);
|
|
235
157
|
});
|
|
236
158
|
|
|
237
159
|
test("handles missing workspace files gracefully", () => {
|
|
238
|
-
// No files exist — should still work
|
|
160
|
+
// No files exist — should still work.
|
|
239
161
|
setCachedIntro(["Hello!"]);
|
|
240
162
|
const cached = getCachedIntro();
|
|
241
163
|
expect(cached).not.toBeNull();
|
|
@@ -244,9 +166,7 @@ describe("identity intro cache", () => {
|
|
|
244
166
|
|
|
245
167
|
test("handles legacy single-string cache value", () => {
|
|
246
168
|
// Simulate a cache entry written by an older daemon version
|
|
247
|
-
const hash = computeIdentityContentHash();
|
|
248
169
|
checkpointStore.set("identity:intro:greetings", "Legacy greeting");
|
|
249
|
-
checkpointStore.set("identity:intro:content_hash", hash);
|
|
250
170
|
checkpointStore.set("identity:intro:cached_at", String(Date.now()));
|
|
251
171
|
|
|
252
172
|
const cached = getCachedIntro();
|
|
@@ -255,20 +175,12 @@ describe("identity intro cache", () => {
|
|
|
255
175
|
});
|
|
256
176
|
|
|
257
177
|
test("returns null when greetings checkpoint is missing", () => {
|
|
258
|
-
checkpointStore.set("identity:intro:content_hash", "abc");
|
|
259
|
-
checkpointStore.set("identity:intro:cached_at", String(Date.now()));
|
|
260
|
-
expect(getCachedIntro()).toBeNull();
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
test("returns null when hash checkpoint is missing", () => {
|
|
264
|
-
checkpointStore.set("identity:intro:greetings", '["Hello"]');
|
|
265
178
|
checkpointStore.set("identity:intro:cached_at", String(Date.now()));
|
|
266
179
|
expect(getCachedIntro()).toBeNull();
|
|
267
180
|
});
|
|
268
181
|
|
|
269
182
|
test("returns null when timestamp checkpoint is missing", () => {
|
|
270
183
|
checkpointStore.set("identity:intro:greetings", '["Hello"]');
|
|
271
|
-
checkpointStore.set("identity:intro:content_hash", "abc");
|
|
272
184
|
expect(getCachedIntro()).toBeNull();
|
|
273
185
|
});
|
|
274
186
|
});
|
|
@@ -22,14 +22,70 @@ mock.module("../util/logger.js", () => ({
|
|
|
22
22
|
}),
|
|
23
23
|
}));
|
|
24
24
|
|
|
25
|
+
const checkpointStore = new Map<string, string>();
|
|
26
|
+
|
|
27
|
+
mock.module("../memory/checkpoints.js", () => ({
|
|
28
|
+
getMemoryCheckpoint: (key: string) => checkpointStore.get(key) ?? null,
|
|
29
|
+
setMemoryCheckpoint: (key: string, value: string) => {
|
|
30
|
+
checkpointStore.set(key, value);
|
|
31
|
+
},
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
const getConfiguredProviderCalls: string[] = [];
|
|
35
|
+
const mockProvider = { name: "mock-provider" };
|
|
36
|
+
|
|
37
|
+
mock.module("../providers/provider-send-message.js", () => ({
|
|
38
|
+
getConfiguredProvider: mock(async (callSite: string) => {
|
|
39
|
+
getConfiguredProviderCalls.push(callSite);
|
|
40
|
+
return mockProvider;
|
|
41
|
+
}),
|
|
42
|
+
}));
|
|
43
|
+
|
|
44
|
+
type SidechainCall = {
|
|
45
|
+
callSite?: string;
|
|
46
|
+
content: string;
|
|
47
|
+
maxTokens?: number;
|
|
48
|
+
systemPrompt?: string;
|
|
49
|
+
tools: unknown[];
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
type SidechainResult = {
|
|
53
|
+
text: string;
|
|
54
|
+
hadTextDeltas: false;
|
|
55
|
+
response: { content: [] };
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const sidechainCalls: SidechainCall[] = [];
|
|
59
|
+
let sidechainText = "";
|
|
60
|
+
let sidechainResultPromise: Promise<SidechainResult> | null = null;
|
|
61
|
+
|
|
62
|
+
mock.module("../runtime/btw-sidechain.js", () => ({
|
|
63
|
+
runBtwSidechain: mock(async (params: SidechainCall) => {
|
|
64
|
+
sidechainCalls.push(params);
|
|
65
|
+
if (sidechainResultPromise) {
|
|
66
|
+
return sidechainResultPromise;
|
|
67
|
+
}
|
|
68
|
+
return {
|
|
69
|
+
text: sidechainText,
|
|
70
|
+
hadTextDeltas: false,
|
|
71
|
+
response: { content: [] },
|
|
72
|
+
};
|
|
73
|
+
}),
|
|
74
|
+
}));
|
|
75
|
+
|
|
76
|
+
const assistantFeatureFlags: Record<string, boolean> = {};
|
|
77
|
+
|
|
78
|
+
mock.module("../config/assistant-feature-flags.js", () => ({
|
|
79
|
+
isAssistantFeatureFlagEnabled: (key: string) =>
|
|
80
|
+
assistantFeatureFlags[key] ?? false,
|
|
81
|
+
}));
|
|
82
|
+
|
|
25
83
|
import {
|
|
26
84
|
handleDetailedHealth,
|
|
27
85
|
handleReadyz,
|
|
28
86
|
ROUTES,
|
|
29
87
|
} from "../runtime/routes/identity-routes.js";
|
|
30
|
-
import {
|
|
31
|
-
setCesClient,
|
|
32
|
-
} from "../security/secure-keys.js";
|
|
88
|
+
import { setCesClient } from "../security/secure-keys.js";
|
|
33
89
|
import { getWorkspaceDir } from "../util/platform.js";
|
|
34
90
|
import {
|
|
35
91
|
getHatchedSidecarPath,
|
|
@@ -37,6 +93,17 @@ import {
|
|
|
37
93
|
selectHatchedAtFromStats,
|
|
38
94
|
} from "../workspace/hatched-date.js";
|
|
39
95
|
|
|
96
|
+
function createDeferred<T>(): {
|
|
97
|
+
promise: Promise<T>;
|
|
98
|
+
resolve: (value: T) => void;
|
|
99
|
+
} {
|
|
100
|
+
let resolve!: (value: T) => void;
|
|
101
|
+
const promise = new Promise<T>((res) => {
|
|
102
|
+
resolve = res;
|
|
103
|
+
});
|
|
104
|
+
return { promise, resolve };
|
|
105
|
+
}
|
|
106
|
+
|
|
40
107
|
// ── Env helpers ─────────────────────────────────────────────────────────
|
|
41
108
|
|
|
42
109
|
let savedEnv: Record<string, string | undefined>;
|
|
@@ -133,6 +200,15 @@ beforeEach(() => {
|
|
|
133
200
|
|
|
134
201
|
rmSync(getHatchedSidecarPath(), { force: true });
|
|
135
202
|
rmSync(join(getWorkspaceDir(), "IDENTITY.md"), { force: true });
|
|
203
|
+
rmSync(join(getWorkspaceDir(), "SOUL.md"), { force: true });
|
|
204
|
+
checkpointStore.clear();
|
|
205
|
+
getConfiguredProviderCalls.length = 0;
|
|
206
|
+
sidechainCalls.length = 0;
|
|
207
|
+
sidechainText = "";
|
|
208
|
+
sidechainResultPromise = null;
|
|
209
|
+
for (const key of Object.keys(assistantFeatureFlags)) {
|
|
210
|
+
delete assistantFeatureFlags[key];
|
|
211
|
+
}
|
|
136
212
|
});
|
|
137
213
|
|
|
138
214
|
afterEach(() => {
|
|
@@ -202,21 +278,30 @@ describe("identity routes — health endpoint", () => {
|
|
|
202
278
|
});
|
|
203
279
|
|
|
204
280
|
test("readyz returns 200 when CES is connected and ready", () => {
|
|
205
|
-
const mockClient = {
|
|
281
|
+
const mockClient = {
|
|
282
|
+
isReady: () => true,
|
|
283
|
+
close: () => {},
|
|
284
|
+
} as unknown as import("../credential-execution/client.js").CesClient;
|
|
206
285
|
setCesClient(mockClient);
|
|
207
286
|
const res = handleReadyz();
|
|
208
287
|
expect(res.status).toBe(200);
|
|
209
288
|
});
|
|
210
289
|
|
|
211
290
|
test("readyz returns 200 when CES client exists but is not ready", () => {
|
|
212
|
-
const mockClient = {
|
|
291
|
+
const mockClient = {
|
|
292
|
+
isReady: () => false,
|
|
293
|
+
close: () => {},
|
|
294
|
+
} as unknown as import("../credential-execution/client.js").CesClient;
|
|
213
295
|
setCesClient(mockClient);
|
|
214
296
|
const res = handleReadyz();
|
|
215
297
|
expect(res.status).toBe(200);
|
|
216
298
|
});
|
|
217
299
|
|
|
218
300
|
test("/v1/health reports ces.connected=true when CES is ready", async () => {
|
|
219
|
-
const mockClient = {
|
|
301
|
+
const mockClient = {
|
|
302
|
+
isReady: () => true,
|
|
303
|
+
close: () => {},
|
|
304
|
+
} as unknown as import("../credential-execution/client.js").CesClient;
|
|
220
305
|
setCesClient(mockClient);
|
|
221
306
|
const res = handleDetailedHealth();
|
|
222
307
|
const body = (await res.json()) as Record<string, unknown>;
|
|
@@ -226,7 +311,10 @@ describe("identity routes — health endpoint", () => {
|
|
|
226
311
|
});
|
|
227
312
|
|
|
228
313
|
test("/v1/health reports ces.connected=false when CES is not ready", async () => {
|
|
229
|
-
const mockClient = {
|
|
314
|
+
const mockClient = {
|
|
315
|
+
isReady: () => false,
|
|
316
|
+
close: () => {},
|
|
317
|
+
} as unknown as import("../credential-execution/client.js").CesClient;
|
|
230
318
|
setCesClient(mockClient);
|
|
231
319
|
const res = handleDetailedHealth();
|
|
232
320
|
const body = (await res.json()) as Record<string, unknown>;
|
|
@@ -485,3 +573,156 @@ describe("identity routes — createdAt selection", () => {
|
|
|
485
573
|
expect(existsSync(getHatchedSidecarPath())).toBe(false);
|
|
486
574
|
});
|
|
487
575
|
});
|
|
576
|
+
|
|
577
|
+
describe("identity routes — intro greetings", () => {
|
|
578
|
+
test("returns static fallback and does not generate when the dynamic greetings flag is off", async () => {
|
|
579
|
+
const workspaceDir = getWorkspaceDir();
|
|
580
|
+
writeFileSync(
|
|
581
|
+
join(workspaceDir, "IDENTITY.md"),
|
|
582
|
+
"# Identity\n\n- **Name:** Example Assistant\n",
|
|
583
|
+
"utf-8",
|
|
584
|
+
);
|
|
585
|
+
writeFileSync(
|
|
586
|
+
join(workspaceDir, "SOUL.md"),
|
|
587
|
+
"# Soul\n\nNo explicit greetings section here.\n",
|
|
588
|
+
"utf-8",
|
|
589
|
+
);
|
|
590
|
+
|
|
591
|
+
const route = ROUTES.find(
|
|
592
|
+
(candidate) => candidate.operationId === "identity_intro",
|
|
593
|
+
);
|
|
594
|
+
expect(route).toBeDefined();
|
|
595
|
+
|
|
596
|
+
const body = route!.handler({}) as {
|
|
597
|
+
greetings: string[];
|
|
598
|
+
text: string;
|
|
599
|
+
source: string;
|
|
600
|
+
refreshing: boolean;
|
|
601
|
+
};
|
|
602
|
+
|
|
603
|
+
expect(body).toEqual({
|
|
604
|
+
greetings: [
|
|
605
|
+
"What are we working on?",
|
|
606
|
+
"I'm here whenever you need me.",
|
|
607
|
+
"What's on your mind?",
|
|
608
|
+
"Ready when you are.",
|
|
609
|
+
],
|
|
610
|
+
text: "What are we working on?",
|
|
611
|
+
source: "fallback",
|
|
612
|
+
refreshing: false,
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
await Promise.resolve();
|
|
616
|
+
await Promise.resolve();
|
|
617
|
+
|
|
618
|
+
expect(getConfiguredProviderCalls).toEqual([]);
|
|
619
|
+
expect(sidechainCalls).toEqual([]);
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
test("returns fallback immediately, generates personalized greetings in the background, then reuses the cache", async () => {
|
|
623
|
+
assistantFeatureFlags["empty-state-dynamic-greetings"] = true;
|
|
624
|
+
|
|
625
|
+
const workspaceDir = getWorkspaceDir();
|
|
626
|
+
writeFileSync(
|
|
627
|
+
join(workspaceDir, "IDENTITY.md"),
|
|
628
|
+
[
|
|
629
|
+
"# Identity",
|
|
630
|
+
"",
|
|
631
|
+
"- **Name:** Example Assistant",
|
|
632
|
+
"- **Personality:** enjoys crisp, useful hellos",
|
|
633
|
+
"",
|
|
634
|
+
"Identity sentinel: chartreuse compass.",
|
|
635
|
+
].join("\n"),
|
|
636
|
+
"utf-8",
|
|
637
|
+
);
|
|
638
|
+
writeFileSync(
|
|
639
|
+
join(workspaceDir, "SOUL.md"),
|
|
640
|
+
[
|
|
641
|
+
"# Soul",
|
|
642
|
+
"",
|
|
643
|
+
"Soul sentinel: copper lighthouse.",
|
|
644
|
+
"",
|
|
645
|
+
"Keep greetings warm and specific.",
|
|
646
|
+
].join("\n"),
|
|
647
|
+
"utf-8",
|
|
648
|
+
);
|
|
649
|
+
const deferredSidechain = createDeferred<SidechainResult>();
|
|
650
|
+
sidechainResultPromise = deferredSidechain.promise;
|
|
651
|
+
|
|
652
|
+
const route = ROUTES.find(
|
|
653
|
+
(candidate) => candidate.operationId === "identity_intro",
|
|
654
|
+
);
|
|
655
|
+
expect(route).toBeDefined();
|
|
656
|
+
|
|
657
|
+
const body = route!.handler({}) as {
|
|
658
|
+
greetings: string[];
|
|
659
|
+
text: string;
|
|
660
|
+
source: string;
|
|
661
|
+
refreshing: boolean;
|
|
662
|
+
};
|
|
663
|
+
|
|
664
|
+
expect(body).toEqual({
|
|
665
|
+
greetings: [
|
|
666
|
+
"What are we working on?",
|
|
667
|
+
"I'm here whenever you need me.",
|
|
668
|
+
"What's on your mind?",
|
|
669
|
+
"Ready when you are.",
|
|
670
|
+
],
|
|
671
|
+
text: "What are we working on?",
|
|
672
|
+
source: "fallback",
|
|
673
|
+
refreshing: true,
|
|
674
|
+
});
|
|
675
|
+
expect(getConfiguredProviderCalls).toEqual([]);
|
|
676
|
+
expect(sidechainCalls).toEqual([]);
|
|
677
|
+
|
|
678
|
+
await Promise.resolve();
|
|
679
|
+
await Promise.resolve();
|
|
680
|
+
|
|
681
|
+
expect(getConfiguredProviderCalls).toEqual(["emptyStateGreeting"]);
|
|
682
|
+
expect(sidechainCalls).toHaveLength(1);
|
|
683
|
+
expect(sidechainCalls[0]?.callSite).toBe("emptyStateGreeting");
|
|
684
|
+
expect(sidechainCalls[0]?.tools).toEqual([]);
|
|
685
|
+
expect(sidechainCalls[0]?.content).toContain("JSON array");
|
|
686
|
+
expect(sidechainCalls[0]?.systemPrompt).toContain(
|
|
687
|
+
"Identity sentinel: chartreuse compass.",
|
|
688
|
+
);
|
|
689
|
+
expect(sidechainCalls[0]?.systemPrompt).toContain(
|
|
690
|
+
"Soul sentinel: copper lighthouse.",
|
|
691
|
+
);
|
|
692
|
+
deferredSidechain.resolve({
|
|
693
|
+
text: JSON.stringify([
|
|
694
|
+
"Charting the next useful thing?",
|
|
695
|
+
"I brought the compass. Where to?",
|
|
696
|
+
"Ready to make this lighter.",
|
|
697
|
+
]),
|
|
698
|
+
hadTextDeltas: false,
|
|
699
|
+
response: { content: [] },
|
|
700
|
+
});
|
|
701
|
+
|
|
702
|
+
await sidechainResultPromise;
|
|
703
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
704
|
+
|
|
705
|
+
sidechainCalls.length = 0;
|
|
706
|
+
getConfiguredProviderCalls.length = 0;
|
|
707
|
+
|
|
708
|
+
const cachedBody = (await route!.handler({})) as {
|
|
709
|
+
greetings: string[];
|
|
710
|
+
text: string;
|
|
711
|
+
source: string;
|
|
712
|
+
refreshing: boolean;
|
|
713
|
+
};
|
|
714
|
+
|
|
715
|
+
expect(cachedBody).toEqual({
|
|
716
|
+
greetings: [
|
|
717
|
+
"Charting the next useful thing?",
|
|
718
|
+
"I brought the compass. Where to?",
|
|
719
|
+
"Ready to make this lighter.",
|
|
720
|
+
],
|
|
721
|
+
text: "Charting the next useful thing?",
|
|
722
|
+
source: "cache",
|
|
723
|
+
refreshing: false,
|
|
724
|
+
});
|
|
725
|
+
expect(getConfiguredProviderCalls).toEqual([]);
|
|
726
|
+
expect(sidechainCalls).toEqual([]);
|
|
727
|
+
});
|
|
728
|
+
});
|
|
@@ -105,10 +105,14 @@ function createTestContext(
|
|
|
105
105
|
}
|
|
106
106
|
: null;
|
|
107
107
|
|
|
108
|
+
let processing = false;
|
|
108
109
|
return {
|
|
109
110
|
conversationId: "conv-test",
|
|
110
111
|
messages: [],
|
|
111
|
-
|
|
112
|
+
isProcessing: () => processing,
|
|
113
|
+
setProcessing: (value: boolean) => {
|
|
114
|
+
processing = value;
|
|
115
|
+
},
|
|
112
116
|
abortController: null,
|
|
113
117
|
queue: queueStub,
|
|
114
118
|
getTurnChannelContext: () => turnChannel,
|