@vellumai/assistant 0.8.8 → 0.8.9-dev.202606091853.fbaa2ae
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 +263 -121
- package/package.json +2 -2
- 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__/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__/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-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__/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-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__/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__/send-endpoint-busy.test.ts +4 -1
- 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__/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/responses/conversation-message.ts +22 -2
- 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 -16
- package/src/config/llm-resolver.ts +39 -7
- 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 +18 -36
- 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/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/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 +34 -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 +22 -0
- 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
package/src/memory/v3/tree.ts
DELETED
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import { existsSync } from "node:fs";
|
|
2
|
-
import { readdir, readFile } from "node:fs/promises";
|
|
3
|
-
import { dirname, join, relative, sep } from "node:path";
|
|
4
|
-
import { fileURLToPath } from "node:url";
|
|
5
|
-
|
|
6
|
-
import { parse as parseYaml } from "yaml";
|
|
7
|
-
|
|
8
|
-
import { getWorkspaceDir } from "../../util/platform.js";
|
|
9
|
-
import type {
|
|
10
|
-
LeafFrontmatter,
|
|
11
|
-
LeafNode,
|
|
12
|
-
LeafPath,
|
|
13
|
-
LeafTree,
|
|
14
|
-
Slug,
|
|
15
|
-
} from "./types.js";
|
|
16
|
-
|
|
17
|
-
const HERE = dirname(fileURLToPath(import.meta.url));
|
|
18
|
-
|
|
19
|
-
/** Bundled stub data shipped alongside the loader. */
|
|
20
|
-
const BUNDLED_DATA_DIR = join(HERE, "data");
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Resolve the directory the loaders should read leaf-tree artifacts from.
|
|
24
|
-
*
|
|
25
|
-
* Prefers a maintainer's per-instance workspace override
|
|
26
|
-
* (`<workspace>/memory/v3/data/`) when present, otherwise falls back to the
|
|
27
|
-
* generic bundled stub shipped in this package.
|
|
28
|
-
*
|
|
29
|
-
* The public {@link loadLeafTree} API still takes an explicit `dataDir` for
|
|
30
|
-
* testability; orchestrators that want the runtime default call this helper.
|
|
31
|
-
*/
|
|
32
|
-
export function resolveDataDir(): string {
|
|
33
|
-
const workspaceData = join(getWorkspaceDir(), "memory", "v3", "data");
|
|
34
|
-
if (existsSync(workspaceData)) return workspaceData;
|
|
35
|
-
return BUNDLED_DATA_DIR;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/** Recursively collect every `*.md` file under `dir`. */
|
|
39
|
-
async function collectMarkdownFiles(dir: string): Promise<string[]> {
|
|
40
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
41
|
-
const files: string[] = [];
|
|
42
|
-
for (const entry of entries) {
|
|
43
|
-
const full = join(dir, entry.name);
|
|
44
|
-
if (entry.isDirectory()) {
|
|
45
|
-
files.push(...(await collectMarkdownFiles(full)));
|
|
46
|
-
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
47
|
-
files.push(full);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
return files;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Split a leaf `.md` file into its YAML frontmatter and body.
|
|
55
|
-
*
|
|
56
|
-
* Frontmatter is the `---`-delimited block at the top of the file; everything
|
|
57
|
-
* after the closing delimiter is the description body.
|
|
58
|
-
*/
|
|
59
|
-
function splitFrontmatter(raw: string): { frontmatter: string; body: string } {
|
|
60
|
-
const normalized = raw.replace(/^/, "");
|
|
61
|
-
const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(normalized);
|
|
62
|
-
if (!match) {
|
|
63
|
-
throw new Error("leaf markdown is missing YAML frontmatter");
|
|
64
|
-
}
|
|
65
|
-
return { frontmatter: match[1], body: match[2] };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function parseLeafFrontmatter(yaml: string, file: string): LeafFrontmatter {
|
|
69
|
-
const parsed = parseYaml(yaml) as unknown;
|
|
70
|
-
if (parsed === null || typeof parsed !== "object") {
|
|
71
|
-
throw new Error(`leaf ${file} frontmatter is not a mapping`);
|
|
72
|
-
}
|
|
73
|
-
const record = parsed as Record<string, unknown>;
|
|
74
|
-
if (typeof record.path !== "string") {
|
|
75
|
-
throw new Error(`leaf ${file} frontmatter is missing a string \`path\``);
|
|
76
|
-
}
|
|
77
|
-
if (typeof record.in_core !== "boolean") {
|
|
78
|
-
throw new Error(
|
|
79
|
-
`leaf ${file} frontmatter is missing a boolean \`in_core\``,
|
|
80
|
-
);
|
|
81
|
-
}
|
|
82
|
-
return {
|
|
83
|
-
path: record.path,
|
|
84
|
-
in_core: record.in_core,
|
|
85
|
-
...(typeof record.id === "string" ? { id: record.id } : {}),
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/** Derive the canonical leaf path from a file's location under `leaves/`. */
|
|
90
|
-
function pathFromLocation(leavesDir: string, file: string): LeafPath {
|
|
91
|
-
return relative(leavesDir, file).replace(/\.md$/, "").split(sep).join("/");
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Load the leaf tree from `dataDir`. READ-ONLY: walks `leaves/**\/*.md` and
|
|
96
|
-
* reads `assignments.json` without mutating anything on disk.
|
|
97
|
-
*
|
|
98
|
-
* Builds:
|
|
99
|
-
* - `leaves`: leaf path → {@link LeafNode} (frontmatter, description, domain,
|
|
100
|
-
* and the slugs assigned to it).
|
|
101
|
-
* - `byPage`: slug → the leaf paths it is assigned to (inverted assignments).
|
|
102
|
-
*
|
|
103
|
-
* Page→leaf membership precedence is per-page: when `pageLeaves` supplies a
|
|
104
|
-
* non-empty array for a slug, those frontmatter-derived leaves win for that
|
|
105
|
-
* page; slugs absent from `pageLeaves` (or mapped to an empty array) fall back
|
|
106
|
-
* to `assignments.json`. Omitting `pageLeaves` reproduces the legacy
|
|
107
|
-
* assignments.json-only behavior exactly (used by the bundled stub and tests);
|
|
108
|
-
* orchestrators supply `pageLeaves` from the page index.
|
|
109
|
-
*/
|
|
110
|
-
export async function loadLeafTree(
|
|
111
|
-
dataDir: string,
|
|
112
|
-
pageLeaves?: Map<Slug, LeafPath[]>,
|
|
113
|
-
): Promise<LeafTree> {
|
|
114
|
-
const leavesDir = join(dataDir, "leaves");
|
|
115
|
-
const files = await collectMarkdownFiles(leavesDir);
|
|
116
|
-
|
|
117
|
-
const nodes = await Promise.all(
|
|
118
|
-
files.map(async (file): Promise<LeafNode> => {
|
|
119
|
-
const raw = await readFile(file, "utf8");
|
|
120
|
-
const { frontmatter, body } = splitFrontmatter(raw);
|
|
121
|
-
const path = pathFromLocation(leavesDir, file);
|
|
122
|
-
return {
|
|
123
|
-
path,
|
|
124
|
-
frontmatter: parseLeafFrontmatter(frontmatter, file),
|
|
125
|
-
description: body.trim(),
|
|
126
|
-
members: [],
|
|
127
|
-
domain: path.split("/")[0],
|
|
128
|
-
};
|
|
129
|
-
}),
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
const leaves = new Map<LeafPath, LeafNode>(
|
|
133
|
-
nodes.map((node) => [node.path, node]),
|
|
134
|
-
);
|
|
135
|
-
|
|
136
|
-
const assignmentsRaw = await readFile(
|
|
137
|
-
join(dataDir, "assignments.json"),
|
|
138
|
-
"utf8",
|
|
139
|
-
);
|
|
140
|
-
const assignments = JSON.parse(assignmentsRaw) as Record<Slug, LeafPath[]>;
|
|
141
|
-
|
|
142
|
-
const slugs = new Set<Slug>(Object.keys(assignments));
|
|
143
|
-
if (pageLeaves) for (const slug of pageLeaves.keys()) slugs.add(slug);
|
|
144
|
-
|
|
145
|
-
const byPage = new Map<Slug, LeafPath[]>();
|
|
146
|
-
for (const slug of slugs) {
|
|
147
|
-
const fromFrontmatter = pageLeaves?.get(slug);
|
|
148
|
-
const leafPaths =
|
|
149
|
-
fromFrontmatter && fromFrontmatter.length > 0
|
|
150
|
-
? fromFrontmatter
|
|
151
|
-
: (assignments[slug] ?? []);
|
|
152
|
-
byPage.set(slug, [...leafPaths]);
|
|
153
|
-
for (const leafPath of leafPaths) {
|
|
154
|
-
leaves.get(leafPath)?.members.push(slug);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
return { leaves, byPage };
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/** The slugs assigned to a leaf. */
|
|
162
|
-
export function membersOf(tree: LeafTree, leaf: LeafPath): Slug[] {
|
|
163
|
-
return tree.leaves.get(leaf)?.members ?? [];
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/** The leaf paths a slug is assigned to. */
|
|
167
|
-
export function leavesOf(tree: LeafTree, slug: Slug): LeafPath[] {
|
|
168
|
-
return tree.byPage.get(slug) ?? [];
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
/** The unique slugs owned by every leaf in `core` (expand leaf-paths → slugs). */
|
|
172
|
-
export function coreSlugs(tree: LeafTree, core: Set<LeafPath>): Set<Slug> {
|
|
173
|
-
return new Set([...core].flatMap((leaf) => membersOf(tree, leaf)));
|
|
174
|
-
}
|
package/src/memory/v3/types.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
export type LeafPath = string; // dot- or slash-pathed taxonomy node
|
|
2
|
-
export type Slug = string;
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Injection-block id for the v3 live `<memory>` block. Shared between the
|
|
6
|
-
* producer (the v3 injector in `shadow-plugin.ts`) and the v2-suppression
|
|
7
|
-
* consumer (`conversation-runtime-assembly.ts`), which keys off this id to
|
|
8
|
-
* detect that v3 actually produced a block this turn. Keeping it in one place
|
|
9
|
-
* makes a rename a compile error on both sides instead of a silent
|
|
10
|
-
* suppression bypass.
|
|
11
|
-
*/
|
|
12
|
-
export const MEMORY_V3_BLOCK_ID = "memory-v3" as const;
|
|
13
|
-
|
|
14
|
-
export interface LeafFrontmatter {
|
|
15
|
-
path: LeafPath;
|
|
16
|
-
in_core: boolean;
|
|
17
|
-
/**
|
|
18
|
-
* Optional stable identifier for the leaf, independent of its taxonomy path.
|
|
19
|
-
* Older leaves predate this field and omit it, so it is optional.
|
|
20
|
-
*/
|
|
21
|
-
id?: string;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface LeafNode {
|
|
25
|
-
path: LeafPath;
|
|
26
|
-
frontmatter: LeafFrontmatter;
|
|
27
|
-
description: string;
|
|
28
|
-
members: Slug[];
|
|
29
|
-
domain: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface LeafTree {
|
|
33
|
-
leaves: Map<LeafPath, LeafNode>;
|
|
34
|
-
byPage: Map<Slug, LeafPath[]>;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
export interface WorkingSetEntry {
|
|
38
|
-
slug: Slug;
|
|
39
|
-
selectedAtTurn: number;
|
|
40
|
-
pinned: boolean;
|
|
41
|
-
lastSeenTurn: number;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface TurnContext {
|
|
45
|
-
conversationId: string;
|
|
46
|
-
turnNumber: number;
|
|
47
|
-
currentMessage: string;
|
|
48
|
-
recentContext: string;
|
|
49
|
-
/**
|
|
50
|
-
* Optional situational signal — the current date plus the live NOW.md
|
|
51
|
-
* scratchpad — so a leaf or page can be routed/selected on a date or
|
|
52
|
-
* live-state cue the message itself never names (e.g. a person whose
|
|
53
|
-
* anniversary is today). Omitted when unavailable; the router and selector
|
|
54
|
-
* render nothing for an undefined value.
|
|
55
|
-
*/
|
|
56
|
-
situationalContext?: string;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export type SelectionSource = "l1+l2" | "core+l2" | "needle" | "carry-forward";
|
|
@@ -1,187 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Verifies that `emitNotificationSignal` invokes the home-feed side
|
|
3
|
-
* effect after dispatching, and that the side effect's background-only
|
|
4
|
-
* filter correctly suppresses interactive signals.
|
|
5
|
-
*
|
|
6
|
-
* The side effect itself is unit-tested in `home-feed-side-effect.test.ts`;
|
|
7
|
-
* this test exercises the wire-up boundary in `emit-signal.ts` by mocking
|
|
8
|
-
* at the `appendFeedItem` / `getConversation` boundary so the real
|
|
9
|
-
* background-filter logic runs end-to-end.
|
|
10
|
-
*/
|
|
11
|
-
import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
12
|
-
|
|
13
|
-
import type { FeedItem } from "../../home/feed-types.js";
|
|
14
|
-
import type {
|
|
15
|
-
NotificationDecision,
|
|
16
|
-
NotificationDeliveryResult,
|
|
17
|
-
} from "../types.js";
|
|
18
|
-
|
|
19
|
-
// ── Module mocks ───────────────────────────────────────────────────────
|
|
20
|
-
//
|
|
21
|
-
// `mock.module` is hoisted, so these intercepts apply before the module
|
|
22
|
-
// under test resolves its imports. Closures over module-scoped state
|
|
23
|
-
// let each test reset and inspect captured calls afterwards.
|
|
24
|
-
|
|
25
|
-
const appendCalls: FeedItem[] = [];
|
|
26
|
-
let conversationRow: { conversationType: string } | null = null;
|
|
27
|
-
|
|
28
|
-
mock.module("../../home/feed-writer.js", () => ({
|
|
29
|
-
appendFeedItem: async (item: FeedItem) => {
|
|
30
|
-
appendCalls.push(item);
|
|
31
|
-
},
|
|
32
|
-
}));
|
|
33
|
-
|
|
34
|
-
// home-feed-side-effect.ts only consumes `getConversation`.
|
|
35
|
-
mock.module("../../memory/conversation-crud.js", () => ({
|
|
36
|
-
getConversation: () => conversationRow,
|
|
37
|
-
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
38
|
-
}));
|
|
39
|
-
|
|
40
|
-
// Stub the broadcaster so emit-signal's `getBroadcaster()` does not need
|
|
41
|
-
// to build real adapters or pull in `conversation-pairing.ts` transitively
|
|
42
|
-
// (which would force us to mirror the full `conversation-crud.js` surface).
|
|
43
|
-
class StubBroadcaster {
|
|
44
|
-
setOnConversationCreated() {}
|
|
45
|
-
async broadcastDecision() {
|
|
46
|
-
return [];
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
mock.module("../broadcaster.js", () => ({
|
|
50
|
-
NotificationBroadcaster: StubBroadcaster,
|
|
51
|
-
}));
|
|
52
|
-
|
|
53
|
-
mock.module("../adapters/macos.js", () => ({
|
|
54
|
-
VellumAdapter: class {},
|
|
55
|
-
isGuardianSensitiveEvent: () => false,
|
|
56
|
-
}));
|
|
57
|
-
mock.module("../adapters/slack.js", () => ({
|
|
58
|
-
SlackAdapter: class {},
|
|
59
|
-
}));
|
|
60
|
-
mock.module("../adapters/telegram.js", () => ({
|
|
61
|
-
TelegramAdapter: class {},
|
|
62
|
-
}));
|
|
63
|
-
|
|
64
|
-
// Stub out the persistence + channel resolution layers so emitNotificationSignal
|
|
65
|
-
// can run end-to-end without touching the DB / contacts store / LLM.
|
|
66
|
-
|
|
67
|
-
mock.module("../events-store.js", () => ({
|
|
68
|
-
createEvent: (params: { id: string }) => ({
|
|
69
|
-
id: params.id,
|
|
70
|
-
sourceEventName: "schedule.notify",
|
|
71
|
-
sourceChannel: "scheduler",
|
|
72
|
-
sourceContextId: "conv-source-1",
|
|
73
|
-
attentionHintsJson: "{}",
|
|
74
|
-
payloadJson: "{}",
|
|
75
|
-
dedupeKey: null,
|
|
76
|
-
createdAt: 0,
|
|
77
|
-
updatedAt: 0,
|
|
78
|
-
}),
|
|
79
|
-
updateEventDedupeKey: () => {},
|
|
80
|
-
}));
|
|
81
|
-
|
|
82
|
-
mock.module("../decisions-store.js", () => ({
|
|
83
|
-
updateDecision: () => {},
|
|
84
|
-
}));
|
|
85
|
-
|
|
86
|
-
mock.module("../../contacts/contact-store.js", () => ({
|
|
87
|
-
findGuardianForChannel: () => null,
|
|
88
|
-
listGuardianChannels: () => [],
|
|
89
|
-
}));
|
|
90
|
-
|
|
91
|
-
const stubDecision: NotificationDecision = {
|
|
92
|
-
shouldNotify: true,
|
|
93
|
-
selectedChannels: ["vellum"],
|
|
94
|
-
reasoningSummary: "test",
|
|
95
|
-
renderedCopy: {
|
|
96
|
-
vellum: { title: "Background job done", body: "Summary of what happened." },
|
|
97
|
-
},
|
|
98
|
-
dedupeKey: "dk-1",
|
|
99
|
-
confidence: 1,
|
|
100
|
-
fallbackUsed: false,
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
mock.module("../decision-engine.js", () => ({
|
|
104
|
-
evaluateSignal: async () => stubDecision,
|
|
105
|
-
// Pass the decision through unchanged — emit-signal calls this after
|
|
106
|
-
// evaluateSignal and only re-persists when the reference changes.
|
|
107
|
-
enforceRoutingIntent: (decision: NotificationDecision) => decision,
|
|
108
|
-
}));
|
|
109
|
-
|
|
110
|
-
mock.module("../deterministic-checks.js", () => ({
|
|
111
|
-
runDeterministicChecks: async () => ({ passed: true }),
|
|
112
|
-
}));
|
|
113
|
-
|
|
114
|
-
const stubDeliveryResults: NotificationDeliveryResult[] = [
|
|
115
|
-
{
|
|
116
|
-
channel: "vellum",
|
|
117
|
-
destination: "vellum-client",
|
|
118
|
-
status: "sent",
|
|
119
|
-
conversationId: "conv-vellum-1",
|
|
120
|
-
},
|
|
121
|
-
];
|
|
122
|
-
|
|
123
|
-
mock.module("../runtime-dispatch.js", () => ({
|
|
124
|
-
dispatchDecision: async () => ({
|
|
125
|
-
dispatched: true,
|
|
126
|
-
reason: "Dispatched to 1/1 channels",
|
|
127
|
-
deliveryResults: stubDeliveryResults,
|
|
128
|
-
}),
|
|
129
|
-
}));
|
|
130
|
-
|
|
131
|
-
const { emitNotificationSignal } = await import("../emit-signal.js");
|
|
132
|
-
|
|
133
|
-
// ── Tests ──────────────────────────────────────────────────────────────
|
|
134
|
-
|
|
135
|
-
beforeEach(() => {
|
|
136
|
-
appendCalls.length = 0;
|
|
137
|
-
conversationRow = null;
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
describe("emitNotificationSignal home-feed wire-up", () => {
|
|
141
|
-
test("background-conversation signal triggers appendFeedItem", async () => {
|
|
142
|
-
conversationRow = { conversationType: "background" };
|
|
143
|
-
|
|
144
|
-
const result = await emitNotificationSignal({
|
|
145
|
-
sourceEventName: "schedule.notify",
|
|
146
|
-
sourceChannel: "scheduler",
|
|
147
|
-
sourceContextId: "conv-source-1",
|
|
148
|
-
contextPayload: { title: "Background job done" },
|
|
149
|
-
attentionHints: {
|
|
150
|
-
requiresAction: false,
|
|
151
|
-
urgency: "medium",
|
|
152
|
-
isAsyncBackground: false,
|
|
153
|
-
visibleInSourceNow: false,
|
|
154
|
-
},
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
expect(result.dispatched).toBe(true);
|
|
158
|
-
expect(appendCalls).toHaveLength(1);
|
|
159
|
-
const appended = appendCalls[0]!;
|
|
160
|
-
expect(appended.id).toBe(`notif:${result.signalId}`);
|
|
161
|
-
expect(appended.title).toBe("Background job done");
|
|
162
|
-
expect(appended.summary).toBe("Summary of what happened.");
|
|
163
|
-
// The feed item's conversationId points to the source conversation
|
|
164
|
-
// that emitted the signal, not the conversation the notification
|
|
165
|
-
// pipeline spawned to handle it.
|
|
166
|
-
expect(appended.conversationId).toBe("conv-source-1");
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
test("interactive standard conversation does NOT trigger appendFeedItem", async () => {
|
|
170
|
-
conversationRow = { conversationType: "standard" };
|
|
171
|
-
|
|
172
|
-
const result = await emitNotificationSignal({
|
|
173
|
-
sourceEventName: "schedule.notify",
|
|
174
|
-
sourceChannel: "scheduler",
|
|
175
|
-
sourceContextId: "conv-source-1",
|
|
176
|
-
attentionHints: {
|
|
177
|
-
requiresAction: false,
|
|
178
|
-
urgency: "medium",
|
|
179
|
-
isAsyncBackground: false,
|
|
180
|
-
visibleInSourceNow: true,
|
|
181
|
-
},
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
expect(result.dispatched).toBe(true);
|
|
185
|
-
expect(appendCalls).toHaveLength(0);
|
|
186
|
-
});
|
|
187
|
-
});
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
CircuitBreakerArgs,
|
|
3
|
-
CircuitBreakerResult,
|
|
4
|
-
Middleware,
|
|
5
|
-
} from "../../../types.js";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Consecutive failures required to trip the breaker. Matches the legacy
|
|
9
|
-
* `COMPACTION_CIRCUIT_FAILURE_THRESHOLD` in `conversation-agent-loop.ts`.
|
|
10
|
-
*/
|
|
11
|
-
export const COMPACTION_CIRCUIT_FAILURE_THRESHOLD = 3;
|
|
12
|
-
|
|
13
|
-
/**
|
|
14
|
-
* Cooldown window after the breaker trips, during which auto-compaction is
|
|
15
|
-
* suspended. Matches the legacy `COMPACTION_CIRCUIT_COOLDOWN_MS`.
|
|
16
|
-
*/
|
|
17
|
-
export const COMPACTION_CIRCUIT_COOLDOWN_MS = 60 * 60 * 1000;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Default middleware for the `circuitBreaker` pipeline. A thin wrapper over the
|
|
21
|
-
* state container passed in `CircuitBreakerArgs.state`:
|
|
22
|
-
*
|
|
23
|
-
* - `{ key }` — query. Defers to `next` for the current
|
|
24
|
-
* `{ open, cooldownRemainingMs? }`.
|
|
25
|
-
* - `{ key, outcome }` — update state based on outcome, then defer to `next`
|
|
26
|
-
* for the post-update decision. A run of three failures trips the breaker;
|
|
27
|
-
* any non-failure outcome resets both the counter and the cooldown timestamp.
|
|
28
|
-
*
|
|
29
|
-
* Event emission preserves the existing `trackCompactionOutcome` behavior:
|
|
30
|
-
* `compaction_circuit_open` fires once when the counter first reaches the
|
|
31
|
-
* threshold while the circuit is dormant; `compaction_circuit_closed` fires
|
|
32
|
-
* only on the open→closed transition.
|
|
33
|
-
*/
|
|
34
|
-
const circuitBreaker: Middleware<CircuitBreakerArgs, CircuitBreakerResult> =
|
|
35
|
-
async function circuitBreaker(args, next) {
|
|
36
|
-
const { outcome, state, onEvent } = args;
|
|
37
|
-
|
|
38
|
-
// Update branch — mutate state first, then defer to the downstream
|
|
39
|
-
// chain (or terminal) for the decision so outer observers still see
|
|
40
|
-
// the fully-processed outcome. Separating state mutation from
|
|
41
|
-
// decision computation also keeps this middleware composable: an
|
|
42
|
-
// outer plugin may wrap the invocation to observe both the pre-update
|
|
43
|
-
// args and the post-update result.
|
|
44
|
-
if (outcome !== undefined) {
|
|
45
|
-
if (outcome === "failure") {
|
|
46
|
-
state.consecutiveCompactionFailures += 1;
|
|
47
|
-
// Treat a stale/expired open-until timestamp the same as null so
|
|
48
|
-
// a new 3-strike window can re-open the circuit after the prior
|
|
49
|
-
// cooldown elapses. Without this, subsequent trips would no-op
|
|
50
|
-
// because `compactionCircuitOpenUntil` remains set to a past
|
|
51
|
-
// timestamp even though the breaker is effectively closed.
|
|
52
|
-
const circuitDormant =
|
|
53
|
-
state.compactionCircuitOpenUntil === null ||
|
|
54
|
-
Date.now() >= state.compactionCircuitOpenUntil;
|
|
55
|
-
if (
|
|
56
|
-
state.consecutiveCompactionFailures >=
|
|
57
|
-
COMPACTION_CIRCUIT_FAILURE_THRESHOLD &&
|
|
58
|
-
circuitDormant
|
|
59
|
-
) {
|
|
60
|
-
const openUntil = Date.now() + COMPACTION_CIRCUIT_COOLDOWN_MS;
|
|
61
|
-
state.compactionCircuitOpenUntil = openUntil;
|
|
62
|
-
if (onEvent) {
|
|
63
|
-
onEvent({
|
|
64
|
-
type: "compaction_circuit_open",
|
|
65
|
-
conversationId: state.conversationId,
|
|
66
|
-
reason: "3_consecutive_failures",
|
|
67
|
-
openUntil,
|
|
68
|
-
});
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
} else {
|
|
72
|
-
// Emit only on the open→closed transition; firing on the common
|
|
73
|
-
// closed→closed case would be noise.
|
|
74
|
-
const wasOpen = state.compactionCircuitOpenUntil !== null;
|
|
75
|
-
state.consecutiveCompactionFailures = 0;
|
|
76
|
-
state.compactionCircuitOpenUntil = null;
|
|
77
|
-
if (wasOpen && onEvent) {
|
|
78
|
-
onEvent({
|
|
79
|
-
type: "compaction_circuit_closed",
|
|
80
|
-
conversationId: state.conversationId,
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
// Defer to downstream (the terminal, in the default registration, but
|
|
87
|
-
// potentially another plugin in a customized chain) for the final
|
|
88
|
-
// decision. The terminal's implementation is the canonical read of
|
|
89
|
-
// the (now-updated) state container.
|
|
90
|
-
return next(args);
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
export default circuitBreaker;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "default-circuit-breaker",
|
|
3
|
-
"version": "1.0.0",
|
|
4
|
-
"description": "First-party default plugin wrapping the assistant's built-in circuit-breaker pipeline with a passthrough implementation.",
|
|
5
|
-
"private": true,
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"type": "module",
|
|
8
|
-
"main": "./register.ts",
|
|
9
|
-
"engines": {
|
|
10
|
-
"node": ">=20.12.0"
|
|
11
|
-
},
|
|
12
|
-
"peerDependencies": {
|
|
13
|
-
"@vellumai/plugin-api": "^0.8.0"
|
|
14
|
-
}
|
|
15
|
-
}
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Default `circuitBreaker` plugin.
|
|
3
|
-
*
|
|
4
|
-
* Replicates the inline compaction circuit-breaker logic that previously
|
|
5
|
-
* lived in `daemon/conversation-agent-loop.ts`: three consecutive summary-LLM
|
|
6
|
-
* failures open the circuit for a one-hour cooldown, and any successful
|
|
7
|
-
* compaction resets the counter.
|
|
8
|
-
*
|
|
9
|
-
* The plugin is a thin wrapper over the state container passed in
|
|
10
|
-
* `CircuitBreakerArgs.state`. The {@link Conversation} owns the underlying
|
|
11
|
-
* fields (`consecutiveCompactionFailures`, `compactionCircuitOpenUntil`)
|
|
12
|
-
* because dev-only playground routes (`POST /playground/reset-compaction-circuit`,
|
|
13
|
-
* `POST /playground/inject-compaction-failures`) read and mutate them
|
|
14
|
-
* directly. Keeping ownership on the conversation lets this plugin stay a
|
|
15
|
-
* pure wrapper while preserving those hatches.
|
|
16
|
-
*
|
|
17
|
-
* The `key` parameter is carried through for multi-circuit futures but the
|
|
18
|
-
* default plugin currently bundles all circuit state into the `state`
|
|
19
|
-
* container; the key is attached to the log record via the pipeline runner.
|
|
20
|
-
*/
|
|
21
|
-
|
|
22
|
-
import { type Plugin } from "../../types.js";
|
|
23
|
-
import circuitBreaker from "./middlewares/circuitBreaker.js";
|
|
24
|
-
import pkg from "./package.json" with { type: "json" };
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Default plugin registered at daemon startup. Consumers negotiate against
|
|
28
|
-
* `circuitBreakerApi@v1` via the registry's capability table.
|
|
29
|
-
*/
|
|
30
|
-
export const defaultCircuitBreakerPlugin: Plugin = {
|
|
31
|
-
manifest: {
|
|
32
|
-
name: pkg.name,
|
|
33
|
-
version: pkg.version,
|
|
34
|
-
},
|
|
35
|
-
|
|
36
|
-
middleware: {
|
|
37
|
-
circuitBreaker,
|
|
38
|
-
},
|
|
39
|
-
};
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
CompactionArgs,
|
|
3
|
-
CompactionResult,
|
|
4
|
-
Middleware,
|
|
5
|
-
} from "../../../types.js";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Passthrough middleware for the `compaction` pipeline. Forwards to
|
|
9
|
-
* `next(args)` so any custom plugins layered outside still run; when this is
|
|
10
|
-
* the only middleware, `next` is the terminal handler (`../terminal.ts`) and
|
|
11
|
-
* returns the real compaction output.
|
|
12
|
-
*
|
|
13
|
-
* Defaults register at the OUTERMOST onion position, so deciding here without
|
|
14
|
-
* calling `next` would shadow every later-registered plugin. Routing through
|
|
15
|
-
* `next(args)` lets user middleware participate normally.
|
|
16
|
-
*/
|
|
17
|
-
const defaultCompactionMiddleware: Middleware<
|
|
18
|
-
CompactionArgs,
|
|
19
|
-
CompactionResult
|
|
20
|
-
> = async function defaultCompaction(args, next, ctx) {
|
|
21
|
-
void ctx;
|
|
22
|
-
return next(args);
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
export default defaultCompactionMiddleware;
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Default `compaction` plugin.
|
|
3
|
-
*
|
|
4
|
-
* The plugin's middleware is a passthrough — it calls `next(args)` and returns
|
|
5
|
-
* the result unchanged. The actual compaction lives in the terminal handler in
|
|
6
|
-
* `./terminal.ts`, which is wired in as the pipeline's `terminal` argument by
|
|
7
|
-
* the `runPipeline` call site in `daemon/conversation-agent-loop.ts`. This
|
|
8
|
-
* separation matters: the default plugin is registered before any user plugin
|
|
9
|
-
* (defaults load first in `bootstrapPlugins()`), which puts it at the
|
|
10
|
-
* OUTERMOST position of the onion chain. If the default middleware were to
|
|
11
|
-
* invoke the terminal directly without calling `next`, it would shadow every
|
|
12
|
-
* later-registered plugin. Routing through `next(args)` lets user middleware
|
|
13
|
-
* participate normally.
|
|
14
|
-
*
|
|
15
|
-
* Design doc: `.private/plans/agent-plugin-system.md` (PR 25).
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
import { type Plugin } from "../../types.js";
|
|
19
|
-
import defaultCompactionMiddleware from "./middlewares/compaction.js";
|
|
20
|
-
import pkg from "./package.json" with { type: "json" };
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Manifest + middleware wiring for the default compaction plugin. The
|
|
24
|
-
* registration happens in `daemon/external-plugins-bootstrap.ts` before
|
|
25
|
-
* {@link bootstrapPlugins} fires plugin `init()` hooks.
|
|
26
|
-
*/
|
|
27
|
-
export const defaultCompactionPlugin: Plugin = {
|
|
28
|
-
manifest: {
|
|
29
|
-
name: pkg.name,
|
|
30
|
-
version: pkg.version,
|
|
31
|
-
},
|
|
32
|
-
middleware: {
|
|
33
|
-
compaction: defaultCompactionMiddleware,
|
|
34
|
-
},
|
|
35
|
-
};
|