@vellumai/assistant 0.3.5 → 0.3.7
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/README.md +51 -0
- 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 +18 -2
- package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +338 -1
- package/src/__tests__/approval-conversation-turn.test.ts +214 -0
- package/src/__tests__/browser-manager.test.ts +1 -0
- package/src/__tests__/call-conversation-messages.test.ts +130 -0
- package/src/__tests__/call-orchestrator.test.ts +752 -271
- 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 +5 -0
- package/src/__tests__/call-store.test.ts +3 -0
- package/src/__tests__/channel-approval-routes.test.ts +1260 -85
- package/src/__tests__/channel-approval.test.ts +37 -0
- package/src/__tests__/channel-approvals.test.ts +4 -65
- package/src/__tests__/channel-guardian.test.ts +556 -0
- package/src/__tests__/channel-readiness-service.test.ts +74 -7
- 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 +12 -7
- 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 +6 -2
- package/src/__tests__/db-migration-rollback.test.ts +752 -0
- package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -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 +126 -0
- package/src/__tests__/intent-routing.test.ts +2 -0
- package/src/__tests__/ipc-snapshot.test.ts +228 -1
- package/src/__tests__/memory-upsert-concurrency.test.ts +828 -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 +2 -0
- package/src/__tests__/run-orchestrator.test.ts +20 -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 +237 -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 +2 -1
- 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 +141 -21
- 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 +45 -29
- 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 +106 -5
- package/src/calls/call-orchestrator.ts +252 -54
- 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 +7 -5
- package/src/calls/twilio-provider.ts +6 -4
- package/src/calls/twilio-rest.ts +40 -15
- package/src/calls/twilio-routes.ts +60 -45
- 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/macos-automation/icon.svg +12 -0
- package/src/config/bundled-skills/media-processing/SKILL.md +72 -95
- package/src/config/bundled-skills/media-processing/TOOLS.json +57 -147
- 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 +7 -9
- package/src/config/bundled-skills/media-processing/services/reduce.ts +197 -0
- package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +88 -253
- package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +22 -153
- package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +2 -2
- package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +28 -51
- package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +35 -270
- package/src/config/bundled-skills/messaging/SKILL.md +12 -2
- 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/phone-calls/SKILL.md +86 -21
- 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 +27 -3
- package/src/config/env-registry.ts +169 -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 +157 -1138
- 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 +107 -56
- 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 +0 -7
- package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +5 -1
- package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +1 -0
- package/src/config/vellum-skills/sms-setup/SKILL.md +112 -14
- 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 +254 -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 -2463
- package/src/daemon/handlers/diagnostics.ts +1 -1
- package/src/daemon/handlers/dictation.ts +4 -6
- package/src/daemon/handlers/documents.ts +18 -32
- package/src/daemon/handlers/index.ts +9 -0
- package/src/daemon/handlers/misc.ts +3 -5
- package/src/daemon/handlers/pairing.ts +98 -0
- package/src/daemon/handlers/sessions.ts +74 -5
- package/src/daemon/handlers/shared.ts +3 -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 +2 -2
- 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 +321 -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 +62 -0
- package/src/daemon/ipc-contract-inventory.ts +55 -29
- package/src/daemon/ipc-contract.ts +227 -2527
- package/src/daemon/ipc-protocol.ts +1 -1
- package/src/daemon/ipc-validate.ts +7 -0
- package/src/daemon/lifecycle.ts +97 -379
- package/src/daemon/pairing-store.ts +177 -0
- package/src/daemon/providers-setup.ts +43 -0
- package/src/daemon/ride-shotgun-handler.ts +67 -2
- package/src/daemon/server.ts +60 -44
- package/src/daemon/session-agent-loop-handlers.ts +421 -0
- package/src/daemon/session-agent-loop.ts +113 -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 +98 -4
- package/src/daemon/session-runtime-assembly.ts +149 -15
- package/src/daemon/session-surfaces.ts +26 -4
- package/src/daemon/session-tool-setup.ts +28 -30
- package/src/daemon/session-workspace.ts +1 -1
- package/src/daemon/session.ts +24 -1
- 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 +3 -1
- 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 +200 -1
- 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 +121 -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 +11 -42
- package/src/memory/job-handlers/summarization.ts +32 -26
- package/src/memory/job-utils.ts +3 -10
- package/src/memory/jobs-store.ts +6 -9
- package/src/memory/jobs-worker.ts +51 -36
- 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 +12 -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 +163 -47
- package/src/memory/schema-migration.ts +25 -984
- package/src/memory/schema.ts +130 -7
- package/src/memory/search/entity.ts +10 -19
- package/src/memory/search/lexical.ts +81 -52
- package/src/memory/search/ranking.ts +21 -22
- package/src/memory/search/semantic.ts +157 -19
- 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/providers/sms/adapter.ts +3 -6
- 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 +126 -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 +115 -5
- package/src/runtime/assistant-event-hub.ts +3 -1
- package/src/runtime/channel-approval-parser.ts +36 -2
- package/src/runtime/channel-approvals.ts +0 -21
- package/src/runtime/channel-guardian-service.ts +48 -7
- package/src/runtime/channel-readiness-service.ts +160 -34
- package/src/runtime/channel-readiness-types.ts +10 -4
- package/src/runtime/channel-retry-sweep.ts +184 -0
- package/src/runtime/guardian-context-resolver.ts +108 -0
- package/src/runtime/http-server.ts +289 -745
- package/src/runtime/http-types.ts +56 -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 +49 -6
- 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 -1634
- 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 +144 -0
- package/src/runtime/routes/run-routes.ts +15 -1
- package/src/runtime/run-orchestrator.ts +52 -34
- 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/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 +13 -1
- package/src/tools/browser/browser-manager.ts +119 -4
- package/src/tools/browser/network-recorder.ts +5 -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/executor.ts +80 -18
- 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 +7 -3
- 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/watcher/delete.ts +6 -0
- package/src/tools/weather/service.ts +1 -1
- package/src/twitter/client.ts +190 -24
- 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 +25 -18
- package/src/util/object.ts +3 -0
- package/src/util/platform.ts +72 -365
- 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/bundled-skills/media-processing/services/capability-registry.ts +0 -137
- package/src/config/bundled-skills/media-processing/services/event-detection-service.ts +0 -280
- package/src/config/bundled-skills/media-processing/services/feedback-aggregation.ts +0 -144
- package/src/config/bundled-skills/media-processing/services/feedback-store.ts +0 -136
- package/src/config/bundled-skills/media-processing/services/retrieval-service.ts +0 -95
- package/src/config/bundled-skills/media-processing/services/timeline-service.ts +0 -267
- package/src/config/bundled-skills/media-processing/tools/detect-events.ts +0 -110
- package/src/config/bundled-skills/media-processing/tools/recalibrate.ts +0 -235
- package/src/config/bundled-skills/media-processing/tools/select-tracking-profile.ts +0 -142
- package/src/config/bundled-skills/media-processing/tools/submit-feedback.ts +0 -150
- 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,261 +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 { smsMessagingProvider } from '../messaging/providers/sms/adapter.js';
|
|
46
|
-
import { browserManager } from '../tools/browser/browser-manager.js';
|
|
47
37
|
import { RuntimeHttpServer } from '../runtime/http-server.js';
|
|
48
38
|
import { getHookManager } from '../hooks/manager.js';
|
|
49
39
|
import { installTemplates } from '../hooks/templates.js';
|
|
40
|
+
import { installCliLaunchers } from './install-cli-launchers.js';
|
|
50
41
|
import { HeartbeatService } from '../workspace/heartbeat-service.js';
|
|
51
42
|
import { AgentHeartbeatService } from '../agent-heartbeat/agent-heartbeat-service.js';
|
|
52
|
-
import { getEnrichmentService } from '../workspace/commit-message-enrichment-service.js';
|
|
53
43
|
import { reconcileCallsOnStartup } from '../calls/call-recovery.js';
|
|
54
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';
|
|
55
59
|
|
|
56
60
|
const log = getLogger('lifecycle');
|
|
57
61
|
|
|
58
|
-
function isProcessRunning(pid: number): boolean {
|
|
59
|
-
try {
|
|
60
|
-
process.kill(pid, 0);
|
|
61
|
-
return true;
|
|
62
|
-
} catch {
|
|
63
|
-
return false;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function readPid(): number | null {
|
|
68
|
-
const pidPath = getPidPath();
|
|
69
|
-
if (!existsSync(pidPath)) return null;
|
|
70
|
-
try {
|
|
71
|
-
const pid = parseInt(readFileSync(pidPath, 'utf-8').trim(), 10);
|
|
72
|
-
return isNaN(pid) ? null : pid;
|
|
73
|
-
} catch {
|
|
74
|
-
return null;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function writePid(pid: number): void {
|
|
79
|
-
writeFileSync(getPidPath(), String(pid));
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function cleanupPidFile(): void {
|
|
83
|
-
const pidPath = getPidPath();
|
|
84
|
-
if (existsSync(pidPath)) {
|
|
85
|
-
unlinkSync(pidPath);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function isDaemonRunning(): boolean {
|
|
90
|
-
const pid = readPid();
|
|
91
|
-
if (pid === null) return false;
|
|
92
|
-
if (!isProcessRunning(pid)) {
|
|
93
|
-
// Stale PID file
|
|
94
|
-
cleanupPidFile();
|
|
95
|
-
return false;
|
|
96
|
-
}
|
|
97
|
-
return true;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export function getDaemonStatus(): { running: boolean; pid?: number } {
|
|
101
|
-
const pid = readPid();
|
|
102
|
-
if (pid === null) return { running: false };
|
|
103
|
-
if (!isProcessRunning(pid)) {
|
|
104
|
-
cleanupPidFile();
|
|
105
|
-
return { running: false };
|
|
106
|
-
}
|
|
107
|
-
return { running: true, pid };
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export async function startDaemon(): Promise<{
|
|
111
|
-
pid: number;
|
|
112
|
-
alreadyRunning: boolean;
|
|
113
|
-
}> {
|
|
114
|
-
const status = getDaemonStatus();
|
|
115
|
-
if (status.running && status.pid) {
|
|
116
|
-
return { pid: status.pid, alreadyRunning: true };
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
// Only create the root dir for socket/PID — the daemon process itself
|
|
120
|
-
// handles migration + full ensureDataDir() in runDaemon(). Calling
|
|
121
|
-
// ensureDataDir() here would pre-create workspace destination dirs
|
|
122
|
-
// and cause migration moves to no-op.
|
|
123
|
-
const rootDir = getRootDir();
|
|
124
|
-
if (!existsSync(rootDir)) {
|
|
125
|
-
mkdirSync(rootDir, { recursive: true });
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// Clean up stale socket (only if it's actually a Unix socket)
|
|
129
|
-
const socketPath = getSocketPath();
|
|
130
|
-
removeSocketFile(socketPath);
|
|
131
|
-
|
|
132
|
-
// Spawn the daemon as a detached child process
|
|
133
|
-
const mainPath = resolve(
|
|
134
|
-
import.meta.dirname ?? __dirname,
|
|
135
|
-
'main.ts',
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
// Redirect the child's stderr to a file instead of piping it back to the
|
|
139
|
-
// parent. A pipe's read end is destroyed when the parent exits, leaving
|
|
140
|
-
// fd 2 broken in the child. Bun (unlike Node.js) does not ignore SIGPIPE,
|
|
141
|
-
// so any later stderr write would silently kill the daemon.
|
|
142
|
-
const stderrPath = join(rootDir, 'daemon-stderr.log');
|
|
143
|
-
const stderrFd = openSync(stderrPath, 'w');
|
|
144
|
-
|
|
145
|
-
const child = spawn('bun', ['run', mainPath], {
|
|
146
|
-
detached: true,
|
|
147
|
-
stdio: ['ignore', 'ignore', stderrFd],
|
|
148
|
-
env: { ...process.env },
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
// The child inherited the fd; close the parent's copy.
|
|
152
|
-
closeSync(stderrFd);
|
|
153
|
-
|
|
154
|
-
let childExited = false;
|
|
155
|
-
let childExitCode: number | null = null;
|
|
156
|
-
child.on('exit', (code) => {
|
|
157
|
-
childExited = true;
|
|
158
|
-
childExitCode = code;
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
child.unref();
|
|
162
|
-
|
|
163
|
-
const pid = child.pid;
|
|
164
|
-
if (!pid) {
|
|
165
|
-
throw new DaemonError('Failed to start daemon: no PID returned');
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
writePid(pid);
|
|
169
|
-
|
|
170
|
-
// Wait for socket to appear
|
|
171
|
-
const maxWait = 5000;
|
|
172
|
-
const interval = 100;
|
|
173
|
-
let waited = 0;
|
|
174
|
-
while (waited < maxWait) {
|
|
175
|
-
if (existsSync(socketPath)) {
|
|
176
|
-
return { pid, alreadyRunning: false };
|
|
177
|
-
}
|
|
178
|
-
if (childExited) {
|
|
179
|
-
cleanupPidFile();
|
|
180
|
-
const stderr = readFileSync(stderrPath, 'utf-8').trim();
|
|
181
|
-
const detail = stderr
|
|
182
|
-
? `\n${stderr}`
|
|
183
|
-
: `\nCheck logs at ~/.vellum/workspace/data/logs/ for details.`;
|
|
184
|
-
throw new DaemonError(
|
|
185
|
-
`Daemon exited immediately (code ${childExitCode ?? 'unknown'}).${detail}`,
|
|
186
|
-
);
|
|
187
|
-
}
|
|
188
|
-
await new Promise((r) => setTimeout(r, interval));
|
|
189
|
-
waited += interval;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
throw new DaemonError(
|
|
193
|
-
'Daemon started but socket not available after 5 seconds',
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
export type StopResult =
|
|
198
|
-
| { stopped: true }
|
|
199
|
-
| { stopped: false; reason: 'not_running' | 'stop_failed' };
|
|
200
|
-
|
|
201
|
-
export async function stopDaemon(): Promise<StopResult> {
|
|
202
|
-
const pid = readPid();
|
|
203
|
-
if (pid === null || !isProcessRunning(pid)) {
|
|
204
|
-
cleanupPidFile();
|
|
205
|
-
return { stopped: false, reason: 'not_running' };
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
process.kill(pid, 'SIGTERM');
|
|
209
|
-
|
|
210
|
-
// Wait for process to exit
|
|
211
|
-
const maxWait = 5000;
|
|
212
|
-
const interval = 100;
|
|
213
|
-
let waited = 0;
|
|
214
|
-
while (waited < maxWait) {
|
|
215
|
-
if (!isProcessRunning(pid)) {
|
|
216
|
-
cleanupPidFile();
|
|
217
|
-
return { stopped: true };
|
|
218
|
-
}
|
|
219
|
-
await new Promise((r) => setTimeout(r, interval));
|
|
220
|
-
waited += interval;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
// Force kill
|
|
224
|
-
try {
|
|
225
|
-
process.kill(pid, 'SIGKILL');
|
|
226
|
-
} catch (err) {
|
|
227
|
-
log.debug({ err, pid }, 'SIGKILL failed, process already exited');
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
// Wait for the process to actually die after SIGKILL. Without this,
|
|
231
|
-
// startDaemon() can race with the dying process's shutdown handler,
|
|
232
|
-
// which removes the socket file and bricks the new daemon.
|
|
233
|
-
const killMaxWait = 2000;
|
|
234
|
-
let killWaited = 0;
|
|
235
|
-
while (killWaited < killMaxWait && isProcessRunning(pid)) {
|
|
236
|
-
await new Promise((r) => setTimeout(r, 100));
|
|
237
|
-
killWaited += 100;
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
// Only clean up if the process has actually exited.
|
|
241
|
-
// If it's still alive after SIGKILL + timeout, preserve both socket
|
|
242
|
-
// and PID file so isDaemonRunning() still reports true and prevents
|
|
243
|
-
// a duplicate daemon from being spawned.
|
|
244
|
-
if (!isProcessRunning(pid)) {
|
|
245
|
-
removeSocketFile(getSocketPath());
|
|
246
|
-
cleanupPidFile();
|
|
247
|
-
return { stopped: true };
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
log.warn({ pid }, 'Daemon process still running after SIGKILL + timeout, leaving socket and PID file intact');
|
|
251
|
-
return { stopped: false, reason: 'stop_failed' };
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
export async function ensureDaemonRunning(): Promise<void> {
|
|
255
|
-
if (isDaemonRunning()) return;
|
|
256
|
-
await startDaemon();
|
|
257
|
-
}
|
|
258
|
-
|
|
259
62
|
function loadDotEnv(): void {
|
|
260
63
|
dotenvConfig({ path: join(getRootDir(), '.env'), quiet: true });
|
|
261
64
|
}
|
|
@@ -263,6 +66,7 @@ function loadDotEnv(): void {
|
|
|
263
66
|
// Entry point for the daemon process itself
|
|
264
67
|
export async function runDaemon(): Promise<void> {
|
|
265
68
|
loadDotEnv();
|
|
69
|
+
validateEnv();
|
|
266
70
|
initSentry();
|
|
267
71
|
await initLogfire();
|
|
268
72
|
|
|
@@ -312,6 +116,12 @@ export async function runDaemon(): Promise<void> {
|
|
|
312
116
|
log.info('Daemon startup: installing templates and initializing DB');
|
|
313
117
|
installTemplates();
|
|
314
118
|
ensurePromptFiles();
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
installCliLaunchers();
|
|
122
|
+
} catch (err) {
|
|
123
|
+
log.warn({ err }, 'CLI launcher installation failed — continuing startup');
|
|
124
|
+
}
|
|
315
125
|
initializeDb();
|
|
316
126
|
log.info('Daemon startup: DB initialized');
|
|
317
127
|
|
|
@@ -326,8 +136,6 @@ export async function runDaemon(): Promise<void> {
|
|
|
326
136
|
log.info({ count: orphanedRunning.length }, 'Recovered orphaned running work items');
|
|
327
137
|
}
|
|
328
138
|
|
|
329
|
-
// Reconcile in-flight calls that were left in non-terminal states
|
|
330
|
-
// after a daemon crash or restart.
|
|
331
139
|
try {
|
|
332
140
|
const twilioProvider = new TwilioConversationRelayProvider();
|
|
333
141
|
await reconcileCallsOnStartup(twilioProvider, log);
|
|
@@ -342,10 +150,7 @@ export async function runDaemon(): Promise<void> {
|
|
|
342
150
|
initLogger({ dir: config.logFile.dir, retentionDays: config.logFile.retentionDays });
|
|
343
151
|
}
|
|
344
152
|
|
|
345
|
-
|
|
346
|
-
initializeProviders(config);
|
|
347
|
-
await initializeTools();
|
|
348
|
-
log.info('Daemon startup: providers and tools initialized');
|
|
153
|
+
await initializeProvidersAndTools(config);
|
|
349
154
|
|
|
350
155
|
// Start the IPC socket BEFORE Qdrant so that clients can connect
|
|
351
156
|
// immediately. Qdrant startup can take 30+ seconds (binary download,
|
|
@@ -356,11 +161,9 @@ export async function runDaemon(): Promise<void> {
|
|
|
356
161
|
log.info('Daemon startup: DaemonServer started');
|
|
357
162
|
|
|
358
163
|
// Initialize Qdrant vector store — non-fatal so the daemon stays up without it
|
|
359
|
-
const qdrantUrl =
|
|
164
|
+
const qdrantUrl = getQdrantUrlEnv() || config.memory.qdrant.url;
|
|
360
165
|
log.info({ qdrantUrl }, 'Daemon startup: initializing Qdrant');
|
|
361
|
-
const qdrantManager = new QdrantManager({
|
|
362
|
-
url: qdrantUrl,
|
|
363
|
-
});
|
|
166
|
+
const qdrantManager = new QdrantManager({ url: qdrantUrl });
|
|
364
167
|
try {
|
|
365
168
|
await qdrantManager.start();
|
|
366
169
|
initQdrantClient({
|
|
@@ -377,17 +180,9 @@ export async function runDaemon(): Promise<void> {
|
|
|
377
180
|
|
|
378
181
|
log.info('Daemon startup: starting memory worker');
|
|
379
182
|
const memoryWorker = startMemoryJobsWorker();
|
|
380
|
-
// Initialize watcher engine and register providers
|
|
381
|
-
registerWatcherProvider(gmailProvider);
|
|
382
|
-
registerWatcherProvider(googleCalendarProvider);
|
|
383
|
-
registerWatcherProvider(slackWatcherProvider);
|
|
384
|
-
initWatcherEngine();
|
|
385
183
|
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
registerMessagingProvider(gmailMessagingProvider);
|
|
389
|
-
registerMessagingProvider(telegramBotMessagingProvider);
|
|
390
|
-
registerMessagingProvider(smsMessagingProvider);
|
|
184
|
+
registerWatcherProviders();
|
|
185
|
+
registerMessagingProviders();
|
|
391
186
|
|
|
392
187
|
const scheduler = startScheduler(
|
|
393
188
|
async (conversationId, message) => {
|
|
@@ -424,57 +219,57 @@ export async function runDaemon(): Promise<void> {
|
|
|
424
219
|
},
|
|
425
220
|
);
|
|
426
221
|
|
|
427
|
-
// 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.
|
|
428
224
|
let runtimeHttp: RuntimeHttpServer | null = null;
|
|
429
|
-
const
|
|
430
|
-
log.info({
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
} catch {
|
|
445
|
-
// File doesn't exist or can't be read — will generate below
|
|
446
|
-
}
|
|
447
|
-
}
|
|
448
|
-
if (!bearerToken) {
|
|
449
|
-
bearerToken = randomBytes(32).toString('hex');
|
|
450
|
-
}
|
|
451
|
-
writeFileSync(httpTokenPath, bearerToken, { mode: 0o600 });
|
|
452
|
-
chmodSync(httpTokenPath, 0o600);
|
|
453
|
-
|
|
454
|
-
const hostname = process.env.RUNTIME_HTTP_HOST?.trim() || '127.0.0.1';
|
|
455
|
-
|
|
456
|
-
runtimeHttp = new RuntimeHttpServer({
|
|
457
|
-
port,
|
|
458
|
-
hostname,
|
|
459
|
-
bearerToken,
|
|
460
|
-
processMessage: (conversationId, content, attachmentIds, options, sourceChannel) =>
|
|
461
|
-
server.processMessage(conversationId, content, attachmentIds, options, sourceChannel),
|
|
462
|
-
persistAndProcessMessage: (conversationId, content, attachmentIds, options, sourceChannel) =>
|
|
463
|
-
server.persistAndProcessMessage(conversationId, content, attachmentIds, options, sourceChannel),
|
|
464
|
-
runOrchestrator: server.createRunOrchestrator(),
|
|
465
|
-
interfacesDir: getInterfacesDir(),
|
|
466
|
-
});
|
|
467
|
-
try {
|
|
468
|
-
log.info({ port, hostname }, 'Daemon startup: starting runtime HTTP server');
|
|
469
|
-
await runtimeHttp.start();
|
|
470
|
-
server.setHttpPort(port);
|
|
471
|
-
log.info({ port, hostname }, 'Daemon startup: runtime HTTP server listening');
|
|
472
|
-
} catch (err) {
|
|
473
|
-
log.warn({ err, port }, 'Failed to start runtime HTTP server, continuing without it');
|
|
474
|
-
runtimeHttp = null;
|
|
475
|
-
}
|
|
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
|
|
476
240
|
}
|
|
477
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
|
+
}
|
|
478
273
|
|
|
479
274
|
writePid(process.pid);
|
|
480
275
|
log.info({ pid: process.pid }, 'Daemon started');
|
|
@@ -487,9 +282,6 @@ export async function runDaemon(): Promise<void> {
|
|
|
487
282
|
socketPath: getSocketPath(),
|
|
488
283
|
});
|
|
489
284
|
|
|
490
|
-
// Rotate old audit log entries after startup handshake is complete.
|
|
491
|
-
// This runs after the socket is listening so it won't block the 5s
|
|
492
|
-
// readiness window in startDaemon().
|
|
493
285
|
if (config.auditLog.retentionDays > 0) {
|
|
494
286
|
try {
|
|
495
287
|
rotateToolInvocations(config.auditLog.retentionDays);
|
|
@@ -498,15 +290,9 @@ export async function runDaemon(): Promise<void> {
|
|
|
498
290
|
}
|
|
499
291
|
}
|
|
500
292
|
|
|
501
|
-
// Start workspace heartbeat service. This periodically checks all
|
|
502
|
-
// tracked workspaces for uncommitted changes and auto-commits when
|
|
503
|
-
// thresholds are exceeded (age > 5 min OR > 20 files changed).
|
|
504
|
-
// Acts as a safety net for long-running operations or background
|
|
505
|
-
// processes that modify workspace files between turn-boundary commits.
|
|
506
293
|
const heartbeat = new HeartbeatService();
|
|
507
294
|
heartbeat.start();
|
|
508
295
|
|
|
509
|
-
// Start model-driven heartbeat service (opt-in via config).
|
|
510
296
|
const agentHeartbeat = new AgentHeartbeatService({
|
|
511
297
|
processMessage: (conversationId, content) =>
|
|
512
298
|
server.processMessage(conversationId, content),
|
|
@@ -514,83 +300,15 @@ export async function runDaemon(): Promise<void> {
|
|
|
514
300
|
});
|
|
515
301
|
agentHeartbeat.start();
|
|
516
302
|
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
// Set this BEFORE awaiting heartbeat stop and triggering daemon-stop hooks
|
|
528
|
-
// so it covers all potentially-blocking async shutdown work.
|
|
529
|
-
const forceTimer = setTimeout(() => {
|
|
530
|
-
log.warn('Graceful shutdown timed out, forcing exit');
|
|
531
|
-
cleanupPidFile();
|
|
532
|
-
process.exit(1);
|
|
533
|
-
}, 10_000);
|
|
534
|
-
forceTimer.unref();
|
|
535
|
-
|
|
536
|
-
await heartbeat.stop();
|
|
537
|
-
await agentHeartbeat.stop();
|
|
538
|
-
|
|
539
|
-
try {
|
|
540
|
-
await hookManager.trigger('daemon-stop', { pid: process.pid });
|
|
541
|
-
} catch {
|
|
542
|
-
// Don't let hook failures block shutdown
|
|
543
|
-
}
|
|
544
|
-
|
|
545
|
-
// Commit any uncommitted workspace changes before stopping the server.
|
|
546
|
-
// This ensures no workspace state is lost during graceful shutdown.
|
|
547
|
-
try {
|
|
548
|
-
log.info({ phase: 'pre_stop' }, 'Committing pending workspace changes');
|
|
549
|
-
await heartbeat.commitAllPending();
|
|
550
|
-
} catch (err) {
|
|
551
|
-
log.warn({ err, phase: 'pre_stop' }, 'Shutdown workspace commit failed');
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
await server.stop();
|
|
555
|
-
|
|
556
|
-
// Final commit sweep: catch any writes that occurred during server.stop()
|
|
557
|
-
// (e.g. in-flight tool executions completing during drain).
|
|
558
|
-
try {
|
|
559
|
-
log.info({ phase: 'post_stop' }, 'Final workspace commit sweep');
|
|
560
|
-
await heartbeat.commitAllPending();
|
|
561
|
-
} catch (err) {
|
|
562
|
-
log.warn({ err, phase: 'post_stop' }, 'Post-stop workspace commit failed');
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
// Flush in-flight enrichment jobs so shutdown commit notes are not dropped.
|
|
566
|
-
// The enrichment service's shutdown() drains active jobs and discards pending ones.
|
|
567
|
-
try {
|
|
568
|
-
await getEnrichmentService().shutdown();
|
|
569
|
-
} catch (err) {
|
|
570
|
-
log.warn({ err }, 'Enrichment service shutdown failed (non-fatal)');
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
if (runtimeHttp) await runtimeHttp.stop();
|
|
574
|
-
await browserManager.closeAllPages();
|
|
575
|
-
scheduler.stop();
|
|
576
|
-
memoryWorker.stop();
|
|
577
|
-
await qdrantManager.stop();
|
|
578
|
-
await Sentry.flush(2000);
|
|
579
|
-
clearTimeout(forceTimer);
|
|
580
|
-
cleanupPidFile();
|
|
581
|
-
process.exit(0);
|
|
582
|
-
};
|
|
583
|
-
|
|
584
|
-
process.on('SIGTERM', shutdown);
|
|
585
|
-
process.on('SIGINT', shutdown);
|
|
586
|
-
|
|
587
|
-
process.on('unhandledRejection', (reason) => {
|
|
588
|
-
log.error({ err: reason }, 'Unhandled promise rejection');
|
|
589
|
-
Sentry.captureException(reason);
|
|
590
|
-
});
|
|
591
|
-
|
|
592
|
-
process.on('uncaughtException', (err) => {
|
|
593
|
-
log.error({ err }, 'Uncaught exception');
|
|
594
|
-
Sentry.captureException(err);
|
|
303
|
+
installShutdownHandlers({
|
|
304
|
+
server,
|
|
305
|
+
heartbeat,
|
|
306
|
+
agentHeartbeat,
|
|
307
|
+
hookManager,
|
|
308
|
+
runtimeHttp,
|
|
309
|
+
scheduler,
|
|
310
|
+
memoryWorker,
|
|
311
|
+
qdrantManager,
|
|
312
|
+
cleanupPidFile,
|
|
595
313
|
});
|
|
596
314
|
}
|