@vellumai/assistant 0.4.49 → 0.4.51
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 +24 -33
- package/README.md +3 -3
- package/docs/architecture/integrations.md +2 -2
- package/docs/architecture/keychain-broker.md +6 -6
- package/docs/architecture/memory.md +180 -119
- package/knip.json +32 -0
- package/package.json +3 -2
- package/src/__tests__/agent-loop.test.ts +3 -1
- package/src/__tests__/anthropic-provider.test.ts +114 -23
- package/src/__tests__/approval-cascade.test.ts +1 -15
- package/src/__tests__/approval-routes-http.test.ts +2 -0
- package/src/__tests__/assistant-feature-flag-guard.test.ts +0 -23
- package/src/__tests__/btw-routes.test.ts +61 -5
- package/src/__tests__/canonical-guardian-store.test.ts +95 -0
- package/src/__tests__/checker.test.ts +13 -0
- package/src/__tests__/config-schema.test.ts +1 -68
- package/src/__tests__/config-watcher.test.ts +8 -0
- package/src/__tests__/context-memory-e2e.test.ts +11 -100
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +8 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/credential-security-e2e.test.ts +1 -0
- package/src/__tests__/credential-security-invariants.test.ts +8 -7
- package/src/__tests__/credential-vault-unit.test.ts +23 -18
- package/src/__tests__/credential-vault.test.ts +30 -18
- package/src/__tests__/credentials-cli.test.ts +257 -82
- package/src/__tests__/cu-unified-flow.test.ts +532 -0
- package/src/__tests__/date-context.test.ts +93 -77
- package/src/__tests__/deterministic-verification-control-plane.test.ts +64 -0
- package/src/__tests__/guardian-routing-invariants.test.ts +93 -0
- package/src/__tests__/history-repair.test.ts +245 -0
- package/src/__tests__/host-cu-proxy.test.ts +165 -3
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/inbound-invite-redemption.test.ts +36 -7
- package/src/__tests__/integration-status.test.ts +31 -30
- package/src/__tests__/invite-redemption-service.test.ts +166 -13
- package/src/__tests__/invite-routes-http.test.ts +166 -5
- package/src/__tests__/keychain-broker-client.test.ts +4 -4
- package/src/__tests__/list-messages-attachments.test.ts +193 -0
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +56 -18
- package/src/__tests__/memory-lifecycle-e2e.test.ts +244 -387
- package/src/__tests__/memory-recall-quality.test.ts +244 -407
- package/src/__tests__/memory-regressions.experimental.test.ts +126 -101
- package/src/__tests__/memory-regressions.test.ts +477 -2841
- package/src/__tests__/memory-retrieval.benchmark.test.ts +33 -150
- package/src/__tests__/memory-upsert-concurrency.test.ts +5 -244
- package/src/__tests__/mime-builder.test.ts +28 -0
- package/src/__tests__/native-web-search.test.ts +1 -0
- package/src/__tests__/oauth-cli.test.ts +824 -31
- package/src/__tests__/oauth-provider-profiles.test.ts +1 -1
- package/src/__tests__/oauth-store.test.ts +363 -17
- package/src/__tests__/qdrant-collection-migration.test.ts +53 -8
- package/src/__tests__/registry.test.ts +0 -1
- package/src/__tests__/relay-server.test.ts +55 -1
- package/src/__tests__/schedule-tools.test.ts +32 -0
- package/src/__tests__/script-proxy-certs.test.ts +1 -1
- package/src/__tests__/secret-onetime-send.test.ts +1 -0
- package/src/__tests__/secret-routes-managed-proxy.test.ts +183 -0
- package/src/__tests__/secure-keys.test.ts +78 -18
- package/src/__tests__/send-endpoint-busy.test.ts +3 -0
- package/src/__tests__/server-history-render.test.ts +2 -2
- package/src/__tests__/session-abort-tool-results.test.ts +1 -14
- package/src/__tests__/session-agent-loop-overflow.test.ts +1583 -0
- package/src/__tests__/session-agent-loop.test.ts +19 -15
- package/src/__tests__/session-confirmation-signals.test.ts +1 -15
- package/src/__tests__/session-error.test.ts +124 -2
- package/src/__tests__/session-history-web-search.test.ts +918 -0
- package/src/__tests__/session-pre-run-repair.test.ts +1 -14
- package/src/__tests__/session-provider-retry-repair.test.ts +25 -28
- package/src/__tests__/session-queue.test.ts +37 -27
- package/src/__tests__/session-runtime-assembly.test.ts +54 -0
- package/src/__tests__/session-slash-known.test.ts +1 -15
- package/src/__tests__/session-slash-queue.test.ts +1 -15
- package/src/__tests__/session-slash-unknown.test.ts +1 -15
- package/src/__tests__/session-workspace-cache-state.test.ts +3 -33
- package/src/__tests__/session-workspace-injection.test.ts +3 -37
- package/src/__tests__/session-workspace-tool-tracking.test.ts +3 -37
- package/src/__tests__/skills-install-extract.test.ts +93 -0
- package/src/__tests__/skills.test.ts +2 -2
- package/src/__tests__/skillssh-registry.test.ts +451 -0
- package/src/__tests__/slack-channel-config.test.ts +10 -8
- package/src/__tests__/trust-store.test.ts +15 -0
- package/src/__tests__/twilio-config.test.ts +11 -10
- package/src/__tests__/twilio-provider.test.ts +9 -4
- package/src/__tests__/voice-invite-redemption.test.ts +85 -5
- package/src/agent/ax-tree-compaction.test.ts +51 -0
- package/src/agent/loop.ts +39 -12
- package/src/approvals/AGENTS.md +1 -1
- package/src/approvals/guardian-request-resolvers.ts +14 -2
- package/src/bundler/compiler-tools.ts +66 -2
- package/src/calls/call-domain.ts +134 -3
- package/src/calls/call-store.ts +6 -0
- package/src/calls/relay-server.ts +44 -6
- package/src/calls/relay-setup-router.ts +17 -1
- package/src/calls/twilio-config.ts +5 -4
- package/src/calls/twilio-provider.ts +14 -9
- package/src/calls/twilio-rest.ts +10 -7
- package/src/calls/types.ts +3 -1
- package/src/cli/commands/config.ts +14 -9
- package/src/cli/commands/contacts.ts +3 -0
- package/src/cli/commands/credentials.ts +170 -174
- package/src/cli/commands/doctor.ts +11 -8
- package/src/cli/commands/keys.ts +9 -9
- package/src/cli/commands/mcp.ts +46 -59
- package/src/cli/commands/memory.ts +16 -165
- package/src/cli/commands/oauth/apps.ts +68 -10
- package/src/cli/commands/oauth/connections.ts +475 -105
- package/src/cli/commands/oauth/index.ts +3 -3
- package/src/cli/commands/oauth/providers.ts +18 -4
- package/src/cli/commands/sessions.ts +5 -2
- package/src/cli/commands/skills.ts +173 -1
- package/src/cli/http-client.ts +0 -20
- package/src/cli/main-screen.tsx +2 -2
- package/src/cli/program.ts +5 -6
- package/src/cli.ts +20 -22
- package/src/config/__tests__/feature-flag-registry-bundled.test.ts +39 -0
- package/src/config/bundled-skills/computer-use/TOOLS.json +1 -1
- package/src/config/bundled-skills/computer-use/tools/computer-use-observe.ts +12 -0
- package/src/config/bundled-skills/contacts/SKILL.md +35 -11
- package/src/config/bundled-skills/contacts/tools/google-contacts.ts +1 -1
- package/src/config/bundled-skills/gmail/SKILL.md +1 -1
- package/src/config/bundled-skills/gmail/TOOLS.json +52 -0
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +13 -3
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +9 -2
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +9 -2
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +5 -1
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +5 -1
- package/src/config/bundled-skills/google-calendar/TOOLS.json +20 -0
- package/src/config/bundled-skills/google-calendar/tools/calendar-check-availability.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-create-event.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-get-event.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-list-events.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/calendar-rsvp.ts +2 -1
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +8 -2
- package/src/config/bundled-skills/messaging/SKILL.md +1 -1
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-auth-test.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-list-conversations.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-mark-read.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-read.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-search.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/shared.ts +7 -5
- package/src/config/bundled-skills/slack/tools/shared.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-add-reaction.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-channel-details.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-delete-message.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-edit-message.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-leave-channel.ts +1 -1
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +1 -1
- package/src/config/bundled-tool-registry.ts +2 -5
- package/src/config/loader.ts +6 -42
- package/src/config/schema.ts +1 -12
- package/src/config/schemas/memory-lifecycle.ts +0 -9
- package/src/config/schemas/memory-processing.ts +0 -180
- package/src/config/schemas/memory-retrieval.ts +32 -104
- package/src/config/schemas/memory.ts +0 -10
- package/src/config/types.ts +0 -4
- package/src/contacts/contact-store.ts +39 -2
- package/src/contacts/contacts-write.ts +9 -0
- package/src/context/window-manager.ts +4 -1
- package/src/daemon/config-watcher.ts +55 -2
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/date-context.ts +114 -31
- package/src/daemon/handlers/config-ingress.ts +2 -2
- package/src/daemon/handlers/config-slack-channel.ts +59 -39
- package/src/daemon/handlers/config-telegram.ts +23 -14
- package/src/daemon/handlers/session-history.ts +1 -358
- package/src/daemon/handlers/sessions.ts +18 -13
- package/src/daemon/handlers/shared.ts +3 -17
- package/src/daemon/handlers/skills.ts +20 -1
- package/src/daemon/history-repair.ts +72 -8
- package/src/daemon/host-cu-proxy.ts +55 -26
- package/src/daemon/lifecycle.ts +39 -4
- package/src/daemon/mcp-reload-service.ts +2 -2
- package/src/daemon/message-types/computer-use.ts +1 -12
- package/src/daemon/message-types/memory.ts +4 -16
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/message-types/sessions.ts +4 -42
- package/src/daemon/server.ts +6 -1
- package/src/daemon/session-agent-loop-handlers.ts +38 -0
- package/src/daemon/session-agent-loop.ts +334 -48
- package/src/daemon/session-error.ts +89 -6
- package/src/daemon/session-history.ts +17 -7
- package/src/daemon/session-media-retry.ts +6 -2
- package/src/daemon/session-memory.ts +69 -149
- package/src/daemon/session-process.ts +10 -1
- package/src/daemon/session-runtime-assembly.ts +49 -19
- package/src/daemon/session-slash.ts +3 -5
- package/src/daemon/session-surfaces.ts +4 -1
- package/src/daemon/session-tool-setup.ts +7 -1
- package/src/daemon/session.ts +12 -2
- package/src/email/providers/index.ts +2 -2
- package/src/instrument.ts +61 -1
- package/src/media/avatar-router.ts +1 -1
- package/src/memory/admin.ts +2 -191
- package/src/memory/canonical-guardian-store.ts +38 -2
- package/src/memory/conversation-crud.ts +0 -33
- package/src/memory/conversation-queries.ts +25 -83
- package/src/memory/db-init.ts +32 -0
- package/src/memory/embedding-backend.ts +84 -8
- package/src/memory/embedding-types.ts +9 -1
- package/src/memory/indexer.ts +7 -46
- package/src/memory/invite-store.ts +19 -0
- package/src/memory/items-extractor.ts +274 -76
- package/src/memory/job-handlers/backfill.ts +2 -127
- package/src/memory/job-handlers/cleanup.ts +2 -16
- package/src/memory/job-handlers/extraction.ts +2 -138
- package/src/memory/job-handlers/index-maintenance.ts +1 -6
- package/src/memory/job-handlers/summarization.ts +3 -148
- package/src/memory/job-utils.ts +21 -59
- package/src/memory/jobs-store.ts +1 -159
- package/src/memory/jobs-worker.ts +9 -52
- package/src/memory/migrations/104-core-indexes.ts +3 -3
- package/src/memory/migrations/149-oauth-tables.ts +2 -0
- package/src/memory/migrations/150-oauth-apps-client-secret-path.ts +98 -0
- package/src/memory/migrations/151-oauth-providers-ping-url.ts +11 -0
- package/src/memory/migrations/152-memory-item-supersession.ts +44 -0
- package/src/memory/migrations/153-drop-entity-tables.ts +15 -0
- package/src/memory/migrations/154-drop-fts.ts +20 -0
- package/src/memory/migrations/155-drop-conflicts.ts +7 -0
- package/src/memory/migrations/156-call-session-invite-metadata.ts +24 -0
- package/src/memory/migrations/157-invite-contact-id.ts +104 -0
- package/src/memory/migrations/index.ts +8 -0
- package/src/memory/migrations/registry.ts +6 -0
- package/src/memory/qdrant-client.ts +148 -51
- package/src/memory/raw-query.ts +1 -1
- package/src/memory/retriever.test.ts +294 -273
- package/src/memory/retriever.ts +421 -645
- package/src/memory/schema/calls.ts +2 -0
- package/src/memory/schema/contacts.ts +1 -0
- package/src/memory/schema/memory-core.ts +3 -48
- package/src/memory/schema/oauth.ts +2 -0
- package/src/memory/search/formatting.ts +263 -176
- package/src/memory/search/lexical.ts +1 -254
- package/src/memory/search/ranking.ts +0 -455
- package/src/memory/search/semantic.ts +100 -14
- package/src/memory/search/staleness.ts +47 -0
- package/src/memory/search/tier-classifier.ts +21 -0
- package/src/memory/search/types.ts +15 -77
- package/src/memory/task-memory-cleanup.ts +4 -6
- package/src/messaging/provider.ts +1 -1
- package/src/messaging/providers/gmail/adapter.ts +1 -1
- package/src/messaging/providers/gmail/mime-builder.ts +17 -7
- package/src/messaging/providers/telegram-bot/adapter.ts +17 -8
- package/src/messaging/providers/whatsapp/adapter.ts +13 -9
- package/src/messaging/registry.ts +9 -5
- package/src/oauth/byo-connection.test.ts +40 -25
- package/src/oauth/connect-orchestrator.ts +4 -10
- package/src/oauth/connection-resolver.ts +20 -6
- package/src/oauth/manual-token-connection.ts +5 -5
- package/src/oauth/oauth-store.ts +183 -31
- package/src/oauth/platform-connection.test.ts +1 -1
- package/src/oauth/provider-behaviors.ts +503 -4
- package/src/oauth/seed-providers.ts +214 -8
- package/src/oauth/token-persistence.ts +31 -16
- package/src/permissions/defaults.ts +1 -0
- package/src/permissions/trust-store.ts +23 -1
- package/src/playbooks/playbook-compiler.ts +1 -1
- package/src/prompts/system-prompt.ts +18 -2
- package/src/providers/anthropic/client.ts +56 -126
- package/src/providers/types.ts +7 -1
- package/src/runtime/AGENTS.md +9 -0
- package/src/runtime/auth/route-policy.ts +6 -3
- package/src/runtime/channel-readiness-service.ts +48 -40
- package/src/runtime/guardian-reply-router.ts +24 -22
- package/src/runtime/http-server.ts +2 -2
- package/src/runtime/http-types.ts +2 -0
- package/src/runtime/invite-redemption-service.ts +72 -12
- package/src/runtime/invite-service.ts +43 -0
- package/src/runtime/middleware/twilio-validation.ts +1 -1
- package/src/runtime/pending-interactions.ts +2 -2
- package/src/runtime/routes/brain-graph-routes.ts +10 -90
- package/src/runtime/routes/btw-routes.ts +10 -5
- package/src/runtime/routes/conversation-routes.ts +56 -11
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +21 -12
- package/src/runtime/routes/integrations/slack/channel.ts +2 -2
- package/src/runtime/routes/integrations/telegram.ts +2 -2
- package/src/runtime/routes/integrations/twilio.ts +17 -17
- package/src/runtime/routes/invite-routes.ts +29 -4
- package/src/runtime/routes/memory-item-routes.test.ts +754 -0
- package/src/runtime/routes/memory-item-routes.ts +503 -0
- package/src/runtime/routes/secret-routes.ts +17 -0
- package/src/runtime/routes/session-management-routes.ts +3 -3
- package/src/runtime/routes/settings-routes.ts +3 -3
- package/src/runtime/routes/trust-rules-routes.ts +14 -0
- package/src/runtime/routes/workspace-routes.ts +9 -4
- package/src/runtime/routes/workspace-utils.ts +8 -2
- package/src/schedule/integration-status.ts +26 -19
- package/src/security/keychain-broker-client.ts +17 -4
- package/src/security/oauth2.ts +6 -7
- package/src/security/secure-keys.ts +44 -19
- package/src/security/token-manager.ts +46 -39
- package/src/services/vercel-deploy.ts +0 -24
- package/src/signals/confirm.ts +78 -0
- package/src/signals/mcp-reload.ts +18 -0
- package/src/skills/catalog-install.ts +74 -18
- package/src/skills/skillssh-registry.ts +503 -0
- package/src/tools/assets/search.ts +5 -1
- package/src/tools/computer-use/definitions.ts +0 -10
- package/src/tools/computer-use/registry.ts +1 -1
- package/src/tools/credentials/vault.ts +22 -7
- package/src/tools/memory/definitions.ts +4 -13
- package/src/tools/memory/handlers.test.ts +83 -103
- package/src/tools/memory/handlers.ts +50 -85
- package/src/tools/network/script-proxy/session-manager.ts +8 -8
- package/src/tools/schedule/create.ts +10 -3
- package/src/tools/schedule/update.ts +8 -1
- package/src/tools/skills/load.ts +25 -2
- package/src/watcher/provider-types.ts +1 -1
- package/src/watcher/providers/github.ts +1 -1
- package/src/watcher/providers/gmail.ts +3 -3
- package/src/watcher/providers/google-calendar.ts +3 -3
- package/src/watcher/providers/linear.ts +1 -1
- package/src/__tests__/clarification-resolver.test.ts +0 -193
- package/src/__tests__/conflict-intent-tokenization.test.ts +0 -160
- package/src/__tests__/conflict-policy.test.ts +0 -269
- package/src/__tests__/conflict-store.test.ts +0 -372
- package/src/__tests__/contradiction-checker.test.ts +0 -361
- package/src/__tests__/entity-extractor.test.ts +0 -211
- package/src/__tests__/entity-search.test.ts +0 -1117
- package/src/__tests__/profile-compiler.test.ts +0 -392
- package/src/__tests__/session-conflict-gate.test.ts +0 -1228
- package/src/__tests__/session-profile-injection.test.ts +0 -557
- package/src/config/bundled-skills/knowledge-graph/SKILL.md +0 -25
- package/src/config/bundled-skills/knowledge-graph/TOOLS.json +0 -66
- package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +0 -211
- package/src/daemon/session-conflict-gate.ts +0 -167
- package/src/daemon/session-dynamic-profile.ts +0 -77
- package/src/memory/clarification-resolver.ts +0 -417
- package/src/memory/conflict-intent.ts +0 -205
- package/src/memory/conflict-policy.ts +0 -127
- package/src/memory/conflict-store.ts +0 -410
- package/src/memory/contradiction-checker.ts +0 -508
- package/src/memory/entity-extractor.ts +0 -535
- package/src/memory/format-recall.ts +0 -47
- package/src/memory/fts-reconciler.ts +0 -165
- package/src/memory/job-handlers/conflict.ts +0 -200
- package/src/memory/profile-compiler.ts +0 -195
- package/src/memory/recall-cache.ts +0 -117
- package/src/memory/search/entity.ts +0 -535
- package/src/memory/search/query-expansion.test.ts +0 -70
- package/src/memory/search/query-expansion.ts +0 -118
- package/src/runtime/routes/mcp-routes.ts +0 -20
|
@@ -12,52 +12,51 @@ graph TB
|
|
|
12
12
|
INDEX["Memory Indexer"]
|
|
13
13
|
SEGMENT["Split into segments<br/>→ memory_segments"]
|
|
14
14
|
EXTRACT_JOB["Enqueue extract_items job<br/>→ memory_jobs"]
|
|
15
|
-
CONFLICT_RESOLVE_JOB["Enqueue resolve_pending_conflicts_for_message<br/>(dedupe by type+message+scope)<br/>→ memory_jobs"]
|
|
16
15
|
SUMMARY_JOB["Enqueue build_conversation_summary<br/>→ memory_jobs"]
|
|
17
16
|
end
|
|
18
17
|
|
|
19
18
|
subgraph "Background Worker (polls every 1.5s)"
|
|
20
19
|
WORKER["MemoryJobsWorker"]
|
|
21
|
-
EMBED_SEG["embed_segment<br/>→
|
|
22
|
-
EMBED_ITEM["embed_item<br/>→
|
|
23
|
-
EMBED_SUM["embed_summary<br/>→
|
|
24
|
-
EXTRACT["extract_items<br/>→ memory_items +<br/>memory_item_sources"]
|
|
25
|
-
|
|
26
|
-
RESOLVE_PENDING["resolve_pending_conflicts_for_message<br/>message-scoped clarification resolution<br/>→ resolved conflict + item status updates"]
|
|
27
|
-
CLEAN_CONFLICTS["cleanup_resolved_conflicts<br/>delete resolved conflict rows<br/>older than retention window"]
|
|
28
|
-
CLEAN_SUPERSEDED["cleanup_stale_superseded_items<br/>delete stale superseded items<br/>and item embedding rows"]
|
|
29
|
-
EXTRACT_ENTITIES["extract_entities<br/>→ memory_entities +<br/>memory_item_entities +<br/>memory_entity_relations"]
|
|
30
|
-
BACKFILL_REL["backfill_entity_relations<br/>checkpointed message scan<br/>→ enqueue extract_entities"]
|
|
20
|
+
EMBED_SEG["embed_segment<br/>→ Qdrant (dense + sparse)"]
|
|
21
|
+
EMBED_ITEM["embed_item<br/>→ Qdrant (dense + sparse)"]
|
|
22
|
+
EMBED_SUM["embed_summary<br/>→ Qdrant (dense + sparse)"]
|
|
23
|
+
EXTRACT["extract_items<br/>→ memory_items +<br/>memory_item_sources<br/>(LLM-directed supersession)"]
|
|
24
|
+
CLEAN_SUPERSEDED["cleanup_stale_superseded_items<br/>delete stale superseded items<br/>and Qdrant vectors"]
|
|
31
25
|
BUILD_SUM["build_conversation_summary<br/>→ memory_summaries"]
|
|
32
|
-
WEEKLY["refresh_weekly_summary<br/>→ memory_summaries"]
|
|
33
26
|
end
|
|
34
27
|
|
|
35
|
-
subgraph "Embedding
|
|
28
|
+
subgraph "Embedding Provider Selection (selectEmbeddingBackend)"
|
|
29
|
+
PROVIDER_SELECT["Provider Selection<br/>auto: local → OpenAI → Gemini → Ollama<br/>or explicit config override"]
|
|
36
30
|
LOCAL_EMB["Local (ONNX)<br/>bge-small-en-v1.5"]
|
|
37
31
|
OAI_EMB["OpenAI<br/>text-embedding-3-small"]
|
|
38
32
|
GEM_EMB["Gemini<br/>gemini-embedding-001"]
|
|
39
33
|
OLL_EMB["Ollama<br/>nomic-embed-text"]
|
|
40
34
|
end
|
|
41
35
|
|
|
36
|
+
subgraph "Sparse Embedding (in-process)"
|
|
37
|
+
SPARSE_GEN["generateSparseEmbedding()<br/>TF-IDF, FNV-1a hashing<br/>(no external calls)"]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
subgraph "Qdrant Vector Store"
|
|
41
|
+
DENSE["Named vector: dense<br/>(cosine similarity)"]
|
|
42
|
+
SPARSE["Named vector: sparse<br/>(TF-IDF based)"]
|
|
43
|
+
RRF["Query API:<br/>Reciprocal Rank Fusion"]
|
|
44
|
+
end
|
|
45
|
+
|
|
42
46
|
subgraph "Read Path (Memory Recall)"
|
|
47
|
+
NEEDS_MEM["needsMemory gate<br/>(skip short/empty/tool-result turns)"]
|
|
43
48
|
QUERY["Recall Query Builder<br/>User request + compacted context summary"]
|
|
44
|
-
CONFLICT_GATE["Soft Conflict Gate<br/>dismiss non-actionable conflicts (kind + statement + provenance policy)<br/>attempt internal resolution from user turn<br/>relevance-based; never produces user-facing prompts"]
|
|
45
|
-
PROFILE_BUILD["Dynamic Profile Compiler<br/>active trusted profile memories<br/>user_confirmed > user_reported > assistant_inferred"]
|
|
46
|
-
PROFILE_INJECT["Inject profile context block<br/>into runtime user tail<br/>(strict token cap)"]
|
|
47
49
|
BUDGET["Dynamic Recall Budget<br/>computeRecallBudget()<br/>from prompt headroom"]
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
DIRECT["Direct Item Search<br/>LIKE on subject/statement"]
|
|
50
|
+
EMBED_Q["Generate dense + sparse<br/>query embeddings"]
|
|
51
|
+
HYBRID["Hybrid Search<br/>dense + sparse RRF on Qdrant"]
|
|
52
|
+
RECENCY["Recency Search<br/>conversation-scoped, DB only"]
|
|
53
|
+
MERGE["Merge + Deduplicate<br/>weighted score combination"]
|
|
53
54
|
SCOPE["Scope Filter<br/>scope_id filtering<br/>(strict | global_fallback)<br/>Private threads: own scope + 'default'"]
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
TELEMETRY["Emit memory_recalled<br/>hits + relation counters +<br/>ranking diagnostics"]
|
|
60
|
-
STRIP_PROFILE["Strip injected dynamic profile block<br/>before persisting conversation history"]
|
|
55
|
+
TIER["Tier Classification<br/>score > 0.8 → tier 1<br/>score > 0.6 → tier 2<br/>below → dropped"]
|
|
56
|
+
STALE["Staleness Computation<br/>kind-specific lifetimes<br/>+ reinforcement from<br/>source conversation count"]
|
|
57
|
+
DEMOTE["Stale Demotion<br/>very_stale tier 1 → tier 2"]
|
|
58
|
+
INJECT["Two-Layer XML Injection<br/>budget-aware rendering"]
|
|
59
|
+
TELEMETRY["Emit memory_recalled<br/>tier counts + hybrid search ms +<br/>staleness stats"]
|
|
61
60
|
end
|
|
62
61
|
|
|
63
62
|
subgraph "Context Window Management"
|
|
@@ -83,49 +82,47 @@ graph TB
|
|
|
83
82
|
STORE --> INDEX
|
|
84
83
|
INDEX --> SEGMENT
|
|
85
84
|
INDEX --> EXTRACT_JOB
|
|
86
|
-
INDEX --> CONFLICT_RESOLVE_JOB
|
|
87
85
|
INDEX --> SUMMARY_JOB
|
|
88
86
|
|
|
89
87
|
WORKER --> EMBED_SEG
|
|
90
88
|
WORKER --> EMBED_ITEM
|
|
91
89
|
WORKER --> EMBED_SUM
|
|
92
90
|
WORKER --> EXTRACT
|
|
93
|
-
WORKER --> CHECK_CONTRA
|
|
94
|
-
WORKER --> RESOLVE_PENDING
|
|
95
|
-
WORKER --> CLEAN_CONFLICTS
|
|
96
91
|
WORKER --> CLEAN_SUPERSEDED
|
|
97
|
-
WORKER --> EXTRACT_ENTITIES
|
|
98
|
-
WORKER --> BACKFILL_REL
|
|
99
92
|
WORKER --> BUILD_SUM
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
93
|
+
|
|
94
|
+
EMBED_SEG --> PROVIDER_SELECT
|
|
95
|
+
EMBED_ITEM --> PROVIDER_SELECT
|
|
96
|
+
EMBED_SUM --> PROVIDER_SELECT
|
|
97
|
+
PROVIDER_SELECT --> LOCAL_EMB
|
|
98
|
+
PROVIDER_SELECT --> OAI_EMB
|
|
99
|
+
PROVIDER_SELECT --> GEM_EMB
|
|
100
|
+
PROVIDER_SELECT --> OLL_EMB
|
|
101
|
+
LOCAL_EMB --> DENSE
|
|
102
|
+
OAI_EMB --> DENSE
|
|
103
|
+
GEM_EMB --> DENSE
|
|
104
|
+
OLL_EMB --> DENSE
|
|
105
|
+
EMBED_SEG --> SPARSE_GEN
|
|
106
|
+
EMBED_ITEM --> SPARSE_GEN
|
|
107
|
+
EMBED_SUM --> SPARSE_GEN
|
|
108
|
+
SPARSE_GEN --> SPARSE
|
|
109
|
+
|
|
110
|
+
NEEDS_MEM --> QUERY
|
|
111
|
+
QUERY --> EMBED_Q
|
|
112
|
+
EMBED_Q --> PROVIDER_SELECT
|
|
113
|
+
EMBED_Q --> SPARSE_GEN
|
|
114
|
+
EMBED_Q --> HYBRID
|
|
115
|
+
HYBRID --> RRF
|
|
116
|
+
QUERY --> RECENCY
|
|
117
|
+
HYBRID --> SCOPE
|
|
118
|
+
RECENCY --> SCOPE
|
|
120
119
|
SCOPE --> MERGE
|
|
121
|
-
MERGE -->
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
BUDGET -->
|
|
125
|
-
|
|
126
|
-
PROFILE_INJECT --> INJECT
|
|
120
|
+
MERGE --> TIER
|
|
121
|
+
TIER --> STALE
|
|
122
|
+
STALE --> DEMOTE
|
|
123
|
+
BUDGET --> INJECT
|
|
124
|
+
DEMOTE --> INJECT
|
|
127
125
|
INJECT --> TELEMETRY
|
|
128
|
-
INJECT --> STRIP_PROFILE
|
|
129
126
|
|
|
130
127
|
CTX --> COMPACT
|
|
131
128
|
COMPACT --> GUARDS
|
|
@@ -158,92 +155,159 @@ The key distinction: normal compaction is a cost-optimized background process th
|
|
|
158
155
|
|
|
159
156
|
### Memory Retrieval Config Knobs (Defaults)
|
|
160
157
|
|
|
161
|
-
| Config key
|
|
162
|
-
|
|
|
163
|
-
| `memory.retrieval.dynamicBudget.enabled`
|
|
164
|
-
| `memory.retrieval.dynamicBudget.minInjectTokens`
|
|
165
|
-
| `memory.retrieval.dynamicBudget.maxInjectTokens`
|
|
166
|
-
| `memory.retrieval.dynamicBudget.targetHeadroomTokens`
|
|
167
|
-
| `memory.
|
|
168
|
-
| `memory.
|
|
169
|
-
| `memory.entity.relationRetrieval.enabled` | `true` | Enable one-hop relation expansion from matched seed entities at recall time. |
|
|
170
|
-
| `memory.entity.relationRetrieval.maxSeedEntities` | `8` | Maximum matched seed entities from the query. |
|
|
171
|
-
| `memory.entity.relationRetrieval.maxNeighborEntities` | `20` | Maximum unique neighbor entities expanded from relation edges. |
|
|
172
|
-
| `memory.entity.relationRetrieval.maxEdges` | `40` | Maximum relation edges traversed during expansion. |
|
|
173
|
-
| `memory.entity.relationRetrieval.neighborScoreMultiplier` | `0.7` | Downweight multiplier for relation-expanded candidates vs direct entity hits. |
|
|
174
|
-
| `memory.conflicts.enabled` | `true` | Enable soft conflict gate for unresolved `memory_item_conflicts`. |
|
|
175
|
-
| `memory.conflicts.resolverLlmTimeoutMs` | `12000` | Timeout bound for clarification resolver LLM fallback. |
|
|
176
|
-
| `memory.conflicts.relevanceThreshold` | `0.3` | Similarity threshold for deciding whether a pending conflict is relevant to the current request. |
|
|
177
|
-
| `memory.conflicts.gateMode` | `'soft'` | Conflict gate strategy. Currently only `'soft'` is supported (resolves conflicts internally without user prompts). |
|
|
178
|
-
| `memory.conflicts.conflictableKinds` | `['preference', 'profile', 'constraint', 'instruction', 'style']` | Memory item kinds eligible for conflict detection. Items with kinds outside this list are auto-dismissed. |
|
|
179
|
-
| `memory.profile.enabled` | `true` | Enable dynamic profile compilation from active trusted profile/preference/constraint/instruction memories. |
|
|
180
|
-
| `memory.profile.maxInjectTokens` | `800` | Hard token cap enforced by `ProfileCompiler` when generating the runtime profile block. |
|
|
158
|
+
| Config key | Default | Purpose |
|
|
159
|
+
| ----------------------------------------------------- | ------------------------: | -------------------------------------------------------------------- |
|
|
160
|
+
| `memory.retrieval.dynamicBudget.enabled` | `true` | Toggle per-turn recall budget calculation from live prompt headroom. |
|
|
161
|
+
| `memory.retrieval.dynamicBudget.minInjectTokens` | `1200` | Lower clamp for computed recall injection budget. |
|
|
162
|
+
| `memory.retrieval.dynamicBudget.maxInjectTokens` | `10000` | Upper clamp for computed recall injection budget. |
|
|
163
|
+
| `memory.retrieval.dynamicBudget.targetHeadroomTokens` | `10000` | Reserved headroom to keep free for response generation/tool traces. |
|
|
164
|
+
| `memory.retrieval.maxInjectTokens` | `10000` | Static fallback when dynamic budget is disabled. |
|
|
165
|
+
| `memory.retrieval.scopePolicy` | `'allow_global_fallback'` | Scope filtering strategy: `'strict'` or `'allow_global_fallback'`. |
|
|
181
166
|
|
|
182
167
|
### Memory Recall Debugging Playbook
|
|
183
168
|
|
|
184
169
|
1. Run a recall-heavy turn and inspect `memory_recalled` events in the client trace stream.
|
|
185
170
|
2. Validate baseline counters:
|
|
186
|
-
- `
|
|
187
|
-
- `
|
|
171
|
+
- `semanticHits`, `recencyHits`
|
|
172
|
+
- `tier1Count`, `tier2Count`
|
|
173
|
+
- `hybridSearchLatencyMs`
|
|
188
174
|
- `mergedCount`, `selectedCount`, `injectedTokens`, `latencyMs`
|
|
189
175
|
3. Cross-check context pressure with `context_compacted` events:
|
|
190
176
|
- `previousEstimatedInputTokens` vs `estimatedInputTokens`
|
|
191
177
|
- `summaryCalls`, `compactedMessages`
|
|
192
178
|
4. If dynamic budget is enabled, verify `injectedTokens` stays within the configured min/max clamps for `dynamicBudget`.
|
|
193
|
-
5.
|
|
194
|
-
- `
|
|
195
|
-
-
|
|
196
|
-
6. Before tuning ranking
|
|
179
|
+
5. Inspect staleness distribution in debug logs:
|
|
180
|
+
- `fresh`, `aging`, `stale`, `very_stale` counts
|
|
181
|
+
- Check for unexpected tier demotions (very_stale tier 1 items demoted to tier 2)
|
|
182
|
+
6. Before tuning ranking settings, run:
|
|
197
183
|
- `cd assistant && bun test src/__tests__/context-memory-e2e.test.ts`
|
|
198
184
|
- `cd assistant && bun test src/__tests__/memory-context-benchmark.benchmark.test.ts`
|
|
199
185
|
- `cd assistant && bun test src/__tests__/memory-recall-quality.test.ts`
|
|
200
|
-
- `cd assistant && bun test src/__tests__/memory-regressions.test.ts -t "relation"`
|
|
201
186
|
7. After tuning, rerun the same suite and compare:
|
|
202
|
-
-
|
|
187
|
+
- tier counts (coverage)
|
|
203
188
|
- selected count / injected tokens (budget safety)
|
|
204
189
|
- latency and ordering regressions via top candidate snapshots
|
|
205
190
|
|
|
206
|
-
###
|
|
191
|
+
### Write Path — Extraction and Supersession
|
|
207
192
|
|
|
208
193
|
```mermaid
|
|
209
194
|
stateDiagram-v2
|
|
210
|
-
[*] -->
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
PendingConflict --> ResolvedKeepCandidate : clarification resolver\n+ applyConflictResolution
|
|
216
|
-
PendingConflict --> ResolvedMerge : clarification resolver\n+ applyConflictResolution
|
|
217
|
-
ResolvedKeepExisting --> CleanupConflicts : cleanup_resolved_conflicts
|
|
218
|
-
ResolvedKeepCandidate --> CleanupConflicts : cleanup_resolved_conflicts
|
|
219
|
-
ResolvedMerge --> CleanupConflicts : cleanup_resolved_conflicts
|
|
220
|
-
ResolvedKeepExisting --> SupersededItems : candidate superseded
|
|
221
|
-
ResolvedMerge --> SupersededItems : merged-from candidate superseded
|
|
222
|
-
SupersededItems --> CleanupItems : cleanup_stale_superseded_items
|
|
195
|
+
[*] --> ActiveItem : extract_items\n(LLM or pattern-based)
|
|
196
|
+
ActiveItem --> Superseded : explicit supersession\n(overrideConfidence = "explicit"\n+ supersedes = oldItemId)
|
|
197
|
+
ActiveItem --> ActiveItem : tentative/inferred override\n(both items coexist)
|
|
198
|
+
ActiveItem --> Superseded : subject-match fallback\n(same kind + subject,\nno LLM-directed supersession)
|
|
199
|
+
Superseded --> Cleanup : cleanup_stale_superseded_items\n(delete from DB + Qdrant)
|
|
223
200
|
```
|
|
224
201
|
|
|
225
|
-
|
|
202
|
+
**Item extraction** uses LLM-powered extraction (with pattern-based fallback) to identify memorable information from conversation messages. Each extracted item belongs to one of six kinds:
|
|
203
|
+
|
|
204
|
+
| Kind | Description | Base Lifetime |
|
|
205
|
+
| ------------ | ------------------------------------------------- | ------------- |
|
|
206
|
+
| `identity` | Personal info, facts, relationships | 6 months |
|
|
207
|
+
| `preference` | Likes, dislikes, preferred approaches/tools | 3 months |
|
|
208
|
+
| `constraint` | Rules, requirements, directives | 1 month |
|
|
209
|
+
| `project` | Project details, repos, tech stacks, action items | 2 weeks |
|
|
210
|
+
| `decision` | Choices made, approaches selected | 2 weeks |
|
|
211
|
+
| `event` | Deadlines, milestones, meetings, dates | 3 days |
|
|
212
|
+
|
|
213
|
+
**Supersession chains** replace the old conflict resolution system. When the LLM extracts a new item that updates an existing one, it sets `supersedes` to the old item's ID and `overrideConfidence` to one of three levels:
|
|
214
|
+
|
|
215
|
+
- `explicit` — Clear override signal (e.g. "I changed my mind about X"). The old item is marked `superseded` and removed from Qdrant.
|
|
216
|
+
- `tentative` — Ambiguous; both items coexist as active.
|
|
217
|
+
- `inferred` — Weak signal; both items coexist (logged for observability).
|
|
218
|
+
|
|
219
|
+
A fallback subject-match supersession also runs for items without LLM-directed supersession: same kind + same subject = old item superseded.
|
|
220
|
+
|
|
221
|
+
**Semantic density gating** skips extraction for messages that are too short, consist of low-value filler (e.g. "ok", "thanks", "got it"), or have fewer than 3 words.
|
|
222
|
+
|
|
223
|
+
### Read Path — Hybrid Recall Pipeline
|
|
224
|
+
|
|
225
|
+
The recall pipeline runs on every turn that passes the `needsMemory` gate (skips empty, very short, and tool-result-only turns). The pipeline is orchestrated by `buildMemoryRecall()` in `retriever.ts`:
|
|
226
|
+
|
|
227
|
+
1. **Query construction** (`query-builder.ts`): Combines the user request text (up to 2000 chars) with any in-context session summary (up to 1200 chars).
|
|
228
|
+
|
|
229
|
+
2. **Dense + sparse embedding generation**: The query is embedded using the configured embedding provider (auto-selection order: local → OpenAI → Gemini → Ollama). A TF-IDF sparse embedding is also generated in-process using FNV-1a hashing to a 30K vocabulary with sub-linear TF weighting and L2 normalization.
|
|
230
|
+
|
|
231
|
+
3. **Hybrid search on Qdrant**: When both dense and sparse vectors are available, the pipeline uses Qdrant's query API with two prefetch stages (dense and sparse, each fetching up to 40 candidates) fused via Reciprocal Rank Fusion (RRF). Falls back to dense-only search when sparse vectors are unavailable.
|
|
232
|
+
|
|
233
|
+
4. **Recency supplement**: A DB-only recency search fetches the 5 most recent segments from the current conversation, providing conversation-local context even when vector search misses.
|
|
234
|
+
|
|
235
|
+
5. **Merge and deduplicate**: Hybrid and recency candidates are merged by key. Duplicate entries keep the highest scores from each source. A weighted final score is computed: `semantic * 0.7 + recency * 0.2 + confidence * 0.1`.
|
|
226
236
|
|
|
227
|
-
|
|
237
|
+
6. **Tier classification** (`tier-classifier.ts`): Score-based, deterministic classification:
|
|
238
|
+
- `finalScore > 0.8` → **tier 1** (high relevance)
|
|
239
|
+
- `finalScore > 0.6` → **tier 2** (possibly relevant)
|
|
240
|
+
- Below 0.6 → dropped
|
|
228
241
|
|
|
229
|
-
|
|
242
|
+
7. **Staleness computation** (`staleness.ts`): Each item candidate is annotated with a staleness level based on its age relative to a kind-specific base lifetime (see table above). The effective lifetime is extended by a reinforcement factor: `baseLifetime * (1 + 0.3 * (sourceConversationCount - 1))`, so items mentioned across multiple conversations age more slowly. Staleness levels:
|
|
243
|
+
- `ratio < 0.5` → `fresh`
|
|
244
|
+
- `ratio <= 1.0` → `aging`
|
|
245
|
+
- `ratio <= 2.0` → `stale`
|
|
246
|
+
- `ratio > 2.0` → `very_stale`
|
|
230
247
|
|
|
231
|
-
|
|
232
|
-
2. Session injects that block only into runtime prompt state.
|
|
233
|
-
3. Session strips the injected profile block before persisting conversation history, so dynamic profile context never pollutes durable message rows.
|
|
248
|
+
8. **Stale demotion**: `very_stale` tier 1 candidates are demoted to tier 2, preventing old information from occupying prime injection space.
|
|
234
249
|
|
|
235
|
-
|
|
250
|
+
9. **Two-layer XML injection** (`formatting.ts`): Budget-aware rendering into four XML sections:
|
|
236
251
|
|
|
237
|
-
|
|
252
|
+
```xml
|
|
253
|
+
<memory_context>
|
|
254
|
+
|
|
255
|
+
<user_identity>
|
|
256
|
+
<!-- identity-kind tier 1 items (plain statements) -->
|
|
257
|
+
</user_identity>
|
|
258
|
+
|
|
259
|
+
<relevant_context>
|
|
260
|
+
<!-- tier 1 non-identity/non-preference items (episode-wrapped with source attribution) -->
|
|
261
|
+
</relevant_context>
|
|
262
|
+
|
|
263
|
+
<applicable_preferences>
|
|
264
|
+
<!-- preference/constraint tier 1 items (plain statements) -->
|
|
265
|
+
</applicable_preferences>
|
|
266
|
+
|
|
267
|
+
<possibly_relevant>
|
|
268
|
+
<!-- tier 2 items (episode-wrapped with staleness annotations) -->
|
|
269
|
+
</possibly_relevant>
|
|
270
|
+
|
|
271
|
+
</memory_context>
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Empty sections are omitted. Each section has a per-item token budget (150 tokens for tier 1, 100 for tier 2). Tier 1 sections consume budget first; tier 2 uses the remainder.
|
|
275
|
+
|
|
276
|
+
10. **Injection strategy**: The rendered `<memory_context>` block is injected as a separate user + assistant acknowledgment message pair before the last user message (`injectMemoryRecallAsSeparateMessage`). This separates memory context from the user's actual query.
|
|
277
|
+
|
|
278
|
+
### Internal-Only Trust Gating
|
|
279
|
+
|
|
280
|
+
**Provenance-aware pipeline**: Every persisted message carries provenance metadata (`provenanceTrustClass`, `provenanceSourceChannel`, etc.) derived from the `TrustContext` resolved by `trust-context-resolver.ts`.
|
|
238
281
|
|
|
239
282
|
Two trust gates enforce trust-class-based access control over the memory pipeline:
|
|
240
283
|
|
|
241
|
-
- **Write gate** (`indexer.ts`): The `extract_items`
|
|
284
|
+
- **Write gate** (`indexer.ts`): The `extract_items` job only runs for messages from trusted actors (guardian or undefined provenance). Messages from untrusted actors (`trusted_contact`, `unknown`) are still segmented and embedded — so they appear in conversation context — but no item extraction is triggered. This prevents untrusted channels from injecting or mutating long-term memory items.
|
|
242
285
|
|
|
243
|
-
- **Read gate** (`session-memory.ts`): When the current session's actor is untrusted, the memory recall pipeline returns a no-op context — no recall injection
|
|
286
|
+
- **Read gate** (`session-memory.ts`): When the current session's actor is untrusted, the memory recall pipeline returns a no-op context — no recall injection. This ensures untrusted actors cannot surface or exploit previously extracted memory.
|
|
244
287
|
|
|
245
288
|
Trust policy is **cross-channel and trust-class-based**: decisions use `trustContext.trustClass`, not the channel string. Desktop sessions default to `trustClass: 'guardian'`. External channels (Telegram, WhatsApp, phone) provide explicit trust context via the resolver. Messages without provenance metadata are treated as trusted (guardian); all new messages carry provenance.
|
|
246
289
|
|
|
290
|
+
### Embedding Backend Selection
|
|
291
|
+
|
|
292
|
+
The embedding backend is selected based on `memory.embeddings.provider` config:
|
|
293
|
+
|
|
294
|
+
- `auto` (default): Tries local → OpenAI → Gemini → Ollama, using the first available.
|
|
295
|
+
- `local`: ONNX-based local model (bge-small-en-v1.5). Lazy-loaded to avoid crashing in compiled binaries where onnxruntime-node is unavailable.
|
|
296
|
+
- `openai`: OpenAI text-embedding-3-small. Requires `apiKeys.openai`.
|
|
297
|
+
- `gemini`: Gemini gemini-embedding-001. Requires `apiKeys.gemini`. Only backend supporting multimodal embeddings (images, audio, video).
|
|
298
|
+
- `ollama`: Ollama nomic-embed-text. Requires Ollama to be configured.
|
|
299
|
+
|
|
300
|
+
An in-memory LRU vector cache (32 MB cap, keyed by `sha256(provider + model + content)`) avoids redundant embedding calls for identical content. Sparse embeddings are generated in-process (no external calls).
|
|
301
|
+
|
|
302
|
+
### Graceful Degradation
|
|
303
|
+
|
|
304
|
+
When the embedding backend or Qdrant is unavailable:
|
|
305
|
+
|
|
306
|
+
- A **circuit breaker** on Qdrant (`qdrant-circuit-breaker.ts`) tracks consecutive failures and short-circuits search calls when the breaker is open.
|
|
307
|
+
- If embedding generation fails and `memory.embeddings.required` is `true`, recall returns an empty result with a degradation status (`embedding_generation_failed` or `embedding_provider_down`).
|
|
308
|
+
- If embeddings are optional (default), the pipeline falls back to recency-only search.
|
|
309
|
+
- Degradation status is reported to clients via `memory_status` events.
|
|
310
|
+
|
|
247
311
|
---
|
|
248
312
|
|
|
249
313
|
## Private Threads — Isolated Memory and Strict Side-Effect Controls
|
|
@@ -289,8 +353,6 @@ graph TB
|
|
|
289
353
|
|
|
290
354
|
**Read fallback**: When recalling memories for a private thread, the retriever queries both the thread's own scope and the `'default'` scope. This ensures the assistant still has access to general knowledge (user profile, preferences, facts) learned in standard threads, while private-thread-specific memories take precedence in ranking. The fallback is implemented via `ScopePolicyOverride` with `fallbackToDefault: true`, which overrides the global scope policy on a per-call basis.
|
|
291
355
|
|
|
292
|
-
**Profile compilation**: The `ProfileCompiler` also respects this dual-scope behavior for private threads — it includes profile/preference/constraint items from both the private scope and the default scope when building the runtime profile block.
|
|
293
|
-
|
|
294
356
|
### SessionMemoryPolicy
|
|
295
357
|
|
|
296
358
|
The daemon derives a `SessionMemoryPolicy` from the conversation's `thread_type` and `memory_scope_id` when creating or restoring a session:
|
|
@@ -333,8 +395,7 @@ This ensures that file writes, bash commands, host operations, and other mutatin
|
|
|
333
395
|
| `assistant/src/tools/executor.ts` | `forcePromptSideEffects` gate — promotes allow to prompt for side-effect tools |
|
|
334
396
|
| `assistant/src/memory/search/types.ts` | `ScopePolicyOverride` interface for per-call scope control |
|
|
335
397
|
| `assistant/src/memory/retriever.ts` | `buildScopeFilter()` — builds scope ID list from override or global config |
|
|
336
|
-
| `assistant/src/
|
|
337
|
-
| `assistant/src/daemon/session-memory.ts` | Wires `scopeId` and `includeDefaultFallback` into recall and profile compilation |
|
|
398
|
+
| `assistant/src/daemon/session-memory.ts` | Wires `scopeId` and `includeDefaultFallback` into recall |
|
|
338
399
|
|
|
339
400
|
---
|
|
340
401
|
|
|
@@ -387,7 +448,7 @@ graph TB
|
|
|
387
448
|
|
|
388
449
|
### Cache compatibility
|
|
389
450
|
|
|
390
|
-
The Anthropic provider places `cache_control: { type: 'ephemeral' }` on the **last content block** of the last two user turns. Since workspace context is prepended (first block), the cache breakpoint correctly lands on the trailing user text
|
|
451
|
+
The Anthropic provider places `cache_control: { type: 'ephemeral' }` on the **last content block** of the last two user turns. Since workspace context is prepended (first block), the cache breakpoint correctly lands on the trailing user text block. This is validated by dedicated cache-compatibility tests.
|
|
391
452
|
|
|
392
453
|
### Key files
|
|
393
454
|
|
|
@@ -425,7 +486,7 @@ graph TB
|
|
|
425
486
|
|
|
426
487
|
- **Fresh each turn**: `buildTemporalContext()` is called at the start of every agent loop invocation, ensuring the model always sees the current date even in long-running conversations.
|
|
427
488
|
- **Clock source invariant**: Absolute time (`now`) always comes from the assistant host clock (`Date.now()`), never from channel/client clocks.
|
|
428
|
-
- **Timezone precedence**: If `ui.userTimezone` is configured, temporal context uses it for local-date interpretation. Otherwise it falls back to
|
|
489
|
+
- **Timezone precedence**: If `ui.userTimezone` is configured, temporal context uses it for local-date interpretation. Otherwise it falls back to memory-stored timezone, then assistant host timezone.
|
|
429
490
|
- **Timezone-aware**: Uses `Intl.DateTimeFormat` APIs for DST-safe date arithmetic and timezone validation/canonicalization.
|
|
430
491
|
- **Bounded output**: Hard-capped at 1500 characters and 14 horizon entries to prevent prompt bloat.
|
|
431
492
|
- **Runtime-only**: The injected `<temporal_context>` block is stripped from `this.messages` after the agent loop completes via `stripTemporalContext`. It never persists in conversation history.
|
package/knip.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"entry": ["src/**/*.test.ts", "src/**/__tests__/**/*.ts", "scripts/**/*.ts"],
|
|
3
|
+
"project": ["src/**/*.ts", "src/**/*.tsx", "scripts/**/*.ts"],
|
|
4
|
+
"ignore": [
|
|
5
|
+
"src/browser-extension-relay/client.ts",
|
|
6
|
+
"src/contacts/index.ts",
|
|
7
|
+
"src/daemon/main.ts",
|
|
8
|
+
"src/daemon/tls-certs.ts",
|
|
9
|
+
"src/errors.ts",
|
|
10
|
+
"src/events/index.ts",
|
|
11
|
+
"src/followups/index.ts",
|
|
12
|
+
"src/playbooks/index.ts",
|
|
13
|
+
"src/runtime/auth/index.ts",
|
|
14
|
+
"src/tasks/candidate-store.ts",
|
|
15
|
+
"src/tools/browser/api-map.ts",
|
|
16
|
+
"src/tools/browser/auto-navigate.ts",
|
|
17
|
+
"src/tools/browser/headless-browser.ts",
|
|
18
|
+
"src/tools/browser/recording-store.ts",
|
|
19
|
+
"src/tools/tasks/index.ts"
|
|
20
|
+
],
|
|
21
|
+
"ignoreDependencies": [
|
|
22
|
+
"@hono/node-server",
|
|
23
|
+
"@vellumai/cli",
|
|
24
|
+
"esbuild",
|
|
25
|
+
"hono",
|
|
26
|
+
"ink",
|
|
27
|
+
"preact",
|
|
28
|
+
"quicktype-core",
|
|
29
|
+
"tree-sitter-bash",
|
|
30
|
+
"typescript-json-schema"
|
|
31
|
+
]
|
|
32
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vellumai/assistant",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.51",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": "./src/index.ts"
|
|
@@ -16,13 +16,14 @@
|
|
|
16
16
|
"format": "prettier --write .",
|
|
17
17
|
"format:check": "prettier --check .",
|
|
18
18
|
"lint": "eslint",
|
|
19
|
+
"lint:unused": "knip --include files,dependencies,unlisted",
|
|
19
20
|
"typecheck": "bunx tsc --noEmit",
|
|
20
21
|
"test": "bash scripts/test.sh",
|
|
21
22
|
"test:coverage": "COVERAGE=true bash scripts/test.sh",
|
|
22
23
|
"test:stable": "EXCLUDE_EXPERIMENTAL=true bash scripts/test.sh",
|
|
23
24
|
"test:bench": "find src/__tests__ -maxdepth 1 -type f -name '*.benchmark.test.ts' -print0 | xargs -0 -P 1 -I {} bun test {}",
|
|
24
25
|
"test:filesystem-tools": "bash scripts/test-filesystem-tools.sh",
|
|
25
|
-
"postinstall": "cd .. && git config core.hooksPath || git config core.hooksPath .githooks 2>/dev/null || true"
|
|
26
|
+
"postinstall": "cd .. && (git config core.hooksPath || git config core.hooksPath .githooks 2>/dev/null || true) && ([ -f meta/feature-flags/sync-bundled-copies.ts ] && bun run meta/feature-flags/sync-bundled-copies.ts 2>/dev/null || true)"
|
|
26
27
|
},
|
|
27
28
|
"dependencies": {
|
|
28
29
|
"@anthropic-ai/claude-agent-sdk": "^0.2.42",
|
|
@@ -869,11 +869,13 @@ describe("AgentLoop", () => {
|
|
|
869
869
|
await loop.run([userMessage], () => {}, undefined, undefined, onCheckpoint);
|
|
870
870
|
|
|
871
871
|
expect(checkpoints).toHaveLength(1);
|
|
872
|
-
expect(checkpoints[0]).
|
|
872
|
+
expect(checkpoints[0]).toMatchObject({
|
|
873
873
|
turnIndex: 0,
|
|
874
874
|
toolCount: 1,
|
|
875
875
|
hasToolUse: true,
|
|
876
876
|
});
|
|
877
|
+
// history should contain the full conversation at checkpoint time
|
|
878
|
+
expect(checkpoints[0].history.length).toBeGreaterThanOrEqual(3);
|
|
877
879
|
});
|
|
878
880
|
|
|
879
881
|
// 17. Returning 'continue' lets the loop proceed normally
|