@vellumai/assistant 0.10.1 → 0.10.2-dev.202606241651.2d2b40d
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/docs/workspace-tools.md +42 -33
- package/eslint-rules/cli-no-daemon-internals.js +6 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-delivery-contract.test.ts +91 -0
- package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +31 -0
- package/node_modules/@vellumai/gateway-client/src/guardian-delivery-contract.ts +48 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +14 -0
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +17 -0
- package/openapi.yaml +74 -1
- package/package.json +1 -1
- package/scripts/test.sh +36 -15
- package/src/__tests__/actor-token-service.test.ts +36 -14
- package/src/__tests__/agent-loop-override-profile.test.ts +1 -0
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +2 -0
- package/src/__tests__/agent-wake-override-profile.test.ts +2 -0
- package/src/__tests__/annotate-activity-metadata.test.ts +2 -0
- package/src/__tests__/annotate-risk-options.test.ts +2 -0
- package/src/__tests__/approval-cascade.test.ts +2 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +2 -0
- package/src/__tests__/btw-routes.test.ts +2 -0
- package/src/__tests__/build-persisted-content.test.ts +2 -0
- package/src/__tests__/call-controller.test.ts +19 -0
- package/src/__tests__/channel-guardian.test.ts +94 -58
- package/src/__tests__/channel-reply-delivery.test.ts +2 -0
- package/src/__tests__/compaction-events.test.ts +2 -0
- package/src/__tests__/compaction.benchmark.test.ts +2 -0
- package/src/__tests__/compactor-call-site-logging.test.ts +2 -0
- package/src/__tests__/compactor-low-watermark-cut.test.ts +2 -0
- package/src/__tests__/compactor-preserved-tail-count.test.ts +2 -0
- package/src/__tests__/compactor-summary-call-truncation.test.ts +2 -0
- package/src/__tests__/compactor-web-search-strip.test.ts +2 -0
- package/src/__tests__/computer-use-tools.test.ts +13 -0
- package/src/__tests__/config-loader-backfill.test.ts +5 -1
- package/src/__tests__/config-schema.test.ts +1 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +31 -29
- package/src/__tests__/contacts-relay-reads.test.ts +13 -15
- package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop.test.ts +7 -0
- package/src/__tests__/conversation-analysis-routes.test.ts +2 -0
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +2 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -0
- package/src/__tests__/conversation-history-web-search.test.ts +2 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +2 -0
- package/src/__tests__/conversation-load-history-stripped.test.ts +2 -0
- package/src/__tests__/conversation-pairing.test.ts +2 -0
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +2 -0
- package/src/__tests__/conversation-process-callsite.test.ts +2 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
- package/src/__tests__/conversation-queue.test.ts +91 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +14 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +14 -0
- package/src/__tests__/conversation-slash-queue.test.ts +2 -0
- package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
- package/src/__tests__/conversation-speed-override.test.ts +2 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +65 -0
- package/src/__tests__/conversation-title-service.test.ts +2 -0
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +47 -0
- package/src/__tests__/conversation-usage.test.ts +2 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
- package/src/__tests__/credential-security-invariants.test.ts +0 -1
- package/src/__tests__/db-migration-rollback.test.ts +205 -171
- package/src/__tests__/db-test-helpers.ts +5 -4
- package/src/__tests__/deterministic-verification-control-plane.test.ts +4 -2
- package/src/__tests__/disk-pressure-guard.test.ts +41 -0
- package/src/__tests__/dm-persistence.test.ts +2 -0
- package/src/__tests__/emit-signal-routing-intent.test.ts +10 -5
- package/src/__tests__/events-dev-bypass-actor.test.ts +7 -1
- package/src/__tests__/filing-service.test.ts +2 -0
- package/src/__tests__/guardian-binding-drift-heal.test.ts +75 -10
- package/src/__tests__/guardian-dispatch.test.ts +95 -1
- package/src/__tests__/guardian-outbound-http.test.ts +13 -0
- package/src/__tests__/heartbeat-disk-pressure.test.ts +2 -0
- package/src/__tests__/heartbeat-service.test.ts +2 -0
- package/src/__tests__/helpers/channel-test-adapter.ts +1 -7
- package/src/__tests__/host-app-control-routes.test.ts +24 -30
- package/src/__tests__/host-bash-routes.test.ts +31 -41
- package/src/__tests__/host-browser-routes.test.ts +26 -32
- package/src/__tests__/host-cu-proxy.test.ts +299 -0
- package/src/__tests__/host-cu-routes-targeted.test.ts +25 -33
- package/src/__tests__/host-file-routes-targeted.test.ts +40 -52
- package/src/__tests__/host-transfer-routes-targeted.test.ts +31 -43
- package/src/__tests__/http-user-message-parity.test.ts +167 -8
- package/src/__tests__/inbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/invite-redemption-service.test.ts +43 -0
- package/src/__tests__/llm-context-normalization.test.ts +105 -0
- package/src/__tests__/llm-usage-store.test.ts +25 -0
- package/src/__tests__/media-stream-server-integration.test.ts +127 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +2 -0
- package/src/__tests__/messaging-send-tool.test.ts +2 -0
- package/src/__tests__/migration-import-from-url.test.ts +2 -2
- package/src/__tests__/native-web-search.test.ts +2 -0
- package/src/__tests__/non-member-access-request.test.ts +189 -17
- package/src/__tests__/notification-broadcaster.test.ts +4 -0
- package/src/__tests__/notification-decision-recipient-context.test.ts +33 -32
- package/src/__tests__/notification-deep-link.test.ts +6 -0
- package/src/__tests__/notification-guardian-path.test.ts +19 -0
- package/src/__tests__/outbound-slack-persistence.test.ts +2 -0
- package/src/__tests__/pending-interactions-resolved-event.test.ts +7 -4
- package/src/__tests__/persistence-secret-redaction.test.ts +2 -0
- package/src/__tests__/plugin-bootstrap.test.ts +3 -73
- package/src/__tests__/plugin-route-contribution.test.ts +4 -17
- package/src/__tests__/plugin-tool-contribution.test.ts +3 -18
- package/src/__tests__/plugin-types.test.ts +0 -2
- package/src/__tests__/process-message-background-slack.test.ts +2 -0
- package/src/__tests__/process-message-display-content.test.ts +2 -0
- package/src/__tests__/provider-usage-tracking.test.ts +39 -0
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +2 -0
- package/src/__tests__/registry.test.ts +3 -0
- package/src/__tests__/relay-server.test.ts +694 -25
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/secret-ingress-http.test.ts +14 -0
- package/src/__tests__/send-endpoint-busy.test.ts +30 -8
- package/src/__tests__/skills.test.ts +44 -0
- package/src/__tests__/slack-inbound-verification.test.ts +47 -2
- package/src/__tests__/sse-actor-principal-guardian-source.test.ts +102 -0
- package/src/__tests__/steer-on-enqueue-question.test.ts +181 -0
- package/src/__tests__/stt-hints.test.ts +44 -13
- package/src/__tests__/subagent-detail.test.ts +27 -0
- package/src/__tests__/subagent-disposal.test.ts +65 -0
- package/src/__tests__/subagent-notify-parent.test.ts +2 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +2 -0
- package/src/__tests__/subagent-tools.test.ts +2 -0
- package/src/__tests__/suggestion-routes.test.ts +2 -0
- package/src/__tests__/title-generate-hook.test.ts +2 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +2 -0
- package/src/__tests__/tool-executor.test.ts +16 -11
- package/src/__tests__/tool-preview-lifecycle.test.ts +2 -0
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +2 -0
- package/src/__tests__/tool-start-timestamp.test.ts +2 -0
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +10 -10
- package/src/__tests__/twilio-routes.test.ts +96 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +2 -0
- package/src/__tests__/web-search-backend-failure.test.ts +2 -0
- package/src/__tests__/workspace-tool-loader.test.ts +195 -2
- package/src/agent/loop-exclusive-tool.test.ts +150 -0
- package/src/agent/loop.ts +56 -0
- package/src/api/constants/sse-replay.ts +41 -0
- package/src/api/index.ts +6 -0
- package/src/api/responses/llm-request-log-entry.ts +25 -0
- package/src/api/responses/subagent-detail.ts +17 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +262 -4
- package/src/calls/call-domain.ts +3 -3
- package/src/calls/guardian-dispatch.ts +10 -8
- package/src/calls/inbound-trust-reader.ts +17 -1
- package/src/calls/media-stream-server.ts +21 -0
- package/src/calls/relay-server.ts +167 -50
- package/src/calls/relay-setup-router.ts +37 -7
- package/src/calls/relay-verification.ts +4 -4
- package/src/calls/stt-hints.ts +9 -12
- package/src/calls/twilio-routes.ts +14 -4
- package/src/cli/commands/__tests__/cache.test.ts +8 -1
- package/src/cli/commands/cache.ts +194 -181
- package/src/cli/commands/db/__tests__/repair.test.ts +6 -5
- package/src/cli/commands/db/status.ts +37 -1
- package/src/cli/commands/mcp.ts +252 -218
- package/src/cli/commands/memory/__tests__/worker.test.ts +302 -0
- package/src/cli/commands/memory/index.ts +2 -0
- package/src/cli/commands/memory/worker.ts +175 -0
- package/src/cli/commands/plugins.ts +75 -3
- package/src/cli/lib/__tests__/install-from-github.test.ts +102 -0
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +160 -1
- package/src/cli/lib/list-installed-plugins.ts +179 -1
- package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +143 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +6 -1
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +27 -17
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +13 -3
- package/src/config/feature-flag-registry.json +0 -8
- package/src/config/loader.ts +36 -5
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/memory-lifecycle.ts +12 -0
- package/src/config/schemas/memory-v3.ts +7 -0
- package/src/config/schemas/memory.ts +4 -0
- package/src/config/schemas/timeouts.ts +8 -0
- package/src/config/seed-inference-profiles.ts +14 -5
- package/src/config/skills.ts +27 -5
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +312 -0
- package/src/contacts/contacts-write.ts +3 -0
- package/src/contacts/guardian-delivery-reader.ts +223 -0
- package/src/daemon/conversation-agent-loop.ts +9 -0
- package/src/daemon/conversation-process.ts +39 -17
- package/src/daemon/conversation-surfaces.ts +8 -0
- package/src/daemon/conversation-tool-setup.ts +49 -16
- package/src/daemon/conversation.ts +21 -2
- package/src/daemon/disk-pressure-guard.ts +12 -2
- package/src/daemon/event-loop-watchdog.ts +28 -1
- package/src/daemon/external-plugins-bootstrap.ts +4 -34
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +25 -0
- package/src/daemon/handlers/__tests__/config-channels.test.ts +225 -0
- package/src/daemon/handlers/config-a2a.ts +6 -14
- package/src/daemon/handlers/config-channels.ts +78 -22
- package/src/daemon/handlers/conversations.ts +77 -0
- package/src/daemon/host-cu-proxy.ts +102 -11
- package/src/daemon/lifecycle.ts +4 -0
- package/src/daemon/memory-v2-startup.test.ts +72 -0
- package/src/daemon/memory-v2-startup.ts +87 -19
- package/src/daemon/server.ts +0 -4
- package/src/daemon/shutdown-handlers.ts +20 -0
- package/src/daemon/tool-setup-types.ts +9 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +1 -1
- package/src/ipc/assistant-server.ts +2 -2
- package/src/memory/__tests__/301-create-watchdog-events.test.ts +110 -0
- package/src/memory/__tests__/memory-retrospective-job.test.ts +8 -0
- package/src/memory/__tests__/prompt-override.test.ts +192 -0
- package/src/memory/__tests__/watchdog-events-store.test.ts +161 -0
- package/src/memory/conversation-crud.ts +38 -0
- package/src/memory/db-connection.ts +22 -3
- package/src/memory/db-init.ts +36 -502
- package/src/memory/db-singleton.ts +6 -4
- package/src/memory/jobs-worker.ts +58 -0
- package/src/memory/llm-usage-store.ts +48 -20
- package/src/memory/memory-retrospective-job.ts +9 -8
- package/src/memory/migrations/014-backfill-inbox-thread-state.ts +13 -3
- package/src/memory/migrations/136-drop-assistant-id-columns.ts +52 -27
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +130 -56
- package/src/memory/migrations/300-add-processing-started-at.ts +30 -0
- package/src/memory/migrations/301-create-watchdog-events.ts +45 -0
- package/src/memory/migrations/__tests__/014-backfill-inbox-thread-state.test.ts +108 -0
- package/src/memory/migrations/__tests__/136-drop-assistant-id-columns.test.ts +82 -0
- package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +224 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
- package/src/memory/migrations/run-migrations.ts +90 -6
- package/src/memory/migrations/schema-introspection.ts +14 -0
- package/src/memory/migrations/validate-migration-state.ts +101 -66
- package/src/memory/prompt-override.ts +129 -0
- package/src/memory/schema/conversations.ts +9 -0
- package/src/memory/schema/infrastructure.ts +20 -0
- package/src/memory/steps.ts +573 -0
- package/src/memory/v2/__tests__/cli-command-store.test.ts +25 -0
- package/src/memory/v2/__tests__/skill-store.test.ts +80 -0
- package/src/memory/v2/cli-command-store.ts +75 -38
- package/src/memory/v2/prompts/consolidation.ts +13 -82
- package/src/memory/v2/prompts/router.ts +21 -93
- package/src/memory/v2/skill-store.ts +68 -31
- package/src/memory/watchdog-events-store.ts +87 -0
- package/src/memory/worker-control.ts +118 -0
- package/src/memory/worker-process.ts +72 -0
- package/src/notifications/__tests__/broadcaster.test.ts +16 -8
- package/src/notifications/__tests__/connected-channels.test.ts +114 -0
- package/src/notifications/__tests__/decision-engine.test.ts +78 -9
- package/src/notifications/__tests__/destination-resolver.test.ts +256 -0
- package/src/notifications/broadcaster.ts +8 -1
- package/src/notifications/decision-engine.ts +15 -7
- package/src/notifications/destination-resolver.ts +68 -24
- package/src/notifications/emit-signal.ts +39 -14
- package/src/onboarding/checkin-event.test.ts +220 -0
- package/src/onboarding/checkin-event.ts +321 -0
- package/src/onboarding/schedule-checkin.ts +190 -0
- package/src/permissions/question-prompter.test.ts +1 -1
- package/src/permissions/question-prompter.ts +7 -4
- package/src/plugin-api/index.ts +6 -6
- package/src/plugin-api/types.ts +3 -5
- package/src/plugin-api/vision-support.test.ts +28 -4
- package/src/plugin-api/vision-support.ts +66 -31
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +161 -0
- package/src/plugins/defaults/advisor/__tests__/context-pack-gating.test.ts +106 -0
- package/src/plugins/defaults/advisor/__tests__/context-pack.test.ts +60 -0
- package/src/plugins/defaults/advisor/consult.ts +110 -6
- package/src/plugins/defaults/advisor/context-pack.ts +288 -0
- package/src/plugins/defaults/advisor/steering.ts +14 -2
- package/src/plugins/defaults/advisor/tools/advisor.ts +32 -5
- package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +47 -7
- package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +10 -11
- package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +12 -20
- package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +42 -11
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +11 -2
- package/src/plugins/defaults/memory-v3-shadow/pool-select.test.ts +146 -0
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +29 -1
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +8 -1
- package/src/plugins/mtime-cache.ts +7 -2
- package/src/plugins/types.ts +0 -2
- package/src/providers/anthropic/client.ts +5 -0
- package/src/providers/call-site-routing.ts +4 -0
- package/src/providers/model-catalog.ts +16 -0
- package/src/providers/openai/responses-provider.ts +5 -0
- package/src/providers/openrouter/client.ts +5 -0
- package/src/providers/provider-send-message.ts +4 -0
- package/src/providers/ratelimit.ts +4 -0
- package/src/providers/retry.ts +4 -0
- package/src/providers/types.ts +9 -0
- package/src/providers/usage-tracking.ts +4 -0
- package/src/runtime/__tests__/channel-verification-service.test.ts +133 -0
- package/src/runtime/__tests__/guardian-vellum-migration.test.ts +181 -0
- package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +66 -0
- package/src/runtime/__tests__/local-principal-trust.test.ts +164 -0
- package/src/runtime/__tests__/trust-verdict-consumer.test.ts +335 -3
- package/src/runtime/access-request-helper.ts +19 -39
- package/src/runtime/actor-trust-resolver.ts +2 -2
- package/src/runtime/anchored-guardian.test.ts +156 -0
- package/src/runtime/anchored-guardian.ts +135 -0
- package/src/runtime/assistant-event-hub.ts +1 -1
- package/src/runtime/assistant-stream-state.ts +9 -2
- package/src/runtime/auth/__tests__/require-bound-guardian.test.ts +99 -0
- package/src/runtime/auth/require-bound-guardian.ts +21 -11
- package/src/runtime/channel-verification-service.ts +56 -31
- package/src/runtime/confirmation-request-guardian-bridge.ts +3 -3
- package/src/runtime/guardian-vellum-migration.ts +66 -7
- package/src/runtime/invite-redemption-service.ts +50 -18
- package/src/runtime/local-actor-identity.ts +76 -11
- package/src/runtime/local-principal-trust.ts +52 -0
- package/src/runtime/pending-interactions.ts +11 -1
- package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +56 -5
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/contact-routes.test.ts +212 -0
- package/src/runtime/routes/__tests__/global-search-routes.test.ts +93 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +215 -1
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/channel-verification-routes.ts +3 -3
- package/src/runtime/routes/contact-routes.ts +8 -32
- package/src/runtime/routes/conversation-cli-routes.ts +4 -5
- package/src/runtime/routes/conversation-list-routes.ts +4 -7
- package/src/runtime/routes/conversation-routes.ts +74 -81
- package/src/runtime/routes/events-routes.ts +2 -2
- package/src/runtime/routes/global-search-routes.ts +3 -1
- package/src/runtime/routes/guardian-action-routes.ts +4 -5
- package/src/runtime/routes/host-app-control-routes.ts +5 -4
- package/src/runtime/routes/host-bash-routes.ts +5 -4
- package/src/runtime/routes/host-browser-routes.ts +9 -11
- package/src/runtime/routes/host-cu-routes.ts +5 -4
- package/src/runtime/routes/host-file-routes.ts +5 -4
- package/src/runtime/routes/host-transfer-routes.ts +6 -6
- package/src/runtime/routes/http-adapter.ts +1 -1
- package/src/runtime/routes/identity-routes.ts +3 -2
- package/src/runtime/routes/inbound-message-handler.ts +5 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +97 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +61 -49
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +16 -4
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +21 -8
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +14 -3
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/llm-context-normalization.ts +71 -0
- package/src/runtime/routes/mcp-auth-routes.ts +38 -15
- package/src/runtime/routes/migration-rollback-routes.ts +4 -3
- package/src/runtime/routes/migration-routes.ts +4 -1
- package/src/runtime/routes/onboarding-checkin-routes.ts +86 -0
- package/src/runtime/routes/subagents-routes.ts +5 -0
- package/src/runtime/routes/surface-action-routes.ts +51 -55
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +1 -0
- package/src/runtime/services/conversation-serializer.ts +7 -9
- package/src/runtime/tool-grant-request-helper.ts +3 -3
- package/src/runtime/trust-verdict-consumer.ts +85 -9
- package/src/runtime/verification-outbound-actions.ts +18 -18
- package/src/signals/user-message.ts +16 -0
- package/src/subagent/manager.ts +9 -0
- package/src/telemetry/types.ts +34 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
- package/src/telemetry/usage-telemetry-reporter.ts +87 -3
- package/src/tools/ask-question/ask-question-tool.test.ts +29 -0
- package/src/tools/ask-question/ask-question-tool.ts +13 -0
- package/src/tools/computer-use/definitions.ts +8 -2
- package/src/tools/executor.ts +4 -4
- package/src/tools/registry.ts +18 -0
- package/src/tools/tool-approval-handler.ts +1 -1
- package/src/tools/tool-defaults.ts +9 -2
- package/src/tools/types.ts +17 -2
- package/src/tools/workspace-tools/loader.ts +348 -244
- package/src/util/platform.ts +5 -0
- package/src/util/telemetry-db-path.ts +24 -0
- package/src/workspace/migrations/017-seed-persona-dirs.ts +3 -34
- package/src/workspace/migrations/019-scope-journal-to-guardian.ts +3 -24
- package/src/__tests__/workspace-tools-watcher-flag.test.ts +0 -70
- package/src/daemon/workspace-tools-watcher.ts +0 -328
- package/src/memory/migrations/registry.ts +0 -573
|
@@ -619,6 +619,86 @@ Write a local article draft.
|
|
|
619
619
|
expect(after).toEqual(before);
|
|
620
620
|
});
|
|
621
621
|
|
|
622
|
+
test("populates the cache from the local catalog on the first seed even when the embedding backend is unavailable (cold-start needle resilience)", async () => {
|
|
623
|
+
// Regression: on a brand-new managed assistant the startup seed runs before
|
|
624
|
+
// the platform provisions the managed embedding credential, so the very
|
|
625
|
+
// first `embedWithBackend` throws. The in-memory cache (which the v3 needle
|
|
626
|
+
// lane and the page index read) must still populate from the local catalog
|
|
627
|
+
// so skills are discoverable from first boot; only the dense Qdrant upsert
|
|
628
|
+
// is deferred until the backend recovers.
|
|
629
|
+
const skillA = makeSummary({
|
|
630
|
+
id: "example-skill-a",
|
|
631
|
+
displayName: "Skill A",
|
|
632
|
+
});
|
|
633
|
+
state.catalog = [skillA];
|
|
634
|
+
state.resolved = [{ summary: skillA, state: "enabled" }];
|
|
635
|
+
// No prior successful seed — the backend is unconfigured from the start.
|
|
636
|
+
state.embedThrows = new Error(
|
|
637
|
+
'Embedding backend "gemini" is not configured',
|
|
638
|
+
);
|
|
639
|
+
|
|
640
|
+
// Best-effort callers (the startup seed) must resolve.
|
|
641
|
+
await expect(seedV2SkillEntries()).resolves.toBeUndefined();
|
|
642
|
+
|
|
643
|
+
// The needle-lane cache is populated despite the dense-embed failure.
|
|
644
|
+
const entry = getSkillCapability("example-skill-a");
|
|
645
|
+
expect(entry).not.toBeNull();
|
|
646
|
+
expect(entry?.id).toBe("example-skill-a");
|
|
647
|
+
expect(entry?.content).toContain("Skill A");
|
|
648
|
+
expect(listSkillEntries().map((e) => e.id)).toEqual(["example-skill-a"]);
|
|
649
|
+
|
|
650
|
+
// No dense vectors were produced, so the Qdrant write is skipped entirely.
|
|
651
|
+
expect(state.upsertCalls).toHaveLength(0);
|
|
652
|
+
expect(state.pruneCalls).toHaveLength(0);
|
|
653
|
+
});
|
|
654
|
+
|
|
655
|
+
test("surfaces the dense-embed failure to throwOnError callers while still populating the needle cache", async () => {
|
|
656
|
+
// The managed-credential reseed and the operator reembed route pass
|
|
657
|
+
// `throwOnError` so they learn the dense lane didn't complete and the
|
|
658
|
+
// retry/maintain machinery backfills it — but the needle cache is fixed
|
|
659
|
+
// regardless before the error propagates.
|
|
660
|
+
const skillA = makeSummary({ id: "example-skill-a" });
|
|
661
|
+
state.catalog = [skillA];
|
|
662
|
+
state.resolved = [{ summary: skillA, state: "enabled" }];
|
|
663
|
+
state.embedThrows = new Error("backend down");
|
|
664
|
+
|
|
665
|
+
await expect(seedV2SkillEntries({ throwOnError: true })).rejects.toThrow(
|
|
666
|
+
"backend down",
|
|
667
|
+
);
|
|
668
|
+
|
|
669
|
+
expect(getSkillCapability("example-skill-a")).not.toBeNull();
|
|
670
|
+
expect(state.upsertCalls).toHaveLength(0);
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
test("drops a locally-disabled installed skill even when the catalog is unavailable (local state is authoritative)", async () => {
|
|
674
|
+
// Regression: the local resolution is authoritative, so a skill that is
|
|
675
|
+
// still installed but explicitly disabled must NOT be kept alive by remote-
|
|
676
|
+
// catalog uncertainty — `getSkillCapability` and the page index must drop it.
|
|
677
|
+
const skillA = makeSummary({ id: "example-skill-a" });
|
|
678
|
+
state.catalog = [skillA];
|
|
679
|
+
state.resolved = [{ summary: skillA, state: "enabled" }];
|
|
680
|
+
state.fullCatalog = [
|
|
681
|
+
{ id: "example-skill-a", name: "example-skill-a", description: "A" },
|
|
682
|
+
];
|
|
683
|
+
state.embedReturn = [[0.1, 0.2, 0.3]];
|
|
684
|
+
|
|
685
|
+
// First run: skillA enabled and cached.
|
|
686
|
+
await seedV2SkillEntries();
|
|
687
|
+
expect(getSkillCapability("example-skill-a")).not.toBeNull();
|
|
688
|
+
|
|
689
|
+
// Second run: skillA is still locally installed but now disabled; the remote
|
|
690
|
+
// catalog is unavailable. It stays in `installedIds`, so the carry-forward
|
|
691
|
+
// must skip it and the cache drops it.
|
|
692
|
+
state.resolved = [{ summary: skillA, state: "disabled" }];
|
|
693
|
+
state.fullCatalog = [];
|
|
694
|
+
state.fullCatalogThrows = new Error("catalog fetch failed");
|
|
695
|
+
|
|
696
|
+
await seedV2SkillEntries();
|
|
697
|
+
|
|
698
|
+
expect(getSkillCapability("example-skill-a")).toBeNull();
|
|
699
|
+
expect(listSkillEntries()).toEqual([]);
|
|
700
|
+
});
|
|
701
|
+
|
|
622
702
|
test("no enabled skills yields empty cache and no prune when catalog is empty", async () => {
|
|
623
703
|
state.catalog = [];
|
|
624
704
|
state.resolved = [];
|
|
@@ -161,22 +161,23 @@ async function runSeedV2CliCommandEntries(generation: number): Promise<void> {
|
|
|
161
161
|
seeds.push({ id: name, description, content });
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
// Sparse (BM25/TF) encoding is computed locally; only the dense vectors
|
|
165
|
+
// require `embedWithBackend`, which is unconfigured during the cold-start
|
|
166
|
+
// window before a managed-proxy embedding credential is provisioned. A
|
|
167
|
+
// dense-embed failure is non-fatal to the in-memory cache: the v3 needle
|
|
168
|
+
// finder lane reads CLI capabilities from `entries` / the page index, NOT
|
|
169
|
+
// from Qdrant, so the cache is populated from the local Commander tree
|
|
170
|
+
// regardless of backend state and commands stay discoverable from first
|
|
171
|
+
// boot. Only the dense Qdrant upsert is skipped; the managed-credential
|
|
172
|
+
// reseed and the v3 maintain pass backfill the dense vectors on recovery.
|
|
164
173
|
const nextEntries = new Map<string, CliCommandEntry>();
|
|
165
174
|
let denseVectors: number[][] = [];
|
|
175
|
+
let denseAvailable = false;
|
|
176
|
+
let denseError: unknown = null;
|
|
166
177
|
let encodeSparse: (
|
|
167
178
|
input: string,
|
|
168
179
|
) => ReturnType<typeof generateSparseEmbedding> = generateSparseEmbedding;
|
|
169
180
|
if (seeds.length > 0) {
|
|
170
|
-
const embedded = await embedWithBackend(
|
|
171
|
-
config,
|
|
172
|
-
seeds.map((s) => s.content),
|
|
173
|
-
);
|
|
174
|
-
denseVectors = await Promise.all(
|
|
175
|
-
embedded.vectors.map((v) =>
|
|
176
|
-
applyCorrectionIfCalibrated(v, embedded.provider, embedded.model),
|
|
177
|
-
),
|
|
178
|
-
);
|
|
179
|
-
|
|
180
181
|
// CLI commands share the concept-page Qdrant collection, so the sparse
|
|
181
182
|
// vector must use the same stemmed BM25 encoding as the concept-page
|
|
182
183
|
// documents. Fall back to the legacy TF encoder only during the cold-
|
|
@@ -190,6 +191,24 @@ async function runSeedV2CliCommandEntries(generation: number): Promise<void> {
|
|
|
190
191
|
b: config.memory.v2.bm25_b,
|
|
191
192
|
})
|
|
192
193
|
: generateSparseEmbedding(input);
|
|
194
|
+
try {
|
|
195
|
+
const embedded = await embedWithBackend(
|
|
196
|
+
config,
|
|
197
|
+
seeds.map((s) => s.content),
|
|
198
|
+
);
|
|
199
|
+
denseVectors = await Promise.all(
|
|
200
|
+
embedded.vectors.map((v) =>
|
|
201
|
+
applyCorrectionIfCalibrated(v, embedded.provider, embedded.model),
|
|
202
|
+
),
|
|
203
|
+
);
|
|
204
|
+
denseAvailable = true;
|
|
205
|
+
} catch (err) {
|
|
206
|
+
denseError = err;
|
|
207
|
+
log.warn(
|
|
208
|
+
{ err },
|
|
209
|
+
"Embedding backend unavailable — seeding CLI-command cache without dense Qdrant vectors; the needle lane surfaces commands from the cache and the dense lane backfills when the backend recovers",
|
|
210
|
+
);
|
|
211
|
+
}
|
|
193
212
|
}
|
|
194
213
|
|
|
195
214
|
if (generation !== requestedSeedGeneration) {
|
|
@@ -201,7 +220,17 @@ async function runSeedV2CliCommandEntries(generation: number): Promise<void> {
|
|
|
201
220
|
return;
|
|
202
221
|
}
|
|
203
222
|
|
|
204
|
-
|
|
223
|
+
// Populate the in-memory cache (and therefore the page index / needle lane)
|
|
224
|
+
// from the local Commander tree regardless of dense availability.
|
|
225
|
+
for (const seed of seeds) {
|
|
226
|
+
nextEntries.set(seed.id, seed);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Write the dense+sparse Qdrant points only when dense vectors were
|
|
230
|
+
// produced. In the degraded (backend-unavailable) path we skip the upsert
|
|
231
|
+
// so we never write half-formed points; the dense lane backfills on
|
|
232
|
+
// recovery.
|
|
233
|
+
if (seeds.length > 0 && denseAvailable) {
|
|
205
234
|
const now = Date.now();
|
|
206
235
|
await Promise.all(
|
|
207
236
|
seeds.map((seed, i) =>
|
|
@@ -214,40 +243,48 @@ async function runSeedV2CliCommandEntries(generation: number): Promise<void> {
|
|
|
214
243
|
}),
|
|
215
244
|
),
|
|
216
245
|
);
|
|
217
|
-
for (const seed of seeds) {
|
|
218
|
-
nextEntries.set(seed.id, seed);
|
|
219
|
-
}
|
|
220
246
|
}
|
|
221
247
|
|
|
222
|
-
// The CLI tree is always available (no remote catalog), so pruning
|
|
223
|
-
//
|
|
224
|
-
//
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
)
|
|
248
|
+
// The CLI tree is always available (no remote catalog), so pruning to clear
|
|
249
|
+
// stale rows runs whenever we wrote the current set this run OR there was
|
|
250
|
+
// nothing to embed (empty tree). The cold-start degraded path (commands
|
|
251
|
+
// present but dense embedding unavailable) skips the prune so we never
|
|
252
|
+
// reconcile the collection against a set we did not persist. Run the legacy
|
|
253
|
+
// `kind` backfill once per process so pre-discriminator rows become prunable.
|
|
254
|
+
if (denseAvailable || seeds.length === 0) {
|
|
255
|
+
const knownIds = new Set(seeds.map((s) => s.id));
|
|
256
|
+
if (!legacyKindBackfillDone) {
|
|
257
|
+
try {
|
|
258
|
+
await backfillKindOnPointsWithPrefix(
|
|
259
|
+
CLI_COMMAND_SLUG_PREFIX,
|
|
260
|
+
CLI_COMMAND_PAYLOAD_KIND,
|
|
261
|
+
knownIds,
|
|
262
|
+
);
|
|
263
|
+
legacyKindBackfillDone = true;
|
|
264
|
+
} catch (err) {
|
|
265
|
+
log.warn(
|
|
266
|
+
{ err },
|
|
267
|
+
"Failed to backfill kind on legacy CLI-command points — pruning may leave orphans this run",
|
|
268
|
+
);
|
|
269
|
+
}
|
|
239
270
|
}
|
|
271
|
+
await pruneSlugsWithPrefixExcept(
|
|
272
|
+
CLI_COMMAND_SLUG_PREFIX,
|
|
273
|
+
seeds.map((s) => s.id),
|
|
274
|
+
{ kind: CLI_COMMAND_PAYLOAD_KIND },
|
|
275
|
+
);
|
|
240
276
|
}
|
|
241
|
-
await pruneSlugsWithPrefixExcept(
|
|
242
|
-
CLI_COMMAND_SLUG_PREFIX,
|
|
243
|
-
seeds.map((s) => s.id),
|
|
244
|
-
{ kind: CLI_COMMAND_PAYLOAD_KIND },
|
|
245
|
-
);
|
|
246
277
|
|
|
247
|
-
// Atomically replace the cache
|
|
278
|
+
// Atomically replace the cache from the freshly enumerated commands. Drop
|
|
279
|
+
// the page-index cache so the next router invocation observes the new
|
|
280
|
+
// command set.
|
|
248
281
|
entries = nextEntries;
|
|
249
282
|
invalidatePageIndex();
|
|
250
|
-
|
|
283
|
+
|
|
284
|
+
// Surface a dense-embed failure to `throwOnError` callers so the existing
|
|
285
|
+
// retry + maintain machinery backfills the dense lane. The in-memory cache
|
|
286
|
+
// is already updated above, so the needle lane is fixed regardless.
|
|
287
|
+
lastSeedError = denseError;
|
|
251
288
|
} catch (err) {
|
|
252
289
|
lastSeedError = err;
|
|
253
290
|
log.warn({ err }, "Failed to seed v2 CLI-command entries");
|
|
@@ -18,12 +18,9 @@
|
|
|
18
18
|
* the convention established for the sweep prompt.
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
import { lstatSync, readFileSync } from "node:fs";
|
|
22
|
-
import { homedir } from "node:os";
|
|
23
|
-
import { isAbsolute, join } from "node:path";
|
|
24
|
-
|
|
25
21
|
import { getLogger } from "../../../util/logger.js";
|
|
26
22
|
import { getWorkspaceDir } from "../../../util/platform.js";
|
|
23
|
+
import { loadPromptOverride } from "../../prompt-override.js";
|
|
27
24
|
|
|
28
25
|
const log = getLogger("memory-v2-consolidate-prompt");
|
|
29
26
|
|
|
@@ -61,14 +58,6 @@ export const CORE_PAGES_CONSOLIDATION_SECTION = `## 10. Review \`memory/core-pag
|
|
|
61
58
|
|
|
62
59
|
`;
|
|
63
60
|
|
|
64
|
-
/**
|
|
65
|
-
* Upper bound for the override file. Real consolidation prompts are kilobytes;
|
|
66
|
-
* 1 MiB is generous headroom while preventing a `settings.write` principal from
|
|
67
|
-
* pointing the field at a multi-gigabyte file (or `/dev/zero`-like stream that
|
|
68
|
-
* `lstat` can't size cap on its own) and exfiltrating it through the wake hint.
|
|
69
|
-
*/
|
|
70
|
-
const MAX_PROMPT_BYTES = 1 * 1024 * 1024;
|
|
71
|
-
|
|
72
61
|
/**
|
|
73
62
|
* Consolidation prompt — live-mode only. The agent runs as itself (full
|
|
74
63
|
* SOUL.md + IDENTITY.md + persona + memory autoloads) with the standard
|
|
@@ -837,90 +826,32 @@ export function renderConsolidationPrompt(
|
|
|
837
826
|
/**
|
|
838
827
|
* Load the consolidation prompt template, optionally overridden from the file
|
|
839
828
|
* referenced by `memory.v2.consolidation_prompt_path`, then substitute
|
|
840
|
-
* `{{CUTOFF}}`.
|
|
829
|
+
* `{{CUTOFF}}`. File loading (path resolution, size guard, and the permissive
|
|
830
|
+
* fall-back to the bundled prompt on a missing/unreadable/empty/oversized
|
|
831
|
+
* override) is handled by the shared {@link loadPromptOverride}.
|
|
841
832
|
*
|
|
842
833
|
* Override files get the same placeholder substitutions as the bundled
|
|
843
834
|
* template: `{{CUTOFF}}` always, and `{{CORE_PAGES_SECTION}}` per the same
|
|
844
835
|
* flag gate — so a prompt copied from the bundled source never leaks a raw
|
|
845
836
|
* placeholder, and a customized prompt can opt into the managed section.
|
|
846
|
-
*
|
|
847
|
-
* Failure handling is intentionally permissive — missing file, read error, or
|
|
848
|
-
* empty/whitespace-only body all log a warning and fall back to the bundled
|
|
849
|
-
* prompt. Consolidation must never break because of a bad override: the
|
|
850
|
-
* daemon's startup philosophy is "log and recover."
|
|
851
837
|
*/
|
|
852
838
|
export function resolveConsolidationPrompt(
|
|
853
839
|
overridePath: string | null,
|
|
854
840
|
cutoff: string,
|
|
855
841
|
options: ConsolidationPromptOptions,
|
|
856
842
|
): string {
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
configuredPath: overridePath,
|
|
867
|
-
resolvedPath,
|
|
868
|
-
reason: "not_regular_file",
|
|
869
|
-
fallback: "bundled",
|
|
870
|
-
},
|
|
871
|
-
"consolidation prompt override is not a regular file; using bundled prompt",
|
|
872
|
-
);
|
|
873
|
-
return renderConsolidationPrompt(cutoff, options);
|
|
874
|
-
}
|
|
875
|
-
if (stat.size > MAX_PROMPT_BYTES) {
|
|
876
|
-
log.warn(
|
|
877
|
-
{
|
|
878
|
-
configuredPath: overridePath,
|
|
879
|
-
resolvedPath,
|
|
880
|
-
size: stat.size,
|
|
881
|
-
limit: MAX_PROMPT_BYTES,
|
|
882
|
-
reason: "oversized_override",
|
|
883
|
-
fallback: "bundled",
|
|
884
|
-
},
|
|
885
|
-
"consolidation prompt override exceeds size limit; using bundled prompt",
|
|
886
|
-
);
|
|
887
|
-
return renderConsolidationPrompt(cutoff, options);
|
|
888
|
-
}
|
|
889
|
-
contents = readFileSync(resolvedPath, "utf-8");
|
|
890
|
-
} catch (err) {
|
|
891
|
-
const code = (err as NodeJS.ErrnoException).code;
|
|
892
|
-
log.warn(
|
|
893
|
-
{ configuredPath: overridePath, resolvedPath, code, fallback: "bundled" },
|
|
894
|
-
"consolidation prompt override unreadable; using bundled prompt",
|
|
895
|
-
);
|
|
896
|
-
return renderConsolidationPrompt(cutoff, options);
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
if (contents.trim().length === 0) {
|
|
900
|
-
log.warn(
|
|
901
|
-
{
|
|
902
|
-
configuredPath: overridePath,
|
|
903
|
-
resolvedPath,
|
|
904
|
-
reason: "empty_override",
|
|
905
|
-
fallback: "bundled",
|
|
906
|
-
},
|
|
907
|
-
"consolidation prompt override is empty; using bundled prompt",
|
|
908
|
-
);
|
|
909
|
-
return renderConsolidationPrompt(cutoff, options);
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
return contents
|
|
843
|
+
const override = loadPromptOverride({
|
|
844
|
+
overridePath,
|
|
845
|
+
workspaceDir: getWorkspaceDir(),
|
|
846
|
+
log,
|
|
847
|
+
label: "consolidation prompt",
|
|
848
|
+
});
|
|
849
|
+
if (override === null) return renderConsolidationPrompt(cutoff, options);
|
|
850
|
+
|
|
851
|
+
return override
|
|
913
852
|
.replaceAll(CUTOFF_PLACEHOLDER, cutoff)
|
|
914
853
|
.replaceAll(
|
|
915
854
|
CORE_PAGES_PLACEHOLDER,
|
|
916
855
|
options.includeCorePagesSection ? CORE_PAGES_CONSOLIDATION_SECTION : "",
|
|
917
856
|
);
|
|
918
857
|
}
|
|
919
|
-
|
|
920
|
-
function resolveOverridePath(overridePath: string): string {
|
|
921
|
-
if (overridePath.startsWith("~/")) {
|
|
922
|
-
return join(homedir(), overridePath.slice(2));
|
|
923
|
-
}
|
|
924
|
-
if (isAbsolute(overridePath)) return overridePath;
|
|
925
|
-
return join(getWorkspaceDir(), overridePath);
|
|
926
|
-
}
|
|
@@ -22,22 +22,14 @@
|
|
|
22
22
|
* same placeholder substitution applies to overrides.
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
|
-
import { lstatSync, readFileSync } from "node:fs";
|
|
26
|
-
import { homedir } from "node:os";
|
|
27
|
-
import { isAbsolute, join } from "node:path";
|
|
28
|
-
|
|
29
25
|
import { getLogger } from "../../../util/logger.js";
|
|
26
|
+
import {
|
|
27
|
+
loadPromptOverride,
|
|
28
|
+
MAX_PROMPT_OVERRIDE_BYTES,
|
|
29
|
+
} from "../../prompt-override.js";
|
|
30
30
|
|
|
31
31
|
const log = getLogger("memory-v2-router-prompt");
|
|
32
32
|
|
|
33
|
-
/**
|
|
34
|
-
* Hard upper bound on the override file size. The bundled prompt is well
|
|
35
|
-
* under 4 KiB; 1 MiB is generous-enough for any reasonable hand-edit while
|
|
36
|
-
* still preventing pathological inputs from being slurped into memory on
|
|
37
|
-
* every router call.
|
|
38
|
-
*/
|
|
39
|
-
const MAX_PROMPT_BYTES = 1 * 1024 * 1024;
|
|
40
|
-
|
|
41
33
|
/** Sentinel substituted with the assistant's display name at runtime. */
|
|
42
34
|
const ASSISTANT_NAME_PLACEHOLDER = "{{ASSISTANT_NAME}}";
|
|
43
35
|
|
|
@@ -102,14 +94,12 @@ export function renderRouterPrompt(opts: RenderRouterPromptOpts): string {
|
|
|
102
94
|
/**
|
|
103
95
|
* Load the router prompt template, optionally overridden from the file
|
|
104
96
|
* referenced by `memory.v2.router.router_prompt_path`, then substitute the
|
|
105
|
-
* standard placeholders.
|
|
106
|
-
*
|
|
107
|
-
*
|
|
97
|
+
* standard placeholders. File loading (path resolution, size guard, and the
|
|
98
|
+
* permissive fall-back to the bundled prompt on a missing/unreadable/empty/
|
|
99
|
+
* oversized override) is handled by the shared {@link loadPromptOverride}.
|
|
108
100
|
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
* back to the bundled prompt. Router selection must never break because of
|
|
112
|
-
* a bad override.
|
|
101
|
+
* An `inlineOverride` (e.g. the simulator playground) takes precedence over the
|
|
102
|
+
* configured file path; same placeholder substitution and size guard apply.
|
|
113
103
|
*/
|
|
114
104
|
export function resolveRouterPrompt(
|
|
115
105
|
overridePath: string | null,
|
|
@@ -117,17 +107,15 @@ export function resolveRouterPrompt(
|
|
|
117
107
|
opts: RenderRouterPromptOpts,
|
|
118
108
|
inlineOverride?: string | null,
|
|
119
109
|
): string {
|
|
120
|
-
// Inline override
|
|
121
|
-
//
|
|
122
|
-
//
|
|
123
|
-
// bodies fall through to file/bundled resolution so a "cleared" textarea
|
|
124
|
-
// is treated as no override.
|
|
110
|
+
// Inline override takes precedence over the configured file path and the
|
|
111
|
+
// bundled prompt. Empty/whitespace bodies fall through to file/bundled
|
|
112
|
+
// resolution so a "cleared" textarea is treated as no override.
|
|
125
113
|
if (inlineOverride !== undefined && inlineOverride !== null) {
|
|
126
|
-
if (inlineOverride.length >
|
|
114
|
+
if (inlineOverride.length > MAX_PROMPT_OVERRIDE_BYTES) {
|
|
127
115
|
log.warn(
|
|
128
116
|
{
|
|
129
117
|
size: inlineOverride.length,
|
|
130
|
-
limit:
|
|
118
|
+
limit: MAX_PROMPT_OVERRIDE_BYTES,
|
|
131
119
|
reason: "oversized_inline_override",
|
|
132
120
|
fallback: "path_or_bundled",
|
|
133
121
|
},
|
|
@@ -138,62 +126,13 @@ export function resolveRouterPrompt(
|
|
|
138
126
|
}
|
|
139
127
|
}
|
|
140
128
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
log.warn(
|
|
149
|
-
{
|
|
150
|
-
configuredPath: overridePath,
|
|
151
|
-
resolvedPath,
|
|
152
|
-
reason: "not_regular_file",
|
|
153
|
-
fallback: "bundled",
|
|
154
|
-
},
|
|
155
|
-
"router prompt override is not a regular file; using bundled prompt",
|
|
156
|
-
);
|
|
157
|
-
return renderRouterPrompt(opts);
|
|
158
|
-
}
|
|
159
|
-
if (stat.size > MAX_PROMPT_BYTES) {
|
|
160
|
-
log.warn(
|
|
161
|
-
{
|
|
162
|
-
configuredPath: overridePath,
|
|
163
|
-
resolvedPath,
|
|
164
|
-
size: stat.size,
|
|
165
|
-
limit: MAX_PROMPT_BYTES,
|
|
166
|
-
reason: "oversized_override",
|
|
167
|
-
fallback: "bundled",
|
|
168
|
-
},
|
|
169
|
-
"router prompt override exceeds size limit; using bundled prompt",
|
|
170
|
-
);
|
|
171
|
-
return renderRouterPrompt(opts);
|
|
172
|
-
}
|
|
173
|
-
contents = readFileSync(resolvedPath, "utf-8");
|
|
174
|
-
} catch (err) {
|
|
175
|
-
const code = (err as NodeJS.ErrnoException).code;
|
|
176
|
-
log.warn(
|
|
177
|
-
{ configuredPath: overridePath, resolvedPath, code, fallback: "bundled" },
|
|
178
|
-
"router prompt override unreadable; using bundled prompt",
|
|
179
|
-
);
|
|
180
|
-
return renderRouterPrompt(opts);
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (contents.trim().length === 0) {
|
|
184
|
-
log.warn(
|
|
185
|
-
{
|
|
186
|
-
configuredPath: overridePath,
|
|
187
|
-
resolvedPath,
|
|
188
|
-
reason: "empty_override",
|
|
189
|
-
fallback: "bundled",
|
|
190
|
-
},
|
|
191
|
-
"router prompt override is empty; using bundled prompt",
|
|
192
|
-
);
|
|
193
|
-
return renderRouterPrompt(opts);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
return substitutePlaceholders(contents, opts);
|
|
129
|
+
const override = loadPromptOverride({
|
|
130
|
+
overridePath,
|
|
131
|
+
workspaceDir,
|
|
132
|
+
log,
|
|
133
|
+
label: "router prompt",
|
|
134
|
+
});
|
|
135
|
+
return substitutePlaceholders(override ?? ROUTER_PROMPT, opts);
|
|
197
136
|
}
|
|
198
137
|
|
|
199
138
|
function substitutePlaceholders(
|
|
@@ -207,14 +146,3 @@ function substitutePlaceholders(
|
|
|
207
146
|
.replaceAll(USER_NAME_PLACEHOLDER, () => user)
|
|
208
147
|
.replaceAll(PAGE_INDEX_PLACEHOLDER, () => opts.pageIndexBlock);
|
|
209
148
|
}
|
|
210
|
-
|
|
211
|
-
function resolveOverridePath(
|
|
212
|
-
overridePath: string,
|
|
213
|
-
workspaceDir: string,
|
|
214
|
-
): string {
|
|
215
|
-
if (overridePath.startsWith("~/")) {
|
|
216
|
-
return join(homedir(), overridePath.slice(2));
|
|
217
|
-
}
|
|
218
|
-
if (isAbsolute(overridePath)) return overridePath;
|
|
219
|
-
return join(workspaceDir, overridePath);
|
|
220
|
-
}
|