@wowyuarm/dsh-agent-team 0.1.4 → 0.1.6

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 (77) hide show
  1. package/README.md +15 -1
  2. package/README.zh.md +20 -3
  3. package/package.json +7 -2
  4. package/packages/agent-team/README.md +6 -0
  5. package/packages/agent-team/README.zh.md +6 -0
  6. package/packages/agent-team/core-skills/member-skill-manager/SKILL.md +41 -0
  7. package/packages/agent-team/core-skills/member-skill-manager/references/auth-and-config.md +30 -0
  8. package/packages/agent-team/core-skills/member-skill-manager/references/writing-great-skills.md +17 -0
  9. package/packages/agent-team/lib/auto-compaction.js +39 -1
  10. package/packages/agent-team/lib/index.js +340 -7
  11. package/packages/agent-team/lib/ledger.js +219 -7
  12. package/packages/agent-team/lib/member-context.js +2 -2
  13. package/packages/agent-team/lib/member-skills.js +94 -0
  14. package/packages/agent-team/lib/spec.js +38 -0
  15. package/packages/agent-team/lib/typert.host.js +202 -27
  16. package/packages/agent-team/lib/typert.remote-client.d.ts +3 -1
  17. package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
  18. package/packages/agent-team/lib/typert.remote-client.js +155 -22
  19. package/packages/agent-team/lib/types/auto-compaction.d.ts +9 -0
  20. package/packages/agent-team/lib/types/auto-compaction.d.ts.map +1 -1
  21. package/packages/agent-team/lib/types/index.d.ts +81 -1
  22. package/packages/agent-team/lib/types/index.d.ts.map +1 -1
  23. package/packages/agent-team/lib/types/ledger.d.ts +49 -1
  24. package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
  25. package/packages/agent-team/lib/types/member-skills.d.ts +45 -0
  26. package/packages/agent-team/lib/types/member-skills.d.ts.map +1 -0
  27. package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
  28. package/packages/agent-team/lib/types/types.d.ts +127 -2
  29. package/packages/agent-team/lib/types/types.d.ts.map +1 -1
  30. package/packages/agent-team/preset/team-member/agent.cordis.yml +7 -4
  31. package/packages/client-agent-team/README.md +1 -1
  32. package/packages/client-agent-team/README.zh.md +1 -1
  33. package/packages/client-agent-team/lib/client.js +677 -363
  34. package/packages/client-agent-team/lib/client.js.map +1 -1
  35. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts +2 -4
  36. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
  37. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +48 -24
  38. package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
  39. package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +20 -0
  40. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts.map +1 -1
  41. package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +3 -1
  42. package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts.map +1 -1
  43. package/packages/client-agent-team/lib/types/client/TeamComposer.js +73 -17
  44. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts +4 -8
  45. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts.map +1 -1
  46. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.js +7 -40
  47. package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.d.ts.map +1 -1
  48. package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.js +16 -1
  49. package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -1
  50. package/packages/client-agent-team/lib/types/client/TeamMessage.js +48 -18
  51. package/packages/client-agent-team/lib/types/client/TeamRowMenu.d.ts +3 -2
  52. package/packages/client-agent-team/lib/types/client/TeamRowMenu.d.ts.map +1 -1
  53. package/packages/client-agent-team/lib/types/client/TeamSidebarSection.d.ts +5 -2
  54. package/packages/client-agent-team/lib/types/client/TeamSidebarSection.d.ts.map +1 -1
  55. package/packages/client-agent-team/lib/types/client/TeamSidebarSection.js +2 -4
  56. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
  57. package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +82 -22
  58. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts +1 -1
  59. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts.map +1 -1
  60. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +4 -2
  61. package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
  62. package/packages/client-agent-team/lib/types/client/index.js +15 -13
  63. package/packages/client-agent-team/lib/types/client/locales.d.ts +20 -16
  64. package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
  65. package/packages/client-agent-team/lib/types/client/locales.js +20 -16
  66. package/packages/client-agent-team/lib/types/client/sidebar-sections.d.ts +16 -0
  67. package/packages/client-agent-team/lib/types/client/sidebar-sections.d.ts.map +1 -0
  68. package/packages/client-agent-team/lib/types/client/sidebar-sections.js +102 -0
  69. package/packages/client-agent-team/lib/types/client/slots.d.ts +2 -1
  70. package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
  71. package/packages/client-agent-team/lib/types/client/team-formatters.d.ts.map +1 -1
  72. package/packages/client-agent-team/lib/types/client/team-formatters.js +9 -2
  73. package/packages/client-agent-team/lib/types/client/timeline-scroll.d.ts +5 -7
  74. package/packages/client-agent-team/lib/types/client/timeline-scroll.d.ts.map +1 -1
  75. package/packages/client-agent-team/lib/types/client/timeline-scroll.js +9 -22
  76. package/packages/tool-agent-team/lib/index.js +25 -5
  77. package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -1
@@ -5,12 +5,10 @@ interface TeamAgentsPanelProps {
5
5
  readonly workspaceId: WorkspaceId;
6
6
  readonly loadMembers: TeamSidebarProps['loadMembers'];
7
7
  readonly subscribeChanges: TeamSidebarProps['subscribeChanges'];
8
- readonly loadChannels: TeamSidebarProps['loadChannels'];
9
8
  readonly addMember: TeamSidebarProps['addMember'];
10
9
  readonly updateMember: TeamSidebarProps['updateMember'];
11
10
  readonly recoverMember: TeamSidebarProps['recoverMember'];
12
- readonly joinChannel: TeamSidebarProps['joinChannel'];
13
- readonly removeChannelMember: TeamSidebarProps['removeChannelMember'];
11
+ readonly clearMemberContext: TeamSidebarProps['clearMemberContext'];
14
12
  readonly loadModels: TeamSidebarProps['loadModels'];
15
13
  /** The Member Session currently embedded in the conversation seat, if any. */
16
14
  readonly memberSessionId?: AgentTeamClientMemberStatus['member']['sessionId'];
@@ -18,6 +16,6 @@ interface TeamAgentsPanelProps {
18
16
  readonly onCreatingChange: (request: AgentTeamAddMemberRequest, creating: boolean) => void;
19
17
  readonly t: TeamSidebarProps['t'];
20
18
  }
21
- export declare function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, loadChannels, addMember, updateMember, recoverMember, joinChannel, removeChannelMember, loadModels, memberSessionId, openMemberSession, onCreatingChange, t }: TeamAgentsPanelProps): import("react").JSX.Element;
19
+ export declare function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, addMember, updateMember, recoverMember, clearMemberContext, loadModels, memberSessionId, openMemberSession, onCreatingChange, t }: TeamAgentsPanelProps): import("react").JSX.Element;
22
20
  export {};
23
21
  //# sourceMappingURL=TeamAgentsPanel.d.ts.map
@@ -1 +1 @@
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
+ {"version":3,"file":"TeamAgentsPanel.d.ts","sourceRoot":"","sources":["../../../src/client/TeamAgentsPanel.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,yBAAyB,EACzB,2BAA2B,EAE5B,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,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,kBAAkB,EAAE,gBAAgB,CAAC,oBAAoB,CAAC,CAAA;IACnE,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,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,UAAU,EAAE,eAAe,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,CAAC,EAAE,EAAE,oBAAoB,+BAmKpO"}
@@ -1,20 +1,18 @@
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, IconEditOutline16, IconPlayOutline16, IconPlusOutline16, IconRefreshOutline16, Input, Modal, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
3
+ import { Button, IconEditOutline16, IconNewChatOutline16, 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 { useSidebarSectionOpen, setSidebarSectionOpen } from "./sidebar-sections.js";
7
8
  import { mintRequestId } from "./requests.js";
8
9
  import { TeamRowMenu } from "./TeamRowMenu.js";
9
10
  import { TeamSidebarSection } from "./TeamSidebarSection.js";
10
- import { MultiMenuField } from "./multi-menu-field.js";
11
11
  import { AgentEditorDialog, ModelPickerField, sameModel } from "./TeamMemberEditor.js";
12
12
  import createCss from './create.module.css';
13
13
  import css from './sidebar.module.css';
14
- export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, loadChannels, addMember, updateMember, recoverMember, joinChannel, removeChannelMember, loadModels, memberSessionId, openMemberSession, onCreatingChange, t }) {
14
+ export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, addMember, updateMember, recoverMember, clearMemberContext, loadModels, memberSessionId, openMemberSession, onCreatingChange, t }) {
15
15
  const [members, setMembers] = useState([]);
16
- const [channels, setChannels] = useState([]);
17
- const [channelRefs, setChannelRefs] = useState([]);
18
16
  const [loading, setLoading] = useState(true);
19
17
  const [error, setError] = useState();
20
18
  const [formOpen, setFormOpen] = useState(false);
@@ -36,6 +34,7 @@ export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, lo
36
34
  void moveSidebarItem(workspaceId, 'agents', orderedAgentRefs, movedRef, targetRef, marker);
37
35
  };
38
36
  const drag = useSidebarRowDrag({ refs: orderedAgentRefs, onCommit: applyMove });
37
+ const sectionOpen = useSidebarSectionOpen(workspaceId, 'agents');
39
38
  const refresh = useCallback(async () => {
40
39
  setLoading(true);
41
40
  const result = await loadMembers({ workspaceId });
@@ -49,22 +48,15 @@ export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, lo
49
48
  setLoading(false);
50
49
  }, [loadMembers, workspaceId]);
51
50
  useEffect(() => { void refresh(); }, [refresh]);
52
- const loadWorkspaceChannels = useCallback(async () => {
53
- const result = await loadChannels({ workspaceId, topLevelOnly: true, includeActivities: false, limit: 1 });
54
- if (result.ok)
55
- setChannels(result.value.channels);
56
- }, [loadChannels, workspaceId]);
57
- useEffect(() => { void loadWorkspaceChannels(); }, [loadWorkspaceChannels]);
58
51
  useEffect(() => subscribeChanges({ kind: 'workspace', workspaceId }, update => {
59
52
  if (update.type === 'failed') {
60
53
  setError(update.message);
61
54
  return;
62
55
  }
63
- // The section stays mounted across Channel creation, so both the Member
64
- // roster and the Channel list ride every workspace invalidation.
56
+ // The section stays mounted across Channel creation, so the Member roster
57
+ // rides every workspace invalidation.
65
58
  void refresh();
66
- void loadWorkspaceChannels();
67
- }), [subscribeChanges, refresh, loadWorkspaceChannels, workspaceId]);
59
+ }), [subscribeChanges, refresh, workspaceId]);
68
60
  const closeForm = () => {
69
61
  if (creating)
70
62
  return;
@@ -86,7 +78,6 @@ export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, lo
86
78
  setHandle('');
87
79
  setDescription('');
88
80
  setModel(undefined);
89
- setChannelRefs([]);
90
81
  setFormOpen(false);
91
82
  if (result.value.status.presence === 'unavailable') {
92
83
  setError(result.value.status.diagnostic ?? t('statusUnavailable'));
@@ -118,30 +109,30 @@ export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, lo
118
109
  const sameRequest = retryRequest !== undefined && retryRequest.workspaceId === workspaceId
119
110
  && retryRequest.handle === normalizedHandle && retryRequest.description === normalizedDescription
120
111
  && sameModel(retryRequest.model, model)
121
- && retryRequest.channelRefs.length === channelRefs.length && retryRequest.channelRefs.every(ref => channelRefs.includes(ref));
112
+ && retryRequest.channelRefs.length === 0;
122
113
  void provision(sameRequest ? retryRequest : {
123
114
  requestId: mintRequestId(),
124
115
  workspaceId,
125
116
  handle: normalizedHandle,
126
117
  description: normalizedDescription,
127
118
  presetId: 'team-member',
128
- channelRefs,
119
+ // No initial Channels at creation: the Member joins Channels later from
120
+ // the Channel side and stays reachable through its DM view meanwhile.
121
+ channelRefs: [],
129
122
  ...(model === undefined ? {} : { model }),
130
123
  });
131
124
  };
132
- return (_jsxs("div", { className: css.panel, children: [_jsx(Modal, { open: formOpen, onClose: closeForm, title: t('addAgent'), closeLabel: t('close'), contentClassName: createCss.dialogContent, footer: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", disabled: creating, onClick: closeForm, children: t('cancel') }), _jsx(Button, { type: "submit", form: "team-agent-create-form", variant: "primary", disabled: creating || handle.trim().length === 0, children: creating ? t('creatingAgent') : t('createAgent') })] }), children: _jsxs("form", { id: "team-agent-create-form", className: createCss.form, onSubmit: submit, 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); setRetryRequest(undefined); }, disabled: creating, 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); setRetryRequest(undefined); }, disabled: creating })] }), _jsx(ModelPickerField, { model: model, onModelChange: choice => { setModel(choice); setRetryRequest(undefined); }, loadModels: loadModels, disabled: creating, t: t }), _jsx(MultiMenuField, { label: `${t('initialChannels')}${t('optionalSuffix')}`, disabled: creating, options: channels.map(channel => ({ id: channel.channelRef, label: channel.name })), selected: channelRefs, onToggle: ref => {
133
- setChannelRefs(current => current.includes(ref) ? current.filter(item => item !== ref) : [...current, ref]);
134
- setRetryRequest(undefined);
135
- }, emptyText: t('noChannelsForAgent'), triggerEmptyLabel: t('channelsPickerEmpty'), formatCount: count => t('channelsPickerCount', { count }) }), formOpen && error !== undefined && _jsx("p", { className: createCss.error, role: "alert", children: error })] }) }), _jsxs(TeamSidebarSection, { title: t('agents'), actions: (_jsx(Tooltip, { label: t('addAgent'), delayMs: 500, children: _jsx("button", { ref: triggerRef, type: "button", className: css.iconButton, "aria-label": t('addAgent'), onClick: () => { setError(undefined); setFormOpen(true); }, children: _jsx(IconPlusOutline16, { size: 14 }) }) })), children: [loading && members.length === 0 && _jsx("p", { className: css.emptyState, children: t('loadingAgents') }), !loading && members.length === 0 && _jsx("p", { className: css.emptyState, children: t('emptyAgents') }), _jsx("div", { className: css.agentList, children: orderedMembers.map(status => (_jsx(SortableRow, { drag: drag, orderKey: status.member.memberId, children: _jsx(AgentRow, { status: status, ...(memberSessionId === undefined ? {} : { current: status.member.sessionId === memberSessionId }), channels: channels, loadChannels: loadChannels, updateMember: updateMember, recoverMember: recoverMember, joinChannel: joinChannel, removeChannelMember: removeChannelMember, loadModels: loadModels, openMemberSession: openMemberSession, onUpdated: () => { void refresh(); }, t: t }) }, status.member.memberId))) })] }), !formOpen && error !== undefined && (_jsxs("div", { className: css.retryError, role: "alert", children: [_jsx("span", { children: error }), retryRequest !== undefined && _jsx("button", { type: "button", className: css.textButton, disabled: creating, onClick: () => { setFormOpen(true); }, children: t('retry') })] }))] }));
125
+ return (_jsxs("div", { className: css.panel, children: [_jsx(Modal, { open: formOpen, onClose: closeForm, title: t('addAgent'), closeLabel: t('close'), contentClassName: createCss.dialogContent, footer: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", disabled: creating, onClick: closeForm, children: t('cancel') }), _jsx(Button, { type: "submit", form: "team-agent-create-form", variant: "primary", disabled: creating || handle.trim().length === 0, children: creating ? t('creatingAgent') : t('createAgent') })] }), children: _jsxs("form", { id: "team-agent-create-form", className: createCss.form, onSubmit: submit, 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); setRetryRequest(undefined); }, disabled: creating, 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); setRetryRequest(undefined); }, disabled: creating })] }), _jsx(ModelPickerField, { model: model, onModelChange: choice => { setModel(choice); setRetryRequest(undefined); }, loadModels: loadModels, disabled: creating, t: t }), formOpen && error !== undefined && _jsx("p", { className: createCss.error, role: "alert", children: error })] }) }), _jsxs(TeamSidebarSection, { title: t('agents'), open: sectionOpen, onToggle: open => { setSidebarSectionOpen(workspaceId, 'agents', open); }, actions: (_jsx(Tooltip, { label: t('addAgent'), delayMs: 500, children: _jsx("button", { ref: triggerRef, type: "button", className: css.iconButton, "aria-label": t('addAgent'), onClick: () => { setError(undefined); setFormOpen(true); }, children: _jsx(IconPlusOutline16, { size: 14 }) }) })), children: [loading && members.length === 0 && _jsx("p", { className: css.emptyState, children: t('loadingAgents') }), !loading && members.length === 0 && _jsx("p", { className: css.emptyState, children: t('emptyAgents') }), _jsx("div", { className: css.agentList, children: orderedMembers.map(status => (_jsx(SortableRow, { drag: drag, orderKey: status.member.memberId, children: _jsx(AgentRow, { status: status, ...(memberSessionId === undefined ? {} : { current: status.member.sessionId === memberSessionId }), updateMember: updateMember, recoverMember: recoverMember, clearMemberContext: clearMemberContext, loadModels: loadModels, openMemberSession: openMemberSession, onUpdated: () => { void refresh(); }, t: t }) }, status.member.memberId))) })] }), !formOpen && error !== undefined && (_jsxs("div", { className: css.retryError, role: "alert", children: [_jsx("span", { children: error }), retryRequest !== undefined && _jsx("button", { type: "button", className: css.textButton, disabled: creating, onClick: () => { setFormOpen(true); }, children: t('retry') })] }))] }));
136
126
  }
137
127
  /**
138
128
  * One sidebar Agent row: the select button opens the Member's own Session
139
129
  * conversation page, the avatar carries identity plus the presence badge, and
140
130
  * the row menu opens the editor.
141
131
  */
142
- function AgentRow({ status, current, channels, loadChannels, updateMember, recoverMember, joinChannel, removeChannelMember, loadModels, openMemberSession, onUpdated, t }) {
132
+ function AgentRow({ status, current, updateMember, recoverMember, clearMemberContext, loadModels, openMemberSession, onUpdated, t }) {
143
133
  const [menuOpen, setMenuOpen] = useState(false);
144
134
  const [editing, setEditing] = useState(false);
135
+ const [clearing, setClearing] = useState(false);
145
136
  const [rowAlert, setRowAlert] = useState();
146
137
  // Both row actions ride the same runtime remote: the Host steers a live
147
138
  // session, rebuilds an orphaned composition, or re-runs a failed activation.
@@ -171,14 +162,47 @@ function AgentRow({ status, current, channels, loadChannels, updateMember, recov
171
162
  setRowAlert(t('restartFailed', { message: cause instanceof Error ? cause.message : String(cause) }));
172
163
  }
173
164
  };
165
+ const clearContext = async () => {
166
+ try {
167
+ const result = await clearMemberContext({
168
+ requestId: mintRequestId(),
169
+ workspaceId: status.member.workspaceId,
170
+ memberId: status.member.memberId,
171
+ });
172
+ await onUpdated();
173
+ if (!result.ok) {
174
+ setRowAlert(t('clearContextFailed', { message: result.error.message }));
175
+ return;
176
+ }
177
+ setRowAlert(undefined);
178
+ // The Host moved the Member onto a fresh Session id (the previous log
179
+ // stays archived on disk). A new id has no resident client instance, so
180
+ // re-entering the member view lazily instantiates it from host truth:
181
+ // blank hero, live updates, and no disposed-generation gray-out.
182
+ if (current === true)
183
+ openMemberSession(result.value.status.member.sessionId);
184
+ }
185
+ catch (cause) {
186
+ setRowAlert(t('clearContextFailed', { message: cause instanceof Error ? cause.message : String(cause) }));
187
+ }
188
+ };
189
+ // Error Members keep a live idle handle, so starting from a new context
190
+ // is available to them too — it doubles as a recovery path (the broken
191
+ // handle's error markers are dropped with it). Only a running turn or a
192
+ // missing handle gates the entry.
193
+ const contextClearable = status.presence === 'available' || status.presence === 'error';
174
194
  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
195
  { id: 'edit', label: t('editAgent'), icon: _jsx(IconEditOutline16, {}) },
176
196
  ...(status.presence === 'error' ? [{ id: 'resume', label: t('resumeAgent'), icon: _jsx(IconPlayOutline16, {}) }] : []),
177
197
  ...(status.availability === 'unavailable' ? [{ id: 'restart', label: t('restartAgent'), icon: _jsx(IconRefreshOutline16, {}) }] : []),
198
+ { id: 'clear-context', label: t('clearContextAgent'), icon: _jsx(IconNewChatOutline16, {}), danger: true, disabled: !contextClearable },
199
+ ...(!contextClearable ? [{ type: 'label', id: 'clear-context-reason', text: status.presence === 'working' ? t('clearContextWorkingReason') : t('clearContextUnavailableReason') }] : []),
178
200
  ], onSelect: (id) => {
179
201
  if (id === 'edit')
180
202
  setEditing(true);
203
+ else if (id === 'clear-context')
204
+ setClearing(true);
181
205
  else
182
206
  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 }))] }));
207
+ }, onOpenChange: setMenuOpen }) })] }), rowAlert !== undefined && _jsx("div", { className: css.rowAlert, role: "alert", children: rowAlert }), clearing && (_jsx(Modal, { open: true, onClose: () => { setClearing(false); }, title: t('clearContextTitle', { name: status.member.handle }), closeLabel: t('close'), contentClassName: createCss.dialogContent, footer: _jsxs(_Fragment, { children: [_jsx(Button, { variant: "outline", onClick: () => { setClearing(false); }, children: t('cancel') }), _jsx(Button, { variant: "primary", onClick: () => { setClearing(false); void clearContext(); }, children: t('clearContextConfirm') })] }), children: _jsx("p", { className: createCss.error, children: t('clearContextNotice', { name: status.member.handle }) }) })), editing && (_jsx(AgentEditorDialog, { status: status, updateMember: updateMember, loadModels: loadModels, onCommitted: onUpdated, onClose: () => { setEditing(false); }, t: t }))] }));
184
208
  }
@@ -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;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"}
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,+BAmVnR"}
@@ -60,6 +60,26 @@ export function TeamChannelPage({ workspaceId, channelRef, loadChannels, subscri
60
60
  selectChannel(ref);
61
61
  return;
62
62
  }
63
+ if (ref.startsWith('thread:')) {
64
+ const match = view?.items.find(item => item.thread.threadRef === ref);
65
+ if (match !== undefined) {
66
+ selectThread(match.thread.threadRef, channelRef, match.task?.taskRef, match.taskNumber);
67
+ return;
68
+ }
69
+ // A ref may point outside the currently loaded Channel window. Ask the
70
+ // Host for the bounded Thread view so it can provide the home Channel.
71
+ void loadChannels({ workspaceId, threadRef: ref, includeActivities: false, limit: 1 }).then(result => {
72
+ if (!result.ok)
73
+ return;
74
+ const target = result.value.items[0];
75
+ if (target !== undefined) {
76
+ const targetChannel = result.value.channels.find(channel => channel.channelRef === target.message.channelRef);
77
+ if (targetChannel !== undefined)
78
+ selectThread(target.thread.threadRef, targetChannel.channelRef, target.task?.taskRef, target.taskNumber);
79
+ }
80
+ });
81
+ return;
82
+ }
63
83
  if (ref.startsWith('task:')) {
64
84
  const match = view?.items.find(item => item.task?.taskRef === ref);
65
85
  if (match !== undefined) {
@@ -1 +1 @@
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"}
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;AAalD,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,+BA8L9D"}
@@ -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 { useSidebarSectionOpen, setSidebarSectionOpen } from "./sidebar-sections.js";
8
9
  import { mintRequestId } from "./requests.js";
9
10
  import { TeamRowMenu } from "./TeamRowMenu.js";
10
11
  import { TeamSidebarSection } from "./TeamSidebarSection.js";
@@ -37,6 +38,7 @@ export function TeamChannelsPanel(props) {
37
38
  void moveSidebarItem(workspaceId, 'channels', orderedChannelRefs, movedRef, targetRef, marker);
38
39
  };
39
40
  const drag = useSidebarRowDrag({ refs: orderedChannelRefs, onCommit: applyMove });
41
+ const sectionOpen = useSidebarSectionOpen(workspaceId, 'channels');
40
42
  const refresh = useCallback(async () => {
41
43
  setLoading(true);
42
44
  const [channelResult, memberResult] = await Promise.all([
@@ -144,7 +146,7 @@ export function TeamChannelsPanel(props) {
144
146
  return next;
145
147
  });
146
148
  setPendingCreate(undefined);
147
- }, triggerEmptyLabel: t('membersPickerEmpty'), formatCount: count => t('membersPickerCount', { count }) }), error !== undefined && _jsx("p", { className: createCss.error, role: "alert", children: error })] }) }), _jsxs(TeamSidebarSection, { title: t('channels'), actions: (_jsx(Tooltip, { label: t('addChannel'), delayMs: 500, children: _jsx("button", { ref: triggerRef, type: "button", className: css.iconButton, "aria-label": t('addChannel'), onClick: () => { setError(undefined); setFormOpen(true); }, children: _jsx(IconPlusOutline16, { size: 14 }) }) })), children: [loading && view === undefined && _jsx("p", { className: css.emptyState, children: t('loadingChannels') }), !loading && (view?.channels.length ?? 0) === 0 && _jsx("p", { className: css.emptyState, children: t('emptyChannels') }), _jsx("div", { className: css.channelList, children: orderedChannels.map(channel => {
149
+ }, triggerEmptyLabel: t('membersPickerEmpty'), formatCount: count => t('membersPickerCount', { count }) }), error !== undefined && _jsx("p", { className: createCss.error, role: "alert", children: error })] }) }), _jsxs(TeamSidebarSection, { title: t('channels'), open: sectionOpen, onToggle: open => { setSidebarSectionOpen(workspaceId, 'channels', open); }, actions: (_jsx(Tooltip, { label: t('addChannel'), delayMs: 500, children: _jsx("button", { ref: triggerRef, type: "button", className: css.iconButton, "aria-label": t('addChannel'), onClick: () => { setError(undefined); setFormOpen(true); }, children: _jsx(IconPlusOutline16, { size: 14 }) }) })), children: [loading && view === undefined && _jsx("p", { className: css.emptyState, children: t('loadingChannels') }), !loading && (view?.channels.length ?? 0) === 0 && _jsx("p", { className: css.emptyState, children: t('emptyChannels') }), _jsx("div", { className: css.channelList, children: orderedChannels.map(channel => {
148
150
  const joined = membership.get(channel.channelRef) ?? new Set();
149
151
  return (_jsx(SortableRow, { drag: drag, orderKey: channel.channelRef, children: _jsx(ChannelRow, { channel: channel, members: members, joinedIds: joined, selected: selectedChannelRef === channel.channelRef, updateChannel: updateChannel, joinChannel: props.joinChannel, removeChannelMember: props.removeChannelMember, onCommitted: () => { void refresh(); }, selectChannel: selectChannel, t: t }) }, channel.channelRef));
150
152
  }) })] }), !formOpen && error !== undefined && _jsx("p", { className: css.error, role: "alert", children: error })] }));
@@ -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,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
+ {"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;AA8DvD,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,+BAqSA"}
@@ -30,6 +30,13 @@ function mentionCandidates(members, query) {
30
30
  && status.member.state !== 'inactive'
31
31
  && status.member.handle.toLocaleLowerCase().startsWith(normalized));
32
32
  }
33
+ /** Every member a manual handle pick could reach: the @all expansion snapshot. */
34
+ function allMentionMembers(members) {
35
+ return members.filter(status => status.presence !== 'unavailable' && status.member.state !== 'inactive');
36
+ }
37
+ function containsAllMention(draft) {
38
+ return /(?:^|[^\p{L}\p{N}_])@all(?=$|[^\p{L}\p{N}_])/u.test(draft);
39
+ }
33
40
  /** One object URL per draft file; revoked when the draft is removed. */
34
41
  const draftPreviewUrls = new WeakMap();
35
42
  function draftPreviewUrl(file) {
@@ -50,9 +57,15 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
50
57
  const composingRef = useRef(false);
51
58
  const [mention, setMention] = useState();
52
59
  const [highlight, setHighlight] = useState(0);
53
- const candidates = mention === undefined ? [] : mentionCandidates(members, mention.query);
54
- const menuOpen = mention !== undefined && candidates.length > 0;
55
- const activeCandidate = candidates[highlight];
60
+ // @all is a composer-layer expansion of the same eligibility filter a
61
+ // handle pick uses; the fixed row stays on top of the matching members.
62
+ const memberCandidates = mention === undefined ? [] : mentionCandidates(members, mention.query);
63
+ const options = mention !== undefined && 'all'.startsWith(mention.query.toLocaleLowerCase())
64
+ ? [{ kind: 'all' }, ...memberCandidates.map(status => ({ kind: 'member', status }))]
65
+ : memberCandidates.map(status => ({ kind: 'member', status }));
66
+ const menuOpen = mention !== undefined && options.length > 0;
67
+ const activeOption = options[highlight];
68
+ const allCount = allMentionMembers(members).length;
56
69
  const listId = 'team-mention-suggestions';
57
70
  const menuMaxHeight = useAnchoredMaxHeight(menuRef, 320, menuOpen ? draft : null);
58
71
  useDismissOnOutsidePointer(rootRef, menuOpen, open => { if (!open)
@@ -65,12 +78,12 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
65
78
  input.style.height = `${Math.min(input.scrollHeight, 180)}px`;
66
79
  }, [draft]);
67
80
  useEffect(() => {
68
- if (mention === undefined || candidates.length === 0) {
81
+ if (mention === undefined || options.length === 0) {
69
82
  setHighlight(0);
70
83
  return;
71
84
  }
72
- setHighlight(current => Math.min(current, candidates.length - 1));
73
- }, [mention, candidates.length]);
85
+ setHighlight(current => Math.min(current, options.length - 1));
86
+ }, [mention, options.length]);
74
87
  // Match the resident DSH composer without stealing a later user choice: Team
75
88
  // data can load after navigation, so a dialog or another control may already
76
89
  // own focus by the time this composer appears.
@@ -88,6 +101,10 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
88
101
  inputRef.current?.focus({ preventScroll: true });
89
102
  }, [confirmation, pending]);
90
103
  const pruneRecipients = (nextDraft) => {
104
+ // An @all marker stands for its expansion snapshot: the member handles it
105
+ // stands for are not in the text, so text-based pruning must stand down.
106
+ if (containsAllMention(nextDraft))
107
+ return;
91
108
  const knownMembers = new Map(members.map(status => [status.member.memberId, status.member]));
92
109
  const next = new Set([...recipients].filter(memberId => {
93
110
  const member = knownMembers.get(memberId);
@@ -117,9 +134,31 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
117
134
  pruneRecipients(nextDraft);
118
135
  updateMention(nextDraft, event.target.selectionStart ?? nextDraft.length);
119
136
  };
120
- const selectMention = (member) => {
137
+ const selectOption = (option) => {
121
138
  if (mention === undefined)
122
139
  return;
140
+ if (option.kind === 'all') {
141
+ // Expand at pick time: the recipients snapshot is every eligible member
142
+ // a handle pick could reach at this moment.
143
+ const nextDraft = `${draft.slice(0, mention.start)}@all ${draft.slice(mention.end)}`;
144
+ const nextCaret = mention.start + '@all '.length;
145
+ const nextRecipients = new Set(recipients);
146
+ for (const status of allMentionMembers(members))
147
+ nextRecipients.add(status.member.memberId);
148
+ onDraftChange(nextDraft);
149
+ onRecipientsChange(nextRecipients);
150
+ setMention(undefined);
151
+ setHighlight(0);
152
+ requestAnimationFrame(() => {
153
+ const input = inputRef.current;
154
+ if (input === null)
155
+ return;
156
+ input.focus({ preventScroll: true });
157
+ input.setSelectionRange(nextCaret, nextCaret);
158
+ });
159
+ return;
160
+ }
161
+ const member = option.status;
123
162
  const inserted = `@${member.member.handle} `;
124
163
  const nextDraft = `${draft.slice(0, mention.start)}${inserted}${draft.slice(mention.end)}`;
125
164
  const nextCaret = mention.start + inserted.length;
@@ -136,16 +175,31 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
136
175
  input.setSelectionRange(nextCaret, nextCaret);
137
176
  });
138
177
  };
178
+ // Pasted files (screenshots, copies) join the same chips the "+" picker
179
+ // fills; only a paste that carries files is intercepted, so plain text
180
+ // keeps the browser's native insertion.
181
+ const onPaste = (event) => {
182
+ if (pending || onFilesChange === undefined || pendingFiles === undefined)
183
+ return;
184
+ const files = Array.from(event.clipboardData.items)
185
+ .filter(item => item.kind === 'file')
186
+ .map(item => item.getAsFile())
187
+ .filter((file) => file !== null);
188
+ if (files.length === 0)
189
+ return;
190
+ event.preventDefault();
191
+ onFilesChange([...pendingFiles, ...files]);
192
+ };
139
193
  const onKeyDown = (event) => {
140
194
  const composing = composingRef.current || event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229;
141
195
  if (menuOpen && event.key === 'ArrowDown') {
142
196
  event.preventDefault();
143
- setHighlight(current => (current + 1) % candidates.length);
197
+ setHighlight(current => (current + 1) % options.length);
144
198
  return;
145
199
  }
146
200
  if (menuOpen && event.key === 'ArrowUp') {
147
201
  event.preventDefault();
148
- setHighlight(current => (current - 1 + candidates.length) % candidates.length);
202
+ setHighlight(current => (current - 1 + options.length) % options.length);
149
203
  return;
150
204
  }
151
205
  if (event.key === 'Escape' && menuOpen) {
@@ -156,15 +210,15 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
156
210
  // Tab accepts the highlighted candidate; Shift+Tab keeps default focus reversal.
157
211
  if (menuOpen && event.key === 'Tab' && !event.shiftKey) {
158
212
  event.preventDefault();
159
- if (activeCandidate !== undefined)
160
- selectMention(activeCandidate);
213
+ if (activeOption !== undefined)
214
+ selectOption(activeOption);
161
215
  return;
162
216
  }
163
217
  if (event.key !== 'Enter' || event.shiftKey || composing || event.repeat)
164
218
  return;
165
- if (menuOpen && activeCandidate !== undefined) {
219
+ if (menuOpen && activeOption !== undefined) {
166
220
  event.preventDefault();
167
- selectMention(activeCandidate);
221
+ selectOption(activeOption);
168
222
  return;
169
223
  }
170
224
  event.preventDefault();
@@ -175,11 +229,13 @@ export function TeamComposer({ members, recipients, draft, pending, confirmation
175
229
  event.preventDefault();
176
230
  if (!pending && draft.trim() !== '')
177
231
  onSubmit();
178
- }, children: [_jsxs("div", { className: css.card, "data-team-composer": true, children: [confirmation !== undefined && _jsx("p", { className: css.confirmation, role: "status", children: confirmation }), _jsxs("div", { className: css.inputArea, children: [menuOpen && _jsx("div", { id: listId, ref: menuRef, className: css.mentionMenu, role: "listbox", "aria-label": t('mentionSuggestions'), style: { maxHeight: menuMaxHeight }, children: candidates.map((status, index) => {
179
- const optionId = `${listId}-${status.member.memberId}`;
232
+ }, children: [_jsxs("div", { className: css.card, "data-team-composer": true, children: [confirmation !== undefined && _jsx("p", { className: css.confirmation, role: "status", children: confirmation }), _jsxs("div", { className: css.inputArea, children: [menuOpen && _jsx("div", { id: listId, ref: menuRef, className: css.mentionMenu, role: "listbox", "aria-label": t('mentionSuggestions'), style: { maxHeight: menuMaxHeight }, children: options.map((option, index) => {
233
+ const optionId = option.kind === 'all' ? `${listId}-all` : `${listId}-${option.status.member.memberId}`;
180
234
  const selected = index === highlight;
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);
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) => {
235
+ return option.kind === 'all'
236
+ ? _jsxs("button", { id: optionId, type: "button", role: "option", "aria-selected": selected, className: css.mentionOption, onMouseDown: event => { event.preventDefault(); }, onClick: () => { selectOption(option); }, children: [_jsx("span", { className: css.mentionAllDot, "aria-hidden": "true" }), _jsx("span", { className: css.mentionName, children: "@all" }), _jsx("span", { className: css.mentionDescription, children: t('mentionAll', { count: allCount }) })] }, "all")
237
+ : _jsxs("button", { id: optionId, type: "button", role: "option", "aria-selected": selected, className: css.mentionOption, onMouseDown: event => { event.preventDefault(); }, onClick: () => { selectOption(option); }, children: [_jsx(TeamPresenceDot, { status: option.status, t: t }), _jsxs("span", { className: css.mentionName, children: ["@", option.status.member.handle] }), _jsx("span", { className: css.mentionDescription, children: option.status.member.description })] }, option.status.member.memberId);
238
+ }) }), _jsx("textarea", { ref: inputRef, "aria-label": t('messageDraft'), "aria-autocomplete": "list", "aria-controls": menuOpen ? listId : undefined, "aria-activedescendant": menuOpen && activeOption !== undefined ? activeOption.kind === 'all' ? `${listId}-all` : `${listId}-${activeOption.status.member.memberId}` : undefined, "aria-expanded": menuOpen, value: draft, readOnly: pending, placeholder: placeholder ?? t('messagePlaceholder'), rows: 1, onChange: onChange, onPaste: onPaste, 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) => {
183
239
  const previewUrl = draftPreviewUrl(file);
184
240
  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: () => {
185
241
  const url = draftPreviewUrls.get(file);
@@ -1,4 +1,4 @@
1
- import type { AgentTeamChannel, AgentTeamClientMemberStatus, AgentTeamModelSelection } from '@wowyuarm/dsh-agent-team/types';
1
+ import type { AgentTeamClientMemberStatus, AgentTeamModelSelection } from '@wowyuarm/dsh-agent-team/types';
2
2
  import type { TeamSidebarProps } from './slots.ts';
3
3
  /**
4
4
  * Shared provider/model dropdown for the create and edit forms. The option
@@ -15,16 +15,12 @@ export declare function ModelPickerField({ model, onModelChange, loadModels, dis
15
15
  }): import("react").JSX.Element;
16
16
  /**
17
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.
18
+ * through one durable update. Channel membership is managed from the Channel
19
+ * side, not here.
20
20
  */
21
- export declare function AgentEditorDialog({ status, channels, loadChannels, updateMember, joinChannel, removeChannelMember, loadModels, onCommitted, onClose, t }: {
21
+ export declare function AgentEditorDialog({ status, updateMember, loadModels, onCommitted, onClose, t }: {
22
22
  readonly status: AgentTeamClientMemberStatus;
23
- readonly channels: readonly AgentTeamChannel[];
24
- readonly loadChannels: TeamSidebarProps['loadChannels'];
25
23
  readonly updateMember: TeamSidebarProps['updateMember'];
26
- readonly joinChannel: TeamSidebarProps['joinChannel'];
27
- readonly removeChannelMember: TeamSidebarProps['removeChannelMember'];
28
24
  readonly loadModels: TeamSidebarProps['loadModels'];
29
25
  readonly onCommitted: () => Promise<void> | void;
30
26
  readonly onClose: () => void;
@@ -1 +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"}
1
+ {"version":3,"file":"TeamMemberEditor.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMemberEditor.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,2BAA2B,EAAE,uBAAuB,EAAgC,MAAM,gCAAgC,CAAA;AACxI,OAAO,KAAK,EAAiD,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAYjG;;;;;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,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,EAAE,EAAE;IAC/F,QAAQ,CAAC,MAAM,EAAE,2BAA2B,CAAA;IAC5C,QAAQ,CAAC,YAAY,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAA;IACvD,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,+BA0EA;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,uBAAuB,GAAG,SAAS,EAAE,KAAK,EAAE,uBAAuB,GAAG,SAAS,GAAG,OAAO,CAIxH"}
@@ -1,7 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { useEffect, useRef, useState } from 'react';
3
3
  import { Button, IconChevronDownOutline14, Input, Menu, Modal } from '@deepseek-ai/dsh-client-ui-primitives';
4
- import { useChannelMembership } from "./team-membership.js";
5
4
  import { mintRequestId } from "./requests.js";
6
5
  import createCss from './create.module.css';
7
6
  import css from './sidebar.module.css';
@@ -70,10 +69,10 @@ export function ModelPickerField({ model, onModelChange, loadModels, disabled, t
70
69
  }
71
70
  /**
72
71
  * 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.
72
+ * through one durable update. Channel membership is managed from the Channel
73
+ * side, not here.
75
74
  */
76
- export function AgentEditorDialog({ status, channels, loadChannels, updateMember, joinChannel, removeChannelMember, loadModels, onCommitted, onClose, t }) {
75
+ export function AgentEditorDialog({ status, updateMember, loadModels, onCommitted, onClose, t }) {
77
76
  const memberId = status.member.memberId;
78
77
  const [handle, setHandle] = useState(status.member.handle);
79
78
  const [description, setDescription] = useState(status.member.description);
@@ -81,35 +80,6 @@ export function AgentEditorDialog({ status, channels, loadChannels, updateMember
81
80
  const [saving, setSaving] = useState(false);
82
81
  const [error, setError] = useState();
83
82
  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
83
  const dirty = handle.trim() !== status.member.handle || description.trim() !== status.member.description
114
84
  || !sameModel(model, status.member.model);
115
85
  const submit = async (event) => {
@@ -123,6 +93,9 @@ export function AgentEditorDialog({ status, channels, loadChannels, updateMember
123
93
  handle: normalizedHandle,
124
94
  description: normalizedDescription,
125
95
  ...(model === undefined ? {} : { model }),
96
+ // The editor owns no capabilities UI, but an absent field would clear a
97
+ // Remote-written override; echo the stored intent through the edit.
98
+ ...(status.member.capabilities === undefined ? {} : { capabilities: status.member.capabilities }),
126
99
  };
127
100
  const samePending = pendingRequest.current !== undefined && pendingRequest.current.memberId === payload.memberId
128
101
  && pendingRequest.current.handle === payload.handle && pendingRequest.current.description === payload.description
@@ -152,13 +125,7 @@ export function AgentEditorDialog({ status, channels, loadChannels, updateMember
152
125
  setSaving(false);
153
126
  }
154
127
  };
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 })] }) }));
128
+ 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 }), error !== undefined && _jsx("p", { className: createCss.error, role: "alert", children: error })] }) }));
162
129
  }
163
130
  export function sameModel(left, right) {
164
131
  if (left === undefined && right === undefined)
@@ -1 +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"}
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,+BA4HvL"}