@vellumai/assistant 0.3.5 → 0.3.7

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