dsh-kingdom 2.0.0 → 3.2.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/ASSETS_NOTICE.md +22 -22
- package/README.md +246 -243
- package/THIRD_PARTY_NOTICES.md +92 -92
- package/docs/DSH-KINGDOM-3.0-ANNOUNCEMENT.md +13 -0
- package/docs/V2.0-RELEASE-NOTES.md +30 -30
- package/docs/V2.0-USER-GUIDE.md +74 -74
- package/docs/V3.0-RELEASE-NOTES.md +21 -0
- package/docs/V3.0-USER-GUIDE.md +33 -0
- package/docs/V3.1-RELEASE-NOTES.md +76 -0
- package/docs/V3.2-RELEASE-NOTES.md +103 -0
- package/lib/core/binding.js +167 -60
- package/lib/core/binding.js.map +1 -1
- package/lib/core/db.js +582 -546
- package/lib/core/db.js.map +1 -1
- package/lib/core/delivery-ack.js +1356 -0
- package/lib/core/delivery-ack.js.map +1 -0
- package/lib/core/delivery-change.js +1237 -0
- package/lib/core/delivery-change.js.map +1 -0
- package/lib/core/owner-binding-intent.js +14 -25
- package/lib/core/owner-binding-intent.js.map +1 -1
- package/lib/core/owner-window.js +698 -23
- package/lib/core/owner-window.js.map +1 -1
- package/lib/core/task-service.js +144 -5
- package/lib/core/task-service.js.map +1 -1
- package/lib/core/territory.js +43 -9
- package/lib/core/territory.js.map +1 -1
- package/lib/gui/assets/characters/chancellor-idle.svg +67 -67
- package/lib/gui/assets/characters/chancellor-sleeping.svg +67 -67
- package/lib/gui/assets/characters/chancellor-thinking.svg +67 -67
- package/lib/gui/assets/characters/chancellor-working.svg +67 -67
- package/lib/gui/assets/characters/knight-redraw-r1-idle.svg +67 -67
- package/lib/gui/assets/characters/knight-redraw-r1-sleeping.svg +67 -67
- package/lib/gui/assets/characters/knight-redraw-r1-thinking.svg +67 -67
- package/lib/gui/assets/characters/knight-redraw-r1-working.svg +67 -67
- package/lib/gui/assets/characters/supervisor-idle.svg +67 -67
- package/lib/gui/assets/characters/supervisor-sleeping.svg +67 -67
- package/lib/gui/assets/characters/supervisor-thinking.svg +67 -67
- package/lib/gui/assets/characters/supervisor-working.svg +67 -67
- package/lib/gui/console-app.js +7 -1
- package/lib/gui/console-app.js.map +1 -1
- package/lib/gui/contract.js +6 -0
- package/lib/gui/contract.js.map +1 -1
- package/lib/gui/control-contract.js +3 -2
- package/lib/gui/control-contract.js.map +1 -1
- package/lib/gui/owner-control.js +114 -4
- package/lib/gui/owner-control.js.map +1 -1
- package/lib/gui/owner-ui.js +645 -183
- package/lib/gui/owner-ui.js.map +1 -1
- package/lib/gui/server.js +109 -109
- package/lib/gui/snapshot.js +127 -4
- package/lib/gui/snapshot.js.map +1 -1
- package/lib/gui/visual-assets.js +37 -21
- package/lib/gui/visual-assets.js.map +1 -1
- package/lib/gui/visual-system.js +269 -269
- package/lib/gui/workbench-ui.js +549 -136
- package/lib/gui/workbench-ui.js.map +1 -1
- package/lib/gui/workbench.js +133 -0
- package/lib/gui/workbench.js.map +1 -1
- package/lib/index.js +392 -40
- package/lib/index.js.map +1 -1
- package/lib/types/core/binding.d.ts +29 -0
- package/lib/types/core/db.d.ts +14 -0
- package/lib/types/core/delivery-ack.d.ts +688 -0
- package/lib/types/core/delivery-change.d.ts +381 -0
- package/lib/types/core/owner-binding-intent.d.ts +9 -1
- package/lib/types/core/owner-window.d.ts +237 -3
- package/lib/types/core/task-service.d.ts +22 -0
- package/lib/types/core/territory.d.ts +18 -3
- package/lib/types/gui/contract.d.ts +162 -1
- package/lib/types/gui/owner-control.d.ts +61 -1
- package/lib/types/gui/snapshot.d.ts +8 -0
- package/lib/types/gui/visual-assets.d.ts +16 -0
- package/lib/types/gui/workbench.d.ts +47 -1
- package/lib/types/index.d.ts +60 -1
- package/lib/types/worker/governed-executor.d.ts +15 -0
- package/lib/worker/governed-executor.js +21 -0
- package/lib/worker/governed-executor.js.map +1 -1
- package/package.json +80 -75
package/lib/gui/workbench-ui.js
CHANGED
|
@@ -12,11 +12,11 @@ export const WORKBENCH_CSS = String.raw `
|
|
|
12
12
|
.workbench-item { display: grid; gap: 7px; padding: 14px 0; border-bottom: 1px solid var(--line); min-width: 0; }
|
|
13
13
|
.workbench-item:last-child { border-bottom: 0; }
|
|
14
14
|
.workbench-item a { color: var(--bamboo); text-decoration-thickness: 1px; text-underline-offset: 4px; font-weight: 600; overflow-wrap: anywhere; }
|
|
15
|
-
.workbench-item p, .workbench-item small { color: var(--muted); overflow-wrap: anywhere; }
|
|
16
|
-
.collaboration-plan { min-width: 0; overflow-wrap: anywhere; border-bottom: 1px solid var(--line); padding: 16px 0; }
|
|
17
|
-
.collaboration-plan > summary { cursor: pointer; font-weight: 600; }
|
|
18
|
-
.collaboration-plan .data-row { flex-wrap: wrap; gap: 6px; }
|
|
19
|
-
.collaboration-plan .workbench-item { padding-left: 12px; border-left: 2px solid var(--line); margin-top: 12px; }
|
|
15
|
+
.workbench-item p, .workbench-item small { color: var(--muted); overflow-wrap: anywhere; }
|
|
16
|
+
.collaboration-plan { min-width: 0; overflow-wrap: anywhere; border-bottom: 1px solid var(--line); padding: 16px 0; }
|
|
17
|
+
.collaboration-plan > summary { cursor: pointer; font-weight: 600; }
|
|
18
|
+
.collaboration-plan .data-row { flex-wrap: wrap; gap: 6px; }
|
|
19
|
+
.collaboration-plan .workbench-item { padding-left: 12px; border-left: 2px solid var(--line); margin-top: 12px; }
|
|
20
20
|
.quick-task-form { display: flex; gap: 12px; margin: 18px 0 26px; }
|
|
21
21
|
.quick-task-form input { flex: 1; min-width: 0; min-height: 48px; }
|
|
22
22
|
.quick-task-form button { flex: 0 0 auto; }
|
|
@@ -46,15 +46,15 @@ export const WORKBENCH_CSS = String.raw `
|
|
|
46
46
|
.role-inspector h3 { font-size: 21px; margin: 16px 0 10px; }
|
|
47
47
|
.role-inspector p { margin-bottom: 10px; overflow-wrap: anywhere; }
|
|
48
48
|
.role-inspector .data-row { flex-wrap: wrap; }
|
|
49
|
-
.role-inspect-button { border: 0; padding: 5px 0; color: var(--gold); background: transparent; text-decoration: underline; text-underline-offset: 4px; }
|
|
50
|
-
/* Preserve the authored joint motion at a size where the gesture reads in the map. */
|
|
51
|
-
.role-workbench-layout .chancellor-card { grid-template-columns: 104px minmax(0, 1fr) auto; }
|
|
52
|
-
.role-workbench-layout .chancellor-card .pixel-sprite { width: 100px; height: 136px; }
|
|
53
|
-
.role-workbench-layout .org-node[data-role="supervisor"] { grid-template-columns: 96px minmax(0, 1fr); min-height: 148px; }
|
|
54
|
-
.role-workbench-layout .org-node[data-role="supervisor"] .pixel-sprite { width: 92px; height: 128px; }
|
|
55
|
-
.role-workbench-layout .org-node[data-role="worker"] { grid-template-columns: 92px minmax(0, 1fr); min-height: 140px; }
|
|
56
|
-
.role-workbench-layout .org-node[data-role="worker"] .pixel-sprite { width: 88px; height: 120px; }
|
|
57
|
-
.role-workbench-layout .org-node:is([data-stage-evidence="unbound"],[data-stage-evidence="absent"]) { grid-template-columns: minmax(0, 1fr); }
|
|
49
|
+
.role-inspect-button { border: 0; padding: 5px 0; color: var(--gold); background: transparent; text-decoration: underline; text-underline-offset: 4px; }
|
|
50
|
+
/* Preserve the authored joint motion at a size where the gesture reads in the map. */
|
|
51
|
+
.role-workbench-layout .chancellor-card { grid-template-columns: 104px minmax(0, 1fr) auto; }
|
|
52
|
+
.role-workbench-layout .chancellor-card .pixel-sprite { width: 100px; height: 136px; }
|
|
53
|
+
.role-workbench-layout .org-node[data-role="supervisor"] { grid-template-columns: 96px minmax(0, 1fr); min-height: 148px; }
|
|
54
|
+
.role-workbench-layout .org-node[data-role="supervisor"] .pixel-sprite { width: 92px; height: 128px; }
|
|
55
|
+
.role-workbench-layout .org-node[data-role="worker"] { grid-template-columns: 92px minmax(0, 1fr); min-height: 140px; }
|
|
56
|
+
.role-workbench-layout .org-node[data-role="worker"] .pixel-sprite { width: 88px; height: 120px; }
|
|
57
|
+
.role-workbench-layout .org-node:is([data-stage-evidence="unbound"],[data-stage-evidence="absent"]) { grid-template-columns: minmax(0, 1fr); }
|
|
58
58
|
.settings-list { display: grid; gap: 20px; }
|
|
59
59
|
.settings-entry { border-top: 1px solid var(--line); padding-top: 16px; }
|
|
60
60
|
.settings-entry code { display: block; margin-top: 8px; user-select: all; overflow-wrap: anywhere; }
|
|
@@ -62,6 +62,60 @@ export const WORKBENCH_CSS = String.raw `
|
|
|
62
62
|
.usage-table { width: 100%; border-collapse: collapse; text-align: left; }
|
|
63
63
|
.usage-table th, .usage-table td { padding: 12px 8px; border-bottom: 1px solid var(--line); overflow-wrap: anywhere; }
|
|
64
64
|
.usage-table th { color: var(--muted); font-weight: 500; }
|
|
65
|
+
/* 交付知悉清单:成果摘要 → 模块/事项 → 证据/改动 三层结构。 */
|
|
66
|
+
.delivery-item { display: grid; gap: 10px; }
|
|
67
|
+
.delivery-head { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; flex-wrap: wrap; }
|
|
68
|
+
.delivery-acceptance { display: flex; flex-wrap: wrap; gap: 8px; margin: 2px 0; }
|
|
69
|
+
.delivery-layer { display: grid; gap: 8px; padding: 11px 0 11px 14px; border-left: 2px solid var(--line); min-width: 0; }
|
|
70
|
+
.delivery-layer > h4 { font-size: 14px; margin: 0; color: var(--muted); font-weight: 600; letter-spacing: .04em; }
|
|
71
|
+
.delivery-module { display: grid; gap: 6px; padding: 8px 0 8px 12px; border-left: 2px solid var(--line); min-width: 0; }
|
|
72
|
+
.delivery-module > h5 { font-size: 14px; margin: 0; }
|
|
73
|
+
/* v3.2.0 版面修复:此前是 grid-template-columns: minmax(0,1fr) auto。
|
|
74
|
+
右列 auto = max-content,而控制区里有一条 flex: 1 0 100% 的状态文本,
|
|
75
|
+
它把右轨撑到最宽,左轨被压成 0 宽——正文因此退化成每行一两个字、竖排的"任/务/要/求"。
|
|
76
|
+
改成分行 flex:正文占弹性主列(20rem 起),控制区按自身内容窄排,
|
|
77
|
+
放不下时整体换行,而不是继续抢宽。 */
|
|
78
|
+
.delivery-row { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-start; min-width: 0; }
|
|
79
|
+
.delivery-row-body { flex: 1 1 20rem; display: grid; gap: 5px; min-width: 0; }
|
|
80
|
+
.delivery-row-body p { margin: 0; }
|
|
81
|
+
.delivery-row-label { font-weight: 600; }
|
|
82
|
+
.delivery-controls { flex: 0 1 auto; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; min-width: 0; max-width: 100%; }
|
|
83
|
+
/* 图标按钮的就地操作提示:一个包装层 + 一条提示节点。
|
|
84
|
+
可用按钮的操作说明在 hover 或键盘 focus 时就地弹出,不挤动按钮行;禁用原因常驻可见,
|
|
85
|
+
因为键盘用户必须能读到“为什么不可用”,而不是只看到 title。 */
|
|
86
|
+
.delivery-control-hint { position: relative; display: inline-flex; }
|
|
87
|
+
.delivery-control-status { flex: 1 0 100%; margin: 4px 0 0; font-size: 13px; color: var(--muted); overflow-wrap: anywhere; }
|
|
88
|
+
.delivery-control-hint > .delivery-control-status[data-control-status="AVAILABLE"] {
|
|
89
|
+
position: absolute; top: calc(100% + 4px); left: 0; z-index: 6; display: none;
|
|
90
|
+
width: max-content; max-width: min(320px, 80vw); margin: 0; padding: 6px 8px;
|
|
91
|
+
border: 1px solid var(--line); border-radius: 8px; background: var(--panel-soft);
|
|
92
|
+
color: var(--ink); box-shadow: 0 4px 12px rgba(0, 0, 0, .18);
|
|
93
|
+
}
|
|
94
|
+
.delivery-control-hint:hover > .delivery-control-status[data-control-status="AVAILABLE"],
|
|
95
|
+
.delivery-control-hint:focus-within > .delivery-control-status[data-control-status="AVAILABLE"] { display: block; }
|
|
96
|
+
.delivery-control-hint:has(> .delivery-control-status[data-control-status="DISABLED"]) { flex: 1 0 100%; flex-wrap: wrap; }
|
|
97
|
+
.delivery-state { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; padding: 2px 0; }
|
|
98
|
+
.delivery-state-icon { display: inline-block; flex: 0 0 16px; width: 16px; height: 16px; background: currentColor; -webkit-mask: var(--delivery-state-symbol, var(--icon-unknown)) center / contain no-repeat; mask: var(--delivery-state-symbol, var(--icon-unknown)) center / contain no-repeat; }
|
|
99
|
+
.delivery-state[data-ack-state="ACKNOWLEDGED"] { --delivery-state-symbol: var(--icon-known); font-weight: 600; }
|
|
100
|
+
.delivery-state[data-ack-state="PENDING"] { --delivery-state-symbol: var(--icon-pending); }
|
|
101
|
+
.delivery-state[data-ack-state="PENDING_REVISION"] { --delivery-state-symbol: var(--icon-revised); }
|
|
102
|
+
.delivery-state[data-ack-state="NOT_CONFIRMED"] { --delivery-state-symbol: var(--icon-unknown); }
|
|
103
|
+
/* 历史(v1.0.0)接受证据较弱:可见标注,不只靠颜色,也不是普通注脚。 */
|
|
104
|
+
.delivery-evidence-weak { margin: 0; padding: 9px 11px; border: 1px solid var(--line); border-left: 3px solid var(--gold); border-radius: 8px; background: var(--panel-soft); overflow-wrap: anywhere; }
|
|
105
|
+
.delivery-evidence-weak strong { display: inline-block; margin-right: 4px; }
|
|
106
|
+
.delivery-note { margin: 0; }
|
|
107
|
+
.icon-button { display: inline-flex; align-items: center; justify-content: center; gap: 6px; min-width: 44px; min-height: 44px; padding: 6px 10px; border: 1px solid var(--line); border-radius: 8px; background: var(--panel-soft); color: var(--ink); cursor: pointer; }
|
|
108
|
+
.icon-button .ui-icon { width: 20px; height: 20px; flex: 0 0 20px; }
|
|
109
|
+
.icon-button[disabled] { opacity: .6; cursor: not-allowed; }
|
|
110
|
+
/* 语义化禁用(仍可聚焦以便键盘读出原因)与原生 disabled 保持同一可见弱化。 */
|
|
111
|
+
.icon-button[aria-disabled="true"] { opacity: .6; cursor: not-allowed; }
|
|
112
|
+
.icon-button:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }
|
|
113
|
+
.icon-button[aria-disabled="true"]:focus-visible { outline-style: dashed; }
|
|
114
|
+
.delivery-control-hint:focus-within > .icon-button { outline: 3px solid var(--gold); outline-offset: 2px; }
|
|
115
|
+
/* 复制 Owner 激活命令的就地反馈;失败与成功都写明,不只靠颜色。 */
|
|
116
|
+
.delivery-copy-status { margin: 4px 0 0; font-size: 13px; overflow-wrap: anywhere; }
|
|
117
|
+
.delivery-copy-failed { border-left: 3px solid var(--gold); padding-left: 8px; }
|
|
118
|
+
@media (prefers-reduced-motion: reduce) { .icon-button { transition: none; } }
|
|
65
119
|
.workbench-page [hidden] { display: none !important; }
|
|
66
120
|
#task-detail-content, #task-action-slot { min-width: 0; }
|
|
67
121
|
.task-return { display: inline-block; margin-bottom: 12px; color: var(--gold); }
|
|
@@ -70,13 +124,13 @@ export const WORKBENCH_CSS = String.raw `
|
|
|
70
124
|
.main-nav { order: 3; width: 100%; margin: 0; justify-content: center; }
|
|
71
125
|
.main-nav a { flex: 1 1 0; }
|
|
72
126
|
}
|
|
73
|
-
@media (max-width: 760px) {
|
|
74
|
-
.role-workbench-layout .chancellor-card { grid-template-columns: 88px minmax(0, 1fr); }
|
|
75
|
-
.role-workbench-layout .chancellor-card .pixel-sprite { width: 84px; height: 116px; }
|
|
76
|
-
.role-workbench-layout .org-node[data-role="supervisor"], .role-workbench-layout .org-node[data-role="worker"] { grid-template-columns: 82px minmax(0, 1fr); }
|
|
77
|
-
.role-workbench-layout .org-node[data-role="supervisor"] .pixel-sprite, .role-workbench-layout .org-node[data-role="worker"] .pixel-sprite { width: 78px; height: 108px; }
|
|
78
|
-
.role-workbench-layout .org-node:is([data-stage-evidence="unbound"],[data-stage-evidence="absent"]) { grid-template-columns: minmax(0, 1fr); }
|
|
79
|
-
.main-nav { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); min-width: 0; max-width: 100%; width: 100%; margin: 0; }
|
|
127
|
+
@media (max-width: 760px) {
|
|
128
|
+
.role-workbench-layout .chancellor-card { grid-template-columns: 88px minmax(0, 1fr); }
|
|
129
|
+
.role-workbench-layout .chancellor-card .pixel-sprite { width: 84px; height: 116px; }
|
|
130
|
+
.role-workbench-layout .org-node[data-role="supervisor"], .role-workbench-layout .org-node[data-role="worker"] { grid-template-columns: 82px minmax(0, 1fr); }
|
|
131
|
+
.role-workbench-layout .org-node[data-role="supervisor"] .pixel-sprite, .role-workbench-layout .org-node[data-role="worker"] .pixel-sprite { width: 78px; height: 108px; }
|
|
132
|
+
.role-workbench-layout .org-node:is([data-stage-evidence="unbound"],[data-stage-evidence="absent"]) { grid-template-columns: minmax(0, 1fr); }
|
|
133
|
+
.main-nav { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); min-width: 0; max-width: 100%; width: 100%; margin: 0; }
|
|
80
134
|
.main-nav a { min-width: 0; width: 100%; white-space: normal; text-align: center; }
|
|
81
135
|
}
|
|
82
136
|
@media (max-width: 1050px) { .role-workbench-layout:has(.role-inspector:not([hidden])) { grid-template-columns: minmax(0, 1fr); } .role-inspector { position: static; } }
|
|
@@ -101,18 +155,18 @@ export const WORKBENCH_HTML = String.raw `
|
|
|
101
155
|
<section class="workbench-block"><h3>正在推进</h3><div id="today-active" class="workbench-list"></div></section>
|
|
102
156
|
<section class="workbench-block"><h3>最近交付</h3><p>呈报、主管接受与人类验收分别展示。</p><div id="today-deliveries" class="workbench-list"></div></section>
|
|
103
157
|
</div>
|
|
104
|
-
<section class="workbench-block" style="margin-top:18px"><h3>团队内部处理</h3><p>主管审查与返工由团队继续推进,不计入你的待办。</p><div id="today-internal" class="workbench-list"></div></section>
|
|
105
|
-
<section class="workbench-block" style="margin-top:18px" aria-labelledby="collaboration-title"><h3 id="collaboration-title">按需协作计划</h3><p>单执行者仍是默认。需要协作时,只采纳一份分工明确的计划,由指定主执行者负责整合。</p><div id="collaboration-plans"></div><details data-read-key="collaboration:resources"><summary>当前工作区占用</summary><div id="collaboration-resources"></div></details></section>
|
|
158
|
+
<section class="workbench-block" style="margin-top:18px"><h3>团队内部处理</h3><p>主管审查与返工由团队继续推进,不计入你的待办。</p><div id="today-internal" class="workbench-list"></div></section>
|
|
159
|
+
<section class="workbench-block" style="margin-top:18px" aria-labelledby="collaboration-title"><h3 id="collaboration-title">按需协作计划</h3><p>单执行者仍是默认。需要协作时,只采纳一份分工明确的计划,由指定主执行者负责整合。</p><div id="collaboration-plans"></div><details data-read-key="collaboration:resources"><summary>当前工作区占用</summary><div id="collaboration-resources"></div></details></section>
|
|
106
160
|
</section>
|
|
107
161
|
<section id="inbox" data-console-section="inbox" data-console-page="inbox" class="workbench-page" hidden aria-labelledby="inbox-title">
|
|
108
162
|
<header class="workbench-heading"><div><p class="section-kicker">你的决定</p><h2 id="inbox-title">待我处理</h2><p>当前版本的人类治理入口仍为直接 Slash;这里帮助你找到准确事项。</p></div></header>
|
|
109
163
|
<section class="workbench-block" data-priority="owner"><div id="inbox-owner" class="workbench-list"></div></section>
|
|
110
164
|
<section class="workbench-block" style="margin-top:18px"><h3>由团队内部处理</h3><div id="inbox-internal" class="workbench-list"></div></section>
|
|
111
165
|
</section>
|
|
112
|
-
<section id="usage" data-console-section="usage" data-console-page="usage" class="workbench-page" hidden aria-labelledby="usage-title">
|
|
113
|
-
<header class="workbench-heading"><div><p class="section-kicker">成本基线</p><h2 id="usage-title">用量</h2><p>查看 Provider 实报与覆盖缺口;Token 数量不等于费用账单。</p></div></header>
|
|
114
|
-
<section class="workbench-block" aria-label="软预算与额外角色观测"><div id="budget-summary"></div><div id="role-cost-summary"></div><div id="cost-runtime-summary"></div><div id="prompt-cost-summary"></div></section>
|
|
115
|
-
<section class="workbench-block"><div id="usage-summary"></div><div id="usage-content"></div></section>
|
|
166
|
+
<section id="usage" data-console-section="usage" data-console-page="usage" class="workbench-page" hidden aria-labelledby="usage-title">
|
|
167
|
+
<header class="workbench-heading"><div><p class="section-kicker">成本基线</p><h2 id="usage-title">用量</h2><p>查看 Provider 实报与覆盖缺口;Token 数量不等于费用账单。</p></div></header>
|
|
168
|
+
<section class="workbench-block" aria-label="软预算与额外角色观测"><div id="budget-summary"></div><div id="role-cost-summary"></div><div id="cost-runtime-summary"></div><div id="prompt-cost-summary"></div></section>
|
|
169
|
+
<section class="workbench-block"><div id="usage-summary"></div><div id="usage-content"></div></section>
|
|
116
170
|
</section>
|
|
117
171
|
<section id="settings" data-console-section="settings" data-console-page="settings" class="workbench-page" hidden aria-labelledby="settings-title">
|
|
118
172
|
<header class="workbench-heading"><div><p class="section-kicker">运行与治理</p><h2 id="settings-title">设置</h2><p>现有入口及真实操作权限;未开放的能力不会显示为可执行按钮。</p></div></header>
|
|
@@ -163,14 +217,356 @@ export const WORKBENCH_SCRIPT = String.raw `
|
|
|
163
217
|
if (String(task.status) === 'RUNNING' && !['STARTING', 'RUNNING', 'PAUSED'].includes(value)) return '任务处于处理中;当前没有正在运行的执行。返工决定不会自动启动下一次执行。';
|
|
164
218
|
return '任务治理:' + stateDisplay(task.status) + ' · 本次执行:' + stateDisplay(value);
|
|
165
219
|
};
|
|
166
|
-
const claimSummaryPreview = value => {
|
|
167
|
-
let preview = friendly(value, '尚无呈报摘要');
|
|
168
|
-
if (typeof value === 'string' && value.length <= 32768) {
|
|
169
|
-
try { const parsed = JSON.parse(value); if (parsed && !Array.isArray(parsed) && typeof parsed === 'object' && Object.getPrototypeOf(parsed) === Object.prototype && typeof parsed.summary === 'string') preview = parsed.summary; } catch (_) { /* Plain text remains plain text. */ }
|
|
170
|
-
}
|
|
171
|
-
const characters = Array.from(preview); return characters.length > 240 ? characters.slice(0, 240).join('') + '…' : preview;
|
|
172
|
-
};
|
|
173
|
-
const
|
|
220
|
+
const claimSummaryPreview = value => {
|
|
221
|
+
let preview = friendly(value, '尚无呈报摘要');
|
|
222
|
+
if (typeof value === 'string' && value.length <= 32768) {
|
|
223
|
+
try { const parsed = JSON.parse(value); if (parsed && !Array.isArray(parsed) && typeof parsed === 'object' && Object.getPrototypeOf(parsed) === Object.prototype && typeof parsed.summary === 'string') preview = parsed.summary; } catch (_) { /* Plain text remains plain text. */ }
|
|
224
|
+
}
|
|
225
|
+
const characters = Array.from(preview); return characters.length > 240 ? characters.slice(0, 240).join('') + '…' : preview;
|
|
226
|
+
};
|
|
227
|
+
const DELIVERY_STATE_LABELS = { ACKNOWLEDGED: 'Owner 已知悉该条当前版本', PENDING: '待你知悉(当前版本)', PENDING_REVISION: '已有旧版本知悉,当前版本待你知悉', NOT_CONFIRMED: '交付尚未确认' };
|
|
228
|
+
const DELIVERY_STATE_SYMBOLS = { ACKNOWLEDGED: '✓', PENDING: '○', PENDING_REVISION: '↻', NOT_CONFIRMED: '?' };
|
|
229
|
+
const addDeliveryState = (parent, state, extra) => {
|
|
230
|
+
const row = document.createElement('span');
|
|
231
|
+
row.className = 'delivery-state';
|
|
232
|
+
row.setAttribute('data-ack-state', state);
|
|
233
|
+
row.setAttribute('data-status-icon', DELIVERY_STATE_SYMBOLS[state] || '?');
|
|
234
|
+
const icon = document.createElement('span'); icon.className = 'delivery-state-icon'; icon.setAttribute('aria-hidden', 'true'); row.append(icon);
|
|
235
|
+
append(row, 'span', (DELIVERY_STATE_LABELS[state] || state) + (extra ? ' · ' + extra : ''));
|
|
236
|
+
parent.append(row); return row;
|
|
237
|
+
};
|
|
238
|
+
/**
|
|
239
|
+
* 接受证据强度标注。legacy v1.0.0 的 TASK_ACCEPTED 缺少被审查结果 ID 与内容摘要,
|
|
240
|
+
* Owner 已裁决允许知悉,但工作台必须可见地标注「历史接受证据较弱」,且不得把它
|
|
241
|
+
* 呈现为 exact result-bound。强证据不额外占位。
|
|
242
|
+
*/
|
|
243
|
+
const addAcceptanceEvidence = (parent, evidence) => {
|
|
244
|
+
const value = record(evidence);
|
|
245
|
+
if (value.kind !== 'LEGACY_ATTEMPT_ONLY') return null;
|
|
246
|
+
const notice = document.createElement('p');
|
|
247
|
+
notice.className = 'delivery-evidence-weak';
|
|
248
|
+
notice.setAttribute('data-acceptance-evidence', 'LEGACY_ATTEMPT_ONLY');
|
|
249
|
+
notice.setAttribute('role', 'note');
|
|
250
|
+
append(notice, 'strong', '历史接受证据较弱');
|
|
251
|
+
append(notice, 'span', ' ' + friendly(value.note, '该 Task/attempt 的 TASK_ACCEPTED 是 v1.0.0 旧格式,只有尝试编号,缺少被审查结果 ID 与内容摘要;本条按真实事件字段与同 Task/attempt 的唯一 WorkerResult 判定,不构成 exact result-bound 证据。'));
|
|
252
|
+
parent.append(notice); return notice;
|
|
253
|
+
};
|
|
254
|
+
const addIconButton = (parent, iconHtml, label, key) => {
|
|
255
|
+
const button = append(parent, 'button', null, 'icon-button');
|
|
256
|
+
button.type = 'button';
|
|
257
|
+
button.setAttribute('data-focus-key', key);
|
|
258
|
+
button.setAttribute('aria-label', label);
|
|
259
|
+
button.title = label;
|
|
260
|
+
const icon = document.createElement('span'); icon.className = 'ui-icon'; icon.setAttribute('aria-hidden', 'true');
|
|
261
|
+
icon.innerHTML = iconHtml;
|
|
262
|
+
button.append(icon);
|
|
263
|
+
// 图标按钮不再重复长文案:可见状态由相邻的状态文本承担,名称与提示保留在 aria-label/title。
|
|
264
|
+
return button;
|
|
265
|
+
};
|
|
266
|
+
// 知悉入口只把准确的三元组交给 canonical Owner 窗口预选;写入仍由 Owner 授权、预览、提交与回执完成。
|
|
267
|
+
const ownerAckHref = (taskId, itemId, contentHash) => '/owner?ack_task=' + encodeURIComponent(String(taskId))
|
|
268
|
+
+ '&ack_item=' + encodeURIComponent(String(itemId)) + '&ack_hash=' + encodeURIComponent(String(contentHash));
|
|
269
|
+
/**
|
|
270
|
+
* 提问入口与知悉入口使用**同一套**精确三元组与预选协议,只是目标动作不同。
|
|
271
|
+
*
|
|
272
|
+
* 二者互不触发:打开提问不会写入知悉;打开知悉也不会产生问题。
|
|
273
|
+
*/
|
|
274
|
+
const ownerAskHref = (taskId, itemId, contentHash) => '/owner?ask_task=' + encodeURIComponent(String(taskId))
|
|
275
|
+
+ '&ask_item=' + encodeURIComponent(String(itemId)) + '&ask_hash=' + encodeURIComponent(String(contentHash));
|
|
276
|
+
/**
|
|
277
|
+
* 本条交付的最小 Owner 激活命令;动作参数决定它是知悉命令还是提问命令。
|
|
278
|
+
*
|
|
279
|
+
* 只申请 delivery.item.ack 或 delivery.item.question 之一与**本条领地范围**(territoryIds
|
|
280
|
+
* 只含本条领地),并携带非授权的 exact task/item/contentHash hint 与 hintAction;不含票据、
|
|
281
|
+
* 不含 kingdomWide 兜底、不含本机路径或凭据。范围刻意不引用提交时的主管 bindingId:
|
|
282
|
+
* 绑定可能在该快照之后退任或更换,而领地范围由 direct 入口按当前真实状态校验,命令不会
|
|
283
|
+
* 因过时绑定而无法执行。复制本身不授权、不预选、不写任何事实:人类仍须直接在 DSH 执行它。
|
|
284
|
+
*/
|
|
285
|
+
const ownerLaunchCommand = (entry, action) => {
|
|
286
|
+
const ask = action === 'ask';
|
|
287
|
+
const scope = { kingdomWide: false, territoryIds: [entry.territoryId], bindingIds: [],
|
|
288
|
+
roleTypes: [], targetSessionIds: [], workspaceRoots: [] };
|
|
289
|
+
const envelope = { kingdomId: entry.kingdomId, actions: [ask ? 'delivery.item.question' : 'delivery.item.ack'], scope, ttlMs: 600000 };
|
|
290
|
+
return '/kingdom owner.gui ' + JSON.stringify({ ...envelope, hintAction: ask ? 'ask' : 'ack',
|
|
291
|
+
taskHint: entry.taskId, itemHint: entry.itemId, contentHashHint: entry.contentHash });
|
|
292
|
+
};
|
|
293
|
+
/**
|
|
294
|
+
* 复制命令文本。只使用浏览器 Clipboard API:没有它就没有可确认成功的复制路径,
|
|
295
|
+
* 因此明确失败,不保留任何无法验证结果、也无法确认已复制的旧式回退。
|
|
296
|
+
*/
|
|
297
|
+
const copyLaunchCommand = (entry, action) => {
|
|
298
|
+
const command = ownerLaunchCommand(entry, action);
|
|
299
|
+
const clipboard = globalThis.navigator && globalThis.navigator.clipboard;
|
|
300
|
+
if (clipboard && typeof clipboard.writeText === 'function') return Promise.resolve(clipboard.writeText(command));
|
|
301
|
+
return Promise.reject(new Error('浏览器没有提供 Clipboard API'));
|
|
302
|
+
};
|
|
303
|
+
/** 复制结果就地反馈:成功提示人类去 DSH 直接执行;失败明确说明未复制,不假装成功。 */
|
|
304
|
+
const addCopyStatus = (parent, message, ok) => {
|
|
305
|
+
const host = parent && parent.parentElement ? parent.parentElement : parent;
|
|
306
|
+
if (!host) return null;
|
|
307
|
+
const existing = host.querySelector('[data-copy-status]');
|
|
308
|
+
if (existing) existing.remove();
|
|
309
|
+
const line = append(host, 'p', message, ok ? 'delivery-copy-status' : 'delivery-copy-status delivery-copy-failed');
|
|
310
|
+
line.setAttribute('data-copy-status', ok ? 'COPIED' : 'FAILED');
|
|
311
|
+
line.setAttribute('role', 'status');
|
|
312
|
+
return line;
|
|
313
|
+
};
|
|
314
|
+
/**
|
|
315
|
+
* 给图标按钮加一层「就地操作提示」。
|
|
316
|
+
*
|
|
317
|
+
* 提示文本始终跟随控件,键盘聚焦禁用按钮后可经 aria-describedby 读出原因;
|
|
318
|
+
* 「不可定位 / 已知悉」这类禁用原因不是次要注脚,直接可见,其余提示在 hover 或
|
|
319
|
+
* focus 时显示。只加一层可访问包装与一段文案,不新增控件类型或第二套状态。
|
|
320
|
+
*/
|
|
321
|
+
/**
|
|
322
|
+
* 给图标按钮加一层「就地操作提示」。
|
|
323
|
+
*
|
|
324
|
+
* 提示节点始终挂在控件自己的包装层(由调用方直接传入,不靠 DOM 类型探测):可用
|
|
325
|
+
* 按钮的说明在 hover 或键盘 focus 时可见,禁用原因常驻可见,因为键盘用户必须能
|
|
326
|
+
* 读到“为什么不可用”。两种情形共用这一个机制,不新增控件类型或第二套状态。
|
|
327
|
+
*/
|
|
328
|
+
const addControlStatus = (host, button, message, state) => {
|
|
329
|
+
// 状态行 id 只由控件自己的 focus key 派生,不读取父级属性,也不依赖 DOM 结构。
|
|
330
|
+
const statusId = 'delivery-control-status-' + String(button.getAttribute('data-focus-key') || 'control').replace(/[^A-Za-z0-9_-]/g, '-');
|
|
331
|
+
const line = append(host, 'p', message, 'delivery-control-status');
|
|
332
|
+
line.setAttribute('data-control-status', state);
|
|
333
|
+
line.setAttribute('id', statusId);
|
|
334
|
+
button.setAttribute('aria-describedby', statusId);
|
|
335
|
+
return line;
|
|
336
|
+
};
|
|
337
|
+
const addDeliveryControl = (parent, iconHtml, label, key) => {
|
|
338
|
+
const host = append(parent, 'span', null, 'delivery-control-hint');
|
|
339
|
+
const button = addIconButton(host, iconHtml, label, key);
|
|
340
|
+
return { host, button };
|
|
341
|
+
};
|
|
342
|
+
/** 语义化的「不可用」:保留 aria-disabled 与精确原因,不把禁用原因藏进只读 title。 */
|
|
343
|
+
const markControlDisabled = (host, button, reason) => {
|
|
344
|
+
button.setAttribute('aria-disabled', 'true');
|
|
345
|
+
button.setAttribute('data-disabled', 'true');
|
|
346
|
+
button.onclick = null;
|
|
347
|
+
addControlStatus(host, button, reason, 'DISABLED');
|
|
348
|
+
return button;
|
|
349
|
+
};
|
|
350
|
+
/**
|
|
351
|
+
* 可用的图标按钮:hover / 键盘 focus 时显示操作说明。
|
|
352
|
+
*
|
|
353
|
+
* 说明文本直接取调用方已经写好的 title:同一处文本同时服务 tooltip 与就地提示,
|
|
354
|
+
* 不另造第二份会漂移的文案。调用方必须先设置 button.title。
|
|
355
|
+
*/
|
|
356
|
+
const markControlAvailable = (host, button) => {
|
|
357
|
+
addControlStatus(host, button, button.title, 'AVAILABLE');
|
|
358
|
+
return button;
|
|
359
|
+
};
|
|
360
|
+
const addLaunchHintControl = (parent, entry, key, action) => {
|
|
361
|
+
// 提问与知悉各自有一条最小 direct 命令:命令携带 hintAction,兑换后才会回到同一个
|
|
362
|
+
// 动作与同一条目标,不会把提问入口悄悄退化成知悉入口。
|
|
363
|
+
const ask = action === 'ask';
|
|
364
|
+
const actionLabel = ask ? '提问' : '逐条知悉';
|
|
365
|
+
const actionName = ask ? 'delivery.item.question' : 'delivery.item.ack';
|
|
366
|
+
const exact = Boolean(entry.kingdomId) && Boolean(entry.territoryId) && Boolean(entry.taskId)
|
|
367
|
+
&& Boolean(entry.itemId) && Boolean(entry.contentHash);
|
|
368
|
+
const control = addDeliveryControl(parent, '__DELIVERY_ICON_COPY__',
|
|
369
|
+
exact
|
|
370
|
+
? '复制本条 Owner 激活命令:只申请' + actionLabel + '与本条领地范围,并携带该条 task/item/contentHash;命令不含票据,仍需你在 DSH 直接执行并授权'
|
|
371
|
+
: '本条缺少准确的王国、领地、任务或内容版本,不能生成 Owner 激活命令',
|
|
372
|
+
key);
|
|
373
|
+
const button = control.button;
|
|
374
|
+
if (!exact) {
|
|
375
|
+
// 缺少准确范围时不生成命令;原因既在 aria-label/title,也在随焦点可读的可见提示里。
|
|
376
|
+
button.setAttribute('aria-label', '本条缺少准确的王国、领地、任务或内容版本,不能生成 Owner 激活命令');
|
|
377
|
+
button.title = '缺少准确的王国、领地、任务或内容版本;不会生成命令,也不提供替代范围。';
|
|
378
|
+
control.host.setAttribute('title', button.title);
|
|
379
|
+
return markControlDisabled(control.host, button, '不可用:缺少准确的王国、领地、任务或内容版本;不会生成命令,也不提供替代范围。');
|
|
380
|
+
}
|
|
381
|
+
button.title = '复制一条 direct /kingdom owner.gui 命令:只申请 ' + actionName + ' 与本条领地范围,携带该条准确 task/item/contentHash。复制不授权、不写入;请到 DSH 直接执行。';
|
|
382
|
+
markControlAvailable(control.host, button);
|
|
383
|
+
button.onclick = () => {
|
|
384
|
+
const entryValue = { kingdomId: entry.kingdomId, territoryId: entry.territoryId,
|
|
385
|
+
taskId: entry.taskId, itemId: entry.itemId, contentHash: entry.contentHash };
|
|
386
|
+
const done = () => addCopyStatus(button, '命令已复制。请在 DSH 直接执行并核对授权范围;复制与执行都不会自动' + actionLabel + '该条。', true);
|
|
387
|
+
const failed = () => addCopyStatus(button, '命令未复制:当前浏览器没有可用的 Clipboard API,本页无法交出这条 exact 命令。恢复方式:先在 DSH 直接激活包含 ' + actionName + ' 的管理窗口,再回到本页用条目' + actionLabel + '入口预选该条;单独点击入口或单独激活管理窗口都不会回到本条。', false);
|
|
388
|
+
try { Promise.resolve(copyLaunchCommand(entryValue, action)).then(done, failed); } catch (_) { failed(); }
|
|
389
|
+
};
|
|
390
|
+
return button;
|
|
391
|
+
};
|
|
392
|
+
/**
|
|
393
|
+
* 已确认改动证据的只读入口。
|
|
394
|
+
*
|
|
395
|
+
* 只有 REPO_RELATIVE_VERIFIED 且携带精确 evidenceId/entryId 时才生成链接;
|
|
396
|
+
* 链接指向 canonical Owner 窗口,正文只在有效管理窗口内经只读接口返回。
|
|
397
|
+
* 查看不写入、不自动知悉,也不改变 Task/Claim。
|
|
398
|
+
*/
|
|
399
|
+
const ownerChangeHref = (taskId, evidenceId, entryId) => '/owner?change_task=' + encodeURIComponent(String(taskId))
|
|
400
|
+
+ '&change_evidence=' + encodeURIComponent(String(evidenceId)) + '&change_item=' + encodeURIComponent(String(entryId));
|
|
401
|
+
const addChangeControl = (parent, change, key, taskId) => {
|
|
402
|
+
const value = record(change);
|
|
403
|
+
const trusted = value.kind === 'REPO_RELATIVE_VERIFIED' && Boolean(value.evidenceId) && Boolean(value.entryId) && Boolean(taskId);
|
|
404
|
+
const label = trusted
|
|
405
|
+
? '查看改动(主管确认的改动证据):' + friendly(value.repoPath, '未提供路径') + ',固定版本 ' + friendly(value.revision, '未提供版本') + '。只读,不写入,也不会自动知悉。'
|
|
406
|
+
: '查看改动:不可定位。本条目没有可验证的仓库相对路径与固定源码版本,不提供链接。';
|
|
407
|
+
const control = addDeliveryControl(parent, trusted ? '__DELIVERY_ICON_CHANGE__' : '__DELIVERY_ICON_UNLOCATED__', label, key);
|
|
408
|
+
const button = control.button;
|
|
409
|
+
if (!trusted) {
|
|
410
|
+
button.title = friendly(value.note, '本条目没有可验证的仓库相对路径与固定源码版本;不提供改动链接。');
|
|
411
|
+
control.host.setAttribute('title', button.title);
|
|
412
|
+
return markControlDisabled(control.host, button, '不可定位:' + button.title);
|
|
413
|
+
}
|
|
414
|
+
button.title = '在有效的人类管理窗口中只读查看这条「主管确认的改动证据」;查看不写入、不自动知悉,也不改变任务或 Claim。';
|
|
415
|
+
markControlAvailable(control.host, button);
|
|
416
|
+
button.onclick = () => { globalThis.location.assign(ownerChangeHref(taskId, value.evidenceId, value.entryId)); };
|
|
417
|
+
return button;
|
|
418
|
+
};
|
|
419
|
+
const addAcknowledgeControl = (parent, item, key) => {
|
|
420
|
+
const acknowledged = Boolean(item.acknowledgement && item.acknowledgement.acknowledged);
|
|
421
|
+
const exact = Boolean(item.taskId) && Boolean(item.itemId) && Boolean(item.contentHash);
|
|
422
|
+
const control = addDeliveryControl(parent, '__DELIVERY_ICON_ACK__', acknowledged ? '已知悉(当前版本)' : '记下已知悉', key);
|
|
423
|
+
const button = control.button;
|
|
424
|
+
if (acknowledged) {
|
|
425
|
+
button.setAttribute('aria-label', '该条当前版本已有 Owner 知悉;重复知悉不会新增记录');
|
|
426
|
+
button.title = '该条当前版本已有 Owner 知悉;重复知悉不会新增记录。';
|
|
427
|
+
control.host.setAttribute('title', button.title);
|
|
428
|
+
return markControlDisabled(control.host, button, '已知悉:该条当前版本已有 Owner 知悉;重复知悉不会新增记录。');
|
|
429
|
+
}
|
|
430
|
+
if (!exact) {
|
|
431
|
+
button.setAttribute('aria-label', '该条缺少准确的条目编号或内容版本,不能进入人类管理窗口');
|
|
432
|
+
button.title = '该条缺少准确的条目编号或内容版本;不会打开管理窗口,也不提供替代选择。';
|
|
433
|
+
control.host.setAttribute('title', button.title);
|
|
434
|
+
return markControlDisabled(control.host, button, '不可用:缺少准确的条目编号或内容版本;不会打开管理窗口,也不提供替代选择。');
|
|
435
|
+
}
|
|
436
|
+
button.setAttribute('aria-label', '在人类管理窗口预选并记下已知悉「' + friendly(item.label, '该条') + '」当前版本;仍需由你授权、查看预览并确认提交,不采集理由或理解程度');
|
|
437
|
+
button.title = '在人类管理窗口预选该条当前版本;仍需由你确认授权、查看预览并提交回执。不会一键写入。';
|
|
438
|
+
markControlAvailable(control.host, button);
|
|
439
|
+
button.onclick = () => { globalThis.location.assign(ownerAckHref(item.taskId, item.itemId, item.contentHash)); };
|
|
440
|
+
return button;
|
|
441
|
+
};
|
|
442
|
+
/**
|
|
443
|
+
* 就某条交付提问的入口。
|
|
444
|
+
*
|
|
445
|
+
* 只携带 exact task/item/contentHash 打开 canonical Owner 窗口并预选该条;提问
|
|
446
|
+
* 仍由 Owner 填写、预览、提交与回执。工作台不写事实、不代替 Owner 授权,也不
|
|
447
|
+
* 投影问答正文——正文只经有效管理窗口或当前责任主管的 session-bound Tool 读取。
|
|
448
|
+
* 该控件与「知悉」完全分离:提问不会写入知悉,点击查看/提问也不会自动知悉。
|
|
449
|
+
*/
|
|
450
|
+
const addAskControl = (parent, item, key) => {
|
|
451
|
+
const exact = Boolean(item.taskId) && Boolean(item.itemId) && Boolean(item.contentHash);
|
|
452
|
+
const state = record(item.questions);
|
|
453
|
+
const total = Number(state.totalCount) || 0;
|
|
454
|
+
const pending = Number(state.pendingCount) || 0;
|
|
455
|
+
const unreachable = Boolean(total) && Boolean(state.latestReplyState) && state.latestReplyState !== 'REPLY_ACCESSIBLE';
|
|
456
|
+
const summary = total
|
|
457
|
+
? '该条已有 ' + total + ' 条提问(' + pending + ' 待回复)' + (unreachable ? ';最近一条的接收主管当前不可达' : '')
|
|
458
|
+
: '该条还没有提问记录';
|
|
459
|
+
const control = addDeliveryControl(parent, '__DELIVERY_ICON_ASK__', exact
|
|
460
|
+
? '就这条交付向接受该交付的主管提问:' + summary + '。仍须由你在管理窗口填写问题、查看预览并确认提交'
|
|
461
|
+
: '本条缺少准确的条目编号或内容版本,不能发起提问', key);
|
|
462
|
+
const button = control.button;
|
|
463
|
+
if (!exact) {
|
|
464
|
+
button.setAttribute('aria-label', '该条缺少准确的条目编号或内容版本,不能发起提问');
|
|
465
|
+
button.title = '该条缺少准确的条目编号或内容版本;不会打开管理窗口,也不提供替代选择。';
|
|
466
|
+
control.host.setAttribute('title', button.title);
|
|
467
|
+
return markControlDisabled(control.host, button, '不可用:缺少准确的条目编号或内容版本;不会打开管理窗口,也不提供替代选择。');
|
|
468
|
+
}
|
|
469
|
+
button.setAttribute('aria-label', '就「' + friendly(item.label, '该条') + '」向接受该交付的主管提问;' + summary + '。提问不是知悉,也不代表验收或任务完成');
|
|
470
|
+
button.title = '在人类管理窗口中预选该条并填写一个具体问题;提问与答复各记一条对话事实,不改变任务状态、不自动派发或唤醒。仍需由你确认授权、查看预览并提交回执。';
|
|
471
|
+
markControlAvailable(control.host, button);
|
|
472
|
+
button.onclick = () => { globalThis.location.assign(ownerAskHref(item.taskId, item.itemId, item.contentHash)); };
|
|
473
|
+
return button;
|
|
474
|
+
};
|
|
475
|
+
const addDeliveryEntry = (parent, taskId, item, keyPrefix, scope) => {
|
|
476
|
+
const row = document.createElement('article'); row.className = 'delivery-row';
|
|
477
|
+
const body = document.createElement('div'); body.className = 'delivery-row-body';
|
|
478
|
+
append(body, 'p', friendly(item.label, '条目'), 'delivery-row-label');
|
|
479
|
+
append(body, 'p', friendly(item.detail, '未提供内容'));
|
|
480
|
+
const version = document.createElement('small');
|
|
481
|
+
version.textContent = '内容版本 ' + String(item.contentHash || '').slice(0, 12) + '… · 条目编号 ' + friendly(item.itemId, '未提供');
|
|
482
|
+
body.append(version);
|
|
483
|
+
const questionState = record(item.questions);
|
|
484
|
+
if (Number(questionState.totalCount) > 0) {
|
|
485
|
+
append(body, 'small', '该条有 ' + Number(questionState.totalCount) + ' 条提问(' + Number(questionState.pendingCount) + ' 待主管回复)。未回复的问题在主管实际读取前只显示「待领取」;问答正文不进入本页。');
|
|
486
|
+
}
|
|
487
|
+
const controls = document.createElement('div'); controls.className = 'delivery-controls';
|
|
488
|
+
const acknowledgement = record(item.acknowledgement);
|
|
489
|
+
addDeliveryState(controls, String(acknowledgement.state || 'PENDING'),
|
|
490
|
+
acknowledgement.acknowledgedAt ? '于 ' + friendly(new Date(acknowledgement.acknowledgedAt).toLocaleString(), '时间未知') : null);
|
|
491
|
+
if (Number(acknowledgement.historicalCount) > 0) append(controls, 'small', '此前有 ' + acknowledgement.historicalCount + ' 次旧版本知悉,仅留历史,不覆盖当前版本。');
|
|
492
|
+
addChangeControl(controls, item.change, keyPrefix + ':change', taskId);
|
|
493
|
+
addAcknowledgeControl(controls, { label: item.label, acknowledgement: item.acknowledgement, taskId: taskId,
|
|
494
|
+
itemId: item.itemId, contentHash: item.contentHash }, keyPrefix + ':ack');
|
|
495
|
+
addAskControl(controls, { label: item.label, taskId: taskId, itemId: item.itemId,
|
|
496
|
+
contentHash: item.contentHash, questions: item.questions }, keyPrefix + ':ask');
|
|
497
|
+
// 一键复制的只是 direct 命令文本;授权、预选与写入都不在这里发生。
|
|
498
|
+
// 知悉与提问各有一条最小命令:各自只申请对应动作,并在兑换后各自预选同一条目标。
|
|
499
|
+
const launchHint = { kingdomId: scope.kingdomId, territoryId: scope.territoryId,
|
|
500
|
+
supervisorBindingId: scope.supervisorBindingId, taskId: taskId, itemId: item.itemId, contentHash: item.contentHash };
|
|
501
|
+
addLaunchHintControl(controls, launchHint, keyPrefix + ':launch', 'ack');
|
|
502
|
+
addLaunchHintControl(controls, launchHint, keyPrefix + ':launch-ask', 'ask');
|
|
503
|
+
row.append(body, controls); parent.append(row); return row;
|
|
504
|
+
};
|
|
505
|
+
const renderWorkbenchDelivery = (item, index) => {
|
|
506
|
+
const card = document.createElement('article'); card.className = 'workbench-item delivery-item';
|
|
507
|
+
const head = document.createElement('div'); head.className = 'delivery-head';
|
|
508
|
+
addTaskLink(head, item.taskId, item.title, 'delivery:' + item.taskId);
|
|
509
|
+
append(head, 'small', '任务治理:' + stateDisplay(item.status) + ' · 人类验收:尚未记录');
|
|
510
|
+
card.append(head);
|
|
511
|
+
const acceptance = document.createElement('div'); acceptance.className = 'delivery-acceptance';
|
|
512
|
+
append(acceptance, 'small', item.supervisorAccepted
|
|
513
|
+
? '主管已接受本次尝试的呈报;只有同一尝试的主管 ACCEPT 才使这里显示为已交付。'
|
|
514
|
+
: '主管接受尚未确认:执行者自述不等于交付,也不等于任务完成。');
|
|
515
|
+
append(acceptance, 'small', '人类验收:尚未记录。');
|
|
516
|
+
card.append(acceptance);
|
|
517
|
+
// 弱接受证据在卡片顶部可见,且先于条目层出现;不可见地折叠在注脚里不算标注。
|
|
518
|
+
if (item.deliveryConfirmed) addAcceptanceEvidence(card, item.acceptanceEvidence);
|
|
519
|
+
if (!item.deliveryConfirmed) {
|
|
520
|
+
append(card, 'p', '执行者自述:' + claimSummaryPreview(record(item.claim).summary));
|
|
521
|
+
append(card, 'p', '该任务尚无可确认交付:不展示条目层,也不提供逐条知悉。');
|
|
522
|
+
append(card, 'small', friendly(record(item.acknowledgement).note, ''));
|
|
523
|
+
return card;
|
|
524
|
+
}
|
|
525
|
+
append(card, 'small', '交付编号 ' + friendly(item.deliveryId, '未提供') + ' · 已接受尝试第 ' + friendly(item.attemptNo, '未知') + ' 次。逐条知悉不代表理解、质量认可、人类验收、Task DONE 或发布授权。');
|
|
526
|
+
// 复制命令所需的最小范围事实;缺任一项时按钮禁用,不以 kingdomWide 兜底。
|
|
527
|
+
const launchScope = { kingdomId: item.kingdomId, territoryId: item.territoryId, supervisorBindingId: item.supervisorBindingId };
|
|
528
|
+
const summaryLayer = document.createElement('section'); summaryLayer.className = 'delivery-layer';
|
|
529
|
+
append(summaryLayer, 'h4', '第一层 · 成果摘要');
|
|
530
|
+
const summaryRow = document.createElement('article'); summaryRow.className = 'delivery-row';
|
|
531
|
+
const summaryBody = document.createElement('div'); summaryBody.className = 'delivery-row-body';
|
|
532
|
+
append(summaryBody, 'p', friendly(item.summary, '未提供摘要'), 'delivery-row-label');
|
|
533
|
+
append(summaryBody, 'small', '来源:执行者呈报摘要(Claim),不是独立验证结果。外层摘要知悉不覆盖子条。');
|
|
534
|
+
const summaryControls = document.createElement('div'); summaryControls.className = 'delivery-controls';
|
|
535
|
+
const summaryAcknowledgement = record(item.summaryAcknowledgement);
|
|
536
|
+
if (item.summaryItemId) {
|
|
537
|
+
addDeliveryState(summaryControls, String(summaryAcknowledgement.state || 'PENDING'),
|
|
538
|
+
summaryAcknowledgement.acknowledgedAt ? '于 ' + friendly(new Date(summaryAcknowledgement.acknowledgedAt).toLocaleString(), '时间未知') : null);
|
|
539
|
+
addChangeControl(summaryControls, { kind: 'NOT_LOCATABLE', note: '摘要条目是执行者自述文本,不构成仓库路径;不提供改动链接。' }, 'delivery-summary:' + item.taskId + ':change', item.taskId);
|
|
540
|
+
addAcknowledgeControl(summaryControls, { label: '成果摘要', acknowledgement: summaryAcknowledgement,
|
|
541
|
+
taskId: item.taskId, itemId: item.summaryItemId, contentHash: item.summaryContentHash }, 'delivery-summary:' + item.taskId + ':ack');
|
|
542
|
+
addAskControl(summaryControls, { label: '成果摘要', taskId: item.taskId, itemId: item.summaryItemId,
|
|
543
|
+
contentHash: item.summaryContentHash, questions: item.summaryQuestions }, 'delivery-summary:' + item.taskId + ':ask');
|
|
544
|
+
addLaunchHintControl(summaryControls, { ...launchScope, taskId: item.taskId, itemId: item.summaryItemId,
|
|
545
|
+
contentHash: item.summaryContentHash }, 'delivery-summary:' + item.taskId + ':launch');
|
|
546
|
+
} else {
|
|
547
|
+
addDeliveryState(summaryControls, 'PENDING');
|
|
548
|
+
append(summaryControls, 'small', '摘要层条目投影尚未读取;不能据此判断已知悉。');
|
|
549
|
+
}
|
|
550
|
+
summaryRow.append(summaryBody, summaryControls); summaryLayer.append(summaryRow); card.append(summaryLayer);
|
|
551
|
+
const evidenceLayer = document.createElement('section'); evidenceLayer.className = 'delivery-layer';
|
|
552
|
+
append(evidenceLayer, 'h4', '第二层 · 模块/事项 → 第三层 · 证据/改动');
|
|
553
|
+
append(evidenceLayer, 'small', '只有主管在 ACCEPT 中显式选择、且本地内容寻址证据 hash 重验通过的条目才显示为可定位,并固定标注「主管确认的改动证据」。其余条目一律显示为「不可定位」:执行者自述文本、本机绝对路径与现有 SourceRef 都不会被当作路径。查看差异正文需要有效的人类管理窗口,查看不写入也不自动知悉。', 'delivery-change-policy');
|
|
554
|
+
const modules = Array.isArray(item.modules) ? item.modules : [];
|
|
555
|
+
if (!modules.length) addEmpty(evidenceLayer, '本次交付没有可展示的模块/事项条目。');
|
|
556
|
+
modules.forEach(module => {
|
|
557
|
+
const box = document.createElement('section'); box.className = 'delivery-module';
|
|
558
|
+
append(box, 'h5', friendly(module.label, '模块'));
|
|
559
|
+
append(box, 'small', friendly(module.detail, '未提供模块说明'));
|
|
560
|
+
const entries = Array.isArray(module.items) ? module.items : [];
|
|
561
|
+
if (!entries.length) append(box, 'p', '本模块当前没有条目;这不表示无风险或已验证。');
|
|
562
|
+
entries.forEach(entry => addDeliveryEntry(box, item.taskId, entry, 'delivery:' + item.taskId + ':' + entry.itemId, launchScope));
|
|
563
|
+
evidenceLayer.append(box);
|
|
564
|
+
});
|
|
565
|
+
card.append(evidenceLayer);
|
|
566
|
+
append(card, 'small', friendly(record(item.acknowledgement).note, ''));
|
|
567
|
+
return card;
|
|
568
|
+
};
|
|
569
|
+
const renderWorkbench = snapshot => {
|
|
174
570
|
const data = workbenchData(snapshot);
|
|
175
571
|
renderWorkbenchQueue('today-owner', data.ownerActions, '当前没有明确需要你处理的事项。');
|
|
176
572
|
renderWorkbenchQueue('inbox-owner', data.ownerActions, '当前没有明确需要你处理的事项。主管审查与返工会在下方单独列出。');
|
|
@@ -178,108 +574,118 @@ export const WORKBENCH_SCRIPT = String.raw `
|
|
|
178
574
|
renderWorkbenchQueue('today-internal', data.internalActions, '当前没有等待团队内部处理的事项。');
|
|
179
575
|
renderWorkbenchQueue('inbox-internal', data.internalActions, '当前没有等待团队内部处理的事项。');
|
|
180
576
|
const active = clear('today-active'); if (active) { const tasks = taskItems().filter(task => ['CREATED', 'ASSIGNED', 'RUNNING', 'REVIEW'].includes(task.status)); if (!tasks.length) addEmpty(active, '尚无进行中的任务。可以先写下一句话目标。'); tasks.slice(0, 6).forEach(task => { const row = document.createElement('article'); row.className = 'workbench-item'; addTaskLink(row, task.taskId, task.title, 'active:' + task.taskId); append(row, 'p', executionExplanation(task)); active.append(row); }); if (tasks.length > 6) { const link = append(active, 'a', '查看全部 ' + tasks.length + ' 个任务'); link.href = '#tasks'; } }
|
|
181
|
-
const deliveries = clear('today-deliveries'); if (deliveries) { const queue = data.deliveries; const items = queueItems(queue); if (!queue) addEmpty(deliveries, '最近交付投影尚未提供。'); else if (!items.length) addEmpty(deliveries, '尚无执行者呈报或交付记录。'); items.forEach(
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
plan.
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
append(
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
(
|
|
242
|
-
if (
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
append(parent, 'p',
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
577
|
+
const deliveries = clear('today-deliveries'); if (deliveries) { const queue = data.deliveries; const items = queueItems(queue); if (!queue) addEmpty(deliveries, '最近交付投影尚未提供。'); else if (!items.length) addEmpty(deliveries, '尚无执行者呈报或交付记录。'); items.forEach((item, index) => deliveries.append(renderWorkbenchDelivery(item, index))); if (queue && queue.truncated) addEmpty(deliveries, '最近交付显示 ' + items.length + ' / ' + queue.totalCount + ' 项。'); }
|
|
578
|
+
// 提问汇总只是元数据:正文不进入本页,也不构成待办或通知。旧内容版本的问题
|
|
579
|
+
// 只留历史,既不算当前待办,也不在这里被说成「当前可回复」。
|
|
580
|
+
const questions = record(data.deliveryQuestions);
|
|
581
|
+
if (deliveries && Number(questions.totalQuestions) > 0) {
|
|
582
|
+
append(deliveries, 'p', '条目提问:共 ' + Number(questions.totalQuestions) + ' 条'
|
|
583
|
+
+ '(' + Number(questions.pendingQuestions) + ' 条当前版待主管回复 / ' + Number(questions.answeredQuestions) + ' 已回复'
|
|
584
|
+
+ (Number(questions.unreachableQuestions) > 0 ? ';其中 ' + Number(questions.unreachableQuestions) + ' 条当前版提问的接收主管当前不可达,不会改投继任者' : '')
|
|
585
|
+
+ (Number(questions.historicalQuestions) > 0 ? ';另有 ' + Number(questions.historicalQuestions) + ' 条属于旧内容版本,仅留历史,不计当前待办' : '')
|
|
586
|
+
+ ')。' + friendly(questions.note, ''), 'hint');
|
|
587
|
+
}
|
|
588
|
+
renderCollaboration(data.collaboration); renderWorkbenchUsage(data.usage, data.cost); renderRoleInspector(snapshot);
|
|
589
|
+
};
|
|
590
|
+
const renderCollaboration = value => {
|
|
591
|
+
const parent = clear('collaboration-plans'); const resources = clear('collaboration-resources'); if (!parent || !resources) return;
|
|
592
|
+
const data = record(value); const plans = queueItems(data.plans); const held = queueItems(data.resources);
|
|
593
|
+
const reasons = { PLAN_NOT_ADOPTED:'等待人类采纳整份计划', PLAN_MEMBER_CHANGED:'成员或任务范围已变化,需先核对计划', PLAN_MEMBER_FAILED_OR_UNKNOWN:'成员失败或执行尚未核对,停止新增工作', TASK_NOT_STARTABLE:'任务当前不在可启动状态', DEPENDENCY_NOT_ACCEPTED_OR_STALE:'前置结果尚未接受或已过期', PLAN_NOT_CURRENT:'计划已被替换,需读取当前版本' };
|
|
594
|
+
const explain = (target, readiness, status) => {
|
|
595
|
+
if (status === 'DONE') { append(target, 'p', '主管已接受本任务;不等于人类验收。'); return; }
|
|
596
|
+
if (status === 'REVIEW') { append(target, 'p', '执行者已呈报,等待主管正常审查。'); return; }
|
|
597
|
+
if (!readiness) { append(target, 'p', '就绪情况尚不可用;不能视为已获准启动。'); return; }
|
|
598
|
+
append(target, 'p', readiness.ready ? '依赖条件已满足;启动时仍须核验身份、预算、能力及资源。' : (reasons[readiness.reasonCode] || '当前阻塞原因:' + friendly(readiness.reasonCode, '尚未确认')));
|
|
599
|
+
(readiness.blockingTaskIds || []).forEach(id => addTaskLink(target, id, '核对前置或受影响任务 ' + id, 'plan-block:' + id));
|
|
600
|
+
(readiness.acceptedResults || []).forEach(result => append(target, 'small', '已接受引用:' + result.taskId + ' · 第 ' + result.attemptNo + ' 次 · 结果 ' + result.resultId + ' · 接受记录 ' + result.acceptEventId));
|
|
601
|
+
};
|
|
602
|
+
if (!value) addEmpty(parent, '当前宿主尚未提供协作投影;不会假定已有计划或自动创建团队。');
|
|
603
|
+
else if (!plans.length) addEmpty(parent, '暂无协作计划,沿用单执行者流程。');
|
|
604
|
+
plans.forEach(plan => {
|
|
605
|
+
const panel = document.createElement('details'); panel.className = 'collaboration-plan'; panel.setAttribute('data-read-key', 'plan:' + plan.planId); panel.setAttribute('data-read-anchor', 'plan:' + plan.planId);
|
|
606
|
+
append(panel, 'summary', plan.title + ' · ' + ({PROPOSED:'待采纳',ADOPTED:'已采纳',STALE:'已过期,需重新核对'}[plan.state] || '状态待核对') + ' · 第 ' + plan.version + ' 版');
|
|
607
|
+
addTaskLink(panel, plan.parentTaskId, '查看父任务与整合结果', 'plan-parent:' + plan.planId);
|
|
608
|
+
addDataRow(panel, '协作方式', plan.mode === 'EXPERT' ? '主执行者与只读专家' : '独立工作项小团队');
|
|
609
|
+
append(panel, 'p', '拆分理由:' + plan.reason); addDataRow(panel, '主整合者', friendly(plan.integratorName, plan.integratorBindingId));
|
|
610
|
+
addDataRow(panel, '计划软额度 / 单次估计预留', plan.budgetTokens + ' / ' + plan.reserveTokens + ' Tokens');
|
|
611
|
+
const budget = plan.budget;
|
|
612
|
+
if (budget) {
|
|
613
|
+
append(panel, 'p', ({ALLOW:'预算当前允许新增接纳',WARN:'预算有风险或接近额度',BLOCK_LIMIT:'整项额度不足,停止新增工作',BLOCK_UNKNOWN:'存在未确认成本,停止新增工作'}[budget.state] || '预算状态待核对'));
|
|
614
|
+
addDataRow(panel, '成员执行实报 Token', budget.workerVerifiedTokens); addDataRow(panel, '同期王国协调成本上界 Token', budget.coordinationUpperBoundTokens);
|
|
615
|
+
addDataRow(panel, '估计预留 / 风险敞口', budget.reservedEstimateTokens + ' / ' + budget.exposureTokens);
|
|
616
|
+
addDataRow(panel, '剩余估计额度', budget.remainingTokens); addDataRow(panel, '在途 / 未确认 / 归属缺口', budget.pendingUnits + ' / ' + budget.unknownUnits + ' / ' + budget.attributionGapCount);
|
|
617
|
+
append(panel, 'small', '协调上界含同期王国观测,不能当作精确团队费用或再次分摊。金额未知;王国预算也须同时允许。');
|
|
618
|
+
} else append(panel, 'p', '尚无已采纳计划的用量视图;上方额度是计划值,不是零费用证明。');
|
|
619
|
+
plan.items.forEach(item => {
|
|
620
|
+
const row = document.createElement('article'); row.className = 'workbench-item'; addTaskLink(row, item.status === 'NOT_CREATED' ? null : item.taskId, item.title, 'plan-item:' + item.taskId);
|
|
621
|
+
append(row, 'p', '负责人:' + friendly(item.workerName, item.workerBindingId) + ' · 领地:' + friendly(item.territoryName, item.territoryId) + ' · ' + (item.access === 'READ_ONLY' ? '只读专家' : '允许范围内写入'));
|
|
622
|
+
append(row, 'p', '范围:' + item.description); append(row, 'p', '验收:' + item.acceptanceCriteria); append(row, 'p', '预期产物:' + item.expectedArtifact);
|
|
623
|
+
append(row, 'p', '前置工作:' + (item.dependsOn.length ? item.dependsOn.map(key => friendly(plan.items.find(other => other.key === key)?.title, key)).join('、') : '无'));
|
|
624
|
+
explain(row, item.readiness, item.status);
|
|
625
|
+
const occupancy = held.filter(resource => resource.taskId === item.taskId);
|
|
626
|
+
if (occupancy.length) occupancy.forEach(resource => append(row, 'small', '资源:' + (resource.recovery ? '恢复中,继续保留占用' : resource.state === 'BOUND' ? '已绑定本次执行' : '已预留,尚待绑定') + ' · 第 ' + resource.attemptNo + ' 次'));
|
|
627
|
+
else append(row, 'small', '尚无本项资源接纳记录;不表示工作区空闲,也没有自动排队派发。');
|
|
628
|
+
panel.append(row);
|
|
629
|
+
});
|
|
630
|
+
append(panel, 'h4', '父任务整合'); explain(panel, plan.integration, plan.parentStatus);
|
|
631
|
+
append(panel, 'p', '子项完成后仍由主执行者实际整合、另交呈报并经主管审查;不会自动完成父任务。', 'hint');
|
|
632
|
+
if (plan.state === 'PROPOSED') { const link = append(panel, 'a', '在独立管理窗口审阅并采纳'); link.href = '/owner'; link.setAttribute('data-focus-key', 'plan-adopt:' + plan.planId); }
|
|
633
|
+
const identity = document.createElement('details'); identity.setAttribute('data-read-key', 'plan-identity:' + plan.planId); append(identity, 'summary', '查看固定计划编号与内容指纹'); append(identity, 'p', plan.planId + ' · 第 ' + plan.version + ' 版 · ' + plan.digest); panel.append(identity); parent.append(panel);
|
|
634
|
+
});
|
|
635
|
+
if (record(data.plans).truncated) addEmpty(parent, '仅显示 ' + plans.length + ' / ' + data.plans.totalCount + ' 份计划,请按任务继续核对。');
|
|
636
|
+
if (!held.length) addEmpty(resources, '当前没有已投影的工作区预留;其他原有执行仍可能占用,实际启动前必须重新检查。');
|
|
637
|
+
held.forEach(resource => { const row = document.createElement('article'); row.className = 'workbench-item'; addTaskLink(row, resource.taskId, '任务 ' + resource.taskId, 'resource:' + resource.taskId + ':' + resource.attemptNo); append(row, 'p', '第 ' + resource.attemptNo + ' 次 · ' + (resource.access === 'READ_ONLY' ? '共享只读' : '独占写入') + ' · ' + (resource.recovery ? '恢复中,继续占用' : resource.state === 'BOUND' ? '已绑定执行' : '已预留')); resources.append(row); });
|
|
638
|
+
if (record(data.resources).truncated) addEmpty(resources, '资源占用列表已截断,不能据此判断工作区空闲。');
|
|
639
|
+
};
|
|
640
|
+
const renderAdditionalRoleCost = (parent, value, taskOnly) => {
|
|
641
|
+
if (!value) { addEmpty(parent, '宰相与主管的观测尚不可用;不能按零计算。'); return; }
|
|
642
|
+
append(parent, 'h3', taskOnly ? '准确归属于本任务的宰相与主管用量' : '宰相与主管的已观测用量');
|
|
643
|
+
addDataRow(parent, '可核对的供应商实报 Token', friendly(value.verifiedTokens, '尚无完整实报,合计未知'));
|
|
644
|
+
addDataRow(parent, '完整 / 已观测轮次', value.completeUnits + ' / ' + value.units);
|
|
645
|
+
addDataRow(parent, '在途轮次 / 用量缺失轮次', value.pendingUnits + ' / ' + value.unknownUnits);
|
|
646
|
+
addDataRow(parent, '有实报 / 已观测请求', value.reportedRequests + ' / ' + value.observedRequests);
|
|
647
|
+
(Array.isArray(value.byRole) ? value.byRole : []).forEach(role => addDataRow(parent, (role.roleType === 'CHANCELLOR' ? '宰相' : role.roleType === 'SUPERVISOR' ? '主管' : friendly(role.roleType, '其他角色')) + '已完整实报 Token', friendly(role.verifiedTokens, '未知')));
|
|
648
|
+
if (!taskOnly) { addDataRow(parent, '其中共享轮次 Token', friendly(value.sharedTokens, '尚无可累计的完整实报')); addDataRow(parent, '其中未归属 Token', friendly(value.unattributedTokens, '尚无可累计的完整实报')); addDataRow(parent, '共享或未归属轮次', value.attributionGapCount); append(parent, 'p', '共享和未归属是上述实报的组成部分,不再次累加,也不摊入某个任务。', 'hint'); }
|
|
649
|
+
append(parent, 'p', taskOnly ? '本区只计准确关联本任务的轮次;共享、未归属、未接入的外部参与者及历史缺口均不摊入本任务。费用未知。' : friendly(value.coverageNote, '仅覆盖已接入的观测,外部参与者与历史缺口仍未知。') + ' 费用未知,未配置可信价格表。', 'hint');
|
|
650
|
+
};
|
|
651
|
+
const renderBudgetCost = cost => {
|
|
652
|
+
const parent = clear('budget-summary'); const roles = clear('role-cost-summary'); const runtime = clear('cost-runtime-summary'); const prompts = clear('prompt-cost-summary');
|
|
653
|
+
if (!parent || !roles || !runtime || !prompts) return;
|
|
654
|
+
append(parent, 'h3', '王国软预算'); const budget = record(cost).budget;
|
|
655
|
+
if (!budget) addEmpty(parent, '当前没有可读取的预算视图;这不表示余额充足或成本为零。');
|
|
656
|
+
else {
|
|
657
|
+
const labels = { OFF: '未启用预算拦截', ALLOW: '可接纳新的执行', WARN: '已接近额度或存在需留意的缺口', BLOCK_LIMIT: '额度不足,阻止新增执行', BLOCK_UNKNOWN: '存在未确认用量,阻止新增执行' };
|
|
658
|
+
append(parent, 'p', labels[budget.state] || '预算状态未知', 'usage-coverage');
|
|
659
|
+
addDataRow(parent, '预算周期内可核对实报 Token', budget.verifiedTokens);
|
|
660
|
+
addDataRow(parent, '其中执行者 / 其他已观测角色', budget.workerVerifiedTokens + ' / ' + budget.additionalVerifiedTokens);
|
|
661
|
+
addDataRow(parent, '已预留估计 Token', budget.reservedEstimateTokens);
|
|
662
|
+
addDataRow(parent, '实报与预留合计(估计风险敞口)', budget.exposureTokens);
|
|
663
|
+
addDataRow(parent, '剩余可接纳额度(估计)', friendly(budget.remainingTokens, '尚未设置'));
|
|
664
|
+
addDataRow(parent, '在途 / 未确认 / 恢复中', budget.pendingUnits + ' / ' + budget.unknownUnits + ' / ' + budget.recoveryUnits);
|
|
665
|
+
addDataRow(parent, '共享或未归属项', budget.attributionGapCount);
|
|
666
|
+
const policy = budget.policy;
|
|
667
|
+
if (policy) { addDataRow(parent, '政策额度 / 每次启动预留', policy.limitTokens + ' / ' + policy.reserveTokens + ' Tokens'); addDataRow(parent, '未知用量处理', policy.unknownPolicy === 'BLOCK' ? '阻止新增执行' : '提示后允许继续(风险由政策明确承担)'); addDataRow(parent, '提醒阈值', policy.warningPercent + '%'); addDataRow(parent, '统计起点 / 政策版本', '事件 ' + policy.sinceEventSeq + ' / ' + policy.revision); }
|
|
668
|
+
append(parent, 'p', friendly(budget.note, '预算仅覆盖可观测用量。') + ' 预留是启动前估计,不是供应商账单。收紧政策只影响未来新增接纳,不会自动结束在途执行或释放恢复中的占用;关闭政策不重置统计起点。金额未知。', 'hint');
|
|
669
|
+
}
|
|
670
|
+
const manage = append(parent, 'a', '在管理窗口中调整预算政策'); manage.href = '/owner';
|
|
671
|
+
renderAdditionalRoleCost(roles, record(cost).additionalRoles, false);
|
|
672
|
+
append(runtime, 'h3', '工具展示试点'); const observed = record(record(cost).runtime);
|
|
673
|
+
addDataRow(runtime, '当前配置', observed.toolDisclosureMode === 'off' ? '关闭(默认)' : observed.toolDisclosureMode === 'pilot' ? '有限试点已开启' : '未知,尚未读取运行配置');
|
|
674
|
+
addDataRow(runtime, '角色用量观测接入', observed.observerAvailable === true ? '当前监听可用' : observed.observerAvailable === false ? '当前监听不可用,已有记录仍可查看' : '接入状态未知');
|
|
675
|
+
append(runtime, 'p', '工具展示仅缩小本来已获准的工具集合,不增加权限。尚未获得真实冷暖缓存配对收益证据,不宣称节省比例。', 'hint');
|
|
676
|
+
const list = record(record(cost).prompts); const items = Array.isArray(list.items) ? list.items : [];
|
|
677
|
+
const details = document.createElement('details'); details.setAttribute('data-read-key', 'usage:assembly-byte-observations'); append(details, 'summary', '查看提示装配的字节观察'); prompts.append(details);
|
|
678
|
+
append(details, 'p', '这里只统计装配阶段的片段字节,尚未代表最终请求。历史消息字节与 Token 估算未测;不能把字节下降当作任务总成本下降。', 'hint');
|
|
679
|
+
if (!items.length) addEmpty(details, '尚无可用的提示装配观察。');
|
|
680
|
+
items.forEach(item => { const row = document.createElement('article'); row.className = 'workbench-item'; append(row, 'strong', (item.roleType === 'CHANCELLOR' ? '宰相' : item.roleType === 'SUPERVISOR' ? '主管' : item.roleType === 'WORKER' ? '执行者' : '角色') + ' · 最近装配观察'); addDataRow(row, '工具数量(筛选前 / 后)', friendly(item.toolsBefore, '未知') + ' / ' + friendly(item.toolsAfter, '未知')); addDataRow(row, '工具 schema 字节(筛选前 / 后)', friendly(item.toolsBytesBefore, '未知') + ' / ' + friendly(item.toolsBytesAfter, '未知')); addDataRow(row, '本次观察模式', item.mode === 'pilot' ? '试点' : item.mode === 'off' ? '关闭' : '未知'); (Array.isArray(item.sections) ? item.sections : []).forEach(part => addDataRow(row, '提示片段 · ' + part.name, friendly(part.bytes, '未测') + ' 字节')); (Array.isArray(item.contexts) ? item.contexts : []).forEach(part => addDataRow(row, '上下文片段 · ' + part.name, friendly(part.bytes, '未测') + ' 字节')); if (item.partsTruncated) append(row, 'p', '片段列表已达显示上限,不能视为完整请求。', 'hint'); addDataRow(row, '历史消息字节 / Token 估算', '未测 / 未测'); if (item.reasonCode) addDataRow(row, '观察说明', item.reasonCode); details.append(row); });
|
|
681
|
+
if (list.truncated) append(details, 'p', '仅显示有界的最近角色观察。', 'hint');
|
|
682
|
+
};
|
|
683
|
+
const renderWorkbenchUsage = (usage, cost) => {
|
|
684
|
+
renderBudgetCost(cost);
|
|
279
685
|
const summary = clear('usage-summary'); const content = clear('usage-content'); if (!summary || !content) return;
|
|
280
686
|
if (!usage) { addEmpty(summary, '当前宿主尚未提供用量投影,请重新读取或检查版本。'); return; }
|
|
281
687
|
const statusLabel = ({ COMPLETE: 'Worker Dispatch 报告覆盖完整', PARTIAL: 'Worker Dispatch 报告部分覆盖', UNAVAILABLE: 'Worker Dispatch 用量尚不可用' })[usage.coverage] || '报告覆盖尚未确认';
|
|
282
|
-
append(summary, 'h3', statusLabel); append(summary, 'p', '本区统计范围:Worker Dispatch;宰相、主管与其他请求不在本区合计内。', 'usage-coverage');
|
|
688
|
+
append(summary, 'h3', statusLabel); append(summary, 'p', '本区统计范围:Worker Dispatch;宰相、主管与其他请求不在本区合计内。', 'usage-coverage');
|
|
283
689
|
addDataRow(summary, '完整实报 / 全部 Dispatch', usage.completeDispatches + ' / ' + usage.totalDispatches); addDataRow(summary, '部分实报', usage.partialDispatches); addDataRow(summary, '实报不可用', usage.unavailableDispatches); addDataRow(summary, '没有 Dispatch 用量关联的执行', usage.executionsWithoutDispatch);
|
|
284
690
|
if (usage.reportedTotals) { addDataRow(summary, '已完整报告部分的 Token 合计', usage.reportedTotals.totalTokens); addDataRow(summary, '其中未缓存输入 / 输出', usage.reportedTotals.uncachedInputTokens + ' / ' + usage.reportedTotals.outputTokens); } else addEmpty(summary, '尚无可累计的完整报告。合计无法确认,不能按零计算。');
|
|
285
691
|
append(summary, 'p', '费用:尚未记录。推理属于输出,不重复累加。覆盖不足时,上述已报告部分不是整个任务或平台的总用量。', 'hint');
|
|
@@ -309,7 +715,7 @@ export const WORKBENCH_SCRIPT = String.raw `
|
|
|
309
715
|
claims.forEach(claim => { const card = document.createElement('article'); card.className = 'workbench-item'; append(card, 'strong', '第 ' + claim.attemptNo + ' 次执行者呈报 · 自述'); addParagraph(card, '自称结果', claim.claimedOutcome, '未提供'); addParagraph(card, '摘要', claim.summary, '未提供'); if (Array.isArray(claim.artifacts) && claim.artifacts.length) { append(card, 'strong', '产物引用'); claim.artifacts.forEach(artifact => append(card, 'p', artifact)); } else addEmpty(card, '未提供产物引用。'); if (Array.isArray(claim.risks) && claim.risks.length) addParagraph(card, '执行者报告的风险', claim.risks.join('\n'), '未提供'); delivery.append(card); });
|
|
310
716
|
append(delivery, 'p', '验证说明:呈报与产物引用来自执行者自述。当前契约没有独立验证结果字段;主管裁定单独列出,人类验收尚未记录。', 'hint');
|
|
311
717
|
const people = detailSection(parent, id, 'participants', '参与者'); const organization = record(record(record(snapshot.projection).organization).data); const roles = Array.isArray(organization.roles) ? organization.roles : []; const nameFor = bindingId => { const role = roles.find(item => roleBindingRef(item) === bindingId); return role ? friendly(role.roleName, bindingId) : friendly(bindingId, '尚未分配'); }; addParagraph(people, '领地', record(trusted && trusted.territory).name || ((snapshot.territories || []).find(item => item.territoryId === task.territoryId) || {}).name, '尚未确认'); addParagraph(people, '当前执行者', nameFor(task.assignedBindingId), '尚未分配'); const taskTerritory = (Array.isArray(organization.territories) ? organization.territories : []).find(item => typedEntityId(item.territoryRef, 'territory') === task.territoryId); const supervisorId = typedEntityId(record(taskTerritory).supervisorBindingRef, 'binding'); const supervisors = roles.filter(role => role.roleType === 'SUPERVISOR' && roleBindingRef(role) === supervisorId); addParagraph(people, '领地主管', supervisors.map(role => role.roleName).join('、'), '尚未确认'); addParagraph(people, '宰相', roles.filter(role => role.roleType === 'CHANCELLOR').map(role => role.roleName).join('、'), '尚未确认');
|
|
312
|
-
const usage = detailSection(parent, id, 'usage', '用量'); const dispatches = trusted && Array.isArray(record(trusted.governance).dispatches) ? trusted.governance.dispatches : []; if (!dispatches.length) addEmpty(usage, '尚无可用的 Worker Dispatch 用量记录;不能按零计算。'); dispatches.forEach(dispatch => addUsageObservation(usage, dispatch)); append(usage, 'p', '以上覆盖此任务的 Worker Dispatch,不能单独视为整个任务账单。', 'hint'); renderAdditionalRoleCost(usage, trusted && trusted.additionalRoleCost, true);
|
|
718
|
+
const usage = detailSection(parent, id, 'usage', '用量'); const dispatches = trusted && Array.isArray(record(trusted.governance).dispatches) ? trusted.governance.dispatches : []; if (!dispatches.length) addEmpty(usage, '尚无可用的 Worker Dispatch 用量记录;不能按零计算。'); dispatches.forEach(dispatch => addUsageObservation(usage, dispatch)); append(usage, 'p', '以上覆盖此任务的 Worker Dispatch,不能单独视为整个任务账单。', 'hint'); renderAdditionalRoleCost(usage, trusted && trusted.additionalRoleCost, true);
|
|
313
719
|
const historyPanel = detailSection(parent, id, 'history', '历史'); const historyItems = [];
|
|
314
720
|
if (trusted) { (trusted.assignments || []).forEach(item => historyItems.push({ time: item.assignedAt, label: '分派 · ' + nameFor(item.workerBindingId), detail: item.handoffReason || item.endReason || '已记录责任绑定' })); (trusted.executions || []).forEach(item => historyItems.push({ time: item.startedAt, label: '第 ' + item.attemptNo + ' 次执行 · ' + stateDisplay(item.state), detail: item.detail || '运行观察' })); reviews.forEach(item => historyItems.push({ time: item.createdAt, label: '主管裁定 · ' + stateDisplay(item.decision), detail: item.reason || '未附理由' })); claims.forEach(item => historyItems.push({ time: item.createdAt, label: '第 ' + item.attemptNo + ' 次呈报 · 执行者自述', detail: item.summary || '未附摘要' })); }
|
|
315
721
|
historyItems.sort((a, b) => String(a.time || '').localeCompare(String(b.time || ''))); if (!historyItems.length) addEmpty(historyPanel, trusted ? '尚无分派、执行或审查历史。' : '完整历史尚未读取成功,请重新读取。'); historyItems.forEach(item => { const row = document.createElement('article'); row.className = 'workbench-item'; append(row, 'strong', item.label); append(row, 'p', item.detail); append(row, 'small', friendly(item.time, '时间未提供')); historyPanel.append(row); });
|
|
@@ -324,6 +730,13 @@ export const WORKBENCH_SCRIPT = String.raw `
|
|
|
324
730
|
append(parent, 'h3', role.roleName); const kind = role.roleType; append(parent, 'p', ({ CHANCELLOR: '职责:统筹目标、规划任务,并交给所属领地。', SUPERVISOR: '职责:在所属领地分派任务、核对执行并审查呈报。', WORKER: '职责:在授权范围执行任务,提交产物与风险说明。' })[kind] || '职责以宿主组织绑定为准。');
|
|
325
731
|
addParagraph(parent, '会话', role.sessionBound ? '已绑定' : '尚未绑定', '尚未确认');
|
|
326
732
|
const rolesProjection = record(workbenchData(snapshot).roles); const projected = queueItems(rolesProjection).find(item => item.bindingId === state.selectedRoleId || typedEntityId(item.bindingRef, 'binding') === state.selectedRoleId);
|
|
733
|
+
// v3.2.0:主管必须隶属领地;未隶属的遗留席位在此显式标注为待处理,不推断归属。
|
|
734
|
+
if (kind === 'SUPERVISOR') {
|
|
735
|
+
const seatTerritories = projected && Array.isArray(projected.territories) ? projected.territories : [];
|
|
736
|
+
addParagraph(parent, '隶属领地', seatTerritories.length
|
|
737
|
+
? seatTerritories.map(item => text(item.name) + '(' + text(item.territoryId) + ')').join('、')
|
|
738
|
+
: '未隶属领地(待处理)', '尚未确认');
|
|
739
|
+
}
|
|
327
740
|
const tasks = projected && Array.isArray(projected.taskIds) ? taskItems().filter(task => projected.taskIds.includes(task.taskId)) : taskItems().filter(task => kind === 'WORKER' ? task.assignedBindingId === state.selectedRoleId : kind === 'SUPERVISOR' ? task.territoryId === typedEntityId(role.territoryRef, 'territory') : false);
|
|
328
741
|
append(parent, 'h4', '职责范围内的任务'); if (!tasks.length) addEmpty(parent, '当前没有已投影的关联任务。'); tasks.forEach(task => { const row = document.createElement('article'); row.className = 'workbench-item'; addTaskLink(row, task.taskId, task.title, 'role-task:' + task.taskId); append(row, 'p', executionExplanation(task)); parent.append(row); });
|
|
329
742
|
if (projected) { addDataRow(parent, '进行中执行', projected.activeExecutionCount); addDataRow(parent, '待主管复核', projected.reviewTaskCount); addDataRow(parent, '恢复核对中', projected.recoveringTaskCount); if (projected.taskIdsTruncated) addEmpty(parent, '关联任务已截断:显示 ' + projected.taskIds.length + ' / ' + projected.taskCount + ' 项。'); }
|
|
@@ -347,7 +760,7 @@ export const WORKBENCH_SCRIPT = String.raw `
|
|
|
347
760
|
const capability = document.querySelector('.capability-card'); if (capability) byId('settings-control-slot').append(capability);
|
|
348
761
|
const owner = document.querySelector('.owner-management'); if (owner) { owner.querySelectorAll('.owner-actions').forEach(node => node.remove()); byId('settings-owner-slot').append(owner); }
|
|
349
762
|
const overview = byId('overview'); const grid = overview && overview.querySelector('.council-grid'); if (grid) { grid.classList.add('role-workbench-layout'); const inspector = document.createElement('aside'); inspector.id = 'role-inspector'; inspector.className = 'role-inspector'; inspector.hidden = true; inspector.tabIndex = -1; inspector.setAttribute('aria-label', '角色职责与任务'); const close = append(inspector, 'button', '关闭角色侧栏', 'refresh'); close.type = 'button'; close.onclick = () => { const roleId = state.selectedRoleId; state.selectedRoleId = ''; inspector.hidden = true; const original = Array.from(document.querySelectorAll('[data-focus-key]')).find(node => node.getAttribute('data-focus-key') === 'role:' + roleId); if (original) original.focus(); }; const content = document.createElement('div'); content.id = 'role-inspector-content'; inspector.append(content); grid.append(inspector); }
|
|
350
|
-
const quick = byId('quick-task-form'); if (quick) quick.addEventListener('submit', event => { event.preventDefault(); const goal = String(byId('quick-task-goal').value || '').trim(); if (!goal) return; const title = byId('task-title'); if (!stageWorkbenchGoal(goal)) status('已有未提交草稿,请在任务页继续编辑。新目标保留在工作台输入框中。', 'neutral'); else status('草稿尚未提交,请补充范围和验收条件。', 'neutral'); history.pushState(null, '', '#tasks'); applyNavigationFromLocation(false); const draft = byId('task-draft-panel'); if (draft) { draft.open = true; readerDetails.set(draft.id, true); } if (title) { title.focus(); title.scrollIntoView({ block: 'center' }); } });
|
|
763
|
+
const quick = byId('quick-task-form'); if (quick) quick.addEventListener('submit', event => { event.preventDefault(); const goal = String(byId('quick-task-goal').value || '').trim(); if (!goal) return; const title = byId('task-title'); if (!stageWorkbenchGoal(goal)) status('已有未提交草稿,请在任务页继续编辑。新目标保留在工作台输入框中。', 'neutral'); else status('草稿尚未提交,请补充范围和验收条件。', 'neutral'); history.pushState(null, '', '#tasks'); applyNavigationFromLocation(false); const draft = byId('task-draft-panel'); if (draft) { draft.open = true; readerDetails.set(draft.id, true); } if (title) { title.focus(); title.scrollIntoView({ block: 'center' }); } });
|
|
351
764
|
const refresh = byId('workbench-refresh'); if (refresh) refresh.onclick = () => { void load(false); };
|
|
352
765
|
const territory = byId('task-territory'); if (territory) territory.addEventListener('change', () => { state.selectedTerritoryId = territory.value; const chip = byId('task-territory-chip'); if (chip) chip.hidden = true; });
|
|
353
766
|
document.addEventListener('click', event => { const target = event.target && typeof event.target.closest === 'function' ? event.target.closest('a[href]') : null; if (target && String(target.getAttribute('href') || '').startsWith('#task=') && state.activeSection !== 'tasks') state.taskReturnSection = state.activeSection === 'overview' ? 'map' : state.activeSection; });
|