dsh-kingdom 2.0.0 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ASSETS_NOTICE.md +22 -22
- package/README.md +246 -243
- package/THIRD_PARTY_NOTICES.md +92 -92
- package/docs/DSH-KINGDOM-3.0-ANNOUNCEMENT.md +13 -0
- package/docs/V2.0-RELEASE-NOTES.md +30 -30
- package/docs/V2.0-USER-GUIDE.md +74 -74
- package/docs/V3.0-RELEASE-NOTES.md +21 -0
- package/docs/V3.0-USER-GUIDE.md +33 -0
- package/docs/V3.1-RELEASE-NOTES.md +76 -0
- package/docs/V3.2-RELEASE-NOTES.md +103 -0
- package/lib/core/binding.js +167 -60
- package/lib/core/binding.js.map +1 -1
- package/lib/core/db.js +582 -546
- package/lib/core/db.js.map +1 -1
- package/lib/core/delivery-ack.js +1356 -0
- package/lib/core/delivery-ack.js.map +1 -0
- package/lib/core/delivery-change.js +1237 -0
- package/lib/core/delivery-change.js.map +1 -0
- package/lib/core/owner-binding-intent.js +14 -25
- package/lib/core/owner-binding-intent.js.map +1 -1
- package/lib/core/owner-window.js +698 -23
- package/lib/core/owner-window.js.map +1 -1
- package/lib/core/task-service.js +144 -5
- package/lib/core/task-service.js.map +1 -1
- package/lib/core/territory.js +43 -9
- package/lib/core/territory.js.map +1 -1
- package/lib/gui/assets/characters/chancellor-idle.svg +67 -67
- package/lib/gui/assets/characters/chancellor-sleeping.svg +67 -67
- package/lib/gui/assets/characters/chancellor-thinking.svg +67 -67
- package/lib/gui/assets/characters/chancellor-working.svg +67 -67
- package/lib/gui/assets/characters/knight-redraw-r1-idle.svg +67 -67
- package/lib/gui/assets/characters/knight-redraw-r1-sleeping.svg +67 -67
- package/lib/gui/assets/characters/knight-redraw-r1-thinking.svg +67 -67
- package/lib/gui/assets/characters/knight-redraw-r1-working.svg +67 -67
- package/lib/gui/assets/characters/supervisor-idle.svg +67 -67
- package/lib/gui/assets/characters/supervisor-sleeping.svg +67 -67
- package/lib/gui/assets/characters/supervisor-thinking.svg +67 -67
- package/lib/gui/assets/characters/supervisor-working.svg +67 -67
- package/lib/gui/console-app.js +7 -1
- package/lib/gui/console-app.js.map +1 -1
- package/lib/gui/contract.js +6 -0
- package/lib/gui/contract.js.map +1 -1
- package/lib/gui/control-contract.js +3 -2
- package/lib/gui/control-contract.js.map +1 -1
- package/lib/gui/owner-control.js +114 -4
- package/lib/gui/owner-control.js.map +1 -1
- package/lib/gui/owner-ui.js +645 -183
- package/lib/gui/owner-ui.js.map +1 -1
- package/lib/gui/server.js +109 -109
- package/lib/gui/snapshot.js +127 -4
- package/lib/gui/snapshot.js.map +1 -1
- package/lib/gui/visual-assets.js +37 -21
- package/lib/gui/visual-assets.js.map +1 -1
- package/lib/gui/visual-system.js +269 -269
- package/lib/gui/workbench-ui.js +549 -136
- package/lib/gui/workbench-ui.js.map +1 -1
- package/lib/gui/workbench.js +133 -0
- package/lib/gui/workbench.js.map +1 -1
- package/lib/index.js +392 -40
- package/lib/index.js.map +1 -1
- package/lib/types/core/binding.d.ts +29 -0
- package/lib/types/core/db.d.ts +14 -0
- package/lib/types/core/delivery-ack.d.ts +688 -0
- package/lib/types/core/delivery-change.d.ts +381 -0
- package/lib/types/core/owner-binding-intent.d.ts +9 -1
- package/lib/types/core/owner-window.d.ts +237 -3
- package/lib/types/core/task-service.d.ts +22 -0
- package/lib/types/core/territory.d.ts +18 -3
- package/lib/types/gui/contract.d.ts +162 -1
- package/lib/types/gui/owner-control.d.ts +61 -1
- package/lib/types/gui/snapshot.d.ts +8 -0
- package/lib/types/gui/visual-assets.d.ts +16 -0
- package/lib/types/gui/workbench.d.ts +47 -1
- package/lib/types/index.d.ts +60 -1
- package/lib/types/worker/governed-executor.d.ts +15 -0
- package/lib/worker/governed-executor.js +21 -0
- package/lib/worker/governed-executor.js.map +1 -1
- package/package.json +80 -75
package/lib/gui/owner-ui.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"owner-ui.js","sourceRoot":"","sources":["../../src/gui/owner-ui.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAA;IACxF,OAAO,cAAc,CAAC,UAAU,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAA;AAC5D,CAAC;AAED,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAA
|
|
1
|
+
{"version":3,"file":"owner-ui.js","sourceRoot":"","sources":["../../src/gui/owner-ui.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,0BAA0B,CAAC,CAAA;IACxF,OAAO,cAAc,CAAC,UAAU,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAA;AAC5D,CAAC;AAED,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBAqoBT,CAAA"}
|
package/lib/gui/server.js
CHANGED
|
@@ -198,115 +198,115 @@ function validateCommandPayload(command, payload) {
|
|
|
198
198
|
return null;
|
|
199
199
|
}
|
|
200
200
|
/** v0.9 S2:四区只读 Console;数据只来自现有 GET projection seam。 */
|
|
201
|
-
export const READONLY_CONSOLE_HTML = `<!doctype html>
|
|
202
|
-
<html lang="zh-CN">
|
|
203
|
-
<head>
|
|
204
|
-
<meta charset="utf-8">
|
|
205
|
-
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
206
|
-
<title>dsh-Kingdom Read-only Projection Console</title>
|
|
207
|
-
<style>
|
|
208
|
-
:root { color-scheme: dark; font-family: system-ui, sans-serif; background: #111827; color: #e5e7eb; }
|
|
209
|
-
* { box-sizing: border-box; }
|
|
210
|
-
body { margin: 0; min-width: 0; background: #111827; }
|
|
211
|
-
main { width: min(100% - 24px, 1180px); margin: 0 auto; padding: 16px 0 28px; }
|
|
212
|
-
h1, h2, p { margin: 0; }
|
|
213
|
-
h1 { font-size: 1.35rem; margin-bottom: 12px; }
|
|
214
|
-
h2 { font-size: 1rem; margin-bottom: 8px; }
|
|
215
|
-
.grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
|
|
216
|
-
section { min-width: 0; background: #1f2937; border: 1px solid #374151; border-radius: 10px; padding: 12px; overflow-wrap: anywhere; }
|
|
217
|
-
.wide { grid-column: 1 / -1; }
|
|
218
|
-
.muted { color: #9ca3af; font-size: .88rem; }
|
|
219
|
-
.row { border-top: 1px solid #374151; padding: 8px 0; }
|
|
220
|
-
.row:first-child { border-top: 0; padding-top: 0; }
|
|
221
|
-
.tag { display: inline-block; color: #bfdbfe; font-size: .76rem; margin-right: 6px; }
|
|
222
|
-
.attention { color: #fbbf24; }
|
|
223
|
-
.critical { color: #fca5a5; }
|
|
224
|
-
@media (max-width: 640px) {
|
|
225
|
-
main { width: min(100% - 16px, 1180px); padding-top: 10px; }
|
|
226
|
-
.grid { grid-template-columns: minmax(0, 1fr); }
|
|
227
|
-
.wide { grid-column: auto; }
|
|
228
|
-
section { padding: 10px; }
|
|
229
|
-
}
|
|
230
|
-
</style>
|
|
231
|
-
</head>
|
|
232
|
-
<body>
|
|
233
|
-
<main>
|
|
234
|
-
<h1>Read-only Projection Console</h1>
|
|
235
|
-
<p class="muted">Owner-only actions: DIRECT_SLASH_REQUIRED</p>
|
|
236
|
-
<p id="status" class="muted">正在读取只读 Projection…</p>
|
|
237
|
-
<div class="grid">
|
|
238
|
-
<section id="overview" aria-labelledby="overview-title"><h2 id="overview-title">状态总览</h2><div class="content"></div></section>
|
|
239
|
-
<section id="task-detail" aria-labelledby="task-title"><h2 id="task-title">Task Detail</h2><div class="content"></div></section>
|
|
240
|
-
<section id="timeline" class="wide" aria-labelledby="timeline-title"><h2 id="timeline-title">Timeline</h2><div class="content"></div></section>
|
|
241
|
-
<section id="attention" class="wide" aria-labelledby="attention-title"><h2 id="attention-title">Attention</h2><div class="content"></div></section>
|
|
242
|
-
</div>
|
|
243
|
-
</main>
|
|
244
|
-
<script>
|
|
245
|
-
(() => {
|
|
246
|
-
const text = value => String(value ?? 'UNKNOWN');
|
|
247
|
-
const line = (parent, label, value, className = '') => {
|
|
248
|
-
const item = document.createElement('div');
|
|
249
|
-
item.className = 'row ' + className;
|
|
250
|
-
const tag = document.createElement('span');
|
|
251
|
-
tag.className = 'tag';
|
|
252
|
-
tag.textContent = label;
|
|
253
|
-
const body = document.createElement('span');
|
|
254
|
-
body.textContent = text(value);
|
|
255
|
-
item.append(tag, body);
|
|
256
|
-
parent.append(item);
|
|
257
|
-
};
|
|
258
|
-
const clear = id => {
|
|
259
|
-
const content = document.querySelector('#' + id + ' .content');
|
|
260
|
-
content.replaceChildren();
|
|
261
|
-
return content;
|
|
262
|
-
};
|
|
263
|
-
const renderSnapshot = (snapshot, detail) => {
|
|
264
|
-
const projection = snapshot.projection || {};
|
|
265
|
-
const overview = projection.overview?.data || {};
|
|
266
|
-
const overviewBox = clear('overview');
|
|
267
|
-
line(overviewBox, 'Health', overview.health, overview.health === 'CRITICAL' ? 'critical' : '');
|
|
268
|
-
line(overviewBox, 'Tasks', overview.taskCount);
|
|
269
|
-
line(overviewBox, 'Active Execution', overview.activeExecutionCount);
|
|
270
|
-
for (const action of overview.ownerActions || []) {
|
|
271
|
-
line(overviewBox, 'Owner action', text(action.action) + ' — ' + text(action.disabledReason?.code), 'attention');
|
|
272
|
-
}
|
|
273
|
-
const taskBox = clear('task-detail');
|
|
274
|
-
const task = snapshot.tasks?.[0];
|
|
275
|
-
if (!task) line(taskBox, 'State', 'UNKNOWN — no task detail available');
|
|
276
|
-
else {
|
|
277
|
-
line(taskBox, 'Task status', text(task.status) + ' [GOVERNANCE_FACT]');
|
|
278
|
-
line(taskBox, 'Claim', text(task.latestClaim?.claimedOutcome) + ' [WORKER_CLAIM]');
|
|
279
|
-
line(taskBox, 'Execution', text(task.latestExecution?.state) + ' [RUNTIME_OBSERVATION]');
|
|
280
|
-
line(taskBox, 'Actions', (detail?.projection?.data?.actionAvailability || []).map(a => text(a.action) + ': ' + text(a.disabledReason?.code || 'EXECUTABLE')).join('; ') || 'NONE');
|
|
281
|
-
}
|
|
282
|
-
const timelineBox = clear('timeline');
|
|
283
|
-
for (const item of projection.timeline?.data || []) line(timelineBox, text(item.kind), item.summary);
|
|
284
|
-
if (!(projection.timeline?.data || []).length) line(timelineBox, 'Timeline', 'UNKNOWN / NOT_RUN');
|
|
285
|
-
const attentionBox = clear('attention');
|
|
286
|
-
for (const item of projection.attention?.data || []) line(attentionBox, text(item.severity), text(item.reason?.code) + ' — ' + text(item.summary), item.severity.toLowerCase());
|
|
287
|
-
if (!(projection.attention?.data || []).length) line(attentionBox, 'Attention', 'NONE');
|
|
288
|
-
};
|
|
289
|
-
const load = async () => {
|
|
290
|
-
try {
|
|
291
|
-
const response = await fetch('/api/snapshot', { headers: { Accept: 'application/json' } });
|
|
292
|
-
if (!response.ok) throw new Error('snapshot HTTP ' + response.status);
|
|
293
|
-
const snapshot = await response.json();
|
|
294
|
-
let detail = null;
|
|
295
|
-
const task = snapshot.tasks?.[0];
|
|
296
|
-
if (task?.taskId) {
|
|
297
|
-
const detailResponse = await fetch('/api/tasks/' + encodeURIComponent(task.taskId), { headers: { Accept: 'application/json' } });
|
|
298
|
-
if (detailResponse.ok) detail = await detailResponse.json();
|
|
299
|
-
}
|
|
300
|
-
renderSnapshot(snapshot, detail);
|
|
301
|
-
document.querySelector('#status').textContent = '只读 Projection 已加载;Console 不执行写动作。';
|
|
302
|
-
} catch (error) {
|
|
303
|
-
document.querySelector('#status').textContent = 'UNKNOWN — ' + text(error?.message || error);
|
|
304
|
-
}
|
|
305
|
-
};
|
|
306
|
-
void load();
|
|
307
|
-
})();
|
|
308
|
-
</script>
|
|
309
|
-
</body>
|
|
201
|
+
export const READONLY_CONSOLE_HTML = `<!doctype html>
|
|
202
|
+
<html lang="zh-CN">
|
|
203
|
+
<head>
|
|
204
|
+
<meta charset="utf-8">
|
|
205
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
206
|
+
<title>dsh-Kingdom Read-only Projection Console</title>
|
|
207
|
+
<style>
|
|
208
|
+
:root { color-scheme: dark; font-family: system-ui, sans-serif; background: #111827; color: #e5e7eb; }
|
|
209
|
+
* { box-sizing: border-box; }
|
|
210
|
+
body { margin: 0; min-width: 0; background: #111827; }
|
|
211
|
+
main { width: min(100% - 24px, 1180px); margin: 0 auto; padding: 16px 0 28px; }
|
|
212
|
+
h1, h2, p { margin: 0; }
|
|
213
|
+
h1 { font-size: 1.35rem; margin-bottom: 12px; }
|
|
214
|
+
h2 { font-size: 1rem; margin-bottom: 8px; }
|
|
215
|
+
.grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
|
|
216
|
+
section { min-width: 0; background: #1f2937; border: 1px solid #374151; border-radius: 10px; padding: 12px; overflow-wrap: anywhere; }
|
|
217
|
+
.wide { grid-column: 1 / -1; }
|
|
218
|
+
.muted { color: #9ca3af; font-size: .88rem; }
|
|
219
|
+
.row { border-top: 1px solid #374151; padding: 8px 0; }
|
|
220
|
+
.row:first-child { border-top: 0; padding-top: 0; }
|
|
221
|
+
.tag { display: inline-block; color: #bfdbfe; font-size: .76rem; margin-right: 6px; }
|
|
222
|
+
.attention { color: #fbbf24; }
|
|
223
|
+
.critical { color: #fca5a5; }
|
|
224
|
+
@media (max-width: 640px) {
|
|
225
|
+
main { width: min(100% - 16px, 1180px); padding-top: 10px; }
|
|
226
|
+
.grid { grid-template-columns: minmax(0, 1fr); }
|
|
227
|
+
.wide { grid-column: auto; }
|
|
228
|
+
section { padding: 10px; }
|
|
229
|
+
}
|
|
230
|
+
</style>
|
|
231
|
+
</head>
|
|
232
|
+
<body>
|
|
233
|
+
<main>
|
|
234
|
+
<h1>Read-only Projection Console</h1>
|
|
235
|
+
<p class="muted">Owner-only actions: DIRECT_SLASH_REQUIRED</p>
|
|
236
|
+
<p id="status" class="muted">正在读取只读 Projection…</p>
|
|
237
|
+
<div class="grid">
|
|
238
|
+
<section id="overview" aria-labelledby="overview-title"><h2 id="overview-title">状态总览</h2><div class="content"></div></section>
|
|
239
|
+
<section id="task-detail" aria-labelledby="task-title"><h2 id="task-title">Task Detail</h2><div class="content"></div></section>
|
|
240
|
+
<section id="timeline" class="wide" aria-labelledby="timeline-title"><h2 id="timeline-title">Timeline</h2><div class="content"></div></section>
|
|
241
|
+
<section id="attention" class="wide" aria-labelledby="attention-title"><h2 id="attention-title">Attention</h2><div class="content"></div></section>
|
|
242
|
+
</div>
|
|
243
|
+
</main>
|
|
244
|
+
<script>
|
|
245
|
+
(() => {
|
|
246
|
+
const text = value => String(value ?? 'UNKNOWN');
|
|
247
|
+
const line = (parent, label, value, className = '') => {
|
|
248
|
+
const item = document.createElement('div');
|
|
249
|
+
item.className = 'row ' + className;
|
|
250
|
+
const tag = document.createElement('span');
|
|
251
|
+
tag.className = 'tag';
|
|
252
|
+
tag.textContent = label;
|
|
253
|
+
const body = document.createElement('span');
|
|
254
|
+
body.textContent = text(value);
|
|
255
|
+
item.append(tag, body);
|
|
256
|
+
parent.append(item);
|
|
257
|
+
};
|
|
258
|
+
const clear = id => {
|
|
259
|
+
const content = document.querySelector('#' + id + ' .content');
|
|
260
|
+
content.replaceChildren();
|
|
261
|
+
return content;
|
|
262
|
+
};
|
|
263
|
+
const renderSnapshot = (snapshot, detail) => {
|
|
264
|
+
const projection = snapshot.projection || {};
|
|
265
|
+
const overview = projection.overview?.data || {};
|
|
266
|
+
const overviewBox = clear('overview');
|
|
267
|
+
line(overviewBox, 'Health', overview.health, overview.health === 'CRITICAL' ? 'critical' : '');
|
|
268
|
+
line(overviewBox, 'Tasks', overview.taskCount);
|
|
269
|
+
line(overviewBox, 'Active Execution', overview.activeExecutionCount);
|
|
270
|
+
for (const action of overview.ownerActions || []) {
|
|
271
|
+
line(overviewBox, 'Owner action', text(action.action) + ' — ' + text(action.disabledReason?.code), 'attention');
|
|
272
|
+
}
|
|
273
|
+
const taskBox = clear('task-detail');
|
|
274
|
+
const task = snapshot.tasks?.[0];
|
|
275
|
+
if (!task) line(taskBox, 'State', 'UNKNOWN — no task detail available');
|
|
276
|
+
else {
|
|
277
|
+
line(taskBox, 'Task status', text(task.status) + ' [GOVERNANCE_FACT]');
|
|
278
|
+
line(taskBox, 'Claim', text(task.latestClaim?.claimedOutcome) + ' [WORKER_CLAIM]');
|
|
279
|
+
line(taskBox, 'Execution', text(task.latestExecution?.state) + ' [RUNTIME_OBSERVATION]');
|
|
280
|
+
line(taskBox, 'Actions', (detail?.projection?.data?.actionAvailability || []).map(a => text(a.action) + ': ' + text(a.disabledReason?.code || 'EXECUTABLE')).join('; ') || 'NONE');
|
|
281
|
+
}
|
|
282
|
+
const timelineBox = clear('timeline');
|
|
283
|
+
for (const item of projection.timeline?.data || []) line(timelineBox, text(item.kind), item.summary);
|
|
284
|
+
if (!(projection.timeline?.data || []).length) line(timelineBox, 'Timeline', 'UNKNOWN / NOT_RUN');
|
|
285
|
+
const attentionBox = clear('attention');
|
|
286
|
+
for (const item of projection.attention?.data || []) line(attentionBox, text(item.severity), text(item.reason?.code) + ' — ' + text(item.summary), item.severity.toLowerCase());
|
|
287
|
+
if (!(projection.attention?.data || []).length) line(attentionBox, 'Attention', 'NONE');
|
|
288
|
+
};
|
|
289
|
+
const load = async () => {
|
|
290
|
+
try {
|
|
291
|
+
const response = await fetch('/api/snapshot', { headers: { Accept: 'application/json' } });
|
|
292
|
+
if (!response.ok) throw new Error('snapshot HTTP ' + response.status);
|
|
293
|
+
const snapshot = await response.json();
|
|
294
|
+
let detail = null;
|
|
295
|
+
const task = snapshot.tasks?.[0];
|
|
296
|
+
if (task?.taskId) {
|
|
297
|
+
const detailResponse = await fetch('/api/tasks/' + encodeURIComponent(task.taskId), { headers: { Accept: 'application/json' } });
|
|
298
|
+
if (detailResponse.ok) detail = await detailResponse.json();
|
|
299
|
+
}
|
|
300
|
+
renderSnapshot(snapshot, detail);
|
|
301
|
+
document.querySelector('#status').textContent = '只读 Projection 已加载;Console 不执行写动作。';
|
|
302
|
+
} catch (error) {
|
|
303
|
+
document.querySelector('#status').textContent = 'UNKNOWN — ' + text(error?.message || error);
|
|
304
|
+
}
|
|
305
|
+
};
|
|
306
|
+
void load();
|
|
307
|
+
})();
|
|
308
|
+
</script>
|
|
309
|
+
</body>
|
|
310
310
|
</html>`;
|
|
311
311
|
/**
|
|
312
312
|
* 启动本地 GUI 通道。
|
package/lib/gui/snapshot.js
CHANGED
|
@@ -12,11 +12,13 @@ import { readAdditionalRoleCost, readLatestPromptCosts } from '../core/cost.js';
|
|
|
12
12
|
import { readBudgetView } from '../core/budget.js';
|
|
13
13
|
import { listCollaborationPlans, readCollaborationReadiness, readPlanBudgetView } from '../core/collaboration.js';
|
|
14
14
|
import { readWorkspaceReservations } from '../core/workspace-admission.js';
|
|
15
|
-
import { buildPersonalWorkbench } from './workbench.js';
|
|
15
|
+
import { buildPersonalWorkbench, } from './workbench.js';
|
|
16
16
|
import { GUI_SCHEMA_VERSION, TRANSIENT_WINDOW_MS } from './contract.js';
|
|
17
17
|
import { asExecutionState, isLiveExecutionState, isTerminalExecutionState } from '../core/execution.js';
|
|
18
18
|
import { asTaskStatus } from '../core/task.js';
|
|
19
19
|
import { parseExecutionProfile } from '../core/binding.js';
|
|
20
|
+
import { CHANGE_EVIDENCE_LABEL, DELIVERY_QUESTION_EVENT_TYPES, classifyAcceptedDelivery, deliveryAcknowledgementView, deliveryIdFor, deliveryQuestionThreadForItem, deriveDeliveryItems, readDeliveryAcknowledgements, readDeliveryQuestionThread, readLatestReviewEvent, redactCredentialText, toDeliveryQuestionEventPayload, } from '../core/delivery-ack.js';
|
|
21
|
+
import { readAcceptedChangeEvidence, resolveChangeEvidenceForDelivery } from '../core/delivery-change.js';
|
|
20
22
|
// ── 行 → 视图 ───────────────────────────────────────────────────
|
|
21
23
|
function parseJson(text) {
|
|
22
24
|
try {
|
|
@@ -69,8 +71,7 @@ function sensitivePublicJsonKey(key) {
|
|
|
69
71
|
function sanitizePublicJsonString(value, sensitivity = 'public-content') {
|
|
70
72
|
if (sensitivity === 'private-config')
|
|
71
73
|
return REDACTED_JSON_VALUE;
|
|
72
|
-
const redacted = value
|
|
73
|
-
.replace(/(?:authorization|bearer|session(?:[_ .-]?id)?|principal(?:[_ .-]?id)?|private(?:[_ .-]?config)?|token|cookie|credential|secret|password|api[_ .-]?key|access[_ .-]?key|client[_ .-]?secret|connection[_ .-]?string|csrf)\b(?:\s*["']?\s*(?:=|:)\s*["']?\s*|\s+)[^\s,;&}]+/giu, REDACTED_JSON_VALUE)
|
|
74
|
+
const redacted = redactCredentialText(value)
|
|
74
75
|
.replace(/[A-Za-z]:[\\/][^\s<>"']*/gu, '[redacted-path]')
|
|
75
76
|
.replace(/\\\\[^\s<>"']*/gu, '[redacted-path]')
|
|
76
77
|
.replace(/(^|[\s("'=])\/[^\s<>"']*/gu, '$1[redacted-path]')
|
|
@@ -405,6 +406,14 @@ export function buildActionAvailability(store, task, execution, security = {}) {
|
|
|
405
406
|
};
|
|
406
407
|
});
|
|
407
408
|
}
|
|
409
|
+
/**
|
|
410
|
+
* 事件 → 公开投影。
|
|
411
|
+
*
|
|
412
|
+
* 交付问答事件的正文**只**经受权入口返回:Owner 窗口走
|
|
413
|
+
* `OwnerDecisionController.readDeliveryQuestions`,主管走 session-bound Agent Tool
|
|
414
|
+
* 的账本读取。因此快照、TaskDetail.relatedEvents、recentEvents 与任何事件轮询/SSE
|
|
415
|
+
* 都只保留不含正文的最小元数据,绝不透传 `questionText`/`replyText`。
|
|
416
|
+
*/
|
|
408
417
|
export function toEventView(row) {
|
|
409
418
|
return {
|
|
410
419
|
seq: row.seq,
|
|
@@ -414,7 +423,9 @@ export function toEventView(row) {
|
|
|
414
423
|
actorId: row.actor_id,
|
|
415
424
|
targetType: row.target_type,
|
|
416
425
|
targetId: row.target_id,
|
|
417
|
-
payload:
|
|
426
|
+
payload: DELIVERY_QUESTION_EVENT_TYPES.includes(row.event_type)
|
|
427
|
+
? sanitizePublicJsonObject(toDeliveryQuestionEventPayload(row))
|
|
428
|
+
: sanitizePublicJsonObject(parseJson(row.payload_json)),
|
|
418
429
|
createdAt: row.created_at,
|
|
419
430
|
};
|
|
420
431
|
}
|
|
@@ -1494,6 +1505,115 @@ function readTaskReviewEvents(store, kingdomId, taskId, limit) {
|
|
|
1494
1505
|
return store.db.prepare("SELECT * FROM events WHERE kingdom_id = ? AND target_type = 'task' AND target_id = ? AND actor_role = 'SUPERVISOR' AND event_type IN ('TASK_ACCEPTED', 'TASK_REWORK_REQUESTED', 'TASK_FAILED', 'TASK_HANDED_OFF') ORDER BY seq DESC LIMIT ?")
|
|
1495
1506
|
.all(kingdomId, taskId, limit);
|
|
1496
1507
|
}
|
|
1508
|
+
/**
|
|
1509
|
+
* 已确认交付的条目层投影。
|
|
1510
|
+
*
|
|
1511
|
+
* 只有同一 Task/attempt 主管 ACCEPT(`acceptedDelivery`)后才生成条目层;
|
|
1512
|
+
* Worker Claim 永远不会让这里产生条目。本函数只读取既有事件与 Claim,
|
|
1513
|
+
* 不写入任何事实,也不创建新的验收状态库。
|
|
1514
|
+
*/
|
|
1515
|
+
function buildWorkbenchDeliveryLayers(store, kingdomId, tasks, present) {
|
|
1516
|
+
const layers = {};
|
|
1517
|
+
if (!present)
|
|
1518
|
+
return layers;
|
|
1519
|
+
const ownerId = store.getDefaultKingdom()?.owner_id ?? '';
|
|
1520
|
+
const publicId = (id) => publicReferenceId(id) ?? 'redacted';
|
|
1521
|
+
/** 当前 Task 的条目提问元数据;每个 Task 重新填充,不跨交付混用。 */ const itemQuestions = new Map();
|
|
1522
|
+
/**
|
|
1523
|
+
* 已由主管确认、且本地内容寻址证据 hash 重验通过的改动证据。
|
|
1524
|
+
* 缺失或漂移一律返回 null:投影显示「不可定位」,不退回执行者自述。
|
|
1525
|
+
*/
|
|
1526
|
+
const changeEvidenceFor = (taskId) => {
|
|
1527
|
+
const ref = readAcceptedChangeEvidence(readLatestReviewEvent(store, kingdomId, taskId));
|
|
1528
|
+
return ref ? resolveChangeEvidenceForDelivery(undefined, ref) : null;
|
|
1529
|
+
};
|
|
1530
|
+
const project = (item, acknowledgements) => {
|
|
1531
|
+
const view = deliveryAcknowledgementView(acknowledgements, item, ownerId);
|
|
1532
|
+
const change = item.content.change;
|
|
1533
|
+
return {
|
|
1534
|
+
itemId: item.itemId,
|
|
1535
|
+
contentHash: item.contentHash,
|
|
1536
|
+
label: boundedText(item.content.label),
|
|
1537
|
+
detail: boundedText(item.content.detail),
|
|
1538
|
+
change: {
|
|
1539
|
+
kind: change.kind,
|
|
1540
|
+
repoPath: change.repoPath,
|
|
1541
|
+
revision: change.revision,
|
|
1542
|
+
reasonCode: boundedText(change.reasonCode) || null,
|
|
1543
|
+
note: boundedText(change.note),
|
|
1544
|
+
// 只投影精确引用与固定标注;差异正文永远不进入工作台投影。
|
|
1545
|
+
evidenceId: change.evidenceId ?? null,
|
|
1546
|
+
entryId: change.entryId ?? null,
|
|
1547
|
+
evidenceLabel: change.kind === 'REPO_RELATIVE_VERIFIED' ? CHANGE_EVIDENCE_LABEL : null,
|
|
1548
|
+
coverageNote: boundedText(change.coverageNote) || null,
|
|
1549
|
+
},
|
|
1550
|
+
acknowledgement: { ...view, acknowledgedByOwnerId: view.acknowledgedByOwnerId ? publicId(view.acknowledgedByOwnerId) : null },
|
|
1551
|
+
// 问答只投影计数与接收主管可达性;问题与回复正文永不进入工作台或通用投影。
|
|
1552
|
+
questions: itemQuestions.get(item.itemId) ?? null,
|
|
1553
|
+
};
|
|
1554
|
+
};
|
|
1555
|
+
for (const task of tasks) {
|
|
1556
|
+
const claim = store.latestWorkerResult(task.task_id);
|
|
1557
|
+
if (!claim)
|
|
1558
|
+
continue;
|
|
1559
|
+
const review = readLatestReviewEvent(store, kingdomId, task.task_id);
|
|
1560
|
+
const classification = classifyAcceptedDelivery(store, claim, review);
|
|
1561
|
+
if (!classification)
|
|
1562
|
+
continue;
|
|
1563
|
+
const deliveryId = deliveryIdFor(task.task_id);
|
|
1564
|
+
const acknowledgements = readDeliveryAcknowledgements(store, kingdomId, deliveryId);
|
|
1565
|
+
const derived = deriveDeliveryItems(task.task_id, claim, changeEvidenceFor(task.task_id));
|
|
1566
|
+
const summary = derived.find(item => item.content.layer === 'SUMMARY');
|
|
1567
|
+
if (!summary)
|
|
1568
|
+
continue;
|
|
1569
|
+
const thread = readDeliveryQuestionThread(store, kingdomId, deliveryId);
|
|
1570
|
+
itemQuestions.clear();
|
|
1571
|
+
for (const item of derived) {
|
|
1572
|
+
const state = deliveryItemQuestionState(deliveryQuestionThreadForItem(thread, item.itemId, item.contentHash));
|
|
1573
|
+
if (state)
|
|
1574
|
+
itemQuestions.set(item.itemId, state);
|
|
1575
|
+
}
|
|
1576
|
+
// 摘要层的提问单独投影到 `summaryQuestions`;内部的 `questions` 置空,避免
|
|
1577
|
+
// 同一条提问在卡片层与 `deliveryQuestions` 汇总里被重复计数。
|
|
1578
|
+
const summaryQuestions = itemQuestions.get(summary.itemId) ?? null;
|
|
1579
|
+
itemQuestions.set(summary.itemId, null);
|
|
1580
|
+
layers[publicId(task.task_id)] = {
|
|
1581
|
+
deliveryId,
|
|
1582
|
+
attemptNo: claim.attempt_no,
|
|
1583
|
+
acceptanceEvidenceKind: classification.kind,
|
|
1584
|
+
acceptanceEvidenceNote: classification.legacyNote,
|
|
1585
|
+
summaryQuestions,
|
|
1586
|
+
summary: { ...project(summary, acknowledgements), questions: null, layer: 'SUMMARY' },
|
|
1587
|
+
evidence: derived.filter(item => item.content.layer === 'EVIDENCE').map(item => project(item, acknowledgements)),
|
|
1588
|
+
};
|
|
1589
|
+
}
|
|
1590
|
+
return layers;
|
|
1591
|
+
}
|
|
1592
|
+
/**
|
|
1593
|
+
* 条目提问元数据投影。
|
|
1594
|
+
*
|
|
1595
|
+
* 只保留计数、时间与最近一条问题的接收主管可达性;正文留在 Core 内,只有有效
|
|
1596
|
+
* Owner 窗口或当前责任主管的 session-bound Agent Tool 能读到。
|
|
1597
|
+
*/
|
|
1598
|
+
function deliveryItemQuestionState(thread) {
|
|
1599
|
+
if (!thread || !thread.questions.length)
|
|
1600
|
+
return null;
|
|
1601
|
+
const asked = thread.questions.map(question => question.askedAt).sort();
|
|
1602
|
+
const replied = thread.questions.filter(question => question.reply).map(question => question.reply.repliedAt).sort();
|
|
1603
|
+
const latest = thread.questions[thread.questions.length - 1];
|
|
1604
|
+
return {
|
|
1605
|
+
threadId: thread.deliveryId + ':' + thread.itemId,
|
|
1606
|
+
totalCount: thread.questions.length,
|
|
1607
|
+
pendingCount: thread.pendingCount,
|
|
1608
|
+
answeredCount: thread.answeredCount,
|
|
1609
|
+
historyCount: thread.historyCount,
|
|
1610
|
+
lastAskedAt: asked[asked.length - 1] ?? null,
|
|
1611
|
+
lastRepliedAt: replied.length ? replied[replied.length - 1] : null,
|
|
1612
|
+
latestReplyState: latest.replyState,
|
|
1613
|
+
latestItemVersion: latest.itemVersion,
|
|
1614
|
+
currentContentHash: thread.currentContentHash,
|
|
1615
|
+
};
|
|
1616
|
+
}
|
|
1497
1617
|
/** Canonical reads remain here; the workbench builder only receives public projection values. */
|
|
1498
1618
|
function buildWorkbenchCollaboration(store, kingdomId, present) {
|
|
1499
1619
|
const publicId = (id) => publicReferenceId(id) ?? 'redacted';
|
|
@@ -1559,6 +1679,8 @@ function buildWorkbenchData(store, kingdomId, tasks, security, costRuntime) {
|
|
|
1559
1679
|
governance.leases = governance.leases.map(lease => ({ ...lease, leaseId: publicId(lease.leaseId), taskId: publicId(lease.taskId) }));
|
|
1560
1680
|
return buildPersonalWorkbench({
|
|
1561
1681
|
kingdomPresent: present,
|
|
1682
|
+
// 只投影可与 Owner 授权输入比较的同一 ID;非法形式返回 null,工作台不生成兜底范围。
|
|
1683
|
+
kingdomId: present ? publicReferenceId(kingdomId) : null,
|
|
1562
1684
|
bindings: bindings.map(binding => ({ bindingId: publicId(binding.binding_id), roleType: boundedText(binding.role_type), roleName: boundedText(binding.role_name),
|
|
1563
1685
|
status: boundedText(binding.status), sessionBound: binding.session_id !== null })),
|
|
1564
1686
|
territories: territories.map(territory => ({ territoryId: publicId(territory.territory_id), name: boundedText(territory.name), status: boundedText(territory.status),
|
|
@@ -1569,6 +1691,7 @@ function buildWorkbenchData(store, kingdomId, tasks, security, costRuntime) {
|
|
|
1569
1691
|
governance,
|
|
1570
1692
|
cost: buildWorkbenchCost(store, kingdomId, present, costRuntime),
|
|
1571
1693
|
collaboration: buildWorkbenchCollaboration(store, kingdomId, present),
|
|
1694
|
+
deliveryLayers: buildWorkbenchDeliveryLayers(store, kingdomId, tasks, present),
|
|
1572
1695
|
});
|
|
1573
1696
|
}
|
|
1574
1697
|
/** S1 顶层只读 Projection;没有 kingdom 时显式返回 UNKNOWN/空数据。 */
|