@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
|
@@ -68,6 +68,73 @@ export type ProxyResolver = (
|
|
|
68
68
|
input: Record<string, unknown>,
|
|
69
69
|
) => Promise<ExecutorResult>;
|
|
70
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Validate a `source_files` map (LLM input is not type-checked at runtime).
|
|
73
|
+
* Returns an error ExecutorResult when invalid, or null when absent/valid.
|
|
74
|
+
*/
|
|
75
|
+
function validateSourceFiles(sourceFiles: unknown): ExecutorResult | null {
|
|
76
|
+
if (sourceFiles == null) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
if (typeof sourceFiles !== "object" || Array.isArray(sourceFiles)) {
|
|
80
|
+
return {
|
|
81
|
+
content: JSON.stringify({
|
|
82
|
+
error:
|
|
83
|
+
"source_files must be an object mapping relative file paths to string contents",
|
|
84
|
+
}),
|
|
85
|
+
isError: true,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
for (const [key, val] of Object.entries(sourceFiles)) {
|
|
89
|
+
if (typeof val !== "string") {
|
|
90
|
+
return {
|
|
91
|
+
content: JSON.stringify({
|
|
92
|
+
error: `source_files["${key}"] must be a string, got ${typeof val}`,
|
|
93
|
+
}),
|
|
94
|
+
isError: true,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Resolve an app name when the model omits it. The preview card's title is
|
|
103
|
+
* "always include" guidance and almost always present, so it's the best
|
|
104
|
+
* fallback — a real, meaningful name — before a generic placeholder.
|
|
105
|
+
*/
|
|
106
|
+
function resolveAppName(input: AppCreateInput): string {
|
|
107
|
+
const previewTitle =
|
|
108
|
+
input.preview && typeof input.preview.title === "string"
|
|
109
|
+
? input.preview.title
|
|
110
|
+
: undefined;
|
|
111
|
+
for (const candidate of [input.name, previewTitle]) {
|
|
112
|
+
if (typeof candidate === "string" && candidate.trim() !== "") {
|
|
113
|
+
return candidate.trim();
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return "New App";
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Compile-result fields shared by the app_refresh and app_update responses.
|
|
121
|
+
* On failure the errors/warnings are surfaced so the agent can fix them.
|
|
122
|
+
*/
|
|
123
|
+
function compileResultPayload(
|
|
124
|
+
compileResult: Awaited<ReturnType<typeof compileApp>>,
|
|
125
|
+
): Record<string, unknown> {
|
|
126
|
+
return {
|
|
127
|
+
compiled: compileResult.ok,
|
|
128
|
+
...(compileResult.ok
|
|
129
|
+
? { compile_duration_ms: compileResult.durationMs }
|
|
130
|
+
: {
|
|
131
|
+
compile_errors: compileResult.errors,
|
|
132
|
+
compile_warnings: compileResult.warnings,
|
|
133
|
+
compile_duration_ms: compileResult.durationMs,
|
|
134
|
+
}),
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
71
138
|
// ---------------------------------------------------------------------------
|
|
72
139
|
// app_create
|
|
73
140
|
// ---------------------------------------------------------------------------
|
|
@@ -76,14 +143,12 @@ export interface AppCreateInput {
|
|
|
76
143
|
name: string;
|
|
77
144
|
description?: string;
|
|
78
145
|
schema_json?: string;
|
|
79
|
-
/**
|
|
80
|
-
* Retired single-file shortcut. Kept in the type so legacy or stale callers
|
|
81
|
-
* get a clear tool error instead of silently creating a v2 app with stale
|
|
82
|
-
* scaffold content.
|
|
83
|
-
*/
|
|
146
|
+
/** Retired single-file shortcut. Returns a guidance error. */
|
|
84
147
|
html?: unknown;
|
|
85
|
-
/** Retired single-file multi-page shortcut. */
|
|
148
|
+
/** Retired single-file multi-page shortcut. Returns a guidance error. */
|
|
86
149
|
pages?: unknown;
|
|
150
|
+
/** Lenient alias. Folded into preview.icon when preview.icon is absent. */
|
|
151
|
+
icon?: unknown;
|
|
87
152
|
auto_open?: boolean;
|
|
88
153
|
preview?: Record<string, unknown>;
|
|
89
154
|
source_files?: Record<string, string>;
|
|
@@ -94,15 +159,20 @@ export async function executeAppCreate(
|
|
|
94
159
|
store: AppStore,
|
|
95
160
|
proxyToolResolver?: ProxyResolver,
|
|
96
161
|
): Promise<ExecutorResult> {
|
|
97
|
-
|
|
162
|
+
// The model sometimes omits a name; resolve a sensible one rather than
|
|
163
|
+
// erroring out so the build still succeeds. Users can rename via app_update.
|
|
164
|
+
const name = resolveAppName(input);
|
|
98
165
|
const description = input.description;
|
|
99
166
|
const schemaJson = input.schema_json ?? "{}";
|
|
100
167
|
|
|
168
|
+
// Retired shortcut: a top-level `html` is no longer accepted. Reject with a
|
|
169
|
+
// helpful message (rather than a cryptic schema error) so the model writes a
|
|
170
|
+
// multi-file TSX app under src/ instead.
|
|
101
171
|
if (Object.prototype.hasOwnProperty.call(input, "html")) {
|
|
102
172
|
return {
|
|
103
173
|
content: JSON.stringify({
|
|
104
174
|
error:
|
|
105
|
-
"app_create no longer accepts html.
|
|
175
|
+
"app_create no longer accepts html. Build a multi-file TSX app under src/ (src/index.html + src/main.tsx + src/App.tsx) instead.",
|
|
106
176
|
}),
|
|
107
177
|
isError: true,
|
|
108
178
|
};
|
|
@@ -120,44 +190,18 @@ export async function executeAppCreate(
|
|
|
120
190
|
const autoOpen = input.auto_open !== false; // default true
|
|
121
191
|
const preview = input.preview;
|
|
122
192
|
|
|
123
|
-
|
|
124
|
-
if (
|
|
125
|
-
return
|
|
126
|
-
content: JSON.stringify({
|
|
127
|
-
error: "name is required and must be a non-empty string",
|
|
128
|
-
}),
|
|
129
|
-
isError: true,
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (input.source_files != null) {
|
|
134
|
-
if (
|
|
135
|
-
typeof input.source_files !== "object" ||
|
|
136
|
-
Array.isArray(input.source_files)
|
|
137
|
-
) {
|
|
138
|
-
return {
|
|
139
|
-
content: JSON.stringify({
|
|
140
|
-
error:
|
|
141
|
-
"source_files must be an object mapping relative file paths to string contents",
|
|
142
|
-
}),
|
|
143
|
-
isError: true,
|
|
144
|
-
};
|
|
145
|
-
}
|
|
146
|
-
for (const [key, val] of Object.entries(input.source_files)) {
|
|
147
|
-
if (typeof val !== "string") {
|
|
148
|
-
return {
|
|
149
|
-
content: JSON.stringify({
|
|
150
|
-
error: `source_files["${key}"] must be a string, got ${typeof val}`,
|
|
151
|
-
}),
|
|
152
|
-
isError: true,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
}
|
|
193
|
+
const sourceFilesError = validateSourceFiles(input.source_files);
|
|
194
|
+
if (sourceFilesError) {
|
|
195
|
+
return sourceFilesError;
|
|
156
196
|
}
|
|
157
197
|
|
|
158
198
|
// Extract icon from preview if provided - only persist emoji-like values,
|
|
159
199
|
// not URLs which would render as raw strings in UI and bundle manifests.
|
|
160
|
-
|
|
200
|
+
// Lenient alias: a top-level `icon` is folded in when preview.icon is absent.
|
|
201
|
+
const rawIcon = (preview?.icon ??
|
|
202
|
+
(typeof input.icon === "string" ? input.icon : undefined)) as
|
|
203
|
+
| string
|
|
204
|
+
| undefined;
|
|
161
205
|
const icon = rawIcon && !rawIcon.startsWith("http") ? rawIcon : undefined;
|
|
162
206
|
|
|
163
207
|
const app = store.createApp({
|
|
@@ -351,14 +395,7 @@ export async function executeAppRefresh(
|
|
|
351
395
|
refreshed: true,
|
|
352
396
|
appId: updated.id,
|
|
353
397
|
name: updated.name,
|
|
354
|
-
|
|
355
|
-
...(compileResult.ok
|
|
356
|
-
? { compile_duration_ms: compileResult.durationMs }
|
|
357
|
-
: {
|
|
358
|
-
compile_errors: compileResult.errors,
|
|
359
|
-
compile_warnings: compileResult.warnings,
|
|
360
|
-
compile_duration_ms: compileResult.durationMs,
|
|
361
|
-
}),
|
|
398
|
+
...compileResultPayload(compileResult),
|
|
362
399
|
}),
|
|
363
400
|
isError: false,
|
|
364
401
|
};
|
|
@@ -374,6 +411,85 @@ export async function executeAppRefresh(
|
|
|
374
411
|
};
|
|
375
412
|
}
|
|
376
413
|
|
|
414
|
+
// ---------------------------------------------------------------------------
|
|
415
|
+
// app_update
|
|
416
|
+
// ---------------------------------------------------------------------------
|
|
417
|
+
|
|
418
|
+
export interface AppUpdateInput {
|
|
419
|
+
app_id: string;
|
|
420
|
+
name?: string;
|
|
421
|
+
description?: string;
|
|
422
|
+
schema_json?: string;
|
|
423
|
+
source_files?: Record<string, string>;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
export async function executeAppUpdate(
|
|
427
|
+
input: AppUpdateInput,
|
|
428
|
+
store: AppStore,
|
|
429
|
+
): Promise<ExecutorResult> {
|
|
430
|
+
const app = store.getApp(input.app_id);
|
|
431
|
+
if (!app) {
|
|
432
|
+
return {
|
|
433
|
+
content: JSON.stringify({ error: `App '${input.app_id}' not found` }),
|
|
434
|
+
isError: true,
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const sourceFilesError = validateSourceFiles(input.source_files);
|
|
439
|
+
if (sourceFilesError) {
|
|
440
|
+
return sourceFilesError;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
if (input.source_files) {
|
|
444
|
+
for (const [filePath, content] of Object.entries(input.source_files)) {
|
|
445
|
+
store.writeAppFile(input.app_id, filePath, content);
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const updates: Partial<
|
|
450
|
+
Pick<AppDefinition, "name" | "description" | "schemaJson">
|
|
451
|
+
> = {};
|
|
452
|
+
if (typeof input.name === "string" && input.name.trim() !== "") {
|
|
453
|
+
updates.name = input.name.trim();
|
|
454
|
+
}
|
|
455
|
+
if (typeof input.description === "string") {
|
|
456
|
+
updates.description = input.description;
|
|
457
|
+
}
|
|
458
|
+
if (typeof input.schema_json === "string") {
|
|
459
|
+
updates.schemaJson = input.schema_json;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// An empty update still bumps updatedAt, triggering a client surface refresh.
|
|
463
|
+
const updated = store.updateApp(input.app_id, updates);
|
|
464
|
+
|
|
465
|
+
// Multifile apps recompile so the agent sees any errors from the edited
|
|
466
|
+
// source instead of serving a stale dist (mirrors app_refresh).
|
|
467
|
+
if (app.formatVersion === 2) {
|
|
468
|
+
const appDir = getAppDirPath(input.app_id);
|
|
469
|
+
const compileResult = await compileApp(appDir);
|
|
470
|
+
return {
|
|
471
|
+
content: JSON.stringify({
|
|
472
|
+
updated: true,
|
|
473
|
+
appId: updated.id,
|
|
474
|
+
name: updated.name,
|
|
475
|
+
description: updated.description,
|
|
476
|
+
...compileResultPayload(compileResult),
|
|
477
|
+
}),
|
|
478
|
+
isError: false,
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return {
|
|
483
|
+
content: JSON.stringify({
|
|
484
|
+
updated: true,
|
|
485
|
+
appId: updated.id,
|
|
486
|
+
name: updated.name,
|
|
487
|
+
description: updated.description,
|
|
488
|
+
}),
|
|
489
|
+
isError: false,
|
|
490
|
+
};
|
|
491
|
+
}
|
|
492
|
+
|
|
377
493
|
// ---------------------------------------------------------------------------
|
|
378
494
|
// app_generate_icon
|
|
379
495
|
// ---------------------------------------------------------------------------
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { optimizeImageForTransport } from "../../agent/image-optimize.js";
|
|
1
2
|
import { getConfig } from "../../config/loader.js";
|
|
2
3
|
import { HostBrowserProxy } from "../../daemon/host-browser-proxy.js";
|
|
3
4
|
import type { ImageContent } from "../../providers/types.js";
|
|
@@ -1206,13 +1207,22 @@ export async function executeBrowserScreenshot(
|
|
|
1206
1207
|
{ quality: 80, fullPage },
|
|
1207
1208
|
context.signal,
|
|
1208
1209
|
);
|
|
1209
|
-
const
|
|
1210
|
+
const rawBase64 = buffer.toString("base64");
|
|
1211
|
+
|
|
1212
|
+
// Downscale before handing the screenshot to the model. A full-page
|
|
1213
|
+
// capture of a tall or high-DPI page can exceed Anthropic's 5 MB
|
|
1214
|
+
// per-image cap, which would otherwise persist into the conversation
|
|
1215
|
+
// history inside this tool_result and reject every subsequent turn.
|
|
1216
|
+
const { data: base64Data, mediaType } = optimizeImageForTransport(
|
|
1217
|
+
rawBase64,
|
|
1218
|
+
"image/jpeg",
|
|
1219
|
+
);
|
|
1210
1220
|
|
|
1211
1221
|
const imageBlock: ImageContent = {
|
|
1212
1222
|
type: "image" as const,
|
|
1213
1223
|
source: {
|
|
1214
1224
|
type: "base64" as const,
|
|
1215
|
-
media_type:
|
|
1225
|
+
media_type: mediaType,
|
|
1216
1226
|
data: base64Data,
|
|
1217
1227
|
},
|
|
1218
1228
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { join, resolve, sep } from "node:path";
|
|
2
2
|
|
|
3
|
+
import { getAppsDir } from "../../memory/app-store.js";
|
|
3
4
|
import { enqueuePkbIndexJob } from "../../memory/jobs/embed-pkb-file.js";
|
|
4
5
|
import { PKB_WORKSPACE_SCOPE } from "../../memory/pkb/types.js";
|
|
5
6
|
import { RiskLevel } from "../../permissions/types.js";
|
|
@@ -17,6 +18,29 @@ import type {
|
|
|
17
18
|
|
|
18
19
|
const logger = getLogger("file-write");
|
|
19
20
|
|
|
21
|
+
/**
|
|
22
|
+
* Detects an attempt to write a self-contained interactive HTML document —
|
|
23
|
+
* the signature of a "visualization" / "artifact" the model should be
|
|
24
|
+
* building as a real app via the app-builder skill, not dumping as a loose
|
|
25
|
+
* file. We deliberately trip ONLY on standalone docs with a substantial
|
|
26
|
+
* INLINE script (the data + rendering live in the file). app-builder's own
|
|
27
|
+
* scaffold writes a thin shell whose only script is an external module
|
|
28
|
+
* (`<script type="module" src="/src/main.tsx">`) with an empty body, so this
|
|
29
|
+
* never blocks the app-builder workflow it redirects to.
|
|
30
|
+
*/
|
|
31
|
+
const STANDALONE_HTML_RE = /<!doctype\s+html|<html[\s>]/i;
|
|
32
|
+
const INLINE_SCRIPT_RE = /<script\b(?![^>]*\bsrc=)[^>]*>[\s\S]{400,}?<\/script>/i;
|
|
33
|
+
|
|
34
|
+
function isSelfContainedArtifactHtml(path: string, content: string): boolean {
|
|
35
|
+
if (!/\.html?$/i.test(path)) return false;
|
|
36
|
+
if (content.length < 3000) return false;
|
|
37
|
+
if (!STANDALONE_HTML_RE.test(content)) return false;
|
|
38
|
+
return INLINE_SCRIPT_RE.test(content);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const ARTIFACT_REDIRECT_MESSAGE =
|
|
42
|
+
'Error: This looks like a self-contained interactive visualization/artifact written as a loose HTML file. Do not build these as standalone .html files. Load the app-builder skill first with `skill_load` using `skill: "app-builder"`, then build it as a real persistent app with `app_create` (the skill loads frontend-design for the visual pass and provides chart widgets). If this genuinely needs to be a raw HTML file inside an existing code project, write it under that project folder.';
|
|
43
|
+
|
|
20
44
|
/**
|
|
21
45
|
* Returns `true` iff `absPath` is an absolute path that resolves strictly
|
|
22
46
|
* inside `pkbRoot`. Matches the containment semantics used elsewhere in the
|
|
@@ -82,6 +106,16 @@ export const fileWriteTool = {
|
|
|
82
106
|
};
|
|
83
107
|
}
|
|
84
108
|
|
|
109
|
+
// Redirect self-contained interactive HTML artifacts to app-builder.
|
|
110
|
+
// Exempt writes that land inside the apps directory (app-builder's own
|
|
111
|
+
// scaffold/iteration writes) so we never block the workflow we point to.
|
|
112
|
+
if (isSelfContainedArtifactHtml(rawPath, fileContent)) {
|
|
113
|
+
const candidate = resolve(context.workingDir, rawPath);
|
|
114
|
+
if (!isInsidePkbRoot(candidate, getAppsDir())) {
|
|
115
|
+
return { content: ARTIFACT_REDIRECT_MESSAGE, isError: true };
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
85
119
|
const ops = new FileSystemOps((path, opts) =>
|
|
86
120
|
sandboxPolicy(path, context.workingDir, opts),
|
|
87
121
|
);
|
|
@@ -152,25 +152,56 @@ function isNullBodyStatus(status: number): boolean {
|
|
|
152
152
|
return status === 204 || status === 205 || status === 304;
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
// The WHATWG `Response` constructor only accepts 101 or the range 200–599 and
|
|
156
|
+
// throws a `RangeError` for anything else. Some servers — notably anti-bot
|
|
157
|
+
// gateways like LinkedIn and various job boards — reply with non-standard codes
|
|
158
|
+
// such as 999. Map those onto 502 so we can still build a Response; the real
|
|
159
|
+
// upstream status is preserved via `upstreamStatusByResponse` for reporting.
|
|
160
|
+
// See https://developer.mozilla.org/en-US/docs/Web/API/Response/Response
|
|
161
|
+
const RESPONSE_STATUS_SWITCHING_PROTOCOLS = 101;
|
|
162
|
+
const RESPONSE_STATUS_MIN = 200;
|
|
163
|
+
const RESPONSE_STATUS_MAX = 599;
|
|
164
|
+
|
|
165
|
+
function toConstructableStatus(status: number): number {
|
|
166
|
+
if (status === RESPONSE_STATUS_SWITCHING_PROTOCOLS) return status;
|
|
167
|
+
if (status >= RESPONSE_STATUS_MIN && status <= RESPONSE_STATUS_MAX) {
|
|
168
|
+
return status;
|
|
169
|
+
}
|
|
170
|
+
return 502;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const upstreamStatusByResponse = new WeakMap<Response, number>();
|
|
174
|
+
|
|
175
|
+
// Returns the real status the upstream server sent, which may fall outside the
|
|
176
|
+
// range the `Response` object can represent (e.g. 999). Falls back to
|
|
177
|
+
// `response.status` for responses built by native `fetch`, whose undici
|
|
178
|
+
// implementation already exposes non-standard codes directly.
|
|
179
|
+
export function getUpstreamStatus(response: Response): number {
|
|
180
|
+
return upstreamStatusByResponse.get(response) ?? response.status;
|
|
181
|
+
}
|
|
182
|
+
|
|
155
183
|
export function buildFetchResponseFromNodeResponse(
|
|
156
184
|
res: NodeHttpResponseLike,
|
|
157
185
|
): Response {
|
|
158
|
-
const
|
|
186
|
+
const upstreamStatus = res.statusCode ?? 502;
|
|
187
|
+
const status = toConstructableStatus(upstreamStatus);
|
|
159
188
|
const responseHeaders = buildResponseHeaders(res.headers);
|
|
160
189
|
const statusText = res.statusMessage ?? "";
|
|
161
190
|
|
|
162
|
-
|
|
163
|
-
// Drain any unexpected bytes and produce a valid null-body fetch Response.
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
191
|
+
const response = isNullBodyStatus(status)
|
|
192
|
+
? // Drain any unexpected bytes and produce a valid null-body fetch Response.
|
|
193
|
+
(res.resume(),
|
|
194
|
+
new Response(null, { status, statusText, headers: responseHeaders }))
|
|
195
|
+
: new Response(Readable.toWeb(res) as unknown as BodyInit, {
|
|
196
|
+
status,
|
|
197
|
+
statusText,
|
|
198
|
+
headers: responseHeaders,
|
|
199
|
+
});
|
|
167
200
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
headers: responseHeaders,
|
|
173
|
-
});
|
|
201
|
+
if (status !== upstreamStatus) {
|
|
202
|
+
upstreamStatusByResponse.set(response, upstreamStatus);
|
|
203
|
+
}
|
|
204
|
+
return response;
|
|
174
205
|
}
|
|
175
206
|
|
|
176
207
|
function createAbortError(): Error {
|
|
@@ -252,7 +283,16 @@ const defaultRequestExecutor: WebFetchRequestExecutor = async (
|
|
|
252
283
|
|
|
253
284
|
return await new Promise<Response>((resolve, reject) => {
|
|
254
285
|
const req = requestFn(requestOptions, (res) => {
|
|
255
|
-
|
|
286
|
+
// Building the fetch Response runs inside this HTTP `response` event
|
|
287
|
+
// callback. Any synchronous throw here would escape as an uncaught
|
|
288
|
+
// exception and crash the daemon, so funnel failures into `reject` to
|
|
289
|
+
// keep them local to this request.
|
|
290
|
+
try {
|
|
291
|
+
resolve(buildFetchResponseFromNodeResponse(res));
|
|
292
|
+
} catch (err) {
|
|
293
|
+
res.resume();
|
|
294
|
+
reject(err);
|
|
295
|
+
}
|
|
256
296
|
});
|
|
257
297
|
req.once("error", reject);
|
|
258
298
|
req.end();
|
|
@@ -730,9 +770,10 @@ export async function executeWebFetch(
|
|
|
730
770
|
);
|
|
731
771
|
}
|
|
732
772
|
|
|
773
|
+
const upstreamStatus = getUpstreamStatus(response);
|
|
733
774
|
const location = response.headers.get("location");
|
|
734
775
|
const isRedirect =
|
|
735
|
-
|
|
776
|
+
upstreamStatus >= 300 && upstreamStatus < 400 && !!location;
|
|
736
777
|
if (!isRedirect) break;
|
|
737
778
|
|
|
738
779
|
if (redirectCount >= MAX_REDIRECTS) {
|
|
@@ -741,7 +782,7 @@ export async function executeWebFetch(
|
|
|
741
782
|
{
|
|
742
783
|
url: requestedUrl,
|
|
743
784
|
finalUrl: currentUrl.href,
|
|
744
|
-
status: response
|
|
785
|
+
status: getUpstreamStatus(response),
|
|
745
786
|
redirectCount,
|
|
746
787
|
},
|
|
747
788
|
);
|
|
@@ -761,7 +802,7 @@ export async function executeWebFetch(
|
|
|
761
802
|
{
|
|
762
803
|
url: requestedUrl,
|
|
763
804
|
finalUrl: currentUrl.href,
|
|
764
|
-
status: response
|
|
805
|
+
status: getUpstreamStatus(response),
|
|
765
806
|
redirectCount,
|
|
766
807
|
},
|
|
767
808
|
);
|
|
@@ -773,7 +814,7 @@ export async function executeWebFetch(
|
|
|
773
814
|
{
|
|
774
815
|
url: requestedUrl,
|
|
775
816
|
finalUrl: currentUrl.href,
|
|
776
|
-
status: response
|
|
817
|
+
status: getUpstreamStatus(response),
|
|
777
818
|
redirectCount,
|
|
778
819
|
},
|
|
779
820
|
);
|
|
@@ -785,7 +826,7 @@ export async function executeWebFetch(
|
|
|
785
826
|
{
|
|
786
827
|
url: requestedUrl,
|
|
787
828
|
finalUrl: currentUrl.href,
|
|
788
|
-
status: response
|
|
829
|
+
status: getUpstreamStatus(response),
|
|
789
830
|
redirectCount,
|
|
790
831
|
},
|
|
791
832
|
);
|
|
@@ -805,7 +846,7 @@ export async function executeWebFetch(
|
|
|
805
846
|
{
|
|
806
847
|
url: requestedUrl,
|
|
807
848
|
finalUrl: currentUrl.href,
|
|
808
|
-
status: response
|
|
849
|
+
status: getUpstreamStatus(response),
|
|
809
850
|
redirectCount,
|
|
810
851
|
},
|
|
811
852
|
);
|
|
@@ -817,7 +858,7 @@ export async function executeWebFetch(
|
|
|
817
858
|
{
|
|
818
859
|
url: requestedUrl,
|
|
819
860
|
finalUrl: currentUrl.href,
|
|
820
|
-
status: response
|
|
861
|
+
status: getUpstreamStatus(response),
|
|
821
862
|
redirectCount,
|
|
822
863
|
},
|
|
823
864
|
);
|
|
@@ -844,7 +885,7 @@ export async function executeWebFetch(
|
|
|
844
885
|
{
|
|
845
886
|
url: requestedUrl,
|
|
846
887
|
finalUrl: currentUrl.href,
|
|
847
|
-
status: response
|
|
888
|
+
status: getUpstreamStatus(response),
|
|
848
889
|
contentType,
|
|
849
890
|
redirectCount,
|
|
850
891
|
},
|
|
@@ -909,7 +950,7 @@ export async function executeWebFetch(
|
|
|
909
950
|
const content = formatWebFetchOutput({
|
|
910
951
|
requestedUrl: safeRequestedUrl,
|
|
911
952
|
finalUrl: sanitizeUrlForOutput(currentUrl),
|
|
912
|
-
status: response
|
|
953
|
+
status: getUpstreamStatus(response),
|
|
913
954
|
statusText: response.statusText,
|
|
914
955
|
contentType,
|
|
915
956
|
bytesRead: body.bytesRead,
|
|
@@ -931,7 +972,7 @@ export async function executeWebFetch(
|
|
|
931
972
|
const meta: WebFetchMetadata = {
|
|
932
973
|
url: safeRequestedUrl,
|
|
933
974
|
finalUrl: sanitizeUrlForOutput(currentUrl),
|
|
934
|
-
status: response
|
|
975
|
+
status: getUpstreamStatus(response),
|
|
935
976
|
contentType: contentType || undefined,
|
|
936
977
|
byteCount: body.bytesRead,
|
|
937
978
|
charCount: sliced.length,
|
|
@@ -945,7 +986,7 @@ export async function executeWebFetch(
|
|
|
945
986
|
};
|
|
946
987
|
|
|
947
988
|
if (!response.ok) {
|
|
948
|
-
const errorMessage = `Error: HTTP ${response
|
|
989
|
+
const errorMessage = `Error: HTTP ${getUpstreamStatus(response)}`;
|
|
949
990
|
return {
|
|
950
991
|
content: `${errorMessage}\n\n${content}`,
|
|
951
992
|
isError: true,
|
package/src/tools/skills/load.ts
CHANGED
|
@@ -128,7 +128,7 @@ export const skillLoadTool = {
|
|
|
128
128
|
name: "skill_load",
|
|
129
129
|
|
|
130
130
|
description:
|
|
131
|
-
'Load full instructions for a skill. Works for both bundled skills (listed in the catalog) and custom workspace skills. For app, website, dashboard, game, calculator, tracker, visualization, or interactive tool requests, load `app-builder` with `skill: "app-builder"`.',
|
|
131
|
+
'Load full instructions for a skill. Works for both bundled skills (listed in the catalog) and custom workspace skills. Bundled/first-party skills (like `app-builder`) are already installed — loading only activates their instructions for the current conversation, since skills unload between turns; treat "load" as activation, not installation, and don\'t tell users a bundled skill needs installing. Loading can still fail (e.g. a feature-gated skill returns "currently unavailable", or the load errors) — if it does, relay that specific error rather than claiming the skill is not installed or offering to install it. For app, website, dashboard, game, calculator, tracker, visualization, or interactive tool requests, load `app-builder` with `skill: "app-builder"`.',
|
|
132
132
|
|
|
133
133
|
category: "skills",
|
|
134
134
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { findConversation } from "../../daemon/conversation-
|
|
1
|
+
import { findConversation } from "../../daemon/conversation-registry.js";
|
|
2
2
|
import { getConversationOverrideProfile } from "../../memory/conversation-crud.js";
|
|
3
3
|
import type { Message } from "../../providers/types.js";
|
|
4
4
|
import { getSubagentManager } from "../../subagent/index.js";
|
|
@@ -102,9 +102,7 @@ export async function executeSubagentSpawn(
|
|
|
102
102
|
...(inheritedOverrideProfile
|
|
103
103
|
? { overrideProfile: inheritedOverrideProfile }
|
|
104
104
|
: {}),
|
|
105
|
-
...(context.toolUseId
|
|
106
|
-
? { parentToolUseId: context.toolUseId }
|
|
107
|
-
: {}),
|
|
105
|
+
...(context.toolUseId ? { parentToolUseId: context.toolUseId } : {}),
|
|
108
106
|
...forkFields,
|
|
109
107
|
},
|
|
110
108
|
sendToClient as (msg: unknown) => void,
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { RiskLevel } from "../../permissions/types.js";
|
|
11
|
+
import { ACTIVATION_MOMENT_PARAMS } from "../../telemetry/activation-funnel.js";
|
|
11
12
|
import type {
|
|
12
13
|
ToolContext,
|
|
13
14
|
ToolDefinition,
|
|
@@ -19,7 +20,7 @@ import type {
|
|
|
19
20
|
// ---------------------------------------------------------------------------
|
|
20
21
|
|
|
21
22
|
const APP_BUILDER_ARTIFACT_RE =
|
|
22
|
-
/\b(app|apps|application|applications|website|websites|site|sites|dashboard|dashboards|game|games|calculator|calculators|tracker|trackers|visualization|visualizations|visualisation|visualisations|tool|tools|utility|utilities|counter|counters)\b/i;
|
|
23
|
+
/\b(app|apps|application|applications|website|websites|site|sites|dashboard|dashboards|game|games|calculator|calculators|tracker|trackers|visualization|visualizations|visualisation|visualisations|visualize|visualise|artifact|artifacts|chart|charts|graph|graphs|tool|tools|utility|utilities|counter|counters)\b/i;
|
|
23
24
|
const APP_BUILDER_BUILD_RE =
|
|
24
25
|
/\b(build|building|built|create|creating|created|make|making|made|generate|generating|generated)\b/i;
|
|
25
26
|
|
|
@@ -58,11 +59,31 @@ function isDynamicPageAppSubstitute(input: Record<string, unknown>): boolean {
|
|
|
58
59
|
}
|
|
59
60
|
|
|
60
61
|
const text = collectRoutingText(input).join(" ");
|
|
61
|
-
if (
|
|
62
|
-
|
|
62
|
+
if (
|
|
63
|
+
APP_BUILDER_ARTIFACT_RE.test(text) &&
|
|
64
|
+
(APP_BUILDER_BUILD_RE.test(text) || /\b(app|application)\b/i.test(text))
|
|
65
|
+
) {
|
|
66
|
+
return true;
|
|
63
67
|
}
|
|
64
68
|
|
|
65
|
-
|
|
69
|
+
// Second signal: even when the model gives the surface a clean,
|
|
70
|
+
// non-app-sounding title (dodging the text regex above), substantial
|
|
71
|
+
// interactive HTML is an app being smuggled in as a transient surface.
|
|
72
|
+
// A genuinely transient page is small and static; an app has real
|
|
73
|
+
// scripted markup. Keep the bar high so simple snippets still pass.
|
|
74
|
+
return isSubstantialInteractiveHtml(input);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const INTERACTIVE_HTML_RE =
|
|
78
|
+
/<script\b|on[a-z]+\s*=|addEventListener|new Chart\b|window\.vellum\b/i;
|
|
79
|
+
|
|
80
|
+
function isSubstantialInteractiveHtml(input: Record<string, unknown>): boolean {
|
|
81
|
+
const data = asRecord(input.data);
|
|
82
|
+
const html = data?.html;
|
|
83
|
+
if (typeof html !== "string") {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
return html.length > 2000 && INTERACTIVE_HTML_RE.test(html);
|
|
66
87
|
}
|
|
67
88
|
|
|
68
89
|
function collectRoutingText(input: Record<string, unknown>): string[] {
|
|
@@ -116,7 +137,7 @@ export const uiShowTool = {
|
|
|
116
137
|
"- file_upload: { prompt, acceptedTypes?, maxFiles? }\n" +
|
|
117
138
|
"- task_preferences: {} (no data needed — categories are rendered client-side)\n" +
|
|
118
139
|
'- work_result: { eyebrow?, status?: "completed"|"partial"|"failed"|"in_progress", summary?, metrics?: [{ label, value, detail?, tone?: "neutral"|"positive"|"warning"|"negative" }], sections?: [{ id?, title, description?, type?: "items"|"timeline"|"diff"|"artifacts"|"warnings", items?: [{ id?, title, description?, status?, tone?, metadata?: [{ label, value }], href? }], diffs?: [{ label?, before?, after? }] }] }. Shows a structured receipt after real work: what changed, what was skipped, proof points, and next actions. Keep display-only unless explicit follow-up buttons are needed.\n\n' +
|
|
119
|
-
"
|
|
140
|
+
"Emit a task_progress card BEFORE your first tool call on any multi-step, long-running, or post-form turn (web searches, file operations, research), and keep its steps updated as work progresses. Skills that estimate >30s of work should declare progress upfront with concrete step labels (and counts when known).",
|
|
120
141
|
category: "ui-surface",
|
|
121
142
|
defaultRiskLevel: RiskLevel.Low,
|
|
122
143
|
executionTarget: "host",
|
|
@@ -184,6 +205,12 @@ export const uiShowTool = {
|
|
|
184
205
|
description:
|
|
185
206
|
"When true, clicking an action does not dismiss the surface — the card stays visible and only the clicked action is marked as spent. Use for launcher or menu-style cards where multiple buttons may be clicked. Defaults to false.",
|
|
186
207
|
},
|
|
208
|
+
activation_moment: {
|
|
209
|
+
type: "string",
|
|
210
|
+
enum: ACTIVATION_MOMENT_PARAMS,
|
|
211
|
+
description:
|
|
212
|
+
"Activation-rail telemetry tag (cohort only). Set this when this surface IS one of the activation funnel moments; the milestone is recorded automatically when the user commits the surface. Omit for all non-activation surfaces.",
|
|
213
|
+
},
|
|
187
214
|
},
|
|
188
215
|
required: ["surface_type", "data"],
|
|
189
216
|
},
|
|
@@ -9,10 +9,9 @@ import type { WorkspaceMigration } from "./types.js";
|
|
|
9
9
|
* `conversationSummarization` is invoked from `ContextWindowManager.
|
|
10
10
|
* updateSummary()` during mid-loop compaction. Without a call-site entry it
|
|
11
11
|
* falls through to `llm.default` (opus + `effort: "max"` + `thinking:
|
|
12
|
-
* { enabled: true }` + `maxTokens: 64000`), which is far too expensive
|
|
13
|
-
* summarizing a ~150k-token transcript inside the agent
|
|
14
|
-
*
|
|
15
|
-
* failing the turn.
|
|
12
|
+
* { enabled: true }` + `maxTokens: 64000`), which is far too expensive and
|
|
13
|
+
* slow for summarizing a ~150k-token transcript inside the agent loop —
|
|
14
|
+
* the summary call would stall the turn.
|
|
16
15
|
*
|
|
17
16
|
* This migration seeds `effort: "low"` and `thinking: { enabled: false }`
|
|
18
17
|
* (and opus-4.7 as the model when absent) so the summary call runs cheaply
|
|
@@ -99,7 +98,7 @@ export const seedConversationSummarizationCallsiteMigration: WorkspaceMigration
|
|
|
99
98
|
},
|
|
100
99
|
down(_workspaceDir: string): void {
|
|
101
100
|
// Forward-only: removing the seeded defaults would reintroduce the
|
|
102
|
-
//
|
|
101
|
+
// expensive, slow summary call this migration fixes.
|
|
103
102
|
},
|
|
104
103
|
};
|
|
105
104
|
|