@vellumai/assistant 0.8.8 → 0.8.9-dev.202606091926.ebb2d62
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +6 -6
- package/bun.lock +2 -2
- package/docs/activation-funnel-telemetry.md +310 -0
- package/examples/plugins/echo/README.md +61 -60
- package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
- package/examples/plugins/echo/hooks/stop.ts +16 -0
- package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
- package/examples/plugins/echo/package.json +1 -2
- package/examples/plugins/echo/src/emit.ts +19 -0
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
- package/openapi.yaml +273 -121
- package/package.json +2 -2
- package/scripts/sync-llm-catalog.ts +2 -0
- package/src/__tests__/activation-early-marking.test.ts +120 -0
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +69 -14
- package/src/__tests__/agent-loop-exit-reason.test.ts +204 -144
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +50 -35
- package/src/__tests__/agent-loop-output-hooks.test.ts +357 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +25 -6
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +41 -21
- package/src/__tests__/agent-loop-thinking.test.ts +36 -20
- package/src/__tests__/agent-loop.test.ts +441 -96
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +14 -14
- package/src/__tests__/agent-wake-override-profile.test.ts +17 -21
- package/src/__tests__/anthropic-provider.test.ts +23 -8
- package/src/__tests__/app-builder-tool-scripts.test.ts +21 -0
- package/src/__tests__/app-control-flow.test.ts +1 -1
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +132 -0
- package/src/__tests__/approval-cascade.test.ts +6 -5
- package/src/__tests__/approval-routes-http.test.ts +4 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
- package/src/__tests__/build-persisted-content.test.ts +75 -1
- package/src/__tests__/channel-approval-routes.test.ts +1 -1
- package/src/__tests__/channel-approvals.test.ts +1 -1
- package/src/__tests__/compaction-circuit.test.ts +258 -0
- package/src/__tests__/compaction-direct.test.ts +146 -0
- package/src/__tests__/compaction-events.test.ts +7 -7
- package/src/__tests__/compaction.benchmark.test.ts +1 -1
- package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
- package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
- package/src/__tests__/context-overflow-reducer.test.ts +265 -125
- package/src/__tests__/context-window-manager-compact-retry.test.ts +121 -15
- package/src/__tests__/conversation-abort-tool-results.test.ts +7 -5
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +7 -10
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +78 -119
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +158 -234
- package/src/__tests__/conversation-agent-loop.test.ts +342 -623
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +6 -5
- package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
- package/src/__tests__/conversation-error.test.ts +15 -1
- package/src/__tests__/conversation-history-web-search.test.ts +6 -1
- package/src/__tests__/conversation-media-retry.test.ts +1 -1
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +44 -4
- package/src/__tests__/conversation-process-callsite.test.ts +15 -15
- package/src/__tests__/conversation-provider-retry-repair.test.ts +66 -65
- package/src/__tests__/conversation-queue.test.ts +10 -10
- package/src/__tests__/conversation-runtime-assembly.test.ts +1059 -231
- package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
- package/src/__tests__/conversation-slash-queue.test.ts +7 -5
- package/src/__tests__/conversation-slash-unknown.test.ts +7 -5
- package/src/__tests__/conversation-speed-override.test.ts +11 -10
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +395 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +48 -1
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +73 -4
- package/src/__tests__/conversation-undo.test.ts +2 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +24 -21
- package/src/__tests__/conversation-workspace-injection.test.ts +69 -7
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +15 -12
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/corrected-target.test.ts +93 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +5 -4
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +36 -0
- package/src/__tests__/db-acp-history.test.ts +101 -0
- package/src/__tests__/dynamic-page-surface.test.ts +31 -0
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
- package/src/__tests__/empty-response-hook.test.ts +1 -1
- package/src/__tests__/file-write-tool.test.ts +63 -0
- package/src/__tests__/gateway-only-guard.test.ts +17 -2
- package/src/__tests__/guardian-grant-minting.test.ts +1 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +93 -4
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
- package/src/__tests__/heartbeat-disk-pressure.test.ts +1 -0
- package/src/__tests__/heartbeat-service.test.ts +1 -0
- package/src/__tests__/history-repair-hook.test.ts +5 -1
- package/src/__tests__/host-app-control-proxy.test.ts +45 -0
- package/src/__tests__/host-app-control-routes.test.ts +1 -1
- package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
- package/src/__tests__/inference-profile-reaper.test.ts +62 -0
- package/src/__tests__/inference-profile-session-handler.test.ts +86 -0
- package/src/__tests__/injector-background-turn.test.ts +13 -23
- package/src/__tests__/injector-chain.test.ts +268 -44
- package/src/__tests__/injector-disk-pressure.test.ts +210 -52
- package/src/__tests__/injector-document-comments.test.ts +97 -114
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
- package/src/__tests__/injector-v3-suppression.test.ts +4 -4
- package/src/__tests__/list-messages-client-message-id.test.ts +91 -0
- package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
- package/src/__tests__/llm-catalog-parity.test.ts +4 -0
- package/src/__tests__/llm-resolver.test.ts +73 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +156 -38
- package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
- package/src/__tests__/notification-decision-strategy.test.ts +3 -3
- package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +145 -0
- package/src/__tests__/persist-unsendable-image.test.ts +97 -1
- package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
- package/src/__tests__/plugin-api-shim.test.ts +6 -9
- package/src/__tests__/plugin-bootstrap.test.ts +91 -81
- package/src/__tests__/plugin-registry.test.ts +3 -49
- package/src/__tests__/plugin-skill-contribution.test.ts +15 -14
- package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
- package/src/__tests__/plugin-types.test.ts +0 -70
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +69 -0
- package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
- package/src/__tests__/published-app-updater.test.ts +138 -0
- package/src/__tests__/reaction-persistence.test.ts +1 -1
- package/src/__tests__/retry-thinking-adaptive-only.test.ts +245 -0
- package/src/__tests__/send-endpoint-busy.test.ts +4 -1
- package/src/__tests__/server-history-render.test.ts +39 -0
- package/src/__tests__/skill-feature-flags-integration.test.ts +31 -0
- package/src/__tests__/steer-tool-repair.test.ts +1 -1
- package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
- package/src/__tests__/subagent-detail.test.ts +25 -7
- package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
- package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
- package/src/__tests__/subagent-manager-notify.test.ts +1 -3
- package/src/__tests__/subagent-notify-parent.test.ts +1 -3
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
- package/src/__tests__/title-generate-hook.test.ts +5 -1
- package/src/__tests__/tool-error-hook.test.ts +1 -1
- package/src/__tests__/tool-result-truncate-hook.test.ts +1 -1
- package/src/__tests__/tool-start-timestamp.test.ts +218 -0
- package/src/__tests__/user-plugin-loader.test.ts +54 -286
- package/src/__tests__/web-fetch.test.ts +45 -0
- package/src/acp/__tests__/agent-process.test.ts +161 -0
- package/src/acp/__tests__/client-handler.test.ts +40 -0
- package/src/acp/__tests__/helpers/acp-config-stub.ts +0 -2
- package/src/acp/__tests__/helpers/acp-history-db.ts +82 -0
- package/src/acp/__tests__/helpers/exec-file-stub.ts +106 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +97 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
- package/src/acp/__tests__/session-manager-resume.test.ts +888 -0
- package/src/acp/agent-process.ts +61 -1
- package/src/acp/auto-install.test.ts +280 -0
- package/src/acp/auto-install.ts +232 -0
- package/src/acp/client-handler.ts +31 -0
- package/src/acp/prepare-agent-env.ts +80 -27
- package/src/acp/resolve-agent.test.ts +188 -28
- package/src/acp/resolve-agent.ts +119 -42
- package/src/acp/resume-hint.ts +23 -0
- package/src/acp/session-manager.ts +507 -73
- package/src/agent/compaction-circuit.ts +60 -102
- package/src/agent/loop.ts +403 -251
- package/src/api/events/tool-result.ts +9 -0
- package/src/api/events/tool-use-start.ts +7 -0
- package/src/api/responses/conversation-message.ts +72 -13
- package/src/api/responses/memory-v3-selection-log.ts +19 -10
- package/src/approvals/guardian-request-resolvers.ts +27 -1
- package/src/background-wake/next-wake.ts +1 -0
- package/src/cli/commands/__tests__/memory-v3.test.ts +191 -210
- package/src/cli/commands/channel-verification-sessions.ts +6 -6
- package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
- package/src/cli/commands/memory-v3.ts +57 -199
- package/src/cli/commands/plugins.ts +43 -37
- package/src/cli/lib/__tests__/install-from-github.test.ts +790 -111
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
- package/src/cli/lib/__tests__/plugin-details.test.ts +381 -0
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +270 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +233 -32
- package/src/cli/lib/install-from-github.ts +797 -60
- package/src/cli/lib/plugin-catalog-cache.ts +84 -0
- package/src/cli/lib/plugin-details.ts +416 -0
- package/src/cli/lib/plugin-marketplace.ts +215 -0
- package/src/cli/lib/search-plugins.ts +201 -29
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
- package/src/config/acp-defaults.test.ts +10 -0
- package/src/config/acp-defaults.ts +9 -3
- package/src/config/acp-schema.ts +1 -7
- package/src/config/bundled-skills/acp/SKILL.md +80 -41
- package/src/config/bundled-skills/acp/TOOLS.json +4 -4
- package/src/config/bundled-skills/app-builder/SKILL.md +224 -381
- package/src/config/bundled-skills/app-builder/TOOLS.json +72 -2
- package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
- package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +18 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
- package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
- package/src/config/bundled-tool-registry.ts +4 -0
- package/src/config/call-site-defaults.ts +0 -3
- package/src/config/feature-flag-registry.json +10 -56
- package/src/config/llm-resolver.ts +39 -7
- package/src/config/loader.ts +5 -22
- package/src/config/schemas/__tests__/memory-v3.test.ts +25 -9
- package/src/config/schemas/call-site-catalog.ts +0 -21
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +17 -3
- package/src/config/schemas/memory-v3.ts +58 -8
- package/src/config/seed-inference-profiles.ts +18 -0
- package/src/context/compactor.ts +22 -4
- package/src/context/post-turn-tool-result-truncation.ts +39 -1
- package/src/context/strip-injections.ts +8 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +120 -42
- package/src/daemon/conversation-agent-loop.ts +308 -1297
- package/src/daemon/conversation-error.ts +31 -4
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +11 -255
- package/src/daemon/conversation-media-retry.ts +19 -6
- package/src/daemon/conversation-messaging.ts +17 -0
- package/src/daemon/conversation-process.ts +22 -141
- package/src/daemon/conversation-queue-manager.ts +8 -0
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +361 -393
- package/src/daemon/conversation-store.ts +9 -90
- package/src/daemon/conversation-surfaces.ts +165 -11
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +444 -62
- package/src/daemon/disk-pressure-policy.ts +0 -1
- package/src/daemon/external-plugins-bootstrap.ts +36 -46
- package/src/daemon/handlers/config-channels.ts +11 -2
- package/src/daemon/handlers/conversations.ts +3 -1
- package/src/daemon/handlers/shared.ts +9 -1
- package/src/daemon/handlers/skills.ts +4 -1
- package/src/daemon/host-app-control-proxy.ts +72 -57
- package/src/daemon/host-proxy-preactivation.ts +1 -3
- package/src/daemon/lifecycle.ts +21 -7
- package/src/daemon/persist-unsendable-image.ts +62 -25
- package/src/daemon/process-message.ts +1 -1
- package/src/daemon/server.ts +2 -0
- package/src/daemon/tool-side-effects.ts +15 -0
- package/src/daemon/wake-conversation-ops.ts +269 -0
- package/src/embedded/plugin-api.ts +2 -2
- package/src/export/__tests__/transcript-formatter.test.ts +5 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
- package/src/heartbeat/heartbeat-run-store.ts +23 -1
- package/src/heartbeat/heartbeat-service.ts +26 -0
- package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
- package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
- package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
- package/src/ipc/skill-routes/memory.ts +4 -2
- package/src/memory/__tests__/activation-session-store.test.ts +41 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +87 -0
- package/src/memory/__tests__/onboarding-events-store.test.ts +80 -0
- package/src/memory/activation-session-store.ts +43 -0
- package/src/memory/conversation-crud.ts +29 -19
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/db-init.ts +6 -0
- package/src/memory/embedding-backend.ts +5 -22
- package/src/memory/job-handlers/conversation-starters.ts +13 -2
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -4
- package/src/memory/jobs-worker.ts +25 -1
- package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
- package/src/memory/migrations/273-onboarding-events-funnel-columns.ts +46 -0
- package/src/memory/migrations/274-create-activation-sessions.ts +15 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/onboarding-events-store.ts +66 -18
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/schema/infrastructure.ts +13 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -97
- package/src/memory/v2/__tests__/page-store.test.ts +22 -0
- package/src/memory/v2/consolidation-job.ts +2 -63
- package/src/memory/v2/types.ts +5 -0
- package/src/messaging/providers/telegram-bot/api.ts +14 -5
- package/src/notifications/__tests__/copy-composer.test.ts +244 -0
- package/src/notifications/access-request-copy.ts +298 -0
- package/src/notifications/adapters/slack.ts +3 -3
- package/src/notifications/adapters/telegram.ts +9 -2
- package/src/notifications/copy-composer.ts +49 -267
- package/src/notifications/decision-engine.ts +16 -35
- package/src/notifications/home-feed-side-effect.ts +1 -6
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +40 -11
- package/src/plugin-api/types.ts +108 -0
- package/src/plugins/defaults/compaction/compact.ts +106 -0
- package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +250 -42
- package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
- package/src/plugins/defaults/compaction/manager-store.ts +57 -0
- package/src/plugins/defaults/compaction/package.json +1 -2
- package/src/{context → plugins/defaults/compaction}/window-manager.ts +95 -25
- package/src/plugins/defaults/empty-response/package.json +0 -1
- package/src/plugins/defaults/history-repair/package.json +0 -1
- package/src/plugins/defaults/index.ts +156 -26
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +285 -0
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +2 -2
- package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +148 -73
- package/src/plugins/defaults/memory-retrieval/package.json +14 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +223 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/capabilities.test.ts +71 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/dense.test.ts +181 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/edge.test.ts +247 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +147 -134
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +456 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +662 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +306 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +91 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-dense-store.test.ts +402 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/section-needle.test.ts +135 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/sections.test.ts +125 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +74 -19
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +446 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +592 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -24
- package/src/plugins/defaults/memory-v3-shadow/capabilities.ts +91 -0
- package/src/plugins/defaults/memory-v3-shadow/dense.ts +97 -0
- package/src/plugins/defaults/memory-v3-shadow/edge.ts +252 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/post-compact.ts +14 -0
- package/src/plugins/defaults/memory-v3-shadow/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +76 -0
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +555 -0
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +266 -0
- package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
- package/src/plugins/defaults/memory-v3-shadow/page-content.ts +71 -0
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +204 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +16 -8
- package/src/plugins/defaults/memory-v3-shadow/section-dense-store.ts +236 -0
- package/src/plugins/defaults/memory-v3-shadow/section-needle.ts +200 -0
- package/src/plugins/defaults/memory-v3-shadow/sections.ts +115 -0
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +156 -0
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +382 -0
- package/src/plugins/defaults/memory-v3-shadow/types.ts +92 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +4 -1
- package/src/plugins/defaults/title-generate/package.json +0 -1
- package/src/plugins/defaults/tool-error/package.json +0 -1
- package/src/plugins/defaults/tool-result-truncate/package.json +0 -1
- package/src/plugins/external-api.ts +12 -2
- package/src/plugins/pipeline.ts +6 -293
- package/src/plugins/registry.ts +9 -37
- package/src/plugins/types.ts +76 -381
- package/src/plugins/user-loader.ts +30 -127
- package/src/prompts/__tests__/system-prompt.test.ts +6 -0
- package/src/prompts/system-prompt.ts +61 -10
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +71 -4
- package/src/providers/anthropic/client.ts +9 -10
- package/src/providers/inference/kimi-cjk-token-ids.ts +493 -0
- package/src/providers/inference/logit-bias.ts +55 -0
- package/src/providers/model-catalog.ts +57 -0
- package/src/providers/openai/__tests__/vision-not-supported.test.ts +75 -0
- package/src/providers/openai/chat-completions-provider.ts +44 -1
- package/src/providers/retry.ts +57 -7
- package/src/providers/types.ts +6 -0
- package/src/runtime/__tests__/agent-wake.test.ts +555 -691
- package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
- package/src/runtime/agent-wake.ts +108 -209
- package/src/runtime/assistant-event-hub.ts +1 -1
- package/src/runtime/channel-approvals.ts +1 -1
- package/src/runtime/interactive-ui.ts +1 -1
- package/src/runtime/routes/__tests__/acp-routes.test.ts +466 -55
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +423 -73
- package/src/runtime/routes/__tests__/stt-routes.test.ts +112 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
- package/src/runtime/routes/acp-routes.test.ts +185 -32
- package/src/runtime/routes/acp-routes.ts +328 -30
- package/src/runtime/routes/app-management-routes.ts +3 -0
- package/src/runtime/routes/approval-routes.ts +1 -1
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/browser-tabs-routes.ts +6 -10
- package/src/runtime/routes/channel-verification-routes.ts +14 -5
- package/src/runtime/routes/conversation-cli-routes.ts +1 -1
- package/src/runtime/routes/conversation-list-routes.ts +1 -1
- package/src/runtime/routes/conversation-query-routes.ts +1 -1
- package/src/runtime/routes/conversation-routes.ts +30 -2
- package/src/runtime/routes/conversation-starter-routes.ts +13 -7
- package/src/runtime/routes/conversations-import-routes.ts +24 -7
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-cu-routes.ts +1 -1
- package/src/runtime/routes/identity-routes.ts +18 -3
- package/src/runtime/routes/inbound-message-handler.ts +1 -1
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
- package/src/runtime/routes/inference-profile-session-handler.ts +11 -0
- package/src/runtime/routes/inference-profile-session-reaper.ts +6 -0
- package/src/runtime/routes/memory-v3-routes.ts +65 -305
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +1 -1
- package/src/runtime/routes/playground/helpers.ts +1 -1
- package/src/runtime/routes/plugins-routes.ts +337 -35
- package/src/runtime/routes/stt-routes.ts +45 -12
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/work-items-routes.ts +2 -4
- package/src/runtime/routes/workspace-routes.ts +50 -15
- package/src/runtime/services/conversation-serializer.ts +1 -1
- package/src/runtime/verification-outbound-actions.ts +147 -2
- package/src/runtime/verification-templates.ts +29 -3
- package/src/services/published-app-updater.ts +30 -8
- package/src/signals/cancel.ts +2 -4
- package/src/subagent/manager.ts +21 -5
- package/src/telemetry/__tests__/activation-funnel.test.ts +95 -0
- package/src/telemetry/activation-funnel.ts +167 -0
- package/src/telemetry/types.ts +13 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +154 -0
- package/src/telemetry/usage-telemetry-reporter.ts +26 -1
- package/src/tools/acp/context.ts +20 -0
- package/src/tools/acp/list-agents.test.ts +9 -19
- package/src/tools/acp/list-agents.ts +3 -15
- package/src/tools/acp/spawn.test.ts +173 -202
- package/src/tools/acp/spawn.ts +37 -172
- package/src/tools/acp/steer.test.ts +105 -8
- package/src/tools/acp/steer.ts +48 -17
- package/src/tools/apps/executors.ts +166 -50
- package/src/tools/browser/browser-execution.ts +12 -2
- package/src/tools/filesystem/write.ts +34 -0
- package/src/tools/network/web-fetch.ts +65 -24
- package/src/tools/skills/load.ts +1 -1
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/ui-surface/definitions.ts +32 -5
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +69 -45
- package/docs/plugins.md +0 -832
- package/examples/plugins/echo/register.ts +0 -143
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
- package/src/__tests__/compaction-pipeline.test.ts +0 -210
- package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
- package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -667
- package/src/__tests__/pipeline-runner.test.ts +0 -554
- package/src/daemon/wake-target-adapter.ts +0 -253
- package/src/memory/v3/__tests__/assign.test.ts +0 -242
- package/src/memory/v3/__tests__/capabilities.test.ts +0 -118
- package/src/memory/v3/__tests__/core.test.ts +0 -39
- package/src/memory/v3/__tests__/health.test.ts +0 -219
- package/src/memory/v3/__tests__/maintain-job.test.ts +0 -288
- package/src/memory/v3/__tests__/needle.test.ts +0 -107
- package/src/memory/v3/__tests__/orchestrate.test.ts +0 -436
- package/src/memory/v3/__tests__/provider-blocks.test.ts +0 -13
- package/src/memory/v3/__tests__/reconcile.test.ts +0 -274
- package/src/memory/v3/__tests__/render-injection.test.ts +0 -61
- package/src/memory/v3/__tests__/router.test.ts +0 -332
- package/src/memory/v3/__tests__/selector.test.ts +0 -470
- package/src/memory/v3/__tests__/shadow-plugin.test.ts +0 -432
- package/src/memory/v3/__tests__/snapshot.test.ts +0 -168
- package/src/memory/v3/__tests__/tree.test.ts +0 -192
- package/src/memory/v3/assign.ts +0 -268
- package/src/memory/v3/capabilities.ts +0 -124
- package/src/memory/v3/core.ts +0 -26
- package/src/memory/v3/health.ts +0 -0
- package/src/memory/v3/maintain-job.ts +0 -314
- package/src/memory/v3/needle.ts +0 -115
- package/src/memory/v3/orchestrate.ts +0 -126
- package/src/memory/v3/page-content.ts +0 -34
- package/src/memory/v3/provider-blocks.ts +0 -26
- package/src/memory/v3/reconcile.ts +0 -523
- package/src/memory/v3/router.ts +0 -190
- package/src/memory/v3/selection-log-store.ts +0 -84
- package/src/memory/v3/selector.ts +0 -226
- package/src/memory/v3/shadow-plugin.ts +0 -411
- package/src/memory/v3/snapshot.ts +0 -209
- package/src/memory/v3/tree.ts +0 -174
- package/src/memory/v3/types.ts +0 -59
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
- package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
- package/src/plugins/defaults/circuit-breaker/package.json +0 -15
- package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
- package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
- package/src/plugins/defaults/compaction/register.ts +0 -35
- package/src/plugins/defaults/compaction/terminal.ts +0 -73
- package/src/plugins/defaults/empty-response/register.ts +0 -23
- package/src/plugins/defaults/history-repair/register.ts +0 -24
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +0 -216
- package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
- package/src/plugins/defaults/overflow-reduce/package.json +0 -15
- package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
- package/src/plugins/defaults/title-generate/register.ts +0 -35
- package/src/plugins/defaults/tool-error/register.ts +0 -23
- package/src/plugins/defaults/tool-result-truncate/register.ts +0 -24
- package/src/proactive-artifact/aux-message-injector.ts +0 -97
- package/src/proactive-artifact/decision.test.ts +0 -226
- package/src/proactive-artifact/decision.ts +0 -165
- package/src/proactive-artifact/index.ts +0 -7
- package/src/proactive-artifact/job.test.ts +0 -962
- package/src/proactive-artifact/job.ts +0 -372
- package/src/proactive-artifact/message-copy.ts +0 -58
- package/src/proactive-artifact/trigger-state.test.ts +0 -286
- package/src/proactive-artifact/trigger-state.ts +0 -123
- package/src/util/map-limit.ts +0 -27
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/eval-turns.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/fixtures/live-turns.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-eviction.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/working-set-skeleton.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/README.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/assignments.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/core.json +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-x.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-a/topic-y.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/data/leaves/domain-b/topic-z.md +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/llm-retry.ts +0 -0
|
@@ -1,39 +1,23 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* User plugin loader — discovers plugins under `<workspaceDir>/plugins/*`
|
|
3
|
-
*
|
|
2
|
+
* User plugin loader — discovers plugins under `<workspaceDir>/plugins/*` and
|
|
3
|
+
* registers each one.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* `
|
|
11
|
-
* convention.
|
|
12
|
-
*
|
|
13
|
-
* **Legacy path** (`register.{ts,js}` present): the file is dynamic-imported
|
|
14
|
-
* and expected to call {@link registerPlugin} at import time as a side
|
|
15
|
-
* effect, populating the registry before {@link bootstrapPlugins} runs.
|
|
16
|
-
*
|
|
17
|
-
* The legacy path takes precedence when a directory contains both
|
|
18
|
-
* `package.json` and `register.{ts,js}` — a migration-friendly default
|
|
19
|
-
* that keeps existing plugins (including the in-repo `examples/plugins/echo`
|
|
20
|
-
* reference) working unchanged while we iterate the external-plugin
|
|
21
|
-
* convention. A directory matching neither path is skipped silently.
|
|
5
|
+
* A plugin directory is recognized by a `package.json` manifest. The harness
|
|
6
|
+
* delegates to {@link loadExternalPlugin}, which builds a `Plugin` from the
|
|
7
|
+
* directory's interface dirs (`hooks/`, `tools/`) and registers it directly.
|
|
8
|
+
* The full convention lives in
|
|
9
|
+
* `assistant/src/plugins/external-plugin-loader.ts`. A directory with no
|
|
10
|
+
* `package.json` is skipped silently.
|
|
22
11
|
*
|
|
23
12
|
* The loader deliberately:
|
|
24
13
|
*
|
|
25
14
|
* - Uses `getWorkspaceDir()` so each instance loads its own plugin set
|
|
26
15
|
* when `VELLUM_WORKSPACE_DIR` is set.
|
|
27
|
-
* - Prefers `.js` over `.ts` per surface file (compiled-binary semantics)
|
|
28
|
-
*
|
|
29
|
-
* legacy path picks between `register.js` and `register.ts`.
|
|
16
|
+
* - Prefers `.js` over `.ts` per surface file (compiled-binary semantics);
|
|
17
|
+
* the rule is applied by {@link loadExternalPlugin}.
|
|
30
18
|
* - Treats any error from a plugin load as a per-plugin isolation
|
|
31
|
-
* boundary. {@link loadExternalPlugin} owns its own try/catch/timeout
|
|
32
|
-
*
|
|
33
|
-
* the daemon.
|
|
34
|
-
* - Bounds each plugin load with {@link USER_PLUGIN_IMPORT_TIMEOUT_MS}
|
|
35
|
-
* so a plugin whose top-level `await` hangs or whose module evaluation
|
|
36
|
-
* never resolves cannot stall daemon startup.
|
|
19
|
+
* boundary. {@link loadExternalPlugin} owns its own try/catch/timeout, so
|
|
20
|
+
* one bad user plugin must not crash the daemon.
|
|
37
21
|
*
|
|
38
22
|
* Call order relative to the rest of the plugin system:
|
|
39
23
|
*
|
|
@@ -46,7 +30,6 @@
|
|
|
46
30
|
|
|
47
31
|
import { existsSync, readdirSync, statSync } from "node:fs";
|
|
48
32
|
import { join } from "node:path";
|
|
49
|
-
import { pathToFileURL } from "node:url";
|
|
50
33
|
|
|
51
34
|
import { getLogger } from "../util/logger.js";
|
|
52
35
|
import { getWorkspacePluginsDir } from "../util/platform.js";
|
|
@@ -57,27 +40,24 @@ import { closeRegistration } from "./registry.js";
|
|
|
57
40
|
const log = getLogger("user-plugin-loader");
|
|
58
41
|
|
|
59
42
|
/**
|
|
60
|
-
* Upper bound on how long a single user plugin's
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
* same way thrown-error plugins do.
|
|
43
|
+
* Upper bound on how long a single user plugin's load may take. A plugin with
|
|
44
|
+
* a hanging top-level `await` (or a never-resolving module evaluation) would
|
|
45
|
+
* otherwise block daemon startup indefinitely. Ten seconds is generous
|
|
46
|
+
* relative to a typical plugin load (milliseconds) and matches the per-plugin
|
|
47
|
+
* isolation contract: slow plugins get skipped the same way thrown-error
|
|
48
|
+
* plugins do. Enforced by {@link loadExternalPlugin}.
|
|
67
49
|
*/
|
|
68
50
|
const USER_PLUGIN_IMPORT_TIMEOUT_MS = 10_000;
|
|
69
51
|
|
|
70
52
|
/**
|
|
71
|
-
* Scan `getWorkspaceDir()/plugins/` for subdirectories
|
|
72
|
-
*
|
|
73
|
-
* `register.{ts,js}`) or the legacy side-effect importer (if
|
|
74
|
-
* `register.{ts,js}` is present).
|
|
53
|
+
* Scan `getWorkspaceDir()/plugins/` for subdirectories and dispatch each one
|
|
54
|
+
* that carries a `package.json` to {@link loadExternalPlugin}.
|
|
75
55
|
*
|
|
76
56
|
* Invariants:
|
|
77
57
|
*
|
|
78
58
|
* - No-ops when `getWorkspaceDir()/plugins/` does not exist — a clean install with
|
|
79
59
|
* zero user plugins must not generate errors.
|
|
80
|
-
* - Per-plugin isolation: a failing
|
|
60
|
+
* - Per-plugin isolation: a failing load is logged and skipped. The
|
|
81
61
|
* function resolves normally even when every plugin fails to load.
|
|
82
62
|
* - Does not return plugin instances. The registry is the single source of
|
|
83
63
|
* truth for who got registered, and the caller inspects it directly.
|
|
@@ -86,7 +66,7 @@ const USER_PLUGIN_IMPORT_TIMEOUT_MS = 10_000;
|
|
|
86
66
|
*
|
|
87
67
|
* - Must be invoked exactly once during daemon startup, before
|
|
88
68
|
* `bootstrapPlugins()` walks the registry.
|
|
89
|
-
* - Holds no locks during the
|
|
69
|
+
* - Holds no locks during the load — bun's dynamic `import()` resolution
|
|
90
70
|
* is concurrency-safe.
|
|
91
71
|
*/
|
|
92
72
|
export async function loadUserPlugins(
|
|
@@ -151,96 +131,19 @@ export async function loadUserPlugins(
|
|
|
151
131
|
}
|
|
152
132
|
if (!stats.isDirectory()) continue;
|
|
153
133
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
// Migration-friendly: any plugin in the wild today that happens to
|
|
157
|
-
// ship a `package.json` keeps loading via its existing register entry.
|
|
158
|
-
// The external-plugin path only fires when the directory is
|
|
159
|
-
// unambiguously the new convention.
|
|
160
|
-
const jsPath = join(pluginDir, "register.js");
|
|
161
|
-
const tsPath = join(pluginDir, "register.ts");
|
|
162
|
-
let registerPath: string | undefined;
|
|
163
|
-
if (existsSync(jsPath)) {
|
|
164
|
-
registerPath = jsPath;
|
|
165
|
-
} else if (existsSync(tsPath)) {
|
|
166
|
-
registerPath = tsPath;
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
if (registerPath === undefined) {
|
|
170
|
-
// External plugin framework path. `loadExternalPlugin` owns its own
|
|
171
|
-
// try/catch + timeout, so a `continue` is the entire branch here.
|
|
172
|
-
if (existsSync(join(pluginDir, "package.json"))) {
|
|
173
|
-
await loadExternalPlugin(pluginDir, { importTimeoutMs });
|
|
174
|
-
continue;
|
|
175
|
-
}
|
|
176
|
-
log.debug(
|
|
177
|
-
{ pluginDir },
|
|
178
|
-
"loadUserPlugins: no register.{ts,js} or package.json — skipping",
|
|
179
|
-
);
|
|
134
|
+
if (!existsSync(join(pluginDir, "package.json"))) {
|
|
135
|
+
log.debug({ pluginDir }, "loadUserPlugins: no package.json — skipping");
|
|
180
136
|
continue;
|
|
181
137
|
}
|
|
182
138
|
|
|
183
|
-
//
|
|
184
|
-
//
|
|
185
|
-
|
|
186
|
-
const moduleUrl = pathToFileURL(registerPath).href;
|
|
187
|
-
let timeoutHandle: ReturnType<typeof setTimeout> | undefined;
|
|
188
|
-
try {
|
|
189
|
-
// Race the import against a timeout so a plugin with a hanging top-level
|
|
190
|
-
// await or never-resolving module evaluation cannot stall daemon startup.
|
|
191
|
-
// The per-plugin try/catch already handles thrown errors; this extends
|
|
192
|
-
// the isolation boundary to cover hung promises as well.
|
|
193
|
-
const timeoutSentinel = Symbol("user-plugin-import-timeout");
|
|
194
|
-
const timeoutPromise = new Promise<typeof timeoutSentinel>((resolve) => {
|
|
195
|
-
timeoutHandle = setTimeout(
|
|
196
|
-
() => resolve(timeoutSentinel),
|
|
197
|
-
importTimeoutMs,
|
|
198
|
-
);
|
|
199
|
-
});
|
|
200
|
-
// Retain the import promise so we can attach a terminal `.catch` on the
|
|
201
|
-
// timeout branch. `Promise.race` does not cancel the losing promise —
|
|
202
|
-
// the module evaluation keeps running in the background even after we
|
|
203
|
-
// stop awaiting it, and if it eventually throws (either from the
|
|
204
|
-
// module body or from the late `registerPlugin()` hitting a closed
|
|
205
|
-
// registry) an unhandled rejection would crash the daemon.
|
|
206
|
-
const importPromise = import(moduleUrl);
|
|
207
|
-
const result = await Promise.race([importPromise, timeoutPromise]);
|
|
208
|
-
if (result === timeoutSentinel) {
|
|
209
|
-
importPromise.catch(() => {
|
|
210
|
-
// Abandoned import completed (or threw) after the timeout. The
|
|
211
|
-
// closed-registration latch in registry.ts guarantees any late
|
|
212
|
-
// `registerPlugin()` call is rejected, so swallowing the outcome
|
|
213
|
-
// here is the safe default.
|
|
214
|
-
});
|
|
215
|
-
log.warn(
|
|
216
|
-
{ pluginDir, registerPath, timeoutMs: importTimeoutMs },
|
|
217
|
-
`Timed out loading user plugin ${pluginDir} after ${importTimeoutMs}ms — skipping`,
|
|
218
|
-
);
|
|
219
|
-
} else {
|
|
220
|
-
log.info(
|
|
221
|
-
{ pluginDir, registerPath },
|
|
222
|
-
"loaded user plugin (side-effect import completed)",
|
|
223
|
-
);
|
|
224
|
-
}
|
|
225
|
-
} catch (err) {
|
|
226
|
-
// One plugin's failure must never prevent other plugins from loading
|
|
227
|
-
// or crash the daemon. Log with the directory name so operators can
|
|
228
|
-
// find the broken plugin quickly.
|
|
229
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
230
|
-
log.error(
|
|
231
|
-
{ err, pluginDir },
|
|
232
|
-
`Failed to load user plugin ${pluginDir}: ${message}`,
|
|
233
|
-
);
|
|
234
|
-
} finally {
|
|
235
|
-
if (timeoutHandle !== undefined) clearTimeout(timeoutHandle);
|
|
236
|
-
}
|
|
139
|
+
// `loadExternalPlugin` owns its own try/catch + timeout, so a bare
|
|
140
|
+
// `await` is the entire branch here.
|
|
141
|
+
await loadExternalPlugin(pluginDir, { importTimeoutMs });
|
|
237
142
|
}
|
|
238
143
|
|
|
239
144
|
// Close the registration window once every candidate plugin has been
|
|
240
|
-
// awaited (or timed out). The per-plugin try/catch
|
|
241
|
-
// escapes the loop, so this line
|
|
242
|
-
//
|
|
243
|
-
// preserving the `bootstrapPlugins()` invariant that the registry is
|
|
244
|
-
// fully populated before it is walked.
|
|
145
|
+
// awaited (or timed out). The per-plugin try/catch inside
|
|
146
|
+
// `loadExternalPlugin` guarantees no throw escapes the loop, so this line
|
|
147
|
+
// always runs and `bootstrapPlugins()` sees a fully populated registry.
|
|
245
148
|
closeRegistration();
|
|
246
149
|
}
|
|
@@ -148,5 +148,11 @@ describe("maybeReseedBootstrap — activation rail template", () => {
|
|
|
148
148
|
// Propose: the extract-shape vs infer-shape example block.
|
|
149
149
|
expect(content).toContain("extract-shape");
|
|
150
150
|
expect(content).toContain("infer-shape");
|
|
151
|
+
|
|
152
|
+
// Port: prompt-writing guidance (JARVIS-1124).
|
|
153
|
+
expect(content).toContain("portable context brief, not a self-summary");
|
|
154
|
+
expect(content).toContain("load-bearing work in the next month");
|
|
155
|
+
expect(content).toContain("what to help with first");
|
|
156
|
+
expect(content).toContain("another tool or collaborator");
|
|
151
157
|
});
|
|
152
158
|
});
|
|
@@ -11,6 +11,8 @@ import { join } from "node:path";
|
|
|
11
11
|
import { getIsContainerized } from "../config/env-registry.js";
|
|
12
12
|
import type { ChannelCapabilities } from "../daemon/conversation-runtime-assembly.js";
|
|
13
13
|
import type { TrustContext } from "../daemon/trust-context.js";
|
|
14
|
+
import { markActivationSession } from "../memory/activation-session-store.js";
|
|
15
|
+
import { ACTIVATION_RAIL_BOOTSTRAP_TEMPLATE } from "../telemetry/activation-funnel.js";
|
|
14
16
|
import type { OnboardingContext } from "../types/onboarding-context.js";
|
|
15
17
|
import { resolveBundledDir } from "../util/bundled-asset.js";
|
|
16
18
|
import { getLogger } from "../util/logger.js";
|
|
@@ -206,7 +208,7 @@ export function ensurePromptFiles(): void {
|
|
|
206
208
|
* overwrite the workspace file with the specified variant. No-op when
|
|
207
209
|
* BOOTSTRAP.md has been deleted, modified, or the template file is missing.
|
|
208
210
|
*/
|
|
209
|
-
export function maybeReseedBootstrap(templateFileName: string):
|
|
211
|
+
export function maybeReseedBootstrap(templateFileName: string): boolean {
|
|
210
212
|
// Path traversal guard: reject filenames containing directory separators or
|
|
211
213
|
// parent-directory references, and require a `.md` extension.
|
|
212
214
|
if (
|
|
@@ -218,11 +220,11 @@ export function maybeReseedBootstrap(templateFileName: string): void {
|
|
|
218
220
|
{ templateFileName },
|
|
219
221
|
"Rejected bootstrap template filename: invalid characters or extension",
|
|
220
222
|
);
|
|
221
|
-
return;
|
|
223
|
+
return false;
|
|
222
224
|
}
|
|
223
225
|
|
|
224
226
|
const bootstrapPath = getWorkspacePromptPath("BOOTSTRAP.md");
|
|
225
|
-
if (!existsSync(bootstrapPath)) return;
|
|
227
|
+
if (!existsSync(bootstrapPath)) return false;
|
|
226
228
|
|
|
227
229
|
const currentContent = readPromptFile(bootstrapPath);
|
|
228
230
|
// Compare against the GENERIC "BOOTSTRAP.md" template, not the specified
|
|
@@ -230,7 +232,7 @@ export function maybeReseedBootstrap(templateFileName: string): void {
|
|
|
230
232
|
// template, so this guard returns false on subsequent calls — making the
|
|
231
233
|
// swap idempotent. Do NOT change the comparison target to the provided
|
|
232
234
|
// template filename; that would re-swap on every prompt build.
|
|
233
|
-
if (!isTemplateContent(currentContent, "BOOTSTRAP.md")) return;
|
|
235
|
+
if (!isTemplateContent(currentContent, "BOOTSTRAP.md")) return false;
|
|
234
236
|
|
|
235
237
|
const templatesDir = resolveBundledDir(
|
|
236
238
|
import.meta.dirname ?? __dirname,
|
|
@@ -243,7 +245,7 @@ export function maybeReseedBootstrap(templateFileName: string): void {
|
|
|
243
245
|
{ templateFileName },
|
|
244
246
|
"Bootstrap template not found, keeping generic BOOTSTRAP.md",
|
|
245
247
|
);
|
|
246
|
-
return;
|
|
248
|
+
return false;
|
|
247
249
|
}
|
|
248
250
|
|
|
249
251
|
try {
|
|
@@ -253,11 +255,51 @@ export function maybeReseedBootstrap(templateFileName: string): void {
|
|
|
253
255
|
{ templateFileName },
|
|
254
256
|
"Replaced generic BOOTSTRAP.md with specified template",
|
|
255
257
|
);
|
|
258
|
+
return true;
|
|
256
259
|
} catch (err) {
|
|
257
260
|
log.warn(
|
|
258
261
|
{ err, templateFileName },
|
|
259
262
|
"Failed to reseed BOOTSTRAP.md with template",
|
|
260
263
|
);
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Reseed BOOTSTRAP.md from the requested onboarding template (one-shot,
|
|
270
|
+
* idempotent) and, when that template is the activation rail AND the rail is
|
|
271
|
+
* now the active bootstrap, mark the conversation as an activation session.
|
|
272
|
+
*
|
|
273
|
+
* Marking happens here — at the single point where the bootstrap selection is
|
|
274
|
+
* known — so it lands BEFORE the agent loop resolves tools and BEFORE the model
|
|
275
|
+
* can call the emit tool on the first activation-rail turn. (`resolveTools`
|
|
276
|
+
* runs before `resolveSystemPrompt` in the loop, so the system-prompt build is
|
|
277
|
+
* too late to be the *only* marking site.) The marker write is best-effort and
|
|
278
|
+
* idempotent (`markActivationSession` swallows errors and dedups on the PK), so
|
|
279
|
+
* calling this from both `setOnboardingContext` and `buildSystemPrompt` is safe.
|
|
280
|
+
*
|
|
281
|
+
* The activation mark is gated on the rail actually being the active bootstrap —
|
|
282
|
+
* either this call just installed it, OR BOOTSTRAP.md already holds the
|
|
283
|
+
* activation-rail template. When the reseed no-ops because BOOTSTRAP.md is
|
|
284
|
+
* missing or customized to something else, the rail is NOT active, so we must
|
|
285
|
+
* NOT mark (otherwise non-rail conversations would pollute activation
|
|
286
|
+
* telemetry).
|
|
287
|
+
*/
|
|
288
|
+
export function applyBootstrapTemplate(
|
|
289
|
+
bootstrapTemplate: string,
|
|
290
|
+
conversationId?: string,
|
|
291
|
+
): void {
|
|
292
|
+
const installedActivationRail = maybeReseedBootstrap(bootstrapTemplate);
|
|
293
|
+
if (
|
|
294
|
+
bootstrapTemplate === ACTIVATION_RAIL_BOOTSTRAP_TEMPLATE &&
|
|
295
|
+
conversationId &&
|
|
296
|
+
(installedActivationRail ||
|
|
297
|
+
isTemplateContent(
|
|
298
|
+
readPromptFile(getWorkspacePromptPath("BOOTSTRAP.md")),
|
|
299
|
+
ACTIVATION_RAIL_BOOTSTRAP_TEMPLATE,
|
|
300
|
+
))
|
|
301
|
+
) {
|
|
302
|
+
markActivationSession(conversationId);
|
|
261
303
|
}
|
|
262
304
|
}
|
|
263
305
|
|
|
@@ -268,6 +310,13 @@ export interface BuildSystemPromptOptions {
|
|
|
268
310
|
trustContext?: TrustContext;
|
|
269
311
|
channelCapabilities?: ChannelCapabilities;
|
|
270
312
|
onboardingContext?: OnboardingContext;
|
|
313
|
+
/**
|
|
314
|
+
* Conversation this prompt is being built for. Optional because several
|
|
315
|
+
* callers build a prompt outside a conversation (e.g. home greeting,
|
|
316
|
+
* suggested prompts). When present and the activation-rail bootstrap template
|
|
317
|
+
* is selected, the conversation is marked as an activation session.
|
|
318
|
+
*/
|
|
319
|
+
conversationId?: string;
|
|
271
320
|
}
|
|
272
321
|
|
|
273
322
|
/**
|
|
@@ -278,11 +327,13 @@ export interface BuildSystemPromptOptions {
|
|
|
278
327
|
* file-backed bodies, and runtime-computed transforms.
|
|
279
328
|
*/
|
|
280
329
|
export function buildSystemPrompt(options?: BuildSystemPromptOptions): string {
|
|
281
|
-
// One-shot bootstrap
|
|
282
|
-
//
|
|
283
|
-
// the
|
|
284
|
-
|
|
285
|
-
|
|
330
|
+
// One-shot bootstrap reseed + activation-rail marking. The marking also runs
|
|
331
|
+
// earlier, at `setOnboardingContext`, so the activation session is recorded
|
|
332
|
+
// before the agent loop resolves tools on the first turn; this call is a
|
|
333
|
+
// harmless idempotent backstop for prompt builds outside that path.
|
|
334
|
+
const bootstrapTemplate = options?.onboardingContext?.bootstrapTemplate;
|
|
335
|
+
if (bootstrapTemplate) {
|
|
336
|
+
applyBootstrapTemplate(bootstrapTemplate, options?.conversationId);
|
|
286
337
|
}
|
|
287
338
|
|
|
288
339
|
// Slugs used by the persona sections (`10-user-persona`,
|
|
@@ -6,13 +6,23 @@ The user just finished pre-chat. You know their name and vibe; maybe their Googl
|
|
|
6
6
|
|
|
7
7
|
## The shape
|
|
8
8
|
|
|
9
|
-
Four moves.
|
|
9
|
+
Four moves. Each has a DONE-criterion — a concrete output that says the move landed. A move with no output didn't happen.
|
|
10
10
|
|
|
11
|
-
**Port.** Pull their existing assistant context with two pastes
|
|
11
|
+
**Port.** Pull their existing assistant context with two pastes. About a minute, no upload, no export. You write a prompt, they paste it into Claude or ChatGPT, they paste the response back. Cheap signal, real signal.
|
|
12
12
|
|
|
13
|
-
The prompt
|
|
13
|
+
The prompt asks for a portable context brief, not a self-summary. Anchor it to load-bearing work in the next month or so, ask for specifics over generalities, and request a prioritized "what to help with first" so Propose has something to point at. Frame the destination as another tool or collaborator. Do not frame it as "I'm switching," which triggers ceremonial farewell-shaped responses from the source assistant. Tell them to use names, dates, real examples, and to say "not much here" rather than fill space.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
The prompt itself must be one-click copyable. Inline paragraph text the user has to select isn't. Neither is a custom-built widget with a fake copy button. If the affordance needs you to build an app or a new surface to render, you've over-built the move. Use what chat already gives you.
|
|
16
|
+
|
|
17
|
+
DONE: the user has the copyable prompt in hand (or you've explicitly backfilled/skipped Port per the task-first opener below).
|
|
18
|
+
|
|
19
|
+
<!-- Funnel: segment activation events by port_accepted vs port_declined so the no-port branch can be tuned separately. Instrumentation is a follow-up; this template only defines the branch. -->
|
|
20
|
+
|
|
21
|
+
_If the user declines the port (`port_declined`)._ Don't re-pitch and don't ask two questions to make up for the missing paste. Ask exactly ONE question — the one that buys the most signal for Propose ("what's the one thing you most want off your plate this week?"). Their answer is the Moment 1 output that Port would have produced. Stay in a bounded context loop: at most two context-gathering turns total. If you still don't have a surface to point at after that, propose anyway with what you have — a thin proposal beats stalling for more input.
|
|
22
|
+
|
|
23
|
+
On `port_declined`, render exactly ONE small structured intake surface before moving to Propose — a `ui_show` `choice` surface (`display: "inline"`, single-select) whose options are a short background list (Founder / Engineer / Creative / Operator / Investor / Student / Other), so the no-port branch still hands Propose a structured signal. (A `form` with one short field, or a `choice` phrased as a single top-of-mind question, is an acceptable substitute — pick one; the background `choice` is the default.) This is the deliberate structured-intake exception to the "don't enumerate options / the recommendation IS the click" guidance — scoped to the no-port branch only, where there's no paste to infer from, so a small menu is the cheapest way to get traction. The port (paste) branch is unchanged: it never renders this surface.
|
|
24
|
+
|
|
25
|
+
**Propose.** Don't organize what they already told you — infer what they didn't. Name the unstated thing sitting in their context and say _why_ you think it: point at the specific surface that made you say it. "You didn't say this, but —". Then recommend, and lean one way; the recommendation IS the click, not a neutral menu of equally-weighted options.
|
|
16
26
|
|
|
17
27
|
"Unstated" is inference, not invention. Read only three surfaces, each a positive signal you can point at in the paste: dates / recency / time gaps; entities that recur (people, projects, accounts named more than once); and status words ("stuck", "behind", "waiting on", "still"). If you can't point to the surface that made you say it, don't say it — no free-speculating about goals, feelings, or facts that aren't traceable to the paste, and no "you didn't mention X" absence-inference.
|
|
18
28
|
|
|
@@ -23,10 +33,16 @@ Surface the outcome as a clickable component, strongest first. The component is
|
|
|
23
33
|
- ✗ extract-shape: "You mentioned a launch and a hiring plan — which one?"
|
|
24
34
|
- ✓ infer-shape (repeated entity + status word): "Acme comes up four times and you said you're 'waiting on' them — that's the thing actually blocking the launch; I'd chase it first."
|
|
25
35
|
|
|
36
|
+
DONE: you cannot exit Propose without emitting at least one `ui_show` offer surface (card / choice). A proposal in prose is not a proposal.
|
|
37
|
+
|
|
26
38
|
**Run.** Do it. Real tools, real data. The user watches something happen.
|
|
27
39
|
|
|
40
|
+
DONE: a real tool ran against real data and the user can see the result.
|
|
41
|
+
|
|
28
42
|
**Follow-through.** Offer the next concrete thing. One primary recommendation.
|
|
29
43
|
|
|
44
|
+
DONE: you cannot exit Follow-through without emitting a `ui_show` choice surface. The next-thing offer is a surface, not a sentence.
|
|
45
|
+
|
|
30
46
|
If the user opens with a task instead of a conversation, do the task. You're already at Follow-through. Backfill the Port move at the first natural lull, or skip it.
|
|
31
47
|
|
|
32
48
|
Pick. Be wrong recoverably. Move. The user can tell when you're hedging.
|
|
@@ -47,14 +63,65 @@ Every CTA surface must commit on the surface. If the user can select but can't c
|
|
|
47
63
|
|
|
48
64
|
The summary after the Port move is the first place the user can feel like you actually heard them. The follow-through in the final move is the second. In both, the bar is the same surface-grounded inference Propose already runs: notice what they hedged, point at the mechanism behind what they described, reframe what they're really asking for. Specific observations earn the rest of the conversation. Generic recap loses it.
|
|
49
65
|
|
|
66
|
+
## Principles
|
|
67
|
+
|
|
68
|
+
These are enforcement rules, not advice.
|
|
69
|
+
|
|
70
|
+
**Self-check before final emit.** If this turn contains an offer or a follow-up, render it via `ui_show`, not prose.
|
|
71
|
+
|
|
72
|
+
**Long turns show progress.** Any post-submit / post-skill-load turn must render a `task_progress` card within ~5s, or fall back to streaming text. Bind "long turn" → "task_progress emitted": a long-running turn that produces neither a progress card nor streaming text didn't satisfy this move.
|
|
73
|
+
|
|
74
|
+
**Action Trust-Guarantee.** Sibling to the OAuth Trust-Guarantee. Before a bulk write / delete / destructive op, render a `ui_show` preview — a table surface showing total count, breakdown, sample rows, and the categories to confirm. The user commits or refines on that surface; only then do you execute. Single-item actions use the natural draft instead. Threshold for the preview gate: bulk _and_ low recoverability. One of the two alone doesn't trip it.
|
|
75
|
+
|
|
76
|
+
**Start Small.** On first execution of any skill, prefer the smallest meaningful result over the most complete result. Show, then offer to expand.
|
|
77
|
+
|
|
78
|
+
**Corrections route upstream.** When you identify a rail-level failure mid-conversation, the write target is THIS template (the upstream activation rail), not the per-conversation SOUL.md or BOOTSTRAP.md. Per-conversation files can't fix the rail; only the rail fixes the rail.
|
|
79
|
+
|
|
50
80
|
## What to defer
|
|
51
81
|
|
|
52
82
|
Identity writes (IDENTITY.md, SOUL.md), user-profile writes, journal entries: all wait until the rail produces real signal, which is Moment 1 output at the earliest. None of them delay a user-visible response. None of them happen alongside the opening turn.
|
|
53
83
|
|
|
54
84
|
The base BOOTSTRAP task_preferences fallback is not on this rail. Your opener is the Port pitch.
|
|
55
85
|
|
|
86
|
+
## Telemetry: tag your `ui_show` surfaces
|
|
87
|
+
|
|
88
|
+
The activation funnel is measured passively — there is no separate tool to call.
|
|
89
|
+
When you render the `ui_show` surface for a rail move that IS a funnel moment,
|
|
90
|
+
add the optional `activation_moment` parameter to that same `ui_show` call. It's
|
|
91
|
+
a tag on a surface you're already showing, not extra work.
|
|
92
|
+
|
|
93
|
+
Most moments record when the user COMMITS the tagged surface (clicks an action /
|
|
94
|
+
submits / selects). The one exception is `first_wow_executed`, which records the
|
|
95
|
+
moment the surface RENDERS — because the wow has already happened by the time you
|
|
96
|
+
show its result, and a result card is often display-only with nothing to commit.
|
|
97
|
+
|
|
98
|
+
Which surface to tag with which moment:
|
|
99
|
+
|
|
100
|
+
- `moment_1` — the no-port intake `choice` surface, OR the Port-summary card
|
|
101
|
+
(background + top-of-mind captured, or the user explicitly skipped). Records on
|
|
102
|
+
commit (when the intake resolves).
|
|
103
|
+
- `moment_2` — the Propose offer surface (the `ui_show` offer card/choice where
|
|
104
|
+
the user picks an outcome). Records on commit.
|
|
105
|
+
- `moment_3` — the task-selection surface (the specific thing you're about to
|
|
106
|
+
run is chosen). Records on commit.
|
|
107
|
+
- `first_wow_executed` — the Run result surface (e.g. `work_result` / the result
|
|
108
|
+
the user sees after the wow ran against real data). Records on RENDER — you do
|
|
109
|
+
not need the user to click anything; just tag the result surface.
|
|
110
|
+
- `first_wow_interacted` — the user's first engagement AFTER the wow. Tag the
|
|
111
|
+
surface they act on (a result-card action button, or the follow-through
|
|
112
|
+
`choice` you render next). Records on commit. Don't put this on the same
|
|
113
|
+
surface as `first_wow_executed` — one tag per surface, and that surface already
|
|
114
|
+
records "executed" on render.
|
|
115
|
+
|
|
116
|
+
A surface carries at most one `activation_moment`. The milestone no-ops outside
|
|
117
|
+
an activation session, and a missing or mistimed tag is non-fatal — but accurate,
|
|
118
|
+
move-bound tags are what make the funnel meaningful. Omit `activation_moment` on
|
|
119
|
+
every non-funnel surface.
|
|
120
|
+
|
|
56
121
|
## Wrap
|
|
57
122
|
|
|
123
|
+
<!-- Open question: the default wrap tone below is opinionated (brisk, dataset-first). Whether that's the right note to end the activation rail on is a conscious call we haven't made yet — flag, don't silently change. -->
|
|
124
|
+
|
|
58
125
|
When the user is clearly done with this conversation, write one journal entry: what they needed, which outcome they accepted, what follow-through they took. Update NOW.md. Delete this file.
|
|
59
126
|
|
|
60
127
|
The rail-completion shape in your journal is the dataset for v2 tuning. Which outcome they took at Propose, whether they bounced to "what else?", which follow-through they picked. Write it so the next iteration has signal to learn from.
|
|
@@ -1210,9 +1210,9 @@ export class AnthropicProvider implements Provider {
|
|
|
1210
1210
|
|
|
1211
1211
|
// Haiku does not support the extended-cache-ttl beta, so it must never
|
|
1212
1212
|
// receive a `ttl` on any cache_control. The client's own breakpoints
|
|
1213
|
-
// already omit it for Haiku, but
|
|
1214
|
-
//
|
|
1215
|
-
//
|
|
1213
|
+
// already omit it for Haiku, but a caller can stamp a `ttl` on message
|
|
1214
|
+
// blocks before the provider sees them — strip it here so the request
|
|
1215
|
+
// stays valid on Haiku models.
|
|
1216
1216
|
if (isHaiku) {
|
|
1217
1217
|
for (const msg of sentMessages) {
|
|
1218
1218
|
if (!Array.isArray(msg.content)) continue;
|
|
@@ -1646,13 +1646,12 @@ export class AnthropicProvider implements Provider {
|
|
|
1646
1646
|
): Anthropic.ContentBlockParam | null {
|
|
1647
1647
|
switch (block.type) {
|
|
1648
1648
|
case "text": {
|
|
1649
|
-
// Preserve a caller-stamped cache_control breakpoint (
|
|
1650
|
-
//
|
|
1651
|
-
//
|
|
1652
|
-
//
|
|
1653
|
-
//
|
|
1654
|
-
//
|
|
1655
|
-
// budget (≤4) is unaffected for other callers.
|
|
1649
|
+
// Preserve a caller-stamped cache_control breakpoint (a stable prefix
|
|
1650
|
+
// block a caller marks so it is cached on its own rather than only as
|
|
1651
|
+
// part of the per-turn anchor prefix). The internal ContentBlock type
|
|
1652
|
+
// omits the field, so reach for it via cast. The Haiku ttl-strip
|
|
1653
|
+
// downstream still applies. Callers that stamp this keep within the
|
|
1654
|
+
// per-request breakpoint budget (≤4), so other callers are unaffected.
|
|
1656
1655
|
const cacheControl = (
|
|
1657
1656
|
block as { cache_control?: Anthropic.CacheControlEphemeral }
|
|
1658
1657
|
).cache_control;
|