@agent-native/dispatch 0.13.9 → 0.13.11

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 (261) hide show
  1. package/README.md +7 -28
  2. package/dist/actions/navigate.js +1 -1
  3. package/dist/actions/navigate.js.map +1 -1
  4. package/dist/components/agents-panel.js +3 -3
  5. package/dist/components/agents-panel.js.map +1 -1
  6. package/dist/components/app-keys-popover.d.ts.map +1 -1
  7. package/dist/components/app-keys-popover.js +3 -3
  8. package/dist/components/app-keys-popover.js.map +1 -1
  9. package/dist/components/create-app-popover.d.ts.map +1 -1
  10. package/dist/components/create-app-popover.js +2 -2
  11. package/dist/components/create-app-popover.js.map +1 -1
  12. package/dist/components/dispatch-control-plane.d.ts.map +1 -1
  13. package/dist/components/dispatch-control-plane.js +19 -7
  14. package/dist/components/dispatch-control-plane.js.map +1 -1
  15. package/dist/components/dispatch-shell.d.ts.map +1 -1
  16. package/dist/components/dispatch-shell.js +2 -2
  17. package/dist/components/dispatch-shell.js.map +1 -1
  18. package/dist/components/layout/Header.js +2 -1
  19. package/dist/components/layout/Header.js.map +1 -1
  20. package/dist/components/layout/Layout.d.ts.map +1 -1
  21. package/dist/components/layout/Layout.js +12 -5
  22. package/dist/components/layout/Layout.js.map +1 -1
  23. package/dist/components/messaging-setup-panel.d.ts.map +1 -1
  24. package/dist/components/messaging-setup-panel.js +4 -4
  25. package/dist/components/messaging-setup-panel.js.map +1 -1
  26. package/dist/components/task-queue-health.d.ts +2 -0
  27. package/dist/components/task-queue-health.d.ts.map +1 -0
  28. package/dist/components/task-queue-health.js +49 -0
  29. package/dist/components/task-queue-health.js.map +1 -0
  30. package/dist/components/ui/accordion.js.map +1 -1
  31. package/dist/components/ui/alert-dialog.js +1 -1
  32. package/dist/components/ui/alert-dialog.js.map +1 -1
  33. package/dist/components/ui/alert.js.map +1 -1
  34. package/dist/components/ui/avatar.js.map +1 -1
  35. package/dist/components/ui/badge.js.map +1 -1
  36. package/dist/components/ui/breadcrumb.js.map +1 -1
  37. package/dist/components/ui/button.js.map +1 -1
  38. package/dist/components/ui/calendar.js +1 -1
  39. package/dist/components/ui/calendar.js.map +1 -1
  40. package/dist/components/ui/card.js.map +1 -1
  41. package/dist/components/ui/carousel.d.ts +2 -2
  42. package/dist/components/ui/carousel.d.ts.map +1 -1
  43. package/dist/components/ui/carousel.js +1 -1
  44. package/dist/components/ui/carousel.js.map +1 -1
  45. package/dist/components/ui/chart.js.map +1 -1
  46. package/dist/components/ui/checkbox.js.map +1 -1
  47. package/dist/components/ui/command.js +1 -1
  48. package/dist/components/ui/command.js.map +1 -1
  49. package/dist/components/ui/context-menu.js.map +1 -1
  50. package/dist/components/ui/dialog.js.map +1 -1
  51. package/dist/components/ui/drawer.js.map +1 -1
  52. package/dist/components/ui/dropdown-menu.js.map +1 -1
  53. package/dist/components/ui/form.js +1 -1
  54. package/dist/components/ui/form.js.map +1 -1
  55. package/dist/components/ui/hover-card.js.map +1 -1
  56. package/dist/components/ui/input-otp.js.map +1 -1
  57. package/dist/components/ui/input.js.map +1 -1
  58. package/dist/components/ui/label.js.map +1 -1
  59. package/dist/components/ui/menubar.js.map +1 -1
  60. package/dist/components/ui/navigation-menu.js.map +1 -1
  61. package/dist/components/ui/pagination.d.ts +1 -1
  62. package/dist/components/ui/pagination.d.ts.map +1 -1
  63. package/dist/components/ui/pagination.js +1 -1
  64. package/dist/components/ui/pagination.js.map +1 -1
  65. package/dist/components/ui/popover.js.map +1 -1
  66. package/dist/components/ui/progress.js.map +1 -1
  67. package/dist/components/ui/radio-group.js.map +1 -1
  68. package/dist/components/ui/resizable.js.map +1 -1
  69. package/dist/components/ui/scroll-area.js.map +1 -1
  70. package/dist/components/ui/select.js.map +1 -1
  71. package/dist/components/ui/separator.js.map +1 -1
  72. package/dist/components/ui/sheet.js.map +1 -1
  73. package/dist/components/ui/sidebar.d.ts +2 -2
  74. package/dist/components/ui/sidebar.d.ts.map +1 -1
  75. package/dist/components/ui/sidebar.js +6 -6
  76. package/dist/components/ui/sidebar.js.map +1 -1
  77. package/dist/components/ui/skeleton.js.map +1 -1
  78. package/dist/components/ui/slider.js.map +1 -1
  79. package/dist/components/ui/sonner.js.map +1 -1
  80. package/dist/components/ui/spinner.js.map +1 -1
  81. package/dist/components/ui/switch.js.map +1 -1
  82. package/dist/components/ui/table.js.map +1 -1
  83. package/dist/components/ui/tabs.js.map +1 -1
  84. package/dist/components/ui/textarea.js.map +1 -1
  85. package/dist/components/ui/toast.js.map +1 -1
  86. package/dist/components/ui/toaster.js +1 -1
  87. package/dist/components/ui/toaster.js.map +1 -1
  88. package/dist/components/ui/toggle-group.js +1 -1
  89. package/dist/components/ui/toggle-group.js.map +1 -1
  90. package/dist/components/ui/toggle.js.map +1 -1
  91. package/dist/components/ui/tooltip.js.map +1 -1
  92. package/dist/components/ui/use-toast.d.ts.map +1 -1
  93. package/dist/components/ui/use-toast.js.map +1 -1
  94. package/dist/components/workspace-app-card.d.ts.map +1 -1
  95. package/dist/components/workspace-app-card.js +10 -10
  96. package/dist/components/workspace-app-card.js.map +1 -1
  97. package/dist/components/workspace-resource-effective-stack.js +1 -1
  98. package/dist/components/workspace-resource-effective-stack.js.map +1 -1
  99. package/dist/components/workspace-resource-impact-preview.d.ts.map +1 -1
  100. package/dist/components/workspace-resource-impact-preview.js +2 -2
  101. package/dist/components/workspace-resource-impact-preview.js.map +1 -1
  102. package/dist/hooks/use-navigation-state.js +5 -0
  103. package/dist/hooks/use-navigation-state.js.map +1 -1
  104. package/dist/hooks/use-toast.d.ts.map +1 -1
  105. package/dist/hooks/use-toast.js.map +1 -1
  106. package/dist/lib/automation-display.d.ts +13 -0
  107. package/dist/lib/automation-display.d.ts.map +1 -0
  108. package/dist/lib/automation-display.js +62 -0
  109. package/dist/lib/automation-display.js.map +1 -0
  110. package/dist/lib/task-queue.d.ts +19 -0
  111. package/dist/lib/task-queue.d.ts.map +1 -0
  112. package/dist/lib/task-queue.js +54 -0
  113. package/dist/lib/task-queue.js.map +1 -0
  114. package/dist/routes/index.d.ts.map +1 -1
  115. package/dist/routes/index.js +1 -0
  116. package/dist/routes/index.js.map +1 -1
  117. package/dist/routes/pages/$appId.js.map +1 -1
  118. package/dist/routes/pages/_index.js.map +1 -1
  119. package/dist/routes/pages/agents.d.ts.map +1 -1
  120. package/dist/routes/pages/agents.js +1 -1
  121. package/dist/routes/pages/agents.js.map +1 -1
  122. package/dist/routes/pages/approval.js.map +1 -1
  123. package/dist/routes/pages/approvals.js.map +1 -1
  124. package/dist/routes/pages/apps.$appId.js.map +1 -1
  125. package/dist/routes/pages/apps.js.map +1 -1
  126. package/dist/routes/pages/audit.js.map +1 -1
  127. package/dist/routes/pages/automations.d.ts +5 -0
  128. package/dist/routes/pages/automations.d.ts.map +1 -0
  129. package/dist/routes/pages/automations.js +106 -0
  130. package/dist/routes/pages/automations.js.map +1 -0
  131. package/dist/routes/pages/chat.js.map +1 -1
  132. package/dist/routes/pages/destinations.d.ts.map +1 -1
  133. package/dist/routes/pages/destinations.js +31 -27
  134. package/dist/routes/pages/destinations.js.map +1 -1
  135. package/dist/routes/pages/dreams.d.ts.map +1 -1
  136. package/dist/routes/pages/dreams.js +1 -1
  137. package/dist/routes/pages/dreams.js.map +1 -1
  138. package/dist/routes/pages/identities.js.map +1 -1
  139. package/dist/routes/pages/integrations.js.map +1 -1
  140. package/dist/routes/pages/messaging.js.map +1 -1
  141. package/dist/routes/pages/metrics.js.map +1 -1
  142. package/dist/routes/pages/new-app.js.map +1 -1
  143. package/dist/routes/pages/overview.d.ts +2 -2
  144. package/dist/routes/pages/overview.js.map +1 -1
  145. package/dist/routes/pages/settings.d.ts.map +1 -1
  146. package/dist/routes/pages/settings.js +2 -2
  147. package/dist/routes/pages/settings.js.map +1 -1
  148. package/dist/routes/pages/thread-debug.d.ts.map +1 -1
  149. package/dist/routes/pages/thread-debug.js +1 -1
  150. package/dist/routes/pages/thread-debug.js.map +1 -1
  151. package/dist/routes/pages/vault.d.ts.map +1 -1
  152. package/dist/routes/pages/vault.js +1 -1
  153. package/dist/routes/pages/vault.js.map +1 -1
  154. package/dist/routes/pages/workspace.d.ts.map +1 -1
  155. package/dist/routes/pages/workspace.js +1 -1
  156. package/dist/routes/pages/workspace.js.map +1 -1
  157. package/dist/server/lib/dispatch-integrations.d.ts.map +1 -1
  158. package/dist/server/lib/dispatch-integrations.js +39 -0
  159. package/dist/server/lib/dispatch-integrations.js.map +1 -1
  160. package/dist/server/lib/pre-auth-routing.d.ts.map +1 -1
  161. package/dist/server/lib/pre-auth-routing.js +1 -0
  162. package/dist/server/lib/pre-auth-routing.js.map +1 -1
  163. package/dist/server/plugins/integrations.d.ts.map +1 -1
  164. package/dist/server/plugins/integrations.js +12 -1
  165. package/dist/server/plugins/integrations.js.map +1 -1
  166. package/package.json +7 -7
  167. package/src/actions/navigate.ts +1 -1
  168. package/src/components/agents-panel.tsx +3 -3
  169. package/src/components/app-keys-popover.tsx +3 -7
  170. package/src/components/create-app-popover.tsx +2 -6
  171. package/src/components/dispatch-control-plane.tsx +48 -7
  172. package/src/components/dispatch-shell.tsx +2 -6
  173. package/src/components/layout/Header.tsx +2 -2
  174. package/src/components/layout/Layout.tsx +13 -15
  175. package/src/components/messaging-setup-panel.tsx +4 -8
  176. package/src/components/task-queue-health.tsx +166 -0
  177. package/src/components/ui/accordion.tsx +1 -1
  178. package/src/components/ui/alert-dialog.tsx +2 -2
  179. package/src/components/ui/alert.tsx +1 -1
  180. package/src/components/ui/avatar.tsx +1 -1
  181. package/src/components/ui/badge.tsx +1 -1
  182. package/src/components/ui/breadcrumb.tsx +1 -1
  183. package/src/components/ui/button.tsx +1 -1
  184. package/src/components/ui/calendar.tsx +2 -2
  185. package/src/components/ui/card.tsx +1 -1
  186. package/src/components/ui/carousel.tsx +2 -2
  187. package/src/components/ui/chart.tsx +1 -1
  188. package/src/components/ui/checkbox.tsx +1 -1
  189. package/src/components/ui/command.tsx +2 -2
  190. package/src/components/ui/context-menu.tsx +1 -1
  191. package/src/components/ui/dialog.tsx +1 -1
  192. package/src/components/ui/drawer.tsx +1 -1
  193. package/src/components/ui/dropdown-menu.tsx +1 -1
  194. package/src/components/ui/form.tsx +2 -2
  195. package/src/components/ui/hover-card.tsx +1 -1
  196. package/src/components/ui/input-otp.tsx +1 -1
  197. package/src/components/ui/input.tsx +1 -1
  198. package/src/components/ui/label.tsx +1 -1
  199. package/src/components/ui/menubar.tsx +1 -1
  200. package/src/components/ui/navigation-menu.tsx +1 -1
  201. package/src/components/ui/pagination.tsx +2 -2
  202. package/src/components/ui/popover.tsx +1 -1
  203. package/src/components/ui/progress.tsx +1 -1
  204. package/src/components/ui/radio-group.tsx +1 -1
  205. package/src/components/ui/resizable.tsx +1 -1
  206. package/src/components/ui/scroll-area.tsx +1 -1
  207. package/src/components/ui/select.tsx +1 -1
  208. package/src/components/ui/separator.tsx +1 -1
  209. package/src/components/ui/sheet.tsx +1 -1
  210. package/src/components/ui/sidebar.tsx +8 -13
  211. package/src/components/ui/skeleton.tsx +1 -1
  212. package/src/components/ui/slider.tsx +1 -1
  213. package/src/components/ui/sonner.tsx +1 -1
  214. package/src/components/ui/spinner.tsx +1 -1
  215. package/src/components/ui/switch.tsx +1 -1
  216. package/src/components/ui/table.tsx +1 -1
  217. package/src/components/ui/tabs.tsx +1 -1
  218. package/src/components/ui/textarea.tsx +1 -1
  219. package/src/components/ui/toast.tsx +1 -1
  220. package/src/components/ui/toaster.tsx +2 -2
  221. package/src/components/ui/toggle-group.tsx +2 -2
  222. package/src/components/ui/toggle.tsx +1 -1
  223. package/src/components/ui/tooltip.tsx +1 -1
  224. package/src/components/ui/use-toast.ts +1 -1
  225. package/src/components/workspace-app-card.tsx +16 -20
  226. package/src/components/workspace-resource-effective-stack.tsx +2 -2
  227. package/src/components/workspace-resource-impact-preview.tsx +2 -3
  228. package/src/hooks/use-navigation-state.spec.ts +7 -0
  229. package/src/hooks/use-navigation-state.ts +4 -0
  230. package/src/hooks/use-toast.ts +1 -1
  231. package/src/lib/automation-display.spec.ts +95 -0
  232. package/src/lib/automation-display.ts +70 -0
  233. package/src/lib/task-queue.spec.ts +57 -0
  234. package/src/lib/task-queue.ts +72 -0
  235. package/src/routes/index.ts +1 -0
  236. package/src/routes/pages/$appId.tsx +6 -6
  237. package/src/routes/pages/_index.tsx +1 -1
  238. package/src/routes/pages/agents.tsx +8 -5
  239. package/src/routes/pages/approval.tsx +4 -4
  240. package/src/routes/pages/approvals.tsx +4 -4
  241. package/src/routes/pages/apps.$appId.tsx +5 -5
  242. package/src/routes/pages/apps.tsx +8 -8
  243. package/src/routes/pages/audit.tsx +1 -1
  244. package/src/routes/pages/automations.tsx +280 -0
  245. package/src/routes/pages/chat.tsx +1 -1
  246. package/src/routes/pages/destinations.tsx +163 -154
  247. package/src/routes/pages/dreams.tsx +22 -18
  248. package/src/routes/pages/identities.tsx +2 -2
  249. package/src/routes/pages/integrations.tsx +8 -8
  250. package/src/routes/pages/messaging.tsx +2 -2
  251. package/src/routes/pages/metrics.tsx +6 -6
  252. package/src/routes/pages/new-app.tsx +1 -1
  253. package/src/routes/pages/overview.tsx +1 -1
  254. package/src/routes/pages/settings.tsx +37 -6
  255. package/src/routes/pages/thread-debug.tsx +14 -9
  256. package/src/routes/pages/vault.tsx +17 -12
  257. package/src/routes/pages/workspace.tsx +17 -12
  258. package/src/server/lib/dispatch-integrations.spec.ts +92 -1
  259. package/src/server/lib/dispatch-integrations.ts +48 -0
  260. package/src/server/lib/pre-auth-routing.ts +1 -0
  261. package/src/server/plugins/integrations.ts +13 -1
@@ -2,6 +2,16 @@ import { createIntegrationsPlugin } from "@agent-native/core/server";
2
2
  import { dispatchActions } from "../../actions/index.js";
3
3
  import { getDispatchConfig } from "../index.js";
4
4
  import { beforeDispatchProcess, resolveDispatchOwner, } from "../lib/dispatch-integrations.js";
5
+ const dispatchIntegrationActions = {
6
+ ...dispatchActions,
7
+ // Messaging integrations should use the core call-agent tool for cross-app
8
+ // delegation because it queues A2A continuations when serverless budgets are
9
+ // tight. The MCP-facing ask_app action is still available outside this path.
10
+ ask_app: {
11
+ ...dispatchActions.ask_app,
12
+ agentTool: false,
13
+ },
14
+ };
5
15
  const DISPATCH_INTEGRATION_SYSTEM_PROMPT = `You are the central dispatch for this workspace, responding via a messaging platform integration (Slack, Telegram, email, etc.).
6
16
 
7
17
  Default posture:
@@ -16,6 +26,7 @@ Default posture:
16
26
 
17
27
  When a user asks for something:
18
28
  - If it belongs to analytics, content, slides, clips, assets, etc., delegate via call-agent — do not re-implement the domain logic in dispatch.
29
+ - In messaging integrations, use call-agent for cross-app delegation; do not use ask_app.
19
30
  - After call-agent returns an answer, RELAY IT DIRECTLY to the user with at most a one-line preface — do not rephrase, summarize, or add commentary. The downstream agent already crafted the answer; your job is delivery, not editing. This minimizes round-trips and keeps the user-visible reply fast.
20
31
  - Exception: if the downstream agent reports a missing model/provider credential, do not name exact env vars, Vault keys, tokens, or secrets. Say the target app needs an LLM connection and recommend connecting Builder/managed LLM for that app; keep bring-your-own provider keys as a secondary option only if the user asks.
21
32
  - If the user asks to create, build, make, scaffold, or generate an "agent" from Dispatch chat or by tagging @agent-native in Slack, email, or Telegram, first classify the ask. If it is a simple Dispatch-native behavior like a reminder, digest, monitor, routing rule, saved instruction, or recurring workflow, create or update the recurring job/resource/destination in Dispatch. If it is a robust unique product or teammate that needs its own UI, data model, actions, integrations, or domain workflow, treat it as a new workspace app and call start-workspace-app-creation.
@@ -41,7 +52,7 @@ const dispatchIntegrationsPlugin = async (nitroApp) => {
41
52
  : DISPATCH_INTEGRATION_SYSTEM_PROMPT;
42
53
  const plugin = createIntegrationsPlugin({
43
54
  appId: "dispatch",
44
- actions: dispatchActions,
55
+ actions: dispatchIntegrationActions,
45
56
  resolveOwner: resolveDispatchOwner,
46
57
  beforeProcess: beforeDispatchProcess,
47
58
  systemPrompt,
@@ -1 +1 @@
1
- {"version":3,"file":"integrations.js","sourceRoot":"","sources":["../../../src/server/plugins/integrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAErE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EACL,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,iCAAiC,CAAC;AAEzC,MAAM,kCAAkC,GAAG;;;;;;;;;;;;;;;;;;;;;;;4FAuBiD,CAAC;AAE7F;;;;GAIG;AACH,MAAM,0BAA0B,GAAG,KAAK,EAAE,QAAa,EAAE,EAAE;IACzD,MAAM,EAAE,YAAY,GAAG,EAAE,EAAE,GAAG,iBAAiB,EAAE,CAAC;IAClD,MAAM,cAAc,GAAG,YAAY,CAAC,YAAY,CAAC;IACjD,MAAM,YAAY,GAChB,OAAO,cAAc,KAAK,QAAQ;QAChC,CAAC,CAAC,cAAc;QAChB,CAAC,CAAC,OAAO,cAAc,KAAK,UAAU;YACpC,CAAC,CAAC,cAAc,CAAC,kCAAkC,CAAC;YACpD,CAAC,CAAC,kCAAkC,CAAC;IAE3C,MAAM,MAAM,GAAG,wBAAwB,CAAC;QACtC,KAAK,EAAE,UAAU;QACjB,OAAO,EAAE,eAAe;QACxB,YAAY,EAAE,oBAAoB;QAClC,aAAa,EAAE,qBAAqB;QACpC,YAAY;QACZ,wDAAwD;QACxD,yEAAyE;QACzE,+DAA+D;QAC/D,6EAA6E;KAC9E,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC,CAAC;AAEF,eAAe,0BAA0B,CAAC","sourcesContent":["import { createIntegrationsPlugin } from \"@agent-native/core/server\";\n\nimport { dispatchActions } from \"../../actions/index.js\";\nimport { getDispatchConfig } from \"../index.js\";\nimport {\n beforeDispatchProcess,\n resolveDispatchOwner,\n} from \"../lib/dispatch-integrations.js\";\n\nconst DISPATCH_INTEGRATION_SYSTEM_PROMPT = `You are the central dispatch for this workspace, responding via a messaging platform integration (Slack, Telegram, email, etc.).\n\nDefault posture:\n- Treat Slack, Telegram, and email as shared entrypoints into the workspace.\n- Heavily delegate domain work to specialized agents through A2A (call-agent) when another app owns the job. Apps you can delegate to include slides (decks/presentations), analytics (data/dashboards), content (docs/articles), forms (form builder), clips (screen recordings), design (visual designs), and assets (brand libraries plus generated images/videos).\n- Use the available-apps prompt context first, then list-connected-agents when you need fresh details, to see what agents are available before assuming a request must be handled locally.\n- When asked whether workspace apps expose agent cards or A2A endpoints, call list-workspace-apps with includeAgentCards=true. Without that probe, missing agent-card fields mean unchecked, not unavailable.\n- Treat first-party apps such as Mail, Calendar, Analytics, Brain, Assets, and Dispatch as existing hosted/connected neighbors available through links and A2A/default connected agents. Do not create wrapper apps, child apps, nested routes, or cloned template copies just to give a new app access to them; build only the genuinely new workflow and delegate cross-app work to those existing apps.\n- Integration grants are not provider capability limits. For ad hoc provider inspection, querying, reporting, or troubleshooting, call provider-api-catalog/provider-api-docs, then provider-api-request against the provider's real HTTP API. Use connectionId for a specific shared grant and accountId for a specific OAuth account. Never expose secret values or silently widen app access while doing this.\n- Keep durable memory and operating instructions in resources rather than ephemeral chat.\n- Reply in the originating thread unless the user explicitly asks you to send to a saved destination.\n\nWhen a user asks for something:\n- If it belongs to analytics, content, slides, clips, assets, etc., delegate via call-agent — do not re-implement the domain logic in dispatch.\n- After call-agent returns an answer, RELAY IT DIRECTLY to the user with at most a one-line preface — do not rephrase, summarize, or add commentary. The downstream agent already crafted the answer; your job is delivery, not editing. This minimizes round-trips and keeps the user-visible reply fast.\n- Exception: if the downstream agent reports a missing model/provider credential, do not name exact env vars, Vault keys, tokens, or secrets. Say the target app needs an LLM connection and recommend connecting Builder/managed LLM for that app; keep bring-your-own provider keys as a secondary option only if the user asks.\n- If the user asks to create, build, make, scaffold, or generate an \"agent\" from Dispatch chat or by tagging @agent-native in Slack, email, or Telegram, first classify the ask. If it is a simple Dispatch-native behavior like a reminder, digest, monitor, routing rule, saved instruction, or recurring workflow, create or update the recurring job/resource/destination in Dispatch. If it is a robust unique product or teammate that needs its own UI, data model, actions, integrations, or domain workflow, treat it as a new workspace app and call start-workspace-app-creation.\n- If a new-app prompt asks for access to Mail, Calendar, Analytics, Brain, Assets, or similar first-party app data/agents, keep using the existing hosted/connected app and A2A path. Do not ask Builder to scaffold those apps as children of the new app unless the user explicitly asks for a customized fork/copy.\n- If the chat template is used, treat it as scaffolding only: the finished app must be branded as the requested app with its own home screen/navigation/package metadata/manifest, and must not leave visible \"Chat\", \"Starter\", \"Blank app\", or \"New app\" UI behind.\n- If the user explicitly asks for a new app or workspace app, call start-workspace-app-creation with their prompt and include a concise generated description by default. Do not satisfy a new-app request by adding a route, page, component, or file inside apps/chat or another existing app unless the user explicitly asks to modify that existing app. If the request is too vague to classify, ask one concise follow-up. If the action returns mode \"builder\", reply with the Builder branch URL; Builder is responsible for creating the separate workspace app under apps/<app-id>, mounting it at /<app-id>, ensuring apps/<app-id>/package.json exists with name/displayName and description so Dispatch discovers it, using relative /<app-id> links instead of hardcoded localhost/dev ports, and preserving APP_BASE_PATH/VITE_APP_BASE_PATH via appBasePath() in the React Router client entry. The new app lives at the workspace root /<app-id>, NOT under /dispatch/<app-id>, /apps/<app-id>, or any other Dispatch tab — when telling the user where to find it, link to /<app-id> only. There is no separate workspace app registry to edit. If it returns mode \"local-agent\", tell the user it is ready for the local code agent and include the returned app path/prompt summary. If it returns mode \"coming-soon\", say this requires a code change and they can edit locally or use Builder.io to edit this code in the cloud and continue customizing the app any way they like; do not send them to Builder org/beta settings. If it returns mode \"builder-unavailable\", report the action's message exactly enough to preserve the missing identity/credential detail.\n- For digests, reminders, or saved behavior, prefer recurring jobs, resources, or destinations over chat replies.\n- Keep responses concise and operational — messaging platforms have character limits.\n- Use markdown sparingly (bold and lists are fine, avoid complex formatting).\n- If a task requires many steps, summarize what you did rather than streaming every detail.`;\n\n/**\n * Defer plugin construction until the Nitro plugin actually fires so the\n * config-aware system prompt resolves AFTER `setupDispatch(config)` has\n * stamped the active config (plugin module load order is not guaranteed).\n */\nconst dispatchIntegrationsPlugin = async (nitroApp: any) => {\n const { integrations = {} } = getDispatchConfig();\n const promptOverride = integrations.systemPrompt;\n const systemPrompt =\n typeof promptOverride === \"string\"\n ? promptOverride\n : typeof promptOverride === \"function\"\n ? promptOverride(DISPATCH_INTEGRATION_SYSTEM_PROMPT)\n : DISPATCH_INTEGRATION_SYSTEM_PROMPT;\n\n const plugin = createIntegrationsPlugin({\n appId: \"dispatch\",\n actions: dispatchActions,\n resolveOwner: resolveDispatchOwner,\n beforeProcess: beforeDispatchProcess,\n systemPrompt,\n // Inherit the framework default (claude-sonnet-4-6 from\n // packages/core/src/integrations/plugin.ts). Haiku was tried for latency\n // but hallucinated URLs/IDs after delegated call-agent results\n // (e.g. inventing `https://slides.workspace.com/deck/builder-io-deck-2024`).\n });\n\n return plugin(nitroApp);\n};\n\nexport default dispatchIntegrationsPlugin;\n"]}
1
+ {"version":3,"file":"integrations.js","sourceRoot":"","sources":["../../../src/server/plugins/integrations.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAErE,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EACL,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,iCAAiC,CAAC;AAEzC,MAAM,0BAA0B,GAAG;IACjC,GAAG,eAAe;IAClB,2EAA2E;IAC3E,6EAA6E;IAC7E,6EAA6E;IAC7E,OAAO,EAAE;QACP,GAAG,eAAe,CAAC,OAAO;QAC1B,SAAS,EAAE,KAAK;KACjB;CACF,CAAC;AAEF,MAAM,kCAAkC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;4FAwBiD,CAAC;AAE7F;;;;GAIG;AACH,MAAM,0BAA0B,GAAG,KAAK,EAAE,QAAa,EAAE,EAAE;IACzD,MAAM,EAAE,YAAY,GAAG,EAAE,EAAE,GAAG,iBAAiB,EAAE,CAAC;IAClD,MAAM,cAAc,GAAG,YAAY,CAAC,YAAY,CAAC;IACjD,MAAM,YAAY,GAChB,OAAO,cAAc,KAAK,QAAQ;QAChC,CAAC,CAAC,cAAc;QAChB,CAAC,CAAC,OAAO,cAAc,KAAK,UAAU;YACpC,CAAC,CAAC,cAAc,CAAC,kCAAkC,CAAC;YACpD,CAAC,CAAC,kCAAkC,CAAC;IAE3C,MAAM,MAAM,GAAG,wBAAwB,CAAC;QACtC,KAAK,EAAE,UAAU;QACjB,OAAO,EAAE,0BAA0B;QACnC,YAAY,EAAE,oBAAoB;QAClC,aAAa,EAAE,qBAAqB;QACpC,YAAY;QACZ,wDAAwD;QACxD,yEAAyE;QACzE,+DAA+D;QAC/D,6EAA6E;KAC9E,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC,CAAC;AAEF,eAAe,0BAA0B,CAAC","sourcesContent":["import { createIntegrationsPlugin } from \"@agent-native/core/server\";\n\nimport { dispatchActions } from \"../../actions/index.js\";\nimport { getDispatchConfig } from \"../index.js\";\nimport {\n beforeDispatchProcess,\n resolveDispatchOwner,\n} from \"../lib/dispatch-integrations.js\";\n\nconst dispatchIntegrationActions = {\n ...dispatchActions,\n // Messaging integrations should use the core call-agent tool for cross-app\n // delegation because it queues A2A continuations when serverless budgets are\n // tight. The MCP-facing ask_app action is still available outside this path.\n ask_app: {\n ...dispatchActions.ask_app,\n agentTool: false,\n },\n};\n\nconst DISPATCH_INTEGRATION_SYSTEM_PROMPT = `You are the central dispatch for this workspace, responding via a messaging platform integration (Slack, Telegram, email, etc.).\n\nDefault posture:\n- Treat Slack, Telegram, and email as shared entrypoints into the workspace.\n- Heavily delegate domain work to specialized agents through A2A (call-agent) when another app owns the job. Apps you can delegate to include slides (decks/presentations), analytics (data/dashboards), content (docs/articles), forms (form builder), clips (screen recordings), design (visual designs), and assets (brand libraries plus generated images/videos).\n- Use the available-apps prompt context first, then list-connected-agents when you need fresh details, to see what agents are available before assuming a request must be handled locally.\n- When asked whether workspace apps expose agent cards or A2A endpoints, call list-workspace-apps with includeAgentCards=true. Without that probe, missing agent-card fields mean unchecked, not unavailable.\n- Treat first-party apps such as Mail, Calendar, Analytics, Brain, Assets, and Dispatch as existing hosted/connected neighbors available through links and A2A/default connected agents. Do not create wrapper apps, child apps, nested routes, or cloned template copies just to give a new app access to them; build only the genuinely new workflow and delegate cross-app work to those existing apps.\n- Integration grants are not provider capability limits. For ad hoc provider inspection, querying, reporting, or troubleshooting, call provider-api-catalog/provider-api-docs, then provider-api-request against the provider's real HTTP API. Use connectionId for a specific shared grant and accountId for a specific OAuth account. Never expose secret values or silently widen app access while doing this.\n- Keep durable memory and operating instructions in resources rather than ephemeral chat.\n- Reply in the originating thread unless the user explicitly asks you to send to a saved destination.\n\nWhen a user asks for something:\n- If it belongs to analytics, content, slides, clips, assets, etc., delegate via call-agent — do not re-implement the domain logic in dispatch.\n- In messaging integrations, use call-agent for cross-app delegation; do not use ask_app.\n- After call-agent returns an answer, RELAY IT DIRECTLY to the user with at most a one-line preface — do not rephrase, summarize, or add commentary. The downstream agent already crafted the answer; your job is delivery, not editing. This minimizes round-trips and keeps the user-visible reply fast.\n- Exception: if the downstream agent reports a missing model/provider credential, do not name exact env vars, Vault keys, tokens, or secrets. Say the target app needs an LLM connection and recommend connecting Builder/managed LLM for that app; keep bring-your-own provider keys as a secondary option only if the user asks.\n- If the user asks to create, build, make, scaffold, or generate an \"agent\" from Dispatch chat or by tagging @agent-native in Slack, email, or Telegram, first classify the ask. If it is a simple Dispatch-native behavior like a reminder, digest, monitor, routing rule, saved instruction, or recurring workflow, create or update the recurring job/resource/destination in Dispatch. If it is a robust unique product or teammate that needs its own UI, data model, actions, integrations, or domain workflow, treat it as a new workspace app and call start-workspace-app-creation.\n- If a new-app prompt asks for access to Mail, Calendar, Analytics, Brain, Assets, or similar first-party app data/agents, keep using the existing hosted/connected app and A2A path. Do not ask Builder to scaffold those apps as children of the new app unless the user explicitly asks for a customized fork/copy.\n- If the chat template is used, treat it as scaffolding only: the finished app must be branded as the requested app with its own home screen/navigation/package metadata/manifest, and must not leave visible \"Chat\", \"Starter\", \"Blank app\", or \"New app\" UI behind.\n- If the user explicitly asks for a new app or workspace app, call start-workspace-app-creation with their prompt and include a concise generated description by default. Do not satisfy a new-app request by adding a route, page, component, or file inside apps/chat or another existing app unless the user explicitly asks to modify that existing app. If the request is too vague to classify, ask one concise follow-up. If the action returns mode \"builder\", reply with the Builder branch URL; Builder is responsible for creating the separate workspace app under apps/<app-id>, mounting it at /<app-id>, ensuring apps/<app-id>/package.json exists with name/displayName and description so Dispatch discovers it, using relative /<app-id> links instead of hardcoded localhost/dev ports, and preserving APP_BASE_PATH/VITE_APP_BASE_PATH via appBasePath() in the React Router client entry. The new app lives at the workspace root /<app-id>, NOT under /dispatch/<app-id>, /apps/<app-id>, or any other Dispatch tab — when telling the user where to find it, link to /<app-id> only. There is no separate workspace app registry to edit. If it returns mode \"local-agent\", tell the user it is ready for the local code agent and include the returned app path/prompt summary. If it returns mode \"coming-soon\", say this requires a code change and they can edit locally or use Builder.io to edit this code in the cloud and continue customizing the app any way they like; do not send them to Builder org/beta settings. If it returns mode \"builder-unavailable\", report the action's message exactly enough to preserve the missing identity/credential detail.\n- For digests, reminders, or saved behavior, prefer recurring jobs, resources, or destinations over chat replies.\n- Keep responses concise and operational — messaging platforms have character limits.\n- Use markdown sparingly (bold and lists are fine, avoid complex formatting).\n- If a task requires many steps, summarize what you did rather than streaming every detail.`;\n\n/**\n * Defer plugin construction until the Nitro plugin actually fires so the\n * config-aware system prompt resolves AFTER `setupDispatch(config)` has\n * stamped the active config (plugin module load order is not guaranteed).\n */\nconst dispatchIntegrationsPlugin = async (nitroApp: any) => {\n const { integrations = {} } = getDispatchConfig();\n const promptOverride = integrations.systemPrompt;\n const systemPrompt =\n typeof promptOverride === \"string\"\n ? promptOverride\n : typeof promptOverride === \"function\"\n ? promptOverride(DISPATCH_INTEGRATION_SYSTEM_PROMPT)\n : DISPATCH_INTEGRATION_SYSTEM_PROMPT;\n\n const plugin = createIntegrationsPlugin({\n appId: \"dispatch\",\n actions: dispatchIntegrationActions,\n resolveOwner: resolveDispatchOwner,\n beforeProcess: beforeDispatchProcess,\n systemPrompt,\n // Inherit the framework default (claude-sonnet-4-6 from\n // packages/core/src/integrations/plugin.ts). Haiku was tried for latency\n // but hallucinated URLs/IDs after delegated call-agent results\n // (e.g. inventing `https://slides.workspace.com/deck/builder-io-deck-2024`).\n });\n\n return plugin(nitroApp);\n};\n\nexport default dispatchIntegrationsPlugin;\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/dispatch",
3
- "version": "0.13.9",
3
+ "version": "0.13.11",
4
4
  "description": "Dispatch — workspace control plane for agent-native apps. Vault, integrations, destinations, scheduled jobs, and cross-app delegation, shipped as a single drop-in package.",
5
5
  "homepage": "https://github.com/BuilderIO/agent-native#readme",
6
6
  "bugs": {
@@ -86,15 +86,15 @@
86
86
  "@types/node": "^24.2.1",
87
87
  "@types/react": "^19.2.14",
88
88
  "@types/react-dom": "^19.2.3",
89
- "@typescript/native-preview": "7.0.0-dev.20260624.1",
90
89
  "react": "^19.2.7",
91
90
  "react-dom": "^19.2.7",
92
91
  "react-router": "^8.1.0",
93
92
  "tsc-alias": "^1.8.10",
94
- "typescript": "^6.0.3",
93
+ "typescript": "npm:@typescript/typescript6@^6.0.2",
94
+ "typescript-7": "npm:typescript@^7.0.2",
95
95
  "vite": "8.1.0",
96
96
  "vitest": "^4.1.5",
97
- "@agent-native/core": "0.90.9"
97
+ "@agent-native/core": "0.91.2"
98
98
  },
99
99
  "peerDependencies": {
100
100
  "@agent-native/core": ">=0.8.0",
@@ -103,9 +103,9 @@
103
103
  "react-router": ">=8"
104
104
  },
105
105
  "scripts": {
106
- "build": "tsgo && tsc-alias --resolve-full-paths",
107
- "dev": "tsgo --watch & tsc-alias --watch --resolve-full-paths",
108
- "typecheck": "tsgo --noEmit",
106
+ "build": "tsc && tsc-alias --resolve-full-paths",
107
+ "dev": "tsc --watch & tsc-alias --watch --resolve-full-paths",
108
+ "typecheck": "tsc --noEmit",
109
109
  "test": "vitest --run src --passWithNoTests"
110
110
  }
111
111
  }
@@ -27,7 +27,7 @@ export default defineAction({
27
27
  .string()
28
28
  .optional()
29
29
  .describe(
30
- "Named dispatch view to navigate to. Built-in views include chat, overview, apps, metrics, new-app, vault, integrations, messaging, workspace, agents, destinations, identities, approvals, audit, thread-debug, dreams, and team. Generated Dispatch extension tabs can also use their nav item id.",
30
+ "Named dispatch view to navigate to. Built-in views include chat, overview, apps, metrics, new-app, vault, integrations, messaging, workspace, agents, destinations, identities, approvals, automations, audit, thread-debug, dreams, and team. Generated Dispatch extension tabs can also use their nav item id.",
31
31
  ),
32
32
  path: z.string().optional().describe("URL path to navigate to"),
33
33
  threadId: z
@@ -12,9 +12,9 @@ import {
12
12
  AlertDialogHeader,
13
13
  AlertDialogTitle,
14
14
  AlertDialogTrigger,
15
- } from "@/components/ui/alert-dialog";
16
- import { Button } from "@/components/ui/button";
17
- import { Input } from "@/components/ui/input";
15
+ } from "./ui/alert-dialog";
16
+ import { Button } from "./ui/button";
17
+ import { Input } from "./ui/input";
18
18
 
19
19
  export interface ConnectedAgent {
20
20
  id: string;
@@ -8,13 +8,9 @@ import {
8
8
  import { useMemo, useState, type ReactNode } from "react";
9
9
  import { toast } from "sonner";
10
10
 
11
- import { Button } from "@/components/ui/button";
12
- import {
13
- Popover,
14
- PopoverContent,
15
- PopoverTrigger,
16
- } from "@/components/ui/popover";
17
- import { Skeleton } from "@/components/ui/skeleton";
11
+ import { Button } from "./ui/button";
12
+ import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
13
+ import { Skeleton } from "./ui/skeleton";
18
14
 
19
15
  interface VaultSecret {
20
16
  id: string;
@@ -20,12 +20,8 @@ import {
20
20
  } from "@tabler/icons-react";
21
21
  import { useEffect, useMemo, useState, type ReactNode } from "react";
22
22
 
23
- import { Button } from "@/components/ui/button";
24
- import {
25
- Popover,
26
- PopoverContent,
27
- PopoverTrigger,
28
- } from "@/components/ui/popover";
23
+ import { Button } from "./ui/button";
24
+ import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
29
25
 
30
26
  interface VaultSecretOption {
31
27
  id: string;
@@ -3,6 +3,7 @@ import {
3
3
  isInBuilderFrame,
4
4
  useActionQuery,
5
5
  useChatModels,
6
+ useT,
6
7
  } from "@agent-native/core/client";
7
8
  import {
8
9
  IconArrowUpRight,
@@ -13,13 +14,13 @@ import {
13
14
  import type { ReactNode } from "react";
14
15
  import { Link, useNavigate } from "react-router";
15
16
 
16
- import { CreateAppPopover } from "@/components/create-app-popover";
17
- import { DispatchShell } from "@/components/dispatch-shell";
18
- import { Button } from "@/components/ui/button";
19
- import { Skeleton } from "@/components/ui/skeleton";
20
- import { WorkspaceAppCard } from "@/components/workspace-app-card";
21
- import { submitOverviewPrompt } from "@/lib/overview-chat";
22
- import type { WorkspaceAppSummary } from "@/lib/workspace-apps";
17
+ import { submitOverviewPrompt } from "../lib/overview-chat";
18
+ import type { WorkspaceAppSummary } from "../lib/workspace-apps";
19
+ import { CreateAppPopover } from "./create-app-popover";
20
+ import { DispatchShell } from "./dispatch-shell";
21
+ import { Button } from "./ui/button";
22
+ import { Skeleton } from "./ui/skeleton";
23
+ import { WorkspaceAppCard } from "./workspace-app-card";
23
24
 
24
25
  const PROMPT_SUGGESTIONS = [
25
26
  "Summarize the current workspace health",
@@ -27,6 +28,15 @@ const PROMPT_SUGGESTIONS = [
27
28
  "Check which agents can help with analytics",
28
29
  ];
29
30
 
31
+ const WORKSPACE_LINKS = [
32
+ { to: "/automations", labelKey: "dispatch.nav.automations" },
33
+ { to: "/approvals", labelKey: "dispatch.nav.approvals" },
34
+ { to: "/destinations", labelKey: "dispatch.nav.delivery" },
35
+ { to: "/agents", labelKey: "dispatch.nav.agents" },
36
+ { to: "/vault", labelKey: "dispatch.nav.vault" },
37
+ { to: "/audit", labelKey: "dispatch.nav.audit" },
38
+ ] as const;
39
+
30
40
  function SectionHeader({
31
41
  icon: Icon,
32
42
  title,
@@ -118,6 +128,36 @@ function CommandPanel() {
118
128
  );
119
129
  }
120
130
 
131
+ function WorkspaceLinks() {
132
+ const t = useT();
133
+
134
+ return (
135
+ <nav
136
+ aria-label={t("dispatch.pages.workspaceShortcutsAria")}
137
+ className="flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground"
138
+ >
139
+ <span className="font-medium text-foreground/70">
140
+ {t("dispatch.pages.also")}
141
+ </span>
142
+ {WORKSPACE_LINKS.map((item, index) => (
143
+ <span key={item.to} className="inline-flex items-center gap-x-3">
144
+ {index > 0 ? (
145
+ <span aria-hidden className="text-border">
146
+ ·
147
+ </span>
148
+ ) : null}
149
+ <Link
150
+ to={item.to}
151
+ className="transition hover:text-foreground hover:underline underline-offset-4"
152
+ >
153
+ {t(item.labelKey)}
154
+ </Link>
155
+ </span>
156
+ ))}
157
+ </nav>
158
+ );
159
+ }
160
+
121
161
  function AppsPanel({
122
162
  apps,
123
163
  isLoading,
@@ -184,6 +224,7 @@ export function DispatchControlPlane() {
184
224
  >
185
225
  <div className="flex flex-col gap-6">
186
226
  <CommandPanel />
227
+ <WorkspaceLinks />
187
228
  <AppsPanel apps={workspaceApps ?? []} isLoading={appsLoading} />
188
229
  </div>
189
230
  </DispatchShell>
@@ -2,12 +2,8 @@ import { useT } from "@agent-native/core/client";
2
2
  import { IconInfoCircle } from "@tabler/icons-react";
3
3
  import { type ReactNode } from "react";
4
4
 
5
- import { useSetPageTitle } from "@/components/layout/HeaderActions";
6
- import {
7
- Popover,
8
- PopoverContent,
9
- PopoverTrigger,
10
- } from "@/components/ui/popover";
5
+ import { useSetPageTitle } from "./layout/HeaderActions";
6
+ import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover";
11
7
 
12
8
  /**
13
9
  * DispatchShell renders the per-page title (with an optional click-to-open
@@ -3,8 +3,7 @@ import { RunsTray } from "@agent-native/core/client/progress";
3
3
  import { IconLayoutSidebar } from "@tabler/icons-react";
4
4
  import { useLocation, useNavigate } from "react-router";
5
5
 
6
- import { Button } from "@/components/ui/button";
7
-
6
+ import { Button } from "../ui/button";
8
7
  import { useHeaderTitle, useHeaderActions } from "./HeaderActions";
9
8
 
10
9
  const pageTitles: Record<string, string> = {
@@ -18,6 +17,7 @@ const pageTitles: Record<string, string> = {
18
17
  "/destinations": "Destinations",
19
18
  "/identities": "Identities",
20
19
  "/approvals": "Approvals",
20
+ "/automations": "Automations",
21
21
  "/audit": "Audit",
22
22
  "/settings": "Settings",
23
23
  };
@@ -37,6 +37,7 @@ import {
37
37
  IconLayoutSidebarLeftExpand,
38
38
  IconPuzzle,
39
39
  IconSettings,
40
+ IconSettingsAutomation,
40
41
  IconShieldCheck,
41
42
  } from "@tabler/icons-react";
42
43
  import {
@@ -50,26 +51,16 @@ import {
50
51
  } from "react";
51
52
  import { NavLink, useLocation, useNavigate } from "react-router";
52
53
 
54
+ import { cn } from "../../lib/utils";
53
55
  import {
54
56
  DropdownMenu,
55
57
  DropdownMenuContent,
56
58
  DropdownMenuItem,
57
59
  DropdownMenuTrigger,
58
- } from "@/components/ui/dropdown-menu";
59
- import { Input } from "@/components/ui/input";
60
- import {
61
- Sheet,
62
- SheetContent,
63
- SheetDescription,
64
- SheetTitle,
65
- } from "@/components/ui/sheet";
66
- import {
67
- Tooltip,
68
- TooltipContent,
69
- TooltipTrigger,
70
- } from "@/components/ui/tooltip";
71
- import { cn } from "@/lib/utils";
72
-
60
+ } from "../ui/dropdown-menu";
61
+ import { Input } from "../ui/input";
62
+ import { Sheet, SheetContent, SheetDescription, SheetTitle } from "../ui/sheet";
63
+ import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
73
64
  import { Header } from "./Header";
74
65
  import { HeaderActionsProvider } from "./HeaderActions";
75
66
 
@@ -188,6 +179,13 @@ const OPERATIONS_NAV_ITEMS = [
188
179
  icon: IconShieldCheck,
189
180
  section: "operations",
190
181
  },
182
+ {
183
+ id: "automations",
184
+ to: "/automations",
185
+ label: "Automations",
186
+ icon: IconSettingsAutomation,
187
+ section: "operations",
188
+ },
191
189
  {
192
190
  id: "audit",
193
191
  to: "/audit",
@@ -14,18 +14,14 @@ import {
14
14
  import { useEffect, useMemo, useState } from "react";
15
15
  import { toast } from "sonner";
16
16
 
17
- import { Button } from "@/components/ui/button";
17
+ import { Button } from "./ui/button";
18
18
  import {
19
19
  Collapsible,
20
20
  CollapsibleContent,
21
21
  CollapsibleTrigger,
22
- } from "@/components/ui/collapsible";
23
- import { Input } from "@/components/ui/input";
24
- import {
25
- Tooltip,
26
- TooltipContent,
27
- TooltipTrigger,
28
- } from "@/components/ui/tooltip";
22
+ } from "./ui/collapsible";
23
+ import { Input } from "./ui/input";
24
+ import { Tooltip, TooltipContent, TooltipTrigger } from "./ui/tooltip";
29
25
 
30
26
  interface EnvStatus {
31
27
  key: string;
@@ -0,0 +1,166 @@
1
+ import { useChangeVersions, useT } from "@agent-native/core/client";
2
+ import { IconAlertTriangle, IconPlayerPlay } from "@tabler/icons-react";
3
+ import { useQuery } from "@tanstack/react-query";
4
+
5
+ import {
6
+ formatQueueAgeSeconds,
7
+ getDispatchTaskQueueStats,
8
+ type TaskQueueStats,
9
+ ZERO_TASK_QUEUE_STATS,
10
+ } from "../lib/task-queue";
11
+ import { cn } from "../lib/utils";
12
+ import { Skeleton } from "./ui/skeleton";
13
+
14
+ const TASK_QUEUE_QUERY_KEY = ["dispatch-task-queue"] as const;
15
+
16
+ function formatNumber(value: number): string {
17
+ return new Intl.NumberFormat(undefined, {
18
+ notation: value >= 10_000 ? "compact" : "standard",
19
+ }).format(value);
20
+ }
21
+
22
+ function useTaskQueueStats() {
23
+ const version = useChangeVersions(["action", "screen-refresh"]);
24
+ return useQuery<TaskQueueStats>({
25
+ queryKey: [...TASK_QUEUE_QUERY_KEY, version],
26
+ queryFn: getDispatchTaskQueueStats,
27
+ placeholderData: (prev) => prev,
28
+ refetchInterval: 15_000,
29
+ staleTime: 5_000,
30
+ });
31
+ }
32
+
33
+ function QueueCell({
34
+ label,
35
+ value,
36
+ danger,
37
+ }: {
38
+ label: string;
39
+ value: number;
40
+ danger?: boolean;
41
+ }) {
42
+ return (
43
+ <div className="rounded-md border bg-background px-2 py-2">
44
+ <div
45
+ className={cn(
46
+ "text-lg font-semibold text-foreground",
47
+ danger && "text-destructive",
48
+ )}
49
+ >
50
+ {formatNumber(value)}
51
+ </div>
52
+ <div className="truncate text-[11px] text-muted-foreground">{label}</div>
53
+ </div>
54
+ );
55
+ }
56
+
57
+ export function TaskQueueHealth() {
58
+ const t = useT();
59
+ const query = useTaskQueueStats();
60
+ const taskQueue = query.data ?? ZERO_TASK_QUEUE_STATS;
61
+ const hasFailure = taskQueue.failed_last_hour > 0;
62
+ const hasBacklog =
63
+ taskQueue.pending > 5 || taskQueue.oldest_pending_age_seconds > 300;
64
+ const oldestAge = formatQueueAgeSeconds(taskQueue.oldest_pending_age_seconds);
65
+ const oldestAgeLabel =
66
+ oldestAge === "none" ? t("dispatch.pages.queueAgeNone") : oldestAge;
67
+
68
+ return (
69
+ <section className="rounded-lg border bg-card p-4">
70
+ <div className="flex min-w-0 items-center justify-between gap-3">
71
+ <div className="flex min-w-0 items-center gap-2">
72
+ <IconPlayerPlay
73
+ size={16}
74
+ className="shrink-0 text-muted-foreground"
75
+ />
76
+ <div className="min-w-0">
77
+ <h2 className="truncate text-sm font-semibold text-foreground">
78
+ {t("dispatch.pages.deliveryQueue")}
79
+ </h2>
80
+ <p className="mt-0.5 truncate text-xs text-muted-foreground">
81
+ {hasFailure
82
+ ? t("dispatch.pages.failedLastHour", {
83
+ count: taskQueue.failed_last_hour,
84
+ })
85
+ : t("dispatch.pages.processingCount", {
86
+ count: taskQueue.processing,
87
+ })}
88
+ </p>
89
+ </div>
90
+ </div>
91
+ </div>
92
+
93
+ {query.isLoading && !query.data ? (
94
+ <div className="mt-3 grid grid-cols-2 gap-2 sm:grid-cols-4">
95
+ {Array.from({ length: 4 }).map((_, index) => (
96
+ <Skeleton key={index} className="h-14 rounded-md" />
97
+ ))}
98
+ </div>
99
+ ) : (
100
+ <>
101
+ <div className="mt-3 grid grid-cols-2 gap-2 sm:grid-cols-4">
102
+ <QueueCell
103
+ label={t("dispatch.pages.queued")}
104
+ value={taskQueue.pending}
105
+ />
106
+ <QueueCell
107
+ label={t("dispatch.pages.active")}
108
+ value={taskQueue.processing}
109
+ />
110
+ <QueueCell
111
+ label={t("dispatch.pages.done1h")}
112
+ value={taskQueue.completed_last_hour}
113
+ />
114
+ <QueueCell
115
+ label={t("dispatch.pages.failed1h")}
116
+ value={taskQueue.failed_last_hour}
117
+ danger={hasFailure}
118
+ />
119
+ </div>
120
+
121
+ <div
122
+ className={cn(
123
+ "mt-3 rounded-md border px-3 py-2 text-xs",
124
+ hasFailure
125
+ ? "border-destructive/30 bg-destructive/5 text-destructive"
126
+ : hasBacklog
127
+ ? "border-amber-500/30 bg-amber-500/10 text-amber-700 dark:text-amber-300"
128
+ : "bg-muted/20 text-muted-foreground",
129
+ )}
130
+ >
131
+ {t("dispatch.pages.oldestQueued", { age: oldestAgeLabel })}
132
+ </div>
133
+
134
+ {hasFailure ? (
135
+ <div className="mt-3 flex items-start gap-2 rounded-md border border-destructive/30 bg-destructive/5 px-3 py-2 text-xs text-destructive">
136
+ <IconAlertTriangle size={14} className="mt-0.5 shrink-0" />
137
+ <span>{t("dispatch.pages.queueFailureHint")}</span>
138
+ </div>
139
+ ) : null}
140
+
141
+ {taskQueue.recent_failures.length > 0 ? (
142
+ <div className="mt-3 divide-y rounded-md border">
143
+ {taskQueue.recent_failures.slice(0, 3).map((failure) => (
144
+ <div key={failure.id} className="px-3 py-2">
145
+ <div className="flex items-center justify-between gap-3 text-xs">
146
+ <span className="font-medium text-foreground">
147
+ {failure.platform || t("dispatch.pages.unknownPlatform")}
148
+ </span>
149
+ <span className="text-muted-foreground">
150
+ {t("dispatch.pages.attemptsCount", {
151
+ count: failure.attempts,
152
+ })}
153
+ </span>
154
+ </div>
155
+ <div className="mt-1 truncate text-xs text-muted-foreground">
156
+ {failure.error || t("dispatch.pages.noErrorMessage")}
157
+ </div>
158
+ </div>
159
+ ))}
160
+ </div>
161
+ ) : null}
162
+ </>
163
+ )}
164
+ </section>
165
+ );
166
+ }
@@ -2,7 +2,7 @@ import * as AccordionPrimitive from "@radix-ui/react-accordion";
2
2
  import { IconChevronDown } from "@tabler/icons-react";
3
3
  import * as React from "react";
4
4
 
5
- import { cn } from "@/lib/utils";
5
+ import { cn } from "../../lib/utils";
6
6
 
7
7
  const Accordion = AccordionPrimitive.Root;
8
8
 
@@ -1,8 +1,8 @@
1
1
  import * as AlertDialogPrimitive from "@radix-ui/react-alert-dialog";
2
2
  import * as React from "react";
3
3
 
4
- import { buttonVariants } from "@/components/ui/button";
5
- import { cn } from "@/lib/utils";
4
+ import { cn } from "../../lib/utils";
5
+ import { buttonVariants } from "./button";
6
6
 
7
7
  const AlertDialog = AlertDialogPrimitive.Root;
8
8
 
@@ -1,7 +1,7 @@
1
1
  import { cva, type VariantProps } from "class-variance-authority";
2
2
  import * as React from "react";
3
3
 
4
- import { cn } from "@/lib/utils";
4
+ import { cn } from "../../lib/utils";
5
5
 
6
6
  const alertVariants = cva(
7
7
  "relative w-full rounded-lg border p-4 [&>svg~*]:pl-7 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground",
@@ -1,7 +1,7 @@
1
1
  import * as AvatarPrimitive from "@radix-ui/react-avatar";
2
2
  import * as React from "react";
3
3
 
4
- import { cn } from "@/lib/utils";
4
+ import { cn } from "../../lib/utils";
5
5
 
6
6
  const Avatar = React.forwardRef<
7
7
  React.ElementRef<typeof AvatarPrimitive.Root>,
@@ -1,7 +1,7 @@
1
1
  import { cva, type VariantProps } from "class-variance-authority";
2
2
  import * as React from "react";
3
3
 
4
- import { cn } from "@/lib/utils";
4
+ import { cn } from "../../lib/utils";
5
5
 
6
6
  const badgeVariants = cva(
7
7
  "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
@@ -2,7 +2,7 @@ import { Slot } from "@radix-ui/react-slot";
2
2
  import { IconChevronRight, IconDots } from "@tabler/icons-react";
3
3
  import * as React from "react";
4
4
 
5
- import { cn } from "@/lib/utils";
5
+ import { cn } from "../../lib/utils";
6
6
 
7
7
  const Breadcrumb = React.forwardRef<
8
8
  HTMLElement,
@@ -2,7 +2,7 @@ import { Slot } from "@radix-ui/react-slot";
2
2
  import { cva, type VariantProps } from "class-variance-authority";
3
3
  import * as React from "react";
4
4
 
5
- import { cn } from "@/lib/utils";
5
+ import { cn } from "../../lib/utils";
6
6
 
7
7
  const buttonVariants = cva(
8
8
  "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
@@ -2,8 +2,8 @@ import { IconChevronLeft, IconChevronRight } from "@tabler/icons-react";
2
2
  import * as React from "react";
3
3
  import { DayPicker } from "react-day-picker";
4
4
 
5
- import { buttonVariants } from "@/components/ui/button";
6
- import { cn } from "@/lib/utils";
5
+ import { cn } from "../../lib/utils";
6
+ import { buttonVariants } from "./button";
7
7
 
8
8
  export type CalendarProps = React.ComponentProps<typeof DayPicker>;
9
9
 
@@ -1,6 +1,6 @@
1
1
  import * as React from "react";
2
2
 
3
- import { cn } from "@/lib/utils";
3
+ import { cn } from "../../lib/utils";
4
4
 
5
5
  const Card = React.forwardRef<
6
6
  HTMLDivElement,
@@ -4,8 +4,8 @@ import useEmblaCarousel, {
4
4
  } from "embla-carousel-react";
5
5
  import * as React from "react";
6
6
 
7
- import { Button } from "@/components/ui/button";
8
- import { cn } from "@/lib/utils";
7
+ import { cn } from "../../lib/utils";
8
+ import { Button } from "./button";
9
9
 
10
10
  type CarouselApi = UseEmblaCarouselType[1];
11
11
  type UseCarouselParameters = Parameters<typeof useEmblaCarousel>;