@vellumai/assistant 0.5.1 → 0.5.3
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 +163 -54
- package/docs/architecture/integrations.md +62 -67
- package/docs/credential-execution-service.md +3 -3
- package/docs/skills.md +100 -0
- 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__/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 +156 -5
- package/src/__tests__/conversation-agent-loop.test.ts +297 -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-memory-dirty-tail.test.ts +150 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +7 -0
- 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-wipe.test.ts +226 -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-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__/db-memory-archive-migration.test.ts +372 -0
- package/src/__tests__/db-memory-brief-state-migration.test.ts +213 -0
- package/src/__tests__/db-memory-reducer-checkpoints.test.ts +273 -0
- package/src/__tests__/diagnostics-export.test.ts +70 -1
- 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 +32 -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__/inline-command-runner.test.ts +311 -0
- package/src/__tests__/inline-skill-authoring-guard.test.ts +220 -0
- package/src/__tests__/inline-skill-load-permissions.test.ts +435 -0
- package/src/__tests__/list-messages-attachments.test.ts +96 -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-brief-open-loops.test.ts +530 -0
- package/src/__tests__/memory-brief-time.test.ts +285 -0
- package/src/__tests__/memory-brief-wrapper.test.ts +311 -0
- package/src/__tests__/memory-chunk-archive.test.ts +400 -0
- package/src/__tests__/memory-chunk-dual-write.test.ts +453 -0
- package/src/__tests__/memory-episode-archive.test.ts +370 -0
- package/src/__tests__/memory-episode-dual-write.test.ts +626 -0
- package/src/__tests__/memory-lifecycle-e2e.test.ts +3 -1
- package/src/__tests__/memory-observation-archive.test.ts +375 -0
- package/src/__tests__/memory-observation-dual-write.test.ts +318 -0
- package/src/__tests__/memory-recall-quality.test.ts +7 -7
- package/src/__tests__/memory-reducer-store.test.ts +728 -0
- package/src/__tests__/memory-reducer-types.test.ts +699 -0
- package/src/__tests__/memory-reducer.test.ts +698 -0
- package/src/__tests__/memory-regressions.test.ts +6 -4
- package/src/__tests__/memory-simplified-config.test.ts +281 -0
- 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__/parse-identity-fields.test.ts +129 -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-load-inline-command.test.ts +598 -0
- package/src/__tests__/skill-load-inline-includes.test.ts +644 -0
- package/src/__tests__/skills-inline-command-expansions.test.ts +301 -0
- package/src/__tests__/skills-transitive-hash.test.ts +333 -0
- 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__/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__/vellum-self-knowledge-inline-command.test.ts +320 -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/SKILL.md +1 -1
- 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 +24 -0
- package/src/config/loader.ts +65 -0
- package/src/config/raw-config-utils.ts +58 -0
- package/src/config/schema-utils.ts +28 -7
- package/src/config/schema.ts +20 -0
- package/src/config/schemas/elevenlabs.ts +18 -0
- package/src/config/schemas/memory-lifecycle.ts +4 -2
- package/src/config/schemas/memory-simplified.ts +101 -0
- package/src/config/schemas/memory-storage.ts +1 -1
- package/src/config/schemas/memory.ts +4 -0
- package/src/config/schemas/services.ts +8 -6
- package/src/config/skills.ts +50 -4
- 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 +54 -8
- package/src/daemon/conversation-agent-loop.ts +127 -20
- 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 +50 -16
- package/src/daemon/conversation-messaging.ts +70 -26
- package/src/daemon/conversation-process.ts +58 -34
- package/src/daemon/conversation-runtime-assembly.ts +22 -38
- package/src/daemon/conversation-slash.ts +121 -256
- package/src/daemon/conversation-surfaces.ts +170 -24
- package/src/daemon/conversation-tool-setup.ts +0 -6
- package/src/daemon/conversation-workspace.ts +21 -1
- package/src/daemon/conversation.ts +69 -30
- package/src/daemon/first-greeting.ts +35 -0
- package/src/daemon/handlers/config-embeddings.ts +156 -0
- package/src/daemon/handlers/config-model.ts +62 -26
- package/src/daemon/handlers/conversations.ts +0 -23
- package/src/daemon/handlers/identity.ts +12 -1
- package/src/daemon/handlers/recording.ts +26 -21
- package/src/daemon/host-cu-proxy.ts +2 -2
- package/src/daemon/lifecycle.ts +115 -65
- package/src/daemon/message-protocol.ts +3 -0
- package/src/daemon/message-types/conversations.ts +18 -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/followups/followup-store.ts +47 -1
- 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/archive-store.ts +400 -0
- package/src/memory/attachments-store.ts +558 -130
- package/src/memory/brief-formatting.ts +33 -0
- package/src/memory/brief-open-loops.ts +266 -0
- package/src/memory/brief-time.ts +161 -0
- package/src/memory/brief.ts +75 -0
- package/src/memory/conversation-attention-store.ts +70 -0
- package/src/memory/conversation-crud.ts +591 -8
- 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 +40 -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 +114 -21
- 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 +2 -4
- package/src/memory/job-handlers/embedding.ts +83 -0
- package/src/memory/job-utils.ts +1 -1
- package/src/memory/jobs-store.ts +6 -0
- package/src/memory/jobs-worker.ts +12 -0
- 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/185-memory-brief-state.ts +52 -0
- package/src/memory/migrations/186-memory-archive.ts +109 -0
- package/src/memory/migrations/187-memory-reducer-checkpoints.ts +19 -0
- package/src/memory/migrations/index.ts +10 -0
- package/src/memory/migrations/registry.ts +13 -0
- package/src/memory/qdrant-client.ts +23 -4
- package/src/memory/reducer-store.ts +271 -0
- package/src/memory/reducer-types.ts +99 -0
- package/src/memory/reducer.ts +453 -0
- package/src/memory/retriever.test.ts +601 -2
- package/src/memory/retriever.ts +85 -9
- package/src/memory/schema/conversations.ts +9 -0
- package/src/memory/schema/index.ts +2 -0
- package/src/memory/schema/infrastructure.ts +13 -7
- package/src/memory/schema/memory-archive.ts +121 -0
- package/src/memory/schema/memory-brief.ts +55 -0
- package/src/memory/schema/oauth.ts +6 -0
- package/src/memory/search/semantic.ts +17 -4
- 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 +99 -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 +160 -14
- package/src/permissions/defaults.ts +14 -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 +74 -0
- package/src/runtime/routes/conversation-query-routes.ts +187 -10
- package/src/runtime/routes/conversation-routes.ts +269 -28
- package/src/runtime/routes/conversation-starter-routes.ts +9 -11
- package/src/runtime/routes/diagnostics-routes.ts +1 -0
- package/src/runtime/routes/identity-routes.ts +2 -35
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +2 -2
- package/src/runtime/routes/llm-context-normalization.ts +1212 -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 +94 -5
- 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 +30 -1
- package/src/runtime/routes/settings-routes.ts +14 -0
- package/src/runtime/routes/surface-action-routes.ts +68 -1
- package/src/runtime/routes/trace-event-routes.ts +4 -1
- package/src/schedule/schedule-store.ts +30 -21
- package/src/security/secure-keys.ts +21 -0
- package/src/signals/bash.ts +1 -1
- package/src/skills/inline-command-expansions.ts +204 -0
- package/src/skills/inline-command-render.ts +127 -0
- package/src/skills/inline-command-runner.ts +242 -0
- package/src/skills/transitive-version-hash.ts +88 -0
- package/src/swarm/backend-claude-code.ts +3 -6
- package/src/tasks/task-store.ts +43 -1
- 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/permission-checker.ts +8 -1
- package/src/tools/schedule/list.ts +2 -7
- package/src/tools/schema-transforms.ts +5 -0
- package/src/tools/shared/filesystem/format-diff.ts +2 -7
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/load.ts +140 -6
- 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 +24 -0
- package/src/util/pricing.ts +1 -0
- package/src/util/retry.ts +1 -3
- 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/{002-backfill-installation-id.ts → 011-backfill-installation-id.ts} +24 -13
- 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 +11 -1
- 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
|
@@ -444,6 +444,7 @@ const WORKSPACE_SKIP_DIRS = new Set([
|
|
|
444
444
|
"embedding-models",
|
|
445
445
|
"data/qdrant",
|
|
446
446
|
"data/attachments",
|
|
447
|
+
"conversations",
|
|
447
448
|
]);
|
|
448
449
|
|
|
449
450
|
/** Files at the workspace root to skip (already covered by sanitized fields). */
|
|
@@ -513,6 +514,8 @@ function collectWorkspaceFiles(): Record<string, string> {
|
|
|
513
514
|
|
|
514
515
|
// SQLite DB handling: dump as SQL text, then enforce size cap
|
|
515
516
|
if (entry.endsWith(".db")) {
|
|
517
|
+
// Skip the dump entirely if the budget is already exhausted
|
|
518
|
+
if (totalBytes >= MAX_WORKSPACE_PAYLOAD_BYTES) continue;
|
|
516
519
|
try {
|
|
517
520
|
const proc = spawnSync("sqlite3", [fullPath, ".dump"], {
|
|
518
521
|
timeout: 10_000,
|
|
@@ -347,6 +347,40 @@ describe("Memory Item Routes", () => {
|
|
|
347
347
|
expect(body.items[1].id).toBe("i1");
|
|
348
348
|
});
|
|
349
349
|
|
|
350
|
+
test("supports sort by accessCount descending", async () => {
|
|
351
|
+
insertItem({
|
|
352
|
+
id: "i1",
|
|
353
|
+
kind: "preference",
|
|
354
|
+
subject: "s1",
|
|
355
|
+
statement: "st1",
|
|
356
|
+
});
|
|
357
|
+
insertItem({
|
|
358
|
+
id: "i2",
|
|
359
|
+
kind: "preference",
|
|
360
|
+
subject: "s2",
|
|
361
|
+
statement: "st2",
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
getDb()
|
|
365
|
+
.update(memoryItems)
|
|
366
|
+
.set({ accessCount: 2 })
|
|
367
|
+
.where(eq(memoryItems.id, "i1"))
|
|
368
|
+
.run();
|
|
369
|
+
getDb()
|
|
370
|
+
.update(memoryItems)
|
|
371
|
+
.set({ accessCount: 7 })
|
|
372
|
+
.where(eq(memoryItems.id, "i2"))
|
|
373
|
+
.run();
|
|
374
|
+
|
|
375
|
+
const ctx = makeCtx({ sort: "accessCount", order: "desc" });
|
|
376
|
+
const res = await handler(ctx);
|
|
377
|
+
const body = (await res.json()) as {
|
|
378
|
+
items: Array<{ id: string }>;
|
|
379
|
+
};
|
|
380
|
+
expect(body.items[0].id).toBe("i2");
|
|
381
|
+
expect(body.items[1].id).toBe("i1");
|
|
382
|
+
});
|
|
383
|
+
|
|
350
384
|
test("rejects invalid kind filter", async () => {
|
|
351
385
|
const ctx = makeCtx({ kind: "bogus" });
|
|
352
386
|
const res = await handler(ctx);
|
|
@@ -8,13 +8,17 @@
|
|
|
8
8
|
* DELETE /v1/memory-items/:id — delete a memory item and its embeddings
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { and, asc, count, desc, eq, like, ne, or } from "drizzle-orm";
|
|
11
|
+
import { and, asc, count, desc, eq, inArray, like, ne, or } from "drizzle-orm";
|
|
12
12
|
import { v4 as uuid } from "uuid";
|
|
13
13
|
|
|
14
14
|
import { getDb } from "../../memory/db.js";
|
|
15
15
|
import { computeMemoryFingerprint } from "../../memory/fingerprint.js";
|
|
16
16
|
import { enqueueMemoryJob } from "../../memory/jobs-store.js";
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
conversations,
|
|
19
|
+
memoryEmbeddings,
|
|
20
|
+
memoryItems,
|
|
21
|
+
} from "../../memory/schema.js";
|
|
18
22
|
import { truncate } from "../../util/truncate.js";
|
|
19
23
|
import { httpError } from "../http-errors.js";
|
|
20
24
|
import type { RouteContext, RouteDefinition } from "../http-router.js";
|
|
@@ -37,6 +41,7 @@ type MemoryItemKind = (typeof VALID_KINDS)[number];
|
|
|
37
41
|
const VALID_SORT_FIELDS = [
|
|
38
42
|
"lastSeenAt",
|
|
39
43
|
"importance",
|
|
44
|
+
"accessCount",
|
|
40
45
|
"kind",
|
|
41
46
|
"firstSeenAt",
|
|
42
47
|
] as const;
|
|
@@ -46,6 +51,7 @@ type SortField = (typeof VALID_SORT_FIELDS)[number];
|
|
|
46
51
|
const SORT_COLUMN_MAP = {
|
|
47
52
|
lastSeenAt: memoryItems.lastSeenAt,
|
|
48
53
|
importance: memoryItems.importance,
|
|
54
|
+
accessCount: memoryItems.accessCount,
|
|
49
55
|
kind: memoryItems.kind,
|
|
50
56
|
firstSeenAt: memoryItems.firstSeenAt,
|
|
51
57
|
} as const;
|
|
@@ -62,6 +68,54 @@ function isValidSortField(value: string): value is SortField {
|
|
|
62
68
|
return (VALID_SORT_FIELDS as readonly string[]).includes(value);
|
|
63
69
|
}
|
|
64
70
|
|
|
71
|
+
/**
|
|
72
|
+
* Resolve a `scopeLabel` for a memory item based on its `scopeId`.
|
|
73
|
+
*
|
|
74
|
+
* - `"default"` → `null`
|
|
75
|
+
* - `"private:<conversationId>"` → `"Private · <title>"` when the conversation
|
|
76
|
+
* has a title, or `"Private"` when it doesn't (or the conversation was deleted).
|
|
77
|
+
*/
|
|
78
|
+
function resolveScopeLabel(
|
|
79
|
+
scopeId: string,
|
|
80
|
+
titleMap: Map<string, string | null>,
|
|
81
|
+
): string | null {
|
|
82
|
+
if (scopeId === "default") return null;
|
|
83
|
+
if (scopeId.startsWith("private:")) {
|
|
84
|
+
const conversationId = scopeId.slice("private:".length);
|
|
85
|
+
const title = titleMap.get(conversationId);
|
|
86
|
+
return title ? `Private · ${title}` : "Private";
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Batch-fetch conversation titles for a set of private-scoped memory items.
|
|
93
|
+
* Returns a Map from conversation ID → title (or null).
|
|
94
|
+
*/
|
|
95
|
+
function buildConversationTitleMap(
|
|
96
|
+
db: ReturnType<typeof getDb>,
|
|
97
|
+
scopeIds: string[],
|
|
98
|
+
): Map<string, string | null> {
|
|
99
|
+
const conversationIds = scopeIds
|
|
100
|
+
.filter((s) => s.startsWith("private:"))
|
|
101
|
+
.map((s) => s.slice("private:".length));
|
|
102
|
+
|
|
103
|
+
const uniqueIds = [...new Set(conversationIds)];
|
|
104
|
+
if (uniqueIds.length === 0) return new Map();
|
|
105
|
+
|
|
106
|
+
const rows = db
|
|
107
|
+
.select({ id: conversations.id, title: conversations.title })
|
|
108
|
+
.from(conversations)
|
|
109
|
+
.where(inArray(conversations.id, uniqueIds))
|
|
110
|
+
.all();
|
|
111
|
+
|
|
112
|
+
const map = new Map<string, string | null>();
|
|
113
|
+
for (const row of rows) {
|
|
114
|
+
map.set(row.id, row.title);
|
|
115
|
+
}
|
|
116
|
+
return map;
|
|
117
|
+
}
|
|
118
|
+
|
|
65
119
|
// ---------------------------------------------------------------------------
|
|
66
120
|
// GET /v1/memory-items
|
|
67
121
|
// ---------------------------------------------------------------------------
|
|
@@ -103,6 +157,8 @@ export function handleListMemoryItems(url: URL): Response {
|
|
|
103
157
|
|
|
104
158
|
// Build WHERE conditions
|
|
105
159
|
const conditions = [];
|
|
160
|
+
// Hide system-managed capability memories (skill announcements) from the UI
|
|
161
|
+
conditions.push(ne(memoryItems.kind, "capability"));
|
|
106
162
|
if (statusParam && statusParam !== "all") {
|
|
107
163
|
conditions.push(eq(memoryItems.status, statusParam));
|
|
108
164
|
}
|
|
@@ -141,7 +197,17 @@ export function handleListMemoryItems(url: URL): Response {
|
|
|
141
197
|
.offset(offsetParam)
|
|
142
198
|
.all();
|
|
143
199
|
|
|
144
|
-
|
|
200
|
+
// Resolve scope labels for private-scoped items
|
|
201
|
+
const titleMap = buildConversationTitleMap(
|
|
202
|
+
db,
|
|
203
|
+
items.map((i) => i.scopeId),
|
|
204
|
+
);
|
|
205
|
+
const enrichedItems = items.map((item) => ({
|
|
206
|
+
...item,
|
|
207
|
+
scopeLabel: resolveScopeLabel(item.scopeId, titleMap),
|
|
208
|
+
}));
|
|
209
|
+
|
|
210
|
+
return Response.json({ items: enrichedItems, total });
|
|
145
211
|
}
|
|
146
212
|
|
|
147
213
|
// ---------------------------------------------------------------------------
|
|
@@ -183,9 +249,14 @@ export function handleGetMemoryItem(ctx: RouteContext): Response {
|
|
|
183
249
|
supersededBySubject = superseding?.subject;
|
|
184
250
|
}
|
|
185
251
|
|
|
252
|
+
// Resolve scope label
|
|
253
|
+
const titleMap = buildConversationTitleMap(db, [item.scopeId]);
|
|
254
|
+
const scopeLabel = resolveScopeLabel(item.scopeId, titleMap);
|
|
255
|
+
|
|
186
256
|
return Response.json({
|
|
187
257
|
item: {
|
|
188
258
|
...item,
|
|
259
|
+
scopeLabel,
|
|
189
260
|
...(supersedesSubject !== undefined ? { supersedesSubject } : {}),
|
|
190
261
|
...(supersededBySubject !== undefined ? { supersededBySubject } : {}),
|
|
191
262
|
},
|
|
@@ -299,7 +370,14 @@ export async function handleCreateMemoryItem(
|
|
|
299
370
|
.where(eq(memoryItems.id, id))
|
|
300
371
|
.get();
|
|
301
372
|
|
|
302
|
-
|
|
373
|
+
// Enrich with scopeLabel for API consistency
|
|
374
|
+
const titleMap = buildConversationTitleMap(db, [scopeId]);
|
|
375
|
+
const scopeLabel = resolveScopeLabel(scopeId, titleMap);
|
|
376
|
+
|
|
377
|
+
return Response.json(
|
|
378
|
+
{ item: { ...insertedRow, scopeLabel } },
|
|
379
|
+
{ status: 201 },
|
|
380
|
+
);
|
|
303
381
|
}
|
|
304
382
|
|
|
305
383
|
// ---------------------------------------------------------------------------
|
|
@@ -426,7 +504,18 @@ export async function handleUpdateMemoryItem(
|
|
|
426
504
|
.where(eq(memoryItems.id, id))
|
|
427
505
|
.get();
|
|
428
506
|
|
|
429
|
-
|
|
507
|
+
// Enrich with scopeLabel for API consistency
|
|
508
|
+
const patchTitleMap = buildConversationTitleMap(db, [
|
|
509
|
+
updatedRow?.scopeId ?? existing.scopeId,
|
|
510
|
+
]);
|
|
511
|
+
const patchScopeLabel = resolveScopeLabel(
|
|
512
|
+
updatedRow?.scopeId ?? existing.scopeId,
|
|
513
|
+
patchTitleMap,
|
|
514
|
+
);
|
|
515
|
+
|
|
516
|
+
return Response.json({
|
|
517
|
+
item: { ...updatedRow, scopeLabel: patchScopeLabel },
|
|
518
|
+
});
|
|
430
519
|
}
|
|
431
520
|
|
|
432
521
|
// ---------------------------------------------------------------------------
|
|
@@ -160,7 +160,10 @@ export async function handleMigrationExport(req: Request): Promise<Response> {
|
|
|
160
160
|
// Best-effort: if the DB can't be checkpointed (e.g. not a valid
|
|
161
161
|
// SQLite file, missing WAL, etc.) we still proceed with the export
|
|
162
162
|
// using whatever is on disk.
|
|
163
|
-
log.warn(
|
|
163
|
+
log.warn(
|
|
164
|
+
{ err },
|
|
165
|
+
"WAL checkpoint failed — exporting without checkpoint",
|
|
166
|
+
);
|
|
164
167
|
}
|
|
165
168
|
},
|
|
166
169
|
});
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Route handlers for OAuth app and connection CRUD.
|
|
3
|
+
*
|
|
4
|
+
* Provides endpoints for managing user-supplied OAuth apps (e.g. "your own"
|
|
5
|
+
* Google client credentials) and their connections. All endpoints are
|
|
6
|
+
* bearer-token authenticated via the standard runtime auth middleware.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { orchestrateOAuthConnect } from "../../oauth/connect-orchestrator.js";
|
|
10
|
+
import {
|
|
11
|
+
deleteApp,
|
|
12
|
+
disconnectOAuthProvider,
|
|
13
|
+
getApp,
|
|
14
|
+
getAppClientSecret,
|
|
15
|
+
getConnection,
|
|
16
|
+
getProvider,
|
|
17
|
+
listApps,
|
|
18
|
+
listConnections,
|
|
19
|
+
upsertApp,
|
|
20
|
+
} from "../../oauth/oauth-store.js";
|
|
21
|
+
import { httpError } from "../http-errors.js";
|
|
22
|
+
import type { RouteDefinition } from "../http-router.js";
|
|
23
|
+
|
|
24
|
+
function parseGrantedScopes(grantedScopes: string | string[] | null | undefined): string[] {
|
|
25
|
+
if (Array.isArray(grantedScopes)) {
|
|
26
|
+
return grantedScopes.filter((scope): scope is string => typeof scope === "string");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (typeof grantedScopes !== "string" || grantedScopes.trim() === "") {
|
|
30
|
+
return [];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
try {
|
|
34
|
+
const parsed = JSON.parse(grantedScopes) as unknown;
|
|
35
|
+
if (!Array.isArray(parsed)) return [];
|
|
36
|
+
return parsed.filter((scope): scope is string => typeof scope === "string");
|
|
37
|
+
} catch {
|
|
38
|
+
return [];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function normalizeHasRefreshToken(
|
|
43
|
+
hasRefreshToken: boolean | number | null | undefined,
|
|
44
|
+
): boolean {
|
|
45
|
+
return hasRefreshToken === true || hasRefreshToken === 1;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Build route definitions for OAuth app and connection CRUD endpoints.
|
|
50
|
+
*/
|
|
51
|
+
export function oauthAppsRouteDefinitions(): RouteDefinition[] {
|
|
52
|
+
return [
|
|
53
|
+
// GET /v1/oauth/apps — List apps filtered by provider_key query param.
|
|
54
|
+
{
|
|
55
|
+
endpoint: "oauth/apps",
|
|
56
|
+
method: "GET",
|
|
57
|
+
handler: ({ url }) => {
|
|
58
|
+
const providerKey = url.searchParams.get("provider_key");
|
|
59
|
+
if (!providerKey) {
|
|
60
|
+
return httpError(
|
|
61
|
+
"BAD_REQUEST",
|
|
62
|
+
"provider_key query parameter is required",
|
|
63
|
+
400,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const allApps = listApps();
|
|
68
|
+
const filtered = allApps.filter(
|
|
69
|
+
(row) => row.providerKey === providerKey,
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
const providerRow = getProvider(providerKey);
|
|
73
|
+
const provider = providerRow
|
|
74
|
+
? {
|
|
75
|
+
provider_key: providerRow.providerKey,
|
|
76
|
+
display_name: providerRow.displayName ?? null,
|
|
77
|
+
description: providerRow.description ?? null,
|
|
78
|
+
dashboard_url: providerRow.dashboardUrl ?? null,
|
|
79
|
+
client_id_placeholder: providerRow.clientIdPlaceholder ?? null,
|
|
80
|
+
requires_client_secret: providerRow.requiresClientSecret ?? 1,
|
|
81
|
+
}
|
|
82
|
+
: null;
|
|
83
|
+
|
|
84
|
+
return Response.json({
|
|
85
|
+
provider,
|
|
86
|
+
apps: filtered.map((row) => ({
|
|
87
|
+
id: row.id,
|
|
88
|
+
provider_key: row.providerKey,
|
|
89
|
+
client_id: row.clientId,
|
|
90
|
+
created_at: row.createdAt,
|
|
91
|
+
updated_at: row.updatedAt,
|
|
92
|
+
})),
|
|
93
|
+
});
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
|
|
97
|
+
// POST /v1/oauth/apps — Create an OAuth app.
|
|
98
|
+
{
|
|
99
|
+
endpoint: "oauth/apps",
|
|
100
|
+
method: "POST",
|
|
101
|
+
policyKey: "oauth/apps.create",
|
|
102
|
+
handler: async ({ req }) => {
|
|
103
|
+
const body = (await req.json()) as {
|
|
104
|
+
provider_key?: string;
|
|
105
|
+
client_id?: string;
|
|
106
|
+
client_secret?: string;
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const { provider_key, client_id, client_secret } = body;
|
|
110
|
+
|
|
111
|
+
if (
|
|
112
|
+
!provider_key ||
|
|
113
|
+
typeof provider_key !== "string" ||
|
|
114
|
+
!client_id ||
|
|
115
|
+
typeof client_id !== "string" ||
|
|
116
|
+
!client_secret ||
|
|
117
|
+
typeof client_secret !== "string"
|
|
118
|
+
) {
|
|
119
|
+
return httpError(
|
|
120
|
+
"BAD_REQUEST",
|
|
121
|
+
"provider_key, client_id, and client_secret are required non-empty strings",
|
|
122
|
+
400,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const provider = getProvider(provider_key);
|
|
127
|
+
if (!provider) {
|
|
128
|
+
return httpError(
|
|
129
|
+
"NOT_FOUND",
|
|
130
|
+
`No OAuth provider registered for "${provider_key}"`,
|
|
131
|
+
404,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const app = await upsertApp(provider_key, client_id, {
|
|
136
|
+
clientSecretValue: client_secret,
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
return Response.json(
|
|
140
|
+
{
|
|
141
|
+
app: {
|
|
142
|
+
id: app.id,
|
|
143
|
+
provider_key: app.providerKey,
|
|
144
|
+
client_id: app.clientId,
|
|
145
|
+
created_at: app.createdAt,
|
|
146
|
+
updated_at: app.updatedAt,
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
{ status: 201 },
|
|
150
|
+
);
|
|
151
|
+
},
|
|
152
|
+
},
|
|
153
|
+
|
|
154
|
+
// DELETE /v1/oauth/apps/:id — Delete an OAuth app.
|
|
155
|
+
{
|
|
156
|
+
endpoint: "oauth/apps/:id",
|
|
157
|
+
method: "DELETE",
|
|
158
|
+
policyKey: "oauth/apps.delete",
|
|
159
|
+
handler: async ({ params }) => {
|
|
160
|
+
const app = getApp(params.id);
|
|
161
|
+
if (!app) {
|
|
162
|
+
return httpError("NOT_FOUND", `OAuth app not found: ${params.id}`, 404);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Disconnect all connections for this app first to clean up tokens.
|
|
166
|
+
const connections = listConnections(app.providerKey, app.clientId);
|
|
167
|
+
for (const conn of connections) {
|
|
168
|
+
await disconnectOAuthProvider(app.providerKey, app.clientId, conn.id);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
await deleteApp(params.id);
|
|
172
|
+
|
|
173
|
+
return Response.json({ ok: true });
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
// GET /v1/oauth/apps/:appId/connections — List connections for an app.
|
|
178
|
+
{
|
|
179
|
+
endpoint: "oauth/apps/:appId/connections",
|
|
180
|
+
method: "GET",
|
|
181
|
+
handler: ({ params }) => {
|
|
182
|
+
const app = getApp(params.appId);
|
|
183
|
+
if (!app) {
|
|
184
|
+
return httpError(
|
|
185
|
+
"NOT_FOUND",
|
|
186
|
+
`OAuth app not found: ${params.appId}`,
|
|
187
|
+
404,
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
const connections = listConnections(app.providerKey, app.clientId);
|
|
192
|
+
|
|
193
|
+
return Response.json({
|
|
194
|
+
connections: connections.map((row) => ({
|
|
195
|
+
id: row.id,
|
|
196
|
+
provider_key: row.providerKey,
|
|
197
|
+
account_info: row.accountInfo,
|
|
198
|
+
granted_scopes: parseGrantedScopes(row.grantedScopes),
|
|
199
|
+
status: row.status,
|
|
200
|
+
has_refresh_token: normalizeHasRefreshToken(row.hasRefreshToken),
|
|
201
|
+
expires_at: row.expiresAt,
|
|
202
|
+
created_at: row.createdAt,
|
|
203
|
+
updated_at: row.updatedAt,
|
|
204
|
+
})),
|
|
205
|
+
});
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
|
|
209
|
+
// DELETE /v1/oauth/connections/:id — Disconnect a single connection.
|
|
210
|
+
{
|
|
211
|
+
endpoint: "oauth/connections/:id",
|
|
212
|
+
method: "DELETE",
|
|
213
|
+
handler: async ({ params }) => {
|
|
214
|
+
const conn = getConnection(params.id);
|
|
215
|
+
if (!conn) {
|
|
216
|
+
return httpError(
|
|
217
|
+
"NOT_FOUND",
|
|
218
|
+
`OAuth connection not found: ${params.id}`,
|
|
219
|
+
404,
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
const result = await disconnectOAuthProvider(
|
|
224
|
+
conn.providerKey,
|
|
225
|
+
undefined,
|
|
226
|
+
conn.id,
|
|
227
|
+
);
|
|
228
|
+
if (result === "error") {
|
|
229
|
+
return httpError(
|
|
230
|
+
"INTERNAL_ERROR",
|
|
231
|
+
"Failed to clean up connection tokens. The connection was not removed.",
|
|
232
|
+
500,
|
|
233
|
+
);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return Response.json({ ok: true });
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
|
|
240
|
+
// POST /v1/oauth/apps/:appId/connect — Start OAuth connect flow.
|
|
241
|
+
{
|
|
242
|
+
endpoint: "oauth/apps/:appId/connect",
|
|
243
|
+
method: "POST",
|
|
244
|
+
handler: async ({ req, params }) => {
|
|
245
|
+
const app = getApp(params.appId);
|
|
246
|
+
if (!app) {
|
|
247
|
+
return httpError(
|
|
248
|
+
"NOT_FOUND",
|
|
249
|
+
`OAuth app not found: ${params.appId}`,
|
|
250
|
+
404,
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
let body: { scopes?: string[] } = {};
|
|
255
|
+
try {
|
|
256
|
+
const text = await req.text();
|
|
257
|
+
if (text) {
|
|
258
|
+
body = JSON.parse(text);
|
|
259
|
+
}
|
|
260
|
+
} catch {
|
|
261
|
+
// No body or invalid JSON — use defaults
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const clientSecret = await getAppClientSecret(app);
|
|
265
|
+
|
|
266
|
+
const result = await orchestrateOAuthConnect({
|
|
267
|
+
service: app.providerKey,
|
|
268
|
+
clientId: app.clientId,
|
|
269
|
+
clientSecret,
|
|
270
|
+
requestedScopes: body.scopes,
|
|
271
|
+
isInteractive: false,
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
if (result.success && result.deferred) {
|
|
275
|
+
return Response.json({
|
|
276
|
+
auth_url: result.authUrl,
|
|
277
|
+
state: result.state,
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (!result.success) {
|
|
282
|
+
return Response.json({ error: result.error }, { status: 500 });
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Interactive success (shouldn't happen with isInteractive: false,
|
|
286
|
+
// but handle gracefully)
|
|
287
|
+
return Response.json({ ok: true });
|
|
288
|
+
},
|
|
289
|
+
},
|
|
290
|
+
];
|
|
291
|
+
}
|
|
@@ -11,8 +11,11 @@ import {
|
|
|
11
11
|
} from "../../config/loader.js";
|
|
12
12
|
import type { CesClient } from "../../credential-execution/client.js";
|
|
13
13
|
import { setSentryOrganizationId } from "../../instrument.js";
|
|
14
|
+
import { clearEmbeddingBackendCache } from "../../memory/embedding-backend.js";
|
|
14
15
|
import { syncManualTokenConnection } from "../../oauth/manual-token-connection.js";
|
|
15
16
|
import { validateAnthropicApiKey } from "../../providers/anthropic/client.js";
|
|
17
|
+
import { validateGeminiApiKey } from "../../providers/gemini/client.js";
|
|
18
|
+
import { validateOpenAIApiKey } from "../../providers/openai/client.js";
|
|
16
19
|
import { initializeProviders } from "../../providers/registry.js";
|
|
17
20
|
import { credentialKey } from "../../security/credential-key.js";
|
|
18
21
|
import {
|
|
@@ -131,7 +134,7 @@ export async function handleAddSecret(
|
|
|
131
134
|
400,
|
|
132
135
|
);
|
|
133
136
|
}
|
|
134
|
-
// Validate
|
|
137
|
+
// Validate API keys before storing (Anthropic, OpenAI, Gemini)
|
|
135
138
|
if (name === "anthropic") {
|
|
136
139
|
const validation = await validateAnthropicApiKey(value);
|
|
137
140
|
if (!validation.valid) {
|
|
@@ -144,7 +147,32 @@ export async function handleAddSecret(
|
|
|
144
147
|
{ status: 422 },
|
|
145
148
|
);
|
|
146
149
|
}
|
|
150
|
+
} else if (name === "openai") {
|
|
151
|
+
const validation = await validateOpenAIApiKey(value);
|
|
152
|
+
if (!validation.valid) {
|
|
153
|
+
log.warn(
|
|
154
|
+
{ provider: name, reason: validation.reason },
|
|
155
|
+
"API key validation failed",
|
|
156
|
+
);
|
|
157
|
+
return Response.json(
|
|
158
|
+
{ success: false, error: validation.reason },
|
|
159
|
+
{ status: 422 },
|
|
160
|
+
);
|
|
161
|
+
}
|
|
162
|
+
} else if (name === "gemini") {
|
|
163
|
+
const validation = await validateGeminiApiKey(value);
|
|
164
|
+
if (!validation.valid) {
|
|
165
|
+
log.warn(
|
|
166
|
+
{ provider: name, reason: validation.reason },
|
|
167
|
+
"API key validation failed",
|
|
168
|
+
);
|
|
169
|
+
return Response.json(
|
|
170
|
+
{ success: false, error: validation.reason },
|
|
171
|
+
{ status: 422 },
|
|
172
|
+
);
|
|
173
|
+
}
|
|
147
174
|
}
|
|
175
|
+
// fireworks, openrouter, ollama — no validation (allow storage)
|
|
148
176
|
|
|
149
177
|
const stored = await setSecureKeyAsync(name, value);
|
|
150
178
|
if (!stored) {
|
|
@@ -319,6 +347,7 @@ export async function handleDeleteSecret(req: Request): Promise<Response> {
|
|
|
319
347
|
500,
|
|
320
348
|
);
|
|
321
349
|
}
|
|
350
|
+
clearEmbeddingBackendCache();
|
|
322
351
|
invalidateConfigCache();
|
|
323
352
|
await initializeProviders(getConfig());
|
|
324
353
|
log.info({ provider: name }, "API key deleted via HTTP");
|
|
@@ -644,6 +644,20 @@ export function settingsRouteDefinitions(): RouteDefinition[] {
|
|
|
644
644
|
},
|
|
645
645
|
|
|
646
646
|
// OAuth connect
|
|
647
|
+
{
|
|
648
|
+
endpoint: "oauth/start",
|
|
649
|
+
method: "POST",
|
|
650
|
+
policyKey: "oauth/start",
|
|
651
|
+
handler: async ({ req }) => {
|
|
652
|
+
const body = (await req.json()) as {
|
|
653
|
+
service?: string;
|
|
654
|
+
requestedScopes?: string[];
|
|
655
|
+
};
|
|
656
|
+
return handleOAuthConnectStart(body);
|
|
657
|
+
},
|
|
658
|
+
},
|
|
659
|
+
// Legacy alias for oauth/start (kept for backwards compatibility with
|
|
660
|
+
// older clients and platform proxy routes)
|
|
647
661
|
{
|
|
648
662
|
endpoint: "integrations/oauth/start",
|
|
649
663
|
method: "POST",
|