@suifei/dsh-ai-team 0.3.12 → 0.3.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/CHANGELOG.md +6 -0
- package/README.md +4 -2
- package/client.js +96 -12
- package/index.js +1 -1
- package/lib/host/service.js +2 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.13 — 2026-11-10
|
|
4
|
+
|
|
5
|
+
- 点击成员名字进入成员视图:该成员负责/执行的任务列表 + 直接对话 + 资料入口;
|
|
6
|
+
- 目录选择改用 `ctx.get('uiWorkspace').pickDirectory()`,修复「当前宿主不支持目录选择」;
|
|
7
|
+
- 选择目录取消不再误报为不支持;保留手填路径兜底。
|
|
8
|
+
|
|
3
9
|
## 0.3.12 — 2026-11-10
|
|
4
10
|
|
|
5
11
|
- 点击成员名字直接打开该 Agent 的 DSH 原生 Session,可与它直接对话;
|
package/README.md
CHANGED
|
@@ -88,7 +88,9 @@ dsh --profile ai-team --no-open --port 33271
|
|
|
88
88
|
- **看板**:四列;进行中的卡片显示 accountable owner 与执行者;数量只统计 active Task。
|
|
89
89
|
- **态势**:四象限——需要你、团队在做、等待受阻、最近完成。点击条目打开对应 Task 的 Thread。
|
|
90
90
|
- **收件箱**:跨 Project 聚合;失败决策、需要你决定、关注 Task 的完成报告都在这里。
|
|
91
|
-
-
|
|
91
|
+
- **成员视图**:点击成员名字,切到他的任务列表与「直接对话」入口。
|
|
92
|
+
- **直接对话**:成员视图里点「直接对话」,打开该 Agent 的 DSH 原生 Session;没有 session 会先激活。
|
|
93
|
+
- **成员资料**:点「⋯」改名、职责、personality、provider/model、运行配方、notes、skills、re-distill、离队/人才库。
|
|
92
94
|
|
|
93
95
|
---
|
|
94
96
|
|
|
@@ -103,7 +105,7 @@ dsh --profile ai-team --no-open --port 33271
|
|
|
103
105
|
| 收件箱 | Human/Agent 各自拉取;跨 Project 聚合与项目内 ack |
|
|
104
106
|
| 关注 | 关注 Task;完成后收到 `followed_task_update` |
|
|
105
107
|
| 默认成员 | 新 Project 初始化墨渊 / 青岚 / 白砚;可改名、改职责、改个性、改模型 |
|
|
106
|
-
| 成员 | 持久 session
|
|
108
|
+
| 成员 | 持久 session;点击名字进入成员视图(任务 + 直接对话);「⋯」编辑资料 |
|
|
107
109
|
| 模型 | Provider / Model 下拉读取 DSH `llm` 目录;可跟随系统默认 |
|
|
108
110
|
| 进化 | `team.evolve` 只改自己的 personality;验收后 reflection wake |
|
|
109
111
|
| 人才库 | leave/rejoin;遗留 Task 可被接管(taken-over) |
|
package/client.js
CHANGED
|
@@ -183,6 +183,11 @@ window.__ModuleLoader__.load({
|
|
|
183
183
|
agentChat: '直接对话',
|
|
184
184
|
agentChatOpening: '正在打开对话…',
|
|
185
185
|
agentChatUnavailable: '宿主没有可用的 Session 打开能力。',
|
|
186
|
+
memberTasks: '他的任务',
|
|
187
|
+
memberNoTasks: '还没有属于他的 Task',
|
|
188
|
+
memberNoTasksBody: '在项目里创建或指派 Task 后,会出现在这里。',
|
|
189
|
+
taskOwner: '负责人',
|
|
190
|
+
taskAssignee: '执行',
|
|
186
191
|
newProject: '新建项目',
|
|
187
192
|
addAgent: '添加成员',
|
|
188
193
|
newProjectMock: 'spike:新建项目属于 S-01 流程,M1 之后接入。',
|
|
@@ -473,6 +478,11 @@ window.__ModuleLoader__.load({
|
|
|
473
478
|
agentChat: 'Direct chat',
|
|
474
479
|
agentChatOpening: 'Opening conversation…',
|
|
475
480
|
agentChatUnavailable: 'This host cannot open a Session.',
|
|
481
|
+
memberTasks: 'Their tasks',
|
|
482
|
+
memberNoTasks: 'No Task belongs to this member yet',
|
|
483
|
+
memberNoTasksBody: 'Create or assign a Task in the Project and it appears here.',
|
|
484
|
+
taskOwner: 'Owner',
|
|
485
|
+
taskAssignee: 'Assignee',
|
|
476
486
|
newProject: 'New project',
|
|
477
487
|
addAgent: 'Add member',
|
|
478
488
|
newProjectMock: 'spike: project creation belongs to S-01 and lands after M1.',
|
|
@@ -938,6 +948,20 @@ window.__ModuleLoader__.load({
|
|
|
938
948
|
.at-agentfail { flex: none; color: var(--dsw-alias-state-warn-primary); }
|
|
939
949
|
.at-agentmore { flex: 0 0 auto; width: 28px; height: 28px; border: 0; border-radius: var(--dsw-radius-sm); background: transparent; color: var(--dsw-alias-label-tertiary); font: inherit; font-size: 13px; cursor: pointer; }
|
|
940
950
|
.at-agentmore:hover { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-primary); }
|
|
951
|
+
.at-memberview { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 18px; }
|
|
952
|
+
.at-memberviewhead { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; padding-bottom: 12px; border-bottom: 1px solid var(--dsw-alias-border-l1); }
|
|
953
|
+
.at-memberviewid { display: flex; flex-direction: column; min-width: 0; flex: 1 1 180px; }
|
|
954
|
+
.at-memberviewid strong { font-size: 15px; line-height: 22px; }
|
|
955
|
+
.at-memberviewid span { color: var(--dsw-alias-label-tertiary); font-size: 11px; line-height: 16px; }
|
|
956
|
+
.at-memberviewactions { display: flex; gap: 6px; flex-wrap: wrap; }
|
|
957
|
+
.at-memberviewbody { max-width: 760px; margin: 0 auto; padding-top: 14px; }
|
|
958
|
+
.at-memberviewsub { color: var(--dsw-alias-label-tertiary); font-size: 12px; line-height: 18px; margin-bottom: 8px; }
|
|
959
|
+
.at-membertasklist { display: flex; flex-direction: column; gap: 6px; }
|
|
960
|
+
.at-membertask { display: flex; align-items: center; gap: 8px; width: 100%; padding: 8px 10px; border: 1px solid var(--dsw-alias-border-l1); border-radius: var(--dsw-radius-sm); background: var(--dsw-alias-bg-module-platform); color: var(--dsw-alias-label-primary); font: inherit; text-align: left; cursor: pointer; }
|
|
961
|
+
.at-membertask:hover { border-color: var(--dsw-alias-border-l2); background: var(--dsw-alias-interactive-bg-hover); }
|
|
962
|
+
.at-membertaskno { flex: 0 0 auto; color: var(--dsw-alias-label-tertiary); font-variant-numeric: tabular-nums; font-size: 11px; }
|
|
963
|
+
.at-membertasktitle { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; }
|
|
964
|
+
.at-membertaskmeta { flex: 0 0 auto; color: var(--dsw-alias-label-tertiary); font-size: 11px; }
|
|
941
965
|
.at-sr { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0; }
|
|
942
966
|
.at-chead { flex: 0 0 auto; padding: 14px 18px 0; }
|
|
943
967
|
.at-title { display: flex; align-items: baseline; gap: 8px; min-width: 0; color: var(--dsw-alias-label-primary); font-size: 16px; font-weight: 600; line-height: 22px; letter-spacing: -.01em; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
@@ -1520,6 +1544,33 @@ window.__ModuleLoader__.load({
|
|
|
1520
1544
|
}, '⋯'));
|
|
1521
1545
|
}
|
|
1522
1546
|
|
|
1547
|
+
function MemberView({ t, agent, tasks, onBack, onOpenTask, onOpenChat, onOpenProfile }) {
|
|
1548
|
+
return h('div', { className: 'at-memberview' },
|
|
1549
|
+
h('div', { className: 'at-memberviewhead' },
|
|
1550
|
+
h('button', { className: 'at-btn at-btn-small', type: 'button', onClick: onBack }, '← ' + t('back')),
|
|
1551
|
+
h('span', { className: 'at-dot at-dot-' + (agent.status ?? 'idle'), 'aria-hidden': 'true' }),
|
|
1552
|
+
h('div', { className: 'at-memberviewid' },
|
|
1553
|
+
h('strong', null, agent.name),
|
|
1554
|
+
h('span', null, (agent.role ?? '') + (agent.qa ? ' · QA' : '') + ' · ' + t(AGENT_STATUS[agent.status] ?? 'agentIdle'))),
|
|
1555
|
+
h('div', { className: 'at-memberviewactions' },
|
|
1556
|
+
h('button', { className: 'at-btn at-btn-small at-btn-primary', type: 'button', onClick: () => onOpenChat(agent) }, t('agentChat')),
|
|
1557
|
+
h('button', { className: 'at-btn at-btn-small', type: 'button', onClick: () => onOpenProfile(agent) }, t('memberProfileTitle')))),
|
|
1558
|
+
h('div', { className: 'at-memberviewbody' },
|
|
1559
|
+
h('div', { className: 'at-memberviewsub' }, t('memberTasks') + ' · ' + tasks.length),
|
|
1560
|
+
tasks.length
|
|
1561
|
+
? h('div', { className: 'at-membertasklist' }, tasks.map((task) => h('button', {
|
|
1562
|
+
key: task.id,
|
|
1563
|
+
className: 'at-membertask',
|
|
1564
|
+
type: 'button',
|
|
1565
|
+
onClick: () => onOpenTask(task),
|
|
1566
|
+
},
|
|
1567
|
+
h('span', { className: 'at-membertaskno' }, '#' + taskNo(task)),
|
|
1568
|
+
h('span', { className: 'at-membertasktitle' }, task.title),
|
|
1569
|
+
h(StatusPill, { t, status: task.status }),
|
|
1570
|
+
h('span', { className: 'at-membertaskmeta' }, task.ownerId === agent.id ? t('taskOwner') : t('taskAssignee')))))
|
|
1571
|
+
: h(EmptyState, { t, title: t('memberNoTasks'), body: t('memberNoTasksBody'), compact: true })));
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1523
1574
|
function LeftPanel({ t, data, projectId, scope, tab, inboxUnread, onBack, onInbox, onBoard, onProject, onNewProject, onAgent, onAgentMore, onAddAgent, onRecruit }) {
|
|
1524
1575
|
const projects = data.projects.filter((project) => !project.archived);
|
|
1525
1576
|
const activeAgents = data.agents.filter((agent) => agent.status !== 'retired');
|
|
@@ -1994,6 +2045,7 @@ window.__ModuleLoader__.load({
|
|
|
1994
2045
|
const [workspaceDraft, setWorkspaceDraft] = React.useState('');
|
|
1995
2046
|
const [workspaceError, setWorkspaceError] = React.useState('');
|
|
1996
2047
|
const [workspaceBusy, setWorkspaceBusy] = React.useState(false);
|
|
2048
|
+
const [memberViewId, setMemberViewId] = React.useState(null);
|
|
1997
2049
|
const [nBusy, setNBusy] = React.useState(false);
|
|
1998
2050
|
const [nError, setNError] = React.useState('');
|
|
1999
2051
|
const [pSuccessor, setPSuccessor] = React.useState('');
|
|
@@ -2204,6 +2256,7 @@ window.__ModuleLoader__.load({
|
|
|
2204
2256
|
}
|
|
2205
2257
|
function goConversation() {
|
|
2206
2258
|
closeMobile();
|
|
2259
|
+
setMemberViewId(null);
|
|
2207
2260
|
setScope('project');
|
|
2208
2261
|
setTab('main');
|
|
2209
2262
|
setThreadOpen(false);
|
|
@@ -2215,6 +2268,7 @@ window.__ModuleLoader__.load({
|
|
|
2215
2268
|
}
|
|
2216
2269
|
function switchInbox() {
|
|
2217
2270
|
closeMobile();
|
|
2271
|
+
setMemberViewId(null);
|
|
2218
2272
|
setScope('inbox');
|
|
2219
2273
|
setThreadOpen(false);
|
|
2220
2274
|
setNote('');
|
|
@@ -2233,6 +2287,7 @@ window.__ModuleLoader__.load({
|
|
|
2233
2287
|
await runCommand('project.activate', { projectId: next.id });
|
|
2234
2288
|
setScope('project');
|
|
2235
2289
|
setTab('main');
|
|
2290
|
+
setMemberViewId(null);
|
|
2236
2291
|
setBoardFilter('all');
|
|
2237
2292
|
setThreadOpen(false);
|
|
2238
2293
|
setReply('');
|
|
@@ -2251,17 +2306,21 @@ window.__ModuleLoader__.load({
|
|
|
2251
2306
|
setNewOpen(true);
|
|
2252
2307
|
}
|
|
2253
2308
|
async function pickWorkspacePath() {
|
|
2309
|
+
const service = ctx?.get?.('uiWorkspace') ?? ctx?.uiWorkspace;
|
|
2310
|
+
if (service === undefined || service === null || typeof service.pickDirectory !== 'function') {
|
|
2311
|
+
return { error: 'unavailable', path: null };
|
|
2312
|
+
}
|
|
2254
2313
|
try {
|
|
2255
|
-
|
|
2256
|
-
return picked ?? null;
|
|
2314
|
+
return { path: await service.pickDirectory(), error: null };
|
|
2257
2315
|
} catch {
|
|
2258
|
-
return null;
|
|
2316
|
+
return { error: 'failed', path: null };
|
|
2259
2317
|
}
|
|
2260
2318
|
}
|
|
2261
2319
|
async function pickNewWorkspace() {
|
|
2262
2320
|
const picked = await pickWorkspacePath();
|
|
2263
|
-
if (picked) setNWorkspace(picked);
|
|
2264
|
-
else setNError(t('workspacePickUnavailable'));
|
|
2321
|
+
if (picked.path) setNWorkspace(picked.path);
|
|
2322
|
+
else if (picked.error === 'unavailable') setNError(t('workspacePickUnavailable'));
|
|
2323
|
+
else if (picked.error === 'failed') setNError(t('workspacePickFailed'));
|
|
2265
2324
|
}
|
|
2266
2325
|
function openWorkspaceEditor() {
|
|
2267
2326
|
setWorkspaceDraft(project?.workspacePath ?? '');
|
|
@@ -2270,8 +2329,9 @@ window.__ModuleLoader__.load({
|
|
|
2270
2329
|
}
|
|
2271
2330
|
async function pickProjectWorkspace() {
|
|
2272
2331
|
const picked = await pickWorkspacePath();
|
|
2273
|
-
if (picked) setWorkspaceDraft(picked);
|
|
2274
|
-
else setWorkspaceError(t('workspacePickUnavailable'));
|
|
2332
|
+
if (picked.path) setWorkspaceDraft(picked.path);
|
|
2333
|
+
else if (picked.error === 'unavailable') setWorkspaceError(t('workspacePickUnavailable'));
|
|
2334
|
+
else if (picked.error === 'failed') setWorkspaceError(t('workspacePickFailed'));
|
|
2275
2335
|
}
|
|
2276
2336
|
async function saveWorkspace() {
|
|
2277
2337
|
if (!project || workspaceBusy || !workspaceDraft.trim()) return;
|
|
@@ -2395,15 +2455,27 @@ window.__ModuleLoader__.load({
|
|
|
2395
2455
|
setMBusy(false);
|
|
2396
2456
|
}
|
|
2397
2457
|
}
|
|
2458
|
+
function openMemberView(agent) {
|
|
2459
|
+
closeMobile();
|
|
2460
|
+
setMemberViewId(agent.id);
|
|
2461
|
+
setScope('project');
|
|
2462
|
+
setTab('main');
|
|
2463
|
+
setThreadOpen(false);
|
|
2464
|
+
setNote('');
|
|
2465
|
+
}
|
|
2466
|
+
function closeMemberView() {
|
|
2467
|
+
setMemberViewId(null);
|
|
2468
|
+
}
|
|
2398
2469
|
async function openAgentChat(agent) {
|
|
2399
2470
|
closeMobile();
|
|
2400
|
-
|
|
2471
|
+
const service = ctx?.get?.('uiWorkspace') ?? ctx?.uiWorkspace;
|
|
2472
|
+
if (service?.openSession === undefined) {
|
|
2401
2473
|
say(t('agentChatUnavailable'));
|
|
2402
2474
|
openProfile(agent);
|
|
2403
2475
|
return;
|
|
2404
2476
|
}
|
|
2405
2477
|
if (agent.sessionId) {
|
|
2406
|
-
|
|
2478
|
+
service.openSession(agent.sessionId);
|
|
2407
2479
|
return;
|
|
2408
2480
|
}
|
|
2409
2481
|
try {
|
|
@@ -2413,7 +2485,7 @@ window.__ModuleLoader__.load({
|
|
|
2413
2485
|
prompt: '你正在与 Human 直接对话。先简要说明你是谁、当前负责什么;不要创建 Task,除非 Human 明确要求。',
|
|
2414
2486
|
});
|
|
2415
2487
|
await reloadData();
|
|
2416
|
-
if (activated?.sessionId)
|
|
2488
|
+
if (activated?.sessionId) service.openSession(activated.sessionId);
|
|
2417
2489
|
} catch (error) {
|
|
2418
2490
|
say(tf(t, 'sendFailed', { message: error?.message ?? String(error) }));
|
|
2419
2491
|
openProfile(agent);
|
|
@@ -2767,6 +2839,8 @@ window.__ModuleLoader__.load({
|
|
|
2767
2839
|
}
|
|
2768
2840
|
|
|
2769
2841
|
const inInbox = scope === 'inbox';
|
|
2842
|
+
const memberViewAgent = data.agents.find((agent) => agent.id === memberViewId) ?? null;
|
|
2843
|
+
const memberViewTasks = memberViewAgent === null ? [] : tasks.filter((task) => task.ownerId === memberViewAgent.id || task.assigneeId === memberViewAgent.id);
|
|
2770
2844
|
const headerTitle = inInbox ? t('inboxTitle') : project ? '# ' + project.name : t('noProject');
|
|
2771
2845
|
const headerGoal = inInbox ? t('inboxGoal') : (project?.goal ?? '');
|
|
2772
2846
|
const unread = inboxUnread > 99 ? '99+' : inboxUnread;
|
|
@@ -2783,7 +2857,7 @@ window.__ModuleLoader__.load({
|
|
|
2783
2857
|
onBoard: switchBoard,
|
|
2784
2858
|
onProject: switchProject,
|
|
2785
2859
|
onNewProject: () => { closeMobile(); openNewProject(); },
|
|
2786
|
-
onAgent: (agent) => { closeMobile();
|
|
2860
|
+
onAgent: (agent) => { closeMobile(); openMemberView(agent); },
|
|
2787
2861
|
onAgentMore: (agent) => { closeMobile(); openProfile(agent); },
|
|
2788
2862
|
onAddAgent: () => { closeMobile(); openAddAgent(); },
|
|
2789
2863
|
onRecruit: (agent) => { closeMobile(); recruit(agent); },
|
|
@@ -2818,6 +2892,16 @@ window.__ModuleLoader__.load({
|
|
|
2818
2892
|
body: t('inboxEmptyBody'),
|
|
2819
2893
|
action: { label: t('inboxEmptyAction'), onClick: switchBoard },
|
|
2820
2894
|
})))
|
|
2895
|
+
: memberViewAgent && tab === 'main'
|
|
2896
|
+
? h(MemberView, {
|
|
2897
|
+
t,
|
|
2898
|
+
agent: memberViewAgent,
|
|
2899
|
+
tasks: memberViewTasks,
|
|
2900
|
+
onBack: closeMemberView,
|
|
2901
|
+
onOpenTask: openTask,
|
|
2902
|
+
onOpenChat: (agent) => { void openAgentChat(agent); },
|
|
2903
|
+
onOpenProfile: openProfile,
|
|
2904
|
+
})
|
|
2821
2905
|
: tab === 'board'
|
|
2822
2906
|
? h(Board, {
|
|
2823
2907
|
t,
|
|
@@ -2914,7 +2998,7 @@ window.__ModuleLoader__.load({
|
|
|
2914
2998
|
h('span', { className: 'at-notetext' }, note),
|
|
2915
2999
|
h('button', { className: 'at-noteclose', type: 'button', 'aria-label': t('dismiss'), title: t('dismiss'), onClick: () => setNote('') }, h(Ico, { name: 'close', size: 12 })))) : null,
|
|
2916
3000
|
body,
|
|
2917
|
-
!inInbox && tab === 'main' ? h(Composer, {
|
|
3001
|
+
!inInbox && tab === 'main' && !memberViewAgent ? h(Composer, {
|
|
2918
3002
|
t,
|
|
2919
3003
|
mode,
|
|
2920
3004
|
draft,
|
package/index.js
CHANGED
|
@@ -33,7 +33,7 @@ export const name = 'ai-team';
|
|
|
33
33
|
/** The workbench only makes sense where the Web carrier exists. */
|
|
34
34
|
export const inject = ['webServer', 'permissionPresets', 'sandboxPolicy'];
|
|
35
35
|
|
|
36
|
-
const VERSION = '0.3.
|
|
36
|
+
const VERSION = '0.3.13';
|
|
37
37
|
const PROJECT_ID = 'prj_main';
|
|
38
38
|
const HUMAN_ID = 'mem_human';
|
|
39
39
|
|
package/lib/host/service.js
CHANGED
|
@@ -101,7 +101,9 @@ export class TeamService {
|
|
|
101
101
|
title: task.title,
|
|
102
102
|
status: task.status,
|
|
103
103
|
owner: state.members?.[task.ownerId]?.name ?? task.ownerId,
|
|
104
|
+
ownerId: task.ownerId,
|
|
104
105
|
assignee: task.assigneeId === null || task.assigneeId === undefined ? null : (state.members?.[task.assigneeId]?.name ?? task.assigneeId),
|
|
106
|
+
assigneeId: task.assigneeId ?? null,
|
|
105
107
|
replies: state.threads?.[task.id]?.headSeq ?? 0,
|
|
106
108
|
following: (state.followers?.[task.id] ?? []).length > 0,
|
|
107
109
|
blockedBy: task.blockedBy ?? [],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suifei/dsh-ai-team",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.13",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "文渊阁 (Wenyuan Pavilion) — AI Team mode for DeepSeek Harness: concurrent projects, owned tasks, threads, inbox and an append-only team ledger",
|
|
6
6
|
"license": "MIT",
|