@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
|
@@ -2,21 +2,22 @@
|
|
|
2
2
|
* Plugin bootstrap — runs every registered plugin's `init()` hook once during
|
|
3
3
|
* daemon startup.
|
|
4
4
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* The registry is populated before this runs: first-party defaults are
|
|
6
|
+
* registered explicitly (step 1 below), user plugins are registered by the
|
|
7
|
+
* user-plugin loader from the workspace `plugins/` directory, and hot-reload
|
|
8
|
+
* registers at runtime. By the time {@link bootstrapPlugins} runs, the
|
|
9
|
+
* registry has been fully populated for this boot cycle. This function:
|
|
9
10
|
*
|
|
10
11
|
* 1. Registers the canonical first-party default plugins via
|
|
11
|
-
* {@link registerDefaultPlugins}
|
|
12
|
-
*
|
|
12
|
+
* {@link registerDefaultPlugins}. Registration is idempotent so repeat
|
|
13
|
+
* calls (e.g. during integration tests) do not throw.
|
|
13
14
|
* 2. Walks {@link getRegisteredPlugins} in registration order.
|
|
14
15
|
* 3. For each plugin, consults `manifest.requiresFlag` against
|
|
15
16
|
* {@link isAssistantFeatureFlagEnabled}. If any listed flag is disabled,
|
|
16
17
|
* the plugin is skipped wholesale — no `init()`, no tool/route/skill
|
|
17
18
|
* contributions, no entry in the shutdown hook, and the plugin is also
|
|
18
|
-
* dropped from the registry via {@link unregisterPlugin} so its
|
|
19
|
-
*
|
|
19
|
+
* dropped from the registry via {@link unregisterPlugin} so none of its
|
|
20
|
+
* hooks participate in the turn lifecycle. This is the primary mechanism for
|
|
20
21
|
* shipping experimental plugins behind a feature flag.
|
|
21
22
|
* 4. Resolves the plugin's `manifest.requiresCredential` entries via the
|
|
22
23
|
* credential store helper ({@link getSecureKeyAsync}). In Docker mode
|
|
@@ -36,14 +37,15 @@
|
|
|
36
37
|
* surface is already wired — useful for plugins that want to attach
|
|
37
38
|
* metadata, warm caches, or otherwise interact with their own
|
|
38
39
|
* contributions during initialization.
|
|
39
|
-
* 8. Awaits `plugin.init(ctx)` sequentially. An init failure
|
|
40
|
-
*
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
*
|
|
40
|
+
* 8. Awaits `plugin.init(ctx)` sequentially. An init failure is contained to
|
|
41
|
+
* the offending plugin: its already-registered tools, routes, and skills
|
|
42
|
+
* are rolled back, it is dropped from the registry, the failure is logged,
|
|
43
|
+
* and bootstrap continues with the remaining plugins. A single plugin's
|
|
44
|
+
* failure never deregisters plugins that already initialized — in
|
|
45
|
+
* particular the first-party defaults, which carry core turn behavior
|
|
46
|
+
* (memory retrieval, history repair, title generation) — so the daemon
|
|
47
|
+
* comes up with the failing plugin absent rather than blacking out the
|
|
48
|
+
* whole plugin layer.
|
|
47
49
|
*
|
|
48
50
|
* A single shutdown hook is registered via
|
|
49
51
|
* {@link registerShutdownHook} that walks the plugin list in **reverse
|
|
@@ -217,9 +219,10 @@ export async function initializePlugins(): Promise<void> {
|
|
|
217
219
|
* Run every registered plugin's `init()` hook sequentially and install a
|
|
218
220
|
* reverse-order shutdown hook. See the module docstring for full semantics.
|
|
219
221
|
*
|
|
220
|
-
* Returns once every plugin has
|
|
221
|
-
*
|
|
222
|
-
* the
|
|
222
|
+
* Returns once every plugin has been processed. A plugin whose `init()` throws
|
|
223
|
+
* is rolled back, dropped from the registry, and logged; bootstrap continues
|
|
224
|
+
* with the rest, so one failing plugin never blocks the others — or the
|
|
225
|
+
* first-party defaults — from coming up.
|
|
223
226
|
*
|
|
224
227
|
* Must be called after any custom/third-party plugin registrations have run
|
|
225
228
|
* and before the first conversation is served. First-party defaults are
|
|
@@ -258,36 +261,14 @@ export async function bootstrapPlugins(): Promise<void> {
|
|
|
258
261
|
// onShutdown" invariant.
|
|
259
262
|
const activePlugins: ActivePlugin[] = [];
|
|
260
263
|
|
|
261
|
-
// Tear down a plugin's contributions AND remove it from the registry. The
|
|
262
|
-
// two steps always move together on the bootstrap failure path: the former
|
|
263
|
-
// clears tools/routes/skills (so they stop appearing to the model/HTTP
|
|
264
|
-
// server), the latter drops the plugin's entry from the Map (so
|
|
265
|
-
// `getMiddlewaresFor` doesn't re-enter an uninitialized plugin on the next
|
|
266
|
-
// pipeline invocation).
|
|
267
264
|
// Shutdown context is identical for every plugin in this boot — construct
|
|
268
|
-
// once and reuse across the
|
|
269
|
-
//
|
|
270
|
-
//
|
|
265
|
+
// once and reuse across the per-plugin teardown and the normal shutdown
|
|
266
|
+
// hook below. Only `assistantVersion` is exposed today; future additions
|
|
267
|
+
// live on {@link PluginShutdownContext}.
|
|
271
268
|
const shutdownContext: PluginShutdownContext = {
|
|
272
269
|
assistantVersion: APP_VERSION,
|
|
273
270
|
};
|
|
274
271
|
|
|
275
|
-
async function rollbackPlugin(active: ActivePlugin): Promise<void> {
|
|
276
|
-
await teardownPlugin(active, "bootstrap-failed", shutdownContext);
|
|
277
|
-
unregisterPlugin(active.plugin.manifest.name);
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// If one plugin's init or contribution phase throws, tear down any plugins
|
|
281
|
-
// that already fully initialized (in reverse registration order) before
|
|
282
|
-
// re-throwing. Without this, a mid-loop failure would leave earlier plugins
|
|
283
|
-
// with live tools/routes/skills and no `onShutdown()` call — the shutdown
|
|
284
|
-
// hook is only registered once the loop completes successfully.
|
|
285
|
-
async function teardownPartialInit(): Promise<void> {
|
|
286
|
-
for (let i = activePlugins.length - 1; i >= 0; i--) {
|
|
287
|
-
await rollbackPlugin(activePlugins[i]!);
|
|
288
|
-
}
|
|
289
|
-
}
|
|
290
|
-
|
|
291
272
|
for (const plugin of plugins) {
|
|
292
273
|
const name = plugin.manifest.name;
|
|
293
274
|
const disabledFlag = getDisabledPluginFlag(plugin, assistantConfig);
|
|
@@ -303,9 +284,18 @@ export async function bootstrapPlugins(): Promise<void> {
|
|
|
303
284
|
try {
|
|
304
285
|
activePlugins.push(await initializePlugin(plugin, assistantConfig));
|
|
305
286
|
} catch (err) {
|
|
287
|
+
// Contain the failure to this plugin. `initializePlugin` already rolled
|
|
288
|
+
// back its own partial tool/route/skill contributions, so we just drop
|
|
289
|
+
// it from the registry and move on. A single plugin's init failure must
|
|
290
|
+
// never deregister the plugins that already came up — above all the
|
|
291
|
+
// first-party defaults, which carry core turn behavior. The daemon stays
|
|
292
|
+
// reachable with the failing plugin absent rather than losing the whole
|
|
293
|
+
// plugin layer.
|
|
306
294
|
unregisterPlugin(name);
|
|
307
|
-
|
|
308
|
-
|
|
295
|
+
log.warn(
|
|
296
|
+
{ err, plugin: name },
|
|
297
|
+
`plugin ${name} failed to initialize — skipping (continuing with remaining plugins)`,
|
|
298
|
+
);
|
|
309
299
|
}
|
|
310
300
|
}
|
|
311
301
|
|
|
@@ -31,6 +31,7 @@ import {
|
|
|
31
31
|
} from "../../runtime/channel-verification-service.js";
|
|
32
32
|
import {
|
|
33
33
|
cancelOutbound,
|
|
34
|
+
deliverVerificationEmail,
|
|
34
35
|
deliverVerificationSlack,
|
|
35
36
|
deliverVerificationTelegram,
|
|
36
37
|
DESTINATION_RATE_WINDOW_MS,
|
|
@@ -555,7 +556,11 @@ export async function handleChannelVerificationSession(
|
|
|
555
556
|
const { userId, text, assistantId: aid } = result._pendingSlackDm;
|
|
556
557
|
deliverVerificationSlack(userId, text, aid);
|
|
557
558
|
}
|
|
558
|
-
|
|
559
|
+
if (result._pendingEmail) {
|
|
560
|
+
const { to, text, subject, assistantId: aid } = result._pendingEmail;
|
|
561
|
+
deliverVerificationEmail(to, text, subject, aid);
|
|
562
|
+
}
|
|
563
|
+
const { _pendingSlackDm: _, _pendingEmail: __, ...publicResult } = result;
|
|
559
564
|
broadcastMessage({
|
|
560
565
|
type: "channel_verification_session_response",
|
|
561
566
|
...publicResult,
|
|
@@ -600,7 +605,11 @@ export async function handleChannelVerificationSession(
|
|
|
600
605
|
const { userId, text, assistantId: aid } = result._pendingSlackDm;
|
|
601
606
|
deliverVerificationSlack(userId, text, aid);
|
|
602
607
|
}
|
|
603
|
-
|
|
608
|
+
if (result._pendingEmail) {
|
|
609
|
+
const { to, text, subject, assistantId: aid } = result._pendingEmail;
|
|
610
|
+
deliverVerificationEmail(to, text, subject, aid);
|
|
611
|
+
}
|
|
612
|
+
const { _pendingSlackDm: _, _pendingEmail: __, ...publicResult } = result;
|
|
604
613
|
broadcastMessage({
|
|
605
614
|
type: "channel_verification_session_response",
|
|
606
615
|
...publicResult,
|
|
@@ -8,9 +8,11 @@ import { createAbortReason } from "../../util/abort-reasons.js";
|
|
|
8
8
|
import { truncate } from "../../util/truncate.js";
|
|
9
9
|
import { regenerate } from "../conversation-history.js";
|
|
10
10
|
import {
|
|
11
|
-
clearAllActiveConversations,
|
|
12
11
|
conversationEntries,
|
|
13
12
|
findConversation,
|
|
13
|
+
} from "../conversation-registry.js";
|
|
14
|
+
import {
|
|
15
|
+
clearAllActiveConversations,
|
|
14
16
|
getOrCreateConversation,
|
|
15
17
|
touchConversation,
|
|
16
18
|
} from "../conversation-store.js";
|
|
@@ -428,7 +428,15 @@ export function renderHistoryContent(
|
|
|
428
428
|
finalizeSegment();
|
|
429
429
|
thinkingSegments.push(block.thinking);
|
|
430
430
|
contentOrder.push(`thinking:${thinkingSegments.length - 1}`);
|
|
431
|
-
|
|
431
|
+
const thinkingBlock: Extract<
|
|
432
|
+
ConversationContentBlock,
|
|
433
|
+
{ type: "thinking" }
|
|
434
|
+
> = { type: "thinking", thinking: block.thinking };
|
|
435
|
+
if (typeof block._startedAt === "number")
|
|
436
|
+
thinkingBlock.startedAt = block._startedAt;
|
|
437
|
+
if (typeof block._completedAt === "number")
|
|
438
|
+
thinkingBlock.completedAt = block._completedAt;
|
|
439
|
+
contentBlocks.push(thinkingBlock);
|
|
432
440
|
continue;
|
|
433
441
|
}
|
|
434
442
|
|
|
@@ -31,7 +31,10 @@ import {
|
|
|
31
31
|
isTextMimeType as isTextMime,
|
|
32
32
|
MAX_INLINE_TEXT_SIZE,
|
|
33
33
|
} from "../../runtime/routes/workspace-utils.js";
|
|
34
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
getCachedCatalogSync,
|
|
36
|
+
getCatalog,
|
|
37
|
+
} from "../../skills/catalog-cache.js";
|
|
35
38
|
import type { SkillFileEntry } from "../../skills/catalog-files.js";
|
|
36
39
|
import {
|
|
37
40
|
catalogSkillToSlim,
|
|
@@ -12,11 +12,12 @@
|
|
|
12
12
|
* translation on top.
|
|
13
13
|
*
|
|
14
14
|
* **Session lock.** Only one conversation may hold an active app-control
|
|
15
|
-
* session at a time, and that session is bound to a specific target app
|
|
15
|
+
* session at a time, and that session is bound to a specific target app
|
|
16
|
+
* on a specific client.
|
|
16
17
|
* The lock is module-level (`activeAppControlSession`) because the session
|
|
17
18
|
* targets the user's actual desktop application, which is a host-wide
|
|
18
19
|
* resource. It is acquired optimistically when `app_control_start` is
|
|
19
|
-
* dispatched (storing `(conversationId, app)`) so that the synchronous
|
|
20
|
+
* dispatched (storing `(conversationId, app, targetClientId)`) so that the synchronous
|
|
20
21
|
* guard and the asynchronous host round-trip cannot race. A separate
|
|
21
22
|
* `confirmedAppControlSession` tracks the last session the host reported
|
|
22
23
|
* `running`; the rollback path on a failed `start` restores from the
|
|
@@ -32,7 +33,7 @@
|
|
|
32
33
|
* user's medium-risk approval at start time is the consent boundary. All
|
|
33
34
|
* other tools (observe / press / combo / sequence / type / click / drag)
|
|
34
35
|
* require the calling conversation to own an active session targeting the
|
|
35
|
-
* same `app
|
|
36
|
+
* same `app` and client; otherwise the call is rejected before any host dispatch.
|
|
36
37
|
* This prevents prompt-injected tool calls from sending raw input to
|
|
37
38
|
* arbitrary apps without the user having approved control of that
|
|
38
39
|
* specific app.
|
|
@@ -78,9 +79,9 @@ const STUCK_REPEAT_THRESHOLD = 4;
|
|
|
78
79
|
// ---------------------------------------------------------------------------
|
|
79
80
|
|
|
80
81
|
/**
|
|
81
|
-
* Active app-control session: the conversation that owns the lock
|
|
82
|
-
* `app` it was approved against. Set on a successful
|
|
83
|
-
* cleared by the owning proxy's `dispose()`.
|
|
82
|
+
* Active app-control session: the conversation that owns the lock, the
|
|
83
|
+
* `app` and client it was approved against. Set on a successful
|
|
84
|
+
* `app_control_start`; cleared by the owning proxy's `dispose()`.
|
|
84
85
|
*/
|
|
85
86
|
export interface ActiveAppControlSession {
|
|
86
87
|
conversationId: string;
|
|
@@ -90,6 +91,11 @@ export interface ActiveAppControlSession {
|
|
|
90
91
|
* the `app` of subsequent non-start tool calls.
|
|
91
92
|
*/
|
|
92
93
|
app: string;
|
|
94
|
+
/**
|
|
95
|
+
* The client selected for `app_control_start`. Undefined when the start
|
|
96
|
+
* was untargeted; subsequent calls must match the same value.
|
|
97
|
+
*/
|
|
98
|
+
targetClientId?: string;
|
|
93
99
|
/**
|
|
94
100
|
* Strictly monotonic counter assigned when the session is created (in
|
|
95
101
|
* `request()` for a `start`). Used by {@link promoteStartIfCurrent} to
|
|
@@ -153,11 +159,13 @@ export function _resetActiveAppControlSession(): void {
|
|
|
153
159
|
export function _setActiveAppControlSession(session: {
|
|
154
160
|
conversationId: string;
|
|
155
161
|
app: string;
|
|
162
|
+
targetClientId?: string;
|
|
156
163
|
dispatchedAt?: number;
|
|
157
164
|
}): void {
|
|
158
165
|
const full: ActiveAppControlSession = {
|
|
159
166
|
conversationId: session.conversationId,
|
|
160
167
|
app: session.app,
|
|
168
|
+
targetClientId: session.targetClientId,
|
|
161
169
|
dispatchedAt: session.dispatchedAt ?? nextDispatchedAt++,
|
|
162
170
|
};
|
|
163
171
|
activeAppControlSession = full;
|
|
@@ -177,6 +185,7 @@ export function _setActiveAppControlSession(session: {
|
|
|
177
185
|
function checkNonStartAuthorization(
|
|
178
186
|
input: HostAppControlInput,
|
|
179
187
|
conversationId: string,
|
|
188
|
+
targetClientId: string | undefined,
|
|
180
189
|
): ToolExecutionResult | null {
|
|
181
190
|
if (activeAppControlSession == null) {
|
|
182
191
|
return {
|
|
@@ -219,6 +228,16 @@ function checkNonStartAuthorization(
|
|
|
219
228
|
isError: true,
|
|
220
229
|
};
|
|
221
230
|
}
|
|
231
|
+
if (activeAppControlSession.targetClientId !== targetClientId) {
|
|
232
|
+
return {
|
|
233
|
+
content:
|
|
234
|
+
`Active app-control session targets client ` +
|
|
235
|
+
`${activeAppControlSession.targetClientId ?? "<default>"}; cannot ` +
|
|
236
|
+
`send actions to client ${targetClientId ?? "<default>"}. Call ` +
|
|
237
|
+
`app_control_stop and app_control_start to switch clients.`,
|
|
238
|
+
isError: true,
|
|
239
|
+
};
|
|
240
|
+
}
|
|
222
241
|
return null;
|
|
223
242
|
}
|
|
224
243
|
|
|
@@ -284,11 +303,53 @@ export class HostAppControlProxy extends HostProxyBase<
|
|
|
284
303
|
return { content: "Aborted", isError: true };
|
|
285
304
|
}
|
|
286
305
|
|
|
306
|
+
// Resolve target client and enforce same-actor BEFORE acquiring the
|
|
307
|
+
// session lock. This way early-exit errors don't need rollback, and
|
|
308
|
+
// the resolved client id can be stored in the session.
|
|
309
|
+
let resolvedTargetClientId = targetClientId;
|
|
310
|
+
if (resolvedTargetClientId == null) {
|
|
311
|
+
const resolved = pickSameUserAutoResolve({
|
|
312
|
+
hub: assistantEventHub,
|
|
313
|
+
capability: "host_app_control",
|
|
314
|
+
sourceActorPrincipalId,
|
|
315
|
+
});
|
|
316
|
+
if (resolved.kind === "ambiguous") {
|
|
317
|
+
return {
|
|
318
|
+
content:
|
|
319
|
+
"Multiple host_app_control clients are connected for this user. Specify target_client_id to disambiguate.",
|
|
320
|
+
isError: true,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
if (resolved.kind === "match") {
|
|
324
|
+
resolvedTargetClientId = resolved.clientId;
|
|
325
|
+
} else if (
|
|
326
|
+
assistantEventHub.listClientsByCapability("host_app_control").length > 0
|
|
327
|
+
) {
|
|
328
|
+
return {
|
|
329
|
+
content:
|
|
330
|
+
"App control is not available for the current actor. Connect a host_app_control-capable client as the same user.",
|
|
331
|
+
isError: true,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
if (resolvedTargetClientId != null) {
|
|
337
|
+
const rejection = enforceSameActorOrErrorResult({
|
|
338
|
+
hub: assistantEventHub,
|
|
339
|
+
sourceActorPrincipalId,
|
|
340
|
+
targetClientId: resolvedTargetClientId,
|
|
341
|
+
op: "host_app_control",
|
|
342
|
+
});
|
|
343
|
+
if (rejection) {
|
|
344
|
+
return rejection;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
287
348
|
// Authorization gate. `start` acquires the session lock (the user's
|
|
288
349
|
// medium-risk approval is the consent boundary); all other tools must
|
|
289
|
-
// belong to the active session and target the same `app
|
|
290
|
-
// gate, prompt-injected calls would bypass the start-time
|
|
291
|
-
// send raw input to arbitrary apps.
|
|
350
|
+
// belong to the active session and target the same `app` and client.
|
|
351
|
+
// Without this gate, prompt-injected calls would bypass the start-time
|
|
352
|
+
// approval and send raw input to arbitrary apps or clients.
|
|
292
353
|
let attemptedSession: ActiveAppControlSession | undefined;
|
|
293
354
|
if (input.tool === "start") {
|
|
294
355
|
if (
|
|
@@ -314,6 +375,7 @@ export class HostAppControlProxy extends HostProxyBase<
|
|
|
314
375
|
attemptedSession = {
|
|
315
376
|
conversationId: this.conversationId,
|
|
316
377
|
app: input.app,
|
|
378
|
+
targetClientId: resolvedTargetClientId,
|
|
317
379
|
dispatchedAt: nextDispatchedAt++,
|
|
318
380
|
};
|
|
319
381
|
activeAppControlSession = attemptedSession;
|
|
@@ -321,60 +383,13 @@ export class HostAppControlProxy extends HostProxyBase<
|
|
|
321
383
|
const sessionError = checkNonStartAuthorization(
|
|
322
384
|
input,
|
|
323
385
|
this.conversationId,
|
|
386
|
+
resolvedTargetClientId,
|
|
324
387
|
);
|
|
325
388
|
if (sessionError != null) {
|
|
326
389
|
return sessionError;
|
|
327
390
|
}
|
|
328
391
|
}
|
|
329
392
|
|
|
330
|
-
let resolvedTargetClientId = targetClientId;
|
|
331
|
-
if (resolvedTargetClientId == null) {
|
|
332
|
-
const resolved = pickSameUserAutoResolve({
|
|
333
|
-
hub: assistantEventHub,
|
|
334
|
-
capability: "host_app_control",
|
|
335
|
-
sourceActorPrincipalId,
|
|
336
|
-
});
|
|
337
|
-
if (resolved.kind === "ambiguous") {
|
|
338
|
-
if (input.tool === "start") {
|
|
339
|
-
this.rollbackStartIfCurrent(attemptedSession);
|
|
340
|
-
}
|
|
341
|
-
return {
|
|
342
|
-
content:
|
|
343
|
-
"Multiple host_app_control clients are connected for this user. Specify target_client_id to disambiguate.",
|
|
344
|
-
isError: true,
|
|
345
|
-
};
|
|
346
|
-
}
|
|
347
|
-
if (resolved.kind === "match") {
|
|
348
|
-
resolvedTargetClientId = resolved.clientId;
|
|
349
|
-
} else if (
|
|
350
|
-
assistantEventHub.listClientsByCapability("host_app_control").length > 0
|
|
351
|
-
) {
|
|
352
|
-
if (input.tool === "start") {
|
|
353
|
-
this.rollbackStartIfCurrent(attemptedSession);
|
|
354
|
-
}
|
|
355
|
-
return {
|
|
356
|
-
content:
|
|
357
|
-
"App control is not available for the current actor. Connect a host_app_control-capable client as the same user.",
|
|
358
|
-
isError: true,
|
|
359
|
-
};
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
if (resolvedTargetClientId != null) {
|
|
364
|
-
const rejection = enforceSameActorOrErrorResult({
|
|
365
|
-
hub: assistantEventHub,
|
|
366
|
-
sourceActorPrincipalId,
|
|
367
|
-
targetClientId: resolvedTargetClientId,
|
|
368
|
-
op: "host_app_control",
|
|
369
|
-
});
|
|
370
|
-
if (rejection) {
|
|
371
|
-
if (input.tool === "start") {
|
|
372
|
-
this.rollbackStartIfCurrent(attemptedSession);
|
|
373
|
-
}
|
|
374
|
-
return rejection;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
393
|
try {
|
|
379
394
|
const payload = await this.dispatchRequest(
|
|
380
395
|
toolName,
|
|
@@ -34,9 +34,7 @@ import { getLogger } from "../util/logger.js";
|
|
|
34
34
|
const log = getLogger("host-proxy-preactivation");
|
|
35
35
|
|
|
36
36
|
/**
|
|
37
|
-
* Subset of Conversation
|
|
38
|
-
* `preactivateHostProxySkills` needs. Both `Conversation` and
|
|
39
|
-
* `ProcessConversationContext` satisfy this structurally.
|
|
37
|
+
* Subset of `Conversation` that `preactivateHostProxySkills` needs.
|
|
40
38
|
*/
|
|
41
39
|
export interface HostProxyPreactivationTarget {
|
|
42
40
|
readonly conversationId: string;
|
package/src/daemon/lifecycle.ts
CHANGED
|
@@ -60,7 +60,6 @@ import { sweepConceptPageFrontmatter } from "../memory/v2/frontmatter-sweep.js";
|
|
|
60
60
|
import { emitNotificationSignal } from "../notifications/emit-signal.js";
|
|
61
61
|
import { backfillManualTokenConnections } from "../oauth/manual-token-connection.js";
|
|
62
62
|
import { seedOAuthProviders } from "../oauth/seed-providers.js";
|
|
63
|
-
import { backfillGuardIfNeeded } from "../proactive-artifact/index.js";
|
|
64
63
|
import { ensurePromptFiles } from "../prompts/system-prompt.js";
|
|
65
64
|
import { runProviderConnectionsBackfill } from "../providers/inference/backfill.js";
|
|
66
65
|
import { resolveManagedProxyContext } from "../providers/platform-proxy/context.js";
|
|
@@ -98,6 +97,7 @@ import {
|
|
|
98
97
|
updateWorkItem,
|
|
99
98
|
} from "../work-items/work-item-store.js";
|
|
100
99
|
import { WorkspaceHeartbeatService } from "../workspace/heartbeat-service.js";
|
|
100
|
+
import { repairAdaptiveThinkingOnManagedProfiles } from "../workspace/migrations/097-enable-adaptive-thinking-managed-profiles.js";
|
|
101
101
|
import { WORKSPACE_MIGRATIONS } from "../workspace/migrations/registry.js";
|
|
102
102
|
import { runWorkspaceMigrations } from "../workspace/migrations/runner.js";
|
|
103
103
|
import {
|
|
@@ -589,6 +589,26 @@ export async function runDaemon(): Promise<void> {
|
|
|
589
589
|
);
|
|
590
590
|
}
|
|
591
591
|
|
|
592
|
+
// Re-run the adaptive thinking repair after overlay merge + profile seeding.
|
|
593
|
+
// Workspace migration 097 enables adaptive thinking on managed profiles, but
|
|
594
|
+
// it runs before mergeDefaultWorkspaceConfig() which can overwrite the fix
|
|
595
|
+
// with overlay profiles that have thinking disabled or absent. On-platform
|
|
596
|
+
// instances where the overlay supplies "balanced" / "quality-optimized"
|
|
597
|
+
// profiles without thinking enabled would be stuck permanently because the
|
|
598
|
+
// migration is already checkpointed as completed. This idempotent repair
|
|
599
|
+
// ensures thinking is enabled regardless of overlay ordering.
|
|
600
|
+
if (defaultConfigMerge.hadOverlay) {
|
|
601
|
+
try {
|
|
602
|
+
repairAdaptiveThinkingOnManagedProfiles(getWorkspaceDir());
|
|
603
|
+
log.info("Post-overlay adaptive thinking repair complete");
|
|
604
|
+
} catch (err) {
|
|
605
|
+
log.warn(
|
|
606
|
+
{ err },
|
|
607
|
+
"Post-overlay adaptive thinking repair failed — continuing startup",
|
|
608
|
+
);
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
592
612
|
log.info("Daemon startup: loading config");
|
|
593
613
|
const config = loadConfig();
|
|
594
614
|
|
|
@@ -1276,12 +1296,6 @@ export async function runDaemon(): Promise<void> {
|
|
|
1276
1296
|
"Heartbeat service configured",
|
|
1277
1297
|
);
|
|
1278
1298
|
|
|
1279
|
-
try {
|
|
1280
|
-
backfillGuardIfNeeded();
|
|
1281
|
-
} catch (err) {
|
|
1282
|
-
log.warn({ err }, "Proactive artifact backfill failed");
|
|
1283
|
-
}
|
|
1284
|
-
|
|
1285
1299
|
// Filing yields to the memory v2 consolidation job when v2 is enabled —
|
|
1286
1300
|
// both serve the same role (periodic background memory processing) and
|
|
1287
1301
|
// running both is redundant. The consolidation job runs through the
|
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* retries. That transformation is transient — the stored message row keeps the
|
|
7
7
|
* full-size image block, so the rejected image is rehydrated on every later
|
|
8
8
|
* turn and keeps re-entering the model's context. This module makes the
|
|
9
|
-
* downgrade durable
|
|
10
|
-
*
|
|
9
|
+
* downgrade durable: the oversized block is rewritten to its downscaled form,
|
|
10
|
+
* or to a text note when it cannot be shrunk on this host, so a rejected image
|
|
11
|
+
* cannot resurface and re-reject on every later turn.
|
|
11
12
|
*/
|
|
12
13
|
|
|
13
14
|
import { optimizeImageForTransport } from "../agent/image-optimize.js";
|
|
@@ -40,19 +41,25 @@ export const UNSENDABLE_IMAGE_NOTE =
|
|
|
40
41
|
"(An image was attached but could not be sent — its dimensions exceed the provider limit and automatic resize was not available. Please resize the image and try again.)";
|
|
41
42
|
|
|
42
43
|
/**
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
44
|
+
* Replacement for an image that violates a provider hard limit (per-side pixel
|
|
45
|
+
* cap or payload size), or null when the image is within limits and should be
|
|
46
|
+
* left untouched. Gating on the provider hard caps is what keeps still-sendable
|
|
47
|
+
* images intact: a normally sized image is left alone rather than being noted or
|
|
48
|
+
* needlessly rewritten.
|
|
46
49
|
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
50
|
+
* An oversized image that can be shrunk is rewritten to its downscaled form; one
|
|
51
|
+
* that cannot be shrunk on this host (resize is a no-op, e.g. `sips` is absent
|
|
52
|
+
* off macOS or the format is unsupported) is replaced with a text note.
|
|
53
|
+
*
|
|
54
|
+
* Shared by the in-memory recovery transform and this durable persist pass so
|
|
55
|
+
* both apply the identical rule. Persisting the downscaled form is what lets a
|
|
56
|
+
* poisoned conversation durably self-heal — the latest tool-result media is kept
|
|
57
|
+
* in context, so without it the original oversized block rehydrates and
|
|
58
|
+
* re-rejects on every later turn.
|
|
52
59
|
*/
|
|
53
|
-
function
|
|
60
|
+
export function oversizedImageReplacement(
|
|
54
61
|
block: Extract<ContentBlock, { type: "image" }>,
|
|
55
|
-
):
|
|
62
|
+
): ContentBlock | null {
|
|
56
63
|
const payloadBytes = block.source.data.length;
|
|
57
64
|
const dims = parseImageDimensions(block.source.data, block.source.media_type);
|
|
58
65
|
const exceedsDimensionCap =
|
|
@@ -60,24 +67,35 @@ function isImagePermanentlyUnsendable(
|
|
|
60
67
|
(dims.width > PROVIDER_MAX_IMAGE_DIMENSION ||
|
|
61
68
|
dims.height > PROVIDER_MAX_IMAGE_DIMENSION);
|
|
62
69
|
const exceedsPayloadCap = payloadBytes > PROVIDER_MAX_IMAGE_PAYLOAD_BYTES;
|
|
63
|
-
if (!exceedsDimensionCap && !exceedsPayloadCap) return
|
|
70
|
+
if (!exceedsDimensionCap && !exceedsPayloadCap) return null;
|
|
64
71
|
|
|
65
72
|
const optimized = optimizeImageForTransport(
|
|
66
73
|
block.source.data,
|
|
67
74
|
block.source.media_type,
|
|
68
75
|
);
|
|
69
|
-
|
|
76
|
+
if (optimized.data !== block.source.data) {
|
|
77
|
+
return {
|
|
78
|
+
type: "image",
|
|
79
|
+
source: {
|
|
80
|
+
type: "base64",
|
|
81
|
+
media_type: optimized.mediaType,
|
|
82
|
+
data: optimized.data,
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
return { type: "text", text: UNSENDABLE_IMAGE_NOTE };
|
|
70
87
|
}
|
|
71
88
|
|
|
72
89
|
/**
|
|
73
|
-
* Rewrite every stored message in a conversation that holds
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
90
|
+
* Rewrite every stored message in a conversation that holds an oversized image
|
|
91
|
+
* the provider rejects — whether a top-level block or one nested in a
|
|
92
|
+
* tool_result's contentBlocks — replacing it with its downscaled form, or with
|
|
93
|
+
* {@link UNSENDABLE_IMAGE_NOTE} when it cannot be shrunk on this host. Reads
|
|
94
|
+
* stored content directly (not the in-memory, injection-enriched copy) so
|
|
95
|
+
* injected prefixes and hydrated source paths are never written back.
|
|
78
96
|
*
|
|
79
|
-
* Idempotent:
|
|
80
|
-
*
|
|
97
|
+
* Idempotent: a downscaled image is within limits and a note is no longer an
|
|
98
|
+
* image, so neither matches on a second run. Returns the number of rewritten
|
|
81
99
|
* messages.
|
|
82
100
|
*/
|
|
83
101
|
export function persistUnsendableImageDowngrades(
|
|
@@ -99,10 +117,29 @@ export function persistUnsendableImageDowngrades(
|
|
|
99
117
|
|
|
100
118
|
let changed = false;
|
|
101
119
|
const next = (parsed as ContentBlock[]).map((block): ContentBlock => {
|
|
102
|
-
if (block.type
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
120
|
+
if (block.type === "image") {
|
|
121
|
+
const replacement = oversizedImageReplacement(block);
|
|
122
|
+
if (!replacement) return block;
|
|
123
|
+
changed = true;
|
|
124
|
+
return replacement;
|
|
125
|
+
}
|
|
126
|
+
// Images returned by a tool (e.g. browser_screenshot) live inside the
|
|
127
|
+
// tool_result's contentBlocks, not as top-level blocks. Downgrade them
|
|
128
|
+
// in place so the tool_use/tool_result pairing stays intact.
|
|
129
|
+
if (block.type === "tool_result" && block.contentBlocks?.length) {
|
|
130
|
+
let nestedChanged = false;
|
|
131
|
+
const contentBlocks = block.contentBlocks.map((cb): ContentBlock => {
|
|
132
|
+
if (cb.type !== "image") return cb;
|
|
133
|
+
const replacement = oversizedImageReplacement(cb);
|
|
134
|
+
if (!replacement) return cb;
|
|
135
|
+
nestedChanged = true;
|
|
136
|
+
return replacement;
|
|
137
|
+
});
|
|
138
|
+
if (!nestedChanged) return block;
|
|
139
|
+
changed = true;
|
|
140
|
+
return { ...block, contentBlocks };
|
|
141
|
+
}
|
|
142
|
+
return block;
|
|
106
143
|
});
|
|
107
144
|
if (!changed) continue;
|
|
108
145
|
|
|
@@ -195,7 +195,7 @@ async function prepareConversationForMessage(
|
|
|
195
195
|
"wiring in conversation-routes.ts into a shared helper.",
|
|
196
196
|
);
|
|
197
197
|
}
|
|
198
|
-
const sourceActorPrincipalId = conversation.
|
|
198
|
+
const sourceActorPrincipalId = conversation.authContext?.actorPrincipalId;
|
|
199
199
|
// CU is per-conversation (owns step count, AX tree history, loop detection).
|
|
200
200
|
if (
|
|
201
201
|
shouldAttachHostProxyForCapability(
|
package/src/daemon/server.ts
CHANGED