@vellumai/assistant 0.9.0 → 0.10.0-staging.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +18 -34
- package/bun.lock +7 -8
- package/docs/activation-funnel-telemetry.md +28 -22
- package/docs/architecture/security.md +29 -28
- package/docs/stt-provider-onboarding.md +3 -5
- package/docs/workflows-testing.md +13 -44
- package/docs/workflows.md +3 -5
- package/node_modules/@vellumai/ces-client/src/__tests__/ces-client.test.ts +47 -0
- package/node_modules/@vellumai/ces-client/src/rpc-client.ts +28 -5
- package/node_modules/@vellumai/environments/src/seeds.ts +2 -5
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +97 -0
- package/node_modules/@vellumai/gateway-client/src/inbound-contract.ts +10 -0
- package/node_modules/@vellumai/gateway-client/src/index.ts +32 -6
- package/node_modules/@vellumai/gateway-client/src/outbound-contract.ts +119 -0
- package/node_modules/@vellumai/gateway-client/src/types.ts +15 -84
- package/openapi.yaml +976 -63
- package/package.json +2 -1
- package/scripts/sync-llm-catalog.ts +6 -15
- package/scripts/sync-web-search-catalog.ts +3 -11
- package/src/__tests__/access-request-card-view.test.ts +98 -0
- package/src/__tests__/access-request-seed-content-blocks.test.ts +2 -4
- package/src/__tests__/actor-trust-resolver-address-fallback.test.ts +72 -32
- package/src/__tests__/agent-loop-compaction-strip.test.ts +241 -0
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +16 -13
- package/src/__tests__/agent-loop-output-hooks.test.ts +69 -0
- package/src/__tests__/agent-loop-override-profile.test.ts +25 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -3
- package/src/__tests__/app-compiler.test.ts +15 -1
- package/src/__tests__/app-dir-path-guard.test.ts +0 -1
- package/src/__tests__/assistant-feature-flag-guard.test.ts +1 -4
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
- package/src/__tests__/auth-fallback-events-store.test.ts +6 -14
- package/src/__tests__/avatar-identity-sync.test.ts +2 -27
- package/src/__tests__/btw-routes.test.ts +6 -8
- package/src/__tests__/call-pointer-messages.test.ts +28 -0
- package/src/__tests__/cancel-clears-processing.test.ts +89 -0
- package/src/__tests__/channel-approval-routes.test.ts +0 -4
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +5 -15
- package/src/__tests__/checker.test.ts +0 -3
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +3 -4
- package/src/__tests__/compactor-image-manifest-trust.test.ts +21 -1
- package/src/__tests__/compactor-summary-call-truncation.test.ts +223 -0
- package/src/__tests__/config-loader-backfill.test.ts +268 -27
- package/src/__tests__/config-schema.test.ts +35 -0
- package/src/__tests__/config-watcher.test.ts +0 -18
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -2
- package/src/__tests__/contact-store-user-file.test.ts +0 -6
- package/src/__tests__/contacts-tools.test.ts +29 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +22 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +58 -0
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -1
- package/src/__tests__/conversation-lifecycle.test.ts +7 -9
- package/src/__tests__/conversation-load-history-repair.test.ts +101 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +15 -12
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +6 -3
- package/src/__tests__/conversation-title-service.test.ts +62 -0
- package/src/__tests__/credential-broker.test.ts +449 -1
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +18 -11
- package/src/__tests__/credential-execution-tools.test.ts +0 -1
- package/src/__tests__/credential-prompt-route.test.ts +4 -4
- package/src/__tests__/credential-routes.test.ts +360 -0
- package/src/__tests__/credential-security-invariants.test.ts +4 -13
- package/src/__tests__/disk-pressure-policy.test.ts +12 -0
- package/src/__tests__/disk-usage.test.ts +65 -0
- package/src/__tests__/dynamic-page-surface.test.ts +152 -1
- package/src/__tests__/fixtures/credential-security-fixtures.ts +2 -33
- package/src/__tests__/gateway-flag-listener.test.ts +110 -1
- package/src/__tests__/gateway-only-guard.test.ts +3 -7
- package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
- package/src/__tests__/guardian-card-withdrawal.test.ts +403 -0
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +5 -3
- package/src/__tests__/guardian-grant-minting.test.ts +3 -35
- package/src/__tests__/guardian-routing-invariants.test.ts +64 -26
- package/src/__tests__/guardian-routing-state.test.ts +0 -1
- package/src/__tests__/headless-browser-mode.test.ts +10 -0
- package/src/__tests__/headless-browser-navigate.test.ts +8 -3
- package/src/__tests__/helpers/create-guardian-binding.ts +0 -1
- package/src/__tests__/host-browser-proxy.test.ts +87 -0
- package/src/__tests__/identity-routes.test.ts +0 -189
- package/src/__tests__/inbound-invite-redemption.test.ts +4 -4
- package/src/__tests__/injector-v3-suppression.test.ts +27 -20
- package/src/__tests__/internal-telemetry-routes.test.ts +6 -14
- package/src/__tests__/invite-redemption-service.test.ts +4 -7
- package/src/__tests__/llm-callsite-catalog.test.ts +5 -6
- package/src/__tests__/llm-catalog-parity.test.ts +30 -23
- package/src/__tests__/llm-resolver.test.ts +70 -24
- package/src/__tests__/llm-schema.test.ts +1 -0
- package/src/__tests__/managed-profile-guard.test.ts +163 -4
- package/src/__tests__/mcp-health-check.test.ts +6 -7
- package/src/__tests__/media-stream-server-integration.test.ts +317 -13
- package/src/__tests__/oauth-provider-seed-logos.test.ts +4 -6
- package/src/__tests__/onboarding-persona-write.test.ts +1 -1
- package/src/__tests__/path-policy.test.ts +34 -0
- package/src/__tests__/persona-resolver.test.ts +49 -14
- package/src/__tests__/plugin-api-model-profiles.test.ts +178 -0
- package/src/__tests__/plugin-api-provider.test.ts +24 -0
- package/src/__tests__/plugin-tool-contribution.test.ts +6 -3
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +214 -0
- package/src/__tests__/provider-send-message-override-profile.test.ts +76 -0
- package/src/__tests__/reaction-persistence.test.ts +150 -29
- package/src/__tests__/registry.test.ts +2 -7
- package/src/__tests__/relay-server.test.ts +285 -0
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/schedule-routes-workflow-validation.test.ts +1 -10
- package/src/__tests__/schedule-routes.test.ts +0 -30
- package/src/__tests__/schedule-tools.test.ts +2 -18
- package/src/__tests__/scheduler-reuse-conversation.test.ts +8 -5
- package/src/__tests__/skill-execute-input.test.ts +51 -1
- package/src/__tests__/skill-runtime-path.test.ts +2 -3
- package/src/__tests__/skills.test.ts +51 -0
- package/src/__tests__/slack-notification-approval-card.test.ts +176 -0
- package/src/__tests__/slack-reaction-canonical-approval.test.ts +285 -0
- package/src/__tests__/subagent-tools.test.ts +266 -0
- package/src/__tests__/surface-completion-nudge-hook.test.ts +367 -0
- package/src/__tests__/task-progress-nudge-hook.test.ts +1 -1
- package/src/__tests__/title-generate-hook.test.ts +100 -3
- package/src/__tests__/token-estimator-accuracy.benchmark.test.ts +1 -29
- package/src/__tests__/token-manager.test.ts +519 -0
- package/src/__tests__/tool-approval-seed-content-blocks.test.ts +1 -1
- package/src/__tests__/tool-audit-listener.test.ts +7 -7
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +6 -3
- package/src/__tests__/tool-executor.test.ts +0 -79
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +4 -2
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +220 -3
- package/src/__tests__/trusted-contact-multichannel.test.ts +3 -3
- package/src/__tests__/trusted-contact-verification.test.ts +8 -10
- package/src/__tests__/twilio-routes.test.ts +81 -1
- package/src/__tests__/voice-invite-redemption.test.ts +2 -3
- package/src/__tests__/weak-open-model.test.ts +30 -0
- package/src/__tests__/web-search-catalog-parity.test.ts +6 -25
- package/src/__tests__/workspace-greetings.test.ts +152 -0
- package/src/__tests__/workspace-migration-105-enable-memory-v3-live-for-new-workspaces.test.ts +149 -0
- package/src/__tests__/workspace-migration-108-drop-balanced-economy-profile.test.ts +285 -0
- package/src/__tests__/workspace-migration-add-send-diagnostics.test.ts +1 -1
- package/src/__tests__/workspace-migration-drop-collect-usage-data.test.ts +118 -0
- package/src/__tests__/workspace-migration-drop-send-diagnostics.test.ts +118 -0
- package/src/a2a/__tests__/e2e-a2a-channel.test.ts +0 -4
- package/src/agent/loop.ts +49 -29
- package/src/api/README.md +6 -6
- package/src/api/events/tool-result.ts +6 -0
- package/src/api/events/workflow-completed.ts +53 -0
- package/src/api/events/workflow-leaf-finished.ts +38 -0
- package/src/api/events/workflow-leaf-started.ts +35 -0
- package/src/api/events/workflow-progress.ts +32 -0
- package/src/api/events/workflow-started.ts +31 -0
- package/src/api/index.ts +40 -0
- package/src/api/responses/conversation-message.ts +28 -4
- package/src/api/responses/home.ts +26 -4
- package/src/api/responses/workflow-journal.ts +53 -0
- package/src/approvals/guardian-card-withdrawal.ts +145 -0
- package/src/approvals/guardian-decision-primitive.ts +26 -3
- package/src/approvals/guardian-request-resolvers.ts +183 -80
- package/src/calls/__tests__/channel-admission-reader.test.ts +132 -0
- package/src/calls/__tests__/relay-setup-router.test.ts +350 -0
- package/src/calls/call-pointer-messages.ts +10 -4
- package/src/calls/channel-admission-reader.ts +104 -0
- package/src/calls/guardian-dispatch.ts +17 -45
- package/src/calls/media-stream-server.ts +84 -2
- package/src/calls/relay-access-wait.ts +1 -1
- package/src/calls/relay-server.ts +66 -0
- package/src/calls/relay-setup-router.ts +82 -1
- package/src/calls/twilio-routes.ts +17 -8
- package/src/calls/voice-session-bridge.ts +2 -2
- package/src/cli/commands/clients.ts +3 -0
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2-compare-render.test.ts +1 -1
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v2.test.ts +8 -7
- package/src/cli/commands/{__tests__ → memory/__tests__}/memory-v3.test.ts +5 -4
- package/src/cli/commands/memory/index.ts +30 -0
- package/src/cli/commands/{memory-v2-compare-render.ts → memory/memory-v2-compare-render.ts} +1 -1
- package/src/cli/commands/{memory-v2.ts → memory/memory-v2.ts} +6 -15
- package/src/cli/commands/{memory-v3.ts → memory/memory-v3.ts} +97 -11
- package/src/cli/commands/oauth/status.test.ts +36 -0
- package/src/cli/commands/oauth/status.ts +23 -3
- package/src/cli/commands/plugins.ts +197 -4
- package/src/cli/lib/__tests__/diff-plugin.test.ts +443 -0
- package/src/cli/lib/__tests__/inspect-plugin.test.ts +54 -0
- package/src/cli/lib/__tests__/merge-plugin-tree.test.ts +443 -0
- package/src/cli/lib/__tests__/plugin-surfaces.test.ts +111 -0
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +295 -2
- package/src/cli/lib/diff-plugin.ts +346 -0
- package/src/cli/lib/inspect-plugin.ts +12 -1
- package/src/cli/lib/install-from-github.ts +105 -17
- package/src/cli/lib/merge-plugin-tree.ts +328 -0
- package/src/cli/lib/plugin-fingerprint.ts +14 -0
- package/src/cli/lib/plugin-surfaces.ts +104 -0
- package/src/cli/lib/upgrade-plugin.ts +298 -10
- package/src/cli/program.ts +2 -6
- package/src/config/__tests__/sync-gated-profiles.test.ts +368 -0
- package/src/config/assistant-feature-flags.ts +22 -7
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +0 -1
- package/src/config/bundled-skills/messaging/SKILL.md +6 -4
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +9 -8
- package/src/config/bundled-skills/subagent/SKILL.md +4 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +4 -0
- package/src/config/bundled-skills/workflows/SKILL.md +14 -8
- package/src/config/bundled-tool-registry.ts +2 -7
- package/src/config/call-site-defaults.ts +15 -2
- package/src/config/feature-flag-registry.json +46 -31
- package/src/config/inference-profile-validation.ts +26 -0
- package/src/config/llm-resolver.ts +3 -0
- package/src/config/loader.ts +4 -0
- package/src/config/memory-v3-gate.ts +11 -0
- package/src/config/profile-order.ts +28 -0
- package/src/config/schema.ts +8 -6
- package/src/config/schemas/__tests__/memory-v3.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +7 -0
- package/src/config/schemas/channels.ts +11 -0
- package/src/config/schemas/elevenlabs.ts +0 -1
- package/src/config/schemas/llm.ts +31 -0
- package/src/config/schemas/memory-lifecycle.ts +3 -7
- package/src/config/schemas/memory-v3.ts +6 -0
- package/src/config/schemas/platform.ts +0 -8
- package/src/config/schemas/services.ts +18 -0
- package/src/config/seed-inference-profiles.ts +109 -44
- package/src/config/skills.ts +21 -0
- package/src/config/sync-gated-profiles.ts +220 -0
- package/src/contacts/contact-store.ts +89 -106
- package/src/contacts/contacts-write.ts +5 -22
- package/src/contacts/types.ts +0 -1
- package/src/context/compactor.ts +88 -54
- package/src/context/strip-injections.ts +58 -10
- package/src/context/token-estimator.ts +1 -1
- package/src/credential-execution/process-manager.ts +55 -14
- package/src/credential-execution/prompted-credential.ts +2 -3
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +3 -2
- package/src/daemon/config-watcher.ts +0 -4
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -0
- package/src/daemon/conversation-agent-loop.ts +114 -22
- package/src/daemon/conversation-history.ts +1 -1
- package/src/daemon/conversation-lifecycle.ts +3 -5
- package/src/daemon/conversation-process.ts +13 -5
- package/src/daemon/conversation-runtime-assembly.ts +13 -15
- package/src/daemon/conversation-slash.ts +2 -23
- package/src/daemon/conversation-surfaces.ts +26 -0
- package/src/daemon/conversation-tool-setup.ts +27 -14
- package/src/daemon/conversation.ts +66 -14
- package/src/daemon/disk-pressure-policy.ts +5 -3
- package/src/daemon/handlers/__tests__/config-a2a-complete.test.ts +0 -1
- package/src/daemon/handlers/__tests__/config-a2a-redeem.test.ts +0 -1
- package/src/daemon/handlers/config-a2a.ts +0 -2
- package/src/daemon/handlers/config-channels.ts +15 -16
- package/src/daemon/handlers/config-slack-channel.ts +22 -3
- package/src/daemon/handlers/conversations.ts +107 -0
- package/src/daemon/host-browser-proxy.ts +41 -0
- package/src/daemon/lifecycle.ts +55 -27
- package/src/daemon/message-provenance.ts +2 -0
- package/src/daemon/message-types/contacts.ts +0 -1
- package/src/daemon/message-types/conversations.ts +3 -3
- package/src/daemon/message-types/sync.ts +0 -1
- package/src/daemon/message-types/web-activity.ts +7 -1
- package/src/daemon/message-types/workflows.ts +83 -1
- package/src/daemon/orphan-reaper.test.ts +0 -19
- package/src/daemon/orphan-reaper.ts +2 -24
- package/src/daemon/server.ts +0 -10
- package/src/daemon/tool-setup-types.ts +4 -0
- package/src/daemon/trust-context.ts +1 -1
- package/src/events/tool-audit-listener.ts +2 -2
- package/src/home/feed-source-enrichment.test.ts +151 -0
- package/src/home/feed-source-enrichment.ts +176 -0
- package/src/home/relationship-state.ts +2 -4
- package/src/instrument.ts +18 -6
- package/src/ipc/__tests__/binary-result-ipc.test.ts +81 -0
- package/src/ipc/__tests__/clients-list-ipc.test.ts +20 -0
- package/src/ipc/assistant-server.ts +37 -4
- package/src/ipc/gateway-flag-listener.ts +18 -2
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +5 -16
- package/src/memory/__tests__/jobs-store-enqueue-gate.test.ts +7 -11
- package/src/memory/__tests__/memory-retrospective-enqueue.test.ts +37 -7
- package/src/memory/__tests__/memory-retrospective-job.test.ts +229 -401
- package/src/memory/__tests__/onboarding-events-store.test.ts +7 -7
- package/src/memory/auth-fallback-events-store.ts +2 -2
- package/src/memory/auto-analysis-enqueue.ts +3 -5
- package/src/memory/bookmark-crud.ts +1 -2
- package/src/memory/canonical-guardian-store.ts +39 -1
- package/src/memory/conversation-crud.ts +9 -4
- package/src/memory/conversation-key-store.ts +17 -2
- package/src/memory/conversation-title-service.ts +64 -7
- package/src/memory/db-init.ts +17 -17
- package/src/memory/embedding-backend.ts +38 -1
- package/src/memory/embedding-billing-breaker.ts +96 -0
- package/src/memory/jobs-store.ts +25 -13
- package/src/memory/jobs-worker.ts +54 -1
- package/src/memory/lifecycle-events-store.ts +2 -2
- package/src/memory/memory-retrospective-constants.ts +4 -4
- package/src/memory/memory-retrospective-enqueue.ts +31 -6
- package/src/memory/memory-retrospective-job.ts +28 -227
- package/src/memory/migrations/129-contact-channels-access-fields.ts +18 -9
- package/src/memory/migrations/131-drop-legacy-member-guardian-tables.ts +14 -2
- package/src/memory/migrations/289-contact-channels-unique-ext-user.ts +10 -0
- package/src/memory/migrations/291-contact-channels-renormalize-addresses.ts +72 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.test.ts +67 -0
- package/src/memory/migrations/292-schedule-default-no-reuse-conversation.ts +25 -0
- package/src/memory/migrations/293-workflow-journal-leaf-tokens.ts +32 -0
- package/src/memory/migrations/294-drop-external-user-id.ts +31 -0
- package/src/memory/migrations/295-drop-approval-prompt-ts-tracker.ts +20 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.test.ts +110 -0
- package/src/memory/migrations/296-rewrite-balanced-economy-profile-pins.ts +68 -0
- package/src/memory/migrations/__tests__/131-drop-legacy-member-guardian-tables.test.ts +154 -0
- package/src/memory/migrations/__tests__/289-contact-channels-unique-ext-user.test.ts +31 -0
- package/src/memory/migrations/__tests__/291-contact-channels-renormalize-addresses.test.ts +341 -0
- package/src/memory/migrations/__tests__/run-migrations.test.ts +52 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/run-migrations.ts +41 -0
- package/src/memory/migrations/validate-migration-state.ts +1 -1
- package/src/memory/onboarding-events-store.ts +3 -3
- package/src/memory/schema/contacts.ts +0 -5
- package/src/memory/skill-loaded-events-store.test.ts +7 -15
- package/src/memory/skill-loaded-events-store.ts +2 -2
- package/src/memory/tool-executed-events-store.test.ts +7 -7
- package/src/memory/turn-trace-store.test.ts +736 -0
- package/src/memory/turn-trace-store.ts +364 -0
- package/src/memory/v2/__tests__/consolidation-job.test.ts +8 -0
- package/src/memory/v2/__tests__/skill-content.test.ts +30 -0
- package/src/memory/v2/consolidation-job.ts +2 -2
- package/src/memory/v2/skill-content.ts +25 -7
- package/src/memory/v2/skill-store.ts +7 -1
- package/src/memory/v3-eval/__tests__/eval-packets.test.ts +248 -0
- package/src/memory/v3-eval/eval-packets.ts +546 -0
- package/src/messaging/providers/slack/adapter.ts +1 -1
- package/src/messaging/providers/slack/api.ts +31 -0
- package/src/messaging/providers/slack/send.test.ts +114 -2
- package/src/messaging/providers/slack/send.ts +30 -7
- package/src/messaging/providers/slack/withdraw.test.ts +200 -0
- package/src/messaging/providers/slack/withdraw.ts +161 -0
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/access-request-copy.ts +72 -59
- package/src/notifications/adapters/shared.ts +29 -0
- package/src/notifications/adapters/slack.ts +58 -103
- package/src/notifications/adapters/telegram.ts +2 -20
- package/src/notifications/approval-card-data.ts +333 -0
- package/src/notifications/broadcaster.ts +16 -3
- package/src/notifications/canonical-delivery-recorder.ts +139 -0
- package/src/notifications/copy-composer.ts +3 -3
- package/src/notifications/decision-engine.ts +4 -2
- package/src/notifications/destination-resolver.ts +4 -6
- package/src/notifications/guardian-question-mode.ts +10 -0
- package/src/notifications/home-feed-side-effect.ts +7 -16
- package/src/notifications/notification-utils.ts +19 -20
- package/src/notifications/signal.ts +79 -43
- package/src/notifications/types.ts +98 -121
- package/src/oauth/AGENTS.md +5 -24
- package/src/permissions/checker.test.ts +51 -0
- package/src/permissions/checker.ts +185 -26
- package/src/permissions/ipc-risk-types.ts +24 -0
- package/src/permissions/question-prompter.test.ts +27 -0
- package/src/permissions/question-prompter.ts +4 -0
- package/src/platform/client.test.ts +119 -0
- package/src/platform/client.ts +66 -0
- package/src/platform/consent-cache.test.ts +267 -0
- package/src/platform/consent-cache.ts +174 -0
- package/src/plugin-api/constants.ts +1 -1
- package/src/plugin-api/index.ts +33 -1
- package/src/plugin-api/model-profiles.ts +33 -0
- package/src/plugin-api/types.ts +50 -2
- package/src/plugins/defaults/advisor/__tests__/advisor-gate.test.ts +56 -0
- package/src/plugins/defaults/advisor/__tests__/advisor-state-store.test.ts +43 -0
- package/src/plugins/defaults/advisor/__tests__/agent-loop-integration.test.ts +137 -0
- package/src/plugins/defaults/advisor/__tests__/consult.test.ts +153 -0
- package/src/plugins/defaults/advisor/__tests__/hooks.test.ts +138 -0
- package/src/plugins/defaults/advisor/__tests__/transcript.test.ts +147 -0
- package/src/plugins/defaults/advisor/advisor-gate.ts +29 -0
- package/src/plugins/defaults/advisor/advisor-state-store.ts +94 -0
- package/src/plugins/defaults/advisor/config.ts +21 -0
- package/src/plugins/defaults/advisor/consult.ts +93 -0
- package/src/plugins/defaults/advisor/hooks/post-model-call.ts +34 -0
- package/src/plugins/defaults/advisor/hooks/pre-model-call.ts +30 -0
- package/src/plugins/defaults/advisor/hooks/user-prompt-submit.ts +19 -0
- package/src/plugins/defaults/advisor/package.json +14 -0
- package/src/plugins/defaults/advisor/steering.ts +67 -0
- package/src/plugins/defaults/advisor/tools/advisor.ts +65 -0
- package/src/plugins/defaults/advisor/transcript.ts +76 -0
- package/src/plugins/defaults/index.ts +60 -0
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +22 -9
- package/src/plugins/defaults/memory-retrieval/hooks/user-prompt-submit.ts +2 -2
- package/src/plugins/defaults/memory-retrieval/tail-reinjection-strip.ts +64 -0
- package/src/plugins/defaults/memory-retrieval/unified-turn-context.ts +29 -21
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +129 -9
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +31 -4
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +77 -2
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +1 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +7 -10
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +144 -11
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +32 -20
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +56 -3
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +23 -2
- package/src/plugins/defaults/surface-completion-nudge/hooks/post-model-call.ts +276 -0
- package/src/plugins/defaults/surface-completion-nudge/hooks/stop.ts +22 -0
- package/src/plugins/defaults/surface-completion-nudge/nudge-state-store.ts +46 -0
- package/src/plugins/defaults/surface-completion-nudge/package.json +14 -0
- package/src/plugins/defaults/task-progress-nudge/hooks/post-tool-use.ts +3 -13
- package/src/plugins/defaults/title-generate/hooks/stop.ts +56 -21
- package/src/prompts/persona-resolver.ts +14 -4
- package/src/prompts/templates/system-sections.ts +7 -2
- package/src/providers/__tests__/provider-env-vars.test.ts +6 -0
- package/src/providers/__tests__/provider-secret-catalog.test.ts +1 -0
- package/src/providers/__tests__/retry-callsite.test.ts +176 -0
- package/src/providers/atlascloud/client.ts +85 -0
- package/src/providers/fetch-provider-catalog.ts +85 -0
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/model-catalog.ts +58 -0
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +33 -0
- package/src/providers/openai/chat-completions-provider.ts +7 -0
- package/src/providers/openai/responses-provider.ts +10 -0
- package/src/providers/provider-send-message.ts +11 -3
- package/src/providers/retry.ts +53 -12
- package/src/providers/search-provider-catalog.ts +10 -0
- package/src/providers/weak-open-model.ts +22 -0
- package/src/runtime/AGENTS.md +0 -1
- package/src/runtime/__tests__/agent-wake.test.ts +181 -0
- package/src/runtime/__tests__/client-health.test.ts +44 -0
- package/src/runtime/access-request-helper.ts +21 -53
- package/src/runtime/actor-trust-resolver.ts +59 -63
- package/src/runtime/agent-wake.ts +52 -0
- package/src/runtime/assistant-event-hub.ts +18 -4
- package/src/runtime/auth/__tests__/route-policy.test.ts +12 -0
- package/src/runtime/auth/require-bound-guardian.ts +1 -4
- package/src/runtime/btw-sidechain.ts +3 -6
- package/src/runtime/capabilities.test.ts +120 -0
- package/src/runtime/capabilities.ts +197 -0
- package/src/runtime/channel-approval-types.ts +22 -45
- package/src/runtime/channel-invite-transports/telegram.ts +4 -4
- package/src/runtime/channel-retry-sweep.ts +1 -0
- package/src/runtime/channel-verification-service.ts +3 -3
- package/src/runtime/client-health.ts +26 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +38 -29
- package/src/runtime/effective-capabilities.test.ts +128 -0
- package/src/runtime/effective-capabilities.ts +84 -0
- package/src/runtime/guardian-reply-router.ts +106 -21
- package/src/runtime/invite-redemption-service.ts +9 -25
- package/src/runtime/migrations/__tests__/vbundle-builder-fd-leak.test.ts +123 -0
- package/src/runtime/migrations/vbundle-builder.ts +49 -20
- package/src/runtime/pending-interactions.ts +15 -0
- package/src/runtime/routes/__tests__/client-routes.test.ts +13 -0
- package/src/runtime/routes/__tests__/conversation-management-routes.test.ts +67 -0
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +240 -1
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +2 -2
- package/src/runtime/routes/assets/vellum-design-system.css +1959 -0
- package/src/runtime/routes/browser-tabs-routes.ts +9 -0
- package/src/runtime/routes/btw-routes.ts +1 -27
- package/src/runtime/routes/canonical-guardian-expiry-sweep.ts +17 -8
- package/src/runtime/routes/client-routes.ts +10 -0
- package/src/runtime/routes/contact-routes.ts +31 -8
- package/src/runtime/routes/conversation-compaction-routes.ts +1 -1
- package/src/runtime/routes/conversation-management-routes.ts +80 -1
- package/src/runtime/routes/conversation-query-routes.ts +68 -22
- package/src/runtime/routes/conversation-routes.ts +39 -14
- package/src/runtime/routes/credential-routes.ts +40 -16
- package/src/runtime/routes/empty-state-greeting-cache.ts +1 -2
- package/src/runtime/routes/events-routes.ts +1 -3
- package/src/runtime/routes/guardian-approval-interception.ts +14 -73
- package/src/runtime/routes/guardian-approval-prompt.ts +22 -4
- package/src/runtime/routes/home-feed-routes.ts +8 -3
- package/src/runtime/routes/identity-routes.ts +1 -296
- package/src/runtime/routes/inbound-message-handler.ts +214 -228
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +89 -7
- package/src/runtime/routes/inbound-stages/admission-policy.test.ts +154 -0
- package/src/runtime/routes/inbound-stages/admission-policy.ts +140 -0
- package/src/runtime/routes/inbound-stages/background-dispatch.test.ts +3 -3
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +11 -6
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +1 -2
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +7 -7
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +47 -28
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +358 -0
- package/src/runtime/routes/index.ts +2 -0
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +8 -0
- package/src/runtime/routes/integrations/slack/channel.ts +36 -0
- package/src/runtime/routes/internal-telemetry-routes.ts +1 -1
- package/src/runtime/routes/mcp-auth-routes.ts +233 -41
- package/src/runtime/routes/memory-eval-routes.ts +87 -0
- package/src/runtime/routes/notification-routes.ts +122 -133
- package/src/runtime/routes/platform-routes.ts +2 -2
- package/src/runtime/routes/plugins-routes.ts +202 -3
- package/src/runtime/routes/schedule-routes.ts +0 -22
- package/src/runtime/routes/secret-routes.ts +10 -0
- package/src/runtime/routes/surface-action-routes.ts +2 -1
- package/src/runtime/routes/tool-call-question-enrichment.test.ts +146 -0
- package/src/runtime/routes/tool-call-question-enrichment.ts +66 -0
- package/src/runtime/routes/workflow-routes.test.ts +229 -44
- package/src/runtime/routes/workflow-routes.ts +131 -29
- package/src/runtime/routes/workspace-greetings.ts +55 -0
- package/src/runtime/sync/resource-sync-events.ts +1 -11
- package/src/runtime/tool-grant-request-helper.ts +18 -16
- package/src/runtime/trust-context-resolver.ts +8 -5
- package/src/schedule/inference-profile.ts +2 -14
- package/src/schedule/schedule-store.ts +1 -1
- package/src/schedule/scheduler-types.ts +5 -1
- package/src/security/__tests__/provider-key-env-fallback.test.ts +6 -0
- package/src/security/secret-patterns.ts +3 -0
- package/src/subagent/manager.ts +17 -4
- package/src/subagent/types.ts +6 -0
- package/src/telemetry/trace-collection-policy.test.ts +28 -0
- package/src/telemetry/trace-collection-policy.ts +30 -0
- package/src/telemetry/types.ts +89 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +586 -36
- package/src/telemetry/usage-telemetry-reporter.ts +148 -41
- package/src/tools/AGENTS.md +3 -3
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +31 -0
- package/src/tools/browser/browser-execution.ts +30 -19
- package/src/tools/document/document-tool.ts +2 -3
- package/src/tools/executor.ts +5 -3
- package/src/tools/host-terminal/host-shell.ts +5 -4
- package/src/tools/memory/register.ts +2 -2
- package/src/tools/network/__tests__/web-fetch-firecrawl.test.ts +360 -0
- package/src/tools/network/__tests__/web-search.test.ts +143 -0
- package/src/tools/network/web-fetch.ts +372 -1
- package/src/tools/network/web-search-error.ts +1 -1
- package/src/tools/network/web-search.ts +213 -10
- package/src/tools/permission-checker.ts +4 -3
- package/src/tools/registry.ts +20 -0
- package/src/tools/schedule/create.ts +7 -12
- package/src/tools/schedule/update.ts +4 -11
- package/src/tools/shared/filesystem/path-policy.ts +39 -13
- package/src/tools/side-effects.ts +2 -17
- package/src/tools/skills/execute.ts +33 -0
- package/src/tools/subagent/spawn.ts +61 -12
- package/src/tools/terminal/shell.ts +10 -4
- package/src/tools/tool-approval-handler.ts +18 -13
- package/src/tools/tool-manifest.ts +0 -2
- package/src/tools/types.ts +9 -0
- package/src/tools/ui-surface/definitions.ts +64 -3
- package/src/tools/verification-control-plane-policy.ts +3 -1
- package/src/tools/workflows/run-workflow.test.ts +8 -18
- package/src/tools/workflows/run-workflow.ts +1 -0
- package/src/util/disk-usage.ts +78 -23
- package/src/util/platform.ts +10 -3
- package/src/watcher/telemetry.ts +2 -2
- package/src/workflows/capabilities.ts +2 -3
- package/src/workflows/engine.test.ts +175 -1
- package/src/workflows/engine.ts +82 -0
- package/src/workflows/journal-store.test.ts +70 -0
- package/src/workflows/journal-store.ts +18 -3
- package/src/workflows/run-manager.test.ts +171 -28
- package/src/workflows/run-manager.ts +66 -24
- package/src/workspace/migrations/105-enable-memory-v3-live-for-new-workspaces.ts +63 -0
- package/src/workspace/migrations/106-drop-collect-usage-data.ts +47 -0
- package/src/workspace/migrations/107-drop-send-diagnostics.ts +47 -0
- package/src/workspace/migrations/108-drop-balanced-economy-profile.ts +129 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/__tests__/app-control-no-global-cgevent.test.ts +0 -98
- package/src/__tests__/credential-security-e2e.test.ts +0 -362
- package/src/__tests__/credential-vault-unit.test.ts +0 -1528
- package/src/__tests__/credential-vault.test.ts +0 -1706
- package/src/__tests__/identity-intro-cache.test.ts +0 -315
- package/src/__tests__/secret-onetime-send.test.ts +0 -182
- package/src/cli/commands/__tests__/task.test.ts +0 -914
- package/src/cli/commands/task.ts +0 -771
- package/src/config/bundled-skills/personal-page/SKILL.md +0 -57
- package/src/config/bundled-skills/personal-page/TOOLS.json +0 -27
- package/src/config/bundled-skills/personal-page/tools/app-refresh.ts +0 -17
- package/src/config/preloaded-apps/personal-page/src/components/About.tsx +0 -22
- package/src/config/preloaded-apps/personal-page/src/components/App.tsx +0 -16
- package/src/config/preloaded-apps/personal-page/src/components/Features.tsx +0 -77
- package/src/config/preloaded-apps/personal-page/src/components/Hero.tsx +0 -57
- package/src/config/preloaded-apps/personal-page/src/components/Pending.tsx +0 -28
- package/src/config/preloaded-apps/personal-page/src/components/animations.tsx +0 -234
- package/src/config/preloaded-apps/personal-page/src/components/icons.tsx +0 -48
- package/src/config/preloaded-apps/personal-page/src/components/media.ts +0 -16
- package/src/config/preloaded-apps/personal-page/src/index.html +0 -20
- package/src/config/preloaded-apps/personal-page/src/main.tsx +0 -7
- package/src/config/preloaded-apps/personal-page/src/profile-data.ts +0 -82
- package/src/config/preloaded-apps/personal-page/src/styles.css +0 -759
- package/src/memory/__tests__/preloaded-apps.test.ts +0 -85
- package/src/memory/preloaded-apps.ts +0 -116
- package/src/notifications/tool-approval-copy.ts +0 -142
- package/src/runtime/routes/approval-prompt-ts-tracker.ts +0 -78
- package/src/runtime/routes/identity-intro-cache.ts +0 -172
- package/src/tools/credentials/vault.ts +0 -712
|
@@ -41,9 +41,9 @@
|
|
|
41
41
|
* block id only); it is never persisted to metadata, so the frozen card
|
|
42
42
|
* prefix it follows stays byte-stable and cached regardless.
|
|
43
43
|
*
|
|
44
|
-
*
|
|
45
|
-
* (live off) logs what WOULD inject (net-new slugs + bytes + spotlight
|
|
46
|
-
* and attaches nothing; both off → no orchestration.
|
|
44
|
+
* Gating: `memory.v3.live` (config) attaches blocks; the `memory-v3-shadow`
|
|
45
|
+
* flag (live off) logs what WOULD inject (net-new slugs + bytes + spotlight
|
|
46
|
+
* refs) and attaches nothing; both off → no orchestration.
|
|
47
47
|
*
|
|
48
48
|
* Both injectors apply the same personal-memory trust gate as v2
|
|
49
49
|
* ({@link isPersonalMemoryAllowed}): an untrusted remote actor's turn
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
|
|
63
63
|
import { isAssistantFeatureFlagEnabled } from "../../../config/assistant-feature-flags.js";
|
|
64
64
|
import { getConfig } from "../../../config/loader.js";
|
|
65
|
+
import { isMemoryV3Live } from "../../../config/memory-v3-gate.js";
|
|
65
66
|
import { isPersonalMemoryAllowed } from "../../../daemon/trust-context.js";
|
|
66
67
|
import {
|
|
67
68
|
wrapMemoryBlock,
|
|
@@ -85,11 +86,7 @@ import {
|
|
|
85
86
|
renderSpotlightInner,
|
|
86
87
|
type SpotlightEntry,
|
|
87
88
|
} from "./render-injection.js";
|
|
88
|
-
import {
|
|
89
|
-
MEMORY_V3_LIVE,
|
|
90
|
-
MEMORY_V3_SHADOW,
|
|
91
|
-
observeTurn,
|
|
92
|
-
} from "./shadow-plugin.js";
|
|
89
|
+
import { MEMORY_V3_SHADOW, observeTurn } from "./shadow-plugin.js";
|
|
93
90
|
import {
|
|
94
91
|
MEMORY_V3_BLOCK_ID,
|
|
95
92
|
MEMORY_V3_COMMIT_META_KEY,
|
|
@@ -239,7 +236,7 @@ export const memoryV3Injector: Injector = {
|
|
|
239
236
|
async produce(ctx: TurnContext): Promise<InjectionBlock | null> {
|
|
240
237
|
const config = getConfig();
|
|
241
238
|
if (config.memory.enabled === false) return null;
|
|
242
|
-
const live =
|
|
239
|
+
const live = isMemoryV3Live(config);
|
|
243
240
|
const shadow = isAssistantFeatureFlagEnabled(MEMORY_V3_SHADOW, config);
|
|
244
241
|
if (!live && !shadow) return null;
|
|
245
242
|
if (!isPersonalMemoryAllowed(ctx.trust)) return null;
|
|
@@ -378,7 +375,7 @@ export const memoryV3SpotlightInjector: Injector = {
|
|
|
378
375
|
// Live-only: shadow mode logs spotlight refs from the cards injector and
|
|
379
376
|
// must keep the turn untouched (no ring state either, so a later
|
|
380
377
|
// live-flag flip starts from a clean window).
|
|
381
|
-
if (!
|
|
378
|
+
if (!isMemoryV3Live(config)) return null;
|
|
382
379
|
if (!isPersonalMemoryAllowed(ctx.trust)) return null;
|
|
383
380
|
|
|
384
381
|
try {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Memory v3 — `memory_v3_maintain` job handler.
|
|
3
3
|
*
|
|
4
4
|
* A flag-gated, best-effort self-maintenance pass over the v3 section dense
|
|
5
|
-
* store and the in-memory lanes. It runs
|
|
5
|
+
* store and the in-memory lanes. It runs five independent stages, in order:
|
|
6
6
|
*
|
|
7
7
|
* 1. **Section re-embed** — diff the page index by `modifiedAt` against the
|
|
8
8
|
* last successful pass (the high-water mark below), and for every page that
|
|
@@ -15,19 +15,26 @@
|
|
|
15
15
|
* captured before any potential mtime bumps) so a page is not re-embedded
|
|
16
16
|
* forever, yet a page whose embed failed (and whose sections were therefore
|
|
17
17
|
* left deleted) stays above the mark and is retried next pass.
|
|
18
|
-
* 2. **
|
|
18
|
+
* 2. **Capability reconcile** — embed capability rows (synthetic skill/CLI
|
|
19
|
+
* slugs) present in the page index but missing from the section store. The
|
|
20
|
+
* re-embed delta above EXCLUDES capability rows (they have `modifiedAt` 0,
|
|
21
|
+
* no mtime to diff), so the only other embedder is the one-time
|
|
22
|
+
* `backfillAllSections`. Without this stage a skill enabled AFTER that
|
|
23
|
+
* backfill (e.g. a flag-gated skill flipped on at runtime) lands in the
|
|
24
|
+
* index but never reaches the dense lane. See {@link reconcileCapabilityRows}.
|
|
25
|
+
* 3. **Deleted-page prune** — diff the dense store's stored articles
|
|
19
26
|
* (`listSectionArticles`) against the live page-index slugs and
|
|
20
27
|
* `deleteSectionsForArticle` for any article that is no longer in the
|
|
21
28
|
* index. A deleted page's slug never reaches the re-embed delta selector
|
|
22
29
|
* (it only names live pages), so without this its section points would
|
|
23
30
|
* linger in Qdrant and the dense lane could still surface the deleted page.
|
|
24
31
|
* Synthetic capability rows are in the page index, so they are never pruned.
|
|
25
|
-
*
|
|
32
|
+
* 4. **Core-set validation** — load the maintainer-curated core set
|
|
26
33
|
* (`memory/core-pages.md`) and report entries whose page no longer exists
|
|
27
34
|
* in the page index (dangling slugs) via the log + outcome. The file is
|
|
28
35
|
* maintainer-owned, so this stage NEVER edits it — the maintainer fixes
|
|
29
36
|
* dangling entries at the next consolidation pass.
|
|
30
|
-
*
|
|
37
|
+
* 5. **Lane invalidation** — `invalidateLanes()` so the next turn rebuilds the
|
|
31
38
|
* in-memory section index, needle, and edge graph from the freshly-updated
|
|
32
39
|
* pages.
|
|
33
40
|
*
|
|
@@ -35,8 +42,8 @@
|
|
|
35
42
|
* logged and recorded in the outcome but does NOT abort the others. A single
|
|
36
43
|
* page whose embed fails does not abort the rest of the re-embed stage, and a
|
|
37
44
|
* single prune delete that throws does not abort the rest of the prune stage.
|
|
38
|
-
* The job is a no-op (returns a disabled outcome) unless `memory-v3-shadow`
|
|
39
|
-
* `memory
|
|
45
|
+
* The job is a no-op (returns a disabled outcome) unless the `memory-v3-shadow`
|
|
46
|
+
* flag OR `memory.v3.live` (config) is enabled — the same gates as the v3 plugin.
|
|
40
47
|
*
|
|
41
48
|
* Dependency-injectable: `deps` lets tests substitute the page-index reader,
|
|
42
49
|
* section builder, dense-store ops (including the prune-stage
|
|
@@ -45,11 +52,17 @@
|
|
|
45
52
|
*/
|
|
46
53
|
|
|
47
54
|
import { isAssistantFeatureFlagEnabled } from "../../../config/assistant-feature-flags.js";
|
|
55
|
+
import { isMemoryV3Live } from "../../../config/memory-v3-gate.js";
|
|
48
56
|
import type { AssistantConfig } from "../../../config/types.js";
|
|
49
57
|
import {
|
|
50
58
|
getMemoryCheckpoint,
|
|
51
59
|
setMemoryCheckpoint,
|
|
52
60
|
} from "../../../memory/checkpoints.js";
|
|
61
|
+
import {
|
|
62
|
+
EmbeddingBackendUnavailableError,
|
|
63
|
+
embedWithBackend,
|
|
64
|
+
} from "../../../memory/embedding-backend.js";
|
|
65
|
+
import { EmbeddingBillingBlockError } from "../../../memory/embedding-billing-breaker.js";
|
|
53
66
|
import type { MemoryJob } from "../../../memory/jobs-store.js";
|
|
54
67
|
import { getPageIndex } from "../../../memory/v2/page-index.js";
|
|
55
68
|
import { readPage } from "../../../memory/v2/page-store.js";
|
|
@@ -68,7 +81,6 @@ import { invalidateLanes as realInvalidateLanes } from "./shadow-plugin.js";
|
|
|
68
81
|
import type { Slug } from "./types.js";
|
|
69
82
|
|
|
70
83
|
const MEMORY_V3_SHADOW = "memory-v3-shadow" as const;
|
|
71
|
-
const MEMORY_V3_LIVE = "memory-v3-live" as const;
|
|
72
84
|
|
|
73
85
|
/**
|
|
74
86
|
* Durable checkpoint holding the epoch-ms high-water mark of the last successful
|
|
@@ -110,6 +122,13 @@ export interface MaintainJobDeps {
|
|
|
110
122
|
buildSectionIndex: typeof realBuildSectionIndex;
|
|
111
123
|
/** Read a page's frontmatter-stripped body for `buildSectionIndex`. */
|
|
112
124
|
readPageBody: (slug: Slug) => Promise<string>;
|
|
125
|
+
/**
|
|
126
|
+
* Capability-aware body reader for the reconcile stage: synthetic skill/CLI
|
|
127
|
+
* slugs resolve their rendered capability content; real pages read from disk.
|
|
128
|
+
* The re-embed stage uses `readPageBody` (disk-only) since it only processes
|
|
129
|
+
* real pages — the reconcile stage needs capability bodies.
|
|
130
|
+
*/
|
|
131
|
+
readCapabilityBody: (slug: Slug) => Promise<string>;
|
|
113
132
|
/** Clear an article's stale section points before re-upserting. */
|
|
114
133
|
deleteSectionsForArticle: typeof realDeleteSectionsForArticle;
|
|
115
134
|
/** Embed + upsert an article's current sections into the dense store. */
|
|
@@ -178,6 +197,13 @@ export interface BackfillJobDeps {
|
|
|
178
197
|
nowMs: () => number;
|
|
179
198
|
/** Active assistant config (for the dense-store/embedding calls). */
|
|
180
199
|
config: AssistantConfig;
|
|
200
|
+
/**
|
|
201
|
+
* Smoke-test the embedding backend before any destructive write. Throws when
|
|
202
|
+
* the backend is unavailable, so the backfill aborts BEFORE deleting any
|
|
203
|
+
* article's points (each article is processed delete-then-upsert). Injectable
|
|
204
|
+
* for tests.
|
|
205
|
+
*/
|
|
206
|
+
embedProbe: () => Promise<void>;
|
|
181
207
|
}
|
|
182
208
|
|
|
183
209
|
/** Counts surfaced by {@link backfillAllSections}. */
|
|
@@ -198,6 +224,15 @@ export interface MaintainOutcome {
|
|
|
198
224
|
reembedded: number;
|
|
199
225
|
/** Pages whose re-embed threw (and was contained). */
|
|
200
226
|
reembedFailures: number;
|
|
227
|
+
/**
|
|
228
|
+
* Capability rows (skills/CLI) present in the index but missing from the
|
|
229
|
+
* section store that were embedded this pass — how a skill enabled after the
|
|
230
|
+
* one-time backfill reaches the dense lane (the re-embed delta excludes
|
|
231
|
+
* capability rows).
|
|
232
|
+
*/
|
|
233
|
+
capabilitiesReconciled: number;
|
|
234
|
+
/** Capability reconcile embeds that threw (and were contained). */
|
|
235
|
+
reconcileFailures: number;
|
|
201
236
|
/**
|
|
202
237
|
* Deleted-page articles whose lingering section points were pruned this pass
|
|
203
238
|
* (present in the dense store but absent from the page index).
|
|
@@ -302,6 +337,8 @@ function defaultDeps(config: AssistantConfig): MaintainJobDeps {
|
|
|
302
337
|
selectChangedPages: () => selectChangedPagesFromWorkspace(workspaceDir),
|
|
303
338
|
buildSectionIndex: realBuildSectionIndex,
|
|
304
339
|
readPageBody: (slug) => readPageBodyFromWorkspace(workspaceDir, slug),
|
|
340
|
+
readCapabilityBody: (slug) =>
|
|
341
|
+
backfillPageBodyFromWorkspace(workspaceDir, slug),
|
|
305
342
|
deleteSectionsForArticle: realDeleteSectionsForArticle,
|
|
306
343
|
upsertSections: realUpsertSections,
|
|
307
344
|
commitEmbedHighWater,
|
|
@@ -325,6 +362,9 @@ function defaultBackfillDeps(config: AssistantConfig): BackfillJobDeps {
|
|
|
325
362
|
commitEmbedHighWater,
|
|
326
363
|
nowMs: () => Date.now(),
|
|
327
364
|
config,
|
|
365
|
+
embedProbe: async () => {
|
|
366
|
+
await embedWithBackend(config, ["memory-v3 backfill preflight"]);
|
|
367
|
+
},
|
|
328
368
|
};
|
|
329
369
|
}
|
|
330
370
|
|
|
@@ -357,6 +397,58 @@ async function reembedChangedPages(
|
|
|
357
397
|
return { reembedded, reembedFailures };
|
|
358
398
|
}
|
|
359
399
|
|
|
400
|
+
/**
|
|
401
|
+
* Embed capability rows (synthetic skill/CLI slugs) present in the page index
|
|
402
|
+
* but missing from the section dense store. The incremental re-embed selector
|
|
403
|
+
* ({@link computeChangedPages}) excludes capability rows — they have no on-disk
|
|
404
|
+
* mtime to delta against — so the ONLY other embedder is the one-time
|
|
405
|
+
* {@link backfillAllSections}. Without this, a skill enabled AFTER that backfill
|
|
406
|
+
* (e.g. a flag-gated skill flipped on at runtime) lands in the page index but
|
|
407
|
+
* never reaches the dense lane. This stage makes the periodic maintain pass
|
|
408
|
+
* self-heal: diff the live capability slugs against the stored section articles
|
|
409
|
+
* and embed the missing ones.
|
|
410
|
+
*
|
|
411
|
+
* Each row is independent: a single build/upsert throw is logged and counted in
|
|
412
|
+
* `reconcileFailures` without aborting the rest. A capability row whose body
|
|
413
|
+
* resolves empty (its store has not seeded yet) is skipped WITHOUT deleting any
|
|
414
|
+
* points — never replace good points with a blank — and is retried next pass.
|
|
415
|
+
*/
|
|
416
|
+
async function reconcileCapabilityRows(
|
|
417
|
+
deps: MaintainJobDeps,
|
|
418
|
+
): Promise<{ capabilitiesReconciled: number; reconcileFailures: number }> {
|
|
419
|
+
const [indexedSlugs, storedArticles] = await Promise.all([
|
|
420
|
+
deps.listIndexedSlugs(),
|
|
421
|
+
deps.listSectionArticles(),
|
|
422
|
+
]);
|
|
423
|
+
const stored = new Set(storedArticles);
|
|
424
|
+
const missing = indexedSlugs.filter(
|
|
425
|
+
(slug) => isCapabilitySlug(slug) && !stored.has(slug),
|
|
426
|
+
);
|
|
427
|
+
|
|
428
|
+
let capabilitiesReconciled = 0;
|
|
429
|
+
let reconcileFailures = 0;
|
|
430
|
+
for (const slug of missing) {
|
|
431
|
+
try {
|
|
432
|
+
const body = await deps.readCapabilityBody(slug);
|
|
433
|
+
if (body.trim().length === 0) continue; // store cold — retry next pass
|
|
434
|
+
const index = await deps.buildSectionIndex(
|
|
435
|
+
[slug],
|
|
436
|
+
deps.readCapabilityBody,
|
|
437
|
+
);
|
|
438
|
+
await deps.deleteSectionsForArticle(deps.config, slug);
|
|
439
|
+
await deps.upsertSections(deps.config, index.sections);
|
|
440
|
+
capabilitiesReconciled += 1;
|
|
441
|
+
} catch (err) {
|
|
442
|
+
reconcileFailures += 1;
|
|
443
|
+
log.warn(
|
|
444
|
+
{ slug, err: err instanceof Error ? err.message : String(err) },
|
|
445
|
+
"memory-v3 maintain: capability reconcile embed failed (non-fatal)",
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
return { capabilitiesReconciled, reconcileFailures };
|
|
450
|
+
}
|
|
451
|
+
|
|
360
452
|
/**
|
|
361
453
|
* Prune section points for articles that no longer exist in the page index. A
|
|
362
454
|
* deleted concept page leaves `getPageIndex` (so the needle and edge lanes both
|
|
@@ -444,6 +536,13 @@ export async function backfillAllSections(
|
|
|
444
536
|
const slugs = await deps.selectAllPages();
|
|
445
537
|
await deps.ensureSectionCollection(deps.config);
|
|
446
538
|
|
|
539
|
+
// Pre-flight: smoke-test the embedding backend BEFORE any delete. Each article
|
|
540
|
+
// is processed delete-then-upsert, so starting a full backfill against a down
|
|
541
|
+
// backend would delete every article's points and re-embed none of them. A
|
|
542
|
+
// throw here aborts with nothing deleted; the high-water mark stays put, so a
|
|
543
|
+
// later run retries once the backend is healthy.
|
|
544
|
+
await deps.embedProbe();
|
|
545
|
+
|
|
447
546
|
let articles = 0;
|
|
448
547
|
let sections = 0;
|
|
449
548
|
let failures = 0;
|
|
@@ -466,6 +565,17 @@ export async function backfillAllSections(
|
|
|
466
565
|
sections += index.sections.length;
|
|
467
566
|
return "embedded";
|
|
468
567
|
} catch (err) {
|
|
568
|
+
// A down embedding backend (unconfigured, or billing breaker open) fails
|
|
569
|
+
// EVERY article — the condition is process-wide — and each article is
|
|
570
|
+
// delete-then-upsert, so continuing would delete the rest of the corpus
|
|
571
|
+
// and re-embed none of it. Abort the run; the high-water mark is never
|
|
572
|
+
// advanced, so the next pass retries from here once the backend recovers.
|
|
573
|
+
if (
|
|
574
|
+
err instanceof EmbeddingBackendUnavailableError ||
|
|
575
|
+
err instanceof EmbeddingBillingBlockError
|
|
576
|
+
) {
|
|
577
|
+
throw err;
|
|
578
|
+
}
|
|
469
579
|
failures += 1;
|
|
470
580
|
log.warn(
|
|
471
581
|
{ slug, err: err instanceof Error ? err.message : String(err) },
|
|
@@ -537,6 +647,8 @@ export async function maintainJob(
|
|
|
537
647
|
disabled: false,
|
|
538
648
|
reembedded: 0,
|
|
539
649
|
reembedFailures: 0,
|
|
650
|
+
capabilitiesReconciled: 0,
|
|
651
|
+
reconcileFailures: 0,
|
|
540
652
|
pruned: 0,
|
|
541
653
|
pruneFailures: 0,
|
|
542
654
|
danglingCoreSlugs: [],
|
|
@@ -546,7 +658,7 @@ export async function maintainJob(
|
|
|
546
658
|
|
|
547
659
|
const enabled =
|
|
548
660
|
isAssistantFeatureFlagEnabled(MEMORY_V3_SHADOW, config) ||
|
|
549
|
-
|
|
661
|
+
isMemoryV3Live(config);
|
|
550
662
|
if (!enabled) {
|
|
551
663
|
outcome.disabled = true;
|
|
552
664
|
return outcome;
|
|
@@ -587,7 +699,26 @@ export async function maintainJob(
|
|
|
587
699
|
);
|
|
588
700
|
}
|
|
589
701
|
|
|
590
|
-
// Stage 2:
|
|
702
|
+
// Stage 2: embed capability rows (skills/CLI) present in the index but missing
|
|
703
|
+
// from the section store. They have modifiedAt 0, so computeChangedPages never
|
|
704
|
+
// selects them; the one-time backfill is otherwise their only embedder, so a
|
|
705
|
+
// skill enabled after that backfill stays invisible to the dense lane until
|
|
706
|
+
// this self-heals it. Contained: a failure is logged + recorded without
|
|
707
|
+
// aborting later stages.
|
|
708
|
+
try {
|
|
709
|
+
const { capabilitiesReconciled, reconcileFailures } =
|
|
710
|
+
await reconcileCapabilityRows(deps);
|
|
711
|
+
outcome.capabilitiesReconciled = capabilitiesReconciled;
|
|
712
|
+
outcome.reconcileFailures = reconcileFailures;
|
|
713
|
+
} catch (err) {
|
|
714
|
+
outcome.failures.push("capability-reconcile");
|
|
715
|
+
log.warn(
|
|
716
|
+
{ err: err instanceof Error ? err.message : String(err) },
|
|
717
|
+
"memory-v3 maintain: capability reconcile failed (non-fatal)",
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// Stage 3: prune section points for pages deleted from the index. A deleted
|
|
591
722
|
// page's slug never appears in `selectChangedPages` (the delta selector only
|
|
592
723
|
// names live pages), so its lingering points are cleared by diffing the dense
|
|
593
724
|
// store's stored articles against the live page-index slugs. Contained: a
|
|
@@ -604,7 +735,7 @@ export async function maintainJob(
|
|
|
604
735
|
);
|
|
605
736
|
}
|
|
606
737
|
|
|
607
|
-
// Stage
|
|
738
|
+
// Stage 4: validate the maintainer-curated core set. Diff `core-pages.md`
|
|
608
739
|
// entries against the live page-index slugs and REPORT dangling entries
|
|
609
740
|
// (pages that were renamed or deleted) through the log + outcome — the file
|
|
610
741
|
// is maintainer-owned, so it is never auto-edited; the maintainer fixes it at
|
|
@@ -632,7 +763,7 @@ export async function maintainJob(
|
|
|
632
763
|
);
|
|
633
764
|
}
|
|
634
765
|
|
|
635
|
-
// Stage
|
|
766
|
+
// Stage 5: rebuild section index + needle + edge graph on the next turn.
|
|
636
767
|
try {
|
|
637
768
|
deps.invalidateLanes();
|
|
638
769
|
outcome.invalidated = true;
|
|
@@ -648,6 +779,8 @@ export async function maintainJob(
|
|
|
648
779
|
{
|
|
649
780
|
reembedded: outcome.reembedded,
|
|
650
781
|
reembedFailures: outcome.reembedFailures,
|
|
782
|
+
capabilitiesReconciled: outcome.capabilitiesReconciled,
|
|
783
|
+
reconcileFailures: outcome.reconcileFailures,
|
|
651
784
|
pruned: outcome.pruned,
|
|
652
785
|
pruneFailures: outcome.pruneFailures,
|
|
653
786
|
danglingCoreSlugs: outcome.danglingCoreSlugs,
|
|
@@ -18,9 +18,10 @@
|
|
|
18
18
|
* Each lane only ever ADDS candidates, so the pool is recall-safe by
|
|
19
19
|
* construction.
|
|
20
20
|
* 2. Build the candidate pool in CACHE ORDER: the stable prefix —
|
|
21
|
-
* `[...core (file order), ...hot (score order), ...fresh (recency
|
|
22
|
-
*
|
|
23
|
-
* candidates (needle → dense → edge
|
|
21
|
+
* `[...core (file order), ...hot (score order), ...fresh (recency order),
|
|
22
|
+
* ...always-candidate (skills pinned every turn)]`, all computed at lane
|
|
23
|
+
* init — followed by the finder candidates (needle → dense → edge
|
|
24
|
+
* surfacing order). The stable prefix
|
|
24
25
|
* is identical across consecutive turns while the lanes are unchanged
|
|
25
26
|
* (lane invalidation at consolidation is the recompute cadence), so the
|
|
26
27
|
* selector input's leading segment rides the provider KV cache (the
|
|
@@ -82,6 +83,10 @@ export interface OrchestrateDeps {
|
|
|
82
83
|
/** The modification-recency fresh set in recency order (computed at lane
|
|
83
84
|
* init with core and hot excluded). Follows hot in the stable prefix. */
|
|
84
85
|
freshSlugs: Slug[];
|
|
86
|
+
/** Skills pinned into the candidate pool every turn regardless of retrieval
|
|
87
|
+
* (existence-filtered and core/hot/fresh-excluded at lane init). Follow fresh
|
|
88
|
+
* in the stable prefix so the selector always sees them. */
|
|
89
|
+
alwaysCandidateSlugs?: Slug[];
|
|
85
90
|
/** Pre-rendered FULL cards for the stable-prefix slugs, keyed by slug.
|
|
86
91
|
* Rendered ONCE at lane init so the selector's stable prefix is
|
|
87
92
|
* byte-identical across turns (the cache contract). Every core/hot/fresh
|
|
@@ -172,14 +177,19 @@ export async function orchestrate(
|
|
|
172
177
|
const { sections } = deps.sectionIndex;
|
|
173
178
|
|
|
174
179
|
// The stable prefix: core (file order), hot (score order), fresh (recency
|
|
175
|
-
// order). Hot is computed with core excluded at
|
|
176
|
-
//
|
|
177
|
-
//
|
|
180
|
+
// order), then always-candidate skills. Hot is computed with core excluded at
|
|
181
|
+
// lane init, fresh with both excluded, always-candidate with all three
|
|
182
|
+
// excluded; the filters here are a cheap defensive dedup so a misbehaving lane
|
|
183
|
+
// can never double-list a slug.
|
|
178
184
|
const core = deps.coreSlugs;
|
|
179
185
|
const hot = deps.hotSlugs.filter((slug) => !core.includes(slug));
|
|
180
186
|
const coreHot = new Set<Slug>([...core, ...hot]);
|
|
181
187
|
const fresh = deps.freshSlugs.filter((slug) => !coreHot.has(slug));
|
|
182
|
-
const
|
|
188
|
+
const coreHotFresh = new Set<Slug>([...coreHot, ...fresh]);
|
|
189
|
+
const always = (deps.alwaysCandidateSlugs ?? []).filter(
|
|
190
|
+
(slug) => !coreHotFresh.has(slug),
|
|
191
|
+
);
|
|
192
|
+
const stablePrefix = new Set<Slug>([...coreHotFresh, ...always]);
|
|
183
193
|
|
|
184
194
|
// Step 1: needle (sync BM25) and dense (async embed + Qdrant) lanes run in
|
|
185
195
|
// parallel. Both return distinct articles each tagged with their best-scoring
|
|
@@ -342,19 +352,21 @@ export async function orchestrate(
|
|
|
342
352
|
// its own snippet line so its CURRENT relevance stays visible; `selectPool`
|
|
343
353
|
// dedupes selections by slug. Finder candidates with no match text fall
|
|
344
354
|
// back to the page's lead-section snippet.
|
|
345
|
-
const stable: StableCandidate[] = [...core, ...hot, ...fresh].map(
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
355
|
+
const stable: StableCandidate[] = [...core, ...hot, ...fresh, ...always].map(
|
|
356
|
+
(slug) => {
|
|
357
|
+
const card = deps.prefixCards.get(slug);
|
|
358
|
+
if (card === undefined) {
|
|
359
|
+
// Lane init renders a card for every core/hot slug; a hole here means
|
|
360
|
+
// the lanes and the card map are out of sync. Throw rather than render
|
|
361
|
+
// a degraded card — the caller (observeTurn) logs and skips the turn,
|
|
362
|
+
// which is better than silently breaking the byte-stable prefix.
|
|
363
|
+
throw new Error(
|
|
364
|
+
`memory-v3: no pre-rendered card for stable-prefix slug "${slug}"`,
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
return { slug, card };
|
|
368
|
+
},
|
|
369
|
+
);
|
|
358
370
|
const finderTail: PoolCandidate[] = finder.map((c) => ({
|
|
359
371
|
slug: c.slug,
|
|
360
372
|
lane: c.lane,
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import type { MemoryV3SelectionLog } from "../../../api/responses/memory-v3-selection-log.js";
|
|
20
20
|
import { isAssistantFeatureFlagEnabled } from "../../../config/assistant-feature-flags.js";
|
|
21
21
|
import { getConfig } from "../../../config/loader.js";
|
|
22
|
+
import { isMemoryV3Live } from "../../../config/memory-v3-gate.js";
|
|
22
23
|
import { getDb, getSqliteFrom } from "../../../memory/db-connection.js";
|
|
23
24
|
import { readPage } from "../../../memory/v2/page-store.js";
|
|
24
25
|
import { getWorkspaceDir } from "../../../util/platform.js";
|
|
@@ -35,7 +36,6 @@ import {
|
|
|
35
36
|
} from "./types.js";
|
|
36
37
|
|
|
37
38
|
const MEMORY_V3_SHADOW = "memory-v3-shadow" as const;
|
|
38
|
-
const MEMORY_V3_LIVE = "memory-v3-live" as const;
|
|
39
39
|
|
|
40
40
|
interface SelectionRow {
|
|
41
41
|
turn: number;
|
|
@@ -74,6 +74,52 @@ function rowsForMessageIds(messageIds: string[]): SelectionRow[] {
|
|
|
74
74
|
.all(...messageIds) as SelectionRow[];
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
const MAX_FORK_HOPS = 64;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Read the `forkSourceMessageId` back-pointer that `cloneForkMessageMetadata`
|
|
81
|
+
* stamps onto every message a fork copies, for the given message ids.
|
|
82
|
+
*/
|
|
83
|
+
function forkSourceIdsOf(messageIds: string[]): string[] {
|
|
84
|
+
if (messageIds.length === 0) return [];
|
|
85
|
+
const placeholders = messageIds.map(() => "?").join(", ");
|
|
86
|
+
const rows = getSqliteFrom(getDb())
|
|
87
|
+
.query(
|
|
88
|
+
/*sql*/ `
|
|
89
|
+
SELECT json_extract(metadata, '$.forkSourceMessageId') AS src
|
|
90
|
+
FROM messages
|
|
91
|
+
WHERE id IN (${placeholders})
|
|
92
|
+
`,
|
|
93
|
+
)
|
|
94
|
+
.all(...messageIds) as Array<{ src: string | null }>;
|
|
95
|
+
return rows
|
|
96
|
+
.map((r) => r.src)
|
|
97
|
+
.filter((src): src is string => typeof src === "string" && src.length > 0);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* A fork copies the parent's messages under fresh ids but does not copy their
|
|
102
|
+
* `memory_v3_selections` rows, so an inherited turn has no rows under its own
|
|
103
|
+
* message ids. Each copied message preserves a `forkSourceMessageId` pointer to
|
|
104
|
+
* the message it was cloned from; walk that chain (a fork of a fork chains it
|
|
105
|
+
* again) to the nearest ancestor generation that logged selections and return
|
|
106
|
+
* those rows. Returns `[]` when no ancestor has v3 rows (or the ids aren't fork
|
|
107
|
+
* copies).
|
|
108
|
+
*/
|
|
109
|
+
function rowsViaForkSource(messageIds: string[]): SelectionRow[] {
|
|
110
|
+
let frontier = messageIds;
|
|
111
|
+
const visited = new Set(messageIds);
|
|
112
|
+
for (let hop = 0; hop < MAX_FORK_HOPS; hop++) {
|
|
113
|
+
const sources = forkSourceIdsOf(frontier).filter((id) => !visited.has(id));
|
|
114
|
+
if (sources.length === 0) return [];
|
|
115
|
+
for (const id of sources) visited.add(id);
|
|
116
|
+
const rows = rowsForMessageIds(sources);
|
|
117
|
+
if (rows.length > 0) return rows;
|
|
118
|
+
frontier = sources;
|
|
119
|
+
}
|
|
120
|
+
return [];
|
|
121
|
+
}
|
|
122
|
+
|
|
77
123
|
/**
|
|
78
124
|
* Resolve each selection's persisted matched section `(slug, ordinal)` to the
|
|
79
125
|
* concrete `Section` in the CURRENT page, so the injected block renders the
|
|
@@ -133,7 +179,7 @@ async function buildSelectionLog(
|
|
|
133
179
|
|
|
134
180
|
return {
|
|
135
181
|
turn: rows[0]!.turn,
|
|
136
|
-
live:
|
|
182
|
+
live: isMemoryV3Live(config),
|
|
137
183
|
shadow: isAssistantFeatureFlagEnabled(MEMORY_V3_SHADOW, config),
|
|
138
184
|
selections,
|
|
139
185
|
injectedText,
|
|
@@ -150,13 +196,20 @@ async function buildSelectionLog(
|
|
|
150
196
|
* conversation with no v3 data). Message ids are globally unique, so no
|
|
151
197
|
* conversation scope is needed.
|
|
152
198
|
*
|
|
199
|
+
* For turns inherited from a fork, the copied messages carry fresh ids with no
|
|
200
|
+
* selection rows of their own, so the lookup falls back to the parent's rows by
|
|
201
|
+
* following each message's `forkSourceMessageId` back-pointer.
|
|
202
|
+
*
|
|
153
203
|
* Selection rows are stored in selection order, so rendering them in row order
|
|
154
204
|
* reproduces the block v3 would inject.
|
|
155
205
|
*/
|
|
156
206
|
export async function getMemoryV3SelectionForInspectorByMessageIds(
|
|
157
207
|
messageIds: string[],
|
|
158
208
|
): Promise<MemoryV3SelectionLog | null> {
|
|
159
|
-
|
|
209
|
+
const rows = rowsForMessageIds(messageIds);
|
|
210
|
+
return buildSelectionLog(
|
|
211
|
+
rows.length > 0 ? rows : rowsViaForkSource(messageIds),
|
|
212
|
+
);
|
|
160
213
|
}
|
|
161
214
|
|
|
162
215
|
/**
|
|
@@ -34,6 +34,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
34
34
|
import { isAssistantFeatureFlagEnabled } from "../../../config/assistant-feature-flags.js";
|
|
35
35
|
import { getConfig } from "../../../config/loader.js";
|
|
36
36
|
import type { AssistantConfig } from "../../../config/schema.js";
|
|
37
|
+
import { loadSkillCatalog } from "../../../config/skills.js";
|
|
37
38
|
import { getMessages } from "../../../memory/conversation-crud.js";
|
|
38
39
|
import { getDb, getSqliteFrom } from "../../../memory/db-connection.js";
|
|
39
40
|
import { stringifyMessageContent } from "../../../memory/message-content.js";
|
|
@@ -68,7 +69,6 @@ import {
|
|
|
68
69
|
} from "./types.js";
|
|
69
70
|
|
|
70
71
|
export const MEMORY_V3_SHADOW = "memory-v3-shadow" as const;
|
|
71
|
-
export const MEMORY_V3_LIVE = "memory-v3-live" as const;
|
|
72
72
|
|
|
73
73
|
const log = getLogger("memory-v3-shadow");
|
|
74
74
|
|
|
@@ -107,6 +107,9 @@ export interface ShadowLanes {
|
|
|
107
107
|
/** Modification-recency fresh set in recency order: core and hot excluded,
|
|
108
108
|
* filtered to pages in the section index. */
|
|
109
109
|
freshSlugs: string[];
|
|
110
|
+
/** Skills pinned into the stable prefix every turn (`always-candidate: true`
|
|
111
|
+
* in SKILL.md), existence-filtered and core/hot/fresh-excluded. */
|
|
112
|
+
alwaysCandidateSlugs: string[];
|
|
110
113
|
/** Learned-edge graph: co-selection NPMI associations over the selection
|
|
111
114
|
* log, rebuilt with the lanes (the consolidation cadence). */
|
|
112
115
|
learnedGraph: EdgeGraph;
|
|
@@ -216,6 +219,18 @@ async function initLanes(config: AssistantConfig): Promise<ShadowLanes> {
|
|
|
216
219
|
excludeSlugs: new Set([...coreSlugs, ...hotSlugs]),
|
|
217
220
|
}).filter((slug) => sectionIndex.byArticle.has(slug));
|
|
218
221
|
|
|
222
|
+
// Always-candidate skills are pinned into the stable prefix every turn so the
|
|
223
|
+
// selector can choose a cross-cutting capability (e.g. workflows) even when no
|
|
224
|
+
// retrieval lane surfaces it — its relevance is a judgment the model makes,
|
|
225
|
+
// not something embedding similarity finds. Filtered to skills present in the
|
|
226
|
+
// section index and excluded from core/hot/fresh so the prefix never
|
|
227
|
+
// double-lists a slug.
|
|
228
|
+
const prefixSet = new Set([...coreSlugs, ...hotSlugs, ...freshSlugs]);
|
|
229
|
+
const alwaysCandidateSlugs = loadSkillCatalog()
|
|
230
|
+
.filter((summary) => summary.alwaysCandidate === true)
|
|
231
|
+
.map((summary) => `skills/${summary.id}`)
|
|
232
|
+
.filter((slug) => sectionIndex.byArticle.has(slug) && !prefixSet.has(slug));
|
|
233
|
+
|
|
219
234
|
// Pre-render the stable-prefix cards ONCE per lane build: the selector's
|
|
220
235
|
// stable prefix must be byte-identical across turns to ride the provider KV
|
|
221
236
|
// cache, so the cards are frozen here (lane invalidation at consolidation is
|
|
@@ -228,7 +243,10 @@ async function initLanes(config: AssistantConfig): Promise<ShadowLanes> {
|
|
|
228
243
|
const modifiedAtBySlug = new Map(
|
|
229
244
|
pageIndex.entries.map((entry) => [entry.slug, entry.modifiedAt]),
|
|
230
245
|
);
|
|
231
|
-
const laneAnnotation = (
|
|
246
|
+
const laneAnnotation = (
|
|
247
|
+
slug: Slug,
|
|
248
|
+
lane: "core" | "hot" | "fresh" | "always",
|
|
249
|
+
) => {
|
|
232
250
|
if (lane !== "fresh") return `[lane: ${lane}]`;
|
|
233
251
|
const modifiedAt = modifiedAtBySlug.get(slug);
|
|
234
252
|
if (
|
|
@@ -249,6 +267,7 @@ async function initLanes(config: AssistantConfig): Promise<ShadowLanes> {
|
|
|
249
267
|
["core", coreSlugs],
|
|
250
268
|
["hot", hotSlugs],
|
|
251
269
|
["fresh", freshSlugs],
|
|
270
|
+
["always", alwaysCandidateSlugs],
|
|
252
271
|
] as const) {
|
|
253
272
|
for (const slug of slugs) {
|
|
254
273
|
const raw = await capabilityOrDiskBody(
|
|
@@ -303,6 +322,7 @@ async function initLanes(config: AssistantConfig): Promise<ShadowLanes> {
|
|
|
303
322
|
coreSlugs,
|
|
304
323
|
hotSlugs,
|
|
305
324
|
freshSlugs,
|
|
325
|
+
alwaysCandidateSlugs,
|
|
306
326
|
prefixCards,
|
|
307
327
|
};
|
|
308
328
|
}
|
|
@@ -545,6 +565,7 @@ export async function observeTurn(
|
|
|
545
565
|
coreSlugs: lanes.coreSlugs,
|
|
546
566
|
hotSlugs: lanes.hotSlugs,
|
|
547
567
|
freshSlugs: lanes.freshSlugs,
|
|
568
|
+
alwaysCandidateSlugs: lanes.alwaysCandidateSlugs,
|
|
548
569
|
prefixCards: lanes.prefixCards,
|
|
549
570
|
needleK: v3.needleK,
|
|
550
571
|
denseK: v3.denseK,
|