@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
|
@@ -1,23 +1,28 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
+
import {
|
|
4
|
+
clearConversations,
|
|
5
|
+
setConversation,
|
|
6
|
+
} from "../daemon/conversation-registry.js";
|
|
7
|
+
import type { ChannelCapabilities } from "../daemon/conversation-runtime-assembly.js";
|
|
3
8
|
import {
|
|
4
9
|
applyRuntimeInjections,
|
|
5
10
|
stripInjectionsForCompaction,
|
|
6
11
|
} from "../daemon/conversation-runtime-assembly.js";
|
|
7
12
|
import {
|
|
8
13
|
DEFAULT_INJECTOR_ORDER,
|
|
9
|
-
|
|
14
|
+
defaultInjectors,
|
|
10
15
|
DISK_PRESSURE_WARNING_PROMPT,
|
|
11
|
-
} from "../plugins/defaults/injectors
|
|
12
|
-
import {
|
|
13
|
-
registerPlugin,
|
|
14
|
-
resetPluginRegistryForTests,
|
|
15
|
-
} from "../plugins/registry.js";
|
|
16
|
+
} from "../plugins/defaults/memory-retrieval/injectors.js";
|
|
16
17
|
import type { Injector, TurnContext } from "../plugins/types.js";
|
|
17
18
|
import type { Message } from "../providers/types.js";
|
|
18
19
|
|
|
20
|
+
// `makeContext` and the workspace registry seed share this id so the
|
|
21
|
+
// `workspace-context` injector resolves the seeded block for the turn.
|
|
22
|
+
const TEST_CONVERSATION_ID = "conv-test";
|
|
23
|
+
|
|
19
24
|
function findInjector(name: string): Injector {
|
|
20
|
-
const injector =
|
|
25
|
+
const injector = defaultInjectors.find(
|
|
21
26
|
(candidate) => candidate.name === name,
|
|
22
27
|
);
|
|
23
28
|
if (!injector) {
|
|
@@ -29,7 +34,7 @@ function findInjector(name: string): Injector {
|
|
|
29
34
|
function makeContext(overrides: Partial<TurnContext> = {}): TurnContext {
|
|
30
35
|
return {
|
|
31
36
|
requestId: "req-test",
|
|
32
|
-
conversationId:
|
|
37
|
+
conversationId: TEST_CONVERSATION_ID,
|
|
33
38
|
turnIndex: 0,
|
|
34
39
|
trust: { sourceChannel: "vellum", trustClass: "guardian" },
|
|
35
40
|
...overrides,
|
|
@@ -47,20 +52,58 @@ function tailTexts(messages: Message[]): string[] {
|
|
|
47
52
|
}
|
|
48
53
|
|
|
49
54
|
const diskPressureInjector = findInjector("disk-pressure-warning");
|
|
50
|
-
|
|
55
|
+
|
|
56
|
+
// The disk-pressure-warning and workspace-context injectors both read their
|
|
57
|
+
// per-conversation state off the live `Conversation` looked up by
|
|
58
|
+
// `conversationId`. Seed a single fake instance under the id `makeContext()`
|
|
59
|
+
// uses, carrying the disk-pressure flag and the workspace top-level cache, so a
|
|
60
|
+
// turn can exercise both; the seed helpers mutate and re-register the same
|
|
61
|
+
// instance. An empty (non-dirty) workspace cache resolves to no block, so
|
|
62
|
+
// disk-pressure-only tests don't trigger a directory scan.
|
|
63
|
+
let liveConversation: {
|
|
64
|
+
conversationId: string;
|
|
65
|
+
workingDir: string;
|
|
66
|
+
workspaceTopLevelContext: string | null;
|
|
67
|
+
workspaceTopLevelDirty: boolean;
|
|
68
|
+
diskPressureCleanupModeActive: boolean;
|
|
69
|
+
channelCapabilities?: ChannelCapabilities;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
function resetLiveConversation(): void {
|
|
73
|
+
liveConversation = {
|
|
74
|
+
conversationId: TEST_CONVERSATION_ID,
|
|
75
|
+
workingDir: "/workspace",
|
|
76
|
+
workspaceTopLevelContext: "",
|
|
77
|
+
workspaceTopLevelDirty: false,
|
|
78
|
+
diskPressureCleanupModeActive: false,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function seedChannelCapabilities(caps: ChannelCapabilities): void {
|
|
83
|
+
liveConversation.channelCapabilities = caps;
|
|
84
|
+
setConversation(TEST_CONVERSATION_ID, liveConversation as never);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function seedDiskPressure(cleanupModeActive: boolean): void {
|
|
88
|
+
liveConversation.diskPressureCleanupModeActive = cleanupModeActive;
|
|
89
|
+
setConversation(TEST_CONVERSATION_ID, liveConversation as never);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function seedWorkspaceContext(text: string): void {
|
|
93
|
+
liveConversation.workspaceTopLevelContext = text;
|
|
94
|
+
liveConversation.workspaceTopLevelDirty = false;
|
|
95
|
+
setConversation(TEST_CONVERSATION_ID, liveConversation as never);
|
|
96
|
+
}
|
|
51
97
|
|
|
52
98
|
describe("disk-pressure-warning injector", () => {
|
|
53
99
|
beforeEach(() => {
|
|
54
|
-
|
|
55
|
-
|
|
100
|
+
clearConversations();
|
|
101
|
+
resetLiveConversation();
|
|
56
102
|
});
|
|
57
103
|
|
|
58
104
|
test("emits the exact cleanup prompt during disk pressure cleanup mode", async () => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
injectionInputs: { diskPressureContext: cleanupContext },
|
|
62
|
-
}),
|
|
63
|
-
);
|
|
105
|
+
seedDiskPressure(true);
|
|
106
|
+
const block = await diskPressureInjector.produce(makeContext());
|
|
64
107
|
|
|
65
108
|
expect(block).toEqual({
|
|
66
109
|
id: "disk-pressure-warning",
|
|
@@ -81,21 +124,14 @@ Do not work on unrelated tasks until enough space is freed to clear the lock or
|
|
|
81
124
|
</disk_pressure_warning>`);
|
|
82
125
|
});
|
|
83
126
|
|
|
84
|
-
test("omits the prompt when cleanup context is
|
|
127
|
+
test("omits the prompt when no cleanup context is registered or it is inactive", async () => {
|
|
85
128
|
await expect(
|
|
86
|
-
diskPressureInjector.produce(
|
|
87
|
-
makeContext({ injectionInputs: { diskPressureContext: null } }),
|
|
88
|
-
),
|
|
129
|
+
diskPressureInjector.produce(makeContext()),
|
|
89
130
|
).resolves.toBeNull();
|
|
90
131
|
|
|
132
|
+
seedDiskPressure(false);
|
|
91
133
|
await expect(
|
|
92
|
-
diskPressureInjector.produce(
|
|
93
|
-
makeContext({
|
|
94
|
-
injectionInputs: {
|
|
95
|
-
diskPressureContext: { cleanupModeActive: false },
|
|
96
|
-
},
|
|
97
|
-
}),
|
|
98
|
-
),
|
|
134
|
+
diskPressureInjector.produce(makeContext()),
|
|
99
135
|
).resolves.toBeNull();
|
|
100
136
|
});
|
|
101
137
|
|
|
@@ -106,10 +142,10 @@ Do not work on unrelated tasks until enough space is freed to clear the lock or
|
|
|
106
142
|
const workspace = "<workspace>\nRoot: /workspace\n</workspace>";
|
|
107
143
|
const turnContext = "<turn_context>\ninterface: macos\n</turn_context>";
|
|
108
144
|
|
|
145
|
+
seedWorkspaceContext(workspace);
|
|
146
|
+
seedDiskPressure(true);
|
|
109
147
|
const result = await applyRuntimeInjections(runMessages, {
|
|
110
148
|
turnContext: makeContext(),
|
|
111
|
-
diskPressureContext: cleanupContext,
|
|
112
|
-
workspaceTopLevelContext: workspace,
|
|
113
149
|
unifiedTurnContext: turnContext,
|
|
114
150
|
});
|
|
115
151
|
|
|
@@ -127,13 +163,12 @@ Do not work on unrelated tasks until enough space is freed to clear the lock or
|
|
|
127
163
|
});
|
|
128
164
|
|
|
129
165
|
test("survives minimal mode as safety-critical context", async () => {
|
|
166
|
+
seedDiskPressure(true);
|
|
130
167
|
const result = await applyRuntimeInjections(
|
|
131
168
|
[{ role: "user", content: [{ type: "text", text: "status" }] }],
|
|
132
169
|
{
|
|
133
170
|
turnContext: makeContext(),
|
|
134
171
|
mode: "minimal",
|
|
135
|
-
diskPressureContext: cleanupContext,
|
|
136
|
-
workspaceTopLevelContext: "<workspace>...</workspace>",
|
|
137
172
|
unifiedTurnContext: "<turn_context>...</turn_context>",
|
|
138
173
|
},
|
|
139
174
|
);
|
|
@@ -163,16 +198,16 @@ Do not work on unrelated tasks until enough space is freed to clear the lock or
|
|
|
163
198
|
},
|
|
164
199
|
];
|
|
165
200
|
|
|
201
|
+
seedDiskPressure(true);
|
|
202
|
+
seedChannelCapabilities({
|
|
203
|
+
channel: "slack",
|
|
204
|
+
dashboardCapable: false,
|
|
205
|
+
supportsDynamicUi: false,
|
|
206
|
+
supportsVoiceInput: false,
|
|
207
|
+
chatType: "channel",
|
|
208
|
+
});
|
|
166
209
|
const result = await applyRuntimeInjections(originalRun, {
|
|
167
210
|
turnContext: makeContext(),
|
|
168
|
-
diskPressureContext: cleanupContext,
|
|
169
|
-
channelCapabilities: {
|
|
170
|
-
channel: "slack",
|
|
171
|
-
dashboardCapable: false,
|
|
172
|
-
supportsDynamicUi: false,
|
|
173
|
-
supportsVoiceInput: false,
|
|
174
|
-
chatType: "channel",
|
|
175
|
-
},
|
|
176
211
|
slackChronologicalMessages: slackTranscript,
|
|
177
212
|
});
|
|
178
213
|
|
|
@@ -190,16 +225,15 @@ Do not work on unrelated tasks until enough space is freed to clear the lock or
|
|
|
190
225
|
{ role: "user", content: [{ type: "text", text: "find large files" }] },
|
|
191
226
|
];
|
|
192
227
|
|
|
228
|
+
seedDiskPressure(true);
|
|
193
229
|
const first = await applyRuntimeInjections(runMessages, {
|
|
194
230
|
turnContext: makeContext(),
|
|
195
|
-
diskPressureContext: cleanupContext,
|
|
196
231
|
});
|
|
197
232
|
const stripped = stripInjectionsForCompaction(first.messages);
|
|
198
233
|
expect(tailTexts(stripped)).toEqual(["find large files"]);
|
|
199
234
|
|
|
200
235
|
const second = await applyRuntimeInjections(stripped, {
|
|
201
236
|
turnContext: makeContext(),
|
|
202
|
-
diskPressureContext: cleanupContext,
|
|
203
237
|
});
|
|
204
238
|
expect(
|
|
205
239
|
tailTexts(second.messages).filter(
|
|
@@ -8,12 +8,12 @@ mock.module("../documents/document-comments-store.js", () => ({
|
|
|
8
8
|
listComments: (...args: unknown[]) => listCommentsMock(...args),
|
|
9
9
|
}));
|
|
10
10
|
|
|
11
|
-
const { DEFAULT_INJECTOR_ORDER,
|
|
12
|
-
await import("../plugins/defaults/injectors
|
|
11
|
+
const { DEFAULT_INJECTOR_ORDER, defaultInjectors } =
|
|
12
|
+
await import("../plugins/defaults/memory-retrieval/injectors.js");
|
|
13
13
|
import type { Injector, TurnContext } from "../plugins/types.js";
|
|
14
14
|
|
|
15
15
|
function findInjector(name: string): Injector {
|
|
16
|
-
const injector =
|
|
16
|
+
const injector = defaultInjectors.find(
|
|
17
17
|
(candidate) => candidate.name === name,
|
|
18
18
|
);
|
|
19
19
|
if (!injector) {
|
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
* search so the reminder-with-hints branch can resolve deterministically
|
|
13
13
|
* when called.
|
|
14
14
|
*/
|
|
15
|
-
import {
|
|
15
|
+
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
16
|
+
import { dirname, join } from "node:path";
|
|
17
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
16
18
|
|
|
17
19
|
let v2Active = false;
|
|
18
20
|
|
|
@@ -20,7 +22,12 @@ const realLoader = await import("../config/loader.js");
|
|
|
20
22
|
|
|
21
23
|
mock.module("../config/loader.js", () => ({
|
|
22
24
|
...realLoader,
|
|
23
|
-
getConfig: () => ({
|
|
25
|
+
getConfig: () => ({
|
|
26
|
+
memory: {
|
|
27
|
+
v2: { enabled: v2Active },
|
|
28
|
+
retrieval: { scratchpadInjection: { enabled: true } },
|
|
29
|
+
},
|
|
30
|
+
}),
|
|
24
31
|
}));
|
|
25
32
|
|
|
26
33
|
mock.module("../memory/pkb/pkb-search.js", () => ({
|
|
@@ -29,12 +36,10 @@ mock.module("../memory/pkb/pkb-search.js", () => ({
|
|
|
29
36
|
|
|
30
37
|
const { applyRuntimeInjections } =
|
|
31
38
|
await import("../daemon/conversation-runtime-assembly.js");
|
|
32
|
-
|
|
33
|
-
await import("../plugins/defaults/injectors/register.js");
|
|
34
|
-
const { registerPlugin, resetPluginRegistryForTests } =
|
|
35
|
-
await import("../plugins/registry.js");
|
|
39
|
+
import { getPkbRoot } from "../memory/pkb/types.js";
|
|
36
40
|
import type { TurnContext } from "../plugins/types.js";
|
|
37
41
|
import type { Message } from "../providers/types.js";
|
|
42
|
+
import { getWorkspacePromptPath } from "../util/platform.js";
|
|
38
43
|
|
|
39
44
|
function makeTurnContext(): TurnContext {
|
|
40
45
|
return {
|
|
@@ -55,28 +60,37 @@ function tailTexts(messages: Message[]): string[] {
|
|
|
55
60
|
.map((b) => b.text);
|
|
56
61
|
}
|
|
57
62
|
|
|
58
|
-
const PKB_CONTEXT = "essentials of the project";
|
|
59
|
-
const NOW_CONTENT = "Current focus: shipping G2.1";
|
|
60
63
|
const RUN_MESSAGES: Message[] = [
|
|
61
64
|
{ role: "user", content: [{ type: "text", text: "What next?" }] },
|
|
62
65
|
];
|
|
63
66
|
|
|
64
67
|
describe("PKB injector v2 cutover behavior", () => {
|
|
68
|
+
// The pkb-reminder gate derives PKB-active state from the workspace
|
|
69
|
+
// (`readPkbContext()` returning content) behind the guardian trust on
|
|
70
|
+
// `makeTurnContext()`, so seed a default auto-injected PKB file rather than
|
|
71
|
+
// passing a flag.
|
|
65
72
|
beforeEach(() => {
|
|
66
|
-
resetPluginRegistryForTests();
|
|
67
|
-
registerPlugin(defaultInjectorsPlugin);
|
|
68
73
|
v2Active = false;
|
|
74
|
+
mkdirSync(getPkbRoot(), { recursive: true });
|
|
75
|
+
writeFileSync(
|
|
76
|
+
join(getPkbRoot(), "INDEX.md"),
|
|
77
|
+
"workspace knowledge index",
|
|
78
|
+
"utf-8",
|
|
79
|
+
);
|
|
80
|
+
// now-md sources NOW.md from the workspace behind the same guardian trust,
|
|
81
|
+
// so seed the file rather than passing a flag.
|
|
82
|
+
const nowPath = getWorkspacePromptPath("NOW.md");
|
|
83
|
+
mkdirSync(dirname(nowPath), { recursive: true });
|
|
84
|
+
writeFileSync(nowPath, "Current focus: shipping G2.1", "utf-8");
|
|
85
|
+
});
|
|
86
|
+
afterEach(() => {
|
|
87
|
+
rmSync(getPkbRoot(), { recursive: true, force: true });
|
|
88
|
+
rmSync(getWorkspacePromptPath("NOW.md"), { force: true });
|
|
69
89
|
});
|
|
70
90
|
|
|
71
91
|
test("v2 inactive → pkb-context, pkb-reminder, and now-md all produce blocks", async () => {
|
|
72
92
|
const result = await applyRuntimeInjections(RUN_MESSAGES, {
|
|
73
93
|
turnContext: makeTurnContext(),
|
|
74
|
-
pkbContext: PKB_CONTEXT,
|
|
75
|
-
pkbActive: true,
|
|
76
|
-
pkbScopeId: "scope-default",
|
|
77
|
-
pkbRoot: "/tmp/pkb",
|
|
78
|
-
pkbConversation: { messages: [] },
|
|
79
|
-
nowScratchpad: NOW_CONTENT,
|
|
80
94
|
});
|
|
81
95
|
|
|
82
96
|
const texts = tailTexts(result.messages);
|
|
@@ -89,12 +103,6 @@ describe("PKB injector v2 cutover behavior", () => {
|
|
|
89
103
|
v2Active = true;
|
|
90
104
|
const result = await applyRuntimeInjections(RUN_MESSAGES, {
|
|
91
105
|
turnContext: makeTurnContext(),
|
|
92
|
-
pkbContext: PKB_CONTEXT,
|
|
93
|
-
pkbActive: true,
|
|
94
|
-
pkbScopeId: "scope-default",
|
|
95
|
-
pkbRoot: "/tmp/pkb",
|
|
96
|
-
pkbConversation: { messages: [] },
|
|
97
|
-
nowScratchpad: NOW_CONTENT,
|
|
98
106
|
});
|
|
99
107
|
|
|
100
108
|
const texts = tailTexts(result.messages);
|
|
@@ -1,34 +1,38 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tests for the memory-v3-live v2-suppression branch in
|
|
3
|
-
* `applyRuntimeInjections
|
|
3
|
+
* `applyRuntimeInjections`.
|
|
4
4
|
*
|
|
5
|
-
* When `
|
|
5
|
+
* When the `memory-v3-live` flag is on AND the v3 injector (id `memory-v3`,
|
|
6
6
|
* placement `after-memory-prefix`) actually produces a block, runtime
|
|
7
7
|
* assembly strips the v2 `<memory>` prefix from EVERY user message before
|
|
8
8
|
* splicing the v3 block — so v3 becomes the sole `<memory>` source and history
|
|
9
9
|
* is byte-stable for prompt caching.
|
|
10
10
|
*
|
|
11
|
-
* Keyed off whether v3 produced a block, NOT off the
|
|
11
|
+
* Keyed off whether v3 produced a block, NOT off the flag alone: a v3
|
|
12
12
|
* failure (`produce()` → null) leaves v2's block intact (fallback-to-v2).
|
|
13
13
|
*
|
|
14
14
|
* The flag-off path must be byte-for-byte identical to today — that is the
|
|
15
|
-
* load-bearing regression guard.
|
|
15
|
+
* load-bearing regression guard. `applyRuntimeInjections` reads the flag
|
|
16
|
+
* itself, so these tests drive it through the override cache.
|
|
16
17
|
*/
|
|
17
18
|
|
|
18
|
-
import { beforeEach, describe, expect, test } from "bun:test";
|
|
19
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
19
20
|
|
|
20
|
-
import {
|
|
21
|
-
getInjectors,
|
|
22
|
-
registerPlugin,
|
|
23
|
-
resetPluginRegistryForTests,
|
|
24
|
-
} from "../plugins/registry.js";
|
|
25
21
|
import type {
|
|
26
22
|
InjectionBlock,
|
|
27
23
|
Injector,
|
|
28
|
-
Plugin,
|
|
29
24
|
TurnContext,
|
|
30
25
|
} from "../plugins/types.js";
|
|
31
26
|
import type { Message } from "../providers/types.js";
|
|
27
|
+
import { setOverridesForTesting } from "./feature-flag-test-helpers.js";
|
|
28
|
+
|
|
29
|
+
// Drive the suppression branch by controlling the static injector chain that
|
|
30
|
+
// `applyRuntimeInjections` walks. The slot is mutated per-test to stand in for
|
|
31
|
+
// the memory-v3 injector producing (or not producing) a block.
|
|
32
|
+
const injectorChainSlot: Injector[] = [];
|
|
33
|
+
mock.module("../plugins/defaults/memory-retrieval/injector-chain.js", () => ({
|
|
34
|
+
getInjectorChain: () => injectorChainSlot,
|
|
35
|
+
}));
|
|
32
36
|
|
|
33
37
|
const { applyRuntimeInjections } =
|
|
34
38
|
await import("../daemon/conversation-runtime-assembly.js");
|
|
@@ -42,10 +46,6 @@ function makeTurnContext(): TurnContext {
|
|
|
42
46
|
};
|
|
43
47
|
}
|
|
44
48
|
|
|
45
|
-
function wrapInPlugin(name: string, injectors: Injector[]): Plugin {
|
|
46
|
-
return { manifest: { name, version: "0.0.1" }, injectors };
|
|
47
|
-
}
|
|
48
|
-
|
|
49
49
|
/**
|
|
50
50
|
* A fake v3 injector that mirrors the real one's id + placement. The real
|
|
51
51
|
* injector's `renderMemoryBlock` already wraps its content in
|
|
@@ -88,11 +88,15 @@ function tailTexts(messages: Message[]): string[] {
|
|
|
88
88
|
|
|
89
89
|
describe("memory-v3-live v2 suppression", () => {
|
|
90
90
|
beforeEach(() => {
|
|
91
|
-
|
|
91
|
+
injectorChainSlot.length = 0;
|
|
92
|
+
// Clean baseline: no overrides → `memory-v3-live` resolves to its registry
|
|
93
|
+
// default (off). Each test seeds the flag it needs.
|
|
94
|
+
setOverridesForTesting({});
|
|
92
95
|
});
|
|
93
96
|
|
|
94
97
|
test("flag ON + v3 produced a block → v2 stripped from all turns, exactly one <memory> (the v3 block)", async () => {
|
|
95
|
-
|
|
98
|
+
setOverridesForTesting({ "memory-v3-live": true });
|
|
99
|
+
injectorChainSlot.push(v3Injector("v3 working set"));
|
|
96
100
|
|
|
97
101
|
// History: a prior user turn that still carries a v2 block (rehydrated),
|
|
98
102
|
// plus the current tail user turn with its own v2 block.
|
|
@@ -107,7 +111,6 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
107
111
|
|
|
108
112
|
const result = await applyRuntimeInjections(runMessages, {
|
|
109
113
|
turnContext: makeTurnContext(),
|
|
110
|
-
suppressV2MemoryForV3: true,
|
|
111
114
|
});
|
|
112
115
|
|
|
113
116
|
// Exactly one <memory> source across the WHOLE assembled context.
|
|
@@ -138,7 +141,8 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
138
141
|
});
|
|
139
142
|
|
|
140
143
|
test("flag ON but v3 produced NOTHING → v2 block left intact (fallback-to-v2)", async () => {
|
|
141
|
-
|
|
144
|
+
setOverridesForTesting({ "memory-v3-live": true });
|
|
145
|
+
injectorChainSlot.push(v3Injector(null));
|
|
142
146
|
|
|
143
147
|
const runMessages: Message[] = [
|
|
144
148
|
userMsgWithV2Memory("fresh recalled fact", "current question"),
|
|
@@ -146,7 +150,6 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
146
150
|
|
|
147
151
|
const result = await applyRuntimeInjections(runMessages, {
|
|
148
152
|
turnContext: makeTurnContext(),
|
|
149
|
-
suppressV2MemoryForV3: true,
|
|
150
153
|
});
|
|
151
154
|
|
|
152
155
|
// v2's block survives — the turn still ships memory.
|
|
@@ -158,7 +161,7 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
158
161
|
});
|
|
159
162
|
|
|
160
163
|
test("flag OFF → byte-for-byte identical to today even when v3 would have produced a block", async () => {
|
|
161
|
-
|
|
164
|
+
injectorChainSlot.push(v3Injector("v3 working set"));
|
|
162
165
|
|
|
163
166
|
const runMessages: Message[] = [
|
|
164
167
|
userMsgWithV2Memory("old recalled fact", "earlier question"),
|
|
@@ -169,16 +172,16 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
169
172
|
userMsgWithV2Memory("fresh recalled fact", "current question"),
|
|
170
173
|
];
|
|
171
174
|
|
|
172
|
-
// With
|
|
173
|
-
// (after-memory-prefix), but NO v2 stripping happens. This
|
|
174
|
-
// exact pre-flag assembly behavior: v2 prefix stays, v3
|
|
175
|
+
// With the flag off (registry default), the v3 injector still runs through
|
|
176
|
+
// the chain (after-memory-prefix), but NO v2 stripping happens. This
|
|
177
|
+
// captures the exact pre-flag assembly behavior: v2 prefix stays, v3
|
|
178
|
+
// splices after it.
|
|
175
179
|
const offResult = await applyRuntimeInjections(runMessages, {
|
|
176
180
|
turnContext: makeTurnContext(),
|
|
177
|
-
suppressV2MemoryForV3: false,
|
|
178
181
|
});
|
|
179
182
|
|
|
180
183
|
// The tail keeps v2's block AND gains v3's (the historical double-injection
|
|
181
|
-
// the suppression
|
|
184
|
+
// the suppression exists to prevent) — proving suppression is the ONLY
|
|
182
185
|
// behavior change and it is fully gated off here.
|
|
183
186
|
const texts = tailTexts(offResult.messages);
|
|
184
187
|
expect(texts[0]).toBe("<memory>\nfresh recalled fact\n</memory>");
|
|
@@ -190,21 +193,13 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
190
193
|
.filter((b): b is { type: "text"; text: string } => b.type === "text")
|
|
191
194
|
.map((b) => b.text);
|
|
192
195
|
expect(firstUserTexts[0]).toBe("<memory>\nold recalled fact\n</memory>");
|
|
193
|
-
|
|
194
|
-
// Strongest guard: omitting the option entirely yields the SAME result as
|
|
195
|
-
// passing it false — the default path is untouched by this PR.
|
|
196
|
-
resetPluginRegistryForTests();
|
|
197
|
-
registerPlugin(wrapInPlugin("v3", [v3Injector("v3 working set")]));
|
|
198
|
-
const defaultResult = await applyRuntimeInjections(runMessages, {
|
|
199
|
-
turnContext: makeTurnContext(),
|
|
200
|
-
});
|
|
201
|
-
expect(defaultResult.messages).toEqual(offResult.messages);
|
|
202
196
|
});
|
|
203
197
|
|
|
204
198
|
test("no v3 injector registered + flag ON → no stripping, messages untouched", async () => {
|
|
205
199
|
// No injector named memory-v3 at all (e.g. plugin not loaded): the
|
|
206
200
|
// suppression branch keys off the produced block, so nothing is stripped.
|
|
207
|
-
|
|
201
|
+
setOverridesForTesting({ "memory-v3-live": true });
|
|
202
|
+
expect(injectorChainSlot).toHaveLength(0);
|
|
208
203
|
|
|
209
204
|
const runMessages: Message[] = [
|
|
210
205
|
userMsgWithV2Memory("fresh recalled fact", "current question"),
|
|
@@ -212,7 +207,6 @@ describe("memory-v3-live v2 suppression", () => {
|
|
|
212
207
|
|
|
213
208
|
const result = await applyRuntimeInjections(runMessages, {
|
|
214
209
|
turnContext: makeTurnContext(),
|
|
215
|
-
suppressV2MemoryForV3: true,
|
|
216
210
|
});
|
|
217
211
|
|
|
218
212
|
expect(result.messages).toEqual(runMessages);
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
mock.module("../util/logger.js", () => ({
|
|
4
|
+
getLogger: () =>
|
|
5
|
+
new Proxy({} as Record<string, unknown>, {
|
|
6
|
+
get: () => () => {},
|
|
7
|
+
}),
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
// Toggle for the collectUsageData opt-out the real store consults. The store
|
|
11
|
+
// module is intentionally NOT mocked here — it has its own DB-backed tests, and
|
|
12
|
+
// Bun's `mock.module` is process-global, so mocking it would leak into those
|
|
13
|
+
// tests when files share an invocation. Exercising the real store keeps every
|
|
14
|
+
// auth-fallback test order-independent.
|
|
15
|
+
let collectUsageData = true;
|
|
16
|
+
|
|
17
|
+
mock.module("../config/loader.js", () => ({
|
|
18
|
+
getConfig: () => ({
|
|
19
|
+
ui: {},
|
|
20
|
+
model: "test",
|
|
21
|
+
provider: "test",
|
|
22
|
+
memory: { enabled: false },
|
|
23
|
+
rateLimit: { maxRequestsPerMinute: 0 },
|
|
24
|
+
secretDetection: { enabled: false },
|
|
25
|
+
collectUsageData,
|
|
26
|
+
}),
|
|
27
|
+
}));
|
|
28
|
+
|
|
29
|
+
import { queryUnreportedAuthFallbackEvents } from "../memory/auth-fallback-events-store.js";
|
|
30
|
+
import { getDb } from "../memory/db-connection.js";
|
|
31
|
+
import { initializeDb } from "../memory/db-init.js";
|
|
32
|
+
import { authFallbackEvents } from "../memory/schema.js";
|
|
33
|
+
import { GATEWAY_PRINCIPALS } from "../runtime/auth/route-policy.js";
|
|
34
|
+
import { RouteError } from "../runtime/routes/errors.js";
|
|
35
|
+
import { ROUTES } from "../runtime/routes/internal-telemetry-routes.js";
|
|
36
|
+
import type { RouteHandlerArgs } from "../runtime/routes/types.js";
|
|
37
|
+
|
|
38
|
+
initializeDb();
|
|
39
|
+
|
|
40
|
+
const route = ROUTES.find(
|
|
41
|
+
(r) => r.operationId === "internal_telemetry_auth_fallback",
|
|
42
|
+
);
|
|
43
|
+
|
|
44
|
+
function call(body: unknown) {
|
|
45
|
+
if (!route) throw new Error("route not found");
|
|
46
|
+
return route.handler({ body } as RouteHandlerArgs);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const VALID_BODY = {
|
|
50
|
+
window_start: 1000,
|
|
51
|
+
window_end: 2000,
|
|
52
|
+
counts: [
|
|
53
|
+
{
|
|
54
|
+
guard: "edge",
|
|
55
|
+
path: "/v1/messages",
|
|
56
|
+
failure_kind: "missing_authorization",
|
|
57
|
+
count: 5,
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
describe("internal-telemetry-routes: auth-fallback", () => {
|
|
63
|
+
beforeEach(() => {
|
|
64
|
+
collectUsageData = true;
|
|
65
|
+
getDb().delete(authFallbackEvents).run();
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("route is locked to service-token callers (GATEWAY_PRINCIPALS + internal.write)", () => {
|
|
69
|
+
expect(route).toBeDefined();
|
|
70
|
+
expect(route?.endpoint).toBe("internal/telemetry/auth-fallback");
|
|
71
|
+
expect(route?.method).toBe("POST");
|
|
72
|
+
expect(route?.policy?.allowedPrincipalTypes).toEqual(GATEWAY_PRINCIPALS);
|
|
73
|
+
expect(route?.policy?.requiredScopes).toEqual(["internal.write"]);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("valid batch is persisted with snake_case → camelCase mapping", () => {
|
|
77
|
+
const result = call(VALID_BODY);
|
|
78
|
+
expect(result).toEqual({ recorded: 1 });
|
|
79
|
+
|
|
80
|
+
const rows = queryUnreportedAuthFallbackEvents(0, undefined, 100);
|
|
81
|
+
expect(rows.length).toBe(1);
|
|
82
|
+
expect(rows[0]).toMatchObject({
|
|
83
|
+
guard: "edge",
|
|
84
|
+
path: "/v1/messages",
|
|
85
|
+
failureKind: "missing_authorization",
|
|
86
|
+
count: 5,
|
|
87
|
+
windowStart: 1000,
|
|
88
|
+
windowEnd: 2000,
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("returns skipped and persists nothing under the opt-out", () => {
|
|
93
|
+
collectUsageData = false;
|
|
94
|
+
expect(call(VALID_BODY)).toEqual({ skipped: true });
|
|
95
|
+
expect(queryUnreportedAuthFallbackEvents(0, undefined, 100).length).toBe(0);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test("rejects a malformed body without persisting", () => {
|
|
99
|
+
expect(() => call({ window_start: 1000 })).toThrow(RouteError);
|
|
100
|
+
expect(() => call({ ...VALID_BODY, counts: [] })).toThrow(RouteError);
|
|
101
|
+
expect(() =>
|
|
102
|
+
call({
|
|
103
|
+
...VALID_BODY,
|
|
104
|
+
counts: [{ guard: "edge", path: "/x", failure_kind: "y", count: 0 }],
|
|
105
|
+
}),
|
|
106
|
+
).toThrow(RouteError);
|
|
107
|
+
expect(queryUnreportedAuthFallbackEvents(0, undefined, 100).length).toBe(0);
|
|
108
|
+
});
|
|
109
|
+
});
|
|
@@ -96,6 +96,44 @@ describe("handleListMessages metadata.hidden filtering", () => {
|
|
|
96
96
|
expect(llmRows[1].metadata).toContain('"hidden":true');
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
+
test("UI serializer omits system rows but LLM-side getMessages includes them", async () => {
|
|
100
|
+
// GIVEN a conversation with a system row sandwiched between two
|
|
101
|
+
// renderable turns (e.g. a skill-authored context message)
|
|
102
|
+
const conv = createConversation();
|
|
103
|
+
await addMessage(
|
|
104
|
+
conv.id,
|
|
105
|
+
"user",
|
|
106
|
+
JSON.stringify([{ type: "text", text: "first visible" }]),
|
|
107
|
+
);
|
|
108
|
+
await addMessage(
|
|
109
|
+
conv.id,
|
|
110
|
+
"system",
|
|
111
|
+
JSON.stringify([{ type: "text", text: "system scaffolding" }]),
|
|
112
|
+
);
|
|
113
|
+
await addMessage(
|
|
114
|
+
conv.id,
|
|
115
|
+
"assistant",
|
|
116
|
+
JSON.stringify([{ type: "text", text: "second visible" }]),
|
|
117
|
+
);
|
|
118
|
+
|
|
119
|
+
// WHEN the UI history list is serialized
|
|
120
|
+
const response = handleListMessages({
|
|
121
|
+
queryParams: { conversationId: conv.id },
|
|
122
|
+
});
|
|
123
|
+
const body = response as { messages: MessagePayload[] };
|
|
124
|
+
|
|
125
|
+
// THEN only the user/assistant turns are returned, never the system row
|
|
126
|
+
expect(body.messages.map((m) => m.role)).toEqual(["user", "assistant"]);
|
|
127
|
+
expect(
|
|
128
|
+
body.messages.some((m) => plainText(m).includes("system scaffolding")),
|
|
129
|
+
).toBe(false);
|
|
130
|
+
|
|
131
|
+
// AND the LLM-side loader still includes the system row for agent context
|
|
132
|
+
const llmRows = getMessages(conv.id);
|
|
133
|
+
expect(llmRows).toHaveLength(3);
|
|
134
|
+
expect(llmRows[1].role).toBe("system");
|
|
135
|
+
});
|
|
136
|
+
|
|
99
137
|
test("messages without metadata or with hidden=false are returned", async () => {
|
|
100
138
|
const conv = createConversation();
|
|
101
139
|
await addMessage(
|