@vellumai/assistant 0.6.1 → 0.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bun.lock +40 -40
- package/bunfig.toml +3 -0
- package/docker-entrypoint.sh +12 -2
- package/docs/architecture/memory.md +1 -1
- package/node_modules/@vellumai/ces-contracts/src/handles.ts +7 -9
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +42 -0
- package/openapi.yaml +184 -69
- package/package.json +41 -41
- package/scripts/generate-openapi.ts +1 -2
- package/src/__tests__/acp-session.test.ts +43 -0
- package/src/__tests__/app-builder-tool-scripts.test.ts +1 -0
- package/src/__tests__/app-executors.test.ts +1 -0
- package/src/__tests__/app-source-watcher.test.ts +37 -11
- package/src/__tests__/approval-routes-http.test.ts +178 -1
- package/src/__tests__/assistant-event-hub.test.ts +30 -0
- package/src/__tests__/browser-fill-credential.test.ts +229 -94
- package/src/__tests__/browser-manager.test.ts +40 -27
- package/src/__tests__/catalog-files.test.ts +862 -0
- package/src/__tests__/channel-approvals.test.ts +53 -0
- package/src/__tests__/checker.test.ts +104 -170
- package/src/__tests__/cli-command-risk-guard.test.ts +1 -1
- package/src/__tests__/config-managed-gemini-defaults.test.ts +326 -0
- package/src/__tests__/config-schema-cmd.test.ts +2 -2
- package/src/__tests__/config-schema.test.ts +125 -48
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +23 -0
- package/src/__tests__/context-overflow-approval.test.ts +21 -6
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +1 -1
- package/src/__tests__/conversation-agent-loop.test.ts +1 -1
- package/src/__tests__/conversation-analysis-routes.test.ts +169 -0
- package/src/__tests__/conversation-attachments.test.ts +80 -4
- package/src/__tests__/conversation-confirmation-signals.test.ts +155 -0
- package/src/__tests__/conversation-directories-parse.test.ts +105 -0
- package/src/__tests__/conversation-fork-crud.test.ts +17 -0
- package/src/__tests__/conversation-history-web-search.test.ts +1 -0
- package/src/__tests__/conversation-host-access-routes.test.ts +229 -0
- package/src/__tests__/conversation-inject-context.test.ts +103 -0
- package/src/__tests__/conversation-queue.test.ts +45 -2
- package/src/__tests__/conversation-routes-disk-view.test.ts +5 -0
- package/src/__tests__/conversation-routes-guardian-reply.test.ts +16 -0
- package/src/__tests__/conversation-routes-slash-commands.test.ts +1 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +269 -46
- package/src/__tests__/conversation-starter-routes.test.ts +126 -0
- package/src/__tests__/conversation-starters-cadence.test.ts +161 -0
- package/src/__tests__/conversation-store.test.ts +195 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +193 -0
- package/src/__tests__/credential-execution-approval-bridge.test.ts +32 -3
- package/src/__tests__/credential-security-invariants.test.ts +1 -0
- package/src/__tests__/credential-vault-unit.test.ts +4 -4
- package/src/__tests__/credential-vault.test.ts +152 -13
- package/src/__tests__/credentials-cli.test.ts +2 -2
- package/src/__tests__/date-context.test.ts +4 -4
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +256 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +155 -0
- package/src/__tests__/fixtures/mock-chrome-extension.ts +375 -0
- package/src/__tests__/gateway-only-guard.test.ts +3 -0
- package/src/__tests__/gemini-provider.test.ts +2 -2
- package/src/__tests__/guardian-routing-invariants.test.ts +70 -2
- package/src/__tests__/headless-browser-interactions.test.ts +707 -371
- package/src/__tests__/headless-browser-navigate.test.ts +389 -47
- package/src/__tests__/headless-browser-read-tools.test.ts +266 -103
- package/src/__tests__/headless-browser-snapshot.test.ts +240 -77
- package/src/__tests__/host-bash-proxy.test.ts +150 -1
- package/src/__tests__/host-browser-e2e-cloud.test.ts +462 -0
- package/src/__tests__/host-browser-e2e-self-hosted-capability.test.ts +286 -0
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +374 -0
- package/src/__tests__/host-browser-event-routes.test.ts +350 -0
- package/src/__tests__/host-browser-proxy.test.ts +444 -0
- package/src/__tests__/host-browser-routes.test.ts +198 -0
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +320 -0
- package/src/__tests__/host-cu-proxy.test.ts +171 -1
- package/src/__tests__/host-file-proxy.test.ts +185 -1
- package/src/__tests__/host-file-read-tool.test.ts +52 -0
- package/src/__tests__/host-proxy-interface.test.ts +165 -0
- package/src/__tests__/host-shell-tool.test.ts +1 -11
- package/src/__tests__/http-user-message-parity.test.ts +1 -0
- package/src/__tests__/init-feature-flag-overrides.test.ts +167 -0
- package/src/__tests__/inline-command-runner.test.ts +7 -5
- package/src/__tests__/integration-status.test.ts +6 -7
- package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
- package/src/__tests__/log-export-workspace.test.ts +190 -0
- package/src/__tests__/managed-credential-catalog-cli.test.ts +12 -14
- package/src/__tests__/mcp-client-auth.test.ts +40 -4
- package/src/__tests__/mcp-health-check.test.ts +10 -3
- package/src/__tests__/migration-cross-version-compatibility.test.ts +3 -1
- package/src/__tests__/migration-export-http.test.ts +61 -2
- package/src/__tests__/migration-export-streaming.test.ts +66 -0
- package/src/__tests__/migration-import-commit-http.test.ts +101 -1
- package/src/__tests__/native-host-marker-sync-guard.test.ts +157 -0
- package/src/__tests__/navigate-settings-tab.test.ts +14 -1
- package/src/__tests__/notification-broadcaster.test.ts +65 -0
- package/src/__tests__/oauth-apps-routes.test.ts +17 -12
- package/src/__tests__/oauth-cli.test.ts +707 -60
- package/src/__tests__/oauth-connect-orchestrator.test.ts +116 -24
- package/src/__tests__/oauth-provider-seed-logos.test.ts +23 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +146 -10
- package/src/__tests__/oauth-provider-visibility.test.ts +19 -21
- package/src/__tests__/oauth-providers-routes.test.ts +50 -14
- package/src/__tests__/oauth-store.test.ts +1386 -182
- package/src/__tests__/oauth2-gateway-transport.test.ts +211 -20
- package/src/__tests__/onboarding-template-contract.test.ts +74 -55
- package/src/__tests__/openai-provider.test.ts +2 -2
- package/src/__tests__/outlook-categories.test.ts +1 -1
- package/src/__tests__/outlook-client-automation.test.ts +1 -1
- package/src/__tests__/outlook-compose-tools.test.ts +1 -1
- package/src/__tests__/outlook-email-watcher.test.ts +1 -1
- package/src/__tests__/outlook-follow-up.test.ts +1 -1
- package/src/__tests__/outlook-messaging-provider.test.ts +2 -2
- package/src/__tests__/outlook-trash.test.ts +1 -1
- package/src/__tests__/outlook-unsubscribe.test.ts +1 -1
- package/src/__tests__/permission-checker-host-gate.test.ts +74 -14
- package/src/__tests__/permission-mode.test.ts +28 -56
- package/src/__tests__/pkb-autoinject.test.ts +96 -0
- package/src/__tests__/platform-callback-registration.test.ts +19 -0
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +296 -0
- package/src/__tests__/proxy-approval-callback.test.ts +18 -0
- package/src/__tests__/require-fresh-approval.test.ts +40 -3
- package/src/__tests__/sandbox-diagnostics.test.ts +1 -32
- package/src/__tests__/sanitize-config-for-transfer.test.ts +132 -0
- package/src/__tests__/schedule-routes.test.ts +162 -0
- package/src/__tests__/secret-detection-handler.test.ts +84 -0
- package/src/__tests__/secret-ingress-http.test.ts +1 -0
- package/src/__tests__/send-endpoint-busy.test.ts +3 -0
- package/src/__tests__/set-permission-mode.test.ts +13 -250
- package/src/__tests__/skills-file-content-endpoint.test.ts +670 -0
- package/src/__tests__/skills-files-catalog-fallback.test.ts +450 -0
- package/src/__tests__/slack-channel-config.test.ts +12 -15
- package/src/__tests__/subagent-detail.test.ts +44 -2
- package/src/__tests__/subagent-disposal.test.ts +1 -0
- package/src/__tests__/subagent-fork-notifications.test.ts +291 -0
- package/src/__tests__/subagent-fork-spawn.test.ts +384 -0
- package/src/__tests__/subagent-manager-notify.test.ts +1 -0
- package/src/__tests__/subagent-notify-parent.test.ts +1 -0
- package/src/__tests__/subagent-spawn-tool-fork.test.ts +411 -0
- package/src/__tests__/subagent-tools.test.ts +1 -0
- package/src/__tests__/subagent-types.test.ts +1 -0
- package/src/__tests__/system-prompt-ask-mode.test.ts +27 -71
- package/src/__tests__/system-prompt.test.ts +72 -1
- package/src/__tests__/task-scheduler.test.ts +32 -6
- package/src/__tests__/telegram-config.test.ts +10 -13
- package/src/__tests__/terminal-sandbox.test.ts +1 -1
- package/src/__tests__/terminal-tools.test.ts +11 -5
- package/src/__tests__/test-preload.ts +14 -0
- package/src/__tests__/tool-approval-handler.test.ts +73 -0
- package/src/__tests__/tool-domain-event-publisher.test.ts +0 -1
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +1 -8
- package/src/__tests__/tool-executor.test.ts +0 -1
- package/src/__tests__/tool-side-effects-slack-dm.test.ts +22 -0
- package/src/__tests__/top-level-renderer.test.ts +73 -1
- package/src/__tests__/transport-hints-queue.test.ts +62 -0
- package/src/__tests__/trust-store.test.ts +4 -4
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
- package/src/__tests__/v2-consent-policy.test.ts +103 -0
- package/src/__tests__/workspace-migration-030-seed-pkb-autoinject.test.ts +168 -0
- package/src/__tests__/workspace-policy.test.ts +2 -7
- package/src/acp/client-handler.ts +30 -4
- package/src/agent/loop.ts +12 -35
- package/src/approvals/guardian-request-resolvers.ts +21 -15
- package/src/browser-session/__tests__/manager.test.ts +297 -0
- package/src/browser-session/backends/cdp-inspect.ts +30 -0
- package/src/browser-session/backends/extension.ts +26 -0
- package/src/browser-session/backends/local.ts +24 -0
- package/src/browser-session/events.ts +164 -0
- package/src/browser-session/index.ts +27 -0
- package/src/browser-session/manager.ts +159 -0
- package/src/browser-session/types.ts +28 -0
- package/src/channels/__tests__/types.test.ts +134 -0
- package/src/channels/types.ts +55 -0
- package/src/cli/__tests__/run-assistant-command.ts +34 -7
- package/src/cli/__tests__/unknown-command.test.ts +33 -0
- package/src/cli/commands/browser-relay.ts +339 -409
- package/src/cli/commands/credentials.ts +3 -3
- package/src/cli/commands/default-action.ts +68 -1
- package/src/cli/commands/email.ts +18 -13
- package/src/cli/commands/mcp.ts +16 -4
- package/src/cli/commands/oauth/__tests__/connect.test.ts +68 -41
- package/src/cli/commands/oauth/__tests__/disconnect.test.ts +21 -21
- package/src/cli/commands/oauth/__tests__/mode.test.ts +17 -17
- package/src/cli/commands/oauth/__tests__/ping.test.ts +16 -16
- package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +31 -33
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +329 -0
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +116 -12
- package/src/cli/commands/oauth/__tests__/status.test.ts +10 -10
- package/src/cli/commands/oauth/__tests__/token.test.ts +7 -7
- package/src/cli/commands/oauth/apps.ts +7 -4
- package/src/cli/commands/oauth/connect.ts +16 -2
- package/src/cli/commands/oauth/disconnect.ts +1 -1
- package/src/cli/commands/oauth/providers.ts +200 -36
- package/src/cli/commands/oauth/shared.ts +5 -5
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +259 -0
- package/src/cli/commands/platform/__tests__/connect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +1 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +1 -1
- package/src/cli/commands/platform/index.ts +107 -10
- package/src/cli/commands/usage.ts +10 -9
- package/src/cli/lib/daemon-credential-client.ts +4 -0
- package/src/cli/program.ts +10 -3
- package/src/config/assistant-feature-flags.ts +59 -55
- package/src/config/bundled-skills/app-builder/SKILL.md +33 -173
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +105 -0
- package/src/config/bundled-skills/app-builder/references/INTERACTION_HOOKS.md +56 -0
- package/src/config/bundled-skills/app-builder/references/WIDGETS.md +125 -0
- package/src/config/bundled-skills/contacts/SKILL.md +3 -0
- package/src/config/bundled-skills/document/SKILL.md +4 -0
- package/src/config/bundled-skills/gmail/SKILL.md +12 -7
- package/src/config/bundled-skills/gmail/TOOLS.json +1 -1
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +2 -1
- package/src/config/bundled-skills/outlook/SKILL.md +7 -0
- package/src/config/bundled-skills/settings/TOOLS.json +1 -1
- package/src/config/bundled-skills/settings/tools/navigate-settings-tab.ts +8 -3
- package/src/config/bundled-skills/subagent/SKILL.md +21 -0
- package/src/config/bundled-skills/subagent/TOOLS.json +8 -4
- package/src/config/bundled-skills/tasks/SKILL.md +5 -0
- package/src/config/env-registry.ts +14 -0
- package/src/config/env.ts +21 -0
- package/src/config/feature-flag-registry.json +46 -7
- package/src/config/loader.ts +56 -1
- package/src/config/sanitize-for-transfer.ts +47 -0
- package/src/config/schema.ts +46 -5
- package/src/config/schemas/host-browser.ts +66 -0
- package/src/config/schemas/memory-lifecycle.ts +1 -1
- package/src/config/schemas/memory-retrieval.ts +103 -0
- package/src/config/schemas/security.ts +0 -6
- package/src/config/schemas/services.ts +16 -0
- package/src/config/types.ts +0 -1
- package/src/context/post-turn-tool-result-truncation.ts +176 -0
- package/src/context/window-manager.ts +19 -1
- package/src/credential-execution/approval-bridge.ts +49 -16
- package/src/credential-execution/managed-catalog.ts +3 -7
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +186 -0
- package/src/daemon/app-source-watcher.ts +35 -0
- package/src/daemon/config-watcher.ts +6 -2
- package/src/daemon/context-overflow-approval.ts +5 -1
- package/src/daemon/conversation-agent-loop-handlers.ts +17 -2
- package/src/daemon/conversation-agent-loop.ts +74 -19
- package/src/daemon/conversation-attachments.ts +40 -1
- package/src/daemon/conversation-messaging.ts +3 -0
- package/src/daemon/conversation-process.ts +66 -3
- package/src/daemon/conversation-queue-manager.ts +8 -0
- package/src/daemon/conversation-runtime-assembly.ts +159 -20
- package/src/daemon/conversation-surfaces.ts +78 -12
- package/src/daemon/conversation-tool-setup.ts +74 -11
- package/src/daemon/conversation-workspace.ts +12 -0
- package/src/daemon/conversation.ts +227 -11
- package/src/daemon/date-context.ts +10 -10
- package/src/daemon/first-greeting.ts +3 -2
- package/src/daemon/handlers/conversations.ts +9 -139
- package/src/daemon/handlers/shared.ts +65 -0
- package/src/daemon/handlers/skills.ts +232 -37
- package/src/daemon/host-bash-proxy.ts +48 -13
- package/src/daemon/host-browser-proxy.ts +191 -0
- package/src/daemon/host-cu-proxy.ts +36 -11
- package/src/daemon/host-file-proxy.ts +57 -9
- package/src/daemon/lifecycle.ts +86 -12
- package/src/daemon/message-protocol.ts +7 -0
- package/src/daemon/message-types/conversations.ts +59 -13
- package/src/daemon/message-types/host-browser.ts +100 -0
- package/src/daemon/message-types/messages.ts +5 -6
- package/src/daemon/message-types/notifications.ts +12 -0
- package/src/daemon/message-types/settings.ts +12 -0
- package/src/daemon/message-types/skills.ts +10 -0
- package/src/daemon/message-types/subagents.ts +2 -0
- package/src/daemon/server.ts +112 -35
- package/src/daemon/tool-side-effects.ts +6 -0
- package/src/daemon/transport-hints.ts +14 -0
- package/src/inbound/platform-callback-registration.ts +18 -17
- package/src/index.ts +1 -1
- package/src/mcp/client.ts +59 -24
- package/src/memory/app-store.ts +31 -1
- package/src/memory/conversation-crud.ts +38 -10
- package/src/memory/conversation-directories.ts +39 -0
- package/src/memory/conversation-group-migration.ts +65 -5
- package/src/memory/conversation-starters-cadence.ts +76 -0
- package/src/memory/conversation-title-service.ts +5 -2
- package/src/memory/db-init.ts +12 -0
- package/src/memory/embedding-backend.test.ts +75 -0
- package/src/memory/embedding-backend.ts +131 -5
- package/src/memory/embedding-gemini.test.ts +54 -0
- package/src/memory/embedding-gemini.ts +20 -9
- package/src/memory/embedding-local.ts +177 -18
- package/src/memory/graph/capability-seed.ts +3 -5
- package/src/memory/graph/consolidation.ts +10 -23
- package/src/memory/graph/extraction-job.ts +15 -0
- package/src/memory/graph/retriever.ts +40 -22
- package/src/memory/graph/store.test.ts +7 -3
- package/src/memory/graph/store.ts +47 -12
- package/src/memory/group-crud.ts +25 -9
- package/src/memory/llm-usage-store.ts +45 -4
- package/src/memory/migrations/213-oauth-providers-scope-separator.ts +13 -0
- package/src/memory/migrations/214-oauth-providers-refresh-url.ts +11 -0
- package/src/memory/migrations/215-oauth-providers-revoke.ts +14 -0
- package/src/memory/migrations/216-oauth-providers-token-auth-method.ts +30 -0
- package/src/memory/migrations/217-conversation-host-access.ts +40 -0
- package/src/memory/migrations/218-oauth-providers-logo-url.ts +11 -0
- package/src/memory/migrations/index.ts +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/schema/conversations.ts +1 -0
- package/src/memory/schema/oauth.ts +18 -13
- package/src/messaging/provider.ts +1 -1
- package/src/notifications/broadcaster.ts +6 -0
- package/src/notifications/conversation-pairing.ts +12 -4
- package/src/notifications/emit-signal.ts +14 -0
- package/src/notifications/signal.ts +11 -0
- package/src/oauth/AGENTS.md +76 -0
- package/src/oauth/__tests__/identity-verifier.test.ts +24 -19
- package/src/oauth/__tests__/seed-providers-managed.test.ts +32 -0
- package/src/oauth/byo-connection.test.ts +8 -8
- package/src/oauth/byo-connection.ts +7 -7
- package/src/oauth/connect-orchestrator.ts +23 -21
- package/src/oauth/connect-types.ts +3 -3
- package/src/oauth/connection-resolver.test.ts +17 -4
- package/src/oauth/connection-resolver.ts +16 -16
- package/src/oauth/connection.ts +1 -1
- package/src/oauth/manual-token-connection.ts +13 -13
- package/src/oauth/oauth-store.ts +214 -100
- package/src/oauth/platform-connection.test.ts +5 -5
- package/src/oauth/platform-connection.ts +4 -4
- package/src/oauth/provider-serializer.ts +31 -5
- package/src/oauth/revoke.ts +76 -0
- package/src/oauth/seed-providers.ts +127 -87
- package/src/oauth/token-persistence.ts +1 -1
- package/src/permissions/checker.ts +3 -3
- package/src/permissions/defaults.ts +7 -8
- package/src/permissions/permission-mode.ts +4 -11
- package/src/permissions/prompter.ts +13 -3
- package/src/permissions/v2-consent-policy.ts +87 -0
- package/src/platform/client.ts +1 -1
- package/src/prompts/system-prompt.ts +18 -21
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
- package/src/prompts/templates/BOOTSTRAP.md +59 -96
- package/src/prompts/templates/SOUL.md +11 -11
- package/src/providers/anthropic/client.ts +1 -0
- package/src/providers/types.ts +1 -1
- package/src/runtime/AGENTS.md +23 -0
- package/src/runtime/__tests__/browser-extension-pair-routes.test.ts +715 -0
- package/src/runtime/__tests__/capability-tokens.test.ts +258 -0
- package/src/runtime/__tests__/chrome-extension-registry.test.ts +518 -0
- package/src/runtime/assistant-event-hub.ts +24 -2
- package/src/runtime/auth/__tests__/guard-tests.test.ts +1 -0
- package/src/runtime/auth/__tests__/middleware.test.ts +116 -1
- package/src/runtime/auth/__tests__/route-policy.test.ts +8 -0
- package/src/runtime/auth/middleware.ts +98 -0
- package/src/runtime/auth/route-policy.ts +6 -7
- package/src/runtime/auth/token-service.ts +8 -0
- package/src/runtime/capability-tokens.ts +414 -0
- package/src/runtime/channel-approvals.ts +18 -5
- package/src/runtime/chrome-extension-registry.ts +332 -0
- package/src/runtime/confirmation-request-guardian-bridge.ts +6 -0
- package/src/runtime/guardian-decision-types.ts +7 -0
- package/src/runtime/http-server.ts +425 -70
- package/src/runtime/migrations/__tests__/rebind-secrets-credentials.test.ts +172 -0
- package/src/runtime/migrations/__tests__/vbundle-builder-credentials.test.ts +276 -0
- package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +162 -0
- package/src/runtime/migrations/migration-transport.ts +6 -0
- package/src/runtime/migrations/migration-wizard.ts +22 -2
- package/src/runtime/migrations/rebind-secrets-screen.ts +76 -15
- package/src/runtime/migrations/vbundle-builder.ts +145 -38
- package/src/runtime/migrations/vbundle-import-analyzer.ts +19 -0
- package/src/runtime/migrations/vbundle-importer.ts +55 -5
- package/src/runtime/pending-interactions.ts +29 -13
- package/src/runtime/routes/approval-routes.ts +90 -16
- package/src/runtime/routes/browser-cdp-routes.ts +229 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +497 -0
- package/src/runtime/routes/conversation-analysis-routes.ts +18 -5
- package/src/runtime/routes/conversation-management-routes.ts +108 -0
- package/src/runtime/routes/conversation-routes.ts +308 -28
- package/src/runtime/routes/conversation-starter-routes.ts +78 -16
- package/src/runtime/routes/group-routes.ts +22 -8
- package/src/runtime/routes/guardian-action-routes.ts +24 -13
- package/src/runtime/routes/host-browser-routes.ts +279 -0
- package/src/runtime/routes/host-file-routes.ts +9 -1
- package/src/runtime/routes/identity-routes.ts +259 -16
- package/src/runtime/routes/log-export/AGENTS.md +104 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist-error-contract.test.ts +103 -0
- package/src/runtime/routes/log-export/__tests__/workspace-allowlist.test.ts +716 -0
- package/src/runtime/routes/log-export/workspace-allowlist.ts +458 -0
- package/src/runtime/routes/log-export-routes.ts +60 -25
- package/src/runtime/routes/memory-item-routes.ts +1 -7
- package/src/runtime/routes/migration-routes.ts +87 -2
- package/src/runtime/routes/oauth-apps.ts +15 -17
- package/src/runtime/routes/oauth-providers.ts +4 -0
- package/src/runtime/routes/schedule-routes.ts +24 -11
- package/src/runtime/routes/settings-routes.ts +9 -97
- package/src/runtime/routes/skills-routes.ts +52 -2
- package/src/runtime/routes/subagents-routes.ts +14 -10
- package/src/runtime/routes/usage-routes.ts +8 -7
- package/src/runtime/routes/workspace-routes.test.ts +22 -0
- package/src/runtime/routes/workspace-routes.ts +8 -1
- package/src/runtime/routes/workspace-utils.ts +2 -0
- package/src/schedule/scheduler.ts +7 -5
- package/src/security/ces-credential-client.ts +20 -0
- package/src/security/ces-rpc-credential-backend.ts +17 -0
- package/src/security/credential-backend.ts +5 -0
- package/src/security/oauth2.ts +42 -25
- package/src/security/secure-keys.ts +118 -25
- package/src/security/token-manager.ts +23 -10
- package/src/skills/catalog-files.ts +492 -0
- package/src/skills/inline-command-runner.ts +12 -14
- package/src/subagent/manager.ts +131 -26
- package/src/subagent/types.ts +19 -0
- package/src/tools/apps/executors.ts +11 -2
- package/src/tools/browser/__tests__/auth-detector.test.ts +202 -108
- package/src/tools/browser/auth-detector.ts +43 -12
- package/src/tools/browser/browser-execution.ts +645 -340
- package/src/tools/browser/browser-manager.ts +36 -12
- package/src/tools/browser/cdp-client/__tests__/accessibility-snapshot.test.ts +318 -0
- package/src/tools/browser/cdp-client/__tests__/cdp-dom-helpers.test.ts +1175 -0
- package/src/tools/browser/cdp-client/__tests__/cdp-inspect-client.test.ts +870 -0
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +330 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +377 -0
- package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-nested-frames.json +64 -0
- package/src/tools/browser/cdp-client/__tests__/fixtures/ax-tree-simple.json +69 -0
- package/src/tools/browser/cdp-client/__tests__/local-cdp-client.test.ts +310 -0
- package/src/tools/browser/cdp-client/__tests__/types.test.ts +96 -0
- package/src/tools/browser/cdp-client/accessibility-snapshot.ts +387 -0
- package/src/tools/browser/cdp-client/cdp-dom-helpers.ts +695 -0
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +743 -0
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/ws-transport.test.ts +580 -0
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +578 -0
- package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +579 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +635 -0
- package/src/tools/browser/cdp-client/errors.ts +34 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +125 -0
- package/src/tools/browser/cdp-client/factory.ts +204 -0
- package/src/tools/browser/cdp-client/index.ts +14 -0
- package/src/tools/browser/cdp-client/local-cdp-client.ts +187 -0
- package/src/tools/browser/cdp-client/types.ts +52 -0
- package/src/tools/filesystem/edit.ts +1 -1
- package/src/tools/filesystem/list.ts +1 -1
- package/src/tools/filesystem/read.ts +1 -1
- package/src/tools/filesystem/write.ts +2 -1
- package/src/tools/host-filesystem/edit.ts +1 -1
- package/src/tools/host-filesystem/read.ts +12 -15
- package/src/tools/host-filesystem/write.ts +1 -1
- package/src/tools/host-terminal/host-shell.ts +21 -16
- package/src/tools/permission-checker.ts +77 -100
- package/src/tools/registry.ts +0 -2
- package/src/tools/secret-detection-handler.ts +34 -1
- package/src/tools/shared/filesystem/image-read.ts +61 -40
- package/src/tools/skills/sandbox-runner.ts +3 -6
- package/src/tools/subagent/spawn.ts +47 -3
- package/src/tools/subagent/status.ts +2 -0
- package/src/tools/system/register.ts +2 -16
- package/src/tools/terminal/safe-env.ts +7 -0
- package/src/tools/terminal/sandbox-diagnostics.ts +4 -4
- package/src/tools/terminal/sandbox.ts +4 -1
- package/src/tools/terminal/shell.ts +24 -21
- package/src/tools/tool-approval-handler.ts +48 -2
- package/src/tools/types.ts +2 -3
- package/src/util/platform.ts +14 -19
- package/src/watcher/provider-types.ts +1 -1
- package/src/workspace/migrations/029-seed-pkb.ts +1 -0
- package/src/workspace/migrations/030-seed-pkb-autoinject.ts +73 -0
- package/src/workspace/migrations/registry.ts +2 -0
- package/src/workspace/top-level-renderer.ts +19 -1
- package/src/__tests__/chrome-cdp.test.ts +0 -419
- package/src/__tests__/permission-mode-sse.test.ts +0 -418
- package/src/__tests__/permission-mode-store.test.ts +0 -277
- package/src/browser-extension-relay/protocol.ts +0 -63
- package/src/browser-extension-relay/server.ts +0 -203
- package/src/config/schemas/sandbox.ts +0 -14
- package/src/permissions/permission-mode-store.ts +0 -180
- package/src/tools/browser/chrome-cdp.ts +0 -239
- package/src/tools/system/set-permission-mode.ts +0 -103
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Platform callback route registration
|
|
2
|
+
* Platform callback route registration.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* with
|
|
6
|
-
*
|
|
7
|
-
* must route through the platform's gateway proxy instead of hitting the
|
|
8
|
-
* assistant directly.
|
|
4
|
+
* Both platform-managed (IS_PLATFORM=true) and self-hosted assistants can
|
|
5
|
+
* register callback routes with the platform so inbound provider webhooks
|
|
6
|
+
* (Telegram, Twilio, email, OAuth) are forwarded correctly.
|
|
9
7
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
8
|
+
* Platform-managed assistants pick up context from environment variables.
|
|
9
|
+
* Self-hosted assistants use stored credentials (from `assistant platform
|
|
10
|
+
* connect` or the ensure-registration bootstrap).
|
|
13
11
|
*
|
|
14
12
|
* The platform endpoint is:
|
|
15
13
|
* POST {VELLUM_PLATFORM_URL}/v1/internal/gateway/callback-routes/register/
|
|
@@ -41,17 +39,18 @@ export interface PlatformCallbackRegistrationContext {
|
|
|
41
39
|
}
|
|
42
40
|
|
|
43
41
|
/**
|
|
44
|
-
* Whether the
|
|
42
|
+
* Whether the **runtime** should automatically register callback routes.
|
|
45
43
|
* True when IS_PLATFORM, VELLUM_PLATFORM_URL, and PLATFORM_ASSISTANT_ID
|
|
46
|
-
* are all set
|
|
47
|
-
*
|
|
48
|
-
*
|
|
44
|
+
* are all set — i.e. this is a platform-managed deployment.
|
|
45
|
+
*
|
|
46
|
+
* This is intentionally stricter than `context.enabled` (which also covers
|
|
47
|
+
* self-hosted assistants with stored credentials). Runtime auto-registration
|
|
48
|
+
* only applies to managed deployments; self-hosted assistants register
|
|
49
|
+
* explicitly via the CLI or gateway startup hooks.
|
|
49
50
|
*/
|
|
50
51
|
export function shouldUsePlatformCallbacks(): boolean {
|
|
51
52
|
return (
|
|
52
|
-
getIsPlatform() &&
|
|
53
|
-
!!getPlatformBaseUrl() &&
|
|
54
|
-
!!getPlatformAssistantId()
|
|
53
|
+
getIsPlatform() && !!getPlatformBaseUrl() && !!getPlatformAssistantId()
|
|
55
54
|
);
|
|
56
55
|
}
|
|
57
56
|
|
|
@@ -86,8 +85,10 @@ export async function resolvePlatformCallbackRegistrationContext(): Promise<Plat
|
|
|
86
85
|
hasInternalApiKey: internalApiKey.length > 0,
|
|
87
86
|
hasAssistantApiKey: assistantApiKey.length > 0,
|
|
88
87
|
authHeader,
|
|
88
|
+
// Enabled when we have enough context to register callback routes.
|
|
89
|
+
// Does NOT require IS_PLATFORM — self-hosted assistants with stored
|
|
90
|
+
// credentials can also register routes.
|
|
89
91
|
enabled:
|
|
90
|
-
platform &&
|
|
91
92
|
platformBaseUrl.length > 0 &&
|
|
92
93
|
assistantId.length > 0 &&
|
|
93
94
|
authHeader !== null,
|
package/src/index.ts
CHANGED
package/src/mcp/client.ts
CHANGED
|
@@ -35,6 +35,12 @@ export class McpClient {
|
|
|
35
35
|
| null = null;
|
|
36
36
|
private connected = false;
|
|
37
37
|
private oauthProvider: McpOAuthProvider | null = null;
|
|
38
|
+
private _lastError: Error | null = null;
|
|
39
|
+
|
|
40
|
+
/** The last connection error, if any. Null when connected or not yet attempted. */
|
|
41
|
+
get lastError(): Error | null {
|
|
42
|
+
return this._lastError;
|
|
43
|
+
}
|
|
38
44
|
|
|
39
45
|
get isConnected(): boolean {
|
|
40
46
|
return this.connected;
|
|
@@ -46,6 +52,16 @@ export class McpClient {
|
|
|
46
52
|
name: "vellum-assistant",
|
|
47
53
|
version: "1.0.0",
|
|
48
54
|
});
|
|
55
|
+
|
|
56
|
+
// Prevent SDK-internal transport errors (e.g. SSE reconnection auth
|
|
57
|
+
// failures) from surfacing as unhandled rejections that crash the daemon
|
|
58
|
+
// via the global unhandledRejection → shutdown handler.
|
|
59
|
+
this.client.onerror = (error) => {
|
|
60
|
+
log.warn(
|
|
61
|
+
{ serverId: this.serverId, err: error },
|
|
62
|
+
"MCP SDK transport error (non-fatal)",
|
|
63
|
+
);
|
|
64
|
+
};
|
|
49
65
|
}
|
|
50
66
|
|
|
51
67
|
async connect(transportConfig: McpTransport): Promise<void> {
|
|
@@ -102,34 +118,24 @@ export class McpClient {
|
|
|
102
118
|
}
|
|
103
119
|
this.transport = null;
|
|
104
120
|
|
|
105
|
-
if (isHttpTransport) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
(err instanceof Error &&
|
|
109
|
-
/\b(401|403|unauthorized|forbidden)\b/i.test(err.message)) ||
|
|
110
|
-
(err != null &&
|
|
111
|
-
typeof err === "object" &&
|
|
112
|
-
"code" in err &&
|
|
113
|
-
(err.code === 401 || err.code === 403));
|
|
114
|
-
|
|
115
|
-
if (isAuthError) {
|
|
116
|
-
// Auth-related — user can run `assistant mcp auth <name>` to authenticate.
|
|
117
|
-
log.info(
|
|
118
|
-
{ serverId: this.serverId, err },
|
|
119
|
-
"MCP server requires authentication",
|
|
120
|
-
);
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// Non-auth error (DNS, TLS, timeout, etc.) — log and re-throw
|
|
125
|
-
log.error(
|
|
121
|
+
if (isHttpTransport && isAuthRelatedError(err)) {
|
|
122
|
+
// Auth-related — user can run `assistant mcp auth <name>` to authenticate.
|
|
123
|
+
log.info(
|
|
126
124
|
{ serverId: this.serverId, err },
|
|
127
|
-
"MCP server
|
|
125
|
+
"MCP server requires authentication",
|
|
128
126
|
);
|
|
129
|
-
|
|
127
|
+
return;
|
|
130
128
|
}
|
|
131
129
|
|
|
132
|
-
|
|
130
|
+
// Non-auth error (DNS, TLS, timeout, etc.) — log but never propagate
|
|
131
|
+
// an MCP connection failure to the caller. The daemon must keep
|
|
132
|
+
// running even when individual MCP servers are unreachable.
|
|
133
|
+
this._lastError = err instanceof Error ? err : new Error(String(err));
|
|
134
|
+
log.error(
|
|
135
|
+
{ serverId: this.serverId, err },
|
|
136
|
+
"MCP server connection failed",
|
|
137
|
+
);
|
|
138
|
+
return;
|
|
133
139
|
}
|
|
134
140
|
|
|
135
141
|
this.connected = true;
|
|
@@ -258,3 +264,32 @@ export class McpClient {
|
|
|
258
264
|
}
|
|
259
265
|
}
|
|
260
266
|
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Returns true when `err` looks like an authentication / authorization failure
|
|
270
|
+
* from the MCP SDK or the remote server. Used to distinguish "needs auth"
|
|
271
|
+
* from genuine transport failures so we can log guidance instead of crashing.
|
|
272
|
+
*/
|
|
273
|
+
function isAuthRelatedError(err: unknown): boolean {
|
|
274
|
+
if (err instanceof UnauthorizedError) return true;
|
|
275
|
+
|
|
276
|
+
if (
|
|
277
|
+
err instanceof Error &&
|
|
278
|
+
/\b(401|403|unauthorized|forbidden|authorizationCode is required|prepareTokenRequest)\b/i.test(
|
|
279
|
+
err.message,
|
|
280
|
+
)
|
|
281
|
+
) {
|
|
282
|
+
return true;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (
|
|
286
|
+
err != null &&
|
|
287
|
+
typeof err === "object" &&
|
|
288
|
+
"code" in err &&
|
|
289
|
+
(err.code === 401 || err.code === 403)
|
|
290
|
+
) {
|
|
291
|
+
return true;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return false;
|
|
295
|
+
}
|
package/src/memory/app-store.ts
CHANGED
|
@@ -64,6 +64,23 @@ export function isMultifileApp(app: AppDefinition): boolean {
|
|
|
64
64
|
return app.formatVersion === 2;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
/**
|
|
68
|
+
* Resolve the effective HTML for an app. For single-file apps this is
|
|
69
|
+
* `htmlDefinition` (the root index.html). For multifile apps it reads the
|
|
70
|
+
* compiled `dist/index.html` and inlines JS/CSS assets so the result is a
|
|
71
|
+
* self-contained HTML string suitable for `loadHTMLString`.
|
|
72
|
+
*/
|
|
73
|
+
export function resolveEffectiveAppHtml(app: AppDefinition): string {
|
|
74
|
+
if (!isMultifileApp(app)) return app.htmlDefinition;
|
|
75
|
+
|
|
76
|
+
const appDir = getAppDirPath(app.id);
|
|
77
|
+
const distIndex = join(appDir, "dist", "index.html");
|
|
78
|
+
if (existsSync(distIndex)) {
|
|
79
|
+
return inlineDistAssets(appDir, readFileSync(distIndex, "utf-8"));
|
|
80
|
+
}
|
|
81
|
+
return app.htmlDefinition;
|
|
82
|
+
}
|
|
83
|
+
|
|
67
84
|
/**
|
|
68
85
|
* Inline dist assets (main.js, main.css) into the compiled HTML so it can be
|
|
69
86
|
* delivered as a self-contained string via loadHTMLString/SSE without needing
|
|
@@ -75,7 +92,10 @@ export function inlineDistAssets(appDir: string, html: string): string {
|
|
|
75
92
|
// Inline main.js
|
|
76
93
|
const jsPath = join(distDir, "main.js");
|
|
77
94
|
if (existsSync(jsPath)) {
|
|
78
|
-
const js = readFileSync(jsPath, "utf-8").replace(
|
|
95
|
+
const js = readFileSync(jsPath, "utf-8").replace(
|
|
96
|
+
/<\/script>/g,
|
|
97
|
+
"<\\/script>",
|
|
98
|
+
);
|
|
79
99
|
html = html.replace(
|
|
80
100
|
/<script\s+type="module"\s+src="main\.js"\s*><\/script>/,
|
|
81
101
|
() => `<script type="module">${js}</script>`,
|
|
@@ -818,6 +838,16 @@ export function listAppFiles(appId: string): string[] {
|
|
|
818
838
|
return results.sort();
|
|
819
839
|
}
|
|
820
840
|
|
|
841
|
+
/**
|
|
842
|
+
* Check whether a file exists in the app directory.
|
|
843
|
+
* Path is validated to prevent traversal.
|
|
844
|
+
*/
|
|
845
|
+
export function appFileExists(appId: string, path: string): boolean {
|
|
846
|
+
validateId(appId);
|
|
847
|
+
const resolved = validateFilePath(appId, path);
|
|
848
|
+
return existsSync(resolved);
|
|
849
|
+
}
|
|
850
|
+
|
|
821
851
|
/**
|
|
822
852
|
* Read a file from the app directory.
|
|
823
853
|
* Path is validated to prevent traversal.
|
|
@@ -170,6 +170,7 @@ export interface ConversationRow {
|
|
|
170
170
|
originInterface: string | null;
|
|
171
171
|
forkParentConversationId: string | null;
|
|
172
172
|
forkParentMessageId: string | null;
|
|
173
|
+
hostAccess: number;
|
|
173
174
|
isAutoTitle: number;
|
|
174
175
|
scheduleJobId: string | null;
|
|
175
176
|
lastMessageAt: number | null;
|
|
@@ -196,6 +197,7 @@ export const parseConversation = createRowMapper<
|
|
|
196
197
|
originInterface: "originInterface",
|
|
197
198
|
forkParentConversationId: "forkParentConversationId",
|
|
198
199
|
forkParentMessageId: "forkParentMessageId",
|
|
200
|
+
hostAccess: "hostAccess",
|
|
199
201
|
isAutoTitle: "isAutoTitle",
|
|
200
202
|
scheduleJobId: "scheduleJobId",
|
|
201
203
|
lastMessageAt: "lastMessageAt",
|
|
@@ -245,6 +247,7 @@ export function createConversation(
|
|
|
245
247
|
source?: string;
|
|
246
248
|
scheduleJobId?: string;
|
|
247
249
|
groupId?: string;
|
|
250
|
+
hostAccess?: boolean;
|
|
248
251
|
},
|
|
249
252
|
) {
|
|
250
253
|
const db = getDb();
|
|
@@ -276,6 +279,7 @@ export function createConversation(
|
|
|
276
279
|
contextSummary: null as string | null,
|
|
277
280
|
contextCompactedMessageCount: 0,
|
|
278
281
|
contextCompactedAt: null as number | null,
|
|
282
|
+
hostAccess: opts.hostAccess ? 1 : 0,
|
|
279
283
|
conversationType,
|
|
280
284
|
source,
|
|
281
285
|
memoryScopeId,
|
|
@@ -314,12 +318,15 @@ export function createConversation(
|
|
|
314
318
|
|
|
315
319
|
// group_id is NOT in the Drizzle schema (raw-query-only pattern).
|
|
316
320
|
// Set via raw SQL after the INSERT succeeds.
|
|
317
|
-
|
|
321
|
+
// Always set group_id — default to "system:all" when none provided.
|
|
322
|
+
{
|
|
323
|
+
const effectiveGroupId = groupId ?? "system:all";
|
|
318
324
|
for (let attempt = 0; ; attempt++) {
|
|
319
325
|
try {
|
|
320
326
|
rawRun(
|
|
321
|
-
"UPDATE conversations SET group_id = ? WHERE id = ?",
|
|
322
|
-
|
|
327
|
+
"UPDATE conversations SET group_id = ?, is_pinned = ? WHERE id = ?",
|
|
328
|
+
effectiveGroupId,
|
|
329
|
+
effectiveGroupId === "system:pinned" ? 1 : 0,
|
|
323
330
|
id,
|
|
324
331
|
);
|
|
325
332
|
break;
|
|
@@ -385,6 +392,11 @@ export function getConversationMemoryScopeId(conversationId: string): string {
|
|
|
385
392
|
return conv?.memoryScopeId ?? "default";
|
|
386
393
|
}
|
|
387
394
|
|
|
395
|
+
export function getConversationHostAccess(conversationId: string): boolean {
|
|
396
|
+
const conv = getConversation(conversationId);
|
|
397
|
+
return conv?.hostAccess === 1;
|
|
398
|
+
}
|
|
399
|
+
|
|
388
400
|
/**
|
|
389
401
|
* Fetch group_id for a conversation via raw SQL. group_id is NOT in the
|
|
390
402
|
* Drizzle schema (raw-query-only pattern), so ConversationRow doesn't
|
|
@@ -469,7 +481,7 @@ export function forkConversation(params: {
|
|
|
469
481
|
const fc = createConversation({
|
|
470
482
|
title: forkTitle,
|
|
471
483
|
conversationType: "standard",
|
|
472
|
-
groupId: parentGroupId ??
|
|
484
|
+
groupId: parentGroupId ?? "system:all",
|
|
473
485
|
});
|
|
474
486
|
|
|
475
487
|
db.update(conversations)
|
|
@@ -1120,6 +1132,20 @@ export function updateConversationContextWindow(
|
|
|
1120
1132
|
.run();
|
|
1121
1133
|
}
|
|
1122
1134
|
|
|
1135
|
+
export function updateConversationHostAccess(
|
|
1136
|
+
id: string,
|
|
1137
|
+
hostAccess: boolean,
|
|
1138
|
+
): void {
|
|
1139
|
+
const db = getDb();
|
|
1140
|
+
db.update(conversations)
|
|
1141
|
+
.set({
|
|
1142
|
+
hostAccess: hostAccess ? 1 : 0,
|
|
1143
|
+
updatedAt: Date.now(),
|
|
1144
|
+
})
|
|
1145
|
+
.where(eq(conversations.id, id))
|
|
1146
|
+
.run();
|
|
1147
|
+
}
|
|
1148
|
+
|
|
1123
1149
|
/**
|
|
1124
1150
|
* Delete all conversations, messages, and related data (tool invocations,
|
|
1125
1151
|
* memory segments, etc.) from the daemon database.
|
|
@@ -1547,17 +1573,19 @@ export function batchSetDisplayOrders(
|
|
|
1547
1573
|
if (update.groupId !== undefined) {
|
|
1548
1574
|
// New client: groupId is authoritative.
|
|
1549
1575
|
// Derive is_pinned from groupId.
|
|
1550
|
-
// Sanitize: if groupId references a deleted/unknown group,
|
|
1551
|
-
// to
|
|
1576
|
+
// Sanitize: if groupId is null or references a deleted/unknown group,
|
|
1577
|
+
// fall back to "system:all" to avoid FK violation that would roll back
|
|
1578
|
+
// the entire batch.
|
|
1552
1579
|
let safeGroupId = update.groupId;
|
|
1553
|
-
if (
|
|
1554
|
-
safeGroupId
|
|
1580
|
+
if (safeGroupId === null) {
|
|
1581
|
+
safeGroupId = "system:all";
|
|
1582
|
+
} else if (
|
|
1555
1583
|
!rawGet<{ id: string }>(
|
|
1556
1584
|
"SELECT id FROM conversation_groups WHERE id = ?",
|
|
1557
1585
|
safeGroupId,
|
|
1558
1586
|
)
|
|
1559
1587
|
) {
|
|
1560
|
-
safeGroupId =
|
|
1588
|
+
safeGroupId = "system:all";
|
|
1561
1589
|
}
|
|
1562
1590
|
rawRun(
|
|
1563
1591
|
"UPDATE conversations SET display_order = ?, is_pinned = ?, group_id = ? WHERE id = ?",
|
|
@@ -1587,7 +1615,7 @@ export function batchSetDisplayOrders(
|
|
|
1587
1615
|
WHEN source IN ('schedule', 'reminder') THEN 'system:scheduled'
|
|
1588
1616
|
WHEN source IN ('heartbeat', 'task') THEN 'system:background'
|
|
1589
1617
|
WHEN conversation_type = 'background' AND COALESCE(source, '') != 'notification' THEN 'system:background'
|
|
1590
|
-
ELSE
|
|
1618
|
+
ELSE 'system:all'
|
|
1591
1619
|
END
|
|
1592
1620
|
ELSE group_id END
|
|
1593
1621
|
WHERE id = ?`,
|
|
@@ -26,6 +26,45 @@ export function getConversationDirName(
|
|
|
26
26
|
return `${getConversationDirTimestamp(createdAtMs)}_${id}`;
|
|
27
27
|
}
|
|
28
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Parse a canonical conversation directory name (`<ISO-with-dashes>_<id>`)
|
|
31
|
+
* back into its components. Returns null for names that don't match the
|
|
32
|
+
* canonical format — callers should treat null as "skip this entry"
|
|
33
|
+
* rather than as an error.
|
|
34
|
+
*
|
|
35
|
+
* Inverse of {@link getConversationDirName}. Does NOT parse the legacy
|
|
36
|
+
* `<id>_<ISO-with-dashes>` format.
|
|
37
|
+
*/
|
|
38
|
+
export function parseConversationDirName(
|
|
39
|
+
name: string,
|
|
40
|
+
): { conversationId: string; createdAtMs: number } | null {
|
|
41
|
+
// Canonical format: YYYY-MM-DDTHH-MM-SS.sssZ_<uuid>
|
|
42
|
+
// The timestamp portion has 4 hyphens (date + time-component separators)
|
|
43
|
+
// and ends in `Z`. Anchor the regex to enforce that.
|
|
44
|
+
const match = name.match(
|
|
45
|
+
/^(\d{4}-\d{2}-\d{2})T(\d{2})-(\d{2})-(\d{2}\.\d{1,9}Z)_(.+)$/,
|
|
46
|
+
);
|
|
47
|
+
if (!match) return null;
|
|
48
|
+
const [, datePart, hh, mm, ssAndMs, conversationId] = match;
|
|
49
|
+
const iso = `${datePart}T${hh}:${mm}:${ssAndMs}`;
|
|
50
|
+
const ms = Date.parse(iso);
|
|
51
|
+
if (Number.isNaN(ms)) return null;
|
|
52
|
+
if (!conversationId) return null;
|
|
53
|
+
// Reject conversation IDs that are path-traversal-shaped or contain
|
|
54
|
+
// path separators. These are never valid conversation IDs and would
|
|
55
|
+
// be a defense-in-depth concern if parsed.conversationId is later used
|
|
56
|
+
// to construct filesystem paths.
|
|
57
|
+
if (
|
|
58
|
+
conversationId === "." ||
|
|
59
|
+
conversationId === ".." ||
|
|
60
|
+
conversationId.includes("/") ||
|
|
61
|
+
conversationId.includes("\\")
|
|
62
|
+
) {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
return { conversationId, createdAtMs: ms };
|
|
66
|
+
}
|
|
67
|
+
|
|
29
68
|
/**
|
|
30
69
|
* Return the absolute path to a conversation's timestamp-first disk-view
|
|
31
70
|
* directory.
|
|
@@ -57,15 +57,45 @@ export function ensureGroupMigration(): void {
|
|
|
57
57
|
}
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
// 3. Seed system groups (
|
|
60
|
+
// 3. Seed system groups (four: pinned, scheduled, background, all)
|
|
61
|
+
const now = Math.floor(Date.now() / 1000);
|
|
61
62
|
rawExec(`
|
|
62
|
-
INSERT OR IGNORE INTO conversation_groups (id, name, sort_position, is_system_group)
|
|
63
|
+
INSERT OR IGNORE INTO conversation_groups (id, name, sort_position, is_system_group, created_at, updated_at)
|
|
63
64
|
VALUES
|
|
64
|
-
('system:pinned', 'Pinned', 0, TRUE),
|
|
65
|
-
('system:scheduled', 'Scheduled', 1, TRUE),
|
|
66
|
-
('system:background', 'Background', 2, TRUE)
|
|
65
|
+
('system:pinned', 'Pinned', 0, TRUE, ${now}, ${now}),
|
|
66
|
+
('system:scheduled', 'Scheduled', 1, TRUE, ${now}, ${now}),
|
|
67
|
+
('system:background', 'Background', 2, TRUE, ${now}, ${now}),
|
|
68
|
+
('system:all', 'Recents', 3, TRUE, ${now}, ${now})
|
|
67
69
|
`);
|
|
68
70
|
|
|
71
|
+
// One-time migration: move system:all to sortPosition 3 (from 999999).
|
|
72
|
+
// Bump custom groups at position 3+ up by 1 to make room. Wrapped in a
|
|
73
|
+
// transaction so a crash between the shift and the sentinel can't cause
|
|
74
|
+
// repeated drift on restart.
|
|
75
|
+
const sortShiftDone = rawGet<{ id: string }>(
|
|
76
|
+
"SELECT id FROM conversation_groups WHERE id = '_sort_shift_complete'",
|
|
77
|
+
);
|
|
78
|
+
if (!sortShiftDone) {
|
|
79
|
+
try {
|
|
80
|
+
rawExec("BEGIN");
|
|
81
|
+
rawRun(
|
|
82
|
+
"UPDATE conversation_groups SET sort_position = sort_position + 1 WHERE is_system_group = 0 AND sort_position >= 3",
|
|
83
|
+
);
|
|
84
|
+
rawRun(
|
|
85
|
+
"UPDATE conversation_groups SET sort_position = 3 WHERE id = 'system:all' AND sort_position != 3",
|
|
86
|
+
);
|
|
87
|
+
rawRun(
|
|
88
|
+
`INSERT OR IGNORE INTO conversation_groups (id, name, sort_position, is_system_group, created_at, updated_at)
|
|
89
|
+
VALUES ('_sort_shift_complete', '_sort_shift_complete', -1, TRUE, ${now}, ${now})`,
|
|
90
|
+
);
|
|
91
|
+
rawExec("COMMIT");
|
|
92
|
+
} catch (err) {
|
|
93
|
+
rawExec("ROLLBACK");
|
|
94
|
+
log.error({ err }, "Sort-position shift transaction failed, rolled back");
|
|
95
|
+
throw err;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
69
99
|
// 4. One-time backfill (guard: persistent marker prevents re-running on restart)
|
|
70
100
|
//
|
|
71
101
|
// The backfill sets group_id on existing conversations based on their attributes.
|
|
@@ -153,5 +183,35 @@ export function ensureGroupMigration(): void {
|
|
|
153
183
|
}
|
|
154
184
|
}
|
|
155
185
|
|
|
186
|
+
// 5. One-time backfill: assign all ungrouped conversations to system:all
|
|
187
|
+
//
|
|
188
|
+
// Separate from the initial backfill above because system:all is added later.
|
|
189
|
+
// Uses its own sentinel so it runs exactly once, even on existing installations
|
|
190
|
+
// where the original backfill already completed.
|
|
191
|
+
const allBackfillDone = rawGet<{ id: string }>(
|
|
192
|
+
"SELECT id FROM conversation_groups WHERE id = '_backfill_all_complete'",
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
if (!allBackfillDone) {
|
|
196
|
+
try {
|
|
197
|
+
rawExec("BEGIN");
|
|
198
|
+
|
|
199
|
+
rawExec(`
|
|
200
|
+
UPDATE conversations SET group_id = 'system:all' WHERE group_id IS NULL
|
|
201
|
+
`);
|
|
202
|
+
|
|
203
|
+
rawExec(`
|
|
204
|
+
INSERT OR IGNORE INTO conversation_groups (id, name, sort_position, is_system_group)
|
|
205
|
+
VALUES ('_backfill_all_complete', '_backfill_all_complete', -1, TRUE)
|
|
206
|
+
`);
|
|
207
|
+
|
|
208
|
+
rawExec("COMMIT");
|
|
209
|
+
} catch (err) {
|
|
210
|
+
rawExec("ROLLBACK");
|
|
211
|
+
log.error({ err }, "system:all backfill transaction failed, rolled back");
|
|
212
|
+
throw err;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
156
216
|
migrated = true;
|
|
157
217
|
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cadence logic for conversation starters generation.
|
|
3
|
+
*
|
|
4
|
+
* Decides whether a new generation job should be enqueued based on how many
|
|
5
|
+
* active memory items have accumulated since the last generation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { and, eq, inArray, sql } from "drizzle-orm";
|
|
9
|
+
|
|
10
|
+
import { getLogger } from "../util/logger.js";
|
|
11
|
+
import { getDb } from "./db.js";
|
|
12
|
+
import { enqueueMemoryJob } from "./jobs-store.js";
|
|
13
|
+
import { rawGet } from "./raw-query.js";
|
|
14
|
+
import { memoryCheckpoints, memoryJobs } from "./schema.js";
|
|
15
|
+
|
|
16
|
+
const log = getLogger("conversation-starters-cadence");
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Check whether enough new memory items have accumulated to justify
|
|
20
|
+
* generating a fresh batch of conversation starters.
|
|
21
|
+
*/
|
|
22
|
+
export function maybeEnqueueConversationStartersJob(scopeId: string): void {
|
|
23
|
+
const db = getDb();
|
|
24
|
+
|
|
25
|
+
// Count total active memory items
|
|
26
|
+
const countRow = rawGet<{ c: number }>(
|
|
27
|
+
`SELECT COUNT(*) AS c FROM memory_graph_nodes WHERE fidelity != 'gone' AND scope_id = ?`,
|
|
28
|
+
scopeId,
|
|
29
|
+
);
|
|
30
|
+
const totalActive = countRow?.c ?? 0;
|
|
31
|
+
if (totalActive === 0) return;
|
|
32
|
+
|
|
33
|
+
// Read checkpoint: item count at last generation (scoped so each scope tracks independently)
|
|
34
|
+
const checkpointKey = `conversation_starters:item_count_at_last_gen:${scopeId}`;
|
|
35
|
+
const checkpoint = db
|
|
36
|
+
.select({ value: memoryCheckpoints.value })
|
|
37
|
+
.from(memoryCheckpoints)
|
|
38
|
+
.where(eq(memoryCheckpoints.key, checkpointKey))
|
|
39
|
+
.get();
|
|
40
|
+
const parsedLastCount = checkpoint ? parseInt(checkpoint.value, 10) : 0;
|
|
41
|
+
const lastCount = Number.isFinite(parsedLastCount) ? parsedLastCount : 0;
|
|
42
|
+
|
|
43
|
+
// Cadence formula
|
|
44
|
+
let threshold: number;
|
|
45
|
+
if (totalActive <= 10) {
|
|
46
|
+
threshold = 1;
|
|
47
|
+
} else if (totalActive <= 50) {
|
|
48
|
+
threshold = 5;
|
|
49
|
+
} else {
|
|
50
|
+
threshold = 10;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const checkpointAhead = totalActive < lastCount;
|
|
54
|
+
const delta = Math.max(0, totalActive - lastCount);
|
|
55
|
+
if (!checkpointAhead && delta < threshold) return;
|
|
56
|
+
|
|
57
|
+
// Dedup: don't enqueue if a pending/running job for this scope already exists
|
|
58
|
+
const existing = db
|
|
59
|
+
.select({ id: memoryJobs.id })
|
|
60
|
+
.from(memoryJobs)
|
|
61
|
+
.where(
|
|
62
|
+
and(
|
|
63
|
+
eq(memoryJobs.type, "generate_conversation_starters"),
|
|
64
|
+
inArray(memoryJobs.status, ["pending", "running"]),
|
|
65
|
+
sql`json_extract(${memoryJobs.payload}, '$.scopeId') = ${scopeId}`,
|
|
66
|
+
),
|
|
67
|
+
)
|
|
68
|
+
.get();
|
|
69
|
+
if (existing) return;
|
|
70
|
+
|
|
71
|
+
enqueueMemoryJob("generate_conversation_starters", { scopeId });
|
|
72
|
+
log.info(
|
|
73
|
+
{ totalActive, lastCount, delta, threshold, scopeId, checkpointAhead },
|
|
74
|
+
"Enqueued conversation starters generation job",
|
|
75
|
+
);
|
|
76
|
+
}
|
|
@@ -284,10 +284,13 @@ export function queueRegenerateConversationTitle(
|
|
|
284
284
|
*/
|
|
285
285
|
function buildTitleSystemPrompt(): string {
|
|
286
286
|
return [
|
|
287
|
-
"You generate
|
|
287
|
+
"You generate ultra-concise conversation titles. Output ONLY the title text — no explanation, no quotes, no markdown, no preamble.",
|
|
288
288
|
"",
|
|
289
289
|
"Rules:",
|
|
290
|
-
"-
|
|
290
|
+
"- 2–6 words. Titles longer than 6 words are unacceptable — ruthlessly compress",
|
|
291
|
+
"- Summarize the TOPIC, not the request or instructions",
|
|
292
|
+
"- Noun phrases are ideal (e.g. 'Auth Middleware Rewrite', 'Docker Volume Mounts')",
|
|
293
|
+
"- Do NOT echo back what the user asked you to do",
|
|
291
294
|
"- Do NOT respond to the conversation content",
|
|
292
295
|
"- Do NOT assess feasibility or comment on capabilities",
|
|
293
296
|
].join("\n");
|
package/src/memory/db-init.ts
CHANGED
|
@@ -58,6 +58,7 @@ import {
|
|
|
58
58
|
migrateContactsRolePrincipal,
|
|
59
59
|
migrateContactsUserFileColumn,
|
|
60
60
|
migrateConversationForkLineage,
|
|
61
|
+
migrateConversationHostAccess,
|
|
61
62
|
migrateConversationsLastMessageAt,
|
|
62
63
|
migrateConversationsThreadTypeIndex,
|
|
63
64
|
migrateCreateConversationGraphMemoryState,
|
|
@@ -110,9 +111,14 @@ import {
|
|
|
110
111
|
migrateOAuthProvidersBehaviorColumns,
|
|
111
112
|
migrateOAuthProvidersDisplayMetadata,
|
|
112
113
|
migrateOAuthProvidersFeatureFlag,
|
|
114
|
+
migrateOAuthProvidersLogoUrl,
|
|
113
115
|
migrateOAuthProvidersManagedServiceConfigKey,
|
|
114
116
|
migrateOAuthProvidersPingConfig,
|
|
115
117
|
migrateOAuthProvidersPingUrl,
|
|
118
|
+
migrateOAuthProvidersRefreshUrl,
|
|
119
|
+
migrateOAuthProvidersRevoke,
|
|
120
|
+
migrateOAuthProvidersScopeSeparator,
|
|
121
|
+
migrateOAuthProvidersTokenAuthMethodDefault,
|
|
116
122
|
migrateReminderRoutingIntent,
|
|
117
123
|
migrateRemindersToSchedules,
|
|
118
124
|
migrateRenameConversationTypeColumn,
|
|
@@ -352,6 +358,12 @@ export function initializeDb(): void {
|
|
|
352
358
|
migrateScheduleReuseConversation,
|
|
353
359
|
migrateMemoryRecallLogsQueryContext,
|
|
354
360
|
migrateLlmRequestLogsCreatedAtIndex,
|
|
361
|
+
migrateOAuthProvidersScopeSeparator,
|
|
362
|
+
migrateOAuthProvidersRefreshUrl,
|
|
363
|
+
migrateOAuthProvidersRevoke,
|
|
364
|
+
migrateOAuthProvidersTokenAuthMethodDefault,
|
|
365
|
+
migrateConversationHostAccess,
|
|
366
|
+
migrateOAuthProvidersLogoUrl,
|
|
355
367
|
];
|
|
356
368
|
|
|
357
369
|
// Run each migration step, catching and logging individual failures so one
|