@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
|
@@ -61,7 +61,7 @@ const SENSITIVE_KEYS = new Set([
|
|
|
61
61
|
function redactDeep(value: unknown): unknown {
|
|
62
62
|
if (typeof value === 'string') return redact(value);
|
|
63
63
|
if (Array.isArray(value)) return value.map(redactDeep);
|
|
64
|
-
if (value
|
|
64
|
+
if (value != null && typeof value === 'object') {
|
|
65
65
|
const out: Record<string, unknown> = {};
|
|
66
66
|
for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
|
|
67
67
|
if (SENSITIVE_KEYS.has(k.toLowerCase())) {
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import * as net from 'node:net';
|
|
2
|
+
import { getConfiguredProvider } from '../../providers/provider-send-message.js';
|
|
3
|
+
import type { DictationRequest } from '../ipc-protocol.js';
|
|
4
|
+
import { log, defineHandlers, type HandlerContext } from './shared.js';
|
|
5
|
+
|
|
6
|
+
// Action verbs that signal the user wants a full agent session rather than inline text
|
|
7
|
+
const ACTION_VERBS = ['slack', 'email', 'send', 'create', 'open', 'search', 'find'];
|
|
8
|
+
|
|
9
|
+
const MAX_WINDOW_TITLE_LENGTH = 100;
|
|
10
|
+
|
|
11
|
+
/** Sanitize window title to mitigate prompt injection from attacker-controlled titles (e.g. browser tabs, Slack conversations). */
|
|
12
|
+
function sanitizeWindowTitle(title: string | undefined): string {
|
|
13
|
+
if (!title) return '';
|
|
14
|
+
return title
|
|
15
|
+
.replace(/[<>]/g, '') // strip angle brackets to prevent tag injection
|
|
16
|
+
.slice(0, MAX_WINDOW_TITLE_LENGTH);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Build a delimited app metadata block so the LLM treats it as contextual data, not instructions. */
|
|
20
|
+
function buildAppMetadataBlock(msg: DictationRequest): string {
|
|
21
|
+
const windowTitle = sanitizeWindowTitle(msg.context.windowTitle);
|
|
22
|
+
return [
|
|
23
|
+
'<app_metadata>',
|
|
24
|
+
`App: ${msg.context.appName} (${msg.context.bundleIdentifier})`,
|
|
25
|
+
`Window: ${windowTitle}`,
|
|
26
|
+
'</app_metadata>',
|
|
27
|
+
].join('\n');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
type DictationMode = 'dictation' | 'command' | 'action';
|
|
31
|
+
|
|
32
|
+
export function detectDictationMode(msg: DictationRequest): DictationMode {
|
|
33
|
+
// Command mode: selected text present — treat transcription as a transformation instruction
|
|
34
|
+
if (msg.context.selectedText && msg.context.selectedText.trim().length > 0) {
|
|
35
|
+
return 'command';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Action mode: transcription starts with an action verb
|
|
39
|
+
const firstWord = msg.transcription.trim().split(/\s+/)[0]?.toLowerCase() ?? '';
|
|
40
|
+
if (ACTION_VERBS.includes(firstWord)) {
|
|
41
|
+
return 'action';
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Dictation mode: cursor is in a text field with no selection — clean up for typing
|
|
45
|
+
if (msg.context.cursorInTextField) {
|
|
46
|
+
return 'dictation';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// AX focus-role detection in browser editors (for example Gmail compose)
|
|
50
|
+
// is occasionally incomplete. If we default to action here, normal dictation
|
|
51
|
+
// gets misrouted into a new chat task. Treat ambiguous context as dictation.
|
|
52
|
+
return 'dictation';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function buildDictationPrompt(msg: DictationRequest): string {
|
|
56
|
+
return [
|
|
57
|
+
'You are a dictation assistant. Clean up the following speech transcription for direct insertion into a text field.',
|
|
58
|
+
'',
|
|
59
|
+
'## Rules',
|
|
60
|
+
'- Fix grammar, punctuation, and capitalization',
|
|
61
|
+
'- Remove filler words (um, uh, like, you know)',
|
|
62
|
+
"- Maintain the speaker's intent and meaning",
|
|
63
|
+
'- Do NOT add explanations or commentary',
|
|
64
|
+
'- Return ONLY the cleaned text, nothing else',
|
|
65
|
+
'',
|
|
66
|
+
'## Tone Adaptation',
|
|
67
|
+
'Adapt your output tone based on the active application:',
|
|
68
|
+
'- Email apps (Gmail, Mail): Professional but warm. Use proper greetings and sign-offs if appropriate.',
|
|
69
|
+
'- Slack: Casual and conversational. Match typical chat style.',
|
|
70
|
+
'- Code editors (VS Code, Xcode): Technical and concise. Code comments style.',
|
|
71
|
+
'- Terminal: Command-like, terse.',
|
|
72
|
+
'- Messages/iMessage: Very casual, texting style. Short sentences.',
|
|
73
|
+
'- Notes/Docs: Neutral, clear writing.',
|
|
74
|
+
'- Default: Match the user\'s natural voice.',
|
|
75
|
+
'',
|
|
76
|
+
'## Context Clues',
|
|
77
|
+
'- Window title may contain recipient name (Slack DMs, email compose)',
|
|
78
|
+
'- If you can identify a recipient, adapt formality to the apparent relationship',
|
|
79
|
+
'- Maintain the user\'s natural voice — don\'t over-formalize casual speech',
|
|
80
|
+
'- The user\'s writing patterns and preferences may be available from memory context — follow those when present',
|
|
81
|
+
'',
|
|
82
|
+
buildAppMetadataBlock(msg),
|
|
83
|
+
].join('\n');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function buildCommandPrompt(msg: DictationRequest): string {
|
|
87
|
+
return [
|
|
88
|
+
'You are a text transformation assistant. The user has selected text and given a voice command to transform it.',
|
|
89
|
+
'',
|
|
90
|
+
'## Rules',
|
|
91
|
+
'- Apply the instruction to the selected text',
|
|
92
|
+
'- Return ONLY the transformed text, nothing else',
|
|
93
|
+
'- Do NOT add explanations or commentary',
|
|
94
|
+
'',
|
|
95
|
+
'## Tone Adaptation',
|
|
96
|
+
'Match the tone to the active application context:',
|
|
97
|
+
'- Email apps (Gmail, Mail): Professional but warm.',
|
|
98
|
+
'- Slack: Casual and conversational.',
|
|
99
|
+
'- Code editors (VS Code, Xcode): Technical and concise.',
|
|
100
|
+
'- Terminal: Command-like, terse.',
|
|
101
|
+
'- Messages/iMessage: Very casual, texting style.',
|
|
102
|
+
'- Notes/Docs: Neutral, clear writing.',
|
|
103
|
+
'- Default: Match the user\'s natural voice.',
|
|
104
|
+
'',
|
|
105
|
+
'## Context Clues',
|
|
106
|
+
'- Window title may contain recipient name (Slack DMs, email compose)',
|
|
107
|
+
'- If you can identify a recipient, adapt formality to the apparent relationship',
|
|
108
|
+
'- Maintain the user\'s natural voice — don\'t over-formalize casual speech',
|
|
109
|
+
'- The user\'s writing patterns and preferences may be available from memory context — follow those when present',
|
|
110
|
+
'',
|
|
111
|
+
buildAppMetadataBlock(msg),
|
|
112
|
+
'',
|
|
113
|
+
'Selected text:',
|
|
114
|
+
msg.context.selectedText ?? '',
|
|
115
|
+
'',
|
|
116
|
+
`Instruction: ${msg.transcription}`,
|
|
117
|
+
].join('\n');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export async function handleDictationRequest(
|
|
121
|
+
msg: DictationRequest,
|
|
122
|
+
socket: net.Socket,
|
|
123
|
+
ctx: HandlerContext,
|
|
124
|
+
): Promise<void> {
|
|
125
|
+
const mode = detectDictationMode(msg);
|
|
126
|
+
log.info({ mode, transcriptionLength: msg.transcription.length }, 'Dictation request received');
|
|
127
|
+
|
|
128
|
+
// Action mode: return immediately — the client will route to a full agent session
|
|
129
|
+
if (mode === 'action') {
|
|
130
|
+
ctx.send(socket, {
|
|
131
|
+
type: 'dictation_response',
|
|
132
|
+
text: msg.transcription,
|
|
133
|
+
mode: 'action',
|
|
134
|
+
actionPlan: `User wants to: ${msg.transcription}`,
|
|
135
|
+
});
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Dictation / command mode: make a single-turn LLM call for text cleanup or transformation
|
|
140
|
+
const systemPrompt = mode === 'dictation'
|
|
141
|
+
? buildDictationPrompt(msg)
|
|
142
|
+
: buildCommandPrompt(msg);
|
|
143
|
+
|
|
144
|
+
const userText = mode === 'dictation'
|
|
145
|
+
? msg.transcription
|
|
146
|
+
: msg.transcription; // command prompt already embeds the selected text and instruction
|
|
147
|
+
|
|
148
|
+
try {
|
|
149
|
+
const provider = getConfiguredProvider();
|
|
150
|
+
if (!provider) {
|
|
151
|
+
log.warn('Dictation: no provider available, returning raw transcription');
|
|
152
|
+
const fallbackText = mode === 'command' ? (msg.context.selectedText ?? msg.transcription) : msg.transcription;
|
|
153
|
+
ctx.send(socket, { type: 'dictation_response', text: fallbackText, mode });
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const response = await provider.sendMessage(
|
|
158
|
+
[{ role: 'user', content: [{ type: 'text', text: userText }] }],
|
|
159
|
+
[], // no tools
|
|
160
|
+
systemPrompt,
|
|
161
|
+
{ config: { max_tokens: 1024 } },
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const textBlock = response.content.find((b) => b.type === 'text');
|
|
165
|
+
const inlineFallback = mode === 'command' ? (msg.context.selectedText ?? msg.transcription) : msg.transcription;
|
|
166
|
+
const cleanedText = textBlock && 'text' in textBlock ? textBlock.text.trim() : inlineFallback;
|
|
167
|
+
|
|
168
|
+
ctx.send(socket, { type: 'dictation_response', text: cleanedText, mode });
|
|
169
|
+
} catch (err) {
|
|
170
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
171
|
+
log.error({ err }, 'Dictation LLM call failed, returning raw transcription');
|
|
172
|
+
const fallbackText = mode === 'command' ? (msg.context.selectedText ?? msg.transcription) : msg.transcription;
|
|
173
|
+
ctx.send(socket, { type: 'dictation_response', text: fallbackText, mode });
|
|
174
|
+
ctx.send(socket, { type: 'error', message: `Dictation cleanup failed: ${message}` });
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export const dictationHandlers = defineHandlers({
|
|
179
|
+
dictation_request: handleDictationRequest,
|
|
180
|
+
});
|
|
@@ -2,12 +2,23 @@ import { defineHandlers } from './shared.js';
|
|
|
2
2
|
import type { HandlerContext } from './shared.js';
|
|
3
3
|
import type { DocumentSaveRequest, DocumentLoadRequest, DocumentListRequest } from '../ipc-protocol.js';
|
|
4
4
|
import type * as net from 'node:net';
|
|
5
|
-
import
|
|
6
|
-
import { getDb } from '../../memory/db.js';
|
|
5
|
+
import { rawRun, rawGet, rawAll } from '../../memory/db.js';
|
|
7
6
|
import { getLogger } from '../../util/logger.js';
|
|
8
7
|
|
|
9
8
|
const log = getLogger('documents');
|
|
10
9
|
|
|
10
|
+
interface DocumentRow {
|
|
11
|
+
surface_id: string;
|
|
12
|
+
conversation_id: string;
|
|
13
|
+
title: string;
|
|
14
|
+
content: string;
|
|
15
|
+
word_count: number;
|
|
16
|
+
created_at: number;
|
|
17
|
+
updated_at: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type DocumentListRow = Omit<DocumentRow, 'content'>;
|
|
21
|
+
|
|
11
22
|
export function handleDocumentSave(msg: DocumentSaveRequest, socket: net.Socket, ctx: HandlerContext): void {
|
|
12
23
|
log.info({
|
|
13
24
|
surfaceId: msg.surfaceId,
|
|
@@ -18,13 +29,9 @@ export function handleDocumentSave(msg: DocumentSaveRequest, socket: net.Socket,
|
|
|
18
29
|
}, 'Received save request');
|
|
19
30
|
|
|
20
31
|
try {
|
|
21
|
-
const db = getDb();
|
|
22
|
-
// Get the raw SQLite client from Drizzle
|
|
23
|
-
const sqlite = (db as unknown as { $client: Database }).$client;
|
|
24
32
|
const now = Date.now();
|
|
25
33
|
|
|
26
|
-
|
|
27
|
-
sqlite.run(
|
|
34
|
+
rawRun(
|
|
28
35
|
`INSERT INTO documents (surface_id, conversation_id, title, content, word_count, created_at, updated_at)
|
|
29
36
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
30
37
|
ON CONFLICT(surface_id) DO UPDATE SET
|
|
@@ -32,7 +39,7 @@ export function handleDocumentSave(msg: DocumentSaveRequest, socket: net.Socket,
|
|
|
32
39
|
content = excluded.content,
|
|
33
40
|
word_count = excluded.word_count,
|
|
34
41
|
updated_at = excluded.updated_at`,
|
|
35
|
-
|
|
42
|
+
msg.surfaceId, msg.conversationId, msg.title, msg.content, msg.wordCount, now, now,
|
|
36
43
|
);
|
|
37
44
|
|
|
38
45
|
ctx.send(socket, {
|
|
@@ -55,22 +62,11 @@ export function handleDocumentSave(msg: DocumentSaveRequest, socket: net.Socket,
|
|
|
55
62
|
|
|
56
63
|
export function handleDocumentLoad(msg: DocumentLoadRequest, socket: net.Socket, ctx: HandlerContext): void {
|
|
57
64
|
try {
|
|
58
|
-
const
|
|
59
|
-
const sqlite = (db as unknown as { $client: Database }).$client;
|
|
60
|
-
|
|
61
|
-
const result = sqlite.prepare(/*sql*/ `
|
|
65
|
+
const result = rawGet<DocumentRow>(/*sql*/ `
|
|
62
66
|
SELECT surface_id, conversation_id, title, content, word_count, created_at, updated_at
|
|
63
67
|
FROM documents
|
|
64
68
|
WHERE surface_id = ?
|
|
65
|
-
|
|
66
|
-
surface_id: string;
|
|
67
|
-
conversation_id: string;
|
|
68
|
-
title: string;
|
|
69
|
-
content: string;
|
|
70
|
-
word_count: number;
|
|
71
|
-
created_at: number;
|
|
72
|
-
updated_at: number;
|
|
73
|
-
} | undefined;
|
|
69
|
+
`, msg.surfaceId);
|
|
74
70
|
|
|
75
71
|
if (result) {
|
|
76
72
|
ctx.send(socket, {
|
|
@@ -119,9 +115,6 @@ export function handleDocumentLoad(msg: DocumentLoadRequest, socket: net.Socket,
|
|
|
119
115
|
|
|
120
116
|
export function handleDocumentList(msg: DocumentListRequest, socket: net.Socket, ctx: HandlerContext): void {
|
|
121
117
|
try {
|
|
122
|
-
const db = getDb();
|
|
123
|
-
const sqlite = (db as unknown as { $client: Database }).$client;
|
|
124
|
-
|
|
125
118
|
let query = /*sql*/ `
|
|
126
119
|
SELECT surface_id, conversation_id, title, word_count, created_at, updated_at
|
|
127
120
|
FROM documents
|
|
@@ -135,14 +128,7 @@ export function handleDocumentList(msg: DocumentListRequest, socket: net.Socket,
|
|
|
135
128
|
|
|
136
129
|
query += ' ORDER BY updated_at DESC';
|
|
137
130
|
|
|
138
|
-
const results =
|
|
139
|
-
surface_id: string;
|
|
140
|
-
conversation_id: string;
|
|
141
|
-
title: string;
|
|
142
|
-
word_count: number;
|
|
143
|
-
created_at: number;
|
|
144
|
-
updated_at: number;
|
|
145
|
-
}>;
|
|
131
|
+
const results = rawAll<DocumentListRow>(query, ...params);
|
|
146
132
|
|
|
147
133
|
ctx.send(socket, {
|
|
148
134
|
type: 'document_list_response',
|
|
@@ -2,7 +2,7 @@ import * as net from 'node:net';
|
|
|
2
2
|
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
3
3
|
import { join, dirname } from 'node:path';
|
|
4
4
|
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import { getWorkspacePromptPath } from '../../util/platform.js';
|
|
5
|
+
import { getWorkspacePromptPath, readLockfile } from '../../util/platform.js';
|
|
6
6
|
import { log, defineHandlers, type HandlerContext } from './shared.js';
|
|
7
7
|
|
|
8
8
|
function handleIdentityGet(socket: net.Socket, ctx: HandlerContext): void {
|
|
@@ -68,28 +68,19 @@ function handleIdentityGet(socket: net.Socket, ctx: HandlerContext): void {
|
|
|
68
68
|
let cloud: string | undefined;
|
|
69
69
|
let originSystem: string | undefined;
|
|
70
70
|
try {
|
|
71
|
-
const
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const dateB = new Date(b.hatchedAt as string || 0).getTime();
|
|
85
|
-
return dateB - dateA;
|
|
86
|
-
});
|
|
87
|
-
const latest = sorted[0];
|
|
88
|
-
assistantId = latest.assistantId as string | undefined;
|
|
89
|
-
cloud = latest.cloud as string | undefined;
|
|
90
|
-
originSystem = cloud === 'local' ? 'local' : cloud;
|
|
91
|
-
}
|
|
92
|
-
break;
|
|
71
|
+
const lockData = readLockfile();
|
|
72
|
+
const assistants = lockData?.assistants as Array<Record<string, unknown>> | undefined;
|
|
73
|
+
if (assistants && assistants.length > 0) {
|
|
74
|
+
// Use the most recently hatched assistant
|
|
75
|
+
const sorted = [...assistants].sort((a, b) => {
|
|
76
|
+
const dateA = new Date(a.hatchedAt as string || 0).getTime();
|
|
77
|
+
const dateB = new Date(b.hatchedAt as string || 0).getTime();
|
|
78
|
+
return dateB - dateA;
|
|
79
|
+
});
|
|
80
|
+
const latest = sorted[0];
|
|
81
|
+
assistantId = latest.assistantId as string | undefined;
|
|
82
|
+
cloud = latest.cloud as string | undefined;
|
|
83
|
+
originSystem = cloud === 'local' ? 'local' : cloud;
|
|
93
84
|
}
|
|
94
85
|
} catch {
|
|
95
86
|
// ignore — lockfile may not exist
|
|
@@ -22,6 +22,9 @@ import { signingHandlers } from './signing.js';
|
|
|
22
22
|
import { twitterAuthHandlers } from './twitter-auth.js';
|
|
23
23
|
import { workspaceFileHandlers } from './workspace-files.js';
|
|
24
24
|
import { identityHandlers } from './identity.js';
|
|
25
|
+
import { dictationHandlers } from './dictation.js';
|
|
26
|
+
import { inboxInviteHandlers } from './config-inbox.js';
|
|
27
|
+
import { pairingHandlers } from './pairing.js';
|
|
25
28
|
|
|
26
29
|
// Re-export types and utilities for backwards compatibility
|
|
27
30
|
export type {
|
|
@@ -87,6 +90,11 @@ const inlineHandlers = defineHandlers({
|
|
|
87
90
|
},
|
|
88
91
|
integration_disconnect: () => { /* no-op — integration registry removed */ },
|
|
89
92
|
|
|
93
|
+
// Stub handler: assistant_inbox — real implementation will be added in a follow-up PR.
|
|
94
|
+
assistant_inbox: (_msg, socket, ctx) => {
|
|
95
|
+
ctx.send(socket, { type: 'assistant_inbox_response', success: false, error: 'Not yet implemented' });
|
|
96
|
+
},
|
|
97
|
+
|
|
90
98
|
});
|
|
91
99
|
|
|
92
100
|
const handlers = {
|
|
@@ -107,6 +115,9 @@ const handlers = {
|
|
|
107
115
|
...twitterAuthHandlers,
|
|
108
116
|
...workspaceFileHandlers,
|
|
109
117
|
...identityHandlers,
|
|
118
|
+
...dictationHandlers,
|
|
119
|
+
...inboxInviteHandlers,
|
|
120
|
+
...pairingHandlers,
|
|
110
121
|
...inlineHandlers,
|
|
111
122
|
} satisfies DispatchMap;
|
|
112
123
|
|
|
@@ -3,8 +3,7 @@ import { v4 as uuid } from 'uuid';
|
|
|
3
3
|
import { readFileSync } from 'node:fs';
|
|
4
4
|
import { createHash } from 'node:crypto';
|
|
5
5
|
import * as conversationStore from '../../memory/conversation-store.js';
|
|
6
|
-
import {
|
|
7
|
-
import { getFailoverProvider, listProviders } from '../../providers/registry.js';
|
|
6
|
+
import { getConfiguredProvider } from '../../providers/provider-send-message.js';
|
|
8
7
|
import type { Provider } from '../../providers/types.js';
|
|
9
8
|
import { classifyInteraction } from '../classifier.js';
|
|
10
9
|
import { checkIngressForSecrets } from '../../security/secret-ingress.js';
|
|
@@ -172,10 +171,9 @@ export async function handleSuggestionRequest(
|
|
|
172
171
|
}
|
|
173
172
|
|
|
174
173
|
// Try LLM suggestion using the configured provider
|
|
175
|
-
const
|
|
176
|
-
if (
|
|
174
|
+
const provider = getConfiguredProvider();
|
|
175
|
+
if (provider) {
|
|
177
176
|
try {
|
|
178
|
-
const provider = getFailoverProvider(config.provider, config.providerOrder);
|
|
179
177
|
let promise = suggestionInFlight.get(m.id);
|
|
180
178
|
if (!promise) {
|
|
181
179
|
promise = generateSuggestion(provider, text);
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import * as net from 'node:net';
|
|
2
|
+
import type {
|
|
3
|
+
PairingApprovalResponse,
|
|
4
|
+
ApprovedDeviceRemove,
|
|
5
|
+
} from '../ipc-protocol.js';
|
|
6
|
+
import { log, defineHandlers, type HandlerContext } from './shared.js';
|
|
7
|
+
import {
|
|
8
|
+
approveDevice,
|
|
9
|
+
removeDevice,
|
|
10
|
+
clearAllDevices,
|
|
11
|
+
listDevices,
|
|
12
|
+
} from '../approved-devices-store.js';
|
|
13
|
+
import type { PairingStore } from '../pairing-store.js';
|
|
14
|
+
|
|
15
|
+
/** Module-level reference set by the daemon server at startup. */
|
|
16
|
+
let pairingStoreRef: PairingStore | null = null;
|
|
17
|
+
let bearerTokenRef: string | undefined;
|
|
18
|
+
|
|
19
|
+
export function initPairingHandlers(store: PairingStore, bearerToken: string | undefined): void {
|
|
20
|
+
pairingStoreRef = store;
|
|
21
|
+
bearerTokenRef = bearerToken;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function handlePairingApprovalResponse(
|
|
25
|
+
msg: PairingApprovalResponse,
|
|
26
|
+
_socket: net.Socket,
|
|
27
|
+
_ctx: HandlerContext,
|
|
28
|
+
): void {
|
|
29
|
+
if (!pairingStoreRef) {
|
|
30
|
+
log.warn('Pairing store not initialized');
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const entry = pairingStoreRef.get(msg.pairingRequestId);
|
|
35
|
+
if (!entry) {
|
|
36
|
+
log.warn({ pairingRequestId: msg.pairingRequestId }, 'Pairing request not found for approval response');
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Idempotent: if already approved/denied, just re-broadcast the current status
|
|
41
|
+
if (entry.status === 'approved' || entry.status === 'denied') {
|
|
42
|
+
log.info({ pairingRequestId: msg.pairingRequestId, status: entry.status }, 'Duplicate approval response, no-op');
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (msg.decision === 'deny') {
|
|
47
|
+
pairingStoreRef.deny(msg.pairingRequestId);
|
|
48
|
+
log.info({ pairingRequestId: msg.pairingRequestId }, 'Pairing request denied');
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// approve_once or always_allow
|
|
53
|
+
if (!bearerTokenRef) {
|
|
54
|
+
log.error('Cannot approve pairing: no bearer token configured');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
pairingStoreRef.approve(msg.pairingRequestId, bearerTokenRef);
|
|
59
|
+
log.info({ pairingRequestId: msg.pairingRequestId, decision: msg.decision }, 'Pairing request approved');
|
|
60
|
+
|
|
61
|
+
// If always_allow, persist the device to the allowlist
|
|
62
|
+
if (msg.decision === 'always_allow' && entry.hashedDeviceId) {
|
|
63
|
+
approveDevice(entry.hashedDeviceId, entry.deviceName ?? 'Unknown Device');
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function handleApprovedDevicesList(socket: net.Socket, ctx: HandlerContext): void {
|
|
68
|
+
const devices = listDevices();
|
|
69
|
+
ctx.send(socket, {
|
|
70
|
+
type: 'approved_devices_list_response',
|
|
71
|
+
devices,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function handleApprovedDeviceRemove(
|
|
76
|
+
msg: ApprovedDeviceRemove,
|
|
77
|
+
socket: net.Socket,
|
|
78
|
+
ctx: HandlerContext,
|
|
79
|
+
): void {
|
|
80
|
+
const success = removeDevice(msg.hashedDeviceId);
|
|
81
|
+
ctx.send(socket, {
|
|
82
|
+
type: 'approved_device_remove_response',
|
|
83
|
+
success,
|
|
84
|
+
});
|
|
85
|
+
log.info({ hashedDeviceId: msg.hashedDeviceId, success }, 'Device removal requested via IPC');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function handleApprovedDevicesClear(_socket: net.Socket, _ctx: HandlerContext): void {
|
|
89
|
+
clearAllDevices();
|
|
90
|
+
log.info('All approved devices cleared via IPC');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export const pairingHandlers = defineHandlers({
|
|
94
|
+
pairing_approval_response: handlePairingApprovalResponse,
|
|
95
|
+
approved_devices_list: (_msg, socket, ctx) => handleApprovedDevicesList(socket, ctx),
|
|
96
|
+
approved_device_remove: handleApprovedDeviceRemove,
|
|
97
|
+
approved_devices_clear: (_msg, socket, ctx) => handleApprovedDevicesClear(socket, ctx),
|
|
98
|
+
});
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import * as net from 'node:net';
|
|
2
|
+
import { isChannelId, parseChannelId } from '../../channels/types.js';
|
|
3
|
+
import { silentlyWithLog } from '../../util/silently.js';
|
|
2
4
|
import { v4 as uuid } from 'uuid';
|
|
3
5
|
import * as conversationStore from '../../memory/conversation-store.js';
|
|
4
6
|
import * as externalConversationStore from '../../memory/external-conversation-store.js';
|
|
@@ -22,6 +24,7 @@ import type {
|
|
|
22
24
|
UsageRequest,
|
|
23
25
|
SandboxSetRequest,
|
|
24
26
|
ServerMessage,
|
|
27
|
+
ConversationSearchRequest,
|
|
25
28
|
} from '../ipc-protocol.js';
|
|
26
29
|
import { getConfig } from '../../config/loader.js';
|
|
27
30
|
import { getSubagentManager } from '../../subagent/index.js';
|
|
@@ -80,7 +83,20 @@ export async function handleUserMessage(
|
|
|
80
83
|
attributes: { source: 'user_message' },
|
|
81
84
|
});
|
|
82
85
|
|
|
83
|
-
const
|
|
86
|
+
const ipcChannel = parseChannelId(msg.channel) ?? 'macos';
|
|
87
|
+
const queuedChannelMetadata = {
|
|
88
|
+
userMessageChannel: ipcChannel,
|
|
89
|
+
assistantMessageChannel: ipcChannel,
|
|
90
|
+
};
|
|
91
|
+
const result = session.enqueueMessage(
|
|
92
|
+
msg.content ?? '',
|
|
93
|
+
msg.attachments ?? [],
|
|
94
|
+
sendEvent,
|
|
95
|
+
requestId,
|
|
96
|
+
msg.activeSurfaceId,
|
|
97
|
+
msg.currentPage,
|
|
98
|
+
queuedChannelMetadata,
|
|
99
|
+
);
|
|
84
100
|
if (result.rejected) {
|
|
85
101
|
rlog.warn('Message rejected — queue is full');
|
|
86
102
|
session.traceEmitter.emit('request_error', 'Message rejected — queue is full', {
|
|
@@ -114,6 +130,13 @@ export async function handleUserMessage(
|
|
|
114
130
|
}
|
|
115
131
|
|
|
116
132
|
rlog.info('Processing user message');
|
|
133
|
+
session.setTurnChannelContext({
|
|
134
|
+
userMessageChannel: ipcChannel,
|
|
135
|
+
assistantMessageChannel: ipcChannel,
|
|
136
|
+
});
|
|
137
|
+
session.setAssistantId('self');
|
|
138
|
+
session.setGuardianContext(null);
|
|
139
|
+
session.setCommandIntent(null);
|
|
117
140
|
// Fire-and-forget: don't block the IPC handler so the connection can
|
|
118
141
|
// continue receiving messages (e.g. cancel, confirmations, or
|
|
119
142
|
// additional user_message that will be queued by the session).
|
|
@@ -208,12 +231,14 @@ export function handleSessionList(socket: net.Socket, ctx: HandlerContext, offse
|
|
|
208
231
|
type: 'session_list_response',
|
|
209
232
|
sessions: conversations.map((c) => {
|
|
210
233
|
const binding = bindings.get(c.id);
|
|
234
|
+
const originChannel = parseChannelId(c.originChannel);
|
|
211
235
|
return {
|
|
212
236
|
id: c.id,
|
|
213
237
|
title: c.title ?? 'Untitled',
|
|
214
238
|
updatedAt: c.updatedAt,
|
|
215
239
|
threadType: normalizeThreadType(c.threadType),
|
|
216
|
-
|
|
240
|
+
source: c.source ?? 'user',
|
|
241
|
+
...(binding && isChannelId(binding.sourceChannel) ? {
|
|
217
242
|
channelBinding: {
|
|
218
243
|
sourceChannel: binding.sourceChannel,
|
|
219
244
|
externalChatId: binding.externalChatId,
|
|
@@ -222,6 +247,7 @@ export function handleSessionList(socket: net.Socket, ctx: HandlerContext, offse
|
|
|
222
247
|
username: binding.username,
|
|
223
248
|
},
|
|
224
249
|
} : {}),
|
|
250
|
+
...(originChannel ? { conversationOriginChannel: originChannel } : {}),
|
|
225
251
|
};
|
|
226
252
|
}),
|
|
227
253
|
hasMore: offset + conversations.length < totalCount,
|
|
@@ -274,6 +300,11 @@ export async function handleSessionCreate(
|
|
|
274
300
|
ctx.socketToSession.set(socket, conversation.id);
|
|
275
301
|
const sendEvent = (event: ServerMessage) => ctx.send(socket, event);
|
|
276
302
|
const requestId = uuid();
|
|
303
|
+
const transportChannel = parseChannelId(msg.transport?.channelId) ?? 'macos';
|
|
304
|
+
session.setTurnChannelContext({
|
|
305
|
+
userMessageChannel: transportChannel,
|
|
306
|
+
assistantMessageChannel: transportChannel,
|
|
307
|
+
});
|
|
277
308
|
session.processMessage(msg.initialMessage, [], sendEvent, requestId).catch((err) => {
|
|
278
309
|
const message = err instanceof Error ? err.message : String(err);
|
|
279
310
|
log.error({ err, sessionId: conversation.id }, 'Error processing initial message');
|
|
@@ -401,9 +432,12 @@ export function handleHistoryRequest(
|
|
|
401
432
|
if (a.mimeType.startsWith('video/') && !a.thumbnailBase64) {
|
|
402
433
|
const attachmentId = a.id;
|
|
403
434
|
const base64 = a.dataBase64;
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
435
|
+
silentlyWithLog(
|
|
436
|
+
generateVideoThumbnail(base64).then((thumb) => {
|
|
437
|
+
if (thumb) setAttachmentThumbnail(attachmentId, thumb);
|
|
438
|
+
}),
|
|
439
|
+
'video thumbnail generation',
|
|
440
|
+
);
|
|
407
441
|
}
|
|
408
442
|
|
|
409
443
|
return {
|
|
@@ -539,6 +573,22 @@ export function handleDeleteQueuedMessage(
|
|
|
539
573
|
}
|
|
540
574
|
}
|
|
541
575
|
|
|
576
|
+
export function handleConversationSearch(
|
|
577
|
+
msg: ConversationSearchRequest,
|
|
578
|
+
socket: net.Socket,
|
|
579
|
+
ctx: HandlerContext,
|
|
580
|
+
): void {
|
|
581
|
+
const results = conversationStore.searchConversations(msg.query, {
|
|
582
|
+
limit: msg.limit,
|
|
583
|
+
maxMessagesPerConversation: msg.maxMessagesPerConversation,
|
|
584
|
+
});
|
|
585
|
+
ctx.send(socket, {
|
|
586
|
+
type: 'conversation_search_response',
|
|
587
|
+
query: msg.query,
|
|
588
|
+
results,
|
|
589
|
+
});
|
|
590
|
+
}
|
|
591
|
+
|
|
542
592
|
export const sessionHandlers = defineHandlers({
|
|
543
593
|
user_message: handleUserMessage,
|
|
544
594
|
confirmation_response: handleConfirmationResponse,
|
|
@@ -554,4 +604,5 @@ export const sessionHandlers = defineHandlers({
|
|
|
554
604
|
regenerate: handleRegenerate,
|
|
555
605
|
usage_request: handleUsageRequest,
|
|
556
606
|
sandbox_set: handleSandboxSet,
|
|
607
|
+
conversation_search: handleConversationSearch,
|
|
557
608
|
});
|
|
@@ -9,6 +9,7 @@ import type { ClientMessage, CuSessionCreate, ServerMessage, SessionTransportMet
|
|
|
9
9
|
import type { SecretPromptResult } from '../../permissions/secret-prompter.js';
|
|
10
10
|
import { getConfig } from '../../config/loader.js';
|
|
11
11
|
import type { DebouncerMap } from '../../util/debounce.js';
|
|
12
|
+
import type { GuardianRuntimeContext } from '../session-runtime-assembly.js';
|
|
12
13
|
|
|
13
14
|
const log = getLogger('handlers');
|
|
14
15
|
|
|
@@ -99,9 +100,13 @@ export interface SessionCreateOptions {
|
|
|
99
100
|
systemPromptOverride?: string;
|
|
100
101
|
maxResponseTokens?: number;
|
|
101
102
|
transport?: SessionTransportMetadata;
|
|
103
|
+
assistantId?: string;
|
|
104
|
+
guardianContext?: GuardianRuntimeContext;
|
|
102
105
|
memoryScopeId?: string;
|
|
103
106
|
isPrivateThread?: boolean;
|
|
104
107
|
strictPrivateSideEffects?: boolean;
|
|
108
|
+
/** Channel command intent metadata (e.g. Telegram /start). */
|
|
109
|
+
commandIntent?: { type: string; payload?: string; languageCode?: string };
|
|
105
110
|
}
|
|
106
111
|
|
|
107
112
|
/**
|
|
@@ -249,7 +254,7 @@ export function wireEscalationHandler(
|
|
|
249
254
|
}
|
|
250
255
|
|
|
251
256
|
export function isRecord(value: unknown): value is Record<string, unknown> {
|
|
252
|
-
return typeof value === 'object' && value
|
|
257
|
+
return typeof value === 'object' && value != null;
|
|
253
258
|
}
|
|
254
259
|
|
|
255
260
|
export function formatBytes(sizeBytes: number): string {
|
|
@@ -423,7 +423,7 @@ export async function handleSkillsSearch(
|
|
|
423
423
|
ctx: HandlerContext,
|
|
424
424
|
): Promise<void> {
|
|
425
425
|
try {
|
|
426
|
-
// Search vellum-skills catalog (
|
|
426
|
+
// Search vellum-skills catalog (platform API with bundled fallback)
|
|
427
427
|
const catalogEntries = await listCatalogEntries();
|
|
428
428
|
const query = (msg.query ?? '').toLowerCase();
|
|
429
429
|
const matchingCatalog = catalogEntries.filter((e) => {
|