@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
|
@@ -23,7 +23,7 @@ mock.module("../../assistant-event-hub.js", () => ({
|
|
|
23
23
|
broadcastMessage: () => {},
|
|
24
24
|
}));
|
|
25
25
|
|
|
26
|
-
import { findConversation } from "../../../daemon/conversation-
|
|
26
|
+
import { findConversation } from "../../../daemon/conversation-registry.js";
|
|
27
27
|
import { createConversation } from "../../../memory/conversation-crud.js";
|
|
28
28
|
import { getDb } from "../../../memory/db-connection.js";
|
|
29
29
|
import { initializeDb } from "../../../memory/db-init.js";
|
|
@@ -175,7 +175,10 @@ function seedRequestLog(
|
|
|
175
175
|
.run();
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
function seedConversationKey(
|
|
178
|
+
function seedConversationKey(
|
|
179
|
+
conversationKey: string,
|
|
180
|
+
conversationId: string,
|
|
181
|
+
): void {
|
|
179
182
|
getDb()
|
|
180
183
|
.insert(conversationKeys)
|
|
181
184
|
.values({
|
|
@@ -776,6 +779,33 @@ describe("PUT /v1/config/llm/profiles/:name", () => {
|
|
|
776
779
|
});
|
|
777
780
|
});
|
|
778
781
|
|
|
782
|
+
test("saves a profile using the minimax provider (regression #32404)", async () => {
|
|
783
|
+
// minimax is exposed as a first-class provider in the catalog, so saving
|
|
784
|
+
// a profile bound to it must pass ProfileEntry validation rather than 400.
|
|
785
|
+
const result = await replaceProfileRoute.handler({
|
|
786
|
+
pathParams: { name: "custom" },
|
|
787
|
+
body: {
|
|
788
|
+
provider: "minimax",
|
|
789
|
+
model: "MiniMax-M2.7",
|
|
790
|
+
},
|
|
791
|
+
});
|
|
792
|
+
|
|
793
|
+
expect(result).toEqual({ ok: true });
|
|
794
|
+
const savedProfile = (
|
|
795
|
+
savedRawConfig?.llm as {
|
|
796
|
+
profiles: Record<string, Record<string, unknown>>;
|
|
797
|
+
}
|
|
798
|
+
).profiles.custom;
|
|
799
|
+
|
|
800
|
+
expect(savedProfile.provider).toBe("minimax");
|
|
801
|
+
expect(savedProfile.model).toBe("MiniMax-M2.7");
|
|
802
|
+
expect(savedProfile.provider_connection).toBe("minimax-personal");
|
|
803
|
+
|
|
804
|
+
const conn = getConnection(getDb(), "minimax-personal");
|
|
805
|
+
expect(conn).not.toBeNull();
|
|
806
|
+
expect(conn!.provider).toBe("minimax");
|
|
807
|
+
});
|
|
808
|
+
|
|
779
809
|
describe("managed profile guard", () => {
|
|
780
810
|
beforeEach(() => {
|
|
781
811
|
// Seed a managed profile alongside the existing custom one.
|
|
@@ -155,7 +155,11 @@ describe("memory_v2_ema_scores route registration", () => {
|
|
|
155
155
|
expect(route!.tags).toContain("memory");
|
|
156
156
|
// Schema rejects unknown keys so accidental request-body fields fail
|
|
157
157
|
// loudly during route adapter parsing rather than silently propagating.
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
const requestBody = route!.requestBody;
|
|
159
|
+
if (!requestBody || !("parse" in requestBody)) {
|
|
160
|
+
throw new Error("expected a JSON (Zod) request-body schema");
|
|
161
|
+
}
|
|
162
|
+
expect(() => requestBody.parse({ extra: true })).toThrow();
|
|
163
|
+
expect(() => requestBody.parse({})).not.toThrow();
|
|
160
164
|
});
|
|
161
165
|
});
|
|
@@ -42,7 +42,7 @@ const findBySurfaceCalls: string[] = [];
|
|
|
42
42
|
const getOrCreateCalls: string[] = [];
|
|
43
43
|
const rawGetCalls: Array<{ sql: string; params: unknown[] }> = [];
|
|
44
44
|
|
|
45
|
-
mock.module("../../../daemon/conversation-
|
|
45
|
+
mock.module("../../../daemon/conversation-registry.js", () => ({
|
|
46
46
|
findConversation: (id: string) => {
|
|
47
47
|
findConvCalls.push(id);
|
|
48
48
|
return memoryById ?? undefined;
|
|
@@ -51,6 +51,9 @@ mock.module("../../../daemon/conversation-store.js", () => ({
|
|
|
51
51
|
findBySurfaceCalls.push(surfaceId);
|
|
52
52
|
return memoryBySurface ?? undefined;
|
|
53
53
|
},
|
|
54
|
+
}));
|
|
55
|
+
|
|
56
|
+
mock.module("../../../daemon/conversation-store.js", () => ({
|
|
54
57
|
getOrCreateConversation: async (id: string) => {
|
|
55
58
|
getOrCreateCalls.push(id);
|
|
56
59
|
if (!rehydrated) {
|
|
@@ -183,9 +186,7 @@ describe("triggerSurfaceAction handler", () => {
|
|
|
183
186
|
// SQL must filter the messages table by ui_surface payload pattern.
|
|
184
187
|
expect(rawGetCalls[0]!.sql).toContain("FROM messages");
|
|
185
188
|
expect(rawGetCalls[0]!.sql).toContain("LIKE");
|
|
186
|
-
expect(rawGetCalls[0]!.params).toEqual([
|
|
187
|
-
`%"surfaceId":"surf-evicted"%`,
|
|
188
|
-
]);
|
|
189
|
+
expect(rawGetCalls[0]!.params).toEqual([`%"surfaceId":"surf-evicted"%`]);
|
|
189
190
|
expect(getOrCreateCalls).toEqual(["conv-from-db"]);
|
|
190
191
|
expect(rehydrated.surfaceActionCalls).toEqual([
|
|
191
192
|
{ surfaceId: "surf-evicted", actionId: "act-3", data: undefined },
|
|
@@ -51,7 +51,7 @@ const findBySurfaceCalls: string[] = [];
|
|
|
51
51
|
const getOrCreateCalls: string[] = [];
|
|
52
52
|
const rawGetCalls: Array<{ sql: string; params: unknown[] }> = [];
|
|
53
53
|
|
|
54
|
-
mock.module("../../../daemon/conversation-
|
|
54
|
+
mock.module("../../../daemon/conversation-registry.js", () => ({
|
|
55
55
|
findConversation: (id: string) => {
|
|
56
56
|
findConvCalls.push(id);
|
|
57
57
|
return memoryById ?? undefined;
|
|
@@ -60,6 +60,9 @@ mock.module("../../../daemon/conversation-store.js", () => ({
|
|
|
60
60
|
findBySurfaceCalls.push(surfaceId);
|
|
61
61
|
return undefined;
|
|
62
62
|
},
|
|
63
|
+
}));
|
|
64
|
+
|
|
65
|
+
mock.module("../../../daemon/conversation-store.js", () => ({
|
|
63
66
|
getOrCreateConversation: async (id: string) => {
|
|
64
67
|
getOrCreateCalls.push(id);
|
|
65
68
|
if (!rehydrated) {
|
|
@@ -164,8 +164,12 @@ afterEach(() => {
|
|
|
164
164
|
// ---------------------------------------------------------------------------
|
|
165
165
|
|
|
166
166
|
describe("tts-routes", () => {
|
|
167
|
-
test("exports route definitions for messages/:messageId/tts, tts/synthesize, and tts/synthesize-cli", () => {
|
|
168
|
-
expect(ROUTES).toHaveLength(
|
|
167
|
+
test("exports route definitions for tts/providers, messages/:messageId/tts, tts/synthesize, and tts/synthesize-cli", () => {
|
|
168
|
+
expect(ROUTES).toHaveLength(4);
|
|
169
|
+
|
|
170
|
+
const providers = getRoute("tts/providers");
|
|
171
|
+
expect(providers.method).toBe("GET");
|
|
172
|
+
expect(providers.policy?.requiredScopes).toContain("settings.read");
|
|
169
173
|
|
|
170
174
|
const msgTts = getRoute("messages/:messageId/tts");
|
|
171
175
|
expect(msgTts.method).toBe("POST");
|
|
@@ -2,25 +2,28 @@
|
|
|
2
2
|
* Tests for the ACP route handlers.
|
|
3
3
|
*
|
|
4
4
|
* Suites:
|
|
5
|
-
* - POST /v1/acp/spawn
|
|
6
|
-
*
|
|
5
|
+
* - POST /v1/acp/spawn: resolver failure paths (acp_disabled,
|
|
6
|
+
* unknown_agent) and the body-shape guard. The binary_not_found /
|
|
7
|
+
* auto-install surface is covered in `__tests__/acp-routes.test.ts`.
|
|
7
8
|
* - POST /v1/acp/spawn (env injection) — CLAUDE_CODE_OAUTH_TOKEN is read
|
|
8
9
|
* from the credential broker (policy-gated + audited) and merged into
|
|
9
10
|
* `agentConfig.env` ONLY for the `claude` agent.
|
|
10
11
|
* - DELETE /v1/acp/sessions?status=completed — the bulk-clear route that
|
|
11
12
|
* wipes terminal-state rows (completed/failed/cancelled) from
|
|
12
|
-
* `acp_session_history` while leaving running/initializing rows
|
|
13
|
+
* `acp_session_history` while leaving running/initializing rows and
|
|
14
|
+
* rows with an in-flight resume intact.
|
|
13
15
|
* - DELETE /v1/acp/sessions/:id — single-row delete: completed → 200,
|
|
14
|
-
* running → 409, unknown id → idempotent
|
|
16
|
+
* running or mid-resume → 409, unknown id → idempotent
|
|
17
|
+
* { deleted: false }.
|
|
15
18
|
*
|
|
16
19
|
* The spawn tests mirror the resolver's test setup using the shared
|
|
17
20
|
* `installAcpConfigStub` and `installWhichStub` helpers so the host
|
|
18
21
|
* environment doesn't influence the resolver's PATH preflight.
|
|
19
22
|
*
|
|
20
|
-
* The
|
|
21
|
-
*
|
|
22
|
-
* and use the real DB (initialized via the test preload's
|
|
23
|
-
* workspace) to verify the row is actually removed.
|
|
23
|
+
* The delete tests stub `getAcpSessionManager` so we can drive the
|
|
24
|
+
* in-memory-status and pending-resume checks without spawning real ACP
|
|
25
|
+
* child processes, and use the real DB (initialized via the test preload's
|
|
26
|
+
* per-file workspace) to verify the row is actually removed.
|
|
24
27
|
*/
|
|
25
28
|
|
|
26
29
|
import {
|
|
@@ -48,8 +51,10 @@ afterAll(() => {
|
|
|
48
51
|
// in-memory-status check without spawning real ACP processes, and so the
|
|
49
52
|
// env-injection spawn tests can capture the `agentConfig` arg without
|
|
50
53
|
// launching a real subprocess. Stored in mutable state so individual tests
|
|
51
|
-
// can plant arbitrary states / inspect capture.
|
|
54
|
+
// can plant arbitrary states / inspect capture. `pendingResumeIds` models
|
|
55
|
+
// ids reserved by an in-flight resumeFromHistory (no SessionEntry yet).
|
|
52
56
|
const inMemoryStates = new Map<string, AcpSessionState>();
|
|
57
|
+
const pendingResumeIds = new Set<string>();
|
|
53
58
|
|
|
54
59
|
interface CapturedSpawn {
|
|
55
60
|
agent: string;
|
|
@@ -70,6 +75,9 @@ mock.module("../../acp/index.js", () => ({
|
|
|
70
75
|
if (!state) throw new Error(`ACP session "${id}" not found`);
|
|
71
76
|
return state;
|
|
72
77
|
},
|
|
78
|
+
getActiveAndPendingIds: () => [
|
|
79
|
+
...new Set([...inMemoryStates.keys(), ...pendingResumeIds]),
|
|
80
|
+
],
|
|
73
81
|
spawn: async (
|
|
74
82
|
agent: string,
|
|
75
83
|
agentConfig: { env?: Record<string, string> },
|
|
@@ -226,22 +234,6 @@ describe("POST /v1/acp/spawn", () => {
|
|
|
226
234
|
).rejects.toThrow('Unknown agent "nonexistent"');
|
|
227
235
|
});
|
|
228
236
|
|
|
229
|
-
test("throws FailedDependencyError when the agent binary is missing", async () => {
|
|
230
|
-
config.setConfig({ agents: {} });
|
|
231
|
-
which.setWhich({});
|
|
232
|
-
|
|
233
|
-
const handler = getSpawnHandler();
|
|
234
|
-
await expect(
|
|
235
|
-
handler({
|
|
236
|
-
body: {
|
|
237
|
-
agent: "codex",
|
|
238
|
-
task: "do a thing",
|
|
239
|
-
conversationId: "conv-1",
|
|
240
|
-
},
|
|
241
|
-
}),
|
|
242
|
-
).rejects.toThrow("codex-acp is not on PATH");
|
|
243
|
-
});
|
|
244
|
-
|
|
245
237
|
test("body-shape guard short-circuits before the resolver runs", async () => {
|
|
246
238
|
config.setConfig({ enabled: false });
|
|
247
239
|
|
|
@@ -457,6 +449,8 @@ describe("DELETE /v1/acp/sessions?status=completed", () => {
|
|
|
457
449
|
});
|
|
458
450
|
|
|
459
451
|
beforeEach(() => {
|
|
452
|
+
inMemoryStates.clear();
|
|
453
|
+
pendingResumeIds.clear();
|
|
460
454
|
getSqlite().run("DELETE FROM acp_session_history");
|
|
461
455
|
});
|
|
462
456
|
|
|
@@ -482,6 +476,57 @@ describe("DELETE /v1/acp/sessions?status=completed", () => {
|
|
|
482
476
|
]);
|
|
483
477
|
});
|
|
484
478
|
|
|
479
|
+
test("excludes terminal rows whose session is active in memory (resumed sessions)", async () => {
|
|
480
|
+
// A resumed session reuses its original id: the in-memory state is
|
|
481
|
+
// `running` while its history row still carries the old terminal
|
|
482
|
+
// status until the next terminal upsert. The bulk delete must not
|
|
483
|
+
// remove that row out from under the live session.
|
|
484
|
+
seedHistoryRow("row-resumed", "completed", 1000);
|
|
485
|
+
seedHistoryRow("row-completed", "completed", 2000);
|
|
486
|
+
inMemoryStates.set("row-resumed", {
|
|
487
|
+
id: "row-resumed",
|
|
488
|
+
agentId: "claude",
|
|
489
|
+
acpSessionId: "proto-resumed",
|
|
490
|
+
parentConversationId: "conv-test",
|
|
491
|
+
status: "running",
|
|
492
|
+
startedAt: 1000,
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
const handler = getBulkDeleteHandler();
|
|
496
|
+
const result = (await handler({
|
|
497
|
+
queryParams: { status: "completed" },
|
|
498
|
+
})) as {
|
|
499
|
+
deleted: number;
|
|
500
|
+
};
|
|
501
|
+
expect(result.deleted).toBe(1);
|
|
502
|
+
|
|
503
|
+
const remaining = listRows();
|
|
504
|
+
expect(remaining).toEqual([{ id: "row-resumed", status: "completed" }]);
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
test("excludes terminal rows whose session has a resume in flight", async () => {
|
|
508
|
+
// A resume that is still awaiting env preparation has no in-memory
|
|
509
|
+
// SessionEntry yet, but its history row (still terminal) must survive:
|
|
510
|
+
// deleting it mid-resume would let the later terminal upsert resurrect
|
|
511
|
+
// it as an orphan row.
|
|
512
|
+
seedHistoryRow("row-pending-resume", "completed", 1000);
|
|
513
|
+
seedHistoryRow("row-completed", "completed", 2000);
|
|
514
|
+
pendingResumeIds.add("row-pending-resume");
|
|
515
|
+
|
|
516
|
+
const handler = getBulkDeleteHandler();
|
|
517
|
+
const result = (await handler({
|
|
518
|
+
queryParams: { status: "completed" },
|
|
519
|
+
})) as {
|
|
520
|
+
deleted: number;
|
|
521
|
+
};
|
|
522
|
+
expect(result.deleted).toBe(1);
|
|
523
|
+
|
|
524
|
+
const remaining = listRows();
|
|
525
|
+
expect(remaining).toEqual([
|
|
526
|
+
{ id: "row-pending-resume", status: "completed" },
|
|
527
|
+
]);
|
|
528
|
+
});
|
|
529
|
+
|
|
485
530
|
test("returns deleted=0 when no terminal rows are present", async () => {
|
|
486
531
|
seedHistoryRow("row-running", "running", 1000);
|
|
487
532
|
|
|
@@ -559,6 +604,7 @@ describe("DELETE /v1/acp/sessions/:id", () => {
|
|
|
559
604
|
|
|
560
605
|
beforeEach(() => {
|
|
561
606
|
inMemoryStates.clear();
|
|
607
|
+
pendingResumeIds.clear();
|
|
562
608
|
getDb().delete(acpSessionHistory).run();
|
|
563
609
|
});
|
|
564
610
|
|
|
@@ -610,6 +656,24 @@ describe("DELETE /v1/acp/sessions/:id", () => {
|
|
|
610
656
|
},
|
|
611
657
|
);
|
|
612
658
|
|
|
659
|
+
test("returns 409 when the session has a resume in flight (not yet in memory)", async () => {
|
|
660
|
+
pendingResumeIds.add("sess-resuming");
|
|
661
|
+
insertHistoryRow({ id: "sess-resuming", status: "completed" });
|
|
662
|
+
|
|
663
|
+
const handler = getDeleteSessionHandler();
|
|
664
|
+
expect(() => handler({ pathParams: { id: "sess-resuming" } })).toThrow(
|
|
665
|
+
"resume in flight",
|
|
666
|
+
);
|
|
667
|
+
|
|
668
|
+
// Row untouched.
|
|
669
|
+
const remaining = getDb()
|
|
670
|
+
.select()
|
|
671
|
+
.from(acpSessionHistory)
|
|
672
|
+
.where(eq(acpSessionHistory.id, "sess-resuming"))
|
|
673
|
+
.all();
|
|
674
|
+
expect(remaining).toHaveLength(1);
|
|
675
|
+
});
|
|
676
|
+
|
|
613
677
|
test("idempotent for unknown id — returns { deleted: false }", async () => {
|
|
614
678
|
const handler = getDeleteSessionHandler();
|
|
615
679
|
const result = (await handler({
|
|
@@ -4,12 +4,14 @@
|
|
|
4
4
|
* Exposes spawn, steer, cancel, close, sessions, and permission operations
|
|
5
5
|
* over HTTP and IPC.
|
|
6
6
|
*/
|
|
7
|
-
import { desc, eq, inArray } from "drizzle-orm";
|
|
7
|
+
import { and, desc, eq, inArray, notInArray } from "drizzle-orm";
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
|
|
10
|
+
import { resolveAgentWithAutoInstall } from "../../acp/auto-install.js";
|
|
10
11
|
import { getAcpSessionManager } from "../../acp/index.js";
|
|
11
12
|
import { prepareAgentEnv } from "../../acp/prepare-agent-env.js";
|
|
12
|
-
import {
|
|
13
|
+
import { formatResolveFailure } from "../../acp/resolve-agent.js";
|
|
14
|
+
import { AcpResumeError } from "../../acp/session-manager.js";
|
|
13
15
|
import type { AcpSessionState } from "../../acp/types.js";
|
|
14
16
|
import { getDb } from "../../memory/db-connection.js";
|
|
15
17
|
import { rawChanges } from "../../memory/raw-query.js";
|
|
@@ -61,26 +63,19 @@ async function spawnSession({ body }: RouteHandlerArgs) {
|
|
|
61
63
|
throw new BadRequestError("agent, task, and conversationId are required");
|
|
62
64
|
}
|
|
63
65
|
|
|
64
|
-
|
|
66
|
+
// Resolve the agent, silently auto-installing a missing allowlisted
|
|
67
|
+
// adapter binary (see acp/auto-install.ts). Shared with the skill-tool
|
|
68
|
+
// path in tools/acp/spawn.ts; only the transport mapping differs.
|
|
69
|
+
const { resolved, failureMessage } = await resolveAgentWithAutoInstall(agent);
|
|
70
|
+
if (failureMessage) {
|
|
71
|
+
throw new FailedDependencyError(failureMessage);
|
|
72
|
+
}
|
|
65
73
|
if (!resolved.ok) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
case "unknown_agent":
|
|
70
|
-
throw new BadRequestError(
|
|
71
|
-
`Unknown agent "${agent}". Available: ${resolved.available.join(", ")}.`,
|
|
72
|
-
);
|
|
73
|
-
case "binary_not_found":
|
|
74
|
-
throw new FailedDependencyError(
|
|
75
|
-
`${resolved.command} is not on PATH. ${resolved.hint}`,
|
|
76
|
-
);
|
|
77
|
-
default: {
|
|
78
|
-
const _exhaustive: never = resolved;
|
|
79
|
-
throw new Error(
|
|
80
|
-
`Unexpected acp resolver reason: ${(_exhaustive as { reason: string }).reason}`,
|
|
81
|
-
);
|
|
82
|
-
}
|
|
74
|
+
const message = formatResolveFailure(agent, resolved);
|
|
75
|
+
if (resolved.reason === "binary_not_found") {
|
|
76
|
+
throw new FailedDependencyError(message);
|
|
83
77
|
}
|
|
78
|
+
throw new BadRequestError(message);
|
|
84
79
|
}
|
|
85
80
|
|
|
86
81
|
// Inject required env vars and preflight via the shared helper. See
|
|
@@ -116,13 +111,31 @@ async function steerSession({ pathParams, body }: RouteHandlerArgs) {
|
|
|
116
111
|
throw new BadRequestError("instruction is required");
|
|
117
112
|
}
|
|
118
113
|
|
|
114
|
+
// Sessions no longer in memory (completed, or lost to a daemon restart)
|
|
115
|
+
// are transparently resumed from persisted history with the instruction
|
|
116
|
+
// fired in the same call, mirroring the acp_steer skill tool.
|
|
117
|
+
// broadcastMessage plays the sender role spawnSession gives it, so
|
|
118
|
+
// connected clients render the session.
|
|
119
119
|
const manager = getAcpSessionManager();
|
|
120
120
|
try {
|
|
121
|
-
await manager.
|
|
122
|
-
|
|
121
|
+
const { resumed } = await manager.steerOrResume(
|
|
122
|
+
id,
|
|
123
|
+
instruction,
|
|
124
|
+
broadcastMessage,
|
|
125
|
+
);
|
|
126
|
+
return resumed
|
|
127
|
+
? { acpSessionId: id, steered: true, resumed: true }
|
|
128
|
+
: { acpSessionId: id, steered: true };
|
|
129
|
+
} catch (err) {
|
|
130
|
+
// Resume errors carry the actionable hint (legacy row without cwd,
|
|
131
|
+
// agent capability missing, resolver failures, ...).
|
|
132
|
+
if (err instanceof AcpResumeError) {
|
|
133
|
+
throw new FailedDependencyError(err.message);
|
|
134
|
+
}
|
|
135
|
+
// Unknown ids (no in-memory session, no history row) and plain steer
|
|
136
|
+
// failures both map to 404, as before.
|
|
123
137
|
throw new NotFoundError("ACP session not found");
|
|
124
138
|
}
|
|
125
|
-
return { acpSessionId: id, steered: true };
|
|
126
139
|
}
|
|
127
140
|
|
|
128
141
|
async function cancelSession({ pathParams }: RouteHandlerArgs) {
|
|
@@ -161,9 +174,25 @@ function bulkDeleteSessions({ queryParams }: RouteHandlerArgs) {
|
|
|
161
174
|
"status query param is required and must be 'completed'",
|
|
162
175
|
);
|
|
163
176
|
}
|
|
177
|
+
// Exclude sessions currently active in memory AND ids with a resume in
|
|
178
|
+
// flight (reserved but not yet registered): a resumed session reuses its
|
|
179
|
+
// original id, and its history row keeps the old terminal status until
|
|
180
|
+
// the next terminal upsert - a status-only delete would remove the row
|
|
181
|
+
// out from under the live (or resuming) session, and the later terminal
|
|
182
|
+
// upsert would resurrect it. Mirrors the 409 guard on the single-id
|
|
183
|
+
// delete route.
|
|
184
|
+
const activeIds = getAcpSessionManager().getActiveAndPendingIds();
|
|
185
|
+
const terminalFilter = inArray(
|
|
186
|
+
acpSessionHistory.status,
|
|
187
|
+
TERMINAL_SESSION_STATUSES,
|
|
188
|
+
);
|
|
164
189
|
getDb()
|
|
165
190
|
.delete(acpSessionHistory)
|
|
166
|
-
.where(
|
|
191
|
+
.where(
|
|
192
|
+
activeIds.length > 0
|
|
193
|
+
? and(terminalFilter, notInArray(acpSessionHistory.id, activeIds))
|
|
194
|
+
: terminalFilter,
|
|
195
|
+
)
|
|
167
196
|
.run();
|
|
168
197
|
const deleted = rawChanges();
|
|
169
198
|
log.info({ deleted }, "Bulk-cleared terminal ACP session history");
|
|
@@ -172,9 +201,10 @@ function bulkDeleteSessions({ queryParams }: RouteHandlerArgs) {
|
|
|
172
201
|
|
|
173
202
|
function deleteSession({ pathParams }: RouteHandlerArgs) {
|
|
174
203
|
const id = pathParams?.id as string;
|
|
204
|
+
const manager = getAcpSessionManager();
|
|
175
205
|
|
|
176
206
|
try {
|
|
177
|
-
const state =
|
|
207
|
+
const state = manager.getStatus(id);
|
|
178
208
|
if (
|
|
179
209
|
!Array.isArray(state) &&
|
|
180
210
|
(state.status === "running" || state.status === "initializing")
|
|
@@ -185,7 +215,16 @@ function deleteSession({ pathParams }: RouteHandlerArgs) {
|
|
|
185
215
|
}
|
|
186
216
|
} catch (err) {
|
|
187
217
|
if (err instanceof ConflictError) throw err;
|
|
188
|
-
// Not in memory
|
|
218
|
+
// Not registered in memory, but a resume may be in flight (id reserved
|
|
219
|
+
// while awaiting env preparation). Its history row must survive until
|
|
220
|
+
// the resume lands - the later terminal upsert would resurrect a
|
|
221
|
+
// deleted row.
|
|
222
|
+
if (manager.getActiveAndPendingIds().includes(id)) {
|
|
223
|
+
throw new ConflictError(
|
|
224
|
+
`ACP session "${id}" has a resume in flight. Wait for it to finish before deleting.`,
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
// Otherwise fall through to the (idempotent) DB delete.
|
|
189
228
|
}
|
|
190
229
|
|
|
191
230
|
getDb().delete(acpSessionHistory).where(eq(acpSessionHistory.id, id)).run();
|
|
@@ -233,7 +272,11 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
233
272
|
},
|
|
234
273
|
handler: steerSession,
|
|
235
274
|
summary: "Steer ACP session",
|
|
236
|
-
description:
|
|
275
|
+
description:
|
|
276
|
+
"Send a steering instruction to an ACP session. Sessions no longer " +
|
|
277
|
+
"in memory (completed, or lost to a daemon restart) are " +
|
|
278
|
+
"transparently resumed from persisted history first, when the agent " +
|
|
279
|
+
"supports ACP session loading.",
|
|
237
280
|
tags: ["acp"],
|
|
238
281
|
requestBody: z.object({
|
|
239
282
|
instruction: z.string(),
|
|
@@ -241,6 +284,12 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
241
284
|
responseBody: z.object({
|
|
242
285
|
acpSessionId: z.string(),
|
|
243
286
|
steered: z.boolean(),
|
|
287
|
+
resumed: z
|
|
288
|
+
.boolean()
|
|
289
|
+
.optional()
|
|
290
|
+
.describe(
|
|
291
|
+
"True when the session was resumed from persisted history before steering.",
|
|
292
|
+
),
|
|
244
293
|
}),
|
|
245
294
|
},
|
|
246
295
|
{
|
|
@@ -324,7 +373,9 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
324
373
|
summary: "Bulk-clear terminal ACP sessions",
|
|
325
374
|
description:
|
|
326
375
|
"Remove every terminal-state row (completed/failed/cancelled) from " +
|
|
327
|
-
"the persisted acp_session_history table."
|
|
376
|
+
"the persisted acp_session_history table. Rows whose session is " +
|
|
377
|
+
"currently active in memory (e.g. resumed) or has a resume in " +
|
|
378
|
+
"flight are excluded.",
|
|
328
379
|
tags: ["acp"],
|
|
329
380
|
queryParams: [
|
|
330
381
|
{
|
|
@@ -350,7 +401,8 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
350
401
|
summary: "Delete ACP session from history",
|
|
351
402
|
description:
|
|
352
403
|
"Remove a persisted ACP session row. Rejects with 409 when the " +
|
|
353
|
-
"session is still active in memory
|
|
404
|
+
"session is still active in memory or has a resume in flight; " +
|
|
405
|
+
"idempotent for unknown ids.",
|
|
354
406
|
tags: ["acp"],
|
|
355
407
|
responseBody: z.object({
|
|
356
408
|
deleted: z.boolean(),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Route handlers for app CRUD, bundling, sharing, versioning,
|
|
3
|
-
*
|
|
3
|
+
* and signing operations.
|
|
4
4
|
*/
|
|
5
5
|
import { randomBytes } from "node:crypto";
|
|
6
6
|
import {
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from "node:fs";
|
|
13
13
|
import { stat, unlink } from "node:fs/promises";
|
|
14
14
|
import { homedir, tmpdir } from "node:os";
|
|
15
|
-
import {
|
|
15
|
+
import { join } from "node:path";
|
|
16
16
|
|
|
17
17
|
import { z } from "zod";
|
|
18
18
|
|
|
@@ -22,7 +22,6 @@ import { scanBundle } from "../../bundler/bundle-scanner.js";
|
|
|
22
22
|
import type { SignatureJson } from "../../bundler/bundle-signer.js";
|
|
23
23
|
import { verifyBundleSignature } from "../../bundler/signature-verifier.js";
|
|
24
24
|
import { compareSemver } from "../../daemon/handlers/shared.js";
|
|
25
|
-
import { defaultGallery } from "../../gallery/default-gallery.js";
|
|
26
25
|
import {
|
|
27
26
|
getAppDiff,
|
|
28
27
|
getAppHistory,
|
|
@@ -272,47 +271,6 @@ function forkSharedApp(
|
|
|
272
271
|
return { success: true, appId: newApp.id, name: newApp.name };
|
|
273
272
|
}
|
|
274
273
|
|
|
275
|
-
async function installGalleryApp(
|
|
276
|
-
galleryAppId: string,
|
|
277
|
-
): Promise<
|
|
278
|
-
| { success: true; appId: string; name: string }
|
|
279
|
-
| { success: false; error: string }
|
|
280
|
-
> {
|
|
281
|
-
const galleryApp = defaultGallery.apps.find((a) => a.id === galleryAppId);
|
|
282
|
-
if (!galleryApp) {
|
|
283
|
-
return {
|
|
284
|
-
success: false,
|
|
285
|
-
error: `Gallery app not found: ${galleryAppId}`,
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
const app = createApp({
|
|
290
|
-
name: galleryApp.name,
|
|
291
|
-
description: galleryApp.description,
|
|
292
|
-
schemaJson: galleryApp.schemaJson,
|
|
293
|
-
htmlDefinition: galleryApp.htmlDefinition,
|
|
294
|
-
formatVersion: galleryApp.formatVersion,
|
|
295
|
-
});
|
|
296
|
-
|
|
297
|
-
if (galleryApp.formatVersion === 2 && galleryApp.sourceFiles) {
|
|
298
|
-
const appDir = getAppDirPath(app.id);
|
|
299
|
-
for (const [relPath, content] of Object.entries(galleryApp.sourceFiles)) {
|
|
300
|
-
const fullPath = join(appDir, relPath);
|
|
301
|
-
mkdirSync(dirname(fullPath), { recursive: true });
|
|
302
|
-
writeFileSync(fullPath, content, "utf-8");
|
|
303
|
-
}
|
|
304
|
-
const result = await compileApp(appDir);
|
|
305
|
-
if (!result.ok) {
|
|
306
|
-
log.warn(
|
|
307
|
-
{ appId: app.id, errors: result.errors },
|
|
308
|
-
"Gallery app compilation had errors; falling back to htmlDefinition",
|
|
309
|
-
);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
return { success: true, appId: app.id, name: app.name };
|
|
314
|
-
}
|
|
315
|
-
|
|
316
274
|
async function openBundle(filePath: string): Promise<Record<string, unknown>> {
|
|
317
275
|
const fileStat = await stat(filePath);
|
|
318
276
|
const bundleSizeBytes = fileStat.size;
|
|
@@ -594,22 +552,6 @@ function handleForkSharedApp({ body, headers }: RouteHandlerArgs) {
|
|
|
594
552
|
return result;
|
|
595
553
|
}
|
|
596
554
|
|
|
597
|
-
async function handleInstallGalleryApp({ body, headers }: RouteHandlerArgs) {
|
|
598
|
-
if (!body?.galleryAppId) {
|
|
599
|
-
throw new BadRequestError("galleryAppId is required");
|
|
600
|
-
}
|
|
601
|
-
const result = await installGalleryApp(body.galleryAppId as string);
|
|
602
|
-
if (!result.success) {
|
|
603
|
-
throw new BadRequestError(result.error);
|
|
604
|
-
}
|
|
605
|
-
publishAppsChanged(getOriginClientId(headers));
|
|
606
|
-
return result;
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
function handleListGallery() {
|
|
610
|
-
return { gallery: defaultGallery };
|
|
611
|
-
}
|
|
612
|
-
|
|
613
555
|
function handleSignBundle({ body }: RouteHandlerArgs) {
|
|
614
556
|
if (!body?.payload) {
|
|
615
557
|
throw new BadRequestError("payload is required");
|
|
@@ -900,62 +842,6 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
900
842
|
name: z.string(),
|
|
901
843
|
}),
|
|
902
844
|
},
|
|
903
|
-
{
|
|
904
|
-
operationId: "apps_gallery_install",
|
|
905
|
-
endpoint: "apps/gallery/install",
|
|
906
|
-
method: "POST",
|
|
907
|
-
policy: {
|
|
908
|
-
requiredScopes: ["settings.write"],
|
|
909
|
-
allowedPrincipalTypes: ACTOR_PRINCIPALS,
|
|
910
|
-
},
|
|
911
|
-
handler: handleInstallGalleryApp,
|
|
912
|
-
summary: "Install a gallery app",
|
|
913
|
-
description: "Install an app from the built-in gallery by its ID.",
|
|
914
|
-
tags: ["apps"],
|
|
915
|
-
requestBody: z.object({ galleryAppId: z.string() }),
|
|
916
|
-
responseBody: z.object({
|
|
917
|
-
success: z.literal(true),
|
|
918
|
-
appId: z.string(),
|
|
919
|
-
name: z.string(),
|
|
920
|
-
}),
|
|
921
|
-
},
|
|
922
|
-
{
|
|
923
|
-
operationId: "apps_gallery_list",
|
|
924
|
-
endpoint: "apps/gallery",
|
|
925
|
-
method: "GET",
|
|
926
|
-
policy: {
|
|
927
|
-
requiredScopes: ["settings.read"],
|
|
928
|
-
allowedPrincipalTypes: ACTOR_PRINCIPALS,
|
|
929
|
-
},
|
|
930
|
-
handler: handleListGallery,
|
|
931
|
-
summary: "List gallery apps",
|
|
932
|
-
description: "Return the built-in app gallery catalog.",
|
|
933
|
-
tags: ["apps"],
|
|
934
|
-
responseBody: z.object({
|
|
935
|
-
gallery: z.object({
|
|
936
|
-
version: z.number(),
|
|
937
|
-
updatedAt: z.string(),
|
|
938
|
-
categories: z.array(
|
|
939
|
-
z.object({
|
|
940
|
-
id: z.string(),
|
|
941
|
-
name: z.string(),
|
|
942
|
-
icon: z.string(),
|
|
943
|
-
}),
|
|
944
|
-
),
|
|
945
|
-
apps: z.array(
|
|
946
|
-
z.object({
|
|
947
|
-
id: z.string(),
|
|
948
|
-
name: z.string(),
|
|
949
|
-
description: z.string(),
|
|
950
|
-
icon: z.string(),
|
|
951
|
-
category: z.string(),
|
|
952
|
-
version: z.string(),
|
|
953
|
-
featured: z.boolean().optional(),
|
|
954
|
-
}),
|
|
955
|
-
),
|
|
956
|
-
}),
|
|
957
|
-
}),
|
|
958
|
-
},
|
|
959
845
|
{
|
|
960
846
|
operationId: "apps_import_bundle",
|
|
961
847
|
endpoint: "apps/import-bundle",
|
|
@@ -969,7 +855,10 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
969
855
|
description:
|
|
970
856
|
"Upload, validate, and install a .vbundle archive as a new local app.",
|
|
971
857
|
tags: ["apps"],
|
|
972
|
-
|
|
858
|
+
requestBody: {
|
|
859
|
+
contentType: "application/octet-stream",
|
|
860
|
+
schema: { type: "string", format: "binary" },
|
|
861
|
+
},
|
|
973
862
|
responseBody: z.object({
|
|
974
863
|
success: z.boolean(),
|
|
975
864
|
appId: z.string(),
|