@wowyuarm/dsh-agent-team 0.1.5 → 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 (47) hide show
  1. package/README.md +15 -1
  2. package/README.zh.md +15 -1
  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/index.js +255 -2
  10. package/packages/agent-team/lib/ledger.js +139 -5
  11. package/packages/agent-team/lib/member-context.js +2 -2
  12. package/packages/agent-team/lib/member-skills.js +94 -0
  13. package/packages/agent-team/lib/spec.js +26 -0
  14. package/packages/agent-team/lib/typert.host.js +131 -28
  15. package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
  16. package/packages/agent-team/lib/typert.remote-client.js +99 -23
  17. package/packages/agent-team/lib/types/index.d.ts +69 -1
  18. package/packages/agent-team/lib/types/index.d.ts.map +1 -1
  19. package/packages/agent-team/lib/types/ledger.d.ts +29 -1
  20. package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
  21. package/packages/agent-team/lib/types/member-skills.d.ts +45 -0
  22. package/packages/agent-team/lib/types/member-skills.d.ts.map +1 -0
  23. package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
  24. package/packages/agent-team/lib/types/types.d.ts +87 -2
  25. package/packages/agent-team/lib/types/types.d.ts.map +1 -1
  26. package/packages/agent-team/preset/team-member/agent.cordis.yml +7 -4
  27. package/packages/client-agent-team/README.md +1 -1
  28. package/packages/client-agent-team/README.zh.md +1 -1
  29. package/packages/client-agent-team/lib/client.js +217 -299
  30. package/packages/client-agent-team/lib/client.js.map +1 -1
  31. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts +1 -4
  32. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
  33. package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +11 -23
  34. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts +4 -8
  35. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts.map +1 -1
  36. package/packages/client-agent-team/lib/types/client/TeamMemberEditor.js +7 -40
  37. package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
  38. package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +61 -20
  39. package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +1 -1
  40. package/packages/client-agent-team/lib/types/client/locales.d.ts +4 -16
  41. package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
  42. package/packages/client-agent-team/lib/types/client/locales.js +4 -16
  43. package/packages/client-agent-team/lib/types/client/timeline-scroll.d.ts +5 -7
  44. package/packages/client-agent-team/lib/types/client/timeline-scroll.d.ts.map +1 -1
  45. package/packages/client-agent-team/lib/types/client/timeline-scroll.js +9 -22
  46. package/packages/tool-agent-team/lib/index.js +25 -5
  47. package/packages/tool-agent-team/lib/types/index.d.ts.map +1 -1
@@ -5,13 +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
11
  readonly clearMemberContext: TeamSidebarProps['clearMemberContext'];
13
- readonly joinChannel: TeamSidebarProps['joinChannel'];
14
- readonly removeChannelMember: TeamSidebarProps['removeChannelMember'];
15
12
  readonly loadModels: TeamSidebarProps['loadModels'];
16
13
  /** The Member Session currently embedded in the conversation seat, if any. */
17
14
  readonly memberSessionId?: AgentTeamClientMemberStatus['member']['sessionId'];
@@ -19,6 +16,6 @@ interface TeamAgentsPanelProps {
19
16
  readonly onCreatingChange: (request: AgentTeamAddMemberRequest, creating: boolean) => void;
20
17
  readonly t: TeamSidebarProps['t'];
21
18
  }
22
- export declare function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, loadChannels, addMember, updateMember, recoverMember, clearMemberContext, 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;
23
20
  export {};
24
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;AAalD,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,kBAAkB,EAAE,gBAAgB,CAAC,oBAAoB,CAAC,CAAA;IACnE,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,kBAAkB,EAAE,WAAW,EAAE,mBAAmB,EAAE,UAAU,EAAE,eAAe,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,CAAC,EAAE,EAAE,oBAAoB,+BAoLpR"}
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"}
@@ -8,14 +8,11 @@ import { useSidebarSectionOpen, setSidebarSectionOpen } from "./sidebar-sections
8
8
  import { mintRequestId } from "./requests.js";
9
9
  import { TeamRowMenu } from "./TeamRowMenu.js";
10
10
  import { TeamSidebarSection } from "./TeamSidebarSection.js";
11
- import { MultiMenuField } from "./multi-menu-field.js";
12
11
  import { AgentEditorDialog, ModelPickerField, sameModel } from "./TeamMemberEditor.js";
13
12
  import createCss from './create.module.css';
14
13
  import css from './sidebar.module.css';
15
- export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, loadChannels, addMember, updateMember, recoverMember, clearMemberContext, joinChannel, removeChannelMember, loadModels, memberSessionId, openMemberSession, onCreatingChange, t }) {
14
+ export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, addMember, updateMember, recoverMember, clearMemberContext, loadModels, memberSessionId, openMemberSession, onCreatingChange, t }) {
16
15
  const [members, setMembers] = useState([]);
17
- const [channels, setChannels] = useState([]);
18
- const [channelRefs, setChannelRefs] = useState([]);
19
16
  const [loading, setLoading] = useState(true);
20
17
  const [error, setError] = useState();
21
18
  const [formOpen, setFormOpen] = useState(false);
@@ -51,22 +48,15 @@ export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, lo
51
48
  setLoading(false);
52
49
  }, [loadMembers, workspaceId]);
53
50
  useEffect(() => { void refresh(); }, [refresh]);
54
- const loadWorkspaceChannels = useCallback(async () => {
55
- const result = await loadChannels({ workspaceId, topLevelOnly: true, includeActivities: false, limit: 1 });
56
- if (result.ok)
57
- setChannels(result.value.channels);
58
- }, [loadChannels, workspaceId]);
59
- useEffect(() => { void loadWorkspaceChannels(); }, [loadWorkspaceChannels]);
60
51
  useEffect(() => subscribeChanges({ kind: 'workspace', workspaceId }, update => {
61
52
  if (update.type === 'failed') {
62
53
  setError(update.message);
63
54
  return;
64
55
  }
65
- // The section stays mounted across Channel creation, so both the Member
66
- // 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.
67
58
  void refresh();
68
- void loadWorkspaceChannels();
69
- }), [subscribeChanges, refresh, loadWorkspaceChannels, workspaceId]);
59
+ }), [subscribeChanges, refresh, workspaceId]);
70
60
  const closeForm = () => {
71
61
  if (creating)
72
62
  return;
@@ -88,7 +78,6 @@ export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, lo
88
78
  setHandle('');
89
79
  setDescription('');
90
80
  setModel(undefined);
91
- setChannelRefs([]);
92
81
  setFormOpen(false);
93
82
  if (result.value.status.presence === 'unavailable') {
94
83
  setError(result.value.status.diagnostic ?? t('statusUnavailable'));
@@ -120,28 +109,27 @@ export function TeamAgentsPanel({ workspaceId, loadMembers, subscribeChanges, lo
120
109
  const sameRequest = retryRequest !== undefined && retryRequest.workspaceId === workspaceId
121
110
  && retryRequest.handle === normalizedHandle && retryRequest.description === normalizedDescription
122
111
  && sameModel(retryRequest.model, model)
123
- && retryRequest.channelRefs.length === channelRefs.length && retryRequest.channelRefs.every(ref => channelRefs.includes(ref));
112
+ && retryRequest.channelRefs.length === 0;
124
113
  void provision(sameRequest ? retryRequest : {
125
114
  requestId: mintRequestId(),
126
115
  workspaceId,
127
116
  handle: normalizedHandle,
128
117
  description: normalizedDescription,
129
118
  presetId: 'team-member',
130
- 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: [],
131
122
  ...(model === undefined ? {} : { model }),
132
123
  });
133
124
  };
134
- 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 => {
135
- setChannelRefs(current => current.includes(ref) ? current.filter(item => item !== ref) : [...current, ref]);
136
- setRetryRequest(undefined);
137
- }, 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'), 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 }), channels: channels, loadChannels: loadChannels, updateMember: updateMember, recoverMember: recoverMember, clearMemberContext: clearMemberContext, 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') })] }))] }));
138
126
  }
139
127
  /**
140
128
  * One sidebar Agent row: the select button opens the Member's own Session
141
129
  * conversation page, the avatar carries identity plus the presence badge, and
142
130
  * the row menu opens the editor.
143
131
  */
144
- function AgentRow({ status, current, channels, loadChannels, updateMember, recoverMember, clearMemberContext, joinChannel, removeChannelMember, loadModels, openMemberSession, onUpdated, t }) {
132
+ function AgentRow({ status, current, updateMember, recoverMember, clearMemberContext, loadModels, openMemberSession, onUpdated, t }) {
145
133
  const [menuOpen, setMenuOpen] = useState(false);
146
134
  const [editing, setEditing] = useState(false);
147
135
  const [clearing, setClearing] = useState(false);
@@ -216,5 +204,5 @@ function AgentRow({ status, current, channels, loadChannels, updateMember, recov
216
204
  setClearing(true);
217
205
  else
218
206
  void recover();
219
- }, 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, 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 }))] }));
220
208
  }
@@ -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":"TeamThreadPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamThreadPage.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,mBAAmB,EAInB,gBAAgB,EAGhB,kBAAkB,EAEnB,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAEzE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AACvD,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,aAAa,CAAA;AAc/D,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,UAAU,CAAC,EAAE,mBAAmB,CAAA;IACzC,QAAQ,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAA;IACnC,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAA;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAA;IACxD,QAAQ,CAAC,iBAAiB,EAAE,qBAAqB,CAAC,mBAAmB,CAAC,CAAA;IACtE,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,KAAK,EAAE,qBAAqB,CAAC,OAAO,CAAC,CAAA;IAC9C,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAA;IACxD,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC;AA8BD,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,+BAgoBxD"}
1
+ {"version":3,"file":"TeamThreadPage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamThreadPage.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAGV,mBAAmB,EAInB,gBAAgB,EAGhB,kBAAkB,EAEnB,MAAM,gCAAgC,CAAA;AACvC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,wCAAwC,CAAA;AAEzE,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AACvD,OAAO,KAAK,EAAgB,cAAc,EAAE,MAAM,aAAa,CAAA;AAc/D,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAA;IACjC,QAAQ,CAAC,UAAU,CAAC,EAAE,mBAAmB,CAAA;IACzC,QAAQ,CAAC,OAAO,CAAC,EAAE,gBAAgB,CAAA;IACnC,QAAQ,CAAC,SAAS,EAAE,kBAAkB,CAAA;IACtC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAA;IACxD,QAAQ,CAAC,iBAAiB,EAAE,qBAAqB,CAAC,mBAAmB,CAAC,CAAA;IACtE,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,KAAK,EAAE,qBAAqB,CAAC,OAAO,CAAC,CAAA;IAC9C,QAAQ,CAAC,UAAU,EAAE,qBAAqB,CAAC,YAAY,CAAC,CAAA;IACxD,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,aAAa,EAAE,qBAAqB,CAAC,eAAe,CAAC,CAAA;IAC9D,QAAQ,CAAC,YAAY,EAAE,qBAAqB,CAAC,cAAc,CAAC,CAAA;IAC5D,QAAQ,CAAC,eAAe,EAAE,qBAAqB,CAAC,iBAAiB,CAAC,CAAA;IAClE,QAAQ,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,CAAA;CACvC;AA8BD,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,+BAuqBxD"}
@@ -43,7 +43,13 @@ export function TeamThreadPage(props) {
43
43
  const [readFacts, setReadFacts] = useState([]);
44
44
  const [historyCursor, setHistoryCursor] = useState();
45
45
  const [historyHasMore, setHistoryHasMore] = useState(false);
46
- const [remainingUnreadCount, setRemainingUnreadCount] = useState(0);
46
+ // A bounded read acknowledges at most 20 unread facts; larger backlogs need
47
+ // continuation reads. Reads also never self-wake a change scope, so a
48
+ // backlog beyond a handful of batches would otherwise linger forever. The
49
+ // cap stops a pathological feed (facts arriving faster than they are read)
50
+ // from looping without bound; the error surface keeps the remainder visible.
51
+ const MAX_AUTO_READ_ROUNDS = 50;
52
+ const [autoReadExhausted, setAutoReadExhausted] = useState(false);
47
53
  const [newFactsCount, setNewFactsCount] = useState(0);
48
54
  // The reply draft lives in the keyed draft cache: view switches unmount
49
55
  // this page, and a refresh must not cost the half-written message either.
@@ -70,13 +76,14 @@ export function TeamThreadPage(props) {
70
76
  const currentFactsRef = useRef([]);
71
77
  const sequenceRef = useRef(0);
72
78
  const readRequestIdRef = useRef(mintRequestId());
79
+ const projectionRef = useRef();
73
80
  const mutationRequests = useRef(new Map());
74
81
  const threadLastFact = currentFacts[currentFacts.length - 1];
75
82
  const timeline = useTimelineScroll(`${currentFacts.length}:${olderFacts.length}:${threadLastFact === undefined ? '' : factKey(threadLastFact)}`);
76
83
  const updateProjection = (next) => {
84
+ projectionRef.current = next;
77
85
  setProjection(next);
78
86
  setReadFacts(next.facts);
79
- setRemainingUnreadCount(next.remainingUnreadCount);
80
87
  const anchor = messageFact(next.anchor, next.anchorMentions);
81
88
  const batch = [anchor, ...next.facts.map(fact => fact.fact)];
82
89
  setCurrentFacts(current => {
@@ -84,7 +91,28 @@ export function TeamThreadPage(props) {
84
91
  currentFactsRef.current = merged;
85
92
  return merged;
86
93
  });
87
- setNewFactsCount(0);
94
+ };
95
+ // Continue a bounded read while unread facts remain. Each round must mint a
96
+ // fresh requestId: the Host replays a repeated id from its idempotency
97
+ // cache, which would return the same batch forever. The loop terminates on
98
+ // a zero remainder, a failed read (the existing error surface offers
99
+ // retry), a superseding sequence, or the round cap.
100
+ const drainUnread = async () => {
101
+ for (let round = 1; round <= MAX_AUTO_READ_ROUNDS; round += 1) {
102
+ const snapshot = projectionRef.current;
103
+ if (snapshot === undefined || snapshot.remainingUnreadCount <= 0)
104
+ return;
105
+ const beforeSequence = sequenceRef.current;
106
+ if (!await readCurrent(true))
107
+ return;
108
+ // A newer read or remount owns the tail now; it drains the remainder.
109
+ if (!mountedRef.current || sequenceRef.current !== beforeSequence + 1)
110
+ return;
111
+ // A stalled remainder (Host fault) must not spin the loop.
112
+ if (projectionRef.current?.remainingUnreadCount === snapshot.remainingUnreadCount)
113
+ return;
114
+ }
115
+ setAutoReadExhausted(true);
88
116
  };
89
117
  const readCurrent = async (newRequest = false) => {
90
118
  if (!mountedRef.current)
@@ -157,23 +185,26 @@ export function TeamThreadPage(props) {
157
185
  currentFactsRef.current = merged;
158
186
  return merged;
159
187
  });
160
- setProjection(current => current === undefined ? current : { ...current, ...(result.value.task === undefined ? {} : { task: result.value.task }), thread: result.value.thread, claims: result.value.claims });
188
+ setProjection(current => {
189
+ const next = current === undefined ? current : { ...current, ...(result.value.task === undefined ? {} : { task: result.value.task }), thread: result.value.thread, claims: result.value.claims };
190
+ if (next !== undefined)
191
+ projectionRef.current = next;
192
+ return next;
193
+ });
161
194
  if (additions.length === 0)
162
195
  return;
163
- // A reader scrolled away from the tail must be offered the explicit
164
- // new-updates action; a reader pinned to the bottom is watching these
165
- // facts arrive, so they are already seen and the batch is acknowledged
166
- // durably instead of being counted for an explicit read.
167
- if (!timeline.isPinned()) {
196
+ // Every arrival while the Thread is open is acknowledged durably: the
197
+ // timeline renders it either way, and the Human has no manual read
198
+ // action anymore. The count only feeds the pure jump hint for a reader
199
+ // away from the bottom; returning to the bottom clears it.
200
+ if (!timeline.isPinned())
168
201
  setNewFactsCount(current => current + additions.length);
169
- return;
170
- }
171
202
  const priorSequence = sequenceRef.current;
172
203
  if (await readCurrent(true))
173
204
  return;
174
205
  // The durable acknowledgment did not happen (it failed, a newer read
175
- // superseded it, or the page unmounted); fall back to the explicit
176
- // action unless that newer read now owns the tail.
206
+ // superseded it, or the page unmounted); the count keeps the jump hint
207
+ // visible unless that newer read now owns the tail.
177
208
  if (!mountedRef.current || sequenceRef.current !== priorSequence + 1)
178
209
  return;
179
210
  setNewFactsCount(current => current + additions.length);
@@ -184,6 +215,7 @@ export function TeamThreadPage(props) {
184
215
  };
185
216
  useEffect(() => {
186
217
  mountedRef.current = true;
218
+ projectionRef.current = undefined;
187
219
  setProjection(undefined);
188
220
  setChannelView(undefined);
189
221
  setMembers([]);
@@ -193,7 +225,7 @@ export function TeamThreadPage(props) {
193
225
  setReadFacts([]);
194
226
  setHistoryCursor(undefined);
195
227
  setHistoryHasMore(false);
196
- setRemainingUnreadCount(0);
228
+ setAutoReadExhausted(false);
197
229
  setNewFactsCount(0);
198
230
  setError(undefined);
199
231
  setStatusMessage(undefined);
@@ -215,9 +247,9 @@ export function TeamThreadPage(props) {
215
247
  return;
216
248
  }
217
249
  updateProjection(read.value);
218
- // Opened onto unread updates: land on the boundary instead of the floor.
219
- if (read.value.facts.some(fact => fact.unread))
220
- timeline.jumpToBoundary();
250
+ // The Thread opens at the latest fact; the unread boundary stays
251
+ // rendered as information, but reading is automatic from here on.
252
+ timeline.scrollToBottom();
221
253
  if (history !== undefined && history.ok) {
222
254
  setCurrentFacts(current => {
223
255
  const merged = mergeFacts(current, history.value.facts);
@@ -229,6 +261,7 @@ export function TeamThreadPage(props) {
229
261
  }
230
262
  setError(undefined);
231
263
  setLoading(false);
264
+ await drainUnread();
232
265
  })();
233
266
  void refreshSupplemental();
234
267
  const disposers = [
@@ -258,6 +291,13 @@ export function TeamThreadPage(props) {
258
291
  dispose();
259
292
  };
260
293
  }, [workspaceId, taskRef, threadRef]);
294
+ // Returning to the bottom is the reader's answer to the jump hint: the
295
+ // arrivals are on screen, their durable read already happened (or will be
296
+ // retried by the next change wake), and the hint has nothing left to say.
297
+ useEffect(() => {
298
+ if (newFactsCount > 0 && timeline.isPinned())
299
+ setNewFactsCount(0);
300
+ }, [newFactsCount, currentFacts, olderFacts, timeline]);
261
301
  const loadOlder = async () => {
262
302
  if (historyHasMore === false && historyCursor === undefined)
263
303
  return;
@@ -379,7 +419,7 @@ export function TeamThreadPage(props) {
379
419
  }
380
420
  flushRun();
381
421
  if (boundaryIndex === index)
382
- nodes.push(_jsx("p", { className: threadCss.unreadBoundary, role: "separator", "data-thread-boundary": true, children: _jsx("span", { children: t('unreadBoundary') }) }, `boundary-${index}`));
422
+ nodes.push(_jsx("p", { className: threadCss.unreadBoundary, role: "separator", children: _jsx("span", { children: t('unreadBoundary') }) }, `boundary-${index}`));
383
423
  if (sender !== undefined)
384
424
  run.push(fact);
385
425
  else
@@ -389,9 +429,10 @@ export function TeamThreadPage(props) {
389
429
  return nodes;
390
430
  };
391
431
  const refreshAfterFence = async () => {
392
- timeline.jumpToLatest();
432
+ timeline.scrollToBottom();
393
433
  await readCurrent(true);
394
434
  await refreshSupplemental();
435
+ await drainUnread();
395
436
  };
396
437
  const convertToTask = async () => {
397
438
  if (pending || thread === undefined || task !== undefined)
@@ -596,7 +637,7 @@ export function TeamThreadPage(props) {
596
637
  })(), task !== undefined && thread !== undefined && _jsx("section", { className: threadCss.workSection, "aria-label": t('claims'), children: _jsx(DisclosureRow, { expandOnRowClick: true, expandable: true, open: claimsOpen, onToggle: () => { setClaimsOpen(current => !current); }, icon: _jsx(IconChecklistOutline14, { size: 14 }), title: `${t('claims')} · ${taskClaims.length}`, children: _jsxs("div", { className: threadCss.claimList, children: [taskClaims.length === 0 && _jsx("p", { className: threadCss.emptyClaims, children: t('noClaims') }), taskClaims.map(claim => {
597
638
  const ownerStatus = members.find(status => status.member.memberId === claim.owner);
598
639
  return _jsxs("article", { className: threadCss.claimRow, children: [ownerStatus === undefined ? _jsx("span", {}) : _jsx(TeamPresenceDot, { status: ownerStatus, t: t }), _jsx("strong", { className: threadCss.claimOwner, children: memberName(claim.owner) }), _jsx("span", { className: threadCss.claimDirection, children: claim.direction }), _jsx("small", { className: threadCss.claimState, children: formatClaimState(claim.state, t) })] }, claim.claimRef);
599
- })] }) }) })] }), _jsx("section", { ref: timeline.ref, onScroll: timeline.onScroll, className: css.timeline, "aria-label": t('timelineLabel'), children: _jsxs("div", { className: css.timelineContent, children: [loading && projection === undefined && error === undefined && _jsx("div", { className: css.emptySurface, children: _jsxs("p", { className: css.loadingState, children: [_jsx("span", { className: css.loadingMark, "aria-hidden": "true" }), t('loadingThread')] }) }), projection === undefined && error !== undefined && _jsxs("div", { className: css.errorState, role: "alert", children: [_jsx("span", { children: error }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => { void readCurrent(); }, children: t('retry') })] }), olderFacts.length > 0 && _jsxs("section", { className: threadCss.historySection, "aria-label": t('olderHistory'), children: [_jsx("h2", { children: t('olderHistory') }), renderFactBlocks(olderFacts, undefined)] }), historyHasMore && _jsx("div", { className: css.timelineAction, children: _jsx(Button, { size: "sm", onClick: () => { void loadOlder(); }, children: t('loadOlder') }) }), currentFactsWithAnchor.length > 0 && _jsx("section", { className: threadCss.publicSection, children: renderFactBlocks(currentFactsWithAnchor, unreadBoundary) }), newFactsCount > 0 && _jsxs("div", { className: threadCss.newUpdates, role: "status", children: [_jsx("span", { children: t('readNewUpdates', { count: newFactsCount }) }), _jsx(Button, { size: "sm", variant: "outline", disabled: loading, onClick: () => { timeline.jumpToLatest(); void readCurrent(true); }, children: t('markRead') })] }), remainingUnreadCount > 0 && _jsxs("div", { className: css.timelineAction, role: "status", children: [_jsx("span", { children: t('remainingUnread', { count: remainingUnreadCount }) }), _jsx(Button, { size: "sm", onClick: () => { timeline.jumpToBoundary(); void readCurrent(true); }, disabled: loading, children: t('continueReading') })] })] }) }), projection !== undefined && thread !== undefined ? (task?.resolution === 'closed'
640
+ })] }) }) })] }), _jsx("section", { ref: timeline.ref, onScroll: timeline.onScroll, className: css.timeline, "aria-label": t('timelineLabel'), children: _jsxs("div", { className: css.timelineContent, children: [loading && projection === undefined && error === undefined && _jsx("div", { className: css.emptySurface, children: _jsxs("p", { className: css.loadingState, children: [_jsx("span", { className: css.loadingMark, "aria-hidden": "true" }), t('loadingThread')] }) }), projection === undefined && error !== undefined && _jsxs("div", { className: css.errorState, role: "alert", children: [_jsx("span", { children: error }), _jsx(Button, { size: "sm", variant: "outline", onClick: () => { void readCurrent(); }, children: t('retry') })] }), olderFacts.length > 0 && _jsxs("section", { className: threadCss.historySection, "aria-label": t('olderHistory'), children: [_jsx("h2", { children: t('olderHistory') }), renderFactBlocks(olderFacts, undefined)] }), historyHasMore && _jsx("div", { className: css.timelineAction, children: _jsx(Button, { size: "sm", onClick: () => { void loadOlder(); }, children: t('loadOlder') }) }), currentFactsWithAnchor.length > 0 && _jsx("section", { className: threadCss.publicSection, children: renderFactBlocks(currentFactsWithAnchor, unreadBoundary) }), autoReadExhausted && _jsxs("div", { className: css.timelineAction, role: "alert", children: [_jsx("span", { children: t('autoReadIncomplete') }), _jsx(Button, { size: "sm", onClick: () => { setAutoReadExhausted(false); void drainUnread(); }, disabled: loading, children: t('retry') })] }), newFactsCount > 0 && _jsx("div", { className: threadCss.newUpdates, role: "status", children: _jsx("button", { type: "button", className: threadCss.newUpdatesJump, onClick: timeline.scrollToBottom, children: t('newUpdatesJump', { count: newFactsCount }) }) })] }) }), projection !== undefined && thread !== undefined ? (task?.resolution === 'closed'
600
641
  ? _jsxs("div", { className: threadCss.closedBar, "data-team-closed": true, children: [error !== undefined && _jsx("p", { className: css.error, role: "alert", children: error }), _jsxs("div", { className: threadCss.closedNotice, children: [_jsx("span", { children: t('taskClosedNotice') }), _jsx(Button, { size: "sm", variant: "outline", disabled: pending, onClick: () => { void mutateTask('reopen'); }, children: t('reopenTask') })] })] })
601
642
  : _jsx(TeamComposer, { members: channelMembers, recipients: recipients, draft: draft, pending: pending, ...(statusMessage === undefined ? {} : { confirmation: statusMessage }), ...(error === undefined ? {} : { error }), onDraftChange: next => { drafts.writeDraft(draftKey, next); setConfirmation(undefined); setReplyRequestId(undefined); setStatusMessage(undefined); }, onRecipientsChange: next => { drafts.writeRecipients(draftKey, next); setConfirmation(undefined); setReplyRequestId(undefined); setStatusMessage(undefined); }, onSubmit: () => { void sendReply(); }, placeholder: t('replyPlaceholder'), pendingFiles: pendingFiles, onFilesChange: setPendingFiles, t: t }, draftKey)) : _jsx("div", {})] });
602
643
  }
@@ -39,5 +39,5 @@ export function TeamWorkspaceBrowser({ wide, expandSidebar, navigation, selectWo
39
39
  if (!wide) {
40
40
  return _jsxs("nav", { className: css.railWorkspace, "aria-label": t('workspaceSections'), children: [_jsx(Tooltip, { label: t('channels'), side: "right", children: _jsx("button", { type: "button", className: css.railButton, "aria-label": t('channels'), onClick: () => { setPendingSection('channels'); expandSidebar(); }, children: _jsx(IconListPenOutline16, { size: 16 }) }) }), _jsx(Tooltip, { label: t('agents'), side: "right", children: _jsx("button", { type: "button", className: css.railButton, "aria-label": t('agents'), onClick: () => { setPendingSection('agents'); expandSidebar(); }, children: _jsx(IconAgentPresetOutline16, { size: 16 }) }) })] });
41
41
  }
42
- return _jsxs("section", { className: css.workspaceBrowser, "aria-label": t('workspaces'), children: [_jsx(TeamSidebarSection, { title: t('workspaces'), open: workspacesOpen, onToggle: open => { setSidebarSectionOpen(undefined, 'workspaces', open); }, children: _jsxs("div", { className: css.workspaceList, children: [workspaces.map(workspace => _jsx(TeamWorkspaceRow, { workspaceId: workspace.workspaceId, title: workspace.title, path: workspace.path, selected: workspace.workspaceId === selectedId, current: workspace.workspaceId === selectedId && overviewIsCurrent, onSelect: selectWorkspace }, workspace.workspaceId)), workspaces.length === 0 && _jsx("p", { className: css.emptyState, children: t('empty') })] }) }), selectedId !== undefined && _jsxs("div", { className: css.workspaceSection, children: [_jsx("div", { ref: channelsRef, children: _jsx(TeamChannelsPanel, { workspaceId: selectedId, loadMembers: loadMembers, loadChannels: loadChannels, subscribeChanges: subscribeChanges, createChannel: createChannel, updateChannel: updateChannel, joinChannel: joinChannel, removeChannelMember: removeChannelMember, creatingAgents: creatingAgents.filter(request => request.workspaceId === selectedId), ...(navigationState.memberSessionId !== undefined || navigationState.channelRef === undefined ? {} : { selectedChannelRef: navigationState.channelRef }), selectChannel: selectChannel, t: t }, selectedId) }), _jsx("div", { ref: agentsRef, children: _jsx(TeamAgentsPanel, { workspaceId: selectedId, loadMembers: loadMembers, subscribeChanges: subscribeChanges, loadChannels: loadChannels, addMember: addMember, updateMember: updateMember, recoverMember: recoverMember, clearMemberContext: clearMemberContext, joinChannel: joinChannel, removeChannelMember: removeChannelMember, loadModels: loadModels, ...(navigationState.memberSessionId === undefined ? {} : { memberSessionId: navigationState.memberSessionId }), openMemberSession: openMemberSession, onCreatingChange: (request, creating) => { setCreatingAgents(current => creating ? [...current.filter(item => item.requestId !== request.requestId), request] : current.filter(item => item.requestId !== request.requestId)); }, t: t }, selectedId) })] })] });
42
+ return _jsxs("section", { className: css.workspaceBrowser, "aria-label": t('workspaces'), children: [_jsx(TeamSidebarSection, { title: t('workspaces'), open: workspacesOpen, onToggle: open => { setSidebarSectionOpen(undefined, 'workspaces', open); }, children: _jsxs("div", { className: css.workspaceList, children: [workspaces.map(workspace => _jsx(TeamWorkspaceRow, { workspaceId: workspace.workspaceId, title: workspace.title, path: workspace.path, selected: workspace.workspaceId === selectedId, current: workspace.workspaceId === selectedId && overviewIsCurrent, onSelect: selectWorkspace }, workspace.workspaceId)), workspaces.length === 0 && _jsx("p", { className: css.emptyState, children: t('empty') })] }) }), selectedId !== undefined && _jsxs("div", { className: css.workspaceSection, children: [_jsx("div", { ref: channelsRef, children: _jsx(TeamChannelsPanel, { workspaceId: selectedId, loadMembers: loadMembers, loadChannels: loadChannels, subscribeChanges: subscribeChanges, createChannel: createChannel, updateChannel: updateChannel, joinChannel: joinChannel, removeChannelMember: removeChannelMember, creatingAgents: creatingAgents.filter(request => request.workspaceId === selectedId), ...(navigationState.memberSessionId !== undefined || navigationState.channelRef === undefined ? {} : { selectedChannelRef: navigationState.channelRef }), selectChannel: selectChannel, t: t }, selectedId) }), _jsx("div", { ref: agentsRef, children: _jsx(TeamAgentsPanel, { workspaceId: selectedId, loadMembers: loadMembers, subscribeChanges: subscribeChanges, addMember: addMember, updateMember: updateMember, recoverMember: recoverMember, clearMemberContext: clearMemberContext, loadModels: loadModels, ...(navigationState.memberSessionId === undefined ? {} : { memberSessionId: navigationState.memberSessionId }), openMemberSession: openMemberSession, onCreatingChange: (request, creating) => { setCreatingAgents(current => creating ? [...current.filter(item => item.requestId !== request.requestId), request] : current.filter(item => item.requestId !== request.requestId)); }, t: t }, selectedId) })] })] });
43
43
  }
@@ -15,8 +15,6 @@ export declare const zh: {
15
15
  readonly retry: "重试";
16
16
  readonly agentName: "名称";
17
17
  readonly agentDescription: "说明";
18
- readonly initialChannels: "初始频道";
19
- readonly noChannelsForAgent: "请先创建至少一个频道";
20
18
  readonly createAgent: "创建 Agent";
21
19
  readonly creatingAgent: "正在创建…";
22
20
  readonly loadingAgents: "正在加载 Agents…";
@@ -49,8 +47,6 @@ export declare const zh: {
49
47
  readonly clearContextFailed: "清空上下文失败:{message}";
50
48
  readonly optionalSuffix: "(可选)";
51
49
  readonly agentDescriptionPlaceholder: "留空则暂无描述";
52
- readonly channelsPickerEmpty: "选择初始频道";
53
- readonly channelsPickerCount: "已选 {count} 个频道";
54
50
  readonly membersPickerEmpty: "选择初始成员";
55
51
  readonly membersPickerCount: "已选 {count} 个成员";
56
52
  readonly reasoningEffort: "推理强度";
@@ -112,10 +108,8 @@ export declare const zh: {
112
108
  readonly backToWorkspace: "返回工作区";
113
109
  readonly loadingThread: "正在加载 Thread…";
114
110
  readonly unreadBoundary: "以下是本次打开收到的更新";
115
- readonly readNewUpdates: "读取 {count} 条新更新";
116
- readonly markRead: "标记为已读";
117
- readonly remainingUnread: "还有 {count} 条未读更新";
118
- readonly continueReading: "继续阅读";
111
+ readonly newUpdatesJump: " {count} 条新更新";
112
+ readonly autoReadIncomplete: "仍有未读更新未能自动读取";
119
113
  readonly olderHistory: "更早历史";
120
114
  readonly runtimeRisk: "当前运行风险";
121
115
  readonly runtimeRiskDetail: "@{member} 当前不可用:{diagnostic}";
@@ -163,8 +157,6 @@ export declare const en: {
163
157
  readonly retry: "Retry";
164
158
  readonly agentName: "Name";
165
159
  readonly agentDescription: "Description";
166
- readonly initialChannels: "Initial Channels";
167
- readonly noChannelsForAgent: "Create at least one Channel first";
168
160
  readonly createAgent: "Create Agent";
169
161
  readonly creatingAgent: "Creating…";
170
162
  readonly loadingAgents: "Loading Agents…";
@@ -197,8 +189,6 @@ export declare const en: {
197
189
  readonly clearContextFailed: "Failed to clear context: {message}";
198
190
  readonly optionalSuffix: " (optional)";
199
191
  readonly agentDescriptionPlaceholder: "Leave empty if not needed";
200
- readonly channelsPickerEmpty: "Choose initial channels";
201
- readonly channelsPickerCount: "{count} selected";
202
192
  readonly membersPickerEmpty: "Choose initial members";
203
193
  readonly membersPickerCount: "{count} selected";
204
194
  readonly reasoningEffort: "Reasoning effort";
@@ -260,10 +250,8 @@ export declare const en: {
260
250
  readonly backToWorkspace: "Back to Workspace";
261
251
  readonly loadingThread: "Loading Thread…";
262
252
  readonly unreadBoundary: "Updates received when you opened this Thread";
263
- readonly readNewUpdates: "Read {count} new update(s)";
264
- readonly markRead: "Mark read";
265
- readonly remainingUnread: "{count} unread update(s) remain";
266
- readonly continueReading: "Continue reading";
253
+ readonly newUpdatesJump: " {count} new update(s)";
254
+ readonly autoReadIncomplete: "Some unread updates could not be read automatically";
267
255
  readonly olderHistory: "Older history";
268
256
  readonly runtimeRisk: "Current runtime risk";
269
257
  readonly runtimeRiskDetail: "@{member} is unavailable: {diagnostic}";
@@ -1 +1 @@
1
- {"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmJL,CAAA;AAEV,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmJL,CAAA;AAEV,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,EAAE,CAAA"}
1
+ {"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6IL,CAAA;AAEV,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6IL,CAAA;AAEV,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,EAAE,CAAA"}
@@ -15,8 +15,6 @@ export const zh = {
15
15
  retry: '重试',
16
16
  agentName: '名称',
17
17
  agentDescription: '说明',
18
- initialChannels: '初始频道',
19
- noChannelsForAgent: '请先创建至少一个频道',
20
18
  createAgent: '创建 Agent',
21
19
  creatingAgent: '正在创建…',
22
20
  loadingAgents: '正在加载 Agents…',
@@ -49,8 +47,6 @@ export const zh = {
49
47
  clearContextFailed: '清空上下文失败:{message}',
50
48
  optionalSuffix: '(可选)',
51
49
  agentDescriptionPlaceholder: '留空则暂无描述',
52
- channelsPickerEmpty: '选择初始频道',
53
- channelsPickerCount: '已选 {count} 个频道',
54
50
  membersPickerEmpty: '选择初始成员',
55
51
  membersPickerCount: '已选 {count} 个成员',
56
52
  reasoningEffort: '推理强度',
@@ -112,10 +108,8 @@ export const zh = {
112
108
  backToWorkspace: '返回工作区',
113
109
  loadingThread: '正在加载 Thread…',
114
110
  unreadBoundary: '以下是本次打开收到的更新',
115
- readNewUpdates: '读取 {count} 条新更新',
116
- markRead: '标记为已读',
117
- remainingUnread: '还有 {count} 条未读更新',
118
- continueReading: '继续阅读',
111
+ newUpdatesJump: ' {count} 条新更新',
112
+ autoReadIncomplete: '仍有未读更新未能自动读取',
119
113
  olderHistory: '更早历史',
120
114
  runtimeRisk: '当前运行风险',
121
115
  runtimeRiskDetail: '@{member} 当前不可用:{diagnostic}',
@@ -163,8 +157,6 @@ export const en = {
163
157
  retry: 'Retry',
164
158
  agentName: 'Name',
165
159
  agentDescription: 'Description',
166
- initialChannels: 'Initial Channels',
167
- noChannelsForAgent: 'Create at least one Channel first',
168
160
  createAgent: 'Create Agent',
169
161
  creatingAgent: 'Creating…',
170
162
  loadingAgents: 'Loading Agents…',
@@ -197,8 +189,6 @@ export const en = {
197
189
  clearContextFailed: 'Failed to clear context: {message}',
198
190
  optionalSuffix: ' (optional)',
199
191
  agentDescriptionPlaceholder: 'Leave empty if not needed',
200
- channelsPickerEmpty: 'Choose initial channels',
201
- channelsPickerCount: '{count} selected',
202
192
  membersPickerEmpty: 'Choose initial members',
203
193
  membersPickerCount: '{count} selected',
204
194
  reasoningEffort: 'Reasoning effort',
@@ -260,10 +250,8 @@ export const en = {
260
250
  backToWorkspace: 'Back to Workspace',
261
251
  loadingThread: 'Loading Thread…',
262
252
  unreadBoundary: 'Updates received when you opened this Thread',
263
- readNewUpdates: 'Read {count} new update(s)',
264
- markRead: 'Mark read',
265
- remainingUnread: '{count} unread update(s) remain',
266
- continueReading: 'Continue reading',
253
+ newUpdatesJump: ' {count} new update(s)',
254
+ autoReadIncomplete: 'Some unread updates could not be read automatically',
267
255
  olderHistory: 'Older history',
268
256
  runtimeRisk: 'Current runtime risk',
269
257
  runtimeRiskDetail: '@{member} is unavailable: {diagnostic}',