@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
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Route handlers for conversation messages and suggestions.
|
|
3
3
|
*/
|
|
4
|
+
import { CHANNEL_IDS, parseChannelId } from '../../channels/types.js';
|
|
4
5
|
import { existsSync, readdirSync, statSync } from 'node:fs';
|
|
5
6
|
import { join, relative } from 'node:path';
|
|
6
7
|
import {
|
|
@@ -10,8 +11,7 @@ import {
|
|
|
10
11
|
import * as conversationStore from '../../memory/conversation-store.js';
|
|
11
12
|
import * as attachmentsStore from '../../memory/attachments-store.js';
|
|
12
13
|
import { renderHistoryContent, mergeToolResults } from '../../daemon/handlers.js';
|
|
13
|
-
import {
|
|
14
|
-
import { getFailoverProvider, listProviders } from '../../providers/registry.js';
|
|
14
|
+
import { getConfiguredProvider } from '../../providers/provider-send-message.js';
|
|
15
15
|
import type { Provider } from '../../providers/types.js';
|
|
16
16
|
import type {
|
|
17
17
|
MessageProcessor,
|
|
@@ -148,7 +148,21 @@ export async function handleSendMessage(
|
|
|
148
148
|
sourceChannel?: string;
|
|
149
149
|
};
|
|
150
150
|
|
|
151
|
-
const { conversationKey, content, attachmentIds
|
|
151
|
+
const { conversationKey, content, attachmentIds } = body;
|
|
152
|
+
if (!body.sourceChannel || typeof body.sourceChannel !== 'string') {
|
|
153
|
+
return Response.json(
|
|
154
|
+
{ error: 'sourceChannel is required' },
|
|
155
|
+
{ status: 400 },
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
const sourceChannel = parseChannelId(body.sourceChannel);
|
|
159
|
+
|
|
160
|
+
if (!sourceChannel) {
|
|
161
|
+
return Response.json(
|
|
162
|
+
{ error: `Invalid sourceChannel: ${body.sourceChannel}. Valid values: ${CHANNEL_IDS.join(', ')}` },
|
|
163
|
+
{ status: 400 },
|
|
164
|
+
);
|
|
165
|
+
}
|
|
152
166
|
|
|
153
167
|
if (!conversationKey) {
|
|
154
168
|
return Response.json(
|
|
@@ -158,7 +172,7 @@ export async function handleSendMessage(
|
|
|
158
172
|
}
|
|
159
173
|
|
|
160
174
|
// Reject non-string content values (numbers, objects, etc.)
|
|
161
|
-
if (content
|
|
175
|
+
if (content != null && typeof content !== 'string') {
|
|
162
176
|
return Response.json(
|
|
163
177
|
{ error: 'content must be a string' },
|
|
164
178
|
{ status: 400 },
|
|
@@ -311,10 +325,9 @@ export async function handleGetSuggestion(
|
|
|
311
325
|
}
|
|
312
326
|
|
|
313
327
|
// Try LLM suggestion using the configured provider
|
|
314
|
-
const
|
|
315
|
-
if (
|
|
328
|
+
const provider = getConfiguredProvider();
|
|
329
|
+
if (provider) {
|
|
316
330
|
try {
|
|
317
|
-
const provider = getFailoverProvider(config.provider, config.providerOrder);
|
|
318
331
|
// Deduplicate concurrent requests
|
|
319
332
|
let promise = suggestionInFlight.get(msg.id);
|
|
320
333
|
if (!promise) {
|
|
@@ -350,3 +363,33 @@ export async function handleGetSuggestion(
|
|
|
350
363
|
|
|
351
364
|
return Response.json({ suggestion: null, messageId: null, source: 'none' as const });
|
|
352
365
|
}
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* GET /search?q=<query>[&limit=<n>][&maxMessagesPerConversation=<n>]
|
|
369
|
+
*
|
|
370
|
+
* Full-text search across all conversation threads (message content + titles).
|
|
371
|
+
* Returns ranked results grouped by conversation, each with matching message excerpts.
|
|
372
|
+
*/
|
|
373
|
+
export function handleSearchConversations(url: URL): Response {
|
|
374
|
+
const query = url.searchParams.get('q') ?? '';
|
|
375
|
+
if (!query.trim()) {
|
|
376
|
+
return Response.json(
|
|
377
|
+
{ error: 'q query parameter is required' },
|
|
378
|
+
{ status: 400 },
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const limit = url.searchParams.has('limit')
|
|
383
|
+
? Number(url.searchParams.get('limit'))
|
|
384
|
+
: undefined;
|
|
385
|
+
const maxMessagesPerConversation = url.searchParams.has('maxMessagesPerConversation')
|
|
386
|
+
? Number(url.searchParams.get('maxMessagesPerConversation'))
|
|
387
|
+
: undefined;
|
|
388
|
+
|
|
389
|
+
const results = conversationStore.searchConversations(query, {
|
|
390
|
+
...(limit !== undefined && !isNaN(limit) ? { limit } : {}),
|
|
391
|
+
...(maxMessagesPerConversation !== undefined && !isNaN(maxMessagesPerConversation) ? { maxMessagesPerConversation } : {}),
|
|
392
|
+
});
|
|
393
|
+
|
|
394
|
+
return Response.json({ query, results });
|
|
395
|
+
}
|
|
@@ -69,7 +69,7 @@ export function handleSubscribeAssistantEvents(
|
|
|
69
69
|
try {
|
|
70
70
|
// Shed stalled consumers: desiredSize <= 0 means the 16-event buffer
|
|
71
71
|
// is full and the client isn't draining it.
|
|
72
|
-
if (controller.desiredSize
|
|
72
|
+
if (controller.desiredSize != null && controller.desiredSize <= 0) {
|
|
73
73
|
sub.dispose();
|
|
74
74
|
cleanup();
|
|
75
75
|
return;
|
|
@@ -114,7 +114,7 @@ export function handleSubscribeAssistantEvents(
|
|
|
114
114
|
try {
|
|
115
115
|
// Apply the same slow-consumer guard as the event path: stop
|
|
116
116
|
// feeding heartbeats into a queue the client is not draining.
|
|
117
|
-
if (controller.desiredSize
|
|
117
|
+
if (controller.desiredSize != null && controller.desiredSize <= 0) {
|
|
118
118
|
sub.dispose();
|
|
119
119
|
cleanup();
|
|
120
120
|
return;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Identity and health endpoint handlers.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { existsSync, readFileSync, statSync, statfsSync } from 'node:fs';
|
|
6
|
+
import { join, dirname } from 'node:path';
|
|
7
|
+
import { fileURLToPath } from 'node:url';
|
|
8
|
+
import { getWorkspacePromptPath, readLockfile } from '../../util/platform.js';
|
|
9
|
+
import { getBaseDataDir } from '../../config/env-registry.js';
|
|
10
|
+
|
|
11
|
+
interface DiskSpaceInfo {
|
|
12
|
+
path: string;
|
|
13
|
+
totalMb: number;
|
|
14
|
+
usedMb: number;
|
|
15
|
+
freeMb: number;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function getDiskSpaceInfo(): DiskSpaceInfo | null {
|
|
19
|
+
try {
|
|
20
|
+
const baseDataDir = getBaseDataDir();
|
|
21
|
+
const diskPath = baseDataDir && existsSync(baseDataDir) ? baseDataDir : '/';
|
|
22
|
+
const stats = statfsSync(diskPath);
|
|
23
|
+
const totalBytes = stats.bsize * stats.blocks;
|
|
24
|
+
const freeBytes = stats.bsize * stats.bavail;
|
|
25
|
+
const bytesToMb = (b: number) => Math.round((b / (1024 * 1024)) * 100) / 100;
|
|
26
|
+
return {
|
|
27
|
+
path: diskPath,
|
|
28
|
+
totalMb: bytesToMb(totalBytes),
|
|
29
|
+
usedMb: bytesToMb(totalBytes - freeBytes),
|
|
30
|
+
freeMb: bytesToMb(freeBytes),
|
|
31
|
+
};
|
|
32
|
+
} catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function handleHealth(): Response {
|
|
38
|
+
return Response.json({
|
|
39
|
+
status: 'healthy',
|
|
40
|
+
timestamp: new Date().toISOString(),
|
|
41
|
+
disk: getDiskSpaceInfo(),
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function handleGetIdentity(): Response {
|
|
46
|
+
const identityPath = getWorkspacePromptPath('IDENTITY.md');
|
|
47
|
+
if (!existsSync(identityPath)) {
|
|
48
|
+
return Response.json({ error: 'IDENTITY.md not found' }, { status: 404 });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const content = readFileSync(identityPath, 'utf-8');
|
|
52
|
+
const fields: Record<string, string> = {};
|
|
53
|
+
for (const line of content.split('\n')) {
|
|
54
|
+
const trimmed = line.trim();
|
|
55
|
+
const lower = trimmed.toLowerCase();
|
|
56
|
+
const extract = (prefix: string): string | null => {
|
|
57
|
+
if (!lower.startsWith(prefix)) return null;
|
|
58
|
+
return trimmed.split(':**').pop()?.trim() ?? null;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const name = extract('- **name:**');
|
|
62
|
+
if (name) { fields.name = name; continue; }
|
|
63
|
+
const role = extract('- **role:**');
|
|
64
|
+
if (role) { fields.role = role; continue; }
|
|
65
|
+
const personality = extract('- **personality:**') ?? extract('- **vibe:**');
|
|
66
|
+
if (personality) { fields.personality = personality; continue; }
|
|
67
|
+
const emoji = extract('- **emoji:**');
|
|
68
|
+
if (emoji) { fields.emoji = emoji; continue; }
|
|
69
|
+
const home = extract('- **home:**');
|
|
70
|
+
if (home) { fields.home = home; continue; }
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Read version from package.json
|
|
74
|
+
let version: string | undefined;
|
|
75
|
+
try {
|
|
76
|
+
const pkgPath = join(dirname(fileURLToPath(import.meta.url)), '../../../package.json');
|
|
77
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
78
|
+
version = pkg.version;
|
|
79
|
+
} catch {
|
|
80
|
+
// ignore
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Read createdAt from IDENTITY.md file birthtime
|
|
84
|
+
let createdAt: string | undefined;
|
|
85
|
+
try {
|
|
86
|
+
const stats = statSync(identityPath);
|
|
87
|
+
createdAt = stats.birthtime.toISOString();
|
|
88
|
+
} catch {
|
|
89
|
+
// ignore
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Read lockfile for assistantId, cloud, and originSystem
|
|
93
|
+
let assistantId: string | undefined;
|
|
94
|
+
let cloud: string | undefined;
|
|
95
|
+
let originSystem: string | undefined;
|
|
96
|
+
try {
|
|
97
|
+
const lockData = readLockfile();
|
|
98
|
+
const assistants = lockData?.assistants as Array<Record<string, unknown>> | undefined;
|
|
99
|
+
if (assistants && assistants.length > 0) {
|
|
100
|
+
// Use the most recently hatched assistant
|
|
101
|
+
const sorted = [...assistants].sort((a, b) => {
|
|
102
|
+
const dateA = new Date(a.hatchedAt as string || 0).getTime();
|
|
103
|
+
const dateB = new Date(b.hatchedAt as string || 0).getTime();
|
|
104
|
+
return dateB - dateA;
|
|
105
|
+
});
|
|
106
|
+
const latest = sorted[0];
|
|
107
|
+
assistantId = latest.assistantId as string | undefined;
|
|
108
|
+
cloud = latest.cloud as string | undefined;
|
|
109
|
+
originSystem = cloud === 'local' ? 'local' : cloud;
|
|
110
|
+
}
|
|
111
|
+
} catch {
|
|
112
|
+
// ignore -- lockfile may not exist
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return Response.json({
|
|
116
|
+
name: fields.name ?? '',
|
|
117
|
+
role: fields.role ?? '',
|
|
118
|
+
personality: fields.personality ?? '',
|
|
119
|
+
emoji: fields.emoji ?? '',
|
|
120
|
+
home: fields.home ?? '',
|
|
121
|
+
version,
|
|
122
|
+
assistantId,
|
|
123
|
+
createdAt,
|
|
124
|
+
originSystem,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pairing HTTP route handlers for device pairing flow.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { getLogger } from '../../util/logger.js';
|
|
6
|
+
import { PairingStore } from '../../daemon/pairing-store.js';
|
|
7
|
+
import {
|
|
8
|
+
isDeviceApproved,
|
|
9
|
+
refreshDevice,
|
|
10
|
+
hashDeviceId,
|
|
11
|
+
} from '../../daemon/approved-devices-store.js';
|
|
12
|
+
|
|
13
|
+
const log = getLogger('runtime-http');
|
|
14
|
+
|
|
15
|
+
export interface PairingHandlerContext {
|
|
16
|
+
pairingStore: PairingStore;
|
|
17
|
+
bearerToken: string | undefined;
|
|
18
|
+
pairingBroadcast?: (msg: { type: string; [key: string]: unknown }) => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* POST /v1/pairing/register -- Bearer-authenticated.
|
|
23
|
+
* macOS pre-registers a pairing request when the QR is displayed.
|
|
24
|
+
*/
|
|
25
|
+
export async function handlePairingRegister(req: Request, ctx: PairingHandlerContext): Promise<Response> {
|
|
26
|
+
try {
|
|
27
|
+
const body = await req.json() as Record<string, unknown>;
|
|
28
|
+
const pairingRequestId = typeof body.pairingRequestId === 'string' ? body.pairingRequestId : '';
|
|
29
|
+
const pairingSecret = typeof body.pairingSecret === 'string' ? body.pairingSecret : '';
|
|
30
|
+
const gatewayUrl = typeof body.gatewayUrl === 'string' ? body.gatewayUrl : '';
|
|
31
|
+
const localLanUrl = typeof body.localLanUrl === 'string' ? body.localLanUrl : null;
|
|
32
|
+
|
|
33
|
+
if (!pairingRequestId || !pairingSecret || !gatewayUrl) {
|
|
34
|
+
return Response.json({ error: 'Missing required fields: pairingRequestId, pairingSecret, gatewayUrl' }, { status: 400 });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const result = ctx.pairingStore.register({ pairingRequestId, pairingSecret, gatewayUrl, localLanUrl });
|
|
38
|
+
if (!result.ok) {
|
|
39
|
+
return Response.json({ error: 'Conflict: pairingRequestId exists with different secret' }, { status: 409 });
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return Response.json({ ok: true });
|
|
43
|
+
} catch (err) {
|
|
44
|
+
log.error({ err }, 'Failed to register pairing request');
|
|
45
|
+
return Response.json({ error: 'Internal server error' }, { status: 500 });
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* POST /v1/pairing/request -- Unauthenticated (secret-gated).
|
|
51
|
+
* iOS initiates a pairing handshake.
|
|
52
|
+
*/
|
|
53
|
+
export async function handlePairingRequest(req: Request, ctx: PairingHandlerContext): Promise<Response> {
|
|
54
|
+
try {
|
|
55
|
+
const body = await req.json() as Record<string, unknown>;
|
|
56
|
+
const pairingRequestId = typeof body.pairingRequestId === 'string' ? body.pairingRequestId : '';
|
|
57
|
+
const pairingSecret = typeof body.pairingSecret === 'string' ? body.pairingSecret : '';
|
|
58
|
+
const deviceId = typeof body.deviceId === 'string' ? body.deviceId.trim() : '';
|
|
59
|
+
const deviceName = typeof body.deviceName === 'string' ? body.deviceName.trim() : '';
|
|
60
|
+
|
|
61
|
+
// Redact secret from any potential logging of body
|
|
62
|
+
log.info({ pairingRequestId, deviceName, hasDeviceId: !!deviceId }, 'Pairing request received');
|
|
63
|
+
|
|
64
|
+
if (!deviceId || !deviceName) {
|
|
65
|
+
return Response.json({ error: 'Missing required fields: deviceId, deviceName' }, { status: 400 });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!pairingRequestId || !pairingSecret) {
|
|
69
|
+
return Response.json({ error: 'Missing required fields: pairingRequestId, pairingSecret' }, { status: 400 });
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const result = ctx.pairingStore.beginRequest({ pairingRequestId, pairingSecret, deviceId, deviceName });
|
|
73
|
+
if (!result.ok) {
|
|
74
|
+
const statusCode = result.reason === 'invalid_secret' ? 403 : result.reason === 'not_found' ? 403 : 410;
|
|
75
|
+
return Response.json({ error: 'Forbidden' }, { status: statusCode });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const entry = result.entry;
|
|
79
|
+
const hashedDeviceId = hashDeviceId(deviceId);
|
|
80
|
+
|
|
81
|
+
// Auto-approve if device is in the allowlist
|
|
82
|
+
if (isDeviceApproved(hashedDeviceId) && ctx.bearerToken) {
|
|
83
|
+
refreshDevice(hashedDeviceId, deviceName);
|
|
84
|
+
ctx.pairingStore.approve(pairingRequestId, ctx.bearerToken);
|
|
85
|
+
log.info({ pairingRequestId, hashedDeviceId }, 'Auto-approved allowlisted device');
|
|
86
|
+
return Response.json({
|
|
87
|
+
status: 'approved',
|
|
88
|
+
bearerToken: ctx.bearerToken,
|
|
89
|
+
gatewayUrl: entry.gatewayUrl,
|
|
90
|
+
localLanUrl: entry.localLanUrl,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Send IPC to macOS to show approval prompt
|
|
95
|
+
if (ctx.pairingBroadcast) {
|
|
96
|
+
ctx.pairingBroadcast({
|
|
97
|
+
type: 'pairing_approval_request',
|
|
98
|
+
pairingRequestId,
|
|
99
|
+
deviceId: hashedDeviceId,
|
|
100
|
+
deviceName,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return Response.json({ status: 'pending' });
|
|
105
|
+
} catch (err) {
|
|
106
|
+
log.error({ err }, 'Failed to process pairing request');
|
|
107
|
+
return Response.json({ error: 'Internal server error' }, { status: 500 });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* GET /v1/pairing/status?id=<id>&secret=<secret> -- Unauthenticated (secret-gated).
|
|
113
|
+
* iOS polls for approval status.
|
|
114
|
+
*/
|
|
115
|
+
export function handlePairingStatus(url: URL, ctx: PairingHandlerContext): Response {
|
|
116
|
+
const id = url.searchParams.get('id') ?? '';
|
|
117
|
+
// Note: secret is redacted from logs
|
|
118
|
+
const secret = url.searchParams.get('secret') ?? '';
|
|
119
|
+
|
|
120
|
+
if (!id || !secret) {
|
|
121
|
+
return Response.json({ error: 'Missing required params: id, secret' }, { status: 400 });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (!ctx.pairingStore.validateSecret(id, secret)) {
|
|
125
|
+
return Response.json({ error: 'Forbidden' }, { status: 403 });
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const entry = ctx.pairingStore.get(id);
|
|
129
|
+
if (!entry) {
|
|
130
|
+
return Response.json({ error: 'Not found' }, { status: 404 });
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (entry.status === 'approved') {
|
|
134
|
+
return Response.json({
|
|
135
|
+
status: 'approved',
|
|
136
|
+
bearerToken: entry.bearerToken,
|
|
137
|
+
gatewayUrl: entry.gatewayUrl,
|
|
138
|
+
localLanUrl: entry.localLanUrl,
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return Response.json({ status: entry.status });
|
|
143
|
+
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import { getOrCreateConversation } from '../../memory/conversation-key-store.js';
|
|
5
5
|
import * as attachmentsStore from '../../memory/attachments-store.js';
|
|
6
6
|
import * as runsStore from '../../memory/runs-store.js';
|
|
7
|
+
import { CHANNEL_IDS, parseChannelId } from '../../channels/types.js';
|
|
7
8
|
import { addRule } from '../../permissions/trust-store.js';
|
|
8
9
|
import { getTool } from '../../tools/registry.js';
|
|
9
10
|
import { getLogger } from '../../util/logger.js';
|
|
@@ -19,15 +20,27 @@ export async function handleCreateRun(
|
|
|
19
20
|
conversationKey?: string;
|
|
20
21
|
content?: string;
|
|
21
22
|
attachmentIds?: string[];
|
|
23
|
+
sourceChannel?: string;
|
|
22
24
|
};
|
|
23
25
|
|
|
24
26
|
const { conversationKey, content, attachmentIds } = body;
|
|
27
|
+
if (!body.sourceChannel || typeof body.sourceChannel !== 'string') {
|
|
28
|
+
return Response.json({ error: 'sourceChannel is required' }, { status: 400 });
|
|
29
|
+
}
|
|
30
|
+
const sourceChannel = parseChannelId(body.sourceChannel);
|
|
31
|
+
|
|
32
|
+
if (!sourceChannel) {
|
|
33
|
+
return Response.json(
|
|
34
|
+
{ error: `Invalid sourceChannel: ${body.sourceChannel}. Valid values: ${CHANNEL_IDS.join(', ')}` },
|
|
35
|
+
{ status: 400 },
|
|
36
|
+
);
|
|
37
|
+
}
|
|
25
38
|
|
|
26
39
|
if (!conversationKey) {
|
|
27
40
|
return Response.json({ error: 'conversationKey is required' }, { status: 400 });
|
|
28
41
|
}
|
|
29
42
|
|
|
30
|
-
if (content
|
|
43
|
+
if (content != null && typeof content !== 'string') {
|
|
31
44
|
return Response.json({ error: 'content must be a string' }, { status: 400 });
|
|
32
45
|
}
|
|
33
46
|
|
|
@@ -57,6 +70,7 @@ export async function handleCreateRun(
|
|
|
57
70
|
mapping.conversationId,
|
|
58
71
|
content ?? '',
|
|
59
72
|
hasAttachments ? attachmentIds : undefined,
|
|
73
|
+
{ sourceChannel },
|
|
60
74
|
);
|
|
61
75
|
return Response.json({
|
|
62
76
|
id: run.id,
|
|
@@ -13,11 +13,14 @@
|
|
|
13
13
|
* status and submit a decision or secret via the respective endpoints.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
+
import type { ChannelId, TurnChannelContext } from '../channels/types.js';
|
|
17
|
+
import { parseChannelId } from '../channels/types.js';
|
|
16
18
|
import * as runsStore from '../memory/runs-store.js';
|
|
17
19
|
import type { Run } from '../memory/runs-store.js';
|
|
18
20
|
import type { Session } from '../daemon/session.js';
|
|
19
21
|
import type { ServerMessage } from '../daemon/ipc-protocol.js';
|
|
20
22
|
import { resolveChannelCapabilities } from '../daemon/session-runtime-assembly.js';
|
|
23
|
+
import type { GuardianRuntimeContext } from '../daemon/session-runtime-assembly.js';
|
|
21
24
|
import type { UserDecision } from '../permissions/types.js';
|
|
22
25
|
import { checkIngressForSecrets } from '../security/secret-ingress.js';
|
|
23
26
|
import { IngressBlockedError } from '../util/errors.js';
|
|
@@ -37,7 +40,11 @@ interface PendingRunState {
|
|
|
37
40
|
}
|
|
38
41
|
|
|
39
42
|
export interface RunOrchestratorDeps {
|
|
40
|
-
getOrCreateSession: (conversationId: string
|
|
43
|
+
getOrCreateSession: (conversationId: string, transport?: {
|
|
44
|
+
channelId: ChannelId;
|
|
45
|
+
hints?: string[];
|
|
46
|
+
uxBrief?: string;
|
|
47
|
+
}) => Promise<Session>;
|
|
41
48
|
resolveAttachments: (attachmentIds: string[]) => Array<{
|
|
42
49
|
id: string;
|
|
43
50
|
filename: string;
|
|
@@ -64,9 +71,27 @@ export interface RunStartOptions {
|
|
|
64
71
|
/**
|
|
65
72
|
* The originating channel (e.g. 'telegram', 'slack'). When provided,
|
|
66
73
|
* channel capabilities are resolved for this channel instead of the
|
|
67
|
-
* default '
|
|
74
|
+
* default 'macos'.
|
|
68
75
|
*/
|
|
69
|
-
sourceChannel?:
|
|
76
|
+
sourceChannel?: ChannelId;
|
|
77
|
+
/**
|
|
78
|
+
* Transport hints from sourceMetadata (e.g. reply-context cues).
|
|
79
|
+
* Forwarded to the session so the agent loop can incorporate them.
|
|
80
|
+
*/
|
|
81
|
+
hints?: string[];
|
|
82
|
+
/**
|
|
83
|
+
* Brief UX context from sourceMetadata (e.g. UI surface description).
|
|
84
|
+
* Forwarded to the session so the agent loop can tailor its response.
|
|
85
|
+
*/
|
|
86
|
+
uxBrief?: string;
|
|
87
|
+
/** Assistant scope for multi-assistant channels. */
|
|
88
|
+
assistantId?: string;
|
|
89
|
+
/** Guardian trust/identity context for the inbound actor. */
|
|
90
|
+
guardianContext?: GuardianRuntimeContext;
|
|
91
|
+
/** Channel command intent metadata (e.g. Telegram /start). */
|
|
92
|
+
commandIntent?: { type: string; payload?: string; languageCode?: string };
|
|
93
|
+
/** Resolved channel context for this turn. */
|
|
94
|
+
turnChannelContext?: TurnChannelContext;
|
|
70
95
|
}
|
|
71
96
|
|
|
72
97
|
// ---------------------------------------------------------------------------
|
|
@@ -104,7 +129,17 @@ export class RunOrchestrator {
|
|
|
104
129
|
throw new IngressBlockedError(ingressCheck.userNotice!, ingressCheck.detectedTypes);
|
|
105
130
|
}
|
|
106
131
|
|
|
107
|
-
|
|
132
|
+
// Build transport metadata when channel context is available so the
|
|
133
|
+
// session receives the same hints/uxBrief as the non-orchestrator path.
|
|
134
|
+
const transport = options?.sourceChannel
|
|
135
|
+
? {
|
|
136
|
+
channelId: options.sourceChannel,
|
|
137
|
+
hints: options.hints,
|
|
138
|
+
uxBrief: options.uxBrief,
|
|
139
|
+
}
|
|
140
|
+
: undefined;
|
|
141
|
+
|
|
142
|
+
const session = await this.deps.getOrCreateSession(conversationId, transport);
|
|
108
143
|
|
|
109
144
|
if (session.isProcessing()) {
|
|
110
145
|
throw new Error('Session is already processing a message');
|
|
@@ -121,6 +156,13 @@ export class RunOrchestrator {
|
|
|
121
156
|
...session.memoryPolicy,
|
|
122
157
|
strictSideEffects,
|
|
123
158
|
};
|
|
159
|
+
session.setAssistantId(options?.assistantId ?? 'self');
|
|
160
|
+
session.setGuardianContext(options?.guardianContext ?? null);
|
|
161
|
+
session.setCommandIntent(options?.commandIntent ?? null);
|
|
162
|
+
session.setTurnChannelContext(options?.turnChannelContext ?? {
|
|
163
|
+
userMessageChannel: parseChannelId(options?.sourceChannel) ?? 'macos',
|
|
164
|
+
assistantMessageChannel: parseChannelId(options?.sourceChannel) ?? 'macos',
|
|
165
|
+
});
|
|
124
166
|
|
|
125
167
|
const attachments = attachmentIds
|
|
126
168
|
? this.deps.resolveAttachments(attachmentIds)
|
|
@@ -132,8 +174,8 @@ export class RunOrchestrator {
|
|
|
132
174
|
|
|
133
175
|
// Set channel capabilities based on the originating channel so capabilities
|
|
134
176
|
// (e.g. attachment scope) match the actual transport rather than always
|
|
135
|
-
// defaulting to '
|
|
136
|
-
session.setChannelCapabilities(resolveChannelCapabilities(options?.sourceChannel ?? '
|
|
177
|
+
// defaulting to 'macos'.
|
|
178
|
+
session.setChannelCapabilities(resolveChannelCapabilities(options?.sourceChannel ?? 'macos'));
|
|
137
179
|
|
|
138
180
|
// Serialized publish chain so hub subscribers observe events in order.
|
|
139
181
|
let hubChain: Promise<void> = Promise.resolve();
|
|
@@ -145,11 +187,14 @@ export class RunOrchestrator {
|
|
|
145
187
|
: undefined;
|
|
146
188
|
const resolvedSessionId = msgSessionId ?? conversationId;
|
|
147
189
|
const event = buildAssistantEvent('self', msg, resolvedSessionId);
|
|
148
|
-
hubChain =
|
|
149
|
-
|
|
150
|
-
|
|
190
|
+
hubChain = (async () => {
|
|
191
|
+
await hubChain;
|
|
192
|
+
try {
|
|
193
|
+
await assistantEventHub.publish(event);
|
|
194
|
+
} catch (err) {
|
|
151
195
|
log.warn({ err }, 'assistant-events hub subscriber threw during HTTP run');
|
|
152
|
-
}
|
|
196
|
+
}
|
|
197
|
+
})();
|
|
153
198
|
};
|
|
154
199
|
|
|
155
200
|
|
|
@@ -201,6 +246,9 @@ export class RunOrchestrator {
|
|
|
201
246
|
// Reset channel capabilities so a subsequent IPC/desktop session on the
|
|
202
247
|
// same conversation is not incorrectly treated as an HTTP-API client.
|
|
203
248
|
session.setChannelCapabilities(null);
|
|
249
|
+
session.setGuardianContext(null);
|
|
250
|
+
session.setCommandIntent(null);
|
|
251
|
+
session.setAssistantId('self');
|
|
204
252
|
// Reset the session's client callback to a no-op so the stale
|
|
205
253
|
// closure doesn't intercept events from future runs on the same session.
|
|
206
254
|
// Set hasNoClient=true here since the run is done and no HTTP caller
|
|
@@ -208,32 +256,35 @@ export class RunOrchestrator {
|
|
|
208
256
|
session.updateClient(() => {}, true);
|
|
209
257
|
};
|
|
210
258
|
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
259
|
+
void (async () => {
|
|
260
|
+
try {
|
|
261
|
+
await session.runAgentLoop(content, messageId, (msg: ServerMessage) => {
|
|
262
|
+
if (msg.type === 'error') {
|
|
263
|
+
lastError = msg.message;
|
|
264
|
+
} else if (msg.type === 'session_error') {
|
|
265
|
+
lastError = msg.userMessage;
|
|
266
|
+
}
|
|
267
|
+
// Mirror agent-loop events (assistant_text_delta, message_complete,
|
|
268
|
+
// tool_use_start, tool_result, etc.) to the hub. These travel through
|
|
269
|
+
// the onEvent path, distinct from the updateClient path used by the
|
|
270
|
+
// prompter (confirmation_request). Both paths must publish so SSE
|
|
271
|
+
// consumers receive the full response stream.
|
|
272
|
+
publishToHub(msg);
|
|
273
|
+
});
|
|
274
|
+
if (lastError) {
|
|
275
|
+
log.error({ runId: run.id, error: lastError }, 'Run failed (error event from agent loop)');
|
|
276
|
+
runsStore.failRun(run.id, lastError);
|
|
277
|
+
} else {
|
|
278
|
+
runsStore.completeRun(run.id);
|
|
279
|
+
}
|
|
280
|
+
} catch (err) {
|
|
281
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
282
|
+
log.error({ err, runId: run.id }, 'Run failed');
|
|
283
|
+
runsStore.failRun(run.id, message);
|
|
284
|
+
} finally {
|
|
285
|
+
cleanup();
|
|
229
286
|
}
|
|
230
|
-
|
|
231
|
-
}).catch((err) => {
|
|
232
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
233
|
-
log.error({ err, runId: run.id }, 'Run failed');
|
|
234
|
-
runsStore.failRun(run.id, message);
|
|
235
|
-
cleanup();
|
|
236
|
-
});
|
|
287
|
+
})();
|
|
237
288
|
|
|
238
289
|
return run;
|
|
239
290
|
}
|