planpong 0.3.0 → 0.5.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.
Files changed (59) hide show
  1. package/dist/src/config/defaults.js +1 -0
  2. package/dist/src/config/defaults.js.map +1 -1
  3. package/dist/src/config/loader.d.ts +1 -0
  4. package/dist/src/config/loader.js +3 -0
  5. package/dist/src/config/loader.js.map +1 -1
  6. package/dist/src/core/apply-edits.d.ts +40 -0
  7. package/dist/src/core/apply-edits.js +220 -0
  8. package/dist/src/core/apply-edits.js.map +1 -0
  9. package/dist/src/core/convergence.d.ts +57 -4
  10. package/dist/src/core/convergence.js +134 -6
  11. package/dist/src/core/convergence.js.map +1 -1
  12. package/dist/src/core/loop.js +3 -3
  13. package/dist/src/core/loop.js.map +1 -1
  14. package/dist/src/core/operations.d.ts +14 -1
  15. package/dist/src/core/operations.js +592 -56
  16. package/dist/src/core/operations.js.map +1 -1
  17. package/dist/src/core/plan-diff.d.ts +23 -0
  18. package/dist/src/core/plan-diff.js +135 -0
  19. package/dist/src/core/plan-diff.js.map +1 -0
  20. package/dist/src/core/session.d.ts +11 -0
  21. package/dist/src/core/session.js +51 -1
  22. package/dist/src/core/session.js.map +1 -1
  23. package/dist/src/mcp/tools/get-feedback.d.ts +16 -0
  24. package/dist/src/mcp/tools/get-feedback.js +118 -114
  25. package/dist/src/mcp/tools/get-feedback.js.map +1 -1
  26. package/dist/src/mcp/tools/revise.d.ts +16 -0
  27. package/dist/src/mcp/tools/revise.js +76 -61
  28. package/dist/src/mcp/tools/revise.js.map +1 -1
  29. package/dist/src/mcp/tools/status.js +15 -1
  30. package/dist/src/mcp/tools/status.js.map +1 -1
  31. package/dist/src/prompts/planner.d.ts +34 -1
  32. package/dist/src/prompts/planner.js +272 -17
  33. package/dist/src/prompts/planner.js.map +1 -1
  34. package/dist/src/prompts/reviewer.d.ts +14 -1
  35. package/dist/src/prompts/reviewer.js +84 -1
  36. package/dist/src/prompts/reviewer.js.map +1 -1
  37. package/dist/src/providers/claude.d.ts +3 -0
  38. package/dist/src/providers/claude.js +151 -13
  39. package/dist/src/providers/claude.js.map +1 -1
  40. package/dist/src/providers/codex.d.ts +3 -0
  41. package/dist/src/providers/codex.js +150 -14
  42. package/dist/src/providers/codex.js.map +1 -1
  43. package/dist/src/providers/types.d.ts +69 -3
  44. package/dist/src/schemas/config.d.ts +3 -0
  45. package/dist/src/schemas/config.js +6 -0
  46. package/dist/src/schemas/config.js.map +1 -1
  47. package/dist/src/schemas/json-schema.d.ts +21 -0
  48. package/dist/src/schemas/json-schema.js +172 -0
  49. package/dist/src/schemas/json-schema.js.map +1 -0
  50. package/dist/src/schemas/metrics.d.ts +171 -0
  51. package/dist/src/schemas/metrics.js +49 -0
  52. package/dist/src/schemas/metrics.js.map +1 -0
  53. package/dist/src/schemas/revision.d.ts +166 -2
  54. package/dist/src/schemas/revision.js +35 -2
  55. package/dist/src/schemas/revision.js.map +1 -1
  56. package/dist/src/schemas/session.d.ts +6 -0
  57. package/dist/src/schemas/session.js +10 -0
  58. package/dist/src/schemas/session.js.map +1 -1
  59. package/package.json +4 -2
@@ -8,5 +8,6 @@ export const DEFAULT_CONFIG = {
8
8
  plans_dir: "docs/plans",
9
9
  max_rounds: 10,
10
10
  human_in_loop: true,
11
+ revision_mode: "full",
11
12
  };
12
13
  //# sourceMappingURL=defaults.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../../src/config/defaults.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,cAAc,GAAmB;IAC5C,OAAO,EAAE;QACP,QAAQ,EAAE,QAAQ;KACnB;IACD,QAAQ,EAAE;QACR,QAAQ,EAAE,OAAO;KAClB;IACD,SAAS,EAAE,YAAY;IACvB,UAAU,EAAE,EAAE;IACd,aAAa,EAAE,IAAI;CACpB,CAAC"}
1
+ {"version":3,"file":"defaults.js","sourceRoot":"","sources":["../../../src/config/defaults.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,cAAc,GAAmB;IAC5C,OAAO,EAAE;QACP,QAAQ,EAAE,QAAQ;KACnB;IACD,QAAQ,EAAE;QACR,QAAQ,EAAE,OAAO;KAClB;IACD,SAAS,EAAE,YAAY;IACvB,UAAU,EAAE,EAAE;IACd,aAAa,EAAE,IAAI;IACnB,aAAa,EAAE,MAAM;CACtB,CAAC"}
@@ -12,6 +12,7 @@ export interface LoadConfigOptions {
12
12
  plansDir: string;
13
13
  maxRounds: number;
14
14
  autonomous: boolean;
15
+ revisionMode: "edits" | "full";
15
16
  }>;
16
17
  }
17
18
  export declare function loadConfig(options: LoadConfigOptions): PlanpongConfig;
@@ -68,6 +68,9 @@ export function loadConfig(options) {
68
68
  ? !overrides.autonomous
69
69
  : (fileConfig.human_in_loop ??
70
70
  DEFAULT_CONFIG.human_in_loop),
71
+ revision_mode: overrides.revisionMode ??
72
+ fileConfig.revision_mode ??
73
+ DEFAULT_CONFIG.revision_mode,
71
74
  };
72
75
  return PlanpongConfigSchema.parse(merged);
73
76
  }
@@ -1 +1 @@
1
- {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../../src/config/loader.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"loader.js","sourceRoot":"","sources":["../../../src/config/loader.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ import type { ReplaceEdit } from "../schemas/revision.js";
2
+ export type EditFailureReason = "no-match" | "multi-match" | "section-not-found" | "status-line";
3
+ export interface EditResult {
4
+ edit: ReplaceEdit;
5
+ match_offset: number;
6
+ }
7
+ export interface EditFailure {
8
+ edit: ReplaceEdit;
9
+ reason: EditFailureReason;
10
+ section_searched: string | null;
11
+ diagnostic?: string;
12
+ }
13
+ export interface ApplyEditsResult {
14
+ plan: string;
15
+ applied: EditResult[];
16
+ failures: EditFailure[];
17
+ }
18
+ /**
19
+ * Apply a list of section-scoped text-replacement edits to a markdown plan.
20
+ *
21
+ * Edits are processed sequentially against the running plan — later edits
22
+ * see earlier edits' results. Each edit must locate its section heading and
23
+ * its `before` string must appear exactly once within that section's
24
+ * content. Failures are recorded but do NOT abort the run; surviving edits
25
+ * are applied. The caller decides whether to retry the failed edits.
26
+ *
27
+ * Pure: no filesystem access, no logging side-effects. The caller surfaces
28
+ * diagnostics via stderr or telemetry.
29
+ */
30
+ export declare function applyEdits(plan: string, edits: ReplaceEdit[]): ApplyEditsResult;
31
+ /**
32
+ * Build a stderr-friendly summary of edit application. Used by callers that
33
+ * want a one-line log per round.
34
+ */
35
+ export declare function summarizeApply(result: ApplyEditsResult): string;
36
+ /**
37
+ * Emit per-failure stderr diagnostics. Caller invokes this once after first-
38
+ * pass and once after retry pass.
39
+ */
40
+ export declare function logFailures(prefix: string, failures: EditFailure[]): void;
@@ -0,0 +1,220 @@
1
+ const STATUS_LINE_RE = /^\*\*planpong:\*\*[^\n]*$/m;
2
+ // Collapse a CRLF-or-LF run of trailing whitespace down to "no trailing
3
+ // whitespace on each line." This makes `before` matches tolerant to the
4
+ // kind of whitespace drift that's common when planners paraphrase
5
+ // surrounding content. We deliberately do NOT normalize internal
6
+ // whitespace — that would silently mis-match meaningful indentation.
7
+ function normalizeTrailingWhitespace(s) {
8
+ return s.replace(/\r\n/g, "\n").replace(/[ \t]+\n/g, "\n");
9
+ }
10
+ function parseSections(plan) {
11
+ const lines = plan.split("\n");
12
+ const boundaries = [];
13
+ let inFence = false;
14
+ // First pass: locate heading lines while tracking fenced code blocks
15
+ // (triple-backtick or triple-tilde). Lines inside fences never count as
16
+ // headings even if they start with `#`.
17
+ const headingLines = [];
18
+ for (let i = 0; i < lines.length; i++) {
19
+ const line = lines[i];
20
+ const fenceMatch = line.match(/^(```|~~~)/);
21
+ if (fenceMatch) {
22
+ inFence = !inFence;
23
+ continue;
24
+ }
25
+ if (inFence)
26
+ continue;
27
+ const headingMatch = line.match(/^(#{1,6})\s+(.+?)\s*$/);
28
+ if (!headingMatch)
29
+ continue;
30
+ headingLines.push({
31
+ index: i,
32
+ level: headingMatch[1].length,
33
+ heading: headingMatch[2].trim(),
34
+ });
35
+ }
36
+ // Convert heading lines to character offsets and resolve section ends:
37
+ // section i ends where the next heading of level <= i.level begins.
38
+ // Pre-compute line start offsets once.
39
+ const lineStarts = [0];
40
+ for (let i = 0; i < lines.length; i++) {
41
+ lineStarts.push(lineStarts[i] + lines[i].length + 1); // +1 for the \n
42
+ }
43
+ for (let h = 0; h < headingLines.length; h++) {
44
+ const cur = headingLines[h];
45
+ const start = lineStarts[cur.index];
46
+ const contentStart = lineStarts[cur.index + 1] ?? plan.length;
47
+ let end = plan.length;
48
+ for (let n = h + 1; n < headingLines.length; n++) {
49
+ if (headingLines[n].level <= cur.level) {
50
+ end = lineStarts[headingLines[n].index];
51
+ break;
52
+ }
53
+ }
54
+ boundaries.push({
55
+ heading: cur.heading,
56
+ level: cur.level,
57
+ start,
58
+ end,
59
+ contentStart,
60
+ });
61
+ }
62
+ return boundaries;
63
+ }
64
+ function findSection(boundaries, label) {
65
+ const target = label.trim();
66
+ const matches = boundaries.filter((b) => b.heading === target);
67
+ if (matches.length === 0)
68
+ return null;
69
+ // First-match-wins for duplicate-labeled headings. The ROUND-3 reviewer
70
+ // flagged this as silent-mis-application risk; the plan as approved keeps
71
+ // first-match + warning. The duplicate count is surfaced so the caller can
72
+ // emit the warning to stderr.
73
+ return { boundary: matches[0], duplicateCount: matches.length };
74
+ }
75
+ function indexOfAllOccurrences(haystack, needle) {
76
+ const offsets = [];
77
+ if (needle.length === 0)
78
+ return offsets;
79
+ let from = 0;
80
+ while (true) {
81
+ const idx = haystack.indexOf(needle, from);
82
+ if (idx === -1)
83
+ return offsets;
84
+ offsets.push(idx);
85
+ from = idx + 1;
86
+ }
87
+ }
88
+ function modifiesStatusLine(plan, range) {
89
+ const match = STATUS_LINE_RE.exec(plan);
90
+ if (!match)
91
+ return false;
92
+ const lineStart = match.index;
93
+ const lineEnd = match.index + match[0].length;
94
+ return range.start < lineEnd && range.end > lineStart;
95
+ }
96
+ /**
97
+ * Apply a list of section-scoped text-replacement edits to a markdown plan.
98
+ *
99
+ * Edits are processed sequentially against the running plan — later edits
100
+ * see earlier edits' results. Each edit must locate its section heading and
101
+ * its `before` string must appear exactly once within that section's
102
+ * content. Failures are recorded but do NOT abort the run; surviving edits
103
+ * are applied. The caller decides whether to retry the failed edits.
104
+ *
105
+ * Pure: no filesystem access, no logging side-effects. The caller surfaces
106
+ * diagnostics via stderr or telemetry.
107
+ */
108
+ export function applyEdits(plan, edits) {
109
+ let working = plan.replace(/\r\n/g, "\n");
110
+ const applied = [];
111
+ const failures = [];
112
+ for (const edit of edits) {
113
+ const boundaries = parseSections(working);
114
+ const sectionLabel = edit.section.trim();
115
+ const sectionHit = findSection(boundaries, sectionLabel);
116
+ if (!sectionHit) {
117
+ failures.push({
118
+ edit,
119
+ reason: "section-not-found",
120
+ section_searched: sectionLabel,
121
+ });
122
+ continue;
123
+ }
124
+ const { boundary } = sectionHit;
125
+ const sectionContent = working.slice(boundary.contentStart, boundary.end);
126
+ const normalizedSection = normalizeTrailingWhitespace(sectionContent);
127
+ const normalizedBefore = normalizeTrailingWhitespace(edit.before);
128
+ const offsets = indexOfAllOccurrences(normalizedSection, normalizedBefore);
129
+ if (offsets.length === 0) {
130
+ // Plan-wide diagnostic: locate where the unscoped match would have
131
+ // landed, if anywhere. This is INFORMATIONAL — never applied.
132
+ const planWide = indexOfAllOccurrences(normalizeTrailingWhitespace(working), normalizedBefore);
133
+ const diagnostic = planWide.length
134
+ ? `would have matched at offset ${planWide[0]} of plan (cross-section)`
135
+ : "no plan-wide match either";
136
+ failures.push({
137
+ edit,
138
+ reason: "no-match",
139
+ section_searched: sectionLabel,
140
+ diagnostic,
141
+ });
142
+ continue;
143
+ }
144
+ if (offsets.length > 1) {
145
+ failures.push({
146
+ edit,
147
+ reason: "multi-match",
148
+ section_searched: sectionLabel,
149
+ diagnostic: `${offsets.length} occurrences within section`,
150
+ });
151
+ continue;
152
+ }
153
+ // Unique match. Map normalized offset back to the un-normalized
154
+ // working text. Because normalization only collapses TRAILING
155
+ // whitespace per line, the byte offset within the section is invariant
156
+ // up to the first normalized character — we can locate the match in
157
+ // the un-normalized section by re-scanning with the same `before` and
158
+ // selecting the matching occurrence by index. Edit count is 1, so
159
+ // index 0 of the un-normalized matches.
160
+ const unNormalizedOffsets = indexOfAllOccurrences(sectionContent, edit.before);
161
+ let absoluteStart;
162
+ let matchedLength;
163
+ if (unNormalizedOffsets.length === 1) {
164
+ absoluteStart = boundary.contentStart + unNormalizedOffsets[0];
165
+ matchedLength = edit.before.length;
166
+ }
167
+ else {
168
+ // Trailing-whitespace differences caused the un-normalized match to
169
+ // shift. Fall back to the normalized offset, treat the original
170
+ // before string as the replacement region length. This is rare —
171
+ // typically when the planner stripped a trailing space the file
172
+ // happens to have. Accept the small risk; the alternative is
173
+ // rejecting the edit and the planner gets a retry anyway.
174
+ absoluteStart = boundary.contentStart + offsets[0];
175
+ // Find the actual matched substring in the original by re-scanning
176
+ // tolerantly. Use the section content's character range that
177
+ // contains the normalized hit.
178
+ const candidate = sectionContent.slice(offsets[0], offsets[0] + edit.before.length + 32);
179
+ const restored = candidate.match(new RegExp(edit.before
180
+ .replace(/\r\n/g, "\n")
181
+ .replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
182
+ .replace(/[ \t]+\n/g, "[ \\t]*\\n")));
183
+ matchedLength = restored ? restored[0].length : edit.before.length;
184
+ }
185
+ const range = { start: absoluteStart, end: absoluteStart + matchedLength };
186
+ if (modifiesStatusLine(working, range)) {
187
+ failures.push({
188
+ edit,
189
+ reason: "status-line",
190
+ section_searched: sectionLabel,
191
+ });
192
+ continue;
193
+ }
194
+ working =
195
+ working.slice(0, range.start) + edit.after + working.slice(range.end);
196
+ applied.push({ edit, match_offset: range.start });
197
+ }
198
+ return { plan: working, applied, failures };
199
+ }
200
+ /**
201
+ * Build a stderr-friendly summary of edit application. Used by callers that
202
+ * want a one-line log per round.
203
+ */
204
+ export function summarizeApply(result) {
205
+ return `applied=${result.applied.length} failed=${result.failures.length}` +
206
+ (result.failures.length
207
+ ? ` reasons=${result.failures.map((f) => f.reason).join(",")}`
208
+ : "");
209
+ }
210
+ /**
211
+ * Emit per-failure stderr diagnostics. Caller invokes this once after first-
212
+ * pass and once after retry pass.
213
+ */
214
+ export function logFailures(prefix, failures) {
215
+ for (const f of failures) {
216
+ const detail = f.diagnostic ? ` (${f.diagnostic})` : "";
217
+ process.stderr.write(`[planpong] ${prefix}: edit failed (${f.reason}) section="${f.section_searched ?? "?"}"${detail}\n`);
218
+ }
219
+ }
220
+ //# sourceMappingURL=apply-edits.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apply-edits.js","sourceRoot":"","sources":["../../../src/core/apply-edits.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,24 @@
1
+ import { ZodError } from "zod";
1
2
  import { type ReviewFeedback, type PhaseFeedback } from "../schemas/feedback.js";
2
3
  import { type PlannerRevision } from "../schemas/revision.js";
3
4
  import type { ReviewPhase } from "../prompts/reviewer.js";
5
+ /**
6
+ * Thrown when structured output produces text that is not valid JSON.
7
+ * The state machine treats this as a downgrade-eligible failure.
8
+ */
9
+ export declare class StructuredOutputParseError extends Error {
10
+ constructor(message: string);
11
+ }
12
+ /**
13
+ * Thrown when structured output produces valid JSON that fails Zod
14
+ * validation (e.g., a refinement violation). The state machine treats
15
+ * this as terminal — the structured output mechanism worked, the model
16
+ * just produced semantically invalid content. Retrying won't help.
17
+ */
18
+ export declare class ZodValidationError extends Error {
19
+ readonly zodError: ZodError;
20
+ constructor(message: string, zodError: ZodError);
21
+ }
4
22
  /**
5
23
  * Extract JSON from between sentinel tags. Falls back to finding JSON in
6
24
  * code fences, then tries parsing the entire content as JSON.
@@ -8,10 +26,45 @@ import type { ReviewPhase } from "../prompts/reviewer.js";
8
26
  export declare function extractJSON(content: string, tag: string): string | null;
9
27
  export declare function parseFeedback(content: string): ReviewFeedback;
10
28
  /**
11
- * Phase-aware feedback parser. Tries the phase-specific parser first,
12
- * falls back to base parser, then applies verdict coercion and blocked
13
- * rationale validation.
29
+ * Parse structured-output feedback. The model output is guaranteed to be
30
+ * valid JSON conforming to the JSON Schema we passed to the CLI, so we
31
+ * skip tag/fence extraction and parse directly. Throws:
32
+ * - `StructuredOutputParseError` if JSON.parse fails (downgrade-eligible)
33
+ * - `ZodValidationError` if Zod validation fails (terminal)
34
+ */
35
+ export declare function parseStructuredFeedbackForPhase(content: string, phase: ReviewPhase): PhaseFeedback;
36
+ /**
37
+ * Parse structured-output revision (planner response). Same contract as
38
+ * `parseStructuredFeedbackForPhase`: throws `StructuredOutputParseError`
39
+ * for JSON failures and `ZodValidationError` for Zod failures.
40
+ */
41
+ export type RevisionShape = "full" | "edits";
42
+ /**
43
+ * Phase-aware structured-output revision parser.
44
+ *
45
+ * - `shape: "full"` (direction phase or `revision_mode: "full"` config):
46
+ * accepts only `{ responses, updated_plan }`. Edits payloads are rejected.
47
+ * - `shape: "edits"` (risk + detail with `revision_mode: "edits"`): accepts
48
+ * only `{ responses, edits }`. `updated_plan` payloads are rejected.
49
+ *
50
+ * Strict-mode `.strict()` on the Zod schemas already rejects extra fields,
51
+ * so a payload mixing both shapes fails validation. No silent normalization.
52
+ *
53
+ * Throws `StructuredOutputParseError` for JSON failures (downgrade-eligible)
54
+ * and `ZodValidationError` for shape failures (terminal — the model violated
55
+ * the schema).
56
+ */
57
+ export declare function parseStructuredRevision(content: string, shape?: RevisionShape): PlannerRevision;
58
+ /**
59
+ * Phase-aware feedback parser (LEGACY/DEGRADATION MODE).
60
+ *
61
+ * TODO: deprecate when structured output is stable across all providers.
62
+ *
63
+ * Tries the phase-specific parser first, falls back to base parser, then
64
+ * applies verdict coercion and blocked rationale validation. Used when a
65
+ * provider does not support structured output, or as a fallback when
66
+ * structured output fails.
14
67
  */
15
68
  export declare function parseFeedbackForPhase(content: string, phase: ReviewPhase): PhaseFeedback;
16
- export declare function parseRevision(content: string): PlannerRevision;
69
+ export declare function parseRevision(content: string, shape?: RevisionShape): PlannerRevision;
17
70
  export declare function isConverged(feedback: PhaseFeedback): boolean;
@@ -1,5 +1,29 @@
1
1
  import { ReviewFeedbackSchema, DirectionFeedbackSchema, RiskFeedbackSchema, } from "../schemas/feedback.js";
2
- import { PlannerRevisionSchema, } from "../schemas/revision.js";
2
+ import { DirectionRevisionSchema, EditsRevisionSchema, } from "../schemas/revision.js";
3
+ /**
4
+ * Thrown when structured output produces text that is not valid JSON.
5
+ * The state machine treats this as a downgrade-eligible failure.
6
+ */
7
+ export class StructuredOutputParseError extends Error {
8
+ constructor(message) {
9
+ super(message);
10
+ this.name = "StructuredOutputParseError";
11
+ }
12
+ }
13
+ /**
14
+ * Thrown when structured output produces valid JSON that fails Zod
15
+ * validation (e.g., a refinement violation). The state machine treats
16
+ * this as terminal — the structured output mechanism worked, the model
17
+ * just produced semantically invalid content. Retrying won't help.
18
+ */
19
+ export class ZodValidationError extends Error {
20
+ zodError;
21
+ constructor(message, zodError) {
22
+ super(message);
23
+ this.name = "ZodValidationError";
24
+ this.zodError = zodError;
25
+ }
26
+ }
3
27
  /**
4
28
  * Extract JSON from between sentinel tags. Falls back to finding JSON in
5
29
  * code fences, then tries parsing the entire content as JSON.
@@ -102,9 +126,112 @@ function extractArrayFromRaw(content, field) {
102
126
  return null;
103
127
  }
104
128
  /**
105
- * Phase-aware feedback parser. Tries the phase-specific parser first,
106
- * falls back to base parser, then applies verdict coercion and blocked
107
- * rationale validation.
129
+ * Recursively strip `null` property values from an object. OpenAI-strict
130
+ * structured output requires every optional property to be present as
131
+ * `null`, but our Zod schemas use `.optional()` which expects missing keys
132
+ * (not nulls). This adapter removes nulls so Zod validation succeeds.
133
+ *
134
+ * Only strips top-level and nested object properties that are `null`;
135
+ * array elements and primitive values are preserved.
136
+ */
137
+ function stripNullProperties(value) {
138
+ if (Array.isArray(value))
139
+ return value.map(stripNullProperties);
140
+ if (value && typeof value === "object") {
141
+ const result = {};
142
+ for (const [key, v] of Object.entries(value)) {
143
+ if (v === null)
144
+ continue;
145
+ result[key] = stripNullProperties(v);
146
+ }
147
+ return result;
148
+ }
149
+ return value;
150
+ }
151
+ /**
152
+ * Parse structured-output feedback. The model output is guaranteed to be
153
+ * valid JSON conforming to the JSON Schema we passed to the CLI, so we
154
+ * skip tag/fence extraction and parse directly. Throws:
155
+ * - `StructuredOutputParseError` if JSON.parse fails (downgrade-eligible)
156
+ * - `ZodValidationError` if Zod validation fails (terminal)
157
+ */
158
+ export function parseStructuredFeedbackForPhase(content, phase) {
159
+ let parsed;
160
+ try {
161
+ parsed = JSON.parse(content);
162
+ }
163
+ catch (error) {
164
+ throw new StructuredOutputParseError(`Structured output is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
165
+ }
166
+ // OpenAI-strict output includes optional fields as null; Zod expects them missing.
167
+ parsed = stripNullProperties(parsed);
168
+ const schema = phase === "direction"
169
+ ? DirectionFeedbackSchema
170
+ : phase === "risk"
171
+ ? RiskFeedbackSchema
172
+ : ReviewFeedbackSchema;
173
+ const result = schema.safeParse(parsed);
174
+ if (!result.success) {
175
+ throw new ZodValidationError(`Structured output failed Zod validation for ${phase} phase: ${result.error.message}`, result.error);
176
+ }
177
+ // Apply blocked rationale validation (same rules as legacy path)
178
+ const feedback = result.data;
179
+ if (phase === "direction" && feedback.verdict === "blocked") {
180
+ const direction = feedback;
181
+ if (!direction.approach_assessment?.trim()) {
182
+ console.warn("[planpong] Blocked verdict without approach_assessment rationale — coercing to needs_revision");
183
+ return { ...direction, verdict: "needs_revision" };
184
+ }
185
+ }
186
+ if (phase === "risk" && feedback.verdict === "blocked") {
187
+ const risk = feedback;
188
+ if (!risk.risks || risk.risks.length === 0) {
189
+ console.warn("[planpong] Blocked verdict without risks rationale — coercing to needs_revision");
190
+ return { ...risk, verdict: "needs_revision" };
191
+ }
192
+ }
193
+ return feedback;
194
+ }
195
+ /**
196
+ * Phase-aware structured-output revision parser.
197
+ *
198
+ * - `shape: "full"` (direction phase or `revision_mode: "full"` config):
199
+ * accepts only `{ responses, updated_plan }`. Edits payloads are rejected.
200
+ * - `shape: "edits"` (risk + detail with `revision_mode: "edits"`): accepts
201
+ * only `{ responses, edits }`. `updated_plan` payloads are rejected.
202
+ *
203
+ * Strict-mode `.strict()` on the Zod schemas already rejects extra fields,
204
+ * so a payload mixing both shapes fails validation. No silent normalization.
205
+ *
206
+ * Throws `StructuredOutputParseError` for JSON failures (downgrade-eligible)
207
+ * and `ZodValidationError` for shape failures (terminal — the model violated
208
+ * the schema).
209
+ */
210
+ export function parseStructuredRevision(content, shape = "full") {
211
+ let parsed;
212
+ try {
213
+ parsed = JSON.parse(content);
214
+ }
215
+ catch (error) {
216
+ throw new StructuredOutputParseError(`Structured output is not valid JSON: ${error instanceof Error ? error.message : String(error)}`);
217
+ }
218
+ parsed = stripNullProperties(parsed);
219
+ const schema = shape === "edits" ? EditsRevisionSchema : DirectionRevisionSchema;
220
+ const result = schema.safeParse(parsed);
221
+ if (!result.success) {
222
+ throw new ZodValidationError(`Structured output failed Zod validation for ${shape} revision: ${result.error.message}`, result.error);
223
+ }
224
+ return result.data;
225
+ }
226
+ /**
227
+ * Phase-aware feedback parser (LEGACY/DEGRADATION MODE).
228
+ *
229
+ * TODO: deprecate when structured output is stable across all providers.
230
+ *
231
+ * Tries the phase-specific parser first, falls back to base parser, then
232
+ * applies verdict coercion and blocked rationale validation. Used when a
233
+ * provider does not support structured output, or as a fallback when
234
+ * structured output fails.
108
235
  */
109
236
  export function parseFeedbackForPhase(content, phase) {
110
237
  if (phase === "detail") {
@@ -196,7 +323,7 @@ export function parseFeedbackForPhase(content, phase) {
196
323
  missing_phase_fields: missingFields,
197
324
  };
198
325
  }
199
- export function parseRevision(content) {
326
+ export function parseRevision(content, shape = "full") {
200
327
  const json = extractJSON(content, "planpong-revision");
201
328
  if (!json) {
202
329
  throw new Error("Could not extract revision JSON from planner output. Expected <planpong-revision> tags, JSON code fence, or raw JSON object.");
@@ -208,7 +335,8 @@ export function parseRevision(content) {
208
335
  catch {
209
336
  throw new Error(`Invalid JSON in planner output:\n${json.slice(0, 200)}`);
210
337
  }
211
- return PlannerRevisionSchema.parse(parsed);
338
+ const schema = shape === "edits" ? EditsRevisionSchema : DirectionRevisionSchema;
339
+ return schema.parse(parsed);
212
340
  }
213
341
  export function isConverged(feedback) {
214
342
  return feedback.verdict !== "needs_revision";
@@ -1 +1 @@
1
- {"version":3,"file":"convergence.js","sourceRoot":"","sources":["../../../src/core/convergence.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"convergence.js","sourceRoot":"","sources":["../../../src/core/convergence.ts"],"names":[],"mappings":"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"}
@@ -34,14 +34,14 @@ export async function runLoop(options) {
34
34
  model: config.planner.model,
35
35
  effort: config.planner.effort,
36
36
  });
37
- if (planResponse.exitCode !== 0) {
38
- throw new Error(`Planner failed (exit ${planResponse.exitCode}):\n${planResponse.content.slice(0, 500)}`);
37
+ if (!planResponse.ok) {
38
+ throw new Error(`Planner failed (exit ${planResponse.error.exitCode}):\n${planResponse.error.message}`);
39
39
  }
40
40
  // Step 3: Write plan to disk
41
41
  const filename = resolvePlanSlug(plansDir, planName);
42
42
  const planPath = join(plansDir, filename);
43
43
  const relativePlanPath = relative(cwd, planPath);
44
- let planContent = planResponse.content;
44
+ let planContent = planResponse.output;
45
45
  const initialStatusLine = `**planpong:** R0/${config.max_rounds} | ${formatProviderLabel(config.planner)} → ${formatProviderLabel(config.reviewer)} | Awaiting review`;
46
46
  planContent = updatePlanStatusLine(planContent, initialStatusLine);
47
47
  writeFileSync(planPath, planContent);