@vellumai/assistant 0.5.16 → 0.6.0
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 +1 -1
- package/Dockerfile +0 -3
- package/knip.json +2 -1
- package/openapi.yaml +660 -80
- package/package.json +1 -1
- package/src/__tests__/actor-token-service.test.ts +68 -0
- package/src/__tests__/agent-loop.test.ts +0 -32
- package/src/__tests__/always-loaded-tools-guard.test.ts +2 -2
- package/src/__tests__/anthropic-provider.test.ts +57 -3
- package/src/__tests__/app-compiler.test.ts +120 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +2 -2
- package/src/__tests__/call-conversation-messages.test.ts +2 -6
- package/src/__tests__/call-domain.test.ts +2 -6
- package/src/__tests__/call-pointer-messages.test.ts +2 -14
- package/src/__tests__/call-recovery.test.ts +2 -6
- package/src/__tests__/call-routes-http.test.ts +2 -6
- package/src/__tests__/call-store.test.ts +2 -6
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +2 -6
- package/src/__tests__/canonical-guardian-store.test.ts +2 -6
- package/src/__tests__/channel-delivery-store.test.ts +2 -6
- package/src/__tests__/channel-retry-sweep.test.ts +2 -6
- package/src/__tests__/checker.test.ts +25 -3
- package/src/__tests__/clawhub.test.ts +54 -24
- package/src/__tests__/cli-command-risk-guard.test.ts +14 -0
- package/src/__tests__/cli-memory.test.ts +74 -69
- package/src/__tests__/config-schema.test.ts +1 -1
- package/src/__tests__/config-set-platform-guard.test.ts +302 -0
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +2 -6
- package/src/__tests__/contacts-tools.test.ts +31 -0
- package/src/__tests__/context-overflow-reducer.test.ts +86 -0
- package/src/__tests__/context-token-estimator.test.ts +175 -10
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +9 -0
- package/src/__tests__/conversation-agent-loop.test.ts +9 -0
- package/src/__tests__/conversation-attachments.test.ts +2 -6
- package/src/__tests__/conversation-attention-store.test.ts +2 -6
- package/src/__tests__/conversation-clear-safety.test.ts +2 -6
- package/src/__tests__/conversation-delete-schedule-cleanup.test.ts +4 -10
- package/src/__tests__/conversation-disk-view-integration.test.ts +2 -6
- package/src/__tests__/conversation-disk-view.test.ts +2 -6
- package/src/__tests__/conversation-error.test.ts +33 -2
- package/src/__tests__/conversation-fork-crud.test.ts +2 -6
- package/src/__tests__/conversation-history-web-search.test.ts +5 -0
- package/src/__tests__/conversation-load-history-repair.test.ts +5 -1
- package/src/__tests__/conversation-media-retry.test.ts +91 -0
- package/src/__tests__/conversation-starter-routes.test.ts +20 -11
- package/src/__tests__/conversation-store.test.ts +2 -6
- package/src/__tests__/conversation-usage.test.ts +2 -6
- package/src/__tests__/conversation-wipe.test.ts +11 -408
- package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
- package/src/__tests__/credential-execution-shell-lockdown.test.ts +2 -2
- package/src/__tests__/credential-security-e2e.test.ts +2 -0
- package/src/__tests__/followup-tools.test.ts +2 -6
- package/src/__tests__/graph-extraction-event-date.test.ts +186 -0
- package/src/__tests__/guardian-action-conversation-turn.test.ts +2 -6
- package/src/__tests__/guardian-action-followup-executor.test.ts +2 -6
- package/src/__tests__/guardian-action-followup-store.test.ts +2 -6
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +2 -6
- package/src/__tests__/guardian-action-late-reply.test.ts +2 -6
- package/src/__tests__/guardian-action-store.test.ts +2 -6
- package/src/__tests__/guardian-binding-drift-heal.test.ts +2 -6
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +8 -8
- package/src/__tests__/guardian-dispatch.test.ts +2 -6
- package/src/__tests__/guardian-grant-minting.test.ts +2 -14
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +2 -6
- package/src/__tests__/guardian-routing-invariants.test.ts +192 -6
- package/src/__tests__/guardian-routing-state.test.ts +2 -6
- package/src/__tests__/guardian-verification-voice-binding.test.ts +2 -6
- package/src/__tests__/inbound-invite-redemption.test.ts +2 -6
- package/src/__tests__/injection-block.test.ts +154 -0
- package/src/__tests__/install-meta.test.ts +506 -0
- package/src/__tests__/install-skill-routing.test.ts +292 -0
- package/src/__tests__/invite-redemption-service.test.ts +2 -6
- package/src/__tests__/invite-routes-http.test.ts +2 -6
- package/src/__tests__/jobs-store-qdrant-breaker.test.ts +2 -14
- package/src/__tests__/list-messages-attachments.test.ts +2 -6
- package/src/__tests__/llm-context-route-provider.test.ts +2 -6
- package/src/__tests__/llm-request-log-turn-query.test.ts +2 -6
- package/src/__tests__/llm-usage-store.test.ts +2 -6
- package/src/__tests__/log-export-workspace.test.ts +2 -6
- package/src/__tests__/managed-store.test.ts +38 -11
- package/src/__tests__/memory-jobs-worker-backoff.test.ts +2 -8
- package/src/__tests__/memory-recall-log-store.test.ts +2 -6
- package/src/__tests__/memory-upsert-concurrency.test.ts +4 -112
- package/src/__tests__/non-member-access-request.test.ts +2 -6
- package/src/__tests__/notification-guardian-path.test.ts +2 -6
- package/src/__tests__/oauth-cli.test.ts +364 -2
- package/src/__tests__/oauth2-gateway-transport.test.ts +18 -3
- package/src/__tests__/outlook-attachments.test.ts +301 -0
- package/src/__tests__/outlook-automation-tools.test.ts +425 -0
- package/src/__tests__/outlook-categories.test.ts +212 -0
- package/src/__tests__/outlook-client-automation.test.ts +246 -0
- package/src/__tests__/outlook-compose-tools.test.ts +325 -0
- package/src/__tests__/outlook-declutter-tools.test.ts +585 -0
- package/src/__tests__/outlook-email-watcher.test.ts +322 -0
- package/src/__tests__/outlook-follow-up.test.ts +196 -0
- package/src/__tests__/outlook-messaging-provider.test.ts +498 -3
- package/src/__tests__/outlook-trash.test.ts +77 -0
- package/src/__tests__/outlook-unsubscribe.test.ts +250 -0
- package/src/__tests__/platform-callback-registration.test.ts +4 -4
- package/src/__tests__/playbook-execution.test.ts +76 -80
- package/src/__tests__/playbook-tools.test.ts +5 -7
- package/src/__tests__/provider-error-scenarios.test.ts +21 -0
- package/src/__tests__/rebuild-index-graph-nodes.test.ts +273 -0
- package/src/__tests__/registry.test.ts +2 -2
- package/src/__tests__/require-fresh-approval.test.ts +64 -2
- package/src/__tests__/runtime-events-sse-parity.test.ts +2 -6
- package/src/__tests__/runtime-events-sse.test.ts +2 -6
- package/src/__tests__/schedule-store.test.ts +2 -6
- package/src/__tests__/schedule-tools.test.ts +2 -6
- package/src/__tests__/scheduler-recurrence.test.ts +1 -5
- package/src/__tests__/scoped-approval-grants.test.ts +2 -6
- package/src/__tests__/scoped-grant-security-matrix.test.ts +2 -6
- package/src/__tests__/search-skills-unified.test.ts +421 -0
- package/src/__tests__/secret-onetime-send.test.ts +2 -0
- package/src/__tests__/send-endpoint-busy.test.ts +2 -6
- package/src/__tests__/sequence-store.test.ts +2 -6
- package/src/__tests__/server-history-render.test.ts +2 -6
- package/src/__tests__/skill-feature-flags-integration.test.ts +38 -31
- package/src/__tests__/skill-feature-flags.test.ts +6 -6
- package/src/__tests__/skill-load-feature-flag.test.ts +11 -11
- package/src/__tests__/skill-memory.test.ts +140 -98
- package/src/__tests__/skills-uninstall.test.ts +2 -2
- package/src/__tests__/skills.test.ts +1 -1
- package/src/__tests__/slack-inbound-verification.test.ts +2 -6
- package/src/__tests__/task-compiler.test.ts +2 -6
- package/src/__tests__/task-management-tools.test.ts +2 -6
- package/src/__tests__/task-memory-cleanup.test.ts +173 -229
- package/src/__tests__/task-runner.test.ts +2 -6
- package/src/__tests__/task-scheduler.test.ts +2 -6
- package/src/__tests__/test-preload.ts +3 -0
- package/src/__tests__/tool-approval-handler.test.ts +2 -6
- package/src/__tests__/tool-grant-request-escalation.test.ts +2 -6
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +276 -0
- package/src/__tests__/trust-store.test.ts +1 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +2 -6
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +2 -6
- package/src/__tests__/trusted-contact-multichannel.test.ts +2 -6
- package/src/__tests__/trusted-contact-verification.test.ts +2 -6
- package/src/__tests__/turn-boundary-resolution.test.ts +2 -6
- package/src/__tests__/usage-cache-backfill-migration.test.ts +1 -6
- package/src/__tests__/usage-routes.test.ts +2 -6
- package/src/__tests__/verification-control-plane-policy.test.ts +0 -2
- package/src/__tests__/voice-invite-redemption.test.ts +2 -6
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +2 -6
- package/src/__tests__/voice-session-bridge.test.ts +2 -6
- package/src/__tests__/volume-security-guard.test.ts +2 -0
- package/src/__tests__/workspace-lifecycle.test.ts +29 -1
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +2 -6
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +2 -6
- package/src/__tests__/workspace-migration-026-backfill-install-meta.test.ts +558 -0
- package/src/__tests__/workspace-policy.test.ts +1 -1
- package/src/agent/attachments.ts +7 -2
- package/src/agent/image-optimize.ts +165 -0
- package/src/agent/loop.ts +1 -15
- package/src/bundler/app-compiler.ts +179 -2
- package/src/bundler/package-resolver.ts +3 -5
- package/src/cli/__tests__/notifications.test.ts +1 -2
- package/src/cli/cli-memory.ts +67 -64
- package/src/cli/commands/avatar.ts +3 -3
- package/src/cli/commands/config.ts +26 -13
- package/src/cli/commands/doctor.ts +2 -2
- package/src/cli/commands/memory.ts +41 -55
- package/src/cli/commands/oauth/__tests__/connect.test.ts +2 -2
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +2 -2
- package/src/cli/commands/oauth/__tests__/mode.test.ts +8 -1
- package/src/cli/commands/oauth/__tests__/status.test.ts +2 -2
- package/src/cli/commands/oauth/connect.ts +11 -6
- package/src/cli/commands/oauth/mode.ts +7 -0
- package/src/cli/commands/oauth/shared.ts +39 -3
- package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +5 -5
- package/src/cli/commands/platform/index.ts +16 -16
- package/src/cli/commands/skills.ts +88 -16
- package/src/cli/commands/trust.ts +2 -2
- package/src/cli/lib/daemon-credential-client.ts +2 -3
- package/src/config/bundled-skills/acp/TOOLS.json +1 -1
- package/src/config/bundled-skills/contacts/SKILL.md +0 -1
- package/src/config/bundled-skills/contacts/TOOLS.json +0 -8
- package/src/config/bundled-skills/contacts/tools/contact-upsert.ts +0 -4
- package/src/config/bundled-skills/gmail/SKILL.md +2 -10
- package/src/config/bundled-skills/google-calendar/SKILL.md +1 -9
- package/src/config/bundled-skills/messaging/SKILL.md +10 -18
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +40 -33
- package/src/config/bundled-skills/outlook/SKILL.md +189 -0
- package/src/config/bundled-skills/outlook/TOOLS.json +530 -0
- package/src/config/bundled-skills/outlook/tools/outlook-attachments.ts +85 -0
- package/src/config/bundled-skills/outlook/tools/outlook-categories.ts +77 -0
- package/src/config/bundled-skills/outlook/tools/outlook-draft.ts +84 -0
- package/src/config/bundled-skills/outlook/tools/outlook-follow-up.ts +94 -0
- package/src/config/bundled-skills/outlook/tools/outlook-forward.ts +49 -0
- package/src/config/bundled-skills/outlook/tools/outlook-outreach-scan.ts +237 -0
- package/src/config/bundled-skills/outlook/tools/outlook-rules.ts +161 -0
- package/src/config/bundled-skills/outlook/tools/outlook-send-draft.ts +32 -0
- package/src/config/bundled-skills/outlook/tools/outlook-sender-digest.ts +272 -0
- package/src/config/bundled-skills/outlook/tools/outlook-trash.ts +29 -0
- package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +129 -0
- package/src/config/bundled-skills/outlook/tools/outlook-vacation.ts +87 -0
- package/src/config/bundled-skills/outlook/tools/shared.ts +20 -0
- package/src/config/bundled-skills/outlook-calendar/SKILL.md +51 -0
- package/src/config/bundled-skills/outlook-calendar/TOOLS.json +221 -0
- package/src/config/bundled-skills/outlook-calendar/calendar-client.ts +252 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-check-availability.ts +53 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-create-event.ts +74 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-get-event.ts +18 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-list-events.ts +46 -0
- package/src/config/bundled-skills/outlook-calendar/tools/outlook-calendar-rsvp.ts +36 -0
- package/src/config/bundled-skills/outlook-calendar/tools/shared.ts +17 -0
- package/src/config/bundled-skills/outlook-calendar/types.ts +120 -0
- package/src/config/bundled-skills/playbooks/tools/playbook-create.ts +47 -40
- package/src/config/bundled-skills/playbooks/tools/playbook-delete.ts +16 -29
- package/src/config/bundled-skills/playbooks/tools/playbook-list.ts +16 -18
- package/src/config/bundled-skills/playbooks/tools/playbook-update.ts +39 -47
- package/src/config/bundled-skills/slack/SKILL.md +1 -7
- package/src/config/bundled-tool-registry.ts +56 -4
- package/src/config/env-registry.ts +15 -8
- package/src/config/feature-flag-registry.json +21 -124
- package/src/config/schemas/platform.ts +8 -0
- package/src/config/schemas/timeouts.ts +1 -1
- package/src/config/skills.ts +18 -7
- package/src/context/token-estimator.ts +25 -18
- package/src/context/window-manager.ts +6 -2
- package/src/credential-execution/process-manager.ts +3 -1
- package/src/daemon/context-overflow-reducer.ts +46 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +123 -82
- package/src/daemon/conversation-agent-loop.ts +96 -61
- package/src/daemon/conversation-error.ts +31 -8
- package/src/daemon/conversation-lifecycle.ts +33 -0
- package/src/daemon/conversation-media-retry.ts +85 -7
- package/src/daemon/conversation-notifiers.ts +4 -1
- package/src/daemon/conversation-runtime-assembly.ts +5 -0
- package/src/daemon/conversation.ts +41 -2
- package/src/daemon/daemon-control.ts +8 -2
- package/src/daemon/handlers/shared.ts +22 -12
- package/src/daemon/handlers/skills.ts +416 -202
- package/src/daemon/lifecycle.ts +40 -1
- package/src/daemon/main.ts +5 -1
- package/src/daemon/message-types/conversations.ts +4 -1
- package/src/daemon/message-types/messages.ts +3 -1
- package/src/daemon/message-types/skills.ts +97 -36
- package/src/daemon/providers-setup.ts +5 -0
- package/src/daemon/server.ts +11 -2
- package/src/daemon/tool-side-effects.ts +27 -5
- package/src/heartbeat/heartbeat-service.ts +1 -0
- package/src/hooks/cli.ts +2 -2
- package/src/hooks/runner.ts +15 -38
- package/src/inbound/platform-callback-registration.ts +14 -14
- package/src/memory/admin.ts +11 -45
- package/src/memory/conversation-bootstrap.ts +2 -0
- package/src/memory/conversation-crud.ts +242 -348
- package/src/memory/conversation-group-migration.ts +157 -0
- package/src/memory/conversation-queries.ts +4 -2
- package/src/memory/db-init.ts +30 -3
- package/src/memory/embed.ts +73 -0
- package/src/memory/embedding-backend.ts +8 -14
- package/src/memory/embedding-runtime-manager.ts +12 -114
- package/src/memory/fingerprint.ts +2 -2
- package/src/memory/graph/bootstrap.ts +512 -0
- package/src/memory/graph/capability-seed.ts +297 -0
- package/src/memory/graph/consolidation.ts +691 -0
- package/src/memory/graph/conversation-graph-memory.ts +630 -0
- package/src/memory/graph/decay.test.ts +208 -0
- package/src/memory/graph/decay.ts +195 -0
- package/src/memory/graph/extraction-job.ts +69 -0
- package/src/memory/graph/extraction.test.ts +936 -0
- package/src/memory/graph/extraction.ts +1254 -0
- package/src/memory/graph/graph-search.ts +266 -0
- package/src/memory/graph/image-ref-utils.ts +29 -0
- package/src/memory/graph/injection.test.ts +513 -0
- package/src/memory/graph/injection.ts +439 -0
- package/src/memory/graph/inspect.ts +534 -0
- package/src/memory/graph/narrative.ts +267 -0
- package/src/memory/graph/pattern-scan.ts +269 -0
- package/src/memory/graph/retriever.ts +1008 -0
- package/src/memory/graph/scoring.test.ts +548 -0
- package/src/memory/graph/scoring.ts +232 -0
- package/src/memory/graph/serendipity.ts +65 -0
- package/src/memory/graph/store.test.ts +1050 -0
- package/src/memory/graph/store.ts +699 -0
- package/src/memory/graph/tool-handlers.ts +426 -0
- package/src/memory/graph/tools.ts +141 -0
- package/src/memory/graph/triggers.test.ts +487 -0
- package/src/memory/graph/triggers.ts +223 -0
- package/src/memory/graph/types.ts +271 -0
- package/src/memory/group-crud.ts +191 -0
- package/src/memory/indexer.ts +37 -19
- package/src/memory/job-handlers/cleanup.ts +0 -53
- package/src/memory/job-handlers/conversation-starters.ts +91 -53
- package/src/memory/job-handlers/embedding.ts +5 -31
- package/src/memory/job-handlers/index-maintenance.ts +23 -11
- package/src/memory/job-handlers/summarization.ts +32 -17
- package/src/memory/job-utils.ts +1 -1
- package/src/memory/jobs-store.ts +50 -70
- package/src/memory/jobs-worker.ts +147 -112
- package/src/memory/message-content.ts +1 -0
- package/src/memory/migrations/202-memory-graph-tables.ts +130 -0
- package/src/memory/migrations/203-drop-memory-items-tables.ts +23 -0
- package/src/memory/migrations/204-rename-memory-graph-type-values.ts +46 -0
- package/src/memory/migrations/205-memory-graph-image-refs.ts +11 -0
- package/src/memory/migrations/index.ts +4 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/qdrant-client.ts +44 -17
- package/src/memory/schema/index.ts +1 -0
- package/src/memory/schema/memory-graph.ts +139 -0
- package/src/memory/search/semantic.ts +47 -91
- package/src/memory/task-memory-cleanup.ts +28 -50
- package/src/messaging/providers/outlook/adapter.ts +8 -1
- package/src/messaging/providers/outlook/client.ts +299 -0
- package/src/messaging/providers/outlook/types.ts +118 -0
- package/src/notifications/adapters/macos.ts +1 -0
- package/src/notifications/copy-composer.ts +9 -0
- package/src/notifications/signal.ts +16 -0
- package/src/oauth/seed-providers.ts +2 -1
- package/src/permissions/checker.ts +24 -3
- package/src/permissions/defaults.ts +4 -4
- package/src/permissions/workspace-policy.ts +1 -1
- package/src/playbooks/playbook-compiler.ts +19 -18
- package/src/playbooks/types.ts +4 -3
- package/src/prompts/system-prompt.ts +3 -29
- package/src/providers/anthropic/client.ts +47 -19
- package/src/providers/gemini/client.ts +1 -1
- package/src/providers/openai/client.ts +1 -1
- package/src/providers/registry.ts +1 -1
- package/src/providers/retry.ts +19 -3
- package/src/runtime/actor-trust-resolver.ts +5 -1
- package/src/runtime/auth/route-policy.ts +7 -0
- package/src/runtime/guardian-reply-router.ts +5 -1
- package/src/runtime/http-server.ts +23 -3
- package/src/runtime/middleware/auth.ts +20 -0
- package/src/runtime/routes/attachment-routes.test.ts +106 -0
- package/src/runtime/routes/attachment-routes.ts +106 -16
- package/src/runtime/routes/brain-graph-routes.ts +21 -22
- package/src/runtime/routes/btw-routes.ts +8 -0
- package/src/runtime/routes/conversation-management-routes.ts +2 -0
- package/src/runtime/routes/conversation-starter-routes.ts +2 -2
- package/src/runtime/routes/debug-routes.ts +1 -1
- package/src/runtime/routes/global-search-routes.ts +21 -19
- package/src/runtime/routes/group-routes.ts +207 -0
- package/src/runtime/routes/guardian-action-routes.ts +21 -10
- package/src/runtime/routes/guardian-bootstrap-routes.ts +23 -19
- package/src/runtime/routes/inbound-message-handler.ts +19 -0
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.test.ts +292 -0
- package/src/runtime/routes/inbound-stages/guardian-activation-intercept.ts +207 -0
- package/src/runtime/routes/memory-item-routes.test.ts +2 -14
- package/src/runtime/routes/memory-item-routes.ts +341 -388
- package/src/runtime/routes/schedule-routes.ts +2 -0
- package/src/runtime/routes/skills-routes.ts +103 -37
- package/src/runtime/routes/work-items-routes.test.ts +2 -6
- package/src/schedule/scheduler.ts +8 -1
- package/src/security/oauth2.ts +1 -1
- package/src/security/secure-keys.ts +4 -8
- package/src/shared/provider-env-vars.ts +19 -0
- package/src/skills/catalog-cache.ts +5 -0
- package/src/skills/catalog-install.ts +15 -14
- package/src/skills/clawhub.ts +134 -154
- package/src/skills/install-meta.ts +208 -0
- package/src/skills/managed-store.ts +27 -16
- package/src/skills/skill-memory.ts +152 -77
- package/src/skills/skillssh-registry.ts +19 -17
- package/src/tasks/task-runner.ts +3 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -5
- package/src/tools/browser/runtime-check.ts +3 -1
- package/src/tools/memory/register.ts +63 -46
- package/src/tools/permission-checker.ts +7 -1
- package/src/tools/shared/filesystem/image-read.ts +22 -85
- package/src/tools/terminal/safe-env.ts +1 -0
- package/src/tools/tool-manifest.ts +3 -3
- package/src/util/browser.ts +25 -10
- package/src/util/bun-runtime.ts +172 -0
- package/src/watcher/providers/outlook-calendar.ts +343 -0
- package/src/watcher/providers/outlook.ts +198 -0
- package/src/workspace/migrations/025-remove-oauth-app-setup-skills.ts +76 -0
- package/src/workspace/migrations/026-backfill-install-meta.ts +325 -0
- package/src/workspace/migrations/027-remove-orphaned-optimized-images-cache.ts +42 -0
- package/src/workspace/migrations/registry.ts +6 -0
- package/src/__tests__/context-memory-e2e.test.ts +0 -415
- package/src/__tests__/journal-context.test.ts +0 -268
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +0 -297
- package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -459
- package/src/__tests__/memory-query-builder.test.ts +0 -59
- package/src/__tests__/memory-recall-quality.test.ts +0 -1046
- package/src/__tests__/memory-regressions.experimental.test.ts +0 -629
- package/src/__tests__/memory-regressions.test.ts +0 -3696
- package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -295
- package/src/daemon/conversation-memory.ts +0 -207
- package/src/memory/conversation-starters-cadence.ts +0 -74
- package/src/memory/items-extractor.ts +0 -860
- package/src/memory/job-handlers/batch-extraction.ts +0 -753
- package/src/memory/job-handlers/extraction.ts +0 -40
- package/src/memory/job-handlers/journal-carry-forward.test.ts +0 -355
- package/src/memory/job-handlers/journal-carry-forward.ts +0 -255
- package/src/memory/journal-memory.ts +0 -224
- package/src/memory/query-builder.ts +0 -47
- package/src/memory/query-expansion.ts +0 -83
- package/src/memory/retriever.test.ts +0 -1592
- package/src/memory/retriever.ts +0 -1331
- package/src/memory/search/formatting.test.ts +0 -140
- package/src/memory/search/formatting.ts +0 -262
- package/src/memory/search/mmr.ts +0 -139
- package/src/memory/search/ranking.ts +0 -15
- package/src/memory/search/staleness.ts +0 -40
- package/src/memory/search/tier-classifier.ts +0 -18
- package/src/memory/search/types.ts +0 -121
- package/src/prompts/journal-context.ts +0 -154
- package/src/tools/memory/definitions.ts +0 -69
- package/src/tools/memory/handlers.test.ts +0 -562
- package/src/tools/memory/handlers.ts +0 -434
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime migration for the conversation_groups table and group_id column
|
|
3
|
+
* on the conversations table. Follows the same lazy-initialization pattern
|
|
4
|
+
* as conversation-display-order-migration.ts.
|
|
5
|
+
*
|
|
6
|
+
* group_id is display/organizational metadata, consistent with how
|
|
7
|
+
* display_order and is_pinned are handled via runtime migration rather
|
|
8
|
+
* than the formal migrations/ pipeline.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { getLogger } from "../util/logger.js";
|
|
12
|
+
import { ensureDisplayOrderMigration } from "./conversation-display-order-migration.js";
|
|
13
|
+
import { rawExec, rawGet, rawRun } from "./db.js";
|
|
14
|
+
|
|
15
|
+
const log = getLogger("conversation-store");
|
|
16
|
+
|
|
17
|
+
function isDuplicateColumnError(err: unknown): boolean {
|
|
18
|
+
return err instanceof Error && /duplicate column name:/i.test(err.message);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
let migrated = false;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Uses raw BEGIN/COMMIT for the one-time backfill. Must NOT be called
|
|
25
|
+
* for the first time inside a Drizzle db.transaction() block — SQLite
|
|
26
|
+
* does not support nested transactions. In practice this is safe because
|
|
27
|
+
* the migration is triggered by early startup queries (listConversations,
|
|
28
|
+
* batchSetDisplayOrders) before any transaction-wrapped paths run, and
|
|
29
|
+
* the `migrated` flag makes subsequent calls no-ops.
|
|
30
|
+
*/
|
|
31
|
+
export function ensureGroupMigration(): void {
|
|
32
|
+
if (migrated) return;
|
|
33
|
+
|
|
34
|
+
// 1. Create groups table if not exists
|
|
35
|
+
rawExec(`
|
|
36
|
+
CREATE TABLE IF NOT EXISTS conversation_groups (
|
|
37
|
+
id TEXT PRIMARY KEY,
|
|
38
|
+
name TEXT NOT NULL,
|
|
39
|
+
sort_position REAL NOT NULL DEFAULT 0,
|
|
40
|
+
is_system_group BOOLEAN NOT NULL DEFAULT FALSE,
|
|
41
|
+
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
|
|
42
|
+
updated_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
|
|
43
|
+
)
|
|
44
|
+
`);
|
|
45
|
+
|
|
46
|
+
// 2. Add group_id column if not exists
|
|
47
|
+
// Match existing error-handling pattern from conversation-display-order-migration.ts:
|
|
48
|
+
// only swallow duplicate-column errors, log+throw everything else.
|
|
49
|
+
try {
|
|
50
|
+
rawRun(
|
|
51
|
+
"ALTER TABLE conversations ADD COLUMN group_id TEXT REFERENCES conversation_groups(id) ON DELETE SET NULL",
|
|
52
|
+
);
|
|
53
|
+
} catch (err) {
|
|
54
|
+
if (!isDuplicateColumnError(err)) {
|
|
55
|
+
log.error({ err }, "Failed to add group_id column");
|
|
56
|
+
throw err;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 3. Seed system groups (three: pinned, scheduled, background)
|
|
61
|
+
rawExec(`
|
|
62
|
+
INSERT OR IGNORE INTO conversation_groups (id, name, sort_position, is_system_group)
|
|
63
|
+
VALUES
|
|
64
|
+
('system:pinned', 'Pinned', 0, TRUE),
|
|
65
|
+
('system:scheduled', 'Scheduled', 1, TRUE),
|
|
66
|
+
('system:background', 'Background', 2, TRUE)
|
|
67
|
+
`);
|
|
68
|
+
|
|
69
|
+
// 4. One-time backfill (guard: persistent marker prevents re-running on restart)
|
|
70
|
+
//
|
|
71
|
+
// The backfill sets group_id on existing conversations based on their attributes.
|
|
72
|
+
// It must only run once — re-running would overwrite user-explicit ungrouping
|
|
73
|
+
// (e.g., user removes a conversation from a group → group_id = NULL → next restart
|
|
74
|
+
// would backfill it back into system:background). A persistent marker row in
|
|
75
|
+
// conversation_groups tracks whether backfill has already completed.
|
|
76
|
+
const backfillDone = rawGet<{ id: string }>(
|
|
77
|
+
"SELECT id FROM conversation_groups WHERE id = '_backfill_complete'",
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
if (!backfillDone) {
|
|
81
|
+
ensureDisplayOrderMigration(); // ensure is_pinned column exists first
|
|
82
|
+
|
|
83
|
+
// Canonical classification rules (full decision tree with precedence):
|
|
84
|
+
//
|
|
85
|
+
// 1. Pinned: is_pinned = TRUE
|
|
86
|
+
// → system:pinned (always wins, checked first)
|
|
87
|
+
//
|
|
88
|
+
// 2. Scheduled: source IN ('schedule', 'reminder') OR schedule_job_id IS NOT NULL
|
|
89
|
+
// → system:scheduled (checked second)
|
|
90
|
+
//
|
|
91
|
+
// 3. Background: (conversation_type = 'background' AND COALESCE(source, '') != 'notification')
|
|
92
|
+
// OR source IN ('heartbeat', 'task')
|
|
93
|
+
// AND COALESCE(source, '') NOT IN ('schedule', 'reminder') ← safety + NULL handling
|
|
94
|
+
// AND schedule_job_id IS NULL ← safety: prevents overlap with step 2
|
|
95
|
+
// → system:background
|
|
96
|
+
//
|
|
97
|
+
// 4. Else: ungrouped (group_id = NULL)
|
|
98
|
+
//
|
|
99
|
+
// Each backfill step uses AND group_id IS NULL to avoid reassignment.
|
|
100
|
+
// The exclusion guards in step 3 are belt-and-suspenders — step 2's
|
|
101
|
+
// group_id IS NULL guard already prevents overlap, but the explicit
|
|
102
|
+
// exclusions make the SQL self-documenting and migration-order-independent.
|
|
103
|
+
// COALESCE handles NULL source values (legacy rows).
|
|
104
|
+
|
|
105
|
+
// Wrap all backfill steps in a transaction so a partial failure
|
|
106
|
+
// (e.g. crash mid-backfill) doesn't leave conversations half-classified
|
|
107
|
+
// with the marker row missing, which would cause a re-run to skip
|
|
108
|
+
// already-classified rows (group_id IS NULL guard).
|
|
109
|
+
try {
|
|
110
|
+
rawExec("BEGIN");
|
|
111
|
+
|
|
112
|
+
// Step A: Pinned -> system:pinned (runs first, always wins)
|
|
113
|
+
rawExec(`
|
|
114
|
+
UPDATE conversations SET group_id = 'system:pinned'
|
|
115
|
+
WHERE is_pinned = 1 AND group_id IS NULL
|
|
116
|
+
`);
|
|
117
|
+
|
|
118
|
+
// Step B: Scheduled -> system:scheduled (schedule/reminder source or has schedule_job_id)
|
|
119
|
+
rawExec(`
|
|
120
|
+
UPDATE conversations SET group_id = 'system:scheduled'
|
|
121
|
+
WHERE (source IN ('schedule', 'reminder') OR schedule_job_id IS NOT NULL)
|
|
122
|
+
AND group_id IS NULL
|
|
123
|
+
`);
|
|
124
|
+
|
|
125
|
+
// Step C: Background -> system:background (background type, heartbeat, or task — excluding notifications)
|
|
126
|
+
// Explicit exclusion of schedule sources + schedule_job_id as a safety net.
|
|
127
|
+
// Step B already catches those via group_id IS NULL guard, but the explicit exclusion
|
|
128
|
+
// makes the SQL self-documenting and protects against future migration ordering changes.
|
|
129
|
+
// Note: source can be NULL for legacy rows. NULL != 'notification' evaluates to NULL (not TRUE)
|
|
130
|
+
// in SQL, so use COALESCE to treat NULL source as empty string for the exclusion checks.
|
|
131
|
+
rawExec(`
|
|
132
|
+
UPDATE conversations SET group_id = 'system:background'
|
|
133
|
+
WHERE (
|
|
134
|
+
(conversation_type = 'background' AND COALESCE(source, '') != 'notification')
|
|
135
|
+
OR source IN ('heartbeat', 'task')
|
|
136
|
+
)
|
|
137
|
+
AND COALESCE(source, '') NOT IN ('schedule', 'reminder')
|
|
138
|
+
AND schedule_job_id IS NULL
|
|
139
|
+
AND group_id IS NULL
|
|
140
|
+
`);
|
|
141
|
+
|
|
142
|
+
// Mark backfill as complete so it won't re-run on future process restarts
|
|
143
|
+
rawExec(`
|
|
144
|
+
INSERT OR IGNORE INTO conversation_groups (id, name, sort_position, is_system_group)
|
|
145
|
+
VALUES ('_backfill_complete', '_backfill_complete', -1, TRUE)
|
|
146
|
+
`);
|
|
147
|
+
|
|
148
|
+
rawExec("COMMIT");
|
|
149
|
+
} catch (err) {
|
|
150
|
+
rawExec("ROLLBACK");
|
|
151
|
+
log.error({ err }, "Group backfill transaction failed, rolled back");
|
|
152
|
+
throw err;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
migrated = true;
|
|
157
|
+
}
|
|
@@ -4,6 +4,7 @@ import { getLogger } from "../util/logger.js";
|
|
|
4
4
|
import type { ConversationRow } from "./conversation-crud.js";
|
|
5
5
|
import { parseConversation } from "./conversation-crud.js";
|
|
6
6
|
import { ensureDisplayOrderMigration } from "./conversation-display-order-migration.js";
|
|
7
|
+
import { ensureGroupMigration } from "./conversation-group-migration.js";
|
|
7
8
|
import { getDb, rawAll } from "./db.js";
|
|
8
9
|
import { conversations, messages } from "./schema.js";
|
|
9
10
|
|
|
@@ -13,7 +14,7 @@ const log = getLogger("conversation-store");
|
|
|
13
14
|
* Build an FTS5 MATCH query string from natural text by extracting tokens.
|
|
14
15
|
* Used for messages_fts full-text search over conversation content.
|
|
15
16
|
*/
|
|
16
|
-
function buildFtsMatchQuery(text: string): string | null {
|
|
17
|
+
export function buildFtsMatchQuery(text: string): string | null {
|
|
17
18
|
const tokens = text
|
|
18
19
|
.toLowerCase()
|
|
19
20
|
.split(/[^a-z0-9_]+/g)
|
|
@@ -30,6 +31,7 @@ export function listConversations(
|
|
|
30
31
|
offset = 0,
|
|
31
32
|
): ConversationRow[] {
|
|
32
33
|
ensureDisplayOrderMigration();
|
|
34
|
+
ensureGroupMigration();
|
|
33
35
|
const db = getDb();
|
|
34
36
|
const where = backgroundOnly
|
|
35
37
|
? sql`${conversations.conversationType} = 'background'`
|
|
@@ -309,7 +311,7 @@ export function searchConversations(
|
|
|
309
311
|
* occurrence of `query`. The content may be JSON (content blocks) or plain
|
|
310
312
|
* text; we extract a readable snippet in either case.
|
|
311
313
|
*/
|
|
312
|
-
function buildExcerpt(rawContent: string, query: string): string {
|
|
314
|
+
export function buildExcerpt(rawContent: string, query: string): string {
|
|
313
315
|
// Try to extract plain text from JSON content blocks first.
|
|
314
316
|
let text = rawContent;
|
|
315
317
|
try {
|
package/src/memory/db-init.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { dirname, join } from "node:path";
|
|
|
11
11
|
|
|
12
12
|
import { ensureDataDir, getDbPath } from "../util/platform.js";
|
|
13
13
|
import { getDb, getSqlite } from "./db-connection.js";
|
|
14
|
+
import { migrateToolCreatedItems } from "./graph/bootstrap.js";
|
|
14
15
|
import {
|
|
15
16
|
addCoreColumns,
|
|
16
17
|
createActorRefreshTokenRecordsTable,
|
|
@@ -57,6 +58,7 @@ import {
|
|
|
57
58
|
migrateContactsUserFileColumn,
|
|
58
59
|
migrateConversationForkLineage,
|
|
59
60
|
migrateConversationsThreadTypeIndex,
|
|
61
|
+
migrateCreateMemoryGraphTables,
|
|
60
62
|
migrateCreateMemoryRecallLogs,
|
|
61
63
|
migrateCreateThreadStartersTable,
|
|
62
64
|
migrateCreateTraceEventsTable,
|
|
@@ -69,6 +71,7 @@ import {
|
|
|
69
71
|
migrateDropEntityTables,
|
|
70
72
|
migrateDropLegacyMemberGuardianTables,
|
|
71
73
|
migrateDropLoopbackPortColumn,
|
|
74
|
+
migrateDropMemoryItemsTables,
|
|
72
75
|
migrateDropMemorySegmentFts,
|
|
73
76
|
migrateDropOrphanedMediaTables,
|
|
74
77
|
migrateDropRemindersTable,
|
|
@@ -91,6 +94,7 @@ import {
|
|
|
91
94
|
migrateInviteContactId,
|
|
92
95
|
migrateLlmRequestLogMessageId,
|
|
93
96
|
migrateLlmRequestLogProvider,
|
|
97
|
+
migrateMemoryGraphImageRefs,
|
|
94
98
|
migrateMemoryItemSupersession,
|
|
95
99
|
migrateMessagesConversationCreatedAtIndex,
|
|
96
100
|
migrateMessagesFtsBackfill,
|
|
@@ -111,6 +115,7 @@ import {
|
|
|
111
115
|
migrateRenameGmailProviderKeyToGoogle,
|
|
112
116
|
migrateRenameGuardianVerificationValues,
|
|
113
117
|
migrateRenameInboxThreadStateTable,
|
|
118
|
+
migrateRenameMemoryGraphTypeValues,
|
|
114
119
|
migrateRenameNotificationThreadColumns,
|
|
115
120
|
migrateRenameSequenceEnrollmentsThreadIdColumn,
|
|
116
121
|
migrateRenameSequenceStepsReplyKey,
|
|
@@ -139,8 +144,8 @@ import {
|
|
|
139
144
|
// ---------------------------------------------------------------------------
|
|
140
145
|
|
|
141
146
|
function getTemplateDbPath(): string {
|
|
142
|
-
// Hash this file + all migration files so the template
|
|
143
|
-
// when any migration changes.
|
|
147
|
+
// Hash this file + all migration files + bootstrap migration so the template
|
|
148
|
+
// auto-invalidates when any migration changes.
|
|
144
149
|
const thisFile = new URL(import.meta.url).pathname;
|
|
145
150
|
const hash = createHash("md5");
|
|
146
151
|
hash.update(readFileSync(thisFile, "utf-8"));
|
|
@@ -150,9 +155,15 @@ function getTemplateDbPath(): string {
|
|
|
150
155
|
hash.update(readFileSync(join(migrationsDir, name), "utf-8"));
|
|
151
156
|
}
|
|
152
157
|
}
|
|
158
|
+
// Include the bootstrap migration (migrateToolCreatedItems) which also runs
|
|
159
|
+
// during initializeDb but lives outside the migrations/ directory.
|
|
160
|
+
const bootstrapFile = join(dirname(thisFile), "graph", "bootstrap.ts");
|
|
161
|
+
if (existsSync(bootstrapFile)) {
|
|
162
|
+
hash.update(readFileSync(bootstrapFile, "utf-8"));
|
|
163
|
+
}
|
|
153
164
|
return join(
|
|
154
165
|
tmpdir(),
|
|
155
|
-
`vellum-test-db-template-${hash.digest("hex").slice(0, 12)}.db
|
|
166
|
+
`vellum-test-db-template-${hash.digest("hex").slice(0, 12)}.db`
|
|
156
167
|
);
|
|
157
168
|
}
|
|
158
169
|
|
|
@@ -549,6 +560,22 @@ export function initializeDb(): void {
|
|
|
549
560
|
// (transport is now chosen per-flow via the callbackTransport option, not per-provider)
|
|
550
561
|
migrateDropCallbackTransportColumn(database);
|
|
551
562
|
|
|
563
|
+
// 101. Memory graph tables (nodes, edges, triggers)
|
|
564
|
+
migrateCreateMemoryGraphTables(database);
|
|
565
|
+
|
|
566
|
+
// 101b. Migrate tool-created items from legacy memory_items → graph nodes
|
|
567
|
+
// MUST run before migration 102 drops memory_items so data is preserved.
|
|
568
|
+
migrateToolCreatedItems();
|
|
569
|
+
|
|
570
|
+
// 102. Drop legacy memory_items and memory_item_sources tables (migrated to memory_graph_nodes)
|
|
571
|
+
migrateDropMemoryItemsTables(database);
|
|
572
|
+
|
|
573
|
+
// 103. Rename legacy memory graph node type values: style → behavioral, relationship → semantic
|
|
574
|
+
migrateRenameMemoryGraphTypeValues(database);
|
|
575
|
+
|
|
576
|
+
// 104. Add nullable image_refs TEXT column to memory_graph_nodes
|
|
577
|
+
migrateMemoryGraphImageRefs(database);
|
|
578
|
+
|
|
552
579
|
validateMigrationState(database);
|
|
553
580
|
|
|
554
581
|
if (process.env.BUN_TEST === "1") {
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
// ---------------------------------------------------------------------------
|
|
2
|
+
// Shared embedding utility with retry + exponential backoff
|
|
3
|
+
// ---------------------------------------------------------------------------
|
|
4
|
+
|
|
5
|
+
import type { AssistantConfig } from "../config/types.js";
|
|
6
|
+
import { getLogger } from "../util/logger.js";
|
|
7
|
+
import {
|
|
8
|
+
abortableSleep,
|
|
9
|
+
computeRetryDelay,
|
|
10
|
+
isRetryableNetworkError,
|
|
11
|
+
} from "../util/retry.js";
|
|
12
|
+
import { type EmbeddingInput, embedWithBackend } from "./embedding-backend.js";
|
|
13
|
+
|
|
14
|
+
const log = getLogger("memory-embed");
|
|
15
|
+
|
|
16
|
+
const EMBED_MAX_RETRIES = 3;
|
|
17
|
+
const EMBED_BASE_DELAY_MS = 500;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Wrap embedWithBackend with retry + exponential backoff for transient failures
|
|
21
|
+
* (network errors, 429s, 5xx). Aborts immediately if the caller's signal fires.
|
|
22
|
+
*/
|
|
23
|
+
export async function embedWithRetry(
|
|
24
|
+
config: AssistantConfig,
|
|
25
|
+
texts: EmbeddingInput[],
|
|
26
|
+
opts?: { signal?: AbortSignal },
|
|
27
|
+
): ReturnType<typeof embedWithBackend> {
|
|
28
|
+
let lastError: unknown;
|
|
29
|
+
for (let attempt = 0; attempt <= EMBED_MAX_RETRIES; attempt++) {
|
|
30
|
+
try {
|
|
31
|
+
return await embedWithBackend(config, texts, opts);
|
|
32
|
+
} catch (err) {
|
|
33
|
+
lastError = err;
|
|
34
|
+
if (opts?.signal?.aborted || isAbortError(err)) throw err;
|
|
35
|
+
const isTransient =
|
|
36
|
+
isRetryableNetworkError(err) || isHttpStatusError(err);
|
|
37
|
+
if (!isTransient || attempt === EMBED_MAX_RETRIES) throw err;
|
|
38
|
+
const delay = computeRetryDelay(attempt, EMBED_BASE_DELAY_MS);
|
|
39
|
+
log.warn(
|
|
40
|
+
{ err, attempt: attempt + 1, delayMs: Math.round(delay) },
|
|
41
|
+
"Transient embedding failure, retrying",
|
|
42
|
+
);
|
|
43
|
+
await abortableSleep(delay, opts?.signal);
|
|
44
|
+
if (opts?.signal?.aborted) throw err;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
throw lastError;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isAbortError(err: unknown): boolean {
|
|
51
|
+
if (!(err instanceof Error)) return false;
|
|
52
|
+
return err.name === "AbortError" || err.name === "APIUserAbortError";
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function getErrorStatusCode(err: Error): unknown {
|
|
56
|
+
if ("status" in err) {
|
|
57
|
+
const status = (err as { status: unknown }).status;
|
|
58
|
+
if (status != null) return status;
|
|
59
|
+
}
|
|
60
|
+
if ("statusCode" in err) return (err as { statusCode: unknown }).statusCode;
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function isHttpStatusError(err: unknown): boolean {
|
|
65
|
+
if (!(err instanceof Error)) return false;
|
|
66
|
+
const status = getErrorStatusCode(err);
|
|
67
|
+
if (typeof status === "number") {
|
|
68
|
+
return status === 429 || (status >= 500 && status < 600);
|
|
69
|
+
}
|
|
70
|
+
return /\b429\b|(?:failed|error)\s*\((?:429|5\d{2})\)|(?:status|http)\s*(?:code\s*)?:?\s*5\d{2}\b/i.test(
|
|
71
|
+
err.message,
|
|
72
|
+
);
|
|
73
|
+
}
|
|
@@ -650,27 +650,21 @@ async function selectFallbackBackends(
|
|
|
650
650
|
}
|
|
651
651
|
|
|
652
652
|
/**
|
|
653
|
-
* Returns true when the embedding
|
|
654
|
-
* (images, audio, video). Today only Gemini supports
|
|
653
|
+
* Returns true when the active (primary) embedding backend can handle
|
|
654
|
+
* multimodal inputs (images, audio, video). Today only Gemini supports
|
|
655
|
+
* multimodal.
|
|
655
656
|
*
|
|
656
|
-
*
|
|
657
|
-
*
|
|
658
|
-
*
|
|
659
|
-
*
|
|
657
|
+
* Only returns true when Gemini is the primary selected backend — not when
|
|
658
|
+
* it's merely available as a fallback. Writing multimodal embeddings via a
|
|
659
|
+
* fallback provider while queries go through the primary text backend would
|
|
660
|
+
* mix incompatible vector spaces, making retrieval unreliable.
|
|
660
661
|
*/
|
|
661
662
|
export async function selectedBackendSupportsMultimodal(
|
|
662
663
|
config: AssistantConfig,
|
|
663
664
|
): Promise<boolean> {
|
|
664
665
|
const { backend } = await selectEmbeddingBackend(config);
|
|
665
666
|
if (!backend) return false;
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
// In auto mode, check if Gemini is available as a fallback backend.
|
|
669
|
-
if (config.memory.embeddings.provider === "auto") {
|
|
670
|
-
const fallbacks = await selectFallbackBackends(config, backend.provider);
|
|
671
|
-
return fallbacks.some((fb) => fb.provider === "gemini");
|
|
672
|
-
}
|
|
673
|
-
return false;
|
|
667
|
+
return backend.provider === "gemini";
|
|
674
668
|
}
|
|
675
669
|
|
|
676
670
|
async function isOllamaConfigured(config: AssistantConfig): Promise<boolean> {
|
|
@@ -14,7 +14,6 @@
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
import {
|
|
17
|
-
chmodSync,
|
|
18
17
|
existsSync,
|
|
19
18
|
mkdirSync,
|
|
20
19
|
readdirSync,
|
|
@@ -26,6 +25,7 @@ import {
|
|
|
26
25
|
import { arch, platform } from "node:os";
|
|
27
26
|
import { join } from "node:path";
|
|
28
27
|
|
|
28
|
+
import { ensureBun, findBun } from "../util/bun-runtime.js";
|
|
29
29
|
import { getLogger } from "../util/logger.js";
|
|
30
30
|
import { getEmbeddingModelsDir } from "../util/platform.js";
|
|
31
31
|
import { PromiseGuard } from "../util/promise-guard.js";
|
|
@@ -38,9 +38,6 @@ const ONNXRUNTIME_COMMON_VERSION = "1.21.0";
|
|
|
38
38
|
const TRANSFORMERS_VERSION = "3.8.1";
|
|
39
39
|
const JINJA_VERSION = "0.5.5";
|
|
40
40
|
|
|
41
|
-
/** Bun version to download when system bun is not available. */
|
|
42
|
-
const BUN_VERSION = "1.2.0";
|
|
43
|
-
|
|
44
41
|
/** Composite version string for cache invalidation. */
|
|
45
42
|
const RUNTIME_VERSION = `ort-${ONNXRUNTIME_NODE_VERSION}_hf-${TRANSFORMERS_VERSION}_jinja-${JINJA_VERSION}`;
|
|
46
43
|
|
|
@@ -200,25 +197,15 @@ export class EmbeddingRuntimeManager {
|
|
|
200
197
|
|
|
201
198
|
/**
|
|
202
199
|
* Find a usable bun binary.
|
|
203
|
-
*
|
|
200
|
+
* Delegates to the shared bun-runtime helper, also checking
|
|
201
|
+
* the legacy per-runtime bin/ directory for backwards compat.
|
|
204
202
|
*/
|
|
205
203
|
getBunPath(): string | undefined {
|
|
206
|
-
//
|
|
207
|
-
const
|
|
208
|
-
if (existsSync(
|
|
209
|
-
|
|
210
|
-
// 2. Common installation paths — the compiled daemon inherits a
|
|
211
|
-
// restricted PATH, so we check well-known prefixes directly.
|
|
212
|
-
const home = process.env.HOME ?? "";
|
|
213
|
-
for (const p of [
|
|
214
|
-
join(home, ".bun", "bin", "bun"),
|
|
215
|
-
"/opt/homebrew/bin/bun",
|
|
216
|
-
"/usr/local/bin/bun",
|
|
217
|
-
]) {
|
|
218
|
-
if (existsSync(p)) return p;
|
|
219
|
-
}
|
|
204
|
+
// Check per-runtime bin/ directory for backwards compat
|
|
205
|
+
const legacyBun = join(this.baseDir, "bin", "bun");
|
|
206
|
+
if (existsSync(legacyBun)) return legacyBun;
|
|
220
207
|
|
|
221
|
-
return
|
|
208
|
+
return findBun();
|
|
222
209
|
}
|
|
223
210
|
|
|
224
211
|
/**
|
|
@@ -301,12 +288,10 @@ export class EmbeddingRuntimeManager {
|
|
|
301
288
|
|
|
302
289
|
// Declared outside try so catch/finally can reference them for cleanup
|
|
303
290
|
const modelCacheDir = join(this.baseDir, "model-cache");
|
|
304
|
-
const existingBinDir = join(this.baseDir, "bin");
|
|
305
291
|
let tmpModelCache: string | null = null;
|
|
306
|
-
let tmpBinDir: string | null = null;
|
|
307
292
|
|
|
308
293
|
try {
|
|
309
|
-
// Step 1: Download npm packages
|
|
294
|
+
// Step 1: Download npm packages in parallel
|
|
310
295
|
const nodeModules = join(tmpDir, "node_modules");
|
|
311
296
|
const downloads: Promise<void>[] = [
|
|
312
297
|
downloadAndExtract(
|
|
@@ -331,13 +316,11 @@ export class EmbeddingRuntimeManager {
|
|
|
331
316
|
),
|
|
332
317
|
];
|
|
333
318
|
|
|
334
|
-
// Download bun binary if not already available on the system
|
|
335
|
-
if (!this.getBunPath()) {
|
|
336
|
-
downloads.push(this.downloadBunBinary(tmpDir, signal));
|
|
337
|
-
}
|
|
338
|
-
|
|
339
319
|
await Promise.all(downloads);
|
|
340
320
|
|
|
321
|
+
// Ensure bun is available (downloads to shared location if needed)
|
|
322
|
+
await ensureBun();
|
|
323
|
+
|
|
341
324
|
if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
|
|
342
325
|
|
|
343
326
|
log.info("npm packages downloaded, stripping non-platform binaries");
|
|
@@ -411,7 +394,7 @@ export class EmbeddingRuntimeManager {
|
|
|
411
394
|
);
|
|
412
395
|
|
|
413
396
|
// Step 6: Atomic swap — remove old install and rename temp to final
|
|
414
|
-
// Preserve model-cache
|
|
397
|
+
// Preserve model-cache/ and .gitignore
|
|
415
398
|
const hadModelCache = existsSync(modelCacheDir);
|
|
416
399
|
if (hadModelCache) {
|
|
417
400
|
tmpModelCache = join(
|
|
@@ -421,14 +404,6 @@ export class EmbeddingRuntimeManager {
|
|
|
421
404
|
renameSync(modelCacheDir, tmpModelCache);
|
|
422
405
|
}
|
|
423
406
|
|
|
424
|
-
// Preserve downloaded bun binary if it exists and we didn't just download a new one
|
|
425
|
-
const newBinDir = join(tmpDir, "bin");
|
|
426
|
-
const hadBinDir = existsSync(existingBinDir) && !existsSync(newBinDir);
|
|
427
|
-
if (hadBinDir) {
|
|
428
|
-
tmpBinDir = join(this.baseDir, `.bin-preserve-${Date.now()}`);
|
|
429
|
-
renameSync(existingBinDir, tmpBinDir);
|
|
430
|
-
}
|
|
431
|
-
|
|
432
407
|
// Remove old install (preserving dotfiles like .gitignore, .downloading, temp dirs)
|
|
433
408
|
for (const entry of readdirSync(this.baseDir)) {
|
|
434
409
|
if (entry.startsWith(".") || entry === tmpDir.split("/").pop())
|
|
@@ -446,11 +421,6 @@ export class EmbeddingRuntimeManager {
|
|
|
446
421
|
renameSync(tmpModelCache, modelCacheDir);
|
|
447
422
|
}
|
|
448
423
|
|
|
449
|
-
// Restore bun binary
|
|
450
|
-
if (tmpBinDir && existsSync(tmpBinDir)) {
|
|
451
|
-
renameSync(tmpBinDir, existingBinDir);
|
|
452
|
-
}
|
|
453
|
-
|
|
454
424
|
log.info(
|
|
455
425
|
{ runtimeVersion: RUNTIME_VERSION },
|
|
456
426
|
"Embedding runtime installed successfully",
|
|
@@ -468,13 +438,6 @@ export class EmbeddingRuntimeManager {
|
|
|
468
438
|
/* best effort */
|
|
469
439
|
}
|
|
470
440
|
}
|
|
471
|
-
if (tmpBinDir && existsSync(tmpBinDir) && !existsSync(existingBinDir)) {
|
|
472
|
-
try {
|
|
473
|
-
renameSync(tmpBinDir, existingBinDir);
|
|
474
|
-
} catch {
|
|
475
|
-
/* best effort */
|
|
476
|
-
}
|
|
477
|
-
}
|
|
478
441
|
log.error({ err }, "Failed to install embedding runtime");
|
|
479
442
|
throw err;
|
|
480
443
|
} finally {
|
|
@@ -482,72 +445,7 @@ export class EmbeddingRuntimeManager {
|
|
|
482
445
|
rmSync(tmpDir, { recursive: true, force: true });
|
|
483
446
|
if (tmpModelCache)
|
|
484
447
|
rmSync(tmpModelCache, { recursive: true, force: true });
|
|
485
|
-
if (tmpBinDir) rmSync(tmpBinDir, { recursive: true, force: true });
|
|
486
|
-
}
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
private async downloadBunBinary(
|
|
490
|
-
installDir: string,
|
|
491
|
-
signal?: AbortSignal,
|
|
492
|
-
): Promise<void> {
|
|
493
|
-
const os = platform();
|
|
494
|
-
const cpu = arch() === "arm64" ? "aarch64" : arch();
|
|
495
|
-
const target = `${os}-${cpu}`;
|
|
496
|
-
const url = `https://github.com/oven-sh/bun/releases/download/bun-v${BUN_VERSION}/bun-${target}.zip`;
|
|
497
|
-
|
|
498
|
-
log.info(
|
|
499
|
-
{ url, target, bunVersion: BUN_VERSION },
|
|
500
|
-
"Downloading bun binary",
|
|
501
|
-
);
|
|
502
|
-
|
|
503
|
-
const response = await fetch(url, {
|
|
504
|
-
signal,
|
|
505
|
-
redirect: "follow",
|
|
506
|
-
});
|
|
507
|
-
if (!response.ok) {
|
|
508
|
-
throw new Error(
|
|
509
|
-
`Failed to download bun: ${response.status} ${response.statusText}`,
|
|
510
|
-
);
|
|
511
|
-
}
|
|
512
|
-
|
|
513
|
-
const zipData = await response.arrayBuffer();
|
|
514
|
-
const binDir = join(installDir, "bin");
|
|
515
|
-
mkdirSync(binDir, { recursive: true });
|
|
516
|
-
|
|
517
|
-
const tmpZip = join(binDir, `bun-download-${Date.now()}.zip`);
|
|
518
|
-
writeFileSync(tmpZip, Buffer.from(zipData));
|
|
519
|
-
|
|
520
|
-
try {
|
|
521
|
-
// Extract zip
|
|
522
|
-
const proc = Bun.spawn({
|
|
523
|
-
cmd: ["unzip", "-o", tmpZip, "-d", binDir],
|
|
524
|
-
stdout: "ignore",
|
|
525
|
-
stderr: "pipe",
|
|
526
|
-
});
|
|
527
|
-
await proc.exited;
|
|
528
|
-
if (proc.exitCode !== 0) {
|
|
529
|
-
const stderr = await new Response(proc.stderr).text();
|
|
530
|
-
throw new Error(`Failed to extract bun zip: ${stderr}`);
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
// Move binary from bun-{target}/bun to bin/bun
|
|
534
|
-
const extractedBun = join(binDir, `bun-${target}`, "bun");
|
|
535
|
-
if (existsSync(extractedBun)) {
|
|
536
|
-
renameSync(extractedBun, join(binDir, "bun"));
|
|
537
|
-
rmSync(join(binDir, `bun-${target}`), { recursive: true, force: true });
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
// Make executable
|
|
541
|
-
chmodSync(join(binDir, "bun"), 0o755);
|
|
542
|
-
} finally {
|
|
543
|
-
try {
|
|
544
|
-
rmSync(tmpZip);
|
|
545
|
-
} catch {
|
|
546
|
-
/* ignore */
|
|
547
|
-
}
|
|
548
448
|
}
|
|
549
|
-
|
|
550
|
-
log.info("Bun binary downloaded successfully");
|
|
551
449
|
}
|
|
552
450
|
|
|
553
451
|
private readManifest(): VersionManifest | null {
|
|
@@ -5,8 +5,8 @@ import { createHash } from "node:crypto";
|
|
|
5
5
|
*
|
|
6
6
|
* Format: sha256(`${scopeId}|${kind}|${subject}|${statement}`)
|
|
7
7
|
*
|
|
8
|
-
* All writers (memory_manage save/update ops, items-extractor,
|
|
9
|
-
*
|
|
8
|
+
* All writers (memory_manage save/update ops, items-extractor,
|
|
9
|
+
* gmail-analyze-style) MUST use this function so the
|
|
10
10
|
* fingerprint scheme stays consistent and deduplication works correctly.
|
|
11
11
|
*/
|
|
12
12
|
export function computeMemoryFingerprint(
|