@vellumai/assistant 0.8.8 → 0.8.9-staging.2
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/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 +235 -6
- package/package.json +2 -2
- 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 +1 -1
- 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 +5 -4
- package/src/__tests__/approval-routes-http.test.ts +4 -1
- package/src/__tests__/background-workers-disk-pressure.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__/compaction-circuit.test.ts +258 -0
- package/src/__tests__/compaction-direct.test.ts +132 -0
- package/src/__tests__/compaction-events.test.ts +5 -5
- package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
- package/src/__tests__/context-overflow-reducer.test.ts +1 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +6 -4
- 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 +142 -218
- package/src/__tests__/conversation-agent-loop.test.ts +297 -586
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +5 -4
- package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -4
- package/src/__tests__/conversation-process-callsite.test.ts +14 -14
- package/src/__tests__/conversation-provider-retry-repair.test.ts +70 -65
- package/src/__tests__/conversation-queue.test.ts +9 -9
- package/src/__tests__/conversation-runtime-assembly.test.ts +923 -231
- package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
- package/src/__tests__/conversation-slash-queue.test.ts +6 -4
- package/src/__tests__/conversation-slash-unknown.test.ts +6 -4
- package/src/__tests__/conversation-speed-override.test.ts +10 -9
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-workspace-cache-state.test.ts +23 -20
- package/src/__tests__/conversation-workspace-injection.test.ts +68 -6
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +14 -11
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/credential-security-invariants.test.ts +0 -1
- package/src/__tests__/db-acp-history.test.ts +101 -0
- package/src/__tests__/dynamic-page-surface.test.ts +31 -0
- 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 +12 -2
- 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__/history-repair-hook.test.ts +1 -1
- 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__/memory-retrieval-hook.test.ts +131 -26
- package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
- package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
- package/src/__tests__/plugin-api-shim.test.ts +6 -9
- package/src/__tests__/plugin-bootstrap.test.ts +12 -23
- package/src/__tests__/plugin-registry.test.ts +3 -49
- package/src/__tests__/plugin-types.test.ts +0 -70
- package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
- package/src/__tests__/reaction-persistence.test.ts +1 -1
- package/src/__tests__/send-endpoint-busy.test.ts +4 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +33 -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 +1 -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__/user-plugin-loader.test.ts +54 -286
- 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 +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/feature-gate.test.ts +48 -0
- package/src/acp/feature-gate.ts +34 -0
- package/src/acp/prepare-agent-env.ts +80 -27
- package/src/acp/resolve-agent.test.ts +225 -9
- package/src/acp/resolve-agent.ts +122 -17
- 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 +414 -248
- package/src/api/responses/conversation-message.ts +14 -1
- package/src/approvals/guardian-request-resolvers.ts +1 -1
- package/src/background-wake/next-wake.ts +1 -0
- package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
- package/src/cli/commands/plugins.ts +43 -37
- package/src/cli/lib/__tests__/install-from-github.test.ts +429 -111
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
- package/src/cli/lib/__tests__/plugin-details.test.ts +372 -0
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +220 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +226 -32
- package/src/cli/lib/install-from-github.ts +464 -55
- package/src/cli/lib/plugin-catalog-cache.ts +84 -0
- package/src/cli/lib/plugin-details.ts +409 -0
- package/src/cli/lib/plugin-marketplace.ts +197 -0
- package/src/cli/lib/search-plugins.ts +195 -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 +6 -0
- package/src/config/bundled-skills/acp/SKILL.md +85 -33
- 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 -2
- package/src/config/feature-flag-registry.json +15 -6
- package/src/config/schemas/call-site-catalog.ts +0 -14
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +0 -2
- package/src/context/compactor.ts +22 -4
- package/src/context/strip-injections.ts +8 -2
- package/src/context/window-manager.ts +27 -13
- package/src/daemon/conversation-agent-loop-handlers.ts +10 -35
- package/src/daemon/conversation-agent-loop.ts +175 -1055
- package/src/daemon/conversation-lifecycle.ts +11 -255
- package/src/daemon/conversation-process.ts +8 -136
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +293 -392
- package/src/daemon/conversation-store.ts +9 -90
- package/src/daemon/conversation-surfaces.ts +24 -8
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +404 -57
- package/src/daemon/disk-pressure-policy.ts +0 -1
- package/src/daemon/external-plugins-bootstrap.ts +14 -19
- package/src/daemon/handlers/conversations.ts +3 -1
- package/src/daemon/handlers/skills.ts +4 -1
- package/src/daemon/host-proxy-preactivation.ts +1 -3
- package/src/daemon/lifecycle.ts +21 -7
- package/src/daemon/server.ts +2 -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__/jobs-worker-v2-schedule.test.ts +87 -0
- package/src/memory/conversation-crud.ts +29 -19
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/db-init.ts +2 -0
- 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/index.ts +1 -0
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +3 -3
- package/src/memory/v2/consolidation-job.ts +13 -4
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +6 -5
- package/src/plugin-api/types.ts +75 -0
- package/src/plugins/defaults/compaction/compact.ts +59 -0
- package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +7 -7
- package/src/plugins/defaults/compaction/manager-store.ts +57 -0
- package/src/plugins/defaults/compaction/package.json +1 -2
- 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 +135 -26
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +184 -74
- 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/unified-turn-context.ts +223 -0
- 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__/live-integration.test.ts +9 -6
- 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 +8 -5
- 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 +10 -5
- 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 +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +16 -17
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +9 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +5 -2
- 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/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +15 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/provider-blocks.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +7 -3
- 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 +5 -5
- 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 +7 -7
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +32 -94
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +1 -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/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/templates/BOOTSTRAP-ACTIVATION-RAIL.md +35 -3
- 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 +315 -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__/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 +89 -25
- package/src/runtime/routes/acp-routes.ts +81 -29
- 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/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 +28 -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/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 +16 -6
- package/src/runtime/routes/playground/helpers.ts +1 -1
- package/src/runtime/routes/plugins-routes.ts +337 -35
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/work-items-routes.ts +2 -4
- package/src/runtime/services/conversation-serializer.ts +1 -1
- package/src/signals/cancel.ts +2 -4
- package/src/subagent/manager.ts +21 -5
- package/src/tools/acp/context.ts +20 -0
- package/src/tools/acp/list-agents.test.ts +8 -2
- package/src/tools/acp/spawn.test.ts +176 -195
- 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/filesystem/write.ts +34 -0
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/ui-surface/definitions.ts +25 -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/__tests__/plugin-external-api.test.ts +0 -68
- package/src/daemon/wake-target-adapter.ts +0 -253
- 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/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/plugins/external-api.ts +0 -104
- 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/{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__/health.test.ts +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__/provider-blocks.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__/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}/health.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/llm-retry.ts +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
|
@@ -2,21 +2,22 @@
|
|
|
2
2
|
* Plugin bootstrap — runs every registered plugin's `init()` hook once during
|
|
3
3
|
* daemon startup.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* The registry is populated before this runs: first-party defaults are
|
|
6
|
+
* registered explicitly (step 1 below), user plugins are registered by the
|
|
7
|
+
* user-plugin loader from the workspace `plugins/` directory, and hot-reload
|
|
8
|
+
* registers at runtime. By the time {@link bootstrapPlugins} runs, the
|
|
9
|
+
* registry has been fully populated for this boot cycle. This function:
|
|
9
10
|
*
|
|
10
11
|
* 1. Registers the canonical first-party default plugins via
|
|
11
|
-
* {@link registerDefaultPlugins}
|
|
12
|
-
*
|
|
12
|
+
* {@link registerDefaultPlugins}. Registration is idempotent so repeat
|
|
13
|
+
* calls (e.g. during integration tests) do not throw.
|
|
13
14
|
* 2. Walks {@link getRegisteredPlugins} in registration order.
|
|
14
15
|
* 3. For each plugin, consults `manifest.requiresFlag` against
|
|
15
16
|
* {@link isAssistantFeatureFlagEnabled}. If any listed flag is disabled,
|
|
16
17
|
* the plugin is skipped wholesale — no `init()`, no tool/route/skill
|
|
17
18
|
* contributions, no entry in the shutdown hook, and the plugin is also
|
|
18
|
-
* dropped from the registry via {@link unregisterPlugin} so its
|
|
19
|
-
*
|
|
19
|
+
* dropped from the registry via {@link unregisterPlugin} so none of its
|
|
20
|
+
* hooks participate in the turn lifecycle. This is the primary mechanism for
|
|
20
21
|
* shipping experimental plugins behind a feature flag.
|
|
21
22
|
* 4. Resolves the plugin's `manifest.requiresCredential` entries via the
|
|
22
23
|
* credential store helper ({@link getSecureKeyAsync}). In Docker mode
|
|
@@ -64,7 +65,6 @@ import { getConfig } from "../config/loader.js";
|
|
|
64
65
|
import type { AssistantConfig } from "../config/schema.js";
|
|
65
66
|
import { HOOKS } from "../plugin-api/constants.js";
|
|
66
67
|
import { registerDefaultPlugins } from "../plugins/defaults/index.js";
|
|
67
|
-
import { installPluginRuntime } from "../plugins/external-api.js";
|
|
68
68
|
import { buildExternalPlugin } from "../plugins/external-plugin-loader.js";
|
|
69
69
|
import {
|
|
70
70
|
registerPluginSkills,
|
|
@@ -186,21 +186,17 @@ function getDisabledPluginFlag(
|
|
|
186
186
|
* Bring the plugin layer up during daemon startup. Runs the full sequence in
|
|
187
187
|
* the one order the rest of the system depends on:
|
|
188
188
|
*
|
|
189
|
-
* 1.
|
|
190
|
-
* it from their module body (see `plugins/external-api.ts` — compiled-binary
|
|
191
|
-
* module identity).
|
|
192
|
-
* 2. Register the first-party defaults so their middleware composes innermost,
|
|
189
|
+
* 1. Register the first-party defaults so their middleware composes innermost,
|
|
193
190
|
* ahead of any user plugins.
|
|
194
|
-
*
|
|
191
|
+
* 2. Load user plugins from `<workspaceDir>/plugins/*`. A failing user plugin is
|
|
195
192
|
* logged and skipped; `loadUserPlugins()` closes the registration window
|
|
196
193
|
* when it returns, so the defaults must already be registered by then.
|
|
197
|
-
*
|
|
194
|
+
* 3. Run every registered plugin's `init()` via {@link bootstrapPlugins}.
|
|
198
195
|
*
|
|
199
196
|
* Plugin bootstrap is wrapped so a failing plugin cannot block daemon startup —
|
|
200
197
|
* the daemon comes up with degraded plugin functionality instead.
|
|
201
198
|
*/
|
|
202
199
|
export async function initializePlugins(): Promise<void> {
|
|
203
|
-
installPluginRuntime();
|
|
204
200
|
registerDefaultPlugins();
|
|
205
201
|
await loadUserPlugins();
|
|
206
202
|
try {
|
|
@@ -261,9 +257,8 @@ export async function bootstrapPlugins(): Promise<void> {
|
|
|
261
257
|
// Tear down a plugin's contributions AND remove it from the registry. The
|
|
262
258
|
// two steps always move together on the bootstrap failure path: the former
|
|
263
259
|
// clears tools/routes/skills (so they stop appearing to the model/HTTP
|
|
264
|
-
// server), the latter drops the plugin's entry from the Map (so
|
|
265
|
-
//
|
|
266
|
-
// pipeline invocation).
|
|
260
|
+
// server), the latter drops the plugin's entry from the Map (so the registry
|
|
261
|
+
// doesn't expose an uninitialized plugin's hooks).
|
|
267
262
|
// Shutdown context is identical for every plugin in this boot — construct
|
|
268
263
|
// once and reuse across the bootstrap-failure rollback and the normal
|
|
269
264
|
// shutdown hook below. Only `assistantVersion` is exposed today; future
|
|
@@ -8,9 +8,11 @@ import { createAbortReason } from "../../util/abort-reasons.js";
|
|
|
8
8
|
import { truncate } from "../../util/truncate.js";
|
|
9
9
|
import { regenerate } from "../conversation-history.js";
|
|
10
10
|
import {
|
|
11
|
-
clearAllActiveConversations,
|
|
12
11
|
conversationEntries,
|
|
13
12
|
findConversation,
|
|
13
|
+
} from "../conversation-registry.js";
|
|
14
|
+
import {
|
|
15
|
+
clearAllActiveConversations,
|
|
14
16
|
getOrCreateConversation,
|
|
15
17
|
touchConversation,
|
|
16
18
|
} from "../conversation-store.js";
|
|
@@ -31,7 +31,10 @@ import {
|
|
|
31
31
|
isTextMimeType as isTextMime,
|
|
32
32
|
MAX_INLINE_TEXT_SIZE,
|
|
33
33
|
} from "../../runtime/routes/workspace-utils.js";
|
|
34
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
getCachedCatalogSync,
|
|
36
|
+
getCatalog,
|
|
37
|
+
} from "../../skills/catalog-cache.js";
|
|
35
38
|
import type { SkillFileEntry } from "../../skills/catalog-files.js";
|
|
36
39
|
import {
|
|
37
40
|
catalogSkillToSlim,
|
|
@@ -34,9 +34,7 @@ import { getLogger } from "../util/logger.js";
|
|
|
34
34
|
const log = getLogger("host-proxy-preactivation");
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
|
-
* Subset of Conversation
|
|
38
|
-
* `preactivateHostProxySkills` needs. Both `Conversation` and
|
|
39
|
-
* `ProcessConversationContext` satisfy this structurally.
|
|
37
|
+
* Subset of `Conversation` that `preactivateHostProxySkills` needs.
|
|
40
38
|
*/
|
|
41
39
|
export interface HostProxyPreactivationTarget {
|
|
42
40
|
readonly conversationId: string;
|
package/src/daemon/lifecycle.ts
CHANGED
|
@@ -60,7 +60,6 @@ import { sweepConceptPageFrontmatter } from "../memory/v2/frontmatter-sweep.js";
|
|
|
60
60
|
import { emitNotificationSignal } from "../notifications/emit-signal.js";
|
|
61
61
|
import { backfillManualTokenConnections } from "../oauth/manual-token-connection.js";
|
|
62
62
|
import { seedOAuthProviders } from "../oauth/seed-providers.js";
|
|
63
|
-
import { backfillGuardIfNeeded } from "../proactive-artifact/index.js";
|
|
64
63
|
import { ensurePromptFiles } from "../prompts/system-prompt.js";
|
|
65
64
|
import { runProviderConnectionsBackfill } from "../providers/inference/backfill.js";
|
|
66
65
|
import { resolveManagedProxyContext } from "../providers/platform-proxy/context.js";
|
|
@@ -98,6 +97,7 @@ import {
|
|
|
98
97
|
updateWorkItem,
|
|
99
98
|
} from "../work-items/work-item-store.js";
|
|
100
99
|
import { WorkspaceHeartbeatService } from "../workspace/heartbeat-service.js";
|
|
100
|
+
import { repairAdaptiveThinkingOnManagedProfiles } from "../workspace/migrations/097-enable-adaptive-thinking-managed-profiles.js";
|
|
101
101
|
import { WORKSPACE_MIGRATIONS } from "../workspace/migrations/registry.js";
|
|
102
102
|
import { runWorkspaceMigrations } from "../workspace/migrations/runner.js";
|
|
103
103
|
import {
|
|
@@ -589,6 +589,26 @@ export async function runDaemon(): Promise<void> {
|
|
|
589
589
|
);
|
|
590
590
|
}
|
|
591
591
|
|
|
592
|
+
// Re-run the adaptive thinking repair after overlay merge + profile seeding.
|
|
593
|
+
// Workspace migration 097 enables adaptive thinking on managed profiles, but
|
|
594
|
+
// it runs before mergeDefaultWorkspaceConfig() which can overwrite the fix
|
|
595
|
+
// with overlay profiles that have thinking disabled or absent. On-platform
|
|
596
|
+
// instances where the overlay supplies "balanced" / "quality-optimized"
|
|
597
|
+
// profiles without thinking enabled would be stuck permanently because the
|
|
598
|
+
// migration is already checkpointed as completed. This idempotent repair
|
|
599
|
+
// ensures thinking is enabled regardless of overlay ordering.
|
|
600
|
+
if (defaultConfigMerge.hadOverlay) {
|
|
601
|
+
try {
|
|
602
|
+
repairAdaptiveThinkingOnManagedProfiles(getWorkspaceDir());
|
|
603
|
+
log.info("Post-overlay adaptive thinking repair complete");
|
|
604
|
+
} catch (err) {
|
|
605
|
+
log.warn(
|
|
606
|
+
{ err },
|
|
607
|
+
"Post-overlay adaptive thinking repair failed — continuing startup",
|
|
608
|
+
);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
592
612
|
log.info("Daemon startup: loading config");
|
|
593
613
|
const config = loadConfig();
|
|
594
614
|
|
|
@@ -1276,12 +1296,6 @@ export async function runDaemon(): Promise<void> {
|
|
|
1276
1296
|
"Heartbeat service configured",
|
|
1277
1297
|
);
|
|
1278
1298
|
|
|
1279
|
-
try {
|
|
1280
|
-
backfillGuardIfNeeded();
|
|
1281
|
-
} catch (err) {
|
|
1282
|
-
log.warn({ err }, "Proactive artifact backfill failed");
|
|
1283
|
-
}
|
|
1284
|
-
|
|
1285
1299
|
// Filing yields to the memory v2 consolidation job when v2 is enabled —
|
|
1286
1300
|
// both serve the same role (periodic background memory processing) and
|
|
1287
1301
|
// running both is redundant. The consolidation job runs through the
|
package/src/daemon/server.ts
CHANGED
|
@@ -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", () => {
|