@agent-native/dispatch 0.30.3 → 0.31.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 (31) hide show
  1. package/dist/actions/provider-api-register.d.ts +5 -5
  2. package/dist/components/automation-details-panel.d.ts +8 -0
  3. package/dist/components/automation-details-panel.d.ts.map +1 -0
  4. package/dist/components/automation-details-panel.js +80 -0
  5. package/dist/components/automation-details-panel.js.map +1 -0
  6. package/dist/lib/automations.d.ts +12 -0
  7. package/dist/lib/automations.d.ts.map +1 -1
  8. package/dist/lib/automations.js +9 -0
  9. package/dist/lib/automations.js.map +1 -1
  10. package/dist/routes/pages/admin.d.ts.map +1 -1
  11. package/dist/routes/pages/admin.js +6 -1
  12. package/dist/routes/pages/admin.js.map +1 -1
  13. package/dist/routes/pages/automations.d.ts.map +1 -1
  14. package/dist/routes/pages/automations.js +59 -21
  15. package/dist/routes/pages/automations.js.map +1 -1
  16. package/dist/routes/pages/chat.d.ts.map +1 -1
  17. package/dist/routes/pages/chat.js +45 -18
  18. package/dist/routes/pages/chat.js.map +1 -1
  19. package/package.json +4 -4
  20. package/src/components/automation-details-panel.tsx +420 -0
  21. package/src/lib/automations.spec.ts +19 -1
  22. package/src/lib/automations.ts +23 -0
  23. package/src/routes/pages/admin.tsx +8 -1
  24. package/src/routes/pages/automations.tsx +177 -97
  25. package/src/routes/pages/chat.spec.tsx +49 -0
  26. package/src/routes/pages/chat.tsx +51 -0
  27. package/dist/components/automation-run-history-dialog.d.ts +0 -9
  28. package/dist/components/automation-run-history-dialog.d.ts.map +0 -1
  29. package/dist/components/automation-run-history-dialog.js +0 -63
  30. package/dist/components/automation-run-history-dialog.js.map +0 -1
  31. package/src/components/automation-run-history-dialog.tsx +0 -247
@@ -1,12 +1,15 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { AgentChatSurface, insertAgentComposerReference, markAgentChatHomeHandoff, readChatFirstMode, } from "@agent-native/core/client/agent-chat";
2
+ import { AgentChatSurface, insertAgentComposerReference, markAgentChatHomeHandoff, readChatFirstMode, useActiveAgentChatRunId, } from "@agent-native/core/client/agent-chat";
3
3
  import { appBasePath, appPath } from "@agent-native/core/client/api-path";
4
+ import { writeClipboardText } from "@agent-native/core/client/clipboard";
4
5
  import { useActionQuery } from "@agent-native/core/client/hooks";
5
6
  import { useT } from "@agent-native/core/client/i18n";
6
- import { useCallback, useEffect, useMemo, useRef, } from "react";
7
+ import { IconCheck, IconCopy } from "@tabler/icons-react";
8
+ import { useCallback, useEffect, useMemo, useRef, useState, } from "react";
7
9
  import { useLocation, useNavigate } from "react-router";
8
10
  import { ActionQueryError } from "../../components/action-query-error.js";
9
11
  import { useDispatchExtensions } from "../../components/layout/Layout.js";
12
+ import { Button } from "../../components/ui/button.js";
10
13
  import { Skeleton } from "../../components/ui/skeleton.js";
11
14
  import { submitOverviewPrompt } from "../../lib/overview-chat.js";
12
15
  function chatThreadPath(threadId, agentPath) {
@@ -54,6 +57,29 @@ function dispatchNavTarget(path) {
54
57
  function DispatchAgentChatSurface(props) {
55
58
  return _jsx(AgentChatSurface, { ...props });
56
59
  }
60
+ function DispatchRequestIdButton({ requestId }) {
61
+ const t = useT();
62
+ const [copied, setCopied] = useState(false);
63
+ const handleCopy = useCallback(() => {
64
+ if (!requestId)
65
+ return;
66
+ void writeClipboardText(requestId).then((ok) => {
67
+ if (!ok)
68
+ return;
69
+ setCopied(true);
70
+ window.setTimeout(() => setCopied(false), 1000);
71
+ });
72
+ }, [requestId]);
73
+ return (_jsxs(Button, { type: "button", variant: "outline", size: "sm", disabled: !requestId, onClick: handleCopy, "aria-label": t("dispatch.pages.copyRequestId", {
74
+ defaultValue: requestId ? "Copy request ID" : "Request ID unavailable",
75
+ }), children: [copied ? (_jsx(IconCheck, { "aria-hidden": "true" })) : (_jsx(IconCopy, { "aria-hidden": "true" })), copied
76
+ ? t("dispatch.pages.copied", { defaultValue: "Copied" })
77
+ : t("dispatch.pages.copyRequestId", {
78
+ defaultValue: requestId
79
+ ? "Copy request ID"
80
+ : "Request ID unavailable",
81
+ })] }));
82
+ }
57
83
  export function meta() {
58
84
  return [{ title: "Chat — Dispatch" }];
59
85
  }
@@ -62,6 +88,7 @@ export default function ChatRoute() {
62
88
  const location = useLocation();
63
89
  const navigate = useNavigate();
64
90
  const routeThreadId = threadIdFromPath(location.pathname);
91
+ const activeRunId = useActiveAgentChatRunId(routeThreadId);
65
92
  const agentPath = new URLSearchParams(location.search).get("agent");
66
93
  const agentsQuery = useActionQuery("list-workspace-resources", { kind: "agent" }, { enabled: Boolean(agentPath), staleTime: 30_000 });
67
94
  const agent = useMemo(() => agentPath
@@ -172,21 +199,21 @@ export default function ChatRoute() {
172
199
  if (agentPath && !agent) {
173
200
  return (_jsx("div", { className: "flex h-full min-h-0 items-center justify-center bg-background px-4 text-sm text-muted-foreground", children: "This agent is no longer available." }));
174
201
  }
175
- return (_jsx("div", { className: "flex h-full min-h-0 flex-col bg-background", children: _jsx(DispatchAgentChatSurface, { mode: "page", chatViewTransition: true, className: "dispatch-chat-panel px-4 sm:px-6", defaultMode: "chat", storageKey: agent ? `dispatch-agent-${agent.id}` : "dispatch", scope: agentScope, threadUrlSync: threadUrlSync, showHeader: false, showTabBar: false, dynamicSuggestions: false, suppressInlineOpenApp: suppressInlineOpenApp, suggestions: [], emptyStateText: t("dispatch.pages.chatAcrossAppsDescription", {
176
- defaultValue: "Route work, inspect status, or create something new from one place.",
177
- }), emptyStateDisplay: "hidden", ...(!prompt?.message
178
- ? {
179
- centerComposerWhenEmpty: true,
180
- composerLayoutVariant: "hero",
181
- }
182
- : {}), composerPlaceholder: agent
183
- ? `Ask ${agent.name}...`
184
- : t("dispatch.pages.chatPromptPlaceholder", {
185
- defaultValue: "Ask Dispatch...",
186
- }), composerSlot: agent ? (_jsxs("div", { className: "dispatch-chat-intro", children: [_jsx("h1", { children: agent.name }), agent.description ? _jsx("p", { children: agent.description }) : null] })) : !prompt?.message ? (_jsxs("div", { className: "dispatch-chat-intro", children: [_jsx("h1", { children: t("dispatch.pages.chatAcrossApps", {
187
- defaultValue: "Chat across your apps",
188
- }) }), _jsx("p", { children: t("dispatch.pages.chatAcrossAppsDescription", {
189
- defaultValue: "Route work, inspect status, or create something new from one place.",
190
- }) })] })) : null }, agent ? `agent-${agent.id}` : "dispatch") }));
202
+ return (_jsxs("div", { className: "flex h-full min-h-0 flex-col bg-background", children: [threadUrlSync.routeThreadId ? (_jsx("div", { className: "flex justify-end px-4 pt-4 sm:px-6", children: _jsx(DispatchRequestIdButton, { requestId: activeRunId }) })) : null, _jsx(DispatchAgentChatSurface, { mode: "page", chatViewTransition: true, className: "dispatch-chat-panel px-4 sm:px-6", defaultMode: "chat", storageKey: agent ? `dispatch-agent-${agent.id}` : "dispatch", scope: agentScope, threadUrlSync: threadUrlSync, showHeader: false, showTabBar: false, dynamicSuggestions: false, suppressInlineOpenApp: suppressInlineOpenApp, suggestions: [], emptyStateText: t("dispatch.pages.chatAcrossAppsDescription", {
203
+ defaultValue: "Route work, inspect status, or create something new from one place.",
204
+ }), emptyStateDisplay: "hidden", ...(!prompt?.message
205
+ ? {
206
+ centerComposerWhenEmpty: true,
207
+ composerLayoutVariant: "hero",
208
+ }
209
+ : {}), composerPlaceholder: agent
210
+ ? `Ask ${agent.name}...`
211
+ : t("dispatch.pages.chatPromptPlaceholder", {
212
+ defaultValue: "Ask Dispatch...",
213
+ }), composerSlot: agent ? (_jsxs("div", { className: "dispatch-chat-intro", children: [_jsx("h1", { children: agent.name }), agent.description ? _jsx("p", { children: agent.description }) : null] })) : !prompt?.message ? (_jsxs("div", { className: "dispatch-chat-intro", children: [_jsx("h1", { children: t("dispatch.pages.chatAcrossApps", {
214
+ defaultValue: "Chat across your apps",
215
+ }) }), _jsx("p", { children: t("dispatch.pages.chatAcrossAppsDescription", {
216
+ defaultValue: "Route work, inspect status, or create something new from one place.",
217
+ }) })] })) : null }, agent ? `agent-${agent.id}` : "dispatch")] }));
191
218
  }
192
219
  //# sourceMappingURL=chat.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"chat.js","sourceRoot":"","sources":["../../../src/routes/pages/chat.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n AgentChatSurface,\n insertAgentComposerReference,\n markAgentChatHomeHandoff,\n readChatFirstMode,\n} from \"@agent-native/core/client/agent-chat\";\nimport { appBasePath, appPath } from \"@agent-native/core/client/api-path\";\nimport { useActionQuery } from \"@agent-native/core/client/hooks\";\nimport { useT } from \"@agent-native/core/client/i18n\";\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n type ComponentProps,\n} from \"react\";\nimport { useLocation, useNavigate } from \"react-router\";\n\nimport { ActionQueryError } from \"../../components/action-query-error\";\nimport { useDispatchExtensions } from \"../../components/layout/Layout\";\nimport { Skeleton } from \"../../components/ui/skeleton\";\nimport { submitOverviewPrompt } from \"../../lib/overview-chat\";\n\ninterface WorkspaceAgentResource {\n id: string;\n name: string;\n description: string | null;\n path: string;\n content: string;\n scope: \"all\" | \"selected\";\n updatedAt: number;\n}\n\nfunction chatThreadPath(threadId: string | null, agentPath?: string): string {\n const path = threadId ? `/chat/${encodeURIComponent(threadId)}` : \"/chat\";\n return agentPath ? `${path}?agent=${encodeURIComponent(agentPath)}` : path;\n}\n\nfunction stripBasePath(pathname: string): string {\n const basePath = appBasePath();\n if (!basePath) return pathname;\n if (pathname === basePath) return \"/\";\n if (pathname.startsWith(`${basePath}/`)) {\n return pathname.slice(basePath.length) || \"/\";\n }\n return pathname;\n}\n\n// The chat surface renders for both `/chat` and the `/chat/:threadId` deep\n// link. The thread id is read from the pathname (not `useParams`) because the\n// param is owned by the nested deep-link route, not this leaf component.\nfunction threadIdFromPath(pathname: string): string | null {\n const match = stripBasePath(pathname).match(/^\\/chat\\/([^/]+)/);\n if (!match) return null;\n try {\n const value = decodeURIComponent(match[1]).trim();\n return value || null;\n } catch {\n return null;\n }\n}\n\n// Mirror the basename handling Dispatch's nav links use: pass a router-local\n// path when the live URL is already under the mount, otherwise prefix it.\nfunction dispatchNavTarget(path: string): string {\n if (typeof window === \"undefined\") return path;\n const basePath = appBasePath();\n if (!basePath) return path;\n const pathname = window.location.pathname;\n const routerHasBasename =\n pathname === basePath || pathname.startsWith(`${basePath}/`);\n return routerHasBasename ? path : appPath(path);\n}\n\ninterface DispatchThreadUrlSync {\n routeThreadId: string | null;\n getPath: (threadId: string | null) => string;\n navigate: (path: string, options?: { replace?: boolean }) => void;\n}\n\ntype DispatchAgentChatSurfaceProps = ComponentProps<typeof AgentChatSurface> & {\n threadUrlSync?: DispatchThreadUrlSync;\n};\n\nfunction DispatchAgentChatSurface(props: DispatchAgentChatSurfaceProps) {\n return <AgentChatSurface {...props} />;\n}\n\ninterface DispatchChatLocationState {\n dispatchPrompt?: {\n id?: string | number;\n message?: string;\n selectedModel?: string | null;\n selectedEngine?: string | null;\n selectedEffort?: ComponentProps<typeof AgentChatSurface>[\"selectedEffort\"];\n };\n dispatchThread?: {\n id?: string | number;\n threadId?: string;\n };\n}\n\nexport function meta() {\n return [{ title: \"Chat — Dispatch\" }];\n}\n\nexport default function ChatRoute() {\n const t = useT();\n const location = useLocation();\n const navigate = useNavigate();\n const routeThreadId = threadIdFromPath(location.pathname);\n const agentPath = new URLSearchParams(location.search).get(\"agent\");\n const agentsQuery = useActionQuery<WorkspaceAgentResource[]>(\n \"list-workspace-resources\",\n { kind: \"agent\" },\n { enabled: Boolean(agentPath), staleTime: 30_000 },\n );\n const agent = useMemo(\n () =>\n agentPath\n ? (agentsQuery.data ?? []).find((resource) => {\n const profileId = resource.path\n .replace(/^agents\\//, \"\")\n .replace(/\\.md$/, \"\");\n return (\n resource.path === agentPath ||\n resource.id === agentPath ||\n profileId === agentPath\n );\n })\n : undefined,\n [agentPath, agentsQuery.data],\n );\n const agentScope = useMemo(\n () =>\n agent\n ? {\n type: \"agent\",\n id: agent.id,\n label: agent.name,\n contextKey: `dispatch-agent:${agent.id}`,\n }\n : null,\n [agent],\n );\n const handledStateIds = useRef(new Set<string>());\n const extensions = useDispatchExtensions();\n const suppressInlineOpenApp =\n extensions?.chatFirst === true ||\n readChatFirstMode() ||\n new URLSearchParams(location.search).get(\"chatFirst\") === \"1\";\n\n const navigateThreadUrl = useCallback(\n (path: string, options?: { replace?: boolean }) =>\n navigate(dispatchNavTarget(path), options),\n [navigate],\n );\n const threadUrlSync = useMemo<DispatchThreadUrlSync>(\n () => ({\n routeThreadId: routeThreadId ?? null,\n getPath: (threadId) => chatThreadPath(threadId, agentPath ?? undefined),\n navigate: navigateThreadUrl,\n }),\n [agentPath, routeThreadId, navigateThreadUrl],\n );\n const state = location.state as DispatchChatLocationState | null;\n const prompt = state?.dispatchPrompt;\n const thread = state?.dispatchThread;\n\n useEffect(() => {\n const message = prompt?.message?.trim();\n const threadId = thread?.threadId?.trim();\n if (!message && !threadId) return;\n\n const stateId = String(\n prompt?.id ?? thread?.id ?? `${message ?? \"\"}:${threadId ?? \"\"}`,\n );\n if (handledStateIds.current.has(stateId)) return;\n handledStateIds.current.add(stateId);\n\n const timer = window.setTimeout(() => {\n if (threadId) {\n window.dispatchEvent(\n new CustomEvent(\"agent-chat:open-thread\", {\n detail: { threadId },\n }),\n );\n }\n if (message) {\n submitOverviewPrompt(message, prompt?.selectedModel, {\n openSidebar: false,\n selectedEngine: prompt?.selectedEngine,\n selectedEffort: prompt?.selectedEffort,\n });\n }\n navigate(`${location.pathname}${location.search}${location.hash}`, {\n replace: true,\n state: null,\n });\n }, 0);\n\n return () => window.clearTimeout(timer);\n }, [\n location.hash,\n location.pathname,\n location.search,\n navigate,\n prompt?.id,\n prompt?.message,\n prompt?.selectedModel,\n prompt?.selectedEngine,\n prompt?.selectedEffort,\n thread?.id,\n thread?.threadId,\n ]);\n\n useEffect(() => {\n function handleChatRunning(event: Event) {\n const detail = (event as CustomEvent).detail;\n if (detail?.isRunning === true) markAgentChatHomeHandoff(\"dispatch\");\n }\n\n window.addEventListener(\"agentNative.chatRunning\", handleChatRunning);\n return () =>\n window.removeEventListener(\"agentNative.chatRunning\", handleChatRunning);\n }, []);\n\n useEffect(() => {\n if (!agent) return;\n const timer = window.setTimeout(() => {\n insertAgentComposerReference(\n {\n label: agent.name,\n icon: \"agent\",\n source: \"agent:custom\",\n refType: \"custom-agent\",\n refId: agent.id,\n refPath: agent.path,\n slotKey: \"dispatch-agent\",\n slotLabel: \"Agent\",\n },\n { openSidebar: false },\n );\n }, 0);\n return () => window.clearTimeout(timer);\n }, [agent?.id, agent?.name, agent?.path]);\n\n if (agentPath && agentsQuery.isLoading) {\n return (\n <div className=\"flex h-full min-h-0 flex-col gap-4 bg-background px-4 py-6 sm:px-6\">\n <Skeleton className=\"mx-auto h-6 w-48\" />\n <Skeleton className=\"mx-auto h-12 w-full max-w-2xl\" />\n </div>\n );\n }\n\n if (agentPath && agentsQuery.isError) {\n return (\n <div className=\"flex h-full min-h-0 items-center justify-center bg-background px-4\">\n <ActionQueryError\n error={agentsQuery.error}\n onRetry={() => void agentsQuery.refetch()}\n />\n </div>\n );\n }\n\n if (agentPath && !agent) {\n return (\n <div className=\"flex h-full min-h-0 items-center justify-center bg-background px-4 text-sm text-muted-foreground\">\n This agent is no longer available.\n </div>\n );\n }\n\n return (\n <div className=\"flex h-full min-h-0 flex-col bg-background\">\n <DispatchAgentChatSurface\n key={agent ? `agent-${agent.id}` : \"dispatch\"}\n mode=\"page\"\n chatViewTransition\n className=\"dispatch-chat-panel px-4 sm:px-6\"\n defaultMode=\"chat\"\n storageKey={agent ? `dispatch-agent-${agent.id}` : \"dispatch\"}\n scope={agentScope}\n threadUrlSync={threadUrlSync}\n showHeader={false}\n showTabBar={false}\n dynamicSuggestions={false}\n suppressInlineOpenApp={suppressInlineOpenApp}\n suggestions={[]}\n emptyStateText={t(\"dispatch.pages.chatAcrossAppsDescription\", {\n defaultValue:\n \"Route work, inspect status, or create something new from one place.\",\n })}\n emptyStateDisplay=\"hidden\"\n {...(!prompt?.message\n ? {\n centerComposerWhenEmpty: true,\n composerLayoutVariant: \"hero\" as const,\n }\n : {})}\n composerPlaceholder={\n agent\n ? `Ask ${agent.name}...`\n : t(\"dispatch.pages.chatPromptPlaceholder\", {\n defaultValue: \"Ask Dispatch...\",\n })\n }\n composerSlot={\n agent ? (\n <div className=\"dispatch-chat-intro\">\n <h1>{agent.name}</h1>\n {agent.description ? <p>{agent.description}</p> : null}\n </div>\n ) : !prompt?.message ? (\n <div className=\"dispatch-chat-intro\">\n <h1>\n {t(\"dispatch.pages.chatAcrossApps\", {\n defaultValue: \"Chat across your apps\",\n })}\n </h1>\n <p>\n {t(\"dispatch.pages.chatAcrossAppsDescription\", {\n defaultValue:\n \"Route work, inspect status, or create something new from one place.\",\n })}\n </p>\n </div>\n ) : null\n }\n />\n </div>\n );\n}\n"]}
1
+ {"version":3,"file":"chat.js","sourceRoot":"","sources":["../../../src/routes/pages/chat.tsx"],"names":[],"mappings":";AAAA,OAAO,EACL,gBAAgB,EAChB,4BAA4B,EAC5B,wBAAwB,EACxB,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,sCAAsC,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,oCAAoC,CAAC;AAC1E,OAAO,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AACzE,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,IAAI,EAAE,MAAM,gCAAgC,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EACL,WAAW,EACX,SAAS,EACT,OAAO,EACP,MAAM,EACN,QAAQ,GAET,MAAM,OAAO,CAAC;AACf,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAExD,OAAO,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAC;AACvE,OAAO,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AACvE,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AAY/D,SAAS,cAAc,CAAC,QAAuB,EAAE,SAAkB;IACjE,MAAM,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,SAAS,kBAAkB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC;IAC1E,OAAO,SAAS,CAAC,CAAC,CAAC,GAAG,IAAI,UAAU,kBAAkB,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7E,CAAC;AAED,SAAS,aAAa,CAAC,QAAgB;IACrC,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,IAAI,CAAC,QAAQ;QAAE,OAAO,QAAQ,CAAC;IAC/B,IAAI,QAAQ,KAAK,QAAQ;QAAE,OAAO,GAAG,CAAC;IACtC,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,QAAQ,GAAG,CAAC,EAAE,CAAC;QACxC,OAAO,QAAQ,CAAC,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,GAAG,CAAC;IAChD,CAAC;IACD,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,2EAA2E;AAC3E,8EAA8E;AAC9E,yEAAyE;AACzE,SAAS,gBAAgB,CAAC,QAAgB;IACxC,MAAM,KAAK,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IAChE,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAClD,OAAO,KAAK,IAAI,IAAI,CAAC;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,6EAA6E;AAC7E,0EAA0E;AAC1E,SAAS,iBAAiB,CAAC,IAAY;IACrC,IAAI,OAAO,MAAM,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IAC/C,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3B,MAAM,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;IAC1C,MAAM,iBAAiB,GACrB,QAAQ,KAAK,QAAQ,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,QAAQ,GAAG,CAAC,CAAC;IAC/D,OAAO,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAClD,CAAC;AAYD,SAAS,wBAAwB,CAAC,KAAoC;IACpE,OAAO,KAAC,gBAAgB,OAAK,KAAK,GAAI,CAAC;AACzC,CAAC;AAED,SAAS,uBAAuB,CAAC,EAAE,SAAS,EAAgC;IAC1E,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;IACjB,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAE5C,MAAM,UAAU,GAAG,WAAW,CAAC,GAAG,EAAE;QAClC,IAAI,CAAC,SAAS;YAAE,OAAO;QACvB,KAAK,kBAAkB,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE;YAC7C,IAAI,CAAC,EAAE;gBAAE,OAAO;YAChB,SAAS,CAAC,IAAI,CAAC,CAAC;YAChB,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;QAClD,CAAC,CAAC,CAAC;IACL,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IAEhB,OAAO,CACL,MAAC,MAAM,IACL,IAAI,EAAC,QAAQ,EACb,OAAO,EAAC,SAAS,EACjB,IAAI,EAAC,IAAI,EACT,QAAQ,EAAE,CAAC,SAAS,EACpB,OAAO,EAAE,UAAU,gBACP,CAAC,CAAC,8BAA8B,EAAE;YAC5C,YAAY,EAAE,SAAS,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,wBAAwB;SACvE,CAAC,aAED,MAAM,CAAC,CAAC,CAAC,CACR,KAAC,SAAS,mBAAa,MAAM,GAAG,CACjC,CAAC,CAAC,CAAC,CACF,KAAC,QAAQ,mBAAa,MAAM,GAAG,CAChC,EACA,MAAM;gBACL,CAAC,CAAC,CAAC,CAAC,uBAAuB,EAAE,EAAE,YAAY,EAAE,QAAQ,EAAE,CAAC;gBACxD,CAAC,CAAC,CAAC,CAAC,8BAA8B,EAAE;oBAChC,YAAY,EAAE,SAAS;wBACrB,CAAC,CAAC,iBAAiB;wBACnB,CAAC,CAAC,wBAAwB;iBAC7B,CAAC,IACC,CACV,CAAC;AACJ,CAAC;AAgBD,MAAM,UAAU,IAAI;IAClB,OAAO,CAAC,EAAE,KAAK,EAAE,iBAAiB,EAAE,CAAC,CAAC;AACxC,CAAC;AAED,MAAM,CAAC,OAAO,UAAU,SAAS;IAC/B,MAAM,CAAC,GAAG,IAAI,EAAE,CAAC;IACjB,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,aAAa,GAAG,gBAAgB,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IAC1D,MAAM,WAAW,GAAG,uBAAuB,CAAC,aAAa,CAAC,CAAC;IAC3D,MAAM,SAAS,GAAG,IAAI,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACpE,MAAM,WAAW,GAAG,cAAc,CAChC,0BAA0B,EAC1B,EAAE,IAAI,EAAE,OAAO,EAAE,EACjB,EAAE,OAAO,EAAE,OAAO,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,MAAM,EAAE,CACnD,CAAC;IACF,MAAM,KAAK,GAAG,OAAO,CACnB,GAAG,EAAE,CACH,SAAS;QACP,CAAC,CAAC,CAAC,WAAW,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,EAAE;YACzC,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI;iBAC5B,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC;iBACxB,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;YACxB,OAAO,CACL,QAAQ,CAAC,IAAI,KAAK,SAAS;gBAC3B,QAAQ,CAAC,EAAE,KAAK,SAAS;gBACzB,SAAS,KAAK,SAAS,CACxB,CAAC;QACJ,CAAC,CAAC;QACJ,CAAC,CAAC,SAAS,EACf,CAAC,SAAS,EAAE,WAAW,CAAC,IAAI,CAAC,CAC9B,CAAC;IACF,MAAM,UAAU,GAAG,OAAO,CACxB,GAAG,EAAE,CACH,KAAK;QACH,CAAC,CAAC;YACE,IAAI,EAAE,OAAO;YACb,EAAE,EAAE,KAAK,CAAC,EAAE;YACZ,KAAK,EAAE,KAAK,CAAC,IAAI;YACjB,UAAU,EAAE,kBAAkB,KAAK,CAAC,EAAE,EAAE;SACzC;QACH,CAAC,CAAC,IAAI,EACV,CAAC,KAAK,CAAC,CACR,CAAC;IACF,MAAM,eAAe,GAAG,MAAM,CAAC,IAAI,GAAG,EAAU,CAAC,CAAC;IAClD,MAAM,UAAU,GAAG,qBAAqB,EAAE,CAAC;IAC3C,MAAM,qBAAqB,GACzB,UAAU,EAAE,SAAS,KAAK,IAAI;QAC9B,iBAAiB,EAAE;QACnB,IAAI,eAAe,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,GAAG,CAAC;IAEhE,MAAM,iBAAiB,GAAG,WAAW,CACnC,CAAC,IAAY,EAAE,OAA+B,EAAE,EAAE,CAChD,QAAQ,CAAC,iBAAiB,CAAC,IAAI,CAAC,EAAE,OAAO,CAAC,EAC5C,CAAC,QAAQ,CAAC,CACX,CAAC;IACF,MAAM,aAAa,GAAG,OAAO,CAC3B,GAAG,EAAE,CAAC,CAAC;QACL,aAAa,EAAE,aAAa,IAAI,IAAI;QACpC,OAAO,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,cAAc,CAAC,QAAQ,EAAE,SAAS,IAAI,SAAS,CAAC;QACvE,QAAQ,EAAE,iBAAiB;KAC5B,CAAC,EACF,CAAC,SAAS,EAAE,aAAa,EAAE,iBAAiB,CAAC,CAC9C,CAAC;IACF,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAyC,CAAC;IACjE,MAAM,MAAM,GAAG,KAAK,EAAE,cAAc,CAAC;IACrC,MAAM,MAAM,GAAG,KAAK,EAAE,cAAc,CAAC;IAErC,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,OAAO,GAAG,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QACxC,MAAM,QAAQ,GAAG,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;QAC1C,IAAI,CAAC,OAAO,IAAI,CAAC,QAAQ;YAAE,OAAO;QAElC,MAAM,OAAO,GAAG,MAAM,CACpB,MAAM,EAAE,EAAE,IAAI,MAAM,EAAE,EAAE,IAAI,GAAG,OAAO,IAAI,EAAE,IAAI,QAAQ,IAAI,EAAE,EAAE,CACjE,CAAC;QACF,IAAI,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,OAAO;QACjD,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAErC,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE;YACnC,IAAI,QAAQ,EAAE,CAAC;gBACb,MAAM,CAAC,aAAa,CAClB,IAAI,WAAW,CAAC,wBAAwB,EAAE;oBACxC,MAAM,EAAE,EAAE,QAAQ,EAAE;iBACrB,CAAC,CACH,CAAC;YACJ,CAAC;YACD,IAAI,OAAO,EAAE,CAAC;gBACZ,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE;oBACnD,WAAW,EAAE,KAAK;oBAClB,cAAc,EAAE,MAAM,EAAE,cAAc;oBACtC,cAAc,EAAE,MAAM,EAAE,cAAc;iBACvC,CAAC,CAAC;YACL,CAAC;YACD,QAAQ,CAAC,GAAG,QAAQ,CAAC,QAAQ,GAAG,QAAQ,CAAC,MAAM,GAAG,QAAQ,CAAC,IAAI,EAAE,EAAE;gBACjE,OAAO,EAAE,IAAI;gBACb,KAAK,EAAE,IAAI;aACZ,CAAC,CAAC;QACL,CAAC,EAAE,CAAC,CAAC,CAAC;QAEN,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC,EAAE;QACD,QAAQ,CAAC,IAAI;QACb,QAAQ,CAAC,QAAQ;QACjB,QAAQ,CAAC,MAAM;QACf,QAAQ;QACR,MAAM,EAAE,EAAE;QACV,MAAM,EAAE,OAAO;QACf,MAAM,EAAE,aAAa;QACrB,MAAM,EAAE,cAAc;QACtB,MAAM,EAAE,cAAc;QACtB,MAAM,EAAE,EAAE;QACV,MAAM,EAAE,QAAQ;KACjB,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACb,SAAS,iBAAiB,CAAC,KAAY;YACrC,MAAM,MAAM,GAAI,KAAqB,CAAC,MAAM,CAAC;YAC7C,IAAI,MAAM,EAAE,SAAS,KAAK,IAAI;gBAAE,wBAAwB,CAAC,UAAU,CAAC,CAAC;QACvE,CAAC;QAED,MAAM,CAAC,gBAAgB,CAAC,yBAAyB,EAAE,iBAAiB,CAAC,CAAC;QACtE,OAAO,GAAG,EAAE,CACV,MAAM,CAAC,mBAAmB,CAAC,yBAAyB,EAAE,iBAAiB,CAAC,CAAC;IAC7E,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,CAAC,KAAK;YAAE,OAAO;QACnB,MAAM,KAAK,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE;YACnC,4BAA4B,CAC1B;gBACE,KAAK,EAAE,KAAK,CAAC,IAAI;gBACjB,IAAI,EAAE,OAAO;gBACb,MAAM,EAAE,cAAc;gBACtB,OAAO,EAAE,cAAc;gBACvB,KAAK,EAAE,KAAK,CAAC,EAAE;gBACf,OAAO,EAAE,KAAK,CAAC,IAAI;gBACnB,OAAO,EAAE,gBAAgB;gBACzB,SAAS,EAAE,OAAO;aACnB,EACD,EAAE,WAAW,EAAE,KAAK,EAAE,CACvB,CAAC;QACJ,CAAC,EAAE,CAAC,CAAC,CAAC;QACN,OAAO,GAAG,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IAC1C,CAAC,EAAE,CAAC,KAAK,EAAE,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;IAE1C,IAAI,SAAS,IAAI,WAAW,CAAC,SAAS,EAAE,CAAC;QACvC,OAAO,CACL,eAAK,SAAS,EAAC,oEAAoE,aACjF,KAAC,QAAQ,IAAC,SAAS,EAAC,kBAAkB,GAAG,EACzC,KAAC,QAAQ,IAAC,SAAS,EAAC,+BAA+B,GAAG,IAClD,CACP,CAAC;IACJ,CAAC;IAED,IAAI,SAAS,IAAI,WAAW,CAAC,OAAO,EAAE,CAAC;QACrC,OAAO,CACL,cAAK,SAAS,EAAC,oEAAoE,YACjF,KAAC,gBAAgB,IACf,KAAK,EAAE,WAAW,CAAC,KAAK,EACxB,OAAO,EAAE,GAAG,EAAE,CAAC,KAAK,WAAW,CAAC,OAAO,EAAE,GACzC,GACE,CACP,CAAC;IACJ,CAAC;IAED,IAAI,SAAS,IAAI,CAAC,KAAK,EAAE,CAAC;QACxB,OAAO,CACL,cAAK,SAAS,EAAC,kGAAkG,mDAE3G,CACP,CAAC;IACJ,CAAC;IAED,OAAO,CACL,eAAK,SAAS,EAAC,4CAA4C,aACxD,aAAa,CAAC,aAAa,CAAC,CAAC,CAAC,CAC7B,cAAK,SAAS,EAAC,oCAAoC,YACjD,KAAC,uBAAuB,IAAC,SAAS,EAAE,WAAW,GAAI,GAC/C,CACP,CAAC,CAAC,CAAC,IAAI,EACR,KAAC,wBAAwB,IAEvB,IAAI,EAAC,MAAM,EACX,kBAAkB,QAClB,SAAS,EAAC,kCAAkC,EAC5C,WAAW,EAAC,MAAM,EAClB,UAAU,EAAE,KAAK,CAAC,CAAC,CAAC,kBAAkB,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,EAC7D,KAAK,EAAE,UAAU,EACjB,aAAa,EAAE,aAAa,EAC5B,UAAU,EAAE,KAAK,EACjB,UAAU,EAAE,KAAK,EACjB,kBAAkB,EAAE,KAAK,EACzB,qBAAqB,EAAE,qBAAqB,EAC5C,WAAW,EAAE,EAAE,EACf,cAAc,EAAE,CAAC,CAAC,0CAA0C,EAAE;oBAC5D,YAAY,EACV,qEAAqE;iBACxE,CAAC,EACF,iBAAiB,EAAC,QAAQ,KACtB,CAAC,CAAC,MAAM,EAAE,OAAO;oBACnB,CAAC,CAAC;wBACE,uBAAuB,EAAE,IAAI;wBAC7B,qBAAqB,EAAE,MAAe;qBACvC;oBACH,CAAC,CAAC,EAAE,CAAC,EACP,mBAAmB,EACjB,KAAK;oBACH,CAAC,CAAC,OAAO,KAAK,CAAC,IAAI,KAAK;oBACxB,CAAC,CAAC,CAAC,CAAC,sCAAsC,EAAE;wBACxC,YAAY,EAAE,iBAAiB;qBAChC,CAAC,EAER,YAAY,EACV,KAAK,CAAC,CAAC,CAAC,CACN,eAAK,SAAS,EAAC,qBAAqB,aAClC,uBAAK,KAAK,CAAC,IAAI,GAAM,EACpB,KAAK,CAAC,WAAW,CAAC,CAAC,CAAC,sBAAI,KAAK,CAAC,WAAW,GAAK,CAAC,CAAC,CAAC,IAAI,IAClD,CACP,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,CACrB,eAAK,SAAS,EAAC,qBAAqB,aAClC,uBACG,CAAC,CAAC,+BAA+B,EAAE;gCAClC,YAAY,EAAE,uBAAuB;6BACtC,CAAC,GACC,EACL,sBACG,CAAC,CAAC,0CAA0C,EAAE;gCAC7C,YAAY,EACV,qEAAqE;6BACxE,CAAC,GACA,IACA,CACP,CAAC,CAAC,CAAC,IAAI,IAnDL,KAAK,CAAC,CAAC,CAAC,SAAS,KAAK,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAqD7C,IACE,CACP,CAAC;AACJ,CAAC","sourcesContent":["import {\n AgentChatSurface,\n insertAgentComposerReference,\n markAgentChatHomeHandoff,\n readChatFirstMode,\n useActiveAgentChatRunId,\n} from \"@agent-native/core/client/agent-chat\";\nimport { appBasePath, appPath } from \"@agent-native/core/client/api-path\";\nimport { writeClipboardText } from \"@agent-native/core/client/clipboard\";\nimport { useActionQuery } from \"@agent-native/core/client/hooks\";\nimport { useT } from \"@agent-native/core/client/i18n\";\nimport { IconCheck, IconCopy } from \"@tabler/icons-react\";\nimport {\n useCallback,\n useEffect,\n useMemo,\n useRef,\n useState,\n type ComponentProps,\n} from \"react\";\nimport { useLocation, useNavigate } from \"react-router\";\n\nimport { ActionQueryError } from \"../../components/action-query-error\";\nimport { useDispatchExtensions } from \"../../components/layout/Layout\";\nimport { Button } from \"../../components/ui/button\";\nimport { Skeleton } from \"../../components/ui/skeleton\";\nimport { submitOverviewPrompt } from \"../../lib/overview-chat\";\n\ninterface WorkspaceAgentResource {\n id: string;\n name: string;\n description: string | null;\n path: string;\n content: string;\n scope: \"all\" | \"selected\";\n updatedAt: number;\n}\n\nfunction chatThreadPath(threadId: string | null, agentPath?: string): string {\n const path = threadId ? `/chat/${encodeURIComponent(threadId)}` : \"/chat\";\n return agentPath ? `${path}?agent=${encodeURIComponent(agentPath)}` : path;\n}\n\nfunction stripBasePath(pathname: string): string {\n const basePath = appBasePath();\n if (!basePath) return pathname;\n if (pathname === basePath) return \"/\";\n if (pathname.startsWith(`${basePath}/`)) {\n return pathname.slice(basePath.length) || \"/\";\n }\n return pathname;\n}\n\n// The chat surface renders for both `/chat` and the `/chat/:threadId` deep\n// link. The thread id is read from the pathname (not `useParams`) because the\n// param is owned by the nested deep-link route, not this leaf component.\nfunction threadIdFromPath(pathname: string): string | null {\n const match = stripBasePath(pathname).match(/^\\/chat\\/([^/]+)/);\n if (!match) return null;\n try {\n const value = decodeURIComponent(match[1]).trim();\n return value || null;\n } catch {\n return null;\n }\n}\n\n// Mirror the basename handling Dispatch's nav links use: pass a router-local\n// path when the live URL is already under the mount, otherwise prefix it.\nfunction dispatchNavTarget(path: string): string {\n if (typeof window === \"undefined\") return path;\n const basePath = appBasePath();\n if (!basePath) return path;\n const pathname = window.location.pathname;\n const routerHasBasename =\n pathname === basePath || pathname.startsWith(`${basePath}/`);\n return routerHasBasename ? path : appPath(path);\n}\n\ninterface DispatchThreadUrlSync {\n routeThreadId: string | null;\n getPath: (threadId: string | null) => string;\n navigate: (path: string, options?: { replace?: boolean }) => void;\n}\n\ntype DispatchAgentChatSurfaceProps = ComponentProps<typeof AgentChatSurface> & {\n threadUrlSync?: DispatchThreadUrlSync;\n};\n\nfunction DispatchAgentChatSurface(props: DispatchAgentChatSurfaceProps) {\n return <AgentChatSurface {...props} />;\n}\n\nfunction DispatchRequestIdButton({ requestId }: { requestId: string | null }) {\n const t = useT();\n const [copied, setCopied] = useState(false);\n\n const handleCopy = useCallback(() => {\n if (!requestId) return;\n void writeClipboardText(requestId).then((ok) => {\n if (!ok) return;\n setCopied(true);\n window.setTimeout(() => setCopied(false), 1000);\n });\n }, [requestId]);\n\n return (\n <Button\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n disabled={!requestId}\n onClick={handleCopy}\n aria-label={t(\"dispatch.pages.copyRequestId\", {\n defaultValue: requestId ? \"Copy request ID\" : \"Request ID unavailable\",\n })}\n >\n {copied ? (\n <IconCheck aria-hidden=\"true\" />\n ) : (\n <IconCopy aria-hidden=\"true\" />\n )}\n {copied\n ? t(\"dispatch.pages.copied\", { defaultValue: \"Copied\" })\n : t(\"dispatch.pages.copyRequestId\", {\n defaultValue: requestId\n ? \"Copy request ID\"\n : \"Request ID unavailable\",\n })}\n </Button>\n );\n}\n\ninterface DispatchChatLocationState {\n dispatchPrompt?: {\n id?: string | number;\n message?: string;\n selectedModel?: string | null;\n selectedEngine?: string | null;\n selectedEffort?: ComponentProps<typeof AgentChatSurface>[\"selectedEffort\"];\n };\n dispatchThread?: {\n id?: string | number;\n threadId?: string;\n };\n}\n\nexport function meta() {\n return [{ title: \"Chat — Dispatch\" }];\n}\n\nexport default function ChatRoute() {\n const t = useT();\n const location = useLocation();\n const navigate = useNavigate();\n const routeThreadId = threadIdFromPath(location.pathname);\n const activeRunId = useActiveAgentChatRunId(routeThreadId);\n const agentPath = new URLSearchParams(location.search).get(\"agent\");\n const agentsQuery = useActionQuery<WorkspaceAgentResource[]>(\n \"list-workspace-resources\",\n { kind: \"agent\" },\n { enabled: Boolean(agentPath), staleTime: 30_000 },\n );\n const agent = useMemo(\n () =>\n agentPath\n ? (agentsQuery.data ?? []).find((resource) => {\n const profileId = resource.path\n .replace(/^agents\\//, \"\")\n .replace(/\\.md$/, \"\");\n return (\n resource.path === agentPath ||\n resource.id === agentPath ||\n profileId === agentPath\n );\n })\n : undefined,\n [agentPath, agentsQuery.data],\n );\n const agentScope = useMemo(\n () =>\n agent\n ? {\n type: \"agent\",\n id: agent.id,\n label: agent.name,\n contextKey: `dispatch-agent:${agent.id}`,\n }\n : null,\n [agent],\n );\n const handledStateIds = useRef(new Set<string>());\n const extensions = useDispatchExtensions();\n const suppressInlineOpenApp =\n extensions?.chatFirst === true ||\n readChatFirstMode() ||\n new URLSearchParams(location.search).get(\"chatFirst\") === \"1\";\n\n const navigateThreadUrl = useCallback(\n (path: string, options?: { replace?: boolean }) =>\n navigate(dispatchNavTarget(path), options),\n [navigate],\n );\n const threadUrlSync = useMemo<DispatchThreadUrlSync>(\n () => ({\n routeThreadId: routeThreadId ?? null,\n getPath: (threadId) => chatThreadPath(threadId, agentPath ?? undefined),\n navigate: navigateThreadUrl,\n }),\n [agentPath, routeThreadId, navigateThreadUrl],\n );\n const state = location.state as DispatchChatLocationState | null;\n const prompt = state?.dispatchPrompt;\n const thread = state?.dispatchThread;\n\n useEffect(() => {\n const message = prompt?.message?.trim();\n const threadId = thread?.threadId?.trim();\n if (!message && !threadId) return;\n\n const stateId = String(\n prompt?.id ?? thread?.id ?? `${message ?? \"\"}:${threadId ?? \"\"}`,\n );\n if (handledStateIds.current.has(stateId)) return;\n handledStateIds.current.add(stateId);\n\n const timer = window.setTimeout(() => {\n if (threadId) {\n window.dispatchEvent(\n new CustomEvent(\"agent-chat:open-thread\", {\n detail: { threadId },\n }),\n );\n }\n if (message) {\n submitOverviewPrompt(message, prompt?.selectedModel, {\n openSidebar: false,\n selectedEngine: prompt?.selectedEngine,\n selectedEffort: prompt?.selectedEffort,\n });\n }\n navigate(`${location.pathname}${location.search}${location.hash}`, {\n replace: true,\n state: null,\n });\n }, 0);\n\n return () => window.clearTimeout(timer);\n }, [\n location.hash,\n location.pathname,\n location.search,\n navigate,\n prompt?.id,\n prompt?.message,\n prompt?.selectedModel,\n prompt?.selectedEngine,\n prompt?.selectedEffort,\n thread?.id,\n thread?.threadId,\n ]);\n\n useEffect(() => {\n function handleChatRunning(event: Event) {\n const detail = (event as CustomEvent).detail;\n if (detail?.isRunning === true) markAgentChatHomeHandoff(\"dispatch\");\n }\n\n window.addEventListener(\"agentNative.chatRunning\", handleChatRunning);\n return () =>\n window.removeEventListener(\"agentNative.chatRunning\", handleChatRunning);\n }, []);\n\n useEffect(() => {\n if (!agent) return;\n const timer = window.setTimeout(() => {\n insertAgentComposerReference(\n {\n label: agent.name,\n icon: \"agent\",\n source: \"agent:custom\",\n refType: \"custom-agent\",\n refId: agent.id,\n refPath: agent.path,\n slotKey: \"dispatch-agent\",\n slotLabel: \"Agent\",\n },\n { openSidebar: false },\n );\n }, 0);\n return () => window.clearTimeout(timer);\n }, [agent?.id, agent?.name, agent?.path]);\n\n if (agentPath && agentsQuery.isLoading) {\n return (\n <div className=\"flex h-full min-h-0 flex-col gap-4 bg-background px-4 py-6 sm:px-6\">\n <Skeleton className=\"mx-auto h-6 w-48\" />\n <Skeleton className=\"mx-auto h-12 w-full max-w-2xl\" />\n </div>\n );\n }\n\n if (agentPath && agentsQuery.isError) {\n return (\n <div className=\"flex h-full min-h-0 items-center justify-center bg-background px-4\">\n <ActionQueryError\n error={agentsQuery.error}\n onRetry={() => void agentsQuery.refetch()}\n />\n </div>\n );\n }\n\n if (agentPath && !agent) {\n return (\n <div className=\"flex h-full min-h-0 items-center justify-center bg-background px-4 text-sm text-muted-foreground\">\n This agent is no longer available.\n </div>\n );\n }\n\n return (\n <div className=\"flex h-full min-h-0 flex-col bg-background\">\n {threadUrlSync.routeThreadId ? (\n <div className=\"flex justify-end px-4 pt-4 sm:px-6\">\n <DispatchRequestIdButton requestId={activeRunId} />\n </div>\n ) : null}\n <DispatchAgentChatSurface\n key={agent ? `agent-${agent.id}` : \"dispatch\"}\n mode=\"page\"\n chatViewTransition\n className=\"dispatch-chat-panel px-4 sm:px-6\"\n defaultMode=\"chat\"\n storageKey={agent ? `dispatch-agent-${agent.id}` : \"dispatch\"}\n scope={agentScope}\n threadUrlSync={threadUrlSync}\n showHeader={false}\n showTabBar={false}\n dynamicSuggestions={false}\n suppressInlineOpenApp={suppressInlineOpenApp}\n suggestions={[]}\n emptyStateText={t(\"dispatch.pages.chatAcrossAppsDescription\", {\n defaultValue:\n \"Route work, inspect status, or create something new from one place.\",\n })}\n emptyStateDisplay=\"hidden\"\n {...(!prompt?.message\n ? {\n centerComposerWhenEmpty: true,\n composerLayoutVariant: \"hero\" as const,\n }\n : {})}\n composerPlaceholder={\n agent\n ? `Ask ${agent.name}...`\n : t(\"dispatch.pages.chatPromptPlaceholder\", {\n defaultValue: \"Ask Dispatch...\",\n })\n }\n composerSlot={\n agent ? (\n <div className=\"dispatch-chat-intro\">\n <h1>{agent.name}</h1>\n {agent.description ? <p>{agent.description}</p> : null}\n </div>\n ) : !prompt?.message ? (\n <div className=\"dispatch-chat-intro\">\n <h1>\n {t(\"dispatch.pages.chatAcrossApps\", {\n defaultValue: \"Chat across your apps\",\n })}\n </h1>\n <p>\n {t(\"dispatch.pages.chatAcrossAppsDescription\", {\n defaultValue:\n \"Route work, inspect status, or create something new from one place.\",\n })}\n </p>\n </div>\n ) : null\n }\n />\n </div>\n );\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-native/dispatch",
3
- "version": "0.30.3",
3
+ "version": "0.31.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": {
@@ -87,7 +87,7 @@
87
87
  "tailwind-merge": "^3.5.0",
88
88
  "vaul": "^1.1.2",
89
89
  "zod": "^4.3.6",
90
- "@agent-native/toolkit": "0.16.6"
90
+ "@agent-native/toolkit": "0.16.7"
91
91
  },
92
92
  "devDependencies": {
93
93
  "@react-router/dev": "^8.1.0",
@@ -101,10 +101,10 @@
101
101
  "typescript-7": "npm:typescript@^7.0.2",
102
102
  "vite": "8.1.0",
103
103
  "vitest": "^4.1.5",
104
- "@agent-native/core": "0.164.4"
104
+ "@agent-native/core": "0.0.0-beta-20260819190057"
105
105
  },
106
106
  "peerDependencies": {
107
- "@agent-native/core": ">=0.8.0",
107
+ "@agent-native/core": "0.0.0-beta-20260819190057",
108
108
  "react": ">=19.2.7",
109
109
  "react-dom": ">=19.2.7",
110
110
  "react-router": ">=8"
@@ -0,0 +1,420 @@
1
+ import { useActionQuery } from "@agent-native/core/client/hooks";
2
+ import {
3
+ IconAlertTriangle,
4
+ IconClock,
5
+ IconExternalLink,
6
+ IconPlayerPause,
7
+ IconPlayerPlay,
8
+ } from "@tabler/icons-react";
9
+ import { Link } from "react-router";
10
+
11
+ import {
12
+ automationScopeLabel,
13
+ automationStatus,
14
+ automationTarget,
15
+ } from "../lib/automation-display";
16
+ import {
17
+ automationRunScope,
18
+ type DispatchAutomationItem,
19
+ } from "../lib/automations";
20
+ import { ActionQueryError } from "./action-query-error";
21
+ import { Badge } from "./ui/badge";
22
+ import { Button } from "./ui/button";
23
+ import { Skeleton } from "./ui/skeleton";
24
+
25
+ interface AutomationRun {
26
+ id: string;
27
+ runId: string | null;
28
+ threadId: string | null;
29
+ status: "running" | "success" | "error" | "interrupted";
30
+ startedAt: number;
31
+ finishedAt: number | null;
32
+ error: string | null;
33
+ }
34
+
35
+ export interface AutomationDetailsPanelProps {
36
+ automation: DispatchAutomationItem;
37
+ isToggling?: boolean;
38
+ onToggle: () => void;
39
+ }
40
+
41
+ function formatTimestamp(value: number | string | null | undefined): string {
42
+ if (value == null) return "Not recorded";
43
+ const date = new Date(value);
44
+ return Number.isNaN(date.getTime()) ? "Unknown time" : date.toLocaleString();
45
+ }
46
+
47
+ function formatDuration(run: AutomationRun): string | null {
48
+ if (!run.finishedAt) return null;
49
+ const seconds = Math.max(
50
+ 0,
51
+ Math.round((run.finishedAt - run.startedAt) / 1000),
52
+ );
53
+ return `${seconds}s`;
54
+ }
55
+
56
+ function runDebugPath(run: AutomationRun): string | null {
57
+ const params = new URLSearchParams();
58
+ if (run.runId) params.set("runId", run.runId);
59
+ else if (run.threadId) params.set("threadId", run.threadId);
60
+ else return null;
61
+ return `/admin/thread-debug?${params.toString()}`;
62
+ }
63
+
64
+ function runStatusVariant(
65
+ status: AutomationRun["status"],
66
+ ): "default" | "destructive" | "outline" {
67
+ if (status === "error") return "destructive";
68
+ if (status === "success") return "default";
69
+ return "outline";
70
+ }
71
+
72
+ function DetailField({
73
+ label,
74
+ value,
75
+ mono = false,
76
+ }: {
77
+ label: string;
78
+ value: string;
79
+ mono?: boolean;
80
+ }) {
81
+ return (
82
+ <div className="min-w-0">
83
+ <dt className="text-[11px] uppercase tracking-wide text-muted-foreground">
84
+ {label}
85
+ </dt>
86
+ <dd
87
+ className={
88
+ mono
89
+ ? "mt-1 break-words font-mono text-xs text-foreground"
90
+ : "mt-1 break-words text-sm text-foreground"
91
+ }
92
+ >
93
+ {value}
94
+ </dd>
95
+ </div>
96
+ );
97
+ }
98
+
99
+ function runAsLabel(value: DispatchAutomationItem["runAs"]): string {
100
+ if (value === "creator") return "Creator identity";
101
+ if (value === "shared") return "Shared identity";
102
+ return "Default identity";
103
+ }
104
+
105
+ export function AutomationDetailsPanel({
106
+ automation,
107
+ isToggling = false,
108
+ onToggle,
109
+ }: AutomationDetailsPanelProps) {
110
+ const runsQuery = useActionQuery<AutomationRun[]>(
111
+ "list-automation-runs",
112
+ {
113
+ name: automation.name,
114
+ scope: automationRunScope(automation),
115
+ appId: automation.appId ?? "dispatch",
116
+ },
117
+ { staleTime: 5_000 },
118
+ );
119
+ const runs = runsQuery.data ?? [];
120
+ const status = automationStatus(automation);
121
+ const isScheduled = automation.triggerType !== "event";
122
+ const prompt = automation.body?.trim();
123
+ const hasExecutionTarget = Boolean(
124
+ automation.executionHostId ||
125
+ automation.executionEngine ||
126
+ automation.executionCwd,
127
+ );
128
+ const hasDelivery = Boolean(
129
+ automation.deliveryPlatform ||
130
+ automation.deliveryDestination ||
131
+ automation.deliveryThreadRef,
132
+ );
133
+
134
+ return (
135
+ <aside className="flex min-h-[34rem] min-w-0 flex-col overflow-hidden rounded-lg border bg-card">
136
+ <header className="flex items-start justify-between gap-3 border-b p-5">
137
+ <div className="min-w-0">
138
+ <div className="flex items-center gap-2">
139
+ <span
140
+ className={
141
+ status.tone === "danger"
142
+ ? "size-2 shrink-0 rounded-full bg-destructive"
143
+ : status.tone === "default"
144
+ ? "size-2 shrink-0 rounded-full bg-primary"
145
+ : "size-2 shrink-0 rounded-full bg-muted-foreground/40"
146
+ }
147
+ />
148
+ <Badge
149
+ variant={status.tone === "danger" ? "destructive" : "outline"}
150
+ className="h-5"
151
+ >
152
+ {status.label}
153
+ </Badge>
154
+ </div>
155
+ <h2 className="mt-2 break-words text-base font-semibold text-foreground">
156
+ {automation.name}
157
+ </h2>
158
+ <p className="mt-1 text-xs text-muted-foreground">
159
+ {isScheduled ? "Schedule" : "Event"} ·{" "}
160
+ {automationScopeLabel(automation)}
161
+ </p>
162
+ </div>
163
+ </header>
164
+
165
+ <div className="min-h-0 flex-1 space-y-5 overflow-y-auto p-5">
166
+ <section>
167
+ <h3 className="text-sm font-semibold text-foreground">Prompt</h3>
168
+ {prompt ? (
169
+ <pre className="mt-2 max-h-64 overflow-auto whitespace-pre-wrap break-words rounded-md bg-muted/40 p-3 font-sans text-sm leading-6 text-foreground">
170
+ {prompt}
171
+ </pre>
172
+ ) : (
173
+ <p className="mt-2 rounded-md border border-dashed p-3 text-sm text-muted-foreground">
174
+ No prompt recorded.
175
+ </p>
176
+ )}
177
+ </section>
178
+
179
+ <section className="border-t pt-5">
180
+ <h3 className="text-sm font-semibold text-foreground">
181
+ Configuration
182
+ </h3>
183
+ <dl className="mt-3 grid grid-cols-2 gap-x-4 gap-y-4">
184
+ <DetailField
185
+ label="Trigger"
186
+ value={isScheduled ? "Scheduled" : "Event-triggered"}
187
+ />
188
+ <DetailField
189
+ label={isScheduled ? "Schedule" : "Event"}
190
+ value={automationTarget(automation)}
191
+ />
192
+ {isScheduled ? (
193
+ <>
194
+ {automation.schedule ? (
195
+ <DetailField
196
+ label="Cron expression"
197
+ value={automation.schedule}
198
+ mono
199
+ />
200
+ ) : null}
201
+ <DetailField
202
+ label="Timezone"
203
+ value={automation.timezone || "Account default"}
204
+ />
205
+ </>
206
+ ) : null}
207
+ <DetailField
208
+ label="Condition"
209
+ value={automation.condition || "Always"}
210
+ />
211
+ <DetailField
212
+ label="Domain"
213
+ value={automation.domain || "Default"}
214
+ />
215
+ <DetailField
216
+ label="Model"
217
+ value={automation.model || "Default model"}
218
+ />
219
+ <DetailField label="Mode" value={automation.mode || "Agentic"} />
220
+ <DetailField label="Runs as" value={runAsLabel(automation.runAs)} />
221
+ <DetailField
222
+ label="Next run"
223
+ value={
224
+ automation.enabled
225
+ ? formatTimestamp(automation.nextRun)
226
+ : "Paused"
227
+ }
228
+ />
229
+ <DetailField
230
+ label="Last run"
231
+ value={formatTimestamp(automation.lastRun)}
232
+ />
233
+ <DetailField
234
+ label="Last checked"
235
+ value={formatTimestamp(automation.lastCheck)}
236
+ />
237
+ <DetailField
238
+ label="Created by"
239
+ value={automation.createdBy || "Unknown"}
240
+ />
241
+ </dl>
242
+ </section>
243
+
244
+ <section className="border-t pt-5">
245
+ <h3 className="text-sm font-semibold text-foreground">
246
+ Capabilities
247
+ </h3>
248
+ {automation.mcpTools && automation.mcpTools.length > 0 ? (
249
+ <div className="mt-2 flex flex-wrap gap-1.5">
250
+ {automation.mcpTools.map((toolName) => (
251
+ <code
252
+ key={toolName}
253
+ className="max-w-full break-all rounded bg-muted px-1.5 py-1 text-[11px] text-muted-foreground"
254
+ >
255
+ {toolName}
256
+ </code>
257
+ ))}
258
+ </div>
259
+ ) : (
260
+ <p className="mt-2 text-sm text-muted-foreground">
261
+ No explicit tool allowlist.
262
+ </p>
263
+ )}
264
+ </section>
265
+
266
+ {hasExecutionTarget ? (
267
+ <section className="border-t pt-5">
268
+ <h3 className="text-sm font-semibold text-foreground">
269
+ Execution target
270
+ </h3>
271
+ <dl className="mt-3 grid grid-cols-2 gap-x-4 gap-y-4">
272
+ <DetailField
273
+ label="Host"
274
+ value={automation.executionHostId || "Server default"}
275
+ mono={Boolean(automation.executionHostId)}
276
+ />
277
+ <DetailField
278
+ label="Engine"
279
+ value={automation.executionEngine || "Default"}
280
+ />
281
+ {automation.executionCwd ? (
282
+ <DetailField
283
+ label="Working directory"
284
+ value={automation.executionCwd}
285
+ mono
286
+ />
287
+ ) : null}
288
+ </dl>
289
+ </section>
290
+ ) : null}
291
+
292
+ {hasDelivery ? (
293
+ <section className="border-t pt-5">
294
+ <h3 className="text-sm font-semibold text-foreground">Delivery</h3>
295
+ <dl className="mt-3 grid grid-cols-2 gap-x-4 gap-y-4">
296
+ {automation.deliveryPlatform ? (
297
+ <DetailField
298
+ label="Platform"
299
+ value={automation.deliveryPlatform}
300
+ />
301
+ ) : null}
302
+ {automation.deliveryDestination ? (
303
+ <DetailField
304
+ label="Destination"
305
+ value={automation.deliveryDestination}
306
+ />
307
+ ) : null}
308
+ {automation.deliveryThreadRef ? (
309
+ <DetailField
310
+ label="Thread reference"
311
+ value={automation.deliveryThreadRef}
312
+ mono
313
+ />
314
+ ) : null}
315
+ </dl>
316
+ </section>
317
+ ) : null}
318
+
319
+ {automation.lastError ? (
320
+ <section className="flex items-start gap-2 border-t border-destructive/30 pt-5">
321
+ <IconAlertTriangle className="mt-0.5 size-4 shrink-0 text-destructive" />
322
+ <div className="min-w-0">
323
+ <h3 className="text-sm font-semibold text-destructive">
324
+ Latest scheduler result
325
+ </h3>
326
+ <p className="mt-1 break-words text-sm text-muted-foreground">
327
+ {automation.lastError}
328
+ </p>
329
+ </div>
330
+ </section>
331
+ ) : null}
332
+
333
+ <section className="border-t pt-5">
334
+ <div className="flex items-center justify-between gap-3">
335
+ <h3 className="text-sm font-semibold text-foreground">Past runs</h3>
336
+ {runs.length > 0 ? (
337
+ <Badge variant="outline">{runs.length} shown</Badge>
338
+ ) : null}
339
+ </div>
340
+ <div className="mt-3">
341
+ {runsQuery.isLoading ? (
342
+ <div className="space-y-3 rounded-md border p-3">
343
+ {Array.from({ length: 3 }).map((_, index) => (
344
+ <div key={index} className="space-y-2">
345
+ <Skeleton className="h-4 w-2/3" />
346
+ <Skeleton className="h-3 w-1/2" />
347
+ </div>
348
+ ))}
349
+ </div>
350
+ ) : runsQuery.isError ? (
351
+ <ActionQueryError
352
+ error={runsQuery.error}
353
+ onRetry={() => void runsQuery.refetch()}
354
+ />
355
+ ) : runs.length === 0 ? (
356
+ <p className="rounded-md border border-dashed p-3 text-sm text-muted-foreground">
357
+ No execution recorded yet.
358
+ </p>
359
+ ) : (
360
+ <ul className="divide-y rounded-md border">
361
+ {runs.map((run) => {
362
+ const debugPath = runDebugPath(run);
363
+ const duration = formatDuration(run);
364
+ return (
365
+ <li key={run.id} className="space-y-2 p-3">
366
+ <div className="flex flex-wrap items-center gap-2 text-sm">
367
+ <Badge variant={runStatusVariant(run.status)}>
368
+ {run.status}
369
+ </Badge>
370
+ <span className="text-muted-foreground">
371
+ {formatTimestamp(run.startedAt)}
372
+ </span>
373
+ {duration ? (
374
+ <span className="inline-flex items-center gap-1 text-xs text-muted-foreground">
375
+ <IconClock className="size-3.5" />
376
+ {duration}
377
+ </span>
378
+ ) : null}
379
+ {debugPath ? (
380
+ <Link
381
+ to={debugPath}
382
+ className="ml-auto inline-flex items-center gap-1 text-xs font-medium text-foreground underline-offset-4 hover:underline"
383
+ >
384
+ <IconExternalLink className="size-3.5" />
385
+ Open debug
386
+ </Link>
387
+ ) : null}
388
+ </div>
389
+ {run.error ? (
390
+ <p className="break-words text-sm text-destructive">
391
+ {run.error}
392
+ </p>
393
+ ) : null}
394
+ </li>
395
+ );
396
+ })}
397
+ </ul>
398
+ )}
399
+ </div>
400
+ </section>
401
+ </div>
402
+
403
+ <footer className="flex flex-wrap items-center gap-2 border-t p-4">
404
+ <Button
405
+ type="button"
406
+ variant="outline"
407
+ size="sm"
408
+ onClick={onToggle}
409
+ disabled={automation.canUpdate === false || isToggling}
410
+ >
411
+ {automation.enabled ? <IconPlayerPause /> : <IconPlayerPlay />}
412
+ {automation.enabled ? "Pause" : "Resume"}
413
+ </Button>
414
+ {automation.canUpdate === false ? (
415
+ <span className="text-xs text-muted-foreground">Read-only</span>
416
+ ) : null}
417
+ </footer>
418
+ </aside>
419
+ );
420
+ }