@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.
Files changed (506) hide show
  1. package/Dockerfile +2 -0
  2. package/README.md +88 -2
  3. package/eslint.config.mjs +31 -0
  4. package/package.json +1 -1
  5. package/scripts/ipc/check-swift-decoder-drift.ts +4 -1
  6. package/scripts/ipc/generate-swift.ts +31 -2
  7. package/src/__tests__/__snapshots__/ipc-snapshot.test.ts.snap +438 -1
  8. package/src/__tests__/approval-conversation-turn.test.ts +214 -0
  9. package/src/__tests__/approval-hardcoded-copy-guard.test.ts +41 -0
  10. package/src/__tests__/approval-message-composer.test.ts +253 -0
  11. package/src/__tests__/browser-manager.test.ts +1 -0
  12. package/src/__tests__/call-conversation-messages.test.ts +130 -0
  13. package/src/__tests__/call-domain.test.ts +12 -2
  14. package/src/__tests__/call-orchestrator.test.ts +799 -249
  15. package/src/__tests__/call-pointer-messages.test.ts +148 -0
  16. package/src/__tests__/call-recovery.test.ts +3 -0
  17. package/src/__tests__/call-routes-http.test.ts +32 -2
  18. package/src/__tests__/call-store.test.ts +3 -0
  19. package/src/__tests__/channel-approval-routes.test.ts +1277 -98
  20. package/src/__tests__/channel-approval.test.ts +37 -0
  21. package/src/__tests__/channel-approvals.test.ts +36 -50
  22. package/src/__tests__/channel-guardian.test.ts +630 -22
  23. package/src/__tests__/channel-readiness-service.test.ts +324 -0
  24. package/src/__tests__/checker.test.ts +14 -7
  25. package/src/__tests__/clarification-resolver.test.ts +44 -24
  26. package/src/__tests__/commit-message-enrichment-service.test.ts +9 -4
  27. package/src/__tests__/computer-use-session-working-dir.test.ts +8 -0
  28. package/src/__tests__/config-schema.test.ts +14 -8
  29. package/src/__tests__/context-window-manager.test.ts +30 -2
  30. package/src/__tests__/contradiction-checker.test.ts +20 -5
  31. package/src/__tests__/credential-security-invariants.test.ts +7 -2
  32. package/src/__tests__/daemon-lifecycle.test.ts +13 -12
  33. package/src/__tests__/db-migration-rollback.test.ts +752 -0
  34. package/src/__tests__/dictation-mode-detection.test.ts +63 -0
  35. package/src/__tests__/dynamic-skill-workflow-prompt.test.ts +2 -0
  36. package/src/__tests__/entity-search.test.ts +615 -0
  37. package/src/__tests__/fuzzy-match-property.test.ts +5 -5
  38. package/src/__tests__/guardian-action-store.test.ts +123 -0
  39. package/src/__tests__/guardian-action-sweep.test.ts +277 -0
  40. package/src/__tests__/guardian-dispatch.test.ts +389 -0
  41. package/src/__tests__/guardian-question-copy.test.ts +47 -0
  42. package/src/__tests__/handlers-telegram-config.test.ts +4 -2
  43. package/src/__tests__/handlers-twilio-config.test.ts +533 -0
  44. package/src/__tests__/intent-routing.test.ts +2 -0
  45. package/src/__tests__/ipc-snapshot.test.ts +291 -1
  46. package/src/__tests__/memory-upsert-concurrency.test.ts +828 -0
  47. package/src/__tests__/messaging-send-tool.test.ts +65 -0
  48. package/src/__tests__/model-intents.test.ts +96 -0
  49. package/src/__tests__/no-direct-anthropic-sdk-imports.test.ts +42 -0
  50. package/src/__tests__/oauth2-gateway-transport.test.ts +130 -0
  51. package/src/__tests__/onboarding-starter-tasks.test.ts +2 -0
  52. package/src/__tests__/provider-commit-message-generator.test.ts +89 -13
  53. package/src/__tests__/provider-error-scenarios.test.ts +621 -0
  54. package/src/__tests__/provider-fail-open-selection.test.ts +119 -0
  55. package/src/__tests__/qdrant-manager.test.ts +27 -20
  56. package/src/__tests__/relay-server.test.ts +779 -40
  57. package/src/__tests__/run-orchestrator-assistant-events.test.ts +6 -0
  58. package/src/__tests__/run-orchestrator.test.ts +42 -4
  59. package/src/__tests__/runtime-runs-http.test.ts +17 -1
  60. package/src/__tests__/runtime-runs.test.ts +16 -0
  61. package/src/__tests__/schedule-store.test.ts +18 -4
  62. package/src/__tests__/scheduler-recurrence.test.ts +13 -4
  63. package/src/__tests__/session-abort-tool-results.test.ts +6 -0
  64. package/src/__tests__/session-agent-loop.test.ts +857 -0
  65. package/src/__tests__/session-conflict-gate.test.ts +6 -0
  66. package/src/__tests__/session-pre-run-repair.test.ts +6 -0
  67. package/src/__tests__/session-profile-injection.test.ts +6 -0
  68. package/src/__tests__/session-provider-retry-repair.test.ts +6 -0
  69. package/src/__tests__/session-queue.test.ts +6 -0
  70. package/src/__tests__/session-runtime-assembly.test.ts +321 -13
  71. package/src/__tests__/session-slash-known.test.ts +6 -0
  72. package/src/__tests__/session-slash-queue.test.ts +6 -0
  73. package/src/__tests__/session-slash-unknown.test.ts +6 -0
  74. package/src/__tests__/session-surfaces-task-progress.test.ts +2 -0
  75. package/src/__tests__/session-tool-setup-app-refresh.test.ts +1 -0
  76. package/src/__tests__/session-tool-setup-memory-scope.test.ts +1 -0
  77. package/src/__tests__/session-tool-setup-side-effect-flag.test.ts +1 -0
  78. package/src/__tests__/session-workspace-injection.test.ts +6 -0
  79. package/src/__tests__/session-workspace-tool-tracking.test.ts +6 -0
  80. package/src/__tests__/skills.test.ts +2 -0
  81. package/src/__tests__/sms-messaging-provider.test.ts +126 -0
  82. package/src/__tests__/starter-task-flow.test.ts +2 -0
  83. package/src/__tests__/swarm-dag-pathological.test.ts +535 -0
  84. package/src/__tests__/system-prompt.test.ts +2 -0
  85. package/src/__tests__/task-management-tools.test.ts +2 -2
  86. package/src/__tests__/task-runner.test.ts +14 -4
  87. package/src/__tests__/terminal-tools.test.ts +25 -19
  88. package/src/__tests__/tool-execution-abort-cleanup.test.ts +545 -0
  89. package/src/__tests__/tool-executor-shell-integration.test.ts +11 -11
  90. package/src/__tests__/tool-executor.test.ts +23 -24
  91. package/src/__tests__/trust-store.test.ts +3 -3
  92. package/src/__tests__/twilio-rest.test.ts +29 -0
  93. package/src/__tests__/twilio-routes-elevenlabs.test.ts +3 -0
  94. package/src/__tests__/twilio-routes-twiml.test.ts +11 -0
  95. package/src/__tests__/twilio-routes.test.ts +167 -11
  96. package/src/__tests__/twitter-cli-error-shaping.test.ts +2 -2
  97. package/src/__tests__/user-reference.test.ts +2 -0
  98. package/src/__tests__/voice-quality.test.ts +222 -0
  99. package/src/__tests__/web-search.test.ts +46 -30
  100. package/src/__tests__/work-item-output.test.ts +110 -0
  101. package/src/agent/loop.ts +1 -1
  102. package/src/agent-heartbeat/agent-heartbeat-service.ts +2 -10
  103. package/src/amazon/client.ts +1418 -0
  104. package/src/amazon/request-extractor.ts +135 -0
  105. package/src/amazon/session.ts +109 -0
  106. package/src/autonomy/autonomy-store.ts +5 -5
  107. package/src/browser-extension-relay/client.ts +124 -0
  108. package/src/browser-extension-relay/protocol.ts +63 -0
  109. package/src/browser-extension-relay/server.ts +177 -0
  110. package/src/bundler/app-bundler.ts +3 -3
  111. package/src/bundler/bundle-signer.ts +1 -1
  112. package/src/bundler/signature-verifier.ts +1 -1
  113. package/src/calls/call-conversation-messages.ts +33 -0
  114. package/src/calls/call-domain.ts +114 -10
  115. package/src/calls/call-orchestrator.ts +268 -59
  116. package/src/calls/call-pointer-messages.ts +53 -0
  117. package/src/calls/call-recovery.ts +3 -8
  118. package/src/calls/call-store.ts +69 -87
  119. package/src/calls/elevenlabs-config.ts +3 -2
  120. package/src/calls/guardian-action-sweep.ts +105 -0
  121. package/src/calls/guardian-dispatch.ts +203 -0
  122. package/src/calls/guardian-question-copy.ts +133 -0
  123. package/src/calls/relay-server.ts +466 -8
  124. package/src/calls/speaker-identification.ts +1 -1
  125. package/src/calls/twilio-config.ts +22 -14
  126. package/src/calls/twilio-provider.ts +6 -4
  127. package/src/calls/twilio-rest.ts +308 -7
  128. package/src/calls/twilio-routes.ts +65 -12
  129. package/src/calls/types.ts +3 -1
  130. package/src/channels/types.ts +25 -0
  131. package/src/cli/amazon.ts +815 -0
  132. package/src/cli/config-commands.ts +2 -2
  133. package/src/cli/core-commands.ts +4 -3
  134. package/src/cli/influencer.ts +244 -0
  135. package/src/cli/map.ts +89 -6
  136. package/src/cli.ts +1 -1
  137. package/src/config/agent-schema.ts +171 -0
  138. package/src/config/bundled-skills/amazon/SKILL.md +127 -0
  139. package/src/config/bundled-skills/amazon/icon.svg +13 -0
  140. package/src/config/bundled-skills/api-mapping/SKILL.md +78 -0
  141. package/src/config/bundled-skills/browser/SKILL.md +1 -0
  142. package/src/config/bundled-skills/browser/TOOLS.json +17 -0
  143. package/src/config/bundled-skills/browser/tools/browser-wait-for-download.ts +25 -0
  144. package/src/config/bundled-skills/doordash/SKILL.md +51 -51
  145. package/src/config/bundled-skills/email-setup/SKILL.md +14 -5
  146. package/src/config/bundled-skills/google-oauth-setup/SKILL.md +183 -0
  147. package/src/config/bundled-skills/influencer/SKILL.md +144 -0
  148. package/src/config/bundled-skills/knowledge-graph/SKILL.md +15 -0
  149. package/src/config/bundled-skills/knowledge-graph/TOOLS.json +56 -0
  150. package/src/config/bundled-skills/knowledge-graph/tools/graph-query.ts +185 -0
  151. package/src/config/bundled-skills/macos-automation/icon.svg +12 -0
  152. package/src/config/bundled-skills/media-processing/SKILL.md +176 -0
  153. package/src/config/bundled-skills/media-processing/TOOLS.json +230 -0
  154. package/src/config/bundled-skills/media-processing/__tests__/concurrency-pool.test.ts +77 -0
  155. package/src/config/bundled-skills/media-processing/__tests__/cost-tracker.test.ts +69 -0
  156. package/src/config/bundled-skills/media-processing/__tests__/preprocess.test.ts +303 -0
  157. package/src/config/bundled-skills/media-processing/services/concurrency-pool.ts +55 -0
  158. package/src/config/bundled-skills/media-processing/services/cost-tracker.ts +86 -0
  159. package/src/config/bundled-skills/media-processing/services/gemini-map.ts +339 -0
  160. package/src/config/bundled-skills/media-processing/services/preprocess.ts +551 -0
  161. package/src/config/bundled-skills/media-processing/services/processing-pipeline.ts +259 -0
  162. package/src/config/bundled-skills/media-processing/services/reduce.ts +197 -0
  163. package/src/config/bundled-skills/media-processing/tools/analyze-keyframes.ts +136 -0
  164. package/src/config/bundled-skills/media-processing/tools/extract-keyframes.ts +59 -0
  165. package/src/config/bundled-skills/media-processing/tools/generate-clip.ts +195 -0
  166. package/src/config/bundled-skills/media-processing/tools/ingest-media.ts +197 -0
  167. package/src/config/bundled-skills/media-processing/tools/media-diagnostics.ts +143 -0
  168. package/src/config/bundled-skills/media-processing/tools/media-status.ts +75 -0
  169. package/src/config/bundled-skills/media-processing/tools/query-media-events.ts +65 -0
  170. package/src/config/bundled-skills/messaging/SKILL.md +33 -8
  171. package/src/config/bundled-skills/messaging/tools/messaging-analyze-style.ts +4 -7
  172. package/src/config/bundled-skills/messaging/tools/messaging-reply.ts +2 -1
  173. package/src/config/bundled-skills/messaging/tools/messaging-send.ts +5 -1
  174. package/src/config/bundled-skills/phone-calls/SKILL.md +88 -23
  175. package/src/config/bundled-skills/twitter/SKILL.md +19 -3
  176. package/src/config/bundled-skills/twitter/icon.svg +14 -0
  177. package/src/config/bundled-tool-registry.ts +310 -0
  178. package/src/config/calls-schema.ts +181 -0
  179. package/src/config/core-schema.ts +309 -0
  180. package/src/config/defaults.ts +28 -3
  181. package/src/config/env-registry.ts +162 -0
  182. package/src/config/env.ts +175 -0
  183. package/src/config/loader.ts +6 -6
  184. package/src/config/memory-schema.ts +528 -0
  185. package/src/config/sandbox-schema.ts +55 -0
  186. package/src/config/schema.ts +158 -1133
  187. package/src/config/skill-state.ts +1 -1
  188. package/src/config/skills-schema.ts +32 -0
  189. package/src/config/skills.ts +35 -24
  190. package/src/config/system-prompt.ts +131 -56
  191. package/src/config/templates/IDENTITY.md +2 -2
  192. package/src/config/templates/SOUL.md +1 -1
  193. package/src/config/types.ts +1 -0
  194. package/src/config/user-reference.ts +4 -9
  195. package/src/config/vellum-skills/catalog.json +6 -7
  196. package/src/config/vellum-skills/chatgpt-import/tools/chatgpt-import.ts +5 -1
  197. package/src/config/vellum-skills/slack-oauth-setup/SKILL.md +4 -3
  198. package/src/config/vellum-skills/sms-setup/SKILL.md +216 -0
  199. package/src/config/vellum-skills/twilio-setup/SKILL.md +40 -8
  200. package/src/context/window-manager.ts +27 -7
  201. package/src/daemon/approval-generators.ts +186 -0
  202. package/src/daemon/approved-devices-store.ts +140 -0
  203. package/src/daemon/assistant-attachments.ts +1 -1
  204. package/src/daemon/classifier.ts +35 -32
  205. package/src/daemon/config-watcher.ts +1 -1
  206. package/src/daemon/daemon-control.ts +217 -0
  207. package/src/daemon/handlers/apps.ts +2 -3
  208. package/src/daemon/handlers/config-channels.ts +158 -0
  209. package/src/daemon/handlers/config-inbox.ts +540 -0
  210. package/src/daemon/handlers/config-ingress.ts +231 -0
  211. package/src/daemon/handlers/config-integrations.ts +258 -0
  212. package/src/daemon/handlers/config-model.ts +143 -0
  213. package/src/daemon/handlers/config-parental.ts +163 -0
  214. package/src/daemon/handlers/config-scheduling.ts +172 -0
  215. package/src/daemon/handlers/config-slack.ts +92 -0
  216. package/src/daemon/handlers/config-telegram.ts +301 -0
  217. package/src/daemon/handlers/config-tools.ts +177 -0
  218. package/src/daemon/handlers/config-trust.ts +104 -0
  219. package/src/daemon/handlers/config-twilio.ts +1080 -0
  220. package/src/daemon/handlers/config.ts +53 -1689
  221. package/src/daemon/handlers/diagnostics.ts +1 -1
  222. package/src/daemon/handlers/dictation.ts +180 -0
  223. package/src/daemon/handlers/documents.ts +18 -32
  224. package/src/daemon/handlers/identity.ts +14 -23
  225. package/src/daemon/handlers/index.ts +11 -0
  226. package/src/daemon/handlers/misc.ts +3 -5
  227. package/src/daemon/handlers/pairing.ts +98 -0
  228. package/src/daemon/handlers/sessions.ts +56 -5
  229. package/src/daemon/handlers/shared.ts +6 -1
  230. package/src/daemon/handlers/skills.ts +1 -1
  231. package/src/daemon/handlers/twitter-auth.ts +2 -0
  232. package/src/daemon/handlers/work-items.ts +17 -9
  233. package/src/daemon/handlers/workspace-files.ts +4 -3
  234. package/src/daemon/install-cli-launchers.ts +113 -0
  235. package/src/daemon/ipc-contract/apps.ts +356 -0
  236. package/src/daemon/ipc-contract/browser.ts +74 -0
  237. package/src/daemon/ipc-contract/computer-use.ts +151 -0
  238. package/src/daemon/ipc-contract/diagnostics.ts +56 -0
  239. package/src/daemon/ipc-contract/documents.ts +74 -0
  240. package/src/daemon/ipc-contract/inbox.ts +209 -0
  241. package/src/daemon/ipc-contract/integrations.ts +284 -0
  242. package/src/daemon/ipc-contract/memory.ts +48 -0
  243. package/src/daemon/ipc-contract/messages.ts +211 -0
  244. package/src/daemon/ipc-contract/pairing.ts +45 -0
  245. package/src/daemon/ipc-contract/parental-control.ts +95 -0
  246. package/src/daemon/ipc-contract/schedules.ts +97 -0
  247. package/src/daemon/ipc-contract/sessions.ts +315 -0
  248. package/src/daemon/ipc-contract/shared.ts +42 -0
  249. package/src/daemon/ipc-contract/skills.ts +120 -0
  250. package/src/daemon/ipc-contract/subagents.ts +58 -0
  251. package/src/daemon/ipc-contract/surfaces.ts +250 -0
  252. package/src/daemon/ipc-contract/trust.ts +60 -0
  253. package/src/daemon/ipc-contract/work-items.ts +225 -0
  254. package/src/daemon/ipc-contract/workspace.ts +113 -0
  255. package/src/daemon/ipc-contract-inventory.json +70 -0
  256. package/src/daemon/ipc-contract-inventory.ts +55 -29
  257. package/src/daemon/ipc-contract.ts +229 -2426
  258. package/src/daemon/ipc-protocol.ts +1 -1
  259. package/src/daemon/ipc-validate.ts +7 -0
  260. package/src/daemon/lifecycle.ts +97 -377
  261. package/src/daemon/pairing-store.ts +177 -0
  262. package/src/daemon/providers-setup.ts +43 -0
  263. package/src/daemon/ride-shotgun-handler.ts +68 -3
  264. package/src/daemon/server.ts +66 -46
  265. package/src/daemon/session-agent-loop-handlers.ts +421 -0
  266. package/src/daemon/session-agent-loop.ts +117 -275
  267. package/src/daemon/session-dynamic-profile.ts +1 -1
  268. package/src/daemon/session-history.ts +1 -1
  269. package/src/daemon/session-media-retry.ts +1 -1
  270. package/src/daemon/session-messaging.ts +37 -2
  271. package/src/daemon/session-notifiers.ts +5 -25
  272. package/src/daemon/session-process.ts +99 -59
  273. package/src/daemon/session-queue-manager.ts +96 -4
  274. package/src/daemon/session-runtime-assembly.ts +199 -10
  275. package/src/daemon/session-surfaces.ts +19 -4
  276. package/src/daemon/session-tool-setup.ts +30 -30
  277. package/src/daemon/session-workspace.ts +1 -1
  278. package/src/daemon/session.ts +35 -2
  279. package/src/daemon/shutdown-handlers.ts +122 -0
  280. package/src/daemon/trace-emitter.ts +1 -1
  281. package/src/daemon/watch-handler.ts +36 -33
  282. package/src/doordash/cart-queries.ts +787 -0
  283. package/src/doordash/client.ts +144 -127
  284. package/src/doordash/order-queries.ts +85 -0
  285. package/src/doordash/queries.ts +10 -1308
  286. package/src/doordash/search-queries.ts +203 -0
  287. package/src/doordash/session.ts +3 -2
  288. package/src/doordash/store-queries.ts +246 -0
  289. package/src/doordash/types.ts +367 -0
  290. package/src/email/providers/agentmail.ts +2 -1
  291. package/src/email/providers/index.ts +3 -2
  292. package/src/email/service.ts +3 -2
  293. package/src/errors.ts +43 -0
  294. package/src/home-base/prebuilt/seed.ts +1 -1
  295. package/src/hooks/cli.ts +6 -5
  296. package/src/hooks/config.ts +6 -8
  297. package/src/hooks/discovery.ts +6 -5
  298. package/src/hooks/manager.ts +4 -3
  299. package/src/hooks/runner.ts +2 -2
  300. package/src/hooks/templates.ts +5 -5
  301. package/src/inbound/public-ingress-urls.ts +6 -4
  302. package/src/index.ts +4 -2
  303. package/src/influencer/client.ts +1104 -0
  304. package/src/instrument.ts +4 -3
  305. package/src/logfire.ts +4 -3
  306. package/src/memory/admin.ts +25 -35
  307. package/src/memory/attachments-store.ts +4 -7
  308. package/src/memory/channel-delivery-store.ts +30 -1
  309. package/src/memory/channel-guardian-store.ts +202 -2
  310. package/src/memory/clarification-resolver.ts +37 -33
  311. package/src/memory/conflict-store.ts +67 -61
  312. package/src/memory/contradiction-checker.ts +141 -117
  313. package/src/memory/conversation-store.ts +335 -51
  314. package/src/memory/db-connection.ts +27 -4
  315. package/src/memory/db-init.ts +265 -4
  316. package/src/memory/db.ts +14 -1
  317. package/src/memory/embedding-backend.ts +27 -5
  318. package/src/memory/embedding-ollama.ts +2 -1
  319. package/src/memory/entity-extractor.ts +38 -35
  320. package/src/memory/guardian-action-store.ts +430 -0
  321. package/src/memory/inbox-escalation-projection.ts +59 -0
  322. package/src/memory/inbox-thread-store.ts +218 -0
  323. package/src/memory/ingress-invite-store.ts +338 -0
  324. package/src/memory/ingress-member-store.ts +350 -0
  325. package/src/memory/items-extractor.ts +91 -97
  326. package/src/memory/job-handlers/index-maintenance.ts +3 -3
  327. package/src/memory/job-handlers/media-processing.ts +69 -0
  328. package/src/memory/job-handlers/summarization.ts +32 -26
  329. package/src/memory/job-utils.ts +3 -10
  330. package/src/memory/jobs-store.ts +8 -10
  331. package/src/memory/jobs-worker.ts +55 -36
  332. package/src/memory/media-store.ts +759 -0
  333. package/src/memory/migrations/001-job-deferrals.ts +45 -0
  334. package/src/memory/migrations/002-tool-invocations-fk.ts +43 -0
  335. package/src/memory/migrations/003-memory-fts-backfill.ts +24 -0
  336. package/src/memory/migrations/004-entity-relation-dedup.ts +87 -0
  337. package/src/memory/migrations/005-fingerprint-scope-unique.ts +80 -0
  338. package/src/memory/migrations/006-scope-salted-fingerprints.ts +62 -0
  339. package/src/memory/migrations/007-assistant-id-to-self.ts +254 -0
  340. package/src/memory/migrations/008-remove-assistant-id-columns.ts +208 -0
  341. package/src/memory/migrations/009-llm-usage-events-drop-assistant-id.ts +83 -0
  342. package/src/memory/migrations/010-ext-conv-bindings-channel-chat-unique.ts +56 -0
  343. package/src/memory/migrations/011-call-sessions-provider-sid-dedup.ts +63 -0
  344. package/src/memory/migrations/012-call-sessions-add-initiated-from.ts +19 -0
  345. package/src/memory/migrations/013-guardian-action-tables.ts +68 -0
  346. package/src/memory/migrations/014-backfill-inbox-thread-state.ts +76 -0
  347. package/src/memory/migrations/015-drop-active-search-index.ts +27 -0
  348. package/src/memory/migrations/016-memory-segments-indexes.ts +11 -0
  349. package/src/memory/migrations/017-memory-items-indexes.ts +10 -0
  350. package/src/memory/migrations/018-remaining-table-indexes.ts +13 -0
  351. package/src/memory/migrations/index.ts +24 -0
  352. package/src/memory/migrations/registry.ts +79 -0
  353. package/src/memory/migrations/validate-migration-state.ts +69 -0
  354. package/src/memory/qdrant-manager.ts +49 -8
  355. package/src/memory/query-builder.ts +1 -1
  356. package/src/memory/raw-query.ts +119 -0
  357. package/src/memory/recall-cache.ts +4 -1
  358. package/src/memory/retriever.ts +165 -47
  359. package/src/memory/schema-migration.ts +25 -984
  360. package/src/memory/schema.ts +228 -7
  361. package/src/memory/search/entity.ts +205 -31
  362. package/src/memory/search/lexical.ts +81 -52
  363. package/src/memory/search/ranking.ts +27 -23
  364. package/src/memory/search/semantic.ts +157 -19
  365. package/src/memory/search/types.ts +24 -0
  366. package/src/memory/shared-app-links-store.ts +4 -5
  367. package/src/memory/validation.ts +19 -0
  368. package/src/messaging/draft-store.ts +5 -6
  369. package/src/messaging/provider-types.ts +2 -0
  370. package/src/messaging/providers/sms/adapter.ts +201 -0
  371. package/src/messaging/providers/sms/client.ts +93 -0
  372. package/src/messaging/providers/sms/types.ts +7 -0
  373. package/src/messaging/providers/telegram-bot/adapter.ts +2 -5
  374. package/src/messaging/providers/whatsapp/adapter.ts +136 -0
  375. package/src/messaging/providers/whatsapp/client.ts +67 -0
  376. package/src/messaging/style-analyzer.ts +5 -4
  377. package/src/messaging/thread-summarizer.ts +61 -69
  378. package/src/messaging/triage-engine.ts +62 -71
  379. package/src/migrations/config-merge.ts +53 -0
  380. package/src/migrations/data-layout.ts +68 -0
  381. package/src/migrations/data-merge.ts +33 -0
  382. package/src/migrations/hooks-merge.ts +90 -0
  383. package/src/migrations/index.ts +6 -0
  384. package/src/migrations/log.ts +23 -0
  385. package/src/migrations/skills-merge.ts +33 -0
  386. package/src/migrations/workspace-layout.ts +79 -0
  387. package/src/permissions/checker.ts +133 -11
  388. package/src/permissions/prompter.ts +14 -0
  389. package/src/permissions/shell-identity.ts +31 -1
  390. package/src/permissions/trust-store.ts +21 -1
  391. package/src/providers/anthropic/client.ts +4 -4
  392. package/src/providers/failover.ts +2 -2
  393. package/src/providers/model-intents.ts +70 -0
  394. package/src/providers/ollama/client.ts +2 -1
  395. package/src/providers/provider-send-message.ts +176 -0
  396. package/src/providers/registry.ts +71 -30
  397. package/src/providers/retry.ts +35 -1
  398. package/src/providers/types.ts +12 -1
  399. package/src/runtime/approval-conversation-turn.ts +97 -0
  400. package/src/runtime/approval-message-composer.ts +253 -0
  401. package/src/runtime/channel-approval-parser.ts +36 -2
  402. package/src/runtime/channel-approvals.ts +11 -24
  403. package/src/runtime/channel-guardian-service.ts +88 -21
  404. package/src/runtime/channel-readiness-service.ts +418 -0
  405. package/src/runtime/channel-readiness-types.ts +35 -0
  406. package/src/runtime/channel-retry-sweep.ts +184 -0
  407. package/src/runtime/guardian-context-resolver.ts +108 -0
  408. package/src/runtime/http-server.ts +275 -717
  409. package/src/runtime/http-types.ts +59 -3
  410. package/src/runtime/middleware/auth.ts +116 -0
  411. package/src/runtime/middleware/error-handler.ts +33 -0
  412. package/src/runtime/middleware/twilio-validation.ts +127 -0
  413. package/src/runtime/routes/app-routes.ts +1 -1
  414. package/src/runtime/routes/call-routes.ts +51 -7
  415. package/src/runtime/routes/channel-delivery-routes.ts +170 -0
  416. package/src/runtime/routes/channel-guardian-routes.ts +1191 -0
  417. package/src/runtime/routes/channel-inbound-routes.ts +1152 -0
  418. package/src/runtime/routes/channel-route-shared.ts +144 -0
  419. package/src/runtime/routes/channel-routes.ts +32 -1588
  420. package/src/runtime/routes/conversation-routes.ts +50 -7
  421. package/src/runtime/routes/events-routes.ts +2 -2
  422. package/src/runtime/routes/identity-routes.ts +126 -0
  423. package/src/runtime/routes/pairing-routes.ts +143 -0
  424. package/src/runtime/routes/run-routes.ts +15 -1
  425. package/src/runtime/run-orchestrator.ts +86 -35
  426. package/src/schedule/schedule-store.ts +36 -32
  427. package/src/schedule/scheduler.ts +3 -3
  428. package/src/security/encrypted-store.ts +5 -7
  429. package/src/security/oauth2.ts +45 -15
  430. package/src/security/parental-control-store.ts +183 -0
  431. package/src/security/secret-allowlist.ts +4 -3
  432. package/src/security/secret-scanner.ts +5 -5
  433. package/src/security/secure-keys.ts +1 -1
  434. package/src/security/token-manager.ts +3 -2
  435. package/src/services/vercel-deploy.ts +6 -2
  436. package/src/skills/tool-manifest.ts +3 -3
  437. package/src/skills/vellum-catalog-remote.ts +75 -16
  438. package/src/slack/slack-webhook.ts +2 -1
  439. package/src/swarm/orchestrator.ts +92 -1
  440. package/src/swarm/router-planner.ts +6 -9
  441. package/src/swarm/worker-prompts.ts +9 -12
  442. package/src/tasks/task-compiler.ts +19 -28
  443. package/src/tasks/task-runner.ts +1 -1
  444. package/src/tools/assets/materialize.ts +2 -2
  445. package/src/tools/assets/search.ts +15 -14
  446. package/src/tools/browser/__tests__/auth-detector.test.ts +1 -0
  447. package/src/tools/browser/auto-navigate.ts +1 -0
  448. package/src/tools/browser/browser-execution.ts +10 -1
  449. package/src/tools/browser/browser-manager.ts +119 -4
  450. package/src/tools/browser/network-recorder.ts +5 -0
  451. package/src/tools/calls/call-start.ts +1 -0
  452. package/src/tools/credentials/broker.ts +11 -2
  453. package/src/tools/credentials/metadata-store.ts +18 -14
  454. package/src/tools/credentials/post-connect-hooks.ts +61 -0
  455. package/src/tools/credentials/vault.ts +49 -23
  456. package/src/tools/execution-target.ts +11 -1
  457. package/src/tools/executor.ts +68 -9
  458. package/src/tools/host-terminal/cli-discover.ts +1 -1
  459. package/src/tools/network/script-proxy/http-forwarder.ts +1 -1
  460. package/src/tools/network/script-proxy/mitm-handler.ts +1 -1
  461. package/src/tools/network/script-proxy/server.ts +1 -1
  462. package/src/tools/network/script-proxy/session-manager.ts +6 -5
  463. package/src/tools/network/web-fetch.ts +18 -2
  464. package/src/tools/network/web-search.ts +8 -4
  465. package/src/tools/reminder/reminder-store.ts +14 -15
  466. package/src/tools/schedule/create.ts +1 -0
  467. package/src/tools/schedule/list.ts +2 -1
  468. package/src/tools/shared/filesystem/file-ops-service.ts +5 -7
  469. package/src/tools/skills/skill-script-runner.ts +24 -9
  470. package/src/tools/skills/skill-tool-factory.ts +1 -0
  471. package/src/tools/tasks/work-item-enqueue.ts +2 -2
  472. package/src/tools/terminal/evaluate-typescript.ts +21 -12
  473. package/src/tools/terminal/parser.ts +50 -0
  474. package/src/tools/types.ts +2 -0
  475. package/src/tools/watcher/delete.ts +6 -0
  476. package/src/tools/weather/service.ts +1 -1
  477. package/src/twitter/client.ts +190 -24
  478. package/src/twitter/router.ts +1 -1
  479. package/src/twitter/session.ts +4 -3
  480. package/src/util/clipboard.ts +1 -1
  481. package/src/util/errors.ts +65 -8
  482. package/src/util/fs.ts +40 -0
  483. package/src/util/json.ts +10 -0
  484. package/src/util/log-redact.ts +189 -0
  485. package/src/util/logger.ts +19 -17
  486. package/src/util/object.ts +3 -0
  487. package/src/util/platform.ts +105 -363
  488. package/src/util/pricing.ts +1 -1
  489. package/src/util/promise-guard.ts +1 -1
  490. package/src/util/retry.ts +19 -0
  491. package/src/util/row-mapper.ts +79 -0
  492. package/src/util/silently.ts +21 -0
  493. package/src/watcher/engine.ts +5 -1
  494. package/src/watcher/provider-types.ts +20 -0
  495. package/src/watcher/providers/github.ts +156 -0
  496. package/src/watcher/providers/gmail.ts +1 -0
  497. package/src/watcher/providers/google-calendar.ts +1 -0
  498. package/src/watcher/providers/linear.ts +460 -0
  499. package/src/watcher/providers/slack.ts +1 -0
  500. package/src/work-items/work-item-runner.ts +1 -1
  501. package/src/workspace/git-service.ts +1 -1
  502. package/src/workspace/provider-commit-message-generator.ts +51 -22
  503. package/src/__tests__/call-bridge.test.ts +0 -517
  504. package/src/__tests__/session-process-bridge.test.ts +0 -244
  505. package/src/calls/call-bridge.ts +0 -168
  506. 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 { getConfig } from '../../config/loader.js';
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, sourceChannel } = body;
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 !== undefined && content !== null && typeof content !== 'string') {
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 config = getConfig();
315
- if (listProviders().includes(config.provider)) {
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 !== null && controller.desiredSize <= 0) {
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 !== null && controller.desiredSize <= 0) {
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 !== undefined && content !== null && typeof content !== 'string') {
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) => Promise<Session>;
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 'http-api'.
74
+ * default 'macos'.
68
75
  */
69
- sourceChannel?: string;
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
- const session = await this.deps.getOrCreateSession(conversationId);
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 'http-api'.
136
- session.setChannelCapabilities(resolveChannelCapabilities(options?.sourceChannel ?? 'http-api'));
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 = hubChain
149
- .then(() => assistantEventHub.publish(event))
150
- .catch((err: unknown) => {
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
- session.runAgentLoop(content, messageId, (msg: ServerMessage) => {
212
- if (msg.type === 'error') {
213
- lastError = msg.message;
214
- } else if (msg.type === 'session_error') {
215
- lastError = msg.userMessage;
216
- }
217
- // Mirror agent-loop events (assistant_text_delta, message_complete,
218
- // tool_use_start, tool_result, etc.) to the hub. These travel through
219
- // the onEvent path, distinct from the updateClient path used by the
220
- // prompter (confirmation_request). Both paths must publish so SSE
221
- // consumers receive the full response stream.
222
- publishToHub(msg);
223
- }).then(() => {
224
- if (lastError) {
225
- log.error({ runId: run.id, error: lastError }, 'Run failed (error event from agent loop)');
226
- runsStore.failRun(run.id, lastError);
227
- } else {
228
- runsStore.completeRun(run.id);
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
- cleanup();
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
  }