@vellumai/assistant 0.4.43 → 0.4.45
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/.prettierignore +4 -0
- package/ARCHITECTURE.md +46 -44
- package/README.md +15 -16
- package/bun.lock +10 -35
- package/docs/architecture/integrations.md +102 -215
- package/docs/architecture/keychain-broker.md +1 -1
- package/docs/architecture/memory.md +2 -2
- package/docs/architecture/scheduling.md +1 -1
- package/docs/architecture/security.md +11 -11
- package/docs/error-handling.md +1 -1
- package/docs/trusted-contact-access.md +3 -3
- package/drizzle/meta/0000_snapshot.json +34 -100
- package/drizzle/meta/_journal.json +1 -1
- package/drizzle.config.ts +4 -4
- package/package.json +3 -2
- package/scripts/capture-x-graphql.ts +237 -141
- package/scripts/generate-bundled-tool-registry.ts +223 -0
- package/src/__tests__/access-request-decision.test.ts +0 -1
- package/src/__tests__/actor-token-service.test.ts +23 -24
- package/src/__tests__/agent-loop.test.ts +0 -131
- package/src/__tests__/always-loaded-tools-guard.test.ts +71 -0
- package/src/__tests__/amazon-cdp-integration.test.ts +11 -9
- package/src/__tests__/approval-primitive.test.ts +0 -1
- package/src/__tests__/approval-routes-http.test.ts +11 -3
- package/src/__tests__/asset-materialize-tool.test.ts +0 -1
- package/src/__tests__/asset-search-tool.test.ts +0 -1
- package/src/__tests__/assistant-attachment-directive.test.ts +1 -1
- package/src/__tests__/assistant-events-sse-hardening.test.ts +0 -1
- package/src/__tests__/assistant-feature-flag-guardrails.test.ts +0 -2
- package/src/__tests__/assistant-feature-flags-integration.test.ts +70 -18
- package/src/__tests__/assistant-id-boundary-guard.test.ts +6 -6
- package/src/__tests__/attachments-store.test.ts +0 -1
- package/src/__tests__/avatar-e2e.test.ts +74 -115
- package/src/__tests__/avatar-router.test.ts +25 -62
- package/src/__tests__/browser-manager.test.ts +24 -0
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +4 -3
- package/src/__tests__/browser-skill-endstate.test.ts +8 -11
- package/src/__tests__/btw-routes.test.ts +326 -0
- package/src/__tests__/bundled-asset.test.ts +1 -1
- package/src/__tests__/bundled-skill-retrieval-guard.test.ts +23 -9
- package/src/__tests__/call-controller.test.ts +0 -1
- package/src/__tests__/call-conversation-messages.test.ts +0 -1
- package/src/__tests__/call-domain.test.ts +0 -1
- package/src/__tests__/call-pointer-messages.test.ts +0 -1
- package/src/__tests__/call-recovery.test.ts +0 -1
- package/src/__tests__/call-routes-http.test.ts +0 -1
- package/src/__tests__/call-store.test.ts +0 -1
- package/src/__tests__/canonical-guardian-store.test.ts +0 -1
- package/src/__tests__/channel-approval-routes.test.ts +1 -1
- package/src/__tests__/channel-approvals.test.ts +1 -1
- package/src/__tests__/channel-delivery-store.test.ts +0 -1
- package/src/__tests__/channel-guardian.test.ts +5 -7
- package/src/__tests__/channel-retry-sweep.test.ts +0 -1
- package/src/__tests__/checker.test.ts +32 -36
- package/src/__tests__/compaction.benchmark.test.ts +16 -14
- package/src/__tests__/computer-use-session-lifecycle.test.ts +10 -11
- package/src/__tests__/computer-use-session-working-dir.test.ts +2 -6
- package/src/__tests__/computer-use-skill-lifecycle-cleanup.test.ts +2 -5
- package/src/__tests__/computer-use-tools.test.ts +35 -31
- package/src/__tests__/config-schema.test.ts +11 -15
- package/src/__tests__/config-watcher.test.ts +0 -1
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +0 -1
- package/src/__tests__/conflict-store.test.ts +0 -1
- package/src/__tests__/connection-policy.test.ts +4 -7
- package/src/__tests__/contacts-tools.test.ts +0 -1
- package/src/__tests__/context-memory-e2e.test.ts +2 -4
- package/src/__tests__/context-overflow-reducer.test.ts +2 -4
- package/src/__tests__/context-window-manager.test.ts +147 -60
- package/src/__tests__/contradiction-checker.test.ts +0 -1
- package/src/__tests__/conversation-attention-store.test.ts +0 -1
- package/src/__tests__/conversation-attention-telegram.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +2 -2
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +31 -7
- package/src/__tests__/conversation-routes-slash-commands.test.ts +381 -0
- package/src/__tests__/conversation-store.test.ts +0 -1
- package/src/__tests__/conversation-unread-route.test.ts +1 -2
- package/src/__tests__/credential-security-invariants.test.ts +8 -8
- package/src/__tests__/cross-provider-web-search.test.ts +353 -0
- package/src/__tests__/daemon-assistant-events.test.ts +6 -7
- package/src/__tests__/db-schedule-syntax-migration.test.ts +15 -3
- package/src/__tests__/delete-managed-skill-tool.test.ts +5 -9
- package/src/__tests__/deterministic-verification-control-plane.test.ts +0 -1
- package/src/__tests__/diagnostics-export.test.ts +189 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +0 -1
- package/src/__tests__/emit-signal-routing-intent.test.ts +3 -3
- package/src/__tests__/entity-extractor.test.ts +0 -1
- package/src/__tests__/entity-search.test.ts +0 -1
- package/src/__tests__/ephemeral-permissions.test.ts +2 -4
- package/src/__tests__/error-handler-friendly-messages.test.ts +46 -0
- package/src/__tests__/file-read-tool.test.ts +86 -0
- package/src/__tests__/followup-tools.test.ts +0 -1
- package/src/__tests__/frontmatter.test.ts +77 -34
- package/src/__tests__/gateway-only-enforcement.test.ts +0 -1
- package/src/__tests__/gateway-only-guard.test.ts +1 -1
- package/src/__tests__/guardian-action-conversation-turn.test.ts +0 -1
- package/src/__tests__/guardian-action-followup-executor.test.ts +0 -1
- package/src/__tests__/guardian-action-followup-store.test.ts +0 -1
- package/src/__tests__/guardian-action-grant-mint-consume.test.ts +0 -1
- package/src/__tests__/guardian-action-late-reply.test.ts +0 -1
- package/src/__tests__/guardian-action-store.test.ts +0 -1
- package/src/__tests__/guardian-action-sweep.test.ts +0 -1
- package/src/__tests__/guardian-decision-primitive-canonical.test.ts +0 -1
- package/src/__tests__/guardian-dispatch.test.ts +1 -2
- package/src/__tests__/guardian-grant-minting.test.ts +1 -1
- package/src/__tests__/guardian-outbound-http.test.ts +0 -1
- package/src/__tests__/guardian-principal-id-roundtrip.test.ts +0 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +1 -1
- package/src/__tests__/guardian-routing-state.test.ts +0 -1
- package/src/__tests__/guardian-verification-voice-binding.test.ts +0 -1
- package/src/__tests__/guardian-verify-setup-skill-regression.test.ts +3 -5
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +28 -426
- package/src/__tests__/host-bash-proxy.test.ts +335 -0
- package/src/__tests__/host-file-proxy.test.ts +374 -0
- package/src/__tests__/host-shell-tool.test.ts +147 -1
- package/src/__tests__/http-user-message-parity.test.ts +361 -0
- package/src/__tests__/inbound-invite-redemption.test.ts +0 -1
- package/src/__tests__/integration-status.test.ts +3 -8
- package/src/__tests__/intent-routing.test.ts +7 -46
- package/src/__tests__/invite-redemption-service.test.ts +0 -1
- package/src/__tests__/invite-routes-http.test.ts +0 -1
- package/src/__tests__/llm-usage-store.test.ts +0 -1
- package/src/__tests__/managed-avatar-client.test.ts +101 -55
- package/src/__tests__/managed-skill-lifecycle.test.ts +9 -18
- package/src/__tests__/managed-store.test.ts +94 -21
- package/src/__tests__/media-reuse-story.e2e.test.ts +0 -1
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +2 -4
- package/src/__tests__/memory-lifecycle-e2e.test.ts +0 -1
- package/src/__tests__/memory-recall-quality.test.ts +0 -1
- package/src/__tests__/memory-regressions.experimental.test.ts +0 -1
- package/src/__tests__/memory-regressions.test.ts +0 -1
- package/src/__tests__/memory-retrieval.benchmark.test.ts +0 -1
- package/src/__tests__/memory-upsert-concurrency.test.ts +0 -1
- package/src/__tests__/messaging-send-tool.test.ts +35 -0
- package/src/__tests__/messaging-skill-split.test.ts +138 -0
- package/src/__tests__/migration-cross-version-compatibility.test.ts +0 -1
- package/src/__tests__/migration-export-http.test.ts +2 -3
- package/src/__tests__/migration-import-commit-http.test.ts +1 -2
- package/src/__tests__/migration-import-preflight-http.test.ts +1 -2
- package/src/__tests__/migration-validate-http.test.ts +1 -2
- package/src/__tests__/native-web-search.test.ts +475 -0
- package/src/__tests__/navigate-settings-tab.test.ts +84 -0
- package/src/__tests__/non-member-access-request.test.ts +0 -1
- package/src/__tests__/notification-broadcaster.test.ts +15 -15
- package/src/__tests__/notification-decision-strategy.test.ts +6 -6
- package/src/__tests__/notification-deep-link.test.ts +7 -7
- package/src/__tests__/notification-guardian-path.test.ts +2 -3
- package/src/__tests__/notification-telegram-adapter.test.ts +1 -1
- package/src/__tests__/notification-thread-candidates.test.ts +4 -4
- package/src/__tests__/onboarding-starter-tasks.test.ts +0 -1
- package/src/__tests__/onboarding-template-contract.test.ts +0 -10
- package/src/__tests__/playbook-execution.test.ts +0 -1
- package/src/__tests__/playbook-tools.test.ts +0 -1
- package/src/__tests__/profile-compiler.test.ts +0 -1
- package/src/__tests__/provider-fail-open-selection.test.ts +12 -2
- package/src/__tests__/provider-managed-proxy-integration.test.ts +25 -0
- package/src/__tests__/qdrant-collection-migration.test.ts +223 -0
- package/src/__tests__/recording-handler.test.ts +30 -94
- package/src/__tests__/registry.test.ts +28 -35
- package/src/__tests__/relay-server.test.ts +0 -1
- package/src/__tests__/ride-shotgun-handler.test.ts +4 -20
- package/src/__tests__/runtime-attachment-metadata.test.ts +0 -1
- package/src/__tests__/runtime-events-sse-parity.test.ts +3 -4
- package/src/__tests__/runtime-events-sse.test.ts +0 -1
- package/src/__tests__/sandbox-diagnostics.test.ts +0 -1
- package/src/__tests__/scaffold-managed-skill-tool.test.ts +30 -28
- package/src/__tests__/schedule-store.test.ts +441 -1
- package/src/__tests__/schedule-tools.test.ts +468 -7
- package/src/__tests__/scheduler-recurrence.test.ts +196 -23
- package/src/__tests__/scoped-approval-grants.test.ts +0 -1
- package/src/__tests__/scoped-grant-security-matrix.test.ts +0 -1
- package/src/__tests__/secret-prompt-log-hygiene.test.ts +6 -3
- package/src/__tests__/secret-response-routing.test.ts +4 -1
- package/src/__tests__/send-endpoint-busy.test.ts +14 -5
- package/src/__tests__/send-notification-tool.test.ts +0 -7
- package/src/__tests__/sequence-store.test.ts +0 -1
- package/src/__tests__/server-history-render.test.ts +1 -2
- package/src/__tests__/session-abort-tool-results.test.ts +0 -1
- package/src/__tests__/session-agent-loop.test.ts +46 -6
- package/src/__tests__/session-confirmation-signals.test.ts +7 -46
- package/src/__tests__/session-conflict-gate.test.ts +2 -6
- package/src/__tests__/session-error.test.ts +5 -14
- package/src/__tests__/session-init.benchmark.test.ts +3 -5
- package/src/__tests__/session-load-history-repair.test.ts +0 -1
- package/src/__tests__/session-media-retry.test.ts +12 -74
- package/src/__tests__/session-pre-run-repair.test.ts +0 -1
- package/src/__tests__/session-profile-injection.test.ts +2 -6
- package/src/__tests__/session-provider-retry-repair.test.ts +2 -6
- package/src/__tests__/session-queue.test.ts +94 -139
- package/src/__tests__/session-skill-tools.test.ts +115 -115
- package/src/__tests__/session-slash-known.test.ts +0 -1
- package/src/__tests__/session-slash-queue.test.ts +0 -1
- package/src/__tests__/session-slash-unknown.test.ts +0 -1
- package/src/__tests__/session-surfaces-task-progress.test.ts +34 -0
- package/src/__tests__/session-usage.test.ts +0 -1
- package/src/__tests__/session-workspace-cache-state.test.ts +2 -6
- package/src/__tests__/session-workspace-injection.test.ts +2 -6
- package/src/__tests__/session-workspace-tool-tracking.test.ts +2 -6
- package/src/__tests__/skill-feature-flags-integration.test.ts +180 -184
- package/src/__tests__/skill-feature-flags.test.ts +125 -18
- package/src/__tests__/skill-load-feature-flag.test.ts +1 -2
- package/src/__tests__/skill-load-tool.test.ts +194 -2
- package/src/__tests__/skill-projection-feature-flag.test.ts +27 -16
- package/src/__tests__/skill-projection.benchmark.test.ts +15 -14
- package/src/__tests__/skills.test.ts +14 -53
- package/src/__tests__/slack-channel-config.test.ts +0 -1
- package/src/__tests__/slack-inbound-verification.test.ts +0 -1
- package/src/__tests__/slack-skill.test.ts +1 -1
- package/src/__tests__/starter-task-flow.test.ts +9 -19
- package/src/__tests__/subagent-tools.test.ts +2 -2
- package/src/__tests__/system-prompt.test.ts +7 -7
- package/src/__tests__/task-compiler.test.ts +0 -1
- package/src/__tests__/task-management-tools.test.ts +0 -1
- package/src/__tests__/task-memory-cleanup.test.ts +0 -1
- package/src/__tests__/task-runner.test.ts +0 -1
- package/src/__tests__/task-scheduler.test.ts +0 -1
- package/src/__tests__/terminal-tools.test.ts +0 -1
- package/src/__tests__/test-support/computer-use-skill-harness.ts +2 -4
- package/src/__tests__/thread-seed-composer.test.ts +5 -5
- package/src/__tests__/tool-approval-handler.test.ts +0 -1
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +0 -1
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +0 -1
- package/src/__tests__/tool-executor.test.ts +8 -86
- package/src/__tests__/tool-grant-request-escalation.test.ts +0 -1
- package/src/__tests__/tool-notification-listener.test.ts +1 -1
- package/src/__tests__/tool-preview-lifecycle.test.ts +416 -0
- package/src/__tests__/trust-store.test.ts +84 -8
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +0 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +0 -1
- package/src/__tests__/trusted-contact-lifecycle-notifications.test.ts +0 -1
- package/src/__tests__/trusted-contact-multichannel.test.ts +0 -1
- package/src/__tests__/trusted-contact-verification.test.ts +0 -1
- package/src/__tests__/twilio-provider.test.ts +0 -1
- package/src/__tests__/twilio-routes.test.ts +0 -1
- package/src/__tests__/{request-file-tool.test.ts → ui-file-upload-surface.test.ts} +11 -72
- package/src/__tests__/update-bulletin.test.ts +0 -1
- package/src/__tests__/usage-cache-backfill-migration.test.ts +0 -1
- package/src/__tests__/usage-routes.test.ts +0 -1
- package/src/__tests__/verification-control-plane-policy.test.ts +4 -4
- package/src/__tests__/voice-invite-redemption.test.ts +0 -1
- package/src/__tests__/voice-scoped-grant-consumer.test.ts +0 -1
- package/src/__tests__/voice-session-bridge.test.ts +9 -1
- package/src/__tests__/web-fetch.test.ts +57 -0
- package/src/__tests__/workspace-git-service.test.ts +5 -14
- package/src/__tests__/workspace-policy.test.ts +0 -1
- package/src/agent/loop.ts +22 -34
- package/src/bundler/bundle-signer.ts +4 -4
- package/src/calls/call-controller.ts +1 -1
- package/src/calls/relay-server.ts +1 -1
- package/src/calls/twilio-rest.ts +1 -1
- package/src/calls/voice-session-bridge.ts +3 -1
- package/src/cli/__tests__/notifications.test.ts +3 -4
- package/src/cli/commands/map.ts +2 -6
- package/src/cli/commands/mcp.ts +73 -15
- package/src/cli/commands/notifications.ts +4 -4
- package/src/cli/commands/sessions.ts +9 -1
- package/src/cli/commands/skills.ts +6 -10
- package/src/cli/http-client.ts +2 -3
- package/src/cli/main-screen.tsx +10 -10
- package/src/cli/program.ts +0 -4
- package/src/cli/reference.ts +0 -2
- package/src/cli.ts +15 -9
- package/src/config/__tests__/bundled-tool-registry-guard.test.ts +120 -0
- package/src/config/bundled-skills/_shared/CLI_RETRIEVAL_PATTERN.md +11 -0
- package/src/config/bundled-skills/app-builder/SKILL.md +6 -7
- package/src/config/bundled-skills/app-builder/TOOLS.json +0 -4
- package/src/config/bundled-skills/browser/SKILL.md +6 -1
- package/src/config/bundled-skills/chatgpt-import/SKILL.md +5 -1
- package/src/config/bundled-skills/claude-code/SKILL.md +5 -1
- package/src/config/bundled-skills/computer-use/SKILL.md +6 -1
- package/src/config/bundled-skills/computer-use/TOOLS.json +6 -69
- package/src/config/bundled-skills/computer-use/tools/computer-use-click.ts +10 -1
- package/src/config/bundled-skills/contacts/SKILL.md +10 -1
- package/src/config/bundled-skills/contacts/TOOLS.json +35 -0
- package/src/config/bundled-skills/{messaging → contacts}/tools/google-contacts.ts +9 -2
- package/src/config/bundled-skills/document/SKILL.md +4 -1
- package/src/config/bundled-skills/doordash/SKILL.md +8 -2
- package/src/config/bundled-skills/doordash/__tests__/doordash-session.test.ts +1 -82
- package/src/config/bundled-skills/doordash/doordash-cli.ts +17 -28
- package/src/config/bundled-skills/doordash/lib/session.ts +21 -17
- package/src/config/bundled-skills/doordash/lib/shared/platform.ts +4 -1
- package/src/config/bundled-skills/followups/SKILL.md +4 -1
- package/src/config/bundled-skills/gmail/SKILL.md +180 -0
- package/src/config/bundled-skills/gmail/TOOLS.json +506 -0
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +149 -0
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +110 -0
- package/src/config/bundled-skills/{messaging → gmail}/tools/gmail-draft.ts +1 -1
- package/src/config/bundled-skills/{messaging → gmail}/tools/gmail-filters.ts +1 -1
- package/src/config/bundled-skills/{messaging → gmail}/tools/gmail-follow-up.ts +1 -1
- package/src/config/bundled-skills/{messaging → gmail}/tools/gmail-forward.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +50 -0
- package/src/config/bundled-skills/{messaging → gmail}/tools/gmail-outreach-scan.ts +8 -90
- package/src/config/bundled-skills/{messaging → gmail}/tools/gmail-send-draft.ts +1 -1
- package/src/config/bundled-skills/{messaging → gmail}/tools/gmail-sender-digest.ts +2 -2
- package/src/config/bundled-skills/{messaging → gmail}/tools/gmail-trash.ts +1 -1
- package/src/config/bundled-skills/{messaging → gmail}/tools/gmail-unsubscribe.ts +1 -1
- package/src/config/bundled-skills/{messaging → gmail}/tools/gmail-vacation.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/shared.ts +47 -0
- package/src/config/bundled-skills/google-calendar/SKILL.md +5 -1
- package/src/config/bundled-skills/image-studio/SKILL.md +5 -1
- package/src/config/bundled-skills/knowledge-graph/SKILL.md +4 -1
- package/src/config/bundled-skills/media-processing/SKILL.md +7 -13
- package/src/config/bundled-skills/media-processing/TOOLS.json +0 -22
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +12 -1
- package/src/config/bundled-skills/messaging/SKILL.md +23 -139
- package/src/config/bundled-skills/messaging/TOOLS.json +33 -1215
- package/src/config/bundled-skills/messaging/tools/gmail-mime-helpers.ts +42 -0
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +165 -2
- package/src/config/bundled-skills/messaging/tools/messaging-sender-digest.ts +1 -13
- package/src/config/bundled-skills/messaging/tools/shared.ts +81 -34
- package/src/config/bundled-skills/notifications/SKILL.md +5 -1
- package/src/config/bundled-skills/orchestration/SKILL.md +30 -0
- package/src/config/bundled-skills/orchestration/TOOLS.json +35 -0
- package/src/config/bundled-skills/{reminder/tools/reminder-cancel.ts → orchestration/tools/swarm-delegate.ts} +3 -3
- package/src/config/bundled-skills/phone-calls/SKILL.md +9 -1
- package/src/config/bundled-skills/playbooks/SKILL.md +4 -1
- package/src/config/bundled-skills/schedule/SKILL.md +70 -9
- package/src/config/bundled-skills/schedule/TOOLS.json +38 -6
- package/src/config/bundled-skills/screen-watch/SKILL.md +28 -0
- package/src/config/bundled-skills/screen-watch/TOOLS.json +35 -0
- package/src/config/bundled-skills/{reminder/tools/reminder-create.ts → screen-watch/tools/start-screen-watch.ts} +3 -3
- package/src/config/bundled-skills/sequences/SKILL.md +47 -0
- package/src/config/bundled-skills/sequences/TOOLS.json +340 -0
- package/src/config/bundled-skills/sequences/tools/sequence-update.ts +128 -0
- package/src/config/bundled-skills/sequences/tools/shared.ts +9 -0
- package/src/config/bundled-skills/settings/SKILL.md +12 -0
- package/src/config/bundled-skills/settings/TOOLS.json +112 -0
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +43 -0
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +52 -0
- package/src/config/bundled-skills/{computer-use/tools/computer-use-right-click.ts → settings/tools/set-avatar.ts} +2 -6
- package/src/{tools/system/voice-config.ts → config/bundled-skills/settings/tools/voice-config-update.ts} +59 -96
- package/src/config/bundled-skills/skill-management/SKILL.md +18 -0
- package/src/config/bundled-skills/skill-management/TOOLS.json +90 -0
- package/src/config/bundled-skills/{computer-use/tools/computer-use-double-click.ts → skill-management/tools/delete-managed.ts} +2 -6
- package/src/config/bundled-skills/skill-management/tools/scaffold-managed.ts +12 -0
- package/src/config/bundled-skills/slack/SKILL.md +5 -1
- package/src/config/bundled-skills/subagent/SKILL.md +4 -1
- package/src/config/bundled-skills/tasks/SKILL.md +5 -2
- package/src/config/bundled-skills/transcribe/SKILL.md +4 -1
- package/src/config/bundled-skills/watcher/SKILL.md +4 -1
- package/src/config/bundled-tool-registry.ts +118 -107
- package/src/config/env.ts +5 -2
- package/src/config/feature-flag-registry.json +33 -9
- package/src/config/loader.ts +10 -2
- package/src/config/schema.ts +19 -16
- package/src/config/schemas/inference.ts +12 -22
- package/src/config/schemas/memory-storage.ts +19 -1
- package/src/config/schemas/platform.ts +0 -16
- package/src/config/skill-state.ts +11 -8
- package/src/config/skills.ts +83 -32
- package/src/context/token-estimator.ts +11 -0
- package/src/context/window-manager.ts +180 -151
- package/src/daemon/computer-use-session.ts +11 -43
- package/src/daemon/daemon-control.ts +4 -1
- package/src/daemon/handlers/config-channels.ts +5 -9
- package/src/daemon/handlers/config-ingress.ts +0 -4
- package/src/daemon/handlers/config-model.ts +7 -13
- package/src/daemon/handlers/config-telegram.ts +4 -8
- package/src/daemon/handlers/config-voice.ts +2 -5
- package/src/daemon/handlers/dictation.ts +2 -12
- package/src/daemon/handlers/identity.ts +0 -105
- package/src/daemon/handlers/recording.ts +3 -23
- package/src/daemon/handlers/session-history.ts +42 -10
- package/src/daemon/handlers/sessions.ts +53 -72
- package/src/daemon/handlers/shared.ts +7 -28
- package/src/daemon/handlers/skills.ts +31 -27
- package/src/daemon/host-bash-proxy.ts +148 -0
- package/src/daemon/host-file-proxy.ts +135 -0
- package/src/daemon/lifecycle.ts +53 -41
- package/src/daemon/mcp-reload-service.ts +123 -0
- package/src/daemon/message-protocol.ts +6 -0
- package/src/daemon/message-types/apps.ts +0 -25
- package/src/daemon/message-types/browser.ts +1 -1
- package/src/daemon/message-types/computer-use.ts +1 -4
- package/src/daemon/message-types/guardian-actions.ts +1 -1
- package/src/daemon/message-types/host-bash.ts +18 -0
- package/src/daemon/message-types/host-file.ts +44 -0
- package/src/daemon/message-types/integrations.ts +1 -73
- package/src/daemon/message-types/messages.ts +15 -0
- package/src/daemon/message-types/schedules.ts +11 -27
- package/src/daemon/message-types/sessions.ts +8 -2
- package/src/daemon/message-types/settings.ts +1 -1
- package/src/daemon/message-types/shared.ts +1 -1
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/ride-shotgun-handler.ts +35 -43
- package/src/daemon/seed-files.ts +3 -27
- package/src/daemon/server.ts +45 -28
- package/src/daemon/session-agent-loop-handlers.ts +72 -9
- package/src/daemon/session-agent-loop.ts +97 -66
- package/src/daemon/session-attachments.ts +1 -1
- package/src/daemon/session-error.ts +17 -16
- package/src/daemon/session-lifecycle.ts +20 -1
- package/src/daemon/session-media-retry.ts +1 -15
- package/src/daemon/session-messaging.ts +14 -6
- package/src/daemon/session-process.ts +36 -7
- package/src/daemon/session-queue-manager.ts +62 -103
- package/src/daemon/session-runtime-assembly.ts +27 -7
- package/src/daemon/session-skill-tools.ts +12 -11
- package/src/daemon/session-slash.ts +7 -0
- package/src/daemon/session-surfaces.ts +192 -118
- package/src/daemon/session-tool-setup.ts +146 -6
- package/src/daemon/session.ts +75 -37
- package/src/errors.ts +0 -2
- package/src/export/formatter.ts +6 -0
- package/src/mcp/mcp-oauth-provider.ts +1 -3
- package/src/media/avatar-router.ts +20 -28
- package/src/media/avatar-types.ts +7 -14
- package/src/media/managed-avatar-client.ts +70 -34
- package/src/memory/app-store.ts +0 -18
- package/src/memory/conversation-title-service.ts +1 -2
- package/src/memory/db-init.ts +16 -0
- package/src/memory/embedding-backend.ts +129 -27
- package/src/memory/embedding-gemini.test.ts +256 -0
- package/src/memory/embedding-gemini.ts +47 -13
- package/src/memory/embedding-local.ts +14 -2
- package/src/memory/embedding-ollama.ts +15 -2
- package/src/memory/embedding-openai.ts +15 -2
- package/src/memory/embedding-types.test.ts +116 -0
- package/src/memory/embedding-types.ts +61 -0
- package/src/memory/fingerprint.ts +1 -1
- package/src/memory/indexer.ts +25 -1
- package/src/memory/job-handlers/embedding.test.ts +258 -0
- package/src/memory/job-handlers/embedding.ts +81 -1
- package/src/memory/job-handlers/index-maintenance.ts +35 -1
- package/src/memory/job-handlers/media-processing.ts +11 -1
- package/src/memory/job-utils.ts +21 -6
- package/src/memory/jobs-store.ts +5 -1
- package/src/memory/jobs-worker.ts +8 -0
- package/src/memory/message-content.ts +66 -0
- package/src/memory/migrations/100-core-tables.ts +1 -31
- package/src/memory/migrations/104-core-indexes.ts +0 -11
- package/src/memory/migrations/145-drop-accounts-table.ts +19 -0
- package/src/memory/migrations/146-schedule-oneshot-routing.ts +94 -0
- package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +129 -0
- package/src/memory/migrations/148-drop-reminders-table.ts +18 -0
- package/src/memory/migrations/index.ts +4 -0
- package/src/memory/migrations/registry.ts +19 -0
- package/src/memory/qdrant-client.ts +158 -43
- package/src/memory/retriever.test.ts +0 -1
- package/src/memory/retriever.ts +12 -2
- package/src/memory/schema/infrastructure.ts +5 -37
- package/src/memory/search/formatting.ts +34 -9
- package/src/memory/search/semantic.ts +57 -2
- package/src/memory/search/types.ts +2 -1
- package/src/notifications/AGENTS.md +2 -2
- package/src/notifications/README.md +59 -58
- package/src/notifications/adapters/macos.ts +1 -1
- package/src/notifications/broadcaster.ts +5 -5
- package/src/notifications/copy-composer.ts +1 -1
- package/src/notifications/decision-engine.ts +2 -2
- package/src/notifications/destination-resolver.ts +2 -2
- package/src/notifications/emit-signal.ts +8 -8
- package/src/notifications/signal.ts +1 -1
- package/src/notifications/thread-seed-composer.ts +1 -1
- package/src/oauth/connect-orchestrator.ts +1 -1
- package/src/oauth/token-persistence.ts +1 -1
- package/src/permissions/checker.ts +12 -1
- package/src/permissions/defaults.ts +13 -17
- package/src/permissions/trust-store.ts +37 -0
- package/src/permissions/workspace-policy.ts +0 -1
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +11 -0
- package/src/prompts/computer-use-prompt.ts +1 -1
- package/src/prompts/system-prompt.ts +33 -35
- package/src/prompts/templates/BOOTSTRAP.md +0 -3
- package/src/prompts/templates/SOUL.md +1 -2
- package/src/prompts/templates/UPDATES.md +16 -7
- package/src/providers/anthropic/client.ts +87 -33
- package/src/providers/gemini/client.ts +6 -0
- package/src/providers/managed-proxy/constants.ts +5 -0
- package/src/providers/openai/client.ts +15 -0
- package/src/providers/registry.ts +4 -6
- package/src/providers/types.ts +24 -2
- package/src/runtime/AGENTS.md +18 -0
- package/src/runtime/assistant-event-hub.ts +2 -3
- package/src/runtime/assistant-event.ts +4 -4
- package/src/runtime/auth/__tests__/context.test.ts +5 -5
- package/src/runtime/auth/__tests__/credential-service.test.ts +0 -1
- package/src/runtime/auth/__tests__/guard-tests.test.ts +3 -2
- package/src/runtime/auth/__tests__/{ipc-auth-context.test.ts → local-auth-context.test.ts} +21 -21
- package/src/runtime/auth/__tests__/route-policy.test.ts +2 -2
- package/src/runtime/auth/__tests__/scopes.test.ts +9 -8
- package/src/runtime/auth/__tests__/subject.test.ts +8 -8
- package/src/runtime/auth/__tests__/token-service.test.ts +0 -1
- package/src/runtime/auth/route-policy.ts +8 -8
- package/src/runtime/auth/scopes.ts +2 -1
- package/src/runtime/auth/subject.ts +4 -4
- package/src/runtime/auth/token-service.ts +1 -24
- package/src/runtime/auth/types.ts +3 -3
- package/src/runtime/guardian-action-followup-executor.ts +1 -1
- package/src/runtime/guardian-action-grant-minter.ts +1 -1
- package/src/runtime/guardian-action-service.ts +3 -3
- package/src/runtime/http-server.ts +15 -2
- package/src/runtime/http-types.ts +10 -0
- package/src/runtime/invite-service.ts +3 -3
- package/src/runtime/local-actor-identity.ts +17 -22
- package/src/runtime/middleware/error-handler.ts +14 -1
- package/src/runtime/pending-interactions.ts +21 -9
- package/src/runtime/routes/app-management-routes.ts +63 -67
- package/src/runtime/routes/approval-routes.ts +1 -3
- package/src/runtime/routes/brain-graph/brain-graph.html +1845 -0
- package/src/runtime/routes/brain-graph-routes.ts +4 -42
- package/src/runtime/routes/btw-routes.ts +155 -0
- package/src/runtime/routes/computer-use-routes.ts +77 -31
- package/src/runtime/routes/conversation-routes.ts +234 -47
- package/src/runtime/routes/diagnostics-routes.ts +154 -43
- package/src/runtime/routes/documents-routes.ts +2 -2
- package/src/runtime/routes/global-search-routes.ts +1 -1
- package/src/runtime/routes/host-bash-routes.ts +83 -0
- package/src/runtime/routes/host-file-routes.ts +79 -0
- package/src/runtime/routes/integrations/slack/share.ts +1 -1
- package/src/runtime/routes/log-export-routes.ts +120 -0
- package/src/runtime/routes/mcp-routes.ts +20 -0
- package/src/runtime/routes/migration-routes.ts +3 -3
- package/src/runtime/routes/pairing-routes.ts +1 -1
- package/src/runtime/routes/recording-routes.ts +6 -4
- package/src/runtime/routes/schedule-routes.ts +31 -5
- package/src/runtime/routes/session-management-routes.ts +2 -6
- package/src/runtime/routes/session-query-routes.ts +18 -15
- package/src/runtime/routes/settings-routes.ts +7 -351
- package/src/runtime/routes/skills-routes.ts +7 -6
- package/src/runtime/routes/subagents-routes.ts +4 -10
- package/src/runtime/routes/surface-action-routes.ts +3 -14
- package/src/runtime/routes/surface-content-routes.ts +22 -5
- package/src/runtime/routes/work-items-routes.ts +21 -25
- package/src/runtime/routes/workspace-routes.test.ts +3 -3
- package/src/runtime/routes/workspace-utils.ts +1 -1
- package/src/runtime/telegram-streaming-delivery.ts +3 -0
- package/src/runtime/verification-outbound-actions.ts +2 -2
- package/src/schedule/integration-status.ts +0 -6
- package/src/schedule/schedule-store.ts +234 -43
- package/src/schedule/scheduler.ts +73 -74
- package/src/security/oauth2.ts +1 -1
- package/src/sequence/store.ts +12 -2
- package/src/skills/frontmatter.ts +19 -77
- package/src/skills/managed-store.ts +11 -2
- package/src/subagent/manager.ts +5 -3
- package/src/tasks/ephemeral-permissions.ts +3 -5
- package/src/tools/AGENTS.md +37 -0
- package/src/tools/apps/executors.ts +0 -6
- package/src/tools/browser/browser-manager.ts +17 -11
- package/src/tools/browser/jit-auth.ts +4 -1
- package/src/tools/claude-code/claude-code.ts +1 -1
- package/src/tools/computer-use/definitions.ts +48 -60
- package/src/tools/document/document-tool.ts +6 -6
- package/src/tools/document/editor-template.ts +10 -8
- package/src/tools/filesystem/edit.ts +2 -1
- package/src/tools/filesystem/read.ts +20 -2
- package/src/tools/filesystem/write.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +17 -1
- package/src/tools/host-filesystem/read.ts +16 -1
- package/src/tools/host-filesystem/write.ts +15 -1
- package/src/tools/host-terminal/host-shell.ts +24 -0
- package/src/tools/memory/definitions.ts +45 -81
- package/src/tools/memory/handlers.test.ts +0 -1
- package/src/tools/memory/handlers.ts +1 -1
- package/src/tools/memory/register.ts +26 -60
- package/src/tools/network/script-proxy/session-manager.ts +6 -8
- package/src/tools/network/web-fetch.ts +7 -1
- package/src/tools/network/web-search.ts +2 -1
- package/src/tools/registry.ts +23 -0
- package/src/tools/schedule/create.ts +113 -5
- package/src/tools/schedule/list.ts +57 -15
- package/src/tools/schedule/update.ts +73 -3
- package/src/tools/shared/filesystem/image-read.ts +192 -0
- package/src/tools/side-effects.ts +1 -7
- package/src/tools/skills/delete-managed.ts +27 -64
- package/src/tools/skills/execute.ts +54 -0
- package/src/tools/skills/load.ts +127 -5
- package/src/tools/skills/scaffold-managed.ts +93 -172
- package/src/tools/subagent/message.ts +0 -7
- package/src/tools/subagent/spawn.ts +1 -1
- package/src/tools/swarm/delegate.ts +0 -3
- package/src/tools/system/avatar-generator.ts +13 -19
- package/src/tools/system/request-permission.ts +2 -1
- package/src/tools/terminal/safe-env.ts +1 -0
- package/src/tools/tool-manifest.ts +41 -47
- package/src/tools/types.ts +6 -2
- package/src/tools/ui-surface/definitions.ts +0 -55
- package/src/util/errors.ts +12 -10
- package/src/workspace/git-service.ts +0 -2
- package/src/__tests__/account-registry.test.ts +0 -258
- package/src/__tests__/email-classifier.test.ts +0 -25
- package/src/__tests__/gmail-integration.test.ts +0 -97
- package/src/__tests__/handle-user-message-secret-resume.test.ts +0 -172
- package/src/__tests__/home-base-bootstrap.test.ts +0 -84
- package/src/__tests__/managed-twitter-guardrails.test.ts +0 -353
- package/src/__tests__/prebuilt-home-base-seed.test.ts +0 -79
- package/src/__tests__/recording-intent-fallback.test.ts +0 -199
- package/src/__tests__/recording-intent.test.ts +0 -985
- package/src/__tests__/recording-state-machine.test.ts +0 -1574
- package/src/__tests__/reminder-store.test.ts +0 -350
- package/src/__tests__/reminder.test.ts +0 -337
- package/src/__tests__/scan-result-store.test.ts +0 -121
- package/src/__tests__/twitter-platform-proxy-client.test.ts +0 -450
- package/src/__tests__/view-image-tool.test.ts +0 -241
- package/src/cli/commands/amazon/cart.ts +0 -513
- package/src/cli/commands/amazon/checkout.ts +0 -394
- package/src/cli/commands/amazon/client.ts +0 -513
- package/src/cli/commands/amazon/index.ts +0 -920
- package/src/cli/commands/amazon/product-details.ts +0 -145
- package/src/cli/commands/amazon/request-extractor.ts +0 -187
- package/src/cli/commands/amazon/search.ts +0 -76
- package/src/cli/commands/amazon/session.ts +0 -116
- package/src/cli/commands/twitter/__tests__/cli-error-shaping.test.ts +0 -265
- package/src/cli/commands/twitter/__tests__/cli-read-routing.test.ts +0 -483
- package/src/cli/commands/twitter/__tests__/cli-routing.test.ts +0 -412
- package/src/cli/commands/twitter/__tests__/oauth-client.test.ts +0 -197
- package/src/cli/commands/twitter/client.ts +0 -989
- package/src/cli/commands/twitter/index.ts +0 -1160
- package/src/cli/commands/twitter/oauth-client.ts +0 -94
- package/src/cli/commands/twitter/router.ts +0 -396
- package/src/cli/commands/twitter/session.ts +0 -121
- package/src/config/bundled-skills/agentmail/SKILL.md +0 -132
- package/src/config/bundled-skills/agentmail/icon.svg +0 -21
- package/src/config/bundled-skills/amazon/SKILL.md +0 -137
- package/src/config/bundled-skills/amazon/icon.svg +0 -13
- package/src/config/bundled-skills/api-mapping/SKILL.md +0 -78
- package/src/config/bundled-skills/api-mapping/icon.svg +0 -18
- package/src/config/bundled-skills/cli-discover/SKILL.md +0 -68
- package/src/config/bundled-skills/deploy-fullstack-vercel/SKILL.md +0 -179
- package/src/config/bundled-skills/document-writer/SKILL.md +0 -195
- package/src/config/bundled-skills/elevenlabs-voice/SKILL.md +0 -140
- package/src/config/bundled-skills/email-setup/SKILL.md +0 -68
- package/src/config/bundled-skills/frontend-design/SKILL.md +0 -44
- package/src/config/bundled-skills/frontend-design/icon.svg +0 -16
- package/src/config/bundled-skills/google-oauth-setup/SKILL.md +0 -452
- package/src/config/bundled-skills/guardian-verify-setup/SKILL.md +0 -203
- package/src/config/bundled-skills/influencer/SKILL.md +0 -144
- package/src/config/bundled-skills/influencer/scripts/client.ts +0 -1269
- package/src/config/bundled-skills/influencer/scripts/influencer.ts +0 -267
- package/src/config/bundled-skills/macos-automation/SKILL.md +0 -65
- package/src/config/bundled-skills/macos-automation/icon.svg +0 -12
- package/src/config/bundled-skills/mcp-setup/SKILL.md +0 -75
- package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +0 -184
- package/src/config/bundled-skills/messaging/tools/gmail-archive-by-query.ts +0 -80
- package/src/config/bundled-skills/messaging/tools/gmail-archive.ts +0 -29
- package/src/config/bundled-skills/messaging/tools/gmail-batch-archive.ts +0 -56
- package/src/config/bundled-skills/messaging/tools/gmail-batch-label.ts +0 -34
- package/src/config/bundled-skills/messaging/tools/gmail-download-attachment.ts +0 -47
- package/src/config/bundled-skills/messaging/tools/gmail-label.ts +0 -31
- package/src/config/bundled-skills/messaging/tools/gmail-list-attachments.ts +0 -67
- package/src/config/bundled-skills/messaging/tools/gmail-send-with-attachments.ts +0 -97
- package/src/config/bundled-skills/messaging/tools/gmail-summarize-thread.ts +0 -87
- package/src/config/bundled-skills/messaging/tools/gmail-triage.ts +0 -135
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-activity.ts +0 -24
- package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +0 -201
- package/src/config/bundled-skills/messaging/tools/send-notification.ts +0 -1
- package/src/config/bundled-skills/messaging/tools/sequence-cancel.ts +0 -27
- package/src/config/bundled-skills/messaging/tools/sequence-pause.ts +0 -48
- package/src/config/bundled-skills/messaging/tools/sequence-resume.ts +0 -27
- package/src/config/bundled-skills/messaging/tools/sequence-update.ts +0 -56
- package/src/config/bundled-skills/notion/SKILL.md +0 -240
- package/src/config/bundled-skills/notion-oauth-setup/SKILL.md +0 -126
- package/src/config/bundled-skills/oauth-setup/SKILL.md +0 -143
- package/src/config/bundled-skills/public-ingress/SKILL.md +0 -258
- package/src/config/bundled-skills/reminder/SKILL.md +0 -79
- package/src/config/bundled-skills/reminder/TOOLS.json +0 -89
- package/src/config/bundled-skills/reminder/tools/reminder-list.ts +0 -12
- package/src/config/bundled-skills/restaurant-reservation/SKILL.md +0 -141
- package/src/config/bundled-skills/screen-recording/SKILL.md +0 -148
- package/src/config/bundled-skills/self-upgrade/SKILL.md +0 -69
- package/src/config/bundled-skills/skills-catalog/SKILL.md +0 -78
- package/src/config/bundled-skills/slack-app-setup/SKILL.md +0 -178
- package/src/config/bundled-skills/slack-digest-setup/SKILL.md +0 -163
- package/src/config/bundled-skills/slack-oauth-setup/SKILL.md +0 -157
- package/src/config/bundled-skills/start-the-day/SKILL.md +0 -70
- package/src/config/bundled-skills/start-the-day/icon.svg +0 -13
- package/src/config/bundled-skills/telegram-setup/SKILL.md +0 -105
- package/src/config/bundled-skills/time-based-actions/SKILL.md +0 -142
- package/src/config/bundled-skills/twilio-setup/SKILL.md +0 -232
- package/src/config/bundled-skills/twitter/SKILL.md +0 -319
- package/src/config/bundled-skills/twitter/icon.svg +0 -14
- package/src/config/bundled-skills/typescript-eval/SKILL.md +0 -60
- package/src/config/bundled-skills/vercel-token-setup/SKILL.md +0 -214
- package/src/config/bundled-skills/voice-setup/SKILL.md +0 -131
- package/src/config/bundled-skills/voice-setup/icon.svg +0 -20
- package/src/daemon/handlers/pairing.ts +0 -119
- package/src/daemon/handlers/session-user-message.ts +0 -961
- package/src/daemon/recording-executor.ts +0 -180
- package/src/daemon/recording-intent-fallback.ts +0 -162
- package/src/daemon/recording-intent.ts +0 -493
- package/src/home-base/app-link-store.ts +0 -78
- package/src/home-base/bootstrap.ts +0 -74
- package/src/home-base/prebuilt/brain-graph.html +0 -1483
- package/src/home-base/prebuilt/index.html +0 -702
- package/src/home-base/prebuilt/seed-metadata.json +0 -21
- package/src/home-base/prebuilt/seed.ts +0 -122
- package/src/home-base/prebuilt-home-base-updater.ts +0 -36
- package/src/memory/account-store.ts +0 -117
- package/src/messaging/activity-analyzer.ts +0 -76
- package/src/messaging/email-classifier.ts +0 -208
- package/src/messaging/index.ts +0 -2
- package/src/messaging/outreach-classifier.ts +0 -185
- package/src/messaging/thread-summarizer.ts +0 -346
- package/src/messaging/types.ts +0 -17
- package/src/tools/browser/x-auto-navigate.ts +0 -254
- package/src/tools/credentials/account-registry.ts +0 -144
- package/src/tools/filesystem/view-image.ts +0 -244
- package/src/tools/reminder/reminder-store.ts +0 -194
- package/src/tools/reminder/reminder.ts +0 -158
- package/src/tools/system/navigate-settings.ts +0 -74
- package/src/tools/system/open-system-settings.ts +0 -85
- package/src/tools/system/version.ts +0 -54
- package/src/twitter/platform-proxy-client.ts +0 -405
- package/src/util/cookie-session.ts +0 -98
- /package/src/config/bundled-skills/{messaging → gmail}/tools/scan-result-store.ts +0 -0
- /package/src/config/bundled-skills/{messaging → sequences}/tools/sequence-analytics.ts +0 -0
- /package/src/config/bundled-skills/{messaging → sequences}/tools/sequence-create.ts +0 -0
- /package/src/config/bundled-skills/{messaging → sequences}/tools/sequence-delete.ts +0 -0
- /package/src/config/bundled-skills/{messaging → sequences}/tools/sequence-enroll.ts +0 -0
- /package/src/config/bundled-skills/{messaging → sequences}/tools/sequence-enrollment-list.ts +0 -0
- /package/src/config/bundled-skills/{messaging → sequences}/tools/sequence-get.ts +0 -0
- /package/src/config/bundled-skills/{messaging → sequences}/tools/sequence-import.ts +0 -0
- /package/src/config/bundled-skills/{messaging → sequences}/tools/sequence-list.ts +0 -0
|
@@ -6,6 +6,20 @@ import { getLogger } from "../util/logger.js";
|
|
|
6
6
|
import { GeminiEmbeddingBackend } from "./embedding-gemini.js";
|
|
7
7
|
import { OllamaEmbeddingBackend } from "./embedding-ollama.js";
|
|
8
8
|
import { OpenAIEmbeddingBackend } from "./embedding-openai.js";
|
|
9
|
+
import {
|
|
10
|
+
type EmbeddingInput,
|
|
11
|
+
embeddingInputContentHash,
|
|
12
|
+
type MultimodalEmbeddingInput,
|
|
13
|
+
normalizeEmbeddingInput,
|
|
14
|
+
type TextEmbeddingInput,
|
|
15
|
+
} from "./embedding-types.js";
|
|
16
|
+
|
|
17
|
+
export type {
|
|
18
|
+
EmbeddingInput,
|
|
19
|
+
MultimodalEmbeddingInput,
|
|
20
|
+
TextEmbeddingInput,
|
|
21
|
+
};
|
|
22
|
+
export { embeddingInputContentHash, normalizeEmbeddingInput };
|
|
9
23
|
|
|
10
24
|
const log = getLogger("memory-embeddings");
|
|
11
25
|
|
|
@@ -34,12 +48,12 @@ class LazyLocalEmbeddingBackend implements EmbeddingBackend {
|
|
|
34
48
|
}
|
|
35
49
|
|
|
36
50
|
async embed(
|
|
37
|
-
|
|
51
|
+
inputs: EmbeddingInput[],
|
|
38
52
|
options?: EmbeddingRequestOptions,
|
|
39
53
|
): Promise<number[][]> {
|
|
40
54
|
const backend = await this.getDelegate();
|
|
41
55
|
try {
|
|
42
|
-
return await backend.embed(
|
|
56
|
+
return await backend.embed(inputs, options);
|
|
43
57
|
} catch (err) {
|
|
44
58
|
// The onnxruntime-node failure surfaces here during the first embed() call
|
|
45
59
|
// (via LocalEmbeddingBackend.initialize()). Mark broken so auto mode stops
|
|
@@ -103,18 +117,26 @@ function estimateEntryBytes(key: string, vector: number[]): number {
|
|
|
103
117
|
return key.length * 2 + vector.length * 8;
|
|
104
118
|
}
|
|
105
119
|
|
|
106
|
-
function vectorCacheKey(
|
|
120
|
+
function vectorCacheKey(
|
|
121
|
+
provider: string,
|
|
122
|
+
model: string,
|
|
123
|
+
input: EmbeddingInput,
|
|
124
|
+
extras?: string[],
|
|
125
|
+
): string {
|
|
126
|
+
const contentHash = embeddingInputContentHash(input);
|
|
127
|
+
const suffix = extras && extras.length > 0 ? `\0${extras.join("\0")}` : "";
|
|
107
128
|
return createHash("sha256")
|
|
108
|
-
.update(`${provider}\0${model}\0${
|
|
129
|
+
.update(`${provider}\0${model}\0${contentHash}${suffix}`)
|
|
109
130
|
.digest("hex");
|
|
110
131
|
}
|
|
111
132
|
|
|
112
133
|
function getFromVectorCache(
|
|
113
134
|
provider: string,
|
|
114
135
|
model: string,
|
|
115
|
-
|
|
136
|
+
input: EmbeddingInput,
|
|
137
|
+
extras?: string[],
|
|
116
138
|
): number[] | undefined {
|
|
117
|
-
const key = vectorCacheKey(provider, model,
|
|
139
|
+
const key = vectorCacheKey(provider, model, input, extras);
|
|
118
140
|
const v = vectorCache.get(key);
|
|
119
141
|
if (v !== undefined) {
|
|
120
142
|
// LRU refresh: move to end of insertion order
|
|
@@ -127,10 +149,11 @@ function getFromVectorCache(
|
|
|
127
149
|
function putInVectorCache(
|
|
128
150
|
provider: string,
|
|
129
151
|
model: string,
|
|
130
|
-
|
|
152
|
+
input: EmbeddingInput,
|
|
131
153
|
vector: number[],
|
|
154
|
+
extras?: string[],
|
|
132
155
|
): void {
|
|
133
|
-
const key = vectorCacheKey(provider, model,
|
|
156
|
+
const key = vectorCacheKey(provider, model, input, extras);
|
|
134
157
|
// If replacing an existing entry, subtract its old cost first
|
|
135
158
|
const existing = vectorCache.get(key);
|
|
136
159
|
if (existing !== undefined) {
|
|
@@ -161,7 +184,10 @@ export function clearEmbeddingBackendCache(): void {
|
|
|
161
184
|
localBackendBroken = false;
|
|
162
185
|
}
|
|
163
186
|
|
|
164
|
-
function cacheKey(provider: string, model: string): string {
|
|
187
|
+
function cacheKey(provider: string, model: string, extras?: string[]): string {
|
|
188
|
+
if (extras && extras.length > 0) {
|
|
189
|
+
return `${provider}:${model}:${extras.join(":")}`;
|
|
190
|
+
}
|
|
165
191
|
return `${provider}:${model}`;
|
|
166
192
|
}
|
|
167
193
|
|
|
@@ -169,8 +195,9 @@ function getCachedOrCreate<T extends EmbeddingBackend>(
|
|
|
169
195
|
provider: string,
|
|
170
196
|
model: string,
|
|
171
197
|
create: () => T,
|
|
198
|
+
extras?: string[],
|
|
172
199
|
): T {
|
|
173
|
-
const key = cacheKey(provider, model);
|
|
200
|
+
const key = cacheKey(provider, model, extras);
|
|
174
201
|
const existing = backendCache.get(key);
|
|
175
202
|
if (existing) return existing as T;
|
|
176
203
|
const instance = create();
|
|
@@ -178,6 +205,17 @@ function getCachedOrCreate<T extends EmbeddingBackend>(
|
|
|
178
205
|
return instance;
|
|
179
206
|
}
|
|
180
207
|
|
|
208
|
+
function geminiCacheExtras(config: AssistantConfig): string[] {
|
|
209
|
+
const extras: string[] = [];
|
|
210
|
+
if (config.memory.embeddings.geminiTaskType) {
|
|
211
|
+
extras.push(`task=${config.memory.embeddings.geminiTaskType}`);
|
|
212
|
+
}
|
|
213
|
+
if (config.memory.embeddings.geminiDimensions != null) {
|
|
214
|
+
extras.push(`dim=${config.memory.embeddings.geminiDimensions}`);
|
|
215
|
+
}
|
|
216
|
+
return extras;
|
|
217
|
+
}
|
|
218
|
+
|
|
181
219
|
export type EmbeddingProviderName = "local" | "openai" | "gemini" | "ollama";
|
|
182
220
|
|
|
183
221
|
export interface EmbeddingRequestOptions {
|
|
@@ -188,7 +226,7 @@ export interface EmbeddingBackend {
|
|
|
188
226
|
readonly provider: EmbeddingProviderName;
|
|
189
227
|
readonly model: string;
|
|
190
228
|
embed(
|
|
191
|
-
|
|
229
|
+
inputs: EmbeddingInput[],
|
|
192
230
|
options?: EmbeddingRequestOptions,
|
|
193
231
|
): Promise<number[][]>;
|
|
194
232
|
}
|
|
@@ -272,7 +310,12 @@ export function selectEmbeddingBackend(
|
|
|
272
310
|
new GeminiEmbeddingBackend(
|
|
273
311
|
config.apiKeys.gemini,
|
|
274
312
|
config.memory.embeddings.geminiModel,
|
|
313
|
+
{
|
|
314
|
+
taskType: config.memory.embeddings.geminiTaskType,
|
|
315
|
+
dimensions: config.memory.embeddings.geminiDimensions,
|
|
316
|
+
},
|
|
275
317
|
),
|
|
318
|
+
geminiCacheExtras(config),
|
|
276
319
|
),
|
|
277
320
|
reason: null,
|
|
278
321
|
};
|
|
@@ -336,7 +379,7 @@ export function getMemoryBackendStatus(config: AssistantConfig): {
|
|
|
336
379
|
|
|
337
380
|
export async function embedWithBackend(
|
|
338
381
|
config: AssistantConfig,
|
|
339
|
-
|
|
382
|
+
inputs: EmbeddingInput[],
|
|
340
383
|
options?: EmbeddingRequestOptions,
|
|
341
384
|
): Promise<{
|
|
342
385
|
provider: EmbeddingProviderName;
|
|
@@ -354,15 +397,22 @@ export async function embedWithBackend(
|
|
|
354
397
|
const { provider: primaryProvider, model: primaryModel } = selection.backend;
|
|
355
398
|
|
|
356
399
|
// ── Build fallback backends list (needed for embed fallback) ──
|
|
400
|
+
// In auto mode, build a fallback chain from all configured backends
|
|
401
|
+
// (excluding the primary). This lets multimodal inputs fall through
|
|
402
|
+
// to Gemini even when the primary is local or openai.
|
|
357
403
|
const fallbacks: EmbeddingBackend[] =
|
|
358
404
|
config.memory.embeddings.provider === "auto" &&
|
|
359
|
-
selection.backend.provider
|
|
360
|
-
? selectFallbackBackends(config,
|
|
405
|
+
selection.backend.provider !== "gemini"
|
|
406
|
+
? selectFallbackBackends(config, selection.backend.provider)
|
|
361
407
|
: [];
|
|
362
408
|
|
|
409
|
+
// ── Compute provider-specific vector cache extras ───────────────
|
|
410
|
+
const vectorExtras =
|
|
411
|
+
primaryProvider === "gemini" ? geminiCacheExtras(config) : undefined;
|
|
412
|
+
|
|
363
413
|
// ── In-memory cache check (primary provider only) ──────────────
|
|
364
|
-
const cached: (number[] | null)[] =
|
|
365
|
-
const v = getFromVectorCache(primaryProvider, primaryModel,
|
|
414
|
+
const cached: (number[] | null)[] = inputs.map((input) => {
|
|
415
|
+
const v = getFromVectorCache(primaryProvider, primaryModel, input, vectorExtras);
|
|
366
416
|
if (v && v.length === expectedDim) return v;
|
|
367
417
|
return null;
|
|
368
418
|
});
|
|
@@ -378,23 +428,33 @@ export async function embedWithBackend(
|
|
|
378
428
|
};
|
|
379
429
|
}
|
|
380
430
|
|
|
381
|
-
// ── Embed uncached
|
|
431
|
+
// ── Embed uncached inputs ───────────────────────────────────────
|
|
382
432
|
const backends: EmbeddingBackend[] = [selection.backend, ...fallbacks];
|
|
383
433
|
|
|
384
434
|
let lastErr: unknown;
|
|
435
|
+
let anyBackendAttempted = false;
|
|
385
436
|
for (const backend of backends) {
|
|
386
437
|
const isPrimary = backend === selection.backend;
|
|
387
|
-
// For the primary backend, only embed uncached
|
|
388
|
-
// For fallback backends, embed ALL
|
|
389
|
-
const
|
|
390
|
-
? uncachedIndices.map((i) =>
|
|
391
|
-
:
|
|
438
|
+
// For the primary backend, only embed uncached inputs and merge with cached.
|
|
439
|
+
// For fallback backends, embed ALL inputs since the cache was keyed to the primary.
|
|
440
|
+
const inputsToEmbed = isPrimary
|
|
441
|
+
? uncachedIndices.map((i) => inputs[i])
|
|
442
|
+
: inputs;
|
|
443
|
+
|
|
444
|
+
// Skip text-only backends for multimodal inputs
|
|
445
|
+
const hasNonText = inputsToEmbed.some(
|
|
446
|
+
(i) => typeof i !== "string" && normalizeEmbeddingInput(i).type !== "text",
|
|
447
|
+
);
|
|
448
|
+
if (backend.provider !== "gemini" && hasNonText) {
|
|
449
|
+
continue;
|
|
450
|
+
}
|
|
392
451
|
|
|
393
452
|
try {
|
|
394
|
-
|
|
395
|
-
|
|
453
|
+
anyBackendAttempted = true;
|
|
454
|
+
const vectors = await backend.embed(inputsToEmbed, options);
|
|
455
|
+
if (vectors.length !== inputsToEmbed.length) {
|
|
396
456
|
throw new Error(
|
|
397
|
-
`Embedding backend returned ${vectors.length} vectors for ${
|
|
457
|
+
`Embedding backend returned ${vectors.length} vectors for ${inputsToEmbed.length} inputs`,
|
|
398
458
|
);
|
|
399
459
|
}
|
|
400
460
|
for (const vec of vectors) {
|
|
@@ -406,12 +466,15 @@ export async function embedWithBackend(
|
|
|
406
466
|
}
|
|
407
467
|
|
|
408
468
|
// Populate cache with freshly embedded vectors
|
|
409
|
-
|
|
469
|
+
const backendExtras =
|
|
470
|
+
backend.provider === "gemini" ? geminiCacheExtras(config) : undefined;
|
|
471
|
+
for (let i = 0; i < inputsToEmbed.length; i++) {
|
|
410
472
|
putInVectorCache(
|
|
411
473
|
backend.provider,
|
|
412
474
|
backend.model,
|
|
413
|
-
|
|
475
|
+
inputsToEmbed[i],
|
|
414
476
|
vectors[i],
|
|
477
|
+
backendExtras,
|
|
415
478
|
);
|
|
416
479
|
}
|
|
417
480
|
|
|
@@ -437,6 +500,16 @@ export async function embedWithBackend(
|
|
|
437
500
|
}
|
|
438
501
|
}
|
|
439
502
|
}
|
|
503
|
+
if (!anyBackendAttempted) {
|
|
504
|
+
const hasMultimodal = inputs.some(
|
|
505
|
+
(i) => typeof i !== "string" && normalizeEmbeddingInput(i).type !== "text",
|
|
506
|
+
);
|
|
507
|
+
if (hasMultimodal) {
|
|
508
|
+
throw new Error(
|
|
509
|
+
"No available embedding backend supports multimodal inputs. Gemini API key is required for image/audio/video embeddings.",
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
440
513
|
throw lastErr;
|
|
441
514
|
}
|
|
442
515
|
|
|
@@ -478,7 +551,12 @@ function selectFallbackBackends(
|
|
|
478
551
|
new GeminiEmbeddingBackend(
|
|
479
552
|
config.apiKeys.gemini,
|
|
480
553
|
config.memory.embeddings.geminiModel,
|
|
554
|
+
{
|
|
555
|
+
taskType: config.memory.embeddings.geminiTaskType,
|
|
556
|
+
dimensions: config.memory.embeddings.geminiDimensions,
|
|
557
|
+
},
|
|
481
558
|
),
|
|
559
|
+
geminiCacheExtras(config),
|
|
482
560
|
),
|
|
483
561
|
);
|
|
484
562
|
}
|
|
@@ -505,6 +583,30 @@ function selectFallbackBackends(
|
|
|
505
583
|
return backends;
|
|
506
584
|
}
|
|
507
585
|
|
|
586
|
+
/**
|
|
587
|
+
* Returns true when the embedding pipeline can handle multimodal inputs
|
|
588
|
+
* (images, audio, video). Today only Gemini supports multimodal.
|
|
589
|
+
*
|
|
590
|
+
* In auto mode, the primary backend is usually local or OpenAI, but
|
|
591
|
+
* embedWithBackend builds a fallback chain that includes Gemini when
|
|
592
|
+
* available. We check both the primary and fallback backends so that
|
|
593
|
+
* multimodal jobs are still enqueued when Gemini is reachable via fallback.
|
|
594
|
+
*/
|
|
595
|
+
export function selectedBackendSupportsMultimodal(
|
|
596
|
+
config: AssistantConfig,
|
|
597
|
+
): boolean {
|
|
598
|
+
const { backend } = selectEmbeddingBackend(config);
|
|
599
|
+
if (!backend) return false;
|
|
600
|
+
if (backend.provider === "gemini") return true;
|
|
601
|
+
|
|
602
|
+
// In auto mode, check if Gemini is available as a fallback backend.
|
|
603
|
+
if (config.memory.embeddings.provider === "auto") {
|
|
604
|
+
const fallbacks = selectFallbackBackends(config, backend.provider);
|
|
605
|
+
return fallbacks.some((fb) => fb.provider === "gemini");
|
|
606
|
+
}
|
|
607
|
+
return false;
|
|
608
|
+
}
|
|
609
|
+
|
|
508
610
|
function isOllamaConfigured(config: AssistantConfig): boolean {
|
|
509
611
|
return (
|
|
510
612
|
config.provider === "ollama" ||
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
|
+
|
|
3
|
+
import { GeminiEmbeddingBackend } from "./embedding-gemini.js";
|
|
4
|
+
|
|
5
|
+
function makeSuccessResponse(values: number[]) {
|
|
6
|
+
return new Response(JSON.stringify({ embedding: { values } }), {
|
|
7
|
+
status: 200,
|
|
8
|
+
headers: { "Content-Type": "application/json" },
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
describe("GeminiEmbeddingBackend", () => {
|
|
13
|
+
const originalFetch = globalThis.fetch;
|
|
14
|
+
let mockFetch: ReturnType<typeof mock>;
|
|
15
|
+
|
|
16
|
+
beforeEach(() => {
|
|
17
|
+
mockFetch = mock(() => Promise.resolve(makeSuccessResponse([0.1, 0.2, 0.3])));
|
|
18
|
+
globalThis.fetch = mockFetch as unknown as typeof fetch;
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
globalThis.fetch = originalFetch;
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe("text inputs", () => {
|
|
26
|
+
test("sends text as parts: [{ text }]", async () => {
|
|
27
|
+
const backend = new GeminiEmbeddingBackend("test-key", "test-model");
|
|
28
|
+
await backend.embed(["hello world"]);
|
|
29
|
+
|
|
30
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
31
|
+
const [url, init] = mockFetch.mock.calls[0] as [string, RequestInit];
|
|
32
|
+
expect(url).toContain("models/test-model:embedContent");
|
|
33
|
+
expect(url).toContain("key=test-key");
|
|
34
|
+
|
|
35
|
+
const body = JSON.parse(init.body as string);
|
|
36
|
+
expect(body.model).toBe("models/test-model");
|
|
37
|
+
expect(body.content).toEqual({ parts: [{ text: "hello world" }] });
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("handles TextEmbeddingInput objects", async () => {
|
|
41
|
+
const backend = new GeminiEmbeddingBackend("test-key", "test-model");
|
|
42
|
+
await backend.embed([{ type: "text", text: "structured text" }]);
|
|
43
|
+
|
|
44
|
+
const [, init] = mockFetch.mock.calls[0] as [string, RequestInit];
|
|
45
|
+
const body = JSON.parse(init.body as string);
|
|
46
|
+
expect(body.content).toEqual({ parts: [{ text: "structured text" }] });
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
describe("image inputs", () => {
|
|
51
|
+
test("sends image as inline_data with base64", async () => {
|
|
52
|
+
const imageData = Buffer.from("fake-png-data");
|
|
53
|
+
const backend = new GeminiEmbeddingBackend("test-key", "test-model");
|
|
54
|
+
await backend.embed([
|
|
55
|
+
{ type: "image", data: imageData, mimeType: "image/png" },
|
|
56
|
+
]);
|
|
57
|
+
|
|
58
|
+
expect(mockFetch).toHaveBeenCalledTimes(1);
|
|
59
|
+
const [, init] = mockFetch.mock.calls[0] as [string, RequestInit];
|
|
60
|
+
const body = JSON.parse(init.body as string);
|
|
61
|
+
expect(body.content).toEqual({
|
|
62
|
+
parts: [
|
|
63
|
+
{
|
|
64
|
+
inline_data: {
|
|
65
|
+
mime_type: "image/png",
|
|
66
|
+
data: imageData.toString("base64"),
|
|
67
|
+
},
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
describe("audio inputs", () => {
|
|
75
|
+
test("sends audio as inline_data with base64", async () => {
|
|
76
|
+
const audioData = Buffer.from("fake-audio-data");
|
|
77
|
+
const backend = new GeminiEmbeddingBackend("test-key", "test-model");
|
|
78
|
+
await backend.embed([
|
|
79
|
+
{ type: "audio", data: audioData, mimeType: "audio/mp3" },
|
|
80
|
+
]);
|
|
81
|
+
|
|
82
|
+
const [, init] = mockFetch.mock.calls[0] as [string, RequestInit];
|
|
83
|
+
const body = JSON.parse(init.body as string);
|
|
84
|
+
expect(body.content).toEqual({
|
|
85
|
+
parts: [
|
|
86
|
+
{
|
|
87
|
+
inline_data: {
|
|
88
|
+
mime_type: "audio/mp3",
|
|
89
|
+
data: audioData.toString("base64"),
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe("video inputs", () => {
|
|
98
|
+
test("sends video as inline_data with base64", async () => {
|
|
99
|
+
const videoData = Buffer.from("fake-video-data");
|
|
100
|
+
const backend = new GeminiEmbeddingBackend("test-key", "test-model");
|
|
101
|
+
await backend.embed([
|
|
102
|
+
{ type: "video", data: videoData, mimeType: "video/mp4" },
|
|
103
|
+
]);
|
|
104
|
+
|
|
105
|
+
const [, init] = mockFetch.mock.calls[0] as [string, RequestInit];
|
|
106
|
+
const body = JSON.parse(init.body as string);
|
|
107
|
+
expect(body.content).toEqual({
|
|
108
|
+
parts: [
|
|
109
|
+
{
|
|
110
|
+
inline_data: {
|
|
111
|
+
mime_type: "video/mp4",
|
|
112
|
+
data: videoData.toString("base64"),
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe("taskType and outputDimensionality", () => {
|
|
121
|
+
test("includes taskType in request body when configured", async () => {
|
|
122
|
+
const backend = new GeminiEmbeddingBackend("test-key", "test-model", {
|
|
123
|
+
taskType: "RETRIEVAL_DOCUMENT",
|
|
124
|
+
});
|
|
125
|
+
await backend.embed(["hello"]);
|
|
126
|
+
|
|
127
|
+
const [, init] = mockFetch.mock.calls[0] as [string, RequestInit];
|
|
128
|
+
const body = JSON.parse(init.body as string);
|
|
129
|
+
expect(body.taskType).toBe("RETRIEVAL_DOCUMENT");
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test("includes outputDimensionality in request body when dimensions configured", async () => {
|
|
133
|
+
const backend = new GeminiEmbeddingBackend("test-key", "test-model", {
|
|
134
|
+
dimensions: 256,
|
|
135
|
+
});
|
|
136
|
+
await backend.embed(["hello"]);
|
|
137
|
+
|
|
138
|
+
const [, init] = mockFetch.mock.calls[0] as [string, RequestInit];
|
|
139
|
+
const body = JSON.parse(init.body as string);
|
|
140
|
+
expect(body.outputDimensionality).toBe(256);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
test("includes both taskType and outputDimensionality when both configured", async () => {
|
|
144
|
+
const backend = new GeminiEmbeddingBackend("test-key", "test-model", {
|
|
145
|
+
taskType: "SEMANTIC_SIMILARITY",
|
|
146
|
+
dimensions: 512,
|
|
147
|
+
});
|
|
148
|
+
await backend.embed(["hello"]);
|
|
149
|
+
|
|
150
|
+
const [, init] = mockFetch.mock.calls[0] as [string, RequestInit];
|
|
151
|
+
const body = JSON.parse(init.body as string);
|
|
152
|
+
expect(body.taskType).toBe("SEMANTIC_SIMILARITY");
|
|
153
|
+
expect(body.outputDimensionality).toBe(512);
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
test("omits taskType when not configured", async () => {
|
|
157
|
+
const backend = new GeminiEmbeddingBackend("test-key", "test-model");
|
|
158
|
+
await backend.embed(["hello"]);
|
|
159
|
+
|
|
160
|
+
const [, init] = mockFetch.mock.calls[0] as [string, RequestInit];
|
|
161
|
+
const body = JSON.parse(init.body as string);
|
|
162
|
+
expect(body.taskType).toBeUndefined();
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
test("omits outputDimensionality when not configured", async () => {
|
|
166
|
+
const backend = new GeminiEmbeddingBackend("test-key", "test-model");
|
|
167
|
+
await backend.embed(["hello"]);
|
|
168
|
+
|
|
169
|
+
const [, init] = mockFetch.mock.calls[0] as [string, RequestInit];
|
|
170
|
+
const body = JSON.parse(init.body as string);
|
|
171
|
+
expect(body.outputDimensionality).toBeUndefined();
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test("omits both when options is undefined", async () => {
|
|
175
|
+
const backend = new GeminiEmbeddingBackend("test-key", "test-model");
|
|
176
|
+
await backend.embed(["hello"]);
|
|
177
|
+
|
|
178
|
+
const [, init] = mockFetch.mock.calls[0] as [string, RequestInit];
|
|
179
|
+
const body = JSON.parse(init.body as string);
|
|
180
|
+
expect(body.taskType).toBeUndefined();
|
|
181
|
+
expect(body.outputDimensionality).toBeUndefined();
|
|
182
|
+
expect(Object.keys(body)).toEqual(["model", "content"]);
|
|
183
|
+
});
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
describe("error handling", () => {
|
|
187
|
+
test("throws on non-OK response", async () => {
|
|
188
|
+
mockFetch = mock(() =>
|
|
189
|
+
Promise.resolve(
|
|
190
|
+
new Response("Internal Server Error", { status: 500 }),
|
|
191
|
+
),
|
|
192
|
+
);
|
|
193
|
+
globalThis.fetch = mockFetch as unknown as typeof fetch;
|
|
194
|
+
|
|
195
|
+
const backend = new GeminiEmbeddingBackend("test-key", "test-model");
|
|
196
|
+
await expect(backend.embed(["hello"])).rejects.toThrow(
|
|
197
|
+
"Gemini embeddings request failed (500): Internal Server Error",
|
|
198
|
+
);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
test("throws when response is missing embedding values", async () => {
|
|
202
|
+
mockFetch = mock(() =>
|
|
203
|
+
Promise.resolve(
|
|
204
|
+
new Response(JSON.stringify({}), {
|
|
205
|
+
status: 200,
|
|
206
|
+
headers: { "Content-Type": "application/json" },
|
|
207
|
+
}),
|
|
208
|
+
),
|
|
209
|
+
);
|
|
210
|
+
globalThis.fetch = mockFetch as unknown as typeof fetch;
|
|
211
|
+
|
|
212
|
+
const backend = new GeminiEmbeddingBackend("test-key", "test-model");
|
|
213
|
+
await expect(backend.embed(["hello"])).rejects.toThrow(
|
|
214
|
+
"Gemini embeddings response missing vector values",
|
|
215
|
+
);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
test("throws when embedding values array is empty", async () => {
|
|
219
|
+
mockFetch = mock(() =>
|
|
220
|
+
Promise.resolve(
|
|
221
|
+
new Response(JSON.stringify({ embedding: { values: [] } }), {
|
|
222
|
+
status: 200,
|
|
223
|
+
headers: { "Content-Type": "application/json" },
|
|
224
|
+
}),
|
|
225
|
+
),
|
|
226
|
+
);
|
|
227
|
+
globalThis.fetch = mockFetch as unknown as typeof fetch;
|
|
228
|
+
|
|
229
|
+
const backend = new GeminiEmbeddingBackend("test-key", "test-model");
|
|
230
|
+
await expect(backend.embed(["hello"])).rejects.toThrow(
|
|
231
|
+
"Gemini embeddings response missing vector values",
|
|
232
|
+
);
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
describe("multiple inputs", () => {
|
|
237
|
+
test("embeds multiple inputs sequentially", async () => {
|
|
238
|
+
let callCount = 0;
|
|
239
|
+
mockFetch = mock(() => {
|
|
240
|
+
callCount++;
|
|
241
|
+
return Promise.resolve(
|
|
242
|
+
makeSuccessResponse([0.1 * callCount, 0.2 * callCount]),
|
|
243
|
+
);
|
|
244
|
+
});
|
|
245
|
+
globalThis.fetch = mockFetch as unknown as typeof fetch;
|
|
246
|
+
|
|
247
|
+
const backend = new GeminiEmbeddingBackend("test-key", "test-model");
|
|
248
|
+
const result = await backend.embed(["hello", "world"]);
|
|
249
|
+
|
|
250
|
+
expect(mockFetch).toHaveBeenCalledTimes(2);
|
|
251
|
+
expect(result).toHaveLength(2);
|
|
252
|
+
expect(result[0]).toEqual([0.1, 0.2]);
|
|
253
|
+
expect(result[1]).toEqual([0.2, 0.4]);
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
});
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
EmbeddingBackend,
|
|
3
|
+
EmbeddingInput,
|
|
3
4
|
EmbeddingRequestOptions,
|
|
4
5
|
} from "./embedding-backend.js";
|
|
6
|
+
import type {
|
|
7
|
+
EmbeddingTaskType,
|
|
8
|
+
MultimodalEmbeddingInput,
|
|
9
|
+
} from "./embedding-types.js";
|
|
10
|
+
import { normalizeEmbeddingInput } from "./embedding-types.js";
|
|
5
11
|
|
|
6
12
|
interface GeminiEmbedResponse {
|
|
7
13
|
embedding?: {
|
|
@@ -13,46 +19,59 @@ export class GeminiEmbeddingBackend implements EmbeddingBackend {
|
|
|
13
19
|
readonly provider = "gemini" as const;
|
|
14
20
|
readonly model: string;
|
|
15
21
|
private readonly apiKey: string;
|
|
22
|
+
private readonly taskType?: EmbeddingTaskType;
|
|
23
|
+
private readonly dimensions?: number;
|
|
16
24
|
|
|
17
|
-
constructor(
|
|
25
|
+
constructor(
|
|
26
|
+
apiKey: string,
|
|
27
|
+
model: string,
|
|
28
|
+
options?: { taskType?: EmbeddingTaskType; dimensions?: number },
|
|
29
|
+
) {
|
|
18
30
|
this.apiKey = apiKey;
|
|
19
31
|
this.model = model;
|
|
32
|
+
this.taskType = options?.taskType;
|
|
33
|
+
this.dimensions = options?.dimensions;
|
|
20
34
|
}
|
|
21
35
|
|
|
22
36
|
async embed(
|
|
23
|
-
|
|
37
|
+
inputs: EmbeddingInput[],
|
|
24
38
|
options?: EmbeddingRequestOptions,
|
|
25
39
|
): Promise<number[][]> {
|
|
26
40
|
const vectors: number[][] = [];
|
|
27
|
-
for (const
|
|
28
|
-
const values = await this.embedSingle(
|
|
41
|
+
for (const input of inputs) {
|
|
42
|
+
const values = await this.embedSingle(input, options);
|
|
29
43
|
vectors.push(values);
|
|
30
44
|
}
|
|
31
45
|
return vectors;
|
|
32
46
|
}
|
|
33
47
|
|
|
34
48
|
private async embedSingle(
|
|
35
|
-
|
|
49
|
+
input: EmbeddingInput,
|
|
36
50
|
options?: EmbeddingRequestOptions,
|
|
37
51
|
): Promise<number[]> {
|
|
52
|
+
const normalized = normalizeEmbeddingInput(input);
|
|
53
|
+
const parts = this.buildParts(normalized);
|
|
54
|
+
|
|
55
|
+
const body: Record<string, unknown> = {
|
|
56
|
+
model: `models/${this.model}`,
|
|
57
|
+
content: { parts },
|
|
58
|
+
};
|
|
59
|
+
if (this.taskType) body.taskType = this.taskType;
|
|
60
|
+
if (this.dimensions) body.outputDimensionality = this.dimensions;
|
|
61
|
+
|
|
38
62
|
const url = `https://generativelanguage.googleapis.com/v1beta/models/${encodeURIComponent(
|
|
39
63
|
this.model,
|
|
40
64
|
)}:embedContent?key=${encodeURIComponent(this.apiKey)}`;
|
|
41
65
|
const response = await fetch(url, {
|
|
42
66
|
method: "POST",
|
|
43
67
|
headers: { "Content-Type": "application/json" },
|
|
44
|
-
body: JSON.stringify(
|
|
45
|
-
model: `models/${this.model}`,
|
|
46
|
-
content: {
|
|
47
|
-
parts: [{ text }],
|
|
48
|
-
},
|
|
49
|
-
}),
|
|
68
|
+
body: JSON.stringify(body),
|
|
50
69
|
signal: options?.signal,
|
|
51
70
|
});
|
|
52
71
|
if (!response.ok) {
|
|
53
|
-
const
|
|
72
|
+
const responseBody = await response.text();
|
|
54
73
|
throw new Error(
|
|
55
|
-
`Gemini embeddings request failed (${response.status}): ${
|
|
74
|
+
`Gemini embeddings request failed (${response.status}): ${responseBody}`,
|
|
56
75
|
);
|
|
57
76
|
}
|
|
58
77
|
const payload = (await response.json()) as GeminiEmbedResponse;
|
|
@@ -62,4 +81,19 @@ export class GeminiEmbeddingBackend implements EmbeddingBackend {
|
|
|
62
81
|
}
|
|
63
82
|
return values;
|
|
64
83
|
}
|
|
84
|
+
|
|
85
|
+
private buildParts(input: MultimodalEmbeddingInput): unknown[] {
|
|
86
|
+
if (input.type === "text") {
|
|
87
|
+
return [{ text: input.text }];
|
|
88
|
+
}
|
|
89
|
+
// Image, audio, video: use inline_data with base64
|
|
90
|
+
return [
|
|
91
|
+
{
|
|
92
|
+
inline_data: {
|
|
93
|
+
mime_type: input.mimeType,
|
|
94
|
+
data: input.data.toString("base64"),
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
];
|
|
98
|
+
}
|
|
65
99
|
}
|
|
@@ -6,9 +6,11 @@ import { getEmbeddingModelsDir, getRootDir } from "../util/platform.js";
|
|
|
6
6
|
import { PromiseGuard } from "../util/promise-guard.js";
|
|
7
7
|
import type {
|
|
8
8
|
EmbeddingBackend,
|
|
9
|
+
EmbeddingInput,
|
|
9
10
|
EmbeddingRequestOptions,
|
|
10
11
|
} from "./embedding-backend.js";
|
|
11
12
|
import { EmbeddingRuntimeManager } from "./embedding-runtime-manager.js";
|
|
13
|
+
import { normalizeEmbeddingInput } from "./embedding-types.js";
|
|
12
14
|
|
|
13
15
|
const log = getLogger("memory-embedding-local");
|
|
14
16
|
|
|
@@ -56,10 +58,20 @@ export class LocalEmbeddingBackend implements EmbeddingBackend {
|
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
async embed(
|
|
59
|
-
|
|
61
|
+
inputs: EmbeddingInput[],
|
|
60
62
|
options?: EmbeddingRequestOptions,
|
|
61
63
|
): Promise<number[][]> {
|
|
62
|
-
if (
|
|
64
|
+
if (inputs.length === 0) return [];
|
|
65
|
+
|
|
66
|
+
const texts = inputs.map((i) => {
|
|
67
|
+
const n = normalizeEmbeddingInput(i);
|
|
68
|
+
if (n.type !== "text") {
|
|
69
|
+
throw new Error(
|
|
70
|
+
"Local embedding backend only supports text inputs",
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
return n.text;
|
|
74
|
+
});
|
|
63
75
|
if (options?.signal?.aborted)
|
|
64
76
|
throw new DOMException("Aborted", "AbortError");
|
|
65
77
|
|