dsh-agent-board 1.8.3 → 1.9.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/README.md +33 -4
- package/index.mjs +5 -1
- package/lib/client/board-list.js +51 -7
- package/lib/client/dashboard.js +268 -23
- package/lib/client/kernel.js +41 -6
- package/lib/client/task-detail.js +14 -7
- package/lib/client.js +374 -43
- package/lib/core.mjs +278 -21
- package/lib/dispatch.mjs +611 -25
- package/lib/policy.mjs +177 -7
- package/lib/rpc.mjs +374 -24
- package/lib/store.mjs +37 -0
- package/lib/usage.mjs +34 -4
- package/package.json +2 -2
- package/test/core.test.mjs +452 -33
- package/test/decompose.test.mjs +594 -0
- package/test/helpers/mock-ctx.mjs +6 -1
- package/test/integration.test.mjs +516 -0
- package/test/lane-gui-bysize.test.mjs +548 -0
- package/test/lane-models.test.mjs +594 -0
- package/test/onboarding-banner.test.mjs +88 -0
- package/test/ops-log.test.mjs +138 -0
- package/test/rate-limit-cooldown.test.mjs +308 -0
- package/test/readme-decompose-docs.test.mjs +112 -0
- package/test/settle-stop-interrupt.test.mjs +228 -0
- package/test/size-hint.test.mjs +192 -0
- package/test/spawn-activation.test.mjs +211 -0
- package/test/store-data-safety.test.mjs +102 -0
- package/test/verify-timing.test.mjs +94 -0
|
@@ -345,7 +345,7 @@
|
|
|
345
345
|
React.createElement('div', { onClick: function () { state.detailId = null; notify() }, style: { fontSize: 11, color: C.brand, cursor: 'pointer' } }, '← 返回看板'),
|
|
346
346
|
React.createElement('button', { onClick: function () { state.detailId = null; notify() }, title: '关闭详情面板(返回看板)', style: { fontSize: 11, padding: '1px 6px', border: '1px solid ' + C.border, borderRadius: 3, background: 'transparent', color: C.text2, cursor: 'pointer', display: 'inline-flex', alignItems: 'center', lineHeight: 1.4 } }, ic('x', 11))),
|
|
347
347
|
React.createElement(ReviewSummary, { task: task }),
|
|
348
|
-
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),
|
|
348
|
+
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.size || (state.teamMode ? 'standard' : ''), onChange: function (e) { rpc('update-task', { taskId: task.id, size: e.target.value }).then(fetchTasks).catch(function () {}) }, title: '力度档位(路由板级 laneModels 三档表定模):轻=单点小改/文档/配置,标=常规模块改动,重=跨模块/高风险' + (state.teamMode ? ';Team 板硬闸必填,不可调回继承' : ';继承=清除 size 回标档语义'), style: { fontSize: 10, padding: '1px 4px', border: '1px solid ' + C.border, borderRadius: 3, background: C.card, color: C.brand, fontWeight: 600 } }, (state.teamMode ? [] : [React.createElement('option', { key: '', value: '' }, '继承(标)')]).concat([['light', '轻(light)'], ['standard', '标(standard)'], ['heavy', '重(heavy)']].map(function (s) { return React.createElement('option', { key: s[0], value: s[0] }, s[1]) }))), task.model ? React.createElement('span', { style: { fontSize: 9, padding: '1px 6px', borderRadius: 3, background: 'color-mix(in srgb, ' + C.warn + ' 18%, transparent)', color: C.warn, fontWeight: 700, maxWidth: 200, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, title: '卡级模型特例 model=' + task.model + '(取模链最高优先;只读展示——改/删走对话 task_update,v1 不做 GUI 编辑)' }, '🎯 ' + task.model) : null, 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),
|
|
349
349
|
React.createElement(TagsEditor, { key: task.id, task: task }),
|
|
350
350
|
// 流转到按钮组(键盘可达的状态迁移入口,替代拖拽;无合法迁移的状态(draft/resolved 等)整块不渲染)
|
|
351
351
|
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 } },
|
|
@@ -474,26 +474,33 @@
|
|
|
474
474
|
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,
|
|
475
475
|
canDelete(task) ? React.createElement('button', { onClick: function () { deleteTask(task.id, task.title, setActionMsg) }, title: '删除任务(不可恢复;执行中请先终止,已落定请用归档)', style: { fontSize: 10, padding: '3px 8px', border: '1px solid ' + C.err, borderRadius: 3, background: 'transparent', color: C.err, cursor: 'pointer', fontWeight: 600, display: 'inline-flex', alignItems: 'center', gap: 3 } }, ic('trash-2', 10), '删除') : null) : null,
|
|
476
476
|
(function () {
|
|
477
|
-
// 历史会话:无论任务处于哪个阶段都完整列出(Worker 各次 + Verifier
|
|
477
|
+
// 历史会话:无论任务处于哪个阶段都完整列出(Worker 各次 + Verifier 各次 + 拆解官各次)
|
|
478
478
|
var runs = historyRuns(task)
|
|
479
479
|
if (!runs.length) return null
|
|
480
|
-
|
|
481
|
-
var
|
|
480
|
+
// 拆解官可见性(task-mvomad6c ③):decomposer 角色标签 🧩(此前 fallback 进 worker 桶被误标 W)。
|
|
481
|
+
var roleMeta = { worker: { label: 'W', color: C.brand, tip: 'Worker' }, verifier: { label: 'V', color: C.warn, tip: 'Verifier' }, decomposer: { label: '🧩', color: C.brand, tip: '拆解官' } }
|
|
482
|
+
// 序数按角色分桶:W/V 序号与旧口径逐字一致(未知角色如 hook 仍计 Worker 桶,与 fallback 标签同桶);
|
|
483
|
+
// decomposer 独立编号(🧩 #1 #2…重拆次数一眼可读)。
|
|
484
|
+
var seqN = {}
|
|
482
485
|
return React.createElement('div', { style: { marginTop: 8, padding: '6px 8px', background: C.nested, borderRadius: 6 } },
|
|
483
486
|
React.createElement('div', { style: { fontSize: 10, fontWeight: 600, color: C.text2, marginBottom: 4, display: 'flex', alignItems: 'center', gap: 4 } }, ic('users', 10), '历史会话(点击跳转查看,含全部重试)'),
|
|
484
487
|
React.createElement('div', { style: { display: 'flex', gap: 5, flexWrap: 'wrap' } },
|
|
485
488
|
runs.map(function (r, i) {
|
|
486
489
|
var rm = roleMeta[r.role] || roleMeta.worker
|
|
487
|
-
var
|
|
488
|
-
|
|
490
|
+
var seqKey = roleMeta[r.role] ? r.role : 'worker'
|
|
491
|
+
seqN[seqKey] = (seqN[seqKey] || 0) + 1; var seq = seqN[seqKey]
|
|
492
|
+
// 拆解官在跑高亮:源卡不动卡面状态(无 claimedBy/verifierRun 可比对),以留档 running 未关账为在跑口径
|
|
493
|
+
var isCur = (r.role === 'worker' && task.claimedBy === r.id) || (r.role === 'verifier' && task.verifierRun === r.id) || (r.role === 'decomposer' && r.outcome === 'running' && !r.endedAt)
|
|
489
494
|
var okMark = r.outcome === 'completed' ? ' ✅' : (r.outcome === 'running' ? ' ⏳' : (r.outcome ? ' ⚠' : ''))
|
|
490
495
|
// 续跑标记(卡2 落的 runs[i].resume 字段;卡3 Step3 在此显式标注):同一条会话的第二次及以后
|
|
491
496
|
// 派发是「冷复活续跑」而不是新开 Worker,不标出来会让人把「第 2/3 次」误读成又开了一个新会话。
|
|
492
497
|
// 只读 r.resume,不猜(老留档没有该字段 → 零渲染,与改造前逐字一致)。
|
|
493
498
|
var rsMark = r.resume === true ? ' ↻' : ''
|
|
499
|
+
// 耗时展示(task-mvomad6c ③顺带补齐全角色):落定 run 显 at→endedAt 净耗时,在跑显已跑时长
|
|
500
|
+
var durTxt = r.at ? (r.endedAt ? ' · 耗时 ' + fmtDur(new Date(r.at).getTime(), new Date(r.endedAt).getTime()) : ' · 已跑 ' + elapsedSince(r.at)) : ''
|
|
494
501
|
return React.createElement('button', {
|
|
495
502
|
key: i, onClick: function () { if (uiWorkspaceSvc) uiWorkspaceSvc.openSession(r.id) },
|
|
496
|
-
title: rm.tip + ' 第 ' + seq + ' 次' + (r.resume === true ? '(↻ 冷复活续跑:沿用上一次的子会话,非新开)' : '') + (r.at ? ' · ' + ago(r.at) : '') + (r.model ? ' · ' + r.model : '') + (r.outcome ? ' · ' + r.outcome : '') + '(' + r.id + ')',
|
|
503
|
+
title: rm.tip + ' 第 ' + seq + ' 次' + (r.resume === true ? '(↻ 冷复活续跑:沿用上一次的子会话,非新开)' : '') + (r.at ? ' · ' + ago(r.at) : '') + (r.model ? ' · ' + r.model : '') + (r.outcome ? ' · ' + r.outcome : '') + durTxt + '(' + r.id + ')',
|
|
497
504
|
style: { fontSize: 10, padding: '2px 8px', border: '1px solid ' + (isCur ? rm.color : C.border), borderRadius: 3, background: isCur ? C.card : 'transparent', color: rm.color, cursor: 'pointer', fontWeight: isCur ? 600 : 400 }
|
|
498
505
|
}, '→ ' + rm.tip + ' #' + seq + (r.at ? ' · ' + ago(r.at) : '') + okMark + rsMark)
|
|
499
506
|
})))
|