@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,4 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
4
|
|
|
3
5
|
// This test exercises v1 PKB injection. `config.memory.v2.enabled` (default
|
|
4
6
|
// `true`) makes the PKB injector go silent — force it off here so the v1
|
|
@@ -33,6 +35,10 @@ mock.module("../memory/pkb/pkb-search.js", () => ({
|
|
|
33
35
|
},
|
|
34
36
|
}));
|
|
35
37
|
|
|
38
|
+
import {
|
|
39
|
+
clearConversations,
|
|
40
|
+
setConversation,
|
|
41
|
+
} from "../daemon/conversation-registry.js";
|
|
36
42
|
import type {
|
|
37
43
|
ChannelCapabilities,
|
|
38
44
|
SlackTranscriptInputRow,
|
|
@@ -44,7 +50,6 @@ import {
|
|
|
44
50
|
assembleSlackChronologicalMessages,
|
|
45
51
|
buildSubagentStatusBlock,
|
|
46
52
|
buildUnifiedTurnContextBlock,
|
|
47
|
-
findLastInjectedNowContent,
|
|
48
53
|
getSlackCompactionWatermarkForPrefix,
|
|
49
54
|
injectChannelCapabilityContext,
|
|
50
55
|
injectChannelCommandContext,
|
|
@@ -58,31 +63,106 @@ import {
|
|
|
58
63
|
stripInjectionsForCompaction,
|
|
59
64
|
stripNowScratchpad,
|
|
60
65
|
} from "../daemon/conversation-runtime-assembly.js";
|
|
66
|
+
import type { SurfaceData, SurfaceType } from "../daemon/message-protocol.js";
|
|
61
67
|
import { buildPkbReminder } from "../daemon/pkb-reminder-builder.js";
|
|
62
68
|
import type { MessageRow } from "../memory/conversation-crud.js";
|
|
69
|
+
import { ConversationGraphMemory } from "../memory/graph/conversation-graph-memory.js";
|
|
70
|
+
import { getPkbRoot } from "../memory/pkb/types.js";
|
|
63
71
|
import {
|
|
64
72
|
type SlackMessageMetadata,
|
|
65
73
|
writeSlackMetadata,
|
|
66
74
|
} from "../messaging/providers/slack/message-metadata.js";
|
|
67
75
|
import { parentAlias } from "../messaging/providers/slack/render-transcript.js";
|
|
68
|
-
import { defaultInjectorsPlugin } from "../plugins/defaults/injectors/register.js";
|
|
69
|
-
import {
|
|
70
|
-
registerPlugin,
|
|
71
|
-
resetPluginRegistryForTests,
|
|
72
|
-
} from "../plugins/registry.js";
|
|
73
76
|
import type { Message } from "../providers/types.js";
|
|
74
77
|
import { wrapUntrustedContent } from "../security/untrusted-content.js";
|
|
75
78
|
import type { SubagentState } from "../subagent/types.js";
|
|
79
|
+
import { getWorkspacePromptPath } from "../util/platform.js";
|
|
80
|
+
|
|
81
|
+
// The pkb-reminder injector derives PKB-active state from the workspace itself
|
|
82
|
+
// — `readPkbContext()` returning content behind the personal-memory trust gate
|
|
83
|
+
// — rather than from a threaded flag. Tests that need the reminder to fire seed
|
|
84
|
+
// a default auto-injected PKB file under the workspace pkb root; tests that
|
|
85
|
+
// need it suppressed leave the pkb dir empty.
|
|
86
|
+
function seedPkbContent(): void {
|
|
87
|
+
const root = getPkbRoot();
|
|
88
|
+
mkdirSync(root, { recursive: true });
|
|
89
|
+
writeFileSync(join(root, "INDEX.md"), "workspace knowledge index", "utf-8");
|
|
90
|
+
}
|
|
76
91
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
92
|
+
function clearPkbContent(): void {
|
|
93
|
+
rmSync(getPkbRoot(), { recursive: true, force: true });
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// The now-md injector derives NOW.md state from the workspace itself —
|
|
97
|
+
// `readNowScratchpad()` returning content behind the personal-memory trust gate
|
|
98
|
+
// and the `scratchpadInjection` config toggle — rather than from a threaded
|
|
99
|
+
// option. Seed the file so the injector fires; clear it so suites that assert
|
|
100
|
+
// NOW.md is absent stay unaffected.
|
|
101
|
+
function seedNowScratchpad(content: string): void {
|
|
102
|
+
const nowPath = getWorkspacePromptPath("NOW.md");
|
|
103
|
+
mkdirSync(dirname(nowPath), { recursive: true });
|
|
104
|
+
writeFileSync(nowPath, content, "utf-8");
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function clearNowScratchpad(): void {
|
|
108
|
+
rmSync(getWorkspacePromptPath("NOW.md"), { force: true });
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// The workspace-context injector sources its block off the live `Conversation`
|
|
112
|
+
// looked up by `conversationId`. Register a fake instance carrying both a
|
|
113
|
+
// non-dirty workspace context (non-null content, not dirty) so
|
|
114
|
+
// `resolveWorkspaceTopLevelContext` returns it verbatim without rescanning the
|
|
115
|
+
// filesystem, and an active dynamic-page surface, so `applyRuntimeInjections`
|
|
116
|
+
// resolves the `<workspace>` and `<active_workspace>` blocks from it;
|
|
117
|
+
// `clearConversations()` between tests keeps suites that assert the blocks are
|
|
118
|
+
// absent unaffected.
|
|
119
|
+
function seedActiveSurfaceConversation(
|
|
120
|
+
conversationId: string,
|
|
121
|
+
workspaceText: string,
|
|
122
|
+
surfaceId: string,
|
|
123
|
+
data: SurfaceData,
|
|
124
|
+
channelCapabilities?: ChannelCapabilities,
|
|
125
|
+
commandIntent?: { type: string; payload?: string; languageCode?: string },
|
|
126
|
+
): void {
|
|
127
|
+
setConversation(conversationId, {
|
|
128
|
+
conversationId,
|
|
129
|
+
workingDir: "/sandbox",
|
|
130
|
+
workspaceTopLevelContext: workspaceText,
|
|
131
|
+
workspaceTopLevelDirty: false,
|
|
132
|
+
currentActiveSurfaceId: surfaceId,
|
|
133
|
+
surfaceState: new Map<
|
|
134
|
+
string,
|
|
135
|
+
{ surfaceType: SurfaceType; data: SurfaceData }
|
|
136
|
+
>([[surfaceId, { surfaceType: "dynamic_page", data }]]),
|
|
137
|
+
channelCapabilities: channelCapabilities ?? undefined,
|
|
138
|
+
commandIntent,
|
|
139
|
+
} as never);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function clearWorkspaceContext(): void {
|
|
143
|
+
clearConversations();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// The `<channel_capabilities>` branch and the Slack gates source the channel
|
|
147
|
+
// capabilities off the live `Conversation` looked up by `conversationId`.
|
|
148
|
+
// Register a fake fallback instance carrying the given capabilities (and a
|
|
149
|
+
// non-dirty empty workspace + empty surface so the other live-sourced branches
|
|
150
|
+
// stay inert) so `applyRuntimeInjections` resolves them; `clearConversations()`
|
|
151
|
+
// between tests keeps suites asserting absence unaffected.
|
|
152
|
+
function seedChannelCapabilitiesConversation(
|
|
153
|
+
caps: ChannelCapabilities | null,
|
|
154
|
+
transportHints?: string[],
|
|
155
|
+
): void {
|
|
156
|
+
setConversation("runtime-assembly-fallback", {
|
|
157
|
+
conversationId: "runtime-assembly-fallback",
|
|
158
|
+
workingDir: "/sandbox",
|
|
159
|
+
workspaceTopLevelContext: "",
|
|
160
|
+
workspaceTopLevelDirty: false,
|
|
161
|
+
surfaceState: new Map(),
|
|
162
|
+
channelCapabilities: caps ?? undefined,
|
|
163
|
+
transportHints,
|
|
164
|
+
} as never);
|
|
165
|
+
}
|
|
86
166
|
|
|
87
167
|
// ---------------------------------------------------------------------------
|
|
88
168
|
// resolveChannelCapabilities
|
|
@@ -556,6 +636,8 @@ describe("stripChannelCapabilityContext", () => {
|
|
|
556
636
|
// ---------------------------------------------------------------------------
|
|
557
637
|
|
|
558
638
|
describe("applyRuntimeInjections with channelCapabilities", () => {
|
|
639
|
+
afterEach(clearConversations);
|
|
640
|
+
|
|
559
641
|
const baseMessages: Message[] = [
|
|
560
642
|
{
|
|
561
643
|
role: "user",
|
|
@@ -571,9 +653,8 @@ describe("applyRuntimeInjections with channelCapabilities", () => {
|
|
|
571
653
|
supportsVoiceInput: false,
|
|
572
654
|
};
|
|
573
655
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
});
|
|
656
|
+
seedChannelCapabilitiesConversation(caps);
|
|
657
|
+
const { messages: result } = await applyRuntimeInjections(baseMessages, {});
|
|
577
658
|
|
|
578
659
|
expect(result.length).toBe(1);
|
|
579
660
|
expect(result[0].content.length).toBe(2);
|
|
@@ -584,9 +665,8 @@ describe("applyRuntimeInjections with channelCapabilities", () => {
|
|
|
584
665
|
});
|
|
585
666
|
|
|
586
667
|
test("does not inject when channelCapabilities is null", async () => {
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
});
|
|
668
|
+
seedChannelCapabilitiesConversation(null);
|
|
669
|
+
const { messages: result } = await applyRuntimeInjections(baseMessages, {});
|
|
590
670
|
|
|
591
671
|
expect(result.length).toBe(1);
|
|
592
672
|
expect(result[0].content.length).toBe(1);
|
|
@@ -607,9 +687,8 @@ describe("applyRuntimeInjections with channelCapabilities", () => {
|
|
|
607
687
|
supportsVoiceInput: false,
|
|
608
688
|
};
|
|
609
689
|
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
});
|
|
690
|
+
seedChannelCapabilitiesConversation(caps);
|
|
691
|
+
const { messages: result } = await applyRuntimeInjections(baseMessages, {});
|
|
613
692
|
|
|
614
693
|
expect(result.length).toBe(1);
|
|
615
694
|
// channelCapabilities prepends
|
|
@@ -736,24 +815,52 @@ describe("applyRuntimeInjections — injection mode", () => {
|
|
|
736
815
|
},
|
|
737
816
|
];
|
|
738
817
|
|
|
818
|
+
const channelCapabilities: ChannelCapabilities = {
|
|
819
|
+
channel: "telegram",
|
|
820
|
+
dashboardCapable: false,
|
|
821
|
+
supportsDynamicUi: false,
|
|
822
|
+
supportsVoiceInput: false,
|
|
823
|
+
};
|
|
824
|
+
|
|
739
825
|
const fullOptions = {
|
|
740
|
-
workspaceTopLevelContext: "<workspace>\nRoot: /sandbox\n</workspace>",
|
|
741
|
-
channelCommandContext: { type: "start" } as const,
|
|
742
|
-
activeSurface: { surfaceId: "sf_1", html: "<div>test</div>" },
|
|
743
|
-
channelCapabilities: {
|
|
744
|
-
channel: "telegram",
|
|
745
|
-
dashboardCapable: false,
|
|
746
|
-
supportsDynamicUi: false,
|
|
747
|
-
supportsVoiceInput: false,
|
|
748
|
-
} as ChannelCapabilities,
|
|
749
826
|
unifiedTurnContext:
|
|
750
827
|
"<turn_context>\ncurrent_time: 2026-03-04 (Tuesday) 12:00:00 +00:00 (UTC)\ninterface: telegram\n</turn_context>",
|
|
751
|
-
nowScratchpad: "Current focus: shipping PR 3",
|
|
752
|
-
pkbContext: "essentials content here",
|
|
753
|
-
pkbActive: true,
|
|
754
828
|
isNonInteractive: true,
|
|
829
|
+
// Guardian trust so the personal-memory gate admits the actor regardless
|
|
830
|
+
// of the telegram channel capabilities under test, letting the reminder
|
|
831
|
+
// gate hinge purely on PKB content presence.
|
|
832
|
+
turnContext: {
|
|
833
|
+
requestId: "injection-mode-req",
|
|
834
|
+
conversationId: "injection-mode-conv",
|
|
835
|
+
turnIndex: 0,
|
|
836
|
+
trust: {
|
|
837
|
+
sourceChannel: "vellum" as const,
|
|
838
|
+
trustClass: "guardian" as const,
|
|
839
|
+
},
|
|
840
|
+
},
|
|
755
841
|
};
|
|
756
842
|
|
|
843
|
+
// The reminder fires only when the workspace has PKB content, and the now-md
|
|
844
|
+
// injector only when NOW.md has content; seed both so the full-mode cases
|
|
845
|
+
// exercise the active branch.
|
|
846
|
+
beforeEach(() => {
|
|
847
|
+
seedPkbContent();
|
|
848
|
+
seedNowScratchpad("Current focus: shipping PR 3");
|
|
849
|
+
seedActiveSurfaceConversation(
|
|
850
|
+
"injection-mode-conv",
|
|
851
|
+
"<workspace>\nRoot: /sandbox\n</workspace>",
|
|
852
|
+
"sf_1",
|
|
853
|
+
{ html: "<div>test</div>" },
|
|
854
|
+
channelCapabilities,
|
|
855
|
+
{ type: "start" },
|
|
856
|
+
);
|
|
857
|
+
});
|
|
858
|
+
afterEach(() => {
|
|
859
|
+
clearPkbContent();
|
|
860
|
+
clearNowScratchpad();
|
|
861
|
+
clearWorkspaceContext();
|
|
862
|
+
});
|
|
863
|
+
|
|
757
864
|
test("full mode (default) includes all injections", async () => {
|
|
758
865
|
const { messages: result } = await applyRuntimeInjections(
|
|
759
866
|
baseMessages,
|
|
@@ -860,12 +967,10 @@ describe("applyRuntimeInjections — injection mode", () => {
|
|
|
860
967
|
});
|
|
861
968
|
});
|
|
862
969
|
|
|
863
|
-
// The
|
|
864
|
-
//
|
|
865
|
-
//
|
|
866
|
-
//
|
|
867
|
-
// nowScratchpad` and the injection-mode tests) cover that behaviour
|
|
868
|
-
// end-to-end.
|
|
970
|
+
// The now-md default injector emits the `<NOW.md>` block as an
|
|
971
|
+
// `after-memory-prefix` placement during `applyRuntimeInjections`. The suites
|
|
972
|
+
// below (`applyRuntimeInjections with nowScratchpad` and the injection-mode
|
|
973
|
+
// tests) cover that behaviour end-to-end.
|
|
869
974
|
|
|
870
975
|
// ---------------------------------------------------------------------------
|
|
871
976
|
// stripNowScratchpad
|
|
@@ -1013,7 +1118,7 @@ describe("stripInjectionsForCompaction preserves persistent blocks", () => {
|
|
|
1013
1118
|
).toContain("<turn_context>");
|
|
1014
1119
|
});
|
|
1015
1120
|
|
|
1016
|
-
test("<workspace> blocks
|
|
1121
|
+
test("<workspace> blocks ARE stripped so the injector re-sources them post-compaction", () => {
|
|
1017
1122
|
const messages: Message[] = [
|
|
1018
1123
|
{
|
|
1019
1124
|
role: "user",
|
|
@@ -1029,10 +1134,10 @@ describe("stripInjectionsForCompaction preserves persistent blocks", () => {
|
|
|
1029
1134
|
|
|
1030
1135
|
const result = stripInjectionsForCompaction(messages);
|
|
1031
1136
|
expect(result.length).toBe(1);
|
|
1032
|
-
expect(result[0].content.length).toBe(
|
|
1033
|
-
expect(
|
|
1034
|
-
|
|
1035
|
-
)
|
|
1137
|
+
expect(result[0].content.length).toBe(1);
|
|
1138
|
+
expect((result[0].content[0] as { type: "text"; text: string }).text).toBe(
|
|
1139
|
+
"Hello",
|
|
1140
|
+
);
|
|
1036
1141
|
});
|
|
1037
1142
|
|
|
1038
1143
|
test("legacy <workspace_top_level> blocks ARE stripped for backward compat", () => {
|
|
@@ -1192,10 +1297,14 @@ describe("applyRuntimeInjections with nowScratchpad", () => {
|
|
|
1192
1297
|
},
|
|
1193
1298
|
];
|
|
1194
1299
|
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1300
|
+
// The now-md injector sources NOW.md from the workspace itself rather than
|
|
1301
|
+
// from a threaded option, so seed the file to drive injection and clear it so
|
|
1302
|
+
// the absent-content cases see no block.
|
|
1303
|
+
afterEach(clearNowScratchpad);
|
|
1304
|
+
|
|
1305
|
+
test("injects NOW.md block when the file has content", async () => {
|
|
1306
|
+
seedNowScratchpad("Current focus: fix the bug");
|
|
1307
|
+
const { messages: result } = await applyRuntimeInjections(baseMessages, {});
|
|
1199
1308
|
|
|
1200
1309
|
expect(result.length).toBe(1);
|
|
1201
1310
|
expect(result[0].content.length).toBe(2);
|
|
@@ -1206,9 +1315,8 @@ describe("applyRuntimeInjections with nowScratchpad", () => {
|
|
|
1206
1315
|
});
|
|
1207
1316
|
|
|
1208
1317
|
test("scratchpad appears before user's original text content", async () => {
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
});
|
|
1318
|
+
seedNowScratchpad("scratchpad notes");
|
|
1319
|
+
const { messages: result } = await applyRuntimeInjections(baseMessages, {});
|
|
1212
1320
|
|
|
1213
1321
|
// Scratchpad comes first (before user content)
|
|
1214
1322
|
expect(
|
|
@@ -1220,16 +1328,7 @@ describe("applyRuntimeInjections with nowScratchpad", () => {
|
|
|
1220
1328
|
);
|
|
1221
1329
|
});
|
|
1222
1330
|
|
|
1223
|
-
test("does not inject when
|
|
1224
|
-
const { messages: result } = await applyRuntimeInjections(baseMessages, {
|
|
1225
|
-
nowScratchpad: null,
|
|
1226
|
-
});
|
|
1227
|
-
|
|
1228
|
-
expect(result.length).toBe(1);
|
|
1229
|
-
expect(result[0].content.length).toBe(1);
|
|
1230
|
-
});
|
|
1231
|
-
|
|
1232
|
-
test("does not inject when nowScratchpad is omitted", async () => {
|
|
1331
|
+
test("does not inject when the NOW.md file is absent", async () => {
|
|
1233
1332
|
const { messages: result } = await applyRuntimeInjections(baseMessages, {});
|
|
1234
1333
|
|
|
1235
1334
|
expect(result.length).toBe(1);
|
|
@@ -1237,8 +1336,8 @@ describe("applyRuntimeInjections with nowScratchpad", () => {
|
|
|
1237
1336
|
});
|
|
1238
1337
|
|
|
1239
1338
|
test("skipped in minimal mode", async () => {
|
|
1339
|
+
seedNowScratchpad("Current focus: fix the bug");
|
|
1240
1340
|
const { messages: result } = await applyRuntimeInjections(baseMessages, {
|
|
1241
|
-
nowScratchpad: "Current focus: fix the bug",
|
|
1242
1341
|
mode: "minimal",
|
|
1243
1342
|
});
|
|
1244
1343
|
|
|
@@ -1782,85 +1881,6 @@ describe("applyRuntimeInjections blocks.unifiedTurnContext", () => {
|
|
|
1782
1881
|
});
|
|
1783
1882
|
});
|
|
1784
1883
|
|
|
1785
|
-
// ---------------------------------------------------------------------------
|
|
1786
|
-
// findLastInjectedNowContent
|
|
1787
|
-
// ---------------------------------------------------------------------------
|
|
1788
|
-
|
|
1789
|
-
describe("findLastInjectedNowContent", () => {
|
|
1790
|
-
test("extracts NOW.md content from the last user message", () => {
|
|
1791
|
-
const messages: Message[] = [
|
|
1792
|
-
{
|
|
1793
|
-
role: "user",
|
|
1794
|
-
content: [
|
|
1795
|
-
{
|
|
1796
|
-
type: "text",
|
|
1797
|
-
text: "<NOW.md Always keep this up to date>\nCurrent focus: fix the bug\n</NOW.md>",
|
|
1798
|
-
},
|
|
1799
|
-
{ type: "text", text: "Hello" },
|
|
1800
|
-
],
|
|
1801
|
-
},
|
|
1802
|
-
];
|
|
1803
|
-
|
|
1804
|
-
expect(findLastInjectedNowContent(messages)).toBe(
|
|
1805
|
-
"Current focus: fix the bug",
|
|
1806
|
-
);
|
|
1807
|
-
});
|
|
1808
|
-
|
|
1809
|
-
test("returns null when no NOW.md injection exists", () => {
|
|
1810
|
-
const messages: Message[] = [
|
|
1811
|
-
{
|
|
1812
|
-
role: "user",
|
|
1813
|
-
content: [{ type: "text", text: "Hello" }],
|
|
1814
|
-
},
|
|
1815
|
-
];
|
|
1816
|
-
|
|
1817
|
-
expect(findLastInjectedNowContent(messages)).toBeNull();
|
|
1818
|
-
});
|
|
1819
|
-
|
|
1820
|
-
test("returns the most recent injection when multiple exist", () => {
|
|
1821
|
-
const messages: Message[] = [
|
|
1822
|
-
{
|
|
1823
|
-
role: "user",
|
|
1824
|
-
content: [
|
|
1825
|
-
{
|
|
1826
|
-
type: "text",
|
|
1827
|
-
text: "<NOW.md Always keep this up to date>\nOld focus\n</NOW.md>",
|
|
1828
|
-
},
|
|
1829
|
-
],
|
|
1830
|
-
},
|
|
1831
|
-
{ role: "assistant", content: [{ type: "text", text: "OK" }] },
|
|
1832
|
-
{
|
|
1833
|
-
role: "user",
|
|
1834
|
-
content: [
|
|
1835
|
-
{
|
|
1836
|
-
type: "text",
|
|
1837
|
-
text: "<NOW.md Always keep this up to date>\nNew focus\n</NOW.md>",
|
|
1838
|
-
},
|
|
1839
|
-
],
|
|
1840
|
-
},
|
|
1841
|
-
];
|
|
1842
|
-
|
|
1843
|
-
expect(findLastInjectedNowContent(messages)).toBe("New focus");
|
|
1844
|
-
});
|
|
1845
|
-
|
|
1846
|
-
test("skips assistant messages", () => {
|
|
1847
|
-
const messages: Message[] = [
|
|
1848
|
-
{
|
|
1849
|
-
role: "user",
|
|
1850
|
-
content: [
|
|
1851
|
-
{
|
|
1852
|
-
type: "text",
|
|
1853
|
-
text: "<NOW.md Always keep this up to date>\nUser focus\n</NOW.md>",
|
|
1854
|
-
},
|
|
1855
|
-
],
|
|
1856
|
-
},
|
|
1857
|
-
{ role: "assistant", content: [{ type: "text", text: "response" }] },
|
|
1858
|
-
];
|
|
1859
|
-
|
|
1860
|
-
expect(findLastInjectedNowContent(messages)).toBe("User focus");
|
|
1861
|
-
});
|
|
1862
|
-
});
|
|
1863
|
-
|
|
1864
1884
|
// ---------------------------------------------------------------------------
|
|
1865
1885
|
// Subagent status injection
|
|
1866
1886
|
// ---------------------------------------------------------------------------
|
|
@@ -2032,21 +2052,33 @@ describe("applyRuntimeInjections — PKB relevance hints", () => {
|
|
|
2032
2052
|
|
|
2033
2053
|
const FLAT_REMINDER = buildPkbReminder([]);
|
|
2034
2054
|
|
|
2035
|
-
//
|
|
2036
|
-
//
|
|
2037
|
-
//
|
|
2038
|
-
const
|
|
2039
|
-
|
|
2055
|
+
// The pkb-reminder injector sources the PKB root itself via `getPkbRoot()`,
|
|
2056
|
+
// so the in-context file paths these tests build must resolve against the
|
|
2057
|
+
// same per-test workspace the injector sees.
|
|
2058
|
+
const pkbRoot = getPkbRoot();
|
|
2059
|
+
|
|
2060
|
+
// The pkb-reminder injector reads the dense/sparse PKB query pair off the
|
|
2061
|
+
// conversation's live graph handle (the memory-retrieval hook records it
|
|
2062
|
+
// there during retrieval), not from the injection options. Register a handle
|
|
2063
|
+
// for the fallback conversation id `applyRuntimeInjections` synthesizes and
|
|
2064
|
+
// seed it with a query vector so the hint-search branch runs.
|
|
2065
|
+
let graphHandle: ConversationGraphMemory;
|
|
2066
|
+
beforeEach(() => {
|
|
2067
|
+
graphHandle = new ConversationGraphMemory("runtime-assembly-fallback");
|
|
2068
|
+
graphHandle.recordPkbQueryVectors([0.1, 0.2, 0.3], undefined);
|
|
2069
|
+
});
|
|
2070
|
+
afterEach(() => {
|
|
2071
|
+
graphHandle.dispose();
|
|
2072
|
+
});
|
|
2073
|
+
|
|
2074
|
+
// PKB content makes `readPkbContext()` non-null so the (vellum/guardian-
|
|
2075
|
+
// equivalent) fallback trust gate admits the reminder; cleared after each
|
|
2076
|
+
// test to avoid leaking into suites that assert the reminder is absent.
|
|
2077
|
+
beforeEach(seedPkbContent);
|
|
2078
|
+
afterEach(clearPkbContent);
|
|
2040
2079
|
|
|
2041
2080
|
function makePkbOptions(overrides: Record<string, unknown> = {}) {
|
|
2042
2081
|
return {
|
|
2043
|
-
pkbActive: true,
|
|
2044
|
-
pkbQueryVector: [0.1, 0.2, 0.3],
|
|
2045
|
-
pkbScopeId: "scope-1",
|
|
2046
|
-
pkbConversation: { messages: baseMessages },
|
|
2047
|
-
pkbRoot,
|
|
2048
|
-
pkbWorkingDir,
|
|
2049
|
-
pkbAutoInjectList: [],
|
|
2050
2082
|
...overrides,
|
|
2051
2083
|
};
|
|
2052
2084
|
}
|
|
@@ -2082,10 +2114,11 @@ describe("applyRuntimeInjections — PKB relevance hints", () => {
|
|
|
2082
2114
|
test("default auto-injected files (from PKB_DEFAULT_FILES) are filtered out of hints", async () => {
|
|
2083
2115
|
// Regression test: when `_autoinject.md` is missing, `readPkbContext`
|
|
2084
2116
|
// falls back to PKB_DEFAULT_FILES — so those files ARE in the prompt.
|
|
2085
|
-
// The
|
|
2086
|
-
//
|
|
2087
|
-
//
|
|
2088
|
-
//
|
|
2117
|
+
// The injector sources the same fallback via `getPkbAutoInjectList`, so
|
|
2118
|
+
// it must know about them too, otherwise the reminder would redundantly
|
|
2119
|
+
// recommend e.g. `essentials.md` even though its contents are already
|
|
2120
|
+
// injected. The per-test workspace has no `_autoinject.md`, so the
|
|
2121
|
+
// injector resolves PKB_DEFAULT_FILES here.
|
|
2089
2122
|
pkbSearchResults = [
|
|
2090
2123
|
{ path: "essentials.md", denseScore: 0.95 },
|
|
2091
2124
|
{ path: "topics/alpha.md", denseScore: 0.9 },
|
|
@@ -2094,16 +2127,7 @@ describe("applyRuntimeInjections — PKB relevance hints", () => {
|
|
|
2094
2127
|
|
|
2095
2128
|
const { messages: result } = await applyRuntimeInjections(
|
|
2096
2129
|
baseMessages,
|
|
2097
|
-
makePkbOptions(
|
|
2098
|
-
// Simulate the fallback the agent-loop now threads through:
|
|
2099
|
-
// `_autoinject.md` is missing, so defaults are injected.
|
|
2100
|
-
pkbAutoInjectList: [
|
|
2101
|
-
"INDEX.md",
|
|
2102
|
-
"essentials.md",
|
|
2103
|
-
"threads.md",
|
|
2104
|
-
"buffer.md",
|
|
2105
|
-
],
|
|
2106
|
-
}),
|
|
2130
|
+
makePkbOptions(),
|
|
2107
2131
|
);
|
|
2108
2132
|
const texts = extractTexts(result);
|
|
2109
2133
|
const reminder = texts.find((t) => t.startsWith("<system_reminder>"));
|
|
@@ -2143,27 +2167,26 @@ describe("applyRuntimeInjections — PKB relevance hints", () => {
|
|
|
2143
2167
|
];
|
|
2144
2168
|
pkbSearchThrows = null;
|
|
2145
2169
|
|
|
2146
|
-
//
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
};
|
|
2170
|
+
// Working messages where topics/beta.md was already read via file_read on
|
|
2171
|
+
// an earlier turn, followed by the current user prompt as the tail.
|
|
2172
|
+
const conversationWithRead: Message[] = [
|
|
2173
|
+
{
|
|
2174
|
+
role: "assistant",
|
|
2175
|
+
content: [
|
|
2176
|
+
{
|
|
2177
|
+
type: "tool_use",
|
|
2178
|
+
id: "tu_1",
|
|
2179
|
+
name: "file_read",
|
|
2180
|
+
input: { path: `${pkbRoot}/topics/beta.md` },
|
|
2181
|
+
},
|
|
2182
|
+
],
|
|
2183
|
+
},
|
|
2184
|
+
...baseMessages,
|
|
2185
|
+
];
|
|
2163
2186
|
|
|
2164
2187
|
const { messages: result } = await applyRuntimeInjections(
|
|
2165
|
-
|
|
2166
|
-
makePkbOptions(
|
|
2188
|
+
conversationWithRead,
|
|
2189
|
+
makePkbOptions(),
|
|
2167
2190
|
);
|
|
2168
2191
|
const texts = extractTexts(result);
|
|
2169
2192
|
const reminder = texts.find((t) => t.startsWith("<system_reminder>"));
|
|
@@ -2201,10 +2224,13 @@ describe("applyRuntimeInjections — PKB relevance hints", () => {
|
|
|
2201
2224
|
|
|
2202
2225
|
test("missing query vector → flat fallback, search is not attempted", async () => {
|
|
2203
2226
|
pkbSearchThrows = new Error("should not be called");
|
|
2227
|
+
// No dense vector was recorded on the graph handle this turn, so the
|
|
2228
|
+
// injector must skip the hint search entirely.
|
|
2229
|
+
graphHandle.recordPkbQueryVectors(undefined, undefined);
|
|
2204
2230
|
|
|
2205
2231
|
const { messages: result } = await applyRuntimeInjections(
|
|
2206
2232
|
baseMessages,
|
|
2207
|
-
makePkbOptions(
|
|
2233
|
+
makePkbOptions(),
|
|
2208
2234
|
);
|
|
2209
2235
|
const texts = extractTexts(result);
|
|
2210
2236
|
const reminder = texts.find((t) => t.startsWith("<system_reminder>"));
|
|
@@ -2318,37 +2344,28 @@ describe("applyRuntimeInjections — PKB relevance hints", () => {
|
|
|
2318
2344
|
];
|
|
2319
2345
|
pkbSearchThrows = null;
|
|
2320
2346
|
|
|
2321
|
-
// Pre-compaction
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
|
|
2326
|
-
|
|
2327
|
-
|
|
2328
|
-
|
|
2329
|
-
|
|
2330
|
-
|
|
2331
|
-
|
|
2332
|
-
|
|
2333
|
-
|
|
2334
|
-
|
|
2335
|
-
|
|
2336
|
-
|
|
2337
|
-
};
|
|
2347
|
+
// Pre-compaction working messages: beta was already read on an earlier
|
|
2348
|
+
// turn, followed by the current user prompt as the tail.
|
|
2349
|
+
const preCompactionMessages: Message[] = [
|
|
2350
|
+
{
|
|
2351
|
+
role: "assistant",
|
|
2352
|
+
content: [
|
|
2353
|
+
{
|
|
2354
|
+
type: "tool_use",
|
|
2355
|
+
id: "tu_pre",
|
|
2356
|
+
name: "file_read",
|
|
2357
|
+
input: { path: `${pkbRoot}/topics/beta.md` },
|
|
2358
|
+
},
|
|
2359
|
+
],
|
|
2360
|
+
},
|
|
2361
|
+
...baseMessages,
|
|
2362
|
+
];
|
|
2338
2363
|
|
|
2339
2364
|
// 1. Initial injection sees the pre-compaction state — beta should be
|
|
2340
2365
|
// filtered out.
|
|
2341
2366
|
const { messages: initialResult } = await applyRuntimeInjections(
|
|
2342
|
-
|
|
2343
|
-
{
|
|
2344
|
-
pkbActive: true,
|
|
2345
|
-
pkbQueryVector: [0.1, 0.2],
|
|
2346
|
-
pkbScopeId: "scope-1",
|
|
2347
|
-
pkbConversation: preCompactionConversation,
|
|
2348
|
-
pkbRoot,
|
|
2349
|
-
pkbWorkingDir,
|
|
2350
|
-
pkbAutoInjectList: [],
|
|
2351
|
-
},
|
|
2367
|
+
preCompactionMessages,
|
|
2368
|
+
{},
|
|
2352
2369
|
);
|
|
2353
2370
|
// Unwrap the injected reminder from the last user message.
|
|
2354
2371
|
const initialTexts = extractTexts(initialResult);
|
|
@@ -2360,41 +2377,29 @@ describe("applyRuntimeInjections — PKB relevance hints", () => {
|
|
|
2360
2377
|
expect(initialReminder).toBeDefined();
|
|
2361
2378
|
expect(initialReminder).not.toContain("- topics/beta.md");
|
|
2362
2379
|
|
|
2363
|
-
// 2.
|
|
2364
|
-
//
|
|
2365
|
-
//
|
|
2366
|
-
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2370
|
-
|
|
2371
|
-
|
|
2372
|
-
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
},
|
|
2381
|
-
],
|
|
2382
|
-
};
|
|
2383
|
-
const postCompactionMessages = stripInjectionsForCompaction(initialResult);
|
|
2380
|
+
// 2. After compaction the working messages reflect the post-compaction
|
|
2381
|
+
// state: beta's tool_use was serialized into summary text and dropped,
|
|
2382
|
+
// so the only live file_read is the newly-read gamma.
|
|
2383
|
+
const postCompactionMessages: Message[] = [
|
|
2384
|
+
{
|
|
2385
|
+
role: "assistant",
|
|
2386
|
+
content: [
|
|
2387
|
+
{
|
|
2388
|
+
type: "tool_use",
|
|
2389
|
+
id: "tu_post",
|
|
2390
|
+
name: "file_read",
|
|
2391
|
+
input: { path: `${pkbRoot}/topics/gamma.md` },
|
|
2392
|
+
},
|
|
2393
|
+
],
|
|
2394
|
+
},
|
|
2395
|
+
...baseMessages,
|
|
2396
|
+
];
|
|
2384
2397
|
|
|
2385
|
-
// 3. Re-inject
|
|
2398
|
+
// 3. Re-inject over the post-compaction messages — gamma (now in context)
|
|
2386
2399
|
// should be filtered, and beta (no longer "in context") should appear.
|
|
2387
2400
|
const { messages: rebuiltResult } = await applyRuntimeInjections(
|
|
2388
2401
|
postCompactionMessages,
|
|
2389
|
-
{
|
|
2390
|
-
pkbActive: true,
|
|
2391
|
-
pkbQueryVector: [0.1, 0.2],
|
|
2392
|
-
pkbScopeId: "scope-1",
|
|
2393
|
-
pkbConversation: postCompactionConversation,
|
|
2394
|
-
pkbRoot,
|
|
2395
|
-
pkbWorkingDir,
|
|
2396
|
-
pkbAutoInjectList: [],
|
|
2397
|
-
},
|
|
2402
|
+
{},
|
|
2398
2403
|
);
|
|
2399
2404
|
const rebuiltTexts = extractTexts(rebuiltResult);
|
|
2400
2405
|
const rebuiltReminder = rebuiltTexts.find(
|
|
@@ -2414,6 +2419,8 @@ describe("applyRuntimeInjections — PKB relevance hints", () => {
|
|
|
2414
2419
|
// ---------------------------------------------------------------------------
|
|
2415
2420
|
|
|
2416
2421
|
describe("Slack channel chronological rendering — multi-thread", () => {
|
|
2422
|
+
afterEach(clearConversations);
|
|
2423
|
+
|
|
2417
2424
|
// Slack ts values are seconds-since-epoch with microsecond precision.
|
|
2418
2425
|
// Pick a few stable anchors so thread aliases (sha-derived) stay
|
|
2419
2426
|
// predictable across the scenarios.
|
|
@@ -2499,8 +2506,8 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
2499
2506
|
role: "user",
|
|
2500
2507
|
content: [{ type: "text", text: "current turn" }],
|
|
2501
2508
|
};
|
|
2509
|
+
seedChannelCapabilitiesConversation(slackChannelCaps);
|
|
2502
2510
|
const { messages } = await applyRuntimeInjections([lastUserMessage], {
|
|
2503
|
-
channelCapabilities: slackChannelCaps,
|
|
2504
2511
|
slackChronologicalMessages,
|
|
2505
2512
|
});
|
|
2506
2513
|
return messages;
|
|
@@ -2788,15 +2795,15 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
2788
2795
|
role: "user",
|
|
2789
2796
|
content: [{ type: "text", text: "vellum question" }],
|
|
2790
2797
|
};
|
|
2798
|
+
seedChannelCapabilitiesConversation({
|
|
2799
|
+
channel: "vellum",
|
|
2800
|
+
dashboardCapable: true,
|
|
2801
|
+
supportsDynamicUi: true,
|
|
2802
|
+
supportsVoiceInput: true,
|
|
2803
|
+
});
|
|
2791
2804
|
const { messages: result } = await applyRuntimeInjections(
|
|
2792
2805
|
[lastUserMessage],
|
|
2793
2806
|
{
|
|
2794
|
-
channelCapabilities: {
|
|
2795
|
-
channel: "vellum",
|
|
2796
|
-
dashboardCapable: true,
|
|
2797
|
-
supportsDynamicUi: true,
|
|
2798
|
-
supportsVoiceInput: true,
|
|
2799
|
-
},
|
|
2800
2807
|
// Even if we accidentally pass a chronological transcript, the
|
|
2801
2808
|
// branch must be a no-op for non-slack channels.
|
|
2802
2809
|
slackChronologicalMessages: [
|
|
@@ -2826,16 +2833,16 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
2826
2833
|
role: "user",
|
|
2827
2834
|
content: [{ type: "text", text: "DM question" }],
|
|
2828
2835
|
};
|
|
2836
|
+
seedChannelCapabilitiesConversation({
|
|
2837
|
+
channel: "slack",
|
|
2838
|
+
dashboardCapable: false,
|
|
2839
|
+
supportsDynamicUi: false,
|
|
2840
|
+
supportsVoiceInput: false,
|
|
2841
|
+
chatType: "im",
|
|
2842
|
+
});
|
|
2829
2843
|
const { messages: result } = await applyRuntimeInjections(
|
|
2830
2844
|
[lastUserMessage],
|
|
2831
2845
|
{
|
|
2832
|
-
channelCapabilities: {
|
|
2833
|
-
channel: "slack",
|
|
2834
|
-
dashboardCapable: false,
|
|
2835
|
-
supportsDynamicUi: false,
|
|
2836
|
-
supportsVoiceInput: false,
|
|
2837
|
-
chatType: "im",
|
|
2838
|
-
},
|
|
2839
2846
|
slackChronologicalMessages: [
|
|
2840
2847
|
{
|
|
2841
2848
|
role: "user",
|
|
@@ -2894,10 +2901,10 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
2894
2901
|
],
|
|
2895
2902
|
},
|
|
2896
2903
|
];
|
|
2904
|
+
seedChannelCapabilitiesConversation(slackCaps);
|
|
2897
2905
|
const { messages: result } = await applyRuntimeInjections(
|
|
2898
2906
|
runMessagesWithMemory,
|
|
2899
2907
|
{
|
|
2900
|
-
channelCapabilities: slackCaps,
|
|
2901
2908
|
slackChronologicalMessages: [
|
|
2902
2909
|
{
|
|
2903
2910
|
role: "user",
|
|
@@ -2958,10 +2965,10 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
2958
2965
|
],
|
|
2959
2966
|
},
|
|
2960
2967
|
];
|
|
2968
|
+
seedChannelCapabilitiesConversation(slackCaps);
|
|
2961
2969
|
const { messages: result } = await applyRuntimeInjections(
|
|
2962
2970
|
runMessagesWithMemory,
|
|
2963
2971
|
{
|
|
2964
|
-
channelCapabilities: slackCaps,
|
|
2965
2972
|
slackChronologicalMessages: [
|
|
2966
2973
|
{
|
|
2967
2974
|
role: "user",
|
|
@@ -2997,10 +3004,10 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
2997
3004
|
supportsVoiceInput: false,
|
|
2998
3005
|
chatType: "im",
|
|
2999
3006
|
};
|
|
3007
|
+
seedChannelCapabilitiesConversation(slackCaps);
|
|
3000
3008
|
const { messages: result } = await applyRuntimeInjections(
|
|
3001
3009
|
[{ role: "user", content: [{ type: "text", text: "inbound" }] }],
|
|
3002
3010
|
{
|
|
3003
|
-
channelCapabilities: slackCaps,
|
|
3004
3011
|
slackChronologicalMessages: [
|
|
3005
3012
|
{
|
|
3006
3013
|
role: "user",
|
|
@@ -3041,12 +3048,13 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
3041
3048
|
{ loader: () => rows, trustClass: "guardian" },
|
|
3042
3049
|
);
|
|
3043
3050
|
|
|
3051
|
+
seedChannelCapabilitiesConversation(slackChannelCaps, [
|
|
3052
|
+
"thread context: ...",
|
|
3053
|
+
]);
|
|
3044
3054
|
const { messages: result } = await applyRuntimeInjections(
|
|
3045
3055
|
[{ role: "user", content: [{ type: "text", text: "current turn" }] }],
|
|
3046
3056
|
{
|
|
3047
|
-
channelCapabilities: slackChannelCaps,
|
|
3048
3057
|
slackChronologicalMessages,
|
|
3049
|
-
transportHints: ["thread context: ..."],
|
|
3050
3058
|
},
|
|
3051
3059
|
);
|
|
3052
3060
|
|
|
@@ -3071,12 +3079,10 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
3071
3079
|
chatType: "im",
|
|
3072
3080
|
};
|
|
3073
3081
|
|
|
3082
|
+
seedChannelCapabilitiesConversation(slackDmCaps, ["dm context: ..."]);
|
|
3074
3083
|
const { messages: result } = await applyRuntimeInjections(
|
|
3075
3084
|
[{ role: "user", content: [{ type: "text", text: "hi DM" }] }],
|
|
3076
|
-
{
|
|
3077
|
-
channelCapabilities: slackDmCaps,
|
|
3078
|
-
transportHints: ["dm context: ..."],
|
|
3079
|
-
},
|
|
3085
|
+
{},
|
|
3080
3086
|
);
|
|
3081
3087
|
|
|
3082
3088
|
const allText = result
|
|
@@ -3090,18 +3096,19 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
3090
3096
|
|
|
3091
3097
|
// ── transport_hints kept for non-slack channels ───────────────────────
|
|
3092
3098
|
test("non-slack conversations still receive <transport_hints>", async () => {
|
|
3093
|
-
|
|
3094
|
-
[{ role: "user", content: [{ type: "text", text: "hi" }] }],
|
|
3099
|
+
seedChannelCapabilitiesConversation(
|
|
3095
3100
|
{
|
|
3096
|
-
|
|
3097
|
-
|
|
3098
|
-
|
|
3099
|
-
|
|
3100
|
-
|
|
3101
|
-
chatType: "private",
|
|
3102
|
-
},
|
|
3103
|
-
transportHints: ["please answer concisely"],
|
|
3101
|
+
channel: "telegram",
|
|
3102
|
+
dashboardCapable: false,
|
|
3103
|
+
supportsDynamicUi: false,
|
|
3104
|
+
supportsVoiceInput: false,
|
|
3105
|
+
chatType: "private",
|
|
3104
3106
|
},
|
|
3107
|
+
["please answer concisely"],
|
|
3108
|
+
);
|
|
3109
|
+
const { messages: result } = await applyRuntimeInjections(
|
|
3110
|
+
[{ role: "user", content: [{ type: "text", text: "hi" }] }],
|
|
3111
|
+
{},
|
|
3105
3112
|
);
|
|
3106
3113
|
const allText = result
|
|
3107
3114
|
.flatMap((m) => m.content)
|
|
@@ -3426,8 +3433,8 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
3426
3433
|
role: "user",
|
|
3427
3434
|
content: [{ type: "text", text: "current turn" }],
|
|
3428
3435
|
};
|
|
3436
|
+
seedChannelCapabilitiesConversation(slackChannelCaps);
|
|
3429
3437
|
const { messages } = await applyRuntimeInjections([lastUserMessage], {
|
|
3430
|
-
channelCapabilities: slackChannelCaps,
|
|
3431
3438
|
slackChronologicalMessages,
|
|
3432
3439
|
slackActiveThreadFocusBlock: focusBlock,
|
|
3433
3440
|
});
|
|
@@ -3696,8 +3703,8 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
3696
3703
|
chatType: "channel",
|
|
3697
3704
|
};
|
|
3698
3705
|
const newFocus = "<active_thread>\nnewly built\n</active_thread>";
|
|
3706
|
+
seedChannelCapabilitiesConversation(slackChannelCaps);
|
|
3699
3707
|
const { messages: reInjected } = await applyRuntimeInjections(stripped, {
|
|
3700
|
-
channelCapabilities: slackChannelCaps,
|
|
3701
3708
|
slackActiveThreadFocusBlock: newFocus,
|
|
3702
3709
|
});
|
|
3703
3710
|
const reInjectedTexts = reInjected
|
|
@@ -3717,15 +3724,15 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
3717
3724
|
// Defensive: the focus injection is gated on `slackChannel` (i.e.
|
|
3718
3725
|
// `isSlackChannelConversation`). Even if a caller mistakenly forwards
|
|
3719
3726
|
// a focus block on a non-Slack channel, it must NOT be appended.
|
|
3727
|
+
seedChannelCapabilitiesConversation({
|
|
3728
|
+
channel: "vellum",
|
|
3729
|
+
dashboardCapable: true,
|
|
3730
|
+
supportsDynamicUi: true,
|
|
3731
|
+
supportsVoiceInput: true,
|
|
3732
|
+
});
|
|
3720
3733
|
const { messages: result } = await applyRuntimeInjections(
|
|
3721
3734
|
[{ role: "user", content: [{ type: "text", text: "vellum question" }] }],
|
|
3722
3735
|
{
|
|
3723
|
-
channelCapabilities: {
|
|
3724
|
-
channel: "vellum",
|
|
3725
|
-
dashboardCapable: true,
|
|
3726
|
-
supportsDynamicUi: true,
|
|
3727
|
-
supportsVoiceInput: true,
|
|
3728
|
-
},
|
|
3729
3736
|
slackActiveThreadFocusBlock: "<active_thread>\nbogus\n</active_thread>",
|
|
3730
3737
|
},
|
|
3731
3738
|
);
|
|
@@ -3742,16 +3749,16 @@ describe("Slack channel chronological rendering — multi-thread", () => {
|
|
|
3742
3749
|
// Same as above but for Slack DMs (chatType === "im"). The focus
|
|
3743
3750
|
// injection is keyed on `isSlackChannelConversation` which excludes
|
|
3744
3751
|
// DMs, so the block must not appear.
|
|
3752
|
+
seedChannelCapabilitiesConversation({
|
|
3753
|
+
channel: "slack",
|
|
3754
|
+
dashboardCapable: false,
|
|
3755
|
+
supportsDynamicUi: false,
|
|
3756
|
+
supportsVoiceInput: false,
|
|
3757
|
+
chatType: "im",
|
|
3758
|
+
});
|
|
3745
3759
|
const { messages: result } = await applyRuntimeInjections(
|
|
3746
3760
|
[{ role: "user", content: [{ type: "text", text: "DM question" }] }],
|
|
3747
3761
|
{
|
|
3748
|
-
channelCapabilities: {
|
|
3749
|
-
channel: "slack",
|
|
3750
|
-
dashboardCapable: false,
|
|
3751
|
-
supportsDynamicUi: false,
|
|
3752
|
-
supportsVoiceInput: false,
|
|
3753
|
-
chatType: "im",
|
|
3754
|
-
},
|
|
3755
3762
|
slackActiveThreadFocusBlock: "<active_thread>\nbogus\n</active_thread>",
|
|
3756
3763
|
},
|
|
3757
3764
|
);
|
|
@@ -5100,11 +5107,15 @@ describe("applyRuntimeInjections blocks.pkbSystemReminder", () => {
|
|
|
5100
5107
|
},
|
|
5101
5108
|
];
|
|
5102
5109
|
|
|
5110
|
+
// The reminder gate hinges on PKB content presence; clear it after each test
|
|
5111
|
+
// so the "PKB inactive" case starts from an empty workspace pkb dir.
|
|
5112
|
+
afterEach(clearPkbContent);
|
|
5113
|
+
|
|
5103
5114
|
test("captures exact reminder bytes when full mode and PKB active", async () => {
|
|
5115
|
+
seedPkbContent();
|
|
5104
5116
|
pkbSearchResults = [];
|
|
5105
5117
|
pkbSearchThrows = null;
|
|
5106
5118
|
const { blocks } = await applyRuntimeInjections(baseMessages, {
|
|
5107
|
-
pkbActive: true,
|
|
5108
5119
|
mode: "full",
|
|
5109
5120
|
});
|
|
5110
5121
|
|
|
@@ -5113,8 +5124,8 @@ describe("applyRuntimeInjections blocks.pkbSystemReminder", () => {
|
|
|
5113
5124
|
});
|
|
5114
5125
|
|
|
5115
5126
|
test("not captured in minimal mode", async () => {
|
|
5127
|
+
seedPkbContent();
|
|
5116
5128
|
const { blocks } = await applyRuntimeInjections(baseMessages, {
|
|
5117
|
-
pkbActive: true,
|
|
5118
5129
|
mode: "minimal",
|
|
5119
5130
|
});
|
|
5120
5131
|
|
|
@@ -5123,7 +5134,6 @@ describe("applyRuntimeInjections blocks.pkbSystemReminder", () => {
|
|
|
5123
5134
|
|
|
5124
5135
|
test("not captured when PKB inactive", async () => {
|
|
5125
5136
|
const { blocks } = await applyRuntimeInjections(baseMessages, {
|
|
5126
|
-
pkbActive: false,
|
|
5127
5137
|
mode: "full",
|
|
5128
5138
|
});
|
|
5129
5139
|
|