@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
@@ -45,8 +45,13 @@ export function TeamMemberSessionComposer({ controller, inputActions, overlay, p
45
45
  setSubmissionNotice('Compact 未执行,请重试。');
46
46
  return;
47
47
  }
48
- if (input.phase === 'plain')
48
+ if (input.phase === 'plain') {
49
+ // Reaching plain after a compact flight means the InputMachine adopted
50
+ // the clear-on-success draft; a stale retry notice must not outlive it.
51
+ if (compactWasSubmitting.current)
52
+ setSubmissionNotice(undefined);
49
53
  compactWasSubmitting.current = false;
54
+ }
50
55
  }, [input?.claim?.token, input?.phase]);
51
56
  useEffect(() => {
52
57
  if (input === undefined)
@@ -86,6 +91,16 @@ export function TeamMemberSessionComposer({ controller, inputActions, overlay, p
86
91
  return;
87
92
  if (event.repeat || composing.current || event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229)
88
93
  return;
94
+ // Tab accepts the highlighted candidate, mirroring the team-mode composer
95
+ // and the Channel/Thread bars; Shift+Tab keeps native focus reversal.
96
+ if (event.key === 'Tab' && !event.shiftKey) {
97
+ const highlight = controller.menu.getSnapshot().highlight;
98
+ if (highlight !== null) {
99
+ event.preventDefault();
100
+ controller.pick(highlight.source, highlight.index);
101
+ }
102
+ return;
103
+ }
89
104
  const key = event.key === 'ArrowUp' ? 'up' : event.key === 'ArrowDown' ? 'down' : event.key === 'Escape' ? 'escape' : event.key === 'Enter' ? 'enter' : undefined;
90
105
  if (key !== undefined) {
91
106
  const outcome = controller.arbitrate(key, false);
@@ -1 +1 @@
1
- {"version":3,"file":"TeamMessage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA2F,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAE/H,OAAO,KAAK,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AACrH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAEvD,OAAO,EAA4E,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAI/H,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAA;IACpC,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,uFAAuF;IACvF,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACzC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;IAC1B;2DACuD;IACvD,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAA;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,0BAA0B,EAAE,GAAG,SAAS,CAAA;IACxE,6EAA6E;IAC7E,QAAQ,CAAC,cAAc,CAAC,EAAE,qBAAqB,CAAC,eAAe,CAAC,GAAG,SAAS,CAAA;IAC5E,QAAQ,CAAC,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,GAAG,SAAS,CAAA;IACnD,0FAA0F;IAC1F,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IACxD,sFAAsF;IACtF,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,KAAK,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC,CAAC,GAAG,SAAS,CAAA;IACzH,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAC9B;AAED,kFAAkF;AAClF,wBAAgB,WAAW,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC,EAAE,SAAS,EAAE,iBAAiB,EAAE,QAAQ,EAAE,EAAE,gBAAgB,+BA6G3N"}
1
+ {"version":3,"file":"TeamMessage.d.ts","sourceRoot":"","sources":["../../../src/client/TeamMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA2F,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAE/H,OAAO,KAAK,EAAE,iBAAiB,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,MAAM,gCAAgC,CAAA;AACrH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAA;AAEvD,OAAO,EAA4E,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAA;AAI/H,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;IAC3B,QAAQ,CAAC,QAAQ,EAAE,iBAAiB,CAAA;IACpC,wEAAwE;IACxE,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B,uFAAuF;IACvF,QAAQ,CAAC,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACzC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,8EAA8E;IAC9E,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;IAC1B;2DACuD;IACvD,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAA;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,0BAA0B,EAAE,GAAG,SAAS,CAAA;IACxE,6EAA6E;IAC7E,QAAQ,CAAC,cAAc,CAAC,EAAE,qBAAqB,CAAC,eAAe,CAAC,GAAG,SAAS,CAAA;IAC5E,QAAQ,CAAC,CAAC,CAAC,EAAE,qBAAqB,CAAC,GAAG,CAAC,GAAG,SAAS,CAAA;IACnD,0FAA0F;IAC1F,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAA;IACxD,sFAAsF;IACtF,QAAQ,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE,SAAS,gBAAgB,EAAE,KAAK,OAAO,CAAC,SAAS,eAAe,EAAE,CAAC,CAAC,GAAG,SAAS,CAAA;IACzH,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,CAAA;CAC9B;AAED,kFAAkF;AAClF,wBAAgB,WAAW,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,YAAY,EAAE,WAAW,EAAE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC,EAAE,SAAS,EAAE,iBAAiB,EAAE,QAAQ,EAAE,EAAE,gBAAgB,+BA8G3N"}
@@ -29,6 +29,7 @@ export function TeamMessage({ senderName, memberId, human, body, occurredAt, men
29
29
  void resolveUnknownTaskRefs(bodyTaskRefs, onResolveTaskRefs);
30
30
  }, [onResolveTaskRefs, bodyTaskRefKey, refVersion]);
31
31
  const taskLabel = useCallback((taskNumber) => t?.('taskLabel', { number: taskNumber }) ?? `Task #${taskNumber}`, [t]);
32
+ const refLabel = useCallback((ref) => ref.startsWith('thread:') ? (t?.('threadLabel') ?? 'Thread') : ref.startsWith('channel:') ? (t?.('channels') ?? 'Channels') : ref, [t]);
32
33
  // Long bodies start clamped behind the expand control. The default derives
33
34
  // from the body alone; the toggle itself is per-mount view state nothing
34
35
  // persists.
@@ -49,16 +50,16 @@ export function TeamMessage({ senderName, memberId, human, body, occurredAt, men
49
50
  if (onResolveTaskRefs !== undefined && taskRefs.length > 0)
50
51
  void resolveUnknownTaskRefs(taskRefs, onResolveTaskRefs);
51
52
  for (const code of styledRefCodes)
52
- renderResolvedMarkdownCodeRef(code, taskLabel);
53
+ renderResolvedMarkdownCodeRef(code, taskLabel, refLabel);
53
54
  for (const node of textNodes)
54
- renderResolvedMarkdownText(node, mentionNames ?? [], taskLabel);
55
+ renderResolvedMarkdownText(node, mentionNames ?? [], taskLabel, refLabel);
55
56
  for (const button of root.querySelectorAll('button[data-task-ref]')) {
56
57
  const taskRef = button.dataset.taskRef;
57
58
  const hit = taskRef === undefined ? undefined : cachedResolvedTaskRef(taskRef);
58
59
  if (taskRef !== undefined && hit !== undefined)
59
60
  button.textContent = taskLabel(hit.taskNumber);
60
61
  }
61
- }, [richAgentBody, onOpenRef, onResolveTaskRefs, refVersion, taskLabel, mentionNames]);
62
+ }, [richAgentBody, onOpenRef, onResolveTaskRefs, refVersion, taskLabel, refLabel, mentionNames]);
62
63
  useEffect(() => {
63
64
  const root = markdownRef.current;
64
65
  if (!richAgentBody || root === null || onOpenRef === undefined)
@@ -67,12 +68,12 @@ export function TeamMessage({ senderName, memberId, human, body, occurredAt, men
67
68
  const target = event.target;
68
69
  if (!(target instanceof Element))
69
70
  return;
70
- const button = target.closest('button[data-task-ref]');
71
+ const button = target.closest('button[data-ref], button[data-task-ref]');
71
72
  if (button === null || !root.contains(button))
72
73
  return;
73
- const taskRef = button instanceof HTMLElement ? button.dataset.taskRef : undefined;
74
- if (taskRef !== undefined)
75
- onOpenRef(taskRef);
74
+ const ref = button instanceof HTMLElement ? button.dataset.ref ?? button.dataset.taskRef : undefined;
75
+ if (ref !== undefined)
76
+ onOpenRef(ref);
76
77
  };
77
78
  root.addEventListener('click', openTask);
78
79
  return () => { root.removeEventListener('click', openTask); };
@@ -80,9 +81,9 @@ export function TeamMessage({ senderName, memberId, human, body, occurredAt, men
80
81
  const bodyNode = plan.render === 'inline' && plan.inline !== undefined
81
82
  ? _jsx("div", { className: css.messageText, children: plan.inline.segments.map((segment, index) => segment.mention
82
83
  ? _jsx("span", { className: css.mention, children: segment.text }, index)
83
- : _jsx(Fragment, { children: renderRefs(segment.text, onOpenRef, taskLabel) }, index)) })
84
+ : _jsx(Fragment, { children: renderRefs(segment.text, onOpenRef, taskLabel, refLabel) }, index)) })
84
85
  : plan.render === 'literal'
85
- ? _jsx("div", { className: css.messageText, children: onOpenRef === undefined ? _jsx(MessageText, { text: displayBody }) : renderRefs(displayBody, onOpenRef, taskLabel) })
86
+ ? _jsx("div", { className: css.messageText, children: onOpenRef === undefined ? _jsx(MessageText, { text: displayBody }) : renderRefs(displayBody, onOpenRef, taskLabel, refLabel) })
86
87
  : _jsx("div", { ref: markdownRef, className: css.messageMarkdown, children: _jsx(MarkdownText, { text: displayBody }, `${displayBody}:${onOpenRef === undefined ? 'literal' : 'refs'}`) });
87
88
  return (_jsxs("article", { className: css.messageRow, "data-human": human || undefined, "data-grouped": grouped || undefined, children: [_jsx("div", { className: css.messageIdentity, style: avatarStyle, "aria-hidden": "true", children: senderName.replace('@', '').slice(0, 1).toUpperCase() }), _jsxs("div", { className: css.messageBody, children: [(!grouped || showGroupedTime === true) && (_jsxs("div", { className: css.nameRow, children: [!grouped && _jsx("strong", { ...(senderTitle === undefined ? {} : { title: senderTitle }), children: senderName }), occurredAt !== undefined && _jsx("span", { className: css.messageTime, children: formatMessageTime(occurredAt) })] })), clampable ? _jsx("div", { className: clamped ? css.messageClamp : undefined, children: bodyNode }) : bodyNode, clampable && (_jsx("button", { type: "button", className: css.messageExpand, "data-message-expand": "true", "aria-expanded": expanded, onClick: () => { setExpanded(value => !value); }, children: expanded ? (t?.('collapseMessage') ?? 'Show less') : (t?.('expandMessage') ?? 'Show more') })), (plan.fallbackNames.length > 0 || plan.fallbackRefs.length > 0) && (_jsxs("div", { className: css.mentionsRow, children: [plan.fallbackNames.map(name => _jsxs("span", { className: css.mention, children: ["@", name] }, name)), plan.fallbackRefs.map(ref => {
88
89
  const resolved = cachedResolvedTaskRef(ref);
@@ -132,17 +133,31 @@ function resolvedTaskRefButton(taskRef, label) {
132
133
  return button;
133
134
  }
134
135
  /** Replace a whole-ref code span with its resolved Task chip once known. */
135
- function renderResolvedMarkdownCodeRef(code, taskLabel) {
136
+ function renderResolvedMarkdownCodeRef(code, taskLabel, refLabel) {
136
137
  const segments = splitBrandedRefs((code.textContent ?? '').trim());
137
138
  const segment = segments.length === 1 ? segments[0] : undefined;
138
- const taskRef = segment?.ref?.startsWith('task:') === true ? segment.ref : undefined;
139
+ const ref = segment?.ref;
140
+ if (ref === undefined)
141
+ return;
142
+ const taskRef = ref.startsWith('task:') === true ? ref : undefined;
139
143
  const resolved = taskRef === undefined ? undefined : cachedResolvedTaskRef(taskRef);
140
- if (taskRef === undefined || resolved === undefined)
144
+ if (taskRef !== undefined && resolved === undefined)
145
+ return;
146
+ if (taskRef !== undefined) {
147
+ code.replaceWith(resolvedTaskRefButton(taskRef, taskLabel(resolved.taskNumber)));
141
148
  return;
142
- code.replaceWith(resolvedTaskRefButton(taskRef, taskLabel(resolved.taskNumber)));
149
+ }
150
+ const button = document.createElement('button');
151
+ button.type = 'button';
152
+ if (css.refLink !== undefined)
153
+ button.className = css.refLink;
154
+ button.dataset.ref = ref;
155
+ button.title = ref;
156
+ button.textContent = refLabel(ref);
157
+ code.replaceWith(button);
143
158
  }
144
159
  /** Replace resolved Task refs and structured mention handles in one prose text node without changing Markdown structure. */
145
- function renderResolvedMarkdownText(node, mentionNames, taskLabel) {
160
+ function renderResolvedMarkdownText(node, mentionNames, taskLabel, refLabel) {
146
161
  let changed = false;
147
162
  const fragment = document.createDocumentFragment();
148
163
  for (const refSegment of splitBrandedRefs(node.data)) {
@@ -163,25 +178,40 @@ function renderResolvedMarkdownText(node, mentionNames, taskLabel) {
163
178
  }
164
179
  const taskRef = refSegment.ref.startsWith('task:') === true ? refSegment.ref : undefined;
165
180
  const resolved = taskRef === undefined ? undefined : cachedResolvedTaskRef(taskRef);
166
- if (resolved === undefined) {
181
+ // Task refs need Host resolution before becoming human-readable links;
182
+ // channel/thread refs already have their canonical display text and can be
183
+ // linked immediately at the authored position.
184
+ if (taskRef !== undefined && resolved === undefined) {
167
185
  fragment.append(refSegment.text);
168
186
  continue;
169
187
  }
170
188
  changed = true;
171
- fragment.append(resolvedTaskRefButton(taskRef, taskLabel(resolved.taskNumber)));
189
+ if (taskRef !== undefined) {
190
+ fragment.append(resolvedTaskRefButton(taskRef, taskLabel(resolved.taskNumber)));
191
+ }
192
+ else {
193
+ const button = document.createElement('button');
194
+ button.type = 'button';
195
+ if (css.refLink !== undefined)
196
+ button.className = css.refLink;
197
+ button.dataset.ref = refSegment.ref;
198
+ button.title = refSegment.ref;
199
+ button.textContent = refLabel(refSegment.ref);
200
+ fragment.append(button);
201
+ }
172
202
  }
173
203
  if (changed)
174
204
  node.replaceWith(fragment);
175
205
  }
176
206
  /** Render one literal text run, linkifying branded refs when navigation is available. */
177
- function renderRefs(text, onOpenRef, taskLabel) {
207
+ function renderRefs(text, onOpenRef, taskLabel, refLabel) {
178
208
  if (onOpenRef === undefined)
179
209
  return text;
180
210
  return splitBrandedRefs(text).map((segment, index) => {
181
211
  if (segment.ref === undefined)
182
212
  return _jsx(Fragment, { children: segment.text }, index);
183
213
  const resolved = cachedResolvedTaskRef(segment.ref);
184
- const label = resolved !== undefined && segment.ref.startsWith('task:') ? taskLabel(resolved.taskNumber) : segment.ref;
214
+ const label = resolved !== undefined && segment.ref.startsWith('task:') ? taskLabel(resolved.taskNumber) : refLabel(segment.ref);
185
215
  return _jsx("button", { type: "button", className: css.refLink, title: segment.ref, onClick: () => { onOpenRef(segment.ref); }, children: label }, index);
186
216
  });
187
217
  }
@@ -1,4 +1,4 @@
1
- import type { MenuItem } from '@deepseek-ai/dsh-client-ui-primitives';
1
+ import { type MenuEntry } from '@deepseek-ai/dsh-client-ui-primitives';
2
2
  /**
3
3
  * Row-level overflow menu shared by sidebar Channel and Agent rows, mirroring
4
4
  * the harness session-row pattern: a portal list anchored to a bare ellipsis
@@ -7,7 +7,8 @@ import type { MenuItem } from '@deepseek-ai/dsh-client-ui-primitives';
7
7
  export declare function TeamRowMenu({ label, items, onSelect, onOpenChange }: {
8
8
  /** Localized action label for the trigger, e.g. "{name} 的操作". */
9
9
  readonly label: string;
10
- readonly items: readonly MenuItem[];
10
+ /** Menu rows plus optional non-interactive labels and separators. */
11
+ readonly items: readonly MenuEntry[];
11
12
  readonly onSelect: (id: string) => void;
12
13
  /** Lets the row pin its hover styling while the portal list is up. */
13
14
  readonly onOpenChange?: (open: boolean) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"TeamRowMenu.d.ts","sourceRoot":"","sources":["../../../src/client/TeamRowMenu.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uCAAuC,CAAA;AAGrE;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE;IACpE,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAA;IACnC,QAAQ,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC,sEAAsE;IACtE,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;CAChD,+BAkCA"}
1
+ {"version":3,"file":"TeamRowMenu.d.ts","sourceRoot":"","sources":["../../../src/client/TeamRowMenu.tsx"],"names":[],"mappings":"AACA,OAAO,EAA+B,KAAK,SAAS,EAAE,MAAM,uCAAuC,CAAA;AAGnG;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,YAAY,EAAE,EAAE;IACpE,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,qEAAqE;IACrE,QAAQ,CAAC,KAAK,EAAE,SAAS,SAAS,EAAE,CAAA;IACpC,QAAQ,CAAC,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAA;IACvC,sEAAsE;IACtE,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;CAChD,+BAkCA"}
@@ -1,9 +1,12 @@
1
- import { type ReactNode } from 'react';
1
+ import type { ReactNode } from 'react';
2
2
  /** Collapsible sidebar section header: disclosure toggle plus trailing actions. */
3
- export declare function TeamSidebarSection({ title, actions, children }: {
3
+ export declare function TeamSidebarSection({ title, actions, open, onToggle, children }: {
4
4
  readonly title: string;
5
5
  /** Trailing header controls (the add button); never part of the toggle. */
6
6
  readonly actions?: ReactNode;
7
+ /** Controlled disclosure state; the caller owns persistence. */
8
+ readonly open: boolean;
9
+ readonly onToggle: (open: boolean) => void;
7
10
  readonly children: ReactNode;
8
11
  }): import("react").JSX.Element;
9
12
  //# sourceMappingURL=TeamSidebarSection.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TeamSidebarSection.d.ts","sourceRoot":"","sources":["../../../src/client/TeamSidebarSection.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAIhD,mFAAmF;AACnF,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE;IAC/D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,CAAA;IAC5B,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAA;CAC7B,+BAcA"}
1
+ {"version":3,"file":"TeamSidebarSection.d.ts","sourceRoot":"","sources":["../../../src/client/TeamSidebarSection.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC,mFAAmF;AACnF,wBAAgB,kBAAkB,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE;IAC/E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,CAAA;IAC5B,gEAAgE;IAChE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;IACtB,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IAC1C,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAA;CAC7B,+BAaA"}
@@ -1,9 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
3
2
  import { IconChevronDownOutline14 } from '@deepseek-ai/dsh-client-ui-primitives';
4
3
  import css from './sidebar.module.css';
5
4
  /** Collapsible sidebar section header: disclosure toggle plus trailing actions. */
6
- export function TeamSidebarSection({ title, actions, children }) {
7
- const [open, setOpen] = useState(true);
8
- return (_jsxs("section", { className: css.section, children: [_jsxs("div", { className: css.sectionHeader, children: [_jsxs("button", { type: "button", className: css.sectionToggle, "aria-expanded": open, onClick: () => { setOpen(value => !value); }, children: [_jsx(IconChevronDownOutline14, { className: css.sectionChevron }), _jsx("span", { className: css.sectionTitle, children: title })] }), actions !== undefined && _jsx("span", { className: css.sectionActions, children: actions })] }), open && children] }));
5
+ export function TeamSidebarSection({ title, actions, open, onToggle, children }) {
6
+ return (_jsxs("section", { className: css.section, children: [_jsxs("div", { className: css.sectionHeader, children: [_jsxs("button", { type: "button", className: css.sectionToggle, "aria-expanded": open, onClick: () => { onToggle(!open); }, children: [_jsx(IconChevronDownOutline14, { className: css.sectionChevron }), _jsx("span", { className: css.sectionTitle, children: title })] }), actions !== undefined && _jsx("span", { className: css.sectionActions, children: actions })] }), open && children] }));
9
7
  }
@@ -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,+BA+mBxD"}
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;
@@ -314,8 +354,25 @@ export function TeamThreadPage(props) {
314
354
  // Channel; the open Task's own refs are already on screen, and other Tasks
315
355
  // are not resolvable from this surface, so they degrade to a no-op.
316
356
  const openRef = (ref) => {
317
- if (ref.startsWith('channel:') && ref !== channelRef)
357
+ if (ref.startsWith('channel:') && ref !== channelRef) {
318
358
  selectChannel(ref);
359
+ return;
360
+ }
361
+ if (ref.startsWith('thread:') && ref !== threadRef) {
362
+ // Thread refs do not have a dedicated resolver Remote. The Host view
363
+ // query still returns the target's home Channel and Task projection.
364
+ void loadChannels({ workspaceId, threadRef: ref, includeActivities: false, limit: 1 }).then(result => {
365
+ if (!result.ok)
366
+ return;
367
+ const target = result.value.items[0];
368
+ if (target === undefined)
369
+ return;
370
+ const targetChannel = result.value.channels.find(channel => channel.channelRef === target.message.channelRef);
371
+ if (targetChannel !== undefined)
372
+ selectThread(target.thread.threadRef, targetChannel.channelRef, target.task?.taskRef, target.taskNumber);
373
+ });
374
+ return;
375
+ }
319
376
  if (ref.startsWith('task:') && ref !== taskRef) {
320
377
  // Another Task cited here: resolve its home Channel and jump.
321
378
  jumpToTaskThread(resolveTaskRefs, workspaceId, ref, selectThread);
@@ -362,7 +419,7 @@ export function TeamThreadPage(props) {
362
419
  }
363
420
  flushRun();
364
421
  if (boundaryIndex === index)
365
- 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}`));
366
423
  if (sender !== undefined)
367
424
  run.push(fact);
368
425
  else
@@ -372,9 +429,10 @@ export function TeamThreadPage(props) {
372
429
  return nodes;
373
430
  };
374
431
  const refreshAfterFence = async () => {
375
- timeline.jumpToLatest();
432
+ timeline.scrollToBottom();
376
433
  await readCurrent(true);
377
434
  await refreshSupplemental();
435
+ await drainUnread();
378
436
  };
379
437
  const convertToTask = async () => {
380
438
  if (pending || thread === undefined || task !== undefined)
@@ -559,7 +617,9 @@ export function TeamThreadPage(props) {
559
617
  return _jsxs("main", { className: css.surface, "data-team-thread": threadRef, children: [_jsxs("div", { className: css.surfaceHeader, children: [_jsx("div", { className: css.backRow, children: _jsx(Button, { size: "sm", icon: _jsx(IconChevronLeftOutline14, {}), onClick: backToWorkspace, children: backLabel }) }), _jsxs("header", { className: css.headerRow, children: [_jsxs("div", { className: css.headerCopy, children: [_jsxs("div", { className: threadCss.titleLine, children: [_jsx("h1", { children: task === undefined ? t('threadLabel') : t('taskLabel', { number: resolvedTaskNumber ?? '…' }) }), task !== undefined && _jsx(Pill, { children: formatTaskStatus(task.status, t) })] }), taskTitle !== undefined && taskTitle !== '' && _jsx("p", { className: threadCss.taskTitle, title: taskTitle, children: taskTitle })] }), task === undefined && thread !== undefined && _jsx("div", { className: css.headerActions, children: _jsx(Button, { size: "sm", variant: "primary", disabled: pending, onClick: () => { void convertToTask(); }, children: mutating === 'promote' ? t('promotingTask') : t('promoteToTask') }) }), task !== undefined && thread !== undefined && task.resolution !== 'closed' && _jsxs("div", { className: css.headerActions, children: [(() => {
560
618
  const activeClaims = projection?.claims.filter(claim => claim.taskRef === task.taskRef && claim.state === 'active') ?? [];
561
619
  const earlyAccept = task.resolution === 'open' && task.status === 'in_progress' && activeClaims.length > 0;
562
- if (!(task.status === 'in_review' || earlyAccept) || task.resolution !== 'open')
620
+ // todo Tasks accept directly: the work finished outside the
621
+ // ledger, so there is nothing to confirm and no Claims to list.
622
+ if (!(task.status === 'in_review' || task.status === 'todo' || earlyAccept) || task.resolution !== 'open')
563
623
  return null;
564
624
  return _jsx(Button, { size: "sm", variant: "primary", disabled: pending, onClick: () => { if (earlyAccept) {
565
625
  setConfirmingAccept(true);
@@ -577,7 +637,7 @@ export function TeamThreadPage(props) {
577
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 => {
578
638
  const ownerStatus = members.find(status => status.member.memberId === claim.owner);
579
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);
580
- })] }) }) })] }), _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'
581
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') })] })] })
582
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", {})] });
583
643
  }
@@ -1,3 +1,3 @@
1
1
  import type { TeamSidebarProps } from './slots.ts';
2
- export declare function TeamWorkspaceBrowser({ wide, expandSidebar, navigation, selectWorkspace, selectChannel, t, useWorkspaces, loadMembers, subscribeChanges, addMember, loadChannels, createChannel, updateChannel, updateMember, recoverMember, joinChannel, removeChannelMember, loadModels, openMemberSession }: TeamSidebarProps): import("react").JSX.Element;
2
+ export declare function TeamWorkspaceBrowser({ wide, expandSidebar, navigation, selectWorkspace, selectChannel, t, useWorkspaces, loadMembers, subscribeChanges, addMember, loadChannels, createChannel, updateChannel, updateMember, recoverMember, clearMemberContext, joinChannel, removeChannelMember, loadModels, openMemberSession }: TeamSidebarProps): import("react").JSX.Element;
3
3
  //# sourceMappingURL=TeamWorkspaceBrowser.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"TeamWorkspaceBrowser.d.ts","sourceRoot":"","sources":["../../../src/client/TeamWorkspaceBrowser.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AASlD,wBAAgB,oBAAoB,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,CAAC,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,mBAAmB,EAAE,UAAU,EAAE,iBAAiB,EAAE,EAAE,gBAAgB,+BA6D/T"}
1
+ {"version":3,"file":"TeamWorkspaceBrowser.d.ts","sourceRoot":"","sources":["../../../src/client/TeamWorkspaceBrowser.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAUlD,wBAAgB,oBAAoB,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,UAAU,EAAE,eAAe,EAAE,aAAa,EAAE,CAAC,EAAE,aAAa,EAAE,WAAW,EAAE,gBAAgB,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,mBAAmB,EAAE,UAAU,EAAE,iBAAiB,EAAE,EAAE,gBAAgB,+BA8DnV"}
@@ -3,10 +3,11 @@ import { useEffect, useRef, useState, useSyncExternalStore } from 'react';
3
3
  import { IconAgentPresetOutline16, IconListPenOutline16, Tooltip } from '@deepseek-ai/dsh-client-ui-primitives';
4
4
  import { TeamWorkspaceRow } from "./TeamWorkspaceRow.js";
5
5
  import { TeamSidebarSection } from "./TeamSidebarSection.js";
6
+ import { useSidebarSectionOpen, setSidebarSectionOpen } from "./sidebar-sections.js";
6
7
  import { TeamAgentsPanel } from "./TeamAgentsPanel.js";
7
8
  import { TeamChannelsPanel } from "./TeamChannelsPanel.js";
8
9
  import css from './sidebar.module.css';
9
- export function TeamWorkspaceBrowser({ wide, expandSidebar, navigation, selectWorkspace, selectChannel, t, useWorkspaces, loadMembers, subscribeChanges, addMember, loadChannels, createChannel, updateChannel, updateMember, recoverMember, joinChannel, removeChannelMember, loadModels, openMemberSession }) {
10
+ export function TeamWorkspaceBrowser({ wide, expandSidebar, navigation, selectWorkspace, selectChannel, t, useWorkspaces, loadMembers, subscribeChanges, addMember, loadChannels, createChannel, updateChannel, updateMember, recoverMember, clearMemberContext, joinChannel, removeChannelMember, loadModels, openMemberSession }) {
10
11
  const navigationState = useSyncExternalStore(navigation.subscribe, navigation.getSnapshot, navigation.getSnapshot);
11
12
  const workspaces = useWorkspaces(state => state.items);
12
13
  const selected = navigationState.workspaceId;
@@ -22,6 +23,7 @@ export function TeamWorkspaceBrowser({ wide, expandSidebar, navigation, selectWo
22
23
  const [pendingSection, setPendingSection] = useState();
23
24
  const channelsRef = useRef(null);
24
25
  const agentsRef = useRef(null);
26
+ const workspacesOpen = useSidebarSectionOpen(undefined, 'workspaces');
25
27
  useEffect(() => {
26
28
  if (navigationState.mode === 'team' && selectedId !== undefined && selectedId !== selected) {
27
29
  selectWorkspace(selectedId);
@@ -37,5 +39,5 @@ export function TeamWorkspaceBrowser({ wide, expandSidebar, navigation, selectWo
37
39
  if (!wide) {
38
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 }) }) })] });
39
41
  }
40
- return _jsxs("section", { className: css.workspaceBrowser, "aria-label": t('workspaces'), children: [_jsx(TeamSidebarSection, { title: t('workspaces'), 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, 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) })] })] });
41
43
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAa,MAAM,wCAAwC,CAAA;AAgCtF,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAO5C,OAAO,EAAU,KAAK,OAAO,EAAE,MAAM,cAAc,CAAA;AAEnD,YAAY,EAAE,QAAQ,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAC9F,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAIhD,eAAO,MAAM,MAAM,UAElB,CAAA;AAED,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,kBAAkB;QAC1B,IAAI,EAAE,OAAO,CAAA;KACd;CACF;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf,cAAc,EAAE,cAAc,CAAA;QAC9B,UAAU,EAAE,cAAc,CAAA;KAC3B;CACF;AAwND,wBAAsB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAI7D"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAa,MAAM,wCAAwC,CAAA;AAiCtF,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;AAO5C,OAAO,EAAU,KAAK,OAAO,EAAE,MAAM,cAAc,CAAA;AAEnD,YAAY,EAAE,QAAQ,EAAE,qBAAqB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAA;AAC9F,YAAY,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAIhD,eAAO,MAAM,MAAM,UAElB,CAAA;AAED,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,kBAAkB;QAC1B,IAAI,EAAE,OAAO,CAAA;KACd;CACF;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf,cAAc,EAAE,cAAc,CAAA;QAC9B,UAAU,EAAE,cAAc,CAAA;KAC3B;CACF;AA0ND,wBAAsB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAI7D"}
@@ -15,6 +15,19 @@ export const inject = [
15
15
  'slots', 'workspaces', 'locale', 'remote', 'sessions', 'connection', 'conversation', 'inputTriggers',
16
16
  ];
17
17
  function registerModeShadow(ctx, navigation, changes, drafts, name, component, extraInject) {
18
+ // Stay in Team mode: the conversation shadow stands down for Member Session
19
+ // views (see registerModeShadow), so the shipped conversation root renders
20
+ // the selected Member Session inside the Team shell.
21
+ const openMemberSessionImpl = (sessionId) => {
22
+ const sessions = ctx.sessions;
23
+ const snapshot = navigation.getSnapshot();
24
+ const current = sessions.list.getSnapshot().current;
25
+ // The return target is captured on first entry only — switching between
26
+ // Member Sessions must keep pointing at the Human's original session.
27
+ const returnTo = snapshot.memberSessionId === undefined && current !== undefined && current !== sessionId ? current : undefined;
28
+ navigation.actions().enterMemberSession(sessionId, returnTo);
29
+ sessions.open(sessionId);
30
+ };
18
31
  // Remote bindings shared by every Team slot; surface-specific entries extend it below.
19
32
  const sharedRemotes = {
20
33
  loadChannels: (request) => ctx.remote.agentTeam.view(request),
@@ -26,25 +39,14 @@ function registerModeShadow(ctx, navigation, changes, drafts, name, component, e
26
39
  updateChannel: (request) => ctx.remote.agentTeam.updateChannel(request),
27
40
  updateMember: (request) => ctx.remote.agentTeam.updateMember(request),
28
41
  recoverMember: (request) => ctx.remote.agentTeam.recoverMember(request),
42
+ clearMemberContext: (request) => ctx.remote.agentTeam.clearMemberContext(request),
29
43
  // The Host-scoped catalog needs no live Member, so suspended ones stay editable too.
30
44
  loadModels: async () => {
31
45
  const connection = ctx.get('connection');
32
46
  const response = await connection.api.llm.models({});
33
47
  return response.result;
34
48
  },
35
- openMemberSession: (sessionId) => {
36
- // Stay in Team mode: the conversation shadow stands down for Member
37
- // Session views (see registerModeShadow), so the shipped conversation
38
- // root renders the selected Member Session inside the Team shell.
39
- const sessions = ctx.sessions;
40
- const snapshot = navigation.getSnapshot();
41
- const current = sessions.list.getSnapshot().current;
42
- // The return target is captured on first entry only — switching between
43
- // Member Sessions must keep pointing at the Human's original session.
44
- const returnTo = snapshot.memberSessionId === undefined && current !== undefined && current !== sessionId ? current : undefined;
45
- navigation.actions().enterMemberSession(sessionId, returnTo);
46
- sessions.open(sessionId);
47
- },
49
+ openMemberSession: openMemberSessionImpl,
48
50
  };
49
51
  ctx.slots.inject(name, () => {
50
52
  let dispose;