@vellumai/assistant 0.5.0 → 0.5.2
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 +54 -54
- package/docs/architecture/integrations.md +62 -67
- package/docs/credential-execution-service.md +3 -3
- package/package.json +1 -1
- package/src/__tests__/agent-loop.test.ts +111 -0
- package/src/__tests__/always-loaded-tools-guard.test.ts +3 -4
- package/src/__tests__/app-builder-tool-scripts.test.ts +13 -151
- package/src/__tests__/app-dir-path-guard.test.ts +78 -0
- package/src/__tests__/app-executors.test.ts +1 -291
- package/src/__tests__/app-git-history.test.ts +4 -4
- package/src/__tests__/app-routes-csp.test.ts +1 -0
- package/src/__tests__/app-store-dir-names.test.ts +426 -0
- package/src/__tests__/assistant-feature-flags-integration.test.ts +7 -9
- package/src/__tests__/attachments-store.test.ts +169 -21
- package/src/__tests__/attachments.test.ts +115 -1
- package/src/__tests__/btw-routes.test.ts +1 -0
- package/src/__tests__/canonical-guardian-store.test.ts +38 -0
- package/src/__tests__/channel-reply-delivery.test.ts +55 -0
- package/src/__tests__/checker.test.ts +54 -0
- package/src/__tests__/claude-code-skill-regression.test.ts +2 -0
- package/src/__tests__/claude-code-tool-profiles.test.ts +2 -0
- package/src/__tests__/compaction.benchmark.test.ts +2 -1
- package/src/__tests__/config-schema-cmd.test.ts +68 -21
- package/src/__tests__/config-schema.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +149 -5
- package/src/__tests__/conversation-agent-loop.test.ts +290 -2
- package/src/__tests__/conversation-attachments.test.ts +17 -19
- package/src/__tests__/conversation-disk-view-integration.test.ts +277 -0
- package/src/__tests__/conversation-disk-view.test.ts +810 -0
- package/src/__tests__/conversation-error.test.ts +1 -1
- package/src/__tests__/conversation-fork-crud.test.ts +551 -0
- package/src/__tests__/conversation-fork-route.test.ts +386 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-key-store-disk-view.test.ts +130 -0
- package/src/__tests__/conversation-media-retry.test.ts +8 -2
- package/src/__tests__/conversation-queue.test.ts +36 -1
- package/src/__tests__/conversation-routes-disk-view.test.ts +439 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +2 -2
- package/src/__tests__/conversation-routes-slash-commands.test.ts +2 -7
- package/src/__tests__/conversation-runtime-assembly.test.ts +17 -2
- package/src/__tests__/conversation-skill-tools.test.ts +4 -9
- package/src/__tests__/conversation-slash-commands.test.ts +149 -0
- package/src/__tests__/conversation-store.test.ts +24 -21
- package/src/__tests__/conversation-surfaces-state-update.test.ts +246 -0
- package/src/__tests__/conversation-surfaces-task-progress.test.ts +1 -0
- package/src/__tests__/conversation-title-service.test.ts +137 -0
- package/src/__tests__/conversation-tool-setup-app-refresh.test.ts +25 -315
- package/src/__tests__/conversation-tool-setup-memory-scope.test.ts +1 -0
- package/src/__tests__/conversation-tool-setup-side-effect-flag.test.ts +1 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +44 -2
- package/src/__tests__/conversation-workspace-injection.test.ts +11 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +3 -3
- package/src/__tests__/credential-security-invariants.test.ts +3 -0
- package/src/__tests__/credential-vault-unit.test.ts +5 -10
- package/src/__tests__/cu-unified-flow.test.ts +1 -0
- package/src/__tests__/db-conversation-fork-lineage-migration.test.ts +241 -0
- package/src/__tests__/db-llm-request-log-provider-migration.test.ts +214 -0
- package/src/__tests__/diagnostics-export.test.ts +70 -1
- package/src/__tests__/filesystem-tools.test.ts +4 -2
- package/src/__tests__/first-greeting.test.ts +80 -0
- package/src/__tests__/gateway-only-guard.test.ts +1 -0
- package/src/__tests__/handlers-user-message-approval-consumption.test.ts +3 -7
- package/src/__tests__/history-repair.test.ts +103 -10
- package/src/__tests__/http-conversation-lineage.test.ts +251 -0
- package/src/__tests__/image-source-path-reinject.test.ts +136 -0
- package/src/__tests__/llm-context-normalization.test.ts +1116 -0
- package/src/__tests__/llm-context-route-provider.test.ts +217 -0
- package/src/__tests__/llm-request-log-turn-query.test.ts +270 -0
- package/src/__tests__/media-generate-image.test.ts +47 -94
- package/src/__tests__/memory-lifecycle-e2e.test.ts +3 -1
- package/src/__tests__/memory-recall-quality.test.ts +5 -5
- package/src/__tests__/migration-cross-version-compatibility.test.ts +4 -1
- package/src/__tests__/migration-export-http.test.ts +3 -1
- package/src/__tests__/migration-import-commit-http.test.ts +18 -4
- package/src/__tests__/migration-import-preflight-http.test.ts +1 -3
- package/src/__tests__/mime-builder.test.ts +3 -2
- package/src/__tests__/non-member-access-request.test.ts +12 -1
- package/src/__tests__/notification-decision-identity.test.ts +52 -0
- package/src/__tests__/oauth-apps-routes.test.ts +103 -0
- package/src/__tests__/oauth-store.test.ts +115 -0
- package/src/__tests__/provider-error-scenarios.test.ts +1 -3
- package/src/__tests__/provider-failover-actual-provider.test.ts +66 -0
- package/src/__tests__/recording-handler.test.ts +17 -0
- package/src/__tests__/registry.test.ts +3 -8
- package/src/__tests__/relay-server.test.ts +1 -1
- package/src/__tests__/runtime-attachment-metadata.test.ts +7 -3
- package/src/__tests__/schema-transforms.test.ts +165 -5
- package/src/__tests__/server-history-render.test.ts +2 -2
- package/src/__tests__/skill-feature-flags-integration.test.ts +18 -17
- package/src/__tests__/skill-feature-flags.test.ts +13 -13
- package/src/__tests__/skill-load-feature-flag.test.ts +4 -4
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +3 -1
- package/src/__tests__/slack-inbound-verification.test.ts +2 -2
- package/src/__tests__/starter-task-flow.test.ts +1 -0
- package/src/__tests__/suggestion-routes.test.ts +443 -0
- package/src/__tests__/swarm-conversation-integration.test.ts +1 -0
- package/src/__tests__/swarm-recursion.test.ts +1 -0
- package/src/__tests__/swarm-tool.test.ts +1 -0
- package/src/__tests__/system-prompt.test.ts +8 -0
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -0
- package/src/__tests__/tool-preview-lifecycle.test.ts +32 -5
- package/src/__tests__/top-level-renderer.test.ts +22 -0
- package/src/__tests__/turn-boundary-resolution.test.ts +243 -0
- package/src/__tests__/web-fetch.test.ts +6 -2
- package/src/__tests__/workspace-migration-006-services-config.test.ts +335 -0
- package/src/__tests__/workspace-migration-007-web-search-provider-rename.test.ts +312 -0
- package/src/__tests__/workspace-migration-009-backfill-conversation-disk-view.test.ts +278 -0
- package/src/__tests__/workspace-migration-010-app-dir-rename.test.ts +275 -0
- package/src/__tests__/workspace-migration-012-rename-conversation-disk-view-dirs.test.ts +77 -0
- package/src/__tests__/workspace-migration-013-repair-conversation-disk-view.test.ts +401 -0
- package/src/__tests__/workspace-migration-backfill-installation-id.test.ts +328 -0
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +6 -10
- package/src/agent/attachments.ts +27 -1
- package/src/agent/loop.ts +29 -1
- package/src/avatar/traits-png-sync.ts +80 -25
- package/src/bundler/app-bundler.ts +4 -4
- package/src/calls/call-domain.ts +1 -0
- package/src/calls/voice-session-bridge.ts +1 -0
- package/src/cli/commands/auth.ts +92 -0
- package/src/cli/commands/avatar.ts +7 -6
- package/src/cli/commands/config.ts +2 -0
- package/src/cli/commands/oauth/providers.ts +29 -0
- package/src/cli/program.ts +12 -0
- package/src/cli.ts +15 -48
- package/src/config/bundled-skills/app-builder/SKILL.md +103 -28
- package/src/config/bundled-skills/app-builder/TOOLS.json +5 -199
- package/src/config/bundled-skills/app-builder/tools/{app-query.ts → app-refresh.ts} +2 -2
- package/src/config/bundled-skills/contacts/tools/google-contacts.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +6 -9
- package/src/config/bundled-skills/gmail/tools/gmail-attachments.ts +4 -6
- package/src/config/bundled-skills/gmail/tools/gmail-draft.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-filters.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-follow-up.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-forward.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-label.ts +4 -6
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-send-draft.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-trash.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -3
- package/src/config/bundled-skills/gmail/tools/gmail-vacation.ts +2 -3
- package/src/config/bundled-skills/google-calendar/tools/shared.ts +1 -1
- package/src/config/bundled-skills/image-studio/SKILL.md +2 -2
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -2
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +45 -72
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +2 -2
- package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +19 -3
- package/src/config/bundled-skills/skill-management/TOOLS.json +2 -2
- package/src/config/bundled-skills/slack/tools/shared.ts +19 -4
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +2 -3
- package/src/config/bundled-skills/transcribe/SKILL.md +1 -1
- package/src/config/bundled-skills/transcribe/TOOLS.json +2 -6
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +19 -83
- package/src/config/bundled-tool-registry.ts +2 -14
- package/src/config/feature-flag-registry.json +16 -0
- package/src/config/loader.ts +64 -0
- package/src/config/raw-config-utils.ts +30 -0
- package/src/config/schema-utils.ts +28 -7
- package/src/config/schema.ts +8 -0
- package/src/config/schemas/elevenlabs.ts +18 -0
- package/src/config/schemas/memory-lifecycle.ts +4 -2
- package/src/config/schemas/memory-storage.ts +1 -1
- package/src/config/schemas/services.ts +8 -6
- package/src/contacts/contact-store.ts +13 -6
- package/src/contacts/contacts-write.ts +0 -1
- package/src/context/window-manager.ts +13 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +46 -42
- package/src/daemon/conversation-agent-loop.ts +56 -19
- package/src/daemon/conversation-attachments.ts +18 -36
- package/src/daemon/conversation-error.ts +2 -1
- package/src/daemon/conversation-history.ts +18 -4
- package/src/daemon/conversation-lifecycle.ts +39 -15
- package/src/daemon/conversation-messaging.ts +70 -26
- package/src/daemon/conversation-process.ts +58 -34
- package/src/daemon/conversation-runtime-assembly.ts +21 -38
- package/src/daemon/conversation-slash.ts +121 -256
- package/src/daemon/conversation-surfaces.ts +143 -20
- package/src/daemon/conversation-tool-setup.ts +0 -6
- package/src/daemon/conversation-workspace.ts +21 -1
- package/src/daemon/conversation.ts +51 -29
- package/src/daemon/first-greeting.ts +35 -0
- package/src/daemon/handlers/config-embeddings.ts +148 -0
- package/src/daemon/handlers/config-model.ts +71 -26
- package/src/daemon/handlers/conversations.ts +0 -23
- package/src/daemon/handlers/recording.ts +26 -21
- package/src/daemon/history-repair.ts +28 -8
- package/src/daemon/host-cu-proxy.ts +2 -2
- package/src/daemon/lifecycle.ts +106 -64
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/conversations.ts +19 -0
- package/src/daemon/message-types/messages.ts +1 -0
- package/src/daemon/message-types/shared.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/message-types/upgrades.ts +23 -0
- package/src/daemon/server.ts +83 -12
- package/src/daemon/shutdown-handlers.ts +8 -5
- package/src/daemon/startup-error.ts +9 -0
- package/src/daemon/tool-side-effects.ts +11 -28
- package/src/events/tool-permission-telemetry-listener.ts +1 -3
- package/src/instrument.ts +0 -4
- package/src/media/app-icon-generator.ts +2 -2
- package/src/memory/app-git-service.ts +28 -16
- package/src/memory/app-store.ts +230 -41
- package/src/memory/attachments-store.ts +558 -130
- package/src/memory/conversation-attention-store.ts +70 -0
- package/src/memory/conversation-crud.ts +442 -3
- package/src/memory/conversation-directories.ts +125 -0
- package/src/memory/conversation-disk-view.ts +390 -0
- package/src/memory/conversation-key-store.ts +17 -5
- package/src/memory/conversation-queries.ts +5 -1
- package/src/memory/conversation-title-service.ts +21 -49
- package/src/memory/db-init.ts +28 -0
- package/src/memory/embedding-backend.ts +42 -53
- package/src/memory/embedding-gemini.test.ts +4 -4
- package/src/memory/embedding-local.ts +1 -3
- package/src/memory/embedding-ollama.ts +1 -3
- package/src/memory/embedding-openai.ts +1 -3
- package/src/memory/indexer.ts +9 -7
- package/src/memory/items-extractor.ts +42 -13
- package/src/memory/job-handlers/conversation-starters.ts +6 -1
- package/src/memory/job-handlers/embedding.test.ts +1 -4
- package/src/memory/llm-request-log-store.ts +100 -1
- package/src/memory/migrations/102-alter-table-columns.ts +5 -0
- package/src/memory/migrations/146-schedule-oneshot-routing.ts +3 -3
- package/src/memory/migrations/147-migrate-reminders-to-schedules.ts +66 -70
- package/src/memory/migrations/148-drop-reminders-table.ts +5 -9
- package/src/memory/migrations/160-drop-loopback-port-column.ts +1 -3
- package/src/memory/migrations/174-rename-thread-starters-table.ts +0 -7
- package/src/memory/migrations/178-oauth-providers-managed-service-config-key.ts +15 -0
- package/src/memory/migrations/179-llm-request-log-message-id.ts +16 -0
- package/src/memory/migrations/180-backfill-inline-attachments-to-disk.ts +66 -0
- package/src/memory/migrations/181-rename-thread-starters-checkpoints.ts +46 -0
- package/src/memory/migrations/182-oauth-providers-display-metadata.ts +20 -0
- package/src/memory/migrations/183-add-conversation-fork-lineage.ts +22 -0
- package/src/memory/migrations/184-llm-request-log-provider.ts +12 -0
- package/src/memory/migrations/index.ts +7 -0
- package/src/memory/migrations/registry.ts +13 -0
- package/src/memory/retriever.test.ts +601 -2
- package/src/memory/retriever.ts +85 -9
- package/src/memory/schema/conversations.ts +6 -0
- package/src/memory/schema/infrastructure.ts +13 -7
- package/src/memory/schema/oauth.ts +6 -0
- package/src/messaging/providers/gmail/mime-builder.ts +3 -1
- package/src/notifications/copy-composer.ts +26 -0
- package/src/notifications/decision-engine.ts +14 -1
- package/src/notifications/emit-signal.ts +1 -1
- package/src/notifications/signal.ts +36 -0
- package/src/oauth/byo-connection.test.ts +1 -45
- package/src/oauth/byo-connection.ts +2 -8
- package/src/oauth/connect-orchestrator.ts +15 -11
- package/src/oauth/connection-resolver.test.ts +191 -0
- package/src/oauth/connection-resolver.ts +66 -38
- package/src/oauth/connection.ts +0 -1
- package/src/oauth/oauth-store.ts +97 -47
- package/src/oauth/platform-connection.test.ts +0 -1
- package/src/oauth/platform-connection.ts +11 -3
- package/src/oauth/seed-providers.ts +78 -3
- package/src/oauth/token-persistence.ts +16 -10
- package/src/permissions/checker.ts +62 -19
- package/src/prompts/system-prompt.ts +2 -0
- package/src/prompts/templates/BOOTSTRAP.md +2 -0
- package/src/providers/anthropic/client.ts +8 -1
- package/src/providers/failover.ts +4 -1
- package/src/providers/gemini/client.ts +50 -0
- package/src/providers/model-catalog.ts +92 -0
- package/src/providers/model-intents.ts +29 -20
- package/src/providers/openai/client.ts +49 -0
- package/src/providers/types.ts +2 -0
- package/src/runtime/access-request-helper.ts +16 -7
- package/src/runtime/auth/credential-service.ts +3 -1
- package/src/runtime/auth/route-policy.ts +14 -1
- package/src/runtime/btw-sidechain.ts +101 -0
- package/src/runtime/channel-reply-delivery.ts +17 -1
- package/src/runtime/http-router.ts +3 -1
- package/src/runtime/http-server.ts +196 -141
- package/src/runtime/http-types.ts +1 -0
- package/src/runtime/migrations/vbundle-builder.ts +5 -1
- package/src/runtime/routes/access-request-decision.ts +41 -0
- package/src/runtime/routes/app-management-routes.ts +6 -3
- package/src/runtime/routes/app-routes.ts +7 -3
- package/src/runtime/routes/approval-routes.ts +1 -0
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +34 -2
- package/src/runtime/routes/attachment-routes.ts +45 -15
- package/src/runtime/routes/btw-routes.ts +21 -61
- package/src/runtime/routes/conversation-management-routes.ts +68 -0
- package/src/runtime/routes/conversation-query-routes.ts +180 -10
- package/src/runtime/routes/conversation-routes.ts +222 -28
- package/src/runtime/routes/conversation-starter-routes.ts +9 -11
- package/src/runtime/routes/diagnostics-routes.ts +1 -0
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -2
- package/src/runtime/routes/llm-context-normalization.ts +1199 -0
- package/src/runtime/routes/log-export-routes.ts +3 -0
- package/src/runtime/routes/memory-item-routes.test.ts +34 -0
- package/src/runtime/routes/memory-item-routes.ts +4 -0
- package/src/runtime/routes/migration-routes.ts +4 -1
- package/src/runtime/routes/oauth-apps.ts +291 -0
- package/src/runtime/routes/secret-routes.ts +28 -1
- package/src/runtime/routes/settings-routes.ts +14 -0
- package/src/runtime/routes/trace-event-routes.ts +4 -1
- package/src/schedule/schedule-store.ts +9 -21
- package/src/security/secure-keys.ts +21 -0
- package/src/signals/bash.ts +1 -1
- package/src/swarm/backend-claude-code.ts +3 -6
- package/src/telemetry/usage-telemetry-reporter.test.ts +3 -2
- package/src/telemetry/usage-telemetry-reporter.ts +3 -1
- package/src/tools/AGENTS.md +6 -10
- package/src/tools/apps/executors.ts +17 -232
- package/src/tools/claude-code/claude-code.ts +2 -3
- package/src/tools/credentials/vault.ts +7 -12
- package/src/tools/host-filesystem/read.ts +13 -10
- package/src/tools/network/__tests__/web-search.test.ts +4 -2
- package/src/tools/schedule/list.ts +2 -7
- package/src/tools/schema-transforms.ts +5 -0
- package/src/tools/shared/filesystem/format-diff.ts +4 -21
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/tool-manifest.ts +0 -6
- package/src/tools/ui-surface/definitions.ts +2 -2
- package/src/util/device-id.ts +28 -5
- package/src/util/platform.ts +6 -0
- package/src/util/pricing.ts +1 -0
- package/src/util/retry.ts +1 -3
- package/src/workspace/migrations/002-backfill-installation-id.ts +23 -12
- package/src/workspace/migrations/003-seed-device-id.ts +3 -4
- package/src/workspace/migrations/006-services-config.ts +5 -0
- package/src/workspace/migrations/008-voice-timeout-and-max-steps.ts +12 -0
- package/src/workspace/migrations/009-backfill-conversation-disk-view.ts +10 -0
- package/src/workspace/migrations/010-app-dir-rename.ts +223 -0
- package/src/workspace/migrations/012-rename-conversation-disk-view-dirs.ts +64 -0
- package/src/workspace/migrations/013-repair-conversation-disk-view.ts +11 -0
- package/src/workspace/migrations/rebuild-conversation-disk-view.ts +186 -0
- package/src/workspace/migrations/registry.ts +10 -0
- package/src/workspace/top-level-renderer.ts +12 -0
- package/src/__tests__/asset-materialize-tool.test.ts +0 -523
- package/src/__tests__/asset-search-tool.test.ts +0 -536
- package/src/__tests__/fixtures/media-reuse-fixtures.ts +0 -56
- package/src/__tests__/media-reuse-story.e2e.test.ts +0 -762
- package/src/__tests__/media-visibility-policy.test.ts +0 -190
- package/src/config/bundled-skills/app-builder/tools/app-file-edit.ts +0 -14
- package/src/config/bundled-skills/app-builder/tools/app-file-list.ts +0 -13
- package/src/config/bundled-skills/app-builder/tools/app-file-read.ts +0 -21
- package/src/config/bundled-skills/app-builder/tools/app-file-write.ts +0 -14
- package/src/config/bundled-skills/app-builder/tools/app-list.ts +0 -13
- package/src/config/bundled-skills/app-builder/tools/app-update.ts +0 -23
- package/src/daemon/media-visibility-policy.ts +0 -59
- package/src/tools/assets/materialize.ts +0 -248
- package/src/tools/assets/search.ts +0 -400
package/src/memory/db-init.ts
CHANGED
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
migrateAssistantContactMetadata,
|
|
38
38
|
migrateBackfillContactInteractionStats,
|
|
39
39
|
migrateBackfillGuardianPrincipalId,
|
|
40
|
+
migrateBackfillInlineAttachmentsToDisk,
|
|
40
41
|
migrateBackfillUsageCacheAccounting,
|
|
41
42
|
migrateCallSessionInviteMetadata,
|
|
42
43
|
migrateCallSessionMode,
|
|
@@ -50,6 +51,7 @@ import {
|
|
|
50
51
|
migrateContactsAssistantId,
|
|
51
52
|
migrateContactsNotesColumn,
|
|
52
53
|
migrateContactsRolePrincipal,
|
|
54
|
+
migrateConversationForkLineage,
|
|
53
55
|
migrateConversationsThreadTypeIndex,
|
|
54
56
|
migrateCreateThreadStartersTable,
|
|
55
57
|
migrateCreateTraceEventsTable,
|
|
@@ -78,11 +80,15 @@ import {
|
|
|
78
80
|
migrateGuardianVerificationSessions,
|
|
79
81
|
migrateInviteCodeHashColumn,
|
|
80
82
|
migrateInviteContactId,
|
|
83
|
+
migrateLlmRequestLogMessageId,
|
|
84
|
+
migrateLlmRequestLogProvider,
|
|
81
85
|
migrateMemoryItemSupersession,
|
|
82
86
|
migrateMessagesFtsBackfill,
|
|
83
87
|
migrateNormalizePhoneIdentities,
|
|
84
88
|
migrateNotificationDeliveryThreadDecision,
|
|
85
89
|
migrateOAuthAppsClientSecretPath,
|
|
90
|
+
migrateOAuthProvidersDisplayMetadata,
|
|
91
|
+
migrateOAuthProvidersManagedServiceConfigKey,
|
|
86
92
|
migrateOAuthProvidersPingUrl,
|
|
87
93
|
migrateReminderRoutingIntent,
|
|
88
94
|
migrateRemindersToSchedules,
|
|
@@ -96,6 +102,7 @@ import {
|
|
|
96
102
|
migrateRenameSequenceEnrollmentsThreadIdColumn,
|
|
97
103
|
migrateRenameSequenceStepsReplyKey,
|
|
98
104
|
migrateRenameSourceSessionIdColumn,
|
|
105
|
+
migrateRenameThreadStartersCheckpoints,
|
|
99
106
|
migrateRenameThreadStartersTable,
|
|
100
107
|
migrateRenameVerificationSessionIdColumn,
|
|
101
108
|
migrateRenameVerificationTable,
|
|
@@ -447,6 +454,9 @@ export function initializeDb(): void {
|
|
|
447
454
|
// 77. Rename thread_starters → conversation_starters table and indexes
|
|
448
455
|
migrateRenameThreadStartersTable(database);
|
|
449
456
|
|
|
457
|
+
// 77b. Rename checkpoint keys from thread_starters: → conversation_starters: prefix
|
|
458
|
+
migrateRenameThreadStartersCheckpoints(database);
|
|
459
|
+
|
|
450
460
|
// 78. Lifecycle events table for app_open / hatch telemetry
|
|
451
461
|
createLifecycleEventsTable(database);
|
|
452
462
|
|
|
@@ -456,6 +466,24 @@ export function initializeDb(): void {
|
|
|
456
466
|
// 80. Trace events table for persistent trace/activity storage across sessions
|
|
457
467
|
migrateCreateTraceEventsTable(database);
|
|
458
468
|
|
|
469
|
+
// 81. Add managed_service_config_key column to oauth_providers
|
|
470
|
+
migrateOAuthProvidersManagedServiceConfigKey(database);
|
|
471
|
+
|
|
472
|
+
// 81b. Add display metadata columns to oauth_providers (display_name, description, dashboard_url, etc.)
|
|
473
|
+
migrateOAuthProvidersDisplayMetadata(database);
|
|
474
|
+
|
|
475
|
+
// 82. Add message_id column to llm_request_logs for per-message LLM context lookup
|
|
476
|
+
migrateLlmRequestLogMessageId(database);
|
|
477
|
+
|
|
478
|
+
// 82b. Add provider column to llm_request_logs for runtime provider lookup
|
|
479
|
+
migrateLlmRequestLogProvider(database);
|
|
480
|
+
|
|
481
|
+
// 83. Backfill existing inline (base64-in-DB) attachments to on-disk storage
|
|
482
|
+
migrateBackfillInlineAttachmentsToDisk(database);
|
|
483
|
+
|
|
484
|
+
// 84. Add nullable conversation fork lineage columns and parent lookup index
|
|
485
|
+
migrateConversationForkLineage(database);
|
|
486
|
+
|
|
459
487
|
validateMigrationState(database);
|
|
460
488
|
|
|
461
489
|
if (process.env.BUN_TEST === "1") {
|
|
@@ -203,7 +203,14 @@ function getCachedOrCreate<T extends EmbeddingBackend>(
|
|
|
203
203
|
return instance;
|
|
204
204
|
}
|
|
205
205
|
|
|
206
|
-
/**
|
|
206
|
+
/**
|
|
207
|
+
* Look up a previously cached backend instance. Returns undefined when no
|
|
208
|
+
* cached entry exists. Used as a fallback when a provider key lookup
|
|
209
|
+
* returns undefined — a transient credential-store outage should not
|
|
210
|
+
* disable a provider whose backend is already warmed in memory. Explicit
|
|
211
|
+
* key deletion triggers `clearEmbeddingBackendCache()` which empties the
|
|
212
|
+
* cache, so a stale backend is never returned after intentional removal.
|
|
213
|
+
*/
|
|
207
214
|
function getCached(
|
|
208
215
|
provider: string,
|
|
209
216
|
model: string,
|
|
@@ -259,9 +266,6 @@ export async function selectEmbeddingBackend(
|
|
|
259
266
|
};
|
|
260
267
|
}
|
|
261
268
|
if (requested === "ollama") {
|
|
262
|
-
// Check cache first to avoid unnecessary async key fetch on cache hits
|
|
263
|
-
const cached = getCached("ollama", config.memory.embeddings.ollamaModel);
|
|
264
|
-
if (cached) return { backend: cached, reason: null };
|
|
265
269
|
const ollamaKey = (await getProviderKeyAsync("ollama")) ?? undefined;
|
|
266
270
|
return {
|
|
267
271
|
backend: getCachedOrCreate(
|
|
@@ -298,14 +302,17 @@ export async function selectEmbeddingBackend(
|
|
|
298
302
|
reason: null,
|
|
299
303
|
};
|
|
300
304
|
case "openai": {
|
|
301
|
-
// Check cache first to avoid unnecessary async key fetch on cache hits
|
|
302
|
-
const cachedOpenai = getCached(
|
|
303
|
-
"openai",
|
|
304
|
-
config.memory.embeddings.openaiModel,
|
|
305
|
-
);
|
|
306
|
-
if (cachedOpenai) return { backend: cachedOpenai, reason: null };
|
|
307
305
|
const openaiKey = await getProviderKeyAsync("openai");
|
|
308
|
-
if (!openaiKey)
|
|
306
|
+
if (!openaiKey) {
|
|
307
|
+
// Preserve cached backend on transient credential-store failures.
|
|
308
|
+
// Explicit key deletion clears the cache via clearEmbeddingBackendCache().
|
|
309
|
+
const cached = getCached(
|
|
310
|
+
"openai",
|
|
311
|
+
config.memory.embeddings.openaiModel,
|
|
312
|
+
);
|
|
313
|
+
if (cached) return { backend: cached, reason: null };
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
309
316
|
return {
|
|
310
317
|
backend: getCachedOrCreate(
|
|
311
318
|
"openai",
|
|
@@ -320,15 +327,16 @@ export async function selectEmbeddingBackend(
|
|
|
320
327
|
};
|
|
321
328
|
}
|
|
322
329
|
case "gemini": {
|
|
323
|
-
// Check cache first to avoid unnecessary async key fetch on cache hits
|
|
324
|
-
const cachedGemini = getCached(
|
|
325
|
-
"gemini",
|
|
326
|
-
config.memory.embeddings.geminiModel,
|
|
327
|
-
geminiCacheExtras(config),
|
|
328
|
-
);
|
|
329
|
-
if (cachedGemini) return { backend: cachedGemini, reason: null };
|
|
330
330
|
const geminiKey = await getProviderKeyAsync("gemini");
|
|
331
|
-
if (!geminiKey)
|
|
331
|
+
if (!geminiKey) {
|
|
332
|
+
const cached = getCached(
|
|
333
|
+
"gemini",
|
|
334
|
+
config.memory.embeddings.geminiModel,
|
|
335
|
+
geminiCacheExtras(config),
|
|
336
|
+
);
|
|
337
|
+
if (cached) return { backend: cached, reason: null };
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
332
340
|
return {
|
|
333
341
|
backend: getCachedOrCreate(
|
|
334
342
|
"gemini",
|
|
@@ -348,12 +356,6 @@ export async function selectEmbeddingBackend(
|
|
|
348
356
|
};
|
|
349
357
|
}
|
|
350
358
|
case "ollama": {
|
|
351
|
-
// Check cache first to avoid unnecessary async key fetch on cache hits
|
|
352
|
-
const cachedOllama = getCached(
|
|
353
|
-
"ollama",
|
|
354
|
-
config.memory.embeddings.ollamaModel,
|
|
355
|
-
);
|
|
356
|
-
if (cachedOllama) return { backend: cachedOllama, reason: null };
|
|
357
359
|
if (!(await isOllamaConfigured(config))) continue;
|
|
358
360
|
const ollamaKey = (await getProviderKeyAsync("ollama")) ?? undefined;
|
|
359
361
|
return {
|
|
@@ -570,15 +572,6 @@ async function selectFallbackBackends(
|
|
|
570
572
|
if (provider === exclude) continue;
|
|
571
573
|
switch (provider) {
|
|
572
574
|
case "openai": {
|
|
573
|
-
// Check cache first to avoid unnecessary async key fetch on cache hits
|
|
574
|
-
const cachedOpenai = getCached(
|
|
575
|
-
"openai",
|
|
576
|
-
config.memory.embeddings.openaiModel,
|
|
577
|
-
);
|
|
578
|
-
if (cachedOpenai) {
|
|
579
|
-
backends.push(cachedOpenai);
|
|
580
|
-
break;
|
|
581
|
-
}
|
|
582
575
|
const openaiKey = await getProviderKeyAsync("openai");
|
|
583
576
|
if (openaiKey) {
|
|
584
577
|
backends.push(
|
|
@@ -592,20 +585,17 @@ async function selectFallbackBackends(
|
|
|
592
585
|
),
|
|
593
586
|
),
|
|
594
587
|
);
|
|
588
|
+
} else {
|
|
589
|
+
// Preserve cached backend on transient credential-store failures.
|
|
590
|
+
const cached = getCached(
|
|
591
|
+
"openai",
|
|
592
|
+
config.memory.embeddings.openaiModel,
|
|
593
|
+
);
|
|
594
|
+
if (cached) backends.push(cached);
|
|
595
595
|
}
|
|
596
596
|
break;
|
|
597
597
|
}
|
|
598
598
|
case "gemini": {
|
|
599
|
-
// Check cache first to avoid unnecessary async key fetch on cache hits
|
|
600
|
-
const cachedGemini = getCached(
|
|
601
|
-
"gemini",
|
|
602
|
-
config.memory.embeddings.geminiModel,
|
|
603
|
-
geminiCacheExtras(config),
|
|
604
|
-
);
|
|
605
|
-
if (cachedGemini) {
|
|
606
|
-
backends.push(cachedGemini);
|
|
607
|
-
break;
|
|
608
|
-
}
|
|
609
599
|
const geminiKey = await getProviderKeyAsync("gemini");
|
|
610
600
|
if (geminiKey) {
|
|
611
601
|
backends.push(
|
|
@@ -624,19 +614,18 @@ async function selectFallbackBackends(
|
|
|
624
614
|
geminiCacheExtras(config),
|
|
625
615
|
),
|
|
626
616
|
);
|
|
617
|
+
} else {
|
|
618
|
+
// Preserve cached backend on transient credential-store failures.
|
|
619
|
+
const cached = getCached(
|
|
620
|
+
"gemini",
|
|
621
|
+
config.memory.embeddings.geminiModel,
|
|
622
|
+
geminiCacheExtras(config),
|
|
623
|
+
);
|
|
624
|
+
if (cached) backends.push(cached);
|
|
627
625
|
}
|
|
628
626
|
break;
|
|
629
627
|
}
|
|
630
628
|
case "ollama": {
|
|
631
|
-
// Check cache first to avoid unnecessary async key fetch on cache hits
|
|
632
|
-
const cachedOllama = getCached(
|
|
633
|
-
"ollama",
|
|
634
|
-
config.memory.embeddings.ollamaModel,
|
|
635
|
-
);
|
|
636
|
-
if (cachedOllama) {
|
|
637
|
-
backends.push(cachedOllama);
|
|
638
|
-
break;
|
|
639
|
-
}
|
|
640
629
|
if (await isOllamaConfigured(config)) {
|
|
641
630
|
const ollamaKey = (await getProviderKeyAsync("ollama")) ?? undefined;
|
|
642
631
|
backends.push(
|
|
@@ -14,7 +14,9 @@ describe("GeminiEmbeddingBackend", () => {
|
|
|
14
14
|
let mockFetch: ReturnType<typeof mock>;
|
|
15
15
|
|
|
16
16
|
beforeEach(() => {
|
|
17
|
-
mockFetch = mock(() =>
|
|
17
|
+
mockFetch = mock(() =>
|
|
18
|
+
Promise.resolve(makeSuccessResponse([0.1, 0.2, 0.3])),
|
|
19
|
+
);
|
|
18
20
|
globalThis.fetch = mockFetch as unknown as typeof fetch;
|
|
19
21
|
});
|
|
20
22
|
|
|
@@ -186,9 +188,7 @@ describe("GeminiEmbeddingBackend", () => {
|
|
|
186
188
|
describe("error handling", () => {
|
|
187
189
|
test("throws on non-OK response", async () => {
|
|
188
190
|
mockFetch = mock(() =>
|
|
189
|
-
Promise.resolve(
|
|
190
|
-
new Response("Internal Server Error", { status: 500 }),
|
|
191
|
-
),
|
|
191
|
+
Promise.resolve(new Response("Internal Server Error", { status: 500 })),
|
|
192
192
|
);
|
|
193
193
|
globalThis.fetch = mockFetch as unknown as typeof fetch;
|
|
194
194
|
|
|
@@ -66,9 +66,7 @@ export class LocalEmbeddingBackend implements EmbeddingBackend {
|
|
|
66
66
|
const texts = inputs.map((i) => {
|
|
67
67
|
const n = normalizeEmbeddingInput(i);
|
|
68
68
|
if (n.type !== "text") {
|
|
69
|
-
throw new Error(
|
|
70
|
-
"Local embedding backend only supports text inputs",
|
|
71
|
-
);
|
|
69
|
+
throw new Error("Local embedding backend only supports text inputs");
|
|
72
70
|
}
|
|
73
71
|
return n.text;
|
|
74
72
|
});
|
|
@@ -34,9 +34,7 @@ export class OllamaEmbeddingBackend implements EmbeddingBackend {
|
|
|
34
34
|
const texts = inputs.map((i) => {
|
|
35
35
|
const n = normalizeEmbeddingInput(i);
|
|
36
36
|
if (n.type !== "text") {
|
|
37
|
-
throw new Error(
|
|
38
|
-
"Ollama embedding backend only supports text inputs",
|
|
39
|
-
);
|
|
37
|
+
throw new Error("Ollama embedding backend only supports text inputs");
|
|
40
38
|
}
|
|
41
39
|
return n.text;
|
|
42
40
|
});
|
|
@@ -26,9 +26,7 @@ export class OpenAIEmbeddingBackend implements EmbeddingBackend {
|
|
|
26
26
|
const texts = inputs.map((i) => {
|
|
27
27
|
const n = normalizeEmbeddingInput(i);
|
|
28
28
|
if (n.type !== "text") {
|
|
29
|
-
throw new Error(
|
|
30
|
-
"OpenAI embedding backend only supports text inputs",
|
|
31
|
-
);
|
|
29
|
+
throw new Error("OpenAI embedding backend only supports text inputs");
|
|
32
30
|
}
|
|
33
31
|
return n.text;
|
|
34
32
|
});
|
package/src/memory/indexer.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { getDb } from "./db.js";
|
|
|
9
9
|
import { selectedBackendSupportsMultimodal } from "./embedding-backend.js";
|
|
10
10
|
import { enqueueMemoryJob } from "./jobs-store.js";
|
|
11
11
|
import {
|
|
12
|
-
|
|
12
|
+
extractMediaBlockMeta,
|
|
13
13
|
extractTextFromStoredMessageContent,
|
|
14
14
|
} from "./message-content.js";
|
|
15
15
|
import { memorySegments } from "./schema.js";
|
|
@@ -71,16 +71,18 @@ export async function indexMessageNow(
|
|
|
71
71
|
input.role === "user" ||
|
|
72
72
|
(input.role === "assistant" && config.extraction.extractFromAssistant);
|
|
73
73
|
// Check if the message has any image blocks before probing the backend.
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
//
|
|
77
|
-
|
|
74
|
+
// extractMediaBlockMeta is synchronous and lightweight — it detects image
|
|
75
|
+
// blocks without decoding base64 data into Buffers, avoiding CPU/memory
|
|
76
|
+
// overhead for messages on non-multimodal backends.
|
|
77
|
+
// selectedBackendSupportsMultimodal requires async key resolution, so we
|
|
78
|
+
// skip it entirely for text-only messages.
|
|
79
|
+
const candidateMediaMeta = extractMediaBlockMeta(input.content).filter(
|
|
78
80
|
(b) => b.type === "image",
|
|
79
81
|
);
|
|
80
82
|
const mediaBlocks =
|
|
81
|
-
|
|
83
|
+
candidateMediaMeta.length > 0 &&
|
|
82
84
|
(await selectedBackendSupportsMultimodal(getConfig()))
|
|
83
|
-
?
|
|
85
|
+
? candidateMediaMeta
|
|
84
86
|
: [];
|
|
85
87
|
|
|
86
88
|
// Wrap all segment inserts and job enqueues in a single transaction so they
|
|
@@ -135,6 +135,13 @@ function hasSemanticDensity(text: string): boolean {
|
|
|
135
135
|
|
|
136
136
|
// ── LLM-powered extraction ────────────────────────────────────────────
|
|
137
137
|
|
|
138
|
+
// Budget for the extraction system prompt (in characters). This is a
|
|
139
|
+
// conservative estimate that fits comfortably within even small model
|
|
140
|
+
// context windows (latency-optimized models like Haiku). The remaining
|
|
141
|
+
// context budget is consumed by the user message, tool schema, and response
|
|
142
|
+
// tokens. ~6000 tokens ≈ 24 000 chars is a safe ceiling.
|
|
143
|
+
const EXTRACTION_SYSTEM_PROMPT_CHAR_BUDGET = 24_000;
|
|
144
|
+
|
|
138
145
|
function buildExtractionSystemPrompt(
|
|
139
146
|
existingItems: Array<{
|
|
140
147
|
id: string;
|
|
@@ -144,16 +151,9 @@ function buildExtractionSystemPrompt(
|
|
|
144
151
|
}>,
|
|
145
152
|
messageRole: string,
|
|
146
153
|
): string {
|
|
147
|
-
//
|
|
148
|
-
//
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
let prompt = "";
|
|
152
|
-
if (identityContext) {
|
|
153
|
-
prompt += `# Identity Context\n\n${identityContext}\n\n---\n\n`;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
prompt += `You are a memory extraction system. Given a message from a conversation, extract structured memory items that would be valuable to remember for future interactions.
|
|
154
|
+
// Build the fixed instruction body first so we can measure it and allocate
|
|
155
|
+
// the remaining budget to identity context.
|
|
156
|
+
let instructions = `You are a memory extraction system. Given a message from a conversation, extract structured memory items that would be valuable to remember for future interactions.
|
|
157
157
|
|
|
158
158
|
Extract items in these categories:
|
|
159
159
|
- identity: Personal info (name, role, location, timezone, background), notable facts, relationships between people/teams/systems
|
|
@@ -183,22 +183,51 @@ Rules:
|
|
|
183
183
|
- Only extract genuinely memorable information. Skip pleasantries, filler, and transient discussion.
|
|
184
184
|
- Do NOT extract information about what tools the assistant used or what files it read — only extract substantive facts about the user, their projects, and their preferences.
|
|
185
185
|
- Do NOT extract claims about actions the assistant performed, outcomes it achieved, or progress it reported (e.g., "I booked an appointment", "I sent the email"). Only extract facts stated by the user or from external sources — the assistant's self-reports are not reliable memory material.
|
|
186
|
+
- Do NOT extract raw code snippets, JSON fragments, YAML, configuration values, log output, or data structures. Only extract the human-readable meaning or intent behind such content, not the literal syntax.
|
|
186
187
|
- Prefer fewer high-quality items over many low-quality ones.
|
|
187
188
|
- If the message contains no memorable information, return an empty array.`;
|
|
188
189
|
|
|
189
190
|
if (messageRole === "assistant") {
|
|
190
|
-
|
|
191
|
+
instructions += `
|
|
191
192
|
|
|
192
193
|
IMPORTANT: The message below is from the ASSISTANT, not the user. Do NOT attribute the assistant's own statements, feelings, self-descriptions, or introspection to the user. Only extract facts about the user, the world, or the project that the assistant is referencing or relaying — NOT the assistant's own identity, uncertainty, or behavior. If the assistant is simply talking about itself (e.g., introducing itself, expressing uncertainty about its own purpose), extract nothing.`;
|
|
193
194
|
}
|
|
194
195
|
|
|
195
196
|
if (existingItems.length > 0) {
|
|
196
|
-
|
|
197
|
+
instructions += `\n\nExisting memory items (use these to identify supersession targets — set \`supersedes\` to the item ID if the new information replaces one of these):\n`;
|
|
197
198
|
for (const item of existingItems) {
|
|
198
|
-
|
|
199
|
+
instructions += `- [${item.id}] (${item.kind}) ${item.subject}: ${item.statement}\n`;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Inject identity context so extracted memories use real names instead of
|
|
204
|
+
// generic "User ..." labels. Budget is dynamically computed: whatever
|
|
205
|
+
// remains after the fixed instructions fits within the system prompt
|
|
206
|
+
// ceiling, preventing oversized prompts from exceeding the provider input
|
|
207
|
+
// window (which would cause sendMessage to error and fall back to
|
|
208
|
+
// lower-quality pattern-based extraction).
|
|
209
|
+
const rawIdentityContext = buildCoreIdentityContext();
|
|
210
|
+
|
|
211
|
+
let prompt = "";
|
|
212
|
+
if (rawIdentityContext) {
|
|
213
|
+
// Reserve space for the wrapping text: "# Identity Context\n\n" + "\n\n---\n\n"
|
|
214
|
+
const wrapperOverhead = "# Identity Context\n\n\n\n---\n\n".length;
|
|
215
|
+
const identityBudget =
|
|
216
|
+
EXTRACTION_SYSTEM_PROMPT_CHAR_BUDGET -
|
|
217
|
+
instructions.length -
|
|
218
|
+
wrapperOverhead;
|
|
219
|
+
|
|
220
|
+
if (identityBudget > 0) {
|
|
221
|
+
const identityContext = truncate(
|
|
222
|
+
rawIdentityContext,
|
|
223
|
+
identityBudget,
|
|
224
|
+
"\n...[identity context truncated]",
|
|
225
|
+
);
|
|
226
|
+
prompt += `# Identity Context\n\n${identityContext}\n\n---\n\n`;
|
|
199
227
|
}
|
|
200
228
|
}
|
|
201
229
|
|
|
230
|
+
prompt += instructions;
|
|
202
231
|
return prompt;
|
|
203
232
|
}
|
|
204
233
|
|
|
@@ -169,7 +169,12 @@ async function generateStarters(scopeId: string): Promise<GeneratedStarter[]> {
|
|
|
169
169
|
const now = new Date();
|
|
170
170
|
const timeContext = `Current time: ${now.toLocaleString("en-US", { weekday: "long", year: "numeric", month: "long", day: "numeric", hour: "numeric", minute: "2-digit", hour12: true })}`;
|
|
171
171
|
|
|
172
|
-
|
|
172
|
+
// Truncate identity context to prevent oversized prompts when SOUL.md /
|
|
173
|
+
// IDENTITY.md / USER.md are large.
|
|
174
|
+
const rawIdentityContext = buildCoreIdentityContext();
|
|
175
|
+
const identityContext = rawIdentityContext
|
|
176
|
+
? truncate(rawIdentityContext, 2000, "\n…[truncated]")
|
|
177
|
+
: null;
|
|
173
178
|
|
|
174
179
|
const systemPrompt = `You are generating 4 conversation starters for a personal assistant app. These appear as clickable chips on the empty conversation page — the first thing the user sees when they open the app.
|
|
175
180
|
|
|
@@ -142,10 +142,7 @@ describe("embedMediaJob", () => {
|
|
|
142
142
|
})
|
|
143
143
|
.run();
|
|
144
144
|
|
|
145
|
-
await embedMediaJob(
|
|
146
|
-
makeJob({ assetId: "asset-registered" }),
|
|
147
|
-
TEST_CONFIG,
|
|
148
|
-
);
|
|
145
|
+
await embedMediaJob(makeJob({ assetId: "asset-registered" }), TEST_CONFIG);
|
|
149
146
|
expect(embedAndUpsertCalls).toHaveLength(0);
|
|
150
147
|
});
|
|
151
148
|
|
|
@@ -1,6 +1,11 @@
|
|
|
1
|
-
import { and, eq, gte, lte } from "drizzle-orm";
|
|
1
|
+
import { and, eq, gte, inArray, isNull, lte } from "drizzle-orm";
|
|
2
2
|
import { v4 as uuid } from "uuid";
|
|
3
3
|
|
|
4
|
+
import {
|
|
5
|
+
getAssistantMessageIdsInTurn,
|
|
6
|
+
getMessageById,
|
|
7
|
+
messageMetadataSchema,
|
|
8
|
+
} from "./conversation-crud.js";
|
|
4
9
|
import { getDb } from "./db.js";
|
|
5
10
|
import { llmRequestLogs } from "./schema.js";
|
|
6
11
|
|
|
@@ -8,12 +13,16 @@ export function recordRequestLog(
|
|
|
8
13
|
conversationId: string,
|
|
9
14
|
requestPayload: string,
|
|
10
15
|
responsePayload: string,
|
|
16
|
+
messageId?: string,
|
|
17
|
+
provider?: string,
|
|
11
18
|
): void {
|
|
12
19
|
const db = getDb();
|
|
13
20
|
db.insert(llmRequestLogs)
|
|
14
21
|
.values({
|
|
15
22
|
id: uuid(),
|
|
16
23
|
conversationId,
|
|
24
|
+
messageId: messageId ?? null,
|
|
25
|
+
provider: provider ?? null,
|
|
17
26
|
requestPayload,
|
|
18
27
|
responsePayload,
|
|
19
28
|
createdAt: Date.now(),
|
|
@@ -46,3 +55,93 @@ export function queryRequestLogs(
|
|
|
46
55
|
.orderBy(llmRequestLogs.createdAt)
|
|
47
56
|
.all();
|
|
48
57
|
}
|
|
58
|
+
|
|
59
|
+
export function backfillMessageIdOnLogs(
|
|
60
|
+
conversationId: string,
|
|
61
|
+
messageId: string,
|
|
62
|
+
): void {
|
|
63
|
+
const db = getDb();
|
|
64
|
+
db.update(llmRequestLogs)
|
|
65
|
+
.set({ messageId })
|
|
66
|
+
.where(
|
|
67
|
+
and(
|
|
68
|
+
eq(llmRequestLogs.conversationId, conversationId),
|
|
69
|
+
isNull(llmRequestLogs.messageId),
|
|
70
|
+
),
|
|
71
|
+
)
|
|
72
|
+
.run();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Internal helper: query `llm_request_logs` for rows matching any of the
|
|
77
|
+
* given message IDs, ordered by `createdAt ASC`. Uses the existing
|
|
78
|
+
* `idx_llm_request_logs_message_id` index via `inArray`.
|
|
79
|
+
*/
|
|
80
|
+
function selectLogsByMessageIds(messageIds: string[]): Array<{
|
|
81
|
+
id: string;
|
|
82
|
+
conversationId: string;
|
|
83
|
+
messageId: string | null;
|
|
84
|
+
provider: string | null;
|
|
85
|
+
requestPayload: string;
|
|
86
|
+
responsePayload: string;
|
|
87
|
+
createdAt: number;
|
|
88
|
+
}> {
|
|
89
|
+
if (messageIds.length === 0) return [];
|
|
90
|
+
const db = getDb();
|
|
91
|
+
return db
|
|
92
|
+
.select({
|
|
93
|
+
id: llmRequestLogs.id,
|
|
94
|
+
conversationId: llmRequestLogs.conversationId,
|
|
95
|
+
messageId: llmRequestLogs.messageId,
|
|
96
|
+
provider: llmRequestLogs.provider,
|
|
97
|
+
requestPayload: llmRequestLogs.requestPayload,
|
|
98
|
+
responsePayload: llmRequestLogs.responsePayload,
|
|
99
|
+
createdAt: llmRequestLogs.createdAt,
|
|
100
|
+
})
|
|
101
|
+
.from(llmRequestLogs)
|
|
102
|
+
.where(inArray(llmRequestLogs.messageId, messageIds))
|
|
103
|
+
.orderBy(llmRequestLogs.createdAt)
|
|
104
|
+
.all();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function getRequestLogsByMessageId(messageId: string): Array<{
|
|
108
|
+
id: string;
|
|
109
|
+
conversationId: string;
|
|
110
|
+
messageId: string | null;
|
|
111
|
+
provider: string | null;
|
|
112
|
+
requestPayload: string;
|
|
113
|
+
responsePayload: string;
|
|
114
|
+
createdAt: number;
|
|
115
|
+
}> {
|
|
116
|
+
// Resolve all assistant message IDs in the same turn so the inspector
|
|
117
|
+
// shows every LLM call from the entire agent turn, not just the queried message.
|
|
118
|
+
const turnMessageIds = getAssistantMessageIdsInTurn(messageId);
|
|
119
|
+
const turnLogs = selectLogsByMessageIds(turnMessageIds);
|
|
120
|
+
if (turnLogs.length > 0) {
|
|
121
|
+
return turnLogs;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Fork-source fallback: if no logs found for the turn, check whether
|
|
125
|
+
// the queried message was forked from a source and resolve that source's turn.
|
|
126
|
+
const message = getMessageById(messageId);
|
|
127
|
+
if (!message?.metadata) {
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
try {
|
|
132
|
+
const parsed = messageMetadataSchema.safeParse(
|
|
133
|
+
JSON.parse(message.metadata),
|
|
134
|
+
);
|
|
135
|
+
const sourceMessageId =
|
|
136
|
+
parsed.success && typeof parsed.data.forkSourceMessageId === "string"
|
|
137
|
+
? parsed.data.forkSourceMessageId
|
|
138
|
+
: null;
|
|
139
|
+
if (!sourceMessageId || sourceMessageId === messageId) {
|
|
140
|
+
return [];
|
|
141
|
+
}
|
|
142
|
+
const sourceTurnIds = getAssistantMessageIdsInTurn(sourceMessageId);
|
|
143
|
+
return selectLogsByMessageIds(sourceTurnIds);
|
|
144
|
+
} catch {
|
|
145
|
+
return [];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -261,6 +261,11 @@ export function addCoreColumns(database: DrizzleDb): void {
|
|
|
261
261
|
} catch {
|
|
262
262
|
/* already exists */
|
|
263
263
|
}
|
|
264
|
+
try {
|
|
265
|
+
database.run(/*sql*/ `ALTER TABLE attachments ADD COLUMN source_path TEXT`);
|
|
266
|
+
} catch {
|
|
267
|
+
/* already exists */
|
|
268
|
+
}
|
|
264
269
|
|
|
265
270
|
// cron_jobs
|
|
266
271
|
try {
|
|
@@ -15,9 +15,9 @@ export function migrateScheduleOneShotRouting(database: DrizzleDb): void {
|
|
|
15
15
|
const raw = getSqliteFrom(database);
|
|
16
16
|
|
|
17
17
|
// Check if migration is already done by inspecting whether the status column exists
|
|
18
|
-
const tableInfo = raw
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
const tableInfo = raw.query("PRAGMA table_info(cron_jobs)").all() as Array<{
|
|
19
|
+
name: string;
|
|
20
|
+
}>;
|
|
21
21
|
const hasStatusColumn = tableInfo.some((col) => col.name === "status");
|
|
22
22
|
if (hasStatusColumn) {
|
|
23
23
|
// Ensure all indexes exist even if the column migration already ran
|