@springbrand/agent-config-editor 0.0.0-stage → 0.1.0-alpha.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/README.md +25 -2
- package/dist/agent-config-editor/src/agent-action-selector.d.ts +10 -0
- package/dist/agent-config-editor/src/agent-action-selector.js +114 -0
- package/dist/agent-config-editor/src/agent-component-selector.d.ts +9 -0
- package/dist/agent-config-editor/src/agent-component-selector.js +41 -0
- package/dist/agent-config-editor/src/agent-connector-selector.d.ts +12 -0
- package/dist/agent-config-editor/src/agent-connector-selector.js +27 -0
- package/dist/agent-config-editor/src/agent-deletion-feedback.d.ts +7 -0
- package/dist/agent-config-editor/src/agent-deletion-feedback.js +55 -0
- package/dist/agent-config-editor/src/agent-draft.d.ts +10 -0
- package/dist/agent-config-editor/src/agent-draft.js +28 -0
- package/dist/agent-config-editor/src/agent-editor.d.ts +30 -0
- package/dist/agent-config-editor/src/agent-editor.js +125 -0
- package/dist/agent-config-editor/src/agent-library-selectors.d.ts +16 -0
- package/dist/agent-config-editor/src/agent-library-selectors.js +45 -0
- package/dist/agent-config-editor/src/agent-reference-files.d.ts +10 -0
- package/dist/agent-config-editor/src/agent-reference-files.js +93 -0
- package/dist/agent-config-editor/src/agent-resource-section.d.ts +11 -0
- package/dist/agent-config-editor/src/agent-resource-section.js +6 -0
- package/dist/agent-config-editor/src/agent-selection-dialog.d.ts +25 -0
- package/dist/agent-config-editor/src/agent-selection-dialog.js +8 -0
- package/dist/agent-config-editor/src/assets.d.ts +11 -0
- package/dist/agent-config-editor/src/client/actions.d.ts +48 -0
- package/dist/agent-config-editor/src/client/actions.js +30 -0
- package/dist/agent-config-editor/src/client/agent-configs.d.ts +311 -0
- package/dist/agent-config-editor/src/client/agent-configs.js +42 -0
- package/dist/agent-config-editor/src/client/attachments.d.ts +13 -0
- package/dist/agent-config-editor/src/client/attachments.js +20 -0
- package/dist/agent-config-editor/src/client/components.d.ts +18 -0
- package/dist/agent-config-editor/src/client/components.js +14 -0
- package/dist/agent-config-editor/src/client/connectors.d.ts +40 -0
- package/dist/agent-config-editor/src/client/connectors.js +8 -0
- package/dist/agent-config-editor/src/client/index.d.ts +280 -0
- package/dist/agent-config-editor/src/client/index.js +25 -0
- package/dist/agent-config-editor/src/client/models.d.ts +15 -0
- package/dist/agent-config-editor/src/client/models.js +7 -0
- package/dist/agent-config-editor/src/client/references.d.ts +67 -0
- package/dist/agent-config-editor/src/client/references.js +35 -0
- package/dist/agent-config-editor/src/client/request.d.ts +2 -0
- package/dist/agent-config-editor/src/client/request.js +1 -0
- package/dist/agent-config-editor/src/client/skills.d.ts +98 -0
- package/dist/agent-config-editor/src/client/skills.js +57 -0
- package/dist/agent-config-editor/src/create-agent.d.ts +14 -0
- package/dist/agent-config-editor/src/create-agent.js +53 -0
- package/dist/agent-config-editor/src/host.d.ts +14 -0
- package/dist/agent-config-editor/src/host.js +12 -0
- package/dist/agent-config-editor/src/index.d.ts +10 -0
- package/dist/agent-config-editor/src/index.js +9 -0
- package/dist/agent-config-editor/src/reference-management-dialog.d.ts +6 -0
- package/dist/agent-config-editor/src/reference-management-dialog.js +89 -0
- package/dist/agent-config-editor/src/skill-upload-button.d.ts +17 -0
- package/dist/agent-config-editor/src/skill-upload-button.js +45 -0
- package/dist/agent-config-editor/src/skills-management-page.d.ts +9 -0
- package/dist/agent-config-editor/src/skills-management-page.js +142 -0
- package/dist/agent-config-editor/src/use-connector-authorization.d.ts +61 -0
- package/dist/agent-config-editor/src/use-connector-authorization.js +74 -0
- package/dist/agent-config-editor/src/use-selection-dialog-open.d.ts +2 -0
- package/dist/agent-config-editor/src/use-selection-dialog-open.js +10 -0
- package/dist/agent-contracts/src/action-discovery.d.ts +98 -0
- package/dist/agent-contracts/src/action-discovery.js +32 -0
- package/dist/agent-contracts/src/action.d.ts +102 -0
- package/dist/agent-contracts/src/action.js +52 -0
- package/dist/agent-contracts/src/agent.d.ts +960 -0
- package/dist/agent-contracts/src/agent.js +229 -0
- package/dist/agent-contracts/src/component.d.ts +53 -0
- package/dist/agent-contracts/src/component.js +25 -0
- package/dist/agent-contracts/src/contracts/chat-website-deployed.d.ts +26 -0
- package/dist/agent-contracts/src/contracts/chat-website-deployed.js +29 -0
- package/dist/agent-contracts/src/contracts/chat-workspace-changed.d.ts +7 -0
- package/dist/agent-contracts/src/contracts/chat-workspace-changed.js +31 -0
- package/dist/agent-contracts/src/contracts/chat.d.ts +5 -0
- package/dist/agent-contracts/src/contracts/chat.js +1 -0
- package/dist/agent-contracts/src/contracts/conversation.d.ts +100 -0
- package/dist/agent-contracts/src/contracts/conversation.js +44 -0
- package/dist/agent-contracts/src/contracts/index.d.ts +5 -0
- package/dist/agent-contracts/src/contracts/index.js +5 -0
- package/dist/agent-contracts/src/contracts/runtime-source.d.ts +104 -0
- package/dist/agent-contracts/src/contracts/runtime-source.js +22 -0
- package/dist/agent-contracts/src/contracts/workspace.d.ts +31 -0
- package/dist/agent-contracts/src/contracts/workspace.js +6 -0
- package/dist/agent-contracts/src/host-tool-registry.d.ts +43 -0
- package/dist/agent-contracts/src/host-tool-registry.js +58 -0
- package/dist/agent-contracts/src/inbox-chat-list.d.ts +63 -0
- package/dist/agent-contracts/src/inbox-chat-list.js +102 -0
- package/dist/agent-contracts/src/mcp.d.ts +173 -0
- package/dist/agent-contracts/src/mcp.js +122 -0
- package/dist/agent-contracts/src/model.d.ts +597 -0
- package/dist/agent-contracts/src/model.js +484 -0
- package/dist/agent-contracts/src/runtime-plan.d.ts +177 -0
- package/dist/agent-contracts/src/runtime-plan.js +28 -0
- package/dist/agent-contracts/src/scheduled-task.d.ts +454 -0
- package/dist/agent-contracts/src/scheduled-task.js +141 -0
- package/dist/agent-contracts/src/selection.d.ts +5 -0
- package/dist/agent-contracts/src/selection.js +17 -0
- package/dist/agent-contracts/src/skill.d.ts +199 -0
- package/dist/agent-contracts/src/skill.js +106 -0
- package/dist/agent-contracts/src/trajectory.d.ts +79 -0
- package/dist/agent-contracts/src/trajectory.js +60 -0
- package/dist/agent-contracts/src/user-mcp.d.ts +38 -0
- package/dist/agent-contracts/src/user-mcp.js +11 -0
- package/dist/resource/src/api-supplier.d.ts +35 -0
- package/dist/resource/src/api-supplier.js +12 -0
- package/dist/resource/src/index.d.ts +1748 -0
- package/dist/resource/src/index.js +715 -0
- package/dist/resource/src/skill-render.d.ts +19 -0
- package/dist/resource/src/skill-render.js +84 -0
- package/dist/utils/src/attachment.d.ts +12 -0
- package/dist/utils/src/attachment.js +50 -0
- package/dist/utils/src/base64.d.ts +3 -0
- package/dist/utils/src/base64.js +27 -0
- package/dist/utils/src/content-hash.d.ts +5 -0
- package/dist/utils/src/content-hash.js +27 -0
- package/dist/utils/src/database.d.ts +1 -0
- package/dist/utils/src/database.js +9 -0
- package/dist/utils/src/html.d.ts +2 -0
- package/dist/utils/src/html.js +9 -0
- package/dist/utils/src/image.d.ts +33 -0
- package/dist/utils/src/image.js +35 -0
- package/dist/utils/src/index.d.ts +12 -0
- package/dist/utils/src/index.js +12 -0
- package/dist/utils/src/ip-address.d.ts +6 -0
- package/dist/utils/src/ip-address.js +11 -0
- package/dist/utils/src/json.d.ts +6 -0
- package/dist/utils/src/json.js +18 -0
- package/dist/utils/src/ooxml.d.ts +14 -0
- package/dist/utils/src/ooxml.js +170 -0
- package/dist/utils/src/serialized-size.d.ts +9 -0
- package/dist/utils/src/serialized-size.js +11 -0
- package/dist/utils/src/strict-zip.d.ts +68 -0
- package/dist/utils/src/strict-zip.js +177 -0
- package/dist/utils/src/uuid.d.ts +9 -0
- package/dist/utils/src/uuid.js +27 -0
- package/package.json +53 -4
package/README.md
CHANGED
|
@@ -1,3 +1,26 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @springbrand/agent-config-editor
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The Agent Config editor shared by SP Web and Eval Web. It owns the existing SP
|
|
4
|
+
Behavior, Model, Thinking, Connector Toolkit, Component Connector, Extension,
|
|
5
|
+
Action, Skill and Reference editor, selectors and library dialogs. Agent Config
|
|
6
|
+
schemas, model presets and resource contracts remain in their owning packages.
|
|
7
|
+
|
|
8
|
+
`AgentEditorProvider` accepts an authenticated `AgentEditorClient`, host
|
|
9
|
+
navigation and the Connector authorization return route. `createAgentEditorClient`
|
|
10
|
+
binds the existing public SP HTTP operations to a host-supplied request function.
|
|
11
|
+
The package has no application imports, credentials, database access, router or
|
|
12
|
+
runtime authority. Hosts own creation and edit sessions, persistence, revision
|
|
13
|
+
conflicts, frozen-copy rules and navigation. `AgentDraft` and input conversion
|
|
14
|
+
reuse the public Agent Config schemas and retain settings not exposed by the UI.
|
|
15
|
+
|
|
16
|
+
SP and Eval consume the same components. A read-only editor hides mutation
|
|
17
|
+
actions and disables configuration changes while preserving inspection.
|
|
18
|
+
Resource selections update the host's draft; only its normal Save/Create action
|
|
19
|
+
persists the Agent configuration. Resource library mutations retain their existing
|
|
20
|
+
explicit actions and confirmation dialogs.
|
|
21
|
+
|
|
22
|
+
Consumers load the shared Dune styles and include this package's source in their
|
|
23
|
+
Tailwind source scan. React, React DOM and React Query are host peers.
|
|
24
|
+
|
|
25
|
+
Run `pnpm --filter @springbrand/agent-config-editor check` and
|
|
26
|
+
`pnpm --filter @springbrand/agent-config-editor test` for focused verification.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ActionCatalogItem } from '../../agent-contracts/src/action.js';
|
|
2
|
+
export declare function AgentActionSelector({ open, onOpenChange, userId, selectedIds, selectedItems, onChange, maxActionCount, }: {
|
|
3
|
+
open: boolean;
|
|
4
|
+
onOpenChange: (open: boolean) => void;
|
|
5
|
+
userId: string;
|
|
6
|
+
selectedIds: readonly string[];
|
|
7
|
+
selectedItems: readonly (ActionCatalogItem | undefined)[];
|
|
8
|
+
onChange: (ids: string[]) => void;
|
|
9
|
+
maxActionCount: number;
|
|
10
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useDeferredValue, useEffect, useReducer, useRef, useState } from 'react';
|
|
3
|
+
import { useInfiniteQuery, useQuery } from '@tanstack/react-query';
|
|
4
|
+
import { Button, Checkbox, Collapsible, CollapsibleContent, CollapsibleTrigger } from '@springbrand/site-block';
|
|
5
|
+
import { ChevronDown } from 'lucide-react';
|
|
6
|
+
import { actionCatalogQueryKey, actionGroupQueryKey } from './client/index.js';
|
|
7
|
+
import { useAgentEditorHost } from './host.js';
|
|
8
|
+
import { AgentSelectableRow, AgentSelectionDialog } from './agent-selection-dialog.js';
|
|
9
|
+
import { useSelectionDialogOpen } from './use-selection-dialog-open.js';
|
|
10
|
+
function editSelection(draft, edit) {
|
|
11
|
+
if (edit.type === 'reset')
|
|
12
|
+
return { ids: [...edit.ids], error: null };
|
|
13
|
+
const ids = edit.change(draft.ids);
|
|
14
|
+
return ids.length > edit.maxCount
|
|
15
|
+
? { ...draft, error: `This Agent can select up to ${edit.maxCount} Actions with its other capabilities.` }
|
|
16
|
+
: { ids, error: null };
|
|
17
|
+
}
|
|
18
|
+
function nextPage(last) {
|
|
19
|
+
return last.page * last.pageSize < last.total ? last.page + 1 : undefined;
|
|
20
|
+
}
|
|
21
|
+
async function allGroupActions(listGroupActions, group, signal) {
|
|
22
|
+
const groupQuery = group.supplier ? { supplierId: group.supplier.id } : { unassigned: 'true' };
|
|
23
|
+
const items = [];
|
|
24
|
+
let page = 1;
|
|
25
|
+
while (true) {
|
|
26
|
+
const result = await listGroupActions({ ...groupQuery, page, pageSize: 100 }, signal);
|
|
27
|
+
items.push(...result.data);
|
|
28
|
+
if (page * result.pageSize >= result.total)
|
|
29
|
+
return items;
|
|
30
|
+
page += 1;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
export function AgentActionSelector({ open, onOpenChange, userId, selectedIds, selectedItems, onChange, maxActionCount, }) {
|
|
34
|
+
const { client: { actions: { listActions } } } = useAgentEditorHost();
|
|
35
|
+
const [search, setSearch] = useState('');
|
|
36
|
+
const q = useDeferredValue(search);
|
|
37
|
+
const [draft, dispatch] = useReducer(editSelection, { ids: [], error: null });
|
|
38
|
+
const [pendingGroupSelections, setPendingGroupSelections] = useState(0);
|
|
39
|
+
const selectionSession = useRef(0);
|
|
40
|
+
useSelectionDialogOpen(open, () => dispatch({ type: 'reset', ids: selectedIds }));
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
selectionSession.current += 1;
|
|
43
|
+
setPendingGroupSelections(0);
|
|
44
|
+
}, [open]);
|
|
45
|
+
const query = useInfiniteQuery({
|
|
46
|
+
queryKey: actionCatalogQueryKey(userId, q),
|
|
47
|
+
queryFn: ({ pageParam, signal }) => listActions({ q, page: pageParam, pageSize: 50 }, signal),
|
|
48
|
+
initialPageParam: 1, getNextPageParam: nextPage, enabled: open,
|
|
49
|
+
});
|
|
50
|
+
const groups = query.data?.pages.flatMap((page) => page.data) ?? [];
|
|
51
|
+
const setNext = (change) => dispatch({ type: 'update', change, maxCount: maxActionCount });
|
|
52
|
+
function beginGroupSelection() {
|
|
53
|
+
const session = selectionSession.current;
|
|
54
|
+
setPendingGroupSelections((count) => count + 1);
|
|
55
|
+
return {
|
|
56
|
+
apply(change) {
|
|
57
|
+
if (selectionSession.current === session)
|
|
58
|
+
setNext(change);
|
|
59
|
+
},
|
|
60
|
+
finish() {
|
|
61
|
+
if (selectionSession.current === session)
|
|
62
|
+
setPendingGroupSelections((count) => count - 1);
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function handleOpenChange(nextOpen) {
|
|
67
|
+
if (!nextOpen) {
|
|
68
|
+
selectionSession.current += 1;
|
|
69
|
+
setPendingGroupSelections(0);
|
|
70
|
+
}
|
|
71
|
+
onOpenChange(nextOpen);
|
|
72
|
+
}
|
|
73
|
+
return _jsxs(AgentSelectionDialog, { open: open, onOpenChange: handleOpenChange, title: 'Choose Actions', description: 'Choose a whole Action group or expand it to choose individual Actions.', search: search, searchPlaceholder: 'Search actions...', onSearchChange: setSearch, selectedCount: draft.ids.length, saveDisabled: pendingGroupSelections > 0, onSave: () => { onChange(draft.ids); handleOpenChange(false); }, children: [draft.error ? _jsx("p", { role: 'alert', className: 'text-sm text-destructive', children: draft.error }) : null, query.isPending ? _jsx("p", { role: 'status', children: "Loading Action groups..." }) : null, query.isError ? _jsx("p", { role: 'alert', className: 'text-sm text-destructive', children: "Could not load Actions." }) : null, groups.map((group) => _jsx(ActionGroupRow, { userId: userId, group: group, selectedIds: draft.ids, selectedItems: selectedItems, onChange: setNext, beginGroupSelection: beginGroupSelection }, group.supplier?.id ?? 'unassigned')), !query.isPending && groups.length === 0 ? _jsx("p", { className: 'text-sm text-muted-foreground', children: "No Action groups found." }) : null, query.hasNextPage ? _jsx(Button, { type: 'button', variant: 'outline', size: 'sm', disabled: query.isFetchingNextPage, onClick: () => void query.fetchNextPage(), children: "Load more Action groups" }) : null] });
|
|
74
|
+
}
|
|
75
|
+
function ActionGroupRow({ group, userId, selectedIds, selectedItems, onChange, beginGroupSelection }) {
|
|
76
|
+
const { client: { actions: { listGroupActions } } } = useAgentEditorHost();
|
|
77
|
+
const [open, setOpen] = useState(false);
|
|
78
|
+
const groupId = group.supplier?.id ?? 'unassigned';
|
|
79
|
+
const query = useQuery({
|
|
80
|
+
queryKey: actionGroupQueryKey(userId, groupId, ''),
|
|
81
|
+
queryFn: ({ signal }) => allGroupActions(listGroupActions, group, signal),
|
|
82
|
+
enabled: open,
|
|
83
|
+
});
|
|
84
|
+
const actions = query.data ?? [];
|
|
85
|
+
const selectable = actions.filter((action) => action.selectable && action.status === 'active');
|
|
86
|
+
const selectedInGroup = selectable.filter((action) => selectedIds.includes(action.id)).length;
|
|
87
|
+
const allSelected = selectable.length > 0 && selectedInGroup === selectable.length;
|
|
88
|
+
const knownSelectedCount = selectedItems.filter((item) => item && selectedIds.includes(item.id)
|
|
89
|
+
&& (item.supplier?.id ?? 'unassigned') === groupId)
|
|
90
|
+
.length;
|
|
91
|
+
const displaySelectedCount = query.data ? selectedInGroup : knownSelectedCount;
|
|
92
|
+
async function toggleGroup() {
|
|
93
|
+
const selection = beginGroupSelection();
|
|
94
|
+
setOpen(true);
|
|
95
|
+
try {
|
|
96
|
+
const available = query.data ?? (await query.refetch()).data;
|
|
97
|
+
if (!available)
|
|
98
|
+
return;
|
|
99
|
+
const eligible = available.filter((action) => action.selectable && action.status === 'active');
|
|
100
|
+
const groupIds = new Set(eligible.map((action) => action.id));
|
|
101
|
+
selection.apply((ids) => {
|
|
102
|
+
const selectedAll = eligible.length > 0 && eligible.every((action) => ids.includes(action.id));
|
|
103
|
+
return selectedAll ? ids.filter((id) => !groupIds.has(id))
|
|
104
|
+
: [...ids, ...eligible.map((action) => action.id).filter((id) => !ids.includes(id))];
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
selection.finish();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return _jsxs(Collapsible, { open: open, onOpenChange: setOpen, className: 'rounded-lg border border-border', children: [_jsxs("div", { className: 'flex min-h-14 items-center gap-3 px-3', children: [_jsx(Checkbox, { checked: allSelected, disabled: query.isFetching, onCheckedChange: () => { void toggleGroup(); }, "aria-label": `Select all ${group.supplier?.name ?? 'Other'} Actions` }), group.supplier?.iconUrl ? _jsx("img", { src: group.supplier.iconUrl, alt: '', className: 'size-6 object-contain' }) : null, _jsx(CollapsibleTrigger, { asChild: true, children: _jsxs(Button, { type: 'button', variant: 'ghost', className: 'min-w-0 flex-1 justify-between text-left', children: [_jsxs("span", { className: 'truncate', children: [group.supplier?.name ?? 'Other Actions', " ", _jsxs("span", { className: 'text-xs text-muted-foreground', children: ["(", group.actionCount, ")"] }), displaySelectedCount > 0 ? _jsxs("span", { className: 'ml-2 text-xs text-muted-foreground', children: [displaySelectedCount, " selected"] }) : null] }), _jsx(ChevronDown, { "aria-hidden": 'true', className: 'size-4' })] }) })] }), _jsxs(CollapsibleContent, { className: 'space-y-2 border-t border-border p-3', children: [query.isPending ? _jsx("p", { role: 'status', className: 'text-sm', children: "Loading Actions..." }) : null, query.isError ? _jsx("p", { role: 'alert', className: 'text-sm text-destructive', children: "Could not load this group." }) : null, actions.map((action) => _jsx(AgentSelectableRow, { checked: selectedIds.includes(action.id), onCheckedChange: (checked) => onChange((ids) => checked
|
|
112
|
+
? (ids.includes(action.id) ? [...ids] : [...ids, action.id])
|
|
113
|
+
: ids.filter((id) => id !== action.id)), name: action.name, description: action.description, disabled: !action.selectable || action.status !== 'active', badge: !action.selectable ? 'Not available to this account' : action.status === 'deprecated' ? 'Deprecated' : undefined }, action.id))] })] });
|
|
114
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ComponentCatalogKind } from '../../agent-contracts/src/component.js';
|
|
2
|
+
export declare function AgentComponentSelector({ userId, kind, selectedIds, onChange, maxCount, readOnly }: {
|
|
3
|
+
userId: string;
|
|
4
|
+
kind: ComponentCatalogKind;
|
|
5
|
+
selectedIds: readonly string[];
|
|
6
|
+
onChange: (ids: string[]) => void;
|
|
7
|
+
maxCount: number;
|
|
8
|
+
readOnly?: boolean;
|
|
9
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useDeferredValue, useState } from 'react';
|
|
3
|
+
import { useInfiniteQuery } from '@tanstack/react-query';
|
|
4
|
+
import { Button } from '@springbrand/site-block';
|
|
5
|
+
import { ArrowDown, ArrowUp, X } from 'lucide-react';
|
|
6
|
+
import { componentCatalogQueryKey } from './client/index.js';
|
|
7
|
+
import { useAgentEditorHost } from './host.js';
|
|
8
|
+
import { AgentResourceSection } from './agent-resource-section.js';
|
|
9
|
+
import { AgentSelectableRow, AgentSelectionDialog } from './agent-selection-dialog.js';
|
|
10
|
+
import { useSelectionDialogOpen } from './use-selection-dialog-open.js';
|
|
11
|
+
export function AgentComponentSelector({ userId, kind, selectedIds, onChange, maxCount, readOnly = false }) {
|
|
12
|
+
const { client: { components: { listComponents } } } = useAgentEditorHost();
|
|
13
|
+
const title = kind === 'connector' ? 'Component Connectors' : 'Extensions';
|
|
14
|
+
const itemLabel = kind === 'connector' ? 'Connector' : 'Extension';
|
|
15
|
+
const [open, setOpen] = useState(false);
|
|
16
|
+
const [search, setSearch] = useState('');
|
|
17
|
+
const [draftIds, setDraftIds] = useState([]);
|
|
18
|
+
const q = useDeferredValue(search);
|
|
19
|
+
useSelectionDialogOpen(open, () => setDraftIds([...selectedIds]));
|
|
20
|
+
const query = useInfiniteQuery({
|
|
21
|
+
queryKey: componentCatalogQueryKey(userId, kind, q),
|
|
22
|
+
queryFn: ({ pageParam, signal }) => listComponents({ kind, q, page: pageParam, pageSize: 50 }, signal),
|
|
23
|
+
initialPageParam: 1,
|
|
24
|
+
getNextPageParam: (last) => last.page * last.pageSize < last.total ? last.page + 1 : undefined,
|
|
25
|
+
});
|
|
26
|
+
const components = query.data?.pages.flatMap((page) => page.data) ?? [];
|
|
27
|
+
function move(index, offset) {
|
|
28
|
+
const ids = [...selectedIds];
|
|
29
|
+
const target = index + offset;
|
|
30
|
+
if (target < 0 || target >= ids.length)
|
|
31
|
+
return;
|
|
32
|
+
[ids[index], ids[target]] = [ids[target], ids[index]];
|
|
33
|
+
onChange(ids);
|
|
34
|
+
}
|
|
35
|
+
return _jsxs(_Fragment, { children: [_jsx(AgentResourceSection, { title: title, count: selectedIds.length, onAdd: readOnly ? undefined : () => setOpen(true), children: selectedIds.length === 0 ? _jsx("p", { className: 'text-sm text-muted-foreground', children: "None selected." }) :
|
|
36
|
+
_jsx("ul", { "aria-label": `Selected ${title} in priority order`, className: 'space-y-2', children: selectedIds.map((id, index) => _jsxs("li", { className: 'flex items-center gap-2 rounded-lg border border-border px-3 py-2', children: [_jsx("span", { className: 'min-w-0 flex-1 truncate text-sm', children: components.find((component) => component.id === id)?.name ?? id }), _jsx(Button, { type: 'button', variant: 'ghost', size: 'icon-sm', disabled: readOnly || index === 0, "aria-label": `Move ${itemLabel} ${index + 1} up`, onClick: () => move(index, -1), children: _jsx(ArrowUp, { "aria-hidden": 'true' }) }), _jsx(Button, { type: 'button', variant: 'ghost', size: 'icon-sm', disabled: readOnly || index === selectedIds.length - 1, "aria-label": `Move ${itemLabel} ${index + 1} down`, onClick: () => move(index, 1), children: _jsx(ArrowDown, { "aria-hidden": 'true' }) }), _jsx(Button, { type: 'button', variant: 'ghost', size: 'icon-sm', disabled: readOnly, "aria-label": `Remove ${id} from Agent`, onClick: () => onChange(selectedIds.filter((item) => item !== id)), children: _jsx(X, { "aria-hidden": 'true' }) })] }, id)) }) }), _jsxs(AgentSelectionDialog, { open: open && !readOnly, onOpenChange: setOpen, title: `Choose ${title}`, description: `Choose platform ${title}. Selected capabilities are available to new messages in this order.`, search: search, searchPlaceholder: `Search ${title.toLowerCase()}...`, onSearchChange: setSearch, selectedCount: draftIds.length, onSave: () => { onChange(draftIds); setOpen(false); }, children: [query.isPending ? _jsxs("p", { role: 'status', children: ["Loading ", title, "..."] }) : null, query.isError ? _jsxs("p", { role: 'alert', className: 'text-sm text-destructive', children: ["Could not load ", title, "."] }) : null, components.map((component) => _jsx(AgentSelectableRow, { checked: draftIds.includes(component.id), name: component.name, description: component.description, onCheckedChange: (checked) => setDraftIds((ids) => checked
|
|
37
|
+
? ids.includes(component.id) ? ids : [...ids, component.id]
|
|
38
|
+
: ids.filter((id) => id !== component.id)), disabled: (!component.selectable && !draftIds.includes(component.id))
|
|
39
|
+
|| (draftIds.length >= maxCount && !draftIds.includes(component.id)), badge: !component.selectable ? component.status === 'deprecated'
|
|
40
|
+
? 'No longer available for new selections' : 'Not available for selection' : undefined }, component.id)), !query.isPending && components.length === 0 ? _jsxs("p", { className: 'text-sm text-muted-foreground', children: ["No ", title, " found."] }) : null, query.hasNextPage ? _jsxs(Button, { type: 'button', variant: 'outline', size: 'sm', disabled: query.isFetchingNextPage, onClick: () => void query.fetchNextPage(), children: ["Load more ", title] }) : null] })] });
|
|
41
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { useConnectorAuthorization } from './use-connector-authorization.js';
|
|
2
|
+
export declare function AgentConnectorIcon({ icon }: {
|
|
3
|
+
icon: string | null | undefined;
|
|
4
|
+
}): import("react").JSX.Element;
|
|
5
|
+
export declare function AgentConnectorSelector({ open, onOpenChange, authorization, selectedIds, onChange, maxCount, }: {
|
|
6
|
+
open: boolean;
|
|
7
|
+
onOpenChange: (open: boolean) => void;
|
|
8
|
+
authorization: Pick<ReturnType<typeof useConnectorAuthorization>, 'connections' | 'groups' | 'authorize' | 'connect'>;
|
|
9
|
+
selectedIds: readonly string[];
|
|
10
|
+
onChange: (ids: string[]) => void;
|
|
11
|
+
maxCount: number;
|
|
12
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useAgentEditorHost } from './host.js';
|
|
3
|
+
import { useState } from 'react';
|
|
4
|
+
import { Button } from '@springbrand/site-block';
|
|
5
|
+
import { Plug } from 'lucide-react';
|
|
6
|
+
import { AgentSelectableRow, AgentSelectionDialog } from './agent-selection-dialog.js';
|
|
7
|
+
import { useSelectionDialogOpen } from './use-selection-dialog-open.js';
|
|
8
|
+
export function AgentConnectorIcon({ icon }) {
|
|
9
|
+
return icon ? _jsx("img", { src: icon, alt: '', className: 'size-5 shrink-0 object-contain' })
|
|
10
|
+
: _jsx(Plug, { "aria-hidden": 'true', className: 'size-5 shrink-0 text-muted-foreground' });
|
|
11
|
+
}
|
|
12
|
+
export function AgentConnectorSelector({ open, onOpenChange, authorization, selectedIds, onChange, maxCount, }) {
|
|
13
|
+
const { connectorReturnTo } = useAgentEditorHost();
|
|
14
|
+
const [search, setSearch] = useState('');
|
|
15
|
+
const [draftIds, setDraftIds] = useState([]);
|
|
16
|
+
useSelectionDialogOpen(open, () => setDraftIds([...selectedIds]));
|
|
17
|
+
const { connections, groups, authorize, connect } = authorization;
|
|
18
|
+
const visible = groups.filter((group) => `${group.name} ${group.description}`.toLowerCase().includes(search.trim().toLowerCase()));
|
|
19
|
+
return _jsxs(AgentSelectionDialog, { open: open, onOpenChange: onOpenChange, title: 'Choose Connectors', description: 'Choose preferred apps for this Agent. Other available apps remain usable. Connect your account when required.', search: search, searchPlaceholder: 'Search connectors...', onSearchChange: setSearch, selectedCount: draftIds.length, onSave: () => { onChange(draftIds); onOpenChange(false); }, children: [connections.isPending ? _jsx("p", { role: 'status', children: "Loading connectors..." }) : null, connections.isError ? _jsx("p", { role: 'alert', className: 'text-sm text-destructive', children: "Could not load connectors." }) : null, authorize.isError ? _jsx("p", { role: 'alert', className: 'text-sm text-destructive', children: "Could not start connection." }) : null, visible.map((group) => {
|
|
20
|
+
const connected = group.status === 'connected' || group.connections.some((account) => account.status === 'active');
|
|
21
|
+
const ready = group.credentialSource !== 'user_connection';
|
|
22
|
+
const reconnectConnectionId = group.connections.find((account) => account.status === 'reconnect_required')?.id;
|
|
23
|
+
return _jsx(AgentSelectableRow, { checked: draftIds.includes(group.toolkitId), onCheckedChange: (checked) => setDraftIds((ids) => checked
|
|
24
|
+
? (ids.includes(group.toolkitId) ? ids : [...ids, group.toolkitId])
|
|
25
|
+
: ids.filter((id) => id !== group.toolkitId)), name: group.name, description: group.description, leading: _jsx(AgentConnectorIcon, { icon: group.icon }), badge: connected ? 'Connected' : ready ? 'Ready' : undefined, disabled: draftIds.length >= maxCount && !draftIds.includes(group.toolkitId), trailing: !connected && !ready ? _jsx(Button, { type: 'button', variant: 'outline', size: 'sm', disabled: authorize.isPending, onClick: () => void connect({ toolkitId: group.toolkitId, ...(reconnectConnectionId ? { reconnectConnectionId } : {}) }, connectorReturnTo), children: reconnectConnectionId ? 'Reconnect' : 'Connect' }) : null }, group.toolkitId);
|
|
26
|
+
}), !connections.isPending && visible.length === 0 ? _jsx("p", { className: 'text-sm text-muted-foreground', children: "No connectors found." }) : null, connections.hasNextPage ? _jsx(Button, { type: 'button', variant: 'outline', size: 'sm', disabled: connections.isFetchingNextPage, onClick: () => void connections.fetchNextPage(), children: "Load more connectors" }) : null] });
|
|
27
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type AgentConfigDeletionCheck } from '../../agent-contracts/src/agent.js';
|
|
2
|
+
export type AgentDeletionFeedback = {
|
|
3
|
+
message: string;
|
|
4
|
+
manageScheduledTasks: boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare function deletionBlockerFeedback(check: AgentConfigDeletionCheck | null): AgentDeletionFeedback | null;
|
|
7
|
+
export declare function deletionErrorFeedback(error: unknown): AgentDeletionFeedback;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { agentConfigDeletionBlockerKinds, agentConfigDeletionCheckSchema, } from '../../agent-contracts/src/agent.js';
|
|
2
|
+
import { ApiError } from '@springbrand/http';
|
|
3
|
+
import { errorDefinitions } from '@springbrand/contracts/errors';
|
|
4
|
+
const blockerCode = {
|
|
5
|
+
active_chat: 'AGENT_CONFIG_ACTIVE_CHAT',
|
|
6
|
+
unfinished_run: 'AGENT_CONFIG_ACTIVE_SCHEDULE_RUN',
|
|
7
|
+
enabled_schedule: 'AGENT_CONFIG_ENABLED_SCHEDULE',
|
|
8
|
+
};
|
|
9
|
+
function feedbackForCode(code) {
|
|
10
|
+
return {
|
|
11
|
+
message: errorDefinitions[code].message,
|
|
12
|
+
manageScheduledTasks: code !== 'AGENT_CONFIG_ACTIVE_CHAT',
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export function deletionBlockerFeedback(check) {
|
|
16
|
+
if (!check || check.allowed)
|
|
17
|
+
return null;
|
|
18
|
+
const blockers = agentConfigDeletionBlockerKinds(check);
|
|
19
|
+
if (blockers.length === 0)
|
|
20
|
+
return null;
|
|
21
|
+
return {
|
|
22
|
+
message: blockers.map((kind) => errorDefinitions[blockerCode[kind]].message).join('. '),
|
|
23
|
+
manageScheduledTasks: blockers.some((kind) => kind !== 'active_chat'),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
export function deletionErrorFeedback(error) {
|
|
27
|
+
if (error instanceof ApiError) {
|
|
28
|
+
if (error.status === 401)
|
|
29
|
+
return {
|
|
30
|
+
message: 'Sign in to delete this Agent.',
|
|
31
|
+
manageScheduledTasks: false,
|
|
32
|
+
};
|
|
33
|
+
if (error.status === 403 || error.status === 404)
|
|
34
|
+
return {
|
|
35
|
+
message: 'This Agent is unavailable or you do not have permission to delete it.',
|
|
36
|
+
manageScheduledTasks: false,
|
|
37
|
+
};
|
|
38
|
+
if (error.code === 'AGENT_CONFIG_ACTIVE_CHAT' ||
|
|
39
|
+
error.code === 'AGENT_CONFIG_ACTIVE_SCHEDULE_RUN' ||
|
|
40
|
+
error.code === 'AGENT_CONFIG_ENABLED_SCHEDULE') {
|
|
41
|
+
const details = agentConfigDeletionCheckSchema.safeParse(error.details);
|
|
42
|
+
if (details.success && !details.data.allowed &&
|
|
43
|
+
agentConfigDeletionBlockerKinds(details.data).some((kind) => blockerCode[kind] === error.code)) {
|
|
44
|
+
return deletionBlockerFeedback(details.data) ?? feedbackForCode(error.code);
|
|
45
|
+
}
|
|
46
|
+
return feedbackForCode(error.code);
|
|
47
|
+
}
|
|
48
|
+
if (error.status === 409)
|
|
49
|
+
return {
|
|
50
|
+
message: 'This Agent changed or is already being deleted. Try again.',
|
|
51
|
+
manageScheduledTasks: false,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
return { message: 'Could not check or delete this Agent. Please try again.', manageScheduledTasks: false };
|
|
55
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type AgentConfigInput, type CreateUserAgentConfigInput, type UpdateUserAgentConfigInput, type UserAgentConfig } from '../../agent-contracts/src/agent.js';
|
|
2
|
+
export type AgentDraft = {
|
|
3
|
+
name: string;
|
|
4
|
+
description: string;
|
|
5
|
+
setting: AgentConfigInput;
|
|
6
|
+
};
|
|
7
|
+
export declare function agentDraftEqual(left: AgentDraft, right: AgentDraft): boolean;
|
|
8
|
+
export declare function draftFromAgent(agent: UserAgentConfig): AgentDraft;
|
|
9
|
+
export declare function createInputFromDraft(draft: AgentDraft, slug: string): CreateUserAgentConfigInput;
|
|
10
|
+
export declare function updateInputFromDraft(draft: AgentDraft, revision: number): UpdateUserAgentConfigInput;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { createUserAgentConfigInputSchema, updateUserAgentConfigInputSchema, } from '../../agent-contracts/src/agent.js';
|
|
2
|
+
import { equalJson } from '../../utils/src/index.js';
|
|
3
|
+
export function agentDraftEqual(left, right) {
|
|
4
|
+
return equalJson(left, right);
|
|
5
|
+
}
|
|
6
|
+
export function draftFromAgent(agent) {
|
|
7
|
+
return {
|
|
8
|
+
name: agent.name,
|
|
9
|
+
description: agent.description ?? '',
|
|
10
|
+
setting: agent.setting,
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
export function createInputFromDraft(draft, slug) {
|
|
14
|
+
return createUserAgentConfigInputSchema.parse({
|
|
15
|
+
slug,
|
|
16
|
+
name: draft.name.trim(),
|
|
17
|
+
description: draft.description.trim() || null,
|
|
18
|
+
setting: draft.setting,
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
export function updateInputFromDraft(draft, revision) {
|
|
22
|
+
return updateUserAgentConfigInputSchema.parse({
|
|
23
|
+
revision,
|
|
24
|
+
name: draft.name.trim(),
|
|
25
|
+
description: draft.description.trim() || null,
|
|
26
|
+
setting: draft.setting,
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type ModelPresetAccess } from '../../agent-contracts/src/model.js';
|
|
2
|
+
import { type UserAgentConfig } from '../../agent-contracts/src/agent.js';
|
|
3
|
+
import type { AgentDeletionFeedback } from './agent-deletion-feedback.js';
|
|
4
|
+
import type { AgentDraft } from './agent-draft.js';
|
|
5
|
+
type AgentEditorCommonProps = {
|
|
6
|
+
userId: string;
|
|
7
|
+
draft: AgentDraft;
|
|
8
|
+
models: readonly ModelPresetAccess[];
|
|
9
|
+
modelsLoading: boolean;
|
|
10
|
+
modelsError: boolean;
|
|
11
|
+
saving: boolean;
|
|
12
|
+
error: string | null;
|
|
13
|
+
readOnly?: boolean;
|
|
14
|
+
onDraftChange: (update: (current: AgentDraft) => AgentDraft) => void;
|
|
15
|
+
onSave: () => void;
|
|
16
|
+
onBack: () => void;
|
|
17
|
+
};
|
|
18
|
+
export type AgentEditorProps = AgentEditorCommonProps & ({
|
|
19
|
+
mode: 'create';
|
|
20
|
+
} | {
|
|
21
|
+
mode: 'edit';
|
|
22
|
+
agent: UserAgentConfig;
|
|
23
|
+
dirty: boolean;
|
|
24
|
+
checkingDelete: boolean;
|
|
25
|
+
deleteBlocker: AgentDeletionFeedback | null;
|
|
26
|
+
deleteCheckError: string | null;
|
|
27
|
+
onDelete: () => void;
|
|
28
|
+
});
|
|
29
|
+
export declare function AgentEditor(props: AgentEditorProps): import("react").JSX.Element;
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useEffect, useState } from 'react';
|
|
3
|
+
import { useQueries, useQuery } from '@tanstack/react-query';
|
|
4
|
+
import { Alert, AlertDescription, Badge, Button, Collapsible, CollapsibleContent, CollapsibleTrigger, Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Field, FieldLabel, Input, NativeSelect, NativeSelectOption, Textarea, } from '@springbrand/site-block';
|
|
5
|
+
import { THINKING_LEVELS, modelSelectionFromPresetId } from '../../agent-contracts/src/model.js';
|
|
6
|
+
import { AGENT_CONFIG_SELECTION_LIMIT } from '../../agent-contracts/src/agent.js';
|
|
7
|
+
import { ATTACHMENT_MAX_COUNT } from '../../utils/src/index.js';
|
|
8
|
+
import { selectedConnectorToolkitIds } from '../../agent-contracts/src/mcp.js';
|
|
9
|
+
import { ChevronDown, ChevronLeft, CircleCheck, Trash2, X } from 'lucide-react';
|
|
10
|
+
import { useAgentEditorHost } from './host.js';
|
|
11
|
+
import { skillQueryKeys } from './client/index.js';
|
|
12
|
+
import { useConnectorAuthorization } from './use-connector-authorization.js';
|
|
13
|
+
import { AgentResourceSection } from './agent-resource-section.js';
|
|
14
|
+
import { AgentSkillSelector, AgentReferenceSelector } from './agent-library-selectors.js';
|
|
15
|
+
import { AgentActionSelector } from './agent-action-selector.js';
|
|
16
|
+
import { AgentComponentSelector } from './agent-component-selector.js';
|
|
17
|
+
import { AgentConnectorIcon, AgentConnectorSelector } from './agent-connector-selector.js';
|
|
18
|
+
import { ReferenceManagementDialog } from './reference-management-dialog.js';
|
|
19
|
+
import { ReferenceFileIcon, ReferencePreviewDialog } from './agent-reference-files.js';
|
|
20
|
+
import { SkillsManagementPage } from './skills-management-page.js';
|
|
21
|
+
export function AgentEditor(props) {
|
|
22
|
+
const host = useAgentEditorHost();
|
|
23
|
+
const { skills: { listSelectedSkills }, actions: { getAction }, references: { getReference } } = host.client;
|
|
24
|
+
const { userId, draft, models, modelsLoading, modelsError, saving, error, onDraftChange, onSave, onBack } = props;
|
|
25
|
+
const { mode } = props;
|
|
26
|
+
const readOnly = props.readOnly ?? false;
|
|
27
|
+
const readOnlySelections = readOnly || (mode === 'edit' && props.agent.is_default);
|
|
28
|
+
const deleteBlocker = mode === 'edit' ? props.deleteBlocker : null;
|
|
29
|
+
const deleteCheckError = mode === 'edit' ? props.deleteCheckError : null;
|
|
30
|
+
const [promptOpen, setPromptOpen] = useState(false);
|
|
31
|
+
const [promptText, setPromptText] = useState('');
|
|
32
|
+
const [skillAddOpen, setSkillAddOpen] = useState(false);
|
|
33
|
+
const [skillManageOpen, setSkillManageOpen] = useState(false);
|
|
34
|
+
const [referenceAddOpen, setReferenceAddOpen] = useState(false);
|
|
35
|
+
const [referenceManageOpen, setReferenceManageOpen] = useState(false);
|
|
36
|
+
const [connectorAddOpen, setConnectorAddOpen] = useState(false);
|
|
37
|
+
const [actionAddOpen, setActionAddOpen] = useState(false);
|
|
38
|
+
const [preview, setPreview] = useState(null);
|
|
39
|
+
const updateSetting = (update) => onDraftChange((current) => ({ ...current, setting: update(current.setting) }));
|
|
40
|
+
const selectedModel = draft.setting.presetId
|
|
41
|
+
?? models.find(({ providerModel }) => providerModel === draft.setting.model)?.id
|
|
42
|
+
?? '';
|
|
43
|
+
const connectorIds = selectedConnectorToolkitIds(draft.setting.mcpServers);
|
|
44
|
+
const componentSelectionCount = draft.setting.connectorIds.length + draft.setting.extensionIds.length;
|
|
45
|
+
const maxConnectorCount = AGENT_CONFIG_SELECTION_LIMIT - draft.setting.skillIds.length
|
|
46
|
+
- draft.setting.actionIds.length - (draft.setting.mcpServers.length - connectorIds.length) - componentSelectionCount;
|
|
47
|
+
const maxSkillCount = AGENT_CONFIG_SELECTION_LIMIT - draft.setting.mcpServers.length
|
|
48
|
+
- draft.setting.actionIds.length - componentSelectionCount;
|
|
49
|
+
const skills = useQuery({
|
|
50
|
+
queryKey: skillQueryKeys.selected(userId, draft.setting.skillIds),
|
|
51
|
+
queryFn: ({ signal }) => listSelectedSkills(draft.setting.skillIds, signal),
|
|
52
|
+
enabled: draft.setting.skillIds.length > 0,
|
|
53
|
+
});
|
|
54
|
+
const actions = useQueries({ queries: draft.setting.actionIds.map((id) => ({
|
|
55
|
+
queryKey: ['actions', 'detail', id], queryFn: ({ signal }) => getAction(id, signal),
|
|
56
|
+
})) });
|
|
57
|
+
const references = useQueries({ queries: (draft.setting.referenceFileIds ?? []).map((id) => ({
|
|
58
|
+
queryKey: ['user-references', 'detail', id], queryFn: ({ signal }) => getReference(id, signal),
|
|
59
|
+
})) });
|
|
60
|
+
const connectorAuthorization = useConnectorAuthorization(userId, host.client.connectors, host.onConnectorEvent);
|
|
61
|
+
const { connections } = connectorAuthorization;
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (connections.hasNextPage && !connections.isFetching && !connections.isFetchNextPageError) {
|
|
64
|
+
void connections.fetchNextPage();
|
|
65
|
+
}
|
|
66
|
+
}, [connections.hasNextPage, connections.isFetching, connections.isFetchNextPageError, connections.fetchNextPage]);
|
|
67
|
+
function setConnectorIds(ids) {
|
|
68
|
+
updateSetting((setting) => ({
|
|
69
|
+
...setting,
|
|
70
|
+
mcpServers: [
|
|
71
|
+
...setting.mcpServers.filter((selection) => !('kind' in selection)),
|
|
72
|
+
...ids.map((serverId) => ({ serverId, kind: 'connector_toolkit' })),
|
|
73
|
+
],
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
76
|
+
return _jsxs("main", { className: 'flex min-h-0 min-w-0 flex-1 flex-col overflow-y-auto bg-background', children: [_jsxs("form", { className: 'flex min-h-full flex-col', onSubmit: (event) => { event.preventDefault(); if (!readOnly && !saving)
|
|
77
|
+
onSave(); }, children: [_jsxs("fieldset", { disabled: saving, className: 'mx-auto w-full min-w-0 max-w-3xl flex-1 space-y-4 border-0 px-4 pt-5 pb-12 md:px-8 md:pt-8', children: [_jsxs("header", { className: 'mb-5 space-y-2', children: [_jsxs(Button, { type: 'button', variant: 'ghost', size: 'sm', className: '-ml-2 md:hidden', onClick: onBack, children: [_jsx(ChevronLeft, { "aria-hidden": 'true' }), " Agents"] }), _jsxs("div", { className: 'flex flex-wrap items-center gap-2', children: [_jsx("h1", { className: 'text-2xl font-semibold tracking-tight', children: mode === 'create' ? 'New Agent' : 'Settings' }), mode === 'edit' && props.agent.is_default ? _jsx(Badge, { variant: 'secondary', children: "Default" }) : null] }), _jsx("p", { className: 'text-sm text-muted-foreground', children: mode === 'create'
|
|
78
|
+
? 'Choose how this Agent should work.'
|
|
79
|
+
: 'Changes to references affect new Chats. Existing Chats keep their files.' })] }), _jsxs(AgentResourceSection, { title: 'Behavior', children: [_jsxs("div", { className: 'grid gap-4 sm:grid-cols-2', children: [_jsxs(Field, { children: [_jsx(FieldLabel, { htmlFor: 'agent-name', children: "Name" }), _jsx(Input, { id: 'agent-name', disabled: readOnlySelections, required: true, maxLength: 200, value: draft.name, onChange: (event) => onDraftChange((current) => ({ ...current, name: event.target.value })) })] }), _jsxs(Field, { children: [_jsx(FieldLabel, { htmlFor: 'agent-description', children: "Description" }), _jsx(Input, { id: 'agent-description', disabled: readOnlySelections, maxLength: 2000, value: draft.description, onChange: (event) => onDraftChange((current) => ({ ...current, description: event.target.value })) })] })] }), _jsxs("div", { className: 'space-y-2', children: [_jsx("span", { className: 'text-sm font-medium', children: "System prompt" }), _jsxs("button", { type: 'button', className: 'block min-h-24 w-full rounded-lg border border-border bg-muted/20 p-3 text-left text-sm hover:bg-muted/40 focus-visible:ring-2 focus-visible:ring-ring', onClick: () => { setPromptText(draft.setting.systemPrompt ?? ''); setPromptOpen(true); }, children: [_jsx("span", { className: 'line-clamp-3 whitespace-pre-wrap break-words text-muted-foreground', children: draft.setting.systemPrompt || 'Add instructions for the Agent...' }), _jsx("span", { className: 'mt-2 block text-xs text-muted-foreground', children: "Click to view and edit the full prompt" })] })] })] }), _jsxs(AgentResourceSection, { title: 'Model', children: [_jsxs("div", { className: 'grid gap-4 sm:grid-cols-2', children: [_jsxs(Field, { children: [_jsx(FieldLabel, { htmlFor: 'agent-model', children: "Model" }), _jsxs(NativeSelect, { id: 'agent-model', value: selectedModel, disabled: readOnly || modelsLoading || modelsError, onChange: (event) => {
|
|
80
|
+
const presetId = event.target.value;
|
|
81
|
+
updateSetting((setting) => {
|
|
82
|
+
if (!presetId)
|
|
83
|
+
return { ...setting, presetId: null, model: null };
|
|
84
|
+
const selection = modelSelectionFromPresetId(presetId);
|
|
85
|
+
return { ...setting, presetId: selection.presetId, model: selection.model, thinking: selection.thinking };
|
|
86
|
+
});
|
|
87
|
+
}, children: [_jsx(NativeSelectOption, { value: '', children: "Platform default" }), models.map((model) => _jsxs(NativeSelectOption, { value: model.id, disabled: !model.available, children: [model.name, model.available ? '' : ` (requires ${model.requiredPlan})`] }, model.id))] })] }), _jsxs(Field, { children: [_jsx(FieldLabel, { htmlFor: 'agent-thinking', children: "Thinking" }), _jsx(NativeSelect, { id: 'agent-thinking', value: draft.setting.thinking, disabled: readOnly, onChange: (event) => updateSetting((setting) => ({ ...setting,
|
|
88
|
+
thinking: event.target.value,
|
|
89
|
+
})), children: THINKING_LEVELS.map((level) => _jsx(NativeSelectOption, { value: level, children: level }, level)) })] })] }), modelsError ? _jsx("p", { role: 'alert', className: 'text-sm text-destructive', children: "Could not load models." }) : null] }), _jsx(AgentResourceSection, { title: 'Connectors', count: connectorIds.length, onAdd: readOnlySelections ? undefined : () => setConnectorAddOpen(true), children: connectorIds.length === 0 ? _jsx("p", { className: 'text-sm text-muted-foreground', children: "None selected." }) :
|
|
90
|
+
_jsx("ul", { className: 'space-y-2', children: connectorIds.map((id) => {
|
|
91
|
+
const connector = connectorAuthorization.groups.find((item) => item.toolkitId === id);
|
|
92
|
+
const connected = connector?.status === 'connected' || connector?.connections.some((account) => account.status === 'active');
|
|
93
|
+
return _jsxs("li", { className: 'flex items-center gap-3 rounded-lg border border-border px-3 py-2', children: [_jsx(AgentConnectorIcon, { icon: connector?.icon }), _jsx("span", { className: 'min-w-0 flex-1 truncate text-sm', children: connector?.name ?? id }), connected ? _jsx(CircleCheck, { "aria-label": 'Connected', className: 'size-4 text-green-600' }) : null, _jsx(Button, { type: 'button', variant: 'ghost', size: 'icon-sm', disabled: readOnlySelections, "aria-label": `Remove ${connector?.name ?? id} from Agent`, onClick: () => setConnectorIds(connectorIds.filter((item) => item !== id)), children: _jsx(X, { "aria-hidden": 'true' }) })] }, id);
|
|
94
|
+
}) }) }), _jsx(AgentComponentSelector, { readOnly: readOnlySelections, userId: userId, kind: 'connector', selectedIds: draft.setting.connectorIds, onChange: (connectorIds) => updateSetting((setting) => ({ ...setting, connectorIds })), maxCount: AGENT_CONFIG_SELECTION_LIMIT - draft.setting.skillIds.length - draft.setting.actionIds.length
|
|
95
|
+
- draft.setting.mcpServers.length - draft.setting.extensionIds.length }), _jsx(AgentComponentSelector, { readOnly: readOnlySelections, userId: userId, kind: 'extension', selectedIds: draft.setting.extensionIds, onChange: (extensionIds) => updateSetting((setting) => ({ ...setting, extensionIds })), maxCount: AGENT_CONFIG_SELECTION_LIMIT - draft.setting.skillIds.length - draft.setting.actionIds.length
|
|
96
|
+
- draft.setting.mcpServers.length - draft.setting.connectorIds.length }), _jsx(AgentResourceSection, { title: 'Actions', count: draft.setting.actionIds.length, onAdd: readOnlySelections ? undefined : () => setActionAddOpen(true), children: _jsx(SelectedActions, { readOnly: readOnlySelections, ids: draft.setting.actionIds, items: actions.map((query) => query.data), onChange: (actionIds) => updateSetting((setting) => ({ ...setting, actionIds })) }) }), _jsx(AgentResourceSection, { title: 'Skills', count: draft.setting.skillIds.length, onAdd: readOnlySelections ? undefined : () => setSkillAddOpen(true), onManage: readOnly ? undefined : () => setSkillManageOpen(true), children: draft.setting.skillIds.length === 0 ? _jsx("p", { className: 'text-sm text-muted-foreground', children: "None selected." }) :
|
|
97
|
+
_jsx("ul", { className: 'space-y-2', children: draft.setting.skillIds.map((id) => {
|
|
98
|
+
const skill = skills.data?.data.find((item) => item.id === id);
|
|
99
|
+
return _jsxs("li", { className: 'flex items-center gap-2 rounded-lg border border-border px-3 py-2', children: [_jsx("span", { className: 'min-w-0 flex-1 truncate text-sm', children: skill?.name ?? id }), skill?.deleted ? _jsx(Badge, { variant: 'secondary', children: "Deleted" }) : null, skill ? _jsx(Badge, { variant: 'outline', children: skill.origin === 'upload' ? 'My Skill' : 'Platform' }) : null, _jsx(Button, { type: 'button', variant: 'ghost', size: 'icon-sm', disabled: readOnlySelections, "aria-label": `Remove ${skill?.name ?? id} from Agent`, onClick: () => updateSetting((setting) => ({ ...setting, skillIds: setting.skillIds.filter((item) => item !== id) })), children: _jsx(X, { "aria-hidden": 'true' }) })] }, id);
|
|
100
|
+
}) }) }), _jsx(AgentResourceSection, { title: 'References', count: draft.setting.referenceFileIds?.length ?? 0, onAdd: readOnly ? undefined : () => setReferenceAddOpen(true), onManage: readOnly ? undefined : () => setReferenceManageOpen(true), children: (draft.setting.referenceFileIds ?? []).length === 0 ? _jsx("p", { className: 'text-sm text-muted-foreground', children: "No references selected." }) :
|
|
101
|
+
_jsx("ul", { className: 'space-y-2', children: (draft.setting.referenceFileIds ?? []).map((id, index) => {
|
|
102
|
+
const reference = references[index]?.data;
|
|
103
|
+
return _jsxs("li", { className: 'flex items-center gap-2 rounded-lg border border-border px-3 py-2', children: [_jsx(ReferenceFileIcon, { file: reference ?? null }), _jsx("button", { type: 'button', className: 'min-w-0 flex-1 truncate text-left text-sm hover:underline focus-visible:ring-2 focus-visible:ring-ring', disabled: !reference, onClick: () => { if (reference)
|
|
104
|
+
setPreview(reference); }, children: reference?.name ?? id }), _jsx(Button, { type: 'button', variant: 'ghost', size: 'icon-sm', disabled: readOnly, "aria-label": `Remove ${reference?.name ?? id} from Agent`, onClick: () => updateSetting((setting) => ({ ...setting,
|
|
105
|
+
referenceFileIds: (setting.referenceFileIds ?? []).filter((item) => item !== id),
|
|
106
|
+
})), children: _jsx(X, { "aria-hidden": 'true' }) })] }, id);
|
|
107
|
+
}) }) }), deleteBlocker ? _jsx(Alert, { variant: 'destructive', role: 'alert', children: _jsxs(AlertDescription, { children: [deleteBlocker.message, deleteBlocker.manageScheduledTasks ? _jsxs(_Fragment, { children: [" ", host.scheduledTasksLink] }) : null] }) }) : null, deleteCheckError ? _jsx("p", { role: 'alert', className: 'text-sm text-destructive', children: deleteCheckError }) : null] }), !readOnly ? _jsx("div", { className: 'sticky bottom-0 z-10 border-t border-border bg-background px-4 py-3 pb-[calc(env(safe-area-inset-bottom)+0.75rem)] md:px-8', children: _jsxs("div", { className: 'mx-auto flex w-full max-w-3xl flex-wrap items-center gap-3', children: [_jsx(Button, { type: 'submit', disabled: saving || (mode === 'edit' ? !props.dirty : !draft.name.trim()), loading: saving, children: mode === 'create' ? saving ? 'Creating…' : 'Create Agent' : 'Save changes' }), mode === 'create' ? _jsx(Button, { type: 'button', variant: 'outline', disabled: saving, onClick: onBack, children: "Cancel" })
|
|
108
|
+
: !props.agent.is_default ? _jsxs(Button, { type: 'button', variant: 'outline', disabled: props.checkingDelete || saving, loading: props.checkingDelete, onClick: props.onDelete, children: [_jsx(Trash2, { "aria-hidden": 'true' }), " Delete Agent"] }) : null, mode === 'edit' && props.dirty ? _jsx("span", { className: 'text-xs text-muted-foreground', children: "Unsaved changes" }) : null, error ? _jsx(Alert, { variant: 'destructive', role: 'alert', className: 'w-full', children: _jsx(AlertDescription, { children: error }) }) : null] }) }) : null] }), _jsx(Dialog, { open: promptOpen, onOpenChange: setPromptOpen, children: _jsxs(DialogContent, { className: 'flex max-h-[calc(100dvh-2rem)] min-h-0 flex-col sm:max-w-3xl', children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "System prompt" }), _jsx(DialogDescription, { children: "These instructions guide this Agent's behavior." })] }), _jsx(Textarea, { "aria-label": 'Full system prompt', rows: 18, maxLength: 50_000, value: promptText, readOnly: readOnly, className: 'min-h-0 flex-1 resize-none overflow-y-auto', onChange: (event) => setPromptText(event.target.value) }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: 'button', variant: 'outline', onClick: () => setPromptOpen(false), children: "Cancel" }), !readOnly ? _jsx(Button, { type: 'button', onClick: () => { updateSetting((setting) => ({ ...setting, systemPrompt: promptText || null })); setPromptOpen(false); }, children: "Done" }) : null] })] }) }), _jsx(AgentConnectorSelector, { open: connectorAddOpen, onOpenChange: setConnectorAddOpen, authorization: connectorAuthorization, selectedIds: connectorIds, onChange: setConnectorIds, maxCount: maxConnectorCount }), _jsx(AgentActionSelector, { open: actionAddOpen, onOpenChange: setActionAddOpen, userId: userId, selectedIds: draft.setting.actionIds, selectedItems: actions.map((query) => query.data), onChange: (actionIds) => updateSetting((setting) => ({ ...setting, actionIds })), maxActionCount: AGENT_CONFIG_SELECTION_LIMIT - draft.setting.skillIds.length - draft.setting.mcpServers.length - componentSelectionCount }), _jsx(AgentSkillSelector, { open: skillAddOpen, onOpenChange: setSkillAddOpen, userId: userId, selectedIds: draft.setting.skillIds, onChange: (skillIds) => updateSetting((setting) => ({ ...setting, skillIds })), maxCount: maxSkillCount }), _jsx(AgentReferenceSelector, { open: referenceAddOpen, onOpenChange: setReferenceAddOpen, userId: userId, selectedIds: draft.setting.referenceFileIds ?? [], onChange: (referenceFileIds) => updateSetting((setting) => ({ ...setting, referenceFileIds })), maxCount: ATTACHMENT_MAX_COUNT }), _jsx(Dialog, { open: skillManageOpen, onOpenChange: setSkillManageOpen, children: _jsxs(DialogContent, { className: 'flex max-h-[calc(100dvh-2rem-env(safe-area-inset-top)-env(safe-area-inset-bottom))] min-h-0 flex-col overflow-hidden sm:max-w-3xl', children: [_jsxs(DialogHeader, { children: [_jsx(DialogTitle, { children: "Manage Skills" }), _jsx(DialogDescription, { children: "Upload, preview, or delete your Skills." })] }), skillManageOpen ? _jsx(SkillsManagementPage, { userId: userId, embedded: true }) : null] }) }), _jsx(ReferenceManagementDialog, { open: referenceManageOpen, onOpenChange: setReferenceManageOpen, userId: userId, selectedIds: draft.setting.referenceFileIds ?? [] }), _jsx(ReferencePreviewDialog, { file: preview, onOpenChange: (open) => { if (!open)
|
|
109
|
+
setPreview(null); } })] });
|
|
110
|
+
}
|
|
111
|
+
function SelectedActions({ ids, items, onChange, readOnly }) {
|
|
112
|
+
if (ids.length === 0)
|
|
113
|
+
return _jsx("p", { className: 'text-sm text-muted-foreground', children: "None selected." });
|
|
114
|
+
const groups = new Map();
|
|
115
|
+
ids.forEach((id, index) => {
|
|
116
|
+
const item = items[index];
|
|
117
|
+
const key = item?.supplier?.id ?? 'other';
|
|
118
|
+
const group = groups.get(key) ?? {
|
|
119
|
+
name: item?.supplier?.name ?? 'Other Actions', iconUrl: item?.supplier?.iconUrl ?? null, actions: [],
|
|
120
|
+
};
|
|
121
|
+
group.actions.push({ id, name: item?.name ?? id });
|
|
122
|
+
groups.set(key, group);
|
|
123
|
+
});
|
|
124
|
+
return _jsx("div", { className: 'space-y-2', children: [...groups.entries()].map(([key, group]) => _jsxs(Collapsible, { defaultOpen: true, className: 'rounded-lg border border-border', children: [_jsxs("div", { className: 'flex items-center gap-2 px-2', children: [_jsx(CollapsibleTrigger, { asChild: true, children: _jsxs(Button, { type: 'button', variant: 'ghost', className: 'min-w-0 flex-1 justify-start gap-2', children: [group.iconUrl ? _jsx("img", { src: group.iconUrl, alt: '', className: 'size-6 shrink-0 object-contain' }) : null, group.name, " ", _jsx("span", { className: 'text-xs text-muted-foreground', children: group.actions.length }), _jsx(ChevronDown, { "aria-hidden": 'true', className: 'size-4' })] }) }), _jsx(Button, { type: 'button', variant: 'ghost', size: 'icon-sm', disabled: readOnly, "aria-label": `Remove all ${group.name} from Agent`, onClick: () => { const groupIds = new Set(group.actions.map((action) => action.id)); onChange(ids.filter((id) => !groupIds.has(id))); }, children: _jsx(X, { "aria-hidden": 'true' }) })] }), _jsx(CollapsibleContent, { className: 'space-y-1 border-t border-border p-2', children: group.actions.map((action) => _jsxs("div", { className: 'flex items-center gap-2 rounded-md px-2 py-1', children: [_jsx("span", { className: 'min-w-0 flex-1 truncate text-sm', children: action.name }), _jsx(Button, { type: 'button', variant: 'ghost', size: 'icon-sm', disabled: readOnly, "aria-label": `Remove ${action.name} from Agent`, onClick: () => onChange(ids.filter((id) => id !== action.id)), children: _jsx(X, { "aria-hidden": 'true' }) })] }, action.id)) })] }, key)) });
|
|
125
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare function AgentSkillSelector({ open, onOpenChange, userId, selectedIds, onChange, maxCount, }: {
|
|
2
|
+
open: boolean;
|
|
3
|
+
onOpenChange: (open: boolean) => void;
|
|
4
|
+
userId: string;
|
|
5
|
+
selectedIds: readonly string[];
|
|
6
|
+
onChange: (ids: string[]) => void;
|
|
7
|
+
maxCount: number;
|
|
8
|
+
}): import("react").JSX.Element;
|
|
9
|
+
export declare function AgentReferenceSelector({ open, onOpenChange, userId, selectedIds, onChange, maxCount, }: {
|
|
10
|
+
open: boolean;
|
|
11
|
+
onOpenChange: (open: boolean) => void;
|
|
12
|
+
userId: string;
|
|
13
|
+
selectedIds: readonly string[];
|
|
14
|
+
onChange: (ids: string[]) => void;
|
|
15
|
+
maxCount: number;
|
|
16
|
+
}): import("react").JSX.Element;
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { useDeferredValue, useState } from 'react';
|
|
3
|
+
import { useInfiniteQuery } from '@tanstack/react-query';
|
|
4
|
+
import { Button } from '@springbrand/site-block';
|
|
5
|
+
import { skillQueryKeys, userReferenceQueryKeys } from './client/index.js';
|
|
6
|
+
import { useAgentEditorHost } from './host.js';
|
|
7
|
+
import { AgentSelectableRow, AgentSelectionDialog } from './agent-selection-dialog.js';
|
|
8
|
+
import { ReferenceFileIcon, ReferencePreviewDialog } from './agent-reference-files.js';
|
|
9
|
+
import { useSelectionDialogOpen } from './use-selection-dialog-open.js';
|
|
10
|
+
function nextPage(last) {
|
|
11
|
+
return last.page * last.pageSize < last.total ? last.page + 1 : undefined;
|
|
12
|
+
}
|
|
13
|
+
function toggle(ids, id, checked) {
|
|
14
|
+
return checked ? (ids.includes(id) ? [...ids] : [...ids, id]) : ids.filter((item) => item !== id);
|
|
15
|
+
}
|
|
16
|
+
export function AgentSkillSelector({ open, onOpenChange, userId, selectedIds, onChange, maxCount, }) {
|
|
17
|
+
const { client: { skills: { listSkills } } } = useAgentEditorHost();
|
|
18
|
+
const [search, setSearch] = useState('');
|
|
19
|
+
const q = useDeferredValue(search);
|
|
20
|
+
const [draftIds, setDraftIds] = useState([]);
|
|
21
|
+
useSelectionDialogOpen(open, () => setDraftIds([...selectedIds]));
|
|
22
|
+
const query = useInfiniteQuery({
|
|
23
|
+
queryKey: skillQueryKeys.collection(userId, 'selectable', q),
|
|
24
|
+
queryFn: ({ pageParam, signal }) => listSkills({ view: 'selectable', q, page: pageParam, pageSize: 50 }, signal),
|
|
25
|
+
initialPageParam: 1, getNextPageParam: nextPage, enabled: open,
|
|
26
|
+
});
|
|
27
|
+
const skills = query.data?.pages.flatMap((page) => page.data) ?? [];
|
|
28
|
+
return _jsxs(AgentSelectionDialog, { open: open, onOpenChange: onOpenChange, title: 'Choose Skills', description: 'Choose platform Skills or Skills you uploaded. Manage your uploaded Skills separately.', search: search, searchPlaceholder: 'Search skills...', onSearchChange: setSearch, selectedCount: draftIds.length, onSave: () => { onChange(draftIds); onOpenChange(false); }, children: [query.isPending ? _jsx("p", { role: 'status', children: "Loading Skills..." }) : null, query.isError ? _jsx("p", { role: 'alert', className: 'text-sm text-destructive', children: "Could not load Skills." }) : null, skills.map((skill) => _jsx(AgentSelectableRow, { checked: draftIds.includes(skill.id), onCheckedChange: (checked) => setDraftIds((ids) => toggle(ids, skill.id, checked)), name: skill.name, description: skill.description, badge: skill.origin === 'upload' ? 'My Skill' : 'Platform', disabled: !skill.available || (draftIds.length >= maxCount && !draftIds.includes(skill.id)) }, skill.id)), !query.isPending && skills.length === 0 ? _jsx("p", { className: 'text-sm text-muted-foreground', children: "No Skills found." }) : null, query.hasNextPage ? _jsx(Button, { type: 'button', variant: 'outline', size: 'sm', disabled: query.isFetchingNextPage, onClick: () => void query.fetchNextPage(), children: "Load more Skills" }) : null] });
|
|
29
|
+
}
|
|
30
|
+
export function AgentReferenceSelector({ open, onOpenChange, userId, selectedIds, onChange, maxCount, }) {
|
|
31
|
+
const { client: { references: { listReferences } } } = useAgentEditorHost();
|
|
32
|
+
const [search, setSearch] = useState('');
|
|
33
|
+
const q = useDeferredValue(search);
|
|
34
|
+
const [draftIds, setDraftIds] = useState([]);
|
|
35
|
+
const [preview, setPreview] = useState(null);
|
|
36
|
+
useSelectionDialogOpen(open, () => setDraftIds([...selectedIds]));
|
|
37
|
+
const query = useInfiniteQuery({
|
|
38
|
+
queryKey: userReferenceQueryKeys.collection(userId, q),
|
|
39
|
+
queryFn: ({ pageParam, signal }) => listReferences({ q, page: pageParam, pageSize: 50 }, signal),
|
|
40
|
+
initialPageParam: 1, getNextPageParam: nextPage, enabled: open,
|
|
41
|
+
});
|
|
42
|
+
const references = query.data?.pages.flatMap((page) => page.data) ?? [];
|
|
43
|
+
return _jsxs(_Fragment, { children: [_jsxs(AgentSelectionDialog, { open: open, onOpenChange: onOpenChange, title: 'Choose References', description: 'Choose files already in your reference library. Files are copied into new Chats when they start.', search: search, searchPlaceholder: 'Search references...', onSearchChange: setSearch, selectedCount: draftIds.length, onSave: () => { onChange(draftIds); onOpenChange(false); }, children: [query.isPending ? _jsx("p", { role: 'status', children: "Loading references..." }) : null, query.isError ? _jsx("p", { role: 'alert', className: 'text-sm text-destructive', children: "Could not load references." }) : null, references.map((reference) => _jsx(AgentSelectableRow, { checked: draftIds.includes(reference.id), onCheckedChange: (checked) => setDraftIds((ids) => toggle(ids, reference.id, checked)), name: reference.name, description: reference.mediaType, badge: reference.status === 'disabled' ? 'Disabled' : undefined, leading: _jsx(ReferenceFileIcon, { file: reference }), disabled: reference.status !== 'active' || (draftIds.length >= maxCount && !draftIds.includes(reference.id)), trailing: _jsx(Button, { type: 'button', variant: 'ghost', size: 'sm', onClick: () => setPreview(reference), children: "Preview" }) }, reference.id)), !query.isPending && references.length === 0 ? _jsx("p", { className: 'text-sm text-muted-foreground', children: "No references found." }) : null, query.hasNextPage ? _jsx(Button, { type: 'button', variant: 'outline', size: 'sm', disabled: query.isFetchingNextPage, onClick: () => void query.fetchNextPage(), children: "Load more references" }) : null] }), _jsx(ReferencePreviewDialog, { file: preview, onOpenChange: (next) => { if (!next)
|
|
44
|
+
setPreview(null); } })] });
|
|
45
|
+
}
|