@vellumai/assistant 0.8.8 → 0.8.9-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +6 -6
- package/bun.lock +2 -2
- package/examples/plugins/echo/README.md +61 -60
- package/examples/plugins/echo/hooks/post-tool-use.ts +18 -0
- package/examples/plugins/echo/hooks/stop.ts +16 -0
- package/examples/plugins/echo/hooks/user-prompt-submit.ts +18 -0
- package/examples/plugins/echo/package.json +1 -2
- package/examples/plugins/echo/src/emit.ts +19 -0
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +7 -6
- package/openapi.yaml +235 -6
- package/package.json +2 -2
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +69 -14
- package/src/__tests__/agent-loop-exit-reason.test.ts +204 -144
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +50 -35
- package/src/__tests__/agent-loop-output-hooks.test.ts +357 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +25 -6
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +41 -21
- package/src/__tests__/agent-loop-thinking.test.ts +36 -20
- package/src/__tests__/agent-loop.test.ts +441 -96
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +14 -14
- package/src/__tests__/agent-wake-override-profile.test.ts +17 -21
- package/src/__tests__/anthropic-provider.test.ts +1 -1
- package/src/__tests__/app-builder-tool-scripts.test.ts +21 -0
- package/src/__tests__/app-control-flow.test.ts +1 -1
- package/src/__tests__/app-dir-path-guard.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +132 -0
- package/src/__tests__/approval-cascade.test.ts +5 -4
- package/src/__tests__/approval-routes-http.test.ts +4 -1
- package/src/__tests__/background-workers-disk-pressure.test.ts +1 -1
- package/src/__tests__/channel-approval-routes.test.ts +1 -1
- package/src/__tests__/channel-approvals.test.ts +1 -1
- package/src/__tests__/compaction-circuit.test.ts +258 -0
- package/src/__tests__/compaction-direct.test.ts +132 -0
- package/src/__tests__/compaction-events.test.ts +5 -5
- package/src/__tests__/compactor-web-search-strip.test.ts +213 -0
- package/src/__tests__/context-overflow-reducer.test.ts +1 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +6 -4
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +7 -10
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +78 -119
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +142 -218
- package/src/__tests__/conversation-agent-loop.test.ts +297 -586
- package/src/__tests__/conversation-clean-command.test.ts +5 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +5 -4
- package/src/__tests__/conversation-crud-inference-profile.test.ts +7 -9
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +4 -4
- package/src/__tests__/conversation-process-callsite.test.ts +14 -14
- package/src/__tests__/conversation-provider-retry-repair.test.ts +70 -65
- package/src/__tests__/conversation-queue.test.ts +9 -9
- package/src/__tests__/conversation-runtime-assembly.test.ts +923 -231
- package/src/__tests__/conversation-runtime-workspace.test.ts +115 -20
- package/src/__tests__/conversation-slash-queue.test.ts +6 -4
- package/src/__tests__/conversation-slash-unknown.test.ts +6 -4
- package/src/__tests__/conversation-speed-override.test.ts +10 -9
- package/src/__tests__/conversation-starter-routes.test.ts +14 -6
- package/src/__tests__/conversation-workspace-cache-state.test.ts +23 -20
- package/src/__tests__/conversation-workspace-injection.test.ts +68 -6
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +14 -11
- package/src/__tests__/conversations-import-system-filter.test.ts +101 -0
- package/src/__tests__/credential-security-invariants.test.ts +0 -1
- package/src/__tests__/db-acp-history.test.ts +101 -0
- package/src/__tests__/dynamic-page-surface.test.ts +31 -0
- package/src/__tests__/empty-response-hook.test.ts +1 -1
- package/src/__tests__/file-write-tool.test.ts +63 -0
- package/src/__tests__/gateway-only-guard.test.ts +12 -2
- 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__/history-repair-hook.test.ts +1 -1
- package/src/__tests__/host-app-control-routes.test.ts +1 -1
- package/src/__tests__/host-cu-routes-targeted.test.ts +3 -3
- package/src/__tests__/inference-profile-reaper.test.ts +62 -0
- package/src/__tests__/inference-profile-session-handler.test.ts +86 -0
- package/src/__tests__/injector-background-turn.test.ts +13 -23
- package/src/__tests__/injector-chain.test.ts +268 -44
- package/src/__tests__/injector-disk-pressure.test.ts +210 -52
- package/src/__tests__/injector-document-comments.test.ts +97 -114
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +2 -2
- package/src/__tests__/injector-v3-suppression.test.ts +4 -4
- package/src/__tests__/list-messages-client-message-id.test.ts +91 -0
- package/src/__tests__/list-messages-hidden-metadata.test.ts +38 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +131 -26
- package/src/__tests__/memory-v2-static-injector.test.ts +86 -8
- package/src/__tests__/parallel-tool.benchmark.test.ts +35 -8
- package/src/__tests__/plugin-api-shim.test.ts +6 -9
- package/src/__tests__/plugin-bootstrap.test.ts +12 -23
- package/src/__tests__/plugin-registry.test.ts +3 -49
- package/src/__tests__/plugin-types.test.ts +0 -70
- package/src/__tests__/pre-model-call-sanitize.test.ts +109 -0
- package/src/__tests__/reaction-persistence.test.ts +1 -1
- package/src/__tests__/send-endpoint-busy.test.ts +4 -1
- package/src/__tests__/skill-feature-flags-integration.test.ts +33 -0
- package/src/__tests__/steer-tool-repair.test.ts +1 -1
- package/src/__tests__/subagent-call-site-routing.test.ts +1 -1
- package/src/__tests__/subagent-detail.test.ts +25 -7
- package/src/__tests__/subagent-fork-notifications.test.ts +1 -3
- package/src/__tests__/subagent-fork-spawn.test.ts +1 -1
- package/src/__tests__/subagent-manager-notify.test.ts +1 -3
- package/src/__tests__/subagent-notify-parent.test.ts +1 -3
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +1 -1
- package/src/__tests__/title-generate-hook.test.ts +1 -1
- package/src/__tests__/tool-error-hook.test.ts +1 -1
- package/src/__tests__/tool-result-truncate-hook.test.ts +1 -1
- package/src/__tests__/user-plugin-loader.test.ts +54 -286
- package/src/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 +106 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +97 -0
- package/src/acp/__tests__/session-manager-persistence.test.ts +95 -28
- package/src/acp/__tests__/session-manager-resume.test.ts +888 -0
- package/src/acp/agent-process.ts +61 -1
- package/src/acp/auto-install.test.ts +280 -0
- package/src/acp/auto-install.ts +232 -0
- package/src/acp/client-handler.ts +31 -0
- package/src/acp/feature-gate.test.ts +48 -0
- package/src/acp/feature-gate.ts +34 -0
- package/src/acp/prepare-agent-env.ts +80 -27
- package/src/acp/resolve-agent.test.ts +225 -9
- package/src/acp/resolve-agent.ts +122 -17
- package/src/acp/resume-hint.ts +23 -0
- package/src/acp/session-manager.ts +507 -73
- package/src/agent/compaction-circuit.ts +60 -102
- package/src/agent/loop.ts +414 -248
- package/src/api/responses/conversation-message.ts +14 -1
- package/src/approvals/guardian-request-resolvers.ts +1 -1
- package/src/background-wake/next-wake.ts +1 -0
- package/src/cli/commands/db/__tests__/repair.test.ts +3 -1
- package/src/cli/commands/plugins.ts +43 -37
- package/src/cli/lib/__tests__/install-from-github.test.ts +429 -111
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +196 -0
- package/src/cli/lib/__tests__/plugin-details.test.ts +372 -0
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +220 -0
- package/src/cli/lib/__tests__/search-plugins.test.ts +226 -32
- package/src/cli/lib/install-from-github.ts +464 -55
- package/src/cli/lib/plugin-catalog-cache.ts +84 -0
- package/src/cli/lib/plugin-details.ts +409 -0
- package/src/cli/lib/plugin-marketplace.ts +197 -0
- package/src/cli/lib/search-plugins.ts +195 -29
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
- package/src/config/acp-defaults.test.ts +10 -0
- package/src/config/acp-defaults.ts +6 -0
- package/src/config/bundled-skills/acp/SKILL.md +85 -33
- package/src/config/bundled-skills/acp/TOOLS.json +4 -4
- package/src/config/bundled-skills/app-builder/SKILL.md +224 -381
- package/src/config/bundled-skills/app-builder/TOOLS.json +72 -2
- package/src/config/bundled-skills/app-builder/references/DESIGN_SYSTEM.md +48 -0
- package/src/config/bundled-skills/app-builder/references/RESPONSIVE.md +57 -0
- package/src/config/bundled-skills/app-builder/references/SLIDES.md +38 -0
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +62 -0
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +18 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +28 -23
- package/src/config/bundled-skills/document-editor/TOOLS.json +1 -1
- package/src/config/bundled-tool-registry.ts +4 -0
- package/src/config/call-site-defaults.ts +0 -2
- package/src/config/feature-flag-registry.json +15 -6
- package/src/config/schemas/call-site-catalog.ts +0 -14
- package/src/config/schemas/heartbeat.ts +9 -0
- package/src/config/schemas/llm.ts +0 -2
- package/src/context/compactor.ts +22 -4
- package/src/context/strip-injections.ts +8 -2
- package/src/context/window-manager.ts +27 -13
- package/src/daemon/conversation-agent-loop-handlers.ts +10 -35
- package/src/daemon/conversation-agent-loop.ts +175 -1055
- package/src/daemon/conversation-lifecycle.ts +11 -255
- package/src/daemon/conversation-process.ts +8 -136
- package/src/daemon/conversation-registry.ts +159 -0
- package/src/daemon/conversation-runtime-assembly.ts +293 -392
- package/src/daemon/conversation-store.ts +9 -90
- package/src/daemon/conversation-surfaces.ts +24 -8
- package/src/daemon/conversation-workspace.ts +17 -0
- package/src/daemon/conversation.ts +404 -57
- package/src/daemon/disk-pressure-policy.ts +0 -1
- package/src/daemon/external-plugins-bootstrap.ts +14 -19
- package/src/daemon/handlers/conversations.ts +3 -1
- package/src/daemon/handlers/skills.ts +4 -1
- package/src/daemon/host-proxy-preactivation.ts +1 -3
- package/src/daemon/lifecycle.ts +21 -7
- package/src/daemon/server.ts +2 -0
- package/src/daemon/wake-conversation-ops.ts +269 -0
- package/src/embedded/plugin-api.ts +2 -2
- package/src/export/__tests__/transcript-formatter.test.ts +5 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +3 -0
- package/src/heartbeat/heartbeat-run-store.ts +23 -1
- package/src/heartbeat/heartbeat-service.ts +26 -0
- package/src/ipc/__tests__/browser-ipc.test.ts +1 -1
- package/src/ipc/__tests__/ui-request-route.test.ts +3 -3
- package/src/ipc/skill-routes/__tests__/memory.test.ts +15 -0
- package/src/ipc/skill-routes/memory.ts +4 -2
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +87 -0
- package/src/memory/conversation-crud.ts +29 -19
- package/src/memory/conversation-starter-checkpoints.ts +1 -0
- package/src/memory/db-init.ts +2 -0
- package/src/memory/job-handlers/conversation-starters.ts +13 -2
- package/src/memory/jobs/__tests__/embed-concept-page.test.ts +5 -4
- package/src/memory/jobs-worker.ts +25 -1
- package/src/memory/migrations/272-acp-session-history-cwd.ts +36 -0
- package/src/memory/migrations/index.ts +1 -0
- package/src/memory/schema/acp.ts +4 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +3 -3
- package/src/memory/v2/consolidation-job.ts +13 -4
- package/src/plugin-api/constants.ts +4 -0
- package/src/plugin-api/index.ts +6 -5
- package/src/plugin-api/types.ts +75 -0
- package/src/plugins/defaults/compaction/compact.ts +59 -0
- package/src/{daemon → plugins/defaults/compaction}/context-overflow-reducer.ts +7 -7
- package/src/plugins/defaults/compaction/manager-store.ts +57 -0
- package/src/plugins/defaults/compaction/package.json +1 -2
- package/src/plugins/defaults/empty-response/package.json +0 -1
- package/src/plugins/defaults/history-repair/package.json +0 -1
- package/src/plugins/defaults/index.ts +135 -26
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +100 -52
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit-temp.ts +184 -74
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +2 -2
- package/src/plugins/defaults/{injectors/register.ts → memory-retrieval/injectors.ts} +148 -73
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +223 -0
- 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__/live-integration.test.ts +9 -6
- 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 +8 -5
- 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 +10 -5
- 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 +5 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/shadow-plugin.test.ts +16 -17
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/__tests__/types.test.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/assign.ts +9 -5
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/capabilities.ts +5 -2
- 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/{memory/v3 → plugins/defaults/memory-v3-shadow}/maintain-job.ts +15 -8
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/orchestrate.ts +2 -2
- package/src/plugins/defaults/memory-v3-shadow/package.json +14 -0
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/page-content.ts +2 -2
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/provider-blocks.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/reconcile.ts +7 -3
- 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 +5 -5
- 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 +7 -7
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/shadow-plugin.ts +32 -94
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/tree.ts +1 -1
- package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/types.ts +1 -1
- package/src/plugins/defaults/title-generate/package.json +0 -1
- package/src/plugins/defaults/tool-error/package.json +0 -1
- package/src/plugins/defaults/tool-result-truncate/package.json +0 -1
- package/src/plugins/pipeline.ts +6 -293
- package/src/plugins/registry.ts +9 -37
- package/src/plugins/types.ts +76 -381
- package/src/plugins/user-loader.ts +30 -127
- package/src/prompts/__tests__/system-prompt.test.ts +6 -0
- package/src/prompts/templates/BOOTSTRAP-ACTIVATION-RAIL.md +35 -3
- package/src/runtime/__tests__/agent-wake.test.ts +555 -691
- package/src/runtime/__tests__/interactive-ui.test.ts +1 -1
- package/src/runtime/agent-wake.ts +108 -209
- package/src/runtime/assistant-event-hub.ts +1 -1
- package/src/runtime/channel-approvals.ts +1 -1
- package/src/runtime/interactive-ui.ts +1 -1
- package/src/runtime/routes/__tests__/acp-routes.test.ts +315 -55
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +423 -73
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +5 -4
- package/src/runtime/routes/__tests__/surface-content-routes.test.ts +4 -1
- package/src/runtime/routes/acp-routes.test.ts +89 -25
- package/src/runtime/routes/acp-routes.ts +81 -29
- package/src/runtime/routes/approval-routes.ts +1 -1
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/browser-tabs-routes.ts +6 -10
- package/src/runtime/routes/conversation-cli-routes.ts +1 -1
- package/src/runtime/routes/conversation-list-routes.ts +1 -1
- package/src/runtime/routes/conversation-query-routes.ts +1 -1
- package/src/runtime/routes/conversation-routes.ts +28 -2
- package/src/runtime/routes/conversation-starter-routes.ts +13 -7
- package/src/runtime/routes/conversations-import-routes.ts +24 -7
- package/src/runtime/routes/host-app-control-routes.ts +1 -1
- package/src/runtime/routes/host-cu-routes.ts +1 -1
- package/src/runtime/routes/identity-routes.ts +18 -3
- package/src/runtime/routes/inbound-message-handler.ts +1 -1
- package/src/runtime/routes/inference-profile-session-handler.ts +11 -0
- package/src/runtime/routes/inference-profile-session-reaper.ts +6 -0
- package/src/runtime/routes/memory-v3-routes.ts +16 -6
- package/src/runtime/routes/playground/helpers.ts +1 -1
- package/src/runtime/routes/plugins-routes.ts +337 -35
- package/src/runtime/routes/surface-conversation-resolver.ts +4 -3
- package/src/runtime/routes/work-items-routes.ts +2 -4
- package/src/runtime/services/conversation-serializer.ts +1 -1
- package/src/signals/cancel.ts +2 -4
- package/src/subagent/manager.ts +21 -5
- package/src/tools/acp/context.ts +20 -0
- package/src/tools/acp/list-agents.test.ts +8 -2
- package/src/tools/acp/spawn.test.ts +176 -195
- package/src/tools/acp/spawn.ts +37 -172
- package/src/tools/acp/steer.test.ts +105 -8
- package/src/tools/acp/steer.ts +48 -17
- package/src/tools/apps/executors.ts +166 -50
- package/src/tools/filesystem/write.ts +34 -0
- package/src/tools/subagent/spawn.ts +2 -4
- package/src/tools/ui-surface/definitions.ts +25 -5
- package/src/workspace/migrations/051-seed-conversation-summarization-callsite.ts +4 -5
- package/src/workspace/migrations/097-enable-adaptive-thinking-managed-profiles.ts +69 -45
- package/docs/plugins.md +0 -832
- package/examples/plugins/echo/register.ts +0 -143
- package/src/__tests__/circuit-breaker-pipeline.test.ts +0 -405
- package/src/__tests__/compaction-pipeline.test.ts +0 -210
- package/src/__tests__/compaction-timeout-recovery.test.ts +0 -251
- package/src/__tests__/overflow-reduce-pipeline.test.ts +0 -667
- package/src/__tests__/pipeline-runner.test.ts +0 -554
- package/src/__tests__/plugin-external-api.test.ts +0 -68
- package/src/daemon/wake-target-adapter.ts +0 -253
- package/src/plugins/defaults/circuit-breaker/middlewares/circuitBreaker.ts +0 -93
- package/src/plugins/defaults/circuit-breaker/package.json +0 -15
- package/src/plugins/defaults/circuit-breaker/register.ts +0 -39
- package/src/plugins/defaults/compaction/middlewares/compaction.ts +0 -25
- package/src/plugins/defaults/compaction/register.ts +0 -35
- package/src/plugins/defaults/compaction/terminal.ts +0 -73
- package/src/plugins/defaults/empty-response/register.ts +0 -23
- package/src/plugins/defaults/history-repair/register.ts +0 -24
- package/src/plugins/defaults/overflow-reduce/middlewares/overflowReduce.ts +0 -126
- package/src/plugins/defaults/overflow-reduce/package.json +0 -15
- package/src/plugins/defaults/overflow-reduce/register.ts +0 -42
- package/src/plugins/defaults/title-generate/register.ts +0 -35
- package/src/plugins/defaults/tool-error/register.ts +0 -23
- package/src/plugins/defaults/tool-result-truncate/register.ts +0 -24
- package/src/plugins/external-api.ts +0 -104
- package/src/proactive-artifact/aux-message-injector.ts +0 -97
- package/src/proactive-artifact/decision.test.ts +0 -226
- package/src/proactive-artifact/decision.ts +0 -165
- package/src/proactive-artifact/index.ts +0 -7
- package/src/proactive-artifact/job.test.ts +0 -962
- package/src/proactive-artifact/job.ts +0 -372
- package/src/proactive-artifact/message-copy.ts +0 -58
- package/src/proactive-artifact/trigger-state.test.ts +0 -286
- package/src/proactive-artifact/trigger-state.ts +0 -123
- /package/src/{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__/health.test.ts +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__/provider-blocks.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__/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}/health.ts +0 -0
- /package/src/{memory/v3 → plugins/defaults/memory-v3-shadow}/llm-retry.ts +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
|
@@ -33,6 +33,7 @@ import { NotFoundError } from "./errors.js";
|
|
|
33
33
|
import {
|
|
34
34
|
getCachedIntro,
|
|
35
35
|
readWorkspaceGreetings,
|
|
36
|
+
readWorkspaceIdentityIntro,
|
|
36
37
|
setCachedIntro,
|
|
37
38
|
} from "./identity-intro-cache.js";
|
|
38
39
|
import type { RouteDefinition } from "./types.js";
|
|
@@ -455,16 +456,30 @@ function getIdentityIntro(): IdentityIntroResponse {
|
|
|
455
456
|
return identityIntroResponse(FALLBACK_GREETINGS, "fallback");
|
|
456
457
|
}
|
|
457
458
|
|
|
458
|
-
// 2. Cached LLM-generated greetings
|
|
459
|
+
// 2. Cached LLM-generated greetings (populated by background refresh)
|
|
459
460
|
const cached = getCachedIntro();
|
|
460
461
|
if (cached) {
|
|
461
462
|
return identityIntroResponse(cached.greetings, "cache");
|
|
462
463
|
}
|
|
463
464
|
|
|
464
|
-
// 3.
|
|
465
|
+
// 3. Identity intro tagline from `## Identity Intro` in IDENTITY.md
|
|
466
|
+
// (written during onboarding by BOOTSTRAP.md instructions)
|
|
467
|
+
const identityIntro = readWorkspaceIdentityIntro();
|
|
468
|
+
if (identityIntro) {
|
|
469
|
+
// Still trigger background generation so the next request gets
|
|
470
|
+
// LLM-generated greetings instead of the static tagline.
|
|
471
|
+
const refreshing = triggerEmptyStateGreetingGeneration();
|
|
472
|
+
return identityIntroResponse(
|
|
473
|
+
[identityIntro, ...FALLBACK_GREETINGS],
|
|
474
|
+
"workspace",
|
|
475
|
+
refreshing,
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// 4. Trigger fresh generation without blocking the empty-state UI.
|
|
465
480
|
const refreshing = triggerEmptyStateGreetingGeneration();
|
|
466
481
|
|
|
467
|
-
//
|
|
482
|
+
// 5. Generic fallback only when generation is unavailable.
|
|
468
483
|
return identityIntroResponse(FALLBACK_GREETINGS, "fallback", refreshing);
|
|
469
484
|
}
|
|
470
485
|
|
|
@@ -20,7 +20,7 @@ import {
|
|
|
20
20
|
createApprovalConversationGenerator,
|
|
21
21
|
createApprovalCopyGenerator,
|
|
22
22
|
} from "../../daemon/approval-generators.js";
|
|
23
|
-
import { findConversation } from "../../daemon/conversation-
|
|
23
|
+
import { findConversation } from "../../daemon/conversation-registry.js";
|
|
24
24
|
import {
|
|
25
25
|
canonicalizeTimeZone,
|
|
26
26
|
resolveTurnTimezoneContext,
|
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
import { randomUUID } from "node:crypto";
|
|
16
16
|
|
|
17
17
|
import { loadConfig } from "../../config/loader.js";
|
|
18
|
+
import { findConversation } from "../../daemon/conversation-registry.js";
|
|
18
19
|
import {
|
|
19
20
|
getConversation,
|
|
20
21
|
listActiveInferenceProfileSessions,
|
|
@@ -118,6 +119,11 @@ export async function setInferenceProfileSession({
|
|
|
118
119
|
};
|
|
119
120
|
}
|
|
120
121
|
setConversationInferenceProfileSession(resolvedId, null, null, null);
|
|
122
|
+
findConversation(resolvedId)?.applyInferenceProfileState({
|
|
123
|
+
profile: null,
|
|
124
|
+
sessionId: null,
|
|
125
|
+
expiresAt: null,
|
|
126
|
+
});
|
|
121
127
|
publishConversationInferenceProfileChanged(
|
|
122
128
|
{
|
|
123
129
|
conversationId: resolvedId,
|
|
@@ -188,6 +194,11 @@ export async function setInferenceProfileSession({
|
|
|
188
194
|
newSessionId ?? null,
|
|
189
195
|
newExpiresAt ?? null,
|
|
190
196
|
);
|
|
197
|
+
findConversation(resolvedId)?.applyInferenceProfileState({
|
|
198
|
+
profile,
|
|
199
|
+
sessionId: newSessionId ?? null,
|
|
200
|
+
expiresAt: newExpiresAt ?? null,
|
|
201
|
+
});
|
|
191
202
|
|
|
192
203
|
publishConversationInferenceProfileChanged(
|
|
193
204
|
{
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* background sweeps in `http-server.ts`.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
import { findConversation } from "../../daemon/conversation-registry.js";
|
|
14
15
|
import { clearExpiredInferenceProfiles } from "../../memory/conversation-crud.js";
|
|
15
16
|
import { getLogger } from "../../util/logger.js";
|
|
16
17
|
import { publishConversationInferenceProfileChanged } from "../sync/resource-sync-events.js";
|
|
@@ -37,6 +38,11 @@ export function tickInferenceProfileReaper(): void {
|
|
|
37
38
|
const now = Date.now();
|
|
38
39
|
const cleared = clearExpiredInferenceProfiles(now);
|
|
39
40
|
for (const { conversationId } of cleared) {
|
|
41
|
+
findConversation(conversationId)?.applyInferenceProfileState({
|
|
42
|
+
profile: null,
|
|
43
|
+
sessionId: null,
|
|
44
|
+
expiresAt: null,
|
|
45
|
+
});
|
|
40
46
|
publishConversationInferenceProfileChanged({
|
|
41
47
|
conversationId,
|
|
42
48
|
profile: null,
|
|
@@ -21,16 +21,26 @@ import { join } from "node:path";
|
|
|
21
21
|
import { z } from "zod";
|
|
22
22
|
|
|
23
23
|
import { getPageIndex } from "../../memory/v2/page-index.js";
|
|
24
|
-
import { loadCore } from "../../memory
|
|
25
|
-
import {
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
import { loadCore } from "../../plugins/defaults/memory-v3-shadow/core.js";
|
|
25
|
+
import {
|
|
26
|
+
computeV3Health,
|
|
27
|
+
renderV3Health,
|
|
28
|
+
} from "../../plugins/defaults/memory-v3-shadow/health.js";
|
|
29
|
+
import {
|
|
30
|
+
type LeafRef,
|
|
31
|
+
reconcileTree,
|
|
32
|
+
} from "../../plugins/defaults/memory-v3-shadow/reconcile.js";
|
|
33
|
+
import { invalidateLanes } from "../../plugins/defaults/memory-v3-shadow/shadow-plugin.js";
|
|
28
34
|
import {
|
|
29
35
|
coreSlugs,
|
|
30
36
|
loadLeafTree,
|
|
31
37
|
resolveDataDir,
|
|
32
|
-
} from "../../memory
|
|
33
|
-
import type {
|
|
38
|
+
} from "../../plugins/defaults/memory-v3-shadow/tree.js";
|
|
39
|
+
import type {
|
|
40
|
+
LeafPath,
|
|
41
|
+
LeafTree,
|
|
42
|
+
Slug,
|
|
43
|
+
} from "../../plugins/defaults/memory-v3-shadow/types.js";
|
|
34
44
|
import { getLogger } from "../../util/logger.js";
|
|
35
45
|
import { getWorkspaceDir } from "../../util/platform.js";
|
|
36
46
|
import { ACTOR_PRINCIPALS, type RoutePolicy } from "../auth/route-policy.js";
|
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type { Conversation } from "../../../daemon/conversation.js";
|
|
10
|
+
import { findConversation } from "../../../daemon/conversation-registry.js";
|
|
10
11
|
import {
|
|
11
12
|
destroyActiveConversation,
|
|
12
|
-
findConversation,
|
|
13
13
|
getOrCreateConversation,
|
|
14
14
|
} from "../../../daemon/conversation-store.js";
|
|
15
15
|
import {
|
|
@@ -1,51 +1,66 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Route handlers for the assistant plugins surface.
|
|
3
3
|
*
|
|
4
|
-
* GET /v1/plugins
|
|
5
|
-
* GET /v1/plugins/search
|
|
6
|
-
*
|
|
4
|
+
* GET /v1/plugins — list installed plugins under `<workspaceDir>/plugins/`.
|
|
5
|
+
* GET /v1/plugins/search — search the canonical GitHub catalog of installable plugins.
|
|
6
|
+
* GET /v1/plugins/:name — resolve a single plugin's detail view (metadata + README).
|
|
7
|
+
* POST /v1/plugins/install — install a plugin by name from the canonical source.
|
|
8
|
+
* DELETE /v1/plugins/:name — uninstall a plugin from `<workspaceDir>/plugins/<name>/`.
|
|
7
9
|
*
|
|
8
10
|
* The read-only routes are projections over the same library functions
|
|
9
11
|
* the CLI uses (`assistant plugins list`, `assistant plugins search`).
|
|
10
|
-
* The
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* install surface because installation fetches from GitHub, applies
|
|
16
|
-
* sanitization, and writes to disk — a heavier flow than a single
|
|
17
|
-
* JSON request justifies right now.
|
|
12
|
+
* The install / uninstall routes are symmetric to `assistant plugins
|
|
13
|
+
* install` / `uninstall` and delegate to the same `installPlugin` /
|
|
14
|
+
* `uninstallPlugin` lib functions. CLI / daemon / web stay aligned on
|
|
15
|
+
* what an installed or available plugin looks like — mirroring the
|
|
16
|
+
* skills surface, which already exposes detail + install over HTTP.
|
|
18
17
|
*
|
|
19
18
|
* # Policy gating
|
|
20
19
|
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
* source of truth for the IPC path too: the IPC route adapter resolves
|
|
26
|
-
* each route's policy and ships it in `get_route_schema`, which the
|
|
27
|
-
* gateway's IPC proxy reads from its in-memory cache. Reads require
|
|
28
|
-
* `settings.read`; uninstall requires `settings.write`.
|
|
20
|
+
* Reads require `settings.read`; install and uninstall require
|
|
21
|
+
* `settings.write`. The HTTP router enforces the per-route `policy`
|
|
22
|
+
* block below, and the IPC route adapter ships the same policy in
|
|
23
|
+
* `get_route_schema` so the gateway's IPC proxy stays in sync.
|
|
29
24
|
*/
|
|
30
25
|
|
|
31
26
|
import { z } from "zod";
|
|
32
27
|
|
|
33
|
-
import {
|
|
28
|
+
import {
|
|
29
|
+
DEFAULT_PLUGIN_REF,
|
|
30
|
+
installPlugin,
|
|
31
|
+
InvalidPluginNameError,
|
|
32
|
+
PluginAlreadyInstalledError,
|
|
33
|
+
PluginNotFoundError,
|
|
34
|
+
PluginSourceUnavailableError,
|
|
35
|
+
} from "../../cli/lib/install-from-github.js";
|
|
34
36
|
import {
|
|
35
37
|
type InstalledPluginInfo,
|
|
36
38
|
listInstalledPlugins,
|
|
37
39
|
} from "../../cli/lib/list-installed-plugins.js";
|
|
40
|
+
import { getPluginCatalog } from "../../cli/lib/plugin-catalog-cache.js";
|
|
41
|
+
import {
|
|
42
|
+
getPluginDetails,
|
|
43
|
+
PluginDetailsNotFoundError,
|
|
44
|
+
} from "../../cli/lib/plugin-details.js";
|
|
38
45
|
import {
|
|
46
|
+
assertValidSearchPattern,
|
|
47
|
+
filterPluginCatalog,
|
|
39
48
|
InvalidSearchPatternError,
|
|
49
|
+
PluginCatalogUnavailableError,
|
|
40
50
|
type PluginSearchMatch,
|
|
41
|
-
searchPlugins,
|
|
42
51
|
} from "../../cli/lib/search-plugins.js";
|
|
43
52
|
import {
|
|
44
53
|
PluginNotInstalledError,
|
|
45
54
|
uninstallPlugin,
|
|
46
55
|
} from "../../cli/lib/uninstall-plugin.js";
|
|
47
56
|
import { ACTOR_PRINCIPALS } from "../auth/route-policy.js";
|
|
48
|
-
import {
|
|
57
|
+
import {
|
|
58
|
+
BadRequestError,
|
|
59
|
+
ConflictError,
|
|
60
|
+
InternalError,
|
|
61
|
+
NotFoundError,
|
|
62
|
+
ServiceUnavailableError,
|
|
63
|
+
} from "./errors.js";
|
|
49
64
|
import type { RouteDefinition, RouteHandlerArgs } from "./types.js";
|
|
50
65
|
|
|
51
66
|
// ---------------------------------------------------------------------------
|
|
@@ -83,17 +98,43 @@ const pluginsListResponseSchema = z.object({
|
|
|
83
98
|
plugins: z.array(pluginInfoSchema),
|
|
84
99
|
});
|
|
85
100
|
|
|
101
|
+
const pluginMatchSourceSchema = z
|
|
102
|
+
.discriminatedUnion("kind", [
|
|
103
|
+
z.object({
|
|
104
|
+
kind: z.literal("first-party"),
|
|
105
|
+
}),
|
|
106
|
+
z.object({
|
|
107
|
+
kind: z.literal("github"),
|
|
108
|
+
repo: z
|
|
109
|
+
.string()
|
|
110
|
+
.describe("`owner/repo` of the external plugin repository."),
|
|
111
|
+
path: z
|
|
112
|
+
.string()
|
|
113
|
+
.optional()
|
|
114
|
+
.describe(
|
|
115
|
+
"Directory within the repo, when the plugin is not at the root.",
|
|
116
|
+
),
|
|
117
|
+
ref: z.string().describe("Pinned git ref the plugin is fetched from."),
|
|
118
|
+
}),
|
|
119
|
+
])
|
|
120
|
+
.describe(
|
|
121
|
+
"Origin of the match: a first-party in-repo plugin or a whitelisted external one.",
|
|
122
|
+
);
|
|
123
|
+
|
|
86
124
|
const pluginSearchMatchSchema = z.object({
|
|
87
125
|
name: z
|
|
88
126
|
.string()
|
|
89
|
-
.describe(
|
|
90
|
-
"Directory name under `experimental/plugins/`. Matches `assistant plugins install <name>`.",
|
|
91
|
-
),
|
|
127
|
+
.describe("Install name. Matches `assistant plugins install <name>`."),
|
|
92
128
|
path: z
|
|
93
129
|
.string()
|
|
94
130
|
.describe(
|
|
95
|
-
"
|
|
131
|
+
"Human-readable origin: a repo-relative path (`experimental/plugins/<name>`) for first-party plugins or a `github:owner/repo@ref` locator for external ones.",
|
|
96
132
|
),
|
|
133
|
+
description: z
|
|
134
|
+
.string()
|
|
135
|
+
.optional()
|
|
136
|
+
.describe("Short description, when known (external entries only today)."),
|
|
137
|
+
source: pluginMatchSourceSchema,
|
|
97
138
|
});
|
|
98
139
|
|
|
99
140
|
const pluginsSearchResponseSchema = z.object({
|
|
@@ -119,6 +160,66 @@ const pluginUninstallResponseSchema = z.object({
|
|
|
119
160
|
),
|
|
120
161
|
});
|
|
121
162
|
|
|
163
|
+
const pluginDetailsResponseSchema = z.object({
|
|
164
|
+
name: z
|
|
165
|
+
.string()
|
|
166
|
+
.describe("Install name. Matches `assistant plugins install <name>`."),
|
|
167
|
+
installed: z
|
|
168
|
+
.boolean()
|
|
169
|
+
.describe(
|
|
170
|
+
"Whether a copy is materialized under `<workspaceDir>/plugins/<name>/`.",
|
|
171
|
+
),
|
|
172
|
+
description: z
|
|
173
|
+
.string()
|
|
174
|
+
.nullable()
|
|
175
|
+
.describe(
|
|
176
|
+
"Short description, best-effort across disk, manifest, and repo.",
|
|
177
|
+
),
|
|
178
|
+
homepage: z.string().nullable().describe("Project homepage URL, when known."),
|
|
179
|
+
license: z
|
|
180
|
+
.string()
|
|
181
|
+
.nullable()
|
|
182
|
+
.describe("SPDX license expression, when known."),
|
|
183
|
+
version: z
|
|
184
|
+
.string()
|
|
185
|
+
.nullable()
|
|
186
|
+
.describe(
|
|
187
|
+
"Resolved version (installed copy first, then repo `package.json`).",
|
|
188
|
+
),
|
|
189
|
+
source: pluginMatchSourceSchema,
|
|
190
|
+
readme: z
|
|
191
|
+
.string()
|
|
192
|
+
.nullable()
|
|
193
|
+
.describe("README markdown, or null when the plugin ships none."),
|
|
194
|
+
ref: z
|
|
195
|
+
.string()
|
|
196
|
+
.describe("Git ref the catalog metadata / README were resolved at."),
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
const pluginInstallRequestSchema = z.object({
|
|
200
|
+
name: z
|
|
201
|
+
.string()
|
|
202
|
+
.describe(
|
|
203
|
+
"Install name to resolve against the catalog (first-party directory or marketplace entry).",
|
|
204
|
+
),
|
|
205
|
+
force: z
|
|
206
|
+
.boolean()
|
|
207
|
+
.optional()
|
|
208
|
+
.describe("Overwrite an existing install in place. Defaults to false."),
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
const pluginInstallResponseSchema = z.object({
|
|
212
|
+
ok: z.literal(true),
|
|
213
|
+
name: z.string().describe("Install name that was materialized."),
|
|
214
|
+
target: z
|
|
215
|
+
.string()
|
|
216
|
+
.describe("Absolute path the plugin was materialized into on the host."),
|
|
217
|
+
fileCount: z
|
|
218
|
+
.number()
|
|
219
|
+
.describe("Number of files written for the installed plugin."),
|
|
220
|
+
ref: z.string().describe("Git ref the plugin was fetched from."),
|
|
221
|
+
});
|
|
222
|
+
|
|
122
223
|
// ---------------------------------------------------------------------------
|
|
123
224
|
// Helpers
|
|
124
225
|
// ---------------------------------------------------------------------------
|
|
@@ -149,6 +250,41 @@ function projectPlugin(entry: InstalledPluginInfo): PluginView {
|
|
|
149
250
|
return view;
|
|
150
251
|
}
|
|
151
252
|
|
|
253
|
+
/** Wire shape for a catalog match. Mirrors {@link pluginSearchMatchSchema}. */
|
|
254
|
+
interface PluginMatchView {
|
|
255
|
+
name: string;
|
|
256
|
+
path: string;
|
|
257
|
+
description?: string;
|
|
258
|
+
source:
|
|
259
|
+
| { kind: "first-party" }
|
|
260
|
+
| { kind: "github"; repo: string; path?: string; ref: string };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Re-pack a `readonly` lib match into a mutable wire object so the route
|
|
265
|
+
* serializer's `Record<string, unknown>` contract holds. The wire shape is
|
|
266
|
+
* identical to {@link PluginSearchMatch}.
|
|
267
|
+
*/
|
|
268
|
+
function projectMatch(m: PluginSearchMatch): PluginMatchView {
|
|
269
|
+
const view: PluginMatchView = {
|
|
270
|
+
name: m.name,
|
|
271
|
+
path: m.path,
|
|
272
|
+
source:
|
|
273
|
+
m.source.kind === "github"
|
|
274
|
+
? {
|
|
275
|
+
kind: "github",
|
|
276
|
+
repo: m.source.repo,
|
|
277
|
+
ref: m.source.ref,
|
|
278
|
+
...(m.source.path !== undefined ? { path: m.source.path } : {}),
|
|
279
|
+
}
|
|
280
|
+
: { kind: "first-party" },
|
|
281
|
+
};
|
|
282
|
+
if (m.description !== undefined) {
|
|
283
|
+
view.description = m.description;
|
|
284
|
+
}
|
|
285
|
+
return view;
|
|
286
|
+
}
|
|
287
|
+
|
|
152
288
|
function matchesQuery(plugin: PluginView, needle: string): boolean {
|
|
153
289
|
const q = needle.toLowerCase();
|
|
154
290
|
if (plugin.id.toLowerCase().includes(q)) return true;
|
|
@@ -180,7 +316,7 @@ function handleListPlugins({ queryParams = {} }: RouteHandlerArgs): {
|
|
|
180
316
|
interface PluginsSearchResponse {
|
|
181
317
|
query: string;
|
|
182
318
|
ref: string;
|
|
183
|
-
matches:
|
|
319
|
+
matches: PluginMatchView[];
|
|
184
320
|
}
|
|
185
321
|
|
|
186
322
|
async function handleSearchPlugins({
|
|
@@ -189,25 +325,39 @@ async function handleSearchPlugins({
|
|
|
189
325
|
// Empty string is a legitimate "match everything" query per the lib's
|
|
190
326
|
// contract — accept it without forcing the caller to pick a sentinel.
|
|
191
327
|
const query = queryParams.q ?? "";
|
|
192
|
-
const ref = queryParams.ref?.trim() ||
|
|
328
|
+
const ref = queryParams.ref?.trim() || DEFAULT_PLUGIN_REF;
|
|
193
329
|
|
|
194
330
|
try {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
);
|
|
331
|
+
// Reject a malformed regex before any network I/O so a user typo is a
|
|
332
|
+
// cheap deterministic 400 — never a wasted GitHub request that could
|
|
333
|
+
// surface as 503 on a cold cache when upstream is rate-limited.
|
|
334
|
+
assertValidSearchPattern(query);
|
|
335
|
+
// The catalog is cached per ref (and served stale on upstream failure),
|
|
336
|
+
// so repeated searches don't re-hit GitHub's unauthenticated rate limit.
|
|
337
|
+
// Filtering by the query is an in-memory operation over that catalog.
|
|
338
|
+
const catalog = await getPluginCatalog(ref, {
|
|
339
|
+
fetch: globalThis.fetch.bind(globalThis),
|
|
340
|
+
});
|
|
341
|
+
const matches = filterPluginCatalog(catalog, query);
|
|
199
342
|
// Re-pack `readonly` lib types into mutable copies so the route
|
|
200
343
|
// serializer's `Record<string, unknown>` contract holds. The wire
|
|
201
344
|
// shape is identical.
|
|
202
345
|
return {
|
|
203
|
-
query
|
|
204
|
-
ref:
|
|
205
|
-
matches:
|
|
346
|
+
query,
|
|
347
|
+
ref: catalog.ref,
|
|
348
|
+
matches: matches.map(projectMatch),
|
|
206
349
|
};
|
|
207
350
|
} catch (err) {
|
|
208
351
|
if (err instanceof InvalidSearchPatternError) {
|
|
209
352
|
throw new BadRequestError(err.message);
|
|
210
353
|
}
|
|
354
|
+
// A rate-limited or unavailable upstream (with no cache to fall back on)
|
|
355
|
+
// is transient and retryable — surface it as 503 rather than a
|
|
356
|
+
// misleading 500 so the client can show a "temporarily unavailable"
|
|
357
|
+
// state and retry later.
|
|
358
|
+
if (err instanceof PluginCatalogUnavailableError) {
|
|
359
|
+
throw new ServiceUnavailableError(err.message);
|
|
360
|
+
}
|
|
211
361
|
throw new InternalError(
|
|
212
362
|
err instanceof Error ? err.message : "plugin catalog search failed",
|
|
213
363
|
);
|
|
@@ -248,6 +398,87 @@ function handleUninstallPlugin({
|
|
|
248
398
|
}
|
|
249
399
|
}
|
|
250
400
|
|
|
401
|
+
// ---------------------------------------------------------------------------
|
|
402
|
+
// Handler — detail view
|
|
403
|
+
// ---------------------------------------------------------------------------
|
|
404
|
+
|
|
405
|
+
async function handleGetPluginDetails({
|
|
406
|
+
pathParams = {},
|
|
407
|
+
queryParams = {},
|
|
408
|
+
}: RouteHandlerArgs) {
|
|
409
|
+
const rawName = pathParams.name ?? "";
|
|
410
|
+
const ref = queryParams.ref?.trim() || undefined;
|
|
411
|
+
|
|
412
|
+
try {
|
|
413
|
+
return await getPluginDetails(
|
|
414
|
+
{ name: rawName, ref },
|
|
415
|
+
{ fetch: globalThis.fetch.bind(globalThis) },
|
|
416
|
+
);
|
|
417
|
+
} catch (err) {
|
|
418
|
+
if (err instanceof InvalidPluginNameError) {
|
|
419
|
+
throw new BadRequestError(err.message);
|
|
420
|
+
}
|
|
421
|
+
if (err instanceof PluginDetailsNotFoundError) {
|
|
422
|
+
throw new NotFoundError(err.message);
|
|
423
|
+
}
|
|
424
|
+
throw new InternalError(
|
|
425
|
+
err instanceof Error ? err.message : "plugin detail lookup failed",
|
|
426
|
+
);
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// ---------------------------------------------------------------------------
|
|
431
|
+
// Handler — install
|
|
432
|
+
// ---------------------------------------------------------------------------
|
|
433
|
+
|
|
434
|
+
async function handleInstallPlugin({ body = {} }: RouteHandlerArgs) {
|
|
435
|
+
const name = typeof body.name === "string" ? body.name : "";
|
|
436
|
+
if (!name) {
|
|
437
|
+
throw new BadRequestError("`name` is required");
|
|
438
|
+
}
|
|
439
|
+
const force = typeof body.force === "boolean" ? body.force : undefined;
|
|
440
|
+
|
|
441
|
+
// The ref is pinned to the curated `DEFAULT_PLUGIN_REF` rather than taken
|
|
442
|
+
// from the request: a caller-supplied ref would let any `settings.write`
|
|
443
|
+
// principal install from an unreviewed revision (a PR branch, fork ref,
|
|
444
|
+
// ...) whose marketplace manifest or first-party plugin directory could
|
|
445
|
+
// carry attacker code that the loader then dynamically imports. Installs
|
|
446
|
+
// over HTTP therefore only ever resolve against the reviewed catalog on
|
|
447
|
+
// the default ref. Operators who need another ref use the local CLI's
|
|
448
|
+
// `assistant plugins install --ref`.
|
|
449
|
+
try {
|
|
450
|
+
const result = await installPlugin(
|
|
451
|
+
{ name, ref: DEFAULT_PLUGIN_REF, force },
|
|
452
|
+
{ fetch: globalThis.fetch.bind(globalThis) },
|
|
453
|
+
);
|
|
454
|
+
return {
|
|
455
|
+
ok: true as const,
|
|
456
|
+
name: result.name,
|
|
457
|
+
target: result.target,
|
|
458
|
+
fileCount: result.fileCount,
|
|
459
|
+
ref: result.ref,
|
|
460
|
+
};
|
|
461
|
+
} catch (err) {
|
|
462
|
+
if (err instanceof InvalidPluginNameError) {
|
|
463
|
+
throw new BadRequestError(err.message);
|
|
464
|
+
}
|
|
465
|
+
if (err instanceof PluginAlreadyInstalledError) {
|
|
466
|
+
throw new ConflictError(err.message);
|
|
467
|
+
}
|
|
468
|
+
if (err instanceof PluginNotFoundError) {
|
|
469
|
+
throw new NotFoundError(err.message);
|
|
470
|
+
}
|
|
471
|
+
// A rate-limited or otherwise temporarily-down GitHub source is
|
|
472
|
+
// retryable, so surface 503 rather than a misleading 500.
|
|
473
|
+
if (err instanceof PluginSourceUnavailableError) {
|
|
474
|
+
throw new ServiceUnavailableError(err.message);
|
|
475
|
+
}
|
|
476
|
+
throw new InternalError(
|
|
477
|
+
err instanceof Error ? err.message : "plugin install failed",
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
251
482
|
// ---------------------------------------------------------------------------
|
|
252
483
|
// Route definitions
|
|
253
484
|
// ---------------------------------------------------------------------------
|
|
@@ -305,6 +536,77 @@ export const ROUTES: RouteDefinition[] = [
|
|
|
305
536
|
responseBody: pluginsSearchResponseSchema,
|
|
306
537
|
handler: handleSearchPlugins,
|
|
307
538
|
},
|
|
539
|
+
{
|
|
540
|
+
operationId: "plugins_install",
|
|
541
|
+
endpoint: "plugins/install",
|
|
542
|
+
method: "POST",
|
|
543
|
+
policy: {
|
|
544
|
+
requiredScopes: ["settings.write"],
|
|
545
|
+
allowedPrincipalTypes: ACTOR_PRINCIPALS,
|
|
546
|
+
},
|
|
547
|
+
summary: "Install a plugin",
|
|
548
|
+
description:
|
|
549
|
+
"Install a plugin by name from the canonical source — a whitelisted `experimental/plugins/marketplace.json` entry, else the first-party `experimental/plugins/<name>/` convention. Always resolves against the curated default git ref (no caller-supplied ref): installing from an unreviewed revision would bypass the marketplace/first-party curation boundary and let attacker-controlled code be loaded. Materializes the plugin under `<workspaceDir>/plugins/<name>/`; the assistant must be restarted to load it. Mirrors the CLI's `assistant plugins install <name>`. An already-installed name without `force` returns 409; a name that resolves to nothing returns 404. Sibling to `POST /v1/skills/install`.",
|
|
550
|
+
tags: ["plugins"],
|
|
551
|
+
requestBody: pluginInstallRequestSchema,
|
|
552
|
+
responseBody: pluginInstallResponseSchema,
|
|
553
|
+
additionalResponses: {
|
|
554
|
+
"400": {
|
|
555
|
+
description:
|
|
556
|
+
"The request body was missing `name` or the name failed sanitization.",
|
|
557
|
+
},
|
|
558
|
+
"404": {
|
|
559
|
+
description:
|
|
560
|
+
"No plugin resolves to the given name at the requested ref.",
|
|
561
|
+
},
|
|
562
|
+
"409": {
|
|
563
|
+
description:
|
|
564
|
+
"A plugin with the same name is already installed and `force` was not set.",
|
|
565
|
+
},
|
|
566
|
+
},
|
|
567
|
+
handler: handleInstallPlugin,
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
operationId: "plugins_get",
|
|
571
|
+
endpoint: "plugins/:name",
|
|
572
|
+
method: "GET",
|
|
573
|
+
policy: {
|
|
574
|
+
requiredScopes: ["settings.read"],
|
|
575
|
+
allowedPrincipalTypes: ACTOR_PRINCIPALS,
|
|
576
|
+
},
|
|
577
|
+
summary: "Get a plugin's detail view",
|
|
578
|
+
description:
|
|
579
|
+
"Resolve a single plugin's tracked metadata (description, homepage, license, version, source) plus its README markdown. Unions the locally installed copy, the marketplace manifest, and the plugin's repository at the pinned ref — preferring the installed copy. Names that are neither installed nor present in the catalog return 404. Powers the web plugin detail page; mirrors `GET /v1/skills/:id`.",
|
|
580
|
+
tags: ["plugins"],
|
|
581
|
+
pathParams: [
|
|
582
|
+
{
|
|
583
|
+
name: "name",
|
|
584
|
+
type: "string",
|
|
585
|
+
description:
|
|
586
|
+
"Install name. Must match the kebab-case name accepted by `assistant plugins install`.",
|
|
587
|
+
},
|
|
588
|
+
],
|
|
589
|
+
queryParams: [
|
|
590
|
+
{
|
|
591
|
+
name: "ref",
|
|
592
|
+
schema: { type: "string" },
|
|
593
|
+
description:
|
|
594
|
+
"Optional git ref to read catalog metadata / README at. Defaults to the CLI's `DEFAULT_PLUGIN_REF`.",
|
|
595
|
+
},
|
|
596
|
+
],
|
|
597
|
+
responseBody: pluginDetailsResponseSchema,
|
|
598
|
+
additionalResponses: {
|
|
599
|
+
"400": {
|
|
600
|
+
description:
|
|
601
|
+
"The plugin name failed sanitization (e.g. contained slashes, dots, or uppercase letters).",
|
|
602
|
+
},
|
|
603
|
+
"404": {
|
|
604
|
+
description:
|
|
605
|
+
"No installed copy and no catalog entry claims the given name.",
|
|
606
|
+
},
|
|
607
|
+
},
|
|
608
|
+
handler: handleGetPluginDetails,
|
|
609
|
+
},
|
|
308
610
|
{
|
|
309
611
|
operationId: "plugins_uninstall",
|
|
310
612
|
endpoint: "plugins/:name",
|
|
@@ -18,8 +18,8 @@ import type { Conversation } from "../../daemon/conversation.js";
|
|
|
18
18
|
import {
|
|
19
19
|
findConversation,
|
|
20
20
|
findConversationBySurfaceId,
|
|
21
|
-
|
|
22
|
-
} from "../../daemon/conversation-store.js";
|
|
21
|
+
} from "../../daemon/conversation-registry.js";
|
|
22
|
+
import { getOrCreateConversation } from "../../daemon/conversation-store.js";
|
|
23
23
|
import { rawGet } from "../../memory/raw-query.js";
|
|
24
24
|
|
|
25
25
|
/**
|
|
@@ -60,6 +60,7 @@ export async function resolveSurfaceConversation(
|
|
|
60
60
|
`%"surfaceId":"${escaped}"%`,
|
|
61
61
|
);
|
|
62
62
|
if (!row) return undefined;
|
|
63
|
-
if (conversationId && conversationId !== row.conversation_id)
|
|
63
|
+
if (conversationId && conversationId !== row.conversation_id)
|
|
64
|
+
return undefined;
|
|
64
65
|
return await getOrCreateConversation(row.conversation_id);
|
|
65
66
|
}
|
|
@@ -8,10 +8,8 @@
|
|
|
8
8
|
import { z } from "zod";
|
|
9
9
|
|
|
10
10
|
import type { Conversation } from "../../daemon/conversation.js";
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
getOrCreateConversation,
|
|
14
|
-
} from "../../daemon/conversation-store.js";
|
|
11
|
+
import { findConversation } from "../../daemon/conversation-registry.js";
|
|
12
|
+
import { getOrCreateConversation } from "../../daemon/conversation-store.js";
|
|
15
13
|
import type { ServerMessage } from "../../daemon/message-protocol.js";
|
|
16
14
|
import { getMessages } from "../../memory/conversation-crud.js";
|
|
17
15
|
import { check, classifyRisk } from "../../permissions/checker.js";
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import { parseChannelId } from "../../channels/types.js";
|
|
11
11
|
import { getConfig } from "../../config/loader.js";
|
|
12
|
-
import { findConversation } from "../../daemon/conversation-
|
|
12
|
+
import { findConversation } from "../../daemon/conversation-registry.js";
|
|
13
13
|
import { normalizeConversationType } from "../../daemon/message-types/shared.js";
|
|
14
14
|
import {
|
|
15
15
|
type AttentionState,
|
package/src/signals/cancel.ts
CHANGED
|
@@ -11,10 +11,8 @@ import { readFileSync } from "node:fs";
|
|
|
11
11
|
import { join } from "node:path";
|
|
12
12
|
|
|
13
13
|
import { getIsContainerized } from "../config/env-registry.js";
|
|
14
|
-
import {
|
|
15
|
-
|
|
16
|
-
touchConversation,
|
|
17
|
-
} from "../daemon/conversation-store.js";
|
|
14
|
+
import { findConversation } from "../daemon/conversation-registry.js";
|
|
15
|
+
import { touchConversation } from "../daemon/conversation-store.js";
|
|
18
16
|
import { getSubagentManager } from "../subagent/index.js";
|
|
19
17
|
import { createAbortReason } from "../util/abort-reasons.js";
|
|
20
18
|
import { getLogger } from "../util/logger.js";
|