@nanmicoder/dsh-agent-teams 0.1.12 → 0.1.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +43 -7
  2. package/README_ZH.md +20 -7
  3. package/lib/client/ActivityPanel.js +219 -50
  4. package/lib/client/StagingPlanEditor.js +493 -0
  5. package/lib/client/activity-model.js +71 -0
  6. package/lib/client/activity-monitor.js +39 -13
  7. package/lib/client/index.js +2 -2
  8. package/lib/client/locales.js +224 -2
  9. package/lib/client.js +1808 -250
  10. package/lib/client.js.map +1 -1
  11. package/lib/command.js +116 -99
  12. package/lib/index.js +286 -14
  13. package/lib/members.js +137 -16
  14. package/lib/profiles.js +572 -0
  15. package/lib/quality-gates.js +777 -0
  16. package/lib/scheduler.js +215 -18
  17. package/lib/snapshot.js +25 -1
  18. package/lib/state.js +116 -10
  19. package/lib/tools.js +1230 -38
  20. package/lib/types/client/ActivityPanel.d.ts +3 -1
  21. package/lib/types/client/StagingPlanEditor.d.ts +17 -0
  22. package/lib/types/client/activity-model.d.ts +67 -0
  23. package/lib/types/client/activity-monitor.d.ts +32 -7
  24. package/lib/types/client/locales.d.ts +222 -0
  25. package/lib/types/command.d.ts +11 -56
  26. package/lib/types/event-types.d.ts +35 -1
  27. package/lib/types/index.d.ts +9 -0
  28. package/lib/types/members.d.ts +48 -3
  29. package/lib/types/profiles.d.ts +124 -0
  30. package/lib/types/quality-gates.d.ts +148 -0
  31. package/lib/types/scheduler.d.ts +48 -1
  32. package/lib/types/snapshot.d.ts +18 -1
  33. package/lib/types/state.d.ts +8 -3
  34. package/lib/types/tools.d.ts +73 -9
  35. package/lib/types/types.d.ts +118 -0
  36. package/lib/types.js +11 -0
  37. package/package.json +10 -4
  38. package/release-notes/v0.1.13.md +60 -0
  39. package/release-notes/v0.1.14.md +68 -0
@@ -99,18 +99,31 @@ export function updateActivitySnapshots(update) {
99
99
  }
100
100
  /** Poll cadence for the live host snapshot route. */
101
101
  export const ACTIVITY_POLL_MS = 1000;
102
+ /**
103
+ * Low-frequency probe cadence while a cardless discovery session still owns
104
+ * no team. The probe keeps the panel able to pick up a team created later in
105
+ * that session (e.g. a run_code-wrapped agent_teams_create) without turning
106
+ * every ordinary session into a one-second filesystem scan.
107
+ */
108
+ export const ACTIVITY_PROBE_MS = 5000;
102
109
  /** Host route serving live and archived team snapshots. */
103
110
  export const ACTIVITY_STATE_URL = '/plugins/dsh-agent-teams/state';
111
+ export const ACTIVITY_HALT_URL = '/plugins/dsh-agent-teams/halt';
104
112
  /**
105
113
  * Start the single polling loop for the current session's requested targets.
106
114
  *
107
115
  * 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.
116
+ * Explicit card targets poll at the live cadence from the start. A discovery
117
+ * session performs an immediate live+archive restore pass, then while it
118
+ * still owns no team probes on a low-frequency cadence, so a team created
119
+ * later in that session (e.g. a run_code-wrapped agent_teams_create) is
120
+ * discovered without a manual reload, without turning every ordinary session
121
+ * into a one-second filesystem scan. The moment a team for the discovery
122
+ * session appears, the controller upgrades to the live one-second cadence for
123
+ * the rest of its lifetime. The caller — the session view, which stops the
124
+ * controller when the session is no longer current — bounds the lifetime, and
125
+ * archive state is refreshed when a target or a previously discovered live
126
+ * team disappears.
114
127
  */
115
128
  export function startActivityPolling(monitorTargets, runtime = {}) {
116
129
  const discoverySessionId = runtime.discoverySessionId?.trim();
@@ -124,16 +137,21 @@ export function startActivityPolling(monitorTargets, runtime = {}) {
124
137
  const settleTargets = runtime.settleTargets ?? settleActivityMonitorTargets;
125
138
  let cancelled = false;
126
139
  let inFlight = false;
140
+ // Explicit card targets are demanded work: start at the live cadence. A
141
+ // discovery session starts probing low-frequency and upgrades on detection.
142
+ let hot = monitorTargets.length > 0;
127
143
  let discoveryComplete = false;
128
144
  let discoveredLiveKeys = new Set();
129
145
  let controller;
146
+ let timer;
147
+ const intervalMs = () => (hot ? ACTIVITY_POLL_MS : ACTIVITY_PROBE_MS);
148
+ const reschedule = () => {
149
+ cancel(timer);
150
+ timer = schedule(() => { void tick(); }, intervalMs());
151
+ };
130
152
  const tick = async () => {
131
153
  if (inFlight || cancelled)
132
154
  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;
137
155
  inFlight = true;
138
156
  controller = new AbortController();
139
157
  try {
@@ -154,6 +172,12 @@ export function startActivityPolling(monitorTargets, runtime = {}) {
154
172
  : liveTeams
155
173
  .filter((team) => team.captainSessionId === discoverySessionId)
156
174
  .map((team) => team.teamId));
175
+ // A discovery session found its first team: upgrade from the low-frequency
176
+ // probe to the live cadence for the rest of the controller lifetime.
177
+ if (!hot && discoveredLiveKeys.size > 0) {
178
+ hot = true;
179
+ reschedule();
180
+ }
157
181
  const discoveredTeamArchived = [...previousDiscoveredKeys]
158
182
  .some((teamId) => !discoveredLiveKeys.has(teamId));
159
183
  const missing = monitorTargets.filter((target) => !liveTeams.some((team) => team.captainSessionId === target.sessionId && team.teamId === target.teamId));
@@ -164,8 +188,9 @@ export function startActivityPolling(monitorTargets, runtime = {}) {
164
188
  return;
165
189
  // Archives are immutable per team generation. A successful fallback
166
190
  // 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.
191
+ // host archive no longer exists; a discovery session that already
192
+ // upgraded keeps polling, and a still-probing one keeps probing, so a
193
+ // team created later in the same session stays discoverable.
169
194
  const archivedResponse = await fetchState(`${ACTIVITY_STATE_URL}?archived=1`, {
170
195
  cache: 'no-store',
171
196
  signal: controller.signal,
@@ -189,7 +214,8 @@ export function startActivityPolling(monitorTargets, runtime = {}) {
189
214
  }
190
215
  };
191
216
  const firstTick = tick();
192
- const timer = schedule(() => { void tick(); }, ACTIVITY_POLL_MS);
217
+ if (timer === undefined)
218
+ timer = schedule(() => { void tick(); }, intervalMs());
193
219
  return {
194
220
  firstTick,
195
221
  stop: () => {
@@ -5,7 +5,7 @@ import { agentTeamsCardDefinition } from "./agent-teams-card-definition.js";
5
5
  import { AGENT_TEAMS_LOCALE_NAMESPACE, en, zh, } from "./locales.js";
6
6
  import { openAgentTeamMember } from "./session-navigation.js";
7
7
  /** Required services: conversation nodes, slots, sessions navigation, and locale. */
8
- export const inject = ['conversationEvents', 'slots', 'sessions', 'locale'];
8
+ export const inject = ['conversationEvents', 'slots', 'sessions', 'locale', 'modelDirectories'];
9
9
  /** The replayed user message is the canonical transcript entry. */
10
10
  function HiddenAgentTeamsCommand() {
11
11
  return null;
@@ -22,7 +22,7 @@ export function apply(ctx) {
22
22
  console.warn(`agent-teams: failed to open member transcript ${childId}: ${String(error)}`);
23
23
  });
24
24
  };
25
- const Panel = ({ t }) => (_jsx(ActivityPanel, { sessionsList: ctx.sessions.list, openMember: openMember, t: t }));
25
+ const Panel = ({ t }) => (_jsx(ActivityPanel, { sessionsList: ctx.sessions.list, modelDirectories: ctx.modelDirectories, openMember: openMember, t: t }));
26
26
  ctx.slots.inject('shell.overlay', () => ctx.slots.register({
27
27
  name: 'shell.overlay',
28
28
  id: 'agent-teams-activity',
@@ -6,13 +6,23 @@ export const zh = {
6
6
  'card.memberCount': '{count} 名成员',
7
7
  'action.openActivityPanel': '打开活动面板',
8
8
  'activity.panelButton': '活动面板',
9
- 'activity.badgeAria': 'AgentTeams 活动,{count} 个团队',
9
+ 'activity.badgeAria': 'AgentTeams 活动与历史,{count} 条团队记录',
10
10
  'activity.panelAria': 'AgentTeams 活动面板',
11
11
  'activity.title': 'AgentTeams 活动',
12
12
  'activity.float': '切换为浮动面板',
13
13
  'activity.dockRight': '停靠到右侧',
14
14
  'activity.collapse': '收起活动面板',
15
15
  'activity.empty': '暂无团队活动',
16
+ 'team.stop': '停止团队',
17
+ 'team.stopped': '已停止',
18
+ 'team.stopTitle': '确认停止“{team}”?',
19
+ 'team.stopDescription': '将取消 {tasks} 项未完成任务,并停止 {members} 名正在工作的成员。已完成的结果会保留。',
20
+ 'team.stopCancel': '继续运行',
21
+ 'team.stopConfirm': '确认停止',
22
+ 'team.stopping': '正在停止…',
23
+ 'team.stopFailed': '停止失败:{message}',
24
+ 'team.stopRequestFailed': '服务器未能停止团队,请重试',
25
+ 'team.discarded': '已放弃',
16
26
  'format.listSeparator': '、',
17
27
  'task.status.pending': '待领取',
18
28
  'task.status.claimed': '已认领',
@@ -20,6 +30,7 @@ export const zh = {
20
30
  'task.status.completed': '已完成',
21
31
  'task.status.failed': '失败',
22
32
  'task.status.cancelled': '已取消',
33
+ 'task.status.notRun': '未执行',
23
34
  'member.state.working': '工作中',
24
35
  'member.state.failed': '有失败',
25
36
  'member.state.waiting': '等待',
@@ -28,7 +39,11 @@ export const zh = {
28
39
  'member.state.removed': '已移除',
29
40
  'member.state.pending': '待执行',
30
41
  'member.state.unassigned': '待派工',
42
+ 'member.state.staged': '待创建',
43
+ 'member.state.notCreated': '未创建',
44
+ 'member.state.stopped': '已停止',
31
45
  'member.status.executing': '正在执行 {taskId}',
46
+ 'member.status.executingModel': '正在执行 {taskId} · {model}',
32
47
  'member.status.working': '正在处理已派任务',
33
48
  'member.status.waitingOn': '等待 {taskId} · {assignee}',
34
49
  'member.status.waitingPrerequisite': '等待前置任务',
@@ -36,14 +51,22 @@ export const zh = {
36
51
  'member.status.delivered': '任务已交付',
37
52
  'member.status.idle': '待继续执行',
38
53
  'member.status.unknown': '状态未知',
54
+ 'member.status.staged': '确认后创建并启动',
55
+ 'member.status.settled': '任务均已终结',
56
+ 'member.status.discarded': '计划已放弃,未创建',
57
+ 'member.status.stopped': '团队已停止,需显式恢复',
39
58
  'task.assignee.unclaimed': '待认领',
40
59
  'task.summary.waitingBreakdown': '等待队长拆解任务',
60
+ 'task.summary.staged': '{count} 项计划等待确认',
61
+ 'task.summary.discarded': '{count} 项计划已放弃,均未执行',
41
62
  'task.summary.allDelivered': '全部 {count} 项任务已交付',
63
+ 'task.summary.ended': '终态:{completed} 已交付 · {cancelled} 已取消 · {failed} 失败',
42
64
  'task.summary.blockedAndRunning': '{tasks}{more} 等待前置,其余已开工',
43
65
  'task.summary.more': ' 等 {count} 项',
44
66
  'task.summary.running': '{tasks} 正在执行',
45
67
  'task.summary.ready': '{tasks} 已就绪待开工',
46
68
  'task.summary.blocked': '{tasks} 等待前置',
69
+ 'task.summary.failedSettled': '{count} 项已失败,自动循环已停止',
47
70
  'task.summary.waitingSchedule': '等待下一轮调度',
48
71
  'progress.aria': '团队总进度',
49
72
  'progress.title': '总进度',
@@ -57,13 +80,89 @@ export const zh = {
57
80
  'dependency.hint.chain': '悬停高亮依赖链 · 点击固定',
58
81
  'dependency.hint.pinned': '{taskId} 已固定 · Esc 取消',
59
82
  'task.runningAria': '运行中',
83
+ 'task.model': '{model}',
84
+ 'member.model': '{model}',
60
85
  'task.detail.completed': '已完成并交付',
61
86
  'task.detail.noPrerequisite': '无前置,可立即开工',
62
87
  'task.detail.ready': '前置已就绪,可开工',
63
88
  'task.detail.waitingOn': '等待 {tasks}',
89
+ 'task.detail.notRun': '计划已放弃,任务未执行',
64
90
  'task.detail.noDownstream': '无下游任务',
65
91
  'task.detail.unlocks': '完成后解锁 {tasks}',
66
92
  'team.ended': '已结束',
93
+ 'plan.badge': '待确认',
94
+ 'plan.title': '执行前计划审查',
95
+ 'plan.description': '成员尚未创建、任务尚未调度。可直接调整计划,也可返回对话告诉队长哪里需要修改。',
96
+ 'plan.member.role': '角色',
97
+ 'plan.member.provider': 'Provider',
98
+ 'plan.member.model': '模型',
99
+ 'plan.member.reasoning': '推理等级',
100
+ 'plan.member.reasoningHint': '留空使用默认值;可用 low、medium、high、xhigh 等',
101
+ 'plan.model.choose': '选择模型',
102
+ 'plan.model.currentUnavailable': '{provider}/{model}(当前目录不可用)',
103
+ 'plan.model.route': '路由:{provider}/{model}',
104
+ 'plan.model.defaultReasoning': '默认推理等级',
105
+ 'plan.model.providerDefault': 'Provider 默认值',
106
+ 'plan.model.modelDefault': '模型默认值({effort})',
107
+ 'plan.model.triggerAria': '选择成员模型,当前 {model},推理等级 {effort}',
108
+ 'plan.model.back': '返回',
109
+ 'plan.model.loading': '正在加载模型…',
110
+ 'plan.model.empty': '暂无可用模型',
111
+ 'plan.model.partialFailure': '{count} 个 Provider 的模型目录加载失败',
112
+ 'plan.model.retry': '重试',
113
+ 'plan.member.prompt': '角色提示词',
114
+ 'plan.member.roleFallback': '未设置角色',
115
+ 'plan.task.subject': '任务名称',
116
+ 'plan.task.description': '任务说明',
117
+ 'plan.task.assignee': '负责人',
118
+ 'plan.task.dependencies': '依赖任务 ID(逗号分隔)',
119
+ 'plan.task.dependenciesHint': '例如 task-1, task-2;不得形成循环依赖',
120
+ 'plan.task.unassigned': '共享任务池',
121
+ 'plan.unsaved': '未保存',
122
+ 'plan.save': '保存',
123
+ 'plan.saving': '保存中…',
124
+ 'plan.remove': '删除',
125
+ 'plan.removed': '任务已删除',
126
+ 'plan.removeConfirm': '确认删除',
127
+ 'plan.removeWarning': '删除 {task} 后将重新计算依赖关系。',
128
+ 'plan.cancel': '取消',
129
+ 'plan.addTask': '添加任务',
130
+ 'plan.adding': '添加中…',
131
+ 'plan.taskAdded': '任务已添加',
132
+ 'plan.newTask': '新任务名称',
133
+ 'plan.newTaskLabel': '新增计划任务',
134
+ 'plan.readySummary': '{members} 名成员 · {tasks} 项任务 · {links} 条依赖',
135
+ 'plan.flow.aria': '团队启动流程',
136
+ 'plan.flow.review': '审查计划',
137
+ 'plan.flow.spawn': '创建成员',
138
+ 'plan.flow.run': '开始执行',
139
+ 'plan.members.title': '成员与模型路由',
140
+ 'plan.members.count': '{count} 名成员',
141
+ 'plan.members.empty': '尚未规划成员',
142
+ 'plan.tasks.title': '任务与依赖',
143
+ 'plan.tasks.count': '{count} 项任务 · {links} 条依赖',
144
+ 'plan.tasks.empty': '尚未规划任务',
145
+ 'plan.dependencies.none': '无依赖',
146
+ 'plan.dependencies.count': '{count} 条依赖',
147
+ 'plan.approve': '确认并启动团队',
148
+ 'plan.approving': '正在创建成员…',
149
+ 'plan.approveTitle': '计划检查完毕?',
150
+ 'plan.approveHint': '确认后将创建 {members} 名成员并调度 {tasks} 项任务。',
151
+ 'plan.approveConfirmTitle': '确认启动此团队',
152
+ 'plan.approveWarning': '启动后不能再在此处编辑成员和依赖。',
153
+ 'plan.approveConfirm': '确认启动',
154
+ 'plan.continue': '返回对话修改',
155
+ 'plan.returnToChat': '回到对话',
156
+ 'plan.feedbackTitle': '正在等你说明修改方向',
157
+ 'plan.feedbackHint': '队长会在对话中追问;收到你的回复后,只修改这份草案并再次等待确认。',
158
+ 'plan.discard': '放弃本次计划',
159
+ 'plan.discardConfirmTitle': '放弃本次计划?',
160
+ 'plan.discardWarning': '该计划会结束并归档;尚未创建任何成员,也不会执行任务。',
161
+ 'plan.discardConfirm': '确认放弃',
162
+ 'plan.discarding': '正在放弃…',
163
+ 'plan.pendingEdits': '请先保存当前修改,再启动团队。',
164
+ 'plan.saved': '计划已保存',
165
+ 'plan.failed': '操作失败:{message}',
67
166
  'team.stats.members': '{count} 名成员',
68
167
  'team.stats.completed': '{completed}/{total} 完成',
69
168
  'team.stats.messages': '{count} 条消息',
@@ -71,29 +170,51 @@ export const zh = {
71
170
  'captain.name': '队长',
72
171
  'captain.role': '拆解 · 派发 · 汇总',
73
172
  'captain.summary': '已派发 {tasks} 项任务给 {members} 名成员',
173
+ 'captain.summary.staged': '已规划 {tasks} 项任务与 {members} 名成员,等待确认',
174
+ 'captain.summary.awaitingFeedback': '草案已保留,等待你在对话中说明修改方向',
175
+ 'captain.summary.discarded': '计划已放弃:{members} 名成员未创建,{tasks} 项任务未执行',
176
+ 'captain.summary.withTakeover': '已派发 {tasks} 项给成员 · 队长接管 {captainTasks}',
74
177
  'captain.state.working': '{count} 人执行中',
178
+ 'captain.state.takeover': '正在执行 {tasks}',
75
179
  'captain.state.collected': '已收齐',
76
180
  'captain.state.waiting': '等待回报',
181
+ 'captain.state.staged': '待确认',
182
+ 'captain.state.awaitingFeedback': '待反馈',
183
+ 'captain.state.discarded': '已放弃',
184
+ 'captain.state.settled': '已终结',
77
185
  'members.toggle': '{count} 名成员',
78
186
  'members.collapse': '收起',
79
187
  'members.expand': '展开',
80
188
  'members.empty': '暂无成员,等待队长组建团队',
81
189
  'assignment.label': '队长派发',
190
+ 'assignment.staged': '计划任务',
191
+ 'assignment.discarded': '未执行的计划',
82
192
  'assignment.empty': '暂无任务',
83
193
  'archive.label': '已结束 · 历史归档',
194
+ 'archive.discardedLabel': '计划已放弃 · 历史归档',
84
195
  };
85
196
  /** English dictionary, checked complete against the Chinese source key set. */
86
197
  export const en = {
87
198
  'card.memberCount': '{count} members',
88
199
  'action.openActivityPanel': 'Open activity panel',
89
200
  'activity.panelButton': 'Activity panel',
90
- 'activity.badgeAria': 'AgentTeams activity, {count} teams',
201
+ 'activity.badgeAria': 'AgentTeams activity and history, {count} team records',
91
202
  'activity.panelAria': 'AgentTeams activity panel',
92
203
  'activity.title': 'AgentTeams activity',
93
204
  'activity.float': 'Switch to floating panel',
94
205
  'activity.dockRight': 'Dock to the right',
95
206
  'activity.collapse': 'Collapse activity panel',
96
207
  'activity.empty': 'No team activity',
208
+ 'team.stop': 'Stop team',
209
+ 'team.stopped': 'Stopped',
210
+ 'team.stopTitle': 'Stop “{team}”?',
211
+ 'team.stopDescription': 'This cancels {tasks} unfinished tasks and stops {members} working members. Completed results are kept.',
212
+ 'team.stopCancel': 'Keep running',
213
+ 'team.stopConfirm': 'Stop team',
214
+ 'team.stopping': 'Stopping…',
215
+ 'team.stopFailed': 'Could not stop team: {message}',
216
+ 'team.stopRequestFailed': 'The server could not stop this team. Try again.',
217
+ 'team.discarded': 'Discarded',
97
218
  'format.listSeparator': ', ',
98
219
  'task.status.pending': 'Unclaimed',
99
220
  'task.status.claimed': 'Claimed',
@@ -101,6 +222,7 @@ export const en = {
101
222
  'task.status.completed': 'Completed',
102
223
  'task.status.failed': 'Failed',
103
224
  'task.status.cancelled': 'Cancelled',
225
+ 'task.status.notRun': 'Not run',
104
226
  'member.state.working': 'Working',
105
227
  'member.state.failed': 'Has failures',
106
228
  'member.state.waiting': 'Waiting',
@@ -109,7 +231,11 @@ export const en = {
109
231
  'member.state.removed': 'Removed',
110
232
  'member.state.pending': 'Pending',
111
233
  'member.state.unassigned': 'Awaiting assignment',
234
+ 'member.state.staged': 'Not spawned',
235
+ 'member.state.notCreated': 'Not created',
236
+ 'member.state.stopped': 'Stopped',
112
237
  'member.status.executing': 'Working on {taskId}',
238
+ 'member.status.executingModel': 'Working on {taskId} · {model}',
113
239
  'member.status.working': 'Working on assigned tasks',
114
240
  'member.status.waitingOn': 'Waiting for {taskId} · {assignee}',
115
241
  'member.status.waitingPrerequisite': 'Waiting for prerequisites',
@@ -117,14 +243,22 @@ export const en = {
117
243
  'member.status.delivered': 'Tasks delivered',
118
244
  'member.status.idle': 'Ready to continue',
119
245
  'member.status.unknown': 'Status unknown',
246
+ 'member.status.staged': 'Will be spawned after approval',
247
+ 'member.status.settled': 'All assigned work is settled',
248
+ 'member.status.discarded': 'Plan discarded; member was not created',
249
+ 'member.status.stopped': 'Team stopped; explicit resume required',
120
250
  'task.assignee.unclaimed': 'Unclaimed',
121
251
  'task.summary.waitingBreakdown': 'Waiting for the captain to break down the work',
252
+ 'task.summary.staged': '{count} planned tasks awaiting approval',
253
+ 'task.summary.discarded': '{count} planned tasks discarded; none ran',
122
254
  'task.summary.allDelivered': 'All {count} tasks delivered',
255
+ 'task.summary.ended': 'Final: {completed} delivered · {cancelled} cancelled · {failed} failed',
123
256
  'task.summary.blockedAndRunning': '{tasks}{more} waiting on prerequisites; other work has started',
124
257
  'task.summary.more': ' and {count} more',
125
258
  'task.summary.running': '{tasks} in progress',
126
259
  'task.summary.ready': '{tasks} ready to start',
127
260
  'task.summary.blocked': '{tasks} waiting on prerequisites',
261
+ 'task.summary.failedSettled': '{count} failed; the automatic loop has stopped',
128
262
  'task.summary.waitingSchedule': 'Waiting for the next scheduling round',
129
263
  'progress.aria': 'Overall team progress',
130
264
  'progress.title': 'Overall progress',
@@ -138,13 +272,89 @@ export const en = {
138
272
  'dependency.hint.chain': 'Hover to highlight dependencies · Click to pin',
139
273
  'dependency.hint.pinned': '{taskId} pinned · Esc to clear',
140
274
  'task.runningAria': 'Running',
275
+ 'task.model': '{model}',
276
+ 'member.model': '{model}',
141
277
  'task.detail.completed': 'Completed and delivered',
142
278
  'task.detail.noPrerequisite': 'No prerequisites; ready to start',
143
279
  'task.detail.ready': 'Prerequisites ready; can start',
144
280
  'task.detail.waitingOn': 'Waiting for {tasks}',
281
+ 'task.detail.notRun': 'Plan discarded; task was not run',
145
282
  'task.detail.noDownstream': 'No downstream tasks',
146
283
  'task.detail.unlocks': 'Unlocks {tasks} when complete',
147
284
  'team.ended': 'Ended',
285
+ 'plan.badge': 'Awaiting approval',
286
+ 'plan.title': 'Pre-run plan review',
287
+ 'plan.description': 'Members have not been spawned and tasks have not been scheduled. Edit the draft here, or return to chat and tell the Captain what should change.',
288
+ 'plan.member.role': 'Role',
289
+ 'plan.member.provider': 'Provider',
290
+ 'plan.member.model': 'Model',
291
+ 'plan.member.reasoning': 'Reasoning effort',
292
+ 'plan.member.reasoningHint': 'Leave blank for default; accepts low, medium, high, xhigh, and more',
293
+ 'plan.model.choose': 'Choose a model',
294
+ 'plan.model.currentUnavailable': '{provider}/{model} (not in the current catalog)',
295
+ 'plan.model.route': 'Route: {provider}/{model}',
296
+ 'plan.model.defaultReasoning': 'Default reasoning effort',
297
+ 'plan.model.providerDefault': 'Provider default',
298
+ 'plan.model.modelDefault': 'Model default ({effort})',
299
+ 'plan.model.triggerAria': 'Choose member model, currently {model}, reasoning effort {effort}',
300
+ 'plan.model.back': 'Back',
301
+ 'plan.model.loading': 'Loading models…',
302
+ 'plan.model.empty': 'No models available',
303
+ 'plan.model.partialFailure': '{count} provider catalogs could not be loaded',
304
+ 'plan.model.retry': 'Retry',
305
+ 'plan.member.prompt': 'Role prompt',
306
+ 'plan.member.roleFallback': 'Role not set',
307
+ 'plan.task.subject': 'Task subject',
308
+ 'plan.task.description': 'Task description',
309
+ 'plan.task.assignee': 'Assignee',
310
+ 'plan.task.dependencies': 'Dependency task IDs (comma-separated)',
311
+ 'plan.task.dependenciesHint': 'For example task-1, task-2; cycles are rejected',
312
+ 'plan.task.unassigned': 'Shared task pool',
313
+ 'plan.unsaved': 'Unsaved',
314
+ 'plan.save': 'Save',
315
+ 'plan.saving': 'Saving…',
316
+ 'plan.remove': 'Remove',
317
+ 'plan.removed': 'Task removed',
318
+ 'plan.removeConfirm': 'Confirm remove',
319
+ 'plan.removeWarning': 'Removing {task} will recalculate downstream dependencies.',
320
+ 'plan.cancel': 'Cancel',
321
+ 'plan.addTask': 'Add task',
322
+ 'plan.adding': 'Adding…',
323
+ 'plan.taskAdded': 'Task added',
324
+ 'plan.newTask': 'New task subject',
325
+ 'plan.newTaskLabel': 'Add a planned task',
326
+ 'plan.readySummary': '{members} members · {tasks} tasks · {links} dependencies',
327
+ 'plan.flow.aria': 'Team launch flow',
328
+ 'plan.flow.review': 'Review plan',
329
+ 'plan.flow.spawn': 'Create members',
330
+ 'plan.flow.run': 'Start work',
331
+ 'plan.members.title': 'Members & model routes',
332
+ 'plan.members.count': '{count} members',
333
+ 'plan.members.empty': 'No members planned yet',
334
+ 'plan.tasks.title': 'Tasks & dependencies',
335
+ 'plan.tasks.count': '{count} tasks · {links} dependencies',
336
+ 'plan.tasks.empty': 'No tasks planned yet',
337
+ 'plan.dependencies.none': 'No dependencies',
338
+ 'plan.dependencies.count': '{count} dependencies',
339
+ 'plan.approve': 'Approve & Run',
340
+ 'plan.approving': 'Creating members…',
341
+ 'plan.approveTitle': 'Plan ready?',
342
+ 'plan.approveHint': 'Approval creates {members} members and schedules {tasks} tasks.',
343
+ 'plan.approveConfirmTitle': 'Confirm team launch',
344
+ 'plan.approveWarning': 'Member routes and dependencies cannot be edited here after launch.',
345
+ 'plan.approveConfirm': 'Confirm launch',
346
+ 'plan.continue': 'Return to chat & revise',
347
+ 'plan.returnToChat': 'Return to chat',
348
+ 'plan.feedbackTitle': 'Waiting for your revision direction',
349
+ 'plan.feedbackHint': 'The Captain will ask in chat. After your reply, it will revise this draft and wait for approval again.',
350
+ 'plan.discard': 'Discard this plan',
351
+ 'plan.discardConfirmTitle': 'Discard this plan?',
352
+ 'plan.discardWarning': 'The plan will end and be archived. No members have been spawned and no tasks will run.',
353
+ 'plan.discardConfirm': 'Discard plan',
354
+ 'plan.discarding': 'Discarding…',
355
+ 'plan.pendingEdits': 'Save the current edits before launching the team.',
356
+ 'plan.saved': 'Plan saved',
357
+ 'plan.failed': 'Operation failed: {message}',
148
358
  'team.stats.members': '{count} members',
149
359
  'team.stats.completed': '{completed}/{total} completed',
150
360
  'team.stats.messages': '{count} messages',
@@ -152,14 +362,26 @@ export const en = {
152
362
  'captain.name': 'Captain',
153
363
  'captain.role': 'Break down · Delegate · Synthesize',
154
364
  'captain.summary': 'Assigned {tasks} tasks to {members} members',
365
+ 'captain.summary.staged': 'Planned {tasks} tasks and {members} members; awaiting approval',
366
+ 'captain.summary.awaitingFeedback': 'Draft preserved; waiting for your revision direction in chat',
367
+ 'captain.summary.discarded': 'Plan discarded: {members} members were not created and {tasks} tasks did not run',
368
+ 'captain.summary.withTakeover': 'Assigned {tasks} to members · Captain owns {captainTasks}',
155
369
  'captain.state.working': '{count} active',
370
+ 'captain.state.takeover': 'Working on {tasks}',
156
371
  'captain.state.collected': 'All reports received',
157
372
  'captain.state.waiting': 'Waiting for reports',
373
+ 'captain.state.staged': 'Awaiting approval',
374
+ 'captain.state.awaitingFeedback': 'Awaiting feedback',
375
+ 'captain.state.discarded': 'Discarded',
376
+ 'captain.state.settled': 'Settled',
158
377
  'members.toggle': 'Members {count}',
159
378
  'members.collapse': 'Collapse',
160
379
  'members.expand': 'Expand',
161
380
  'members.empty': 'No members yet; waiting for the captain to assemble the team',
162
381
  'assignment.label': 'Captain assigned',
382
+ 'assignment.staged': 'Planned task',
383
+ 'assignment.discarded': 'Plan not run',
163
384
  'assignment.empty': 'No tasks',
164
385
  'archive.label': 'Ended · Archived history',
386
+ 'archive.discardedLabel': 'Plan discarded · Archived history',
165
387
  };