@vellumai/assistant 0.3.4 → 0.3.6
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/Dockerfile +2 -0
- package/README.md +88 -2
- package/eslint.config.mjs +31 -0
- package/package.json +1 -1
- package/scripts/ipc/check-swift-decoder-drift.ts +4 -1
- package/scripts/ipc/generate-swift.ts +31 -2
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +438 -1
- package/src/__tests__/approval-conversation-turn.test.ts +214 -0
- package/src/__tests__/approval-hardcoded-copy-guard.test.ts +41 -0
- package/src/__tests__/approval-message-composer.test.ts +253 -0
- package/src/__tests__/browser-manager.test.ts +1 -0
- package/src/__tests__/call-conversation-messages.test.ts +130 -0
- package/src/__tests__/call-domain.test.ts +12 -2
- package/src/__tests__/call-orchestrator.test.ts +799 -249
- package/src/__tests__/call-pointer-messages.test.ts +148 -0
- package/src/__tests__/call-recovery.test.ts +3 -0
- package/src/__tests__/call-routes-http.test.ts +32 -2
- package/src/__tests__/call-store.test.ts +3 -0
- package/src/__tests__/channel-approval-routes.test.ts +1277 -98
- package/src/__tests__/channel-approval.test.ts +37 -0
- package/src/__tests__/channel-approvals.test.ts +36 -50
- package/src/__tests__/channel-guardian.test.ts +630 -22
- package/src/__tests__/channel-readiness-service.test.ts +324 -0
- package/src/__tests__/checker.test.ts +14 -7
- package/src/__tests__/clarification-resolver.test.ts +44 -24
- package/src/__tests__/commit-message-enrichment-service.test.ts +9 -4
- package/src/__tests__/computer-use-session-working-dir.test.ts +8 -0
- package/src/__tests__/config-schema.test.ts +14 -8
- package/src/__tests__/context-window-manager.test.ts +30 -2
- package/src/__tests__/contradiction-checker.test.ts +20 -5
- package/src/__tests__/credential-security-invariants.test.ts +7 -2
- package/src/__tests__/daemon-lifecycle.test.ts +13 -12
- package/src/__tests__/db-migration-rollback.test.ts +752 -0
- package/src/__tests__/dictation-mode-detection.test.ts +63 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -0
- package/src/__tests__/entity-search.test.ts +615 -0
- package/src/__tests__/fuzzy-match-property.test.ts +5 -5
- package/src/__tests__/guardian-action-store.test.ts +123 -0
- package/src/__tests__/guardian-action-sweep.test.ts +277 -0
- package/src/__tests__/guardian-dispatch.test.ts +389 -0
- package/src/__tests__/guardian-question-copy.test.ts +47 -0
- package/src/__tests__/handlers-telegram-config.test.ts +4 -2
- package/src/__tests__/handlers-twilio-config.test.ts +533 -0
- package/src/__tests__/intent-routing.test.ts +2 -0
- package/src/__tests__/ipc-snapshot.test.ts +291 -1
- package/src/__tests__/memory-upsert-concurrency.test.ts +828 -0
- package/src/__tests__/messaging-send-tool.test.ts +65 -0
- package/src/__tests__/model-intents.test.ts +96 -0
- package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +42 -0
- package/src/__tests__/oauth2-gateway-transport.test.ts +130 -0
- package/src/__tests__/onboarding-starter-tasks.test.ts +2 -0
- package/src/__tests__/provider-commit-message-generator.test.ts +89 -13
- package/src/__tests__/provider-error-scenarios.test.ts +621 -0
- package/src/__tests__/provider-fail-open-selection.test.ts +119 -0
- package/src/__tests__/qdrant-manager.test.ts +27 -20
- package/src/__tests__/relay-server.test.ts +779 -40
- package/src/__tests__/run-orchestrator-assistant-events.test.ts +6 -0
- package/src/__tests__/run-orchestrator.test.ts +42 -4
- package/src/__tests__/runtime-runs-http.test.ts +17 -1
- package/src/__tests__/runtime-runs.test.ts +16 -0
- package/src/__tests__/schedule-store.test.ts +18 -4
- package/src/__tests__/scheduler-recurrence.test.ts +13 -4
- package/src/__tests__/session-abort-tool-results.test.ts +6 -0
- package/src/__tests__/session-agent-loop.test.ts +857 -0
- package/src/__tests__/session-conflict-gate.test.ts +6 -0
- package/src/__tests__/session-pre-run-repair.test.ts +6 -0
- package/src/__tests__/session-profile-injection.test.ts +6 -0
- package/src/__tests__/session-provider-retry-repair.test.ts +6 -0
- package/src/__tests__/session-queue.test.ts +6 -0
- package/src/__tests__/session-runtime-assembly.test.ts +321 -13
- package/src/__tests__/session-slash-known.test.ts +6 -0
- package/src/__tests__/session-slash-queue.test.ts +6 -0
- package/src/__tests__/session-slash-unknown.test.ts +6 -0
- package/src/__tests__/session-surfaces-task-progress.test.ts +2 -0
- package/src/__tests__/session-tool-setup-app-refresh.test.ts +1 -0
- package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -0
- package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -0
- package/src/__tests__/session-workspace-injection.test.ts +6 -0
- package/src/__tests__/session-workspace-tool-tracking.test.ts +6 -0
- package/src/__tests__/skills.test.ts +2 -0
- package/src/__tests__/sms-messaging-provider.test.ts +126 -0
- package/src/__tests__/starter-task-flow.test.ts +2 -0
- package/src/__tests__/swarm-dag-pathological.test.ts +535 -0
- package/src/__tests__/system-prompt.test.ts +2 -0
- package/src/__tests__/task-management-tools.test.ts +2 -2
- package/src/__tests__/task-runner.test.ts +14 -4
- package/src/__tests__/terminal-tools.test.ts +25 -19
- package/src/__tests__/tool-execution-abort-cleanup.test.ts +545 -0
- package/src/__tests__/tool-executor-shell-integration.test.ts +11 -11
- package/src/__tests__/tool-executor.test.ts +23 -24
- package/src/__tests__/trust-store.test.ts +3 -3
- package/src/__tests__/twilio-rest.test.ts +29 -0
- package/src/__tests__/twilio-routes-elevenlabs.test.ts +3 -0
- package/src/__tests__/twilio-routes-twiml.test.ts +11 -0
- package/src/__tests__/twilio-routes.test.ts +167 -11
- package/src/__tests__/twitter-cli-error-shaping.test.ts +2 -2
- package/src/__tests__/user-reference.test.ts +2 -0
- package/src/__tests__/voice-quality.test.ts +222 -0
- package/src/__tests__/web-search.test.ts +46 -30
- package/src/__tests__/work-item-output.test.ts +110 -0
- package/src/agent/loop.ts +1 -1
- package/src/agent-heartbeat/agent-heartbeat-service.ts +2 -10
- package/src/amazon/client.ts +1418 -0
- package/src/amazon/request-extractor.ts +135 -0
- package/src/amazon/session.ts +109 -0
- package/src/autonomy/autonomy-store.ts +5 -5
- package/src/browser-extension-relay/client.ts +124 -0
- package/src/browser-extension-relay/protocol.ts +63 -0
- package/src/browser-extension-relay/server.ts +177 -0
- package/src/bundler/app-bundler.ts +3 -3
- package/src/bundler/bundle-signer.ts +1 -1
- package/src/bundler/signature-verifier.ts +1 -1
- package/src/calls/call-conversation-messages.ts +33 -0
- package/src/calls/call-domain.ts +114 -10
- package/src/calls/call-orchestrator.ts +268 -59
- package/src/calls/call-pointer-messages.ts +53 -0
- package/src/calls/call-recovery.ts +3 -8
- package/src/calls/call-store.ts +69 -87
- package/src/calls/elevenlabs-config.ts +3 -2
- package/src/calls/guardian-action-sweep.ts +105 -0
- package/src/calls/guardian-dispatch.ts +203 -0
- package/src/calls/guardian-question-copy.ts +133 -0
- package/src/calls/relay-server.ts +466 -8
- package/src/calls/speaker-identification.ts +1 -1
- package/src/calls/twilio-config.ts +22 -14
- package/src/calls/twilio-provider.ts +6 -4
- package/src/calls/twilio-rest.ts +308 -7
- package/src/calls/twilio-routes.ts +65 -12
- package/src/calls/types.ts +3 -1
- package/src/channels/types.ts +25 -0
- package/src/cli/amazon.ts +815 -0
- package/src/cli/config-commands.ts +2 -2
- package/src/cli/core-commands.ts +4 -3
- package/src/cli/influencer.ts +244 -0
- package/src/cli/map.ts +89 -6
- package/src/cli.ts +1 -1
- package/src/config/agent-schema.ts +171 -0
- package/src/config/bundled-skills/amazon/SKILL.md +127 -0
- package/src/config/bundled-skills/amazon/icon.svg +13 -0
- package/src/config/bundled-skills/api-mapping/SKILL.md +78 -0
- package/src/config/bundled-skills/browser/SKILL.md +1 -0
- package/src/config/bundled-skills/browser/TOOLS.json +17 -0
- package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +25 -0
- package/src/config/bundled-skills/doordash/SKILL.md +51 -51
- package/src/config/bundled-skills/email-setup/SKILL.md +14 -5
- package/src/config/bundled-skills/google-oauth-setup/SKILL.md +183 -0
- package/src/config/bundled-skills/influencer/SKILL.md +144 -0
- package/src/config/bundled-skills/knowledge-graph/SKILL.md +15 -0
- package/src/config/bundled-skills/knowledge-graph/TOOLS.json +56 -0
- package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +185 -0
- package/src/config/bundled-skills/macos-automation/icon.svg +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +176 -0
- package/src/config/bundled-skills/media-processing/TOOLS.json +230 -0
- package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +77 -0
- package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +69 -0
- package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +303 -0
- package/src/config/bundled-skills/media-processing/services/concurrency-pool.ts +55 -0
- package/src/config/bundled-skills/media-processing/services/cost-tracker.ts +86 -0
- package/src/config/bundled-skills/media-processing/services/gemini-map.ts +339 -0
- package/src/config/bundled-skills/media-processing/services/preprocess.ts +551 -0
- package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +259 -0
- package/src/config/bundled-skills/media-processing/services/reduce.ts +197 -0
- package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +136 -0
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +59 -0
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +195 -0
- package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +197 -0
- package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +143 -0
- package/src/config/bundled-skills/media-processing/tools/media-status.ts +75 -0
- package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +65 -0
- package/src/config/bundled-skills/messaging/SKILL.md +33 -8
- package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -7
- package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +2 -1
- package/src/config/bundled-skills/messaging/tools/messaging-send.ts +5 -1
- package/src/config/bundled-skills/phone-calls/SKILL.md +88 -23
- package/src/config/bundled-skills/twitter/SKILL.md +19 -3
- package/src/config/bundled-skills/twitter/icon.svg +14 -0
- package/src/config/bundled-tool-registry.ts +310 -0
- package/src/config/calls-schema.ts +181 -0
- package/src/config/core-schema.ts +309 -0
- package/src/config/defaults.ts +28 -3
- package/src/config/env-registry.ts +162 -0
- package/src/config/env.ts +175 -0
- package/src/config/loader.ts +6 -6
- package/src/config/memory-schema.ts +528 -0
- package/src/config/sandbox-schema.ts +55 -0
- package/src/config/schema.ts +158 -1133
- package/src/config/skill-state.ts +1 -1
- package/src/config/skills-schema.ts +32 -0
- package/src/config/skills.ts +35 -24
- package/src/config/system-prompt.ts +131 -56
- package/src/config/templates/IDENTITY.md +2 -2
- package/src/config/templates/SOUL.md +1 -1
- package/src/config/types.ts +1 -0
- package/src/config/user-reference.ts +4 -9
- package/src/config/vellum-skills/catalog.json +6 -7
- package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +5 -1
- package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +4 -3
- package/src/config/vellum-skills/sms-setup/SKILL.md +216 -0
- package/src/config/vellum-skills/twilio-setup/SKILL.md +40 -8
- package/src/context/window-manager.ts +27 -7
- package/src/daemon/approval-generators.ts +186 -0
- package/src/daemon/approved-devices-store.ts +140 -0
- package/src/daemon/assistant-attachments.ts +1 -1
- package/src/daemon/classifier.ts +35 -32
- package/src/daemon/config-watcher.ts +1 -1
- package/src/daemon/daemon-control.ts +217 -0
- package/src/daemon/handlers/apps.ts +2 -3
- package/src/daemon/handlers/config-channels.ts +158 -0
- package/src/daemon/handlers/config-inbox.ts +540 -0
- package/src/daemon/handlers/config-ingress.ts +231 -0
- package/src/daemon/handlers/config-integrations.ts +258 -0
- package/src/daemon/handlers/config-model.ts +143 -0
- package/src/daemon/handlers/config-parental.ts +163 -0
- package/src/daemon/handlers/config-scheduling.ts +172 -0
- package/src/daemon/handlers/config-slack.ts +92 -0
- package/src/daemon/handlers/config-telegram.ts +301 -0
- package/src/daemon/handlers/config-tools.ts +177 -0
- package/src/daemon/handlers/config-trust.ts +104 -0
- package/src/daemon/handlers/config-twilio.ts +1080 -0
- package/src/daemon/handlers/config.ts +53 -1689
- package/src/daemon/handlers/diagnostics.ts +1 -1
- package/src/daemon/handlers/dictation.ts +180 -0
- package/src/daemon/handlers/documents.ts +18 -32
- package/src/daemon/handlers/identity.ts +14 -23
- package/src/daemon/handlers/index.ts +11 -0
- package/src/daemon/handlers/misc.ts +3 -5
- package/src/daemon/handlers/pairing.ts +98 -0
- package/src/daemon/handlers/sessions.ts +56 -5
- package/src/daemon/handlers/shared.ts +6 -1
- package/src/daemon/handlers/skills.ts +1 -1
- package/src/daemon/handlers/twitter-auth.ts +2 -0
- package/src/daemon/handlers/work-items.ts +17 -9
- package/src/daemon/handlers/workspace-files.ts +4 -3
- package/src/daemon/install-cli-launchers.ts +113 -0
- package/src/daemon/ipc-contract/apps.ts +356 -0
- package/src/daemon/ipc-contract/browser.ts +74 -0
- package/src/daemon/ipc-contract/computer-use.ts +151 -0
- package/src/daemon/ipc-contract/diagnostics.ts +56 -0
- package/src/daemon/ipc-contract/documents.ts +74 -0
- package/src/daemon/ipc-contract/inbox.ts +209 -0
- package/src/daemon/ipc-contract/integrations.ts +284 -0
- package/src/daemon/ipc-contract/memory.ts +48 -0
- package/src/daemon/ipc-contract/messages.ts +211 -0
- package/src/daemon/ipc-contract/pairing.ts +45 -0
- package/src/daemon/ipc-contract/parental-control.ts +95 -0
- package/src/daemon/ipc-contract/schedules.ts +97 -0
- package/src/daemon/ipc-contract/sessions.ts +315 -0
- package/src/daemon/ipc-contract/shared.ts +42 -0
- package/src/daemon/ipc-contract/skills.ts +120 -0
- package/src/daemon/ipc-contract/subagents.ts +58 -0
- package/src/daemon/ipc-contract/surfaces.ts +250 -0
- package/src/daemon/ipc-contract/trust.ts +60 -0
- package/src/daemon/ipc-contract/work-items.ts +225 -0
- package/src/daemon/ipc-contract/workspace.ts +113 -0
- package/src/daemon/ipc-contract-inventory.json +70 -0
- package/src/daemon/ipc-contract-inventory.ts +55 -29
- package/src/daemon/ipc-contract.ts +229 -2426
- package/src/daemon/ipc-protocol.ts +1 -1
- package/src/daemon/ipc-validate.ts +7 -0
- package/src/daemon/lifecycle.ts +97 -377
- package/src/daemon/pairing-store.ts +177 -0
- package/src/daemon/providers-setup.ts +43 -0
- package/src/daemon/ride-shotgun-handler.ts +68 -3
- package/src/daemon/server.ts +66 -46
- package/src/daemon/session-agent-loop-handlers.ts +421 -0
- package/src/daemon/session-agent-loop.ts +117 -275
- package/src/daemon/session-dynamic-profile.ts +1 -1
- package/src/daemon/session-history.ts +1 -1
- package/src/daemon/session-media-retry.ts +1 -1
- package/src/daemon/session-messaging.ts +37 -2
- package/src/daemon/session-notifiers.ts +5 -25
- package/src/daemon/session-process.ts +99 -59
- package/src/daemon/session-queue-manager.ts +96 -4
- package/src/daemon/session-runtime-assembly.ts +199 -10
- package/src/daemon/session-surfaces.ts +19 -4
- package/src/daemon/session-tool-setup.ts +30 -30
- package/src/daemon/session-workspace.ts +1 -1
- package/src/daemon/session.ts +35 -2
- package/src/daemon/shutdown-handlers.ts +122 -0
- package/src/daemon/trace-emitter.ts +1 -1
- package/src/daemon/watch-handler.ts +36 -33
- package/src/doordash/cart-queries.ts +787 -0
- package/src/doordash/client.ts +144 -127
- package/src/doordash/order-queries.ts +85 -0
- package/src/doordash/queries.ts +10 -1308
- package/src/doordash/search-queries.ts +203 -0
- package/src/doordash/session.ts +3 -2
- package/src/doordash/store-queries.ts +246 -0
- package/src/doordash/types.ts +367 -0
- package/src/email/providers/agentmail.ts +2 -1
- package/src/email/providers/index.ts +3 -2
- package/src/email/service.ts +3 -2
- package/src/errors.ts +43 -0
- package/src/home-base/prebuilt/seed.ts +1 -1
- package/src/hooks/cli.ts +6 -5
- package/src/hooks/config.ts +6 -8
- package/src/hooks/discovery.ts +6 -5
- package/src/hooks/manager.ts +4 -3
- package/src/hooks/runner.ts +2 -2
- package/src/hooks/templates.ts +5 -5
- package/src/inbound/public-ingress-urls.ts +6 -4
- package/src/index.ts +4 -2
- package/src/influencer/client.ts +1104 -0
- package/src/instrument.ts +4 -3
- package/src/logfire.ts +4 -3
- package/src/memory/admin.ts +25 -35
- package/src/memory/attachments-store.ts +4 -7
- package/src/memory/channel-delivery-store.ts +30 -1
- package/src/memory/channel-guardian-store.ts +202 -2
- package/src/memory/clarification-resolver.ts +37 -33
- package/src/memory/conflict-store.ts +67 -61
- package/src/memory/contradiction-checker.ts +141 -117
- package/src/memory/conversation-store.ts +335 -51
- package/src/memory/db-connection.ts +27 -4
- package/src/memory/db-init.ts +265 -4
- package/src/memory/db.ts +14 -1
- package/src/memory/embedding-backend.ts +27 -5
- package/src/memory/embedding-ollama.ts +2 -1
- package/src/memory/entity-extractor.ts +38 -35
- package/src/memory/guardian-action-store.ts +430 -0
- package/src/memory/inbox-escalation-projection.ts +59 -0
- package/src/memory/inbox-thread-store.ts +218 -0
- package/src/memory/ingress-invite-store.ts +338 -0
- package/src/memory/ingress-member-store.ts +350 -0
- package/src/memory/items-extractor.ts +91 -97
- package/src/memory/job-handlers/index-maintenance.ts +3 -3
- package/src/memory/job-handlers/media-processing.ts +69 -0
- package/src/memory/job-handlers/summarization.ts +32 -26
- package/src/memory/job-utils.ts +3 -10
- package/src/memory/jobs-store.ts +8 -10
- package/src/memory/jobs-worker.ts +55 -36
- package/src/memory/media-store.ts +759 -0
- package/src/memory/migrations/001-job-deferrals.ts +45 -0
- package/src/memory/migrations/002-tool-invocations-fk.ts +43 -0
- package/src/memory/migrations/003-memory-fts-backfill.ts +24 -0
- package/src/memory/migrations/004-entity-relation-dedup.ts +87 -0
- package/src/memory/migrations/005-fingerprint-scope-unique.ts +80 -0
- package/src/memory/migrations/006-scope-salted-fingerprints.ts +62 -0
- package/src/memory/migrations/007-assistant-id-to-self.ts +254 -0
- package/src/memory/migrations/008-remove-assistant-id-columns.ts +208 -0
- package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +83 -0
- package/src/memory/migrations/010-ext-conv-bindings-channel-chat-unique.ts +56 -0
- package/src/memory/migrations/011-call-sessions-provider-sid-dedup.ts +63 -0
- package/src/memory/migrations/012-call-sessions-add-initiated-from.ts +19 -0
- package/src/memory/migrations/013-guardian-action-tables.ts +68 -0
- package/src/memory/migrations/014-backfill-inbox-thread-state.ts +76 -0
- package/src/memory/migrations/015-drop-active-search-index.ts +27 -0
- package/src/memory/migrations/016-memory-segments-indexes.ts +11 -0
- package/src/memory/migrations/017-memory-items-indexes.ts +10 -0
- package/src/memory/migrations/018-remaining-table-indexes.ts +13 -0
- package/src/memory/migrations/index.ts +24 -0
- package/src/memory/migrations/registry.ts +79 -0
- package/src/memory/migrations/validate-migration-state.ts +69 -0
- package/src/memory/qdrant-manager.ts +49 -8
- package/src/memory/query-builder.ts +1 -1
- package/src/memory/raw-query.ts +119 -0
- package/src/memory/recall-cache.ts +4 -1
- package/src/memory/retriever.ts +165 -47
- package/src/memory/schema-migration.ts +25 -984
- package/src/memory/schema.ts +228 -7
- package/src/memory/search/entity.ts +205 -31
- package/src/memory/search/lexical.ts +81 -52
- package/src/memory/search/ranking.ts +27 -23
- package/src/memory/search/semantic.ts +157 -19
- package/src/memory/search/types.ts +24 -0
- package/src/memory/shared-app-links-store.ts +4 -5
- package/src/memory/validation.ts +19 -0
- package/src/messaging/draft-store.ts +5 -6
- package/src/messaging/provider-types.ts +2 -0
- package/src/messaging/providers/sms/adapter.ts +201 -0
- package/src/messaging/providers/sms/client.ts +93 -0
- package/src/messaging/providers/sms/types.ts +7 -0
- package/src/messaging/providers/telegram-bot/adapter.ts +2 -5
- package/src/messaging/providers/whatsapp/adapter.ts +136 -0
- package/src/messaging/providers/whatsapp/client.ts +67 -0
- package/src/messaging/style-analyzer.ts +5 -4
- package/src/messaging/thread-summarizer.ts +61 -69
- package/src/messaging/triage-engine.ts +62 -71
- package/src/migrations/config-merge.ts +53 -0
- package/src/migrations/data-layout.ts +68 -0
- package/src/migrations/data-merge.ts +33 -0
- package/src/migrations/hooks-merge.ts +90 -0
- package/src/migrations/index.ts +6 -0
- package/src/migrations/log.ts +23 -0
- package/src/migrations/skills-merge.ts +33 -0
- package/src/migrations/workspace-layout.ts +79 -0
- package/src/permissions/checker.ts +133 -11
- package/src/permissions/prompter.ts +14 -0
- package/src/permissions/shell-identity.ts +31 -1
- package/src/permissions/trust-store.ts +21 -1
- package/src/providers/anthropic/client.ts +4 -4
- package/src/providers/failover.ts +2 -2
- package/src/providers/model-intents.ts +70 -0
- package/src/providers/ollama/client.ts +2 -1
- package/src/providers/provider-send-message.ts +176 -0
- package/src/providers/registry.ts +71 -30
- package/src/providers/retry.ts +35 -1
- package/src/providers/types.ts +12 -1
- package/src/runtime/approval-conversation-turn.ts +97 -0
- package/src/runtime/approval-message-composer.ts +253 -0
- package/src/runtime/channel-approval-parser.ts +36 -2
- package/src/runtime/channel-approvals.ts +11 -24
- package/src/runtime/channel-guardian-service.ts +88 -21
- package/src/runtime/channel-readiness-service.ts +418 -0
- package/src/runtime/channel-readiness-types.ts +35 -0
- package/src/runtime/channel-retry-sweep.ts +184 -0
- package/src/runtime/guardian-context-resolver.ts +108 -0
- package/src/runtime/http-server.ts +275 -717
- package/src/runtime/http-types.ts +59 -3
- package/src/runtime/middleware/auth.ts +116 -0
- package/src/runtime/middleware/error-handler.ts +33 -0
- package/src/runtime/middleware/twilio-validation.ts +127 -0
- package/src/runtime/routes/app-routes.ts +1 -1
- package/src/runtime/routes/call-routes.ts +51 -7
- package/src/runtime/routes/channel-delivery-routes.ts +170 -0
- package/src/runtime/routes/channel-guardian-routes.ts +1191 -0
- package/src/runtime/routes/channel-inbound-routes.ts +1152 -0
- package/src/runtime/routes/channel-route-shared.ts +144 -0
- package/src/runtime/routes/channel-routes.ts +32 -1588
- package/src/runtime/routes/conversation-routes.ts +50 -7
- package/src/runtime/routes/events-routes.ts +2 -2
- package/src/runtime/routes/identity-routes.ts +126 -0
- package/src/runtime/routes/pairing-routes.ts +143 -0
- package/src/runtime/routes/run-routes.ts +15 -1
- package/src/runtime/run-orchestrator.ts +86 -35
- package/src/schedule/schedule-store.ts +36 -32
- package/src/schedule/scheduler.ts +3 -3
- package/src/security/encrypted-store.ts +5 -7
- package/src/security/oauth2.ts +45 -15
- package/src/security/parental-control-store.ts +183 -0
- package/src/security/secret-allowlist.ts +4 -3
- package/src/security/secret-scanner.ts +5 -5
- package/src/security/secure-keys.ts +1 -1
- package/src/security/token-manager.ts +3 -2
- package/src/services/vercel-deploy.ts +6 -2
- package/src/skills/tool-manifest.ts +3 -3
- package/src/skills/vellum-catalog-remote.ts +75 -16
- package/src/slack/slack-webhook.ts +2 -1
- package/src/swarm/orchestrator.ts +92 -1
- package/src/swarm/router-planner.ts +6 -9
- package/src/swarm/worker-prompts.ts +9 -12
- package/src/tasks/task-compiler.ts +19 -28
- package/src/tasks/task-runner.ts +1 -1
- package/src/tools/assets/materialize.ts +2 -2
- package/src/tools/assets/search.ts +15 -14
- package/src/tools/browser/__tests__/auth-detector.test.ts +1 -0
- package/src/tools/browser/auto-navigate.ts +1 -0
- package/src/tools/browser/browser-execution.ts +10 -1
- package/src/tools/browser/browser-manager.ts +119 -4
- package/src/tools/browser/network-recorder.ts +5 -0
- package/src/tools/calls/call-start.ts +1 -0
- package/src/tools/credentials/broker.ts +11 -2
- package/src/tools/credentials/metadata-store.ts +18 -14
- package/src/tools/credentials/post-connect-hooks.ts +61 -0
- package/src/tools/credentials/vault.ts +49 -23
- package/src/tools/execution-target.ts +11 -1
- package/src/tools/executor.ts +68 -9
- package/src/tools/host-terminal/cli-discover.ts +1 -1
- package/src/tools/network/script-proxy/http-forwarder.ts +1 -1
- package/src/tools/network/script-proxy/mitm-handler.ts +1 -1
- package/src/tools/network/script-proxy/server.ts +1 -1
- package/src/tools/network/script-proxy/session-manager.ts +6 -5
- package/src/tools/network/web-fetch.ts +18 -2
- package/src/tools/network/web-search.ts +8 -4
- package/src/tools/reminder/reminder-store.ts +14 -15
- package/src/tools/schedule/create.ts +1 -0
- package/src/tools/schedule/list.ts +2 -1
- package/src/tools/shared/filesystem/file-ops-service.ts +5 -7
- package/src/tools/skills/skill-script-runner.ts +24 -9
- package/src/tools/skills/skill-tool-factory.ts +1 -0
- package/src/tools/tasks/work-item-enqueue.ts +2 -2
- package/src/tools/terminal/evaluate-typescript.ts +21 -12
- package/src/tools/terminal/parser.ts +50 -0
- package/src/tools/types.ts +2 -0
- package/src/tools/watcher/delete.ts +6 -0
- package/src/tools/weather/service.ts +1 -1
- package/src/twitter/client.ts +190 -24
- package/src/twitter/router.ts +1 -1
- package/src/twitter/session.ts +4 -3
- package/src/util/clipboard.ts +1 -1
- package/src/util/errors.ts +65 -8
- package/src/util/fs.ts +40 -0
- package/src/util/json.ts +10 -0
- package/src/util/log-redact.ts +189 -0
- package/src/util/logger.ts +19 -17
- package/src/util/object.ts +3 -0
- package/src/util/platform.ts +105 -363
- package/src/util/pricing.ts +1 -1
- package/src/util/promise-guard.ts +1 -1
- package/src/util/retry.ts +19 -0
- package/src/util/row-mapper.ts +79 -0
- package/src/util/silently.ts +21 -0
- package/src/watcher/engine.ts +5 -1
- package/src/watcher/provider-types.ts +20 -0
- package/src/watcher/providers/github.ts +156 -0
- package/src/watcher/providers/gmail.ts +1 -0
- package/src/watcher/providers/google-calendar.ts +1 -0
- package/src/watcher/providers/linear.ts +460 -0
- package/src/watcher/providers/slack.ts +1 -0
- package/src/work-items/work-item-runner.ts +1 -1
- package/src/workspace/git-service.ts +1 -1
- package/src/workspace/provider-commit-message-generator.ts +51 -22
- package/src/__tests__/call-bridge.test.ts +0 -517
- package/src/__tests__/session-process-bridge.test.ts +0 -244
- package/src/calls/call-bridge.ts +0 -168
- package/src/config/vellum-skills/google-oauth-setup/SKILL.md +0 -199
|
@@ -42,7 +42,7 @@ export function createMessageParser(options?: { maxLineSize?: number }) {
|
|
|
42
42
|
try {
|
|
43
43
|
const msg = JSON.parse(trimmed);
|
|
44
44
|
const entry: ParsedMessage = { msg };
|
|
45
|
-
if (typeof msg === 'object' && msg
|
|
45
|
+
if (typeof msg === 'object' && msg != null && msg.type === 'cu_observation') {
|
|
46
46
|
entry.rawByteLength = Buffer.byteLength(trimmed, 'utf8');
|
|
47
47
|
}
|
|
48
48
|
results.push(entry);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isChannelId } from '../channels/types.js';
|
|
1
2
|
import type { ClientMessage } from './ipc-contract.js';
|
|
2
3
|
import inventory from './ipc-contract-inventory.json' with { type: 'json' };
|
|
3
4
|
|
|
@@ -82,6 +83,9 @@ const HIGH_RISK_VALIDATORS: Record<string, PropertyValidator> = {
|
|
|
82
83
|
if (obj.activeSurfaceId !== undefined && typeof obj.activeSurfaceId !== 'string') {
|
|
83
84
|
return 'user_message "activeSurfaceId" must be a string when present';
|
|
84
85
|
}
|
|
86
|
+
if (obj.channel !== undefined && !isChannelId(obj.channel)) {
|
|
87
|
+
return 'user_message "channel" must be a valid channel ID when present';
|
|
88
|
+
}
|
|
85
89
|
return null;
|
|
86
90
|
},
|
|
87
91
|
|
|
@@ -100,6 +104,9 @@ const HIGH_RISK_VALIDATORS: Record<string, PropertyValidator> = {
|
|
|
100
104
|
if (typeof transport.channelId !== 'string' || transport.channelId.trim().length === 0) {
|
|
101
105
|
return 'session_create "transport.channelId" must be a non-empty string';
|
|
102
106
|
}
|
|
107
|
+
if (!isChannelId(transport.channelId)) {
|
|
108
|
+
return 'session_create "transport.channelId" must be a valid channel ID';
|
|
109
|
+
}
|
|
103
110
|
if (transport.uxBrief !== undefined && typeof transport.uxBrief !== 'string') {
|
|
104
111
|
return 'session_create "transport.uxBrief" must be a string when present';
|
|
105
112
|
}
|
package/src/daemon/lifecycle.ts
CHANGED
|
@@ -1,260 +1,64 @@
|
|
|
1
|
-
import { spawn } from 'node:child_process';
|
|
2
1
|
import { randomBytes } from 'node:crypto';
|
|
3
|
-
import { mkdirSync, readFileSync, writeFileSync,
|
|
2
|
+
import { mkdirSync, readFileSync, writeFileSync, existsSync, chmodSync } from 'node:fs';
|
|
4
3
|
import { createRequire } from 'node:module';
|
|
5
|
-
import { dirname, join
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
6
5
|
import { config as dotenvConfig } from 'dotenv';
|
|
7
|
-
import * as Sentry from '@sentry/node';
|
|
8
6
|
import {
|
|
9
7
|
getInterfacesDir,
|
|
10
8
|
getSocketPath,
|
|
11
|
-
getPidPath,
|
|
12
9
|
getHttpTokenPath,
|
|
13
10
|
getRootDir,
|
|
14
11
|
ensureDataDir,
|
|
15
|
-
migrateToDataLayout,
|
|
16
|
-
migrateToWorkspaceLayout,
|
|
17
|
-
removeSocketFile,
|
|
18
12
|
} from '../util/platform.js';
|
|
13
|
+
import { migrateToDataLayout } from '../migrations/data-layout.js';
|
|
14
|
+
import { migrateToWorkspaceLayout } from '../migrations/workspace-layout.js';
|
|
19
15
|
import { initializeDb } from '../memory/db.js';
|
|
20
16
|
import { rotateToolInvocations } from '../memory/tool-usage-store.js';
|
|
21
|
-
import { initializeProviders } from '../providers/registry.js';
|
|
22
|
-
import { initializeTools } from '../tools/registry.js';
|
|
23
17
|
import { loadConfig } from '../config/loader.js';
|
|
18
|
+
import {
|
|
19
|
+
getQdrantUrlEnv,
|
|
20
|
+
getRuntimeHttpPort,
|
|
21
|
+
getRuntimeProxyBearerToken,
|
|
22
|
+
getRuntimeHttpHost,
|
|
23
|
+
validateEnv,
|
|
24
|
+
} from '../config/env.js';
|
|
24
25
|
import { ensurePromptFiles } from '../config/system-prompt.js';
|
|
25
26
|
import { loadPrebuiltHtml } from '../home-base/prebuilt/seed.js';
|
|
26
27
|
import { DaemonServer } from './server.js';
|
|
28
|
+
import { setRelayBroadcast } from '../calls/relay-server.js';
|
|
27
29
|
import { listWorkItems, updateWorkItem } from '../work-items/work-item-store.js';
|
|
28
30
|
import { getLogger, initLogger } from '../util/logger.js';
|
|
29
|
-
import { DaemonError } from '../util/errors.js';
|
|
30
31
|
import { initSentry } from '../instrument.js';
|
|
31
32
|
import { initLogfire } from '../logfire.js';
|
|
32
33
|
import { startMemoryJobsWorker } from '../memory/jobs-worker.js';
|
|
33
34
|
import { QdrantManager } from '../memory/qdrant-manager.js';
|
|
34
35
|
import { initQdrantClient } from '../memory/qdrant-client.js';
|
|
35
36
|
import { startScheduler } from '../schedule/scheduler.js';
|
|
36
|
-
import { initWatcherEngine } from '../watcher/engine.js';
|
|
37
|
-
import { registerWatcherProvider } from '../watcher/provider-registry.js';
|
|
38
|
-
import { gmailProvider } from '../watcher/providers/gmail.js';
|
|
39
|
-
import { googleCalendarProvider } from '../watcher/providers/google-calendar.js';
|
|
40
|
-
import { slackProvider as slackWatcherProvider } from '../watcher/providers/slack.js';
|
|
41
|
-
import { registerMessagingProvider } from '../messaging/registry.js';
|
|
42
|
-
import { slackProvider as slackMessagingProvider } from '../messaging/providers/slack/adapter.js';
|
|
43
|
-
import { gmailMessagingProvider } from '../messaging/providers/gmail/adapter.js';
|
|
44
|
-
import { telegramBotMessagingProvider } from '../messaging/providers/telegram-bot/adapter.js';
|
|
45
|
-
import { browserManager } from '../tools/browser/browser-manager.js';
|
|
46
37
|
import { RuntimeHttpServer } from '../runtime/http-server.js';
|
|
47
38
|
import { getHookManager } from '../hooks/manager.js';
|
|
48
39
|
import { installTemplates } from '../hooks/templates.js';
|
|
40
|
+
import { installCliLaunchers } from './install-cli-launchers.js';
|
|
49
41
|
import { HeartbeatService } from '../workspace/heartbeat-service.js';
|
|
50
42
|
import { AgentHeartbeatService } from '../agent-heartbeat/agent-heartbeat-service.js';
|
|
51
|
-
import { getEnrichmentService } from '../workspace/commit-message-enrichment-service.js';
|
|
52
43
|
import { reconcileCallsOnStartup } from '../calls/call-recovery.js';
|
|
53
44
|
import { TwilioConversationRelayProvider } from '../calls/twilio-provider.js';
|
|
45
|
+
import { createApprovalCopyGenerator, createApprovalConversationGenerator } from './approval-generators.js';
|
|
46
|
+
import { initializeProvidersAndTools, registerWatcherProviders, registerMessagingProviders } from './providers-setup.js';
|
|
47
|
+
import { installShutdownHandlers } from './shutdown-handlers.js';
|
|
48
|
+
import { writePid, cleanupPidFile } from './daemon-control.js';
|
|
49
|
+
|
|
50
|
+
// Re-export public API so existing consumers don't need to change imports
|
|
51
|
+
export {
|
|
52
|
+
isDaemonRunning,
|
|
53
|
+
getDaemonStatus,
|
|
54
|
+
startDaemon,
|
|
55
|
+
stopDaemon,
|
|
56
|
+
ensureDaemonRunning,
|
|
57
|
+
} from './daemon-control.js';
|
|
58
|
+
export type { StopResult } from './daemon-control.js';
|
|
54
59
|
|
|
55
60
|
const log = getLogger('lifecycle');
|
|
56
61
|
|
|
57
|
-
function isProcessRunning(pid: number): boolean {
|
|
58
|
-
try {
|
|
59
|
-
process.kill(pid, 0);
|
|
60
|
-
return true;
|
|
61
|
-
} catch {
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function readPid(): number | null {
|
|
67
|
-
const pidPath = getPidPath();
|
|
68
|
-
if (!existsSync(pidPath)) return null;
|
|
69
|
-
try {
|
|
70
|
-
const pid = parseInt(readFileSync(pidPath, 'utf-8').trim(), 10);
|
|
71
|
-
return isNaN(pid) ? null : pid;
|
|
72
|
-
} catch {
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
function writePid(pid: number): void {
|
|
78
|
-
writeFileSync(getPidPath(), String(pid));
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function cleanupPidFile(): void {
|
|
82
|
-
const pidPath = getPidPath();
|
|
83
|
-
if (existsSync(pidPath)) {
|
|
84
|
-
unlinkSync(pidPath);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
export function isDaemonRunning(): boolean {
|
|
89
|
-
const pid = readPid();
|
|
90
|
-
if (pid === null) return false;
|
|
91
|
-
if (!isProcessRunning(pid)) {
|
|
92
|
-
// Stale PID file
|
|
93
|
-
cleanupPidFile();
|
|
94
|
-
return false;
|
|
95
|
-
}
|
|
96
|
-
return true;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export function getDaemonStatus(): { running: boolean; pid?: number } {
|
|
100
|
-
const pid = readPid();
|
|
101
|
-
if (pid === null) return { running: false };
|
|
102
|
-
if (!isProcessRunning(pid)) {
|
|
103
|
-
cleanupPidFile();
|
|
104
|
-
return { running: false };
|
|
105
|
-
}
|
|
106
|
-
return { running: true, pid };
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
export async function startDaemon(): Promise<{
|
|
110
|
-
pid: number;
|
|
111
|
-
alreadyRunning: boolean;
|
|
112
|
-
}> {
|
|
113
|
-
const status = getDaemonStatus();
|
|
114
|
-
if (status.running && status.pid) {
|
|
115
|
-
return { pid: status.pid, alreadyRunning: true };
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// Only create the root dir for socket/PID — the daemon process itself
|
|
119
|
-
// handles migration + full ensureDataDir() in runDaemon(). Calling
|
|
120
|
-
// ensureDataDir() here would pre-create workspace destination dirs
|
|
121
|
-
// and cause migration moves to no-op.
|
|
122
|
-
const rootDir = getRootDir();
|
|
123
|
-
if (!existsSync(rootDir)) {
|
|
124
|
-
mkdirSync(rootDir, { recursive: true });
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// Clean up stale socket (only if it's actually a Unix socket)
|
|
128
|
-
const socketPath = getSocketPath();
|
|
129
|
-
removeSocketFile(socketPath);
|
|
130
|
-
|
|
131
|
-
// Spawn the daemon as a detached child process
|
|
132
|
-
const mainPath = resolve(
|
|
133
|
-
import.meta.dirname ?? __dirname,
|
|
134
|
-
'main.ts',
|
|
135
|
-
);
|
|
136
|
-
|
|
137
|
-
// Redirect the child's stderr to a file instead of piping it back to the
|
|
138
|
-
// parent. A pipe's read end is destroyed when the parent exits, leaving
|
|
139
|
-
// fd 2 broken in the child. Bun (unlike Node.js) does not ignore SIGPIPE,
|
|
140
|
-
// so any later stderr write would silently kill the daemon.
|
|
141
|
-
const stderrPath = join(rootDir, 'daemon-stderr.log');
|
|
142
|
-
const stderrFd = openSync(stderrPath, 'w');
|
|
143
|
-
|
|
144
|
-
const child = spawn('bun', ['run', mainPath], {
|
|
145
|
-
detached: true,
|
|
146
|
-
stdio: ['ignore', 'ignore', stderrFd],
|
|
147
|
-
env: { ...process.env },
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
// The child inherited the fd; close the parent's copy.
|
|
151
|
-
closeSync(stderrFd);
|
|
152
|
-
|
|
153
|
-
let childExited = false;
|
|
154
|
-
let childExitCode: number | null = null;
|
|
155
|
-
child.on('exit', (code) => {
|
|
156
|
-
childExited = true;
|
|
157
|
-
childExitCode = code;
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
child.unref();
|
|
161
|
-
|
|
162
|
-
const pid = child.pid;
|
|
163
|
-
if (!pid) {
|
|
164
|
-
throw new DaemonError('Failed to start daemon: no PID returned');
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
writePid(pid);
|
|
168
|
-
|
|
169
|
-
// Wait for socket to appear
|
|
170
|
-
const maxWait = 5000;
|
|
171
|
-
const interval = 100;
|
|
172
|
-
let waited = 0;
|
|
173
|
-
while (waited < maxWait) {
|
|
174
|
-
if (existsSync(socketPath)) {
|
|
175
|
-
return { pid, alreadyRunning: false };
|
|
176
|
-
}
|
|
177
|
-
if (childExited) {
|
|
178
|
-
cleanupPidFile();
|
|
179
|
-
const stderr = readFileSync(stderrPath, 'utf-8').trim();
|
|
180
|
-
const detail = stderr
|
|
181
|
-
? `\n${stderr}`
|
|
182
|
-
: `\nCheck logs at ~/.vellum/workspace/data/logs/ for details.`;
|
|
183
|
-
throw new DaemonError(
|
|
184
|
-
`Daemon exited immediately (code ${childExitCode ?? 'unknown'}).${detail}`,
|
|
185
|
-
);
|
|
186
|
-
}
|
|
187
|
-
await new Promise((r) => setTimeout(r, interval));
|
|
188
|
-
waited += interval;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
throw new DaemonError(
|
|
192
|
-
'Daemon started but socket not available after 5 seconds',
|
|
193
|
-
);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
export type StopResult =
|
|
197
|
-
| { stopped: true }
|
|
198
|
-
| { stopped: false; reason: 'not_running' | 'stop_failed' };
|
|
199
|
-
|
|
200
|
-
export async function stopDaemon(): Promise<StopResult> {
|
|
201
|
-
const pid = readPid();
|
|
202
|
-
if (pid === null || !isProcessRunning(pid)) {
|
|
203
|
-
cleanupPidFile();
|
|
204
|
-
return { stopped: false, reason: 'not_running' };
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
process.kill(pid, 'SIGTERM');
|
|
208
|
-
|
|
209
|
-
// Wait for process to exit
|
|
210
|
-
const maxWait = 5000;
|
|
211
|
-
const interval = 100;
|
|
212
|
-
let waited = 0;
|
|
213
|
-
while (waited < maxWait) {
|
|
214
|
-
if (!isProcessRunning(pid)) {
|
|
215
|
-
cleanupPidFile();
|
|
216
|
-
return { stopped: true };
|
|
217
|
-
}
|
|
218
|
-
await new Promise((r) => setTimeout(r, interval));
|
|
219
|
-
waited += interval;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Force kill
|
|
223
|
-
try {
|
|
224
|
-
process.kill(pid, 'SIGKILL');
|
|
225
|
-
} catch (err) {
|
|
226
|
-
log.debug({ err, pid }, 'SIGKILL failed, process already exited');
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// Wait for the process to actually die after SIGKILL. Without this,
|
|
230
|
-
// startDaemon() can race with the dying process's shutdown handler,
|
|
231
|
-
// which removes the socket file and bricks the new daemon.
|
|
232
|
-
const killMaxWait = 2000;
|
|
233
|
-
let killWaited = 0;
|
|
234
|
-
while (killWaited < killMaxWait && isProcessRunning(pid)) {
|
|
235
|
-
await new Promise((r) => setTimeout(r, 100));
|
|
236
|
-
killWaited += 100;
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
// Only clean up if the process has actually exited.
|
|
240
|
-
// If it's still alive after SIGKILL + timeout, preserve both socket
|
|
241
|
-
// and PID file so isDaemonRunning() still reports true and prevents
|
|
242
|
-
// a duplicate daemon from being spawned.
|
|
243
|
-
if (!isProcessRunning(pid)) {
|
|
244
|
-
removeSocketFile(getSocketPath());
|
|
245
|
-
cleanupPidFile();
|
|
246
|
-
return { stopped: true };
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
log.warn({ pid }, 'Daemon process still running after SIGKILL + timeout, leaving socket and PID file intact');
|
|
250
|
-
return { stopped: false, reason: 'stop_failed' };
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
export async function ensureDaemonRunning(): Promise<void> {
|
|
254
|
-
if (isDaemonRunning()) return;
|
|
255
|
-
await startDaemon();
|
|
256
|
-
}
|
|
257
|
-
|
|
258
62
|
function loadDotEnv(): void {
|
|
259
63
|
dotenvConfig({ path: join(getRootDir(), '.env'), quiet: true });
|
|
260
64
|
}
|
|
@@ -262,6 +66,7 @@ function loadDotEnv(): void {
|
|
|
262
66
|
// Entry point for the daemon process itself
|
|
263
67
|
export async function runDaemon(): Promise<void> {
|
|
264
68
|
loadDotEnv();
|
|
69
|
+
validateEnv();
|
|
265
70
|
initSentry();
|
|
266
71
|
await initLogfire();
|
|
267
72
|
|
|
@@ -311,6 +116,12 @@ export async function runDaemon(): Promise<void> {
|
|
|
311
116
|
log.info('Daemon startup: installing templates and initializing DB');
|
|
312
117
|
installTemplates();
|
|
313
118
|
ensurePromptFiles();
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
installCliLaunchers();
|
|
122
|
+
} catch (err) {
|
|
123
|
+
log.warn({ err }, 'CLI launcher installation failed — continuing startup');
|
|
124
|
+
}
|
|
314
125
|
initializeDb();
|
|
315
126
|
log.info('Daemon startup: DB initialized');
|
|
316
127
|
|
|
@@ -325,8 +136,6 @@ export async function runDaemon(): Promise<void> {
|
|
|
325
136
|
log.info({ count: orphanedRunning.length }, 'Recovered orphaned running work items');
|
|
326
137
|
}
|
|
327
138
|
|
|
328
|
-
// Reconcile in-flight calls that were left in non-terminal states
|
|
329
|
-
// after a daemon crash or restart.
|
|
330
139
|
try {
|
|
331
140
|
const twilioProvider = new TwilioConversationRelayProvider();
|
|
332
141
|
await reconcileCallsOnStartup(twilioProvider, log);
|
|
@@ -341,10 +150,7 @@ export async function runDaemon(): Promise<void> {
|
|
|
341
150
|
initLogger({ dir: config.logFile.dir, retentionDays: config.logFile.retentionDays });
|
|
342
151
|
}
|
|
343
152
|
|
|
344
|
-
|
|
345
|
-
initializeProviders(config);
|
|
346
|
-
await initializeTools();
|
|
347
|
-
log.info('Daemon startup: providers and tools initialized');
|
|
153
|
+
await initializeProvidersAndTools(config);
|
|
348
154
|
|
|
349
155
|
// Start the IPC socket BEFORE Qdrant so that clients can connect
|
|
350
156
|
// immediately. Qdrant startup can take 30+ seconds (binary download,
|
|
@@ -355,11 +161,9 @@ export async function runDaemon(): Promise<void> {
|
|
|
355
161
|
log.info('Daemon startup: DaemonServer started');
|
|
356
162
|
|
|
357
163
|
// Initialize Qdrant vector store — non-fatal so the daemon stays up without it
|
|
358
|
-
const qdrantUrl =
|
|
164
|
+
const qdrantUrl = getQdrantUrlEnv() || config.memory.qdrant.url;
|
|
359
165
|
log.info({ qdrantUrl }, 'Daemon startup: initializing Qdrant');
|
|
360
|
-
const qdrantManager = new QdrantManager({
|
|
361
|
-
url: qdrantUrl,
|
|
362
|
-
});
|
|
166
|
+
const qdrantManager = new QdrantManager({ url: qdrantUrl });
|
|
363
167
|
try {
|
|
364
168
|
await qdrantManager.start();
|
|
365
169
|
initQdrantClient({
|
|
@@ -376,16 +180,9 @@ export async function runDaemon(): Promise<void> {
|
|
|
376
180
|
|
|
377
181
|
log.info('Daemon startup: starting memory worker');
|
|
378
182
|
const memoryWorker = startMemoryJobsWorker();
|
|
379
|
-
// Initialize watcher engine and register providers
|
|
380
|
-
registerWatcherProvider(gmailProvider);
|
|
381
|
-
registerWatcherProvider(googleCalendarProvider);
|
|
382
|
-
registerWatcherProvider(slackWatcherProvider);
|
|
383
|
-
initWatcherEngine();
|
|
384
183
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
registerMessagingProvider(gmailMessagingProvider);
|
|
388
|
-
registerMessagingProvider(telegramBotMessagingProvider);
|
|
184
|
+
registerWatcherProviders();
|
|
185
|
+
registerMessagingProviders();
|
|
389
186
|
|
|
390
187
|
const scheduler = startScheduler(
|
|
391
188
|
async (conversationId, message) => {
|
|
@@ -422,57 +219,57 @@ export async function runDaemon(): Promise<void> {
|
|
|
422
219
|
},
|
|
423
220
|
);
|
|
424
221
|
|
|
425
|
-
// Start
|
|
222
|
+
// Start the runtime HTTP server. Required for iOS pairing (gateway proxies
|
|
223
|
+
// to it) and optional REST API access. Defaults to port 7821.
|
|
426
224
|
let runtimeHttp: RuntimeHttpServer | null = null;
|
|
427
|
-
const
|
|
428
|
-
log.info({
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
} catch {
|
|
443
|
-
// File doesn't exist or can't be read — will generate below
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
if (!bearerToken) {
|
|
447
|
-
bearerToken = randomBytes(32).toString('hex');
|
|
448
|
-
}
|
|
449
|
-
writeFileSync(httpTokenPath, bearerToken, { mode: 0o600 });
|
|
450
|
-
chmodSync(httpTokenPath, 0o600);
|
|
451
|
-
|
|
452
|
-
const hostname = process.env.RUNTIME_HTTP_HOST?.trim() || '127.0.0.1';
|
|
453
|
-
|
|
454
|
-
runtimeHttp = new RuntimeHttpServer({
|
|
455
|
-
port,
|
|
456
|
-
hostname,
|
|
457
|
-
bearerToken,
|
|
458
|
-
processMessage: (conversationId, content, attachmentIds, options, sourceChannel) =>
|
|
459
|
-
server.processMessage(conversationId, content, attachmentIds, options, sourceChannel),
|
|
460
|
-
persistAndProcessMessage: (conversationId, content, attachmentIds, options, sourceChannel) =>
|
|
461
|
-
server.persistAndProcessMessage(conversationId, content, attachmentIds, options, sourceChannel),
|
|
462
|
-
runOrchestrator: server.createRunOrchestrator(),
|
|
463
|
-
interfacesDir: getInterfacesDir(),
|
|
464
|
-
});
|
|
465
|
-
try {
|
|
466
|
-
log.info({ port, hostname }, 'Daemon startup: starting runtime HTTP server');
|
|
467
|
-
await runtimeHttp.start();
|
|
468
|
-
server.setHttpPort(port);
|
|
469
|
-
log.info({ port, hostname }, 'Daemon startup: runtime HTTP server listening');
|
|
470
|
-
} catch (err) {
|
|
471
|
-
log.warn({ err, port }, 'Failed to start runtime HTTP server, continuing without it');
|
|
472
|
-
runtimeHttp = null;
|
|
473
|
-
}
|
|
225
|
+
const httpPort = getRuntimeHttpPort();
|
|
226
|
+
log.info({ httpPort }, 'Daemon startup: starting runtime HTTP server');
|
|
227
|
+
|
|
228
|
+
// Resolve the bearer token in priority order:
|
|
229
|
+
// 1. Explicit env var (e.g. cloud deploys)
|
|
230
|
+
// 2. Existing token file on disk (preserves QR-paired iOS devices across restarts)
|
|
231
|
+
// 3. Fresh random token (first-time startup)
|
|
232
|
+
const httpTokenPath = getHttpTokenPath();
|
|
233
|
+
let bearerToken = getRuntimeProxyBearerToken();
|
|
234
|
+
if (!bearerToken) {
|
|
235
|
+
try {
|
|
236
|
+
const existing = readFileSync(httpTokenPath, 'utf-8').trim();
|
|
237
|
+
if (existing) bearerToken = existing;
|
|
238
|
+
} catch {
|
|
239
|
+
// File doesn't exist or can't be read — will generate below
|
|
474
240
|
}
|
|
475
241
|
}
|
|
242
|
+
if (!bearerToken) {
|
|
243
|
+
bearerToken = randomBytes(32).toString('hex');
|
|
244
|
+
}
|
|
245
|
+
writeFileSync(httpTokenPath, bearerToken, { mode: 0o600 });
|
|
246
|
+
chmodSync(httpTokenPath, 0o600);
|
|
247
|
+
|
|
248
|
+
const hostname = getRuntimeHttpHost();
|
|
249
|
+
|
|
250
|
+
runtimeHttp = new RuntimeHttpServer({
|
|
251
|
+
port: httpPort,
|
|
252
|
+
hostname,
|
|
253
|
+
bearerToken,
|
|
254
|
+
processMessage: (conversationId, content, attachmentIds, options, sourceChannel) =>
|
|
255
|
+
server.processMessage(conversationId, content, attachmentIds, options, sourceChannel),
|
|
256
|
+
persistAndProcessMessage: (conversationId, content, attachmentIds, options, sourceChannel) =>
|
|
257
|
+
server.persistAndProcessMessage(conversationId, content, attachmentIds, options, sourceChannel),
|
|
258
|
+
runOrchestrator: server.createRunOrchestrator(),
|
|
259
|
+
interfacesDir: getInterfacesDir(),
|
|
260
|
+
approvalCopyGenerator: createApprovalCopyGenerator(),
|
|
261
|
+
approvalConversationGenerator: createApprovalConversationGenerator(),
|
|
262
|
+
});
|
|
263
|
+
try {
|
|
264
|
+
await runtimeHttp.start();
|
|
265
|
+
setRelayBroadcast((msg) => server.broadcast(msg));
|
|
266
|
+
runtimeHttp.setPairingBroadcast((msg) => server.broadcast(msg));
|
|
267
|
+
server.setHttpPort(httpPort);
|
|
268
|
+
log.info({ port: httpPort, hostname }, 'Daemon startup: runtime HTTP server listening');
|
|
269
|
+
} catch (err) {
|
|
270
|
+
log.warn({ err, port: httpPort }, 'Failed to start runtime HTTP server, continuing without it');
|
|
271
|
+
runtimeHttp = null;
|
|
272
|
+
}
|
|
476
273
|
|
|
477
274
|
writePid(process.pid);
|
|
478
275
|
log.info({ pid: process.pid }, 'Daemon started');
|
|
@@ -485,9 +282,6 @@ export async function runDaemon(): Promise<void> {
|
|
|
485
282
|
socketPath: getSocketPath(),
|
|
486
283
|
});
|
|
487
284
|
|
|
488
|
-
// Rotate old audit log entries after startup handshake is complete.
|
|
489
|
-
// This runs after the socket is listening so it won't block the 5s
|
|
490
|
-
// readiness window in startDaemon().
|
|
491
285
|
if (config.auditLog.retentionDays > 0) {
|
|
492
286
|
try {
|
|
493
287
|
rotateToolInvocations(config.auditLog.retentionDays);
|
|
@@ -496,15 +290,9 @@ export async function runDaemon(): Promise<void> {
|
|
|
496
290
|
}
|
|
497
291
|
}
|
|
498
292
|
|
|
499
|
-
// Start workspace heartbeat service. This periodically checks all
|
|
500
|
-
// tracked workspaces for uncommitted changes and auto-commits when
|
|
501
|
-
// thresholds are exceeded (age > 5 min OR > 20 files changed).
|
|
502
|
-
// Acts as a safety net for long-running operations or background
|
|
503
|
-
// processes that modify workspace files between turn-boundary commits.
|
|
504
293
|
const heartbeat = new HeartbeatService();
|
|
505
294
|
heartbeat.start();
|
|
506
295
|
|
|
507
|
-
// Start model-driven heartbeat service (opt-in via config).
|
|
508
296
|
const agentHeartbeat = new AgentHeartbeatService({
|
|
509
297
|
processMessage: (conversationId, content) =>
|
|
510
298
|
server.processMessage(conversationId, content),
|
|
@@ -512,83 +300,15 @@ export async function runDaemon(): Promise<void> {
|
|
|
512
300
|
});
|
|
513
301
|
agentHeartbeat.start();
|
|
514
302
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
// Set this BEFORE awaiting heartbeat stop and triggering daemon-stop hooks
|
|
526
|
-
// so it covers all potentially-blocking async shutdown work.
|
|
527
|
-
const forceTimer = setTimeout(() => {
|
|
528
|
-
log.warn('Graceful shutdown timed out, forcing exit');
|
|
529
|
-
cleanupPidFile();
|
|
530
|
-
process.exit(1);
|
|
531
|
-
}, 10_000);
|
|
532
|
-
forceTimer.unref();
|
|
533
|
-
|
|
534
|
-
await heartbeat.stop();
|
|
535
|
-
await agentHeartbeat.stop();
|
|
536
|
-
|
|
537
|
-
try {
|
|
538
|
-
await hookManager.trigger('daemon-stop', { pid: process.pid });
|
|
539
|
-
} catch {
|
|
540
|
-
// Don't let hook failures block shutdown
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
// Commit any uncommitted workspace changes before stopping the server.
|
|
544
|
-
// This ensures no workspace state is lost during graceful shutdown.
|
|
545
|
-
try {
|
|
546
|
-
log.info({ phase: 'pre_stop' }, 'Committing pending workspace changes');
|
|
547
|
-
await heartbeat.commitAllPending();
|
|
548
|
-
} catch (err) {
|
|
549
|
-
log.warn({ err, phase: 'pre_stop' }, 'Shutdown workspace commit failed');
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
await server.stop();
|
|
553
|
-
|
|
554
|
-
// Final commit sweep: catch any writes that occurred during server.stop()
|
|
555
|
-
// (e.g. in-flight tool executions completing during drain).
|
|
556
|
-
try {
|
|
557
|
-
log.info({ phase: 'post_stop' }, 'Final workspace commit sweep');
|
|
558
|
-
await heartbeat.commitAllPending();
|
|
559
|
-
} catch (err) {
|
|
560
|
-
log.warn({ err, phase: 'post_stop' }, 'Post-stop workspace commit failed');
|
|
561
|
-
}
|
|
562
|
-
|
|
563
|
-
// Flush in-flight enrichment jobs so shutdown commit notes are not dropped.
|
|
564
|
-
// The enrichment service's shutdown() drains active jobs and discards pending ones.
|
|
565
|
-
try {
|
|
566
|
-
await getEnrichmentService().shutdown();
|
|
567
|
-
} catch (err) {
|
|
568
|
-
log.warn({ err }, 'Enrichment service shutdown failed (non-fatal)');
|
|
569
|
-
}
|
|
570
|
-
|
|
571
|
-
if (runtimeHttp) await runtimeHttp.stop();
|
|
572
|
-
await browserManager.closeAllPages();
|
|
573
|
-
scheduler.stop();
|
|
574
|
-
memoryWorker.stop();
|
|
575
|
-
await qdrantManager.stop();
|
|
576
|
-
await Sentry.flush(2000);
|
|
577
|
-
clearTimeout(forceTimer);
|
|
578
|
-
cleanupPidFile();
|
|
579
|
-
process.exit(0);
|
|
580
|
-
};
|
|
581
|
-
|
|
582
|
-
process.on('SIGTERM', shutdown);
|
|
583
|
-
process.on('SIGINT', shutdown);
|
|
584
|
-
|
|
585
|
-
process.on('unhandledRejection', (reason) => {
|
|
586
|
-
log.error({ err: reason }, 'Unhandled promise rejection');
|
|
587
|
-
Sentry.captureException(reason);
|
|
588
|
-
});
|
|
589
|
-
|
|
590
|
-
process.on('uncaughtException', (err) => {
|
|
591
|
-
log.error({ err }, 'Uncaught exception');
|
|
592
|
-
Sentry.captureException(err);
|
|
303
|
+
installShutdownHandlers({
|
|
304
|
+
server,
|
|
305
|
+
heartbeat,
|
|
306
|
+
agentHeartbeat,
|
|
307
|
+
hookManager,
|
|
308
|
+
runtimeHttp,
|
|
309
|
+
scheduler,
|
|
310
|
+
memoryWorker,
|
|
311
|
+
qdrantManager,
|
|
312
|
+
cleanupPidFile,
|
|
593
313
|
});
|
|
594
314
|
}
|