dsh-kingdom 0.3.1
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/LICENSE +29 -0
- package/README.md +184 -0
- package/cordis.patch.yml +6 -0
- package/lib/core/binding.js +57 -0
- package/lib/core/binding.js.map +1 -0
- package/lib/core/db.js +521 -0
- package/lib/core/db.js.map +1 -0
- package/lib/core/execution.js +88 -0
- package/lib/core/execution.js.map +1 -0
- package/lib/core/kingdom.js +117 -0
- package/lib/core/kingdom.js.map +1 -0
- package/lib/core/task-service.js +581 -0
- package/lib/core/task-service.js.map +1 -0
- package/lib/core/task.js +93 -0
- package/lib/core/task.js.map +1 -0
- package/lib/core/territory.js +47 -0
- package/lib/core/territory.js.map +1 -0
- package/lib/gui/contract.js +53 -0
- package/lib/gui/contract.js.map +1 -0
- package/lib/gui/server.js +204 -0
- package/lib/gui/server.js.map +1 -0
- package/lib/gui/snapshot.js +379 -0
- package/lib/gui/snapshot.js.map +1 -0
- package/lib/index.js +489 -0
- package/lib/index.js.map +1 -0
- package/lib/paths.js +23 -0
- package/lib/paths.js.map +1 -0
- package/lib/types/core/binding.d.ts +11 -0
- package/lib/types/core/db.d.ts +261 -0
- package/lib/types/core/execution.d.ts +51 -0
- package/lib/types/core/kingdom.d.ts +34 -0
- package/lib/types/core/task-service.d.ts +107 -0
- package/lib/types/core/task.d.ts +60 -0
- package/lib/types/core/territory.d.ts +9 -0
- package/lib/types/gui/contract.d.ts +226 -0
- package/lib/types/gui/server.d.ts +26 -0
- package/lib/types/gui/snapshot.d.ts +44 -0
- package/lib/types/index.d.ts +80 -0
- package/lib/types/paths.d.ts +6 -0
- package/lib/types/worker/dsh-subagent.d.ts +79 -0
- package/lib/types/worker/executor.d.ts +121 -0
- package/lib/worker/dsh-subagent.js +117 -0
- package/lib/worker/dsh-subagent.js.map +1 -0
- package/lib/worker/executor.js +94 -0
- package/lib/worker/executor.js.map +1 -0
- package/package.json +59 -0
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-kingdom — 插件 ↔ GUI 的线上契约(Phase 3)。
|
|
3
|
+
*
|
|
4
|
+
* ## 唯一架构原则
|
|
5
|
+
*
|
|
6
|
+
* ```text
|
|
7
|
+
* 插件输出治理事实和活动语义
|
|
8
|
+
* GUI 决定使用哪个人物、场景和动画
|
|
9
|
+
* ```
|
|
10
|
+
*
|
|
11
|
+
* 因此本文件里**不会**出现 `chancellor.png`、`sleep.gif`、
|
|
12
|
+
* `sprite.knight.default.forge.work.idle` 之类的美术知识。
|
|
13
|
+
* 插件只输出 `{ role, state, activity }`,这正是 GUI 端 Visual Resolver 的输入
|
|
14
|
+
* (另外两维 `skin` / `scene` 属于 GUI 部署配置,插件不参与)。
|
|
15
|
+
*
|
|
16
|
+
* 角色、模型、工具、运行时与皮肤因此保持解耦:
|
|
17
|
+
* 换一套贴图、换一个场景、把骑士换成别的形象,插件一行都不用改。
|
|
18
|
+
*
|
|
19
|
+
* ## 两类事实必须分开读
|
|
20
|
+
*
|
|
21
|
+
* - `TaskView.status` —— 治理事实:组织对这件事的裁定进度。
|
|
22
|
+
* - `ExecutionView.state` —— 运行事实:某一次执行此刻的状况。
|
|
23
|
+
*
|
|
24
|
+
* `Task.RUNNING` **不代表**人物正在工作(REWORK 后任务立刻回 RUNNING,
|
|
25
|
+
* 但新 Execution 还没创建)。GUI 判断"是否播放工作动画"必须看 Execution。
|
|
26
|
+
*/
|
|
27
|
+
/** 组织角色。GUI 自行决定每个角色用哪个人物形象。 */
|
|
28
|
+
export declare const ACTOR_ROLES: readonly ["OWNER", "CHANCELLOR", "SUPERVISOR", "WORKER"];
|
|
29
|
+
export type ActorRole = (typeof ACTOR_ROLES)[number];
|
|
30
|
+
/**
|
|
31
|
+
* 人物状态(Resolver 的 `state` 维)。
|
|
32
|
+
*
|
|
33
|
+
* `absent` 表示该角色没有绑定:GUI 应当**保留组织节点与姓名牌**,只是不渲染人物 Sprite。
|
|
34
|
+
*
|
|
35
|
+
* 这份清单是**契约的一部分**:GUI 侧 Visual Resolver 必须逐个处理它们,
|
|
36
|
+
* 漏掉任何一个都会让人物静默退化成 idle。改动本数组前请同步 GUI。
|
|
37
|
+
*/
|
|
38
|
+
export declare const ACTOR_STATES: readonly ["absent", "idle", "planning", "assigning", "working", "sleeping", "reviewing", "waiting", "confused", "celebrating"];
|
|
39
|
+
export type ActorState = (typeof ACTOR_STATES)[number];
|
|
40
|
+
/**
|
|
41
|
+
* 角色专属动作(Resolver 的 `activity` 维)。
|
|
42
|
+
*
|
|
43
|
+
* 这是**语义**而不是动画名:`review` 表示"正在复核",
|
|
44
|
+
* 至于播哪个 clip 由 GUI 的 visual-map 决定。
|
|
45
|
+
*/
|
|
46
|
+
export type ActorActivity = 'plan' | 'read' | 'assign' | 'review' | 'rework' | 'accept' | 'execute' | null;
|
|
47
|
+
/** 命令返回的稳定错误码。GUI 据此决定提示与可用按钮,不解析中文文案。 */
|
|
48
|
+
export type KingdomErrorCode = 'KINGDOM_NOT_INITIALIZED' | 'ROLE_BINDING_MISSING' | 'UNAUTHORIZED_PRINCIPAL' | 'TERRITORY_MISSING' | 'TERRITORY_AMBIGUOUS' | 'TERRITORY_NOT_IN_KINGDOM' | 'TASK_NOT_FOUND' | 'TASK_NOT_IN_KINGDOM' | 'ILLEGAL_TASK_STATE' | 'INVALID_INPUT' | 'INVALID_DECISION' | 'REASON_REQUIRED' | 'WORKER_BINDING_INVALID' | 'EXECUTOR_UNAVAILABLE' | 'WORKER_EXECUTION_FAILED' | 'EXECUTION_NOT_FOUND' | 'ILLEGAL_EXECUTION_STATE';
|
|
49
|
+
/** GUI 可以呈现为按钮的下一步动作。 */
|
|
50
|
+
export type AllowedAction = 'assign' | 'start' | 'review:accept' | 'review:rework' | 'review:fail' | 'execution:pause' | 'execution:resume' | 'execution:abort';
|
|
51
|
+
export interface EventView {
|
|
52
|
+
seq: number;
|
|
53
|
+
eventId: string;
|
|
54
|
+
type: string;
|
|
55
|
+
actorRole: string | null;
|
|
56
|
+
actorId: string | null;
|
|
57
|
+
targetType: string | null;
|
|
58
|
+
targetId: string | null;
|
|
59
|
+
payload: Record<string, unknown>;
|
|
60
|
+
createdAt: string;
|
|
61
|
+
}
|
|
62
|
+
export interface BindingView {
|
|
63
|
+
bindingId: string;
|
|
64
|
+
roleType: string;
|
|
65
|
+
roleName: string;
|
|
66
|
+
runtimeType: string;
|
|
67
|
+
sessionId: string | null;
|
|
68
|
+
createdAt: string;
|
|
69
|
+
}
|
|
70
|
+
export interface TerritoryView {
|
|
71
|
+
territoryId: string;
|
|
72
|
+
name: string;
|
|
73
|
+
workspacePath: string | null;
|
|
74
|
+
summary: string | null;
|
|
75
|
+
status: string;
|
|
76
|
+
createdAt: string;
|
|
77
|
+
}
|
|
78
|
+
/** Worker 的一次自述。**是 Claim,不是完成事实。** */
|
|
79
|
+
export interface ClaimView {
|
|
80
|
+
resultId: string;
|
|
81
|
+
attemptNo: number;
|
|
82
|
+
workerBindingId: string | null;
|
|
83
|
+
sessionId: string | null;
|
|
84
|
+
/** Worker 自称的结果,仅供展示与审查,不驱动状态。 */
|
|
85
|
+
claimedOutcome: string;
|
|
86
|
+
summary: string | null;
|
|
87
|
+
artifacts: string[];
|
|
88
|
+
risks: string[];
|
|
89
|
+
createdAt: string;
|
|
90
|
+
}
|
|
91
|
+
/** 运行事实。GUI 判断"人物是否在场/在工作/在休息"只看这个。 */
|
|
92
|
+
export interface ExecutionView {
|
|
93
|
+
executionId: string;
|
|
94
|
+
taskId: string;
|
|
95
|
+
attemptNo: number;
|
|
96
|
+
workerBindingId: string | null;
|
|
97
|
+
sessionId: string | null;
|
|
98
|
+
state: string;
|
|
99
|
+
detail: string | null;
|
|
100
|
+
startedAt: string;
|
|
101
|
+
heartbeatAt: string | null;
|
|
102
|
+
endedAt: string | null;
|
|
103
|
+
/**
|
|
104
|
+
* 已登记暂停请求但尚未生效(one-shot 无法在 turn 中途挂起)。
|
|
105
|
+
* GUI 应显示"准备休息",**不要**直接播睡觉动画——那会谎报状态。
|
|
106
|
+
*/
|
|
107
|
+
pausePending: boolean;
|
|
108
|
+
}
|
|
109
|
+
export interface TaskView {
|
|
110
|
+
taskId: string;
|
|
111
|
+
territoryId: string;
|
|
112
|
+
title: string;
|
|
113
|
+
description: string | null;
|
|
114
|
+
acceptanceCriteria: string | null;
|
|
115
|
+
/** 治理事实。注意它 !== 人物是否在工作。 */
|
|
116
|
+
status: string;
|
|
117
|
+
assignedBindingId: string | null;
|
|
118
|
+
/** 最近一次 Claim 的摘要(Claim,不是事实)。 */
|
|
119
|
+
resultSummary: string | null;
|
|
120
|
+
attemptCount: number;
|
|
121
|
+
latestClaim: ClaimView | null;
|
|
122
|
+
latestExecution: ExecutionView | null;
|
|
123
|
+
allowedActions: AllowedAction[];
|
|
124
|
+
createdAt: string;
|
|
125
|
+
updatedAt: string;
|
|
126
|
+
}
|
|
127
|
+
/** 一个角色此刻应该怎么演。GUI 拿它去查自己的 visual-map。 */
|
|
128
|
+
export interface StageActorView {
|
|
129
|
+
role: ActorRole;
|
|
130
|
+
bindingId: string | null;
|
|
131
|
+
roleName: string | null;
|
|
132
|
+
state: ActorState;
|
|
133
|
+
activity: ActorActivity;
|
|
134
|
+
/** 该状态关联的任务/执行,便于 GUI 做详情联动与人物定位。 */
|
|
135
|
+
taskId: string | null;
|
|
136
|
+
executionId: string | null;
|
|
137
|
+
attemptNo: number | null;
|
|
138
|
+
/** 状态起始时间(ISO)。GUI 可据此对齐动画进度。 */
|
|
139
|
+
since: string | null;
|
|
140
|
+
/**
|
|
141
|
+
* 一次性动作(如庆祝、派发、规划)。GUI 播完应回落到 `fallbackState`。
|
|
142
|
+
* 非 transient 的状态是持续循环。
|
|
143
|
+
*/
|
|
144
|
+
transient: boolean;
|
|
145
|
+
/** transient 状态的剩余毫秒;GUI 可用它决定是否还要播。 */
|
|
146
|
+
remainingMs: number | null;
|
|
147
|
+
/** transient 播完后的稳定状态。 */
|
|
148
|
+
fallbackState: ActorState;
|
|
149
|
+
/** 触发本状态的事件序号,便于 GUI 丢弃过期事件。 */
|
|
150
|
+
sourceSeq: number | null;
|
|
151
|
+
}
|
|
152
|
+
/** 权限诚实度声明。Beta 若未做主体校验,必须让 GUI 能显示这个徽章。 */
|
|
153
|
+
export interface AuthView {
|
|
154
|
+
/**
|
|
155
|
+
* `declarative`:只校验"王国中存在该角色绑定",**不验证调用者就是该角色**。
|
|
156
|
+
* `session-bound`:额外要求调用方 session 与 binding.session_id 匹配。
|
|
157
|
+
*/
|
|
158
|
+
mode: 'declarative' | 'session-bound';
|
|
159
|
+
/**
|
|
160
|
+
* `local-demo`:本地可信演示权限,不构成真实鉴权。
|
|
161
|
+
* GUI **必须**在提供派发/复核/返工按钮时显著标注这一点。
|
|
162
|
+
*/
|
|
163
|
+
trustLevel: 'local-demo' | 'session-verified';
|
|
164
|
+
note: string;
|
|
165
|
+
}
|
|
166
|
+
export interface SnapshotView {
|
|
167
|
+
schemaVersion: number;
|
|
168
|
+
/** = 最大事件序号。GUI 比较它决定是否重绘;也是增量拉取的游标。 */
|
|
169
|
+
revision: number;
|
|
170
|
+
/** 服务端生成快照的时刻(ISO),GUI 用它换算 transient 剩余时间。 */
|
|
171
|
+
generatedAt: string;
|
|
172
|
+
kingdom: {
|
|
173
|
+
kingdomId: string;
|
|
174
|
+
name: string;
|
|
175
|
+
ownerId: string;
|
|
176
|
+
ownerName: string;
|
|
177
|
+
createdAt: string;
|
|
178
|
+
} | null;
|
|
179
|
+
auth: AuthView;
|
|
180
|
+
bindings: BindingView[];
|
|
181
|
+
territories: TerritoryView[];
|
|
182
|
+
tasks: TaskView[];
|
|
183
|
+
liveExecutions: ExecutionView[];
|
|
184
|
+
/** 每个组织角色此刻的表演语义。 */
|
|
185
|
+
stage: StageActorView[];
|
|
186
|
+
recentEvents: EventView[];
|
|
187
|
+
}
|
|
188
|
+
/** 任务详情:验收标准、尝试历史、Claim、Supervisor 决策、关联事件、下一步动作。 */
|
|
189
|
+
export interface TaskDetailView {
|
|
190
|
+
schemaVersion: number;
|
|
191
|
+
revision: number;
|
|
192
|
+
task: TaskView;
|
|
193
|
+
territory: TerritoryView | null;
|
|
194
|
+
assignedBinding: BindingView | null;
|
|
195
|
+
claims: ClaimView[];
|
|
196
|
+
executions: ExecutionView[];
|
|
197
|
+
/** Supervisor 的历次裁定(从 events 还原,不存在 task_reviews 表)。 */
|
|
198
|
+
reviews: {
|
|
199
|
+
seq: number;
|
|
200
|
+
decision: string;
|
|
201
|
+
reason: string | null;
|
|
202
|
+
reviewerBindingId: string | null;
|
|
203
|
+
reviewedAttemptNo: number | null;
|
|
204
|
+
claimedOutcome: string | null;
|
|
205
|
+
createdAt: string;
|
|
206
|
+
}[];
|
|
207
|
+
relatedEvents: EventView[];
|
|
208
|
+
allowedActions: AllowedAction[];
|
|
209
|
+
}
|
|
210
|
+
/** 所有写命令的统一返回。GUI 只读结构化字段,不解析 `message`。 */
|
|
211
|
+
export interface CommandResultView {
|
|
212
|
+
ok: boolean;
|
|
213
|
+
errorCode: KingdomErrorCode | null;
|
|
214
|
+
/** 给人/模型看的中文说明;GUI 可展示但不得据此判断逻辑。 */
|
|
215
|
+
message: string;
|
|
216
|
+
task: TaskView | null;
|
|
217
|
+
execution: ExecutionView | null;
|
|
218
|
+
/** 本次命令产生的事件(已带 seq,升序)。 */
|
|
219
|
+
emittedEvents: EventView[];
|
|
220
|
+
allowedActions: AllowedAction[];
|
|
221
|
+
revision: number;
|
|
222
|
+
}
|
|
223
|
+
/** GUI 契约版本。破坏性变更时递增,GUI 应拒绝不认识的版本。 */
|
|
224
|
+
export declare const GUI_SCHEMA_VERSION = 1;
|
|
225
|
+
/** transient 表演动作的默认存活窗口(毫秒)。GUI 轮询 1–2s,足够捕捉。 */
|
|
226
|
+
export declare const TRANSIENT_WINDOW_MS = 3500;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { CommandResultView, SnapshotView, TaskDetailView } from './contract.js';
|
|
2
|
+
import type { EventView } from './contract.js';
|
|
3
|
+
export interface GuiServerHandlers {
|
|
4
|
+
snapshot(): SnapshotView;
|
|
5
|
+
taskDetail(taskId: string): TaskDetailView | null;
|
|
6
|
+
eventsSince(afterSeq: number, limit: number): {
|
|
7
|
+
revision: number;
|
|
8
|
+
events: EventView[];
|
|
9
|
+
};
|
|
10
|
+
command(name: string, payload: Record<string, unknown>): Promise<CommandResultView>;
|
|
11
|
+
}
|
|
12
|
+
export interface GuiServerOptions {
|
|
13
|
+
port: number;
|
|
14
|
+
host?: string;
|
|
15
|
+
token?: string;
|
|
16
|
+
allowOrigins?: string[];
|
|
17
|
+
logger?: {
|
|
18
|
+
info(message: string): void;
|
|
19
|
+
warn(message: string): void;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* 启动本地 GUI 通道。
|
|
24
|
+
* @returns 关闭函数(挂到 ctx.effect,插件卸载/热重载时自动收回端口)。
|
|
25
|
+
*/
|
|
26
|
+
export declare function startGuiServer(handlers: GuiServerHandlers, options: GuiServerOptions): () => void;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-kingdom — DB 行 → GUI 视图的投影(Phase 3)。
|
|
3
|
+
*
|
|
4
|
+
* 全部是**纯函数**:给定 (库状态, now) 就唯一确定输出。
|
|
5
|
+
* 因此 GUI 轮询即可拿到正确的表演状态,服务端不需要任何定时器或推送状态机。
|
|
6
|
+
*
|
|
7
|
+
* 再次强调边界:本文件只产出 `{ role, state, activity }` 这类语义,
|
|
8
|
+
* 绝不产出贴图、clip、场景文件名——那些是 GUI 的 visual-map 的事。
|
|
9
|
+
*/
|
|
10
|
+
import type { AllowedAction, BindingView, ClaimView, EventView, ExecutionView, StageActorView, TaskDetailView, TaskView, TerritoryView, SnapshotView, AuthView } from './contract.js';
|
|
11
|
+
import { type EventRow, type ExecutionRow, type KingdomStore, type RoleBindingRow, type TaskRow, type TerritoryRow, type WorkerResultRow } from '../core/db.js';
|
|
12
|
+
export declare function toEventView(row: EventRow): EventView;
|
|
13
|
+
export declare function toBindingView(row: RoleBindingRow): BindingView;
|
|
14
|
+
export declare function toTerritoryView(row: TerritoryRow): TerritoryView;
|
|
15
|
+
export declare function toClaimView(row: WorkerResultRow): ClaimView;
|
|
16
|
+
export declare function toExecutionView(row: ExecutionRow): ExecutionView;
|
|
17
|
+
/**
|
|
18
|
+
* 任务当前允许的下一步动作。
|
|
19
|
+
*
|
|
20
|
+
* 这是 GUI 按钮可用性的**唯一**依据——GUI 不应自己从 status 推断,
|
|
21
|
+
* 否则状态机一改按钮就错。
|
|
22
|
+
*/
|
|
23
|
+
export declare function allowedActionsFor(task: TaskRow, execution: ExecutionRow | null): AllowedAction[];
|
|
24
|
+
export declare function toTaskView(store: KingdomStore, task: TaskRow): TaskView;
|
|
25
|
+
interface StageInput {
|
|
26
|
+
bindings: RoleBindingRow[];
|
|
27
|
+
tasks: TaskRow[];
|
|
28
|
+
executions: ExecutionRow[];
|
|
29
|
+
events: EventRow[];
|
|
30
|
+
nowMs: number;
|
|
31
|
+
transientWindowMs: number;
|
|
32
|
+
}
|
|
33
|
+
/** 计算全部角色此刻的表演语义。 */
|
|
34
|
+
export declare function projectStage(input: StageInput): StageActorView[];
|
|
35
|
+
export interface SnapshotOptions {
|
|
36
|
+
auth: AuthView;
|
|
37
|
+
eventLimit?: number;
|
|
38
|
+
transientWindowMs?: number;
|
|
39
|
+
/** 注入的"现在",仅供测试确定性使用。 */
|
|
40
|
+
nowMs?: number;
|
|
41
|
+
}
|
|
42
|
+
export declare function buildSnapshot(store: KingdomStore, options: SnapshotOptions): SnapshotView;
|
|
43
|
+
export declare function buildTaskDetail(store: KingdomStore, kingdomId: string, taskId: string): TaskDetailView | null;
|
|
44
|
+
export {};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-kingdom — 独立 dsh 插件:会话内初始化/接入本地王国。
|
|
3
|
+
*
|
|
4
|
+
* 规范:
|
|
5
|
+
* - 资源注册必须挂 ctx.effect(热重载/卸载自动清理)。
|
|
6
|
+
* - 工具 schema 精简:description 短句点明用途,详解放 tool result。
|
|
7
|
+
*
|
|
8
|
+
* Phase 1 能力:
|
|
9
|
+
* - /kingdom init(幂等:无则初始化,有则接入)
|
|
10
|
+
* - /kingdom status(真实状态)
|
|
11
|
+
* - 工具:kingdom_status / kingdom_create_territory / kingdom_list_territories /
|
|
12
|
+
* kingdom_bind_role / kingdom_list_bindings
|
|
13
|
+
*
|
|
14
|
+
* Phase 2 能力(Worker Claim Bridge → 治理闭环):
|
|
15
|
+
* - 工具:kingdom_plan_task / kingdom_assign_task / kingdom_start_task /
|
|
16
|
+
* kingdom_review_task / kingdom_list_tasks
|
|
17
|
+
* - Worker 经 one-shot subagent 执行(裁决 2),结果落 worker_results(裁决 4)。
|
|
18
|
+
*
|
|
19
|
+
* 治理底线(Phase 1 保持 + Phase 2 强化):
|
|
20
|
+
* - **无任何工具能把 Task 直接标 DONE**:DONE 唯一入口是 REVIEW + Supervisor ACCEPT。
|
|
21
|
+
* - Worker 的结果只是 Claim(→ REVIEW),不是 Fact。
|
|
22
|
+
* - 无任意 SQL 通道暴露给 Agent。
|
|
23
|
+
*/
|
|
24
|
+
import type { Context } from 'cordis';
|
|
25
|
+
import z from 'schemastery';
|
|
26
|
+
export declare const name = "dsh-kingdom";
|
|
27
|
+
export declare const inject: string[];
|
|
28
|
+
export interface Config {
|
|
29
|
+
kingdomName: string;
|
|
30
|
+
ownerName: string;
|
|
31
|
+
workerProvider: string;
|
|
32
|
+
guiPort: number;
|
|
33
|
+
guiToken: string;
|
|
34
|
+
guiAllowOrigins: string[];
|
|
35
|
+
authMode: 'declarative' | 'session-bound';
|
|
36
|
+
}
|
|
37
|
+
export declare const Config: z<Schemastery.ObjectS<{
|
|
38
|
+
kingdomName: z<string, string>;
|
|
39
|
+
ownerName: z<string, string>;
|
|
40
|
+
/** Worker 执行用的 subagent provider(dsh base bundle 默认注册 spawn / fork)。 */
|
|
41
|
+
workerProvider: z<string, string>;
|
|
42
|
+
/**
|
|
43
|
+
* 本地 GUI 通道端口。**默认 0 = 关闭** —— 不在用户不知情时打开监听端口。
|
|
44
|
+
* 设为非零值即启用,只绑定 127.0.0.1。
|
|
45
|
+
*/
|
|
46
|
+
guiPort: z<number, number>;
|
|
47
|
+
/** 可选 bearer token;设置后 GUI 所有请求都要带 Authorization 头。 */
|
|
48
|
+
guiToken: z<string, string>;
|
|
49
|
+
/** CORS 允许的 Origin 列表;默认放开(服务只绑本机回环)。 */
|
|
50
|
+
guiAllowOrigins: z<string[], string[]>;
|
|
51
|
+
/**
|
|
52
|
+
* 角色鉴权强度。
|
|
53
|
+
* `declarative`(默认,Phase 1/2 延续)只校验"王国中存在该角色绑定",
|
|
54
|
+
* **不验证调用者就是该角色** —— snapshot 会如实报 `trustLevel: local-demo`。
|
|
55
|
+
* `session-bound` 额外要求调用方 session 与 binding.session_id 一致。
|
|
56
|
+
*/
|
|
57
|
+
authMode: z<"declarative" | "session-bound", "declarative" | "session-bound">;
|
|
58
|
+
}>, Schemastery.ObjectT<{
|
|
59
|
+
kingdomName: z<string, string>;
|
|
60
|
+
ownerName: z<string, string>;
|
|
61
|
+
/** Worker 执行用的 subagent provider(dsh base bundle 默认注册 spawn / fork)。 */
|
|
62
|
+
workerProvider: z<string, string>;
|
|
63
|
+
/**
|
|
64
|
+
* 本地 GUI 通道端口。**默认 0 = 关闭** —— 不在用户不知情时打开监听端口。
|
|
65
|
+
* 设为非零值即启用,只绑定 127.0.0.1。
|
|
66
|
+
*/
|
|
67
|
+
guiPort: z<number, number>;
|
|
68
|
+
/** 可选 bearer token;设置后 GUI 所有请求都要带 Authorization 头。 */
|
|
69
|
+
guiToken: z<string, string>;
|
|
70
|
+
/** CORS 允许的 Origin 列表;默认放开(服务只绑本机回环)。 */
|
|
71
|
+
guiAllowOrigins: z<string[], string[]>;
|
|
72
|
+
/**
|
|
73
|
+
* 角色鉴权强度。
|
|
74
|
+
* `declarative`(默认,Phase 1/2 延续)只校验"王国中存在该角色绑定",
|
|
75
|
+
* **不验证调用者就是该角色** —— snapshot 会如实报 `trustLevel: local-demo`。
|
|
76
|
+
* `session-bound` 额外要求调用方 session 与 binding.session_id 一致。
|
|
77
|
+
*/
|
|
78
|
+
authMode: z<"declarative" | "session-bound", "declarative" | "session-bound">;
|
|
79
|
+
}>>;
|
|
80
|
+
export declare function apply(ctx: Context, config: Config): void;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** 解析 dsh 主目录:DSH_HOME 环境变量优先,否则 ~/.dsh。 */
|
|
2
|
+
export declare function resolveDshHome(): string;
|
|
3
|
+
/** kingdom 数据根目录:<dshHome>/kingdom */
|
|
4
|
+
export declare function kingdomRoot(): string;
|
|
5
|
+
/** kingdom 数据库文件:<dshHome>/kingdom/kingdom.db */
|
|
6
|
+
export declare function kingdomDbPath(): string;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-kingdom — DshSubagentExecutor:用 dsh one-shot subagent 执行 Worker(裁决 2)。
|
|
3
|
+
*
|
|
4
|
+
* 裁决 2 冻结的形状:
|
|
5
|
+
* - Worker 用 `ctx.subagents.start(provider, { label, prompt, outputSchema })` **one-shot 独立执行**;
|
|
6
|
+
* - **不**采用常驻 `ctx.agents.create`;
|
|
7
|
+
* - 每次执行都是新的 execution/session(REWORK 也一样,裁决 5);
|
|
8
|
+
* - Structured Worker Result 由 subagent 的 `outputSchema` 约束,
|
|
9
|
+
* 宿主(本类)接收后交给 Task Core 落 worker_results。
|
|
10
|
+
*
|
|
11
|
+
* 这也是为什么 `kingdom_report_result` **不是**独立工具:Worker 是 one-shot subagent,
|
|
12
|
+
* 它没有机会自己调工具改 Task 状态 —— 结果只能经宿主这一条路回来。
|
|
13
|
+
*
|
|
14
|
+
* ## 为什么这里对 ctx.subagents 用结构化局部类型
|
|
15
|
+
*
|
|
16
|
+
* 本插件是独立分发的 tgz,peer 只声明 5 条(见 README)。为了不新增第 6 条 peer、
|
|
17
|
+
* 也不让编译产物 .d.ts 泄漏 subagent 类型,这里按 dsh 已发布的接口**结构化**地
|
|
18
|
+
* 声明所需最小面,而不 import `@deepseek-ai/dsh-subagent`。
|
|
19
|
+
* 对应 checkout 定义(0.1.0/0.2.0 基线一致):
|
|
20
|
+
* - `SubagentRuntime.start(name, request)` — packages/subagent/subagent/src/index.ts:414
|
|
21
|
+
* - `SubagentStartRequest{label,prompt,parent,signal,outputSchema}` — .../src/types.ts:100
|
|
22
|
+
* - `SubagentRun{id,result,dispose}` / `SubagentResult{output,structured,stopReason}` — .../src/types.ts:219,249
|
|
23
|
+
*/
|
|
24
|
+
import { type WorkerContext, type WorkerExecutionOutcome, type WorkerExecutor } from './executor.js';
|
|
25
|
+
import type { TaskRow } from '../core/db.js';
|
|
26
|
+
/** dsh `SubagentResult` 的最小结构面。 */
|
|
27
|
+
interface SubagentResultLike {
|
|
28
|
+
readonly structured?: unknown;
|
|
29
|
+
readonly stopReason: string;
|
|
30
|
+
}
|
|
31
|
+
/** dsh `SubagentRun` 的最小结构面。 */
|
|
32
|
+
interface SubagentRunLike {
|
|
33
|
+
readonly id: string;
|
|
34
|
+
readonly result: Promise<SubagentResultLike>;
|
|
35
|
+
dispose(): Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
/** dsh `SubagentRuntime` 的最小结构面。 */
|
|
38
|
+
export interface SubagentsLike {
|
|
39
|
+
start(name: string, request: {
|
|
40
|
+
label?: string;
|
|
41
|
+
prompt: {
|
|
42
|
+
type: 'text';
|
|
43
|
+
text: string;
|
|
44
|
+
}[];
|
|
45
|
+
parent: unknown;
|
|
46
|
+
signal: AbortSignal;
|
|
47
|
+
outputSchema?: unknown;
|
|
48
|
+
}): Promise<SubagentRunLike>;
|
|
49
|
+
getProvider(name: string): unknown;
|
|
50
|
+
list(): string[];
|
|
51
|
+
}
|
|
52
|
+
/** 构造 DshSubagentExecutor 所需的一次性运行期入参。 */
|
|
53
|
+
export interface DshSubagentExecutorOptions {
|
|
54
|
+
/** `ctx.get('subagents')` 拿到的 subagent 注册表。 */
|
|
55
|
+
subagents: SubagentsLike;
|
|
56
|
+
/** provider 名(dsh base bundle 默认注册 `spawn` / `fork`)。 */
|
|
57
|
+
provider: string;
|
|
58
|
+
/**
|
|
59
|
+
* 发起委派的父 Agent(来自工具执行上下文 `exec.agent`)。
|
|
60
|
+
* in-process provider 从它派生 workspace / 血缘 / 委派深度。
|
|
61
|
+
*/
|
|
62
|
+
parent: unknown;
|
|
63
|
+
/** 调用方的取消信号(`exec.signal`)。 */
|
|
64
|
+
signal: AbortSignal;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 薄封装:把一次 Worker 执行落到一个 one-shot subagent run 上。
|
|
68
|
+
*
|
|
69
|
+
* 失败一律收敛成 `executor-failure`,**从不抛异常给 Task Core**
|
|
70
|
+
* —— 让状态机只面对两种确定结局(result / executor-failure),
|
|
71
|
+
* 而不是异常与返回值两条并行的控制流。
|
|
72
|
+
*/
|
|
73
|
+
export declare class DshSubagentExecutor implements WorkerExecutor {
|
|
74
|
+
readonly kind: string;
|
|
75
|
+
private readonly options;
|
|
76
|
+
constructor(options: DshSubagentExecutorOptions);
|
|
77
|
+
execute(task: TaskRow, context: WorkerContext): Promise<WorkerExecutionOutcome>;
|
|
78
|
+
}
|
|
79
|
+
export {};
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-kingdom — WorkerExecutor 接口(Phase 2,Owner 裁决 2)。
|
|
3
|
+
*
|
|
4
|
+
* 裁决 2 的形状:**Task Core 只依赖本接口,绝不直接 import subagents。**
|
|
5
|
+
* 换执行方式(one-shot subagent → 别的 runtime)不碰状态机。
|
|
6
|
+
* Worker ≠ Subagent Session:Worker 是组织角色(role_binding),
|
|
7
|
+
* subagent execution 只是它这一轮的执行载体。
|
|
8
|
+
*
|
|
9
|
+
* 本文件**零 dsh 依赖**:只有类型和纯函数,因此 Task Core 与自测都不需要活的 DSH。
|
|
10
|
+
*/
|
|
11
|
+
import type { TaskRow } from '../core/db.js';
|
|
12
|
+
/**
|
|
13
|
+
* Worker 交回的结构化结果(受 subagent outputSchema 约束)。
|
|
14
|
+
*
|
|
15
|
+
* **这是 Claim,不是 Fact。** outcome 是 Worker 的自述,
|
|
16
|
+
* 不参与任何自动状态决策:即使 outcome === 'FAILED',
|
|
17
|
+
* Task 也只推到 REVIEW,等 Supervisor 裁定(裁决 6)。
|
|
18
|
+
*/
|
|
19
|
+
export interface StructuredResult {
|
|
20
|
+
/** Worker 自称的结果。 */
|
|
21
|
+
outcome: 'COMPLETED' | 'FAILED' | 'BLOCKED';
|
|
22
|
+
/** 一段可供 Supervisor 审查的自述摘要。 */
|
|
23
|
+
summary: string;
|
|
24
|
+
/** 产出物(文件路径 / 制品标识),可选。 */
|
|
25
|
+
artifacts?: string[];
|
|
26
|
+
/** Worker 自述的风险与遗留问题,可选。 */
|
|
27
|
+
risks?: string[];
|
|
28
|
+
}
|
|
29
|
+
/** 传给 Worker 的这一轮上下文(裁决 5:REWORK 时带上一轮摘要 + 返工理由)。 */
|
|
30
|
+
export interface WorkerContext {
|
|
31
|
+
/** 原始 Task。 */
|
|
32
|
+
task: TaskRow;
|
|
33
|
+
/** 验收标准(Task 的 acceptance_criteria)。 */
|
|
34
|
+
acceptanceCriteria: string | null;
|
|
35
|
+
/** 第几次尝试,从 1 起。 */
|
|
36
|
+
attemptNo: number;
|
|
37
|
+
/** 上一轮 Worker Claim 的摘要(仅 REWORK 时存在)。 */
|
|
38
|
+
prevResultSummary?: string;
|
|
39
|
+
/** Supervisor 的 REWORK 理由(仅 REWORK 时存在)。 */
|
|
40
|
+
reworkReason?: string;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 一次 Worker 执行的结果,只有两种:
|
|
44
|
+
*
|
|
45
|
+
* - `result`:subagent 正常返回**合法结构化结果** → Task 推到 REVIEW(Claim 到达)。
|
|
46
|
+
* - `executor-failure`:启动失败 / 异常退出 / 无合法 outputSchema 输出
|
|
47
|
+
* → Core 直接 RUNNING → FAILED(裁决 6:**宿主观察到的运行事实**,不是相信 Worker 自述)。
|
|
48
|
+
*
|
|
49
|
+
* 注意这两者的区别就是 Phase 2 的治理核心:Worker 说自己失败了是 Claim(走 REVIEW),
|
|
50
|
+
* 宿主看见 executor 没跑出合法结果是 Fact(直接 FAILED)。
|
|
51
|
+
*/
|
|
52
|
+
export type WorkerExecutionOutcome = {
|
|
53
|
+
kind: 'result';
|
|
54
|
+
result: StructuredResult;
|
|
55
|
+
sessionId: string | null;
|
|
56
|
+
} | {
|
|
57
|
+
kind: 'executor-failure';
|
|
58
|
+
reason: string;
|
|
59
|
+
sessionId: string | null;
|
|
60
|
+
};
|
|
61
|
+
/** 薄执行封装。Task Core 只认这一个接口。 */
|
|
62
|
+
export interface WorkerExecutor {
|
|
63
|
+
/** 供事件/诊断使用的执行器标识(如 `dsh-subagent:spawn`)。 */
|
|
64
|
+
readonly kind: string;
|
|
65
|
+
execute(task: TaskRow, ctx: WorkerContext): Promise<WorkerExecutionOutcome>;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* 约束 Worker 结构化输出的 JSON Schema。
|
|
69
|
+
*
|
|
70
|
+
* 必须落在 dsh 强制的 schema 子集内
|
|
71
|
+
* (见 checkout `packages/core/tools/src/json-schema.ts`):
|
|
72
|
+
* - 根必须是 object(assertObjectJsonSchema);
|
|
73
|
+
* - `enum` 只允许挂在**已声明 scalar `type`** 的节点上
|
|
74
|
+
* —— 裸 `{ enum: [...] }` 会被判为 `.enum requires type or oneOf` 而拒绝,
|
|
75
|
+
* 所以 outcome 显式写了 `type: 'string'`。
|
|
76
|
+
*/
|
|
77
|
+
export declare const WORKER_OUTPUT_SCHEMA: {
|
|
78
|
+
readonly type: "object";
|
|
79
|
+
readonly required: readonly ["outcome", "summary"];
|
|
80
|
+
readonly properties: {
|
|
81
|
+
readonly outcome: {
|
|
82
|
+
readonly type: "string";
|
|
83
|
+
readonly enum: readonly ["COMPLETED", "FAILED", "BLOCKED"];
|
|
84
|
+
readonly description: "COMPLETED=你认为已完成;FAILED=你认为做不成;BLOCKED=被外部阻塞。这是你的声明,最终由 Supervisor 裁定。";
|
|
85
|
+
};
|
|
86
|
+
readonly summary: {
|
|
87
|
+
readonly type: "string";
|
|
88
|
+
readonly description: "给 Supervisor 审查用的自述摘要:做了什么、结果如何、是否满足验收标准。";
|
|
89
|
+
};
|
|
90
|
+
readonly artifacts: {
|
|
91
|
+
readonly type: "array";
|
|
92
|
+
readonly items: {
|
|
93
|
+
readonly type: "string";
|
|
94
|
+
};
|
|
95
|
+
readonly description: "产出物列表(文件路径或制品标识)。";
|
|
96
|
+
};
|
|
97
|
+
readonly risks: {
|
|
98
|
+
readonly type: "array";
|
|
99
|
+
readonly items: {
|
|
100
|
+
readonly type: "string";
|
|
101
|
+
};
|
|
102
|
+
readonly description: "风险与遗留问题。";
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* 把 subagent 交回的 `structured` 收敛成 StructuredResult。
|
|
108
|
+
*
|
|
109
|
+
* provider 已按 outputSchema 校验过,这里是**宿主侧的第二道防御**:
|
|
110
|
+
* 形状不合法就返回 null,调用方据此判定 executor-failure(裁决 6)。
|
|
111
|
+
* 不猜、不补默认值 —— 一个形状不对的 Claim 不是 Claim。
|
|
112
|
+
*/
|
|
113
|
+
export declare function parseStructuredResult(value: unknown): StructuredResult | null;
|
|
114
|
+
/**
|
|
115
|
+
* 构造这一轮 Worker 的 prompt。
|
|
116
|
+
*
|
|
117
|
+
* 裁决 5:REWORK 轮次必须注入「原 Task + Acceptance Criteria +
|
|
118
|
+
* 上一轮 Result 摘要 + Supervisor REWORK reason」。
|
|
119
|
+
* one-shot subagent 不继承父会话上下文,所以 prompt 必须自包含。
|
|
120
|
+
*/
|
|
121
|
+
export declare function buildWorkerPrompt(context: WorkerContext): string;
|