@twinsearth/dsh-master 3.3.2 → 3.3.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -21,8 +21,8 @@ DSH Host
21
21
  │ mirror / essence / derived core(主 Agent 控制面)
22
22
  │ fangForker / fangDecisionCenter ◄────── ├── superagent-router (essence/mother)
23
23
  │ fangSessionCatalog ├── superagent-worker (fork/essence)
24
- │ ├── cost-optimizer (fork)
25
- │ └── dual-manager (mother/essence)
24
+ │ ├── cost-optimizer (fork/mother)
25
+ │ └── dual-manager (mother/essence/fork)
26
26
  └── 协作只走服务键;任一方向失败一律 fail-closed,不本地降级执行
27
27
  ```
28
28
 
@@ -31,10 +31,29 @@ DSH Host
31
31
  | 子插件 | 行 id | 允许角色 | 路线图 | 已交付 |
32
32
  |---|---|---|---|---|
33
33
  | SuperAgent Router | `master-superagent-router` | **essence / mother** | 3.2.1/3.2.3 | Webhook/Ticket/Mandate、渐进信任、本地或母体桥派发、Worker 实时事件、回写 |
34
- | SuperAgent Worker | `master-superagent-worker` | **fork / essence** | 3.2.1 | Ticket 上下文、Mandate 守卫 |
35
- | Worker Agent Loop | `master-superagent-loop` | **fork / essence** | 3.2.3 | tsc 编译 preset;有 Ticket 自动跑多轮工具循环并流式回传 |
36
- | 成本优化 HarnessMM | `master-cost-optimizer` | **fork only** | 3.3.0 | observe 层:token 估算、实测记录 |
37
- | 双主管 PM + PJM | `master-dual-manager` | **mother / essence** | 3.4.0 | PRD/计划/调度/决策中心澄清 |
34
+ | SuperAgent Worker | `master-superagent-worker` | **fork / essence / mother** | 3.2.1 | Ticket 上下文、Mandate 守卫 |
35
+ | Worker Agent Loop | `master-superagent-loop` | **fork / essence / mother** | 3.2.3 | tsc 编译 preset;有 Ticket 自动跑多轮工具循环并流式回传 |
36
+ | 成本优化 HarnessMM | `master-cost-optimizer` | **fork / mother** | 3.3.0 | observe 层:token 估算、实测记录、瓮城回流 |
37
+ | 双主管 PM + PJM | `master-dual-manager` | **mother / essence / fork** | 3.4.0 | PRD/计划/调度/决策中心澄清 |
38
+
39
+ > 角色矩阵自 **3.3.3** 起:**母体全开**(含 Worker/Loop/成本),**分身也挂双主管**;
40
+ > 唯一保留的限制是 **Router 不进 fork**(分身只执行,不注册控制面)。
41
+ > 旧的「母体不接纳执行体」约束已按用户明确要求废止。
42
+
43
+ ## 智能体(3.3.4)
44
+
45
+ 除子插件行之外,房主还提供两个**可选择的 Agent preset**(新会话创建时选择):
46
+
47
+ | 智能体 | preset | 职责 | 工具边界 |
48
+ |---|---|---|---|
49
+ | 产品经理 PM | `master-pm` | 澄清需求、WHY/WHAT、PRD、六类现状盘点 | 只读 fs + 搜索 + 提问 + 交付;**不执行、不派发、不发布** |
50
+ | 项目经理 PJM | `master-pjm` | WBS、七类项目管理、隔离派发、节点汇总、结案 | 只读 fs + 待办 + 交付;派发只走 `fangForker.spawn`,无则 fail-closed |
51
+
52
+ - PM 的“已完成”必须有证据(文件/测试/报告),否则拒收;冲突与歧义必须给用户重新判断的选项。
53
+ - PJM 的结案有门禁:全部步骤 done + 必须写 summary;结案后冻结,不能再派发。
54
+ - 两者共享 `dual-manager` 的结构化状态(PRD / 盘点 / 计划 / 节点 / 结案),文档只写 Session workspace 下的 `docs/pm`、`docs/pjm`。
55
+ - 详见 [智能体设计](docs/AGENTS-PM-PJM.md)。
56
+ - 面板「房主」抽屉新增两张智能体卡片与「智囊团 · Command Center」**只读**摘要(PRD / 盘点 / 计划 / 结案 / 派发可用性),不提供写入口。
38
57
 
39
58
  ## 本地开发
40
59
 
@@ -96,6 +115,8 @@ dsh plugin add <package> # 加载 dist-host/dist-worker
96
115
  - **3.3.1 发布线**:M5 收口 —— 瓮城回流接口升级到 `@twinsearth/dsh-fang-contract` **1.1.0**(`fang.relay.publish` / `fang.relay.status`),房主侧 `src/cost/fang-relay.ts` 做真实探测与投递(只有对端回执才算送达,不可用给具名原因);里程碑 **M1–M5 全部 done**。详见 [M5 回流实测](docs/EXPERIMENT-M5-fang-relay.md)。
97
116
  - **3.3.0 发布线**:成本优化系统层(ledger / budget / policy / audit)+ Worker 四个成本切点 + A1 紧凑化(默认 shadow)+ A2–A3 / B1–B4;真实 A/B 实测 tokens/task 6263 → 3906.75(−37.6%),成功率 0pp 差,详见 [3.3.0 架构设计](docs/DESIGN-v3.3.0-cost-optimization.md)。
98
117
  - 3.2.10 发布线:角色矩阵放宽——Router 进入母体(essence/mother),Worker 与 Worker Agent Loop 进入原身(fork/essence);母体仍不接纳执行体,fork 仍不注册 Router。
118
+ - **3.3.4 发布线(智能体)**:新增两个可选择的 Agent preset —— 产品经理(PM,`master-pm`)与项目经理(PJM,`master-pjm`),共享 `dual-manager` 的结构化 PRD / 六类盘点 / WBS / 结案;面板新增智能体卡片与「智囊团 · Command Center」只读摘要。详见 [智能体设计](docs/AGENTS-PM-PJM.md)、[产品盘点与项目计划](docs/REVIEW-PM-PJM-3.3.4.md)。
119
+ - **3.3.3 发布线(角色矩阵调整,用户明确要求)**:母体全开(Worker / Worker Agent Loop / 成本优化进 mother),分身挂双主管(dual 进 fork);唯一保留的限制是 Router 不进 fork。
99
120
  - 3.2.9 发布线:房主面板改注册到 `shell.overlay` 常驻浮层——点击面板之外不收起、侧栏「房主」按钮二次点击收起、正文独立滚动;新增无浏览器 hook 运行时行为测试(开关、无 outside-click 监听、滚动容器、SSE 失败不卸载面板)。
100
121
  - 3.2.8 发布线:与瓮城通过 fang-master contract 1.0 互联(local/http/file/legacy 四级保底);GitHub Release + NPM public。
101
122
  - 当前工作树完成新一轮审计修复:工具名/死循环阈值、Router 鉴权与生命周期、真实路由分流、契约幂等与超时、PR 护栏与交付、飞书加密、长 Shell 后台作业、成本实测接线、PJM 派生契约。
package/client.js CHANGED
@@ -110,6 +110,50 @@ window.__ModuleLoader__.load({
110
110
  )
111
111
  }
112
112
 
113
+ /** 3.3.4:PM / PJM 智能体卡片(Agent preset,与子插件行分开显示)。 */
114
+ function AgentCard({ item }) {
115
+ return h('div', { style: S.card },
116
+ h('div', { style: S.row },
117
+ h('span', { style: S.name }, item.name),
118
+ tag(item.nameEn),
119
+ tag('preset ' + item.preset),
120
+ h('span', { style: { marginLeft: 'auto', fontSize: 11.5, ...(item.selectable ? S.ok : S.off) } },
121
+ item.selectable ? '可选择' : '本宿主不可用')),
122
+ h('div', { style: S.desc }, item.boundary),
123
+ h('div', { style: S.desc }, '职责:' + (item.responsibilities || []).join(' · ')),
124
+ h('div', { style: S.desc }, '工具:' + (item.tools || []).join(' · ')),
125
+ h('div', { style: S.desc }, item.hint || ''),
126
+ )
127
+ }
128
+
129
+ /**
130
+ * 3.3.4「智囊团」只读摘要:PM/PJM 两个 Agent 共享 dual-manager 的结构化账目。
131
+ * 面板只展示计数与协作状态,不创建 PRD/计划(写入只经 Agent 自己的工具)。
132
+ */
133
+ function CommandCenter({ dual }) {
134
+ const line = (label, value) => h('div', { style: S.desc }, label + ':' + value)
135
+ if (!dual) {
136
+ return h('div', { style: S.card, 'data-dsh-master': 'command-center' },
137
+ h('div', { style: S.row },
138
+ h('span', { style: S.name }, '智囊团 · Command Center'),
139
+ tag('未装配'),
140
+ h('span', { style: { marginLeft: 'auto', fontSize: 11.5, ...S.off } }, '不可用')),
141
+ h('div', { style: S.desc }, 'dual-manager 未启用:PM/PJM 的工具会返回 dual-service-unavailable。'))
142
+ }
143
+ const pm = dual.pm || {}
144
+ const pjm = dual.pjm || {}
145
+ const coop = dual.cooperation || {}
146
+ return h('div', { style: S.card, 'data-dsh-master': 'command-center' },
147
+ h('div', { style: S.row },
148
+ h('span', { style: S.name }, '智囊团 · Command Center'),
149
+ tag('只读'),
150
+ h('span', { style: { marginLeft: 'auto', fontSize: 11.5, ...S.ok } }, dual.ok ? '已装配' : '降级')),
151
+ line('产品经理', 'PRD ' + (pm.prds ?? 0) + ' · 盘点 ' + (pm.assessments ?? 0)),
152
+ line('项目经理', '计划 ' + (pjm.plans ?? 0) + ' · 已结案 ' + (pjm.closed ?? 0)),
153
+ line('协作', '决策中心 ' + (coop.decisionCenter ? '✓' : '×') + ' · 分身派发 ' + (coop.forker ? '✓' : '×')),
154
+ h('div', { style: S.desc }, '面板只读:PRD/计划由 PM/PJM 会话里的工具创建;派发必须经 fangForker。'))
155
+ }
156
+
113
157
  function FangCard({ cooperation }) {
114
158
  if (!cooperation) return null
115
159
  const entries = Object.entries(cooperation.services || {})
@@ -275,7 +319,7 @@ window.__ModuleLoader__.load({
275
319
  return h('div', { style: S.card, 'data-dsh-master': 'cost-block' },
276
320
  h('div', { style: S.row },
277
321
  h('span', { style: S.name }, '成本(只读)'),
278
- tag(block ? '3.3.2' : '未启用', block ? undefined : '#B45309')),
322
+ tag(block ? '3.3.4' : '未启用', block ? undefined : '#B45309')),
279
323
  failed ? h('div', { style: S.error }, '成本接口不可达') : null,
280
324
  !block && !failed ? h('div', { style: S.desc }, '成本系统未装配(cost-optimizer 未启用或角色不匹配)') : null,
281
325
  columns ? h('div', { style: S.desc },
@@ -332,6 +376,8 @@ window.__ModuleLoader__.load({
332
376
  h('div', { style: S.desc }, '运行:' + Math.max(1, Math.round((status.uptimeMs || 0) / 60000)) + ' 分钟'),
333
377
  ) : null,
334
378
  ...items.map((item) => h(SubpluginCard, { key: item.id, item })),
379
+ ...(status && Array.isArray(status.agents) ? status.agents : []).map((item) => h(AgentCard, { key: item.id, item })),
380
+ h(CommandCenter, { dual: status && status.dual }),
335
381
  h(CostCard),
336
382
  h(FangCard, { cooperation: status && status.cooperation }),
337
383
  h(LiveFeed),
package/cordis.patch.yml CHANGED
@@ -69,3 +69,52 @@
69
69
  name: '@twinsearth/dsh-master/dual-manager'
70
70
  config:
71
71
  enabled: true
72
+ # ── 3.3.4:两个可被新会话选择的智能体 preset ────────────────────────────────
73
+ # PM 只拿“读 + 问 + 交付文档”,PJM 只拿“读 + 待办 + 交付 + 共享服务工具”。
74
+ # 两者都不拿 shell / 写文件 / 发布会话,避免智能体绕过 Role Matrix 自行改代码。
75
+ - id: preset-master-pm
76
+ name: '@deepseek-ai/dsh-agent-preset'
77
+ config:
78
+ id: master-pm
79
+ name: 产品经理(PM)
80
+ description: 把需求翻译成 PRD 与六类现状盘点;只读证据、出选择题、写文档,不执行代码。
81
+ order: 20
82
+ plugins:
83
+ - id: pm-agent
84
+ name: '@twinsearth/dsh-master/pm-agent'
85
+ - id: tool-fs
86
+ name: '@deepseek-ai/dsh-tool-fs'
87
+ config:
88
+ read: true
89
+ readImage: true
90
+ write: false
91
+ edit: false
92
+ - id: tool-fs-search
93
+ name: '@deepseek-ai/dsh-tool-fs-search'
94
+ - id: tool-ask-user
95
+ name: '@deepseek-ai/dsh-tool-ask-user'
96
+ - id: tool-present
97
+ name: '@deepseek-ai/dsh-tool-present'
98
+ - id: preset-master-pjm
99
+ name: '@deepseek-ai/dsh-agent-preset'
100
+ config:
101
+ id: master-pjm
102
+ name: 项目经理(PJM)
103
+ description: 把 PRD 拆成 WBS 与七类项目管理内容;隔离派发、节点汇总、结案汇报。
104
+ order: 21
105
+ plugins:
106
+ - id: pjm-agent
107
+ name: '@twinsearth/dsh-master/pjm-agent'
108
+ - id: tool-fs
109
+ name: '@deepseek-ai/dsh-tool-fs'
110
+ config:
111
+ read: true
112
+ readImage: true
113
+ write: false
114
+ edit: false
115
+ - id: tool-fs-search
116
+ name: '@deepseek-ai/dsh-tool-fs-search'
117
+ - id: tool-todo
118
+ name: '@deepseek-ai/dsh-tool-todo'
119
+ - id: tool-present
120
+ name: '@deepseek-ai/dsh-tool-present'
@@ -0,0 +1,46 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // Copyright (c) 2026 dsh-Master contributors — see LICENSE-CORE.
3
+ // @ts-nocheck
4
+ import { PJM_SYSTEM_PROMPT } from "./prompts.js";
5
+ import { dualService, dualUnavailable, id, installAgent, jsonTool, objectSchema, objects, string, strings } from "./toolkit.js";
6
+ export const name = 'dsh-master-pjm-agent';
7
+ export const inject = ['systemPrompt', 'tools'];
8
+ /** PJM preset:项目计划、节点、隔离派发、结案;不替 PM 决定需求。 */
9
+ export function apply(ctx) {
10
+ const dual = () => dualService(ctx);
11
+ const tools = [
12
+ jsonTool('pjm_create_plan', '把已确认 PRD 拆成 WBS 与七类项目计划,返回 plan id;估时必须以分钟给出。', objectSchema({
13
+ prdId: id('PRD id'), title: string('项目计划标题'), how: string('实施路径'), where: string('执行场景'),
14
+ projectReview: strings('项目会审结论'), requirements: strings('项目需求与内容'),
15
+ steps: objects('WBS:id/title/prompt/owner/where/estimateMinutes/dependsOn/acceptance/kind'),
16
+ schedule: { type: 'object', description: 'startAt 与 milestones[](title/owner/estimateMinutes/dependsOn)', additionalProperties: true },
17
+ team: objects('人员:role/owner/responsibility'),
18
+ quality: { type: 'object', description: 'tests[] / deployment[] / acceptance[]', additionalProperties: true },
19
+ }, ['prdId', 'steps']), async (args) => dual() ? dual().pjm.plan(args.prdId, args) : dualUnavailable()),
20
+ jsonTool('pjm_dispatch_step', '通过 fangForker.spawn 在隔离分身中执行一个 WBS 步骤;不可用会 fail-closed,不在宿主本地执行。', objectSchema({
21
+ planId: id('计划 id'), stepId: id('步骤 id'),
22
+ }, ['planId', 'stepId']), async (args) => dual() ? await dual().pjm.dispatch(args.planId, { stepId: args.stepId }) : dualUnavailable()),
23
+ jsonTool('pjm_complete_step', '记录步骤完成/失败、证据与实际用时;不得把失败写成完成。', objectSchema({
24
+ planId: id('计划 id'), stepId: id('步骤 id'),
25
+ status: { type: 'string', enum: ['done', 'failed'] }, summary: string('结果摘要'),
26
+ evidence: strings('测试/文件/报告证据'), actualMinutes: { type: 'number', description: '实际用时(分钟)' },
27
+ }, ['planId', 'stepId', 'status']), async (args) => dual() ? dual().pjm.completeStep(args.planId, args.stepId, args) : dualUnavailable()),
28
+ jsonTool('pjm_project_status', '读取项目看板:完成/进行/待办/失败、估时、实际用时、是否可结案。', objectSchema({ planId: id('计划 id') }, ['planId']), async (args) => dual() ? dual().pjm.progress(args.planId) : dualUnavailable()),
29
+ jsonTool('pjm_write_project', '把项目计划与当前节点写成 workspace/docs/pjm 下的 Markdown。', objectSchema({ planId: id('计划 id') }, ['planId']), async (args, exec) => dual() ? await dual().pjm.writeProject(args.planId, { agent: exec.agent }) : dualUnavailable()),
30
+ jsonTool('pjm_close_project', '全部步骤完成后结案:写入总结、评审报告、验收与资料清单,并可生成结案文档。', objectSchema({
31
+ planId: id('计划 id'), summary: string('结案总结(必填)'), reviewReport: string('评审报告'),
32
+ acceptance: strings('验收记录'), artifacts: strings('项目资料路径'), write: { type: 'boolean', description: '是否同时写结案 Markdown,默认 true' },
33
+ }, ['planId', 'summary']), async (args, exec) => {
34
+ if (!dual())
35
+ return dualUnavailable();
36
+ const closed = dual().pjm.close(args.planId, args);
37
+ if (!closed.ok)
38
+ return closed;
39
+ if (args.write === false)
40
+ return closed;
41
+ const document = await dual().pjm.writeClosure(args.planId, { agent: exec.agent });
42
+ return { ...closed, document };
43
+ }),
44
+ ];
45
+ return installAgent(ctx, { prompt: PJM_SYSTEM_PROMPT, tools });
46
+ }
@@ -0,0 +1,35 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // Copyright (c) 2026 dsh-Master contributors — see LICENSE-CORE.
3
+ // @ts-nocheck
4
+ import { PM_SYSTEM_PROMPT } from "./prompts.js";
5
+ import { dualService, dualUnavailable, id, installAgent, jsonTool, objectSchema, objects, string, strings } from "./toolkit.js";
6
+ export const name = 'dsh-master-pm-agent';
7
+ export const inject = ['systemPrompt', 'tools'];
8
+ /** PM preset:只提供需求/盘点/文档工具,不提供任何执行或派生能力。 */
9
+ export function apply(ctx) {
10
+ const dual = () => dualService(ctx);
11
+ const tools = [
12
+ jsonTool('pm_create_prd', '创建结构化产品需求文档(WHY/WHAT/模块/约束/验收),返回 PRD id。', objectSchema({
13
+ title: string('需求标题'), goal: string('要解决的问题与目标'),
14
+ why: string('背景与价值'), what: strings('功能范围'), architecture: string('架构说明'),
15
+ outcomes: strings('预期结果'), modules: objects('模块:name/result/architecture/status'),
16
+ priority: { type: 'string', enum: ['low', 'normal', 'high', 'critical'] },
17
+ constraints: strings('约束'), acceptance: strings('验收标准'), nonGoals: strings('非目标'),
18
+ assumptions: strings('假设'), openQuestions: strings('待澄清问题'), requester: string('请求方'),
19
+ }, ['title', 'goal']), async (args) => dual() ? dual().pm.createPrd(args) : dualUnavailable()),
20
+ jsonTool('pm_create_assessment', '保存六类产品现状盘点。completed 每项必须附 evidence;其它类给原因、计划、冲突或歧义。', objectSchema({
21
+ prdId: id('可选:关联的 PRD id'), title: string('盘点标题'), why: string('目标'), what: strings('范围'),
22
+ completed: objects('已完成:title/progress/evidence[](证据必填)'),
23
+ unfinished: objects('未完成:title/reason/nextStep/progress'),
24
+ blocked: objects('受阻:title/reason/blocker/nextStep'),
25
+ planned: objects('已计划:title/plan/estimateMinutes/priority'),
26
+ conflicts: objects('冲突:title/question/options[{value,label,consequence}]'),
27
+ ambiguities: objects('歧义:title/question/currentInterpretation'),
28
+ }, ['title']), async (args) => dual() ? dual().pm.createAssessment(args) : dualUnavailable()),
29
+ jsonTool('pm_write_prd', '把 PRD(可带六类盘点)写成 workspace/docs/pm 下的 Markdown,返回文档路径。', objectSchema({
30
+ prdId: id('PRD id'), assessmentId: id('可选:关联的 Assessment id'),
31
+ }, ['prdId']), async (args, exec) => dual() ? await dual().pm.writePrd(args.prdId, { assessmentId: args.assessmentId, agent: exec.agent }) : dualUnavailable()),
32
+ jsonTool('pm_list_prds', '列出当前 Host 中的 PRD 与六类盘点索引。', objectSchema({}), async () => dual() ? ({ ok: true, prds: dual().pm.listPrds().items, assessments: dual().pm.listAssessments().items }) : dualUnavailable()),
33
+ ];
34
+ return installAgent(ctx, { prompt: PM_SYSTEM_PROMPT, tools });
35
+ }
@@ -0,0 +1,61 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // Copyright (c) 2026 dsh-Master contributors — see LICENSE-CORE.
3
+ export const PM_AGENT_ID = 'master-pm';
4
+ export const PJM_AGENT_ID = 'master-pjm';
5
+ export const PM_SYSTEM_PROMPT = String.raw `# Role
6
+ 你是 DSH-Master「房主」的产品经理 Agent(PM,preset: master-pm)。你把用户和开发者的大白话转换成可核对、可验收的产品需求与文档。
7
+
8
+ # Goals — WHY / WHAT
9
+ 1. WHY:说清解决什么问题、为什么值得做、达成什么目标。
10
+ 2. WHAT:说清功能、模块、架构、预期结果、约束、非目标和验收标准。
11
+ 3. 对现状做证据驱动的六类盘点:已完成、未完成、无法实现/受阻、已计划、冲突、歧义。
12
+
13
+ # Workflow
14
+ 1. 先识别目标、范围、约束和未决项;重大歧义或不可逆选择必须用 ask_user_question 提出一个具体决策点,给 2–4 个选项并标推荐项。不要为琐碎实现细节打断用户。
15
+ 2. 盘点前先用 read/grep/glob 读取真实代码、测试和文档。没有证据的“完成”不得写 100%,不得把设计稿当成已交付。
16
+ 3. 用 pm_create_prd 保存结构化 PRD;用 pm_create_assessment 保存六类盘点。每个 completed 项必须附文件、测试或报告证据。
17
+ 4. 用 pm_write_prd 生成 Markdown 文档;需要用户打开时用 present 交付。需要 PJM 执行时,只移交 PRD id、验收标准、未决问题,不代替 PJM 派工。
18
+
19
+ # Rules
20
+ - 不臆测、不伪造证据、不把“可以实现”写成“已经完成”。
21
+ - 冲突必须列出冲突双方、影响、可选方案和需要用户重新判断的问题。
22
+ - 歧义必须写出当前理解与待确认问题;用户未回答前不得把该理解当成事实。
23
+ - PM 不写生产代码、不跑 shell、不发布、不安装、不调度 VM/分身。
24
+ - 只写 PRD/盘点文档;不改需求范围外的文件。
25
+
26
+ # Output Format
27
+ 默认用简洁 Markdown 汇报,必须按任务需要包含:
28
+ - WHY & WHAT
29
+ - 六类现状矩阵:completed / unfinished / blocked / planned / conflicts / ambiguities
30
+ - 冲突与歧义的用户决策点
31
+ - 已保存的 PRD/Assessment id 与文档路径
32
+ 只输出已核实内容;未知、未验证、受限分别标明。`;
33
+ export const PJM_SYSTEM_PROMPT = String.raw `# Role
34
+ 你是 DSH-Master「房主」的项目经理 Agent(PJM,preset: master-pjm)。你把已确认的 PRD 变成可执行、可追踪、可验收的项目计划,并按节点汇报直至结案。
35
+
36
+ # Goals — HOW / WHO / WHERE / WHEN
37
+ 1. HOW:把 PRD 拆成 WBS,每步写清 prompt、依赖、验收、估时。
38
+ 2. WHO:给每一步分配 owner 与责任;未分配必须写 unassigned,不得虚构人员。
39
+ 3. WHERE:明确执行场景(宿主、fork、workspace、DSH Web);脏任务只能在隔离执行体中运行。
40
+ 4. WHEN:给里程碑与估时;估时是计划,不是承诺工期。
41
+
42
+ # Workflow
43
+ 1. 项目会审:核对 PRD、验收标准、约束、未决问题;未决项未解决不得自行扩权。
44
+ 2. 用 pjm_create_plan 建立七类项目内容:项目会审、WBS、需求内容、时间、人员、测试部署验收、结案。
45
+ 3. 执行前申请隔离资源,通过 pjm_dispatch_step 走 fangForker.spawn;不可用就 fail-closed,绝不在母体/原身本地降级执行,不虚构 vm_create/vm_exec。
46
+ 4. 用 pjm_project_status 汇总节点,用 todo_write 跟踪当前工作;完成步骤时记录 summary/evidence/actualMinutes。
47
+ 5. 用 pjm_write_project 保存计划与节点报告。全部步骤完成后才能 pjm_close_project;结案必须有总结、验收记录、评审报告和资料清单,并交付文档。
48
+
49
+ # Rules
50
+ - 按时、按质、按量报告真实状态;失败、阻塞、未测量不得包装成完成。
51
+ - 不发布、不部署到生产、不修改 profile、不触碰凭据,除非用户对该动作另行明确授权。
52
+ - 不代替 PM 决定需求,不替用户回答选择题;阻塞时给出原因、影响、可选方案和所需决策。
53
+ - 不做无限轮询;节点汇报写完成/进行/待办/失败及下一步。
54
+
55
+ # Output Format
56
+ - 项目会审与范围
57
+ - WBS 看板(Todo / In Progress / Done / Failed)
58
+ - Owner、场景、估时、实际用时与依赖
59
+ - 测试/部署/交付/验收证据
60
+ - 结案:评审报告、验收记录、资料汇总
61
+ - 仍需人类决策的事项(如有)`;
@@ -0,0 +1,64 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // Copyright (c) 2026 dsh-Master contributors — see LICENSE-CORE.
3
+ // @ts-nocheck
4
+ import { SERVICE, masterError, safeGet } from "../kernel.js";
5
+ export function dualService(ctx) {
6
+ const service = safeGet(ctx, SERVICE.dual);
7
+ return service && service.pm && service.pjm ? service : null;
8
+ }
9
+ export function dualUnavailable() {
10
+ return masterError('dual-service-unavailable', 'dshMasterDual is required; enable master-dual-manager in this Host');
11
+ }
12
+ /** 注册一个返回结构化 JSON 的模型工具;异常也转成具名错误,不吞掉。 */
13
+ export function jsonTool(name, description, parameters, execute) {
14
+ return {
15
+ name,
16
+ description,
17
+ parameters,
18
+ output: {
19
+ schema: { type: 'object' },
20
+ render: (_args, value) => [{ type: 'text', text: JSON.stringify(value) }],
21
+ },
22
+ async execute(args, exec) {
23
+ try {
24
+ return await execute(args && typeof args === 'object' ? args : {}, exec);
25
+ }
26
+ catch (error) {
27
+ return masterError('agent-tool-threw', String(error?.message ?? error), { tool: name });
28
+ }
29
+ },
30
+ };
31
+ }
32
+ /** 在 preset scope 内挂 persona 与 scoped tools,全部纳入 Cordis effect 生命周期。 */
33
+ export function installAgent(ctx, { prompt, tools }) {
34
+ const systemPrompt = safeGet(ctx, 'systemPrompt');
35
+ const registry = safeGet(ctx, 'tools');
36
+ if (!systemPrompt || typeof systemPrompt.section !== 'function') {
37
+ return { ok: false, code: 'system-prompt-unavailable', error: 'ctx.systemPrompt with section() is required' };
38
+ }
39
+ if (!registry || typeof registry.register !== 'function') {
40
+ return { ok: false, code: 'tools-unavailable', error: 'ctx.tools with register() is required' };
41
+ }
42
+ const registered = [];
43
+ try {
44
+ ctx.effect(() => systemPrompt.section({ name: 'deployment:persona-prefix', order: 0, text: prompt }));
45
+ }
46
+ catch (error) {
47
+ return { ok: false, code: 'prompt-registration-failed', error: String(error?.message ?? error) };
48
+ }
49
+ for (const definition of tools) {
50
+ try {
51
+ ctx.effect(() => registry.register(definition));
52
+ registered.push(definition.name);
53
+ }
54
+ catch (error) {
55
+ return { ok: false, code: 'tool-registration-failed', error: String(error?.message ?? error), tool: definition.name, registered };
56
+ }
57
+ }
58
+ return { ok: true, tools: registered };
59
+ }
60
+ export const objectSchema = (properties = {}, required = []) => ({ type: 'object', properties, required, additionalProperties: true });
61
+ export const string = (description = '') => ({ type: 'string', description });
62
+ export const strings = (description = '') => ({ type: 'array', items: { type: 'string' }, description });
63
+ export const objects = (description = '') => ({ type: 'array', items: { type: 'object', additionalProperties: true }, description });
64
+ export const id = (description) => ({ type: 'string', description });
@@ -0,0 +1,107 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-or-later
2
+ // Copyright (c) 2026 dsh-Master contributors — see LICENSE-CORE.
3
+ // @ts-nocheck
4
+ import { ASSESSMENT_CATEGORIES, planProgress } from "./schema.js";
5
+ const lines = (items, render) => items.length ? items.map(render).join('\n') : '- (无)';
6
+ const bullets = (items) => lines(items, (item) => `- ${item}`);
7
+ const cell = (value) => String(value ?? '—').replaceAll('|', '\\|').replaceAll('\n', ' ');
8
+ const CATEGORY_TITLES = {
9
+ completed: '1. 已完成(进度与证据)',
10
+ unfinished: '2. 未完成(原因与后续)',
11
+ blocked: '3. 无法实现 / 受阻(卡点)',
12
+ planned: '4. 已计划待开发',
13
+ conflicts: '5. 相互矛盾 / 冲突(待重新判断)',
14
+ ambiguities: '6. 理解歧义(待确认)',
15
+ };
16
+ export function renderPrdDocument(prd, assessment = null) {
17
+ return [
18
+ `# PRD · ${prd.title}`, '',
19
+ `- 文档 ID:\`${prd.id}\``,
20
+ `- 状态:${prd.status}`,
21
+ `- 优先级:${prd.priority}`,
22
+ `- 请求方:${prd.requester}`, '',
23
+ '## WHY · 解决什么问题、达成什么目标', '',
24
+ prd.why || prd.goal, '',
25
+ `**目标**:${prd.goal}`, '',
26
+ '## WHAT · 功能、模块、架构与结果', '',
27
+ bullets(prd.what), '',
28
+ '### 架构', '', prd.architecture || '(待补充)', '',
29
+ '### 模块', '',
30
+ lines(prd.modules, (item) => `- **${item.name}**(${item.status}):${item.result || item.architecture || '待补充'}`), '',
31
+ '### 预期结果', '', bullets(prd.outcomes), '',
32
+ '## 约束与非目标', '',
33
+ '### 约束', '', bullets(prd.constraints), '',
34
+ '### 非目标', '', bullets(prd.nonGoals), '',
35
+ '## 验收标准', '', bullets(prd.acceptance), '',
36
+ '## 假设与待澄清问题', '',
37
+ '### 假设', '', bullets(prd.assumptions), '',
38
+ '### 待澄清', '', bullets(prd.openQuestions), '',
39
+ assessment ? `\n---\n\n${renderAssessmentDocument(assessment)}` : '',
40
+ ].join('\n').trim() + '\n';
41
+ }
42
+ export function renderAssessmentDocument(assessment) {
43
+ const out = [`# 产品现状盘点 · ${assessment.title}`, '', `- 盘点 ID:\`${assessment.id}\``, `- 关联 PRD:${assessment.prdId ?? '未关联'}`, ''];
44
+ if (assessment.why)
45
+ out.push('## WHY', '', assessment.why, '');
46
+ if (assessment.what.length)
47
+ out.push('## WHAT', '', bullets(assessment.what), '');
48
+ for (const category of ASSESSMENT_CATEGORIES) {
49
+ out.push(`## ${CATEGORY_TITLES[category]}`, '');
50
+ const items = assessment[category];
51
+ out.push(lines(items, (item) => {
52
+ if (category === 'completed')
53
+ return `- **${item.title}**(${item.progress}%)${item.detail ? `:${item.detail}` : ''}\n - 证据:${item.evidence.join(';')}`;
54
+ if (category === 'unfinished')
55
+ return `- **${item.title}**:${item.reason}\n - 后续:${item.nextStep || '待计划'}`;
56
+ if (category === 'blocked')
57
+ return `- **${item.title}**:${item.reason}\n - 卡点:${item.blocker}\n - 下一步:${item.nextStep || '待决策'}`;
58
+ if (category === 'planned')
59
+ return `- **${item.title}**(${item.priority}):${item.plan || '待拆解'}${item.estimateMinutes ? `\n - 估时:${item.estimateMinutes} 分钟` : ''}`;
60
+ if (category === 'conflicts')
61
+ return `- **${item.title}**:${item.question}\n - 选项:${item.options.map((option) => `${option.value}=${option.label}`).join(';') || '待补充'}`;
62
+ return `- **${item.title}**:${item.question}\n - 当前理解:${item.currentInterpretation || '未形成结论'}`;
63
+ }), '');
64
+ }
65
+ return out.join('\n').trim() + '\n';
66
+ }
67
+ export function renderProjectDocument(plan) {
68
+ const progress = planProgress(plan);
69
+ return [
70
+ `# 项目计划 · ${plan.title}`, '',
71
+ `- 计划 ID:\`${plan.id}\``,
72
+ `- 关联 PRD:\`${plan.prdId}\``,
73
+ `- 状态:${plan.status}`,
74
+ `- 进度:${progress.done}/${progress.total}(${progress.percent}%)`,
75
+ `- 总估时:${progress.estimatedMinutes} 分钟(估算,不是承诺工期)`, '',
76
+ '## 1. 项目会审', '', bullets(plan.projectReview), '',
77
+ '## 2. 项目计划(HOW / WHERE)', '', plan.how || '(待补充)', '', `**场景**:${plan.where}`, '',
78
+ '### WBS', '',
79
+ '| 步骤 | 内容 | Owner | 场景 | 估时(分钟) | 依赖 | 状态 |',
80
+ '|---|---|---|---|---:|---|---|',
81
+ ...plan.steps.map((step) => `| ${cell(step.id)} | ${cell(step.title)} | ${cell(step.owner)} | ${cell(step.where)} | ${step.estimateMinutes} | ${cell(step.dependsOn.join(', '))} | ${cell(step.status)} |`), '',
82
+ '## 3. 项目需求与内容', '', bullets(plan.requirements), '',
83
+ '## 4. 项目时间', '',
84
+ `- 开始时间:${plan.schedule.startAt ?? '待定'}`,
85
+ `- 总估时:${plan.schedule.totalEstimateMinutes} 分钟`, '',
86
+ lines(plan.schedule.milestones, (item) => `- **${item.title}**:${item.estimateMinutes} 分钟 · ${item.owner} · 依赖 ${item.dependsOn.join(', ') || '无'}`), '',
87
+ '## 5. 项目人员', '',
88
+ lines(plan.team, (item) => `- **${item.role}** · ${item.owner}:${item.responsibility || '待分配'}`), '',
89
+ '## 6. 测试、部署、交付与验收', '',
90
+ '### 测试', '', bullets(plan.quality.tests), '',
91
+ '### 部署', '', bullets(plan.quality.deployment), '',
92
+ '### 验收', '', bullets(plan.quality.acceptance), '',
93
+ '## 7. 项目结案', '',
94
+ plan.closure.status === 'closed' ? renderClosureDocument(plan) : '- 尚未结案;必须全部步骤完成后才能结案。',
95
+ ].join('\n').trim() + '\n';
96
+ }
97
+ export function renderClosureDocument(plan) {
98
+ const closure = plan.closure;
99
+ return [
100
+ `### 结案状态:${closure.status}`, '',
101
+ `- 结案时间:${closure.closedAt ?? '未结案'}`,
102
+ `- 总结:${closure.summary || '未填写'}`, '',
103
+ '#### 评审报告', '', closure.reviewReport || '(待补充)', '',
104
+ '#### 验收记录', '', bullets(closure.acceptance), '',
105
+ '#### 项目资料汇总', '', bullets(closure.artifacts), '',
106
+ ].join('\n');
107
+ }