@nanmicoder/dsh-agent-teams 0.1.10 → 0.1.11
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/lib/client/ActivityPanel.js +14 -10
- package/lib/client/AgentTeamsCard.js +5 -3
- package/lib/client/activity-monitor.js +32 -7
- package/lib/client/index.js +8 -2
- package/lib/client/session-navigation.js +21 -0
- package/lib/client.js +59 -20
- package/lib/client.js.map +1 -1
- package/lib/members.js +21 -47
- package/lib/snapshot.js +4 -15
- package/lib/tools.js +1 -1
- package/lib/types/client/ActivityPanel.d.ts +2 -2
- package/lib/types/client/AgentTeamsCard.d.ts +2 -2
- package/lib/types/client/activity-monitor.d.ts +13 -3
- package/lib/types/client/session-navigation.d.ts +22 -0
- package/lib/types/members.d.ts +17 -13
- package/package.json +1 -1
- package/release-notes/v0.1.11.md +52 -0
|
@@ -236,8 +236,11 @@ function TeamSection({ team, onNavigate, historic = false }) {
|
|
|
236
236
|
const allCompleted = team.tasks.length > 0 && completedCount === team.tasks.length;
|
|
237
237
|
return (_jsxs("section", { className: css.team, "data-team-id": team.teamId, children: [_jsxs("header", { className: css.teamHead, children: [_jsx("span", { className: css.teamName, title: team.name, children: team.name }), historic && _jsx("span", { className: css.historicPill, children: "\u5DF2\u7ED3\u675F" }), _jsxs("span", { className: css.teamStats, children: [_jsxs("span", { "data-stat": "members", children: [team.members.length, " \u6210\u5458"] }), _jsxs("span", { "data-stat": "tasks", children: [completedCount, "/", team.tasks.length, " \u5B8C\u6210"] }), _jsxs("span", { "data-stat": "messages", children: [team.messageCount, " \u6D88\u606F"] })] })] }), _jsxs("section", { className: css.delegationSection, "aria-label": "\u961F\u957F\u6D3E\u5DE5\u5173\u7CFB", "data-delegation-map": true, children: [_jsxs("div", { className: css.captainNode, children: [_jsx("span", { className: css.captainAvatar, children: _jsx("img", { className: css.leadAvatar, src: LEAD_ART, alt: "", "aria-hidden": true }) }), _jsxs("span", { className: css.captainInfo, children: [_jsxs("span", { className: css.captainLine, children: [_jsx("span", { className: css.captainName, children: "\u961F\u957F" }), _jsx("span", { className: css.captainRole, children: "\u62C6\u89E3 \u00B7 \u6D3E\u53D1 \u00B7 \u6C47\u603B" })] }), _jsxs("span", { className: css.captainSummary, children: ["\u5DF2\u6D3E\u53D1 ", assignedCount, " \u9879\u4EFB\u52A1\u7ED9 ", team.members.length, " \u540D\u6210\u5458"] })] }), _jsxs("span", { className: css.captainState, "data-busy": busyCount > 0, children: [_jsx(WorkGlyph, { active: busyCount > 0 }), busyCount > 0 ? `${busyCount} 人执行中` : allCompleted ? '已收齐' : '等待回报'] })] }), _jsx(ProgressOverview, { team: team }), _jsxs("button", { type: "button", className: css.membersToggle, onClick: () => { setMembersOpen((current) => !current); }, "aria-expanded": membersOpen, "data-members-toggle": true, children: [_jsxs("span", { children: [_jsx(Chevron, { open: membersOpen }), "\u6210\u5458 ", team.members.length] }), _jsx("span", { children: membersOpen ? '收起' : '展开' })] }), membersOpen && _jsxs("div", { className: css.delegationTree, children: [team.members.length === 0 && _jsx("span", { className: css.emptyHint, children: "\u6682\u65E0\u6210\u5458\uFF0C\u7B49\u5F85\u961F\u957F\u7EC4\u5EFA\u56E2\u961F" }), team.members.map((member) => {
|
|
238
238
|
const owned = team.tasks.filter((task) => task.assignee === member.name);
|
|
239
|
-
return (_jsxs("div", { className: css.memberBlock, "data-activity": member.activity, children: [_jsx("span", { className: css.memberBranch, "aria-hidden": true, children: _jsx("span", {}) }), _jsxs("button", { type: "button", className: css.memberRow, "data-activity": member.activity, onClick: () => {
|
|
240
|
-
|
|
239
|
+
return (_jsxs("div", { className: css.memberBlock, "data-activity": member.activity, children: [_jsx("span", { className: css.memberBranch, "aria-hidden": true, children: _jsx("span", {}) }), _jsxs("button", { type: "button", className: css.memberRow, "data-activity": member.activity, onClick: () => {
|
|
240
|
+
if (member.id !== '') {
|
|
241
|
+
onNavigate(team.captainSessionId, member.id);
|
|
242
|
+
}
|
|
243
|
+
}, children: [_jsxs("span", { className: css.memberAvatar, "data-unread": member.unread > 0, children: [memberArtUrl(member.name, member.role) !== null ? (_jsx("img", { className: css.memberArt, src: memberArtUrl(member.name, member.role) ?? '', alt: "", "aria-hidden": true })) : (_jsx("span", { className: css.memberInitial, style: { background: accentOf(member.id) }, children: memberInitial(member.name) })), _jsx("img", { className: css.stateArt, "data-activity": member.activity, src: ACTION_ART[member.activity], alt: "", "aria-hidden": true })] }), _jsxs("span", { className: css.memberInfo, children: [_jsxs("span", { className: css.memberLine, children: [_jsx("span", { className: css.memberName, children: member.name }), member.role !== '' && _jsx("span", { className: css.memberRole, children: member.role }), _jsxs("span", { className: css.memberState, "data-activity": member.activity, children: [_jsx(WorkGlyph, { active: member.activity === 'working' }), memberStateLabel(member, team.tasks, historic)] })] }), _jsx("span", { className: css.memberStatusLine, children: memberStatusText(member, team.tasks) })] }), _jsxs("span", { className: css.memberCount, children: [member.done, "/", member.total] })] }), _jsxs("div", { className: css.assignmentLine, children: [_jsx("span", { className: css.assignmentLabel, children: "\u961F\u957F\u6D3E\u53D1" }), _jsx("span", { className: css.assignmentTasks, children: owned.length === 0
|
|
241
244
|
? _jsx("span", { className: css.taskEmpty, children: "\u6682\u65E0\u4EFB\u52A1" })
|
|
242
245
|
: owned.map((task) => (_jsx("span", { className: css.assignmentChip, "data-state": taskTone(task.state, task.status), title: task.subject, children: task.id }, task.id))) })] })] }, member.id));
|
|
243
246
|
})] })] }), _jsx(DependencyMap, { tasks: team.tasks })] }));
|
|
@@ -268,14 +271,14 @@ function historicCardTeam(data, owner) {
|
|
|
268
271
|
/** The top-right activity floater. Teams follow the current session: live
|
|
269
272
|
* snapshots and historic card summaries are only shown while their captain
|
|
270
273
|
* session is the one currently open. */
|
|
271
|
-
export function ActivityPanel({ sessionsList,
|
|
274
|
+
export function ActivityPanel({ sessionsList, openMember }) {
|
|
272
275
|
// Navigating to a member's subagent transcript is an explicit departure:
|
|
273
276
|
// hide the floater immediately instead of waiting out the autocollapse
|
|
274
277
|
// grace, so the panel never lingers over the member session.
|
|
275
|
-
const navigateToSession = (
|
|
278
|
+
const navigateToSession = (parentId, childId) => {
|
|
276
279
|
setOpen(false);
|
|
277
280
|
setWasActive(false);
|
|
278
|
-
|
|
281
|
+
openMember(parentId, childId);
|
|
279
282
|
};
|
|
280
283
|
const [open, setOpen] = useState(false);
|
|
281
284
|
const [openOwner, setOpenOwner] = useState();
|
|
@@ -383,13 +386,14 @@ export function ActivityPanel({ sessionsList, openSession }) {
|
|
|
383
386
|
};
|
|
384
387
|
}, [compact, expanded, geometry.mode, geometry.width]);
|
|
385
388
|
useEffect(() => {
|
|
386
|
-
|
|
387
|
-
// AgentTeams conversation card registers the demand that reaches here.
|
|
388
|
-
if (currentTargets.length === 0)
|
|
389
|
+
if (current === undefined)
|
|
389
390
|
return;
|
|
390
|
-
|
|
391
|
+
// Cards keep live teams on the normal cadence. The current-session scope
|
|
392
|
+
// also performs one cold-start discovery pass so archived/cardless teams
|
|
393
|
+
// survive a browser or `dsh web` restart.
|
|
394
|
+
const controller = startActivityPolling(currentTargets, { discoverySessionId: current });
|
|
391
395
|
return () => { controller.stop(); };
|
|
392
|
-
}, [currentTargets]);
|
|
396
|
+
}, [current, currentTargets]);
|
|
393
397
|
useEffect(() => {
|
|
394
398
|
const onOpenPanel = (event) => {
|
|
395
399
|
const activeSession = currentRef.current;
|
|
@@ -30,7 +30,7 @@ function openActivityPanel(data) {
|
|
|
30
30
|
}));
|
|
31
31
|
}
|
|
32
32
|
/** Render one durable team as a compact conversation card. */
|
|
33
|
-
export function AgentTeamsCard({ node,
|
|
33
|
+
export function AgentTeamsCard({ node, openMember, sessionId }) {
|
|
34
34
|
const data = node.data;
|
|
35
35
|
// `conversation.chat.node` is session-scoped, so its framework-owned id is
|
|
36
36
|
// a stable owner even while another conversation becomes current.
|
|
@@ -47,6 +47,8 @@ export function AgentTeamsCard({ node, openSession, sessionId }) {
|
|
|
47
47
|
teamName: snapshot?.name ?? data.teamName,
|
|
48
48
|
members: snapshot?.members.map((member) => ({ id: member.id, name: member.name, role: member.role })) ?? data.members,
|
|
49
49
|
}), [data, owner, snapshot]);
|
|
50
|
-
return (_jsxs("section", { className: css.root, "data-agent-teams-card": true, "data-team-id": resolved.teamId, children: [_jsxs("header", { className: css.head, children: [_jsx("img", { className: css.leadAvatar, src: LEAD_ART, alt: "", "aria-hidden": true }), _jsx("span", { className: css.teamName, title: resolved.teamName, children: resolved.teamName }), _jsxs("span", { className: css.memberCount, children: [resolved.members.length, " \u540D\u6210\u5458"] }), _jsx("button", { type: "button", className: css.panelButton, onClick: () => { openActivityPanel(resolved); }, "aria-label": "\u6253\u5F00\u6D3B\u52A8\u9762\u677F", title: "\u6253\u5F00\u6D3B\u52A8\u9762\u677F", children: "\u6D3B\u52A8\u9762\u677F" })] }), resolved.members.length > 0 && (_jsx("div", { className: css.members, children: resolved.members.map((member) => (_jsxs("button", { type: "button", className: css.member, onClick: () => {
|
|
51
|
-
|
|
50
|
+
return (_jsxs("section", { className: css.root, "data-agent-teams-card": true, "data-team-id": resolved.teamId, children: [_jsxs("header", { className: css.head, children: [_jsx("img", { className: css.leadAvatar, src: LEAD_ART, alt: "", "aria-hidden": true }), _jsx("span", { className: css.teamName, title: resolved.teamName, children: resolved.teamName }), _jsxs("span", { className: css.memberCount, children: [resolved.members.length, " \u540D\u6210\u5458"] }), _jsx("button", { type: "button", className: css.panelButton, onClick: () => { openActivityPanel(resolved); }, "aria-label": "\u6253\u5F00\u6D3B\u52A8\u9762\u677F", title: "\u6253\u5F00\u6D3B\u52A8\u9762\u677F", children: "\u6D3B\u52A8\u9762\u677F" })] }), resolved.members.length > 0 && (_jsx("div", { className: css.members, children: resolved.members.map((member) => (_jsxs("button", { type: "button", className: css.member, onClick: () => {
|
|
51
|
+
if (member.id !== '')
|
|
52
|
+
openMember(owner, member.id);
|
|
53
|
+
}, title: member.role === '' ? member.name : `${member.name} · ${member.role}`, children: [memberArtUrl(member.name, member.role) !== null ? (_jsx("img", { className: css.memberArt, src: memberArtUrl(member.name, member.role) ?? '', alt: "", "aria-hidden": true })) : (_jsx("span", { className: css.memberInitial, children: member.name.trim().slice(0, 1).toUpperCase() || '?' })), _jsx("span", { className: css.memberName, children: member.name })] }, member.id))) }))] }));
|
|
52
54
|
}
|
|
@@ -104,12 +104,17 @@ export const ACTIVITY_STATE_URL = '/plugins/dsh-agent-teams/state';
|
|
|
104
104
|
/**
|
|
105
105
|
* Start the single polling loop for the current session's requested targets.
|
|
106
106
|
*
|
|
107
|
-
* With
|
|
108
|
-
*
|
|
109
|
-
*
|
|
107
|
+
* With neither targets nor a discovery session this is deliberately inert.
|
|
108
|
+
* A discovery session performs one live+archive pass after selection/restart;
|
|
109
|
+
* it keeps polling only while that captain still owns a live team. This
|
|
110
|
+
* restores legacy/cardless history without turning every ordinary session
|
|
111
|
+
* into a permanent one-second filesystem scan. Explicit card targets retain
|
|
112
|
+
* the normal cadence, and archive state is refreshed when a target or a
|
|
113
|
+
* previously discovered live team disappears.
|
|
110
114
|
*/
|
|
111
115
|
export function startActivityPolling(monitorTargets, runtime = {}) {
|
|
112
|
-
|
|
116
|
+
const discoverySessionId = runtime.discoverySessionId?.trim();
|
|
117
|
+
if (monitorTargets.length === 0 && (discoverySessionId === undefined || discoverySessionId === '')) {
|
|
113
118
|
return { firstTick: Promise.resolve(), stop: () => { } };
|
|
114
119
|
}
|
|
115
120
|
const fetchState = runtime.fetchState ?? ((url, init) => fetch(url, init));
|
|
@@ -119,10 +124,16 @@ export function startActivityPolling(monitorTargets, runtime = {}) {
|
|
|
119
124
|
const settleTargets = runtime.settleTargets ?? settleActivityMonitorTargets;
|
|
120
125
|
let cancelled = false;
|
|
121
126
|
let inFlight = false;
|
|
127
|
+
let discoveryComplete = false;
|
|
128
|
+
let discoveredLiveKeys = new Set();
|
|
122
129
|
let controller;
|
|
123
130
|
const tick = async () => {
|
|
124
131
|
if (inFlight || cancelled)
|
|
125
132
|
return;
|
|
133
|
+
// A cardless ordinary or archive-only session needs one recovery pass,
|
|
134
|
+
// then stays dormant until the component is recreated for another session.
|
|
135
|
+
if (discoveryComplete && monitorTargets.length === 0 && discoveredLiveKeys.size === 0)
|
|
136
|
+
return;
|
|
126
137
|
inFlight = true;
|
|
127
138
|
controller = new AbortController();
|
|
128
139
|
try {
|
|
@@ -137,11 +148,24 @@ export function startActivityPolling(monitorTargets, runtime = {}) {
|
|
|
137
148
|
return;
|
|
138
149
|
const liveTeams = body.teams;
|
|
139
150
|
publishSnapshots({ teams: liveTeams });
|
|
151
|
+
const previousDiscoveredKeys = discoveredLiveKeys;
|
|
152
|
+
discoveredLiveKeys = new Set(discoverySessionId === undefined || discoverySessionId === ''
|
|
153
|
+
? []
|
|
154
|
+
: liveTeams
|
|
155
|
+
.filter((team) => team.captainSessionId === discoverySessionId)
|
|
156
|
+
.map((team) => team.teamId));
|
|
157
|
+
const discoveredTeamArchived = [...previousDiscoveredKeys]
|
|
158
|
+
.some((teamId) => !discoveredLiveKeys.has(teamId));
|
|
140
159
|
const missing = monitorTargets.filter((target) => !liveTeams.some((team) => team.captainSessionId === target.sessionId && team.teamId === target.teamId));
|
|
141
|
-
|
|
160
|
+
const needsDiscoveryArchive = discoverySessionId !== undefined
|
|
161
|
+
&& discoverySessionId !== ''
|
|
162
|
+
&& !discoveryComplete;
|
|
163
|
+
if (missing.length === 0 && !needsDiscoveryArchive && !discoveredTeamArchived)
|
|
142
164
|
return;
|
|
143
|
-
// Archives are immutable. A successful fallback
|
|
144
|
-
// target, including legacy cards whose
|
|
165
|
+
// Archives are immutable per team generation. A successful fallback
|
|
166
|
+
// retires every missing explicit target, including legacy cards whose
|
|
167
|
+
// host archive no longer exists; discovery remains available from the
|
|
168
|
+
// shared snapshot after this controller becomes dormant.
|
|
145
169
|
const archivedResponse = await fetchState(`${ACTIVITY_STATE_URL}?archived=1`, {
|
|
146
170
|
cache: 'no-store',
|
|
147
171
|
signal: controller.signal,
|
|
@@ -152,6 +176,7 @@ export function startActivityPolling(monitorTargets, runtime = {}) {
|
|
|
152
176
|
if (cancelled || !Array.isArray(archivedBody.teams))
|
|
153
177
|
return;
|
|
154
178
|
publishSnapshots({ archivedTeams: archivedBody.teams });
|
|
179
|
+
discoveryComplete = true;
|
|
155
180
|
settleTargets(new Set(missing.map((target) => target.key)));
|
|
156
181
|
}
|
|
157
182
|
catch (error) {
|
package/lib/client/index.js
CHANGED
|
@@ -2,6 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
|
|
|
2
2
|
import { ActivityPanel } from "./ActivityPanel.js";
|
|
3
3
|
import { AgentTeamsCard } from "./AgentTeamsCard.js";
|
|
4
4
|
import { agentTeamsCardDefinition } from "./agent-teams-card-definition.js";
|
|
5
|
+
import { openAgentTeamMember } from "./session-navigation.js";
|
|
5
6
|
/** Required services: conversation nodes, slots, and sessions navigation. */
|
|
6
7
|
export const inject = ['conversationEvents', 'slots', 'sessions'];
|
|
7
8
|
/** The replayed user message is the canonical transcript entry. */
|
|
@@ -14,7 +15,12 @@ function HiddenAgentTeamsCommand() {
|
|
|
14
15
|
* monitor via a window event — the recovery path for an old session.
|
|
15
16
|
*/
|
|
16
17
|
export function apply(ctx) {
|
|
17
|
-
const
|
|
18
|
+
const openMember = (parentId, childId) => {
|
|
19
|
+
void openAgentTeamMember(ctx.sessions, parentId, childId).catch((error) => {
|
|
20
|
+
console.warn(`agent-teams: failed to open member transcript ${childId}: ${String(error)}`);
|
|
21
|
+
});
|
|
22
|
+
};
|
|
23
|
+
const Panel = () => _jsx(ActivityPanel, { sessionsList: ctx.sessions.list, openMember: openMember });
|
|
18
24
|
ctx.slots.inject('shell.overlay', () => ctx.slots.register({
|
|
19
25
|
name: 'shell.overlay',
|
|
20
26
|
id: 'agent-teams-activity',
|
|
@@ -33,7 +39,7 @@ export function apply(ctx) {
|
|
|
33
39
|
name: 'conversation.chat.node',
|
|
34
40
|
key: 'agent-teams',
|
|
35
41
|
inject: () => ({
|
|
36
|
-
|
|
42
|
+
openMember,
|
|
37
43
|
}),
|
|
38
44
|
}, AgentTeamsCard));
|
|
39
45
|
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** Version-tolerant navigation into durable AgentTeams member transcripts. */
|
|
2
|
+
/**
|
|
3
|
+
* Open one member's persisted transcript.
|
|
4
|
+
*
|
|
5
|
+
* Harness rc.8 intentionally removed cold subagents from the ordinary session
|
|
6
|
+
* list. They must first be rediscovered in their parent's catalog, then opened
|
|
7
|
+
* with the exact parent/child/mode address. Older runtimes have only `open()`;
|
|
8
|
+
* the fallback preserves the plugin's rc.6 peer range.
|
|
9
|
+
*/
|
|
10
|
+
export async function openAgentTeamMember(sessions, parentSessionId, childSessionId) {
|
|
11
|
+
if (sessions.openSubagent === undefined || sessions.refreshSubagents === undefined) {
|
|
12
|
+
sessions.open(childSessionId);
|
|
13
|
+
return 'session';
|
|
14
|
+
}
|
|
15
|
+
await sessions.refreshSubagents(parentSessionId);
|
|
16
|
+
const retained = sessions.subagentAddress?.(childSessionId);
|
|
17
|
+
sessions.openSubagent(retained?.parentSessionId === parentSessionId
|
|
18
|
+
? retained
|
|
19
|
+
: { parentSessionId, childSessionId, mode: 'continuable' });
|
|
20
|
+
return 'subagent';
|
|
21
|
+
}
|
package/lib/client.js
CHANGED
|
@@ -246,12 +246,17 @@ window.__ModuleLoader__.load({
|
|
|
246
246
|
/**
|
|
247
247
|
* Start the single polling loop for the current session's requested targets.
|
|
248
248
|
*
|
|
249
|
-
* With
|
|
250
|
-
*
|
|
251
|
-
*
|
|
249
|
+
* With neither targets nor a discovery session this is deliberately inert.
|
|
250
|
+
* A discovery session performs one live+archive pass after selection/restart;
|
|
251
|
+
* it keeps polling only while that captain still owns a live team. This
|
|
252
|
+
* restores legacy/cardless history without turning every ordinary session
|
|
253
|
+
* into a permanent one-second filesystem scan. Explicit card targets retain
|
|
254
|
+
* the normal cadence, and archive state is refreshed when a target or a
|
|
255
|
+
* previously discovered live team disappears.
|
|
252
256
|
*/
|
|
253
257
|
function startActivityPolling(monitorTargets, runtime = {}) {
|
|
254
|
-
|
|
258
|
+
const discoverySessionId = runtime.discoverySessionId?.trim();
|
|
259
|
+
if (monitorTargets.length === 0 && (discoverySessionId === void 0 || discoverySessionId === "")) return {
|
|
255
260
|
firstTick: Promise.resolve(),
|
|
256
261
|
stop: () => {}
|
|
257
262
|
};
|
|
@@ -264,9 +269,12 @@ window.__ModuleLoader__.load({
|
|
|
264
269
|
const settleTargets = runtime.settleTargets ?? settleActivityMonitorTargets;
|
|
265
270
|
let cancelled = false;
|
|
266
271
|
let inFlight = false;
|
|
272
|
+
let discoveryComplete = false;
|
|
273
|
+
let discoveredLiveKeys = /* @__PURE__ */ new Set();
|
|
267
274
|
let controller;
|
|
268
275
|
const tick = async () => {
|
|
269
276
|
if (inFlight || cancelled) return;
|
|
277
|
+
if (discoveryComplete && monitorTargets.length === 0 && discoveredLiveKeys.size === 0) return;
|
|
270
278
|
inFlight = true;
|
|
271
279
|
controller = new AbortController();
|
|
272
280
|
try {
|
|
@@ -279,8 +287,12 @@ window.__ModuleLoader__.load({
|
|
|
279
287
|
if (cancelled || !Array.isArray(body.teams)) return;
|
|
280
288
|
const liveTeams = body.teams;
|
|
281
289
|
publishSnapshots({ teams: liveTeams });
|
|
290
|
+
const previousDiscoveredKeys = discoveredLiveKeys;
|
|
291
|
+
discoveredLiveKeys = new Set(discoverySessionId === void 0 || discoverySessionId === "" ? [] : liveTeams.filter((team) => team.captainSessionId === discoverySessionId).map((team) => team.teamId));
|
|
292
|
+
const discoveredTeamArchived = [...previousDiscoveredKeys].some((teamId) => !discoveredLiveKeys.has(teamId));
|
|
282
293
|
const missing = monitorTargets.filter((target) => !liveTeams.some((team) => team.captainSessionId === target.sessionId && team.teamId === target.teamId));
|
|
283
|
-
|
|
294
|
+
const needsDiscoveryArchive = discoverySessionId !== void 0 && discoverySessionId !== "" && !discoveryComplete;
|
|
295
|
+
if (missing.length === 0 && !needsDiscoveryArchive && !discoveredTeamArchived) return;
|
|
284
296
|
const archivedResponse = await fetchState(`${ACTIVITY_STATE_URL}?archived=1`, {
|
|
285
297
|
cache: "no-store",
|
|
286
298
|
signal: controller.signal
|
|
@@ -289,6 +301,7 @@ window.__ModuleLoader__.load({
|
|
|
289
301
|
const archivedBody = await archivedResponse.json();
|
|
290
302
|
if (cancelled || !Array.isArray(archivedBody.teams)) return;
|
|
291
303
|
publishSnapshots({ archivedTeams: archivedBody.teams });
|
|
304
|
+
discoveryComplete = true;
|
|
292
305
|
settleTargets(new Set(missing.map((target) => target.key)));
|
|
293
306
|
} catch (error) {
|
|
294
307
|
if (error?.name === "AbortError") return;
|
|
@@ -401,7 +414,7 @@ window.__ModuleLoader__.load({
|
|
|
401
414
|
} }));
|
|
402
415
|
}
|
|
403
416
|
/** Render one durable team as a compact conversation card. */
|
|
404
|
-
function AgentTeamsCard({ node,
|
|
417
|
+
function AgentTeamsCard({ node, openMember, sessionId }) {
|
|
405
418
|
const data = node.data;
|
|
406
419
|
const owner = data.captainSessionId || sessionId;
|
|
407
420
|
const { teams, archivedTeams } = (0, react.useSyncExternalStore)(subscribeActivitySnapshots, getActivitySnapshotsSnapshot);
|
|
@@ -462,7 +475,7 @@ window.__ModuleLoader__.load({
|
|
|
462
475
|
type: "button",
|
|
463
476
|
className: AgentTeamsCard_module_css_default.member,
|
|
464
477
|
onClick: () => {
|
|
465
|
-
if (member.id !== "")
|
|
478
|
+
if (member.id !== "") openMember(owner, member.id);
|
|
466
479
|
},
|
|
467
480
|
title: member.role === "" ? member.name : `${member.name} · ${member.role}`,
|
|
468
481
|
children: [memberArtUrl(member.name, member.role) !== null ? (0, react_jsx_runtime.jsx)("img", {
|
|
@@ -1272,7 +1285,7 @@ window.__ModuleLoader__.load({
|
|
|
1272
1285
|
className: ActivityPanel_module_css_default.memberRow,
|
|
1273
1286
|
"data-activity": member.activity,
|
|
1274
1287
|
onClick: () => {
|
|
1275
|
-
if (member.id !== "") onNavigate(member.id);
|
|
1288
|
+
if (member.id !== "") onNavigate(team.captainSessionId, member.id);
|
|
1276
1289
|
},
|
|
1277
1290
|
children: [
|
|
1278
1291
|
(0, react_jsx_runtime.jsxs)("span", {
|
|
@@ -1383,11 +1396,11 @@ window.__ModuleLoader__.load({
|
|
|
1383
1396
|
/** The top-right activity floater. Teams follow the current session: live
|
|
1384
1397
|
* snapshots and historic card summaries are only shown while their captain
|
|
1385
1398
|
* session is the one currently open. */
|
|
1386
|
-
function ActivityPanel({ sessionsList,
|
|
1387
|
-
const navigateToSession = (
|
|
1399
|
+
function ActivityPanel({ sessionsList, openMember }) {
|
|
1400
|
+
const navigateToSession = (parentId, childId) => {
|
|
1388
1401
|
setOpen(false);
|
|
1389
1402
|
setWasActive(false);
|
|
1390
|
-
|
|
1403
|
+
openMember(parentId, childId);
|
|
1391
1404
|
};
|
|
1392
1405
|
const [open, setOpen] = (0, react.useState)(false);
|
|
1393
1406
|
const [openOwner, setOpenOwner] = (0, react.useState)();
|
|
@@ -1480,12 +1493,12 @@ window.__ModuleLoader__.load({
|
|
|
1480
1493
|
geometry.width
|
|
1481
1494
|
]);
|
|
1482
1495
|
(0, react.useEffect)(() => {
|
|
1483
|
-
if (
|
|
1484
|
-
const controller = startActivityPolling(currentTargets);
|
|
1496
|
+
if (current === void 0) return;
|
|
1497
|
+
const controller = startActivityPolling(currentTargets, { discoverySessionId: current });
|
|
1485
1498
|
return () => {
|
|
1486
1499
|
controller.stop();
|
|
1487
1500
|
};
|
|
1488
|
-
}, [currentTargets]);
|
|
1501
|
+
}, [current, currentTargets]);
|
|
1489
1502
|
(0, react.useEffect)(() => {
|
|
1490
1503
|
const onOpenPanel = (event) => {
|
|
1491
1504
|
const activeSession = currentRef.current;
|
|
@@ -1867,6 +1880,31 @@ window.__ModuleLoader__.load({
|
|
|
1867
1880
|
}
|
|
1868
1881
|
};
|
|
1869
1882
|
//#endregion
|
|
1883
|
+
//#region lib/client/session-navigation.js
|
|
1884
|
+
/** Version-tolerant navigation into durable AgentTeams member transcripts. */
|
|
1885
|
+
/**
|
|
1886
|
+
* Open one member's persisted transcript.
|
|
1887
|
+
*
|
|
1888
|
+
* Harness rc.8 intentionally removed cold subagents from the ordinary session
|
|
1889
|
+
* list. They must first be rediscovered in their parent's catalog, then opened
|
|
1890
|
+
* with the exact parent/child/mode address. Older runtimes have only `open()`;
|
|
1891
|
+
* the fallback preserves the plugin's rc.6 peer range.
|
|
1892
|
+
*/
|
|
1893
|
+
async function openAgentTeamMember(sessions, parentSessionId, childSessionId) {
|
|
1894
|
+
if (sessions.openSubagent === void 0 || sessions.refreshSubagents === void 0) {
|
|
1895
|
+
sessions.open(childSessionId);
|
|
1896
|
+
return "session";
|
|
1897
|
+
}
|
|
1898
|
+
await sessions.refreshSubagents(parentSessionId);
|
|
1899
|
+
const retained = sessions.subagentAddress?.(childSessionId);
|
|
1900
|
+
sessions.openSubagent(retained?.parentSessionId === parentSessionId ? retained : {
|
|
1901
|
+
parentSessionId,
|
|
1902
|
+
childSessionId,
|
|
1903
|
+
mode: "continuable"
|
|
1904
|
+
});
|
|
1905
|
+
return "subagent";
|
|
1906
|
+
}
|
|
1907
|
+
//#endregion
|
|
1870
1908
|
//#region lib/client/index.js
|
|
1871
1909
|
/** Required services: conversation nodes, slots, and sessions navigation. */
|
|
1872
1910
|
const inject = [
|
|
@@ -1884,11 +1922,14 @@ window.__ModuleLoader__.load({
|
|
|
1884
1922
|
* monitor via a window event — the recovery path for an old session.
|
|
1885
1923
|
*/
|
|
1886
1924
|
function apply(ctx) {
|
|
1925
|
+
const openMember = (parentId, childId) => {
|
|
1926
|
+
openAgentTeamMember(ctx.sessions, parentId, childId).catch((error) => {
|
|
1927
|
+
console.warn(`agent-teams: failed to open member transcript ${childId}: ${String(error)}`);
|
|
1928
|
+
});
|
|
1929
|
+
};
|
|
1887
1930
|
const Panel = () => (0, react_jsx_runtime.jsx)(ActivityPanel, {
|
|
1888
1931
|
sessionsList: ctx.sessions.list,
|
|
1889
|
-
|
|
1890
|
-
ctx.sessions.open(id);
|
|
1891
|
-
}
|
|
1932
|
+
openMember
|
|
1892
1933
|
});
|
|
1893
1934
|
ctx.slots.inject("shell.overlay", () => ctx.slots.register({
|
|
1894
1935
|
name: "shell.overlay",
|
|
@@ -1904,9 +1945,7 @@ window.__ModuleLoader__.load({
|
|
|
1904
1945
|
ctx.slots.inject("conversation.chat.node", () => ctx.slots.register({
|
|
1905
1946
|
name: "conversation.chat.node",
|
|
1906
1947
|
key: "agent-teams",
|
|
1907
|
-
inject: () => ({
|
|
1908
|
-
ctx.sessions.open(id);
|
|
1909
|
-
} })
|
|
1948
|
+
inject: () => ({ openMember })
|
|
1910
1949
|
}, AgentTeamsCard));
|
|
1911
1950
|
}
|
|
1912
1951
|
//#endregion
|