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
package/lib/client/dashboard.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// dashboard —— 仪表盘域:统计(computeStats/TrendChart/StatCard/BarRow/ResearchRoiRow)· 范围筛选(RangeFilter)
|
|
3
3
|
// · 全局总览(GlobalBoards)· 报告(buildReport/ReportButton)· Token 消耗(TokenUsage)
|
|
4
4
|
// · 架构健康(HealthHints,架构自省 L1)
|
|
5
|
-
// · 团队池(TeamView/PoolStatus)· 设置(WorkModeSwitch/PoolCfg/MinCfg/ModelCfg/PoolCfgPopover)
|
|
5
|
+
// · 团队池(TeamView/PoolStatus)· 设置(WorkModeSwitch/PoolCfg/MinCfg/ModelCfg/OrderedModelsEditor/PoolCfgPopover/ModelRoutePopover)
|
|
6
6
|
// 本文件是 apply(ctx) 函数体片段,不可独立运行;由 scripts/build-client.cjs 按序拼接生成 lib/client.js。
|
|
7
7
|
// ============================================================================
|
|
8
8
|
|
|
@@ -508,6 +508,44 @@
|
|
|
508
508
|
{ k: 'resumeRate', label: '续跑率', title: '续跑率 = resume:true 续跑记录 / 桶内 runs', fmt: function (b) { return mpPct(b.resumeRate) } },
|
|
509
509
|
{ k: 'cacheHitRate', label: '缓存命中率', title: '缓存命中率 = cacheRead / (input + cacheRead);⚠️ 跨 provider 口径可能不一(input 是否含缓存读各家不同),对比前先核口径;无 usage 样本 → —', fmt: function (b) { return mpPct(b.cacheHitRate) } }
|
|
510
510
|
]
|
|
511
|
+
// ===== 按档位小表(task-mvneq4mw):行=力度三档(轻/标/重),列=runs/驳回/异常/token =====
|
|
512
|
+
// 数据源 sb.bySize(host 装配层 assembleScoreboard 按 run.size 留档归桶;无 size 老 run 归标档,零迁移)。
|
|
513
|
+
// 口径:驳回 = 归因到该档 worker run 的验收驳回;异常 = 超时落定 + 跑崩落定(行 title 拆分);
|
|
514
|
+
// token = 有效口径(输入+输出+缓存写,与本区八指标同),行 title 附总量(含缓存读);随统计范围裁剪。
|
|
515
|
+
// 只认 worker/verifier run(hook 恒父模型、不进档位统计);全部档位零 runs 时整块不渲染(不画空表)。
|
|
516
|
+
var LANE_LABEL = { light: '轻档', standard: '标档', heavy: '重档' }
|
|
517
|
+
function LaneTable(props) {
|
|
518
|
+
var bySize = props.bySize
|
|
519
|
+
if (!bySize) return null
|
|
520
|
+
var lanes = ['light', 'standard', 'heavy']
|
|
521
|
+
var totalRuns = 0
|
|
522
|
+
for (var li = 0; li < lanes.length; li++) totalRuns += (bySize[lanes[li]] && bySize[lanes[li]].runs) || 0
|
|
523
|
+
if (!totalRuns) return null
|
|
524
|
+
var thL2 = { fontSize: 9, color: C.text2, fontWeight: 600, textAlign: 'left', padding: '2px 4px', borderBottom: '1px solid ' + C.border, whiteSpace: 'nowrap' }
|
|
525
|
+
var thR2 = { fontSize: 9, color: C.text2, fontWeight: 600, textAlign: 'right', padding: '2px 4px', borderBottom: '1px solid ' + C.border, whiteSpace: 'nowrap', cursor: 'help' }
|
|
526
|
+
var tdL2 = { fontSize: 10, color: C.text, textAlign: 'left', padding: '3px 4px', borderBottom: '1px solid ' + C.nested, whiteSpace: 'nowrap' }
|
|
527
|
+
var tdR2 = { fontSize: 10, color: C.text, textAlign: 'right', padding: '3px 4px', borderBottom: '1px solid ' + C.nested, whiteSpace: 'nowrap' }
|
|
528
|
+
return React.createElement('div', { style: { marginTop: 8 } },
|
|
529
|
+
React.createElement('div', { style: { fontSize: 10, fontWeight: 600, color: C.text2, marginBottom: 4 } }, '按档位'),
|
|
530
|
+
React.createElement('table', { style: { width: '100%', borderCollapse: 'collapse' } },
|
|
531
|
+
React.createElement('thead', null, React.createElement('tr', null,
|
|
532
|
+
React.createElement('th', { style: thL2 }, '档位'),
|
|
533
|
+
React.createElement('th', { style: thR2, title: '该档已落定 Worker/Verifier run 数(hook 不计入)' }, 'runs'),
|
|
534
|
+
React.createElement('th', { style: thR2, title: '驳回 = 归因到该档 worker run 的验收驳回次数' }, '驳回'),
|
|
535
|
+
React.createElement('th', { style: thR2, title: '异常 = 超时落定(timeout/error)+ 跑崩落定(incomplete/error/failed)的 run 数;terminated 人工终止不计入' }, '异常'),
|
|
536
|
+
React.createElement('th', { style: thR2, title: 'token = 有效口径合计(输入+输出+缓存写,不含缓存读);行悬浮见总量(含缓存读)' }, 'token'))),
|
|
537
|
+
React.createElement('tbody', null, lanes.map(function (ln) {
|
|
538
|
+
var b = bySize[ln] || { runs: 0, rejected: 0, failed: 0, timeouts: 0, tokSum: 0, effSum: 0 }
|
|
539
|
+
var tip = LANE_LABEL[ln] + ':runs=' + b.runs + ';驳回 ' + b.rejected + ';异常 ' + (b.failed + b.timeouts) + '(超时 ' + b.timeouts + ' + 跑崩 ' + b.failed + ');token 有效 ' + fmtTokens(b.effSum) + ' · 总量 ' + fmtTokens(b.tokSum) + '(含缓存读);老 run 无 size 归标档'
|
|
540
|
+
return React.createElement('tr', { key: ln, title: tip },
|
|
541
|
+
React.createElement('td', { style: tdL2 }, LANE_LABEL[ln]),
|
|
542
|
+
React.createElement('td', { style: tdR2 }, String(b.runs)),
|
|
543
|
+
React.createElement('td', { style: tdR2 }, String(b.rejected)),
|
|
544
|
+
React.createElement('td', { style: tdR2 }, String(b.failed + b.timeouts)),
|
|
545
|
+
React.createElement('td', { style: tdR2 }, fmtTokens(b.effSum)))
|
|
546
|
+
}))),
|
|
547
|
+
React.createElement('div', { style: { fontSize: 9, color: C.text2, marginTop: 4, lineHeight: 1.5 } }, '口径:行 = 力度档位(run.size 派发留档,老 run 无 size 归标档);只计 Worker/Verifier run(hook 恒父模型不计入);驳回 = 归因到该档 worker run 的验收驳回;异常 = 超时 + 跑崩;token = 有效口径(悬浮行看总量);随统计范围裁剪'))
|
|
548
|
+
}
|
|
511
549
|
function ModelPerf(props) {
|
|
512
550
|
var _R = React; var useState = _R.useState
|
|
513
551
|
var _a = useState(state.mpRole || ''), roleF = _a[0], setRoleF = _a[1]
|
|
@@ -522,8 +560,9 @@
|
|
|
522
560
|
tgOn ? React.createElement('span', { style: { fontSize: 9, fontWeight: 400, color: C.brand, border: '1px solid ' + C.brand, borderRadius: 8, padding: '0 6px' }, title: '本区桶数据已按统计范围过滤(与 Token 区同一 range 口径):' + rangeLabel() }, '范围内: ' + rangeLabel()) : null)
|
|
523
561
|
var sb = props.sb
|
|
524
562
|
var buckets = (sb && Array.isArray(sb.buckets)) ? sb.buckets : []
|
|
525
|
-
// 空态:无 scoreboard(老 host)/ 无入桶 run → 一句话空态(锚 id 照挂,Token
|
|
526
|
-
|
|
563
|
+
// 空态:无 scoreboard(老 host)/ 无入桶 run → 一句话空态(锚 id 照挂,Token 区链接始终可达);
|
|
564
|
+
// 按档位小表仍渲染(它不要求学模型名——无模型老 run 也烧了档位 token,档位视图可能恰好还有数据)
|
|
565
|
+
if (!buckets.length) return React.createElement('div', { id: 'tskb-model-perf', style: box }, head, React.createElement('div', { style: { fontSize: 10, color: C.text2 } }, '暂无足够 run 数据(还没有可入桶的已落定 Worker/Verifier run 留账)'), React.createElement(LaneTable, { bySize: sb && sb.bySize }))
|
|
527
566
|
// 本地筛选:角色 × 规模段,纯数组 pass 不重拉
|
|
528
567
|
var shown = buckets.filter(function (b) { return (!roleF || b.role === roleF) && (!sizeF || b.size === sizeF) })
|
|
529
568
|
function chip(cur, setter, v, label) {
|
|
@@ -561,7 +600,9 @@
|
|
|
561
600
|
React.createElement('td', { style: tdR }, String(b.runs)),
|
|
562
601
|
MP_COLS.map(function (c) { return React.createElement('td', { key: c.k, style: tdR }, c.fmt(b)) }))
|
|
563
602
|
})))),
|
|
564
|
-
React.createElement('div', { style: { fontSize: 9, color: C.text2, marginTop: 6, lineHeight: 1.5 } }, '口径:每行 = 模型 × 角色 × 规模段桶(规模按任务有效 token:小 <1M / 中 1~10M / 大 >10M);率类分母为桶内 runs,一次通过率分母为桶内 resolved 任务;失败率 = 跑崩落定(outcome 为 incomplete/error/failed)的 run / 桶内 runs,terminated 人工终止不计入;runs<5 行灰显 = 样本不足仅供参考;随统计范围裁剪(与 Token 区同一 range)')
|
|
603
|
+
React.createElement('div', { style: { fontSize: 9, color: C.text2, marginTop: 6, lineHeight: 1.5 } }, '口径:每行 = 模型 × 角色 × 规模段桶(规模按任务有效 token:小 <1M / 中 1~10M / 大 >10M);率类分母为桶内 runs,一次通过率分母为桶内 resolved 任务;失败率 = 跑崩落定(outcome 为 incomplete/error/failed)的 run / 桶内 runs,terminated 人工终止不计入;runs<5 行灰显 = 样本不足仅供参考;随统计范围裁剪(与 Token 区同一 range)'),
|
|
604
|
+
// 按档位小表(task-mvneq4mw):三档 × runs/驳回/异常/token,挂在八指标表下方
|
|
605
|
+
React.createElement(LaneTable, { bySize: sb && sb.bySize }))
|
|
565
606
|
}
|
|
566
607
|
|
|
567
608
|
// ===== 质量趋势区(记分卡卡4,task-muxhtgil):一次通过率趋势 / 卡时长分布 / 超时率走势 / 续跑成功率 =====
|
|
@@ -882,7 +923,7 @@
|
|
|
882
923
|
function commit(v) {
|
|
883
924
|
var n = parseInt(v, 10)
|
|
884
925
|
if (isNaN(n) || n < props.min || n > props.max) { setVal(String(props.value)); setDirty(false); return }
|
|
885
|
-
setVal(String(n)); setDirty(false); rpc('set-board-config', { key: props.cfgKey, value: n }).then(fetchTasks).catch(function () {})
|
|
926
|
+
setVal(String(n)); setDirty(false); rpc('set-board-config', { key: props.cfgKey, value: n }).then(function (r) { if (typeof props.onSaved === 'function') props.onSaved(r); return fetchTasks() }).catch(function () {})
|
|
886
927
|
}
|
|
887
928
|
return React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 3, fontSize: 9, color: C.text2 } },
|
|
888
929
|
props.label,
|
|
@@ -921,20 +962,48 @@
|
|
|
921
962
|
dirty ? React.createElement('button', { onClick: function () { commit(val) }, title: '应用', style: { fontSize: 9, padding: '0px 5px', border: 'none', borderRadius: 2, background: C.brand, color: C_INV, cursor: 'pointer', lineHeight: '14px', fontWeight: 600, display: 'inline-flex', alignItems: 'center' } }, ic('check', 9)) : null)
|
|
922
963
|
}
|
|
923
964
|
|
|
965
|
+
// ===== 取模来源角标(task-mvnyc8vo,用户「能看到子 agent 当前在用什么模型跑」)=====
|
|
966
|
+
// board/parent=常规继承路径不标(绝大多数,标了反成噪音);其余路径给小角标,一眼看出本次取模走了哪条路:
|
|
967
|
+
// card=卡级特例「卡」/ lane=档位路由「档」/ fallback=备用模型链「备」/ cooldown-downgrade=冷却降档「降」。
|
|
968
|
+
var MODEL_SRC_BADGE = { card: '卡', lane: '档', fallback: '备', 'cooldown-downgrade': '降' }
|
|
969
|
+
var MODEL_SRC_LABEL = { card: '卡级特例', lane: '档位路由', board: '板级模型', parent: '继承父模型', fallback: '备用模型链', 'cooldown-downgrade': '冷却降档' }
|
|
970
|
+
function modelSourceBadge(src) {
|
|
971
|
+
if (!src || src === 'board' || src === 'parent') return null
|
|
972
|
+
return React.createElement('span', { title: '取模来源:' + (MODEL_SRC_LABEL[src] || src), style: { fontSize: 8, padding: '0 3px', borderRadius: 3, border: '1px solid ' + C.warn, color: C.warn, lineHeight: '11px', flexShrink: 0 } }, MODEL_SRC_BADGE[src] || src)
|
|
973
|
+
}
|
|
974
|
+
|
|
924
975
|
function PoolStatus() {
|
|
925
976
|
var ps = state.poolStatus
|
|
926
977
|
if (!ps) return null
|
|
927
|
-
var
|
|
978
|
+
var wBusy = (ps.workers || []).filter(function (w) { return w.busy })
|
|
979
|
+
var vBusy = (ps.verifiers || []).filter(function (v) { return v.busy })
|
|
980
|
+
var wActive = wBusy.length
|
|
928
981
|
var wTotal = state.maxWorkers // 分母=并发上限而非当前池实例数,空闲也显示 0/上限
|
|
929
|
-
var vActive =
|
|
982
|
+
var vActive = vBusy.length
|
|
930
983
|
var vTotal = state.maxVerifiers
|
|
984
|
+
// 拆解官可见性(task-mvomad6c ①):decomposer 与 hook 同归 verifiers 桶,靠条目 role 择出——
|
|
985
|
+
// 在跑时于 Verifier 计数旁独立亮「🧩 拆解中 ×N」。Verifier x/y 计数口径不变(并发位占用语义:
|
|
986
|
+
// decomposer 计 verifier 位,剔掉会让水位失真);缺 role 字段的老快照条目零渲染(能力检测)。
|
|
987
|
+
var decBusy = vBusy.filter(function (v) { return v.role === 'decomposer' })
|
|
988
|
+
// 在跑子代理模型可见(task-mvnyc8vo):每个 busy run 渲染 model 小芯片 + 取模来源角标,
|
|
989
|
+
// 与执行池成员卡同口径;缺 model 字段的老数据条目不渲染芯片。
|
|
990
|
+
var chips = wBusy.concat(vBusy).filter(function (m) { return !!m.model })
|
|
931
991
|
return React.createElement('span', { style: { fontSize: 9, color: C.text2, display: 'inline-flex', gap: 4, alignItems: 'center' } },
|
|
932
992
|
// 池水位标注(反馈 n-mut9rzoq3flu / n-muyg4wl9208c):裸数字第一屏无解,补可见文字标签 + title
|
|
933
993
|
React.createElement('span', { title: '活跃 Worker 数/上限(⚡ = 在跑)', style: { display: 'inline-flex', alignItems: 'center', gap: 2 } }, ic('zap', 10), 'Worker ' + wActive + '/' + wTotal),
|
|
934
|
-
React.createElement('span', { title: '活跃 Verifier 数/上限(✓ =
|
|
994
|
+
React.createElement('span', { title: '活跃 Verifier 数/上限(✓ = 在跑;拆解官在跑也占 Verifier 并发位,见右侧 🧩 计数)', style: { display: 'inline-flex', alignItems: 'center', gap: 2 } }, ic('check', 10), 'Verifier ' + vActive + '/' + vTotal),
|
|
995
|
+
decBusy.length ? React.createElement('span', { title: '拆解官运行中(占 Verifier 并发位,源卡:' + decBusy.map(function (m) { return m.taskId || '-' }).join('、') + ')——读码产出子任务提案,落定后源卡亮「🧩 已提案」徽标', style: { display: 'inline-flex', alignItems: 'center', gap: 2, color: C.brand, fontWeight: 600 } }, '🧩 拆解中 ×' + decBusy.length) : null,
|
|
996
|
+
chips.map(function (m) {
|
|
997
|
+
return React.createElement('span', { key: m.id, title: '在跑模型:' + m.model + '(任务 ' + (m.taskId || '-') + (m.modelSource ? ',取模来源:' + (MODEL_SRC_LABEL[m.modelSource] || m.modelSource) : '') + ')', style: { display: 'inline-flex', alignItems: 'center', gap: 2, padding: '0 4px', border: '1px solid ' + C.border, borderRadius: 3, background: C.card, maxWidth: 150 } },
|
|
998
|
+
React.createElement('span', { style: { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, m.model),
|
|
999
|
+
modelSourceBadge(m.modelSource))
|
|
1000
|
+
}))
|
|
935
1001
|
}
|
|
936
1002
|
|
|
937
|
-
// #11 头部减负:池配置收纳进 ⚙️
|
|
1003
|
+
// #11 头部减负:池配置收纳进 ⚙️ 弹出层(并发/超时/开关);模型区(含 #17 verifier 异构模型设置、
|
|
1004
|
+
// 力度路由六槽、备用链)2026-10-25 起搬至「模型路由」独立弹层 ModelRoutePopover(task-mvnyc8vo)。
|
|
1005
|
+
// 六槽/备用链 2026-10-26 起升级为 OrderedModelsEditor 有序链编辑器(task-mvokikl3:下拉添加+芯片 ×/↑ 排序,
|
|
1006
|
+
// 槽位读写整个有序数组)——原「GUI 只编首选」的 laneFirst 单选形态退役。
|
|
938
1007
|
function ModelCfg(props) {
|
|
939
1008
|
var _R = React; var useState = _R.useState, useEffect = _R.useEffect
|
|
940
1009
|
var _a = useState(props.value || ''), val = _a[0], setVal = _a[1]
|
|
@@ -942,6 +1011,10 @@
|
|
|
942
1011
|
var _m = useState(null), models = _m[0], setModels = _m[1]
|
|
943
1012
|
var cfgKey = props.cfgKey || 'verifierModel'
|
|
944
1013
|
var label = props.label || 'Verifier 模型'
|
|
1014
|
+
// 只服务板级 Worker/Verifier 模型行(两段式单选,空=「继承父级」)。力度路由档位槽曾复用本组件
|
|
1015
|
+
// (task-mvneq4mw lane 分支),2026-10-26 起随链式编辑器升级迁往 OrderedModelsEditor(task-mvokikl3)——
|
|
1016
|
+
// 档位槽走 laneModels 部分合并写通道的职责随编辑器搬走,本组件不再承担。
|
|
1017
|
+
var inhLabel = props.inheritLabel || '继承父级'
|
|
945
1018
|
// 下拉列出当前网关可用模型(host list-models RPC);枚举失败降级为文本输入
|
|
946
1019
|
useEffect(function () {
|
|
947
1020
|
var cancelled = false
|
|
@@ -949,21 +1022,24 @@
|
|
|
949
1022
|
return function () { cancelled = true }
|
|
950
1023
|
}, [])
|
|
951
1024
|
useEffect(function () { setVal(props.value || ''); setDirty(false) }, [props.value])
|
|
952
|
-
function save() {
|
|
1025
|
+
function save() {
|
|
1026
|
+
setDirty(false)
|
|
1027
|
+
rpc('set-board-config', { key: cfgKey, value: (val || '').trim() }).then(fetchTasks).catch(function () {})
|
|
1028
|
+
}
|
|
953
1029
|
// 下拉两段式(task-mv9yuq2l,实证 n-mv8ki92qnhdq:巡检戳即点即写 → verifierModel 神隐回退无从审计):
|
|
954
1030
|
// 选中只改本地草稿态 + 亮 dirty(对比服务端权威值 props.value),点旁边 ✓ 保存键才走 save() 提交 rpc——
|
|
955
1031
|
// 与下方降级文本输入同款两段式语义,巡检路过戳一下不再直接写配置。
|
|
956
1032
|
function pick(v) { setVal(v); setDirty(v !== (props.value || '')) }
|
|
957
1033
|
if (models) {
|
|
958
1034
|
return React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 3, fontSize: 9, color: C.text2 } }, label,
|
|
959
|
-
React.createElement('select', { value: val, onChange: function (e) { pick(e.target.value) }, title: label + '
|
|
960
|
-
React.createElement('option', { value: '' },
|
|
1035
|
+
React.createElement('select', { value: val, onChange: function (e) { pick(e.target.value) }, title: label + '(空=' + inhLabel + ';选中后点 ✓ 才保存)', style: { width: 150, padding: '0px 4px', fontSize: 9, border: '1px solid ' + (dirty ? C.brand : C.border), borderRadius: 2, background: C.card, color: C.text } },
|
|
1036
|
+
React.createElement('option', { value: '' }, inhLabel),
|
|
961
1037
|
models.map(function (m) { return React.createElement('option', { key: m.id, value: m.id }, m.name + ' (' + m.provider + ')') })),
|
|
962
1038
|
dirty ? React.createElement('button', { onClick: save, title: '保存 ' + label, style: { fontSize: 9, padding: '0px 5px', border: 'none', borderRadius: 2, background: C.brand, color: C_INV, cursor: 'pointer', lineHeight: '14px', fontWeight: 600, display: 'inline-flex', alignItems: 'center' } }, ic('check', 9)) : null)
|
|
963
1039
|
}
|
|
964
1040
|
// 降级:文本输入(模型枚举不可用时)
|
|
965
1041
|
return React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 3, fontSize: 9, color: C.text2 } }, label,
|
|
966
|
-
React.createElement('input', { value: val, onChange: function (e) { setVal(e.target.value); setDirty(e.target.value !== (props.value || '')) }, onBlur: function () { if (dirty) save() }, onKeyDown: function (e) { if (e.key === 'Enter') save() }, placeholder: '
|
|
1042
|
+
React.createElement('input', { value: val, onChange: function (e) { setVal(e.target.value); setDirty(e.target.value !== (props.value || '')) }, onBlur: function () { if (dirty) save() }, onKeyDown: function (e) { if (e.key === 'Enter') save() }, placeholder: '空=' + inhLabel, style: { width: 110, padding: '0px 4px', fontSize: 9, border: '1px solid ' + (dirty ? C.brand : C.border2), borderRadius: 2, background: C.card, color: C.text } }),
|
|
967
1043
|
dirty ? React.createElement('button', { onClick: save, title: '应用', style: { fontSize: 9, padding: '0px 5px', border: 'none', borderRadius: 2, background: C.brand, color: C_INV, cursor: 'pointer', lineHeight: '14px', fontWeight: 600, display: 'inline-flex', alignItems: 'center' } }, ic('check', 9)) : null)
|
|
968
1044
|
}
|
|
969
1045
|
|
|
@@ -984,6 +1060,172 @@
|
|
|
984
1060
|
UI_ZOOM_LEVELS.map(function (z) { return React.createElement('option', { key: String(z), value: String(z) }, Math.round(z * 100) + '%' + (z === 1 ? '(默认)' : '')) })))
|
|
985
1061
|
}
|
|
986
1062
|
|
|
1063
|
+
// 首用引导横幅重开入口(task-mvnnkv7x-2):✕ 关闭后此前无任何重开通道(kernel.js 仅 localStorage.setItem
|
|
1064
|
+
// 写键,title 写死「不再显示」)。照 ZoomCfg 同族纯客户端偏好范式:localStorage.removeItem(ONBOARD_DISMISS_KEY)
|
|
1065
|
+
// + state.onboardDismissed=false + notify——TopPanel update 链 setObd 订阅即时重渲染出横幅,无需刷新。
|
|
1066
|
+
// 仅关闭态(state.onboardDismissed=true)有意义:组件内自守 + 「界面」小节挂载点双闸,未关闭时不渲染。
|
|
1067
|
+
function OnboardReset() {
|
|
1068
|
+
if (!state.onboardDismissed) return null
|
|
1069
|
+
function reopen() {
|
|
1070
|
+
try { localStorage.removeItem(ONBOARD_DISMISS_KEY) } catch (_) {}
|
|
1071
|
+
state.onboardDismissed = false; notify()
|
|
1072
|
+
}
|
|
1073
|
+
return React.createElement('span', { style: { display: 'inline-flex', alignItems: 'center', gap: 3, fontSize: 9, color: C.text2 } }, '引导横幅',
|
|
1074
|
+
React.createElement('button', { onClick: reopen, title: '重新显示首用引导横幅(board 视图顶部定位行)', style: { padding: '0px 5px', fontSize: 9, border: '1px solid ' + C.border, borderRadius: 2, background: C.card, color: C.text, cursor: 'pointer', lineHeight: '14px' } }, '重新显示'))
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
// ===== 模型健康点(task-mvoulhm7):🟢健康 / 🟡冷却中至HH:mm / 🔴熔断 / ⚪未知 =====
|
|
1078
|
+
// 数据只来自 get-tasks.modelHealth;title 悬浮展示探测/run 时间、最近延迟与近 5 次 run 聚合。
|
|
1079
|
+
var MODEL_HEALTH_META = { healthy: { icon: '🟢', label: '健康', color: C.ok }, cooldown: { icon: '🟡', label: '冷却中', color: C.warn }, circuit: { icon: '🔴', label: '熔断', color: C.err }, unknown: { icon: '⚪', label: '未知', color: C.text2 } }
|
|
1080
|
+
function healthHhmm(ts) { var d = new Date(Number(ts)); return isNaN(d.getTime()) ? '' : ('0' + d.getHours()).slice(-2) + ':' + ('0' + d.getMinutes()).slice(-2) }
|
|
1081
|
+
function healthTimeText(v) { if (!v) return ''; var d = new Date(v); return isNaN(d.getTime()) ? String(v) : d.toLocaleString() }
|
|
1082
|
+
function ModelHealthDot(props) {
|
|
1083
|
+
var h = (props.health && props.health[props.model]) || { state: 'unknown', note: '尚无健康信号' }
|
|
1084
|
+
var meta = MODEL_HEALTH_META[h.state] || MODEL_HEALTH_META.unknown
|
|
1085
|
+
var untilText = h.state === 'cooldown' && h.until ? healthHhmm(h.until) : ''
|
|
1086
|
+
var label = meta.label + (untilText ? '至 ' + untilText : '')
|
|
1087
|
+
var lines = [props.model + ':' + label]
|
|
1088
|
+
if (h.note) lines.push(String(h.note))
|
|
1089
|
+
if (h.lastProbe) lines.push('最近探测:' + healthTimeText(h.lastProbe))
|
|
1090
|
+
if (h.lastSeen) lines.push('最近信号:' + healthTimeText(h.lastSeen))
|
|
1091
|
+
if (typeof h.latencyMs === 'number') lines.push('最近延迟:' + h.latencyMs + 'ms')
|
|
1092
|
+
if (Array.isArray(h.recentRuns) && h.recentRuns.length) lines.push('近 ' + h.recentRuns.length + ' 次 run:' + h.recentRuns.filter(function (r) { return r.ok }).length + ' 次成功')
|
|
1093
|
+
return React.createElement('span', { role: 'img', 'aria-label': props.model + ' ' + label, title: lines.join('\n'), style: { color: meta.color, cursor: 'help', flexShrink: 0, fontSize: 9, lineHeight: '12px' } }, meta.icon)
|
|
1094
|
+
}
|
|
1095
|
+
|
|
1096
|
+
// ===== 有序模型链编辑器(task-mvokikl3 链式编辑器:下拉添加 + 芯片排序,替代首选单选与文本框)=====
|
|
1097
|
+
// 弹层内复用 7 处(力度路由六槽 + 备用模型链):当前链渲染为模型芯片行(每片 × 移除 + ↑ 前移一位),
|
|
1098
|
+
// 尾部下拉(合法目录全量、过滤已在链中的)+「添加」追加到链尾;空链显示 props.emptyHint 占位语义
|
|
1099
|
+
// (六槽=「继承板级」、备用链=「无(不启用)」)。改动即写:props.onSave(nextArr) 返回 rpc Promise——
|
|
1100
|
+
// 复用既有 set-board-config 通道(laneModels 槽位写**整个有序数组** / fallbackModels 写**整个数组**,
|
|
1101
|
+
// 零 RPC 改动),host 逐项合法清单校验、非法整体拒写(ok:false 亮 ⚠️ 不静默吞);写成功后 fetchTasks 回读权威值。
|
|
1102
|
+
// props.models=null(目录枚举失败/老宿主)时降级为文本输入 + 添加,不丢功能。
|
|
1103
|
+
function OrderedModelsEditor(props) {
|
|
1104
|
+
var _R = React; var useState = _R.useState
|
|
1105
|
+
// 读侧容错:老 host 单字符串形态归一为单元素数组;非字符串/空串项滤掉(host 归一后恒为干净数组,此处只是防御)
|
|
1106
|
+
var list = Array.isArray(props.value) ? props.value.filter(function (x) { return typeof x === 'string' && x }) : (typeof props.value === 'string' && props.value ? [props.value] : [])
|
|
1107
|
+
var _p = useState(''), pickVal = _p[0], setPickVal = _p[1]
|
|
1108
|
+
var _m = useState(''), msg = _m[0], setMsg = _m[1]
|
|
1109
|
+
var models = Array.isArray(props.models) ? props.models : null
|
|
1110
|
+
// 下拉过滤已在链中的(链内唯一,重复添加无意义)
|
|
1111
|
+
var avail = models ? models.filter(function (m) { return list.indexOf(m.id) < 0 }) : null
|
|
1112
|
+
function commit(next) {
|
|
1113
|
+
setMsg('')
|
|
1114
|
+
Promise.resolve(props.onSave(next)).then(function (r) {
|
|
1115
|
+
if (r && r.ok === false) { setMsg('⚠️ ' + (r.error || '保存失败')); setTimeout(function () { setMsg('') }, 5000); return }
|
|
1116
|
+
fetchTasks()
|
|
1117
|
+
}).catch(function () { setMsg('⚠️ 保存失败'); setTimeout(function () { setMsg('') }, 3000) })
|
|
1118
|
+
}
|
|
1119
|
+
function removeAt(i) { var next = list.slice(); next.splice(i, 1); commit(next) }
|
|
1120
|
+
function upAt(i) { if (i <= 0) return; var next = list.slice(); var t = next[i - 1]; next[i - 1] = next[i]; next[i] = t; commit(next) }
|
|
1121
|
+
function add() { var v = (pickVal || '').trim(); if (!v || list.indexOf(v) >= 0) return; setPickVal(''); commit(list.concat([v])) }
|
|
1122
|
+
var chipBtn = { fontSize: 9, padding: '0px 2px', border: 'none', background: 'transparent', color: C.text2, cursor: 'pointer', lineHeight: '12px' }
|
|
1123
|
+
return React.createElement('span', { style: { display: 'inline-flex', alignItems: 'flex-start', gap: 4, fontSize: 9, color: C.text2, whiteSpace: 'normal' } },
|
|
1124
|
+
props.label ? React.createElement('span', { style: { paddingTop: 1, minWidth: 62 } }, props.label) : null,
|
|
1125
|
+
React.createElement('span', { style: { display: 'inline-flex', flexWrap: 'wrap', gap: 3, alignItems: 'center', maxWidth: 250 } },
|
|
1126
|
+
list.length === 0 ? React.createElement('span', { style: { color: C.text2, fontStyle: 'italic', paddingTop: 1 } }, props.emptyHint || '无') : null,
|
|
1127
|
+
list.map(function (m, i) {
|
|
1128
|
+
return React.createElement('span', { key: m + '@' + i, title: (i === 0 ? '首选' : '第 ' + (i + 1) + ' 顺位') + ' · 按序取第一个健康模型', style: { display: 'inline-flex', alignItems: 'center', gap: 1, padding: '0px 2px 0px 4px', border: '1px solid ' + C.border, borderRadius: 3, background: C.nested, color: C.text, whiteSpace: 'nowrap' } },
|
|
1129
|
+
React.createElement(ModelHealthDot, { model: m, health: props.health }),
|
|
1130
|
+
React.createElement('span', null, m),
|
|
1131
|
+
i > 0 ? React.createElement('button', { onClick: function () { upAt(i) }, title: '前移一位(↑)', style: chipBtn }, '↑') : null,
|
|
1132
|
+
React.createElement('button', { onClick: function () { removeAt(i) }, title: '移除(×)', style: chipBtn }, '×'))
|
|
1133
|
+
})),
|
|
1134
|
+
// 尾部:下拉(过滤已选)/ 目录缺失降级文本输入 +「添加」追加链尾
|
|
1135
|
+
avail ? React.createElement('select', { value: pickVal, onChange: function (e) { setPickVal(e.target.value) }, title: '选择要追加到链尾的模型(已在链中的已过滤)', style: { width: 150, padding: '0px 2px', fontSize: 9, border: '1px solid ' + C.border, borderRadius: 2, background: C.card, color: C.text } },
|
|
1136
|
+
React.createElement('option', { value: '' }, '选择模型…'),
|
|
1137
|
+
avail.map(function (m) { return React.createElement('option', { key: m.id, value: m.id }, m.name + ' (' + m.provider + ')') }))
|
|
1138
|
+
: React.createElement('input', { value: pickVal, onChange: function (e) { setPickVal(e.target.value) }, onKeyDown: function (e) { if (e.key === 'Enter') add() }, placeholder: 'provider/model', title: '模型目录不可用,手输 provider/model 后点添加', style: { width: 110, padding: '0px 4px', fontSize: 9, border: '1px solid ' + C.border2, borderRadius: 2, background: C.card, color: C.text } }),
|
|
1139
|
+
React.createElement('button', { onClick: add, disabled: !pickVal, title: '追加到链尾', style: { fontSize: 9, padding: '0px 5px', border: '1px solid ' + C.border, borderRadius: 2, background: C.card, color: pickVal ? C.brand : C.text2, cursor: pickVal ? 'pointer' : 'default', lineHeight: '14px', fontWeight: 600 } }, '添加'),
|
|
1140
|
+
msg ? React.createElement('span', { style: { color: C.err, maxWidth: 140 } }, msg) : null)
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
// ===== 模型路由独立弹层(task-mvnyc8vo,用户 2026-10-25「模型配置独立弹出卡片」)=====
|
|
1144
|
+
// 模型相关全部配置从池配置弹层**搬走**(不是复制)到本弹层:①板级 Worker/Verifier 模型(ModelCfg
|
|
1145
|
+
// 复用)②力度路由六槽 ③fallbackModels 备用链——②③ 2026-10-26 起统一为 OrderedModelsEditor
|
|
1146
|
+
// 有序链编辑器(task-mvokikl3:下拉添加+芯片 ×/↑ 排序,替代首选单选与文本框);池配置弹层只留并发/超时/开关。
|
|
1147
|
+
// 弹层样式复用 PoolCfgPopover 的 fixed 定位模式(脱离面板 overflow 裁剪 + uiZoom 档位补偿)。
|
|
1148
|
+
function ModelRoutePopover(props) {
|
|
1149
|
+
var _R = React; var useState = _R.useState, useEffect = _R.useEffect, useRef = _R.useRef
|
|
1150
|
+
var _a = useState(false), open = _a[0], setOpen = _a[1]
|
|
1151
|
+
var _p = useState(null), pos = _p[0], setPos = _p[1]
|
|
1152
|
+
var ref = useRef(null); var btnRef = useRef(null)
|
|
1153
|
+
// 链编辑器共用模型目录:弹层取一次 list-models 下发 7 处编辑器(六槽+备用链),免每编辑器各发一次 RPC;
|
|
1154
|
+
// 目录不可用(老宿主/枚举失败)时 routeModels 恒 null,编辑器降级为文本输入 + 添加,不丢功能。
|
|
1155
|
+
var _rm = useState(null), routeModels = _rm[0], setRouteModels = _rm[1]
|
|
1156
|
+
// 健康面局部读取(不改 kernel 全局状态):弹层打开即取 + 15s 刷新;关闭时令 epoch 失效,迟到响应丢弃。
|
|
1157
|
+
var _mh = useState({}), modelHealth = _mh[0], setModelHealth = _mh[1]
|
|
1158
|
+
var _pe = useState(true), probeEnabled = _pe[0], setProbeEnabled = _pe[1]
|
|
1159
|
+
var _pi = useState(15), probeIntervalMin = _pi[0], setProbeIntervalMin = _pi[1]
|
|
1160
|
+
var healthEpoch = useRef(0)
|
|
1161
|
+
function refreshModelHealth() {
|
|
1162
|
+
var epoch = healthEpoch.current, args = state.tasksHash ? { ifHash: state.tasksHash } : {}
|
|
1163
|
+
return rpc('get-tasks', args).then(function (d) {
|
|
1164
|
+
if (!d || d.error || epoch !== healthEpoch.current) return d
|
|
1165
|
+
setModelHealth(d.modelHealth || {})
|
|
1166
|
+
setProbeEnabled(d.probeEnabled !== false)
|
|
1167
|
+
setProbeIntervalMin(Number(d.probeIntervalMin) > 0 ? Number(d.probeIntervalMin) : 15)
|
|
1168
|
+
return d
|
|
1169
|
+
}).catch(function () { return null })
|
|
1170
|
+
}
|
|
1171
|
+
useEffect(function () { if (!open) return; refreshModelHealth(); var timer = setInterval(refreshModelHealth, 15000); return function () { clearInterval(timer); healthEpoch.current++ } }, [open])
|
|
1172
|
+
useEffect(function () {
|
|
1173
|
+
var cancelled = false
|
|
1174
|
+
rpc('list-models').then(function (r) { if (!cancelled && r && r.ok && Array.isArray(r.models) && r.models.length) setRouteModels(r.models) }).catch(function () {})
|
|
1175
|
+
return function () { cancelled = true }
|
|
1176
|
+
}, [])
|
|
1177
|
+
// 改动即写(复用既有 set-board-config 通道,零 RPC 改动):六槽=laneModels 部分合并、槽位写**整个有序数组**
|
|
1178
|
+
// (非仅首选——task-mvokikl3 起 GUI 可编完整链);备用链=fallbackModels 整个数组。host 逐项合法清单校验、
|
|
1179
|
+
// 任一非法整体拒写(ok:false 由编辑器亮 ⚠️ 不静默吞);写成功后编辑器侧 fetchTasks 回读权威值。
|
|
1180
|
+
function saveLaneSlot(lane, role, nextArr) { var patch = {}; patch[lane] = {}; patch[lane][role] = nextArr; return rpc('set-board-config', { key: 'laneModels', value: patch }) }
|
|
1181
|
+
function saveFallbackChain(nextArr) { return rpc('set-board-config', { key: 'fallbackModels', value: nextArr }) }
|
|
1182
|
+
function saveProbeEnabled(next) { setProbeEnabled(next); return rpc('set-board-config', { key: 'probeEnabled', value: next }).then(refreshModelHealth).catch(refreshModelHealth) }
|
|
1183
|
+
useEffect(function () { if (!open) return; function onDown(e) { if (ref.current && !ref.current.contains(e.target)) setOpen(false) }; document.addEventListener('mousedown', onDown); return function () { document.removeEventListener('mousedown', onDown) } }, [open])
|
|
1184
|
+
function toggle() {
|
|
1185
|
+
// 与 PoolCfgPopover 同口径:fixed + 视口坐标(面板 overflow:hidden 会裁 absolute 弹层);
|
|
1186
|
+
// 界面缩放补偿——zoom 子树内 getBoundingClientRect 返回缩放后坐标,各项除以档位还原 CSS px。
|
|
1187
|
+
var z = state.uiZoom || 1
|
|
1188
|
+
if (!open && btnRef.current) { var r = btnRef.current.getBoundingClientRect(); setPos({ top: (r.bottom + 4) / z, right: Math.max(8, window.innerWidth - r.right) / z, maxH: Math.max(140, (window.innerHeight - r.bottom - 16) / z) }) }
|
|
1189
|
+
setOpen(!open)
|
|
1190
|
+
}
|
|
1191
|
+
return React.createElement('span', { ref: ref, style: { position: 'relative', display: 'inline-flex' } },
|
|
1192
|
+
React.createElement('button', { ref: btnRef, onClick: toggle, title: '模型路由(板级模型 / 力度路由六槽 / 备用模型链)', style: { fontSize: 12, padding: '3px 8px', border: '1px solid ' + (open ? C.brand : C.border), borderRadius: 6, cursor: 'pointer', background: open ? C.nested : 'transparent', color: C.text2, display: 'inline-flex', alignItems: 'center' } }, ic('cpu', 13)),
|
|
1193
|
+
open && pos ? React.createElement('div', { style: { position: 'fixed', top: pos.top + 'px', right: pos.right + 'px', padding: '8px 10px', background: C.card, border: '1px solid ' + C.border, borderRadius: 8, boxShadow: '0 4px 16px rgba(0,0,0,0.15)', zIndex: 1000, whiteSpace: 'nowrap', maxHeight: (pos.maxH || 300) + 'px', overflowY: 'auto' } },
|
|
1194
|
+
// ① 板级模型(从池配置弹层搬入,组件与写通道逐字不变)
|
|
1195
|
+
React.createElement('div', { style: { fontSize: 10, fontWeight: 600, color: C.text2, marginBottom: 5 } }, '板级模型'),
|
|
1196
|
+
React.createElement('div', { style: { display: 'flex', gap: 8, alignItems: 'center', marginBottom: 5 } },
|
|
1197
|
+
React.createElement(ModelCfg, { label: 'Worker 模型', cfgKey: 'workerModel', value: props.workerModel }),
|
|
1198
|
+
props.workerModel ? React.createElement(ModelHealthDot, { model: props.workerModel, health: modelHealth }) : null,
|
|
1199
|
+
React.createElement('span', { style: { fontSize: 9, color: C.text2 } }, '(Worker 执行模型)')),
|
|
1200
|
+
React.createElement('div', { style: { display: 'flex', gap: 8, alignItems: 'center' } },
|
|
1201
|
+
React.createElement(ModelCfg, { label: 'Verifier 模型', cfgKey: 'verifierModel', value: props.verifierModel }),
|
|
1202
|
+
props.verifierModel ? React.createElement(ModelHealthDot, { model: props.verifierModel, health: modelHealth }) : null,
|
|
1203
|
+
React.createElement('span', { style: { fontSize: 9, color: C.text2 } }, '(Verifier 异构审查)')),
|
|
1204
|
+
// 主动健康嗅探配置:只打本页已配置模型;关掉后冷却/熔断/run 被动健康面仍然保留。
|
|
1205
|
+
React.createElement('div', { style: { fontSize: 10, fontWeight: 600, color: C.text2, margin: '7px 0 5px' } }, '健康嗅探(事前调度)'),
|
|
1206
|
+
React.createElement('div', { style: { display: 'flex', gap: 8, alignItems: 'center', whiteSpace: 'normal', maxWidth: 360 } },
|
|
1207
|
+
React.createElement('label', { style: { display: 'inline-flex', alignItems: 'center', gap: 4, fontSize: 9, color: C.text, cursor: 'pointer' }, title: '每间隔对板级/档位/备用链中已配置模型发一次 maxTokens=1 的极短 ping;不创建子代理' },
|
|
1208
|
+
React.createElement('input', { type: 'checkbox', checked: !!probeEnabled, onChange: function (e) { saveProbeEnabled(e.target.checked) } }), '主动探测'),
|
|
1209
|
+
React.createElement(MinCfg, { label: '间隔', cfgKey: 'probeIntervalMin', value: probeIntervalMin, min: 1, max: 1440, tip: '主动健康探测间隔(1~1440 分钟,默认 15)', onSaved: refreshModelHealth }),
|
|
1210
|
+
React.createElement('span', { style: { fontSize: 9, color: C.text2 } }, '仅已配置模型 · 关闭仍保留被动健康')),
|
|
1211
|
+
// ② 力度路由六槽(链编辑器,task-mvokikl3):三档 × Worker/Verifier 各一条有序模型链——
|
|
1212
|
+
// 读写整个槽位数组(非仅首选);空链=继承上方板级模型行。
|
|
1213
|
+
React.createElement('div', { style: { fontSize: 10, fontWeight: 600, color: C.text2, margin: '7px 0 5px' } }, '力度路由'),
|
|
1214
|
+
[['light', '轻档'], ['standard', '标档'], ['heavy', '重档']].map(function (ln) {
|
|
1215
|
+
var laneObj = (props.laneModels && props.laneModels[ln[0]]) || {}
|
|
1216
|
+
return React.createElement('div', { key: ln[0], style: { marginBottom: 4 } },
|
|
1217
|
+
React.createElement('div', { style: { marginBottom: 2 } },
|
|
1218
|
+
React.createElement(OrderedModelsEditor, { label: ln[1] + ' Worker', value: laneObj.worker, models: routeModels, emptyHint: '继承板级', health: modelHealth, onSave: function (next) { return saveLaneSlot(ln[0], 'worker', next) } })),
|
|
1219
|
+
React.createElement('div', null,
|
|
1220
|
+
React.createElement(OrderedModelsEditor, { label: ln[1] + ' Verifier', value: laneObj.verifier, models: routeModels, emptyHint: '继承板级', health: modelHealth, onSave: function (next) { return saveLaneSlot(ln[0], 'verifier', next) } })))
|
|
1221
|
+
}),
|
|
1222
|
+
React.createElement('div', { style: { fontSize: 9, color: C.text2, marginTop: 1, whiteSpace: 'normal', maxWidth: 360 } }, '每槽=有序模型链:按序取第一个健康模型(首选冷却/熔断则次选递补,槽内全病走备用链→降档→父模型);空链 = 继承上方 Worker/Verifier 模型行;卡档位走对话 task_update size=light/standard/heavy'),
|
|
1223
|
+
// ③ 备用模型链:同款链编辑器(task-mvokikl3,替代逗号/换行文本框)
|
|
1224
|
+
React.createElement('div', { style: { fontSize: 10, fontWeight: 600, color: C.text2, margin: '7px 0 5px' } }, '备用模型链'),
|
|
1225
|
+
React.createElement(OrderedModelsEditor, { label: '备用链', value: props.fbModels, models: routeModels, emptyHint: '无(不启用)', health: modelHealth, onSave: saveFallbackChain }),
|
|
1226
|
+
React.createElement('div', { style: { fontSize: 9, color: C.text2, marginTop: 3, whiteSpace: 'normal', maxWidth: 360 } }, '取模口径:卡级特例 → 档位路由 → 板级模型;备用链=档位链全不可用时的跨档逃生索——候选冷却/熔断时按链顺序找首个健康替代,链尽再走降档/父模型兜底;空链=不启用')) : null)
|
|
1227
|
+
}
|
|
1228
|
+
|
|
987
1229
|
function PoolCfgPopover(props) {
|
|
988
1230
|
var _R = React; var useState = _R.useState, useEffect = _R.useEffect, useRef = _R.useRef
|
|
989
1231
|
var _a = useState(false), open = _a[0], setOpen = _a[1]
|
|
@@ -1021,12 +1263,8 @@
|
|
|
1021
1263
|
React.createElement('div', { style: { display: 'flex', gap: 8, alignItems: 'center', marginBottom: 5 } },
|
|
1022
1264
|
React.createElement(PoolCfg, { label: 'Worker 并发', cfgKey: 'maxWorkers', value: props.maxW }),
|
|
1023
1265
|
React.createElement(PoolCfg, { label: 'Verifier 并发', cfgKey: 'maxVerifiers', value: props.maxV })),
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
React.createElement('span', { style: { fontSize: 9, color: C.text2 } }, '(Worker 执行模型)')),
|
|
1027
|
-
React.createElement('div', { style: { display: 'flex', gap: 8, alignItems: 'center' } },
|
|
1028
|
-
React.createElement(ModelCfg, { label: 'Verifier 模型', cfgKey: 'verifierModel', value: props.verifierModel }),
|
|
1029
|
-
React.createElement('span', { style: { fontSize: 9, color: C.text2 } }, '(Verifier 异构审查)')),
|
|
1266
|
+
// 模型相关配置(板级 Worker/Verifier 模型行 + 力度路由六槽)已搬至「模型路由」独立弹层
|
|
1267
|
+
//(task-mvnyc8vo,ModelRoutePopover)——本弹层只留并发/超时/开关。
|
|
1030
1268
|
React.createElement('div', { style: { fontSize: 10, fontWeight: 600, color: C.text2, margin: '7px 0 5px' } }, '两级超时(软超时只提醒主窗口,由你决定继续等待或终止)'),
|
|
1031
1269
|
React.createElement('div', { style: { display: 'flex', gap: 8, alignItems: 'center' } },
|
|
1032
1270
|
React.createElement(MinCfg, { label: '软超时', cfgKey: 'softTimeoutMin', value: props.softT, min: 1, max: 480, tip: '超过后在看板提醒主窗口,不终止 run(1~480 分钟)' }),
|
|
@@ -1054,7 +1292,7 @@
|
|
|
1054
1292
|
React.createElement('input', { type: 'checkbox', checked: props.verifyUserGuide !== false, onChange: function (e) { setCfg('verifyUserGuide', e.target.checked) } }),
|
|
1055
1293
|
React.createElement('span', null, '📋 自测指南(验收结论附用户自测步骤,详情页与报告展示)')),
|
|
1056
1294
|
// 史诗拆分总开关(板级 epicSplit,缺省 true):**只关引导,不禁机制**——关掉后 Team 提示词不再
|
|
1057
|
-
// 注入「大任务必须拆分」第 6
|
|
1295
|
+
// 注入「大任务必须拆分」第 6~8 条拆分 doctrine、create-task 响应不再附 suggestSplit 软提示;显式传 parentId 建
|
|
1058
1296
|
// 子卡、史诗自动收口/hooks 状态机照常(用户/主窗口明确要拆时不受阻)。勾选态缺字段=开,与 host 同口径。
|
|
1059
1297
|
React.createElement('div', { style: { fontSize: 10, fontWeight: 600, color: C.text2, margin: '7px 0 5px' } }, '功能'),
|
|
1060
1298
|
React.createElement('label', { style: { display: 'flex', alignItems: 'center', gap: 5, fontSize: 10, color: C.text, cursor: 'pointer', whiteSpace: 'normal', maxWidth: 260 } },
|
|
@@ -1072,7 +1310,11 @@
|
|
|
1072
1310
|
React.createElement('div', { style: { fontSize: 10, fontWeight: 600, color: C.text2, margin: '7px 0 5px' } }, '界面'),
|
|
1073
1311
|
React.createElement('div', { style: { display: 'flex', gap: 8, alignItems: 'center' } },
|
|
1074
1312
|
React.createElement(ZoomCfg, null),
|
|
1075
|
-
React.createElement('span', { style: { fontSize: 9, color: C.text2 } }, '(等比缩放面板字体/图标/间距,仅本面板生效并本地记忆)'))
|
|
1313
|
+
React.createElement('span', { style: { fontSize: 9, color: C.text2 } }, '(等比缩放面板字体/图标/间距,仅本面板生效并本地记忆)')),
|
|
1314
|
+
// 引导横幅重开行(task-mvnnkv7x-2):仅关闭态(obd=true)渲染——横幅挂着时无需重开入口
|
|
1315
|
+
state.onboardDismissed ? React.createElement('div', { style: { display: 'flex', gap: 8, alignItems: 'center', marginTop: 3 } },
|
|
1316
|
+
React.createElement(OnboardReset, null),
|
|
1317
|
+
React.createElement('span', { style: { fontSize: 9, color: C.text2 } }, '(首用定位行已 ✕ 关闭,点此重新显示)')) : null) : null)
|
|
1076
1318
|
}
|
|
1077
1319
|
|
|
1078
1320
|
function TeamView() {
|
|
@@ -1080,13 +1322,16 @@
|
|
|
1080
1322
|
// v74 一次性模型:每个活跃卡片 = 一个在跑的 run(随任务结算销毁),没有常驻成员
|
|
1081
1323
|
function memberCard(m, role) {
|
|
1082
1324
|
var isW = role === 'worker'
|
|
1325
|
+
// 拆解官可见性(task-mvomad6c ①同族):verifiers 桶里的 decomposer 条目不再冒充「Verifier · 执行中」
|
|
1326
|
+
// (缺 role 的老快照条目 isDec=false,维持原标签——零回归)
|
|
1327
|
+
var isDec = !isW && m.role === 'decomposer'
|
|
1083
1328
|
var curTask = m.taskId ? getTask(m.taskId) : null
|
|
1084
1329
|
return React.createElement('div', { key: m.id, style: { minWidth: 170, padding: '8px 10px', background: C.card, border: '1px solid ' + C.border, borderRadius: 8, borderTop: '3px solid ' + (isW ? C.brand : C.warn) } },
|
|
1085
1330
|
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 4, marginBottom: 4 } },
|
|
1086
1331
|
React.createElement('span', { style: { display: 'inline-flex', color: isW ? C.brand : C.warn } }, ic(isW ? 'zap' : 'clipboard-check', 13)),
|
|
1087
|
-
React.createElement('span', { style: { fontSize: 12, fontWeight: 700, color: C.text } }, (isW ? 'Worker' : 'Verifier') + ' · 执行中'),
|
|
1332
|
+
React.createElement('span', { style: { fontSize: 12, fontWeight: 700, color: C.text } }, (isW ? 'Worker' : (isDec ? '🧩 拆解官' : 'Verifier')) + ' · 执行中'),
|
|
1088
1333
|
React.createElement('span', { style: { marginLeft: 'auto', fontSize: 9, padding: '1px 6px', borderRadius: 3, background: C.brand, color: C_INV } }, '忙碌')),
|
|
1089
|
-
m.model ? React.createElement('div', { style: { fontSize: 9, color: C.
|
|
1334
|
+
m.model ? React.createElement('div', { style: { fontSize: 9, color: C.text2, marginBottom: 3, display: 'flex', alignItems: 'center', gap: 3 }, title: '本次取模:' + m.model + (m.modelSource ? '(' + (MODEL_SRC_LABEL[m.modelSource] || m.modelSource) + ')' : '') }, React.createElement('span', { style: { overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' } }, '🧬 ' + m.model), modelSourceBadge(m.modelSource)) : null,
|
|
1090
1335
|
curTask ? React.createElement('div', { onClick: function () { state.detailId = curTask.id; state.view = 'board'; notify() }, style: { fontSize: 10, color: C.brand, cursor: 'pointer', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, title: curTask.title }, '→ ' + curTask.title) : React.createElement('div', { style: { fontSize: 10, color: C.text2 } }, '→ ' + m.taskId),
|
|
1091
1336
|
React.createElement('div', { style: { fontSize: 9, color: C.text2, marginTop: 3 }, title: '一次性子代理会话:' + (m.runId || '') }, '会话 ' + String(m.runId || '').slice(0, 8) + '…(用完即销毁)'))
|
|
1092
1337
|
}
|
package/lib/client/kernel.js
CHANGED
|
@@ -162,6 +162,19 @@
|
|
|
162
162
|
['path', { d: 'M12.22 2h-.44a2 2 0 0 0-2 2v.18a2 2 0 0 1-1 1.73l-.43.25a2 2 0 0 1-2 0l-.15-.08a2 2 0 0 0-2.73.73l-.22.38a2 2 0 0 0 .73 2.73l.15.1a2 2 0 0 1 1 1.72v.51a2 2 0 0 1-1 1.74l-.15.09a2 2 0 0 0-.73 2.73l.22.38a2 2 0 0 0 2.73.73l.15-.08a2 2 0 0 1 2 0l.43.25a2 2 0 0 1 1 1.73V20a2 2 0 0 0 2 2h.44a2 2 0 0 0 2-2v-.18a2 2 0 0 1 1-1.73l.43-.25a2 2 0 0 1 2 0l.15.08a2 2 0 0 0 2.73-.73l.22-.39a2 2 0 0 0-.73-2.73l-.15-.08a2 2 0 0 1-1-1.74v-.5a2 2 0 0 1 1-1.74l.15-.09a2 2 0 0 0 .73-2.73l-.22-.38a2 2 0 0 0-2.73-.73l-.15.08a2 2 0 0 1-2 0l-.43-.25a2 2 0 0 1-1-1.73V4a2 2 0 0 0-2-2z' }],
|
|
163
163
|
['circle', { cx: 12, cy: 12, r: 3 }]
|
|
164
164
|
],
|
|
165
|
+
// 模型路由弹层按钮图标(task-mvnyc8vo):芯片语义(lucide cpu)
|
|
166
|
+
'cpu': [
|
|
167
|
+
['rect', { x: 4, y: 4, width: 16, height: 16, rx: 2 }],
|
|
168
|
+
['rect', { x: 9, y: 9, width: 6, height: 6 }],
|
|
169
|
+
['path', { d: 'M15 2v2' }],
|
|
170
|
+
['path', { d: 'M15 20v2' }],
|
|
171
|
+
['path', { d: 'M2 15h2' }],
|
|
172
|
+
['path', { d: 'M2 9h2' }],
|
|
173
|
+
['path', { d: 'M20 15h2' }],
|
|
174
|
+
['path', { d: 'M20 9h2' }],
|
|
175
|
+
['path', { d: 'M9 2v2' }],
|
|
176
|
+
['path', { d: 'M9 20v2' }]
|
|
177
|
+
],
|
|
165
178
|
'rocket': [
|
|
166
179
|
['path', { d: 'M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91-.09z' }],
|
|
167
180
|
['path', { d: 'm12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z' }],
|
|
@@ -242,7 +255,12 @@
|
|
|
242
255
|
var UI_ZOOM_KEY = 'dsh-agent-board-ui-zoom'
|
|
243
256
|
var UI_ZOOM_LEVELS = [0.9, 1, 1.1, 1.25, 1.5] // 90% / 100%(默认)/ 110% / 125% / 150%
|
|
244
257
|
function readUiZoom() { try { var v = parseFloat(localStorage.getItem(UI_ZOOM_KEY)); return UI_ZOOM_LEVELS.indexOf(v) >= 0 ? v : 1 } catch (_) { return 1 } }
|
|
245
|
-
|
|
258
|
+
// ===== 首用引导横幅 dismissal(反馈 n-muygxqc7045b 最低成本方案,task-mvmttnux)=====
|
|
259
|
+
// 纯客户端偏好(同 uiZoom 模式,不走 host set-board-config):localStorage 键 dsh-agent-board-onboard-dismissed,
|
|
260
|
+
// '1'=用户已 ✕ 关闭、不再渲染;读取异常一律视为未关闭(横幅照常渲染,下次 ✕ 写入成功即持久)。
|
|
261
|
+
var ONBOARD_DISMISS_KEY = 'dsh-agent-board-onboard-dismissed'
|
|
262
|
+
function readOnboardDismissed() { try { return localStorage.getItem(ONBOARD_DISMISS_KEY) === '1' } catch (_) { return false } }
|
|
263
|
+
var state = { sessionId: null, tasks: [], boardMode: 'auto', teamMode: false, workMode: 'auto', minWorkers: 1, maxWorkers: 3, minVerifiers: 0, maxVerifiers: 2, workerModel: '', verifierModel: '', laneModels: null, fallbackModels: null, softTimeoutMin: 30, hardTimeoutMin: 120, cleanupRetentionDays: 7, feedbackEnabled: true, notifyDispatch: true, notifyDone: true, epicSplit: true, verifyUserGuide: true, uiZoom: readUiZoom(), onboardDismissed: readOnboardDismissed(), lessonPushed: {}, isRoot: true, isRootEverTrue: false, isRootFalseN: 0, isRootStable: true, open: false, detailId: null, children: [], dragOver: null, dragTask: null, dispatchInfo: '', view: 'board', layoutLeft: 280, layoutRight: 0, poolStatus: null, escalatedIds: [], filterQ: '', filterPrio: [], filterTag: '', filterReview: false, selectMode: false, selected: {}, undoSnapshot: null, cardHover: '', archSort: 'time-desc', dateRange: { from: '', to: '' }, rfOpen: false, gboOpen: false, activity: {}, archived: [], archQ: '', globalBoards: [], createOpen: false, createFlash: '', mpRole: '', mpSize: '', usageSummary: null, childStats: {}, tasksErr: '', tasksHash: '' }
|
|
246
264
|
|
|
247
265
|
// #16 快捷键:Esc 逐级关闭(详情→看板→面板);输入框聚焦时不劫持
|
|
248
266
|
try {
|
|
@@ -312,7 +330,7 @@
|
|
|
312
330
|
// 「缺字段/脏值=开,只有显式 false 才关」,与 host core.cfg 同口径——单点定义,避免纠偏把脏值当真值。
|
|
313
331
|
function cfgKnobOf(src, key) { return !(src && src[key] === false) }
|
|
314
332
|
// 变更检测:与 notify 同类的轻量浅比较(只看这几个布尔开关;settings 无嵌套对象)。
|
|
315
|
-
function cfgKnobsChanged(a, b) { return a.feedbackEnabled !== b.feedbackEnabled || a.notifyDispatch !== b.notifyDispatch || a.notifyDone !== b.notifyDone || a.epicSplit !== b.epicSplit || a.verifyUserGuide !== b.verifyUserGuide }
|
|
333
|
+
function cfgKnobsChanged(a, b) { return a.feedbackEnabled !== b.feedbackEnabled || a.notifyDispatch !== b.notifyDispatch || a.notifyDone !== b.notifyDone || a.epicSplit !== b.epicSplit || a.verifyUserGuide !== b.verifyUserGuide || JSON.stringify(a.laneModels || null) !== JSON.stringify(b.laneModels || null) || JSON.stringify(a.fallbackModels || null) !== JSON.stringify(b.fallbackModels || null) }
|
|
316
334
|
|
|
317
335
|
function fetchTasks() {
|
|
318
336
|
if (!state.sessionId) return
|
|
@@ -352,7 +370,10 @@
|
|
|
352
370
|
// → 勾选框必须等下一次任意 notify 才翻面(安静板卡数秒)。这里在赋值前快照、赋值后比对,
|
|
353
371
|
// 有变化就补一次 notify:乐观更新(PoolCfgPopover)已让点击瞬时翻面,本兜底是服务端权威值纠偏
|
|
354
372
|
// (乐观值与服务端不一致时以服务端为准,失败回滚亦由此收敛)。
|
|
355
|
-
|
|
373
|
+
// 快照 laneModels 存**原值**(非 JSON 串):比较器 cfgKnobsChanged 已对双侧各做一次 stringify,
|
|
374
|
+
// 快照侧若先 stringify 一次则类型不对称(串 vs 对象/null 恒不等 → null→null 也判变化),
|
|
375
|
+
// 3s 轮询每轮补 notify、tasksHash 渲染节约当场作废(验收驳回回归,task-mvneq4mw)。
|
|
376
|
+
var cfgKnobs = { feedbackEnabled: state.feedbackEnabled, notifyDispatch: state.notifyDispatch, notifyDone: state.notifyDone, epicSplit: state.epicSplit, verifyUserGuide: state.verifyUserGuide, laneModels: state.laneModels || null, fallbackModels: state.fallbackModels || null }
|
|
356
377
|
if (tasksChanged) {
|
|
357
378
|
state.tasksHash = newHash
|
|
358
379
|
state.tasks = (d && d.tasks) || []
|
|
@@ -368,6 +389,13 @@
|
|
|
368
389
|
state.maxVerifiers = (d && d.maxVerifiers) || 2
|
|
369
390
|
state.verifierModel = (d && d.verifierModel) || ''
|
|
370
391
|
state.workerModel = (d && d.workerModel) || ''
|
|
392
|
+
// 力度路由表(task-mvneq4mw GUI):cfg 归一形态 {light/standard/heavy:{worker,verifier}} 透传进 state,
|
|
393
|
+
// 池配置弹层「力度路由」小节 6 槽下拉读它;老 host 不返回 → null(弹层按全空槽渲染,与板级模型行同语义)
|
|
394
|
+
state.laneModels = (d && d.laneModels) || null
|
|
395
|
+
// 备用模型链(task-mvnyc8vo GUI 透传 / task-mvnqxlpn 数据面):cfg 归一后的有序数组透传进 state,
|
|
396
|
+
// 模型路由弹层「备用模型链」文本框读它;老 host 不返回 → null(按空链渲染)。写通道=既有
|
|
397
|
+
// set-board-config key='fallbackModels'(host 逐项合法清单校验,非法整体拒写)。
|
|
398
|
+
state.fallbackModels = (d && d.fallbackModels) || null
|
|
371
399
|
state.softTimeoutMin = (d && d.softTimeoutMin) || 30
|
|
372
400
|
state.hardTimeoutMin = (d && d.hardTimeoutMin) || 120
|
|
373
401
|
state.poolStatus = (d && d.poolStatus) || null
|
|
@@ -615,6 +643,8 @@
|
|
|
615
643
|
var _m = useState(state.minWorkers), minW = _m[0], setMinW = _m[1]; var _n = useState(state.maxWorkers), maxW = _n[0], setMaxW = _n[1]; var _o = useState(state.minVerifiers), minV = _o[0], setMinV = _o[1]; var _p = useState(state.maxVerifiers), maxV = _p[0], setMaxV = _p[1]; var _wm = useState(state.workerModel), workerModel = _wm[0], setWorkerModel = _wm[1]; var _vm = useState(state.verifierModel), verifierModel = _vm[0], setVerifierModel = _vm[1]
|
|
616
644
|
var _q = useState(state.teamMode), teamMode = _q[0], setTeamMode = _q[1]; var _dr = useState(state.dateRange), setDR = _dr[1]
|
|
617
645
|
var _wmo = useState(state.workMode), workMode = _wmo[0], setWorkModeState = _wmo[1]
|
|
646
|
+
var _lms = useState(state.laneModels), laneMs = _lms[0], setLaneMs = _lms[1] // 力度路由表(模型路由弹层「力度路由」小节 6 槽下拉数据源)
|
|
647
|
+
var _fbm = useState(state.fallbackModels), fbMs = _fbm[0], setFbMs = _fbm[1] // 备用模型链(模型路由弹层「备用模型链」文本框数据源)
|
|
618
648
|
var _cro = useState(state.createOpen), createOpen = _cro[0], setCreateOpen = _cro[1]; var _crf = useState(state.createFlash), createFlash = _crf[0], setCreateFlash = _crf[1]
|
|
619
649
|
var _te = useState(state.tasksErr), tasksErr = _te[0], setTasksErr = _te[1] // 读路径错误条(无错不渲染)
|
|
620
650
|
var _sto = useState(state.softTimeoutMin), softT = _sto[0], setSoftT = _sto[1]; var _hto = useState(state.hardTimeoutMin), hardT = _hto[0], setHardT = _hto[1]
|
|
@@ -624,8 +654,9 @@
|
|
|
624
654
|
var _vug = useState(state.verifyUserGuide), vugOn = _vug[0], setVugOn = _vug[1] // 自测指南开关勾选态(设置区「验收」)
|
|
625
655
|
var _crd = useState(state.cleanupRetentionDays), crd = _crd[0], setCrd = _crd[1] // 清理保留天数(设置区「存储清理」)
|
|
626
656
|
var _uzm = useState(state.uiZoom), uiZoom = _uzm[0], setUiZoom = _uzm[1] // 界面缩放档位(设置区「界面」,zoom 挂面板根容器)
|
|
657
|
+
var _obd = useState(state.onboardDismissed), obd = _obd[0], setObd = _obd[1] // 首用引导横幅关闭态(✕ 写 localStorage 持久化,仅 board 视图 content 内渲染)
|
|
627
658
|
var _usg = useState(state.usageSummary), setUsage = _usg[1] // Token 区聚合订阅(task-mv10lvud 根修):usageSummary 换新对象即触发渲染
|
|
628
|
-
useEffect(function () { function update() { setOpen(state.open); setTasksState(state.tasks); setModeState(state.boardMode); setDetailId(state.detailId); setDragOver(state.dragOver); setDispatchInfo(state.dispatchInfo); setViewState(state.view); setLayL(state.layoutLeft); setLayR(state.layoutRight); setMinW(state.minWorkers); setMaxW(state.maxWorkers); setMinV(state.minVerifiers); setMaxV(state.maxVerifiers); setWorkerModel(state.workerModel); setVerifierModel(state.verifierModel); setTeamMode(state.teamMode); setWorkModeState(state.workMode); setDR(state.dateRange); setSoftT(state.softTimeoutMin); setHardT(state.hardTimeoutMin); setFbEnabled(state.feedbackEnabled); setNdOn(state.notifyDispatch); setNnOn(state.notifyDone); setEsOn(state.epicSplit); setVugOn(state.verifyUserGuide); setCrd(state.cleanupRetentionDays); setUiZoom(state.uiZoom); setCreateOpen(state.createOpen); setCreateFlash(state.createFlash); setTasksErr(state.tasksErr); setUsage(state.usageSummary) }; listeners.push(update); update(); return function () { var i = listeners.indexOf(update); if (i >= 0) listeners.splice(i, 1) } }, [])
|
|
659
|
+
useEffect(function () { function update() { setOpen(state.open); setTasksState(state.tasks); setModeState(state.boardMode); setDetailId(state.detailId); setDragOver(state.dragOver); setDispatchInfo(state.dispatchInfo); setViewState(state.view); setLayL(state.layoutLeft); setLayR(state.layoutRight); setMinW(state.minWorkers); setMaxW(state.maxWorkers); setMinV(state.minVerifiers); setMaxV(state.maxVerifiers); setWorkerModel(state.workerModel); setVerifierModel(state.verifierModel); setLaneMs(state.laneModels); setFbMs(state.fallbackModels); setTeamMode(state.teamMode); setWorkModeState(state.workMode); setDR(state.dateRange); setSoftT(state.softTimeoutMin); setHardT(state.hardTimeoutMin); setFbEnabled(state.feedbackEnabled); setNdOn(state.notifyDispatch); setNnOn(state.notifyDone); setEsOn(state.epicSplit); setVugOn(state.verifyUserGuide); setCrd(state.cleanupRetentionDays); setUiZoom(state.uiZoom); setObd(state.onboardDismissed); setCreateOpen(state.createOpen); setCreateFlash(state.createFlash); setTasksErr(state.tasksErr); setUsage(state.usageSummary) }; listeners.push(update); update(); return function () { var i = listeners.indexOf(update); if (i >= 0) listeners.splice(i, 1) } }, [])
|
|
629
660
|
if (!open) return null
|
|
630
661
|
if (!state.isRootStable) return null // 子代理会话不渲染看板面板(读 isRootStable:瞬态 false 不闪,见 isRoot 蝶变防抖)
|
|
631
662
|
var active = tasks.filter(function (t) { return t.status !== 'archived' }); var archived = tasks.filter(function (t) { return t.status === 'archived' })
|
|
@@ -645,7 +676,9 @@
|
|
|
645
676
|
var ta = a.archivedAt || a.resolvedAt || a.createdAt || '', tb = b.archivedAt || b.resolvedAt || b.createdAt || ''
|
|
646
677
|
return state.archSort === 'time-asc' ? ta.localeCompare(tb) : tb.localeCompare(ta)
|
|
647
678
|
})
|
|
648
|
-
|
|
679
|
+
// 首用引导横幅(task-mvmttnux,反馈 n-muygxqc7045b 最低成本方案):仅未 dismiss 时渲染(localStorage 持久化),
|
|
680
|
+
// 「?」链 README + ✕ 关闭;独立于下方空态 CTA 行,不动空态统一决策(六列骨架照常)。
|
|
681
|
+
content = React.createElement('div', null, React.createElement(FilterBar, null), hasFilter && active.length === 0 && archived.length === 0 ? React.createElement('div', { style: { textAlign: 'center', padding: 16, color: C.text2, fontSize: 11 } }, '无匹配任务') : null, obd ? null : React.createElement('div', { style: { fontSize: 10, color: C.text2, marginBottom: 6, display: 'flex', alignItems: 'center', gap: 6, padding: '4px 8px', background: C.nested, borderRadius: 6 } }, React.createElement('span', null, '看板 = 后台 Worker 执行、Verifier 独立验收;拆解官拆大任务,轻/标/重三档力度路由'), React.createElement('a', { href: 'https://github.com/PPawnsir/task-board-plugin#readme', target: '_blank', rel: 'noopener', title: '查看 README 使用文档', style: { color: C.brand, textDecoration: 'none', fontWeight: 600, flexShrink: 0 } }, '?'), React.createElement('button', { onClick: function () { try { localStorage.setItem(ONBOARD_DISMISS_KEY, '1') } catch (_) {} state.onboardDismissed = true; notify() }, title: '关闭引导(可在 ⚙️ 设置-界面重新开启)', style: { marginLeft: 'auto', border: 'none', background: 'transparent', cursor: 'pointer', fontSize: 10, color: C.text2, padding: '0 2px', lineHeight: 1, flexShrink: 0 } }, '✕')), React.createElement('div', { style: { fontSize: 10, color: C.text2, marginBottom: 6 } }, state.selectMode ? '多选模式:点击卡片勾选,底部批量操作' : '提示:拖拽卡片到目标列即可流转状态'), tasks.length === 0 ? React.createElement('div', { style: { textAlign: 'center', padding: '16px 8px 12px', color: C.text2, fontSize: 12 } }, '点击右上角', React.createElement('span', { style: { color: C.brand, fontWeight: 600 } }, '「+ 新建任务」'), '创建第一张卡,或直接在对话里说:帮我建一张任务卡') : null, React.createElement('div', { style: { display: 'flex', gap: 8, overflowX: 'auto', paddingBottom: 4 } }, cols), React.createElement(BatchBar, null))
|
|
649
682
|
}
|
|
650
683
|
// 面板根容器(顶层 div):zoom 挂这里——等比缩放整个面板子树(字体/图标/间距),只影响本面板
|
|
651
684
|
// 不影响宿主页面;uiZoom 为订阅值(设置弹层选档 → state.uiZoom + notify → 本组件重渲染即时生效)。
|
|
@@ -654,7 +687,9 @@
|
|
|
654
687
|
React.createElement('div', { style: { display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '6px 12px', borderBottom: '1px solid ' + C.border, flexShrink: 0, flexWrap: 'wrap', gap: 4 } },
|
|
655
688
|
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 6 } }, React.createElement('span', { style: { fontWeight: 600, fontSize: 13, color: C.text, display: 'inline-flex', alignItems: 'center', gap: 5 } }, ic('clipboard-list', 15), '智能看板'), React.createElement(ViewTab, null), React.createElement(PoolStatus, null)),
|
|
656
689
|
React.createElement('div', { style: { display: 'flex', alignItems: 'center', gap: 4, flexWrap: 'wrap' } },
|
|
657
|
-
React.createElement(PoolCfgPopover, { minW: minW, maxW: maxW, minV: minV, maxV: maxV,
|
|
690
|
+
React.createElement(PoolCfgPopover, { minW: minW, maxW: maxW, minV: minV, maxV: maxV, softT: softT, hardT: hardT, feedbackEnabled: fbEnabled, notifyDispatch: ndOn, notifyDone: nnOn, epicSplit: esOn, verifyUserGuide: vugOn, retentionDays: crd }),
|
|
691
|
+
// 模型路由独立弹层(task-mvnyc8vo):板级模型 + 力度路由六槽 + 备用链,从池配置弹层搬走
|
|
692
|
+
React.createElement(ModelRoutePopover, { workerModel: workerModel, verifierModel: verifierModel, laneModels: laneMs, fbModels: fbMs }),
|
|
658
693
|
dispatchInfo ? React.createElement('span', { style: { fontSize: 9, color: C.brand, maxWidth: 120, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, title: dispatchInfo }, dispatchInfo) : null,
|
|
659
694
|
React.createElement('button', { onClick: function () { state.createOpen = true; notify() }, title: '新建任务(可存为草稿)', style: { fontSize: 11, padding: '3px 8px', border: '1px solid ' + C.border, borderRadius: 6, cursor: 'pointer', background: 'transparent', color: C.text2, display: 'inline-flex', alignItems: 'center', gap: 3 } }, ic('plus', 11), '新建任务'),
|
|
660
695
|
createFlash ? React.createElement('span', { style: { fontSize: 10, color: C.ok } }, createFlash) : null,
|