@vellumai/assistant 0.5.13 → 0.5.15
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/.env.example +1 -6
- package/AGENTS.md +4 -0
- package/ARCHITECTURE.md +0 -1
- package/bunfig.toml +1 -0
- package/docs/architecture/memory.md +3 -3
- package/openapi.yaml +127 -22
- package/package.json +1 -1
- package/src/__tests__/access-request-decision.test.ts +2 -32
- package/src/__tests__/actor-token-service.test.ts +1 -31
- package/src/__tests__/anthropic-provider.test.ts +53 -40
- package/src/__tests__/app-git-history.test.ts +9 -17
- package/src/__tests__/app-git-service.test.ts +14 -20
- package/src/__tests__/app-store-dir-names.test.ts +10 -20
- package/src/__tests__/approval-cascade.test.ts +2 -19
- package/src/__tests__/approval-primitive.test.ts +2 -27
- package/src/__tests__/approval-routes-http.test.ts +2 -30
- package/src/__tests__/assistant-events-sse-hardening.test.ts +2 -28
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -45
- package/src/__tests__/attachments-store.test.ts +5 -32
- package/src/__tests__/audit-log-rotation.test.ts +5 -36
- package/src/__tests__/avatar-e2e.test.ts +1 -9
- package/src/__tests__/avatar-generator.test.ts +1 -7
- package/src/__tests__/browser-fill-credential.test.ts +0 -4
- package/src/__tests__/browser-manager.test.ts +0 -6
- package/src/__tests__/call-controller.test.ts +1 -22
- package/src/__tests__/call-conversation-messages.test.ts +0 -21
- package/src/__tests__/call-domain.test.ts +0 -25
- package/src/__tests__/call-pointer-messages.test.ts +0 -21
- package/src/__tests__/call-recovery.test.ts +0 -22
- package/src/__tests__/call-routes-http.test.ts +0 -24
- package/src/__tests__/call-store.test.ts +0 -21
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +0 -24
- package/src/__tests__/canonical-guardian-store.test.ts +48 -21
- package/src/__tests__/channel-approval-routes.test.ts +6 -26
- package/src/__tests__/channel-approvals.test.ts +1 -38
- package/src/__tests__/channel-delivery-store.test.ts +0 -21
- package/src/__tests__/channel-guardian.test.ts +0 -26
- package/src/__tests__/channel-reply-delivery.test.ts +5 -0
- package/src/__tests__/channel-retry-sweep.test.ts +0 -21
- package/src/__tests__/checker.test.ts +26 -61
- package/src/__tests__/clawhub.test.ts +9 -25
- package/src/__tests__/cli-command-risk-guard.test.ts +0 -18
- package/src/__tests__/config-loader-backfill.test.ts +9 -28
- package/src/__tests__/config-schema-cmd.test.ts +5 -25
- package/src/__tests__/config-schema.test.ts +21 -40
- package/src/__tests__/config-watcher.test.ts +4 -91
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -21
- package/src/__tests__/contacts-tools.test.ts +0 -21
- package/src/__tests__/context-memory-e2e.test.ts +0 -21
- package/src/__tests__/context-window-manager.test.ts +130 -3
- package/src/__tests__/conversation-abort-tool-results.test.ts +0 -4
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +0 -4
- package/src/__tests__/conversation-agent-loop.test.ts +0 -4
- package/src/__tests__/conversation-attachments.test.ts +1 -24
- package/src/__tests__/conversation-attention-store.test.ts +0 -21
- package/src/__tests__/conversation-attention-telegram.test.ts +0 -22
- package/src/__tests__/conversation-clear-safety.test.ts +0 -22
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -21
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +0 -24
- package/src/__tests__/conversation-disk-view-integration.test.ts +1 -23
- package/src/__tests__/conversation-disk-view.test.ts +5 -27
- package/src/__tests__/conversation-error.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +1 -33
- package/src/__tests__/conversation-fork-route.test.ts +0 -27
- package/src/__tests__/conversation-history-web-search.test.ts +23 -16
- package/src/__tests__/conversation-init.benchmark.test.ts +22 -43
- package/src/__tests__/conversation-key-store-disk-view.test.ts +8 -34
- package/src/__tests__/conversation-load-history-repair.test.ts +0 -4
- package/src/__tests__/conversation-pre-run-repair.test.ts +0 -4
- package/src/__tests__/conversation-provider-retry-repair.test.ts +0 -4
- package/src/__tests__/conversation-queue.test.ts +8 -8
- package/src/__tests__/conversation-routes-disk-view.test.ts +13 -51
- package/src/__tests__/conversation-runtime-assembly.test.ts +64 -38
- package/src/__tests__/conversation-slash-commands.test.ts +5 -0
- package/src/__tests__/conversation-slash-queue.test.ts +0 -4
- package/src/__tests__/conversation-slash-unknown.test.ts +0 -4
- package/src/__tests__/conversation-speed-override.test.ts +326 -0
- package/src/__tests__/conversation-starter-routes.test.ts +0 -23
- package/src/__tests__/conversation-store.test.ts +0 -21
- package/src/__tests__/conversation-unread-route.test.ts +0 -24
- package/src/__tests__/conversation-usage.test.ts +56 -21
- package/src/__tests__/conversation-wipe.test.ts +0 -21
- package/src/__tests__/conversation-workspace-cache-state.test.ts +0 -4
- package/src/__tests__/conversation-workspace-injection.test.ts +0 -4
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +0 -4
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +8 -5
- package/src/__tests__/credential-vault-unit.test.ts +9 -428
- package/src/__tests__/credentials-cli.test.ts +10 -10
- package/src/__tests__/daemon-assistant-events.test.ts +0 -19
- package/src/__tests__/date-context.test.ts +77 -97
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +7 -24
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +29 -42
- package/src/__tests__/delete-managed-skill-tool.test.ts +2 -10
- package/src/__tests__/deterministic-verification-control-plane.test.ts +1 -26
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +61 -15
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -36
- package/src/__tests__/email-cli.test.ts +6 -6
- package/src/__tests__/ephemeral-permissions.test.ts +5 -17
- package/src/__tests__/first-greeting.test.ts +4 -32
- package/src/__tests__/followup-tools.test.ts +0 -21
- package/src/__tests__/gateway-only-enforcement.test.ts +0 -20
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -23
- package/src/__tests__/guardian-action-followup-executor.test.ts +0 -23
- package/src/__tests__/guardian-action-followup-store.test.ts +0 -21
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -21
- package/src/__tests__/guardian-action-late-reply.test.ts +0 -21
- package/src/__tests__/guardian-action-store.test.ts +0 -21
- package/src/__tests__/guardian-action-sweep.test.ts +0 -21
- package/src/__tests__/guardian-binding-drift-heal.test.ts +0 -23
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +172 -22
- package/src/__tests__/guardian-dispatch.test.ts +0 -21
- package/src/__tests__/guardian-grant-minting.test.ts +0 -22
- package/src/__tests__/guardian-outbound-http.test.ts +0 -22
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +0 -23
- package/src/__tests__/guardian-routing-invariants.test.ts +0 -22
- package/src/__tests__/guardian-routing-state.test.ts +0 -22
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -24
- package/src/__tests__/headless-browser-interactions.test.ts +0 -4
- package/src/__tests__/headless-browser-navigate.test.ts +0 -4
- package/src/__tests__/headless-browser-read-tools.test.ts +0 -4
- package/src/__tests__/headless-browser-snapshot.test.ts +0 -4
- package/src/__tests__/heartbeat-service.test.ts +99 -26
- package/src/__tests__/hooks-blocking.test.ts +3 -3
- package/src/__tests__/hooks-config.test.ts +7 -7
- package/src/__tests__/hooks-discovery.test.ts +3 -3
- package/src/__tests__/hooks-integration.test.ts +5 -5
- package/src/__tests__/hooks-manager.test.ts +3 -3
- package/src/__tests__/hooks-runner.test.ts +5 -23
- package/src/__tests__/hooks-settings.test.ts +3 -3
- package/src/__tests__/hooks-templates.test.ts +3 -3
- package/src/__tests__/http-conversation-lineage.test.ts +0 -27
- package/src/__tests__/identity-intro-cache.test.ts +0 -4
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -22
- package/src/__tests__/inline-skill-load-permissions.test.ts +5 -16
- package/src/__tests__/intent-routing.test.ts +2 -55
- package/src/__tests__/invite-redemption-service.test.ts +0 -21
- package/src/__tests__/invite-routes-http.test.ts +0 -21
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +0 -17
- package/src/__tests__/journal-context.test.ts +8 -75
- package/src/__tests__/list-messages-attachments.test.ts +0 -22
- package/src/__tests__/llm-context-route-provider.test.ts +0 -21
- package/src/__tests__/llm-request-log-turn-query.test.ts +46 -28
- package/src/__tests__/llm-usage-store.test.ts +0 -21
- package/src/__tests__/log-export-workspace.test.ts +1 -1
- package/src/__tests__/managed-skill-lifecycle.test.ts +1 -1
- package/src/__tests__/managed-store.test.ts +1 -1
- package/src/__tests__/mcp-cli.test.ts +7 -10
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -21
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +0 -11
- package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -21
- package/src/__tests__/memory-recall-log-store.test.ts +0 -27
- package/src/__tests__/memory-recall-quality.test.ts +0 -21
- package/src/__tests__/memory-regressions.experimental.test.ts +31 -30
- package/src/__tests__/memory-regressions.test.ts +282 -70
- package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -21
- package/src/__tests__/memory-upsert-concurrency.test.ts +0 -21
- package/src/__tests__/messaging-send-tool.test.ts +201 -0
- package/src/__tests__/migration-cross-version-compatibility.test.ts +18 -13
- package/src/__tests__/migration-export-http.test.ts +7 -1
- package/src/__tests__/migration-import-commit-http.test.ts +16 -14
- package/src/__tests__/migration-import-preflight-http.test.ts +27 -44
- package/src/__tests__/migration-validate-http.test.ts +1 -28
- package/src/__tests__/native-web-search.test.ts +25 -22
- package/src/__tests__/non-member-access-request.test.ts +0 -22
- package/src/__tests__/notification-guardian-path.test.ts +0 -21
- package/src/__tests__/notification-schedule-dedup.test.ts +1 -25
- package/src/__tests__/oauth-apps-routes.test.ts +103 -2
- package/src/__tests__/oauth-cli.test.ts +52 -0
- package/src/__tests__/oauth-provider-profiles.test.ts +0 -16
- package/src/__tests__/oauth-provider-serializer.test.ts +232 -0
- package/src/__tests__/oauth-providers-routes.test.ts +257 -0
- package/src/__tests__/oauth-store.test.ts +0 -21
- package/src/__tests__/onboarding-template-contract.test.ts +2 -2
- package/src/__tests__/openai-provider.test.ts +261 -0
- package/src/__tests__/pairing-concurrent.test.ts +6 -6
- package/src/__tests__/pairing-routes.test.ts +7 -1
- package/src/__tests__/path-policy.test.ts +1 -1
- package/src/__tests__/platform.test.ts +64 -88
- package/src/__tests__/playbook-execution.test.ts +0 -21
- package/src/__tests__/playbook-tools.test.ts +0 -21
- package/src/__tests__/pricing.test.ts +100 -0
- package/src/__tests__/relay-server.test.ts +1 -25
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -24
- package/src/__tests__/runtime-events-sse-parity.test.ts +2 -24
- package/src/__tests__/runtime-events-sse.test.ts +0 -24
- package/src/__tests__/sandbox-diagnostics.test.ts +2 -1
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +1 -1
- package/src/__tests__/schedule-store.test.ts +0 -21
- package/src/__tests__/schedule-tools.test.ts +0 -21
- package/src/__tests__/scheduler-recurrence.test.ts +0 -21
- package/src/__tests__/scoped-approval-grants.test.ts +0 -21
- package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -21
- package/src/__tests__/secret-allowlist.test.ts +1 -1
- package/src/__tests__/secret-ingress-channel.test.ts +0 -5
- package/src/__tests__/secret-ingress-cli.test.ts +0 -6
- package/src/__tests__/secret-ingress-http.test.ts +0 -5
- package/src/__tests__/secret-ingress.test.ts +0 -5
- package/src/__tests__/send-endpoint-busy.test.ts +0 -24
- package/src/__tests__/sequence-store.test.ts +0 -21
- package/src/__tests__/server-history-render.test.ts +0 -24
- package/src/__tests__/shell-tool-proxy-mode.test.ts +0 -4
- package/src/__tests__/skill-load-inline-command.test.ts +9 -0
- package/src/__tests__/skill-load-inline-includes.test.ts +9 -0
- package/src/__tests__/skill-load-tool.test.ts +11 -0
- package/src/__tests__/skills-uninstall.test.ts +10 -8
- package/src/__tests__/skills.test.ts +1 -1
- package/src/__tests__/slack-channel-config.test.ts +1 -1
- package/src/__tests__/slack-inbound-verification.test.ts +0 -22
- package/src/__tests__/starter-bundle.test.ts +4 -1
- package/src/__tests__/suggestion-routes.test.ts +2 -0
- package/src/__tests__/system-prompt.test.ts +1 -1
- package/src/__tests__/terminal-tools.test.ts +1 -1
- package/src/__tests__/test-preload.ts +31 -0
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -1
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +0 -20
- package/src/__tests__/tool-input-summary.test.ts +124 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +2 -1
- package/src/__tests__/trust-store.test.ts +7 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +1 -1
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +1 -1
- package/src/__tests__/trusted-contact-multichannel.test.ts +1 -1
- package/src/__tests__/trusted-contact-verification.test.ts +1 -1
- package/src/__tests__/turn-boundary-resolution.test.ts +1 -1
- package/src/__tests__/twilio-routes.test.ts +1 -1
- package/src/__tests__/update-bulletin.test.ts +1 -1
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +1 -1
- package/src/__tests__/vellum-self-knowledge-inline-command.test.ts +1 -0
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +1 -1
- package/src/__tests__/voice-session-bridge.test.ts +1 -1
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +4 -4
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +1 -1
- package/src/__tests__/workspace-migration-down-functions.test.ts +15 -3
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +40 -4
- package/src/agent/loop.ts +6 -9
- package/src/approvals/guardian-decision-primitive.ts +46 -18
- package/src/approvals/guardian-request-resolvers.ts +19 -2
- package/src/calls/active-call-lease.ts +2 -2
- package/src/cli/AGENTS.md +1 -1
- package/src/cli/commands/doctor.ts +9 -9
- package/src/cli/commands/memory.ts +142 -0
- package/src/cli/commands/oauth/__tests__/connect.test.ts +13 -11
- package/src/cli/commands/oauth/__tests__/ping.test.ts +1 -1
- package/src/cli/commands/oauth/connect.ts +13 -12
- package/src/cli/commands/oauth/index.ts +1 -1
- package/src/cli/commands/oauth/providers.ts +47 -62
- package/src/cli/commands/platform/__tests__/connect.test.ts +72 -46
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +54 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +36 -0
- package/src/cli/commands/platform/connect.ts +17 -7
- package/src/cli/commands/platform/disconnect.ts +28 -3
- package/src/cli/commands/platform/index.ts +3 -3
- package/src/cli.ts +1 -299
- package/src/config/assistant-feature-flags.ts +23 -15
- package/src/config/bundled-skills/app-builder/TOOLS.json +16 -0
- package/src/config/bundled-skills/app-builder/tools/app-create.ts +4 -0
- package/src/config/bundled-skills/app-builder/tools/app-delete.ts +5 -1
- package/src/config/bundled-skills/app-builder/tools/app-generate-icon.ts +9 -1
- package/src/config/bundled-skills/app-builder/tools/app-refresh.ts +5 -1
- package/src/config/bundled-skills/contacts/TOOLS.json +8 -0
- package/src/config/bundled-skills/contacts/tools/contact-search.ts +10 -1
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +16 -2
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +1 -0
- package/src/config/bundled-skills/messaging/SKILL.md +7 -7
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +37 -0
- package/src/config/bundled-skills/slack/SKILL.md +18 -0
- package/src/config/env-registry.ts +15 -11
- package/src/config/env.ts +1 -11
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/schema.ts +4 -0
- package/src/config/schemas/heartbeat.ts +6 -1
- package/src/config/schemas/inference.ts +14 -3
- package/src/config/schemas/memory-processing.ts +16 -8
- package/src/config/schemas/memory-retrieval.ts +3 -3
- package/src/config/skills.ts +1 -1
- package/src/context/window-manager.ts +174 -51
- package/src/credential-execution/executable-discovery.ts +2 -2
- package/src/daemon/approved-devices-store.ts +2 -2
- package/src/daemon/assistant-attachments.ts +2 -0
- package/src/daemon/config-watcher.ts +4 -50
- package/src/daemon/conversation-agent-loop-handlers.ts +9 -1
- package/src/daemon/conversation-agent-loop.ts +12 -0
- package/src/daemon/conversation-error.ts +3 -5
- package/src/daemon/conversation-history.ts +7 -3
- package/src/daemon/conversation-lifecycle.ts +16 -0
- package/src/daemon/conversation-messaging.ts +1 -0
- package/src/daemon/conversation-notifiers.ts +67 -30
- package/src/daemon/conversation-process.ts +161 -2
- package/src/daemon/conversation-queue-manager.ts +2 -0
- package/src/daemon/conversation-runtime-assembly.ts +33 -11
- package/src/daemon/conversation-slash.ts +14 -3
- package/src/daemon/conversation-tool-setup.ts +2 -0
- package/src/daemon/conversation-usage.ts +32 -4
- package/src/daemon/conversation.ts +33 -1
- package/src/daemon/daemon-control.ts +32 -16
- package/src/daemon/date-context.ts +47 -45
- package/src/daemon/dictation-profile-store.ts +2 -2
- package/src/daemon/handlers/conversations.ts +19 -0
- package/src/daemon/handlers/shared.ts +14 -21
- package/src/daemon/lifecycle.ts +5 -7
- package/src/daemon/message-types/conversations.ts +2 -0
- package/src/daemon/message-types/guardian-actions.ts +3 -17
- package/src/daemon/message-types/integrations.ts +11 -1
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/pairing-store.ts +2 -79
- package/src/daemon/server.ts +154 -8
- package/src/daemon/watch-handler.ts +65 -21
- package/src/email/guardrails.ts +3 -3
- package/src/heartbeat/heartbeat-service.ts +14 -7
- package/src/hooks/cli.ts +2 -2
- package/src/hooks/config.ts +2 -2
- package/src/hooks/discovery.ts +2 -2
- package/src/hooks/manager.ts +2 -2
- package/src/hooks/runner.ts +5 -2
- package/src/hooks/templates.ts +2 -2
- package/src/memory/admin.ts +181 -2
- package/src/memory/app-git-service.ts +61 -4
- package/src/memory/attachments-store.ts +2 -0
- package/src/memory/canonical-guardian-store.ts +16 -0
- package/src/memory/db-init.ts +8 -0
- package/src/memory/embedding-local.ts +5 -2
- package/src/memory/indexer.ts +44 -26
- package/src/memory/items-extractor.ts +34 -82
- package/src/memory/job-handlers/batch-extraction.ts +741 -0
- package/src/memory/job-handlers/journal-carry-forward.test.ts +383 -0
- package/src/memory/job-handlers/journal-carry-forward.ts +255 -0
- package/src/memory/jobs-store.ts +28 -0
- package/src/memory/jobs-worker.ts +56 -9
- package/src/memory/lifecycle-events-store.ts +4 -2
- package/src/memory/llm-request-log-store.ts +40 -2
- package/src/memory/llm-usage-store.ts +4 -3
- package/src/memory/migrations/199-guardian-request-enrichment-columns.ts +71 -0
- package/src/memory/migrations/200-usage-llm-call-count.ts +20 -0
- package/src/memory/migrations/index.ts +2 -0
- package/src/memory/query-expansion.ts +83 -0
- package/src/memory/retriever.test.ts +119 -0
- package/src/memory/retriever.ts +513 -105
- package/src/memory/schema/guardian.ts +4 -0
- package/src/memory/schema/infrastructure.ts +1 -0
- package/src/memory/search/formatting.test.ts +140 -0
- package/src/memory/search/formatting.ts +143 -198
- package/src/memory/search/mmr.ts +136 -0
- package/src/memory/search/staleness.ts +0 -15
- package/src/memory/search/tier-classifier.ts +10 -21
- package/src/memory/search/types.ts +17 -0
- package/src/messaging/providers/slack/adapter.ts +51 -5
- package/src/notifications/broadcaster.ts +13 -0
- package/src/notifications/copy-composer.ts +8 -0
- package/src/oauth/connect-orchestrator.ts +1 -1
- package/src/oauth/connection-resolver.ts +2 -2
- package/src/oauth/provider-serializer.ts +116 -0
- package/src/permissions/trust-store.ts +24 -7
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +5 -0
- package/src/prompts/journal-context.ts +50 -35
- package/src/prompts/persona-resolver.ts +1 -1
- package/src/prompts/system-prompt.ts +27 -28
- package/src/prompts/templates/BOOTSTRAP.md +14 -1
- package/src/prompts/templates/HEARTBEAT.md +10 -0
- package/src/prompts/templates/NOW.md +19 -25
- package/src/prompts/templates/SOUL.md +13 -1
- package/src/prompts/templates/UPDATES.md +12 -0
- package/src/prompts/update-bulletin.ts +1 -1
- package/src/providers/anthropic/client.ts +89 -18
- package/src/providers/model-catalog.ts +22 -2
- package/src/providers/model-intents.ts +2 -2
- package/src/providers/openai/client.ts +40 -1
- package/src/providers/retry.ts +23 -4
- package/src/providers/types.ts +2 -0
- package/src/runtime/assistant-scope.ts +1 -1
- package/src/runtime/auth/__tests__/credential-service.test.ts +1 -0
- package/src/runtime/auth/route-policy.ts +1 -0
- package/src/runtime/auth/token-service.ts +51 -29
- package/src/runtime/confirmation-request-guardian-bridge.ts +3 -1
- package/src/runtime/guardian-decision-types.ts +16 -10
- package/src/runtime/http-server.ts +3 -14
- package/src/runtime/http-types.ts +1 -0
- package/src/runtime/migrations/vbundle-builder.ts +7 -4
- package/src/runtime/migrations/vbundle-import-analyzer.ts +0 -4
- package/src/runtime/migrations/vbundle-importer.ts +1 -1
- package/src/runtime/routes/conversation-query-routes.ts +40 -8
- package/src/runtime/routes/conversation-routes.ts +125 -3
- package/src/runtime/routes/guardian-action-routes.ts +9 -3
- package/src/runtime/routes/identity-routes.ts +25 -4
- package/src/runtime/routes/llm-context-normalization.ts +1 -0
- package/src/runtime/routes/log-export-routes.ts +34 -12
- package/src/runtime/routes/migration-routes.ts +6 -10
- package/src/runtime/routes/oauth-apps.ts +2 -9
- package/src/runtime/routes/oauth-providers.ts +60 -0
- package/src/runtime/routes/pairing-routes.ts +0 -8
- package/src/runtime/routes/settings-routes.ts +0 -1
- package/src/runtime/routes/telemetry-routes.ts +16 -4
- package/src/security/encrypted-store.ts +2 -2
- package/src/security/secret-allowlist.ts +3 -3
- package/src/signals/emit-event.ts +42 -0
- package/src/signals/user-message.ts +37 -0
- package/src/telemetry/usage-telemetry-reporter.test.ts +83 -19
- package/src/telemetry/usage-telemetry-reporter.ts +23 -17
- package/src/tools/browser/runtime-check.ts +2 -2
- package/src/tools/credentials/vault.ts +2 -249
- package/src/tools/memory/definitions.ts +1 -1
- package/src/tools/memory/handlers.test.ts +50 -8
- package/src/tools/memory/handlers.ts +3 -1
- package/src/tools/side-effects.ts +1 -6
- package/src/tools/terminal/safe-env.ts +3 -2
- package/src/tools/terminal/shell.ts +11 -14
- package/src/tools/tool-approval-handler.ts +20 -1
- package/src/tools/tool-input-summary.ts +66 -0
- package/src/tools/types.ts +4 -0
- package/src/usage/types.ts +4 -0
- package/src/util/device-id.ts +10 -10
- package/src/util/platform.ts +71 -33
- package/src/util/pricing.ts +19 -6
- package/src/util/strip-comment-lines.ts +28 -0
- package/src/workspace/git-service.ts +8 -18
- package/src/workspace/migrations/003-seed-device-id.ts +6 -4
- package/src/workspace/migrations/016-extract-feature-flags-to-protected.ts +7 -1
- package/src/workspace/migrations/017-seed-persona-dirs.ts +2 -4
- package/src/workspace/migrations/021-move-signals-to-workspace.ts +84 -0
- package/src/workspace/migrations/022-move-hooks-to-workspace.ts +94 -0
- package/src/workspace/migrations/023-move-config-files-to-workspace.ts +86 -0
- package/src/workspace/migrations/024-move-runtime-files-to-workspace.ts +126 -0
- package/src/workspace/migrations/migrate-to-workspace-volume.ts +3 -6
- package/src/workspace/migrations/registry.ts +8 -0
- package/src/signals/confirm.ts +0 -82
- package/src/signals/trust-rule.ts +0 -174
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { mkdirSync,
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
1
|
+
import { mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
3
2
|
import { join } from "node:path";
|
|
4
3
|
import {
|
|
5
4
|
afterAll,
|
|
@@ -11,22 +10,7 @@ import {
|
|
|
11
10
|
test,
|
|
12
11
|
} from "bun:test";
|
|
13
12
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
const testWorkspaceDir = join(testDir, ".vellum", "workspace");
|
|
17
|
-
|
|
18
|
-
mock.module("../util/platform.js", () => ({
|
|
19
|
-
getDataDir: () => testDir,
|
|
20
|
-
isMacOS: () => process.platform === "darwin",
|
|
21
|
-
isLinux: () => process.platform === "linux",
|
|
22
|
-
isWindows: () => process.platform === "win32",
|
|
23
|
-
getPidPath: () => join(testDir, "test.pid"),
|
|
24
|
-
getDbPath: () => join(testDir, "test.db"),
|
|
25
|
-
getLogPath: () => join(testDir, "test.log"),
|
|
26
|
-
ensureDataDir: () => {},
|
|
27
|
-
getWorkspaceDir: () => testWorkspaceDir,
|
|
28
|
-
getWorkspacePromptPath: (file: string) => join(testWorkspaceDir, file),
|
|
29
|
-
}));
|
|
13
|
+
const testWorkspaceDir = process.env.VELLUM_WORKSPACE_DIR!;
|
|
30
14
|
|
|
31
15
|
mock.module("../util/logger.js", () => ({
|
|
32
16
|
getLogger: () =>
|
|
@@ -129,6 +113,7 @@ import {
|
|
|
129
113
|
formatAbsoluteTime,
|
|
130
114
|
formatRelativeTime,
|
|
131
115
|
injectMemoryRecallAsUserBlock,
|
|
116
|
+
lookupSupersessionChain,
|
|
132
117
|
} from "../memory/retriever.js";
|
|
133
118
|
import {
|
|
134
119
|
conversations,
|
|
@@ -140,6 +125,7 @@ import {
|
|
|
140
125
|
memorySummaries,
|
|
141
126
|
messages,
|
|
142
127
|
} from "../memory/schema.js";
|
|
128
|
+
import { buildMemoryInjection } from "../memory/search/formatting.js";
|
|
143
129
|
import { buildCoreIdentityContext } from "../prompts/system-prompt.js";
|
|
144
130
|
import type { Message } from "../providers/types.js";
|
|
145
131
|
|
|
@@ -167,11 +153,6 @@ describe("Memory regressions", () => {
|
|
|
167
153
|
|
|
168
154
|
afterAll(() => {
|
|
169
155
|
resetDb();
|
|
170
|
-
try {
|
|
171
|
-
rmSync(testDir, { recursive: true });
|
|
172
|
-
} catch {
|
|
173
|
-
// best effort cleanup
|
|
174
|
-
}
|
|
175
156
|
});
|
|
176
157
|
|
|
177
158
|
function semanticRecallConfig() {
|
|
@@ -216,7 +197,10 @@ describe("Memory regressions", () => {
|
|
|
216
197
|
conversationId: "conv-baseline-scope",
|
|
217
198
|
role: "user",
|
|
218
199
|
content: JSON.stringify([
|
|
219
|
-
{
|
|
200
|
+
{
|
|
201
|
+
type: "text",
|
|
202
|
+
text: "The user strongly prefers dark mode for all editor themes and UIs.",
|
|
203
|
+
},
|
|
220
204
|
]),
|
|
221
205
|
createdAt: now,
|
|
222
206
|
})
|
|
@@ -229,7 +213,10 @@ describe("Memory regressions", () => {
|
|
|
229
213
|
conversationId: "conv-baseline-scope",
|
|
230
214
|
role: "user",
|
|
231
215
|
content: JSON.stringify([
|
|
232
|
-
{
|
|
216
|
+
{
|
|
217
|
+
type: "text",
|
|
218
|
+
text: "The user strongly prefers dark mode for all editor themes and UIs.",
|
|
219
|
+
},
|
|
233
220
|
]),
|
|
234
221
|
createdAt: now,
|
|
235
222
|
},
|
|
@@ -1571,7 +1558,10 @@ describe("Memory regressions", () => {
|
|
|
1571
1558
|
conversationId: "conv-scope-test",
|
|
1572
1559
|
role: "user",
|
|
1573
1560
|
content: JSON.stringify([
|
|
1574
|
-
{
|
|
1561
|
+
{
|
|
1562
|
+
type: "text",
|
|
1563
|
+
text: "Remember my scope preference for organizing projects by team and priority level.",
|
|
1564
|
+
},
|
|
1575
1565
|
]),
|
|
1576
1566
|
createdAt: now,
|
|
1577
1567
|
})
|
|
@@ -1583,7 +1573,10 @@ describe("Memory regressions", () => {
|
|
|
1583
1573
|
conversationId: "conv-scope-test",
|
|
1584
1574
|
role: "user",
|
|
1585
1575
|
content: JSON.stringify([
|
|
1586
|
-
{
|
|
1576
|
+
{
|
|
1577
|
+
type: "text",
|
|
1578
|
+
text: "Remember my scope preference for organizing projects by team and priority level.",
|
|
1579
|
+
},
|
|
1587
1580
|
]),
|
|
1588
1581
|
createdAt: now,
|
|
1589
1582
|
scopeId: "project-xyz",
|
|
@@ -2278,11 +2271,13 @@ describe("Memory regressions", () => {
|
|
|
2278
2271
|
}
|
|
2279
2272
|
});
|
|
2280
2273
|
|
|
2281
|
-
// PR-18:
|
|
2282
|
-
test("
|
|
2274
|
+
// PR-18: batch_extract jobs carry scopeId through the async pipeline
|
|
2275
|
+
test("batch_extract job payload includes scopeId from private conversation", async () => {
|
|
2283
2276
|
// These tests verify job payload contents, so LLM extraction must be
|
|
2284
|
-
// enabled — otherwise the indexer skips enqueuing
|
|
2277
|
+
// enabled — otherwise the indexer skips enqueuing batch_extract entirely.
|
|
2278
|
+
// Set batchSize=1 so a single message triggers immediate batch_extract.
|
|
2285
2279
|
TEST_CONFIG.memory.extraction.useLLM = true;
|
|
2280
|
+
TEST_CONFIG.memory.extraction.batchSize = 1;
|
|
2286
2281
|
try {
|
|
2287
2282
|
const conv = createConversation({
|
|
2288
2283
|
title: "Private scope job test",
|
|
@@ -2300,8 +2295,13 @@ describe("Memory regressions", () => {
|
|
|
2300
2295
|
const extractJobs = db
|
|
2301
2296
|
.select()
|
|
2302
2297
|
.from(memoryJobs)
|
|
2303
|
-
.where(eq(memoryJobs.type, "
|
|
2304
|
-
.all()
|
|
2298
|
+
.where(eq(memoryJobs.type, "batch_extract"))
|
|
2299
|
+
.all()
|
|
2300
|
+
.filter(
|
|
2301
|
+
(j) =>
|
|
2302
|
+
JSON.parse(j.payload).conversationId === conv.id &&
|
|
2303
|
+
JSON.parse(j.payload).scopeId != null,
|
|
2304
|
+
);
|
|
2305
2305
|
|
|
2306
2306
|
expect(extractJobs.length).toBeGreaterThan(0);
|
|
2307
2307
|
const lastJob = extractJobs[extractJobs.length - 1];
|
|
@@ -2309,11 +2309,13 @@ describe("Memory regressions", () => {
|
|
|
2309
2309
|
expect(payload.scopeId).toBe(conv.memoryScopeId);
|
|
2310
2310
|
} finally {
|
|
2311
2311
|
TEST_CONFIG.memory.extraction.useLLM = false;
|
|
2312
|
+
TEST_CONFIG.memory.extraction.batchSize = 10;
|
|
2312
2313
|
}
|
|
2313
2314
|
});
|
|
2314
2315
|
|
|
2315
|
-
test("
|
|
2316
|
+
test("batch_extract job payload defaults scopeId to default for standard conversations", async () => {
|
|
2316
2317
|
TEST_CONFIG.memory.extraction.useLLM = true;
|
|
2318
|
+
TEST_CONFIG.memory.extraction.batchSize = 1;
|
|
2317
2319
|
try {
|
|
2318
2320
|
const conv = createConversation({
|
|
2319
2321
|
title: "Standard scope job test",
|
|
@@ -2331,8 +2333,13 @@ describe("Memory regressions", () => {
|
|
|
2331
2333
|
const extractJobs = db
|
|
2332
2334
|
.select()
|
|
2333
2335
|
.from(memoryJobs)
|
|
2334
|
-
.where(eq(memoryJobs.type, "
|
|
2335
|
-
.all()
|
|
2336
|
+
.where(eq(memoryJobs.type, "batch_extract"))
|
|
2337
|
+
.all()
|
|
2338
|
+
.filter(
|
|
2339
|
+
(j) =>
|
|
2340
|
+
JSON.parse(j.payload).conversationId === conv.id &&
|
|
2341
|
+
JSON.parse(j.payload).scopeId != null,
|
|
2342
|
+
);
|
|
2336
2343
|
|
|
2337
2344
|
expect(extractJobs.length).toBeGreaterThan(0);
|
|
2338
2345
|
const lastJob = extractJobs[extractJobs.length - 1];
|
|
@@ -2340,6 +2347,7 @@ describe("Memory regressions", () => {
|
|
|
2340
2347
|
expect(payload.scopeId).toBe("default");
|
|
2341
2348
|
} finally {
|
|
2342
2349
|
TEST_CONFIG.memory.extraction.useLLM = false;
|
|
2350
|
+
TEST_CONFIG.memory.extraction.batchSize = 10;
|
|
2343
2351
|
}
|
|
2344
2352
|
});
|
|
2345
2353
|
|
|
@@ -3209,7 +3217,7 @@ describe("Memory regressions", () => {
|
|
|
3209
3217
|
|
|
3210
3218
|
// ── Trust-aware extraction gating tests (M3) ───────────────────────
|
|
3211
3219
|
|
|
3212
|
-
test("untrusted actor messages do not enqueue
|
|
3220
|
+
test("untrusted actor messages do not enqueue batch_extract", async () => {
|
|
3213
3221
|
const db = getDb();
|
|
3214
3222
|
const now = Date.now();
|
|
3215
3223
|
db.insert(conversations)
|
|
@@ -3232,7 +3240,10 @@ describe("Memory regressions", () => {
|
|
|
3232
3240
|
conversationId: "conv-untrusted-gate",
|
|
3233
3241
|
role: "user",
|
|
3234
3242
|
content: JSON.stringify([
|
|
3235
|
-
{
|
|
3243
|
+
{
|
|
3244
|
+
type: "text",
|
|
3245
|
+
text: "Untrusted user preference for dark mode across all editor themes and interfaces.",
|
|
3246
|
+
},
|
|
3236
3247
|
]),
|
|
3237
3248
|
createdAt: now,
|
|
3238
3249
|
})
|
|
@@ -3244,7 +3255,10 @@ describe("Memory regressions", () => {
|
|
|
3244
3255
|
conversationId: "conv-untrusted-gate",
|
|
3245
3256
|
role: "user",
|
|
3246
3257
|
content: JSON.stringify([
|
|
3247
|
-
{
|
|
3258
|
+
{
|
|
3259
|
+
type: "text",
|
|
3260
|
+
text: "Untrusted user preference for dark mode across all editor themes and interfaces.",
|
|
3261
|
+
},
|
|
3248
3262
|
]),
|
|
3249
3263
|
createdAt: now,
|
|
3250
3264
|
provenanceTrustClass: "trusted_contact",
|
|
@@ -3254,18 +3268,19 @@ describe("Memory regressions", () => {
|
|
|
3254
3268
|
|
|
3255
3269
|
expect(result.indexedSegments).toBeGreaterThan(0);
|
|
3256
3270
|
|
|
3257
|
-
// No
|
|
3271
|
+
// No batch_extract jobs should be enqueued for untrusted actors
|
|
3258
3272
|
const extractJobs = db
|
|
3259
3273
|
.select()
|
|
3260
3274
|
.from(memoryJobs)
|
|
3261
|
-
.where(
|
|
3275
|
+
.where(eq(memoryJobs.type, "batch_extract"))
|
|
3262
3276
|
.all()
|
|
3263
|
-
.filter(
|
|
3277
|
+
.filter(
|
|
3278
|
+
(j) => JSON.parse(j.payload).conversationId === "conv-untrusted-gate",
|
|
3279
|
+
);
|
|
3264
3280
|
expect(extractJobs.length).toBe(0);
|
|
3265
3281
|
|
|
3266
|
-
// enqueuedJobs
|
|
3267
|
-
|
|
3268
|
-
expect(result.enqueuedJobs).toBe(expectedJobs);
|
|
3282
|
+
// enqueuedJobs reflects embed jobs only (no extraction for untrusted)
|
|
3283
|
+
expect(result.enqueuedJobs).toBe(result.indexedSegments);
|
|
3269
3284
|
});
|
|
3270
3285
|
|
|
3271
3286
|
test("trusted guardian messages still enqueue extraction", async () => {
|
|
@@ -3291,7 +3306,10 @@ describe("Memory regressions", () => {
|
|
|
3291
3306
|
conversationId: "conv-trusted-gate",
|
|
3292
3307
|
role: "user",
|
|
3293
3308
|
content: JSON.stringify([
|
|
3294
|
-
{
|
|
3309
|
+
{
|
|
3310
|
+
type: "text",
|
|
3311
|
+
text: "Trusted guardian preference for light mode with high contrast accessibility settings.",
|
|
3312
|
+
},
|
|
3295
3313
|
]),
|
|
3296
3314
|
createdAt: now,
|
|
3297
3315
|
})
|
|
@@ -3303,7 +3321,10 @@ describe("Memory regressions", () => {
|
|
|
3303
3321
|
conversationId: "conv-trusted-gate",
|
|
3304
3322
|
role: "user",
|
|
3305
3323
|
content: JSON.stringify([
|
|
3306
|
-
{
|
|
3324
|
+
{
|
|
3325
|
+
type: "text",
|
|
3326
|
+
text: "Trusted guardian preference for light mode with high contrast accessibility settings.",
|
|
3327
|
+
},
|
|
3307
3328
|
]),
|
|
3308
3329
|
createdAt: now,
|
|
3309
3330
|
provenanceTrustClass: "guardian",
|
|
@@ -3313,18 +3334,16 @@ describe("Memory regressions", () => {
|
|
|
3313
3334
|
|
|
3314
3335
|
expect(result.indexedSegments).toBeGreaterThan(0);
|
|
3315
3336
|
|
|
3316
|
-
//
|
|
3337
|
+
// batch_extract job should be enqueued (debounced) for trusted guardian
|
|
3317
3338
|
const extractJobs = db
|
|
3318
3339
|
.select()
|
|
3319
3340
|
.from(memoryJobs)
|
|
3320
|
-
.where(eq(memoryJobs.type, "
|
|
3341
|
+
.where(eq(memoryJobs.type, "batch_extract"))
|
|
3321
3342
|
.all()
|
|
3322
|
-
.filter(
|
|
3343
|
+
.filter(
|
|
3344
|
+
(j) => JSON.parse(j.payload).conversationId === "conv-trusted-gate",
|
|
3345
|
+
);
|
|
3323
3346
|
expect(extractJobs.length).toBe(1);
|
|
3324
|
-
|
|
3325
|
-
// enqueuedJobs: embed per segment + extract_items (counts as 2: extract + summary)
|
|
3326
|
-
// For user role: shouldExtract=true
|
|
3327
|
-
expect(result.enqueuedJobs).toBeGreaterThan(result.indexedSegments + 1);
|
|
3328
3347
|
});
|
|
3329
3348
|
|
|
3330
3349
|
test("legacy messages without provenance still enqueue extraction", async () => {
|
|
@@ -3350,7 +3369,10 @@ describe("Memory regressions", () => {
|
|
|
3350
3369
|
conversationId: "conv-legacy-gate",
|
|
3351
3370
|
role: "user",
|
|
3352
3371
|
content: JSON.stringify([
|
|
3353
|
-
{
|
|
3372
|
+
{
|
|
3373
|
+
type: "text",
|
|
3374
|
+
text: "Legacy message with no provenance info that still needs full extraction processing.",
|
|
3375
|
+
},
|
|
3354
3376
|
]),
|
|
3355
3377
|
createdAt: now,
|
|
3356
3378
|
})
|
|
@@ -3362,7 +3384,10 @@ describe("Memory regressions", () => {
|
|
|
3362
3384
|
conversationId: "conv-legacy-gate",
|
|
3363
3385
|
role: "user",
|
|
3364
3386
|
content: JSON.stringify([
|
|
3365
|
-
{
|
|
3387
|
+
{
|
|
3388
|
+
type: "text",
|
|
3389
|
+
text: "Legacy message with no provenance info that still needs full extraction processing.",
|
|
3390
|
+
},
|
|
3366
3391
|
]),
|
|
3367
3392
|
createdAt: now,
|
|
3368
3393
|
// provenanceTrustClass is intentionally omitted (undefined) to test default behavior
|
|
@@ -3372,20 +3397,19 @@ describe("Memory regressions", () => {
|
|
|
3372
3397
|
|
|
3373
3398
|
expect(result.indexedSegments).toBeGreaterThan(0);
|
|
3374
3399
|
|
|
3375
|
-
//
|
|
3400
|
+
// batch_extract job should still be enqueued (debounced) for messages without provenance
|
|
3376
3401
|
const extractJobs = db
|
|
3377
3402
|
.select()
|
|
3378
3403
|
.from(memoryJobs)
|
|
3379
|
-
.where(eq(memoryJobs.type, "
|
|
3404
|
+
.where(eq(memoryJobs.type, "batch_extract"))
|
|
3380
3405
|
.all()
|
|
3381
|
-
.filter(
|
|
3406
|
+
.filter(
|
|
3407
|
+
(j) => JSON.parse(j.payload).conversationId === "conv-legacy-gate",
|
|
3408
|
+
);
|
|
3382
3409
|
expect(extractJobs.length).toBe(1);
|
|
3383
|
-
|
|
3384
|
-
// enqueuedJobs should include extraction jobs
|
|
3385
|
-
expect(result.enqueuedJobs).toBeGreaterThan(result.indexedSegments + 1);
|
|
3386
3410
|
});
|
|
3387
3411
|
|
|
3388
|
-
test("unverified_channel messages do not enqueue
|
|
3412
|
+
test("unverified_channel messages do not enqueue batch_extract", async () => {
|
|
3389
3413
|
const db = getDb();
|
|
3390
3414
|
const now = Date.now();
|
|
3391
3415
|
db.insert(conversations)
|
|
@@ -3410,7 +3434,7 @@ describe("Memory regressions", () => {
|
|
|
3410
3434
|
content: JSON.stringify([
|
|
3411
3435
|
{
|
|
3412
3436
|
type: "text",
|
|
3413
|
-
text: "Unverified channel preference for compact layout.",
|
|
3437
|
+
text: "Unverified channel preference for compact layout with sidebar navigation always visible.",
|
|
3414
3438
|
},
|
|
3415
3439
|
]),
|
|
3416
3440
|
createdAt: now,
|
|
@@ -3425,7 +3449,7 @@ describe("Memory regressions", () => {
|
|
|
3425
3449
|
content: JSON.stringify([
|
|
3426
3450
|
{
|
|
3427
3451
|
type: "text",
|
|
3428
|
-
text: "Unverified channel preference for compact layout.",
|
|
3452
|
+
text: "Unverified channel preference for compact layout with sidebar navigation always visible.",
|
|
3429
3453
|
},
|
|
3430
3454
|
]),
|
|
3431
3455
|
createdAt: now,
|
|
@@ -3436,18 +3460,19 @@ describe("Memory regressions", () => {
|
|
|
3436
3460
|
|
|
3437
3461
|
expect(result.indexedSegments).toBeGreaterThan(0);
|
|
3438
3462
|
|
|
3439
|
-
// No
|
|
3463
|
+
// No batch_extract jobs should be enqueued for unverified channel
|
|
3440
3464
|
const extractJobs = db
|
|
3441
3465
|
.select()
|
|
3442
3466
|
.from(memoryJobs)
|
|
3443
|
-
.where(eq(memoryJobs.type, "
|
|
3467
|
+
.where(eq(memoryJobs.type, "batch_extract"))
|
|
3444
3468
|
.all()
|
|
3445
|
-
.filter(
|
|
3469
|
+
.filter(
|
|
3470
|
+
(j) => JSON.parse(j.payload).conversationId === "conv-unverified-gate",
|
|
3471
|
+
);
|
|
3446
3472
|
expect(extractJobs.length).toBe(0);
|
|
3447
3473
|
|
|
3448
|
-
// enqueuedJobs
|
|
3449
|
-
|
|
3450
|
-
expect(result.enqueuedJobs).toBe(expectedJobs);
|
|
3474
|
+
// enqueuedJobs reflects embed jobs only (no extraction for untrusted)
|
|
3475
|
+
expect(result.enqueuedJobs).toBe(result.indexedSegments);
|
|
3451
3476
|
});
|
|
3452
3477
|
|
|
3453
3478
|
test("buildCoreIdentityContext includes identity files when they exist", () => {
|
|
@@ -3481,4 +3506,191 @@ describe("Memory regressions", () => {
|
|
|
3481
3506
|
const context = buildCoreIdentityContext();
|
|
3482
3507
|
expect(context).toBeNull();
|
|
3483
3508
|
});
|
|
3509
|
+
|
|
3510
|
+
// ── Inline supersession rendering tests ──────────────────────────
|
|
3511
|
+
|
|
3512
|
+
test("buildMemoryInjection renders inline supersedes tag for items with supersession chain", () => {
|
|
3513
|
+
const db = getDb();
|
|
3514
|
+
const now = Date.now();
|
|
3515
|
+
|
|
3516
|
+
// Create the superseded (predecessor) item in the DB
|
|
3517
|
+
db.insert(memoryItems)
|
|
3518
|
+
.values({
|
|
3519
|
+
id: "item-predecessor-render",
|
|
3520
|
+
kind: "preference",
|
|
3521
|
+
subject: "color",
|
|
3522
|
+
statement: "Favorite color is blue",
|
|
3523
|
+
status: "active",
|
|
3524
|
+
confidence: 0.9,
|
|
3525
|
+
importance: 0.7,
|
|
3526
|
+
fingerprint: "fp-pred-render",
|
|
3527
|
+
firstSeenAt: now - 86_400_000,
|
|
3528
|
+
lastSeenAt: now - 86_400_000,
|
|
3529
|
+
accessCount: 1,
|
|
3530
|
+
verificationState: "assistant_inferred",
|
|
3531
|
+
})
|
|
3532
|
+
.run();
|
|
3533
|
+
|
|
3534
|
+
const candidate = {
|
|
3535
|
+
key: "item:item-superseding-render",
|
|
3536
|
+
type: "item" as const,
|
|
3537
|
+
id: "item-superseding-render",
|
|
3538
|
+
source: "semantic" as const,
|
|
3539
|
+
text: "Favorite color is green",
|
|
3540
|
+
kind: "preference",
|
|
3541
|
+
confidence: 0.9,
|
|
3542
|
+
importance: 0.8,
|
|
3543
|
+
createdAt: now,
|
|
3544
|
+
semantic: 0.9,
|
|
3545
|
+
recency: 0.8,
|
|
3546
|
+
finalScore: 0.85,
|
|
3547
|
+
supersedes: "item-predecessor-render",
|
|
3548
|
+
};
|
|
3549
|
+
|
|
3550
|
+
const injection = buildMemoryInjection({
|
|
3551
|
+
candidates: [candidate],
|
|
3552
|
+
totalBudgetTokens: 5000,
|
|
3553
|
+
});
|
|
3554
|
+
|
|
3555
|
+
expect(injection).toContain("<supersedes count=");
|
|
3556
|
+
expect(injection).toContain('count="1"');
|
|
3557
|
+
expect(injection).toContain("Favorite color is blue");
|
|
3558
|
+
expect(injection).toContain("</supersedes>");
|
|
3559
|
+
// The supersedes tag should be inside the item tag
|
|
3560
|
+
expect(injection).toMatch(
|
|
3561
|
+
/<item[^>]*>.*<supersedes.*<\/supersedes><\/item>/,
|
|
3562
|
+
);
|
|
3563
|
+
|
|
3564
|
+
// Clean up
|
|
3565
|
+
db.delete(memoryItems)
|
|
3566
|
+
.where(eq(memoryItems.id, "item-predecessor-render"))
|
|
3567
|
+
.run();
|
|
3568
|
+
});
|
|
3569
|
+
|
|
3570
|
+
test("lookupSupersessionChain counts chain depth correctly", () => {
|
|
3571
|
+
const db = getDb();
|
|
3572
|
+
const now = Date.now();
|
|
3573
|
+
const MS_PER_DAY = 86_400_000;
|
|
3574
|
+
|
|
3575
|
+
// Create a chain of 3 items: grandparent → parent → child
|
|
3576
|
+
db.insert(memoryItems)
|
|
3577
|
+
.values({
|
|
3578
|
+
id: "item-chain-grandparent",
|
|
3579
|
+
kind: "fact",
|
|
3580
|
+
subject: "address",
|
|
3581
|
+
statement: "Lives at 123 Main St",
|
|
3582
|
+
status: "active",
|
|
3583
|
+
confidence: 0.8,
|
|
3584
|
+
importance: 0.6,
|
|
3585
|
+
fingerprint: "fp-chain-gp",
|
|
3586
|
+
firstSeenAt: now - 3 * MS_PER_DAY,
|
|
3587
|
+
lastSeenAt: now - 3 * MS_PER_DAY,
|
|
3588
|
+
accessCount: 1,
|
|
3589
|
+
verificationState: "assistant_inferred",
|
|
3590
|
+
})
|
|
3591
|
+
.run();
|
|
3592
|
+
|
|
3593
|
+
db.insert(memoryItems)
|
|
3594
|
+
.values({
|
|
3595
|
+
id: "item-chain-parent",
|
|
3596
|
+
kind: "fact",
|
|
3597
|
+
subject: "address",
|
|
3598
|
+
statement: "Lives at 456 Oak Ave",
|
|
3599
|
+
status: "active",
|
|
3600
|
+
confidence: 0.8,
|
|
3601
|
+
importance: 0.6,
|
|
3602
|
+
fingerprint: "fp-chain-p",
|
|
3603
|
+
supersedes: "item-chain-grandparent",
|
|
3604
|
+
firstSeenAt: now - 2 * MS_PER_DAY,
|
|
3605
|
+
lastSeenAt: now - 2 * MS_PER_DAY,
|
|
3606
|
+
accessCount: 1,
|
|
3607
|
+
verificationState: "assistant_inferred",
|
|
3608
|
+
})
|
|
3609
|
+
.run();
|
|
3610
|
+
|
|
3611
|
+
db.insert(memoryItems)
|
|
3612
|
+
.values({
|
|
3613
|
+
id: "item-chain-child",
|
|
3614
|
+
kind: "fact",
|
|
3615
|
+
subject: "address",
|
|
3616
|
+
statement: "Lives at 789 Pine Blvd",
|
|
3617
|
+
status: "active",
|
|
3618
|
+
confidence: 0.9,
|
|
3619
|
+
importance: 0.7,
|
|
3620
|
+
fingerprint: "fp-chain-c",
|
|
3621
|
+
supersedes: "item-chain-parent",
|
|
3622
|
+
firstSeenAt: now - 1 * MS_PER_DAY,
|
|
3623
|
+
lastSeenAt: now - 1 * MS_PER_DAY,
|
|
3624
|
+
accessCount: 1,
|
|
3625
|
+
verificationState: "assistant_inferred",
|
|
3626
|
+
})
|
|
3627
|
+
.run();
|
|
3628
|
+
|
|
3629
|
+
// Look up from the child's perspective (supersedes parent)
|
|
3630
|
+
const result = lookupSupersessionChain("item-chain-parent");
|
|
3631
|
+
expect(result).not.toBeNull();
|
|
3632
|
+
expect(result!.previousStatement).toBe("Lives at 456 Oak Ave");
|
|
3633
|
+
expect(result!.previousTimestamp).toBe(now - 2 * MS_PER_DAY);
|
|
3634
|
+
// Chain: parent → grandparent = depth 2
|
|
3635
|
+
expect(result!.chainDepth).toBe(2);
|
|
3636
|
+
|
|
3637
|
+
// Look up direct predecessor (grandparent has no supersedes)
|
|
3638
|
+
const gpResult = lookupSupersessionChain("item-chain-grandparent");
|
|
3639
|
+
expect(gpResult).not.toBeNull();
|
|
3640
|
+
expect(gpResult!.previousStatement).toBe("Lives at 123 Main St");
|
|
3641
|
+
expect(gpResult!.chainDepth).toBe(1);
|
|
3642
|
+
|
|
3643
|
+
// Non-existent ID returns null
|
|
3644
|
+
const nullResult = lookupSupersessionChain("item-nonexistent");
|
|
3645
|
+
expect(nullResult).toBeNull();
|
|
3646
|
+
|
|
3647
|
+
// Clean up
|
|
3648
|
+
db.delete(memoryItems).where(eq(memoryItems.id, "item-chain-child")).run();
|
|
3649
|
+
db.delete(memoryItems).where(eq(memoryItems.id, "item-chain-parent")).run();
|
|
3650
|
+
db.delete(memoryItems)
|
|
3651
|
+
.where(eq(memoryItems.id, "item-chain-grandparent"))
|
|
3652
|
+
.run();
|
|
3653
|
+
});
|
|
3654
|
+
|
|
3655
|
+
test("escapeXmlTags escapes memory_context, recalled, and item delimiter tags", () => {
|
|
3656
|
+
// Verify new tag vocabulary is escaped by the existing generic escaper
|
|
3657
|
+
expect(escapeXmlTags("</memory_context>")).toBe("\uFF1C/memory_context>");
|
|
3658
|
+
expect(escapeXmlTags("</recalled>")).toBe("\uFF1C/recalled>");
|
|
3659
|
+
expect(escapeXmlTags("</item>")).toBe("\uFF1C/item>");
|
|
3660
|
+
expect(escapeXmlTags("</segment>")).toBe("\uFF1C/segment>");
|
|
3661
|
+
expect(escapeXmlTags("</supersedes>")).toBe("\uFF1C/supersedes>");
|
|
3662
|
+
expect(escapeXmlTags("</echoes>")).toBe("\uFF1C/echoes>");
|
|
3663
|
+
|
|
3664
|
+
// Opening tags too
|
|
3665
|
+
expect(escapeXmlTags("<memory_context>")).toBe("\uFF1Cmemory_context>");
|
|
3666
|
+
expect(escapeXmlTags("<recalled>")).toBe("\uFF1Crecalled>");
|
|
3667
|
+
expect(escapeXmlTags("<item>")).toBe("\uFF1Citem>");
|
|
3668
|
+
});
|
|
3669
|
+
|
|
3670
|
+
test("buildMemoryInjection renders items without supersedes normally", () => {
|
|
3671
|
+
const now = Date.now();
|
|
3672
|
+
const candidate = {
|
|
3673
|
+
key: "item:item-no-supersedes",
|
|
3674
|
+
type: "item" as const,
|
|
3675
|
+
id: "item-no-supersedes",
|
|
3676
|
+
source: "semantic" as const,
|
|
3677
|
+
text: "User prefers dark mode",
|
|
3678
|
+
kind: "preference",
|
|
3679
|
+
confidence: 0.9,
|
|
3680
|
+
importance: 0.8,
|
|
3681
|
+
createdAt: now,
|
|
3682
|
+
semantic: 0.9,
|
|
3683
|
+
recency: 0.8,
|
|
3684
|
+
finalScore: 0.85,
|
|
3685
|
+
};
|
|
3686
|
+
|
|
3687
|
+
const injection = buildMemoryInjection({
|
|
3688
|
+
candidates: [candidate],
|
|
3689
|
+
totalBudgetTokens: 5000,
|
|
3690
|
+
});
|
|
3691
|
+
|
|
3692
|
+
expect(injection).toContain("User prefers dark mode");
|
|
3693
|
+
expect(injection).not.toContain("<supersedes");
|
|
3694
|
+
expect(injection).not.toContain("</supersedes>");
|
|
3695
|
+
});
|
|
3484
3696
|
});
|
|
@@ -9,9 +9,6 @@
|
|
|
9
9
|
* no candidates are found and injectedText is empty. The tests verify
|
|
10
10
|
* pipeline completion, latency bounds, and token budget enforcement.
|
|
11
11
|
*/
|
|
12
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
13
|
-
import { tmpdir } from "node:os";
|
|
14
|
-
import { join } from "node:path";
|
|
15
12
|
import {
|
|
16
13
|
afterAll,
|
|
17
14
|
beforeAll,
|
|
@@ -22,19 +19,6 @@ import {
|
|
|
22
19
|
test,
|
|
23
20
|
} from "bun:test";
|
|
24
21
|
|
|
25
|
-
const testDir = mkdtempSync(join(tmpdir(), "mem-retrieval-bench-"));
|
|
26
|
-
|
|
27
|
-
mock.module("../util/platform.js", () => ({
|
|
28
|
-
getDataDir: () => testDir,
|
|
29
|
-
isMacOS: () => process.platform === "darwin",
|
|
30
|
-
isLinux: () => process.platform === "linux",
|
|
31
|
-
isWindows: () => process.platform === "win32",
|
|
32
|
-
getPidPath: () => join(testDir, "test.pid"),
|
|
33
|
-
getDbPath: () => join(testDir, "test.db"),
|
|
34
|
-
getLogPath: () => join(testDir, "test.log"),
|
|
35
|
-
ensureDataDir: () => {},
|
|
36
|
-
}));
|
|
37
|
-
|
|
38
22
|
mock.module("../util/logger.js", () => ({
|
|
39
23
|
getLogger: () =>
|
|
40
24
|
new Proxy({} as Record<string, unknown>, {
|
|
@@ -172,11 +156,6 @@ describe("Memory retrieval benchmark", () => {
|
|
|
172
156
|
|
|
173
157
|
afterAll(() => {
|
|
174
158
|
resetDb();
|
|
175
|
-
try {
|
|
176
|
-
rmSync(testDir, { recursive: true });
|
|
177
|
-
} catch {
|
|
178
|
-
// best effort cleanup
|
|
179
|
-
}
|
|
180
159
|
});
|
|
181
160
|
|
|
182
161
|
test("retrieval completes under 500ms for 100 items", async () => {
|
|
@@ -16,26 +16,10 @@
|
|
|
16
16
|
* processes and is not tested here.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import { mkdtempSync, rmSync } from "node:fs";
|
|
20
|
-
import { tmpdir } from "node:os";
|
|
21
|
-
import { join } from "node:path";
|
|
22
19
|
import { afterAll, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
23
20
|
|
|
24
21
|
import { eq } from "drizzle-orm";
|
|
25
22
|
|
|
26
|
-
const testDir = mkdtempSync(join(tmpdir(), "memory-upsert-concurrency-"));
|
|
27
|
-
|
|
28
|
-
mock.module("../util/platform.js", () => ({
|
|
29
|
-
getDataDir: () => testDir,
|
|
30
|
-
isMacOS: () => process.platform === "darwin",
|
|
31
|
-
isLinux: () => process.platform === "linux",
|
|
32
|
-
isWindows: () => process.platform === "win32",
|
|
33
|
-
getPidPath: () => join(testDir, "test.pid"),
|
|
34
|
-
getDbPath: () => join(testDir, "test.db"),
|
|
35
|
-
getLogPath: () => join(testDir, "test.log"),
|
|
36
|
-
ensureDataDir: () => {},
|
|
37
|
-
}));
|
|
38
|
-
|
|
39
23
|
mock.module("../util/logger.js", () => ({
|
|
40
24
|
getLogger: () =>
|
|
41
25
|
new Proxy({} as Record<string, unknown>, {
|
|
@@ -89,11 +73,6 @@ initializeDb();
|
|
|
89
73
|
|
|
90
74
|
afterAll(() => {
|
|
91
75
|
resetDb();
|
|
92
|
-
try {
|
|
93
|
-
rmSync(testDir, { recursive: true });
|
|
94
|
-
} catch {
|
|
95
|
-
// best effort cleanup
|
|
96
|
-
}
|
|
97
76
|
});
|
|
98
77
|
|
|
99
78
|
function resetTables() {
|