@vellumai/assistant 0.8.7 → 0.8.8-dev.202606052332.17fc8ea
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/Dockerfile +20 -4
- package/bun.lock +2 -2
- package/docker-entrypoint.sh +4 -2
- package/docker-init-apt-root.sh +3 -1
- package/docker-kata-apt-env.sh +3 -1
- package/docker-kata-runtime-family.sh +12 -0
- package/docs/architecture/memory.md +1 -1
- package/examples/plugins/echo/README.md +61 -66
- 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/server-message.ts +3 -3
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
- package/openapi.yaml +3378 -335
- package/package.json +2 -2
- package/scripts/generate-openapi.ts +68 -41
- package/src/__tests__/agent-loop-exit-reason.test.ts +35 -93
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +1 -1
- package/src/__tests__/agent-loop.test.ts +37 -87
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/annotate-activity-metadata.test.ts +262 -0
- package/src/__tests__/annotate-risk-options.test.ts +2 -3
- package/src/__tests__/anthropic-provider.test.ts +95 -2
- package/src/__tests__/app-control-flow.test.ts +1 -1
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +4 -1
- package/src/__tests__/assistant-event-hub.test.ts +25 -0
- package/src/__tests__/assistant-events-sse-shed.test.ts +8 -0
- package/src/__tests__/{conversation-stream-state.test.ts → assistant-stream-state.test.ts} +252 -91
- package/src/__tests__/auth-fallback-events-store.test.ts +116 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +6 -0
- package/src/__tests__/btw-routes.test.ts +62 -3
- package/src/__tests__/build-persisted-content.test.ts +184 -0
- package/src/__tests__/catalog-files.test.ts +1 -1
- package/src/__tests__/channel-approval-routes.test.ts +1 -1
- package/src/__tests__/channel-approvals.test.ts +1 -1
- package/src/__tests__/clawhub-files.test.ts +1 -1
- package/src/__tests__/compaction-circuit.test.ts +258 -0
- package/src/__tests__/compaction-direct.test.ts +132 -0
- package/src/__tests__/compaction.benchmark.test.ts +0 -30
- package/src/__tests__/config-watcher.test.ts +1 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +57 -19
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +6 -5
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +10 -7
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +316 -1143
- package/src/__tests__/conversation-agent-loop.test.ts +638 -1655
- package/src/__tests__/conversation-analysis-routes.test.ts +6 -0
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-history-web-search.test.ts +11 -1
- package/src/__tests__/conversation-pairing.test.ts +4 -31
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +6 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +30 -10
- package/src/__tests__/conversation-queue.test.ts +2 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +3 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +6 -5
- package/src/__tests__/conversation-runtime-assembly.test.ts +310 -300
- package/src/__tests__/conversation-runtime-workspace.test.ts +105 -45
- package/src/__tests__/conversation-slash-commands.test.ts +8 -42
- package/src/__tests__/conversation-slash-queue.test.ts +6 -1
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +84 -0
- package/src/__tests__/conversation-sync-tags.test.ts +27 -15
- package/src/__tests__/conversation-title-service.test.ts +135 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +17 -16
- package/src/__tests__/conversation-workspace-injection.test.ts +67 -2
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +7 -6
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/cross-provider-web-search.test.ts +214 -1
- package/src/__tests__/db-acp-history.test.ts +101 -0
- package/src/__tests__/db-schedule-syntax-migration.test.ts +5 -0
- package/src/__tests__/dm-persistence.test.ts +5 -1
- package/src/__tests__/dynamic-page-surface.test.ts +31 -0
- package/src/__tests__/empty-response-hook.test.ts +304 -0
- package/src/__tests__/feature-flag-test-helpers.ts +2 -2
- package/src/__tests__/file-write-tool.test.ts +63 -0
- package/src/__tests__/gateway-only-guard.test.ts +12 -2
- package/src/__tests__/gemini-image-service.test.ts +13 -0
- package/src/__tests__/guardian-grant-minting.test.ts +1 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +2 -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__/helpers/mock-provider.ts +110 -0
- package/src/__tests__/helpers/native-web-search-harness.ts +129 -0
- package/src/__tests__/history-repair-hook.test.ts +1 -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__/identity-intro-cache.test.ts +12 -100
- package/src/__tests__/identity-routes.test.ts +248 -7
- package/src/__tests__/inbound-slack-persistence.test.ts +5 -1
- package/src/__tests__/injector-background-turn.test.ts +3 -9
- package/src/__tests__/injector-chain.test.ts +139 -275
- package/src/__tests__/injector-disk-pressure.test.ts +75 -41
- package/src/__tests__/injector-document-comments.test.ts +3 -3
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +30 -22
- package/src/__tests__/injector-v3-suppression.test.ts +31 -37
- package/src/__tests__/internal-telemetry-routes.test.ts +109 -0
- package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
- package/src/__tests__/list-messages-page-latest.test.ts +60 -0
- package/src/__tests__/list-messages-tool-merge.test.ts +20 -0
- package/src/__tests__/llm-usage-store.test.ts +223 -1
- package/src/__tests__/memory-retrieval-hook.test.ts +297 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +103 -35
- package/src/__tests__/native-web-search.test.ts +191 -0
- package/src/__tests__/onboarding-template-contract.test.ts +2 -0
- package/src/__tests__/openai-image-service.test.ts +17 -0
- package/src/__tests__/openai-provider.test.ts +31 -1
- package/src/__tests__/{overflow-reduce-pipeline.test.ts → overflow-reduction-loop.test.ts} +64 -284
- package/src/__tests__/persist-unsendable-image.test.ts +215 -0
- package/src/__tests__/persistence-secret-redaction.test.ts +1 -0
- package/src/__tests__/pkb-autoinject.test.ts +2 -5
- package/src/__tests__/plugin-api-shim.test.ts +3 -6
- package/src/__tests__/plugin-bootstrap.test.ts +14 -40
- package/src/__tests__/plugin-registry.test.ts +3 -76
- package/src/__tests__/plugin-types.test.ts +0 -193
- package/src/__tests__/process-message-display-content.test.ts +6 -2
- package/src/__tests__/reaction-persistence.test.ts +1 -1
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +5 -1
- package/src/__tests__/resolve-trust-class.test.ts +4 -4
- package/src/__tests__/runtime-events-sse-reconnect.test.ts +60 -23
- package/src/__tests__/schedule-routes.test.ts +603 -2
- package/src/__tests__/schedule-store.test.ts +41 -0
- package/src/__tests__/schedule-tools.test.ts +35 -0
- package/src/__tests__/send-endpoint-busy.test.ts +4 -1
- package/src/__tests__/server-history-render.test.ts +314 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +33 -0
- package/src/__tests__/skillssh-files.test.ts +1 -1
- package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
- 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__/system-prompt.test.ts +20 -0
- package/src/__tests__/task-scheduler.test.ts +162 -1
- package/src/__tests__/terminal-tools.test.ts +6 -1
- package/src/__tests__/title-generate-hook.test.ts +319 -0
- package/src/__tests__/tool-error-hook.test.ts +278 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +468 -5
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +1 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +127 -0
- package/src/__tests__/tool-result-truncation.test.ts +0 -2
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +254 -0
- package/src/__tests__/ui-work-result-surface.test.ts +159 -0
- package/src/__tests__/usage-routes.test.ts +285 -1
- package/src/__tests__/user-plugin-loader.test.ts +54 -286
- package/src/__tests__/voice-session-bridge.test.ts +6 -3
- package/src/__tests__/web-search-backend-failure.test.ts +166 -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-history-db.ts +82 -0
- package/src/acp/__tests__/helpers/exec-file-stub.ts +101 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +137 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
- package/src/acp/__tests__/session-manager-resume.test.ts +736 -0
- package/src/acp/agent-process.ts +61 -1
- package/src/acp/auto-install.test.ts +196 -0
- package/src/acp/auto-install.ts +177 -0
- package/src/acp/client-handler.ts +31 -0
- package/src/acp/feature-gate.test.ts +48 -0
- package/src/acp/feature-gate.ts +34 -0
- package/src/acp/prepare-agent-env.ts +83 -29
- package/src/acp/resolve-agent.test.ts +320 -7
- package/src/acp/resolve-agent.ts +182 -18
- package/src/acp/resume-hint.ts +25 -0
- package/src/acp/session-manager.ts +495 -73
- package/src/acp/types.ts +8 -0
- package/src/agent/compaction-circuit.ts +60 -102
- package/src/agent/loop.ts +362 -485
- package/src/api/events/assistant-thinking-delta.ts +33 -0
- package/src/api/events/tool-output-chunk.ts +45 -0
- package/src/api/events/tool-use-preview-start.ts +32 -0
- package/src/api/events/trace-event.ts +69 -0
- package/src/api/index.ts +48 -13
- package/src/api/responses/conversation-message.ts +374 -0
- package/src/approvals/guardian-request-resolvers.ts +1 -1
- package/src/avatar/__tests__/avatar-store.test.ts +34 -29
- package/src/background-wake/next-wake.ts +1 -0
- package/src/cli/commands/__tests__/notifications.test.ts +58 -14
- package/src/cli/commands/notifications.ts +112 -60
- 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 +6 -0
- package/src/config/assistant-feature-flags.ts +22 -11
- package/src/config/bundled-skills/acp/SKILL.md +83 -31
- package/src/config/bundled-skills/acp/TOOLS.json +4 -4
- package/src/config/bundled-skills/app-builder/SKILL.md +224 -398
- package/src/config/bundled-skills/app-builder/TOOLS.json +29 -0
- 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/references/examples/README.md +17 -0
- package/src/config/bundled-skills/app-builder/references/examples/expense-tracker.md +515 -0
- package/src/config/bundled-skills/app-builder/references/examples/focus-timer.md +342 -0
- package/src/config/bundled-skills/app-builder/references/examples/habit-tracker.md +490 -0
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -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-skills/messaging/SKILL.md +0 -7
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/feature-flag-cache.ts +3 -3
- package/src/config/feature-flag-registry.json +48 -7
- package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
- package/src/config/schemas/__tests__/memory-v3.test.ts +25 -0
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +1 -0
- package/src/config/schemas/memory-v2.ts +8 -0
- package/src/config/schemas/memory-v3.ts +8 -0
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/seed-inference-profiles.ts +2 -2
- package/src/config/skills.ts +13 -0
- package/src/context/compactor.ts +1 -1
- package/src/context/strip-injections.ts +128 -0
- package/src/context/token-estimator.ts +23 -0
- package/src/context/tool-result-truncation.ts +0 -23
- package/src/context/window-manager.ts +5 -7
- package/src/credential-execution/executable-discovery.ts +16 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +6 -0
- package/src/daemon/__tests__/inference-profile-notification.test.ts +153 -0
- package/src/daemon/__tests__/native-web-search-metadata.test.ts +10 -8
- package/src/daemon/assistant-attachments.ts +1 -1
- package/src/daemon/config-watcher.ts +2 -2
- package/src/daemon/context-overflow-reducer.ts +0 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +594 -153
- package/src/daemon/conversation-agent-loop.ts +301 -997
- package/src/daemon/conversation-history.ts +5 -4
- package/src/daemon/conversation-lifecycle.ts +3 -4
- package/src/daemon/conversation-messaging.ts +7 -6
- package/src/daemon/conversation-process.ts +11 -16
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +218 -398
- package/src/daemon/conversation-slash.ts +6 -25
- package/src/daemon/conversation-store.ts +9 -90
- package/src/daemon/conversation-surfaces.ts +222 -4
- package/src/daemon/conversation-tool-setup.ts +2 -29
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +32 -20
- package/src/daemon/external-plugins-bootstrap.ts +17 -18
- package/src/daemon/handlers/config-a2a.ts +51 -36
- package/src/daemon/handlers/config-slack-channel.ts +20 -14
- package/src/daemon/handlers/config-telegram.ts +16 -2
- package/src/daemon/handlers/conversations.ts +3 -1
- package/src/daemon/handlers/shared.ts +156 -84
- package/src/daemon/handlers/skills.ts +42 -10
- package/src/daemon/lifecycle.ts +25 -0
- package/src/daemon/message-types/apps.ts +1 -29
- package/src/daemon/message-types/messages.ts +9 -57
- package/src/daemon/message-types/skills.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +136 -3
- package/src/daemon/now-scratchpad.ts +21 -0
- package/src/daemon/orphan-reaper.test.ts +210 -0
- package/src/daemon/orphan-reaper.ts +240 -0
- package/src/daemon/overflow-reduction-loop.ts +230 -0
- package/src/daemon/persist-unsendable-image.ts +117 -0
- package/src/daemon/process-message.ts +1 -3
- package/src/daemon/server.ts +2 -0
- package/src/daemon/trace-emitter.ts +6 -4
- package/src/daemon/trust-context.ts +19 -0
- package/src/daemon/wake-target-adapter.ts +3 -1
- 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/home/home-greeting-cache.ts +24 -1
- package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
- package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
- package/src/ipc/gateway-client.test.ts +2 -2
- package/src/ipc/gateway-client.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/media/gemini-image-service.ts +15 -0
- package/src/media/openai-image-service.ts +14 -0
- package/src/media/types.ts +34 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +56 -0
- package/src/memory/auth-fallback-events-store.ts +94 -0
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/conversation-title-service.ts +65 -41
- package/src/memory/db-init.ts +6 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-registry.test.ts +119 -0
- package/src/memory/graph/conversation-graph-memory.ts +65 -0
- package/src/memory/job-handlers/conversation-starters.ts +13 -2
- package/src/memory/jobs-store.ts +33 -0
- package/src/memory/jobs-worker.ts +32 -5
- package/src/memory/llm-usage-store.ts +224 -50
- package/src/memory/migrations/222-strip-placeholder-sentinels-from-messages.ts +6 -5
- package/src/memory/migrations/270-schedule-source-conversation.ts +13 -0
- package/src/memory/migrations/271-create-auth-fallback-events.ts +21 -0
- package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
- package/src/memory/migrations/index.ts +3 -0
- package/src/memory/pkb/autoinject.ts +61 -0
- package/src/memory/pkb/context.ts +50 -0
- package/src/memory/pkb/types.ts +14 -0
- package/src/memory/schedule-attribution-sql.ts +104 -0
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/schema/infrastructure.ts +16 -0
- package/src/memory/usage-grouped-buckets.ts +6 -1
- package/src/memory/v2/__tests__/consolidation-job.test.ts +4 -4
- package/src/memory/v2/consolidation-job.ts +14 -5
- package/src/notifications/conversation-pairing.ts +8 -15
- package/src/notifications/decision-engine.ts +6 -3
- package/src/notifications/home-feed-side-effect.ts +12 -1
- package/src/permissions/prompter.ts +4 -0
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +7 -5
- package/src/plugin-api/types.ts +151 -1
- package/src/plugins/defaults/compaction/compact.ts +59 -0
- package/src/plugins/defaults/compaction/package.json +1 -1
- package/src/plugins/defaults/compaction/register.ts +8 -19
- package/src/plugins/defaults/empty-response/hooks/stop.ts +126 -0
- package/src/plugins/defaults/empty-response/register.ts +8 -13
- package/src/plugins/defaults/index.ts +2 -18
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +95 -0
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +216 -0
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +35 -0
- package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +288 -81
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/assign.test.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/health.test.ts +16 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/live-integration.test.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/maintain-job.test.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/orchestrate.test.ts +48 -12
- package/src/plugins/defaults/memory-v3-shadow/__tests__/provider-blocks.test.ts +13 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/reconcile.test.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/render-injection.test.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/router.test.ts +104 -32
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selection-log-store.test.ts +8 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/selector.test.ts +96 -30
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +34 -16
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/health.ts +0 -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 +75 -0
- package/src/plugins/defaults/memory-v3-shadow/llm-retry.ts +32 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +8 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +26 -14
- package/src/plugins/defaults/{llm-call → memory-v3-shadow}/package.json +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/provider-blocks.ts +26 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +3 -3
- package/src/plugins/defaults/memory-v3-shadow/register.ts +26 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/render-injection.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/router.ts +51 -45
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selection-log-store.ts +4 -4
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/selector.ts +61 -46
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +69 -99
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +8 -0
- package/src/plugins/defaults/title-generate/hooks/stop.ts +75 -0
- package/src/plugins/defaults/title-generate/hooks/user-prompt-submit.ts +35 -0
- package/src/plugins/defaults/title-generate/package.json +1 -1
- package/src/plugins/defaults/title-generate/register.ts +18 -18
- package/src/plugins/defaults/tool-error/hooks/post-tool-use.ts +118 -0
- package/src/plugins/defaults/tool-error/package.json +1 -1
- package/src/plugins/defaults/tool-error/register.ts +9 -21
- package/src/plugins/defaults/tool-result-truncate/hooks/post-tool-use.ts +32 -0
- package/src/plugins/defaults/tool-result-truncate/register.ts +10 -21
- package/src/plugins/defaults/tool-result-truncate/terminal.ts +37 -18
- package/src/plugins/external-api.ts +2 -2
- package/src/plugins/pipeline.ts +6 -305
- package/src/plugins/registry.ts +10 -55
- package/src/plugins/types.ts +62 -797
- package/src/plugins/user-loader.ts +30 -127
- package/src/proactive-artifact/aux-message-injector.ts +4 -4
- package/src/proactive-artifact/job.test.ts +8 -13
- package/src/prompts/__tests__/system-prompt.test.ts +42 -0
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +64 -0
- package/src/prompts/templates/BOOTSTRAP.md +2 -2
- package/src/prompts/templates/system-sections.ts +15 -0
- package/src/providers/anthropic/client.ts +37 -29
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +112 -0
- package/src/providers/openai/chat-completions-provider.ts +44 -0
- package/src/providers/openrouter/client.ts +1 -0
- package/src/providers/placeholder-sentinels.ts +35 -0
- package/src/runtime/__tests__/agent-wake.test.ts +10 -6
- package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
- package/src/runtime/agent-wake.ts +2 -5
- package/src/runtime/assistant-event-hub.ts +37 -7
- package/src/runtime/{conversation-stream-state.ts → assistant-stream-state.ts} +132 -58
- package/src/runtime/channel-approvals.ts +1 -1
- package/src/runtime/http-router.ts +16 -21
- package/src/runtime/http-types.ts +16 -70
- package/src/runtime/interactive-ui.ts +1 -1
- package/src/runtime/pending-interactions.ts +1 -0
- package/src/runtime/routes/__tests__/acp-routes.test.ts +283 -55
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +265 -2
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +31 -1
- package/src/runtime/routes/__tests__/memory-v2-routes.test.ts +6 -2
- 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/__tests__/tts-routes.test.ts +6 -2
- package/src/runtime/routes/acp-routes.test.ts +89 -25
- package/src/runtime/routes/acp-routes.ts +81 -29
- package/src/runtime/routes/app-management-routes.ts +6 -117
- package/src/runtime/routes/app-routes.ts +13 -15
- package/src/runtime/routes/approval-routes.ts +1 -1
- package/src/runtime/routes/attachment-routes.ts +26 -15
- package/src/runtime/routes/avatar-routes.ts +26 -0
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/browser-tabs-routes.ts +6 -10
- package/src/runtime/routes/btw-routes.ts +29 -23
- package/src/runtime/routes/consolidation-routes.ts +120 -20
- 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 +3 -1
- package/src/runtime/routes/conversation-routes.ts +372 -185
- package/src/runtime/routes/conversation-starter-routes.ts +13 -7
- package/src/runtime/routes/conversations-import-routes.ts +24 -7
- package/src/runtime/routes/documents-routes.ts +4 -0
- package/src/runtime/routes/domain-routes.ts +51 -37
- package/src/runtime/routes/epoch-millis-range.ts +34 -0
- package/src/runtime/routes/events-routes.ts +28 -34
- package/src/runtime/routes/gateway-log-routes.ts +26 -4
- package/src/runtime/routes/heartbeat-routes.ts +32 -12
- 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-intro-cache.ts +11 -34
- package/src/runtime/routes/identity-routes.ts +224 -18
- package/src/runtime/routes/image-generation-routes.ts +40 -2
- package/src/runtime/routes/inbound-message-handler.ts +1 -1
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/a2a.ts +12 -10
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +16 -0
- package/src/runtime/routes/integrations/slack/channel.ts +4 -0
- package/src/runtime/routes/integrations/slack/share.ts +27 -6
- package/src/runtime/routes/integrations/telegram.ts +6 -0
- package/src/runtime/routes/integrations/twilio.ts +42 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +88 -0
- package/src/runtime/routes/log-export-routes.ts +8 -0
- package/src/runtime/routes/memory-v2-routes.ts +15 -8
- package/src/runtime/routes/memory-v3-routes.ts +66 -34
- package/src/runtime/routes/oauth-apps.ts +66 -12
- package/src/runtime/routes/oauth-providers.ts +44 -5
- package/src/runtime/routes/platform-routes.ts +81 -5
- package/src/runtime/routes/playground/__tests__/force-compact.test.ts +6 -4
- package/src/runtime/routes/playground/force-compact.ts +1 -1
- package/src/runtime/routes/playground/helpers.ts +1 -1
- package/src/runtime/routes/rename-conversation-routes.ts +5 -0
- package/src/runtime/routes/schedule-routes.ts +152 -42
- package/src/runtime/routes/secret-routes.ts +14 -2
- package/src/runtime/routes/skills-routes.ts +43 -14
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/tool-call-confirmation-enrichment.test.ts +161 -0
- package/src/runtime/routes/tool-call-confirmation-enrichment.ts +107 -0
- package/src/runtime/routes/trust-rules-routes.ts +26 -2
- package/src/runtime/routes/tts-routes.ts +35 -0
- package/src/runtime/routes/types.ts +66 -8
- package/src/runtime/routes/usage-routes.ts +47 -39
- package/src/runtime/routes/webhook-routes.ts +41 -2
- package/src/runtime/routes/work-items-routes.ts +2 -4
- package/src/runtime/routes/workspace-routes.ts +4 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +6 -0
- package/src/runtime/services/analyze-conversation.ts +2 -2
- package/src/runtime/services/conversation-serializer.ts +1 -1
- package/src/schedule/schedule-store.ts +20 -1
- package/src/schedule/schedule-usage-store.ts +83 -0
- package/src/schedule/scheduler.ts +12 -5
- package/src/signals/cancel.ts +2 -4
- package/src/skills/catalog-files.ts +2 -2
- package/src/skills/catalog-install.ts +3 -0
- package/src/skills/categories-cache.ts +118 -0
- package/src/skills/clawhub-files.ts +1 -2
- package/src/skills/skillssh-files.ts +1 -2
- package/src/subagent/manager.ts +17 -5
- package/src/telemetry/types.ts +29 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +112 -3
- package/src/telemetry/usage-telemetry-reporter.ts +57 -2
- package/src/tools/acp/context.ts +20 -0
- package/src/tools/acp/list-agents.test.ts +7 -1
- package/src/tools/acp/spawn.test.ts +158 -55
- package/src/tools/acp/spawn.ts +47 -72
- package/src/tools/acp/steer.test.ts +105 -8
- package/src/tools/acp/steer.ts +48 -17
- package/src/tools/apps/executors.ts +13 -8
- package/src/tools/executor.ts +1 -53
- package/src/tools/filesystem/write.ts +34 -0
- package/src/tools/network/__tests__/web-search-metadata.test.ts +7 -1
- package/src/tools/network/__tests__/web-search.test.ts +11 -3
- package/src/tools/network/web-search-error.test.ts +248 -0
- package/src/tools/network/web-search-error.ts +267 -0
- package/src/tools/network/web-search.ts +207 -48
- package/src/tools/schedule/create.ts +2 -0
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/terminal/safe-env.ts +10 -1
- package/src/tools/ui-surface/definitions.ts +34 -5
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +85 -1
- package/src/tts/provider-catalog.ts +76 -1
- package/src/util/mutex.ts +47 -0
- package/src/workspace/git-service.ts +1 -42
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
- package/src/workspace/migrations/095-bump-heartbeat-interval-30m-to-60m.ts +51 -0
- package/src/workspace/migrations/096-reduce-quality-profile-effort.ts +72 -0
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +117 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/docs/plugins.md +0 -836
- package/examples/plugins/echo/register.ts +0 -184
- package/src/__tests__/bootstrap-turn-cleanup.test.ts +0 -44
- 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__/empty-response-pipeline.test.ts +0 -423
- package/src/__tests__/llm-call-pipeline.test.ts +0 -287
- package/src/__tests__/memory-retrieval-pipeline.test.ts +0 -418
- package/src/__tests__/persistence-pipeline.test.ts +0 -503
- package/src/__tests__/pipeline-runner.test.ts +0 -564
- package/src/__tests__/title-generate-pipeline.test.ts +0 -211
- package/src/__tests__/token-estimate-pipeline.test.ts +0 -479
- package/src/__tests__/tool-error-pipeline.test.ts +0 -241
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -417
- package/src/__tests__/tool-result-truncate-pipeline.test.ts +0 -341
- package/src/daemon/bootstrap-turn-cleanup.ts +0 -45
- package/src/gallery/default-gallery.ts +0 -1359
- package/src/gallery/gallery-manifest.ts +0 -28
- package/src/home/feature-gate.ts +0 -22
- package/src/memory/v3/provider-blocks.ts +0 -16
- 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/terminal.ts +0 -73
- package/src/plugins/defaults/empty-response/middlewares/emptyResponse.ts +0 -22
- package/src/plugins/defaults/empty-response/terminal.ts +0 -106
- package/src/plugins/defaults/injectors/package.json +0 -15
- package/src/plugins/defaults/llm-call/middlewares/llmCall.ts +0 -17
- package/src/plugins/defaults/llm-call/register.ts +0 -45
- package/src/plugins/defaults/memory-retrieval/middlewares/memoryRetrieval.ts +0 -17
- package/src/plugins/defaults/memory-retrieval/package.json +0 -15
- package/src/plugins/defaults/memory-retrieval/register.ts +0 -181
- 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/persistence/middlewares/persistence.ts +0 -19
- package/src/plugins/defaults/persistence/package.json +0 -15
- package/src/plugins/defaults/persistence/register.ts +0 -38
- package/src/plugins/defaults/persistence/terminal.ts +0 -83
- package/src/plugins/defaults/title-generate/terminal.ts +0 -31
- package/src/plugins/defaults/token-estimate/middlewares/tokenEstimate.ts +0 -23
- package/src/plugins/defaults/token-estimate/package.json +0 -15
- package/src/plugins/defaults/token-estimate/register.ts +0 -34
- package/src/plugins/defaults/token-estimate/terminal.ts +0 -40
- package/src/plugins/defaults/tool-error/middlewares/toolError.ts +0 -21
- package/src/plugins/defaults/tool-error/terminal.ts +0 -47
- package/src/plugins/defaults/tool-execute/middlewares/toolExecute.ts +0 -23
- package/src/plugins/defaults/tool-execute/package.json +0 -15
- package/src/plugins/defaults/tool-execute/register.ts +0 -49
- package/src/plugins/defaults/tool-result-truncate/middlewares/toolResultTruncate.ts +0 -23
- package/src/plugins/defaults/tool-result-truncate/types.ts +0 -22
- package/src/skills/category-inference.ts +0 -111
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/capabilities.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/core.test.ts +0 -0
- /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__/needle.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/snapshot.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/tree.test.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +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}/core.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}/needle.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/snapshot.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/working-set.ts +0 -0
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { afterEach, describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
-
import { applyRuntimeInjections } from "../daemon/conversation-runtime-assembly.js";
|
|
4
|
-
import { defaultInjectorsPlugin } from "../plugins/defaults/injectors/register.js";
|
|
5
3
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} from "../
|
|
4
|
+
clearConversations,
|
|
5
|
+
setConversation,
|
|
6
|
+
} from "../daemon/conversation-registry.js";
|
|
7
|
+
import { applyRuntimeInjections } from "../daemon/conversation-runtime-assembly.js";
|
|
8
|
+
import type { SurfaceData, SurfaceType } from "../daemon/message-protocol.js";
|
|
9
|
+
import { createApp } from "../memory/app-store.js";
|
|
9
10
|
import type { Message } from "../providers/types.js";
|
|
10
11
|
|
|
11
12
|
// ---------------------------------------------------------------------------
|
|
@@ -16,6 +17,46 @@ function userMsg(text: string): Message {
|
|
|
16
17
|
return { role: "user", content: [{ type: "text", text }] };
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
// `applyRuntimeInjections` synthesizes this conversation id when no
|
|
21
|
+
// `turnContext` is supplied, so the `workspace-context` injector resolves the
|
|
22
|
+
// live workspace block from the registry under this key.
|
|
23
|
+
const FALLBACK_CONVERSATION_ID = "runtime-assembly-fallback";
|
|
24
|
+
|
|
25
|
+
// Register the fallback conversation in the live registry so the runtime
|
|
26
|
+
// injectors resolve their blocks from it (the orchestrator no longer threads
|
|
27
|
+
// workspace or active-surface content as options).
|
|
28
|
+
function registerFallbackConversation(fields: Record<string, unknown>): void {
|
|
29
|
+
setConversation(FALLBACK_CONVERSATION_ID, {
|
|
30
|
+
conversationId: FALLBACK_CONVERSATION_ID,
|
|
31
|
+
workingDir: "/sandbox",
|
|
32
|
+
// Non-dirty empty workspace by default so the workspace-context injector
|
|
33
|
+
// skips both the filesystem rescan and the DB refresh unless a test
|
|
34
|
+
// explicitly seeds a block via `workspaceTopLevelContext`.
|
|
35
|
+
workspaceTopLevelContext: "",
|
|
36
|
+
workspaceTopLevelDirty: false,
|
|
37
|
+
...fields,
|
|
38
|
+
} as never);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Seed the live conversation registry with a pre-rendered top-level block. The
|
|
42
|
+
// cache is non-dirty with non-null content, so `resolveWorkspaceTopLevelContext`
|
|
43
|
+
// returns it verbatim without rescanning the filesystem.
|
|
44
|
+
function seedWorkspaceContext(text: string): void {
|
|
45
|
+
registerFallbackConversation({
|
|
46
|
+
workspaceTopLevelContext: text,
|
|
47
|
+
workspaceTopLevelDirty: false,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Build the conversation surface-state map that `buildActiveSurfaceContext`
|
|
52
|
+
// reads to render the `<active_workspace>` block.
|
|
53
|
+
function makeSurfaceState(
|
|
54
|
+
surfaceId: string,
|
|
55
|
+
data: SurfaceData,
|
|
56
|
+
): Map<string, { surfaceType: SurfaceType; data: SurfaceData }> {
|
|
57
|
+
return new Map([[surfaceId, { surfaceType: "dynamic_page", data }]]);
|
|
58
|
+
}
|
|
59
|
+
|
|
19
60
|
// ---------------------------------------------------------------------------
|
|
20
61
|
// Tests
|
|
21
62
|
// ---------------------------------------------------------------------------
|
|
@@ -23,26 +64,21 @@ function userMsg(text: string): Message {
|
|
|
23
64
|
const sampleContext =
|
|
24
65
|
"<workspace>\nRoot: /sandbox\nDirectories: src, lib, tests\n</workspace>";
|
|
25
66
|
|
|
26
|
-
// The
|
|
27
|
-
//
|
|
28
|
-
//
|
|
29
|
-
//
|
|
30
|
-
//
|
|
67
|
+
// The workspace-context default injector emits the workspace block as a
|
|
68
|
+
// `prepend-user-tail` placement during `applyRuntimeInjections`. It sources
|
|
69
|
+
// the rendered block from the per-conversation workspace registry and
|
|
70
|
+
// (re)injects only when the block is absent from the working messages, so the
|
|
71
|
+
// suite seeds the registry and exercises that end-to-end path.
|
|
31
72
|
|
|
32
73
|
describe("applyRuntimeInjections — workspace top-level context", () => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
// default injector, so the plugin must be registered for the chain to
|
|
36
|
-
// produce a block. Each test gets a clean registry.
|
|
37
|
-
resetPluginRegistryForTests();
|
|
38
|
-
registerPlugin(defaultInjectorsPlugin);
|
|
74
|
+
afterEach(() => {
|
|
75
|
+
clearConversations();
|
|
39
76
|
});
|
|
40
77
|
|
|
41
|
-
test("injects workspace context when
|
|
78
|
+
test("injects workspace context when registered", async () => {
|
|
79
|
+
seedWorkspaceContext(sampleContext);
|
|
42
80
|
const messages: Message[] = [userMsg("Hello")];
|
|
43
|
-
const { messages: result } = await applyRuntimeInjections(messages, {
|
|
44
|
-
workspaceTopLevelContext: sampleContext,
|
|
45
|
-
});
|
|
81
|
+
const { messages: result } = await applyRuntimeInjections(messages, {});
|
|
46
82
|
|
|
47
83
|
expect(result).toHaveLength(1);
|
|
48
84
|
expect(result[0].content).toHaveLength(2);
|
|
@@ -50,22 +86,38 @@ describe("applyRuntimeInjections — workspace top-level context", () => {
|
|
|
50
86
|
expect((result[0].content[1] as { text: string }).text).toBe("Hello");
|
|
51
87
|
});
|
|
52
88
|
|
|
53
|
-
test("does not inject when workspace context is
|
|
89
|
+
test("does not inject when no workspace context is registered", async () => {
|
|
54
90
|
const messages: Message[] = [userMsg("Hello")];
|
|
55
|
-
const { messages: result } = await applyRuntimeInjections(messages, {
|
|
56
|
-
workspaceTopLevelContext: null,
|
|
57
|
-
});
|
|
91
|
+
const { messages: result } = await applyRuntimeInjections(messages, {});
|
|
58
92
|
|
|
59
93
|
expect(result).toHaveLength(1);
|
|
60
94
|
expect(result[0].content).toHaveLength(1);
|
|
61
95
|
});
|
|
62
96
|
|
|
97
|
+
test("does not re-inject when the workspace block is already present", async () => {
|
|
98
|
+
// GIVEN the registry holds a workspace block AND the working messages
|
|
99
|
+
// already carry that block (a normal cached turn, post-injection).
|
|
100
|
+
seedWorkspaceContext(sampleContext);
|
|
101
|
+
const messages: Message[] = [userMsg(sampleContext), userMsg("Hello")];
|
|
102
|
+
|
|
103
|
+
// WHEN injections are applied
|
|
104
|
+
const { messages: result } = await applyRuntimeInjections(messages, {});
|
|
105
|
+
|
|
106
|
+
// THEN presence detection skips the block to keep the prefix stable.
|
|
107
|
+
expect(result).toHaveLength(2);
|
|
108
|
+
expect(result[1].content).toHaveLength(1);
|
|
109
|
+
expect((result[1].content[0] as { text: string }).text).toBe("Hello");
|
|
110
|
+
});
|
|
111
|
+
|
|
63
112
|
test("workspace context appears before active surface context in content", async () => {
|
|
64
|
-
|
|
65
|
-
const { messages: result } = await applyRuntimeInjections(messages, {
|
|
66
|
-
activeSurface: { surfaceId: "sf_1", html: "<div>test</div>" },
|
|
113
|
+
registerFallbackConversation({
|
|
67
114
|
workspaceTopLevelContext: sampleContext,
|
|
115
|
+
workspaceTopLevelDirty: false,
|
|
116
|
+
currentActiveSurfaceId: "sf_1",
|
|
117
|
+
surfaceState: makeSurfaceState("sf_1", { html: "<div>test</div>" }),
|
|
68
118
|
});
|
|
119
|
+
const messages: Message[] = [userMsg("Hello")];
|
|
120
|
+
const { messages: result } = await applyRuntimeInjections(messages, {});
|
|
69
121
|
|
|
70
122
|
// Workspace is injected last (in applyRuntimeInjections order) so it
|
|
71
123
|
// prepends to whatever was already prepended by activeSurface.
|
|
@@ -79,17 +131,20 @@ describe("applyRuntimeInjections — workspace top-level context", () => {
|
|
|
79
131
|
});
|
|
80
132
|
|
|
81
133
|
test("app-backed active surface tells the model to load app-builder with the right argument", async () => {
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
134
|
+
const app = createApp({
|
|
135
|
+
name: "Example App",
|
|
136
|
+
schemaJson: "{}",
|
|
137
|
+
htmlDefinition: "<div>test</div>",
|
|
138
|
+
});
|
|
139
|
+
registerFallbackConversation({
|
|
140
|
+
currentActiveSurfaceId: "sf_1",
|
|
141
|
+
surfaceState: makeSurfaceState("sf_1", {
|
|
86
142
|
html: "<div>test</div>",
|
|
87
|
-
appId:
|
|
88
|
-
|
|
89
|
-
appFiles: [],
|
|
90
|
-
},
|
|
91
|
-
workspaceTopLevelContext: null,
|
|
143
|
+
appId: app.id,
|
|
144
|
+
}),
|
|
92
145
|
});
|
|
146
|
+
const messages: Message[] = [userMsg("Edit this app")];
|
|
147
|
+
const { messages: result } = await applyRuntimeInjections(messages, {});
|
|
93
148
|
|
|
94
149
|
const activeWorkspaceText = (result[0].content[0] as { text: string }).text;
|
|
95
150
|
expect(activeWorkspaceText).toContain('skill: "app-builder"');
|
|
@@ -98,15 +153,14 @@ describe("applyRuntimeInjections — workspace top-level context", () => {
|
|
|
98
153
|
});
|
|
99
154
|
|
|
100
155
|
describe("applyRuntimeInjections — minimal mode skips workspace blocks", () => {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
registerPlugin(defaultInjectorsPlugin);
|
|
156
|
+
afterEach(() => {
|
|
157
|
+
clearConversations();
|
|
104
158
|
});
|
|
105
159
|
|
|
106
160
|
test("minimal mode skips workspace top-level context", async () => {
|
|
161
|
+
seedWorkspaceContext(sampleContext);
|
|
107
162
|
const messages: Message[] = [userMsg("Hello")];
|
|
108
163
|
const { messages: result } = await applyRuntimeInjections(messages, {
|
|
109
|
-
workspaceTopLevelContext: sampleContext,
|
|
110
164
|
mode: "minimal",
|
|
111
165
|
});
|
|
112
166
|
|
|
@@ -116,9 +170,12 @@ describe("applyRuntimeInjections — minimal mode skips workspace blocks", () =>
|
|
|
116
170
|
});
|
|
117
171
|
|
|
118
172
|
test("minimal mode skips active surface context", async () => {
|
|
173
|
+
registerFallbackConversation({
|
|
174
|
+
currentActiveSurfaceId: "sf_1",
|
|
175
|
+
surfaceState: makeSurfaceState("sf_1", { html: "<div>test</div>" }),
|
|
176
|
+
});
|
|
119
177
|
const messages: Message[] = [userMsg("Hello")];
|
|
120
178
|
const { messages: result } = await applyRuntimeInjections(messages, {
|
|
121
|
-
activeSurface: { surfaceId: "sf_1", html: "<div>test</div>" },
|
|
122
179
|
mode: "minimal",
|
|
123
180
|
});
|
|
124
181
|
|
|
@@ -128,11 +185,14 @@ describe("applyRuntimeInjections — minimal mode skips workspace blocks", () =>
|
|
|
128
185
|
});
|
|
129
186
|
|
|
130
187
|
test("full mode (default) still includes workspace blocks", async () => {
|
|
131
|
-
|
|
132
|
-
const { messages: result } = await applyRuntimeInjections(messages, {
|
|
188
|
+
registerFallbackConversation({
|
|
133
189
|
workspaceTopLevelContext: sampleContext,
|
|
134
|
-
|
|
190
|
+
workspaceTopLevelDirty: false,
|
|
191
|
+
currentActiveSurfaceId: "sf_1",
|
|
192
|
+
surfaceState: makeSurfaceState("sf_1", { html: "<div>test</div>" }),
|
|
135
193
|
});
|
|
194
|
+
const messages: Message[] = [userMsg("Hello")];
|
|
195
|
+
const { messages: result } = await applyRuntimeInjections(messages, {});
|
|
136
196
|
|
|
137
197
|
expect(result[0].content).toHaveLength(3);
|
|
138
198
|
expect((result[0].content[0] as { text: string }).text).toBe(sampleContext);
|
|
@@ -140,53 +140,19 @@ describe("resolveSlash command contract", () => {
|
|
|
140
140
|
});
|
|
141
141
|
});
|
|
142
142
|
|
|
143
|
-
describe("resolveSlash /compact
|
|
144
|
-
test("plain /compact
|
|
143
|
+
describe("resolveSlash /compact", () => {
|
|
144
|
+
test("plain /compact resolves to kind=compact", async () => {
|
|
145
145
|
const result = await resolveSlash("/compact");
|
|
146
146
|
expect(result).toEqual({ kind: "compact" });
|
|
147
147
|
});
|
|
148
148
|
|
|
149
|
-
test("/compact
|
|
149
|
+
test("/compact rejects arguments with usage hint", async () => {
|
|
150
150
|
const result = await resolveSlash("/compact 30000");
|
|
151
|
-
expect(result).toEqual({
|
|
152
|
-
kind: "compact",
|
|
153
|
-
targetInputTokensOverride: 30000,
|
|
154
|
-
});
|
|
155
|
-
});
|
|
156
|
-
|
|
157
|
-
test("/compact <n>k expands to thousands", async () => {
|
|
158
|
-
const result = await resolveSlash("/compact 30k");
|
|
159
|
-
expect(result).toEqual({
|
|
160
|
-
kind: "compact",
|
|
161
|
-
targetInputTokensOverride: 30_000,
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
test("/compact <n>m expands to millions", async () => {
|
|
166
|
-
const result = await resolveSlash("/compact 1.5M");
|
|
167
|
-
expect(result).toEqual({
|
|
168
|
-
kind: "compact",
|
|
169
|
-
targetInputTokensOverride: 1_500_000,
|
|
170
|
-
});
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
test("/compact rejects malformed args with usage hint", async () => {
|
|
174
|
-
const result = await resolveSlash("/compact bogus");
|
|
175
151
|
expect(result.kind).toBe("unknown");
|
|
176
152
|
if (result.kind !== "unknown") throw new Error("expected unknown");
|
|
177
|
-
expect(result.message).toContain("
|
|
153
|
+
expect(result.message).toContain("does not take arguments");
|
|
178
154
|
expect(result.message).toContain("/compact");
|
|
179
155
|
});
|
|
180
|
-
|
|
181
|
-
test("/compact rejects zero", async () => {
|
|
182
|
-
const result = await resolveSlash("/compact 0");
|
|
183
|
-
expect(result.kind).toBe("unknown");
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
test("/compact rejects negative numbers", async () => {
|
|
187
|
-
const result = await resolveSlash("/compact -50");
|
|
188
|
-
expect(result.kind).toBe("unknown");
|
|
189
|
-
});
|
|
190
156
|
});
|
|
191
157
|
|
|
192
158
|
describe("resolveSlash /clean", () => {
|
|
@@ -227,9 +193,9 @@ describe("classifySlash is a pure classifier matching resolveSlash kinds", () =>
|
|
|
227
193
|
{ input: "/status", kind: "unknown" },
|
|
228
194
|
{ input: "/commands", kind: "unknown" },
|
|
229
195
|
{ input: "/compact", kind: "compact" },
|
|
230
|
-
{ input: "/compact 30000", kind: "
|
|
231
|
-
{ input: "/compact 30k", kind: "
|
|
232
|
-
{ input: "/compact 1.5M", kind: "
|
|
196
|
+
{ input: "/compact 30000", kind: "unknown" },
|
|
197
|
+
{ input: "/compact 30k", kind: "unknown" },
|
|
198
|
+
{ input: "/compact 1.5M", kind: "unknown" },
|
|
233
199
|
{ input: "/compact bogus", kind: "unknown" },
|
|
234
200
|
{ input: "/clean", kind: "clean" },
|
|
235
201
|
{ input: " /clean ", kind: "clean" },
|
|
@@ -239,7 +205,7 @@ describe("classifySlash is a pure classifier matching resolveSlash kinds", () =>
|
|
|
239
205
|
{ input: "/opus", kind: "unknown" },
|
|
240
206
|
{ input: "hello", kind: "passthrough" },
|
|
241
207
|
{ input: " /compact ", kind: "compact" },
|
|
242
|
-
{ input: " /compact 50k ", kind: "
|
|
208
|
+
{ input: " /compact 50k ", kind: "unknown" },
|
|
243
209
|
{ input: "/models foo", kind: "passthrough" },
|
|
244
210
|
];
|
|
245
211
|
|
|
@@ -228,7 +228,12 @@ mock.module("../agent/loop.js", () => ({
|
|
|
228
228
|
const history = await new Promise<Message[]>((resolve) => {
|
|
229
229
|
pendingRuns.push({ resolve, messages, onEvent });
|
|
230
230
|
});
|
|
231
|
-
return {
|
|
231
|
+
return {
|
|
232
|
+
history,
|
|
233
|
+
exitReason: null,
|
|
234
|
+
appendedNewMessages: history.length > messages.length,
|
|
235
|
+
newMessages: history.slice(messages.length),
|
|
236
|
+
};
|
|
232
237
|
}
|
|
233
238
|
},
|
|
234
239
|
}));
|
|
@@ -339,8 +339,8 @@ describe("GET /v1/conversation-starters", () => {
|
|
|
339
339
|
|
|
340
340
|
test("does not re-enqueue for invalid items when within cooldown period", async () => {
|
|
341
341
|
const now = Date.now();
|
|
342
|
-
//
|
|
343
|
-
const
|
|
342
|
+
// Last attempt 30 seconds ago (within the 5-minute cooldown)
|
|
343
|
+
const recentAttemptAt = now - 30_000;
|
|
344
344
|
insertStarter({
|
|
345
345
|
label: "Let me check calendar",
|
|
346
346
|
prompt: "Let me check what Alice has today.",
|
|
@@ -353,9 +353,13 @@ describe("GET /v1/conversation-starters", () => {
|
|
|
353
353
|
category: "productivity",
|
|
354
354
|
createdAt: now - 1,
|
|
355
355
|
});
|
|
356
|
+
setCheckpoint(
|
|
357
|
+
"conversation_starters:last_attempt_at:default",
|
|
358
|
+
String(recentAttemptAt),
|
|
359
|
+
);
|
|
356
360
|
setCheckpoint(
|
|
357
361
|
"conversation_starters:last_gen_at:default",
|
|
358
|
-
String(
|
|
362
|
+
String(recentAttemptAt),
|
|
359
363
|
);
|
|
360
364
|
setCheckpoint("conversation_starters:item_count_at_last_gen:default", "1");
|
|
361
365
|
insertMemoryItem();
|
|
@@ -375,8 +379,8 @@ describe("GET /v1/conversation-starters", () => {
|
|
|
375
379
|
|
|
376
380
|
test("re-enqueues for invalid items after cooldown expires", async () => {
|
|
377
381
|
const now = Date.now();
|
|
378
|
-
//
|
|
379
|
-
const
|
|
382
|
+
// Last attempt 6 minutes ago (past the 5-minute cooldown)
|
|
383
|
+
const oldAttemptAt = now - 6 * 60_000;
|
|
380
384
|
insertStarter({
|
|
381
385
|
label: "Let me check calendar",
|
|
382
386
|
prompt: "Let me check what Alice has today.",
|
|
@@ -389,9 +393,13 @@ describe("GET /v1/conversation-starters", () => {
|
|
|
389
393
|
category: "productivity",
|
|
390
394
|
createdAt: now - 1,
|
|
391
395
|
});
|
|
396
|
+
setCheckpoint(
|
|
397
|
+
"conversation_starters:last_attempt_at:default",
|
|
398
|
+
String(oldAttemptAt),
|
|
399
|
+
);
|
|
392
400
|
setCheckpoint(
|
|
393
401
|
"conversation_starters:last_gen_at:default",
|
|
394
|
-
String(
|
|
402
|
+
String(oldAttemptAt),
|
|
395
403
|
);
|
|
396
404
|
setCheckpoint("conversation_starters:item_count_at_last_gen:default", "1");
|
|
397
405
|
insertMemoryItem();
|
|
@@ -366,6 +366,90 @@ describe("surface action delivery to assistant", () => {
|
|
|
366
366
|
expect(JSON.stringify(completeMsg)).not.toContain(largeBase64);
|
|
367
367
|
});
|
|
368
368
|
|
|
369
|
+
test("choice surface broadcasts ui_surface_complete on action", async () => {
|
|
370
|
+
const sent: ServerMessage[] = [];
|
|
371
|
+
const ctx = makeContext(sent);
|
|
372
|
+
|
|
373
|
+
const showResult = await surfaceProxyResolver(ctx, "ui_show", {
|
|
374
|
+
surface_type: "choice",
|
|
375
|
+
title: "Pick an outcome",
|
|
376
|
+
data: {
|
|
377
|
+
options: [
|
|
378
|
+
{ id: "inbox", title: "Clean up my inbox" },
|
|
379
|
+
{ id: "calendar", title: "Plan my week" },
|
|
380
|
+
],
|
|
381
|
+
},
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
expect(showResult.isError).toBe(false);
|
|
385
|
+
expect(showResult.yieldToUser).toBe(true);
|
|
386
|
+
|
|
387
|
+
const showMessage = sent.find(
|
|
388
|
+
(msg): msg is UiSurfaceShow => msg.type === "ui_surface_show",
|
|
389
|
+
) as UiSurfaceShow;
|
|
390
|
+
const surfaceId = showMessage.surfaceId;
|
|
391
|
+
expect(ctx.pendingSurfaceActions.has(surfaceId)).toBe(true);
|
|
392
|
+
|
|
393
|
+
await handleSurfaceAction(ctx, surfaceId, "inbox", {
|
|
394
|
+
choiceId: "inbox",
|
|
395
|
+
choiceTitle: "Clean up my inbox",
|
|
396
|
+
selectedIds: ["inbox"],
|
|
397
|
+
selectedTitles: ["Clean up my inbox"],
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
const completeMsg = broadcastedMessages.find(
|
|
401
|
+
(m) =>
|
|
402
|
+
(m as unknown as Record<string, unknown>).type ===
|
|
403
|
+
"ui_surface_complete" &&
|
|
404
|
+
(m as unknown as Record<string, unknown>).surfaceId === surfaceId,
|
|
405
|
+
) as unknown as Record<string, unknown> | undefined;
|
|
406
|
+
expect(completeMsg).toBeDefined();
|
|
407
|
+
expect(completeMsg?.conversationId).toBe("conv-1");
|
|
408
|
+
expect(completeMsg?.summary).toBe('User chose: "Clean up my inbox"');
|
|
409
|
+
expect(ctx.pendingSurfaceActions.has(surfaceId)).toBe(false);
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
test("oauth_connect surface broadcasts ui_surface_complete on action", async () => {
|
|
413
|
+
const sent: ServerMessage[] = [];
|
|
414
|
+
const ctx = makeContext(sent);
|
|
415
|
+
|
|
416
|
+
const showResult = await surfaceProxyResolver(ctx, "ui_show", {
|
|
417
|
+
surface_type: "oauth_connect",
|
|
418
|
+
title: "Connect Google",
|
|
419
|
+
data: {
|
|
420
|
+
providerKey: "google",
|
|
421
|
+
displayName: "Google",
|
|
422
|
+
},
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
expect(showResult.isError).toBe(false);
|
|
426
|
+
expect(showResult.yieldToUser).toBe(true);
|
|
427
|
+
|
|
428
|
+
const showMessage = sent.find(
|
|
429
|
+
(msg): msg is UiSurfaceShow => msg.type === "ui_surface_show",
|
|
430
|
+
) as UiSurfaceShow;
|
|
431
|
+
const surfaceId = showMessage.surfaceId;
|
|
432
|
+
expect(ctx.pendingSurfaceActions.has(surfaceId)).toBe(true);
|
|
433
|
+
|
|
434
|
+
await handleSurfaceAction(ctx, surfaceId, "connect", {
|
|
435
|
+
status: "connected",
|
|
436
|
+
providerKey: "google",
|
|
437
|
+
providerLabel: "Google",
|
|
438
|
+
accountLabel: "user@example.com",
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
const completeMsg = broadcastedMessages.find(
|
|
442
|
+
(m) =>
|
|
443
|
+
(m as unknown as Record<string, unknown>).type ===
|
|
444
|
+
"ui_surface_complete" &&
|
|
445
|
+
(m as unknown as Record<string, unknown>).surfaceId === surfaceId,
|
|
446
|
+
) as unknown as Record<string, unknown> | undefined;
|
|
447
|
+
expect(completeMsg).toBeDefined();
|
|
448
|
+
expect(completeMsg?.conversationId).toBe("conv-1");
|
|
449
|
+
expect(completeMsg?.summary).toBe("Connected Google: user@example.com");
|
|
450
|
+
expect(ctx.pendingSurfaceActions.has(surfaceId)).toBe(false);
|
|
451
|
+
});
|
|
452
|
+
|
|
369
453
|
test("table surface does NOT broadcast ui_surface_complete (not one-shot)", async () => {
|
|
370
454
|
const sent: ServerMessage[] = [];
|
|
371
455
|
const ctx = makeContext(sent);
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { readFileSync } from "node:fs";
|
|
2
|
-
import { join } from "node:path";
|
|
3
1
|
import { afterAll, beforeEach, describe, expect, test } from "bun:test";
|
|
4
2
|
|
|
5
3
|
import {
|
|
@@ -18,6 +16,7 @@ import { assistantEventHub } from "../runtime/assistant-event-hub.js";
|
|
|
18
16
|
import { ROUTES as CONVERSATION_LIST_ROUTES } from "../runtime/routes/conversation-list-routes.js";
|
|
19
17
|
import { ROUTES as CONVERSATION_MANAGEMENT_ROUTES } from "../runtime/routes/conversation-management-routes.js";
|
|
20
18
|
import type { RouteDefinition } from "../runtime/routes/types.js";
|
|
19
|
+
import { publishConversationTitleChanged } from "../runtime/sync/resource-sync-events.js";
|
|
21
20
|
import { resetDbForTesting } from "./db-test-helpers.js";
|
|
22
21
|
import { waitFor } from "./helpers/wait-for.js";
|
|
23
22
|
|
|
@@ -122,20 +121,33 @@ describe("conversation sync tags", () => {
|
|
|
122
121
|
).toBe(false);
|
|
123
122
|
});
|
|
124
123
|
|
|
125
|
-
test("
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
124
|
+
test("auto-title generation emits the typed title event and a metadata-only sync tag (no list umbrella)", async () => {
|
|
125
|
+
// Auto-title generation (first-pass on prompt submit, second-pass
|
|
126
|
+
// regeneration, bootstrap, and voice) persists via the title service and
|
|
127
|
+
// broadcasts through `publishConversationTitleChanged` — the same helper
|
|
128
|
+
// the rename route uses. Like a rename, generation is content-only: the
|
|
129
|
+
// row stays in place and only the title flips, so web patches the cached
|
|
130
|
+
// row from the typed `conversation_title_updated` event and the per-
|
|
131
|
+
// conversation metadata tag is the belt-and-suspenders signal. The list
|
|
132
|
+
// umbrella is deliberately omitted so web never redrains the paginated
|
|
133
|
+
// list for a title change.
|
|
134
|
+
const conversation = createConversation("Generating…");
|
|
135
|
+
|
|
136
|
+
const received = await captureEvents(() => {
|
|
137
|
+
publishConversationTitleChanged(conversation.id, "Generated title");
|
|
138
|
+
}, 2);
|
|
139
|
+
|
|
140
|
+
expect(received.map((event) => event.message.type)).toEqual([
|
|
141
|
+
"conversation_title_updated",
|
|
142
|
+
"sync_changed",
|
|
143
|
+
]);
|
|
144
|
+
expect(received[1]!.message).toEqual({
|
|
145
|
+
type: "sync_changed",
|
|
146
|
+
tags: [conversationMetadataSyncTag(conversation.id)],
|
|
147
|
+
});
|
|
148
|
+
expect((received[1]!.message as { tags: string[] }).tags).not.toContain(
|
|
149
|
+
SYNC_TAGS.conversationsList,
|
|
129
150
|
);
|
|
130
|
-
const titleUpdateBlocks =
|
|
131
|
-
source.match(
|
|
132
|
-
/type: "conversation_title_updated"[\s\S]{0,500}?type: "sync_changed"[\s\S]{0,250}?tags: \[[\s\S]*?\]/g,
|
|
133
|
-
) ?? [];
|
|
134
|
-
|
|
135
|
-
expect(titleUpdateBlocks.length).toBeGreaterThanOrEqual(2);
|
|
136
|
-
for (const block of titleUpdateBlocks) {
|
|
137
|
-
expect(block).not.toContain("SYNC_TAGS.conversationsList");
|
|
138
|
-
}
|
|
139
151
|
});
|
|
140
152
|
|
|
141
153
|
test("create emits a sync_changed with the conversationsList umbrella tag", async () => {
|