pi-firecode 0.8.1 → 1.0.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.
Files changed (79) hide show
  1. package/README.md +58 -24
  2. package/README.zh-CN.md +74 -0
  3. package/config.example.jsonc +47 -57
  4. package/dist/config.example.jsonc +90 -0
  5. package/dist/index.js +11464 -0
  6. package/dist/master/prompts/master.en.md +22 -0
  7. package/dist/master/prompts/master.zh.md +22 -0
  8. package/dist/master/prompts/worker.en.md +1 -0
  9. package/dist/master/prompts/worker.zh.md +1 -0
  10. package/{review → dist/review}/prompts/review.en.md +7 -6
  11. package/{review → dist/review}/prompts/review.zh.md +7 -6
  12. package/dist/watcher/prompts/watch.en.md +43 -0
  13. package/{watcher → dist/watcher}/prompts/watch.zh.md +1 -1
  14. package/package.json +10 -27
  15. package/config.ts +0 -474
  16. package/deliver.ts +0 -32
  17. package/flame-frames.ts +0 -460
  18. package/format.ts +0 -80
  19. package/header.ts +0 -92
  20. package/herdr-client.ts +0 -60
  21. package/index.ts +0 -62
  22. package/jsonc.ts +0 -34
  23. package/master/event-card.ts +0 -106
  24. package/master/event-format.ts +0 -52
  25. package/master/index.ts +0 -1200
  26. package/master/prompt.ts +0 -26
  27. package/master/prompts/master.zh.md +0 -17
  28. package/master/prompts/worker.zh.md +0 -1
  29. package/master/role.ts +0 -13
  30. package/master/spawn.ts +0 -193
  31. package/master/state.ts +0 -201
  32. package/provider/claude-sub.ts +0 -129
  33. package/provider/openai-native/index.ts +0 -8
  34. package/provider/openai-native/src/compact-client.ts +0 -362
  35. package/provider/openai-native/src/config.ts +0 -297
  36. package/provider/openai-native/src/extension.ts +0 -93
  37. package/provider/openai-native/src/native-compaction.ts +0 -151
  38. package/provider/openai-native/src/native-details.ts +0 -157
  39. package/provider/openai-native/src/native-replay.ts +0 -253
  40. package/provider/openai-native/src/native-runtime.ts +0 -144
  41. package/provider/openai-native/src/options.ts +0 -85
  42. package/provider/openai-native/src/request-pipeline.ts +0 -21
  43. package/provider/openai-native/src/responses-input.ts +0 -433
  44. package/review/advisor.ts +0 -112
  45. package/review/card.ts +0 -385
  46. package/review/checkpoint.ts +0 -384
  47. package/review/evidence.ts +0 -232
  48. package/review/index.ts +0 -1246
  49. package/review/outcome.ts +0 -74
  50. package/review/progress.ts +0 -324
  51. package/review/prompt.ts +0 -210
  52. package/review/reviewer.ts +0 -356
  53. package/review/session.ts +0 -129
  54. package/review/state.ts +0 -905
  55. package/review/ui.ts +0 -528
  56. package/session/bark.ts +0 -157
  57. package/session/herdr-display.ts +0 -87
  58. package/session/presets.ts +0 -257
  59. package/session/rename.ts +0 -46
  60. package/session/stats.ts +0 -310
  61. package/session/working-flame.ts +0 -116
  62. package/statusbar/index.ts +0 -118
  63. package/statusbar/quota-cache.ts +0 -60
  64. package/statusbar/quota-parse.ts +0 -85
  65. package/statusbar/quota.ts +0 -203
  66. package/statusbar/render.ts +0 -181
  67. package/statusbar/tps.ts +0 -87
  68. package/theme.ts +0 -84
  69. package/tools/grouping.ts +0 -233
  70. package/tools/index.ts +0 -183
  71. package/tools/line.ts +0 -194
  72. package/tools/parts.ts +0 -143
  73. package/tools/timing.ts +0 -22
  74. package/watcher/card.ts +0 -85
  75. package/watcher/index.ts +0 -204
  76. package/watcher/observer.ts +0 -75
  77. package/watcher/transcript.ts +0 -64
  78. /package/{review → dist/review}/prompts/advisor.en.md +0 -0
  79. /package/{review → dist/review}/prompts/advisor.zh.md +0 -0
package/review/outcome.ts DELETED
@@ -1,74 +0,0 @@
1
- import { readFileSync } from "node:fs";
2
- import { CHECKPOINT_TYPE, isValidCheckpoint } from "./checkpoint.js";
3
- import type { ReviewState } from "./state.js";
4
-
5
- /** 审查活跃期在 herdr:blocked 频道发布的展示标签。 */
6
- export const REVIEW_OCCUPANCY_LABEL = "对抗审查进行中";
7
-
8
- export type ReviewOutcome =
9
- | { status: "passed"; runId: string; rounds: number }
10
- | { status: "stopped"; runId: string; rounds: number; advisorAdvice?: string }
11
- | { status: "failed"; runId: string; rounds: number; reason: string }
12
- | { status: "in_progress"; runId: string }
13
- | { status: "none"; runId?: string }
14
- | { status: "error"; message: string };
15
-
16
- /** 只读 Worker session,解析最近一条 fire-review checkpoint 的判定。 */
17
- export function readReviewOutcome(sessionPath: string): ReviewOutcome {
18
- let content: string;
19
- try {
20
- content = readFileSync(sessionPath, "utf8");
21
- } catch (error) {
22
- if (isMissingFile(error)) return { status: "none" };
23
- return { status: "error", message: `无法读取 session 文件:${errorMessage(error)}` };
24
- }
25
-
26
- let latest: ReviewState | undefined;
27
- let damage: string | undefined;
28
- // session 尾行可能正写到一半;跳过损坏行并保留最近一条可验证记录,
29
- // 不能让截断尾行抹掉已有结果。
30
- for (const [index, line] of content.split(/\r?\n/u).entries()) {
31
- if (!line.trim()) continue;
32
- let entry: unknown;
33
- try {
34
- entry = JSON.parse(line);
35
- } catch {
36
- damage ??= `session 第 ${index + 1} 行不是有效 JSON`;
37
- continue;
38
- }
39
- if (!isCheckpointEntry(entry)) continue;
40
- if (isValidCheckpoint(entry.data)) latest = entry.data as ReviewState;
41
- else damage ??= "fire-review checkpoint 格式无效";
42
- }
43
- if (!latest) return damage ? { status: "error", message: damage } : { status: "none" };
44
-
45
- if (latest.phase === "idle") return { status: "none", runId: latest.runId };
46
- if (latest.phase !== "settled") return { status: "in_progress", runId: latest.runId };
47
- const rounds = latest.history.length;
48
- const result = latest.history.at(-1)?.result;
49
- if (result === "passed") return { status: "passed", runId: latest.runId, rounds };
50
- // stopped(顾问叫停)与 failed(maxRounds 用尽)都是质量裁决终止;
51
- // error / cancelled / timed_out 是基础设施故障或人为中断,不弱化成“停止”。
52
- if (result === "stopped" || result === "failed") {
53
- // 顾问叫停时把裁决带给读取方:Master 拿到停止原因才能调整方向。
54
- const advice = latest.history.at(-1)?.advisor?.advice;
55
- return { status: "stopped", runId: latest.runId, rounds, ...(advice ? { advisorAdvice: advice } : {}) };
56
- }
57
- return { status: "failed", runId: latest.runId, rounds, reason: result ?? "unknown" };
58
- }
59
-
60
- function isCheckpointEntry(value: unknown): value is { data: unknown } {
61
- return typeof value === "object" && value !== null
62
- && (value as Record<string, unknown>).type === "custom"
63
- && (value as Record<string, unknown>).customType === CHECKPOINT_TYPE
64
- && "data" in value;
65
- }
66
-
67
- function isMissingFile(error: unknown): boolean {
68
- return typeof error === "object" && error !== null
69
- && (error as { code?: unknown }).code === "ENOENT";
70
- }
71
-
72
- function errorMessage(error: unknown): string {
73
- return error instanceof Error ? error.message : String(error);
74
- }
@@ -1,324 +0,0 @@
1
- /**
2
- * 审查者实时进度:从结构化会话事件派生的 UI 层状态(纯函数)。
3
- * 不进 reducer、不写 checkpoint;恢复时由 reducer 状态重建骨架。
4
- */
5
- import { clip, formatModelName } from "../format.js";
6
- import type { Language } from "../config.js";
7
- import type { ReviewerStatus } from "./state.js";
8
-
9
- export interface ProgressTool {
10
- id: string;
11
- tool: string;
12
- args: string;
13
- startedAt: number;
14
- endedAt?: number;
15
- isError?: boolean;
16
- }
17
-
18
- /** 单个审查者的活动快照。 */
19
- export interface ReviewerProgress {
20
- index: number;
21
- label: string;
22
- status: ReviewerStatus;
23
- /** 当前动作的人话描述(读某文件 / 跑某命令 / 思考中)。 */
24
- action: string;
25
- /** 落定后的一行结果摘要(PASS 收敛摘要 / FAIL 首条发现);运行中为空。 */
26
- summary: string;
27
- /** 落定后的结构化多行人话结论与建议(不含命令参数等机械流水)。 */
28
- details?: string[];
29
- toolCalls: number;
30
- tokens: number;
31
- activeTools: ProgressTool[];
32
- recentTools: ProgressTool[];
33
- /** 最近动作流水,供活动测试和降级展示。 */
34
- trail: string[];
35
- /** 本模型启动时刻;活动条据此显示每个模型自己的耗时。 */
36
- startedAt: number;
37
- /** 落定时刻;落定后耗时冻结在 settledAt - startedAt。 */
38
- settledAt?: number;
39
- }
40
-
41
- const TRAIL_LIMIT = 40;
42
- const RECENT_TOOL_LIMIT = 5;
43
- const ACTION_WIDTH = 48;
44
-
45
- export function initialProgress(
46
- reviewers: readonly { model: string }[],
47
- language: Language,
48
- now = Date.now(),
49
- ): ReviewerProgress[] {
50
- return reviewers.map((reviewer, index) => ({
51
- index,
52
- label: formatModelName(reviewer.model),
53
- status: "running",
54
- action: thinkingText(language),
55
- summary: "",
56
- toolCalls: 0,
57
- tokens: 0,
58
- activeTools: [],
59
- recentTools: [],
60
- trail: [],
61
- startedAt: now,
62
- }));
63
- }
64
-
65
- /**
66
- * 把一条会话事件并入进度快照,返回新数组(无事件相关变化时返回原数组,
67
- * 调用方据此跳过重绘)。
68
- */
69
- export function applySessionEvent(
70
- progress: readonly ReviewerProgress[],
71
- index: number,
72
- event: Record<string, unknown>,
73
- language: Language,
74
- ): readonly ReviewerProgress[] {
75
- const current = progress.find((item) => item.index === index);
76
- if (!current) return progress;
77
- const next = applyReviewerEvent(current, event, language);
78
- if (next === current) return progress;
79
- return progress.map((item) => item.index === index ? next : item);
80
- }
81
-
82
- function applyReviewerEvent(
83
- item: ReviewerProgress,
84
- event: Record<string, unknown>,
85
- language: Language,
86
- ): ReviewerProgress {
87
- if (event.type === "tool_execution_start") {
88
- const tool = typeof event.toolName === "string" ? event.toolName : "tool";
89
- const args = summarizeArgs(event.args);
90
- const active: ProgressTool = {
91
- id: typeof event.toolCallId === "string" ? event.toolCallId : `${tool}:${Date.now()}`,
92
- tool,
93
- args,
94
- startedAt: Date.now(),
95
- };
96
- const action = actionOf(tool, args, language);
97
- return {
98
- ...item,
99
- action,
100
- toolCalls: item.toolCalls + 1,
101
- activeTools: [...item.activeTools, active],
102
- trail: [...item.trail, action].slice(-TRAIL_LIMIT),
103
- };
104
- }
105
- if (event.type === "tool_execution_end") {
106
- const id = typeof event.toolCallId === "string" ? event.toolCallId : "";
107
- const completed = item.activeTools.find((tool) => tool.id === id);
108
- if (!completed) return item;
109
- const activeTools = item.activeTools.filter((tool) => tool.id !== id);
110
- const current = activeTools.at(-1);
111
- return {
112
- ...item,
113
- action: current ? actionOf(current.tool, current.args, language) : thinkingText(language),
114
- activeTools,
115
- recentTools: [
116
- ...item.recentTools,
117
- { ...completed, endedAt: Date.now(), isError: event.isError === true },
118
- ].slice(-RECENT_TOOL_LIMIT),
119
- };
120
- }
121
- if (event.type === "message_end") {
122
- const message = isRecord(event.message) ? event.message : {};
123
- const usage = isRecord(message.usage) ? message.usage : {};
124
- const tokens = Number.isFinite(usage.totalTokens) ? Number(usage.totalTokens) : 0;
125
- return tokens ? { ...item, tokens: item.tokens + tokens } : item;
126
- }
127
- return item;
128
- }
129
-
130
- export function settleProgress(
131
- progress: readonly ReviewerProgress[],
132
- index: number,
133
- status: ReviewerStatus,
134
- language: Language,
135
- summary = "",
136
- rawDetails = "",
137
- ): readonly ReviewerProgress[] {
138
- const details = extractReviewDetails(status, summary, rawDetails, language);
139
- return progress.map((item) =>
140
- item.index === index
141
- ? {
142
- ...item,
143
- status,
144
- action: settledText(status, language),
145
- summary: oneLine(summary) || (details[0] ?? settledText(status, language)),
146
- details,
147
- activeTools: [],
148
- settledAt: Date.now(),
149
- }
150
- : item,
151
- );
152
- }
153
-
154
- export function extractReviewDetails(
155
- status: ReviewerStatus,
156
- summary: string,
157
- rawDetails = "",
158
- language: Language = "zh",
159
- ): string[] {
160
- const lines: string[] = [];
161
- const cleanSummary = summary.trim();
162
-
163
- if (status === "passed") {
164
- if (cleanSummary) {
165
- lines.push(cleanSummary);
166
- }
167
- if (rawDetails) {
168
- const suggestionSections = rawDetails.split(/^##\s+(?:建议(非阻塞)|Suggestions \(non-blocking\))\s*$/imu);
169
- if (suggestionSections.length > 1) {
170
- const suggestionBody = suggestionSections.slice(1).join("\n");
171
- for (const line of suggestionBody.split(/\r?\n/)) {
172
- const trimmed = line.trim();
173
- if (/^[-*+]\s+/u.test(trimmed)) {
174
- const text = trimmed.replace(/^[-*+]\s+/u, "").trim();
175
- if (text) {
176
- lines.push(language === "en" ? `Suggestion: ${text}` : `建议:${text}`);
177
- }
178
- }
179
- }
180
- }
181
- }
182
- } else if (status === "failed") {
183
- // 只列发现标题(带严重度标签),首行是数量汇总;问题正文留给结果卡。
184
- if (rawDetails) {
185
- const rawFindings = rawDetails.split(/^##\s+(?:建议(非阻塞)|Suggestions \(non-blocking\))\s*$/imu)[0] ?? "";
186
- const rawSections = rawFindings.split(/\n(?=#{1,6}\s*(?:发现|Finding))/imu);
187
- const findings: string[] = [];
188
- for (const section of rawSections) {
189
- const rawLines = section.split(/\r?\n/).map((l) => l.trim()).filter(Boolean);
190
- if (rawLines.length === 0 || !/^#{1,6}\s*(?:发现|Finding)/iu.test(rawLines[0] ?? "")) {
191
- continue;
192
- }
193
- const heading = (rawLines[0] ?? "").replace(/^#{1,6}\s*(?:发现|Finding)\s*(?:\d+|[a-zA-Z0-9]+)?\s*[::]?\s*/iu, "").trim();
194
- let severity = "";
195
- const issueLines: string[] = [];
196
- let inIssue = false;
197
- for (let i = 1; i < rawLines.length; i += 1) {
198
- const line = rawLines[i] ?? "";
199
- const sevMatch = /^[-*+]\s*(?:\*\*)?(?:严重程度|Severity)(?:\*\*)?\s*[::]\s*(高|中|低|High|Medium|Low)/iu.exec(line);
200
- if (sevMatch) {
201
- severity = sevMatch[1] ?? "";
202
- inIssue = false;
203
- continue;
204
- }
205
- const issueStart = /^[-*+]\s*(?:\*\*)?(?:问题|Issue)(?:\*\*)?\s*[::]\s*(.*)$/u.exec(line);
206
- if (issueStart) {
207
- inIssue = true;
208
- if (issueStart[1]?.trim()) issueLines.push(issueStart[1].trim());
209
- continue;
210
- }
211
- if (/^[-*+]\s*(?:\*\*)?(?:违反|证据|验证|Violated|Evidence|Verification)/iu.test(line) || /^#{1,6}\s+/u.test(line)) {
212
- inIssue = false;
213
- continue;
214
- }
215
- if (inIssue && line) issueLines.push(line.replace(/^[-*+]\s*/u, ""));
216
- }
217
- const issueText = issueLines.join(" ").replace(/`([^`]+)`/gu, "$1").trim();
218
- const title = heading || issueText;
219
- if (!title) continue;
220
- const sevTag = severity
221
- ? `[${language === "en" ? severity : `严重·${severity}`}] `
222
- : "";
223
- findings.push(`${sevTag}${title}`);
224
- }
225
- if (findings.length > 0) {
226
- lines.push(
227
- language === "en"
228
- ? `Found ${findings.length} issue${findings.length === 1 ? "" : "s"}`
229
- : `发现 ${findings.length} 个问题`,
230
- ...findings,
231
- );
232
- }
233
- }
234
- if (lines.length === 0 && cleanSummary) {
235
- lines.push(cleanSummary);
236
- }
237
- }
238
-
239
- return lines.length > 0 ? lines : [cleanSummary || settledText(status, language)];
240
- }
241
-
242
- /** 工具调用事件 → 人话动作;非工具事件返回 undefined。 */
243
- function actionOf(tool: string, args: string, language: Language) {
244
- const verb = verbOf(tool, language);
245
- const target = tool === "bash" ? args : basename(args);
246
- return clip(target ? `${verb} ${target}` : verb, ACTION_WIDTH);
247
- }
248
-
249
- function verbOf(tool: string, language: Language) {
250
- const zh: Record<string, string> = {
251
- read: "读",
252
- bash: "跑",
253
- grep: "搜",
254
- find: "找",
255
- ls: "看",
256
- };
257
- const en: Record<string, string> = {
258
- read: "read",
259
- bash: "run",
260
- grep: "grep",
261
- find: "find",
262
- ls: "ls",
263
- };
264
- const table = language === "en" ? en : zh;
265
- return table[tool] ?? tool ?? "?";
266
- }
267
-
268
- function summarizeArgs(value: unknown) {
269
- const args = isRecord(value) ? value : {};
270
- const raw =
271
- firstString(args, ["command"]) ??
272
- firstString(args, ["path", "pattern", "query", "file"]);
273
- if (raw) return oneLine(raw);
274
- if (value === undefined) return "";
275
- try {
276
- const serialized = JSON.stringify(value);
277
- return serialized === "{}" ? "" : clip(serialized, 100);
278
- } catch {
279
- return clip(String(value), 100);
280
- }
281
- }
282
-
283
- function firstString(args: Record<string, unknown>, keys: readonly string[]) {
284
- for (const key of keys) {
285
- const value = args[key];
286
- if (typeof value === "string" && value.trim()) return value.trim();
287
- }
288
- return undefined;
289
- }
290
-
291
- function basename(path: string) {
292
- const parts = path.split("/").filter(Boolean);
293
- return parts.length > 1 ? `${parts.at(-2)}/${parts.at(-1)}` : (parts.at(-1) ?? path);
294
- }
295
-
296
- function oneLine(text: string) {
297
- return text.replace(/\s+/gu, " ").trim();
298
- }
299
-
300
- function thinkingText(language: Language) {
301
- return language === "en" ? "thinking" : "思考中";
302
- }
303
-
304
- function settledText(status: ReviewerStatus, language: Language) {
305
- if (language === "en")
306
- return status === "passed"
307
- ? "passed"
308
- : status === "failed"
309
- ? "found issues"
310
- : status === "error"
311
- ? "infra error"
312
- : "thinking";
313
- return status === "passed"
314
- ? "通过"
315
- : status === "failed"
316
- ? "发现问题"
317
- : status === "error"
318
- ? "异常"
319
- : "思考中";
320
- }
321
-
322
- function isRecord(value: unknown): value is Record<string, unknown> {
323
- return typeof value === "object" && value !== null && !Array.isArray(value);
324
- }
package/review/prompt.ts DELETED
@@ -1,210 +0,0 @@
1
- /**
2
- * Prompt 组装:纯函数拼装审查 / 顾问 / 修复反馈文本。
3
- * 模板文件读取是唯一 IO(readPrompt),拼装本身零副作用可单测。
4
- */
5
- import { readFileSync } from "node:fs";
6
- import { dirname, join } from "node:path";
7
- import { fileURLToPath } from "node:url";
8
- import type { Language } from "../config.js";
9
- import type { AdvisorResult, ReviewState, SummaryKind } from "./state.js";
10
-
11
- const PROMPTS_DIR = join(dirname(fileURLToPath(import.meta.url)), "prompts");
12
-
13
- export type PromptKind = "review" | "advisor";
14
-
15
- export function readPrompt(kind: PromptKind, language: Language): string {
16
- return readFileSync(join(PROMPTS_DIR, `${kind}.${language}.md`), "utf8");
17
- }
18
-
19
- export interface ReviewPromptInput {
20
- language: Language;
21
- scope: string;
22
- focus: string;
23
- evidence: string;
24
- history: ReviewState["history"];
25
- round: number;
26
- }
27
-
28
- export interface PromptLayers {
29
- system: string;
30
- user: string;
31
- }
32
-
33
- /** 审查政策走 system 层;需求与历史留在 user 层,不能反向改写审查契约。 */
34
- export function buildReviewPrompt(template: string, input: ReviewPromptInput): PromptLayers {
35
- const sep = input.language === "en" ? ":" : ":";
36
- const evidence = input.evidence.replaceAll("</session_evidence>", "&lt;/session_evidence&gt;");
37
- const parts = [
38
- `${input.language === "en" ? "Review target" : "审查对象"}${sep}\n${input.scope}`,
39
- ];
40
- if (input.focus)
41
- parts.push(`${input.language === "en" ? "Focus" : "关注点"}${sep}\n${input.focus}`);
42
- const prior = priorRoundsSection(input.history, input.round, input.language);
43
- if (prior) parts.push(prior);
44
- parts.push(
45
- `${input.language === "en" ? "Session record" : "会话记录"}${sep}\n<session_evidence>\n${evidence}\n</session_evidence>`,
46
- reviewReminder(input.language),
47
- );
48
- return promptLayers(
49
- template,
50
- parts.filter((part) => part !== "").join("\n\n"),
51
- input.language,
52
- );
53
- }
54
-
55
- function promptLayers(system: string, user: string, language: Language): PromptLayers {
56
- if (!system.trim())
57
- throw new Error(
58
- language === "en" ? "FireReview system prompt is empty" : "FireReview system prompt 为空",
59
- );
60
- return { system, user };
61
- }
62
-
63
- function reviewReminder(language: Language) {
64
- return language === "en"
65
- ? "Now complete the review under the system prompt and strictly follow its output contract."
66
- : "现在按 system prompt 的审查规则完成审查,并严格遵守其输出契约。";
67
- }
68
-
69
- /** 往轮 FAIL 发现清单(两相收敛的闭环输入):第 2 轮起注入。 */
70
- export function priorRoundsSection(
71
- history: ReviewState["history"],
72
- round: number,
73
- language: Language,
74
- ): string | undefined {
75
- const prior = history.filter((entry) => entry.round < round && entry.result === "failed");
76
- if (round <= 1 || prior.length === 0) return undefined;
77
- const header =
78
- language === "en"
79
- ? "Prior round findings (newest first)"
80
- : "往轮发现清单(由新到旧):";
81
- const body = prior
82
- .map((entry) => {
83
- const label =
84
- language === "en" ? `## Round ${entry.round} · failed` : `## 第 ${entry.round} 轮 · 未通过`;
85
- // 顾问裁决必须随轮注入:否则被顾问排除的发现会在后续轮被审查者原样重提,循环无法收敛。
86
- const advisor = entry.advisor
87
- ? `\n\n### ${language === "en" ? "Advisor ruling" : "顾问裁决"}(${entry.advisor.verdict})\n${entry.advisor.advice}`
88
- : "";
89
- return `${label}\n${entry.details}${advisor}`;
90
- })
91
- .join("\n\n");
92
- return `${header}\n${body}`;
93
- }
94
-
95
- export interface AdvisorPromptInput {
96
- language: Language;
97
- focus: string;
98
- details: string;
99
- history: ReviewState["history"];
100
- round: number;
101
- }
102
-
103
- export function buildAdvisorPrompt(template: string, input: AdvisorPromptInput): PromptLayers {
104
- const sep = input.language === "en" ? ":" : ":";
105
- const parts: string[] = [];
106
- if (input.focus)
107
- parts.push(`${input.language === "en" ? "Review focus" : "审查关注点"}${sep}\n${input.focus}`);
108
- parts.push(
109
- `${input.language === "en" ? "This round FAIL findings" : "本轮 FAIL 发现"}${sep}\n${input.details}`,
110
- );
111
- const prior = priorRoundsSection(input.history, input.round, input.language);
112
- if (prior) parts.push(`${input.language === "en" ? "Prior FAIL history" : "往轮 FAIL 历史"}${sep}\n${prior}`);
113
- parts.push(
114
- input.language === "en"
115
- ? "Now arbitrate under the system prompt and strictly follow its output contract."
116
- : "现在按 system prompt 的规则完成仲裁,并严格遵守其输出契约。",
117
- );
118
- return promptLayers(template, parts.join("\n\n"), input.language);
119
- }
120
-
121
- export interface FixFeedbackInput {
122
- language: Language;
123
- details: string;
124
- advisor: AdvisorResult | null;
125
- }
126
-
127
- /** 投递给执行模型的修复反馈:把审查发现当假设核实,修根因不压表象。 */
128
- export function buildFixFeedback(input: FixFeedbackInput): string {
129
- // narrow 与 continue 必须产生可区分的行为:narrow 不再要求逐条修全部发现,
130
- // 而是把顾问给的范围当约束,只修真正阻塞当前需求的那部分。
131
- const narrowed = input.advisor?.verdict === "narrow";
132
- const parts = [narrowInstruction(input.language, narrowed), "", input.details];
133
- if (input.advisor?.advice) {
134
- const label =
135
- input.language === "en"
136
- ? narrowed
137
- ? "Advisor scope (authoritative)"
138
- : "Advisor note"
139
- : narrowed
140
- ? "顾问收窄后的范围(以此为准)"
141
- : "顾问建议";
142
- parts.push("", `${label}${input.language === "en" ? ":" : ":"}`, input.advisor.advice);
143
- }
144
- return reviewEnvelope(parts.join("\n"));
145
- }
146
-
147
- export function reviewEnvelope(content: string): string {
148
- return `<firecode_review>\n${content}\n</firecode_review>`;
149
- }
150
-
151
- function narrowInstruction(language: Language, narrowed: boolean) {
152
- if (!narrowed) return language === "en" ? FIX_INSTRUCTION_EN : FIX_INSTRUCTION_ZH;
153
- return language === "en" ? NARROW_INSTRUCTION_EN : NARROW_INSTRUCTION_ZH;
154
- }
155
-
156
- const FIX_INSTRUCTION_ZH =
157
- "本轮审查未通过,请修复以下发现。将审查反馈视为待核实假设,而非事实:先基于当前文件、测试/检查输出和会话约束核实。反馈属实时,逐条修复全部属实发现,修根因而非表象,同一根因的其他出现点一并修复,修完端到端验证问题已彻底解决后直接结束(本回合结束后会自动进入下一轮复审);避免无关重构、抽象、依赖或风格改动。反馈不成立时,不应用该反馈,并说明依据(文件、命令输出或约束)。";
158
-
159
- const NARROW_INSTRUCTION_ZH =
160
- "本轮审查未通过,但顾问判定发现清单范围过宽。下方是完整发现,仅供参考:只修顾问收窄后的范围内、真正阻塞当前需求的那部分,其余发现不要处理。先根据当前文件与命令输出核实再修,修根因不压表象,修复验证后直接结束(本回合结束后会自动进入下一轮复审);避免无关重构、抽象、依赖或风格改动。若认为收窄范围内的发现也不成立,说明依据并停下。";
161
-
162
- const NARROW_INSTRUCTION_EN =
163
- "This round's review failed, but the advisor judged the finding list too broad. The full findings below are context only: fix only the part inside the advisor's narrowed scope that actually blocks the current requirement, and leave the rest alone. Verify against current files and command output before fixing, fix root causes not symptoms, and finish directly after verification (the next review round will start automatically); avoid unrelated refactors, abstractions, dependency or style changes. If even the narrowed findings do not hold, explain why and stop.";
164
-
165
- /** 总结提示携带的终态材料上限:模型已经历过修复轮,材料只补它没见过的终态结论。 */
166
- const SUMMARY_MATERIAL_LIMIT = 4_000;
167
-
168
- export interface SummaryPromptInput {
169
- language: Language;
170
- kind: SummaryKind;
171
- rounds: number;
172
- /** 终态材料:通过=末轮审查结论;max_rounds=末轮发现;advisor_stop=顾问裁决。 */
173
- material: string;
174
- }
175
-
176
- /** 质量裁决终态后投给执行模型的总结回合提示:人话收尾,带反循环禁令。 */
177
- export function buildSummaryPrompt(input: SummaryPromptInput): string {
178
- const material = input.material.length > SUMMARY_MATERIAL_LIMIT
179
- ? `${input.material.slice(0, SUMMARY_MATERIAL_LIMIT)}\n…`
180
- : input.material;
181
- const body = summaryInstruction(input.language, input.kind, input.rounds);
182
- const content = material.trim()
183
- ? `${body}\n\n${summaryMaterialLabel(input.language, input.kind)}\n${material}`
184
- : body;
185
- return reviewEnvelope(content);
186
- }
187
-
188
- function summaryMaterialLabel(language: Language, kind: SummaryKind): string {
189
- if (kind === "advisor_stop") return language === "en" ? "Advisor ruling:" : "顾问裁决:";
190
- if (kind === "max_rounds") return language === "en" ? "Final round findings:" : "末轮未通过的发现:";
191
- return language === "en" ? "Final review verdict:" : "末轮审查结论:";
192
- }
193
-
194
- function summaryInstruction(language: Language, kind: SummaryKind, rounds: number): string {
195
- if (language === "en") {
196
- if (kind === "passed")
197
- return `The adversarial review passed after ${rounds} round(s). Give the user a concise plain-language wrap-up: 1) what the review rounds found and what you fixed; 2) how it finally passed (key fixes and verification evidence); 3) non-blocking suggestions raised during review — list each and say whether you recommend addressing it and why. Summary only: do not change code or run tools; end this turn with the final reply.`;
198
- if (kind === "max_rounds")
199
- return `The adversarial review did not pass within ${rounds} round(s) and stopped at the limit. Summarize honestly for the user in plain language: 1) what each round got stuck on and what fixes you attempted; 2) your root-cause read on why it would not converge; 3) the real current state of the code and remaining risks; 4) what you recommend the user do next. Summary only: do not keep changing code or run tools; end this turn with the final reply.`;
200
- return `The adversarial review was stopped by the advisor (round ${rounds}). Using the advisor ruling below, summarize for the user: 1) how far the review loop got and what was fixed; 2) why the advisor called it off; 3) the current state and your recommended next step. Summary only: do not change code or run tools; end this turn with the final reply.`;
201
- }
202
- if (kind === "passed")
203
- return `对抗审查已通过(共 ${rounds} 轮)。请给用户一个简洁的人话收尾总结:1) 各轮审查发现了什么、你修了什么;2) 最终靠什么通过(关键修复与验证证据);3) 审查中提到但未阻塞通过的建议——逐条列出,并给出你建议处理还是不处理及理由。只做总结:不要修改代码、不要运行工具,直接以最终回复结束本回合。`;
204
- if (kind === "max_rounds")
205
- return `对抗审查在 ${rounds} 轮内未能通过,已按上限终止。请如实向用户总结(人话):1) 各轮分别卡在什么发现上、你做了哪些修复尝试;2) 你认为无法收敛的根因;3) 当前代码的真实状态与剩余风险;4) 建议用户下一步怎么办。只做总结:不要再继续修改代码或运行工具,直接以最终回复结束本回合。`;
206
- return `对抗审查被顾问裁定终止(第 ${rounds} 轮)。请结合下方顾问裁决向用户总结:1) 审查循环走到了哪一步、修了什么;2) 顾问为什么叫停;3) 当前状态与你建议的下一步。只做总结:不要再修改代码或运行工具,直接以最终回复结束本回合。`;
207
- }
208
-
209
- const FIX_INSTRUCTION_EN =
210
- "This round's review failed. Fix the findings below. Treat the review feedback as hypotheses to verify, not facts: verify against current files, test/check output and session constraints. When feedback is valid, fix every valid finding, fixing root causes not symptoms and other occurrences of the same root cause, and verify end-to-end that issues are truly resolved then finish directly (the next review round will start automatically); avoid unrelated refactors, abstractions, dependency or style changes. When feedback is not valid, do not apply it and explain why (files, command output, or constraints).";