@vellumai/assistant 0.8.4 → 0.8.5
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 +2 -2
- package/docs/browser-use-architecture-phase2.md +1 -1
- package/knip.json +2 -1
- package/openapi.yaml +809 -11
- package/package.json +1 -1
- package/src/__tests__/anthropic-provider.test.ts +34 -37
- package/src/__tests__/assistant-event-hub-self-exclusion.test.ts +293 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +3 -3
- package/src/__tests__/audit-log-rotation.test.ts +70 -16
- package/src/__tests__/background-workers-disk-pressure.test.ts +3 -3
- package/src/__tests__/btw-routes.test.ts +2 -3
- package/src/__tests__/call-controller.test.ts +0 -1
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +1 -1
- package/src/__tests__/channel-guardian.test.ts +3 -3
- package/src/__tests__/checker.test.ts +6 -15
- package/src/__tests__/compaction-events.test.ts +1 -0
- package/src/__tests__/compactor-call-site-logging.test.ts +214 -0
- package/src/__tests__/computer-use-skill-manifest-regression.test.ts +5 -11
- package/src/__tests__/computer-use-tools.test.ts +2 -4
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +197 -2
- package/src/__tests__/conversation-agent-loop.test.ts +163 -122
- package/src/__tests__/conversation-app-control-instantiation.test.ts +2 -5
- package/src/__tests__/conversation-clear-safety.test.ts +25 -25
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +1 -1
- package/src/__tests__/conversation-disk-view-integration.test.ts +2 -2
- package/src/__tests__/conversation-error.test.ts +31 -0
- package/src/__tests__/conversation-fork-crud.test.ts +178 -15
- package/src/__tests__/conversation-lifecycle.test.ts +52 -11
- package/src/__tests__/{conversation-load-cleaned-at.test.ts → conversation-load-history-stripped.test.ts} +13 -13
- package/src/__tests__/conversation-provider-retry-repair.test.ts +1 -0
- package/src/__tests__/conversation-routes-disk-view.test.ts +109 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +35 -0
- package/src/__tests__/conversation-skill-tools.test.ts +2 -5
- package/src/__tests__/conversation-store.test.ts +1 -1
- package/src/__tests__/conversation-sync-tags.test.ts +99 -32
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -1
- package/src/__tests__/credential-execution-feature-gates.test.ts +9 -7
- package/src/__tests__/credential-execution-tools.test.ts +6 -6
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault-unit.test.ts +2 -2
- package/src/__tests__/dynamic-page-surface.test.ts +2 -2
- package/src/__tests__/email-html-renderer.test.ts +12 -0
- package/src/__tests__/gateway-flag-listener.test.ts +237 -0
- package/src/__tests__/gemini-provider.test.ts +78 -0
- package/src/__tests__/guardian-dispatch.test.ts +0 -1
- package/src/__tests__/guardian-outbound-http.test.ts +7 -5
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +1 -1
- package/src/__tests__/heartbeat-disk-pressure.test.ts +4 -0
- package/src/__tests__/heartbeat-service.test.ts +4 -0
- package/src/__tests__/host-shell-tool.test.ts +1 -1
- package/src/__tests__/init-feature-flag-overrides.test.ts +5 -6
- package/src/__tests__/list-messages-tool-merge.test.ts +70 -11
- package/src/__tests__/llm-request-log-call-site.test.ts +136 -0
- package/src/__tests__/llm-request-log-source-clickhouse.test.ts +26 -0
- package/src/__tests__/llm-resolver.test.ts +77 -9
- package/src/__tests__/llm-usage-store.test.ts +66 -0
- package/src/__tests__/logger.test.ts +89 -0
- package/src/__tests__/mcp-abort-signal.test.ts +2 -2
- package/src/__tests__/media-generate-image.test.ts +31 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +7 -7
- package/src/__tests__/model-intents.test.ts +2 -4
- package/src/__tests__/notification-guardian-path.test.ts +0 -1
- package/src/__tests__/onboarding-template-contract.test.ts +1 -1
- package/src/__tests__/openai-provider.test.ts +46 -0
- package/src/__tests__/openai-responses-provider.test.ts +114 -12
- package/src/__tests__/pending-interactions-resolved-event.test.ts +0 -1
- package/src/__tests__/platform-bash-auto-approve.test.ts +2 -2
- package/src/__tests__/platform.test.ts +2 -2
- package/src/__tests__/plugin-api-tool-definition.test.ts +92 -0
- package/src/__tests__/plugin-bootstrap.test.ts +2 -2
- package/src/__tests__/plugin-tool-contribution.test.ts +13 -6
- package/src/__tests__/plugin-types.test.ts +3 -2
- package/src/__tests__/prechat-onboarding-contract.test.ts +131 -98
- package/src/__tests__/pricing.test.ts +12 -0
- package/src/__tests__/prune-jobs-changes-parser.test.ts +61 -0
- package/src/__tests__/registry.test.ts +2 -8
- package/src/__tests__/require-fresh-approval.test.ts +2 -2
- package/src/__tests__/runtime-events-sse-bilingual.test.ts +154 -0
- package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -1
- package/src/__tests__/skill-feature-flags.test.ts +2 -2
- package/src/__tests__/skill-projection-feature-flag.test.ts +4 -7
- package/src/__tests__/skill-projection.benchmark.test.ts +2 -6
- package/src/__tests__/skill-tool-factory.test.ts +1 -1
- package/src/__tests__/subagent-notify-parent.test.ts +1 -1
- package/src/__tests__/suggestion-routes.test.ts +1 -0
- package/src/__tests__/sync-message-contract.test.ts +59 -0
- package/src/__tests__/system-prompt.test.ts +145 -131
- package/src/__tests__/terminal-tools.test.ts +1 -1
- package/src/__tests__/tool-approval-handler.test.ts +1 -5
- package/src/__tests__/tool-execute-pipeline.test.ts +2 -2
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +2 -5
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +15 -5
- package/src/__tests__/tool-executor.test.ts +9 -62
- package/src/__tests__/tool-grant-request-escalation.test.ts +1 -6
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -6
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
- package/src/__tests__/ui-file-upload-surface.test.ts +2 -2
- package/src/__tests__/usage-routes.test.ts +3 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -2
- package/src/__tests__/workspace-git-service.test.ts +6 -5
- package/src/__tests__/workspace-migration-089-move-memory-tree-out-of-v3.test.ts +86 -0
- package/src/acp/__tests__/prepare-agent-env.test.ts +146 -0
- package/src/acp/prepare-agent-env.ts +78 -0
- package/src/acp/session-manager.ts +1 -1
- package/src/agent/loop.ts +8 -0
- package/src/api/README.md +5 -0
- package/src/api/index.ts +4 -0
- package/src/api/package.json +10 -0
- package/src/background-wake/background-wake-routes.test.ts +233 -0
- package/src/background-wake/runtime-registry.ts +24 -0
- package/src/cli/commands/__tests__/browser.test.ts +23 -5
- package/src/cli/commands/__tests__/domain-register.test.ts +110 -0
- package/src/cli/commands/__tests__/domain-status.test.ts +33 -33
- package/src/cli/commands/__tests__/inference-send.test.ts +108 -5
- package/src/cli/commands/__tests__/memory-v2-compare-render.test.ts +98 -0
- package/src/cli/commands/__tests__/memory-v2.test.ts +1 -0
- package/src/cli/commands/__tests__/memory-v3-render.test.ts +340 -0
- package/src/cli/commands/browser.ts +247 -0
- package/src/cli/commands/domain.ts +91 -41
- package/src/cli/commands/inference.ts +93 -40
- package/src/cli/commands/memory-v2-compare-render.ts +115 -0
- package/src/cli/commands/memory-v2.ts +176 -1
- package/src/cli/commands/memory-v3-render.ts +344 -0
- package/src/cli/commands/memory-v3.ts +316 -0
- package/src/cli/program.ts +2 -0
- package/src/config/assistant-feature-flags.ts +21 -9
- package/src/config/bundled-skills/document-editor/SKILL.md +11 -2
- package/src/config/bundled-skills/document-editor/TOOLS.json +18 -0
- package/src/config/bundled-skills/document-editor/tools/document-open.ts +12 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +4 -0
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +2 -2
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +13 -8
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +10 -3
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +16 -14
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +7 -2
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +7 -2
- package/src/config/bundled-tool-registry.ts +2 -0
- package/src/config/call-site-defaults.ts +7 -6
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +213 -1
- package/src/config/schemas/call-site-catalog.ts +21 -7
- package/src/config/schemas/llm.ts +12 -1
- package/src/config/schemas/memory-v2.ts +246 -0
- package/src/config/schemas/memory.ts +2 -1
- package/src/context/compactor.ts +52 -0
- package/src/conversations/__tests__/message-consolidation.test.ts +350 -0
- package/src/conversations/message-consolidation.ts +404 -0
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +1 -1
- package/src/daemon/__tests__/meet-manifest-loader.test.ts +1 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -13
- package/src/daemon/conversation-agent-loop.ts +126 -76
- package/src/daemon/conversation-error.ts +31 -1
- package/src/daemon/conversation-lifecycle.ts +27 -22
- package/src/daemon/conversation-runtime-assembly.ts +10 -9
- package/src/daemon/conversation-tool-setup.ts +63 -3
- package/src/daemon/conversation-usage.ts +2 -0
- package/src/daemon/conversation.ts +14 -29
- package/src/daemon/disk-pressure-guard.ts +14 -2
- package/src/daemon/handlers/config-model.test.ts +1 -0
- package/src/daemon/handlers/conversations.ts +11 -3
- package/src/daemon/host-browser-proxy.ts +5 -5
- package/src/daemon/host-cu-proxy.ts +4 -4
- package/src/daemon/host-file-proxy.ts +4 -4
- package/src/daemon/host-proxy-base.ts +4 -4
- package/src/daemon/host-transfer-proxy.ts +10 -10
- package/src/daemon/lifecycle.ts +23 -20
- package/src/daemon/meet-manifest-loader.ts +1 -7
- package/src/daemon/message-types/conversations.ts +6 -9
- package/src/daemon/message-types/home.ts +1 -13
- package/src/daemon/message-types/messages.ts +6 -14
- package/src/daemon/message-types/sync.ts +14 -0
- package/src/daemon/shutdown-handlers.ts +24 -5
- package/src/daemon/switch-inference-profile-tool.ts +52 -0
- package/src/daemon/tool-setup-types.ts +13 -0
- package/src/events/relationship-state-updated.ts +25 -0
- package/src/heartbeat/__tests__/heartbeat-service.test.ts +1 -1
- package/src/home/home-greeting.ts +0 -9
- package/src/home/suggested-prompts.ts +0 -9
- package/src/ipc/gateway-flag-listener.ts +123 -0
- package/src/ipc/skill-routes/registries.ts +8 -12
- package/src/memory/__tests__/db-async-query.test.ts +165 -0
- package/src/memory/__tests__/db-maintenance.test.ts +115 -0
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +241 -0
- package/src/memory/__tests__/jobs-store-job-classes.test.ts +28 -1
- package/src/memory/__tests__/memory-retrospective-job.test.ts +7 -0
- package/src/memory/auto-analysis-enqueue.ts +5 -1
- package/src/memory/conversation-crud.ts +71 -70
- package/src/memory/conversation-starters-cadence.ts +3 -1
- package/src/memory/conversation-title-service.ts +19 -3
- package/src/memory/db-async-query.ts +214 -0
- package/src/memory/db-init.ts +10 -0
- package/src/memory/db-maintenance.ts +30 -21
- package/src/memory/graph/bootstrap.ts +8 -1
- package/src/memory/graph/capability-seed.ts +7 -3
- package/src/memory/graph/conversation-graph-memory.ts +100 -17
- package/src/memory/graph/extraction.ts +1 -5
- package/src/memory/graph/graph-search.ts +7 -1
- package/src/memory/indexer.ts +28 -18
- package/src/memory/job-handlers/cleanup.ts +76 -18
- package/src/memory/job-handlers/conversation-starters.ts +1 -4
- package/src/memory/jobs/embed-pkb-file.ts +6 -1
- package/src/memory/jobs-store.ts +14 -0
- package/src/memory/jobs-worker.ts +55 -22
- package/src/memory/llm-request-log-source-clickhouse.ts +42 -2
- package/src/memory/llm-request-log-source-local.ts +7 -0
- package/src/memory/llm-request-log-source.ts +9 -2
- package/src/memory/llm-request-log-store.ts +43 -1
- package/src/memory/llm-usage-store.ts +24 -0
- package/src/memory/memory-retrospective-enqueue.ts +8 -1
- package/src/memory/memory-retrospective-job.ts +5 -0
- package/src/memory/memory-v2-activation-log-store.ts +15 -6
- package/src/memory/migrations/260-rename-cleaned-at.ts +44 -0
- package/src/memory/migrations/261-llm-usage-add-raw-usage.ts +36 -0
- package/src/memory/migrations/262-memory-v3-coactivation.ts +57 -0
- package/src/memory/migrations/263-memory-v3-auto-edges.ts +50 -0
- package/src/memory/migrations/264-llm-request-log-call-site.ts +29 -0
- package/src/memory/migrations/index.ts +17 -0
- package/src/memory/migrations/registry.ts +33 -0
- package/src/memory/schema/conversations.ts +1 -1
- package/src/memory/schema/infrastructure.ts +21 -0
- package/src/memory/tool-usage-store.ts +36 -8
- package/src/memory/v2/__tests__/consolidation-job.test.ts +1 -0
- package/src/memory/v2/__tests__/harness-compare.test.ts +186 -0
- package/src/memory/v2/__tests__/harness-metrics.test.ts +74 -0
- package/src/memory/v2/__tests__/harness-oracle.test.ts +257 -0
- package/src/memory/v2/__tests__/harness-replay-input.test.ts +225 -0
- package/src/memory/v2/__tests__/harness-runner.test.ts +109 -0
- package/src/memory/v2/__tests__/injection.test.ts +127 -98
- package/src/memory/v2/__tests__/qdrant.test.ts +36 -0
- package/src/memory/v2/__tests__/router.test.ts +171 -3
- package/src/memory/v2/harness/compare.ts +57 -0
- package/src/memory/v2/harness/metrics.ts +124 -0
- package/src/memory/v2/harness/oracle.ts +145 -0
- package/src/memory/v2/harness/replay-input.ts +224 -0
- package/src/memory/v2/harness/retriever.ts +74 -0
- package/src/memory/v2/harness/router-retriever.ts +43 -0
- package/src/memory/v2/harness/runner.ts +106 -0
- package/src/memory/v2/harness/trace.ts +58 -0
- package/src/memory/v2/injection.ts +21 -15
- package/src/memory/v2/prompts/router.ts +26 -1
- package/src/memory/v2/qdrant.ts +14 -2
- package/src/memory/v2/router.ts +171 -18
- package/src/memory/v3/__tests__/coactivation-store.test.ts +422 -0
- package/src/memory/v3/__tests__/consolidation-job.test.ts +468 -0
- package/src/memory/v3/__tests__/edge-learning-job.test.ts +324 -0
- package/src/memory/v3/__tests__/edges.test.ts +563 -0
- package/src/memory/v3/__tests__/filter.test.ts +512 -0
- package/src/memory/v3/__tests__/gate.test.ts +574 -0
- package/src/memory/v3/__tests__/index-composition.test.ts +233 -0
- package/src/memory/v3/__tests__/loop.test.ts +530 -0
- package/src/memory/v3/__tests__/retriever.test.ts +226 -0
- package/src/memory/v3/__tests__/scouts.test.ts +440 -0
- package/src/memory/v3/__tests__/shadow-middleware.test.ts +312 -0
- package/src/memory/v3/__tests__/system-prompts.test.ts +154 -0
- package/src/memory/v3/__tests__/traversal.test.ts +469 -0
- package/src/memory/v3/__tests__/tree-index.test.ts +280 -0
- package/src/memory/v3/__tests__/tree-store.test.ts +529 -0
- package/src/memory/v3/__tests__/tree-walk.test.ts +707 -0
- package/src/memory/v3/__tests__/validate.test.ts +245 -0
- package/src/memory/v3/auto-edges.ts +223 -0
- package/src/memory/v3/coactivation-store.ts +124 -0
- package/src/memory/v3/consolidation-job.ts +323 -0
- package/src/memory/v3/edge-learning-job.ts +160 -0
- package/src/memory/v3/edges.ts +249 -0
- package/src/memory/v3/filter.ts +281 -0
- package/src/memory/v3/gate.ts +334 -0
- package/src/memory/v3/index-composition.ts +113 -0
- package/src/memory/v3/llm-capture.ts +46 -0
- package/src/memory/v3/loop.ts +382 -0
- package/src/memory/v3/maintenance.ts +144 -0
- package/src/memory/v3/prompt-context.ts +33 -0
- package/src/memory/v3/prompts/consolidation.ts +458 -0
- package/src/memory/v3/prompts/system-prompts.ts +196 -0
- package/src/memory/v3/retriever.ts +33 -0
- package/src/memory/v3/scouts.ts +420 -0
- package/src/memory/v3/shadow-middleware.ts +305 -0
- package/src/memory/v3/traversal.ts +206 -0
- package/src/memory/v3/tree-index.ts +237 -0
- package/src/memory/v3/tree-store.ts +394 -0
- package/src/memory/v3/tree-walk.ts +351 -0
- package/src/memory/v3/types.ts +65 -0
- package/src/memory/v3/validate.ts +300 -0
- package/src/notifications/adapters/macos.ts +18 -1
- package/src/notifications/adapters/platform.ts +1 -1
- package/src/notifications/decision-engine.ts +1 -4
- package/src/notifications/emit-signal.ts +29 -49
- package/src/permissions/prompter.ts +3 -3
- package/src/permissions/question-prompter.ts +5 -2
- package/src/permissions/secret-prompter.ts +2 -2
- package/src/plugin-api/index.ts +4 -0
- package/src/plugin-api/types.ts +7 -33
- package/src/plugins/defaults/index.ts +6 -0
- package/src/plugins/defaults/injectors.ts +18 -11
- package/src/plugins/external-plugin-loader.ts +5 -68
- package/src/plugins/types.ts +11 -16
- package/src/proactive-artifact/aux-message-injector.ts +17 -4
- package/src/prompts/__tests__/task-progress-hint-section.test.ts +3 -9
- package/src/prompts/persona-resolver.ts +36 -21
- package/src/prompts/sections.ts +39 -7
- package/src/prompts/system-prompt.ts +50 -185
- package/src/prompts/templates/BOOTSTRAP.md +2 -2
- package/src/prompts/templates/system-sections.ts +230 -8
- package/src/providers/__tests__/connection-model-compat.test.ts +234 -0
- package/src/providers/__tests__/retry-callsite.test.ts +85 -5
- package/src/providers/anthropic/client.ts +32 -66
- package/src/providers/call-site-routing.ts +14 -2
- package/src/providers/connection-model-compat.ts +38 -0
- package/src/providers/connection-resolution.ts +16 -2
- package/src/providers/gemini/client.ts +49 -6
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/minimax/client.ts +106 -0
- package/src/providers/model-catalog.ts +43 -0
- package/src/providers/model-intents.ts +1 -1
- package/src/providers/openai/chat-completions-provider.ts +6 -3
- package/src/providers/openai/codex-models.ts +18 -0
- package/src/providers/openai/responses-provider.ts +78 -21
- package/src/providers/provider-send-message.ts +7 -1
- package/src/providers/retry.ts +34 -3
- package/src/providers/thinking-config.ts +26 -1
- package/src/providers/usage-tracking.ts +2 -0
- package/src/runtime/AGENTS.md +2 -2
- package/src/runtime/agent-wake.ts +1 -0
- package/src/runtime/assistant-event-hub.ts +76 -6
- package/src/runtime/auth/route-policy.ts +36 -0
- package/src/runtime/btw-sidechain.ts +0 -6
- package/src/runtime/http-types.ts +0 -2
- package/src/runtime/migrations/vbundle-builder.ts +10 -3
- package/src/runtime/pending-interactions.ts +0 -1
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +106 -0
- package/src/runtime/routes/__tests__/memory-v2-simulate-route.test.ts +25 -6
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +512 -0
- package/src/runtime/routes/acp-routes.test.ts +255 -6
- package/src/runtime/routes/acp-routes.ts +8 -1
- package/src/runtime/routes/avatar-routes.ts +10 -10
- package/src/runtime/routes/background-wake-routes.ts +188 -0
- package/src/runtime/routes/browser-tabs-routes.ts +200 -0
- package/src/runtime/routes/btw-routes.ts +0 -6
- package/src/runtime/routes/conversation-cli-routes.ts +1 -1
- package/src/runtime/routes/conversation-list-routes.ts +12 -4
- package/src/runtime/routes/conversation-management-routes.ts +77 -20
- package/src/runtime/routes/conversation-query-routes.ts +142 -36
- package/src/runtime/routes/conversation-routes.ts +252 -410
- package/src/runtime/routes/conversation-starter-routes.ts +6 -3
- package/src/runtime/routes/disk-pressure-routes.ts +1 -1
- package/src/runtime/routes/domain-routes.ts +60 -10
- package/src/runtime/routes/email-routes.ts +5 -2
- package/src/runtime/routes/events-routes.ts +54 -10
- package/src/runtime/routes/group-routes.ts +24 -8
- package/src/runtime/routes/host-browser-routes.ts +10 -2
- package/src/runtime/routes/host-cu-routes.ts +2 -2
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +96 -3
- package/src/runtime/routes/index.ts +8 -0
- package/src/runtime/routes/inference-profile-session-handler.ts +22 -12
- package/src/runtime/routes/inference-profile-session-routes.ts +7 -1
- package/src/runtime/routes/llm-call-sites-routes.ts +32 -5
- package/src/runtime/routes/memory-item-routes.ts +8 -3
- package/src/runtime/routes/memory-v2-routes.ts +215 -5
- package/src/runtime/routes/memory-v3-routes.ts +316 -0
- package/src/runtime/routes/migration-routes.ts +21 -24
- package/src/runtime/routes/plugins-routes.ts +337 -0
- package/src/runtime/routes/rename-conversation-routes.ts +6 -2
- package/src/runtime/routes/secret-routes.ts +25 -5
- package/src/runtime/routes/settings-routes.ts +12 -11
- package/src/runtime/routes/slack-channel-routes.ts +5 -4
- package/src/runtime/routes/workspace-routes.ts +25 -10
- package/src/runtime/sync/resource-sync-events.ts +106 -38
- package/src/runtime/sync/sync-publisher.test.ts +49 -0
- package/src/runtime/sync/sync-publisher.ts +2 -1
- package/src/runtime/verification-outbound-actions.ts +73 -1
- package/src/telemetry/types.ts +12 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +48 -0
- package/src/telemetry/usage-telemetry-reporter.ts +1 -0
- package/src/tools/acp/spawn.test.ts +119 -0
- package/src/tools/acp/spawn.ts +15 -2
- package/src/tools/apps/definitions.ts +2 -8
- package/src/tools/ask-question/ask-question-tool.test.ts +3 -3
- package/src/tools/ask-question/ask-question-tool.ts +38 -45
- package/src/tools/browser/__tests__/pinned-tabs.test.ts +70 -0
- package/src/tools/browser/browser-execution.ts +16 -3
- package/src/tools/browser/cdp-client/__tests__/browser-tabs-factory.test.ts +402 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +3 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +12 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +27 -1
- package/src/tools/browser/cdp-client/factory.ts +100 -17
- package/src/tools/browser/cdp-client/local-cdp-client.ts +12 -0
- package/src/tools/browser/cdp-client/types.ts +65 -0
- package/src/tools/browser/pinned-tabs.ts +96 -40
- package/src/tools/computer-use/definitions.ts +22 -78
- package/src/tools/credential-execution/make-authenticated-request.ts +3 -9
- package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -9
- package/src/tools/credential-execution/run-authenticated-command.ts +3 -9
- package/src/tools/credentials/vault.ts +3 -9
- package/src/tools/document/document-tool.ts +59 -0
- package/src/tools/execution-target.ts +21 -23
- package/src/tools/executor.ts +6 -1
- package/src/tools/filesystem/edit.ts +3 -9
- package/src/tools/filesystem/list.ts +3 -9
- package/src/tools/filesystem/read.ts +3 -9
- package/src/tools/filesystem/write.ts +3 -9
- package/src/tools/host-filesystem/edit.ts +3 -9
- package/src/tools/host-filesystem/read.ts +3 -9
- package/src/tools/host-filesystem/transfer.ts +3 -9
- package/src/tools/host-filesystem/write.ts +3 -9
- package/src/tools/host-terminal/host-shell.ts +3 -9
- package/src/tools/mcp/mcp-tool-factory.ts +1 -8
- package/src/tools/memory/register.test.ts +1 -1
- package/src/tools/memory/register.ts +4 -9
- package/src/tools/network/web-fetch.ts +3 -9
- package/src/tools/network/web-search.ts +25 -32
- package/src/tools/registry.ts +7 -23
- package/src/tools/schema-transforms.ts +1 -1
- package/src/tools/skills/execute.ts +3 -9
- package/src/tools/skills/load.ts +3 -9
- package/src/tools/skills/skill-tool-factory.ts +1 -8
- package/src/tools/subagent/notify-parent.ts +3 -9
- package/src/tools/system/request-permission.ts +3 -9
- package/src/tools/terminal/shell.ts +3 -9
- package/src/tools/tool-defaults.ts +94 -0
- package/src/tools/types.ts +27 -98
- package/src/tools/ui-surface/definitions.ts +6 -22
- package/src/usage/pricing.ts +23 -0
- package/src/usage/types.ts +12 -0
- package/src/util/logger.ts +16 -7
- package/src/util/platform.ts +7 -2
- package/src/util/sqlite3-runtime.ts +65 -0
- package/src/workspace/migrations/086-revert-stale-gemini-mis-rewrites.ts +1 -0
- package/src/workspace/migrations/089-move-memory-tree-out-of-v3.ts +86 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/__tests__/compaction-strip-metadata-clear.test.ts +0 -206
- package/src/__tests__/message-complete-display-id.test.ts +0 -175
- package/src/daemon/query-complexity-router.ts +0 -75
- package/src/prompts/cache-boundary.ts +0 -8
|
@@ -387,17 +387,20 @@ async function buildPkbReminderWithHints(
|
|
|
387
387
|
* `memory-v2-static` injector — order 38, after-memory-prefix.
|
|
388
388
|
*
|
|
389
389
|
* Injects the v2 static memory block (essentials/threads/recent/buffer
|
|
390
|
-
* concatenated under markdown headings) wrapped in `<
|
|
390
|
+
* concatenated under markdown headings) wrapped in `<info>...</info>`
|
|
391
391
|
* onto the user message. The agent loop only forwards `memoryV2Static` on
|
|
392
392
|
* full-mode turns (first turn / post-compaction), mirroring the PKB
|
|
393
393
|
* auto-inject cadence — subsequent turns get `null` and the prior block
|
|
394
394
|
* stays cached on its original user message.
|
|
395
395
|
*
|
|
396
|
-
* Sits between `pkb-reminder` (35) and `now-md` (40)
|
|
397
|
-
* after
|
|
398
|
-
*
|
|
399
|
-
*
|
|
400
|
-
*
|
|
396
|
+
* Sits between `pkb-reminder` (35) and `now-md` (40). Because every
|
|
397
|
+
* after-memory-prefix splice lands at the memory-prefix boundary in
|
|
398
|
+
* ascending `order`, higher-order blocks end up closer to the memory
|
|
399
|
+
* prefix. The rendered layout is therefore `[<memory>dynamic</memory>,
|
|
400
|
+
* <info>memory-v2-static</info>, <NOW.md>, <system_reminder>,
|
|
401
|
+
* <knowledge_base>, ...user text]` when every PKB injector also fires.
|
|
402
|
+
* `countMemoryPrefixBlocks` treats the `<info>` static block as part of
|
|
403
|
+
* the memory prefix so `now-md` (40) splices after it.
|
|
401
404
|
*
|
|
402
405
|
* Gating:
|
|
403
406
|
* - `mode === "full"`.
|
|
@@ -420,14 +423,18 @@ const memoryV2StaticInjector: Injector = {
|
|
|
420
423
|
},
|
|
421
424
|
};
|
|
422
425
|
|
|
426
|
+
const INFO_CLOSE_TAG_RE = /<\/info\s*>/gi;
|
|
427
|
+
|
|
423
428
|
/**
|
|
424
|
-
* Wrap the static memory content in `<
|
|
425
|
-
* closing `</
|
|
426
|
-
* accidentally break out of the wrapper.
|
|
429
|
+
* Wrap the static memory content in `<info>...</info>`. Escapes any
|
|
430
|
+
* closing `</info>` inside the content so authored memory files cannot
|
|
431
|
+
* accidentally break out of the wrapper. Distinct from the dynamic
|
|
432
|
+
* activation block (which uses `<memory>...</memory>`) so downstream
|
|
433
|
+
* logic can address the two differently.
|
|
427
434
|
*/
|
|
428
435
|
function buildMemoryV2StaticBlock(content: string): string {
|
|
429
|
-
const escaped = content.replace(
|
|
430
|
-
return `<
|
|
436
|
+
const escaped = content.replace(INFO_CLOSE_TAG_RE, "</info>");
|
|
437
|
+
return `<info>\n${escaped}\n</info>`;
|
|
431
438
|
}
|
|
432
439
|
|
|
433
440
|
/**
|
|
@@ -49,12 +49,8 @@ import semver from "semver";
|
|
|
49
49
|
import { z } from "zod";
|
|
50
50
|
|
|
51
51
|
import assistantPkg from "../../package.json" with { type: "json" };
|
|
52
|
-
import
|
|
53
|
-
|
|
54
|
-
PluginTool,
|
|
55
|
-
RiskLevel,
|
|
56
|
-
ToolExecutionResult,
|
|
57
|
-
} from "../tools/types.js";
|
|
52
|
+
import { finalizeTool } from "../tools/tool-defaults.js";
|
|
53
|
+
import type { LoadedTool, ToolDefinition } from "../tools/types.js";
|
|
58
54
|
import { getLogger } from "../util/logger.js";
|
|
59
55
|
import { registerPlugin } from "./registry.js";
|
|
60
56
|
import type {
|
|
@@ -62,7 +58,6 @@ import type {
|
|
|
62
58
|
PluginHookFn,
|
|
63
59
|
PluginHooks,
|
|
64
60
|
PluginManifest,
|
|
65
|
-
PluginToolRegistration,
|
|
66
61
|
} from "./types.js";
|
|
67
62
|
|
|
68
63
|
const PLUGIN_API_PEER_DEP = "@vellumai/plugin-api";
|
|
@@ -122,64 +117,6 @@ function deriveToolName(toolFileBaseName: string): string {
|
|
|
122
117
|
return toToolNameSegment(toolFileBaseName);
|
|
123
118
|
}
|
|
124
119
|
|
|
125
|
-
/**
|
|
126
|
-
* Defaults applied by {@link applyPluginToolDefaults} when a plugin tool
|
|
127
|
-
* omits one of the normally-required fields. Exported as a constant so
|
|
128
|
-
* tests and callers can reference the same source of truth.
|
|
129
|
-
*
|
|
130
|
-
* The default `execute` returns an error result so the model sees a clear
|
|
131
|
-
* "this tool isn't wired up" signal at call time. The plugin still loads
|
|
132
|
-
* cleanly — broken individual tools must never block daemon boot.
|
|
133
|
-
*/
|
|
134
|
-
export const PLUGIN_TOOL_DEFAULTS = Object.freeze({
|
|
135
|
-
description: "",
|
|
136
|
-
defaultRiskLevel: "medium" as RiskLevel,
|
|
137
|
-
input_schema: Object.freeze({
|
|
138
|
-
type: "object",
|
|
139
|
-
properties: {},
|
|
140
|
-
additionalProperties: false,
|
|
141
|
-
}) as object,
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Fill the four normally-required {@link PluginTool} fields with documented
|
|
146
|
-
* defaults when the author omitted them. Returns a {@link LoadedPluginTool}
|
|
147
|
-
* that is safe to register.
|
|
148
|
-
*/
|
|
149
|
-
function applyPluginToolDefaults(
|
|
150
|
-
tool: PluginTool,
|
|
151
|
-
name: string,
|
|
152
|
-
): LoadedPluginTool {
|
|
153
|
-
const description =
|
|
154
|
-
typeof tool.description === "string"
|
|
155
|
-
? tool.description
|
|
156
|
-
: PLUGIN_TOOL_DEFAULTS.description;
|
|
157
|
-
const defaultRiskLevel =
|
|
158
|
-
typeof tool.defaultRiskLevel === "string"
|
|
159
|
-
? tool.defaultRiskLevel
|
|
160
|
-
: PLUGIN_TOOL_DEFAULTS.defaultRiskLevel;
|
|
161
|
-
const input_schema =
|
|
162
|
-
tool.input_schema !== null &&
|
|
163
|
-
typeof tool.input_schema === "object"
|
|
164
|
-
? tool.input_schema
|
|
165
|
-
: PLUGIN_TOOL_DEFAULTS.input_schema;
|
|
166
|
-
const execute =
|
|
167
|
-
typeof tool.execute === "function"
|
|
168
|
-
? tool.execute
|
|
169
|
-
: async (): Promise<ToolExecutionResult> => ({
|
|
170
|
-
content: `plugin tool ${name} has no execute implementation`,
|
|
171
|
-
isError: true,
|
|
172
|
-
});
|
|
173
|
-
return {
|
|
174
|
-
...tool,
|
|
175
|
-
name,
|
|
176
|
-
description,
|
|
177
|
-
defaultRiskLevel,
|
|
178
|
-
input_schema,
|
|
179
|
-
execute,
|
|
180
|
-
};
|
|
181
|
-
}
|
|
182
|
-
|
|
183
120
|
/**
|
|
184
121
|
* Dynamic-import `absolutePath` and return its default export. Throws when
|
|
185
122
|
* the module has no default export — callers attribute the error.
|
|
@@ -339,17 +276,17 @@ async function buildPluginFromDir(pluginDir: string): Promise<Plugin> {
|
|
|
339
276
|
const hooks = await loadHooks(pluginDir, name);
|
|
340
277
|
if (hooks !== undefined) plugin.hooks = hooks;
|
|
341
278
|
|
|
342
|
-
const tools:
|
|
279
|
+
const tools: LoadedTool[] = [];
|
|
343
280
|
for (const { name: toolName, path: toolPath } of listSurfaceDir(
|
|
344
281
|
join(pluginDir, "tools"),
|
|
345
282
|
)) {
|
|
346
|
-
const tool = await importDefault<
|
|
283
|
+
const tool = await importDefault<ToolDefinition>(toolPath);
|
|
347
284
|
if (tool === null || typeof tool !== "object") {
|
|
348
285
|
throw new Error(
|
|
349
286
|
`external plugin ${name}: ${toolPath} default export must be an object`,
|
|
350
287
|
);
|
|
351
288
|
}
|
|
352
|
-
tools.push(
|
|
289
|
+
tools.push(finalizeTool(tool, deriveToolName(toolName)));
|
|
353
290
|
}
|
|
354
291
|
if (tools.length > 0) plugin.tools = tools;
|
|
355
292
|
|
package/src/plugins/types.ts
CHANGED
|
@@ -43,7 +43,7 @@ import type {
|
|
|
43
43
|
} from "../providers/types.js";
|
|
44
44
|
import type { SkillRoute } from "../runtime/skill-route-registry.js";
|
|
45
45
|
import type {
|
|
46
|
-
|
|
46
|
+
LoadedTool,
|
|
47
47
|
ToolContext,
|
|
48
48
|
ToolExecutionResult,
|
|
49
49
|
} from "../tools/types.js";
|
|
@@ -1007,19 +1007,6 @@ export interface Injector {
|
|
|
1007
1007
|
// `PluginSkillRegistration` shape below so plugins can declare
|
|
1008
1008
|
// catalog-discoverable skills today.
|
|
1009
1009
|
|
|
1010
|
-
/**
|
|
1011
|
-
* Tool registration contributed by a plugin. Uses the narrow
|
|
1012
|
-
* {@link LoadedPluginTool} shape. External plugin authors declare the
|
|
1013
|
-
* nameless `PluginTool` file shape; the loader derives `name` from the
|
|
1014
|
-
* `tools/<name>.ts` basename before storing it on `plugin.tools`. Authors
|
|
1015
|
-
* also leave category / ownership metadata to the bootstrap, which stamps
|
|
1016
|
-
* `category: "plugin"`, `origin: "plugin"`, and
|
|
1017
|
-
* `ownerPluginId: <plugin.name>` before handing the batch to
|
|
1018
|
-
* `registerPluginTools`. The registration boundary synthesizes
|
|
1019
|
-
* `getDefinition()` from `{name, description, input_schema}` so the canonical
|
|
1020
|
-
* {@link Tool} interface used by the internal registry stays unchanged.
|
|
1021
|
-
*/
|
|
1022
|
-
export type PluginToolRegistration = LoadedPluginTool;
|
|
1023
1010
|
/**
|
|
1024
1011
|
* HTTP route registration contributed by a plugin. Plugins express routes as
|
|
1025
1012
|
* {@link SkillRoute} values — the same shape the skill-route registry
|
|
@@ -1120,8 +1107,16 @@ export interface Plugin {
|
|
|
1120
1107
|
manifest: PluginManifest;
|
|
1121
1108
|
/** Lifecycle hooks (init, shutdown). See {@link PluginHooks}. */
|
|
1122
1109
|
hooks?: PluginHooks;
|
|
1123
|
-
/**
|
|
1124
|
-
|
|
1110
|
+
/**
|
|
1111
|
+
* Tool registrations visible to the model. External plugin authors
|
|
1112
|
+
* declare the nameless `ToolDefinition` file shape (from
|
|
1113
|
+
* `@vellumai/plugin-api`); the loader derives `name` from the
|
|
1114
|
+
* `tools/<name>.ts` basename and runs the definition through
|
|
1115
|
+
* `finalizeTool` to fill omitted required fields, producing the
|
|
1116
|
+
* `LoadedTool` values stored here. Category / ownership metadata is
|
|
1117
|
+
* stamped by `registerPluginTools` at registration time.
|
|
1118
|
+
*/
|
|
1119
|
+
tools?: LoadedTool[];
|
|
1125
1120
|
/** HTTP route registrations served by the assistant. */
|
|
1126
1121
|
routes?: PluginRouteRegistration[];
|
|
1127
1122
|
/** Skill registrations loaded at startup. */
|
|
@@ -71,10 +71,23 @@ export async function injectAuxAssistantMessage(params: {
|
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
// The injected aux message bumps `lastMessageAt`, which can move the
|
|
75
|
+
// row in the paginated sidebar window — that's a shape change. macOS
|
|
76
|
+
// refreshes its full list on `conversation_list_invalidated`; web
|
|
77
|
+
// refreshes via the paired `sync_changed` `conversationsList` tag.
|
|
78
|
+
//
|
|
79
|
+
// TODO(electron-cutover): drop the `conversation_list_invalidated`
|
|
80
|
+
// emission once macOS migrates to the Electron client and consumes
|
|
81
|
+
// `sync_changed` directly. See `runtime/sync/resource-sync-events.ts`
|
|
82
|
+
// for the symmetric helper used by route handlers.
|
|
83
|
+
params.broadcastMessage(
|
|
84
|
+
{
|
|
85
|
+
type: "conversation_list_invalidated",
|
|
86
|
+
reason: "reordered",
|
|
87
|
+
},
|
|
88
|
+
undefined,
|
|
89
|
+
{ targetInterfaceId: "macos" },
|
|
90
|
+
);
|
|
78
91
|
params.broadcastMessage({
|
|
79
92
|
type: "sync_changed",
|
|
80
93
|
tags: [
|
|
@@ -54,8 +54,9 @@ mock.module("../../config/loader.js", () => ({
|
|
|
54
54
|
setNestedValue: () => {},
|
|
55
55
|
}));
|
|
56
56
|
|
|
57
|
-
const { buildSystemPrompt, ensurePromptFiles
|
|
58
|
-
|
|
57
|
+
const { buildSystemPrompt, ensurePromptFiles } = await import(
|
|
58
|
+
"../system-prompt.js"
|
|
59
|
+
);
|
|
59
60
|
|
|
60
61
|
describe("task_progress hint in parallel-tool-calls section", () => {
|
|
61
62
|
beforeEach(() => {
|
|
@@ -85,11 +86,4 @@ describe("task_progress hint in parallel-tool-calls section", () => {
|
|
|
85
86
|
expect(withExcludePrefix).toContain("task_progress");
|
|
86
87
|
});
|
|
87
88
|
|
|
88
|
-
test("hint lives in the static (cached) block before SYSTEM_PROMPT_CACHE_BOUNDARY", () => {
|
|
89
|
-
const result = buildSystemPrompt();
|
|
90
|
-
const boundaryIdx = result.indexOf(SYSTEM_PROMPT_CACHE_BOUNDARY);
|
|
91
|
-
expect(boundaryIdx).toBeGreaterThan(-1);
|
|
92
|
-
const staticBlock = result.slice(0, boundaryIdx);
|
|
93
|
-
expect(staticBlock).toContain("task_progress");
|
|
94
|
-
});
|
|
95
89
|
});
|
|
@@ -77,31 +77,46 @@ function resolveUserFilename(
|
|
|
77
77
|
): string | null {
|
|
78
78
|
let filename: string | null = null;
|
|
79
79
|
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
filename = guardian.contact.userFile ?? "guardian.md";
|
|
87
|
-
}
|
|
88
|
-
} else if (trustContext.requesterExternalUserId) {
|
|
89
|
-
// Channel-routed request — look up contact by channel identity
|
|
90
|
-
const contactWithChannels = findContactByChannelExternalId(
|
|
91
|
-
trustContext.sourceChannel,
|
|
92
|
-
trustContext.requesterExternalUserId,
|
|
93
|
-
);
|
|
94
|
-
if (contactWithChannels) {
|
|
95
|
-
filename = contactWithChannels.userFile ?? null;
|
|
96
|
-
} else if (trustContext.trustClass === "guardian") {
|
|
97
|
-
// Managed desktop: the JWT principal ID used as requesterExternalUserId
|
|
98
|
-
// may differ from the contact channel's external_user_id (they are
|
|
99
|
-
// separate identity concepts). Fall back to the channel-type guardian.
|
|
100
|
-
const guardian = findGuardianForChannel(trustContext.sourceChannel);
|
|
80
|
+
try {
|
|
81
|
+
if (trustContext === undefined) {
|
|
82
|
+
// Desktop / native (no gateway) — resolve via guardian contact,
|
|
83
|
+
// preferring the vellum-channel guardian when multiple exist.
|
|
84
|
+
const vellumGuardian = findGuardianForChannel("vellum");
|
|
85
|
+
const guardian = vellumGuardian ?? listGuardianChannels();
|
|
101
86
|
if (guardian) {
|
|
102
87
|
filename = guardian.contact.userFile ?? "guardian.md";
|
|
103
88
|
}
|
|
89
|
+
} else if (trustContext.requesterExternalUserId) {
|
|
90
|
+
// Channel-routed request — look up contact by channel identity
|
|
91
|
+
const contactWithChannels = findContactByChannelExternalId(
|
|
92
|
+
trustContext.sourceChannel,
|
|
93
|
+
trustContext.requesterExternalUserId,
|
|
94
|
+
);
|
|
95
|
+
if (contactWithChannels) {
|
|
96
|
+
filename = contactWithChannels.userFile ?? null;
|
|
97
|
+
} else if (trustContext.trustClass === "guardian") {
|
|
98
|
+
// Managed desktop: the JWT principal ID used as requesterExternalUserId
|
|
99
|
+
// may differ from the contact channel's external_user_id (they are
|
|
100
|
+
// separate identity concepts). Fall back to the channel-type guardian.
|
|
101
|
+
const guardian = findGuardianForChannel(trustContext.sourceChannel);
|
|
102
|
+
if (guardian) {
|
|
103
|
+
filename = guardian.contact.userFile ?? "guardian.md";
|
|
104
|
+
}
|
|
105
|
+
}
|
|
104
106
|
}
|
|
107
|
+
} catch (err) {
|
|
108
|
+
// Contacts table may be absent — happens during early bootstrap
|
|
109
|
+
// before migrations run, in CLI smoke commands that don't touch
|
|
110
|
+
// the DB, and in tests that build the system prompt without first
|
|
111
|
+
// initializing a schema. Treat the same as "no guardian found"
|
|
112
|
+
// so callers fall back to `users/default.md`. Mirrors the same
|
|
113
|
+
// try/catch pattern used by `renderConnectedServices()` around
|
|
114
|
+
// `listConnections()`.
|
|
115
|
+
log.debug(
|
|
116
|
+
{ err: err instanceof Error ? err.message : String(err) },
|
|
117
|
+
"Contacts lookup failed during persona resolution; treating as no guardian",
|
|
118
|
+
);
|
|
119
|
+
return null;
|
|
105
120
|
}
|
|
106
121
|
|
|
107
122
|
// Validate basename to prevent path traversal
|
package/src/prompts/sections.ts
CHANGED
|
@@ -110,6 +110,7 @@ interface ResolvedSection {
|
|
|
110
110
|
|
|
111
111
|
function resolveSection(
|
|
112
112
|
id: string,
|
|
113
|
+
ctx: SectionRenderContext,
|
|
113
114
|
workspaceDir: string,
|
|
114
115
|
): ResolvedSection | null {
|
|
115
116
|
const workspacePath = join(workspaceDir, `${id}.md`);
|
|
@@ -139,15 +140,28 @@ function resolveSection(
|
|
|
139
140
|
|
|
140
141
|
// A bundled section may delegate its body to a workspace file outside
|
|
141
142
|
// the section override directory (e.g. `SOUL.md` at the workspace
|
|
142
|
-
// root).
|
|
143
|
-
//
|
|
144
|
-
//
|
|
143
|
+
// root). `workspacePath` may be a single path or an array of paths
|
|
144
|
+
// tried in order — the first one whose file exists and has non-empty
|
|
145
|
+
// content wins. Each entry may reference `{{ctx-key}}` variables
|
|
146
|
+
// (e.g. `users/{{userSlug}}.md`) that are interpolated against the
|
|
147
|
+
// render context before resolution. Missing/empty files yield "",
|
|
148
|
+
// which `renderSection` then gates off via its empty-body check (or
|
|
149
|
+
// via the section's `transform`, if set).
|
|
145
150
|
if (bundled.workspacePath) {
|
|
146
|
-
const
|
|
151
|
+
const paths = Array.isArray(bundled.workspacePath)
|
|
152
|
+
? bundled.workspacePath
|
|
153
|
+
: [bundled.workspacePath];
|
|
147
154
|
let body = "";
|
|
148
|
-
|
|
155
|
+
for (const pathTemplate of paths) {
|
|
156
|
+
const interpolated = interpolateWorkspacePath(pathTemplate, ctx);
|
|
157
|
+
const filePath = getWorkspacePromptPath(interpolated);
|
|
158
|
+
if (!existsSync(filePath)) continue;
|
|
149
159
|
try {
|
|
150
|
-
|
|
160
|
+
const content = readFileSync(filePath, "utf-8");
|
|
161
|
+
if (content.trim().length > 0) {
|
|
162
|
+
body = content;
|
|
163
|
+
break;
|
|
164
|
+
}
|
|
151
165
|
} catch (err) {
|
|
152
166
|
log.warn({ err, filePath, id }, "Failed to read section workspacePath");
|
|
153
167
|
}
|
|
@@ -162,12 +176,30 @@ function resolveSection(
|
|
|
162
176
|
};
|
|
163
177
|
}
|
|
164
178
|
|
|
179
|
+
/**
|
|
180
|
+
* Interpolate `{{key}}` references in a workspace-path template against
|
|
181
|
+
* `ctx`. Section / inverted-section tags are not supported in paths —
|
|
182
|
+
* only flat variable substitution. Unresolved keys stay literal so a
|
|
183
|
+
* typo surfaces as a missing file rather than silently rendering an
|
|
184
|
+
* unrelated section.
|
|
185
|
+
*/
|
|
186
|
+
function interpolateWorkspacePath(
|
|
187
|
+
template: string,
|
|
188
|
+
ctx: SectionRenderContext,
|
|
189
|
+
): string {
|
|
190
|
+
return template.replace(VARIABLE, (match, key: string) => {
|
|
191
|
+
const value = ctx[key];
|
|
192
|
+
if (value === undefined || value === null) return match;
|
|
193
|
+
return String(value);
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
165
197
|
function renderSection(
|
|
166
198
|
id: string,
|
|
167
199
|
ctx: SectionRenderContext,
|
|
168
200
|
workspaceDir: string,
|
|
169
201
|
): string | null {
|
|
170
|
-
const section = resolveSection(id, workspaceDir);
|
|
202
|
+
const section = resolveSection(id, ctx, workspaceDir);
|
|
171
203
|
if (section === null) return null;
|
|
172
204
|
|
|
173
205
|
if (!isEnabled(section.enabled, ctx)) return null;
|