managed-deepagents 0.10.0-dev.17 → 0.10.0-dev.19

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 (257) hide show
  1. package/README.md +530 -83
  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 +43 -0
  7. package/dist/channels/actor.d.ts.map +1 -0
  8. package/dist/channels/actor.js +2 -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 +72 -1
  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 +36 -0
  19. package/dist/channels/delivery.d.ts.map +1 -0
  20. package/dist/channels/delivery.js +359 -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.map +1 -1
  27. package/dist/channels/github/index.js +21 -33
  28. package/dist/channels/github/index.js.map +1 -1
  29. package/dist/channels/github/schema.d.ts +50 -1
  30. package/dist/channels/github/schema.d.ts.map +1 -1
  31. package/dist/channels/github/schema.js +12 -0
  32. package/dist/channels/github/schema.js.map +1 -1
  33. package/dist/channels/http/endpoint.d.ts +6 -4
  34. package/dist/channels/http/endpoint.d.ts.map +1 -1
  35. package/dist/channels/http/endpoint.js.map +1 -1
  36. package/dist/channels/http/http.d.ts.map +1 -1
  37. package/dist/channels/http/http.js +4 -10
  38. package/dist/channels/http/http.js.map +1 -1
  39. package/dist/channels/http/index.d.ts +6 -0
  40. package/dist/channels/http/index.d.ts.map +1 -1
  41. package/dist/channels/http/index.js +32 -23
  42. package/dist/channels/http/index.js.map +1 -1
  43. package/dist/channels/http/ingress.d.ts +2 -2
  44. package/dist/channels/http/ingress.d.ts.map +1 -1
  45. package/dist/channels/http/ingress.js +9 -21
  46. package/dist/channels/http/ingress.js.map +1 -1
  47. package/dist/channels/http/provider.d.ts +10 -5
  48. package/dist/channels/http/provider.d.ts.map +1 -1
  49. package/dist/channels/http/provider.js +17 -10
  50. package/dist/channels/http/provider.js.map +1 -1
  51. package/dist/channels/http/studio.d.ts +5 -5
  52. package/dist/channels/http/studio.d.ts.map +1 -1
  53. package/dist/channels/http/studio.js +4 -6
  54. package/dist/channels/http/studio.js.map +1 -1
  55. package/dist/channels/http/types.d.ts +7 -1
  56. package/dist/channels/http/types.d.ts.map +1 -1
  57. package/dist/channels/linear/app-setup.d.ts +29 -0
  58. package/dist/channels/linear/app-setup.d.ts.map +1 -0
  59. package/dist/channels/linear/app-setup.js +102 -0
  60. package/dist/channels/linear/app-setup.js.map +1 -0
  61. package/dist/channels/linear/index.d.ts.map +1 -1
  62. package/dist/channels/linear/index.js +26 -65
  63. package/dist/channels/linear/index.js.map +1 -1
  64. package/dist/channels/linear/schema.d.ts +11 -25
  65. package/dist/channels/linear/schema.d.ts.map +1 -1
  66. package/dist/channels/linear/schema.js +6 -0
  67. package/dist/channels/linear/schema.js.map +1 -1
  68. package/dist/channels/linear/types.d.ts +9 -2
  69. package/dist/channels/linear/types.d.ts.map +1 -1
  70. package/dist/channels/messaging-registry.d.ts +16 -0
  71. package/dist/channels/messaging-registry.d.ts.map +1 -1
  72. package/dist/channels/messaging-registry.js +29 -0
  73. package/dist/channels/messaging-registry.js.map +1 -1
  74. package/dist/channels/reserved-connections.d.ts.map +1 -1
  75. package/dist/channels/reserved-connections.js +10 -0
  76. package/dist/channels/reserved-connections.js.map +1 -1
  77. package/dist/channels/run-input.d.ts +11 -0
  78. package/dist/channels/run-input.d.ts.map +1 -1
  79. package/dist/channels/run-input.js +18 -8
  80. package/dist/channels/run-input.js.map +1 -1
  81. package/dist/channels/runtime.d.ts +5 -2
  82. package/dist/channels/runtime.d.ts.map +1 -1
  83. package/dist/channels/runtime.js +19 -10
  84. package/dist/channels/runtime.js.map +1 -1
  85. package/dist/channels/schedule-delivery.d.ts +2 -13
  86. package/dist/channels/schedule-delivery.d.ts.map +1 -1
  87. package/dist/channels/schedule-delivery.js +6 -73
  88. package/dist/channels/schedule-delivery.js.map +1 -1
  89. package/dist/channels/slack/api.d.ts +2 -0
  90. package/dist/channels/slack/api.d.ts.map +1 -1
  91. package/dist/channels/slack/api.js +6 -0
  92. package/dist/channels/slack/api.js.map +1 -1
  93. package/dist/channels/slack/connections.d.ts +6 -0
  94. package/dist/channels/slack/connections.d.ts.map +1 -0
  95. package/dist/channels/slack/connections.js +6 -0
  96. package/dist/channels/slack/connections.js.map +1 -0
  97. package/dist/channels/slack/events.d.ts +272 -0
  98. package/dist/channels/slack/events.d.ts.map +1 -0
  99. package/dist/channels/slack/events.js +235 -0
  100. package/dist/channels/slack/events.js.map +1 -0
  101. package/dist/channels/slack/files.d.ts.map +1 -1
  102. package/dist/channels/slack/files.js +2 -3
  103. package/dist/channels/slack/files.js.map +1 -1
  104. package/dist/channels/slack/index.d.ts +44 -2
  105. package/dist/channels/slack/index.d.ts.map +1 -1
  106. package/dist/channels/slack/index.js +127 -3
  107. package/dist/channels/slack/index.js.map +1 -1
  108. package/dist/channels/slack/message-content.d.ts.map +1 -1
  109. package/dist/channels/slack/message-content.js +23 -28
  110. package/dist/channels/slack/message-content.js.map +1 -1
  111. package/dist/channels/slack/status.d.ts +76 -0
  112. package/dist/channels/slack/status.d.ts.map +1 -0
  113. package/dist/channels/slack/status.js +427 -0
  114. package/dist/channels/slack/status.js.map +1 -0
  115. package/dist/channels/trigger/client.d.ts.map +1 -1
  116. package/dist/channels/trigger/client.js +6 -9
  117. package/dist/channels/trigger/client.js.map +1 -1
  118. package/dist/channels/trigger/conversation.d.ts +11 -0
  119. package/dist/channels/trigger/conversation.d.ts.map +1 -0
  120. package/dist/channels/trigger/conversation.js +72 -0
  121. package/dist/channels/trigger/conversation.js.map +1 -0
  122. package/dist/channels/trigger/http.d.ts +123 -4
  123. package/dist/channels/trigger/http.d.ts.map +1 -1
  124. package/dist/channels/trigger/http.js +331 -76
  125. package/dist/channels/trigger/http.js.map +1 -1
  126. package/dist/channels/trigger/ingress.d.ts +70 -1
  127. package/dist/channels/trigger/ingress.d.ts.map +1 -1
  128. package/dist/channels/trigger/ingress.js +136 -7
  129. package/dist/channels/trigger/ingress.js.map +1 -1
  130. package/dist/channels/trigger/transport.d.ts +10 -0
  131. package/dist/channels/trigger/transport.d.ts.map +1 -1
  132. package/dist/channels/trigger/transport.js +25 -9
  133. package/dist/channels/trigger/transport.js.map +1 -1
  134. package/dist/channels/trigger/types.d.ts +92 -3
  135. package/dist/channels/trigger/types.d.ts.map +1 -1
  136. package/dist/channels/trigger/types.js +36 -0
  137. package/dist/channels/trigger/types.js.map +1 -1
  138. package/dist/channels/types.d.ts +43 -4
  139. package/dist/channels/types.d.ts.map +1 -1
  140. package/dist/channels/types.js +2 -0
  141. package/dist/channels/types.js.map +1 -1
  142. package/dist/define-deep-agent.d.ts +2 -2
  143. package/dist/define-deep-agent.d.ts.map +1 -1
  144. package/dist/define-deep-agent.js +53 -8
  145. package/dist/define-deep-agent.js.map +1 -1
  146. package/dist/index.d.ts +9 -4
  147. package/dist/index.d.ts.map +1 -1
  148. package/dist/index.js +2 -1
  149. package/dist/index.js.map +1 -1
  150. package/dist/ingress/runs.d.ts +22 -0
  151. package/dist/ingress/runs.d.ts.map +1 -1
  152. package/dist/ingress/runs.js +45 -1
  153. package/dist/ingress/runs.js.map +1 -1
  154. package/dist/managed-composition.d.ts +3 -0
  155. package/dist/managed-composition.d.ts.map +1 -1
  156. package/dist/managed-composition.js +29 -13
  157. package/dist/managed-composition.js.map +1 -1
  158. package/dist/namespaces.d.ts +0 -21
  159. package/dist/namespaces.d.ts.map +1 -1
  160. package/dist/namespaces.js +0 -29
  161. package/dist/namespaces.js.map +1 -1
  162. package/dist/runtime/async-subagents.d.ts +27 -0
  163. package/dist/runtime/async-subagents.d.ts.map +1 -0
  164. package/dist/runtime/async-subagents.js +139 -0
  165. package/dist/runtime/async-subagents.js.map +1 -0
  166. package/dist/runtime/auth.d.ts +1 -2
  167. package/dist/runtime/auth.d.ts.map +1 -1
  168. package/dist/runtime/auth.js +125 -63
  169. package/dist/runtime/auth.js.map +1 -1
  170. package/dist/runtime/backend.d.ts +7 -0
  171. package/dist/runtime/backend.d.ts.map +1 -1
  172. package/dist/runtime/backend.js +32 -7
  173. package/dist/runtime/backend.js.map +1 -1
  174. package/dist/runtime/browser-facade.d.ts +10 -0
  175. package/dist/runtime/browser-facade.d.ts.map +1 -0
  176. package/dist/runtime/browser-facade.js +23 -0
  177. package/dist/runtime/browser-facade.js.map +1 -0
  178. package/dist/runtime/browser-langsmith.d.ts +108 -0
  179. package/dist/runtime/browser-langsmith.d.ts.map +1 -0
  180. package/dist/runtime/browser-langsmith.js +379 -0
  181. package/dist/runtime/browser-langsmith.js.map +1 -0
  182. package/dist/runtime/browser-selection.d.ts +10 -0
  183. package/dist/runtime/browser-selection.d.ts.map +1 -0
  184. package/dist/runtime/browser-selection.js +21 -0
  185. package/dist/runtime/browser-selection.js.map +1 -0
  186. package/dist/runtime/browser.d.ts +125 -0
  187. package/dist/runtime/browser.d.ts.map +1 -0
  188. package/dist/runtime/browser.js +606 -0
  189. package/dist/runtime/browser.js.map +1 -0
  190. package/dist/runtime/identity-runtime.d.ts +4 -9
  191. package/dist/runtime/identity-runtime.d.ts.map +1 -1
  192. package/dist/runtime/identity-runtime.js +12 -25
  193. package/dist/runtime/identity-runtime.js.map +1 -1
  194. package/dist/runtime/index.d.ts +38 -1
  195. package/dist/runtime/index.d.ts.map +1 -1
  196. package/dist/runtime/index.js +74 -34
  197. package/dist/runtime/index.js.map +1 -1
  198. package/dist/runtime/managed-thread.d.ts +61 -0
  199. package/dist/runtime/managed-thread.d.ts.map +1 -0
  200. package/dist/runtime/managed-thread.js +76 -0
  201. package/dist/runtime/managed-thread.js.map +1 -0
  202. package/dist/runtime/managed-tools.d.ts +10 -2
  203. package/dist/runtime/managed-tools.d.ts.map +1 -1
  204. package/dist/runtime/managed-tools.js +10 -8
  205. package/dist/runtime/managed-tools.js.map +1 -1
  206. package/dist/runtime/memory/policy.d.ts.map +1 -1
  207. package/dist/runtime/memory/policy.js +1 -3
  208. package/dist/runtime/memory/policy.js.map +1 -1
  209. package/dist/runtime/parent-run.d.ts +33 -0
  210. package/dist/runtime/parent-run.d.ts.map +1 -0
  211. package/dist/runtime/parent-run.js +114 -0
  212. package/dist/runtime/parent-run.js.map +1 -0
  213. package/dist/runtime/run-input.d.ts +14 -1
  214. package/dist/runtime/run-input.d.ts.map +1 -1
  215. package/dist/runtime/run-input.js +75 -6
  216. package/dist/runtime/run-input.js.map +1 -1
  217. package/dist/runtime/sandbox-manager.d.ts +14 -1
  218. package/dist/runtime/sandbox-manager.d.ts.map +1 -1
  219. package/dist/runtime/sandbox-manager.js +34 -9
  220. package/dist/runtime/sandbox-manager.js.map +1 -1
  221. package/dist/runtime/schedule-auth.d.ts +79 -0
  222. package/dist/runtime/schedule-auth.d.ts.map +1 -1
  223. package/dist/runtime/schedule-auth.js +163 -6
  224. package/dist/runtime/schedule-auth.js.map +1 -1
  225. package/dist/runtime/static-entry.d.ts.map +1 -1
  226. package/dist/runtime/static-entry.js +1 -0
  227. package/dist/runtime/static-entry.js.map +1 -1
  228. package/dist/runtime/thread-record.d.ts +88 -0
  229. package/dist/runtime/thread-record.d.ts.map +1 -0
  230. package/dist/runtime/thread-record.js +213 -0
  231. package/dist/runtime/thread-record.js.map +1 -0
  232. package/dist/runtime/types.d.ts +12 -1
  233. package/dist/runtime/types.d.ts.map +1 -1
  234. package/dist/runtime-schedules.d.ts +13 -0
  235. package/dist/runtime-schedules.d.ts.map +1 -1
  236. package/dist/runtime-schedules.js +83 -20
  237. package/dist/runtime-schedules.js.map +1 -1
  238. package/dist/sandbox.d.ts +0 -6
  239. package/dist/sandbox.d.ts.map +1 -1
  240. package/dist/sandbox.js +3 -7
  241. package/dist/sandbox.js.map +1 -1
  242. package/dist/schedule-channel.d.ts +5 -2
  243. package/dist/schedule-channel.d.ts.map +1 -1
  244. package/dist/schedule-channel.js +13 -12
  245. package/dist/schedule-channel.js.map +1 -1
  246. package/dist/types.d.ts +19 -2
  247. package/dist/types.d.ts.map +1 -1
  248. package/dist/types.js.map +1 -1
  249. package/dist/values.d.ts +15 -0
  250. package/dist/values.d.ts.map +1 -0
  251. package/dist/values.js +31 -0
  252. package/dist/values.js.map +1 -0
  253. package/package.json +22 -8
  254. package/dist/channels/http/reply.d.ts +0 -20
  255. package/dist/channels/http/reply.d.ts.map +0 -1
  256. package/dist/channels/http/reply.js +0 -116
  257. package/dist/channels/http/reply.js.map +0 -1
package/README.md CHANGED
@@ -151,7 +151,7 @@ strings and arrays remain explicit selections. Select memory with `defineMemory(
151
151
  selected memory cannot use `allow`. Root `memory.ts` remains the fallback.
152
152
 
153
153
  On direct runs, `executionRuntime.context` is application context or `undefined`.
154
- 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`.
155
155
  `ManagedServerRuntime<AppContext>` preserves the application's context type.
156
156
  `accessContext` identifies the Agent Server operation that called the factory.
157
157
  During inspection, `executionRuntime` is `null`. Keep the factory repeatable and
@@ -243,12 +243,18 @@ current channel delivery:
243
243
  runtime
244
244
  .context // Application data on direct runs
245
245
  .serverInfo // Trusted caller and server information
246
- .channel // Present for a managed channel delivery
247
- .name // Configured channel name
248
- .provider // Provider label, such as slack
249
- .event // Typed MDA delivery; no internal routing
250
- .rawEvent // Optional original provider payload
251
- .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
252
258
  .backend // Present when a sandbox is configured
253
259
  .schedules // Create and manage schedules for this run's caller
254
260
  ```
@@ -265,8 +271,8 @@ await client.runs.create(threadId, assistantId, {
265
271
  Direct API runs use your authored `contextSchema` and native LangGraph context
266
272
  validation. On channel runs, tools and authored factories receive `undefined`
267
273
  as context. Native middleware receives an empty context. Use
268
- `runtime.channel.event` for the typed event and `runtime.channel.rawEvent` for
269
- 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
270
276
  required fields do not apply to channel runs. Supplying a `channel` field in a
271
277
  direct API call does not establish a trusted channel source.
272
278
 
@@ -277,28 +283,63 @@ in the graph factory for each run or resume. Direct callers of the managed
277
283
  compiler must build a fresh graph with the execution's context; a compiled graph
278
284
  is scoped to that execution.
279
285
 
280
- `runtime.channel` describes the current verified delivery. It is absent on
281
- ordinary API and schedule runs. Its facts are available even when the channel
282
- cannot send. `post` uses a private, verified reply target. The channel helper
283
- 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.
284
295
 
285
296
  ```ts
286
- if (runtime.channel?.post) {
287
- await runtime.channel.post({
297
+ const channel = runtime.thread?.origin?.channel;
298
+ if (channel?.post) {
299
+ await channel.post({
288
300
  type: "content",
289
301
  content: "I am checking that now.",
290
302
  });
291
303
  }
292
304
  ```
293
305
 
294
- 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 the Linear
309
+ channel, 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.
295
323
  It rejects native payloads and content it cannot send. For channels that support
296
324
  native messages, set `type` to `"native"` and supply the JSON in `native`.
297
325
  For content messages, set `type` to `"content"` and supply `content`.
298
326
  Supply only the matching payload. Native JSON has no extra provider or payload
299
- wrapper. The `runtime.channel` helper has no `address`, `update`, or destination override.
327
+ wrapper. The channel helper has no `address`, `update`, or destination override.
300
328
  A post sends an additional message; the normal final agent reply remains automatic.
301
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
+
302
343
  Managed Slack channels save incoming files under `/workspace/attachments/` and
303
344
  add `upload_files(paths)` so the agent can attach completed workspace files
304
345
  to its reply. This requires a managed sandbox.
@@ -318,7 +359,8 @@ MDA does not reuse the previous delivery's event.
318
359
  For a prompt that answers an interrupt, put a shared ID on the prompt and on the
319
360
  interrupt value under `INTERRUPT_CORRELATION_KEY`. MDA uses `correlation_id` to
320
361
  select that interrupt. Without a correlation ID, an answer can resume the only
321
- 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
322
364
  values are unchanged.
323
365
 
324
366
  The managed runtime fields are available to authored tools and middleware,
@@ -339,6 +381,312 @@ alias for the tool type. MDA adds `serverInfo`, `channel`, `backend`, and
339
381
  but the type marks it optional so annotated hooks still match LangChain's native
340
382
  runtime types.
341
383
 
384
+ ## Slack channels
385
+
386
+ `channels.slack(...)` receives Slack messages through Trigger Server. Each
387
+ Slack thread is one conversation, and the run's reply is posted in it. By
388
+ default every message that reaches the app starts a run with the message.
389
+ `onEvent` decides what each kind of message starts instead:
390
+
391
+ ```ts
392
+ // channels/slack.ts
393
+ import { channels } from "managed-deepagents";
394
+
395
+ export const channel = channels.slack({
396
+ name: "Support Bot",
397
+ triggerMode: "thread",
398
+ onEvent: {
399
+ mention: (event, thread) => [
400
+ event.text,
401
+ {
402
+ context: { ticket: thread.id },
403
+ metadata: { slack_channel: event.channelId },
404
+ },
405
+ ],
406
+ directMessage: (event) => event.text,
407
+ // Replies in a followed thread that don't mention the app start nothing.
408
+ message: () => null,
409
+ },
410
+ multitaskStrategy: "enqueue",
411
+ });
412
+ ```
413
+
414
+ The keys are `mention`, a channel message that @-mentions the app;
415
+ `directMessage`; and `message`, any other channel message, which only
416
+ `triggerMode` `"thread"` or `"all"` delivers. Under those modes Slack sends a
417
+ mention as a plain message, so the channel asks Slack once for the app's bot
418
+ user, with the app's bot token, to tell a mention from a message. An unknown
419
+ key throws. A message kind without a handler starts a run with the message,
420
+ as without `onEvent`. With `triggerMode: "mention"`, a `message` handler is
421
+ never called, and defining the channel warns.
422
+
423
+ With a `mention` handler under `triggerMode` `"thread"`, a reply only
424
+ continues a thread the agent already ran on: when the handler declines a
425
+ thread's mention, the thread's later replies start nothing until someone
426
+ mentions the agent there again.
427
+
428
+ A handler gets the message (`text`, `userId`, `channelId`, `messageTs`,
429
+ `threadTs`, `files`, Slack's own event as `payload`, and `actor`) and its
430
+ thread (`id`, `sourceThreadKey`, `channel.name`). It returns the run's input, as
431
+ `agent.invoke` takes it, optionally with `context`, `metadata` and `webhook`
432
+ as `[input, options]`, as the GitHub and Linear handlers do. `null` or
433
+ `undefined` starts no run, and the app neither reacts nor replies. A handler
434
+ that throws, or returns input the channel can't start, starts no run either;
435
+ the error is logged without its message. The message's blocks and attachments
436
+ are added to the handler's messages, and its files still reach
437
+ `/workspace/attachments/`. Interrupt resumes don't go through `onEvent`; a
438
+ click on a button the agent posted does, as `interaction`: see
439
+ [Buttons your agent posts](#buttons-your-agent-posts).
440
+
441
+ A handler may also be an endpoint of your service, a URL or a `Request`:
442
+ Trigger Server gets its answer first, then MDA asks the service what the
443
+ message starts. See [Asking your service what to start](#asking-your-service-what-to-start).
444
+
445
+ ```ts
446
+ const backend = new Request(`${process.env.BACKEND_URL}/slack/runs`, {
447
+ headers: { "x-api-key": process.env.BACKEND_KEY! },
448
+ });
449
+
450
+ export const channel = channels.slack({
451
+ onEvent: { mention: backend, directMessage: backend },
452
+ });
453
+ ```
454
+
455
+ `multitaskStrategy` is what a message that starts a run does while its thread
456
+ already runs: `enqueue` (the default) waits for the run and starts after it,
457
+ `interrupt` cancels the run, keeping its progress, and starts at once,
458
+ `rollback` cancels it and discards its progress, and `reject` starts nothing.
459
+
460
+ `event.actor` is the Slack user who sent the message, as Trigger Server read
461
+ it from Slack's signed request: `{ provider: "slack", id, teamId }`. `teamId`
462
+ is their own workspace: in a Slack Connect channel, someone from another
463
+ organization keeps theirs. It is absent rather than guessed when Trigger
464
+ Server names no one, such as an older Trigger Server or a bot's message.
465
+
466
+ > **Not yet an identity check.** Trigger Server sends `actor` as a plain
467
+ > field of the delivery, and MDA doesn't yet verify the key that delivery
468
+ > comes with, only that it has one. Use `actor` to address or describe the
469
+ > sender, not to decide what they may do or which of your accounts they are.
470
+
471
+ Every channel's events carry `actor` in this shape, a `ChannelActor`: `{
472
+ provider, id }` plus the provider's own fields. GitHub's is the webhook's
473
+ `sender` (`name` is its login, plus `type` and `email?`), and Linear's is who
474
+ acted (plus `type?`, `name?` and `email?`); Slack's has no name or email,
475
+ since Trigger Server's delivery names none. Endpoints get it as `event.actor` too. It is personal
476
+ data: MDA doesn't log it or add it to a run's metadata; add what you need
477
+ through `metadata` yourself.
478
+
479
+ `onFinish` is called once when a message or an `interaction` is done, with
480
+ the event, its thread, and how it finished, as Linear's `onFinish` is:
481
+
482
+ - `run`: the message started a run, which ended with `run.status`
483
+ (`success`, `error`, `timeout` or `interrupted`). `interrupts` lists the
484
+ decisions the thread waits on.
485
+ - `skipped`: no run started. The handler returned nothing or failed
486
+ (`ignored`), or the thread was busy under `multitaskStrategy: "reject"`
487
+ (`rejected`).
488
+ - `failed`: MDA couldn't start the run, or lost track of it.
489
+
490
+ Use it to record which run answered which message in your own service,
491
+ update memory after a run succeeds, or alert someone when one fails:
492
+
493
+ ```ts
494
+ channels.slack({
495
+ onFinish: async (event, thread, finished) => {
496
+ if (finished.type !== "run") return;
497
+ if (finished.run.status === "success") {
498
+ // e.g. record the answer in your service, or update memory
499
+ } else {
500
+ // e.g. alert someone that the run failed
501
+ }
502
+ },
503
+ });
504
+ ```
505
+
506
+ It runs in the background after the reply, and errors are logged. Interrupt
507
+ resumes don't call it, and neither does a message Trigger Server will send
508
+ again because its run couldn't start. Like `channels.http`'s, it runs in the deployment's
509
+ process, so a restart loses it: set the run's `webhook` for anything you
510
+ can't miss.
511
+
512
+ ### The status a run shows
513
+
514
+ While a run works, Slack shows Trigger Server's working phrases ("is thinking
515
+ it through...", "reviewing your question..."). `status` shows yours instead,
516
+ the way `reactions` picks the emoji: a value for every run, or a function that
517
+ picks one. A value is a line, or a list of steps, each a title or `{ title,
518
+ status }` with `status` `pending` (the default), `in_progress`, `complete` or
519
+ `error`:
520
+
521
+ ```ts
522
+ channels.slack({ status: "is consulting the crystal ball" });
523
+ channels.slack({ status: ["Read the question", "Search the docs", "Answer"] });
524
+ channels.slack({
525
+ // The agent's to-do list, as Slack's plan.
526
+ status: ({ state }) =>
527
+ state.todos?.map((todo) => ({
528
+ title: todo.content,
529
+ status: todo.status === "completed" ? "complete" : todo.status,
530
+ })) ?? null,
531
+ });
532
+ ```
533
+
534
+ The function gets the agent's state as it is now, so it can say which tool is
535
+ about to run (the last message's `tool_calls`) or what's left on the to-do
536
+ list. It is called as the run starts, before and after each model call, and
537
+ every 4 s while a model or tool call runs, one call at a time. `null`, or an
538
+ empty list (as a to-do list is before the agent writes one), leaves what's
539
+ shown. A function that throws, takes longer than 1 s, or returns
540
+ something else leaves it too, with one warning per run. The status is only
541
+ posted when it changed, at most once a second, in the background, so it never
542
+ slows the run; a failed post is logged. A sync function runs on the event
543
+ loop, so do slow work in an async one.
544
+
545
+ Apps with Slack's agent view show a custom status as a plan above the reply:
546
+ the line is the plan's title and each step one of its tasks. A step that's
547
+ no longer listed is marked complete, since Slack can't remove one, and the
548
+ plan reads "Done" when the run ends. Other apps show the line, or the first
549
+ step not yet done, as the thread's status. Without `status`, nothing changes.
550
+
551
+ ### Replying from the agent
552
+
553
+ The channel posts the agent's last reply in the thread once a run succeeds.
554
+ `reply: "none"` leaves replying to the agent instead, for an agent that posts
555
+ its own messages, with `runtime.channel.post` or its own Slack client, or
556
+ that answers somewhere else:
557
+
558
+ ```ts
559
+ channels.slack({ reply: "none" });
560
+ ```
561
+
562
+ Reactions, the status, Stop and `onFinish` work as before. `channels.linear`
563
+ and `channels.http` take the same option. Deploying `reply: "none"` needs an
564
+ `mda` CLI from this release; an older one refuses it.
565
+
566
+ ### Buttons your agent posts
567
+
568
+ `runtime.channel.post` only sends what every channel can. For anything
569
+ Slack-specific, such as buttons, feedback buttons or a message the agent
570
+ updates later, the agent posts with its own Slack client. The app's bot token
571
+ is the agent connection `mda/slack-bot-token` (reading it as a user connection
572
+ throws), and `runtime.channel.rawEvent`, the Slack event the run came from,
573
+ names the thread: a message's own event, or a click's interaction payload,
574
+ whose `container` says where the button was.
575
+
576
+ ```ts
577
+ import { WebClient } from "@slack/web-api";
578
+ import { tool } from "langchain";
579
+ import { z } from "zod";
580
+ import { connections, type ManagedToolRuntime } from "managed-deepagents";
581
+
582
+ export const offerChoices = tool(
583
+ async ({ question, choices }, runtime: ManagedToolRuntime) => {
584
+ if (runtime.channel?.provider !== "slack") return "Not in Slack.";
585
+ const raw = runtime.channel.rawEvent as Record<string, any> | undefined;
586
+ const where = raw?.container ?? raw;
587
+ const channel = where?.channel_id ?? where?.channel;
588
+ const threadTs = where?.thread_ts ?? where?.message_ts ?? where?.ts;
589
+ const token = await connections.get("mda/slack-bot-token", { type: "agent" });
590
+ await new WebClient(token).chat.postMessage({
591
+ channel,
592
+ thread_ts: threadTs,
593
+ text: question,
594
+ blocks: [
595
+ { type: "section", text: { type: "mrkdwn", text: question } },
596
+ {
597
+ type: "actions",
598
+ elements: choices.map((choice) => ({
599
+ type: "button",
600
+ action_id: "quick_reply",
601
+ text: { type: "plain_text", text: choice },
602
+ value: choice,
603
+ })),
604
+ },
605
+ ],
606
+ });
607
+ return "Posted the choices.";
608
+ },
609
+ {
610
+ name: "offer_choices",
611
+ description: "Ask the user to pick one of a few answers in Slack.",
612
+ schema: z.object({ question: z.string(), choices: z.array(z.string()) }),
613
+ }
614
+ );
615
+ ```
616
+
617
+ Trigger Server forwards a click on such a button to the channel. `onEvent`'s
618
+ `interaction` decides what it starts, as the other keys do for messages:
619
+
620
+ ```ts
621
+ channels.slack({
622
+ onEvent: {
623
+ interaction: (event) =>
624
+ event.actionId === "quick_reply" ? String(event.value) : null,
625
+ },
626
+ });
627
+ ```
628
+
629
+ The event has `actionId` (the button's `action_id`, or a modal's
630
+ `callback_id`), `value` (the button's `value`, or the modal's `state.values`),
631
+ `userId`, `channelId`, `messageTs` (the message holding the button; absent for
632
+ a modal), `threadTs`, `actor`, and Slack's interaction payload as `payload`,
633
+ with its `response_url` and without its verification `token`. The run it
634
+ starts runs on the button's thread. Return `null` for a click that starts
635
+ nothing, such as a `feedback_buttons` rating you record yourself. An endpoint works here too, as
636
+ does `multitaskStrategy`.
637
+
638
+ Without `interaction`, a click starts a run with no new message, which reads
639
+ the click from `runtime.channel.event` (`type: "user_prompt_response"`, with
640
+ `action` and `value`). A click that answers a decision the thread waits on
641
+ resumes that run instead, and never reaches `interaction`. A click without a
642
+ correlation ID never answers a tool-call review, which its own prompt answers:
643
+ while the thread waits on one, such a click starts nothing. Trigger Server
644
+ forwards clicks on buttons without a URL and on Slack's `feedback_buttons`;
645
+ selects and inputs aren't forwarded.
646
+
647
+ ### Setting up the Slack app
648
+
649
+ The first `mda deploy` of a Slack channel asks which Slack app it uses, as the
650
+ GitHub and Linear channels do:
651
+
652
+ - **Create a new Slack app in _your workspace_**: LangSmith creates and manages
653
+ it, in the workspace you authorized LangSmith in. Deploy names the workspace.
654
+ - **Let LangSmith create and manage a new Slack app**: the same, when you
655
+ haven't authorized LangSmith in Slack yet. Deploy prints the link to do that.
656
+ - **Create a new Slack app yourself**: Slack opens with the app filled in from
657
+ a manifest. Install it, then paste its Bot User OAuth Token and Signing
658
+ Secret into the prompts.
659
+ - **Use an existing Slack app**: deploy lists the request URLs, bot events and
660
+ scopes to set on the app, then asks for its token and signing secret. Slack
661
+ sends an app's events to one URL, so whatever received them before stops
662
+ receiving them.
663
+
664
+ Self-hosted LangSmith can't create Slack apps, so it offers only your own.
665
+ LangSmith keeps the app's credentials, and later deploys keep whichever app
666
+ the channel has without asking. The agent reads the app's bot token as the
667
+ agent connection `mda/slack-bot-token`, so nothing goes into `.env`.
668
+
669
+ To skip the question, with or without a terminal, set `MDA_SLACK_BOT_TOKEN`
670
+ and `MDA_SLACK_SIGNING_SECRET` in the shell or `.env` to your own app's bot
671
+ token and signing secret. Deploy checks them before uploading. Only the CLI
672
+ reads them: they are never uploaded to the deployment, and you can remove both
673
+ once the channel is set up. Without a terminal and without them, hosted
674
+ LangSmith creates a managed app in the workspace you already authorized it in,
675
+ and says so. When you haven't authorized it yet, or on self-hosted LangSmith,
676
+ deploy stops before uploading and says what to set.
677
+
678
+ When Slack still needs you once the deployment is live, to authorize LangSmith
679
+ or to install the new app (some workspaces have an admin approve it first),
680
+ deploy prints the link and waits, continuing as soon as Slack reports it done.
681
+ In a terminal the link also opens in your browser, and Ctrl-C skips the
682
+ channel. Without one, deploy waits up to an hour, so a coding agent running it
683
+ can hand you the link; Ctrl-C stops it.
684
+
685
+ If a setup step fails once the deployment is live, deploy offers to try again
686
+ or to continue without the Slack channel. Without a terminal the failure ends
687
+ the deploy, once the other channels are set up. An agent with a Slack channel
688
+ can't be deployed with `--no-wait`, because setup needs the live deployment.
689
+
342
690
  ## GitHub channels
343
691
 
344
692
  The GitHub channel is a thin HTTP adapter. Add a channel file with event handlers:
@@ -375,26 +723,8 @@ Supabase or another provider. So MCP servers and tools that act on a user's
375
723
  behalf or need a user connection are left out of those runs, with a warning,
376
724
  and the run goes on without them. An issue or pull request is a shared
377
725
  conversation, so the sender gets user memory only where the user memory
378
- layer's `allow` opts in.
379
- The first deploy asks which GitHub account should own the App. Without a
380
- terminal, for example when a coding agent deploys, set `MDA_GITHUB_APP_OWNER` in
381
- the shell or `.env` to that organization, or to `@me` for the personal account
382
- of whoever creates the App. Deploy checks this before creating the deployment
383
- and fails without it. During setup it prints the link for a person to open and
384
- waits up to an hour for them to finish.
385
- To use an existing App instead, choose it on the first deploy and enter its App
386
- ID, private key file, installation ID and webhook secret. Agent Auth keeps them
387
- like a created App's, and deploy looks up the bot ID, so nothing goes into
388
- `.env`. `GH_WEBHOOK_SECRET` and `GH_APP_BOT_ID` (the App bot account's numeric
389
- user ID) still override the stored values. An App has one webhook URL: if the
390
- existing App's webhook points somewhere else, deploy asks before moving it to
391
- the deployment, and fails without a terminal. Answer "No" to keep it there, for
392
- example when another service receives the App's events and forwards the ones
393
- for this agent to `<deployment-url>/channels/github/events` with the body and
394
- GitHub's headers unchanged. Deploy remembers that URL and doesn't ask again, with
395
- or without a terminal, until the App's webhook moves. Without a terminal, set
396
- `MDA_GITHUB_WEBHOOK_ELSEWHERE` to the URL instead. If the App's installation
397
- lacks a permission the channel needs, deploy names it.
726
+ layer's `allow` opts in. See [Setting up the GitHub App](#setting-up-the-github-app)
727
+ for what the first deploy asks.
398
728
 
399
729
  Pass `app` to narrow what the agent can reach. Deploy applies changes to it on
400
730
  every run:
@@ -425,8 +755,6 @@ manifest only when the App is created, so the App's owner grants added
425
755
  permissions, and removes dropped or lowered ones, in the App's settings. Deploy
426
756
  names both and links there. `mda delete` removes the App's connections
427
757
  from Agent Auth and links the App on GitHub, where its owner can delete it.
428
- A new App is named after the agent, and the first deploy asks which account to
429
- create it in.
430
758
  The `GH_*` overrides are read at request time; the webhook secret and bot ID read
431
759
  from Agent Auth are cached for up to 5 minutes. A matched event fails closed if
432
760
  the bot ID is missing or invalid. Calling `channels.github()` without handlers preserves
@@ -571,17 +899,68 @@ Callbacks receive a normalized event and its `thread` (`thread.id`,
571
899
  `thread.channel.name`, and `thread.interrupted` while it waits on an interrupt). The event includes
572
900
  the original payload, delivery and installation IDs, repository and sender.
573
901
  Types describe handwritten subsets, not the complete GitHub schema. Run code can
574
- inspect the normalized event through `runtime.channel.rawEvent`. Logs correlate the GitHub
902
+ inspect the normalized event through `runtime.thread.origin.channel.rawEvent`. Logs correlate the GitHub
575
903
  delivery ID to the fresh thread and run IDs without printing the payload.
576
904
  `GitHub handler returned run input` records the callback decision;
577
905
  `channel run started` confirms run creation.
578
906
 
579
907
  Each issue or pull request's thread belongs to that GitHub conversation, whoever
580
- starts its runs. User connections,
581
- `{messages}` results and automatic GitHub replies are not supported. To act on
908
+ starts its runs. User connections and automatic GitHub replies are not
909
+ supported. To act on
582
910
  GitHub from a run (comment, label, open pull requests), give the agent a GitHub
583
911
  MCP server in `tools/mcp.ts`.
584
912
 
913
+ ### Setting up the GitHub App
914
+
915
+ The first `mda deploy` of a GitHub channel asks which GitHub App it uses, as
916
+ the Slack and Linear channels do:
917
+
918
+ - **Create a new GitHub App**: deploy asks which account should own it, an
919
+ organization or your personal account, then opens GitHub with the App filled
920
+ in and named after the agent. Create it, then install it on the repositories
921
+ the agent may reach.
922
+ - **Use an existing GitHub App**: enter its App ID, private key file and
923
+ webhook secret. Deploy records the installation GitHub reports. It asks you
924
+ to pick one when the App is installed in several accounts, opens the install
925
+ page when it's installed nowhere, and asks for the installation ID only when
926
+ GitHub can't be checked.
927
+
928
+ Agent Auth keeps the App's key, webhook secret and bot ID, so nothing goes into
929
+ `.env`, and later deploys keep the App without asking. An App has one webhook
930
+ URL: if an existing App's webhook points somewhere else, deploy asks before
931
+ moving it to the deployment. Answer "No" to keep it there, for example when
932
+ another service receives the App's events and forwards the ones for this agent
933
+ to `<deployment-url>/channels/github/events` with the body and GitHub's headers
934
+ unchanged. Deploy remembers that URL and doesn't ask again, with or without a
935
+ terminal, until the App's webhook moves. If the App's installation lacks a
936
+ permission the channel needs, deploy names it.
937
+
938
+ To skip the questions, with or without a terminal, set these in the shell or
939
+ `.env`:
940
+
941
+ - `MDA_GITHUB_APP_OWNER`: create a new App owned by this organization, or by
942
+ `@me`, the personal account of whoever creates it.
943
+ - `MDA_GITHUB_APP_ID`, `MDA_GITHUB_APP_PRIVATE_KEY_FILE` (the path to its
944
+ `.pem` file) and `MDA_GITHUB_WEBHOOK_SECRET`: use this existing App instead.
945
+ Add `MDA_GITHUB_APP_INSTALLATION_ID` when it's installed in several accounts.
946
+ - `MDA_GITHUB_WEBHOOK_SECRET` alone: the webhook secret of an existing App
947
+ whose secret Agent Auth doesn't have yet.
948
+ - `MDA_GITHUB_WEBHOOK_ELSEWHERE`: the URL an existing App's webhook keeps.
949
+ Without a terminal, deploy otherwise stops when the webhook points elsewhere.
950
+
951
+ Deploy checks them before uploading. Only the CLI reads them: they are never
952
+ uploaded to the deployment, and you can remove them once the channel is set
953
+ up. Without a terminal and without an owner or an existing App, deploy stops
954
+ before uploading and says what to set. During setup it prints the link for a
955
+ person to open and waits up to an hour for them to finish. `GH_WEBHOOK_SECRET`
956
+ and `GH_APP_BOT_ID` (the App bot account's numeric user ID) are different: the
957
+ deployment reads them, and they override what Agent Auth stores.
958
+
959
+ If a setup step fails once the deployment is live, deploy offers to try again
960
+ or to continue without the GitHub channel. Without a terminal the failure ends
961
+ the deploy, once the other channels are set up. An agent with a GitHub channel
962
+ can't be deployed with `--no-wait`, because setup needs the live deployment.
963
+
585
964
  ## Linear channels
586
965
 
587
966
  The Linear channel receives a Linear app's webhooks. Each issue is one
@@ -644,37 +1023,76 @@ Slack channel's, such as `assets/icon.png` (at least 256×256 pixels, at most
644
1023
  the app's icon there. They apply only when deploy creates the app: Linear has
645
1024
  no API to change an app, so later changes to them do nothing. Change an
646
1025
  existing app on its settings page in Linear; deploy reminds you of this when
647
- it keeps an existing app.
648
-
649
- `mda deploy` sets up the Linear app, which takes a Linear workspace admin.
650
- Linear has no public API to create apps, so deploy opens Linear's new-app page
651
- filled in with the app's name, redirect URI, webhook URL
652
- (`<deployment-url>/channels/linear/events`) and events. The admin creates the
653
- app and pastes its client ID, client secret and webhook signing secret into
654
- deploy's prompts. Deploy asks for the workspace's URL key (as in
655
- `https://linear.app/<key>/`) first, so the page opens in that workspace. Deploy
656
- then opens the install page, where the admin approves the app for the
657
- workspace. Agent Auth keeps the credentials, and the agent's token, so nothing
658
- goes into `.env`. Without a terminal, for example when a coding agent deploys,
659
- set `MDA_LINEAR_CLIENT_ID`, `MDA_LINEAR_CLIENT_SECRET` and
660
- `MDA_LINEAR_WEBHOOK_SECRET` instead, and `MDA_LINEAR_WORKSPACE` to the
661
- workspace's URL key so the links deploy prints open in that workspace; deploy
662
- prints the install link. These four are read by the CLI only and never
663
- uploaded to the deployment. If Linear later stops renewing the app's access,
664
- the next deploy keeps the app and only asks an admin to approve it again.
665
- Linear has no API to change an app's webhook URL either, so if the deployment
666
- moves, update it on the app's page. `mda delete` removes the credentials;
667
- delete the app itself in Linear.
1026
+ it installs the app.
1027
+
1028
+ `mda deploy` sets up the Linear app, which takes a Linear workspace admin; see
1029
+ [Setting up the Linear app](#setting-up-the-linear-app).
668
1030
 
669
1031
  The channel reads the webhook signing secret from Agent Auth, or from
670
- `LINEAR_WEBHOOK_SECRET` when set. It acts as the app through the agent OAuth
671
- connection `mda/linear-app/<deployment-id>`, installed with `actor=app`; read
672
- its token with `await connections.get("mda/linear-token", { type: "agent" })` to call Linear's API from tools.
1032
+ `LINEAR_WEBHOOK_SECRET` when set. It acts as the app through the deployment's
1033
+ Agent Auth app, whose OAuth connection is `mda/linear-app/<deployment-id>`,
1034
+ installed with `actor=app`; read its token with
1035
+ `await connections.get("mda/linear-token", { type: "agent" })` to call Linear's API from tools.
1036
+
1037
+ `reply: "none"` leaves the run's answer to the agent: the channel doesn't post
1038
+ it as a comment or in the session. Session acknowledgements, Stop and error
1039
+ notices still post.
673
1040
 
674
1041
  [`examples/linear`](../../examples/linear) triages issues in agent sessions,
675
1042
  shows its steps and plan there, and waits for approval in LangSmith Studio
676
1043
  before moving an issue to another state.
677
1044
 
1045
+ ### Setting up the Linear app
1046
+
1047
+ The first `mda deploy` of a Linear channel asks which Linear app it uses, as
1048
+ the Slack and GitHub channels do. Setting it up takes a Linear workspace admin:
1049
+
1050
+ - **Create a new Linear app**: Linear has no public API to create apps, so
1051
+ deploy asks for the workspace's URL key (as in `https://linear.app/<key>/`)
1052
+ and opens Linear's new-app page in that workspace, filled in with the app's
1053
+ name, redirect URI, webhook URL (`<deployment-url>/channels/linear/events`)
1054
+ and events. The admin creates the app and pastes its client ID, client
1055
+ secret and webhook signing secret into deploy's prompts.
1056
+ - **Use an existing Linear app**: deploy lists the redirect URI, webhook URL
1057
+ and events to set on the app's settings page, then asks for the same three
1058
+ values. Linear sends an app's webhooks to one URL, so whatever received them
1059
+ before stops receiving them.
1060
+
1061
+ Deploy then opens the install page, where the admin approves the app for the
1062
+ workspace. Agent Auth keeps the credentials and the agent's token, so nothing
1063
+ goes into `.env`, and later deploys keep the app without asking. If Linear
1064
+ stops renewing the app's access, the next deploy keeps the app and only asks
1065
+ an admin to approve it again. Linear has no API to change an app, so when the
1066
+ channel's `app` settings change or the deployment moves to another URL, update
1067
+ the app on its settings page.
1068
+
1069
+ To skip the questions, with or without a terminal, set
1070
+ `MDA_LINEAR_CLIENT_ID`, `MDA_LINEAR_CLIENT_SECRET` and
1071
+ `MDA_LINEAR_WEBHOOK_SECRET` in the shell or `.env`, and `MDA_LINEAR_WORKSPACE`
1072
+ to the workspace's URL key so the links deploy prints open in that workspace.
1073
+ `MDA_LINEAR_WEBHOOK_SECRET` alone supplies the signing secret of an installed
1074
+ app whose secret Agent Auth doesn't have yet. Deploy checks them before
1075
+ uploading, and asks Linear whether the client ID and secret belong together.
1076
+ Only the CLI reads them: they are never uploaded to the deployment, and you can
1077
+ remove them once the channel is set up. Without a terminal and without them,
1078
+ deploy stops before uploading with the link to create the app.
1079
+
1080
+ An app from these variables wasn't made from deploy's filled-in page, and
1081
+ Linear has no API that says what it has set. So before the install link,
1082
+ deploy lists the redirect URI, webhook URL and events the app needs, the same
1083
+ as for an existing app. In a terminal it waits until you say they're set;
1084
+ without one it prints them before the install link for an admin to open.
1085
+ Without the redirect URI, Linear answers the install with "Invalid
1086
+ redirect_uri".
1087
+
1088
+ If a setup step fails once the deployment is live, deploy offers to try again
1089
+ or to continue without the Linear channel. Without a terminal the failure ends
1090
+ the deploy, once the other channels are set up. An agent with a Linear channel
1091
+ can't be deployed with `--no-wait`, because setup needs the live deployment.
1092
+ `mda delete` removes the credentials; delete the app itself in Linear.
1093
+ Deployments set up by an earlier `mda` keep the app as workspace connections,
1094
+ which only a LangSmith workspace admin can delete.
1095
+
678
1096
  ## HTTP channels
679
1097
 
680
1098
  Use `channels.http({ provider, verify, parse, post })` for a provider webhook.
@@ -687,21 +1105,22 @@ A message returned by `parse` contains `userId`, `threadId`, `content`, and `tar
687
1105
  `threadId` is the stable LangGraph thread UUID. `target` is separate JSON that the
688
1106
  adapter needs to reply, such as a Photon conversation ID or a Slack channel and
689
1107
  thread timestamp. MDA keeps this target private and binds it to
690
- `runtime.channel.post`. The adapter's `post` callback receives the target for
1108
+ `runtime.thread.origin.channel.post`. The adapter's `post` callback receives the target for
691
1109
  reply routing.
692
1110
 
693
1111
  `verify` runs first with the original bytes and headers. `verify` and `parse`
694
1112
  each get a separately readable request body. `parse` owns body decoding and can
695
1113
  read JSON, form data, text, or bytes. It returns the existing `message` or `ignore`
696
- result. MDA creates `runtime.channel.event` from the parsed message.
1114
+ result. MDA creates `runtime.thread.origin.channel.event` from the parsed message.
697
1115
 
698
1116
  To expose provider data, return
699
1117
  `{ type: "message", message, rawEvent: event }` from `parse`. MDA validates this
700
- value as finite, acyclic JSON and copies it into `runtime.channel.rawEvent`.
1118
+ value as finite, acyclic JSON and copies it into
1119
+ `runtime.thread.origin.channel.rawEvent`, in the run that message starts.
701
1120
  If you omit `rawEvent`, it stays absent. MDA does not decode the body again.
702
1121
  An explicit `null` is a valid event value.
703
1122
 
704
- HTTP and Slack Trigger runs expose delivery through `runtime.channel`.
1123
+ HTTP and Slack Trigger runs expose delivery through `runtime.thread.origin.channel`.
705
1124
 
706
1125
  The optional method has this signature:
707
1126
 
@@ -726,9 +1145,11 @@ credentials when it calls the provider API. Native routing or authentication
726
1145
  fields must not replace them.
727
1146
 
728
1147
  Automatic agent replies use the `content` form. An explicit
729
- `runtime.channel.post({ type: "native", native: ... })` reaches your adapter
1148
+ `runtime.thread.origin.channel.post({ type: "native", native: ... })` reaches your adapter
730
1149
  without conversion. Automatic replies run after the request is accepted. A failed
731
- `post` is recorded as a delivery error.
1150
+ `post` is recorded as a delivery error. `reply: "none"` turns automatic
1151
+ replies off, so the agent replies itself; `post` still serves
1152
+ `runtime.channel.post` and error notices.
732
1153
  If `post` is omitted, the channel can receive events and exposes facts, but
733
1154
  has no runtime `post` method. The webhook path is `/channels/<name>/events`, where
734
1155
  `<name>` is the channel file name. An existing Photon webhook needs no URL change
@@ -805,7 +1226,9 @@ a handler's result. `null` or HTTP 204 starts nothing. A GitHub event also
805
1226
  carries what a handler gets beside the payload: its `action`, `repository`,
806
1227
  `sender` and `mentions`, so a service checks them as a handler does. These are
807
1228
  optional in `GitHubEndpointRequest`, so a receiver takes requests from older
808
- deployments.
1229
+ deployments. A Slack message carries its `text`, `userId`, `channelId`,
1230
+ `messageTs`, `threadTs` and `files` the same way, with Slack's own event as
1231
+ `payload`.
809
1232
 
810
1233
  ```ts
811
1234
  const backend = new Request(`${process.env.BACKEND_URL}/linear/runs`, {
@@ -822,8 +1245,8 @@ export const channel = channels.linear({
822
1245
  });
823
1246
  ```
824
1247
 
825
- GitHub or Linear gets its answer first, and MDA asks the service in the
826
- background, so a slow service doesn't time out the delivery. A network error
1248
+ GitHub, Linear or Trigger Server gets its answer first, and MDA asks the
1249
+ service in the background, so a slow service doesn't time out the delivery. A network error
827
1250
  before the answer, HTTP 429 or a 5xx is tried again, up to three attempts with
828
1251
  the same `webhook-id`. Another error status, a redirect, an answer cut off or
829
1252
  that isn't one of these, more than 10 MB, or no answer within 60 seconds fails the handler, as a
@@ -855,8 +1278,9 @@ const event = new Webhook(process.env.ENDPOINT_SECRET!).verify(
855
1278
  );
856
1279
  ```
857
1280
 
858
- On the service's side, `LinearEndpointRequest<K>` and
859
- `GitHubEndpointRequest<K>` type what the channel sends, `{ event: { key,
1281
+ On the service's side, `LinearEndpointRequest<K>`,
1282
+ `GitHubEndpointRequest<K>` and `SlackEndpointRequest<K>` type what the channel
1283
+ sends, `{ event: { key,
860
1284
  deliveryId, payload }, thread }`, with the payload typed by the event key, and
861
1285
  `EndpointAnswer` types an answer, all JSON; answer `null` to start nothing. The
862
1286
  request types are types only: validate the body as your service validates
@@ -987,8 +1411,34 @@ await runtime.schedules!.create({
987
1411
  ```
988
1412
 
989
1413
  Set `newThread` to choose whether a run starts a new thread or continues the
990
- current thread. When a schedule is created from a channel such as Slack,
991
- each run posts its final answer to that channel. Runtime schedules stay when you deploy again.
1414
+ current thread. When a schedule is created on a channel's thread, such as a
1415
+ Slack thread, each run posts its final answer to that channel. A same-thread
1416
+ schedule posts wherever the thread's origin points when it runs, so its record
1417
+ has `channel: null` and `target: null`. A new-thread schedule keeps the
1418
+ conversation it was created in. On Slack, each run starts a new Slack thread
1419
+ there, with its answer or, when it waits on a decision, with a notice. Replies
1420
+ and decisions in that Slack thread continue the run's thread. Runtime schedules
1421
+ stay when you deploy again.
1422
+
1423
+ A user schedule can run as another person on the thread. Pass one of
1424
+ `runtime.thread.participants` as `runAs`; the caller still owns the schedule
1425
+ and is the one who can list, update, and delete it. Runs act as that person,
1426
+ with their connections.
1427
+
1428
+ ```ts
1429
+ const me = runtime.serverInfo?.principal?.id;
1430
+ const bob = runtime.thread!.participants.find(({ id }) => id !== me);
1431
+ await runtime.schedules!.create({
1432
+ owner: { type: "user" },
1433
+ cron: "0 9 * * 1",
1434
+ prompt: "Remind Bob about the weekly report.",
1435
+ newThread: false,
1436
+ runAs: bob,
1437
+ });
1438
+ ```
1439
+
1440
+ `runAs` needs `owner: { type: "user" }` and a participant with the same id and
1441
+ role; anything else is an error. The schedule's record shows it as `runAs`.
992
1442
 
993
1443
  Each owner can have at most 20 runtime schedules per agent, paused ones
994
1444
  included. Set `MDA_MAX_AGENT_SCHEDULES` or `MDA_MAX_USER_SCHEDULES` in the
@@ -1068,9 +1518,6 @@ server. In `mda dev`, an agent-owned connection reads from
1068
1518
  slashes to double underscores (`mda/github-token` → `MDA_DEV_MDA__GITHUB_TOKEN`).
1069
1519
  Hosted deployments resolve workspace connections from Agent Auth:
1070
1520
 
1071
- The old `connectors/mcp.ts` file API remains available with a warning during
1072
- `0.7.x`. It will be removed in `0.8.0`.
1073
-
1074
1521
  ```ts
1075
1522
  import { defineMcp, connections } from "managed-deepagents";
1076
1523