@vellumai/assistant 0.8.10 → 0.8.11-staging.1
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/bun.lock +62 -1
- package/docs/workspace-tools.md +196 -0
- package/examples/plugins/echo/README.md +3 -3
- package/knip.json +1 -0
- package/openapi.yaml +460 -128
- package/package.json +2 -1
- package/scripts/build-plugin-api.ts +299 -0
- package/src/__tests__/agent-loop-callsite-precedence.test.ts +7 -0
- package/src/__tests__/agent-loop-compaction-events.test.ts +197 -0
- package/src/__tests__/agent-loop-exit-reason.test.ts +93 -96
- package/src/__tests__/agent-loop-mutable-latest-user-message.test.ts +2 -0
- package/src/__tests__/agent-loop-output-hooks.test.ts +274 -1
- package/src/__tests__/agent-loop-override-profile.test.ts +3 -0
- package/src/__tests__/agent-loop-provider-error-recording.test.ts +4 -0
- package/src/__tests__/agent-loop-thinking.test.ts +4 -0
- package/src/__tests__/agent-loop.test.ts +578 -5
- package/src/__tests__/agent-wake-disk-pressure-callsite.test.ts +0 -1
- package/src/__tests__/approval-cascade.test.ts +1 -0
- package/src/__tests__/background-workers-disk-pressure.test.ts +0 -2
- package/src/__tests__/btw-routes.test.ts +0 -1
- package/src/__tests__/build-persisted-content.test.ts +75 -1
- package/src/__tests__/catalog-install-normalize.test.ts +141 -0
- package/src/__tests__/ces-startup-timeout.test.ts +60 -0
- package/src/__tests__/compaction-events.test.ts +1 -0
- package/src/__tests__/config-managed-gemini-defaults.test.ts +2 -46
- package/src/__tests__/context-overflow-reducer.test.ts +264 -124
- package/src/__tests__/context-window-manager-overflow-rung.test.ts +351 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop-inference-profile.test.ts +13 -5
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +284 -455
- package/src/__tests__/conversation-agent-loop.test.ts +131 -551
- package/src/__tests__/conversation-app-control-instantiation.test.ts +13 -0
- package/src/__tests__/conversation-confirmation-signals.test.ts +1 -0
- package/src/__tests__/conversation-fork-crud.test.ts +259 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -1
- package/src/__tests__/conversation-lifecycle.test.ts +257 -1
- package/src/__tests__/conversation-process-callsite.test.ts +1 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +38 -377
- package/src/__tests__/conversation-queue.test.ts +1 -39
- package/src/__tests__/conversation-runtime-assembly.test.ts +119 -8
- package/src/__tests__/conversation-skill-tools.test.ts +491 -5
- package/src/__tests__/conversation-slash-queue.test.ts +1 -1
- package/src/__tests__/conversation-slash-unknown.test.ts +1 -0
- package/src/__tests__/conversation-speed-override.test.ts +1 -0
- package/src/__tests__/conversation-store.test.ts +74 -0
- package/src/__tests__/conversation-surfaces-app-control.test.ts +4 -1
- package/src/__tests__/conversation-tool-setup-attribution.test.ts +323 -0
- package/src/__tests__/conversation-tool-setup-tools-disabled.test.ts +34 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +1 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +1 -1
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +1 -0
- package/src/__tests__/corrected-target.test.ts +93 -0
- package/src/__tests__/credential-execution-feature-gates.test.ts +3 -5
- package/src/__tests__/credential-execution-tools.test.ts +23 -11
- package/src/__tests__/credential-security-invariants.test.ts +6 -1
- package/src/__tests__/db-schedule-syntax-migration.test.ts +80 -0
- package/src/__tests__/device-id.test.ts +70 -1
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +6 -40
- package/src/__tests__/empty-response-hook.test.ts +242 -66
- package/src/__tests__/external-plugin-loader.test.ts +0 -31
- package/src/__tests__/get-skill-detail-audit.test.ts +43 -1
- package/src/__tests__/guardian-routing-invariants.test.ts +91 -0
- package/src/__tests__/history-repair-hook.test.ts +228 -3
- package/src/__tests__/host-app-control-proxy.test.ts +45 -0
- package/src/__tests__/host-browser-proxy.test.ts +254 -9
- package/src/__tests__/identity-routes.test.ts +1 -0
- package/src/__tests__/image-recovery-hook.test.ts +387 -0
- package/src/__tests__/injector-chain.test.ts +5 -4
- package/src/__tests__/injector-v3-suppression.test.ts +373 -47
- package/src/__tests__/intent-routing.test.ts +7 -0
- package/src/__tests__/memory-retrieval-hook.test.ts +117 -15
- package/src/__tests__/notification-decision-strategy.test.ts +3 -3
- package/src/__tests__/oauth-store.test.ts +0 -85
- package/src/__tests__/{context-overflow-policy.test.ts → overflow-policy.test.ts} +1 -1
- package/src/__tests__/parallel-tool.benchmark.test.ts +4 -0
- package/src/__tests__/persist-unsendable-image-downscale.test.ts +29 -9
- package/src/__tests__/persist-unsendable-image.test.ts +4 -4
- package/src/__tests__/persistence-secret-redaction.test.ts +78 -0
- package/src/__tests__/plugin-bootstrap.test.ts +82 -73
- package/src/__tests__/plugin-tool-contribution.test.ts +7 -4
- package/src/__tests__/plugin-types.test.ts +0 -8
- package/src/__tests__/provider-catalog-visibility.test.ts +1 -9
- package/src/__tests__/prune-old-conversations-job.test.ts +99 -0
- package/src/__tests__/registry.test.ts +240 -1
- package/src/__tests__/require-fresh-approval.test.ts +3 -0
- package/src/__tests__/schedule-routes.test.ts +116 -1
- package/src/__tests__/schedule-store.test.ts +28 -0
- package/src/__tests__/schedule-tools.test.ts +94 -1
- package/src/__tests__/server-history-render.test.ts +39 -0
- package/src/__tests__/skill-projection-feature-flag.test.ts +13 -0
- package/src/__tests__/skill-projection.benchmark.test.ts +25 -7
- package/src/__tests__/skills.test.ts +202 -0
- package/src/__tests__/slim-skill-category.test.ts +195 -0
- package/src/__tests__/strip-memory-injections.test.ts +33 -39
- package/src/__tests__/test-support/tool-invocation-seed.ts +79 -0
- package/src/__tests__/title-generate-hook.test.ts +9 -7
- package/src/__tests__/tool-audit-listener.test.ts +264 -1
- package/src/__tests__/tool-error-hook.test.ts +4 -3
- package/src/__tests__/tool-execution-pipeline.benchmark.test.ts +1 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +273 -0
- package/src/__tests__/tool-result-truncate-hook.test.ts +1 -0
- package/src/__tests__/tool-start-timestamp.test.ts +218 -0
- package/src/__tests__/tools-get-route.test.ts +202 -0
- package/src/__tests__/workspace-tool-loader.test.ts +319 -0
- package/src/__tests__/workspace-tools-watcher-flag.test.ts +70 -0
- package/src/agent/loop.ts +569 -319
- package/src/api/events/tool-result.ts +9 -0
- package/src/api/events/tool-use-start.ts +7 -0
- package/src/api/index.ts +10 -0
- package/src/api/responses/conversation-message.ts +135 -27
- package/src/api/responses/memory-v3-selection-log.ts +4 -4
- package/src/approvals/guardian-request-resolvers.ts +26 -0
- package/src/browser-session/backends/host-bridge.ts +29 -0
- package/src/browser-session/index.ts +1 -0
- package/src/browser-session/types.ts +5 -1
- package/src/cli/commands/__tests__/schedules.test.ts +62 -4
- package/src/cli/commands/__tests__/skills.test.ts +53 -0
- package/src/cli/commands/channel-verification-sessions.ts +6 -6
- package/src/cli/commands/inference-providers.ts +0 -8
- package/src/cli/commands/plugins.ts +2 -2
- package/src/cli/commands/schedules.ts +27 -4
- package/src/cli/commands/skills.ts +187 -146
- package/src/cli/commands/tools.ts +106 -0
- package/src/cli/lib/__tests__/install-from-github.test.ts +256 -328
- package/src/cli/lib/__tests__/plugin-catalog-cache.test.ts +6 -2
- package/src/cli/lib/__tests__/plugin-details.test.ts +10 -16
- package/src/cli/lib/__tests__/plugin-marketplace.test.ts +2 -2
- package/src/cli/lib/__tests__/search-plugins.test.ts +145 -240
- package/src/cli/lib/install-from-github.ts +187 -117
- package/src/cli/lib/plugin-catalog-cache.ts +9 -9
- package/src/cli/lib/plugin-details.ts +38 -68
- package/src/cli/lib/plugin-marketplace.ts +42 -14
- package/src/cli/lib/search-plugins.ts +29 -129
- package/src/cli/program.ts +2 -0
- package/src/config/bundled-skills/acp/SKILL.md +1 -0
- package/src/config/bundled-skills/app-builder/SKILL.md +1 -0
- package/src/config/bundled-skills/app-control/SKILL.md +1 -0
- package/src/config/bundled-skills/computer-use/SKILL.md +1 -0
- package/src/config/bundled-skills/contacts/SKILL.md +1 -0
- package/src/config/bundled-skills/document-editor/SKILL.md +1 -0
- package/src/config/bundled-skills/followups/SKILL.md +1 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +1 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +1 -0
- package/src/config/bundled-skills/messaging/SKILL.md +1 -0
- package/src/config/bundled-skills/phone-calls/SKILL.md +1 -0
- package/src/config/bundled-skills/playbooks/SKILL.md +1 -0
- package/src/config/bundled-skills/schedule/SKILL.md +1 -0
- package/src/config/bundled-skills/schedule/TOOLS.json +11 -3
- package/src/config/bundled-skills/sequences/SKILL.md +1 -0
- package/src/config/bundled-skills/settings/SKILL.md +1 -0
- package/src/config/bundled-skills/skill-management/SKILL.md +101 -1
- package/src/config/bundled-skills/subagent/SKILL.md +1 -0
- package/src/config/bundled-skills/transcribe/SKILL.md +1 -0
- package/src/config/env-registry.ts +23 -0
- package/src/config/feature-flag-registry.json +17 -81
- package/src/config/loader.ts +5 -22
- package/src/config/schema.ts +2 -0
- package/src/config/schemas/__tests__/compaction-logs.test.ts +56 -0
- package/src/config/schemas/__tests__/memory-v2.test.ts +0 -1
- package/src/config/schemas/__tests__/memory-v3.test.ts +61 -1
- package/src/config/schemas/compaction-logs.ts +79 -0
- package/src/config/schemas/memory-v2.ts +0 -8
- package/src/config/schemas/memory-v3.ts +104 -33
- package/src/config/seed-inference-profiles.ts +1 -1
- package/src/config/skills.ts +117 -47
- package/src/context/compactor.ts +11 -0
- package/src/context/strip-injections.ts +38 -4
- package/src/credential-execution/feature-gates.ts +0 -21
- package/src/credential-execution/startup-timeout.ts +32 -4
- package/src/daemon/__tests__/conversation-tool-setup-exclude.test.ts +18 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +140 -91
- package/src/daemon/conversation-agent-loop.ts +123 -658
- package/src/daemon/conversation-error.ts +6 -33
- package/src/daemon/conversation-lifecycle.ts +1 -1
- package/src/daemon/conversation-runtime-assembly.ts +183 -22
- package/src/daemon/conversation-skill-tools.ts +137 -8
- package/src/daemon/conversation-slash.ts +0 -14
- package/src/daemon/conversation-store.ts +2 -19
- package/src/daemon/conversation-tool-setup.ts +87 -1
- package/src/daemon/conversation.ts +119 -50
- package/src/daemon/external-plugins-bootstrap.ts +36 -95
- package/src/daemon/handlers/config-channels.ts +11 -2
- package/src/daemon/handlers/shared.ts +9 -1
- package/src/daemon/handlers/skills.ts +10 -4
- package/src/daemon/host-app-control-proxy.ts +72 -57
- package/src/daemon/host-browser-proxy.ts +117 -22
- package/src/daemon/lifecycle.ts +34 -5
- package/src/daemon/message-protocol.ts +0 -7
- package/src/daemon/message-types/schedules.ts +1 -0
- package/src/daemon/message-types/skills.ts +17 -0
- package/src/daemon/providers-setup.ts +3 -0
- package/src/daemon/server.ts +3 -3
- package/src/daemon/tool-setup-types.ts +9 -3
- package/src/daemon/trust-context.ts +23 -0
- package/src/daemon/workspace-tools-watcher.ts +324 -0
- package/src/events/tool-audit-listener.ts +78 -16
- package/src/events/tool-metrics-listener.ts +2 -5
- package/src/memory/__tests__/compaction-log-writer-clickhouse.test.ts +227 -0
- package/src/memory/__tests__/conversation-queries.test.ts +176 -0
- package/src/memory/__tests__/jobs-worker-v2-schedule.test.ts +20 -32
- package/src/memory/compaction-log-writer-clickhouse.ts +418 -0
- package/src/memory/conversation-crud.ts +134 -3
- package/src/memory/conversation-queries.ts +64 -7
- package/src/memory/db-init.ts +14 -0
- package/src/memory/embedding-backend.test.ts +130 -1
- package/src/memory/embedding-backend.ts +79 -106
- package/src/memory/embedding-gemini.ts +5 -0
- package/src/memory/graph/__tests__/conversation-graph-memory-v2-routing.test.ts +12 -0
- package/src/memory/graph/__tests__/handle-remember-v2.test.ts +19 -0
- package/src/memory/graph/conversation-graph-memory.ts +36 -25
- package/src/memory/graph/tool-handlers.ts +3 -0
- package/src/memory/job-handlers/cleanup.ts +3 -1
- package/src/memory/jobs-store.ts +0 -28
- package/src/memory/jobs-worker.ts +10 -22
- package/src/memory/memory-marker.ts +29 -0
- package/src/memory/memory-retrospective-startup-cleanup.ts +1 -1
- package/src/memory/migrations/268-add-memory-v3-selections.ts +6 -0
- package/src/memory/migrations/270-schedule-description.ts +36 -0
- package/src/memory/migrations/275-tool-invocations-add-skill-id.test.ts +81 -0
- package/src/memory/migrations/275-tool-invocations-add-skill-id.ts +20 -0
- package/src/memory/migrations/276-tool-invocations-created-at-id-index.test.ts +68 -0
- package/src/memory/migrations/276-tool-invocations-created-at-id-index.ts +20 -0
- package/src/memory/migrations/277-add-memory-v3-ever-injected.ts +29 -0
- package/src/memory/migrations/278-tool-invocations-telemetry-columns.test.ts +96 -0
- package/src/memory/migrations/278-tool-invocations-telemetry-columns.ts +39 -0
- package/src/memory/migrations/279-create-skill-loaded-events.test.ts +84 -0
- package/src/memory/migrations/279-create-skill-loaded-events.ts +26 -0
- package/src/memory/migrations/280-conversations-surfaced-at.test.ts +88 -0
- package/src/memory/migrations/280-conversations-surfaced-at.ts +24 -0
- package/src/memory/migrations/index.ts +10 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +16 -0
- package/src/memory/schema/infrastructure.ts +26 -0
- package/src/memory/skill-loaded-events-store.test.ts +160 -0
- package/src/memory/skill-loaded-events-store.ts +95 -0
- package/src/memory/tool-executed-events-store.test.ts +219 -0
- package/src/memory/tool-executed-events-store.ts +102 -0
- package/src/memory/tool-usage-store.ts +15 -3
- package/src/memory/v2/__tests__/consolidation-job.test.ts +117 -12
- package/src/memory/v2/__tests__/consolidation-prompt-flag-gating-guard.test.ts +189 -0
- package/src/memory/v2/__tests__/injected-block-slugs.test.ts +90 -0
- package/src/memory/v2/__tests__/page-store.test.ts +33 -0
- package/src/memory/v2/__tests__/prompts-consolidation.test.ts +88 -15
- package/src/memory/v2/activation-store.ts +50 -1
- package/src/memory/v2/consolidation-job.ts +113 -29
- package/src/memory/v2/injected-block-slugs.ts +79 -0
- package/src/memory/v2/injection.ts +6 -1
- package/src/memory/v2/prompts/consolidation.ts +414 -13
- package/src/memory/v2/router.ts +2 -28
- package/src/memory/v2/static-context.ts +1 -1
- package/src/memory/v2/types.ts +16 -0
- package/src/notifications/__tests__/copy-composer.test.ts +244 -0
- package/src/notifications/access-request-copy.ts +298 -0
- package/src/notifications/adapters/slack.ts +3 -3
- package/src/notifications/adapters/telegram.ts +2 -1
- package/src/notifications/copy-composer.ts +49 -267
- package/src/notifications/decision-engine.ts +16 -35
- package/src/notifications/home-feed-side-effect.ts +1 -6
- package/src/oauth/oauth-store.ts +0 -9
- package/src/permissions/checker.test.ts +83 -1
- package/src/permissions/checker.ts +25 -2
- package/src/permissions/gateway-threshold-reader.test.ts +182 -0
- package/src/permissions/gateway-threshold-reader.ts +80 -0
- package/src/platform/client.ts +1 -3
- package/src/platform/feature-gate.ts +3 -12
- package/src/plugin-api/constants.ts +4 -2
- package/src/plugin-api/index.ts +63 -11
- package/src/plugin-api/types.ts +236 -71
- package/src/plugins/defaults/compaction/compact.ts +66 -2
- package/src/plugins/defaults/compaction/context-overflow-reducer.ts +240 -32
- package/src/plugins/defaults/compaction/corrected-target.ts +53 -0
- package/src/{daemon/context-overflow-policy.ts → plugins/defaults/compaction/overflow-policy.ts} +1 -1
- package/src/plugins/defaults/compaction/window-manager.ts +303 -1
- package/src/plugins/defaults/empty-response/hooks/post-model-call.ts +173 -0
- package/src/plugins/defaults/empty-response/hooks/stop.ts +11 -115
- package/src/plugins/defaults/empty-response/nudge-state-store.ts +46 -0
- package/src/plugins/defaults/history-repair/hooks/post-model-call.ts +50 -0
- package/src/plugins/defaults/history-repair/hooks/stop.ts +22 -0
- package/src/plugins/defaults/history-repair/repair-state-store.ts +51 -0
- package/src/plugins/defaults/history-repair/terminal.ts +39 -2
- package/src/plugins/defaults/image-recovery/detect.ts +25 -0
- package/src/plugins/defaults/image-recovery/hooks/post-model-call.ts +73 -0
- package/src/plugins/defaults/image-recovery/hooks/stop.ts +22 -0
- package/src/plugins/defaults/image-recovery/image-recovery-state-store.ts +48 -0
- package/src/plugins/defaults/image-recovery/package.json +14 -0
- package/src/{daemon/persist-unsendable-image.ts → plugins/defaults/image-recovery/recover.ts} +67 -14
- package/src/plugins/defaults/index.ts +71 -5
- package/src/plugins/defaults/memory-retrieval/hooks/post-compact.ts +76 -112
- package/src/plugins/defaults/memory-retrieval/hooks/{user-prompt-submit-temp.ts → user-prompt-submit.ts} +83 -74
- package/src/plugins/defaults/memory-retrieval/injector-chain.ts +14 -8
- package/src/plugins/defaults/memory-retrieval/injectors.ts +2 -18
- package/src/plugins/defaults/memory-retrieval/package.json +14 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/carry-integration.test.ts +1157 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/injection.test.ts +683 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/live-integration.test.ts +161 -140
- package/src/plugins/defaults/memory-v3-shadow/__tests__/maintain-job.test.ts +160 -0
- package/src/plugins/defaults/memory-v3-shadow/__tests__/orchestrate.test.ts +335 -316
- package/src/plugins/defaults/memory-v3-shadow/__tests__/pool-select.test.ts +145 -53
- package/src/plugins/defaults/memory-v3-shadow/__tests__/render-injection.test.ts +38 -1
- package/src/plugins/defaults/memory-v3-shadow/__tests__/selection-log-store.test.ts +18 -8
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-integration.test.ts +112 -71
- package/src/plugins/defaults/memory-v3-shadow/__tests__/shadow-plugin.test.ts +192 -51
- package/src/plugins/defaults/memory-v3-shadow/__tests__/types.test.ts +4 -16
- package/src/plugins/defaults/memory-v3-shadow/card.test.ts +173 -0
- package/src/plugins/defaults/memory-v3-shadow/card.ts +116 -0
- package/src/plugins/defaults/memory-v3-shadow/core-set.test.ts +104 -0
- package/src/plugins/defaults/memory-v3-shadow/core-set.ts +59 -0
- package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.test.ts +305 -0
- package/src/plugins/defaults/memory-v3-shadow/ever-injected-store.ts +278 -0
- package/src/plugins/defaults/memory-v3-shadow/hot-set.test.ts +138 -0
- package/src/plugins/defaults/memory-v3-shadow/hot-set.ts +85 -0
- package/src/plugins/defaults/memory-v3-shadow/injector.ts +331 -24
- package/src/plugins/defaults/memory-v3-shadow/maintain-job.ts +119 -13
- package/src/plugins/defaults/memory-v3-shadow/orchestrate.ts +169 -114
- package/src/plugins/defaults/memory-v3-shadow/page-content.ts +47 -16
- package/src/plugins/defaults/memory-v3-shadow/pool-select.ts +144 -66
- package/src/plugins/defaults/memory-v3-shadow/prune.test.ts +758 -0
- package/src/plugins/defaults/memory-v3-shadow/prune.ts +471 -0
- package/src/plugins/defaults/memory-v3-shadow/render-injection.ts +68 -16
- package/src/plugins/defaults/memory-v3-shadow/selection-log-store.ts +19 -12
- package/src/plugins/defaults/memory-v3-shadow/shadow-plugin.ts +96 -43
- package/src/plugins/defaults/memory-v3-shadow/types.ts +34 -17
- package/src/plugins/defaults/title-generate/hooks/stop.ts +9 -11
- package/src/plugins/pipeline.ts +8 -5
- package/src/plugins/types.ts +5 -51
- package/src/providers/cache-control.ts +26 -0
- package/src/providers/inference/__tests__/base-url-route-validation.test.ts +1 -2
- package/src/providers/model-catalog.ts +13 -1
- package/src/providers/openai/__tests__/tool-choice-mapping.test.ts +147 -0
- package/src/providers/openai/chat-completions-provider.ts +46 -0
- package/src/providers/openai/responses-provider.ts +45 -0
- package/src/providers/registry.ts +0 -8
- package/src/runtime/__tests__/agent-wake.test.ts +0 -1
- package/src/runtime/agent-wake.ts +13 -0
- package/src/runtime/routes/__tests__/acp-routes.test.ts +151 -0
- package/src/runtime/routes/__tests__/consolidation-routes.test.ts +12 -50
- package/src/runtime/routes/__tests__/conversation-surface-routes.test.ts +322 -0
- package/src/runtime/routes/__tests__/inference-provider-connection-routes.test.ts +0 -62
- package/src/runtime/routes/__tests__/plugins-routes.test.ts +31 -11
- package/src/runtime/routes/acp-routes.test.ts +106 -0
- package/src/runtime/routes/acp-routes.ts +248 -2
- package/src/runtime/routes/browser-tabs-routes.ts +1 -1
- package/src/runtime/routes/channel-verification-routes.ts +14 -5
- package/src/runtime/routes/chatgpt-subscription-auth-routes.ts +0 -14
- package/src/runtime/routes/consolidation-routes.ts +6 -82
- package/src/runtime/routes/conversation-list-routes.ts +6 -0
- package/src/runtime/routes/conversation-management-routes.ts +71 -0
- package/src/runtime/routes/identity-routes.ts +8 -0
- package/src/runtime/routes/inbound-stages/acl-enforcement.ts +33 -34
- package/src/runtime/routes/inference-provider-connection-routes.ts +0 -45
- package/src/runtime/routes/plugins-routes.ts +34 -45
- package/src/runtime/routes/schedule-routes.ts +43 -5
- package/src/runtime/routes/settings-routes.ts +140 -15
- package/src/runtime/routes/skills-routes.ts +18 -6
- package/src/runtime/services/__tests__/conversation-serializer.test.ts +140 -0
- package/src/runtime/services/conversation-serializer.ts +38 -1
- package/src/runtime/verification-outbound-actions.ts +147 -2
- package/src/runtime/verification-templates.ts +29 -3
- package/src/schedule/schedule-store.ts +19 -0
- package/src/skills/catalog-install.ts +77 -13
- package/src/tasks/task-scheduler.ts +1 -0
- package/src/telemetry/types.ts +66 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +542 -13
- package/src/telemetry/usage-telemetry-reporter.ts +213 -20
- package/src/tools/browser/__tests__/browser-execution-acquire.test.ts +49 -2
- package/src/tools/browser/__tests__/browser-status.test.ts +29 -5
- package/src/tools/browser/browser-execution.ts +27 -9
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +380 -4
- package/src/tools/browser/cdp-client/__tests__/host-bridge-cdp-client.test.ts +107 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +6 -1
- package/src/tools/browser/cdp-client/factory.ts +217 -17
- package/src/tools/browser/cdp-client/host-bridge-cdp-client.ts +67 -0
- package/src/tools/browser/cdp-client/types.ts +22 -2
- package/src/tools/credential-execution/make-authenticated-request.ts +2 -1
- package/src/tools/credential-execution/manage-secure-command-tool.ts +169 -164
- package/src/tools/credential-execution/run-authenticated-command.ts +2 -1
- package/src/tools/executor.ts +39 -7
- package/src/tools/registry.ts +387 -5
- package/src/tools/schedule/create.ts +16 -0
- package/src/tools/schedule/list.ts +12 -4
- package/src/tools/schedule/update.ts +12 -0
- package/src/tools/skills/load.ts +11 -6
- package/src/tools/terminal/safe-env.ts +2 -0
- package/src/tools/types.ts +65 -9
- package/src/tools/workspace-tools/loader.ts +673 -0
- package/src/usage/attribution.ts +28 -0
- package/src/util/device-id.ts +17 -3
- package/src/util/platform.ts +16 -0
- package/tsconfig.plugin-api.json +13 -0
- package/src/__tests__/plugin-external-api.test.ts +0 -68
- package/src/__tests__/plugin-skill-contribution.test.ts +0 -355
- package/src/daemon/message-types/browser.ts +0 -10
- package/src/notifications/__tests__/emit-signal-home-feed.test.ts +0 -187
- package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/eval-turns.json +0 -36
- package/src/plugins/defaults/memory-v3-shadow/__tests__/fixtures/live-turns.json +0 -37
- package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-eviction.test.ts +0 -106
- package/src/plugins/defaults/memory-v3-shadow/__tests__/working-set-skeleton.test.ts +0 -44
- package/src/plugins/defaults/memory-v3-shadow/working-set.ts +0 -91
- package/src/plugins/external-api.ts +0 -114
- package/src/plugins/plugin-skill-contributions.ts +0 -292
|
@@ -73,6 +73,52 @@ function archiveStatusClause(status: ArchiveStatusFilter) {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Raw SQL predicate for "visible in the standard (Recents) listing".
|
|
78
|
+
*
|
|
79
|
+
* Shared by the `"standard"` bucket of {@link conversationTypeClause} (list +
|
|
80
|
+
* count) and by every match path in {@link searchConversations} (FTS content,
|
|
81
|
+
* LIKE content fallback, and title LIKE) so the listing and search can never
|
|
82
|
+
* drift: anything the sidebar shows in Recents is also findable by search,
|
|
83
|
+
* and vice versa.
|
|
84
|
+
*
|
|
85
|
+
* Two arms:
|
|
86
|
+
* - Foreground rows: not background/scheduled/private by type, and not routed
|
|
87
|
+
* to the `system:background` / `system:scheduled` groups.
|
|
88
|
+
* - Surfaced rows (`surfaced_at IS NOT NULL`): background/scheduled rows
|
|
89
|
+
* explicitly promoted via the surface API. Private rows stay excluded
|
|
90
|
+
* unconditionally, and subagent runs are excluded from the surfaced arm so
|
|
91
|
+
* they can never reach the sidebar.
|
|
92
|
+
*
|
|
93
|
+
* @param alias Table name or alias qualifying the column references
|
|
94
|
+
* (e.g. `"c"` in the search joins).
|
|
95
|
+
*/
|
|
96
|
+
function standardListingVisibilitySql(alias = "conversations"): string {
|
|
97
|
+
return (
|
|
98
|
+
`((${alias}.conversation_type NOT IN ('background', 'scheduled', 'private')` +
|
|
99
|
+
` AND COALESCE(${alias}.group_id, 'system:all') NOT IN ('system:background', 'system:scheduled'))` +
|
|
100
|
+
` OR ${surfacedVisibilitySql(alias)})`
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Raw SQL predicate for the surfaced arm of standard-listing visibility:
|
|
106
|
+
* background/scheduled rows explicitly promoted via the surface API
|
|
107
|
+
* (`surfaced_at IS NOT NULL`), with private rows excluded unconditionally and
|
|
108
|
+
* subagent runs excluded so they can never reach the sidebar.
|
|
109
|
+
*
|
|
110
|
+
* Shared by {@link standardListingVisibilitySql} and
|
|
111
|
+
* {@link listPinnedConversations} so pinned surfaced rows stay visible
|
|
112
|
+
* everywhere the standard listing would show them.
|
|
113
|
+
*/
|
|
114
|
+
function surfacedVisibilitySql(alias = "conversations"): string {
|
|
115
|
+
return (
|
|
116
|
+
`(${alias}.surfaced_at IS NOT NULL` +
|
|
117
|
+
` AND ${alias}.conversation_type != 'private'` +
|
|
118
|
+
` AND (${alias}.source IS NULL OR ${alias}.source != 'subagent'))`
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
|
|
76
122
|
/**
|
|
77
123
|
* SQL predicate selecting which bucket {@link listConversations} and
|
|
78
124
|
* {@link countConversations} return, keyed by the canonical
|
|
@@ -82,7 +128,10 @@ function archiveStatusClause(status: ArchiveStatusFilter) {
|
|
|
82
128
|
* excluding background, scheduled, and private rows. `"private"` is excluded
|
|
83
129
|
* defensively because in-place snapshot restore swaps the SQLite file without
|
|
84
130
|
* running migrations in-process, so legacy private rows can briefly exist
|
|
85
|
-
* before migration cleanup deletes them.
|
|
131
|
+
* before migration cleanup deletes them. Background/scheduled rows with a
|
|
132
|
+
* non-null `surfaced_at` (explicitly promoted via the surface API) are
|
|
133
|
+
* included so clients can render them in the Recents grouping without a
|
|
134
|
+
* separate fetch.
|
|
86
135
|
* - `"background"` — the background **umbrella**: background *and* scheduled
|
|
87
136
|
* rows together. The back-compat bucket for the single
|
|
88
137
|
* `conversationType=background` fetch that older clients (e.g. the macOS app,
|
|
@@ -101,7 +150,10 @@ function conversationTypeClause(type: ConversationType) {
|
|
|
101
150
|
const notSubagent = sql`(${conversations.source} IS NULL OR ${conversations.source} != 'subagent')`;
|
|
102
151
|
switch (type) {
|
|
103
152
|
case "standard":
|
|
104
|
-
|
|
153
|
+
// Surfaced rows (`surfaced_at IS NOT NULL`) are promoted into the
|
|
154
|
+
// standard listing even when they're background/scheduled — see
|
|
155
|
+
// standardListingVisibilitySql for the full predicate semantics.
|
|
156
|
+
return sql.raw(standardListingVisibilitySql());
|
|
105
157
|
case "background":
|
|
106
158
|
return sql`(${conversations.conversationType} IN ('background', 'scheduled') OR group_id IN ('system:background', 'system:scheduled')) AND ${notSubagent}`;
|
|
107
159
|
case "scheduled":
|
|
@@ -233,7 +285,13 @@ export function listPinnedConversations(
|
|
|
233
285
|
.from(conversations)
|
|
234
286
|
.where(
|
|
235
287
|
and(
|
|
236
|
-
|
|
288
|
+
// Mirror the standard listing: plain foreground rows by type, plus
|
|
289
|
+
// surfaced background/scheduled rows — a pinned surfaced conversation
|
|
290
|
+
// must stay injectable into page 1 (see surfacedVisibilitySql).
|
|
291
|
+
sql.raw(
|
|
292
|
+
`(conversations.conversation_type NOT IN ('background', 'scheduled', 'private')` +
|
|
293
|
+
` OR ${surfacedVisibilitySql()})`,
|
|
294
|
+
),
|
|
237
295
|
sql`is_pinned = 1`,
|
|
238
296
|
...(archiveCond ? [archiveCond] : []),
|
|
239
297
|
),
|
|
@@ -417,7 +475,7 @@ export function searchConversations(
|
|
|
417
475
|
FROM messages_fts f
|
|
418
476
|
JOIN messages m ON m.id = f.message_id
|
|
419
477
|
JOIN conversations c ON c.id = m.conversation_id
|
|
420
|
-
WHERE messages_fts MATCH ? AND
|
|
478
|
+
WHERE messages_fts MATCH ? AND ${standardListingVisibilitySql("c")} AND c.archived_at IS NULL
|
|
421
479
|
LIMIT 1000
|
|
422
480
|
`,
|
|
423
481
|
ftsMatch,
|
|
@@ -442,7 +500,7 @@ export function searchConversations(
|
|
|
442
500
|
SELECT DISTINCT m.conversation_id
|
|
443
501
|
FROM messages m
|
|
444
502
|
JOIN conversations c ON c.id = m.conversation_id
|
|
445
|
-
WHERE m.content LIKE ? ESCAPE '\\' AND
|
|
503
|
+
WHERE m.content LIKE ? ESCAPE '\\' AND ${standardListingVisibilitySql("c")} AND c.archived_at IS NULL
|
|
446
504
|
LIMIT 1000
|
|
447
505
|
`,
|
|
448
506
|
likePattern,
|
|
@@ -456,8 +514,7 @@ export function searchConversations(
|
|
|
456
514
|
.from(conversations)
|
|
457
515
|
.where(
|
|
458
516
|
and(
|
|
459
|
-
sql
|
|
460
|
-
sql`COALESCE(group_id, 'system:all') NOT IN ('system:background', 'system:scheduled')`,
|
|
517
|
+
sql.raw(standardListingVisibilitySql()),
|
|
461
518
|
sql`${conversations.title} LIKE ${titlePattern} ESCAPE '\\'`,
|
|
462
519
|
sql`${conversations.archivedAt} IS NULL`,
|
|
463
520
|
),
|
package/src/memory/db-init.ts
CHANGED
|
@@ -37,6 +37,7 @@ import {
|
|
|
37
37
|
createOnboardingEventsTable,
|
|
38
38
|
createScopedApprovalGrantsTable,
|
|
39
39
|
createSequenceTables,
|
|
40
|
+
createSkillLoadedEventsTable,
|
|
40
41
|
createTasksAndWorkItemsTables,
|
|
41
42
|
createWatchersAndLogsTables,
|
|
42
43
|
migrate230AcpSessionHistory,
|
|
@@ -46,6 +47,7 @@ import {
|
|
|
46
47
|
migrateActivationState,
|
|
47
48
|
migrateActivationStateFkCascade,
|
|
48
49
|
migrateAddConversationInferenceProfile,
|
|
50
|
+
migrateAddMemoryV3EverInjected,
|
|
49
51
|
migrateAddMemoryV3Selections,
|
|
50
52
|
migrateAddSourceTypeColumns,
|
|
51
53
|
migrateAssistantContactMetadata,
|
|
@@ -77,6 +79,7 @@ import {
|
|
|
77
79
|
migrateConversationLastNotifiedProfile,
|
|
78
80
|
migrateConversationsArchivedAt,
|
|
79
81
|
migrateConversationsLastMessageAt,
|
|
82
|
+
migrateConversationsSurfacedAt,
|
|
80
83
|
migrateConversationsThreadTypeIndex,
|
|
81
84
|
migrateCreateConversationGraphMemoryState,
|
|
82
85
|
migrateCreateDocumentComments,
|
|
@@ -186,6 +189,7 @@ import {
|
|
|
186
189
|
migrateRenameVerificationSessionIdColumn,
|
|
187
190
|
migrateRenameVerificationTable,
|
|
188
191
|
migrateRenameVoiceToPhone,
|
|
192
|
+
migrateScheduleDescription,
|
|
189
193
|
migrateScheduleOneShotRouting,
|
|
190
194
|
migrateScheduleQuietFlag,
|
|
191
195
|
migrateScheduleRetryPolicy,
|
|
@@ -201,7 +205,10 @@ import {
|
|
|
201
205
|
migrateStripIntegrationPrefixFromProviderKeys,
|
|
202
206
|
migrateStripPlaceholderSentinelsFromMessages,
|
|
203
207
|
migrateStripThinkingFromConsolidated,
|
|
208
|
+
migrateToolInvocationsCreatedAtIdIndex,
|
|
204
209
|
migrateToolInvocationsMatchedRuleId,
|
|
210
|
+
migrateToolInvocationsSkillId,
|
|
211
|
+
migrateToolInvocationsTelemetryColumns,
|
|
205
212
|
migrateTraceEventsCreatedAtIndex,
|
|
206
213
|
migrateUsageDashboardIndexes,
|
|
207
214
|
migrateUsageLlmCallCount,
|
|
@@ -478,12 +485,19 @@ export function initializeDb(): void {
|
|
|
478
485
|
migrateLlmUsageEventsAddAssistantVersion,
|
|
479
486
|
migrateAddMemoryV3Selections,
|
|
480
487
|
migrateScheduleScriptTimeout,
|
|
488
|
+
migrateScheduleDescription,
|
|
481
489
|
migrateScheduleSourceConversation,
|
|
482
490
|
migrateMessagesRoleCreatedAtIndex,
|
|
483
491
|
createAuthFallbackEventsTable,
|
|
484
492
|
migrateAcpSessionHistoryCwd,
|
|
485
493
|
migrateOnboardingEventsFunnelColumns,
|
|
486
494
|
createActivationSessionsTable,
|
|
495
|
+
migrateToolInvocationsSkillId,
|
|
496
|
+
migrateToolInvocationsCreatedAtIdIndex,
|
|
497
|
+
migrateAddMemoryV3EverInjected,
|
|
498
|
+
migrateToolInvocationsTelemetryColumns,
|
|
499
|
+
createSkillLoadedEventsTable,
|
|
500
|
+
migrateConversationsSurfacedAt,
|
|
487
501
|
];
|
|
488
502
|
|
|
489
503
|
// Run each migration step, catching and logging individual failures so one
|
|
@@ -1,12 +1,33 @@
|
|
|
1
|
-
import { afterEach, describe, expect, mock, test } from "bun:test";
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, test } from "bun:test";
|
|
2
2
|
|
|
3
3
|
import type { AssistantConfig } from "../config/types.js";
|
|
4
4
|
import {
|
|
5
5
|
clearEmbeddingBackendCache,
|
|
6
|
+
embedWithBackend,
|
|
6
7
|
resetLocalEmbeddingFailureState,
|
|
7
8
|
selectEmbeddingBackend,
|
|
8
9
|
} from "./embedding-backend.js";
|
|
9
10
|
|
|
11
|
+
const getProviderKeyAsyncMock = mock(
|
|
12
|
+
async (_provider: string): Promise<string | undefined> => undefined,
|
|
13
|
+
);
|
|
14
|
+
mock.module("../security/secure-keys.js", () => ({
|
|
15
|
+
getProviderKeyAsync: getProviderKeyAsyncMock,
|
|
16
|
+
}));
|
|
17
|
+
|
|
18
|
+
const DISABLED_PROXY_CONTEXT = {
|
|
19
|
+
enabled: false,
|
|
20
|
+
platformBaseUrl: "",
|
|
21
|
+
assistantApiKey: "",
|
|
22
|
+
};
|
|
23
|
+
const resolveManagedProxyContextMock = mock(async () => DISABLED_PROXY_CONTEXT);
|
|
24
|
+
mock.module("../providers/platform-proxy/context.js", () => ({
|
|
25
|
+
resolveManagedProxyContext: resolveManagedProxyContextMock,
|
|
26
|
+
hasManagedProxyPrereqs: mock(async () => false),
|
|
27
|
+
buildManagedBaseUrl: mock(async () => undefined),
|
|
28
|
+
managedFallbackEnabledFor: mock(async () => false),
|
|
29
|
+
}));
|
|
30
|
+
|
|
10
31
|
const LOCAL_CONFIG = {
|
|
11
32
|
memory: {
|
|
12
33
|
embeddings: {
|
|
@@ -73,3 +94,111 @@ describe("embedding backend cache invalidation", () => {
|
|
|
73
94
|
expect(secondSelection.backend).toBe(firstSelection.backend);
|
|
74
95
|
});
|
|
75
96
|
});
|
|
97
|
+
|
|
98
|
+
const GEMINI_CONFIG = {
|
|
99
|
+
memory: {
|
|
100
|
+
embeddings: {
|
|
101
|
+
provider: "gemini",
|
|
102
|
+
geminiModel: "test-model",
|
|
103
|
+
},
|
|
104
|
+
qdrant: {
|
|
105
|
+
vectorSize: 3,
|
|
106
|
+
},
|
|
107
|
+
},
|
|
108
|
+
} as unknown as AssistantConfig;
|
|
109
|
+
|
|
110
|
+
const ENABLED_PROXY_CONTEXT = {
|
|
111
|
+
enabled: true,
|
|
112
|
+
platformBaseUrl: "https://proxy.example.com",
|
|
113
|
+
assistantApiKey: "stale-platform-key",
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
function jsonResponse(body: unknown, status: number): Response {
|
|
117
|
+
return new Response(JSON.stringify(body), {
|
|
118
|
+
status,
|
|
119
|
+
headers: { "Content-Type": "application/json" },
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
describe("managed-proxy Gemini fallback to direct key", () => {
|
|
124
|
+
const originalFetch = globalThis.fetch;
|
|
125
|
+
let fetchMock: ReturnType<typeof mock>;
|
|
126
|
+
|
|
127
|
+
beforeEach(() => {
|
|
128
|
+
clearEmbeddingBackendCache();
|
|
129
|
+
// Managed-proxy requests (proxy.example.com) are rejected like a platform
|
|
130
|
+
// proxy with a bad credential; direct Google API requests succeed.
|
|
131
|
+
fetchMock = mock(async (url: string | URL) => {
|
|
132
|
+
if (String(url).startsWith("https://proxy.example.com/")) {
|
|
133
|
+
return jsonResponse({ detail: "Invalid or revoked API key." }, 403);
|
|
134
|
+
}
|
|
135
|
+
return jsonResponse({ embedding: { values: [0.1, 0.2, 0.3] } }, 200);
|
|
136
|
+
});
|
|
137
|
+
globalThis.fetch = fetchMock as unknown as typeof fetch;
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
afterEach(() => {
|
|
141
|
+
globalThis.fetch = originalFetch;
|
|
142
|
+
clearEmbeddingBackendCache();
|
|
143
|
+
getProviderKeyAsyncMock.mockReset();
|
|
144
|
+
getProviderKeyAsyncMock.mockImplementation(async () => undefined);
|
|
145
|
+
resolveManagedProxyContextMock.mockReset();
|
|
146
|
+
resolveManagedProxyContextMock.mockImplementation(
|
|
147
|
+
async () => DISABLED_PROXY_CONTEXT,
|
|
148
|
+
);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test("falls back to the direct key when the managed-proxy primary fails", async () => {
|
|
152
|
+
resolveManagedProxyContextMock.mockImplementation(
|
|
153
|
+
async () => ENABLED_PROXY_CONTEXT,
|
|
154
|
+
);
|
|
155
|
+
getProviderKeyAsyncMock.mockImplementation(async (provider) =>
|
|
156
|
+
provider === "gemini" ? "direct-key" : undefined,
|
|
157
|
+
);
|
|
158
|
+
|
|
159
|
+
const result = await embedWithBackend(GEMINI_CONFIG, ["hello world"]);
|
|
160
|
+
|
|
161
|
+
expect(result.provider).toBe("gemini");
|
|
162
|
+
expect(result.vectors).toEqual([[0.1, 0.2, 0.3]]);
|
|
163
|
+
|
|
164
|
+
expect(fetchMock).toHaveBeenCalledTimes(2);
|
|
165
|
+
const [managedUrl, managedInit] = fetchMock.mock.calls[0] as [
|
|
166
|
+
string,
|
|
167
|
+
RequestInit,
|
|
168
|
+
];
|
|
169
|
+
expect(managedUrl).toBe(
|
|
170
|
+
"https://proxy.example.com/v1/runtime-proxy/gemini/v1beta/models/test-model:embedContent",
|
|
171
|
+
);
|
|
172
|
+
expect(
|
|
173
|
+
(managedInit.headers as Record<string, string>)["Authorization"],
|
|
174
|
+
).toBe("Bearer stale-platform-key");
|
|
175
|
+
const [directUrl] = fetchMock.mock.calls[1] as [string, RequestInit];
|
|
176
|
+
expect(directUrl).toContain("generativelanguage.googleapis.com");
|
|
177
|
+
expect(directUrl).toContain("key=direct-key");
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
test("surfaces the managed-proxy error when no direct key exists", async () => {
|
|
181
|
+
resolveManagedProxyContextMock.mockImplementation(
|
|
182
|
+
async () => ENABLED_PROXY_CONTEXT,
|
|
183
|
+
);
|
|
184
|
+
|
|
185
|
+
await expect(
|
|
186
|
+
embedWithBackend(GEMINI_CONFIG, ["hello world"]),
|
|
187
|
+
).rejects.toThrow(/403/);
|
|
188
|
+
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test("uses the direct key as primary when the managed proxy is disabled", async () => {
|
|
192
|
+
getProviderKeyAsyncMock.mockImplementation(async (provider) =>
|
|
193
|
+
provider === "gemini" ? "direct-key" : undefined,
|
|
194
|
+
);
|
|
195
|
+
|
|
196
|
+
const result = await embedWithBackend(GEMINI_CONFIG, ["another input"]);
|
|
197
|
+
|
|
198
|
+
expect(result.vectors).toEqual([[0.1, 0.2, 0.3]]);
|
|
199
|
+
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
200
|
+
const [directUrl] = fetchMock.mock.calls[0] as [string, RequestInit];
|
|
201
|
+
expect(directUrl).toContain("generativelanguage.googleapis.com");
|
|
202
|
+
expect(directUrl).toContain("key=direct-key");
|
|
203
|
+
});
|
|
204
|
+
});
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
|
|
3
|
-
import { isAssistantFeatureFlagEnabled } from "../config/assistant-feature-flags.js";
|
|
4
3
|
import { getOllamaBaseUrlEnv } from "../config/env.js";
|
|
5
4
|
import { resolveCallSiteConfig } from "../config/llm-resolver.js";
|
|
6
5
|
import type { AssistantConfig } from "../config/types.js";
|
|
@@ -267,6 +266,56 @@ function geminiCacheExtras(config: AssistantConfig): string[] {
|
|
|
267
266
|
return extras;
|
|
268
267
|
}
|
|
269
268
|
|
|
269
|
+
/** Build (or reuse) the direct-API Gemini backend for the given key. */
|
|
270
|
+
function getDirectGeminiBackend(
|
|
271
|
+
config: AssistantConfig,
|
|
272
|
+
geminiKey: string,
|
|
273
|
+
): EmbeddingBackend {
|
|
274
|
+
return getCachedOrCreate(
|
|
275
|
+
"gemini",
|
|
276
|
+
config.memory.embeddings.geminiModel,
|
|
277
|
+
() =>
|
|
278
|
+
new GeminiEmbeddingBackend(
|
|
279
|
+
geminiKey,
|
|
280
|
+
config.memory.embeddings.geminiModel,
|
|
281
|
+
{
|
|
282
|
+
taskType: config.memory.embeddings.geminiTaskType,
|
|
283
|
+
dimensions: config.memory.embeddings.geminiDimensions,
|
|
284
|
+
},
|
|
285
|
+
),
|
|
286
|
+
geminiCacheExtras(config),
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Build (or reuse) the managed-proxy Gemini backend, or return undefined
|
|
292
|
+
* when the managed proxy prerequisites (platform URL + assistant API key)
|
|
293
|
+
* are not satisfied.
|
|
294
|
+
*/
|
|
295
|
+
async function tryGetManagedGeminiBackend(
|
|
296
|
+
config: AssistantConfig,
|
|
297
|
+
): Promise<EmbeddingBackend | undefined> {
|
|
298
|
+
const proxyCtx = await resolveManagedProxyContext();
|
|
299
|
+
const meta = PLATFORM_PROVIDER_META["gemini"];
|
|
300
|
+
if (!proxyCtx.enabled || !meta?.managed || !meta.proxyPath) return undefined;
|
|
301
|
+
const managedBaseUrl = `${proxyCtx.platformBaseUrl}${meta.proxyPath}`;
|
|
302
|
+
return getCachedOrCreate(
|
|
303
|
+
"gemini",
|
|
304
|
+
config.memory.embeddings.geminiModel,
|
|
305
|
+
() =>
|
|
306
|
+
new GeminiEmbeddingBackend(
|
|
307
|
+
proxyCtx.assistantApiKey,
|
|
308
|
+
config.memory.embeddings.geminiModel,
|
|
309
|
+
{
|
|
310
|
+
taskType: config.memory.embeddings.geminiTaskType,
|
|
311
|
+
dimensions: config.memory.embeddings.geminiDimensions ?? 3072,
|
|
312
|
+
managedBaseUrl,
|
|
313
|
+
},
|
|
314
|
+
),
|
|
315
|
+
[...geminiCacheExtras(config), "managed"],
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
|
|
270
319
|
export interface EmbeddingBackendSelection {
|
|
271
320
|
backend: EmbeddingBackend | null;
|
|
272
321
|
reason: string | null;
|
|
@@ -302,41 +351,13 @@ export async function selectEmbeddingBackend(
|
|
|
302
351
|
};
|
|
303
352
|
}
|
|
304
353
|
|
|
305
|
-
// When
|
|
306
|
-
//
|
|
307
|
-
//
|
|
308
|
-
if (
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
const proxyCtx = await resolveManagedProxyContext();
|
|
313
|
-
if (proxyCtx.enabled) {
|
|
314
|
-
const meta = PLATFORM_PROVIDER_META["gemini"];
|
|
315
|
-
if (meta?.managed && meta.proxyPath) {
|
|
316
|
-
const managedBaseUrl = `${proxyCtx.platformBaseUrl}${meta.proxyPath}`;
|
|
317
|
-
const managedModel = config.memory.embeddings.geminiModel;
|
|
318
|
-
const managedDimensions =
|
|
319
|
-
config.memory.embeddings.geminiDimensions ?? 3072;
|
|
320
|
-
const extras = geminiCacheExtras(config);
|
|
321
|
-
return {
|
|
322
|
-
backend: getCachedOrCreate(
|
|
323
|
-
"gemini",
|
|
324
|
-
managedModel,
|
|
325
|
-
() =>
|
|
326
|
-
new GeminiEmbeddingBackend(
|
|
327
|
-
proxyCtx.assistantApiKey,
|
|
328
|
-
managedModel,
|
|
329
|
-
{
|
|
330
|
-
taskType: config.memory.embeddings.geminiTaskType,
|
|
331
|
-
dimensions: managedDimensions,
|
|
332
|
-
managedBaseUrl,
|
|
333
|
-
},
|
|
334
|
-
),
|
|
335
|
-
[...extras, "managed"],
|
|
336
|
-
),
|
|
337
|
-
reason: null,
|
|
338
|
-
};
|
|
339
|
-
}
|
|
354
|
+
// When managed proxy prerequisites are satisfied, insert managed-proxy Gemini
|
|
355
|
+
// at the front of the auto chain so platform assistants use Vellum-managed
|
|
356
|
+
// Gemini embeddings.
|
|
357
|
+
if (requested === "auto" || requested === "gemini") {
|
|
358
|
+
const managed = await tryGetManagedGeminiBackend(config);
|
|
359
|
+
if (managed) {
|
|
360
|
+
return { backend: managed, reason: null };
|
|
340
361
|
}
|
|
341
362
|
}
|
|
342
363
|
|
|
@@ -405,20 +426,7 @@ export async function selectEmbeddingBackend(
|
|
|
405
426
|
continue;
|
|
406
427
|
}
|
|
407
428
|
return {
|
|
408
|
-
backend:
|
|
409
|
-
"gemini",
|
|
410
|
-
config.memory.embeddings.geminiModel,
|
|
411
|
-
() =>
|
|
412
|
-
new GeminiEmbeddingBackend(
|
|
413
|
-
geminiKey,
|
|
414
|
-
config.memory.embeddings.geminiModel,
|
|
415
|
-
{
|
|
416
|
-
taskType: config.memory.embeddings.geminiTaskType,
|
|
417
|
-
dimensions: config.memory.embeddings.geminiDimensions,
|
|
418
|
-
},
|
|
419
|
-
),
|
|
420
|
-
geminiCacheExtras(config),
|
|
421
|
-
),
|
|
429
|
+
backend: getDirectGeminiBackend(config, geminiKey),
|
|
422
430
|
reason: null,
|
|
423
431
|
};
|
|
424
432
|
}
|
|
@@ -454,7 +462,7 @@ export async function getMemoryBackendStatus(config: AssistantConfig): Promise<{
|
|
|
454
462
|
model: string | null;
|
|
455
463
|
reason: string | null;
|
|
456
464
|
}> {
|
|
457
|
-
if (
|
|
465
|
+
if (config.memory.enabled === false) {
|
|
458
466
|
return {
|
|
459
467
|
enabled: false,
|
|
460
468
|
degraded: false,
|
|
@@ -511,6 +519,21 @@ export async function embedWithBackend(
|
|
|
511
519
|
? await selectFallbackBackends(config, selection.backend.provider)
|
|
512
520
|
: [];
|
|
513
521
|
|
|
522
|
+
// A managed-proxy Gemini primary can fail at the proxy (e.g. a stale or
|
|
523
|
+
// revoked platform credential) while a valid direct Gemini key sits in the
|
|
524
|
+
// credential store. The provider-level chain above never includes Gemini
|
|
525
|
+
// when Gemini IS the primary, so without this the direct key would never
|
|
526
|
+
// be tried and the provider would stay down for the whole process.
|
|
527
|
+
if (
|
|
528
|
+
selection.backend instanceof GeminiEmbeddingBackend &&
|
|
529
|
+
selection.backend.managed
|
|
530
|
+
) {
|
|
531
|
+
const geminiKey = await getProviderKeyAsync("gemini");
|
|
532
|
+
if (geminiKey) {
|
|
533
|
+
fallbacks.push(getDirectGeminiBackend(config, geminiKey));
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
514
537
|
// ── Compute provider-specific vector cache extras ───────────────
|
|
515
538
|
const vectorExtras =
|
|
516
539
|
primaryProvider === "gemini" ? geminiCacheExtras(config) : undefined;
|
|
@@ -661,54 +684,12 @@ async function selectFallbackBackends(
|
|
|
661
684
|
case "gemini": {
|
|
662
685
|
const geminiKey = await getProviderKeyAsync("gemini");
|
|
663
686
|
if (geminiKey) {
|
|
664
|
-
backends.push(
|
|
665
|
-
|
|
666
|
-
"gemini",
|
|
667
|
-
config.memory.embeddings.geminiModel,
|
|
668
|
-
() =>
|
|
669
|
-
new GeminiEmbeddingBackend(
|
|
670
|
-
geminiKey,
|
|
671
|
-
config.memory.embeddings.geminiModel,
|
|
672
|
-
{
|
|
673
|
-
taskType: config.memory.embeddings.geminiTaskType,
|
|
674
|
-
dimensions: config.memory.embeddings.geminiDimensions,
|
|
675
|
-
},
|
|
676
|
-
),
|
|
677
|
-
geminiCacheExtras(config),
|
|
678
|
-
),
|
|
679
|
-
);
|
|
680
|
-
} else if (
|
|
681
|
-
isAssistantFeatureFlagEnabled(
|
|
682
|
-
"managed-gemini-embeddings-enabled",
|
|
683
|
-
config,
|
|
684
|
-
)
|
|
685
|
-
) {
|
|
687
|
+
backends.push(getDirectGeminiBackend(config, geminiKey));
|
|
688
|
+
} else {
|
|
686
689
|
// Try managed proxy Gemini as fallback when no direct key exists.
|
|
687
|
-
const
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
const managedBaseUrl = `${proxyCtx.platformBaseUrl}${meta.proxyPath}`;
|
|
691
|
-
const managedModel = config.memory.embeddings.geminiModel;
|
|
692
|
-
const managedDimensions =
|
|
693
|
-
config.memory.embeddings.geminiDimensions ?? 3072;
|
|
694
|
-
const extras = geminiCacheExtras(config);
|
|
695
|
-
backends.push(
|
|
696
|
-
getCachedOrCreate(
|
|
697
|
-
"gemini",
|
|
698
|
-
managedModel,
|
|
699
|
-
() =>
|
|
700
|
-
new GeminiEmbeddingBackend(
|
|
701
|
-
proxyCtx.assistantApiKey,
|
|
702
|
-
managedModel,
|
|
703
|
-
{
|
|
704
|
-
taskType: config.memory.embeddings.geminiTaskType,
|
|
705
|
-
dimensions: managedDimensions,
|
|
706
|
-
managedBaseUrl,
|
|
707
|
-
},
|
|
708
|
-
),
|
|
709
|
-
[...extras, "managed"],
|
|
710
|
-
),
|
|
711
|
-
);
|
|
690
|
+
const managed = await tryGetManagedGeminiBackend(config);
|
|
691
|
+
if (managed) {
|
|
692
|
+
backends.push(managed);
|
|
712
693
|
} else {
|
|
713
694
|
// Check managed cache variant first, then non-managed, so a warm
|
|
714
695
|
// managed backend survives transient proxy-context blips.
|
|
@@ -724,14 +705,6 @@ async function selectFallbackBackends(
|
|
|
724
705
|
);
|
|
725
706
|
if (cached) backends.push(cached);
|
|
726
707
|
}
|
|
727
|
-
} else {
|
|
728
|
-
// Preserve cached backend on transient credential-store failures.
|
|
729
|
-
const cached = getCached(
|
|
730
|
-
"gemini",
|
|
731
|
-
config.memory.embeddings.geminiModel,
|
|
732
|
-
geminiCacheExtras(config),
|
|
733
|
-
);
|
|
734
|
-
if (cached) backends.push(cached);
|
|
735
708
|
}
|
|
736
709
|
break;
|
|
737
710
|
}
|
|
@@ -36,6 +36,11 @@ export class GeminiEmbeddingBackend implements EmbeddingBackend {
|
|
|
36
36
|
this.managedBaseUrl = options?.managedBaseUrl;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/** True when requests route through the managed platform proxy. */
|
|
40
|
+
get managed(): boolean {
|
|
41
|
+
return Boolean(this.managedBaseUrl);
|
|
42
|
+
}
|
|
43
|
+
|
|
39
44
|
async embed(
|
|
40
45
|
inputs: EmbeddingInput[],
|
|
41
46
|
options?: EmbeddingRequestOptions,
|
|
@@ -173,6 +173,10 @@ const { applyNestedDefaults } = await import("../../../config/loader.js");
|
|
|
173
173
|
const { getSqliteFrom } = await import("../../db-connection.js");
|
|
174
174
|
const { migrateActivationState } =
|
|
175
175
|
await import("../../migrations/232-activation-state.js");
|
|
176
|
+
const { migrateAddMemoryV3EverInjected } =
|
|
177
|
+
await import("../../migrations/277-add-memory-v3-ever-injected.js");
|
|
178
|
+
const { getActiveSlugs: getV3ActiveSlugs, recordInjected: recordV3Injected } =
|
|
179
|
+
await import("../../../plugins/defaults/memory-v3-shadow/ever-injected-store.js");
|
|
176
180
|
const schema = await import("../../schema.js");
|
|
177
181
|
const { _resetMemoryV2QdrantForTests } = await import("../../v2/qdrant.js");
|
|
178
182
|
const { hydrate: hydrateActivationState, save: saveActivationState } =
|
|
@@ -210,6 +214,8 @@ function createTestDb(): DrizzleDb {
|
|
|
210
214
|
)
|
|
211
215
|
`);
|
|
212
216
|
migrateActivationState(db);
|
|
217
|
+
// `onCompacted` also clears memory-v3's everInjected record.
|
|
218
|
+
migrateAddMemoryV3EverInjected(db);
|
|
213
219
|
return db;
|
|
214
220
|
}
|
|
215
221
|
|
|
@@ -589,10 +595,16 @@ describe("ConversationGraphMemory.onCompacted — v2 activation eviction", () =>
|
|
|
589
595
|
const before = await hydrateActivationState(testDbHandle!, conversationId);
|
|
590
596
|
expect(before?.everInjected.map((e) => e.slug)).toContain("alice-vscode");
|
|
591
597
|
|
|
598
|
+
// Seed a memory-v3 everInjected record too: the same trigger clears it
|
|
599
|
+
// (the frozen card blocks those slugs rode were just compacted away).
|
|
600
|
+
recordV3Injected(conversationId, [{ slug: "alice-vscode", bytes: 100 }]);
|
|
601
|
+
expect(getV3ActiveSlugs(conversationId)).toEqual(new Set(["alice-vscode"]));
|
|
602
|
+
|
|
592
603
|
await memory.onCompacted(1);
|
|
593
604
|
|
|
594
605
|
const after = await hydrateActivationState(testDbHandle!, conversationId);
|
|
595
606
|
expect(after?.everInjected).toEqual([]);
|
|
607
|
+
expect(getV3ActiveSlugs(conversationId)).toEqual(new Set());
|
|
596
608
|
|
|
597
609
|
// Turn 2 — same Qdrant relevance. With everInjected cleared the slug
|
|
598
610
|
// should appear again in the injection block (re-attached on the new
|
|
@@ -60,6 +60,10 @@ const { handleRemember } = await import("../tool-handlers.js");
|
|
|
60
60
|
const { applyNestedDefaults } = await import("../../../config/loader.js");
|
|
61
61
|
|
|
62
62
|
const CONFIG = applyNestedDefaults({});
|
|
63
|
+
const CONFIG_MEMORY_OFF = {
|
|
64
|
+
...CONFIG,
|
|
65
|
+
memory: { ...CONFIG.memory, enabled: false },
|
|
66
|
+
};
|
|
63
67
|
const CONFIG_V2_OFF = {
|
|
64
68
|
...CONFIG,
|
|
65
69
|
memory: { ...CONFIG.memory, v2: { ...CONFIG.memory.v2, enabled: false } },
|
|
@@ -81,6 +85,21 @@ function todaysArchiveBasename(now: Date = new Date()): string {
|
|
|
81
85
|
}
|
|
82
86
|
|
|
83
87
|
describe("handleRemember — memory.v2.enabled on", () => {
|
|
88
|
+
test("does not write when global memory is disabled", () => {
|
|
89
|
+
const result = handleRemember(
|
|
90
|
+
{ content: "do not save this" },
|
|
91
|
+
"conv-memory-off",
|
|
92
|
+
"default",
|
|
93
|
+
CONFIG_MEMORY_OFF,
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
expect(result.success).toBe(false);
|
|
97
|
+
expect(result.message).toContain("Memory is disabled");
|
|
98
|
+
expect(existsSync(join(tmpWorkspace, "memory", "buffer.md"))).toBe(false);
|
|
99
|
+
expect(existsSync(join(tmpWorkspace, "pkb", "buffer.md"))).toBe(false);
|
|
100
|
+
expect(enqueueCalls).toEqual([]);
|
|
101
|
+
});
|
|
102
|
+
|
|
84
103
|
test("writes to memory/buffer.md and memory/archive/<today>.md", () => {
|
|
85
104
|
const result = handleRemember(
|
|
86
105
|
{ content: "Alice prefers VS Code over Vim" },
|