@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
|
@@ -22,7 +22,6 @@ import type { Message } from "../providers/types.js";
|
|
|
22
22
|
|
|
23
23
|
mock.module("../memory/conversation-crud.js", () => ({
|
|
24
24
|
getConversationOverrideProfile: () => undefined,
|
|
25
|
-
reserveMessage: mock(async () => ({ id: "msg-reserve" })),
|
|
26
25
|
}));
|
|
27
26
|
|
|
28
27
|
mock.module("../config/loader.js", () => ({
|
|
@@ -62,36 +61,37 @@ mock.module("../daemon/disk-pressure-guard.js", () => ({
|
|
|
62
61
|
}),
|
|
63
62
|
}));
|
|
64
63
|
|
|
64
|
+
import type { Conversation } from "../daemon/conversation.js";
|
|
65
65
|
import {
|
|
66
66
|
__resetWakeChainForTests,
|
|
67
67
|
wakeAgentForOpportunity,
|
|
68
|
-
type WakeTarget,
|
|
69
68
|
} from "../runtime/agent-wake.js";
|
|
70
69
|
|
|
71
|
-
function makeTarget():
|
|
72
|
-
const
|
|
70
|
+
function makeTarget(): Conversation {
|
|
71
|
+
const messages: Message[] = [];
|
|
73
72
|
let processing = false;
|
|
74
|
-
|
|
73
|
+
const target = {
|
|
75
74
|
conversationId: "conv-wake-callsite",
|
|
76
75
|
agentLoop: {
|
|
77
|
-
run:
|
|
76
|
+
run: async ({ messages }: { messages: Message[] }) => ({
|
|
78
77
|
history: messages,
|
|
79
78
|
exitReason: null,
|
|
80
79
|
appendedNewMessages: false,
|
|
81
80
|
newMessages: [],
|
|
82
|
-
})
|
|
81
|
+
}),
|
|
83
82
|
},
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
history.push(msg);
|
|
87
|
-
},
|
|
88
|
-
emitAgentEvent: () => {},
|
|
83
|
+
messages,
|
|
84
|
+
getMessages: () => messages,
|
|
89
85
|
isProcessing: () => processing,
|
|
90
|
-
|
|
86
|
+
setProcessing: (on: boolean) => {
|
|
91
87
|
processing = on;
|
|
92
88
|
},
|
|
93
|
-
|
|
89
|
+
setTrustContext: () => {},
|
|
90
|
+
getTurnChannelContext: () => null,
|
|
91
|
+
getTurnInterfaceContext: () => null,
|
|
92
|
+
drainQueue: async () => {},
|
|
94
93
|
};
|
|
94
|
+
return target as unknown as Conversation;
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
beforeEach(() => {
|
|
@@ -48,8 +48,9 @@ mock.module("../config/loader.js", () => ({
|
|
|
48
48
|
}),
|
|
49
49
|
}));
|
|
50
50
|
|
|
51
|
-
import type {
|
|
51
|
+
import type { AgentLoopRunOptions } from "../agent/loop.js";
|
|
52
52
|
import { LLMSchema } from "../config/schemas/llm.js";
|
|
53
|
+
import type { Conversation } from "../daemon/conversation.js";
|
|
53
54
|
import { RetryProvider } from "../providers/retry.js";
|
|
54
55
|
import type {
|
|
55
56
|
Message,
|
|
@@ -60,7 +61,6 @@ import type {
|
|
|
60
61
|
import {
|
|
61
62
|
__resetWakeChainForTests,
|
|
62
63
|
wakeAgentForOpportunity,
|
|
63
|
-
type WakeTarget,
|
|
64
64
|
} from "../runtime/agent-wake.js";
|
|
65
65
|
|
|
66
66
|
interface RunArgs {
|
|
@@ -69,44 +69,40 @@ interface RunArgs {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
function makeTarget(): {
|
|
72
|
-
target:
|
|
72
|
+
target: Conversation;
|
|
73
73
|
runArgs: RunArgs[];
|
|
74
74
|
} {
|
|
75
75
|
const runArgs: RunArgs[] = [];
|
|
76
|
-
const
|
|
76
|
+
const messages: Message[] = [
|
|
77
77
|
{ role: "user", content: [{ type: "text", text: "hi" }] },
|
|
78
78
|
];
|
|
79
79
|
let processing = false;
|
|
80
80
|
|
|
81
|
-
const target
|
|
81
|
+
const target = {
|
|
82
82
|
conversationId: "conv-wake-override",
|
|
83
83
|
agentLoop: {
|
|
84
|
-
run:
|
|
85
|
-
messages: Message[],
|
|
86
|
-
_onEvent: (event: AgentEvent) => void | Promise<void>,
|
|
87
|
-
options?: AgentLoopRunOptions,
|
|
88
|
-
) => {
|
|
84
|
+
run: async (options: AgentLoopRunOptions) => {
|
|
89
85
|
runArgs.push({
|
|
90
|
-
messages: [...messages],
|
|
86
|
+
messages: [...options.messages],
|
|
91
87
|
options,
|
|
92
88
|
});
|
|
93
89
|
// Return the input verbatim → silent no-op (no assistant tail).
|
|
94
90
|
// Wake never yields at a checkpoint, so the pause-reason is null.
|
|
95
|
-
return { history: messages, exitReason: null };
|
|
96
|
-
}
|
|
97
|
-
},
|
|
98
|
-
getMessages: () => history,
|
|
99
|
-
pushMessage: (msg) => {
|
|
100
|
-
history.push(msg);
|
|
91
|
+
return { history: options.messages, exitReason: null };
|
|
92
|
+
},
|
|
101
93
|
},
|
|
102
|
-
|
|
94
|
+
messages,
|
|
95
|
+
getMessages: () => messages,
|
|
103
96
|
isProcessing: () => processing,
|
|
104
|
-
|
|
97
|
+
setProcessing: (on: boolean) => {
|
|
105
98
|
processing = on;
|
|
106
99
|
},
|
|
107
|
-
|
|
100
|
+
setTrustContext: () => {},
|
|
101
|
+
getTurnChannelContext: () => null,
|
|
102
|
+
getTurnInterfaceContext: () => null,
|
|
103
|
+
drainQueue: async () => {},
|
|
108
104
|
};
|
|
109
|
-
return { target, runArgs };
|
|
105
|
+
return { target: target as unknown as Conversation, runArgs };
|
|
110
106
|
}
|
|
111
107
|
|
|
112
108
|
beforeEach(() => {
|
|
@@ -2,7 +2,11 @@ import { beforeEach, describe, expect, mock, test } from "bun:test";
|
|
|
2
2
|
|
|
3
3
|
import type { Anthropic } from "@anthropic-ai/sdk";
|
|
4
4
|
|
|
5
|
-
import type {
|
|
5
|
+
import type {
|
|
6
|
+
ContentBlock,
|
|
7
|
+
Message,
|
|
8
|
+
ToolDefinition,
|
|
9
|
+
} from "../providers/types.js";
|
|
6
10
|
|
|
7
11
|
// ---------------------------------------------------------------------------
|
|
8
12
|
// Mock Anthropic SDK — must be before importing the provider
|
|
@@ -102,7 +106,6 @@ mock.module("@anthropic-ai/sdk", () => ({
|
|
|
102
106
|
}));
|
|
103
107
|
|
|
104
108
|
// Import after mocking
|
|
105
|
-
import { cachedTextBlock } from "../memory/v3/provider-blocks.js";
|
|
106
109
|
import { AnthropicProvider } from "../providers/anthropic/client.js";
|
|
107
110
|
import {
|
|
108
111
|
isPlaceholderSentinelText,
|
|
@@ -138,6 +141,18 @@ function toolResultMsg(toolUseId: string, content: string): Message {
|
|
|
138
141
|
};
|
|
139
142
|
}
|
|
140
143
|
|
|
144
|
+
// A stable prefix text block carrying a 1h-TTL cache_control breakpoint, as a
|
|
145
|
+
// caller may stamp before the provider sees it. The internal ContentBlock type
|
|
146
|
+
// omits cache_control (only the provider transforms it onto the wire), so reach
|
|
147
|
+
// it via a cast.
|
|
148
|
+
function cachedPrefixBlock(text: string): ContentBlock {
|
|
149
|
+
return {
|
|
150
|
+
type: "text",
|
|
151
|
+
text,
|
|
152
|
+
cache_control: { type: "ephemeral", ttl: "1h" },
|
|
153
|
+
} as unknown as ContentBlock;
|
|
154
|
+
}
|
|
155
|
+
|
|
141
156
|
const sampleTools: ToolDefinition[] = [
|
|
142
157
|
{
|
|
143
158
|
name: "file_read",
|
|
@@ -325,13 +340,13 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
325
340
|
});
|
|
326
341
|
|
|
327
342
|
test("preserves a caller's 1h block cache_control and sends the extended-cache beta (non-Haiku)", async () => {
|
|
328
|
-
//
|
|
329
|
-
//
|
|
343
|
+
// A caller that stamps a stable prefix block with a 1h TTL: the non-Haiku
|
|
344
|
+
// path must forward it unchanged and send the beta header.
|
|
330
345
|
await provider.sendMessage([
|
|
331
346
|
{
|
|
332
347
|
role: "user",
|
|
333
348
|
content: [
|
|
334
|
-
|
|
349
|
+
cachedPrefixBlock("stable pages block"),
|
|
335
350
|
{ type: "text", text: "volatile current message" },
|
|
336
351
|
],
|
|
337
352
|
},
|
|
@@ -349,7 +364,7 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
349
364
|
|
|
350
365
|
test("strips ttl from a caller's block cache_control and omits the beta for Haiku", async () => {
|
|
351
366
|
// Haiku doesn't support the extended-cache-ttl beta, so a caller-stamped
|
|
352
|
-
// 1h ttl
|
|
367
|
+
// 1h ttl on a message block must be stripped before sending.
|
|
353
368
|
const haiku = new AnthropicProvider(
|
|
354
369
|
"sk-ant-test",
|
|
355
370
|
"claude-haiku-4-5-20251001",
|
|
@@ -358,7 +373,7 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
358
373
|
{
|
|
359
374
|
role: "user",
|
|
360
375
|
content: [
|
|
361
|
-
|
|
376
|
+
cachedPrefixBlock("stable pages block"),
|
|
362
377
|
{ type: "text", text: "volatile current message" },
|
|
363
378
|
],
|
|
364
379
|
},
|
|
@@ -385,7 +400,7 @@ describe("AnthropicProvider — Cache-Control Characterization", () => {
|
|
|
385
400
|
{
|
|
386
401
|
role: "user",
|
|
387
402
|
content: [
|
|
388
|
-
|
|
403
|
+
cachedPrefixBlock("stable pages block"),
|
|
389
404
|
{ type: "text", text: "volatile current message" },
|
|
390
405
|
],
|
|
391
406
|
},
|
|
@@ -81,6 +81,7 @@ mock.module("../bundler/app-compiler.js", () => ({
|
|
|
81
81
|
import * as appCreateScript from "../config/bundled-skills/app-builder/tools/app-create.js";
|
|
82
82
|
import * as appDeleteScript from "../config/bundled-skills/app-builder/tools/app-delete.js";
|
|
83
83
|
import * as appRefreshScript from "../config/bundled-skills/app-builder/tools/app-refresh.js";
|
|
84
|
+
import * as appUpdateScript from "../config/bundled-skills/app-builder/tools/app-update.js";
|
|
84
85
|
|
|
85
86
|
// ---------------------------------------------------------------------------
|
|
86
87
|
// Tests
|
|
@@ -171,4 +172,24 @@ describe("app-builder skill tool scripts", () => {
|
|
|
171
172
|
expect(parsed.appId).toBe("app-1");
|
|
172
173
|
});
|
|
173
174
|
});
|
|
175
|
+
|
|
176
|
+
// ---- app-update --------------------------------------------------------
|
|
177
|
+
|
|
178
|
+
describe("app-update", () => {
|
|
179
|
+
test("exports a run function", () => {
|
|
180
|
+
expect(typeof appUpdateScript.run).toBe("function");
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test("delegates to executeAppUpdate and returns result", async () => {
|
|
184
|
+
const result = await appUpdateScript.run(
|
|
185
|
+
{ app_id: "app-1", name: "Renamed" },
|
|
186
|
+
makeContext(),
|
|
187
|
+
);
|
|
188
|
+
expect(result.isError).toBe(false);
|
|
189
|
+
const parsed = JSON.parse(result.content);
|
|
190
|
+
expect(parsed.updated).toBe(true);
|
|
191
|
+
expect(parsed.appId).toBe("app-1");
|
|
192
|
+
expect(parsed.name).toBe("Renamed");
|
|
193
|
+
});
|
|
194
|
+
});
|
|
174
195
|
});
|
|
@@ -103,7 +103,7 @@ const { ROUTES } = await import("../runtime/routes/host-app-control-routes.js");
|
|
|
103
103
|
const { surfaceProxyResolver } =
|
|
104
104
|
await import("../daemon/conversation-surfaces.js");
|
|
105
105
|
const { setConversation, clearConversations } =
|
|
106
|
-
await import("../daemon/conversation-
|
|
106
|
+
await import("../daemon/conversation-registry.js");
|
|
107
107
|
type SurfaceConversationContext =
|
|
108
108
|
import("../daemon/conversation-surfaces.js").SurfaceConversationContext;
|
|
109
109
|
|
|
@@ -19,6 +19,7 @@ const ALLOWLIST = new Set([
|
|
|
19
19
|
"assistant/src/memory/app-store.ts", // defines getAppsDir
|
|
20
20
|
"assistant/src/memory/app-git-service.ts", // uses getAppsDir for git repo root, not per-app paths
|
|
21
21
|
"assistant/src/daemon/app-source-watcher.ts", // uses getAppsDir for recursive fs.watch root, not per-app paths
|
|
22
|
+
"assistant/src/tools/filesystem/write.ts", // uses getAppsDir as an exemption root for the artifact-HTML guard, not per-app paths
|
|
22
23
|
]);
|
|
23
24
|
|
|
24
25
|
function isTestFile(filePath: string): boolean {
|
|
@@ -32,6 +32,7 @@ import type { AppStore } from "../tools/apps/executors.js";
|
|
|
32
32
|
import {
|
|
33
33
|
executeAppCreate,
|
|
34
34
|
executeAppRefresh,
|
|
35
|
+
executeAppUpdate,
|
|
35
36
|
} from "../tools/apps/executors.js";
|
|
36
37
|
|
|
37
38
|
// ---------------------------------------------------------------------------
|
|
@@ -89,6 +90,46 @@ function mockStore(
|
|
|
89
90
|
// ---------------------------------------------------------------------------
|
|
90
91
|
|
|
91
92
|
describe("executeAppCreate", () => {
|
|
93
|
+
test("falls back to the preview title when the name is omitted or blank", async () => {
|
|
94
|
+
let createdParams: Record<string, unknown> | undefined;
|
|
95
|
+
const app = makeMultifileApp({ name: "Coffee Tracker" });
|
|
96
|
+
const store: AppStore = {
|
|
97
|
+
...mockStore(app, {}),
|
|
98
|
+
createApp: (params) => {
|
|
99
|
+
createdParams = params as unknown as Record<string, unknown>;
|
|
100
|
+
return app;
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const result = await executeAppCreate(
|
|
105
|
+
{ name: " ", preview: { title: "Coffee Tracker" } },
|
|
106
|
+
store,
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
expect(result.isError).toBe(false);
|
|
110
|
+
expect(createdParams?.name).toBe("Coffee Tracker");
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("defaults the name to 'New App' when neither name nor preview title is given", async () => {
|
|
114
|
+
let createdParams: Record<string, unknown> | undefined;
|
|
115
|
+
const app = makeMultifileApp({ name: "New App" });
|
|
116
|
+
const store: AppStore = {
|
|
117
|
+
...mockStore(app, {}),
|
|
118
|
+
createApp: (params) => {
|
|
119
|
+
createdParams = params as unknown as Record<string, unknown>;
|
|
120
|
+
return app;
|
|
121
|
+
},
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const result = await executeAppCreate(
|
|
125
|
+
{} as unknown as Parameters<typeof executeAppCreate>[0],
|
|
126
|
+
store,
|
|
127
|
+
);
|
|
128
|
+
|
|
129
|
+
expect(result.isError).toBe(false);
|
|
130
|
+
expect(createdParams?.name).toBe("New App");
|
|
131
|
+
});
|
|
132
|
+
|
|
92
133
|
test("creates multifile app with src/ scaffold", async () => {
|
|
93
134
|
const files: Record<string, string> = {};
|
|
94
135
|
let createdParams: Record<string, unknown> | undefined;
|
|
@@ -408,3 +449,94 @@ describe("executeAppRefresh", () => {
|
|
|
408
449
|
expect(parsed.error).toContain("not found");
|
|
409
450
|
});
|
|
410
451
|
});
|
|
452
|
+
|
|
453
|
+
// ---------------------------------------------------------------------------
|
|
454
|
+
// executeAppUpdate
|
|
455
|
+
// ---------------------------------------------------------------------------
|
|
456
|
+
|
|
457
|
+
describe("executeAppUpdate", () => {
|
|
458
|
+
test("updates metadata and recompiles a multifile app", async () => {
|
|
459
|
+
const app = makeMultifileApp({ name: "Old", description: "old desc" });
|
|
460
|
+
let updateArgs: unknown;
|
|
461
|
+
const store: AppStore = {
|
|
462
|
+
...mockStore(app),
|
|
463
|
+
updateApp: (_id, updates) => {
|
|
464
|
+
updateArgs = updates;
|
|
465
|
+
return { ...app, ...updates };
|
|
466
|
+
},
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
const result = await executeAppUpdate(
|
|
470
|
+
{ app_id: app.id, name: "New Name", description: "new desc" },
|
|
471
|
+
store,
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
expect(result.isError).toBe(false);
|
|
475
|
+
expect(updateArgs).toEqual({ name: "New Name", description: "new desc" });
|
|
476
|
+
const parsed = JSON.parse(result.content);
|
|
477
|
+
expect(parsed.updated).toBe(true);
|
|
478
|
+
expect(parsed.name).toBe("New Name");
|
|
479
|
+
expect(parsed.description).toBe("new desc");
|
|
480
|
+
expect(parsed.compiled).toBe(true);
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
test("writes source_files before recompiling", async () => {
|
|
484
|
+
const files: Record<string, string> = {};
|
|
485
|
+
const app = makeMultifileApp();
|
|
486
|
+
const store: AppStore = {
|
|
487
|
+
...mockStore(app, files),
|
|
488
|
+
updateApp: (_id, updates) => ({ ...app, ...updates }),
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
const result = await executeAppUpdate(
|
|
492
|
+
{ app_id: app.id, source_files: { "src/App.tsx": "// new code" } },
|
|
493
|
+
store,
|
|
494
|
+
);
|
|
495
|
+
|
|
496
|
+
expect(result.isError).toBe(false);
|
|
497
|
+
expect(files["src/App.tsx"]).toBe("// new code");
|
|
498
|
+
expect(JSON.parse(result.content).compiled).toBe(true);
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
test("legacy app: updates without compiling", async () => {
|
|
502
|
+
const app = makeLegacyApp({ name: "Legacy" });
|
|
503
|
+
const store: AppStore = {
|
|
504
|
+
...mockStore(app),
|
|
505
|
+
updateApp: (_id, updates) => ({ ...app, ...updates }),
|
|
506
|
+
};
|
|
507
|
+
|
|
508
|
+
const result = await executeAppUpdate(
|
|
509
|
+
{ app_id: app.id, name: "Renamed" },
|
|
510
|
+
store,
|
|
511
|
+
);
|
|
512
|
+
|
|
513
|
+
expect(result.isError).toBe(false);
|
|
514
|
+
const parsed = JSON.parse(result.content);
|
|
515
|
+
expect(parsed.updated).toBe(true);
|
|
516
|
+
expect(parsed.name).toBe("Renamed");
|
|
517
|
+
expect(parsed.compiled).toBeUndefined();
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
test("returns error for unknown app", async () => {
|
|
521
|
+
const store = mockStore(makeMultifileApp());
|
|
522
|
+
const result = await executeAppUpdate({ app_id: "nope" }, store);
|
|
523
|
+
|
|
524
|
+
expect(result.isError).toBe(true);
|
|
525
|
+
expect(JSON.parse(result.content).error).toContain("not found");
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
test("rejects invalid source_files", async () => {
|
|
529
|
+
const app = makeMultifileApp();
|
|
530
|
+
const store = mockStore(app);
|
|
531
|
+
const result = await executeAppUpdate(
|
|
532
|
+
{
|
|
533
|
+
app_id: app.id,
|
|
534
|
+
source_files: { "src/App.tsx": 123 as unknown as string },
|
|
535
|
+
},
|
|
536
|
+
store,
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
expect(result.isError).toBe(true);
|
|
540
|
+
expect(JSON.parse(result.content).error).toContain("must be a string");
|
|
541
|
+
});
|
|
542
|
+
});
|
|
@@ -173,9 +173,10 @@ mock.module("../memory/retriever.js", () => ({
|
|
|
173
173
|
injectMemoryRecallAsUserBlock: (msgs: Message[]) => msgs,
|
|
174
174
|
}));
|
|
175
175
|
|
|
176
|
-
mock.module("../
|
|
176
|
+
mock.module("../plugins/defaults/compaction/window-manager.js", () => ({
|
|
177
177
|
ContextWindowManager: class {
|
|
178
178
|
constructor() {}
|
|
179
|
+
updateConfig() {}
|
|
179
180
|
shouldCompact() {
|
|
180
181
|
return { needed: false, estimatedTokens: 0 };
|
|
181
182
|
}
|
|
@@ -208,10 +209,10 @@ mock.module("../agent/loop.js", () => ({
|
|
|
208
209
|
getActiveModel() {
|
|
209
210
|
return undefined;
|
|
210
211
|
}
|
|
211
|
-
async run(
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
): Promise<Message[]> {
|
|
212
|
+
async run(_options: {
|
|
213
|
+
messages: Message[];
|
|
214
|
+
onEvent: (event: AgentEvent) => void;
|
|
215
|
+
}): Promise<Message[]> {
|
|
215
216
|
return [];
|
|
216
217
|
}
|
|
217
218
|
},
|
|
@@ -119,12 +119,15 @@ mock.module("../permissions/trust-store.js", () => ({
|
|
|
119
119
|
// ---------------------------------------------------------------------------
|
|
120
120
|
let _conversationFactory: (() => Conversation) | undefined;
|
|
121
121
|
|
|
122
|
-
mock.module("../daemon/conversation-
|
|
122
|
+
mock.module("../daemon/conversation-registry.js", () => ({
|
|
123
123
|
findConversation: () => {
|
|
124
124
|
// Return the current test session for any conversation ID lookup.
|
|
125
125
|
if (!_conversationFactory) return undefined;
|
|
126
126
|
return _conversationFactory();
|
|
127
127
|
},
|
|
128
|
+
}));
|
|
129
|
+
|
|
130
|
+
mock.module("../daemon/conversation-store.js", () => ({
|
|
128
131
|
getOrCreateConversation: async () => {
|
|
129
132
|
if (!_conversationFactory)
|
|
130
133
|
throw new Error("_conversationFactory not set in test");
|
|
@@ -117,7 +117,7 @@ mock.module("../memory/conversation-crud.js", () => ({
|
|
|
117
117
|
findMostRecentRetrospectiveFor: mock(() => null),
|
|
118
118
|
forkConversation: mock(() => ({ id: "conv-fork" })),
|
|
119
119
|
getConversationOverrideProfile: () => undefined,
|
|
120
|
-
|
|
120
|
+
resolveOverrideProfile: () => undefined,
|
|
121
121
|
getConversationMemoryScopeId: () => "default",
|
|
122
122
|
getConversationOriginChannel: () => null,
|
|
123
123
|
getConversationOriginInterface: () => null,
|
|
@@ -54,7 +54,10 @@ mock.module("../memory/llm-request-log-store.js", () => ({
|
|
|
54
54
|
}));
|
|
55
55
|
|
|
56
56
|
// ── Imports (after mocks) ─────────────────────────────────────────────────────
|
|
57
|
-
import {
|
|
57
|
+
import {
|
|
58
|
+
buildPersistedAssistantContent,
|
|
59
|
+
stampThinkingTiming,
|
|
60
|
+
} from "../daemon/conversation-agent-loop-handlers.js";
|
|
58
61
|
import type { ToolActivityMetadata } from "../daemon/message-types/web-activity.js";
|
|
59
62
|
import type { ContentBlock } from "../providers/types.js";
|
|
60
63
|
|
|
@@ -182,3 +185,74 @@ describe("buildPersistedAssistantContent — native activityMetadata", () => {
|
|
|
182
185
|
expect(block._activityMetadata).toBeUndefined();
|
|
183
186
|
});
|
|
184
187
|
});
|
|
188
|
+
|
|
189
|
+
describe("stampThinkingTiming", () => {
|
|
190
|
+
test("stamps internal timing onto thinking blocks by position", () => {
|
|
191
|
+
// GIVEN a turn that interleaves text and two thinking blocks AND the
|
|
192
|
+
// per-block timing captured while streaming (one entry per thinking block,
|
|
193
|
+
// in stream order)
|
|
194
|
+
const content = [
|
|
195
|
+
{ type: "thinking", thinking: "first", signature: "s1" },
|
|
196
|
+
{ type: "text", text: "answer" },
|
|
197
|
+
{ type: "thinking", thinking: "second", signature: "s2" },
|
|
198
|
+
] as unknown as ContentBlock[];
|
|
199
|
+
const timings = [
|
|
200
|
+
{ startedAt: 100, completedAt: 250 },
|
|
201
|
+
{ startedAt: 400, completedAt: 480 },
|
|
202
|
+
];
|
|
203
|
+
|
|
204
|
+
// WHEN the content is stamped before persistence
|
|
205
|
+
const stamped = stampThinkingTiming(content, timings) as unknown as Array<
|
|
206
|
+
Record<string, unknown>
|
|
207
|
+
>;
|
|
208
|
+
|
|
209
|
+
// THEN each thinking block carries the `_`-prefixed timing for its position
|
|
210
|
+
expect(stamped[0]).toMatchObject({
|
|
211
|
+
type: "thinking",
|
|
212
|
+
_startedAt: 100,
|
|
213
|
+
_completedAt: 250,
|
|
214
|
+
});
|
|
215
|
+
expect(stamped[2]).toMatchObject({
|
|
216
|
+
type: "thinking",
|
|
217
|
+
_startedAt: 400,
|
|
218
|
+
_completedAt: 480,
|
|
219
|
+
});
|
|
220
|
+
// AND the interleaved text block is left untouched
|
|
221
|
+
expect(stamped[1]).toEqual({ type: "text", text: "answer" });
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test("leaves thinking blocks unstamped when no timing was captured", () => {
|
|
225
|
+
// GIVEN thinking content but an empty timing list (thinking streaming was
|
|
226
|
+
// disabled, so no per-block timing was recorded this turn)
|
|
227
|
+
const content = [
|
|
228
|
+
{ type: "thinking", thinking: "first", signature: "s1" },
|
|
229
|
+
] as unknown as ContentBlock[];
|
|
230
|
+
|
|
231
|
+
// WHEN the content is stamped with no timing
|
|
232
|
+
const stamped = stampThinkingTiming(content, []);
|
|
233
|
+
|
|
234
|
+
// THEN the original content is returned unchanged so the UI hides duration,
|
|
235
|
+
// exactly as a tool call with no timing
|
|
236
|
+
expect(stamped).toBe(content);
|
|
237
|
+
expect(stamped[0]).not.toHaveProperty("_startedAt");
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test("stamps only the thinking blocks that have a matching timing entry", () => {
|
|
241
|
+
// GIVEN two thinking blocks but only one captured timing entry (e.g. the
|
|
242
|
+
// second block opened after the timing array was already finalized)
|
|
243
|
+
const content = [
|
|
244
|
+
{ type: "thinking", thinking: "first", signature: "s1" },
|
|
245
|
+
{ type: "thinking", thinking: "second", signature: "s2" },
|
|
246
|
+
] as unknown as ContentBlock[];
|
|
247
|
+
const timings = [{ startedAt: 100, completedAt: 250 }];
|
|
248
|
+
|
|
249
|
+
// WHEN the content is stamped
|
|
250
|
+
const stamped = stampThinkingTiming(content, timings) as unknown as Array<
|
|
251
|
+
Record<string, unknown>
|
|
252
|
+
>;
|
|
253
|
+
|
|
254
|
+
// THEN the first block is stamped and the unmatched second block is left as-is
|
|
255
|
+
expect(stamped[0]).toMatchObject({ _startedAt: 100, _completedAt: 250 });
|
|
256
|
+
expect(stamped[1]).not.toHaveProperty("_startedAt");
|
|
257
|
+
});
|
|
258
|
+
});
|
|
@@ -16,7 +16,7 @@ mock.module("../util/logger.js", () => ({
|
|
|
16
16
|
}));
|
|
17
17
|
|
|
18
18
|
const _conversationMocks = new Map<string, unknown>();
|
|
19
|
-
mock.module("../daemon/conversation-
|
|
19
|
+
mock.module("../daemon/conversation-registry.js", () => ({
|
|
20
20
|
findConversation: (id: string) => _conversationMocks.get(id),
|
|
21
21
|
}));
|
|
22
22
|
|
|
@@ -11,7 +11,7 @@ mock.module("../util/logger.js", () => ({
|
|
|
11
11
|
|
|
12
12
|
// Map conversationId → mock session so findConversation returns the right mock.
|
|
13
13
|
const conversationMocks = new Map<string, unknown>();
|
|
14
|
-
mock.module("../daemon/conversation-
|
|
14
|
+
mock.module("../daemon/conversation-registry.js", () => ({
|
|
15
15
|
findConversation: (id: string) => conversationMocks.get(id),
|
|
16
16
|
}));
|
|
17
17
|
|