@nanmicoder/dsh-agent-teams 0.1.11 → 0.1.13

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/index.js CHANGED
@@ -44,9 +44,9 @@ function usageSectionText(toolNames) {
44
44
  1. Call agent_teams_create with a team name and the goal as description. You become the captain and may lead one team at a time.
45
45
  2. Call agent_teams_add_member once per role the goal needs (researcher, engineer, reviewer, ...). Members are durable subagents: they wait for your messages, then work a full turn. By default a member on your current provider/model snapshots your current reasoning effort; a member routed to a different provider or model automatically uses that target model's default effort. Never ask the user to choose these per member; only pass provider/model when the user explicitly requests a different route for that role, and reasoning_effort only when the user explicitly requests a particular effort ("default" explicitly selects the target model's default).
46
46
  3. Break the goal into tasks with agent_teams_create_task and wire dependencies. Assign role-specific work when useful; unassigned ready work belongs to the shared pool. The scheduler automatically claims one ready task for each truly idle member and wakes it, including across later rounds.
47
- 4. Lead by delegation: monitor with agent_teams_status, send guidance with agent_teams_send_message, and let idle teammates execute ready work. Do not duplicate a teammate's work merely because its turn is slow.
48
- 5. If work is blocked, stale, or needs takeover, always call agent_teams_reassign_task first. Reassign to another idle member, or use assignee=captain before doing it yourself. Reassignment revokes the old attempt and waits for that member to quiesce, preventing late results from overwriting the new attempt.
49
- 6. Tasks carry attempt_id capabilities. Members must use the current attempt_id for updates; stale-attempt errors mean ownership changed. Poll status until every required task is terminal and every member is idle/ready.
47
+ 4. Lead by delegation: monitor with agent_teams_status, send guidance with agent_teams_send_message, and let idle teammates execute ready work. Do not duplicate a teammate's work merely because its turn is slow. If the user requires every member to contribute or report, create one task per required contribution (or message each member directly); never wait for an unassigned member to produce work it was never given.
48
+ 5. If the user explicitly asks to pause a running member, its open attempt remains parked after interruption; after answering the user, send that same member guidance with agent_teams_send_message so it continues the same attempt. Do not interrupt members for an ordinary user question that did not request a pause. If work must change owner, restart from scratch, or be taken over, call agent_teams_reassign_task first. Reassign to another idle member, retry with the same member, or use assignee=captain before doing it yourself. Reassignment revokes the old attempt and waits for that member to quiesce, preventing late results from overwriting the new attempt.
49
+ 6. Tasks carry attempt_id capabilities. Members must use the current attempt_id for updates; stale-attempt errors mean ownership changed. Check status after progress notifications until every required task is terminal and every member is idle/ready; do not busy-poll or require reports from members with no assigned work.
50
50
  7. Present the team's results to the user, then agent_teams_delete the team unless the user wants to keep working with it.
51
51
 
52
52
  Tools: ${toolNames}`;
package/lib/scheduler.js CHANGED
@@ -5,7 +5,10 @@
5
5
  * continuable agents instead expose explicit idle/running edges, so this
6
6
  * scheduler closes the same loop without keeping a polling turn alive: every
7
7
  * idle edge and every task-graph mutation attempts one atomic claim and wakes
8
- * the selected durable member.
8
+ * the selected durable member. A resident member that becomes idle while it
9
+ * still owns an open attempt is parked: only an explicit captain reassignment
10
+ * may rotate that capability. Automatic retry is reserved for cold recovery,
11
+ * when the durable owner is no longer resident in the live Agent registry.
9
12
  * @module dsh-agent-teams/scheduler
10
13
  */
11
14
  import { join } from 'node:path';
@@ -22,8 +25,11 @@ function liveCaptain(ctx, captainSessionId, supplied) {
22
25
  return supplied;
23
26
  return ctx.agents.get(captainSessionId);
24
27
  }
28
+ function liveMember(ctx, member) {
29
+ return ctx.agents.get(member.id);
30
+ }
25
31
  function isMemberAvailable(ctx, member) {
26
- const live = ctx.agents.get(member.id);
32
+ const live = liveMember(ctx, member);
27
33
  return live === undefined || live.status === 'idle';
28
34
  }
29
35
  function ownedOpenTask(tasks, memberName) {
@@ -59,6 +65,13 @@ function fallbackMailboxPrompt(messages) {
59
65
  /** Install one scheduler and its member activity observer. */
60
66
  export function installTeamScheduler(ctx, config) {
61
67
  const memberQueues = new Map();
68
+ // An idle edge in this process proves that the resident member ended its
69
+ // turn while the current attempt was still open. Remember that capability
70
+ // even after Harness disposes the continuable AgentHandle: later status or
71
+ // graph kicks must keep it parked. A cold process starts with an empty map,
72
+ // so durable open attempts are still recovered after restart.
73
+ const parkedAttempts = new Map();
74
+ const memberQueueKey = (stateRoot, teamId, memberName) => (`${stateRoot}\u0000${teamId}\u0000${memberName}`);
62
75
  const serializeMember = async (key, operation) => {
63
76
  const previous = memberQueues.get(key) ?? Promise.resolve();
64
77
  let release;
@@ -92,7 +105,7 @@ export function installTeamScheduler(ctx, config) {
92
105
  },
93
106
  async kickMember(workspace, teamId, memberName, suppliedCaptain) {
94
107
  const stateRoot = stateRootOf(workspace, config);
95
- const queueKey = `${stateRoot}\u0000${teamId}\u0000${memberName}`;
108
+ const queueKey = memberQueueKey(stateRoot, teamId, memberName);
96
109
  await serializeMember(queueKey, async () => {
97
110
  let team = await readTeam(stateRoot, teamId);
98
111
  if (team === undefined)
@@ -124,12 +137,20 @@ export function installTeamScheduler(ctx, config) {
124
137
  const currentMember = fresh.members.find(candidate => candidate.name === memberName && candidate.status !== 'removed');
125
138
  if (currentMember === undefined || currentMember.id === '' || !isMemberAvailable(ctx, currentMember))
126
139
  return undefined;
127
- // An idle/ready member that still owns an open task lost the turn
128
- // that was executing it (model stopped early, interrupt settlement,
129
- // or process restart). Retry that task with a fresh capability
130
- // instead of permanently treating the durable claim as "busy".
131
- const task = ownedOpenTask(fresh.tasks, currentMember.name)
132
- ?? nextReadyTask(fresh.tasks, currentMember.name);
140
+ const owned = ownedOpenTask(fresh.tasks, currentMember.name);
141
+ // A resident idle member can intentionally leave an attempt open
142
+ // while waiting for guidance, or because the user paused its turn.
143
+ // Re-dispatching here would revoke still-valid work on every idle
144
+ // edge and every status kick. The idle observer remembers that exact
145
+ // capability across normal continuable disposal; only an unobserved
146
+ // durable capability (cold process recovery) or a legacy open task
147
+ // with no capability is retried.
148
+ const parkedAttemptId = parkedAttempts.get(currentMember.id);
149
+ const recoverOwned = owned !== undefined
150
+ && (owned.attemptId === undefined || owned.attemptId !== parkedAttemptId);
151
+ const task = recoverOwned ? owned : owned === undefined
152
+ ? nextReadyTask(fresh.tasks, currentMember.name)
153
+ : undefined;
133
154
  if (task === undefined) {
134
155
  if (currentMember.status !== 'idle') {
135
156
  currentMember.status = 'idle';
@@ -139,6 +160,7 @@ export function installTeamScheduler(ctx, config) {
139
160
  }
140
161
  const previousAssignee = task.assignee;
141
162
  const attemptId = beginTaskAttempt(task, currentMember.name);
163
+ parkedAttempts.delete(currentMember.id);
142
164
  currentMember.status = 'working';
143
165
  await writeTeam(stateRoot, fresh);
144
166
  return {
@@ -184,17 +206,33 @@ export function installTeamScheduler(ctx, config) {
184
206
  const workspace = agent.session.header.cwd ?? process.cwd();
185
207
  const stateRoot = stateRootOf(workspace, config);
186
208
  const located = await findTeamByParticipant(stateRoot, agent.id);
187
- if (located === undefined || located.captainSessionId === agent.id)
209
+ if (located === undefined) {
210
+ parkedAttempts.delete(agent.id);
211
+ return;
212
+ }
213
+ if (located.captainSessionId === agent.id)
188
214
  return;
189
215
  const member = located.members.find(candidate => candidate.id === agent.id && candidate.status !== 'removed');
190
- if (member === undefined)
216
+ if (member === undefined) {
217
+ parkedAttempts.delete(agent.id);
191
218
  return;
219
+ }
192
220
  await withTeamLock(teamLockKey(stateRoot, located.id), async () => {
193
221
  const fresh = await readTeam(stateRoot, located.id);
194
222
  const current = fresh?.members.find(candidate => candidate.id === agent.id && candidate.status !== 'removed');
195
223
  if (fresh === undefined || current === undefined)
196
224
  return;
197
225
  const next = status === 'running' ? 'working' : 'idle';
226
+ if (next === 'idle') {
227
+ const owned = ownedOpenTask(fresh.tasks, current.name);
228
+ if (owned?.attemptId === undefined)
229
+ parkedAttempts.delete(agent.id);
230
+ else
231
+ parkedAttempts.set(agent.id, owned.attemptId);
232
+ }
233
+ else {
234
+ parkedAttempts.delete(agent.id);
235
+ }
198
236
  if (current.status === next)
199
237
  return;
200
238
  current.status = next;
@@ -16,12 +16,14 @@
16
16
  * always-available monitor.
17
17
  * @module dsh-agent-teams/client/activity
18
18
  */
19
+ import type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
19
20
  import type { SessionId } from '@deepseek-ai/dsh-session/types';
20
21
  import type { ObservableSnapshot, SessionListState } from '@deepseek-ai/dsh-client-runtime/client';
21
22
  /** The top-right activity floater. Teams follow the current session: live
22
23
  * snapshots and historic card summaries are only shown while their captain
23
24
  * session is the one currently open. */
24
- export declare function ActivityPanel({ sessionsList, openMember }: {
25
+ export type ActivityPanelProps = {
25
26
  readonly sessionsList: ObservableSnapshot<SessionListState>;
26
27
  readonly openMember: (parentId: SessionId, childId: SessionId) => void;
27
- }): import("react").JSX.Element | null;
28
+ } & PropsLocale<'agentTeams'>;
29
+ export declare function ActivityPanel({ sessionsList, openMember, t }: ActivityPanelProps): import("react").JSX.Element | null;
@@ -20,4 +20,4 @@ export interface AgentTeamsCardInjected {
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, openMember, sessionId }: AgentTeamsCardProps): import("react").JSX.Element;
23
+ export declare function AgentTeamsCard({ node, openMember, sessionId, t }: AgentTeamsCardProps): import("react").JSX.Element;
@@ -71,6 +71,13 @@ export declare function getActivitySnapshotsSnapshot(): ActivitySnapshots;
71
71
  export declare function updateActivitySnapshots(update: Partial<ActivitySnapshots>): void;
72
72
  /** Poll cadence for the live host snapshot route. */
73
73
  export declare const ACTIVITY_POLL_MS = 1000;
74
+ /**
75
+ * Low-frequency probe cadence while a cardless discovery session still owns
76
+ * no team. The probe keeps the panel able to pick up a team created later in
77
+ * that session (e.g. a run_code-wrapped agent_teams_create) without turning
78
+ * every ordinary session into a one-second filesystem scan.
79
+ */
80
+ export declare const ACTIVITY_PROBE_MS = 5000;
74
81
  /** Host route serving live and archived team snapshots. */
75
82
  export declare const ACTIVITY_STATE_URL = "/plugins/dsh-agent-teams/state";
76
83
  interface ActivityFetchResponse {
@@ -105,12 +112,17 @@ export interface ActivityPollingController {
105
112
  * Start the single polling loop for the current session's requested targets.
106
113
  *
107
114
  * 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.
115
+ * Explicit card targets poll at the live cadence from the start. A discovery
116
+ * session performs an immediate live+archive restore pass, then while it
117
+ * still owns no team probes on a low-frequency cadence, so a team created
118
+ * later in that session (e.g. a run_code-wrapped agent_teams_create) is
119
+ * discovered without a manual reload, without turning every ordinary session
120
+ * into a one-second filesystem scan. The moment a team for the discovery
121
+ * session appears, the controller upgrades to the live one-second cadence for
122
+ * the rest of its lifetime. The caller — the session view, which stops the
123
+ * controller when the session is no longer current — bounds the lifetime, and
124
+ * archive state is refreshed when a target or a previously discovered live
125
+ * team disappears.
114
126
  */
115
127
  export declare function startActivityPolling(monitorTargets: readonly ActivityMonitorTarget[], runtime?: ActivityPollingRuntime): ActivityPollingController;
116
128
  export {};
@@ -1,6 +1,13 @@
1
1
  /** Browser plugin for the AgentTeams activity floater and conversation card. */
2
2
  import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
3
- /** Required services: conversation nodes, slots, and sessions navigation. */
3
+ import { type AgentTeamsLocaleKey } from './locales.ts';
4
+ declare module '@deepseek-ai/dsh-client-ui-slots' {
5
+ interface LocaleNamespaceMap {
6
+ /** AgentTeams conversation card and activity monitor copy. */
7
+ agentTeams: AgentTeamsLocaleKey;
8
+ }
9
+ }
10
+ /** Required services: conversation nodes, slots, sessions navigation, and locale. */
4
11
  export declare const inject: string[];
5
12
  /**
6
13
  * Register the activity monitor in the shell's additive overlay and the
@@ -0,0 +1,169 @@
1
+ /** `agentTeams` namespace dictionaries for every plugin-owned Web surface. */
2
+ /** Dictionary namespace owned by the AgentTeams client plugin. */
3
+ export declare const AGENT_TEAMS_LOCALE_NAMESPACE = "agentTeams";
4
+ /** Simplified Chinese dictionary (the key-set source of truth). */
5
+ export declare const zh: {
6
+ 'card.memberCount': string;
7
+ 'action.openActivityPanel': string;
8
+ 'activity.panelButton': string;
9
+ 'activity.badgeAria': string;
10
+ 'activity.panelAria': string;
11
+ 'activity.title': string;
12
+ 'activity.float': string;
13
+ 'activity.dockRight': string;
14
+ 'activity.collapse': string;
15
+ 'activity.empty': string;
16
+ 'format.listSeparator': string;
17
+ 'task.status.pending': string;
18
+ 'task.status.claimed': string;
19
+ 'task.status.inProgress': string;
20
+ 'task.status.completed': string;
21
+ 'task.status.failed': string;
22
+ 'task.status.cancelled': string;
23
+ 'member.state.working': string;
24
+ 'member.state.failed': string;
25
+ 'member.state.waiting': string;
26
+ 'member.state.delivered': string;
27
+ 'member.state.left': string;
28
+ 'member.state.removed': string;
29
+ 'member.state.pending': string;
30
+ 'member.state.unassigned': string;
31
+ 'member.status.executing': string;
32
+ 'member.status.working': string;
33
+ 'member.status.waitingOn': string;
34
+ 'member.status.waitingPrerequisite': string;
35
+ 'member.status.waitingAssignment': string;
36
+ 'member.status.delivered': string;
37
+ 'member.status.idle': string;
38
+ 'member.status.unknown': string;
39
+ 'task.assignee.unclaimed': string;
40
+ 'task.summary.waitingBreakdown': string;
41
+ 'task.summary.allDelivered': string;
42
+ 'task.summary.blockedAndRunning': string;
43
+ 'task.summary.more': string;
44
+ 'task.summary.running': string;
45
+ 'task.summary.ready': string;
46
+ 'task.summary.blocked': string;
47
+ 'task.summary.waitingSchedule': string;
48
+ 'progress.aria': string;
49
+ 'progress.title': string;
50
+ 'progress.running': string;
51
+ 'progress.blocked': string;
52
+ 'progress.delivered': string;
53
+ 'dependency.aria': string;
54
+ 'dependency.parallel': string;
55
+ 'dependency.title': string;
56
+ 'dependency.hint.parallel': string;
57
+ 'dependency.hint.chain': string;
58
+ 'dependency.hint.pinned': string;
59
+ 'task.runningAria': string;
60
+ 'task.detail.completed': string;
61
+ 'task.detail.noPrerequisite': string;
62
+ 'task.detail.ready': string;
63
+ 'task.detail.waitingOn': string;
64
+ 'task.detail.noDownstream': string;
65
+ 'task.detail.unlocks': string;
66
+ 'team.ended': string;
67
+ 'team.stats.members': string;
68
+ 'team.stats.completed': string;
69
+ 'team.stats.messages': string;
70
+ 'delegation.aria': string;
71
+ 'captain.name': string;
72
+ 'captain.role': string;
73
+ 'captain.summary': string;
74
+ 'captain.state.working': string;
75
+ 'captain.state.collected': string;
76
+ 'captain.state.waiting': string;
77
+ 'members.toggle': string;
78
+ 'members.collapse': string;
79
+ 'members.expand': string;
80
+ 'members.empty': string;
81
+ 'assignment.label': string;
82
+ 'assignment.empty': string;
83
+ 'archive.label': string;
84
+ };
85
+ /** AgentTeams namespace key union. */
86
+ export type AgentTeamsLocaleKey = keyof typeof zh;
87
+ /** English dictionary, checked complete against the Chinese source key set. */
88
+ export declare const en: {
89
+ 'card.memberCount': string;
90
+ 'action.openActivityPanel': string;
91
+ 'activity.panelButton': string;
92
+ 'activity.badgeAria': string;
93
+ 'activity.panelAria': string;
94
+ 'activity.title': string;
95
+ 'activity.float': string;
96
+ 'activity.dockRight': string;
97
+ 'activity.collapse': string;
98
+ 'activity.empty': string;
99
+ 'format.listSeparator': string;
100
+ 'task.status.pending': string;
101
+ 'task.status.claimed': string;
102
+ 'task.status.inProgress': string;
103
+ 'task.status.completed': string;
104
+ 'task.status.failed': string;
105
+ 'task.status.cancelled': string;
106
+ 'member.state.working': string;
107
+ 'member.state.failed': string;
108
+ 'member.state.waiting': string;
109
+ 'member.state.delivered': string;
110
+ 'member.state.left': string;
111
+ 'member.state.removed': string;
112
+ 'member.state.pending': string;
113
+ 'member.state.unassigned': string;
114
+ 'member.status.executing': string;
115
+ 'member.status.working': string;
116
+ 'member.status.waitingOn': string;
117
+ 'member.status.waitingPrerequisite': string;
118
+ 'member.status.waitingAssignment': string;
119
+ 'member.status.delivered': string;
120
+ 'member.status.idle': string;
121
+ 'member.status.unknown': string;
122
+ 'task.assignee.unclaimed': string;
123
+ 'task.summary.waitingBreakdown': string;
124
+ 'task.summary.allDelivered': string;
125
+ 'task.summary.blockedAndRunning': string;
126
+ 'task.summary.more': string;
127
+ 'task.summary.running': string;
128
+ 'task.summary.ready': string;
129
+ 'task.summary.blocked': string;
130
+ 'task.summary.waitingSchedule': string;
131
+ 'progress.aria': string;
132
+ 'progress.title': string;
133
+ 'progress.running': string;
134
+ 'progress.blocked': string;
135
+ 'progress.delivered': string;
136
+ 'dependency.aria': string;
137
+ 'dependency.parallel': string;
138
+ 'dependency.title': string;
139
+ 'dependency.hint.parallel': string;
140
+ 'dependency.hint.chain': string;
141
+ 'dependency.hint.pinned': string;
142
+ 'task.runningAria': string;
143
+ 'task.detail.completed': string;
144
+ 'task.detail.noPrerequisite': string;
145
+ 'task.detail.ready': string;
146
+ 'task.detail.waitingOn': string;
147
+ 'task.detail.noDownstream': string;
148
+ 'task.detail.unlocks': string;
149
+ 'team.ended': string;
150
+ 'team.stats.members': string;
151
+ 'team.stats.completed': string;
152
+ 'team.stats.messages': string;
153
+ 'delegation.aria': string;
154
+ 'captain.name': string;
155
+ 'captain.role': string;
156
+ 'captain.summary': string;
157
+ 'captain.state.working': string;
158
+ 'captain.state.collected': string;
159
+ 'captain.state.waiting': string;
160
+ 'members.toggle': string;
161
+ 'members.collapse': string;
162
+ 'members.expand': string;
163
+ 'members.empty': string;
164
+ 'assignment.label': string;
165
+ 'assignment.empty': string;
166
+ 'archive.label': string;
167
+ };
168
+ /** Translation function consumed by pure view helpers. */
169
+ export type AgentTeamsTranslate = (key: AgentTeamsLocaleKey, params?: Record<string, unknown>) => string;
@@ -5,7 +5,10 @@
5
5
  * continuable agents instead expose explicit idle/running edges, so this
6
6
  * scheduler closes the same loop without keeping a polling turn alive: every
7
7
  * idle edge and every task-graph mutation attempts one atomic claim and wakes
8
- * the selected durable member.
8
+ * the selected durable member. A resident member that becomes idle while it
9
+ * still owns an open attempt is parked: only an explicit captain reassignment
10
+ * may rotate that capability. Automatic retry is reserved for cold recovery,
11
+ * when the durable owner is no longer resident in the live Agent registry.
9
12
  * @module dsh-agent-teams/scheduler
10
13
  */
11
14
  import type { Context } from '@deepseek-ai/cordis';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nanmicoder/dsh-agent-teams",
3
- "version": "0.1.11",
3
+ "version": "0.1.13",
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,54 @@
1
+ # AgentTeams v0.1.12
2
+
3
+ This release adds host-native English and Simplified Chinese localization to the AgentTeams Web UI.
4
+
5
+ ## New & Improved
6
+
7
+ - **Official Harness locale integration**: the plugin registers its own `agentTeams` namespace with the Harness locale service instead of inspecting or rewriting host DOM.
8
+ - **Complete localized Web surfaces**: the conversation card and activity panel now translate task and member states, dynamic summaries, progress labels, dependency guidance, archive markers, controls, and accessibility text.
9
+ - **Live language switching**: changing the Harness language updates the active AgentTeams card and panel immediately without a page reload or a separate plugin setting.
10
+ - **Consistent fallback behavior**: English remains the fallback for missing locale entries, and dictionary keys and placeholders are verified as a release contract.
11
+
12
+ The `/agent-teams` slash-command description and input hint remain stable English metadata because the current Harness command protocol does not expose a locale namespace.
13
+
14
+ ## Verification
15
+
16
+ - Ran a real DeepSeek API workflow in an isolated `/tmp` Git project with one captain, two members, two dependent tasks, durable messaging, generated artifacts, and a successful project verification command.
17
+ - Verified English → Chinese → English live switching in the real Harness Web UI, including panel folding, dock/floating mode, task dependencies, and member transcript navigation.
18
+ - Passed type checking, production build, offline verification, lifecycle verification, the eight-member complex stress suite, and the Skill mirror check.
19
+
20
+ ## Installation
21
+
22
+ ```sh
23
+ dsh plugin --profile web add @nanmicoder/dsh-agent-teams
24
+ ```
25
+
26
+ <details>
27
+ <summary><b>中文版本 / Chinese Version</b></summary>
28
+
29
+ # AgentTeams v0.1.12
30
+
31
+ 本次更新为 AgentTeams Web UI 增加基于宿主官方能力的简体中文与英文多语言支持。
32
+
33
+ ## 新增与改进
34
+
35
+ - **接入 Harness 官方多语言服务**:插件注册独立的 `agentTeams` 命名空间,不读取或改写宿主 DOM。
36
+ - **完整覆盖 Web 界面**:对话卡片与活动面板中的任务/成员状态、动态摘要、进度、依赖提示、归档标识、操作按钮及无障碍文案均已适配。
37
+ - **语言实时切换**:修改 Harness 语言后,当前 AgentTeams 卡片和面板无需刷新即可同步更新,也不需要插件自己的语言设置。
38
+ - **稳定回退与发布校验**:缺失文案按官方机制回退到英文,发布验证会检查中英文字典的键和模板参数保持一致。
39
+
40
+ 由于当前 Harness 命令协议尚未提供 locale namespace,`/agent-teams` 在 slash 菜单中的描述和输入提示暂时保留稳定的英文元数据。
41
+
42
+ ## 验证
43
+
44
+ - 在隔离的 `/tmp` Git 项目中使用真实 DeepSeek API 完成队长、两名成员、两个依赖任务、持久消息、真实产物及项目校验的完整链路。
45
+ - 在真实 Harness Web UI 中验证英文 → 中文 → 英文实时切换,以及面板收起、停靠/浮动、任务依赖和成员记录跳转。
46
+ - 通过类型检查、生产构建、离线验证、生命周期验证、八成员复杂压力测试及 Skill 镜像检查。
47
+
48
+ ## 安装
49
+
50
+ ```sh
51
+ dsh plugin --profile web add @nanmicoder/dsh-agent-teams
52
+ ```
53
+
54
+ </details>
@@ -0,0 +1,60 @@
1
+ # AgentTeams v0.1.13
2
+
3
+ This release makes long-running AgentTeams sessions calmer, more predictable, and easier to observe.
4
+
5
+ ## Fixed & Improved
6
+
7
+ - **No retry storms while checking progress**: an idle member with an open task attempt now stays parked instead of restarting the same work whenever the captain checks status.
8
+ - **Explicit pause and resume**: a captain message resumes a parked member with the same task capability, while reassignment still revokes the previous attempt safely.
9
+ - **Activity panel discovery**: teams created after the first page discovery pass appear without a manual reload.
10
+ - **Lower idle overhead**: ordinary cardless sessions probe every five seconds and upgrade to the one-second live cadence only after a team is discovered.
11
+ - **Clearer delegation guidance**: the captain is instructed to create work for every required contributor and to avoid busy polling or waiting on unassigned members.
12
+
13
+ ## Verification
14
+
15
+ - Passed production build, offline verification, lifecycle verification, the eight-member complex stress suite, and the Skill mirror check.
16
+ - Ran three independent four-member workflows with real DeepSeek models in a `/tmp` workspace; all assigned tasks completed in all three runs.
17
+ - Verified parked-member resume, task dependency gates, member-to-member reassignment, captain takeover, archive readback, live language switching, panel persistence, and idle polling cadence in the real Harness Web UI.
18
+
19
+ ## Known Limitation
20
+
21
+ - After a hard DSH restart, the team, members, tasks, and dependency graph are restored, but unfinished members may remain in **Ready to continue** instead of resuming automatically. The captain can explicitly resume or reassign them; automatic cold-restart continuation will be addressed separately.
22
+
23
+ ## Installation
24
+
25
+ ```sh
26
+ dsh plugin --profile web add @nanmicoder/dsh-agent-teams
27
+ ```
28
+
29
+ <details>
30
+ <summary><b>中文版本 / Chinese Version</b></summary>
31
+
32
+ # AgentTeams v0.1.13
33
+
34
+ 本次更新让长时间运行的 AgentTeams 协作更安静、更可预测,也更容易观察。
35
+
36
+ ## 修复与改进
37
+
38
+ - **查询进度不再触发重复工作**:成员空闲但仍持有未完成任务时会保持暂停,不会因为队长查询状态而重新执行同一件事。
39
+ - **明确的暂停与继续语义**:队长发消息可让暂停成员沿用原任务能力继续执行;任务转派仍会安全撤销旧 attempt。
40
+ - **活动面板自动发现团队**:首次页面探测之后才创建的团队,也能自动出现在面板中,无需手动刷新。
41
+ - **降低普通会话开销**:没有团队卡片的普通会话每 5 秒低频探测一次,发现团队后才升级为每秒实时轮询。
42
+ - **更清晰的派工指导**:要求每位必须参与的成员都有明确任务,并避免高频查询或等待未分配工作的成员。
43
+
44
+ ## 验证
45
+
46
+ - 通过生产构建、离线验证、生命周期验证、八成员复杂压力测试及 Skill 镜像检查。
47
+ - 在 `/tmp` workspace 中使用真实 DeepSeek 模型连续运行 3 轮独立四人团队流程,三轮所有已分配任务均完成。
48
+ - 在真实 Harness Web UI 中验证成员暂停继续、任务依赖、成员间转派、队长接管、归档读取、语言实时切换、面板状态保持及普通会话轮询频率。
49
+
50
+ ## 已知限制
51
+
52
+ - DSH 硬重启后,团队、成员、任务和依赖图能够恢复,但未完成成员可能停在“待继续执行”,不会自动恢复工作。队长可以显式继续或转派;冷重启自动续跑将在后续版本单独处理。
53
+
54
+ ## 安装
55
+
56
+ ```sh
57
+ dsh plugin --profile web add @nanmicoder/dsh-agent-teams
58
+ ```
59
+
60
+ </details>