dsh-agent-board 1.7.6 → 1.7.8

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.
@@ -218,11 +218,79 @@
218
218
  React.createElement('div', { style: { fontSize: 9, color: C.text2, marginTop: 4, lineHeight: 1.6 } }, '每个点位是一次真实 agent 运行(不是声明式命令):prompt 只给薄框架契约与上下文,动作由 hook agent 自行决策,吃不准会歧义上报;失败即转阻塞等人裁决(重试/跳过/放弃),不会自动重跑。清空 prompt 保存 = 撤掉该点位。hooks 仅主窗口可设(host 门禁,失败原因原样回显)。'))
219
219
  }
220
220
 
221
+ // ===== 审视摘要区(异常驱动审视①④):详情顶部新块,仅 reviewHint 命中时显示 =====
222
+ // 四问动线 + 来源徽标,素材全用现有字段纯展示拼装(reviewHint 由 host 现算、get-tasks 透出):
223
+ // ①为什么在这 → reviewHint.reasons(host 现算中文理由行)
224
+ // ②改了什么 → deliverable.summary + deliverable.diff(git diff --stat 原文),标「Worker 汇报」来源徽标
225
+ // ③机器怎么验的 → verification 结论(标「Verifier 实证」)+ 验收脚本 + 驳回/仲裁史(标「系统记录」)
226
+ // ④怎么亲自确认 → verification.userTest 自测指南复用渲染,缺省显示 tier=internal 提示
227
+ // 已阅(reviewedAt 非空)仍显示:已阅只收敛列表徽章/chip,详情块不消失——用户要真正过目,不能打开即闪退。
228
+ function ReviewSummary(props) {
229
+ var task = props.task
230
+ var rh = task.reviewHint
231
+ if (!rh || !(rh.score > 0)) return null
232
+ function srcBadge(text, color) { return React.createElement('span', { style: { fontSize: 9, padding: '0 5px', borderRadius: 3, background: 'color-mix(in srgb, ' + color + ' 16%, transparent)', color: color, fontWeight: 600, flexShrink: 0 } }, text) }
233
+ // ③ 驳回/仲裁史:history(歧义/裁决/驳回/干预/rejected)+ messages(rejection/arbitration)
234
+ var histLines = []
235
+ if (Array.isArray(task.history)) task.history.forEach(function (h) { if (h && h.note && /歧义|裁决|驳回|干预|rejected/i.test(h.note)) histLines.push('[' + (h.timestamp || '') + '] ' + String(h.note).slice(0, 160)) })
236
+ if (Array.isArray(task.messages)) task.messages.forEach(function (m) { if (m && (m.kind === 'rejection' || m.kind === 'arbitration')) histLines.push('[' + (m.kind === 'rejection' ? '驳回' : '裁决') + '] ' + String(m.text || '').slice(0, 160)) })
237
+ histLines = histLines.slice(0, 5)
238
+ var q3rows = []
239
+ if (task.verification) q3rows.push(React.createElement('div', { key: 'v', style: { display: 'flex', alignItems: 'flex-start', gap: 4, marginBottom: 2 } }, srcBadge('Verifier 实证', C.ok), React.createElement('span', { style: { fontSize: 11, color: C.text } }, (task.verification.verdict === 'approved' ? '验收通过' : '验收驳回') + (task.verification.summary ? ':' + task.verification.summary : ''))))
240
+ if (task.acceptance) q3rows.push(React.createElement('div', { key: 'a', style: { display: 'flex', alignItems: 'flex-start', gap: 4, marginBottom: 2 } }, srcBadge('系统记录', C.text2), React.createElement('span', { style: { fontSize: 10, color: C.text2, fontFamily: 'monospace' } }, '验收脚本:' + task.acceptance)))
241
+ if (histLines.length) q3rows.push(React.createElement('div', { key: 'h', style: { display: 'flex', alignItems: 'flex-start', gap: 4 } }, srcBadge('系统记录', C.text2), React.createElement('span', { style: { fontSize: 10, color: C.text2, whiteSpace: 'pre-wrap', lineHeight: 1.5 } }, '驳回/仲裁史:\n' + histLines.join('\n'))))
242
+ // ④ 自测指南:复用现有 userTest 渲染口径,缺省 tier=internal 提示
243
+ var q4
244
+ var ut = task.verification && task.verification.userTest
245
+ if (ut) {
246
+ var tierMeta = ut.tier === 'ui' ? { c: C.ok, label: 'UI 可操作' } : (ut.tier === 'metric' ? { c: C.brand, label: '看指标' } : { c: C.text2, label: '纯内部' })
247
+ q4 = React.createElement('div', { style: { marginBottom: 6 } },
248
+ React.createElement('div', { style: { fontSize: 10, fontWeight: 600, color: C.text2, marginBottom: 2, display: 'flex', alignItems: 'center', gap: 4 } }, '④ 怎么亲自确认', srcBadge('自测指南 · ' + tierMeta.label, tierMeta.c)),
249
+ ut.gist ? React.createElement('div', { style: { fontSize: 11, color: C.text, marginBottom: 2 } }, ut.gist) : null,
250
+ ut.steps && ut.steps.length ? React.createElement('div', { style: { fontSize: 10, color: C.text } }, ut.steps.map(function (s, i) { return React.createElement('div', { key: i, style: { marginBottom: 1 } }, (i + 1) + '. ' + s) })) : null,
251
+ ut.expect ? React.createElement('div', { style: { fontSize: 10, color: C.text2 } }, '预期:' + ut.expect) : null)
252
+ } else {
253
+ q4 = React.createElement('div', { style: { fontSize: 10, color: C.text2, lineHeight: 1.5 } }, '④ 怎么亲自确认:本卡无用户自测指南(tier=internal / 未附 userTest)——纯内部改动,验证靠测试套件')
254
+ }
255
+ return React.createElement('div', { id: 'tskb-review-summary', style: { marginBottom: 8, padding: '8px 10px', border: '1px solid ' + C.warn, borderRadius: 6, background: 'color-mix(in srgb, ' + C.warn + ' 7%, transparent)' } },
256
+ React.createElement('div', { style: { fontSize: 12, fontWeight: 700, color: C.warn, marginBottom: 6, display: 'flex', alignItems: 'center', gap: 4 } }, ic('alert-triangle', 13), '待你过目 · 命中 ' + rh.score + ' 条风险信号'),
257
+ React.createElement('div', { style: { marginBottom: 6 } },
258
+ React.createElement('div', { style: { fontSize: 10, fontWeight: 600, color: C.text2, marginBottom: 2 } }, '① 为什么在这'),
259
+ (rh.reasons || []).map(function (r, i) { return React.createElement('div', { key: i, style: { fontSize: 11, color: C.text, lineHeight: 1.5 } }, '· ' + r) })),
260
+ React.createElement('div', { style: { marginBottom: 6 } },
261
+ React.createElement('div', { style: { fontSize: 10, fontWeight: 600, color: C.text2, marginBottom: 2, display: 'flex', alignItems: 'center', gap: 4 } }, '② 改了什么', srcBadge('Worker 汇报', C.brand)),
262
+ (task.deliverable && task.deliverable.summary) ? React.createElement('div', { style: { fontSize: 11, color: C.text, whiteSpace: 'pre-wrap', marginBottom: 2 } }, task.deliverable.summary) : React.createElement('div', { style: { fontSize: 10, color: C.text2, marginBottom: 2 } }, '(无开发描述)'),
263
+ (task.deliverable && task.deliverable.diff) ? React.createElement('div', { style: { fontSize: 10, color: C.text2, whiteSpace: 'pre-wrap', maxHeight: 80, overflowY: 'auto', fontFamily: 'monospace' } }, task.deliverable.diff) : null),
264
+ q3rows.length ? React.createElement('div', { style: { marginBottom: 6 } },
265
+ React.createElement('div', { style: { fontSize: 10, fontWeight: 600, color: C.text2, marginBottom: 2 } }, '③ 机器怎么验的'),
266
+ q3rows) : null,
267
+ q4)
268
+ }
269
+
270
+ // 标签编辑(巡检三连①):逗号分隔 input,提交走 update-task 的 tags 通道(trim + 去空)
271
+ function TagsEditor(props) {
272
+ var task = props.task
273
+ var _R = React; var useState = _R.useState
274
+ var _s = useState((task.tags || []).join(', ')), draft = _s[0], setDraft = _s[1]
275
+ function commit() {
276
+ var tags = draft.split(/[,,]/).map(function (s) { return s.trim() }).filter(function (s) { return s.length > 0 })
277
+ rpc('update-task', { taskId: task.id, tags: tags }).then(function () { fetchTasks() }).catch(function () {})
278
+ }
279
+ return React.createElement('div', { style: { marginBottom: 8, padding: '6px 8px', border: '1px solid ' + C.border, borderRadius: 6, background: C.card } },
280
+ React.createElement('div', { style: { fontSize: 11, fontWeight: 600, color: C.text2, marginBottom: 4 } }, '🏷 标签(逗号分隔)'),
281
+ React.createElement('div', { style: { display: 'flex', gap: 4 } },
282
+ React.createElement('input', { value: draft, onChange: function (e) { setDraft(e.target.value) }, onKeyDown: function (e) { if (e.key === 'Enter') commit() }, placeholder: '如 bug, epic, 待验收', style: { flex: 1, minWidth: 0, fontSize: 11, padding: '4px 8px', border: '1px solid ' + C.border2, borderRadius: 4, background: C.card, color: C.text, fontFamily: 'inherit', boxSizing: 'border-box' } }),
283
+ React.createElement('button', { onClick: commit, title: '保存标签(逗号分隔,自动 trim + 去空)', style: { fontSize: 10, padding: '3px 10px', border: 'none', borderRadius: 4, background: C.brand, color: C_INV, cursor: 'pointer', flexShrink: 0 } }, '保存')))
284
+ }
285
+
221
286
  function DetailView() {
222
287
  var _R = React; var useState = _R.useState, useEffect = _R.useEffect; var task = getTask(state.detailId)
223
288
  var _a = useState(task ? task.title : ''), editTitle = _a[0], setEditTitle = _a[1]; var _b = useState(task ? task.description || '' : ''), editDesc = _b[0], setEditDesc = _b[1]; var _c = useState(false), saving = _c[0], setSaving = _c[1]; var _d = useState(state.boardMode), mode = _d[0], setMode = _d[1]
224
289
  var _e2 = useState(''), arbAnswer = _e2[0], setArbAnswer = _e2[1]; var _f2 = useState(''), interveneMsg = _f2[0], setInterveneMsg = _f2[1]; var _g2 = useState(''), actionMsg = _g2[0], setActionMsg = _g2[1]; var _h2 = useState('resume'), arbAction = _h2[0], setArbAction = _h2[1]
225
290
  useEffect(function () { function update() { setMode(state.boardMode) }; listeners.push(update); update(); return function () { var i = listeners.indexOf(update); if (i >= 0) listeners.splice(i, 1) } }, [])
291
+ // 已阅落账(异常驱动审视①③):打开命中且未阅的详情页即幂等 mark-reviewed——
292
+ // host 幂等只写一次;本轮渲染仍按「未阅」展示审视摘要区(fetchTasks 回来前不闪退)。
293
+ useEffect(function () { var t = getTask(state.detailId); if (t && t.reviewHint && t.reviewHint.score > 0 && !t.reviewedAt) { rpc('mark-reviewed', { taskId: t.id }).then(fetchTasks).catch(function () {}) } }, [state.detailId])
226
294
  if (!task) { state.detailId = null; return React.createElement('div', { style: { padding: 20, color: C.text2 } }, '任务不存在') }
227
295
  function doAction(fn) { fn().then(fetchTasks).catch(function () {}) }
228
296
  function saveEdit() { setSaving(true); rpc('update-task', { taskId: task.id, title: editTitle, description: editDesc, resetToPending: true }).then(function () { setSaving(false); fetchTasks() }).catch(function () { setSaving(false) }) }
@@ -273,7 +341,9 @@
273
341
  }
274
342
  return React.createElement('div', { style: { padding: '4px 2px' } },
275
343
  React.createElement('div', { onClick: function () { state.detailId = null; notify() }, style: { fontSize: 11, color: C.brand, cursor: 'pointer', marginBottom: 8 } }, '← 返回看板'),
276
- React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 6, marginBottom: 8 } }, React.createElement('span', { style: { fontSize: 10, padding: '1px 6px', borderRadius: 3, background: 'color-mix(in srgb, ' + (prioColor[task.priority] || prioColor.low) + ' 20%, transparent)', color: (prioColor[task.priority] || prioColor.low) } }, prioLabel[task.priority] || '中'), React.createElement('span', { style: { fontSize: 11, padding: '1px 8px', borderRadius: 3, background: C.nested, color: C.text } }, statusLabels[task.status] || task.status), React.createElement('select', { value: task.pipeline || 'full', onChange: function (e) { rpc('update-task', { taskId: task.id, pipeline: e.target.value }).then(fetchTasks).catch(function () {}) }, title: '管线档位', style: { fontSize: 10, padding: '1px 4px', border: '1px solid ' + C.border, borderRadius: 3, background: C.card, color: C.text2 } }, React.createElement('option', { value: 'full' }, '全流程(执行+验证)'), React.createElement('option', { value: 'work' }, '免验证(只做不验)'), React.createElement('option', { value: 'direct' }, '主窗口处理')), task.pipelineAuto ? React.createElement('span', { style: { fontSize: 9, color: C.text2 }, title: '由规则自动分类,可手动覆盖' }, 'auto') : null, isManual ? React.createElement('span', { style: { fontSize: 10, color: C.text2, display: 'inline-flex', alignItems: 'center', gap: 2 } }, ic('user', 10), '手动派发') : null),
344
+ React.createElement(ReviewSummary, { task: task }),
345
+ React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 6, marginBottom: 8 } }, React.createElement('select', { value: task.priority || 'medium', onChange: function (e) { rpc('update-task', { taskId: task.id, priority: e.target.value }).then(fetchTasks).catch(function () {}) }, title: '优先级(四档:紧急/高/中/低)', style: { fontSize: 10, padding: '1px 4px', border: '1px solid ' + C.border, borderRadius: 3, background: C.card, color: (prioColor[task.priority] || prioColor.low), fontWeight: 600 } }, ['critical', 'high', 'medium', 'low'].map(function (p) { return React.createElement('option', { key: p, value: p }, prioLabel[p] + '(' + p + ')') })), React.createElement('span', { style: { fontSize: 11, padding: '1px 8px', borderRadius: 3, background: C.nested, color: C.text } }, statusLabels[task.status] || task.status), React.createElement('select', { value: task.pipeline || 'full', onChange: function (e) { rpc('update-task', { taskId: task.id, pipeline: e.target.value }).then(fetchTasks).catch(function () {}) }, title: '管线档位', style: { fontSize: 10, padding: '1px 4px', border: '1px solid ' + C.border, borderRadius: 3, background: C.card, color: C.text2 } }, React.createElement('option', { value: 'full' }, '全流程(执行+验证)'), React.createElement('option', { value: 'work' }, '免验证(只做不验)'), React.createElement('option', { value: 'direct' }, '主窗口处理')), task.pipelineAuto ? React.createElement('span', { style: { fontSize: 9, color: C.text2 }, title: '由规则自动分类,可手动覆盖' }, 'auto') : null, isManual ? React.createElement('span', { style: { fontSize: 10, color: C.text2, display: 'inline-flex', alignItems: 'center', gap: 2 } }, ic('user', 10), '手动派发') : null),
346
+ React.createElement(TagsEditor, { key: task.id, task: task }),
277
347
  // 流转到按钮组(键盘可达的状态迁移入口,替代拖拽;无合法迁移的状态(draft/resolved 等)整块不渲染)
278
348
  flowBtns.length > 0 ? React.createElement('div', { role: 'group', 'aria-label': '状态流转', style: { display: 'flex', alignItems: 'center', gap: 5, flexWrap: 'wrap', marginBottom: 8, padding: '5px 8px', border: '1px solid ' + C.border, borderRadius: 6, background: C.card } },
279
349
  React.createElement('span', { style: { fontSize: 10, fontWeight: 600, color: C.text2 } }, '⇄ 流转到:'),
@@ -371,6 +441,14 @@
371
441
  ut.steps && ut.steps.length ? React.createElement('div', { style: { fontSize: 10, color: C.text, marginBottom: 3 } }, ut.steps.map(function (s, i) { return React.createElement('div', { key: i, style: { marginBottom: 1 } }, (i + 1) + '. ' + s) })) : null,
372
442
  ut.expect ? React.createElement('div', { style: { fontSize: 10, color: C.text2 } }, '预期:' + ut.expect) : null)
373
443
  })() : null) : null,
444
+ // ===== 迟到 Verifier 结论块(验收时序三洞 ③):verifier 结论落到已落定卡后的留痕展示 =====
445
+ // 主窗口抢批/归档早于 Verifier 结算时,其独立结论落 t.lateVerdict 而非静默丢弃——此处整块展示
446
+ // (verdict/summary/checks + note 说明 + 时间来源),与上方正常 verification 块并列可对照。
447
+ task.lateVerdict ? React.createElement('div', { style: { marginBottom: 8, padding: '6px 8px', border: '1px solid ' + (task.lateVerdict.verdict === 'approved' ? C.ok : C.err), borderRadius: 6, background: 'color-mix(in srgb, ' + (task.lateVerdict.verdict === 'approved' ? C.ok : C.err) + ' 8%, transparent)' } },
448
+ React.createElement('div', { style: { fontSize: 11, fontWeight: 700, color: task.lateVerdict.verdict === 'approved' ? C.ok : C.err, marginBottom: 4, display: 'flex', alignItems: 'center', gap: 4 } }, ic('alert-triangle', 12), '迟到 Verifier 结论 · ' + (task.lateVerdict.by || '') + ' · ' + ago(task.lateVerdict.at)),
449
+ React.createElement('div', { style: { fontSize: 10, color: C.text2, marginBottom: 4 } }, String(task.lateVerdict.note || '')),
450
+ React.createElement('div', { style: { fontSize: 11, color: C.text, marginBottom: 4, whiteSpace: 'pre-wrap' } }, (task.lateVerdict.verdict === 'approved' ? '验收通过' : '验收驳回') + (task.lateVerdict.summary ? ':' + task.lateVerdict.summary : '')),
451
+ task.lateVerdict.checks ? React.createElement('div', { style: { marginTop: 4 } }, React.createElement('div', { style: { fontSize: 10, fontWeight: 600, color: C.text2 } }, '核对项'), React.createElement('div', { style: { fontSize: 10, color: C.text2, whiteSpace: 'pre-wrap', maxHeight: 120, overflowY: 'auto' } }, task.lateVerdict.checks)) : null) : null,
374
452
  React.createElement(MsgThread, { messages: task.messages, taskId: task.id }),
375
453
  Array.isArray(task.history) && task.history.length > 0 ? React.createElement('div', { style: { marginBottom: 8 } }, React.createElement('div', { style: { fontSize: 11, fontWeight: 600, color: C.text2, marginBottom: 3 } }, '流转轨迹'), React.createElement('div', { style: { fontSize: 10, color: C.text2, padding: '4px 6px', background: C.nested, borderRadius: 4 } }, task.history.map(function (h, i) { return React.createElement('div', { key: i, style: { marginBottom: 2 } }, React.createElement('span', { style: { color: C.brand } }, statusLabels[h.to] || h.to), ' · ' + ago(h.timestamp) + ' · ', React.createElement(ActorLink, { id: h.actor }), h.note ? ' · ' + h.note : '') }))) : null,
376
454
  React.createElement('div', { style: { display: 'flex', gap: 4, flexWrap: 'wrap', marginTop: 6 } },
@@ -383,6 +461,8 @@
383
461
  task.status === 'verifying' ? React.createElement('button', { onClick: function () { doAction(function () { return rpc('verify-task', { taskId: task.id, verdict: 'approved' }) }) }, style: { fontSize: 10, padding: '3px 8px', border: 'none', borderRadius: 3, background: C.ok, color: C_INV, cursor: 'pointer', display: 'inline-flex', alignItems: 'center', gap: 3 } }, ic('check-circle', 10), '通过') : null,
384
462
  task.status === 'verifying' ? React.createElement('button', { onClick: function () { var r = window.prompt('驳回原因:'); doAction(function () { return rpc('verify-task', { taskId: task.id, verdict: 'rejected', comment: r || '' }) }) }, style: { fontSize: 10, padding: '3px 8px', border: 'none', borderRadius: 3, background: C.err, color: C_INV, cursor: 'pointer', display: 'inline-flex', alignItems: 'center', gap: 3 } }, ic('x-circle', 10), '驳回') : null,
385
463
  task.status === 'resolved' ? React.createElement('button', { onClick: function () { doAction(function () { return rpc('archive-task', { taskId: task.id }) }) }, style: { fontSize: 10, padding: '3px 8px', border: 'none', borderRadius: 3, background: C.text2, color: C_INV, cursor: 'pointer', display: 'inline-flex', alignItems: 'center', gap: 3 } }, ic('archive', 10), '归档') : null),
464
+ // 取消(仅草稿/待办/阻塞):confirm 预警「取消后不再派发,可在归档恢复」;与删除并列但语义=作废留档(可恢复)
465
+ canCancel(task) ? React.createElement('button', { onClick: function () { cancelTask(task.id, task.title, setActionMsg) }, title: '取消任务(取消后不再派发,可在归档恢复待办)', style: { fontSize: 10, padding: '3px 8px', border: '1px solid ' + C.warn, borderRadius: 3, background: 'transparent', color: C.warn, cursor: 'pointer', fontWeight: 600, display: 'inline-flex', alignItems: 'center', gap: 3 } }, ic('ban', 10), '取消') : null,
386
466
  // 删除(仅草稿/待办/阻塞):confirm 防误删;被 host 门禁拒绝时把原因回显到详情页 actionMsg
387
467
  // 容器 div 的收尾括号已挂在「归档」行;删除按钮作为**额外一行元素**排在它后面
388
468
  // (多一个实参给同一个 createElement,属于合法调用),因此本行自身必须配平。