@vellumai/assistant 0.11.3 → 0.11.4-dev.202608190019.b94dbf2
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/AGENTS.md +9 -3
- package/ARCHITECTURE.md +32 -10
- package/docs/architecture/memory.md +43 -5
- package/docs/architecture/security.md +96 -152
- package/docs/architecture/turn-actor.md +70 -0
- package/docs/browser-use-architecture-phase2.md +128 -56
- package/docs/flux-turn-detection-spike.md +248 -0
- package/docs/guardian-request-flow.md +35 -0
- package/docs/stt-provider-onboarding.md +3 -1
- package/knip.json +3 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
- package/node_modules/@vellumai/ces-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
- package/node_modules/@vellumai/gateway-client/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/node_modules/@vellumai/gateway-client/src/admission-policy-contract.ts +34 -0
- package/node_modules/@vellumai/gateway-client/src/gateway-ipc-contracts.ts +166 -1
- package/node_modules/@vellumai/gateway-client/src/index.ts +2 -0
- package/node_modules/@vellumai/service-contracts/package.json +1 -0
- package/node_modules/@vellumai/service-contracts/src/__tests__/url-normalization.test.ts +135 -0
- package/node_modules/@vellumai/service-contracts/src/channels.ts +11 -0
- package/node_modules/@vellumai/service-contracts/src/index.ts +1 -0
- package/node_modules/@vellumai/service-contracts/src/remote-web-pairing.ts +60 -0
- package/node_modules/@vellumai/service-contracts/src/url-normalization.ts +107 -0
- package/openapi.yaml +289 -113
- package/package.json +1 -1
- package/scripts/voice-ttft-spike.ts +3 -3
- package/scripts/write-plugin-api-shim.ts +10 -0
- package/src/__tests__/app-compiler.test.ts +38 -3
- package/src/__tests__/app-control-flow.test.ts +1 -0
- package/src/__tests__/approval-routes-http.test.ts +2 -2
- package/src/__tests__/assistant-feature-flag-guard.test.ts +25 -3
- package/src/__tests__/attachments-store.test.ts +21 -12
- package/src/__tests__/byok-default-profile-ensure.test.ts +17 -0
- package/src/__tests__/call-setup-flow-name-capture.test.ts +0 -1
- package/src/__tests__/call-site-routing-provider.test.ts +1 -1
- package/src/__tests__/channel-availability-routes.test.ts +14 -1
- package/src/__tests__/channel-capabilities-dedupe.test.ts +214 -0
- package/src/__tests__/channel-delivery-store.test.ts +14 -14
- package/src/__tests__/channel-inbound-disk-pressure.test.ts +4 -4
- package/src/__tests__/channel-setup-panel-ack.test.ts +1 -1
- package/src/__tests__/checker.test.ts +20 -314
- package/src/__tests__/cli-memory-v2-reembed-skills.test.ts +6 -2
- package/src/__tests__/compaction-events.test.ts +8 -10
- package/src/__tests__/config-loader-backfill.test.ts +3 -3
- package/src/__tests__/config-schema.test.ts +25 -10
- package/src/__tests__/context-search-memory-v2-source.test.ts +0 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +8 -11
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +8 -11
- package/src/__tests__/conversation-agent-loop.test.ts +32 -21
- package/src/__tests__/conversation-attachments.test.ts +0 -1
- package/src/__tests__/conversation-attention-store.test.ts +63 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +112 -0
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -4
- package/src/__tests__/conversation-fork-crud.test.ts +69 -0
- package/src/__tests__/conversation-fork-referential.test.ts +67 -0
- package/src/__tests__/conversation-fork-retrospective.test.ts +24 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +209 -0
- package/src/__tests__/conversation-notifiers-provenance.test.ts +59 -0
- package/src/__tests__/conversation-queue.test.ts +154 -4
- package/src/__tests__/conversation-routes-disk-view.test.ts +1 -1
- package/src/__tests__/conversation-routes-enabled-plugins.test.ts +1 -1
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +9 -9
- package/src/__tests__/conversation-routes-hidden-queue.test.ts +1 -1
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -1
- package/src/__tests__/conversation-runtime-assembly.test.ts +187 -102
- package/src/__tests__/conversation-runtime-workspace.test.ts +14 -10
- package/src/__tests__/conversation-slash-queue.test.ts +3 -0
- package/src/__tests__/conversation-surfaces-action-delivery.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-activation-emit.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-app-open.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-data-persist.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-history-restored-completion.test.ts +21 -14
- package/src/__tests__/conversation-surfaces-queued-emit.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-standalone.test.ts +1 -0
- package/src/__tests__/conversation-surfaces-state-update.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-table-action.test.ts +1 -1
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -1
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +1 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +1 -1
- package/src/__tests__/credential-prompt-route.test.ts +7 -10
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/cu-unified-flow.test.ts +1 -0
- package/src/__tests__/custom-profile-ensure.test.ts +5 -1
- package/src/__tests__/default-plugin-names-guard.test.ts +12 -1
- package/src/__tests__/discord-access-request-privacy.test.ts +5 -1
- package/src/__tests__/discord-requester-notice-privacy.test.ts +3 -3
- package/src/__tests__/document-append-idempotency.test.ts +233 -0
- package/src/__tests__/document-sync-tags.test.ts +0 -75
- package/src/__tests__/dynamic-skill-background-guard.test.ts +0 -2
- package/src/__tests__/edit-propagation.test.ts +107 -11
- package/src/__tests__/file-ops-service.test.ts +163 -30
- package/src/__tests__/filesystem-tools.test.ts +23 -24
- package/src/__tests__/gateway-only-guard.test.ts +2 -5
- package/src/__tests__/helpers/gateway-classify-mock.ts +27 -6
- package/src/__tests__/helpers/mock-actor-context.ts +49 -0
- package/src/__tests__/helpers/mock-conversation.ts +13 -1
- package/src/__tests__/host-file-read-tool.test.ts +16 -19
- package/src/__tests__/http-user-message-parity.test.ts +1 -1
- package/src/__tests__/init-feature-flag-overrides.test.ts +49 -0
- package/src/__tests__/injector-chain.test.ts +63 -41
- package/src/__tests__/injector-disk-pressure.test.ts +11 -23
- package/src/__tests__/inline-skill-load-permissions.test.ts +47 -36
- package/src/__tests__/llm-context-resolution.test.ts +73 -1
- package/src/__tests__/llm-schema.test.ts +5 -2
- package/src/__tests__/managed-skill-lifecycle.test.ts +7 -0
- package/src/__tests__/mcp-list-plugin-servers.test.ts +250 -0
- package/src/__tests__/media-generate-image.test.ts +131 -21
- package/src/__tests__/memory-retrieval-hook.test.ts +100 -7
- package/src/__tests__/messages-read-boundary-guard.test.ts +134 -0
- package/src/__tests__/mtime-cache.test.ts +3 -1
- package/src/__tests__/non-member-access-request.test.ts +0 -20
- package/src/__tests__/outbound-slack-persistence.test.ts +40 -1
- package/src/__tests__/platform-bash-auto-approve.test.ts +0 -4
- package/src/__tests__/plugin-api-store-credential.test.ts +261 -0
- package/src/__tests__/plugin-api-webhook-url.test.ts +10 -7
- package/src/__tests__/plugin-disabled-state.test.ts +2 -0
- package/src/__tests__/plugin-effective-enabled-set.test.ts +55 -3
- package/src/__tests__/plugin-execution-context.test.ts +73 -0
- package/src/__tests__/plugin-import-boundary-guard.test.ts +5 -1
- package/src/__tests__/plugin-import-boundary-reverse-guard.test.ts +7 -4
- package/src/__tests__/plugin-secret-pattern-contribution.test.ts +1 -1
- package/src/__tests__/post-compaction-reinjection-idempotency.test.ts +14 -7
- package/src/__tests__/provider-commit-message-generator.test.ts +20 -0
- package/src/__tests__/proxy-approval-callback.test.ts +1 -0
- package/src/__tests__/qdrant-manager.test.ts +14 -1
- package/src/__tests__/reaction-persistence.test.ts +200 -7
- package/src/__tests__/require-fresh-approval.test.ts +0 -4
- package/src/__tests__/risk-classification-boundary-guard.test.ts +115 -0
- package/src/__tests__/run-conversation-turn-persistence.test.ts +460 -105
- package/src/__tests__/run-due-schedules.test.ts +21 -0
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +187 -18
- package/src/__tests__/schedule-routes.test.ts +23 -0
- package/src/__tests__/schedule-store.test.ts +17 -0
- package/src/__tests__/scoped-approval-grants.test.ts +11 -6
- package/src/__tests__/secret-ingress-channel.test.ts +0 -1
- package/src/__tests__/secret-ingress-http.test.ts +1 -1
- package/src/__tests__/send-endpoint-busy.test.ts +3 -3
- package/src/__tests__/skills.test.ts +32 -0
- package/src/__tests__/slack-edit-ordering-characterization.test.ts +0 -1
- package/src/__tests__/starter-task-flow.test.ts +5 -4
- package/src/__tests__/subagent-call-site-routing.test.ts +31 -19
- package/src/__tests__/subagent-fork-prompt-role.test.ts +1 -1
- package/src/__tests__/subagent-spawn-and-await.test.ts +18 -17
- package/src/__tests__/subagent-tool-gate-mode.test.ts +1 -1
- package/src/__tests__/subagent-tools.test.ts +81 -101
- package/src/__tests__/surface-completion-in-flight-snapshot.test.ts +1 -0
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +132 -5
- package/src/__tests__/tool-executor.test.ts +68 -39
- package/src/__tests__/turn-events-store.test.ts +43 -0
- package/src/__tests__/ui-choice-copy-surfaces.test.ts +1 -1
- package/src/__tests__/ui-shape-teaching.test.ts +33 -0
- package/src/__tests__/ui-visual-surface.test.ts +1 -1
- package/src/__tests__/ui-voice-picker-surface.test.ts +128 -0
- package/src/__tests__/ui-work-result-surface.test.ts +1 -1
- package/src/__tests__/user-plugin-loader.test.ts +3 -1
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
- package/src/__tests__/visible-app-context.test.ts +16 -9
- package/src/__tests__/voice-config-update.test.ts +40 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +5 -3
- package/src/__tests__/voice-session-bridge.test.ts +85 -29
- package/src/__tests__/worker-entrypoint-guards.test.ts +54 -0
- package/src/__tests__/worker-plugin-surface.test.ts +77 -0
- package/src/__tests__/workspace-migration-142-consolidate-voice-front-door.test.ts +158 -0
- package/src/__tests__/workspace-migration-143-repair-deprecated-codex-model-id.test.ts +133 -0
- package/src/__tests__/workspace-migration-144-convert-stranded-subscription-openai-profiles.test.ts +316 -0
- package/src/__tests__/workspace-migration-145-collapse-profile-bindings-to-entries.test.ts +325 -0
- package/src/__tests__/workspace-migration-146-repair-retired-fireworks-deepseek-flash-model-id.test.ts +235 -0
- package/src/acp/__tests__/acp-claude-oauth.test.ts +10 -2
- package/src/acp/__tests__/auth-required.test.ts +161 -0
- package/src/acp/acp-claude-oauth.ts +19 -2
- package/src/acp/agent-process.test.ts +100 -0
- package/src/acp/agent-process.ts +29 -26
- package/src/acp/auth-required.ts +102 -0
- package/src/acp/session-manager.test.ts +119 -0
- package/src/acp/session-manager.ts +76 -3
- package/src/api/events/acp-auth-required.ts +55 -0
- package/src/api/events/host-file.ts +2 -2
- package/src/api/index.ts +7 -0
- package/src/api/surfaces.ts +12 -3
- package/src/apps/app-store.ts +3 -0
- package/src/bundler/package-resolver.ts +2 -30
- package/src/calls/__tests__/voice-session-bridge.test.ts +194 -11
- package/src/calls/__tests__/voice-triage-escalate.test.ts +102 -0
- package/src/calls/call-controller.ts +19 -3
- package/src/calls/call-setup-flow.ts +0 -1
- package/src/calls/media-stream-stt-session.ts +15 -0
- package/src/calls/voice-session-bridge.ts +115 -41
- package/src/calls/voice-triage-escalate.ts +105 -2
- package/src/channels/__tests__/plugin-channel-declarations.test.ts +161 -0
- package/src/channels/config.ts +13 -0
- package/src/channels/plugin-channel-declarations.ts +108 -0
- package/src/channels/types.ts +30 -0
- package/src/cli/AGENTS.md +5 -2
- package/src/cli/bundled-modules.ts +29 -0
- package/src/cli/commands/credentials.help.ts +2 -2
- package/src/cli/commands/db/repair.ts +4 -8
- package/src/cli/commands/domain.ts +6 -3
- package/src/cli/commands/email.ts +6 -3
- package/src/cli/commands/inference-providers.ts +1 -1
- package/src/cli/commands/keys.ts +8 -3
- package/src/cli/commands/mcp.help.ts +13 -4
- package/src/cli/commands/mcp.ts +9 -0
- package/src/cli/commands/memory/__tests__/memory-v2.test.ts +57 -7
- package/src/cli/commands/memory/__tests__/memory-v3.test.ts +128 -5
- package/src/cli/commands/memory/index.help.ts +84 -28
- package/src/cli/commands/memory/index.ts +2 -0
- package/src/cli/commands/memory/memory-v2.ts +58 -54
- package/src/cli/commands/memory/memory-v3.ts +64 -0
- package/src/cli/commands/memory/memory-validate.ts +18 -0
- package/src/cli/commands/plugins.ts +85 -36
- package/src/cli/commands/schedules.ts +35 -1
- package/src/cli/commands/stt.help.ts +27 -2
- package/src/cli/commands/trust.ts +3 -14
- package/src/cli/lib/__tests__/upgrade-plugin.test.ts +39 -0
- package/src/cli/lib/bundled-marketplace.json +14 -1
- package/src/cli/lib/upgrade-plugin.ts +42 -0
- package/src/config/__tests__/balanced-model-experiment.test.ts +278 -0
- package/src/config/__tests__/default-profile-catalog.test.ts +34 -2
- package/src/config/__tests__/default-provider.test.ts +6 -1
- package/src/config/__tests__/profile-materialization.test.ts +75 -19
- package/src/config/assistant-feature-flags.ts +36 -15
- package/src/config/balanced-model-experiment.ts +35 -0
- package/src/config/bundled-skills/acp/SKILL.md +6 -7
- package/src/config/bundled-skills/document-editor/SKILL.md +2 -2
- package/src/config/bundled-skills/document-editor/TOOLS.json +2 -2
- package/src/config/bundled-skills/image-studio/SKILL.md +5 -4
- package/src/config/bundled-skills/image-studio/TOOLS.json +1 -1
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +101 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +14 -4
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.test.ts +65 -0
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +7 -1
- package/src/config/bundled-skills/settings/tools/shared.ts +16 -0
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +9 -3
- package/src/config/bundled-skills/subagent/SKILL.md +17 -12
- package/src/config/bundled-skills/subagent/TOOLS.json +4 -4
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +22 -1
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +9 -2
- package/src/config/call-site-defaults.ts +11 -5
- package/src/config/default-profile-catalog.ts +179 -16
- package/src/config/default-profile-names.ts +4 -1
- package/src/config/default-provider-resolution.ts +4 -0
- package/src/config/feature-flag-registry.json +12 -12
- package/src/config/llm-context-resolution.ts +11 -3
- package/src/config/llm-resolver.ts +28 -1
- package/src/config/profile-materialization.ts +70 -22
- package/src/config/schemas/__tests__/live-voice.test.ts +107 -4
- package/src/config/schemas/call-site-catalog.ts +4 -4
- package/src/config/schemas/live-voice.ts +57 -23
- package/src/config/schemas/llm.ts +59 -32
- package/src/config/schemas/mcp.ts +23 -0
- package/src/config/schemas/plugin-updates.ts +6 -2
- package/src/config/schemas/stt.ts +1 -0
- package/src/config/skills.ts +9 -2
- package/src/context/outbound-sanitize.ts +96 -1
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +1 -1
- package/src/daemon/__tests__/plugin-mcp-reconcile.test.ts +82 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +15 -10
- package/src/daemon/conversation-agent-loop.ts +31 -19
- package/src/daemon/conversation-messaging.ts +5 -1
- package/src/daemon/conversation-notifiers.ts +20 -10
- package/src/daemon/conversation-process.ts +20 -6
- package/src/daemon/conversation-runtime-assembly.ts +12 -6
- package/src/daemon/conversation-store.ts +14 -4
- package/src/daemon/conversation-surfaces.ts +75 -26
- package/src/daemon/conversation-tool-setup.ts +3 -6
- package/src/daemon/conversation.ts +154 -51
- package/src/daemon/doordash-steps.ts +2 -2
- package/src/daemon/lifecycle.ts +14 -1
- package/src/daemon/mcp-reload-service.ts +36 -6
- package/src/daemon/process-message.ts +13 -26
- package/src/daemon/providers-setup.ts +6 -3
- package/src/daemon/trust-context-types.ts +29 -0
- package/src/daemon/trust-context.ts +12 -7
- package/src/daemon/wake-conversation-ops.ts +3 -2
- package/src/daemon/windows-compiled-entry.ts +4 -0
- package/src/documents/document-store.ts +143 -240
- package/src/hooks/hook-loader.ts +3 -3
- package/src/hooks/registry.ts +103 -45
- package/src/hooks/types.ts +5 -0
- package/src/inbound/__tests__/oauth-callback-url.test.ts +83 -0
- package/src/inbound/oauth-callback-url.ts +61 -0
- package/src/ipc/gateway-client.test.ts +1 -1
- package/src/ipc/gateway-client.ts +14 -21
- package/src/ipc/gateway-flag-listener.ts +17 -3
- package/src/live-voice/__tests__/live-voice-agent-turn.test.ts +1 -104
- package/src/live-voice/__tests__/live-voice-events.test.ts +7 -8
- package/src/live-voice/__tests__/live-voice-flux-turn-end.test.ts +1050 -0
- package/src/live-voice/__tests__/live-voice-metrics.test.ts +115 -8
- package/src/live-voice/__tests__/live-voice-photo.test.ts +100 -0
- package/src/live-voice/__tests__/live-voice-progress.test.ts +60 -194
- package/src/live-voice/__tests__/live-voice-stt.test.ts +14 -0
- package/src/live-voice/__tests__/live-voice-triage-escalate.test.ts +29 -0
- package/src/live-voice/__tests__/live-voice-tts-session.test.ts +0 -483
- package/src/live-voice/__tests__/live-voice-vad.test.ts +0 -16
- package/src/live-voice/__tests__/progress-narration.test.ts +214 -0
- package/src/live-voice/live-voice-archive.ts +2 -0
- package/src/live-voice/live-voice-manager.ts +16 -3
- package/src/live-voice/live-voice-metrics.ts +57 -32
- package/src/live-voice/live-voice-photo.ts +1 -2
- package/src/live-voice/live-voice-session.ts +598 -323
- package/src/live-voice/progress-narration.ts +277 -0
- package/src/live-voice/protocol.ts +21 -1
- package/src/live-voice/windows-compiled-live-voice.ts +4 -0
- package/src/mcp/__tests__/effective-config.test.ts +238 -0
- package/src/mcp/__tests__/mcp-auth-orchestrator.test.ts +0 -1
- package/src/mcp/__tests__/mcp-oauth-client-registration.test.ts +200 -0
- package/src/mcp/__tests__/mcp-oauth-provider.test.ts +9 -9
- package/src/mcp/__tests__/plugin-server-credential-isolation.test.ts +95 -0
- package/src/mcp/client.ts +16 -11
- package/src/mcp/effective-config.ts +113 -0
- package/src/mcp/manager.ts +11 -6
- package/src/mcp/mcp-auth-orchestrator.ts +13 -22
- package/src/mcp/mcp-oauth-provider.ts +205 -240
- package/src/monitoring/__tests__/plugin-auto-update.test.ts +166 -3
- package/src/monitoring/control.ts +1 -0
- package/src/monitoring/db-integrity-sample.ts +4 -5
- package/src/monitoring/plugin-auto-update.ts +128 -24
- package/src/notifications/AGENTS.md +2 -0
- package/src/notifications/approval-card-data.ts +33 -0
- package/src/notifications/signal.ts +1 -0
- package/src/permissions/AGENTS.md +16 -0
- package/src/permissions/checker.test.ts +75 -122
- package/src/permissions/checker.ts +44 -560
- package/src/permissions/confirmation-guardian-request.test.ts +15 -11
- package/src/permissions/confirmation-guardian-request.ts +2 -2
- package/src/permissions/prompter.ts +1 -5
- package/src/permissions/question-guardian-request.test.ts +14 -6
- package/src/permissions/question-guardian-request.ts +1 -2
- package/src/persistence/attachments-store.ts +8 -1
- package/src/persistence/bookmark-crud.ts +3 -7
- package/src/persistence/conversation-attention-store.ts +16 -45
- package/src/persistence/conversation-crud.ts +33 -4
- package/src/persistence/conversation-lineage.ts +9 -0
- package/src/persistence/conversation-queries.ts +182 -52
- package/src/persistence/delivery-crud.ts +150 -36
- package/src/persistence/embeddings/qdrant-manager.ts +84 -49
- package/src/persistence/external-conversation-store.ts +32 -4
- package/src/persistence/llm-request-log-store.ts +4 -10
- package/src/persistence/llm-usage-store.ts +8 -3
- package/src/persistence/message-reads.test.ts +197 -0
- package/src/persistence/message-reads.ts +211 -0
- package/src/persistence/migrations/360-add-document-workspace-path.ts +5 -14
- package/src/persistence/migrations/366-chatgpt-subscription-row-identity.test.ts +120 -0
- package/src/persistence/migrations/366-chatgpt-subscription-row-identity.ts +62 -0
- package/src/persistence/real-user-turn-filter.ts +27 -3
- package/src/persistence/schema/documents.ts +4 -4
- package/src/persistence/steps.ts +9 -0
- package/src/plugin-api/__tests__/oauth-callback-url-export.test.ts +29 -0
- package/src/plugin-api/constants.ts +8 -0
- package/src/plugin-api/conversation-turn.ts +195 -5
- package/src/plugin-api/index.ts +38 -6
- package/src/plugin-api/resolve-credential.ts +3 -2
- package/src/plugin-api/store-credential.ts +146 -0
- package/src/plugin-api/vision-support.test.ts +1 -1
- package/src/plugin-api/webhook-url.ts +13 -11
- package/src/plugins/__tests__/mcp-servers.test.ts +371 -0
- package/src/plugins/defaults/main.ts +21 -7
- package/src/plugins/defaults/memory/AGENTS.md +15 -3
- package/src/plugins/defaults/memory/__tests__/buffer-format.test.ts +204 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-accounting.test.ts +72 -0
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-job.test.ts +4 -1
- package/src/plugins/defaults/memory/__tests__/memory-retrospective-provider-path.test.ts +4 -1
- package/src/plugins/defaults/memory/__tests__/memory-tier-boundary-guard.test.ts +0 -1
- package/src/plugins/defaults/memory/buffer-format.ts +165 -0
- package/src/plugins/defaults/memory/context-search/sources/conversations.ts +6 -0
- package/src/plugins/defaults/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +77 -0
- package/src/plugins/defaults/memory/graph/conversation-graph-memory.ts +24 -6
- package/src/plugins/defaults/memory/graph/image-ref-utils.ts +3 -0
- package/src/plugins/defaults/memory/graph/tool-handlers.ts +1 -30
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.test.ts +34 -0
- package/src/plugins/defaults/memory/graph-topology/pending-buffer.ts +17 -24
- package/src/plugins/defaults/memory/hooks/post-compact.ts +1 -4
- package/src/plugins/defaults/memory/hooks/user-prompt-submit.ts +53 -5
- package/src/plugins/defaults/memory/indexer.ts +3 -1
- package/src/plugins/defaults/memory/memory-retrospective-accounting.ts +19 -7
- package/src/plugins/defaults/memory/memory-retrospective-job.ts +4 -4
- package/src/plugins/defaults/memory/src/__tests__/memory-v3-gate-stats.test.ts +281 -0
- package/src/plugins/defaults/memory/src/memory-v2-routes.ts +12 -10
- package/src/plugins/defaults/memory/src/memory-v3-routes.ts +207 -0
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-job.test.ts +147 -0
- package/src/plugins/defaults/memory/substrate/__tests__/consolidation-prompt-flag-gating-guard.test.ts +8 -0
- package/src/plugins/defaults/memory/substrate/__tests__/edge-index.test.ts +0 -30
- package/src/plugins/defaults/memory/substrate/__tests__/ingest.test.ts +73 -1
- package/src/plugins/defaults/memory/substrate/__tests__/page-index.test.ts +37 -0
- package/src/plugins/defaults/memory/substrate/__tests__/page-links.test.ts +208 -0
- package/src/plugins/defaults/memory/substrate/__tests__/prompts-consolidation.test.ts +158 -1
- package/src/plugins/defaults/memory/substrate/__tests__/static-context.test.ts +195 -58
- package/src/plugins/defaults/memory/substrate/consolidation-job.ts +93 -24
- package/src/plugins/defaults/memory/substrate/edge-index.ts +0 -31
- package/src/plugins/defaults/memory/substrate/ingest.ts +59 -0
- package/src/plugins/defaults/memory/substrate/page-index.ts +35 -8
- package/src/plugins/defaults/memory/substrate/page-links.ts +133 -0
- package/src/plugins/defaults/memory/substrate/page-store.ts +10 -0
- package/src/plugins/defaults/memory/substrate/prompts/consolidation.ts +124 -34
- package/src/plugins/defaults/memory/substrate/skill-content.ts +8 -1
- package/src/plugins/defaults/memory/substrate/static-context.ts +160 -33
- package/src/plugins/defaults/memory/substrate/sweep-job.ts +2 -4
- package/src/plugins/defaults/memory/v1/graph/extraction.ts +3 -1
- package/src/plugins/defaults/memory/v3/__tests__/injection.test.ts +18 -0
- package/src/plugins/defaults/memory/v3/__tests__/shadow-plugin.test.ts +66 -1
- package/src/plugins/defaults/memory/v3/card.ts +9 -9
- package/src/plugins/defaults/memory/v3/core-set.test.ts +7 -0
- package/src/plugins/defaults/memory/v3/core-set.ts +5 -1
- package/src/plugins/defaults/memory/v3/edge.ts +13 -57
- package/src/plugins/defaults/memory/v3/injector.ts +8 -0
- package/src/plugins/defaults/memory/v3/prune.ts +2 -0
- package/src/plugins/defaults/memory/v3/selection-log-store.ts +2 -0
- package/src/plugins/defaults/memory/v3/shadow-plugin.ts +23 -9
- package/src/plugins/defaults/memory/worker-control.ts +1 -0
- package/src/plugins/defaults/memory/worker.ts +6 -3
- package/src/plugins/defaults/worker-entrypoints.ts +3 -0
- package/src/plugins/external-plugin-loader.ts +47 -0
- package/src/plugins/mcp-servers.ts +361 -0
- package/src/plugins/mtime-cache.ts +37 -46
- package/src/plugins/pipeline.ts +14 -7
- package/src/plugins/plugin-execution-context.ts +35 -11
- package/src/plugins/worker-plugin-surface.ts +33 -0
- package/src/prompts/templates/system-sections.ts +0 -7
- package/src/providers/__tests__/connection-model-compat.test.ts +1 -1
- package/src/providers/__tests__/context-overflow-error.test.ts +24 -0
- package/src/providers/__tests__/dispatch-connection-routing.test.ts +214 -2
- package/src/providers/__tests__/preflight-resolved-config.test.ts +57 -0
- package/src/providers/__tests__/retry-callsite.test.ts +25 -2
- package/src/providers/anthropic/__tests__/pause-turn-continuation.test.ts +170 -0
- package/src/providers/anthropic/client.ts +332 -241
- package/src/providers/call-site-routing.ts +30 -3
- package/src/providers/connection-resolution.ts +194 -11
- package/src/providers/inference/__tests__/adapter-factory-openai-compatible.test.ts +4 -0
- package/src/providers/inference/adapter-factory.ts +3 -0
- package/src/providers/inference/auth.ts +6 -0
- package/src/providers/inference/connection-availability.ts +24 -2
- package/src/providers/inference/connections.ts +2 -0
- package/src/providers/model-catalog.ts +3 -3
- package/src/providers/model-intents.ts +28 -8
- package/src/providers/openai/__tests__/chat-completions-provider-reasoning.test.ts +163 -40
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +125 -2
- package/src/providers/openai/chat-completions-provider.ts +133 -36
- package/src/providers/openai/codex-models.ts +2 -1
- package/src/providers/provider-send-message.ts +32 -3
- package/src/providers/speech-to-text/__tests__/deepgram-flux-frames.test.ts +433 -0
- package/src/providers/speech-to-text/__tests__/deepgram-flux-realtime.test.ts +625 -0
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +34 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +285 -6
- package/src/providers/speech-to-text/deepgram-flux-frames.ts +395 -0
- package/src/providers/speech-to-text/deepgram-flux-realtime.ts +679 -0
- package/src/providers/speech-to-text/provider-catalog.ts +99 -8
- package/src/providers/speech-to-text/resolve.ts +25 -2
- package/src/routes/control.ts +1 -0
- package/src/routes/route-host-client.ts +1 -0
- package/src/routes/route-host-protocol.ts +7 -0
- package/src/routes/worker.ts +48 -15
- package/src/runtime/AGENTS.md +16 -17
- package/src/runtime/access-request-helper.ts +9 -12
- package/src/runtime/agent-wake.ts +18 -15
- package/src/runtime/effective-capabilities.test.ts +43 -0
- package/src/runtime/effective-capabilities.ts +27 -0
- package/src/runtime/guardian-reply-router.ts +6 -2
- package/src/runtime/pre-first-message-gate.ts +4 -0
- package/src/runtime/routes/__tests__/acp-claude-auth-routes.test.ts +12 -4
- package/src/runtime/routes/__tests__/conversation-list-routes.test.ts +522 -2
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +52 -0
- package/src/runtime/routes/__tests__/default-provider-routes.test.ts +61 -0
- package/src/runtime/routes/__tests__/inference-profiles-routes.test.ts +44 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +102 -1
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +132 -35
- package/src/runtime/routes/__tests__/schedule-routes-disarm-reason.test.ts +215 -0
- package/src/runtime/routes/__tests__/stt-routes.test.ts +25 -0
- package/src/runtime/routes/__tests__/surface-action-routes.test.ts +55 -6
- package/src/runtime/routes/__tests__/user-route-dispatcher-host.test.ts +52 -1
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +133 -1
- package/src/runtime/routes/channel-availability-routes.ts +32 -14
- package/src/runtime/routes/channel-route-shared.ts +0 -6
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +6 -6
- package/src/runtime/routes/conversation-list-routes.ts +181 -23
- package/src/runtime/routes/conversation-management-routes.ts +17 -12
- package/src/runtime/routes/conversation-query-routes.ts +40 -27
- package/src/runtime/routes/conversation-routes.ts +43 -29
- package/src/runtime/routes/credential-prompt-routes.ts +4 -7
- package/src/runtime/routes/credential-routes.ts +26 -82
- package/src/runtime/routes/default-provider-routes.ts +15 -0
- package/src/runtime/routes/documents-routes.ts +3 -222
- package/src/runtime/routes/inbound-message-handler.ts +21 -45
- package/src/runtime/routes/inbound-stages/acl-enforcement.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +0 -9
- package/src/runtime/routes/inbound-stages/admission-policy.ts +1 -17
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/bootstrap-intercept.ts +2 -3
- package/src/runtime/routes/inbound-stages/edit-intercept.ts +101 -79
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.test.ts +0 -1
- package/src/runtime/routes/inbound-stages/guardian-reply-intercept.ts +12 -7
- package/src/runtime/routes/inbound-stages/reaction-intercept.test.ts +91 -8
- package/src/runtime/routes/inbound-stages/reaction-intercept.ts +72 -79
- package/src/runtime/routes/inbound-stages/secret-ingress-check.ts +2 -3
- package/src/runtime/routes/inference-profiles-routes.ts +20 -11
- package/src/runtime/routes/inference-provider-connection-routes.ts +77 -15
- package/src/runtime/routes/log-export-routes.ts +3 -0
- package/src/runtime/routes/mcp-auth-routes.ts +148 -57
- package/src/runtime/routes/playground/__tests__/inject-failures.test.ts +2 -0
- package/src/runtime/routes/playground/__tests__/reset-circuit.test.ts +3 -0
- package/src/runtime/routes/playground/inject-failures.ts +2 -2
- package/src/runtime/routes/playground/reset-circuit.ts +1 -1
- package/src/runtime/routes/plugins-routes.ts +38 -8
- package/src/runtime/routes/schedule-routes.ts +94 -7
- package/src/runtime/routes/settings-routes.ts +6 -6
- package/src/runtime/routes/stt-routes.ts +31 -25
- package/src/runtime/routes/surface-action-routes.ts +20 -19
- package/src/runtime/routes/surface-conversation-resolver.ts +3 -0
- package/src/runtime/routes/user-route-dispatcher.ts +69 -15
- package/src/runtime/routes/user-route-import.ts +108 -0
- package/src/runtime/routes/user-route-resolution.ts +18 -1
- package/src/runtime/routes/workspace-routes.ts +0 -9
- package/src/runtime/routes/workspace-utils.ts +3 -13
- package/src/runtime/services/conversation-serializer.ts +7 -2
- package/src/schedule/__tests__/plugin-schedule-declarations.test.ts +68 -5
- package/src/schedule/__tests__/plugin-schedule-reconciler.test.ts +81 -0
- package/src/schedule/plugin-schedule-availability.ts +58 -0
- package/src/schedule/plugin-schedule-declarations.ts +23 -27
- package/src/schedule/plugin-schedule-reconciler.ts +12 -3
- package/src/schedule/schedule-store.ts +5 -1
- package/src/schedule/scheduler.ts +9 -4
- package/src/schedule/worker-control.ts +1 -0
- package/src/schedule/worker.ts +6 -0
- package/src/security/oauth2.ts +6 -22
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +22 -0
- package/src/stt/__tests__/types.test.ts +94 -0
- package/src/stt/daemon-batch-transcriber.ts +10 -0
- package/src/stt/stt-stream-session.ts +8 -4
- package/src/stt/types.ts +103 -0
- package/src/subagent/__tests__/consult-prompt.test.ts +26 -15
- package/src/subagent/consult-context.ts +11 -11
- package/src/subagent/consult-prompt.ts +26 -35
- package/src/subagent/manager.ts +21 -40
- package/src/subagent/notify.ts +7 -1
- package/src/subagent/types.ts +15 -12
- package/src/tools/__tests__/tool-input-schemas.test.ts +7 -7
- package/src/tools/acp/spawn.test.ts +97 -0
- package/src/tools/acp/spawn.ts +38 -4
- package/src/tools/credentials/ref-parse.ts +35 -0
- package/src/tools/credentials/resolve.ts +4 -10
- package/src/tools/credentials/store.ts +168 -0
- package/src/tools/document/document-tool.ts +12 -3
- package/src/tools/executor.ts +53 -15
- package/src/tools/filesystem/read.ts +27 -10
- package/src/tools/host-filesystem/read.ts +27 -15
- package/src/tools/network/url-safety.ts +9 -16
- package/src/tools/permission-checker.ts +43 -52
- package/src/tools/registry.ts +2 -1
- package/src/tools/shared/filesystem/file-ops-service.ts +63 -35
- package/src/tools/shared/filesystem/legacy-read-args.ts +22 -0
- package/src/tools/shared/filesystem/types.ts +5 -5
- package/src/tools/skills/scaffold-managed.ts +25 -7
- package/src/tools/subagent/spawn.ts +28 -88
- package/src/tools/tool-types.ts +26 -13
- package/src/tools/types.ts +6 -6
- package/src/tools/ui-surface/surface-shape-docs.ts +12 -1
- package/src/tools/workflows/run-workflow.ts +1 -2
- package/src/tts/__tests__/reasoning-tag-filter.test.ts +78 -0
- package/src/tts/reasoning-tag-filter.ts +89 -0
- package/src/util/__tests__/worker-process-command.test.ts +37 -0
- package/src/util/logger.ts +16 -0
- package/src/util/think-tag-stream.ts +95 -0
- package/src/util/worker-process.ts +37 -4
- package/src/windows-compiled-cli.ts +32 -0
- package/src/windows-compiled-entry.ts +4 -0
- package/src/windows-compiled-logger.ts +6 -0
- package/src/windows-compiled-worker-entry.ts +29 -0
- package/src/workspace/byok-default-profile-ensure.ts +76 -24
- package/src/workspace/custom-profile-ensure.ts +4 -24
- package/src/workspace/migrations/142-consolidate-voice-front-door.ts +70 -0
- package/src/workspace/migrations/143-repair-deprecated-codex-model-id.ts +134 -0
- package/src/workspace/migrations/144-convert-stranded-subscription-openai-profiles.ts +265 -0
- package/src/workspace/migrations/145-collapse-profile-bindings-to-entries.ts +328 -0
- package/src/workspace/migrations/146-repair-retired-fireworks-deepseek-flash-model-id.ts +195 -0
- package/src/workspace/migrations/__tests__/141-stt-english-default-to-multilingual.test.ts +0 -10
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/provider-commit-message-generator.ts +7 -5
- package/src/__tests__/document-workspace-file.test.ts +0 -467
- package/src/live-voice/__tests__/front-decision.test.ts +0 -645
- package/src/live-voice/front-decision.ts +0 -476
- package/src/permissions/ipc-risk-types.ts +0 -143
- package/src/permissions/risk-types.ts +0 -76
- package/src/subagent/__tests__/consult-transcript.test.ts +0 -184
- package/src/subagent/consult-transcript.ts +0 -90
|
@@ -18,7 +18,15 @@
|
|
|
18
18
|
import { readFile } from "node:fs/promises";
|
|
19
19
|
import { join } from "node:path";
|
|
20
20
|
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
formatRememberEntry,
|
|
23
|
+
matchBufferEntryStart,
|
|
24
|
+
} from "../buffer-format.js";
|
|
25
|
+
import {
|
|
26
|
+
extractWikilinkTargets,
|
|
27
|
+
WIKILINK_REGEX,
|
|
28
|
+
wikilinkTarget,
|
|
29
|
+
} from "../substrate/page-links.js";
|
|
22
30
|
import type { MemoryGraphEdge, MemoryGraphNode } from "./types.js";
|
|
23
31
|
|
|
24
32
|
/** Node id prefix marking a pending buffer entry (id = prefix + text hash). */
|
|
@@ -36,22 +44,10 @@ const MAX_PENDING_NODES = 200;
|
|
|
36
44
|
|
|
37
45
|
const PENDING_LABEL_MAX_CHARS = 60;
|
|
38
46
|
|
|
39
|
-
/**
|
|
40
|
-
* Matches a real entry start: `- [Mon D, h:mm AM/PM] fact`, the exact shape
|
|
41
|
-
* `formatRememberEntry` writes. The timestamp must be present and
|
|
42
|
-
* timestamp-shaped — a bullet with other bracketed text (e.g. a `- [ ]`
|
|
43
|
-
* checklist item inside a multiline fact) is a continuation, not an entry.
|
|
44
|
-
*/
|
|
45
|
-
const BUFFER_ENTRY_REGEX =
|
|
46
|
-
/^-\s+\[[A-Z][a-z]{2}\s+\d{1,2},\s+\d{1,2}:\d{2}\s+[AP]M\]\s*(.*)$/;
|
|
47
|
-
|
|
48
47
|
/** Lenient fallback for hand-written buffers: a plain bullet with no
|
|
49
48
|
* timestamped entry seen yet still counts as an entry of its own. */
|
|
50
49
|
const PLAIN_BULLET_REGEX = /^-\s+(.+)$/;
|
|
51
50
|
|
|
52
|
-
/** Matches `[[slug]]` / `[[slug|label]]` wikilinks inside an entry. */
|
|
53
|
-
const WIKILINK_REGEX = /\[\[([^[\]|]+)(?:\|[^\]]*)?\]\]/g;
|
|
54
|
-
|
|
55
51
|
export interface PendingBufferEntry {
|
|
56
52
|
/** Stable node id (`buffer:` + content hash, deduped within one parse). */
|
|
57
53
|
id: string;
|
|
@@ -102,10 +98,12 @@ export function parseBufferEntries(content: string): PendingBufferEntry[] {
|
|
|
102
98
|
|
|
103
99
|
for (const rawLine of content.split("\n")) {
|
|
104
100
|
const line = rawLine.trimEnd();
|
|
105
|
-
|
|
101
|
+
// Matched against the untrimmed line: an indented entry-shaped bullet is
|
|
102
|
+
// body text inside the fact above it, not a fact of its own.
|
|
103
|
+
const entryStart = matchBufferEntryStart(line);
|
|
106
104
|
if (entryStart) {
|
|
107
105
|
flush();
|
|
108
|
-
current = [entryStart
|
|
106
|
+
current = [entryStart.text];
|
|
109
107
|
continue;
|
|
110
108
|
}
|
|
111
109
|
if (current !== null) {
|
|
@@ -120,13 +118,7 @@ export function parseBufferEntries(content: string): PendingBufferEntry[] {
|
|
|
120
118
|
flush();
|
|
121
119
|
|
|
122
120
|
for (const text of texts) {
|
|
123
|
-
const slugs
|
|
124
|
-
for (const link of text.matchAll(WIKILINK_REGEX)) {
|
|
125
|
-
const slug = link[1]!.trim();
|
|
126
|
-
if (slug && !slugs.includes(slug)) {
|
|
127
|
-
slugs.push(slug);
|
|
128
|
-
}
|
|
129
|
-
}
|
|
121
|
+
const slugs = [...new Set(extractWikilinkTargets(text))];
|
|
130
122
|
|
|
131
123
|
const hash = hashText(text);
|
|
132
124
|
const count = (seen.get(hash) ?? 0) + 1;
|
|
@@ -148,8 +140,9 @@ export function parseBufferEntries(content: string): PendingBufferEntry[] {
|
|
|
148
140
|
function pendingLabel(text: string): string {
|
|
149
141
|
const firstLine = text.split("\n", 1)[0] ?? text;
|
|
150
142
|
const plain = firstLine
|
|
151
|
-
.replace(WIKILINK_REGEX, (_match,
|
|
152
|
-
|
|
143
|
+
.replace(WIKILINK_REGEX, (_match, inner: string) => {
|
|
144
|
+
const slug = wikilinkTarget(inner);
|
|
145
|
+
return slug.split("/").pop() || slug;
|
|
153
146
|
})
|
|
154
147
|
.trim();
|
|
155
148
|
if (plain.length <= PENDING_LABEL_MAX_CHARS) {
|
|
@@ -70,10 +70,7 @@ const postCompact: HookFunction<PostCompactContext> = async (ctx) => {
|
|
|
70
70
|
// concurrent request's actor and the contact/member registry the actor
|
|
71
71
|
// context derives from can be mutated by contact tools. The snapshots are the
|
|
72
72
|
// values the turn's initial assembly resolved.
|
|
73
|
-
const trust =
|
|
74
|
-
conversation?.currentTurnTrustContext ??
|
|
75
|
-
conversation?.trustContext ??
|
|
76
|
-
FALLBACK_TURN_TRUST;
|
|
73
|
+
const trust = conversation?.getTurnOrRestingTrust() ?? FALLBACK_TURN_TRUST;
|
|
77
74
|
const actorContext = conversation?.currentTurnInboundActorContext ?? null;
|
|
78
75
|
// Render the `model_profile:` label from the turn-start notice key, using
|
|
79
76
|
// the call site self-resolved from the live conversation — the same
|
|
@@ -31,7 +31,10 @@ import type {
|
|
|
31
31
|
HookFunction,
|
|
32
32
|
UserPromptSubmitContext,
|
|
33
33
|
} from "@vellumai/plugin-api";
|
|
34
|
-
import {
|
|
34
|
+
import {
|
|
35
|
+
updateMessageMetadata,
|
|
36
|
+
VOICE_ESCALATION_CONTINUATION_MESSAGE_KIND,
|
|
37
|
+
} from "@vellumai/plugin-api";
|
|
35
38
|
|
|
36
39
|
import type { MemoryRecalledEvent } from "../../../../api/events/memory-recalled.js";
|
|
37
40
|
import { getConfig } from "../../../../config/loader.js";
|
|
@@ -48,6 +51,7 @@ import { timeLatencySubSpan } from "../../../../daemon/turn-latency-sub-spans.js
|
|
|
48
51
|
import { broadcastMessage } from "../../../../runtime/assistant-event-hub.js";
|
|
49
52
|
import type { GraphMemoryResult } from "../graph/conversation-graph-memory.js";
|
|
50
53
|
import { recordMemoryRecallLog } from "../memory-recall-log-store.js";
|
|
54
|
+
import { stripTailInjectionsForReinjection } from "../tail-reinjection-strip.js";
|
|
51
55
|
import { MEMORY_V3_INJECTED_BLOCK_METADATA_KEY } from "../v3/ever-injected-store.js";
|
|
52
56
|
|
|
53
57
|
/**
|
|
@@ -69,6 +73,39 @@ export function shouldRunLegacyMemoryRetrieval(params: {
|
|
|
69
73
|
return params.isTrustedActor && !params.memoryV3Live;
|
|
70
74
|
}
|
|
71
75
|
|
|
76
|
+
/**
|
|
77
|
+
* The voice escalation continuation is a model-control message, not a
|
|
78
|
+
* retrieval query. Route memory on the latest preceding user message and strip
|
|
79
|
+
* the runtime context that was frozen onto it during its original turn.
|
|
80
|
+
*/
|
|
81
|
+
function legacyRetrievalRoutingMessages(
|
|
82
|
+
messages: UserPromptSubmitContext["latestMessages"],
|
|
83
|
+
routeOnPreviousUser: boolean,
|
|
84
|
+
): UserPromptSubmitContext["latestMessages"] {
|
|
85
|
+
if (!routeOnPreviousUser) {
|
|
86
|
+
return messages;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
for (let i = messages.length - 2; i >= 0; i -= 1) {
|
|
90
|
+
if (messages[i]?.role !== "user") {
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
const candidate = stripTailInjectionsForReinjection(
|
|
94
|
+
messages.slice(0, i + 1),
|
|
95
|
+
);
|
|
96
|
+
const tail = candidate[candidate.length - 1];
|
|
97
|
+
if (
|
|
98
|
+
tail?.role === "user" &&
|
|
99
|
+
tail.content.some(
|
|
100
|
+
(block) => block.type === "text" && block.text.trim().length > 0,
|
|
101
|
+
)
|
|
102
|
+
) {
|
|
103
|
+
return candidate;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return [];
|
|
107
|
+
}
|
|
108
|
+
|
|
72
109
|
/**
|
|
73
110
|
* Persist and broadcast the retrieval's side effects: the injected block on
|
|
74
111
|
* the user message's metadata (so it survives reloads), a recall-log row, and
|
|
@@ -263,10 +300,11 @@ async function persistInjectionBlocks(
|
|
|
263
300
|
* runs for every actor, writing the fully injected result back onto
|
|
264
301
|
* `latestMessages` and persisting the assembled blocks.
|
|
265
302
|
*
|
|
266
|
-
* Memory retrieval blocks the turn
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
303
|
+
* Memory retrieval ordinarily blocks the turn. The voice front door is the
|
|
304
|
+
* latency-sensitive exception: it keeps carried memory in the front prompt
|
|
305
|
+
* and defers current-turn retrieval to the escalated leg when one is needed.
|
|
306
|
+
* Cancellation still works via the live conversation signal for ordinary
|
|
307
|
+
* retrieval.
|
|
270
308
|
*/
|
|
271
309
|
const userPromptSubmitMemoryRetrieval: HookFunction<
|
|
272
310
|
UserPromptSubmitContext
|
|
@@ -292,9 +330,14 @@ const userPromptSubmitMemoryRetrieval: HookFunction<
|
|
|
292
330
|
// fallback — a v3 empty/failed selection yields no NEW injected memory that
|
|
293
331
|
// turn (prior turns' frozen v3 cards still ride history).
|
|
294
332
|
const memoryV3Live = isMemoryV3Live(config);
|
|
333
|
+
const isVoiceFrontDoor = conversation?.currentCallSite === "voiceFrontDoor";
|
|
334
|
+
if (isVoiceFrontDoor && conversation) {
|
|
335
|
+
conversation.graphMemory.recordPkbQueryVectors(undefined, undefined);
|
|
336
|
+
}
|
|
295
337
|
let v2BlockPersisted = false;
|
|
296
338
|
if (
|
|
297
339
|
shouldRunLegacyMemoryRetrieval({ isTrustedActor, memoryV3Live }) &&
|
|
340
|
+
!isVoiceFrontDoor &&
|
|
298
341
|
conversation &&
|
|
299
342
|
abortSignal
|
|
300
343
|
) {
|
|
@@ -311,6 +354,11 @@ const userPromptSubmitMemoryRetrieval: HookFunction<
|
|
|
311
354
|
config,
|
|
312
355
|
abortSignal,
|
|
313
356
|
broadcastMessage,
|
|
357
|
+
legacyRetrievalRoutingMessages(
|
|
358
|
+
ctx.latestMessages,
|
|
359
|
+
ctx.isHiddenPrompt === true &&
|
|
360
|
+
ctx.messageKind === VOICE_ESCALATION_CONTINUATION_MESSAGE_KIND,
|
|
361
|
+
),
|
|
314
362
|
),
|
|
315
363
|
);
|
|
316
364
|
|
|
@@ -123,7 +123,9 @@ export async function indexMessageNow(
|
|
|
123
123
|
// memory_segments has no cross-file FK to messages, so this call must not
|
|
124
124
|
// leave pieces for a message with no row. Skip early when the source
|
|
125
125
|
// row is already gone, the common case of a backfill job running after the
|
|
126
|
-
// message was deleted.
|
|
126
|
+
// message was deleted. Any-state existence check, deliberately: a
|
|
127
|
+
// streaming row exists, and orphan prevention is about deletion, not
|
|
128
|
+
// completeness. A post-write re-check below closes the narrower window
|
|
127
129
|
// where the delete lands mid-transaction.
|
|
128
130
|
const sourceMessage = getDb()
|
|
129
131
|
.select({ id: messages.id })
|
|
@@ -52,18 +52,30 @@ export function isSkillCardMessage(row: { metadata: string | null }): boolean {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
/**
|
|
55
|
-
* `getMessagesAfter` minus skill-card
|
|
56
|
-
* new-message slice: a card-only tail yields an
|
|
57
|
-
* `no_new_messages` early return), and a mixed tail's
|
|
58
|
-
* last real message rather than the card.
|
|
55
|
+
* `getMessagesAfter` truncated at the first unfinalized row, minus skill-card
|
|
56
|
+
* rows. The retrospective job's new-message slice: a card-only tail yields an
|
|
57
|
+
* empty slice (the job's `no_new_messages` early return), and a mixed tail's
|
|
58
|
+
* cutoff lands on the last real message rather than the card.
|
|
59
|
+
*
|
|
60
|
+
* The slice STOPS BEFORE the first unfinalized row rather than filtering it
|
|
61
|
+
* out. The job's cutoff (and therefore `lastProcessedMessageId` on success)
|
|
62
|
+
* is chosen from this slice, while the fork the job reviews skips unfinalized
|
|
63
|
+
* rows, so the cursor must never move past one: filtering through a stale
|
|
64
|
+
* mid-slice `finalized = 0` row would advance the cursor beyond it, and once
|
|
65
|
+
* the row later finalizes its `(createdAt, id)` sits behind the cursor,
|
|
66
|
+
* unreviewed forever. Truncating parks the cursor before the unfinalized row;
|
|
67
|
+
* the rows behind it are reviewed by the retrospective that runs after the
|
|
68
|
+
* row resolves (finalizes, or is repaired away by crash recovery).
|
|
59
69
|
*/
|
|
60
70
|
export function getRetrospectiveMessagesAfter(
|
|
61
71
|
conversationId: string,
|
|
62
72
|
afterMessageId: string | null,
|
|
63
73
|
): MessageRow[] {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
74
|
+
const rows = getMessagesAfter(conversationId, afterMessageId);
|
|
75
|
+
const firstUnfinalized = rows.findIndex((row) => row.finalized !== 1);
|
|
76
|
+
const bounded =
|
|
77
|
+
firstUnfinalized === -1 ? rows : rows.slice(0, firstUnfinalized);
|
|
78
|
+
return bounded.filter((row) => !isSkillCardMessage(row));
|
|
67
79
|
}
|
|
68
80
|
|
|
69
81
|
/**
|
|
@@ -702,10 +702,10 @@ interface SourceParityPins {
|
|
|
702
702
|
* parity; no system-prompt section branches on the flag, so this pin does not
|
|
703
703
|
* affect prompt output) and the tool-context pin (the live consumer, gating
|
|
704
704
|
* tool availability), using the live-turn derivation: interactive
|
|
705
|
-
* interfaces run `
|
|
706
|
-
* channel-routed and chrome-extension turns stay
|
|
707
|
-
*
|
|
708
|
-
* `process-message.ts` apply. Pinned explicitly even when it matches the
|
|
705
|
+
* interfaces run their turns with `isInteractive: true` (`hasNoClient` reads
|
|
706
|
+
* `false` for the turn), while channel-routed and chrome-extension turns stay
|
|
707
|
+
* clientless (`true`) — the exact `isInteractiveInterface` predicate
|
|
708
|
+
* `conversation-routes.ts` / `process-message.ts` apply. Pinned explicitly even when it matches the
|
|
709
709
|
* fork's hydrated value (`true`) so the parity contract doesn't depend on
|
|
710
710
|
* hydration defaults.
|
|
711
711
|
*
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for `handleMemoryV3GateStats` in `memory-v3-routes.ts`.
|
|
3
|
+
*
|
|
4
|
+
* The handler aggregates gate telemetry from the telemetry DB and buckets it
|
|
5
|
+
* by concept page count. These tests cover:
|
|
6
|
+
* - empty DB → zero counts, all buckets present, scoredPassRate null
|
|
7
|
+
* - mixed rows land in the correct buckets by real_concept_page_count
|
|
8
|
+
* - scored vs unscored runs are counted separately
|
|
9
|
+
* - rows with no real_concept_page_count go to unknownPageCount
|
|
10
|
+
* - lookbackDays is clamped to [1, 90]
|
|
11
|
+
* - malformed JSON rows are skipped
|
|
12
|
+
* - null/absent DB returns a zero response
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import type { Database } from "bun:sqlite";
|
|
16
|
+
import { describe, expect, test } from "bun:test";
|
|
17
|
+
|
|
18
|
+
import { handleMemoryV3GateStats } from "../memory-v3-routes.js";
|
|
19
|
+
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Minimal fake SQLite DB that returns pre-set rows
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
type FakeRow = { payload: string };
|
|
25
|
+
|
|
26
|
+
function makePayload(detail: Record<string, unknown>): string {
|
|
27
|
+
return JSON.stringify({
|
|
28
|
+
type: "watchdog",
|
|
29
|
+
daemon_event_id: "test-id",
|
|
30
|
+
recorded_at: Date.now(),
|
|
31
|
+
check_name: "memory_v3_injection_gate",
|
|
32
|
+
value: 1,
|
|
33
|
+
detail,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function fakeDb(rows: FakeRow[]): Database {
|
|
38
|
+
return {
|
|
39
|
+
query: (_sql: string) => ({
|
|
40
|
+
all: (..._args: unknown[]) => rows,
|
|
41
|
+
}),
|
|
42
|
+
} as unknown as Database;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// Tests
|
|
47
|
+
// ---------------------------------------------------------------------------
|
|
48
|
+
|
|
49
|
+
describe("handleMemoryV3GateStats", () => {
|
|
50
|
+
test("returns zero counts with all buckets when DB is null", () => {
|
|
51
|
+
const result = handleMemoryV3GateStats(30, null);
|
|
52
|
+
expect(result.totalRuns).toBe(0);
|
|
53
|
+
expect(result.buckets).toHaveLength(4);
|
|
54
|
+
for (const bucket of result.buckets) {
|
|
55
|
+
expect(bucket.total).toBe(0);
|
|
56
|
+
expect(bucket.scored).toBe(0);
|
|
57
|
+
expect(bucket.passed).toBe(0);
|
|
58
|
+
expect(bucket.scoredPassRate).toBeNull();
|
|
59
|
+
expect(bucket.reasons).toEqual({});
|
|
60
|
+
}
|
|
61
|
+
expect(result.unknownPageCount).toEqual({
|
|
62
|
+
total: 0,
|
|
63
|
+
passed: 0,
|
|
64
|
+
reasons: {},
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test("returns zero counts with all buckets when DB returns no rows", () => {
|
|
69
|
+
const result = handleMemoryV3GateStats(30, fakeDb([]));
|
|
70
|
+
expect(result.totalRuns).toBe(0);
|
|
71
|
+
expect(result.buckets[0]!.pageCountRange).toBe("0–9");
|
|
72
|
+
expect(result.buckets[3]!.pageCountRange).toBe("200+");
|
|
73
|
+
for (const b of result.buckets) {
|
|
74
|
+
expect(b.scoredPassRate).toBeNull();
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("buckets a dense_pass row into 10–49 bucket", () => {
|
|
79
|
+
const rows = [
|
|
80
|
+
{
|
|
81
|
+
payload: makePayload({
|
|
82
|
+
pass: true,
|
|
83
|
+
reason: "dense_pass",
|
|
84
|
+
scored: true,
|
|
85
|
+
real_concept_page_count: 25,
|
|
86
|
+
}),
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
const result = handleMemoryV3GateStats(30, fakeDb(rows));
|
|
90
|
+
expect(result.totalRuns).toBe(1);
|
|
91
|
+
const bucket = result.buckets.find((b) => b.pageCountRange === "10–49")!;
|
|
92
|
+
expect(bucket.total).toBe(1);
|
|
93
|
+
expect(bucket.scored).toBe(1);
|
|
94
|
+
expect(bucket.passed).toBe(1);
|
|
95
|
+
expect(bucket.scoredPassRate).toBe(1);
|
|
96
|
+
expect(bucket.reasons).toEqual({ dense_pass: 1 });
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("buckets a fail row into 200+ bucket", () => {
|
|
100
|
+
const rows = [
|
|
101
|
+
{
|
|
102
|
+
payload: makePayload({
|
|
103
|
+
pass: false,
|
|
104
|
+
reason: "fail_no_signal",
|
|
105
|
+
scored: true,
|
|
106
|
+
real_concept_page_count: 350,
|
|
107
|
+
}),
|
|
108
|
+
},
|
|
109
|
+
];
|
|
110
|
+
const result = handleMemoryV3GateStats(30, fakeDb(rows));
|
|
111
|
+
const bucket = result.buckets.find((b) => b.pageCountRange === "200+")!;
|
|
112
|
+
expect(bucket.total).toBe(1);
|
|
113
|
+
expect(bucket.passed).toBe(0);
|
|
114
|
+
expect(bucket.scoredPassRate).toBe(0);
|
|
115
|
+
expect(bucket.reasons).toEqual({ fail_no_signal: 1 });
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("unscored pass-open rows count in total but not scored", () => {
|
|
119
|
+
const rows = [
|
|
120
|
+
{
|
|
121
|
+
payload: makePayload({
|
|
122
|
+
pass: true,
|
|
123
|
+
reason: "dense_unavailable",
|
|
124
|
+
scored: false,
|
|
125
|
+
real_concept_page_count: 5,
|
|
126
|
+
}),
|
|
127
|
+
},
|
|
128
|
+
];
|
|
129
|
+
const result = handleMemoryV3GateStats(30, fakeDb(rows));
|
|
130
|
+
const bucket = result.buckets.find((b) => b.pageCountRange === "0–9")!;
|
|
131
|
+
expect(bucket.total).toBe(1);
|
|
132
|
+
expect(bucket.scored).toBe(0);
|
|
133
|
+
expect(bucket.passed).toBe(1);
|
|
134
|
+
// No scored runs → scoredPassRate is null (unscored pass does not count)
|
|
135
|
+
expect(bucket.scoredPassRate).toBeNull();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
test("unscored pass mixed with scored fail does not inflate scoredPassRate", () => {
|
|
139
|
+
// Regression: before fix, pass-open + scored-fail in same bucket gave 100%
|
|
140
|
+
const rows = [
|
|
141
|
+
{
|
|
142
|
+
payload: makePayload({
|
|
143
|
+
pass: true,
|
|
144
|
+
reason: "dense_unavailable",
|
|
145
|
+
scored: false,
|
|
146
|
+
real_concept_page_count: 30,
|
|
147
|
+
}),
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
payload: makePayload({
|
|
151
|
+
pass: false,
|
|
152
|
+
reason: "fail_no_signal",
|
|
153
|
+
scored: true,
|
|
154
|
+
real_concept_page_count: 40,
|
|
155
|
+
}),
|
|
156
|
+
},
|
|
157
|
+
];
|
|
158
|
+
const result = handleMemoryV3GateStats(30, fakeDb(rows));
|
|
159
|
+
const bucket = result.buckets.find((b) => b.pageCountRange === "10–49")!;
|
|
160
|
+
expect(bucket.total).toBe(2);
|
|
161
|
+
expect(bucket.scored).toBe(1);
|
|
162
|
+
expect(bucket.passed).toBe(1); // total passes (scored + unscored)
|
|
163
|
+
// Only the scored fail counts toward the rate — no scored passes
|
|
164
|
+
expect(bucket.scoredPassRate).toBe(0);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test("rows with no real_concept_page_count go to unknownPageCount", () => {
|
|
168
|
+
const rows = [
|
|
169
|
+
{
|
|
170
|
+
payload: makePayload({
|
|
171
|
+
pass: false,
|
|
172
|
+
reason: "fail_no_signal",
|
|
173
|
+
scored: true,
|
|
174
|
+
}),
|
|
175
|
+
},
|
|
176
|
+
];
|
|
177
|
+
const result = handleMemoryV3GateStats(30, fakeDb(rows));
|
|
178
|
+
expect(result.unknownPageCount.total).toBe(1);
|
|
179
|
+
expect(result.unknownPageCount.passed).toBe(0);
|
|
180
|
+
expect(result.unknownPageCount.reasons).toEqual({ fail_no_signal: 1 });
|
|
181
|
+
for (const b of result.buckets) {
|
|
182
|
+
expect(b.total).toBe(0);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test("mixed rows across multiple buckets are aggregated correctly", () => {
|
|
187
|
+
const rows: FakeRow[] = [
|
|
188
|
+
// 0–9 bucket: 1 unscored pass
|
|
189
|
+
{
|
|
190
|
+
payload: makePayload({
|
|
191
|
+
pass: true,
|
|
192
|
+
reason: "dense_disabled",
|
|
193
|
+
scored: false,
|
|
194
|
+
real_concept_page_count: 3,
|
|
195
|
+
}),
|
|
196
|
+
},
|
|
197
|
+
// 10–49 bucket: 1 pass, 1 fail (both scored)
|
|
198
|
+
{
|
|
199
|
+
payload: makePayload({
|
|
200
|
+
pass: true,
|
|
201
|
+
reason: "dense_pass",
|
|
202
|
+
scored: true,
|
|
203
|
+
real_concept_page_count: 20,
|
|
204
|
+
}),
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
payload: makePayload({
|
|
208
|
+
pass: false,
|
|
209
|
+
reason: "fail_no_signal",
|
|
210
|
+
scored: true,
|
|
211
|
+
real_concept_page_count: 45,
|
|
212
|
+
}),
|
|
213
|
+
},
|
|
214
|
+
// 50–199 bucket: 2 passes
|
|
215
|
+
{
|
|
216
|
+
payload: makePayload({
|
|
217
|
+
pass: true,
|
|
218
|
+
reason: "sparse_only_strong",
|
|
219
|
+
scored: true,
|
|
220
|
+
real_concept_page_count: 100,
|
|
221
|
+
}),
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
payload: makePayload({
|
|
225
|
+
pass: true,
|
|
226
|
+
reason: "dense_pass",
|
|
227
|
+
scored: true,
|
|
228
|
+
real_concept_page_count: 150,
|
|
229
|
+
}),
|
|
230
|
+
},
|
|
231
|
+
];
|
|
232
|
+
|
|
233
|
+
const result = handleMemoryV3GateStats(30, fakeDb(rows));
|
|
234
|
+
expect(result.totalRuns).toBe(5);
|
|
235
|
+
|
|
236
|
+
const b09 = result.buckets.find((b) => b.pageCountRange === "0–9")!;
|
|
237
|
+
expect(b09.total).toBe(1);
|
|
238
|
+
expect(b09.scored).toBe(0);
|
|
239
|
+
expect(b09.scoredPassRate).toBeNull();
|
|
240
|
+
|
|
241
|
+
const b1049 = result.buckets.find((b) => b.pageCountRange === "10–49")!;
|
|
242
|
+
expect(b1049.total).toBe(2);
|
|
243
|
+
expect(b1049.scored).toBe(2);
|
|
244
|
+
expect(b1049.passed).toBe(1);
|
|
245
|
+
expect(b1049.scoredPassRate).toBe(0.5);
|
|
246
|
+
expect(b1049.reasons).toEqual({ dense_pass: 1, fail_no_signal: 1 });
|
|
247
|
+
|
|
248
|
+
const b50199 = result.buckets.find((b) => b.pageCountRange === "50–199")!;
|
|
249
|
+
expect(b50199.scored).toBe(2);
|
|
250
|
+
expect(b50199.passed).toBe(2);
|
|
251
|
+
expect(b50199.scoredPassRate).toBe(1);
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
test("lookbackDays is clamped to 1–90", () => {
|
|
255
|
+
const r1 = handleMemoryV3GateStats(0, fakeDb([]));
|
|
256
|
+
expect(r1.lookbackDays).toBe(1);
|
|
257
|
+
const r2 = handleMemoryV3GateStats(999, fakeDb([]));
|
|
258
|
+
expect(r2.lookbackDays).toBe(90);
|
|
259
|
+
const r3 = handleMemoryV3GateStats(30, fakeDb([]));
|
|
260
|
+
expect(r3.lookbackDays).toBe(30);
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
test("malformed JSON payload rows are skipped", () => {
|
|
264
|
+
const rows: FakeRow[] = [
|
|
265
|
+
{ payload: "not-valid-json" },
|
|
266
|
+
{
|
|
267
|
+
payload: makePayload({
|
|
268
|
+
pass: true,
|
|
269
|
+
reason: "dense_pass",
|
|
270
|
+
scored: true,
|
|
271
|
+
real_concept_page_count: 25,
|
|
272
|
+
}),
|
|
273
|
+
},
|
|
274
|
+
];
|
|
275
|
+
const result = handleMemoryV3GateStats(30, fakeDb(rows));
|
|
276
|
+
// Only the valid row is counted
|
|
277
|
+
expect(result.totalRuns).toBe(2); // totalRuns counts raw rows before parse
|
|
278
|
+
const bucket = result.buckets.find((b) => b.pageCountRange === "10–49")!;
|
|
279
|
+
expect(bucket.total).toBe(1);
|
|
280
|
+
});
|
|
281
|
+
});
|
|
@@ -22,12 +22,9 @@ import type { RouteDefinition } from "../../../../runtime/routes/types.js";
|
|
|
22
22
|
import type { RouteHandlerArgs } from "../../../../runtime/routes/types.js";
|
|
23
23
|
import { getLogger } from "../logging.js";
|
|
24
24
|
import { getWorkspaceDir } from "../paths.js";
|
|
25
|
-
import {
|
|
26
|
-
getEdgeIndex,
|
|
27
|
-
totalEdgeCount,
|
|
28
|
-
validateEdgeTargets,
|
|
29
|
-
} from "../substrate/edge-index.js";
|
|
25
|
+
import { getEdgeIndex, totalEdgeCount } from "../substrate/edge-index.js";
|
|
30
26
|
import { getPageIndex } from "../substrate/page-index.js";
|
|
27
|
+
import type { DanglingLink } from "../substrate/page-links.js";
|
|
31
28
|
import {
|
|
32
29
|
getConceptsDir,
|
|
33
30
|
listPages,
|
|
@@ -111,14 +108,17 @@ async function handleBackfill({
|
|
|
111
108
|
|
|
112
109
|
const MemoryV2ValidateParams = z.object({}).strict();
|
|
113
110
|
|
|
114
|
-
type MissingEdgeEndpoint = { from: string; to: string };
|
|
115
111
|
type OversizedPage = { slug: string; chars: number };
|
|
116
112
|
type ParseFailure = { slug: string; error: string };
|
|
117
113
|
|
|
118
114
|
export type MemoryV2ValidateResult = {
|
|
119
115
|
pageCount: number;
|
|
120
116
|
edgeCount: number;
|
|
121
|
-
|
|
117
|
+
/**
|
|
118
|
+
* `links:`, `[[wikilink]]`, and `edges:` references whose target has no
|
|
119
|
+
* page (`PageIndex.danglingLinks`).
|
|
120
|
+
*/
|
|
121
|
+
danglingLinks: DanglingLink[];
|
|
122
122
|
oversizedPages: OversizedPage[];
|
|
123
123
|
parseFailures: ParseFailure[];
|
|
124
124
|
};
|
|
@@ -162,13 +162,15 @@ async function handleValidate({
|
|
|
162
162
|
}
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
const edgeIndex = await
|
|
166
|
-
|
|
165
|
+
const [edgeIndex, pageIndex] = await Promise.all([
|
|
166
|
+
getEdgeIndex(workspaceDir),
|
|
167
|
+
getPageIndex(workspaceDir),
|
|
168
|
+
]);
|
|
167
169
|
|
|
168
170
|
return {
|
|
169
171
|
pageCount: knownSlugs.size,
|
|
170
172
|
edgeCount: totalEdgeCount(edgeIndex),
|
|
171
|
-
|
|
173
|
+
danglingLinks: pageIndex.danglingLinks,
|
|
172
174
|
oversizedPages,
|
|
173
175
|
parseFailures,
|
|
174
176
|
};
|