@wowyuarm/dsh-agent-team 0.1.2 → 0.1.3

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.
Files changed (67) hide show
  1. package/README.md +1 -0
  2. package/README.zh.md +2 -0
  3. package/package.json +12 -3
  4. package/packages/agent-team/README.md +1 -1
  5. package/packages/agent-team/README.zh.md +1 -1
  6. package/packages/agent-team/lib/attachments.js +11 -12
  7. package/packages/agent-team/lib/auto-compaction.js +163 -0
  8. package/packages/agent-team/lib/index.js +214 -130
  9. package/packages/agent-team/lib/ledger.js +77 -70
  10. package/packages/agent-team/lib/recovery.js +30 -35
  11. package/packages/agent-team/lib/typert.host.js +33 -23
  12. package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
  13. package/packages/agent-team/lib/typert.remote-client.js +21 -21
  14. package/packages/agent-team/lib/types/attachments.d.ts +2 -2
  15. package/packages/agent-team/lib/types/attachments.d.ts.map +1 -1
  16. package/packages/agent-team/lib/types/auto-compaction.d.ts +38 -0
  17. package/packages/agent-team/lib/types/auto-compaction.d.ts.map +1 -0
  18. package/packages/agent-team/lib/types/index.d.ts +40 -12
  19. package/packages/agent-team/lib/types/index.d.ts.map +1 -1
  20. package/packages/agent-team/lib/types/ledger.d.ts +13 -0
  21. package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
  22. package/packages/agent-team/lib/types/recovery.d.ts +15 -16
  23. package/packages/agent-team/lib/types/recovery.d.ts.map +1 -1
  24. package/packages/agent-team/preset/team-member/agent.cordis.yml +6 -1
  25. package/packages/client-agent-team/README.md +1 -1
  26. package/packages/client-agent-team/README.zh.md +1 -1
  27. package/packages/client-agent-team/lib/client.js +942 -534
  28. package/packages/client-agent-team/lib/client.js.map +1 -1
  29. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
  30. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +5 -166
  31. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
  32. package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +6 -20
  33. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts.map +1 -1
  34. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +3 -2
  35. package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts.map +1 -1
  36. package/packages/client-agent-team/lib/types/client/TeamComposer.js +0 -1
  37. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts +34 -0
  38. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts.map +1 -0
  39. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.js +169 -0
  40. package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.d.ts +17 -0
  41. package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.d.ts.map +1 -0
  42. package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.js +120 -0
  43. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -1
  44. package/packages/client-agent-team/lib/types/client/TeamMessage.js +83 -43
  45. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
  46. package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +10 -23
  47. package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
  48. package/packages/client-agent-team/lib/types/client/index.js +71 -1
  49. package/packages/client-agent-team/lib/types/client/member-session-input.d.ts +22 -0
  50. package/packages/client-agent-team/lib/types/client/member-session-input.d.ts.map +1 -0
  51. package/packages/client-agent-team/lib/types/client/member-session-input.js +98 -0
  52. package/packages/client-agent-team/lib/types/client/requests.d.ts +16 -0
  53. package/packages/client-agent-team/lib/types/client/requests.d.ts.map +1 -0
  54. package/packages/client-agent-team/lib/types/client/requests.js +22 -0
  55. package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
  56. package/packages/client-agent-team/lib/types/client/task-refs.d.ts +6 -1
  57. package/packages/client-agent-team/lib/types/client/task-refs.d.ts.map +1 -1
  58. package/packages/client-agent-team/lib/types/client/task-refs.js +19 -0
  59. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts +43 -3
  60. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts.map +1 -1
  61. package/packages/client-agent-team/lib/types/client/team-formatters.js +61 -4
  62. package/packages/client-agent-team/lib/types/client/team-membership.d.ts.map +1 -1
  63. package/packages/client-agent-team/lib/types/client/team-membership.js +2 -1
  64. package/packages/tool-agent-team/lib/index.js +1 -1
  65. package/packages/client-agent-team/lib/types/client/TeamInboxPanel.d.ts +0 -13
  66. package/packages/client-agent-team/lib/types/client/TeamInboxPanel.d.ts.map +0 -1
  67. 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,EAM5B,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAGzE,OAAO,KAAK,EAAiD,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAWjG,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
+ {"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, IconChevronDownOutline14, IconEditOutline16, IconPlayOutline16, IconPlusOutline16, Input, Menu, Modal, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
3
+ import { Button, IconEditOutline16, IconPlayOutline16, IconPlusOutline16, 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 { useChannelMembership } from "./team-membership.js";
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: crypto.randomUUID(),
123
+ requestId: mintRequestId(),
123
124
  workspaceId,
124
125
  handle: normalizedHandle,
125
126
  description: normalizedDescription,
@@ -143,7 +144,7 @@ function AgentRow({ status, current, channels, loadChannels, updateMember, recov
143
144
  const [editing, setEditing] = useState(false);
144
145
  const resume = async () => {
145
146
  await recoverMember({
146
- requestId: crypto.randomUUID(),
147
+ requestId: mintRequestId(),
147
148
  workspaceId: status.member.workspaceId,
148
149
  memberId: status.member.memberId,
149
150
  });
@@ -159,165 +160,3 @@ function AgentRow({ status, current, channels, loadChannels, updateMember, recov
159
160
  setEditing(true);
160
161
  }, 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
  }
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)
181
- return;
182
- if (result.ok) {
183
- setGroups(result.value.groups);
184
- setModelsError(undefined);
185
- }
186
- else {
187
- setModelsError(result.error.message);
188
- }
189
- });
190
- return () => { mounted = false; };
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)
246
- 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
- }
301
- }
302
- catch (cause) {
303
- setError(cause instanceof Error ? cause.message : String(cause));
304
- }
305
- finally {
306
- setSaving(false);
307
- }
308
- };
309
- return (_jsx(Modal, { open: true, onClose: onClose, title: t('editAgent'), description: `@${status.member.handle}`, closeLabel: t('close'), contentClassName: createCss.dialogContent, footer: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", disabled: saving, onClick: onClose, children: t('cancel') }), _jsx(Button, { type: "submit", form: "team-agent-edit-form", variant: "primary", disabled: saving || !dirty || handle.trim().length === 0, children: saving ? t('editSaving') : t('editSave') })] }), children: _jsxs("form", { id: "team-agent-edit-form", className: createCss.form, onSubmit: event => { void submit(event); }, children: [_jsxs("label", { className: createCss.field, children: [_jsx("span", { children: t('agentName') }), _jsx(Input, { className: createCss.input, value: handle, onChange: event => { setHandle(event.target.value); pendingRequest.current = undefined; }, disabled: saving, autoFocus: true })] }), _jsxs("label", { className: createCss.field, children: [_jsxs("span", { children: [t('agentDescription'), t('optionalSuffix')] }), _jsx(Input, { className: createCss.input, value: description, placeholder: t('agentDescriptionPlaceholder'), onChange: event => { setDescription(event.target.value); pendingRequest.current = undefined; }, disabled: saving })] }), _jsx(ModelPickerField, { model: model, onModelChange: choice => { pendingRequest.current = undefined; setModel(choice); }, loadModels: loadModels, disabled: saving, t: t }), _jsxs("fieldset", { className: createCss.memberPicker, disabled: saving, children: [_jsx("legend", { children: t('channelMembersSection') }), channels.map(channel => {
310
- const joined = joinedChannels.has(channel.channelRef);
311
- const rowPending = membership.pending.has(channel.channelRef);
312
- const disabled = rowPending || (!joined && status.presence === 'unavailable');
313
- const rowError = membership.errors.get(channel.channelRef);
314
- return _jsxs("div", { className: `${css.editMemberRow} ${css.editMemberRowChannels}`, children: [_jsxs("strong", { className: css.editChannelName, children: ["# ", channel.name] }), _jsx(Button, { size: "sm", variant: "outline", disabled: disabled, onClick: () => { void membership.change({ workspaceId: status.member.workspaceId, channelRef: channel.channelRef, memberId, joined }); }, children: rowPending ? t('membershipUpdating') : joined ? t('removeFromChannel') : t('addToChannel') }), rowError !== undefined && _jsx("p", { className: css.rowError, role: "alert", children: rowError })] }, channel.channelRef);
315
- }), channels.length === 0 && _jsx("small", { children: t('noChannelsForAgent') }), loadError !== undefined && _jsx("p", { className: css.rowError, role: "alert", children: loadError })] }), error !== undefined && _jsx("p", { className: createCss.error, role: "alert", children: error })] }) }));
316
- }
317
- function sameModel(left, right) {
318
- if (left === undefined && right === undefined)
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;
323
- }
@@ -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;AAGvD,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,+BAgUnR"}
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,+BAqTnR"}
@@ -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 { rememberResolvedTaskRef } from "./task-refs.js";
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';
@@ -66,24 +67,10 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
66
67
  }
67
68
  // Not in the loaded timeline: resolve through the Host and jump to the
68
69
  // Task's home Channel.
69
- void resolveTaskRefs({ workspaceId, taskRefs: [ref] })
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
- });
70
+ jumpToTaskThread(resolveTaskRefs, workspaceId, ref, selectThread);
77
71
  }
78
72
  };
79
- const lookupTaskRefs = (taskRefs) => resolveTaskRefs({ workspaceId, taskRefs })
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
- });
73
+ const lookupTaskRefs = hostTaskRefLookup(resolveTaskRefs, workspaceId);
87
74
  const timeline = useTimelineScroll(`${view?.items.length ?? 0}:${channelLastItem?.message.messageRef ?? ''}`);
88
75
  const channel = view?.channels.find(item => item.channelRef === channelRef);
89
76
  const channelMemberIds = new Set(view?.members.filter(item => item.channelRef === channelRef).map(item => item.memberId) ?? []);
@@ -219,11 +206,10 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
219
206
  // definitive outcomes (committed or rejected) start the next send fresh.
220
207
  const pendingSendId = useRef();
221
208
  const send = async () => {
222
- console.log('DBG send entry pending=%s draft=%j files=%d', pending, draft, pendingFiles.length);
223
209
  if (pending || draft.trim() === '')
224
210
  return;
225
211
  const recipientIds = [...recipients].sort();
226
- const requestId = pendingSendId.current ?? crypto.randomUUID();
212
+ const requestId = pendingSendId.current ?? mintRequestId();
227
213
  pendingSendId.current = requestId;
228
214
  setPending(true);
229
215
  setError(undefined);
@@ -234,7 +220,7 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
234
220
  const attachmentIds = [];
235
221
  for (const file of pendingFiles) {
236
222
  const uploaded = await putAttachment({
237
- requestId: crypto.randomUUID(),
223
+ requestId: mintRequestId(),
238
224
  workspaceId,
239
225
  name: file.name,
240
226
  mediaType: file.type === '' ? undefined : file.type,
@@ -1 +1 @@
1
- {"version":3,"file":"TeamChannelsPanel.d.ts","sourceRoot":"","sources":["../../../src/client/TeamChannelsPanel.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,yBAAyB,EAEzB,mBAAmB,EAOpB,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAEzE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAWlD,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"}
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: crypto.randomUUID(), ...payload,
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: crypto.randomUUID(),
182
+ requestId: mintRequestId(),
182
183
  workspaceId: channel.workspaceId,
183
184
  channelRef: channel.channelRef,
184
185
  name: normalizedName,
@@ -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;IACtK,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,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,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC,+BAiOA"}
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;IACtK,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,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,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC,+BAgOA"}
@@ -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();
@@ -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"}
@@ -0,0 +1,169 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useEffect, useRef, useState } from 'react';
3
+ import { Button, IconChevronDownOutline14, Input, Menu, Modal } from '@deepseek-ai/dsh-client-ui-primitives';
4
+ import { useChannelMembership } from "./team-membership.js";
5
+ import { mintRequestId } from "./requests.js";
6
+ import createCss from './create.module.css';
7
+ import css from './sidebar.module.css';
8
+ /** Model option key inside one editor; opaque and resolved against the loaded groups. */
9
+ function modelKey(provider, model) {
10
+ return `${provider}\u0000${model}`;
11
+ }
12
+ /**
13
+ * Shared provider/model dropdown for the create and edit forms. The option
14
+ * list rides the shared Menu primitive (one leading "follow Host default"
15
+ * row, then non-selectable provider headings) with a capped, internally
16
+ * scrolling card so growing model catalogs cannot stretch the dialog.
17
+ */
18
+ export function ModelPickerField({ model, onModelChange, loadModels, disabled, t }) {
19
+ const [groups, setGroups] = useState();
20
+ const [modelsError, setModelsError] = useState();
21
+ const [open, setModelOpen] = useState(false);
22
+ const [effortOpen, setEffortOpen] = useState(false);
23
+ useEffect(() => {
24
+ let mounted = true;
25
+ void loadModels().then(result => {
26
+ if (!mounted)
27
+ return;
28
+ if (result.ok) {
29
+ setGroups(result.value.groups);
30
+ setModelsError(undefined);
31
+ }
32
+ else {
33
+ setModelsError(result.error.message);
34
+ }
35
+ });
36
+ return () => { mounted = false; };
37
+ }, [loadModels]);
38
+ const items = [{ id: '', label: t('modelFollowDefault') }];
39
+ const byKey = new Map();
40
+ for (const group of groups ?? []) {
41
+ items.push({ type: 'label', id: `model-group:${group.id}`, text: group.name });
42
+ for (const entry of group.models) {
43
+ const key = modelKey(group.id, entry.id);
44
+ byKey.set(key, { provider: group.id, id: entry.id, name: entry.name, efforts: entry.reasoning?.efforts ?? [] });
45
+ items.push({ id: key, label: entry.name });
46
+ }
47
+ }
48
+ const selectedModelKey = model === undefined ? '' : modelKey(model.provider, model.model);
49
+ const triggerLabel = model === undefined
50
+ ? t('modelFollowDefault')
51
+ : byKey.get(selectedModelKey)?.name ?? `${model.provider} / ${model.model}`;
52
+ // The effort sub-row only makes sense for a pinned model with adapter-exposed
53
+ // efforts; following the Host default inherits the operator's whole selection.
54
+ const efforts = model === undefined ? [] : byKey.get(selectedModelKey)?.efforts ?? [];
55
+ const effortItems = [{ id: '', label: t('effortFollowDefault') }, ...efforts.map(effort => ({ id: effort.id, label: effort.name }))];
56
+ const selectedEffort = model?.reasoningEffort ?? '';
57
+ const effortTriggerLabel = model === undefined || selectedEffort === ''
58
+ ? t('effortFollowDefault')
59
+ : efforts.find(effort => effort.id === selectedEffort)?.name ?? selectedEffort;
60
+ 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 => {
61
+ setModelOpen(false);
62
+ const choice = byKey.get(key);
63
+ onModelChange(choice === undefined ? undefined : { provider: choice.provider, model: choice.id });
64
+ }, 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 => {
65
+ setEffortOpen(false);
66
+ onModelChange(key === ''
67
+ ? { provider: model.provider, model: model.model }
68
+ : { provider: model.provider, model: model.model, reasoningEffort: key });
69
+ }, 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, {}) })] }) }))] });
70
+ }
71
+ /**
72
+ * Agent editor: handle, description, and per-Member model selection commit
73
+ * through one durable update; Channel membership below keeps its own
74
+ * immediate add/remove flow.
75
+ */
76
+ export function AgentEditorDialog({ status, channels, loadChannels, updateMember, joinChannel, removeChannelMember, loadModels, onCommitted, onClose, t }) {
77
+ const memberId = status.member.memberId;
78
+ const [handle, setHandle] = useState(status.member.handle);
79
+ const [description, setDescription] = useState(status.member.description);
80
+ const [model, setModel] = useState(status.member.model);
81
+ const [saving, setSaving] = useState(false);
82
+ const [error, setError] = useState();
83
+ const pendingRequest = useRef();
84
+ // Membership comes from a fresh projection at open time so stale checkbox
85
+ // state never flips a committed fact backwards.
86
+ const [memberships, setMemberships] = useState();
87
+ const [loadError, setLoadError] = useState();
88
+ useEffect(() => {
89
+ let mounted = true;
90
+ void loadChannels({ workspaceId: status.member.workspaceId, topLevelOnly: true, includeActivities: false, limit: 1 }).then(result => {
91
+ if (!mounted)
92
+ return;
93
+ if (result.ok) {
94
+ setMemberships(result.value.members);
95
+ setLoadError(undefined);
96
+ }
97
+ else {
98
+ setLoadError(result.error.message);
99
+ }
100
+ });
101
+ return () => { mounted = false; };
102
+ }, [loadChannels, status.member.workspaceId]);
103
+ const membership = useChannelMembership({ joinChannel, removeChannelMember }, change => change.channelRef, change => {
104
+ setMemberships(current => {
105
+ const base = current ?? [];
106
+ return change.joined
107
+ ? base.filter(item => !(item.channelRef === change.channelRef && item.memberId === change.memberId))
108
+ : [...base, { channelRef: change.channelRef, memberId: change.memberId }];
109
+ });
110
+ });
111
+ // A successful join appends the durable fact; removal filters it out above.
112
+ const joinedChannels = new Set((memberships ?? []).filter(item => item.memberId === memberId).map(item => item.channelRef));
113
+ const dirty = handle.trim() !== status.member.handle || description.trim() !== status.member.description
114
+ || !sameModel(model, status.member.model);
115
+ const submit = async (event) => {
116
+ event.preventDefault();
117
+ const normalizedHandle = handle.trim();
118
+ const normalizedDescription = description.trim();
119
+ if (saving || !dirty || normalizedHandle.length === 0)
120
+ return;
121
+ const payload = {
122
+ memberId,
123
+ handle: normalizedHandle,
124
+ description: normalizedDescription,
125
+ ...(model === undefined ? {} : { model }),
126
+ };
127
+ const samePending = pendingRequest.current !== undefined && pendingRequest.current.memberId === payload.memberId
128
+ && pendingRequest.current.handle === payload.handle && pendingRequest.current.description === payload.description
129
+ && sameModel(pendingRequest.current.model, model);
130
+ const request = samePending ? pendingRequest.current : {
131
+ requestId: mintRequestId(),
132
+ ...payload,
133
+ };
134
+ pendingRequest.current = request;
135
+ setSaving(true);
136
+ setError(undefined);
137
+ try {
138
+ const result = await updateMember(request);
139
+ if (result.ok) {
140
+ pendingRequest.current = undefined;
141
+ await onCommitted();
142
+ onClose();
143
+ }
144
+ else {
145
+ setError(result.error.message);
146
+ }
147
+ }
148
+ catch (cause) {
149
+ setError(cause instanceof Error ? cause.message : String(cause));
150
+ }
151
+ finally {
152
+ setSaving(false);
153
+ }
154
+ };
155
+ return (_jsx(Modal, { open: true, onClose: onClose, title: t('editAgent'), description: `@${status.member.handle}`, closeLabel: t('close'), contentClassName: createCss.dialogContent, footer: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", disabled: saving, onClick: onClose, children: t('cancel') }), _jsx(Button, { type: "submit", form: "team-agent-edit-form", variant: "primary", disabled: saving || !dirty || handle.trim().length === 0, children: saving ? t('editSaving') : t('editSave') })] }), children: _jsxs("form", { id: "team-agent-edit-form", className: createCss.form, onSubmit: event => { void submit(event); }, children: [_jsxs("label", { className: createCss.field, children: [_jsx("span", { children: t('agentName') }), _jsx(Input, { className: createCss.input, value: handle, onChange: event => { setHandle(event.target.value); pendingRequest.current = undefined; }, disabled: saving, autoFocus: true })] }), _jsxs("label", { className: createCss.field, children: [_jsxs("span", { children: [t('agentDescription'), t('optionalSuffix')] }), _jsx(Input, { className: createCss.input, value: description, placeholder: t('agentDescriptionPlaceholder'), onChange: event => { setDescription(event.target.value); pendingRequest.current = undefined; }, disabled: saving })] }), _jsx(ModelPickerField, { model: model, onModelChange: choice => { pendingRequest.current = undefined; setModel(choice); }, loadModels: loadModels, disabled: saving, t: t }), _jsxs("fieldset", { className: createCss.memberPicker, disabled: saving, children: [_jsx("legend", { children: t('channelMembersSection') }), channels.map(channel => {
156
+ const joined = joinedChannels.has(channel.channelRef);
157
+ const rowPending = membership.pending.has(channel.channelRef);
158
+ const disabled = rowPending || (!joined && status.presence === 'unavailable');
159
+ const rowError = membership.errors.get(channel.channelRef);
160
+ return _jsxs("div", { className: `${css.editMemberRow} ${css.editMemberRowChannels}`, children: [_jsxs("strong", { className: css.editChannelName, children: ["# ", channel.name] }), _jsx(Button, { size: "sm", variant: "outline", disabled: disabled, onClick: () => { void membership.change({ workspaceId: status.member.workspaceId, channelRef: channel.channelRef, memberId, joined }); }, children: rowPending ? t('membershipUpdating') : joined ? t('removeFromChannel') : t('addToChannel') }), rowError !== undefined && _jsx("p", { className: css.rowError, role: "alert", children: rowError })] }, channel.channelRef);
161
+ }), channels.length === 0 && _jsx("small", { children: t('noChannelsForAgent') }), loadError !== undefined && _jsx("p", { className: css.rowError, role: "alert", children: loadError })] }), error !== undefined && _jsx("p", { className: createCss.error, role: "alert", children: error })] }) }));
162
+ }
163
+ export function sameModel(left, right) {
164
+ if (left === undefined && right === undefined)
165
+ return true;
166
+ if (left === undefined || right === undefined)
167
+ return false;
168
+ return left.provider === right.provider && left.model === right.model && left.reasoningEffort === right.reasoningEffort;
169
+ }
@@ -0,0 +1,17 @@
1
+ import type { PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
2
+ import type { InputTriggerController } from '@deepseek-ai/dsh-client-ui-input-trigger/client';
3
+ /** Public composer-bar slot currency plus Team's scoped trigger controller. */
4
+ export type TeamMemberSessionComposerProps = PropsRuntime<'conversation.composer.bar'> & {
5
+ readonly controller: InputTriggerController;
6
+ /** Team inject provides public Session.cancel as the running stop action. */
7
+ readonly stop?: (() => void) | undefined;
8
+ /** Team inject keeps delivery inside the public InputMachine facade. */
9
+ readonly submitInput: (mode: 'queue' | 'steer') => void;
10
+ };
11
+ /**
12
+ * Narrow, Team-owned member-session composer. It deliberately uses the public
13
+ * input state/actions and trigger controller rather than the shipped InputBar;
14
+ * owner-provided overlay remains inside the card so the shared menu renders.
15
+ */
16
+ export declare function TeamMemberSessionComposer({ controller, inputActions, overlay, placeholder, disabled, blocked, stop, submitInput, useInput, useSession }: TeamMemberSessionComposerProps): import("react").JSX.Element;
17
+ //# sourceMappingURL=TeamMemberSessionComposer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TeamMemberSessionComposer.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMemberSessionComposer.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AACpE,OAAO,KAAK,EAAE,sBAAsB,EAAc,MAAM,iDAAiD,CAAA;AAIzG,+EAA+E;AAC/E,MAAM,MAAM,8BAA8B,GAAG,YAAY,CAAC,2BAA2B,CAAC,GAAG;IACvF,QAAQ,CAAC,UAAU,EAAE,sBAAsB,CAAA;IAC3C,6EAA6E;IAC7E,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,SAAS,CAAA;IACxC,wEAAwE;IACxE,QAAQ,CAAC,WAAW,EAAE,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO,KAAK,IAAI,CAAA;CACxD,CAAA;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CAAC,EAAE,UAAU,EAAE,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,UAAU,EAAE,EAAE,8BAA8B,+BA6GvL"}