@vellumai/assistant 0.4.56 → 0.5.0
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 +10 -10
- package/Dockerfile +3 -0
- package/README.md +11 -11
- package/docs/architecture/integrations.md +2 -2
- package/docs/architecture/memory.md +3 -4
- package/docs/credential-execution-service.md +13 -20
- package/node_modules/@vellumai/ces-contracts/src/error.ts +5 -4
- package/package.json +1 -1
- package/src/__tests__/actor-token-service.test.ts +7 -7
- package/src/__tests__/anthropic-provider.test.ts +172 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +15 -1
- package/src/__tests__/approval-cascade.test.ts +2 -2
- package/src/__tests__/approval-routes-http.test.ts +3 -4
- package/src/__tests__/asset-materialize-tool.test.ts +5 -5
- package/src/__tests__/asset-search-tool.test.ts +1 -1
- package/src/__tests__/assistant-attachments.test.ts +5 -5
- package/src/__tests__/assistant-events-sse-hardening.test.ts +1 -1
- package/src/__tests__/assistant-feature-flags-integration.test.ts +50 -38
- package/src/__tests__/attachments-store.test.ts +2 -2
- package/src/__tests__/avatar-e2e.test.ts +5 -3
- package/src/__tests__/browser-skill-endstate.test.ts +0 -1
- package/src/__tests__/call-routes-http.test.ts +2 -2
- package/src/__tests__/callback-handoff-copy.test.ts +1 -1
- package/src/__tests__/cancel-resolves-conversation-key.test.ts +158 -0
- package/src/__tests__/channel-readiness-routes.test.ts +0 -1
- package/src/__tests__/channel-readiness-service.test.ts +0 -1
- package/src/__tests__/checker.test.ts +31 -32
- package/src/__tests__/chrome-cdp.test.ts +47 -18
- package/src/__tests__/claude-code-skill-regression.test.ts +2 -2
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +9 -18
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +1 -1
- package/src/__tests__/conversation-abort-tool-results.test.ts +4 -4
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -2
- package/src/__tests__/conversation-agent-loop.test.ts +11 -4
- package/src/__tests__/conversation-attachments.test.ts +1 -1
- package/src/__tests__/conversation-confirmation-signals.test.ts +2 -2
- package/src/__tests__/conversation-error.test.ts +33 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +0 -1
- package/src/__tests__/conversation-load-history-repair.test.ts +1 -1
- package/src/__tests__/conversation-pairing.test.ts +1 -1
- package/src/__tests__/conversation-pre-run-repair.test.ts +4 -4
- package/src/__tests__/conversation-provider-retry-repair.test.ts +4 -4
- package/src/__tests__/conversation-queue.test.ts +23 -14
- package/src/__tests__/conversation-routes-slash-commands.test.ts +3 -3
- package/src/__tests__/conversation-runtime-assembly.test.ts +204 -185
- package/src/__tests__/conversation-seed-composer.test.ts +1 -1
- package/src/__tests__/conversation-slash-queue.test.ts +4 -4
- package/src/__tests__/conversation-slash-unknown.test.ts +4 -4
- package/src/__tests__/conversation-starter-routes.test.ts +291 -0
- package/src/__tests__/conversation-wipe.test.ts +438 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +2 -3
- package/src/__tests__/conversation-workspace-injection.test.ts +4 -5
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +4 -5
- package/src/__tests__/credential-security-e2e.test.ts +20 -0
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault-unit.test.ts +227 -0
- package/src/__tests__/credentials-cli.test.ts +3 -0
- package/src/__tests__/date-context.test.ts +59 -377
- package/src/__tests__/drop-capability-card-state-migration.test.ts +169 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +11 -45
- package/src/__tests__/emit-signal-routing-intent.test.ts +3 -3
- package/src/__tests__/encrypted-store.test.ts +249 -15
- package/src/__tests__/ephemeral-permissions.test.ts +4 -5
- package/src/__tests__/event-bus.test.ts +3 -3
- package/src/__tests__/file-read-tool.test.ts +40 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +2 -2
- package/src/__tests__/gateway-only-guard.test.ts +1 -0
- package/src/__tests__/gemini-image-service.test.ts +4 -4
- package/src/__tests__/gemini-provider.test.ts +6 -9
- package/src/__tests__/guardian-binding-drift-heal.test.ts +128 -0
- package/src/__tests__/guardian-dispatch.test.ts +0 -1
- package/src/__tests__/host-file-read-tool.test.ts +87 -0
- package/src/__tests__/host-shell-tool.test.ts +6 -6
- package/src/__tests__/http-user-message-parity.test.ts +2 -2
- package/src/__tests__/identity-intro-cache.test.ts +209 -0
- package/src/__tests__/intent-routing.test.ts +51 -99
- package/src/__tests__/invite-routes-http.test.ts +5 -0
- package/src/__tests__/list-messages-attachments.test.ts +1 -1
- package/src/__tests__/managed-proxy-context.test.ts +2 -5
- package/src/__tests__/managed-skill-lifecycle.test.ts +8 -8
- package/src/__tests__/media-generate-image.test.ts +32 -15
- package/src/__tests__/media-reuse-story.e2e.test.ts +1 -1
- package/src/__tests__/memory-context-benchmark.benchmark.test.ts +1 -1
- package/src/__tests__/memory-lifecycle-e2e.test.ts +24 -18
- package/src/__tests__/memory-recall-quality.test.ts +4 -3
- package/src/__tests__/memory-regressions.test.ts +86 -90
- package/src/__tests__/migration-cross-version-compatibility.test.ts +32 -32
- package/src/__tests__/migration-export-http.test.ts +26 -27
- package/src/__tests__/migration-import-commit-http.test.ts +165 -37
- package/src/__tests__/migration-import-preflight-http.test.ts +81 -20
- package/src/__tests__/migration-validate-http.test.ts +16 -16
- package/src/__tests__/model-intents.test.ts +2 -2
- package/src/__tests__/no-domain-routing-in-prompt-guard.test.ts +1 -1
- package/src/__tests__/non-member-access-request.test.ts +3 -3
- package/src/__tests__/notification-broadcaster.test.ts +1 -1
- package/src/__tests__/notification-decision-fallback.test.ts +2 -2
- package/src/__tests__/notification-decision-identity.test.ts +8 -9
- package/src/__tests__/notification-decision-strategy.test.ts +1 -1
- package/src/__tests__/notification-deep-link.test.ts +1 -1
- package/src/__tests__/notification-guardian-path.test.ts +0 -1
- package/src/__tests__/notification-schedule-dedup.test.ts +7 -7
- package/src/__tests__/oauth-store.test.ts +1 -3
- package/src/__tests__/oauth2-gateway-transport.test.ts +6 -1
- package/src/__tests__/onboarding-template-contract.test.ts +23 -59
- package/src/__tests__/provider-error-scenarios.test.ts +154 -0
- package/src/__tests__/provider-fail-open-selection.test.ts +2 -2
- package/src/__tests__/provider-managed-proxy-integration.test.ts +8 -9
- package/src/__tests__/provider-registry-ollama.test.ts +5 -2
- package/src/__tests__/qdrant-manager.test.ts +7 -7
- package/src/__tests__/ratelimit.test.ts +0 -74
- package/src/__tests__/recording-handler.test.ts +0 -1
- package/src/__tests__/require-fresh-approval.test.ts +1 -1
- package/src/__tests__/runtime-attachment-metadata.test.ts +1 -1
- package/src/__tests__/runtime-events-sse-parity.test.ts +1 -1
- package/src/__tests__/runtime-events-sse.test.ts +1 -1
- package/src/__tests__/scheduler-recurrence.test.ts +46 -2
- package/src/__tests__/schema-transforms.test.ts +114 -54
- package/src/__tests__/secret-onetime-send.test.ts +20 -0
- package/src/__tests__/secret-routes-managed-proxy.test.ts +5 -2
- package/src/__tests__/secret-scanner-executor.test.ts +1 -2
- package/src/__tests__/send-endpoint-busy.test.ts +63 -4
- package/src/__tests__/send-notification-tool.test.ts +2 -2
- package/src/__tests__/shell-credential-ref.test.ts +0 -1
- package/src/__tests__/shell-tool-proxy-mode.test.ts +1 -2
- package/src/__tests__/skill-memory.test.ts +549 -0
- package/src/__tests__/skill-script-runner-sandbox.test.ts +1 -2
- package/src/__tests__/slack-app-setup-skill-regression.test.ts +37 -0
- package/src/__tests__/slack-channel-config.test.ts +109 -94
- package/src/__tests__/swarm-conversation-integration.test.ts +2 -2
- package/src/__tests__/swarm-recursion.test.ts +2 -2
- package/src/__tests__/swarm-tool.test.ts +2 -2
- package/src/__tests__/system-prompt.test.ts +19 -66
- package/src/__tests__/telegram-config.test.ts +121 -0
- package/src/__tests__/terminal-tools.test.ts +1 -1
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +1 -2
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -1
- package/src/__tests__/tool-executor-shell-integration.test.ts +1 -1
- package/src/__tests__/tool-executor.test.ts +1 -1
- package/src/__tests__/trace-emitter.test.ts +8 -1
- package/src/__tests__/trust-store.test.ts +7 -8
- package/src/__tests__/twilio-routes.test.ts +1 -18
- package/src/__tests__/user-reference.test.ts +82 -2
- package/src/__tests__/vbundle-pax-and-symlink.test.ts +196 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +1 -1
- package/src/approvals/guardian-request-resolvers.ts +3 -3
- package/src/avatar/ascii-renderer.ts +2 -2
- package/src/avatar/png-renderer.ts +2 -2
- package/src/avatar/resvg-lazy.ts +21 -0
- package/src/calls/guardian-dispatch.ts +1 -1
- package/src/calls/relay-access-wait.ts +2 -2
- package/src/calls/twilio-rest.ts +0 -248
- package/src/cli/AGENTS.md +5 -8
- package/src/cli/__tests__/notifications.test.ts +5 -5
- package/src/cli/commands/avatar.ts +64 -2
- package/src/cli/commands/conversations.ts +131 -1
- package/src/cli/commands/credentials.ts +2 -0
- package/src/cli/commands/notifications.ts +3 -3
- package/src/cli.ts +10 -0
- package/src/config/bundled-skills/acp/SKILL.md +5 -5
- package/src/config/bundled-skills/acp/TOOLS.json +6 -6
- package/src/config/bundled-skills/app-builder/SKILL.md +42 -42
- package/src/config/bundled-skills/app-builder/TOOLS.json +10 -10
- package/src/config/bundled-skills/browser/SKILL.md +15 -15
- package/src/config/bundled-skills/browser/TOOLS.json +14 -14
- package/src/config/bundled-skills/chatgpt-import/SKILL.md +2 -2
- package/src/config/bundled-skills/chatgpt-import/TOOLS.json +1 -1
- package/src/config/bundled-skills/chatgpt-import/tools/chatgpt-import.ts +1 -1
- package/src/config/bundled-skills/claude-code/SKILL.md +5 -5
- package/src/config/bundled-skills/computer-use/SKILL.md +2 -2
- package/src/config/bundled-skills/computer-use/TOOLS.json +15 -15
- package/src/config/bundled-skills/contacts/SKILL.md +3 -3
- package/src/config/bundled-skills/contacts/TOOLS.json +4 -4
- package/src/config/bundled-skills/document/SKILL.md +4 -4
- package/src/config/bundled-skills/document/TOOLS.json +2 -2
- package/src/config/bundled-skills/followups/TOOLS.json +3 -3
- package/src/config/bundled-skills/gmail/SKILL.md +32 -32
- package/src/config/bundled-skills/gmail/TOOLS.json +16 -16
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +1 -1
- package/src/config/bundled-skills/google-calendar/SKILL.md +1 -1
- package/src/config/bundled-skills/google-calendar/TOOLS.json +5 -5
- package/src/config/bundled-skills/google-calendar/types.ts +1 -1
- package/src/config/bundled-skills/heartbeat/SKILL.md +43 -0
- package/src/config/bundled-skills/image-studio/SKILL.md +3 -3
- package/src/config/bundled-skills/image-studio/TOOLS.json +2 -3
- package/src/config/bundled-skills/image-studio/tools/media-generate-image.ts +16 -12
- package/src/config/bundled-skills/media-processing/SKILL.md +40 -40
- package/src/config/bundled-skills/media-processing/TOOLS.json +8 -8
- package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +2 -2
- package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +1 -1
- package/src/config/bundled-skills/media-processing/services/gemini-map.ts +5 -5
- package/src/config/bundled-skills/media-processing/services/gemini-video.ts +2 -2
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +2 -2
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +2 -2
- package/src/config/bundled-skills/media-processing/services/reduce.ts +3 -3
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
- package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +1 -1
- package/src/config/bundled-skills/messaging/SKILL.md +29 -25
- package/src/config/bundled-skills/messaging/TOOLS.json +11 -11
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +1 -1
- package/src/config/bundled-skills/messaging/tools/shared.ts +1 -1
- package/src/config/bundled-skills/notifications/SKILL.md +3 -3
- package/src/config/bundled-skills/notifications/TOOLS.json +2 -2
- package/src/config/bundled-skills/notifications/tools/send-notification.ts +3 -3
- package/src/config/bundled-skills/orchestration/SKILL.md +1 -1
- package/src/config/bundled-skills/orchestration/TOOLS.json +1 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +18 -14
- package/src/config/bundled-skills/phone-calls/TOOLS.json +3 -3
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +2 -2
- package/src/config/bundled-skills/phone-calls/references/TRANSCRIPTS.md +2 -2
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +1 -1
- package/src/config/bundled-skills/playbooks/TOOLS.json +4 -4
- package/src/config/bundled-skills/schedule/SKILL.md +26 -26
- package/src/config/bundled-skills/schedule/TOOLS.json +5 -5
- package/src/config/bundled-skills/screen-watch/SKILL.md +3 -3
- package/src/config/bundled-skills/screen-watch/TOOLS.json +1 -1
- package/src/config/bundled-skills/sequences/SKILL.md +2 -2
- package/src/config/bundled-skills/sequences/TOOLS.json +10 -10
- package/src/config/bundled-skills/sequences/tools/sequence-analytics.ts +2 -2
- package/src/config/bundled-skills/sequences/tools/sequence-enroll.ts +2 -2
- package/src/config/bundled-skills/sequences/tools/sequence-enrollment-list.ts +1 -1
- package/src/config/bundled-skills/sequences/tools/sequence-get.ts +1 -1
- package/src/config/bundled-skills/sequences/tools/sequence-import.ts +3 -3
- package/src/config/bundled-skills/sequences/tools/sequence-list.ts +1 -1
- package/src/config/bundled-skills/sequences/tools/sequence-update.ts +1 -1
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/open-system-settings.ts +1 -1
- package/src/config/bundled-skills/skill-management/TOOLS.json +5 -5
- package/src/config/bundled-skills/skills-catalog/SKILL.md +84 -0
- package/src/config/bundled-skills/slack/SKILL.md +2 -2
- package/src/config/bundled-skills/slack/TOOLS.json +8 -8
- package/src/config/bundled-skills/slack/tools/slack-scan-digest.ts +3 -3
- package/src/config/bundled-skills/subagent/TOOLS.json +5 -5
- package/src/config/bundled-skills/tasks/SKILL.md +1 -1
- package/src/config/bundled-skills/tasks/TOOLS.json +9 -9
- package/src/config/bundled-skills/transcribe/SKILL.md +5 -5
- package/src/config/bundled-skills/transcribe/TOOLS.json +1 -1
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +10 -10
- package/src/config/bundled-skills/watcher/SKILL.md +4 -4
- package/src/config/bundled-skills/watcher/TOOLS.json +5 -5
- package/src/config/feature-flag-registry.json +33 -17
- package/src/config/schemas/sandbox.ts +1 -1
- package/src/config/schemas/services.ts +13 -3
- package/src/config/schemas/timeouts.ts +0 -10
- package/src/contacts/contact-store.ts +63 -0
- package/src/contacts/contacts-write.ts +1 -1
- package/src/daemon/assistant-attachments.ts +2 -2
- package/src/daemon/conversation-agent-loop-handlers.ts +2 -2
- package/src/daemon/conversation-agent-loop.ts +7 -30
- package/src/daemon/conversation-error.ts +24 -0
- package/src/daemon/conversation-memory.ts +8 -7
- package/src/daemon/conversation-runtime-assembly.ts +141 -275
- package/src/daemon/conversation-slash.ts +7 -26
- package/src/daemon/conversation-surfaces.ts +14 -0
- package/src/daemon/conversation-tool-setup.ts +9 -8
- package/src/daemon/conversation.ts +2 -0
- package/src/daemon/daemon-control.ts +1 -1
- package/src/daemon/date-context.ts +10 -83
- package/src/daemon/handlers/config-channels.ts +12 -2
- package/src/daemon/handlers/config-slack-channel.ts +7 -1
- package/src/daemon/handlers/config-telegram.ts +6 -1
- package/src/daemon/handlers/conversations.ts +2 -2
- package/src/daemon/handlers/skills.ts +4 -0
- package/src/daemon/lifecycle.ts +28 -4
- package/src/daemon/providers-setup.ts +1 -1
- package/src/daemon/server.ts +1 -5
- package/src/daemon/shutdown-handlers.ts +9 -3
- package/src/daemon/tool-side-effects.ts +40 -0
- package/src/daemon/trace-emitter.ts +26 -2
- package/src/events/domain-events.ts +1 -1
- package/src/events/tool-permission-telemetry-listener.ts +46 -0
- package/src/inbound/platform-callback-registration.ts +0 -18
- package/src/media/app-icon-generator.ts +15 -8
- package/src/media/avatar-router.ts +15 -8
- package/src/media/gemini-image-service.ts +125 -21
- package/src/memory/attachments-store.ts +3 -3
- package/src/memory/channel-verification-sessions.ts +6 -6
- package/src/memory/conversation-crud.ts +196 -1
- package/src/memory/{thread-starters-cadence.ts → conversation-starters-cadence.ts} +9 -42
- package/src/memory/conversation-title-service.ts +2 -3
- package/src/memory/db-init.ts +25 -1
- package/src/memory/invite-store.ts +4 -4
- package/src/memory/items-extractor.ts +4 -4
- package/src/memory/job-handlers/{thread-starters.ts → conversation-starters.ts} +123 -38
- package/src/memory/jobs-store.ts +3 -2
- package/src/memory/jobs-worker.ts +7 -5
- package/src/memory/lifecycle-events-store.ts +63 -0
- package/src/memory/migrations/172-rename-created-by-session-id.ts +27 -0
- package/src/memory/migrations/173-rename-source-session-id.ts +16 -0
- package/src/memory/migrations/174-rename-thread-starters-table.ts +52 -0
- package/src/memory/migrations/175-create-lifecycle-events.ts +15 -0
- package/src/memory/migrations/176-drop-capability-card-state.ts +36 -0
- package/src/memory/migrations/177-create-trace-events-table.ts +40 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +13 -0
- package/src/memory/retriever.test.ts +223 -96
- package/src/memory/retriever.ts +115 -138
- package/src/memory/schema/calls.ts +1 -1
- package/src/memory/schema/contacts.ts +1 -1
- package/src/memory/schema/infrastructure.ts +29 -0
- package/src/memory/schema/memory-core.ts +7 -17
- package/src/memory/schema/notifications.ts +1 -1
- package/src/memory/search/formatting.ts +23 -6
- package/src/memory/search/lexical.ts +2 -0
- package/src/memory/search/semantic.ts +2 -0
- package/src/memory/search/staleness.ts +5 -1
- package/src/memory/search/types.ts +4 -0
- package/src/memory/task-memory-cleanup.ts +96 -6
- package/src/memory/trace-event-store.ts +148 -0
- package/src/notifications/README.md +1 -1
- package/src/notifications/decision-engine.ts +45 -4
- package/src/notifications/emit-signal.ts +5 -4
- package/src/notifications/events-store.ts +4 -4
- package/src/notifications/signal.ts +1 -1
- package/src/oauth/manual-token-connection.ts +49 -25
- package/src/permissions/checker.ts +6 -5
- package/src/permissions/defaults.ts +4 -4
- package/src/prompts/__tests__/build-cli-reference-section.test.ts +9 -90
- package/src/prompts/cache-boundary.ts +8 -0
- package/src/prompts/system-prompt.ts +105 -634
- package/src/prompts/templates/BOOTSTRAP.md +172 -33
- package/src/prompts/templates/IDENTITY.md +8 -24
- package/src/prompts/templates/SOUL.md +20 -41
- package/src/prompts/templates/USER.md +3 -19
- package/src/prompts/user-reference.ts +14 -16
- package/src/providers/anthropic/client.ts +51 -19
- package/src/providers/gemini/client.ts +6 -9
- package/src/providers/managed-proxy/constants.ts +1 -7
- package/src/providers/managed-proxy/context.ts +0 -1
- package/src/providers/model-intents.ts +5 -5
- package/src/providers/openai/client.ts +10 -1
- package/src/providers/openrouter/client.ts +1 -0
- package/src/providers/ratelimit.ts +0 -35
- package/src/providers/registry.ts +3 -5
- package/src/providers/retry.ts +18 -1
- package/src/runtime/access-request-helper.ts +16 -2
- package/src/runtime/auth/route-policy.ts +7 -0
- package/src/runtime/channel-verification-service.ts +1 -1
- package/src/runtime/confirmation-request-guardian-bridge.ts +1 -1
- package/src/runtime/guardian-vellum-migration.ts +61 -1
- package/src/runtime/http-server.ts +8 -4
- package/src/runtime/migrations/vbundle-builder.ts +212 -32
- package/src/runtime/migrations/vbundle-import-analyzer.ts +74 -8
- package/src/runtime/migrations/vbundle-importer.ts +66 -1
- package/src/runtime/migrations/vbundle-validator.ts +17 -3
- package/src/runtime/routes/approval-strategies/guardian-callback-strategy.ts +4 -4
- package/src/runtime/routes/attachment-routes.ts +2 -2
- package/src/runtime/routes/btw-routes.ts +93 -0
- package/src/runtime/routes/channel-verification-routes.ts +19 -2
- package/src/runtime/routes/conversation-management-routes.ts +55 -1
- package/src/runtime/routes/conversation-query-routes.ts +1 -1
- package/src/runtime/routes/conversation-routes.ts +49 -5
- package/src/runtime/routes/conversation-starter-routes.ts +207 -0
- package/src/runtime/routes/guardian-bootstrap-routes.ts +13 -9
- package/src/runtime/routes/identity-intro-cache.ts +105 -0
- package/src/runtime/routes/identity-routes.ts +51 -0
- package/src/runtime/routes/inbound-stages/escalation-intercept.ts +1 -1
- package/src/runtime/routes/inbound-stages/verification-intercept.ts +1 -1
- package/src/runtime/routes/migration-routes.ts +25 -13
- package/src/runtime/routes/secret-routes.ts +18 -0
- package/src/runtime/routes/settings-routes.ts +9 -9
- package/src/runtime/routes/telemetry-routes.ts +53 -0
- package/src/runtime/routes/trace-event-routes.ts +62 -0
- package/src/runtime/tool-grant-request-helper.ts +1 -1
- package/src/runtime/verification-outbound-actions.ts +47 -31
- package/src/security/encrypted-store.ts +262 -78
- package/src/skills/catalog-install.ts +10 -0
- package/src/skills/managed-store.ts +2 -0
- package/src/skills/skill-memory.ts +222 -0
- package/src/subagent/manager.ts +1 -4
- package/src/telemetry/types.ts +10 -1
- package/src/telemetry/usage-telemetry-reporter.test.ts +7 -2
- package/src/telemetry/usage-telemetry-reporter.ts +53 -4
- package/src/tools/AGENTS.md +11 -11
- package/src/tools/acp/spawn.ts +1 -1
- package/src/tools/apps/executors.ts +8 -8
- package/src/tools/apps/registry.ts +1 -1
- package/src/tools/assets/materialize.ts +6 -6
- package/src/tools/assets/search.ts +10 -10
- package/src/tools/browser/__tests__/auth-cache.test.ts +2 -2
- package/src/tools/browser/__tests__/auth-detector.test.ts +4 -4
- package/src/tools/browser/auth-detector.ts +6 -6
- package/src/tools/browser/browser-execution.ts +13 -13
- package/src/tools/browser/browser-manager.ts +3 -3
- package/src/tools/browser/chrome-cdp.ts +5 -5
- package/src/tools/browser/jit-auth.ts +2 -2
- package/src/tools/browser/network-recorder.test.ts +2 -2
- package/src/tools/browser/network-recorder.ts +3 -3
- package/src/tools/browser/runtime-check.ts +3 -3
- package/src/tools/claude-code/claude-code.ts +2 -2
- package/src/tools/computer-use/definitions.ts +18 -18
- package/src/tools/credential-execution/make-authenticated-request.ts +4 -4
- package/src/tools/credential-execution/manage-secure-command-tool.ts +3 -3
- package/src/tools/credential-execution/run-authenticated-command.ts +4 -4
- package/src/tools/credentials/broker-types.ts +5 -5
- package/src/tools/credentials/broker.ts +15 -15
- package/src/tools/credentials/metadata-store.ts +2 -2
- package/src/tools/credentials/resolve.ts +1 -1
- package/src/tools/credentials/selection.ts +1 -1
- package/src/tools/credentials/tool-policy.ts +1 -1
- package/src/tools/credentials/vault.ts +115 -25
- package/src/tools/execution-target.ts +2 -2
- package/src/tools/executor.ts +7 -7
- package/src/tools/filesystem/edit.ts +2 -2
- package/src/tools/filesystem/read.ts +15 -4
- package/src/tools/filesystem/write.ts +1 -1
- package/src/tools/host-filesystem/edit.ts +2 -1
- package/src/tools/host-filesystem/read.ts +18 -1
- package/src/tools/host-filesystem/write.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +9 -8
- package/src/tools/mcp/mcp-tool-factory.ts +7 -6
- package/src/tools/memory/definitions.ts +6 -5
- package/src/tools/memory/handlers.test.ts +1 -1
- package/src/tools/network/__tests__/web-search.test.ts +3 -3
- package/src/tools/network/domain-normalize.ts +2 -2
- package/src/tools/network/script-proxy/session-manager.ts +10 -10
- package/src/tools/network/web-fetch.ts +1 -1
- package/src/tools/network/web-search.ts +3 -3
- package/src/tools/permission-checker.ts +8 -8
- package/src/tools/registry.ts +7 -7
- package/src/tools/schedule/list.ts +2 -2
- package/src/tools/schema-transforms.ts +31 -21
- package/src/tools/secret-detection-handler.ts +1 -1
- package/src/tools/sensitive-output-placeholders.ts +1 -1
- package/src/tools/shared/filesystem/edit-engine.ts +1 -1
- package/src/tools/shared/filesystem/file-ops-service.ts +3 -3
- package/src/tools/shared/filesystem/image-read.ts +25 -5
- package/src/tools/shared/filesystem/path-policy.ts +2 -2
- package/src/tools/shared/shell-output.ts +1 -1
- package/src/tools/side-effects.ts +1 -1
- package/src/tools/skills/execute.ts +1 -1
- package/src/tools/skills/load.ts +3 -3
- package/src/tools/skills/sandbox-runner.ts +3 -3
- package/src/tools/subagent/read.ts +1 -1
- package/src/tools/subagent/spawn.ts +2 -2
- package/src/tools/swarm/delegate.ts +3 -3
- package/src/tools/system/request-permission.ts +5 -4
- package/src/tools/terminal/backends/native.ts +4 -4
- package/src/tools/terminal/parser.ts +6 -6
- package/src/tools/terminal/sandbox-diagnostics.ts +1 -1
- package/src/tools/terminal/shell.ts +16 -16
- package/src/tools/tool-approval-handler.ts +21 -12
- package/src/tools/tool-manifest.ts +4 -4
- package/src/tools/types.ts +3 -3
- package/src/tools/ui-surface/definitions.ts +9 -37
- package/src/tools/watcher/list.ts +1 -1
- package/src/util/logger.ts +7 -2
- package/src/util/pricing.ts +4 -0
- package/src/util/retry.ts +29 -1
- package/src/workspace/migrations/007-web-search-provider-rename.ts +37 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/__tests__/cli-help-reference-sync.test.ts +0 -26
- package/src/__tests__/onboarding-starter-tasks.test.ts +0 -190
- package/src/cli/reference.ts +0 -38
- package/src/memory/job-handlers/capability-cards.ts +0 -420
- package/src/runtime/routes/thread-starter-routes.ts +0 -294
|
@@ -37,13 +37,13 @@ export async function generateAppIcon(
|
|
|
37
37
|
appDescription?: string,
|
|
38
38
|
): Promise<void> {
|
|
39
39
|
const config = getConfig();
|
|
40
|
-
const
|
|
40
|
+
const imageGenMode = config.services["image-generation"].mode;
|
|
41
41
|
|
|
42
|
+
// Resolve credentials strictly based on mode — no cross-mode fallbacks
|
|
42
43
|
let credentials: ImageGenCredentials | undefined;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
const managedBaseUrl = await buildManagedBaseUrl("vertex");
|
|
44
|
+
|
|
45
|
+
if (imageGenMode === "managed") {
|
|
46
|
+
const managedBaseUrl = await buildManagedBaseUrl("gemini");
|
|
47
47
|
if (managedBaseUrl) {
|
|
48
48
|
const ctx = await resolveManagedProxyContext();
|
|
49
49
|
credentials = {
|
|
@@ -52,12 +52,19 @@ export async function generateAppIcon(
|
|
|
52
52
|
baseUrl: managedBaseUrl,
|
|
53
53
|
};
|
|
54
54
|
}
|
|
55
|
+
} else {
|
|
56
|
+
const apiKey = await getProviderKeyAsync("gemini");
|
|
57
|
+
if (apiKey) {
|
|
58
|
+
credentials = { type: "direct", apiKey };
|
|
59
|
+
}
|
|
55
60
|
}
|
|
56
61
|
|
|
57
62
|
if (!credentials) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
const reason =
|
|
64
|
+
imageGenMode === "managed"
|
|
65
|
+
? "Managed proxy is not available"
|
|
66
|
+
: "No Gemini API key configured";
|
|
67
|
+
log.debug(`${reason} — skipping app icon generation`);
|
|
61
68
|
return;
|
|
62
69
|
}
|
|
63
70
|
|
|
@@ -14,13 +14,13 @@ export async function generateAvatar(
|
|
|
14
14
|
prompt: string,
|
|
15
15
|
): Promise<{ imageBase64: string; mimeType: string }> {
|
|
16
16
|
const config = getConfig();
|
|
17
|
-
const
|
|
17
|
+
const imageGenMode = config.services["image-generation"].mode;
|
|
18
18
|
|
|
19
|
+
// Resolve credentials strictly based on mode — no cross-mode fallbacks
|
|
19
20
|
let credentials: ImageGenCredentials | undefined;
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const managedBaseUrl = await buildManagedBaseUrl("vertex");
|
|
21
|
+
|
|
22
|
+
if (imageGenMode === "managed") {
|
|
23
|
+
const managedBaseUrl = await buildManagedBaseUrl("gemini");
|
|
24
24
|
if (managedBaseUrl) {
|
|
25
25
|
const ctx = await resolveManagedProxyContext();
|
|
26
26
|
credentials = {
|
|
@@ -29,12 +29,19 @@ export async function generateAvatar(
|
|
|
29
29
|
baseUrl: managedBaseUrl,
|
|
30
30
|
};
|
|
31
31
|
}
|
|
32
|
+
} else {
|
|
33
|
+
const geminiKey = await getProviderKeyAsync("gemini");
|
|
34
|
+
if (geminiKey) {
|
|
35
|
+
credentials = { type: "direct", apiKey: geminiKey };
|
|
36
|
+
}
|
|
32
37
|
}
|
|
33
38
|
|
|
34
39
|
if (!credentials) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
40
|
+
const hint =
|
|
41
|
+
imageGenMode === "managed"
|
|
42
|
+
? "Managed proxy is not available. Please log in to Vellum or switch to Your Own mode."
|
|
43
|
+
: "Gemini API key is not configured. Please set your Gemini API key in Settings > Models & Services.";
|
|
44
|
+
throw new ConfigError(hint);
|
|
38
45
|
}
|
|
39
46
|
|
|
40
47
|
const result = await generateImage(credentials, {
|
|
@@ -7,7 +7,7 @@ interface ImageGenerationRequest {
|
|
|
7
7
|
mode: "generate" | "edit";
|
|
8
8
|
/** Base64-encoded source images for edit mode */
|
|
9
9
|
sourceImages?: Array<{ mimeType: string; dataBase64: string }>;
|
|
10
|
-
/** Model override; defaults to 'gemini-
|
|
10
|
+
/** Model override; defaults to 'gemini-3.1-flash-image-preview' */
|
|
11
11
|
model?: string;
|
|
12
12
|
/** Number of output variants (1-4, default 1) */
|
|
13
13
|
variants?: number;
|
|
@@ -19,7 +19,7 @@ interface DirectCredentials {
|
|
|
19
19
|
apiKey: string;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
/** Credentials for managed proxy access
|
|
22
|
+
/** Credentials for managed proxy access (platform translates to Vertex AI). */
|
|
23
23
|
interface ManagedProxyCredentials {
|
|
24
24
|
type: "managed-proxy";
|
|
25
25
|
assistantApiKey: string;
|
|
@@ -43,10 +43,9 @@ interface ImageGenerationResult {
|
|
|
43
43
|
|
|
44
44
|
// --- Constants ---
|
|
45
45
|
|
|
46
|
-
const DEFAULT_MODEL = "gemini-
|
|
46
|
+
const DEFAULT_MODEL = "gemini-3.1-flash-image-preview";
|
|
47
47
|
const ALLOWED_MODELS = new Set([
|
|
48
|
-
"gemini-
|
|
49
|
-
"gemini-3-pro-image",
|
|
48
|
+
"gemini-3.1-flash-image-preview",
|
|
50
49
|
"gemini-3-pro-image-preview",
|
|
51
50
|
]);
|
|
52
51
|
const MAX_VARIANTS = 4;
|
|
@@ -76,6 +75,77 @@ export function mapGeminiError(error: unknown): string {
|
|
|
76
75
|
return "An unexpected error occurred during image generation.";
|
|
77
76
|
}
|
|
78
77
|
|
|
78
|
+
// --- Managed proxy direct HTTP call ---
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Call the managed proxy directly via fetch, using the Gemini API URL format.
|
|
82
|
+
* The platform proxy translates this to Vertex AI internally with ADC auth.
|
|
83
|
+
*
|
|
84
|
+
* Uses the Gemini API format:
|
|
85
|
+
* POST {baseUrl}/v1beta/models/{model}:generateContent
|
|
86
|
+
*/
|
|
87
|
+
async function generateImageViaProxy(
|
|
88
|
+
credentials: ManagedProxyCredentials,
|
|
89
|
+
model: string,
|
|
90
|
+
contents: unknown[],
|
|
91
|
+
config: Record<string, unknown>,
|
|
92
|
+
): Promise<{
|
|
93
|
+
images: GeneratedImage[];
|
|
94
|
+
text?: string;
|
|
95
|
+
}> {
|
|
96
|
+
const url = `${credentials.baseUrl}/v1beta/models/${model}:generateContent`;
|
|
97
|
+
|
|
98
|
+
const response = await fetch(url, {
|
|
99
|
+
method: "POST",
|
|
100
|
+
headers: {
|
|
101
|
+
"Content-Type": "application/json",
|
|
102
|
+
Authorization: `Bearer ${credentials.assistantApiKey}`,
|
|
103
|
+
},
|
|
104
|
+
body: JSON.stringify({
|
|
105
|
+
contents,
|
|
106
|
+
generationConfig: config,
|
|
107
|
+
}),
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
if (!response.ok) {
|
|
111
|
+
const body = await response.text();
|
|
112
|
+
throw new Error(
|
|
113
|
+
`Managed proxy request failed (${response.status}): ${body}`,
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const data = (await response.json()) as {
|
|
118
|
+
candidates?: Array<{
|
|
119
|
+
content?: {
|
|
120
|
+
parts?: Array<{
|
|
121
|
+
text?: string;
|
|
122
|
+
inlineData?: { mimeType?: string; data?: string };
|
|
123
|
+
}>;
|
|
124
|
+
};
|
|
125
|
+
}>;
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
const images: GeneratedImage[] = [];
|
|
129
|
+
let text: string | undefined;
|
|
130
|
+
|
|
131
|
+
const responseParts = data.candidates?.[0]?.content?.parts;
|
|
132
|
+
if (responseParts) {
|
|
133
|
+
for (const part of responseParts) {
|
|
134
|
+
if (part.inlineData) {
|
|
135
|
+
images.push({
|
|
136
|
+
mimeType: part.inlineData.mimeType ?? "image/png",
|
|
137
|
+
dataBase64: part.inlineData.data ?? "",
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
if (part.text) {
|
|
141
|
+
text = text ? `${text}\n${part.text}` : part.text;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return { images, text };
|
|
147
|
+
}
|
|
148
|
+
|
|
79
149
|
// --- Core function ---
|
|
80
150
|
|
|
81
151
|
export async function generateImage(
|
|
@@ -89,19 +159,6 @@ export async function generateImage(
|
|
|
89
159
|
|
|
90
160
|
const variants = Math.max(1, Math.min(request.variants ?? 1, MAX_VARIANTS));
|
|
91
161
|
|
|
92
|
-
const client =
|
|
93
|
-
credentials.type === "managed-proxy"
|
|
94
|
-
? new GoogleGenAI({
|
|
95
|
-
vertexai: true,
|
|
96
|
-
project: "proxy",
|
|
97
|
-
location: "us-central1",
|
|
98
|
-
httpOptions: {
|
|
99
|
-
baseUrl: credentials.baseUrl,
|
|
100
|
-
headers: { Authorization: `Bearer ${credentials.assistantApiKey}` },
|
|
101
|
-
},
|
|
102
|
-
})
|
|
103
|
-
: new GoogleGenAI({ apiKey: credentials.apiKey });
|
|
104
|
-
|
|
105
162
|
// Build contents array — append a title request so the model's text
|
|
106
163
|
// response contains a short filename-safe title for the generated image.
|
|
107
164
|
const promptWithTitle = `${request.prompt}\n\nAlso respond with a short title (max 6 words) for the image on its own line, prefixed with "Title: ".`;
|
|
@@ -118,11 +175,60 @@ export async function generateImage(
|
|
|
118
175
|
}
|
|
119
176
|
|
|
120
177
|
const config = { responseModalities: ["TEXT", "IMAGE"] as string[] };
|
|
178
|
+
const contents = [{ role: "user" as const, parts }];
|
|
179
|
+
|
|
180
|
+
// For the managed proxy, bypass the @google/genai SDK and make direct HTTP
|
|
181
|
+
// calls. The SDK's generateContent doesn't support responseModalities for
|
|
182
|
+
// image generation. Direct fetch lets us use the Gemini API format with
|
|
183
|
+
// the managed proxy translating to Vertex internally.
|
|
184
|
+
if (credentials.type === "managed-proxy") {
|
|
185
|
+
const makeSingleCall = () =>
|
|
186
|
+
generateImageViaProxy(credentials, model, contents, config);
|
|
187
|
+
|
|
188
|
+
if (variants === 1) {
|
|
189
|
+
const result = await makeSingleCall();
|
|
190
|
+
const title = extractTitle(result.text);
|
|
191
|
+
if (title) {
|
|
192
|
+
for (const img of result.images) img.title = title;
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
images: result.images,
|
|
196
|
+
text: stripTitleLine(result.text),
|
|
197
|
+
resolvedModel: model,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const results = await Promise.all(
|
|
202
|
+
Array.from({ length: variants }, () => makeSingleCall()),
|
|
203
|
+
);
|
|
204
|
+
const allImages: GeneratedImage[] = [];
|
|
205
|
+
let combinedText: string | undefined;
|
|
206
|
+
for (const result of results) {
|
|
207
|
+
const title = extractTitle(result.text);
|
|
208
|
+
if (title) {
|
|
209
|
+
for (const img of result.images) img.title = title;
|
|
210
|
+
}
|
|
211
|
+
allImages.push(...result.images);
|
|
212
|
+
if (result.text) {
|
|
213
|
+
combinedText = combinedText
|
|
214
|
+
? `${combinedText}\n${result.text}`
|
|
215
|
+
: result.text;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
images: allImages,
|
|
220
|
+
text: stripTitleLine(combinedText),
|
|
221
|
+
resolvedModel: model,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
// Direct Gemini API path — use the SDK with API key auth.
|
|
226
|
+
const client = new GoogleGenAI({ apiKey: credentials.apiKey });
|
|
121
227
|
|
|
122
228
|
const makeSingleCall = async () => {
|
|
123
229
|
const response = await client.models.generateContent({
|
|
124
230
|
model,
|
|
125
|
-
contents
|
|
231
|
+
contents,
|
|
126
232
|
config,
|
|
127
233
|
});
|
|
128
234
|
|
|
@@ -144,7 +250,6 @@ export async function generateImage(
|
|
|
144
250
|
}
|
|
145
251
|
}
|
|
146
252
|
|
|
147
|
-
// Extract title from the text response and apply to images
|
|
148
253
|
const title = extractTitle(text);
|
|
149
254
|
if (title) {
|
|
150
255
|
for (const img of images) {
|
|
@@ -160,7 +265,6 @@ export async function generateImage(
|
|
|
160
265
|
return { ...result, resolvedModel: model };
|
|
161
266
|
}
|
|
162
267
|
|
|
163
|
-
// Parallel calls for multiple variants
|
|
164
268
|
const results = await Promise.all(
|
|
165
269
|
Array.from({ length: variants }, () => makeSingleCall()),
|
|
166
270
|
);
|
|
@@ -48,8 +48,8 @@ function formatBytes(bytes: number): string {
|
|
|
48
48
|
// Size and encoding limits
|
|
49
49
|
// ---------------------------------------------------------------------------
|
|
50
50
|
|
|
51
|
-
/** Hard ceiling on a single uploaded attachment (
|
|
52
|
-
export const MAX_UPLOAD_BYTES =
|
|
51
|
+
/** Hard ceiling on a single uploaded attachment (100 MB, matching assistant limits). */
|
|
52
|
+
export const MAX_UPLOAD_BYTES = 100 * 1024 * 1024;
|
|
53
53
|
|
|
54
54
|
/** Attachments larger than this are stored on disk instead of inline in SQLite. */
|
|
55
55
|
export const FILE_BACKED_THRESHOLD_BYTES = 5 * 1024 * 1024;
|
|
@@ -226,7 +226,7 @@ function computeContentHash(dataBase64: string): string {
|
|
|
226
226
|
/**
|
|
227
227
|
* Store a file-backed attachment by path reference, without reading the file
|
|
228
228
|
* into memory. This avoids OOM risk for large recordings that exceed the
|
|
229
|
-
* normal
|
|
229
|
+
* normal 100 MB upload limit.
|
|
230
230
|
*
|
|
231
231
|
* The file stays on disk; the attachment row stores an empty dataBase64 and
|
|
232
232
|
* records the on-disk path in a `file_path` column (added via DB migration
|
|
@@ -59,7 +59,7 @@ export interface VerificationSession {
|
|
|
59
59
|
challengeHash: string;
|
|
60
60
|
expiresAt: number;
|
|
61
61
|
status: SessionStatus;
|
|
62
|
-
|
|
62
|
+
sourceConversationId: string | null;
|
|
63
63
|
consumedByExternalUserId: string | null;
|
|
64
64
|
consumedByChatId: string | null;
|
|
65
65
|
// Outbound session: expected-identity binding
|
|
@@ -96,7 +96,7 @@ function rowToSession(
|
|
|
96
96
|
challengeHash: row.challengeHash,
|
|
97
97
|
expiresAt: row.expiresAt,
|
|
98
98
|
status: row.status as SessionStatus,
|
|
99
|
-
|
|
99
|
+
sourceConversationId: row.sourceConversationId,
|
|
100
100
|
consumedByExternalUserId: row.consumedByExternalUserId,
|
|
101
101
|
consumedByChatId: row.consumedByChatId,
|
|
102
102
|
expectedExternalUserId: row.expectedExternalUserId ?? null,
|
|
@@ -127,7 +127,7 @@ export function createInboundSession(params: {
|
|
|
127
127
|
channel: string;
|
|
128
128
|
challengeHash: string;
|
|
129
129
|
expiresAt: number;
|
|
130
|
-
|
|
130
|
+
sourceConversationId?: string;
|
|
131
131
|
}): VerificationSession {
|
|
132
132
|
const db = getDb();
|
|
133
133
|
const now = Date.now();
|
|
@@ -150,7 +150,7 @@ export function createInboundSession(params: {
|
|
|
150
150
|
challengeHash: params.challengeHash,
|
|
151
151
|
expiresAt: params.expiresAt,
|
|
152
152
|
status: "pending" as const,
|
|
153
|
-
|
|
153
|
+
sourceConversationId: params.sourceConversationId ?? null,
|
|
154
154
|
consumedByExternalUserId: null,
|
|
155
155
|
consumedByChatId: null,
|
|
156
156
|
expectedExternalUserId: null,
|
|
@@ -278,7 +278,7 @@ export function createVerificationSession(params: {
|
|
|
278
278
|
challengeHash: string;
|
|
279
279
|
expiresAt: number;
|
|
280
280
|
status: SessionStatus;
|
|
281
|
-
|
|
281
|
+
sourceConversationId?: string;
|
|
282
282
|
expectedExternalUserId?: string | null;
|
|
283
283
|
expectedChatId?: string | null;
|
|
284
284
|
expectedPhoneE164?: string | null;
|
|
@@ -313,7 +313,7 @@ export function createVerificationSession(params: {
|
|
|
313
313
|
challengeHash: params.challengeHash,
|
|
314
314
|
expiresAt: params.expiresAt,
|
|
315
315
|
status: params.status as string,
|
|
316
|
-
|
|
316
|
+
sourceConversationId: params.sourceConversationId ?? null,
|
|
317
317
|
consumedByExternalUserId: null,
|
|
318
318
|
consumedByChatId: null,
|
|
319
319
|
expectedExternalUserId: params.expectedExternalUserId ?? null,
|
|
@@ -12,8 +12,9 @@ import { createRowMapper } from "../util/row-mapper.js";
|
|
|
12
12
|
import { deleteOrphanAttachments } from "./attachments-store.js";
|
|
13
13
|
import { projectAssistantMessage } from "./conversation-attention-store.js";
|
|
14
14
|
import { ensureDisplayOrderMigration } from "./conversation-display-order-migration.js";
|
|
15
|
-
import { getDb, rawExec, rawGet, rawRun } from "./db.js";
|
|
15
|
+
import { getDb, rawAll, rawExec, rawGet, rawRun } from "./db.js";
|
|
16
16
|
import { indexMessageNow } from "./indexer.js";
|
|
17
|
+
import { enqueueMemoryJob } from "./jobs-store.js";
|
|
17
18
|
import {
|
|
18
19
|
channelInboundEvents,
|
|
19
20
|
conversations,
|
|
@@ -22,10 +23,12 @@ import {
|
|
|
22
23
|
memoryItems,
|
|
23
24
|
memoryItemSources,
|
|
24
25
|
memorySegments,
|
|
26
|
+
memorySummaries,
|
|
25
27
|
messageAttachments,
|
|
26
28
|
messages,
|
|
27
29
|
toolInvocations,
|
|
28
30
|
} from "./schema.js";
|
|
31
|
+
import { cancelPendingJobsForConversation } from "./task-memory-cleanup.js";
|
|
29
32
|
|
|
30
33
|
const log = getLogger("conversation-store");
|
|
31
34
|
|
|
@@ -357,6 +360,192 @@ export function deleteConversation(id: string): DeletedMemoryIds {
|
|
|
357
360
|
return result;
|
|
358
361
|
}
|
|
359
362
|
|
|
363
|
+
/**
|
|
364
|
+
* Wipe a conversation and revert all memory changes it caused.
|
|
365
|
+
*
|
|
366
|
+
* Extends `deleteConversation` with:
|
|
367
|
+
* - Cancelling pending memory jobs before deletion
|
|
368
|
+
* - Restoring memory items that were explicitly superseded by items from this conversation
|
|
369
|
+
* - Restoring orphaned subject-match superseded items after deletion
|
|
370
|
+
* - Deleting conversation-scoped memory summaries and their embeddings
|
|
371
|
+
* - Enqueuing `embed_item` jobs for all restored items
|
|
372
|
+
*/
|
|
373
|
+
export function wipeConversation(id: string): WipeConversationResult {
|
|
374
|
+
const db = getDb();
|
|
375
|
+
const unsupersededItemIds: string[] = [];
|
|
376
|
+
const deletedSummaryIds: string[] = [];
|
|
377
|
+
|
|
378
|
+
// Step A — Cancel pending memory jobs (before deleting messages, since
|
|
379
|
+
// the cancellation queries join on `messages`).
|
|
380
|
+
const cancelledJobCount = cancelPendingJobsForConversation(id);
|
|
381
|
+
|
|
382
|
+
// Step B — Un-supersede memory items with explicit `supersededBy` links.
|
|
383
|
+
// Find memory items whose `superseded_by` points to an item sourced
|
|
384
|
+
// exclusively from this conversation.
|
|
385
|
+
const explicitSuperseded = rawAll<{ oldItemId: string }>(
|
|
386
|
+
`SELECT DISTINCT mi_old.id AS oldItemId
|
|
387
|
+
FROM memory_items mi_old
|
|
388
|
+
JOIN memory_items mi_new ON mi_old.superseded_by = mi_new.id
|
|
389
|
+
WHERE mi_old.status = 'superseded'
|
|
390
|
+
AND mi_new.id IN (
|
|
391
|
+
SELECT mis.memory_item_id
|
|
392
|
+
FROM memory_item_sources mis
|
|
393
|
+
JOIN messages m ON m.id = mis.message_id
|
|
394
|
+
WHERE m.conversation_id = ?
|
|
395
|
+
)
|
|
396
|
+
AND NOT EXISTS (
|
|
397
|
+
SELECT 1 FROM memory_item_sources mis2
|
|
398
|
+
JOIN messages m2 ON m2.id = mis2.message_id
|
|
399
|
+
WHERE mis2.memory_item_id = mi_new.id
|
|
400
|
+
AND m2.conversation_id != ?
|
|
401
|
+
)
|
|
402
|
+
AND NOT EXISTS (
|
|
403
|
+
SELECT 1 FROM memory_items mi_active
|
|
404
|
+
WHERE mi_active.kind = mi_old.kind
|
|
405
|
+
AND mi_active.subject = mi_old.subject
|
|
406
|
+
AND mi_active.scope_id = mi_old.scope_id
|
|
407
|
+
AND mi_active.status = 'active'
|
|
408
|
+
AND mi_active.id != mi_old.id
|
|
409
|
+
-- Exclude items sourced exclusively from the conversation being
|
|
410
|
+
-- wiped — deleteConversation will remove them, so they should not
|
|
411
|
+
-- block restoration of mi_old.
|
|
412
|
+
AND NOT (
|
|
413
|
+
EXISTS (
|
|
414
|
+
SELECT 1 FROM memory_item_sources mis_a
|
|
415
|
+
JOIN messages m_a ON m_a.id = mis_a.message_id
|
|
416
|
+
WHERE mis_a.memory_item_id = mi_active.id
|
|
417
|
+
AND m_a.conversation_id = ?
|
|
418
|
+
)
|
|
419
|
+
AND NOT EXISTS (
|
|
420
|
+
SELECT 1 FROM memory_item_sources mis_b
|
|
421
|
+
JOIN messages m_b ON m_b.id = mis_b.message_id
|
|
422
|
+
WHERE mis_b.memory_item_id = mi_active.id
|
|
423
|
+
AND m_b.conversation_id != ?
|
|
424
|
+
)
|
|
425
|
+
)
|
|
426
|
+
)`,
|
|
427
|
+
id,
|
|
428
|
+
id,
|
|
429
|
+
id,
|
|
430
|
+
id,
|
|
431
|
+
);
|
|
432
|
+
for (const { oldItemId } of explicitSuperseded) {
|
|
433
|
+
rawRun(
|
|
434
|
+
"UPDATE memory_items SET status = 'active', superseded_by = NULL WHERE id = ?",
|
|
435
|
+
oldItemId,
|
|
436
|
+
);
|
|
437
|
+
enqueueMemoryJob("embed_item", { itemId: oldItemId });
|
|
438
|
+
unsupersededItemIds.push(oldItemId);
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// Step C — Delete conversation-scoped memory summaries and their embeddings.
|
|
442
|
+
const summaryRows = db
|
|
443
|
+
.select({ id: memorySummaries.id })
|
|
444
|
+
.from(memorySummaries)
|
|
445
|
+
.where(
|
|
446
|
+
and(
|
|
447
|
+
eq(memorySummaries.scope, "conversation"),
|
|
448
|
+
eq(memorySummaries.scopeKey, id),
|
|
449
|
+
),
|
|
450
|
+
)
|
|
451
|
+
.all();
|
|
452
|
+
const summaryIds = summaryRows.map((r) => r.id);
|
|
453
|
+
if (summaryIds.length > 0) {
|
|
454
|
+
db.delete(memoryEmbeddings)
|
|
455
|
+
.where(
|
|
456
|
+
and(
|
|
457
|
+
eq(memoryEmbeddings.targetType, "summary"),
|
|
458
|
+
inArray(memoryEmbeddings.targetId, summaryIds),
|
|
459
|
+
),
|
|
460
|
+
)
|
|
461
|
+
.run();
|
|
462
|
+
db.delete(memorySummaries)
|
|
463
|
+
.where(inArray(memorySummaries.id, summaryIds))
|
|
464
|
+
.run();
|
|
465
|
+
}
|
|
466
|
+
deletedSummaryIds.push(...summaryIds);
|
|
467
|
+
|
|
468
|
+
// Step D — Get the conversation's memoryScopeId before deletion.
|
|
469
|
+
const scopeId = getConversationMemoryScopeId(id);
|
|
470
|
+
|
|
471
|
+
// Step D.5 — Collect kind + subject pairs of items that will be orphaned
|
|
472
|
+
// by deleteConversation. These are items sourced from this conversation's
|
|
473
|
+
// messages that have NO sources from any other conversation. We need this
|
|
474
|
+
// before deletion so we can scope Step F to only restore superseded items
|
|
475
|
+
// matching the specific kind + subject pairs that just lost their active
|
|
476
|
+
// replacement.
|
|
477
|
+
const orphanedKindSubjects = rawAll<{ kind: string; subject: string }>(
|
|
478
|
+
`SELECT DISTINCT mi.kind, mi.subject
|
|
479
|
+
FROM memory_items mi
|
|
480
|
+
JOIN memory_item_sources mis ON mis.memory_item_id = mi.id
|
|
481
|
+
JOIN messages m ON m.id = mis.message_id
|
|
482
|
+
WHERE m.conversation_id = ?
|
|
483
|
+
AND NOT EXISTS (
|
|
484
|
+
SELECT 1 FROM memory_item_sources mis2
|
|
485
|
+
JOIN messages m2 ON m2.id = mis2.message_id
|
|
486
|
+
WHERE mis2.memory_item_id = mi.id
|
|
487
|
+
AND m2.conversation_id != ?
|
|
488
|
+
)`,
|
|
489
|
+
id,
|
|
490
|
+
id,
|
|
491
|
+
);
|
|
492
|
+
|
|
493
|
+
// Step E — Delegate to deleteConversation which handles messages (cascade
|
|
494
|
+
// segments, item_sources, attachments), llmRequestLogs, toolInvocations,
|
|
495
|
+
// orphaned memory items + embeddings, and the conversation row.
|
|
496
|
+
const deletedMemoryIds = deleteConversation(id);
|
|
497
|
+
|
|
498
|
+
// Step F — Restore orphaned subject-match superseded items. After
|
|
499
|
+
// deleteConversation removes superseding items, find superseded items
|
|
500
|
+
// with no supersededBy link where no active item with the same
|
|
501
|
+
// kind + subject + scope_id exists. Scoped to only the kind + subject
|
|
502
|
+
// pairs of items that were just orphaned by deleteConversation, so we
|
|
503
|
+
// don't accidentally restore items superseded by unrelated conversations.
|
|
504
|
+
let orphanedSuperseded: Array<{ id: string }> = [];
|
|
505
|
+
if (orphanedKindSubjects.length > 0) {
|
|
506
|
+
const placeholders = orphanedKindSubjects.map(() => "(?, ?)").join(", ");
|
|
507
|
+
const params: Array<string> = [scopeId];
|
|
508
|
+
for (const { kind, subject } of orphanedKindSubjects) {
|
|
509
|
+
params.push(kind, subject);
|
|
510
|
+
}
|
|
511
|
+
orphanedSuperseded = rawAll<{ id: string }>(
|
|
512
|
+
`SELECT id FROM (
|
|
513
|
+
SELECT id, ROW_NUMBER() OVER (
|
|
514
|
+
PARTITION BY kind, subject, scope_id
|
|
515
|
+
ORDER BY last_seen_at DESC
|
|
516
|
+
) AS rn
|
|
517
|
+
FROM memory_items
|
|
518
|
+
WHERE status = 'superseded'
|
|
519
|
+
AND superseded_by IS NULL
|
|
520
|
+
AND scope_id = ?
|
|
521
|
+
AND (kind, subject) IN (VALUES ${placeholders})
|
|
522
|
+
AND NOT EXISTS (
|
|
523
|
+
SELECT 1 FROM memory_items mi2
|
|
524
|
+
WHERE mi2.kind = memory_items.kind
|
|
525
|
+
AND mi2.subject = memory_items.subject
|
|
526
|
+
AND mi2.scope_id = memory_items.scope_id
|
|
527
|
+
AND mi2.status = 'active'
|
|
528
|
+
AND mi2.id != memory_items.id
|
|
529
|
+
)
|
|
530
|
+
) WHERE rn = 1`,
|
|
531
|
+
...params,
|
|
532
|
+
);
|
|
533
|
+
}
|
|
534
|
+
for (const { id: itemId } of orphanedSuperseded) {
|
|
535
|
+
rawRun("UPDATE memory_items SET status = 'active' WHERE id = ?", itemId);
|
|
536
|
+
enqueueMemoryJob("embed_item", { itemId });
|
|
537
|
+
unsupersededItemIds.push(itemId);
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// Step G — Return the combined result.
|
|
541
|
+
return {
|
|
542
|
+
...deletedMemoryIds,
|
|
543
|
+
unsupersededItemIds,
|
|
544
|
+
deletedSummaryIds,
|
|
545
|
+
cancelledJobCount,
|
|
546
|
+
};
|
|
547
|
+
}
|
|
548
|
+
|
|
360
549
|
/**
|
|
361
550
|
* Delete all private (temporary) conversations and their associated data.
|
|
362
551
|
* Called at daemon startup to clean up ephemeral conversations from previous sessions.
|
|
@@ -755,6 +944,12 @@ export interface DeletedMemoryIds {
|
|
|
755
944
|
orphanedItemIds: string[];
|
|
756
945
|
}
|
|
757
946
|
|
|
947
|
+
export interface WipeConversationResult extends DeletedMemoryIds {
|
|
948
|
+
unsupersededItemIds: string[];
|
|
949
|
+
deletedSummaryIds: string[];
|
|
950
|
+
cancelledJobCount: number;
|
|
951
|
+
}
|
|
952
|
+
|
|
758
953
|
/**
|
|
759
954
|
* Update the content of an existing message. Used when consolidating
|
|
760
955
|
* multiple assistant messages into one.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Cadence logic for
|
|
2
|
+
* Cadence logic for conversation starters generation.
|
|
3
3
|
*
|
|
4
|
-
* Decides whether new generation
|
|
4
|
+
* Decides whether a new generation job should be enqueued based on how many
|
|
5
5
|
* active memory items have accumulated since the last generation.
|
|
6
6
|
*/
|
|
7
7
|
|
|
@@ -9,18 +9,17 @@ import { and, eq, inArray, like } from "drizzle-orm";
|
|
|
9
9
|
|
|
10
10
|
import { getLogger } from "../util/logger.js";
|
|
11
11
|
import { getDb } from "./db.js";
|
|
12
|
-
import { CAPABILITY_CARD_CATEGORIES } from "./job-handlers/capability-cards.js";
|
|
13
12
|
import { enqueueMemoryJob } from "./jobs-store.js";
|
|
14
13
|
import { rawGet } from "./raw-query.js";
|
|
15
14
|
import { memoryCheckpoints, memoryJobs } from "./schema.js";
|
|
16
15
|
|
|
17
|
-
const log = getLogger("
|
|
16
|
+
const log = getLogger("conversation-starters-cadence");
|
|
18
17
|
|
|
19
18
|
/**
|
|
20
19
|
* Check whether enough new memory items have accumulated to justify
|
|
21
|
-
* generating a fresh batch of
|
|
20
|
+
* generating a fresh batch of conversation starters.
|
|
22
21
|
*/
|
|
23
|
-
export function
|
|
22
|
+
export function maybeEnqueueConversationStartersJob(scopeId: string): void {
|
|
24
23
|
const db = getDb();
|
|
25
24
|
|
|
26
25
|
// Count total active memory items
|
|
@@ -32,7 +31,7 @@ export function maybeEnqueueThreadStartersJob(scopeId: string): void {
|
|
|
32
31
|
if (totalActive === 0) return;
|
|
33
32
|
|
|
34
33
|
// Read checkpoint: item count at last generation (scoped so each scope tracks independently)
|
|
35
|
-
const checkpointKey = `
|
|
34
|
+
const checkpointKey = `conversation_starters:item_count_at_last_gen:${scopeId}`;
|
|
36
35
|
const checkpoint = db
|
|
37
36
|
.select({ value: memoryCheckpoints.value })
|
|
38
37
|
.from(memoryCheckpoints)
|
|
@@ -59,7 +58,7 @@ export function maybeEnqueueThreadStartersJob(scopeId: string): void {
|
|
|
59
58
|
.from(memoryJobs)
|
|
60
59
|
.where(
|
|
61
60
|
and(
|
|
62
|
-
eq(memoryJobs.type, "
|
|
61
|
+
eq(memoryJobs.type, "generate_conversation_starters"),
|
|
63
62
|
inArray(memoryJobs.status, ["pending", "running"]),
|
|
64
63
|
like(memoryJobs.payload, `%"scopeId":"${scopeId}"%`),
|
|
65
64
|
),
|
|
@@ -67,41 +66,9 @@ export function maybeEnqueueThreadStartersJob(scopeId: string): void {
|
|
|
67
66
|
.get();
|
|
68
67
|
if (existing) return;
|
|
69
68
|
|
|
70
|
-
enqueueMemoryJob("
|
|
69
|
+
enqueueMemoryJob("generate_conversation_starters", { scopeId });
|
|
71
70
|
log.info(
|
|
72
71
|
{ totalActive, lastCount, delta, threshold, scopeId },
|
|
73
|
-
"Enqueued
|
|
72
|
+
"Enqueued conversation starters generation job",
|
|
74
73
|
);
|
|
75
|
-
|
|
76
|
-
// Also enqueue capability card regeneration for all categories
|
|
77
|
-
maybeEnqueueCapabilityCardJobs(scopeId);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* Enqueue capability card generation jobs for all categories.
|
|
82
|
-
* Skips categories that already have pending/running jobs.
|
|
83
|
-
*/
|
|
84
|
-
function maybeEnqueueCapabilityCardJobs(scopeId: string): void {
|
|
85
|
-
const db = getDb();
|
|
86
|
-
|
|
87
|
-
for (const category of CAPABILITY_CARD_CATEGORIES) {
|
|
88
|
-
const existing = db
|
|
89
|
-
.select({ id: memoryJobs.id })
|
|
90
|
-
.from(memoryJobs)
|
|
91
|
-
.where(
|
|
92
|
-
and(
|
|
93
|
-
eq(memoryJobs.type, "generate_capability_cards"),
|
|
94
|
-
inArray(memoryJobs.status, ["pending", "running"]),
|
|
95
|
-
like(memoryJobs.payload, `%"scopeId":"${scopeId}"%`),
|
|
96
|
-
like(memoryJobs.payload, `%"category":"${category}"%`),
|
|
97
|
-
),
|
|
98
|
-
)
|
|
99
|
-
.get();
|
|
100
|
-
|
|
101
|
-
if (!existing) {
|
|
102
|
-
enqueueMemoryJob("generate_capability_cards", { scopeId, category });
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
log.info({ scopeId }, "Enqueued capability card generation jobs");
|
|
107
74
|
}
|