@agent-native/dispatch 0.31.24 → 0.31.25-nightly-20260826192357

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 (35) hide show
  1. package/dist/actions/create-browser-chat-session.js +1 -1
  2. package/dist/actions/create-browser-chat-session.js.map +1 -1
  3. package/dist/actions/get-agent-thread-debug.js +1 -1
  4. package/dist/actions/get-agent-thread-debug.js.map +1 -1
  5. package/dist/components/dispatch-shell.d.ts +3 -4
  6. package/dist/components/dispatch-shell.d.ts.map +1 -1
  7. package/dist/components/dispatch-shell.js +3 -4
  8. package/dist/components/dispatch-shell.js.map +1 -1
  9. package/dist/components/layout/Layout.d.ts.map +1 -1
  10. package/dist/components/layout/Layout.js +28 -10
  11. package/dist/components/layout/Layout.js.map +1 -1
  12. package/dist/server/lib/app-creation-store.js +1 -1
  13. package/dist/server/lib/app-creation-store.js.map +1 -1
  14. package/dist/server/lib/curated-workspace-templates.js +1 -1
  15. package/dist/server/lib/curated-workspace-templates.js.map +1 -1
  16. package/dist/server/lib/dispatch-integrations.js +2 -2
  17. package/dist/server/lib/dispatch-integrations.js.map +1 -1
  18. package/dist/server/lib/mcp-gateway.d.ts.map +1 -1
  19. package/dist/server/lib/mcp-gateway.js +12 -2
  20. package/dist/server/lib/mcp-gateway.js.map +1 -1
  21. package/dist/server/lib/onboarding-steps.js +1 -1
  22. package/dist/server/lib/onboarding-steps.js.map +1 -1
  23. package/package.json +4 -4
  24. package/src/actions/create-browser-chat-session.spec.ts +1 -1
  25. package/src/actions/create-browser-chat-session.ts +1 -1
  26. package/src/actions/get-agent-thread-debug.ts +1 -1
  27. package/src/components/dispatch-shell.tsx +3 -4
  28. package/src/components/layout/Layout.app-chat.spec.ts +16 -0
  29. package/src/components/layout/Layout.tsx +65 -13
  30. package/src/server/lib/app-creation-store.ts +1 -1
  31. package/src/server/lib/curated-workspace-templates.ts +1 -1
  32. package/src/server/lib/dispatch-integrations.spec.ts +1 -1
  33. package/src/server/lib/dispatch-integrations.ts +2 -2
  34. package/src/server/lib/mcp-gateway.ts +23 -9
  35. package/src/server/lib/onboarding-steps.ts +1 -1
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  CHAT_FIRST_MODE_CHANGED_EVENT,
3
3
  AgentChatSurface,
4
+ AgentToggleButton,
4
5
  chatFirstSurfaceTabId,
5
6
  AgentSidebar,
6
7
  ChatFirstSurfacePanelToggle,
@@ -61,6 +62,7 @@ import { useActionQuery } from "@agent-native/core/client/hooks";
61
62
  import { useT } from "@agent-native/core/client/i18n";
62
63
  import { openCommandMenu } from "@agent-native/core/client/navigation";
63
64
  import { InvitationBanner, OrgSwitcher } from "@agent-native/core/client/org";
65
+ import { RunsTray } from "@agent-native/core/client/progress";
64
66
  import { FeedbackButton } from "@agent-native/core/client/ui";
65
67
  import { SidebarFooterActions } from "@agent-native/toolkit/app-shell";
66
68
  import {
@@ -76,6 +78,7 @@ import {
76
78
  IconHierarchy2,
77
79
  IconMessageQuestion,
78
80
  IconBroadcast,
81
+ IconLayoutSidebar,
79
82
  IconLayoutSidebarLeftCollapse,
80
83
  IconLayoutSidebarLeftExpand,
81
84
  IconSettings,
@@ -116,6 +119,7 @@ import {
116
119
  import { CHAT_FIRST_PANE_STATE_KEY } from "../../shared/chat-first-pane";
117
120
  import { AppIcon } from "../app-icon";
118
121
  import { CreateAppPopover } from "../create-app-popover";
122
+ import { Button } from "../ui/button";
119
123
  import { Sheet, SheetContent, SheetDescription, SheetTitle } from "../ui/sheet";
120
124
  import { Skeleton } from "../ui/skeleton";
121
125
  import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
@@ -125,7 +129,11 @@ import {
125
129
  WorkspaceAppKeepAlive,
126
130
  } from "../workspace-app-host";
127
131
  import { Header } from "./Header";
128
- import { HeaderActionsProvider } from "./HeaderActions";
132
+ import {
133
+ HeaderActionsProvider,
134
+ useHeaderActions,
135
+ useHeaderTitle,
136
+ } from "./HeaderActions";
129
137
 
130
138
  export { buildChatFirstEmbedSessionInput } from "../workspace-app-host";
131
139
 
@@ -256,9 +264,8 @@ export function useDispatchExtensions(): DispatchExtensionConfig | undefined {
256
264
  return useContext(DispatchExtensionsContext);
257
265
  }
258
266
 
259
- // Routes whose page renders its own toolbar.
260
- // Layout still mounts the sidebar + AgentSidebar, but skips its own Header so
261
- // there's no double-header.
267
+ // Routes whose page renders its own toolbar. Layout skips its sticky chat
268
+ // control so there's no duplicate page chrome.
262
269
  function pageOwnsToolbar(pathname: string): boolean {
263
270
  if (pathname === "/tools" || pathname.startsWith("/tools/")) return true;
264
271
  if (pathname === "/extensions" || pathname.startsWith("/extensions/"))
@@ -1400,6 +1407,8 @@ export function Layout({
1400
1407
  const t = useT();
1401
1408
  const location = useLocation();
1402
1409
  const navigate = useNavigate();
1410
+ const pageTitle = useHeaderTitle();
1411
+ const headerActions = useHeaderActions();
1403
1412
  const [mobileOpen, setMobileOpen] = useState(false);
1404
1413
  // Drives renderChatFirstSurfaceTab's app-tab chatSidebar decision below —
1405
1414
  // the chat-first surface panel is already a full-screen overlay at this
@@ -2283,7 +2292,7 @@ export function Layout({
2283
2292
  );
2284
2293
  }
2285
2294
 
2286
- const showHeader = !isChatRoute && !pageOwnsToolbar(localPathname);
2295
+ const showAgentControls = !isChatRoute && !pageOwnsToolbar(localPathname);
2287
2296
  function openAskAgentFullscreen() {
2288
2297
  focusAgentChat();
2289
2298
  navigateWithAgentChatViewTransition(
@@ -2291,14 +2300,30 @@ export function Layout({
2291
2300
  dispatchNavLinkTarget("/chat"),
2292
2301
  );
2293
2302
  }
2303
+ function openRunThread(threadId: string) {
2304
+ navigate("/chat", {
2305
+ state: {
2306
+ dispatchThread: {
2307
+ id: `${Date.now()}-${threadId}`,
2308
+ threadId,
2309
+ },
2310
+ },
2311
+ });
2312
+ window.requestAnimationFrame(() => {
2313
+ window.dispatchEvent(
2314
+ new CustomEvent("agent-chat:open-thread", {
2315
+ detail: { threadId },
2316
+ }),
2317
+ );
2318
+ });
2319
+ }
2294
2320
  const sidebarSuggestions = [
2295
2321
  t("dispatch.sidebar.suggestionBuildApp"),
2296
2322
  t("dispatch.sidebar.suggestionRouteSlack"),
2297
2323
  t("dispatch.sidebar.suggestionGrantKey"),
2298
2324
  ];
2299
2325
  const appContent = (
2300
- <div className="relative flex h-full min-w-0 flex-1 flex-col overflow-hidden">
2301
- {showHeader ? <Header onOpenMobile={() => setMobileOpen(true)} /> : null}
2326
+ <div className="relative flex h-full min-h-0 min-w-0 flex-1 flex-col overflow-hidden">
2302
2327
  <InvitationBanner />
2303
2328
  {isChatRoute && chatFirstMode && chatFirstHasActiveChat ? (
2304
2329
  <ChatFirstSurfacePanelToggle
@@ -2325,16 +2350,43 @@ export function Layout({
2325
2350
  ) : null}
2326
2351
  <main
2327
2352
  className={cn(
2328
- "flex-1",
2353
+ "min-h-0 flex-1",
2329
2354
  isChatRoute || isWorkspaceAppRoute
2330
- ? "min-h-0 overflow-hidden"
2355
+ ? "overflow-hidden"
2331
2356
  : "overflow-y-auto",
2332
2357
  )}
2333
2358
  >
2334
- {showHeader ? (
2335
- <div className="mx-auto max-w-7xl space-y-10 px-4 py-6 sm:px-6">
2336
- {children}
2337
- </div>
2359
+ {showAgentControls ? (
2360
+ <>
2361
+ <div className="pointer-events-none sticky top-0 z-10 flex justify-end px-4 pt-2 lg:px-6">
2362
+ <Button
2363
+ variant="ghost"
2364
+ size="icon"
2365
+ className="pointer-events-auto absolute start-4 top-2 h-8 w-8 lg:hidden"
2366
+ onClick={() => setMobileOpen(true)}
2367
+ aria-label="Open navigation"
2368
+ >
2369
+ <IconLayoutSidebar className="h-4 w-4" />
2370
+ </Button>
2371
+ <div className="pointer-events-auto flex items-center gap-1">
2372
+ <RunsTray limit={8} onOpenThread={openRunThread} />
2373
+ <AgentToggleButton className="h-8 w-8 rounded-md bg-background/80 hover:bg-accent" />
2374
+ </div>
2375
+ </div>
2376
+ <div className="mx-auto max-w-7xl space-y-10 px-4 py-6 sm:px-6">
2377
+ {localPathname !== "/overview" && (pageTitle || headerActions) ? (
2378
+ <div className="flex min-w-0 items-center justify-between gap-3">
2379
+ <div className="min-w-0 flex-1">{pageTitle}</div>
2380
+ {headerActions ? (
2381
+ <div className="flex shrink-0 items-center gap-2">
2382
+ {headerActions}
2383
+ </div>
2384
+ ) : null}
2385
+ </div>
2386
+ ) : null}
2387
+ {children}
2388
+ </div>
2389
+ </>
2338
2390
  ) : (
2339
2391
  children
2340
2392
  )}
@@ -2439,7 +2439,7 @@ function buildWorkspaceAppPrompt(input: {
2439
2439
  ? `Dispatch will create workspace resource grants for the selected resources for appId "${appId}". After the app exists, sync workspace resources so the app receives both global and selected shared resources.`
2440
2440
  : "Do not grant any selected-only Dispatch workspace resources unless the user asks later.",
2441
2441
  "",
2442
- "Agent-native rules (these are the framework's contract — not optional):",
2442
+ "Agent-Native rules (these are the framework's contract — not optional):",
2443
2443
  `- Persist ALL data in SQL via Drizzle. Add tables to apps/${appId}/server/db/schema.ts and migrations to apps/${appId}/server/plugins/db.ts. NEVER use localStorage, sessionStorage, IndexedDB, or in-memory state for anything the user expects to persist — agent and UI must read the same source of truth.`,
2444
2444
  `- Define every create/read/update/delete as an action in apps/${appId}/actions/ using defineAction. The agent calls these as tools and the frontend calls them via useActionQuery / useActionMutation. If you must raw-fetch framework action endpoints, use agentNativePath("/_agent-native/actions/<name>") so mounted apps call the right URL. Don't add /api/* routes for CRUD.`,
2445
2445
  "- Build the UI from shadcn/ui components in app/components/ui/ (Button, Input, Dialog, Popover, Card, etc.) and Tailwind utilities. Don't author bespoke CSS classes in global.css unless you genuinely need a primitive that shadcn doesn't ship.",
@@ -26,7 +26,7 @@ export const CURATED_WORKSPACE_TEMPLATES: readonly CuratedWorkspaceTemplate[] =
26
26
  id: "mail",
27
27
  name: "Mail",
28
28
  description:
29
- "Agent-native email client with keyboard shortcuts and AI triage.",
29
+ "Agent-Native email client with keyboard shortcuts and AI triage.",
30
30
  icon: "Mail",
31
31
  color: "#3B82F6",
32
32
  template: "mail",
@@ -844,7 +844,7 @@ describe("managed Slack execution identity", () => {
844
844
  {
845
845
  handled: true,
846
846
  responseText:
847
- "Agent Native is ready, but this Slack account is not linked to an Agent Native user yet. Open https://dispatch.agent-native.test/identities, create a Slack link token, then send `/link <token>` in this DM.",
847
+ "Agent-Native is ready, but this Slack account is not linked to an Agent-Native user yet. Open https://dispatch.agent-native.test/identities, create a Slack link token, then send `/link <token>` in this DM.",
848
848
  },
849
849
  );
850
850
  });
@@ -184,7 +184,7 @@ function formatSlackLinkRequiredMessage(): string {
184
184
  const linkStep = identitiesUrl
185
185
  ? `Open ${identitiesUrl}, create a Slack link token, then send \`/link <token>\` in this DM.`
186
186
  : "Open Dispatch while signed in, create a Slack link token, then send `/link <token>` in this DM.";
187
- return `Agent Native is ready, but this Slack account is not linked to an Agent Native user yet. ${linkStep}`;
187
+ return `Agent-Native is ready, but this Slack account is not linked to an Agent-Native user yet. ${linkStep}`;
188
188
  }
189
189
 
190
190
  function formatSlackIdentityVerificationFailedMessage(): string {
@@ -574,7 +574,7 @@ export async function resolveDispatchExecutionContext(
574
574
  access,
575
575
  );
576
576
  }
577
- if (!scope) throw new Error("Slack channel is not enabled for Agent Native");
577
+ if (!scope) throw new Error("Slack channel is not enabled for Agent-Native");
578
578
  const decision = evaluateIntegrationScopePolicy(scope, {
579
579
  // Slack channel turns only arrive here after an explicit current-message
580
580
  // mention; keep the generic thread_reply trigger for other adapters.
@@ -45,6 +45,9 @@ const DISPATCH_DESCRIPTION =
45
45
  const DISPATCH_COLOR = "#14B8A6";
46
46
  const TARGET_EMBED_SESSION_ATTEMPTS = 3;
47
47
  const TARGET_EMBED_SESSION_RETRY_BASE_MS = 250;
48
+ // target apps can take a long time to cold-boot, so we give a large timeout here
49
+ const TARGET_EMBED_SESSION_CONNECT_TIMEOUT_MS = 90_000;
50
+ const TARGET_EMBED_SESSION_BUDGET_MS = 95_000;
48
51
  const DISPATCH_ASK_APP_DEFAULT_INLINE_WAIT_MS = 20_000;
49
52
  const DISPATCH_ASK_APP_MAX_INLINE_WAIT_MS = 25_000;
50
53
  const DISPATCH_ASK_APP_POLL_INTERVAL_MS = 1_500;
@@ -1027,6 +1030,12 @@ function targetMcpRequestDetails(input: {
1027
1030
  };
1028
1031
  }
1029
1032
 
1033
+ function targetMcpConnectTimeout(deadline: number): number {
1034
+ const remaining = deadline - Date.now();
1035
+ const budget = Math.min(TARGET_EMBED_SESSION_CONNECT_TIMEOUT_MS, remaining);
1036
+ return Math.max(1000, budget);
1037
+ }
1038
+
1030
1039
  function targetMcpRetryDelay(attempt: number): number {
1031
1040
  const base =
1032
1041
  TARGET_EMBED_SESSION_RETRY_BASE_MS * Math.pow(2, Math.max(0, attempt - 1));
@@ -1040,18 +1049,22 @@ async function callTargetCreateEmbedSession(input: {
1040
1049
  chrome?: "full" | "minimal";
1041
1050
  }): Promise<unknown> {
1042
1051
  const serverId = "target";
1052
+ const deadline = Date.now() + TARGET_EMBED_SESSION_BUDGET_MS;
1043
1053
  for (let attempt = 1; ; attempt += 1) {
1044
- const manager = new McpClientManager({
1045
- servers: {
1046
- [serverId]: {
1047
- type: "http",
1048
- url: `${appBaseUrl(input.app)}/mcp`,
1049
- headers: {
1050
- Authorization: `Bearer ${input.token}`,
1054
+ const manager = new McpClientManager(
1055
+ {
1056
+ servers: {
1057
+ [serverId]: {
1058
+ type: "http",
1059
+ url: `${appBaseUrl(input.app)}/mcp`,
1060
+ headers: {
1061
+ Authorization: `Bearer ${input.token}`,
1062
+ },
1051
1063
  },
1052
1064
  },
1053
1065
  },
1054
- });
1066
+ { connectTimeoutMs: targetMcpConnectTimeout(deadline) },
1067
+ );
1055
1068
  try {
1056
1069
  await manager.start();
1057
1070
  return await manager.callTool(
@@ -1064,7 +1077,8 @@ async function callTargetCreateEmbedSession(input: {
1064
1077
  } catch (error) {
1065
1078
  if (
1066
1079
  attempt >= TARGET_EMBED_SESSION_ATTEMPTS ||
1067
- !isRetryableTargetMcpError(error)
1080
+ !isRetryableTargetMcpError(error) ||
1081
+ deadline - Date.now() < TARGET_EMBED_SESSION_RETRY_BASE_MS
1068
1082
  ) {
1069
1083
  throw error;
1070
1084
  }
@@ -57,7 +57,7 @@ export function registerDispatchOnboardingSteps(): void {
57
57
  id: "dispatch:connect-slack-workspace",
58
58
  title: "Connect a Slack workspace",
59
59
  description:
60
- "Install Agent Native with OAuth, then configure channel identities and access policies.",
60
+ "Install Agent-Native with OAuth, then configure channel identities and access policies.",
61
61
  order: 60,
62
62
  required: false,
63
63
  methods: [