@zhushanwen/pi-plan 0.5.2 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -9
- package/package.json +13 -11
- package/skills/plan-ext-config/SKILL.md +33 -18
- package/src/__tests__/active-path.test.ts +204 -0
- package/src/__tests__/command.test.ts +102 -32
- package/src/__tests__/enter.test.ts +58 -20
- package/src/__tests__/exec-options-alignment.test.ts +56 -0
- package/src/__tests__/execution-notice.test.ts +197 -0
- package/src/__tests__/{compact.test.ts → extract-plan-steps.test.ts} +24 -15
- package/src/__tests__/fsm-consumption.test.ts +388 -0
- package/src/__tests__/index.test.ts +175 -75
- package/src/__tests__/prompts.test.ts +66 -0
- package/src/__tests__/review-contract-boundary.test.ts +123 -0
- package/src/__tests__/review.test.ts +264 -97
- package/src/__tests__/skills.test.ts +14 -5
- package/src/__tests__/state.test.ts +254 -52
- package/src/__tests__/{compact-criteria-array.test.ts → success-criteria-array.test.ts} +29 -70
- package/src/__tests__/templates.test.ts +8 -25
- package/src/__tests__/tool.test.ts +269 -121
- package/src/command.ts +42 -34
- package/src/enter.ts +31 -50
- package/src/{compact.ts → execution-notice.ts} +30 -118
- package/src/index.ts +100 -88
- package/src/prompts.ts +42 -12
- package/src/state.ts +290 -87
- package/src/templates.ts +0 -15
- package/src/tool.ts +665 -257
- package/src/__tests__/compact-handler.test.ts +0 -348
- package/src/__tests__/exec-skills.test.ts +0 -397
- package/src/exec-skills.ts +0 -407
package/src/index.ts
CHANGED
|
@@ -1,138 +1,150 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
BeforeAgentStartEvent,
|
|
3
|
-
BeforeAgentStartEventResult,
|
|
4
2
|
ExtensionAPI,
|
|
5
3
|
ExtensionContext,
|
|
6
4
|
} from "@earendil-works/pi-coding-agent";
|
|
7
5
|
|
|
8
|
-
import { getLogger } from "@zhushanwen/pi-extension-logger";
|
|
9
|
-
|
|
10
6
|
import { registerPlanCommand } from "./command.js";
|
|
11
|
-
import { registerPlanEventHandlers } from "./compact.js";
|
|
12
7
|
import {
|
|
13
|
-
type PlanAbortControllers,
|
|
14
|
-
type PlanSessionMap,
|
|
15
8
|
PLAN_CONTEXT_CUSTOM_TYPE,
|
|
16
9
|
PLAN_MODE_TOOLS,
|
|
10
|
+
applyPlanEvent,
|
|
11
|
+
createPlanCtx,
|
|
12
|
+
isTaijiGuiHost,
|
|
17
13
|
persistPlanState,
|
|
18
14
|
reconstructPlanState,
|
|
19
15
|
} from "./state.js";
|
|
20
16
|
import { registerPlanTool } from "./tool.js";
|
|
21
17
|
import { updatePlanWidget } from "./widget.js";
|
|
22
18
|
|
|
23
|
-
const logger = getLogger("pi-plan");
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* D9 引导文案(约 60 token):仅在 taiji 宿主注入。plan 模式是只读子集(读代码、产
|
|
27
|
-
* 文档、不改源码),进入它不是危险操作——agent 可在合适时机**自行进入**(plan-mode-
|
|
28
|
-
* agent-enter U1 后 enter 是 tool action,无需用户确认;用户随时可经 PlanModeBar 退出)。
|
|
29
|
-
*/
|
|
30
|
-
const PLAN_MODE_SUGGESTION_PROMPT =
|
|
31
|
-
"\n\n" +
|
|
32
|
-
"When the user's request involves large-scale refactoring, cross-module changes, or other high-risk modifications, " +
|
|
33
|
-
"proactively enter plan mode yourself by calling plan(action='enter', requirement='<the task>', skills=[...relevant skills]). " +
|
|
34
|
-
"Plan mode is read-only for source code: you explore and write plan documents, then the user reviews before implementation. " +
|
|
35
|
-
"Do not ask for permission to enter — entering plan mode is safe and reversible (the user can exit anytime).";
|
|
36
|
-
|
|
37
19
|
export default function planExtension(pi: ExtensionAPI) {
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
const
|
|
20
|
+
// 单 ctx 对象(D-B4-3):states(per-session 生命周期态缓存)+ controllers(挂起
|
|
21
|
+
// select 注册表——E10:submit-review 审批 + complete 执行方式选择两处共用;值 =
|
|
22
|
+
// PendingSelect 含解散来源槽位,D-B1-2;发挂起 select 前 fresh,settled/重建/关闭
|
|
23
|
+
// 即弃)两表收敛 + dissolveAll 解散方法(exitPlanMode 入口经它打 'self' 标)
|
|
24
|
+
const planCtx = createPlanCtx();
|
|
43
25
|
|
|
44
26
|
// Register tool and command
|
|
45
|
-
registerPlanTool(pi,
|
|
46
|
-
registerPlanCommand(pi,
|
|
47
|
-
|
|
48
|
-
// Register compact/tree event handlers
|
|
49
|
-
registerPlanEventHandlers(pi, sessions);
|
|
27
|
+
registerPlanTool(pi, planCtx);
|
|
28
|
+
registerPlanCommand(pi, planCtx);
|
|
50
29
|
|
|
51
30
|
// Reconstruct state on session start
|
|
52
31
|
pi.on("session_start", async (_event: unknown, ctx: ExtensionContext) => {
|
|
53
32
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
54
33
|
const state = reconstructPlanState(ctx);
|
|
55
|
-
|
|
34
|
+
planCtx.states.set(sessionId, state);
|
|
56
35
|
// session 重建即弃旧 controller:挂起 select 只存在于 tool execute 进行中的
|
|
57
36
|
// turn,pi 重生后不可能有存活的 select;残留的已 abort controller 禁止复用
|
|
58
37
|
// (发起新挂起 select 会 fresh 新建,此处删除是防御性清理)
|
|
59
|
-
|
|
38
|
+
planCtx.controllers.delete(sessionId);
|
|
60
39
|
updatePlanWidget(ctx, state);
|
|
61
40
|
// If plan mode was active, re-restrict tools to the plan-mode set (includes bash — file-write constraints come from the injected plan mode prompt)
|
|
62
41
|
if (state.isActive) {
|
|
63
42
|
pi.setActiveTools(PLAN_MODE_TOOLS);
|
|
64
43
|
|
|
65
|
-
// E3
|
|
66
|
-
// pi 懒重生之后(taiji 冷启动不拉 pi 进程——用户重开 session
|
|
67
|
-
// 才重生、hook
|
|
68
|
-
|
|
69
|
-
//
|
|
70
|
-
//
|
|
44
|
+
// E3 按 state 查表恢复(D5/D9④):挂起交互点(审批 select / 执行方式 form)随 pi
|
|
45
|
+
// 进程消亡。本 hook 跑在 pi 懒重生之后(taiji 冷启动不拉 pi 进程——用户重开 session
|
|
46
|
+
// 发首条消息时 pi 才重生、hook 才跑),此时冷启动扫描已恢复持久态但挂起交互不复存在
|
|
47
|
+
//(上方 delete 已清残留 controller,pi 重生后不可能有存活的挂起)→ 按 state 查表
|
|
48
|
+
// steer 恢复(恢复动作全部在 extension 侧,前端不造失败信号链;旧 entry 无 state 字段
|
|
49
|
+
// 经 reconstructPlanState 的 reviewState 映射后同样落本表,D2 读方①):
|
|
50
|
+
// - reviewing:按宿主分流(F-W3-1,见分支内注释);
|
|
71
51
|
// - revising:继续按评论修订并重新提交(P1-1:原实现只覆盖 awaiting,revising
|
|
72
|
-
//
|
|
73
|
-
//
|
|
74
|
-
//
|
|
75
|
-
//
|
|
76
|
-
|
|
77
|
-
|
|
52
|
+
// 崩溃后审批条恒显假「修订中」且无任何恢复指引);
|
|
53
|
+
// - dispatching(D5 E3 新支,覆盖现状自认的不可恢复窗口):崩溃消散的表单即
|
|
54
|
+
// 「无选择解散」极端形态——先走 review_aborted 边落 approved(批准事实保留,
|
|
55
|
+
// 且使重调 complete 的 approve 边合法),再 steer 重调 complete 重新选执行方式。
|
|
56
|
+
// 其余态(planning/approved/终态)不打扰(S16「重开 session 无 E3 重挂打扰」)。
|
|
57
|
+
if (state.state === "reviewing") {
|
|
58
|
+
// F-W3-1 审批条复活拦截(S8①/S2「stop 后不复活」+ D8/S6 恢复语义):taiji GUI
|
|
59
|
+
// 宿主下 E3 **不自动 steer 重挂**——steer triggerTurn 是无人值守自动重提通道
|
|
60
|
+
// (pi 进程重启 → agent 自动 submit-review → 新 select 挂起 → 审批条复活,
|
|
61
|
+
// 用户停止/搁置的审批在无任何用户输入介入时自己回来)。宿主形态只落
|
|
62
|
+
// resumeHint='resubmit':renderer degraded 分支(state=reviewing ∧ 无挂起,
|
|
63
|
+
// PlanReviewBar)据此呈现「审批提问已随会话重启失效 + 重新提交审批」按钮,
|
|
64
|
+
// 用户点击(= 用户输入介入)才重提——agent 侧 submit-review 对 reviewing 降级
|
|
65
|
+
// 态的自环重挂照常放行,恢复触发权归用户。独立 pi(TUI)没有 degraded 按钮,
|
|
66
|
+
// 自动重挂是唯一恢复路径(F8 死路防护),steer 保留。
|
|
67
|
+
// 分流判定与 executeSubmitReview 的 E8 宿主分流同源(isTaijiGuiHost,D-B1-7)。
|
|
68
|
+
if (!isTaijiGuiHost(ctx)) {
|
|
69
|
+
// D9④:E3 重挂是唯一「不重新思考」场景——steer 原样回传上轮 selfReview 全文,
|
|
70
|
+
// 豁免只发生在「自审内容」(复用既有结论),不存在绕过「过门义务」的路径
|
|
71
|
+
const carried =
|
|
72
|
+
state.selfReview ?? "(none recorded — write a brief self-review before re-submitting)";
|
|
73
|
+
pi.sendMessage(
|
|
74
|
+
{
|
|
75
|
+
customType: PLAN_CONTEXT_CUSTOM_TYPE,
|
|
76
|
+
content:
|
|
77
|
+
"[PLAN MODE] A previous review request was interrupted (session restarted). " +
|
|
78
|
+
"The registered documents are still waiting for user approval. " +
|
|
79
|
+
"The self-review for this exact document set is already done — carry it back VERBATIM as the selfReview parameter, no need to redo the thinking:\n\n" +
|
|
80
|
+
`<self-review>\n${carried}\n</self-review>\n\n` +
|
|
81
|
+
"Call plan(action='submit-review', selfReview='<the text above>') now to re-hang the review dialog.",
|
|
82
|
+
display: false,
|
|
83
|
+
},
|
|
84
|
+
{ deliverAs: "steer", triggerTurn: true },
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
// E3 重挂即落 'resubmit' resumeHint + persist(此处原只发 steer 不落盘,renderer
|
|
88
|
+
// 冷启动扫描的 View 恒无 hint、恒渲染通用文案;落盘后才能渲染「会话已重启,
|
|
89
|
+
// 尚未重新提交」分支)。无条件覆盖既有 hint 为有意——「会话已重启」语义
|
|
90
|
+
// 优先成立(P3-9 登记);清除 = submit-review 重挂起点(clearRoundFields 单函数
|
|
91
|
+
// 出口,D4)。宿主形态该 hint 即 degraded 分源文案的判定源(无 steer,按钮文案依赖它)。
|
|
92
|
+
state.resumeHint = "resubmit";
|
|
93
|
+
persistPlanState(pi, state);
|
|
94
|
+
} else if (state.state === "revising") {
|
|
78
95
|
pi.sendMessage(
|
|
79
96
|
{
|
|
80
97
|
customType: PLAN_CONTEXT_CUSTOM_TYPE,
|
|
81
|
-
content:
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
98
|
+
content:
|
|
99
|
+
"[PLAN MODE] A revision round was interrupted (session restarted). The user's revision comments were already injected — continue handling them: rewrite the documents, re-register each revised document via plan(action='register-doc'), then re-submit via plan(action='submit-review', selfReview='<a fresh self-review for the revised documents>').",
|
|
100
|
+
display: false,
|
|
101
|
+
},
|
|
102
|
+
{ deliverAs: "steer", triggerTurn: true },
|
|
103
|
+
);
|
|
104
|
+
// revising 恢复不落 resumeHint('resubmit' 只描述 reviewing 降级等待;revising 由
|
|
105
|
+
// steer triggerTurn 立即开轮接续,恢复期间显示的 revising 是真实进行中)
|
|
106
|
+
} else if (state.state === "dispatching") {
|
|
107
|
+
// review_aborted 边(D1:dispatching --review_aborted--> approved)+ steer 双动作:
|
|
108
|
+
// 转移/落盘先于 steer,恢复后持久态与指示一致;ok:false(坏数据格)不落盘照常 steer
|
|
109
|
+
const moved = applyPlanEvent(state, "review_aborted");
|
|
110
|
+
if (moved.ok) persistPlanState(pi, state);
|
|
111
|
+
pi.sendMessage(
|
|
112
|
+
{
|
|
113
|
+
customType: PLAN_CONTEXT_CUSTOM_TYPE,
|
|
114
|
+
content:
|
|
115
|
+
"[PLAN MODE] The plan was approved and the execution-method choice was interrupted (session restarted). " +
|
|
116
|
+
"The approval stands. Re-initiate the execution-method choice by calling plan(action='complete') now. " +
|
|
117
|
+
"Do not implement any changes before the user picks an execution method.",
|
|
86
118
|
display: false,
|
|
87
119
|
},
|
|
88
120
|
{ deliverAs: "steer", triggerTurn: true },
|
|
89
121
|
);
|
|
90
|
-
if (!revising) {
|
|
91
|
-
// E3 重挂即落 'resubmit' 源标记 + persist(此处原只发 steer 不落盘,renderer
|
|
92
|
-
// 冷启动扫描的 View 恒无 source、恒渲染通用文案;落盘后才能渲染「会话已重启,
|
|
93
|
-
// 尚未重新提交」分支)。无条件覆盖既有 source 为有意——「会话已重启」语义
|
|
94
|
-
// 优先成立(P3-9 登记;旧 entry 存量的其他 source 值一并被覆盖归一)。
|
|
95
|
-
state.reviewStateSource = "resubmit";
|
|
96
|
-
persistPlanState(pi, state);
|
|
97
|
-
}
|
|
98
122
|
}
|
|
99
123
|
}
|
|
100
124
|
});
|
|
101
125
|
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
//
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
// (system-prompt logHookFailure 同款形态:logger 首选、stderr 内层兜底)
|
|
118
|
-
try {
|
|
119
|
-
logger.warn("plan: before_agent_start injection failed", { error: msg });
|
|
120
|
-
} catch (nestedErr) {
|
|
121
|
-
try {
|
|
122
|
-
process.stderr.write(`[pi-plan] before_agent_start injection log also failed: ${String(nestedErr)}\n`);
|
|
123
|
-
} catch (finalErr) {
|
|
124
|
-
// 完全静默:两层兜底都失败时无处可写
|
|
125
|
-
void finalErr;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return undefined;
|
|
129
|
-
}
|
|
126
|
+
// U6b:树回退(撤回 __taiji_nav__ → navigateTree / 用户树跳转)后即时重建 plan
|
|
127
|
+
// 内存态,消除 getPlanState 缓存命中短路的 stale 窗口(运行中撤回后压缩摘要会按
|
|
128
|
+
// 陈旧 state 注入被撤 requirement)。
|
|
129
|
+
//
|
|
130
|
+
// 纯重建体(todo 的 session_tree handler 同款)——只做 reconstruct + 缓存同步 +
|
|
131
|
+
// widget 刷新。为什么绝不复刻 session_start 块的副作用(E3 steer 重挂 /
|
|
132
|
+
// setActiveTools / persistPlanState):本事件由撤回编排触发,steer 会让撤回编排
|
|
133
|
+
// 自己注入消息(违背「被撤内容从未发生」语义),persist 会向回退后的新分支追加
|
|
134
|
+
// entry 污染文件尾。缓存同步(planCtx.states.set)保持「getPlanState 缓存命中 = 最新
|
|
135
|
+
// 重建态」——session_start 块的重建走同一 reconstructPlanState(已含活跃路径裁剪)。
|
|
136
|
+
pi.on("session_tree", async (_event: unknown, ctx: ExtensionContext) => {
|
|
137
|
+
const sessionId = ctx.sessionManager.getSessionId();
|
|
138
|
+
const state = reconstructPlanState(ctx);
|
|
139
|
+
planCtx.states.set(sessionId, state);
|
|
140
|
+
updatePlanWidget(ctx, state);
|
|
130
141
|
});
|
|
131
142
|
|
|
143
|
+
|
|
132
144
|
// Clean up on session end
|
|
133
145
|
pi.on("session_shutdown", async (_event: unknown, ctx: ExtensionContext) => {
|
|
134
146
|
const sessionId = ctx.sessionManager.getSessionId();
|
|
135
|
-
|
|
136
|
-
|
|
147
|
+
planCtx.states.delete(sessionId);
|
|
148
|
+
planCtx.controllers.delete(sessionId);
|
|
137
149
|
});
|
|
138
150
|
}
|
package/src/prompts.ts
CHANGED
|
@@ -46,20 +46,47 @@ const PHASE_B_SECTION =
|
|
|
46
46
|
`4. **Propose 2-3 approaches** with trade-offs + recommendation.\n` +
|
|
47
47
|
`5. **Assumption audit**: Grep-verify interfaces/types exist. Mark [UNVERIFIED] what can't be verified.`;
|
|
48
48
|
|
|
49
|
+
/**
|
|
50
|
+
* 执行方式选项集的技能档上限(SSOT):PHASE_D 散文(本文件)与 buildExecOptions
|
|
51
|
+
* (tool.ts)共同消费——散文数量与实际截断同源,对齐测试锁定。
|
|
52
|
+
*/
|
|
53
|
+
export const MAX_SKILL_OPTIONS = 2;
|
|
54
|
+
|
|
49
55
|
// Phase D 第 3 点的选项集描述必须与 buildExecOptions(tool.ts)产出的实际选项一致
|
|
50
|
-
//
|
|
51
|
-
//
|
|
56
|
+
// (有 plan-exec 技能时 = ≤MAX_SKILL_OPTIONS 个技能档 + Execute + Not now,label 与
|
|
57
|
+
// i18n exec.* 词典同源;无技能时不弹表单直通 execute,D7②)——描述漂移会诱导模型
|
|
58
|
+
// 向用户预告不存在的执行方式。对齐测试(exec-options-alignment.test.ts)锁定散文锚点。
|
|
52
59
|
const PHASE_D_SECTION =
|
|
53
60
|
`## Phase D: Completion\n` +
|
|
54
61
|
`1. Ask user to review the complete plan.\n` +
|
|
55
|
-
`2. Call plan tool (complete)
|
|
56
|
-
`3. After plan complete: the user picks an execution method in the completion dialog — up to
|
|
62
|
+
`2. Call plan tool (complete).\n` +
|
|
63
|
+
`3. After plan complete: when plan-exec skills are detected, the user picks an execution method in the completion dialog — up to ${MAX_SKILL_OPTIONS} detected plan-exec skills (Execute via skill: <name>), Execute (goal tracking integrated when available), or Not now (stay in plan mode). When NO plan-exec skill is detected, no dialog appears and the plan executes directly.`;
|
|
64
|
+
|
|
65
|
+
/** 对齐测试消费(exec-options-alignment.test.ts)——PHASE_D 散文的机器防线锚点。 */
|
|
66
|
+
export const PHASE_D_PROSE = PHASE_D_SECTION;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Phase C.5 自审清单(D9②):submit-review 前的自审引导——产物纪律段之后恒注入(全流程,
|
|
70
|
+
* 不随模板/技能流程分叉——自审硬门对每次 submit-review 生效,含修订后重挂)。
|
|
71
|
+
* 门语义三层边界:存在性 = 结构保证(tool 参数门)/ 防照抄 = 启发式(逐字节拒收)/
|
|
72
|
+
* 语义级新鲜度 = 本提示词纪律(机器不可判)。自审发现的问题先修文档再提交。
|
|
73
|
+
*/
|
|
74
|
+
const PHASE_C_5_SECTION =
|
|
75
|
+
`## Phase C.5: Self-Review Before Submission\n` +
|
|
76
|
+
`Before every plan(action='submit-review'), do a self-review and put the conclusions into the selfReview parameter:\n` +
|
|
77
|
+
`1. **Requirement coverage**: check each requirement item is covered by the documents, one by one.\n` +
|
|
78
|
+
`2. **Assumption audit**: clear or explicitly list every [UNVERIFIED] assumption.\n` +
|
|
79
|
+
`3. **Chapter completeness**: compare against the template's chapter structure — no unwritten chapters.\n` +
|
|
80
|
+
`4. **Acceptance realism**: verify the acceptance scenarios are truly executable.\n` +
|
|
81
|
+
`Fix any issues found DURING the self-review in the documents first (then re-register), then submit with the self-review conclusions.`;
|
|
57
82
|
|
|
58
83
|
/**
|
|
59
|
-
* /plan
|
|
84
|
+
* /plan 命令的提示词段注入(D2):
|
|
60
85
|
* ① 技能指令(仅挂载 --skills 时)——按技能 SKILL.md 流程产出计划文档;
|
|
61
|
-
* ② 产物纪律(恒注入)——register-doc 登记 / 修订重调 version+1 /
|
|
62
|
-
* submit-review
|
|
86
|
+
* ② 产物纪律(恒注入)——register-doc 登记 / 修订重调 version+1 / 全部完成先自审再调
|
|
87
|
+
* submit-review(必带 selfReview)/ submit-review 被消费后当轮回应完重挂直到确认 /
|
|
88
|
+
* 收到重挂请求直接 submit-review(D8 提示词纪律);
|
|
89
|
+
* ②.5 Phase C.5 自审清单(恒注入,产物纪律段之后,D9②);
|
|
63
90
|
* ③ 只读纪律(恒注入,现状 pi-ext-021 提示词保持);
|
|
64
91
|
* ④ 模板流程(仅未指定 --skills 时)——<available-plans> 三源清单注入(内置 5
|
|
65
92
|
* + 用户级 + 项目级,模型自选模板,D8);空发现不注入段(warn 另落)。
|
|
@@ -83,14 +110,16 @@ export function buildPlanModePrompt(input: PlanPromptInput): string {
|
|
|
83
110
|
);
|
|
84
111
|
}
|
|
85
112
|
|
|
86
|
-
// ② 产物纪律
|
|
113
|
+
// ② 产物纪律 + ②.5 Phase C.5 自审清单(D9②:产物纪律段之后恒注入)
|
|
87
114
|
sections.push(
|
|
88
115
|
`## Deliverable Discipline\n` +
|
|
89
116
|
`- Write every deliverable document into the plan directory (.tmp/plans/<slug>/), then register it: plan(action='register-doc', fileName='design.md', sourceSkill='<skill name or omit>').\n` +
|
|
90
117
|
`- After revising a document, REWRITE the file and re-register it with plan(action='register-doc', fileName=...) again — the version is bumped so the UI refreshes its content.\n` +
|
|
91
|
-
`- When ALL documents are done, call plan(action='submit-review') to request user review.\n` +
|
|
92
|
-
`- After submit-review is consumed (a revision request), finish responding for the current turn, then call plan(action='submit-review') again to re-hang the review — repeat until the user confirms execution
|
|
118
|
+
`- When ALL documents are done, self-review first (Phase C.5), then call plan(action='submit-review', selfReview='<your self-review conclusions>') to request user review — selfReview is REQUIRED every time (including re-submissions after revisions).\n` +
|
|
119
|
+
`- After submit-review is consumed (a revision request), finish responding for the current turn, revise the documents, redo the self-review for the revised versions, then call plan(action='submit-review', selfReview='...') again to re-hang the review — repeat until the user confirms execution.\n` +
|
|
120
|
+
`- When the user asks to re-submit the plan review (e.g. a re-submit notice like "请重新提交计划审批"), call plan(action='submit-review', selfReview='...') IMMEDIATELY — carry back your previous self-review VERBATIM when the documents are unchanged.`,
|
|
93
121
|
);
|
|
122
|
+
sections.push(PHASE_C_5_SECTION);
|
|
94
123
|
|
|
95
124
|
// ③ 只读纪律(现状 pi-ext-021 提示词保持)
|
|
96
125
|
sections.push(
|
|
@@ -134,8 +163,9 @@ export function buildPlanModePrompt(input: PlanPromptInput): string {
|
|
|
134
163
|
}
|
|
135
164
|
|
|
136
165
|
/**
|
|
137
|
-
* revise decision 的评论清单注入文本(D5 decision 消费;explain
|
|
138
|
-
*
|
|
166
|
+
* revise decision 的评论清单注入文本(D5 decision 消费;explain 决策已删——PlanReviewDecision
|
|
167
|
+
* 现为三键值域 'approve' | 'revise' | 'dismiss'(D3 加员):approve 走 complete 流程、
|
|
168
|
+
* dismiss 走搁置分支,均不经本函数)。
|
|
139
169
|
* 评论语义:quote 是用户划选引文(agent 定位段落用),comment 是评语。
|
|
140
170
|
*/
|
|
141
171
|
export function formatReviewComments(comments: PlanReviewComment[]): string {
|