@suifei/dsh-ai-team 0.3.12 → 0.3.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.14 — 2026-11-10
4
+
5
+ - 主对话顶部增加「团队动态」:显示正在推进的 Task、owner 和运行中的成员;
6
+ - 点击团队动态里的 Task 直接打开 Thread;没有 Task 时显示“成员已唤醒,正在拆解”;
7
+ - 解决 Human 发消息后主对话没有反馈、只能去任务看板找 Task 的问题。
8
+
9
+ ## 0.3.13 — 2026-11-10
10
+
11
+ - 点击成员名字进入成员视图:该成员负责/执行的任务列表 + 直接对话 + 资料入口;
12
+ - 目录选择改用 `ctx.get('uiWorkspace').pickDirectory()`,修复「当前宿主不支持目录选择」;
13
+ - 选择目录取消不再误报为不支持;保留手填路径兜底。
14
+
3
15
  ## 0.3.12 — 2026-11-10
4
16
 
5
17
  - 点击成员名字直接打开该 Agent 的 DSH 原生 Session,可与它直接对话;
package/README.md CHANGED
@@ -84,11 +84,13 @@ dsh --profile ai-team --no-open --port 33271
84
84
  └─────────────────┴────────────────────────────────────┴─────────────┘
85
85
  ```
86
86
 
87
- - **主对话**:Human 的一句话进入 Main stream,Project owner 拆 Task;Composer 支持讨论 / 任务模式。
87
+ - **主对话**:Human 的一句话进入 Main stream,Project owner 拆 Task;顶部「团队动态」直接显示正在推进的 Task 和运行中的成员,点击 Task 可打开 Thread。
88
88
  - **看板**:四列;进行中的卡片显示 accountable owner 与执行者;数量只统计 active Task。
89
89
  - **态势**:四象限——需要你、团队在做、等待受阻、最近完成。点击条目打开对应 Task 的 Thread。
90
90
  - **收件箱**:跨 Project 聚合;失败决策、需要你决定、关注 Task 的完成报告都在这里。
91
- - **成员资料**:改名、职责、personality、provider/model、运行配方、notes、skills、re-distill、离队/人才库。
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;点击名字直接打开该 Agent 的 DSH 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,16 @@ 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: '执行',
191
+ teamPulseTitle: '团队动态',
192
+ teamPulseCount: '{n} 个 Task 正在推进',
193
+ teamPulseWorking: '成员已接单',
194
+ teamPulseStarting: '成员已唤醒,正在拆解任务…',
195
+ teamPulseAgents: '运行中:{names}',
186
196
  newProject: '新建项目',
187
197
  addAgent: '添加成员',
188
198
  newProjectMock: 'spike:新建项目属于 S-01 流程,M1 之后接入。',
@@ -473,6 +483,16 @@ window.__ModuleLoader__.load({
473
483
  agentChat: 'Direct chat',
474
484
  agentChatOpening: 'Opening conversation…',
475
485
  agentChatUnavailable: 'This host cannot open a Session.',
486
+ memberTasks: 'Their tasks',
487
+ memberNoTasks: 'No Task belongs to this member yet',
488
+ memberNoTasksBody: 'Create or assign a Task in the Project and it appears here.',
489
+ taskOwner: 'Owner',
490
+ taskAssignee: 'Assignee',
491
+ teamPulseTitle: 'Team activity',
492
+ teamPulseCount: '{n} Task(s) in motion',
493
+ teamPulseWorking: 'Members picked up the work',
494
+ teamPulseStarting: 'Members are awake and breaking the work down…',
495
+ teamPulseAgents: 'Running: {names}',
476
496
  newProject: 'New project',
477
497
  addAgent: 'Add member',
478
498
  newProjectMock: 'spike: project creation belongs to S-01 and lands after M1.',
@@ -938,6 +958,32 @@ window.__ModuleLoader__.load({
938
958
  .at-agentfail { flex: none; color: var(--dsw-alias-state-warn-primary); }
939
959
  .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
960
  .at-agentmore:hover { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-primary); }
961
+ .at-teampulse { width: 100%; max-width: 760px; margin: 4px auto 10px; padding: 10px 12px; border: 1px solid var(--dsw-alias-state-business-primary); border-radius: var(--dsw-radius-md); background: var(--dsw-alias-bg-module-platform); }
962
+ .at-teampulsehead { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 6px; }
963
+ .at-teampulsetitle { font-size: 12px; font-weight: 600; color: var(--dsw-alias-label-primary); }
964
+ .at-teampulsecount { font-size: 11px; color: var(--dsw-alias-state-business-primary); }
965
+ .at-teampulselist { display: flex; flex-direction: column; gap: 4px; }
966
+ .at-teampulsetask { display: flex; align-items: center; gap: 7px; width: 100%; padding: 4px 6px; border: 0; border-radius: var(--dsw-radius-sm); background: transparent; color: var(--dsw-alias-label-primary); font: inherit; text-align: left; cursor: pointer; }
967
+ .at-teampulsetask:hover { background: var(--dsw-alias-interactive-bg-hover); }
968
+ .at-teampulseno { flex: 0 0 auto; color: var(--dsw-alias-label-tertiary); font-size: 11px; font-variant-numeric: tabular-nums; }
969
+ .at-teampulsetasktitle { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; }
970
+ .at-teampulseowner { flex: 0 0 auto; color: var(--dsw-alias-label-tertiary); font-size: 11px; }
971
+ .at-teampulsehint { color: var(--dsw-alias-label-tertiary); font-size: 11px; line-height: 16px; }
972
+ .at-teampulseagents { margin-top: 6px; color: var(--dsw-alias-label-tertiary); font-size: 11px; line-height: 16px; }
973
+ .at-memberview { flex: 1 1 auto; min-height: 0; overflow: auto; padding: 18px; }
974
+ .at-memberviewhead { display: flex; align-items: center; flex-wrap: wrap; gap: 8px; padding-bottom: 12px; border-bottom: 1px solid var(--dsw-alias-border-l1); }
975
+ .at-memberviewid { display: flex; flex-direction: column; min-width: 0; flex: 1 1 180px; }
976
+ .at-memberviewid strong { font-size: 15px; line-height: 22px; }
977
+ .at-memberviewid span { color: var(--dsw-alias-label-tertiary); font-size: 11px; line-height: 16px; }
978
+ .at-memberviewactions { display: flex; gap: 6px; flex-wrap: wrap; }
979
+ .at-memberviewbody { max-width: 760px; margin: 0 auto; padding-top: 14px; }
980
+ .at-memberviewsub { color: var(--dsw-alias-label-tertiary); font-size: 12px; line-height: 18px; margin-bottom: 8px; }
981
+ .at-membertasklist { display: flex; flex-direction: column; gap: 6px; }
982
+ .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; }
983
+ .at-membertask:hover { border-color: var(--dsw-alias-border-l2); background: var(--dsw-alias-interactive-bg-hover); }
984
+ .at-membertaskno { flex: 0 0 auto; color: var(--dsw-alias-label-tertiary); font-variant-numeric: tabular-nums; font-size: 11px; }
985
+ .at-membertasktitle { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; }
986
+ .at-membertaskmeta { flex: 0 0 auto; color: var(--dsw-alias-label-tertiary); font-size: 11px; }
941
987
  .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
988
  .at-chead { flex: 0 0 auto; padding: 14px 18px 0; }
943
989
  .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 +1566,56 @@ window.__ModuleLoader__.load({
1520
1566
  }, '⋯'));
1521
1567
  }
1522
1568
 
1569
+ function TeamPulse({ t, agents, tasks, onOpen }) {
1570
+ const active = tasks.filter((task) => ['pending', 'in_progress', 'pending_acceptance'].includes(task.status)).slice(-5);
1571
+ const running = agents.filter((agent) => agent.status === 'running');
1572
+ if (active.length === 0 && running.length === 0) return null;
1573
+ return h('section', { className: 'at-teampulse', 'aria-label': t('teamPulseTitle') },
1574
+ h('div', { className: 'at-teampulsehead' },
1575
+ h('span', { className: 'at-teampulsetitle' }, t('teamPulseTitle')),
1576
+ h('span', { className: 'at-teampulsecount' }, active.length ? t('teamPulseCount').replace('{n}', String(active.length)) : t('teamPulseWorking'))),
1577
+ active.length
1578
+ ? h('div', { className: 'at-teampulselist' }, active.map((task) => h('button', {
1579
+ key: task.id,
1580
+ className: 'at-teampulsetask',
1581
+ type: 'button',
1582
+ onClick: () => onOpen(task),
1583
+ },
1584
+ h('span', { className: 'at-teampulseno' }, '#' + taskNo(task)),
1585
+ h('span', { className: 'at-teampulsetasktitle' }, task.title),
1586
+ h(StatusPill, { t, status: task.status }),
1587
+ h('span', { className: 'at-teampulseowner' }, task.owner ?? ''))))
1588
+ : h('div', { className: 'at-teampulsehint' }, t('teamPulseStarting')),
1589
+ running.length ? h('div', { className: 'at-teampulseagents' }, t('teamPulseAgents').replace('{names}', running.map((agent) => agent.name).join('、'))) : null);
1590
+ }
1591
+
1592
+ function MemberView({ t, agent, tasks, onBack, onOpenTask, onOpenChat, onOpenProfile }) {
1593
+ return h('div', { className: 'at-memberview' },
1594
+ h('div', { className: 'at-memberviewhead' },
1595
+ h('button', { className: 'at-btn at-btn-small', type: 'button', onClick: onBack }, '← ' + t('back')),
1596
+ h('span', { className: 'at-dot at-dot-' + (agent.status ?? 'idle'), 'aria-hidden': 'true' }),
1597
+ h('div', { className: 'at-memberviewid' },
1598
+ h('strong', null, agent.name),
1599
+ h('span', null, (agent.role ?? '') + (agent.qa ? ' · QA' : '') + ' · ' + t(AGENT_STATUS[agent.status] ?? 'agentIdle'))),
1600
+ h('div', { className: 'at-memberviewactions' },
1601
+ h('button', { className: 'at-btn at-btn-small at-btn-primary', type: 'button', onClick: () => onOpenChat(agent) }, t('agentChat')),
1602
+ h('button', { className: 'at-btn at-btn-small', type: 'button', onClick: () => onOpenProfile(agent) }, t('memberProfileTitle')))),
1603
+ h('div', { className: 'at-memberviewbody' },
1604
+ h('div', { className: 'at-memberviewsub' }, t('memberTasks') + ' · ' + tasks.length),
1605
+ tasks.length
1606
+ ? h('div', { className: 'at-membertasklist' }, tasks.map((task) => h('button', {
1607
+ key: task.id,
1608
+ className: 'at-membertask',
1609
+ type: 'button',
1610
+ onClick: () => onOpenTask(task),
1611
+ },
1612
+ h('span', { className: 'at-membertaskno' }, '#' + taskNo(task)),
1613
+ h('span', { className: 'at-membertasktitle' }, task.title),
1614
+ h(StatusPill, { t, status: task.status }),
1615
+ h('span', { className: 'at-membertaskmeta' }, task.ownerId === agent.id ? t('taskOwner') : t('taskAssignee')))))
1616
+ : h(EmptyState, { t, title: t('memberNoTasks'), body: t('memberNoTasksBody'), compact: true })));
1617
+ }
1618
+
1523
1619
  function LeftPanel({ t, data, projectId, scope, tab, inboxUnread, onBack, onInbox, onBoard, onProject, onNewProject, onAgent, onAgentMore, onAddAgent, onRecruit }) {
1524
1620
  const projects = data.projects.filter((project) => !project.archived);
1525
1621
  const activeAgents = data.agents.filter((agent) => agent.status !== 'retired');
@@ -1994,6 +2090,7 @@ window.__ModuleLoader__.load({
1994
2090
  const [workspaceDraft, setWorkspaceDraft] = React.useState('');
1995
2091
  const [workspaceError, setWorkspaceError] = React.useState('');
1996
2092
  const [workspaceBusy, setWorkspaceBusy] = React.useState(false);
2093
+ const [memberViewId, setMemberViewId] = React.useState(null);
1997
2094
  const [nBusy, setNBusy] = React.useState(false);
1998
2095
  const [nError, setNError] = React.useState('');
1999
2096
  const [pSuccessor, setPSuccessor] = React.useState('');
@@ -2204,6 +2301,7 @@ window.__ModuleLoader__.load({
2204
2301
  }
2205
2302
  function goConversation() {
2206
2303
  closeMobile();
2304
+ setMemberViewId(null);
2207
2305
  setScope('project');
2208
2306
  setTab('main');
2209
2307
  setThreadOpen(false);
@@ -2215,6 +2313,7 @@ window.__ModuleLoader__.load({
2215
2313
  }
2216
2314
  function switchInbox() {
2217
2315
  closeMobile();
2316
+ setMemberViewId(null);
2218
2317
  setScope('inbox');
2219
2318
  setThreadOpen(false);
2220
2319
  setNote('');
@@ -2233,6 +2332,7 @@ window.__ModuleLoader__.load({
2233
2332
  await runCommand('project.activate', { projectId: next.id });
2234
2333
  setScope('project');
2235
2334
  setTab('main');
2335
+ setMemberViewId(null);
2236
2336
  setBoardFilter('all');
2237
2337
  setThreadOpen(false);
2238
2338
  setReply('');
@@ -2251,17 +2351,21 @@ window.__ModuleLoader__.load({
2251
2351
  setNewOpen(true);
2252
2352
  }
2253
2353
  async function pickWorkspacePath() {
2354
+ const service = ctx?.get?.('uiWorkspace') ?? ctx?.uiWorkspace;
2355
+ if (service === undefined || service === null || typeof service.pickDirectory !== 'function') {
2356
+ return { error: 'unavailable', path: null };
2357
+ }
2254
2358
  try {
2255
- const picked = await ctx?.uiWorkspace?.pickDirectory?.();
2256
- return picked ?? null;
2359
+ return { path: await service.pickDirectory(), error: null };
2257
2360
  } catch {
2258
- return null;
2361
+ return { error: 'failed', path: null };
2259
2362
  }
2260
2363
  }
2261
2364
  async function pickNewWorkspace() {
2262
2365
  const picked = await pickWorkspacePath();
2263
- if (picked) setNWorkspace(picked);
2264
- else setNError(t('workspacePickUnavailable'));
2366
+ if (picked.path) setNWorkspace(picked.path);
2367
+ else if (picked.error === 'unavailable') setNError(t('workspacePickUnavailable'));
2368
+ else if (picked.error === 'failed') setNError(t('workspacePickFailed'));
2265
2369
  }
2266
2370
  function openWorkspaceEditor() {
2267
2371
  setWorkspaceDraft(project?.workspacePath ?? '');
@@ -2270,8 +2374,9 @@ window.__ModuleLoader__.load({
2270
2374
  }
2271
2375
  async function pickProjectWorkspace() {
2272
2376
  const picked = await pickWorkspacePath();
2273
- if (picked) setWorkspaceDraft(picked);
2274
- else setWorkspaceError(t('workspacePickUnavailable'));
2377
+ if (picked.path) setWorkspaceDraft(picked.path);
2378
+ else if (picked.error === 'unavailable') setWorkspaceError(t('workspacePickUnavailable'));
2379
+ else if (picked.error === 'failed') setWorkspaceError(t('workspacePickFailed'));
2275
2380
  }
2276
2381
  async function saveWorkspace() {
2277
2382
  if (!project || workspaceBusy || !workspaceDraft.trim()) return;
@@ -2395,15 +2500,27 @@ window.__ModuleLoader__.load({
2395
2500
  setMBusy(false);
2396
2501
  }
2397
2502
  }
2503
+ function openMemberView(agent) {
2504
+ closeMobile();
2505
+ setMemberViewId(agent.id);
2506
+ setScope('project');
2507
+ setTab('main');
2508
+ setThreadOpen(false);
2509
+ setNote('');
2510
+ }
2511
+ function closeMemberView() {
2512
+ setMemberViewId(null);
2513
+ }
2398
2514
  async function openAgentChat(agent) {
2399
2515
  closeMobile();
2400
- if (ctx?.uiWorkspace?.openSession === undefined) {
2516
+ const service = ctx?.get?.('uiWorkspace') ?? ctx?.uiWorkspace;
2517
+ if (service?.openSession === undefined) {
2401
2518
  say(t('agentChatUnavailable'));
2402
2519
  openProfile(agent);
2403
2520
  return;
2404
2521
  }
2405
2522
  if (agent.sessionId) {
2406
- ctx.uiWorkspace.openSession(agent.sessionId);
2523
+ service.openSession(agent.sessionId);
2407
2524
  return;
2408
2525
  }
2409
2526
  try {
@@ -2413,7 +2530,7 @@ window.__ModuleLoader__.load({
2413
2530
  prompt: '你正在与 Human 直接对话。先简要说明你是谁、当前负责什么;不要创建 Task,除非 Human 明确要求。',
2414
2531
  });
2415
2532
  await reloadData();
2416
- if (activated?.sessionId) ctx.uiWorkspace.openSession(activated.sessionId);
2533
+ if (activated?.sessionId) service.openSession(activated.sessionId);
2417
2534
  } catch (error) {
2418
2535
  say(tf(t, 'sendFailed', { message: error?.message ?? String(error) }));
2419
2536
  openProfile(agent);
@@ -2767,6 +2884,8 @@ window.__ModuleLoader__.load({
2767
2884
  }
2768
2885
 
2769
2886
  const inInbox = scope === 'inbox';
2887
+ const memberViewAgent = data.agents.find((agent) => agent.id === memberViewId) ?? null;
2888
+ const memberViewTasks = memberViewAgent === null ? [] : tasks.filter((task) => task.ownerId === memberViewAgent.id || task.assigneeId === memberViewAgent.id);
2770
2889
  const headerTitle = inInbox ? t('inboxTitle') : project ? '# ' + project.name : t('noProject');
2771
2890
  const headerGoal = inInbox ? t('inboxGoal') : (project?.goal ?? '');
2772
2891
  const unread = inboxUnread > 99 ? '99+' : inboxUnread;
@@ -2783,7 +2902,7 @@ window.__ModuleLoader__.load({
2783
2902
  onBoard: switchBoard,
2784
2903
  onProject: switchProject,
2785
2904
  onNewProject: () => { closeMobile(); openNewProject(); },
2786
- onAgent: (agent) => { closeMobile(); void openAgentChat(agent); },
2905
+ onAgent: (agent) => { closeMobile(); openMemberView(agent); },
2787
2906
  onAgentMore: (agent) => { closeMobile(); openProfile(agent); },
2788
2907
  onAddAgent: () => { closeMobile(); openAddAgent(); },
2789
2908
  onRecruit: (agent) => { closeMobile(); recruit(agent); },
@@ -2818,6 +2937,16 @@ window.__ModuleLoader__.load({
2818
2937
  body: t('inboxEmptyBody'),
2819
2938
  action: { label: t('inboxEmptyAction'), onClick: switchBoard },
2820
2939
  })))
2940
+ : memberViewAgent && tab === 'main'
2941
+ ? h(MemberView, {
2942
+ t,
2943
+ agent: memberViewAgent,
2944
+ tasks: memberViewTasks,
2945
+ onBack: closeMemberView,
2946
+ onOpenTask: openTask,
2947
+ onOpenChat: (agent) => { void openAgentChat(agent); },
2948
+ onOpenProfile: openProfile,
2949
+ })
2821
2950
  : tab === 'board'
2822
2951
  ? h(Board, {
2823
2952
  t,
@@ -2837,6 +2966,7 @@ window.__ModuleLoader__.load({
2837
2966
  ? h(Situation, { t, quadrants, tasks, onItem: openQuadrantItem, onViewAll: viewQuadrant })
2838
2967
  : h('div', { className: 'at-stream', role: 'tabpanel', id: 'at-panel-main', 'aria-labelledby': 'at-tab-main' },
2839
2968
  h('div', { className: 'at-streaminner', role: 'log', 'aria-live': 'polite', 'aria-label': t('streamAria') },
2969
+ h(TeamPulse, { t, agents: data.agents, tasks, onOpen: openTask }),
2840
2970
  data.stream.length
2841
2971
  ? data.stream.map((item) => h(StreamMessage, {
2842
2972
  key: item.id,
@@ -2914,7 +3044,7 @@ window.__ModuleLoader__.load({
2914
3044
  h('span', { className: 'at-notetext' }, note),
2915
3045
  h('button', { className: 'at-noteclose', type: 'button', 'aria-label': t('dismiss'), title: t('dismiss'), onClick: () => setNote('') }, h(Ico, { name: 'close', size: 12 })))) : null,
2916
3046
  body,
2917
- !inInbox && tab === 'main' ? h(Composer, {
3047
+ !inInbox && tab === 'main' && !memberViewAgent ? h(Composer, {
2918
3048
  t,
2919
3049
  mode,
2920
3050
  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.12';
36
+ const VERSION = '0.3.14';
37
37
  const PROJECT_ID = 'prj_main';
38
38
  const HUMAN_ID = 'mem_human';
39
39
 
@@ -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.12",
3
+ "version": "0.3.14",
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",