dsh-plugin-teamflow 0.1.5 → 0.1.7
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 +43 -0
- package/README.md +15 -0
- package/lib/client.js +216 -23
- package/lib/host.mjs +770 -182
- package/lib/store.mjs +3 -1
- package/package.json +14 -8
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,49 @@
|
|
|
2
2
|
|
|
3
3
|
> 本插件首次公开发布版本为 **v0.1.0**;发布前的内部迭代(v0.3~v0.13)记录于 `AGENTS.md` §5,对外统一归到 v0.1.0。
|
|
4
4
|
|
|
5
|
+
## [0.1.7] - 2026-09-11
|
|
6
|
+
|
|
7
|
+
### 新增
|
|
8
|
+
- **工作台产物一键预览**:任务卡详情里的「任务夹」现在按真实存在的产物列按钮(PRD / DESIGN / TECHNICAL / QA-REPORT / ACCEPTANCE / meta),点一下即在 DSH **右侧栏**打开预览(Markdown 由官方文档预览器接管)。地址由 host 用官方 `fileAddressFor` 生成(`dsh-resource://file/session/<id>/<相对路径>`)——客户端不拼地址、也不引宿主包进 client bundle;只列真实存在的文件(不出死按钮);右侧栏服务缺失时静默降级
|
|
9
|
+
- **产物交付(`present`)**:prd/tech/qa/acceptance 四个阶段被要求把任务夹产物交给官方 `present` 工具 → 用户在该会话得到「交付文件卡」(预览 / 默认程序打开 / 文件管理器定位)。诚实标注为 `[policy]` 增强项:文件仍是唯一事实源,缺文件依旧是硬失败;卡片渲染在**该子代理会话**的轮次尾部(主会话不显示)
|
|
10
|
+
- **机械阶段推理强度降档(省 token)**:DeepSeek 路由默认 `reasoningEffort: high`,而推理 token **计入 output** 且**推理内容每个带推理回合原样回传**(同时抬高后续 input)。现对两处机械阶段下发 `low`——patch 档的「单点确认」与 `scaffold`(脚手架落地);判据类阶段(PRD/设计/技术方案/QA/验收)保持宿主默认 `high`,**重试自动回升 `high`**(质量优先)。安全前提:先经 `llm.resolveModelInfo()` 探测该路由的 `reasoning.efforts`,只有声明支持才下发——宿主对不支持的值会 `UNSUPPORTED_REASONING_EFFORT` 硬失败且不降级;探测结果按 provider/model 缓存。阶段日志记录实际下发的档位
|
|
11
|
+
|
|
12
|
+
### 修复
|
|
13
|
+
- **token 计量改走官方 Session 投影(宿主弃用同步事件读取器)**:dsh 0.1.5-rc.2 起 `Session.eventAt()` / `snapshotEvents()` / `ownEvents()` 标记为 deprecated(存量可留、新调用禁止,宿主方向是不再把完整事件序列常驻内存)。计量来源改为**官方投影优先**——`ctx.sessionProjections.stateOf(session,'tokenUsage')` 取四桶(与宿主 token-meter 同一份 fold,重试替换语义更准)+ `'sessionStats'.steps` 取调用数,**零历史扫描**;投影缺失/无数据/读取异常时静默回退原事件扫描(最小 profile 与存量宿主不受影响,不虚报 0,不中断流水线)。`sessionProjections` 走可选 `ctx.inject`,不进 `static inject`——服务缺失时插件照常加载
|
|
14
|
+
- **护栏适配官方通道(提醒 + 挂死检测)**:轻提醒从手写 `session.append('user/message')` + step/end flush 时序状态机,改为官方 `run.localAgent.inject()`(宿主在协议安全边界整批认领,旧注释声称的「会插进 tool_calls→tool_result 触发 400」不成立);挂死检测从「多源取最长事件视图」长度启发式改为官方 **`subagentTiming` 投影**的 `active.through`(已提交事件时间,不受视图失明影响——上次 QA 误判 stalled 的根因)。长工具静默执行仍由 agent 活动守卫豁免;投影不可用时回退旧启发式。中止语义未变
|
|
15
|
+
|
|
16
|
+
### 改进
|
|
17
|
+
- **声明宿主兼容窗口**:`package.json` 新增 `engines.dsh: ">=0.1.5-rc.2 <0.2.0"` 与 `dsh.manifestVersion: 1`(dsh 0.1.5 起支持的公共 manifest 字段;当前宿主不校验,属作者声明);README「版本锚定」段同步到 v0.1.5-rc.2,并记录本次兼容核对结论与两个待跟进项
|
|
18
|
+
- **清理死注入**:`static inject` 长期硬注入 `tokenMeter` 却全仓从未使用 → 从 static inject / setRuntime / runtime 三处移除(假依赖会拖累插件的加载条件)
|
|
19
|
+
- **工作区 key 文档纠偏**:`workspaceScopeOf` 的「优先用 DSH workspace UUID」分支**当前不可达**(宿主 `resolveByPath` 是异步、我们同步调用),实际生效的是路径派生 `slugPath(cwd)`;注释与 AGENTS.md 改为事实描述,真修(改异步 + 存储 key 迁移)列入 `docs/TODO.md` 待 v0.1.8
|
|
20
|
+
|
|
21
|
+
### 已知待办
|
|
22
|
+
- **护栏复读检测仍读已弃用的事件读取器**(提醒通道与挂死检测已改官方;复读需要流式文本内容):官方替代是订阅 `'session/event'` post-commit 投递,需先定等价判据,见 `docs/TODO.md`(当前行为不变,存量调用被宿主明确允许)
|
|
23
|
+
|
|
24
|
+
## [0.1.6] - 2026-09-07
|
|
25
|
+
|
|
26
|
+
### 新增
|
|
27
|
+
- **评测层 L1+L2(prompt/注入改动收益评测)**:每次改 prompt/注入后可直接验证收益——L1 行为级契约测试(直接调用 prompt 工厂断言产出锚点,29 条契约分 HOST-ENFORCED/policy/structural 三级,改 prompt 一眼看出断了哪条、丢哪级保障)+ L2 回放语料一致性门禁(冻结真实形状的 QA-REPORT/ACCEPTANCE/dev 回复/蓝图产物,喂宿主真实解析器做 golden corpus 回归,零 LLM 成本)。语料只增不改,防评测过拟合
|
|
28
|
+
- **dev/qaFix 验证证据块**:开发回复末尾强制「[Verification evidence]」块(命令+退出码+断言计数+失败行引用,或显式 N/A),host 提取存证、阶段详情可见、可与命令日志交叉核对——开发阶段从「单方宣称全绿」升级为「可审计的具体自述」
|
|
29
|
+
- **代码级英文化(为 UI i18n 铺路)**:代码判断/命名全英文(阶段键 prd/design/scaffold/tech/dev/qa/acceptance、任务结构化键 taskKey),中文只留展示层;存量数据提供迁移脚本
|
|
30
|
+
|
|
31
|
+
### 改进
|
|
32
|
+
- **resume 断点续跑状态机化**:两级状态(大阶段 + 子图任务)聚合判定——有 done 尝试的任务即成功(历史失败不算失败),resume 只补跑「聚合后未成功」任务、复用已完成产物;不再读 backlog 子卡(残留失败卡不再污染判定);dev 部分成功时起点精确回开发补跑未完成,全 done 时正确落在 QA/验收
|
|
33
|
+
- **输出单轨制**:QA/验收的完整报告只写在任务夹文件(QA-REPORT.md/ACCEPTANCE.md),子代理回复仅摘要+路径——host 直接导入文件(缺陷表/核对表),杜绝「回复与文件不一致」的双轨问题
|
|
34
|
+
- **验收结论契约强度**:验收报告结论行必须为字面量模板(✅/⚠️/❌/📝 四档)且为文件最后一行;无结论行/空结论行 → 需人工确认(不再默认通过,防质量门禁漏报)
|
|
35
|
+
- **prompt 约束分级**:区分 [HOST-ENFORCED](host 真实强制,如单轨产物/结论行)与 [policy](自律 + 轻提醒),prompt 内不再自称 hard constraint
|
|
36
|
+
- **重试诊断包**:重试时把上次失败详情(outcome/护栏原因/拒绝词命中点/产出尾部)附进 prompt——盲试 → 带因重试;stalled(挂死/空转)不再自动重试
|
|
37
|
+
|
|
38
|
+
### 修复
|
|
39
|
+
- **token 计量适配宿主 session v2**:宿主新版会话已无 `events` 属性,计量读不到 → 流水线卡与图卡无「TOKEN · 官方口径」。修复:多源回退(snapshotEvents/ownEvents)+ usage 双路径(含 stream 内嵌),新 run 计量恢复
|
|
40
|
+
- **护栏挂死误杀**:QA 子代理正常干活却被判「10 分钟无事件」(宿主事件视图失明)——事件读取多源回退 + agent 活动守卫(非 idle 且已动手即不中止)
|
|
41
|
+
- **任务卡 token 统计**:子卡 usage 按 stage 引用直写(并发下不再错位/超计)
|
|
42
|
+
- **存量迁移脚本 taskKey 误判**:合法中文括号结尾的任务键不再被当作历史残留(幂等无损但计数虚高)
|
|
43
|
+
- **验收空结论行/裸否定词漏报**:四档词白名单校验覆盖 accepted 分支
|
|
44
|
+
|
|
45
|
+
### 其他
|
|
46
|
+
- AGENTS.md 注入面瘦身 64%;dsh 0.1.3-alpha.1 兼容性核对(依赖包名更新)
|
|
47
|
+
|
|
5
48
|
## [0.1.5] - 2026-09-01
|
|
6
49
|
|
|
7
50
|
### 新增
|
package/README.md
CHANGED
|
@@ -119,6 +119,21 @@ dsh-plugin-teamflow/
|
|
|
119
119
|
- Node.js ≥ 22.18;
|
|
120
120
|
- 依赖宿主提供的 `@deepseek-ai/dsh-*` 与 `react`(peerDependencies,宿主注入,无需单独安装)。
|
|
121
121
|
|
|
122
|
+
### 版本锚定(dsh 宿主兼容性)
|
|
123
|
+
|
|
124
|
+
本插件开发与验证基于 **dsh v0.1.5-rc.2(2026-09-10,tag `dsh-v0.1.5-rc.2`)**;npm 侧 `next`=0.1.5-rc.2、`latest`=0.1.5-rc.1(`latest` 常滞后于 `next`,勿以 latest 判断发布线)。`peerDependencies` 保持 `*`(宿主注入,宽松兼容),并在 `package.json` 声明兼容窗口 **`engines.dsh: ">=0.1.5-rc.2 <0.2.0"`** 与 **`dsh.manifestVersion: 1`**——当前 dsh 不读取/校验这两个字段(源码内仅有类型声明),属作者声明性元数据。
|
|
125
|
+
|
|
126
|
+
2026-09-10 兼容性核对(dsh 0.1.5-rc.2):插件面板 Slot(原 `conversation` 根 slot → `main` 下的 `conversation` key)、会话格式 V3 + Session 生命周期(`SessionHandle`、异步 `agentLoop.create()`、会话锁)、`ctx.agent` 移除与 Inbox 类型化、SDK/Headless/ACP 默认工具调整、subprocess handle 去除 pid——**插件全部兼容**(未使用被改动的接口;`conversation.view` / `conversation.input.right` 声明未变,slot 树无删除)。其中一项需要跟进:
|
|
127
|
+
|
|
128
|
+
- **Session 同步事件读取器已弃用**(`session.eventAt()` / `snapshotEvents()` / `ownEvents()`,宿主 2026-09-09 起「存量可留、新调用禁止」,方向是不再把完整事件序列常驻内存):**token 计量已改为官方 Session 投影优先**(`ctx.sessionProjections.stateOf(session,'tokenUsage')` 取四桶 + `'sessionStats'.steps` 取调用数),事件扫描降级为无投影宿主的回退;**护栏的提醒通道已改官方 `Agent.inject()`、挂死判据已改用官方 `subagentTiming` 投影的 `active.through`**(长工具静默仍由 agent 活动守卫豁免),只剩**复读检测**仍在读事件(需要流式文本内容,官方替代=订阅 `'session/event'` post-commit 投递,需先定等价判据,见 `docs/TODO.md`)。
|
|
129
|
+
|
|
130
|
+
2026-09-04 核对(dsh 0.1.3-alpha.1):session 持久化 v2(write-lease/JSONL 快照/版本化导出)、attachment/file-upload 收口、Windows 子进程隐藏、workspace 全限定路径硬化——全部兼容,插件无需调整。
|
|
131
|
+
|
|
132
|
+
插件侧契约约束(dsh 升级后若行为异常先核对本段;锚定版本变更会在此更新):
|
|
133
|
+
- 插件注入的 session 事件(`tool-workflow/agent-start`、`user/message` 带 `source.kind='plugin'`)均在宿主 `known-event-types` 词表内;**未来新增自定义事件类型须带 `ignorable: true`**,已知类型载荷不加词表外键;
|
|
134
|
+
- `@deepseek-ai/dsh-client-modules` 自 0.1.2-rc.1 起替代 `@deepseek-ai/dsh-client-runtime`(后者已从 monorepo 移除);
|
|
135
|
+
- 计量读的是**宿主投影 key**(`tokenUsage` / `sessionStats`)而非插件自有格式:宿主若改 key 或 state 版本,此段与 `host/core/metering.ts` 同步更新。
|
|
136
|
+
|
|
122
137
|
## 安装(对使用者)
|
|
123
138
|
|
|
124
139
|
```bash
|
package/lib/client.js
CHANGED
|
@@ -308,15 +308,37 @@ window.__ModuleLoader__.load({
|
|
|
308
308
|
interrupted: T.warn,
|
|
309
309
|
superseded: T.text2
|
|
310
310
|
};
|
|
311
|
+
/** 阶段英文键 → 图标/中文展示名(2026-09-06 英文化:journal.phase 为英文键,展示名统一走映射——未来 i18n 换表即换语言)。 */
|
|
311
312
|
const PHASE_ICON = {
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
313
|
+
prd: "📋",
|
|
314
|
+
design: "🎨",
|
|
315
|
+
scaffold: "🏗️",
|
|
316
|
+
tech: "📐",
|
|
317
|
+
dev: "💻",
|
|
318
|
+
qa: "🧪",
|
|
319
|
+
acceptance: "✅"
|
|
319
320
|
};
|
|
321
|
+
const PHASE_NAME = {
|
|
322
|
+
prd: "PRD 产品需求",
|
|
323
|
+
design: "UI/UX 设计",
|
|
324
|
+
scaffold: "架构规划",
|
|
325
|
+
tech: "技术方案",
|
|
326
|
+
dev: "开发",
|
|
327
|
+
qa: "QA 测试",
|
|
328
|
+
acceptance: "产品验收"
|
|
329
|
+
};
|
|
330
|
+
const phaseNameOf = (p) => PHASE_NAME[p] || p || "—";
|
|
331
|
+
const phaseIconOf = (p) => PHASE_ICON[p] || "⚙️";
|
|
332
|
+
/** phase 归一:英文键直通;存量中文映射(防御性——新数据全英文)。 */
|
|
333
|
+
const phaseKeyOf = (p) => ({
|
|
334
|
+
"PRD 产品需求": "prd",
|
|
335
|
+
"UI/UX 设计": "design",
|
|
336
|
+
"架构规划": "scaffold",
|
|
337
|
+
"技术方案": "tech",
|
|
338
|
+
"开发": "dev",
|
|
339
|
+
"QA 测试": "qa",
|
|
340
|
+
"产品验收": "acceptance"
|
|
341
|
+
})[p] || String(p || "");
|
|
320
342
|
const RUN_STATUS_TEXT = {
|
|
321
343
|
pending: "等待中",
|
|
322
344
|
running: "进行中",
|
|
@@ -642,7 +664,20 @@ window.__ModuleLoader__.load({
|
|
|
642
664
|
textOverflow: "ellipsis",
|
|
643
665
|
whiteSpace: "nowrap"
|
|
644
666
|
}
|
|
645
|
-
}, s.label),
|
|
667
|
+
}, s.__taskKey || s.label), s.attempts && s.attempts.length > 1 ? h("span", {
|
|
668
|
+
title: `重试 ${s.attempts.length - 1} 次(共 ${s.attempts.length} 次尝试)`,
|
|
669
|
+
style: {
|
|
670
|
+
fontFamily: MONO,
|
|
671
|
+
fontSize: 10,
|
|
672
|
+
fontWeight: 800,
|
|
673
|
+
color: T.warn,
|
|
674
|
+
background: `color-mix(in srgb, ${T.warn} 14%, transparent)`,
|
|
675
|
+
borderRadius: 999,
|
|
676
|
+
padding: "0 6px",
|
|
677
|
+
lineHeight: "15px",
|
|
678
|
+
flex: "0 0 auto"
|
|
679
|
+
}
|
|
680
|
+
}, `↻${s.attempts.length - 1}`) : null, chip(stText(s.status), color, { dot: true }), h("span", { style: {
|
|
646
681
|
color: T.text2,
|
|
647
682
|
fontSize: 11,
|
|
648
683
|
opacity: .5
|
|
@@ -724,12 +759,12 @@ window.__ModuleLoader__.load({
|
|
|
724
759
|
fontWeight: 700,
|
|
725
760
|
background: `linear-gradient(90deg, color-mix(in srgb, ${g.headColor} 13%, transparent), color-mix(in srgb, ${g.headColor} 5%, transparent))`,
|
|
726
761
|
border: `1px solid color-mix(in srgb, ${g.headColor} 30%, transparent)`
|
|
727
|
-
} }, h("span", { style: { fontSize: 13.5 } },
|
|
762
|
+
} }, h("span", { style: { fontSize: 13.5 } }, phaseIconOf(g.phase)), h("span", { style: {
|
|
728
763
|
flex: 1,
|
|
729
764
|
overflow: "hidden",
|
|
730
765
|
textOverflow: "ellipsis",
|
|
731
766
|
whiteSpace: "nowrap"
|
|
732
|
-
} }, g.phase), g.stages.length > 1 ? h("span", { style: {
|
|
767
|
+
} }, phaseNameOf(g.phase)), g.stages.length > 1 ? h("span", { style: {
|
|
733
768
|
fontFamily: MONO,
|
|
734
769
|
fontSize: 10,
|
|
735
770
|
fontWeight: 800,
|
|
@@ -752,26 +787,32 @@ window.__ModuleLoader__.load({
|
|
|
752
787
|
paddingTop: 10
|
|
753
788
|
} }, g.stages.map((s) => FlowStageCard(s, s.seq, onOpen))));
|
|
754
789
|
}
|
|
755
|
-
/** 阶段详情抽屉(卡片点击打开;浮于画布右侧,不参与拖动/缩放)。
|
|
790
|
+
/** 阶段详情抽屉(卡片点击打开;浮于画布右侧,不参与拖动/缩放)。
|
|
791
|
+
* 2026-09-06 状态机化:同任务多次尝试 → 顶部尝试时间线 + 选中展开(默认最新);
|
|
792
|
+
* 单次尝试保持现状(不渲染时间线)。 */
|
|
756
793
|
function StageDetailDrawer({ det, onClose, sessionId, sessions }) {
|
|
794
|
+
const [sel, setSel] = react.default.useState(null);
|
|
757
795
|
const st = det.stage;
|
|
758
796
|
const d = det.data;
|
|
797
|
+
const attempts = d && d.attempts && d.attempts.length > 1 ? d.attempts : null;
|
|
798
|
+
const selIdx = attempts ? sel === null ? attempts.length - 1 : sel : null;
|
|
799
|
+
const cur = attempts ? attempts[selIdx] || d : d;
|
|
759
800
|
const color = st && st.status ? stColor(st.status) : T.text2;
|
|
760
801
|
const ownerSession = d && d.ownerSession ? String(d.ownerSession) : null;
|
|
761
802
|
const mySession = sessionId ? String(sessionId) : null;
|
|
762
803
|
const crossSession = !!ownerSession && !!mySession && ownerSession !== mySession;
|
|
763
|
-
const hasChild = !!(!crossSession &&
|
|
804
|
+
const hasChild = !!(!crossSession && cur && cur.childId && sessions && typeof sessions.openSubagent === "function");
|
|
764
805
|
const openChild = () => {
|
|
765
806
|
if (crossSession || !hasChild) return;
|
|
766
807
|
try {
|
|
767
808
|
sessions.openSubagent({
|
|
768
809
|
parentSessionId: ownerSession || sessionId,
|
|
769
|
-
childSessionId:
|
|
810
|
+
childSessionId: cur.childId,
|
|
770
811
|
mode: "one-shot"
|
|
771
812
|
});
|
|
772
813
|
} catch (e) {}
|
|
773
814
|
};
|
|
774
|
-
const outText =
|
|
815
|
+
const outText = cur && cur.output ? cur.output : cur && cur.summary ? `(该 run 未保存完整正文,展示摘要)\n\n${cur.summary}` : det.err ? `⚠ 加载失败:${det.err}` : det.loading ? "加载中…" : "(无产物正文)";
|
|
775
816
|
const closeBtn = {
|
|
776
817
|
font: "inherit",
|
|
777
818
|
width: 26,
|
|
@@ -820,7 +861,7 @@ window.__ModuleLoader__.load({
|
|
|
820
861
|
padding: "12px 14px",
|
|
821
862
|
borderBottom: `1px solid ${T.border}`,
|
|
822
863
|
background: `linear-gradient(135deg, color-mix(in srgb, ${color} 14%, transparent), transparent 62%)`
|
|
823
|
-
} }, h("span", { style: { fontSize: 17 } },
|
|
864
|
+
} }, h("span", { style: { fontSize: 17 } }, phaseIconOf(st && st.phase)), h("div", { style: {
|
|
824
865
|
flex: 1,
|
|
825
866
|
minWidth: 0
|
|
826
867
|
} }, h("div", {
|
|
@@ -864,7 +905,66 @@ window.__ModuleLoader__.load({
|
|
|
864
905
|
fontFamily: MONO,
|
|
865
906
|
color: T.text,
|
|
866
907
|
lineHeight: 1.65
|
|
867
|
-
} }, usageDetail(
|
|
908
|
+
} }, usageDetail(cur || st || {}))), attempts ? h("div", { style: {
|
|
909
|
+
display: "flex",
|
|
910
|
+
flexDirection: "column",
|
|
911
|
+
gap: 5
|
|
912
|
+
} }, h("span", { style: {
|
|
913
|
+
fontSize: 10.5,
|
|
914
|
+
fontWeight: 700,
|
|
915
|
+
color: T.text2,
|
|
916
|
+
letterSpacing: .3
|
|
917
|
+
} }, `↻ 尝试历史(${attempts.length} 次 · 点击查看该次详情)`), h("div", { style: {
|
|
918
|
+
display: "flex",
|
|
919
|
+
flexDirection: "column",
|
|
920
|
+
gap: 4
|
|
921
|
+
} }, attempts.map((a, idx) => {
|
|
922
|
+
const aColor = a.status === "done" ? T.success : a.status === "failed" ? T.error : T.warn;
|
|
923
|
+
const sel = idx === selIdx;
|
|
924
|
+
return h("div", {
|
|
925
|
+
key: a.seq,
|
|
926
|
+
onClick: () => setSel(idx),
|
|
927
|
+
title: (a.summary || a.outcome || "").slice(0, 200),
|
|
928
|
+
style: {
|
|
929
|
+
cursor: "pointer",
|
|
930
|
+
borderRadius: 9,
|
|
931
|
+
padding: "6px 9px",
|
|
932
|
+
display: "flex",
|
|
933
|
+
gap: 7,
|
|
934
|
+
alignItems: "center",
|
|
935
|
+
border: `1px solid ${sel ? color : T.border}`,
|
|
936
|
+
background: sel ? `color-mix(in srgb, ${color} 10%, transparent)` : T.layer1
|
|
937
|
+
}
|
|
938
|
+
}, h("span", { style: {
|
|
939
|
+
fontFamily: MONO,
|
|
940
|
+
fontSize: 10.5,
|
|
941
|
+
color: T.text2,
|
|
942
|
+
flex: "0 0 52px"
|
|
943
|
+
} }, `#${a.seq}`), h("span", { style: {
|
|
944
|
+
fontSize: 11,
|
|
945
|
+
color: aColor,
|
|
946
|
+
flex: "0 0 64px",
|
|
947
|
+
fontWeight: 700
|
|
948
|
+
} }, a.status === "done" ? "✅ 成功" : a.status === "failed" ? `❌ ${a.outcome || "失败"}` : "⏳ 进行中"), h("span", { style: {
|
|
949
|
+
flex: 1,
|
|
950
|
+
minWidth: 0,
|
|
951
|
+
fontSize: 10.5,
|
|
952
|
+
color: T.text2,
|
|
953
|
+
overflow: "hidden",
|
|
954
|
+
textOverflow: "ellipsis",
|
|
955
|
+
whiteSpace: "nowrap"
|
|
956
|
+
} }, (a.summary || a.outcome || "(无摘要)").slice(0, 80)), h("span", { style: {
|
|
957
|
+
fontFamily: MONO,
|
|
958
|
+
fontSize: 10,
|
|
959
|
+
color: T.text2,
|
|
960
|
+
flex: "0 0 auto"
|
|
961
|
+
} }, a.startedAt ? fmtDur(a.startedAt, a.endedAt) : "—"), h("span", { style: {
|
|
962
|
+
fontFamily: MONO,
|
|
963
|
+
fontSize: 10,
|
|
964
|
+
color: T.text2,
|
|
965
|
+
flex: "0 0 auto"
|
|
966
|
+
} }, a.usage ? fmtTokens(totalTokens(a.usage)) : ""));
|
|
967
|
+
}))) : null, h("div", { style: {
|
|
868
968
|
display: "flex",
|
|
869
969
|
flexDirection: "column",
|
|
870
970
|
gap: 5
|
|
@@ -898,7 +998,37 @@ window.__ModuleLoader__.load({
|
|
|
898
998
|
color: T.text2,
|
|
899
999
|
textAlign: "center",
|
|
900
1000
|
lineHeight: 1.55
|
|
901
|
-
} }, "跳转成功后,请切「对话」tab 查看该子代理的完整会话轨迹") : null), h("div", { style: {
|
|
1001
|
+
} }, "跳转成功后,请切「对话」tab 查看该子代理的完整会话轨迹") : null), st && phaseKeyOf(st.phase) === "dev" ? h("div", { style: {
|
|
1002
|
+
display: "flex",
|
|
1003
|
+
flexDirection: "column",
|
|
1004
|
+
gap: 5
|
|
1005
|
+
} }, h("span", { style: {
|
|
1006
|
+
fontSize: 10.5,
|
|
1007
|
+
fontWeight: 700,
|
|
1008
|
+
color: T.text2,
|
|
1009
|
+
letterSpacing: .3
|
|
1010
|
+
} }, "🔬 验证证据"), cur && cur.verifyEvidence ? h("div", { style: {
|
|
1011
|
+
whiteSpace: "pre-wrap",
|
|
1012
|
+
wordBreak: "break-word",
|
|
1013
|
+
fontSize: 11.5,
|
|
1014
|
+
lineHeight: 1.62,
|
|
1015
|
+
color: T.text,
|
|
1016
|
+
background: `color-mix(in srgb, ${T.layer2} 55%, transparent)`,
|
|
1017
|
+
border: `1px solid ${T.border}`,
|
|
1018
|
+
borderRadius: 10,
|
|
1019
|
+
padding: "10px 12px",
|
|
1020
|
+
maxHeight: 180,
|
|
1021
|
+
overflowY: "auto",
|
|
1022
|
+
fontFamily: MONO
|
|
1023
|
+
} }, cur.verifyEvidence) : h("div", { style: {
|
|
1024
|
+
fontSize: 11,
|
|
1025
|
+
color: T.warn,
|
|
1026
|
+
background: `color-mix(in srgb, ${T.warn} 8%, transparent)`,
|
|
1027
|
+
border: `1px dashed color-mix(in srgb, ${T.warn} 45%, transparent)`,
|
|
1028
|
+
borderRadius: 10,
|
|
1029
|
+
padding: "8px 12px",
|
|
1030
|
+
lineHeight: 1.55
|
|
1031
|
+
} }, "(缺失——契约未兑现,host 已记警告;可与 logs/teamflow/<runId>/ 命令输出日志对照)")) : null, h("div", { style: {
|
|
902
1032
|
display: "flex",
|
|
903
1033
|
flexDirection: "column",
|
|
904
1034
|
gap: 5
|
|
@@ -932,6 +1062,7 @@ window.__ModuleLoader__.load({
|
|
|
932
1062
|
marginBottom: 8
|
|
933
1063
|
} }, "🏭"), "暂无运行中的流水线——让模型调用 teamflow_start,或在上方输入需求");
|
|
934
1064
|
const groups = [];
|
|
1065
|
+
const taskKeyOf = (s) => String(s.taskKey || String(s.label || "").replace(/^开发 · /, "").replace(/((?:第 \d+ 次重试|补跑))$/, "").trim());
|
|
935
1066
|
for (const st of active.stages || []) {
|
|
936
1067
|
let g = groups.length ? groups[groups.length - 1] : null;
|
|
937
1068
|
if (!g || g.phase !== st.phase) {
|
|
@@ -941,7 +1072,31 @@ window.__ModuleLoader__.load({
|
|
|
941
1072
|
};
|
|
942
1073
|
groups.push(g);
|
|
943
1074
|
}
|
|
944
|
-
|
|
1075
|
+
const key = taskKeyOf(st);
|
|
1076
|
+
const prev = g.stages.find((x) => x.__taskKey === key);
|
|
1077
|
+
if (prev) {
|
|
1078
|
+
prev.attempts = prev.attempts || [prev];
|
|
1079
|
+
prev.attempts.push(st);
|
|
1080
|
+
if ((st.seq || 0) > (prev.seq || 0)) {
|
|
1081
|
+
for (const f of [
|
|
1082
|
+
"status",
|
|
1083
|
+
"outcome",
|
|
1084
|
+
"usage",
|
|
1085
|
+
"output",
|
|
1086
|
+
"summary",
|
|
1087
|
+
"childId",
|
|
1088
|
+
"startedAt",
|
|
1089
|
+
"endedAt",
|
|
1090
|
+
"verifyEvidence"
|
|
1091
|
+
]) prev[f] = st[f];
|
|
1092
|
+
prev.seq = st.seq;
|
|
1093
|
+
prev.label = st.label;
|
|
1094
|
+
}
|
|
1095
|
+
} else g.stages.push({
|
|
1096
|
+
...st,
|
|
1097
|
+
__taskKey: key,
|
|
1098
|
+
attempts: [st]
|
|
1099
|
+
});
|
|
945
1100
|
}
|
|
946
1101
|
const wrapRef = react.default.useRef(null);
|
|
947
1102
|
const [vw, setVw] = react.default.useState(900);
|
|
@@ -1237,7 +1392,7 @@ window.__ModuleLoader__.load({
|
|
|
1237
1392
|
sessions
|
|
1238
1393
|
}) : null);
|
|
1239
1394
|
}
|
|
1240
|
-
function BoardPanel({ backlog, api, onRefresh, sessionId, onShowRun }) {
|
|
1395
|
+
function BoardPanel({ backlog, api, onRefresh, sessionId, onShowRun, openArtifact }) {
|
|
1241
1396
|
const [drag, setDrag] = react.default.useState(null);
|
|
1242
1397
|
const [over, setOver] = react.default.useState(null);
|
|
1243
1398
|
const [det, setDet] = react.default.useState(null);
|
|
@@ -1527,7 +1682,8 @@ window.__ModuleLoader__.load({
|
|
|
1527
1682
|
}), det ? h(ItemDetailDrawer, {
|
|
1528
1683
|
det,
|
|
1529
1684
|
onClose: () => setDet(null),
|
|
1530
|
-
onShowRun
|
|
1685
|
+
onShowRun,
|
|
1686
|
+
openArtifact
|
|
1531
1687
|
}) : null);
|
|
1532
1688
|
}
|
|
1533
1689
|
function fmtAt(ts) {
|
|
@@ -1536,7 +1692,7 @@ window.__ModuleLoader__.load({
|
|
|
1536
1692
|
minute: "2-digit"
|
|
1537
1693
|
}) : "—";
|
|
1538
1694
|
}
|
|
1539
|
-
function ItemDetailDrawer({ det, onClose, onShowRun }) {
|
|
1695
|
+
function ItemDetailDrawer({ det, onClose, onShowRun, openArtifact }) {
|
|
1540
1696
|
const d = det && det.data;
|
|
1541
1697
|
const loading = det && det.loading;
|
|
1542
1698
|
const err = det && det.err;
|
|
@@ -1795,7 +1951,30 @@ window.__ModuleLoader__.load({
|
|
|
1795
1951
|
borderRadius: 8,
|
|
1796
1952
|
padding: "7px 10px",
|
|
1797
1953
|
wordBreak: "break-all"
|
|
1798
|
-
} }, d.runDocs + "/")) :
|
|
1954
|
+
} }, d.runDocs + "/"), Array.isArray(d.artifacts) && d.artifacts.length > 0 ? h("div", { style: {
|
|
1955
|
+
display: "flex",
|
|
1956
|
+
flexWrap: "wrap",
|
|
1957
|
+
gap: 6,
|
|
1958
|
+
marginTop: 1
|
|
1959
|
+
} }, d.artifacts.map((a) => h("button", {
|
|
1960
|
+
key: a.name,
|
|
1961
|
+
onClick: () => {
|
|
1962
|
+
if (openArtifact) openArtifact(a.address, a.name);
|
|
1963
|
+
},
|
|
1964
|
+
title: `在右侧栏预览 ${d.runDocs}/${a.name}`,
|
|
1965
|
+
style: {
|
|
1966
|
+
fontSize: 10.5,
|
|
1967
|
+
fontWeight: 600,
|
|
1968
|
+
padding: "3px 9px",
|
|
1969
|
+
borderRadius: 6,
|
|
1970
|
+
cursor: "pointer",
|
|
1971
|
+
lineHeight: "16px",
|
|
1972
|
+
border: `1px solid color-mix(in srgb, ${T.brand} 38%, transparent)`,
|
|
1973
|
+
background: `color-mix(in srgb, ${T.brand} 10%, transparent)`,
|
|
1974
|
+
color: T.brand,
|
|
1975
|
+
whiteSpace: "nowrap"
|
|
1976
|
+
}
|
|
1977
|
+
}, `📄 ${String(a.name).replace(/\.md$/, "")}`))) : null) : null,
|
|
1799
1978
|
kind === "task" && d.usage ? h("div", { style: {
|
|
1800
1979
|
display: "flex",
|
|
1801
1980
|
flexDirection: "column",
|
|
@@ -2363,6 +2542,7 @@ window.__ModuleLoader__.load({
|
|
|
2363
2542
|
api,
|
|
2364
2543
|
onRefresh: refresh,
|
|
2365
2544
|
sessionId: props.sessionId,
|
|
2545
|
+
openArtifact: props.openArtifact,
|
|
2366
2546
|
onShowRun: (rid) => {
|
|
2367
2547
|
setTab("pipeline");
|
|
2368
2548
|
setRunId(rid);
|
|
@@ -2381,6 +2561,18 @@ window.__ModuleLoader__.load({
|
|
|
2381
2561
|
async function apply(ctx) {
|
|
2382
2562
|
await ctx.remote.$mount(TEAMFLOW_REMOTE_CONTRIBUTION);
|
|
2383
2563
|
const teamflow = ctx.get("remote.teamflow");
|
|
2564
|
+
const openArtifact = (address, name) => {
|
|
2565
|
+
try {
|
|
2566
|
+
const sidebarRight = ctx.get("sidebarRight");
|
|
2567
|
+
if (!sidebarRight || typeof sidebarRight.openResource !== "function") {
|
|
2568
|
+
console.warn("[teamflow] 右侧栏服务不可用,无法预览产物", name);
|
|
2569
|
+
return;
|
|
2570
|
+
}
|
|
2571
|
+
sidebarRight.openResource(address);
|
|
2572
|
+
} catch (e) {
|
|
2573
|
+
console.warn("[teamflow] 打开产物失败", name, e && e.message);
|
|
2574
|
+
}
|
|
2575
|
+
};
|
|
2384
2576
|
ctx.slots.inject("conversation.view", () => ctx.slots.register({
|
|
2385
2577
|
name: "conversation.view",
|
|
2386
2578
|
id: "teamflow",
|
|
@@ -2389,7 +2581,8 @@ window.__ModuleLoader__.load({
|
|
|
2389
2581
|
inject: (sessionId) => ({
|
|
2390
2582
|
sessionId,
|
|
2391
2583
|
remote: teamflow,
|
|
2392
|
-
sessions: ctx.get("sessions")
|
|
2584
|
+
sessions: ctx.get("sessions"),
|
|
2585
|
+
openArtifact
|
|
2393
2586
|
})
|
|
2394
2587
|
}, TeamFlowView));
|
|
2395
2588
|
ctx.slots.inject("conversation.input.right", () => ctx.slots.register({
|