@vellumai/assistant 0.6.3 → 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 +5 -13
- 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/openapi.yaml +982 -72
- package/package.json +4 -6
- package/scripts/generate-openapi.ts +0 -1
- package/scripts/test.sh +73 -18
- 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__/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 +11 -0
- 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 +138 -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-schema.test.ts +1013 -66
- package/src/__tests__/config-watcher-cleanup-throttle.test.ts +339 -0
- package/src/__tests__/config-watcher.test.ts +43 -8
- package/src/__tests__/contact-store-user-file.test.ts +512 -0
- package/src/__tests__/contacts-write.test.ts +197 -0
- 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 +1 -0
- package/src/__tests__/conversation-agent-loop.test.ts +98 -2
- package/src/__tests__/conversation-confirmation-signals.test.ts +135 -0
- package/src/__tests__/conversation-error.test.ts +70 -0
- package/src/__tests__/conversation-history-web-search.test.ts +11 -4
- package/src/__tests__/conversation-init.benchmark.test.ts +6 -1
- 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 +901 -60
- package/src/__tests__/conversation-routes-disk-view.test.ts +270 -0
- package/src/__tests__/conversation-runtime-assembly.test.ts +55 -0
- 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-tool-setup-batch-authorized.test.ts +226 -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-health-service.test.ts +352 -0
- package/src/__tests__/credential-security-invariants.test.ts +5 -3
- package/src/__tests__/credential-vault-unit.test.ts +379 -3
- package/src/__tests__/credentials-cli.test.ts +40 -16
- package/src/__tests__/cross-provider-web-search.test.ts +146 -35
- 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__/emit-event-signal.test.ts +71 -0
- package/src/__tests__/extension-id-sync-guard.test.ts +75 -8
- package/src/__tests__/fixtures/mock-chrome-extension.ts +11 -0
- package/src/__tests__/gateway-only-enforcement.test.ts +206 -1
- package/src/__tests__/gateway-only-guard.test.ts +0 -1
- package/src/__tests__/gemini-provider.test.ts +64 -0
- 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__/headless-browser-interactions.test.ts +43 -0
- package/src/__tests__/headless-browser-mode.test.ts +614 -0
- package/src/__tests__/headless-browser-navigate.test.ts +142 -5
- package/src/__tests__/headless-browser-read-tools.test.ts +11 -0
- package/src/__tests__/headless-browser-snapshot.test.ts +10 -0
- 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 +0 -5
- package/src/__tests__/host-browser-e2e-cloud.test.ts +138 -4
- package/src/__tests__/host-browser-e2e-self-hosted.test.ts +4 -4
- package/src/__tests__/host-browser-ws-events-e2e.test.ts +103 -0
- package/src/__tests__/host-cu-proxy.test.ts +0 -5
- package/src/__tests__/identity-intro-cache.test.ts +40 -10
- package/src/__tests__/init-feature-flag-overrides.test.ts +38 -112
- package/src/__tests__/jobs-store-upsert-debounced.test.ts +141 -0
- 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__/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-export-http.test.ts +6 -6
- package/src/__tests__/migration-import-commit-http.test.ts +8 -6
- 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__/oauth-apps-routes.test.ts +1 -0
- package/src/__tests__/oauth-cli.test.ts +2 -0
- package/src/__tests__/oauth-connect-orchestrator.test.ts +2 -0
- package/src/__tests__/oauth-provider-serializer.test.ts +1 -0
- package/src/__tests__/oauth-providers-routes.test.ts +2 -0
- package/src/__tests__/oauth-store.test.ts +85 -0
- package/src/__tests__/oauth2-gateway-transport.test.ts +249 -6
- package/src/__tests__/onboarding-template-contract.test.ts +6 -13
- package/src/__tests__/openai-provider.test.ts +176 -0
- 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-unsubscribe.test.ts +31 -2
- package/src/__tests__/persona-resolver.test.ts +251 -0
- package/src/__tests__/platform-bash-auto-approve.test.ts +4 -0
- package/src/__tests__/platform.test.ts +92 -1
- package/src/__tests__/post-turn-tool-result-truncation.test.ts +47 -0
- package/src/__tests__/prechat-onboarding-contract.test.ts +267 -0
- package/src/__tests__/pricing.test.ts +174 -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__/search-skills-unified.test.ts +118 -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 +5 -1
- package/src/__tests__/sequence-store.test.ts +1 -1
- package/src/__tests__/server-history-render.test.ts +49 -0
- 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 +276 -145
- package/src/__tests__/skills-files-catalog-fallback.test.ts +381 -93
- 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 +564 -1
- package/src/__tests__/stt-catalog-parity.test.ts +282 -0
- package/src/__tests__/stt-stream-session.test.ts +535 -0
- package/src/__tests__/system-prompt.test.ts +112 -26
- package/src/__tests__/telephony-stt-routing.test.ts +329 -0
- package/src/__tests__/terminal-tools.test.ts +18 -7
- package/src/__tests__/test-preload.ts +18 -0
- package/src/__tests__/test-support/browser-skill-harness.ts +4 -1
- 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__/trust-store.test.ts +7 -1
- package/src/__tests__/trusted-contact-approval-notifier.test.ts +1 -1
- 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__/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/agent/image-optimize.ts +24 -12
- package/src/agent/loop.ts +43 -3
- 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/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/types.ts +16 -0
- 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 +0 -1
- package/src/cli/commands/domain.ts +210 -0
- package/src/cli/commands/email.ts +255 -3
- package/src/cli/commands/oauth/__tests__/connect.test.ts +12 -0
- package/src/cli/commands/oauth/__tests__/providers-delete.test.ts +1 -0
- package/src/cli/commands/oauth/__tests__/providers-register.test.ts +1 -0
- package/src/cli/commands/oauth/__tests__/providers-update.test.ts +1 -0
- package/src/cli/commands/oauth/mode.ts +12 -3
- package/src/cli/commands/oauth/providers.ts +15 -0
- package/src/cli/commands/oauth/shared.ts +2 -1
- package/src/cli/commands/platform/__tests__/callback-routes-list.test.ts +4 -9
- 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/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/references/CUSTOM_ROUTES.md +37 -1
- 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 +2 -2
- package/src/config/bundled-skills/gmail/SKILL.md +53 -7
- 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 +2 -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/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 +24 -0
- package/src/config/env.ts +34 -10
- package/src/config/feature-flag-registry.json +46 -14
- package/src/config/loader.ts +26 -12
- package/src/config/schema.ts +35 -10
- 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 +47 -1
- package/src/config/schemas/inference.ts +1 -1
- package/src/config/schemas/memory-lifecycle.ts +14 -2
- package/src/config/schemas/services.ts +44 -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 -0
- 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 +3 -2
- package/src/context/tool-result-truncation.ts +2 -1
- package/src/context/window-manager.ts +45 -12
- 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 +17 -8
- package/src/daemon/__tests__/lifecycle-startup-ordering.test.ts +127 -0
- package/src/daemon/config-watcher.ts +99 -5
- package/src/daemon/conversation-agent-loop-handlers.ts +6 -0
- package/src/daemon/conversation-agent-loop.ts +101 -24
- 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 +581 -19
- package/src/daemon/conversation-queue-manager.ts +24 -0
- package/src/daemon/conversation-runtime-assembly.ts +11 -1
- package/src/daemon/conversation-slash.ts +36 -0
- package/src/daemon/conversation-surfaces.ts +94 -4
- package/src/daemon/conversation-tool-setup.ts +25 -0
- package/src/daemon/conversation-usage.ts +7 -4
- package/src/daemon/conversation.ts +86 -28
- package/src/daemon/handlers/config-slack-channel.ts +269 -94
- package/src/daemon/handlers/conversations.ts +4 -1
- package/src/daemon/handlers/shared.ts +22 -0
- package/src/daemon/handlers/skills.ts +321 -77
- package/src/daemon/host-browser-proxy.ts +2 -1
- package/src/daemon/lifecycle.ts +122 -25
- package/src/daemon/message-protocol.ts +6 -0
- package/src/daemon/message-types/conversations.ts +34 -1
- package/src/daemon/message-types/home.ts +40 -0
- package/src/daemon/message-types/meet.ts +143 -0
- package/src/daemon/message-types/messages.ts +14 -0
- package/src/daemon/message-types/schedules.ts +34 -2
- package/src/daemon/message-types/skills.ts +16 -0
- package/src/daemon/message-types/surfaces.ts +2 -0
- package/src/daemon/server.ts +347 -2
- package/src/daemon/shutdown-handlers.ts +32 -4
- package/src/daemon/shutdown-registry.ts +40 -0
- package/src/daemon/tool-side-effects.ts +9 -0
- 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 +12 -3
- 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/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 +1 -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 +99 -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/db-init.ts +6 -0
- package/src/memory/db-maintenance.ts +108 -0
- package/src/memory/db.ts +1 -0
- package/src/memory/graph/conversation-graph-memory.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 +27 -18
- package/src/memory/graph/scoring.test.ts +186 -0
- package/src/memory/graph/scoring.ts +31 -1
- 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 +92 -56
- 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 +6 -0
- package/src/memory/migrations/registry.ts +8 -0
- package/src/memory/qdrant-manager.ts +43 -16
- package/src/memory/schema/conversations.ts +2 -0
- package/src/memory/schema/oauth.ts +3 -0
- 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/__tests__/identity-verifier.test.ts +1 -0
- package/src/oauth/byo-connection.test.ts +18 -1
- package/src/oauth/byo-connection.ts +3 -1
- package/src/oauth/connect-orchestrator.ts +2 -0
- package/src/oauth/connection-resolver.ts +6 -2
- package/src/oauth/connection.ts +2 -0
- package/src/oauth/oauth-store.ts +9 -0
- package/src/oauth/platform-connection.test.ts +98 -0
- package/src/oauth/platform-connection.ts +52 -31
- package/src/oauth/seed-providers.ts +7 -0
- package/src/permissions/checker.ts +16 -6
- package/src/permissions/defaults.ts +49 -1
- package/src/permissions/trust-store.ts +3 -3
- 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 +59 -18
- package/src/prompts/templates/BOOTSTRAP.md +5 -5
- 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 -61
- 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 +9 -0
- package/src/runtime/AGENTS.md +43 -1
- package/src/runtime/__tests__/agent-wake.test.ts +831 -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/auth/__tests__/route-policy.test.ts +40 -0
- package/src/runtime/auth/route-policy.ts +30 -5
- package/src/runtime/auth/token-service.ts +56 -1
- package/src/runtime/btw-sidechain.ts +2 -0
- package/src/runtime/capability-tokens.ts +10 -10
- 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 +38 -2
- package/src/runtime/http-server.ts +395 -10
- package/src/runtime/http-types.ts +6 -2
- package/src/runtime/migrations/__tests__/vbundle-import-credentials.test.ts +36 -0
- package/src/runtime/migrations/__tests__/vbundle-legacy-user-md.test.ts +360 -0
- package/src/runtime/migrations/migration-transport.ts +1 -0
- package/src/runtime/migrations/migration-wizard.ts +1 -0
- package/src/runtime/migrations/vbundle-import-analyzer.ts +77 -1
- package/src/runtime/migrations/vbundle-importer.ts +34 -0
- package/src/runtime/pending-interactions.ts +0 -11
- 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/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 +82 -23
- 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 -142
- package/src/runtime/routes/conversation-management-routes.ts +115 -0
- package/src/runtime/routes/conversation-routes.ts +367 -146
- package/src/runtime/routes/filing-routes.ts +93 -0
- 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 +3 -14
- package/src/runtime/routes/identity-intro-cache.ts +7 -3
- package/src/runtime/routes/identity-routes.ts +3 -17
- 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/memory-item-routes.test.ts +3 -2
- package/src/runtime/routes/migration-routes.ts +40 -5
- package/src/runtime/routes/settings-routes.ts +22 -5
- package/src/runtime/routes/skills-routes.ts +76 -7
- package/src/runtime/routes/stt-routes.ts +233 -0
- 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 +30 -2
- 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/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 +50 -0
- package/src/security/oauth2.ts +26 -4
- package/src/security/secure-keys.ts +25 -2
- package/src/security/token-manager.ts +8 -0
- package/src/sequence/engine.ts +23 -0
- package/src/sequence/types.ts +1 -1
- package/src/skills/catalog-files.ts +64 -2
- 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 +38 -14
- 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/browser-execution.ts +1163 -23
- package/src/tools/browser/browser-manager.ts +45 -0
- 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__/cdp-inspect-client.test.ts +393 -0
- package/src/tools/browser/cdp-client/__tests__/extension-cdp-client.test.ts +29 -0
- package/src/tools/browser/cdp-client/__tests__/factory.test.ts +1648 -32
- package/src/tools/browser/cdp-client/cdp-inspect/__tests__/discovery.test.ts +264 -0
- package/src/tools/browser/cdp-client/cdp-inspect/discovery.ts +183 -17
- package/src/tools/browser/cdp-client/cdp-inspect-client.ts +254 -21
- package/src/tools/browser/cdp-client/errors.ts +15 -0
- package/src/tools/browser/cdp-client/extension-cdp-client.ts +39 -16
- package/src/tools/browser/cdp-client/factory.ts +797 -87
- package/src/tools/browser/cdp-client/index.ts +16 -2
- package/src/tools/browser/cdp-client/types.ts +68 -0
- package/src/tools/credentials/vault.ts +35 -6
- package/src/tools/network/web-fetch.ts +5 -2
- package/src/tools/network/web-search.ts +5 -2
- 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/terminal/safe-env.ts +10 -2
- package/src/tools/terminal/shell.ts +15 -4
- package/src/tools/tool-manifest.ts +21 -0
- package/src/tools/types.ts +17 -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 +54 -10
- 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 +13 -1
- package/src/workspace/turn-commit.ts +31 -0
- package/src/__tests__/email-cli.test.ts +0 -297
- package/src/__tests__/email-service-config-fallback.test.ts +0 -102
- package/src/cli/commands/browser-relay.ts +0 -466
- 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/prompts/templates/USER.md +0 -13
- package/src/providers/speech-to-text/types.ts +0 -17
- package/src/runtime/routes/browser-cdp-routes.ts +0 -229
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Home activity feed scheduler.
|
|
3
|
+
*
|
|
4
|
+
* Periodic tick loop that drives the feed producers — the assistant
|
|
5
|
+
* roll-up loop and the platform-baseline Gmail digest generator. This
|
|
6
|
+
* is the layer that turns the Phase-5 scaffolding into a live feed: the
|
|
7
|
+
* producers exist as standalone functions, the writer knows how to
|
|
8
|
+
* persist their output, and the HTTP route + SSE pipeline surface it
|
|
9
|
+
* to the macOS client; this scheduler is what actually calls them.
|
|
10
|
+
*
|
|
11
|
+
* Design notes:
|
|
12
|
+
*
|
|
13
|
+
* - Mirrors `schedule/scheduler.ts`: `setInterval` + coalescing flag
|
|
14
|
+
* so two ticks never run in parallel, `timer.unref()` so the timer
|
|
15
|
+
* never blocks daemon exit, stop() clears the interval on shutdown.
|
|
16
|
+
*
|
|
17
|
+
* - Each producer tracks its own "last ran at" timestamp and decides
|
|
18
|
+
* whether to run on each tick. This keeps the tick cadence short
|
|
19
|
+
* (so cheap producers refresh often) while expensive producers
|
|
20
|
+
* (LLM roll-up) self-throttle independently.
|
|
21
|
+
*
|
|
22
|
+
* - Fire-and-forget: every producer failure is logged and swallowed.
|
|
23
|
+
* A broken producer must never break the tick loop or the daemon.
|
|
24
|
+
*
|
|
25
|
+
* - `writeAssistantFeedItem` / `generateGmailDigest` both invoke the
|
|
26
|
+
* feed writer directly, which publishes `home_feed_updated` on every
|
|
27
|
+
* successful write, so the macOS store auto-refreshes without this
|
|
28
|
+
* scheduler needing to touch the event hub.
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import { getLogger } from "../util/logger.js";
|
|
32
|
+
import type { FeedItem } from "./feed-types.js";
|
|
33
|
+
import {
|
|
34
|
+
generateGmailDigest,
|
|
35
|
+
type GmailCountSource,
|
|
36
|
+
} from "./platform-gmail-digest.js";
|
|
37
|
+
import { type RollupResult, runRollupProducer } from "./rollup-producer.js";
|
|
38
|
+
|
|
39
|
+
const log = getLogger("home-feed-scheduler");
|
|
40
|
+
|
|
41
|
+
/** Tick cadence — fast enough for a Gmail digest to feel fresh. */
|
|
42
|
+
const TICK_INTERVAL_MS = 5 * 60 * 1000;
|
|
43
|
+
|
|
44
|
+
/** Per-producer minimum gap between runs. */
|
|
45
|
+
const GMAIL_DIGEST_INTERVAL_MS = 5 * 60 * 1000;
|
|
46
|
+
const ROLLUP_INTERVAL_MS = 30 * 60 * 1000;
|
|
47
|
+
|
|
48
|
+
export interface FeedSchedulerHandle {
|
|
49
|
+
/** Stops the interval. Safe to call multiple times. */
|
|
50
|
+
stop(): void;
|
|
51
|
+
/**
|
|
52
|
+
* Run a single tick synchronously for tests. Returns a summary of
|
|
53
|
+
* which producers ran during this tick.
|
|
54
|
+
*/
|
|
55
|
+
runOnce(now?: Date): Promise<FeedTickSummary>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface FeedTickSummary {
|
|
59
|
+
gmailDigestRan: boolean;
|
|
60
|
+
rollupRan: boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface FeedSchedulerOptions {
|
|
64
|
+
/**
|
|
65
|
+
* Optional count source for the Gmail digest. Defaults to
|
|
66
|
+
* {@link defaultGmailCountSource}, which reads the active OAuth
|
|
67
|
+
* connection and issues one `messages.list?q=is:unread&maxResults=1`
|
|
68
|
+
* call to read the `resultSizeEstimate` field (no body fetches).
|
|
69
|
+
*/
|
|
70
|
+
gmailCountSource?: GmailCountSource;
|
|
71
|
+
/**
|
|
72
|
+
* When true (the default), the scheduler fires one tick synchronously
|
|
73
|
+
* after startup so the feed is populated on the first app open after
|
|
74
|
+
* daemon boot. Set to false in tests so the only ticks that run are
|
|
75
|
+
* the ones the test drives via `runOnce()`.
|
|
76
|
+
*/
|
|
77
|
+
runOnStart?: boolean;
|
|
78
|
+
/**
|
|
79
|
+
* Dependency seams for tests. Production callers pass `undefined` so
|
|
80
|
+
* the scheduler uses the real producer implementations. Tests pass
|
|
81
|
+
* spies to avoid `mock.module`, which leaks across files in Bun's
|
|
82
|
+
* test runner.
|
|
83
|
+
*/
|
|
84
|
+
gmailDigestRunner?: (
|
|
85
|
+
now: Date,
|
|
86
|
+
countSource: GmailCountSource,
|
|
87
|
+
) => Promise<FeedItem | null>;
|
|
88
|
+
rollupRunner?: (now: Date) => Promise<RollupResult>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Start the home feed scheduler. Returns a handle whose `stop()` is
|
|
93
|
+
* wired into the daemon shutdown sequence via `ShutdownDeps`.
|
|
94
|
+
*/
|
|
95
|
+
export function startFeedScheduler(
|
|
96
|
+
options: FeedSchedulerOptions = {},
|
|
97
|
+
): FeedSchedulerHandle {
|
|
98
|
+
let stopped = false;
|
|
99
|
+
let tickRunning = false;
|
|
100
|
+
let lastGmailDigestAt = 0;
|
|
101
|
+
let lastRollupAt = 0;
|
|
102
|
+
|
|
103
|
+
const gmailCountSource = options.gmailCountSource ?? defaultGmailCountSource;
|
|
104
|
+
const gmailDigestRunner = options.gmailDigestRunner ?? generateGmailDigest;
|
|
105
|
+
const rollupRunner =
|
|
106
|
+
options.rollupRunner ?? ((now: Date) => runRollupProducer(now));
|
|
107
|
+
|
|
108
|
+
const tick = async (now: Date = new Date()): Promise<FeedTickSummary> => {
|
|
109
|
+
const summary: FeedTickSummary = {
|
|
110
|
+
gmailDigestRan: false,
|
|
111
|
+
rollupRan: false,
|
|
112
|
+
};
|
|
113
|
+
if (stopped || tickRunning) return summary;
|
|
114
|
+
tickRunning = true;
|
|
115
|
+
const nowMs = now.getTime();
|
|
116
|
+
try {
|
|
117
|
+
if (nowMs - lastGmailDigestAt >= GMAIL_DIGEST_INTERVAL_MS) {
|
|
118
|
+
lastGmailDigestAt = nowMs;
|
|
119
|
+
summary.gmailDigestRan = true;
|
|
120
|
+
const startedAt = Date.now();
|
|
121
|
+
try {
|
|
122
|
+
const item = await gmailDigestRunner(now, gmailCountSource);
|
|
123
|
+
log.info(
|
|
124
|
+
{ wroteItem: item !== null, durationMs: Date.now() - startedAt },
|
|
125
|
+
"Gmail digest producer ran",
|
|
126
|
+
);
|
|
127
|
+
} catch (err) {
|
|
128
|
+
log.warn(
|
|
129
|
+
{ err, durationMs: Date.now() - startedAt },
|
|
130
|
+
"Gmail digest producer threw",
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (nowMs - lastRollupAt >= ROLLUP_INTERVAL_MS) {
|
|
136
|
+
summary.rollupRan = true;
|
|
137
|
+
const startedAt = Date.now();
|
|
138
|
+
try {
|
|
139
|
+
const result = await rollupRunner(now);
|
|
140
|
+
log.info(
|
|
141
|
+
{
|
|
142
|
+
wroteCount: result.wroteCount,
|
|
143
|
+
skippedReason: result.skippedReason,
|
|
144
|
+
durationMs: Date.now() - startedAt,
|
|
145
|
+
},
|
|
146
|
+
"Rollup producer ran",
|
|
147
|
+
);
|
|
148
|
+
// Only advance the cooldown gate when the producer actually
|
|
149
|
+
// had a chance to run the LLM. Two skip reasons short-circuit
|
|
150
|
+
// before any provider call and should NOT burn the window:
|
|
151
|
+
// - `no_provider`: the provider registry wasn't ready yet
|
|
152
|
+
// (happens on the startup tick because the feed scheduler
|
|
153
|
+
// boots before the provider init pass in
|
|
154
|
+
// `daemon/lifecycle.ts`).
|
|
155
|
+
// - `no_actions`: there was nothing to roll up. A subsequent
|
|
156
|
+
// tick should retry as soon as new actions land, not wait
|
|
157
|
+
// the full window.
|
|
158
|
+
// Every other outcome — success, empty items, malformed
|
|
159
|
+
// output, provider error — is a real LLM attempt and does
|
|
160
|
+
// advance the gate so a broken producer doesn't hammer us.
|
|
161
|
+
if (
|
|
162
|
+
result.skippedReason !== "no_provider" &&
|
|
163
|
+
result.skippedReason !== "no_actions"
|
|
164
|
+
) {
|
|
165
|
+
lastRollupAt = nowMs;
|
|
166
|
+
}
|
|
167
|
+
} catch (err) {
|
|
168
|
+
lastRollupAt = nowMs;
|
|
169
|
+
log.warn(
|
|
170
|
+
{ err, durationMs: Date.now() - startedAt },
|
|
171
|
+
"Rollup producer threw",
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
} finally {
|
|
176
|
+
tickRunning = false;
|
|
177
|
+
}
|
|
178
|
+
return summary;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
const timer = setInterval(() => {
|
|
182
|
+
void tick();
|
|
183
|
+
}, TICK_INTERVAL_MS);
|
|
184
|
+
timer.unref();
|
|
185
|
+
if (options.runOnStart !== false) {
|
|
186
|
+
// Fire once on startup so the feed is populated on the first app
|
|
187
|
+
// open after daemon boot. Runs in the background — startup does
|
|
188
|
+
// not await it.
|
|
189
|
+
void tick();
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
log.info(
|
|
193
|
+
{
|
|
194
|
+
tickIntervalMs: TICK_INTERVAL_MS,
|
|
195
|
+
gmailDigestIntervalMs: GMAIL_DIGEST_INTERVAL_MS,
|
|
196
|
+
rollupIntervalMs: ROLLUP_INTERVAL_MS,
|
|
197
|
+
},
|
|
198
|
+
"Home feed scheduler started",
|
|
199
|
+
);
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
stop(): void {
|
|
203
|
+
if (stopped) return;
|
|
204
|
+
stopped = true;
|
|
205
|
+
clearInterval(timer);
|
|
206
|
+
log.info("Home feed scheduler stopped");
|
|
207
|
+
},
|
|
208
|
+
runOnce: tick,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Default Gmail count source — resolves the active `google` OAuth
|
|
214
|
+
* connection via {@link resolveOAuthConnection}, issues one cheap
|
|
215
|
+
* `messages.list` call with `q=is:unread&maxResults=1`, and returns
|
|
216
|
+
* the `resultSizeEstimate` field. Returns 0 when no Gmail connection
|
|
217
|
+
* exists or the call fails so the digest generator no-ops without
|
|
218
|
+
* throwing.
|
|
219
|
+
*
|
|
220
|
+
* Deliberately does NOT pre-check `isProviderConnected("google")` —
|
|
221
|
+
* that helper only reports local oauth-store rows, which are absent
|
|
222
|
+
* in managed-OAuth mode even when `resolveOAuthConnection("google")`
|
|
223
|
+
* would still resolve via the platform path. Letting the resolver
|
|
224
|
+
* make the decision is the only way to cover both modes.
|
|
225
|
+
*/
|
|
226
|
+
async function defaultGmailCountSource(): Promise<number> {
|
|
227
|
+
// Lazy import — the oauth resolver + gmail client drag in a fair
|
|
228
|
+
// bit of transitive state, and we only want to pay that cost when a
|
|
229
|
+
// tick actually runs (not at module load time).
|
|
230
|
+
const [{ resolveOAuthConnection }, { listMessages }] = await Promise.all([
|
|
231
|
+
import("../oauth/connection-resolver.js"),
|
|
232
|
+
import("../messaging/providers/gmail/client.js"),
|
|
233
|
+
]);
|
|
234
|
+
|
|
235
|
+
try {
|
|
236
|
+
const connection = await resolveOAuthConnection("google");
|
|
237
|
+
const response = await listMessages(connection, "is:unread", 1);
|
|
238
|
+
const estimate = response.resultSizeEstimate;
|
|
239
|
+
return typeof estimate === "number" && estimate >= 0 ? estimate : 0;
|
|
240
|
+
} catch (err) {
|
|
241
|
+
// Either no Gmail connection exists (managed or direct) or the
|
|
242
|
+
// API call itself failed. Both paths degrade to zero so the
|
|
243
|
+
// digest generator no-ops without writing a stale item.
|
|
244
|
+
log.warn({ err }, "Gmail count source failed; treating as zero");
|
|
245
|
+
return 0;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Home activity feed data contract.
|
|
3
|
+
*
|
|
4
|
+
* Defines the shape of `FeedItem`s shown in the macOS Home page feed
|
|
5
|
+
* section (nudges, digests, actions, threads) plus the on-disk file
|
|
6
|
+
* format written by the daemon feed writer (PR 5) and served by the
|
|
7
|
+
* daemon HTTP route (PR 6).
|
|
8
|
+
*
|
|
9
|
+
* The TDD contract field originally named `ttl` is renamed internally
|
|
10
|
+
* to `expiresAt` — it is an absolute ISO-8601 timestamp, not a
|
|
11
|
+
* duration. Absolute timestamps match the `timestamp` and `createdAt`
|
|
12
|
+
* fields and make stateless read-time expiry filtering trivial. If we
|
|
13
|
+
* ever need to expose `ttl` on a public wire format again we can add
|
|
14
|
+
* a translation layer at the route boundary; within the daemon, all
|
|
15
|
+
* internal types use `expiresAt`.
|
|
16
|
+
*
|
|
17
|
+
* A structurally compatible Swift mirror lives at
|
|
18
|
+
* `clients/shared/Network/FeedItem.swift` (PR 3). Any change here
|
|
19
|
+
* must be mirrored there.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { z } from "zod";
|
|
23
|
+
|
|
24
|
+
/** High-level kind of feed item — drives which Swift view renders it. */
|
|
25
|
+
export type FeedItemType = "nudge" | "digest" | "action" | "thread";
|
|
26
|
+
|
|
27
|
+
/** User-facing lifecycle of a feed item. */
|
|
28
|
+
export type FeedItemStatus = "new" | "seen" | "acted_on";
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Origin of the underlying event.
|
|
32
|
+
*
|
|
33
|
+
* In v1 this is constrained to a closed set so the Swift icon mapping
|
|
34
|
+
* stays exhaustive. Future sources will be added explicitly rather
|
|
35
|
+
* than letting arbitrary strings slip through.
|
|
36
|
+
*/
|
|
37
|
+
export type FeedItemSource = "gmail" | "slack" | "calendar" | "assistant";
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Internal field used by the hybrid authoring resolver (PR 5 writer).
|
|
41
|
+
*
|
|
42
|
+
* Not part of the TDD public interface — it distinguishes items the
|
|
43
|
+
* assistant produced on its own from items the platform baseline
|
|
44
|
+
* generators (e.g. Gmail digest in PR 12) produced, so assistant
|
|
45
|
+
* overrides can win over platform defaults for the same source.
|
|
46
|
+
*/
|
|
47
|
+
export type FeedItemAuthor = "assistant" | "platform";
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A single action button attached to a feed item.
|
|
51
|
+
*
|
|
52
|
+
* `prompt` is the pre-seeded user message the action sends to the
|
|
53
|
+
* assistant when triggered — the HTTP route (PR 6) creates a new
|
|
54
|
+
* conversation with this prompt as the first user turn.
|
|
55
|
+
*/
|
|
56
|
+
export interface FeedAction {
|
|
57
|
+
id: string;
|
|
58
|
+
label: string;
|
|
59
|
+
prompt: string;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* A single item rendered in the Home feed.
|
|
64
|
+
*
|
|
65
|
+
* Mirrors the TDD contract plus two internal-only fields:
|
|
66
|
+
* - `author` — hybrid-authoring resolver discriminator
|
|
67
|
+
* - `createdAt` — when the writer recorded the item (distinct from
|
|
68
|
+
* `timestamp`, which is the event time). Used for
|
|
69
|
+
* TTL sweeps and stable ordering.
|
|
70
|
+
*
|
|
71
|
+
* The TDD's `ttl` field is renamed to `expiresAt` here; see the
|
|
72
|
+
* module comment above for the rationale.
|
|
73
|
+
*/
|
|
74
|
+
export interface FeedItem {
|
|
75
|
+
id: string;
|
|
76
|
+
type: FeedItemType;
|
|
77
|
+
/** Integer in [0, 100]; higher values sort earlier. */
|
|
78
|
+
priority: number;
|
|
79
|
+
title: string;
|
|
80
|
+
summary: string;
|
|
81
|
+
/** Optional; when present must be one of the four v1 sources. */
|
|
82
|
+
source?: FeedItemSource;
|
|
83
|
+
/** Event time (ISO-8601). */
|
|
84
|
+
timestamp: string;
|
|
85
|
+
/** Defaults to `"new"` at parse time. */
|
|
86
|
+
status: FeedItemStatus;
|
|
87
|
+
/** Absolute ISO-8601 expiry timestamp (renamed from TDD `ttl`). */
|
|
88
|
+
expiresAt?: string;
|
|
89
|
+
/** Minimum seconds the user must be away before the item is shown. */
|
|
90
|
+
minTimeAway?: number;
|
|
91
|
+
actions?: FeedAction[];
|
|
92
|
+
/** Internal: who authored this item. */
|
|
93
|
+
author: FeedItemAuthor;
|
|
94
|
+
/** Internal: ISO-8601 writer-record time, used for ordering + TTL. */
|
|
95
|
+
createdAt: string;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* On-disk file format for `~/.vellum/workspace/data/home-feed.json`.
|
|
100
|
+
*
|
|
101
|
+
* Written by the PR 5 writer, read by the PR 6 HTTP route and
|
|
102
|
+
* `parseFeedFile` below. `version` is pinned to `1`; future format
|
|
103
|
+
* changes bump this and live behind a workspace migration.
|
|
104
|
+
*/
|
|
105
|
+
export interface HomeFeedFile {
|
|
106
|
+
version: 1;
|
|
107
|
+
items: FeedItem[];
|
|
108
|
+
updatedAt: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ---------------------------------------------------------------------------
|
|
112
|
+
// Zod schemas
|
|
113
|
+
// ---------------------------------------------------------------------------
|
|
114
|
+
|
|
115
|
+
const feedItemTypeSchema = z.enum(["nudge", "digest", "action", "thread"]);
|
|
116
|
+
|
|
117
|
+
const feedItemStatusSchema = z.enum(["new", "seen", "acted_on"]);
|
|
118
|
+
|
|
119
|
+
const feedItemSourceSchema = z.enum([
|
|
120
|
+
"gmail",
|
|
121
|
+
"slack",
|
|
122
|
+
"calendar",
|
|
123
|
+
"assistant",
|
|
124
|
+
]);
|
|
125
|
+
|
|
126
|
+
const feedItemAuthorSchema = z.enum(["assistant", "platform"]);
|
|
127
|
+
|
|
128
|
+
const feedActionSchema = z.object({
|
|
129
|
+
id: z.string(),
|
|
130
|
+
label: z.string(),
|
|
131
|
+
prompt: z.string(),
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Schema for a single `FeedItem`.
|
|
136
|
+
*
|
|
137
|
+
* Notes:
|
|
138
|
+
* - `priority` must be an integer in [0, 100]; string numerics
|
|
139
|
+
* (e.g. `"5"`) are rejected — we want deterministic ordering and
|
|
140
|
+
* silent coercion tends to mask writer bugs.
|
|
141
|
+
* - `status` defaults to `"new"` so the writer does not need to
|
|
142
|
+
* set it on every append.
|
|
143
|
+
* - `source` is optional but, when present, must be one of the
|
|
144
|
+
* four v1 sources — unknown values (e.g. `"facebook"`) are
|
|
145
|
+
* rejected rather than silently passed through.
|
|
146
|
+
* - `minTimeAway` is a non-negative integer number of seconds.
|
|
147
|
+
*/
|
|
148
|
+
export const feedItemSchema = z.object({
|
|
149
|
+
id: z.string(),
|
|
150
|
+
type: feedItemTypeSchema,
|
|
151
|
+
priority: z.number().int().min(0).max(100),
|
|
152
|
+
title: z.string(),
|
|
153
|
+
summary: z.string(),
|
|
154
|
+
source: feedItemSourceSchema.optional(),
|
|
155
|
+
timestamp: z.string(),
|
|
156
|
+
status: feedItemStatusSchema.default("new"),
|
|
157
|
+
expiresAt: z.string().optional(),
|
|
158
|
+
minTimeAway: z.number().int().min(0).optional(),
|
|
159
|
+
actions: z.array(feedActionSchema).optional(),
|
|
160
|
+
author: feedItemAuthorSchema,
|
|
161
|
+
createdAt: z.string(),
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
/** Schema for the on-disk `home-feed.json` file. */
|
|
165
|
+
export const homeFeedFileSchema = z.object({
|
|
166
|
+
version: z.literal(1),
|
|
167
|
+
items: z.array(feedItemSchema),
|
|
168
|
+
updatedAt: z.string(),
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Parse and validate a raw value read from `home-feed.json`.
|
|
173
|
+
*
|
|
174
|
+
* Used by the PR 5 writer on read-back and by the PR 6 HTTP route
|
|
175
|
+
* when serving the feed. Throws a `ZodError` on any validation
|
|
176
|
+
* failure — callers are expected to log + recover (e.g. treat the
|
|
177
|
+
* file as empty).
|
|
178
|
+
*/
|
|
179
|
+
export function parseFeedFile(raw: unknown): HomeFeedFile {
|
|
180
|
+
return homeFeedFileSchema.parse(raw) as HomeFeedFile;
|
|
181
|
+
}
|