@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.
- package/README.md +15 -1
- package/README.zh.md +20 -3
- package/package.json +7 -2
- package/packages/agent-team/README.md +6 -0
- package/packages/agent-team/README.zh.md +6 -0
- package/packages/agent-team/core-skills/member-skill-manager/SKILL.md +41 -0
- package/packages/agent-team/core-skills/member-skill-manager/references/auth-and-config.md +30 -0
- package/packages/agent-team/core-skills/member-skill-manager/references/writing-great-skills.md +17 -0
- package/packages/agent-team/lib/auto-compaction.js +39 -1
- package/packages/agent-team/lib/index.js +340 -7
- package/packages/agent-team/lib/ledger.js +219 -7
- package/packages/agent-team/lib/member-context.js +2 -2
- package/packages/agent-team/lib/member-skills.js +94 -0
- package/packages/agent-team/lib/spec.js +38 -0
- package/packages/agent-team/lib/typert.host.js +202 -27
- package/packages/agent-team/lib/typert.remote-client.d.ts +3 -1
- package/packages/agent-team/lib/typert.remote-client.d.ts.map +1 -1
- package/packages/agent-team/lib/typert.remote-client.js +155 -22
- package/packages/agent-team/lib/types/auto-compaction.d.ts +9 -0
- package/packages/agent-team/lib/types/auto-compaction.d.ts.map +1 -1
- package/packages/agent-team/lib/types/index.d.ts +81 -1
- package/packages/agent-team/lib/types/index.d.ts.map +1 -1
- package/packages/agent-team/lib/types/ledger.d.ts +49 -1
- package/packages/agent-team/lib/types/ledger.d.ts.map +1 -1
- package/packages/agent-team/lib/types/member-skills.d.ts +45 -0
- package/packages/agent-team/lib/types/member-skills.d.ts.map +1 -0
- package/packages/agent-team/lib/types/spec.d.ts.map +1 -1
- package/packages/agent-team/lib/types/types.d.ts +127 -2
- package/packages/agent-team/lib/types/types.d.ts.map +1 -1
- package/packages/agent-team/preset/team-member/agent.cordis.yml +7 -4
- package/packages/client-agent-team/README.md +1 -1
- package/packages/client-agent-team/README.zh.md +1 -1
- package/packages/client-agent-team/lib/client.js +677 -363
- package/packages/client-agent-team/lib/client.js.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts +2 -4
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamAgentsPanel.js +48 -24
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamChannelPage.js +20 -0
- package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamChannelsPanel.js +3 -1
- package/packages/client-agent-team/lib/types/client/TeamComposer.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamComposer.js +73 -17
- package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts +4 -8
- package/packages/client-agent-team/lib/types/client/TeamMemberEditor.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamMemberEditor.js +7 -40
- package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamMemberSessionComposer.js +16 -1
- package/packages/client-agent-team/lib/types/client/TeamMessage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamMessage.js +48 -18
- package/packages/client-agent-team/lib/types/client/TeamRowMenu.d.ts +3 -2
- package/packages/client-agent-team/lib/types/client/TeamRowMenu.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamSidebarSection.d.ts +5 -2
- package/packages/client-agent-team/lib/types/client/TeamSidebarSection.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamSidebarSection.js +2 -4
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamThreadPage.js +82 -22
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts +1 -1
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/TeamWorkspaceBrowser.js +4 -2
- package/packages/client-agent-team/lib/types/client/index.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/index.js +15 -13
- package/packages/client-agent-team/lib/types/client/locales.d.ts +20 -16
- package/packages/client-agent-team/lib/types/client/locales.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/locales.js +20 -16
- package/packages/client-agent-team/lib/types/client/sidebar-sections.d.ts +16 -0
- package/packages/client-agent-team/lib/types/client/sidebar-sections.d.ts.map +1 -0
- package/packages/client-agent-team/lib/types/client/sidebar-sections.js +102 -0
- package/packages/client-agent-team/lib/types/client/slots.d.ts +2 -1
- package/packages/client-agent-team/lib/types/client/slots.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/team-formatters.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/team-formatters.js +9 -2
- package/packages/client-agent-team/lib/types/client/timeline-scroll.d.ts +5 -7
- package/packages/client-agent-team/lib/types/client/timeline-scroll.d.ts.map +1 -1
- package/packages/client-agent-team/lib/types/client/timeline-scroll.js +9 -22
- package/packages/tool-agent-team/lib/index.js +25 -5
- 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,+
|
|
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
|
|
74
|
-
if (
|
|
75
|
-
onOpenRef(
|
|
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
|
|
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
|
|
144
|
+
if (taskRef !== undefined && resolved === undefined)
|
|
145
|
+
return;
|
|
146
|
+
if (taskRef !== undefined) {
|
|
147
|
+
code.replaceWith(resolvedTaskRefButton(taskRef, taskLabel(resolved.taskNumber)));
|
|
141
148
|
return;
|
|
142
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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":"
|
|
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 {
|
|
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,
|
|
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
|
-
|
|
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,+
|
|
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
|
-
|
|
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
|
-
|
|
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 =>
|
|
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
|
-
//
|
|
164
|
-
//
|
|
165
|
-
//
|
|
166
|
-
//
|
|
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);
|
|
176
|
-
//
|
|
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
|
-
|
|
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
|
-
//
|
|
219
|
-
|
|
220
|
-
|
|
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",
|
|
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.
|
|
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
|
-
|
|
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) }),
|
|
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;
|
|
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,
|
|
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;
|
|
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:
|
|
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;
|