@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
package/lib/members.js
CHANGED
|
@@ -318,44 +318,22 @@ export function interruptMember(ctx, captain, childId) {
|
|
|
318
318
|
ctx.logger.warn(`agent-teams: interrupt of member ${childId} failed: ${String(error)}`);
|
|
319
319
|
}
|
|
320
320
|
}
|
|
321
|
-
/** Resolve one live parent's workspace-scoped retirement index. */
|
|
322
|
-
async function retiredForParent(ctx, parentId, stateDir) {
|
|
323
|
-
const parent = ctx.agents.get(parentId);
|
|
324
|
-
return parent === undefined
|
|
325
|
-
? new Set()
|
|
326
|
-
: readRetiredMemberIds(join(parent.session.header.cwd ?? process.cwd(), stateDir));
|
|
327
|
-
}
|
|
328
321
|
/**
|
|
329
322
|
* Install the missing per-child retirement boundary above Harness rc.6.
|
|
330
323
|
*
|
|
331
324
|
* Upstream `interrupt()` deliberately preserves continuable sessions and the
|
|
332
325
|
* upstream seam exposes no targeted forget/retire method. The durable
|
|
333
|
-
* AgentTeams index therefore
|
|
334
|
-
* retired
|
|
335
|
-
*
|
|
336
|
-
*
|
|
337
|
-
*
|
|
326
|
+
* AgentTeams index therefore rejects `followup()` before it can cold-resume a
|
|
327
|
+
* retired member. Catalog rows deliberately remain discoverable: Harness rc.8
|
|
328
|
+
* uses the direct-child catalog to authorize historical transcript reads and
|
|
329
|
+
* `openSubagent()`, so filtering those rows would make an archived member's
|
|
330
|
+
* persisted conversation inaccessible. Exact ids keep unrelated subagents
|
|
331
|
+
* untouched while the followup boundary still prevents further model turns.
|
|
338
332
|
*/
|
|
339
333
|
export function installRetiredMemberGuard(ctx, stateDir) {
|
|
340
334
|
const runtime = ctx.subagents;
|
|
341
335
|
ctx.effect(() => {
|
|
342
|
-
const listChildren = runtime.listChildren;
|
|
343
|
-
const listDescendants = runtime.listDescendants;
|
|
344
336
|
const followup = runtime.followup;
|
|
345
|
-
const guardedChildren = async (parentId, signal) => {
|
|
346
|
-
const [entries, retired] = await Promise.all([
|
|
347
|
-
listChildren.call(runtime, parentId, signal),
|
|
348
|
-
retiredForParent(ctx, parentId, stateDir),
|
|
349
|
-
]);
|
|
350
|
-
return entries.filter(entry => !retired.has(entry.id));
|
|
351
|
-
};
|
|
352
|
-
const guardedDescendants = async (rootId, signal) => {
|
|
353
|
-
const [entries, retired] = await Promise.all([
|
|
354
|
-
listDescendants.call(runtime, rootId, signal),
|
|
355
|
-
retiredForParent(ctx, rootId, stateDir),
|
|
356
|
-
]);
|
|
357
|
-
return entries.filter(entry => !retired.has(entry.id));
|
|
358
|
-
};
|
|
359
337
|
const guardedFollowup = async (parent, childId, content, options) => {
|
|
360
338
|
const retired = await readRetiredMemberIds(join(parent.session.header.cwd ?? process.cwd(), stateDir));
|
|
361
339
|
if (retired.has(childId)) {
|
|
@@ -363,36 +341,32 @@ export function installRetiredMemberGuard(ctx, stateDir) {
|
|
|
363
341
|
}
|
|
364
342
|
return followup.call(runtime, parent, childId, content, options);
|
|
365
343
|
};
|
|
366
|
-
runtime.listChildren = guardedChildren;
|
|
367
|
-
runtime.listDescendants = guardedDescendants;
|
|
368
344
|
runtime.followup = guardedFollowup;
|
|
369
345
|
return () => {
|
|
370
|
-
if (runtime.listChildren === guardedChildren)
|
|
371
|
-
runtime.listChildren = listChildren;
|
|
372
|
-
if (runtime.listDescendants === guardedDescendants)
|
|
373
|
-
runtime.listDescendants = listDescendants;
|
|
374
346
|
if (runtime.followup === guardedFollowup)
|
|
375
347
|
runtime.followup = followup;
|
|
376
348
|
};
|
|
377
349
|
}, 'agent-teams: retired member guard');
|
|
378
350
|
}
|
|
379
351
|
/**
|
|
380
|
-
* Snapshot
|
|
381
|
-
*
|
|
382
|
-
*
|
|
383
|
-
*
|
|
384
|
-
*
|
|
385
|
-
*
|
|
386
|
-
*
|
|
352
|
+
* Snapshot the real driver activity for durable member ids.
|
|
353
|
+
*
|
|
354
|
+
* The team record is the membership authority, so this path intentionally no
|
|
355
|
+
* longer depends on `listChildren()`'s versioned projection shape. Harness
|
|
356
|
+
* rc.8 changed those rows to branded `SessionId` values plus residency-only
|
|
357
|
+
* `activity`; neither is needed to answer whether the live Agent driver is
|
|
358
|
+
* running, idle, or absent/ready.
|
|
359
|
+
* @param ctx - the plugin context (injects `agents`).
|
|
360
|
+
* @param memberIds - child ids restored from the durable team record.
|
|
361
|
+
* @returns child id → live activity.
|
|
387
362
|
*/
|
|
388
|
-
export
|
|
389
|
-
const entries = await ctx.subagents.listChildren(brandedSessionId(captainSessionId));
|
|
363
|
+
export function memberActivity(ctx, memberIds) {
|
|
390
364
|
const activity = new Map();
|
|
391
|
-
for (const
|
|
392
|
-
if (
|
|
365
|
+
for (const id of memberIds) {
|
|
366
|
+
if (id === '')
|
|
393
367
|
continue;
|
|
394
|
-
const live = ctx.agents.get(
|
|
395
|
-
activity.set(
|
|
368
|
+
const live = ctx.agents.get(brandedSessionId(id));
|
|
369
|
+
activity.set(id, live === undefined ? 'ready' : live.status);
|
|
396
370
|
}
|
|
397
371
|
return activity;
|
|
398
372
|
}
|
package/lib/snapshot.js
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import { readdir } from 'node:fs/promises';
|
|
11
11
|
import { join } from 'node:path';
|
|
12
|
+
import { memberActivity } from "./members.js";
|
|
12
13
|
import { CAPTAIN_KEY, listArchivedTeamIds, readArchivedTeam, readUnreadMailbox, readTeam, taskDepthsById, taskVisualState, } from "./state.js";
|
|
13
14
|
/** The current task of a member: its first unfinished owned task. */
|
|
14
15
|
function currentTaskOf(memberName, tasks) {
|
|
@@ -32,21 +33,9 @@ export async function assembleTeamSnapshot(ctx, stateRoot, workspace, state, opt
|
|
|
32
33
|
const roster = options.includeRemoved === true
|
|
33
34
|
? state.members
|
|
34
35
|
: state.members.filter((member) => member.status !== 'removed');
|
|
35
|
-
const activity =
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const children = await ctx.subagents.listChildren(state.captainSessionId);
|
|
39
|
-
for (const entry of children) {
|
|
40
|
-
if (entry.kind === 'child') {
|
|
41
|
-
const live = ctx.agents.get(entry.id);
|
|
42
|
-
activity.set(entry.id, live === undefined ? 'ready' : live.status);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
catch (error) {
|
|
47
|
-
ctx.logger.warn(`agent-teams: activity listing failed for ${state.name}: ${String(error)}`);
|
|
48
|
-
}
|
|
49
|
-
}
|
|
36
|
+
const activity = options.historic === true
|
|
37
|
+
? new Map()
|
|
38
|
+
: memberActivity(ctx, roster.map((member) => member.id));
|
|
50
39
|
const unreadByMember = new Map();
|
|
51
40
|
for (const member of roster) {
|
|
52
41
|
try {
|
package/lib/tools.js
CHANGED
|
@@ -871,7 +871,7 @@ export function registerAgentTeamsTools(ctx, config) {
|
|
|
871
871
|
await scheduler.kickTeam(workspace, located.id, caller);
|
|
872
872
|
}
|
|
873
873
|
const { team, identity } = await withTeamLock(teamLockKey(stateRoot, located.id), () => requireFreshParticipant(stateRoot, located.id, caller.id));
|
|
874
|
-
const activity =
|
|
874
|
+
const activity = memberActivity(ctx, team.members.map((member) => member.id));
|
|
875
875
|
const members = team.members
|
|
876
876
|
.filter((member) => member.status !== 'removed')
|
|
877
877
|
.map((member) => ({
|
|
@@ -21,7 +21,7 @@ import type { ObservableSnapshot, SessionListState } from '@deepseek-ai/dsh-clie
|
|
|
21
21
|
/** The top-right activity floater. Teams follow the current session: live
|
|
22
22
|
* snapshots and historic card summaries are only shown while their captain
|
|
23
23
|
* session is the one currently open. */
|
|
24
|
-
export declare function ActivityPanel({ sessionsList,
|
|
24
|
+
export declare function ActivityPanel({ sessionsList, openMember }: {
|
|
25
25
|
readonly sessionsList: ObservableSnapshot<SessionListState>;
|
|
26
|
-
readonly
|
|
26
|
+
readonly openMember: (parentId: SessionId, childId: SessionId) => void;
|
|
27
27
|
}): import("react").JSX.Element | null;
|
|
@@ -15,9 +15,9 @@ import type { SessionId } from '@deepseek-ai/dsh-session/types';
|
|
|
15
15
|
export declare const OPEN_PANEL_EVENT = "agent-teams:open-panel";
|
|
16
16
|
/** Navigation action injected from the plugin's own SessionsService access. */
|
|
17
17
|
export interface AgentTeamsCardInjected {
|
|
18
|
-
readonly
|
|
18
|
+
readonly openMember: (parentId: SessionId, childId: SessionId) => void;
|
|
19
19
|
}
|
|
20
20
|
/** Complete keyed Chat renderer props. */
|
|
21
21
|
export type AgentTeamsCardProps = PropsRuntime<'conversation.chat.node', 'agent-teams'> & PropsLocale<'agentTeams'> & AgentTeamsCardInjected;
|
|
22
22
|
/** Render one durable team as a compact conversation card. */
|
|
23
|
-
export declare function AgentTeamsCard({ node,
|
|
23
|
+
export declare function AgentTeamsCard({ node, openMember, sessionId }: AgentTeamsCardProps): import("react").JSX.Element;
|
|
@@ -79,6 +79,12 @@ interface ActivityFetchResponse {
|
|
|
79
79
|
}
|
|
80
80
|
/** Injectable browser primitives used by the poll controller and its tests. */
|
|
81
81
|
export interface ActivityPollingRuntime {
|
|
82
|
+
/**
|
|
83
|
+
* Current captain session to discover after a cold client/host restart.
|
|
84
|
+
* This one-time scope restores teams whose older conversation log has no
|
|
85
|
+
* AgentTeams card capable of registering an explicit monitor target.
|
|
86
|
+
*/
|
|
87
|
+
readonly discoverySessionId?: string;
|
|
82
88
|
readonly fetchState?: (url: string, init: {
|
|
83
89
|
readonly cache: 'no-store';
|
|
84
90
|
readonly signal: AbortSignal;
|
|
@@ -98,9 +104,13 @@ export interface ActivityPollingController {
|
|
|
98
104
|
/**
|
|
99
105
|
* Start the single polling loop for the current session's requested targets.
|
|
100
106
|
*
|
|
101
|
-
* With
|
|
102
|
-
*
|
|
103
|
-
*
|
|
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.
|
|
104
114
|
*/
|
|
105
115
|
export declare function startActivityPolling(monitorTargets: readonly ActivityMonitorTarget[], runtime?: ActivityPollingRuntime): ActivityPollingController;
|
|
106
116
|
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Version-tolerant navigation into durable AgentTeams member transcripts. */
|
|
2
|
+
import type { SessionId, SubagentAddress } from '@deepseek-ai/dsh-client-runtime/client';
|
|
3
|
+
/** Narrow sessions-service face used by the activity panel and team card. */
|
|
4
|
+
export interface AgentTeamsSessionNavigator {
|
|
5
|
+
/** Legacy/ordinary session navigation. */
|
|
6
|
+
open(id: SessionId): void;
|
|
7
|
+
/** rc.8 addressed subagent navigation. */
|
|
8
|
+
openSubagent?(address: SubagentAddress): void;
|
|
9
|
+
/** Refresh the exact parent's durable direct-child catalog. */
|
|
10
|
+
refreshSubagents?(parentSessionId: SessionId): Promise<void>;
|
|
11
|
+
/** Reuse an address already retained by the client runtime when available. */
|
|
12
|
+
subagentAddress?(id: SessionId): SubagentAddress | undefined;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Open one member's persisted transcript.
|
|
16
|
+
*
|
|
17
|
+
* Harness rc.8 intentionally removed cold subagents from the ordinary session
|
|
18
|
+
* list. They must first be rediscovered in their parent's catalog, then opened
|
|
19
|
+
* with the exact parent/child/mode address. Older runtimes have only `open()`;
|
|
20
|
+
* the fallback preserves the plugin's rc.6 peer range.
|
|
21
|
+
*/
|
|
22
|
+
export declare function openAgentTeamMember(sessions: AgentTeamsSessionNavigator, parentSessionId: SessionId, childSessionId: SessionId): Promise<'subagent' | 'session'>;
|
package/lib/types/members.d.ts
CHANGED
|
@@ -123,20 +123,24 @@ export declare function interruptMember(ctx: Context, captain: Agent, childId: s
|
|
|
123
123
|
*
|
|
124
124
|
* Upstream `interrupt()` deliberately preserves continuable sessions and the
|
|
125
125
|
* upstream seam exposes no targeted forget/retire method. The durable
|
|
126
|
-
* AgentTeams index therefore
|
|
127
|
-
* retired
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
126
|
+
* AgentTeams index therefore rejects `followup()` before it can cold-resume a
|
|
127
|
+
* retired member. Catalog rows deliberately remain discoverable: Harness rc.8
|
|
128
|
+
* uses the direct-child catalog to authorize historical transcript reads and
|
|
129
|
+
* `openSubagent()`, so filtering those rows would make an archived member's
|
|
130
|
+
* persisted conversation inaccessible. Exact ids keep unrelated subagents
|
|
131
|
+
* untouched while the followup boundary still prevents further model turns.
|
|
131
132
|
*/
|
|
132
133
|
export declare function installRetiredMemberGuard(ctx: Context, stateDir: string): void;
|
|
133
134
|
/**
|
|
134
|
-
* Snapshot
|
|
135
|
-
*
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
139
|
-
*
|
|
140
|
-
*
|
|
135
|
+
* Snapshot the real driver activity for durable member ids.
|
|
136
|
+
*
|
|
137
|
+
* The team record is the membership authority, so this path intentionally no
|
|
138
|
+
* longer depends on `listChildren()`'s versioned projection shape. Harness
|
|
139
|
+
* rc.8 changed those rows to branded `SessionId` values plus residency-only
|
|
140
|
+
* `activity`; neither is needed to answer whether the live Agent driver is
|
|
141
|
+
* running, idle, or absent/ready.
|
|
142
|
+
* @param ctx - the plugin context (injects `agents`).
|
|
143
|
+
* @param memberIds - child ids restored from the durable team record.
|
|
144
|
+
* @returns child id → live activity.
|
|
141
145
|
*/
|
|
142
|
-
export declare function memberActivity(ctx: Context,
|
|
146
|
+
export declare function memberActivity(ctx: Context, memberIds: readonly string[]): Map<string, 'running' | 'idle' | 'ready'>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nanmicoder/dsh-agent-teams",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"description": "AgentTeams for DeepSeek Harness: multi-agent team collaboration (captain, members, tasks with dependencies, messaging) driven by natural language, with a tree monitor in the web GUI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/index.js",
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# AgentTeams v0.1.11
|
|
2
|
+
|
|
3
|
+
This release restores AgentTeams activity and member history after a DeepSeek Harness client or host restart. It fixes the rc.8 compatibility regressions reported in [#60](https://github.com/NanmiCoder/dsh-agent-teams/issues/60) and [#61](https://github.com/NanmiCoder/dsh-agent-teams/issues/61).
|
|
4
|
+
|
|
5
|
+
## Fixes
|
|
6
|
+
|
|
7
|
+
- **Cold-start activity recovery**: selecting a captain session now performs a lightweight live and archived team discovery pass, so older sessions without an AgentTeams conversation card restore their activity panel after restart.
|
|
8
|
+
- **Reliable member activity**: live `running / idle / ready` state is resolved from the durable team roster and Agent registry instead of depending on the versioned `listChildren()` projection shape.
|
|
9
|
+
- **Persistent member history**: retired AgentTeams members remain discoverable in the Harness subagent catalog for transcript review while direct follow-up remains blocked.
|
|
10
|
+
- **rc.8 transcript navigation**: activity-panel and conversation-card member links refresh the parent catalog and use addressed `openSubagent()` navigation, with a fallback for older Harness runtimes.
|
|
11
|
+
|
|
12
|
+
## Verification
|
|
13
|
+
|
|
14
|
+
- Reproduced the full captain/member/task/archive lifecycle in a real `/tmp` workspace on the Harness web client.
|
|
15
|
+
- Verified cold restart recovery without opening the built-in subagent catalog first.
|
|
16
|
+
- Verified archived member navigation and complete persisted task history.
|
|
17
|
+
- Passed type checking, production build, offline verification, lifecycle verification, and the eight-member complex stress suite.
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
dsh plugin --profile web add @nanmicoder/dsh-agent-teams
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
<details>
|
|
26
|
+
<summary><b>中文版本 / Chinese Version</b></summary>
|
|
27
|
+
|
|
28
|
+
# AgentTeams v0.1.11
|
|
29
|
+
|
|
30
|
+
本次更新修复 DeepSeek Harness 客户端或 host 重启后 AgentTeams 活动面板与成员历史消失的问题,对应 [#60](https://github.com/NanmiCoder/dsh-agent-teams/issues/60) 和 [#61](https://github.com/NanmiCoder/dsh-agent-teams/issues/61) 中报告的 rc.8 兼容性回归。
|
|
31
|
+
|
|
32
|
+
## 修复
|
|
33
|
+
|
|
34
|
+
- **冷启动活动恢复**:选择队长会话时会执行一次轻量的活动团队与历史归档发现;即使旧会话没有 AgentTeams 对话卡片,重启后也能恢复活动面板。
|
|
35
|
+
- **可靠的成员活动状态**:从持久化团队成员表和 Agent registry 解析真实的 `running / idle / ready`,不再依赖容易变化的 `listChildren()` 投影结构。
|
|
36
|
+
- **成员历史持续可见**:已退休的 AgentTeams 成员继续保留在 Harness 子代理目录中供历史记录查看,但直接 follow-up 仍会被拒绝。
|
|
37
|
+
- **rc.8 历史会话导航**:活动面板和对话卡片会先刷新父级目录,再通过带父子地址的 `openSubagent()` 打开成员记录;旧版 Harness 继续使用兼容回退。
|
|
38
|
+
|
|
39
|
+
## 验证
|
|
40
|
+
|
|
41
|
+
- 在 Harness Web 客户端的真实 `/tmp` workspace 中完成队长、成员、任务和归档全流程复现。
|
|
42
|
+
- 验证未预先打开内置子代理目录时的冷重启恢复。
|
|
43
|
+
- 验证归档成员导航及完整持久化任务历史。
|
|
44
|
+
- 通过类型检查、生产构建、离线验证、生命周期验证及八成员复杂压力测试。
|
|
45
|
+
|
|
46
|
+
## 安装
|
|
47
|
+
|
|
48
|
+
```sh
|
|
49
|
+
dsh plugin --profile web add @nanmicoder/dsh-agent-teams
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
</details>
|