@vellumai/assistant 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +6 -7
- package/Dockerfile +1 -0
- package/README.md +2 -2
- package/__tests__/permissions/gateway-threshold-reader.test.ts +79 -139
- package/bun.lock +3 -0
- package/docs/architecture/security.md +18 -16
- package/knip.json +1 -0
- package/node_modules/@vellumai/skill-host-contracts/__tests__/client.test.ts +1 -5
- package/node_modules/@vellumai/skill-host-contracts/src/assistant-event.ts +0 -5
- package/node_modules/@vellumai/skill-host-contracts/src/client.ts +10 -16
- package/node_modules/@vellumai/skill-host-contracts/src/skill-host.ts +1 -9
- package/node_modules/@vellumai/skill-host-contracts/src/tool-types.ts +12 -12
- package/node_modules/@vellumai/slack-text/bun.lock +24 -0
- package/node_modules/@vellumai/slack-text/package.json +18 -0
- package/node_modules/@vellumai/slack-text/src/index.test.ts +153 -0
- package/node_modules/@vellumai/slack-text/src/index.ts +235 -0
- package/node_modules/@vellumai/slack-text/tsconfig.json +20 -0
- package/openapi.yaml +294 -107
- package/package.json +4 -2
- package/scripts/generate-openapi.ts +16 -111
- package/src/__tests__/agent-wake-override-profile.test.ts +23 -1
- package/src/__tests__/anthropic-provider.test.ts +56 -13
- package/src/__tests__/app-conversation-ids-backfill.test.ts +278 -0
- package/src/__tests__/app-conversation-ids.test.ts +151 -0
- package/src/__tests__/approval-cascade.test.ts +0 -15
- package/src/__tests__/approval-routes-http.test.ts +6 -17
- package/src/__tests__/assistant-event-hub.test.ts +126 -77
- package/src/__tests__/assistant-event.test.ts +0 -5
- package/src/__tests__/assistant-events-sse-hardening.test.ts +37 -15
- package/src/__tests__/assistant-feature-flags-integration.test.ts +0 -29
- package/src/__tests__/background-shell-host-bash.test.ts +34 -43
- package/src/__tests__/call-controller.test.ts +1 -1
- package/src/__tests__/call-site-routing-provider.test.ts +193 -0
- package/src/__tests__/channel-approval-routes.test.ts +10 -296
- package/src/__tests__/channel-approvals.test.ts +25 -17
- package/src/__tests__/channel-guardian.test.ts +100 -146
- package/src/__tests__/checker.test.ts +20 -34
- package/src/__tests__/compact-event-conversation-id-guard.test.ts +50 -0
- package/src/__tests__/compaction-events.test.ts +2 -0
- package/src/__tests__/config-schema.test.ts +6 -48
- package/src/__tests__/config-watcher.test.ts +12 -0
- package/src/__tests__/connection-policy.test.ts +1 -52
- package/src/__tests__/contacts-write.test.ts +2 -64
- package/src/__tests__/context-image-dimensions.test.ts +1 -1
- package/src/__tests__/context-search-memory-source.test.ts +120 -1
- package/src/__tests__/context-search-memory-v2-source.test.ts +383 -0
- package/src/__tests__/context-search-pkb-source.test.ts +49 -0
- package/src/__tests__/context-search-workspace-source.test.ts +9 -22
- package/src/__tests__/context-window-manager.test.ts +46 -0
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +102 -29
- package/src/__tests__/conversation-agent-loop.test.ts +980 -13
- package/src/__tests__/conversation-analysis-routes.test.ts +12 -10
- package/src/__tests__/conversation-attention-telegram.test.ts +11 -3
- package/src/__tests__/conversation-confirmation-signals.test.ts +0 -291
- package/src/__tests__/conversation-history-web-search.test.ts +4 -3
- package/src/__tests__/conversation-inference-profile-route.test.ts +12 -23
- package/src/__tests__/conversation-lifecycle.test.ts +4 -4
- package/src/__tests__/conversation-process-callsite.test.ts +79 -2
- package/src/__tests__/conversation-queue.test.ts +3 -8
- package/src/__tests__/conversation-routes-disk-view.test.ts +1 -161
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +0 -32
- package/src/__tests__/conversation-routes-slash-commands.test.ts +75 -66
- package/src/__tests__/conversation-runtime-assembly.test.ts +257 -3
- package/src/__tests__/conversation-slash-commands.test.ts +24 -4
- package/src/__tests__/conversation-slash-queue.test.ts +2 -0
- package/src/__tests__/conversation-speed-override.test.ts +0 -3
- package/src/__tests__/conversation-starter-routes.test.ts +79 -2
- package/src/__tests__/conversation-surfaces-standalone-payloads.test.ts +12 -5
- package/src/__tests__/conversation-surfaces-standalone.test.ts +18 -14
- package/src/__tests__/conversation-surfaces-state-update.test.ts +3 -2
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +8 -46
- package/src/__tests__/conversation-usage.test.ts +253 -3
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +0 -39
- package/src/__tests__/credential-health-service.test.ts +68 -0
- package/src/__tests__/credential-security-e2e.test.ts +4 -3
- package/src/__tests__/credential-security-invariants.test.ts +1 -5
- package/src/__tests__/credential-token-resolver.test.ts +180 -0
- package/src/__tests__/cu-unified-flow.test.ts +33 -16
- package/src/__tests__/daemon-assistant-events.test.ts +34 -21
- package/src/__tests__/daemon-credential-client.test.ts +4 -1
- package/src/__tests__/db-connection-isolation.test.ts +125 -0
- package/src/__tests__/db-migration-rollback.test.ts +101 -0
- package/src/__tests__/db-slack-compaction-watermark-migration.test.ts +169 -0
- package/src/__tests__/deterministic-verification-control-plane.test.ts +7 -80
- package/src/__tests__/document-conversations.test.ts +332 -0
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +2 -2
- package/src/__tests__/emit-event-signal.test.ts +4 -6
- package/src/__tests__/events-client-registration.test.ts +193 -49
- package/src/__tests__/filing-service.test.ts +58 -7
- package/src/__tests__/first-greeting.test.ts +156 -150
- package/src/__tests__/fixtures/mock-chrome-extension.ts +108 -66
- package/src/__tests__/get-skill-detail-audit.test.ts +3 -8
- package/src/__tests__/guardian-binding-drift-heal.test.ts +1 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -1
- package/src/__tests__/guardian-grant-minting.test.ts +7 -2
- package/src/__tests__/guardian-routing-invariants.test.ts +7 -2
- package/src/__tests__/guardian-routing-state.test.ts +1 -1
- package/src/__tests__/handlers-skills-memory-v2-reseed.test.ts +32 -11
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +2 -83
- package/src/__tests__/headless-browser-mode.test.ts +4 -9
- package/src/__tests__/headless-browser-navigate.test.ts +21 -20
- package/src/__tests__/heartbeat-service.test.ts +289 -7
- package/src/__tests__/helpers/channel-test-adapter.ts +2 -2
- package/src/__tests__/helpers/create-guardian-binding.ts +91 -0
- package/src/__tests__/host-bash-proxy.test.ts +46 -122
- package/src/__tests__/host-browser-e2e-cloud.test.ts +36 -497
- package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +26 -96
- package/src/__tests__/host-browser-proxy.test.ts +111 -185
- package/src/__tests__/host-browser-routes.test.ts +45 -75
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +26 -30
- package/src/__tests__/host-cu-proxy.test.ts +56 -111
- package/src/__tests__/host-file-proxy.test.ts +44 -98
- package/src/__tests__/host-file-read-tool.test.ts +42 -21
- package/src/__tests__/host-shell-tool.test.ts +33 -68
- package/src/__tests__/host-transfer-pending-interactions.test.ts +2 -18
- package/src/__tests__/host-transfer-proxy.test.ts +43 -53
- package/src/__tests__/http-user-message-parity.test.ts +0 -6
- package/src/__tests__/inbound-slack-persistence.test.ts +31 -0
- package/src/__tests__/injector-chain.test.ts +10 -5
- package/src/__tests__/injector-pkb-v2-silenced.test.ts +124 -0
- package/src/__tests__/inline-command-runner.test.ts +0 -66
- package/src/__tests__/inline-skill-load-permissions.test.ts +0 -2
- package/src/__tests__/install-skill-routing.test.ts +1 -13
- package/src/__tests__/llm-callsite-catalog.test.ts +34 -0
- package/src/__tests__/llm-catalog-parity.test.ts +90 -0
- package/src/__tests__/llm-context-resolution.test.ts +180 -0
- package/src/__tests__/llm-resolver.test.ts +80 -12
- package/src/__tests__/llm-usage-store.test.ts +269 -4
- package/src/__tests__/log-export-routes.test.ts +89 -0
- package/src/__tests__/managed-profile-guard.test.ts +225 -0
- package/src/__tests__/managed-skill-lifecycle.test.ts +0 -10
- package/src/__tests__/manual-token-reconciliation.test.ts +334 -0
- package/src/__tests__/memory-v2-static-injector.test.ts +95 -0
- package/src/__tests__/migration-cross-version-compatibility.test.ts +197 -291
- package/src/__tests__/migration-export-http.test.ts +33 -26
- package/src/__tests__/migration-export-streaming.test.ts +18 -10
- package/src/__tests__/migration-export-to-gcs.test.ts +49 -9
- package/src/__tests__/migration-import-commit-http.test.ts +66 -21
- package/src/__tests__/migration-import-from-gcs.test.ts +50 -9
- package/src/__tests__/migration-import-from-url.test.ts +20 -6
- package/src/__tests__/migration-import-preflight-http.test.ts +95 -95
- package/src/__tests__/migration-parity-persistence.test.ts +62 -25
- package/src/__tests__/migration-transport.test.ts +115 -23
- package/src/__tests__/migration-validate-http.test.ts +105 -80
- package/src/__tests__/migration-wizard.test.ts +133 -27
- package/src/__tests__/non-member-access-request.test.ts +1 -1
- package/src/__tests__/notification-guardian-path.test.ts +1 -1
- package/src/__tests__/oauth-store.test.ts +19 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +21 -12
- package/src/__tests__/prechat-onboarding-contract.test.ts +31 -7
- package/src/__tests__/pricing.test.ts +68 -4
- package/src/__tests__/process-message-background-slack.test.ts +331 -0
- package/src/__tests__/provider-managed-proxy-integration.test.ts +153 -17
- package/src/__tests__/provider-send-message-override-profile.test.ts +50 -0
- package/src/__tests__/provider-usage-tracking.test.ts +208 -0
- package/src/__tests__/reaction-persistence.test.ts +9 -6
- package/src/__tests__/rebind-secrets-screen.test.ts +53 -16
- package/src/__tests__/recording-handler.test.ts +64 -81
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +4 -3
- package/src/__tests__/relay-server.test.ts +18 -13
- package/src/__tests__/require-fresh-approval.test.ts +13 -22
- package/src/__tests__/runtime-attachment-metadata.test.ts +1 -1
- package/src/__tests__/runtime-events-sse-parity.test.ts +3 -4
- package/src/__tests__/runtime-events-sse.test.ts +3 -12
- package/src/__tests__/search-skills-unified.test.ts +9 -15
- package/src/__tests__/secret-ingress-cli.test.ts +2 -5
- package/src/__tests__/secret-ingress-http.test.ts +0 -4
- package/src/__tests__/secret-onetime-send.test.ts +4 -2
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +24 -7
- package/src/__tests__/secret-prompter-channel-fallback.test.ts +42 -47
- package/src/__tests__/secret-response-routing.test.ts +29 -15
- package/src/__tests__/secret-routes-managed-proxy.test.ts +5 -1
- package/src/__tests__/secret-scanner.test.ts +2 -545
- package/src/__tests__/send-endpoint-busy.test.ts +9 -24
- package/src/__tests__/settings-routes.test.ts +1 -1
- package/src/__tests__/shell-credential-ref.test.ts +0 -8
- package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -56
- package/src/__tests__/skill-script-runner-sandbox.test.ts +0 -11
- package/src/__tests__/skill-tool-factory.test.ts +97 -0
- package/src/__tests__/skills-file-content-endpoint.test.ts +9 -30
- package/src/__tests__/skills-files-catalog-fallback.test.ts +11 -17
- package/src/__tests__/slack-inbound-verification.test.ts +1 -62
- package/src/__tests__/subagent-fork-notifications.test.ts +57 -47
- package/src/__tests__/subagent-manager-notify.test.ts +70 -70
- package/src/__tests__/subagent-notify-parent.test.ts +80 -83
- package/src/__tests__/system-prompt.test.ts +115 -13
- package/src/__tests__/terminal-tools.test.ts +0 -89
- package/src/__tests__/thread-backfill.test.ts +945 -31
- package/src/__tests__/tool-domain-event-publisher.test.ts +0 -36
- package/src/__tests__/tool-execute-pipeline.test.ts +0 -6
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -16
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +9 -19
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +4 -7
- package/src/__tests__/tool-executor.test.ts +12 -19
- package/src/__tests__/tool-metrics-listener.test.ts +0 -35
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +1 -0
- package/src/__tests__/tool-trace-listener.test.ts +0 -17
- package/src/__tests__/transfer-progress-screen.test.ts +63 -26
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +2 -149
- package/src/__tests__/trusted-contact-multichannel.test.ts +2 -4
- package/src/__tests__/trusted-contact-verification.test.ts +1 -1
- package/src/__tests__/tts-catalog-parity.test.ts +16 -5
- package/src/__tests__/usage-attribution.test.ts +247 -0
- package/src/__tests__/usage-cli.test.ts +143 -0
- package/src/__tests__/usage-grouped-buckets.test.ts +155 -0
- package/src/__tests__/usage-routes.test.ts +150 -0
- package/src/__tests__/validation-results-screen.test.ts +39 -16
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +12 -3
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +49 -137
- package/src/__tests__/verification-control-plane-policy.test.ts +4 -7
- package/src/__tests__/voice-session-bridge.test.ts +5 -5
- package/src/__tests__/workspace-migration-062-drop-memory-v2-edges-json.test.ts +103 -0
- package/src/__tests__/workspace-migration-063-release-notes-dynamic-model-context.test.ts +77 -0
- package/src/__tests__/workspace-migration-064-unwind-main-agent-opus-seed.test.ts +225 -0
- package/src/__tests__/workspace-migration-memory-v2-init.test.ts +8 -30
- package/src/acp/index.ts +0 -15
- package/src/acp/session-manager.ts +37 -34
- package/src/agent/loop.ts +16 -1
- package/src/approvals/AGENTS.md +4 -0
- package/src/approvals/__tests__/guardian-feed-event.test.ts +10 -3
- package/src/approvals/guardian-request-resolvers.ts +10 -2
- package/src/backup/__tests__/backup-worker.test.ts +36 -8
- package/src/backup/__tests__/paths.test.ts +2 -2
- package/src/backup/__tests__/restore.test.ts +45 -28
- package/src/backup/backup-worker.ts +36 -2
- package/src/backup/paths.ts +9 -6
- package/src/browser-session/events.ts +0 -9
- package/src/calls/call-store.ts +1 -34
- package/src/calls/guardian-question-copy.ts +0 -108
- package/src/calls/relay-server.ts +0 -24
- package/src/calls/twilio-rest.ts +0 -38
- package/src/calls/twilio-routes.ts +1 -1
- package/src/calls/voice-session-bridge.ts +7 -38
- package/src/channels/types.ts +1 -36
- package/src/cli/commands/__tests__/cache.test.ts +152 -5
- package/src/cli/commands/__tests__/memory-v2.test.ts +14 -28
- package/src/cli/commands/__tests__/trust.test.ts +21 -387
- package/src/cli/commands/backup.ts +4 -4
- package/src/cli/commands/cache-fs.ts +8 -0
- package/src/cli/commands/cache.ts +153 -82
- package/src/cli/commands/clients.ts +63 -5
- package/src/cli/commands/completions.ts +3 -3
- package/src/cli/commands/contacts.ts +231 -76
- package/src/cli/commands/keys.ts +4 -1
- package/src/cli/commands/memory-v2.ts +24 -52
- package/src/cli/commands/oauth/shared.ts +2 -29
- package/src/cli/commands/pending.ts +102 -0
- package/src/cli/commands/skills.ts +77 -35
- package/src/cli/commands/trust.ts +70 -430
- package/src/cli/commands/usage.ts +25 -16
- package/src/cli/lib/daemon-credential-client.ts +14 -0
- package/src/cli/program.ts +2 -0
- package/src/cli.ts +0 -21
- package/src/config/__tests__/feature-flag-registry-guard.test.ts +2 -2
- package/src/config/bundled-skills/messaging/TOOLS.json +14 -4
- package/src/config/env-registry.ts +12 -2
- package/src/config/env.ts +3 -14
- package/src/config/feature-flag-registry.json +30 -30
- package/src/config/llm-callsite-catalog.ts +12 -0
- package/src/config/llm-context-resolution.ts +80 -0
- package/src/config/llm-resolver.ts +58 -22
- package/src/config/loader.ts +3 -3
- package/src/config/schema.ts +2 -158
- package/src/config/schemas/__tests__/memory-v2.test.ts +1 -0
- package/src/config/schemas/call-site-catalog.ts +271 -0
- package/src/config/schemas/calls.ts +5 -5
- package/src/config/schemas/inference.ts +1 -1
- package/src/config/schemas/ingress.ts +1 -1
- package/src/config/schemas/llm.ts +31 -3
- package/src/config/schemas/memory-retrieval.ts +2 -2
- package/src/config/schemas/memory-v2.ts +9 -0
- package/src/config/schemas/security.ts +1 -42
- package/src/config/schemas/services.ts +6 -6
- package/src/config/schemas/skills.ts +5 -5
- package/src/config/schemas/tts.ts +1 -1
- package/src/config/seed-inference-profiles.ts +117 -0
- package/src/config/skills.ts +0 -90
- package/src/config/types.ts +3 -6
- package/src/contacts/contact-store.ts +0 -17
- package/src/contacts/contacts-write.ts +1 -105
- package/src/context/window-manager.ts +44 -5
- package/src/credential-execution/process-manager.ts +34 -10
- package/src/credential-health/credential-health-service.ts +21 -16
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +75 -82
- package/src/daemon/__tests__/daemon-skill-host.test.ts +2 -9
- package/src/daemon/connection-policy.ts +1 -26
- package/src/daemon/conversation-agent-loop-handlers.ts +53 -4
- package/src/daemon/conversation-agent-loop.ts +277 -36
- package/src/daemon/conversation-history.ts +8 -8
- package/src/daemon/conversation-launch.ts +20 -135
- package/src/daemon/conversation-lifecycle.ts +1 -1
- package/src/daemon/conversation-messaging.ts +1 -0
- package/src/daemon/conversation-process.ts +83 -163
- package/src/daemon/conversation-runtime-assembly.ts +219 -76
- package/src/daemon/conversation-slash.ts +47 -5
- package/src/daemon/conversation-store.ts +7 -31
- package/src/daemon/conversation-surfaces.ts +22 -28
- package/src/daemon/conversation-tool-setup.ts +3 -33
- package/src/daemon/conversation-usage.ts +36 -0
- package/src/daemon/conversation.ts +117 -233
- package/src/daemon/daemon-control.ts +3 -71
- package/src/daemon/daemon-skill-host.ts +8 -11
- package/src/daemon/dictation-profile-store.ts +2 -26
- package/src/daemon/first-greeting.ts +44 -156
- package/src/daemon/handlers/config-channels.ts +12 -12
- package/src/daemon/handlers/config-ingress.ts +4 -165
- package/src/daemon/handlers/config-model.ts +1 -1
- package/src/daemon/handlers/config-voice.ts +0 -42
- package/src/daemon/handlers/conversations.ts +11 -190
- package/src/daemon/handlers/recording.ts +26 -158
- package/src/daemon/handlers/shared.ts +23 -71
- package/src/daemon/handlers/skills.ts +42 -93
- package/src/daemon/host-bash-proxy.ts +67 -45
- package/src/daemon/host-browser-proxy.ts +65 -27
- package/src/daemon/host-cu-proxy.ts +40 -39
- package/src/daemon/host-file-proxy.ts +58 -37
- package/src/daemon/host-transfer-proxy.ts +84 -46
- package/src/daemon/lifecycle.ts +49 -15
- package/src/daemon/message-types/conversations.ts +7 -0
- package/src/daemon/message-types/host-bash.ts +1 -0
- package/src/daemon/message-types/host-cu.ts +1 -0
- package/src/daemon/message-types/host-file.ts +1 -0
- package/src/daemon/message-types/host-transfer.ts +1 -0
- package/src/daemon/message-types/messages.ts +10 -9
- package/src/daemon/message-types/workspace.ts +1 -1
- package/src/daemon/process-message.ts +102 -239
- package/src/daemon/server.ts +13 -462
- package/src/daemon/shutdown-handlers.ts +2 -2
- package/src/daemon/tool-side-effects.ts +125 -107
- package/src/daemon/trust-context.ts +13 -0
- package/src/daemon/wake-target-adapter.ts +4 -9
- package/src/events/domain-events.ts +0 -8
- package/src/events/tool-audit-listener.ts +3 -1
- package/src/events/tool-domain-event-publisher.ts +0 -10
- package/src/events/tool-metrics-listener.ts +0 -17
- package/src/events/tool-trace-listener.ts +0 -14
- package/src/filing/filing-service.ts +13 -1
- package/src/heartbeat/__tests__/heartbeat-feed-event.test.ts +6 -2
- package/src/heartbeat/heartbeat-service.ts +23 -5
- package/src/home/__tests__/feed-writer.test.ts +0 -4
- package/src/home/__tests__/relationship-state-writer.test.ts +30 -0
- package/src/home/feed-writer.ts +1 -2
- package/src/home/relationship-state-writer.ts +16 -3
- package/src/ipc/__tests__/browser-ipc.test.ts +2 -12
- package/src/ipc/__tests__/skill-server-bidirectional.test.ts +0 -1
- package/src/ipc/assistant-server.ts +3 -10
- package/src/ipc/routes/__tests__/memory-v2-backfill.test.ts +39 -20
- package/src/ipc/routes/route-adapter.ts +1 -1
- package/src/ipc/routes/trust-rules.test.ts +0 -95
- package/src/ipc/skill-ipc-types.ts +41 -0
- package/src/ipc/skill-routes/__tests__/events-ipc.test.ts +13 -27
- package/src/ipc/skill-routes/__tests__/identity.test.ts +4 -23
- package/src/ipc/skill-routes/events.ts +12 -23
- package/src/ipc/skill-routes/identity.ts +4 -17
- package/src/ipc/skill-routes/index.ts +1 -1
- package/src/ipc/skill-server.ts +6 -39
- package/src/live-voice/__tests__/runtime-websocket-shell.test.ts +0 -8
- package/src/live-voice/protocol.ts +4 -13
- package/src/mcp/manager.ts +0 -5
- package/src/memory/__tests__/fixtures/memory-v2-activation-fixtures.ts +55 -0
- package/src/memory/__tests__/memory-v2-activation-log-store.test.ts +127 -0
- package/src/memory/app-git-service.ts +0 -32
- package/src/memory/app-store.ts +154 -0
- package/src/memory/attachments-store.ts +6 -0
- package/src/memory/context-search/sources/memory-v2.ts +578 -0
- package/src/memory/context-search/sources/memory.ts +5 -0
- package/src/memory/context-search/sources/pkb.ts +10 -1
- package/src/memory/context-search/sources/workspace.ts +3 -2
- package/src/memory/conversation-crud.ts +29 -4
- package/src/memory/conversation-disk-view.ts +1 -5
- package/src/memory/conversation-starter-checkpoints.ts +63 -0
- package/src/memory/db-connection.ts +62 -0
- package/src/memory/db-init.ts +14 -0
- package/src/memory/embedding-backend.ts +3 -21
- package/src/memory/embedding-gemini.ts +0 -2
- package/src/memory/embedding-local.ts +6 -6
- package/src/memory/embedding-ollama.ts +6 -6
- package/src/memory/embedding-openai.ts +6 -6
- package/src/memory/embedding-types.ts +21 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +3 -7
- package/src/memory/graph/conversation-graph-memory.ts +35 -13
- package/src/memory/graph/injection.test.ts +2 -2
- package/src/memory/graph/injection.ts +1 -1
- package/src/memory/guardian-action-store.ts +0 -83
- package/src/memory/guardian-approvals.ts +0 -48
- package/src/memory/indexer.ts +1 -15
- package/src/memory/job-handlers/conversation-starters.ts +36 -53
- package/src/memory/job-utils.ts +0 -6
- package/src/memory/jobs-store.ts +0 -1
- package/src/memory/jobs-worker.ts +2 -16
- package/src/memory/llm-request-log-store.ts +0 -41
- package/src/memory/llm-usage-store.ts +129 -43
- package/src/memory/memory-v2-activation-log-store.ts +115 -0
- package/src/memory/migrations/233-document-conversations.ts +54 -0
- package/src/memory/migrations/234-memory-v2-activation-logs.ts +55 -0
- package/src/memory/migrations/235-llm-usage-attribution.ts +31 -0
- package/src/memory/migrations/235-slack-compaction-watermark.ts +44 -0
- package/src/memory/migrations/236-tool-invocations-matched-rule-id.ts +26 -0
- package/src/memory/migrations/__tests__/234-memory-v2-activation-logs.test.ts +182 -0
- package/src/memory/migrations/index.ts +14 -0
- package/src/memory/migrations/registry.ts +24 -0
- package/src/memory/raw-query.ts +2 -68
- package/src/memory/schema/conversations.ts +7 -0
- package/src/memory/schema/infrastructure.ts +25 -0
- package/src/memory/search/semantic.ts +5 -16
- package/src/memory/tool-usage-store.ts +2 -0
- package/src/memory/usage-buckets.ts +40 -1
- package/src/memory/usage-grouped-buckets.ts +127 -0
- package/src/memory/v2/__tests__/activation.test.ts +289 -90
- package/src/memory/v2/__tests__/backfill-jobs.test.ts +2 -129
- package/src/memory/v2/__tests__/consolidation-job.test.ts +28 -11
- package/src/memory/v2/__tests__/edge-index.test.ts +278 -0
- package/src/memory/v2/__tests__/injection.test.ts +384 -15
- package/src/memory/v2/__tests__/migration.test.ts +64 -36
- package/src/memory/v2/__tests__/page-store.test.ts +191 -8
- package/src/memory/v2/__tests__/prompts-consolidation.test.ts +181 -0
- package/src/memory/v2/__tests__/skill-store.test.ts +115 -3
- package/src/memory/v2/__tests__/static-context.test.ts +153 -0
- package/src/memory/v2/activation.ts +168 -97
- package/src/memory/v2/backfill-jobs.ts +15 -100
- package/src/memory/v2/consolidation-job.ts +14 -12
- package/src/memory/v2/edge-index.ts +191 -0
- package/src/memory/v2/injection.ts +182 -58
- package/src/memory/v2/migration.ts +57 -64
- package/src/memory/v2/now-text.ts +2 -3
- package/src/memory/v2/page-store.ts +168 -31
- package/src/memory/v2/prompts/consolidation.ts +118 -42
- package/src/memory/v2/prompts/sweep.ts +3 -3
- package/src/memory/v2/skill-store.ts +55 -7
- package/src/memory/v2/static-context.ts +62 -0
- package/src/memory/v2/types.ts +10 -20
- package/src/memory/validation.ts +0 -11
- package/src/messaging/draft-store.ts +0 -6
- package/src/messaging/provider-types.ts +8 -0
- package/src/messaging/provider.ts +7 -0
- package/src/messaging/providers/gmail/client.ts +1 -121
- package/src/messaging/providers/outlook/client.ts +0 -73
- package/src/messaging/providers/slack/__tests__/adapter-mention-rendering.test.ts +226 -0
- package/src/messaging/providers/slack/adapter.ts +122 -21
- package/src/messaging/providers/slack/backfill.test.ts +95 -6
- package/src/messaging/providers/slack/backfill.ts +89 -11
- package/src/messaging/providers/slack/client.ts +10 -124
- package/src/messaging/providers/slack/message-metadata.ts +12 -2
- package/src/messaging/providers/slack/render-transcript.test.ts +56 -0
- package/src/messaging/providers/slack/render-transcript.ts +126 -25
- package/src/messaging/providers/slack/types.ts +1 -0
- package/src/oauth/connection-resolver.test.ts +8 -0
- package/src/oauth/connection-resolver.ts +8 -16
- package/src/oauth/credential-token-resolver.ts +97 -0
- package/src/oauth/manual-token-connection.ts +30 -34
- package/src/oauth/oauth-store.ts +6 -4
- package/src/outbound-proxy/certs.ts +0 -7
- package/src/outbound-proxy/config.ts +0 -74
- package/src/outbound-proxy/health.ts +0 -44
- package/src/outbound-proxy/index.ts +0 -22
- package/src/permissions/approval-provenance.test.ts +184 -0
- package/src/permissions/approval-provenance.ts +70 -0
- package/src/permissions/checker.ts +4 -1
- package/src/permissions/gateway-threshold-reader.ts +4 -1
- package/src/permissions/prompter.ts +9 -2
- package/src/permissions/secret-prompter.ts +21 -48
- package/src/permissions/types.ts +33 -0
- package/src/permissions/workspace-policy.ts +0 -5
- package/src/platform/sync-identity.ts +0 -8
- package/src/plugins/defaults/injectors.ts +69 -2
- package/src/plugins/defaults/overflow-reduce.ts +3 -2
- package/src/plugins/types.ts +8 -0
- package/src/prompts/system-prompt.ts +34 -70
- package/src/prompts/templates/BOOTSTRAP.md +52 -6
- package/src/prompts/update-bulletin-job.ts +2 -0
- package/src/providers/__tests__/retry-callsite.test.ts +138 -1
- package/src/providers/anthropic/client.ts +72 -33
- package/src/providers/call-site-routing.ts +42 -3
- package/src/providers/gemini/client.ts +18 -2
- package/src/providers/managed-proxy/context.ts +0 -5
- package/src/providers/model-catalog.ts +105 -19
- package/src/providers/openai/chat-completions-provider.ts +6 -0
- package/src/providers/openai/responses-provider.ts +7 -1
- package/src/providers/provider-send-message.ts +45 -2
- package/src/providers/ratelimit.ts +7 -2
- package/src/providers/registry.ts +14 -9
- package/src/providers/retry.ts +96 -8
- package/src/providers/types.ts +13 -0
- package/src/providers/usage-tracking.ts +96 -0
- package/src/runtime/AGENTS.md +10 -6
- package/src/runtime/__tests__/agent-wake.test.ts +89 -0
- package/src/runtime/agent-wake.ts +39 -2
- package/src/runtime/assistant-event-hub.ts +541 -45
- package/src/runtime/assistant-event.ts +1 -6
- package/src/runtime/auth/context.ts +0 -9
- package/src/runtime/auth/middleware.ts +1 -1
- package/src/runtime/auth/route-policy.ts +11 -9
- package/src/runtime/auth/token-service.ts +0 -11
- package/src/runtime/channel-approvals.ts +6 -2
- package/src/runtime/channel-verification-service.ts +3 -5
- package/src/runtime/http-errors.ts +0 -34
- package/src/runtime/http-router.ts +6 -3
- package/src/runtime/http-server.ts +22 -82
- package/src/runtime/http-types.ts +5 -0
- package/src/runtime/interactive-ui.ts +0 -1
- package/src/runtime/middleware/auth.ts +0 -20
- package/src/runtime/migrations/__tests__/v1-test-helpers.ts +112 -0
- package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +11 -4
- package/src/runtime/migrations/__tests__/vbundle-builder-v1-shape.test.ts +253 -0
- package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +19 -6
- package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +71 -27
- package/src/runtime/migrations/__tests__/vbundle-metadata-merge-integration.test.ts +41 -2
- package/src/runtime/migrations/__tests__/vbundle-streaming-importer.test.ts +143 -79
- package/src/runtime/migrations/__tests__/vbundle-streaming-validator.test.ts +143 -23
- package/src/runtime/migrations/__tests__/vbundle-tar-stream.test.ts +2 -2
- package/src/runtime/migrations/__tests__/vbundle-validator-v1-schema.test.ts +371 -0
- package/src/runtime/migrations/migration-transport.ts +46 -13
- package/src/runtime/migrations/migration-wizard.ts +2 -2
- package/src/runtime/migrations/origin-mode.ts +40 -0
- package/src/runtime/migrations/vbundle-builder.ts +133 -79
- package/src/runtime/migrations/vbundle-import-analyzer.ts +9 -7
- package/src/runtime/migrations/vbundle-importer.ts +7 -7
- package/src/runtime/migrations/vbundle-metadata-merge.ts +1 -1
- package/src/runtime/migrations/vbundle-streaming-importer.ts +3 -3
- package/src/runtime/migrations/vbundle-streaming-validator.ts +48 -26
- package/src/runtime/migrations/vbundle-validator.ts +214 -41
- package/src/runtime/pending-interactions.ts +13 -4
- package/src/runtime/routes/__tests__/acp-routes.test.ts +0 -1
- package/src/runtime/routes/__tests__/backup-routes.test.ts +28 -19
- package/src/runtime/routes/__tests__/conversation-query-routes.test.ts +235 -0
- package/src/runtime/routes/__tests__/llm-call-sites-routes.test.ts +58 -0
- package/src/runtime/routes/__tests__/migration-export-secrets-redacted.test.ts +54 -0
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +19 -6
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +7 -7
- package/src/runtime/routes/acp-routes.test.ts +0 -3
- package/src/runtime/routes/acp-routes.ts +3 -7
- package/src/runtime/routes/app-management-routes.ts +18 -9
- package/src/runtime/routes/approval-routes.ts +55 -14
- package/src/runtime/routes/avatar-routes.ts +3 -5
- package/src/runtime/routes/browser-routes.ts +1 -15
- package/src/runtime/routes/channel-guardian-routes.ts +1 -5
- package/src/runtime/routes/channel-readiness-routes.ts +3 -7
- package/src/runtime/routes/channel-route-shared.ts +2 -28
- package/src/runtime/routes/client-routes.ts +45 -12
- package/src/runtime/routes/consolidation-routes.ts +115 -0
- package/src/runtime/routes/conversation-list-routes.ts +12 -29
- package/src/runtime/routes/conversation-management-routes.ts +14 -51
- package/src/runtime/routes/conversation-query-routes.ts +120 -8
- package/src/runtime/routes/conversation-routes.ts +44 -528
- package/src/runtime/routes/conversation-starter-routes.ts +19 -40
- package/src/runtime/routes/documents-routes.ts +53 -18
- package/src/runtime/routes/events-routes.ts +59 -91
- package/src/runtime/routes/filing-routes.ts +18 -1
- package/src/runtime/routes/guardian-action-routes.ts +4 -9
- package/src/runtime/routes/host-bash-routes.ts +3 -2
- package/src/runtime/routes/host-browser-routes.ts +9 -33
- package/src/runtime/routes/host-cu-routes.ts +6 -1
- package/src/runtime/routes/host-file-routes.ts +3 -2
- package/src/runtime/routes/host-transfer-routes.ts +11 -15
- package/src/runtime/routes/identity-routes.ts +78 -6
- package/src/runtime/routes/inbound-message-handler.ts +580 -137
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -88
- package/src/runtime/routes/inbound-stages/background-dispatch.ts +3 -0
- package/src/runtime/routes/index.ts +4 -0
- package/src/runtime/routes/integrations/slack/channel.ts +0 -24
- package/src/runtime/routes/llm-call-sites-routes.ts +22 -0
- package/src/runtime/routes/memory-v2-routes.ts +10 -15
- package/src/runtime/routes/migration-routes.ts +188 -31
- package/src/runtime/routes/playground/guard.ts +1 -1
- package/src/runtime/routes/playground/index.ts +0 -2
- package/src/runtime/routes/recording-routes.ts +4 -24
- package/src/runtime/routes/rename-conversation-routes.ts +2 -6
- package/src/runtime/routes/schedule-routes.ts +3 -6
- package/src/runtime/routes/secret-routes.ts +87 -18
- package/src/runtime/routes/settings-routes.ts +29 -28
- package/src/runtime/routes/skills-routes.ts +12 -31
- package/src/runtime/routes/suggest-trust-rule-routes.ts +32 -1
- package/src/runtime/routes/task-routes.ts +6 -6
- package/src/runtime/routes/trust-rules-routes.ts +3 -94
- package/src/runtime/routes/types.ts +4 -4
- package/src/runtime/routes/upgrade-broadcast-routes.ts +3 -10
- package/src/runtime/routes/usage-routes.ts +87 -10
- package/src/runtime/routes/user-routes.ts +17 -31
- package/src/runtime/routes/work-items-routes.ts +1 -4
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +2 -2
- package/src/runtime/services/analyze-conversation.ts +7 -17
- package/src/runtime/services/conversation-serializer.ts +2 -4
- package/src/runtime/verification-outbound-actions.ts +1 -1
- package/src/runtime/verification-rate-limiter.ts +1 -1
- package/src/schedule/schedule-store.ts +0 -16
- package/src/security/secret-scanner.ts +14 -547
- package/src/security/secure-keys.ts +31 -11
- package/src/security/token-manager.ts +7 -3
- package/src/signals/cancel.ts +16 -25
- package/src/signals/conversation-undo.ts +2 -27
- package/src/signals/emit-event.ts +1 -2
- package/src/signals/user-message.ts +108 -22
- package/src/skills/catalog-install.ts +1 -0
- package/src/skills/clawhub.ts +2 -2
- package/src/skills/inline-command-runner.ts +1 -7
- package/src/subagent/manager.ts +67 -84
- package/src/tasks/task-store.ts +1 -28
- package/src/telemetry/types.ts +6 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +38 -15
- package/src/telemetry/usage-telemetry-reporter.ts +3 -5
- package/src/tools/acp/spawn.test.ts +1 -2
- package/src/tools/acp/steer.test.ts +1 -2
- package/src/tools/browser/__tests__/browser-status.test.ts +44 -127
- package/src/tools/browser/browser-execution.ts +31 -147
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +92 -68
- package/src/tools/browser/cdp-client/factory.ts +48 -76
- package/src/tools/browser/cdp-client/index.ts +1 -14
- package/src/tools/executor.ts +44 -31
- package/src/tools/host-filesystem/edit.ts +3 -2
- package/src/tools/host-filesystem/read.ts +3 -2
- package/src/tools/host-filesystem/transfer.test.ts +45 -42
- package/src/tools/host-filesystem/transfer.ts +4 -3
- package/src/tools/host-filesystem/write.ts +3 -2
- package/src/tools/host-terminal/host-shell.ts +4 -3
- package/src/tools/network/script-proxy/index.ts +1 -10
- package/src/tools/permission-checker.ts +66 -1
- package/src/tools/skills/sandbox-runner.ts +1 -6
- package/src/tools/skills/skill-tool-factory.ts +32 -0
- package/src/tools/terminal/safe-env.ts +1 -0
- package/src/tools/terminal/shell.ts +2 -78
- package/src/tools/types.ts +12 -39
- package/src/tts/__tests__/provider-catalog.test.ts +2 -2
- package/src/tts/provider-catalog.ts +1 -1
- package/src/usage/actors.ts +2 -1
- package/src/usage/attribution.ts +185 -0
- package/src/usage/pricing.ts +166 -0
- package/src/usage/types.ts +14 -0
- package/src/util/json.ts +13 -0
- package/src/util/logger.ts +3 -3
- package/src/util/pricing.ts +50 -3
- package/src/work-items/work-item-runner.ts +15 -42
- package/src/workspace/migrations/050-seed-main-agent-opus-callsite.ts +4 -3
- package/src/workspace/migrations/052-seed-default-inference-profiles.ts +3 -3
- package/src/workspace/migrations/060-memory-v2-init.ts +2 -18
- package/src/workspace/migrations/061-move-backup-key-to-workspace.ts +59 -0
- package/src/workspace/migrations/062-drop-memory-v2-edges-json.ts +27 -0
- package/src/workspace/migrations/063-release-notes-dynamic-model-context.ts +70 -0
- package/src/workspace/migrations/064-unwind-main-agent-opus-seed.ts +64 -0
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/workspace/provider-commit-message-generator.ts +3 -3
- package/src/__tests__/sandbox-diagnostics.test.ts +0 -138
- package/src/__tests__/sandbox-host-parity.test.ts +0 -1024
- package/src/__tests__/secret-detection-handler.test.ts +0 -67
- package/src/__tests__/secret-scanner-executor.test.ts +0 -450
- package/src/__tests__/tcc-sandbox-deny.test.ts +0 -198
- package/src/__tests__/terminal-sandbox.test.ts +0 -374
- package/src/__tests__/tool-notification-listener.test.ts +0 -65
- package/src/context/__tests__/microcompact.test.ts +0 -805
- package/src/context/microcompact.ts +0 -443
- package/src/daemon/handlers/slack-channel-oauth-install.ts +0 -197
- package/src/events/tool-notification-listener.ts +0 -17
- package/src/ipc/routes/__tests__/memory-v2-validate.test.ts +0 -219
- package/src/memory/v2/__tests__/edges.test.ts +0 -435
- package/src/memory/v2/edges.ts +0 -217
- package/src/prompts/__tests__/system-prompt-memory-v2.test.ts +0 -197
- package/src/runtime/__tests__/chrome-extension-registry.test.ts +0 -518
- package/src/runtime/__tests__/client-registry.test.ts +0 -271
- package/src/runtime/chrome-extension-registry.ts +0 -368
- package/src/runtime/client-registry.ts +0 -254
- package/src/runtime/routes/inbound-stages/verification-intercept.ts +0 -329
- package/src/tools/secret-detection-handler.ts +0 -269
- package/src/tools/terminal/backends/native.ts +0 -327
- package/src/tools/terminal/backends/types.ts +0 -37
- package/src/tools/terminal/sandbox-diagnostics.ts +0 -87
- package/src/tools/terminal/sandbox.ts +0 -40
|
@@ -150,7 +150,10 @@ mock.module("../activation.js", () => ({
|
|
|
150
150
|
// injection logic introspects. Stub them to empty so the test stays focused
|
|
151
151
|
// on the wiring, not the pipeline internals (covered in activation.test.ts).
|
|
152
152
|
selectSkillCandidates: async () => new Set<string>(),
|
|
153
|
-
computeSkillActivation: async () =>
|
|
153
|
+
computeSkillActivation: async () => ({
|
|
154
|
+
activation: new Map<string, number>(),
|
|
155
|
+
breakdown: new Map(),
|
|
156
|
+
}),
|
|
154
157
|
selectSkillInjections: ({ topK }: { topK: number }) => ({
|
|
155
158
|
topNow: skillState.topSkillIds.slice(0, topK),
|
|
156
159
|
}),
|
|
@@ -160,6 +163,30 @@ mock.module("../skill-store.js", () => ({
|
|
|
160
163
|
getSkillCapability: (id: string) => skillState.entries.get(id) ?? null,
|
|
161
164
|
}));
|
|
162
165
|
|
|
166
|
+
// ---------------------------------------------------------------------------
|
|
167
|
+
// Activation-log store mock
|
|
168
|
+
// ---------------------------------------------------------------------------
|
|
169
|
+
//
|
|
170
|
+
// The real `recordMemoryV2ActivationLog` writes to the singleton
|
|
171
|
+
// `getDb()` — but this test uses an isolated in-memory database, so we mock
|
|
172
|
+
// the writer to capture calls in-process. `recordCalls` is the captured log
|
|
173
|
+
// array; `recordShouldThrow` makes the next call throw to verify the caller
|
|
174
|
+
// swallows the failure.
|
|
175
|
+
|
|
176
|
+
const telemetryState = {
|
|
177
|
+
recordCalls: [] as Array<Record<string, unknown>>,
|
|
178
|
+
recordShouldThrow: false,
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
mock.module("../../memory-v2-activation-log-store.js", () => ({
|
|
182
|
+
recordMemoryV2ActivationLog: (params: Record<string, unknown>) => {
|
|
183
|
+
if (telemetryState.recordShouldThrow) {
|
|
184
|
+
throw new Error("simulated telemetry write failure");
|
|
185
|
+
}
|
|
186
|
+
telemetryState.recordCalls.push(params);
|
|
187
|
+
},
|
|
188
|
+
}));
|
|
189
|
+
|
|
163
190
|
// ---------------------------------------------------------------------------
|
|
164
191
|
// Workspace + DB setup
|
|
165
192
|
// ---------------------------------------------------------------------------
|
|
@@ -174,15 +201,9 @@ beforeAll(() => {
|
|
|
174
201
|
|
|
175
202
|
// Seed the v2 directory layout the migration would normally create.
|
|
176
203
|
mkdirSync(join(tmpWorkspace, "memory", "concepts"), { recursive: true });
|
|
177
|
-
//
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
JSON.stringify({
|
|
181
|
-
version: 1,
|
|
182
|
-
edges: [["alice-vscode", "bob-coffee"]],
|
|
183
|
-
}),
|
|
184
|
-
);
|
|
185
|
-
// Three concept pages with generic, placeholder bodies.
|
|
204
|
+
// Three concept pages with generic, placeholder bodies. Outgoing edges
|
|
205
|
+
// live in each page's frontmatter `edges:` list — there is no separate
|
|
206
|
+
// edges-index file under the directed-edges model.
|
|
186
207
|
writeFileSync(
|
|
187
208
|
join(tmpWorkspace, "memory", "concepts", "alice-vscode.md"),
|
|
188
209
|
`---
|
|
@@ -207,6 +228,18 @@ ref_files: []
|
|
|
207
228
|
---
|
|
208
229
|
Carol loves jazz music — Coltrane in particular.`,
|
|
209
230
|
);
|
|
231
|
+
// A page with both `edges` and `ref_files` populated so the frontmatter-
|
|
232
|
+
// injection test can assert the full canonical shape.
|
|
233
|
+
writeFileSync(
|
|
234
|
+
join(tmpWorkspace, "memory", "concepts", "frontmatter-demo.md"),
|
|
235
|
+
`---
|
|
236
|
+
edges:
|
|
237
|
+
- alice-vscode
|
|
238
|
+
ref_files:
|
|
239
|
+
- images/demo.jpg
|
|
240
|
+
---
|
|
241
|
+
Demo body content.`,
|
|
242
|
+
);
|
|
210
243
|
});
|
|
211
244
|
|
|
212
245
|
afterAll(() => {
|
|
@@ -319,6 +352,8 @@ function resetState(): void {
|
|
|
319
352
|
state.queryResponses.sparse.length = 0;
|
|
320
353
|
skillState.topSkillIds.length = 0;
|
|
321
354
|
skillState.entries.clear();
|
|
355
|
+
telemetryState.recordCalls.length = 0;
|
|
356
|
+
telemetryState.recordShouldThrow = false;
|
|
322
357
|
// The qdrant module caches its client; the cached client may be a
|
|
323
358
|
// MockQdrantClient instance from a sibling test file. Reset to force a
|
|
324
359
|
// fresh `new QdrantClient()` against this file's mock.
|
|
@@ -364,8 +399,8 @@ describe("injectMemoryV2Block", () => {
|
|
|
364
399
|
|
|
365
400
|
expect(result.toInject).toEqual(["alice-vscode"]);
|
|
366
401
|
expect(result.block).not.toBeNull();
|
|
367
|
-
expect(result.block).toContain("<memory
|
|
368
|
-
expect(result.block).toContain("## What I Remember Right Now");
|
|
402
|
+
expect(result.block).toContain("<memory>");
|
|
403
|
+
expect(result.block).not.toContain("## What I Remember Right Now");
|
|
369
404
|
expect(result.block).toContain("### alice-vscode");
|
|
370
405
|
expect(result.block).toContain("VS Code");
|
|
371
406
|
expect(result.block).toContain("</memory>");
|
|
@@ -512,6 +547,35 @@ describe("injectMemoryV2Block", () => {
|
|
|
512
547
|
]);
|
|
513
548
|
});
|
|
514
549
|
|
|
550
|
+
test("includes the page frontmatter (edges, ref_files) in each rendered section", async () => {
|
|
551
|
+
// The frontmatter (`edges`, `ref_files`) lives on disk above the page
|
|
552
|
+
// body and is part of the page's content. Injection must reproduce both
|
|
553
|
+
// fields verbatim — bracketed by the canonical `---` delimiters — so the
|
|
554
|
+
// agent sees the page's edges and any referenced media paths alongside
|
|
555
|
+
// the prose body.
|
|
556
|
+
stageTurn([{ slug: "frontmatter-demo", denseScore: 0.9 }]);
|
|
557
|
+
|
|
558
|
+
const result = await injectMemoryV2Block({
|
|
559
|
+
database: db,
|
|
560
|
+
conversationId: "conv-1",
|
|
561
|
+
currentTurn: 1,
|
|
562
|
+
userMessage: "show me the demo",
|
|
563
|
+
assistantMessage: "",
|
|
564
|
+
nowText: "Now",
|
|
565
|
+
messageId: "msg-1",
|
|
566
|
+
config: makeConfig(),
|
|
567
|
+
});
|
|
568
|
+
|
|
569
|
+
expect(result.block).not.toBeNull();
|
|
570
|
+
// Slug header is immediately followed by the frontmatter open delimiter.
|
|
571
|
+
expect(result.block).toContain("### frontmatter-demo\n---\n");
|
|
572
|
+
// Both fields render in YAML block style with their populated values.
|
|
573
|
+
expect(result.block).toContain("edges:\n - alice-vscode");
|
|
574
|
+
expect(result.block).toContain("ref_files:\n - images/demo.jpg");
|
|
575
|
+
// Body still renders after the closing delimiter.
|
|
576
|
+
expect(result.block).toContain("Demo body content.");
|
|
577
|
+
});
|
|
578
|
+
|
|
515
579
|
test("renders pages in activation-descending order", async () => {
|
|
516
580
|
// Both slugs are fresh (no prior state). carol scores higher than alice
|
|
517
581
|
// on every channel — so carol should be ranked first in topNow and
|
|
@@ -594,7 +658,7 @@ describe("injectMemoryV2Block", () => {
|
|
|
594
658
|
// Skill subsection rendering
|
|
595
659
|
// ---------------------------------------------------------------------------
|
|
596
660
|
|
|
597
|
-
test("renders a skill-only block
|
|
661
|
+
test("renders a skill-only block in the same `<memory>` wrapper as concept-page-only blocks", async () => {
|
|
598
662
|
// No concept-page candidates this turn — the candidate query and the three
|
|
599
663
|
// simBatch queries all return empty. The skill pipeline is mocked to
|
|
600
664
|
// surface a single skill.
|
|
@@ -624,8 +688,8 @@ describe("injectMemoryV2Block", () => {
|
|
|
624
688
|
expect(result.toInject).toEqual([]);
|
|
625
689
|
expect(result.block).not.toBeNull();
|
|
626
690
|
// Same outer wrapping as concept-page-only blocks.
|
|
627
|
-
expect(result.block).toContain("<memory
|
|
628
|
-
expect(result.block).toContain("## What I Remember Right Now");
|
|
691
|
+
expect(result.block).toContain("<memory>");
|
|
692
|
+
expect(result.block).not.toContain("## What I Remember Right Now");
|
|
629
693
|
expect(result.block).toContain("</memory>");
|
|
630
694
|
// No concept-page sections; skills subsection present with the right
|
|
631
695
|
// bullet shape and the unconditional `→ use skill_load to activate` suffix.
|
|
@@ -757,6 +821,95 @@ describe("injectMemoryV2Block", () => {
|
|
|
757
821
|
expect(persisted!.everInjected).toEqual([]);
|
|
758
822
|
});
|
|
759
823
|
|
|
824
|
+
test("context-load mode renders topNow even when every slug was previously injected", async () => {
|
|
825
|
+
// Turn 1 (per-turn): seed alice as injected.
|
|
826
|
+
stageTurn([{ slug: "alice-vscode", denseScore: 0.9 }]);
|
|
827
|
+
await injectMemoryV2Block({
|
|
828
|
+
database: db,
|
|
829
|
+
conversationId: "conv-1",
|
|
830
|
+
currentTurn: 1,
|
|
831
|
+
userMessage: "Alice's editor",
|
|
832
|
+
assistantMessage: "",
|
|
833
|
+
nowText: "Now",
|
|
834
|
+
messageId: "msg-1",
|
|
835
|
+
config: makeConfig(),
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
// Subsequent context-load (post-compaction or fresh load): alice is
|
|
839
|
+
// back in the candidate pool. Per-turn would dedup against everInjected
|
|
840
|
+
// and produce a null block; context-load must re-render the full top-K
|
|
841
|
+
// because cached attachments don't exist on a fresh load.
|
|
842
|
+
stageTurn([{ slug: "alice-vscode", denseScore: 0.9 }]);
|
|
843
|
+
const result = await injectMemoryV2Block({
|
|
844
|
+
database: db,
|
|
845
|
+
conversationId: "conv-1",
|
|
846
|
+
currentTurn: 2,
|
|
847
|
+
userMessage: "Reload context",
|
|
848
|
+
assistantMessage: "",
|
|
849
|
+
nowText: "Now",
|
|
850
|
+
messageId: "msg-2",
|
|
851
|
+
mode: "context-load",
|
|
852
|
+
config: makeConfig(),
|
|
853
|
+
});
|
|
854
|
+
|
|
855
|
+
expect(result.block).not.toBeNull();
|
|
856
|
+
expect(result.block).toContain("### alice-vscode");
|
|
857
|
+
// No newly-injected slug — alice was already in everInjected.
|
|
858
|
+
expect(result.toInject).toEqual([]);
|
|
859
|
+
|
|
860
|
+
// everInjected stays a single entry (alice was already there) — context-
|
|
861
|
+
// load doesn't double-stamp.
|
|
862
|
+
const persisted = await hydrate(db, "conv-1");
|
|
863
|
+
expect(persisted!.everInjected).toEqual([
|
|
864
|
+
{ slug: "alice-vscode", turn: 1 },
|
|
865
|
+
]);
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
test("context-load mode renders the full top-K on a fresh first turn", async () => {
|
|
869
|
+
// Turn 1 with no prior state and three candidates. Per-turn and context-
|
|
870
|
+
// load behave identically when everInjected is empty (toInject == topNow),
|
|
871
|
+
// but this asserts the contract: a fresh first user message gets the
|
|
872
|
+
// entire top-K rendered, not just a delta.
|
|
873
|
+
stageTurn([
|
|
874
|
+
{ slug: "alice-vscode", denseScore: 0.9 },
|
|
875
|
+
{ slug: "bob-coffee", denseScore: 0.8 },
|
|
876
|
+
{ slug: "carol-jazz", denseScore: 0.7 },
|
|
877
|
+
]);
|
|
878
|
+
|
|
879
|
+
const result = await injectMemoryV2Block({
|
|
880
|
+
database: db,
|
|
881
|
+
conversationId: "conv-1",
|
|
882
|
+
currentTurn: 1,
|
|
883
|
+
userMessage: "hi",
|
|
884
|
+
assistantMessage: "",
|
|
885
|
+
nowText: "",
|
|
886
|
+
messageId: "msg-1",
|
|
887
|
+
mode: "context-load",
|
|
888
|
+
config: makeConfig({ top_k: 3 }),
|
|
889
|
+
});
|
|
890
|
+
|
|
891
|
+
expect(result.block).not.toBeNull();
|
|
892
|
+
expect(result.block).toContain("### alice-vscode");
|
|
893
|
+
expect(result.block).toContain("### bob-coffee");
|
|
894
|
+
expect(result.block).toContain("### carol-jazz");
|
|
895
|
+
// The seeded directed edges (alice→bob, bob→alice, frontmatter-demo→alice)
|
|
896
|
+
// mean alice has two incoming predecessors and bob has one, so directed
|
|
897
|
+
// spread normalizes alice's activation more aggressively than bob's. The
|
|
898
|
+
// resulting rank order is bob > carol (no predecessors) > alice.
|
|
899
|
+
expect(new Set(result.toInject)).toEqual(
|
|
900
|
+
new Set(["alice-vscode", "bob-coffee", "carol-jazz"]),
|
|
901
|
+
);
|
|
902
|
+
expect(result.toInject).toHaveLength(3);
|
|
903
|
+
|
|
904
|
+
// All three slugs persisted to everInjected so the next per-turn doesn't
|
|
905
|
+
// re-attach the same content.
|
|
906
|
+
const persisted = await hydrate(db, "conv-1");
|
|
907
|
+
expect(new Set(persisted!.everInjected.map((e) => e.slug))).toEqual(
|
|
908
|
+
new Set(["alice-vscode", "bob-coffee", "carol-jazz"]),
|
|
909
|
+
);
|
|
910
|
+
expect(persisted!.everInjected).toHaveLength(3);
|
|
911
|
+
});
|
|
912
|
+
|
|
760
913
|
test("`top_k_skills: 0` short-circuits to no skills subsection", async () => {
|
|
761
914
|
// Even when the underlying mock would surface skills, the cap at 0 must
|
|
762
915
|
// drop them via `selectSkillInjections.topK = 0` → empty `topNow`.
|
|
@@ -789,4 +942,220 @@ describe("injectMemoryV2Block", () => {
|
|
|
789
942
|
expect(result.block).not.toContain("### Skills You Can Use");
|
|
790
943
|
expect(result.block).not.toContain("example-skill-a");
|
|
791
944
|
});
|
|
945
|
+
|
|
946
|
+
// ---------------------------------------------------------------------------
|
|
947
|
+
// Activation-log telemetry
|
|
948
|
+
// ---------------------------------------------------------------------------
|
|
949
|
+
|
|
950
|
+
test("writes one activation-log row per turn with concept rows partitioned and sorted", async () => {
|
|
951
|
+
// Turn 1: seed alice as injected so turn 2 has an `in_context` candidate.
|
|
952
|
+
stageTurn([{ slug: "alice-vscode", denseScore: 0.9 }]);
|
|
953
|
+
await injectMemoryV2Block({
|
|
954
|
+
database: db,
|
|
955
|
+
conversationId: "conv-1",
|
|
956
|
+
currentTurn: 1,
|
|
957
|
+
userMessage: "Alice's editor",
|
|
958
|
+
assistantMessage: "",
|
|
959
|
+
nowText: "Now",
|
|
960
|
+
messageId: "msg-1",
|
|
961
|
+
config: makeConfig(),
|
|
962
|
+
});
|
|
963
|
+
expect(telemetryState.recordCalls.length).toBe(1);
|
|
964
|
+
|
|
965
|
+
// Turn 2: alice carries forward (now `in_context`); carol is freshly
|
|
966
|
+
// surfaced (`injected`); bob would be a candidate only if it carried
|
|
967
|
+
// forward, but with no prior bob entry it doesn't appear here.
|
|
968
|
+
stageTurn([
|
|
969
|
+
{ slug: "alice-vscode", denseScore: 0.6 },
|
|
970
|
+
{ slug: "carol-jazz", denseScore: 0.95 },
|
|
971
|
+
]);
|
|
972
|
+
await injectMemoryV2Block({
|
|
973
|
+
database: db,
|
|
974
|
+
conversationId: "conv-1",
|
|
975
|
+
currentTurn: 2,
|
|
976
|
+
userMessage: "Carol's music",
|
|
977
|
+
assistantMessage: "",
|
|
978
|
+
nowText: "Now",
|
|
979
|
+
messageId: "msg-2",
|
|
980
|
+
config: makeConfig(),
|
|
981
|
+
});
|
|
982
|
+
|
|
983
|
+
expect(telemetryState.recordCalls.length).toBe(2);
|
|
984
|
+
const row = telemetryState.recordCalls[1] as {
|
|
985
|
+
conversationId: string;
|
|
986
|
+
turn: number;
|
|
987
|
+
mode: string;
|
|
988
|
+
concepts: Array<{
|
|
989
|
+
slug: string;
|
|
990
|
+
finalActivation: number;
|
|
991
|
+
status: string;
|
|
992
|
+
source: string;
|
|
993
|
+
}>;
|
|
994
|
+
skills: unknown[];
|
|
995
|
+
config: { top_k: number };
|
|
996
|
+
};
|
|
997
|
+
expect(row.conversationId).toBe("conv-1");
|
|
998
|
+
expect(row.turn).toBe(2);
|
|
999
|
+
expect(row.mode).toBe("per-turn");
|
|
1000
|
+
expect(row.config.top_k).toBe(20);
|
|
1001
|
+
|
|
1002
|
+
// The candidate set is the union of fromPrior (alice) and fromAnn
|
|
1003
|
+
// (alice + carol) → two concept rows.
|
|
1004
|
+
expect(row.concepts.length).toBe(2);
|
|
1005
|
+
const slugs = row.concepts.map((c) => c.slug);
|
|
1006
|
+
expect(new Set(slugs)).toEqual(new Set(["alice-vscode", "carol-jazz"]));
|
|
1007
|
+
|
|
1008
|
+
// Sorted descending by finalActivation.
|
|
1009
|
+
for (let i = 1; i < row.concepts.length; i++) {
|
|
1010
|
+
expect(row.concepts[i - 1]!.finalActivation).toBeGreaterThanOrEqual(
|
|
1011
|
+
row.concepts[i]!.finalActivation,
|
|
1012
|
+
);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
const byslug = new Map(row.concepts.map((c) => [c.slug, c]));
|
|
1016
|
+
// Alice was attached on turn 1 → status `in_context` on turn 2.
|
|
1017
|
+
expect(byslug.get("alice-vscode")!.status).toBe("in_context");
|
|
1018
|
+
// Carol is freshly injected on turn 2.
|
|
1019
|
+
expect(byslug.get("carol-jazz")!.status).toBe("injected");
|
|
1020
|
+
});
|
|
1021
|
+
|
|
1022
|
+
test("context-load mode marks every rendered slug as `injected`, never `in_context`", async () => {
|
|
1023
|
+
// Turn 1 (per-turn): seed alice as injected so the next turn's prior
|
|
1024
|
+
// `everInjected` includes her — the same setup the per-turn telemetry
|
|
1025
|
+
// test uses, so the difference between modes is unambiguous.
|
|
1026
|
+
stageTurn([{ slug: "alice-vscode", denseScore: 0.9 }]);
|
|
1027
|
+
await injectMemoryV2Block({
|
|
1028
|
+
database: db,
|
|
1029
|
+
conversationId: "conv-1",
|
|
1030
|
+
currentTurn: 1,
|
|
1031
|
+
userMessage: "Alice's editor",
|
|
1032
|
+
assistantMessage: "",
|
|
1033
|
+
nowText: "Now",
|
|
1034
|
+
messageId: "msg-1",
|
|
1035
|
+
config: makeConfig(),
|
|
1036
|
+
});
|
|
1037
|
+
expect(telemetryState.recordCalls.length).toBe(1);
|
|
1038
|
+
|
|
1039
|
+
// Turn 2 in context-load mode (post-compaction or fresh load). Alice
|
|
1040
|
+
// carries forward AND ranks high again; carol is a brand-new candidate.
|
|
1041
|
+
// Both end up in `topNow` (and therefore in `slugsToRender` since
|
|
1042
|
+
// context-load renders the full top-K). The status field must reflect
|
|
1043
|
+
// that they were physically rendered into the new user message on this
|
|
1044
|
+
// turn — `injected` for both — rather than reading `in_context` for
|
|
1045
|
+
// alice based on stale prior `everInjected` state.
|
|
1046
|
+
stageTurn([
|
|
1047
|
+
{ slug: "alice-vscode", denseScore: 0.6 },
|
|
1048
|
+
{ slug: "carol-jazz", denseScore: 0.95 },
|
|
1049
|
+
]);
|
|
1050
|
+
await injectMemoryV2Block({
|
|
1051
|
+
database: db,
|
|
1052
|
+
conversationId: "conv-1",
|
|
1053
|
+
currentTurn: 2,
|
|
1054
|
+
userMessage: "Reload context",
|
|
1055
|
+
assistantMessage: "",
|
|
1056
|
+
nowText: "Now",
|
|
1057
|
+
messageId: "msg-2",
|
|
1058
|
+
mode: "context-load",
|
|
1059
|
+
config: makeConfig(),
|
|
1060
|
+
});
|
|
1061
|
+
|
|
1062
|
+
expect(telemetryState.recordCalls.length).toBe(2);
|
|
1063
|
+
const row = telemetryState.recordCalls[1] as {
|
|
1064
|
+
mode: string;
|
|
1065
|
+
concepts: Array<{ slug: string; status: string }>;
|
|
1066
|
+
};
|
|
1067
|
+
expect(row.mode).toBe("context-load");
|
|
1068
|
+
|
|
1069
|
+
const byslug = new Map(row.concepts.map((c) => [c.slug, c]));
|
|
1070
|
+
// Both rendered slugs read as `injected` — alice especially, even though
|
|
1071
|
+
// she's in prior `everInjected`, because context-load actually rendered
|
|
1072
|
+
// her into the fresh user message on this turn.
|
|
1073
|
+
expect(byslug.get("alice-vscode")!.status).toBe("injected");
|
|
1074
|
+
expect(byslug.get("carol-jazz")!.status).toBe("injected");
|
|
1075
|
+
|
|
1076
|
+
// No slug reads as `in_context` in context-load mode — the cache was
|
|
1077
|
+
// wiped, so there is no prior cached attachment to reference.
|
|
1078
|
+
for (const concept of row.concepts) {
|
|
1079
|
+
expect(concept.status).not.toBe("in_context");
|
|
1080
|
+
}
|
|
1081
|
+
});
|
|
1082
|
+
|
|
1083
|
+
test("context-load mode marks candidates outside `slugsToRender` as `not_injected`", async () => {
|
|
1084
|
+
// Turn 1 (per-turn): seed both alice and bob with positive activation
|
|
1085
|
+
// so they survive into turn 2's prior-state candidate pool.
|
|
1086
|
+
stageTurn([
|
|
1087
|
+
{ slug: "alice-vscode", denseScore: 0.9 },
|
|
1088
|
+
{ slug: "bob-coffee", denseScore: 0.8 },
|
|
1089
|
+
]);
|
|
1090
|
+
await injectMemoryV2Block({
|
|
1091
|
+
database: db,
|
|
1092
|
+
conversationId: "conv-1",
|
|
1093
|
+
currentTurn: 1,
|
|
1094
|
+
userMessage: "Alice's editor and Bob's coffee",
|
|
1095
|
+
assistantMessage: "",
|
|
1096
|
+
nowText: "Now",
|
|
1097
|
+
messageId: "msg-1",
|
|
1098
|
+
config: makeConfig(),
|
|
1099
|
+
});
|
|
1100
|
+
|
|
1101
|
+
// Turn 2 (context-load) with `top_k: 1`: alice and bob both carry
|
|
1102
|
+
// forward as candidates, but only the top-ranked slug is rendered.
|
|
1103
|
+
// Whichever slug doesn't make the cut must read as `not_injected`.
|
|
1104
|
+
stageTurn([
|
|
1105
|
+
{ slug: "alice-vscode", denseScore: 0.95 },
|
|
1106
|
+
{ slug: "bob-coffee", denseScore: 0.05 },
|
|
1107
|
+
]);
|
|
1108
|
+
await injectMemoryV2Block({
|
|
1109
|
+
database: db,
|
|
1110
|
+
conversationId: "conv-1",
|
|
1111
|
+
currentTurn: 2,
|
|
1112
|
+
userMessage: "Reload context",
|
|
1113
|
+
assistantMessage: "",
|
|
1114
|
+
nowText: "Now",
|
|
1115
|
+
messageId: "msg-2",
|
|
1116
|
+
mode: "context-load",
|
|
1117
|
+
config: makeConfig({ top_k: 1 }),
|
|
1118
|
+
});
|
|
1119
|
+
|
|
1120
|
+
expect(telemetryState.recordCalls.length).toBe(2);
|
|
1121
|
+
const row = telemetryState.recordCalls[1] as {
|
|
1122
|
+
mode: string;
|
|
1123
|
+
concepts: Array<{ slug: string; status: string }>;
|
|
1124
|
+
};
|
|
1125
|
+
expect(row.mode).toBe("context-load");
|
|
1126
|
+
|
|
1127
|
+
const byslug = new Map(row.concepts.map((c) => [c.slug, c]));
|
|
1128
|
+
// Alice ranked first → she is in `slugsToRender` → `injected`.
|
|
1129
|
+
expect(byslug.get("alice-vscode")!.status).toBe("injected");
|
|
1130
|
+
// Bob was a candidate but didn't make `top_k: 1` → `not_injected`.
|
|
1131
|
+
expect(byslug.get("bob-coffee")!.status).toBe("not_injected");
|
|
1132
|
+
});
|
|
1133
|
+
|
|
1134
|
+
test("telemetry write failure is non-fatal — injection still returns a normal result", async () => {
|
|
1135
|
+
telemetryState.recordShouldThrow = true;
|
|
1136
|
+
|
|
1137
|
+
stageTurn([{ slug: "alice-vscode", denseScore: 0.9 }]);
|
|
1138
|
+
const result = await injectMemoryV2Block({
|
|
1139
|
+
database: db,
|
|
1140
|
+
conversationId: "conv-1",
|
|
1141
|
+
currentTurn: 1,
|
|
1142
|
+
userMessage: "Alice's editor",
|
|
1143
|
+
assistantMessage: "",
|
|
1144
|
+
nowText: "Now",
|
|
1145
|
+
messageId: "msg-1",
|
|
1146
|
+
config: makeConfig(),
|
|
1147
|
+
});
|
|
1148
|
+
|
|
1149
|
+
// No row captured (the throw aborted the push), but the caller still
|
|
1150
|
+
// produced a regular block + toInject result and persisted state.
|
|
1151
|
+
expect(telemetryState.recordCalls.length).toBe(0);
|
|
1152
|
+
expect(result.toInject).toEqual(["alice-vscode"]);
|
|
1153
|
+
expect(result.block).not.toBeNull();
|
|
1154
|
+
expect(result.block).toContain("### alice-vscode");
|
|
1155
|
+
|
|
1156
|
+
const persisted = await hydrate(db, "conv-1");
|
|
1157
|
+
expect(persisted!.everInjected).toEqual([
|
|
1158
|
+
{ slug: "alice-vscode", turn: 1 },
|
|
1159
|
+
]);
|
|
1160
|
+
});
|
|
792
1161
|
});
|
|
@@ -546,7 +546,7 @@ describe("derivePromotions", () => {
|
|
|
546
546
|
// ---------------------------------------------------------------------------
|
|
547
547
|
|
|
548
548
|
describe("collapseEdges", () => {
|
|
549
|
-
test("maps v1 ids to v2 slugs and
|
|
549
|
+
test("maps v1 ids to v2 slugs and preserves direction (source → target)", () => {
|
|
550
550
|
const v1: V1Edge[] = [
|
|
551
551
|
{ sourceNodeId: "node-a", targetNodeId: "node-b" },
|
|
552
552
|
{ sourceNodeId: "node-b", targetNodeId: "node-c" },
|
|
@@ -556,9 +556,13 @@ describe("collapseEdges", () => {
|
|
|
556
556
|
["node-b", "bob"],
|
|
557
557
|
["node-c", "carol"],
|
|
558
558
|
]);
|
|
559
|
-
const
|
|
560
|
-
|
|
561
|
-
expect(
|
|
559
|
+
const outgoing = collapseEdges(v1, slugMap);
|
|
560
|
+
// Two distinct sources, each pointing at one target.
|
|
561
|
+
expect(outgoing.size).toBe(2);
|
|
562
|
+
expect([...(outgoing.get("alice") ?? new Set<string>())]).toEqual(["bob"]);
|
|
563
|
+
expect([...(outgoing.get("bob") ?? new Set<string>())]).toEqual(["carol"]);
|
|
564
|
+
// Direction is preserved — carol's outgoing list is empty (it's a sink).
|
|
565
|
+
expect(outgoing.has("carol")).toBe(false);
|
|
562
566
|
});
|
|
563
567
|
|
|
564
568
|
test("drops edges whose endpoints aren't in the slug map", () => {
|
|
@@ -570,21 +574,48 @@ describe("collapseEdges", () => {
|
|
|
570
574
|
["node-a", "alice"],
|
|
571
575
|
["node-b", "bob"],
|
|
572
576
|
]);
|
|
573
|
-
const
|
|
574
|
-
expect(
|
|
577
|
+
const outgoing = collapseEdges(v1, slugMap);
|
|
578
|
+
expect(outgoing.size).toBe(0);
|
|
575
579
|
});
|
|
576
580
|
|
|
577
581
|
test("drops self-loops introduced by the slug mapping", () => {
|
|
578
582
|
// Two distinct v1 nodes mapped to the same v2 slug (e.g. clustered
|
|
579
|
-
// together) cannot produce
|
|
580
|
-
//
|
|
583
|
+
// together) cannot produce a self-edge — concept-page graphs are simple,
|
|
584
|
+
// so we filter at collapse time.
|
|
581
585
|
const v1: V1Edge[] = [{ sourceNodeId: "node-a", targetNodeId: "node-b" }];
|
|
582
586
|
const slugMap = new Map([
|
|
583
587
|
["node-a", "merged"],
|
|
584
588
|
["node-b", "merged"],
|
|
585
589
|
]);
|
|
586
|
-
const
|
|
587
|
-
expect(
|
|
590
|
+
const outgoing = collapseEdges(v1, slugMap);
|
|
591
|
+
expect(outgoing.size).toBe(0);
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
test("collapses duplicate (source, target) pairs into a single entry", () => {
|
|
595
|
+
const v1: V1Edge[] = [
|
|
596
|
+
{ sourceNodeId: "node-a", targetNodeId: "node-b" },
|
|
597
|
+
{ sourceNodeId: "node-a", targetNodeId: "node-b" },
|
|
598
|
+
];
|
|
599
|
+
const slugMap = new Map([
|
|
600
|
+
["node-a", "alice"],
|
|
601
|
+
["node-b", "bob"],
|
|
602
|
+
]);
|
|
603
|
+
const outgoing = collapseEdges(v1, slugMap);
|
|
604
|
+
expect([...(outgoing.get("alice") ?? new Set<string>())]).toEqual(["bob"]);
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
test("keeps A→B and B→A as separate directed edges", () => {
|
|
608
|
+
const v1: V1Edge[] = [
|
|
609
|
+
{ sourceNodeId: "node-a", targetNodeId: "node-b" },
|
|
610
|
+
{ sourceNodeId: "node-b", targetNodeId: "node-a" },
|
|
611
|
+
];
|
|
612
|
+
const slugMap = new Map([
|
|
613
|
+
["node-a", "alice"],
|
|
614
|
+
["node-b", "bob"],
|
|
615
|
+
]);
|
|
616
|
+
const outgoing = collapseEdges(v1, slugMap);
|
|
617
|
+
expect([...(outgoing.get("alice") ?? new Set<string>())]).toEqual(["bob"]);
|
|
618
|
+
expect([...(outgoing.get("bob") ?? new Set<string>())]).toEqual(["alice"]);
|
|
588
619
|
});
|
|
589
620
|
});
|
|
590
621
|
|
|
@@ -648,13 +679,22 @@ describe("runMemoryV2Migration", () => {
|
|
|
648
679
|
expect(pages.length).toBe(3);
|
|
649
680
|
expect(result.pagesCreated).toBe(3);
|
|
650
681
|
|
|
651
|
-
// -- edges
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
682
|
+
// -- Outgoing edges live in source-page frontmatter (no edges.json). --
|
|
683
|
+
expect(existsSync(join(workspaceDir, "memory", "edges.json"))).toBe(false);
|
|
684
|
+
expect(result.edgesWritten).toBe(1);
|
|
685
|
+
// Find the page whose frontmatter has the outgoing edge — that's the
|
|
686
|
+
// source slug. Exactly one page should carry the surviving v1 edge.
|
|
687
|
+
let pagesWithEdges = 0;
|
|
688
|
+
for (const file of pages) {
|
|
689
|
+
const body = readFileSync(join(conceptDir, file), "utf-8");
|
|
690
|
+
if (
|
|
691
|
+
/\nedges:\s*\n?\s*-\s*/.test(body) ||
|
|
692
|
+
/edges:\s*\[[^\]]+\]/.test(body)
|
|
693
|
+
) {
|
|
694
|
+
pagesWithEdges += 1;
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
expect(pagesWithEdges).toBe(1);
|
|
658
698
|
|
|
659
699
|
// -- Promotions appended to the right files. --
|
|
660
700
|
const essentials = readFileSync(
|
|
@@ -664,23 +704,13 @@ describe("runMemoryV2Migration", () => {
|
|
|
664
704
|
expect(essentials).toContain("User is Alice and works at Vellum");
|
|
665
705
|
expect(result.essentialsLines).toBe(1);
|
|
666
706
|
|
|
667
|
-
// -- Embed jobs enqueued (one per page)
|
|
668
|
-
//
|
|
669
|
-
expect(enqueuedJobs.length).toBe(
|
|
707
|
+
// -- Embed jobs enqueued (one per page). No rebuild-edges follow-up:
|
|
708
|
+
// the migration writes outgoing edges directly into page frontmatter. --
|
|
709
|
+
expect(enqueuedJobs.length).toBe(3);
|
|
670
710
|
const embedJobs = enqueuedJobs.filter(
|
|
671
711
|
(j) => j.type === "embed_concept_page",
|
|
672
712
|
);
|
|
673
713
|
expect(embedJobs.length).toBe(3);
|
|
674
|
-
const rebuildJobs = enqueuedJobs.filter(
|
|
675
|
-
(j) => j.type === "memory_v2_rebuild_edges",
|
|
676
|
-
);
|
|
677
|
-
expect(rebuildJobs.length).toBe(1);
|
|
678
|
-
// Rebuild-edges must come last — the embeds depend only on pages on disk,
|
|
679
|
-
// but rebuild-edges should observe the final edges.json from this run.
|
|
680
|
-
expect(enqueuedJobs[enqueuedJobs.length - 1].type).toBe(
|
|
681
|
-
"memory_v2_rebuild_edges",
|
|
682
|
-
);
|
|
683
|
-
expect(result.rebuildEdgesJobId).toBe(`job-${enqueuedJobs.length}`);
|
|
684
714
|
|
|
685
715
|
// -- Sentinel written. --
|
|
686
716
|
expect(existsSync(join(workspaceDir, MIGRATION_SENTINEL_RELATIVE))).toBe(
|
|
@@ -785,13 +815,11 @@ describe("runMemoryV2Migration", () => {
|
|
|
785
815
|
});
|
|
786
816
|
expect(result.pagesCreated).toBe(0);
|
|
787
817
|
expect(result.embedsEnqueued).toBe(0);
|
|
818
|
+
expect(result.edgesWritten).toBe(0);
|
|
788
819
|
expect(result.sentinelWritten).toBe(true);
|
|
789
|
-
//
|
|
790
|
-
//
|
|
791
|
-
expect(
|
|
792
|
-
expect(
|
|
793
|
-
enqueuedJobs.filter((j) => j.type === "memory_v2_rebuild_edges").length,
|
|
794
|
-
).toBe(1);
|
|
820
|
+
// No rebuild-edges follow-up — outgoing edges live directly in page
|
|
821
|
+
// frontmatter, so a workspace with no pages has nothing to rebuild.
|
|
822
|
+
expect(enqueuedJobs).toEqual([]);
|
|
795
823
|
});
|
|
796
824
|
});
|
|
797
825
|
|