@agent-native/dispatch 0.23.5 → 0.24.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.
- package/dist/actions/list-connected-agents.d.ts +1 -0
- package/dist/actions/list-connected-agents.js +5 -1
- package/dist/actions/list-connected-agents.js.map +1 -1
- package/dist/actions/list-dispatch-usage-metrics.d.ts +2 -0
- package/dist/actions/list-dispatch-usage-metrics.js +11 -1
- package/dist/actions/list-dispatch-usage-metrics.js.map +1 -1
- package/dist/actions/remix-workspace-template.js +4 -3
- package/dist/actions/remix-workspace-template.js.map +1 -1
- package/dist/actions/scaffold-workspace-app.js +3 -2
- package/dist/actions/scaffold-workspace-app.js.map +1 -1
- package/dist/actions/start-workspace-app-creation.js +3 -2
- package/dist/actions/start-workspace-app-creation.js.map +1 -1
- package/dist/actions/view-screen.js +11 -1
- package/dist/actions/view-screen.js.map +1 -1
- package/dist/components/app-icon.js +1 -1
- package/dist/components/app-icon.js.map +1 -1
- package/dist/components/connected-app-card.d.ts.map +1 -1
- package/dist/components/connected-app-card.js +2 -1
- package/dist/components/connected-app-card.js.map +1 -1
- package/dist/components/layout/Layout.d.ts.map +1 -1
- package/dist/components/layout/Layout.js +6 -7
- package/dist/components/layout/Layout.js.map +1 -1
- package/dist/components/workspace-template-card.d.ts.map +1 -1
- package/dist/components/workspace-template-card.js +8 -14
- package/dist/components/workspace-template-card.js.map +1 -1
- package/dist/hooks/use-navigation-state.d.ts +2 -0
- package/dist/hooks/use-navigation-state.d.ts.map +1 -1
- package/dist/hooks/use-navigation-state.js +10 -0
- package/dist/hooks/use-navigation-state.js.map +1 -1
- package/dist/lib/embed-session-recovery.d.ts +3 -0
- package/dist/lib/embed-session-recovery.d.ts.map +1 -0
- package/dist/lib/embed-session-recovery.js +18 -0
- package/dist/lib/embed-session-recovery.js.map +1 -0
- package/dist/lib/other-apps.d.ts +2 -0
- package/dist/lib/other-apps.d.ts.map +1 -1
- package/dist/lib/other-apps.js.map +1 -1
- package/dist/lib/workspace-apps.d.ts +4 -0
- package/dist/lib/workspace-apps.d.ts.map +1 -1
- package/dist/lib/workspace-apps.js +7 -0
- package/dist/lib/workspace-apps.js.map +1 -1
- package/dist/lib/workspace-apps.test.d.ts +2 -0
- package/dist/lib/workspace-apps.test.d.ts.map +1 -0
- package/dist/lib/workspace-apps.test.js +16 -0
- package/dist/lib/workspace-apps.test.js.map +1 -0
- package/dist/routes/pages/apps.$appId.d.ts.map +1 -1
- package/dist/routes/pages/apps.$appId.js +10 -11
- package/dist/routes/pages/apps.$appId.js.map +1 -1
- package/dist/routes/pages/chat.d.ts.map +1 -1
- package/dist/routes/pages/chat.js +7 -2
- package/dist/routes/pages/chat.js.map +1 -1
- package/dist/routes/pages/metrics.d.ts.map +1 -1
- package/dist/routes/pages/metrics.js +143 -17
- package/dist/routes/pages/metrics.js.map +1 -1
- package/dist/routes/pages/settings.d.ts.map +1 -1
- package/dist/routes/pages/settings.js +13 -1
- package/dist/routes/pages/settings.js.map +1 -1
- package/dist/server/lib/usage-metrics-store.d.ts +17 -0
- package/dist/server/lib/usage-metrics-store.d.ts.map +1 -1
- package/dist/server/lib/usage-metrics-store.js +187 -30
- package/dist/server/lib/usage-metrics-store.js.map +1 -1
- package/dist/server/plugins/agent-chat.js +2 -0
- package/dist/server/plugins/agent-chat.js.map +1 -1
- package/dist/server/plugins/integrations.d.ts.map +1 -1
- package/dist/server/plugins/integrations.js +1 -0
- package/dist/server/plugins/integrations.js.map +1 -1
- package/package.json +3 -3
- package/src/actions/list-connected-agents.spec.ts +73 -0
- package/src/actions/list-connected-agents.ts +6 -2
- package/src/actions/list-dispatch-usage-metrics.ts +11 -1
- package/src/actions/remix-workspace-template.spec.ts +29 -0
- package/src/actions/remix-workspace-template.ts +13 -6
- package/src/actions/scaffold-workspace-app.ts +12 -5
- package/src/actions/start-workspace-app-creation.ts +8 -2
- package/src/actions/view-screen.ts +13 -1
- package/src/components/app-icon.spec.tsx +34 -0
- package/src/components/app-icon.tsx +1 -1
- package/src/components/connected-app-card.tsx +3 -1
- package/src/components/dispatch-control-plane.spec.tsx +8 -1
- package/src/components/layout/Layout.spec.tsx +3 -0
- package/src/components/layout/Layout.tsx +13 -9
- package/src/components/workspace-template-card.spec.tsx +1 -1
- package/src/components/workspace-template-card.tsx +9 -16
- package/src/hooks/use-navigation-state.ts +12 -0
- package/src/lib/embed-session-recovery.spec.ts +63 -0
- package/src/lib/embed-session-recovery.ts +26 -0
- package/src/lib/other-apps.ts +2 -0
- package/src/lib/workspace-apps.test.ts +20 -0
- package/src/lib/workspace-apps.ts +10 -0
- package/src/routes/pages/apps.$appId.tsx +10 -11
- package/src/routes/pages/chat.spec.tsx +10 -0
- package/src/routes/pages/chat.tsx +8 -0
- package/src/routes/pages/metrics.tsx +452 -102
- package/src/routes/pages/settings.tsx +13 -1
- package/src/server/lib/usage-metrics-store.spec.ts +117 -0
- package/src/server/lib/usage-metrics-store.ts +249 -30
- package/src/server/plugins/agent-chat.spec.ts +4 -0
- package/src/server/plugins/agent-chat.ts +2 -0
- package/src/server/plugins/integrations.ts +1 -0
- package/src/styles/dispatch.css +9 -0
package/src/lib/other-apps.ts
CHANGED
|
@@ -5,6 +5,8 @@ export interface ConnectedAppSummary {
|
|
|
5
5
|
name: string;
|
|
6
6
|
description?: string;
|
|
7
7
|
url: string;
|
|
8
|
+
/** Canonical app-home URL used for launchers; `url` remains the A2A endpoint. */
|
|
9
|
+
homeUrl?: string;
|
|
8
10
|
color?: string;
|
|
9
11
|
source?: "builtin" | "custom" | "workspace";
|
|
10
12
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { workspaceAppEmbedTarget } from "./workspace-apps";
|
|
4
|
+
|
|
5
|
+
describe("workspaceAppEmbedTarget", () => {
|
|
6
|
+
it("uses the app URL as the embed root when a mount path is also present", () => {
|
|
7
|
+
expect(
|
|
8
|
+
workspaceAppEmbedTarget({
|
|
9
|
+
path: "/content",
|
|
10
|
+
url: "https://workspace.example.test/content",
|
|
11
|
+
}),
|
|
12
|
+
).toEqual({ url: "https://workspace.example.test/content" });
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it("falls back to the mounted path when no app URL is available", () => {
|
|
16
|
+
expect(workspaceAppEmbedTarget({ path: "/content", url: null })).toEqual({
|
|
17
|
+
path: "/content",
|
|
18
|
+
});
|
|
19
|
+
});
|
|
20
|
+
});
|
|
@@ -70,6 +70,16 @@ export function workspaceAppHref(app: WorkspaceAppSummary): string | null {
|
|
|
70
70
|
return app.path || app.url || null;
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
export function workspaceAppEmbedTarget(
|
|
74
|
+
app: Pick<WorkspaceAppSummary, "path" | "url">,
|
|
75
|
+
): { path?: string; url?: string } {
|
|
76
|
+
const url = app.url?.trim();
|
|
77
|
+
if (url) return { url };
|
|
78
|
+
|
|
79
|
+
const path = app.path.trim();
|
|
80
|
+
return path.startsWith("/") ? { path } : path ? { url: path } : {};
|
|
81
|
+
}
|
|
82
|
+
|
|
73
83
|
export function isPendingBuilderHref(app: WorkspaceAppSummary): boolean {
|
|
74
84
|
return app.status === "pending" && !!app.builderUrl;
|
|
75
85
|
}
|
|
@@ -13,7 +13,9 @@ import { Badge } from "../../components/ui/badge";
|
|
|
13
13
|
import { Button } from "../../components/ui/button";
|
|
14
14
|
import { Skeleton } from "../../components/ui/skeleton";
|
|
15
15
|
import { Spinner } from "../../components/ui/spinner";
|
|
16
|
+
import { isEmbedSessionExpiredMessage } from "../../lib/embed-session-recovery";
|
|
16
17
|
import {
|
|
18
|
+
workspaceAppEmbedTarget,
|
|
17
19
|
workspaceAppHref,
|
|
18
20
|
type WorkspaceAppSummary,
|
|
19
21
|
} from "../../lib/workspace-apps";
|
|
@@ -61,12 +63,12 @@ export default function WorkspaceAppRoute() {
|
|
|
61
63
|
});
|
|
62
64
|
const embedInput = useMemo<EmbedSessionInput | null>(() => {
|
|
63
65
|
if (!app || !href) return null;
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}, [app?.id, app?.path, href]);
|
|
66
|
+
return {
|
|
67
|
+
app: app.id,
|
|
68
|
+
...workspaceAppEmbedTarget(app),
|
|
69
|
+
chrome: "minimal",
|
|
70
|
+
};
|
|
71
|
+
}, [app?.id, app?.path, app?.url, href]);
|
|
70
72
|
|
|
71
73
|
useEffect(() => {
|
|
72
74
|
if (!app || app.status === "pending" || !embedInput) return;
|
|
@@ -99,12 +101,8 @@ export default function WorkspaceAppRoute() {
|
|
|
99
101
|
|
|
100
102
|
useEffect(() => {
|
|
101
103
|
const handleEmbedSessionExpired = (event: MessageEvent) => {
|
|
102
|
-
if (
|
|
103
|
-
event.data?.type !== "agentNative.embedSessionExpired" ||
|
|
104
|
-
event.source !== embedFrameRef.current?.contentWindow
|
|
105
|
-
) {
|
|
104
|
+
if (!isEmbedSessionExpiredMessage(event, embedFrameRef.current, null))
|
|
106
105
|
return;
|
|
107
|
-
}
|
|
108
106
|
setEmbedAttempt((attempt) => attempt + 1);
|
|
109
107
|
};
|
|
110
108
|
|
|
@@ -222,6 +220,7 @@ export default function WorkspaceAppRoute() {
|
|
|
222
220
|
<div className="min-h-0 flex-1 bg-muted/20">
|
|
223
221
|
{embedUrl ? (
|
|
224
222
|
<iframe
|
|
223
|
+
key={`${embedUrl}:${embedAttempt}`}
|
|
225
224
|
data-dispatch-workspace-app-frame
|
|
226
225
|
src={embedUrl}
|
|
227
226
|
title={app.name}
|
|
@@ -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:
|