@vellumai/assistant 0.10.2-dev.202606250318.5e7cfb0 → 0.10.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/bun.lock +0 -20
- package/docs/workspace-tools.md +33 -42
- package/eslint-rules/cli-no-daemon-internals.js +0 -6
- package/node_modules/@vellumai/gateway-client/src/__tests__/trust-verdict-contract.test.ts +0 -31
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +0 -44
- package/node_modules/@vellumai/gateway-client/src/index.ts +0 -14
- package/node_modules/@vellumai/gateway-client/src/trust-verdict-contract.ts +0 -17
- package/node_modules/@vellumai/service-contracts/package.json +0 -1
- package/node_modules/@vellumai/service-contracts/src/index.ts +0 -1
- package/openapi.yaml +0 -155
- package/package.json +1 -4
- package/scripts/test.sh +15 -36
- package/src/__tests__/actor-token-service.test.ts +14 -36
- package/src/__tests__/agent-loop-override-profile.test.ts +0 -1
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +0 -2
- package/src/__tests__/agent-wake-override-profile.test.ts +0 -2
- package/src/__tests__/annotate-activity-metadata.test.ts +0 -2
- package/src/__tests__/annotate-risk-options.test.ts +0 -2
- package/src/__tests__/approval-cascade.test.ts +0 -2
- package/src/__tests__/assistant-attachments.test.ts +0 -42
- package/src/__tests__/background-workers-disk-pressure.test.ts +0 -2
- package/src/__tests__/btw-routes.test.ts +0 -2
- package/src/__tests__/build-persisted-content.test.ts +0 -2
- package/src/__tests__/call-controller.test.ts +0 -19
- package/src/__tests__/channel-guardian.test.ts +58 -94
- package/src/__tests__/channel-reply-delivery.test.ts +0 -2
- package/src/__tests__/compaction-events.test.ts +0 -2
- package/src/__tests__/compaction.benchmark.test.ts +0 -2
- package/src/__tests__/compactor-call-site-logging.test.ts +0 -2
- package/src/__tests__/compactor-low-watermark-cut.test.ts +0 -2
- package/src/__tests__/compactor-preserved-tail-count.test.ts +0 -2
- package/src/__tests__/compactor-summary-call-truncation.test.ts +0 -2
- package/src/__tests__/compactor-web-search-strip.test.ts +0 -2
- package/src/__tests__/config-loader-backfill.test.ts +10 -123
- package/src/__tests__/config-schema.test.ts +0 -1
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +29 -31
- package/src/__tests__/contacts-relay-reads.test.ts +15 -13
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -2
- package/src/__tests__/conversation-agent-loop-disk-pressure.test.ts +0 -2
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +0 -2
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -2
- package/src/__tests__/conversation-agent-loop.test.ts +0 -134
- package/src/__tests__/conversation-analysis-routes.test.ts +0 -2
- package/src/__tests__/conversation-app-control-lifecycle.test.ts +0 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +0 -2
- package/src/__tests__/conversation-history-web-search.test.ts +0 -2
- package/src/__tests__/conversation-load-history-repair.test.ts +0 -2
- package/src/__tests__/conversation-load-history-stripped.test.ts +0 -2
- package/src/__tests__/conversation-pairing.test.ts +0 -2
- package/src/__tests__/conversation-process-app-control-preactivation.test.ts +0 -2
- package/src/__tests__/conversation-process-callsite.test.ts +0 -2
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -2
- package/src/__tests__/conversation-queue.test.ts +0 -91
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +0 -14
- package/src/__tests__/conversation-routes-slash-commands.test.ts +0 -14
- package/src/__tests__/conversation-slash-queue.test.ts +0 -2
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -2
- package/src/__tests__/conversation-speed-override.test.ts +0 -2
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +0 -29
- package/src/__tests__/conversation-title-service.test.ts +0 -2
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +0 -47
- package/src/__tests__/conversation-usage.test.ts +0 -2
- package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -2
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -2
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -2
- package/src/__tests__/credential-security-invariants.test.ts +1 -1
- package/src/__tests__/db-migration-rollback.test.ts +171 -205
- package/src/__tests__/db-test-helpers.ts +4 -5
- package/src/__tests__/deterministic-verification-control-plane.test.ts +2 -4
- package/src/__tests__/disk-pressure-guard.test.ts +0 -41
- package/src/__tests__/dm-persistence.test.ts +0 -2
- package/src/__tests__/emit-signal-routing-intent.test.ts +5 -10
- package/src/__tests__/events-dev-bypass-actor.test.ts +1 -7
- package/src/__tests__/exploration-drift-hook.test.ts +2 -3
- package/src/__tests__/filing-service.test.ts +0 -2
- package/src/__tests__/guardian-binding-drift-heal.test.ts +10 -75
- package/src/__tests__/guardian-dispatch.test.ts +1 -95
- package/src/__tests__/guardian-outbound-http.test.ts +0 -13
- package/src/__tests__/heartbeat-disk-pressure.test.ts +0 -2
- package/src/__tests__/heartbeat-service.test.ts +0 -2
- package/src/__tests__/helpers/channel-test-adapter.ts +7 -1
- package/src/__tests__/host-app-control-routes.test.ts +30 -24
- package/src/__tests__/host-bash-routes.test.ts +41 -31
- package/src/__tests__/host-browser-routes.test.ts +32 -26
- package/src/__tests__/host-cu-routes-targeted.test.ts +33 -25
- package/src/__tests__/host-file-routes-targeted.test.ts +52 -40
- package/src/__tests__/host-transfer-routes-targeted.test.ts +43 -31
- package/src/__tests__/http-user-message-parity.test.ts +8 -290
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -28
- package/src/__tests__/inbound-slack-persistence.test.ts +0 -2
- package/src/__tests__/invite-redemption-service.test.ts +0 -198
- package/src/__tests__/llm-context-normalization.test.ts +0 -105
- package/src/__tests__/llm-request-log-error-payload.test.ts +9 -71
- package/src/__tests__/llm-usage-store.test.ts +0 -25
- package/src/__tests__/mcp-health-check.test.ts +1 -2
- package/src/__tests__/media-stream-server-integration.test.ts +0 -127
- package/src/__tests__/memory-retrieval-hook.test.ts +0 -2
- package/src/__tests__/messaging-send-tool.test.ts +0 -2
- package/src/__tests__/migration-import-from-url.test.ts +2 -2
- package/src/__tests__/mtime-cache.test.ts +5 -146
- package/src/__tests__/native-web-search.test.ts +0 -2
- package/src/__tests__/non-member-access-request.test.ts +17 -189
- package/src/__tests__/notification-broadcaster.test.ts +0 -4
- package/src/__tests__/notification-decision-recipient-context.test.ts +32 -33
- package/src/__tests__/notification-deep-link.test.ts +0 -6
- package/src/__tests__/notification-guardian-path.test.ts +0 -19
- package/src/__tests__/openai-provider.test.ts +12 -22
- package/src/__tests__/openai-responses-provider.test.ts +2 -12
- package/src/__tests__/outbound-slack-persistence.test.ts +0 -2
- package/src/__tests__/pending-interactions-resolved-event.test.ts +4 -7
- package/src/__tests__/persistence-secret-redaction.test.ts +0 -2
- package/src/__tests__/plugin-bootstrap.test.ts +73 -3
- package/src/__tests__/plugin-route-contribution.test.ts +17 -4
- package/src/__tests__/plugin-tool-contribution.test.ts +18 -3
- package/src/__tests__/plugin-types.test.ts +2 -0
- package/src/__tests__/process-message-background-slack.test.ts +0 -2
- package/src/__tests__/process-message-display-content.test.ts +0 -2
- package/src/__tests__/provider-error-scenarios.test.ts +4 -5
- package/src/__tests__/provider-usage-tracking.test.ts +0 -39
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +0 -2
- package/src/__tests__/registry.test.ts +1 -4
- package/src/__tests__/relay-server.test.ts +25 -694
- package/src/__tests__/runtime-attachment-metadata.test.ts +1 -0
- package/src/__tests__/secret-ingress-http.test.ts +0 -14
- package/src/__tests__/send-endpoint-busy.test.ts +8 -30
- package/src/__tests__/skills.test.ts +0 -44
- package/src/__tests__/slack-inbound-verification.test.ts +2 -47
- package/src/__tests__/stt-hints.test.ts +13 -44
- package/src/__tests__/subagent-detail.test.ts +0 -27
- package/src/__tests__/subagent-disposal.test.ts +0 -65
- package/src/__tests__/subagent-notify-parent.test.ts +0 -2
- package/src/__tests__/subagent-role-registry.test.ts +2 -7
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +0 -2
- package/src/__tests__/subagent-tools.test.ts +0 -2
- package/src/__tests__/suggestion-routes.test.ts +0 -2
- package/src/__tests__/title-generate-hook.test.ts +0 -2
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +0 -2
- package/src/__tests__/tool-executor.test.ts +11 -16
- package/src/__tests__/tool-preview-lifecycle.test.ts +0 -2
- package/src/__tests__/tool-result-metadata-plumbing.test.ts +0 -2
- package/src/__tests__/tool-start-timestamp.test.ts +0 -2
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +10 -10
- package/src/__tests__/twilio-routes.test.ts +0 -96
- package/src/__tests__/ui-file-upload-surface.test.ts +0 -86
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
- package/src/__tests__/voice-invite-redemption.test.ts +0 -33
- package/src/__tests__/web-search-backend-failure.test.ts +0 -2
- package/src/__tests__/workspace-migration-remove-hooks.test.ts +35 -14
- package/src/__tests__/workspace-tool-loader.test.ts +2 -195
- package/src/__tests__/workspace-tools-watcher-flag.test.ts +70 -0
- package/src/agent/loop.ts +0 -56
- package/src/api/index.ts +1 -19
- package/src/api/responses/llm-request-log-entry.ts +0 -29
- package/src/api/responses/subagent-detail.ts +0 -17
- package/src/api/surfaces.ts +3 -39
- package/src/approvals/guardian-request-resolvers.ts +11 -1
- package/src/calls/__tests__/relay-setup-router.test.ts +4 -262
- package/src/calls/call-domain.ts +3 -3
- package/src/calls/guardian-dispatch.ts +8 -10
- package/src/calls/inbound-trust-reader.ts +1 -17
- package/src/calls/media-stream-server.ts +0 -21
- package/src/calls/relay-server.ts +50 -167
- package/src/calls/relay-setup-router.ts +7 -37
- package/src/calls/relay-verification.ts +4 -4
- package/src/calls/stt-hints.ts +12 -9
- package/src/calls/twilio-routes.ts +4 -14
- package/src/channels/types.ts +20 -10
- package/src/cli/commands/__tests__/cache.test.ts +1 -8
- package/src/cli/commands/cache.ts +181 -194
- package/src/cli/commands/db/__tests__/repair.test.ts +5 -6
- package/src/cli/commands/db/status.ts +1 -37
- package/src/cli/commands/mcp.ts +218 -252
- package/src/cli/commands/memory/index.ts +0 -2
- package/src/cli/commands/plugins.ts +3 -75
- package/src/cli/lib/__tests__/install-from-github.test.ts +0 -102
- package/src/cli/lib/__tests__/list-installed-plugins.test.ts +1 -160
- package/src/cli/lib/list-installed-plugins.ts +1 -179
- package/src/config/__tests__/sync-gated-profiles.test.ts +3 -11
- package/src/config/bundled-skills/contacts/tools/contact-merge.ts +17 -27
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +3 -13
- package/src/config/bundled-skills/subagent/SKILL.md +1 -1
- package/src/config/bundled-skills/subagent/TOOLS.json +1 -1
- package/src/config/feature-flag-registry.json +13 -5
- package/src/config/loader.ts +5 -38
- package/src/config/schemas/__tests__/memory-v3.test.ts +0 -1
- package/src/config/schemas/memory-lifecycle.ts +0 -12
- package/src/config/schemas/memory-v3.ts +0 -7
- package/src/config/schemas/memory.ts +0 -4
- package/src/config/schemas/timeouts.ts +0 -8
- package/src/config/seed-inference-profiles.ts +11 -21
- package/src/config/skills.ts +5 -27
- package/src/config/sync-gated-profiles.ts +13 -12
- package/src/contacts/contacts-write.ts +0 -3
- package/src/daemon/assistant-attachments.ts +4 -27
- package/src/daemon/conversation-agent-loop.ts +0 -28
- package/src/daemon/conversation-process.ts +16 -35
- package/src/daemon/conversation-surfaces.ts +38 -111
- package/src/daemon/conversation-tool-setup.ts +16 -50
- package/src/daemon/conversation.ts +1 -13
- package/src/daemon/disk-pressure-guard.ts +2 -12
- package/src/daemon/event-loop-watchdog.ts +1 -28
- package/src/daemon/external-plugins-bootstrap.ts +34 -4
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -25
- package/src/daemon/handlers/config-a2a.ts +14 -6
- package/src/daemon/handlers/config-channels.ts +22 -78
- package/src/daemon/handlers/conversations.ts +0 -77
- package/src/daemon/lifecycle.ts +0 -4
- package/src/daemon/mcp-reload-service.ts +0 -10
- package/src/daemon/memory-v2-startup.test.ts +0 -72
- package/src/daemon/memory-v2-startup.ts +19 -87
- package/src/daemon/message-types/conversations.ts +0 -2
- package/src/daemon/message-types/surfaces.ts +12 -12
- package/src/daemon/server.ts +4 -0
- package/src/daemon/shutdown-handlers.ts +0 -20
- package/src/daemon/tool-setup-types.ts +0 -9
- package/src/daemon/workspace-tools-watcher.ts +328 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +1 -1
- package/src/ipc/assistant-server.ts +2 -2
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
- package/src/mcp/client.ts +1 -15
- package/src/mcp/mcp-auth-orchestrator.ts +1 -6
- package/src/mcp/mcp-oauth-provider.ts +8 -19
- package/src/memory/__tests__/memory-retrospective-job.test.ts +0 -8
- package/src/memory/conversation-crud.ts +0 -38
- package/src/memory/db-connection.ts +3 -22
- package/src/memory/db-init.ts +502 -36
- package/src/memory/db-singleton.ts +4 -6
- package/src/memory/jobs-worker.ts +0 -58
- package/src/memory/llm-request-log-store.ts +1 -26
- package/src/memory/llm-usage-store.ts +20 -48
- package/src/memory/memory-retrospective-job.ts +8 -9
- package/src/memory/migrations/209-strip-thinking-from-consolidated.ts +56 -130
- package/src/memory/migrations/__tests__/run-migrations.test.ts +2 -2
- package/src/memory/migrations/registry.ts +573 -0
- package/src/memory/migrations/run-migrations.ts +6 -90
- package/src/memory/migrations/validate-migration-state.ts +66 -101
- package/src/memory/schema/conversations.ts +0 -9
- package/src/memory/schema/infrastructure.ts +0 -20
- package/src/memory/v2/__tests__/cli-command-store.test.ts +0 -25
- package/src/memory/v2/__tests__/skill-store.test.ts +0 -80
- package/src/memory/v2/cli-command-store.ts +38 -75
- package/src/memory/v2/prompts/consolidation.ts +82 -13
- package/src/memory/v2/prompts/router.ts +93 -21
- package/src/memory/v2/skill-store.ts +31 -68
- package/src/notifications/__tests__/broadcaster.test.ts +8 -16
- package/src/notifications/__tests__/decision-engine.test.ts +9 -78
- package/src/notifications/broadcaster.ts +1 -8
- package/src/notifications/decision-engine.ts +7 -15
- package/src/notifications/destination-resolver.ts +24 -68
- package/src/notifications/emit-signal.ts +14 -39
- package/src/permissions/question-prompter.test.ts +1 -1
- package/src/permissions/question-prompter.ts +4 -7
- package/src/plugin-api/index.ts +6 -6
- package/src/plugin-api/types.ts +5 -3
- package/src/plugin-api/vision-support.test.ts +4 -28
- package/src/plugin-api/vision-support.ts +31 -66
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +0 -161
- package/src/plugins/defaults/advisor/consult.ts +6 -110
- package/src/plugins/defaults/advisor/steering.ts +2 -14
- package/src/plugins/defaults/advisor/tools/advisor.ts +5 -32
- package/src/plugins/defaults/exploration-drift/hooks/post-tool-use.ts +1 -2
- package/src/plugins/defaults/image-fallback/__tests__/image-fallback.test.ts +7 -47
- package/src/plugins/defaults/image-fallback/hooks/post-tool-use.ts +11 -10
- package/src/plugins/defaults/image-fallback/hooks/user-prompt-submit.ts +20 -12
- package/src/plugins/defaults/image-fallback/src/caption-blocks.ts +11 -42
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +3 -33
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +4 -48
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +8 -4
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +15 -43
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +2 -11
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +13 -77
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +11 -12
- package/src/plugins/mtime-cache.ts +291 -76
- package/src/plugins/pipeline.ts +13 -111
- package/src/plugins/types.ts +2 -0
- package/src/providers/anthropic/client.ts +0 -5
- package/src/providers/call-site-routing.ts +0 -4
- package/src/providers/model-catalog.ts +0 -16
- package/src/providers/openai/__tests__/api-error-detail.test.ts +120 -0
- package/src/providers/openai/chat-completions-provider.ts +83 -37
- package/src/providers/openai/responses-provider.ts +46 -50
- package/src/providers/openrouter/client.ts +0 -5
- package/src/providers/provider-send-message.ts +0 -4
- package/src/providers/ratelimit.ts +0 -4
- package/src/providers/retry.ts +0 -4
- package/src/providers/types.ts +0 -9
- package/src/providers/usage-tracking.ts +0 -4
- package/src/runtime/__tests__/trust-verdict-consumer.test.ts +3 -335
- package/src/runtime/access-request-helper.ts +39 -19
- package/src/runtime/actor-trust-resolver.ts +2 -2
- package/src/runtime/assistant-event-hub.ts +1 -1
- package/src/runtime/assistant-stream-state.ts +2 -9
- package/src/runtime/auth/require-bound-guardian.ts +11 -21
- package/src/runtime/channel-verification-service.ts +31 -56
- package/src/runtime/confirmation-request-guardian-bridge.ts +3 -3
- package/src/runtime/guardian-vellum-migration.ts +7 -66
- package/src/runtime/invite-redemption-service.ts +187 -198
- package/src/runtime/local-actor-identity.ts +11 -76
- package/src/runtime/pending-interactions.ts +1 -11
- package/src/runtime/routes/__tests__/channel-verification-revoke.test.ts +5 -56
- package/src/runtime/routes/__tests__/channel-verification-routes.test.ts +1 -1
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +0 -187
- package/src/runtime/routes/browser-routes.ts +1 -1
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +5 -13
- package/src/runtime/routes/channel-verification-routes.ts +3 -3
- package/src/runtime/routes/contact-routes.ts +32 -8
- package/src/runtime/routes/conversation-cli-routes.ts +5 -4
- package/src/runtime/routes/conversation-list-routes.ts +7 -4
- package/src/runtime/routes/conversation-query-routes.ts +0 -72
- package/src/runtime/routes/conversation-routes.ts +85 -84
- package/src/runtime/routes/events-routes.ts +2 -2
- package/src/runtime/routes/global-search-routes.ts +1 -3
- package/src/runtime/routes/guardian-action-routes.ts +5 -4
- package/src/runtime/routes/host-app-control-routes.ts +4 -5
- package/src/runtime/routes/host-bash-routes.ts +4 -5
- package/src/runtime/routes/host-browser-routes.ts +11 -9
- package/src/runtime/routes/host-cu-routes.ts +4 -5
- package/src/runtime/routes/host-file-routes.ts +4 -5
- 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 +2 -3
- package/src/runtime/routes/inbound-message-handler.ts +5 -5
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +5 -97
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +49 -61
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +4 -16
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +8 -21
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +3 -14
- package/src/runtime/routes/index.ts +0 -2
- package/src/runtime/routes/llm-context-normalization.ts +0 -83
- package/src/runtime/routes/mcp-auth-routes.ts +19 -171
- package/src/runtime/routes/migration-rollback-routes.ts +3 -4
- package/src/runtime/routes/migration-routes.ts +1 -4
- package/src/runtime/routes/subagents-routes.ts +0 -5
- package/src/runtime/routes/surface-action-routes.ts +56 -42
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +0 -1
- package/src/runtime/services/conversation-serializer.ts +9 -7
- package/src/runtime/tool-grant-request-helper.ts +3 -3
- package/src/runtime/trust-verdict-consumer.ts +9 -85
- package/src/runtime/verification-outbound-actions.ts +18 -18
- package/src/signals/user-message.ts +0 -16
- package/src/subagent/manager.ts +0 -9
- package/src/subagent/types.ts +3 -3
- package/src/telemetry/types.ts +1 -34
- package/src/telemetry/usage-telemetry-reporter.test.ts +2 -3
- package/src/telemetry/usage-telemetry-reporter.ts +3 -87
- package/src/tools/ask-question/ask-question-tool.test.ts +0 -29
- package/src/tools/ask-question/ask-question-tool.ts +0 -13
- package/src/tools/executor.ts +4 -4
- package/src/tools/registry.ts +0 -18
- package/src/tools/shared/filesystem/path-policy.ts +5 -12
- package/src/tools/tool-approval-handler.ts +1 -1
- package/src/tools/tool-defaults.ts +2 -9
- package/src/tools/tool-manifest.ts +0 -3
- package/src/tools/types.ts +2 -17
- package/src/tools/workspace-tools/loader.ts +244 -348
- package/src/util/errors.ts +1 -26
- package/src/util/platform.ts +0 -5
- package/src/workflows/library.test.ts +0 -140
- package/src/workflows/library.ts +28 -82
- package/src/workspace/migrations/017-seed-persona-dirs.ts +34 -3
- package/src/workspace/migrations/019-scope-journal-to-guardian.ts +24 -3
- package/src/workspace/migrations/048-remove-workspace-hooks.ts +66 -14
- package/src/workspace/migrations/registry.ts +0 -2
- package/node_modules/@vellumai/gateway-client/src/__tests__/guardian-delivery-contract.test.ts +0 -91
- package/node_modules/@vellumai/gateway-client/src/guardian-delivery-contract.ts +0 -48
- package/node_modules/@vellumai/service-contracts/src/__tests__/channels.test.ts +0 -28
- package/node_modules/@vellumai/service-contracts/src/channels.ts +0 -41
- package/src/__tests__/code-search-tool.test.ts +0 -585
- package/src/__tests__/guardian-expiry-notifier.test.ts +0 -282
- package/src/__tests__/mcp-config-secret-boundary.test.ts +0 -390
- package/src/__tests__/plugin-pipeline.test.ts +0 -96
- package/src/__tests__/sse-actor-principal-guardian-source.test.ts +0 -102
- package/src/__tests__/steer-on-enqueue-question.test.ts +0 -181
- package/src/__tests__/workspace-migration-111-prune-seeded-callsite-defaults.test.ts +0 -208
- package/src/agent/loop-exclusive-tool.test.ts +0 -150
- package/src/api/constants/sse-replay.ts +0 -41
- package/src/api/events/conversation-notice.ts +0 -26
- package/src/approvals/guardian-channel-delivery.ts +0 -30
- package/src/approvals/guardian-expiry-notifier.ts +0 -148
- package/src/cli/commands/memory/__tests__/worker.test.ts +0 -302
- package/src/cli/commands/memory/worker.ts +0 -175
- package/src/config/__tests__/loader-callsite-strip-fallback.test.ts +0 -143
- package/src/config/prune-seeded-callsite-defaults.ts +0 -110
- package/src/contacts/__tests__/contacts-write-revoke-relay.test.ts +0 -129
- package/src/contacts/__tests__/guardian-delivery-reader.test.ts +0 -312
- package/src/contacts/__tests__/member-write-relay.test.ts +0 -202
- package/src/contacts/guardian-delivery-reader.ts +0 -223
- package/src/contacts/member-write-relay.ts +0 -189
- package/src/daemon/conversation-notices.ts +0 -60
- package/src/daemon/handlers/__tests__/config-channels.test.ts +0 -225
- package/src/hooks/hook-loader.ts +0 -341
- package/src/mcp/mcp-header-store.ts +0 -134
- package/src/memory/__tests__/301-create-watchdog-events.test.ts +0 -110
- package/src/memory/__tests__/prompt-override.test.ts +0 -192
- package/src/memory/__tests__/watchdog-events-store.test.ts +0 -161
- package/src/memory/migrations/300-add-processing-started-at.ts +0 -30
- package/src/memory/migrations/301-create-watchdog-events.ts +0 -45
- package/src/memory/migrations/__tests__/209-strip-thinking-from-consolidated.test.ts +0 -224
- package/src/memory/prompt-override.ts +0 -129
- package/src/memory/steps.ts +0 -573
- package/src/memory/watchdog-events-store.ts +0 -87
- package/src/memory/worker-control.ts +0 -118
- package/src/memory/worker-process.ts +0 -72
- package/src/notifications/__tests__/connected-channels.test.ts +0 -114
- package/src/notifications/__tests__/destination-resolver.test.ts +0 -256
- package/src/onboarding/checkin-event.test.ts +0 -222
- package/src/onboarding/checkin-event.ts +0 -321
- package/src/onboarding/schedule-checkin.ts +0 -190
- package/src/plugins/defaults/advisor/__tests__/context-pack-gating.test.ts +0 -106
- package/src/plugins/defaults/advisor/__tests__/context-pack.test.ts +0 -60
- package/src/plugins/defaults/advisor/context-pack.ts +0 -288
- package/src/plugins/defaults/memory-v3-shadow/pool-select.test.ts +0 -146
- package/src/plugins/surface-import.ts +0 -121
- package/src/providers/openai/__tests__/api-error-normalization.test.ts +0 -321
- package/src/providers/openai/api-error-normalization.ts +0 -270
- package/src/runtime/__tests__/channel-verification-service.test.ts +0 -133
- package/src/runtime/__tests__/guardian-vellum-migration.test.ts +0 -181
- package/src/runtime/__tests__/is-guardian-bound-for-channel.test.ts +0 -66
- package/src/runtime/__tests__/local-principal-trust.test.ts +0 -164
- package/src/runtime/anchored-guardian.test.ts +0 -156
- package/src/runtime/anchored-guardian.ts +0 -135
- package/src/runtime/auth/__tests__/require-bound-guardian.test.ts +0 -99
- package/src/runtime/local-principal-trust.ts +0 -52
- package/src/runtime/routes/__tests__/contact-routes.test.ts +0 -212
- package/src/runtime/routes/__tests__/global-search-routes.test.ts +0 -93
- package/src/runtime/routes/onboarding-checkin-routes.ts +0 -86
- package/src/tools/filesystem/search.ts +0 -543
- package/src/util/telemetry-db-path.ts +0 -24
- package/src/workspace/migrations/111-prune-seeded-callsite-defaults.ts +0 -134
|
@@ -161,23 +161,22 @@ 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.
|
|
173
164
|
const nextEntries = new Map<string, CliCommandEntry>();
|
|
174
165
|
let denseVectors: number[][] = [];
|
|
175
|
-
let denseAvailable = false;
|
|
176
|
-
let denseError: unknown = null;
|
|
177
166
|
let encodeSparse: (
|
|
178
167
|
input: string,
|
|
179
168
|
) => ReturnType<typeof generateSparseEmbedding> = generateSparseEmbedding;
|
|
180
169
|
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
|
+
|
|
181
180
|
// CLI commands share the concept-page Qdrant collection, so the sparse
|
|
182
181
|
// vector must use the same stemmed BM25 encoding as the concept-page
|
|
183
182
|
// documents. Fall back to the legacy TF encoder only during the cold-
|
|
@@ -191,24 +190,6 @@ async function runSeedV2CliCommandEntries(generation: number): Promise<void> {
|
|
|
191
190
|
b: config.memory.v2.bm25_b,
|
|
192
191
|
})
|
|
193
192
|
: 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
|
-
}
|
|
212
193
|
}
|
|
213
194
|
|
|
214
195
|
if (generation !== requestedSeedGeneration) {
|
|
@@ -220,17 +201,7 @@ async function runSeedV2CliCommandEntries(generation: number): Promise<void> {
|
|
|
220
201
|
return;
|
|
221
202
|
}
|
|
222
203
|
|
|
223
|
-
|
|
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) {
|
|
204
|
+
if (seeds.length > 0) {
|
|
234
205
|
const now = Date.now();
|
|
235
206
|
await Promise.all(
|
|
236
207
|
seeds.map((seed, i) =>
|
|
@@ -243,48 +214,40 @@ async function runSeedV2CliCommandEntries(generation: number): Promise<void> {
|
|
|
243
214
|
}),
|
|
244
215
|
),
|
|
245
216
|
);
|
|
217
|
+
for (const seed of seeds) {
|
|
218
|
+
nextEntries.set(seed.id, seed);
|
|
219
|
+
}
|
|
246
220
|
}
|
|
247
221
|
|
|
248
|
-
// The CLI tree is always available (no remote catalog), so pruning
|
|
249
|
-
//
|
|
250
|
-
//
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
log.warn(
|
|
266
|
-
{ err },
|
|
267
|
-
"Failed to backfill kind on legacy CLI-command points — pruning may leave orphans this run",
|
|
268
|
-
);
|
|
269
|
-
}
|
|
222
|
+
// The CLI tree is always available (no remote catalog), so pruning is
|
|
223
|
+
// unconditional. Run the legacy `kind` backfill once per process so
|
|
224
|
+
// pre-discriminator rows become prunable.
|
|
225
|
+
const knownIds = new Set(seeds.map((s) => s.id));
|
|
226
|
+
if (!legacyKindBackfillDone) {
|
|
227
|
+
try {
|
|
228
|
+
await backfillKindOnPointsWithPrefix(
|
|
229
|
+
CLI_COMMAND_SLUG_PREFIX,
|
|
230
|
+
CLI_COMMAND_PAYLOAD_KIND,
|
|
231
|
+
knownIds,
|
|
232
|
+
);
|
|
233
|
+
legacyKindBackfillDone = true;
|
|
234
|
+
} catch (err) {
|
|
235
|
+
log.warn(
|
|
236
|
+
{ err },
|
|
237
|
+
"Failed to backfill kind on legacy CLI-command points — pruning may leave orphans this run",
|
|
238
|
+
);
|
|
270
239
|
}
|
|
271
|
-
await pruneSlugsWithPrefixExcept(
|
|
272
|
-
CLI_COMMAND_SLUG_PREFIX,
|
|
273
|
-
seeds.map((s) => s.id),
|
|
274
|
-
{ kind: CLI_COMMAND_PAYLOAD_KIND },
|
|
275
|
-
);
|
|
276
240
|
}
|
|
241
|
+
await pruneSlugsWithPrefixExcept(
|
|
242
|
+
CLI_COMMAND_SLUG_PREFIX,
|
|
243
|
+
seeds.map((s) => s.id),
|
|
244
|
+
{ kind: CLI_COMMAND_PAYLOAD_KIND },
|
|
245
|
+
);
|
|
277
246
|
|
|
278
|
-
// Atomically replace the cache
|
|
279
|
-
// the page-index cache so the next router invocation observes the new
|
|
280
|
-
// command set.
|
|
247
|
+
// Atomically replace the cache only after every step above succeeds.
|
|
281
248
|
entries = nextEntries;
|
|
282
249
|
invalidatePageIndex();
|
|
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;
|
|
250
|
+
lastSeedError = null;
|
|
288
251
|
} catch (err) {
|
|
289
252
|
lastSeedError = err;
|
|
290
253
|
log.warn({ err }, "Failed to seed v2 CLI-command entries");
|
|
@@ -18,9 +18,12 @@
|
|
|
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
|
+
|
|
21
25
|
import { getLogger } from "../../../util/logger.js";
|
|
22
26
|
import { getWorkspaceDir } from "../../../util/platform.js";
|
|
23
|
-
import { loadPromptOverride } from "../../prompt-override.js";
|
|
24
27
|
|
|
25
28
|
const log = getLogger("memory-v2-consolidate-prompt");
|
|
26
29
|
|
|
@@ -58,6 +61,14 @@ export const CORE_PAGES_CONSOLIDATION_SECTION = `## 10. Review \`memory/core-pag
|
|
|
58
61
|
|
|
59
62
|
`;
|
|
60
63
|
|
|
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
|
+
|
|
61
72
|
/**
|
|
62
73
|
* Consolidation prompt — live-mode only. The agent runs as itself (full
|
|
63
74
|
* SOUL.md + IDENTITY.md + persona + memory autoloads) with the standard
|
|
@@ -826,32 +837,90 @@ export function renderConsolidationPrompt(
|
|
|
826
837
|
/**
|
|
827
838
|
* Load the consolidation prompt template, optionally overridden from the file
|
|
828
839
|
* referenced by `memory.v2.consolidation_prompt_path`, then substitute
|
|
829
|
-
* `{{CUTOFF}}`.
|
|
830
|
-
* fall-back to the bundled prompt on a missing/unreadable/empty/oversized
|
|
831
|
-
* override) is handled by the shared {@link loadPromptOverride}.
|
|
840
|
+
* `{{CUTOFF}}`. Path-resolution rules are documented on the schema field.
|
|
832
841
|
*
|
|
833
842
|
* Override files get the same placeholder substitutions as the bundled
|
|
834
843
|
* template: `{{CUTOFF}}` always, and `{{CORE_PAGES_SECTION}}` per the same
|
|
835
844
|
* flag gate — so a prompt copied from the bundled source never leaks a raw
|
|
836
845
|
* 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."
|
|
837
851
|
*/
|
|
838
852
|
export function resolveConsolidationPrompt(
|
|
839
853
|
overridePath: string | null,
|
|
840
854
|
cutoff: string,
|
|
841
855
|
options: ConsolidationPromptOptions,
|
|
842
856
|
): string {
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
857
|
+
if (overridePath === null) return renderConsolidationPrompt(cutoff, options);
|
|
858
|
+
|
|
859
|
+
const resolvedPath = resolveOverridePath(overridePath);
|
|
860
|
+
let contents: string;
|
|
861
|
+
try {
|
|
862
|
+
const stat = lstatSync(resolvedPath);
|
|
863
|
+
if (!stat.isFile()) {
|
|
864
|
+
log.warn(
|
|
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
|
|
852
913
|
.replaceAll(CUTOFF_PLACEHOLDER, cutoff)
|
|
853
914
|
.replaceAll(
|
|
854
915
|
CORE_PAGES_PLACEHOLDER,
|
|
855
916
|
options.includeCorePagesSection ? CORE_PAGES_CONSOLIDATION_SECTION : "",
|
|
856
917
|
);
|
|
857
918
|
}
|
|
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,14 +22,22 @@
|
|
|
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
|
+
|
|
25
29
|
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
|
+
|
|
33
41
|
/** Sentinel substituted with the assistant's display name at runtime. */
|
|
34
42
|
const ASSISTANT_NAME_PLACEHOLDER = "{{ASSISTANT_NAME}}";
|
|
35
43
|
|
|
@@ -94,12 +102,14 @@ export function renderRouterPrompt(opts: RenderRouterPromptOpts): string {
|
|
|
94
102
|
/**
|
|
95
103
|
* Load the router prompt template, optionally overridden from the file
|
|
96
104
|
* referenced by `memory.v2.router.router_prompt_path`, then substitute the
|
|
97
|
-
* standard placeholders.
|
|
98
|
-
*
|
|
99
|
-
*
|
|
105
|
+
* standard placeholders. Path-resolution rules mirror the consolidation
|
|
106
|
+
* prompt override: absolute paths used as-is, leading `~/` expanded to home,
|
|
107
|
+
* relative paths resolved under `workspaceDir`.
|
|
100
108
|
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
109
|
+
* Failure handling is intentionally permissive — missing file, read error,
|
|
110
|
+
* oversized file, or empty/whitespace-only body all log a warning and fall
|
|
111
|
+
* back to the bundled prompt. Router selection must never break because of
|
|
112
|
+
* a bad override.
|
|
103
113
|
*/
|
|
104
114
|
export function resolveRouterPrompt(
|
|
105
115
|
overridePath: string | null,
|
|
@@ -107,15 +117,17 @@ export function resolveRouterPrompt(
|
|
|
107
117
|
opts: RenderRouterPromptOpts,
|
|
108
118
|
inlineOverride?: string | null,
|
|
109
119
|
): string {
|
|
110
|
-
// Inline override takes precedence over the
|
|
111
|
-
//
|
|
112
|
-
//
|
|
120
|
+
// Inline override (e.g. simulator playground) takes precedence over the
|
|
121
|
+
// configured file path and the bundled prompt. Same placeholder
|
|
122
|
+
// substitution + size guard as the file-path branch; empty/whitespace
|
|
123
|
+
// bodies fall through to file/bundled resolution so a "cleared" textarea
|
|
124
|
+
// is treated as no override.
|
|
113
125
|
if (inlineOverride !== undefined && inlineOverride !== null) {
|
|
114
|
-
if (inlineOverride.length >
|
|
126
|
+
if (inlineOverride.length > MAX_PROMPT_BYTES) {
|
|
115
127
|
log.warn(
|
|
116
128
|
{
|
|
117
129
|
size: inlineOverride.length,
|
|
118
|
-
limit:
|
|
130
|
+
limit: MAX_PROMPT_BYTES,
|
|
119
131
|
reason: "oversized_inline_override",
|
|
120
132
|
fallback: "path_or_bundled",
|
|
121
133
|
},
|
|
@@ -126,13 +138,62 @@ export function resolveRouterPrompt(
|
|
|
126
138
|
}
|
|
127
139
|
}
|
|
128
140
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
141
|
+
if (overridePath === null) return renderRouterPrompt(opts);
|
|
142
|
+
|
|
143
|
+
const resolvedPath = resolveOverridePath(overridePath, workspaceDir);
|
|
144
|
+
let contents: string;
|
|
145
|
+
try {
|
|
146
|
+
const stat = lstatSync(resolvedPath);
|
|
147
|
+
if (!stat.isFile()) {
|
|
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);
|
|
136
197
|
}
|
|
137
198
|
|
|
138
199
|
function substitutePlaceholders(
|
|
@@ -146,3 +207,14 @@ function substitutePlaceholders(
|
|
|
146
207
|
.replaceAll(USER_NAME_PLACEHOLDER, () => user)
|
|
147
208
|
.replaceAll(PAGE_INDEX_PLACEHOLDER, () => opts.pageIndexBlock);
|
|
148
209
|
}
|
|
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
|
+
}
|
|
@@ -16,11 +16,8 @@
|
|
|
16
16
|
//
|
|
17
17
|
// Skill entries are kept in a small in-process cache so the render path can
|
|
18
18
|
// fetch a `SkillEntry` synchronously by id without round-tripping to Qdrant.
|
|
19
|
-
// The cache is replaced atomically
|
|
20
|
-
//
|
|
21
|
-
// case only the dense Qdrant write is deferred so the cache (and the v3 needle
|
|
22
|
-
// lane it feeds) still reflects the current skills. An unexpected error in
|
|
23
|
-
// another step leaves the prior cache intact (skills are best-effort).
|
|
19
|
+
// The cache is replaced atomically at the end of a successful seed run; on
|
|
20
|
+
// error the prior cache stays intact (skills are best-effort).
|
|
24
21
|
|
|
25
22
|
import { isAssistantFeatureFlagEnabled } from "../../config/assistant-feature-flags.js";
|
|
26
23
|
import { getConfig } from "../../config/loader.js";
|
|
@@ -231,26 +228,26 @@ async function runSeedV2SkillEntries(generation: number): Promise<void> {
|
|
|
231
228
|
);
|
|
232
229
|
}
|
|
233
230
|
|
|
234
|
-
//
|
|
235
|
-
//
|
|
236
|
-
//
|
|
237
|
-
//
|
|
238
|
-
//
|
|
239
|
-
// A dense-embed failure is non-fatal to the in-memory cache: the v3 needle
|
|
240
|
-
// finder lane and always-candidate skill pinning read skills from `entries`
|
|
241
|
-
// / the page index, NOT from Qdrant, so the cache is populated from the
|
|
242
|
-
// local catalog regardless of backend state and skills stay discoverable
|
|
243
|
-
// from first boot. Only the dense Qdrant upsert is skipped; the managed-
|
|
244
|
-
// credential reseed (`maybeReseedCapabilitiesAfterManagedCredential`) and
|
|
245
|
-
// the v3 maintain pass backfill the dense vectors once the backend recovers.
|
|
231
|
+
// Embed all content strings in one batched call when there is anything to
|
|
232
|
+
// embed. Skipping the call when `seeds` is empty avoids throwing on an
|
|
233
|
+
// unavailable embedding backend in the all-disabled case, so pruning and
|
|
234
|
+
// cache replacement still run and clear stale state.
|
|
246
235
|
const nextEntries = new Map<string, SkillEntry>();
|
|
247
236
|
let denseVectors: number[][] = [];
|
|
248
|
-
let denseAvailable = false;
|
|
249
|
-
let denseError: unknown = null;
|
|
250
237
|
let encodeSparse: (
|
|
251
238
|
input: string,
|
|
252
239
|
) => ReturnType<typeof generateSparseEmbedding> = generateSparseEmbedding;
|
|
253
240
|
if (seeds.length > 0) {
|
|
241
|
+
const embedded = await embedWithBackend(
|
|
242
|
+
config,
|
|
243
|
+
seeds.map((s) => s.content),
|
|
244
|
+
);
|
|
245
|
+
denseVectors = await Promise.all(
|
|
246
|
+
embedded.vectors.map((v) =>
|
|
247
|
+
applyCorrectionIfCalibrated(v, embedded.provider, embedded.model),
|
|
248
|
+
),
|
|
249
|
+
);
|
|
250
|
+
|
|
254
251
|
// Skills share the concept-page Qdrant collection, so the sparse vector
|
|
255
252
|
// must use the same stemmed BM25 encoding the concept-page documents
|
|
256
253
|
// carry — otherwise the stemmed BM25 query vectors used by callers (see
|
|
@@ -266,24 +263,6 @@ async function runSeedV2SkillEntries(generation: number): Promise<void> {
|
|
|
266
263
|
b: config.memory.v2.bm25_b,
|
|
267
264
|
})
|
|
268
265
|
: generateSparseEmbedding(input);
|
|
269
|
-
try {
|
|
270
|
-
const embedded = await embedWithBackend(
|
|
271
|
-
config,
|
|
272
|
-
seeds.map((s) => s.content),
|
|
273
|
-
);
|
|
274
|
-
denseVectors = await Promise.all(
|
|
275
|
-
embedded.vectors.map((v) =>
|
|
276
|
-
applyCorrectionIfCalibrated(v, embedded.provider, embedded.model),
|
|
277
|
-
),
|
|
278
|
-
);
|
|
279
|
-
denseAvailable = true;
|
|
280
|
-
} catch (err) {
|
|
281
|
-
denseError = err;
|
|
282
|
-
log.warn(
|
|
283
|
-
{ err },
|
|
284
|
-
"Embedding backend unavailable — seeding skill cache without dense Qdrant vectors; the needle lane surfaces skills from the cache and the dense lane backfills when the backend recovers",
|
|
285
|
-
);
|
|
286
|
-
}
|
|
287
266
|
}
|
|
288
267
|
|
|
289
268
|
if (generation !== requestedSeedGeneration) {
|
|
@@ -295,17 +274,7 @@ async function runSeedV2SkillEntries(generation: number): Promise<void> {
|
|
|
295
274
|
return;
|
|
296
275
|
}
|
|
297
276
|
|
|
298
|
-
|
|
299
|
-
// from the local catalog regardless of dense availability.
|
|
300
|
-
for (const seed of seeds) {
|
|
301
|
-
nextEntries.set(seed.id, seed);
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
// Write Qdrant points only when dense vectors were produced. In the
|
|
305
|
-
// degraded (backend-unavailable) path we skip Qdrant mutation entirely —
|
|
306
|
-
// both the upsert and the prune below — so we never write half-formed
|
|
307
|
-
// points or reconcile the collection against a set we did not persist.
|
|
308
|
-
if (seeds.length > 0 && denseAvailable) {
|
|
277
|
+
if (seeds.length > 0) {
|
|
309
278
|
const now = Date.now();
|
|
310
279
|
await Promise.all(
|
|
311
280
|
seeds.map((seed, i) =>
|
|
@@ -318,15 +287,16 @@ async function runSeedV2SkillEntries(generation: number): Promise<void> {
|
|
|
318
287
|
}),
|
|
319
288
|
),
|
|
320
289
|
);
|
|
290
|
+
for (const seed of seeds) {
|
|
291
|
+
nextEntries.set(seed.id, seed);
|
|
292
|
+
}
|
|
321
293
|
}
|
|
322
294
|
|
|
323
|
-
// Prune stale skill slugs.
|
|
324
|
-
// from network failure or cold cache
|
|
325
|
-
// catalog skills should exist
|
|
326
|
-
//
|
|
327
|
-
|
|
328
|
-
// all-disabled case clears stale rows.
|
|
329
|
-
if (catalogAvailable && (denseAvailable || seeds.length === 0)) {
|
|
295
|
+
// Prune stale skill slugs. When the catalog is unavailable (empty array
|
|
296
|
+
// from network failure or cold cache), we cannot enumerate which
|
|
297
|
+
// uninstalled catalog skills should exist, so skip pruning entirely to
|
|
298
|
+
// avoid aggressively removing previously-seeded catalog skill embeddings.
|
|
299
|
+
if (catalogAvailable) {
|
|
330
300
|
// Tag legacy skill points missing `payload.kind` before pruning so the
|
|
331
301
|
// kind-scoped prune can see them. Once-per-process; the backfill is
|
|
332
302
|
// idempotent (server-side `is_empty` filter), so a partial failure
|
|
@@ -351,26 +321,19 @@ async function runSeedV2SkillEntries(generation: number): Promise<void> {
|
|
|
351
321
|
seeds.map((s) => s.id),
|
|
352
322
|
{ kind: SKILL_PAYLOAD_KIND },
|
|
353
323
|
);
|
|
354
|
-
} else
|
|
324
|
+
} else {
|
|
355
325
|
log.info(
|
|
356
326
|
"Catalog unavailable — skipping skill pruning to preserve prior catalog embeddings",
|
|
357
327
|
);
|
|
358
328
|
}
|
|
359
329
|
|
|
360
|
-
// Atomically replace the cache
|
|
361
|
-
// resolution (`resolveSkillStates`) is authoritative, so a skill the config
|
|
362
|
-
// just disabled or removed drops out here even when the remote catalog is
|
|
363
|
-
// unavailable. Drop the page-index cache so the next router invocation
|
|
364
|
-
// observes the new skill set (skill entries share the unified concept-page
|
|
365
|
-
// collection and surface in the same index).
|
|
330
|
+
// Atomically replace the cache only after every step above succeeds.
|
|
366
331
|
entries = nextEntries;
|
|
332
|
+
// Drop the page-index cache so the next router invocation observes the
|
|
333
|
+
// freshly seeded skill set (skill entries share the unified concept-page
|
|
334
|
+
// collection and surface in the same index).
|
|
367
335
|
invalidatePageIndex();
|
|
368
|
-
|
|
369
|
-
// Surface a dense-embed failure to `throwOnError` callers (the managed-
|
|
370
|
-
// credential reseed and the operator reembed route) so the existing retry +
|
|
371
|
-
// maintain machinery backfills the dense lane. The in-memory cache is
|
|
372
|
-
// already updated above, so the needle lane is fixed regardless.
|
|
373
|
-
lastSeedError = denseError;
|
|
336
|
+
lastSeedError = null;
|
|
374
337
|
} catch (err) {
|
|
375
338
|
lastSeedError = err;
|
|
376
339
|
log.warn({ err }, "Failed to seed v2 skill entries");
|
|
@@ -27,22 +27,14 @@ mock.module("../copy-composer.js", () => ({
|
|
|
27
27
|
composeFallbackCopy: () => composeFallbackReturn,
|
|
28
28
|
}));
|
|
29
29
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
);
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}));
|
|
39
|
-
|
|
40
|
-
// Use the real destination-resolver (DB-free via the local-read stub below)
|
|
41
|
-
// so this mock does not leak into destination-resolver.test.ts under a shared
|
|
42
|
-
// bun-test invocation. With no guardian, the resolver still yields a vellum
|
|
43
|
-
// destination, which is all these tests exercise.
|
|
44
|
-
mock.module("../../contacts/contact-store.js", () => ({
|
|
45
|
-
findGuardianForChannel: () => null,
|
|
30
|
+
mock.module("../destination-resolver.js", () => ({
|
|
31
|
+
resolveDestinations: (channels: string[]) => {
|
|
32
|
+
const map = new Map<string, ChannelDestination>();
|
|
33
|
+
for (const ch of channels) {
|
|
34
|
+
map.set(ch, { channel: ch as ChannelDestination["channel"] });
|
|
35
|
+
}
|
|
36
|
+
return map;
|
|
37
|
+
},
|
|
46
38
|
}));
|
|
47
39
|
|
|
48
40
|
mock.module("../conversation-pairing.js", () => ({
|