managed-deepagents 0.10.0-dev.8 → 0.10.0

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 (397) hide show
  1. package/README.md +881 -139
  2. package/dist/browser.d.ts +37 -0
  3. package/dist/browser.d.ts.map +1 -0
  4. package/dist/browser.js +31 -0
  5. package/dist/browser.js.map +1 -0
  6. package/dist/channels/actor.d.ts +57 -0
  7. package/dist/channels/actor.d.ts.map +1 -0
  8. package/dist/channels/actor.js +24 -0
  9. package/dist/channels/actor.js.map +1 -0
  10. package/dist/channels/assistant-text.d.ts +29 -0
  11. package/dist/channels/assistant-text.d.ts.map +1 -1
  12. package/dist/channels/assistant-text.js +81 -4
  13. package/dist/channels/assistant-text.js.map +1 -1
  14. package/dist/channels/binding.d.ts +20 -1
  15. package/dist/channels/binding.d.ts.map +1 -1
  16. package/dist/channels/binding.js +44 -11
  17. package/dist/channels/binding.js.map +1 -1
  18. package/dist/channels/delivery.d.ts +51 -0
  19. package/dist/channels/delivery.d.ts.map +1 -0
  20. package/dist/channels/delivery.js +424 -0
  21. package/dist/channels/delivery.js.map +1 -0
  22. package/dist/channels/github/app-setup.d.ts +32 -0
  23. package/dist/channels/github/app-setup.d.ts.map +1 -0
  24. package/dist/channels/github/app-setup.js +146 -0
  25. package/dist/channels/github/app-setup.js.map +1 -0
  26. package/dist/channels/github/index.d.ts +5 -1
  27. package/dist/channels/github/index.d.ts.map +1 -1
  28. package/dist/channels/github/index.js +49 -51
  29. package/dist/channels/github/index.js.map +1 -1
  30. package/dist/channels/github/mentions.d.ts +12 -0
  31. package/dist/channels/github/mentions.d.ts.map +1 -0
  32. package/dist/channels/github/mentions.js +185 -0
  33. package/dist/channels/github/mentions.js.map +1 -0
  34. package/dist/channels/github/schema.d.ts +271 -0
  35. package/dist/channels/github/schema.d.ts.map +1 -1
  36. package/dist/channels/github/schema.js +33 -0
  37. package/dist/channels/github/schema.js.map +1 -1
  38. package/dist/channels/github/types.d.ts +55 -15
  39. package/dist/channels/github/types.d.ts.map +1 -1
  40. package/dist/channels/http/endpoint.d.ts +137 -0
  41. package/dist/channels/http/endpoint.d.ts.map +1 -0
  42. package/dist/channels/http/endpoint.js +286 -0
  43. package/dist/channels/http/endpoint.js.map +1 -0
  44. package/dist/channels/http/http.d.ts.map +1 -1
  45. package/dist/channels/http/http.js +240 -95
  46. package/dist/channels/http/http.js.map +1 -1
  47. package/dist/channels/http/index.d.ts +7 -1
  48. package/dist/channels/http/index.d.ts.map +1 -1
  49. package/dist/channels/http/index.js +32 -32
  50. package/dist/channels/http/index.js.map +1 -1
  51. package/dist/channels/http/ingress.d.ts +30 -24
  52. package/dist/channels/http/ingress.d.ts.map +1 -1
  53. package/dist/channels/http/ingress.js +61 -57
  54. package/dist/channels/http/ingress.js.map +1 -1
  55. package/dist/channels/http/provider.d.ts +51 -9
  56. package/dist/channels/http/provider.d.ts.map +1 -1
  57. package/dist/channels/http/provider.js +66 -15
  58. package/dist/channels/http/provider.js.map +1 -1
  59. package/dist/channels/http/studio.d.ts +14 -5
  60. package/dist/channels/http/studio.d.ts.map +1 -1
  61. package/dist/channels/http/studio.js +20 -5
  62. package/dist/channels/http/studio.js.map +1 -1
  63. package/dist/channels/http/types.d.ts +110 -56
  64. package/dist/channels/http/types.d.ts.map +1 -1
  65. package/dist/channels/linear/app-setup.d.ts +29 -0
  66. package/dist/channels/linear/app-setup.d.ts.map +1 -0
  67. package/dist/channels/linear/app-setup.js +102 -0
  68. package/dist/channels/linear/app-setup.js.map +1 -0
  69. package/dist/channels/linear/index.d.ts +5 -1
  70. package/dist/channels/linear/index.d.ts.map +1 -1
  71. package/dist/channels/linear/index.js +75 -93
  72. package/dist/channels/linear/index.js.map +1 -1
  73. package/dist/channels/linear/schema.d.ts +11 -25
  74. package/dist/channels/linear/schema.d.ts.map +1 -1
  75. package/dist/channels/linear/schema.js +6 -0
  76. package/dist/channels/linear/schema.js.map +1 -1
  77. package/dist/channels/linear/types.d.ts +38 -16
  78. package/dist/channels/linear/types.d.ts.map +1 -1
  79. package/dist/channels/messaging-registry.d.ts +16 -0
  80. package/dist/channels/messaging-registry.d.ts.map +1 -1
  81. package/dist/channels/messaging-registry.js +29 -0
  82. package/dist/channels/messaging-registry.js.map +1 -1
  83. package/dist/channels/reserved-connections.d.ts.map +1 -1
  84. package/dist/channels/reserved-connections.js +10 -0
  85. package/dist/channels/reserved-connections.js.map +1 -1
  86. package/dist/channels/resolve-user-endpoint.d.ts +34 -0
  87. package/dist/channels/resolve-user-endpoint.d.ts.map +1 -0
  88. package/dist/channels/resolve-user-endpoint.js +51 -0
  89. package/dist/channels/resolve-user-endpoint.js.map +1 -0
  90. package/dist/channels/resolve-user.d.ts +95 -0
  91. package/dist/channels/resolve-user.d.ts.map +1 -0
  92. package/dist/channels/resolve-user.js +143 -0
  93. package/dist/channels/resolve-user.js.map +1 -0
  94. package/dist/channels/run-input.d.ts +46 -4
  95. package/dist/channels/run-input.d.ts.map +1 -1
  96. package/dist/channels/run-input.js +94 -30
  97. package/dist/channels/run-input.js.map +1 -1
  98. package/dist/channels/runtime.d.ts +5 -2
  99. package/dist/channels/runtime.d.ts.map +1 -1
  100. package/dist/channels/runtime.js +19 -10
  101. package/dist/channels/runtime.js.map +1 -1
  102. package/dist/channels/schedule-delivery.d.ts +2 -13
  103. package/dist/channels/schedule-delivery.d.ts.map +1 -1
  104. package/dist/channels/schedule-delivery.js +9 -76
  105. package/dist/channels/schedule-delivery.js.map +1 -1
  106. package/dist/channels/slack/api.d.ts +2 -0
  107. package/dist/channels/slack/api.d.ts.map +1 -1
  108. package/dist/channels/slack/api.js +6 -0
  109. package/dist/channels/slack/api.js.map +1 -1
  110. package/dist/channels/slack/connections.d.ts +6 -0
  111. package/dist/channels/slack/connections.d.ts.map +1 -0
  112. package/dist/channels/slack/connections.js +6 -0
  113. package/dist/channels/slack/connections.js.map +1 -0
  114. package/dist/channels/slack/events.d.ts +278 -0
  115. package/dist/channels/slack/events.d.ts.map +1 -0
  116. package/dist/channels/slack/events.js +235 -0
  117. package/dist/channels/slack/events.js.map +1 -0
  118. package/dist/channels/slack/files.d.ts.map +1 -1
  119. package/dist/channels/slack/files.js +2 -3
  120. package/dist/channels/slack/files.js.map +1 -1
  121. package/dist/channels/slack/index.d.ts +90 -2
  122. package/dist/channels/slack/index.d.ts.map +1 -1
  123. package/dist/channels/slack/index.js +188 -6
  124. package/dist/channels/slack/index.js.map +1 -1
  125. package/dist/channels/slack/message-content.d.ts.map +1 -1
  126. package/dist/channels/slack/message-content.js +23 -28
  127. package/dist/channels/slack/message-content.js.map +1 -1
  128. package/dist/channels/slack/status.d.ts +76 -0
  129. package/dist/channels/slack/status.d.ts.map +1 -0
  130. package/dist/channels/slack/status.js +427 -0
  131. package/dist/channels/slack/status.js.map +1 -0
  132. package/dist/channels/trigger/client.d.ts.map +1 -1
  133. package/dist/channels/trigger/client.js +9 -11
  134. package/dist/channels/trigger/client.js.map +1 -1
  135. package/dist/channels/trigger/conversation.d.ts +11 -0
  136. package/dist/channels/trigger/conversation.d.ts.map +1 -0
  137. package/dist/channels/trigger/conversation.js +72 -0
  138. package/dist/channels/trigger/conversation.js.map +1 -0
  139. package/dist/channels/trigger/http.d.ts +130 -4
  140. package/dist/channels/trigger/http.d.ts.map +1 -1
  141. package/dist/channels/trigger/http.js +419 -82
  142. package/dist/channels/trigger/http.js.map +1 -1
  143. package/dist/channels/trigger/identity-context.d.ts +1 -1
  144. package/dist/channels/trigger/identity-context.d.ts.map +1 -1
  145. package/dist/channels/trigger/identity-context.js +3 -5
  146. package/dist/channels/trigger/identity-context.js.map +1 -1
  147. package/dist/channels/trigger/ingress.d.ts +70 -1
  148. package/dist/channels/trigger/ingress.d.ts.map +1 -1
  149. package/dist/channels/trigger/ingress.js +141 -9
  150. package/dist/channels/trigger/ingress.js.map +1 -1
  151. package/dist/channels/trigger/transport.d.ts +10 -0
  152. package/dist/channels/trigger/transport.d.ts.map +1 -1
  153. package/dist/channels/trigger/transport.js +25 -9
  154. package/dist/channels/trigger/transport.js.map +1 -1
  155. package/dist/channels/trigger/types.d.ts +92 -3
  156. package/dist/channels/trigger/types.d.ts.map +1 -1
  157. package/dist/channels/trigger/types.js +37 -0
  158. package/dist/channels/trigger/types.js.map +1 -1
  159. package/dist/channels/types.d.ts +53 -4
  160. package/dist/channels/types.d.ts.map +1 -1
  161. package/dist/channels/types.js +2 -0
  162. package/dist/channels/types.js.map +1 -1
  163. package/dist/concurrency/batch-processor.d.ts +51 -0
  164. package/dist/concurrency/batch-processor.d.ts.map +1 -0
  165. package/dist/concurrency/batch-processor.js +99 -0
  166. package/dist/concurrency/batch-processor.js.map +1 -0
  167. package/dist/connections.d.ts +5 -12
  168. package/dist/connections.d.ts.map +1 -1
  169. package/dist/connections.js +28 -30
  170. package/dist/connections.js.map +1 -1
  171. package/dist/connectors/mcp/langsmith-url.d.ts.map +1 -1
  172. package/dist/connectors/mcp/langsmith-url.js +0 -1
  173. package/dist/connectors/mcp/langsmith-url.js.map +1 -1
  174. package/dist/credential-gate.js +3 -3
  175. package/dist/credential-gate.js.map +1 -1
  176. package/dist/define-deep-agent.d.ts +2 -2
  177. package/dist/define-deep-agent.d.ts.map +1 -1
  178. package/dist/define-deep-agent.js +53 -2
  179. package/dist/define-deep-agent.js.map +1 -1
  180. package/dist/identity/index.d.ts.map +1 -1
  181. package/dist/identity/index.js +3 -1
  182. package/dist/identity/index.js.map +1 -1
  183. package/dist/identity/loopback.d.ts +7 -2
  184. package/dist/identity/loopback.d.ts.map +1 -1
  185. package/dist/identity/loopback.js +15 -4
  186. package/dist/identity/loopback.js.map +1 -1
  187. package/dist/identity/options.d.ts +9 -0
  188. package/dist/identity/options.d.ts.map +1 -1
  189. package/dist/identity/options.js +1 -1
  190. package/dist/identity/options.js.map +1 -1
  191. package/dist/identity/slack/links.js +3 -2
  192. package/dist/identity/slack/links.js.map +1 -1
  193. package/dist/identity/types.d.ts +3 -0
  194. package/dist/identity/types.d.ts.map +1 -1
  195. package/dist/index.d.ts +15 -5
  196. package/dist/index.d.ts.map +1 -1
  197. package/dist/index.js +2 -1
  198. package/dist/index.js.map +1 -1
  199. package/dist/ingress/index.d.ts +8 -0
  200. package/dist/ingress/index.d.ts.map +1 -1
  201. package/dist/ingress/index.js +32 -2
  202. package/dist/ingress/index.js.map +1 -1
  203. package/dist/ingress/loopback.d.ts +1 -0
  204. package/dist/ingress/loopback.d.ts.map +1 -1
  205. package/dist/ingress/loopback.js +7 -3
  206. package/dist/ingress/loopback.js.map +1 -1
  207. package/dist/ingress/runs.d.ts +27 -1
  208. package/dist/ingress/runs.d.ts.map +1 -1
  209. package/dist/ingress/runs.js +69 -6
  210. package/dist/ingress/runs.js.map +1 -1
  211. package/dist/managed-composition.d.ts +3 -0
  212. package/dist/managed-composition.d.ts.map +1 -1
  213. package/dist/managed-composition.js +29 -13
  214. package/dist/managed-composition.js.map +1 -1
  215. package/dist/memory/instructions-middleware.d.ts.map +1 -1
  216. package/dist/memory/instructions-middleware.js +6 -5
  217. package/dist/memory/instructions-middleware.js.map +1 -1
  218. package/dist/namespaces.d.ts +4 -21
  219. package/dist/namespaces.d.ts.map +1 -1
  220. package/dist/namespaces.js +0 -29
  221. package/dist/namespaces.js.map +1 -1
  222. package/dist/resilience/retry.d.ts +21 -0
  223. package/dist/resilience/retry.d.ts.map +1 -0
  224. package/dist/resilience/retry.js +40 -0
  225. package/dist/resilience/retry.js.map +1 -0
  226. package/dist/runtime/async-subagents.d.ts +27 -0
  227. package/dist/runtime/async-subagents.d.ts.map +1 -0
  228. package/dist/runtime/async-subagents.js +139 -0
  229. package/dist/runtime/async-subagents.js.map +1 -0
  230. package/dist/runtime/auth.d.ts +1 -2
  231. package/dist/runtime/auth.d.ts.map +1 -1
  232. package/dist/runtime/auth.js +138 -72
  233. package/dist/runtime/auth.js.map +1 -1
  234. package/dist/runtime/backend.d.ts +9 -1
  235. package/dist/runtime/backend.d.ts.map +1 -1
  236. package/dist/runtime/backend.js +36 -11
  237. package/dist/runtime/backend.js.map +1 -1
  238. package/dist/runtime/browser-facade.d.ts +10 -0
  239. package/dist/runtime/browser-facade.d.ts.map +1 -0
  240. package/dist/runtime/browser-facade.js +23 -0
  241. package/dist/runtime/browser-facade.js.map +1 -0
  242. package/dist/runtime/browser-langsmith.d.ts +143 -0
  243. package/dist/runtime/browser-langsmith.d.ts.map +1 -0
  244. package/dist/runtime/browser-langsmith.js +507 -0
  245. package/dist/runtime/browser-langsmith.js.map +1 -0
  246. package/dist/runtime/browser-recorder.py +831 -0
  247. package/dist/runtime/browser-selection.d.ts +10 -0
  248. package/dist/runtime/browser-selection.d.ts.map +1 -0
  249. package/dist/runtime/browser-selection.js +21 -0
  250. package/dist/runtime/browser-selection.js.map +1 -0
  251. package/dist/runtime/browser.d.ts +138 -0
  252. package/dist/runtime/browser.d.ts.map +1 -0
  253. package/dist/runtime/browser.js +802 -0
  254. package/dist/runtime/browser.js.map +1 -0
  255. package/dist/runtime/channel-icon.d.ts.map +1 -1
  256. package/dist/runtime/channel-icon.js +18 -7
  257. package/dist/runtime/channel-icon.js.map +1 -1
  258. package/dist/runtime/channel.d.ts +5 -0
  259. package/dist/runtime/channel.d.ts.map +1 -1
  260. package/dist/runtime/channel.js.map +1 -1
  261. package/dist/runtime/graph-build-timing.d.ts +30 -0
  262. package/dist/runtime/graph-build-timing.d.ts.map +1 -0
  263. package/dist/runtime/graph-build-timing.js +50 -0
  264. package/dist/runtime/graph-build-timing.js.map +1 -0
  265. package/dist/runtime/http.d.ts +15 -0
  266. package/dist/runtime/http.d.ts.map +1 -1
  267. package/dist/runtime/http.js +37 -0
  268. package/dist/runtime/http.js.map +1 -1
  269. package/dist/runtime/identity-http.d.ts.map +1 -1
  270. package/dist/runtime/identity-http.js +19 -7
  271. package/dist/runtime/identity-http.js.map +1 -1
  272. package/dist/runtime/identity-runtime.d.ts +4 -9
  273. package/dist/runtime/identity-runtime.d.ts.map +1 -1
  274. package/dist/runtime/identity-runtime.js +12 -25
  275. package/dist/runtime/identity-runtime.js.map +1 -1
  276. package/dist/runtime/index.d.ts +38 -1
  277. package/dist/runtime/index.d.ts.map +1 -1
  278. package/dist/runtime/index.js +104 -52
  279. package/dist/runtime/index.js.map +1 -1
  280. package/dist/runtime/langsmith-env.d.ts +19 -0
  281. package/dist/runtime/langsmith-env.d.ts.map +1 -0
  282. package/dist/runtime/langsmith-env.js +38 -0
  283. package/dist/runtime/langsmith-env.js.map +1 -0
  284. package/dist/runtime/log.d.ts +8 -14
  285. package/dist/runtime/log.d.ts.map +1 -1
  286. package/dist/runtime/log.js +16 -7
  287. package/dist/runtime/log.js.map +1 -1
  288. package/dist/runtime/managed-middleware.d.ts.map +1 -1
  289. package/dist/runtime/managed-middleware.js +1 -14
  290. package/dist/runtime/managed-middleware.js.map +1 -1
  291. package/dist/runtime/managed-thread.d.ts +61 -0
  292. package/dist/runtime/managed-thread.d.ts.map +1 -0
  293. package/dist/runtime/managed-thread.js +76 -0
  294. package/dist/runtime/managed-thread.js.map +1 -0
  295. package/dist/runtime/managed-tools.d.ts +18 -2
  296. package/dist/runtime/managed-tools.d.ts.map +1 -1
  297. package/dist/runtime/managed-tools.js +26 -22
  298. package/dist/runtime/managed-tools.js.map +1 -1
  299. package/dist/runtime/memory/mounts.d.ts +82 -11
  300. package/dist/runtime/memory/mounts.d.ts.map +1 -1
  301. package/dist/runtime/memory/mounts.js +173 -34
  302. package/dist/runtime/memory/mounts.js.map +1 -1
  303. package/dist/runtime/memory/policy.d.ts.map +1 -1
  304. package/dist/runtime/memory/policy.js +1 -3
  305. package/dist/runtime/memory/policy.js.map +1 -1
  306. package/dist/runtime/parent-run.d.ts +33 -0
  307. package/dist/runtime/parent-run.d.ts.map +1 -0
  308. package/dist/runtime/parent-run.js +114 -0
  309. package/dist/runtime/parent-run.js.map +1 -0
  310. package/dist/runtime/run-input.d.ts +14 -1
  311. package/dist/runtime/run-input.d.ts.map +1 -1
  312. package/dist/runtime/run-input.js +118 -7
  313. package/dist/runtime/run-input.js.map +1 -1
  314. package/dist/runtime/sandbox-manager.d.ts +31 -17
  315. package/dist/runtime/sandbox-manager.d.ts.map +1 -1
  316. package/dist/runtime/sandbox-manager.js +112 -70
  317. package/dist/runtime/sandbox-manager.js.map +1 -1
  318. package/dist/runtime/schedule-auth.d.ts +79 -0
  319. package/dist/runtime/schedule-auth.d.ts.map +1 -1
  320. package/dist/runtime/schedule-auth.js +165 -7
  321. package/dist/runtime/schedule-auth.js.map +1 -1
  322. package/dist/runtime/selected-skills-backend.d.ts +3 -3
  323. package/dist/runtime/selected-skills-backend.d.ts.map +1 -1
  324. package/dist/runtime/selected-skills-backend.js +5 -5
  325. package/dist/runtime/selected-skills-backend.js.map +1 -1
  326. package/dist/runtime/static-entry.d.ts.map +1 -1
  327. package/dist/runtime/static-entry.js +1 -0
  328. package/dist/runtime/static-entry.js.map +1 -1
  329. package/dist/runtime/thread-record.d.ts +88 -0
  330. package/dist/runtime/thread-record.d.ts.map +1 -0
  331. package/dist/runtime/thread-record.js +213 -0
  332. package/dist/runtime/thread-record.js.map +1 -0
  333. package/dist/runtime/types.d.ts +12 -1
  334. package/dist/runtime/types.d.ts.map +1 -1
  335. package/dist/runtime-schedules.d.ts +15 -0
  336. package/dist/runtime-schedules.d.ts.map +1 -1
  337. package/dist/runtime-schedules.js +85 -23
  338. package/dist/runtime-schedules.js.map +1 -1
  339. package/dist/sandbox.d.ts +0 -6
  340. package/dist/sandbox.d.ts.map +1 -1
  341. package/dist/sandbox.js +3 -7
  342. package/dist/sandbox.js.map +1 -1
  343. package/dist/schedule-channel.d.ts +5 -2
  344. package/dist/schedule-channel.d.ts.map +1 -1
  345. package/dist/schedule-channel.js +13 -12
  346. package/dist/schedule-channel.js.map +1 -1
  347. package/dist/telemetry/lifecycle.d.ts +26 -0
  348. package/dist/telemetry/lifecycle.d.ts.map +1 -0
  349. package/dist/telemetry/lifecycle.js +49 -0
  350. package/dist/telemetry/lifecycle.js.map +1 -0
  351. package/dist/telemetry/metadata.d.ts.map +1 -1
  352. package/dist/telemetry/metadata.js +4 -4
  353. package/dist/telemetry/metadata.js.map +1 -1
  354. package/dist/telemetry/otel/context.d.ts +57 -0
  355. package/dist/telemetry/otel/context.d.ts.map +1 -0
  356. package/dist/telemetry/otel/context.js +122 -0
  357. package/dist/telemetry/otel/context.js.map +1 -0
  358. package/dist/telemetry/otel/exporter.d.ts +53 -0
  359. package/dist/telemetry/otel/exporter.d.ts.map +1 -0
  360. package/dist/telemetry/otel/exporter.js +190 -0
  361. package/dist/telemetry/otel/exporter.js.map +1 -0
  362. package/dist/telemetry/otel/index.d.ts +6 -0
  363. package/dist/telemetry/otel/index.d.ts.map +1 -0
  364. package/dist/telemetry/otel/index.js +3 -0
  365. package/dist/telemetry/otel/index.js.map +1 -0
  366. package/dist/telemetry/otel/otlp.d.ts +44 -0
  367. package/dist/telemetry/otel/otlp.d.ts.map +1 -0
  368. package/dist/telemetry/otel/otlp.js +7 -0
  369. package/dist/telemetry/otel/otlp.js.map +1 -0
  370. package/dist/telemetry/otel/truncate.d.ts +10 -0
  371. package/dist/telemetry/otel/truncate.d.ts.map +1 -0
  372. package/dist/telemetry/otel/truncate.js +39 -0
  373. package/dist/telemetry/otel/truncate.js.map +1 -0
  374. package/dist/telemetry/run-listener.d.ts +1 -1
  375. package/dist/telemetry/run-listener.d.ts.map +1 -1
  376. package/dist/telemetry/run-listener.js +32 -2
  377. package/dist/telemetry/run-listener.js.map +1 -1
  378. package/dist/telemetry/transport/posthog.d.ts +13 -1
  379. package/dist/telemetry/transport/posthog.d.ts.map +1 -1
  380. package/dist/telemetry/transport/posthog.js +68 -30
  381. package/dist/telemetry/transport/posthog.js.map +1 -1
  382. package/dist/types.d.ts +19 -2
  383. package/dist/types.d.ts.map +1 -1
  384. package/dist/types.js.map +1 -1
  385. package/dist/values.d.ts +15 -0
  386. package/dist/values.d.ts.map +1 -0
  387. package/dist/values.js +31 -0
  388. package/dist/values.js.map +1 -0
  389. package/package.json +26 -12
  390. package/dist/channels/http/forward.d.ts +0 -13
  391. package/dist/channels/http/forward.d.ts.map +0 -1
  392. package/dist/channels/http/forward.js +0 -128
  393. package/dist/channels/http/forward.js.map +0 -1
  394. package/dist/channels/http/reply.d.ts +0 -20
  395. package/dist/channels/http/reply.d.ts.map +0 -1
  396. package/dist/channels/http/reply.js +0 -116
  397. package/dist/channels/http/reply.js.map +0 -1
package/README.md CHANGED
@@ -136,7 +136,10 @@ export function agent(runtime: ManagedServerRuntime) {
136
136
  const ctx = runtime.executionRuntime?.context ?? {};
137
137
  return defineDeepAgent({
138
138
  name: "support",
139
- model: "tier" in ctx && ctx.tier === "fast" ? "openai:gpt-5-mini" : "openai:gpt-5.5",
139
+ model:
140
+ "tier" in ctx && ctx.tier === "fast"
141
+ ? "openai:gpt-5-mini"
142
+ : "openai:gpt-5.5",
140
143
  });
141
144
  }
142
145
  ```
@@ -148,7 +151,7 @@ strings and arrays remain explicit selections. Select memory with `defineMemory(
148
151
  selected memory cannot use `allow`. Root `memory.ts` remains the fallback.
149
152
 
150
153
  On direct runs, `executionRuntime.context` is application context or `undefined`.
151
- On channel runs, it is `undefined`; read delivery data from `runtime.channel`.
154
+ On channel runs, it is `undefined`; read the conversation from `runtime.thread`.
152
155
  `ManagedServerRuntime<AppContext>` preserves the application's context type.
153
156
  `accessContext` identifies the Agent Server operation that called the factory.
154
157
  During inspection, `executionRuntime` is `null`. Keep the factory repeatable and
@@ -240,12 +243,18 @@ current channel delivery:
240
243
  runtime
241
244
  .context // Application data on direct runs
242
245
  .serverInfo // Trusted caller and server information
243
- .channel // Present for a managed channel delivery
244
- .name // Configured channel name
245
- .provider // Provider label, such as slack
246
- .event // Typed MDA delivery; no internal routing
247
- .rawEvent // Optional original provider payload
248
- .post(...) // Present when sending is supported
246
+ .thread // The run's thread
247
+ .id // LangGraph thread ID
248
+ .origin // The channel conversation, or null
249
+ .channel // Where answers go
250
+ .name // Configured channel name
251
+ .provider // Provider label, such as slack
252
+ .event // Typed MDA delivery, or null
253
+ .rawEvent // Optional original provider payload
254
+ .post(...) // Present when sending is supported
255
+ .sender // The participant who sent the latest event, or null
256
+ .participants // The people on the thread: { id, role }
257
+ .channel // Deprecated: runtime.thread.origin?.channel
249
258
  .backend // Present when a sandbox is configured
250
259
  .schedules // Create and manage schedules for this run's caller
251
260
  ```
@@ -262,8 +271,8 @@ await client.runs.create(threadId, assistantId, {
262
271
  Direct API runs use your authored `contextSchema` and native LangGraph context
263
272
  validation. On channel runs, tools and authored factories receive `undefined`
264
273
  as context. Native middleware receives an empty context. Use
265
- `runtime.channel.event` for the typed event and `runtime.channel.rawEvent` for
266
- optional provider data. The application schema stays on the graph, but its
274
+ `runtime.thread.origin.channel.event` for the typed event and
275
+ `runtime.thread.origin.channel.rawEvent` for optional provider data. The application schema stays on the graph, but its
267
276
  required fields do not apply to channel runs. Supplying a `channel` field in a
268
277
  direct API call does not establish a trusted channel source.
269
278
 
@@ -274,25 +283,63 @@ in the graph factory for each run or resume. Direct callers of the managed
274
283
  compiler must build a fresh graph with the execution's context; a compiled graph
275
284
  is scoped to that execution.
276
285
 
277
- `runtime.channel` describes the current verified delivery. It is absent on
278
- ordinary API and schedule runs. Its facts are available even when the channel
279
- cannot send. `post` uses a private, verified reply target. The channel helper
280
- does not expose the target or let callers replace it.
286
+ `runtime.thread` is set in every run. `origin` is the channel conversation the
287
+ thread came from; it is `null` on a thread no channel message reached. Managed
288
+ ingress records the origin on the thread on every channel message, so runs that
289
+ no message started still have it: a run LangSmith Studio resumes, and a
290
+ scheduled run on the thread. In those runs `origin.channel.event` is `null` (a
291
+ scheduled run gets a `schedule` event) and there is no `rawEvent`. The channel's
292
+ facts are available even when it cannot send. `post` uses a private, verified
293
+ reply target. The channel helper does not expose the target or let callers
294
+ replace it.
281
295
 
282
296
  ```ts
283
- if (runtime.channel?.post) {
284
- await runtime.channel.post({ type: "content", content: "I am checking that now." });
297
+ const channel = runtime.thread?.origin?.channel;
298
+ if (channel?.post) {
299
+ await channel.post({
300
+ type: "content",
301
+ content: "I am checking that now.",
302
+ });
285
303
  }
286
304
  ```
287
305
 
288
- The built-in Slack `runtime.channel.post` sends text content through Trigger.
306
+ `participants` lists the people on the thread, in the order they joined, by
307
+ `principal.id` (the same id as `runtime.serverInfo.principal.id`). The first
308
+ person is `owner`; everyone after is a `member`. Services, such as a GitHub
309
+ bot, are never participants. `origin.sender` is the participant who sent the
310
+ latest channel event, or `null` when that was not a person. The record holds
311
+ ids and routing only, never message text or provider payloads, because thread
312
+ metadata reaches traces.
313
+
314
+ `runtime.channel` is deprecated and will be removed in 0.11. It is the same
315
+ value as `runtime.thread.origin?.channel`.
316
+
317
+ When a run ends, MDA posts its answer to the thread's origin: Slack through
318
+ Trigger, HTTP channels through their `post`. A run that stops on an interrupt
319
+ reports it to Trigger, so Slack can ask for the decision. Answer `[quiet]` to
320
+ post nothing.
321
+
322
+ The built-in Slack channel `post` sends text content through Trigger.
289
323
  It rejects native payloads and content it cannot send. For channels that support
290
324
  native messages, set `type` to `"native"` and supply the JSON in `native`.
291
325
  For content messages, set `type` to `"content"` and supply `content`.
292
326
  Supply only the matching payload. Native JSON has no extra provider or payload
293
- wrapper. The `runtime.channel` helper has no `address`, `update`, or destination override.
327
+ wrapper. The channel helper has no `address`, `update`, or destination override.
294
328
  A post sends an additional message; the normal final agent reply remains automatic.
295
329
 
330
+ On Slack, `post(message, { newThread: true })` posts at top level in the same
331
+ conversation instead, which starts a new Slack thread. Replies in it continue a
332
+ thread of their own, also without a mention in thread mode. Add `run` to start a
333
+ run there at once, with a channel handler's run input, as the current run's
334
+ person. The result also has `threadId` and, on Slack, the message `url`:
335
+
336
+ ```ts
337
+ const posted = await runtime.thread?.origin?.channel.post?.(
338
+ { type: "content", content: "Haiku about mango" },
339
+ { newThread: true, run: { messages: [...history, new HumanMessage("Go on.")] } }
340
+ );
341
+ ```
342
+
296
343
  Managed Slack channels save incoming files under `/workspace/attachments/` and
297
344
  add `upload_files(paths)` so the agent can attach completed workspace files
298
345
  to its reply. This requires a managed sandbox.
@@ -312,7 +359,8 @@ MDA does not reuse the previous delivery's event.
312
359
  For a prompt that answers an interrupt, put a shared ID on the prompt and on the
313
360
  interrupt value under `INTERRUPT_CORRELATION_KEY`. MDA uses `correlation_id` to
314
361
  select that interrupt. Without a correlation ID, an answer can resume the only
315
- pending interrupt; multiple pending interrupts require a match. Existing resume
362
+ pending interrupt that isn't a tool-call review; multiple pending interrupts
363
+ require a match. Existing resume
316
364
  values are unchanged.
317
365
 
318
366
  The managed runtime fields are available to authored tools and middleware,
@@ -323,6 +371,23 @@ when available. Its `link.status` currently reports `not_required`; it does not
323
371
  perform an account-link lookup. Its source thread identifier retains the existing
324
372
  provider-thread/LangGraph-thread fallback behavior.
325
373
 
374
+ ### Self-hosted channel runtime configuration
375
+
376
+ Agent Server's `MOUNT_PREFIX` is honored by managed event routes and default
377
+ loopback clients. Explicit `MDA_CHANNEL_API_URL` or `LANGGRAPH_API_URL` overrides
378
+ must already include the deployment path.
379
+
380
+ Trigger-backed channels require `LANGSMITH_MDA_TRIGGER_SERVER_URL` and a
381
+ deployment key (`LANGSMITH_API_KEY`, or `LANGCHAIN_API_KEY` on self-hosted).
382
+ The runtime prefers `LANGSMITH_API_KEY` when both are present.
383
+
384
+ If the Agent Auth token issuer differs from the runtime's LangSmith API URL,
385
+ set `OAUTH_AS_ISSUER` in the deployment environment to the configured issuer
386
+ (for example, `https://langsmith.example.com/api`). The runtime verifies the
387
+ exact issuer and fetches its public `/.well-known/jwks.json`; it does not trust
388
+ an issuer supplied by an incoming token. LangSmith must have signing keys
389
+ configured, and the agent must be able to reach that public-key endpoint.
390
+
326
391
  ## Tool and middleware runtime types
327
392
 
328
393
  Use `ManagedToolRuntime<State, Context>` for tools and tool-call hooks. Use
@@ -333,6 +398,327 @@ alias for the tool type. MDA adds `serverInfo`, `channel`, `backend`, and
333
398
  but the type marks it optional so annotated hooks still match LangChain's native
334
399
  runtime types.
335
400
 
401
+ ## Slack channels
402
+
403
+ `channels.slack(...)` receives Slack messages through Trigger Server. Each
404
+ Slack thread is one conversation, and the run's reply is posted in it.
405
+ `triggerMode` decides which channel messages reach the app; direct messages
406
+ always do:
407
+
408
+ - `"mention"`: only messages that @-mention the agent. New Slack apps use this.
409
+ - `"thread"`: one @-mention, then every later reply in that thread, so people
410
+ mention the agent once.
411
+ - `"all"`: every message in the channels the agent is in.
412
+
413
+ When `triggerMode` is unset, a deployed agent keeps its current mode, except
414
+ that an agent that drops `"all"` moves to `"mention"`. Redeploying with a new
415
+ mode updates the existing Slack app. `triggerOnAllMessages` is deprecated;
416
+ `true` is `triggerMode: "all"`.
417
+
418
+ By default every message that reaches the app starts a run with the message.
419
+ `onEvent` decides what each kind of message starts instead:
420
+
421
+ ```ts
422
+ // channels/slack.ts
423
+ import { channels } from "managed-deepagents";
424
+
425
+ export const channel = channels.slack({
426
+ name: "Support Bot",
427
+ triggerMode: "thread",
428
+ onEvent: {
429
+ mention: (event, thread) => [
430
+ event.text,
431
+ {
432
+ context: { ticket: thread.id },
433
+ metadata: { slack_channel: event.channelId },
434
+ },
435
+ ],
436
+ directMessage: (event) => event.text,
437
+ // Replies in a followed thread that don't mention the app start nothing.
438
+ message: () => null,
439
+ },
440
+ multitaskStrategy: "enqueue",
441
+ });
442
+ ```
443
+
444
+ The keys are `mention`, a channel message that @-mentions the app;
445
+ `directMessage`; and `message`, any other channel message, which only
446
+ `triggerMode` `"thread"` or `"all"` delivers. Under those modes Slack sends a
447
+ mention as a plain message, so the channel asks Slack once for the app's bot
448
+ user, with the app's bot token, to tell a mention from a message. An unknown
449
+ key throws. A message kind without a handler starts a run with the message,
450
+ as without `onEvent`. With `triggerMode: "mention"`, a `message` handler is
451
+ never called, and defining the channel warns.
452
+
453
+ With a `mention` handler under `triggerMode` `"thread"`, a reply only
454
+ continues a thread the agent already ran on: when the handler declines a
455
+ thread's mention, the thread's later replies start nothing until someone
456
+ mentions the agent there again.
457
+
458
+ A handler gets the message (`text`, `userId`, `channelId`, `messageTs`,
459
+ `threadTs`, `files`, Slack's own event as `payload`, and `actor`) and its
460
+ thread (`id`, `sourceThreadKey`, `channel.name`). It returns the run's input, as
461
+ `agent.invoke` takes it, optionally with `context`, `metadata` and `webhook`
462
+ as `[input, options]`, as the GitHub and Linear handlers do. `null` or
463
+ `undefined` starts no run, and the app neither reacts nor replies. A handler
464
+ that throws, or returns input the channel can't start, starts no run either;
465
+ the error is logged without its message. The message's blocks and attachments
466
+ are added to the handler's messages, and its files still reach
467
+ `/workspace/attachments/`. Interrupt resumes don't go through `onEvent`; a
468
+ click on a button the agent posted does, as `interaction`: see
469
+ [Buttons your agent posts](#buttons-your-agent-posts).
470
+
471
+ A handler may also be an endpoint of your service, a URL or a `Request`:
472
+ Trigger Server gets its answer first, then MDA asks the service what the
473
+ message starts. See [Asking your service what to start](#asking-your-service-what-to-start).
474
+
475
+ ```ts
476
+ const backend = new Request(`${process.env.BACKEND_URL}/slack/runs`, {
477
+ headers: { "x-api-key": process.env.BACKEND_KEY! },
478
+ });
479
+
480
+ export const channel = channels.slack({
481
+ onEvent: { mention: backend, directMessage: backend },
482
+ });
483
+ ```
484
+
485
+ `multitaskStrategy` is what a message that starts a run does while its thread
486
+ already runs: `enqueue` (the default) waits for the run and starts after it,
487
+ `interrupt` cancels the run, keeping its progress, and starts at once,
488
+ `rollback` cancels it and discards its progress, and `reject` starts nothing.
489
+
490
+ `event.actor` is the Slack user who sent the message, as Trigger Server read
491
+ it from Slack's signed request: `{ provider: "slack", id, teamId }`. `teamId`
492
+ is their own workspace: in a Slack Connect channel, someone from another
493
+ organization keeps theirs. It is absent rather than guessed when Trigger
494
+ Server names no one, such as an older Trigger Server or a bot's message,
495
+ or when the check below fails.
496
+
497
+ > **Checked against the signed principal.** Trigger Server sends `actor` as
498
+ > a plain field of the delivery. MDA asks Agent Auth for this user's principal
499
+ > in the app's workspace and keeps `actor` only when it matches the principal
500
+ > Agent Auth signed for the delivery. `id` is checked this way. A Slack Connect
501
+ > guest's own team is not.
502
+
503
+ Every channel's events carry `actor` in this shape, a `ChannelActor`: `{
504
+ provider, id }` plus the provider's own fields. GitHub's is the webhook's
505
+ `sender` (`name` is its login, plus `type` and `email?`), and Linear's is who
506
+ acted (plus `type?`, `name?` and `email?`); Slack's has no name or email,
507
+ since Trigger Server's delivery names none. Endpoints get it as `event.actor` too. It is personal
508
+ data: MDA doesn't log it or add it to a run's metadata; add what you need
509
+ through `metadata` yourself.
510
+
511
+ `onFinish` is called once when a message or an `interaction` is done, with
512
+ the event, its thread, and how it finished, as Linear's `onFinish` is:
513
+
514
+ - `run`: the message started a run, which ended with `run.status`
515
+ (`success`, `error`, `timeout` or `interrupted`). `interrupts` lists the
516
+ decisions the thread waits on.
517
+ - `skipped`: no run started. The handler returned nothing or failed
518
+ (`ignored`), or the thread was busy under `multitaskStrategy: "reject"`
519
+ (`rejected`).
520
+ - `failed`: MDA couldn't start the run, or lost track of it.
521
+
522
+ Use it to record which run answered which message in your own service,
523
+ update memory after a run succeeds, or alert someone when one fails:
524
+
525
+ ```ts
526
+ channels.slack({
527
+ onFinish: async (event, thread, finished) => {
528
+ if (finished.type !== "run") return;
529
+ if (finished.run.status === "success") {
530
+ // e.g. record the answer in your service, or update memory
531
+ } else {
532
+ // e.g. alert someone that the run failed
533
+ }
534
+ },
535
+ });
536
+ ```
537
+
538
+ It runs in the background after the reply, and errors are logged. Interrupt
539
+ resumes don't call it, and neither does a message Trigger Server will send
540
+ again because its run couldn't start. Like `channels.http`'s, it runs in the deployment's
541
+ process, so a restart loses it: set the run's `webhook` for anything you
542
+ can't miss.
543
+
544
+ ### The status a run shows
545
+
546
+ While a run works, Slack shows Trigger Server's working phrases ("is thinking
547
+ it through...", "reviewing your question..."). `status` shows yours instead,
548
+ the way `reactions` picks the emoji: a value for every run, or a function that
549
+ picks one. A value is a line, or a list of steps, each a title or `{ title,
550
+ status }` with `status` `pending` (the default), `in_progress`, `complete` or
551
+ `error`:
552
+
553
+ ```ts
554
+ channels.slack({ status: "is consulting the crystal ball" });
555
+ channels.slack({ status: ["Read the question", "Search the docs", "Answer"] });
556
+ channels.slack({
557
+ // The agent's to-do list, as Slack's plan.
558
+ status: ({ state }) =>
559
+ state.todos?.map((todo) => ({
560
+ title: todo.content,
561
+ status: todo.status === "completed" ? "complete" : todo.status,
562
+ })) ?? null,
563
+ });
564
+ ```
565
+
566
+ The function gets the agent's state as it is now, so it can say which tool is
567
+ about to run (the last message's `tool_calls`) or what's left on the to-do
568
+ list. It is called as the run starts, before and after each model call, and
569
+ every 4 s while a model or tool call runs, one call at a time. `null`, or an
570
+ empty list (as a to-do list is before the agent writes one), leaves what's
571
+ shown. A function that throws, takes longer than 1 s, or returns
572
+ something else leaves it too, with one warning per run. The status is only
573
+ posted when it changed, at most once a second, in the background, so it never
574
+ slows the run; a failed post is logged. A sync function runs on the event
575
+ loop, so do slow work in an async one.
576
+
577
+ Apps with Slack's agent view show a custom status as a plan above the reply:
578
+ the line is the plan's title and each step one of its tasks. A step that's
579
+ no longer listed is marked complete, since Slack can't remove one, and the
580
+ plan reads "Done" when the run ends. Other apps show the line, or the first
581
+ step not yet done, as the thread's status. Without `status`, nothing changes.
582
+
583
+ ### Replying from the agent
584
+
585
+ The channel posts the agent's last reply in the thread once a run succeeds.
586
+ `reply: "none"` leaves replying to the agent instead, for an agent that posts
587
+ its own messages, with `runtime.channel.post` or its own Slack client, or
588
+ that answers somewhere else:
589
+
590
+ ```ts
591
+ channels.slack({ reply: "none" });
592
+ ```
593
+
594
+ Reactions, the status, Stop and `onFinish` work as before. `channels.linear`
595
+ and `channels.http` take the same option. Deploying `reply: "none"` needs an
596
+ `mda` CLI from this release; an older one refuses it.
597
+
598
+ ### Buttons your agent posts
599
+
600
+ `runtime.channel.post` only sends what every channel can. For anything
601
+ Slack-specific, such as buttons, feedback buttons or a message the agent
602
+ updates later, the agent posts with its own Slack client. The app's bot token
603
+ is the agent connection `mda/slack-bot-token` (reading it as a user connection
604
+ throws), and `runtime.channel.rawEvent`, the Slack event the run came from,
605
+ names the thread: a message's own event, or a click's interaction payload,
606
+ whose `container` says where the button was.
607
+
608
+ ```ts
609
+ import { WebClient } from "@slack/web-api";
610
+ import { tool } from "langchain";
611
+ import { z } from "zod";
612
+ import { connections, type ManagedToolRuntime } from "managed-deepagents";
613
+
614
+ export const offerChoices = tool(
615
+ async ({ question, choices }, runtime: ManagedToolRuntime) => {
616
+ if (runtime.channel?.provider !== "slack") return "Not in Slack.";
617
+ const raw = runtime.channel.rawEvent as Record<string, any> | undefined;
618
+ const where = raw?.container ?? raw;
619
+ const channel = where?.channel_id ?? where?.channel;
620
+ const threadTs = where?.thread_ts ?? where?.message_ts ?? where?.ts;
621
+ const token = await connections.get("mda/slack-bot-token", { type: "agent" });
622
+ await new WebClient(token).chat.postMessage({
623
+ channel,
624
+ thread_ts: threadTs,
625
+ text: question,
626
+ blocks: [
627
+ { type: "section", text: { type: "mrkdwn", text: question } },
628
+ {
629
+ type: "actions",
630
+ elements: choices.map((choice) => ({
631
+ type: "button",
632
+ action_id: "quick_reply",
633
+ text: { type: "plain_text", text: choice },
634
+ value: choice,
635
+ })),
636
+ },
637
+ ],
638
+ });
639
+ return "Posted the choices.";
640
+ },
641
+ {
642
+ name: "offer_choices",
643
+ description: "Ask the user to pick one of a few answers in Slack.",
644
+ schema: z.object({ question: z.string(), choices: z.array(z.string()) }),
645
+ }
646
+ );
647
+ ```
648
+
649
+ Trigger Server forwards a click on such a button to the channel. `onEvent`'s
650
+ `interaction` decides what it starts, as the other keys do for messages:
651
+
652
+ ```ts
653
+ channels.slack({
654
+ onEvent: {
655
+ interaction: (event) =>
656
+ event.actionId === "quick_reply" ? String(event.value) : null,
657
+ },
658
+ });
659
+ ```
660
+
661
+ The event has `actionId` (the button's `action_id`, or a modal's
662
+ `callback_id`), `value` (the button's `value`, or the modal's `state.values`),
663
+ `userId`, `channelId`, `messageTs` (the message holding the button; absent for
664
+ a modal), `threadTs`, `actor`, and Slack's interaction payload as `payload`,
665
+ with its `response_url` and without its verification `token`. The run it
666
+ starts runs on the button's thread. Return `null` for a click that starts
667
+ nothing, such as a `feedback_buttons` rating you record yourself. An endpoint works here too, as
668
+ does `multitaskStrategy`.
669
+
670
+ Without `interaction`, a click starts a run with no new message, which reads
671
+ the click from `runtime.channel.event` (`type: "user_prompt_response"`, with
672
+ `action` and `value`). A click that answers a decision the thread waits on
673
+ resumes that run instead, and never reaches `interaction`. A click without a
674
+ correlation ID never answers a tool-call review, which its own prompt answers:
675
+ while the thread waits on one, such a click starts nothing. Trigger Server
676
+ forwards clicks on buttons without a URL and on Slack's `feedback_buttons`;
677
+ selects and inputs aren't forwarded.
678
+
679
+ ### Setting up the Slack app
680
+
681
+ The first `mda deploy` of a Slack channel asks which Slack app it uses, as the
682
+ GitHub and Linear channels do:
683
+
684
+ - **Create a new Slack app in _your workspace_**: LangSmith creates and manages
685
+ it, in the workspace you authorized LangSmith in. Deploy names the workspace.
686
+ - **Let LangSmith create and manage a new Slack app**: the same, when you
687
+ haven't authorized LangSmith in Slack yet. Deploy prints the link to do that.
688
+ - **Create a new Slack app yourself**: Slack opens with the app filled in from
689
+ a manifest. Install it, then paste its Bot User OAuth Token and Signing
690
+ Secret into the prompts.
691
+ - **Use an existing Slack app**: deploy lists the request URLs, bot events and
692
+ scopes to set on the app, then asks for its token and signing secret. Slack
693
+ sends an app's events to one URL, so whatever received them before stops
694
+ receiving them.
695
+
696
+ Self-hosted LangSmith can't create Slack apps, so it offers only your own.
697
+ LangSmith keeps the app's credentials, and later deploys keep whichever app
698
+ the channel has without asking. The agent reads the app's bot token as the
699
+ agent connection `mda/slack-bot-token`, so nothing goes into `.env`.
700
+
701
+ To skip the question, with or without a terminal, set `MDA_SLACK_BOT_TOKEN`
702
+ and `MDA_SLACK_SIGNING_SECRET` in the shell or `.env` to your own app's bot
703
+ token and signing secret. Deploy checks them before uploading. Only the CLI
704
+ reads them: they are never uploaded to the deployment, and you can remove both
705
+ once the channel is set up. Without a terminal and without them, hosted
706
+ LangSmith creates a managed app in the workspace you already authorized it in,
707
+ and says so. When you haven't authorized it yet, or on self-hosted LangSmith,
708
+ deploy stops before uploading and says what to set.
709
+
710
+ When Slack still needs you once the deployment is live, to authorize LangSmith
711
+ or to install the new app (some workspaces have an admin approve it first),
712
+ deploy prints the link and waits, continuing as soon as Slack reports it done.
713
+ In a terminal the link also opens in your browser, and Ctrl-C skips the
714
+ channel. Without one, deploy waits up to an hour, so a coding agent running it
715
+ can hand you the link; Ctrl-C stops it.
716
+
717
+ If a setup step fails once the deployment is live, deploy offers to try again
718
+ or to continue without the Slack channel. Without a terminal the failure ends
719
+ the deploy, once the other channels are set up. An agent with a Slack channel
720
+ can't be deployed with `--no-wait`, because setup needs the live deployment.
721
+
336
722
  ## GitHub channels
337
723
 
338
724
  The GitHub channel is a thin HTTP adapter. Add a channel file with event handlers:
@@ -361,34 +747,18 @@ Acting on GitHub as the person running the agent is not supported yet:
361
747
  `connections.get("mda/github-token", { type: "user" })` throws, and deploy refuses
362
748
  a project that declares it.
363
749
 
364
- A run acts for the GitHub user who sent the event, as `github:<id>`: GitHub
365
- signs each delivery, and the numeric ID outlives login renames. Events from
366
- bots and organizations run as the channel. A GitHub sender cannot be mapped to a
367
- user of the agent's identity, the principal `identity.ts` resolves from LangSmith,
368
- Supabase or another provider. So MCP servers and tools that act on a user's
369
- behalf or need a user connection are left out of those runs, with a warning,
370
- and the run goes on without them. An issue or pull request is a shared
750
+ A run acts for the GitHub user who sent the event, as their Agent Auth
751
+ principal for `(github, github.com, <id>)`: GitHub signs each delivery, and the
752
+ numeric ID outlives login renames. Events from bots and organizations run as
753
+ the channel. Unless `resolveUser` maps it to one of
754
+ your users (see [Mapping channel senders to your users](#mapping-channel-senders-to-your-users)),
755
+ a GitHub sender is not a user of the agent's identity, the principal `identity.ts`
756
+ resolves from LangSmith, Supabase or another provider. So MCP servers and tools
757
+ that act on a user's behalf or need a user connection are left out of those
758
+ runs, with a warning, and the run goes on without them. An issue or pull request is a shared
371
759
  conversation, so the sender gets user memory only where the user memory
372
- layer's `allow` opts in.
373
- The first deploy asks which GitHub account should own the App. Without a
374
- terminal, for example when a coding agent deploys, set `MDA_GITHUB_APP_OWNER` in
375
- the shell or `.env` to that organization, or to `@me` for the personal account
376
- of whoever creates the App. Deploy checks this before creating the deployment
377
- and fails without it. During setup it prints the link for a person to open and
378
- waits up to an hour for them to finish.
379
- To use an existing App instead, choose it on the first deploy and enter its App
380
- ID, private key file, installation ID and webhook secret. Agent Auth keeps them
381
- like a created App's, and deploy looks up the bot ID, so nothing goes into
382
- `.env`. `GH_WEBHOOK_SECRET` and `GH_APP_BOT_ID` (the App bot account's numeric
383
- user ID) still override the stored values. An App has one webhook URL: if the
384
- existing App's webhook points somewhere else, deploy asks before moving it to
385
- the deployment, and fails without a terminal. Answer "No" to keep it there, for
386
- example when another service receives the App's events and forwards the ones
387
- for this agent to `<deployment-url>/channels/github/events` with the body and
388
- GitHub's headers unchanged. Deploy remembers that URL and doesn't ask again, with
389
- or without a terminal, until the App's webhook moves. Without a terminal, set
390
- `MDA_GITHUB_WEBHOOK_ELSEWHERE` to the URL instead. If the App's installation
391
- lacks a permission the channel needs, deploy names it.
760
+ layer's `allow` opts in. See [Setting up the GitHub App](#setting-up-the-github-app)
761
+ for what the first deploy asks.
392
762
 
393
763
  Pass `app` to narrow what the agent can reach. Deploy applies changes to it on
394
764
  every run:
@@ -399,7 +769,9 @@ channels.github({
399
769
  permissions: { contents: "read" }, // for the token, beyond what the events need
400
770
  repositories: ["acme/api", "acme/web"], // one owner
401
771
  },
402
- onEvent: { "issues.opened": (event) => `Triage ${event.payload.issue.title}` },
772
+ onEvent: {
773
+ "issues.opened": (event) => `Triage ${event.payload.issue.title}`,
774
+ },
403
775
  });
404
776
  ```
405
777
 
@@ -417,8 +789,6 @@ manifest only when the App is created, so the App's owner grants added
417
789
  permissions, and removes dropped or lowered ones, in the App's settings. Deploy
418
790
  names both and links there. `mda delete` removes the App's connections
419
791
  from Agent Auth and links the App on GitHub, where its owner can delete it.
420
- A new App is named after the agent, and the first deploy asks which account to
421
- create it in.
422
792
  The `GH_*` overrides are read at request time; the webhook secret and bot ID read
423
793
  from Agent Auth are cached for up to 5 minutes. A matched event fails closed if
424
794
  the bot ID is missing or invalid. Calling `channels.github()` without handlers preserves
@@ -440,6 +810,33 @@ Supported keys:
440
810
  the event name. A CI event continues a pull request's conversation when it
441
811
  ran for exactly one pull request.
442
812
 
813
+ GitHub sends no event for a mention, so each event lists the handles the
814
+ text it wrote mentions in `event.mentions`: the body of a new or edited
815
+ comment, review or review comment, or the title and body of an opened or
816
+ edited issue or pull request. An edit lists only the handles it added, so
817
+ fixing a typo doesn't mention anyone again. Events that write no text, such
818
+ as a push to a pull request, a label or a reopen, list none, even when the
819
+ body they belong to mentions someone. Handles are lowercased, listed once, in
820
+ order, and read as GitHub links them: `@octocat` or `@octo-org/reviewers`, not
821
+ `team@octo.dev`, and not in code, quotes or HTML comments, such as a pull
822
+ request template's `<!-- @openswe -->`. Any text tag counts, such as
823
+ `@openswe`, even one that is no GitHub account. A
824
+ handler that answers mentions checks the list:
825
+
826
+ ```ts
827
+ channels.github({
828
+ onEvent: {
829
+ "issue_comment.created": (event) => {
830
+ if (!event.mentions.includes("@openswe")) return;
831
+ return `Answer the comment on #${event.payload.issue.number}.`;
832
+ },
833
+ },
834
+ });
835
+ ```
836
+
837
+ An issue comment on a pull request has `issue.pull_request` set, and an edit
838
+ says what it changed in `payload.changes`.
839
+
443
840
  While a conversation's run is still going, a new event that starts a run waits
444
841
  for it by default. `multitaskStrategy` changes that, using LangGraph's names:
445
842
  `"interrupt"` cancels the active run and keeps its progress, `"rollback"`
@@ -453,13 +850,19 @@ is application code; incoming webhook payloads are validated at runtime.
453
850
  Signatures are verified against the raw bytes. Ping returns 200; other valid
454
851
  receipts return 202. An unconfigured event or the App's own activity starts
455
852
  nothing. The request awaits the configured callback and run creation before
456
- acknowledgment; synchronous and async callbacks are supported. A handler returns
853
+ acknowledgment (an endpoint's is asked after; see "Asking your service what to
854
+ start"); synchronous and async callbacks are supported. A handler returns
457
855
  what `agent.invoke` takes: a nonblank string (one user message), a message such
458
856
  as `new HumanMessage(...)` with text or content blocks, state such as
459
857
  `{ messages: [...], todos: [] }`, or `[input, { context, metadata }]` to also
460
858
  set the run's context (`context.channel` is reserved) or metadata, such as
461
859
  `{ issue: "ENG-123" }` to find the run by in LangSmith. Metadata keys are
462
- letters, digits, `_`, `.` or `-`; `mda_` and `langgraph_` keys are reserved.
860
+ letters, digits, `_`, `.` or `-`. Keys the Agent Server and LangSmith set
861
+ are reserved: `mda_`, `langgraph_` and `ls_` keys, and ids such as
862
+ `thread_id`, `session_id`, `run_id` and `user_id`.
863
+ `webhook: url` has the Agent Server POST the finished run (its status,
864
+ metadata, values and any error) to `url`, as `runs.create({ webhook })` does;
865
+ http(s), without credentials in the URL.
463
866
  The handler's second argument is the conversation's `thread`: `thread.id` is
464
867
  the thread the run starts on, for context that names it, such as a token your
465
868
  backend signs for the thread.
@@ -491,6 +894,36 @@ The resumed run uses no user connections, even though a person answered: the
491
894
  conversation holds content from outside the workspace, which must not act with
492
895
  the grants of whoever resumes it from Studio.
493
896
 
897
+ To acknowledge an event the moment it arrives, such as with a 👀 reaction,
898
+ pass `onReceived(event, thread)`. It hears each delivery of a handled event once,
899
+ with the parsed event and its thread, as soon as the delivery reaches its
900
+ thread: before MDA reads the thread or asks the handler, which under the
901
+ default `enqueue` waits until a busy conversation's run has finished. It
902
+ fires even when the thread then waits on an interrupt or the handler declines,
903
+ so apply your own filters there, such as checking `event.mentions`. It runs in the
904
+ background, before `onFinish`; a failure is logged and doesn't stop the run.
905
+
906
+ ```ts
907
+ import { Octokit } from "@octokit/rest";
908
+ import { channels, connections } from "managed-deepagents";
909
+
910
+ export const channel = channels.github({
911
+ onEvent: { "issue_comment.created": (event) => answerMentions(event) },
912
+ onReceived: async (event) => {
913
+ if (event.key !== "issue_comment.created") return;
914
+ if (!event.mentions.includes("@my-agent")) return;
915
+ const auth = await connections.get("mda/github-token", { type: "agent" });
916
+ await new Octokit({ auth }).rest.reactions.createForIssueComment({
917
+ owner: event.repository.owner,
918
+ repo: event.repository.name,
919
+ comment_id: event.payload.comment.id,
920
+ content: "eyes",
921
+ request: { signal: AbortSignal.timeout(10_000) },
922
+ });
923
+ },
924
+ });
925
+ ```
926
+
494
927
  Keep callbacks short: their work and run creation must fit within GitHub's webhook
495
928
  response window. A handler for an event that waits for a busy conversation runs
496
929
  once that run has finished, after the webhook was acknowledged. The request does not wait for agent completion. Run-start
@@ -500,17 +933,68 @@ Callbacks receive a normalized event and its `thread` (`thread.id`,
500
933
  `thread.channel.name`, and `thread.interrupted` while it waits on an interrupt). The event includes
501
934
  the original payload, delivery and installation IDs, repository and sender.
502
935
  Types describe handwritten subsets, not the complete GitHub schema. Run code can
503
- inspect the normalized event through `runtime.channel.rawEvent`. Logs correlate the GitHub
936
+ inspect the normalized event through `runtime.thread.origin.channel.rawEvent`. Logs correlate the GitHub
504
937
  delivery ID to the fresh thread and run IDs without printing the payload.
505
938
  `GitHub handler returned run input` records the callback decision;
506
939
  `channel run started` confirms run creation.
507
940
 
508
941
  Each issue or pull request's thread belongs to that GitHub conversation, whoever
509
- starts its runs. User connections,
510
- `{messages}` results and automatic GitHub replies are not supported. To act on
942
+ starts its runs. User connections and automatic GitHub replies are not
943
+ supported. To act on
511
944
  GitHub from a run (comment, label, open pull requests), give the agent a GitHub
512
945
  MCP server in `tools/mcp.ts`.
513
946
 
947
+ ### Setting up the GitHub App
948
+
949
+ The first `mda deploy` of a GitHub channel asks which GitHub App it uses, as
950
+ the Slack and Linear channels do:
951
+
952
+ - **Create a new GitHub App**: deploy asks which account should own it, an
953
+ organization or your personal account, then opens GitHub with the App filled
954
+ in and named after the agent. Create it, then install it on the repositories
955
+ the agent may reach.
956
+ - **Use an existing GitHub App**: enter its App ID, private key file and
957
+ webhook secret. Deploy records the installation GitHub reports. It asks you
958
+ to pick one when the App is installed in several accounts, opens the install
959
+ page when it's installed nowhere, and asks for the installation ID only when
960
+ GitHub can't be checked.
961
+
962
+ Agent Auth keeps the App's key, webhook secret and bot ID, so nothing goes into
963
+ `.env`, and later deploys keep the App without asking. An App has one webhook
964
+ URL: if an existing App's webhook points somewhere else, deploy asks before
965
+ moving it to the deployment. Answer "No" to keep it there, for example when
966
+ another service receives the App's events and forwards the ones for this agent
967
+ to `<deployment-url>/channels/github/events` with the body and GitHub's headers
968
+ unchanged. Deploy remembers that URL and doesn't ask again, with or without a
969
+ terminal, until the App's webhook moves. If the App's installation lacks a
970
+ permission the channel needs, deploy names it.
971
+
972
+ To skip the questions, with or without a terminal, set these in the shell or
973
+ `.env`:
974
+
975
+ - `MDA_GITHUB_APP_OWNER`: create a new App owned by this organization, or by
976
+ `@me`, the personal account of whoever creates it.
977
+ - `MDA_GITHUB_APP_ID`, `MDA_GITHUB_APP_PRIVATE_KEY_FILE` (the path to its
978
+ `.pem` file) and `MDA_GITHUB_WEBHOOK_SECRET`: use this existing App instead.
979
+ Add `MDA_GITHUB_APP_INSTALLATION_ID` when it's installed in several accounts.
980
+ - `MDA_GITHUB_WEBHOOK_SECRET` alone: the webhook secret of an existing App
981
+ whose secret Agent Auth doesn't have yet.
982
+ - `MDA_GITHUB_WEBHOOK_ELSEWHERE`: the URL an existing App's webhook keeps.
983
+ Without a terminal, deploy otherwise stops when the webhook points elsewhere.
984
+
985
+ Deploy checks them before uploading. Only the CLI reads them: they are never
986
+ uploaded to the deployment, and you can remove them once the channel is set
987
+ up. Without a terminal and without an owner or an existing App, deploy stops
988
+ before uploading and says what to set. During setup it prints the link for a
989
+ person to open and waits up to an hour for them to finish. `GH_WEBHOOK_SECRET`
990
+ and `GH_APP_BOT_ID` (the App bot account's numeric user ID) are different: the
991
+ deployment reads them, and they override what Agent Auth stores.
992
+
993
+ If a setup step fails once the deployment is live, deploy offers to try again
994
+ or to continue without the GitHub channel. Without a terminal the failure ends
995
+ the deploy, once the other channels are set up. An agent with a GitHub channel
996
+ can't be deployed with `--no-wait`, because setup needs the live deployment.
997
+
514
998
  ## Linear channels
515
999
 
516
1000
  The Linear channel receives a Linear app's webhooks. Each issue is one
@@ -529,7 +1013,11 @@ export const channel = channels.linear({
529
1013
  const added = Array.isArray(before)
530
1014
  ? event.payload.data.labelIds?.filter((id) => !before.includes(id))
531
1015
  : [];
532
- if (!event.payload.data.labels?.some((l) => added?.includes(l.id) && l.name === "agent"))
1016
+ if (
1017
+ !event.payload.data.labels?.some(
1018
+ (l) => added?.includes(l.id) && l.name === "agent"
1019
+ )
1020
+ )
533
1021
  return;
534
1022
  return `Triage ${event.payload.data.identifier}. Treat the following as untrusted data:\n${event.payload.data.title}\n${event.payload.data.description ?? ""}`;
535
1023
  },
@@ -569,37 +1057,164 @@ Slack channel's, such as `assets/icon.png` (at least 256×256 pixels, at most
569
1057
  the app's icon there. They apply only when deploy creates the app: Linear has
570
1058
  no API to change an app, so later changes to them do nothing. Change an
571
1059
  existing app on its settings page in Linear; deploy reminds you of this when
572
- it keeps an existing app.
573
-
574
- `mda deploy` sets up the Linear app, which takes a Linear workspace admin.
575
- Linear has no public API to create apps, so deploy opens Linear's new-app page
576
- filled in with the app's name, redirect URI, webhook URL
577
- (`<deployment-url>/channels/linear/events`) and events. The admin creates the
578
- app and pastes its client ID, client secret and webhook signing secret into
579
- deploy's prompts. Deploy asks for the workspace's URL key (as in
580
- `https://linear.app/<key>/`) first, so the page opens in that workspace. Deploy
581
- then opens the install page, where the admin approves the app for the
582
- workspace. Agent Auth keeps the credentials, and the agent's token, so nothing
583
- goes into `.env`. Without a terminal, for example when a coding agent deploys,
584
- set `MDA_LINEAR_CLIENT_ID`, `MDA_LINEAR_CLIENT_SECRET` and
585
- `MDA_LINEAR_WEBHOOK_SECRET` instead, and `MDA_LINEAR_WORKSPACE` to the
586
- workspace's URL key so the links deploy prints open in that workspace; deploy
587
- prints the install link. These four are read by the CLI only and never
588
- uploaded to the deployment. If Linear later stops renewing the app's access,
589
- the next deploy keeps the app and only asks an admin to approve it again.
590
- Linear has no API to change an app's webhook URL either, so if the deployment
591
- moves, update it on the app's page. `mda delete` removes the credentials;
592
- delete the app itself in Linear.
1060
+ it installs the app.
1061
+
1062
+ `mda deploy` sets up the Linear app, which takes a Linear workspace admin; see
1063
+ [Setting up the Linear app](#setting-up-the-linear-app).
593
1064
 
594
1065
  The channel reads the webhook signing secret from Agent Auth, or from
595
- `LINEAR_WEBHOOK_SECRET` when set. It acts as the app through the agent OAuth
596
- connection `mda/linear-app/<deployment-id>`, installed with `actor=app`; read
597
- its token with `await connections.get("mda/linear-token", { type: "agent" })` to call Linear's API from tools.
1066
+ `LINEAR_WEBHOOK_SECRET` when set. It acts as the app through the deployment's
1067
+ Agent Auth app, whose OAuth connection is `mda/linear-app/<deployment-id>`,
1068
+ installed with `actor=app`; read its token with
1069
+ `await connections.get("mda/linear-token", { type: "agent" })` to call Linear's API from tools.
1070
+
1071
+ `reply: "none"` leaves the run's answer to the agent: the channel doesn't post
1072
+ it as a comment or in the session. Session acknowledgements, Stop and error
1073
+ notices still post.
598
1074
 
599
1075
  [`examples/linear`](../../examples/linear) triages issues in agent sessions,
600
1076
  shows its steps and plan there, and waits for approval in LangSmith Studio
601
1077
  before moving an issue to another state.
602
1078
 
1079
+ ### Setting up the Linear app
1080
+
1081
+ The first `mda deploy` of a Linear channel asks which Linear app it uses, as
1082
+ the Slack and GitHub channels do. Setting it up takes a Linear workspace admin:
1083
+
1084
+ - **Create a new Linear app**: Linear has no public API to create apps, so
1085
+ deploy asks for the workspace's URL key (as in `https://linear.app/<key>/`)
1086
+ and opens Linear's new-app page in that workspace, filled in with the app's
1087
+ name, redirect URI, webhook URL (`<deployment-url>/channels/linear/events`)
1088
+ and events. The admin creates the app and pastes its client ID, client
1089
+ secret and webhook signing secret into deploy's prompts.
1090
+ - **Use an existing Linear app**: deploy lists the redirect URI, webhook URL
1091
+ and events to set on the app's settings page, then asks for the same three
1092
+ values. Linear sends an app's webhooks to one URL, so whatever received them
1093
+ before stops receiving them.
1094
+
1095
+ Deploy then opens the install page, where the admin approves the app for the
1096
+ workspace. Agent Auth keeps the credentials and the agent's token, so nothing
1097
+ goes into `.env`, and later deploys keep the app without asking. If Linear
1098
+ stops renewing the app's access, the next deploy keeps the app and only asks
1099
+ an admin to approve it again. Linear has no API to change an app, so when the
1100
+ channel's `app` settings change or the deployment moves to another URL, update
1101
+ the app on its settings page.
1102
+
1103
+ To skip the questions, with or without a terminal, set
1104
+ `MDA_LINEAR_CLIENT_ID`, `MDA_LINEAR_CLIENT_SECRET` and
1105
+ `MDA_LINEAR_WEBHOOK_SECRET` in the shell or `.env`, and `MDA_LINEAR_WORKSPACE`
1106
+ to the workspace's URL key so the links deploy prints open in that workspace.
1107
+ `MDA_LINEAR_WEBHOOK_SECRET` alone supplies the signing secret of an installed
1108
+ app whose secret Agent Auth doesn't have yet. Deploy checks them before
1109
+ uploading, and asks Linear whether the client ID and secret belong together.
1110
+ Only the CLI reads them: they are never uploaded to the deployment, and you can
1111
+ remove them once the channel is set up. Without a terminal and without them,
1112
+ deploy stops before uploading with the link to create the app.
1113
+
1114
+ An app from these variables wasn't made from deploy's filled-in page, and
1115
+ Linear has no API that says what it has set. So before the install link,
1116
+ deploy lists the redirect URI, webhook URL and events the app needs, the same
1117
+ as for an existing app. In a terminal it waits until you say they're set;
1118
+ without one it prints them before the install link for an admin to open.
1119
+ Without the redirect URI, Linear answers the install with "Invalid
1120
+ redirect_uri".
1121
+
1122
+ If a setup step fails once the deployment is live, deploy offers to try again
1123
+ or to continue without the Linear channel. Without a terminal the failure ends
1124
+ the deploy, once the other channels are set up. An agent with a Linear channel
1125
+ can't be deployed with `--no-wait`, because setup needs the live deployment.
1126
+ `mda delete` removes the credentials; delete the app itself in Linear.
1127
+ Deployments set up by an earlier `mda` keep the app as workspace connections,
1128
+ which only a LangSmith workspace admin can delete.
1129
+
1130
+ ## Mapping channel senders to your users
1131
+
1132
+ Each channel gives a person its own principal: a Slack user, a GitHub sender
1133
+ and a Linear user are three different callers. When your app already keeps one
1134
+ users table, `resolveUser` maps every verified sender to one of your users:
1135
+
1136
+ ```ts
1137
+ // identity.ts
1138
+ export const identity = defineIdentity({
1139
+ auth: "backend",
1140
+ resolveUser: async ({ actor }) => {
1141
+ if (!actor) return { type: "ignore" };
1142
+ const user = await db.userForIdentity(actor.provider, actor.id);
1143
+ return user
1144
+ ? { type: "user", id: user.id }
1145
+ : {
1146
+ type: "reply",
1147
+ message: {
1148
+ type: "content",
1149
+ content: "Link your account at https://app.example.com/link first.",
1150
+ },
1151
+ };
1152
+ },
1153
+ });
1154
+ ```
1155
+
1156
+ MDA calls it before every run or resume a channel starts, with the verified
1157
+ `actor` and the channel's name. `actor` is absent when the channel could not
1158
+ verify who sent the delivery, such as a Slack actor that failed its check or a
1159
+ Linear event that names no one. It returns:
1160
+
1161
+ - `{ type: "user", id }`: run as one of your users, with the principal a
1162
+ backend call with `auth: "backend"` gets for that `id`. Channels and your
1163
+ backend then share connections and memory.
1164
+ - `{ type: "identity", provider, issuer, subject }`: run as an exact Agent
1165
+ Auth identity, such as the IdP `sub` your web users sign in with.
1166
+ - `{ type: "reply", message }`: start nothing, and post `message` where the
1167
+ event came from, as `runtime.channel.post` does, but only when the event
1168
+ would start or resume a run. `message` is a `ChannelMessage`. Slack and Linear send text only. A GitHub channel can't
1169
+ post, so it only starts nothing.
1170
+ - `{ type: "ignore" }`: start nothing. This also stops a stranger from
1171
+ answering an interrupt.
1172
+ - `null` or `undefined`: the channel default. The sender runs as their own
1173
+ Agent Auth principal: a Slack user's for `(slack, <install team>, <user>)`,
1174
+ which Trigger Server resolves; a GitHub user's for
1175
+ `(github, github.com, <id>)`; a Linear user's for
1176
+ `(linear, linear.app, <id>)`. Bots, apps and a sender the channel could not
1177
+ verify run as the channel. A GitHub or Linear sender gets no user
1178
+ connections this way, since a sign-in link would be public; map them to one
1179
+ of your users for that.
1180
+
1181
+ A channel's own `resolveUser`, such as `channels.github({ resolveUser })`,
1182
+ overrides the identity's. If `resolveUser` throws, MDA starts nothing. Key on
1183
+ `provider` and `id` (and `teamId` for Slack), never on `name` or `email`.
1184
+
1185
+ Your app links accounts: a `reply` can point to a page where the person signs
1186
+ in and connects their Slack or GitHub account. The reply can be public, so its
1187
+ link must not carry anything that proves who the sender is.
1188
+
1189
+ When your users table lives in another service, `resolveUser` can be that
1190
+ service's endpoint instead of a function: a URL, or a `Request` that also
1191
+ carries headers, as an `onEvent` endpoint can be (see
1192
+ [Asking your service what to start](#asking-your-service-what-to-start)).
1193
+
1194
+ ```ts
1195
+ export const identity = defineIdentity({
1196
+ auth: "backend",
1197
+ resolveUser: new Request(`${process.env.BACKEND_URL}/mda/resolve-user`, {
1198
+ headers: { "x-api-key": process.env.BACKEND_KEY! },
1199
+ }),
1200
+ });
1201
+ ```
1202
+
1203
+ MDA POSTs the `ResolveUserRequest` a function gets,
1204
+ `{ actor?, channel: { name } }`, as JSON, signed with `MDA_SERVICE_SECRET` as an
1205
+ `onEvent` endpoint's request is, with the `webhook-id`
1206
+ `msg_<deliveryId>_resolve_user`. The service
1207
+ answers with what a function returns, checked the same way; `null` or HTTP 204
1208
+ keeps the channel's own principal. The delivery waits on the answer, so the
1209
+ service gets 5 seconds in all, and one more attempt after a network error,
1210
+ HTTP 429 or a 5xx. Any other failure starts nothing, as a function that throws
1211
+ does. A bad URL or secret throws when the identity or channel is declared.
1212
+
1213
+ Answer every `_resolve_user` request, even one whose `webhook-id` you have
1214
+ seen. The attempt after a 5xx and a redelivery from Trigger Server both keep
1215
+ the same ID. A receiver that dedupes on the ID and answers 204 to a repeat runs
1216
+ that delivery as the channel's own principal instead of your user.
1217
+
603
1218
  ## HTTP channels
604
1219
 
605
1220
  Use `channels.http({ provider, verify, parse, post })` for a provider webhook.
@@ -612,21 +1227,22 @@ A message returned by `parse` contains `userId`, `threadId`, `content`, and `tar
612
1227
  `threadId` is the stable LangGraph thread UUID. `target` is separate JSON that the
613
1228
  adapter needs to reply, such as a Photon conversation ID or a Slack channel and
614
1229
  thread timestamp. MDA keeps this target private and binds it to
615
- `runtime.channel.post`. The adapter's `post` callback receives the target for
1230
+ `runtime.thread.origin.channel.post`. The adapter's `post` callback receives the target for
616
1231
  reply routing.
617
1232
 
618
1233
  `verify` runs first with the original bytes and headers. `verify` and `parse`
619
1234
  each get a separately readable request body. `parse` owns body decoding and can
620
1235
  read JSON, form data, text, or bytes. It returns the existing `message` or `ignore`
621
- result. MDA creates `runtime.channel.event` from the parsed message.
1236
+ result. MDA creates `runtime.thread.origin.channel.event` from the parsed message.
622
1237
 
623
1238
  To expose provider data, return
624
1239
  `{ type: "message", message, rawEvent: event }` from `parse`. MDA validates this
625
- value as finite, acyclic JSON and copies it into `runtime.channel.rawEvent`.
1240
+ value as finite, acyclic JSON and copies it into
1241
+ `runtime.thread.origin.channel.rawEvent`, in the run that message starts.
626
1242
  If you omit `rawEvent`, it stays absent. MDA does not decode the body again.
627
1243
  An explicit `null` is a valid event value.
628
1244
 
629
- HTTP and Slack Trigger runs expose delivery through `runtime.channel`.
1245
+ HTTP and Slack Trigger runs expose delivery through `runtime.thread.origin.channel`.
630
1246
 
631
1247
  The optional method has this signature:
632
1248
 
@@ -651,68 +1267,162 @@ credentials when it calls the provider API. Native routing or authentication
651
1267
  fields must not replace them.
652
1268
 
653
1269
  Automatic agent replies use the `content` form. An explicit
654
- `runtime.channel.post({ type: "native", native: ... })` reaches your adapter
1270
+ `runtime.thread.origin.channel.post({ type: "native", native: ... })` reaches your adapter
655
1271
  without conversion. Automatic replies run after the request is accepted. A failed
656
- `post` is recorded as a delivery error.
1272
+ `post` is recorded as a delivery error. `reply: "none"` turns automatic
1273
+ replies off, so the agent replies itself; `post` still serves
1274
+ `runtime.channel.post` and error notices.
657
1275
  If `post` is omitted, the channel can receive events and exposes facts, but
658
1276
  has no runtime `post` method. The webhook path is `/channels/<name>/events`, where
659
1277
  `<name>` is the channel file name. An existing Photon webhook needs no URL change
660
1278
  when the deployment host and channel name stay the same.
661
1279
 
662
- If a run or its observation fails, MDA uses `post` to send “Something went wrong
1280
+ If a run fails, MDA uses `post` to send “Something went wrong
663
1281
  while processing your request.” A failed send is logged without another attempt.
664
1282
  Optional async `onError(error, target)` replaces this default. MDA supplies the
665
1283
  reply target; pass it to your adapter's `post` to send a custom error message.
666
1284
 
667
- Optional async hooks tell the adapter what happened to a message:
1285
+ Two optional async hooks follow each message through its lifecycle:
668
1286
 
669
- - `onDelivered(target, run)` runs for every verified message that reaches its
670
- thread, before MDA starts or queues anything for it, for providers that
1287
+ - `onReceived(target, delivery)` runs for every verified message that reaches
1288
+ its thread, before MDA starts or queues anything for it, for providers that
671
1289
  expect a prompt acknowledgement.
672
- - `onInterrupted(target, run, reason)` runs when the thread waits on a decision.
673
- `reason` is `"waiting"` when a message arrives while it waits, which starts
674
- nothing, and `"stopped"` right after the run a message started stops on
675
- interrupts. `interruptedReasons` lists the reasons it hears, only `["waiting"]`
676
- by default. Point the sender to Studio, where the interrupts can be resumed.
677
- - A `parse` result of `{ type: "cancel", message: { userId, threadId, target } }`
678
- cancels the thread's queued and running runs, keeping their progress, and
679
- drops messages waiting for the thread. `onCancelled(target, { ...run, cancelled })`
680
- then reports how many runs were active.
681
- - A run that fails calls `onError`, or `post`s an apology. A run that answers
682
- posts its own answer.
683
-
684
- Each runs in the background; failures are logged.
685
-
686
- ### Forwarding deliveries
687
-
688
- Any HTTP channel can send each verified delivery on to other services, as the
689
- provider sent it, besides acting on it here. `forward(request, delivery)`
690
- gets a copy of the delivery, with its raw body and headers (the provider's
691
- signature and delivery ID included, the platform's own headers removed), and
692
- returns a request to send, such as `new Request(url, request)`, several, or
693
- nothing. The receiver can check the provider's signature itself.
694
-
695
- GitHub and Linear take the same `forward`, with their parsed event as its
696
- second argument, and `events`: more event keys to receive without a handler,
697
- only to forward them. Deploy subscribes the app to them too.
1290
+ - `onFinish(target, { ...delivery, type, ..., interrupts })` runs once each
1291
+ message is done, after `onReceived` and after `onError`, and says how by
1292
+ `type`:
1293
+ - `run`: the run it started ended, as `run: { id, status }` with
1294
+ LangGraph's run status: `success`, `error`, `timeout` or `interrupted` (it
1295
+ stopped on interrupts, or was cancelled).
1296
+ - `skipped`: it started no run, and `reason` says why: `cancel`, `waiting`
1297
+ (the thread waits on a decision), `ignored` (its `input` returned
1298
+ nothing), `duplicate` (a redelivery; the first delivery reports the run),
1299
+ `rejected` (a busy thread and `multitaskStrategy: "reject"`) or `dropped`
1300
+ (a cancel dropped it while it waited).
1301
+ - `failed`: MDA couldn't start its run after the provider got its answer
1302
+ (`reason: "start"`, final); couldn't start it and answered the provider with an
1303
+ error (`retry`), so it may redeliver it as a new delivery, which reports
1304
+ its own outcome; or lost track of the run (`observe`), which may still
1305
+ answer.
1306
+
1307
+ `interrupts` lists the decisions the thread waits on, as LangGraph's
1308
+ `thread.interrupts` does: when it isn't empty, point the sender to Studio,
1309
+ where they can be resumed.
1310
+
1311
+ A `parse` result of `{ type: "cancel", message: { userId, threadId, target } }`
1312
+ cancels the thread's queued and running runs, keeping their progress, and
1313
+ drops messages waiting for the thread. It finishes at once, as `skipped` for
1314
+ `cancel`, with what the thread still waits on.
1315
+
1316
+ ```ts
1317
+ onFinish: async (target, finished) => {
1318
+ if (finished.type === "run" && finished.run.status === "success")
1319
+ await markDone(target);
1320
+ else if (finished.interrupts.length)
1321
+ await notify(target, `A decision is pending: ${studioLink(finished.threadId)}`);
1322
+ },
1323
+ ```
1324
+
1325
+ The hooks run in the background, in the process that took the delivery;
1326
+ failures are logged. A restart while a run is in flight loses its
1327
+ `onFinish`, and a run Studio resumes has no delivery, so it gets none: keep a
1328
+ reconciliation, or the run's `webhook` (see handlers' run options), for anything
1329
+ that must not miss one. The run posts its own
1330
+ answer.
1331
+
1332
+ GitHub and Linear take `onFinish(event, thread, finished)`, `finished` as
1333
+ above without the delivery's own fields, with
1334
+ their parsed event and the thread its handler saw, called after their own
1335
+ notices, such as the comment or the session elicitation that a decision is
1336
+ pending, or Linear's "Stopped.".
1337
+
1338
+ ### Asking your service what to start
1339
+
1340
+ When the logic that decides a run lives in a service you already run, an
1341
+ `onEvent` handler can be that service's endpoint instead of a function: a URL,
1342
+ or a `Request` that also carries headers, such as a credential the service
1343
+ checks. When the event's thread takes a turn, MDA POSTs
1344
+ `{ event: { key, deliveryId, payload }, thread: { id, channel, interrupted? } }`
1345
+ as JSON to it, with the `Request`'s headers (never logged), and starts the run
1346
+ the service answers with: `{ input, context?, metadata?, webhook? }`, checked as
1347
+ a handler's result. `null` or HTTP 204 starts nothing. A GitHub event also
1348
+ carries what a handler gets beside the payload: its `action`, `repository`,
1349
+ `sender` and `mentions`, so a service checks them as a handler does. These are
1350
+ optional in `GitHubEndpointRequest`, so a receiver takes requests from older
1351
+ deployments. A Slack message carries its `text`, `userId`, `channelId`,
1352
+ `messageTs`, `threadTs` and `files` the same way, with Slack's own event as
1353
+ `payload`.
698
1354
 
699
1355
  ```ts
1356
+ const backend = new Request(`${process.env.BACKEND_URL}/linear/runs`, {
1357
+ headers: { "x-api-key": process.env.BACKEND_KEY! },
1358
+ });
1359
+
700
1360
  export const channel = channels.linear({
701
- onEvent: { "AgentSessionEvent.created": startRun },
702
- events: ["Issue.create", "Issue.update", "Comment.create"],
703
- forward: (request, event) =>
704
- event.type === "AgentSessionEvent"
705
- ? null
706
- : new Request(`${process.env.BACKEND_URL}/webhooks/linear`, request),
1361
+ onEvent: {
1362
+ "AgentSessionEvent.created": backend,
1363
+ "AgentSessionEvent.prompted": backend,
1364
+ "Issue.update": backend,
1365
+ "Comment.create": (event) => `Summarize ${event.payload.data.body}`,
1366
+ },
707
1367
  });
708
1368
  ```
709
1369
 
710
- A delivery is forwarded once the channel's filters (teams or repositories,
711
- other bots, the app's own activity) keep it. MDA sends the requests in the
712
- background, at most 10 per delivery, each within 10 seconds and without
713
- following redirects, and logs failures. The provider's answer never waits for
714
- them, so forwarding is best effort: a receiver should dedupe by the
715
- provider's delivery ID.
1370
+ GitHub, Linear or Trigger Server gets its answer first, and MDA asks the
1371
+ service in the background, so a slow service doesn't time out the delivery. A network error
1372
+ before the answer, HTTP 429 or a 5xx is tried again, up to three attempts with
1373
+ the same `webhook-id`. Another error status, a redirect, an answer cut off or
1374
+ that isn't one of these, more than 10 MB, or no answer within 60 seconds fails the handler, as a
1375
+ handler that throws does: Linear tells the session. The URL must be https, or
1376
+ plain http to a loopback host such as `localhost` for local development, and
1377
+ carry no credentials; a bad one throws when the channel is declared.
1378
+
1379
+ To let the service check that a request came from your deployment and wasn't
1380
+ changed or replayed, set `MDA_SERVICE_SECRET` in the project's `.env`;
1381
+ `mda deploy` forwards it to the deployment. MDA then signs every endpoint
1382
+ request as [Standard Webhooks](https://www.standardwebhooks.com/) does:
1383
+ `webhook-id`, `webhook-timestamp` and `webhook-signature`, an HMAC-SHA256 of the
1384
+ id, the timestamp and the exact body. The id is `msg_<deliveryId>` for the call
1385
+ that starts the run, `msg_<deliveryId>_interrupted` for a GitHub call that
1386
+ asks whether to tell the conversation its thread waits on a decision, and
1387
+ `msg_<deliveryId>_resolve_user` for a `resolveUser` call
1388
+ (see [Mapping channel senders to your users](#mapping-channel-senders-to-your-users)). The secret is `whsec_` and padded base64
1389
+ of 24 to 64 random bytes (`echo "whsec_$(openssl rand -base64 32)"`; an
1390
+ `openssl rand -hex 32` value is valid too); several, separated by commas, each
1391
+ sign the request, for rotation. A malformed secret throws when the channel is declared.
1392
+
1393
+ The service keeps the same secret and verifies with any Standard Webhooks
1394
+ library, which also rejects timestamps older than five minutes:
1395
+
1396
+ ```ts
1397
+ import { Webhook } from "standardwebhooks";
1398
+
1399
+ const event = new Webhook(process.env.ENDPOINT_SECRET!).verify(
1400
+ rawBody,
1401
+ Object.fromEntries(request.headers)
1402
+ );
1403
+ ```
1404
+
1405
+ On the service's side, `LinearEndpointRequest<K>`,
1406
+ `GitHubEndpointRequest<K>` and `SlackEndpointRequest<K>` type what the channel
1407
+ sends, `{ event: { key,
1408
+ deliveryId, payload }, thread }`, with the payload typed by the event key, and
1409
+ `EndpointAnswer` types an answer, all JSON; answer `null` to start nothing. The
1410
+ request types are types only: validate the body as your service validates
1411
+ input, after verifying its signature, or the credential your endpoint's `Request`
1412
+ carries.
1413
+
1414
+ ```ts
1415
+ import type { EndpointAnswer, LinearEndpointRequest } from "managed-deepagents";
1416
+
1417
+ app.post("/linear/runs", (req, res) => {
1418
+ const { event, thread } = req.body as LinearEndpointRequest;
1419
+ const answer: EndpointAnswer | null =
1420
+ event.key === "AgentSessionEvent.created"
1421
+ ? { input: `Plan ${event.payload.agentSession.issue?.title}` }
1422
+ : null;
1423
+ res.json(answer);
1424
+ });
1425
+ ```
716
1426
 
717
1427
  ## Project Shape
718
1428
 
@@ -755,6 +1465,7 @@ export const schedule = defineSchedule({
755
1465
 
756
1466
  ```md
757
1467
  <!-- schedules/daily-digest.md -->
1468
+
758
1469
  Write the daily digest.
759
1470
  ```
760
1471
 
@@ -824,8 +1535,34 @@ await runtime.schedules!.create({
824
1535
  ```
825
1536
 
826
1537
  Set `newThread` to choose whether a run starts a new thread or continues the
827
- current thread. When a schedule is created from a channel such as Slack,
828
- each run posts its final answer to that channel. Runtime schedules stay when you deploy again.
1538
+ current thread. When a schedule is created on a channel's thread, such as a
1539
+ Slack thread, each run posts its final answer to that channel. A same-thread
1540
+ schedule posts wherever the thread's origin points when it runs, so its record
1541
+ has `channel: null` and `target: null`. A new-thread schedule keeps the
1542
+ conversation it was created in. On Slack, each run starts a new Slack thread
1543
+ there, with its answer or, when it waits on a decision, with a notice. Replies
1544
+ and decisions in that Slack thread continue the run's thread. Runtime schedules
1545
+ stay when you deploy again.
1546
+
1547
+ A user schedule can run as another person on the thread. Pass one of
1548
+ `runtime.thread.participants` as `runAs`; the caller still owns the schedule
1549
+ and is the one who can list, update, and delete it. Runs act as that person,
1550
+ with their connections.
1551
+
1552
+ ```ts
1553
+ const me = runtime.serverInfo?.principal?.id;
1554
+ const bob = runtime.thread!.participants.find(({ id }) => id !== me);
1555
+ await runtime.schedules!.create({
1556
+ owner: { type: "user" },
1557
+ cron: "0 9 * * 1",
1558
+ prompt: "Remind Bob about the weekly report.",
1559
+ newThread: false,
1560
+ runAs: bob,
1561
+ });
1562
+ ```
1563
+
1564
+ `runAs` needs `owner: { type: "user" }` and a participant with the same id and
1565
+ role; anything else is an error. The schedule's record shows it as `runAs`.
829
1566
 
830
1567
  Each owner can have at most 20 runtime schedules per agent, paused ones
831
1568
  included. Set `MDA_MAX_AGENT_SCHEDULES` or `MDA_MAX_USER_SCHEDULES` in the
@@ -865,7 +1602,10 @@ import type { ManagedToolRuntime } from "managed-deepagents";
865
1602
 
866
1603
  async function writeReport(runtime: ManagedToolRuntime) {
867
1604
  if (!runtime.backend) throw new Error("This tool requires a sandbox");
868
- const result = await runtime.backend.write("/workspace/report.txt", "Report ready");
1605
+ const result = await runtime.backend.write(
1606
+ "/workspace/report.txt",
1607
+ "Report ready"
1608
+ );
869
1609
  if (result.error) throw new Error(result.error);
870
1610
  }
871
1611
  ```
@@ -902,9 +1642,6 @@ server. In `mda dev`, an agent-owned connection reads from
902
1642
  slashes to double underscores (`mda/github-token` → `MDA_DEV_MDA__GITHUB_TOKEN`).
903
1643
  Hosted deployments resolve workspace connections from Agent Auth:
904
1644
 
905
- The old `connectors/mcp.ts` file API remains available with a warning during
906
- `0.7.x`. It will be removed in `0.8.0`.
907
-
908
1645
  ```ts
909
1646
  import { defineMcp, connections } from "managed-deepagents";
910
1647
 
@@ -996,10 +1733,10 @@ export const sandbox = defineSandbox({
996
1733
  });
997
1734
  ```
998
1735
 
999
- | Header value | Sent value |
1000
- | --- | --- |
1001
- | `ref` | Raw token |
1002
- | `bearer(ref)` | `Bearer <token>` |
1736
+ | Header value | Sent value |
1737
+ | ---------------------- | -------------------------------- |
1738
+ | `ref` | Raw token |
1739
+ | `bearer(ref)` | `Bearer <token>` |
1003
1740
  | `basic(username, ref)` | `Basic <base64(username:token)>` |
1004
1741
 
1005
1742
  Use `{ type: "agent" }` for agent credentials. Omit header `type` for
@@ -1044,10 +1781,13 @@ mda deploy ./my-agent
1044
1781
 
1045
1782
  The generated build is written to `<root>/.mda/build` by default.
1046
1783
 
1784
+ The deployment type defaults to `serverless`. Use `--deployment-type dedicated`
1785
+ for a dedicated deployment. Serverless requires a supported GCP SaaS environment.
1786
+
1047
1787
  Common deploy options:
1048
1788
 
1049
1789
  ```bash
1050
- mda deploy ./my-agent --name my-agent-dev --deployment-type dev
1790
+ mda deploy ./my-agent --name my-agent-dev --deployment-type serverless
1051
1791
  mda deploy ./my-agent --workspace-id "$LANGSMITH_WORKSPACE_ID"
1052
1792
  mda deploy ./my-agent --no-wait
1053
1793
  ```
@@ -1091,4 +1831,6 @@ workspace secrets; a value exported in your shell is not deployed. For
1091
1831
  LangSmith itself, set `LANGSMITH_API_KEY` in `.env` or your shell, or run
1092
1832
  interactively and press Enter at the prompt to sign in with your browser (the
1093
1833
  CLI creates a key and writes it to `.env`). TypeScript deploys also require
1094
- `npm` on `PATH` because the CLI generates a lockfile for the remote build.
1834
+ `npm` on `PATH` because the CLI generates a lockfile for the remote build;
1835
+ other lockfiles (`pnpm-lock.yaml`, `yarn.lock`, `bun.lock`) are left out of
1836
+ the build.