@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,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Helpers for listing on-disk backup snapshots.
|
|
3
|
+
*
|
|
4
|
+
* A "snapshot" here is any file inside a backup destination directory whose
|
|
5
|
+
* name matches the canonical `backup-YYYYMMDD-HHMMSS.vbundle[.enc]` pattern
|
|
6
|
+
* defined in `./paths.ts`. Anything else (READMEs, dotfiles, partial writes)
|
|
7
|
+
* is silently ignored so callers can scan a destination without worrying
|
|
8
|
+
* about non-snapshot clutter.
|
|
9
|
+
*
|
|
10
|
+
* The list helper is intentionally pure: it takes an explicit directory and
|
|
11
|
+
* touches no global state, which makes both production code and tests cheap
|
|
12
|
+
* to drive against tmp directories.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { readdir, stat, unlink } from "node:fs/promises";
|
|
16
|
+
import { dirname, join } from "node:path";
|
|
17
|
+
|
|
18
|
+
import { parseBackupTimestamp } from "./paths.js";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Metadata about a single backup snapshot file. The `path` is the absolute
|
|
22
|
+
* path on disk; `createdAt` is parsed from the filename (UTC) rather than
|
|
23
|
+
* read from filesystem mtime so that copies/restores preserve their original
|
|
24
|
+
* snapshot identity.
|
|
25
|
+
*/
|
|
26
|
+
export interface SnapshotEntry {
|
|
27
|
+
path: string;
|
|
28
|
+
filename: string;
|
|
29
|
+
createdAt: Date;
|
|
30
|
+
sizeBytes: number;
|
|
31
|
+
encrypted: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Lists all backup snapshots in a directory, newest-first.
|
|
36
|
+
*
|
|
37
|
+
* Returns `[]` when the directory does not exist -- this is a normal case
|
|
38
|
+
* for fresh installs where no backup has been written yet, so callers do
|
|
39
|
+
* not need to special-case ENOENT.
|
|
40
|
+
*
|
|
41
|
+
* Files that don't match the canonical backup filename pattern are
|
|
42
|
+
* filtered out. Encrypted snapshots (`.vbundle.enc`) and plaintext
|
|
43
|
+
* snapshots (`.vbundle`) are both returned, distinguished by the
|
|
44
|
+
* `encrypted` flag on each entry.
|
|
45
|
+
*/
|
|
46
|
+
export async function listSnapshotsInDir(
|
|
47
|
+
dir: string,
|
|
48
|
+
): Promise<SnapshotEntry[]> {
|
|
49
|
+
let names: string[];
|
|
50
|
+
try {
|
|
51
|
+
names = await readdir(dir);
|
|
52
|
+
} catch (err) {
|
|
53
|
+
if ((err as NodeJS.ErrnoException).code === "ENOENT") return [];
|
|
54
|
+
throw err;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const entries: SnapshotEntry[] = [];
|
|
58
|
+
for (const name of names) {
|
|
59
|
+
const createdAt = parseBackupTimestamp(name);
|
|
60
|
+
if (createdAt == null) continue;
|
|
61
|
+
const fullPath = join(dir, name);
|
|
62
|
+
let stats;
|
|
63
|
+
try {
|
|
64
|
+
stats = await stat(fullPath);
|
|
65
|
+
} catch (err) {
|
|
66
|
+
// Race: a file we just listed may have been removed (e.g. by a
|
|
67
|
+
// concurrent prune). Skip it rather than failing the whole listing.
|
|
68
|
+
if ((err as NodeJS.ErrnoException).code === "ENOENT") continue;
|
|
69
|
+
throw err;
|
|
70
|
+
}
|
|
71
|
+
if (!stats.isFile()) continue;
|
|
72
|
+
entries.push({
|
|
73
|
+
path: fullPath,
|
|
74
|
+
filename: name,
|
|
75
|
+
createdAt,
|
|
76
|
+
sizeBytes: stats.size,
|
|
77
|
+
encrypted: name.endsWith(".vbundle.enc"),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Newest-first by parsed snapshot timestamp. Stable across filesystems
|
|
82
|
+
// since we don't depend on inode/mtime ordering from `readdir`.
|
|
83
|
+
entries.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime());
|
|
84
|
+
return entries;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Apply retention policy to a backup directory.
|
|
89
|
+
*
|
|
90
|
+
* Lists snapshots newest-first, keeps the first `retention` entries, and
|
|
91
|
+
* `unlink`s the rest. Returns a `{ kept, deleted }` split so callers can
|
|
92
|
+
* log/report what happened without re-listing the directory.
|
|
93
|
+
*
|
|
94
|
+
* The `skipped` flag distinguishes two missing-directory cases:
|
|
95
|
+
*
|
|
96
|
+
* - **Parent missing** (`skipped: true`): the parent of `dir` does not exist.
|
|
97
|
+
* For offsite destinations this typically means the backing volume is not
|
|
98
|
+
* available (e.g. iCloud Drive not enabled, external SSD unplugged). The
|
|
99
|
+
* caller should treat the destination as temporarily unavailable rather
|
|
100
|
+
* than silently creating it.
|
|
101
|
+
* - **`dir` missing, parent exists** (no `skipped` flag): the destination is
|
|
102
|
+
* just empty. Returns `{ kept: [], deleted: [] }`. This is the normal
|
|
103
|
+
* fresh-install case for local backups, where `writeLocalSnapshot` creates
|
|
104
|
+
* the directory on demand.
|
|
105
|
+
*
|
|
106
|
+
* Treats both `.vbundle` and `.vbundle.enc` files as one pool ordered by
|
|
107
|
+
* parsed timestamp, so mixing plaintext and encrypted snapshots in the same
|
|
108
|
+
* directory retains the newest `retention` regardless of extension.
|
|
109
|
+
*/
|
|
110
|
+
export async function pruneDir(
|
|
111
|
+
dir: string,
|
|
112
|
+
retention: number,
|
|
113
|
+
): Promise<{
|
|
114
|
+
kept: SnapshotEntry[];
|
|
115
|
+
deleted: SnapshotEntry[];
|
|
116
|
+
skipped?: boolean;
|
|
117
|
+
}> {
|
|
118
|
+
// If the parent of `dir` does not exist, the destination is unreachable
|
|
119
|
+
// (e.g. iCloud Drive disabled, external volume unplugged). Signal the
|
|
120
|
+
// skipped state so callers can surface a useful error rather than treating
|
|
121
|
+
// an unavailable destination as an empty one.
|
|
122
|
+
try {
|
|
123
|
+
await stat(dirname(dir));
|
|
124
|
+
} catch (err) {
|
|
125
|
+
if ((err as NodeJS.ErrnoException).code === "ENOENT") {
|
|
126
|
+
return { kept: [], deleted: [], skipped: true };
|
|
127
|
+
}
|
|
128
|
+
throw err;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const snapshots = await listSnapshotsInDir(dir);
|
|
132
|
+
const kept = snapshots.slice(0, retention);
|
|
133
|
+
const deleted = snapshots.slice(retention);
|
|
134
|
+
|
|
135
|
+
for (const entry of deleted) {
|
|
136
|
+
try {
|
|
137
|
+
await unlink(entry.path);
|
|
138
|
+
} catch (err) {
|
|
139
|
+
// Tolerate races with concurrent prunes / external deletions: a file
|
|
140
|
+
// we just stat'd may have been removed before we could unlink.
|
|
141
|
+
// Anything else (EACCES, EBUSY, ...) should still propagate.
|
|
142
|
+
if ((err as NodeJS.ErrnoException).code !== "ENOENT") throw err;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return { kept, deleted };
|
|
147
|
+
}
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local snapshot writer + retention pruner.
|
|
3
|
+
*
|
|
4
|
+
* The "local" destination is the on-device backup directory (typically under
|
|
5
|
+
* `~/.vellum/backups/local`). It always stores plaintext `.vbundle` files --
|
|
6
|
+
* the encrypted variant is reserved for offsite destinations where the user
|
|
7
|
+
* cannot rely on filesystem-level access controls.
|
|
8
|
+
*
|
|
9
|
+
* Both helpers operate on an explicit directory path so callers can pick the
|
|
10
|
+
* right destination from config and so tests can drive everything against
|
|
11
|
+
* tmp directories without monkey-patching path helpers.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { randomBytes } from "node:crypto";
|
|
15
|
+
import { copyFile, mkdir, rename, stat, unlink } from "node:fs/promises";
|
|
16
|
+
import { basename, join } from "node:path";
|
|
17
|
+
|
|
18
|
+
import { pruneDir, type SnapshotEntry } from "./list-snapshots.js";
|
|
19
|
+
import { formatBackupFilename } from "./paths.js";
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Resolve a destination path that does not already exist on disk. Milliseconds
|
|
23
|
+
* in the filename already make same-second collisions effectively impossible
|
|
24
|
+
* from normal operation, but two backups fired in the same millisecond (or a
|
|
25
|
+
* leftover file from a previous run) would still collide. Fall back to a
|
|
26
|
+
* random suffix so the write never silently overwrites an existing file.
|
|
27
|
+
*/
|
|
28
|
+
async function resolveUniqueDestPath(
|
|
29
|
+
localDir: string,
|
|
30
|
+
filename: string,
|
|
31
|
+
): Promise<string> {
|
|
32
|
+
const primary = join(localDir, filename);
|
|
33
|
+
try {
|
|
34
|
+
await stat(primary);
|
|
35
|
+
} catch (err) {
|
|
36
|
+
if ((err as NodeJS.ErrnoException).code === "ENOENT") return primary;
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
39
|
+
// Path occupied — insert a short random token before the extension. Loop in
|
|
40
|
+
// case the collision itself repeats, though 6 hex chars gives 16M values.
|
|
41
|
+
const extIdx = filename.indexOf(".vbundle");
|
|
42
|
+
const base = filename.slice(0, extIdx);
|
|
43
|
+
const ext = filename.slice(extIdx);
|
|
44
|
+
for (let attempt = 0; attempt < 5; attempt++) {
|
|
45
|
+
const token = randomBytes(3).toString("hex");
|
|
46
|
+
const candidate = join(localDir, `${base}-${token}${ext}`);
|
|
47
|
+
try {
|
|
48
|
+
await stat(candidate);
|
|
49
|
+
} catch (err) {
|
|
50
|
+
if ((err as NodeJS.ErrnoException).code === "ENOENT") return candidate;
|
|
51
|
+
throw err;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
throw new Error(
|
|
55
|
+
`Unable to find a unique backup filename under ${localDir} for ${filename}`,
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Move a freshly-built `.vbundle` temp file into the local backup directory
|
|
61
|
+
* under its canonical timestamped name.
|
|
62
|
+
*
|
|
63
|
+
* - Creates `localDir` (recursively, mode `0o700`) if it does not yet exist.
|
|
64
|
+
* - Renames the temp file to `<localDir>/backup-YYYYMMDD-HHMMSS-SSS.vbundle`.
|
|
65
|
+
* On EXDEV (cross-device move, e.g. when the temp dir is on a different
|
|
66
|
+
* filesystem than the backup directory) it falls back to copy + unlink.
|
|
67
|
+
* - If the canonical filename is already taken on disk (two backups in the
|
|
68
|
+
* same millisecond, or a leftover from a prior crash), a short random
|
|
69
|
+
* suffix is appended so the rename never silently overwrites an existing
|
|
70
|
+
* snapshot.
|
|
71
|
+
* - Returns a `SnapshotEntry` describing the final on-disk file.
|
|
72
|
+
*
|
|
73
|
+
* The caller is expected to pass the same `now` it used when staging the
|
|
74
|
+
* bundle so that the filename, the entry's `createdAt`, and any external
|
|
75
|
+
* record stay in sync.
|
|
76
|
+
*/
|
|
77
|
+
export async function writeLocalSnapshot(
|
|
78
|
+
tempVBundlePath: string,
|
|
79
|
+
localDir: string,
|
|
80
|
+
now: Date,
|
|
81
|
+
): Promise<SnapshotEntry> {
|
|
82
|
+
await mkdir(localDir, { recursive: true, mode: 0o700 });
|
|
83
|
+
|
|
84
|
+
const baseFilename = formatBackupFilename(now, { encrypted: false });
|
|
85
|
+
const destPath = await resolveUniqueDestPath(localDir, baseFilename);
|
|
86
|
+
const filename = basename(destPath);
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
await rename(tempVBundlePath, destPath);
|
|
90
|
+
} catch (err) {
|
|
91
|
+
if ((err as NodeJS.ErrnoException).code !== "EXDEV") throw err;
|
|
92
|
+
// Cross-device fallback: copy then remove the source so callers don't
|
|
93
|
+
// leak the temp file. We deliberately use copyFile (not a stream pipe)
|
|
94
|
+
// because the bundle has already been fully written to disk by the
|
|
95
|
+
// staging step -- there's nothing to stream.
|
|
96
|
+
await copyFile(tempVBundlePath, destPath);
|
|
97
|
+
await unlink(tempVBundlePath);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const stats = await stat(destPath);
|
|
101
|
+
return {
|
|
102
|
+
path: destPath,
|
|
103
|
+
filename,
|
|
104
|
+
createdAt: now,
|
|
105
|
+
sizeBytes: stats.size,
|
|
106
|
+
encrypted: false,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Apply retention policy to the local backup directory.
|
|
112
|
+
*
|
|
113
|
+
* Thin wrapper around the shared `pruneDir` helper in `list-snapshots.ts`.
|
|
114
|
+
* Local backup directories live under `~/.vellum/backups/local` and are
|
|
115
|
+
* created on demand by `writeLocalSnapshot`, so the parent is effectively
|
|
116
|
+
* always present — we strip the `skipped` flag from the returned shape to
|
|
117
|
+
* match the original local-writer contract.
|
|
118
|
+
*
|
|
119
|
+
* Edge cases:
|
|
120
|
+
* - Missing directory: returns `{ kept: [], deleted: [] }` (inherited from
|
|
121
|
+
* `pruneDir`, which defers to `listSnapshotsInDir`'s ENOENT handling).
|
|
122
|
+
* - `retention >= snapshots.length`: nothing is deleted; everything is kept.
|
|
123
|
+
* - `retention === 0`: every snapshot is deleted. The config schema rejects
|
|
124
|
+
* `retention: 0` (min is 1), so this branch only fires when callers
|
|
125
|
+
* explicitly opt into a wipe; treat it as a defensive guarantee.
|
|
126
|
+
*/
|
|
127
|
+
export async function pruneLocalSnapshots(
|
|
128
|
+
localDir: string,
|
|
129
|
+
retention: number,
|
|
130
|
+
): Promise<{ kept: SnapshotEntry[]; deleted: SnapshotEntry[] }> {
|
|
131
|
+
const { kept, deleted } = await pruneDir(localDir, retention);
|
|
132
|
+
return { kept, deleted };
|
|
133
|
+
}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Offsite snapshot writer with per-destination encryption.
|
|
3
|
+
*
|
|
4
|
+
* "Offsite" destinations are any location outside the local backup directory
|
|
5
|
+
* where the user wants a redundant copy of a just-written local snapshot.
|
|
6
|
+
* Canonical examples: iCloud Drive, an external SSD, a network share.
|
|
7
|
+
*
|
|
8
|
+
* Per-destination `encrypt` flag:
|
|
9
|
+
* - `encrypt: true` → AES-256-GCM stream-encrypt into `.vbundle.enc`.
|
|
10
|
+
* - `encrypt: false` → plaintext copy into `.vbundle`. Intended for volumes
|
|
11
|
+
* where the user controls physical access (e.g. an external SSD).
|
|
12
|
+
*
|
|
13
|
+
* Each destination is written independently and sequentially, so one bad
|
|
14
|
+
* destination cannot poison the others: a missing iCloud mount or a broken
|
|
15
|
+
* external drive surfaces as a per-destination `skipped` or `error` in the
|
|
16
|
+
* returned array while every other destination still gets its copy.
|
|
17
|
+
*
|
|
18
|
+
* The helpers are pure with respect to daemon state — they operate on an
|
|
19
|
+
* explicit `localSnapshotPath`, `destinations`, `key`, and `now` so tests can
|
|
20
|
+
* drive the whole surface against temp directories.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { copyFile, mkdir, rename, stat } from "node:fs/promises";
|
|
24
|
+
import { join } from "node:path";
|
|
25
|
+
|
|
26
|
+
import type { BackupDestination } from "../config/schema.js";
|
|
27
|
+
import {
|
|
28
|
+
pruneDir,
|
|
29
|
+
type SnapshotEntry,
|
|
30
|
+
} from "./list-snapshots.js";
|
|
31
|
+
import { deriveSafeAncestor, formatBackupFilename } from "./paths.js";
|
|
32
|
+
import { encryptFile } from "./stream-crypt.js";
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Result of writing a single offsite destination.
|
|
36
|
+
*
|
|
37
|
+
* Exactly one of `entry`, `skipped`, or `error` is meaningful:
|
|
38
|
+
* - `entry` non-null → the write succeeded.
|
|
39
|
+
* - `skipped: "parent-missing"` → the destination's safe ancestor does not
|
|
40
|
+
* exist (e.g. iCloud Drive not enabled, external volume unplugged). Not an
|
|
41
|
+
* error — the write is simply deferred until the volume is back. The
|
|
42
|
+
* ancestor is derived by `deriveSafeAncestor`: for iCloud Drive or
|
|
43
|
+
* `/Volumes/<name>/...` paths it is a well-known mount root, which lets us
|
|
44
|
+
* bootstrap intermediate directories on first run; for arbitrary
|
|
45
|
+
* user-configured paths it falls back to the immediate parent.
|
|
46
|
+
* - `error` set → an unexpected failure while writing. Surfaced as a string
|
|
47
|
+
* so callers can log without serializing an `Error` object.
|
|
48
|
+
*
|
|
49
|
+
* `destination` always preserves the full config record (path + encrypt) so
|
|
50
|
+
* callers can correlate a result with the destination that produced it.
|
|
51
|
+
*/
|
|
52
|
+
export interface OffsiteWriteResult {
|
|
53
|
+
destination: BackupDestination;
|
|
54
|
+
entry: SnapshotEntry | null;
|
|
55
|
+
skipped?: "parent-missing";
|
|
56
|
+
error?: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Write a local snapshot to a single offsite destination.
|
|
61
|
+
*
|
|
62
|
+
* Behavior:
|
|
63
|
+
* - If the destination's safe ancestor (see `deriveSafeAncestor`) does not
|
|
64
|
+
* exist → returns `{ destination, entry: null, skipped: "parent-missing" }`.
|
|
65
|
+
* The offsite volume is (temporarily) unavailable; the caller should not
|
|
66
|
+
* treat this as an error.
|
|
67
|
+
* - Otherwise `mkdir -p` the destination directory (mode `0o700`). This
|
|
68
|
+
* bootstraps any intermediate directories between the safe ancestor and
|
|
69
|
+
* the destination (e.g. creating `VellumAssistant/backups/` under iCloud
|
|
70
|
+
* Drive on first run).
|
|
71
|
+
* - If `destination.encrypt === true`, stream-encrypts via `encryptFile`
|
|
72
|
+
* with the provided `key` and writes `.vbundle.enc`. A missing `key`
|
|
73
|
+
* here is a programmer error, but per the plan we still catch it rather
|
|
74
|
+
* than throwing — a broken destination must never poison the others.
|
|
75
|
+
* - If `destination.encrypt === false`, copies the local snapshot into a
|
|
76
|
+
* `.tmp` sibling and renames into place (atomic; cross-filesystem safe
|
|
77
|
+
* because `copyFile` handles the copy itself).
|
|
78
|
+
*
|
|
79
|
+
* On any unexpected throw, returns `{ destination, entry: null, error: msg }`.
|
|
80
|
+
*/
|
|
81
|
+
export async function writeOffsiteSnapshotToOne(
|
|
82
|
+
localSnapshotPath: string,
|
|
83
|
+
destination: BackupDestination,
|
|
84
|
+
key: Buffer | null,
|
|
85
|
+
now: Date,
|
|
86
|
+
): Promise<OffsiteWriteResult> {
|
|
87
|
+
try {
|
|
88
|
+
// Ancestor-missing probe: if the destination's derived "safe ancestor"
|
|
89
|
+
// does not exist we treat the destination as temporarily unavailable
|
|
90
|
+
// rather than auto-creating a deep tree we have no reason to own. The
|
|
91
|
+
// ancestor is a well-known mount root (iCloud Drive, /Volumes/<name>)
|
|
92
|
+
// for recognized path shapes, or the immediate parent otherwise. That
|
|
93
|
+
// way an unplugged external drive still skips cleanly while the default
|
|
94
|
+
// iCloud destination can bootstrap its intermediate folders on first run.
|
|
95
|
+
try {
|
|
96
|
+
await stat(deriveSafeAncestor(destination.path));
|
|
97
|
+
} catch (err) {
|
|
98
|
+
if ((err as NodeJS.ErrnoException).code === "ENOENT") {
|
|
99
|
+
return { destination, entry: null, skipped: "parent-missing" };
|
|
100
|
+
}
|
|
101
|
+
throw err;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// `destination.path` itself may not exist yet — create it now that we
|
|
105
|
+
// know its parent is reachable.
|
|
106
|
+
await mkdir(destination.path, { recursive: true, mode: 0o700 });
|
|
107
|
+
|
|
108
|
+
const filename = formatBackupFilename(now, {
|
|
109
|
+
encrypted: destination.encrypt,
|
|
110
|
+
});
|
|
111
|
+
const outputPath = join(destination.path, filename);
|
|
112
|
+
|
|
113
|
+
if (destination.encrypt) {
|
|
114
|
+
// Programmer-contract: the caller must have ensured a key exists if any
|
|
115
|
+
// destination is encrypted. We still route this through the catch block
|
|
116
|
+
// so a single broken destination cannot poison the others.
|
|
117
|
+
if (key == null) {
|
|
118
|
+
throw new Error(
|
|
119
|
+
"Offsite destination requires encryption but no key was provided",
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
await encryptFile(localSnapshotPath, outputPath, key);
|
|
123
|
+
} else {
|
|
124
|
+
// Atomic plaintext copy: write into a sibling `.tmp` then rename into
|
|
125
|
+
// place. `copyFile` handles cross-filesystem copies, so we don't need
|
|
126
|
+
// the EXDEV fallback dance that `writeLocalSnapshot` uses for a
|
|
127
|
+
// same-device rename.
|
|
128
|
+
const tempPath = `${outputPath}.tmp`;
|
|
129
|
+
await copyFile(localSnapshotPath, tempPath);
|
|
130
|
+
await rename(tempPath, outputPath);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const stats = await stat(outputPath);
|
|
134
|
+
return {
|
|
135
|
+
destination,
|
|
136
|
+
entry: {
|
|
137
|
+
path: outputPath,
|
|
138
|
+
filename,
|
|
139
|
+
createdAt: now,
|
|
140
|
+
sizeBytes: stats.size,
|
|
141
|
+
encrypted: destination.encrypt,
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
} catch (err) {
|
|
145
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
146
|
+
return { destination, entry: null, error: message };
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Write a local snapshot to every configured offsite destination, in order.
|
|
152
|
+
*
|
|
153
|
+
* Sequential by design: parallelizing wouldn't save meaningful wall-clock
|
|
154
|
+
* time (the dominant cost is filesystem IO on potentially-slow network
|
|
155
|
+
* volumes) and sequential writes make per-destination failures trivially
|
|
156
|
+
* observable. Empty array returns `[]` immediately without any stat/mkdir.
|
|
157
|
+
*
|
|
158
|
+
* Returns one `OffsiteWriteResult` per input destination, in the same order
|
|
159
|
+
* as `destinations`. Callers can `filter` the result to extract successes
|
|
160
|
+
* (`entry != null`), skips (`skipped === "parent-missing"`), or errors
|
|
161
|
+
* (`error != null`).
|
|
162
|
+
*/
|
|
163
|
+
export async function writeOffsiteSnapshotToAll(
|
|
164
|
+
localSnapshotPath: string,
|
|
165
|
+
destinations: BackupDestination[],
|
|
166
|
+
key: Buffer | null,
|
|
167
|
+
now: Date,
|
|
168
|
+
): Promise<OffsiteWriteResult[]> {
|
|
169
|
+
if (destinations.length === 0) return [];
|
|
170
|
+
|
|
171
|
+
const results: OffsiteWriteResult[] = [];
|
|
172
|
+
for (const destination of destinations) {
|
|
173
|
+
const result = await writeOffsiteSnapshotToOne(
|
|
174
|
+
localSnapshotPath,
|
|
175
|
+
destination,
|
|
176
|
+
key,
|
|
177
|
+
now,
|
|
178
|
+
);
|
|
179
|
+
results.push(result);
|
|
180
|
+
}
|
|
181
|
+
return results;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Apply retention to every configured offsite destination.
|
|
186
|
+
*
|
|
187
|
+
* Retention is applied **per destination** — each keeps its own newest
|
|
188
|
+
* `retention` snapshots independently. A `skipped: true` result means the
|
|
189
|
+
* destination's parent directory is missing (e.g. iCloud Drive disabled);
|
|
190
|
+
* callers should treat this as a transient unavailability rather than an
|
|
191
|
+
* empty directory.
|
|
192
|
+
*
|
|
193
|
+
* Mixed `.vbundle` and `.vbundle.enc` files in a single destination are
|
|
194
|
+
* treated as one pool ordered by parsed timestamp, so retention still holds
|
|
195
|
+
* if a destination's `encrypt` flag changes over its lifetime.
|
|
196
|
+
*/
|
|
197
|
+
export async function pruneOffsiteSnapshotsInAll(
|
|
198
|
+
destinations: BackupDestination[],
|
|
199
|
+
retention: number,
|
|
200
|
+
): Promise<
|
|
201
|
+
Array<{
|
|
202
|
+
destination: BackupDestination;
|
|
203
|
+
kept: SnapshotEntry[];
|
|
204
|
+
deleted: SnapshotEntry[];
|
|
205
|
+
skipped?: boolean;
|
|
206
|
+
}>
|
|
207
|
+
> {
|
|
208
|
+
const results: Array<{
|
|
209
|
+
destination: BackupDestination;
|
|
210
|
+
kept: SnapshotEntry[];
|
|
211
|
+
deleted: SnapshotEntry[];
|
|
212
|
+
skipped?: boolean;
|
|
213
|
+
}> = [];
|
|
214
|
+
for (const destination of destinations) {
|
|
215
|
+
const { kept, deleted, skipped } = await pruneDir(
|
|
216
|
+
destination.path,
|
|
217
|
+
retention,
|
|
218
|
+
);
|
|
219
|
+
results.push({ destination, kept, deleted, skipped });
|
|
220
|
+
}
|
|
221
|
+
return results;
|
|
222
|
+
}
|