@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
|
@@ -19,6 +19,7 @@ import { z } from "zod";
|
|
|
19
19
|
|
|
20
20
|
import { getConfig } from "../../../../config/loader.js";
|
|
21
21
|
import type { AssistantConfig } from "../../../../config/types.js";
|
|
22
|
+
import { getTelemetrySqlite } from "../../../../persistence/db-connection.js";
|
|
22
23
|
import {
|
|
23
24
|
ACTOR_PRINCIPALS,
|
|
24
25
|
type RoutePolicy,
|
|
@@ -26,6 +27,7 @@ import {
|
|
|
26
27
|
import type { RouteDefinition } from "../../../../runtime/routes/types.js";
|
|
27
28
|
import { getLogger } from "../logging.js";
|
|
28
29
|
import { backfillAllSections } from "../v3/maintain-job.js";
|
|
30
|
+
import { MEMORY_V3_INJECTION_GATE_CHECK_NAME } from "../v3/orchestrate.js";
|
|
29
31
|
import { invalidateLanes } from "../v3/shadow-plugin.js";
|
|
30
32
|
|
|
31
33
|
const log = getLogger("memory-v3-routes");
|
|
@@ -101,6 +103,211 @@ const WRITE_POLICY: RoutePolicy = {
|
|
|
101
103
|
allowedPrincipalTypes: ACTOR_PRINCIPALS,
|
|
102
104
|
};
|
|
103
105
|
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
// gate-stats
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
|
|
110
|
+
const DEFAULT_LOOKBACK_DAYS = 30;
|
|
111
|
+
const MAX_LOOKBACK_DAYS = 90;
|
|
112
|
+
const MS_PER_DAY = 24 * 60 * 60 * 1000;
|
|
113
|
+
|
|
114
|
+
/** Corpus size buckets for gate-stats aggregation (in concept pages). */
|
|
115
|
+
const CORPUS_BUCKETS = [
|
|
116
|
+
{ label: "0–9", min: 0, max: 9 },
|
|
117
|
+
{ label: "10–49", min: 10, max: 49 },
|
|
118
|
+
{ label: "50–199", min: 50, max: 199 },
|
|
119
|
+
{ label: "200+", min: 200, max: Infinity },
|
|
120
|
+
] as const;
|
|
121
|
+
|
|
122
|
+
type BucketLabel = (typeof CORPUS_BUCKETS)[number]["label"];
|
|
123
|
+
|
|
124
|
+
interface BucketAcc {
|
|
125
|
+
total: number;
|
|
126
|
+
scored: number;
|
|
127
|
+
passed: number;
|
|
128
|
+
/** Passes that came from scored runs — the numerator for scoredPassRate. */
|
|
129
|
+
scoredPassed: number;
|
|
130
|
+
reasons: Record<string, number>;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function corpusBucketLabel(pageCount: number): BucketLabel {
|
|
134
|
+
for (const b of CORPUS_BUCKETS) {
|
|
135
|
+
if (pageCount <= b.max) {
|
|
136
|
+
return b.label;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return "200+";
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
interface GatePayloadDetail {
|
|
143
|
+
pass?: boolean;
|
|
144
|
+
reason?: string;
|
|
145
|
+
scored?: boolean;
|
|
146
|
+
real_concept_page_count?: number;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const GateStatsBucketSchema = z.object({
|
|
150
|
+
pageCountRange: z
|
|
151
|
+
.string()
|
|
152
|
+
.describe("Concept page count range (e.g. '10–49')"),
|
|
153
|
+
total: z
|
|
154
|
+
.number()
|
|
155
|
+
.describe("All gate runs in this bucket (scored + pass-open)"),
|
|
156
|
+
scored: z
|
|
157
|
+
.number()
|
|
158
|
+
.describe("Runs that weighed scores (dense lane available)"),
|
|
159
|
+
passed: z.number().describe("Runs that passed the gate"),
|
|
160
|
+
scoredPassRate: z
|
|
161
|
+
.number()
|
|
162
|
+
.nullable()
|
|
163
|
+
.describe(
|
|
164
|
+
"scoredPasses / scored (contested decisions only); null when no scored runs in bucket",
|
|
165
|
+
),
|
|
166
|
+
reasons: z
|
|
167
|
+
.record(z.string(), z.number())
|
|
168
|
+
.describe("Gate reason code → run count (dense_pass, fail_no_signal, …)"),
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
export const GateStatsResponseSchema = z.object({
|
|
172
|
+
lookbackDays: z.number().describe("Lookback window applied"),
|
|
173
|
+
totalRuns: z.number().describe("Total gate runs found in the window"),
|
|
174
|
+
buckets: z
|
|
175
|
+
.array(GateStatsBucketSchema)
|
|
176
|
+
.describe("Stats by concept page count range, lean→large"),
|
|
177
|
+
unknownPageCount: z
|
|
178
|
+
.object({
|
|
179
|
+
total: z.number(),
|
|
180
|
+
passed: z.number(),
|
|
181
|
+
reasons: z.record(z.string(), z.number()),
|
|
182
|
+
})
|
|
183
|
+
.describe("Runs with no real_concept_page_count in telemetry detail"),
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
export type GateStatsResponse = z.infer<typeof GateStatsResponseSchema>;
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Aggregate memory v3 injection gate runs from the telemetry outbox for the
|
|
190
|
+
* given lookback window. Groups runs by concept page count bucket and computes
|
|
191
|
+
* pass rates and reason distributions per bucket.
|
|
192
|
+
*
|
|
193
|
+
* Pass-open shortcuts (dense disabled/unavailable, gate threw) are included in
|
|
194
|
+
* `total` but not in `scored`, so `scoredPassRate` reflects only contested gate
|
|
195
|
+
* decisions — the signal that actually reveals whether the gate thresholds are
|
|
196
|
+
* calibrated correctly.
|
|
197
|
+
*/
|
|
198
|
+
export function handleMemoryV3GateStats(
|
|
199
|
+
lookbackDays: number = DEFAULT_LOOKBACK_DAYS,
|
|
200
|
+
db: ReturnType<typeof getTelemetrySqlite> = getTelemetrySqlite(),
|
|
201
|
+
): GateStatsResponse {
|
|
202
|
+
const days = Math.min(
|
|
203
|
+
Math.max(1, Math.floor(lookbackDays)),
|
|
204
|
+
MAX_LOOKBACK_DAYS,
|
|
205
|
+
);
|
|
206
|
+
const since = Date.now() - days * MS_PER_DAY;
|
|
207
|
+
|
|
208
|
+
const emptyBuckets = (): GateStatsResponse["buckets"] =>
|
|
209
|
+
CORPUS_BUCKETS.map((b) => ({
|
|
210
|
+
pageCountRange: b.label,
|
|
211
|
+
total: 0,
|
|
212
|
+
scored: 0,
|
|
213
|
+
passed: 0,
|
|
214
|
+
scoredPassRate: null,
|
|
215
|
+
reasons: {},
|
|
216
|
+
}));
|
|
217
|
+
if (!db) {
|
|
218
|
+
return {
|
|
219
|
+
lookbackDays: days,
|
|
220
|
+
totalRuns: 0,
|
|
221
|
+
buckets: emptyBuckets(),
|
|
222
|
+
unknownPageCount: { total: 0, passed: 0, reasons: {} },
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
const rows = db
|
|
227
|
+
.query(
|
|
228
|
+
/*sql*/ `
|
|
229
|
+
SELECT payload FROM telemetry_events
|
|
230
|
+
WHERE name = 'watchdog'
|
|
231
|
+
AND created_at >= ?
|
|
232
|
+
AND json_extract(payload, '$.check_name') = ?
|
|
233
|
+
`,
|
|
234
|
+
)
|
|
235
|
+
.all(since, MEMORY_V3_INJECTION_GATE_CHECK_NAME) as Array<{
|
|
236
|
+
payload: string;
|
|
237
|
+
}>;
|
|
238
|
+
|
|
239
|
+
const bucketMap = new Map<BucketLabel, BucketAcc>(
|
|
240
|
+
CORPUS_BUCKETS.map((b) => [
|
|
241
|
+
b.label,
|
|
242
|
+
{ total: 0, scored: 0, passed: 0, scoredPassed: 0, reasons: {} },
|
|
243
|
+
]),
|
|
244
|
+
);
|
|
245
|
+
const unknown: BucketAcc = {
|
|
246
|
+
total: 0,
|
|
247
|
+
scored: 0,
|
|
248
|
+
passed: 0,
|
|
249
|
+
scoredPassed: 0,
|
|
250
|
+
reasons: {},
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
for (const row of rows) {
|
|
254
|
+
let detail: GatePayloadDetail;
|
|
255
|
+
try {
|
|
256
|
+
const parsed = JSON.parse(row.payload) as { detail?: GatePayloadDetail };
|
|
257
|
+
detail = parsed.detail ?? {};
|
|
258
|
+
} catch {
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const pageCount =
|
|
263
|
+
typeof detail.real_concept_page_count === "number"
|
|
264
|
+
? detail.real_concept_page_count
|
|
265
|
+
: null;
|
|
266
|
+
|
|
267
|
+
const acc =
|
|
268
|
+
pageCount !== null
|
|
269
|
+
? (bucketMap.get(corpusBucketLabel(pageCount)) ?? unknown)
|
|
270
|
+
: unknown;
|
|
271
|
+
|
|
272
|
+
acc.total += 1;
|
|
273
|
+
if (detail.scored === true) {
|
|
274
|
+
acc.scored += 1;
|
|
275
|
+
if (detail.pass === true) {
|
|
276
|
+
acc.scoredPassed += 1;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
if (detail.pass === true) {
|
|
280
|
+
acc.passed += 1;
|
|
281
|
+
}
|
|
282
|
+
const reason =
|
|
283
|
+
typeof detail.reason === "string" ? detail.reason : "unknown";
|
|
284
|
+
acc.reasons[reason] = (acc.reasons[reason] ?? 0) + 1;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const buckets = CORPUS_BUCKETS.map((b) => {
|
|
288
|
+
const acc = bucketMap.get(b.label)!;
|
|
289
|
+
return {
|
|
290
|
+
pageCountRange: b.label,
|
|
291
|
+
total: acc.total,
|
|
292
|
+
scored: acc.scored,
|
|
293
|
+
passed: acc.passed,
|
|
294
|
+
scoredPassRate: acc.scored > 0 ? acc.scoredPassed / acc.scored : null,
|
|
295
|
+
reasons: acc.reasons,
|
|
296
|
+
};
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
return {
|
|
300
|
+
lookbackDays: days,
|
|
301
|
+
totalRuns: rows.length,
|
|
302
|
+
buckets,
|
|
303
|
+
unknownPageCount: {
|
|
304
|
+
total: unknown.total,
|
|
305
|
+
passed: unknown.passed,
|
|
306
|
+
reasons: unknown.reasons,
|
|
307
|
+
},
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
104
311
|
export const ROUTES: RouteDefinition[] = [
|
|
105
312
|
{
|
|
106
313
|
operationId: "memory_v3_rebuild_index",
|
|
@@ -197,6 +197,7 @@ const { memoryV2ConsolidateJob, CONSOLIDATION_FAILURE_CHECKPOINT_KEY } =
|
|
|
197
197
|
const { CUTOFF_PLACEHOLDER, CONSOLIDATION_PROMPT } =
|
|
198
198
|
await import("../prompts/consolidation.js");
|
|
199
199
|
const { invalidatePageIndex } = await import("../page-index.js");
|
|
200
|
+
const { formatRememberEntry } = await import("../../buffer-format.js");
|
|
200
201
|
|
|
201
202
|
// The handler only reads `config.memory.enabled`, `config.memory.v2.enabled`,
|
|
202
203
|
// `config.memory.v2.consolidation_prompt_path`, and
|
|
@@ -437,6 +438,38 @@ describe("memoryV2ConsolidateJob — chunked cutoff (consolidation_max_entries_p
|
|
|
437
438
|
expect(outcome.cutoff).not.toBe("[meeting-notes]");
|
|
438
439
|
});
|
|
439
440
|
|
|
441
|
+
test("a fact whose body holds a canonical entry line counts once and never supplies the cutoff", async () => {
|
|
442
|
+
// Written through the real writer, so this covers the round trip rather
|
|
443
|
+
// than a hand-built buffer: the body line carries the exact shape an entry
|
|
444
|
+
// opening has. Counting it would both inflate the per-run budget and let
|
|
445
|
+
// the cutoff be drawn from inside a fact, deferring entries against a
|
|
446
|
+
// timestamp the user never filed anything at.
|
|
447
|
+
writeFileSync(
|
|
448
|
+
bufferPath(),
|
|
449
|
+
formatRememberEntry(
|
|
450
|
+
"Alice's rollout plan:\n- [Apr 28, 9:00 AM] cutover",
|
|
451
|
+
new Date(2026, 3, 27, 9, 0),
|
|
452
|
+
) +
|
|
453
|
+
formatRememberEntry(
|
|
454
|
+
"Bob takes his coffee black.",
|
|
455
|
+
new Date(2026, 3, 27, 9, 1),
|
|
456
|
+
),
|
|
457
|
+
);
|
|
458
|
+
|
|
459
|
+
const outcome = await memoryV2ConsolidateJob(
|
|
460
|
+
makeJob(),
|
|
461
|
+
configWithMaxEntries(2),
|
|
462
|
+
);
|
|
463
|
+
|
|
464
|
+
expect(outcome.kind).toBe("invoked");
|
|
465
|
+
if (outcome.kind !== "invoked") {
|
|
466
|
+
throw new Error("unreachable");
|
|
467
|
+
}
|
|
468
|
+
// Two facts, cap 2 → no chunking. Three would chunk and defer one.
|
|
469
|
+
expect(outcome.deferredEntries).toBe(0);
|
|
470
|
+
expect(outcome.cutoff).not.toBe("Apr 28, 9:00 AM");
|
|
471
|
+
});
|
|
472
|
+
|
|
440
473
|
test("multi-line entries under the cap → full-buffer cutoff even when raw line count exceeds it", async () => {
|
|
441
474
|
writeFileSync(
|
|
442
475
|
bufferPath(),
|
|
@@ -582,6 +615,32 @@ describe("memoryV2ConsolidateJob — non-empty buffer", () => {
|
|
|
582
615
|
}
|
|
583
616
|
});
|
|
584
617
|
|
|
618
|
+
test("threads page-index dangling links into the prompt's repair step", async () => {
|
|
619
|
+
const conceptsDir = join(tmpWorkspace, "memory", "concepts");
|
|
620
|
+
const sourcePath = join(conceptsDir, "atl-1290.md");
|
|
621
|
+
// A `links:` entry naming a page that does not exist (the LUM-3316 shape).
|
|
622
|
+
writeFileSync(
|
|
623
|
+
sourcePath,
|
|
624
|
+
"---\nlinks:\n - atl-1291\n---\n# ATL-1290\n\nSee [[atl-1291]].\n",
|
|
625
|
+
);
|
|
626
|
+
invalidatePageIndex();
|
|
627
|
+
try {
|
|
628
|
+
const result = await memoryV2ConsolidateJob(makeJob(), CONFIG);
|
|
629
|
+
expect(result.kind).toBe("invoked");
|
|
630
|
+
const prompt = runnerLastArgs?.prompt as string;
|
|
631
|
+
expect(prompt).toContain("resolve dangling links");
|
|
632
|
+
expect(prompt).toContain(
|
|
633
|
+
"- `memory/concepts/atl-1290.md` → `atl-1291` (frontmatter `links:` entry)",
|
|
634
|
+
);
|
|
635
|
+
expect(prompt).toContain(
|
|
636
|
+
"- `memory/concepts/atl-1290.md` → `atl-1291` (inline `[[wikilink]]` in the body)",
|
|
637
|
+
);
|
|
638
|
+
} finally {
|
|
639
|
+
rmSync(sourcePath, { force: true });
|
|
640
|
+
invalidatePageIndex();
|
|
641
|
+
}
|
|
642
|
+
});
|
|
643
|
+
|
|
585
644
|
test("dispatches with skipPromptIndexing so the kickoff prompt is not indexed", async () => {
|
|
586
645
|
// The prompt is a static instruction manual — indexing it would write
|
|
587
646
|
// near-identical memory segments, embeddings, and a lexical entry on
|
|
@@ -847,6 +906,94 @@ describe("memoryV2ConsolidateJob — progress check and follow-up coalescing", (
|
|
|
847
906
|
expect(enqueuedJobs).toHaveLength(0);
|
|
848
907
|
});
|
|
849
908
|
|
|
909
|
+
test("a run that leaves dangling links reports the count and still enqueues follow-ups", async () => {
|
|
910
|
+
const conceptsDir = join(tmpWorkspace, "memory", "concepts");
|
|
911
|
+
runnerImpl = async () => {
|
|
912
|
+
// The agent files the buffer into a page whose links: names a page it
|
|
913
|
+
// never spawned. Writing through the file tools would invalidate the
|
|
914
|
+
// index via the tool hook; the stub does so by hand.
|
|
915
|
+
writeFileSync(
|
|
916
|
+
join(conceptsDir, "atl-1290.md"),
|
|
917
|
+
"---\nlinks:\n - atl-1291\n---\n# ATL-1290\n",
|
|
918
|
+
);
|
|
919
|
+
writeFileSync(bufferPath(), "");
|
|
920
|
+
invalidatePageIndex();
|
|
921
|
+
return { conversationId: "conv-1", ok: true };
|
|
922
|
+
};
|
|
923
|
+
try {
|
|
924
|
+
const result = await memoryV2ConsolidateJob(makeJob(), CONFIG);
|
|
925
|
+
|
|
926
|
+
expect(result.kind).toBe("invoked");
|
|
927
|
+
if (result.kind !== "invoked") {
|
|
928
|
+
throw new Error("unreachable");
|
|
929
|
+
}
|
|
930
|
+
expect(result.noProgress).toBe(false);
|
|
931
|
+
expect(result.danglingLinks).toBe(1);
|
|
932
|
+
// Surfaced, not gating: the page that was written still gets indexed.
|
|
933
|
+
expect(result.followUpJobIds).toEqual(["job-1"]);
|
|
934
|
+
expect(enqueuedJobs.map((j) => j.type)).toEqual(["memory_v2_reembed"]);
|
|
935
|
+
} finally {
|
|
936
|
+
rmSync(join(conceptsDir, "atl-1290.md"), { force: true });
|
|
937
|
+
invalidatePageIndex();
|
|
938
|
+
}
|
|
939
|
+
});
|
|
940
|
+
|
|
941
|
+
test("a run that spawns the missing target clears the pre-run dangling link", async () => {
|
|
942
|
+
const conceptsDir = join(tmpWorkspace, "memory", "concepts");
|
|
943
|
+
writeFileSync(
|
|
944
|
+
join(conceptsDir, "atl-1290.md"),
|
|
945
|
+
"---\nlinks:\n - atl-1291\n---\n# ATL-1290\n",
|
|
946
|
+
);
|
|
947
|
+
invalidatePageIndex();
|
|
948
|
+
runnerImpl = async () => {
|
|
949
|
+
writeFileSync(join(conceptsDir, "atl-1291.md"), "# ATL-1291\n");
|
|
950
|
+
writeFileSync(bufferPath(), "");
|
|
951
|
+
invalidatePageIndex();
|
|
952
|
+
return { conversationId: "conv-1", ok: true };
|
|
953
|
+
};
|
|
954
|
+
try {
|
|
955
|
+
const result = await memoryV2ConsolidateJob(makeJob(), CONFIG);
|
|
956
|
+
|
|
957
|
+
expect(result.kind).toBe("invoked");
|
|
958
|
+
if (result.kind !== "invoked") {
|
|
959
|
+
throw new Error("unreachable");
|
|
960
|
+
}
|
|
961
|
+
// The pre-run index fed the repair step...
|
|
962
|
+
expect(runnerLastArgs?.prompt as string).toContain(
|
|
963
|
+
"resolve dangling links",
|
|
964
|
+
);
|
|
965
|
+
// ...and the post-run count reflects the repair.
|
|
966
|
+
expect(result.danglingLinks).toBe(0);
|
|
967
|
+
} finally {
|
|
968
|
+
rmSync(join(conceptsDir, "atl-1290.md"), { force: true });
|
|
969
|
+
rmSync(join(conceptsDir, "atl-1291.md"), { force: true });
|
|
970
|
+
invalidatePageIndex();
|
|
971
|
+
}
|
|
972
|
+
});
|
|
973
|
+
|
|
974
|
+
test("a no-progress run still reports its dangling-link count", async () => {
|
|
975
|
+
const conceptsDir = join(tmpWorkspace, "memory", "concepts");
|
|
976
|
+
writeFileSync(
|
|
977
|
+
join(conceptsDir, "atl-1290.md"),
|
|
978
|
+
"---\nlinks:\n - atl-1291\n---\n# ATL-1290\n",
|
|
979
|
+
);
|
|
980
|
+
invalidatePageIndex();
|
|
981
|
+
runnerImpl = async () => ({ conversationId: "conv-1", ok: true });
|
|
982
|
+
try {
|
|
983
|
+
const result = await memoryV2ConsolidateJob(makeJob(), CONFIG);
|
|
984
|
+
|
|
985
|
+
expect(result.kind).toBe("invoked");
|
|
986
|
+
if (result.kind !== "invoked") {
|
|
987
|
+
throw new Error("unreachable");
|
|
988
|
+
}
|
|
989
|
+
expect(result.noProgress).toBe(true);
|
|
990
|
+
expect(result.danglingLinks).toBe(1);
|
|
991
|
+
} finally {
|
|
992
|
+
rmSync(join(conceptsDir, "atl-1290.md"), { force: true });
|
|
993
|
+
invalidatePageIndex();
|
|
994
|
+
}
|
|
995
|
+
});
|
|
996
|
+
|
|
850
997
|
test("a pending reembed suppresses its duplicate; v3 maintain still enqueues", async () => {
|
|
851
998
|
v3FlagOn = true;
|
|
852
999
|
pendingJobTypes.add("memory_v2_reembed");
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
CONSOLIDATION_PROMPT_V3,
|
|
27
27
|
CORE_PAGES_CONSOLIDATION_SECTION,
|
|
28
28
|
renderConsolidationPrompt,
|
|
29
|
+
renderDanglingLinksSection,
|
|
29
30
|
renderParseFailuresSection,
|
|
30
31
|
} from "../prompts/consolidation.js";
|
|
31
32
|
|
|
@@ -59,6 +60,13 @@ const GATED_SECTIONS: Array<{
|
|
|
59
60
|
]),
|
|
60
61
|
marker: "repair unreadable pages",
|
|
61
62
|
},
|
|
63
|
+
{
|
|
64
|
+
name: "dangling-link repair step (gate: index-reported dangling links)",
|
|
65
|
+
section: renderDanglingLinksSection([
|
|
66
|
+
{ from: "example-page", to: "example-target", kind: "links" },
|
|
67
|
+
]),
|
|
68
|
+
marker: "resolve dangling links",
|
|
69
|
+
},
|
|
62
70
|
];
|
|
63
71
|
|
|
64
72
|
/** All `ConsolidationPromptOptions` gates forced off — the v2-only rendering. */
|
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
getReachable,
|
|
16
16
|
invalidateEdgeIndex,
|
|
17
17
|
totalEdgeCount,
|
|
18
|
-
validateEdgeTargets,
|
|
19
18
|
} from "../edge-index.js";
|
|
20
19
|
import { deletePage, writePage } from "../page-store.js";
|
|
21
20
|
import type { ConceptPage } from "../types.js";
|
|
@@ -228,35 +227,6 @@ describe("getReachable", () => {
|
|
|
228
227
|
});
|
|
229
228
|
});
|
|
230
229
|
|
|
231
|
-
// ---------------------------------------------------------------------------
|
|
232
|
-
// validateEdgeTargets
|
|
233
|
-
// ---------------------------------------------------------------------------
|
|
234
|
-
|
|
235
|
-
describe("validateEdgeTargets", () => {
|
|
236
|
-
test("ok=true when every outgoing target has a known slug", async () => {
|
|
237
|
-
await writePage(workspaceDir, makePage("alice", ["bob"]));
|
|
238
|
-
await writePage(workspaceDir, makePage("bob"));
|
|
239
|
-
|
|
240
|
-
const idx = await getEdgeIndex(workspaceDir);
|
|
241
|
-
const result = validateEdgeTargets(idx, new Set(["alice", "bob"]));
|
|
242
|
-
expect(result).toEqual({ ok: true, missing: [] });
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
test("flags outgoing targets that don't correspond to a known slug", async () => {
|
|
246
|
-
await writePage(workspaceDir, makePage("alice", ["ghost", "bob"]));
|
|
247
|
-
await writePage(workspaceDir, makePage("bob", ["phantom"]));
|
|
248
|
-
|
|
249
|
-
const idx = await getEdgeIndex(workspaceDir);
|
|
250
|
-
const result = validateEdgeTargets(idx, new Set(["alice", "bob"]));
|
|
251
|
-
|
|
252
|
-
expect(result.ok).toBe(false);
|
|
253
|
-
expect(result.missing).toEqual([
|
|
254
|
-
{ from: "alice", to: "ghost" },
|
|
255
|
-
{ from: "bob", to: "phantom" },
|
|
256
|
-
]);
|
|
257
|
-
});
|
|
258
|
-
});
|
|
259
|
-
|
|
260
230
|
// ---------------------------------------------------------------------------
|
|
261
231
|
// totalEdgeCount
|
|
262
232
|
// ---------------------------------------------------------------------------
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
* - Batch over the cap: RangeError.
|
|
13
13
|
* - In-batch duplicate slug: later occurrence `invalid`.
|
|
14
14
|
* - Non-blocking warnings: missing `source`, unparseable `origin_date`,
|
|
15
|
-
* frontmatter `slug` differing from the storage slug
|
|
15
|
+
* frontmatter `slug` differing from the storage slug, link targets
|
|
16
|
+
* neither on disk nor in the batch.
|
|
16
17
|
* - Under-lock collision snapshot: a page committed between validation and
|
|
17
18
|
* the write phase is skipped, and `listPages` runs after `tryAcquireLock`.
|
|
18
19
|
* - Partial write failure: the reindex fan-out still runs for committed
|
|
@@ -363,6 +364,77 @@ describe("ingestPages", () => {
|
|
|
363
364
|
expect(await listPages(workspace)).toEqual(["people/alice", "people/bob"]);
|
|
364
365
|
});
|
|
365
366
|
|
|
367
|
+
test("link targets neither on disk nor in the batch warn without blocking the write", async () => {
|
|
368
|
+
// `people/bob` already exists on disk; `projects/garden` arrives in the
|
|
369
|
+
// same batch; `atl-1291` and `ghost` exist nowhere.
|
|
370
|
+
await ingestPages(workspace, [
|
|
371
|
+
{ slug: "people/bob", content: validPage("Bob.") },
|
|
372
|
+
]);
|
|
373
|
+
enqueuedJobs.length = 0;
|
|
374
|
+
|
|
375
|
+
const summary = await ingestPages(workspace, [
|
|
376
|
+
{
|
|
377
|
+
slug: "people/alice",
|
|
378
|
+
content:
|
|
379
|
+
"---\nsource: import:chatgpt\norigin_date: 2025-03-14\n" +
|
|
380
|
+
"links:\n - people/bob\n - projects/garden\n - atl-1291\n" +
|
|
381
|
+
"edges: [ghost]\n---\n" +
|
|
382
|
+
"Alice mentions [[projects/garden]] and [[people/bob]] and [[skills/deploy]].\n",
|
|
383
|
+
},
|
|
384
|
+
{ slug: "projects/garden", content: validPage("Garden.") },
|
|
385
|
+
]);
|
|
386
|
+
|
|
387
|
+
expect(summary.written).toBe(2);
|
|
388
|
+
expect(summary.invalid).toBe(0);
|
|
389
|
+
const [alice, garden] = summary.results;
|
|
390
|
+
expect(alice.action).toBe("written");
|
|
391
|
+
expect(alice.warnings).toHaveLength(1);
|
|
392
|
+
// Every unresolved target on one line, sorted; targets that resolve on
|
|
393
|
+
// disk, in the batch, or under a synthetic prefix stay silent.
|
|
394
|
+
expect(alice.warnings[0]).toContain(
|
|
395
|
+
"link targets not on disk or in this batch: atl-1291, ghost",
|
|
396
|
+
);
|
|
397
|
+
expect(alice.warnings[0]).not.toContain("people/bob");
|
|
398
|
+
expect(alice.warnings[0]).not.toContain("projects/garden");
|
|
399
|
+
expect(alice.warnings[0]).not.toContain("skills/deploy");
|
|
400
|
+
expect(garden.warnings).toEqual([]);
|
|
401
|
+
// Written regardless, and reindexed.
|
|
402
|
+
expect(await readPage(workspace, "people/alice")).not.toBeNull();
|
|
403
|
+
expect(enqueuedJobs.map((j) => j.type).sort()).toEqual([
|
|
404
|
+
"memory_v2_reembed",
|
|
405
|
+
"memory_v3_maintain",
|
|
406
|
+
]);
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
test("dangling-link warnings are reported on dry-run and skipped for pages the batch will not write", async () => {
|
|
410
|
+
await ingestPages(workspace, [
|
|
411
|
+
{ slug: "people/bob", content: validPage("Bob.") },
|
|
412
|
+
]);
|
|
413
|
+
|
|
414
|
+
const summary = await ingestPages(
|
|
415
|
+
workspace,
|
|
416
|
+
[
|
|
417
|
+
{
|
|
418
|
+
slug: "people/alice",
|
|
419
|
+
content: validPage("Alice mentions [[nowhere]]."),
|
|
420
|
+
},
|
|
421
|
+
// Exists on disk and overwrite is off, so this content is never
|
|
422
|
+
// written; its dangling link is not this batch's business.
|
|
423
|
+
{
|
|
424
|
+
slug: "people/bob",
|
|
425
|
+
content: validPage("Bob mentions [[also-nowhere]]."),
|
|
426
|
+
},
|
|
427
|
+
],
|
|
428
|
+
{ dryRun: true },
|
|
429
|
+
);
|
|
430
|
+
|
|
431
|
+
expect(summary.results[0].action).toBe("written");
|
|
432
|
+
expect(summary.results[0].warnings.join(" ")).toContain("nowhere");
|
|
433
|
+
expect(summary.results[1].action).toBe("skipped_exists");
|
|
434
|
+
expect(summary.results[1].warnings).toEqual([]);
|
|
435
|
+
expect(await readPage(workspace, "people/alice")).toBeNull();
|
|
436
|
+
});
|
|
437
|
+
|
|
366
438
|
test("a pending reindex job of the same type suppresses its duplicate enqueue", async () => {
|
|
367
439
|
pendingJobTypes = new Set(["memory_v2_reembed"]);
|
|
368
440
|
await ingestPages(workspace, [
|
|
@@ -56,6 +56,7 @@ const {
|
|
|
56
56
|
} = await import("../page-index.js");
|
|
57
57
|
const { writePage } = await import("../page-store.js");
|
|
58
58
|
const { invalidateEdgeIndex } = await import("../edge-index.js");
|
|
59
|
+
const { LINK_SEPARATOR } = await import("../page-links.js");
|
|
59
60
|
|
|
60
61
|
let workspaceDir: string;
|
|
61
62
|
|
|
@@ -272,6 +273,42 @@ describe("getPageIndex", () => {
|
|
|
272
273
|
const alice = idx.bySlug.get("alice")!;
|
|
273
274
|
const bob = idx.bySlug.get("bob")!;
|
|
274
275
|
expect(alice.edges).toEqual([bob.id]);
|
|
276
|
+
// Dropped from the numeric edges, reported on the index.
|
|
277
|
+
expect(idx.danglingLinks).toEqual([
|
|
278
|
+
{ from: "alice", to: "ghost", kind: "edges" },
|
|
279
|
+
]);
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
test("danglingLinks covers links:, body wikilinks, and edges:, resolved against pages and synthetic rows", async () => {
|
|
283
|
+
skillState.entries = [{ id: "browser", content: "Drive a browser." }];
|
|
284
|
+
const alice = makePage("alice", {
|
|
285
|
+
summary: "A",
|
|
286
|
+
edges: ["bob"],
|
|
287
|
+
body: "Alice uses [[skills/browser]] and [[bob|Bob]] but also [[nobody#sec]].",
|
|
288
|
+
});
|
|
289
|
+
alice.frontmatter.links = [
|
|
290
|
+
`bob${LINK_SEPARATOR}peer`,
|
|
291
|
+
`atl-1291${LINK_SEPARATOR}ticket`,
|
|
292
|
+
];
|
|
293
|
+
await writePage(workspaceDir, alice);
|
|
294
|
+
await writePage(workspaceDir, makePage("bob", { summary: "B" }));
|
|
295
|
+
|
|
296
|
+
const idx = await getPageIndex(workspaceDir);
|
|
297
|
+
expect(idx.danglingLinks).toEqual([
|
|
298
|
+
{ from: "alice", to: "atl-1291", kind: "links" },
|
|
299
|
+
{ from: "alice", to: "nobody", kind: "wikilink" },
|
|
300
|
+
]);
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
test("danglingLinks is empty when every reference resolves", async () => {
|
|
304
|
+
await writePage(
|
|
305
|
+
workspaceDir,
|
|
306
|
+
makePage("alice", { summary: "A", edges: ["bob"], body: "See [[bob]]." }),
|
|
307
|
+
);
|
|
308
|
+
await writePage(workspaceDir, makePage("bob", { summary: "B" }));
|
|
309
|
+
|
|
310
|
+
const idx = await getPageIndex(workspaceDir);
|
|
311
|
+
expect(idx.danglingLinks).toEqual([]);
|
|
275
312
|
});
|
|
276
313
|
|
|
277
314
|
test("exposes frontmatter leaves in the index entry", async () => {
|