playwright-intelligence-engine 0.0.0-stage → 2.0.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 (353) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +229 -3
  3. package/bin/pw-intel.js +37 -0
  4. package/dist/authoring/author.d.ts +83 -0
  5. package/dist/authoring/author.d.ts.map +1 -0
  6. package/dist/authoring/generation-contract.d.ts +90 -0
  7. package/dist/authoring/generation-contract.d.ts.map +1 -0
  8. package/dist/authoring/hybrid-author.d.ts +6 -0
  9. package/dist/authoring/hybrid-author.d.ts.map +1 -0
  10. package/dist/authoring/index.d.ts +7 -0
  11. package/dist/authoring/index.d.ts.map +1 -0
  12. package/dist/authoring/llm-author.d.ts +28 -0
  13. package/dist/authoring/llm-author.d.ts.map +1 -0
  14. package/dist/authoring/naming/index.d.ts +4 -0
  15. package/dist/authoring/naming/index.d.ts.map +1 -0
  16. package/dist/authoring/naming/name-plan.d.ts +45 -0
  17. package/dist/authoring/naming/name-plan.d.ts.map +1 -0
  18. package/dist/authoring/naming/name-planner.d.ts +25 -0
  19. package/dist/authoring/naming/name-planner.d.ts.map +1 -0
  20. package/dist/authoring/naming/naming-linter.d.ts +20 -0
  21. package/dist/authoring/naming/naming-linter.d.ts.map +1 -0
  22. package/dist/authoring/template-author.d.ts +6 -0
  23. package/dist/authoring/template-author.d.ts.map +1 -0
  24. package/dist/cli/index.d.ts +2 -0
  25. package/dist/cli/index.d.ts.map +1 -0
  26. package/dist/collectors/collector-interface.d.ts +7 -0
  27. package/dist/collectors/collector-interface.d.ts.map +1 -0
  28. package/dist/collectors/environment-collector.d.ts +12 -0
  29. package/dist/collectors/environment-collector.d.ts.map +1 -0
  30. package/dist/collectors/error-collector.d.ts +18 -0
  31. package/dist/collectors/error-collector.d.ts.map +1 -0
  32. package/dist/collectors/git-diff-collector.d.ts +20 -0
  33. package/dist/collectors/git-diff-collector.d.ts.map +1 -0
  34. package/dist/collectors/live-browser-collector.d.ts +45 -0
  35. package/dist/collectors/live-browser-collector.d.ts.map +1 -0
  36. package/dist/collectors/network-collector.d.ts +28 -0
  37. package/dist/collectors/network-collector.d.ts.map +1 -0
  38. package/dist/collectors/static-ast-collector.d.ts +32 -0
  39. package/dist/collectors/static-ast-collector.d.ts.map +1 -0
  40. package/dist/collectors/trace-collector.d.ts +12 -0
  41. package/dist/collectors/trace-collector.d.ts.map +1 -0
  42. package/dist/config/project-config.d.ts +33 -0
  43. package/dist/config/project-config.d.ts.map +1 -0
  44. package/dist/core/errors/engine-errors.d.ts +34 -0
  45. package/dist/core/errors/engine-errors.d.ts.map +1 -0
  46. package/dist/core/types/diagnostic-context.d.ts +151 -0
  47. package/dist/core/types/diagnostic-context.d.ts.map +1 -0
  48. package/dist/core/types/repair-evidence.d.ts +56 -0
  49. package/dist/core/types/repair-evidence.d.ts.map +1 -0
  50. package/dist/core/version.d.ts +13 -0
  51. package/dist/core/version.d.ts.map +1 -0
  52. package/dist/diagnostics/diagnostic-contract.d.ts +52 -0
  53. package/dist/diagnostics/diagnostic-contract.d.ts.map +1 -0
  54. package/dist/diagnostics/diagnostic-report.d.ts +11 -0
  55. package/dist/diagnostics/diagnostic-report.d.ts.map +1 -0
  56. package/dist/diagnostics/evidence-correlator.d.ts +22 -0
  57. package/dist/diagnostics/evidence-correlator.d.ts.map +1 -0
  58. package/dist/diagnostics/external-failure-evidence.d.ts +18 -0
  59. package/dist/diagnostics/external-failure-evidence.d.ts.map +1 -0
  60. package/dist/diagnostics/failure-category.d.ts +4 -0
  61. package/dist/diagnostics/failure-category.d.ts.map +1 -0
  62. package/dist/diagnostics/failure-classifier.d.ts +58 -0
  63. package/dist/diagnostics/failure-classifier.d.ts.map +1 -0
  64. package/dist/evaluation/evaluation-scorecard.d.ts +72 -0
  65. package/dist/evaluation/evaluation-scorecard.d.ts.map +1 -0
  66. package/dist/evaluation/repository-inspector.d.ts +21 -0
  67. package/dist/evaluation/repository-inspector.d.ts.map +1 -0
  68. package/dist/evaluation/repository-runner.d.ts +24 -0
  69. package/dist/evaluation/repository-runner.d.ts.map +1 -0
  70. package/dist/evidence/evidence-model.d.ts +88 -0
  71. package/dist/evidence/evidence-model.d.ts.map +1 -0
  72. package/dist/evidence/evidence-normalizer.d.ts +19 -0
  73. package/dist/evidence/evidence-normalizer.d.ts.map +1 -0
  74. package/dist/evidence/evidence-redactor.d.ts +37 -0
  75. package/dist/evidence/evidence-redactor.d.ts.map +1 -0
  76. package/dist/framework/config-adapter.d.ts +28 -0
  77. package/dist/framework/config-adapter.d.ts.map +1 -0
  78. package/dist/gate/index.d.ts +21 -0
  79. package/dist/gate/index.d.ts.map +1 -0
  80. package/dist/gate/oracle.d.ts +26 -0
  81. package/dist/gate/oracle.d.ts.map +1 -0
  82. package/dist/gate/quality.d.ts +27 -0
  83. package/dist/gate/quality.d.ts.map +1 -0
  84. package/dist/gate/safety.d.ts +28 -0
  85. package/dist/gate/safety.d.ts.map +1 -0
  86. package/dist/generator/architecture-generator.d.ts +55 -0
  87. package/dist/generator/architecture-generator.d.ts.map +1 -0
  88. package/dist/generator/assertion-quality-gate.d.ts +18 -0
  89. package/dist/generator/assertion-quality-gate.d.ts.map +1 -0
  90. package/dist/generator/candidate-writer.d.ts +34 -0
  91. package/dist/generator/candidate-writer.d.ts.map +1 -0
  92. package/dist/generator/test-generator.d.ts +26 -0
  93. package/dist/generator/test-generator.d.ts.map +1 -0
  94. package/dist/governance/audit-log.d.ts +23 -0
  95. package/dist/governance/audit-log.d.ts.map +1 -0
  96. package/dist/governance/governance-policy.d.ts +24 -0
  97. package/dist/governance/governance-policy.d.ts.map +1 -0
  98. package/dist/governance/team-dashboard.d.ts +51 -0
  99. package/dist/governance/team-dashboard.d.ts.map +1 -0
  100. package/dist/governance/workflow-audit.d.ts +13 -0
  101. package/dist/governance/workflow-audit.d.ts.map +1 -0
  102. package/dist/index.d.ts +90 -0
  103. package/dist/index.d.ts.map +1 -0
  104. package/dist/index.js +20318 -0
  105. package/dist/integrations/ci-summary.d.ts +14 -0
  106. package/dist/integrations/ci-summary.d.ts.map +1 -0
  107. package/dist/integrations/playwright-report-adapter.d.ts +22 -0
  108. package/dist/integrations/playwright-report-adapter.d.ts.map +1 -0
  109. package/dist/integrations/quality-gate.d.ts +35 -0
  110. package/dist/integrations/quality-gate.d.ts.map +1 -0
  111. package/dist/integrations/sarif-report.d.ts +48 -0
  112. package/dist/integrations/sarif-report.d.ts.map +1 -0
  113. package/dist/intelligence/autonomous-repair-workflow.d.ts +31 -0
  114. package/dist/intelligence/autonomous-repair-workflow.d.ts.map +1 -0
  115. package/dist/intelligence/business-intent.d.ts +58 -0
  116. package/dist/intelligence/business-intent.d.ts.map +1 -0
  117. package/dist/intelligence/evidence-bundle.d.ts +364 -0
  118. package/dist/intelligence/evidence-bundle.d.ts.map +1 -0
  119. package/dist/intelligence/failure-clustering.d.ts +38 -0
  120. package/dist/intelligence/failure-clustering.d.ts.map +1 -0
  121. package/dist/intelligence/generation-agent.d.ts +120 -0
  122. package/dist/intelligence/generation-agent.d.ts.map +1 -0
  123. package/dist/intelligence/generation-verifier.d.ts +151 -0
  124. package/dist/intelligence/generation-verifier.d.ts.map +1 -0
  125. package/dist/intelligence/llm-proposal.d.ts +377 -0
  126. package/dist/intelligence/llm-proposal.d.ts.map +1 -0
  127. package/dist/intelligence/locator-disambiguation-engine.d.ts +38 -0
  128. package/dist/intelligence/locator-disambiguation-engine.d.ts.map +1 -0
  129. package/dist/intelligence/model-contract.d.ts +86 -0
  130. package/dist/intelligence/model-contract.d.ts.map +1 -0
  131. package/dist/intelligence/model-provider.d.ts +75 -0
  132. package/dist/intelligence/model-provider.d.ts.map +1 -0
  133. package/dist/intelligence/project-intelligence.d.ts +22 -0
  134. package/dist/intelligence/project-intelligence.d.ts.map +1 -0
  135. package/dist/intelligence/repair-agent.d.ts +46 -0
  136. package/dist/intelligence/repair-agent.d.ts.map +1 -0
  137. package/dist/intelligence/repository-profile.d.ts +131 -0
  138. package/dist/intelligence/repository-profile.d.ts.map +1 -0
  139. package/dist/intelligence/suite-intelligence.d.ts +59 -0
  140. package/dist/intelligence/suite-intelligence.d.ts.map +1 -0
  141. package/dist/intelligence/test-intelligence.d.ts +44 -0
  142. package/dist/intelligence/test-intelligence.d.ts.map +1 -0
  143. package/dist/intelligence/trace-proposal-workflow.d.ts +30 -0
  144. package/dist/intelligence/trace-proposal-workflow.d.ts.map +1 -0
  145. package/dist/intelligence/verified-llm-repair-workflow.d.ts +21 -0
  146. package/dist/intelligence/verified-llm-repair-workflow.d.ts.map +1 -0
  147. package/dist/intelligence/workflow-planner.d.ts +278 -0
  148. package/dist/intelligence/workflow-planner.d.ts.map +1 -0
  149. package/dist/mcp/server.d.ts +32 -0
  150. package/dist/mcp/server.d.ts.map +1 -0
  151. package/dist/memory/stability-ledger.d.ts +46 -0
  152. package/dist/memory/stability-ledger.d.ts.map +1 -0
  153. package/dist/perception/app-model.d.ts +55 -0
  154. package/dist/perception/app-model.d.ts.map +1 -0
  155. package/dist/perception/crawler.d.ts +18 -0
  156. package/dist/perception/crawler.d.ts.map +1 -0
  157. package/dist/perception/index.d.ts +3 -0
  158. package/dist/perception/index.d.ts.map +1 -0
  159. package/dist/repair/approval-service.d.ts +139 -0
  160. package/dist/repair/approval-service.d.ts.map +1 -0
  161. package/dist/repair/assertion-hasher.d.ts +62 -0
  162. package/dist/repair/assertion-hasher.d.ts.map +1 -0
  163. package/dist/repair/ast-patcher.d.ts +36 -0
  164. package/dist/repair/ast-patcher.d.ts.map +1 -0
  165. package/dist/repair/browser-operation-executor.d.ts +27 -0
  166. package/dist/repair/browser-operation-executor.d.ts.map +1 -0
  167. package/dist/repair/diff-allowlist.d.ts +74 -0
  168. package/dist/repair/diff-allowlist.d.ts.map +1 -0
  169. package/dist/repair/intent-validator.d.ts +157 -0
  170. package/dist/repair/intent-validator.d.ts.map +1 -0
  171. package/dist/repair/repair-operations.d.ts +40 -0
  172. package/dist/repair/repair-operations.d.ts.map +1 -0
  173. package/dist/repair/repair-planner.d.ts +98 -0
  174. package/dist/repair/repair-planner.d.ts.map +1 -0
  175. package/dist/repair/repair-workflow.d.ts +42 -0
  176. package/dist/repair/repair-workflow.d.ts.map +1 -0
  177. package/dist/repair/safe-operation-compiler.d.ts +15 -0
  178. package/dist/repair/safe-operation-compiler.d.ts.map +1 -0
  179. package/dist/repair/source-mapper.d.ts +19 -0
  180. package/dist/repair/source-mapper.d.ts.map +1 -0
  181. package/dist/repair/statement-patcher.d.ts +75 -0
  182. package/dist/repair/statement-patcher.d.ts.map +1 -0
  183. package/dist/repair/verification-gates.d.ts +25 -0
  184. package/dist/repair/verification-gates.d.ts.map +1 -0
  185. package/dist/repair/workflow-plan.d.ts +35 -0
  186. package/dist/repair/workflow-plan.d.ts.map +1 -0
  187. package/dist/repair/workspace-promoter.d.ts +43 -0
  188. package/dist/repair/workspace-promoter.d.ts.map +1 -0
  189. package/dist/reproduce/diagnostic-rerunner.d.ts +40 -0
  190. package/dist/reproduce/diagnostic-rerunner.d.ts.map +1 -0
  191. package/dist/reproduce/direct-browser-investigator.d.ts +41 -0
  192. package/dist/reproduce/direct-browser-investigator.d.ts.map +1 -0
  193. package/dist/reproduce/interactive-workflow-runner.d.ts +43 -0
  194. package/dist/reproduce/interactive-workflow-runner.d.ts.map +1 -0
  195. package/dist/sandbox/dependency-strategy.d.ts +45 -0
  196. package/dist/sandbox/dependency-strategy.d.ts.map +1 -0
  197. package/dist/sandbox/execution-runner.d.ts +81 -0
  198. package/dist/sandbox/execution-runner.d.ts.map +1 -0
  199. package/dist/sandbox/worktree-manager.d.ts +51 -0
  200. package/dist/sandbox/worktree-manager.d.ts.map +1 -0
  201. package/dist/selector/abstention-policy.d.ts +34 -0
  202. package/dist/selector/abstention-policy.d.ts.map +1 -0
  203. package/dist/selector/candidate-extractor.d.ts +71 -0
  204. package/dist/selector/candidate-extractor.d.ts.map +1 -0
  205. package/dist/selector/live-locator-verifier.d.ts +28 -0
  206. package/dist/selector/live-locator-verifier.d.ts.map +1 -0
  207. package/dist/selector/repair-candidate-ranker.d.ts +10 -0
  208. package/dist/selector/repair-candidate-ranker.d.ts.map +1 -0
  209. package/dist/selector/selector-scorer.d.ts +43 -0
  210. package/dist/selector/selector-scorer.d.ts.map +1 -0
  211. package/dist/trace/adapters/adapter-registry.d.ts +18 -0
  212. package/dist/trace/adapters/adapter-registry.d.ts.map +1 -0
  213. package/dist/trace/adapters/trace-adapter.d.ts +21 -0
  214. package/dist/trace/adapters/trace-adapter.d.ts.map +1 -0
  215. package/dist/trace/adapters/unknown-adapter.d.ts +10 -0
  216. package/dist/trace/adapters/unknown-adapter.d.ts.map +1 -0
  217. package/dist/trace/adapters/v10-adapter.d.ts +10 -0
  218. package/dist/trace/adapters/v10-adapter.d.ts.map +1 -0
  219. package/dist/trace/adapters/v4-adapter.d.ts +11 -0
  220. package/dist/trace/adapters/v4-adapter.d.ts.map +1 -0
  221. package/dist/trace/adapters/v8-adapter.d.ts +10 -0
  222. package/dist/trace/adapters/v8-adapter.d.ts.map +1 -0
  223. package/dist/trace/adapters/v9-adapter.d.ts +10 -0
  224. package/dist/trace/adapters/v9-adapter.d.ts.map +1 -0
  225. package/dist/trace/archive-reader.d.ts +44 -0
  226. package/dist/trace/archive-reader.d.ts.map +1 -0
  227. package/dist/trace/snapshot-resolver.d.ts +55 -0
  228. package/dist/trace/snapshot-resolver.d.ts.map +1 -0
  229. package/dist/trace/trace-parser.d.ts +50 -0
  230. package/dist/trace/trace-parser.d.ts.map +1 -0
  231. package/dist/trace/version-detector.d.ts +20 -0
  232. package/dist/trace/version-detector.d.ts.map +1 -0
  233. package/package.json +76 -6
  234. package/skills/README.md +20 -0
  235. package/skills/pie-failure-investigation/SKILL.md +16 -0
  236. package/skills/pie-quality-engineer/SKILL.md +66 -0
  237. package/skills/pie-release-readiness/SKILL.md +22 -0
  238. package/skills/pie-repair-review/SKILL.md +21 -0
  239. package/skills/pie-trace-to-repair/SKILL.md +35 -0
  240. package/src/authoring/author.ts +81 -0
  241. package/src/authoring/generation-contract.ts +283 -0
  242. package/src/authoring/hybrid-author.ts +115 -0
  243. package/src/authoring/index.ts +7 -0
  244. package/src/authoring/llm-author.ts +282 -0
  245. package/src/authoring/naming/index.ts +4 -0
  246. package/src/authoring/naming/name-plan.ts +28 -0
  247. package/src/authoring/naming/name-planner.ts +339 -0
  248. package/src/authoring/naming/naming-linter.ts +292 -0
  249. package/src/authoring/template-author.ts +39 -0
  250. package/src/cli/index.ts +1172 -0
  251. package/src/collectors/collector-interface.ts +8 -0
  252. package/src/collectors/environment-collector.ts +103 -0
  253. package/src/collectors/error-collector.ts +177 -0
  254. package/src/collectors/git-diff-collector.ts +190 -0
  255. package/src/collectors/live-browser-collector.ts +412 -0
  256. package/src/collectors/network-collector.ts +90 -0
  257. package/src/collectors/static-ast-collector.ts +226 -0
  258. package/src/collectors/trace-collector.ts +66 -0
  259. package/src/config/project-config.ts +114 -0
  260. package/src/core/errors/engine-errors.ts +90 -0
  261. package/src/core/types/diagnostic-context.ts +94 -0
  262. package/src/core/types/repair-evidence.ts +45 -0
  263. package/src/core/version.ts +25 -0
  264. package/src/diagnostics/diagnostic-contract.ts +79 -0
  265. package/src/diagnostics/diagnostic-report.ts +204 -0
  266. package/src/diagnostics/evidence-correlator.ts +117 -0
  267. package/src/diagnostics/external-failure-evidence.ts +72 -0
  268. package/src/diagnostics/failure-category.ts +40 -0
  269. package/src/diagnostics/failure-classifier.ts +701 -0
  270. package/src/evaluation/evaluation-scorecard.ts +198 -0
  271. package/src/evaluation/repository-inspector.ts +143 -0
  272. package/src/evaluation/repository-runner.ts +197 -0
  273. package/src/evidence/evidence-model.ts +76 -0
  274. package/src/evidence/evidence-normalizer.ts +322 -0
  275. package/src/evidence/evidence-redactor.ts +218 -0
  276. package/src/framework/config-adapter.ts +131 -0
  277. package/src/gate/index.ts +43 -0
  278. package/src/gate/oracle.ts +69 -0
  279. package/src/gate/quality.ts +101 -0
  280. package/src/gate/safety.ts +94 -0
  281. package/src/generator/architecture-generator.ts +582 -0
  282. package/src/generator/assertion-quality-gate.ts +88 -0
  283. package/src/generator/candidate-writer.ts +288 -0
  284. package/src/generator/test-generator.ts +52 -0
  285. package/src/governance/audit-log.ts +115 -0
  286. package/src/governance/governance-policy.ts +72 -0
  287. package/src/governance/team-dashboard.ts +189 -0
  288. package/src/governance/workflow-audit.ts +72 -0
  289. package/src/index.ts +107 -0
  290. package/src/integrations/ci-summary.ts +37 -0
  291. package/src/integrations/playwright-report-adapter.ts +173 -0
  292. package/src/integrations/quality-gate.ts +95 -0
  293. package/src/integrations/sarif-report.ts +110 -0
  294. package/src/intelligence/autonomous-repair-workflow.ts +139 -0
  295. package/src/intelligence/business-intent.ts +163 -0
  296. package/src/intelligence/evidence-bundle.ts +300 -0
  297. package/src/intelligence/failure-clustering.ts +134 -0
  298. package/src/intelligence/generation-agent.ts +561 -0
  299. package/src/intelligence/generation-verifier.ts +1115 -0
  300. package/src/intelligence/llm-proposal.ts +519 -0
  301. package/src/intelligence/locator-disambiguation-engine.ts +331 -0
  302. package/src/intelligence/model-contract.ts +205 -0
  303. package/src/intelligence/model-provider.ts +348 -0
  304. package/src/intelligence/project-intelligence.ts +140 -0
  305. package/src/intelligence/repair-agent.ts +297 -0
  306. package/src/intelligence/repository-profile.ts +796 -0
  307. package/src/intelligence/suite-intelligence.ts +305 -0
  308. package/src/intelligence/test-intelligence.ts +136 -0
  309. package/src/intelligence/trace-proposal-workflow.ts +318 -0
  310. package/src/intelligence/verified-llm-repair-workflow.ts +48 -0
  311. package/src/intelligence/workflow-planner.ts +966 -0
  312. package/src/mcp/server.ts +970 -0
  313. package/src/memory/stability-ledger.ts +243 -0
  314. package/src/perception/app-model.ts +32 -0
  315. package/src/perception/crawler.ts +263 -0
  316. package/src/perception/index.ts +3 -0
  317. package/src/repair/approval-service.ts +541 -0
  318. package/src/repair/assertion-hasher.ts +386 -0
  319. package/src/repair/ast-patcher.ts +223 -0
  320. package/src/repair/browser-operation-executor.ts +263 -0
  321. package/src/repair/diff-allowlist.ts +612 -0
  322. package/src/repair/intent-validator.ts +761 -0
  323. package/src/repair/repair-operations.ts +65 -0
  324. package/src/repair/repair-planner.ts +467 -0
  325. package/src/repair/repair-workflow.ts +241 -0
  326. package/src/repair/safe-operation-compiler.ts +68 -0
  327. package/src/repair/source-mapper.ts +156 -0
  328. package/src/repair/statement-patcher.ts +544 -0
  329. package/src/repair/verification-gates.ts +46 -0
  330. package/src/repair/workflow-plan.ts +59 -0
  331. package/src/repair/workspace-promoter.ts +313 -0
  332. package/src/reproduce/diagnostic-rerunner.ts +287 -0
  333. package/src/reproduce/direct-browser-investigator.ts +208 -0
  334. package/src/reproduce/interactive-workflow-runner.ts +828 -0
  335. package/src/sandbox/dependency-strategy.ts +215 -0
  336. package/src/sandbox/execution-runner.ts +408 -0
  337. package/src/sandbox/worktree-manager.ts +287 -0
  338. package/src/selector/abstention-policy.ts +175 -0
  339. package/src/selector/candidate-extractor.ts +343 -0
  340. package/src/selector/live-locator-verifier.ts +139 -0
  341. package/src/selector/repair-candidate-ranker.ts +92 -0
  342. package/src/selector/selector-scorer.ts +344 -0
  343. package/src/trace/adapters/adapter-registry.ts +49 -0
  344. package/src/trace/adapters/trace-adapter.ts +28 -0
  345. package/src/trace/adapters/unknown-adapter.ts +50 -0
  346. package/src/trace/adapters/v10-adapter.ts +39 -0
  347. package/src/trace/adapters/v4-adapter.ts +31 -0
  348. package/src/trace/adapters/v8-adapter.ts +42 -0
  349. package/src/trace/adapters/v9-adapter.ts +39 -0
  350. package/src/trace/archive-reader.ts +246 -0
  351. package/src/trace/snapshot-resolver.ts +308 -0
  352. package/src/trace/trace-parser.ts +467 -0
  353. package/src/trace/version-detector.ts +81 -0
@@ -0,0 +1,519 @@
1
+ import { z } from 'zod';
2
+ import { RepairOperation, RepairOperationSchema, validateRepairOperation } from '../repair/repair-operations.js';
3
+
4
+ export const EvidenceBundleSchema = z.object({
5
+ bundleId: z.string().min(1),
6
+ executionId: z.string().min(1),
7
+ testIdentity: z.string().min(1),
8
+ failedActionId: z.string().optional(),
9
+ failureCategory: z.string().optional(),
10
+ sourceContext: z.array(z.object({
11
+ filePath: z.string(),
12
+ startLine: z.number().int().positive().optional(),
13
+ endLine: z.number().int().positive().optional(),
14
+ content: z.string(),
15
+ })).default([]),
16
+ artifacts: z.array(z.object({
17
+ kind: z.enum(['trace', 'screenshot', 'dom', 'aria', 'network', 'console', 'video', 'git-diff']),
18
+ reference: z.string(),
19
+ redacted: z.boolean(),
20
+ })).default([]),
21
+ diagnostic: z.object({
22
+ playwrightVersion: z.string(),
23
+ traceFormatVersion: z.number(),
24
+ failedAction: z.object({
25
+ callId: z.string(),
26
+ action: z.string(),
27
+ selector: z.string().optional(),
28
+ sourceFile: z.string().optional(),
29
+ line: z.number().optional(),
30
+ column: z.number().optional(),
31
+ errorMessage: z.string().optional(),
32
+ errorStack: z.string().optional(),
33
+ durationMs: z.number().optional(),
34
+ logs: z.array(z.string()).optional(),
35
+ }),
36
+ snapshots: z.object({
37
+ before: z.string().optional(),
38
+ action: z.string().optional(),
39
+ after: z.string().optional(),
40
+ }),
41
+ networkEvents: z.array(z.object({
42
+ method: z.string(),
43
+ url: z.string(),
44
+ status: z.number(),
45
+ statusText: z.string().optional(),
46
+ errorText: z.string().optional(),
47
+ })),
48
+ consoleEvents: z.array(z.object({ type: z.string(), text: z.string() })),
49
+ evidenceCompleteness: z.object({
50
+ domAvailable: z.boolean(),
51
+ networkAvailable: z.boolean(),
52
+ sourceAvailable: z.boolean(),
53
+ }),
54
+ }).optional(),
55
+ redactionComplete: z.boolean(),
56
+ limitations: z.array(z.string()).default([]),
57
+ }).strict();
58
+
59
+ export type EvidenceBundle = z.infer<typeof EvidenceBundleSchema>;
60
+
61
+ export const LlmRepairProposalSchema = z.object({
62
+ proposalVersion: z.literal('1'),
63
+ decision: z.enum(['PROPOSE', 'ABSTAIN']),
64
+ failureKind: z.string().min(1),
65
+ confidence: z.number().min(0).max(1),
66
+ reasoningSummary: z.string().min(1),
67
+ operations: z.array(RepairOperationSchema).max(20),
68
+ requiredVerification: z.array(z.string()).min(1),
69
+ evidenceReferences: z.array(z.string()),
70
+ }).strict();
71
+
72
+ export type LlmRepairProposal = z.infer<typeof LlmRepairProposalSchema>;
73
+
74
+ export interface ProposalParseResult {
75
+ isValid: boolean;
76
+ proposal?: LlmRepairProposal;
77
+ errors: string[];
78
+ normalizationNotes?: string[] | undefined;
79
+ }
80
+
81
+ export const AgentActionKindSchema = z.enum([
82
+ 'inspect_evidence',
83
+ 'inspect_browser_state',
84
+ 'inspect_repository',
85
+ 'read_source_file',
86
+ 'search_repository',
87
+ 'discover_source_mapping',
88
+ 'rank_locator_candidates',
89
+ 'build_repair_proposal',
90
+ 'run_target_test',
91
+ 'verify_repair',
92
+ 'abstain',
93
+ ]);
94
+
95
+ export type AgentActionKind = z.infer<typeof AgentActionKindSchema>;
96
+
97
+ export const AgentActionSchema = z.object({
98
+ kind: AgentActionKindSchema,
99
+ selector: z.string().min(1).optional(),
100
+ target: z.string().min(1).optional(),
101
+ locator: z.string().min(1).optional(),
102
+ path: z.string().min(1).optional(),
103
+ query: z.string().min(1).optional(),
104
+ testFile: z.string().min(1).optional(),
105
+ configPath: z.string().min(1).optional(),
106
+ sourceFile: z.string().min(1).optional(),
107
+ className: z.string().min(1).optional(),
108
+ memberName: z.string().min(1).optional(),
109
+ reason: z.string().min(1).optional(),
110
+ }).strict();
111
+
112
+ export type AgentAction = z.infer<typeof AgentActionSchema>;
113
+
114
+ const AgentClassificationSchema = z.object({
115
+ category: z.string().min(1),
116
+ confidence: z.number().min(0).max(1),
117
+ }).strict();
118
+
119
+ const AgentStepResponseSchema = z.object({
120
+ protocolVersion: z.literal('2'),
121
+ responseType: z.literal('STEP'),
122
+ status: z.literal('CONTINUE'),
123
+ decision: z.enum([
124
+ 'INSPECT_EVIDENCE',
125
+ 'INSPECT_BROWSER_STATE',
126
+ 'INSPECT_REPOSITORY',
127
+ 'READ_SOURCE_FILE',
128
+ 'SEARCH_REPOSITORY',
129
+ 'DISCOVER_SOURCE_MAPPING',
130
+ 'RANK_LOCATOR_CANDIDATES',
131
+ 'BUILD_REPAIR_PROPOSAL',
132
+ 'RUN_TARGET_TEST',
133
+ 'VERIFY_REPAIR',
134
+ 'ABSTAIN',
135
+ ]),
136
+ confidence: z.number().min(0).max(1),
137
+ reasoningSummary: z.string().min(1),
138
+ evidenceReferences: z.array(z.string()),
139
+ action: AgentActionSchema,
140
+ requiredObservation: z.string().min(1),
141
+ }).strict();
142
+
143
+ const AgentFinalResponseSchema = z.object({
144
+ protocolVersion: z.literal('2'),
145
+ responseType: z.literal('FINAL'),
146
+ status: z.enum(['PROPOSAL_READY', 'VERIFIED_READY_FOR_APPROVAL', 'ABSTAIN', 'BLOCKED', 'REJECTED']),
147
+ classification: AgentClassificationSchema,
148
+ reasoningSummary: z.string().min(1),
149
+ evidenceReferences: z.array(z.string()),
150
+ proposal: LlmRepairProposalSchema.optional(),
151
+ verification: z.object({
152
+ isolatedTestPassed: z.boolean().optional(),
153
+ assertionsChanged: z.boolean().optional(),
154
+ unrelatedFilesChanged: z.boolean().optional(),
155
+ }).strict().optional(),
156
+ humanApprovalRequired: z.boolean(),
157
+ }).strict();
158
+
159
+ export const AgentResponseSchema = z.discriminatedUnion('responseType', [AgentStepResponseSchema, AgentFinalResponseSchema]);
160
+
161
+ export type AgentStepResponse = z.infer<typeof AgentStepResponseSchema>;
162
+ export type AgentFinalResponse = z.infer<typeof AgentFinalResponseSchema>;
163
+ export type AgentResponse = z.infer<typeof AgentResponseSchema>;
164
+
165
+ export interface AgentResponseParseResult {
166
+ isValid: boolean;
167
+ response?: AgentResponse;
168
+ errors: string[];
169
+ normalizationNotes?: string[] | undefined;
170
+ /** Recognized transport metadata removed before strict protocol validation. */
171
+ protocolMetadata?: AgentProtocolMetadata | undefined;
172
+ }
173
+
174
+ export interface AgentProtocolMetadata {
175
+ callId?: string | undefined;
176
+ requestId?: string | undefined;
177
+ responseId?: string | undefined;
178
+ messageId?: string | undefined;
179
+ model?: string | undefined;
180
+ provider?: string | undefined;
181
+ finishReason?: string | undefined;
182
+ usage?: unknown;
183
+ }
184
+
185
+ function extractJsonCandidate(input: string): string {
186
+ const trimmed = input.trim();
187
+ const fenced = trimmed.match(/```(?:json)?\s*([\s\S]*?)\s*```/i);
188
+ if (fenced?.[1]) return fenced[1].trim();
189
+ const start = trimmed.indexOf('{');
190
+ const end = trimmed.lastIndexOf('}');
191
+ return start >= 0 && end > start ? trimmed.slice(start, end + 1) : trimmed;
192
+ }
193
+
194
+ function parseJsonInput(input: unknown): { candidate?: unknown; errors: string[] } {
195
+ if (typeof input !== 'string') return { candidate: input, errors: [] };
196
+ try {
197
+ return { candidate: JSON.parse(extractJsonCandidate(input)), errors: [] };
198
+ } catch (error) {
199
+ return { errors: [`Response is not valid JSON: ${String(error)}`] };
200
+ }
201
+ }
202
+
203
+ function normalizeAgentCandidate(input: unknown): { candidate: unknown; notes: string[]; metadata?: AgentProtocolMetadata | undefined } {
204
+ if (!input || typeof input !== 'object' || Array.isArray(input)) return { candidate: input, notes: [] };
205
+ const envelope = normalizeAgentEnvelope(input as Record<string, unknown>);
206
+ const source = envelope.candidate as Record<string, unknown>;
207
+ const withEnvelope = (candidate: unknown, notes: string[] = []) => ({
208
+ candidate,
209
+ notes: [...envelope.notes, ...notes],
210
+ ...(envelope.metadata ? { metadata: envelope.metadata } : {}),
211
+ });
212
+ if (source.responseType === 'FINAL') {
213
+ const allowedFinalKeys = new Set([
214
+ 'protocolVersion', 'responseType', 'status', 'classification',
215
+ 'reasoningSummary', 'evidenceReferences', 'proposal', 'verification',
216
+ 'humanApprovalRequired',
217
+ ]);
218
+ if (Object.keys(source).some((key) => !allowedFinalKeys.has(key))) return withEnvelope(source);
219
+ // Accept a complete PIE final response unchanged. Compatibility handling
220
+ // below is deliberately limited to documented sparse-final variants.
221
+ if (AgentFinalResponseSchema.safeParse(source).success) return withEnvelope(source);
222
+
223
+ const notes: string[] = [];
224
+ const classification = source.classification && typeof source.classification === 'object' && !Array.isArray(source.classification)
225
+ ? source.classification as Record<string, unknown>
226
+ : {};
227
+ const normalizedClassification = typeof classification.category === 'string'
228
+ && typeof classification.confidence === 'number'
229
+ && classification.confidence >= 0
230
+ && classification.confidence <= 1
231
+ ? { category: classification.category, confidence: classification.confidence }
232
+ : { category: 'MODEL_RESPONSE', confidence: 0 };
233
+ const evidenceReferences = Array.isArray(source.evidenceReferences)
234
+ ? source.evidenceReferences.filter((value): value is string => typeof value === 'string')
235
+ : [];
236
+ const reasoningSummary = typeof source.reasoningSummary === 'string'
237
+ ? source.reasoningSummary
238
+ : 'The model returned an incomplete final response.';
239
+ const humanApprovalRequired = typeof source.humanApprovalRequired === 'boolean' ? source.humanApprovalRequired : true;
240
+
241
+ if (source.status === 'ABSTAIN') {
242
+ notes.push('Normalized sparse FINAL ABSTAIN response with conservative classification and approval defaults.');
243
+ return withEnvelope({
244
+ protocolVersion: '2',
245
+ responseType: 'FINAL',
246
+ status: 'ABSTAIN',
247
+ classification: normalizedClassification,
248
+ reasoningSummary,
249
+ evidenceReferences,
250
+ humanApprovalRequired,
251
+ }, notes);
252
+ }
253
+
254
+ if (source.status === 'PROPOSAL' && source.proposal && typeof source.proposal === 'object' && !Array.isArray(source.proposal)) {
255
+ const legacyProposal = source.proposal as Record<string, unknown>;
256
+ const target = legacyProposal.target;
257
+ const locator = legacyProposal.locator;
258
+ if (target && typeof target === 'object' && !Array.isArray(target)
259
+ && typeof (target as Record<string, unknown>).filePath === 'string'
260
+ && typeof locator === 'string' && locator.length > 0) {
261
+ notes.push('Mapped recognized legacy FINAL PROPOSAL shape to BLOCKED because it lacks a safe old-locator operation.');
262
+ return withEnvelope({
263
+ protocolVersion: '2',
264
+ responseType: 'FINAL',
265
+ status: 'BLOCKED',
266
+ classification: normalizedClassification,
267
+ reasoningSummary: 'The model returned a source-location proposal without a complete PIE repair operation; no mutation was synthesized.',
268
+ evidenceReferences,
269
+ humanApprovalRequired: true,
270
+ }, notes);
271
+ }
272
+ }
273
+
274
+ const legacyFinalStatuses = new Set(['OK', 'PROPOSED', 'PROPOSAL_ONLY', 'REPAIR_PROPOSAL', 'COMPLETE']);
275
+ if (typeof source.status === 'string' && legacyFinalStatuses.has(source.status)
276
+ && source.proposal && typeof source.proposal === 'object' && !Array.isArray(source.proposal)) {
277
+ const legacyProposal = source.proposal as Record<string, unknown>;
278
+ const failureCategory = typeof legacyProposal.failureCategory === 'string'
279
+ ? legacyProposal.failureCategory.trim().toUpperCase()
280
+ : typeof legacyProposal.failureKind === 'string'
281
+ ? legacyProposal.failureKind.trim().toUpperCase()
282
+ : typeof legacyProposal.category === 'string'
283
+ ? legacyProposal.category.trim().toUpperCase()
284
+ : undefined;
285
+ const confidence = typeof legacyProposal.confidence === 'number' && legacyProposal.confidence >= 0 && legacyProposal.confidence <= 1
286
+ ? legacyProposal.confidence
287
+ : 0;
288
+ const summary = typeof legacyProposal.summary === 'string'
289
+ ? legacyProposal.summary
290
+ : typeof legacyProposal.rationale === 'string'
291
+ ? legacyProposal.rationale
292
+ : 'The model returned a legacy locator proposal.';
293
+ const directTarget = typeof legacyProposal.oldLocator === 'string' ? legacyProposal.oldLocator
294
+ : typeof legacyProposal.oldText === 'string' ? legacyProposal.oldText
295
+ : typeof legacyProposal.originalLocator === 'string' ? legacyProposal.originalLocator : undefined;
296
+ const directLocator = typeof legacyProposal.newLocator === 'string' ? legacyProposal.newLocator
297
+ : typeof legacyProposal.newText === 'string' ? legacyProposal.newText
298
+ : typeof legacyProposal.proposedLocator === 'string' ? legacyProposal.proposedLocator : undefined;
299
+ const edits = Array.isArray(legacyProposal.edits) ? legacyProposal.edits : Array.isArray(legacyProposal.changes) ? legacyProposal.changes : [];
300
+ const firstEdit = edits[0] && typeof edits[0] === 'object' && !Array.isArray(edits[0]) ? edits[0] as Record<string, unknown> : {};
301
+ const target = directTarget
302
+ ?? (typeof firstEdit.oldLocator === 'string' ? firstEdit.oldLocator : typeof firstEdit.oldText === 'string' ? firstEdit.oldText : undefined)
303
+ ?? (typeof legacyProposal.target === 'string' ? legacyProposal.target : undefined);
304
+ const locator = directLocator
305
+ ?? (typeof firstEdit.newLocator === 'string' ? firstEdit.newLocator : typeof firstEdit.newText === 'string' ? firstEdit.newText : undefined)
306
+ ?? (typeof legacyProposal.locator === 'string' ? legacyProposal.locator : undefined);
307
+ const hasLegacyLocatorShape = typeof target === 'string'
308
+ || typeof locator === 'string'
309
+ || typeof legacyProposal.target === 'string'
310
+ || typeof legacyProposal.locator === 'string'
311
+ || edits.length > 0;
312
+ const inferredLocatorDrift = failureCategory === undefined
313
+ && source.status === 'PROPOSED'
314
+ && typeof legacyProposal.target === 'string'
315
+ && typeof legacyProposal.locator === 'string';
316
+
317
+ if ((failureCategory === 'LOCATOR_DRIFT' || inferredLocatorDrift) && target && locator) {
318
+ notes.push('Normalized recognized legacy locator proposal into the PIE replacement-operation schema.');
319
+ return withEnvelope({
320
+ protocolVersion: '2',
321
+ responseType: 'FINAL',
322
+ status: 'PROPOSAL_READY',
323
+ classification: { category: 'LOCATOR_DRIFT', confidence },
324
+ reasoningSummary: summary,
325
+ evidenceReferences,
326
+ proposal: {
327
+ proposalVersion: '1',
328
+ decision: 'PROPOSE',
329
+ failureKind: 'LOCATOR_DRIFT',
330
+ confidence,
331
+ reasoningSummary: summary,
332
+ operations: [{ kind: 'replace_locator', target, locator }],
333
+ requiredVerification: ['isolated target test', 'assertions unchanged'],
334
+ evidenceReferences,
335
+ },
336
+ humanApprovalRequired: true,
337
+ }, notes);
338
+ }
339
+
340
+ if (hasLegacyLocatorShape) {
341
+ notes.push('Mapped an incomplete or non-locator legacy final proposal to BLOCKED; no mutation operation was synthesized.');
342
+ return withEnvelope({
343
+ protocolVersion: '2',
344
+ responseType: 'FINAL',
345
+ status: 'BLOCKED',
346
+ classification: normalizedClassification,
347
+ reasoningSummary: 'The legacy proposal did not contain a safe LOCATOR_DRIFT replacement operation; no mutation was synthesized.',
348
+ evidenceReferences,
349
+ humanApprovalRequired: true,
350
+ }, notes);
351
+ }
352
+ }
353
+
354
+ return withEnvelope(source, notes);
355
+ }
356
+ if (source.responseType === 'STEP') {
357
+ const allowedStepKeys = new Set([
358
+ 'protocolVersion', 'responseType', 'status', 'decision', 'confidence',
359
+ 'reasoningSummary', 'evidenceReferences', 'action', 'requiredObservation',
360
+ ]);
361
+ if (Object.keys(source).some((key) => !allowedStepKeys.has(key))) return withEnvelope(source);
362
+ }
363
+ if (source.responseType !== 'STEP' || source.status !== 'CONTINUE' || !source.action
364
+ || (typeof source.action !== 'object' && typeof source.action !== 'string')) {
365
+ return withEnvelope(source);
366
+ }
367
+
368
+ const rawAction = typeof source.action === 'string'
369
+ ? { kind: source.action }
370
+ : source.action as Record<string, unknown>;
371
+ const kind = rawAction.kind;
372
+ const allowedActionFields = ['kind', 'selector', 'target', 'locator', 'path', 'query', 'testFile', 'configPath', 'sourceFile', 'className', 'memberName', 'reason'];
373
+ const action: Record<string, unknown> = {};
374
+ for (const field of allowedActionFields) {
375
+ if (typeof rawAction[field] === 'string' && rawAction[field].length > 0) action[field] = rawAction[field];
376
+ }
377
+ const notes: string[] = [];
378
+ if (typeof source.action === 'string') notes.push('Normalized a recognized string action into the PIE action object schema.');
379
+ const legacyProposal = rawAction.proposal;
380
+ if (legacyProposal && typeof legacyProposal === 'object') {
381
+ const legacy = legacyProposal as Record<string, unknown>;
382
+ if (typeof legacy.oldLocator === 'string' && action.target === undefined) action.target = legacy.oldLocator;
383
+ if (typeof legacy.oldText === 'string' && action.target === undefined) action.target = legacy.oldText;
384
+ if (typeof legacy.newLocator === 'string' && action.locator === undefined) action.locator = legacy.newLocator;
385
+ if (typeof legacy.newText === 'string' && action.locator === undefined) action.locator = legacy.newText;
386
+ const edits = Array.isArray(legacy.edits) ? legacy.edits : Array.isArray(legacy.changes) ? legacy.changes : [];
387
+ const firstEdit = edits[0] && typeof edits[0] === 'object' && !Array.isArray(edits[0]) ? edits[0] as Record<string, unknown> : {};
388
+ if (typeof firstEdit.oldLocator === 'string' && action.target === undefined) action.target = firstEdit.oldLocator;
389
+ if (typeof firstEdit.oldText === 'string' && action.target === undefined) action.target = firstEdit.oldText;
390
+ if (typeof firstEdit.newLocator === 'string' && action.locator === undefined) action.locator = firstEdit.newLocator;
391
+ if (typeof firstEdit.newText === 'string' && action.locator === undefined) action.locator = firstEdit.newText;
392
+ notes.push('Dropped unsupported nested proposal metadata; retained only allowlisted locator fields.');
393
+ }
394
+
395
+ const decisionByAction: Record<string, string> = {
396
+ inspect_evidence: 'INSPECT_EVIDENCE',
397
+ inspect_browser_state: 'INSPECT_BROWSER_STATE',
398
+ inspect_repository: 'INSPECT_REPOSITORY',
399
+ read_source_file: 'READ_SOURCE_FILE',
400
+ search_repository: 'SEARCH_REPOSITORY',
401
+ discover_source_mapping: 'DISCOVER_SOURCE_MAPPING',
402
+ rank_locator_candidates: 'RANK_LOCATOR_CANDIDATES',
403
+ build_repair_proposal: 'BUILD_REPAIR_PROPOSAL',
404
+ run_target_test: 'RUN_TARGET_TEST',
405
+ verify_repair: 'VERIFY_REPAIR',
406
+ abstain: 'ABSTAIN',
407
+ };
408
+ const normalized: Record<string, unknown> = {
409
+ protocolVersion: source.protocolVersion === '2' ? '2' : '2',
410
+ responseType: 'STEP',
411
+ status: 'CONTINUE',
412
+ decision: typeof source.decision === 'string' ? source.decision : decisionByAction[String(kind)] ?? kind,
413
+ confidence: typeof source.confidence === 'number' ? source.confidence : 0,
414
+ reasoningSummary: typeof source.reasoningSummary === 'string' ? source.reasoningSummary : 'Model requested an allowlisted PIE observation.',
415
+ evidenceReferences: Array.isArray(source.evidenceReferences) ? source.evidenceReferences.filter((value): value is string => typeof value === 'string') : [],
416
+ action,
417
+ requiredObservation: typeof source.requiredObservation === 'string' ? source.requiredObservation : 'Return the PIE action observation.',
418
+ };
419
+ if (source.decision === undefined) notes.push('Derived decision from the allowlisted action kind.');
420
+ if (source.confidence === undefined) notes.push('Applied conservative confidence default 0.');
421
+ if (source.reasoningSummary === undefined) notes.push('Applied a neutral reasoning summary because none was returned.');
422
+ if (source.evidenceReferences === undefined) notes.push('Applied an empty evidence-reference list because none was returned.');
423
+ if (source.requiredObservation === undefined) notes.push('Applied a default observation requirement because none was returned.');
424
+ return withEnvelope(normalized, notes);
425
+ }
426
+
427
+ /**
428
+ * Some OpenAI-compatible endpoints and models place correlation metadata next
429
+ * to the PIE response object. These fields are transport metadata, not model
430
+ * instructions. Remove only this explicit allowlist before strict validation;
431
+ * every other unknown field remains an error.
432
+ */
433
+ function normalizeAgentEnvelope(input: Record<string, unknown>): {
434
+ candidate: Record<string, unknown>;
435
+ notes: string[];
436
+ metadata?: AgentProtocolMetadata | undefined;
437
+ } {
438
+ const metadataKeys = [
439
+ 'callId', 'requestId', 'responseId', 'messageId', 'model', 'provider',
440
+ 'finishReason', 'finish_reason', 'usage', 'id',
441
+ ] as const;
442
+ const candidate = { ...input };
443
+ const metadata: AgentProtocolMetadata = {};
444
+ const notes: string[] = [];
445
+ for (const key of metadataKeys) {
446
+ if (!(key in candidate)) continue;
447
+ const value = candidate[key];
448
+ delete candidate[key];
449
+ if (key === 'finish_reason') {
450
+ if (typeof value === 'string') metadata.finishReason = value;
451
+ } else if (key === 'usage') {
452
+ metadata.usage = value;
453
+ } else if (typeof value === 'string') {
454
+ switch (key) {
455
+ case 'callId': metadata.callId = value; break;
456
+ case 'requestId': metadata.requestId = value; break;
457
+ case 'responseId': metadata.responseId = value; break;
458
+ case 'messageId': metadata.messageId = value; break;
459
+ case 'model': metadata.model = value; break;
460
+ case 'provider': metadata.provider = value; break;
461
+ case 'finishReason': metadata.finishReason = value; break;
462
+ case 'id': metadata.responseId = value; break;
463
+ }
464
+ }
465
+ notes.push(`Normalized recognized response envelope metadata '${key}'.`);
466
+ }
467
+ return {
468
+ candidate,
469
+ notes,
470
+ ...(Object.keys(metadata).length > 0 ? { metadata } : {}),
471
+ };
472
+ }
473
+
474
+ export function parseLlmRepairProposal(input: unknown): ProposalParseResult {
475
+ const parsedInput = parseJsonInput(input);
476
+ if (parsedInput.errors.length > 0) return { isValid: false, errors: parsedInput.errors.map((error) => error.replace(/^Response/, 'Proposal')) };
477
+ const candidate = parsedInput.candidate;
478
+
479
+ const parsed = LlmRepairProposalSchema.safeParse(candidate);
480
+ if (!parsed.success) {
481
+ return { isValid: false, errors: parsed.error.issues.map((issue) => issue.message) };
482
+ }
483
+
484
+ const operationErrors = parsed.data.operations.flatMap((operation) => {
485
+ const result = validateRepairOperation(operation);
486
+ return result.isValid ? [] : [result.reason ?? 'Invalid repair operation'];
487
+ });
488
+ if (operationErrors.length > 0) return { isValid: false, errors: operationErrors };
489
+
490
+ if (parsed.data.decision === 'ABSTAIN' && parsed.data.operations.some((operation) => operation.kind !== 'abstain')) {
491
+ return { isValid: false, errors: ['ABSTAIN proposals cannot contain source mutation operations'] };
492
+ }
493
+
494
+ return { isValid: true, proposal: parsed.data, errors: [] };
495
+ }
496
+
497
+ export function proposalOperations(proposal: LlmRepairProposal): RepairOperation[] {
498
+ return proposal.operations;
499
+ }
500
+
501
+ export function parseAgentResponse(input: unknown): AgentResponseParseResult {
502
+ const parsedInput = parseJsonInput(input);
503
+ if (parsedInput.errors.length > 0) return { isValid: false, errors: parsedInput.errors };
504
+ const normalized = normalizeAgentCandidate(parsedInput.candidate);
505
+ const parsed = AgentResponseSchema.safeParse(normalized.candidate);
506
+ if (!parsed.success) return {
507
+ isValid: false,
508
+ errors: parsed.error.issues.map((issue) => issue.message),
509
+ normalizationNotes: normalized.notes,
510
+ protocolMetadata: normalized.metadata,
511
+ };
512
+ if (parsed.data.responseType === 'FINAL' && parsed.data.proposal) {
513
+ const proposal = parseLlmRepairProposal(parsed.data.proposal);
514
+ if (!proposal.isValid || !proposal.proposal) return { isValid: false, errors: proposal.errors, normalizationNotes: normalized.notes, protocolMetadata: normalized.metadata };
515
+ return { isValid: true, response: { ...parsed.data, proposal: proposal.proposal }, errors: [], normalizationNotes: normalized.notes, protocolMetadata: normalized.metadata };
516
+ }
517
+ return { isValid: true, response: parsed.data, errors: [], normalizationNotes: normalized.notes, protocolMetadata: normalized.metadata };
518
+ }
519
+