@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
|
@@ -24,6 +24,7 @@ mock.module("../runtime/background-job-runner.js", () => ({
|
|
|
24
24
|
prompt: string;
|
|
25
25
|
groupId?: string;
|
|
26
26
|
trustContext: { sourceChannel: string; trustClass: string };
|
|
27
|
+
onConversationCreated?: (conversationId: string) => void;
|
|
27
28
|
}) => {
|
|
28
29
|
const { createConversation } =
|
|
29
30
|
await import("../memory/conversation-crud.js");
|
|
@@ -33,6 +34,7 @@ mock.module("../runtime/background-job-runner.js", () => ({
|
|
|
33
34
|
source: "schedule",
|
|
34
35
|
...(opts.groupId ? { groupId: opts.groupId } : {}),
|
|
35
36
|
});
|
|
37
|
+
opts.onConversationCreated?.(conv.id);
|
|
36
38
|
onRunBackgroundJobCall?.({
|
|
37
39
|
conversationId: conv.id,
|
|
38
40
|
prompt: opts.prompt,
|
|
@@ -60,12 +62,17 @@ mock.module("../notifications/emit-signal.js", () => ({
|
|
|
60
62
|
|
|
61
63
|
import { getDb } from "../memory/db-connection.js";
|
|
62
64
|
import { initializeDb } from "../memory/db-init.js";
|
|
65
|
+
import { recordUsageEvent } from "../memory/llm-usage-store.js";
|
|
63
66
|
import {
|
|
64
67
|
createSchedule,
|
|
65
68
|
getSchedule,
|
|
66
69
|
getScheduleRuns,
|
|
67
70
|
} from "../schedule/schedule-store.js";
|
|
68
|
-
import {
|
|
71
|
+
import { getScheduleUsageSummaries } from "../schedule/schedule-usage-store.js";
|
|
72
|
+
import {
|
|
73
|
+
runScheduleDueWorkOnce,
|
|
74
|
+
startScheduler,
|
|
75
|
+
} from "../schedule/scheduler.js";
|
|
69
76
|
import { scheduleTask } from "../tasks/task-scheduler.js";
|
|
70
77
|
import { createTask } from "../tasks/task-store.js";
|
|
71
78
|
|
|
@@ -160,6 +167,7 @@ describe("scheduler run_task detection", () => {
|
|
|
160
167
|
db.run("DELETE FROM cron_jobs");
|
|
161
168
|
db.run("DELETE FROM task_runs");
|
|
162
169
|
db.run("DELETE FROM tasks");
|
|
170
|
+
db.run("DELETE FROM llm_usage_events");
|
|
163
171
|
db.run("DELETE FROM messages");
|
|
164
172
|
db.run("DELETE FROM conversations");
|
|
165
173
|
onRunBackgroundJobCall = null;
|
|
@@ -298,4 +306,157 @@ describe("scheduler run_task detection", () => {
|
|
|
298
306
|
/^activity-failed:task:nonexistent-task-id:\d{4}-\d{2}-\d{2}$/,
|
|
299
307
|
);
|
|
300
308
|
});
|
|
309
|
+
|
|
310
|
+
test("opens a task-backed schedule run before task processing and backfills the real conversation id", async () => {
|
|
311
|
+
const task = createTask({
|
|
312
|
+
title: "Usage Attribution Task",
|
|
313
|
+
template: "Spend scheduled tokens",
|
|
314
|
+
});
|
|
315
|
+
const schedule = scheduleTask({
|
|
316
|
+
taskId: task.id,
|
|
317
|
+
name: "Usage Attribution Schedule",
|
|
318
|
+
cronExpression: "* * * * *",
|
|
319
|
+
});
|
|
320
|
+
forceScheduleDue(schedule.id);
|
|
321
|
+
|
|
322
|
+
const from = Date.now() - 1000;
|
|
323
|
+
let processingConversationId: string | null = null;
|
|
324
|
+
let usageEventCreatedAt: number | null = null;
|
|
325
|
+
let runsDuringProcessing: ReturnType<typeof getScheduleRuns> = [];
|
|
326
|
+
|
|
327
|
+
const result = await runScheduleDueWorkOnce(
|
|
328
|
+
async (conversationId) => {
|
|
329
|
+
processingConversationId = conversationId;
|
|
330
|
+
runsDuringProcessing = getScheduleRuns(schedule.id);
|
|
331
|
+
const event = recordUsageEvent(
|
|
332
|
+
{
|
|
333
|
+
conversationId,
|
|
334
|
+
runId: null,
|
|
335
|
+
requestId: "req-scheduled-task-usage",
|
|
336
|
+
actor: "main_agent",
|
|
337
|
+
callSite: "mainAgent",
|
|
338
|
+
inferenceProfile: "balanced",
|
|
339
|
+
provider: "anthropic",
|
|
340
|
+
model: "claude-sonnet-4-20250514",
|
|
341
|
+
inputTokens: 100,
|
|
342
|
+
outputTokens: 50,
|
|
343
|
+
cacheCreationInputTokens: 0,
|
|
344
|
+
cacheReadInputTokens: 0,
|
|
345
|
+
rawUsage: null,
|
|
346
|
+
},
|
|
347
|
+
{ estimatedCostUsd: 0.25, pricingStatus: "priced" },
|
|
348
|
+
);
|
|
349
|
+
usageEventCreatedAt = event.createdAt;
|
|
350
|
+
},
|
|
351
|
+
() => {},
|
|
352
|
+
);
|
|
353
|
+
const to = Date.now() + 1000;
|
|
354
|
+
|
|
355
|
+
expect(result.completed).toBe(1);
|
|
356
|
+
expect(result.failed).toBe(0);
|
|
357
|
+
expect(processingConversationId).not.toBeNull();
|
|
358
|
+
expect(usageEventCreatedAt).not.toBeNull();
|
|
359
|
+
expect(runsDuringProcessing).toHaveLength(1);
|
|
360
|
+
expect(runsDuringProcessing[0].status).toBe("running");
|
|
361
|
+
expect(runsDuringProcessing[0].conversationId).toBeNull();
|
|
362
|
+
expect(runsDuringProcessing[0].startedAt).toBeLessThanOrEqual(
|
|
363
|
+
usageEventCreatedAt!,
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
const runs = getScheduleRuns(schedule.id);
|
|
367
|
+
expect(runs).toHaveLength(1);
|
|
368
|
+
expect(runs[0].id).toBe(runsDuringProcessing[0].id);
|
|
369
|
+
expect(runs[0].status).toBe("ok");
|
|
370
|
+
expect(runs[0].conversationId).toBe(processingConversationId);
|
|
371
|
+
expect(runs[0].startedAt).toBeLessThanOrEqual(usageEventCreatedAt!);
|
|
372
|
+
expect(runs[0].finishedAt).not.toBeNull();
|
|
373
|
+
expect(runs[0].finishedAt!).toBeGreaterThanOrEqual(usageEventCreatedAt!);
|
|
374
|
+
|
|
375
|
+
const summary = getScheduleUsageSummaries({ from, to }).find(
|
|
376
|
+
(row) => row.scheduleId === schedule.id,
|
|
377
|
+
);
|
|
378
|
+
expect(summary).toEqual({
|
|
379
|
+
scheduleId: schedule.id,
|
|
380
|
+
runCount: 1,
|
|
381
|
+
totalEstimatedCostUsd: 0.25,
|
|
382
|
+
eventCount: 1,
|
|
383
|
+
});
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
test("opens a normal execute schedule run before fresh background processing and records the conversation id", async () => {
|
|
387
|
+
const schedule = createSchedule({
|
|
388
|
+
name: "Usage Attribution Message Schedule",
|
|
389
|
+
cronExpression: "* * * * *",
|
|
390
|
+
message: "Spend scheduled message tokens",
|
|
391
|
+
syntax: "cron",
|
|
392
|
+
});
|
|
393
|
+
forceScheduleDue(schedule.id);
|
|
394
|
+
|
|
395
|
+
const from = Date.now() - 1000;
|
|
396
|
+
let processingConversationId: string | null = null;
|
|
397
|
+
let usageEventCreatedAt: number | null = null;
|
|
398
|
+
let runsDuringProcessing: ReturnType<typeof getScheduleRuns> = [];
|
|
399
|
+
|
|
400
|
+
onRunBackgroundJobCall = (info) => {
|
|
401
|
+
processingConversationId = info.conversationId;
|
|
402
|
+
runsDuringProcessing = getScheduleRuns(schedule.id);
|
|
403
|
+
const event = recordUsageEvent(
|
|
404
|
+
{
|
|
405
|
+
conversationId: info.conversationId,
|
|
406
|
+
runId: null,
|
|
407
|
+
requestId: "req-scheduled-message-usage",
|
|
408
|
+
actor: "main_agent",
|
|
409
|
+
callSite: "mainAgent",
|
|
410
|
+
inferenceProfile: "balanced",
|
|
411
|
+
provider: "anthropic",
|
|
412
|
+
model: "claude-sonnet-4-20250514",
|
|
413
|
+
inputTokens: 80,
|
|
414
|
+
outputTokens: 20,
|
|
415
|
+
cacheCreationInputTokens: 0,
|
|
416
|
+
cacheReadInputTokens: 0,
|
|
417
|
+
rawUsage: null,
|
|
418
|
+
},
|
|
419
|
+
{ estimatedCostUsd: 0.1, pricingStatus: "priced" },
|
|
420
|
+
);
|
|
421
|
+
usageEventCreatedAt = event.createdAt;
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
const result = await runScheduleDueWorkOnce(
|
|
425
|
+
async () => {},
|
|
426
|
+
() => {},
|
|
427
|
+
);
|
|
428
|
+
const to = Date.now() + 1000;
|
|
429
|
+
|
|
430
|
+
expect(result.completed).toBe(1);
|
|
431
|
+
expect(result.failed).toBe(0);
|
|
432
|
+
expect(processingConversationId).not.toBeNull();
|
|
433
|
+
expect(usageEventCreatedAt).not.toBeNull();
|
|
434
|
+
expect(runsDuringProcessing).toHaveLength(1);
|
|
435
|
+
expect(runsDuringProcessing[0].status).toBe("running");
|
|
436
|
+
expect(runsDuringProcessing[0].conversationId).toBe(
|
|
437
|
+
processingConversationId,
|
|
438
|
+
);
|
|
439
|
+
expect(runsDuringProcessing[0].startedAt).toBeLessThanOrEqual(
|
|
440
|
+
usageEventCreatedAt!,
|
|
441
|
+
);
|
|
442
|
+
|
|
443
|
+
const runs = getScheduleRuns(schedule.id);
|
|
444
|
+
expect(runs).toHaveLength(1);
|
|
445
|
+
expect(runs[0].id).toBe(runsDuringProcessing[0].id);
|
|
446
|
+
expect(runs[0].status).toBe("ok");
|
|
447
|
+
expect(runs[0].conversationId).toBe(processingConversationId);
|
|
448
|
+
expect(runs[0].startedAt).toBeLessThanOrEqual(usageEventCreatedAt!);
|
|
449
|
+
expect(runs[0].finishedAt).not.toBeNull();
|
|
450
|
+
expect(runs[0].finishedAt!).toBeGreaterThanOrEqual(usageEventCreatedAt!);
|
|
451
|
+
|
|
452
|
+
const summary = getScheduleUsageSummaries({ from, to }).find(
|
|
453
|
+
(row) => row.scheduleId === schedule.id,
|
|
454
|
+
);
|
|
455
|
+
expect(summary).toEqual({
|
|
456
|
+
scheduleId: schedule.id,
|
|
457
|
+
runCount: 1,
|
|
458
|
+
totalEstimatedCostUsd: 0.1,
|
|
459
|
+
eventCount: 1,
|
|
460
|
+
});
|
|
461
|
+
});
|
|
301
462
|
});
|
|
@@ -141,7 +141,7 @@ describe("buildSanitizedEnv", () => {
|
|
|
141
141
|
expect(env.LC_CTYPE).toBe("UTF-8");
|
|
142
142
|
});
|
|
143
143
|
|
|
144
|
-
test("only includes Kata apt variables
|
|
144
|
+
test("only includes Kata apt variables for Kata-family sandbox runtimes", () => {
|
|
145
145
|
process.env.VELLUM_SANDBOX_RUNTIME = "gvisor";
|
|
146
146
|
process.env.PATH = "/usr/bin";
|
|
147
147
|
process.env.VELLUM_APT_DATA_ROOT = "/data/system";
|
|
@@ -162,6 +162,11 @@ describe("buildSanitizedEnv", () => {
|
|
|
162
162
|
"/data/system/usr/local/lib",
|
|
163
163
|
);
|
|
164
164
|
expect(env.LD_LIBRARY_PATH.split(":")).not.toContain("/host/lib");
|
|
165
|
+
|
|
166
|
+
process.env.VELLUM_SANDBOX_RUNTIME = "cloud-hypervisor";
|
|
167
|
+
env = buildSanitizedEnv();
|
|
168
|
+
expect(env.VELLUM_APT_DATA_ROOT).toBe("/data/system");
|
|
169
|
+
expect(env.PATH.split(":")).toContain("/data/system/usr/bin");
|
|
165
170
|
});
|
|
166
171
|
|
|
167
172
|
test("defaults LANG and LC_ALL to UTF-8 when unset", () => {
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the default `title-generate` plugin's hooks.
|
|
3
|
+
*
|
|
4
|
+
* The plugin contributes two pure-trigger hooks that delegate the title work
|
|
5
|
+
* to the service:
|
|
6
|
+
*
|
|
7
|
+
* - `user-prompt-submit` — first-pass generation from the submitted prompt,
|
|
8
|
+
* scheduled on a later macrotask so the main agent-loop LLM request is
|
|
9
|
+
* issued first.
|
|
10
|
+
* - `stop` — second-pass regeneration once the conversation reaches its third
|
|
11
|
+
* user turn (turn count derived from the user prompts in history).
|
|
12
|
+
*
|
|
13
|
+
* Both let the title service resolve the provider, persist the title, and
|
|
14
|
+
* broadcast the resulting `conversation_title_updated` / `sync_changed`
|
|
15
|
+
* events.
|
|
16
|
+
*
|
|
17
|
+
* Mocks `memory/conversation-title-service.js` and `config/loader.js` so the
|
|
18
|
+
* tests don't touch the real provider stack or config, and resets the plugin
|
|
19
|
+
* registry between cases.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
23
|
+
|
|
24
|
+
// Stub the title-generation service before importing anything that binds
|
|
25
|
+
// to it, so both the default plugin and the hooks capture the stubbed binding.
|
|
26
|
+
const queueGenerateConversationTitleMock = mock(
|
|
27
|
+
(_params: {
|
|
28
|
+
conversationId: string;
|
|
29
|
+
provider?: unknown;
|
|
30
|
+
userMessage?: string;
|
|
31
|
+
}): void => undefined,
|
|
32
|
+
);
|
|
33
|
+
const queueRegenerateConversationTitleMock = mock(
|
|
34
|
+
(_params: { conversationId: string; provider?: unknown }): void => undefined,
|
|
35
|
+
);
|
|
36
|
+
mock.module("../memory/conversation-title-service.js", () => ({
|
|
37
|
+
queueGenerateConversationTitle: queueGenerateConversationTitleMock,
|
|
38
|
+
queueRegenerateConversationTitle: queueRegenerateConversationTitleMock,
|
|
39
|
+
}));
|
|
40
|
+
|
|
41
|
+
// The `stop` hook reads `conversations.skipAutoRetitling`; stub the loader so
|
|
42
|
+
// the opt-out is controllable per test.
|
|
43
|
+
let skipAutoRetitling = false;
|
|
44
|
+
mock.module("../config/loader.js", () => ({
|
|
45
|
+
getConfig: () => ({ conversations: { skipAutoRetitling } }),
|
|
46
|
+
}));
|
|
47
|
+
|
|
48
|
+
import { HOOKS } from "../plugin-api/constants.js";
|
|
49
|
+
import type {
|
|
50
|
+
PluginLogger,
|
|
51
|
+
StopContext,
|
|
52
|
+
UserPromptSubmitContext,
|
|
53
|
+
} from "../plugin-api/types.js";
|
|
54
|
+
import stop from "../plugins/defaults/title-generate/hooks/stop.js";
|
|
55
|
+
import userPromptSubmit from "../plugins/defaults/title-generate/hooks/user-prompt-submit.js";
|
|
56
|
+
import { defaultTitleGeneratePlugin } from "../plugins/defaults/title-generate/register.js";
|
|
57
|
+
import { runHook } from "../plugins/pipeline.js";
|
|
58
|
+
import {
|
|
59
|
+
registerPlugin,
|
|
60
|
+
resetPluginRegistryForTests,
|
|
61
|
+
} from "../plugins/registry.js";
|
|
62
|
+
import type { Message } from "../providers/types.js";
|
|
63
|
+
|
|
64
|
+
const noopLogger: PluginLogger = {
|
|
65
|
+
info: () => {},
|
|
66
|
+
warn: () => {},
|
|
67
|
+
error: () => {},
|
|
68
|
+
debug: () => {},
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
function makeCtx(
|
|
72
|
+
overrides: Partial<UserPromptSubmitContext> = {},
|
|
73
|
+
): UserPromptSubmitContext {
|
|
74
|
+
const messages: Message[] = [
|
|
75
|
+
{ role: "user", content: [{ type: "text", text: "first message" }] },
|
|
76
|
+
];
|
|
77
|
+
return {
|
|
78
|
+
conversationId: "conv-1",
|
|
79
|
+
prompt: "first message",
|
|
80
|
+
originalMessages: messages,
|
|
81
|
+
latestMessages: messages,
|
|
82
|
+
logger: noopLogger,
|
|
83
|
+
...overrides,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Flush pending `setTimeout(0)` callbacks so the fire-and-forget trigger runs. */
|
|
88
|
+
function flushMacrotasks(): Promise<void> {
|
|
89
|
+
return new Promise((resolve) => setTimeout(resolve, 0));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function userTurn(text: string): Message {
|
|
93
|
+
return { role: "user", content: [{ type: "text", text }] };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function assistantTurn(text: string): Message {
|
|
97
|
+
return { role: "assistant", content: [{ type: "text", text }] };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/** A user-role message carrying only tool results, not a fresh prompt. */
|
|
101
|
+
function toolResultTurn(): Message {
|
|
102
|
+
return {
|
|
103
|
+
role: "user",
|
|
104
|
+
content: [{ type: "tool_result", tool_use_id: "tool-1", content: "ok" }],
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** History with `count` genuine user turns interleaved with assistant replies. */
|
|
109
|
+
function historyWithUserTurns(count: number): Message[] {
|
|
110
|
+
const messages: Message[] = [];
|
|
111
|
+
for (let i = 1; i <= count; i++) {
|
|
112
|
+
messages.push(userTurn(`message ${i}`));
|
|
113
|
+
messages.push(assistantTurn(`reply ${i}`));
|
|
114
|
+
}
|
|
115
|
+
return messages;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function makeStopCtx(overrides: Partial<StopContext> = {}): StopContext {
|
|
119
|
+
return {
|
|
120
|
+
conversationId: "conv-1",
|
|
121
|
+
messages: historyWithUserTurns(3),
|
|
122
|
+
responseContent: [{ type: "text", text: "reply 3" }],
|
|
123
|
+
stopReason: "end_turn",
|
|
124
|
+
decision: "stop",
|
|
125
|
+
logger: noopLogger,
|
|
126
|
+
...overrides,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
describe("title-generate user-prompt-submit hook", () => {
|
|
131
|
+
beforeEach(() => {
|
|
132
|
+
resetPluginRegistryForTests();
|
|
133
|
+
queueGenerateConversationTitleMock.mockReset();
|
|
134
|
+
queueGenerateConversationTitleMock.mockImplementation(() => undefined);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("queues a title-generation job from the submitted prompt", async () => {
|
|
138
|
+
// GIVEN a fresh user prompt submission
|
|
139
|
+
const ctx = makeCtx({ conversationId: "conv-1", prompt: "first message" });
|
|
140
|
+
|
|
141
|
+
// WHEN the default hook runs and its deferred work flushes
|
|
142
|
+
await userPromptSubmit(ctx);
|
|
143
|
+
await flushMacrotasks();
|
|
144
|
+
|
|
145
|
+
// THEN the title service is invoked with just the conversation id and the
|
|
146
|
+
// submitted prompt — provider resolution and emit are owned by the service.
|
|
147
|
+
expect(queueGenerateConversationTitleMock).toHaveBeenCalledTimes(1);
|
|
148
|
+
const call = queueGenerateConversationTitleMock.mock.calls[0]?.[0];
|
|
149
|
+
expect(call?.conversationId).toBe("conv-1");
|
|
150
|
+
expect(call?.userMessage).toBe("first message");
|
|
151
|
+
expect(call).not.toHaveProperty("provider");
|
|
152
|
+
expect(call).not.toHaveProperty("onTitleUpdated");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("does not block: returns before the title job is scheduled", async () => {
|
|
156
|
+
// GIVEN a fresh user prompt submission
|
|
157
|
+
const ctx = makeCtx();
|
|
158
|
+
|
|
159
|
+
// WHEN the hook resolves
|
|
160
|
+
await userPromptSubmit(ctx);
|
|
161
|
+
|
|
162
|
+
// THEN the title job has not run yet (it is deferred to a later macrotask),
|
|
163
|
+
// AND it runs once the macrotask queue is flushed.
|
|
164
|
+
expect(queueGenerateConversationTitleMock).toHaveBeenCalledTimes(0);
|
|
165
|
+
await flushMacrotasks();
|
|
166
|
+
expect(queueGenerateConversationTitleMock).toHaveBeenCalledTimes(1);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test("fires through runHook once the default plugin is registered", async () => {
|
|
170
|
+
// GIVEN the default title-generate plugin registered in the registry
|
|
171
|
+
registerPlugin(defaultTitleGeneratePlugin);
|
|
172
|
+
|
|
173
|
+
// WHEN a prompt is submitted through the hook chain
|
|
174
|
+
await runHook(
|
|
175
|
+
HOOKS.USER_PROMPT_SUBMIT,
|
|
176
|
+
makeCtx({ prompt: "draft a plan" }),
|
|
177
|
+
);
|
|
178
|
+
await flushMacrotasks();
|
|
179
|
+
|
|
180
|
+
// THEN the title service is triggered with the submitted prompt text
|
|
181
|
+
expect(queueGenerateConversationTitleMock).toHaveBeenCalledTimes(1);
|
|
182
|
+
expect(
|
|
183
|
+
queueGenerateConversationTitleMock.mock.calls[0]?.[0]?.userMessage,
|
|
184
|
+
).toBe("draft a plan");
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
describe("title-generate stop hook", () => {
|
|
189
|
+
beforeEach(() => {
|
|
190
|
+
resetPluginRegistryForTests();
|
|
191
|
+
queueRegenerateConversationTitleMock.mockReset();
|
|
192
|
+
queueRegenerateConversationTitleMock.mockImplementation(() => undefined);
|
|
193
|
+
skipAutoRetitling = false;
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
test("regenerates the title on the third user turn", async () => {
|
|
197
|
+
// GIVEN a turn ending with three genuine user prompts in history
|
|
198
|
+
const ctx = makeStopCtx({ messages: historyWithUserTurns(3) });
|
|
199
|
+
|
|
200
|
+
// WHEN the stop hook runs and its deferred work flushes
|
|
201
|
+
await stop(ctx);
|
|
202
|
+
await flushMacrotasks();
|
|
203
|
+
|
|
204
|
+
// THEN the second-pass regeneration is triggered with just the
|
|
205
|
+
// conversation id — provider resolution and emit are owned by the service.
|
|
206
|
+
expect(queueRegenerateConversationTitleMock).toHaveBeenCalledTimes(1);
|
|
207
|
+
const call = queueRegenerateConversationTitleMock.mock.calls[0]?.[0];
|
|
208
|
+
expect(call?.conversationId).toBe("conv-1");
|
|
209
|
+
expect(call).not.toHaveProperty("provider");
|
|
210
|
+
expect(call).not.toHaveProperty("signal");
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test("defers the regeneration so the completed turn is persisted first", async () => {
|
|
214
|
+
// GIVEN a turn ending on the third user turn
|
|
215
|
+
const ctx = makeStopCtx({ messages: historyWithUserTurns(3) });
|
|
216
|
+
|
|
217
|
+
// WHEN the hook resolves
|
|
218
|
+
await stop(ctx);
|
|
219
|
+
|
|
220
|
+
// THEN the regeneration has not fired yet — it is deferred to a later
|
|
221
|
+
// macrotask so the turn's assistant reply lands first, AND it fires once
|
|
222
|
+
// the macrotask queue is flushed.
|
|
223
|
+
expect(queueRegenerateConversationTitleMock).toHaveBeenCalledTimes(0);
|
|
224
|
+
await flushMacrotasks();
|
|
225
|
+
expect(queueRegenerateConversationTitleMock).toHaveBeenCalledTimes(1);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test("does not regenerate before the third user turn", async () => {
|
|
229
|
+
// GIVEN a turn ending with only two genuine user prompts
|
|
230
|
+
const ctx = makeStopCtx({ messages: historyWithUserTurns(2) });
|
|
231
|
+
|
|
232
|
+
// WHEN the stop hook runs and any deferred work flushes
|
|
233
|
+
await stop(ctx);
|
|
234
|
+
await flushMacrotasks();
|
|
235
|
+
|
|
236
|
+
// THEN no regeneration fires — the conversation lacks enough context yet
|
|
237
|
+
expect(queueRegenerateConversationTitleMock).toHaveBeenCalledTimes(0);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test("does not regenerate after the third user turn", async () => {
|
|
241
|
+
// GIVEN a turn ending with four genuine user prompts
|
|
242
|
+
const ctx = makeStopCtx({ messages: historyWithUserTurns(4) });
|
|
243
|
+
|
|
244
|
+
// WHEN the stop hook runs and any deferred work flushes
|
|
245
|
+
await stop(ctx);
|
|
246
|
+
await flushMacrotasks();
|
|
247
|
+
|
|
248
|
+
// THEN no regeneration fires — the single second pass already passed
|
|
249
|
+
expect(queueRegenerateConversationTitleMock).toHaveBeenCalledTimes(0);
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test("ignores tool-result user messages when counting turns", async () => {
|
|
253
|
+
// GIVEN three genuine user prompts plus a tool-result user message
|
|
254
|
+
const messages: Message[] = [
|
|
255
|
+
userTurn("message 1"),
|
|
256
|
+
assistantTurn("reply 1"),
|
|
257
|
+
userTurn("message 2"),
|
|
258
|
+
assistantTurn("calling a tool"),
|
|
259
|
+
toolResultTurn(),
|
|
260
|
+
assistantTurn("reply 2"),
|
|
261
|
+
userTurn("message 3"),
|
|
262
|
+
];
|
|
263
|
+
const ctx = makeStopCtx({ messages });
|
|
264
|
+
|
|
265
|
+
// WHEN the stop hook runs and its deferred work flushes
|
|
266
|
+
await stop(ctx);
|
|
267
|
+
await flushMacrotasks();
|
|
268
|
+
|
|
269
|
+
// THEN the tool-result message is not counted as a turn, so the third
|
|
270
|
+
// genuine prompt still triggers the regeneration
|
|
271
|
+
expect(queueRegenerateConversationTitleMock).toHaveBeenCalledTimes(1);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
test("defers when an earlier hook chose to continue the run", async () => {
|
|
275
|
+
// GIVEN a third-user-turn stop that an earlier hook flipped to "continue"
|
|
276
|
+
const ctx = makeStopCtx({
|
|
277
|
+
messages: historyWithUserTurns(3),
|
|
278
|
+
decision: "continue",
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// WHEN the stop hook runs and any deferred work flushes
|
|
282
|
+
await stop(ctx);
|
|
283
|
+
await flushMacrotasks();
|
|
284
|
+
|
|
285
|
+
// THEN it defers to the eventual terminal stop rather than re-titling now
|
|
286
|
+
expect(queueRegenerateConversationTitleMock).toHaveBeenCalledTimes(0);
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
test("respects the skipAutoRetitling opt-out", async () => {
|
|
290
|
+
// GIVEN the user opted out of second-pass retitling
|
|
291
|
+
skipAutoRetitling = true;
|
|
292
|
+
const ctx = makeStopCtx({ messages: historyWithUserTurns(3) });
|
|
293
|
+
|
|
294
|
+
// WHEN the stop hook runs on the third user turn and any work flushes
|
|
295
|
+
await stop(ctx);
|
|
296
|
+
await flushMacrotasks();
|
|
297
|
+
|
|
298
|
+
// THEN no regeneration fires
|
|
299
|
+
expect(queueRegenerateConversationTitleMock).toHaveBeenCalledTimes(0);
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
test("fires through runHook once the default plugin is registered", async () => {
|
|
303
|
+
// GIVEN the default title-generate plugin registered in the registry
|
|
304
|
+
registerPlugin(defaultTitleGeneratePlugin);
|
|
305
|
+
|
|
306
|
+
// WHEN a third-user-turn stop is dispatched through the hook chain
|
|
307
|
+
await runHook(
|
|
308
|
+
HOOKS.STOP,
|
|
309
|
+
makeStopCtx({ messages: historyWithUserTurns(3) }),
|
|
310
|
+
);
|
|
311
|
+
await flushMacrotasks();
|
|
312
|
+
|
|
313
|
+
// THEN the second-pass regeneration is triggered
|
|
314
|
+
expect(queueRegenerateConversationTitleMock).toHaveBeenCalledTimes(1);
|
|
315
|
+
expect(
|
|
316
|
+
queueRegenerateConversationTitleMock.mock.calls[0]?.[0]?.conversationId,
|
|
317
|
+
).toBe("conv-1");
|
|
318
|
+
});
|
|
319
|
+
});
|