@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
|
@@ -4,12 +4,19 @@ import {
|
|
|
4
4
|
saveRawConfig,
|
|
5
5
|
} from "../../config/loader.js";
|
|
6
6
|
import { setServiceField } from "../../config/raw-config-utils.js";
|
|
7
|
+
import { VALID_INFERENCE_PROVIDERS } from "../../config/schemas/services.js";
|
|
8
|
+
import type { ProviderCatalogEntry } from "../../providers/model-catalog.js";
|
|
9
|
+
import {
|
|
10
|
+
isModelInCatalog,
|
|
11
|
+
PROVIDER_CATALOG,
|
|
12
|
+
} from "../../providers/model-catalog.js";
|
|
13
|
+
import { getProviderDefaultModel } from "../../providers/model-intents.js";
|
|
7
14
|
import {
|
|
8
15
|
getConfiguredProviders,
|
|
9
16
|
isProviderAvailable,
|
|
10
17
|
} from "../../providers/provider-availability.js";
|
|
11
18
|
import { initializeProviders } from "../../providers/registry.js";
|
|
12
|
-
import {
|
|
19
|
+
import { getMaskedProviderKey } from "../../security/secure-keys.js";
|
|
13
20
|
import type {
|
|
14
21
|
ImageGenModelSetRequest,
|
|
15
22
|
ModelSetRequest,
|
|
@@ -20,6 +27,13 @@ import {
|
|
|
20
27
|
log,
|
|
21
28
|
} from "./shared.js";
|
|
22
29
|
|
|
30
|
+
/** Reverse lookup: model ID → provider, derived from PROVIDER_CATALOG. */
|
|
31
|
+
export const MODEL_TO_PROVIDER: Record<string, string> = Object.fromEntries(
|
|
32
|
+
PROVIDER_CATALOG.flatMap((provider) =>
|
|
33
|
+
provider.models.map(({ id }) => [id, provider.id]),
|
|
34
|
+
),
|
|
35
|
+
);
|
|
36
|
+
|
|
23
37
|
// ---------------------------------------------------------------------------
|
|
24
38
|
// Shared business logic (transport-agnostic)
|
|
25
39
|
// ---------------------------------------------------------------------------
|
|
@@ -28,15 +42,29 @@ export interface ModelInfo {
|
|
|
28
42
|
model: string;
|
|
29
43
|
provider: string;
|
|
30
44
|
configuredProviders?: string[];
|
|
45
|
+
availableModels?: Array<{ id: string; displayName: string }>;
|
|
46
|
+
allProviders?: ProviderCatalogEntry[];
|
|
47
|
+
maskedKeys?: Record<string, string>;
|
|
31
48
|
}
|
|
32
49
|
|
|
33
50
|
/** Return current model configuration. */
|
|
34
51
|
export async function getModelInfo(): Promise<ModelInfo> {
|
|
35
52
|
const config = getConfig();
|
|
53
|
+
const provider = config.services.inference.provider;
|
|
54
|
+
|
|
55
|
+
const maskedKeys: Record<string, string> = {};
|
|
56
|
+
for (const p of VALID_INFERENCE_PROVIDERS) {
|
|
57
|
+
const masked = await getMaskedProviderKey(p);
|
|
58
|
+
if (masked) maskedKeys[p] = masked;
|
|
59
|
+
}
|
|
60
|
+
|
|
36
61
|
return {
|
|
37
62
|
model: config.services.inference.model,
|
|
38
|
-
provider
|
|
63
|
+
provider,
|
|
39
64
|
configuredProviders: await getConfiguredProviders(),
|
|
65
|
+
availableModels: PROVIDER_CATALOG.find((p) => p.id === provider)?.models,
|
|
66
|
+
allProviders: PROVIDER_CATALOG,
|
|
67
|
+
maskedKeys,
|
|
40
68
|
};
|
|
41
69
|
}
|
|
42
70
|
|
|
@@ -58,28 +86,52 @@ export interface ModelSetContext {
|
|
|
58
86
|
/**
|
|
59
87
|
* Set the active model. Returns the resulting ModelInfo, or throws on failure.
|
|
60
88
|
* The caller is responsible for sending the response to the client.
|
|
89
|
+
*
|
|
90
|
+
* When `explicitProvider` is supplied, it takes precedence over automatic
|
|
91
|
+
* provider inference from the model ID. If the provider changes and the
|
|
92
|
+
* current model doesn't belong to the new provider's catalog, the model
|
|
93
|
+
* is auto-reset to the provider's default.
|
|
61
94
|
*/
|
|
62
95
|
export async function setModel(
|
|
63
96
|
modelId: string,
|
|
64
97
|
ctx: ModelSetContext,
|
|
98
|
+
explicitProvider?: string,
|
|
65
99
|
): Promise<ModelInfo> {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
//
|
|
69
|
-
{
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
100
|
+
const validProviders = new Set<string>(VALID_INFERENCE_PROVIDERS);
|
|
101
|
+
|
|
102
|
+
// Validate explicit provider against allowlist
|
|
103
|
+
if (explicitProvider && !validProviders.has(explicitProvider)) {
|
|
104
|
+
throw new Error(
|
|
105
|
+
`Invalid provider "${explicitProvider}". Valid providers: ${[...validProviders].join(", ")}`,
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Resolve provider: explicit > MODEL_TO_PROVIDER lookup > current
|
|
110
|
+
const current = getConfig();
|
|
111
|
+
const resolvedProvider =
|
|
112
|
+
explicitProvider ??
|
|
113
|
+
MODEL_TO_PROVIDER[modelId] ??
|
|
114
|
+
current.services.inference.provider;
|
|
115
|
+
|
|
116
|
+
// Auto-reset model when provider changes and current modelId doesn't
|
|
117
|
+
// belong to the new provider's catalog.
|
|
118
|
+
if (
|
|
119
|
+
resolvedProvider !== current.services.inference.provider &&
|
|
120
|
+
!isModelInCatalog(resolvedProvider, modelId)
|
|
121
|
+
) {
|
|
122
|
+
modelId = getProviderDefaultModel(resolvedProvider);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// No-op guard: skip expensive reinitialization when nothing changed
|
|
126
|
+
if (
|
|
127
|
+
modelId === current.services.inference.model &&
|
|
128
|
+
resolvedProvider === current.services.inference.provider
|
|
129
|
+
) {
|
|
130
|
+
return await getModelInfo();
|
|
78
131
|
}
|
|
79
132
|
|
|
80
133
|
// Validate provider availability (secure key, env var, or managed proxy) before switching
|
|
81
|
-
|
|
82
|
-
if (provider && !(await isProviderAvailable(provider))) {
|
|
134
|
+
if (!(await isProviderAvailable(resolvedProvider))) {
|
|
83
135
|
// Return current model_info so the client resyncs its optimistic state
|
|
84
136
|
return await getModelInfo();
|
|
85
137
|
}
|
|
@@ -87,10 +139,7 @@ export async function setModel(
|
|
|
87
139
|
// Use raw config to avoid persisting env-var API keys to disk
|
|
88
140
|
const raw = loadRawConfig();
|
|
89
141
|
setServiceField(raw, "inference", "model", modelId);
|
|
90
|
-
|
|
91
|
-
if (provider) {
|
|
92
|
-
setServiceField(raw, "inference", "provider", provider);
|
|
93
|
-
}
|
|
142
|
+
setServiceField(raw, "inference", "provider", resolvedProvider);
|
|
94
143
|
|
|
95
144
|
// Suppress the file watcher callback — setModel already does
|
|
96
145
|
// the full reload sequence; a redundant watcher-triggered reload
|
|
@@ -128,11 +177,7 @@ export async function setModel(
|
|
|
128
177
|
|
|
129
178
|
ctx.updateConfigFingerprint();
|
|
130
179
|
|
|
131
|
-
return
|
|
132
|
-
model: config.services.inference.model,
|
|
133
|
-
provider: config.services.inference.provider,
|
|
134
|
-
configuredProviders: await getConfiguredProviders(),
|
|
135
|
-
};
|
|
180
|
+
return await getModelInfo();
|
|
136
181
|
}
|
|
137
182
|
|
|
138
183
|
/**
|
|
@@ -179,7 +224,7 @@ export async function handleModelSet(
|
|
|
179
224
|
ctx: HandlerContext,
|
|
180
225
|
): Promise<void> {
|
|
181
226
|
try {
|
|
182
|
-
const info = await setModel(msg.model, ctx);
|
|
227
|
+
const info = await setModel(msg.model, ctx, msg.provider);
|
|
183
228
|
ctx.send({ type: "model_info", ...info });
|
|
184
229
|
} catch (err) {
|
|
185
230
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -9,7 +9,6 @@ import { getConfig } from "../../config/loader.js";
|
|
|
9
9
|
import {
|
|
10
10
|
createCanonicalGuardianRequest,
|
|
11
11
|
generateCanonicalRequestCode,
|
|
12
|
-
resolveCanonicalGuardianRequest,
|
|
13
12
|
} from "../../memory/canonical-guardian-store.js";
|
|
14
13
|
import {
|
|
15
14
|
batchSetDisplayOrders,
|
|
@@ -50,27 +49,6 @@ import {
|
|
|
50
49
|
pendingStandaloneSecrets,
|
|
51
50
|
} from "./shared.js";
|
|
52
51
|
|
|
53
|
-
export function syncCanonicalStatusFromConfirmationDecision(
|
|
54
|
-
requestId: string,
|
|
55
|
-
decision: ConfirmationResponse["decision"],
|
|
56
|
-
): void {
|
|
57
|
-
const targetStatus =
|
|
58
|
-
decision === "deny" || decision === "always_deny"
|
|
59
|
-
? ("denied" as const)
|
|
60
|
-
: ("approved" as const);
|
|
61
|
-
|
|
62
|
-
try {
|
|
63
|
-
resolveCanonicalGuardianRequest(requestId, "pending", {
|
|
64
|
-
status: targetStatus,
|
|
65
|
-
});
|
|
66
|
-
} catch (err) {
|
|
67
|
-
log.debug(
|
|
68
|
-
{ err, requestId, targetStatus },
|
|
69
|
-
"Failed to resolve canonical request from local confirmation response",
|
|
70
|
-
);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
52
|
export function makeEventSender(params: {
|
|
75
53
|
ctx: HandlerContext;
|
|
76
54
|
conversation: Conversation;
|
|
@@ -165,7 +143,6 @@ export function handleConfirmationResponse(
|
|
|
165
143
|
undefined,
|
|
166
144
|
{ source: "button" },
|
|
167
145
|
);
|
|
168
|
-
syncCanonicalStatusFromConfirmationDecision(msg.requestId, msg.decision);
|
|
169
146
|
pendingInteractions.resolve(msg.requestId);
|
|
170
147
|
return;
|
|
171
148
|
}
|
|
@@ -4,10 +4,10 @@ import * as path from "node:path";
|
|
|
4
4
|
import { v4 as uuid } from "uuid";
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
|
-
|
|
8
|
-
uploadFileBackedAttachment,
|
|
7
|
+
attachFileBackedAttachmentToMessage,
|
|
9
8
|
} from "../../memory/attachments-store.js";
|
|
10
|
-
import { addMessage } from "../../memory/conversation-crud.js";
|
|
9
|
+
import { addMessage, getConversation } from "../../memory/conversation-crud.js";
|
|
10
|
+
import { syncMessageToDisk } from "../../memory/conversation-disk-view.js";
|
|
11
11
|
import type { RecordingOptions, RecordingStatus } from "../message-protocol.js";
|
|
12
12
|
import { type HandlerContext, log } from "./shared.js";
|
|
13
13
|
|
|
@@ -616,23 +616,6 @@ export async function finalizeAndPublishRecording(params: {
|
|
|
616
616
|
const ext = filename.split(".").pop()?.toLowerCase();
|
|
617
617
|
const mimeType = (ext && RECORDING_MIME_TYPES.get(ext)) || "video/mp4";
|
|
618
618
|
|
|
619
|
-
// Store as file-backed attachment (avoids reading large files into memory)
|
|
620
|
-
const attachment = uploadFileBackedAttachment(
|
|
621
|
-
filename,
|
|
622
|
-
mimeType,
|
|
623
|
-
resolvedPath,
|
|
624
|
-
sizeBytes,
|
|
625
|
-
);
|
|
626
|
-
log.info(
|
|
627
|
-
{
|
|
628
|
-
recordingId,
|
|
629
|
-
attachmentId: attachment.id,
|
|
630
|
-
sizeBytes,
|
|
631
|
-
filePath: resolvedPath,
|
|
632
|
-
},
|
|
633
|
-
"Created attachment for standalone recording",
|
|
634
|
-
);
|
|
635
|
-
|
|
636
619
|
// Always create a new assistant message for the recording attachment.
|
|
637
620
|
// Reusing the last assistant message would attach the recording to an
|
|
638
621
|
// unrelated older message after reload.
|
|
@@ -648,12 +631,34 @@ export async function finalizeAndPublishRecording(params: {
|
|
|
648
631
|
"Created assistant message for recording attachment",
|
|
649
632
|
);
|
|
650
633
|
|
|
651
|
-
|
|
634
|
+
const attachment = attachFileBackedAttachmentToMessage(
|
|
635
|
+
messageId,
|
|
636
|
+
0,
|
|
637
|
+
filename,
|
|
638
|
+
mimeType,
|
|
639
|
+
resolvedPath,
|
|
640
|
+
sizeBytes,
|
|
641
|
+
);
|
|
642
|
+
log.info(
|
|
643
|
+
{
|
|
644
|
+
recordingId,
|
|
645
|
+
attachmentId: attachment.id,
|
|
646
|
+
sizeBytes,
|
|
647
|
+
filePath: resolvedPath,
|
|
648
|
+
},
|
|
649
|
+
"Created attachment for standalone recording",
|
|
650
|
+
);
|
|
652
651
|
log.info(
|
|
653
652
|
{ recordingId, messageId, attachmentId: attachment.id },
|
|
654
653
|
"Linked recording attachment to assistant message",
|
|
655
654
|
);
|
|
656
655
|
|
|
656
|
+
// Sync the recording message (with attachment) to the disk view
|
|
657
|
+
const convForDisk = getConversation(conversationId);
|
|
658
|
+
if (convForDisk) {
|
|
659
|
+
syncMessageToDisk(conversationId, messageId, convForDisk.createdAt);
|
|
660
|
+
}
|
|
661
|
+
|
|
657
662
|
// Skip server-side thumbnail generation for recordings — the client
|
|
658
663
|
// generates thumbnails natively from the local file path using
|
|
659
664
|
// AVAssetImageGenerator, which is faster and doesn't depend on ffmpeg.
|
|
@@ -69,7 +69,10 @@ export function repairHistory(messages: Message[]): RepairResult {
|
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
// Ensure every server_tool_use has a paired web_search_tool_result
|
|
72
|
-
// in the same assistant message (handles interrupted streams)
|
|
72
|
+
// in the same assistant message (handles interrupted streams).
|
|
73
|
+
// Synthetic results are inserted IMMEDIATELY AFTER their corresponding
|
|
74
|
+
// server_tool_use block — not appended to the end — so that
|
|
75
|
+
// ensureToolPairing's split at tool_use boundaries cannot separate them.
|
|
73
76
|
const serverToolIds = new Set(
|
|
74
77
|
cleanedContent
|
|
75
78
|
.filter(
|
|
@@ -82,18 +85,35 @@ export function repairHistory(messages: Message[]): RepairResult {
|
|
|
82
85
|
.filter((b) => b.type === "web_search_tool_result")
|
|
83
86
|
.map((b) => (b as { tool_use_id: string }).tool_use_id),
|
|
84
87
|
);
|
|
88
|
+
const orphanedServerIds = new Set<string>();
|
|
85
89
|
for (const id of serverToolIds) {
|
|
86
90
|
if (!matchedServerIds.has(id)) {
|
|
87
|
-
|
|
88
|
-
type: "web_search_tool_result",
|
|
89
|
-
tool_use_id: id,
|
|
90
|
-
content: SYNTHETIC_WEB_SEARCH_ERROR,
|
|
91
|
-
});
|
|
92
|
-
stats.missingToolResultsInserted++;
|
|
91
|
+
orphanedServerIds.add(id);
|
|
93
92
|
}
|
|
94
93
|
}
|
|
95
94
|
|
|
96
|
-
|
|
95
|
+
let repairedContent: ContentBlock[];
|
|
96
|
+
if (orphanedServerIds.size > 0) {
|
|
97
|
+
repairedContent = [];
|
|
98
|
+
for (const block of cleanedContent) {
|
|
99
|
+
repairedContent.push(block);
|
|
100
|
+
if (
|
|
101
|
+
block.type === "server_tool_use" &&
|
|
102
|
+
orphanedServerIds.has(block.id)
|
|
103
|
+
) {
|
|
104
|
+
repairedContent.push({
|
|
105
|
+
type: "web_search_tool_result",
|
|
106
|
+
tool_use_id: block.id,
|
|
107
|
+
content: SYNTHETIC_WEB_SEARCH_ERROR,
|
|
108
|
+
});
|
|
109
|
+
stats.missingToolResultsInserted++;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
} else {
|
|
113
|
+
repairedContent = cleanedContent;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
result.push({ role: "assistant", content: repairedContent });
|
|
97
117
|
|
|
98
118
|
// Only track client-side tool_use IDs as pending (not server_tool_use)
|
|
99
119
|
pendingToolUseIds = new Set(
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import { v4 as uuid } from "uuid";
|
|
11
11
|
|
|
12
12
|
import { escapeAxTreeContent } from "../agent/loop.js";
|
|
13
|
+
import { loadConfig } from "../config/loader.js";
|
|
13
14
|
import type { ContentBlock } from "../providers/types.js";
|
|
14
15
|
import type { ToolExecutionResult } from "../tools/types.js";
|
|
15
16
|
import { AssistantError, ErrorCode } from "../util/errors.js";
|
|
@@ -23,7 +24,6 @@ const log = getLogger("host-cu-proxy");
|
|
|
23
24
|
// ---------------------------------------------------------------------------
|
|
24
25
|
|
|
25
26
|
const REQUEST_TIMEOUT_SEC = 60;
|
|
26
|
-
const MAX_STEPS = 50;
|
|
27
27
|
const MAX_HISTORY_ENTRIES = 10;
|
|
28
28
|
const LOOP_DETECTION_WINDOW = 3;
|
|
29
29
|
const CONSECUTIVE_UNCHANGED_WARNING_THRESHOLD = 2;
|
|
@@ -79,7 +79,7 @@ export class HostCuProxy {
|
|
|
79
79
|
constructor(
|
|
80
80
|
sendToClient: (msg: ServerMessage) => void,
|
|
81
81
|
onInternalResolve?: (requestId: string) => void,
|
|
82
|
-
maxSteps =
|
|
82
|
+
maxSteps = loadConfig().maxStepsPerSession,
|
|
83
83
|
) {
|
|
84
84
|
this.sendToClient = sendToClient;
|
|
85
85
|
this.onInternalResolve = onInternalResolve;
|
package/src/daemon/lifecycle.ts
CHANGED
|
@@ -142,7 +142,6 @@ export async function runDaemon(): Promise<void> {
|
|
|
142
142
|
await initLogfire();
|
|
143
143
|
|
|
144
144
|
ensureDataDir();
|
|
145
|
-
await runWorkspaceMigrations(getWorkspaceDir(), WORKSPACE_MIGRATIONS);
|
|
146
145
|
|
|
147
146
|
// Load (or generate + persist) the auth signing key so tokens survive
|
|
148
147
|
// daemon restarts. Must happen after ensureDataDir() creates the
|
|
@@ -150,14 +149,16 @@ export async function runDaemon(): Promise<void> {
|
|
|
150
149
|
const signingKey = loadOrCreateSigningKey();
|
|
151
150
|
initAuthSigningKey(signingKey);
|
|
152
151
|
|
|
153
|
-
log.info("Daemon startup: migrations complete");
|
|
154
|
-
|
|
155
152
|
seedInterfaceFiles();
|
|
156
153
|
|
|
157
154
|
log.info("Daemon startup: installing templates and initializing DB");
|
|
158
155
|
installTemplates();
|
|
159
156
|
ensurePromptFiles();
|
|
160
157
|
|
|
158
|
+
// DB must be initialized before workspace migrations because some
|
|
159
|
+
// workspace migrations (e.g. 009-backfill-conversation-disk-view)
|
|
160
|
+
// depend on DB migrations having run (e.g. the inline-attachment-to-disk
|
|
161
|
+
// backfill that populates attachment filePaths).
|
|
161
162
|
initializeDb();
|
|
162
163
|
// Seed well-known OAuth provider configurations (insert-if-not-exists)
|
|
163
164
|
seedOAuthProviders();
|
|
@@ -174,6 +175,9 @@ export async function runDaemon(): Promise<void> {
|
|
|
174
175
|
}
|
|
175
176
|
log.info("Daemon startup: DB initialized");
|
|
176
177
|
|
|
178
|
+
await runWorkspaceMigrations(getWorkspaceDir(), WORKSPACE_MIGRATIONS);
|
|
179
|
+
log.info("Daemon startup: workspace migrations complete");
|
|
180
|
+
|
|
177
181
|
// Purge private (temporary) conversations from the previous daemon run.
|
|
178
182
|
// These are ephemeral by design and should not survive daemon restarts.
|
|
179
183
|
const { count: purgedCount, deletedMemory } = purgePrivateConversations();
|
|
@@ -210,17 +214,21 @@ export async function runDaemon(): Promise<void> {
|
|
|
210
214
|
}
|
|
211
215
|
}
|
|
212
216
|
|
|
213
|
-
// Expire pending
|
|
214
|
-
//
|
|
215
|
-
//
|
|
216
|
-
//
|
|
217
|
-
//
|
|
218
|
-
//
|
|
217
|
+
// Expire stale pending canonical guardian requests left over from before
|
|
218
|
+
// this process started. Two categories are cleaned up:
|
|
219
|
+
//
|
|
220
|
+
// 1. Interaction-bound kinds (tool_approval, pending_question) — their
|
|
221
|
+
// in-memory pending-interaction session references are gone, so they
|
|
222
|
+
// can never be completed.
|
|
223
|
+
// 2. Any pending request whose expiresAt has already passed — persistent
|
|
224
|
+
// kinds (access_request, tool_grant_request) that expired while the
|
|
225
|
+
// daemon was stopped are transitioned so dedup logic doesn't return
|
|
226
|
+
// stale rows.
|
|
219
227
|
const expiredCount = expireAllPendingCanonicalRequests();
|
|
220
228
|
if (expiredCount > 0) {
|
|
221
229
|
log.info(
|
|
222
230
|
{ event: "startup_expired_stale_requests", expiredCount },
|
|
223
|
-
`Expired ${expiredCount} stale
|
|
231
|
+
`Expired ${expiredCount} stale canonical request(s) from previous process`,
|
|
224
232
|
);
|
|
225
233
|
}
|
|
226
234
|
|
|
@@ -329,56 +337,74 @@ export async function runDaemon(): Promise<void> {
|
|
|
329
337
|
await server.start();
|
|
330
338
|
log.info("Daemon startup: DaemonServer started");
|
|
331
339
|
|
|
332
|
-
//
|
|
333
|
-
//
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
const
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
340
|
+
// Mutable refs for Qdrant and memory worker so background init can assign
|
|
341
|
+
// them and the shutdown handler always sees the latest value.
|
|
342
|
+
const bgRefs: {
|
|
343
|
+
qdrantManager: QdrantManager | null;
|
|
344
|
+
memoryWorker: { stop(): void } | null;
|
|
345
|
+
} = { qdrantManager: null, memoryWorker: null };
|
|
346
|
+
|
|
347
|
+
// Initialize Qdrant vector store and memory worker in the background so the
|
|
348
|
+
// RuntimeHttpServer can start accepting requests without waiting for Qdrant.
|
|
349
|
+
async function initializeQdrantAndMemory(): Promise<void> {
|
|
350
|
+
// Prefer QDRANT_HTTP_PORT (locally-spawned Qdrant on a specific port) over
|
|
351
|
+
// QDRANT_URL (external Qdrant instance) so the CLI can set the port without
|
|
352
|
+
// triggering QdrantManager's external mode which skips local process spawn.
|
|
353
|
+
const qdrantHttpPort = getQdrantHttpPortEnv();
|
|
354
|
+
const qdrantUrl = qdrantHttpPort
|
|
355
|
+
? `http://127.0.0.1:${qdrantHttpPort}`
|
|
356
|
+
: getQdrantUrlEnv() || config.memory.qdrant.url;
|
|
357
|
+
log.info({ qdrantUrl }, "Daemon startup: initializing Qdrant");
|
|
358
|
+
const manager = new QdrantManager({ url: qdrantUrl });
|
|
359
|
+
bgRefs.qdrantManager = manager;
|
|
360
|
+
try {
|
|
361
|
+
await manager.start();
|
|
362
|
+
const embeddingSelection = await selectEmbeddingBackend(config);
|
|
363
|
+
const embeddingModel = embeddingSelection.backend
|
|
364
|
+
? `${embeddingSelection.backend.provider}:${embeddingSelection.backend.model}:sparse-v${SPARSE_EMBEDDING_VERSION}`
|
|
365
|
+
: undefined;
|
|
366
|
+
const qdrantClient = initQdrantClient({
|
|
367
|
+
url: qdrantUrl,
|
|
368
|
+
collection: config.memory.qdrant.collection,
|
|
369
|
+
vectorSize: config.memory.qdrant.vectorSize,
|
|
370
|
+
onDisk: config.memory.qdrant.onDisk,
|
|
371
|
+
quantization: config.memory.qdrant.quantization,
|
|
372
|
+
embeddingModel,
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
// Eagerly ensure the collection exists so we detect migrations
|
|
376
|
+
// (unnamed→named vectors, dimension/model changes) at startup.
|
|
377
|
+
// If a destructive migration occurred, enqueue a rebuild_index job
|
|
378
|
+
// to re-embed all memory items from the SQLite cache.
|
|
379
|
+
const { migrated } = await qdrantClient.ensureCollection();
|
|
380
|
+
if (migrated) {
|
|
381
|
+
enqueueMemoryJob("rebuild_index", {});
|
|
382
|
+
log.info(
|
|
383
|
+
"Qdrant collection was migrated — enqueued rebuild_index job",
|
|
384
|
+
);
|
|
385
|
+
}
|
|
356
386
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
enqueueMemoryJob("rebuild_index", {});
|
|
364
|
-
log.info("Qdrant collection was migrated — enqueued rebuild_index job");
|
|
387
|
+
log.info("Qdrant vector store initialized");
|
|
388
|
+
} catch (err) {
|
|
389
|
+
log.warn(
|
|
390
|
+
{ err },
|
|
391
|
+
"Qdrant failed to start — memory features will be unavailable",
|
|
392
|
+
);
|
|
365
393
|
}
|
|
366
394
|
|
|
367
|
-
log.info("
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
395
|
+
log.info("Daemon startup: starting memory worker");
|
|
396
|
+
bgRefs.memoryWorker = startMemoryJobsWorker();
|
|
397
|
+
|
|
398
|
+
// Seed capability memories for first-party catalog skills so the memory
|
|
399
|
+
// pipeline can surface relevant skills via semantic search.
|
|
400
|
+
void seedCatalogSkillMemories().catch((err) =>
|
|
401
|
+
log.warn({ err }, "Catalog skill memory seeding failed — continuing"),
|
|
372
402
|
);
|
|
373
403
|
}
|
|
374
404
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
// Seed capability memories for first-party catalog skills so the memory
|
|
379
|
-
// pipeline can surface relevant skills via semantic search.
|
|
380
|
-
void seedCatalogSkillMemories().catch((err) =>
|
|
381
|
-
log.warn({ err }, "Catalog skill memory seeding failed — continuing"),
|
|
405
|
+
// Fire-and-forget: Qdrant init runs concurrently with the rest of startup
|
|
406
|
+
void initializeQdrantAndMemory().catch((err) =>
|
|
407
|
+
log.warn({ err }, "Background Qdrant init failed"),
|
|
382
408
|
);
|
|
383
409
|
|
|
384
410
|
registerWatcherProviders();
|
|
@@ -459,7 +485,7 @@ export async function runDaemon(): Promise<void> {
|
|
|
459
485
|
title: notification.title,
|
|
460
486
|
body: notification.body,
|
|
461
487
|
},
|
|
462
|
-
dedupeKey: `watcher:notification:${
|
|
488
|
+
dedupeKey: `watcher:notification:${crypto.randomUUID()}`,
|
|
463
489
|
});
|
|
464
490
|
},
|
|
465
491
|
(params) => {
|
|
@@ -477,7 +503,7 @@ export async function runDaemon(): Promise<void> {
|
|
|
477
503
|
title: params.title,
|
|
478
504
|
body: params.body,
|
|
479
505
|
},
|
|
480
|
-
dedupeKey: `watcher:escalation:${
|
|
506
|
+
dedupeKey: `watcher:escalation:${crypto.randomUUID()}`,
|
|
481
507
|
});
|
|
482
508
|
},
|
|
483
509
|
(info) => {
|
|
@@ -532,13 +558,22 @@ export async function runDaemon(): Promise<void> {
|
|
|
532
558
|
getOrCreateConversation: (conversationId) =>
|
|
533
559
|
server.getConversationForMessages(conversationId),
|
|
534
560
|
assistantEventHub,
|
|
535
|
-
resolveAttachments: (attachmentIds) =>
|
|
536
|
-
attachmentsStore.getAttachmentsByIds(attachmentIds
|
|
561
|
+
resolveAttachments: (attachmentIds) => {
|
|
562
|
+
const resolved = attachmentsStore.getAttachmentsByIds(attachmentIds, {
|
|
563
|
+
hydrateFileData: true,
|
|
564
|
+
});
|
|
565
|
+
const sourcePaths =
|
|
566
|
+
attachmentsStore.getSourcePathsForAttachments(attachmentIds);
|
|
567
|
+
return resolved.map((a) => ({
|
|
537
568
|
id: a.id,
|
|
538
569
|
filename: a.originalFilename,
|
|
539
570
|
mimeType: a.mimeType,
|
|
540
571
|
data: a.dataBase64,
|
|
541
|
-
|
|
572
|
+
...(sourcePaths.has(a.id)
|
|
573
|
+
? { filePath: sourcePaths.get(a.id) }
|
|
574
|
+
: {}),
|
|
575
|
+
}));
|
|
576
|
+
},
|
|
542
577
|
},
|
|
543
578
|
findConversation: (conversationId) =>
|
|
544
579
|
server.findConversation(conversationId),
|
|
@@ -623,13 +658,20 @@ export async function runDaemon(): Promise<void> {
|
|
|
623
658
|
setVoiceBridgeDeps({
|
|
624
659
|
getOrCreateConversation: (conversationId, _transport) =>
|
|
625
660
|
server.getConversationForMessages(conversationId),
|
|
626
|
-
resolveAttachments: (attachmentIds) =>
|
|
627
|
-
attachmentsStore.getAttachmentsByIds(attachmentIds
|
|
661
|
+
resolveAttachments: (attachmentIds) => {
|
|
662
|
+
const resolved = attachmentsStore.getAttachmentsByIds(attachmentIds, {
|
|
663
|
+
hydrateFileData: true,
|
|
664
|
+
});
|
|
665
|
+
const sourcePaths =
|
|
666
|
+
attachmentsStore.getSourcePathsForAttachments(attachmentIds);
|
|
667
|
+
return resolved.map((a) => ({
|
|
628
668
|
id: a.id,
|
|
629
669
|
filename: a.originalFilename,
|
|
630
670
|
mimeType: a.mimeType,
|
|
631
671
|
data: a.dataBase64,
|
|
632
|
-
|
|
672
|
+
...(sourcePaths.has(a.id) ? { filePath: sourcePaths.get(a.id) } : {}),
|
|
673
|
+
}));
|
|
674
|
+
},
|
|
633
675
|
deriveDefaultStrictSideEffects: (conversationId) => {
|
|
634
676
|
const conversationType = getConversationType(conversationId);
|
|
635
677
|
return conversationType === "private";
|
|
@@ -881,8 +923,8 @@ export async function runDaemon(): Promise<void> {
|
|
|
881
923
|
hookManager,
|
|
882
924
|
runtimeHttp,
|
|
883
925
|
scheduler,
|
|
884
|
-
memoryWorker,
|
|
885
|
-
qdrantManager,
|
|
926
|
+
getMemoryWorker: () => bgRefs.memoryWorker,
|
|
927
|
+
getQdrantManager: () => bgRefs.qdrantManager,
|
|
886
928
|
mcpManager,
|
|
887
929
|
telemetryReporter,
|
|
888
930
|
cleanupPidFile,
|
|
@@ -38,6 +38,7 @@ export * from "./message-types/skills.js";
|
|
|
38
38
|
export * from "./message-types/subagents.js";
|
|
39
39
|
export * from "./message-types/surfaces.js";
|
|
40
40
|
export * from "./message-types/trust.js";
|
|
41
|
+
export * from "./message-types/upgrades.js";
|
|
41
42
|
export * from "./message-types/work-items.js";
|
|
42
43
|
export * from "./message-types/workspace.js";
|
|
43
44
|
|
|
@@ -123,6 +124,7 @@ import type {
|
|
|
123
124
|
_TrustClientMessages,
|
|
124
125
|
_TrustServerMessages,
|
|
125
126
|
} from "./message-types/trust.js";
|
|
127
|
+
import type { _UpgradesServerMessages } from "./message-types/upgrades.js";
|
|
126
128
|
import type {
|
|
127
129
|
_WorkItemsClientMessages,
|
|
128
130
|
_WorkItemsServerMessages,
|
|
@@ -194,6 +196,7 @@ export type ServerMessage =
|
|
|
194
196
|
| _InboxServerMessages
|
|
195
197
|
| _PairingServerMessages
|
|
196
198
|
| _NotificationsServerMessages
|
|
199
|
+
| _UpgradesServerMessages
|
|
197
200
|
| _AcpServerMessages
|
|
198
201
|
| SubagentEvent;
|
|
199
202
|
|