@vellumai/assistant 0.8.8 → 0.8.9-dev.202606091926.ebb2d62
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/ARCHITECTURE.md +6 -6
- package/bun.lock +2 -2
- package/docs/activation-funnel-telemetry.md +310 -0
- package/examples/plugins/echo/README.md +61 -60
- 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/skill-host.ts +7 -6
- package/openapi.yaml +273 -121
- package/package.json +2 -2
- package/scripts/sync-llm-catalog.ts +2 -0
- package/src/__tests__/activation-early-marking.test.ts +120 -0
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +69 -14
- package/src/__tests__/agent-loop-exit-reason.test.ts +204 -144
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +50 -35
- package/src/__tests__/agent-loop-output-hooks.test.ts +357 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +25 -6
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +41 -21
- package/src/__tests__/agent-loop-thinking.test.ts +36 -20
- package/src/__tests__/agent-loop.test.ts +441 -96
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +14 -14
- package/src/__tests__/agent-wake-override-profile.test.ts +17 -21
- package/src/__tests__/anthropic-provider.test.ts +23 -8
- package/src/__tests__/app-builder-tool-scripts.test.ts +21 -0
- package/src/__tests__/app-control-flow.test.ts +1 -1
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +132 -0
- package/src/__tests__/approval-cascade.test.ts +6 -5
- package/src/__tests__/approval-routes-http.test.ts +4 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
- package/src/__tests__/build-persisted-content.test.ts +75 -1
- package/src/__tests__/channel-approval-routes.test.ts +1 -1
- package/src/__tests__/channel-approvals.test.ts +1 -1
- package/src/__tests__/compaction-circuit.test.ts +258 -0
- package/src/__tests__/compaction-direct.test.ts +146 -0
- package/src/__tests__/compaction-events.test.ts +7 -7
- package/src/__tests__/compaction.benchmark.test.ts +1 -1
- package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
- package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
- package/src/__tests__/context-overflow-reducer.test.ts +265 -125
- package/src/__tests__/context-window-manager-compact-retry.test.ts +121 -15
- package/src/__tests__/conversation-abort-tool-results.test.ts +7 -5
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +7 -10
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +78 -119
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +158 -234
- package/src/__tests__/conversation-agent-loop.test.ts +342 -623
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +6 -5
- package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
- package/src/__tests__/conversation-error.test.ts +15 -1
- package/src/__tests__/conversation-history-web-search.test.ts +6 -1
- package/src/__tests__/conversation-media-retry.test.ts +1 -1
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +44 -4
- package/src/__tests__/conversation-process-callsite.test.ts +15 -15
- package/src/__tests__/conversation-provider-retry-repair.test.ts +66 -65
- package/src/__tests__/conversation-queue.test.ts +10 -10
- package/src/__tests__/conversation-runtime-assembly.test.ts +1059 -231
- package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
- package/src/__tests__/conversation-slash-queue.test.ts +7 -5
- package/src/__tests__/conversation-slash-unknown.test.ts +7 -5
- package/src/__tests__/conversation-speed-override.test.ts +11 -10
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +395 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +48 -1
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +73 -4
- package/src/__tests__/conversation-undo.test.ts +2 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +24 -21
- package/src/__tests__/conversation-workspace-injection.test.ts +69 -7
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +15 -12
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/corrected-target.test.ts +93 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +5 -4
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +36 -0
- package/src/__tests__/db-acp-history.test.ts +101 -0
- package/src/__tests__/dynamic-page-surface.test.ts +31 -0
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
- package/src/__tests__/empty-response-hook.test.ts +1 -1
- package/src/__tests__/file-write-tool.test.ts +63 -0
- package/src/__tests__/gateway-only-guard.test.ts +17 -2
- package/src/__tests__/guardian-grant-minting.test.ts +1 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +93 -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__/history-repair-hook.test.ts +5 -1
- package/src/__tests__/host-app-control-proxy.test.ts +45 -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__/inference-profile-reaper.test.ts +62 -0
- package/src/__tests__/inference-profile-session-handler.test.ts +86 -0
- package/src/__tests__/injector-background-turn.test.ts +13 -23
- package/src/__tests__/injector-chain.test.ts +268 -44
- package/src/__tests__/injector-disk-pressure.test.ts +210 -52
- package/src/__tests__/injector-document-comments.test.ts +97 -114
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
- package/src/__tests__/injector-v3-suppression.test.ts +4 -4
- package/src/__tests__/list-messages-client-message-id.test.ts +91 -0
- package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
- package/src/__tests__/llm-catalog-parity.test.ts +4 -0
- package/src/__tests__/llm-resolver.test.ts +73 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +156 -38
- package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
- package/src/__tests__/notification-decision-strategy.test.ts +3 -3
- package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +145 -0
- package/src/__tests__/persist-unsendable-image.test.ts +97 -1
- package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
- package/src/__tests__/plugin-api-shim.test.ts +6 -9
- package/src/__tests__/plugin-bootstrap.test.ts +91 -81
- package/src/__tests__/plugin-registry.test.ts +3 -49
- package/src/__tests__/plugin-skill-contribution.test.ts +15 -14
- package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
- package/src/__tests__/plugin-types.test.ts +0 -70
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +69 -0
- package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
- package/src/__tests__/published-app-updater.test.ts +138 -0
- package/src/__tests__/reaction-persistence.test.ts +1 -1
- package/src/__tests__/retry-thinking-adaptive-only.test.ts +245 -0
- package/src/__tests__/send-endpoint-busy.test.ts +4 -1
- package/src/__tests__/server-history-render.test.ts +39 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +31 -0
- package/src/__tests__/steer-tool-repair.test.ts +1 -1
- package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
- package/src/__tests__/subagent-detail.test.ts +25 -7
- 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__/title-generate-hook.test.ts +5 -1
- package/src/__tests__/tool-error-hook.test.ts +1 -1
- package/src/__tests__/tool-result-truncate-hook.test.ts +1 -1
- package/src/__tests__/tool-start-timestamp.test.ts +218 -0
- package/src/__tests__/user-plugin-loader.test.ts +54 -286
- package/src/__tests__/web-fetch.test.ts +45 -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-config-stub.ts +0 -2
- package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
- package/src/acp/__tests__/helpers/exec-file-stub.ts +106 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +97 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
- package/src/acp/__tests__/session-manager-resume.test.ts +888 -0
- package/src/acp/agent-process.ts +61 -1
- package/src/acp/auto-install.test.ts +280 -0
- package/src/acp/auto-install.ts +232 -0
- package/src/acp/client-handler.ts +31 -0
- package/src/acp/prepare-agent-env.ts +80 -27
- package/src/acp/resolve-agent.test.ts +188 -28
- package/src/acp/resolve-agent.ts +119 -42
- package/src/acp/resume-hint.ts +23 -0
- package/src/acp/session-manager.ts +507 -73
- package/src/agent/compaction-circuit.ts +60 -102
- package/src/agent/loop.ts +403 -251
- package/src/api/events/tool-result.ts +9 -0
- package/src/api/events/tool-use-start.ts +7 -0
- package/src/api/responses/conversation-message.ts +72 -13
- package/src/api/responses/memory-v3-selection-log.ts +19 -10
- package/src/approvals/guardian-request-resolvers.ts +27 -1
- package/src/background-wake/next-wake.ts +1 -0
- package/src/cli/commands/__tests__/memory-v3.test.ts +191 -210
- package/src/cli/commands/channel-verification-sessions.ts +6 -6
- package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
- package/src/cli/commands/memory-v3.ts +57 -199
- package/src/cli/commands/plugins.ts +43 -37
- package/src/cli/lib/__tests__/install-from-github.test.ts +790 -111
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
- package/src/cli/lib/__tests__/plugin-details.test.ts +381 -0
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +270 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +233 -32
- package/src/cli/lib/install-from-github.ts +797 -60
- package/src/cli/lib/plugin-catalog-cache.ts +84 -0
- package/src/cli/lib/plugin-details.ts +416 -0
- package/src/cli/lib/plugin-marketplace.ts +215 -0
- package/src/cli/lib/search-plugins.ts +201 -29
- 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 +9 -3
- package/src/config/acp-schema.ts +1 -7
- package/src/config/bundled-skills/acp/SKILL.md +80 -41
- package/src/config/bundled-skills/acp/TOOLS.json +4 -4
- package/src/config/bundled-skills/app-builder/SKILL.md +224 -381
- package/src/config/bundled-skills/app-builder/TOOLS.json +72 -2
- 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/tools/app-list.ts +62 -0
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +18 -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-tool-registry.ts +4 -0
- package/src/config/call-site-defaults.ts +0 -3
- package/src/config/feature-flag-registry.json +10 -56
- package/src/config/llm-resolver.ts +39 -7
- package/src/config/loader.ts +5 -22
- package/src/config/schemas/__tests__/memory-v3.test.ts +25 -9
- package/src/config/schemas/call-site-catalog.ts +0 -21
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +17 -3
- package/src/config/schemas/memory-v3.ts +58 -8
- package/src/config/seed-inference-profiles.ts +18 -0
- package/src/context/compactor.ts +22 -4
- package/src/context/post-turn-tool-result-truncation.ts +39 -1
- package/src/context/strip-injections.ts +8 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +120 -42
- package/src/daemon/conversation-agent-loop.ts +308 -1297
- package/src/daemon/conversation-error.ts +31 -4
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +11 -255
- package/src/daemon/conversation-media-retry.ts +19 -6
- package/src/daemon/conversation-messaging.ts +17 -0
- package/src/daemon/conversation-process.ts +22 -141
- package/src/daemon/conversation-queue-manager.ts +8 -0
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +361 -393
- package/src/daemon/conversation-store.ts +9 -90
- package/src/daemon/conversation-surfaces.ts +165 -11
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +444 -62
- package/src/daemon/disk-pressure-policy.ts +0 -1
- package/src/daemon/external-plugins-bootstrap.ts +36 -46
- package/src/daemon/handlers/config-channels.ts +11 -2
- package/src/daemon/handlers/conversations.ts +3 -1
- package/src/daemon/handlers/shared.ts +9 -1
- package/src/daemon/handlers/skills.ts +4 -1
- package/src/daemon/host-app-control-proxy.ts +72 -57
- package/src/daemon/host-proxy-preactivation.ts +1 -3
- package/src/daemon/lifecycle.ts +21 -7
- package/src/daemon/persist-unsendable-image.ts +62 -25
- package/src/daemon/process-message.ts +1 -1
- package/src/daemon/server.ts +2 -0
- package/src/daemon/tool-side-effects.ts +15 -0
- package/src/daemon/wake-conversation-ops.ts +269 -0
- package/src/embedded/plugin-api.ts +2 -2
- package/src/export/__tests__/transcript-formatter.test.ts +5 -0
- 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/ipc/__tests__/browser-ipc.test.ts +1 -1
- package/src/ipc/__tests__/ui-request-route.test.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/memory/__tests__/activation-session-store.test.ts +41 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +87 -0
- package/src/memory/__tests__/onboarding-events-store.test.ts +80 -0
- package/src/memory/activation-session-store.ts +43 -0
- package/src/memory/conversation-crud.ts +29 -19
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/db-init.ts +6 -0
- package/src/memory/embedding-backend.ts +5 -22
- package/src/memory/job-handlers/conversation-starters.ts +13 -2
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -4
- package/src/memory/jobs-worker.ts +25 -1
- package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
- package/src/memory/migrations/273-onboarding-events-funnel-columns.ts +46 -0
- package/src/memory/migrations/274-create-activation-sessions.ts +15 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/onboarding-events-store.ts +66 -18
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/schema/infrastructure.ts +13 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -97
- package/src/memory/v2/__tests__/page-store.test.ts +22 -0
- package/src/memory/v2/consolidation-job.ts +2 -63
- package/src/memory/v2/types.ts +5 -0
- package/src/messaging/providers/telegram-bot/api.ts +14 -5
- package/src/notifications/__tests__/copy-composer.test.ts +244 -0
- package/src/notifications/access-request-copy.ts +298 -0
- package/src/notifications/adapters/slack.ts +3 -3
- package/src/notifications/adapters/telegram.ts +9 -2
- package/src/notifications/copy-composer.ts +49 -267
- package/src/notifications/decision-engine.ts +16 -35
- package/src/notifications/home-feed-side-effect.ts +1 -6
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +40 -11
- package/src/plugin-api/types.ts +108 -0
- package/src/plugins/defaults/compaction/compact.ts +106 -0
- package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +250 -42
- package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
- package/src/plugins/defaults/compaction/manager-store.ts +57 -0
- package/src/plugins/defaults/compaction/package.json +1 -2
- package/src/{context → plugins/defaults/compaction}/window-manager.ts +95 -25
- package/src/plugins/defaults/empty-response/package.json +0 -1
- package/src/plugins/defaults/history-repair/package.json +0 -1
- package/src/plugins/defaults/index.ts +156 -26
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +285 -0
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +2 -2
- package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +148 -73
- package/src/plugins/defaults/memory-retrieval/package.json +14 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +223 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/capabilities.test.ts +71 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/dense.test.ts +181 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/edge.test.ts +247 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +147 -134
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +456 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +662 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +306 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +91 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +402 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-needle.test.ts +135 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/sections.test.ts +125 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +74 -19
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +446 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +592 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -24
- package/src/plugins/defaults/memory-v3-shadow/capabilities.ts +91 -0
- package/src/plugins/defaults/memory-v3-shadow/dense.ts +97 -0
- package/src/plugins/defaults/memory-v3-shadow/edge.ts +252 -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 +76 -0
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +555 -0
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +266 -0
- package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
- package/src/plugins/defaults/memory-v3-shadow/page-content.ts +71 -0
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +204 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +16 -8
- package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +236 -0
- package/src/plugins/defaults/memory-v3-shadow/section-needle.ts +200 -0
- package/src/plugins/defaults/memory-v3-shadow/sections.ts +115 -0
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +156 -0
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +382 -0
- package/src/plugins/defaults/memory-v3-shadow/types.ts +92 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +4 -1
- package/src/plugins/defaults/title-generate/package.json +0 -1
- package/src/plugins/defaults/tool-error/package.json +0 -1
- package/src/plugins/defaults/tool-result-truncate/package.json +0 -1
- package/src/plugins/external-api.ts +12 -2
- package/src/plugins/pipeline.ts +6 -293
- package/src/plugins/registry.ts +9 -37
- package/src/plugins/types.ts +76 -381
- package/src/plugins/user-loader.ts +30 -127
- package/src/prompts/__tests__/system-prompt.test.ts +6 -0
- package/src/prompts/system-prompt.ts +61 -10
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +71 -4
- package/src/providers/anthropic/client.ts +9 -10
- package/src/providers/inference/kimi-cjk-token-ids.ts +493 -0
- package/src/providers/inference/logit-bias.ts +55 -0
- package/src/providers/model-catalog.ts +57 -0
- package/src/providers/openai/__tests__/vision-not-supported.test.ts +75 -0
- package/src/providers/openai/chat-completions-provider.ts +44 -1
- package/src/providers/retry.ts +57 -7
- package/src/providers/types.ts +6 -0
- package/src/runtime/__tests__/agent-wake.test.ts +555 -691
- package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
- package/src/runtime/agent-wake.ts +108 -209
- package/src/runtime/assistant-event-hub.ts +1 -1
- package/src/runtime/channel-approvals.ts +1 -1
- package/src/runtime/interactive-ui.ts +1 -1
- package/src/runtime/routes/__tests__/acp-routes.test.ts +466 -55
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +423 -73
- package/src/runtime/routes/__tests__/stt-routes.test.ts +112 -0
- 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/acp-routes.test.ts +185 -32
- package/src/runtime/routes/acp-routes.ts +328 -30
- package/src/runtime/routes/app-management-routes.ts +3 -0
- package/src/runtime/routes/approval-routes.ts +1 -1
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/browser-tabs-routes.ts +6 -10
- package/src/runtime/routes/channel-verification-routes.ts +14 -5
- 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 +1 -1
- package/src/runtime/routes/conversation-routes.ts +30 -2
- package/src/runtime/routes/conversation-starter-routes.ts +13 -7
- package/src/runtime/routes/conversations-import-routes.ts +24 -7
- 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-routes.ts +18 -3
- package/src/runtime/routes/inbound-message-handler.ts +1 -1
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
- package/src/runtime/routes/inference-profile-session-handler.ts +11 -0
- package/src/runtime/routes/inference-profile-session-reaper.ts +6 -0
- package/src/runtime/routes/memory-v3-routes.ts +65 -305
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +1 -1
- package/src/runtime/routes/playground/helpers.ts +1 -1
- package/src/runtime/routes/plugins-routes.ts +337 -35
- package/src/runtime/routes/stt-routes.ts +45 -12
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/work-items-routes.ts +2 -4
- package/src/runtime/routes/workspace-routes.ts +50 -15
- package/src/runtime/services/conversation-serializer.ts +1 -1
- package/src/runtime/verification-outbound-actions.ts +147 -2
- package/src/runtime/verification-templates.ts +29 -3
- package/src/services/published-app-updater.ts +30 -8
- package/src/signals/cancel.ts +2 -4
- package/src/subagent/manager.ts +21 -5
- package/src/telemetry/__tests__/activation-funnel.test.ts +95 -0
- package/src/telemetry/activation-funnel.ts +167 -0
- package/src/telemetry/types.ts +13 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +154 -0
- package/src/telemetry/usage-telemetry-reporter.ts +26 -1
- package/src/tools/acp/context.ts +20 -0
- package/src/tools/acp/list-agents.test.ts +9 -19
- package/src/tools/acp/list-agents.ts +3 -15
- package/src/tools/acp/spawn.test.ts +173 -202
- package/src/tools/acp/spawn.ts +37 -172
- package/src/tools/acp/steer.test.ts +105 -8
- package/src/tools/acp/steer.ts +48 -17
- package/src/tools/apps/executors.ts +166 -50
- package/src/tools/browser/browser-execution.ts +12 -2
- package/src/tools/filesystem/write.ts +34 -0
- package/src/tools/network/web-fetch.ts +65 -24
- package/src/tools/skills/load.ts +1 -1
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/ui-surface/definitions.ts +32 -5
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +69 -45
- package/docs/plugins.md +0 -832
- package/examples/plugins/echo/register.ts +0 -143
- 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__/overflow-reduce-pipeline.test.ts +0 -667
- package/src/__tests__/pipeline-runner.test.ts +0 -554
- package/src/daemon/wake-target-adapter.ts +0 -253
- package/src/memory/v3/__tests__/assign.test.ts +0 -242
- package/src/memory/v3/__tests__/capabilities.test.ts +0 -118
- package/src/memory/v3/__tests__/core.test.ts +0 -39
- package/src/memory/v3/__tests__/health.test.ts +0 -219
- package/src/memory/v3/__tests__/maintain-job.test.ts +0 -288
- package/src/memory/v3/__tests__/needle.test.ts +0 -107
- package/src/memory/v3/__tests__/orchestrate.test.ts +0 -436
- package/src/memory/v3/__tests__/provider-blocks.test.ts +0 -13
- package/src/memory/v3/__tests__/reconcile.test.ts +0 -274
- package/src/memory/v3/__tests__/render-injection.test.ts +0 -61
- package/src/memory/v3/__tests__/router.test.ts +0 -332
- package/src/memory/v3/__tests__/selector.test.ts +0 -470
- package/src/memory/v3/__tests__/shadow-plugin.test.ts +0 -432
- package/src/memory/v3/__tests__/snapshot.test.ts +0 -168
- package/src/memory/v3/__tests__/tree.test.ts +0 -192
- package/src/memory/v3/assign.ts +0 -268
- package/src/memory/v3/capabilities.ts +0 -124
- package/src/memory/v3/core.ts +0 -26
- package/src/memory/v3/health.ts +0 -0
- package/src/memory/v3/maintain-job.ts +0 -314
- package/src/memory/v3/needle.ts +0 -115
- package/src/memory/v3/orchestrate.ts +0 -126
- package/src/memory/v3/page-content.ts +0 -34
- package/src/memory/v3/provider-blocks.ts +0 -26
- package/src/memory/v3/reconcile.ts +0 -523
- package/src/memory/v3/router.ts +0 -190
- package/src/memory/v3/selection-log-store.ts +0 -84
- package/src/memory/v3/selector.ts +0 -226
- package/src/memory/v3/shadow-plugin.ts +0 -411
- package/src/memory/v3/snapshot.ts +0 -209
- package/src/memory/v3/tree.ts +0 -174
- package/src/memory/v3/types.ts +0 -59
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
- 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/register.ts +0 -35
- package/src/plugins/defaults/compaction/terminal.ts +0 -73
- package/src/plugins/defaults/empty-response/register.ts +0 -23
- package/src/plugins/defaults/history-repair/register.ts +0 -24
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +0 -216
- 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/title-generate/register.ts +0 -35
- package/src/plugins/defaults/tool-error/register.ts +0 -23
- package/src/plugins/defaults/tool-result-truncate/register.ts +0 -24
- package/src/proactive-artifact/aux-message-injector.ts +0 -97
- package/src/proactive-artifact/decision.test.ts +0 -226
- package/src/proactive-artifact/decision.ts +0 -165
- package/src/proactive-artifact/index.ts +0 -7
- package/src/proactive-artifact/job.test.ts +0 -962
- package/src/proactive-artifact/job.ts +0 -372
- package/src/proactive-artifact/message-copy.ts +0 -58
- package/src/proactive-artifact/trigger-state.test.ts +0 -286
- package/src/proactive-artifact/trigger-state.ts +0 -123
- package/src/util/map-limit.ts +0 -27
- /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__/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}/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}/llm-retry.ts +0 -0
|
@@ -155,6 +155,21 @@ registerHook("app_refresh", (_name, input, _result, { ctx }) => {
|
|
|
155
155
|
notifyAppChanged(ctx, appId, { fileChange: true });
|
|
156
156
|
});
|
|
157
157
|
|
|
158
|
+
// app_update compiles internally (like app_refresh) but emits no events of its
|
|
159
|
+
// own, so without this hook an updated app leaves open surfaces rendering the
|
|
160
|
+
// stale dist and never re-deploys or invalidates the Library. The executor owns
|
|
161
|
+
// the compile; notifyAppChanged only refreshes surfaces and broadcasts.
|
|
162
|
+
registerHook("app_update", (_name, input, _result, { ctx }) => {
|
|
163
|
+
const appId = input.app_id as string | undefined;
|
|
164
|
+
if (!appId) return;
|
|
165
|
+
try {
|
|
166
|
+
addAppConversationId(appId, ctx.conversationId);
|
|
167
|
+
} catch (err) {
|
|
168
|
+
log.warn({ err, appId }, "Failed to track conversation ID on app_update");
|
|
169
|
+
}
|
|
170
|
+
notifyAppChanged(ctx, appId, { fileChange: true });
|
|
171
|
+
});
|
|
172
|
+
|
|
158
173
|
registerHook("voice_config_update", (_name, input) => {
|
|
159
174
|
const setting = input.setting as string | undefined;
|
|
160
175
|
if (!setting) return;
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Daemon-side side effects a wake performs against a live
|
|
3
|
+
* {@link Conversation}, used by `wakeAgentForOpportunity()` in
|
|
4
|
+
* `runtime/agent-wake.ts`.
|
|
5
|
+
*
|
|
6
|
+
* These are kept as standalone functions (rather than methods on
|
|
7
|
+
* `Conversation`) so the wake-specific concerns — event→wire translation,
|
|
8
|
+
* the wake ui_surface card, and the wake's one-shot tail persistence —
|
|
9
|
+
* stay out of the conversation class. They live in the daemon layer
|
|
10
|
+
* because they translate agent-loop events into client wire frames and
|
|
11
|
+
* touch the message store, so `runtime/agent-wake.ts` references only the
|
|
12
|
+
* erased `Conversation` type and never imports `message-protocol.ts`.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { AgentEvent } from "../agent/loop.js";
|
|
16
|
+
import {
|
|
17
|
+
addMessage,
|
|
18
|
+
getConversation,
|
|
19
|
+
provenanceFromTrustContext,
|
|
20
|
+
} from "../memory/conversation-crud.js";
|
|
21
|
+
import { syncMessageToDisk } from "../memory/conversation-disk-view.js";
|
|
22
|
+
import { backfillMessageIdOnLogs } from "../memory/llm-request-log-store.js";
|
|
23
|
+
import type { Message } from "../providers/types.js";
|
|
24
|
+
import { broadcastMessage } from "../runtime/assistant-event-hub.js";
|
|
25
|
+
import { getLogger } from "../util/logger.js";
|
|
26
|
+
import type { Conversation } from "./conversation.js";
|
|
27
|
+
import type { ServerMessage } from "./message-protocol.js";
|
|
28
|
+
|
|
29
|
+
const log = getLogger("wake-conversation-ops");
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Translate a raw {@link AgentEvent} from the agent loop into the
|
|
33
|
+
* corresponding {@link ServerMessage} wire frame. The normal user-turn
|
|
34
|
+
* path does this via the full state-aware handler in
|
|
35
|
+
* `conversation-agent-loop-handlers.ts`; the wake path has no tool
|
|
36
|
+
* accounting, title generation, or activity-state tracking to worry
|
|
37
|
+
* about, so we only need the subset that produces client-visible
|
|
38
|
+
* frames. Events that have no client-visible wire shape (usage, error,
|
|
39
|
+
* preview/input-json deltas, etc.) are dropped — they produce no UI.
|
|
40
|
+
*/
|
|
41
|
+
function translateAgentEventToServerMessage(
|
|
42
|
+
event: AgentEvent,
|
|
43
|
+
conversationId: string,
|
|
44
|
+
): ServerMessage | null {
|
|
45
|
+
switch (event.type) {
|
|
46
|
+
case "text_delta":
|
|
47
|
+
return {
|
|
48
|
+
type: "assistant_text_delta",
|
|
49
|
+
text: event.text,
|
|
50
|
+
conversationId,
|
|
51
|
+
};
|
|
52
|
+
case "thinking_delta":
|
|
53
|
+
return {
|
|
54
|
+
type: "assistant_thinking_delta",
|
|
55
|
+
thinking: event.thinking,
|
|
56
|
+
conversationId,
|
|
57
|
+
};
|
|
58
|
+
case "tool_use":
|
|
59
|
+
return {
|
|
60
|
+
type: "tool_use_start",
|
|
61
|
+
toolName: event.name,
|
|
62
|
+
input: event.input,
|
|
63
|
+
conversationId,
|
|
64
|
+
toolUseId: event.id,
|
|
65
|
+
};
|
|
66
|
+
case "tool_use_preview_start":
|
|
67
|
+
return {
|
|
68
|
+
type: "tool_use_preview_start",
|
|
69
|
+
toolUseId: event.toolUseId,
|
|
70
|
+
toolName: event.toolName,
|
|
71
|
+
conversationId,
|
|
72
|
+
};
|
|
73
|
+
case "tool_output_chunk":
|
|
74
|
+
return {
|
|
75
|
+
type: "tool_output_chunk",
|
|
76
|
+
chunk: event.chunk,
|
|
77
|
+
conversationId,
|
|
78
|
+
toolUseId: event.toolUseId,
|
|
79
|
+
};
|
|
80
|
+
case "tool_result": {
|
|
81
|
+
const imageBlocks = event.contentBlocks?.filter(
|
|
82
|
+
(b): b is Extract<typeof b, { type: "image" }> => b.type === "image",
|
|
83
|
+
);
|
|
84
|
+
const imageDataList = imageBlocks?.length
|
|
85
|
+
? imageBlocks.map((b) => b.source.data)
|
|
86
|
+
: undefined;
|
|
87
|
+
return {
|
|
88
|
+
type: "tool_result",
|
|
89
|
+
toolName: "",
|
|
90
|
+
result: event.content,
|
|
91
|
+
isError: event.isError,
|
|
92
|
+
diff: event.diff,
|
|
93
|
+
status: event.status,
|
|
94
|
+
conversationId,
|
|
95
|
+
imageData: imageDataList?.[0],
|
|
96
|
+
imageDataList,
|
|
97
|
+
toolUseId: event.toolUseId,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
case "server_tool_start":
|
|
101
|
+
return {
|
|
102
|
+
type: "tool_use_start",
|
|
103
|
+
toolName: event.name,
|
|
104
|
+
input: event.input,
|
|
105
|
+
conversationId,
|
|
106
|
+
toolUseId: event.toolUseId,
|
|
107
|
+
};
|
|
108
|
+
case "server_tool_complete": {
|
|
109
|
+
let resultText = "";
|
|
110
|
+
if (Array.isArray(event.content) && event.content.length > 0) {
|
|
111
|
+
resultText = (event.content as unknown[])
|
|
112
|
+
.filter(
|
|
113
|
+
(r): r is { type: string; title: string; url: string } =>
|
|
114
|
+
typeof r === "object" &&
|
|
115
|
+
r != null &&
|
|
116
|
+
(r as { type?: string }).type === "web_search_result",
|
|
117
|
+
)
|
|
118
|
+
.map((r) => `${r.title}\n${r.url}`)
|
|
119
|
+
.join("\n\n");
|
|
120
|
+
}
|
|
121
|
+
return {
|
|
122
|
+
type: "tool_result",
|
|
123
|
+
toolName: "web_search",
|
|
124
|
+
result: resultText,
|
|
125
|
+
isError: event.isError,
|
|
126
|
+
conversationId,
|
|
127
|
+
toolUseId: event.toolUseId,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
case "message_complete":
|
|
131
|
+
return {
|
|
132
|
+
type: "message_complete",
|
|
133
|
+
conversationId,
|
|
134
|
+
};
|
|
135
|
+
case "input_json_delta":
|
|
136
|
+
case "usage":
|
|
137
|
+
case "error":
|
|
138
|
+
case "provider_error":
|
|
139
|
+
case "max_tokens_reached":
|
|
140
|
+
case "context_compacting":
|
|
141
|
+
case "compaction_circuit_open":
|
|
142
|
+
case "compaction_circuit_closed":
|
|
143
|
+
case "compaction_completed":
|
|
144
|
+
case "history_stripped":
|
|
145
|
+
case "agent_loop_exit":
|
|
146
|
+
return null;
|
|
147
|
+
case "llm_call_started":
|
|
148
|
+
// The wake path persists its assistant tail via
|
|
149
|
+
// `persistWakeTailMessage` (an `addMessage`-shaped call) rather than
|
|
150
|
+
// the main event-handler's `reserve` → `updateContent` pipeline, so
|
|
151
|
+
// there is no row to reserve here. Translation returns null and the
|
|
152
|
+
// wake path's existing end-of-turn persist continues to mint the row.
|
|
153
|
+
return null;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Translate a raw agent event to its wire frame and broadcast it to
|
|
159
|
+
* connected clients. No-op for events with no client-visible shape.
|
|
160
|
+
*/
|
|
161
|
+
export function emitWakeAgentEvent(
|
|
162
|
+
conversation: Conversation,
|
|
163
|
+
event: AgentEvent,
|
|
164
|
+
): void {
|
|
165
|
+
const frame = translateAgentEventToServerMessage(
|
|
166
|
+
event,
|
|
167
|
+
conversation.conversationId,
|
|
168
|
+
);
|
|
169
|
+
if (!frame) return;
|
|
170
|
+
broadcastMessage(frame);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Emit the live ui_surface card announcing that a wake produced output.
|
|
175
|
+
* The `surfaceId` matches the `ui_surface` content block injected into
|
|
176
|
+
* the wake's first assistant tail message so the client renders one card,
|
|
177
|
+
* not two.
|
|
178
|
+
*/
|
|
179
|
+
export function broadcastWakeSurface(
|
|
180
|
+
conversation: Conversation,
|
|
181
|
+
source: string,
|
|
182
|
+
hint: string,
|
|
183
|
+
surfaceId: string,
|
|
184
|
+
): void {
|
|
185
|
+
broadcastMessage({
|
|
186
|
+
type: "ui_surface_show",
|
|
187
|
+
conversationId: conversation.conversationId,
|
|
188
|
+
surfaceId,
|
|
189
|
+
surfaceType: "card",
|
|
190
|
+
data: {
|
|
191
|
+
title: "Conversation Woke",
|
|
192
|
+
body: hint,
|
|
193
|
+
metadata: [{ label: "Source", value: source }],
|
|
194
|
+
},
|
|
195
|
+
display: "inline",
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Persist a single tail message produced by a wake (assistant outputs and
|
|
201
|
+
* intervening tool_result user messages). Builds the channel/interface
|
|
202
|
+
* metadata, persists via the canonical `addMessage` path, syncs the row to
|
|
203
|
+
* the disk view, and backfills the message id onto this turn's LLM request
|
|
204
|
+
* logs so wake-produced messages match the user-turn persistence path.
|
|
205
|
+
*/
|
|
206
|
+
export async function persistWakeTailMessage(
|
|
207
|
+
conversation: Conversation,
|
|
208
|
+
message: Message,
|
|
209
|
+
): Promise<void> {
|
|
210
|
+
const turnChannelCtx = conversation.getTurnChannelContext();
|
|
211
|
+
const turnInterfaceCtx = conversation.getTurnInterfaceContext();
|
|
212
|
+
const metadata: Record<string, unknown> = {
|
|
213
|
+
...provenanceFromTrustContext(conversation.trustContext),
|
|
214
|
+
userMessageChannel: turnChannelCtx?.userMessageChannel ?? "vellum",
|
|
215
|
+
assistantMessageChannel:
|
|
216
|
+
turnChannelCtx?.assistantMessageChannel ?? "vellum",
|
|
217
|
+
userMessageInterface: turnInterfaceCtx?.userMessageInterface ?? "web",
|
|
218
|
+
assistantMessageInterface:
|
|
219
|
+
turnInterfaceCtx?.assistantMessageInterface ?? "web",
|
|
220
|
+
};
|
|
221
|
+
const persisted = await addMessage(
|
|
222
|
+
conversation.conversationId,
|
|
223
|
+
message.role,
|
|
224
|
+
JSON.stringify(message.content),
|
|
225
|
+
{ metadata },
|
|
226
|
+
);
|
|
227
|
+
if (message.role === "assistant") {
|
|
228
|
+
try {
|
|
229
|
+
backfillMessageIdOnLogs(conversation.conversationId, persisted.id);
|
|
230
|
+
} catch (err) {
|
|
231
|
+
log.warn(
|
|
232
|
+
{ err, conversationId: conversation.conversationId },
|
|
233
|
+
"wake persist: backfill messageId on LLM logs failed (non-fatal)",
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
try {
|
|
238
|
+
const convRow = getConversation(conversation.conversationId);
|
|
239
|
+
if (convRow) {
|
|
240
|
+
syncMessageToDisk(
|
|
241
|
+
conversation.conversationId,
|
|
242
|
+
persisted.id,
|
|
243
|
+
convRow.createdAt,
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
} catch (err) {
|
|
247
|
+
log.warn(
|
|
248
|
+
{ err, conversationId: conversation.conversationId },
|
|
249
|
+
"wake persist: syncMessageToDisk failed (non-fatal)",
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Temporarily restrict the tools visible/executable during a wake by
|
|
256
|
+
* reusing the conversation's subagent allowlist slot. Returns a restore
|
|
257
|
+
* callback that reinstates the previous allowlist so the wake can release
|
|
258
|
+
* the scope before any queued user turn is drained.
|
|
259
|
+
*/
|
|
260
|
+
export function scopeWakeAllowedTools(
|
|
261
|
+
conversation: Conversation,
|
|
262
|
+
tools: ReadonlySet<string>,
|
|
263
|
+
): () => void {
|
|
264
|
+
const previous = conversation.subagentAllowedTools;
|
|
265
|
+
conversation.setSubagentAllowedTools(new Set(tools));
|
|
266
|
+
return () => {
|
|
267
|
+
conversation.setSubagentAllowedTools(previous);
|
|
268
|
+
};
|
|
269
|
+
}
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* means plugin-api ships as part of the assistant binary's regular code
|
|
9
9
|
* graph; in JIT/Docker, it loads from source.
|
|
10
10
|
*
|
|
11
|
-
* The loaded namespace is then installed on `globalThis` under a
|
|
11
|
+
* The loaded namespace is then installed on `globalThis` under a well-known
|
|
12
12
|
* symbol. The boot-time shim writer (`ensurePluginApiShim`) enumerates
|
|
13
13
|
* {@link PLUGIN_API_EXPORTS} and generates a tiny ESM module at
|
|
14
14
|
* `<workspaceDir>/node_modules/@vellumai/plugin-api/index.js` that
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
import * as pluginApi from "../plugin-api/index.js";
|
|
27
27
|
|
|
28
28
|
/** Symbol key under which the plugin-api namespace is published on globalThis. */
|
|
29
|
-
export const PLUGIN_API_REGISTRY_KEY = Symbol.for("vellum.plugin-api
|
|
29
|
+
export const PLUGIN_API_REGISTRY_KEY = Symbol.for("vellum.plugin-api");
|
|
30
30
|
|
|
31
31
|
// Install on globalThis once at module-load time. The shim writer reads
|
|
32
32
|
// `PLUGIN_API_EXPORTS` to know which bindings to re-export; the shim's
|
|
@@ -9,6 +9,7 @@ type TestMessage = {
|
|
|
9
9
|
content: string;
|
|
10
10
|
createdAt: number;
|
|
11
11
|
metadata: string | null;
|
|
12
|
+
clientMessageId: string | null;
|
|
12
13
|
};
|
|
13
14
|
|
|
14
15
|
const parentMessages: TestMessage[] = [
|
|
@@ -19,6 +20,7 @@ const parentMessages: TestMessage[] = [
|
|
|
19
20
|
content: JSON.stringify([{ type: "text", text: "go research foo" }]),
|
|
20
21
|
createdAt: 1_700_000_000_000,
|
|
21
22
|
metadata: null,
|
|
23
|
+
clientMessageId: null,
|
|
22
24
|
},
|
|
23
25
|
{
|
|
24
26
|
id: "msg-parent-2",
|
|
@@ -34,6 +36,7 @@ const parentMessages: TestMessage[] = [
|
|
|
34
36
|
conversationId: "child-conv-1",
|
|
35
37
|
},
|
|
36
38
|
}),
|
|
39
|
+
clientMessageId: null,
|
|
37
40
|
},
|
|
38
41
|
];
|
|
39
42
|
|
|
@@ -47,6 +50,7 @@ const childMessages: TestMessage[] = [
|
|
|
47
50
|
]),
|
|
48
51
|
createdAt: 1_700_000_002_000,
|
|
49
52
|
metadata: null,
|
|
53
|
+
clientMessageId: null,
|
|
50
54
|
},
|
|
51
55
|
{
|
|
52
56
|
id: "msg-child-2",
|
|
@@ -57,6 +61,7 @@ const childMessages: TestMessage[] = [
|
|
|
57
61
|
]),
|
|
58
62
|
createdAt: 1_700_000_003_000,
|
|
59
63
|
metadata: null,
|
|
64
|
+
clientMessageId: null,
|
|
60
65
|
},
|
|
61
66
|
];
|
|
62
67
|
|
|
@@ -85,6 +85,7 @@ const stubConfig: {
|
|
|
85
85
|
activeHoursStart: number | null;
|
|
86
86
|
activeHoursEnd: number | null;
|
|
87
87
|
maxConsecutiveRuns: number | null;
|
|
88
|
+
maxDailyRuns: number | null;
|
|
88
89
|
disposition: string;
|
|
89
90
|
};
|
|
90
91
|
} = {
|
|
@@ -94,6 +95,7 @@ const stubConfig: {
|
|
|
94
95
|
activeHoursStart: null,
|
|
95
96
|
activeHoursEnd: null,
|
|
96
97
|
maxConsecutiveRuns: null,
|
|
98
|
+
maxDailyRuns: null,
|
|
97
99
|
disposition: "Default disposition text.",
|
|
98
100
|
},
|
|
99
101
|
};
|
|
@@ -195,6 +197,7 @@ mock.module("../heartbeat-run-store.js", () => ({
|
|
|
195
197
|
markStaleRunsAsMissed: () => 0,
|
|
196
198
|
markStaleRunningAsError: () => 0,
|
|
197
199
|
countCompletedHeartbeatRuns: () => 10,
|
|
200
|
+
countCompletedRunsToday: () => 0,
|
|
198
201
|
countRecentConsecutiveRuns: () => 0,
|
|
199
202
|
}));
|
|
200
203
|
|
|
@@ -24,7 +24,8 @@ export type HeartbeatSkipReason =
|
|
|
24
24
|
| "outside_active_hours"
|
|
25
25
|
| "overlap"
|
|
26
26
|
| "pre_first_user_message"
|
|
27
|
-
| "max_consecutive_runs"
|
|
27
|
+
| "max_consecutive_runs"
|
|
28
|
+
| "max_daily_runs";
|
|
28
29
|
|
|
29
30
|
export interface HeartbeatRunRecord {
|
|
30
31
|
id: string;
|
|
@@ -217,6 +218,27 @@ export function countCompletedHeartbeatRuns(): number {
|
|
|
217
218
|
return row?.count ?? 0;
|
|
218
219
|
}
|
|
219
220
|
|
|
221
|
+
/**
|
|
222
|
+
* Count heartbeat runs that completed with status `ok` today (local midnight).
|
|
223
|
+
*/
|
|
224
|
+
export function countCompletedRunsToday(): number {
|
|
225
|
+
const db = getDb();
|
|
226
|
+
const now = new Date();
|
|
227
|
+
const startOfDay = new Date(
|
|
228
|
+
now.getFullYear(),
|
|
229
|
+
now.getMonth(),
|
|
230
|
+
now.getDate(),
|
|
231
|
+
).getTime();
|
|
232
|
+
const row = db
|
|
233
|
+
.select({ count: sql<number>`count(*)` })
|
|
234
|
+
.from(heartbeatRuns)
|
|
235
|
+
.where(
|
|
236
|
+
sql`${heartbeatRuns.status} = 'ok' AND ${heartbeatRuns.scheduledFor} >= ${startOfDay}`,
|
|
237
|
+
)
|
|
238
|
+
.get();
|
|
239
|
+
return row?.count ?? 0;
|
|
240
|
+
}
|
|
241
|
+
|
|
220
242
|
/**
|
|
221
243
|
* Count the most recent consecutive heartbeat runs with status `ok`,
|
|
222
244
|
* walking backwards from newest. Stops at the first non-`ok` status
|
|
@@ -25,6 +25,7 @@ import { stripCommentLines } from "../util/strip-comment-lines.js";
|
|
|
25
25
|
import {
|
|
26
26
|
completeHeartbeatRun,
|
|
27
27
|
countCompletedHeartbeatRuns,
|
|
28
|
+
countCompletedRunsToday,
|
|
28
29
|
countRecentConsecutiveRuns,
|
|
29
30
|
insertPendingHeartbeatRun,
|
|
30
31
|
markStaleRunningAsError,
|
|
@@ -187,6 +188,13 @@ export class HeartbeatService {
|
|
|
187
188
|
);
|
|
188
189
|
}
|
|
189
190
|
|
|
191
|
+
/** Whether the daily run cap has been reached. */
|
|
192
|
+
get isDailyCapReached(): boolean {
|
|
193
|
+
const config = getConfig().heartbeat;
|
|
194
|
+
if (config.maxDailyRuns == null) return false;
|
|
195
|
+
return countCompletedRunsToday() >= config.maxDailyRuns;
|
|
196
|
+
}
|
|
197
|
+
|
|
190
198
|
async runManagedWakeIfDue(
|
|
191
199
|
options: ManagedWakeHeartbeatRunOptions = {},
|
|
192
200
|
): Promise<ManagedWakeHeartbeatRunResult> {
|
|
@@ -551,6 +559,24 @@ export class HeartbeatService {
|
|
|
551
559
|
return false;
|
|
552
560
|
}
|
|
553
561
|
|
|
562
|
+
// Daily run cap — stop burning tokens when the daily budget is exhausted.
|
|
563
|
+
// Force runs bypass the cap.
|
|
564
|
+
if (
|
|
565
|
+
!force &&
|
|
566
|
+
config.maxDailyRuns != null &&
|
|
567
|
+
countCompletedRunsToday() >= config.maxDailyRuns
|
|
568
|
+
) {
|
|
569
|
+
log.debug(
|
|
570
|
+
{ maxDailyRuns: config.maxDailyRuns },
|
|
571
|
+
"Daily run cap reached, skipping",
|
|
572
|
+
);
|
|
573
|
+
if (runId) skipHeartbeatRun(runId, "max_daily_runs");
|
|
574
|
+
if (!this.cronMode) {
|
|
575
|
+
this.scheduleNextRun(config.intervalMs);
|
|
576
|
+
}
|
|
577
|
+
return false;
|
|
578
|
+
}
|
|
579
|
+
|
|
554
580
|
// Overlap prevention
|
|
555
581
|
if (this.activeRun) {
|
|
556
582
|
log.debug("Previous heartbeat run still active, skipping");
|
|
@@ -55,7 +55,7 @@ mock.module("../../browser/operations.js", () => ({
|
|
|
55
55
|
},
|
|
56
56
|
}));
|
|
57
57
|
|
|
58
|
-
mock.module("../../daemon/conversation-
|
|
58
|
+
mock.module("../../daemon/conversation-registry.js", () => ({
|
|
59
59
|
findConversation: (conversationId: string) => {
|
|
60
60
|
mockFindConversationCalls.push(conversationId);
|
|
61
61
|
return mockConversation ?? undefined;
|
|
@@ -32,9 +32,9 @@ let mockSurfaceResult: InteractiveUiResult = {
|
|
|
32
32
|
let mockSurfaceThrows: Error | null = null;
|
|
33
33
|
|
|
34
34
|
// Re-export the real module and override only findConversation.
|
|
35
|
-
const
|
|
36
|
-
mock.module("../../daemon/conversation-
|
|
37
|
-
...
|
|
35
|
+
const realRegistry = await import("../../daemon/conversation-registry.js");
|
|
36
|
+
mock.module("../../daemon/conversation-registry.js", () => ({
|
|
37
|
+
...realRegistry,
|
|
38
38
|
findConversation: (_conversationId: string) => mockConversation ?? undefined,
|
|
39
39
|
}));
|
|
40
40
|
|
|
@@ -137,6 +137,21 @@ describe("host.memory.addMessage", () => {
|
|
|
137
137
|
}),
|
|
138
138
|
).rejects.toThrow();
|
|
139
139
|
});
|
|
140
|
+
|
|
141
|
+
test("rejects the non-renderable system role", async () => {
|
|
142
|
+
// GIVEN the messages store is UI-facing (ConversationMessage), so only
|
|
143
|
+
// renderable turns may be persisted via this facet
|
|
144
|
+
// WHEN a skill attempts to add a system row
|
|
145
|
+
// THEN the route rejects it instead of persisting agent-context scaffolding
|
|
146
|
+
await expect(
|
|
147
|
+
memoryAddMessageRoute.handler({
|
|
148
|
+
conversationId: "c",
|
|
149
|
+
role: "system",
|
|
150
|
+
content: "agent-context scaffolding",
|
|
151
|
+
}),
|
|
152
|
+
).rejects.toThrow();
|
|
153
|
+
expect(addMessageSpy).not.toHaveBeenCalled();
|
|
154
|
+
});
|
|
140
155
|
});
|
|
141
156
|
|
|
142
157
|
describe("host.memory.wakeAgentForOpportunity", () => {
|
|
@@ -17,7 +17,9 @@ import type { SkillIpcRoute } from "../skill-ipc-types.js";
|
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* IPC params for `addMessage()`. `role` is constrained to the
|
|
20
|
-
*
|
|
20
|
+
* renderable conversation roles (`user`/`assistant`): the messages store
|
|
21
|
+
* is UI-facing (`ConversationMessage`), so agent-context `system` rows are
|
|
22
|
+
* not accepted here. `metadata` is a free-form record (validated
|
|
21
23
|
* downstream by `messageMetadataSchema` with a warn-and-store fallback).
|
|
22
24
|
* `skipIndexing` and `clientMessageId` mirror `AddMessageOptions`.
|
|
23
25
|
*
|
|
@@ -28,7 +30,7 @@ import type { SkillIpcRoute } from "../skill-ipc-types.js";
|
|
|
28
30
|
*/
|
|
29
31
|
const MemoryAddMessageParams = z.object({
|
|
30
32
|
conversationId: z.string().min(1),
|
|
31
|
-
role: z.enum(["user", "assistant"
|
|
33
|
+
role: z.enum(["user", "assistant"]),
|
|
32
34
|
content: z.string(),
|
|
33
35
|
metadata: z.record(z.string(), z.unknown()).optional(),
|
|
34
36
|
skipIndexing: z.boolean().optional(),
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
mock.module("../../util/logger.js", () => ({
|
|
4
|
+
getLogger: () =>
|
|
5
|
+
new Proxy({} as Record<string, unknown>, {
|
|
6
|
+
get: () => () => {},
|
|
7
|
+
}),
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
isActivationSession,
|
|
12
|
+
markActivationSession,
|
|
13
|
+
} from "../activation-session-store.js";
|
|
14
|
+
import { getDb } from "../db-connection.js";
|
|
15
|
+
import { initializeDb } from "../db-init.js";
|
|
16
|
+
import { activationSessions } from "../schema.js";
|
|
17
|
+
|
|
18
|
+
initializeDb();
|
|
19
|
+
|
|
20
|
+
describe("activation-session-store", () => {
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
getDb().delete(activationSessions).run();
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("marks a conversation and reports it as an activation session", () => {
|
|
26
|
+
markActivationSession("c1");
|
|
27
|
+
expect(isActivationSession("c1")).toBe(true);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
test("unmarked conversations are not activation sessions", () => {
|
|
31
|
+
expect(isActivationSession("nope")).toBe(false);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("marking the same conversation twice is idempotent", () => {
|
|
35
|
+
markActivationSession("c1");
|
|
36
|
+
markActivationSession("c1");
|
|
37
|
+
expect(isActivationSession("c1")).toBe(true);
|
|
38
|
+
const rows = getDb().select().from(activationSessions).all();
|
|
39
|
+
expect(rows.length).toBe(1);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
@@ -151,6 +151,7 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
|
|
|
151
151
|
|
|
152
152
|
test("enqueues consolidate when v2 is on and no checkpoint exists", () => {
|
|
153
153
|
const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
|
|
154
|
+
writeBuffer(15);
|
|
154
155
|
|
|
155
156
|
maybeEnqueueGraphMaintenanceJobs(config, Date.now());
|
|
156
157
|
|
|
@@ -177,6 +178,7 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
|
|
|
177
178
|
|
|
178
179
|
test("enqueues consolidate again once the interval elapses", () => {
|
|
179
180
|
const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
|
|
181
|
+
writeBuffer(15);
|
|
180
182
|
|
|
181
183
|
const now = Date.now();
|
|
182
184
|
// Stamp checkpoint to >1h ago.
|
|
@@ -193,6 +195,7 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
|
|
|
193
195
|
|
|
194
196
|
test("respects a custom consolidation_interval_hours value", () => {
|
|
195
197
|
const config = buildConfig({ v2Enabled: true, intervalHours: 6 });
|
|
198
|
+
writeBuffer(15);
|
|
196
199
|
|
|
197
200
|
const now = Date.now();
|
|
198
201
|
// 4h elapsed — under the configured 6h interval.
|
|
@@ -216,6 +219,7 @@ describe("maybeEnqueueGraphMaintenanceJobs — memory v2 consolidation", () => {
|
|
|
216
219
|
|
|
217
220
|
test("v1 maintenance entries are suppressed when v2 is active", () => {
|
|
218
221
|
const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
|
|
222
|
+
writeBuffer(15);
|
|
219
223
|
|
|
220
224
|
// No checkpoints set — every entry would be due if it were scheduled.
|
|
221
225
|
deleteMemoryCheckpoint("graph_maintenance:decay:last_run");
|
|
@@ -426,3 +430,86 @@ describe("maybeEnqueueGraphMaintenanceJobs — buffer-size trigger", () => {
|
|
|
426
430
|
expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
|
|
427
431
|
});
|
|
428
432
|
});
|
|
433
|
+
|
|
434
|
+
describe("maybeEnqueueGraphMaintenanceJobs — min buffer lines noop", () => {
|
|
435
|
+
test("skips scheduled consolidation when buffer is under 10 lines", () => {
|
|
436
|
+
// GIVEN v2 consolidation is enabled and the interval has elapsed
|
|
437
|
+
const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
|
|
438
|
+
const now = Date.now();
|
|
439
|
+
setMemoryCheckpoint(
|
|
440
|
+
CONSOLIDATE_CHECKPOINT_KEY,
|
|
441
|
+
String(now - 2 * 60 * 60 * 1000),
|
|
442
|
+
);
|
|
443
|
+
// AND the buffer has fewer than 10 lines
|
|
444
|
+
writeBuffer(5);
|
|
445
|
+
|
|
446
|
+
// WHEN the schedule runs
|
|
447
|
+
maybeEnqueueGraphMaintenanceJobs(config, now);
|
|
448
|
+
|
|
449
|
+
// THEN no consolidation job is enqueued
|
|
450
|
+
expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
|
|
451
|
+
// AND the checkpoint advances so the skip doesn't re-fire next tick
|
|
452
|
+
expect(getMemoryCheckpoint(CONSOLIDATE_CHECKPOINT_KEY)).toBe(String(now));
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
test("allows scheduled consolidation when buffer has exactly 10 lines", () => {
|
|
456
|
+
// GIVEN v2 consolidation is enabled and the interval has elapsed
|
|
457
|
+
const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
|
|
458
|
+
const now = Date.now();
|
|
459
|
+
setMemoryCheckpoint(
|
|
460
|
+
CONSOLIDATE_CHECKPOINT_KEY,
|
|
461
|
+
String(now - 2 * 60 * 60 * 1000),
|
|
462
|
+
);
|
|
463
|
+
// AND the buffer has exactly 10 lines
|
|
464
|
+
writeBuffer(10);
|
|
465
|
+
|
|
466
|
+
// WHEN the schedule runs
|
|
467
|
+
maybeEnqueueGraphMaintenanceJobs(config, now);
|
|
468
|
+
|
|
469
|
+
// THEN consolidation is enqueued
|
|
470
|
+
expect(countPendingJobs("memory_v2_consolidate")).toBe(1);
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
test("skips scheduled consolidation when buffer file is missing", () => {
|
|
474
|
+
// GIVEN v2 consolidation is enabled and the interval has elapsed
|
|
475
|
+
const config = buildConfig({ v2Enabled: true, intervalHours: 1 });
|
|
476
|
+
const now = Date.now();
|
|
477
|
+
setMemoryCheckpoint(
|
|
478
|
+
CONSOLIDATE_CHECKPOINT_KEY,
|
|
479
|
+
String(now - 2 * 60 * 60 * 1000),
|
|
480
|
+
);
|
|
481
|
+
// AND no buffer file exists (0 lines)
|
|
482
|
+
removeBuffer();
|
|
483
|
+
|
|
484
|
+
// WHEN the schedule runs
|
|
485
|
+
maybeEnqueueGraphMaintenanceJobs(config, now);
|
|
486
|
+
|
|
487
|
+
// THEN no consolidation job is enqueued
|
|
488
|
+
expect(countPendingJobs("memory_v2_consolidate")).toBe(0);
|
|
489
|
+
// AND the checkpoint advances
|
|
490
|
+
expect(getMemoryCheckpoint(CONSOLIDATE_CHECKPOINT_KEY)).toBe(String(now));
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
test("size-based trigger is independent of the min-lines noop", () => {
|
|
494
|
+
// GIVEN the time-based interval has elapsed but the buffer has fewer
|
|
495
|
+
// than 10 lines, while exceeding the configured maxBufferLines threshold
|
|
496
|
+
const config = buildConfig({
|
|
497
|
+
v2Enabled: true,
|
|
498
|
+
intervalHours: 1,
|
|
499
|
+
maxBufferLines: 5,
|
|
500
|
+
});
|
|
501
|
+
const now = Date.now();
|
|
502
|
+
setMemoryCheckpoint(
|
|
503
|
+
CONSOLIDATE_CHECKPOINT_KEY,
|
|
504
|
+
String(now - 2 * 60 * 60 * 1000),
|
|
505
|
+
);
|
|
506
|
+
writeBuffer(8);
|
|
507
|
+
|
|
508
|
+
// WHEN the schedule runs
|
|
509
|
+
maybeEnqueueGraphMaintenanceJobs(config, now);
|
|
510
|
+
|
|
511
|
+
// THEN consolidation is enqueued via the size trigger despite the
|
|
512
|
+
// time-based schedule being nooped (8 < 10 min lines, but 8 >= 5 max)
|
|
513
|
+
expect(countPendingJobs("memory_v2_consolidate")).toBe(1);
|
|
514
|
+
});
|
|
515
|
+
});
|