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/index.js
CHANGED
|
@@ -14,23 +14,78 @@ import { observeDshDispatchUsage } from './adapter/dsh-usage.js';
|
|
|
14
14
|
import { ToolDisclosureRuntime, DISCOVERY_TOOL_NAME } from './capability/tool-disclosure.js';
|
|
15
15
|
import { installRuntimeCostObservers } from './runtime-cost-observer.js';
|
|
16
16
|
import { setCapabilityCeiling } from './capability/admin.js';
|
|
17
|
-
import { bindRole, listBindings, parseExecutionProfile, rebindSession, resolveBinding, setExecutionProfile, unbindRole, } from './core/binding.js';
|
|
17
|
+
import { bindRole, bindSupervisorForTerritory, listBindings, parseExecutionProfile, rebindSession, resolveBinding, setExecutionProfile, unbindRole, } from './core/binding.js';
|
|
18
18
|
import { createRunnerContextPort } from './core/governed.js';
|
|
19
19
|
import { forgetRunnerContextPort, getRunnerContextPort, revokeRunnerContextBrokerContext, } from './runner-context-broker.js';
|
|
20
20
|
import { asExecutionState, isTerminalExecutionState } from './core/execution.js';
|
|
21
21
|
import { createTerritory, deleteTerritory, listTerritories, setTerritorySupervisor } from './core/territory.js';
|
|
22
|
-
import { abortExecution, assignTask, listTasks, pauseExecution, planTask, reclaimOrphanExecutions, resolveGovernedStartSupervisor, resumeExecution, reviewTask, startTask, } from './core/task-service.js';
|
|
22
|
+
import { abortExecution, assignTask, describeTaskChangeCandidates, listTasks, pauseExecution, planTask, reclaimOrphanExecutions, resolveGovernedStartSupervisor, resumeExecution, reviewTask, startTask, } from './core/task-service.js';
|
|
23
|
+
import { DELIVERY_CHANGE_SNAPSHOT_EVENT_TYPE, changeSnapshotEventId, changeSnapshotEventPayload, } from './core/delivery-change.js';
|
|
24
|
+
import { DELIVERY_QUESTION_UNCLAIMED, readDeliveryQuestionInboxForSession, replyToDeliveryQuestion, } from './core/delivery-ack.js';
|
|
23
25
|
import { buildSnapshot, buildTaskDetail, toEventView } from './gui/snapshot.js';
|
|
24
26
|
import { startGuiServer } from './gui/server.js';
|
|
25
27
|
import { DuplicateJsonKeyError, GUI_OWNER_ONLY_HTTP_COMMANDS, GUI_SESSION_COMMANDS, GUI_START_SANDBOX_MODES, parseStrictJsonObject, } from './gui/control-contract.js';
|
|
26
28
|
import { LOCAL_CONTROL_LAUNCH_PATH, LocalControlManager } from './gui/local-control.js';
|
|
27
|
-
import { OwnerLocalControlManager } from './gui/owner-control.js';
|
|
29
|
+
import { OwnerLocalControlManager, validateLaunchAckHint } from './gui/owner-control.js';
|
|
28
30
|
import { OwnerDecisionController } from './core/owner-window.js';
|
|
29
31
|
import { resolveWorkerExecution } from './worker/executor-factory.js';
|
|
30
32
|
import { issueOwnerControlCapability, ownerControlAuth, } from './core/owner-control.js';
|
|
31
33
|
import { draftOwnerBindingIntent, draftOwnerBindingIntentFromRejectedWrite, serializeOwnerBindingIntentDraft, } from './core/owner-binding-intent.js';
|
|
32
34
|
export const name = 'dsh-kingdom';
|
|
33
35
|
export const inject = ['tools', 'commands'];
|
|
36
|
+
/**
|
|
37
|
+
* 解析 direct `/kingdom role.bind|role.session` 的**目标**会话。
|
|
38
|
+
*
|
|
39
|
+
* live Agent 优先;**只有** ABSENT(注册表里没有这条 live Agent)才回退到持久会话
|
|
40
|
+
* 存在性证明。FOREIGN / MULTIPLE / EXPIRED / UNKNOWN 表示身份不一致或无法判定,
|
|
41
|
+
* 一律保持 fail-closed,绝不因为“可能是它”而放行。
|
|
42
|
+
*/
|
|
43
|
+
export async function resolveDirectTargetSession(sessionId, seam, probe) {
|
|
44
|
+
if (!exactRuntimeToken(sessionId)) {
|
|
45
|
+
return { ok: false, classification: 'ABSENT', reason: 'session_id 必须是非空 exact token。' };
|
|
46
|
+
}
|
|
47
|
+
const live = validateLiveDirectSession(sessionId, seam);
|
|
48
|
+
if (live.ok) {
|
|
49
|
+
return {
|
|
50
|
+
ok: true,
|
|
51
|
+
classification: 'LIVE',
|
|
52
|
+
sessionId: live.sessionId,
|
|
53
|
+
agent: live.agent,
|
|
54
|
+
session: live.session,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (live.classification !== 'ABSENT') {
|
|
58
|
+
return { ok: false, classification: live.classification, reason: live.reason };
|
|
59
|
+
}
|
|
60
|
+
if (probe === null) {
|
|
61
|
+
return {
|
|
62
|
+
ok: false,
|
|
63
|
+
classification: 'ABSENT',
|
|
64
|
+
reason: `${live.reason};宿主未提供持久会话观察通道,无法证明该会话属于本机 DSH。`,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
const durable = await probe.observe(sessionId);
|
|
68
|
+
if (durable.ok) {
|
|
69
|
+
return {
|
|
70
|
+
ok: true,
|
|
71
|
+
classification: 'REGISTERED_UNSTARTED',
|
|
72
|
+
sessionId,
|
|
73
|
+
detail: '该会话已在本机 DSH 持久会话存储中登记,但当前没有 live Agent;席位写入后,该会话真正启动前不能执行。',
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
if (durable.notFound) {
|
|
77
|
+
return {
|
|
78
|
+
ok: false,
|
|
79
|
+
classification: 'ABSENT',
|
|
80
|
+
reason: `${live.reason};本机持久会话存储中也没有该 session_id。`,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
ok: false,
|
|
85
|
+
classification: 'UNKNOWN',
|
|
86
|
+
reason: `${live.reason};持久会话存在性无法判定:${durable.reason}`,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
34
89
|
function exactRuntimeToken(value) {
|
|
35
90
|
return typeof value === 'string'
|
|
36
91
|
&& value.length > 0
|
|
@@ -240,16 +295,32 @@ export function ensureGuiSetupTerritory(store, input, auth) {
|
|
|
240
295
|
throw new Error('错误:setup.basic 创建后找不到 Territory。');
|
|
241
296
|
return created;
|
|
242
297
|
}
|
|
243
|
-
/**
|
|
298
|
+
/**
|
|
299
|
+
* Ensure setup role identity without ever binding a Worker to the activator.
|
|
300
|
+
*
|
|
301
|
+
* v3.2.0:主管席位必须隶属一个领地,因此这里的 SUPERVISOR 分支走**原子通道**
|
|
302
|
+
* (`bindSupervisorForTerritory`:建席位 + 设主理同一事务),并要求显式 `territoryId`。
|
|
303
|
+
* 缺少领地时由该通道给出明确拒绝,不会先建出一个没有领地的主管席位。
|
|
304
|
+
* 本助手当前只有测试调用(产品入口是 direct Slash / Owner 管理窗口),但它是包的公开导出,
|
|
305
|
+
* 所以同样不放宽不变量。
|
|
306
|
+
*/
|
|
244
307
|
export function ensureGuiSetupBinding(store, input, auth) {
|
|
245
308
|
const existing = store.getBindingByRole(input.kingdomId, input.roleType);
|
|
246
309
|
if (!existing) {
|
|
247
|
-
const result =
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
310
|
+
const result = input.roleType === 'SUPERVISOR'
|
|
311
|
+
? bindSupervisorForTerritory(store, {
|
|
312
|
+
kingdomId: input.kingdomId,
|
|
313
|
+
roleType: input.roleType,
|
|
314
|
+
roleName: input.roleName,
|
|
315
|
+
sessionId: input.sessionId,
|
|
316
|
+
territoryId: input.territoryId ?? '',
|
|
317
|
+
}, auth)
|
|
318
|
+
: bindRole(store, {
|
|
319
|
+
kingdomId: input.kingdomId,
|
|
320
|
+
roleType: input.roleType,
|
|
321
|
+
roleName: input.roleName,
|
|
322
|
+
sessionId: input.sessionId,
|
|
323
|
+
}, auth);
|
|
253
324
|
throwOnGuiSetupFailure(result);
|
|
254
325
|
const created = store.getBindingByRole(input.kingdomId, input.roleType);
|
|
255
326
|
if (!created)
|
|
@@ -348,10 +419,92 @@ export function apply(ctx, config, dependencies = {}) {
|
|
|
348
419
|
+ 'GUI 若提供派发/复核/返工按钮,必须显著标注为「本地可信演示权限」。',
|
|
349
420
|
};
|
|
350
421
|
const commandContext = (kingdomId, principal) => ({ kingdomId, auth: authView, ...principal ? { principal } : {} });
|
|
422
|
+
/**
|
|
423
|
+
* 改动证据专用上下文:只对**显式改动选择**与候选查看入口强制 session-bound。
|
|
424
|
+
*
|
|
425
|
+
* 默认产品配置是 `declarative`,此时 `commandContext` 只确认存在角色绑定;改动
|
|
426
|
+
* 证据要求「真实、当前、同领地 Supervisor session」,若继续用弱鉴权,任何本地
|
|
427
|
+
* 调用者都能签发「主管确认的改动证据」。这里只把这两个入口升级为 session-bound
|
|
428
|
+
* (身份仍由既有 `resolveTaskSupervisor` 在 Core 内核对),不顺手修改全局 P0-D
|
|
429
|
+
* 鉴权策略,普通 review/ACCEPT 语义完全不变。取不到 ACTIVE session 时不放行。
|
|
430
|
+
*/
|
|
431
|
+
const sessionBoundAuthView = {
|
|
432
|
+
mode: 'session-bound',
|
|
433
|
+
trustLevel: 'session-verified',
|
|
434
|
+
note: '命令调用方的 session 必须与角色 binding 的 session_id 一致。',
|
|
435
|
+
};
|
|
436
|
+
const changeEvidenceContext = (kingdomId, exec) => {
|
|
437
|
+
const principal = trustedToolPrincipal(exec);
|
|
438
|
+
return { kingdomId, auth: sessionBoundAuthView, ...principal ? { principal } : {} };
|
|
439
|
+
};
|
|
440
|
+
/**
|
|
441
|
+
* 交付问答专用上下文:与改动证据同款,**默认 declarative 配置下也必须**由真实、
|
|
442
|
+
* 当前、可信的 ACTIVE session 证明主管身份,再由 Core 核对它是否正是该交付的接收者。
|
|
443
|
+
*
|
|
444
|
+
* 绝不从 caller 自报的 session_id 授权:`trustedToolPrincipal` 只接受 DSH Runtime
|
|
445
|
+
* 注册表可核对的 ACTIVE session;取不到时不放行(Core 侧 fail-closed)。
|
|
446
|
+
*/
|
|
447
|
+
const deliveryQuestionContext = (kingdomId, exec) => {
|
|
448
|
+
const principal = trustedToolPrincipal(exec);
|
|
449
|
+
return { kingdomId, auth: sessionBoundAuthView, ...principal ? { principal } : {} };
|
|
450
|
+
};
|
|
351
451
|
const dshRegistrySeam = () => ({
|
|
352
452
|
agents: ctx.get('agents'),
|
|
353
453
|
sessions: ctx.get('sessions'),
|
|
354
454
|
});
|
|
455
|
+
/**
|
|
456
|
+
* v3.1:持久会话存在性证明。DSH 未挂载 `sessionQuery` 时返回 null,
|
|
457
|
+
* 此时目标会话解析退化为原来的 live Agent 单一路径(fail-closed 不变)。
|
|
458
|
+
*/
|
|
459
|
+
const durableSessionProbe = () => {
|
|
460
|
+
const query = ctx.get('sessionQuery');
|
|
461
|
+
if (!query || typeof query.observeSession !== 'function')
|
|
462
|
+
return null;
|
|
463
|
+
const observeSession = query.observeSession.bind(query);
|
|
464
|
+
return {
|
|
465
|
+
observe: async (sessionId) => {
|
|
466
|
+
let observation;
|
|
467
|
+
try {
|
|
468
|
+
observation = await observeSession(sessionId, { projectionMode: 'none' });
|
|
469
|
+
}
|
|
470
|
+
catch (error) {
|
|
471
|
+
const code = error?.code;
|
|
472
|
+
if (code === 'SESSION_QUERY_SESSION_NOT_FOUND') {
|
|
473
|
+
return { ok: false, notFound: true, reason: '持久会话存储中没有该 session_id' };
|
|
474
|
+
}
|
|
475
|
+
return {
|
|
476
|
+
ok: false,
|
|
477
|
+
notFound: false,
|
|
478
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
try {
|
|
482
|
+
const header = observation?.header;
|
|
483
|
+
const headerId = header?.id;
|
|
484
|
+
if (typeof headerId !== 'string' || headerId !== sessionId) {
|
|
485
|
+
return {
|
|
486
|
+
ok: false,
|
|
487
|
+
notFound: false,
|
|
488
|
+
reason: 'observation header.id 与请求的 session_id 不一致',
|
|
489
|
+
};
|
|
490
|
+
}
|
|
491
|
+
return { ok: true };
|
|
492
|
+
}
|
|
493
|
+
finally {
|
|
494
|
+
const lease = observation;
|
|
495
|
+
const dispose = lease?.[Symbol.dispose];
|
|
496
|
+
if (typeof dispose === 'function') {
|
|
497
|
+
try {
|
|
498
|
+
dispose.call(observation);
|
|
499
|
+
}
|
|
500
|
+
catch {
|
|
501
|
+
// 观察租约释放失败不影响本次存在性判定
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
},
|
|
506
|
+
};
|
|
507
|
+
};
|
|
355
508
|
const trustedToolPrincipal = (exec) => {
|
|
356
509
|
const resolved = resolveTrustedToolSession(exec, dshRegistrySeam());
|
|
357
510
|
return resolved.classification === 'ACTIVE' ? { sessionId: resolved.sessionId } : undefined;
|
|
@@ -572,7 +725,12 @@ export function apply(ctx, config, dependencies = {}) {
|
|
|
572
725
|
try {
|
|
573
726
|
result = await runGovernedTask({
|
|
574
727
|
store, adapter, kingdomId, workerBindingId: worker,
|
|
728
|
+
// R4 范围回退:Runtime 输入保持既有行为——无 workspace 的旧 Territory 仍以
|
|
729
|
+
// `process.cwd()` 作为 createSession 的 cwd,执行/错误路径不变。
|
|
575
730
|
territoryId: loadedTask.territory_id, cwd: territory.workspace_path ?? process.cwd(),
|
|
731
|
+
// 差异采集**不复用**该回退值:单独传入 Territory 的 canonical workspace;
|
|
732
|
+
// 缺失时采集端如实 `NOT_SUPPORTED`,绝不把插件安装目录当成领地工作区。
|
|
733
|
+
changeWorkspacePath: territory.workspace_path ?? null,
|
|
576
734
|
taskId: loadedTask.task_id, attemptNo, supervisorBindingId: supervisor.binding.binding_id,
|
|
577
735
|
grant: parsedGrant.grant, requirementJson: store.getTaskCapabilityRequirement(loadedTask.task_id),
|
|
578
736
|
sandboxMode,
|
|
@@ -656,6 +814,24 @@ export function apply(ctx, config, dependencies = {}) {
|
|
|
656
814
|
const outcome = result.terminalOutcome;
|
|
657
815
|
const summary = result.summary;
|
|
658
816
|
submitGovernedClaim(store, result.dispatchId, summary);
|
|
817
|
+
// 改动快照事件:只记 task/attempt/result、manifest/hash 与有界元数据。
|
|
818
|
+
// 仓库正文留在专用本地证据目录;这里不写任何正文。
|
|
819
|
+
if (result.changeManifest) {
|
|
820
|
+
const claimRow = store.latestWorkerResult(loadedTask.task_id);
|
|
821
|
+
if (claimRow && claimRow.attempt_no === attemptNo && !store.getEventById(changeSnapshotEventId(kingdomId, loadedTask.task_id, attemptNo, result.executionId))) {
|
|
822
|
+
store.appendEvent({
|
|
823
|
+
event_id: changeSnapshotEventId(kingdomId, loadedTask.task_id, attemptNo, result.executionId),
|
|
824
|
+
kingdom_id: kingdomId,
|
|
825
|
+
event_type: DELIVERY_CHANGE_SNAPSHOT_EVENT_TYPE,
|
|
826
|
+
actor_role: 'SUPERVISOR',
|
|
827
|
+
actor_id: supervisor.binding.binding_id,
|
|
828
|
+
target_type: 'task',
|
|
829
|
+
target_id: loadedTask.task_id,
|
|
830
|
+
payload_json: JSON.stringify(changeSnapshotEventPayload(result.changeManifest, claimRow.result_id)),
|
|
831
|
+
created_at: new Date().toISOString(),
|
|
832
|
+
});
|
|
833
|
+
}
|
|
834
|
+
}
|
|
659
835
|
await collectDispatchUsage(result.dispatchId);
|
|
660
836
|
const recoveryNotice = settledLease?.state === 'RECOVERING'
|
|
661
837
|
? result.cleanupReceipt.status === 'CONFIRMED'
|
|
@@ -1176,6 +1352,8 @@ export function apply(ctx, config, dependencies = {}) {
|
|
|
1176
1352
|
decision: { type: 'string', required: true, description: 'ACCEPT / REWORK / FAIL / HANDOFF' },
|
|
1177
1353
|
reason: { type: 'string', description: '裁定理由;REWORK/FAIL/HANDOFF 必填' },
|
|
1178
1354
|
to_binding_id: { type: 'string', description: 'HANDOFF 专用:目标 Worker binding id(ACTIVE)' },
|
|
1355
|
+
change_evidence_id: { type: 'string', description: 'ACCEPT 专用:主管显式选择改动证据时的 evidence id(先用 kingdom_delivery_changes 查看候选)' },
|
|
1356
|
+
change_entry_ids: { type: 'array', items: { type: 'string' }, description: 'ACCEPT 专用:显式选择属于本次交付的改动条目 id;与 change_evidence_id 同时给出,普通 ACCEPT 不自动确认全部差异' },
|
|
1179
1357
|
},
|
|
1180
1358
|
output: {
|
|
1181
1359
|
schema: { type: 'string' },
|
|
@@ -1185,14 +1363,127 @@ export function apply(ctx, config, dependencies = {}) {
|
|
|
1185
1363
|
const kingdomId = requireKingdom();
|
|
1186
1364
|
if (!kingdomId)
|
|
1187
1365
|
return '尚未初始化王国。请先 /kingdom init。';
|
|
1188
|
-
|
|
1366
|
+
if (args.change_entry_ids !== undefined && (!Array.isArray(args.change_entry_ids) || args.change_entry_ids.some(id => typeof id !== 'string'))) {
|
|
1367
|
+
return 'INPUT_DENIED [CHANGE_SELECTION_INVALID]: change_entry_ids 必须是字符串数组。';
|
|
1368
|
+
}
|
|
1369
|
+
// 只有显式改动选择才切换到 session-bound 上下文;普通 ACCEPT/REWORK/FAIL/HANDOFF
|
|
1370
|
+
// 继续使用既有(可能 declarative)上下文,语义不变。
|
|
1371
|
+
const selectingChange = args.change_evidence_id !== undefined || args.change_entry_ids !== undefined;
|
|
1372
|
+
const context = selectingChange
|
|
1373
|
+
? changeEvidenceContext(kingdomId, exec)
|
|
1374
|
+
: commandContext(kingdomId, trustedToolPrincipal(exec));
|
|
1375
|
+
return reviewTask(store, context, {
|
|
1189
1376
|
taskId: args.task_id,
|
|
1190
1377
|
decision: args.decision,
|
|
1191
1378
|
reason: args.reason,
|
|
1192
1379
|
to_binding_id: args.to_binding_id,
|
|
1380
|
+
change_evidence_id: args.change_evidence_id,
|
|
1381
|
+
change_entry_ids: args.change_entry_ids,
|
|
1193
1382
|
}).message;
|
|
1194
1383
|
},
|
|
1195
1384
|
})), 'dsh-kingdom: review-task tool');
|
|
1385
|
+
ctx.effect(() => ctx.tools.register(defineTool({
|
|
1386
|
+
name: 'kingdom_delivery_changes',
|
|
1387
|
+
description: '只读列出当前 Task/attempt 的有界改动候选(真实 session-bound 主管专用):改动证据来自执行前后有界快照与主管确认,不证明 Git 作者身份。只读,不写任何事实,也不能替代 ACCEPT。',
|
|
1388
|
+
parameters: {
|
|
1389
|
+
task_id: { type: 'string', required: true, description: '当前主管管辖的任务 id' },
|
|
1390
|
+
},
|
|
1391
|
+
output: {
|
|
1392
|
+
schema: { type: 'string' },
|
|
1393
|
+
render: (_args, value) => [{ type: 'text', text: String(value) }],
|
|
1394
|
+
},
|
|
1395
|
+
async execute(args, exec) {
|
|
1396
|
+
const kingdomId = requireKingdom();
|
|
1397
|
+
if (!kingdomId)
|
|
1398
|
+
return '尚未初始化王国。请先 /kingdom init。';
|
|
1399
|
+
const result = describeTaskChangeCandidates(store, changeEvidenceContext(kingdomId, exec), args.task_id);
|
|
1400
|
+
return result.ok ? result.text : result.message;
|
|
1401
|
+
},
|
|
1402
|
+
})), 'dsh-kingdom: delivery change candidates tool');
|
|
1403
|
+
ctx.effect(() => ctx.tools.register(defineTool({
|
|
1404
|
+
name: 'kingdom_delivery_questions',
|
|
1405
|
+
description: '读取**只属于当前 session-bound 主管本人**的交付条目提问(含问题正文与已有回复):按权威 events 账本逐条精确读取,不是有界的最近事件投影。只接受真实、当前、可信的 ACTIVE 主管 session;默认 declarative 配置下同样拒绝。只读,不写任何事实;未回复的问题只表示「待领取」,不代表已通知或已阅读。',
|
|
1406
|
+
parameters: {
|
|
1407
|
+
pending_only: { type: 'boolean', description: 'true 时只列出尚无回复的问题;省略/false 列出全部(含已回复)' },
|
|
1408
|
+
},
|
|
1409
|
+
output: {
|
|
1410
|
+
schema: { type: 'string' },
|
|
1411
|
+
render: (_args, value) => [{ type: 'text', text: String(value) }],
|
|
1412
|
+
},
|
|
1413
|
+
async execute(args, exec) {
|
|
1414
|
+
const kingdomId = requireKingdom();
|
|
1415
|
+
if (!kingdomId)
|
|
1416
|
+
return '尚未初始化王国。请先 /kingdom init。';
|
|
1417
|
+
const principal = trustedToolPrincipal(exec);
|
|
1418
|
+
if (!principal)
|
|
1419
|
+
return 'AUTHZ_DENIED [UNAUTHORIZED_PRINCIPAL]: 无法从 DSH Runtime 证明当前调用方的唯一活动 session。';
|
|
1420
|
+
// 一个 session 可以合法持有多个 ACTIVE SUPERVISOR binding:必须枚举并精确核对
|
|
1421
|
+
// **全部**该 session 可证明的 binding,不能只取第一个而静默遗漏其余绑定的问题。
|
|
1422
|
+
const inbox = readDeliveryQuestionInboxForSession(store, kingdomId, principal.sessionId);
|
|
1423
|
+
if (!inbox.bindings.length)
|
|
1424
|
+
return 'AUTHZ_DENIED [UNAUTHORIZED_PRINCIPAL]: 当前 session 没有匹配的 ACTIVE SUPERVISOR binding。';
|
|
1425
|
+
// 每条问题事实只有一个接收主管,逐 binding 读取再合并因此不会重复;每个 binding
|
|
1426
|
+
// 仍只看到写给自己的问题。旧内容版本与无法重验的问题仍列出,但明确标出:它们不计
|
|
1427
|
+
// 当前待办,也不能被说成「当前可回复」。
|
|
1428
|
+
const entries = inbox.entries.filter(entry => args.pending_only === true
|
|
1429
|
+
? entry.question.reply === null && entry.question.itemVersion === 'CURRENT' : true);
|
|
1430
|
+
const bindingLabel = inbox.bindings.map(candidate => `${candidate.role_name}(${candidate.binding_id})`).join('、');
|
|
1431
|
+
if (!entries.length) {
|
|
1432
|
+
return args.pending_only === true
|
|
1433
|
+
? `主管 ${bindingLabel} 当前没有待回复的交付条目提问。`
|
|
1434
|
+
: `主管 ${bindingLabel} 当前没有被提问的交付条目。`;
|
|
1435
|
+
}
|
|
1436
|
+
const lines = [
|
|
1437
|
+
`主管 ${bindingLabel} 的交付条目提问共 ${entries.length} 条${args.pending_only === true ? `(仅当前版待回复)` : ''}。`,
|
|
1438
|
+
'这些正文只由此 session-bound Tool 返回,不进入工作台、快照或通用事件投影。',
|
|
1439
|
+
'回复请用 kingdom_delivery_reply(question_id, reply_text);回复只记一条对话事实,不写知悉、Task/Claim、ACCEPT、Owner acceptance 或发布。',
|
|
1440
|
+
];
|
|
1441
|
+
for (const entry of entries) {
|
|
1442
|
+
const question = entry.question;
|
|
1443
|
+
const versionLabel = question.itemVersion === 'CURRENT' ? '当前版'
|
|
1444
|
+
: question.itemVersion === 'HISTORICAL' ? '历史版(旧内容版本;不计当前待办,也不能回复)'
|
|
1445
|
+
: '无法重验版本(当前 ACCEPT/证据无法重验该条目;不计当前待办,也不能回复)';
|
|
1446
|
+
lines.push('', `- 问题 ${question.questionId} · ${question.reply ? '已回复' : '待领取'} · ${versionLabel} · ${question.itemLabel || question.itemId}`);
|
|
1447
|
+
lines.push(` 任务:${entry.taskTitle}(${question.taskId})· 领地:${entry.territoryName} · 已接受尝试第 ${question.attemptNo} 次`);
|
|
1448
|
+
lines.push(` 条目:${question.itemId} · 问题内容版本 ${question.contentHash}${question.currentItemContentHash ? ` · 当前内容版本 ${question.currentItemContentHash}` : ''}`);
|
|
1449
|
+
lines.push(` 提问时间:${question.askedAt} · 接收主管:${question.reviewerBindingId}`);
|
|
1450
|
+
lines.push(` 问题:${question.questionText}`);
|
|
1451
|
+
if (question.reply)
|
|
1452
|
+
lines.push(` 回复(${question.reply.repliedAt}):${question.reply.replyText}`);
|
|
1453
|
+
else if (question.itemVersion === 'HISTORICAL')
|
|
1454
|
+
lines.push(' 状态:该问题属于旧内容版本,仅留历史,不能回复。');
|
|
1455
|
+
else if (question.itemVersion === 'UNVERIFIABLE')
|
|
1456
|
+
lines.push(' 状态:当前无法重验该条目的内容版本(例如主管确认的改动证据已丢失或被替换),不能作为当前待办,也不能回复。问题正文仍可读。');
|
|
1457
|
+
else
|
|
1458
|
+
lines.push(` 状态:${DELIVERY_QUESTION_UNCLAIMED};${question.replyState === 'REPLY_ACCESSIBLE' ? '该主管当前仍可回复。' : '该问题当前不可达:' + question.replyState + ',不会改投继任主管。'}`);
|
|
1459
|
+
}
|
|
1460
|
+
return lines.join('\n');
|
|
1461
|
+
},
|
|
1462
|
+
})), 'dsh-kingdom: delivery questions inbox tool');
|
|
1463
|
+
ctx.effect(() => ctx.tools.register(defineTool({
|
|
1464
|
+
name: 'kingdom_delivery_reply',
|
|
1465
|
+
description: '以当前 session-bound 主管身份回复一条写给他的交付条目提问(被提问的只能是接受该交付的主管本人)。一问最多一个当前回复:同一文本重试幂等,冲突回复被拒绝。只写一条对话事实,不写知悉、Task/Claim、主管 ACCEPT、Owner acceptance 或发布,也不自动派发或唤醒。',
|
|
1466
|
+
parameters: {
|
|
1467
|
+
question_id: { type: 'string', required: true, description: '先用 kingdom_delivery_questions 读取到的 question id' },
|
|
1468
|
+
reply_text: { type: 'string', required: true, description: '给 Owner 的回复正文;非空且长度受限,写入前统一脱敏' },
|
|
1469
|
+
},
|
|
1470
|
+
output: {
|
|
1471
|
+
schema: { type: 'string' },
|
|
1472
|
+
render: (_args, value) => [{ type: 'text', text: String(value) }],
|
|
1473
|
+
},
|
|
1474
|
+
async execute(args, exec) {
|
|
1475
|
+
const kingdomId = requireKingdom();
|
|
1476
|
+
if (!kingdomId)
|
|
1477
|
+
return '尚未初始化王国。请先 /kingdom init。';
|
|
1478
|
+
// CHANGE 条目的回复不再在调用边界预先解析证据:Core 会在同一个写锁事务内按
|
|
1479
|
+
// 当前 ACCEPT 引用重新读取并重验 exact evidence/item/hash,证据漂移即 fail-closed。
|
|
1480
|
+
const result = replyToDeliveryQuestion(store, deliveryQuestionContext(kingdomId, exec), {
|
|
1481
|
+
questionId: args.question_id,
|
|
1482
|
+
replyText: args.reply_text,
|
|
1483
|
+
});
|
|
1484
|
+
return result.ok ? result.text : `${result.code}: ${result.message}`;
|
|
1485
|
+
},
|
|
1486
|
+
})), 'dsh-kingdom: delivery question reply tool');
|
|
1196
1487
|
ctx.effect(() => ctx.tools.register(defineTool({
|
|
1197
1488
|
name: 'kingdom_list_tasks',
|
|
1198
1489
|
description: '列出王国任务的真实状态(含尝试次数与最新 Worker Claim 摘要),可按领地/状态过滤',
|
|
@@ -1329,13 +1620,24 @@ export function apply(ctx, config, dependencies = {}) {
|
|
|
1329
1620
|
validateTargetSession: async ({ sessionId, signal }) => {
|
|
1330
1621
|
if (signal.aborted)
|
|
1331
1622
|
return { ok: false, code: 'OWNER_VALIDATION_ABORTED', message: '管理窗口已撤销或校验超时。' };
|
|
1332
|
-
const
|
|
1333
|
-
|
|
1623
|
+
const resolved = await resolveDirectTargetSession(sessionId, dshRegistrySeam(), durableSessionProbe());
|
|
1624
|
+
if (resolved.ok)
|
|
1625
|
+
return { ok: true };
|
|
1626
|
+
return {
|
|
1627
|
+
ok: false,
|
|
1628
|
+
code: 'OWNER_SESSION_UNAVAILABLE',
|
|
1629
|
+
message: resolved.classification === 'ABSENT'
|
|
1630
|
+
? '目标会话既不是当前有效的 DSH 会话,也不在本机持久会话存储中;请刷新后重新选择。'
|
|
1631
|
+
: '目标会话不是当前唯一、有效的 DSH 会话;请刷新后重新选择。',
|
|
1632
|
+
};
|
|
1334
1633
|
},
|
|
1335
1634
|
listTargetSessions: async ({ sessionIds, signal }) => {
|
|
1635
|
+
if (signal.aborted)
|
|
1636
|
+
return [];
|
|
1336
1637
|
const seam = dshRegistrySeam();
|
|
1337
|
-
|
|
1338
|
-
|
|
1638
|
+
const probe = durableSessionProbe();
|
|
1639
|
+
const resolved = await Promise.all(sessionIds.map(async (id) => ({ id, ok: (await resolveDirectTargetSession(id, seam, probe)).ok })));
|
|
1640
|
+
return resolved.filter(item => item.ok).map(item => ({ id: item.id, label: `DSH 会话 ${item.id}` }));
|
|
1339
1641
|
},
|
|
1340
1642
|
validateExecutionProfile: async ({ profile, signal }) => {
|
|
1341
1643
|
const provider = profile?.provider?.trim() || config.workerProvider;
|
|
@@ -1670,16 +1972,26 @@ export function apply(ctx, config, dependencies = {}) {
|
|
|
1670
1972
|
return `UNKNOWN/RECOVERY_REQUIRED: Owner operation ${operation} 的写结果不确定;请先 /kingdom status 对账。(${detail})`;
|
|
1671
1973
|
}
|
|
1672
1974
|
};
|
|
1673
|
-
|
|
1975
|
+
/**
|
|
1976
|
+
* direct Owner 目标会话校验(v3.1:异步,返回值携带证明强度)。
|
|
1977
|
+
*
|
|
1978
|
+
* `evidence` 只描述“写入当时怎么证明这条会话属于本机 DSH”,会记进事件 payload;
|
|
1979
|
+
* 它既不是 Role Authority,也不承诺该会话将来可执行。
|
|
1980
|
+
*/
|
|
1981
|
+
const validateDirectSessionPayload = async (operation, value) => {
|
|
1674
1982
|
if (value === undefined || value === null)
|
|
1675
|
-
return null;
|
|
1676
|
-
if (typeof value !== 'string')
|
|
1677
|
-
return `INPUT_DENIED [${operation}]: session_id 必须是 string 或 null
|
|
1678
|
-
|
|
1679
|
-
const
|
|
1680
|
-
if (
|
|
1681
|
-
return
|
|
1682
|
-
|
|
1983
|
+
return { ok: true, evidence: null };
|
|
1984
|
+
if (typeof value !== 'string') {
|
|
1985
|
+
return { ok: false, message: `INPUT_DENIED [${operation}]: session_id 必须是 string 或 null。` };
|
|
1986
|
+
}
|
|
1987
|
+
const resolved = await resolveDirectTargetSession(value, dshRegistrySeam(), durableSessionProbe());
|
|
1988
|
+
if (!resolved.ok) {
|
|
1989
|
+
return {
|
|
1990
|
+
ok: false,
|
|
1991
|
+
message: `INPUT_DENIED [SESSION_${resolved.classification}]: ${resolved.reason};direct /kingdom ${operation} 未写入。`,
|
|
1992
|
+
};
|
|
1993
|
+
}
|
|
1994
|
+
return { ok: true, evidence: resolved.classification === 'LIVE' ? 'LIVE_AGENT' : 'DURABLE_SESSION' };
|
|
1683
1995
|
};
|
|
1684
1996
|
const ownerAuth = () => ownerControlAuth(issueOwnerControlCapability());
|
|
1685
1997
|
ctx.effect(() => ctx.commands.register({
|
|
@@ -1691,12 +2003,27 @@ export function apply(ctx, config, dependencies = {}) {
|
|
|
1691
2003
|
const auth = ownerAuth();
|
|
1692
2004
|
switch (sub) {
|
|
1693
2005
|
case 'owner.gui': {
|
|
1694
|
-
|
|
2006
|
+
// taskHint/itemHint/contentHashHint 只用于在新标签页预选人类刚点的那一条;
|
|
2007
|
+
// 它们不进入 OwnerDecisionInput,也不改变授权含义。
|
|
2008
|
+
const parsed = parseJsonEnvelope(rest, ['kingdomId', 'actions', 'scope', 'ttlMs',
|
|
2009
|
+
'taskHint', 'itemHint', 'contentHashHint', 'hintAction']);
|
|
1695
2010
|
if (!parsed.ok)
|
|
1696
2011
|
return { kind: 'error', text: `INPUT_DENIED [OWNER_COMMAND_GRAMMAR]: ${parsed.message}` };
|
|
2012
|
+
const envelope = parsed.value;
|
|
2013
|
+
for (const key of ['taskHint', 'itemHint', 'contentHashHint', 'hintAction']) {
|
|
2014
|
+
if (envelope[key] !== undefined && typeof envelope[key] !== 'string') {
|
|
2015
|
+
return { kind: 'error', text: `INPUT_DENIED [OWNER_COMMAND_GRAMMAR]: ${key} 必须是 string。` };
|
|
2016
|
+
}
|
|
2017
|
+
}
|
|
2018
|
+
const { taskHint, itemHint, contentHashHint, hintAction, ...authorization } = envelope;
|
|
2019
|
+
// 三个 hint 必须同时提供且格式有效;hintAction 只决定兑换后预选知悉还是提问,
|
|
2020
|
+
// 不参与授权。任一不合法都在激活前整体拒绝,不做「丢掉提示照常激活」。
|
|
2021
|
+
const hint = validateLaunchAckHint({ task: taskHint, item: itemHint, contentHash: contentHashHint, action: hintAction });
|
|
2022
|
+
if (!hint.ok)
|
|
2023
|
+
return { kind: 'error', text: `INPUT_DENIED [OWNER_COMMAND_GRAMMAR]: ${hint.message}` };
|
|
1697
2024
|
try {
|
|
1698
2025
|
const runtime = await ensureGuiServer();
|
|
1699
|
-
const activation = runtime.ownerControl.activate(issueOwnerControlCapability(),
|
|
2026
|
+
const activation = runtime.ownerControl.activate(issueOwnerControlCapability(), authorization, hint.hint ?? undefined);
|
|
1700
2027
|
const opened = openLocalConsole(`${runtime.address.origin}${activation.launchPath}?ticket=${encodeURIComponent(activation.launchTicket)}`);
|
|
1701
2028
|
return {
|
|
1702
2029
|
kind: opened ? 'success' : 'error',
|
|
@@ -1824,20 +2151,43 @@ export function apply(ctx, config, dependencies = {}) {
|
|
|
1824
2151
|
return { kind: text.startsWith('UNKNOWN/') || text.startsWith('错误:') ? 'error' : 'success', text };
|
|
1825
2152
|
}
|
|
1826
2153
|
case 'role.bind': {
|
|
1827
|
-
const parsed = parseJsonEnvelope(rest, ['role_type', 'role_name', 'session_id', 'model_name', 'agent_name', 'session_meta'], ['role_type']);
|
|
2154
|
+
const parsed = parseJsonEnvelope(rest, ['role_type', 'role_name', 'session_id', 'model_name', 'agent_name', 'session_meta', 'territory_id'], ['role_type']);
|
|
1828
2155
|
if (!parsed.ok || typeof parsed.value.role_type !== 'string')
|
|
1829
2156
|
return { kind: 'error', text: `INPUT_DENIED [OWNER_COMMAND_GRAMMAR]: ${parsed.ok ? 'role_type 必须是 string。' : parsed.message}` };
|
|
1830
2157
|
const value = parsed.value;
|
|
1831
|
-
|
|
2158
|
+
const boundRoleType = value.role_type.trim().toUpperCase();
|
|
2159
|
+
if (boundRoleType === 'OWNER')
|
|
1832
2160
|
return { kind: 'error', text: 'OWNER_CONTROL_REQUIRED: OWNER projection 不得通过 role.bind 改绑为 Session。' };
|
|
1833
|
-
for (const key of ['role_name', 'session_id', 'model_name', 'agent_name', 'session_meta']) {
|
|
2161
|
+
for (const key of ['role_name', 'session_id', 'model_name', 'agent_name', 'session_meta', 'territory_id']) {
|
|
1834
2162
|
if (value[key] !== undefined && typeof value[key] !== 'string')
|
|
1835
2163
|
return { kind: 'error', text: `INPUT_DENIED [OWNER_COMMAND_GRAMMAR]: ${key} 必须是 string。` };
|
|
1836
2164
|
}
|
|
1837
|
-
const
|
|
1838
|
-
if (
|
|
1839
|
-
return { kind: 'error', text:
|
|
1840
|
-
|
|
2165
|
+
const sessionCheck = await validateDirectSessionPayload('role.bind', value.session_id);
|
|
2166
|
+
if (!sessionCheck.ok)
|
|
2167
|
+
return { kind: 'error', text: sessionCheck.message };
|
|
2168
|
+
// v3.2.0(Owner 2026-09-28 口径):领地与主管完全绑定——任命主管必须同时给出领地,
|
|
2169
|
+
// 且「建席位 + 设主理」必须在同一事务里原子完成,不存在"有主管、没领地"的中间态。
|
|
2170
|
+
// 语义校验放在会话校验之后,保证「会话不可信」仍按 SESSION_* 原因码优先暴露。
|
|
2171
|
+
const territoryId = typeof value.territory_id === 'string' ? value.territory_id.trim() : '';
|
|
2172
|
+
if (boundRoleType !== 'SUPERVISOR' && territoryId) {
|
|
2173
|
+
return { kind: 'error', text: `INPUT_DENIED [OWNER_COMMAND_GRAMMAR]: territory_id 只用于任命 SUPERVISOR(收到 ${boundRoleType || '空'});其它角色不隶属领地。` };
|
|
2174
|
+
}
|
|
2175
|
+
if (boundRoleType === 'SUPERVISOR' && !territoryId) {
|
|
2176
|
+
return { kind: 'error', text: 'INPUT_DENIED [OWNER_COMMAND_GRAMMAR]: 任命 SUPERVISOR 必须同时给出 territory_id —— 主管不能没有领地(未写入)。' };
|
|
2177
|
+
}
|
|
2178
|
+
const bindParams = {
|
|
2179
|
+
kingdomId: requireKingdom(),
|
|
2180
|
+
roleType: value.role_type,
|
|
2181
|
+
roleName: value.role_name,
|
|
2182
|
+
sessionId: value.session_id,
|
|
2183
|
+
modelName: value.model_name,
|
|
2184
|
+
agentName: value.agent_name,
|
|
2185
|
+
sessionMeta: value.session_meta,
|
|
2186
|
+
sessionEvidence: sessionCheck.evidence,
|
|
2187
|
+
};
|
|
2188
|
+
const text = ownerWrite('role.bind', () => boundRoleType === 'SUPERVISOR'
|
|
2189
|
+
? bindSupervisorForTerritory(store, { ...bindParams, territoryId }, auth)
|
|
2190
|
+
: bindRole(store, bindParams, auth));
|
|
1841
2191
|
return { kind: text.startsWith('UNKNOWN/') || text.startsWith('错误:') || text.startsWith('角色 ') && text.includes('已有绑定') ? 'error' : 'success', text };
|
|
1842
2192
|
}
|
|
1843
2193
|
case 'role.unbind': {
|
|
@@ -1883,10 +2233,10 @@ export function apply(ctx, config, dependencies = {}) {
|
|
|
1883
2233
|
if (value[key] !== undefined && value[key] !== null && typeof value[key] !== 'string')
|
|
1884
2234
|
return { kind: 'error', text: `INPUT_DENIED [OWNER_COMMAND_GRAMMAR]: ${key} 必须是 string 或 null。` };
|
|
1885
2235
|
}
|
|
1886
|
-
const
|
|
1887
|
-
if (
|
|
1888
|
-
return { kind: 'error', text:
|
|
1889
|
-
const text = ownerWrite('role.session', () => rebindSession(store, { kingdomId: requireKingdom(), roleType: value.role_type, bindingId: value.binding_id, sessionId: value.session_id, modelName: value.model_name, agentName: value.agent_name, sessionMeta: value.session_meta }, auth));
|
|
2236
|
+
const sessionCheck = await validateDirectSessionPayload('role.session', value.session_id);
|
|
2237
|
+
if (!sessionCheck.ok)
|
|
2238
|
+
return { kind: 'error', text: sessionCheck.message };
|
|
2239
|
+
const text = ownerWrite('role.session', () => rebindSession(store, { kingdomId: requireKingdom(), roleType: value.role_type, bindingId: value.binding_id, sessionId: value.session_id, modelName: value.model_name, agentName: value.agent_name, sessionMeta: value.session_meta, sessionEvidence: sessionCheck.evidence }, auth));
|
|
1890
2240
|
return { kind: text.startsWith('UNKNOWN/') || text.startsWith('错误:') ? 'error' : 'success', text };
|
|
1891
2241
|
}
|
|
1892
2242
|
case 'execution-profile': {
|
|
@@ -1918,12 +2268,14 @@ export function apply(ctx, config, dependencies = {}) {
|
|
|
1918
2268
|
'/kingdom init 原子初始化/接入;OWNER.session_id 永远为 null',
|
|
1919
2269
|
'/kingdom status 查看真实状态(只读)',
|
|
1920
2270
|
'/kingdom owner.gui <严格 JSON> 激活具体动作/资源范围的短期人类管理窗口;/owner 页面提供激活格式说明',
|
|
2271
|
+
'owner.gui 可选携带非授权的 taskHint/itemHint/contentHashHint(仅用于预选刚点的那一条,不授予权限);工作台条目图标可一键复制该命令',
|
|
1921
2272
|
'/kingdom ceiling {"ceiling":{"tool:pwsh":true}} | {"clear":true}',
|
|
1922
2273
|
'/kingdom territory.create {"name":"研发领","workspace_path":"C:/work"}',
|
|
1923
2274
|
'/kingdom territory.delete {"territory_id":"...","force":false}',
|
|
1924
|
-
'/kingdom territory.supervisor {"territory_id":"...","supervisor_binding_id":"..."}',
|
|
1925
|
-
'/kingdom role.bind {"role_type":"SUPERVISOR","role_name":"主理","session_id":"真实 DSH session"}',
|
|
1926
|
-
'/kingdom role.
|
|
2275
|
+
'/kingdom territory.supervisor {"territory_id":"...","supervisor_binding_id":"..."}(改派/解除既有主理)',
|
|
2276
|
+
'/kingdom role.bind {"role_type":"SUPERVISOR","role_name":"主理","territory_id":"...","session_id":"真实 DSH session"} ← 主管必须同时指定领地(同一事务原子完成)',
|
|
2277
|
+
'/kingdom role.bind {"role_type":"WORKER","role_name":"执行者"}(非主管角色不带 territory_id)',
|
|
2278
|
+
'/kingdom role.unbind {"binding_id":"...","reason":"换届"}(退任主管会同时解除其领地主理)',
|
|
1927
2279
|
'/kingdom role.session {"binding_id":"...","session_id":"真实 DSH session"}',
|
|
1928
2280
|
'/kingdom execution-profile {"binding_id":"...","provider":"spawn","model":"..."}',
|
|
1929
2281
|
'以上写命令只接受一个 JSON object;unknown key、额外 token、Owner Session 均拒绝。',
|