@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
|
@@ -17,7 +17,7 @@ import type {
|
|
|
17
17
|
Message,
|
|
18
18
|
Provider,
|
|
19
19
|
ProviderResponse,
|
|
20
|
-
} from "
|
|
20
|
+
} from "../../../../providers/types.js";
|
|
21
21
|
import type { NeedleIndex } from "../needle.js";
|
|
22
22
|
import type {
|
|
23
23
|
LeafNode,
|
|
@@ -35,13 +35,13 @@ import evalTurns from "./fixtures/eval-turns.json" with { type: "json" };
|
|
|
35
35
|
|
|
36
36
|
let providerStub: Provider | null = null;
|
|
37
37
|
|
|
38
|
-
mock.module("
|
|
38
|
+
mock.module("../../../../providers/provider-send-message.js", () => ({
|
|
39
39
|
getConfiguredProvider: async () => providerStub,
|
|
40
40
|
extractToolUse: (response: ProviderResponse) =>
|
|
41
41
|
response.content.find((b) => b.type === "tool_use"),
|
|
42
42
|
}));
|
|
43
43
|
|
|
44
|
-
mock.module("
|
|
44
|
+
mock.module("../../../../util/logger.js", () => ({
|
|
45
45
|
getLogger: () =>
|
|
46
46
|
new Proxy({} as Record<string, unknown>, {
|
|
47
47
|
get: (_t, prop) => (prop === "child" ? () => ({}) : () => {}),
|
|
@@ -217,6 +217,43 @@ describe("orchestrate — fixture sequence (carry-forward)", () => {
|
|
|
217
217
|
expect(t2.currentSelections.map((s) => s.slug)).not.toContain("page-a");
|
|
218
218
|
expect(t2.finalInjection).toContain("page-a");
|
|
219
219
|
});
|
|
220
|
+
|
|
221
|
+
test("carry-forward survives a turn whose selections fill the cap", async () => {
|
|
222
|
+
const tree = makeTree();
|
|
223
|
+
// Cap of 1: under a naive record-then-cap order this turn's own selection
|
|
224
|
+
// would evict the carried page before injection. Snapshotting the carry
|
|
225
|
+
// BEFORE recording this turn keeps the earlier page in the injection.
|
|
226
|
+
const workingSet = new WorkingSet(1);
|
|
227
|
+
const needle = fakeNeedle([]);
|
|
228
|
+
const stub = (selectIds: number[]): Provider => ({
|
|
229
|
+
name: "stub",
|
|
230
|
+
sendMessage: async (_messages, options) =>
|
|
231
|
+
options?.tools?.[0]?.name === "open_leaves"
|
|
232
|
+
? toolUseResponse("open_leaves", { ids: [1] })
|
|
233
|
+
: toolUseResponse("select_pages", { ids: selectIds, pinned_ids: [] }),
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
providerStub = stub([1]); // turn 1 → page-a
|
|
237
|
+
await orchestrate(makeTurn(1, "page a"), {
|
|
238
|
+
tree,
|
|
239
|
+
core: new Set(),
|
|
240
|
+
needle,
|
|
241
|
+
workingSet,
|
|
242
|
+
pageSummary: summaryOf,
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
providerStub = stub([2]); // turn 2 → page-b, never re-selects page-a
|
|
246
|
+
const t2 = await orchestrate(makeTurn(2, "page b"), {
|
|
247
|
+
tree,
|
|
248
|
+
core: new Set(),
|
|
249
|
+
needle,
|
|
250
|
+
workingSet,
|
|
251
|
+
pageSummary: summaryOf,
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
expect(t2.currentSelections.map((s) => s.slug)).toEqual(["page-b"]);
|
|
255
|
+
expect(t2.finalInjection).toContain("page-a"); // carried despite the cap
|
|
256
|
+
});
|
|
220
257
|
});
|
|
221
258
|
|
|
222
259
|
// ---------------------------------------------------------------------------
|
|
@@ -298,14 +335,16 @@ describe("orchestrate — edge cases", () => {
|
|
|
298
335
|
expect(result.finalInjection).toEqual(["page-a", "page-b"]);
|
|
299
336
|
});
|
|
300
337
|
|
|
301
|
-
test("L1
|
|
338
|
+
test("omitted L1 ids opens only the deterministic lanes, not the whole tree", async () => {
|
|
302
339
|
const tree = makeTree();
|
|
303
|
-
//
|
|
340
|
+
// L1 omits ids → routeL1 opens NO routed leaves; only the needle/core lanes
|
|
341
|
+
// drive the open set, so the whole tree is never fanned out (topic-y, which
|
|
342
|
+
// nothing routes or needles to, stays closed).
|
|
304
343
|
providerStub = {
|
|
305
344
|
name: "stub",
|
|
306
345
|
sendMessage: async (_messages, options) => {
|
|
307
346
|
if (options?.tools?.[0]?.name === "open_leaves") {
|
|
308
|
-
return toolUseResponse("open_leaves", {}); // omitted ids
|
|
347
|
+
return toolUseResponse("open_leaves", {}); // omitted ids → []
|
|
309
348
|
}
|
|
310
349
|
return toolUseResponse("select_pages", {}); // omitted → all members
|
|
311
350
|
},
|
|
@@ -313,15 +352,12 @@ describe("orchestrate — edge cases", () => {
|
|
|
313
352
|
const result = await orchestrate(makeTurn(1, "x"), {
|
|
314
353
|
tree,
|
|
315
354
|
core: new Set(),
|
|
316
|
-
needle: fakeNeedle([]),
|
|
355
|
+
needle: fakeNeedle(["page-a"]), // needle opens domain-a/topic-x only
|
|
317
356
|
workingSet: new WorkingSet(),
|
|
318
357
|
pageSummary: summaryOf,
|
|
319
358
|
});
|
|
320
|
-
expect(result.openedLeaves).toEqual([
|
|
321
|
-
|
|
322
|
-
"domain-a/topic-y",
|
|
323
|
-
]);
|
|
324
|
-
expect(result.finalInjection).toEqual(["page-a", "page-b", "page-c"]);
|
|
359
|
+
expect(result.openedLeaves).toEqual(["domain-a/topic-x"]);
|
|
360
|
+
expect(result.finalInjection).toEqual(["page-a", "page-b"]);
|
|
325
361
|
});
|
|
326
362
|
|
|
327
363
|
test("pinned current-turn selections land in the working set", async () => {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { cachedTextBlock } from "../provider-blocks.js";
|
|
4
|
+
|
|
5
|
+
describe("cachedTextBlock", () => {
|
|
6
|
+
test("stamps an ephemeral cache_control with a 1h TTL", () => {
|
|
7
|
+
const block = cachedTextBlock("stable leaf block");
|
|
8
|
+
expect(block).toMatchObject({ type: "text", text: "stable leaf block" });
|
|
9
|
+
expect(
|
|
10
|
+
(block as unknown as { cache_control?: unknown }).cache_control,
|
|
11
|
+
).toEqual({ type: "ephemeral", ttl: "1h" });
|
|
12
|
+
});
|
|
13
|
+
});
|
|
@@ -3,8 +3,8 @@ import { tmpdir } from "node:os";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
5
5
|
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
6
|
+
import { readPage, writePage } from "../../../../memory/v2/page-store.js";
|
|
7
|
+
import type { Provider } from "../../../../providers/types.js";
|
|
8
8
|
import { type LeafRef, reconcileTree } from "../reconcile.js";
|
|
9
9
|
|
|
10
10
|
/**
|
package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/render-injection.test.ts
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
-
import { wrapMemoryBlock } from "
|
|
3
|
+
import { wrapMemoryBlock } from "../../../../memory/memory-marker.js";
|
|
4
4
|
import { renderMemoryBlock } from "../render-injection.js";
|
|
5
5
|
import { Slug } from "../types.js";
|
|
6
6
|
|
|
@@ -3,10 +3,13 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Coverage matrix:
|
|
5
5
|
* - Returned IDs map to the right leaves by 1-based index, in model order.
|
|
6
|
-
* - Omitted `ids` →
|
|
6
|
+
* - Omitted `ids` → no leaves (the router must name leaves explicitly,
|
|
7
|
+
* never open the whole tree).
|
|
7
8
|
* - Explicit `ids: []` → no leaves (deliberate abstention).
|
|
8
9
|
* - Out-of-range / duplicate IDs ignored, no throw.
|
|
9
|
-
* - No provider / missing tool_use / schema mismatch / throw →
|
|
10
|
+
* - No provider / missing tool_use / schema mismatch / throw → no leaves
|
|
11
|
+
* (degrade to the deterministic lanes), the last three after a re-prompt
|
|
12
|
+
* retry; a malformed response that recovers on retry returns its IDs.
|
|
10
13
|
* - The rendered leaf block is byte-identical across two calls with
|
|
11
14
|
* different queries (the cache invariant).
|
|
12
15
|
* - The system prompt mentions "register" (locks the routing commitment).
|
|
@@ -22,7 +25,7 @@ import type {
|
|
|
22
25
|
ProviderResponse,
|
|
23
26
|
SendMessageOptions,
|
|
24
27
|
ToolUseContent,
|
|
25
|
-
} from "
|
|
28
|
+
} from "../../../../providers/types.js";
|
|
26
29
|
import type { LeafNode, LeafPath, LeafTree, TurnContext } from "../types.js";
|
|
27
30
|
|
|
28
31
|
// ---------------------------------------------------------------------------
|
|
@@ -38,13 +41,13 @@ interface ProviderCall {
|
|
|
38
41
|
}
|
|
39
42
|
const providerCalls: ProviderCall[] = [];
|
|
40
43
|
|
|
41
|
-
mock.module("
|
|
44
|
+
mock.module("../../../../providers/provider-send-message.js", () => ({
|
|
42
45
|
getConfiguredProvider: async () => providerStub,
|
|
43
46
|
extractToolUse: (response: ProviderResponse) =>
|
|
44
47
|
response.content.find((b): b is ToolUseContent => b.type === "tool_use"),
|
|
45
48
|
}));
|
|
46
49
|
|
|
47
|
-
mock.module("
|
|
50
|
+
mock.module("../../../../util/logger.js", () => ({
|
|
48
51
|
getLogger: () =>
|
|
49
52
|
new Proxy({} as Record<string, unknown>, {
|
|
50
53
|
get: (_t, prop) => (prop === "child" ? () => ({}) : () => {}),
|
|
@@ -76,6 +79,45 @@ function toolUseResponse(input: Record<string, unknown>): ProviderResponse {
|
|
|
76
79
|
};
|
|
77
80
|
}
|
|
78
81
|
|
|
82
|
+
/** A 200 response that carries no tool_use — the malformed-but-successful case
|
|
83
|
+
* the re-prompt retry exists to recover from. */
|
|
84
|
+
function noToolResponse(): ProviderResponse {
|
|
85
|
+
return {
|
|
86
|
+
model: "stub-model",
|
|
87
|
+
stopReason: "end_turn",
|
|
88
|
+
usage: { inputTokens: 0, outputTokens: 0 },
|
|
89
|
+
content: [{ type: "text", text: "no tool call" }],
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Provider returning a different response per call (the i-th call returns
|
|
94
|
+
* responses[i], or the last entry once exhausted), recording each call so a
|
|
95
|
+
* test can assert how many attempts were made. */
|
|
96
|
+
function makeSequenceProvider(responses: ProviderResponse[]): Provider {
|
|
97
|
+
let i = 0;
|
|
98
|
+
return {
|
|
99
|
+
name: "sequence",
|
|
100
|
+
sendMessage: async (messages, options) => {
|
|
101
|
+
providerCalls.push({ messages, options });
|
|
102
|
+
const response = responses[Math.min(i, responses.length - 1)];
|
|
103
|
+
i += 1;
|
|
104
|
+
return response;
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Provider that records each call and then throws — for the throw-after-retries
|
|
110
|
+
* path (the provider's own RetryProvider has already exhausted its backoff). */
|
|
111
|
+
function makeThrowingProvider(): Provider {
|
|
112
|
+
return {
|
|
113
|
+
name: "throwing",
|
|
114
|
+
sendMessage: async (messages, options) => {
|
|
115
|
+
providerCalls.push({ messages, options });
|
|
116
|
+
throw new Error("boom");
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
79
121
|
function makeLeaf(path: LeafPath, description: string): LeafNode {
|
|
80
122
|
return {
|
|
81
123
|
path,
|
|
@@ -101,8 +143,6 @@ function makeTree(): LeafTree {
|
|
|
101
143
|
};
|
|
102
144
|
}
|
|
103
145
|
|
|
104
|
-
const SORTED_PATHS = ["people/alice", "people/bob", "projects/atlas"];
|
|
105
|
-
|
|
106
146
|
function makeTurn(currentMessage: string): TurnContext {
|
|
107
147
|
return {
|
|
108
148
|
conversationId: "conv-xyz",
|
|
@@ -128,10 +168,10 @@ describe("routeL1 — id mapping", () => {
|
|
|
128
168
|
expect(result).toEqual(["projects/atlas", "people/alice"]);
|
|
129
169
|
});
|
|
130
170
|
|
|
131
|
-
test("omitted ids opens
|
|
171
|
+
test("omitted ids opens no leaves (must name leaves explicitly)", async () => {
|
|
132
172
|
providerStub = makeProvider(toolUseResponse({}));
|
|
133
173
|
const result = await routeL1(makeTurn("anything"), makeTree());
|
|
134
|
-
expect(result).toEqual(
|
|
174
|
+
expect(result).toEqual([]);
|
|
135
175
|
});
|
|
136
176
|
|
|
137
177
|
test("explicit empty ids opens no leaves (abstention)", async () => {
|
|
@@ -157,39 +197,43 @@ describe("routeL1 — id mapping", () => {
|
|
|
157
197
|
});
|
|
158
198
|
});
|
|
159
199
|
|
|
160
|
-
describe("routeL1 —
|
|
161
|
-
test("no provider →
|
|
200
|
+
describe("routeL1 — degradation on failure", () => {
|
|
201
|
+
test("no provider → no leaves, without calling the provider", async () => {
|
|
162
202
|
providerStub = null;
|
|
163
203
|
const result = await routeL1(makeTurn("x"), makeTree());
|
|
164
|
-
expect(result).toEqual(
|
|
204
|
+
expect(result).toEqual([]);
|
|
205
|
+
expect(providerCalls).toHaveLength(0);
|
|
165
206
|
});
|
|
166
207
|
|
|
167
|
-
test("missing tool_use →
|
|
168
|
-
providerStub = makeProvider(
|
|
169
|
-
model: "stub-model",
|
|
170
|
-
stopReason: "end_turn",
|
|
171
|
-
usage: { inputTokens: 0, outputTokens: 0 },
|
|
172
|
-
content: [{ type: "text", text: "no tool call" }],
|
|
173
|
-
});
|
|
208
|
+
test("missing tool_use → no leaves after retrying", async () => {
|
|
209
|
+
providerStub = makeProvider(noToolResponse());
|
|
174
210
|
const result = await routeL1(makeTurn("x"), makeTree());
|
|
175
|
-
expect(result).toEqual(
|
|
211
|
+
expect(result).toEqual([]);
|
|
212
|
+
expect(providerCalls).toHaveLength(3);
|
|
176
213
|
});
|
|
177
214
|
|
|
178
|
-
test("schema mismatch →
|
|
215
|
+
test("schema mismatch → no leaves after retrying", async () => {
|
|
179
216
|
providerStub = makeProvider(toolUseResponse({ ids: "not-an-array" }));
|
|
180
217
|
const result = await routeL1(makeTurn("x"), makeTree());
|
|
181
|
-
expect(result).toEqual(
|
|
218
|
+
expect(result).toEqual([]);
|
|
219
|
+
expect(providerCalls).toHaveLength(3);
|
|
182
220
|
});
|
|
183
221
|
|
|
184
|
-
test("provider throw →
|
|
185
|
-
providerStub =
|
|
186
|
-
name: "throwing",
|
|
187
|
-
sendMessage: async () => {
|
|
188
|
-
throw new Error("boom");
|
|
189
|
-
},
|
|
190
|
-
};
|
|
222
|
+
test("provider throw → no leaves after retrying", async () => {
|
|
223
|
+
providerStub = makeThrowingProvider();
|
|
191
224
|
const result = await routeL1(makeTurn("x"), makeTree());
|
|
192
|
-
expect(result).toEqual(
|
|
225
|
+
expect(result).toEqual([]);
|
|
226
|
+
expect(providerCalls).toHaveLength(3);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
test("a malformed response that recovers on retry returns its IDs", async () => {
|
|
230
|
+
providerStub = makeSequenceProvider([
|
|
231
|
+
noToolResponse(),
|
|
232
|
+
toolUseResponse({ ids: [2] }),
|
|
233
|
+
]);
|
|
234
|
+
const result = await routeL1(makeTurn("bob?"), makeTree());
|
|
235
|
+
expect(result).toEqual(["people/bob"]);
|
|
236
|
+
expect(providerCalls).toHaveLength(2);
|
|
193
237
|
});
|
|
194
238
|
});
|
|
195
239
|
|
|
@@ -213,11 +257,11 @@ describe("routeL1 — request shape", () => {
|
|
|
213
257
|
const [blockA, blockB] = providerCalls[0].messages[0].content as Array<{
|
|
214
258
|
type: string;
|
|
215
259
|
text: string;
|
|
216
|
-
cache_control?: { type: string };
|
|
260
|
+
cache_control?: { type: string; ttl?: string };
|
|
217
261
|
}>;
|
|
218
262
|
expect(blockA.type).toBe("text");
|
|
219
263
|
expect(blockA.text).toContain("<leaves>");
|
|
220
|
-
expect(blockA.cache_control).toEqual({ type: "ephemeral" });
|
|
264
|
+
expect(blockA.cache_control).toEqual({ type: "ephemeral", ttl: "1h" });
|
|
221
265
|
|
|
222
266
|
expect(blockB.type).toBe("text");
|
|
223
267
|
expect(blockB.text).toContain("<current_message>alice?</current_message>");
|
|
@@ -225,6 +269,34 @@ describe("routeL1 — request shape", () => {
|
|
|
225
269
|
expect(blockB.cache_control).toBeUndefined();
|
|
226
270
|
});
|
|
227
271
|
|
|
272
|
+
test("situational context renders in the per-turn block when present", async () => {
|
|
273
|
+
providerStub = makeProvider(toolUseResponse({ ids: [1] }));
|
|
274
|
+
await routeL1(
|
|
275
|
+
{
|
|
276
|
+
...makeTurn("x"),
|
|
277
|
+
situationalContext: "Today is Saturday. Alice's anniversary is today.",
|
|
278
|
+
},
|
|
279
|
+
makeTree(),
|
|
280
|
+
);
|
|
281
|
+
const blockB = providerCalls[0].messages[0].content[1] as { text: string };
|
|
282
|
+
expect(blockB.text).toContain(
|
|
283
|
+
"<situation>Today is Saturday. Alice's anniversary is today.</situation>",
|
|
284
|
+
);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
test("situational context is omitted when the turn has none", async () => {
|
|
288
|
+
providerStub = makeProvider(toolUseResponse({ ids: [1] }));
|
|
289
|
+
await routeL1(makeTurn("x"), makeTree());
|
|
290
|
+
const blockB = providerCalls[0].messages[0].content[1] as { text: string };
|
|
291
|
+
expect(blockB.text).not.toContain("<situation>");
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
test("system prompt mentions situation (locks the routing commitment)", async () => {
|
|
295
|
+
providerStub = makeProvider(toolUseResponse({ ids: [1] }));
|
|
296
|
+
await routeL1(makeTurn("x"), makeTree());
|
|
297
|
+
expect(providerCalls[0].options?.systemPrompt).toMatch(/[Ss]ituation/);
|
|
298
|
+
});
|
|
299
|
+
|
|
228
300
|
test("system prompt mentions register (locks the routing commitment)", async () => {
|
|
229
301
|
providerStub = makeProvider(toolUseResponse({ ids: [1] }));
|
|
230
302
|
await routeL1(makeTurn("x"), makeTree());
|
package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts
RENAMED
|
@@ -20,14 +20,14 @@ import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
|
20
20
|
|
|
21
21
|
import { drizzle } from "drizzle-orm/bun-sqlite";
|
|
22
22
|
|
|
23
|
-
import { migrateAddMemoryV3Selections } from "
|
|
24
|
-
import * as schema from "
|
|
23
|
+
import { migrateAddMemoryV3Selections } from "../../../../memory/migrations/268-add-memory-v3-selections.js";
|
|
24
|
+
import * as schema from "../../../../memory/schema.js";
|
|
25
25
|
|
|
26
26
|
const realFlags = {
|
|
27
|
-
...(await import("
|
|
27
|
+
...(await import("../../../../config/assistant-feature-flags.js")),
|
|
28
28
|
};
|
|
29
|
-
const realLoader = { ...(await import("
|
|
30
|
-
const realDb = { ...(await import("
|
|
29
|
+
const realLoader = { ...(await import("../../../../config/loader.js")) };
|
|
30
|
+
const realDb = { ...(await import("../../../../memory/db-connection.js")) };
|
|
31
31
|
const realPageContent = { ...(await import("../page-content.js")) };
|
|
32
32
|
|
|
33
33
|
let storeMockActive = false;
|
|
@@ -64,7 +64,7 @@ function seed(
|
|
|
64
64
|
}
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
mock.module("
|
|
67
|
+
mock.module("../../../../config/assistant-feature-flags.js", () => ({
|
|
68
68
|
...realFlags,
|
|
69
69
|
isAssistantFeatureFlagEnabled: (key: string, config: unknown) =>
|
|
70
70
|
storeMockActive
|
|
@@ -81,12 +81,12 @@ mock.module("../../../config/assistant-feature-flags.js", () => ({
|
|
|
81
81
|
),
|
|
82
82
|
}));
|
|
83
83
|
|
|
84
|
-
mock.module("
|
|
84
|
+
mock.module("../../../../config/loader.js", () => ({
|
|
85
85
|
...realLoader,
|
|
86
86
|
getConfig: () => (storeMockActive ? {} : realLoader.getConfig()),
|
|
87
87
|
}));
|
|
88
88
|
|
|
89
|
-
mock.module("
|
|
89
|
+
mock.module("../../../../memory/db-connection.js", () => ({
|
|
90
90
|
...realDb,
|
|
91
91
|
getDb: () => (storeMockActive ? testDb : realDb.getDb()),
|
|
92
92
|
getSqliteFrom: (db: unknown) =>
|
|
@@ -4,9 +4,12 @@
|
|
|
4
4
|
* Coverage matrix:
|
|
5
5
|
* - Returned IDs map to the right member slugs by 1-based index, with
|
|
6
6
|
* `pinned` driven by `pinned_ids`.
|
|
7
|
-
* - Omitted `ids` → ALL members of the leaf (recall-safe
|
|
7
|
+
* - Omitted `ids` → ALL members of the leaf (recall-safe; bounded to one
|
|
8
|
+
* leaf, so this stays a select-all unlike the L1 router).
|
|
8
9
|
* - Explicit `ids: []` → no pages (deliberate abstention).
|
|
9
|
-
* - No provider / missing tool_use / schema mismatch / throw →
|
|
10
|
+
* - No provider / missing tool_use / schema mismatch / throw → no pages
|
|
11
|
+
* (degrade to the deterministic lanes), the last three after a re-prompt
|
|
12
|
+
* retry; a malformed response that recovers on retry returns its pages.
|
|
10
13
|
* - The per-leaf `<pages>` prefix is byte-identical across two calls with
|
|
11
14
|
* different turns (the cache invariant).
|
|
12
15
|
* - `selectAcrossLeaves` flattens per-leaf results and never exceeds the
|
|
@@ -23,7 +26,7 @@ import type {
|
|
|
23
26
|
ProviderResponse,
|
|
24
27
|
SendMessageOptions,
|
|
25
28
|
ToolUseContent,
|
|
26
|
-
} from "
|
|
29
|
+
} from "../../../../providers/types.js";
|
|
27
30
|
import type {
|
|
28
31
|
LeafNode,
|
|
29
32
|
LeafPath,
|
|
@@ -45,13 +48,13 @@ interface ProviderCall {
|
|
|
45
48
|
}
|
|
46
49
|
const providerCalls: ProviderCall[] = [];
|
|
47
50
|
|
|
48
|
-
mock.module("
|
|
51
|
+
mock.module("../../../../providers/provider-send-message.js", () => ({
|
|
49
52
|
getConfiguredProvider: async () => providerStub,
|
|
50
53
|
extractToolUse: (response: ProviderResponse) =>
|
|
51
54
|
response.content.find((b): b is ToolUseContent => b.type === "tool_use"),
|
|
52
55
|
}));
|
|
53
56
|
|
|
54
|
-
mock.module("
|
|
57
|
+
mock.module("../../../../util/logger.js", () => ({
|
|
55
58
|
getLogger: () =>
|
|
56
59
|
new Proxy({} as Record<string, unknown>, {
|
|
57
60
|
get: (_t, prop) => (prop === "child" ? () => ({}) : () => {}),
|
|
@@ -83,6 +86,45 @@ function toolUseResponse(input: Record<string, unknown>): ProviderResponse {
|
|
|
83
86
|
};
|
|
84
87
|
}
|
|
85
88
|
|
|
89
|
+
/** A 200 response that carries no tool_use — the malformed-but-successful case
|
|
90
|
+
* the re-prompt retry exists to recover from. */
|
|
91
|
+
function noToolResponse(): ProviderResponse {
|
|
92
|
+
return {
|
|
93
|
+
model: "stub-model",
|
|
94
|
+
stopReason: "end_turn",
|
|
95
|
+
usage: { inputTokens: 0, outputTokens: 0 },
|
|
96
|
+
content: [{ type: "text", text: "no tool call" }],
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** Provider returning a different response per call (the i-th call returns
|
|
101
|
+
* responses[i], or the last entry once exhausted), recording each call so a
|
|
102
|
+
* test can assert how many attempts were made. */
|
|
103
|
+
function makeSequenceProvider(responses: ProviderResponse[]): Provider {
|
|
104
|
+
let i = 0;
|
|
105
|
+
return {
|
|
106
|
+
name: "sequence",
|
|
107
|
+
sendMessage: async (messages, options) => {
|
|
108
|
+
providerCalls.push({ messages, options });
|
|
109
|
+
const response = responses[Math.min(i, responses.length - 1)];
|
|
110
|
+
i += 1;
|
|
111
|
+
return response;
|
|
112
|
+
},
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Provider that records each call and then throws — for the throw-after-retries
|
|
117
|
+
* path (the provider's own RetryProvider has already exhausted its backoff). */
|
|
118
|
+
function makeThrowingProvider(): Provider {
|
|
119
|
+
return {
|
|
120
|
+
name: "throwing",
|
|
121
|
+
sendMessage: async (messages, options) => {
|
|
122
|
+
providerCalls.push({ messages, options });
|
|
123
|
+
throw new Error("boom");
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
86
128
|
function makeLeaf(path: LeafPath, members: Slug[]): LeafNode {
|
|
87
129
|
return {
|
|
88
130
|
path,
|
|
@@ -206,10 +248,8 @@ describe("selectFromLeaf — id mapping", () => {
|
|
|
206
248
|
// selectFromLeaf — recall-safe fallbacks.
|
|
207
249
|
// ---------------------------------------------------------------------------
|
|
208
250
|
|
|
209
|
-
describe("selectFromLeaf —
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
test("no provider → ALL members", async () => {
|
|
251
|
+
describe("selectFromLeaf — degradation on failure", () => {
|
|
252
|
+
test("no provider → no pages, without calling the provider", async () => {
|
|
213
253
|
providerStub = null;
|
|
214
254
|
const result = await selectFromLeaf(
|
|
215
255
|
"people/alice",
|
|
@@ -217,26 +257,23 @@ describe("selectFromLeaf — recall-safe fallbacks", () => {
|
|
|
217
257
|
makeTree(),
|
|
218
258
|
summaryOf,
|
|
219
259
|
);
|
|
220
|
-
expect(result).toEqual(
|
|
260
|
+
expect(result).toEqual([]);
|
|
261
|
+
expect(providerCalls).toHaveLength(0);
|
|
221
262
|
});
|
|
222
263
|
|
|
223
|
-
test("missing tool_use →
|
|
224
|
-
providerStub = makeProvider(
|
|
225
|
-
model: "stub-model",
|
|
226
|
-
stopReason: "end_turn",
|
|
227
|
-
usage: { inputTokens: 0, outputTokens: 0 },
|
|
228
|
-
content: [{ type: "text", text: "no tool call" }],
|
|
229
|
-
});
|
|
264
|
+
test("missing tool_use → no pages after retrying", async () => {
|
|
265
|
+
providerStub = makeProvider(noToolResponse());
|
|
230
266
|
const result = await selectFromLeaf(
|
|
231
267
|
"people/alice",
|
|
232
268
|
makeTurn("x"),
|
|
233
269
|
makeTree(),
|
|
234
270
|
summaryOf,
|
|
235
271
|
);
|
|
236
|
-
expect(result).toEqual(
|
|
272
|
+
expect(result).toEqual([]);
|
|
273
|
+
expect(providerCalls).toHaveLength(3);
|
|
237
274
|
});
|
|
238
275
|
|
|
239
|
-
test("schema mismatch →
|
|
276
|
+
test("schema mismatch → no pages after retrying", async () => {
|
|
240
277
|
providerStub = makeProvider(toolUseResponse({ ids: "not-an-array" }));
|
|
241
278
|
const result = await selectFromLeaf(
|
|
242
279
|
"people/alice",
|
|
@@ -244,23 +281,35 @@ describe("selectFromLeaf — recall-safe fallbacks", () => {
|
|
|
244
281
|
makeTree(),
|
|
245
282
|
summaryOf,
|
|
246
283
|
);
|
|
247
|
-
expect(result).toEqual(
|
|
284
|
+
expect(result).toEqual([]);
|
|
285
|
+
expect(providerCalls).toHaveLength(3);
|
|
248
286
|
});
|
|
249
287
|
|
|
250
|
-
test("provider throw →
|
|
251
|
-
providerStub =
|
|
252
|
-
name: "throwing",
|
|
253
|
-
sendMessage: async () => {
|
|
254
|
-
throw new Error("boom");
|
|
255
|
-
},
|
|
256
|
-
};
|
|
288
|
+
test("provider throw → no pages after retrying", async () => {
|
|
289
|
+
providerStub = makeThrowingProvider();
|
|
257
290
|
const result = await selectFromLeaf(
|
|
258
291
|
"people/alice",
|
|
259
292
|
makeTurn("x"),
|
|
260
293
|
makeTree(),
|
|
261
294
|
summaryOf,
|
|
262
295
|
);
|
|
263
|
-
expect(result).toEqual(
|
|
296
|
+
expect(result).toEqual([]);
|
|
297
|
+
expect(providerCalls).toHaveLength(3);
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
test("a malformed response that recovers on retry returns its pages", async () => {
|
|
301
|
+
providerStub = makeSequenceProvider([
|
|
302
|
+
noToolResponse(),
|
|
303
|
+
toolUseResponse({ ids: [2] }),
|
|
304
|
+
]);
|
|
305
|
+
const result = await selectFromLeaf(
|
|
306
|
+
"people/alice",
|
|
307
|
+
makeTurn("the 1:1"),
|
|
308
|
+
makeTree(),
|
|
309
|
+
summaryOf,
|
|
310
|
+
);
|
|
311
|
+
expect(result).toEqual([{ slug: "alice-1on1", pinned: false }]);
|
|
312
|
+
expect(providerCalls).toHaveLength(2);
|
|
264
313
|
});
|
|
265
314
|
});
|
|
266
315
|
|
|
@@ -298,13 +347,13 @@ describe("selectFromLeaf — request shape", () => {
|
|
|
298
347
|
const [blockA, blockB] = providerCalls[0].messages[0].content as Array<{
|
|
299
348
|
type: string;
|
|
300
349
|
text: string;
|
|
301
|
-
cache_control?: { type: string };
|
|
350
|
+
cache_control?: { type: string; ttl?: string };
|
|
302
351
|
}>;
|
|
303
352
|
expect(blockA.type).toBe("text");
|
|
304
353
|
expect(blockA.text).toContain("<leaf>people/alice</leaf>");
|
|
305
354
|
expect(blockA.text).toContain("<pages>");
|
|
306
355
|
expect(blockA.text).toContain("[1] alice-bio — summary of alice-bio");
|
|
307
|
-
expect(blockA.cache_control).toEqual({ type: "ephemeral" });
|
|
356
|
+
expect(blockA.cache_control).toEqual({ type: "ephemeral", ttl: "1h" });
|
|
308
357
|
|
|
309
358
|
expect(blockB.type).toBe("text");
|
|
310
359
|
expect(blockB.text).toContain("<current_message>alice?</current_message>");
|
|
@@ -312,6 +361,23 @@ describe("selectFromLeaf — request shape", () => {
|
|
|
312
361
|
expect(blockB.cache_control).toBeUndefined();
|
|
313
362
|
});
|
|
314
363
|
|
|
364
|
+
test("situational context renders in the per-turn block when present", async () => {
|
|
365
|
+
providerStub = makeProvider(toolUseResponse({ ids: [1] }));
|
|
366
|
+
await selectFromLeaf(
|
|
367
|
+
"people/alice",
|
|
368
|
+
{
|
|
369
|
+
...makeTurn("alice?"),
|
|
370
|
+
situationalContext: "Today is Saturday. Alice's anniversary is today.",
|
|
371
|
+
},
|
|
372
|
+
makeTree(),
|
|
373
|
+
summaryOf,
|
|
374
|
+
);
|
|
375
|
+
const blockB = providerCalls[0].messages[0].content[1] as { text: string };
|
|
376
|
+
expect(blockB.text).toContain(
|
|
377
|
+
"<situation>Today is Saturday. Alice's anniversary is today.</situation>",
|
|
378
|
+
);
|
|
379
|
+
});
|
|
380
|
+
|
|
315
381
|
test("system prompt mentions pinned (locks the pinning commitment)", async () => {
|
|
316
382
|
providerStub = makeProvider(toolUseResponse({ ids: [1] }));
|
|
317
383
|
await selectFromLeaf("people/alice", makeTurn("x"), makeTree(), summaryOf);
|