@agent-native/dispatch 0.41.1-nightly-20261104110058 → 0.42.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (195) hide show
  1. package/CHANGELOG.md +26 -55
  2. package/dist/actions/get-hosted-apps-settings.d.ts +5 -0
  3. package/dist/actions/get-hosted-apps-settings.d.ts.map +1 -0
  4. package/dist/actions/get-hosted-apps-settings.js +25 -0
  5. package/dist/actions/get-hosted-apps-settings.js.map +1 -0
  6. package/dist/actions/index.d.ts.map +1 -1
  7. package/dist/actions/index.js +4 -0
  8. package/dist/actions/index.js.map +1 -1
  9. package/dist/actions/list-connected-agents.d.ts +1 -0
  10. package/dist/actions/list-connected-agents.js +11 -10
  11. package/dist/actions/list-connected-agents.js.map +1 -1
  12. package/dist/actions/list_apps.js +3 -2
  13. package/dist/actions/list_apps.js.map +1 -1
  14. package/dist/actions/provider-api-register.d.ts +10 -10
  15. package/dist/actions/set-hosted-apps-settings.d.ts +7 -0
  16. package/dist/actions/set-hosted-apps-settings.d.ts.map +1 -0
  17. package/dist/actions/set-hosted-apps-settings.js +35 -0
  18. package/dist/actions/set-hosted-apps-settings.js.map +1 -0
  19. package/dist/components/app-card-tiers.d.ts +13 -0
  20. package/dist/components/app-card-tiers.d.ts.map +1 -0
  21. package/dist/components/app-card-tiers.js +22 -0
  22. package/dist/components/app-card-tiers.js.map +1 -0
  23. package/dist/components/app-list-row.d.ts +12 -4
  24. package/dist/components/app-list-row.d.ts.map +1 -1
  25. package/dist/components/app-list-row.js +19 -4
  26. package/dist/components/app-list-row.js.map +1 -1
  27. package/dist/components/app-open-actions.d.ts +4 -0
  28. package/dist/components/app-open-actions.d.ts.map +1 -1
  29. package/dist/components/app-open-actions.js +9 -0
  30. package/dist/components/app-open-actions.js.map +1 -1
  31. package/dist/components/app-pin-button.d.ts +9 -0
  32. package/dist/components/app-pin-button.d.ts.map +1 -0
  33. package/dist/components/app-pin-button.js +29 -0
  34. package/dist/components/app-pin-button.js.map +1 -0
  35. package/dist/components/app-tier-sections.d.ts +27 -0
  36. package/dist/components/app-tier-sections.d.ts.map +1 -0
  37. package/dist/components/app-tier-sections.js +44 -0
  38. package/dist/components/app-tier-sections.js.map +1 -0
  39. package/dist/components/chat-home-apps.d.ts.map +1 -1
  40. package/dist/components/chat-home-apps.js +19 -26
  41. package/dist/components/chat-home-apps.js.map +1 -1
  42. package/dist/components/connected-app-card.d.ts +3 -1
  43. package/dist/components/connected-app-card.d.ts.map +1 -1
  44. package/dist/components/connected-app-card.js +6 -3
  45. package/dist/components/connected-app-card.js.map +1 -1
  46. package/dist/components/create-app-popover.d.ts +4 -0
  47. package/dist/components/create-app-popover.d.ts.map +1 -1
  48. package/dist/components/create-app-popover.js +6 -0
  49. package/dist/components/create-app-popover.js.map +1 -1
  50. package/dist/components/dispatch-control-plane.d.ts.map +1 -1
  51. package/dist/components/dispatch-control-plane.js +25 -35
  52. package/dist/components/dispatch-control-plane.js.map +1 -1
  53. package/dist/components/hosted-apps-settings-group.d.ts +6 -0
  54. package/dist/components/hosted-apps-settings-group.d.ts.map +1 -0
  55. package/dist/components/hosted-apps-settings-group.js +76 -0
  56. package/dist/components/hosted-apps-settings-group.js.map +1 -0
  57. package/dist/components/layout/Layout.d.ts +6 -4
  58. package/dist/components/layout/Layout.d.ts.map +1 -1
  59. package/dist/components/layout/Layout.js +97 -80
  60. package/dist/components/layout/Layout.js.map +1 -1
  61. package/dist/components/workspace-app-card.d.ts.map +1 -1
  62. package/dist/components/workspace-app-card.js +9 -20
  63. package/dist/components/workspace-app-card.js.map +1 -1
  64. package/dist/components/workspace-app-layout-notice.d.ts +9 -0
  65. package/dist/components/workspace-app-layout-notice.d.ts.map +1 -0
  66. package/dist/components/workspace-app-layout-notice.js +27 -0
  67. package/dist/components/workspace-app-layout-notice.js.map +1 -0
  68. package/dist/components/workspace-app-search.d.ts.map +1 -1
  69. package/dist/components/workspace-app-search.js +11 -5
  70. package/dist/components/workspace-app-search.js.map +1 -1
  71. package/dist/components/workspace-template-card.d.ts.map +1 -1
  72. package/dist/components/workspace-template-card.js +7 -2
  73. package/dist/components/workspace-template-card.js.map +1 -1
  74. package/dist/db/index.d.ts.map +1 -1
  75. package/dist/db/index.js +1 -0
  76. package/dist/db/index.js.map +1 -1
  77. package/dist/lib/other-apps.d.ts +10 -1
  78. package/dist/lib/other-apps.d.ts.map +1 -1
  79. package/dist/lib/other-apps.js +7 -1
  80. package/dist/lib/other-apps.js.map +1 -1
  81. package/dist/lib/workspace-app-layout.d.ts +62 -6
  82. package/dist/lib/workspace-app-layout.d.ts.map +1 -1
  83. package/dist/lib/workspace-app-layout.js +196 -41
  84. package/dist/lib/workspace-app-layout.js.map +1 -1
  85. package/dist/lib/workspace-app-lists.d.ts +17 -0
  86. package/dist/lib/workspace-app-lists.d.ts.map +1 -0
  87. package/dist/lib/workspace-app-lists.js +21 -0
  88. package/dist/lib/workspace-app-lists.js.map +1 -0
  89. package/dist/lib/workspace-apps.d.ts +3 -1
  90. package/dist/lib/workspace-apps.d.ts.map +1 -1
  91. package/dist/lib/workspace-apps.js +5 -1
  92. package/dist/lib/workspace-apps.js.map +1 -1
  93. package/dist/routes/pages/apps.d.ts.map +1 -1
  94. package/dist/routes/pages/apps.js +23 -35
  95. package/dist/routes/pages/apps.js.map +1 -1
  96. package/dist/routes/pages/settings-page.d.ts.map +1 -1
  97. package/dist/routes/pages/settings-page.js +24 -3
  98. package/dist/routes/pages/settings-page.js.map +1 -1
  99. package/dist/server/lib/app-creation-store.d.ts.map +1 -1
  100. package/dist/server/lib/app-creation-store.js +13 -18
  101. package/dist/server/lib/app-creation-store.js.map +1 -1
  102. package/dist/server/lib/app-roles.d.ts +2 -0
  103. package/dist/server/lib/app-roles.d.ts.map +1 -1
  104. package/dist/server/lib/app-roles.js +26 -7
  105. package/dist/server/lib/app-roles.js.map +1 -1
  106. package/dist/server/lib/hosted-apps-settings-schema.d.ts +6 -0
  107. package/dist/server/lib/hosted-apps-settings-schema.d.ts.map +1 -0
  108. package/dist/server/lib/hosted-apps-settings-schema.js +6 -0
  109. package/dist/server/lib/hosted-apps-settings-schema.js.map +1 -0
  110. package/dist/server/lib/hosted-apps-settings.d.ts +27 -0
  111. package/dist/server/lib/hosted-apps-settings.d.ts.map +1 -0
  112. package/dist/server/lib/hosted-apps-settings.js +72 -0
  113. package/dist/server/lib/hosted-apps-settings.js.map +1 -0
  114. package/dist/server/plugins/agent-chat.js +6 -3
  115. package/dist/server/plugins/agent-chat.js.map +1 -1
  116. package/dist/server/plugins/integrations.js +3 -3
  117. package/dist/server/plugins/integrations.js.map +1 -1
  118. package/dist/shared/hosted-apps-settings.d.ts +3 -0
  119. package/dist/shared/hosted-apps-settings.d.ts.map +1 -0
  120. package/dist/shared/hosted-apps-settings.js +3 -0
  121. package/dist/shared/hosted-apps-settings.js.map +1 -0
  122. package/package.json +4 -4
  123. package/src/actions/get-hosted-apps-settings.ts +29 -0
  124. package/src/actions/hosted-apps-settings.spec.ts +100 -0
  125. package/src/actions/index.ts +4 -0
  126. package/src/actions/list-connected-agents.spec.ts +67 -0
  127. package/src/actions/list-connected-agents.ts +14 -15
  128. package/src/actions/list_apps.spec.ts +16 -0
  129. package/src/actions/list_apps.ts +5 -2
  130. package/src/actions/set-hosted-apps-settings.ts +42 -0
  131. package/src/components/app-card-tiers.tsx +85 -0
  132. package/src/components/app-list-row.spec.tsx +54 -0
  133. package/src/components/app-list-row.tsx +50 -8
  134. package/src/components/app-open-actions.tsx +16 -0
  135. package/src/components/app-pin-button.spec.tsx +68 -0
  136. package/src/components/app-pin-button.tsx +66 -0
  137. package/src/components/app-tier-sections.spec.tsx +155 -0
  138. package/src/components/app-tier-sections.tsx +158 -0
  139. package/src/components/chat-home-apps.spec.tsx +135 -29
  140. package/src/components/chat-home-apps.tsx +115 -111
  141. package/src/components/connected-app-card.spec.tsx +18 -2
  142. package/src/components/connected-app-card.tsx +18 -1
  143. package/src/components/create-app-popover.tsx +22 -0
  144. package/src/components/dispatch-control-plane.spec.tsx +89 -20
  145. package/src/components/dispatch-control-plane.tsx +91 -125
  146. package/src/components/hosted-apps-settings-group.spec.tsx +218 -0
  147. package/src/components/hosted-apps-settings-group.tsx +189 -0
  148. package/src/components/layout/Layout.spec.tsx +356 -49
  149. package/src/components/layout/Layout.tsx +189 -142
  150. package/src/components/workspace-app-card.spec.tsx +33 -29
  151. package/src/components/workspace-app-card.tsx +13 -31
  152. package/src/components/workspace-app-layout-notice.spec.tsx +106 -0
  153. package/src/components/workspace-app-layout-notice.tsx +65 -0
  154. package/src/components/workspace-app-search.spec.tsx +53 -0
  155. package/src/components/workspace-app-search.tsx +39 -32
  156. package/src/components/workspace-template-card.spec.tsx +10 -0
  157. package/src/components/workspace-template-card.tsx +7 -2
  158. package/src/db/index.spec.ts +9 -0
  159. package/src/db/index.ts +1 -0
  160. package/src/lib/other-apps.spec.ts +25 -0
  161. package/src/lib/other-apps.ts +9 -0
  162. package/src/lib/workspace-app-layout.spec.ts +61 -3
  163. package/src/lib/workspace-app-layout.store.spec.tsx +261 -0
  164. package/src/lib/workspace-app-layout.ts +265 -48
  165. package/src/lib/workspace-app-lists.ts +32 -0
  166. package/src/lib/workspace-apps.ts +10 -2
  167. package/src/routes/pages/apps.spec.tsx +133 -5
  168. package/src/routes/pages/apps.tsx +79 -140
  169. package/src/routes/pages/chat.spec.tsx +66 -49
  170. package/src/routes/pages/settings-page.tsx +31 -3
  171. package/src/routes/pages/settings.spec.tsx +37 -0
  172. package/src/server/lib/app-creation-store.spec.ts +14 -0
  173. package/src/server/lib/app-creation-store.ts +16 -29
  174. package/src/server/lib/app-creation-store.write-observation.spec.ts +216 -0
  175. package/src/server/lib/app-roles.spec.ts +48 -1
  176. package/src/server/lib/app-roles.ts +44 -12
  177. package/src/server/lib/hosted-apps-settings-schema.ts +6 -0
  178. package/src/server/lib/hosted-apps-settings.spec.ts +154 -0
  179. package/src/server/lib/hosted-apps-settings.ts +108 -0
  180. package/src/server/plugins/agent-chat.spec.ts +16 -0
  181. package/src/server/plugins/agent-chat.ts +7 -2
  182. package/src/server/plugins/integrations.ts +3 -3
  183. package/src/shared/hosted-apps-settings.ts +3 -0
  184. package/src/styles/dispatch.css +40 -11
  185. package/dist/components/available-apps-section.d.ts +0 -9
  186. package/dist/components/available-apps-section.d.ts.map +0 -1
  187. package/dist/components/available-apps-section.js +0 -97
  188. package/dist/components/available-apps-section.js.map +0 -1
  189. package/dist/components/other-apps-section.d.ts +0 -35
  190. package/dist/components/other-apps-section.d.ts.map +0 -1
  191. package/dist/components/other-apps-section.js +0 -79
  192. package/dist/components/other-apps-section.js.map +0 -1
  193. package/src/components/available-apps-section.tsx +0 -239
  194. package/src/components/other-apps-section.spec.ts +0 -50
  195. package/src/components/other-apps-section.tsx +0 -254
@@ -1,3 +1,5 @@
1
1
  import type { ActionRunContext } from "@agent-native/core/action";
2
2
  export declare function authorizeDispatchAdmin(_args: unknown, ctx?: ActionRunContext): Promise<void>;
3
+ /** Organization owners and admins only; Dispatch admin roles do not count. */
4
+ export declare function authorizeOrgOwnerOrAdmin(_args: unknown, ctx?: ActionRunContext): Promise<void>;
3
5
  //# sourceMappingURL=app-roles.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"app-roles.d.ts","sourceRoot":"","sources":["../../../src/server/lib/app-roles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAwBlE,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,OAAO,EACd,GAAG,CAAC,EAAE,gBAAgB,GACrB,OAAO,CAAC,IAAI,CAAC,CAoCf"}
1
+ {"version":3,"file":"app-roles.d.ts","sourceRoot":"","sources":["../../../src/server/lib/app-roles.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAiElE,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,OAAO,EACd,GAAG,CAAC,EAAE,gBAAgB,GACrB,OAAO,CAAC,IAAI,CAAC,CAUf;AAED,8EAA8E;AAC9E,wBAAsB,wBAAwB,CAC5C,KAAK,EAAE,OAAO,EACd,GAAG,CAAC,EAAE,gBAAgB,GACrB,OAAO,CAAC,IAAI,CAAC,CAWf"}
@@ -8,14 +8,18 @@ function getDispatchAccess() {
8
8
  allowOrgAdmins: true,
9
9
  }));
10
10
  }
11
- export async function authorizeDispatchAdmin(_args, ctx) {
11
+ /**
12
+ * The caller's active membership in the request org, or null when there is no
13
+ * org to check (a user without one, or standalone Dispatch with no org tables).
14
+ */
15
+ async function requireActiveMembership(ctx, action) {
12
16
  const email = ctx?.userEmail !== undefined ? ctx.userEmail : getRequestUserEmail();
13
17
  const orgId = ctx?.orgId !== undefined ? ctx.orgId : getRequestOrgId();
14
18
  if (!email?.trim()) {
15
- throw new ForbiddenError("Dispatch administration requires an authenticated user.");
19
+ throw new ForbiddenError(`${action} requires an authenticated user.`);
16
20
  }
17
21
  if (!orgId?.trim())
18
- return;
22
+ return null;
19
23
  let membership;
20
24
  try {
21
25
  membership = await validateFederatedOrganizationMembershipForCurrentRequest({
@@ -26,16 +30,31 @@ export async function authorizeDispatchAdmin(_args, ctx) {
26
30
  catch (error) {
27
31
  if (isMissingOrganizationTableError(error) &&
28
32
  isStandaloneDispatchRuntime()) {
29
- return;
33
+ return null;
30
34
  }
31
35
  throw error;
32
36
  }
33
37
  if (!membership.active) {
34
- throw new ForbiddenError("Dispatch administration requires active organization membership.");
38
+ throw new ForbiddenError(`${action} requires active organization membership.`);
35
39
  }
40
+ return { email, orgId, role: membership.role };
41
+ }
42
+ export async function authorizeDispatchAdmin(_args, ctx) {
43
+ const membership = await requireActiveMembership(ctx, "Dispatch administration");
44
+ if (!membership)
45
+ return;
36
46
  await getDispatchAccess().assertPermission(["administer"], {
37
- userEmail: email,
38
- orgId,
47
+ userEmail: membership.email,
48
+ orgId: membership.orgId,
39
49
  });
40
50
  }
51
+ /** Organization owners and admins only; Dispatch admin roles do not count. */
52
+ export async function authorizeOrgOwnerOrAdmin(_args, ctx) {
53
+ const membership = await requireActiveMembership(ctx, "This organization setting");
54
+ if (!membership)
55
+ return;
56
+ if (membership.role !== "owner" && membership.role !== "admin") {
57
+ throw new ForbiddenError("Only organization owners and admins can change this setting.");
58
+ }
59
+ }
41
60
  //# sourceMappingURL=app-roles.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"app-roles.js","sourceRoot":"","sources":["../../../src/server/lib/app-roles.ts"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,EACd,+BAA+B,EAC/B,2BAA2B,EAE3B,wDAAwD,GACzD,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,eAAe,EACf,mBAAmB,GACpB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAErE,IAAI,cAA2D,CAAC;AAEhE,SAAS,iBAAiB;IACxB,OAAO,CAAC,cAAc,KAAK,cAAc,CAAC,wBAAwB,EAAE;QAClE,cAAc,EAAE,IAAI;KACrB,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,KAAc,EACd,GAAsB;IAEtB,MAAM,KAAK,GACT,GAAG,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,mBAAmB,EAAE,CAAC;IACvE,MAAM,KAAK,GAAG,GAAG,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC;IACvE,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC;QACnB,MAAM,IAAI,cAAc,CACtB,yDAAyD,CAC1D,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE;QAAE,OAAO;IAC3B,IAAI,UAAU,CAAC;IACf,IAAI,CAAC;QACH,UAAU,GAAG,MAAM,wDAAwD,CACzE;YACE,KAAK;YACL,KAAK;SACN,CACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IACE,+BAA+B,CAAC,KAAK,CAAC;YACtC,2BAA2B,EAAE,EAC7B,CAAC;YACD,OAAO;QACT,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;QACvB,MAAM,IAAI,cAAc,CACtB,kEAAkE,CACnE,CAAC;IACJ,CAAC;IACD,MAAM,iBAAiB,EAAE,CAAC,gBAAgB,CAAC,CAAC,YAAY,CAAC,EAAE;QACzD,SAAS,EAAE,KAAK;QAChB,KAAK;KACN,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type { ActionRunContext } from \"@agent-native/core/action\";\nimport {\n defineAppRoles,\n isMissingOrganizationTableError,\n isStandaloneDispatchRuntime,\n type AppRoles,\n validateFederatedOrganizationMembershipForCurrentRequest,\n} from \"@agent-native/core/org\";\nimport {\n getRequestOrgId,\n getRequestUserEmail,\n} from \"@agent-native/core/server\";\nimport { ForbiddenError } from \"@agent-native/core/sharing\";\n\nimport { dispatchAccessDescriptor } from \"../../shared/app-roles.js\";\n\nlet dispatchAccess: AppRoles<\"admin\", \"administer\"> | undefined;\n\nfunction getDispatchAccess(): AppRoles<\"admin\", \"administer\"> {\n return (dispatchAccess ??= defineAppRoles(dispatchAccessDescriptor, {\n allowOrgAdmins: true,\n }));\n}\n\nexport async function authorizeDispatchAdmin(\n _args: unknown,\n ctx?: ActionRunContext,\n): Promise<void> {\n const email =\n ctx?.userEmail !== undefined ? ctx.userEmail : getRequestUserEmail();\n const orgId = ctx?.orgId !== undefined ? ctx.orgId : getRequestOrgId();\n if (!email?.trim()) {\n throw new ForbiddenError(\n \"Dispatch administration requires an authenticated user.\",\n );\n }\n if (!orgId?.trim()) return;\n let membership;\n try {\n membership = await validateFederatedOrganizationMembershipForCurrentRequest(\n {\n orgId,\n email,\n },\n );\n } catch (error) {\n if (\n isMissingOrganizationTableError(error) &&\n isStandaloneDispatchRuntime()\n ) {\n return;\n }\n throw error;\n }\n if (!membership.active) {\n throw new ForbiddenError(\n \"Dispatch administration requires active organization membership.\",\n );\n }\n await getDispatchAccess().assertPermission([\"administer\"], {\n userEmail: email,\n orgId,\n });\n}\n"]}
1
+ {"version":3,"file":"app-roles.js","sourceRoot":"","sources":["../../../src/server/lib/app-roles.ts"],"names":[],"mappings":"AACA,OAAO,EACL,cAAc,EACd,+BAA+B,EAC/B,2BAA2B,EAG3B,wDAAwD,GACzD,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,eAAe,EACf,mBAAmB,GACpB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAE5D,OAAO,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAErE,IAAI,cAA2D,CAAC;AAEhE,SAAS,iBAAiB;IACxB,OAAO,CAAC,cAAc,KAAK,cAAc,CAAC,wBAAwB,EAAE;QAClE,cAAc,EAAE,IAAI;KACrB,CAAC,CAAC,CAAC;AACN,CAAC;AAED;;;GAGG;AACH,KAAK,UAAU,uBAAuB,CACpC,GAAiC,EACjC,MAAc;IAEd,MAAM,KAAK,GACT,GAAG,EAAE,SAAS,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,mBAAmB,EAAE,CAAC;IACvE,MAAM,KAAK,GAAG,GAAG,EAAE,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,eAAe,EAAE,CAAC;IACvE,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC;QACnB,MAAM,IAAI,cAAc,CAAC,GAAG,MAAM,kCAAkC,CAAC,CAAC;IACxE,CAAC;IACD,IAAI,CAAC,KAAK,EAAE,IAAI,EAAE;QAAE,OAAO,IAAI,CAAC;IAChC,IAAI,UAAU,CAAC;IACf,IAAI,CAAC;QACH,UAAU,GAAG,MAAM,wDAAwD,CACzE;YACE,KAAK;YACL,KAAK;SACN,CACF,CAAC;IACJ,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IACE,+BAA+B,CAAC,KAAK,CAAC;YACtC,2BAA2B,EAAE,EAC7B,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;IACD,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC;QACvB,MAAM,IAAI,cAAc,CACtB,GAAG,MAAM,2CAA2C,CACrD,CAAC;IACJ,CAAC;IACD,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,CAAC,IAAI,EAAE,CAAC;AACjD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,KAAc,EACd,GAAsB;IAEtB,MAAM,UAAU,GAAG,MAAM,uBAAuB,CAC9C,GAAG,EACH,yBAAyB,CAC1B,CAAC;IACF,IAAI,CAAC,UAAU;QAAE,OAAO;IACxB,MAAM,iBAAiB,EAAE,CAAC,gBAAgB,CAAC,CAAC,YAAY,CAAC,EAAE;QACzD,SAAS,EAAE,UAAU,CAAC,KAAK;QAC3B,KAAK,EAAE,UAAU,CAAC,KAAK;KACxB,CAAC,CAAC;AACL,CAAC;AAED,8EAA8E;AAC9E,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,KAAc,EACd,GAAsB;IAEtB,MAAM,UAAU,GAAG,MAAM,uBAAuB,CAC9C,GAAG,EACH,2BAA2B,CAC5B,CAAC;IACF,IAAI,CAAC,UAAU;QAAE,OAAO;IACxB,IAAI,UAAU,CAAC,IAAI,KAAK,OAAO,IAAI,UAAU,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC/D,MAAM,IAAI,cAAc,CACtB,8DAA8D,CAC/D,CAAC;IACJ,CAAC;AACH,CAAC","sourcesContent":["import type { ActionRunContext } from \"@agent-native/core/action\";\nimport {\n defineAppRoles,\n isMissingOrganizationTableError,\n isStandaloneDispatchRuntime,\n type AppRoles,\n type OrgRole,\n validateFederatedOrganizationMembershipForCurrentRequest,\n} from \"@agent-native/core/org\";\nimport {\n getRequestOrgId,\n getRequestUserEmail,\n} from \"@agent-native/core/server\";\nimport { ForbiddenError } from \"@agent-native/core/sharing\";\n\nimport { dispatchAccessDescriptor } from \"../../shared/app-roles.js\";\n\nlet dispatchAccess: AppRoles<\"admin\", \"administer\"> | undefined;\n\nfunction getDispatchAccess(): AppRoles<\"admin\", \"administer\"> {\n return (dispatchAccess ??= defineAppRoles(dispatchAccessDescriptor, {\n allowOrgAdmins: true,\n }));\n}\n\n/**\n * The caller's active membership in the request org, or null when there is no\n * org to check (a user without one, or standalone Dispatch with no org tables).\n */\nasync function requireActiveMembership(\n ctx: ActionRunContext | undefined,\n action: string,\n): Promise<{ email: string; orgId: string; role: OrgRole } | null> {\n const email =\n ctx?.userEmail !== undefined ? ctx.userEmail : getRequestUserEmail();\n const orgId = ctx?.orgId !== undefined ? ctx.orgId : getRequestOrgId();\n if (!email?.trim()) {\n throw new ForbiddenError(`${action} requires an authenticated user.`);\n }\n if (!orgId?.trim()) return null;\n let membership;\n try {\n membership = await validateFederatedOrganizationMembershipForCurrentRequest(\n {\n orgId,\n email,\n },\n );\n } catch (error) {\n if (\n isMissingOrganizationTableError(error) &&\n isStandaloneDispatchRuntime()\n ) {\n return null;\n }\n throw error;\n }\n if (!membership.active) {\n throw new ForbiddenError(\n `${action} requires active organization membership.`,\n );\n }\n return { email, orgId, role: membership.role };\n}\n\nexport async function authorizeDispatchAdmin(\n _args: unknown,\n ctx?: ActionRunContext,\n): Promise<void> {\n const membership = await requireActiveMembership(\n ctx,\n \"Dispatch administration\",\n );\n if (!membership) return;\n await getDispatchAccess().assertPermission([\"administer\"], {\n userEmail: membership.email,\n orgId: membership.orgId,\n });\n}\n\n/** Organization owners and admins only; Dispatch admin roles do not count. */\nexport async function authorizeOrgOwnerOrAdmin(\n _args: unknown,\n ctx?: ActionRunContext,\n): Promise<void> {\n const membership = await requireActiveMembership(\n ctx,\n \"This organization setting\",\n );\n if (!membership) return;\n if (membership.role !== \"owner\" && membership.role !== \"admin\") {\n throw new ForbiddenError(\n \"Only organization owners and admins can change this setting.\",\n );\n }\n}\n"]}
@@ -0,0 +1,6 @@
1
+ import { z } from "zod";
2
+ /** Result of the get/set hosted-apps actions. */
3
+ export declare const hostedAppsSettingsViewSchema: z.ZodObject<{
4
+ showHostedApps: z.ZodBoolean;
5
+ }, z.core.$strip>;
6
+ //# sourceMappingURL=hosted-apps-settings-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hosted-apps-settings-schema.d.ts","sourceRoot":"","sources":["../../../src/server/lib/hosted-apps-settings-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,iDAAiD;AACjD,eAAO,MAAM,4BAA4B;;iBAEvC,CAAC"}
@@ -0,0 +1,6 @@
1
+ import { z } from "zod";
2
+ /** Result of the get/set hosted-apps actions. */
3
+ export const hostedAppsSettingsViewSchema = z.object({
4
+ showHostedApps: z.boolean(),
5
+ });
6
+ //# sourceMappingURL=hosted-apps-settings-schema.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hosted-apps-settings-schema.js","sourceRoot":"","sources":["../../../src/server/lib/hosted-apps-settings-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,iDAAiD;AACjD,MAAM,CAAC,MAAM,4BAA4B,GAAG,CAAC,CAAC,MAAM,CAAC;IACnD,cAAc,EAAE,CAAC,CAAC,OAAO,EAAE;CAC5B,CAAC,CAAC","sourcesContent":["import { z } from \"zod\";\n\n/** Result of the get/set hosted-apps actions. */\nexport const hostedAppsSettingsViewSchema = z.object({\n showHostedApps: z.boolean(),\n});\n"]}
@@ -0,0 +1,27 @@
1
+ import { ActionContractError } from "@agent-native/core/action";
2
+ import { type DiscoveredAgent } from "@agent-native/core/server/agent-discovery";
3
+ export declare const HOSTED_APPS_SETTINGS_KEY = "dispatch-hosted-apps-settings";
4
+ export interface HostedAppsSettings {
5
+ showHostedApps: boolean;
6
+ }
7
+ export declare class InvalidHostedAppsSettingsError extends ActionContractError {
8
+ constructor();
9
+ }
10
+ /**
11
+ * Built-in catalog apps this workspace offers. A mounted workspace app with a
12
+ * built-in's id is a workspace app; the built-in config never applies to it.
13
+ */
14
+ export declare function listHostedApps(): Promise<DiscoveredAgent[]>;
15
+ /**
16
+ * The stored setting. An unreadable value is `invalid` and hides hosted apps,
17
+ * so it cannot fail the app lists or agent context; Settings reports it.
18
+ */
19
+ export declare function readHostedAppsSettings(): Promise<HostedAppsSettings & {
20
+ invalid: boolean;
21
+ }>;
22
+ export declare function setHostedAppsSettings(next: HostedAppsSettings): Promise<HostedAppsSettings>;
23
+ /** Hides hosted apps from Dispatch's lists and agent context when off. */
24
+ export declare function hideHostedAppsWhenOff<T extends {
25
+ id: string;
26
+ }>(agents: T[]): Promise<T[]>;
27
+ //# sourceMappingURL=hosted-apps-settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hosted-apps-settings.d.ts","sourceRoot":"","sources":["../../../src/server/lib/hosted-apps-settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAKhE,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,2CAA2C,CAAC;AAUnD,eAAO,MAAM,wBAAwB,kCAAkC,CAAC;AAExE,MAAM,WAAW,kBAAkB;IACjC,cAAc,EAAE,OAAO,CAAC;CACzB;AAID,qBAAa,8BAA+B,SAAQ,mBAAmB;IACrE,cAMC;CACF;AAED;;;GAGG;AACH,wBAAsB,cAAc,IAAI,OAAO,CAAC,eAAe,EAAE,CAAC,CAQjE;AASD;;;GAGG;AACH,wBAAsB,sBAAsB,IAAI,OAAO,CACrD,kBAAkB,GAAG;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE,CAC1C,CAaA;AAED,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,kBAAkB,CAAC,CAe7B;AAED,0EAA0E;AAC1E,wBAAsB,qBAAqB,CAAC,CAAC,SAAS;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,EAClE,MAAM,EAAE,CAAC,EAAE,GACV,OAAO,CAAC,CAAC,EAAE,CAAC,CAKd"}
@@ -0,0 +1,72 @@
1
+ import { ActionContractError } from "@agent-native/core/action";
2
+ import { getRequestOrgId, getRequestUserEmail, } from "@agent-native/core/server";
3
+ import { getBuiltinAgents, loadWorkspaceAppsManifest, normalizeAgentId, } from "@agent-native/core/server/agent-discovery";
4
+ import { getOrgSetting, getUserSetting, putOrgSetting, putUserSetting, } from "@agent-native/core/settings";
5
+ import { HOSTED_APPS_SETTINGS_INVALID_ERROR_CODE } from "../../shared/hosted-apps-settings.js";
6
+ export const HOSTED_APPS_SETTINGS_KEY = "dispatch-hosted-apps-settings";
7
+ // A contract error, so its code reaches Settings, which offers the save that
8
+ // repairs it; a bare error would arrive as an anonymous 500.
9
+ export class InvalidHostedAppsSettingsError extends ActionContractError {
10
+ constructor() {
11
+ super(`Stored ${HOSTED_APPS_SETTINGS_KEY} is not { showHostedApps: boolean }. Save the Agent-Native.com hosted apps setting again to repair it.`, { errorCode: HOSTED_APPS_SETTINGS_INVALID_ERROR_CODE, statusCode: 409 });
12
+ this.name = "InvalidHostedAppsSettingsError";
13
+ }
14
+ }
15
+ /**
16
+ * Built-in catalog apps this workspace offers. A mounted workspace app with a
17
+ * built-in's id is a workspace app; the built-in config never applies to it.
18
+ */
19
+ export async function listHostedApps() {
20
+ const workspaceApps = await loadWorkspaceAppsManifest();
21
+ const workspaceIds = new Set((workspaceApps ?? []).map((app) => normalizeAgentId(app.id)));
22
+ return getBuiltinAgents("dispatch").filter((agent) => !workspaceIds.has(agent.id));
23
+ }
24
+ function hostedAppsSettingsScope() {
25
+ const orgId = getRequestOrgId();
26
+ if (orgId)
27
+ return { scope: "org", scopeId: orgId };
28
+ const email = getRequestUserEmail();
29
+ return email ? { scope: "user", scopeId: email } : null;
30
+ }
31
+ /**
32
+ * The stored setting. An unreadable value is `invalid` and hides hosted apps,
33
+ * so it cannot fail the app lists or agent context; Settings reports it.
34
+ */
35
+ export async function readHostedAppsSettings() {
36
+ const scope = hostedAppsSettingsScope();
37
+ // No org and no user means there is nowhere the setting could be stored.
38
+ if (!scope)
39
+ return { showHostedApps: true, invalid: false };
40
+ const raw = scope.scope === "org"
41
+ ? await getOrgSetting(scope.scopeId, HOSTED_APPS_SETTINGS_KEY)
42
+ : await getUserSetting(scope.scopeId, HOSTED_APPS_SETTINGS_KEY);
43
+ if (raw === null)
44
+ return { showHostedApps: true, invalid: false };
45
+ if (typeof raw.showHostedApps !== "boolean") {
46
+ return { showHostedApps: false, invalid: true };
47
+ }
48
+ return { showHostedApps: raw.showHostedApps, invalid: false };
49
+ }
50
+ export async function setHostedAppsSettings(next) {
51
+ const scope = hostedAppsSettingsScope();
52
+ if (!scope) {
53
+ throw Object.assign(new Error("Sign in to change the Agent-Native.com hosted apps setting."), { statusCode: 401 });
54
+ }
55
+ const value = { showHostedApps: next.showHostedApps };
56
+ if (scope.scope === "org") {
57
+ await putOrgSetting(scope.scopeId, HOSTED_APPS_SETTINGS_KEY, value);
58
+ }
59
+ else {
60
+ await putUserSetting(scope.scopeId, HOSTED_APPS_SETTINGS_KEY, value);
61
+ }
62
+ return value;
63
+ }
64
+ /** Hides hosted apps from Dispatch's lists and agent context when off. */
65
+ export async function hideHostedAppsWhenOff(agents) {
66
+ const { showHostedApps } = await readHostedAppsSettings();
67
+ if (showHostedApps)
68
+ return agents;
69
+ const hostedIds = new Set((await listHostedApps()).map((app) => app.id));
70
+ return agents.filter((agent) => !hostedIds.has(agent.id));
71
+ }
72
+ //# sourceMappingURL=hosted-apps-settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hosted-apps-settings.js","sourceRoot":"","sources":["../../../src/server/lib/hosted-apps-settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EACL,eAAe,EACf,mBAAmB,GACpB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EACL,gBAAgB,EAChB,yBAAyB,EACzB,gBAAgB,GAEjB,MAAM,2CAA2C,CAAC;AACnD,OAAO,EACL,aAAa,EACb,cAAc,EACd,aAAa,EACb,cAAc,GACf,MAAM,6BAA6B,CAAC;AAErC,OAAO,EAAE,uCAAuC,EAAE,MAAM,sCAAsC,CAAC;AAE/F,MAAM,CAAC,MAAM,wBAAwB,GAAG,+BAA+B,CAAC;AAMxE,6EAA6E;AAC7E,6DAA6D;AAC7D,MAAM,OAAO,8BAA+B,SAAQ,mBAAmB;IACrE;QACE,KAAK,CACH,UAAU,wBAAwB,wGAAwG,EAC1I,EAAE,SAAS,EAAE,uCAAuC,EAAE,UAAU,EAAE,GAAG,EAAE,CACxE,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,gCAAgC,CAAC;IAC/C,CAAC;CACF;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,MAAM,aAAa,GAAG,MAAM,yBAAyB,EAAE,CAAC;IACxD,MAAM,YAAY,GAAG,IAAI,GAAG,CAC1B,CAAC,aAAa,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAC7D,CAAC;IACF,OAAO,gBAAgB,CAAC,UAAU,CAAC,CAAC,MAAM,CACxC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CACvC,CAAC;AACJ,CAAC;AAED,SAAS,uBAAuB;IAC9B,MAAM,KAAK,GAAG,eAAe,EAAE,CAAC;IAChC,IAAI,KAAK;QAAE,OAAO,EAAE,KAAK,EAAE,KAAc,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC5D,MAAM,KAAK,GAAG,mBAAmB,EAAE,CAAC;IACpC,OAAO,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,MAAe,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AACnE,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB;IAG1C,MAAM,KAAK,GAAG,uBAAuB,EAAE,CAAC;IACxC,yEAAyE;IACzE,IAAI,CAAC,KAAK;QAAE,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC5D,MAAM,GAAG,GACP,KAAK,CAAC,KAAK,KAAK,KAAK;QACnB,CAAC,CAAC,MAAM,aAAa,CAAC,KAAK,CAAC,OAAO,EAAE,wBAAwB,CAAC;QAC9D,CAAC,CAAC,MAAM,cAAc,CAAC,KAAK,CAAC,OAAO,EAAE,wBAAwB,CAAC,CAAC;IACpE,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAClE,IAAI,OAAO,GAAG,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;QAC5C,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAClD,CAAC;IACD,OAAO,EAAE,cAAc,EAAE,GAAG,CAAC,cAAc,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAChE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,IAAwB;IAExB,MAAM,KAAK,GAAG,uBAAuB,EAAE,CAAC;IACxC,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,MAAM,CAAC,MAAM,CACjB,IAAI,KAAK,CAAC,6DAA6D,CAAC,EACxE,EAAE,UAAU,EAAE,GAAG,EAAE,CACpB,CAAC;IACJ,CAAC;IACD,MAAM,KAAK,GAAG,EAAE,cAAc,EAAE,IAAI,CAAC,cAAc,EAAE,CAAC;IACtD,IAAI,KAAK,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;QAC1B,MAAM,aAAa,CAAC,KAAK,CAAC,OAAO,EAAE,wBAAwB,EAAE,KAAK,CAAC,CAAC;IACtE,CAAC;SAAM,CAAC;QACN,MAAM,cAAc,CAAC,KAAK,CAAC,OAAO,EAAE,wBAAwB,EAAE,KAAK,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,0EAA0E;AAC1E,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,MAAW;IAEX,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,sBAAsB,EAAE,CAAC;IAC1D,IAAI,cAAc;QAAE,OAAO,MAAM,CAAC;IAClC,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,cAAc,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IACzE,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;AAC5D,CAAC","sourcesContent":["import { ActionContractError } from \"@agent-native/core/action\";\nimport {\n getRequestOrgId,\n getRequestUserEmail,\n} from \"@agent-native/core/server\";\nimport {\n getBuiltinAgents,\n loadWorkspaceAppsManifest,\n normalizeAgentId,\n type DiscoveredAgent,\n} from \"@agent-native/core/server/agent-discovery\";\nimport {\n getOrgSetting,\n getUserSetting,\n putOrgSetting,\n putUserSetting,\n} from \"@agent-native/core/settings\";\n\nimport { HOSTED_APPS_SETTINGS_INVALID_ERROR_CODE } from \"../../shared/hosted-apps-settings.js\";\n\nexport const HOSTED_APPS_SETTINGS_KEY = \"dispatch-hosted-apps-settings\";\n\nexport interface HostedAppsSettings {\n showHostedApps: boolean;\n}\n\n// A contract error, so its code reaches Settings, which offers the save that\n// repairs it; a bare error would arrive as an anonymous 500.\nexport class InvalidHostedAppsSettingsError extends ActionContractError {\n constructor() {\n super(\n `Stored ${HOSTED_APPS_SETTINGS_KEY} is not { showHostedApps: boolean }. Save the Agent-Native.com hosted apps setting again to repair it.`,\n { errorCode: HOSTED_APPS_SETTINGS_INVALID_ERROR_CODE, statusCode: 409 },\n );\n this.name = \"InvalidHostedAppsSettingsError\";\n }\n}\n\n/**\n * Built-in catalog apps this workspace offers. A mounted workspace app with a\n * built-in's id is a workspace app; the built-in config never applies to it.\n */\nexport async function listHostedApps(): Promise<DiscoveredAgent[]> {\n const workspaceApps = await loadWorkspaceAppsManifest();\n const workspaceIds = new Set(\n (workspaceApps ?? []).map((app) => normalizeAgentId(app.id)),\n );\n return getBuiltinAgents(\"dispatch\").filter(\n (agent) => !workspaceIds.has(agent.id),\n );\n}\n\nfunction hostedAppsSettingsScope() {\n const orgId = getRequestOrgId();\n if (orgId) return { scope: \"org\" as const, scopeId: orgId };\n const email = getRequestUserEmail();\n return email ? { scope: \"user\" as const, scopeId: email } : null;\n}\n\n/**\n * The stored setting. An unreadable value is `invalid` and hides hosted apps,\n * so it cannot fail the app lists or agent context; Settings reports it.\n */\nexport async function readHostedAppsSettings(): Promise<\n HostedAppsSettings & { invalid: boolean }\n> {\n const scope = hostedAppsSettingsScope();\n // No org and no user means there is nowhere the setting could be stored.\n if (!scope) return { showHostedApps: true, invalid: false };\n const raw =\n scope.scope === \"org\"\n ? await getOrgSetting(scope.scopeId, HOSTED_APPS_SETTINGS_KEY)\n : await getUserSetting(scope.scopeId, HOSTED_APPS_SETTINGS_KEY);\n if (raw === null) return { showHostedApps: true, invalid: false };\n if (typeof raw.showHostedApps !== \"boolean\") {\n return { showHostedApps: false, invalid: true };\n }\n return { showHostedApps: raw.showHostedApps, invalid: false };\n}\n\nexport async function setHostedAppsSettings(\n next: HostedAppsSettings,\n): Promise<HostedAppsSettings> {\n const scope = hostedAppsSettingsScope();\n if (!scope) {\n throw Object.assign(\n new Error(\"Sign in to change the Agent-Native.com hosted apps setting.\"),\n { statusCode: 401 },\n );\n }\n const value = { showHostedApps: next.showHostedApps };\n if (scope.scope === \"org\") {\n await putOrgSetting(scope.scopeId, HOSTED_APPS_SETTINGS_KEY, value);\n } else {\n await putUserSetting(scope.scopeId, HOSTED_APPS_SETTINGS_KEY, value);\n }\n return value;\n}\n\n/** Hides hosted apps from Dispatch's lists and agent context when off. */\nexport async function hideHostedAppsWhenOff<T extends { id: string }>(\n agents: T[],\n): Promise<T[]> {\n const { showHostedApps } = await readHostedAppsSettings();\n if (showHostedApps) return agents;\n const hostedIds = new Set((await listHostedApps()).map((app) => app.id));\n return agents.filter((agent) => !hostedIds.has(agent.id));\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  import { getOrgContext } from "@agent-native/core/org";
2
- import { createAgentChatPlugin, getBuilderWorkspaceUrl, } from "@agent-native/core/server";
2
+ import { createAgentChatPlugin, getBuilderWorkspaceUrl, registerAgentContextAgentFilter, } from "@agent-native/core/server";
3
3
  import { dispatchActions } from "../../actions/index.js";
4
+ import { hideHostedAppsWhenOff } from "../lib/hosted-apps-settings.js";
4
5
  import { workspaceAppActionRouteAuth, WORKSPACE_APP_CLAIM_ACTION_PATH, WORKSPACE_APPS_ACTION_PATH, } from "../lib/workspace-app-action-auth.js";
5
6
  const INITIAL_TOOL_NAMES = [
6
7
  "view-screen",
@@ -31,6 +32,7 @@ const INITIAL_TOOL_NAMES = [
31
32
  "query-staged-dataset",
32
33
  "navigate",
33
34
  ];
35
+ registerAgentContextAgentFilter("dispatch", hideHostedAppsWhenOff);
34
36
  export default createAgentChatPlugin({
35
37
  appId: "dispatch",
36
38
  connectApps: true,
@@ -79,11 +81,12 @@ Use the standard workspace primitives:
79
81
  - You receive a compact available-apps block with sibling workspace app names and descriptions. Use it to pick the right A2A target, and call list-connected-agents or tool-search only when you need fresh details.
80
82
  - Hosted/connected A2A neighbors such as Analytics and Content come from the available-apps context or list-connected-agents. list-workspace-apps only inventories apps mounted inside this workspace deployment; never use a missing row there to conclude that a connected agent is unavailable.
81
83
  - When answering whether a mounted workspace app exposes an agent card or A2A endpoint, call list-workspace-apps with includeAgentCards=true. If you have not requested that probe, absence of agent-card fields means unchecked, not unavailable.
82
- - When creating a new workspace app, create a separate app under apps/<app-id> with apps/<app-id>/package.json including a concise generated description, mount it at /<app-id>, use relative /<app-id> links, never hardcode localhost or dev ports, use shadcn/ui with @tabler/icons-react rather than lucide-react, and ensure the React Router client entry preserves APP_BASE_PATH/VITE_APP_BASE_PATH via appBasePath(). There is no separate workspace app registry to edit.
84
+ - When creating a new workspace app, create a separate app under apps/<app-id> with apps/<app-id>/package.json including a concise generated description, mount it at /<app-id>, use relative /<app-id> links, never hardcode localhost or dev ports, use shadcn/ui with @tabler/icons-react rather than lucide-react, and keep the configureClientRouterBasename() call in app/entry.client.tsx so the app hydrates at /<app-id>. There is no separate workspace app registry to edit.
83
85
  - When a user asks to rename an existing workspace app or change its Dispatch title/description, call update-workspace-app-metadata with the existing appId from list-workspace-apps. This is a metadata-only edit — never call start-workspace-app-creation, which creates a new app and Builder branch.
84
86
  - When an explicit app-creation request already includes a source brief or a concrete repeatable workflow, call start-workspace-app-creation without asking non-blocking product or UX questions. Choose recommended defaults, let the Builder handoff record assumptions, and ask only for authorization, credentials, a destructive action, an ambiguous target workspace, or a genuinely missing workflow.
85
87
  - 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.
86
- - 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.
88
+ - Treat the apps in the available-apps context as existing neighbors available through links and A2A. 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.
89
+ - Owners and admins can hide Agent-Native.com hosted apps for the organization with set-hosted-apps-settings; get-hosted-apps-settings reads it. When hidden they are left out of available-apps and list-connected-agents marks them hiddenFromLaunchers, but call-agent by name still reaches them.
87
90
  - 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.
88
91
  - For broad provider searches, joins, classification, corpus counts, or absence claims, fetch every relevant page or an explicitly bounded cohort, stage/save large responses with stageAs/saveToFile/fetchAllPages, and reduce them with query-staged-dataset or run-code. Report source, filters, row counts, pagination, truncation, failed pages, and uncovered gaps.
89
92
  - Use list-dispatch-usage-metrics only for this workspace's own members' agent/LLM usage and spend, or aggregate adoption of an accessible workspace app. For agent/LLM usage or spend, call it with scope=workspace and the requested sinceDays; it reports workspace-member usage only. For app adoption, call it with scope=app and appId; app creators can see aggregate daily/weekly active users and tracked actions for their own app, while organization owners/admins can inspect any accessible app. App scope omits individual users and prompt previews. External customer, account, or company usage, credits, activity, or product data belongs to the relevant app: use its identity in <relevant-apps> when clear, or call describe-workspace-apps with a query when ownership is unclear. Do not use this action for external customer/account/company data.
@@ -1 +1 @@
1
- {"version":3,"file":"agent-chat.js","sourceRoot":"","sources":["../../../src/server/plugins/agent-chat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EACL,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EACL,2BAA2B,EAC3B,+BAA+B,EAC/B,0BAA0B,GAC3B,MAAM,qCAAqC,CAAC;AAE7C,MAAM,kBAAkB,GAAG;IACzB,aAAa;IACb,qBAAqB;IACrB,+BAA+B;IAC/B,uBAAuB;IACvB,SAAS;IACT,UAAU;IACV,0BAA0B;IAC1B,2BAA2B;IAC3B,2BAA2B;IAC3B,cAAc;IACd,mBAAmB;IACnB,iBAAiB;IACjB,wBAAwB;IACxB,oBAAoB;IACpB,sBAAsB;IACtB,qBAAqB;IACrB,mBAAmB;IACnB,oBAAoB;IACpB,aAAa;IACb,8BAA8B;IAC9B,6BAA6B;IAC7B,oCAAoC;IACpC,sBAAsB;IACtB,mBAAmB;IACnB,sBAAsB;IACtB,sBAAsB;IACtB,UAAU;CACX,CAAC;AAEF,eAAe,qBAAqB,CAAC;IACnC,KAAK,EAAE,UAAU;IACjB,WAAW,EAAE,IAAI;IACjB,qBAAqB,EAAE,IAAI;IAC3B,gBAAgB,EAAE,kBAAkB;IACpC,GAAG,EAAE;QACH,eAAe,EAAE,IAAI;QACrB,gBAAgB,EAAE;YAChB,oCAAoC;YACpC,8BAA8B;YAC9B,6BAA6B;SAC9B;KACF;IACD,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QAC5B,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,CAAC;QACvC,OAAO,GAAG,CAAC,KAAK,CAAC;IACnB,CAAC;IACD,OAAO,EAAE,eAAe;IACxB,eAAe,EAAE,2BAA2B;IAC5C,sBAAsB,EAAE;QACtB,0BAA0B;QAC1B,+BAA+B;KAChC;IACD,aAAa,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE;IAC1C,YAAY,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6EAoC6D,sBAAsB,EAAE,EAAE;CACtG,CAAC,CAAC;AAEH;;;GAGG;AACH,SAAS,sBAAsB;IAC7B,MAAM,GAAG,GAAG,sBAAsB,EAAE,CAAC;IACrC,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACpB,OAAO;;2JAEkJ,GAAG,sCAAsC,CAAC;AACrM,CAAC","sourcesContent":["import { getOrgContext } from \"@agent-native/core/org\";\nimport {\n createAgentChatPlugin,\n getBuilderWorkspaceUrl,\n} from \"@agent-native/core/server\";\n\nimport { dispatchActions } from \"../../actions/index.js\";\nimport {\n workspaceAppActionRouteAuth,\n WORKSPACE_APP_CLAIM_ACTION_PATH,\n WORKSPACE_APPS_ACTION_PATH,\n} from \"../lib/workspace-app-action-auth.js\";\n\nconst INITIAL_TOOL_NAMES = [\n \"view-screen\",\n \"list-workspace-apps\",\n \"update-workspace-app-metadata\",\n \"list-connected-agents\",\n \"ask_app\",\n \"open_app\",\n \"list-workspace-resources\",\n \"create-workspace-resource\",\n \"update-workspace-resource\",\n \"import-agent\",\n \"import-agent-pack\",\n \"list-agent-pack\",\n \"connect-external-agent\",\n \"list-vault-secrets\",\n \"request-vault-secret\",\n \"create-vault-secret\",\n \"list-destinations\",\n \"upsert-destination\",\n \"list-dreams\",\n \"start-workspace-app-creation\",\n \"list-dispatch-usage-metrics\",\n \"list-available-workspace-templates\",\n \"provider-api-catalog\",\n \"provider-api-docs\",\n \"provider-api-request\",\n \"query-staged-dataset\",\n \"navigate\",\n];\n\nexport default createAgentChatPlugin({\n appId: \"dispatch\",\n connectApps: true,\n durableBackgroundRuns: true,\n initialToolNames: INITIAL_TOOL_NAMES,\n mcp: {\n appApprovalPage: true,\n connectorCatalog: [\n \"resolve-integration-source-context\",\n \"start-workspace-app-creation\",\n \"list-dispatch-usage-metrics\",\n ],\n },\n resolveOrgId: async (event) => {\n const ctx = await getOrgContext(event);\n return ctx.orgId;\n },\n actions: dispatchActions,\n actionRouteAuth: workspaceAppActionRouteAuth,\n actionRoutePublicPaths: [\n WORKSPACE_APPS_ACTION_PATH,\n WORKSPACE_APP_CLAIM_ACTION_PATH,\n ],\n codeExecution: { production: \"sandboxed\" },\n systemPrompt: `You are the central dispatch for this workspace.\n\nDefault posture:\n- Treat Slack and Telegram as shared entrypoints into the workspace.\n- Heavily delegate domain work to specialized agents through A2A when another app owns the job.\n- Keep durable memory and operating instructions in resources rather than ephemeral chat.\n- Prefer replying in the current external thread unless the user explicitly asks you to send to a saved destination.\n\nUse the standard workspace primitives:\n- Read and update resources like AGENTS.md, LEARNINGS.md, jobs/*.md, agents/*.md, and remote-agents/*.json when appropriate.\n- Use recurring jobs for scheduled behavior.\n- Use custom agent profiles in agents/*.md for local spawned work and remote-agents/*.json for remote A2A apps.\n- For a Claude-style Markdown or JSON agent setup, use import-agent so the\n profile is normalized into agents/<slug>.md. Never import credentials, hooks,\n shell commands, or local environment settings.\n- For a folder of agent instructions, references, or skills, use\n import-agent-pack. It preserves the text files under agents/<slug>/ and\n creates the normalized profile alongside them. Use list-agent-pack to inspect\n the pack before editing or handing it off to an app.\n- For an existing HTTP/A2A endpoint, use connect-external-agent and let the\n normal A2A/MCP connection flow handle authentication.\n- You receive a compact available-apps block with sibling workspace app names and descriptions. Use it to pick the right A2A target, and call list-connected-agents or tool-search only when you need fresh details.\n- Hosted/connected A2A neighbors such as Analytics and Content come from the available-apps context or list-connected-agents. list-workspace-apps only inventories apps mounted inside this workspace deployment; never use a missing row there to conclude that a connected agent is unavailable.\n- When answering whether a mounted workspace app exposes an agent card or A2A endpoint, call list-workspace-apps with includeAgentCards=true. If you have not requested that probe, absence of agent-card fields means unchecked, not unavailable.\n- When creating a new workspace app, create a separate app under apps/<app-id> with apps/<app-id>/package.json including a concise generated description, mount it at /<app-id>, use relative /<app-id> links, never hardcode localhost or dev ports, use shadcn/ui with @tabler/icons-react rather than lucide-react, and ensure the React Router client entry preserves APP_BASE_PATH/VITE_APP_BASE_PATH via appBasePath(). There is no separate workspace app registry to edit.\n- When a user asks to rename an existing workspace app or change its Dispatch title/description, call update-workspace-app-metadata with the existing appId from list-workspace-apps. This is a metadata-only edit — never call start-workspace-app-creation, which creates a new app and Builder branch.\n- When an explicit app-creation request already includes a source brief or a concrete repeatable workflow, call start-workspace-app-creation without asking non-blocking product or UX questions. Choose recommended defaults, let the Builder handoff record assumptions, and ask only for authorization, credentials, a destructive action, an ambiguous target workspace, or a genuinely missing workflow.\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- 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- For broad provider searches, joins, classification, corpus counts, or absence claims, fetch every relevant page or an explicitly bounded cohort, stage/save large responses with stageAs/saveToFile/fetchAllPages, and reduce them with query-staged-dataset or run-code. Report source, filters, row counts, pagination, truncation, failed pages, and uncovered gaps.\n- Use list-dispatch-usage-metrics only for this workspace's own members' agent/LLM usage and spend, or aggregate adoption of an accessible workspace app. For agent/LLM usage or spend, call it with scope=workspace and the requested sinceDays; it reports workspace-member usage only. For app adoption, call it with scope=app and appId; app creators can see aggregate daily/weekly active users and tracked actions for their own app, while organization owners/admins can inspect any accessible app. App scope omits individual users and prompt previews. External customer, account, or company usage, credits, activity, or product data belongs to the relevant app: use its identity in <relevant-apps> when clear, or call describe-workspace-apps with a query when ownership is unclear. Do not use this action for external customer/account/company data.\n\nWhen a user asks for something like a digest, reminder, routing rule, or saved behavior:\n- First decide whether it should be a resource, a recurring job, a destination, or a delegated task.\n- Keep responses concise and operational.\n- Avoid inventing integrations or destinations that are not configured yet.${builderManagedAppsNote()}`,\n});\n\n/**\n * A Builder-linked workspace builds its apps in Builder; app-creation actions\n * return a link there, and the agent should offer it rather than retry.\n */\nfunction builderManagedAppsNote(): string {\n const url = getBuilderWorkspaceUrl();\n if (!url) return \"\";\n return `\n\nThis workspace is managed in Builder. Never create, scaffold, remix, or install workspace apps from here. When someone asks for a new app, point them to ${url} and tell them to use New app there.`;\n}\n"]}
1
+ {"version":3,"file":"agent-chat.js","sourceRoot":"","sources":["../../../src/server/plugins/agent-chat.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EACL,qBAAqB,EACrB,sBAAsB,EACtB,+BAA+B,GAChC,MAAM,2BAA2B,CAAC;AAEnC,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACzD,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EACL,2BAA2B,EAC3B,+BAA+B,EAC/B,0BAA0B,GAC3B,MAAM,qCAAqC,CAAC;AAE7C,MAAM,kBAAkB,GAAG;IACzB,aAAa;IACb,qBAAqB;IACrB,+BAA+B;IAC/B,uBAAuB;IACvB,SAAS;IACT,UAAU;IACV,0BAA0B;IAC1B,2BAA2B;IAC3B,2BAA2B;IAC3B,cAAc;IACd,mBAAmB;IACnB,iBAAiB;IACjB,wBAAwB;IACxB,oBAAoB;IACpB,sBAAsB;IACtB,qBAAqB;IACrB,mBAAmB;IACnB,oBAAoB;IACpB,aAAa;IACb,8BAA8B;IAC9B,6BAA6B;IAC7B,oCAAoC;IACpC,sBAAsB;IACtB,mBAAmB;IACnB,sBAAsB;IACtB,sBAAsB;IACtB,UAAU;CACX,CAAC;AAEF,+BAA+B,CAAC,UAAU,EAAE,qBAAqB,CAAC,CAAC;AAEnE,eAAe,qBAAqB,CAAC;IACnC,KAAK,EAAE,UAAU;IACjB,WAAW,EAAE,IAAI;IACjB,qBAAqB,EAAE,IAAI;IAC3B,gBAAgB,EAAE,kBAAkB;IACpC,GAAG,EAAE;QACH,eAAe,EAAE,IAAI;QACrB,gBAAgB,EAAE;YAChB,oCAAoC;YACpC,8BAA8B;YAC9B,6BAA6B;SAC9B;KACF;IACD,YAAY,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QAC5B,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,KAAK,CAAC,CAAC;QACvC,OAAO,GAAG,CAAC,KAAK,CAAC;IACnB,CAAC;IACD,OAAO,EAAE,eAAe;IACxB,eAAe,EAAE,2BAA2B;IAC5C,sBAAsB,EAAE;QACtB,0BAA0B;QAC1B,+BAA+B;KAChC;IACD,aAAa,EAAE,EAAE,UAAU,EAAE,WAAW,EAAE;IAC1C,YAAY,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;6EAqC6D,sBAAsB,EAAE,EAAE;CACtG,CAAC,CAAC;AAEH;;;GAGG;AACH,SAAS,sBAAsB;IAC7B,MAAM,GAAG,GAAG,sBAAsB,EAAE,CAAC;IACrC,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACpB,OAAO;;2JAEkJ,GAAG,sCAAsC,CAAC;AACrM,CAAC","sourcesContent":["import { getOrgContext } from \"@agent-native/core/org\";\nimport {\n createAgentChatPlugin,\n getBuilderWorkspaceUrl,\n registerAgentContextAgentFilter,\n} from \"@agent-native/core/server\";\n\nimport { dispatchActions } from \"../../actions/index.js\";\nimport { hideHostedAppsWhenOff } from \"../lib/hosted-apps-settings.js\";\nimport {\n workspaceAppActionRouteAuth,\n WORKSPACE_APP_CLAIM_ACTION_PATH,\n WORKSPACE_APPS_ACTION_PATH,\n} from \"../lib/workspace-app-action-auth.js\";\n\nconst INITIAL_TOOL_NAMES = [\n \"view-screen\",\n \"list-workspace-apps\",\n \"update-workspace-app-metadata\",\n \"list-connected-agents\",\n \"ask_app\",\n \"open_app\",\n \"list-workspace-resources\",\n \"create-workspace-resource\",\n \"update-workspace-resource\",\n \"import-agent\",\n \"import-agent-pack\",\n \"list-agent-pack\",\n \"connect-external-agent\",\n \"list-vault-secrets\",\n \"request-vault-secret\",\n \"create-vault-secret\",\n \"list-destinations\",\n \"upsert-destination\",\n \"list-dreams\",\n \"start-workspace-app-creation\",\n \"list-dispatch-usage-metrics\",\n \"list-available-workspace-templates\",\n \"provider-api-catalog\",\n \"provider-api-docs\",\n \"provider-api-request\",\n \"query-staged-dataset\",\n \"navigate\",\n];\n\nregisterAgentContextAgentFilter(\"dispatch\", hideHostedAppsWhenOff);\n\nexport default createAgentChatPlugin({\n appId: \"dispatch\",\n connectApps: true,\n durableBackgroundRuns: true,\n initialToolNames: INITIAL_TOOL_NAMES,\n mcp: {\n appApprovalPage: true,\n connectorCatalog: [\n \"resolve-integration-source-context\",\n \"start-workspace-app-creation\",\n \"list-dispatch-usage-metrics\",\n ],\n },\n resolveOrgId: async (event) => {\n const ctx = await getOrgContext(event);\n return ctx.orgId;\n },\n actions: dispatchActions,\n actionRouteAuth: workspaceAppActionRouteAuth,\n actionRoutePublicPaths: [\n WORKSPACE_APPS_ACTION_PATH,\n WORKSPACE_APP_CLAIM_ACTION_PATH,\n ],\n codeExecution: { production: \"sandboxed\" },\n systemPrompt: `You are the central dispatch for this workspace.\n\nDefault posture:\n- Treat Slack and Telegram as shared entrypoints into the workspace.\n- Heavily delegate domain work to specialized agents through A2A when another app owns the job.\n- Keep durable memory and operating instructions in resources rather than ephemeral chat.\n- Prefer replying in the current external thread unless the user explicitly asks you to send to a saved destination.\n\nUse the standard workspace primitives:\n- Read and update resources like AGENTS.md, LEARNINGS.md, jobs/*.md, agents/*.md, and remote-agents/*.json when appropriate.\n- Use recurring jobs for scheduled behavior.\n- Use custom agent profiles in agents/*.md for local spawned work and remote-agents/*.json for remote A2A apps.\n- For a Claude-style Markdown or JSON agent setup, use import-agent so the\n profile is normalized into agents/<slug>.md. Never import credentials, hooks,\n shell commands, or local environment settings.\n- For a folder of agent instructions, references, or skills, use\n import-agent-pack. It preserves the text files under agents/<slug>/ and\n creates the normalized profile alongside them. Use list-agent-pack to inspect\n the pack before editing or handing it off to an app.\n- For an existing HTTP/A2A endpoint, use connect-external-agent and let the\n normal A2A/MCP connection flow handle authentication.\n- You receive a compact available-apps block with sibling workspace app names and descriptions. Use it to pick the right A2A target, and call list-connected-agents or tool-search only when you need fresh details.\n- Hosted/connected A2A neighbors such as Analytics and Content come from the available-apps context or list-connected-agents. list-workspace-apps only inventories apps mounted inside this workspace deployment; never use a missing row there to conclude that a connected agent is unavailable.\n- When answering whether a mounted workspace app exposes an agent card or A2A endpoint, call list-workspace-apps with includeAgentCards=true. If you have not requested that probe, absence of agent-card fields means unchecked, not unavailable.\n- When creating a new workspace app, create a separate app under apps/<app-id> with apps/<app-id>/package.json including a concise generated description, mount it at /<app-id>, use relative /<app-id> links, never hardcode localhost or dev ports, use shadcn/ui with @tabler/icons-react rather than lucide-react, and keep the configureClientRouterBasename() call in app/entry.client.tsx so the app hydrates at /<app-id>. There is no separate workspace app registry to edit.\n- When a user asks to rename an existing workspace app or change its Dispatch title/description, call update-workspace-app-metadata with the existing appId from list-workspace-apps. This is a metadata-only edit — never call start-workspace-app-creation, which creates a new app and Builder branch.\n- When an explicit app-creation request already includes a source brief or a concrete repeatable workflow, call start-workspace-app-creation without asking non-blocking product or UX questions. Choose recommended defaults, let the Builder handoff record assumptions, and ask only for authorization, credentials, a destructive action, an ambiguous target workspace, or a genuinely missing workflow.\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- Treat the apps in the available-apps context as existing neighbors available through links and A2A. 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- Owners and admins can hide Agent-Native.com hosted apps for the organization with set-hosted-apps-settings; get-hosted-apps-settings reads it. When hidden they are left out of available-apps and list-connected-agents marks them hiddenFromLaunchers, but call-agent by name still reaches them.\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- For broad provider searches, joins, classification, corpus counts, or absence claims, fetch every relevant page or an explicitly bounded cohort, stage/save large responses with stageAs/saveToFile/fetchAllPages, and reduce them with query-staged-dataset or run-code. Report source, filters, row counts, pagination, truncation, failed pages, and uncovered gaps.\n- Use list-dispatch-usage-metrics only for this workspace's own members' agent/LLM usage and spend, or aggregate adoption of an accessible workspace app. For agent/LLM usage or spend, call it with scope=workspace and the requested sinceDays; it reports workspace-member usage only. For app adoption, call it with scope=app and appId; app creators can see aggregate daily/weekly active users and tracked actions for their own app, while organization owners/admins can inspect any accessible app. App scope omits individual users and prompt previews. External customer, account, or company usage, credits, activity, or product data belongs to the relevant app: use its identity in <relevant-apps> when clear, or call describe-workspace-apps with a query when ownership is unclear. Do not use this action for external customer/account/company data.\n\nWhen a user asks for something like a digest, reminder, routing rule, or saved behavior:\n- First decide whether it should be a resource, a recurring job, a destination, or a delegated task.\n- Keep responses concise and operational.\n- Avoid inventing integrations or destinations that are not configured yet.${builderManagedAppsNote()}`,\n});\n\n/**\n * A Builder-linked workspace builds its apps in Builder; app-creation actions\n * return a link there, and the agent should offer it rather than retry.\n */\nfunction builderManagedAppsNote(): string {\n const url = getBuilderWorkspaceUrl();\n if (!url) return \"\";\n return `\n\nThis workspace is managed in Builder. Never create, scaffold, remix, or install workspace apps from here. When someone asks for a new app, point them to ${url} and tell them to use New app there.`;\n}\n"]}
@@ -17,7 +17,7 @@ Default posture:
17
17
  - Use relevant-apps prompt profiles first, then list-connected-agents when you need fresh details. If that context is absent or does not list the needed capability, search with describe-workspace-apps query="<capability>" before assuming a request must be handled locally.
18
18
  - Hosted/connected A2A neighbors such as Analytics and Content come from the relevant-apps context or list-connected-agents. If neither identifies the needed capability, use describe-workspace-apps before concluding a connected agent is unavailable. list-workspace-apps only inventories apps mounted inside this workspace deployment; never use a missing row there to conclude that a connected agent is unavailable.
19
19
  - When asked whether a mounted workspace app exposes an agent card or A2A endpoint, call list-workspace-apps with includeAgentCards=true. Without that probe, missing agent-card fields mean unchecked, not unavailable.
20
- - 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.
20
+ - Treat the apps in the relevant-apps context as existing neighbors available through links and A2A. 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.
21
21
  - 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.
22
22
  - Keep durable memory and operating instructions in resources rather than ephemeral chat.
23
23
  - Reply in the originating thread unless the user explicitly asks you to send to a saved destination.
@@ -32,9 +32,9 @@ When a user asks for something:
32
32
  - 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 using Builder/managed LLM for that app; keep bring-your-own provider keys as a secondary option only if the user asks.
33
33
  - 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.
34
34
  - If a user asks to rename an existing workspace app or change its Dispatch title/description, call update-workspace-app-metadata with the existing appId from list-workspace-apps. Do not call start-workspace-app-creation for a metadata-only edit because that starts a new app and Builder branch.
35
- - 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 app from that template.
35
+ - 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 app through A2A. Do not ask Builder to scaffold those apps as children of the new app unless the user explicitly asks for a customized app from that template.
36
36
  - 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.
37
- - 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. When the prompt contains a source brief or concrete repeatable workflow, choose recommended defaults and do not ask non-blocking product or UX questions; ask only for authorization, credentials, a destructive action, an ambiguous target workspace, or a genuinely missing workflow. 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 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. Dispatch reuses the connected workspace Builder project or provisions it through the Builder Projects API when an organization owner/admin starts the first app; members need an owner/admin to configure the project first. Do not ask the user to use Builder org/beta settings. If it returns mode "builder-unavailable", the action also returns a \`reason\` code plus a user-facing \`message\` and (for some reasons) an operator-facing \`detail\`. Relay the \`message\` to the user as-is; only surface \`detail\` when the user is clearly an operator debugging the deployment (e.g. troubleshooting a broken Builder connection for the workspace), never as routine detail for an end user. If it returns mode "app-id-taken", the app id is already in use: \`conflict\` is "registered" for a live app or "pending" for a creation in flight, and \`owner\` names who started it. Relay the \`message\` and propose a different app id; never retry the same id.
37
+ - 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. When the prompt contains a source brief or concrete repeatable workflow, choose recommended defaults and do not ask non-blocking product or UX questions; ask only for authorization, credentials, a destructive action, an ambiguous target workspace, or a genuinely missing workflow. 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 keeping the configureClientRouterBasename() call in app/entry.client.tsx. 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. Dispatch reuses the connected workspace Builder project or provisions it through the Builder Projects API when an organization owner/admin starts the first app; members need an owner/admin to configure the project first. Do not ask the user to use Builder org/beta settings. If it returns mode "builder-unavailable", the action also returns a \`reason\` code plus a user-facing \`message\` and (for some reasons) an operator-facing \`detail\`. Relay the \`message\` to the user as-is; only surface \`detail\` when the user is clearly an operator debugging the deployment (e.g. troubleshooting a broken Builder connection for the workspace), never as routine detail for an end user. If it returns mode "app-id-taken", the app id is already in use: \`conflict\` is "registered" for a live app or "pending" for a creation in flight, and \`owner\` names who started it. Relay the \`message\` and propose a different app id; never retry the same id.
38
38
  - For digests, reminders, or saved behavior, prefer recurring jobs, resources, or destinations over chat replies.
39
39
  - Keep responses concise and operational — messaging platforms have character limits.
40
40
  - Use markdown sparingly (bold and lists are fine, avoid complex formatting).
@@ -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,+BAA+B,GAChC,MAAM,iCAAiC,CAAC;AAEzC,MAAM,0BAA0B,GAAG;IACjC,GAAG,eAAe;IAClB,OAAO,EAAE;QACP,GAAG,eAAe,CAAC,OAAO;QAC1B,SAAS,EAAE,KAAK;KACjB;CACF,CAAC;AAEF,MAAM,kCAAkC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4FA6BiD,CAAC;AAE7F,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,uBAAuB,EAAE,+BAA+B;QACxD,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 resolveDispatchExecutionContext,\n} from \"../lib/dispatch-integrations.js\";\n\nconst dispatchIntegrationActions = {\n ...dispatchActions,\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.\n- Use relevant-apps prompt profiles first, then list-connected-agents when you need fresh details. If that context is absent or does not list the needed capability, search with describe-workspace-apps query=\"<capability>\" before assuming a request must be handled locally.\n- Hosted/connected A2A neighbors such as Analytics and Content come from the relevant-apps context or list-connected-agents. If neither identifies the needed capability, use describe-workspace-apps before concluding a connected agent is unavailable. list-workspace-apps only inventories apps mounted inside this workspace deployment; never use a missing row there to conclude that a connected agent is unavailable.\n- When asked whether a mounted workspace app exposes an agent card or A2A endpoint, 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- Synthetic uptime, health-check, and URL-availability monitors belong to Analytics, even when the monitored target is another app such as Clips. Delegate creation to Analytics and relay its exact monitor URL. Dispatch-native recurring jobs are for reminders, digests, and agent workflows, not HTTP uptime probes.\n- Route by the requested artifact type, not by organization-specific names stored in code. For structured records, databases, tables, queues, boards, and intake forms, resolve the owning app and canonical destination from loaded workspace instructions/resources plus discovered app capabilities; do not assume Content, a database ID, schema, owner, or required fields. Visual designs, mockups, wireframes, screens, and interfaces belong to Design. A trusted Required target agent hint in integration context is authoritative.\n- When delegating structured intake to the resolved owning app, preserve the exact Source thread URL and the workspace instruction context, inspect the destination's current required fields, ask only for missing values, submit once, verify the saved record, and return the exact link.\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 using 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 user asks to rename an existing workspace app or change its Dispatch title/description, call update-workspace-app-metadata with the existing appId from list-workspace-apps. Do not call start-workspace-app-creation for a metadata-only edit because that starts a new app and Builder branch.\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 app from that template.\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. When the prompt contains a source brief or concrete repeatable workflow, choose recommended defaults and do not ask non-blocking product or UX questions; ask only for authorization, credentials, a destructive action, an ambiguous target workspace, or a genuinely missing workflow. 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 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. Dispatch reuses the connected workspace Builder project or provisions it through the Builder Projects API when an organization owner/admin starts the first app; members need an owner/admin to configure the project first. Do not ask the user to use Builder org/beta settings. If it returns mode \"builder-unavailable\", the action also returns a \\`reason\\` code plus a user-facing \\`message\\` and (for some reasons) an operator-facing \\`detail\\`. Relay the \\`message\\` to the user as-is; only surface \\`detail\\` when the user is clearly an operator debugging the deployment (e.g. troubleshooting a broken Builder connection for the workspace), never as routine detail for an end user. If it returns mode \"app-id-taken\", the app id is already in use: \\`conflict\\` is \"registered\" for a live app or \"pending\" for a creation in flight, and \\`owner\\` names who started it. Relay the \\`message\\` and propose a different app id; never retry the same id.\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\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 resolveExecutionContext: resolveDispatchExecutionContext,\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,+BAA+B,GAChC,MAAM,iCAAiC,CAAC;AAEzC,MAAM,0BAA0B,GAAG;IACjC,GAAG,eAAe;IAClB,OAAO,EAAE;QACP,GAAG,eAAe,CAAC,OAAO;QAC1B,SAAS,EAAE,KAAK;KACjB;CACF,CAAC;AAEF,MAAM,kCAAkC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4FA6BiD,CAAC;AAE7F,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,uBAAuB,EAAE,+BAA+B;QACxD,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 resolveDispatchExecutionContext,\n} from \"../lib/dispatch-integrations.js\";\n\nconst dispatchIntegrationActions = {\n ...dispatchActions,\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.\n- Use relevant-apps prompt profiles first, then list-connected-agents when you need fresh details. If that context is absent or does not list the needed capability, search with describe-workspace-apps query=\"<capability>\" before assuming a request must be handled locally.\n- Hosted/connected A2A neighbors such as Analytics and Content come from the relevant-apps context or list-connected-agents. If neither identifies the needed capability, use describe-workspace-apps before concluding a connected agent is unavailable. list-workspace-apps only inventories apps mounted inside this workspace deployment; never use a missing row there to conclude that a connected agent is unavailable.\n- When asked whether a mounted workspace app exposes an agent card or A2A endpoint, call list-workspace-apps with includeAgentCards=true. Without that probe, missing agent-card fields mean unchecked, not unavailable.\n- Treat the apps in the relevant-apps context as existing neighbors available through links and A2A. 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- Synthetic uptime, health-check, and URL-availability monitors belong to Analytics, even when the monitored target is another app such as Clips. Delegate creation to Analytics and relay its exact monitor URL. Dispatch-native recurring jobs are for reminders, digests, and agent workflows, not HTTP uptime probes.\n- Route by the requested artifact type, not by organization-specific names stored in code. For structured records, databases, tables, queues, boards, and intake forms, resolve the owning app and canonical destination from loaded workspace instructions/resources plus discovered app capabilities; do not assume Content, a database ID, schema, owner, or required fields. Visual designs, mockups, wireframes, screens, and interfaces belong to Design. A trusted Required target agent hint in integration context is authoritative.\n- When delegating structured intake to the resolved owning app, preserve the exact Source thread URL and the workspace instruction context, inspect the destination's current required fields, ask only for missing values, submit once, verify the saved record, and return the exact link.\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 using 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 user asks to rename an existing workspace app or change its Dispatch title/description, call update-workspace-app-metadata with the existing appId from list-workspace-apps. Do not call start-workspace-app-creation for a metadata-only edit because that starts a new app and Builder branch.\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 app through A2A. Do not ask Builder to scaffold those apps as children of the new app unless the user explicitly asks for a customized app from that template.\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. When the prompt contains a source brief or concrete repeatable workflow, choose recommended defaults and do not ask non-blocking product or UX questions; ask only for authorization, credentials, a destructive action, an ambiguous target workspace, or a genuinely missing workflow. 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 keeping the configureClientRouterBasename() call in app/entry.client.tsx. 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. Dispatch reuses the connected workspace Builder project or provisions it through the Builder Projects API when an organization owner/admin starts the first app; members need an owner/admin to configure the project first. Do not ask the user to use Builder org/beta settings. If it returns mode \"builder-unavailable\", the action also returns a \\`reason\\` code plus a user-facing \\`message\\` and (for some reasons) an operator-facing \\`detail\\`. Relay the \\`message\\` to the user as-is; only surface \\`detail\\` when the user is clearly an operator debugging the deployment (e.g. troubleshooting a broken Builder connection for the workspace), never as routine detail for an end user. If it returns mode \"app-id-taken\", the app id is already in use: \\`conflict\\` is \"registered\" for a live app or \"pending\" for a creation in flight, and \\`owner\\` names who started it. Relay the \\`message\\` and propose a different app id; never retry the same id.\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\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 resolveExecutionContext: resolveDispatchExecutionContext,\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"]}
@@ -0,0 +1,3 @@
1
+ /** The stored setting is not `{ showHostedApps: boolean }`; saving repairs it. */
2
+ export declare const HOSTED_APPS_SETTINGS_INVALID_ERROR_CODE = "dispatch_hosted_apps_settings_invalid";
3
+ //# sourceMappingURL=hosted-apps-settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hosted-apps-settings.d.ts","sourceRoot":"","sources":["../../src/shared/hosted-apps-settings.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,eAAO,MAAM,uCAAuC,0CACX,CAAC"}
@@ -0,0 +1,3 @@
1
+ /** The stored setting is not `{ showHostedApps: boolean }`; saving repairs it. */
2
+ export const HOSTED_APPS_SETTINGS_INVALID_ERROR_CODE = "dispatch_hosted_apps_settings_invalid";
3
+ //# sourceMappingURL=hosted-apps-settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hosted-apps-settings.js","sourceRoot":"","sources":["../../src/shared/hosted-apps-settings.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,MAAM,CAAC,MAAM,uCAAuC,GAClD,uCAAuC,CAAC","sourcesContent":["/** The stored setting is not `{ showHostedApps: boolean }`; saving repairs it. */\nexport const HOSTED_APPS_SETTINGS_INVALID_ERROR_CODE =\n \"dispatch_hosted_apps_settings_invalid\";\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/dispatch",
3
- "version": "0.41.1-nightly-20261104110058",
3
+ "version": "0.42.0",
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": {
@@ -90,7 +90,7 @@
90
90
  "tailwind-merge": "^3.5.0",
91
91
  "vaul": "^1.1.2",
92
92
  "zod": "^4.3.6",
93
- "@agent-native/toolkit": "0.224.0-nightly-20261104110058"
93
+ "@agent-native/toolkit": "0.224.0"
94
94
  },
95
95
  "devDependencies": {
96
96
  "@react-router/dev": "^8.1.0",
@@ -104,10 +104,10 @@
104
104
  "typescript-7": "npm:typescript@^7.0.2",
105
105
  "vite": "8.1.0",
106
106
  "vitest": "^4.1.5",
107
- "@agent-native/core": "0.224.0-nightly-20261104110058"
107
+ "@agent-native/core": "0.224.0"
108
108
  },
109
109
  "peerDependencies": {
110
- "@agent-native/core": "0.224.0-nightly-20261104110058",
110
+ "@agent-native/core": ">=0.224.0-0",
111
111
  "react": ">=19.2.7",
112
112
  "react-dom": ">=19.2.7",
113
113
  "react-router": ">=8"
@@ -0,0 +1,29 @@
1
+ import { defineAction, fail } from "@agent-native/core/action";
2
+ import { getRequestUserEmail } from "@agent-native/core/server";
3
+ import { z } from "zod";
4
+
5
+ import { hostedAppsSettingsViewSchema } from "../server/lib/hosted-apps-settings-schema.js";
6
+ import {
7
+ InvalidHostedAppsSettingsError,
8
+ readHostedAppsSettings,
9
+ } from "../server/lib/hosted-apps-settings.js";
10
+
11
+ export default defineAction({
12
+ description:
13
+ "Read whether this organization shows Agent-Native.com hosted apps (built-in apps such as Mail or Analytics) in Dispatch app lists and agent context. When showHostedApps is false they are hidden; call-agent by name still reaches them. list-connected-agents lists them, marked hiddenFromLaunchers while hidden.",
14
+ schema: z.object({}),
15
+ outputSchema: hostedAppsSettingsViewSchema,
16
+ http: { method: "GET" },
17
+ readOnly: true,
18
+ run: async () => {
19
+ if (!getRequestUserEmail()) {
20
+ fail("Sign in to read the Agent-Native.com hosted apps setting.", {
21
+ errorCode: "dispatch_user_identity_missing",
22
+ statusCode: 401,
23
+ });
24
+ }
25
+ const { showHostedApps, invalid } = await readHostedAppsSettings();
26
+ if (invalid) throw new InvalidHostedAppsSettingsError();
27
+ return { showHostedApps };
28
+ },
29
+ });
@@ -0,0 +1,100 @@
1
+ import { beforeEach, describe, expect, it, vi } from "vitest";
2
+
3
+ const mocks = vi.hoisted(() => ({
4
+ authorizeOrgOwnerOrAdmin: vi.fn(),
5
+ email: vi.fn<() => string | undefined>(() => "member@example.test"),
6
+ readHostedAppsSettings: vi.fn(),
7
+ setHostedAppsSettings: vi.fn(),
8
+ recordAudit: vi.fn(),
9
+ }));
10
+
11
+ vi.mock("@agent-native/core/server", () => ({
12
+ getRequestUserEmail: () => mocks.email(),
13
+ }));
14
+
15
+ vi.mock("../server/lib/app-roles.js", () => ({
16
+ authorizeOrgOwnerOrAdmin: mocks.authorizeOrgOwnerOrAdmin,
17
+ }));
18
+
19
+ vi.mock("../server/lib/dispatch-store.js", () => ({
20
+ recordAudit: mocks.recordAudit,
21
+ }));
22
+
23
+ vi.mock("../server/lib/hosted-apps-settings.js", async (importOriginal) => ({
24
+ InvalidHostedAppsSettingsError: (
25
+ await importOriginal<
26
+ typeof import("../server/lib/hosted-apps-settings.js")
27
+ >()
28
+ ).InvalidHostedAppsSettingsError,
29
+ HOSTED_APPS_SETTINGS_KEY: "dispatch-hosted-apps-settings",
30
+ readHostedAppsSettings: mocks.readHostedAppsSettings,
31
+ setHostedAppsSettings: mocks.setHostedAppsSettings,
32
+ }));
33
+
34
+ const getAction = (await import("./get-hosted-apps-settings.js")).default;
35
+ const setAction = (await import("./set-hosted-apps-settings.js")).default;
36
+
37
+ describe("hosted apps settings actions", () => {
38
+ beforeEach(() => {
39
+ vi.clearAllMocks();
40
+ mocks.email.mockReturnValue("member@example.test");
41
+ mocks.readHostedAppsSettings.mockResolvedValue({
42
+ showHostedApps: false,
43
+ invalid: false,
44
+ });
45
+ mocks.setHostedAppsSettings.mockImplementation(async (value) => value);
46
+ mocks.authorizeOrgOwnerOrAdmin.mockResolvedValue(undefined);
47
+ });
48
+
49
+ it("lets any signed-in member read the setting", async () => {
50
+ await expect(getAction.run({})).resolves.toEqual({
51
+ showHostedApps: false,
52
+ });
53
+ expect(mocks.authorizeOrgOwnerOrAdmin).not.toHaveBeenCalled();
54
+ });
55
+
56
+ it("refuses to read without a signed-in user", async () => {
57
+ mocks.email.mockReturnValue(undefined);
58
+ await expect(getAction.run({})).rejects.toThrow("Sign in");
59
+ expect(mocks.readHostedAppsSettings).not.toHaveBeenCalled();
60
+ });
61
+
62
+ it("reports an invalid stored value with the code Settings repairs from", async () => {
63
+ mocks.readHostedAppsSettings.mockResolvedValue({
64
+ showHostedApps: false,
65
+ invalid: true,
66
+ });
67
+ await expect(getAction.run({})).rejects.toMatchObject({
68
+ errorCode: "dispatch_hosted_apps_settings_invalid",
69
+ statusCode: 409,
70
+ });
71
+ });
72
+
73
+ it("denies callers who are not org owners or admins before writing", async () => {
74
+ mocks.authorizeOrgOwnerOrAdmin.mockRejectedValue(
75
+ Object.assign(new Error("Only organization owners and admins"), {
76
+ statusCode: 403,
77
+ }),
78
+ );
79
+ await expect(setAction.run({ showHostedApps: false })).rejects.toThrow(
80
+ "Only organization owners and admins",
81
+ );
82
+ expect(mocks.setHostedAppsSettings).not.toHaveBeenCalled();
83
+ expect(mocks.recordAudit).not.toHaveBeenCalled();
84
+ });
85
+
86
+ it("writes, records the change in Dispatch's audit log, and returns the read shape", async () => {
87
+ await expect(setAction.run({ showHostedApps: false })).resolves.toEqual({
88
+ showHostedApps: false,
89
+ });
90
+ expect(mocks.setHostedAppsSettings).toHaveBeenCalledWith({
91
+ showHostedApps: false,
92
+ });
93
+ expect(mocks.recordAudit).toHaveBeenCalledWith(
94
+ expect.objectContaining({
95
+ targetId: "dispatch-hosted-apps-settings",
96
+ summary: "Hid Agent-Native.com hosted apps",
97
+ }),
98
+ );
99
+ });
100
+ });