openteam 0.6.0 → 0.7.0
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/package.json
CHANGED
|
@@ -223,6 +223,11 @@ function getCollaborationRules() {
|
|
|
223
223
|
- **直接输出文字对方看不到**,必须用 \`msg\` 工具
|
|
224
224
|
- 收到 \`[from agent]\` 消息后,必须用 \`msg\` 回复对方才能看到
|
|
225
225
|
|
|
226
|
+
### Boss 消息(重要)
|
|
227
|
+
- 收到 \`[from boss]\` 时**直接回复**即可(boss 在同一会话中,不是 agent)
|
|
228
|
+
- **绝对禁止** \`msg(who="boss", ...)\`,msg 只能发给团队 agent
|
|
229
|
+
- boss 能直接看到你的输出,不需要任何工具
|
|
230
|
+
|
|
226
231
|
### 任务汇报(重要)
|
|
227
232
|
- **任务完成后必须用 \`msg\` 向任务分配者汇报结果**
|
|
228
233
|
- 汇报内容:完成了什么、关键产出、是否有遗留问题
|
|
@@ -233,14 +238,5 @@ function getCollaborationRules() {
|
|
|
233
238
|
- 完成后调 \`taskboard(action="done", id=N)\` 标记完成,系统会自动通知下游
|
|
234
239
|
- 用 \`taskboard(action="list")\` 查看所有任务及状态
|
|
235
240
|
- 任务完成标记后不需要额外用 msg 汇报(系统自动处理流转)
|
|
236
|
-
|
|
237
|
-
### Boss 消息回复方式
|
|
238
|
-
- 收到 \`[from boss]\` 时**直接回复**即可(boss 在同一会话中)
|
|
239
|
-
- **禁止**用 \`msg(who="boss", ...)\`,boss 不是 agent
|
|
240
|
-
|
|
241
|
-
### 记忆系统
|
|
242
|
-
- 系统会在对话结束后**自动巩固**有价值的信息到长期记忆,你无需刻意记录
|
|
243
|
-
- \`<memory>\` 中的 index 类型记忆显示了你所有笔记的摘要,需要详情时用 \`recall\` 查阅
|
|
244
|
-
- 用 \`review\` 和 \`reread\` 可以回顾历史对话
|
|
245
241
|
</collaboration-rules>`;
|
|
246
242
|
}
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
import blessed from 'blessed';
|
|
6
6
|
|
|
7
|
-
//
|
|
7
|
+
// 模块级变量:保存当前列表原始数据,供展开详情用
|
|
8
8
|
let _currentMessages = [];
|
|
9
|
+
let _currentTasks = [];
|
|
10
|
+
let _lastFocused = null;
|
|
9
11
|
|
|
10
12
|
// Agent 颜色映射(按首次出现顺序轮转)
|
|
11
13
|
const AGENT_COLORS = ['green', 'cyan', 'magenta', 'blue', 'yellow', 'red'];
|
|
@@ -27,6 +29,7 @@ export function createDashboard(teamName) {
|
|
|
27
29
|
const screen = blessed.screen({
|
|
28
30
|
smartCSR: true,
|
|
29
31
|
fullUnicode: true,
|
|
32
|
+
mouse: true,
|
|
30
33
|
title: `OpenTeam Dashboard - ${teamName}`,
|
|
31
34
|
});
|
|
32
35
|
|
|
@@ -90,7 +93,7 @@ export function createDashboard(teamName) {
|
|
|
90
93
|
height: '100%-33',
|
|
91
94
|
tags: true,
|
|
92
95
|
border: { type: 'line' },
|
|
93
|
-
label: ' 消息流 (
|
|
96
|
+
label: ' 消息流 (点击/Enter展开 Tab切换 q退出) ',
|
|
94
97
|
scrollable: true,
|
|
95
98
|
keys: true,
|
|
96
99
|
vi: true,
|
|
@@ -105,16 +108,15 @@ export function createDashboard(teamName) {
|
|
|
105
108
|
items: [],
|
|
106
109
|
});
|
|
107
110
|
|
|
108
|
-
//
|
|
109
|
-
const taskBoard = blessed.
|
|
111
|
+
// 任务看板(钉在底部,可选中)
|
|
112
|
+
const taskBoard = blessed.list({
|
|
110
113
|
bottom: 0,
|
|
111
114
|
left: 0,
|
|
112
115
|
width: '100%',
|
|
113
116
|
height: 12,
|
|
114
|
-
content: '',
|
|
115
117
|
tags: true,
|
|
116
118
|
border: { type: 'line' },
|
|
117
|
-
label: ' 任务看板 ',
|
|
119
|
+
label: ' 任务看板 (点击/Enter详情 Tab切换) ',
|
|
118
120
|
scrollable: true,
|
|
119
121
|
keys: true,
|
|
120
122
|
vi: true,
|
|
@@ -123,7 +125,10 @@ export function createDashboard(teamName) {
|
|
|
123
125
|
style: {
|
|
124
126
|
fg: 'white',
|
|
125
127
|
border: { fg: 'blue' },
|
|
128
|
+
selected: { bg: 'blue', fg: 'white' },
|
|
129
|
+
item: { fg: 'white' },
|
|
126
130
|
},
|
|
131
|
+
items: [],
|
|
127
132
|
});
|
|
128
133
|
|
|
129
134
|
// 消息详情弹窗(默认隐藏)
|
|
@@ -156,12 +161,30 @@ export function createDashboard(teamName) {
|
|
|
156
161
|
screen.append(taskBoard);
|
|
157
162
|
screen.append(detailBox);
|
|
158
163
|
|
|
164
|
+
// Tab 焦点切换(消息流 ↔ 任务看板)
|
|
165
|
+
const focusable = [messageStream, taskBoard];
|
|
166
|
+
let focusIndex = 0;
|
|
167
|
+
|
|
168
|
+
screen.key(['tab'], () => {
|
|
169
|
+
if (!detailBox.hidden) return; // 弹窗打开时不切换
|
|
170
|
+
focusIndex = (focusIndex + 1) % focusable.length;
|
|
171
|
+
focusable[focusIndex].focus();
|
|
172
|
+
screen.render();
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
screen.key(['S-tab'], () => {
|
|
176
|
+
if (!detailBox.hidden) return;
|
|
177
|
+
focusIndex = (focusIndex - 1 + focusable.length) % focusable.length;
|
|
178
|
+
focusable[focusIndex].focus();
|
|
179
|
+
screen.render();
|
|
180
|
+
});
|
|
181
|
+
|
|
159
182
|
// 全局退出
|
|
160
183
|
screen.key(['q', 'C-c'], () => {
|
|
161
184
|
if (!detailBox.hidden) {
|
|
162
185
|
// 如果详情弹窗打开,先关闭弹窗
|
|
163
186
|
detailBox.hide();
|
|
164
|
-
messageStream.focus();
|
|
187
|
+
(_lastFocused || messageStream).focus();
|
|
165
188
|
screen.render();
|
|
166
189
|
return;
|
|
167
190
|
}
|
|
@@ -170,6 +193,7 @@ export function createDashboard(teamName) {
|
|
|
170
193
|
|
|
171
194
|
// Enter 展开消息详情
|
|
172
195
|
messageStream.on('select', (item, index) => {
|
|
196
|
+
_lastFocused = messageStream;
|
|
173
197
|
const msg = _currentMessages[index];
|
|
174
198
|
if (!msg) return;
|
|
175
199
|
|
|
@@ -184,6 +208,43 @@ export function createDashboard(teamName) {
|
|
|
184
208
|
msg.fullContent || msg.content,
|
|
185
209
|
].join('\n');
|
|
186
210
|
|
|
211
|
+
detailBox.setLabel(' 消息详情 (Esc/q 关闭) ');
|
|
212
|
+
detailBox.setContent(detail);
|
|
213
|
+
detailBox.setScrollPerc(0);
|
|
214
|
+
detailBox.show();
|
|
215
|
+
detailBox.focus();
|
|
216
|
+
screen.render();
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// Enter 展开任务详情
|
|
220
|
+
taskBoard.on('select', (item, index) => {
|
|
221
|
+
_lastFocused = taskBoard;
|
|
222
|
+
const task = _currentTasks[index];
|
|
223
|
+
if (!task) return;
|
|
224
|
+
|
|
225
|
+
const created = new Date(task.createdAt).toLocaleString('zh-CN', { hour12: false });
|
|
226
|
+
const doneAt = task.doneAt ? new Date(task.doneAt).toLocaleString('zh-CN', { hour12: false }) : '—';
|
|
227
|
+
const status = task.status === 'done' ? '{green-fg}已完成{/green-fg}' : '{yellow-fg}待处理{/yellow-fg}';
|
|
228
|
+
const deps = task.dependsOn.length > 0
|
|
229
|
+
? task.dependsOn.map(id => '#' + id).join(', ')
|
|
230
|
+
: '无';
|
|
231
|
+
|
|
232
|
+
const detail = [
|
|
233
|
+
`{bold}任务 #${task.id}{/bold}`,
|
|
234
|
+
'',
|
|
235
|
+
`{bold}标题:{/bold} ${task.title}`,
|
|
236
|
+
`{bold}分配人:{/bold} {cyan-fg}${task.assignee}{/cyan-fg}`,
|
|
237
|
+
`{bold}状态:{/bold} ${status}`,
|
|
238
|
+
`{bold}依赖:{/bold} ${deps}`,
|
|
239
|
+
`{bold}创建时间:{/bold} ${created}`,
|
|
240
|
+
`{bold}完成时间:{/bold} ${doneAt}`,
|
|
241
|
+
'',
|
|
242
|
+
'{bold}描述:{/bold}',
|
|
243
|
+
'─'.repeat(60),
|
|
244
|
+
task.description || '{gray-fg}(无描述){/gray-fg}',
|
|
245
|
+
].join('\n');
|
|
246
|
+
|
|
247
|
+
detailBox.setLabel(' 任务详情 (Esc/q 关闭) ');
|
|
187
248
|
detailBox.setContent(detail);
|
|
188
249
|
detailBox.setScrollPerc(0);
|
|
189
250
|
detailBox.show();
|
|
@@ -194,10 +255,25 @@ export function createDashboard(teamName) {
|
|
|
194
255
|
// Esc 关闭详情弹窗
|
|
195
256
|
detailBox.key(['escape', 'q'], () => {
|
|
196
257
|
detailBox.hide();
|
|
197
|
-
messageStream.focus();
|
|
258
|
+
(_lastFocused || messageStream).focus();
|
|
198
259
|
screen.render();
|
|
199
260
|
});
|
|
200
261
|
|
|
262
|
+
// 点击弹窗外区域关闭详情
|
|
263
|
+
screen.on('click', (mouse) => {
|
|
264
|
+
if (detailBox.hidden) return;
|
|
265
|
+
// 检查点击是否在 detailBox 范围外
|
|
266
|
+
const top = detailBox.atop;
|
|
267
|
+
const left = detailBox.aleft;
|
|
268
|
+
const bottom = top + detailBox.height;
|
|
269
|
+
const right = left + detailBox.width;
|
|
270
|
+
if (mouse.x < left || mouse.x >= right || mouse.y < top || mouse.y >= bottom) {
|
|
271
|
+
detailBox.hide();
|
|
272
|
+
(_lastFocused || messageStream).focus();
|
|
273
|
+
screen.render();
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
|
|
201
277
|
// 默认焦点在消息流
|
|
202
278
|
messageStream.focus();
|
|
203
279
|
screen.render();
|
|
@@ -290,13 +366,15 @@ function formatActivity(agent) {
|
|
|
290
366
|
/**
|
|
291
367
|
* 更新任务看板
|
|
292
368
|
*/
|
|
293
|
-
export function updateTaskBoard(
|
|
294
|
-
|
|
295
|
-
|
|
369
|
+
export function updateTaskBoard(listBox, tasks) {
|
|
370
|
+
_currentTasks = tasks || [];
|
|
371
|
+
|
|
372
|
+
if (_currentTasks.length === 0) {
|
|
373
|
+
listBox.setItems(['{yellow-fg}暂无任务{/yellow-fg}']);
|
|
296
374
|
return;
|
|
297
375
|
}
|
|
298
376
|
|
|
299
|
-
const
|
|
377
|
+
const items = _currentTasks.map(t => {
|
|
300
378
|
const status = t.status === 'done'
|
|
301
379
|
? '{green-fg}✓{/green-fg}'
|
|
302
380
|
: '{yellow-fg}⏳{/yellow-fg}';
|
|
@@ -307,7 +385,7 @@ export function updateTaskBoard(box, tasks) {
|
|
|
307
385
|
let deps = '';
|
|
308
386
|
if (t.status === 'pending' && t.dependsOn.length > 0) {
|
|
309
387
|
const pendingDeps = t.dependsOn.filter(depId => {
|
|
310
|
-
const dep =
|
|
388
|
+
const dep = _currentTasks.find(d => d.id === depId);
|
|
311
389
|
return dep && dep.status !== 'done';
|
|
312
390
|
});
|
|
313
391
|
if (pendingDeps.length > 0) {
|
|
@@ -318,7 +396,7 @@ export function updateTaskBoard(box, tasks) {
|
|
|
318
396
|
return `${id} ${status} ${title} ${assignee} ${deps}`;
|
|
319
397
|
});
|
|
320
398
|
|
|
321
|
-
|
|
399
|
+
listBox.setItems(items);
|
|
322
400
|
}
|
|
323
401
|
|
|
324
402
|
/**
|
|
@@ -21,7 +21,7 @@ export function createToolDefs() {
|
|
|
21
21
|
return {
|
|
22
22
|
msg: {
|
|
23
23
|
description:
|
|
24
|
-
'发消息(异步,像发微信)。直接输出文字对方看不到,必须用 msg。收到 [from
|
|
24
|
+
'给团队 agent 发消息(异步,像发微信)。直接输出文字对方看不到,必须用 msg。收到 [from agent] 消息后需用 msg 回复对方才能看到。注意:收到 [from boss] 时直接回复即可,不要用 msg——boss 在同一会话中,不是 agent。Leader 可广播。',
|
|
25
25
|
args: {
|
|
26
26
|
who: tool.schema
|
|
27
27
|
.string()
|
|
@@ -62,6 +62,10 @@ export function createToolDefs() {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
// 单点发送
|
|
65
|
+
if (args.who === 'boss') {
|
|
66
|
+
return 'Error: boss 在同一会话中,直接回复即可,不需要用 msg。';
|
|
67
|
+
}
|
|
68
|
+
|
|
65
69
|
if (!isAgentInTeam(currentAgent.team, args.who)) {
|
|
66
70
|
return `Error: 团队里没有 "${args.who}",可选: ${teamConfig.agents.join(', ')}`;
|
|
67
71
|
}
|