dsh-kingdom 3.2.1 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -16
- package/docs/CURRENT-USER-GUIDE.md +106 -0
- package/docs/V3.3-RELEASE-NOTES.md +46 -0
- package/lib/core/owner-binding-intent.js +168 -11
- package/lib/core/owner-binding-intent.js.map +1 -1
- package/lib/gui/console-app.js +34 -30
- package/lib/gui/console-app.js.map +1 -1
- package/lib/gui/local-control.js +59 -3
- package/lib/gui/local-control.js.map +1 -1
- package/lib/gui/owner-ui.js +71 -8
- package/lib/gui/owner-ui.js.map +1 -1
- package/lib/gui/server.js +1 -1
- package/lib/gui/server.js.map +1 -1
- package/lib/gui/workbench-ui.js +179 -10
- package/lib/gui/workbench-ui.js.map +1 -1
- package/lib/index.js +29 -8
- package/lib/index.js.map +1 -1
- package/lib/types/core/owner-binding-intent.d.ts +26 -0
- package/lib/types/gui/console-app.d.ts +1 -1
- package/lib/types/gui/control-contract.d.ts +5 -3
- package/lib/types/gui/local-control.d.ts +14 -2
- package/lib/types/gui/owner-ui.d.ts +31 -0
- package/package.json +86 -84
package/lib/gui/workbench-ui.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/** Personal workbench presentation. No model calls or authority are created here. */
|
|
2
2
|
export const WORKBENCH_CSS = String.raw `
|
|
3
|
-
.workbench-page { grid-column: 1 / -1; min-width: 0; width: 100%; }
|
|
3
|
+
.workbench-page { grid-column: 1 / -1; min-width: 0; width: 100%; scroll-margin-top: 130px; }
|
|
4
|
+
.runtime-table-scroll-hint { display: none; color: var(--muted); font-size: .8rem; }
|
|
4
5
|
.workbench-heading { display: flex; justify-content: space-between; align-items: start; gap: 24px; margin: 18px 0 24px; }
|
|
5
6
|
.workbench-heading h2 { font-size: clamp(25px, 3vw, 36px); margin: 6px 0; }
|
|
6
7
|
.workbench-heading p, .workbench-block > p { color: var(--muted); }
|
|
@@ -21,6 +22,26 @@ export const WORKBENCH_CSS = String.raw `
|
|
|
21
22
|
.quick-task-form input { flex: 1; min-width: 0; min-height: 48px; }
|
|
22
23
|
.quick-task-form button { flex: 0 0 auto; }
|
|
23
24
|
.workbench-section-link { color: var(--gold); font-size: 13px; }
|
|
25
|
+
.runtime-overview { display: grid; gap: 18px; }
|
|
26
|
+
.runtime-table-scroll { overflow-x: auto; margin-top: 16px; }
|
|
27
|
+
.runtime-table { width: 100%; border-collapse: collapse; text-align: left; font-size: 14px; }
|
|
28
|
+
.runtime-table caption { text-align: left; color: var(--muted); margin-bottom: 10px; }
|
|
29
|
+
.runtime-table th, .runtime-table td { padding: 13px 10px; border-bottom: 1px solid var(--line); vertical-align: top; overflow-wrap: anywhere; }
|
|
30
|
+
.runtime-table th { color: var(--muted); font-weight: 500; }
|
|
31
|
+
.runtime-table td:first-child { min-width: 150px; }
|
|
32
|
+
.runtime-table a { color: var(--bamboo); text-underline-offset: 3px; }
|
|
33
|
+
.runtime-table small { display: block; color: var(--muted); margin-top: 5px; }
|
|
34
|
+
.runtime-table .runtime-related-task { display: block; margin-bottom: 5px; }
|
|
35
|
+
.runtime-overview-note { margin: 8px 0 0; color: var(--muted); font-size: 13px; }
|
|
36
|
+
.workbench-advanced { margin-top: 18px; }
|
|
37
|
+
.workbench-advanced > summary, .settings-list > details > summary { cursor: pointer; font-weight: 600; }
|
|
38
|
+
.workbench-advanced[open] > .workbench-grid { margin-top: 18px; }
|
|
39
|
+
.dispatch-content { display: grid; gap: 18px; }
|
|
40
|
+
.dispatch-step { min-width: 0; padding: 20px; border: 1px solid var(--line); border-radius: 12px; background: var(--patina); }
|
|
41
|
+
.dispatch-step h3 { margin-bottom: 12px; }
|
|
42
|
+
.dispatch-step > form { border: 0; padding: 0; box-shadow: none; }
|
|
43
|
+
.dispatch-content > .task-draft-panel { margin: 0; }
|
|
44
|
+
.supervisor-request { margin-top: 8px; min-height: 44px; }
|
|
24
45
|
.task-detail-section { padding: 20px 0; border-bottom: 1px solid var(--line); }
|
|
25
46
|
.task-detail-section h3 { margin-bottom: 12px; font-size: 18px; }
|
|
26
47
|
.task-detail-section p { white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
@@ -135,9 +156,14 @@ export const WORKBENCH_CSS = String.raw `
|
|
|
135
156
|
}
|
|
136
157
|
@media (max-width: 1050px) { .role-workbench-layout:has(.role-inspector:not([hidden])) { grid-template-columns: minmax(0, 1fr); } .role-inspector { position: static; } }
|
|
137
158
|
@media (max-width: 700px) {
|
|
159
|
+
.runtime-table-scroll-hint { display: block; margin-bottom: 12px; }
|
|
160
|
+
.workbench-page { scroll-margin-top: 185px; }
|
|
138
161
|
.workbench-grid, .task-evidence-states { grid-template-columns: minmax(0, 1fr); }
|
|
139
162
|
.workbench-heading { flex-direction: column; gap: 12px; }
|
|
140
163
|
.workbench-block { padding: 16px; }
|
|
164
|
+
.dispatch-step { padding: 16px; }
|
|
165
|
+
.runtime-table { font-size: 13px; min-width: 640px; }
|
|
166
|
+
.runtime-table th, .runtime-table td { padding: 11px 8px; }
|
|
141
167
|
.quick-task-form { flex-direction: column; }
|
|
142
168
|
.usage-table { font-size: 12px; }
|
|
143
169
|
.usage-table th, .usage-table td { padding: 9px 4px; }
|
|
@@ -147,16 +173,26 @@ export const WORKBENCH_CSS = String.raw `
|
|
|
147
173
|
`;
|
|
148
174
|
export const WORKBENCH_HTML = String.raw `
|
|
149
175
|
<section id="today" data-console-section="today" data-console-page="today" class="workbench-page" aria-labelledby="today-title">
|
|
150
|
-
<header class="workbench-heading"><div><p class="section-kicker">个人工作台</p><h2 id="today-title"
|
|
151
|
-
<
|
|
152
|
-
|
|
176
|
+
<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>
|
|
177
|
+
<div class="runtime-overview">
|
|
178
|
+
<section class="workbench-block" aria-labelledby="runtime-tasks-title"><div class="delivery-head"><h3 id="runtime-tasks-title">任务</h3><a class="workbench-section-link" href="#dispatch">派发新任务 →</a></div><div id="runtime-tasks" class="runtime-table-scroll"></div></section>
|
|
179
|
+
<section class="workbench-block" aria-labelledby="runtime-sessions-title"><h3 id="runtime-sessions-title">会话</h3><p class="runtime-overview-note">展示王国已登记角色与 Worker 长期会话。任务在推进,不代表主管会话正在调用模型;未提供运行观测时显示未知。</p><div id="runtime-sessions" class="runtime-table-scroll"></div></section>
|
|
180
|
+
</div>
|
|
181
|
+
<details class="workbench-block workbench-advanced" id="today-advanced"><summary>待处理、异常与交付</summary><div class="workbench-grid">
|
|
153
182
|
<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>
|
|
154
183
|
<section class="workbench-block"><h3>执行异常</h3><p>说明发生了什么、由谁继续处理。</p><div id="today-exceptions" class="workbench-list"></div></section>
|
|
155
184
|
<section class="workbench-block"><h3>正在推进</h3><div id="today-active" class="workbench-list"></div></section>
|
|
156
185
|
<section class="workbench-block"><h3>最近交付</h3><p>呈报、主管接受与人类验收分别展示。</p><div id="today-deliveries" class="workbench-list"></div></section>
|
|
157
186
|
</div>
|
|
158
187
|
<section class="workbench-block" style="margin-top:18px"><h3>团队内部处理</h3><p>主管审查与返工由团队继续推进,不计入你的待办。</p><div id="today-internal" class="workbench-list"></div></section>
|
|
159
|
-
|
|
188
|
+
</details>
|
|
189
|
+
<details class="workbench-block workbench-advanced" id="today-collaboration"><summary id="collaboration-title">协作计划与工作区占用</summary><p>单执行者仍是默认。需要协作时,由指定主执行者负责整合。</p><div id="collaboration-plans"></div><details data-read-key="collaboration:resources"><summary>当前工作区占用</summary><div id="collaboration-resources"></div></details></details>
|
|
190
|
+
</section>
|
|
191
|
+
<section id="dispatch-page" data-console-section="dispatch" data-console-page="dispatch" class="workbench-page" hidden aria-labelledby="dispatch-title">
|
|
192
|
+
<header class="workbench-heading"><div><p class="section-kicker">把事情交给团队</p><h2 id="dispatch-title">派发任务</h2><p>写清任务目标与所属领地,再由该领地主管指定执行者并启动。</p></div></header>
|
|
193
|
+
<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>
|
|
194
|
+
<div id="dispatch-content" class="dispatch-content"></div>
|
|
195
|
+
<p class="runtime-overview-note">按钮是否可用取决于打开本页的 DSH 会话身份。需要绑定或更换主管时,去<a class="workbench-section-link" href="#settings">主管设置</a>复制一句话请求,发给目标会话中的 AI。</p>
|
|
160
196
|
</section>
|
|
161
197
|
<section id="inbox" data-console-section="inbox" data-console-page="inbox" class="workbench-page" hidden aria-labelledby="inbox-title">
|
|
162
198
|
<header class="workbench-heading"><div><p class="section-kicker">你的决定</p><h2 id="inbox-title">待我处理</h2><p>当前版本的人类治理入口仍为直接 Slash;这里帮助你找到准确事项。</p></div></header>
|
|
@@ -169,8 +205,8 @@ export const WORKBENCH_HTML = String.raw `
|
|
|
169
205
|
<section class="workbench-block"><div id="usage-summary"></div><div id="usage-content"></div></section>
|
|
170
206
|
</section>
|
|
171
207
|
<section id="settings" data-console-section="settings" data-console-page="settings" class="workbench-page" hidden aria-labelledby="settings-title">
|
|
172
|
-
<header class="workbench-heading"><div><p class="section-kicker"
|
|
173
|
-
<div class="settings-list"><section class="workbench-block"><h3
|
|
208
|
+
<header class="workbench-heading"><div><p class="section-kicker">让会话负责领地</p><h2 id="settings-title">主管设置</h2><p>把下面的一句话发给目标 DSH 会话中的 AI,让它引导你确认绑定或更换主管。</p></div></header>
|
|
209
|
+
<div class="settings-list"><section class="workbench-block"><h3>各领地主管</h3><div id="settings-supervisors" class="runtime-table-scroll"></div><p class="hint">复制请求不会更改主管。到你希望担任主管的 DSH 会话发送请求,AI 会说明当前绑定并引导你确认;原生的人类确认仍需由你完成。</p></section><details class="workbench-block" id="settings-control"><summary>当前会话权限</summary><p>角色操作来自激活本页的合法会话,每次提交仍由宿主重新核验。</p><div id="settings-control-slot"></div><p class="hint">在已绑定角色的 DSH 会话输入 /kingdom gui,按宿主提供的链接重新打开本页。</p></details><details class="workbench-block" id="settings-owner"><summary>其他王国配置</summary><p>需要调整领地、权限或模型时,让当前 DSH 会话中的 AI 说明所需步骤并引导确认。</p><a class="workbench-section-link" href="/owner">打开人类管理窗口 →</a><div id="settings-owner-slot"></div></details><details class="workbench-block" id="settings-appearance"><summary>外观与动态效果</summary><p>顶部可选择羊皮纸、夜蓝、森林与酒红主题。人物动作沿用当前王国风格;减少动态效果跟随系统偏好。</p></details><details class="workbench-block" id="settings-advanced"><summary>高级记录与组织</summary><nav class="task-detail-jump"><a href="#map">王国地图</a><a href="#usage">用量</a><a href="#ledger">王国账本</a><a href="#organization">领地名册</a><a href="#executions">执行记录</a><a href="#activity">治理史册</a><a href="#tasks">任务详情</a></nav></details></div>
|
|
174
210
|
</section>
|
|
175
211
|
`;
|
|
176
212
|
/** Injected into the existing console closure; all Host text uses append/textContent. */
|
|
@@ -217,6 +253,132 @@ export const WORKBENCH_SCRIPT = String.raw `
|
|
|
217
253
|
if (String(task.status) === 'RUNNING' && !['STARTING', 'RUNNING', 'PAUSED'].includes(value)) return '任务处于处理中;当前没有正在运行的执行。返工决定不会自动启动下一次执行。';
|
|
218
254
|
return '任务治理:' + stateDisplay(task.status) + ' · 本次执行:' + stateDisplay(value);
|
|
219
255
|
};
|
|
256
|
+
const runtimeView = snapshot => {
|
|
257
|
+
const source = record(snapshot); const governance = record(source.governance);
|
|
258
|
+
const organization = record(record(record(source.projection).organization).data);
|
|
259
|
+
// Top-level bindings preserve retired history and do not expose status. The stage
|
|
260
|
+
// contains every exact ACTIVE binding; it must be used before listing current roles.
|
|
261
|
+
const activeIds = Array.isArray(source.stage) ? new Set(source.stage.map(actor => actor.bindingId).filter(Boolean)) : null;
|
|
262
|
+
const bindings = Array.isArray(source.bindings) ? source.bindings : [];
|
|
263
|
+
const territories = Array.isArray(source.territories) ? source.territories : [];
|
|
264
|
+
const seats = Array.isArray(organization.territories) ? organization.territories : [];
|
|
265
|
+
return { tasks: Array.isArray(source.tasks) ? source.tasks : [], bindings: activeIds ? bindings.filter(item => activeIds.has(item.bindingId)) : bindings,
|
|
266
|
+
territories, seats, roles: queueItems(workbenchData(source).roles),
|
|
267
|
+
seatCoverageComplete: Array.isArray(source.territories) && Array.isArray(organization.territories) && organization.territoriesTruncated !== true
|
|
268
|
+
&& territories.every(territory => seats.some(seat => typedEntityId(seat.territoryRef, 'territory') === territory.territoryId)),
|
|
269
|
+
affinities: Array.isArray(governance.workerSessions) ? governance.workerSessions.filter(item => item.isCurrent === true) : [],
|
|
270
|
+
executions: Array.isArray(source.liveExecutions) ? source.liveExecutions : [] };
|
|
271
|
+
};
|
|
272
|
+
const runtimeTerritoryName = (view, id) => {
|
|
273
|
+
const territory = view.territories.find(item => item.territoryId === id);
|
|
274
|
+
return territory ? friendly(territory.name, '领地名称未提供') : '领地未确认';
|
|
275
|
+
};
|
|
276
|
+
const runtimeSupervisorLookup = (view, id) => {
|
|
277
|
+
const seat = view.seats.find(item => typedEntityId(item.territoryRef, 'territory') === id);
|
|
278
|
+
if (seat) return { known: true, bindingId: typedEntityId(record(seat).supervisorBindingRef, 'binding') };
|
|
279
|
+
const roles = view.roles.filter(role => role.roleType === 'SUPERVISOR' && view.bindings.some(binding => binding.bindingId === role.bindingId && binding.roleType === 'SUPERVISOR')
|
|
280
|
+
&& Array.isArray(role.territories) && role.territories.some(territory => territory.territoryId === id));
|
|
281
|
+
return roles.length === 1 ? { known: true, bindingId: roles[0].bindingId } : { known: false, bindingId: null };
|
|
282
|
+
};
|
|
283
|
+
const runtimeSupervisorScope = (view, bindingId) => {
|
|
284
|
+
const role = view.roles.find(item => item.bindingId === bindingId && item.roleType === 'SUPERVISOR' && Array.isArray(item.territories));
|
|
285
|
+
if (role) return { known: true, territories: role.territories.map(territory => view.territories.find(item => item.territoryId === territory.territoryId) || territory) };
|
|
286
|
+
return { known: view.seatCoverageComplete, territories: view.territories.filter(territory => runtimeSupervisorLookup(view, territory.territoryId).bindingId === bindingId) };
|
|
287
|
+
};
|
|
288
|
+
const runtimeBindingName = (view, id) => {
|
|
289
|
+
const binding = view.bindings.find(item => item.bindingId === id);
|
|
290
|
+
return binding ? friendly(binding.roleName, '角色名称未提供') : '尚未指定或角色不可见';
|
|
291
|
+
};
|
|
292
|
+
const runtimeTable = (parent, columns, label) => {
|
|
293
|
+
append(parent, 'p', '左右滑动表格,可查看完整信息与操作按钮。', 'runtime-table-scroll-hint');
|
|
294
|
+
const table = document.createElement('table'); table.className = 'runtime-table';
|
|
295
|
+
if (label) append(table, 'caption', label);
|
|
296
|
+
const head = document.createElement('thead'); const row = document.createElement('tr');
|
|
297
|
+
columns.forEach(label => { const cell = append(row, 'th', label); cell.setAttribute('scope', 'col'); }); head.append(row); table.append(head);
|
|
298
|
+
const body = document.createElement('tbody'); table.append(body); parent.append(table); return body;
|
|
299
|
+
};
|
|
300
|
+
const runtimeLinkCell = row => { const cell = document.createElement('td'); row.append(cell); return cell; };
|
|
301
|
+
const addRuntimeTasks = (parent, tasks, key, scopeKnown = true) => {
|
|
302
|
+
if (!tasks.length) { append(parent, 'span', scopeKnown ? '暂无关联任务' : '关联任务未确认'); return; }
|
|
303
|
+
tasks.slice(0, 8).forEach(task => { const link = addTaskLink(parent, task.taskId, friendly(task.title, '未命名任务'), key + ':' + task.taskId); link.className = 'runtime-related-task'; });
|
|
304
|
+
if (tasks.length > 8) append(parent, 'small', '另有 ' + (tasks.length - 8) + ' 项,去任务详情查看。');
|
|
305
|
+
if (!scopeKnown) append(parent, 'small', '其他关联任务未确认;当前领地关系投影不完整。');
|
|
306
|
+
};
|
|
307
|
+
const renderRuntimeOverview = snapshot => {
|
|
308
|
+
const view = runtimeView(snapshot); const tasksParent = clear('runtime-tasks'); const sessionsParent = clear('runtime-sessions');
|
|
309
|
+
if (tasksParent) {
|
|
310
|
+
if (!view.tasks.length) addEmpty(tasksParent, '暂无任务。去“派发任务”填写目标并选择领地。');
|
|
311
|
+
else {
|
|
312
|
+
const body = runtimeTable(tasksParent, ['任务', '领地', '执行者', '任务状态', '本次执行'], '当前快照中的 ' + view.tasks.length + ' 个任务;点击名称查看详情。');
|
|
313
|
+
view.tasks.forEach(task => {
|
|
314
|
+
const row = document.createElement('tr'); row.setAttribute('data-runtime-task', task.taskId);
|
|
315
|
+
addTaskLink(runtimeLinkCell(row), task.taskId, friendly(task.title, '未命名任务'), 'runtime-task:' + task.taskId);
|
|
316
|
+
append(row, 'td', runtimeTerritoryName(view, task.territoryId)); append(row, 'td', runtimeBindingName(view, task.assignedBindingId));
|
|
317
|
+
append(row, 'td', stateDisplay(task.status)); const cell = append(row, 'td', task.latestExecution ? stateDisplay(record(task.latestExecution).state || 'UNKNOWN') : '尚无执行');
|
|
318
|
+
if (record(task.latestExecution).state === 'RECOVERING') append(cell, 'small', '等待主管核对,不会自动重发');
|
|
319
|
+
body.append(row);
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
if (!sessionsParent) return;
|
|
324
|
+
const rows = [];
|
|
325
|
+
view.bindings.forEach(binding => {
|
|
326
|
+
const affinities = binding.roleType === 'WORKER' ? view.affinities.filter(item => item.workerBindingId === binding.bindingId) : [];
|
|
327
|
+
if (affinities.length) affinities.forEach(affinity => rows.push({ binding, affinity }));
|
|
328
|
+
else rows.push({ binding, affinity: null });
|
|
329
|
+
});
|
|
330
|
+
view.affinities.filter(affinity => !view.bindings.some(binding => binding.bindingId === affinity.workerBindingId)).forEach(affinity => rows.push({ binding: { bindingId: affinity.workerBindingId, roleType: 'WORKER', roleName: '执行者(登记角色不可见)' }, affinity }));
|
|
331
|
+
if (!rows.length) { addEmpty(sessionsParent, '尚未登记角色或 Worker 长期会话。可在“主管设置”让会话引导配置。'); return; }
|
|
332
|
+
const body = runtimeTable(sessionsParent, ['角色名称', '绑定会话', '领地', '关联任务', '执行观察'], '仅含王国已登记角色与当前 Worker 长期会话,非平台全部会话。');
|
|
333
|
+
rows.forEach(({ binding, affinity }) => {
|
|
334
|
+
const row = document.createElement('tr'); row.setAttribute('data-runtime-binding', binding.bindingId);
|
|
335
|
+
if (affinity) row.setAttribute('data-runtime-affinity', affinity.affinityId);
|
|
336
|
+
const roleCell = append(row, 'td', friendly(binding.roleName, '角色名称未提供'));
|
|
337
|
+
append(roleCell, 'small', ({ CHANCELLOR: '宰相', SUPERVISOR: '领地主管', WORKER: '执行者', OWNER: '人类所有者' })[binding.roleType] || '登记角色');
|
|
338
|
+
const session = affinity ? affinity.sessionDisplay : binding.sessionDisplay;
|
|
339
|
+
const sessionCell = append(row, 'td', friendly(session, binding.sessionBound ? '已绑定(标识未提供)' : '尚未绑定'));
|
|
340
|
+
if (binding.roleType === 'WORKER') append(sessionCell, 'small', affinity ? '长期执行会话' : '登记会话;长期执行会话尚未建立');
|
|
341
|
+
const supervisorScope = binding.roleType === 'SUPERVISOR' ? runtimeSupervisorScope(view, binding.bindingId) : null;
|
|
342
|
+
const territories = affinity ? view.territories.filter(item => item.territoryId === affinity.territoryId)
|
|
343
|
+
: supervisorScope ? supervisorScope.territories
|
|
344
|
+
: binding.roleType === 'WORKER' ? view.territories.filter(item => view.tasks.some(task => task.territoryId === item.territoryId && task.assignedBindingId === binding.bindingId)) : [];
|
|
345
|
+
const territoryCell = append(row, 'td', territories.length ? territories.map(item => friendly(item.name, '领地名称未提供')).join('、') : affinity || supervisorScope && !supervisorScope.known ? '领地未确认' : binding.roleType === 'CHANCELLOR' || binding.roleType === 'OWNER' ? '王国范围' : '尚未隶属领地');
|
|
346
|
+
if (territories.length && supervisorScope && !supervisorScope.known) append(territoryCell, 'small', '其他领地未确认');
|
|
347
|
+
const tasks = view.tasks.filter(task => binding.roleType === 'WORKER' ? task.assignedBindingId === binding.bindingId && (!affinity || task.territoryId === affinity.territoryId)
|
|
348
|
+
: supervisorScope ? supervisorScope.territories.some(territory => territory.territoryId === task.territoryId) : binding.roleType === 'CHANCELLOR');
|
|
349
|
+
addRuntimeTasks(runtimeLinkCell(row), tasks, 'runtime-session-task:' + binding.bindingId + ':' + (affinity ? affinity.affinityId : 'binding'), !supervisorScope || supervisorScope.known);
|
|
350
|
+
// Supervisor responsibilities and task counts are not a model activity observation.
|
|
351
|
+
// Worker observations must identify this binding, its task and the projected session.
|
|
352
|
+
const executions = binding.roleType === 'WORKER' && session ? view.executions.filter(execution => execution.workerBindingId === binding.bindingId
|
|
353
|
+
&& execution.sessionId === session && tasks.some(task => task.taskId === execution.taskId)
|
|
354
|
+
&& ['STARTING', 'RUNNING', 'PAUSED', 'RECOVERING'].includes(execution.state)) : [];
|
|
355
|
+
const observed = append(row, 'td', executions.length ? '执行记录:' + Array.from(new Set(executions.map(item => stateDisplay(item.state)))).join('、') : '未知:未提供该会话的运行观测');
|
|
356
|
+
if (executions.length) append(observed, 'small', executions.length + ' 条关联执行;不表示模型持续生成中');
|
|
357
|
+
body.append(row);
|
|
358
|
+
});
|
|
359
|
+
};
|
|
360
|
+
const renderSupervisorSettings = snapshot => {
|
|
361
|
+
const parent = clear('settings-supervisors'); if (!parent) return; const view = runtimeView(snapshot);
|
|
362
|
+
if (!view.territories.length) { addEmpty(parent, '尚无领地。请在 DSH 会话中告诉 AI“帮我创建项目领地,并引导我设置主管”。'); return; }
|
|
363
|
+
const body = runtimeTable(parent, ['领地', '当前主管', '绑定会话', '交给会话处理'], null);
|
|
364
|
+
view.territories.forEach(territory => {
|
|
365
|
+
const lookup = runtimeSupervisorLookup(view, territory.territoryId); const seatKnown = lookup.known; const bindingId = lookup.bindingId;
|
|
366
|
+
const supervisor = view.bindings.find(item => item.bindingId === bindingId && item.roleType === 'SUPERVISOR');
|
|
367
|
+
const row = document.createElement('tr'); row.setAttribute('data-supervisor-territory', territory.territoryId);
|
|
368
|
+
append(row, 'td', friendly(territory.name, '未命名领地')); append(row, 'td', supervisor ? friendly(supervisor.roleName, '主管名称未提供') : seatKnown ? '尚未绑定可用主管' : '当前主管未确认');
|
|
369
|
+
append(row, 'td', supervisor ? friendly(supervisor.sessionDisplay, supervisor.sessionBound ? '已绑定(标识未提供)' : '尚未绑定会话') : seatKnown ? '尚未绑定' : '尚未确认');
|
|
370
|
+
const cell = runtimeLinkCell(row); const request = supervisor ? '把「' + territory.name + '」主管换成当前会话' : '让当前会话负责「' + territory.name + '」';
|
|
371
|
+
append(cell, 'p', request); const button = append(cell, 'button', supervisor ? '复制更换主管请求' : '复制绑定主管请求', 'refresh supervisor-request');
|
|
372
|
+
button.type = 'button'; button.setAttribute('data-focus-key', 'supervisor-request:' + territory.territoryId);
|
|
373
|
+
button.onclick = () => {
|
|
374
|
+
const clipboard = globalThis.navigator && globalThis.navigator.clipboard;
|
|
375
|
+
const succeeded = () => addCopyStatus(button, '已复制。到目标 DSH 会话发给 AI,让它引导确认。', true);
|
|
376
|
+
const failed = () => addCopyStatus(button, '未能复制。请选中上面的一句话,发给目标 DSH 会话中的 AI。', false);
|
|
377
|
+
try { if (!clipboard || typeof clipboard.writeText !== 'function') { failed(); return; } Promise.resolve(clipboard.writeText(request)).then(succeeded, failed); } catch (_) { failed(); }
|
|
378
|
+
};
|
|
379
|
+
body.append(row);
|
|
380
|
+
});
|
|
381
|
+
};
|
|
220
382
|
const claimSummaryPreview = value => {
|
|
221
383
|
let preview = friendly(value, '尚无呈报摘要');
|
|
222
384
|
if (typeof value === 'string' && value.length <= 32768) {
|
|
@@ -568,6 +730,7 @@ export const WORKBENCH_SCRIPT = String.raw `
|
|
|
568
730
|
};
|
|
569
731
|
const renderWorkbench = snapshot => {
|
|
570
732
|
const data = workbenchData(snapshot);
|
|
733
|
+
renderRuntimeOverview(snapshot); renderSupervisorSettings(snapshot);
|
|
571
734
|
renderWorkbenchQueue('today-owner', data.ownerActions, '当前没有明确需要你处理的事项。');
|
|
572
735
|
renderWorkbenchQueue('inbox-owner', data.ownerActions, '当前没有明确需要你处理的事项。主管审查与返工会在下方单独列出。');
|
|
573
736
|
renderWorkbenchQueue('today-exceptions', data.exceptions, '当前没有已投影的执行异常。');
|
|
@@ -701,7 +864,7 @@ export const WORKBENCH_SCRIPT = String.raw `
|
|
|
701
864
|
const trusted = detail && state.detailTaskId === state.selectedTaskId && record(detail.task).taskId === state.selectedTaskId ? detail : null;
|
|
702
865
|
const task = trusted ? trusted.task : selectedTask();
|
|
703
866
|
if (!task) { addEmpty(parent, state.selectedTaskId ? '所选任务暂不可用,请重新读取;不会改选其他任务。' : '尚无任务,先展开任务草稿,写清目标和验收。'); return; }
|
|
704
|
-
const id = task.taskId; const back = append(parent, 'a', '← 返回' + ({ today: '
|
|
867
|
+
const id = task.taskId; const back = append(parent, 'a', '← 返回' + ({ today: '运行总览', dispatch: '派发任务', settings: '主管设置', inbox: '待我处理', overview: '王国地图', map: '王国地图', usage: '用量' }[state.taskReturnSection] || '运行总览'), 'task-return'); back.href = '#' + (state.taskReturnSection || 'today'); back.setAttribute('data-focus-key', 'task-back');
|
|
705
868
|
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);
|
|
706
869
|
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);
|
|
707
870
|
const goal = detailSection(parent, id, 'goal', '目标与验收'); addParagraph(goal, '目标', task.title, '尚未填写'); addParagraph(goal, '范围', task.description, '尚未填写范围'); addParagraph(goal, '验收条件', task.acceptanceCriteria, '尚未填写验收条件');
|
|
@@ -755,12 +918,18 @@ export const WORKBENCH_SCRIPT = String.raw `
|
|
|
755
918
|
const mountWorkbench = () => {
|
|
756
919
|
const tasks = byId('tasks'); const zones = document.querySelector('.zones'); if (tasks && zones) { tasks.setAttribute('data-console-page', 'tasks'); tasks.hidden = true; zones.append(tasks); }
|
|
757
920
|
const management = byId('management'); if (management) management.setAttribute('data-console-page', 'legacy-management');
|
|
758
|
-
const
|
|
921
|
+
const dispatch = byId('dispatch-content'); const composer = document.querySelector('.task-composer-card');
|
|
922
|
+
if (composer && dispatch) { const draft = document.createElement('details'); draft.id = 'task-draft-panel'; draft.className = 'task-draft-panel'; draft.open = true; append(draft, 'summary', '1. 写清任务与领地'); draft.append(composer); dispatch.append(draft); }
|
|
923
|
+
[['assign-form', '2. 指定执行者'], ['start-form', '3. 启动执行']].forEach(([id, title]) => {
|
|
924
|
+
const form = byId(id); if (!form || !dispatch) return; const oldWrapper = form.closest('details');
|
|
925
|
+
const section = document.createElement('section'); section.className = 'dispatch-step'; append(section, 'h3', title); section.append(form); dispatch.append(section);
|
|
926
|
+
if (oldWrapper) oldWrapper.remove();
|
|
927
|
+
});
|
|
759
928
|
const actionDock = byId('operation-forms'); const detailParent = byId('task-detail-content'); if (actionDock && detailParent) { actionDock.classList.add('task-actions-inline'); detailParent.parentElement.append(actionDock); }
|
|
760
929
|
const capability = document.querySelector('.capability-card'); if (capability) byId('settings-control-slot').append(capability);
|
|
761
930
|
const owner = document.querySelector('.owner-management'); if (owner) { owner.querySelectorAll('.owner-actions').forEach(node => node.remove()); byId('settings-owner-slot').append(owner); }
|
|
762
931
|
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); }
|
|
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('
|
|
932
|
+
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, '', '#dispatch'); 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' }); } });
|
|
764
933
|
const refresh = byId('workbench-refresh'); if (refresh) refresh.onclick = () => { void load(false); };
|
|
765
934
|
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; });
|
|
766
935
|
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; });
|
|
@@ -1 +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
|
|
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2KtC,CAAC;AAEF,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqCvC,CAAC;AAEF,yFAAyF;AACzF,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAstBzC,CAAC"}
|
package/lib/index.js
CHANGED
|
@@ -30,7 +30,7 @@ import { OwnerLocalControlManager, validateLaunchAckHint } from './gui/owner-con
|
|
|
30
30
|
import { OwnerDecisionController } from './core/owner-window.js';
|
|
31
31
|
import { resolveWorkerExecution } from './worker/executor-factory.js';
|
|
32
32
|
import { issueOwnerControlCapability, ownerControlAuth, } from './core/owner-control.js';
|
|
33
|
-
import { draftOwnerBindingIntent, draftOwnerBindingIntentFromRejectedWrite, serializeOwnerBindingIntentDraft, } from './core/owner-binding-intent.js';
|
|
33
|
+
import { draftOwnerBindingIntent, buildOwnerBindingGuidance, draftOwnerBindingIntentFromRejectedWrite, serializeOwnerBindingIntentDraft, } from './core/owner-binding-intent.js';
|
|
34
34
|
export const name = 'dsh-kingdom';
|
|
35
35
|
export const inject = ['tools', 'commands'];
|
|
36
36
|
/**
|
|
@@ -859,12 +859,17 @@ export function apply(ctx, config, dependencies = {}) {
|
|
|
859
859
|
return 'OWNER_CONTROL_REQUIRED: Owner-only 写操作只能通过 direct `/kingdom` Slash 执行;'
|
|
860
860
|
+ 'Agent Tool、未激活的 GUI/HTTP、工具参数和 caller session 不能代表 Owner。';
|
|
861
861
|
}
|
|
862
|
-
|
|
862
|
+
const context = ownerBindingIntentContext(rejection.exec);
|
|
863
|
+
const draft = draftOwnerBindingIntentFromRejectedWrite({
|
|
863
864
|
code: 'OWNER_CONTROL_REQUIRED',
|
|
864
865
|
operation: rejection.operation,
|
|
865
866
|
request: rejection.request,
|
|
866
|
-
context
|
|
867
|
-
})
|
|
867
|
+
context,
|
|
868
|
+
});
|
|
869
|
+
return JSON.stringify({
|
|
870
|
+
...JSON.parse(serializeOwnerBindingIntentDraft(draft)),
|
|
871
|
+
guidance: buildOwnerBindingGuidance(draft, context),
|
|
872
|
+
});
|
|
868
873
|
};
|
|
869
874
|
// ── 加载期孤儿处理:按 Execution Contract 保持事实诚实 ──────────
|
|
870
875
|
//
|
|
@@ -1107,20 +1112,24 @@ export function apply(ctx, config, dependencies = {}) {
|
|
|
1107
1112
|
})), 'dsh-kingdom: bind-session tool');
|
|
1108
1113
|
ctx.effect(() => ctx.tools.register(defineTool({
|
|
1109
1114
|
name: 'kingdom_draft_owner_binding_intent',
|
|
1110
|
-
description: '
|
|
1115
|
+
description: '为当前会话绑定或接替宰相、指定领地主管,解析自然语言并准备完整管理窗口命令、预览确认步骤;只读不绑定。用户确认后须读回结果;缺失或歧义时按 ambiguity.question 与 guidance 追问。',
|
|
1111
1116
|
parameters: {
|
|
1112
|
-
text: { type: 'string', required: true, description: '
|
|
1117
|
+
text: { type: 'string', required: true, description: '用户原意,例如“把当前会话设为「领地名」的主管”或“把「领地名」主管换成当前会话”' },
|
|
1113
1118
|
},
|
|
1114
1119
|
output: {
|
|
1115
1120
|
schema: { type: 'string' },
|
|
1116
1121
|
render: (_args, value) => [{ type: 'text', text: String(value) }],
|
|
1117
1122
|
},
|
|
1118
1123
|
async execute(args, exec) {
|
|
1124
|
+
const context = ownerBindingIntentContext(exec);
|
|
1119
1125
|
const draft = draftOwnerBindingIntent({
|
|
1120
1126
|
text: args.text,
|
|
1121
|
-
context
|
|
1127
|
+
context,
|
|
1128
|
+
});
|
|
1129
|
+
return JSON.stringify({
|
|
1130
|
+
...JSON.parse(serializeOwnerBindingIntentDraft(draft)),
|
|
1131
|
+
guidance: buildOwnerBindingGuidance(draft, context),
|
|
1122
1132
|
});
|
|
1123
|
-
return serializeOwnerBindingIntentDraft(draft);
|
|
1124
1133
|
},
|
|
1125
1134
|
})), 'dsh-kingdom: owner-binding-intent draft tool');
|
|
1126
1135
|
ctx.effect(() => ctx.tools.register(defineTool({
|
|
@@ -1633,6 +1642,18 @@ export function apply(ctx, config, dependencies = {}) {
|
|
|
1633
1642
|
// This remains empty until startGuiServer reports the actual port.
|
|
1634
1643
|
// In particular, port=0 can never activate against a guessed origin.
|
|
1635
1644
|
expectedOrigin: () => listeningAddress?.origin ?? '',
|
|
1645
|
+
inspectRoleAction: (principal, command) => {
|
|
1646
|
+
const kingdomId = requireKingdom();
|
|
1647
|
+
if (!kingdomId || !principal.sessionId
|
|
1648
|
+
|| !validateLiveDirectSession(principal.sessionId, dshRegistrySeam()).ok) {
|
|
1649
|
+
return { executable: false, disabledReason: 'ROLE_AUTH_REQUIRED' };
|
|
1650
|
+
}
|
|
1651
|
+
const role = command === 'plan' ? 'CHANCELLOR' : 'SUPERVISOR';
|
|
1652
|
+
const matches = store.getBindingsByRole(kingdomId, role).some(binding => binding.status === 'ACTIVE' && binding.session_id === principal.sessionId);
|
|
1653
|
+
return matches
|
|
1654
|
+
? { executable: true }
|
|
1655
|
+
: { executable: false, disabledReason: role === 'CHANCELLOR' ? 'CHANCELLOR_REQUIRED' : 'SUPERVISOR_REQUIRED' };
|
|
1656
|
+
},
|
|
1636
1657
|
});
|
|
1637
1658
|
const ownerController = new OwnerDecisionController(store, {
|
|
1638
1659
|
validateTargetSession: async ({ sessionId, signal }) => {
|