dsh-kingdom 0.8.0 → 2.0.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.
Files changed (162) hide show
  1. package/ASSETS_NOTICE.md +22 -0
  2. package/LICENSE +661 -29
  3. package/NOTICE +9 -0
  4. package/README.md +93 -94
  5. package/THIRD_PARTY_NOTICES.md +92 -0
  6. package/cordis.patch.yml +6 -6
  7. package/docs/V2.0-RELEASE-NOTES.md +30 -0
  8. package/docs/V2.0-USER-GUIDE.md +74 -0
  9. package/lib/adapter/dsh-backend.js +327 -24
  10. package/lib/adapter/dsh-backend.js.map +1 -1
  11. package/lib/adapter/dsh-cost.js +68 -0
  12. package/lib/adapter/dsh-cost.js.map +1 -0
  13. package/lib/adapter/dsh-session-events.js +86 -0
  14. package/lib/adapter/dsh-session-events.js.map +1 -0
  15. package/lib/adapter/dsh-usage.js +159 -0
  16. package/lib/adapter/dsh-usage.js.map +1 -0
  17. package/lib/capability/admin.js +72 -0
  18. package/lib/capability/admin.js.map +1 -0
  19. package/lib/capability/dsh-enforcement.js +143 -34
  20. package/lib/capability/dsh-enforcement.js.map +1 -1
  21. package/lib/capability/resolver.js +21 -8
  22. package/lib/capability/resolver.js.map +1 -1
  23. package/lib/capability/service.js +169 -26
  24. package/lib/capability/service.js.map +1 -1
  25. package/lib/capability/tool-disclosure.js +144 -0
  26. package/lib/capability/tool-disclosure.js.map +1 -0
  27. package/lib/core/binding.js +52 -34
  28. package/lib/core/binding.js.map +1 -1
  29. package/lib/core/budget.js +304 -0
  30. package/lib/core/budget.js.map +1 -0
  31. package/lib/core/collaboration.js +422 -0
  32. package/lib/core/collaboration.js.map +1 -0
  33. package/lib/core/cost.js +212 -0
  34. package/lib/core/cost.js.map +1 -0
  35. package/lib/core/db.js +197 -4
  36. package/lib/core/db.js.map +1 -1
  37. package/lib/core/execution.js +2 -2
  38. package/lib/core/execution.js.map +1 -1
  39. package/lib/core/governed-claim.js +46 -0
  40. package/lib/core/governed-claim.js.map +1 -0
  41. package/lib/core/governed.js +891 -98
  42. package/lib/core/governed.js.map +1 -1
  43. package/lib/core/kingdom.js +78 -70
  44. package/lib/core/kingdom.js.map +1 -1
  45. package/lib/core/owner-binding-intent.js +732 -0
  46. package/lib/core/owner-binding-intent.js.map +1 -0
  47. package/lib/core/owner-control.js +86 -0
  48. package/lib/core/owner-control.js.map +1 -0
  49. package/lib/core/owner-window.js +822 -0
  50. package/lib/core/owner-window.js.map +1 -0
  51. package/lib/core/task-service.js +296 -133
  52. package/lib/core/task-service.js.map +1 -1
  53. package/lib/core/territory.js +44 -14
  54. package/lib/core/territory.js.map +1 -1
  55. package/lib/core/usage.js +39 -0
  56. package/lib/core/usage.js.map +1 -0
  57. package/lib/core/workspace-admission.js +225 -0
  58. package/lib/core/workspace-admission.js.map +1 -0
  59. package/lib/dispatch/evidence.js +5 -2
  60. package/lib/dispatch/evidence.js.map +1 -1
  61. package/lib/dispatch/reconcile.js +3 -9
  62. package/lib/dispatch/reconcile.js.map +1 -1
  63. package/lib/dispatch/service.js +464 -64
  64. package/lib/dispatch/service.js.map +1 -1
  65. package/lib/gui/assets/characters/chancellor-idle.svg +67 -0
  66. package/lib/gui/assets/characters/chancellor-sleeping.svg +67 -0
  67. package/lib/gui/assets/characters/chancellor-thinking.svg +67 -0
  68. package/lib/gui/assets/characters/chancellor-working.svg +67 -0
  69. package/lib/gui/assets/characters/knight-redraw-r1-idle.svg +67 -0
  70. package/lib/gui/assets/characters/knight-redraw-r1-sleeping.svg +67 -0
  71. package/lib/gui/assets/characters/knight-redraw-r1-thinking.svg +67 -0
  72. package/lib/gui/assets/characters/knight-redraw-r1-working.svg +67 -0
  73. package/lib/gui/assets/characters/supervisor-idle.svg +67 -0
  74. package/lib/gui/assets/characters/supervisor-sleeping.svg +67 -0
  75. package/lib/gui/assets/characters/supervisor-thinking.svg +67 -0
  76. package/lib/gui/assets/characters/supervisor-working.svg +67 -0
  77. package/lib/gui/console-app.js +2330 -0
  78. package/lib/gui/console-app.js.map +1 -0
  79. package/lib/gui/contract.js +5 -5
  80. package/lib/gui/contract.js.map +1 -1
  81. package/lib/gui/control-contract.js +232 -0
  82. package/lib/gui/control-contract.js.map +1 -0
  83. package/lib/gui/local-control.js +423 -0
  84. package/lib/gui/local-control.js.map +1 -0
  85. package/lib/gui/owner-control.js +310 -0
  86. package/lib/gui/owner-control.js.map +1 -0
  87. package/lib/gui/owner-ui.js +191 -0
  88. package/lib/gui/owner-ui.js.map +1 -0
  89. package/lib/gui/server.js +445 -35
  90. package/lib/gui/server.js.map +1 -1
  91. package/lib/gui/snapshot.js +1346 -67
  92. package/lib/gui/snapshot.js.map +1 -1
  93. package/lib/gui/visual-assets.js +56 -0
  94. package/lib/gui/visual-assets.js.map +1 -0
  95. package/lib/gui/visual-system.js +272 -0
  96. package/lib/gui/visual-system.js.map +1 -0
  97. package/lib/gui/workbench-ui.js +358 -0
  98. package/lib/gui/workbench-ui.js.map +1 -0
  99. package/lib/gui/workbench.js +163 -0
  100. package/lib/gui/workbench.js.map +1 -0
  101. package/lib/index.js +1384 -361
  102. package/lib/index.js.map +1 -1
  103. package/lib/runner-context-broker.js +1134 -0
  104. package/lib/runner-context-broker.js.map +1 -0
  105. package/lib/runtime-cost-observer.js +145 -0
  106. package/lib/runtime-cost-observer.js.map +1 -0
  107. package/lib/types/adapter/contract.d.ts +61 -1
  108. package/lib/types/adapter/dsh-backend.d.ts +32 -13
  109. package/lib/types/adapter/dsh-cost.d.ts +13 -0
  110. package/lib/types/adapter/dsh-session-events.d.ts +16 -0
  111. package/lib/types/adapter/dsh-usage.d.ts +36 -0
  112. package/lib/types/capability/admin.d.ts +13 -0
  113. package/lib/types/capability/dsh-enforcement.d.ts +11 -7
  114. package/lib/types/capability/service.d.ts +10 -3
  115. package/lib/types/capability/tool-disclosure.d.ts +36 -0
  116. package/lib/types/core/binding.d.ts +19 -10
  117. package/lib/types/core/budget.d.ts +81 -0
  118. package/lib/types/core/collaboration.d.ts +110 -0
  119. package/lib/types/core/cost.d.ts +99 -0
  120. package/lib/types/core/db.d.ts +44 -4
  121. package/lib/types/core/execution.d.ts +1 -1
  122. package/lib/types/core/governed-claim.d.ts +7 -0
  123. package/lib/types/core/governed.d.ts +182 -1
  124. package/lib/types/core/kingdom.d.ts +3 -0
  125. package/lib/types/core/owner-binding-intent.d.ts +176 -0
  126. package/lib/types/core/owner-control.d.ts +53 -0
  127. package/lib/types/core/owner-window.d.ts +226 -0
  128. package/lib/types/core/task-service.d.ts +60 -13
  129. package/lib/types/core/territory.d.ts +9 -2
  130. package/lib/types/core/usage.d.ts +26 -0
  131. package/lib/types/core/workspace-admission.d.ts +43 -0
  132. package/lib/types/dispatch/evidence.d.ts +2 -5
  133. package/lib/types/dispatch/service.d.ts +62 -4
  134. package/lib/types/gui/console-app.d.ts +137 -0
  135. package/lib/types/gui/contract.d.ts +396 -16
  136. package/lib/types/gui/control-contract.d.ts +111 -0
  137. package/lib/types/gui/local-control.d.ts +120 -0
  138. package/lib/types/gui/owner-control.d.ts +45 -0
  139. package/lib/types/gui/owner-ui.d.ts +2 -0
  140. package/lib/types/gui/server.d.ts +17 -3
  141. package/lib/types/gui/snapshot.d.ts +33 -11
  142. package/lib/types/gui/visual-assets.d.ts +27 -0
  143. package/lib/types/gui/visual-system.d.ts +2 -0
  144. package/lib/types/gui/workbench-ui.d.ts +5 -0
  145. package/lib/types/gui/workbench.d.ts +33 -0
  146. package/lib/types/index.d.ts +157 -6
  147. package/lib/types/runner-context-broker.d.ts +119 -0
  148. package/lib/types/runtime-cost-observer.d.ts +22 -0
  149. package/lib/types/worker/collaboration.d.ts +35 -0
  150. package/lib/types/worker/dsh-subagent.d.ts +4 -4
  151. package/lib/types/worker/executor-factory.d.ts +2 -1
  152. package/lib/types/worker/executor.d.ts +8 -3
  153. package/lib/types/worker/governed-executor.d.ts +8 -1
  154. package/lib/worker/collaboration.js +172 -0
  155. package/lib/worker/collaboration.js.map +1 -0
  156. package/lib/worker/dsh-subagent.js +4 -4
  157. package/lib/worker/executor-factory.js.map +1 -1
  158. package/lib/worker/executor.js +7 -2
  159. package/lib/worker/executor.js.map +1 -1
  160. package/lib/worker/governed-executor.js +295 -84
  161. package/lib/worker/governed-executor.js.map +1 -1
  162. package/package.json +23 -8
@@ -0,0 +1,358 @@
1
+ /** Personal workbench presentation. No model calls or authority are created here. */
2
+ export const WORKBENCH_CSS = String.raw `
3
+ .workbench-page { grid-column: 1 / -1; min-width: 0; width: 100%; }
4
+ .workbench-heading { display: flex; justify-content: space-between; align-items: start; gap: 24px; margin: 18px 0 24px; }
5
+ .workbench-heading h2 { font-size: clamp(25px, 3vw, 36px); margin: 6px 0; }
6
+ .workbench-heading p, .workbench-block > p { color: var(--muted); }
7
+ .workbench-grid { display: grid; grid-template-columns: minmax(0, 1.3fr) minmax(280px, 1fr); gap: 18px; }
8
+ .workbench-block { min-width: 0; padding: 20px; border: 1px solid var(--line); background: var(--patina); border-radius: 12px; }
9
+ .workbench-block h3 { font-size: 18px; margin-bottom: 8px; }
10
+ .workbench-block[data-priority="owner"] { border-top: 3px solid var(--gold); }
11
+ .workbench-list { display: grid; gap: 10px; margin-top: 16px; }
12
+ .workbench-item { display: grid; gap: 7px; padding: 14px 0; border-bottom: 1px solid var(--line); min-width: 0; }
13
+ .workbench-item:last-child { border-bottom: 0; }
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; }
20
+ .quick-task-form { display: flex; gap: 12px; margin: 18px 0 26px; }
21
+ .quick-task-form input { flex: 1; min-width: 0; min-height: 48px; }
22
+ .quick-task-form button { flex: 0 0 auto; }
23
+ .workbench-section-link { color: var(--gold); font-size: 13px; }
24
+ .task-detail-section { padding: 20px 0; border-bottom: 1px solid var(--line); }
25
+ .task-detail-section h3 { margin-bottom: 12px; font-size: 18px; }
26
+ .task-detail-section p { white-space: pre-wrap; overflow-wrap: anywhere; }
27
+ .task-detail-section p + p { margin-top: 9px; }
28
+ .task-evidence-states { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; margin: 14px 0; }
29
+ .task-evidence-states > div { padding: 13px; background: var(--panel-soft); border-radius: 8px; }
30
+ .task-evidence-states strong, .task-evidence-states span { display: block; }
31
+ .task-evidence-states span { margin-top: 6px; color: var(--muted); }
32
+ .task-detail-jump { display: flex; gap: 12px; flex-wrap: wrap; margin: 15px 0; }
33
+ .task-detail-jump a { color: var(--gold); }
34
+ .task-draft-panel { margin-bottom: 22px; border: 1px solid var(--line); border-radius: 12px; padding: 16px; background: var(--patina); }
35
+ .task-draft-panel > summary { cursor: pointer; font-weight: 600; }
36
+ .task-draft-panel .task-composer-card { margin-top: 16px; box-shadow: none; border: 0; padding: 0; }
37
+ .draft-fields { margin-top: 16px; display: grid; gap: 14px; }
38
+ .draft-fields textarea { min-height: 100px; }
39
+ .task-actions-inline { margin-top: 22px; }
40
+ .task-actions-inline .action-dock-head { display: block; }
41
+ .task-actions-inline .action-dock-head p { margin-top: 8px; }
42
+ .task-actions-inline .forms > .governance-flow { display: none; }
43
+ .map-page .council-grid.role-workbench-layout { display: grid; grid-template-columns: minmax(0, 1fr); gap: 20px; }
44
+ .role-workbench-layout:has(.role-inspector:not([hidden])) { grid-template-columns: minmax(0, 1fr) 310px; }
45
+ .role-inspector { padding: 20px; align-self: start; position: sticky; top: calc(var(--nav-height) + 20px); border: 1px solid var(--line); border-radius: 12px; background: var(--patina); }
46
+ .role-inspector h3 { font-size: 21px; margin: 16px 0 10px; }
47
+ .role-inspector p { margin-bottom: 10px; overflow-wrap: anywhere; }
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); }
58
+ .settings-list { display: grid; gap: 20px; }
59
+ .settings-entry { border-top: 1px solid var(--line); padding-top: 16px; }
60
+ .settings-entry code { display: block; margin-top: 8px; user-select: all; overflow-wrap: anywhere; }
61
+ .usage-coverage { padding: 15px; border-left: 3px solid var(--gold); background: var(--panel-soft); margin: 14px 0; }
62
+ .usage-table { width: 100%; border-collapse: collapse; text-align: left; }
63
+ .usage-table th, .usage-table td { padding: 12px 8px; border-bottom: 1px solid var(--line); overflow-wrap: anywhere; }
64
+ .usage-table th { color: var(--muted); font-weight: 500; }
65
+ .workbench-page [hidden] { display: none !important; }
66
+ #task-detail-content, #task-action-slot { min-width: 0; }
67
+ .task-return { display: inline-block; margin-bottom: 12px; color: var(--gold); }
68
+ @media (min-width: 761px) and (max-width: 1100px) {
69
+ .realm-sidebar { flex-wrap: wrap; }
70
+ .main-nav { order: 3; width: 100%; margin: 0; justify-content: center; }
71
+ .main-nav a { flex: 1 1 0; }
72
+ }
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; }
80
+ .main-nav a { min-width: 0; width: 100%; white-space: normal; text-align: center; }
81
+ }
82
+ @media (max-width: 1050px) { .role-workbench-layout:has(.role-inspector:not([hidden])) { grid-template-columns: minmax(0, 1fr); } .role-inspector { position: static; } }
83
+ @media (max-width: 700px) {
84
+ .workbench-grid, .task-evidence-states { grid-template-columns: minmax(0, 1fr); }
85
+ .workbench-heading { flex-direction: column; gap: 12px; }
86
+ .workbench-block { padding: 16px; }
87
+ .quick-task-form { flex-direction: column; }
88
+ .usage-table { font-size: 12px; }
89
+ .usage-table th, .usage-table td { padding: 9px 4px; }
90
+ .main-nav a { flex: 0 0 auto; }
91
+ }
92
+ @media (max-width: 480px) { .main-nav .ui-icon { display: none; } }
93
+ `;
94
+ export const WORKBENCH_HTML = String.raw `
95
+ <section id="today" data-console-section="today" data-console-page="today" class="workbench-page" aria-labelledby="today-title">
96
+ <header class="workbench-heading"><div><p class="section-kicker">个人工作台</p><h2 id="today-title">今天,从要紧的事开始</h2><p>需要你决定的事、执行进展和最近交付,都在这里。</p></div><button id="workbench-refresh" class="refresh" type="button">重新读取</button></header>
97
+ <form id="quick-task-form" class="quick-task-form"><label class="sr-only" for="quick-task-goal">写下一句话目标</label><input id="quick-task-goal" autocomplete="off" placeholder="写下一句话目标,先整理为草稿" required><button class="primary" type="submit">整理任务草稿</button></form>
98
+ <div class="workbench-grid">
99
+ <section class="workbench-block" data-priority="owner"><h3>待我处理</h3><p>仅列出已明确需要人类决定的事项。</p><div id="today-owner" class="workbench-list"></div><a class="workbench-section-link" href="#inbox">查看待我处理 →</a></section>
100
+ <section class="workbench-block"><h3>执行异常</h3><p>说明发生了什么、由谁继续处理。</p><div id="today-exceptions" class="workbench-list"></div></section>
101
+ <section class="workbench-block"><h3>正在推进</h3><div id="today-active" class="workbench-list"></div></section>
102
+ <section class="workbench-block"><h3>最近交付</h3><p>呈报、主管接受与人类验收分别展示。</p><div id="today-deliveries" class="workbench-list"></div></section>
103
+ </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>
106
+ </section>
107
+ <section id="inbox" data-console-section="inbox" data-console-page="inbox" class="workbench-page" hidden aria-labelledby="inbox-title">
108
+ <header class="workbench-heading"><div><p class="section-kicker">你的决定</p><h2 id="inbox-title">待我处理</h2><p>当前版本的人类治理入口仍为直接 Slash;这里帮助你找到准确事项。</p></div></header>
109
+ <section class="workbench-block" data-priority="owner"><div id="inbox-owner" class="workbench-list"></div></section>
110
+ <section class="workbench-block" style="margin-top:18px"><h3>由团队内部处理</h3><div id="inbox-internal" class="workbench-list"></div></section>
111
+ </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>
116
+ </section>
117
+ <section id="settings" data-console-section="settings" data-console-page="settings" class="workbench-page" hidden aria-labelledby="settings-title">
118
+ <header class="workbench-heading"><div><p class="section-kicker">运行与治理</p><h2 id="settings-title">设置</h2><p>现有入口及真实操作权限;未开放的能力不会显示为可执行按钮。</p></div></header>
119
+ <div class="settings-list"><section class="workbench-block"><h3>当前控制通道</h3><p>角色操作来自激活本页的合法会话,每次提交仍由宿主重新核验。</p><div id="settings-control-slot"></div><p class="hint">在已绑定角色的 DSH 会话直接输入 /kingdom gui,按宿主提供的链接重新打开本页。</p></section><section class="workbench-block"><h3>人类所有者设置</h3><p>通过短期人类管理窗口配置王国、领地、任命、权限上限与模型。先由人类在 DSH 直接激活具体管理范围,再在网页预览并提交。</p><a class="workbench-section-link" href="/owner">打开人类管理窗口 →</a><div id="settings-owner-slot"></div></section><section class="workbench-block"><h3>外观与动态效果</h3><p>顶部可选择羊皮纸、夜蓝、森林与酒红主题。人物动作沿用当前王国风格;减少动态效果跟随系统偏好。</p></section><details class="workbench-block" id="settings-advanced"><summary>高级治理记录</summary><nav class="task-detail-jump"><a href="#ledger">王国账本</a><a href="#organization">领地名册</a><a href="#executions">执行记录</a><a href="#activity">治理史册</a></nav></details></div>
120
+ </section>
121
+ `;
122
+ /** Injected into the existing console closure; all Host text uses append/textContent. */
123
+ export const WORKBENCH_SCRIPT = String.raw `
124
+ const readerDetails = new Map();
125
+ const preserveWorkbenchView = render => {
126
+ const active = document.activeElement; const focusId = active && active.id;
127
+ const focusKey = active && typeof active.getAttribute === 'function' ? active.getAttribute('data-focus-key') : null;
128
+ const selection = active && typeof active.selectionStart === 'number' ? [active.selectionStart, active.selectionEnd] : null;
129
+ const x = Number(globalThis.scrollX || 0); const y = Number(globalThis.scrollY || 0);
130
+ const details = Array.from(document.querySelectorAll('details[data-read-key], details[id]'));
131
+ details.forEach(node => readerDetails.set(node.getAttribute('data-read-key') || node.id, node.open));
132
+ const anchors = Array.from(document.querySelectorAll('[data-read-anchor]'));
133
+ const anchor = anchors.find(node => typeof node.getBoundingClientRect === 'function' && node.getBoundingClientRect().height > 0 && node.getBoundingClientRect().bottom > 140);
134
+ const anchorKey = anchor && anchor.getAttribute('data-read-anchor'); const anchorTop = anchor ? anchor.getBoundingClientRect().top : null;
135
+ render();
136
+ document.querySelectorAll('details[data-read-key], details[id]').forEach(node => { const key = node.getAttribute('data-read-key') || node.id; if (readerDetails.has(key)) node.open = readerDetails.get(key); });
137
+ const replacement = focusId ? byId(focusId) : focusKey ? Array.from(document.querySelectorAll('[data-focus-key]')).find(node => node.getAttribute('data-focus-key') === focusKey) : active;
138
+ if (replacement && replacement.isConnected !== false && typeof replacement.focus === 'function' && !replacement.disabled) {
139
+ if (document.activeElement !== replacement) replacement.focus({ preventScroll: true });
140
+ if (selection && typeof replacement.setSelectionRange === 'function') { try { replacement.setSelectionRange(selection[0], selection[1]); } catch (_) {} }
141
+ }
142
+ const newAnchor = anchorKey ? Array.from(document.querySelectorAll('[data-read-anchor]')).find(node => node.getAttribute('data-read-anchor') === anchorKey) : null;
143
+ const delta = newAnchor && typeof newAnchor.getBoundingClientRect === 'function' ? newAnchor.getBoundingClientRect().top - anchorTop : 0;
144
+ if (typeof globalThis.scrollTo === 'function') globalThis.scrollTo({ left: x, top: Math.max(0, y + delta), behavior: 'instant' });
145
+ };
146
+ const workbenchData = snapshot => record(record(record(record(snapshot).projection).workbench).data);
147
+ const queueItems = queue => Array.isArray(record(queue).items) ? queue.items : [];
148
+ const friendly = (value, fallback) => value === null || value === undefined || value === '' ? fallback : String(value);
149
+ const addTaskLink = (parent, taskId, label, key) => {
150
+ if (!taskId) return append(parent, 'strong', label);
151
+ const link = append(parent, 'a', label); link.href = '#task=' + encodeURIComponent(String(taskId)); link.setAttribute('data-focus-key', key || 'task-link:' + taskId); return link;
152
+ };
153
+ const renderWorkbenchQueue = (id, queue, emptyMessage) => {
154
+ const parent = clear(id); if (!parent) return; const items = queueItems(queue);
155
+ if (!queue || !Array.isArray(queue.items)) { addEmpty(parent, '当前宿主尚未提供此项投影,请重新读取或检查版本。'); return; }
156
+ if (!items.length) addEmpty(parent, emptyMessage);
157
+ items.forEach(item => { const card = document.createElement('article'); card.className = 'workbench-item'; card.setAttribute('data-read-anchor', id + ':' + item.id); addTaskLink(card, item.taskId, friendly(item.title, '治理事项'), id + ':' + item.id); append(card, 'p', item.summary); append(card, 'small', '负责处理:' + ({ OWNER: '人类所有者', CHANCELLOR: '宰相', SUPERVISOR: '领地主管', UNDETERMINED: '尚未确认' })[item.responsibility]); if (item.responsibility === 'OWNER') { const link = append(card, 'a', '查看当前合法入口'); link.href = '#settings'; } parent.append(card); });
158
+ if (queue.truncated) addEmpty(parent, '当前显示 ' + items.length + ' / ' + queue.totalCount + ' 项;投影已注明截断。');
159
+ };
160
+ const executionExplanation = task => {
161
+ const execution = record(task.latestExecution); const value = String(execution.state || 'NONE');
162
+ if (value === 'RECOVERING') return '任务仍处于 ' + stateDisplay(task.status) + ';本次执行正在恢复核对。主管需核对原执行结果,不会自动重新派发。';
163
+ if (String(task.status) === 'RUNNING' && !['STARTING', 'RUNNING', 'PAUSED'].includes(value)) return '任务处于处理中;当前没有正在运行的执行。返工决定不会自动启动下一次执行。';
164
+ return '任务治理:' + stateDisplay(task.status) + ' · 本次执行:' + stateDisplay(value);
165
+ };
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 renderWorkbench = snapshot => {
174
+ const data = workbenchData(snapshot);
175
+ renderWorkbenchQueue('today-owner', data.ownerActions, '当前没有明确需要你处理的事项。');
176
+ renderWorkbenchQueue('inbox-owner', data.ownerActions, '当前没有明确需要你处理的事项。主管审查与返工会在下方单独列出。');
177
+ renderWorkbenchQueue('today-exceptions', data.exceptions, '当前没有已投影的执行异常。');
178
+ renderWorkbenchQueue('today-internal', data.internalActions, '当前没有等待团队内部处理的事项。');
179
+ renderWorkbenchQueue('inbox-internal', data.internalActions, '当前没有等待团队内部处理的事项。');
180
+ 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(item => { const row = document.createElement('article'); row.className = 'workbench-item'; addTaskLink(row, item.taskId, item.title, 'delivery:' + item.taskId); append(row, 'p', '执行者自述:' + claimSummaryPreview(record(item.claim).summary)); append(row, 'small', (item.supervisorAccepted ? '主管已接受' : '主管接受尚未确认') + ' · 人类验收:尚未记录'); deliveries.append(row); }); if (queue && queue.truncated) addEmpty(deliveries, '最近交付显示 ' + items.length + ' / ' + queue.totalCount + ' 项。'); }
182
+ renderCollaboration(data.collaboration); renderWorkbenchUsage(data.usage, data.cost); renderRoleInspector(snapshot);
183
+ };
184
+ const renderCollaboration = value => {
185
+ const parent = clear('collaboration-plans'); const resources = clear('collaboration-resources'); if (!parent || !resources) return;
186
+ const data = record(value); const plans = queueItems(data.plans); const held = queueItems(data.resources);
187
+ const reasons = { PLAN_NOT_ADOPTED:'等待人类采纳整份计划', PLAN_MEMBER_CHANGED:'成员或任务范围已变化,需先核对计划', PLAN_MEMBER_FAILED_OR_UNKNOWN:'成员失败或执行尚未核对,停止新增工作', TASK_NOT_STARTABLE:'任务当前不在可启动状态', DEPENDENCY_NOT_ACCEPTED_OR_STALE:'前置结果尚未接受或已过期', PLAN_NOT_CURRENT:'计划已被替换,需读取当前版本' };
188
+ const explain = (target, readiness, status) => {
189
+ if (status === 'DONE') { append(target, 'p', '主管已接受本任务;不等于人类验收。'); return; }
190
+ if (status === 'REVIEW') { append(target, 'p', '执行者已呈报,等待主管正常审查。'); return; }
191
+ if (!readiness) { append(target, 'p', '就绪情况尚不可用;不能视为已获准启动。'); return; }
192
+ append(target, 'p', readiness.ready ? '依赖条件已满足;启动时仍须核验身份、预算、能力及资源。' : (reasons[readiness.reasonCode] || '当前阻塞原因:' + friendly(readiness.reasonCode, '尚未确认')));
193
+ (readiness.blockingTaskIds || []).forEach(id => addTaskLink(target, id, '核对前置或受影响任务 ' + id, 'plan-block:' + id));
194
+ (readiness.acceptedResults || []).forEach(result => append(target, 'small', '已接受引用:' + result.taskId + ' · 第 ' + result.attemptNo + ' 次 · 结果 ' + result.resultId + ' · 接受记录 ' + result.acceptEventId));
195
+ };
196
+ if (!value) addEmpty(parent, '当前宿主尚未提供协作投影;不会假定已有计划或自动创建团队。');
197
+ else if (!plans.length) addEmpty(parent, '暂无协作计划,沿用单执行者流程。');
198
+ plans.forEach(plan => {
199
+ 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);
200
+ append(panel, 'summary', plan.title + ' · ' + ({PROPOSED:'待采纳',ADOPTED:'已采纳',STALE:'已过期,需重新核对'}[plan.state] || '状态待核对') + ' · 第 ' + plan.version + ' 版');
201
+ addTaskLink(panel, plan.parentTaskId, '查看父任务与整合结果', 'plan-parent:' + plan.planId);
202
+ addDataRow(panel, '协作方式', plan.mode === 'EXPERT' ? '主执行者与只读专家' : '独立工作项小团队');
203
+ append(panel, 'p', '拆分理由:' + plan.reason); addDataRow(panel, '主整合者', friendly(plan.integratorName, plan.integratorBindingId));
204
+ addDataRow(panel, '计划软额度 / 单次估计预留', plan.budgetTokens + ' / ' + plan.reserveTokens + ' Tokens');
205
+ const budget = plan.budget;
206
+ if (budget) {
207
+ append(panel, 'p', ({ALLOW:'预算当前允许新增接纳',WARN:'预算有风险或接近额度',BLOCK_LIMIT:'整项额度不足,停止新增工作',BLOCK_UNKNOWN:'存在未确认成本,停止新增工作'}[budget.state] || '预算状态待核对'));
208
+ addDataRow(panel, '成员执行实报 Token', budget.workerVerifiedTokens); addDataRow(panel, '同期王国协调成本上界 Token', budget.coordinationUpperBoundTokens);
209
+ addDataRow(panel, '估计预留 / 风险敞口', budget.reservedEstimateTokens + ' / ' + budget.exposureTokens);
210
+ addDataRow(panel, '剩余估计额度', budget.remainingTokens); addDataRow(panel, '在途 / 未确认 / 归属缺口', budget.pendingUnits + ' / ' + budget.unknownUnits + ' / ' + budget.attributionGapCount);
211
+ append(panel, 'small', '协调上界含同期王国观测,不能当作精确团队费用或再次分摊。金额未知;王国预算也须同时允许。');
212
+ } else append(panel, 'p', '尚无已采纳计划的用量视图;上方额度是计划值,不是零费用证明。');
213
+ plan.items.forEach(item => {
214
+ const row = document.createElement('article'); row.className = 'workbench-item'; addTaskLink(row, item.status === 'NOT_CREATED' ? null : item.taskId, item.title, 'plan-item:' + item.taskId);
215
+ append(row, 'p', '负责人:' + friendly(item.workerName, item.workerBindingId) + ' · 领地:' + friendly(item.territoryName, item.territoryId) + ' · ' + (item.access === 'READ_ONLY' ? '只读专家' : '允许范围内写入'));
216
+ append(row, 'p', '范围:' + item.description); append(row, 'p', '验收:' + item.acceptanceCriteria); append(row, 'p', '预期产物:' + item.expectedArtifact);
217
+ append(row, 'p', '前置工作:' + (item.dependsOn.length ? item.dependsOn.map(key => friendly(plan.items.find(other => other.key === key)?.title, key)).join('、') : '无'));
218
+ explain(row, item.readiness, item.status);
219
+ const occupancy = held.filter(resource => resource.taskId === item.taskId);
220
+ if (occupancy.length) occupancy.forEach(resource => append(row, 'small', '资源:' + (resource.recovery ? '恢复中,继续保留占用' : resource.state === 'BOUND' ? '已绑定本次执行' : '已预留,尚待绑定') + ' · 第 ' + resource.attemptNo + ' 次'));
221
+ else append(row, 'small', '尚无本项资源接纳记录;不表示工作区空闲,也没有自动排队派发。');
222
+ panel.append(row);
223
+ });
224
+ append(panel, 'h4', '父任务整合'); explain(panel, plan.integration, plan.parentStatus);
225
+ append(panel, 'p', '子项完成后仍由主执行者实际整合、另交呈报并经主管审查;不会自动完成父任务。', 'hint');
226
+ if (plan.state === 'PROPOSED') { const link = append(panel, 'a', '在独立管理窗口审阅并采纳'); link.href = '/owner'; link.setAttribute('data-focus-key', 'plan-adopt:' + plan.planId); }
227
+ 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);
228
+ });
229
+ if (record(data.plans).truncated) addEmpty(parent, '仅显示 ' + plans.length + ' / ' + data.plans.totalCount + ' 份计划,请按任务继续核对。');
230
+ if (!held.length) addEmpty(resources, '当前没有已投影的工作区预留;其他原有执行仍可能占用,实际启动前必须重新检查。');
231
+ 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); });
232
+ if (record(data.resources).truncated) addEmpty(resources, '资源占用列表已截断,不能据此判断工作区空闲。');
233
+ };
234
+ const renderAdditionalRoleCost = (parent, value, taskOnly) => {
235
+ if (!value) { addEmpty(parent, '宰相与主管的观测尚不可用;不能按零计算。'); return; }
236
+ append(parent, 'h3', taskOnly ? '准确归属于本任务的宰相与主管用量' : '宰相与主管的已观测用量');
237
+ addDataRow(parent, '可核对的供应商实报 Token', friendly(value.verifiedTokens, '尚无完整实报,合计未知'));
238
+ addDataRow(parent, '完整 / 已观测轮次', value.completeUnits + ' / ' + value.units);
239
+ addDataRow(parent, '在途轮次 / 用量缺失轮次', value.pendingUnits + ' / ' + value.unknownUnits);
240
+ addDataRow(parent, '有实报 / 已观测请求', value.reportedRequests + ' / ' + value.observedRequests);
241
+ (Array.isArray(value.byRole) ? value.byRole : []).forEach(role => addDataRow(parent, (role.roleType === 'CHANCELLOR' ? '宰相' : role.roleType === 'SUPERVISOR' ? '主管' : friendly(role.roleType, '其他角色')) + '已完整实报 Token', friendly(role.verifiedTokens, '未知')));
242
+ if (!taskOnly) { addDataRow(parent, '其中共享轮次 Token', friendly(value.sharedTokens, '尚无可累计的完整实报')); addDataRow(parent, '其中未归属 Token', friendly(value.unattributedTokens, '尚无可累计的完整实报')); addDataRow(parent, '共享或未归属轮次', value.attributionGapCount); append(parent, 'p', '共享和未归属是上述实报的组成部分,不再次累加,也不摊入某个任务。', 'hint'); }
243
+ append(parent, 'p', taskOnly ? '本区只计准确关联本任务的轮次;共享、未归属、未接入的外部参与者及历史缺口均不摊入本任务。费用未知。' : friendly(value.coverageNote, '仅覆盖已接入的观测,外部参与者与历史缺口仍未知。') + ' 费用未知,未配置可信价格表。', 'hint');
244
+ };
245
+ const renderBudgetCost = cost => {
246
+ const parent = clear('budget-summary'); const roles = clear('role-cost-summary'); const runtime = clear('cost-runtime-summary'); const prompts = clear('prompt-cost-summary');
247
+ if (!parent || !roles || !runtime || !prompts) return;
248
+ append(parent, 'h3', '王国软预算'); const budget = record(cost).budget;
249
+ if (!budget) addEmpty(parent, '当前没有可读取的预算视图;这不表示余额充足或成本为零。');
250
+ else {
251
+ const labels = { OFF: '未启用预算拦截', ALLOW: '可接纳新的执行', WARN: '已接近额度或存在需留意的缺口', BLOCK_LIMIT: '额度不足,阻止新增执行', BLOCK_UNKNOWN: '存在未确认用量,阻止新增执行' };
252
+ append(parent, 'p', labels[budget.state] || '预算状态未知', 'usage-coverage');
253
+ addDataRow(parent, '预算周期内可核对实报 Token', budget.verifiedTokens);
254
+ addDataRow(parent, '其中执行者 / 其他已观测角色', budget.workerVerifiedTokens + ' / ' + budget.additionalVerifiedTokens);
255
+ addDataRow(parent, '已预留估计 Token', budget.reservedEstimateTokens);
256
+ addDataRow(parent, '实报与预留合计(估计风险敞口)', budget.exposureTokens);
257
+ addDataRow(parent, '剩余可接纳额度(估计)', friendly(budget.remainingTokens, '尚未设置'));
258
+ addDataRow(parent, '在途 / 未确认 / 恢复中', budget.pendingUnits + ' / ' + budget.unknownUnits + ' / ' + budget.recoveryUnits);
259
+ addDataRow(parent, '共享或未归属项', budget.attributionGapCount);
260
+ const policy = budget.policy;
261
+ if (policy) { addDataRow(parent, '政策额度 / 每次启动预留', policy.limitTokens + ' / ' + policy.reserveTokens + ' Tokens'); addDataRow(parent, '未知用量处理', policy.unknownPolicy === 'BLOCK' ? '阻止新增执行' : '提示后允许继续(风险由政策明确承担)'); addDataRow(parent, '提醒阈值', policy.warningPercent + '%'); addDataRow(parent, '统计起点 / 政策版本', '事件 ' + policy.sinceEventSeq + ' / ' + policy.revision); }
262
+ append(parent, 'p', friendly(budget.note, '预算仅覆盖可观测用量。') + ' 预留是启动前估计,不是供应商账单。收紧政策只影响未来新增接纳,不会自动结束在途执行或释放恢复中的占用;关闭政策不重置统计起点。金额未知。', 'hint');
263
+ }
264
+ const manage = append(parent, 'a', '在管理窗口中调整预算政策'); manage.href = '/owner';
265
+ renderAdditionalRoleCost(roles, record(cost).additionalRoles, false);
266
+ append(runtime, 'h3', '工具展示试点'); const observed = record(record(cost).runtime);
267
+ addDataRow(runtime, '当前配置', observed.toolDisclosureMode === 'off' ? '关闭(默认)' : observed.toolDisclosureMode === 'pilot' ? '有限试点已开启' : '未知,尚未读取运行配置');
268
+ addDataRow(runtime, '角色用量观测接入', observed.observerAvailable === true ? '当前监听可用' : observed.observerAvailable === false ? '当前监听不可用,已有记录仍可查看' : '接入状态未知');
269
+ append(runtime, 'p', '工具展示仅缩小本来已获准的工具集合,不增加权限。尚未获得真实冷暖缓存配对收益证据,不宣称节省比例。', 'hint');
270
+ const list = record(record(cost).prompts); const items = Array.isArray(list.items) ? list.items : [];
271
+ const details = document.createElement('details'); details.setAttribute('data-read-key', 'usage:assembly-byte-observations'); append(details, 'summary', '查看提示装配的字节观察'); prompts.append(details);
272
+ append(details, 'p', '这里只统计装配阶段的片段字节,尚未代表最终请求。历史消息字节与 Token 估算未测;不能把字节下降当作任务总成本下降。', 'hint');
273
+ if (!items.length) addEmpty(details, '尚无可用的提示装配观察。');
274
+ 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); });
275
+ if (list.truncated) append(details, 'p', '仅显示有界的最近角色观察。', 'hint');
276
+ };
277
+ const renderWorkbenchUsage = (usage, cost) => {
278
+ renderBudgetCost(cost);
279
+ const summary = clear('usage-summary'); const content = clear('usage-content'); if (!summary || !content) return;
280
+ if (!usage) { addEmpty(summary, '当前宿主尚未提供用量投影,请重新读取或检查版本。'); return; }
281
+ 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');
283
+ addDataRow(summary, '完整实报 / 全部 Dispatch', usage.completeDispatches + ' / ' + usage.totalDispatches); addDataRow(summary, '部分实报', usage.partialDispatches); addDataRow(summary, '实报不可用', usage.unavailableDispatches); addDataRow(summary, '没有 Dispatch 用量关联的执行', usage.executionsWithoutDispatch);
284
+ if (usage.reportedTotals) { addDataRow(summary, '已完整报告部分的 Token 合计', usage.reportedTotals.totalTokens); addDataRow(summary, '其中未缓存输入 / 输出', usage.reportedTotals.uncachedInputTokens + ' / ' + usage.reportedTotals.outputTokens); } else addEmpty(summary, '尚无可累计的完整报告。合计无法确认,不能按零计算。');
285
+ append(summary, 'p', '费用:尚未记录。推理属于输出,不重复累加。覆盖不足时,上述已报告部分不是整个任务或平台的总用量。', 'hint');
286
+ const dispatches = Array.isArray(record(record(state.snapshot).governance).dispatches) ? state.snapshot.governance.dispatches : [];
287
+ if (!dispatches.length) { addEmpty(content, '当前快照没有逐次 Dispatch 记录;可在任务详情查看已有证据。'); return; }
288
+ append(content, 'h3', '当前投影中的逐次用量'); dispatches.forEach(dispatch => { const row = document.createElement('article'); row.className = 'workbench-item'; const task = taskItems().find(item => item.taskId === dispatch.taskId); addTaskLink(row, dispatch.taskId, task ? task.title : dispatch.taskId, 'usage:' + dispatch.dispatchId); addUsageObservation(row, dispatch); content.append(row); });
289
+ };
290
+ const detailSection = (parent, taskId, key, label) => { const section = document.createElement('section'); section.className = 'task-detail-section'; section.id = 'task-section-' + key; section.setAttribute('data-read-anchor', taskId + ':' + key); append(section, 'h3', label); parent.append(section); return section; };
291
+ const addParagraph = (parent, label, value, fallback) => { append(parent, 'p', label + ':' + friendly(value, fallback)); };
292
+ const addUsageObservation = (parent, dispatch) => { const observed = record(dispatch.usage); const counts = record(observed.usage); append(parent, 'p', '第 ' + dispatch.attemptNo + ' 次执行 · ' + (observed.status === 'COMPLETE' ? counts.totalTokens + ' Tokens;输入未缓存 ' + counts.uncachedInputTokens + ',输出 ' + counts.outputTokens + '(含推理)' : observed.status === 'PARTIAL' ? '部分覆盖:' + observed.reportedRequests + '/' + observed.observedRequests + ' 次请求有实报,合计尚未确认' : '用量不可用,不能按零计算')); if (observed.status === 'COMPLETE') append(parent, 'p', '缓存读取 ' + friendly(counts.cacheReadTokens, '未报告') + ' · 缓存写入 ' + friendly(counts.cacheWriteTokens, '未报告') + ' · 推理 ' + friendly(counts.reasoningTokens, '未单独报告') + '(属于输出,不重复累加)', 'hint'); };
293
+ const renderWorkbenchTaskDetail = (snapshot, detail) => preserveWorkbenchView(() => {
294
+ const parent = clear('task-detail-content'); if (!parent) return;
295
+ const trusted = detail && state.detailTaskId === state.selectedTaskId && record(detail.task).taskId === state.selectedTaskId ? detail : null;
296
+ const task = trusted ? trusted.task : selectedTask();
297
+ if (!task) { addEmpty(parent, state.selectedTaskId ? '所选任务暂不可用,请重新读取;不会改选其他任务。' : '尚无任务,先展开任务草稿,写清目标和验收。'); return; }
298
+ const id = task.taskId; const back = append(parent, 'a', '← 返回' + ({ today: '今日工作台', inbox: '待我处理', overview: '王国地图', map: '王国地图', usage: '用量' }[state.taskReturnSection] || '今日工作台'), 'task-return'); back.href = '#' + (state.taskReturnSection || 'today'); back.setAttribute('data-focus-key', 'task-back');
299
+ const summary = document.createElement('article'); summary.className = 'task-summary-card'; summary.setAttribute('data-read-anchor', id + ':summary'); append(summary, 'h3', task.title); append(summary, 'p', executionExplanation(task)); parent.append(summary);
300
+ const nav = document.createElement('nav'); nav.className = 'task-detail-jump'; nav.setAttribute('aria-label', '任务内容'); ['目标与验收', '当前进展', '交付及验证', '参与者', '用量', '历史'].forEach((label, index) => { const button = append(nav, 'button', label, 'role-inspect-button'); button.type = 'button'; button.setAttribute('data-focus-key', 'detail-jump:' + index); button.onclick = () => { const target = byId('task-section-' + ['goal', 'progress', 'delivery', 'participants', 'usage', 'history'][index]); if (target) { target.tabIndex = -1; target.focus({ preventScroll: true }); target.scrollIntoView({ block: 'start' }); } }; }); parent.append(nav);
301
+ const goal = detailSection(parent, id, 'goal', '目标与验收'); addParagraph(goal, '目标', task.title, '尚未填写'); addParagraph(goal, '范围', task.description, '尚未填写范围'); addParagraph(goal, '验收条件', task.acceptanceCriteria, '尚未填写验收条件');
302
+ const progress = detailSection(parent, id, 'progress', '当前进展'); addStateRow(progress, '任务治理状态', task.status); addStateRow(progress, '本次执行状态', record(task.latestExecution).state || 'NONE'); addDataRow(progress, '尝试次数', task.attemptCount); append(progress, 'p', executionExplanation(task));
303
+ const operations = append(progress, 'button', '查看此任务的合法操作', 'refresh'); operations.type = 'button'; operations.setAttribute('data-focus-key', 'task-operations'); operations.onclick = () => { const target = byId('operation-forms'); if (target) { target.tabIndex = -1; target.focus({ preventScroll: true }); target.scrollIntoView({ block: 'start' }); } };
304
+ const delivery = detailSection(parent, id, 'delivery', '交付及验证'); const claims = trusted && Array.isArray(trusted.claims) ? trusted.claims : task.latestClaim ? [task.latestClaim] : []; const reviews = trusted && Array.isArray(trusted.reviews) ? trusted.reviews : [];
305
+ const statuses = document.createElement('div'); statuses.className = 'task-evidence-states'; const latest = record(task.latestExecution); const latestReview = reviews.slice().sort((a, b) => Number(a.seq || 0) - Number(b.seq || 0))[reviews.length - 1];
306
+ [['执行记录', stateDisplay(latest.state || 'NONE')], ['主管裁定', latestReview ? stateDisplay(latestReview.decision) : '尚无已读取的裁定记录'], ['人类验收', '尚未记录']].forEach(item => { const cell = document.createElement('div'); append(cell, 'strong', item[0]); append(cell, 'span', item[1]); statuses.append(cell); }); delivery.append(statuses);
307
+ if (!trusted) append(delivery, 'p', '完整详情尚未读取成功。下方只显示快照已有的最近呈报;可重新读取补全历史。', 'hint');
308
+ if (!claims.length) addEmpty(delivery, '尚无执行者呈报或产物记录。');
309
+ 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
+ append(delivery, 'p', '验证说明:呈报与产物引用来自执行者自述。当前契约没有独立验证结果字段;主管裁定单独列出,人类验收尚未记录。', 'hint');
311
+ 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);
313
+ const historyPanel = detailSection(parent, id, 'history', '历史'); const historyItems = [];
314
+ 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
+ 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); });
316
+ if (trusted && (trusted.relatedEventsTruncated || trusted.reviewsTruncated)) addEmpty(historyPanel, '历史事件或主管裁定已达到投影上限;这里展示有界窗口,不代表完整历史。');
317
+ const technical = document.createElement('details'); technical.className = 'task-technical'; technical.setAttribute('data-read-key', id + ':technical'); append(technical, 'summary', '展开治理与技术详情'); addDataRow(technical, '任务编号', id); addDataRow(technical, '领地编号', task.territoryId); addDataRow(technical, '详情版本', trusted ? trusted.revision : '尚未读取'); parent.append(technical);
318
+ setText('task-detail-revision', trusted ? '最近更新 · 详情版本 ' + trusted.revision : '完整详情尚未读取');
319
+ });
320
+ const renderRoleInspector = snapshot => {
321
+ const shell = byId('role-inspector'); const parent = clear('role-inspector-content'); if (!shell || !parent) return; shell.hidden = !state.selectedRoleId; if (shell.hidden) return;
322
+ const organization = record(record(record(snapshot.projection).organization).data); const role = (Array.isArray(organization.roles) ? organization.roles : []).find(item => roleBindingRef(item) === state.selectedRoleId);
323
+ if (!role) { addEmpty(parent, '所选角色已不在当前投影中,请重新选择地图中的角色。'); return; }
324
+ append(parent, 'h3', role.roleName); const kind = role.roleType; append(parent, 'p', ({ CHANCELLOR: '职责:统筹目标、规划任务,并交给所属领地。', SUPERVISOR: '职责:在所属领地分派任务、核对执行并审查呈报。', WORKER: '职责:在授权范围执行任务,提交产物与风险说明。' })[kind] || '职责以宿主组织绑定为准。');
325
+ addParagraph(parent, '会话', role.sessionBound ? '已绑定' : '尚未绑定', '尚未确认');
326
+ const rolesProjection = record(workbenchData(snapshot).roles); const projected = queueItems(rolesProjection).find(item => item.bindingId === state.selectedRoleId || typedEntityId(item.bindingRef, 'binding') === state.selectedRoleId);
327
+ 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
+ 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
+ if (projected) { addDataRow(parent, '进行中执行', projected.activeExecutionCount); addDataRow(parent, '待主管复核', projected.reviewTaskCount); addDataRow(parent, '恢复核对中', projected.recoveringTaskCount); if (projected.taskIdsTruncated) addEmpty(parent, '关联任务已截断:显示 ' + projected.taskIds.length + ' / ' + projected.taskCount + ' 项。'); }
330
+ };
331
+ const openRoleInspector = bindingId => { state.selectedRoleId = bindingId; renderRoleInspector(state.snapshot || {}); const inspector = byId('role-inspector'); if (inspector) { inspector.hidden = false; inspector.focus({ preventScroll: true }); inspector.scrollIntoView({ block: 'nearest' }); } };
332
+ const addRoleInspectorButton = (parent, bindingId) => { if (!bindingId) return; const button = append(parent, 'button', '查看职责与任务', 'role-inspect-button'); button.type = 'button'; button.setAttribute('data-focus-key', 'role:' + bindingId); button.onclick = () => openRoleInspector(bindingId); };
333
+ const stageWorkbenchGoal = goal => { const title = byId('task-title'); if (!title) return false; if (String(title.value || '').trim() && String(title.value || '').trim() !== goal) return false; title.value = goal; return true; };
334
+ const consumeSubmittedDraft = payload => {
335
+ const fields = [['task-title', 'title'], ['task-description', 'description'], ['task-acceptance', 'acceptance_criteria'], ['task-territory', 'territory_id']];
336
+ const unchanged = fields.every(([id, key]) => { const input = byId(id); return input && String(input.value || '').trim() === String(payload[key] || '').trim(); });
337
+ if (!unchanged) return false;
338
+ fields.forEach(([id]) => { byId(id).value = ''; }); clearComposerTerritory();
339
+ const quick = byId('quick-task-goal'); if (quick && String(quick.value || '').trim() === String(payload.title || '').trim()) quick.value = '';
340
+ return true;
341
+ };
342
+ const mountWorkbench = () => {
343
+ const tasks = byId('tasks'); const zones = document.querySelector('.zones'); if (tasks && zones) { tasks.setAttribute('data-console-page', 'tasks'); tasks.hidden = true; zones.append(tasks); }
344
+ const management = byId('management'); if (management) management.setAttribute('data-console-page', 'legacy-management');
345
+ const composer = document.querySelector('.task-composer-card'); if (composer && tasks) { const draft = document.createElement('details'); draft.id = 'task-draft-panel'; draft.className = 'task-draft-panel'; append(draft, 'summary', '新建任务 · 编辑草稿'); draft.append(composer); tasks.insertBefore(draft, tasks.firstChild); }
346
+ const actionDock = byId('operation-forms'); const detailParent = byId('task-detail-content'); if (actionDock && detailParent) { actionDock.classList.add('task-actions-inline'); detailParent.parentElement.append(actionDock); }
347
+ const capability = document.querySelector('.capability-card'); if (capability) byId('settings-control-slot').append(capability);
348
+ const owner = document.querySelector('.owner-management'); if (owner) { owner.querySelectorAll('.owner-actions').forEach(node => node.remove()); byId('settings-owner-slot').append(owner); }
349
+ 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' }); } });
351
+ const refresh = byId('workbench-refresh'); if (refresh) refresh.onclick = () => { void load(false); };
352
+ 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
+ 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; });
354
+ const header = document.querySelector('.realm-sidebar'); if (header) header.style.minHeight = '0'; const syncHeaderHeight = () => { if (header && typeof header.getBoundingClientRect === 'function') document.documentElement.style.setProperty('--nav-height', Math.ceil(header.getBoundingClientRect().height) + 'px'); }; syncHeaderHeight();
355
+ if (header && typeof globalThis.ResizeObserver === 'function') { const headerObserver = new globalThis.ResizeObserver(syncHeaderHeight); headerObserver.observe(header); }
356
+ };
357
+ `;
358
+ //# sourceMappingURL=workbench-ui.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workbench-ui.js","sourceRoot":"","sources":["../../src/gui/workbench-ui.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2FtC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2BvC,CAAC;AAEF,yFAAyF;AACzF,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA0OzC,CAAC"}
@@ -0,0 +1,163 @@
1
+ export const WORKBENCH_ITEM_LIMIT = 40;
2
+ const source = (entityType, entityId) => ({ sourceType: 'table-row', entityType, entityId });
3
+ const rule = (ruleCode) => ({ sourceType: 'derived-rule', entityType: 'projection-rule', entityId: null, ruleCode });
4
+ const live = (state) => ['STARTING', 'RUNNING', 'PAUSED', 'RECOVERING'].includes(state);
5
+ const queue = (items) => ({ totalCount: items.length, items: items.slice(0, WORKBENCH_ITEM_LIMIT), truncated: items.length > WORKBENCH_ITEM_LIMIT });
6
+ /** Sum only provider reports whose identity and numeric shape match this Dispatch. */
7
+ export function buildWorkbenchUsage(input) {
8
+ let completeDispatches = 0;
9
+ let partialDispatches = 0;
10
+ let unavailableDispatches = 0;
11
+ const totals = { uncachedInputTokens: 0, outputTokens: 0, totalTokens: 0 };
12
+ const dispatches = [...new Map(input.governance.dispatches.map(dispatch => [dispatch.dispatchId, dispatch])).values()];
13
+ for (const dispatch of dispatches) {
14
+ const report = dispatch.usage;
15
+ const matches = report?.type === 'KingdomDispatchUsage/v1' && report.source === 'provider-reported'
16
+ && report.dispatchId === dispatch.dispatchId && report.taskId === dispatch.taskId && report.attemptNo === dispatch.attemptNo;
17
+ if (matches && report.status === 'PARTIAL') {
18
+ partialDispatches += 1;
19
+ continue;
20
+ }
21
+ const usage = matches && report.status === 'COMPLETE' ? report.usage : null;
22
+ if (!usage || ![usage.uncachedInputTokens, usage.outputTokens, usage.totalTokens].every(value => Number.isSafeInteger(value) && value >= 0)
23
+ || !Object.keys(totals).every(key => Number.isSafeInteger(totals[key] + usage[key]))) {
24
+ unavailableDispatches += 1;
25
+ continue;
26
+ }
27
+ completeDispatches += 1;
28
+ totals.uncachedInputTokens += usage.uncachedInputTokens;
29
+ totals.outputTokens += usage.outputTokens;
30
+ totals.totalTokens += usage.totalTokens;
31
+ }
32
+ const dispatchExecutionIds = new Set(dispatches.map(dispatch => dispatch.executionId));
33
+ const executionsWithoutDispatch = input.executions.filter(execution => !dispatchExecutionIds.has(execution.executionId)).length;
34
+ return {
35
+ scope: 'WORKER_DISPATCH_ONLY', totalDispatches: dispatches.length, completeDispatches, partialDispatches, unavailableDispatches,
36
+ executionsWithoutDispatch,
37
+ coverage: completeDispatches === 0 && partialDispatches === 0 ? 'UNAVAILABLE'
38
+ : completeDispatches === dispatches.length && executionsWithoutDispatch === 0 ? 'COMPLETE' : 'PARTIAL',
39
+ reportedTotals: completeDispatches ? totals : null,
40
+ cost: null, costStatus: 'NOT_RECORDED',
41
+ sourceRefs: [rule('WORKER_DISPATCH_USAGE_COVERAGE'), rule('WORKER_SUMMARY_EXCLUDES_OTHER_USAGE')],
42
+ };
43
+ }
44
+ /** Derive current work; past DENIED and ordinary in-flight receipts are not current incidents. */
45
+ export function buildPersonalWorkbench(input) {
46
+ const ownerActions = [];
47
+ const internalActions = [];
48
+ const exceptions = [];
49
+ const deliveries = [];
50
+ const activeBindings = input.bindings.filter(binding => binding.status === 'ACTIVE');
51
+ const supervisors = new Map(activeBindings.filter(binding => binding.roleType === 'SUPERVISOR').map(binding => [binding.bindingId, binding]));
52
+ const territories = new Map(input.territories.map(territory => [territory.territoryId, territory]));
53
+ const ownerConfiguration = (kind, title, summary, territoryId = null) => {
54
+ ownerActions.push({ id: `${kind}:${territoryId ?? 'kingdom'}`, kind, title, summary, territoryId, taskId: null,
55
+ responsibility: 'OWNER', responsibleBindingId: null, actionAvailability: [],
56
+ sourceRefs: [rule(kind), ...(territoryId ? [source('territories', territoryId)] : [])] });
57
+ };
58
+ if (!input.kingdomPresent) {
59
+ ownerConfiguration('KINGDOM_CONFIGURATION_REQUIRED', '建立王国', '尚未初始化王国。请通过现有的用户设置入口创建王国。');
60
+ }
61
+ else {
62
+ if (!activeBindings.some(binding => binding.roleType === 'CHANCELLOR')) {
63
+ ownerConfiguration('CHANCELLOR_CONFIGURATION_REQUIRED', '任命宰相', '当前没有有效的宰相绑定。需要用户通过现有任命入口完成配置。');
64
+ }
65
+ if (!input.territories.some(territory => territory.status === 'ACTIVE')) {
66
+ ownerConfiguration('TERRITORY_CONFIGURATION_REQUIRED', '建立工作领地', '当前没有可用领地。需要用户通过现有设置入口创建领地。');
67
+ }
68
+ for (const territory of input.territories) {
69
+ if (territory.status !== 'ACTIVE')
70
+ continue;
71
+ if (!territory.supervisorBindingId || !supervisors.has(territory.supervisorBindingId)) {
72
+ ownerConfiguration('TERRITORY_SUPERVISOR_CONFIGURATION_REQUIRED', `为「${territory.name}」指定主管`, '领地没有有效的在任主管。需要用户作任命决定;这不是任务的人类验收请求。', territory.territoryId);
73
+ }
74
+ }
75
+ }
76
+ const orderedTasks = [...input.tasks].sort((left, right) => right.task.updatedAt.localeCompare(left.task.updatedAt) || left.task.taskId.localeCompare(right.task.taskId));
77
+ const recoveringTaskIds = new Set();
78
+ for (const item of orderedTasks) {
79
+ const { task, actionAvailability } = item;
80
+ const territory = territories.get(task.territoryId);
81
+ const supervisor = territory?.status === 'ACTIVE' && territory.supervisorBindingId ? supervisors.get(territory.supervisorBindingId) : null;
82
+ const responsibility = supervisor ? 'SUPERVISOR' : 'UNDETERMINED';
83
+ const taskRefs = [source('tasks', task.taskId), source('territories', task.territoryId)];
84
+ const action = (kind, summary, extraRefs = []) => ({
85
+ id: `${kind}:${task.taskId}`, kind, taskId: task.taskId, territoryId: task.territoryId,
86
+ title: task.title, summary, responsibility, responsibleBindingId: supervisor?.bindingId ?? null,
87
+ actionAvailability, sourceRefs: [...taskRefs, ...extraRefs].slice(0, 8),
88
+ });
89
+ const dispatches = input.governance.dispatches.filter(dispatch => dispatch.taskId === task.taskId);
90
+ const leases = input.governance.leases.filter(lease => lease.taskId === task.taskId);
91
+ const recoveringDispatches = dispatches.filter(dispatch => dispatch.state === 'RECOVERING');
92
+ const unreleased = leases.filter(lease => lease.state === 'RECOVERING' || (lease.state === 'TERMINAL' && !lease.releasedAt));
93
+ const recovering = task.latestExecution?.state === 'RECOVERING' || recoveringDispatches.length > 0 || unreleased.length > 0;
94
+ if (recovering) {
95
+ recoveringTaskIds.add(task.taskId);
96
+ exceptions.push(action('EXECUTION_RECOVERY_REQUIRED', supervisor
97
+ ? '主管需先核对原执行的运行证据与资源释放,再决定后续;当前不能视为已完成。'
98
+ : '原执行的运行证据或资源释放尚未确认;当前没有可确认的有效主管,先核实领地任命。', [
99
+ ...(task.latestExecution?.state === 'RECOVERING' ? [source('executions', task.latestExecution.executionId)] : []),
100
+ ...recoveringDispatches.map(dispatch => source('dispatch_records', dispatch.dispatchId)),
101
+ ...unreleased.map(lease => source('execution_leases', lease.leaseId)),
102
+ ]));
103
+ }
104
+ if (item.claimExecutionMismatch) {
105
+ exceptions.push(action('CLAIM_EXECUTION_MISMATCH', '执行者自述与同次执行的运行证据不一致,需要主管核验;尚不能确认交付。', [
106
+ source('worker_results', task.latestClaim?.resultId ?? null), source('executions', task.latestExecution?.executionId ?? null),
107
+ ]));
108
+ }
109
+ // Only the latest unambiguous denial before a still-pending start is current.
110
+ const decisions = input.governance.decisions.filter(decision => decision.taskId === task.taskId);
111
+ const newestTime = decisions.reduce((latest, decision) => decision.createdAt > latest ? decision.createdAt : latest, '');
112
+ const newest = decisions.filter(decision => decision.createdAt === newestTime);
113
+ const denial = newest.length === 1 && newest[0].decision === 'DENIED' ? newest[0] : null;
114
+ const pendingStart = actionAvailability.some(availability => availability.action === 'start' && availability.lifecycleAllowed);
115
+ if (!recovering && denial && pendingStart && (!task.latestExecution || task.latestExecution.startedAt < denial.createdAt)) {
116
+ exceptions.push(action('CAPABILITY_DENIED', `最近一次能力检查被拒绝${denial.reasonCode ? `(${denial.reasonCode})` : ''}。由主管检查任务需求及运行配置;该记录不自动要求用户扩大权限。`, [source('capability_decisions', denial.decisionId)]));
117
+ }
118
+ if (task.status === 'REVIEW') {
119
+ internalActions.push(action('SUPERVISOR_REVIEW', supervisor ? '执行者已提交自述,等待本领地主管审查。尚不是用户验收待办。' : '执行者已提交自述,但当前没有有效的领地主管;待任命明确后再审查。'));
120
+ }
121
+ else if (!recovering && task.status === 'CREATED') {
122
+ internalActions.push(action('SUPERVISOR_ASSIGN', '任务已创建,等待领地主管选择执行者。'));
123
+ }
124
+ else if (!recovering && pendingStart) {
125
+ internalActions.push(action(item.latestReview?.decision === 'REWORK' ? 'SUPERVISOR_REWORK' : 'SUPERVISOR_START', item.latestReview?.decision === 'REWORK' ? '主管已要求返工,下一次执行尚未开始。' : '任务已分配,等待主管启动执行。'));
126
+ }
127
+ if (task.latestClaim || task.status === 'DONE') {
128
+ const accepted = task.status === 'DONE' && item.latestReview?.decision === 'ACCEPT'
129
+ && item.latestReview.reviewedAttemptNo === task.latestClaim?.attemptNo;
130
+ deliveries.push({ taskId: task.taskId, title: task.title, status: task.status, claim: task.latestClaim,
131
+ supervisorAccepted: accepted, supervisorDecision: item.latestReview, humanAcceptance: 'NOT_RECORDED', updatedAt: task.updatedAt,
132
+ sourceRefs: [...taskRefs, ...(task.latestClaim ? [source('worker_results', task.latestClaim.resultId)] : []), ...(item.latestReview?.sourceRefs ?? [])].slice(0, 8) });
133
+ }
134
+ }
135
+ const roles = activeBindings.map(binding => {
136
+ const taskItems = orderedTasks.filter(({ task }) => binding.roleType === 'WORKER' ? task.assignedBindingId === binding.bindingId
137
+ : binding.roleType === 'SUPERVISOR' ? territories.get(task.territoryId)?.supervisorBindingId === binding.bindingId
138
+ : binding.roleType === 'CHANCELLOR');
139
+ const taskIds = new Set(taskItems.map(({ task }) => task.taskId));
140
+ const roleExecutions = input.executions.filter(execution => live(execution.state)
141
+ && (binding.roleType === 'WORKER' ? execution.workerBindingId === binding.bindingId : taskIds.has(execution.taskId)));
142
+ return { bindingId: binding.bindingId, roleType: binding.roleType, roleName: binding.roleName, sessionBound: binding.sessionBound,
143
+ taskCount: taskItems.length, taskIds: [...taskIds].slice(0, WORKBENCH_ITEM_LIMIT), taskIdsTruncated: taskIds.size > WORKBENCH_ITEM_LIMIT,
144
+ activeExecutionCount: roleExecutions.length, reviewTaskCount: taskItems.filter(({ task }) => task.status === 'REVIEW').length,
145
+ recoveringTaskCount: taskItems.filter(({ task }) => recoveringTaskIds.has(task.taskId)).length,
146
+ sourceRefs: [source('role_bindings', binding.bindingId), rule('ROLE_TASK_RESPONSIBILITY_DERIVED')] };
147
+ });
148
+ for (const plan of input.collaboration?.plans.items ?? []) {
149
+ if (plan.state !== 'PROPOSED')
150
+ continue;
151
+ ownerActions.push({ id: 'plan:' + plan.planId, kind: 'PLAN_ADOPTION', taskId: plan.parentTaskId, territoryId: null,
152
+ title: '采纳协作计划 · ' + plan.title, summary: '审阅一份完整计划:分工、依赖、整合者及整项软预算。采纳后仍由主管合法推进,不自动派发。',
153
+ responsibility: 'OWNER', responsibleBindingId: null, actionAvailability: [], sourceRefs: [source('tasks', plan.parentTaskId), rule('COLLABORATION_PLAN_PROPOSED')] });
154
+ }
155
+ const ownerQueue = queue(ownerActions);
156
+ const omittedAdoptions = Math.max(0, (input.collaboration?.pendingAdoptionCount ?? 0) - (input.collaboration?.plans.items.filter(plan => plan.state === 'PROPOSED').length ?? 0));
157
+ ownerQueue.totalCount += omittedAdoptions;
158
+ ownerQueue.truncated ||= omittedAdoptions > 0;
159
+ return { ownerActions: ownerQueue, internalActions: queue(internalActions), exceptions: queue(exceptions), deliveries: queue(deliveries), roles: queue(roles), usage: buildWorkbenchUsage(input),
160
+ collaboration: input.collaboration ?? { plans: queue([]), resources: queue([]), pendingAdoptionCount: 0 },
161
+ cost: input.cost ?? { additionalRoles: null, budget: null, prompts: queue([]), runtime: { toolDisclosureMode: 'UNKNOWN', observerAvailable: null } } };
162
+ }
163
+ //# sourceMappingURL=workbench.js.map