@wowyuarm/dsh-agent-team 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -1
- package/README.zh.md +9 -1
- package/package.json +12 -3
- package/packages/agent-team/README.md +3 -3
- package/packages/agent-team/README.zh.md +3 -3
- package/packages/agent-team/lib/attachments.js +11 -12
- package/packages/agent-team/lib/auto-compaction.js +163 -0
- package/packages/agent-team/lib/index.js +252 -139
- package/packages/agent-team/lib/ledger.js +344 -170
- package/packages/agent-team/lib/recovery.js +30 -35
- package/packages/agent-team/lib/spec.js +27 -11
- package/packages/agent-team/lib/typert.host.js +197 -88
- package/packages/agent-team/lib/typert.remote-client.d.ts +3 -1
- package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
- package/packages/agent-team/lib/typert.remote-client.js +139 -59
- package/packages/agent-team/lib/types/attachments.d.ts +2 -2
- package/packages/agent-team/lib/types/attachments.d.ts.map +1 -1
- package/packages/agent-team/lib/types/auto-compaction.d.ts +38 -0
- package/packages/agent-team/lib/types/auto-compaction.d.ts.map +1 -0
- package/packages/agent-team/lib/types/index.d.ts +52 -19
- package/packages/agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/agent-team/lib/types/ledger.d.ts +29 -2
- package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
- package/packages/agent-team/lib/types/recovery.d.ts +15 -16
- package/packages/agent-team/lib/types/recovery.d.ts.map +1 -1
- package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types.d.ts +69 -30
- package/packages/agent-team/lib/types/types.d.ts.map +1 -1
- package/packages/agent-team/preset/team-member/agent.cordis.yml +7 -2
- package/packages/client-agent-team/README.md +2 -1
- package/packages/client-agent-team/README.zh.md +2 -1
- package/packages/client-agent-team/lib/client.js +1577 -863
- package/packages/client-agent-team/lib/client.js.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +35 -174
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +20 -30
- package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +3 -2
- package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts +6 -1
- package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamComposer.js +4 -5
- package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/TeamConversation.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamConversation.js +3 -3
- package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts +34 -0
- package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/TeamMemberEditor.js +169 -0
- package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.d.ts +17 -0
- package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.js +120 -0
- package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts +4 -1
- package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamMessage.js +94 -47
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts +2 -1
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +81 -39
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +2 -1
- package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/index.js +72 -1
- package/packages/client-agent-team/lib/types/client/locales.d.ts +24 -0
- package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/locales.js +24 -0
- package/packages/client-agent-team/lib/types/client/member-session-input.d.ts +22 -0
- package/packages/client-agent-team/lib/types/client/member-session-input.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/member-session-input.js +98 -0
- package/packages/client-agent-team/lib/types/client/navigation.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/navigation.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/navigation.js +12 -6
- package/packages/client-agent-team/lib/types/client/requests.d.ts +16 -0
- package/packages/client-agent-team/lib/types/client/requests.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/requests.js +22 -0
- package/packages/client-agent-team/lib/types/client/slots.d.ts +2 -1
- package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/task-refs.d.ts +6 -1
- package/packages/client-agent-team/lib/types/client/task-refs.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/task-refs.js +19 -0
- package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +52 -3
- package/packages/client-agent-team/lib/types/client/team-formatters.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/team-formatters.js +74 -4
- package/packages/client-agent-team/lib/types/client/team-membership.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/team-membership.js +2 -1
- package/packages/tool-agent-team/README.md +7 -7
- package/packages/tool-agent-team/README.zh.md +7 -7
- package/packages/tool-agent-team/lib/index.js +38 -28
- package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamInboxPanel.d.ts +0 -13
- package/packages/client-agent-team/lib/types/client/TeamInboxPanel.d.ts.map +0 -1
- package/packages/client-agent-team/lib/types/client/TeamInboxPanel.js +0 -90
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamAgentsPanel.d.ts","sourceRoot":"","sources":["../../../src/client/TeamAgentsPanel.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,yBAAyB,EACzB,2BAA2B,
|
|
1
|
+
{"version":3,"file":"TeamAgentsPanel.d.ts","sourceRoot":"","sources":["../../../src/client/TeamAgentsPanel.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,yBAAyB,EACzB,2BAA2B,EAG5B,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAEzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAYlD,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IACrD,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,kBAAkB,CAAC,CAAA;IAC/D,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAA;IACvD,QAAQ,CAAC,SAAS,EAAE,gBAAgB,CAAC,WAAW,CAAC,CAAA;IACjD,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAA;IACvD,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAA;IACzD,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IACrD,QAAQ,CAAC,mBAAmB,EAAE,gBAAgB,CAAC,qBAAqB,CAAC,CAAA;IACrE,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAA;IACnD,8EAA8E;IAC9E,QAAQ,CAAC,eAAe,CAAC,EAAE,2BAA2B,CAAC,QAAQ,CAAC,CAAC,WAAW,CAAC,CAAA;IAC7E,QAAQ,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,mBAAmB,CAAC,CAAA;IACjE,QAAQ,CAAC,gBAAgB,EAAE,CAAC,OAAO,EAAE,yBAAyB,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAA;IAC1F,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC;AAED,wBAAgB,eAAe,CAAC,EAAE,WAAW,EAAE,WAAW,EAAE,gBAAgB,EAAE,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,mBAAmB,EAAE,UAAU,EAAE,eAAe,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,CAAC,EAAE,EAAE,oBAAoB,+BAiLhQ"}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
-
import { Button,
|
|
3
|
+
import { Button, IconEditOutline16, IconPlayOutline16, IconPlusOutline16, IconRefreshOutline16, Input, Modal, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
4
|
import { TeamMemberAvatar } from "./TeamMemberAvatar.js";
|
|
5
5
|
import { SortableRow, useSidebarRowDrag } from "./sidebar-drag.js";
|
|
6
6
|
import { moveSidebarItem, useSidebarOrder } from "./sidebar-order.js";
|
|
7
|
+
import { mintRequestId } from "./requests.js";
|
|
7
8
|
import { TeamRowMenu } from "./TeamRowMenu.js";
|
|
8
9
|
import { TeamSidebarSection } from "./TeamSidebarSection.js";
|
|
9
10
|
import { MultiMenuField } from "./multi-menu-field.js";
|
|
10
|
-
import {
|
|
11
|
+
import { AgentEditorDialog, ModelPickerField, sameModel } from "./TeamMemberEditor.js";
|
|
11
12
|
import createCss from './create.module.css';
|
|
12
13
|
import css from './sidebar.module.css';
|
|
13
14
|
export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, loadChannels, addMember, updateMember, recoverMember, joinChannel, removeChannelMember, loadModels, memberSessionId, openMemberSession, onCreatingChange, t }) {
|
|
@@ -119,7 +120,7 @@ export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, lo
|
|
|
119
120
|
&& sameModel(retryRequest.model, model)
|
|
120
121
|
&& retryRequest.channelRefs.length === channelRefs.length && retryRequest.channelRefs.every(ref => channelRefs.includes(ref));
|
|
121
122
|
void provision(sameRequest ? retryRequest : {
|
|
122
|
-
requestId:
|
|
123
|
+
requestId: mintRequestId(),
|
|
123
124
|
workspaceId,
|
|
124
125
|
handle: normalizedHandle,
|
|
125
126
|
description: normalizedDescription,
|
|
@@ -141,183 +142,43 @@ export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, lo
|
|
|
141
142
|
function AgentRow({ status, current, channels, loadChannels, updateMember, recoverMember, joinChannel, removeChannelMember, loadModels, openMemberSession, onUpdated, t }) {
|
|
142
143
|
const [menuOpen, setMenuOpen] = useState(false);
|
|
143
144
|
const [editing, setEditing] = useState(false);
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
void resume();
|
|
158
|
-
else
|
|
159
|
-
setEditing(true);
|
|
160
|
-
}, onOpenChange: setMenuOpen }) })] }), editing && (_jsx(AgentEditorDialog, { status: status, channels: channels, loadChannels: loadChannels, updateMember: updateMember, joinChannel: joinChannel, removeChannelMember: removeChannelMember, loadModels: loadModels, onCommitted: onUpdated, onClose: () => { setEditing(false); }, t: t }))] }));
|
|
161
|
-
}
|
|
162
|
-
/** Model option key inside one editor; opaque and resolved against the loaded groups. */
|
|
163
|
-
function modelKey(provider, model) {
|
|
164
|
-
return `${provider}\u0000${model}`;
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Shared provider/model dropdown for the create and edit forms. The option
|
|
168
|
-
* list rides the shared Menu primitive (one leading "follow Host default"
|
|
169
|
-
* row, then non-selectable provider headings) with a capped, internally
|
|
170
|
-
* scrolling card so growing model catalogs cannot stretch the dialog.
|
|
171
|
-
*/
|
|
172
|
-
function ModelPickerField({ model, onModelChange, loadModels, disabled, t }) {
|
|
173
|
-
const [groups, setGroups] = useState();
|
|
174
|
-
const [modelsError, setModelsError] = useState();
|
|
175
|
-
const [open, setModelOpen] = useState(false);
|
|
176
|
-
const [effortOpen, setEffortOpen] = useState(false);
|
|
177
|
-
useEffect(() => {
|
|
178
|
-
let mounted = true;
|
|
179
|
-
void loadModels().then(result => {
|
|
180
|
-
if (!mounted)
|
|
145
|
+
const [rowAlert, setRowAlert] = useState();
|
|
146
|
+
// Both row actions ride the same runtime remote: the Host steers a live
|
|
147
|
+
// session, rebuilds an orphaned composition, or re-runs a failed activation.
|
|
148
|
+
const recover = async () => {
|
|
149
|
+
try {
|
|
150
|
+
const result = await recoverMember({
|
|
151
|
+
requestId: mintRequestId(),
|
|
152
|
+
workspaceId: status.member.workspaceId,
|
|
153
|
+
memberId: status.member.memberId,
|
|
154
|
+
});
|
|
155
|
+
await onUpdated();
|
|
156
|
+
if (!result.ok) {
|
|
157
|
+
setRowAlert(t('restartFailed', { message: result.error.message }));
|
|
181
158
|
return;
|
|
182
|
-
if (result.ok) {
|
|
183
|
-
setGroups(result.value.groups);
|
|
184
|
-
setModelsError(undefined);
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
setModelsError(result.error.message);
|
|
188
159
|
}
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
}, [loadModels]);
|
|
192
|
-
const items = [{ id: '', label: t('modelFollowDefault') }];
|
|
193
|
-
const byKey = new Map();
|
|
194
|
-
for (const group of groups ?? []) {
|
|
195
|
-
items.push({ type: 'label', id: `model-group:${group.id}`, text: group.name });
|
|
196
|
-
for (const entry of group.models) {
|
|
197
|
-
const key = modelKey(group.id, entry.id);
|
|
198
|
-
byKey.set(key, { provider: group.id, id: entry.id, name: entry.name, efforts: entry.reasoning?.efforts ?? [] });
|
|
199
|
-
items.push({ id: key, label: entry.name });
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
const selectedModelKey = model === undefined ? '' : modelKey(model.provider, model.model);
|
|
203
|
-
const triggerLabel = model === undefined
|
|
204
|
-
? t('modelFollowDefault')
|
|
205
|
-
: byKey.get(selectedModelKey)?.name ?? `${model.provider} / ${model.model}`;
|
|
206
|
-
// The effort sub-row only makes sense for a pinned model with adapter-exposed
|
|
207
|
-
// efforts; following the Host default inherits the operator's whole selection.
|
|
208
|
-
const efforts = model === undefined ? [] : byKey.get(selectedModelKey)?.efforts ?? [];
|
|
209
|
-
const effortItems = [{ id: '', label: t('effortFollowDefault') }, ...efforts.map(effort => ({ id: effort.id, label: effort.name }))];
|
|
210
|
-
const selectedEffort = model?.reasoningEffort ?? '';
|
|
211
|
-
const effortTriggerLabel = model === undefined || selectedEffort === ''
|
|
212
|
-
? t('effortFollowDefault')
|
|
213
|
-
: efforts.find(effort => effort.id === selectedEffort)?.name ?? selectedEffort;
|
|
214
|
-
return _jsxs("div", { className: createCss.field, children: [_jsx("span", { children: t('memberModel') }), groups === undefined && modelsError === undefined && _jsx("small", { className: css.editHint, children: t('modelsLoading') }), modelsError !== undefined && _jsx("small", { className: css.editHint, children: t('modelsLoadFailed', { message: modelsError }) }), groups !== undefined && (_jsx(Menu, { open: open, portal: true, className: createCss.menuCap, items: items, selectedId: selectedModelKey, onSelect: key => {
|
|
215
|
-
setModelOpen(false);
|
|
216
|
-
const choice = byKey.get(key);
|
|
217
|
-
onModelChange(choice === undefined ? undefined : { provider: choice.provider, model: choice.id });
|
|
218
|
-
}, onClose: () => { setModelOpen(false); }, anchor: _jsxs("button", { type: "button", className: createCss.selectTrigger, "aria-label": t('memberModel'), "aria-haspopup": "listbox", "aria-expanded": open, disabled: disabled, onClick: () => { setModelOpen(value => !value); }, children: [_jsx("span", { className: createCss.selectValue, children: triggerLabel }), _jsx("span", { className: `${createCss.chevron} ${open ? createCss.chevronOpen : ''}`, "aria-hidden": true, children: _jsx(IconChevronDownOutline14, {}) })] }) })), model !== undefined && efforts.length > 0 && (_jsx(Menu, { open: effortOpen, portal: true, className: createCss.menuCap, items: effortItems, selectedId: selectedEffort, onSelect: key => {
|
|
219
|
-
setEffortOpen(false);
|
|
220
|
-
onModelChange(key === ''
|
|
221
|
-
? { provider: model.provider, model: model.model }
|
|
222
|
-
: { provider: model.provider, model: model.model, reasoningEffort: key });
|
|
223
|
-
}, onClose: () => { setEffortOpen(false); }, anchor: _jsxs("button", { type: "button", className: createCss.selectTrigger, "aria-label": t('reasoningEffort'), "aria-haspopup": "listbox", "aria-expanded": effortOpen, disabled: disabled, onClick: () => { setEffortOpen(value => !value); }, children: [_jsx("span", { className: createCss.selectValue, children: `${t('reasoningEffort')} · ${effortTriggerLabel}` }), _jsx("span", { className: `${createCss.chevron} ${effortOpen ? createCss.chevronOpen : ''}`, "aria-hidden": true, children: _jsx(IconChevronDownOutline14, {}) })] }) }))] });
|
|
224
|
-
}
|
|
225
|
-
/**
|
|
226
|
-
* Agent editor: handle, description, and per-Member model selection commit
|
|
227
|
-
* through one durable update; Channel membership below keeps its own
|
|
228
|
-
* immediate add/remove flow.
|
|
229
|
-
*/
|
|
230
|
-
function AgentEditorDialog({ status, channels, loadChannels, updateMember, joinChannel, removeChannelMember, loadModels, onCommitted, onClose, t }) {
|
|
231
|
-
const memberId = status.member.memberId;
|
|
232
|
-
const [handle, setHandle] = useState(status.member.handle);
|
|
233
|
-
const [description, setDescription] = useState(status.member.description);
|
|
234
|
-
const [model, setModel] = useState(status.member.model);
|
|
235
|
-
const [saving, setSaving] = useState(false);
|
|
236
|
-
const [error, setError] = useState();
|
|
237
|
-
const pendingRequest = useRef();
|
|
238
|
-
// Membership comes from a fresh projection at open time so stale checkbox
|
|
239
|
-
// state never flips a committed fact backwards.
|
|
240
|
-
const [memberships, setMemberships] = useState();
|
|
241
|
-
const [loadError, setLoadError] = useState();
|
|
242
|
-
useEffect(() => {
|
|
243
|
-
let mounted = true;
|
|
244
|
-
void loadChannels({ workspaceId: status.member.workspaceId, topLevelOnly: true, includeActivities: false, limit: 1 }).then(result => {
|
|
245
|
-
if (!mounted)
|
|
160
|
+
if (result.value.status.availability === 'unavailable') {
|
|
161
|
+
setRowAlert(t('restartStillUnavailable', { diagnostic: result.value.status.diagnostic ?? t('statusUnavailable') }));
|
|
246
162
|
return;
|
|
247
|
-
if (result.ok) {
|
|
248
|
-
setMemberships(result.value.members);
|
|
249
|
-
setLoadError(undefined);
|
|
250
|
-
}
|
|
251
|
-
else {
|
|
252
|
-
setLoadError(result.error.message);
|
|
253
|
-
}
|
|
254
|
-
});
|
|
255
|
-
return () => { mounted = false; };
|
|
256
|
-
}, [loadChannels, status.member.workspaceId]);
|
|
257
|
-
const membership = useChannelMembership({ joinChannel, removeChannelMember }, change => change.channelRef, change => {
|
|
258
|
-
setMemberships(current => {
|
|
259
|
-
const base = current ?? [];
|
|
260
|
-
return change.joined
|
|
261
|
-
? base.filter(item => !(item.channelRef === change.channelRef && item.memberId === change.memberId))
|
|
262
|
-
: [...base, { channelRef: change.channelRef, memberId: change.memberId }];
|
|
263
|
-
});
|
|
264
|
-
});
|
|
265
|
-
// A successful join appends the durable fact; removal filters it out above.
|
|
266
|
-
const joinedChannels = new Set((memberships ?? []).filter(item => item.memberId === memberId).map(item => item.channelRef));
|
|
267
|
-
const dirty = handle.trim() !== status.member.handle || description.trim() !== status.member.description
|
|
268
|
-
|| !sameModel(model, status.member.model);
|
|
269
|
-
const submit = async (event) => {
|
|
270
|
-
event.preventDefault();
|
|
271
|
-
const normalizedHandle = handle.trim();
|
|
272
|
-
const normalizedDescription = description.trim();
|
|
273
|
-
if (saving || !dirty || normalizedHandle.length === 0)
|
|
274
|
-
return;
|
|
275
|
-
const payload = {
|
|
276
|
-
memberId,
|
|
277
|
-
handle: normalizedHandle,
|
|
278
|
-
description: normalizedDescription,
|
|
279
|
-
...(model === undefined ? {} : { model }),
|
|
280
|
-
};
|
|
281
|
-
const samePending = pendingRequest.current !== undefined && pendingRequest.current.memberId === payload.memberId
|
|
282
|
-
&& pendingRequest.current.handle === payload.handle && pendingRequest.current.description === payload.description
|
|
283
|
-
&& sameModel(pendingRequest.current.model, model);
|
|
284
|
-
const request = samePending ? pendingRequest.current : {
|
|
285
|
-
requestId: crypto.randomUUID(),
|
|
286
|
-
...payload,
|
|
287
|
-
};
|
|
288
|
-
pendingRequest.current = request;
|
|
289
|
-
setSaving(true);
|
|
290
|
-
setError(undefined);
|
|
291
|
-
try {
|
|
292
|
-
const result = await updateMember(request);
|
|
293
|
-
if (result.ok) {
|
|
294
|
-
pendingRequest.current = undefined;
|
|
295
|
-
await onCommitted();
|
|
296
|
-
onClose();
|
|
297
|
-
}
|
|
298
|
-
else {
|
|
299
|
-
setError(result.error.message);
|
|
300
163
|
}
|
|
164
|
+
setRowAlert(undefined);
|
|
165
|
+
// A page opened while the Member was down predates the live Session;
|
|
166
|
+
// re-selecting is a no-op when already bound and rebinds when stale.
|
|
167
|
+
if (current === true)
|
|
168
|
+
openMemberSession(status.member.sessionId);
|
|
301
169
|
}
|
|
302
170
|
catch (cause) {
|
|
303
|
-
|
|
304
|
-
}
|
|
305
|
-
finally {
|
|
306
|
-
setSaving(false);
|
|
171
|
+
setRowAlert(t('restartFailed', { message: cause instanceof Error ? cause.message : String(cause) }));
|
|
307
172
|
}
|
|
308
173
|
};
|
|
309
|
-
return (
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
return true;
|
|
320
|
-
if (left === undefined || right === undefined)
|
|
321
|
-
return false;
|
|
322
|
-
return left.provider === right.provider && left.model === right.model && left.reasoningEffort === right.reasoningEffort;
|
|
174
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: css.agentRow, "data-menu-open": menuOpen || undefined, children: [_jsxs("button", { type: "button", className: css.agentSelect, "aria-label": t('openAgentSession', { name: status.member.handle }), "aria-current": current ? 'page' : undefined, disabled: status.availability !== 'active', onClick: () => { openMemberSession(status.member.sessionId); }, children: [_jsx(TeamMemberAvatar, { status: status, t: t }), _jsxs("span", { className: css.agentCopy, children: [_jsx("strong", { children: status.member.handle }), _jsx("small", { children: status.member.description })] })] }), _jsx("span", { className: css.rowMenu, children: _jsx(TeamRowMenu, { label: t('actionsAgent', { name: status.member.handle }), items: [
|
|
175
|
+
{ id: 'edit', label: t('editAgent'), icon: _jsx(IconEditOutline16, {}) },
|
|
176
|
+
...(status.presence === 'error' ? [{ id: 'resume', label: t('resumeAgent'), icon: _jsx(IconPlayOutline16, {}) }] : []),
|
|
177
|
+
...(status.availability === 'unavailable' ? [{ id: 'restart', label: t('restartAgent'), icon: _jsx(IconRefreshOutline16, {}) }] : []),
|
|
178
|
+
], onSelect: (id) => {
|
|
179
|
+
if (id === 'edit')
|
|
180
|
+
setEditing(true);
|
|
181
|
+
else
|
|
182
|
+
void recover();
|
|
183
|
+
}, onOpenChange: setMenuOpen }) })] }), rowAlert !== undefined && _jsx("div", { className: css.rowAlert, role: "alert", children: rowAlert }), editing && (_jsx(AgentEditorDialog, { status: status, channels: channels, loadChannels: loadChannels, updateMember: updateMember, joinChannel: joinChannel, removeChannelMember: removeChannelMember, loadModels: loadModels, onCommitted: onUpdated, onClose: () => { setEditing(false); }, t: t }))] }));
|
|
323
184
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamChannelPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamChannelPage.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAA+B,mBAAmB,EAE7D,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAEzE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"TeamChannelPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamChannelPage.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAA+B,mBAAmB,EAE7D,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAEzE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAIvD,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,aAAa,CAAA;AAc/D,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,mBAAmB,CAAA;IACxC,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,gBAAgB,EAAE,qBAAqB,CAAC,kBAAkB,CAAC,CAAA;IACpE,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,MAAM,EAAE,cAAc,CAAA;IAC/B,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,WAAW,EAAE,qBAAqB,CAAC,aAAa,CAAC,CAAA;IAC1D,QAAQ,CAAC,mBAAmB,EAAE,qBAAqB,CAAC,qBAAqB,CAAC,CAAA;IAC1E,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,cAAc,EAAE,qBAAqB,CAAC,gBAAgB,CAAC,CAAA;IAChE,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC;AAmBD,wBAAgB,eAAe,CAAC,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE,YAAY,EAAE,aAAa,EAAE,cAAc,EAAE,eAAe,EAAE,CAAC,EAAE,EAAE,oBAAoB,+BAiUnR"}
|
|
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Fragment, useEffect, useRef, useState, useSyncExternalStore } from 'react';
|
|
3
3
|
import { Button, IconChevronLeftOutline14, IconChevronRightOutline14, Modal, StateDot } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
4
|
import { bytesToBase64 } from "./attachment-preview.js";
|
|
5
|
+
import { mintRequestId } from "./requests.js";
|
|
5
6
|
import { TeamComposer } from "./TeamComposer.js";
|
|
6
7
|
import { TeamPresenceDot } from "./TeamPresenceDot.js";
|
|
7
8
|
import { TeamMessage } from "./TeamMessage.js";
|
|
@@ -9,7 +10,7 @@ import { TeamRunDivider } from "./TeamRunDivider.js";
|
|
|
9
10
|
import { formatTaskStatus, taskStatusDot, mentionNamesOf } from "./team-formatters.js";
|
|
10
11
|
import { useChannelMembership } from "./team-membership.js";
|
|
11
12
|
import { useTimelineScroll } from "./timeline-scroll.js";
|
|
12
|
-
import {
|
|
13
|
+
import { hostTaskRefLookup, jumpToTaskThread } from "./task-refs.js";
|
|
13
14
|
import { chunkRunsWithDays, isRunGap } from "./team-separators.js";
|
|
14
15
|
import channelCss from './channel.module.css';
|
|
15
16
|
import css from './conversation.module.css';
|
|
@@ -38,6 +39,7 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
|
|
|
38
39
|
const [statusMessage, setStatusMessage] = useState();
|
|
39
40
|
const [loading, setLoading] = useState(true);
|
|
40
41
|
const [pending, setPending] = useState(false);
|
|
42
|
+
const [asTask, setAsTask] = useState(false);
|
|
41
43
|
const [loadingOlder, setLoadingOlder] = useState(false);
|
|
42
44
|
const [managingMembers, setManagingMembers] = useState(false);
|
|
43
45
|
// The composer draft lives in the keyed draft cache: view switches unmount
|
|
@@ -59,31 +61,17 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
|
|
|
59
61
|
return;
|
|
60
62
|
}
|
|
61
63
|
if (ref.startsWith('task:')) {
|
|
62
|
-
const match = view?.items.find(item => item.task
|
|
64
|
+
const match = view?.items.find(item => item.task?.taskRef === ref);
|
|
63
65
|
if (match !== undefined) {
|
|
64
|
-
selectThread(match.
|
|
66
|
+
selectThread(match.thread.threadRef, channelRef, match.task?.taskRef, match.taskNumber);
|
|
65
67
|
return;
|
|
66
68
|
}
|
|
67
69
|
// Not in the loaded timeline: resolve through the Host and jump to the
|
|
68
70
|
// Task's home Channel.
|
|
69
|
-
|
|
70
|
-
.then(result => {
|
|
71
|
-
if (!result.ok)
|
|
72
|
-
return;
|
|
73
|
-
const hit = result.value.resolved[0];
|
|
74
|
-
if (hit !== undefined)
|
|
75
|
-
selectThread(hit.taskRef, hit.threadRef, hit.channelRef, hit.taskNumber);
|
|
76
|
-
});
|
|
71
|
+
jumpToTaskThread(resolveTaskRefs, workspaceId, ref, selectThread);
|
|
77
72
|
}
|
|
78
73
|
};
|
|
79
|
-
const lookupTaskRefs = (
|
|
80
|
-
.then(result => {
|
|
81
|
-
if (!result.ok)
|
|
82
|
-
return [];
|
|
83
|
-
for (const entry of result.value.resolved)
|
|
84
|
-
rememberResolvedTaskRef(entry);
|
|
85
|
-
return result.value.resolved;
|
|
86
|
-
});
|
|
74
|
+
const lookupTaskRefs = hostTaskRefLookup(resolveTaskRefs, workspaceId);
|
|
87
75
|
const timeline = useTimelineScroll(`${view?.items.length ?? 0}:${channelLastItem?.message.messageRef ?? ''}`);
|
|
88
76
|
const channel = view?.channels.find(item => item.channelRef === channelRef);
|
|
89
77
|
const channelMemberIds = new Set(view?.members.filter(item => item.channelRef === channelRef).map(item => item.memberId) ?? []);
|
|
@@ -219,11 +207,10 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
|
|
|
219
207
|
// definitive outcomes (committed or rejected) start the next send fresh.
|
|
220
208
|
const pendingSendId = useRef();
|
|
221
209
|
const send = async () => {
|
|
222
|
-
console.log('DBG send entry pending=%s draft=%j files=%d', pending, draft, pendingFiles.length);
|
|
223
210
|
if (pending || draft.trim() === '')
|
|
224
211
|
return;
|
|
225
212
|
const recipientIds = [...recipients].sort();
|
|
226
|
-
const requestId = pendingSendId.current ??
|
|
213
|
+
const requestId = pendingSendId.current ?? mintRequestId();
|
|
227
214
|
pendingSendId.current = requestId;
|
|
228
215
|
setPending(true);
|
|
229
216
|
setError(undefined);
|
|
@@ -234,7 +221,7 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
|
|
|
234
221
|
const attachmentIds = [];
|
|
235
222
|
for (const file of pendingFiles) {
|
|
236
223
|
const uploaded = await putAttachment({
|
|
237
|
-
requestId:
|
|
224
|
+
requestId: mintRequestId(),
|
|
238
225
|
workspaceId,
|
|
239
226
|
name: file.name,
|
|
240
227
|
mediaType: file.type === '' ? undefined : file.type,
|
|
@@ -250,6 +237,7 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
|
|
|
250
237
|
const request = {
|
|
251
238
|
requestId, workspaceId,
|
|
252
239
|
channelRef, body: draft.trim(), recipients: recipientIds,
|
|
240
|
+
asTask,
|
|
253
241
|
...(attachmentIds.length === 0 ? {} : { attachments: attachmentIds }),
|
|
254
242
|
};
|
|
255
243
|
const result = await sendMessage(request);
|
|
@@ -259,6 +247,7 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
|
|
|
259
247
|
}
|
|
260
248
|
else if (result.value.kind === 'committed') {
|
|
261
249
|
pendingSendId.current = undefined;
|
|
250
|
+
setAsTask(false);
|
|
262
251
|
await refresh();
|
|
263
252
|
drafts.clear(draftKey);
|
|
264
253
|
setPendingFiles([]);
|
|
@@ -293,12 +282,13 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
|
|
|
293
282
|
const senderStatus = members.find(member => member.member.memberId === item.message.sender);
|
|
294
283
|
const human = item.message.sender === view.humanMemberId;
|
|
295
284
|
const sender = human ? t('human') : senderStatus?.member.handle ?? item.message.sender;
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
285
|
+
const turnGap = isRunGap(index > 0 ? block.items[index - 1].message.occurredAt : undefined, item.message.occurredAt);
|
|
286
|
+
// A turn divider already labels the gapped entry; its row stays chrome-free.
|
|
287
|
+
return _jsxs(Fragment, { children: [turnGap && _jsx(TeamRunDivider, { occurredAt: item.message.occurredAt }), _jsxs(TeamMessage, { senderName: sender, memberId: item.message.sender, human: human, body: item.message.body, attachments: item.message.attachments, loadAttachment: getAttachment, t: t, occurredAt: item.message.occurredAt, mentionNames: mentionNamesOf(item.mentions, mentionHandlesMap), onOpenRef: openRef, onResolveTaskRefs: lookupTaskRefs, grouped: index > 0, showGroupedTime: item.message.topLevel === true && !turnGap, ...(senderStatus === undefined ? {} : { senderTitle: senderStatus.member.description }), children: [item.message.topLevel && item.task !== undefined && item.taskNumber !== undefined && _jsxs("button", { type: "button", className: channelCss.taskFooter, "aria-label": t('openTask', { number: item.taskNumber }), onClick: () => { selectThread(item.thread.threadRef, channelRef, item.task?.taskRef, item.taskNumber); }, children: [_jsx("span", { className: channelCss.taskDot, children: (() => {
|
|
288
|
+
const dot = taskStatusDot(item.task.status);
|
|
289
|
+
return dot === 'ongoing' || dot === 'warning' || dot === 'done'
|
|
290
|
+
? _jsx(StateDot, { size: 8, state: dot })
|
|
291
|
+
: _jsx("span", { className: channelCss.taskDotQuiet, "data-variant": dot });
|
|
292
|
+
})() }), _jsx("span", { className: channelCss.taskNumber, children: t('taskLabel', { number: item.taskNumber }) }), _jsx("span", { className: channelCss.taskStatus, children: formatTaskStatus(item.task.status, t) }), _jsx("span", { className: channelCss.taskCount, children: t('taskMessageCount', { count: item.messageCount }) }), _jsx("span", { className: channelCss.taskArrow, "aria-hidden": "true", children: _jsx(IconChevronRightOutline14, { size: 12 }) })] }), item.message.topLevel && item.task === undefined && _jsxs("button", { type: "button", className: channelCss.taskFooter, "aria-label": t('openThread'), onClick: () => { selectThread(item.thread.threadRef, channelRef); }, children: [_jsx("span", { className: channelCss.taskNumber, children: t('threadLabel') }), _jsx("span", { className: channelCss.taskCount, children: t('taskMessageCount', { count: item.messageCount }) }), _jsx("span", { className: channelCss.taskArrow, "aria-hidden": "true", children: _jsx(IconChevronRightOutline14, { size: 12 }) })] })] })] }, item.message.messageRef);
|
|
293
|
+
}) }, `run-${block.items[0].message.messageRef}`))] }) }), channel !== undefined ? _jsx(TeamComposer, { members: channelMembers, recipients: recipients, draft: draft, pending: pending, ...(statusMessage === undefined ? {} : { confirmation: statusMessage }), ...(error === undefined ? {} : { error }), onDraftChange: next => { drafts.writeDraft(draftKey, next); setStatusMessage(undefined); }, onRecipientsChange: next => { drafts.writeRecipients(draftKey, next); setStatusMessage(undefined); }, onSubmit: () => { void send(); }, pendingFiles: pendingFiles, onFilesChange: setPendingFiles, asTask: asTask, onAsTaskChange: setAsTask, t: t }, draftKey) : _jsx("div", {})] });
|
|
304
294
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamChannelsPanel.d.ts","sourceRoot":"","sources":["../../../src/client/TeamChannelsPanel.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,yBAAyB,EAEzB,mBAAmB,
|
|
1
|
+
{"version":3,"file":"TeamChannelsPanel.d.ts","sourceRoot":"","sources":["../../../src/client/TeamChannelsPanel.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,yBAAyB,EAEzB,mBAAmB,EAMpB,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAEzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAYlD,UAAU,sBAAsB;IAC9B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IACrD,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAA;IACvD,wGAAwG;IACxG,QAAQ,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,kBAAkB,CAAC,CAAA;IAC/D,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAA;IACzD,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAA;IACzD,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IACrD,QAAQ,CAAC,mBAAmB,EAAE,gBAAgB,CAAC,qBAAqB,CAAC,CAAA;IACrE,QAAQ,CAAC,cAAc,EAAE,SAAS,yBAAyB,EAAE,CAAA;IAC7D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,mBAAmB,CAAA;IACjD,QAAQ,CAAC,aAAa,EAAE,gBAAgB,CAAC,eAAe,CAAC,CAAA;IACzD,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,+BA2L9D"}
|
|
@@ -5,6 +5,7 @@ import { TeamPresenceDot } from "./TeamPresenceDot.js";
|
|
|
5
5
|
import { MultiMenuField } from "./multi-menu-field.js";
|
|
6
6
|
import { SortableRow, useSidebarRowDrag } from "./sidebar-drag.js";
|
|
7
7
|
import { moveSidebarItem, useSidebarOrder } from "./sidebar-order.js";
|
|
8
|
+
import { mintRequestId } from "./requests.js";
|
|
8
9
|
import { TeamRowMenu } from "./TeamRowMenu.js";
|
|
9
10
|
import { TeamSidebarSection } from "./TeamSidebarSection.js";
|
|
10
11
|
import { useChannelMembership } from "./team-membership.js";
|
|
@@ -98,7 +99,7 @@ export function TeamChannelsPanel(props) {
|
|
|
98
99
|
&& pendingCreate.name === payload.name && pendingCreate.description === payload.description
|
|
99
100
|
&& JSON.stringify(pendingCreate.memberIds) === JSON.stringify(payload.memberIds);
|
|
100
101
|
const request = samePending ? pendingCreate : {
|
|
101
|
-
requestId:
|
|
102
|
+
requestId: mintRequestId(), ...payload,
|
|
102
103
|
};
|
|
103
104
|
setPendingCreate(request);
|
|
104
105
|
setMutating(true);
|
|
@@ -178,7 +179,7 @@ function ChannelEditorDialog({ channel, members, joinedIds, updateChannel, joinC
|
|
|
178
179
|
const samePending = pendingRequest.current !== undefined && pendingRequest.current.channelRef === channel.channelRef
|
|
179
180
|
&& pendingRequest.current.name === normalizedName && pendingRequest.current.description === normalizedDescription;
|
|
180
181
|
const request = samePending ? pendingRequest.current : {
|
|
181
|
-
requestId:
|
|
182
|
+
requestId: mintRequestId(),
|
|
182
183
|
workspaceId: channel.workspaceId,
|
|
183
184
|
channelRef: channel.channelRef,
|
|
184
185
|
name: normalizedName,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentTeamClientMemberStatus, AgentTeamMemberId } from '@wowyuarm/dsh-agent-team/types';
|
|
2
2
|
import type { TeamConversationProps } from './slots.ts';
|
|
3
|
-
export declare function TeamComposer({ members, recipients, draft, pending, confirmation, error, onDraftChange, onRecipientsChange, onSubmit, pendingFiles, onFilesChange, t }: {
|
|
3
|
+
export declare function TeamComposer({ members, recipients, draft, pending, confirmation, error, onDraftChange, onRecipientsChange, onSubmit, placeholder, pendingFiles, onFilesChange, asTask, onAsTaskChange, t }: {
|
|
4
4
|
readonly members: readonly AgentTeamClientMemberStatus[];
|
|
5
5
|
readonly recipients: ReadonlySet<AgentTeamMemberId>;
|
|
6
6
|
readonly draft: string;
|
|
@@ -10,9 +10,14 @@ export declare function TeamComposer({ members, recipients, draft, pending, conf
|
|
|
10
10
|
readonly onDraftChange: (draft: string) => void;
|
|
11
11
|
readonly onRecipientsChange: (recipients: ReadonlySet<AgentTeamMemberId>) => void;
|
|
12
12
|
readonly onSubmit: () => void;
|
|
13
|
+
/** Conversation-specific prompt; the shared default fits Channel surfaces. */
|
|
14
|
+
readonly placeholder?: string;
|
|
13
15
|
/** Upload-capable surfaces pass this to enable the "+" file picker; the reply path omits it. */
|
|
14
16
|
readonly pendingFiles?: readonly File[];
|
|
15
17
|
readonly onFilesChange?: (files: readonly File[]) => void;
|
|
18
|
+
/** Channel-only: create a real Task with the top-level Message. Default off. */
|
|
19
|
+
readonly asTask?: boolean;
|
|
20
|
+
readonly onAsTaskChange?: (asTask: boolean) => void;
|
|
16
21
|
readonly t: TeamConversationProps['t'];
|
|
17
22
|
}): import("react").JSX.Element;
|
|
18
23
|
//# sourceMappingURL=TeamComposer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamComposer.d.ts","sourceRoot":"","sources":["../../../src/client/TeamComposer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAEpG,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAkDvD,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,QAAQ,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC,EAAE,EAAE;
|
|
1
|
+
{"version":3,"file":"TeamComposer.d.ts","sourceRoot":"","sources":["../../../src/client/TeamComposer.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,2BAA2B,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAEpG,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAkDvD,wBAAgB,YAAY,CAAC,EAAE,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,QAAQ,EAAE,WAAW,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,EAAE,cAAc,EAAE,CAAC,EAAE,EAAE;IAC3M,QAAQ,CAAC,OAAO,EAAE,SAAS,2BAA2B,EAAE,CAAA;IACxD,QAAQ,CAAC,UAAU,EAAE,WAAW,CAAC,iBAAiB,CAAC,CAAA;IACnD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAC9B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/C,QAAQ,CAAC,kBAAkB,EAAE,CAAC,UAAU,EAAE,WAAW,CAAC,iBAAiB,CAAC,KAAK,IAAI,CAAA;IACjF,QAAQ,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAA;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,gGAAgG;IAChG,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,IAAI,EAAE,CAAA;IACvC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,SAAS,IAAI,EAAE,KAAK,IAAI,CAAA;IACzD,gFAAgF;IAChF,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAA;IACnD,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC,+BA0OA"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
3
|
-
import { IconPlusOutline16, IconSendOutline16, useAnchoredMaxHeight, useDismissOnOutsidePointer } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
3
|
+
import { Button, IconPlusOutline16, IconSendOutline16, useAnchoredMaxHeight, useDismissOnOutsidePointer } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
4
4
|
import { TeamPresenceDot } from "./TeamPresenceDot.js";
|
|
5
5
|
import css from './composer.module.css';
|
|
6
6
|
import { formatByteSize } from "./attachment-preview.js";
|
|
@@ -42,7 +42,7 @@ function draftPreviewUrl(file) {
|
|
|
42
42
|
}
|
|
43
43
|
return url;
|
|
44
44
|
}
|
|
45
|
-
export function TeamComposer({ members, recipients, draft, pending, confirmation, error, onDraftChange, onRecipientsChange, onSubmit, pendingFiles, onFilesChange, t }) {
|
|
45
|
+
export function TeamComposer({ members, recipients, draft, pending, confirmation, error, onDraftChange, onRecipientsChange, onSubmit, placeholder, pendingFiles, onFilesChange, asTask, onAsTaskChange, t }) {
|
|
46
46
|
const inputRef = useRef(null);
|
|
47
47
|
const fileInputRef = useRef(null);
|
|
48
48
|
const rootRef = useRef(null);
|
|
@@ -172,7 +172,6 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
|
|
|
172
172
|
onSubmit();
|
|
173
173
|
};
|
|
174
174
|
return _jsxs("form", { ref: rootRef, className: css.root, onSubmit: event => {
|
|
175
|
-
console.log('DBG composer submit pending=%s draft=%j', pending, draft);
|
|
176
175
|
event.preventDefault();
|
|
177
176
|
if (!pending && draft.trim() !== '')
|
|
178
177
|
onSubmit();
|
|
@@ -180,7 +179,7 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
|
|
|
180
179
|
const optionId = `${listId}-${status.member.memberId}`;
|
|
181
180
|
const selected = index === highlight;
|
|
182
181
|
return _jsxs("button", { id: optionId, type: "button", role: "option", "aria-selected": selected, className: css.mentionOption, onMouseDown: event => { event.preventDefault(); }, onClick: () => { selectMention(status); }, children: [_jsx(TeamPresenceDot, { status: status, t: t }), _jsxs("span", { className: css.mentionName, children: ["@", status.member.handle] }), _jsx("span", { className: css.mentionDescription, children: status.member.description })] }, status.member.memberId);
|
|
183
|
-
}) }), _jsx("textarea", { ref: inputRef, "aria-label": t('messageDraft'), "aria-autocomplete": "list", "aria-controls": menuOpen ? listId : undefined, "aria-activedescendant": menuOpen && activeCandidate !== undefined ? `${listId}-${activeCandidate.member.memberId}` : undefined, "aria-expanded": menuOpen, value: draft, readOnly: pending, placeholder: t('messagePlaceholder'), rows: 1, onChange: onChange, onKeyDown: onKeyDown, onSelect: event => { updateMention(event.currentTarget.value, event.currentTarget.selectionStart ?? event.currentTarget.value.length); }, onCompositionStart: () => { composingRef.current = true; }, onCompositionEnd: () => { setTimeout(() => { composingRef.current = false; }, 10); } })] }), recipients.size > 0 && _jsx("p", { className: css.notifyRow, "data-team-notify": true, children: t('composerNotify', { ids: [...recipients].sort().map(memberId => `@${members.find(candidate => candidate.member.memberId === memberId)?.member.handle ?? memberId}`).join(', ') }) }), onFilesChange !== undefined && pendingFiles !== undefined && pendingFiles.length > 0 && (_jsx("ul", { className: css.fileChips, "aria-label": t('attachFiles'), children: pendingFiles.map((file, index) => {
|
|
182
|
+
}) }), _jsx("textarea", { ref: inputRef, "aria-label": t('messageDraft'), "aria-autocomplete": "list", "aria-controls": menuOpen ? listId : undefined, "aria-activedescendant": menuOpen && activeCandidate !== undefined ? `${listId}-${activeCandidate.member.memberId}` : undefined, "aria-expanded": menuOpen, value: draft, readOnly: pending, placeholder: placeholder ?? t('messagePlaceholder'), rows: 1, onChange: onChange, onKeyDown: onKeyDown, onSelect: event => { updateMention(event.currentTarget.value, event.currentTarget.selectionStart ?? event.currentTarget.value.length); }, onCompositionStart: () => { composingRef.current = true; }, onCompositionEnd: () => { setTimeout(() => { composingRef.current = false; }, 10); } })] }), recipients.size > 0 && _jsx("p", { className: css.notifyRow, "data-team-notify": true, children: t('composerNotify', { ids: [...recipients].sort().map(memberId => `@${members.find(candidate => candidate.member.memberId === memberId)?.member.handle ?? memberId}`).join(', ') }) }), onFilesChange !== undefined && pendingFiles !== undefined && pendingFiles.length > 0 && (_jsx("ul", { className: css.fileChips, "aria-label": t('attachFiles'), children: pendingFiles.map((file, index) => {
|
|
184
183
|
const previewUrl = draftPreviewUrl(file);
|
|
185
184
|
return _jsxs("li", { className: `${css.fileChip} ${previewUrl !== undefined ? css.imageChip : ''}`, children: [previewUrl !== undefined && _jsx("img", { src: previewUrl, alt: "", className: css.imageChipPreview }), _jsxs("span", { className: css.fileChipName, title: file.name, children: [file.name, _jsx("span", { className: css.fileChipSize, children: formatByteSize(file.size) })] }), _jsx("button", { type: "button", className: css.fileChipRemove, "aria-label": t('removeFile', { name: file.name }), disabled: pending, onClick: () => {
|
|
186
185
|
const url = draftPreviewUrls.get(file);
|
|
@@ -188,7 +187,7 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
|
|
|
188
187
|
URL.revokeObjectURL(url);
|
|
189
188
|
onFilesChange(pendingFiles.filter((_, candidate) => candidate !== index));
|
|
190
189
|
}, children: "\u00D7" })] }, `${file.name}-${index}`);
|
|
191
|
-
}) })), _jsxs("div", { className: css.toolbar, children: [onFilesChange !== undefined && (_jsxs(_Fragment, { children: [_jsx("input", { ref: fileInputRef, type: "file", multiple: true, className: css.fileInput, "aria-hidden": "true", tabIndex: -1, onChange: event => {
|
|
190
|
+
}) })), _jsxs("div", { className: css.toolbar, children: [onAsTaskChange !== undefined && (_jsx(Button, { size: "sm", variant: "outline", className: asTask === true ? css.asTaskOn : undefined, "aria-pressed": asTask === true, disabled: pending, onClick: () => { onAsTaskChange(asTask !== true); }, children: t('asTask') })), onFilesChange !== undefined && (_jsxs(_Fragment, { children: [_jsx("input", { ref: fileInputRef, type: "file", multiple: true, className: css.fileInput, "aria-hidden": "true", tabIndex: -1, onChange: event => {
|
|
192
191
|
const chosen = [...event.target.files ?? []];
|
|
193
192
|
if (chosen.length > 0 && pendingFiles !== undefined)
|
|
194
193
|
onFilesChange([...pendingFiles, ...chosen]);
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { TeamConversationProps } from './slots.ts';
|
|
2
|
-
export declare function TeamConversation({ t, useWorkspaces, navigation, drafts, putAttachment, getAttachment, loadChannels, readThread, loadThreadHistory, subscribeChanges, loadMembers, sendMessage, joinChannel, removeChannelMember, reply, changeTask, selectThread, selectChannel, backToWorkspace, backToChannels, resolveTaskRefs }: TeamConversationProps): import("react").JSX.Element;
|
|
2
|
+
export declare function TeamConversation({ t, useWorkspaces, navigation, drafts, putAttachment, getAttachment, loadChannels, readThread, loadThreadHistory, subscribeChanges, loadMembers, sendMessage, joinChannel, removeChannelMember, reply, changeTask, promoteThread, selectThread, selectChannel, backToWorkspace, backToChannels, resolveTaskRefs }: TeamConversationProps): import("react").JSX.Element;
|
|
3
3
|
//# sourceMappingURL=TeamConversation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TeamConversation.d.ts","sourceRoot":"","sources":["../../../src/client/TeamConversation.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAKvD,wBAAgB,gBAAgB,CAAC,EAAE,CAAC,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE,KAAK,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,EAAE,qBAAqB,+
|
|
1
|
+
{"version":3,"file":"TeamConversation.d.ts","sourceRoot":"","sources":["../../../src/client/TeamConversation.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAKvD,wBAAgB,gBAAgB,CAAC,EAAE,CAAC,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,UAAU,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,WAAW,EAAE,WAAW,EAAE,WAAW,EAAE,mBAAmB,EAAE,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,cAAc,EAAE,eAAe,EAAE,EAAE,qBAAqB,+BAoBzW"}
|
|
@@ -3,12 +3,12 @@ import { useSyncExternalStore } from 'react';
|
|
|
3
3
|
import { TeamChannelPage } from "./TeamChannelPage.js";
|
|
4
4
|
import { TeamThreadPage } from "./TeamThreadPage.js";
|
|
5
5
|
import css from './conversation.module.css';
|
|
6
|
-
export function TeamConversation({ t, useWorkspaces, navigation, drafts, putAttachment, getAttachment, loadChannels, readThread, loadThreadHistory, subscribeChanges, loadMembers, sendMessage, joinChannel, removeChannelMember, reply, changeTask, selectThread, selectChannel, backToWorkspace, backToChannels, resolveTaskRefs }) {
|
|
6
|
+
export function TeamConversation({ t, useWorkspaces, navigation, drafts, putAttachment, getAttachment, loadChannels, readThread, loadThreadHistory, subscribeChanges, loadMembers, sendMessage, joinChannel, removeChannelMember, reply, changeTask, promoteThread, selectThread, selectChannel, backToWorkspace, backToChannels, resolveTaskRefs }) {
|
|
7
7
|
const navigationState = useSyncExternalStore(navigation.subscribe, navigation.getSnapshot, navigation.getSnapshot);
|
|
8
8
|
const workspaces = useWorkspaces(state => state.items);
|
|
9
9
|
const current = workspaces.find(workspace => workspace.workspaceId === navigationState.workspaceId);
|
|
10
|
-
if (current !== undefined && navigationState.
|
|
11
|
-
return _jsx(TeamThreadPage, { workspaceId: current.workspaceId, putAttachment: putAttachment,
|
|
10
|
+
if (current !== undefined && navigationState.threadRef !== undefined) {
|
|
11
|
+
return _jsx(TeamThreadPage, { workspaceId: current.workspaceId, putAttachment: putAttachment, threadRef: navigationState.threadRef, backToWorkspace: backToWorkspace, selectChannel: selectChannel, selectThread: selectThread, resolveTaskRefs: resolveTaskRefs, ...(navigationState.channelRef === undefined ? {} : { channelRef: navigationState.channelRef }), ...(navigationState.taskRef === undefined ? {} : { taskRef: navigationState.taskRef }), ...(navigationState.taskNumber === undefined ? {} : { taskNumber: navigationState.taskNumber }), drafts: drafts, getAttachment: getAttachment, readThread: readThread, loadChannels: loadChannels, loadThreadHistory: loadThreadHistory, subscribeChanges: subscribeChanges, loadMembers: loadMembers, reply: reply, changeTask: changeTask, promoteThread: promoteThread, t: t }, navigationState.threadRef);
|
|
12
12
|
}
|
|
13
13
|
if (current !== undefined && navigationState.channelRef !== undefined) {
|
|
14
14
|
return _jsx(TeamChannelPage, { workspaceId: current.workspaceId, channelRef: navigationState.channelRef, drafts: drafts, putAttachment: putAttachment, getAttachment: getAttachment, loadChannels: loadChannels, subscribeChanges: subscribeChanges, loadMembers: loadMembers, sendMessage: sendMessage, joinChannel: joinChannel, removeChannelMember: removeChannelMember, selectThread: selectThread, selectChannel: selectChannel, backToChannels: backToChannels, resolveTaskRefs: resolveTaskRefs, t: t }, navigationState.channelRef);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { AgentTeamChannel, AgentTeamClientMemberStatus, AgentTeamModelSelection } from '@wowyuarm/dsh-agent-team/types';
|
|
2
|
+
import type { TeamSidebarProps } from './slots.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Shared provider/model dropdown for the create and edit forms. The option
|
|
5
|
+
* list rides the shared Menu primitive (one leading "follow Host default"
|
|
6
|
+
* row, then non-selectable provider headings) with a capped, internally
|
|
7
|
+
* scrolling card so growing model catalogs cannot stretch the dialog.
|
|
8
|
+
*/
|
|
9
|
+
export declare function ModelPickerField({ model, onModelChange, loadModels, disabled, t }: {
|
|
10
|
+
readonly model: AgentTeamModelSelection | undefined;
|
|
11
|
+
readonly onModelChange: (choice: AgentTeamModelSelection | undefined) => void;
|
|
12
|
+
readonly loadModels: TeamSidebarProps['loadModels'];
|
|
13
|
+
readonly disabled: boolean;
|
|
14
|
+
readonly t: TeamSidebarProps['t'];
|
|
15
|
+
}): import("react").JSX.Element;
|
|
16
|
+
/**
|
|
17
|
+
* Agent editor: handle, description, and per-Member model selection commit
|
|
18
|
+
* through one durable update; Channel membership below keeps its own
|
|
19
|
+
* immediate add/remove flow.
|
|
20
|
+
*/
|
|
21
|
+
export declare function AgentEditorDialog({ status, channels, loadChannels, updateMember, joinChannel, removeChannelMember, loadModels, onCommitted, onClose, t }: {
|
|
22
|
+
readonly status: AgentTeamClientMemberStatus;
|
|
23
|
+
readonly channels: readonly AgentTeamChannel[];
|
|
24
|
+
readonly loadChannels: TeamSidebarProps['loadChannels'];
|
|
25
|
+
readonly updateMember: TeamSidebarProps['updateMember'];
|
|
26
|
+
readonly joinChannel: TeamSidebarProps['joinChannel'];
|
|
27
|
+
readonly removeChannelMember: TeamSidebarProps['removeChannelMember'];
|
|
28
|
+
readonly loadModels: TeamSidebarProps['loadModels'];
|
|
29
|
+
readonly onCommitted: () => Promise<void> | void;
|
|
30
|
+
readonly onClose: () => void;
|
|
31
|
+
readonly t: TeamSidebarProps['t'];
|
|
32
|
+
}): import("react").JSX.Element;
|
|
33
|
+
export declare function sameModel(left: AgentTeamModelSelection | undefined, right: AgentTeamModelSelection | undefined): boolean;
|
|
34
|
+
//# sourceMappingURL=TeamMemberEditor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"TeamMemberEditor.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMemberEditor.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,gBAAgB,EAA8B,2BAA2B,EAAE,uBAAuB,EAAgC,MAAM,gCAAgC,CAAA;AACtL,OAAO,KAAK,EAAiD,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAajG;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,EAAE,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,QAAQ,EAAE,CAAC,EAAE,EAAE;IAClF,QAAQ,CAAC,KAAK,EAAE,uBAAuB,GAAG,SAAS,CAAA;IACnD,QAAQ,CAAC,aAAa,EAAE,CAAC,MAAM,EAAE,uBAAuB,GAAG,SAAS,KAAK,IAAI,CAAA;IAC7E,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAA;IACnD,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC,+BA0GA;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,mBAAmB,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE;IACzJ,QAAQ,CAAC,MAAM,EAAE,2BAA2B,CAAA;IAC5C,QAAQ,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,CAAA;IAC9C,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAA;IACvD,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAA;IACvD,QAAQ,CAAC,WAAW,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAA;IACrD,QAAQ,CAAC,mBAAmB,EAAE,gBAAgB,CAAC,qBAAqB,CAAC,CAAA;IACrE,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAA;IACnD,QAAQ,CAAC,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IAChD,QAAQ,CAAC,OAAO,EAAE,MAAM,IAAI,CAAA;IAC5B,QAAQ,CAAC,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAA;CAClC,+BA0HA;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,uBAAuB,GAAG,SAAS,EAAE,KAAK,EAAE,uBAAuB,GAAG,SAAS,GAAG,OAAO,CAIxH"}
|