@agent-native/dispatch 0.23.5 → 0.24.2

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 (224) hide show
  1. package/dist/actions/list-connected-agents.d.ts +1 -0
  2. package/dist/actions/list-connected-agents.js +5 -1
  3. package/dist/actions/list-connected-agents.js.map +1 -1
  4. package/dist/actions/list-dispatch-usage-metrics.d.ts +2 -0
  5. package/dist/actions/list-dispatch-usage-metrics.js +11 -1
  6. package/dist/actions/list-dispatch-usage-metrics.js.map +1 -1
  7. package/dist/actions/provider-api-register.d.ts +12 -12
  8. package/dist/actions/remix-workspace-template.js +4 -3
  9. package/dist/actions/remix-workspace-template.js.map +1 -1
  10. package/dist/actions/scaffold-workspace-app.js +3 -2
  11. package/dist/actions/scaffold-workspace-app.js.map +1 -1
  12. package/dist/actions/start-workspace-app-creation.js +3 -2
  13. package/dist/actions/start-workspace-app-creation.js.map +1 -1
  14. package/dist/actions/view-screen.js +11 -1
  15. package/dist/actions/view-screen.js.map +1 -1
  16. package/dist/components/admin-navigation.d.ts.map +1 -1
  17. package/dist/components/admin-navigation.js +21 -5
  18. package/dist/components/admin-navigation.js.map +1 -1
  19. package/dist/components/app-icon.js +1 -1
  20. package/dist/components/app-icon.js.map +1 -1
  21. package/dist/components/app-list-row.d.ts +1 -1
  22. package/dist/components/app-list-row.d.ts.map +1 -1
  23. package/dist/components/app-list-row.js +1 -1
  24. package/dist/components/app-list-row.js.map +1 -1
  25. package/dist/components/app-open-actions.d.ts +1 -17
  26. package/dist/components/app-open-actions.d.ts.map +1 -1
  27. package/dist/components/app-open-actions.js +1 -20
  28. package/dist/components/app-open-actions.js.map +1 -1
  29. package/dist/components/app-row-settings.d.ts +6 -0
  30. package/dist/components/app-row-settings.d.ts.map +1 -0
  31. package/dist/components/app-row-settings.js +11 -0
  32. package/dist/components/app-row-settings.js.map +1 -0
  33. package/dist/components/automation-run-history-dialog.d.ts.map +1 -1
  34. package/dist/components/automation-run-history-dialog.js +3 -2
  35. package/dist/components/automation-run-history-dialog.js.map +1 -1
  36. package/dist/components/connected-app-card.d.ts.map +1 -1
  37. package/dist/components/connected-app-card.js +3 -2
  38. package/dist/components/connected-app-card.js.map +1 -1
  39. package/dist/components/create-app-popover.js +2 -2
  40. package/dist/components/create-app-popover.js.map +1 -1
  41. package/dist/components/dispatch-control-plane.d.ts.map +1 -1
  42. package/dist/components/dispatch-control-plane.js +23 -7
  43. package/dist/components/dispatch-control-plane.js.map +1 -1
  44. package/dist/components/dispatch-shell.d.ts.map +1 -1
  45. package/dist/components/dispatch-shell.js +5 -1
  46. package/dist/components/dispatch-shell.js.map +1 -1
  47. package/dist/components/docs-link.d.ts +16 -0
  48. package/dist/components/docs-link.d.ts.map +1 -0
  49. package/dist/components/docs-link.js +34 -0
  50. package/dist/components/docs-link.js.map +1 -0
  51. package/dist/components/layout/Layout.d.ts +1 -0
  52. package/dist/components/layout/Layout.d.ts.map +1 -1
  53. package/dist/components/layout/Layout.js +43 -35
  54. package/dist/components/layout/Layout.js.map +1 -1
  55. package/dist/components/messaging-setup-panel.d.ts.map +1 -1
  56. package/dist/components/messaging-setup-panel.js +3 -2
  57. package/dist/components/messaging-setup-panel.js.map +1 -1
  58. package/dist/components/usage-alerts-panel.d.ts +11 -0
  59. package/dist/components/usage-alerts-panel.d.ts.map +1 -0
  60. package/dist/components/usage-alerts-panel.js +125 -0
  61. package/dist/components/usage-alerts-panel.js.map +1 -0
  62. package/dist/components/workspace-app-card.d.ts +3 -1
  63. package/dist/components/workspace-app-card.d.ts.map +1 -1
  64. package/dist/components/workspace-app-card.js +70 -29
  65. package/dist/components/workspace-app-card.js.map +1 -1
  66. package/dist/components/workspace-app-search.d.ts +10 -0
  67. package/dist/components/workspace-app-search.d.ts.map +1 -0
  68. package/dist/components/workspace-app-search.js +27 -0
  69. package/dist/components/workspace-app-search.js.map +1 -0
  70. package/dist/components/workspace-resource-effective-stack.d.ts.map +1 -1
  71. package/dist/components/workspace-resource-effective-stack.js +3 -2
  72. package/dist/components/workspace-resource-effective-stack.js.map +1 -1
  73. package/dist/components/workspace-template-card.d.ts.map +1 -1
  74. package/dist/components/workspace-template-card.js +10 -15
  75. package/dist/components/workspace-template-card.js.map +1 -1
  76. package/dist/hooks/use-navigation-state.d.ts +2 -0
  77. package/dist/hooks/use-navigation-state.d.ts.map +1 -1
  78. package/dist/hooks/use-navigation-state.js +10 -0
  79. package/dist/hooks/use-navigation-state.js.map +1 -1
  80. package/dist/lib/embed-session-recovery.d.ts +3 -0
  81. package/dist/lib/embed-session-recovery.d.ts.map +1 -0
  82. package/dist/lib/embed-session-recovery.js +18 -0
  83. package/dist/lib/embed-session-recovery.js.map +1 -0
  84. package/dist/lib/other-apps.d.ts +2 -0
  85. package/dist/lib/other-apps.d.ts.map +1 -1
  86. package/dist/lib/other-apps.js.map +1 -1
  87. package/dist/lib/workspace-app-layout.d.ts +19 -0
  88. package/dist/lib/workspace-app-layout.d.ts.map +1 -0
  89. package/dist/lib/workspace-app-layout.js +75 -0
  90. package/dist/lib/workspace-app-layout.js.map +1 -0
  91. package/dist/lib/workspace-apps.d.ts +14 -0
  92. package/dist/lib/workspace-apps.d.ts.map +1 -1
  93. package/dist/lib/workspace-apps.js +66 -0
  94. package/dist/lib/workspace-apps.js.map +1 -1
  95. package/dist/lib/workspace-apps.test.d.ts +2 -0
  96. package/dist/lib/workspace-apps.test.d.ts.map +1 -0
  97. package/dist/lib/workspace-apps.test.js +33 -0
  98. package/dist/lib/workspace-apps.test.js.map +1 -0
  99. package/dist/routes/pages/$appId.d.ts.map +1 -1
  100. package/dist/routes/pages/$appId.js +4 -2
  101. package/dist/routes/pages/$appId.js.map +1 -1
  102. package/dist/routes/pages/agents.d.ts.map +1 -1
  103. package/dist/routes/pages/agents.js +2 -3
  104. package/dist/routes/pages/agents.js.map +1 -1
  105. package/dist/routes/pages/apps.$appId.d.ts.map +1 -1
  106. package/dist/routes/pages/apps.$appId.js +17 -13
  107. package/dist/routes/pages/apps.$appId.js.map +1 -1
  108. package/dist/routes/pages/apps.d.ts.map +1 -1
  109. package/dist/routes/pages/apps.js +31 -16
  110. package/dist/routes/pages/apps.js.map +1 -1
  111. package/dist/routes/pages/audit.d.ts.map +1 -1
  112. package/dist/routes/pages/audit.js +2 -1
  113. package/dist/routes/pages/audit.js.map +1 -1
  114. package/dist/routes/pages/chat.d.ts.map +1 -1
  115. package/dist/routes/pages/chat.js +7 -2
  116. package/dist/routes/pages/chat.js.map +1 -1
  117. package/dist/routes/pages/dreams.d.ts.map +1 -1
  118. package/dist/routes/pages/dreams.js +14 -18
  119. package/dist/routes/pages/dreams.js.map +1 -1
  120. package/dist/routes/pages/integrations.d.ts.map +1 -1
  121. package/dist/routes/pages/integrations.js +51 -14
  122. package/dist/routes/pages/integrations.js.map +1 -1
  123. package/dist/routes/pages/metrics.d.ts.map +1 -1
  124. package/dist/routes/pages/metrics.js +146 -17
  125. package/dist/routes/pages/metrics.js.map +1 -1
  126. package/dist/routes/pages/operations.js +1 -1
  127. package/dist/routes/pages/operations.js.map +1 -1
  128. package/dist/routes/pages/settings.d.ts.map +1 -1
  129. package/dist/routes/pages/settings.js +19 -2
  130. package/dist/routes/pages/settings.js.map +1 -1
  131. package/dist/routes/pages/thread-debug.d.ts.map +1 -1
  132. package/dist/routes/pages/thread-debug.js +5 -8
  133. package/dist/routes/pages/thread-debug.js.map +1 -1
  134. package/dist/routes/pages/vault.d.ts.map +1 -1
  135. package/dist/routes/pages/vault.js +1 -1
  136. package/dist/routes/pages/vault.js.map +1 -1
  137. package/dist/routes/pages/workspace.d.ts.map +1 -1
  138. package/dist/routes/pages/workspace.js +13 -10
  139. package/dist/routes/pages/workspace.js.map +1 -1
  140. package/dist/server/index.d.ts.map +1 -1
  141. package/dist/server/index.js +3 -0
  142. package/dist/server/index.js.map +1 -1
  143. package/dist/server/lib/app-creation-store.d.ts.map +1 -1
  144. package/dist/server/lib/app-creation-store.js +12 -52
  145. package/dist/server/lib/app-creation-store.js.map +1 -1
  146. package/dist/server/lib/usage-metrics-store.d.ts +17 -0
  147. package/dist/server/lib/usage-metrics-store.d.ts.map +1 -1
  148. package/dist/server/lib/usage-metrics-store.js +187 -30
  149. package/dist/server/lib/usage-metrics-store.js.map +1 -1
  150. package/dist/server/plugins/agent-chat.js +2 -0
  151. package/dist/server/plugins/agent-chat.js.map +1 -1
  152. package/dist/server/plugins/integrations.d.ts.map +1 -1
  153. package/dist/server/plugins/integrations.js +1 -0
  154. package/dist/server/plugins/integrations.js.map +1 -1
  155. package/dist/shared/app-roles.d.ts +9 -0
  156. package/dist/shared/app-roles.d.ts.map +1 -0
  157. package/dist/shared/app-roles.js +7 -0
  158. package/dist/shared/app-roles.js.map +1 -0
  159. package/package.json +4 -3
  160. package/src/actions/list-connected-agents.spec.ts +73 -0
  161. package/src/actions/list-connected-agents.ts +6 -2
  162. package/src/actions/list-dispatch-usage-metrics.ts +11 -1
  163. package/src/actions/remix-workspace-template.spec.ts +29 -0
  164. package/src/actions/remix-workspace-template.ts +13 -6
  165. package/src/actions/scaffold-workspace-app.ts +12 -5
  166. package/src/actions/start-workspace-app-creation.ts +8 -2
  167. package/src/actions/view-screen.ts +13 -1
  168. package/src/components/admin-navigation.tsx +57 -11
  169. package/src/components/app-icon.spec.tsx +34 -0
  170. package/src/components/app-icon.tsx +1 -1
  171. package/src/components/app-list-row.tsx +1 -2
  172. package/src/components/app-open-actions.tsx +5 -114
  173. package/src/components/app-row-settings.tsx +54 -0
  174. package/src/components/automation-run-history-dialog.tsx +8 -4
  175. package/src/components/connected-app-card.tsx +3 -2
  176. package/src/components/create-app-popover.spec.tsx +1 -1
  177. package/src/components/create-app-popover.tsx +2 -2
  178. package/src/components/dispatch-control-plane.spec.tsx +44 -3
  179. package/src/components/dispatch-control-plane.tsx +83 -27
  180. package/src/components/dispatch-shell.tsx +7 -1
  181. package/src/components/docs-link.tsx +52 -0
  182. package/src/components/layout/Layout.spec.tsx +13 -0
  183. package/src/components/layout/Layout.tsx +63 -32
  184. package/src/components/messaging-setup-panel.tsx +10 -3
  185. package/src/components/usage-alerts-panel.tsx +445 -0
  186. package/src/components/workspace-app-card.spec.tsx +130 -9
  187. package/src/components/workspace-app-card.tsx +128 -71
  188. package/src/components/workspace-app-search.tsx +96 -0
  189. package/src/components/workspace-resource-effective-stack.tsx +10 -5
  190. package/src/components/workspace-template-card.spec.tsx +53 -24
  191. package/src/components/workspace-template-card.tsx +16 -26
  192. package/src/hooks/use-navigation-state.ts +12 -0
  193. package/src/lib/embed-session-recovery.spec.ts +63 -0
  194. package/src/lib/embed-session-recovery.ts +26 -0
  195. package/src/lib/other-apps.ts +2 -0
  196. package/src/lib/workspace-app-layout.spec.ts +47 -0
  197. package/src/lib/workspace-app-layout.ts +121 -0
  198. package/src/lib/workspace-apps.test.ts +64 -0
  199. package/src/lib/workspace-apps.ts +82 -0
  200. package/src/routes/pages/$appId.tsx +3 -6
  201. package/src/routes/pages/agents.tsx +6 -3
  202. package/src/routes/pages/apps.$appId.tsx +16 -17
  203. package/src/routes/pages/apps.tsx +116 -62
  204. package/src/routes/pages/audit.tsx +28 -8
  205. package/src/routes/pages/chat.spec.tsx +10 -0
  206. package/src/routes/pages/chat.tsx +8 -0
  207. package/src/routes/pages/dreams.tsx +92 -142
  208. package/src/routes/pages/integrations.tsx +147 -68
  209. package/src/routes/pages/metrics.tsx +460 -102
  210. package/src/routes/pages/operations.tsx +2 -2
  211. package/src/routes/pages/settings.tsx +27 -10
  212. package/src/routes/pages/thread-debug.tsx +18 -36
  213. package/src/routes/pages/vault.tsx +7 -2
  214. package/src/routes/pages/workspace.tsx +92 -56
  215. package/src/server/index.ts +4 -0
  216. package/src/server/lib/app-creation-store.spec.ts +90 -43
  217. package/src/server/lib/app-creation-store.ts +15 -61
  218. package/src/server/lib/usage-metrics-store.spec.ts +117 -0
  219. package/src/server/lib/usage-metrics-store.ts +249 -30
  220. package/src/server/plugins/agent-chat.spec.ts +4 -0
  221. package/src/server/plugins/agent-chat.ts +2 -0
  222. package/src/server/plugins/integrations.ts +1 -0
  223. package/src/shared/app-roles.ts +8 -0
  224. package/src/styles/dispatch.css +9 -0
@@ -27,12 +27,21 @@ import {
27
27
  } from "../../components/ui/collapsible";
28
28
  import { Skeleton } from "../../components/ui/skeleton";
29
29
  import { WorkspaceAppCard } from "../../components/workspace-app-card";
30
+ import {
31
+ WorkspaceAppSearch,
32
+ WorkspaceAppSearchEmpty,
33
+ } from "../../components/workspace-app-search";
30
34
  import type {
31
35
  CuratedWorkspaceTemplatesResult,
32
36
  WorkspaceTemplateLabels,
33
37
  } from "../../components/workspace-template-card";
34
38
  import type { ConnectedAppSummary } from "../../lib/other-apps";
35
39
  import { cn } from "../../lib/utils";
40
+ import {
41
+ orderWorkspaceApps,
42
+ useWorkspaceAppLayout,
43
+ workspaceAppMatchesQuery,
44
+ } from "../../lib/workspace-app-layout";
36
45
  import {
37
46
  isWorkspaceAppVisibleInDefaultLaunchers,
38
47
  type WorkspaceAppSummary,
@@ -57,6 +66,8 @@ function AppsRoute() {
57
66
  const t = useT();
58
67
  const [showPending, setShowPending] = useState(false);
59
68
  const [showHidden, setShowHidden] = useState(false);
69
+ const [searchQuery, setSearchQuery] = useState("");
70
+ const { layout, persistenceError, togglePinned } = useWorkspaceAppLayout();
60
71
  const appsQuery = useActionQuery("list-workspace-apps", {
61
72
  includeAgentCards: false,
62
73
  includeArchived: true,
@@ -81,6 +92,22 @@ function AppsRoute() {
81
92
  const activeApps = visibleApps.filter((app) => app.status !== "pending");
82
93
  const pendingApps = visibleApps.filter((app) => app.status === "pending");
83
94
  const archivedApps = allApps.filter((app) => app.archived);
95
+ const orderedActiveApps = orderWorkspaceApps(activeApps, layout);
96
+ const orderedPendingApps = orderWorkspaceApps(pendingApps, layout);
97
+ const orderedArchivedApps = orderWorkspaceApps(archivedApps, layout);
98
+ const filteredActiveApps = orderedActiveApps.filter((app) =>
99
+ workspaceAppMatchesQuery(app, searchQuery),
100
+ );
101
+ const filteredPendingApps = orderedPendingApps.filter((app) =>
102
+ workspaceAppMatchesQuery(app, searchQuery),
103
+ );
104
+ const filteredArchivedApps = orderedArchivedApps.filter((app) =>
105
+ workspaceAppMatchesQuery(app, searchQuery),
106
+ );
107
+ const hasSearchResults =
108
+ filteredActiveApps.length > 0 ||
109
+ filteredPendingApps.length > 0 ||
110
+ filteredArchivedApps.length > 0;
84
111
  const showAppSkeletons = appsLoading && allApps.length === 0;
85
112
  const templateLabels: WorkspaceTemplateLabels = {
86
113
  appId: t("dispatch.pages.remixAppIdLabel"),
@@ -111,7 +138,7 @@ function AppsRoute() {
111
138
  >
112
139
  <div className="space-y-8">
113
140
  <section className="space-y-3">
114
- <div className="flex flex-wrap items-end justify-between gap-3">
141
+ <div className="flex flex-wrap items-center justify-between gap-3">
115
142
  <div className="flex min-w-0 items-start gap-2">
116
143
  <IconApps
117
144
  size={16}
@@ -121,36 +148,44 @@ function AppsRoute() {
121
148
  <h2 className="truncate text-sm font-semibold text-foreground">
122
149
  {t("dispatch.pages.yourApps", { defaultValue: "Your apps" })}
123
150
  </h2>
124
- <p className="mt-0.5 text-xs text-muted-foreground">
125
- {t("dispatch.pages.activeCount", {
126
- count: activeApps.length,
127
- })}
128
- {pendingApps.length > 0
129
- ? ` · ${t("dispatch.pages.pendingCount", {
130
- count: pendingApps.length,
131
- })}`
132
- : ""}
133
- {archivedApps.length > 0
134
- ? ` · ${t("dispatch.pages.hiddenCount", {
135
- count: archivedApps.length,
136
- })}`
137
- : ""}
138
- </p>
139
151
  </div>
140
152
  </div>
141
- {activeApps.length > 0 || pendingApps.length > 0 ? (
142
- <CreateAppPopover
143
- align="end"
144
- trigger={
145
- <Button size="sm" variant="outline">
146
- <IconPlus size={15} className="mr-1.5" />
147
- {t("dispatch.pages.addApp", { defaultValue: "Add app" })}
148
- </Button>
149
- }
150
- />
151
- ) : null}
153
+ <div className="flex min-w-0 flex-wrap items-center justify-end gap-2">
154
+ {!showAppSkeletons && allApps.length > 0 ? (
155
+ <WorkspaceAppSearch
156
+ className="w-full sm:w-[250px]"
157
+ query={searchQuery}
158
+ onQueryChange={setSearchQuery}
159
+ />
160
+ ) : null}
161
+ {activeApps.length > 0 || pendingApps.length > 0 ? (
162
+ <CreateAppPopover
163
+ align="end"
164
+ trigger={
165
+ <Button size="sm" variant="outline">
166
+ <IconPlus size={15} className="mr-1.5" />
167
+ {t("dispatch.pages.addApp", {
168
+ defaultValue: "Add app",
169
+ })}
170
+ </Button>
171
+ }
172
+ />
173
+ ) : null}
174
+ </div>
152
175
  </div>
153
176
 
177
+ {!showAppSkeletons && allApps.length > 0 && persistenceError ? (
178
+ <p className="text-xs text-muted-foreground" role="status">
179
+ {persistenceError === "both"
180
+ ? t("dispatch.pages.appPinSaveFailed", {
181
+ defaultValue: "App pins could not be saved.",
182
+ })
183
+ : t("dispatch.pages.appPinSavedLocally", {
184
+ defaultValue: "App pins are saved on this device only.",
185
+ })}
186
+ </p>
187
+ ) : null}
188
+
154
189
  {appsQuery.isError ? (
155
190
  <ActionQueryError
156
191
  error={appsQuery.error}
@@ -158,25 +193,36 @@ function AppsRoute() {
158
193
  />
159
194
  ) : showAppSkeletons ? (
160
195
  <AppsSkeletonGrid />
161
- ) : activeApps.length > 0 ? (
196
+ ) : !hasSearchResults && searchQuery.trim() ? (
197
+ <WorkspaceAppSearchEmpty
198
+ query={searchQuery}
199
+ onClear={() => setSearchQuery("")}
200
+ />
201
+ ) : filteredActiveApps.length > 0 ? (
162
202
  <AppList className={APP_LIST_GRID_CLASS}>
163
- {activeApps.map((app) => (
203
+ {filteredActiveApps.map((app) => (
164
204
  <WorkspaceAppCard
165
205
  key={app.id}
166
206
  app={app}
167
207
  className={APP_LIST_GRID_ROW_CLASS}
208
+ isPinned={layout.pinnedIds.includes(app.id)}
209
+ onTogglePinned={() => togglePinned(app.id)}
168
210
  />
169
211
  ))}
170
212
  </AppList>
171
- ) : pendingApps.length > 0 ? (
213
+ ) : searchQuery.trim() ? null : pendingApps.length > 0 ? (
172
214
  <EmptyActiveAppsState />
173
215
  ) : (
174
216
  <EmptyAppsState />
175
217
  )}
176
218
  </section>
177
219
 
178
- {pendingApps.length > 0 ? (
179
- <Collapsible open={showPending} onOpenChange={setShowPending}>
220
+ {pendingApps.length > 0 &&
221
+ (!searchQuery.trim() || filteredPendingApps.length > 0) ? (
222
+ <Collapsible
223
+ open={showPending || Boolean(searchQuery.trim())}
224
+ onOpenChange={setShowPending}
225
+ >
180
226
  <section className="space-y-3 border-t pt-6">
181
227
  <div className="flex flex-wrap items-center justify-between gap-3">
182
228
  <div className="flex min-w-0 items-center gap-2">
@@ -202,14 +248,15 @@ function AppsRoute() {
202
248
  size="sm"
203
249
  className="gap-1.5"
204
250
  >
205
- {showPending
251
+ {showPending || Boolean(searchQuery.trim())
206
252
  ? t("dispatch.pages.hidePendingApps")
207
253
  : t("dispatch.pages.showPendingApps")}
208
254
  <IconChevronDown
209
255
  size={14}
210
256
  className={cn(
211
257
  "transition-transform",
212
- showPending && "rotate-180",
258
+ (showPending || Boolean(searchQuery.trim())) &&
259
+ "rotate-180",
213
260
  )}
214
261
  />
215
262
  </Button>
@@ -217,7 +264,7 @@ function AppsRoute() {
217
264
  </div>
218
265
  <CollapsibleContent>
219
266
  <AppList className={APP_LIST_GRID_CLASS}>
220
- {pendingApps.map((app) => (
267
+ {filteredPendingApps.map((app) => (
221
268
  <WorkspaceAppCard
222
269
  key={app.id}
223
270
  app={app}
@@ -230,31 +277,37 @@ function AppsRoute() {
230
277
  </Collapsible>
231
278
  ) : null}
232
279
 
233
- <OtherAppsSection
234
- templates={
235
- curatedTemplatesQuery.data as
236
- | CuratedWorkspaceTemplatesResult
237
- | undefined
238
- }
239
- connectedApps={
240
- connectedAppsQuery.data as ConnectedAppSummary[] | undefined
241
- }
242
- workspaceApps={allApps}
243
- templatesLoading={curatedTemplatesQuery.isLoading}
244
- connectedAppsLoading={connectedAppsQuery.isLoading}
245
- templatesError={curatedTemplatesQuery.error}
246
- connectedAppsError={connectedAppsQuery.error}
247
- onRetryTemplates={() => void curatedTemplatesQuery.refetch()}
248
- onRetryConnectedApps={() => void connectedAppsQuery.refetch()}
249
- templateLabels={templateLabels}
250
- onRemixSuccess={() => {
251
- void appsQuery.refetch();
252
- void curatedTemplatesQuery.refetch();
253
- }}
254
- />
280
+ {!searchQuery.trim() ? (
281
+ <OtherAppsSection
282
+ templates={
283
+ curatedTemplatesQuery.data as
284
+ | CuratedWorkspaceTemplatesResult
285
+ | undefined
286
+ }
287
+ connectedApps={
288
+ connectedAppsQuery.data as ConnectedAppSummary[] | undefined
289
+ }
290
+ workspaceApps={allApps}
291
+ templatesLoading={curatedTemplatesQuery.isLoading}
292
+ connectedAppsLoading={connectedAppsQuery.isLoading}
293
+ templatesError={curatedTemplatesQuery.error}
294
+ connectedAppsError={connectedAppsQuery.error}
295
+ onRetryTemplates={() => void curatedTemplatesQuery.refetch()}
296
+ onRetryConnectedApps={() => void connectedAppsQuery.refetch()}
297
+ templateLabels={templateLabels}
298
+ onRemixSuccess={() => {
299
+ void appsQuery.refetch();
300
+ void curatedTemplatesQuery.refetch();
301
+ }}
302
+ />
303
+ ) : null}
255
304
 
256
- {archivedApps.length > 0 ? (
257
- <Collapsible open={showHidden} onOpenChange={setShowHidden}>
305
+ {archivedApps.length > 0 &&
306
+ (!searchQuery.trim() || filteredArchivedApps.length > 0) ? (
307
+ <Collapsible
308
+ open={showHidden || Boolean(searchQuery.trim())}
309
+ onOpenChange={setShowHidden}
310
+ >
258
311
  <section className="space-y-3">
259
312
  <div className="flex flex-wrap items-center justify-between gap-3 border-t pt-4">
260
313
  <div className="flex min-w-0 items-center gap-2">
@@ -280,14 +333,15 @@ function AppsRoute() {
280
333
  size="sm"
281
334
  className="gap-1.5"
282
335
  >
283
- {showHidden
336
+ {showHidden || Boolean(searchQuery.trim())
284
337
  ? t("dispatch.pages.hide")
285
338
  : t("dispatch.pages.show")}
286
339
  <IconChevronDown
287
340
  size={14}
288
341
  className={cn(
289
342
  "transition-transform",
290
- showHidden && "rotate-180",
343
+ (showHidden || Boolean(searchQuery.trim())) &&
344
+ "rotate-180",
291
345
  )}
292
346
  />
293
347
  </Button>
@@ -295,7 +349,7 @@ function AppsRoute() {
295
349
  </div>
296
350
  <CollapsibleContent>
297
351
  <AppList className={APP_LIST_GRID_CLASS}>
298
- {archivedApps.map((app) => (
352
+ {filteredArchivedApps.map((app) => (
299
353
  <WorkspaceAppCard
300
354
  key={app.id}
301
355
  app={app}
@@ -2,6 +2,7 @@ import { useActionQuery } from "@agent-native/core/client/hooks";
2
2
 
3
3
  import { ActionQueryError } from "../../components/action-query-error";
4
4
  import { DispatchShell } from "../../components/dispatch-shell";
5
+ import { Skeleton } from "../../components/ui/skeleton";
5
6
 
6
7
  export function meta() {
7
8
  return [{ title: "Audit — Dispatch" }];
@@ -16,30 +17,49 @@ export default function AuditRoute() {
16
17
  title="Audit"
17
18
  description="Change history for routes, settings, and approvals."
18
19
  >
19
- <section className="rounded-2xl bg-card p-5">
20
+ <section className="overflow-hidden rounded-lg bg-card">
20
21
  {query.isError ? (
21
22
  <ActionQueryError
22
23
  error={query.error}
23
24
  onRetry={() => void query.refetch()}
24
25
  />
26
+ ) : query.isLoading ? (
27
+ <div>
28
+ {Array.from({ length: 6 }).map((_, index) => (
29
+ <div
30
+ key={index}
31
+ className="grid gap-2 border-b px-4 py-3 last:border-b-0 sm:grid-cols-[minmax(0,1fr)_auto] sm:items-center"
32
+ >
33
+ <div>
34
+ <Skeleton className="h-4 w-2/3 max-w-sm" />
35
+ <Skeleton className="mt-2 h-3 w-1/2 max-w-xs" />
36
+ </div>
37
+ <Skeleton className="h-3 w-28" />
38
+ </div>
39
+ ))}
40
+ </div>
25
41
  ) : (
26
- <div className="space-y-3">
42
+ <div>
27
43
  {(data || []).map((event: any) => (
28
44
  <div
29
45
  key={event.id}
30
- className="rounded-xl border bg-muted/30 px-4 py-3"
46
+ className="grid gap-1 border-b px-4 py-3 last:border-b-0 sm:grid-cols-[minmax(0,1fr)_auto] sm:items-center"
31
47
  >
32
- <div className="text-sm font-medium text-foreground">
33
- {event.summary}
48
+ <div className="min-w-0">
49
+ <div className="truncate text-sm font-medium text-foreground">
50
+ {event.summary}
51
+ </div>
52
+ <div className="mt-1 text-xs text-muted-foreground">
53
+ {event.actor} · {event.action}
54
+ </div>
34
55
  </div>
35
- <div className="mt-1 text-xs text-muted-foreground">
36
- {event.actor} · {event.action} ·{" "}
56
+ <div className="text-xs text-muted-foreground sm:text-right">
37
57
  {new Date(event.createdAt).toLocaleString()}
38
58
  </div>
39
59
  </div>
40
60
  ))}
41
61
  {(data?.length || 0) === 0 && (
42
- <div className="rounded-xl border border-dashed px-4 py-6 text-sm text-muted-foreground">
62
+ <div className="px-4 py-10 text-center text-sm text-muted-foreground">
43
63
  No audit entries yet.
44
64
  </div>
45
65
  )}
@@ -16,6 +16,7 @@ vi.mock("@agent-native/core/client/agent-chat", () => ({
16
16
  return <>{props.composerSlot as ReactNode}</>;
17
17
  },
18
18
  markAgentChatHomeHandoff: vi.fn(),
19
+ readChatFirstMode: () => true,
19
20
  navigateWithAgentChatViewTransition: (
20
21
  navigate: (path: string) => void,
21
22
  path: string,
@@ -23,6 +24,10 @@ vi.mock("@agent-native/core/client/agent-chat", () => ({
23
24
  sendToAgentChat: vi.fn(),
24
25
  }));
25
26
 
27
+ vi.mock("../../components/layout/Layout", () => ({
28
+ useDispatchExtensions: () => undefined,
29
+ }));
30
+
26
31
  vi.mock("@agent-native/core/client/api-path", () => ({
27
32
  agentNativePath: (path: string) => path,
28
33
  appApiPath: (path: string) => path,
@@ -74,6 +79,7 @@ describe("Dispatch ChatRoute", () => {
74
79
  centerComposerWhenEmpty: true,
75
80
  composerLayoutVariant: "hero",
76
81
  composerPlaceholder: "Ask Dispatch...",
82
+ suppressInlineOpenApp: true,
77
83
  });
78
84
  expect(container.textContent).toContain("Chat across your apps");
79
85
  });
@@ -106,6 +112,10 @@ describe("Dispatch ChatRoute", () => {
106
112
  expect(clientState.surfaceProps).not.toHaveProperty(
107
113
  "composerLayoutVariant",
108
114
  );
115
+ expect(clientState.surfaceProps).toHaveProperty(
116
+ "suppressInlineOpenApp",
117
+ true,
118
+ );
109
119
  expect(container.textContent).not.toContain("Chat across your apps");
110
120
  });
111
121
  });
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  AgentChatSurface,
3
3
  markAgentChatHomeHandoff,
4
+ readChatFirstMode,
4
5
  } from "@agent-native/core/client/agent-chat";
5
6
  import { appBasePath, appPath } from "@agent-native/core/client/api-path";
6
7
  import { useT } from "@agent-native/core/client/i18n";
@@ -13,6 +14,7 @@ import {
13
14
  } from "react";
14
15
  import { useLocation, useNavigate } from "react-router";
15
16
 
17
+ import { useDispatchExtensions } from "../../components/layout/Layout";
16
18
  import { submitOverviewPrompt } from "../../lib/overview-chat";
17
19
 
18
20
  function chatThreadPath(threadId: string | null): string {
@@ -93,6 +95,11 @@ export default function ChatRoute() {
93
95
  const navigate = useNavigate();
94
96
  const routeThreadId = threadIdFromPath(location.pathname);
95
97
  const handledStateIds = useRef(new Set<string>());
98
+ const extensions = useDispatchExtensions();
99
+ const suppressInlineOpenApp =
100
+ extensions?.chatFirst === true ||
101
+ readChatFirstMode() ||
102
+ new URLSearchParams(location.search).get("chatFirst") === "1";
96
103
 
97
104
  const navigateThreadUrl = useCallback(
98
105
  (path: string, options?: { replace?: boolean }) =>
@@ -181,6 +188,7 @@ export default function ChatRoute() {
181
188
  showHeader={false}
182
189
  showTabBar={false}
183
190
  dynamicSuggestions={false}
191
+ suppressInlineOpenApp={suppressInlineOpenApp}
184
192
  suggestions={[]}
185
193
  emptyStateText={t("dispatch.pages.chatAcrossAppsDescription", {
186
194
  defaultValue: