@agent-native/core 0.72.2 → 0.72.4
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/corpus/README.md +1 -1
- package/corpus/core/CHANGELOG.md +16 -0
- package/corpus/core/package.json +6 -6
- package/corpus/core/src/a2a/client.ts +7 -3
- package/corpus/core/src/client/AgentPanel.tsx +12 -1
- package/corpus/core/src/client/MultiTabAssistantChat.tsx +263 -13
- package/corpus/core/src/client/api-path.ts +28 -0
- package/corpus/core/src/client/embed-auth.ts +26 -0
- package/corpus/core/src/client/mcp-app-host.ts +6 -0
- package/corpus/core/src/client/mcp-apps/McpAppRenderer.tsx +227 -33
- package/corpus/core/src/client/resources/use-mcp-servers.ts +1 -0
- package/corpus/core/src/client/settings/DemoModeSection.tsx +3 -1
- package/corpus/core/src/client/use-agent-chat-home-handoff.ts +16 -4
- package/corpus/core/src/client/use-chat-threads.ts +86 -5
- package/corpus/core/src/demo/fetch-interceptor.ts +1 -1
- package/corpus/core/src/embedding/agent.ts +6 -1
- package/corpus/core/src/embedding/bridge.ts +34 -1
- package/corpus/core/src/mcp/build-server.ts +38 -10
- package/corpus/core/src/mcp/embed-app.ts +120 -45
- package/corpus/core/src/mcp/index.ts +5 -0
- package/corpus/core/src/mcp/org-directory.ts +70 -11
- package/corpus/core/src/mcp/server.ts +14 -0
- package/corpus/core/src/mcp-client/config.ts +12 -0
- package/corpus/core/src/mcp-client/index.ts +2 -0
- package/corpus/core/src/mcp-client/manager.ts +268 -9
- package/corpus/core/src/mcp-client/remote-store.ts +154 -0
- package/corpus/core/src/mcp-client/routes.ts +2 -0
- package/corpus/core/src/server/agent-chat-plugin.ts +8 -0
- package/corpus/core/src/server/index.ts +1 -0
- package/corpus/core/src/shared/mcp-embed-headers.ts +44 -3
- package/corpus/core/src/vite/client.ts +53 -0
- package/corpus/templates/assets/.agents/skills/a2a-assets/SKILL.md +17 -10
- package/corpus/templates/assets/.agents/skills/asset-generation/SKILL.md +15 -6
- package/corpus/templates/assets/AGENTS.md +7 -5
- package/corpus/templates/assets/actions/generate-asset.ts +161 -0
- package/corpus/templates/assets/actions/list-assets.ts +4 -2
- package/corpus/templates/assets/actions/navigate.ts +2 -1
- package/corpus/templates/assets/actions/open-asset-picker.ts +84 -3
- package/corpus/templates/assets/app/components/layout/Layout.tsx +3 -2
- package/corpus/templates/assets/app/components/layout/Sidebar.tsx +27 -4
- package/corpus/templates/assets/app/hooks/use-navigation-state.ts +35 -5
- package/corpus/templates/assets/app/routes/_index.tsx +12 -0
- package/corpus/templates/assets/app/routes/chat.$threadId.tsx +1 -0
- package/corpus/templates/assets/app/routes/library.tsx +243 -40
- package/corpus/templates/chat/actions/navigate.ts +3 -0
- package/corpus/templates/chat/app/components/layout/Layout.tsx +7 -2
- package/corpus/templates/chat/app/components/layout/Sidebar.tsx +27 -4
- package/corpus/templates/chat/app/hooks/use-navigation-state.ts +38 -6
- package/corpus/templates/chat/app/routes/_index.tsx +13 -0
- package/corpus/templates/chat/app/routes/chat.$threadId.tsx +1 -0
- package/corpus/templates/clips/desktop/src-tauri/src/native_speech.rs +9 -0
- package/corpus/templates/design/AGENTS.md +10 -5
- package/corpus/templates/design/actions/connect-assets-mcp.ts +131 -0
- package/corpus/templates/design/actions/insert-asset.ts +219 -0
- package/corpus/templates/design/actions/view-screen.ts +3 -1
- package/corpus/templates/design/app/components/editor/PromptDialog.tsx +1 -1
- package/corpus/templates/design/app/pages/DesignEditor.tsx +53 -0
- package/corpus/templates/design/server/plugins/agent-chat.ts +5 -1
- package/corpus/templates/design/server/plugins/assets-mcp.ts +60 -0
- package/corpus/templates/dispatch/app/routes/chat.$threadId.tsx +26 -0
- package/dist/a2a/client.d.ts +1 -0
- package/dist/a2a/client.d.ts.map +1 -1
- package/dist/a2a/client.js +5 -3
- package/dist/a2a/client.js.map +1 -1
- package/dist/client/AgentPanel.d.ts +6 -2
- package/dist/client/AgentPanel.d.ts.map +1 -1
- package/dist/client/AgentPanel.js +4 -4
- package/dist/client/AgentPanel.js.map +1 -1
- package/dist/client/MultiTabAssistantChat.d.ts +22 -1
- package/dist/client/MultiTabAssistantChat.d.ts.map +1 -1
- package/dist/client/MultiTabAssistantChat.js +215 -13
- package/dist/client/MultiTabAssistantChat.js.map +1 -1
- package/dist/client/api-path.d.ts.map +1 -1
- package/dist/client/api-path.js +27 -0
- package/dist/client/api-path.js.map +1 -1
- package/dist/client/embed-auth.d.ts.map +1 -1
- package/dist/client/embed-auth.js +27 -0
- package/dist/client/embed-auth.js.map +1 -1
- package/dist/client/mcp-app-host.d.ts.map +1 -1
- package/dist/client/mcp-app-host.js +5 -0
- package/dist/client/mcp-app-host.js.map +1 -1
- package/dist/client/mcp-apps/McpAppRenderer.d.ts +1 -0
- package/dist/client/mcp-apps/McpAppRenderer.d.ts.map +1 -1
- package/dist/client/mcp-apps/McpAppRenderer.js +179 -33
- package/dist/client/mcp-apps/McpAppRenderer.js.map +1 -1
- package/dist/client/resources/use-mcp-servers.d.ts +1 -0
- package/dist/client/resources/use-mcp-servers.d.ts.map +1 -1
- package/dist/client/resources/use-mcp-servers.js.map +1 -1
- package/dist/client/settings/DemoModeSection.d.ts.map +1 -1
- package/dist/client/settings/DemoModeSection.js +3 -1
- package/dist/client/settings/DemoModeSection.js.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.d.ts +7 -1
- package/dist/client/use-agent-chat-home-handoff.d.ts.map +1 -1
- package/dist/client/use-agent-chat-home-handoff.js +9 -5
- package/dist/client/use-agent-chat-home-handoff.js.map +1 -1
- package/dist/client/use-chat-threads.d.ts +6 -0
- package/dist/client/use-chat-threads.d.ts.map +1 -1
- package/dist/client/use-chat-threads.js +79 -5
- package/dist/client/use-chat-threads.js.map +1 -1
- package/dist/demo/fetch-interceptor.js +1 -1
- package/dist/demo/fetch-interceptor.js.map +1 -1
- package/dist/embedding/agent.d.ts +1 -1
- package/dist/embedding/agent.d.ts.map +1 -1
- package/dist/embedding/agent.js +1 -1
- package/dist/embedding/agent.js.map +1 -1
- package/dist/embedding/bridge.d.ts.map +1 -1
- package/dist/embedding/bridge.js +27 -1
- package/dist/embedding/bridge.js.map +1 -1
- package/dist/extensions/schema.d.ts +2 -2
- package/dist/mcp/build-server.d.ts +2 -0
- package/dist/mcp/build-server.d.ts.map +1 -1
- package/dist/mcp/build-server.js +32 -9
- package/dist/mcp/build-server.js.map +1 -1
- package/dist/mcp/embed-app.d.ts.map +1 -1
- package/dist/mcp/embed-app.js +120 -45
- package/dist/mcp/embed-app.js.map +1 -1
- package/dist/mcp/index.d.ts +1 -0
- package/dist/mcp/index.d.ts.map +1 -1
- package/dist/mcp/index.js +1 -0
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/org-directory.d.ts +1 -0
- package/dist/mcp/org-directory.d.ts.map +1 -1
- package/dist/mcp/org-directory.js +59 -11
- package/dist/mcp/org-directory.js.map +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +9 -0
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp-client/config.d.ts +12 -0
- package/dist/mcp-client/config.d.ts.map +1 -1
- package/dist/mcp-client/config.js.map +1 -1
- package/dist/mcp-client/index.d.ts +1 -1
- package/dist/mcp-client/index.d.ts.map +1 -1
- package/dist/mcp-client/index.js +1 -1
- package/dist/mcp-client/index.js.map +1 -1
- package/dist/mcp-client/manager.d.ts +2 -0
- package/dist/mcp-client/manager.d.ts.map +1 -1
- package/dist/mcp-client/manager.js +201 -9
- package/dist/mcp-client/manager.js.map +1 -1
- package/dist/mcp-client/remote-store.d.ts +27 -0
- package/dist/mcp-client/remote-store.d.ts.map +1 -1
- package/dist/mcp-client/remote-store.js +105 -0
- package/dist/mcp-client/remote-store.js.map +1 -1
- package/dist/mcp-client/routes.d.ts +1 -0
- package/dist/mcp-client/routes.d.ts.map +1 -1
- package/dist/mcp-client/routes.js +1 -0
- package/dist/mcp-client/routes.js.map +1 -1
- package/dist/provider-api/actions/github-repo-files.d.ts +2 -2
- package/dist/provider-api/actions/query-staged-dataset.d.ts +3 -3
- package/dist/provider-api/corpus-jobs.d.ts +8 -8
- package/dist/server/agent-chat-plugin.d.ts +2 -0
- package/dist/server/agent-chat-plugin.d.ts.map +1 -1
- package/dist/server/agent-chat-plugin.js +8 -0
- package/dist/server/agent-chat-plugin.js.map +1 -1
- package/dist/server/index.d.ts +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/index.js.map +1 -1
- package/dist/shared/mcp-embed-headers.d.ts +3 -1
- package/dist/shared/mcp-embed-headers.d.ts.map +1 -1
- package/dist/shared/mcp-embed-headers.js +40 -3
- package/dist/shared/mcp-embed-headers.js.map +1 -1
- package/dist/sharing/actions/list-resource-shares.d.ts +1 -1
- package/dist/sharing/actions/set-resource-visibility.d.ts +2 -2
- package/dist/sharing/actions/share-resource.d.ts +1 -1
- package/dist/sharing/schema.d.ts +1 -1
- package/dist/vite/client.d.ts.map +1 -1
- package/dist/vite/client.js +44 -0
- package/dist/vite/client.js.map +1 -1
- package/package.json +6 -6
|
@@ -22,11 +22,18 @@ function optionalLibraryTab(params: URLSearchParams) {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
function navigationFromPath(pathname: string, search = "") {
|
|
25
|
+
const params = new URLSearchParams(search);
|
|
26
|
+
const chat = pathname.match(/^\/chat\/([^/]+)/);
|
|
27
|
+
if (chat) {
|
|
28
|
+
return {
|
|
29
|
+
view: "create",
|
|
30
|
+
threadId: decodePathParam(chat[1]),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
25
33
|
// The "library" view is the brand-kit detail page (route /brand-kits/:id).
|
|
26
34
|
// Keep the internal view key stable for the agent/MCP contract.
|
|
27
35
|
const library = pathname.match(/^\/brand-kits\/([^/]+)/);
|
|
28
36
|
if (library) {
|
|
29
|
-
const params = new URLSearchParams(search);
|
|
30
37
|
return {
|
|
31
38
|
view: "library",
|
|
32
39
|
libraryId: library[1],
|
|
@@ -37,10 +44,13 @@ function navigationFromPath(pathname: string, search = "") {
|
|
|
37
44
|
if (asset) return { view: "asset", assetId: asset[1] };
|
|
38
45
|
const image = pathname.match(/^\/image\/([^/]+)/);
|
|
39
46
|
if (image) return { view: "asset", assetId: image[1] };
|
|
40
|
-
if (pathname === "/")
|
|
47
|
+
if (pathname === "/") {
|
|
48
|
+
return {
|
|
49
|
+
view: "create",
|
|
50
|
+
};
|
|
51
|
+
}
|
|
41
52
|
// The "picker" view is the image Library browser (route /library).
|
|
42
53
|
if (pathname === "/library") {
|
|
43
|
-
const params = new URLSearchParams(search);
|
|
44
54
|
return {
|
|
45
55
|
view: "picker",
|
|
46
56
|
mediaType:
|
|
@@ -93,7 +103,12 @@ function pathFromCommand(command: any): string | null {
|
|
|
93
103
|
}
|
|
94
104
|
if (command.view === "audit") return "/audit";
|
|
95
105
|
if (command.view === "settings") return "/settings";
|
|
96
|
-
if (command.view === "create")
|
|
106
|
+
if (command.view === "create") {
|
|
107
|
+
if (typeof command.threadId === "string" && command.threadId.trim()) {
|
|
108
|
+
return `/chat/${encodeURIComponent(command.threadId.trim())}`;
|
|
109
|
+
}
|
|
110
|
+
return "/";
|
|
111
|
+
}
|
|
97
112
|
if (command.view === "picker") {
|
|
98
113
|
const params = new URLSearchParams();
|
|
99
114
|
if (command.mediaType === "image" || command.mediaType === "video") {
|
|
@@ -131,7 +146,10 @@ export function useNavigationState() {
|
|
|
131
146
|
navigationFromPath(pathname, search),
|
|
132
147
|
getCommandPath: (command) => pathFromCommand(command),
|
|
133
148
|
onNavigate: (_command, path) => {
|
|
134
|
-
if (
|
|
149
|
+
if (
|
|
150
|
+
isCreatePath(location.pathname) &&
|
|
151
|
+
!isCreatePath(pathnameFromPath(path))
|
|
152
|
+
) {
|
|
135
153
|
markAgentChatHomeHandoff(ASSETS_CHAT_STORAGE_KEY);
|
|
136
154
|
}
|
|
137
155
|
},
|
|
@@ -141,3 +159,15 @@ export function useNavigationState() {
|
|
|
141
159
|
function pathnameFromPath(path: string): string {
|
|
142
160
|
return path.split(/[?#]/, 1)[0] || "/";
|
|
143
161
|
}
|
|
162
|
+
|
|
163
|
+
function decodePathParam(value: string): string {
|
|
164
|
+
try {
|
|
165
|
+
return decodeURIComponent(value);
|
|
166
|
+
} catch {
|
|
167
|
+
return value;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function isCreatePath(pathname: string): boolean {
|
|
172
|
+
return pathname === "/" || pathname.startsWith("/chat/");
|
|
173
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { useCallback, useEffect, useState } from "react";
|
|
2
|
+
import { useNavigate, useParams } from "react-router";
|
|
2
3
|
import {
|
|
3
4
|
AgentChatSurface,
|
|
4
5
|
getBrowserTabId,
|
|
@@ -61,7 +62,13 @@ export function meta() {
|
|
|
61
62
|
];
|
|
62
63
|
}
|
|
63
64
|
|
|
65
|
+
function chatThreadPath(threadId: string | null) {
|
|
66
|
+
return threadId ? `/chat/${encodeURIComponent(threadId)}` : "/";
|
|
67
|
+
}
|
|
68
|
+
|
|
64
69
|
export default function CreatePage() {
|
|
70
|
+
const { threadId } = useParams();
|
|
71
|
+
const navigate = useNavigate();
|
|
65
72
|
const [imageModel, setImageModel] = useState<string>(DEFAULT_IMAGE_MODEL);
|
|
66
73
|
|
|
67
74
|
useEffect(() => {
|
|
@@ -113,6 +120,11 @@ export default function CreatePage() {
|
|
|
113
120
|
className="assets-create-chat-panel"
|
|
114
121
|
defaultMode="chat"
|
|
115
122
|
storageKey={ASSETS_CHAT_STORAGE_KEY}
|
|
123
|
+
threadUrlSync={{
|
|
124
|
+
routeThreadId: threadId ?? null,
|
|
125
|
+
getPath: chatThreadPath,
|
|
126
|
+
navigate,
|
|
127
|
+
}}
|
|
116
128
|
browserTabId={getBrowserTabId()}
|
|
117
129
|
imageModelMenu={{
|
|
118
130
|
value: imageModel,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default, meta } from "./_index";
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
appPath,
|
|
10
10
|
getBrowserTabId,
|
|
11
11
|
getEmbedAuthToken,
|
|
12
|
+
isEmbedMcpChatBridgeActive,
|
|
12
13
|
isEmbedAuthActive,
|
|
13
14
|
sendMcpAppHostMessage,
|
|
14
15
|
updateMcpAppModelContext,
|
|
@@ -53,6 +54,7 @@ import {
|
|
|
53
54
|
DialogTitle,
|
|
54
55
|
} from "@/components/ui/dialog";
|
|
55
56
|
import { DEFAULT_LIBRARY_PRESETS } from "../../shared/library-presets";
|
|
57
|
+
import type { ImageQualityTier, StyleStrength } from "../../shared/api";
|
|
56
58
|
|
|
57
59
|
type AssetTab = "all" | "generated" | "references";
|
|
58
60
|
|
|
@@ -127,12 +129,44 @@ type HostConfig = {
|
|
|
127
129
|
prompt?: string;
|
|
128
130
|
query?: string;
|
|
129
131
|
libraryId?: string;
|
|
132
|
+
libraryHint?: string;
|
|
130
133
|
aspectRatio?: string;
|
|
131
134
|
presetId?: string;
|
|
132
135
|
count?: number;
|
|
136
|
+
tier?: ImageQualityTier;
|
|
137
|
+
styleStrength?: StyleStrength;
|
|
138
|
+
includeLogo?: boolean;
|
|
139
|
+
callerAppId?: string;
|
|
133
140
|
autoGenerate?: boolean;
|
|
141
|
+
candidateRunIds?: string[];
|
|
134
142
|
};
|
|
135
143
|
|
|
144
|
+
// Preselect the library whose title/description best matches a free-text brand
|
|
145
|
+
// or use-case hint. Falls back to no match (caller uses the first library).
|
|
146
|
+
function matchLibraryByHint(
|
|
147
|
+
libraries: Library[],
|
|
148
|
+
hint: string | undefined,
|
|
149
|
+
): string | undefined {
|
|
150
|
+
const needle = hint?.trim().toLowerCase();
|
|
151
|
+
if (!needle) return undefined;
|
|
152
|
+
const terms = needle.split(/\s+/).filter(Boolean);
|
|
153
|
+
let best: { id: string; score: number } | null = null;
|
|
154
|
+
for (const library of libraries) {
|
|
155
|
+
const haystack =
|
|
156
|
+
`${library.title ?? ""} ${library.description ?? ""}`.toLowerCase();
|
|
157
|
+
if (!haystack.trim()) continue;
|
|
158
|
+
let score = 0;
|
|
159
|
+
if (haystack.includes(needle)) score += 10;
|
|
160
|
+
for (const term of terms) {
|
|
161
|
+
if (haystack.includes(term)) score += 1;
|
|
162
|
+
}
|
|
163
|
+
if (score > 0 && (!best || score > best.score)) {
|
|
164
|
+
best = { id: library.id, score };
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return best?.id;
|
|
168
|
+
}
|
|
169
|
+
|
|
136
170
|
function isEmbeddedWindow() {
|
|
137
171
|
if (typeof window === "undefined") return false;
|
|
138
172
|
try {
|
|
@@ -154,6 +188,40 @@ function normalizeMediaType(value: unknown): PickerMediaType {
|
|
|
154
188
|
return value === "video" ? "video" : "image";
|
|
155
189
|
}
|
|
156
190
|
|
|
191
|
+
function normalizeBoolean(value: unknown): boolean | undefined {
|
|
192
|
+
if (typeof value === "boolean") return value;
|
|
193
|
+
if (typeof value !== "string") return undefined;
|
|
194
|
+
const normalized = value.trim().toLowerCase();
|
|
195
|
+
if (["1", "true", "yes", "on"].includes(normalized)) return true;
|
|
196
|
+
if (["", "0", "false", "no", "off"].includes(normalized)) return false;
|
|
197
|
+
return undefined;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function normalizeTier(value: unknown): ImageQualityTier | undefined {
|
|
201
|
+
return value === "auto" || value === "fast" || value === "best"
|
|
202
|
+
? value
|
|
203
|
+
: undefined;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function normalizeStyleStrength(value: unknown): StyleStrength | undefined {
|
|
207
|
+
return value === "subtle" || value === "balanced" || value === "strong"
|
|
208
|
+
? value
|
|
209
|
+
: undefined;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function normalizeCandidateRunIds(value: unknown): string[] | undefined {
|
|
213
|
+
if (value === null || value === undefined) return undefined;
|
|
214
|
+
const raw = Array.isArray(value)
|
|
215
|
+
? value
|
|
216
|
+
: typeof value === "string"
|
|
217
|
+
? value.split(",")
|
|
218
|
+
: [];
|
|
219
|
+
const ids = raw
|
|
220
|
+
.map((item) => (typeof item === "string" ? item.trim() : ""))
|
|
221
|
+
.filter(Boolean);
|
|
222
|
+
return ids;
|
|
223
|
+
}
|
|
224
|
+
|
|
157
225
|
function normalizeHostConfig(value: unknown): HostConfig {
|
|
158
226
|
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
159
227
|
const record = value as Record<string, unknown>;
|
|
@@ -166,17 +234,22 @@ function normalizeHostConfig(value: unknown): HostConfig {
|
|
|
166
234
|
query: typeof record.query === "string" ? record.query : undefined,
|
|
167
235
|
libraryId:
|
|
168
236
|
typeof record.libraryId === "string" ? record.libraryId : undefined,
|
|
237
|
+
libraryHint:
|
|
238
|
+
typeof record.libraryHint === "string" ? record.libraryHint : undefined,
|
|
169
239
|
aspectRatio:
|
|
170
240
|
typeof record.aspectRatio === "string" ? record.aspectRatio : undefined,
|
|
171
241
|
presetId: typeof record.presetId === "string" ? record.presetId : undefined,
|
|
172
242
|
count:
|
|
173
243
|
record.count === undefined ? undefined : normalizeCount(record.count),
|
|
244
|
+
tier: normalizeTier(record.tier),
|
|
245
|
+
styleStrength: normalizeStyleStrength(record.styleStrength),
|
|
246
|
+
includeLogo: normalizeBoolean(record.includeLogo),
|
|
247
|
+
callerAppId:
|
|
248
|
+
typeof record.callerAppId === "string" ? record.callerAppId : undefined,
|
|
249
|
+
candidateRunIds: normalizeCandidateRunIds(record.candidateRunIds),
|
|
174
250
|
};
|
|
175
251
|
if (Object.prototype.hasOwnProperty.call(record, "autoGenerate")) {
|
|
176
|
-
config.autoGenerate =
|
|
177
|
-
record.autoGenerate === true ||
|
|
178
|
-
record.autoGenerate === "true" ||
|
|
179
|
-
record.autoGenerate === "1";
|
|
252
|
+
config.autoGenerate = normalizeBoolean(record.autoGenerate) ?? false;
|
|
180
253
|
}
|
|
181
254
|
return config;
|
|
182
255
|
}
|
|
@@ -440,32 +513,53 @@ function blobToBase64(blob: Blob): Promise<string> {
|
|
|
440
513
|
});
|
|
441
514
|
}
|
|
442
515
|
|
|
516
|
+
const MCP_IMAGE_CONTENT_MAX_BYTES = 4 * 1024 * 1024;
|
|
517
|
+
|
|
518
|
+
function base64ByteLength(data: string) {
|
|
519
|
+
const padding = data.endsWith("==") ? 2 : data.endsWith("=") ? 1 : 0;
|
|
520
|
+
return Math.floor((data.length * 3) / 4) - padding;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
async function fetchImageContent(url: string, fallbackMimeType: string) {
|
|
524
|
+
const credentials =
|
|
525
|
+
typeof window !== "undefined" &&
|
|
526
|
+
new URL(url, window.location.href).origin === window.location.origin
|
|
527
|
+
? "same-origin"
|
|
528
|
+
: "omit";
|
|
529
|
+
const response = await fetch(url, { credentials });
|
|
530
|
+
if (!response.ok) return null;
|
|
531
|
+
const blob = await response.blob();
|
|
532
|
+
const detectedMimeType = blob.type.startsWith("image/")
|
|
533
|
+
? blob.type
|
|
534
|
+
: fallbackMimeType;
|
|
535
|
+
return {
|
|
536
|
+
type: "image",
|
|
537
|
+
data: await blobToBase64(blob),
|
|
538
|
+
mimeType: detectedMimeType,
|
|
539
|
+
};
|
|
540
|
+
}
|
|
541
|
+
|
|
443
542
|
async function imageContentForAsset(payload: ReturnType<typeof assetPayload>) {
|
|
444
543
|
const url = payload.url ?? payload.downloadUrl ?? payload.previewUrl;
|
|
445
544
|
const mimeType = payload.mimeType?.startsWith("image/")
|
|
446
545
|
? payload.mimeType
|
|
447
546
|
: "image/png";
|
|
448
547
|
if (!url || payload.mediaType !== "image") return null;
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
type: "image",
|
|
463
|
-
data: await blobToBase64(blob),
|
|
464
|
-
mimeType: detectedMimeType,
|
|
465
|
-
};
|
|
466
|
-
} catch {
|
|
467
|
-
return null;
|
|
548
|
+
const sources = uniqueSources([url, payload.thumbnailUrl]);
|
|
549
|
+
for (const source of sources) {
|
|
550
|
+
try {
|
|
551
|
+
const content = await fetchImageContent(source, mimeType);
|
|
552
|
+
if (
|
|
553
|
+
content &&
|
|
554
|
+
base64ByteLength(content.data) <= MCP_IMAGE_CONTENT_MAX_BYTES
|
|
555
|
+
) {
|
|
556
|
+
return content;
|
|
557
|
+
}
|
|
558
|
+
} catch {
|
|
559
|
+
// Try the next smaller source.
|
|
560
|
+
}
|
|
468
561
|
}
|
|
562
|
+
return null;
|
|
469
563
|
}
|
|
470
564
|
|
|
471
565
|
function notifyMcpHost(payload: ReturnType<typeof assetPayload>) {
|
|
@@ -490,16 +584,18 @@ function notifyMcpHost(payload: ReturnType<typeof assetPayload>) {
|
|
|
490
584
|
}) || false,
|
|
491
585
|
)
|
|
492
586
|
.catch(() => false)
|
|
493
|
-
.then(() =>
|
|
494
|
-
Promise.resolve(
|
|
587
|
+
.then((contextOk) => {
|
|
588
|
+
return Promise.resolve(
|
|
495
589
|
sendMcpAppHostMessage({
|
|
496
590
|
message,
|
|
497
591
|
context: JSON.stringify(context, null, 2),
|
|
498
592
|
content: chatContent,
|
|
499
593
|
structuredContent: context,
|
|
500
594
|
}) || false,
|
|
501
|
-
)
|
|
502
|
-
|
|
595
|
+
)
|
|
596
|
+
.catch(() => false)
|
|
597
|
+
.then((chatOk) => contextOk || chatOk);
|
|
598
|
+
});
|
|
503
599
|
});
|
|
504
600
|
}
|
|
505
601
|
|
|
@@ -619,6 +715,9 @@ function AssetOverlayImage({ asset }: { asset: Asset }) {
|
|
|
619
715
|
export default function AssetPicker() {
|
|
620
716
|
const [searchParams] = useSearchParams();
|
|
621
717
|
const searchParamsKey = searchParams.toString();
|
|
718
|
+
const mcpChatBridgeActive =
|
|
719
|
+
searchParams.get("__an_mcp_chat_bridge") === "1" ||
|
|
720
|
+
isEmbedMcpChatBridgeActive();
|
|
622
721
|
const urlHostConfig = useMemo(() => {
|
|
623
722
|
const params = new URLSearchParams(searchParamsKey);
|
|
624
723
|
return {
|
|
@@ -626,12 +725,20 @@ export default function AssetPicker() {
|
|
|
626
725
|
prompt: params.get("prompt") ?? undefined,
|
|
627
726
|
query: params.get("q") ?? undefined,
|
|
628
727
|
libraryId: params.get("libraryId") ?? undefined,
|
|
728
|
+
libraryHint: params.get("libraryHint") ?? undefined,
|
|
629
729
|
aspectRatio: params.get("aspectRatio") ?? undefined,
|
|
630
730
|
presetId: params.get("presetId") ?? undefined,
|
|
631
731
|
count: normalizeCount(params.get("count")),
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
732
|
+
tier: normalizeTier(params.get("tier")),
|
|
733
|
+
styleStrength: normalizeStyleStrength(params.get("styleStrength")),
|
|
734
|
+
includeLogo: normalizeBoolean(params.get("includeLogo")),
|
|
735
|
+
callerAppId: params.get("callerAppId") ?? undefined,
|
|
736
|
+
candidateRunIds: normalizeCandidateRunIds(
|
|
737
|
+
params.getAll("candidateRunIds").length > 0
|
|
738
|
+
? params.getAll("candidateRunIds")
|
|
739
|
+
: params.get("candidateRunIds"),
|
|
740
|
+
),
|
|
741
|
+
autoGenerate: normalizeBoolean(params.get("autoGenerate")) ?? false,
|
|
635
742
|
} satisfies HostConfig;
|
|
636
743
|
}, [searchParamsKey]);
|
|
637
744
|
const bridgeRef = useRef<EmbeddedAppBridge | null>(null);
|
|
@@ -655,6 +762,9 @@ export default function AssetPicker() {
|
|
|
655
762
|
useState<Library | null>(null);
|
|
656
763
|
const autoGenerateKeyRef = useRef<string | null>(null);
|
|
657
764
|
const autoCreateLibraryRef = useRef(false);
|
|
765
|
+
const [visibleCandidateRunIds, setVisibleCandidateRunIds] = useState<
|
|
766
|
+
string[]
|
|
767
|
+
>(() => hostConfig.candidateRunIds ?? []);
|
|
658
768
|
|
|
659
769
|
useEffect(() => {
|
|
660
770
|
setHostConfig((current) => ({ ...current, ...urlHostConfig }));
|
|
@@ -665,6 +775,7 @@ export default function AssetPicker() {
|
|
|
665
775
|
setAspectRatio(urlHostConfig.aspectRatio ?? "16:9");
|
|
666
776
|
setPresetId(urlHostConfig.presetId ?? "none");
|
|
667
777
|
setCount(urlHostConfig.count ?? 3);
|
|
778
|
+
setVisibleCandidateRunIds(urlHostConfig.candidateRunIds ?? []);
|
|
668
779
|
}, [urlHostConfig]);
|
|
669
780
|
|
|
670
781
|
const librariesQuery = useActionQuery("list-libraries", {
|
|
@@ -693,7 +804,10 @@ export default function AssetPicker() {
|
|
|
693
804
|
const firstLibraryId = displayLibraries[0]?.id;
|
|
694
805
|
if (!firstLibraryId) return;
|
|
695
806
|
if (!selectedLibraryId) {
|
|
696
|
-
setSelectedLibraryId(
|
|
807
|
+
setSelectedLibraryId(
|
|
808
|
+
matchLibraryByHint(displayLibraries, hostConfig.libraryHint) ??
|
|
809
|
+
firstLibraryId,
|
|
810
|
+
);
|
|
697
811
|
return;
|
|
698
812
|
}
|
|
699
813
|
if (!libraryListReady) return;
|
|
@@ -703,7 +817,12 @@ export default function AssetPicker() {
|
|
|
703
817
|
if (!selectedLibraryExists) {
|
|
704
818
|
setSelectedLibraryId(firstLibraryId);
|
|
705
819
|
}
|
|
706
|
-
}, [
|
|
820
|
+
}, [
|
|
821
|
+
displayLibraries,
|
|
822
|
+
hostConfig.libraryHint,
|
|
823
|
+
libraryListReady,
|
|
824
|
+
selectedLibraryId,
|
|
825
|
+
]);
|
|
707
826
|
|
|
708
827
|
const { data: config } = useActionQuery(
|
|
709
828
|
"get-image-generation-config",
|
|
@@ -742,9 +861,6 @@ export default function AssetPicker() {
|
|
|
742
861
|
Boolean(selectedLibraryId) &&
|
|
743
862
|
(presetsLoading || presetsFetching || presetsPending || !selectedPreset);
|
|
744
863
|
const mediaLabel = mediaType === "video" ? "video" : "image";
|
|
745
|
-
const [visibleCandidateRunIds, setVisibleCandidateRunIds] = useState<
|
|
746
|
-
string[]
|
|
747
|
-
>([]);
|
|
748
864
|
const generateBatch = useActionMutation(
|
|
749
865
|
"generate-image-batch" as any,
|
|
750
866
|
{
|
|
@@ -876,12 +992,28 @@ export default function AssetPicker() {
|
|
|
876
992
|
[],
|
|
877
993
|
);
|
|
878
994
|
|
|
995
|
+
const postEmbeddedSelectionMessage = useCallback(
|
|
996
|
+
(
|
|
997
|
+
name: "chooseAsset" | "chooseImage",
|
|
998
|
+
payload: ReturnType<typeof assetPayload>,
|
|
999
|
+
) => {
|
|
1000
|
+
try {
|
|
1001
|
+
return bridgeRef.current?.postMessage(name, payload) ?? false;
|
|
1002
|
+
} catch {
|
|
1003
|
+
return false;
|
|
1004
|
+
}
|
|
1005
|
+
},
|
|
1006
|
+
[],
|
|
1007
|
+
);
|
|
1008
|
+
|
|
879
1009
|
const chooseAsset = (asset: Asset) => {
|
|
880
1010
|
const payload = assetPayload(asset, mediaType);
|
|
881
1011
|
if (embedded) {
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
1012
|
+
if (!mcpChatBridgeActive) {
|
|
1013
|
+
postEmbeddedSelectionMessage("chooseAsset", payload);
|
|
1014
|
+
if (payload.mediaType === "image") {
|
|
1015
|
+
postEmbeddedSelectionMessage("chooseImage", payload);
|
|
1016
|
+
}
|
|
885
1017
|
}
|
|
886
1018
|
void notifyMcpHost(payload).then((ok) => {
|
|
887
1019
|
if (ok) {
|
|
@@ -912,6 +1044,9 @@ export default function AssetPicker() {
|
|
|
912
1044
|
setQuery("");
|
|
913
1045
|
},
|
|
914
1046
|
onError: (error: Error) => {
|
|
1047
|
+
// Allow the auto-create effect to retry after a transient failure;
|
|
1048
|
+
// otherwise the picker stays stuck on "Preparing..." until reload.
|
|
1049
|
+
autoCreateLibraryRef.current = false;
|
|
915
1050
|
toast.error(error.message || "Could not prepare an image library");
|
|
916
1051
|
},
|
|
917
1052
|
} as any,
|
|
@@ -931,12 +1066,20 @@ export default function AssetPicker() {
|
|
|
931
1066
|
imageSize: selectedPreset?.imageSize || "2K",
|
|
932
1067
|
dismissible: false,
|
|
933
1068
|
})),
|
|
1069
|
+
tier: hostConfig.tier,
|
|
1070
|
+
styleStrength: hostConfig.styleStrength ?? "balanced",
|
|
1071
|
+
includeLogo: hostConfig.includeLogo ?? false,
|
|
934
1072
|
source: "ui",
|
|
1073
|
+
callerAppId: hostConfig.callerAppId,
|
|
935
1074
|
} as any);
|
|
936
1075
|
}, [
|
|
937
1076
|
count,
|
|
938
1077
|
effectiveAspectRatio,
|
|
939
1078
|
generateBatch,
|
|
1079
|
+
hostConfig.callerAppId,
|
|
1080
|
+
hostConfig.includeLogo,
|
|
1081
|
+
hostConfig.styleStrength,
|
|
1082
|
+
hostConfig.tier,
|
|
940
1083
|
prompt,
|
|
941
1084
|
setVisibleCandidateRunIds,
|
|
942
1085
|
selectedLibraryId,
|
|
@@ -945,8 +1088,35 @@ export default function AssetPicker() {
|
|
|
945
1088
|
]);
|
|
946
1089
|
|
|
947
1090
|
useEffect(() => {
|
|
1091
|
+
const hostCandidateFilterMatches =
|
|
1092
|
+
hostConfig.candidateRunIds?.length &&
|
|
1093
|
+
normalizeMediaType(hostConfig.mediaType) === mediaType &&
|
|
1094
|
+
(hostConfig.libraryId ?? "") === (selectedLibraryId ?? "") &&
|
|
1095
|
+
(hostConfig.prompt ?? "") === prompt &&
|
|
1096
|
+
(hostConfig.aspectRatio ?? "16:9") === aspectRatio &&
|
|
1097
|
+
(hostConfig.presetId ?? "none") === presetId &&
|
|
1098
|
+
(hostConfig.count ?? 3) === count;
|
|
1099
|
+
if (hostCandidateFilterMatches) return;
|
|
948
1100
|
setVisibleCandidateRunIds([]);
|
|
949
|
-
}, [
|
|
1101
|
+
}, [
|
|
1102
|
+
aspectRatio,
|
|
1103
|
+
count,
|
|
1104
|
+
hostConfig.aspectRatio,
|
|
1105
|
+
hostConfig.candidateRunIds,
|
|
1106
|
+
hostConfig.count,
|
|
1107
|
+
hostConfig.callerAppId,
|
|
1108
|
+
hostConfig.includeLogo,
|
|
1109
|
+
hostConfig.libraryId,
|
|
1110
|
+
hostConfig.mediaType,
|
|
1111
|
+
hostConfig.presetId,
|
|
1112
|
+
hostConfig.prompt,
|
|
1113
|
+
hostConfig.styleStrength,
|
|
1114
|
+
hostConfig.tier,
|
|
1115
|
+
mediaType,
|
|
1116
|
+
presetId,
|
|
1117
|
+
prompt,
|
|
1118
|
+
selectedLibraryId,
|
|
1119
|
+
]);
|
|
950
1120
|
|
|
951
1121
|
useEffect(() => {
|
|
952
1122
|
const bridge = createEmbeddedAppBridge({
|
|
@@ -961,6 +1131,9 @@ export default function AssetPicker() {
|
|
|
961
1131
|
if (next.aspectRatio !== undefined) setAspectRatio(next.aspectRatio);
|
|
962
1132
|
if (next.presetId !== undefined) setPresetId(next.presetId || "none");
|
|
963
1133
|
if (next.count !== undefined) setCount(next.count);
|
|
1134
|
+
if (next.candidateRunIds !== undefined) {
|
|
1135
|
+
setVisibleCandidateRunIds(next.candidateRunIds);
|
|
1136
|
+
}
|
|
964
1137
|
},
|
|
965
1138
|
});
|
|
966
1139
|
bridgeRef.current = bridge;
|
|
@@ -1016,6 +1189,31 @@ export default function AssetPicker() {
|
|
|
1016
1189
|
usingStarterLibrary;
|
|
1017
1190
|
const preparingGenerationLibrary =
|
|
1018
1191
|
needsGenerationLibrary && createPickerLibrary.isPending;
|
|
1192
|
+
const waitingForLibraries = mediaType === "image" && !libraryListReady;
|
|
1193
|
+
const preparingAutoLibrary =
|
|
1194
|
+
mediaType === "image" &&
|
|
1195
|
+
Boolean(prompt.trim()) &&
|
|
1196
|
+
(waitingForLibraries ||
|
|
1197
|
+
needsGenerationLibrary ||
|
|
1198
|
+
preparingGenerationLibrary);
|
|
1199
|
+
const generationButtonLabel = generateBatch.isPending
|
|
1200
|
+
? "Generating..."
|
|
1201
|
+
: waitingForRequestedPreset
|
|
1202
|
+
? "Loading preset..."
|
|
1203
|
+
: setupNeeded
|
|
1204
|
+
? "Setup needed"
|
|
1205
|
+
: preparingAutoLibrary
|
|
1206
|
+
? "Preparing..."
|
|
1207
|
+
: "Generate";
|
|
1208
|
+
const generationStatus = generateBatch.isPending
|
|
1209
|
+
? "Generating candidates..."
|
|
1210
|
+
: preparingAutoLibrary
|
|
1211
|
+
? waitingForLibraries
|
|
1212
|
+
? "Checking image libraries..."
|
|
1213
|
+
: "Preparing an image library..."
|
|
1214
|
+
: waitingForRequestedPreset
|
|
1215
|
+
? "Loading the requested preset..."
|
|
1216
|
+
: null;
|
|
1019
1217
|
|
|
1020
1218
|
useEffect(() => {
|
|
1021
1219
|
if (!selectedPreset?.aspectRatio) return;
|
|
@@ -1213,9 +1411,14 @@ export default function AssetPicker() {
|
|
|
1213
1411
|
disabled={!canGenerate}
|
|
1214
1412
|
onClick={runGenerate}
|
|
1215
1413
|
>
|
|
1216
|
-
{
|
|
1414
|
+
{generationButtonLabel}
|
|
1217
1415
|
</Button>
|
|
1218
1416
|
</div>
|
|
1417
|
+
{generationStatus && (
|
|
1418
|
+
<div className="-mt-1 px-3 pb-2 text-[11px] leading-4 text-muted-foreground">
|
|
1419
|
+
{generationStatus}
|
|
1420
|
+
</div>
|
|
1421
|
+
)}
|
|
1219
1422
|
</div>
|
|
1220
1423
|
) : null}
|
|
1221
1424
|
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* Options:
|
|
11
11
|
* --view View name to navigate to
|
|
12
12
|
* --path URL path to navigate to
|
|
13
|
+
* --threadId Chat thread ID to open on the chat route
|
|
13
14
|
*/
|
|
14
15
|
|
|
15
16
|
import { defineAction } from "@agent-native/core/action";
|
|
@@ -22,6 +23,7 @@ export default defineAction({
|
|
|
22
23
|
schema: z.object({
|
|
23
24
|
view: z.string().optional().describe("View name to navigate to"),
|
|
24
25
|
path: z.string().optional().describe("URL path to navigate to"),
|
|
26
|
+
threadId: z.string().optional().describe("Chat thread ID to open"),
|
|
25
27
|
}),
|
|
26
28
|
http: false,
|
|
27
29
|
run: async (args) => {
|
|
@@ -31,6 +33,7 @@ export default defineAction({
|
|
|
31
33
|
const nav: Record<string, string> = {};
|
|
32
34
|
if (args.view) nav.view = args.view;
|
|
33
35
|
if (args.path) nav.path = args.path;
|
|
36
|
+
if (args.threadId) nav.threadId = args.threadId;
|
|
34
37
|
nav._writeId = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
35
38
|
await writeAppState("navigate", nav);
|
|
36
39
|
return `Navigating to ${args.view || args.path}`;
|
|
@@ -35,6 +35,7 @@ const SIDEBAR_COLLAPSE_KEY = "chat.sidebar.collapsed";
|
|
|
35
35
|
function routeOwnsToolbar(pathname: string): boolean {
|
|
36
36
|
return (
|
|
37
37
|
pathname === "/" ||
|
|
38
|
+
pathname.startsWith("/chat/") ||
|
|
38
39
|
pathname === "/database" ||
|
|
39
40
|
pathname.startsWith("/extensions")
|
|
40
41
|
);
|
|
@@ -45,13 +46,17 @@ export function Layout({ children }: LayoutProps) {
|
|
|
45
46
|
const navigate = useNavigate();
|
|
46
47
|
const [mobileSidebarOpen, setMobileSidebarOpen] = useState(false);
|
|
47
48
|
const [sidebarCollapsed, setSidebarCollapsed] = useState(true);
|
|
48
|
-
const isChatRoute =
|
|
49
|
+
const isChatRoute =
|
|
50
|
+
location.pathname === "/" || location.pathname.startsWith("/chat/");
|
|
49
51
|
const chatHomeHandoffActive = useAgentChatHomeHandoff({
|
|
50
52
|
storageKey: "chat",
|
|
51
53
|
activePath: location.pathname,
|
|
52
54
|
enabled: !isChatRoute,
|
|
53
55
|
});
|
|
54
|
-
useAgentChatHomeHandoffLinks({
|
|
56
|
+
useAgentChatHomeHandoffLinks({
|
|
57
|
+
storageKey: "chat",
|
|
58
|
+
isChatPath: (pathname) => pathname === "/" || pathname.startsWith("/chat/"),
|
|
59
|
+
});
|
|
55
60
|
|
|
56
61
|
useEffect(() => {
|
|
57
62
|
setMobileSidebarOpen(false);
|