@nextclaw/ui 0.15.0 → 0.15.1
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/CHANGELOG.md +19 -0
- package/dist/assets/{api-DLtsiBKm.js → api-b1R9VdeH.js} +1 -1
- package/dist/assets/{appearance-settings-page-CtkeYXux.js → appearance-settings-page-D2O9pch9.js} +1 -1
- package/dist/assets/{book-open-BQwWBDKb.js → book-open-B77lQ2Tg.js} +1 -1
- package/dist/assets/{channels-list-page-Cv8o9Hxg.js → channels-list-page-DcsIQc6A.js} +2 -2
- package/dist/assets/chat-page-CZ7WxPir.js +1 -0
- package/dist/assets/{config-split-page-EidRBpP2.js → config-split-page-BWQ6_Raq.js} +1 -1
- package/dist/assets/{confirm-dialog-iIJFFp2T.js → confirm-dialog-C-NXSioX.js} +1 -1
- package/dist/assets/desktop-update-config-usHpXugP.js +1 -0
- package/dist/assets/{dist-CBxWOazz.js → dist-YtoqU-AT.js} +1 -1
- package/dist/assets/{dist-D2eZC3OR.js → dist-jdGmbb6w.js} +1 -1
- package/dist/assets/doc-browser-B_Tf9n70.js +1 -0
- package/dist/assets/{doc-browser-B76XOXmW.js → doc-browser-BdZPFOVa.js} +1 -1
- package/dist/assets/{doc-browser-context-Sv-044Cl.js → doc-browser-context-DwokemUn.js} +1 -1
- package/dist/assets/{es2015-DhQo_oS6.js → es2015-bNpL2LKy.js} +1 -1
- package/dist/assets/{external-link-CM5dnUI6.js → external-link-BM6fk8q-.js} +1 -1
- package/dist/assets/{i18n-provider-di1waTGX.js → i18n-provider-CntWolRA.js} +1 -1
- package/dist/assets/index-BIcEw90u.js +104 -0
- package/dist/assets/index-BiNSTKTU.css +1 -0
- package/dist/assets/loader-circle-MBgNSVIH.js +1 -0
- package/dist/assets/{mcp-marketplace-page-C_MVQUbT.js → mcp-marketplace-page-CLmUstLN.js} +1 -1
- package/dist/assets/mcp-marketplace-page-rZkmWPMy.js +1 -0
- package/dist/assets/{model-config-page-Cdr9P6TQ.js → model-config-page-Di0ySJD2.js} +1 -1
- package/dist/assets/plus-vDqW5Kxp.js +1 -0
- package/dist/assets/{provider-scoped-model-input-g1WkGiJ8.js → provider-scoped-model-input-Cm_a4GHf.js} +1 -1
- package/dist/assets/providers-config-page-BAf2W0MV.js +1 -0
- package/dist/assets/{react-DjpzoZXB.js → react-BN8uzPnp.js} +1 -1
- package/dist/assets/{refresh-cw-DaTv3zxE.js → refresh-cw-BpVcFPmJ.js} +1 -1
- package/dist/assets/remote-CLQ0IBM8.js +1 -0
- package/dist/assets/rotate-cw-CvNBv7_w.js +1 -0
- package/dist/assets/{runtime-config-page-CfTqh1nv.js → runtime-config-page-CflvvoDc.js} +1 -1
- package/dist/assets/{save-BZof1uie.js → save-BpFW5fpk.js} +1 -1
- package/dist/assets/{search-MxAarS_g.js → search-D0uO7enf.js} +1 -1
- package/dist/assets/{search-config-page-Dm8daYny.js → search-config-page-D0klFUXd.js} +1 -1
- package/dist/assets/{secrets-config-page-D8AbcR05.js → secrets-config-page-dVZ631Wp.js} +1 -1
- package/dist/assets/security-config-C5gnlkuj.js +1 -0
- package/dist/assets/{select-D7GcNP0z.js → select-Bh-CrA4p.js} +1 -1
- package/dist/assets/{setting-row-CuozlnJd.js → setting-row-BJfCHRcb.js} +1 -1
- package/dist/assets/settings-2-Blzi1vr6.js +1 -0
- package/dist/assets/{side-dock-BcLau5S_.js → side-dock-ZdaSrQVB.js} +1 -1
- package/dist/assets/{skeleton-shfmXuYp.js → skeleton-BLlEzebZ.js} +1 -1
- package/dist/assets/{switch-C1BWMU5c.js → switch-CFLJFNSd.js} +1 -1
- package/dist/assets/{tag-chip-DCUrQUCP.js → tag-chip-CzGihiQx.js} +1 -1
- package/dist/assets/{tooltip-DfAHHA76.js → tooltip-CdcrBjrC.js} +1 -1
- package/dist/assets/{use-config-DAdbT5QJ.js → use-config-CVPgsBmU.js} +1 -1
- package/dist/assets/{use-confirm-dialog-eWcJ6V-s.js → use-confirm-dialog-CSeBk9SF.js} +1 -1
- package/dist/assets/x-BF2KQ--V.js +1 -0
- package/dist/index.html +26 -26
- package/package.json +9 -9
- package/src/features/agents/components/__tests__/agents-page.test.tsx +135 -2
- package/src/features/agents/components/agent-advanced-config-fields.tsx +70 -0
- package/src/features/agents/components/agent-details-dialog.tsx +398 -0
- package/src/features/agents/components/agent-dialogs.tsx +20 -47
- package/src/features/agents/components/agents-page.tsx +76 -10
- package/src/features/agents/types/agent-form.types.ts +21 -0
- package/src/features/agents/utils/agent-form.utils.ts +39 -0
- package/src/features/chat/components/conversation/__tests__/chat-conversation-header-section.test.tsx +132 -1
- package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +8 -0
- package/src/features/chat/components/conversation/chat-conversation-header.tsx +7 -3
- package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +18 -3
- package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +1 -1
- package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-agent-runtime.test.tsx +23 -1
- package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-session-list-view.test.tsx +74 -0
- package/src/features/chat/features/ncp/hooks/use-ncp-session-list-view.ts +3 -2
- package/src/features/chat/features/session/components/session-header/chat-session-title-switcher.tsx +294 -0
- package/src/features/chat/features/session/utils/__tests__/chat-session-display.utils.test.ts +15 -1
- package/src/features/chat/features/session/utils/__tests__/ncp-session-adapter.utils.test.ts +24 -4
- package/src/features/chat/features/session/utils/chat-session-display.utils.ts +3 -0
- package/src/features/chat/features/session/utils/ncp-session-adapter.utils.ts +1 -0
- package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.test.tsx +34 -0
- package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +1 -7
- package/src/features/chat/features/workspace/utils/__tests__/chat-workspace-panel-view-model.utils.test.ts +46 -0
- package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +47 -0
- package/src/features/chat/managers/chat-thread.manager.ts +8 -2
- package/src/features/system-status/components/__tests__/desktop-update-config.test.tsx +97 -5
- package/src/features/system-status/components/desktop-update-config.tsx +91 -2
- package/src/features/system-status/utils/update-release-notes.utils.ts +94 -0
- package/src/shared/lib/api/ncp-session.types.ts +1 -1
- package/src/shared/lib/api/types.ts +12 -3
- package/src/shared/lib/i18n/locales/en-US/agents.json +41 -0
- package/src/shared/lib/i18n/locales/en-US/chat.json +3 -0
- package/src/shared/lib/i18n/locales/en-US/desktop-update.json +8 -0
- package/src/shared/lib/i18n/locales/zh-CN/agents.json +41 -0
- package/src/shared/lib/i18n/locales/zh-CN/chat.json +3 -0
- package/src/shared/lib/i18n/locales/zh-CN/desktop-update.json +8 -0
- package/dist/assets/chat-page-BtA7H1Ea.js +0 -1
- package/dist/assets/desktop-update-config-C6n_TB1r.js +0 -1
- package/dist/assets/doc-browser-DBnj9UMB.js +0 -1
- package/dist/assets/ellipsis-2uzhypw5.js +0 -1
- package/dist/assets/index-CBRP-7-a.css +0 -1
- package/dist/assets/index-Z1fYpbNd.js +0 -104
- package/dist/assets/loader-circle-BjmN5DeJ.js +0 -1
- package/dist/assets/mcp-marketplace-page-CoZIiD6G.js +0 -1
- package/dist/assets/plus-3ztRZD_l.js +0 -1
- package/dist/assets/providers-config-page-CWlO7J4A.js +0 -1
- package/dist/assets/remote-CKWZfVVB.js +0 -1
- package/dist/assets/rotate-cw-CHcaRUDz.js +0 -1
- package/dist/assets/security-config-Ds0LsJmo.js +0 -1
- package/dist/assets/x-DqL1mYLC.js +0 -1
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
import type { ReactNode } from "react";
|
|
2
|
+
import type { AgentProfileView, ConfigView } from "@/shared/lib/api";
|
|
3
|
+
import { normalizeSessionType, resolveSessionTypeLabel } from "@/features/chat";
|
|
4
|
+
import { AgentAvatar } from "@/shared/components/common/agent-avatar";
|
|
5
|
+
import { Button } from "@/shared/components/ui/button";
|
|
6
|
+
import {
|
|
7
|
+
Dialog,
|
|
8
|
+
DialogContent,
|
|
9
|
+
DialogDescription,
|
|
10
|
+
DialogFooter,
|
|
11
|
+
DialogHeader,
|
|
12
|
+
DialogTitle,
|
|
13
|
+
} from "@/shared/components/ui/dialog";
|
|
14
|
+
import { t } from "@/shared/lib/i18n";
|
|
15
|
+
import { cn } from "@/shared/lib/utils";
|
|
16
|
+
import { Bot, Brain, Gauge, Pencil, Sparkles, type LucideIcon } from "lucide-react";
|
|
17
|
+
|
|
18
|
+
type AgentDefaultsView = ConfigView["agents"]["defaults"];
|
|
19
|
+
|
|
20
|
+
type AgentDetailsDialogProps = {
|
|
21
|
+
agent: AgentProfileView | null;
|
|
22
|
+
defaults?: AgentDefaultsView;
|
|
23
|
+
runtimeOptions: { value: string; label: string }[];
|
|
24
|
+
defaultRuntime: string;
|
|
25
|
+
defaultRuntimeLabel: string;
|
|
26
|
+
onOpenChange: (open: boolean) => void;
|
|
27
|
+
onEdit: (agent: AgentProfileView) => void;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const numberFormatter = new Intl.NumberFormat();
|
|
31
|
+
|
|
32
|
+
export function AgentDetailsDialog({
|
|
33
|
+
agent,
|
|
34
|
+
defaults,
|
|
35
|
+
runtimeOptions,
|
|
36
|
+
defaultRuntime,
|
|
37
|
+
defaultRuntimeLabel,
|
|
38
|
+
onOpenChange,
|
|
39
|
+
onEdit,
|
|
40
|
+
}: AgentDetailsDialogProps) {
|
|
41
|
+
if (!agent) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const model = resolveTextValue(agent.model, defaults?.model);
|
|
46
|
+
const runtime = resolveRuntimeValue({
|
|
47
|
+
agent,
|
|
48
|
+
defaults,
|
|
49
|
+
runtimeOptions,
|
|
50
|
+
defaultRuntime,
|
|
51
|
+
defaultRuntimeLabel,
|
|
52
|
+
});
|
|
53
|
+
const contextTokens = resolveNumberValue(
|
|
54
|
+
agent.contextTokens,
|
|
55
|
+
defaults?.contextTokens ?? 200000,
|
|
56
|
+
);
|
|
57
|
+
const reservedContextTokens = resolveNumberValue(
|
|
58
|
+
agent.reservedContextTokens,
|
|
59
|
+
defaults?.reservedContextTokens,
|
|
60
|
+
);
|
|
61
|
+
const maxToolIterations = resolveNumberValue(
|
|
62
|
+
agent.maxToolIterations,
|
|
63
|
+
defaults?.maxToolIterations ?? 1000,
|
|
64
|
+
);
|
|
65
|
+
const thinkingDefault = resolveTextValue(
|
|
66
|
+
agent.thinkingDefault,
|
|
67
|
+
defaults?.thinkingDefault ?? "off",
|
|
68
|
+
);
|
|
69
|
+
const runtimeConfig = resolveRecordValue(
|
|
70
|
+
agent.runtimeConfig ?? agent.engineConfig,
|
|
71
|
+
defaults?.engineConfig,
|
|
72
|
+
);
|
|
73
|
+
const models = resolveRecordValue(agent.models, defaults?.models);
|
|
74
|
+
|
|
75
|
+
return (
|
|
76
|
+
<Dialog open={agent !== null} onOpenChange={onOpenChange}>
|
|
77
|
+
<DialogContent className="flex max-h-[calc(100vh-2rem)] flex-col overflow-hidden border-none bg-white p-0 sm:max-h-[720px] sm:max-w-2xl">
|
|
78
|
+
<div className="shrink-0 px-5 pb-3 pt-5">
|
|
79
|
+
<DialogHeader className="text-left">
|
|
80
|
+
<div className="flex min-w-0 items-center gap-3">
|
|
81
|
+
<AgentAvatar
|
|
82
|
+
agentId={agent.id}
|
|
83
|
+
displayName={agent.displayName}
|
|
84
|
+
avatarUrl={agent.avatarUrl}
|
|
85
|
+
className="h-10 w-10 shrink-0"
|
|
86
|
+
/>
|
|
87
|
+
<div className="min-w-0">
|
|
88
|
+
<DialogTitle className="truncate text-base">
|
|
89
|
+
{agent.displayName?.trim() || agent.id}
|
|
90
|
+
</DialogTitle>
|
|
91
|
+
<DialogDescription className="truncate text-xs">
|
|
92
|
+
@{agent.id}
|
|
93
|
+
</DialogDescription>
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
96
|
+
</DialogHeader>
|
|
97
|
+
</div>
|
|
98
|
+
<div className="min-h-0 flex-1 space-y-4 overflow-y-auto overscroll-contain px-5 py-3">
|
|
99
|
+
<DetailSection
|
|
100
|
+
icon={Bot}
|
|
101
|
+
title={t("agentsDetailsIdentitySection")}
|
|
102
|
+
>
|
|
103
|
+
<DetailItem
|
|
104
|
+
label={t("agentsDetailsFieldDisplayName")}
|
|
105
|
+
value={agent.displayName?.trim() || agent.id}
|
|
106
|
+
/>
|
|
107
|
+
<DetailItem
|
|
108
|
+
label={t("agentsDetailsFieldId")}
|
|
109
|
+
value={agent.id}
|
|
110
|
+
mono
|
|
111
|
+
/>
|
|
112
|
+
<DetailItem
|
|
113
|
+
label={t("agentsDetailsFieldDescription")}
|
|
114
|
+
value={agent.description?.trim() || t("agentsDetailsEmptyValue")}
|
|
115
|
+
wide
|
|
116
|
+
/>
|
|
117
|
+
<DetailItem
|
|
118
|
+
label={t("agentsDetailsFieldWorkspace")}
|
|
119
|
+
value={agent.workspace ?? t("agentsDetailsUnsetValue")}
|
|
120
|
+
mono
|
|
121
|
+
wide
|
|
122
|
+
/>
|
|
123
|
+
</DetailSection>
|
|
124
|
+
|
|
125
|
+
<DetailSection
|
|
126
|
+
icon={Sparkles}
|
|
127
|
+
title={t("agentsDetailsRuntimeSection")}
|
|
128
|
+
>
|
|
129
|
+
<DetailItem
|
|
130
|
+
label={t("agentsDetailsFieldModel")}
|
|
131
|
+
value={model.value}
|
|
132
|
+
source={model.source}
|
|
133
|
+
/>
|
|
134
|
+
<DetailItem
|
|
135
|
+
label={t("agentsDetailsFieldRuntime")}
|
|
136
|
+
value={runtime.value}
|
|
137
|
+
source={runtime.source}
|
|
138
|
+
/>
|
|
139
|
+
<DetailItem
|
|
140
|
+
label={t("agentsDetailsFieldRuntimeConfig")}
|
|
141
|
+
value={runtimeConfig.value}
|
|
142
|
+
source={runtimeConfig.source}
|
|
143
|
+
mono
|
|
144
|
+
prewrap
|
|
145
|
+
/>
|
|
146
|
+
</DetailSection>
|
|
147
|
+
|
|
148
|
+
<DetailSection
|
|
149
|
+
icon={Gauge}
|
|
150
|
+
title={t("agentsDetailsContextSection")}
|
|
151
|
+
>
|
|
152
|
+
<DetailItem
|
|
153
|
+
label={t("agentsDetailsFieldContextTokens")}
|
|
154
|
+
value={contextTokens.value}
|
|
155
|
+
source={contextTokens.source}
|
|
156
|
+
/>
|
|
157
|
+
<DetailItem
|
|
158
|
+
label={t("agentsDetailsFieldReservedContextTokens")}
|
|
159
|
+
value={reservedContextTokens.value}
|
|
160
|
+
source={reservedContextTokens.source}
|
|
161
|
+
/>
|
|
162
|
+
<DetailItem
|
|
163
|
+
label={t("agentsDetailsFieldMaxToolIterations")}
|
|
164
|
+
value={maxToolIterations.value}
|
|
165
|
+
source={maxToolIterations.source}
|
|
166
|
+
/>
|
|
167
|
+
</DetailSection>
|
|
168
|
+
|
|
169
|
+
<DetailSection
|
|
170
|
+
icon={Brain}
|
|
171
|
+
title={t("agentsDetailsBehaviorSection")}
|
|
172
|
+
>
|
|
173
|
+
<DetailItem
|
|
174
|
+
label={t("agentsDetailsFieldThinkingDefault")}
|
|
175
|
+
value={thinkingDefault.value}
|
|
176
|
+
source={thinkingDefault.source}
|
|
177
|
+
/>
|
|
178
|
+
<DetailItem
|
|
179
|
+
label={t("agentsDetailsFieldModelOverrides")}
|
|
180
|
+
value={models.value}
|
|
181
|
+
source={models.source}
|
|
182
|
+
mono
|
|
183
|
+
prewrap
|
|
184
|
+
/>
|
|
185
|
+
<DetailItem
|
|
186
|
+
label={t("agentsDetailsFieldAvatar")}
|
|
187
|
+
value={agent.avatar ?? t("agentsDetailsUnsetValue")}
|
|
188
|
+
mono
|
|
189
|
+
/>
|
|
190
|
+
</DetailSection>
|
|
191
|
+
</div>
|
|
192
|
+
<DialogFooter className="shrink-0 px-5 pb-4 pt-2">
|
|
193
|
+
<Button
|
|
194
|
+
type="button"
|
|
195
|
+
variant="ghost"
|
|
196
|
+
onClick={() => onOpenChange(false)}
|
|
197
|
+
>
|
|
198
|
+
{t("cancel")}
|
|
199
|
+
</Button>
|
|
200
|
+
<Button
|
|
201
|
+
type="button"
|
|
202
|
+
variant="primary"
|
|
203
|
+
className="px-4"
|
|
204
|
+
onClick={() => onEdit(agent)}
|
|
205
|
+
>
|
|
206
|
+
<Pencil className="mr-2 h-4 w-4" />
|
|
207
|
+
{t("agentsDetailsEditAction")}
|
|
208
|
+
</Button>
|
|
209
|
+
</DialogFooter>
|
|
210
|
+
</DialogContent>
|
|
211
|
+
</Dialog>
|
|
212
|
+
);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
type DetailSource = "default" | "override";
|
|
216
|
+
|
|
217
|
+
type ResolvedValue = {
|
|
218
|
+
value: string;
|
|
219
|
+
source: DetailSource;
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
function resolveTextValue(
|
|
223
|
+
agentValue: string | undefined,
|
|
224
|
+
defaultValue: string | undefined,
|
|
225
|
+
): ResolvedValue {
|
|
226
|
+
const normalizedAgentValue = agentValue?.trim();
|
|
227
|
+
if (normalizedAgentValue) {
|
|
228
|
+
return { value: normalizedAgentValue, source: "override" };
|
|
229
|
+
}
|
|
230
|
+
return {
|
|
231
|
+
value: defaultValue?.trim() || t("agentsDetailsUnsetValue"),
|
|
232
|
+
source: "default",
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function resolveNumberValue(
|
|
237
|
+
agentValue: number | undefined,
|
|
238
|
+
defaultValue: number | undefined,
|
|
239
|
+
): ResolvedValue {
|
|
240
|
+
if (typeof agentValue === "number") {
|
|
241
|
+
return { value: numberFormatter.format(agentValue), source: "override" };
|
|
242
|
+
}
|
|
243
|
+
return {
|
|
244
|
+
value:
|
|
245
|
+
typeof defaultValue === "number"
|
|
246
|
+
? numberFormatter.format(defaultValue)
|
|
247
|
+
: t("agentsDetailsUnsetValue"),
|
|
248
|
+
source: "default",
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
function resolveRuntimeValue(params: {
|
|
253
|
+
agent: AgentProfileView;
|
|
254
|
+
defaults?: AgentDefaultsView;
|
|
255
|
+
runtimeOptions: { value: string; label: string }[];
|
|
256
|
+
defaultRuntime: string;
|
|
257
|
+
defaultRuntimeLabel: string;
|
|
258
|
+
}): ResolvedValue {
|
|
259
|
+
const { agent, defaults, runtimeOptions, defaultRuntime, defaultRuntimeLabel } =
|
|
260
|
+
params;
|
|
261
|
+
const runtime = agent.runtime?.trim() || agent.engine?.trim();
|
|
262
|
+
if (runtime) {
|
|
263
|
+
return {
|
|
264
|
+
value: resolveRuntimeLabel(runtime, runtimeOptions, defaultRuntimeLabel),
|
|
265
|
+
source: "override",
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
const inheritedRuntime = defaults?.engine?.trim() || defaultRuntime;
|
|
269
|
+
return {
|
|
270
|
+
value: inheritedRuntime
|
|
271
|
+
? resolveRuntimeLabel(inheritedRuntime, runtimeOptions, defaultRuntimeLabel)
|
|
272
|
+
: defaultRuntimeLabel,
|
|
273
|
+
source: "default",
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function resolveRuntimeLabel(
|
|
278
|
+
value: string,
|
|
279
|
+
runtimeOptions: { value: string; label: string }[],
|
|
280
|
+
defaultRuntimeLabel: string,
|
|
281
|
+
): string {
|
|
282
|
+
const normalized = normalizeSessionType(value);
|
|
283
|
+
if (!normalized) {
|
|
284
|
+
return defaultRuntimeLabel;
|
|
285
|
+
}
|
|
286
|
+
return (
|
|
287
|
+
runtimeOptions.find((option) => option.value === normalized)?.label ??
|
|
288
|
+
resolveSessionTypeLabel(normalized)
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function resolveRecordValue(
|
|
293
|
+
agentValue: Record<string, unknown> | undefined,
|
|
294
|
+
defaultValue: Record<string, unknown> | undefined,
|
|
295
|
+
): ResolvedValue {
|
|
296
|
+
if (isNonEmptyRecord(agentValue)) {
|
|
297
|
+
return { value: JSON.stringify(agentValue, null, 2), source: "override" };
|
|
298
|
+
}
|
|
299
|
+
return {
|
|
300
|
+
value: isNonEmptyRecord(defaultValue)
|
|
301
|
+
? JSON.stringify(defaultValue, null, 2)
|
|
302
|
+
: t("agentsDetailsUnsetValue"),
|
|
303
|
+
source: "default",
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function isNonEmptyRecord(value: unknown): value is Record<string, unknown> {
|
|
308
|
+
return Boolean(
|
|
309
|
+
value &&
|
|
310
|
+
typeof value === "object" &&
|
|
311
|
+
!Array.isArray(value) &&
|
|
312
|
+
Object.keys(value).length > 0,
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function DetailSection(props: {
|
|
317
|
+
icon: LucideIcon;
|
|
318
|
+
title: string;
|
|
319
|
+
children: ReactNode;
|
|
320
|
+
}) {
|
|
321
|
+
const { icon: Icon, title, children } = props;
|
|
322
|
+
|
|
323
|
+
return (
|
|
324
|
+
<section className="space-y-2 border-t border-gray-100 pt-3 first:border-t-0 first:pt-0">
|
|
325
|
+
<h3 className="flex items-center gap-1.5 text-xs font-semibold leading-5 text-gray-900">
|
|
326
|
+
<Icon className="h-3.5 w-3.5 text-gray-400" />
|
|
327
|
+
{title}
|
|
328
|
+
</h3>
|
|
329
|
+
<dl className="grid min-w-0 gap-x-6 gap-y-2.5 pl-5 sm:grid-cols-2">
|
|
330
|
+
{children}
|
|
331
|
+
</dl>
|
|
332
|
+
</section>
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function DetailItem(props: {
|
|
337
|
+
label: string;
|
|
338
|
+
value: string;
|
|
339
|
+
source?: DetailSource;
|
|
340
|
+
mono?: boolean;
|
|
341
|
+
prewrap?: boolean;
|
|
342
|
+
wide?: boolean;
|
|
343
|
+
}) {
|
|
344
|
+
const {
|
|
345
|
+
label,
|
|
346
|
+
value,
|
|
347
|
+
source,
|
|
348
|
+
mono = false,
|
|
349
|
+
prewrap = false,
|
|
350
|
+
wide = false,
|
|
351
|
+
} = props;
|
|
352
|
+
|
|
353
|
+
const isStructuredValue = prewrap && value.trim().startsWith("{");
|
|
354
|
+
const shouldUseMono = mono && (!prewrap || isStructuredValue);
|
|
355
|
+
|
|
356
|
+
return (
|
|
357
|
+
<div
|
|
358
|
+
className={cn(
|
|
359
|
+
"grid min-w-0 grid-cols-[10rem_minmax(0,1fr)] items-baseline gap-x-2",
|
|
360
|
+
(wide || isStructuredValue) && "sm:col-span-2",
|
|
361
|
+
)}
|
|
362
|
+
>
|
|
363
|
+
<dt className="flex min-w-0 items-baseline leading-5">
|
|
364
|
+
<span className="whitespace-nowrap text-xs font-medium text-gray-500">
|
|
365
|
+
{label}
|
|
366
|
+
</span>
|
|
367
|
+
</dt>
|
|
368
|
+
<dd
|
|
369
|
+
className={cn(
|
|
370
|
+
"min-w-0 break-words text-xs leading-5 text-gray-900",
|
|
371
|
+
isStructuredValue
|
|
372
|
+
? "max-h-28 overflow-auto whitespace-pre-wrap rounded bg-gray-50/70 px-2 py-1.5 text-left"
|
|
373
|
+
: "",
|
|
374
|
+
)}
|
|
375
|
+
>
|
|
376
|
+
<span className={shouldUseMono ? "font-mono" : undefined}>
|
|
377
|
+
{value}
|
|
378
|
+
</span>
|
|
379
|
+
{source ? <SourceBadge source={source} /> : null}
|
|
380
|
+
</dd>
|
|
381
|
+
</div>
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function SourceBadge({ source }: { source: DetailSource }) {
|
|
386
|
+
return (
|
|
387
|
+
<span
|
|
388
|
+
className={cn(
|
|
389
|
+
"ml-1 whitespace-nowrap text-xs font-normal leading-5",
|
|
390
|
+
source === "override" ? "text-blue-500" : "text-gray-400",
|
|
391
|
+
)}
|
|
392
|
+
>
|
|
393
|
+
({source === "override"
|
|
394
|
+
? t("agentsDetailsAgentOverride")
|
|
395
|
+
: t("agentsDetailsInheritedDefault")})
|
|
396
|
+
</span>
|
|
397
|
+
);
|
|
398
|
+
}
|
|
@@ -5,6 +5,15 @@ import {
|
|
|
5
5
|
resolveSessionTypeLabel,
|
|
6
6
|
type ChatSessionTypeOption,
|
|
7
7
|
} from "@/features/chat";
|
|
8
|
+
import { AgentAdvancedConfigFields } from "@/features/agents/components/agent-advanced-config-fields";
|
|
9
|
+
import type {
|
|
10
|
+
AgentCreateFormState,
|
|
11
|
+
AgentEditFormState,
|
|
12
|
+
} from "@/features/agents/types/agent-form.types";
|
|
13
|
+
import {
|
|
14
|
+
EMPTY_AGENT_CREATE_FORM,
|
|
15
|
+
toAgentEditFormState,
|
|
16
|
+
} from "@/features/agents/utils/agent-form.utils";
|
|
8
17
|
import { ProviderScopedModelInput } from "@/shared/components/common/provider-scoped-model-input";
|
|
9
18
|
import { Button } from "@/shared/components/ui/button";
|
|
10
19
|
import { NoticeCard } from "@/shared/components/feedback/notice-card";
|
|
@@ -29,53 +38,7 @@ import { t } from "@/shared/lib/i18n";
|
|
|
29
38
|
import type { ProviderModelCatalogItem } from "@/shared/lib/provider-models";
|
|
30
39
|
import { Pencil, Plus } from "lucide-react";
|
|
31
40
|
|
|
32
|
-
export type AgentCreateFormState
|
|
33
|
-
id: string;
|
|
34
|
-
displayName: string;
|
|
35
|
-
description: string;
|
|
36
|
-
avatar: string;
|
|
37
|
-
home: string;
|
|
38
|
-
model: string;
|
|
39
|
-
runtime: string;
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
export type AgentEditFormState = {
|
|
43
|
-
displayName: string;
|
|
44
|
-
description: string;
|
|
45
|
-
avatar: string;
|
|
46
|
-
model: string;
|
|
47
|
-
runtime: string;
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
export const EMPTY_AGENT_CREATE_FORM: AgentCreateFormState = {
|
|
51
|
-
id: "",
|
|
52
|
-
displayName: "",
|
|
53
|
-
description: "",
|
|
54
|
-
avatar: "",
|
|
55
|
-
model: "",
|
|
56
|
-
home: "",
|
|
57
|
-
runtime: "",
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
export const EMPTY_AGENT_EDIT_FORM: AgentEditFormState = {
|
|
61
|
-
displayName: "",
|
|
62
|
-
description: "",
|
|
63
|
-
avatar: "",
|
|
64
|
-
model: "",
|
|
65
|
-
runtime: "",
|
|
66
|
-
};
|
|
67
|
-
|
|
68
|
-
export function toAgentEditFormState(
|
|
69
|
-
agent: AgentProfileView,
|
|
70
|
-
): AgentEditFormState {
|
|
71
|
-
return {
|
|
72
|
-
displayName: agent.displayName ?? "",
|
|
73
|
-
description: agent.description ?? "",
|
|
74
|
-
avatar: agent.avatar ?? "",
|
|
75
|
-
model: agent.model ?? "",
|
|
76
|
-
runtime: agent.runtime ?? agent.engine ?? "",
|
|
77
|
-
};
|
|
78
|
-
}
|
|
41
|
+
export type { AgentCreateFormState, AgentEditFormState };
|
|
79
42
|
|
|
80
43
|
function buildRuntimeSelectOptions(params: {
|
|
81
44
|
runtimeOptions: ChatSessionTypeOption[];
|
|
@@ -472,6 +435,16 @@ function AgentEditDialogContent(props: {
|
|
|
472
435
|
disabled={pending}
|
|
473
436
|
/>
|
|
474
437
|
</div>
|
|
438
|
+
<AgentAdvancedConfigFields
|
|
439
|
+
form={form}
|
|
440
|
+
disabled={pending}
|
|
441
|
+
onChange={(patch) =>
|
|
442
|
+
setForm((prev) => ({
|
|
443
|
+
...prev,
|
|
444
|
+
...patch,
|
|
445
|
+
}))
|
|
446
|
+
}
|
|
447
|
+
/>
|
|
475
448
|
</div>
|
|
476
449
|
</div>
|
|
477
450
|
<DialogFooter className="shrink-0 border-t border-[#f1e7d4] px-6 py-5">
|
|
@@ -6,7 +6,8 @@ import {
|
|
|
6
6
|
useUpdateAgent,
|
|
7
7
|
} from "@/shared/hooks/use-agents";
|
|
8
8
|
import { useConfig, useProviderTemplates, useProviders } from "@/shared/hooks/use-config";
|
|
9
|
-
import type { AgentProfileView } from "@/shared/lib/api";
|
|
9
|
+
import type { AgentProfileView, AgentUpdateRequest } from "@/shared/lib/api";
|
|
10
|
+
import { AgentDetailsDialog } from "@/features/agents/components/agent-details-dialog";
|
|
10
11
|
import {
|
|
11
12
|
AgentEditDialog,
|
|
12
13
|
type AgentEditFormState,
|
|
@@ -35,6 +36,7 @@ import { buildProviderModelCatalog } from "@/shared/lib/provider-models";
|
|
|
35
36
|
import { cn } from "@/shared/lib/utils";
|
|
36
37
|
import {
|
|
37
38
|
Bot,
|
|
39
|
+
Eye,
|
|
38
40
|
House,
|
|
39
41
|
MessageCircle,
|
|
40
42
|
MoreHorizontal,
|
|
@@ -44,10 +46,38 @@ import {
|
|
|
44
46
|
Sparkles,
|
|
45
47
|
Trash2,
|
|
46
48
|
} from "lucide-react";
|
|
49
|
+
import { toast } from "sonner";
|
|
47
50
|
|
|
48
51
|
const AGENT_CREATION_PROMPT =
|
|
49
52
|
"请直接创建一个默认示例 Agent,不要问我问题。创建完成后,简单告诉我它能做什么。";
|
|
50
53
|
|
|
54
|
+
function parseOptionalIntegerField(value: string, min: number): number | null {
|
|
55
|
+
const trimmed = value.trim();
|
|
56
|
+
if (!trimmed) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const parsed = Number(trimmed);
|
|
60
|
+
if (!Number.isFinite(parsed)) {
|
|
61
|
+
throw new Error(t("agentsAdvancedInvalidNumberError"));
|
|
62
|
+
}
|
|
63
|
+
return Math.max(min, Math.trunc(parsed));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function buildAgentUpdateRequest(
|
|
67
|
+
form: AgentEditFormState,
|
|
68
|
+
): AgentUpdateRequest {
|
|
69
|
+
return {
|
|
70
|
+
displayName: form.displayName,
|
|
71
|
+
description: form.description,
|
|
72
|
+
avatar: form.avatar,
|
|
73
|
+
model: form.model,
|
|
74
|
+
...(form.runtime.trim()
|
|
75
|
+
? { runtime: form.runtime.trim() }
|
|
76
|
+
: { runtime: "" }),
|
|
77
|
+
contextTokens: parseOptionalIntegerField(form.contextTokens, 1000),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
51
81
|
function AgentsHero(props: { agentCount: number; onCreate: () => void }) {
|
|
52
82
|
const { agentCount, onCreate } = props;
|
|
53
83
|
|
|
@@ -119,6 +149,7 @@ function AgentListCard(props: {
|
|
|
119
149
|
updatePending: boolean;
|
|
120
150
|
deletePending: boolean;
|
|
121
151
|
onStartChat: () => void;
|
|
152
|
+
onView: () => void;
|
|
122
153
|
onEdit: () => void;
|
|
123
154
|
onDelete: () => void;
|
|
124
155
|
}) {
|
|
@@ -129,6 +160,7 @@ function AgentListCard(props: {
|
|
|
129
160
|
updatePending,
|
|
130
161
|
deletePending,
|
|
131
162
|
onStartChat,
|
|
163
|
+
onView,
|
|
132
164
|
onEdit,
|
|
133
165
|
onDelete,
|
|
134
166
|
} = props;
|
|
@@ -194,6 +226,11 @@ function AgentListCard(props: {
|
|
|
194
226
|
</Button>
|
|
195
227
|
</PopoverTrigger>
|
|
196
228
|
<PopoverContent align="end" className="w-44 p-1.5">
|
|
229
|
+
<AgentActionMenuItem
|
|
230
|
+
icon={Eye}
|
|
231
|
+
label={t("agentsViewDetailsAction")}
|
|
232
|
+
onClick={onView}
|
|
233
|
+
/>
|
|
197
234
|
<AgentActionMenuItem
|
|
198
235
|
icon={Pencil}
|
|
199
236
|
label={t("agentsEditAction")}
|
|
@@ -246,6 +283,9 @@ export function AgentsPage() {
|
|
|
246
283
|
const sessionTypesQuery = useNcpChatSessionTypes();
|
|
247
284
|
const updateAgent = useUpdateAgent();
|
|
248
285
|
const deleteAgent = useDeleteAgent();
|
|
286
|
+
const [viewingAgent, setViewingAgent] = useState<AgentProfileView | null>(
|
|
287
|
+
null,
|
|
288
|
+
);
|
|
249
289
|
const [editingAgent, setEditingAgent] = useState<AgentProfileView | null>(
|
|
250
290
|
null,
|
|
251
291
|
);
|
|
@@ -286,23 +326,31 @@ export function AgentsPage() {
|
|
|
286
326
|
resolveSessionTypeLabel(defaultRuntime),
|
|
287
327
|
[defaultRuntime, runtimeOptions],
|
|
288
328
|
);
|
|
329
|
+
const agentDefaults = configQuery.data?.agents.defaults;
|
|
330
|
+
|
|
331
|
+
const handleStartView = (agent: AgentProfileView) => {
|
|
332
|
+
setViewingAgent(agent);
|
|
333
|
+
};
|
|
289
334
|
|
|
290
335
|
const handleStartEdit = (agent: AgentProfileView) => {
|
|
291
336
|
setEditingAgent(agent);
|
|
292
337
|
};
|
|
293
338
|
|
|
294
339
|
const handleUpdate = async (agentId: string, form: AgentEditFormState) => {
|
|
340
|
+
let data: AgentUpdateRequest;
|
|
341
|
+
try {
|
|
342
|
+
data = buildAgentUpdateRequest(form);
|
|
343
|
+
} catch (error) {
|
|
344
|
+
toast.error(
|
|
345
|
+
error instanceof Error
|
|
346
|
+
? error.message
|
|
347
|
+
: t("agentsAdvancedParseFailed"),
|
|
348
|
+
);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
295
351
|
await updateAgent.mutateAsync({
|
|
296
352
|
agentId,
|
|
297
|
-
data
|
|
298
|
-
displayName: form.displayName,
|
|
299
|
-
description: form.description,
|
|
300
|
-
avatar: form.avatar,
|
|
301
|
-
model: form.model,
|
|
302
|
-
...(form.runtime.trim()
|
|
303
|
-
? { runtime: form.runtime.trim() }
|
|
304
|
-
: { runtime: "" }),
|
|
305
|
-
},
|
|
353
|
+
data,
|
|
306
354
|
});
|
|
307
355
|
setEditingAgent(null);
|
|
308
356
|
};
|
|
@@ -365,6 +413,7 @@ export function AgentsPage() {
|
|
|
365
413
|
updatePending={updateAgent.isPending}
|
|
366
414
|
deletePending={deleteAgent.isPending}
|
|
367
415
|
onStartChat={() => startChatWithAgent(agent)}
|
|
416
|
+
onView={() => handleStartView(agent)}
|
|
368
417
|
onEdit={() => handleStartEdit(agent)}
|
|
369
418
|
onDelete={() => deleteAgent.mutate({ agentId: agent.id })}
|
|
370
419
|
/>
|
|
@@ -372,6 +421,23 @@ export function AgentsPage() {
|
|
|
372
421
|
)}
|
|
373
422
|
</div>
|
|
374
423
|
|
|
424
|
+
<AgentDetailsDialog
|
|
425
|
+
agent={viewingAgent}
|
|
426
|
+
defaults={agentDefaults}
|
|
427
|
+
runtimeOptions={runtimeOptions}
|
|
428
|
+
defaultRuntime={defaultRuntime}
|
|
429
|
+
defaultRuntimeLabel={defaultRuntimeLabel}
|
|
430
|
+
onOpenChange={(open) => {
|
|
431
|
+
if (!open) {
|
|
432
|
+
setViewingAgent(null);
|
|
433
|
+
}
|
|
434
|
+
}}
|
|
435
|
+
onEdit={(agent) => {
|
|
436
|
+
setViewingAgent(null);
|
|
437
|
+
setEditingAgent(agent);
|
|
438
|
+
}}
|
|
439
|
+
/>
|
|
440
|
+
|
|
375
441
|
<AgentEditDialog
|
|
376
442
|
agent={editingAgent}
|
|
377
443
|
pending={updateAgent.isPending}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type AgentCreateFormState = {
|
|
2
|
+
id: string;
|
|
3
|
+
displayName: string;
|
|
4
|
+
description: string;
|
|
5
|
+
avatar: string;
|
|
6
|
+
home: string;
|
|
7
|
+
model: string;
|
|
8
|
+
runtime: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export type AgentAdvancedFormState = {
|
|
12
|
+
contextTokens: string;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export type AgentEditFormState = {
|
|
16
|
+
displayName: string;
|
|
17
|
+
description: string;
|
|
18
|
+
avatar: string;
|
|
19
|
+
model: string;
|
|
20
|
+
runtime: string;
|
|
21
|
+
} & AgentAdvancedFormState;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { AgentProfileView } from "@/shared/lib/api";
|
|
2
|
+
import type {
|
|
3
|
+
AgentCreateFormState,
|
|
4
|
+
AgentEditFormState,
|
|
5
|
+
} from "@/features/agents/types/agent-form.types";
|
|
6
|
+
|
|
7
|
+
export const EMPTY_AGENT_CREATE_FORM: AgentCreateFormState = {
|
|
8
|
+
id: "",
|
|
9
|
+
displayName: "",
|
|
10
|
+
description: "",
|
|
11
|
+
avatar: "",
|
|
12
|
+
model: "",
|
|
13
|
+
home: "",
|
|
14
|
+
runtime: "",
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export const EMPTY_AGENT_EDIT_FORM: AgentEditFormState = {
|
|
18
|
+
displayName: "",
|
|
19
|
+
description: "",
|
|
20
|
+
avatar: "",
|
|
21
|
+
model: "",
|
|
22
|
+
runtime: "",
|
|
23
|
+
contextTokens: "",
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export function toAgentEditFormState(agent: AgentProfileView): AgentEditFormState {
|
|
27
|
+
return {
|
|
28
|
+
displayName: agent.displayName ?? "",
|
|
29
|
+
description: agent.description ?? "",
|
|
30
|
+
avatar: agent.avatar ?? "",
|
|
31
|
+
model: agent.model ?? "",
|
|
32
|
+
runtime: agent.runtime ?? agent.engine ?? "",
|
|
33
|
+
contextTokens: formatOptionalNumberField(agent.contextTokens),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function formatOptionalNumberField(value: number | undefined): string {
|
|
38
|
+
return typeof value === "number" ? String(value) : "";
|
|
39
|
+
}
|