@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
|
@@ -46,14 +46,24 @@ const workspaceTreeEntrySchema = z.object({
|
|
|
46
46
|
|
|
47
47
|
type TreeEntry = z.infer<typeof workspaceTreeEntrySchema>;
|
|
48
48
|
|
|
49
|
-
//
|
|
50
|
-
//
|
|
51
|
-
//
|
|
52
|
-
//
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
|
|
49
|
+
// Recursive directory sizing walks the filesystem synchronously, so it is
|
|
50
|
+
// bounded by two caps that work together:
|
|
51
|
+
//
|
|
52
|
+
// - `DIR_SIZE_PER_DIR_ENTRY_BUDGET` caps a single directory. No one giant
|
|
53
|
+
// subtree (e.g. a `node_modules/` or `repos/` checkout) can consume the whole
|
|
54
|
+
// listing's budget and starve its siblings; only the oversized directory
|
|
55
|
+
// itself reports `size: null` (the caller renders an unknown-size
|
|
56
|
+
// placeholder) while every other folder still shows an accurate size.
|
|
57
|
+
// - `DIR_SIZE_TOTAL_ENTRY_BUDGET` caps the listing as a whole, so a workspace
|
|
58
|
+
// with many large sibling directories still can't block the daemon event loop
|
|
59
|
+
// for an unbounded amount of time. Once it is exhausted, the remaining
|
|
60
|
+
// directories report `size: null`.
|
|
61
|
+
//
|
|
62
|
+
// Each directory is allotted `min(per-dir cap, remaining listing budget)`
|
|
63
|
+
// entries, and whatever it actually consumes is deducted from the listing
|
|
64
|
+
// budget.
|
|
65
|
+
const DIR_SIZE_PER_DIR_ENTRY_BUDGET = 250_000;
|
|
66
|
+
const DIR_SIZE_TOTAL_ENTRY_BUDGET = 1_000_000;
|
|
57
67
|
|
|
58
68
|
interface DirSizeBudget {
|
|
59
69
|
remaining: number;
|
|
@@ -61,13 +71,13 @@ interface DirSizeBudget {
|
|
|
61
71
|
|
|
62
72
|
/**
|
|
63
73
|
* Recursively sum the byte size of every regular file under `absPath`,
|
|
64
|
-
*
|
|
74
|
+
* traversing up to `budget.remaining` filesystem entries.
|
|
65
75
|
*
|
|
66
76
|
* Returns:
|
|
67
77
|
* - the total size in bytes when the entire subtree was traversed within
|
|
68
78
|
* budget
|
|
69
|
-
* - `null` if the
|
|
70
|
-
*
|
|
79
|
+
* - `null` if the budget was exhausted before completion (the caller surfaces
|
|
80
|
+
* that as an unknown size in the UI)
|
|
71
81
|
*
|
|
72
82
|
* Symlinks are not followed. We rely on `withFileTypes` so we never `stat`
|
|
73
83
|
* directories purely to discover their type.
|
|
@@ -108,6 +118,27 @@ function computeDirSize(absPath: string, budget: DirSizeBudget): number | null {
|
|
|
108
118
|
return total;
|
|
109
119
|
}
|
|
110
120
|
|
|
121
|
+
/**
|
|
122
|
+
* Compute a directory's recursive size while drawing from a shared listing
|
|
123
|
+
* budget. The directory is allotted at most `DIR_SIZE_PER_DIR_ENTRY_BUDGET`
|
|
124
|
+
* entries — and never more than the listing has left — and whatever it consumes
|
|
125
|
+
* is deducted from `listingBudget` so the next sibling sees the reduced
|
|
126
|
+
* remainder. Returns `null` (unknown size) when the allotment is exhausted.
|
|
127
|
+
*/
|
|
128
|
+
function computeDirSizeWithinListing(
|
|
129
|
+
absPath: string,
|
|
130
|
+
listingBudget: DirSizeBudget,
|
|
131
|
+
): number | null {
|
|
132
|
+
const allotment = Math.min(
|
|
133
|
+
DIR_SIZE_PER_DIR_ENTRY_BUDGET,
|
|
134
|
+
listingBudget.remaining,
|
|
135
|
+
);
|
|
136
|
+
const dirBudget: DirSizeBudget = { remaining: allotment };
|
|
137
|
+
const size = computeDirSize(absPath, dirBudget);
|
|
138
|
+
listingBudget.remaining -= allotment - dirBudget.remaining;
|
|
139
|
+
return size;
|
|
140
|
+
}
|
|
141
|
+
|
|
111
142
|
const SOUNDS_WORKSPACE_PATH = "data/sounds";
|
|
112
143
|
|
|
113
144
|
function normaliseWorkspacePathForSync(path: string): string {
|
|
@@ -153,9 +184,11 @@ function handleWorkspaceTree({ queryParams }: RouteHandlerArgs) {
|
|
|
153
184
|
const dirents = readdirSync(resolved, { withFileTypes: true });
|
|
154
185
|
const workspaceDir = getWorkspaceDir();
|
|
155
186
|
|
|
156
|
-
//
|
|
157
|
-
//
|
|
158
|
-
|
|
187
|
+
// Listing-wide budget, only constructed when sizes are requested. Each
|
|
188
|
+
// directory draws a per-directory allotment from it so one oversized
|
|
189
|
+
// subtree can't starve its siblings, while the shared remainder still caps
|
|
190
|
+
// total synchronous traversal for the whole listing.
|
|
191
|
+
const listingBudget: DirSizeBudget | undefined = includeDirSizes
|
|
159
192
|
? { remaining: DIR_SIZE_TOTAL_ENTRY_BUDGET }
|
|
160
193
|
: undefined;
|
|
161
194
|
|
|
@@ -176,7 +209,9 @@ function handleWorkspaceTree({ queryParams }: RouteHandlerArgs) {
|
|
|
176
209
|
const relativePath = fullPath.slice(workspaceDir.length + 1);
|
|
177
210
|
|
|
178
211
|
const dirSize =
|
|
179
|
-
isDir &&
|
|
212
|
+
isDir && listingBudget
|
|
213
|
+
? computeDirSizeWithinListing(fullPath, listingBudget)
|
|
214
|
+
: null;
|
|
180
215
|
|
|
181
216
|
entries.push({
|
|
182
217
|
name: entry.name,
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { parseChannelId } from "../../channels/types.js";
|
|
11
11
|
import { getConfig } from "../../config/loader.js";
|
|
12
|
-
import { findConversation } from "../../daemon/conversation-
|
|
12
|
+
import { findConversation } from "../../daemon/conversation-registry.js";
|
|
13
13
|
import { normalizeConversationType } from "../../daemon/message-types/shared.js";
|
|
14
14
|
import {
|
|
15
15
|
type AttentionState,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Shared outbound verification action logic.
|
|
3
3
|
*
|
|
4
4
|
* These pure functions encapsulate the business logic for starting, resending,
|
|
5
|
-
* and cancelling outbound verification flows (Telegram, voice, Slack).
|
|
5
|
+
* and cancelling outbound verification flows (Telegram, voice, Slack, email).
|
|
6
6
|
* They return transport-agnostic result objects and are consumed by both the
|
|
7
7
|
* message handler (config-channels.ts) and the HTTP route layer (channel-verification-routes.ts).
|
|
8
8
|
*/
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
updateSessionStatus,
|
|
27
27
|
} from "./channel-verification-service.js";
|
|
28
28
|
import {
|
|
29
|
+
composeVerificationEmail,
|
|
29
30
|
composeVerificationSlack,
|
|
30
31
|
composeVerificationTelegram,
|
|
31
32
|
GUARDIAN_VERIFY_TEMPLATE_KEYS,
|
|
@@ -52,6 +53,8 @@ export const DESTINATION_RATE_WINDOW_MS = 3_600_000;
|
|
|
52
53
|
/** Session TTL in seconds (matches challenge TTL of 10 minutes). */
|
|
53
54
|
const SESSION_TTL_SECONDS = 600;
|
|
54
55
|
|
|
56
|
+
const EMAIL_VERIFICATION_SUBJECT = "Vellum Assistant Guardian Verification";
|
|
57
|
+
|
|
55
58
|
// ---------------------------------------------------------------------------
|
|
56
59
|
// Telegram destination classification
|
|
57
60
|
// ---------------------------------------------------------------------------
|
|
@@ -122,6 +125,8 @@ interface OutboundActionResult {
|
|
|
122
125
|
* reach the gateway. The daemon HTTP route handler calls
|
|
123
126
|
* deliverVerificationSlack() after receiving this payload. */
|
|
124
127
|
_pendingSlackDm?: { userId: string; text: string; assistantId: string };
|
|
128
|
+
/** Internal: email delivery payload for the caller to dispatch (same pattern as Slack). */
|
|
129
|
+
_pendingEmail?: { to: string; text: string; subject: string; assistantId: string };
|
|
125
130
|
}
|
|
126
131
|
|
|
127
132
|
// ---------------------------------------------------------------------------
|
|
@@ -512,6 +517,80 @@ export function deliverVerificationSlack(
|
|
|
512
517
|
deliverVerificationSlackAsync(userId, text, assistantId);
|
|
513
518
|
}
|
|
514
519
|
|
|
520
|
+
// ---------------------------------------------------------------------------
|
|
521
|
+
// Email delivery helper
|
|
522
|
+
// ---------------------------------------------------------------------------
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* Deliver a verification email via the platform email send API.
|
|
526
|
+
* Fire-and-forget wrapper for use in the daemon process (HTTP route handlers).
|
|
527
|
+
*/
|
|
528
|
+
export function deliverVerificationEmail(
|
|
529
|
+
to: string,
|
|
530
|
+
text: string,
|
|
531
|
+
subject: string,
|
|
532
|
+
_assistantId: string,
|
|
533
|
+
): void {
|
|
534
|
+
(async () => {
|
|
535
|
+
try {
|
|
536
|
+
const { VellumPlatformClient } = await import(
|
|
537
|
+
"../platform/client.js"
|
|
538
|
+
);
|
|
539
|
+
const client = await VellumPlatformClient.create();
|
|
540
|
+
if (!client?.platformAssistantId) {
|
|
541
|
+
log.error("Cannot deliver verification email: platform client not configured");
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
const listResponse = await client.fetch(
|
|
546
|
+
`/v1/assistants/${client.platformAssistantId}/email-addresses/`,
|
|
547
|
+
);
|
|
548
|
+
if (!listResponse.ok) {
|
|
549
|
+
log.error({ status: listResponse.status }, "Failed to list email addresses for verification");
|
|
550
|
+
return;
|
|
551
|
+
}
|
|
552
|
+
const listData = (await listResponse.json()) as {
|
|
553
|
+
results: { address: string }[];
|
|
554
|
+
};
|
|
555
|
+
const addresses = listData.results ?? [];
|
|
556
|
+
if (addresses.length === 0) {
|
|
557
|
+
log.error("No email address registered — cannot deliver verification email");
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
const fromAddress = addresses[0].address;
|
|
561
|
+
|
|
562
|
+
const { markdownToEmailHtml } = await import(
|
|
563
|
+
"../email/html-renderer.js"
|
|
564
|
+
);
|
|
565
|
+
const html = markdownToEmailHtml(text);
|
|
566
|
+
|
|
567
|
+
const response = await client.fetch("/v1/runtime-proxy/email/send/", {
|
|
568
|
+
method: "POST",
|
|
569
|
+
headers: { "Content-Type": "application/json" },
|
|
570
|
+
body: JSON.stringify({
|
|
571
|
+
to: [to],
|
|
572
|
+
from_address: fromAddress,
|
|
573
|
+
text,
|
|
574
|
+
subject,
|
|
575
|
+
html,
|
|
576
|
+
}),
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
if (response.ok) {
|
|
580
|
+
log.info({ to }, "Verification email delivered");
|
|
581
|
+
} else {
|
|
582
|
+
const respBody = await response.json().catch(() => ({}));
|
|
583
|
+
log.error(
|
|
584
|
+
{ to, status: response.status, respBody },
|
|
585
|
+
"Failed to deliver verification email",
|
|
586
|
+
);
|
|
587
|
+
}
|
|
588
|
+
} catch (err) {
|
|
589
|
+
log.error({ err, to }, "Failed to deliver verification email");
|
|
590
|
+
}
|
|
591
|
+
})();
|
|
592
|
+
}
|
|
593
|
+
|
|
515
594
|
function startOutboundSlack(
|
|
516
595
|
destination: string | undefined,
|
|
517
596
|
assistantId: string,
|
|
@@ -644,13 +723,35 @@ function startOutboundEmail(
|
|
|
644
723
|
verificationPurpose: "guardian",
|
|
645
724
|
});
|
|
646
725
|
|
|
726
|
+
const emailBody = composeVerificationEmail(
|
|
727
|
+
GUARDIAN_VERIFY_TEMPLATE_KEYS.EMAIL_CHALLENGE_REQUEST,
|
|
728
|
+
{
|
|
729
|
+
code: sessionResult.secret,
|
|
730
|
+
expiresInMinutes: Math.floor(SESSION_TTL_SECONDS / 60),
|
|
731
|
+
},
|
|
732
|
+
);
|
|
733
|
+
|
|
734
|
+
const now = Date.now();
|
|
735
|
+
const nextResendAt = now + RESEND_COOLDOWN_MS;
|
|
736
|
+
const sendCount = 1;
|
|
737
|
+
|
|
738
|
+
updateSessionDelivery(sessionResult.sessionId, now, sendCount, nextResendAt);
|
|
739
|
+
|
|
647
740
|
return {
|
|
648
741
|
success: true,
|
|
649
742
|
verificationSessionId: sessionResult.sessionId,
|
|
650
743
|
secret: sessionResult.secret,
|
|
651
744
|
expiresAt: sessionResult.expiresAt,
|
|
745
|
+
nextResendAt,
|
|
746
|
+
sendCount,
|
|
652
747
|
channel,
|
|
653
748
|
originConversationId,
|
|
749
|
+
_pendingEmail: {
|
|
750
|
+
to: normalizedEmail,
|
|
751
|
+
text: emailBody,
|
|
752
|
+
subject: EMAIL_VERIFICATION_SUBJECT,
|
|
753
|
+
assistantId,
|
|
754
|
+
},
|
|
654
755
|
};
|
|
655
756
|
}
|
|
656
757
|
|
|
@@ -851,12 +952,56 @@ export function resendOutbound(
|
|
|
851
952
|
originConversationId,
|
|
852
953
|
_pendingSlackDm: { userId: destination, text: slackBody, assistantId },
|
|
853
954
|
};
|
|
955
|
+
} else if (channel === "email") {
|
|
956
|
+
const newSession = createOutboundSession({
|
|
957
|
+
channel,
|
|
958
|
+
expectedExternalUserId: destination,
|
|
959
|
+
expectedChatId: destination,
|
|
960
|
+
identityBindingStatus: "bound",
|
|
961
|
+
destinationAddress: destination,
|
|
962
|
+
verificationPurpose: "guardian",
|
|
963
|
+
});
|
|
964
|
+
|
|
965
|
+
const emailBody = composeVerificationEmail(
|
|
966
|
+
GUARDIAN_VERIFY_TEMPLATE_KEYS.EMAIL_RESEND,
|
|
967
|
+
{
|
|
968
|
+
code: newSession.secret,
|
|
969
|
+
expiresInMinutes: Math.floor(SESSION_TTL_SECONDS / 60),
|
|
970
|
+
},
|
|
971
|
+
);
|
|
972
|
+
|
|
973
|
+
const now = Date.now();
|
|
974
|
+
const newSendCount = currentSendCount + 1;
|
|
975
|
+
const nextResendAt = now + RESEND_COOLDOWN_MS;
|
|
976
|
+
|
|
977
|
+
updateSessionDelivery(
|
|
978
|
+
newSession.sessionId,
|
|
979
|
+
now,
|
|
980
|
+
newSendCount,
|
|
981
|
+
nextResendAt,
|
|
982
|
+
);
|
|
983
|
+
|
|
984
|
+
return {
|
|
985
|
+
success: true,
|
|
986
|
+
verificationSessionId: newSession.sessionId,
|
|
987
|
+
secret: newSession.secret,
|
|
988
|
+
nextResendAt,
|
|
989
|
+
sendCount: newSendCount,
|
|
990
|
+
channel,
|
|
991
|
+
originConversationId,
|
|
992
|
+
_pendingEmail: {
|
|
993
|
+
to: destination,
|
|
994
|
+
text: emailBody,
|
|
995
|
+
subject: EMAIL_VERIFICATION_SUBJECT,
|
|
996
|
+
assistantId,
|
|
997
|
+
},
|
|
998
|
+
};
|
|
854
999
|
}
|
|
855
1000
|
|
|
856
1001
|
return {
|
|
857
1002
|
success: false,
|
|
858
1003
|
error: "unsupported_channel",
|
|
859
|
-
message: `Resend is only supported for Telegram, phone, and
|
|
1004
|
+
message: `Resend is only supported for Telegram, phone, Slack, and email. Got: ${channel}`,
|
|
860
1005
|
channel,
|
|
861
1006
|
};
|
|
862
1007
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Template-only copy for outbound guardian verification messages (Telegram, Slack, and voice).
|
|
2
|
+
* Template-only copy for outbound guardian verification messages (Telegram, Slack, email, and voice).
|
|
3
3
|
*
|
|
4
4
|
* All outbound verification messages are composed from these templates
|
|
5
5
|
* to prevent free-form caller/user text injection. Only typed variables
|
|
@@ -26,6 +26,10 @@ export const GUARDIAN_VERIFY_TEMPLATE_KEYS = {
|
|
|
26
26
|
"guardian_verify.slack.trusted_contact_challenge",
|
|
27
27
|
/** Resend Slack DM verification for inbound trusted contact (includes the code). */
|
|
28
28
|
SLACK_TRUSTED_CONTACT_RESEND: "guardian_verify.slack.trusted_contact_resend",
|
|
29
|
+
/** Initial outbound email verification prompt (includes the code). */
|
|
30
|
+
EMAIL_CHALLENGE_REQUEST: "guardian_verify.email.challenge_request",
|
|
31
|
+
/** Resend email verification prompt (includes the code). */
|
|
32
|
+
EMAIL_RESEND: "guardian_verify.email.resend",
|
|
29
33
|
/** Outbound voice call intro prompt: asks guardian to enter verification code via keypad. */
|
|
30
34
|
VOICE_CALL_INTRO: "guardian_verify.voice.call_intro",
|
|
31
35
|
/** Voice retry prompt after an incorrect code entry. */
|
|
@@ -42,7 +46,7 @@ export const GUARDIAN_VERIFY_TEMPLATE_KEYS = {
|
|
|
42
46
|
CHANNEL_BOOTSTRAP_BOUND: "guardian_verify.channel.bootstrap_bound",
|
|
43
47
|
} as const;
|
|
44
48
|
|
|
45
|
-
/** Template keys for Telegram/Slack text-based verification messages. */
|
|
49
|
+
/** Template keys for Telegram/Slack/email text-based verification messages. */
|
|
46
50
|
type TextVerifyTemplateKey =
|
|
47
51
|
| typeof GUARDIAN_VERIFY_TEMPLATE_KEYS.ALREADY_VERIFIED
|
|
48
52
|
| typeof GUARDIAN_VERIFY_TEMPLATE_KEYS.TELEGRAM_CHALLENGE_REQUEST
|
|
@@ -50,7 +54,9 @@ type TextVerifyTemplateKey =
|
|
|
50
54
|
| typeof GUARDIAN_VERIFY_TEMPLATE_KEYS.SLACK_CHALLENGE_REQUEST
|
|
51
55
|
| typeof GUARDIAN_VERIFY_TEMPLATE_KEYS.SLACK_RESEND
|
|
52
56
|
| typeof GUARDIAN_VERIFY_TEMPLATE_KEYS.SLACK_TRUSTED_CONTACT_CHALLENGE
|
|
53
|
-
| typeof GUARDIAN_VERIFY_TEMPLATE_KEYS.SLACK_TRUSTED_CONTACT_RESEND
|
|
57
|
+
| typeof GUARDIAN_VERIFY_TEMPLATE_KEYS.SLACK_TRUSTED_CONTACT_RESEND
|
|
58
|
+
| typeof GUARDIAN_VERIFY_TEMPLATE_KEYS.EMAIL_CHALLENGE_REQUEST
|
|
59
|
+
| typeof GUARDIAN_VERIFY_TEMPLATE_KEYS.EMAIL_RESEND;
|
|
54
60
|
|
|
55
61
|
/** Template keys for deterministic channel verification reply messages. */
|
|
56
62
|
type ChannelVerifyReplyTemplateKey =
|
|
@@ -115,6 +121,14 @@ const templates: Record<
|
|
|
115
121
|
[GUARDIAN_VERIFY_TEMPLATE_KEYS.SLACK_TRUSTED_CONTACT_RESEND]: (vars) => {
|
|
116
122
|
return `Vellum assistant verification: your code is ${vars.code}. Reply with this code to verify your identity. It expires in ${vars.expiresInMinutes} minutes. (resent)`;
|
|
117
123
|
},
|
|
124
|
+
|
|
125
|
+
[GUARDIAN_VERIFY_TEMPLATE_KEYS.EMAIL_CHALLENGE_REQUEST]: (vars) => {
|
|
126
|
+
return `Vellum assistant guardian verification requested.\n\nYour verification code is: ${vars.code}\n\nReply to this email with only the 6-digit code above to complete verification. This code expires in ${vars.expiresInMinutes} minutes.`;
|
|
127
|
+
},
|
|
128
|
+
|
|
129
|
+
[GUARDIAN_VERIFY_TEMPLATE_KEYS.EMAIL_RESEND]: (vars) => {
|
|
130
|
+
return `Vellum assistant guardian verification requested.\n\nYour new verification code is: ${vars.code}\n\nReply to this email with only the 6-digit code above to complete verification. This code expires in ${vars.expiresInMinutes} minutes. (resent)`;
|
|
131
|
+
},
|
|
118
132
|
};
|
|
119
133
|
|
|
120
134
|
/**
|
|
@@ -129,6 +143,18 @@ export function composeVerificationSlack(
|
|
|
129
143
|
return composer(vars);
|
|
130
144
|
}
|
|
131
145
|
|
|
146
|
+
/**
|
|
147
|
+
* Compose an outbound verification email from a template key and typed variables.
|
|
148
|
+
* Returns plain string content suitable for email delivery.
|
|
149
|
+
*/
|
|
150
|
+
export function composeVerificationEmail(
|
|
151
|
+
templateKey: TextVerifyTemplateKey,
|
|
152
|
+
vars: GuardianVerifyTemplateVars,
|
|
153
|
+
): string {
|
|
154
|
+
const composer = templates[templateKey];
|
|
155
|
+
return composer(vars);
|
|
156
|
+
}
|
|
157
|
+
|
|
132
158
|
/**
|
|
133
159
|
* Compose an outbound verification Telegram message from a template key and typed variables.
|
|
134
160
|
* Returns plain string content suitable for Telegram delivery.
|
|
@@ -3,8 +3,15 @@
|
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
6
|
+
import { existsSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
6
8
|
|
|
7
|
-
import {
|
|
9
|
+
import {
|
|
10
|
+
getApp,
|
|
11
|
+
getAppDirPath,
|
|
12
|
+
isMultifileApp,
|
|
13
|
+
resolveEffectiveAppHtml,
|
|
14
|
+
} from "../memory/app-store.js";
|
|
8
15
|
import {
|
|
9
16
|
getActivePublishedPageByAppId,
|
|
10
17
|
updatePublishedPage,
|
|
@@ -23,17 +30,32 @@ export async function updatePublishedAppDeployment(
|
|
|
23
30
|
const publishedPage = getActivePublishedPageByAppId(appId);
|
|
24
31
|
if (!publishedPage) return;
|
|
25
32
|
|
|
26
|
-
// 2. Load the app
|
|
33
|
+
// 2. Load the app and resolve its deployable HTML. For multifile apps the
|
|
34
|
+
// real content lives in dist/index.html (compiled from src/), not in
|
|
35
|
+
// htmlDefinition (which is "" for them) — using htmlDefinition directly
|
|
36
|
+
// would deploy a blank page.
|
|
27
37
|
const app = getApp(appId);
|
|
28
|
-
if (!app
|
|
29
|
-
log.warn({ appId }, "Published app not found
|
|
38
|
+
if (!app) {
|
|
39
|
+
log.warn({ appId }, "Published app not found");
|
|
30
40
|
return;
|
|
31
41
|
}
|
|
32
42
|
|
|
43
|
+
// Skip rather than deploy the compile-failure fallback when a multifile
|
|
44
|
+
// app has no compiled output (e.g. a concurrent compile failed); a later
|
|
45
|
+
// successful compile re-triggers this path.
|
|
46
|
+
if (
|
|
47
|
+
isMultifileApp(app) &&
|
|
48
|
+
!existsSync(join(getAppDirPath(app.id), "dist", "index.html"))
|
|
49
|
+
) {
|
|
50
|
+
log.warn({ appId }, "Skipping auto-redeploy: compiled output missing");
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const html = resolveEffectiveAppHtml(app);
|
|
55
|
+
if (!html) return;
|
|
56
|
+
|
|
33
57
|
// 3. Hash the current HTML and check if it changed
|
|
34
|
-
const newHash = createHash("sha256")
|
|
35
|
-
.update(app.htmlDefinition)
|
|
36
|
-
.digest("hex");
|
|
58
|
+
const newHash = createHash("sha256").update(html).digest("hex");
|
|
37
59
|
if (newHash === publishedPage.htmlHash) return; // No change
|
|
38
60
|
|
|
39
61
|
// 4. Get Vercel token — don't prompt, just skip if unavailable
|
|
@@ -46,7 +68,7 @@ export async function updatePublishedAppDeployment(
|
|
|
46
68
|
execute: async (token) => {
|
|
47
69
|
// 5. Deploy updated HTML using the same project slug
|
|
48
70
|
const result = await deployHtmlToVercel({
|
|
49
|
-
html
|
|
71
|
+
html,
|
|
50
72
|
name: slug,
|
|
51
73
|
token,
|
|
52
74
|
});
|
package/src/signals/cancel.ts
CHANGED
|
@@ -11,10 +11,8 @@ import { readFileSync } from "node:fs";
|
|
|
11
11
|
import { join } from "node:path";
|
|
12
12
|
|
|
13
13
|
import { getIsContainerized } from "../config/env-registry.js";
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
touchConversation,
|
|
17
|
-
} from "../daemon/conversation-store.js";
|
|
14
|
+
import { findConversation } from "../daemon/conversation-registry.js";
|
|
15
|
+
import { touchConversation } from "../daemon/conversation-store.js";
|
|
18
16
|
import { getSubagentManager } from "../subagent/index.js";
|
|
19
17
|
import { createAbortReason } from "../util/abort-reasons.js";
|
|
20
18
|
import { getLogger } from "../util/logger.js";
|
package/src/subagent/manager.ts
CHANGED
|
@@ -13,7 +13,11 @@ import { v4 as uuid } from "uuid";
|
|
|
13
13
|
import { resolveCallSiteConfig } from "../config/llm-resolver.js";
|
|
14
14
|
import { getConfig } from "../config/loader.js";
|
|
15
15
|
import { Conversation } from "../daemon/conversation.js";
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
findConversation,
|
|
18
|
+
removeSubagentConversation,
|
|
19
|
+
setSubagentConversation,
|
|
20
|
+
} from "../daemon/conversation-registry.js";
|
|
17
21
|
import type { ServerMessage } from "../daemon/message-protocol.js";
|
|
18
22
|
import { bootstrapConversation } from "../memory/conversation-bootstrap.js";
|
|
19
23
|
import { wrapWithCallSiteRouting } from "../providers/call-site-routing.js";
|
|
@@ -294,6 +298,10 @@ export class SubagentManager {
|
|
|
294
298
|
// This ensures interactive prompts (host attachment reads) fail fast.
|
|
295
299
|
conversation.updateClient(wrappedSendToClient, true);
|
|
296
300
|
conversation.setIsSubagent(true);
|
|
301
|
+
// Subagents are created as background conversations (see the
|
|
302
|
+
// `bootstrapConversation` call above) and never call `loadFromDb`, so cache
|
|
303
|
+
// the type on the live conversation directly for the runtime-assembly path.
|
|
304
|
+
conversation.conversationType = "background";
|
|
297
305
|
|
|
298
306
|
// Subagents execute as background child conversations, but their tool
|
|
299
307
|
// permissions must still be scoped to the actor that spawned them. Without
|
|
@@ -334,6 +342,10 @@ export class SubagentManager {
|
|
|
334
342
|
|
|
335
343
|
managed.conversation = conversation;
|
|
336
344
|
this.subagents.set(subagentId, managed);
|
|
345
|
+
// Index the live conversation so the per-conversation injectors (workspace
|
|
346
|
+
// context, disk-pressure warning) can resolve it by id; subagents are not
|
|
347
|
+
// in the eviction-managed conversation store.
|
|
348
|
+
setSubagentConversation(conversationRecord.id, conversation);
|
|
337
349
|
const labelKey = `${config.parentConversationId}:${config.label.toLowerCase().trim()}`;
|
|
338
350
|
if (this.labelIndex.has(labelKey)) {
|
|
339
351
|
log.warn(
|
|
@@ -695,7 +707,9 @@ export class SubagentManager {
|
|
|
695
707
|
*/
|
|
696
708
|
private releaseConversation(managed: ManagedSubagent): void {
|
|
697
709
|
if (!managed.conversation) return;
|
|
698
|
-
managed.conversation
|
|
710
|
+
const conversation = managed.conversation;
|
|
711
|
+
removeSubagentConversation(conversation.conversationId, conversation);
|
|
712
|
+
conversation.dispose();
|
|
699
713
|
managed.conversation = null;
|
|
700
714
|
managed.retainedUntil = Date.now() + TERMINAL_RETENTION_MS;
|
|
701
715
|
this.ensureSweepRunning();
|
|
@@ -716,16 +730,18 @@ export class SubagentManager {
|
|
|
716
730
|
if (!managed) return;
|
|
717
731
|
|
|
718
732
|
if (managed.conversation) {
|
|
733
|
+
const conversation = managed.conversation;
|
|
719
734
|
if (!TERMINAL_STATUSES.has(managed.state.status)) {
|
|
720
|
-
|
|
735
|
+
conversation.abort(
|
|
721
736
|
createAbortReason(
|
|
722
737
|
"subagent_aborted",
|
|
723
738
|
"SubagentManager.dispose",
|
|
724
|
-
|
|
739
|
+
conversation.conversationId,
|
|
725
740
|
),
|
|
726
741
|
);
|
|
727
742
|
}
|
|
728
|
-
|
|
743
|
+
removeSubagentConversation(conversation.conversationId, conversation);
|
|
744
|
+
conversation.dispose();
|
|
729
745
|
managed.conversation = null;
|
|
730
746
|
}
|
|
731
747
|
this.subagents.delete(subagentId);
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
ACTIVATION_MOMENT_PARAMS,
|
|
5
|
+
ACTIVATION_STEPS,
|
|
6
|
+
activationStepIndex,
|
|
7
|
+
activationStepNameForMomentParam,
|
|
8
|
+
buildActivationDaemonEventId,
|
|
9
|
+
isActivationMomentParam,
|
|
10
|
+
isActivationStepName,
|
|
11
|
+
} from "../activation-funnel.js";
|
|
12
|
+
|
|
13
|
+
const ALL_STEP_NAMES = Object.values(ACTIVATION_STEPS).map((s) => s.stepName);
|
|
14
|
+
|
|
15
|
+
describe("activation funnel vocabulary", () => {
|
|
16
|
+
test("step indices are 1-5 and unique", () => {
|
|
17
|
+
const indices = Object.values(ACTIVATION_STEPS).map((s) => s.stepIndex);
|
|
18
|
+
expect([...indices].sort((a, b) => a - b)).toEqual([1, 2, 3, 4, 5]);
|
|
19
|
+
expect(new Set(indices).size).toBe(indices.length);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("activationStepIndex resolves index from name", () => {
|
|
23
|
+
for (const { stepName, stepIndex } of Object.values(ACTIVATION_STEPS)) {
|
|
24
|
+
expect(activationStepIndex(stepName)).toBe(stepIndex);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
test("isActivationStepName accepts all five step names", () => {
|
|
29
|
+
expect(ALL_STEP_NAMES).toHaveLength(5);
|
|
30
|
+
for (const stepName of ALL_STEP_NAMES) {
|
|
31
|
+
expect(isActivationStepName(stepName)).toBe(true);
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("isActivationStepName rejects unknown names", () => {
|
|
36
|
+
expect(isActivationStepName("bogus")).toBe(false);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test("buildActivationDaemonEventId is deterministic", () => {
|
|
40
|
+
expect(
|
|
41
|
+
buildActivationDaemonEventId("conv-abc", "activation_moment_1_complete"),
|
|
42
|
+
).toBe("activation_v1_2026_06:conv-abc:activation_moment_1_complete");
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
describe("activation moment param (model-facing tag)", () => {
|
|
47
|
+
test("ACTIVATION_MOMENT_PARAMS lists the five tokens in step order", () => {
|
|
48
|
+
expect(ACTIVATION_MOMENT_PARAMS).toEqual([
|
|
49
|
+
"moment_1",
|
|
50
|
+
"moment_2",
|
|
51
|
+
"moment_3",
|
|
52
|
+
"first_wow_executed",
|
|
53
|
+
"first_wow_interacted",
|
|
54
|
+
]);
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test("isActivationMomentParam accepts all five tokens", () => {
|
|
58
|
+
for (const param of ACTIVATION_MOMENT_PARAMS) {
|
|
59
|
+
expect(isActivationMomentParam(param)).toBe(true);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
test("isActivationMomentParam rejects unknown tokens (incl. wire step names)", () => {
|
|
64
|
+
expect(isActivationMomentParam("bogus")).toBe(false);
|
|
65
|
+
// The wire step name is NOT a valid model-facing token.
|
|
66
|
+
expect(isActivationMomentParam("activation_moment_1_complete")).toBe(false);
|
|
67
|
+
expect(isActivationMomentParam("moment_4")).toBe(false);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("activationStepNameForMomentParam maps each token to its step name", () => {
|
|
71
|
+
expect(activationStepNameForMomentParam("moment_1")).toBe(
|
|
72
|
+
ACTIVATION_STEPS.moment1.stepName,
|
|
73
|
+
);
|
|
74
|
+
expect(activationStepNameForMomentParam("moment_2")).toBe(
|
|
75
|
+
ACTIVATION_STEPS.moment2.stepName,
|
|
76
|
+
);
|
|
77
|
+
expect(activationStepNameForMomentParam("moment_3")).toBe(
|
|
78
|
+
ACTIVATION_STEPS.moment3.stepName,
|
|
79
|
+
);
|
|
80
|
+
expect(activationStepNameForMomentParam("first_wow_executed")).toBe(
|
|
81
|
+
ACTIVATION_STEPS.firstWowExecuted.stepName,
|
|
82
|
+
);
|
|
83
|
+
expect(activationStepNameForMomentParam("first_wow_interacted")).toBe(
|
|
84
|
+
ACTIVATION_STEPS.firstWowInteracted.stepName,
|
|
85
|
+
);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("every moment param maps to a valid wire step name", () => {
|
|
89
|
+
for (const param of ACTIVATION_MOMENT_PARAMS) {
|
|
90
|
+
expect(
|
|
91
|
+
isActivationStepName(activationStepNameForMomentParam(param)),
|
|
92
|
+
).toBe(true);
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
});
|