@vellumai/assistant 0.6.2 → 0.6.4
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 +273 -10
- package/Dockerfile +2 -3
- package/bun.lock +41 -49
- package/bunfig.toml +3 -0
- package/docs/architecture/memory.md +1 -1
- package/docs/backup-troubleshooting.md +52 -0
- package/docs/browser-use-architecture-phase2.md +174 -0
- package/docs/stt-provider-onboarding.md +120 -0
- package/knip.json +12 -2
- package/node_modules/@vellumai/ces-contracts/bun.lock +8 -6
- package/node_modules/@vellumai/ces-contracts/package.json +3 -3
- package/node_modules/@vellumai/ces-contracts/src/rpc.ts +42 -0
- package/openapi.yaml +1111 -86
- package/package.json +40 -42
- package/scripts/generate-openapi.ts +0 -2
- package/scripts/test.sh +73 -18
- package/src/__tests__/acp-session.test.ts +43 -0
- package/src/__tests__/agent-image-optimize.test.ts +28 -0
- package/src/__tests__/agent-loop.test.ts +123 -0
- package/src/__tests__/anthropic-provider.test.ts +263 -10
- 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__/auto-analysis-end-to-end.test.ts +550 -0
- package/src/__tests__/auto-analysis-prompt.test.ts +50 -0
- package/src/__tests__/browser-fill-credential.test.ts +240 -94
- package/src/__tests__/browser-manager.test.ts +40 -27
- package/src/__tests__/browser-skill-baseline-tool-payload.test.ts +2 -2
- package/src/__tests__/browser-skill-endstate.test.ts +31 -7
- package/src/__tests__/btw-routes.test.ts +7 -0
- package/src/__tests__/call-controller.test.ts +581 -20
- package/src/__tests__/catalog-files.test.ts +1000 -0
- package/src/__tests__/channel-approvals.test.ts +53 -0
- package/src/__tests__/channel-invite-transport.test.ts +2 -2
- package/src/__tests__/channel-readiness-routes.test.ts +16 -20
- package/src/__tests__/channel-readiness-service.test.ts +12 -7
- package/src/__tests__/checker.test.ts +157 -10
- package/src/__tests__/clawhub-files.test.ts +347 -0
- package/src/__tests__/commit-message-enrichment-service.test.ts +36 -19
- package/src/__tests__/config-analysis.test.ts +100 -0
- 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 +1248 -224
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
- package/src/__tests__/config-watcher.test.ts +43 -8
- package/src/__tests__/confirmation-request-guardian-bridge.test.ts +23 -0
- package/src/__tests__/contact-store-user-file.test.ts +512 -0
- package/src/__tests__/contacts-write.test.ts +197 -0
- package/src/__tests__/context-overflow-approval.test.ts +16 -1
- package/src/__tests__/context-window-manager.test.ts +88 -0
- package/src/__tests__/conversation-abort-tool-results.test.ts +2 -0
- package/src/__tests__/conversation-agent-loop-overflow.test.ts +2 -1
- package/src/__tests__/conversation-agent-loop.test.ts +99 -3
- package/src/__tests__/conversation-analysis-routes.test.ts +2 -2
- package/src/__tests__/conversation-attachments.test.ts +80 -4
- package/src/__tests__/conversation-confirmation-signals.test.ts +290 -0
- package/src/__tests__/conversation-error.test.ts +70 -0
- package/src/__tests__/conversation-fork-crud.test.ts +17 -0
- package/src/__tests__/conversation-history-web-search.test.ts +12 -4
- package/src/__tests__/conversation-host-access-routes.test.ts +229 -0
- package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
- package/src/__tests__/conversation-inject-context.test.ts +103 -0
- package/src/__tests__/conversation-launcher-skill-regression.test.ts +51 -0
- package/src/__tests__/conversation-list-source.test.ts +145 -0
- package/src/__tests__/conversation-pre-run-repair.test.ts +2 -0
- package/src/__tests__/conversation-provider-retry-repair.test.ts +2 -0
- package/src/__tests__/conversation-queue.test.ts +946 -62
- package/src/__tests__/conversation-routes-disk-view.test.ts +275 -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 +324 -46
- package/src/__tests__/conversation-skill-tools.test.ts +7 -4
- package/src/__tests__/conversation-slash-commands.test.ts +33 -0
- package/src/__tests__/conversation-slash-queue.test.ts +89 -18
- package/src/__tests__/conversation-slash-unknown.test.ts +2 -0
- 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-tool-setup-batch-authorized.test.ts +226 -0
- package/src/__tests__/conversation-workspace-cache-state.test.ts +193 -0
- package/src/__tests__/conversation-workspace-injection.test.ts +2 -0
- package/src/__tests__/conversation-workspace-tool-tracking.test.ts +2 -0
- package/src/__tests__/credential-execution-approval-bridge.test.ts +32 -1
- package/src/__tests__/credential-health-service.test.ts +352 -0
- package/src/__tests__/credential-security-invariants.test.ts +6 -3
- package/src/__tests__/credential-vault-unit.test.ts +383 -7
- package/src/__tests__/credential-vault.test.ts +152 -13
- package/src/__tests__/credentials-cli.test.ts +42 -18
- package/src/__tests__/cross-provider-web-search.test.ts +146 -35
- package/src/__tests__/date-context.test.ts +4 -4
- package/src/__tests__/deterministic-verification-control-plane.test.ts +10 -1
- package/src/__tests__/device-id.test.ts +112 -0
- package/src/__tests__/docker-signing-key-bootstrap.test.ts +167 -4
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +1 -3
- package/src/__tests__/email-html-renderer.test.ts +71 -0
- package/src/__tests__/email-invite-adapter.test.ts +36 -32
- package/src/__tests__/embedding-managed-proxy-selection.test.ts +256 -0
- package/src/__tests__/emit-event-signal.test.ts +71 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +222 -0
- package/src/__tests__/fixtures/mock-chrome-extension.ts +386 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
- package/src/__tests__/gateway-only-guard.test.ts +2 -0
- package/src/__tests__/gemini-provider.test.ts +66 -2
- package/src/__tests__/get-skill-detail-audit.test.ts +325 -0
- package/src/__tests__/gmail-archive-fallback.test.ts +193 -0
- package/src/__tests__/gmail-archive-gate.test.ts +246 -0
- package/src/__tests__/gmail-preferences.test.ts +117 -0
- package/src/__tests__/guardian-routing-invariants.test.ts +70 -2
- package/src/__tests__/headless-browser-interactions.test.ts +738 -359
- package/src/__tests__/headless-browser-mode.test.ts +614 -0
- package/src/__tests__/headless-browser-navigate.test.ts +528 -49
- package/src/__tests__/headless-browser-read-tools.test.ts +274 -100
- package/src/__tests__/headless-browser-snapshot.test.ts +250 -77
- package/src/__tests__/heartbeat-service.test.ts +70 -17
- package/src/__tests__/home-state-routes.test.ts +162 -0
- package/src/__tests__/host-bash-proxy.test.ts +145 -1
- package/src/__tests__/host-browser-e2e-cloud.test.ts +596 -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 +423 -0
- package/src/__tests__/host-cu-proxy.test.ts +166 -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__/identity-intro-cache.test.ts +40 -10
- package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
- package/src/__tests__/integration-status.test.ts +6 -7
- package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
- package/src/__tests__/list-messages-tool-merge.test.ts +37 -12
- package/src/__tests__/llm-context-normalization.test.ts +488 -0
- package/src/__tests__/llm-context-route-provider.test.ts +86 -5
- package/src/__tests__/llm-usage-store.test.ts +363 -0
- package/src/__tests__/mcp-client-auth.test.ts +40 -4
- package/src/__tests__/mcp-health-check.test.ts +10 -3
- package/src/__tests__/media-stream-output.test.ts +555 -0
- package/src/__tests__/media-stream-parser.test.ts +374 -0
- package/src/__tests__/media-stream-server-integration.test.ts +1234 -0
- package/src/__tests__/media-stream-stt-session.test.ts +588 -0
- package/src/__tests__/media-turn-detector.test.ts +440 -0
- package/src/__tests__/message-queue.test.ts +125 -0
- package/src/__tests__/migration-cross-version-compatibility.test.ts +3 -1
- package/src/__tests__/migration-export-http.test.ts +67 -8
- package/src/__tests__/migration-export-streaming.test.ts +66 -0
- package/src/__tests__/migration-import-commit-http.test.ts +109 -7
- package/src/__tests__/migration-import-preflight-http.test.ts +6 -5
- package/src/__tests__/migration-validate-http.test.ts +3 -3
- package/src/__tests__/mock-gateway-ipc.ts +151 -0
- package/src/__tests__/model-intents.test.ts +2 -2
- package/src/__tests__/native-host-marker-sync-guard.test.ts +157 -0
- package/src/__tests__/oauth-apps-routes.test.ts +18 -12
- package/src/__tests__/oauth-cli.test.ts +709 -60
- package/src/__tests__/oauth-connect-orchestrator.test.ts +118 -24
- package/src/__tests__/oauth-provider-seed-logos.test.ts +23 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +147 -10
- package/src/__tests__/oauth-provider-visibility.test.ts +19 -21
- package/src/__tests__/oauth-providers-routes.test.ts +52 -14
- package/src/__tests__/oauth-store.test.ts +1465 -176
- package/src/__tests__/oauth2-gateway-transport.test.ts +460 -26
- package/src/__tests__/onboarding-template-contract.test.ts +81 -70
- package/src/__tests__/openai-provider.test.ts +178 -2
- package/src/__tests__/openai-responses-cutover-guard.test.ts +184 -0
- package/src/__tests__/openai-responses-provider.test.ts +1105 -0
- package/src/__tests__/openrouter-token-estimation.test.ts +100 -0
- 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 +32 -3
- package/src/__tests__/permission-checker-host-gate.test.ts +74 -14
- package/src/__tests__/permission-mode.test.ts +28 -56
- package/src/__tests__/persona-resolver.test.ts +251 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
- package/src/__tests__/platform-callback-registration.test.ts +19 -0
- package/src/__tests__/platform.test.ts +92 -1
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +343 -0
- package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
- package/src/__tests__/pricing.test.ts +174 -0
- package/src/__tests__/proxy-approval-callback.test.ts +18 -0
- package/src/__tests__/qdrant-manager.test.ts +29 -8
- package/src/__tests__/regenerate-fire-and-forget-trace.test.ts +194 -0
- package/src/__tests__/relationship-state-contract.test.ts +175 -0
- package/src/__tests__/relay-server.test.ts +423 -5
- package/src/__tests__/require-fresh-approval.test.ts +40 -1
- package/src/__tests__/sanitize-config-for-transfer.test.ts +132 -0
- package/src/__tests__/schedule-routes.test.ts +162 -0
- package/src/__tests__/search-skills-unified.test.ts +118 -0
- package/src/__tests__/secret-detection-handler.test.ts +84 -0
- package/src/__tests__/secret-ingress-http.test.ts +1 -0
- package/src/__tests__/secret-scanner-executor.test.ts +4 -0
- package/src/__tests__/secure-keys.test.ts +107 -0
- package/src/__tests__/send-endpoint-busy.test.ts +8 -1
- package/src/__tests__/sequence-store.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +49 -0
- package/src/__tests__/set-permission-mode.test.ts +13 -250
- package/src/__tests__/settings-routes.test.ts +201 -0
- package/src/__tests__/skill-load-feature-flag.test.ts +1 -0
- package/src/__tests__/skills-file-content-endpoint.test.ts +801 -0
- package/src/__tests__/skills-files-catalog-fallback.test.ts +738 -0
- package/src/__tests__/skills.test.ts +5 -2
- package/src/__tests__/skillssh-files.test.ts +446 -0
- package/src/__tests__/slack-block-formatting.test.ts +110 -0
- package/src/__tests__/slack-channel-config.test.ts +576 -16
- package/src/__tests__/stt-catalog-parity.test.ts +282 -0
- package/src/__tests__/stt-stream-session.test.ts +535 -0
- 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 +184 -27
- package/src/__tests__/task-scheduler.test.ts +32 -6
- package/src/__tests__/telegram-config.test.ts +10 -13
- package/src/__tests__/telephony-stt-routing.test.ts +329 -0
- package/src/__tests__/terminal-tools.test.ts +25 -5
- package/src/__tests__/test-preload.ts +18 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
- package/src/__tests__/tool-approval-handler.test.ts +73 -0
- package/src/__tests__/tool-executor-lifecycle-events.test.ts +9 -5
- package/src/__tests__/tool-executor-shell-integration.test.ts +4 -0
- package/src/__tests__/tool-executor.test.ts +33 -24
- package/src/__tests__/tool-result-truncation.test.ts +36 -0
- 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 +14 -29
- package/src/__tests__/trust-store.test.ts +7 -1
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
- package/src/__tests__/trusted-contact-inline-approval-integration.test.ts +109 -0
- package/src/__tests__/tts-catalog-parity.test.ts +345 -0
- package/src/__tests__/twilio-routes-twiml.test.ts +512 -114
- package/src/__tests__/twilio-routes.test.ts +376 -0
- package/src/__tests__/unicode.test.ts +293 -0
- package/src/__tests__/update-bulletin-format.test.ts +59 -0
- package/src/__tests__/update-bulletin.test.ts +206 -5
- package/src/__tests__/usage-routes.test.ts +25 -4
- package/src/__tests__/user-reference.test.ts +46 -61
- package/src/__tests__/v2-consent-policy.test.ts +103 -0
- package/src/__tests__/verification-control-plane-policy.test.ts +4 -0
- package/src/__tests__/voice-config-update.test.ts +403 -0
- package/src/__tests__/voice-quality.test.ts +434 -19
- package/src/__tests__/workspace-heartbeat-service.test.ts +7 -0
- package/src/__tests__/workspace-migration-033-stt-service-explicit-config.test.ts +547 -0
- package/src/__tests__/workspace-migration-034-remove-calls-voice-transcription-provider.test.ts +596 -0
- package/src/__tests__/workspace-migration-drop-user-md.test.ts +368 -0
- package/src/__tests__/workspace-migration-meets.test.ts +244 -0
- package/src/__tests__/workspace-migration-seed-device-id.test.ts +14 -20
- package/src/__tests__/workspace-policy.test.ts +2 -0
- package/src/acp/client-handler.ts +30 -4
- package/src/agent/image-optimize.ts +24 -12
- package/src/agent/loop.ts +55 -9
- package/src/approvals/guardian-request-resolvers.ts +21 -15
- package/src/backup/__tests__/backup-key.test.ts +152 -0
- package/src/backup/__tests__/backup-worker.test.ts +767 -0
- package/src/backup/__tests__/list-snapshots.test.ts +87 -0
- package/src/backup/__tests__/local-writer.test.ts +218 -0
- package/src/backup/__tests__/offsite-writer.test.ts +641 -0
- package/src/backup/__tests__/paths.test.ts +300 -0
- package/src/backup/__tests__/restore.test.ts +498 -0
- package/src/backup/__tests__/snapshot-lock.test.ts +352 -0
- package/src/backup/__tests__/stream-crypt.test.ts +228 -0
- package/src/backup/backup-key.ts +137 -0
- package/src/backup/backup-worker.ts +459 -0
- package/src/backup/list-snapshots.ts +147 -0
- package/src/backup/local-writer.ts +133 -0
- package/src/backup/offsite-writer.ts +222 -0
- package/src/backup/paths.ts +226 -0
- package/src/backup/restore.ts +322 -0
- package/src/backup/snapshot-lock.ts +431 -0
- package/src/backup/stream-crypt.ts +263 -0
- 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/bundler/package-resolver.ts +4 -0
- package/src/calls/audio-store.ts +11 -5
- package/src/calls/call-controller.ts +226 -71
- package/src/calls/call-domain.ts +9 -0
- package/src/calls/call-speech-output.ts +190 -0
- package/src/calls/call-transport.ts +77 -0
- package/src/calls/media-stream-audio-transcode.ts +173 -0
- package/src/calls/media-stream-output.ts +660 -0
- package/src/calls/media-stream-parser.ts +300 -0
- package/src/calls/media-stream-protocol.ts +166 -0
- package/src/calls/media-stream-server.ts +592 -0
- package/src/calls/media-stream-stt-session.ts +460 -0
- package/src/calls/media-turn-detector.ts +230 -0
- package/src/calls/relay-server.ts +90 -75
- package/src/calls/resolve-call-tts-provider.ts +136 -0
- package/src/calls/telephony-stt-routing.ts +145 -0
- package/src/calls/tts-call-strategy.ts +161 -0
- package/src/calls/tts-text-sanitizer.ts +32 -16
- package/src/calls/twilio-routes.ts +281 -17
- package/src/calls/voice-quality.ts +78 -35
- package/src/calls/voice-session-bridge.ts +8 -1
- package/src/channels/__tests__/types.test.ts +134 -0
- package/src/channels/types.ts +69 -3
- package/src/cli/__tests__/run-assistant-command.ts +11 -1
- package/src/cli/commands/__tests__/backup.test.ts +1165 -0
- package/src/cli/commands/__tests__/domain-register.test.ts +234 -0
- package/src/cli/commands/__tests__/domain-status.test.ts +132 -0
- package/src/cli/commands/__tests__/email-attachment.test.ts +422 -0
- package/src/cli/commands/__tests__/email-download.test.ts +16 -1
- package/src/cli/commands/__tests__/email-list.test.ts +22 -4
- package/src/cli/commands/__tests__/email-register.test.ts +4 -4
- package/src/cli/commands/__tests__/email-send.test.ts +37 -4
- package/src/cli/commands/__tests__/email-status.test.ts +5 -1
- package/src/cli/commands/__tests__/email-unregister.test.ts +34 -5
- package/src/cli/commands/backup.ts +993 -0
- package/src/cli/commands/conversations.ts +77 -0
- package/src/cli/commands/credentials.ts +3 -4
- package/src/cli/commands/domain.ts +210 -0
- package/src/cli/commands/email.ts +273 -16
- package/src/cli/commands/mcp.ts +16 -4
- package/src/cli/commands/oauth/__tests__/connect.test.ts +56 -44
- 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 +32 -33
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +330 -0
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +117 -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 +6 -3
- package/src/cli/commands/oauth/disconnect.ts +1 -1
- package/src/cli/commands/oauth/mode.ts +12 -3
- package/src/cli/commands/oauth/providers.ts +215 -36
- package/src/cli/commands/oauth/shared.ts +7 -6
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +254 -0
- package/src/cli/commands/platform/__tests__/connect.test.ts +6 -0
- package/src/cli/commands/platform/__tests__/disconnect.test.ts +7 -1
- package/src/cli/commands/platform/__tests__/status.test.ts +6 -0
- 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 +30 -4
- package/src/config/__tests__/backup-schema.test.ts +134 -0
- package/src/config/assistant-feature-flags.ts +61 -62
- package/src/config/bundled-skills/app-builder/SKILL.md +26 -249
- package/src/config/bundled-skills/app-builder/references/CUSTOM_ROUTES.md +141 -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/browser/SKILL.md +30 -5
- package/src/config/bundled-skills/browser/TOOLS.json +123 -0
- package/src/config/bundled-skills/browser/tools/browser-attach.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-detach.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-status.ts +12 -0
- package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +17 -0
- package/src/config/bundled-skills/contacts/SKILL.md +5 -2
- package/src/config/bundled-skills/document/SKILL.md +4 -0
- package/src/config/bundled-skills/gmail/SKILL.md +54 -8
- package/src/config/bundled-skills/gmail/TOOLS.json +33 -3
- package/src/config/bundled-skills/gmail/tools/gmail-archive.ts +116 -9
- package/src/config/bundled-skills/gmail/tools/gmail-outreach-scan.ts +138 -11
- package/src/config/bundled-skills/gmail/tools/gmail-preferences-tool.ts +59 -0
- package/src/config/bundled-skills/gmail/tools/gmail-preferences.ts +82 -0
- package/src/config/bundled-skills/gmail/tools/gmail-sender-digest.ts +113 -17
- package/src/config/bundled-skills/gmail/tools/gmail-unsubscribe.ts +2 -2
- package/src/config/bundled-skills/media-processing/SKILL.md +3 -9
- package/src/config/bundled-skills/media-processing/TOOLS.json +1 -6
- package/src/config/bundled-skills/media-processing/__tests__/audio-transcribe.test.ts +125 -0
- package/src/config/bundled-skills/media-processing/__tests__/extract-keyframes.test.ts +181 -0
- package/src/config/bundled-skills/media-processing/__tests__/preprocess-audio.test.ts +141 -0
- package/src/config/bundled-skills/media-processing/services/audio-transcribe.ts +32 -87
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +8 -4
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +0 -10
- package/src/config/bundled-skills/messaging/SKILL.md +3 -3
- package/src/config/bundled-skills/messaging/tools/messaging-archive-by-sender.ts +2 -2
- package/src/config/bundled-skills/outlook/SKILL.md +9 -2
- package/src/config/bundled-skills/outlook/tools/outlook-unsubscribe.ts +2 -2
- package/src/config/bundled-skills/phone-calls/SKILL.md +2 -2
- package/src/config/bundled-skills/phone-calls/references/CONFIG.md +27 -18
- package/src/config/bundled-skills/phone-calls/references/TROUBLESHOOTING.md +3 -3
- package/src/config/bundled-skills/settings/TOOLS.json +3 -3
- package/src/config/bundled-skills/settings/tools/voice-config-update.ts +26 -22
- package/src/config/bundled-skills/slack/SKILL.md +1 -0
- 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/bundled-skills/transcribe/SKILL.md +9 -14
- package/src/config/bundled-skills/transcribe/TOOLS.json +2 -7
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.test.ts +256 -0
- package/src/config/bundled-skills/transcribe/tools/transcribe-media.ts +38 -188
- package/src/config/bundled-tool-registry.ts +8 -0
- package/src/config/env-registry.ts +38 -0
- package/src/config/env.ts +49 -4
- package/src/config/feature-flag-registry.json +85 -14
- package/src/config/loader.ts +82 -13
- package/src/config/sanitize-for-transfer.ts +47 -0
- package/src/config/schema.ts +81 -15
- package/src/config/schemas/__tests__/stt.test.ts +43 -0
- package/src/config/schemas/analysis.ts +51 -0
- package/src/config/schemas/backup.ts +72 -0
- package/src/config/schemas/calls.ts +1 -26
- package/src/config/schemas/elevenlabs.ts +0 -59
- package/src/config/schemas/filing.ts +47 -7
- package/src/config/schemas/heartbeat.ts +27 -5
- package/src/config/schemas/host-browser.ts +112 -0
- package/src/config/schemas/inference.ts +1 -1
- package/src/config/schemas/memory-lifecycle.ts +14 -2
- package/src/config/schemas/memory-retrieval.ts +103 -0
- package/src/config/schemas/security.ts +0 -6
- package/src/config/schemas/services.ts +52 -0
- package/src/config/schemas/stt.ts +59 -0
- package/src/config/schemas/tts.ts +230 -0
- package/src/config/schemas/updates.ts +14 -0
- package/src/config/skills.ts +4 -0
- package/src/config/types.ts +4 -1
- package/src/contacts/contact-store.ts +56 -11
- package/src/contacts/contacts-write.ts +38 -1
- package/src/context/post-turn-tool-result-truncation.ts +177 -0
- package/src/context/tool-result-truncation.ts +2 -1
- package/src/context/window-manager.ts +61 -10
- package/src/credential-execution/approval-bridge.ts +49 -15
- package/src/credential-execution/executable-discovery.ts +12 -2
- package/src/credential-execution/process-manager.ts +33 -2
- package/src/credential-health/credential-health-service.ts +366 -0
- package/src/daemon/__tests__/conversation-lifecycle-auto-analyze.test.ts +324 -0
- package/src/daemon/__tests__/conversation-surfaces-launch.test.ts +497 -0
- package/src/daemon/__tests__/conversation-tool-setup.test.ts +195 -0
- package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
- package/src/daemon/app-source-watcher.ts +35 -0
- package/src/daemon/config-watcher.ts +99 -5
- package/src/daemon/context-overflow-approval.ts +5 -0
- package/src/daemon/conversation-agent-loop-handlers.ts +23 -2
- package/src/daemon/conversation-agent-loop.ts +153 -42
- package/src/daemon/conversation-attachments.ts +40 -0
- package/src/daemon/conversation-error.ts +11 -0
- package/src/daemon/conversation-history.ts +40 -6
- package/src/daemon/conversation-launch.ts +220 -0
- package/src/daemon/conversation-lifecycle.ts +59 -9
- package/src/daemon/conversation-messaging.ts +37 -3
- package/src/daemon/conversation-notifiers.ts +5 -0
- package/src/daemon/conversation-process.ts +622 -13
- package/src/daemon/conversation-queue-manager.ts +24 -0
- package/src/daemon/conversation-runtime-assembly.ts +128 -36
- package/src/daemon/conversation-slash.ts +36 -0
- package/src/daemon/conversation-surfaces.ts +131 -40
- package/src/daemon/conversation-tool-setup.ts +99 -8
- package/src/daemon/conversation-usage.ts +7 -4
- package/src/daemon/conversation-workspace.ts +12 -0
- package/src/daemon/conversation.ts +292 -16
- package/src/daemon/date-context.ts +10 -10
- package/src/daemon/first-greeting.ts +3 -2
- package/src/daemon/handlers/config-slack-channel.ts +269 -94
- package/src/daemon/handlers/conversations.ts +13 -141
- package/src/daemon/handlers/shared.ts +80 -0
- package/src/daemon/handlers/skills.ts +483 -44
- package/src/daemon/host-bash-proxy.ts +48 -13
- package/src/daemon/host-browser-proxy.ts +192 -0
- package/src/daemon/host-cu-proxy.ts +36 -11
- package/src/daemon/host-file-proxy.ts +57 -9
- package/src/daemon/lifecycle.ts +179 -28
- package/src/daemon/message-protocol.ts +13 -0
- package/src/daemon/message-types/conversations.ts +89 -14
- package/src/daemon/message-types/home.ts +40 -0
- package/src/daemon/message-types/host-browser.ts +100 -0
- package/src/daemon/message-types/meet.ts +143 -0
- package/src/daemon/message-types/messages.ts +19 -5
- package/src/daemon/message-types/schedules.ts +34 -2
- package/src/daemon/message-types/skills.ts +26 -0
- package/src/daemon/message-types/subagents.ts +2 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/server.ts +439 -14
- package/src/daemon/shutdown-handlers.ts +32 -4
- package/src/daemon/shutdown-registry.ts +40 -0
- package/src/daemon/tool-side-effects.ts +15 -0
- package/src/daemon/transport-hints.ts +5 -24
- package/src/email/html-renderer.ts +76 -0
- package/src/heartbeat/heartbeat-service.ts +93 -7
- package/src/home/__tests__/assistant-feed-authoring.test.ts +156 -0
- package/src/home/__tests__/emit-feed-event.test.ts +169 -0
- package/src/home/__tests__/feed-scheduler.test.ts +194 -0
- package/src/home/__tests__/feed-types.test.ts +275 -0
- package/src/home/__tests__/feed-writer.test.ts +688 -0
- package/src/home/__tests__/phase5-exit-criteria.test.ts +212 -0
- package/src/home/__tests__/platform-gmail-digest.test.ts +222 -0
- package/src/home/__tests__/progress-formula.test.ts +213 -0
- package/src/home/__tests__/relationship-state-writer.test.ts +740 -0
- package/src/home/__tests__/rollup-producer.test.ts +398 -0
- package/src/home/assistant-feed-authoring.ts +124 -0
- package/src/home/emit-feed-event.ts +158 -0
- package/src/home/feed-scheduler.ts +247 -0
- package/src/home/feed-types.ts +181 -0
- package/src/home/feed-writer.ts +469 -0
- package/src/home/platform-gmail-digest.ts +163 -0
- package/src/home/progress-formula.ts +86 -0
- package/src/home/relationship-state-writer.ts +824 -0
- package/src/home/relationship-state.ts +143 -0
- package/src/home/rollup-producer.ts +384 -0
- package/src/hooks/runner.ts +7 -0
- package/src/inbound/platform-callback-registration.ts +30 -20
- package/src/inbound/public-ingress-urls.ts +12 -0
- package/src/instrument.ts +1 -1
- package/src/ipc/__tests__/cli-ipc.test.ts +200 -0
- package/src/ipc/cli-client.ts +151 -0
- package/src/ipc/cli-server.ts +234 -0
- package/src/ipc/gateway-client.ts +180 -0
- package/src/ipc/routes/index.ts +5 -0
- package/src/ipc/routes/wake-conversation.ts +19 -0
- package/src/mcp/client.ts +59 -24
- package/src/memory/__tests__/auto-analysis-enqueue.test.ts +356 -0
- package/src/memory/__tests__/auto-analysis-guard.test.ts +57 -0
- package/src/memory/__tests__/conversation-analyze-job.test.ts +232 -0
- package/src/memory/__tests__/find-analysis-conversation.test.ts +196 -0
- package/src/memory/app-store.ts +31 -1
- package/src/memory/attachments-store.ts +70 -0
- package/src/memory/auto-analysis-enqueue.ts +127 -0
- package/src/memory/auto-analysis-guard.ts +27 -0
- package/src/memory/cleanup-schedule-state.ts +37 -0
- package/src/memory/conversation-analyze-job.ts +73 -0
- package/src/memory/conversation-crud.ts +122 -0
- package/src/memory/conversation-disk-view.ts +7 -0
- package/src/memory/conversation-group-migration.ts +34 -2
- package/src/memory/conversation-queries.ts +6 -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 +18 -0
- package/src/memory/db-maintenance.ts +108 -0
- package/src/memory/db.ts +1 -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 +176 -17
- package/src/memory/graph/consolidation.ts +10 -23
- package/src/memory/graph/conversation-graph-memory.ts +15 -0
- package/src/memory/graph/extraction-job.ts +15 -0
- package/src/memory/graph/extraction.test.ts +23 -0
- package/src/memory/graph/extraction.ts +8 -0
- package/src/memory/graph/retriever.ts +67 -40
- package/src/memory/graph/scoring.test.ts +186 -0
- package/src/memory/graph/scoring.ts +31 -1
- package/src/memory/graph/store.test.ts +7 -3
- package/src/memory/graph/store.ts +47 -12
- package/src/memory/graph/tools.ts +1 -1
- package/src/memory/group-crud.ts +6 -1
- package/src/memory/indexer.ts +95 -16
- package/src/memory/job-handlers/cleanup.ts +11 -8
- package/src/memory/job-handlers/conversation-starters.ts +16 -10
- package/src/memory/jobs-store.ts +64 -4
- package/src/memory/jobs-worker.ts +22 -9
- package/src/memory/llm-usage-store.ts +137 -60
- 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/219-oauth-providers-token-exchange-body-format.ts +15 -0
- package/src/memory/migrations/220-normalize-user-file-by-principal.ts +190 -0
- package/src/memory/migrations/221-conversations-archived-at.ts +16 -0
- package/src/memory/migrations/index.ts +12 -0
- package/src/memory/migrations/registry.ts +16 -0
- package/src/memory/qdrant-manager.ts +43 -16
- package/src/memory/schema/conversations.ts +3 -0
- package/src/memory/schema/oauth.ts +21 -13
- package/src/memory/usage-buckets.ts +396 -0
- package/src/messaging/providers/gmail/client.ts +57 -6
- package/src/messaging/providers/slack/__tests__/adapter-token-routing.test.ts +282 -0
- package/src/messaging/providers/slack/adapter.ts +143 -38
- package/src/messaging/providers/slack/client.ts +16 -0
- package/src/messaging/providers/slack/types.ts +4 -0
- package/src/notifications/decision-engine.ts +3 -3
- package/src/notifications/signal.ts +5 -0
- package/src/oauth/AGENTS.md +76 -0
- package/src/oauth/__tests__/identity-verifier.test.ts +25 -19
- package/src/oauth/__tests__/seed-providers-managed.test.ts +32 -0
- package/src/oauth/byo-connection.test.ts +26 -9
- package/src/oauth/byo-connection.ts +10 -8
- package/src/oauth/connect-orchestrator.ts +25 -21
- package/src/oauth/connect-types.ts +3 -3
- package/src/oauth/connection-resolver.test.ts +17 -4
- package/src/oauth/connection-resolver.ts +22 -18
- package/src/oauth/connection.ts +3 -1
- package/src/oauth/manual-token-connection.ts +13 -13
- package/src/oauth/oauth-store.ts +223 -100
- package/src/oauth/platform-connection.test.ts +101 -3
- package/src/oauth/platform-connection.ts +56 -35
- package/src/oauth/provider-serializer.ts +31 -5
- package/src/oauth/revoke.ts +76 -0
- package/src/oauth/seed-providers.ts +133 -87
- package/src/oauth/token-persistence.ts +1 -1
- package/src/permissions/checker.ts +16 -6
- package/src/permissions/defaults.ts +49 -1
- package/src/permissions/permission-mode.ts +4 -11
- package/src/permissions/prompter.ts +13 -1
- package/src/permissions/trust-store.ts +3 -3
- package/src/permissions/v2-consent-policy.ts +87 -0
- package/src/permissions/workspace-policy.ts +3 -0
- package/src/platform/client.test.ts +10 -0
- package/src/platform/sync-identity.ts +129 -0
- package/src/prompts/persona-resolver.ts +126 -2
- package/src/prompts/system-prompt.ts +76 -38
- package/src/prompts/templates/BOOTSTRAP-REFERENCE.md +3 -65
- package/src/prompts/templates/BOOTSTRAP.md +59 -105
- package/src/prompts/templates/SOUL.md +3 -1
- package/src/prompts/templates/UPDATES.md +12 -0
- package/src/prompts/templates/channels/slack.md +20 -0
- package/src/prompts/update-bulletin-format.ts +26 -9
- package/src/prompts/update-bulletin.ts +34 -23
- package/src/prompts/user-reference.ts +20 -17
- package/src/providers/__tests__/provider-secret-catalog.test.ts +42 -0
- package/src/providers/anthropic/client.ts +157 -60
- package/src/providers/fireworks/client.ts +2 -2
- package/src/providers/gemini/client.ts +9 -1
- package/src/providers/model-catalog.ts +6 -0
- package/src/providers/model-intents.ts +4 -4
- package/src/providers/ollama/client.ts +2 -2
- package/src/providers/openai/chat-completions-provider.ts +474 -0
- package/src/providers/openai/client.ts +25 -440
- package/src/providers/openai/responses-provider.ts +502 -0
- package/src/providers/openrouter/client.ts +101 -4
- package/src/providers/provider-secret-catalog.ts +139 -0
- package/src/providers/registry.ts +2 -2
- package/src/providers/retry.ts +14 -3
- package/src/providers/speech-to-text/__tests__/provider-catalog.test.ts +251 -0
- package/src/providers/speech-to-text/__tests__/resolve.test.ts +828 -0
- package/src/providers/speech-to-text/deepgram-realtime.test.ts +980 -0
- package/src/providers/speech-to-text/deepgram-realtime.ts +767 -0
- package/src/providers/speech-to-text/deepgram.test.ts +332 -0
- package/src/providers/speech-to-text/deepgram.ts +115 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.test.ts +743 -0
- package/src/providers/speech-to-text/google-gemini-live-stream.ts +625 -0
- package/src/providers/speech-to-text/google-gemini.test.ts +226 -0
- package/src/providers/speech-to-text/google-gemini.ts +101 -0
- package/src/providers/speech-to-text/openai-whisper-stream.test.ts +564 -0
- package/src/providers/speech-to-text/openai-whisper-stream.ts +381 -0
- package/src/providers/speech-to-text/openai-whisper.test.ts +1 -37
- package/src/providers/speech-to-text/openai-whisper.ts +63 -33
- package/src/providers/speech-to-text/provider-catalog.ts +306 -0
- package/src/providers/speech-to-text/resolve.ts +386 -6
- package/src/providers/types.ts +10 -1
- package/src/runtime/AGENTS.md +65 -0
- package/src/runtime/__tests__/agent-wake.test.ts +831 -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/__tests__/runtime-mode.test.ts +62 -0
- package/src/runtime/__tests__/slack-block-formatting.test.ts +481 -0
- package/src/runtime/agent-wake.ts +512 -0
- package/src/runtime/assistant-event-hub.ts +2 -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 +48 -0
- package/src/runtime/auth/middleware.ts +98 -0
- package/src/runtime/auth/route-policy.ts +33 -9
- package/src/runtime/auth/token-service.ts +56 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/capability-tokens.ts +414 -0
- package/src/runtime/channel-approvals.ts +18 -5
- package/src/runtime/channel-invite-transport.ts +1 -1
- package/src/runtime/channel-invite-transports/email.ts +14 -6
- package/src/runtime/channel-readiness-service.ts +12 -22
- package/src/runtime/chrome-extension-registry.ts +368 -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 +815 -75
- package/src/runtime/http-types.ts +6 -2
- 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 +198 -0
- package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
- package/src/runtime/migrations/migration-transport.ts +7 -0
- package/src/runtime/migrations/migration-wizard.ts +23 -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 +96 -1
- package/src/runtime/migrations/vbundle-importer.ts +89 -5
- package/src/runtime/pending-interactions.ts +18 -13
- package/src/runtime/routes/__tests__/backup-routes.test.ts +967 -0
- package/src/runtime/routes/__tests__/home-feed-routes.test.ts +507 -0
- package/src/runtime/routes/__tests__/migration-import-credential-filter.test.ts +208 -0
- package/src/runtime/routes/__tests__/stt-routes.test.ts +406 -0
- package/src/runtime/routes/__tests__/tts-routes.test.ts +474 -0
- package/src/runtime/routes/__tests__/user-route-dispatcher.test.ts +148 -17
- package/src/runtime/routes/app-management-routes.ts +12 -18
- package/src/runtime/routes/approval-routes.ts +90 -16
- package/src/runtime/routes/attachment-routes.test.ts +9 -3
- package/src/runtime/routes/attachment-routes.ts +216 -17
- package/src/runtime/routes/backup-routes.ts +519 -0
- package/src/runtime/routes/browser-extension-pair-routes.ts +556 -0
- package/src/runtime/routes/btw-routes.ts +8 -6
- package/src/runtime/routes/contact-routes.test.ts +298 -0
- package/src/runtime/routes/contact-routes.ts +132 -5
- package/src/runtime/routes/conversation-analysis-routes.ts +22 -141
- package/src/runtime/routes/conversation-management-routes.ts +223 -0
- package/src/runtime/routes/conversation-routes.ts +598 -103
- package/src/runtime/routes/conversation-starter-routes.ts +78 -16
- package/src/runtime/routes/filing-routes.ts +93 -0
- package/src/runtime/routes/guardian-action-routes.ts +24 -13
- package/src/runtime/routes/home-feed-routes.ts +334 -0
- package/src/runtime/routes/home-state-routes.ts +138 -0
- package/src/runtime/routes/host-browser-routes.ts +268 -0
- package/src/runtime/routes/host-file-routes.ts +9 -1
- package/src/runtime/routes/identity-intro-cache.ts +7 -3
- package/src/runtime/routes/identity-routes.ts +262 -33
- package/src/runtime/routes/inbound-stages/transcribe-audio.test.ts +46 -39
- package/src/runtime/routes/inbound-stages/transcribe-audio.ts +15 -15
- package/src/runtime/routes/integrations/slack/__tests__/channel.test.ts +137 -0
- package/src/runtime/routes/integrations/slack/__tests__/share.test.ts +179 -0
- package/src/runtime/routes/integrations/slack/channel.ts +11 -3
- package/src/runtime/routes/integrations/slack/share.ts +45 -7
- package/src/runtime/routes/llm-context-normalization.ts +303 -0
- package/src/runtime/routes/log-export-routes.ts +42 -22
- package/src/runtime/routes/memory-item-routes.test.ts +3 -2
- package/src/runtime/routes/memory-item-routes.ts +1 -7
- package/src/runtime/routes/migration-routes.ts +122 -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 +31 -102
- package/src/runtime/routes/skills-routes.ts +128 -9
- package/src/runtime/routes/stt-routes.ts +233 -0
- package/src/runtime/routes/subagents-routes.ts +14 -10
- package/src/runtime/routes/surface-action-routes.ts +41 -2
- package/src/runtime/routes/tts-routes.ts +108 -24
- package/src/runtime/routes/usage-routes.ts +38 -9
- package/src/runtime/routes/user-route-dispatcher.ts +50 -5
- package/src/runtime/routes/user-routes.ts +13 -1
- package/src/runtime/routes/work-items-routes.ts +8 -1
- 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/runtime/runtime-mode.ts +33 -0
- package/src/runtime/services/__tests__/analyze-conversation.test.ts +444 -0
- package/src/runtime/services/__tests__/analyze-deps-singleton.test.ts +67 -0
- package/src/runtime/services/__tests__/auto-analysis-prompt.test.ts +53 -0
- package/src/runtime/services/__tests__/manual-analysis-prompt.test.ts +41 -0
- package/src/runtime/services/analyze-conversation.ts +344 -0
- package/src/runtime/services/analyze-deps-singleton.ts +32 -0
- package/src/runtime/services/auto-analysis-prompt.ts +55 -0
- package/src/runtime/skill-route-registry.ts +49 -0
- package/src/runtime/slack-block-formatting.ts +437 -10
- package/src/schedule/scheduler.ts +57 -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 +68 -29
- package/src/security/secure-keys.ts +143 -27
- package/src/security/token-manager.ts +31 -10
- package/src/sequence/engine.ts +23 -0
- package/src/sequence/types.ts +1 -1
- package/src/skills/catalog-files.ts +554 -0
- package/src/skills/category-inference.ts +122 -0
- package/src/skills/clawhub-files.ts +213 -0
- package/src/skills/clawhub.ts +84 -23
- package/src/skills/skill-file-provider.ts +40 -0
- package/src/skills/skillssh-files.ts +395 -0
- package/src/skills/skillssh-registry.ts +4 -4
- package/src/stt/__tests__/daemon-batch-transcriber.test.ts +392 -0
- package/src/stt/__tests__/types.test.ts +89 -0
- package/src/stt/daemon-batch-transcriber.ts +195 -0
- package/src/stt/stt-stream-session.ts +499 -0
- package/src/stt/types.ts +330 -0
- package/src/stt/wav-encoder.test.ts +373 -0
- package/src/stt/wav-encoder.ts +175 -0
- package/src/subagent/manager.ts +169 -40
- 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/__tests__/browser-mode.test.ts +119 -0
- package/src/tools/browser/__tests__/browser-status.test.ts +123 -0
- package/src/tools/browser/auth-detector.ts +43 -12
- package/src/tools/browser/browser-execution.ts +1787 -342
- package/src/tools/browser/browser-manager.ts +81 -12
- package/src/tools/browser/browser-mode-constants.ts +12 -0
- package/src/tools/browser/browser-mode.ts +92 -0
- package/src/tools/browser/browser-status-constants.ts +33 -0
- 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 +1263 -0
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +359 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1993 -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 +1007 -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 +744 -0
- package/src/tools/browser/cdp-client/cdp-inspect/ws-transport.ts +579 -0
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +868 -0
- package/src/tools/browser/cdp-client/errors.ts +49 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +148 -0
- package/src/tools/browser/cdp-client/factory.ts +914 -0
- package/src/tools/browser/cdp-client/index.ts +28 -0
- package/src/tools/browser/cdp-client/local-cdp-client.ts +187 -0
- package/src/tools/browser/cdp-client/types.ts +120 -0
- package/src/tools/credentials/vault.ts +35 -6
- 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/network/web-fetch.ts +5 -2
- package/src/tools/network/web-search.ts +5 -2
- package/src/tools/permission-checker.ts +77 -82
- package/src/tools/registry.ts +0 -2
- package/src/tools/secret-detection-handler.ts +34 -0
- package/src/tools/shared/filesystem/image-read.ts +61 -40
- package/src/tools/shared/shell-output.ts +3 -1
- package/src/tools/side-effects.ts +2 -0
- package/src/tools/skills/sandbox-runner.ts +3 -2
- 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 +15 -0
- package/src/tools/terminal/shell.ts +36 -20
- package/src/tools/tool-approval-handler.ts +48 -2
- package/src/tools/tool-manifest.ts +21 -0
- package/src/tools/types.ts +19 -0
- package/src/tools/ui-surface/definitions.ts +6 -1
- package/src/tts/__tests__/provider-adapters.test.ts +834 -0
- package/src/tts/__tests__/provider-catalog-consistency.test.ts +196 -0
- package/src/tts/__tests__/provider-catalog.test.ts +183 -0
- package/src/tts/__tests__/provider-registry.test.ts +90 -0
- package/src/tts/provider-catalog.ts +201 -0
- package/src/tts/provider-registry.ts +73 -0
- package/src/tts/providers/deepgram-provider.ts +219 -0
- package/src/tts/providers/elevenlabs-provider.ts +211 -0
- package/src/tts/providers/fish-audio-provider.ts +183 -0
- package/src/tts/providers/index.ts +42 -0
- package/src/tts/providers/register-builtins.ts +130 -0
- package/src/tts/synthesize-text.ts +110 -0
- package/src/tts/tts-config-resolver.ts +78 -0
- package/src/tts/types.ts +153 -0
- package/src/types/onboarding-context.ts +7 -0
- package/src/util/abort-reasons.ts +58 -0
- package/src/util/device-id.ts +32 -16
- package/src/util/errors.ts +9 -1
- package/src/util/platform.ts +63 -24
- package/src/util/pricing.ts +66 -3
- package/src/util/spawn.ts +1 -1
- package/src/util/truncate.ts +4 -2
- package/src/util/unicode.ts +201 -0
- package/src/version.ts +19 -24
- package/src/watcher/engine.ts +23 -0
- package/src/watcher/watcher-store.ts +31 -0
- package/src/workspace/migrations/003-seed-device-id.ts +9 -3
- package/src/workspace/migrations/017-seed-persona-dirs.ts +68 -4
- package/src/workspace/migrations/029-seed-pkb.ts +1 -1
- package/src/workspace/migrations/031-drop-user-md.ts +317 -0
- package/src/workspace/migrations/031-llm-log-retention-zero-to-null.ts +73 -0
- package/src/workspace/migrations/032-tts-provider-unification.ts +227 -0
- package/src/workspace/migrations/033-stt-service-explicit-config.ts +122 -0
- package/src/workspace/migrations/034-remove-calls-voice-transcription-provider.ts +215 -0
- package/src/workspace/migrations/035-seed-slack-channel-persona.ts +50 -0
- package/src/workspace/migrations/036-update-pkb-index-bar.ts +37 -0
- package/src/workspace/migrations/037-create-meets-dir.ts +61 -0
- package/src/workspace/migrations/registry.ts +16 -0
- package/src/workspace/top-level-renderer.ts +31 -1
- package/src/workspace/turn-commit.ts +31 -0
- package/src/__tests__/chrome-cdp.test.ts +0 -419
- package/src/__tests__/email-cli.test.ts +0 -297
- package/src/__tests__/email-service-config-fallback.test.ts +0 -102
- 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/cli/commands/browser-relay.ts +0 -536
- package/src/config/schemas/sandbox.ts +0 -14
- package/src/email/guardrails.ts +0 -221
- package/src/email/provider.ts +0 -117
- package/src/email/providers/agentmail.ts +0 -361
- package/src/email/providers/index.ts +0 -65
- package/src/email/service.ts +0 -384
- package/src/email/types.ts +0 -126
- package/src/permissions/permission-mode-store.ts +0 -180
- package/src/prompts/templates/USER.md +0 -13
- package/src/providers/speech-to-text/types.ts +0 -17
- package/src/tools/browser/chrome-cdp.ts +0 -239
- package/src/tools/system/set-permission-mode.ts +0 -103
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backup key management.
|
|
3
|
+
*
|
|
4
|
+
* The backup key is a 32-byte random secret used to authenticate / encrypt
|
|
5
|
+
* workspace backups. It is generated once per install and persisted to disk
|
|
6
|
+
* so subsequent backup/restore operations reuse the same key.
|
|
7
|
+
*
|
|
8
|
+
* This module is intentionally pure: callers pass the full `keyPath` rather
|
|
9
|
+
* than resolving a default location. That keeps the helpers trivially
|
|
10
|
+
* testable against temp directories and avoids any coupling to daemon
|
|
11
|
+
* startup, workspace layout, or global path helpers.
|
|
12
|
+
*
|
|
13
|
+
* On-disk invariants:
|
|
14
|
+
* - Parent directory is created with mode `0o700`.
|
|
15
|
+
* - Key file is written atomically (temp + `link`) with mode `0o600`, so
|
|
16
|
+
* concurrent callers converge on the first winner's bytes.
|
|
17
|
+
* - Key file is exactly 32 bytes; any other size is treated as corruption.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { randomBytes } from "node:crypto";
|
|
21
|
+
import {
|
|
22
|
+
chmod,
|
|
23
|
+
link,
|
|
24
|
+
mkdir,
|
|
25
|
+
readFile,
|
|
26
|
+
stat,
|
|
27
|
+
unlink,
|
|
28
|
+
writeFile,
|
|
29
|
+
} from "node:fs/promises";
|
|
30
|
+
import { dirname } from "node:path";
|
|
31
|
+
|
|
32
|
+
/** Required length of the backup key file, in bytes. */
|
|
33
|
+
const BACKUP_KEY_LENGTH = 32;
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Check whether a filesystem path exists without throwing.
|
|
37
|
+
*
|
|
38
|
+
* Only `ENOENT` is treated as "missing". Any other errno (EIO, ESTALE,
|
|
39
|
+
* EACCES, ...) is rethrown — we must not silently treat a transient I/O
|
|
40
|
+
* failure as "file is absent" because that can cause an existing backup
|
|
41
|
+
* key to be rotated away under the caller's feet, breaking decryption of
|
|
42
|
+
* data encrypted with the prior key.
|
|
43
|
+
*/
|
|
44
|
+
async function pathExists(path: string): Promise<boolean> {
|
|
45
|
+
try {
|
|
46
|
+
await stat(path);
|
|
47
|
+
return true;
|
|
48
|
+
} catch (err) {
|
|
49
|
+
if ((err as NodeJS.ErrnoException)?.code === "ENOENT") return false;
|
|
50
|
+
throw err;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Read the backup key from disk if it exists.
|
|
56
|
+
*
|
|
57
|
+
* Returns the raw 32-byte buffer, or `null` if the file is missing. Intended
|
|
58
|
+
* for read-only callers (e.g. restore paths) that should not create a new
|
|
59
|
+
* key as a side effect.
|
|
60
|
+
*
|
|
61
|
+
* Throws if the file exists but is not exactly 32 bytes -- callers should
|
|
62
|
+
* treat that as a corruption signal rather than silently regenerating.
|
|
63
|
+
*/
|
|
64
|
+
export async function readBackupKey(keyPath: string): Promise<Buffer | null> {
|
|
65
|
+
if (!(await pathExists(keyPath))) return null;
|
|
66
|
+
const buf = await readFile(keyPath);
|
|
67
|
+
if (buf.length !== BACKUP_KEY_LENGTH) {
|
|
68
|
+
throw new Error(
|
|
69
|
+
`Backup key at ${keyPath} has invalid length ${buf.length} (expected ${BACKUP_KEY_LENGTH})`,
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
return buf;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Ensure a backup key exists at `keyPath`, returning its bytes.
|
|
77
|
+
*
|
|
78
|
+
* - If the file exists, it is read and validated. A wrong-size file throws,
|
|
79
|
+
* so a corrupt key is never silently replaced.
|
|
80
|
+
* - Otherwise, the parent directory is created (mode `0o700`), a fresh
|
|
81
|
+
* 32-byte random key is generated, written to a unique tmp file, and
|
|
82
|
+
* atomically published to `keyPath` via `link()`.
|
|
83
|
+
*
|
|
84
|
+
* Concurrency: callers that race here must all converge on the same bytes
|
|
85
|
+
* — otherwise one caller encrypts data with bytes that will never be
|
|
86
|
+
* persisted and can never be decrypted.
|
|
87
|
+
*
|
|
88
|
+
* We use the canonical Unix atomic-create idiom: write full contents to
|
|
89
|
+
* a per-call tmp file, then `link(tmp, keyPath)`. `link` fails with
|
|
90
|
+
* `EEXIST` if `keyPath` already exists, which makes exactly one racing
|
|
91
|
+
* caller the winner; the rest read the winner's bytes. `rename(2)` by
|
|
92
|
+
* contrast overwrites the destination and is not race-safe here — two
|
|
93
|
+
* renames can leave either caller's bytes on disk regardless of who
|
|
94
|
+
* generated them, so a lost caller would return bytes that don't match
|
|
95
|
+
* what's persisted. `link` avoids that entirely.
|
|
96
|
+
*/
|
|
97
|
+
export async function ensureBackupKey(keyPath: string): Promise<Buffer> {
|
|
98
|
+
const existing = await readBackupKey(keyPath);
|
|
99
|
+
if (existing) return existing;
|
|
100
|
+
|
|
101
|
+
const parent = dirname(keyPath);
|
|
102
|
+
await mkdir(parent, { recursive: true, mode: 0o700 });
|
|
103
|
+
|
|
104
|
+
const key = randomBytes(BACKUP_KEY_LENGTH);
|
|
105
|
+
const tmpPath = `${keyPath}.tmp.${process.pid}.${randomBytes(8).toString("hex")}`;
|
|
106
|
+
try {
|
|
107
|
+
// `wx` fails if tmpPath somehow exists (stale orphan or collision) so
|
|
108
|
+
// we never silently overwrite another writer's in-flight tmp file.
|
|
109
|
+
await writeFile(tmpPath, key, { flag: "wx", mode: 0o600 });
|
|
110
|
+
// Some platforms / umasks ignore the `mode` option on writeFile, so
|
|
111
|
+
// enforce 0o600 explicitly before publishing.
|
|
112
|
+
await chmod(tmpPath, 0o600);
|
|
113
|
+
try {
|
|
114
|
+
// Atomic publish: only one racing caller's link() succeeds.
|
|
115
|
+
await link(tmpPath, keyPath);
|
|
116
|
+
return key;
|
|
117
|
+
} catch (err) {
|
|
118
|
+
if ((err as NodeJS.ErrnoException)?.code !== "EEXIST") throw err;
|
|
119
|
+
// Another caller won the race. Return their bytes, not ours.
|
|
120
|
+
const winner = await readBackupKey(keyPath);
|
|
121
|
+
if (!winner) {
|
|
122
|
+
throw new Error(
|
|
123
|
+
`link() reported EEXIST but ${keyPath} is unreadable`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
return winner;
|
|
127
|
+
}
|
|
128
|
+
} finally {
|
|
129
|
+
// Remove our tmp file whether we won (tmp is a hard link to keyPath,
|
|
130
|
+
// safe to unlink), lost, or errored. Best-effort.
|
|
131
|
+
try {
|
|
132
|
+
await unlink(tmpPath);
|
|
133
|
+
} catch {
|
|
134
|
+
// ignore
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,459 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Periodic backup worker.
|
|
3
|
+
*
|
|
4
|
+
* Drives the backup pipeline on a 5-minute tick interval. On each tick it
|
|
5
|
+
* checks whether `config.enabled` is true and whether enough time has passed
|
|
6
|
+
* since the last successful run; if so, it builds a workspace vbundle,
|
|
7
|
+
* writes it to the local backup directory, mirrors it to every configured
|
|
8
|
+
* offsite destination, applies retention to each pool, and records the run
|
|
9
|
+
* timestamp in the memory checkpoint store.
|
|
10
|
+
*
|
|
11
|
+
* The public surface is intentionally split into three layers:
|
|
12
|
+
*
|
|
13
|
+
* - `startBackupWorker` — installs the `setInterval` and returns a handle
|
|
14
|
+
* with `stop()` and `runOnce()`. Must never throw during startup (daemon
|
|
15
|
+
* startup philosophy); any failure during setup falls back to a no-op
|
|
16
|
+
* handle and logs the error.
|
|
17
|
+
* - `runBackupTick` — the pure tick body. Gates on `enabled` + interval +
|
|
18
|
+
* mutex, then delegates to `performBackup`. Propagates errors so callers
|
|
19
|
+
* (tests, the interval wrapper) can observe failures.
|
|
20
|
+
* - `createSnapshotNow` — manual-trigger variant. Bypasses the enabled and
|
|
21
|
+
* interval checks, but still honors the concurrency mutex (so a manual
|
|
22
|
+
* trigger will reject with "snapshot in progress" if one is in flight).
|
|
23
|
+
*
|
|
24
|
+
* Everything that touches real daemon state (DB, workspace, filesystem) is
|
|
25
|
+
* injected through the `BackupDeps` shape so tests can drive the whole
|
|
26
|
+
* surface against temp directories with tiny fake bundles.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { join } from "node:path";
|
|
30
|
+
import { Database } from "bun:sqlite";
|
|
31
|
+
|
|
32
|
+
import { getConfig } from "../config/loader.js";
|
|
33
|
+
import type { BackupConfig } from "../config/schema.js";
|
|
34
|
+
import {
|
|
35
|
+
getMemoryCheckpoint as realGetMemoryCheckpoint,
|
|
36
|
+
setMemoryCheckpoint as realSetMemoryCheckpoint,
|
|
37
|
+
} from "../memory/checkpoints.js";
|
|
38
|
+
import type { StreamExportVBundleResult } from "../runtime/migrations/vbundle-builder.js";
|
|
39
|
+
import { streamExportVBundle as realStreamExportVBundle } from "../runtime/migrations/vbundle-builder.js";
|
|
40
|
+
import { getLogger } from "../util/logger.js";
|
|
41
|
+
import {
|
|
42
|
+
getDbPath,
|
|
43
|
+
getProtectedDir,
|
|
44
|
+
getWorkspaceDir,
|
|
45
|
+
getWorkspaceHooksDir,
|
|
46
|
+
} from "../util/platform.js";
|
|
47
|
+
import { ensureBackupKey as realEnsureBackupKey } from "./backup-key.js";
|
|
48
|
+
import type { SnapshotEntry } from "./list-snapshots.js";
|
|
49
|
+
import {
|
|
50
|
+
pruneLocalSnapshots,
|
|
51
|
+
writeLocalSnapshot,
|
|
52
|
+
} from "./local-writer.js";
|
|
53
|
+
import type { OffsiteWriteResult } from "./offsite-writer.js";
|
|
54
|
+
import {
|
|
55
|
+
pruneOffsiteSnapshotsInAll,
|
|
56
|
+
writeOffsiteSnapshotToAll,
|
|
57
|
+
} from "./offsite-writer.js";
|
|
58
|
+
import {
|
|
59
|
+
getBackupKeyPath,
|
|
60
|
+
getLocalBackupsDir,
|
|
61
|
+
resolveOffsiteDestinations,
|
|
62
|
+
} from "./paths.js";
|
|
63
|
+
import {
|
|
64
|
+
acquireSnapshotLock,
|
|
65
|
+
getSnapshotLockPath,
|
|
66
|
+
} from "./snapshot-lock.js";
|
|
67
|
+
|
|
68
|
+
const log = getLogger("backup-worker");
|
|
69
|
+
|
|
70
|
+
/** Memory checkpoint key for the last successful backup run timestamp. */
|
|
71
|
+
const LAST_RUN_CHECKPOINT_KEY = "backup:last_run_at";
|
|
72
|
+
|
|
73
|
+
/** Default tick interval — fires every 5 minutes, gated by interval check. */
|
|
74
|
+
const TICK_INTERVAL_MS = 5 * 60 * 1000;
|
|
75
|
+
|
|
76
|
+
// ---------------------------------------------------------------------------
|
|
77
|
+
// Public types
|
|
78
|
+
// ---------------------------------------------------------------------------
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Result of a single backup run. `offsite` is an array with one entry per
|
|
82
|
+
* destination so callers can inspect per-destination success / skip / error
|
|
83
|
+
* status — a single missing offsite volume does not poison the whole run.
|
|
84
|
+
*/
|
|
85
|
+
export interface BackupRunResult {
|
|
86
|
+
local: SnapshotEntry;
|
|
87
|
+
offsite: OffsiteWriteResult[];
|
|
88
|
+
durationMs: number;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Opaque handle returned by `startBackupWorker`. Callers drive the worker
|
|
93
|
+
* exclusively through this handle; the underlying timer and mutex state are
|
|
94
|
+
* module-scoped implementation details.
|
|
95
|
+
*/
|
|
96
|
+
export interface BackupWorkerHandle {
|
|
97
|
+
stop(): void;
|
|
98
|
+
runOnce(): Promise<BackupRunResult | null>;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Dependency injection bag for `runBackupTick` / `createSnapshotNow`.
|
|
103
|
+
*
|
|
104
|
+
* In production the defaults wire up the real DB, workspace, and memory
|
|
105
|
+
* checkpoint store. Tests inject fakes so they can drive the worker
|
|
106
|
+
* against temp directories with in-memory checkpoint state.
|
|
107
|
+
*/
|
|
108
|
+
export interface BackupDeps {
|
|
109
|
+
streamExportVBundle?: (
|
|
110
|
+
options: Parameters<typeof realStreamExportVBundle>[0],
|
|
111
|
+
) => Promise<StreamExportVBundleResult>;
|
|
112
|
+
getMemoryCheckpoint?: (key: string) => string | null;
|
|
113
|
+
setMemoryCheckpoint?: (key: string, value: string) => void;
|
|
114
|
+
ensureBackupKey?: (path: string) => Promise<Buffer>;
|
|
115
|
+
/** Override for the workspace directory (tests). */
|
|
116
|
+
workspaceDir?: string;
|
|
117
|
+
/** Override for the local backup directory (tests). */
|
|
118
|
+
localDir?: string;
|
|
119
|
+
/** Override for the trust.json path (tests). */
|
|
120
|
+
trustPath?: string;
|
|
121
|
+
/** Override for the hooks directory (tests). */
|
|
122
|
+
hooksDir?: string;
|
|
123
|
+
/** Override for the backup key file path (tests). */
|
|
124
|
+
backupKeyPath?: string;
|
|
125
|
+
/**
|
|
126
|
+
* Override for the cross-process snapshot lock file path (tests). Defaults
|
|
127
|
+
* to `getSnapshotLockPath()` in production. Tests that drive multiple
|
|
128
|
+
* parallel runs against temp directories inject a path under their fixture
|
|
129
|
+
* root so they don't collide with each other or with the real daemon.
|
|
130
|
+
*/
|
|
131
|
+
snapshotLockPath?: string;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
// Concurrency mutex (two layers)
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* In-memory mutex flag — the first line of defense. Protects the current
|
|
140
|
+
* process from racing itself:
|
|
141
|
+
* - A scheduled tick that fires while a manual run is in flight skips silently.
|
|
142
|
+
* - A manual run that starts while a scheduled tick is running throws so the
|
|
143
|
+
* user can decide how to react (retry, wait, surface the conflict).
|
|
144
|
+
*
|
|
145
|
+
* The in-process flag is module-scoped rather than tied to the handle
|
|
146
|
+
* returned by `startBackupWorker` — the daemon may start the worker once at
|
|
147
|
+
* boot and also call `createSnapshotNow` from other code paths, and both must
|
|
148
|
+
* see the same concurrency state.
|
|
149
|
+
*
|
|
150
|
+
* Beyond the in-process flag, both entry points also acquire a cross-process
|
|
151
|
+
* file lock at `getSnapshotLockPath()` so a CLI `vellum backup create` run
|
|
152
|
+
* cannot race the daemon's periodic tick — they would otherwise hold
|
|
153
|
+
* independent copies of this module-scoped flag. See `./snapshot-lock.ts`.
|
|
154
|
+
* The in-process flag stays as a fast path so same-process conflicts skip
|
|
155
|
+
* the filesystem entirely.
|
|
156
|
+
*/
|
|
157
|
+
let snapshotInProgress = false;
|
|
158
|
+
|
|
159
|
+
// ---------------------------------------------------------------------------
|
|
160
|
+
// Core pipeline body
|
|
161
|
+
// ---------------------------------------------------------------------------
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* The shared body that both `runBackupTick` and `createSnapshotNow` call
|
|
165
|
+
* after their gating checks pass. Does not touch the mutex — callers are
|
|
166
|
+
* responsible for acquiring it.
|
|
167
|
+
*
|
|
168
|
+
* Pipeline:
|
|
169
|
+
* 1. Resolve offsite destinations (iCloud default if config did not
|
|
170
|
+
* specify an explicit array).
|
|
171
|
+
* 2. Load the backup key only if at least one destination needs it.
|
|
172
|
+
* Plaintext-only setups never touch the key file.
|
|
173
|
+
* 3. Stream the workspace into a temp .vbundle file, passing a WAL
|
|
174
|
+
* checkpoint callback so the exported DB has every committed row.
|
|
175
|
+
* 4. Move the temp file into the local backup directory (rename).
|
|
176
|
+
* After this point the temp file no longer exists, so we must not
|
|
177
|
+
* call the `cleanup()` callback on success.
|
|
178
|
+
* 5. Mirror the local file to every offsite destination (sequential).
|
|
179
|
+
* 6. Apply retention to the local pool and every offsite pool.
|
|
180
|
+
*/
|
|
181
|
+
async function performBackup(
|
|
182
|
+
config: BackupConfig,
|
|
183
|
+
now: Date,
|
|
184
|
+
deps: BackupDeps,
|
|
185
|
+
): Promise<BackupRunResult> {
|
|
186
|
+
const streamExport = deps.streamExportVBundle ?? realStreamExportVBundle;
|
|
187
|
+
const ensureKey = deps.ensureBackupKey ?? realEnsureBackupKey;
|
|
188
|
+
const workspaceDir = deps.workspaceDir ?? getWorkspaceDir();
|
|
189
|
+
const localDir = deps.localDir ?? getLocalBackupsDir(config.localDirectory);
|
|
190
|
+
const trustPath =
|
|
191
|
+
deps.trustPath ?? join(getProtectedDir(), "trust.json");
|
|
192
|
+
const hooksDir = deps.hooksDir ?? getWorkspaceHooksDir();
|
|
193
|
+
const backupKeyPath = deps.backupKeyPath ?? getBackupKeyPath();
|
|
194
|
+
|
|
195
|
+
const startTimestamp = Date.now();
|
|
196
|
+
|
|
197
|
+
const destinations = config.offsite.enabled
|
|
198
|
+
? resolveOffsiteDestinations(config.offsite.destinations)
|
|
199
|
+
: [];
|
|
200
|
+
const needsKey = destinations.some((d) => d.encrypt);
|
|
201
|
+
const key: Buffer | null = needsKey ? await ensureKey(backupKeyPath) : null;
|
|
202
|
+
|
|
203
|
+
// Build the vbundle into a temp file. Pass a WAL checkpoint callback that
|
|
204
|
+
// mirrors the pattern in `handleMigrationExport`: open a fresh Database
|
|
205
|
+
// handle, run PRAGMA wal_checkpoint(TRUNCATE), close it. Any failure is
|
|
206
|
+
// best-effort — the export still proceeds with whatever is on disk.
|
|
207
|
+
const result = await streamExport({
|
|
208
|
+
workspaceDir,
|
|
209
|
+
trustPath,
|
|
210
|
+
hooksDir,
|
|
211
|
+
source: "backup-worker",
|
|
212
|
+
description: "Automated backup snapshot",
|
|
213
|
+
checkpoint: () => {
|
|
214
|
+
const dbPath = getDbPath();
|
|
215
|
+
try {
|
|
216
|
+
const db = new Database(dbPath);
|
|
217
|
+
try {
|
|
218
|
+
db.exec("PRAGMA wal_checkpoint(TRUNCATE)");
|
|
219
|
+
} finally {
|
|
220
|
+
db.close();
|
|
221
|
+
}
|
|
222
|
+
} catch (err) {
|
|
223
|
+
log.warn(
|
|
224
|
+
{ err },
|
|
225
|
+
"WAL checkpoint failed — proceeding with backup without checkpoint",
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
const { tempPath, cleanup } = result;
|
|
232
|
+
|
|
233
|
+
// `writeLocalSnapshot` moves (renames) the temp file to its final
|
|
234
|
+
// location. On success the temp file no longer exists, so we MUST NOT
|
|
235
|
+
// call `cleanup()` afterwards — it would try to unlink a missing path.
|
|
236
|
+
// On failure we still need to unlink the temp file to avoid leaks.
|
|
237
|
+
let localResult: SnapshotEntry;
|
|
238
|
+
try {
|
|
239
|
+
localResult = await writeLocalSnapshot(tempPath, localDir, now);
|
|
240
|
+
} catch (err) {
|
|
241
|
+
try {
|
|
242
|
+
await cleanup();
|
|
243
|
+
} catch {
|
|
244
|
+
// best-effort
|
|
245
|
+
}
|
|
246
|
+
throw err;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const offsiteResults = await writeOffsiteSnapshotToAll(
|
|
250
|
+
localResult.path,
|
|
251
|
+
destinations,
|
|
252
|
+
key,
|
|
253
|
+
now,
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
// Apply retention to both pools. Retention is per-destination so a
|
|
257
|
+
// missing offsite volume doesn't skew the local pool's retention count.
|
|
258
|
+
await pruneLocalSnapshots(localDir, config.retention);
|
|
259
|
+
await pruneOffsiteSnapshotsInAll(destinations, config.retention);
|
|
260
|
+
|
|
261
|
+
log.info(
|
|
262
|
+
{
|
|
263
|
+
localPath: localResult.path,
|
|
264
|
+
offsite: offsiteResults.map((r) => ({
|
|
265
|
+
path: r.destination.path,
|
|
266
|
+
status: r.entry ? "ok" : r.skipped ? "skipped" : "error",
|
|
267
|
+
reason: r.skipped ?? r.error,
|
|
268
|
+
})),
|
|
269
|
+
},
|
|
270
|
+
"Backup snapshot complete",
|
|
271
|
+
);
|
|
272
|
+
|
|
273
|
+
return {
|
|
274
|
+
local: localResult,
|
|
275
|
+
offsite: offsiteResults,
|
|
276
|
+
durationMs: Date.now() - startTimestamp,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// ---------------------------------------------------------------------------
|
|
281
|
+
// Public entry points
|
|
282
|
+
// ---------------------------------------------------------------------------
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Pure tick body for the scheduled backup worker. Runs the enabled + interval
|
|
286
|
+
* gates, acquires the mutex, delegates to `performBackup`, and records the
|
|
287
|
+
* last-run timestamp on success. Returns `null` if any gate rejects the run.
|
|
288
|
+
*
|
|
289
|
+
* Errors from `performBackup` propagate — the `setInterval` caller wraps this
|
|
290
|
+
* in a try/catch that logs and swallows, so daemon startup and steady-state
|
|
291
|
+
* ticks never crash the process.
|
|
292
|
+
*/
|
|
293
|
+
export async function runBackupTick(
|
|
294
|
+
config: BackupConfig,
|
|
295
|
+
now: Date,
|
|
296
|
+
deps: BackupDeps = {},
|
|
297
|
+
): Promise<BackupRunResult | null> {
|
|
298
|
+
if (config.enabled !== true) return null;
|
|
299
|
+
|
|
300
|
+
const getCheckpoint = deps.getMemoryCheckpoint ?? realGetMemoryCheckpoint;
|
|
301
|
+
const setCheckpoint = deps.setMemoryCheckpoint ?? realSetMemoryCheckpoint;
|
|
302
|
+
const lockPath = deps.snapshotLockPath ?? getSnapshotLockPath();
|
|
303
|
+
|
|
304
|
+
const lastRunRaw = getCheckpoint(LAST_RUN_CHECKPOINT_KEY);
|
|
305
|
+
if (lastRunRaw != null) {
|
|
306
|
+
const lastRunMs = Number.parseInt(lastRunRaw, 10);
|
|
307
|
+
if (!Number.isNaN(lastRunMs)) {
|
|
308
|
+
const intervalMs = config.intervalHours * 3600 * 1000;
|
|
309
|
+
if (now.getTime() - lastRunMs < intervalMs) {
|
|
310
|
+
return null;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// A manual snapshot in flight inside this process wins — the scheduled
|
|
316
|
+
// tick silently defers and will reconsider on the next interval.
|
|
317
|
+
if (snapshotInProgress) return null;
|
|
318
|
+
snapshotInProgress = true;
|
|
319
|
+
|
|
320
|
+
// Acquire the cross-process lock after the in-process fast path passes.
|
|
321
|
+
// If another process (a CLI `vellum backup create`, say) holds the lock,
|
|
322
|
+
// the scheduled tick defers silently — same semantics as when the
|
|
323
|
+
// in-process flag is set — so a concurrent CLI run does not spam warning
|
|
324
|
+
// logs on every 5-minute tick.
|
|
325
|
+
let release: (() => Promise<void>) | null = null;
|
|
326
|
+
try {
|
|
327
|
+
release = await acquireSnapshotLock(lockPath);
|
|
328
|
+
} catch (err) {
|
|
329
|
+
snapshotInProgress = false;
|
|
330
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
331
|
+
if (message.startsWith("snapshot in progress")) {
|
|
332
|
+
return null;
|
|
333
|
+
}
|
|
334
|
+
throw err;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
try {
|
|
338
|
+
const result = await performBackup(config, now, deps);
|
|
339
|
+
setCheckpoint(LAST_RUN_CHECKPOINT_KEY, String(now.getTime()));
|
|
340
|
+
return result;
|
|
341
|
+
} finally {
|
|
342
|
+
try {
|
|
343
|
+
await release();
|
|
344
|
+
} catch {
|
|
345
|
+
// release is best-effort; logged internally
|
|
346
|
+
}
|
|
347
|
+
snapshotInProgress = false;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Manual-trigger variant of the backup pipeline. Bypasses the `enabled` and
|
|
353
|
+
* interval checks so users can force a snapshot regardless of schedule, but
|
|
354
|
+
* still honors the concurrency mutex — a second concurrent caller throws
|
|
355
|
+
* with "snapshot in progress".
|
|
356
|
+
*
|
|
357
|
+
* Does NOT update the last-run checkpoint on success: manual snapshots are
|
|
358
|
+
* an escape hatch and should not reset the automatic cadence.
|
|
359
|
+
*/
|
|
360
|
+
export async function createSnapshotNow(
|
|
361
|
+
config: BackupConfig,
|
|
362
|
+
now: Date,
|
|
363
|
+
deps: BackupDeps = {},
|
|
364
|
+
): Promise<BackupRunResult> {
|
|
365
|
+
const lockPath = deps.snapshotLockPath ?? getSnapshotLockPath();
|
|
366
|
+
|
|
367
|
+
// Fast path: in-process flag. Catches same-process races without touching
|
|
368
|
+
// the filesystem. The thrown message matches the cross-process variant's
|
|
369
|
+
// prefix so downstream consumers (HTTP 409, CLI error output) can test for
|
|
370
|
+
// "snapshot in progress" uniformly.
|
|
371
|
+
if (snapshotInProgress) {
|
|
372
|
+
throw new Error("snapshot in progress");
|
|
373
|
+
}
|
|
374
|
+
snapshotInProgress = true;
|
|
375
|
+
|
|
376
|
+
// Cross-process lock: the source of truth when a CLI invocation and the
|
|
377
|
+
// daemon worker could both be alive. On conflict, acquireSnapshotLock
|
|
378
|
+
// throws "snapshot in progress (locked by pid N)". We reset the
|
|
379
|
+
// in-process flag before rethrowing so subsequent local attempts aren't
|
|
380
|
+
// permanently blocked by a failed acquisition.
|
|
381
|
+
let release: (() => Promise<void>) | null = null;
|
|
382
|
+
try {
|
|
383
|
+
release = await acquireSnapshotLock(lockPath);
|
|
384
|
+
} catch (err) {
|
|
385
|
+
snapshotInProgress = false;
|
|
386
|
+
throw err;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
try {
|
|
390
|
+
return await performBackup(config, now, deps);
|
|
391
|
+
} finally {
|
|
392
|
+
try {
|
|
393
|
+
await release();
|
|
394
|
+
} catch {
|
|
395
|
+
// release is best-effort; logged internally
|
|
396
|
+
}
|
|
397
|
+
snapshotInProgress = false;
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// ---------------------------------------------------------------------------
|
|
402
|
+
// Scheduler
|
|
403
|
+
// ---------------------------------------------------------------------------
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* A no-op handle used when `startBackupWorker` fails to install its timer.
|
|
407
|
+
* Returning a live handle even on failure lets callers follow the normal
|
|
408
|
+
* `.stop()` cleanup path unconditionally.
|
|
409
|
+
*/
|
|
410
|
+
const NOOP_HANDLE: BackupWorkerHandle = {
|
|
411
|
+
stop: () => {},
|
|
412
|
+
runOnce: async () => null,
|
|
413
|
+
};
|
|
414
|
+
|
|
415
|
+
/**
|
|
416
|
+
* Install the periodic backup worker.
|
|
417
|
+
*
|
|
418
|
+
* Schedules a `setInterval` tick every 5 minutes and returns a handle with
|
|
419
|
+
* `stop()` and `runOnce()`. `runOnce()` invokes the tick body synchronously
|
|
420
|
+
* (bypassing the interval) so callers can drive a backup from code without
|
|
421
|
+
* waiting up to 5 minutes for the next tick.
|
|
422
|
+
*
|
|
423
|
+
* Daemon startup philosophy: this function must never throw. Any unexpected
|
|
424
|
+
* error during setup logs and returns a no-op handle so the caller's startup
|
|
425
|
+
* sequence proceeds unperturbed.
|
|
426
|
+
*/
|
|
427
|
+
export function startBackupWorker(): BackupWorkerHandle {
|
|
428
|
+
try {
|
|
429
|
+
const timer = setInterval(() => {
|
|
430
|
+
void (async () => {
|
|
431
|
+
try {
|
|
432
|
+
const config = getConfig();
|
|
433
|
+
await runBackupTick(config.backup, new Date());
|
|
434
|
+
} catch (err) {
|
|
435
|
+
log.warn({ err }, "Backup worker tick failed");
|
|
436
|
+
}
|
|
437
|
+
})();
|
|
438
|
+
}, TICK_INTERVAL_MS);
|
|
439
|
+
|
|
440
|
+
// Non-blocking: the process may exit even if the timer is still armed.
|
|
441
|
+
(timer as NodeJS.Timeout).unref?.();
|
|
442
|
+
|
|
443
|
+
let stopped = false;
|
|
444
|
+
return {
|
|
445
|
+
stop(): void {
|
|
446
|
+
if (stopped) return;
|
|
447
|
+
stopped = true;
|
|
448
|
+
clearInterval(timer);
|
|
449
|
+
},
|
|
450
|
+
async runOnce(): Promise<BackupRunResult | null> {
|
|
451
|
+
const config = getConfig();
|
|
452
|
+
return runBackupTick(config.backup, new Date());
|
|
453
|
+
},
|
|
454
|
+
};
|
|
455
|
+
} catch (err) {
|
|
456
|
+
log.warn({ err }, "Failed to start backup worker — continuing without it");
|
|
457
|
+
return NOOP_HANDLE;
|
|
458
|
+
}
|
|
459
|
+
}
|