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,541 @@
1
+ import crypto from 'node:crypto';
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { VerifiedExecutionProof } from './intent-validator.js';
5
+
6
+ export interface ApprovalTokenPayload {
7
+ tokenId: string;
8
+ proposalId: string;
9
+ targetFilePath: string; // canonical realpath
10
+ targetClassName: string;
11
+ targetMemberName: string;
12
+ originalSourceHash: string;
13
+ replacementSelector: string;
14
+ replacementAstHash: string;
15
+ fullDiffHash: string;
16
+ patchedSourceHash: string;
17
+ testFilePath: string; // canonical realpath
18
+ testFileHash: string;
19
+ executionVerified: boolean;
20
+ issuedAt: number;
21
+ expiresAt: number;
22
+ used: boolean;
23
+ nonce: string;
24
+ }
25
+
26
+ export interface StagedProposal {
27
+ proposalId: string;
28
+ targetFilePath: string;
29
+ targetClassName: string;
30
+ targetMemberName: string;
31
+ originalSourceHash: string;
32
+ replacementSelector: string;
33
+ replacementAstHash: string;
34
+ fullDiff: string;
35
+ fullDiffHash: string;
36
+ patchedSource: string;
37
+ patchedSourceHash: string;
38
+ testFilePath: string;
39
+ testFileHash: string;
40
+ worktreeId: string;
41
+ worktreeExecutionVerified: boolean;
42
+ executionProof?: VerifiedExecutionProof | undefined;
43
+ createdAt: number;
44
+ expiresAt: number;
45
+ }
46
+
47
+ export interface TokenValidationResult {
48
+ isValid: boolean;
49
+ reason?: string;
50
+ payload?: ApprovalTokenPayload;
51
+ }
52
+
53
+ /**
54
+ * Centralized cryptographic approval service for all source modifications.
55
+ * Guarantees that no repair can be applied without an explicit, unexpired,
56
+ * one-time approval token bound cryptographically to:
57
+ * - Proposal ID & Nonce
58
+ * - Canonical target file path & Original source SHA-256
59
+ * - Target Class Name & Member Name
60
+ * - Replacement Selector & AST Hash
61
+ * - Full Diff SHA-256 & Patched Source SHA-256
62
+ * - Test File Canonical Path & Test File SHA-256
63
+ * - Isolated Worktree Execution Verification Proof
64
+ */
65
+ export class ApprovalService {
66
+ private static instance: ApprovalService | undefined;
67
+ private proposals = new Map<string, StagedProposal>();
68
+ private tokens = new Map<string, ApprovalTokenPayload>();
69
+ private readonly defaultTtlMs = 10 * 60 * 1000; // 10 minutes
70
+ private storageDir: string;
71
+ private proposalsDir: string;
72
+ private tokensDir: string;
73
+
74
+ public constructor(customStorageDir?: string) {
75
+ this.storageDir = customStorageDir || process.env.PIE_STORAGE_DIR || path.resolve(process.cwd(), '.pw-intelligence');
76
+ this.proposalsDir = path.join(this.storageDir, 'proposals');
77
+ this.tokensDir = path.join(this.storageDir, 'tokens');
78
+ try {
79
+ fs.mkdirSync(this.proposalsDir, { recursive: true });
80
+ fs.mkdirSync(this.tokensDir, { recursive: true });
81
+ } catch {
82
+ // Safe fallback if filesystem is read-only
83
+ }
84
+ }
85
+
86
+ public static getInstance(customStorageDir?: string): ApprovalService {
87
+ if (!ApprovalService.instance) {
88
+ ApprovalService.instance = new ApprovalService(customStorageDir);
89
+ }
90
+ return ApprovalService.instance;
91
+ }
92
+
93
+ /**
94
+ * Computes SHA-256 hash of a string.
95
+ */
96
+ public static hashContent(content: string): string {
97
+ return crypto.createHash('sha256').update(content, 'utf8').digest('hex');
98
+ }
99
+
100
+ /**
101
+ * Stages a verified repair proposal in memory.
102
+ * Note: Staging a proposal does NOT generate an approval token.
103
+ * An approval token is generated only via an explicit, trusted authorization call.
104
+ */
105
+ public stageProposal(params: {
106
+ proposalId?: string;
107
+ targetFilePath: string;
108
+ targetClassName: string;
109
+ targetMemberName: string;
110
+ originalSourceContent: string;
111
+ replacementSelector: string;
112
+ fullDiff: string;
113
+ patchedSource: string;
114
+ testFilePath: string;
115
+ testFileContent: string;
116
+ worktreeId: string;
117
+ worktreeExecutionVerified?: boolean;
118
+ proof?: VerifiedExecutionProof;
119
+ ttlMs?: number;
120
+ }): StagedProposal {
121
+ const canonicalTarget = fs.existsSync(params.targetFilePath)
122
+ ? fs.realpathSync(params.targetFilePath)
123
+ : path.resolve(params.targetFilePath);
124
+
125
+ const canonicalTest = fs.existsSync(params.testFilePath)
126
+ ? fs.realpathSync(params.testFilePath)
127
+ : path.resolve(params.testFilePath);
128
+
129
+ const proposalId = params.proposalId ?? `prop-${Date.now()}-${crypto.randomBytes(8).toString('hex')}`;
130
+ const now = Date.now();
131
+ const ttl = params.ttlMs ?? this.defaultTtlMs;
132
+
133
+ // Derived execution verification: caller-supplied boolean is never trusted
134
+ // unless verified proof from an actual worktree execution is attached.
135
+ let isVerified = false;
136
+ if (params.proof) {
137
+ isVerified = Boolean(
138
+ params.proof.passed &&
139
+ params.proof.exitCode === 0 &&
140
+ (params.proof.executionOutcome === 'PASS' || params.proof.executionOutcome === undefined) &&
141
+ (params.proof.assertionIntegrityValid !== false)
142
+ );
143
+ }
144
+
145
+ const staged: StagedProposal = {
146
+ proposalId,
147
+ targetFilePath: canonicalTarget,
148
+ targetClassName: params.targetClassName,
149
+ targetMemberName: params.targetMemberName,
150
+ originalSourceHash: ApprovalService.hashContent(params.originalSourceContent),
151
+ replacementSelector: params.replacementSelector,
152
+ replacementAstHash: ApprovalService.hashContent(params.replacementSelector.trim()),
153
+ fullDiff: params.fullDiff,
154
+ fullDiffHash: ApprovalService.hashContent(params.fullDiff),
155
+ patchedSource: params.patchedSource,
156
+ patchedSourceHash: ApprovalService.hashContent(params.patchedSource),
157
+ testFilePath: canonicalTest,
158
+ testFileHash: ApprovalService.hashContent(params.testFileContent),
159
+ worktreeId: params.worktreeId,
160
+ worktreeExecutionVerified: isVerified,
161
+ executionProof: params.proof,
162
+ createdAt: now,
163
+ expiresAt: now + ttl,
164
+ };
165
+
166
+ this.proposals.set(proposalId, staged);
167
+ try {
168
+ fs.mkdirSync(this.proposalsDir, { recursive: true });
169
+ fs.writeFileSync(
170
+ path.join(this.proposalsDir, `${proposalId}.json`),
171
+ JSON.stringify(staged, null, 2),
172
+ 'utf-8'
173
+ );
174
+ } catch {
175
+ // Safe fallback if disk is read-only
176
+ }
177
+ return staged;
178
+ }
179
+
180
+ public getTokenPayload(tokenId: string): ApprovalTokenPayload | undefined {
181
+ this.cleanupExpired();
182
+ let payload = this.tokens.get(tokenId);
183
+ if (!payload) {
184
+ const diskPath = path.join(this.tokensDir, `${tokenId}.json`);
185
+ if (fs.existsSync(diskPath)) {
186
+ try {
187
+ const raw = fs.readFileSync(diskPath, 'utf-8');
188
+ const parsed = JSON.parse(raw) as ApprovalTokenPayload;
189
+ if (Date.now() <= parsed.expiresAt && !parsed.used) {
190
+ payload = parsed;
191
+ this.tokens.set(tokenId, parsed);
192
+ } else {
193
+ try { fs.unlinkSync(diskPath); } catch {}
194
+ }
195
+ } catch {
196
+ // Ignore corrupt file
197
+ }
198
+ }
199
+ }
200
+ return payload;
201
+ }
202
+
203
+ public getProposal(proposalId: string): StagedProposal | undefined {
204
+ this.cleanupExpired();
205
+ let proposal = this.proposals.get(proposalId);
206
+ if (!proposal) {
207
+ const diskPath = path.join(this.proposalsDir, `${proposalId}.json`);
208
+ if (fs.existsSync(diskPath)) {
209
+ try {
210
+ const raw = fs.readFileSync(diskPath, 'utf-8');
211
+ const parsed = JSON.parse(raw) as StagedProposal;
212
+ if (Date.now() <= parsed.expiresAt) {
213
+ proposal = parsed;
214
+ this.proposals.set(proposalId, parsed);
215
+ } else {
216
+ try { fs.unlinkSync(diskPath); } catch {}
217
+ }
218
+ } catch {
219
+ // Ignore corrupt file
220
+ }
221
+ }
222
+ }
223
+ return proposal;
224
+ }
225
+
226
+ /**
227
+ * Authorizes a staged proposal and issues an unforgeable single-use approval token.
228
+ * Must be called explicitly by a human CLI action or a verified authorization hook.
229
+ */
230
+ public authorizeProposal(params: {
231
+ proposalId: string;
232
+ ttlMs?: number;
233
+ }): ApprovalTokenPayload {
234
+ this.cleanupExpired();
235
+ const proposal = this.getProposal(params.proposalId);
236
+ if (!proposal) {
237
+ throw new Error(`Cannot authorize proposal: '${params.proposalId}' not found or expired.`);
238
+ }
239
+
240
+ if (!proposal.worktreeExecutionVerified || !proposal.executionProof || !proposal.executionProof.passed) {
241
+ throw new Error(
242
+ `Security Violation: Cannot authorize proposal '${params.proposalId}'. Worktree test execution was not verified.`
243
+ );
244
+ }
245
+
246
+ const tokenId = crypto.randomBytes(32).toString('hex');
247
+ const nonce = crypto.randomBytes(16).toString('hex');
248
+ const now = Date.now();
249
+ const ttl = params.ttlMs ?? this.defaultTtlMs;
250
+
251
+ const payload: ApprovalTokenPayload = {
252
+ tokenId,
253
+ proposalId: proposal.proposalId,
254
+ targetFilePath: proposal.targetFilePath,
255
+ targetClassName: proposal.targetClassName,
256
+ targetMemberName: proposal.targetMemberName,
257
+ originalSourceHash: proposal.originalSourceHash,
258
+ replacementSelector: proposal.replacementSelector,
259
+ replacementAstHash: proposal.replacementAstHash,
260
+ fullDiffHash: proposal.fullDiffHash,
261
+ patchedSourceHash: proposal.patchedSourceHash,
262
+ testFilePath: proposal.testFilePath,
263
+ testFileHash: proposal.testFileHash,
264
+ executionVerified: proposal.worktreeExecutionVerified,
265
+ issuedAt: now,
266
+ expiresAt: now + ttl,
267
+ used: false,
268
+ nonce,
269
+ };
270
+
271
+ this.tokens.set(tokenId, payload);
272
+ try {
273
+ fs.mkdirSync(this.tokensDir, { recursive: true });
274
+ fs.writeFileSync(
275
+ path.join(this.tokensDir, `${tokenId}.json`),
276
+ JSON.stringify(payload, null, 2),
277
+ 'utf-8'
278
+ );
279
+ } catch {
280
+ // Safe fallback
281
+ }
282
+ return payload;
283
+ }
284
+
285
+ /**
286
+ * Legacy generator helper retained for compatibility, now strictly requires explicit execution verification.
287
+ */
288
+ public generateToken(params: {
289
+ proposalId: string;
290
+ patchHash: string;
291
+ targetFilePath: string;
292
+ originalSourceHash: string;
293
+ targetClassName?: string;
294
+ targetMemberName?: string;
295
+ replacementSelector?: string;
296
+ fullDiffHash?: string;
297
+ patchedSourceHash?: string;
298
+ testFilePath?: string;
299
+ testFileHash?: string;
300
+ executionVerified?: boolean;
301
+ ttlMs?: number;
302
+ }): ApprovalTokenPayload {
303
+ let canonicalTarget = params.targetFilePath;
304
+ try {
305
+ if (fs.existsSync(params.targetFilePath)) {
306
+ canonicalTarget = fs.realpathSync(params.targetFilePath);
307
+ }
308
+ } catch {
309
+ // fallback
310
+ }
311
+
312
+ let canonicalTest = params.testFilePath ?? params.targetFilePath;
313
+ try {
314
+ if (fs.existsSync(canonicalTest)) {
315
+ canonicalTest = fs.realpathSync(canonicalTest);
316
+ }
317
+ } catch {
318
+ // fallback
319
+ }
320
+
321
+ const tokenId = crypto.randomBytes(32).toString('hex');
322
+ const nonce = crypto.randomBytes(16).toString('hex');
323
+ const now = Date.now();
324
+ const ttl = params.ttlMs ?? this.defaultTtlMs;
325
+
326
+ // Fail closed: executionVerified is strictly false unless explicitly supplied as true
327
+ const executionVerified = params.executionVerified === true;
328
+
329
+ const payload: ApprovalTokenPayload = {
330
+ tokenId,
331
+ proposalId: params.proposalId,
332
+ targetFilePath: canonicalTarget,
333
+ targetClassName: params.targetClassName ?? 'UnknownClass',
334
+ targetMemberName: params.targetMemberName ?? 'unknownMember',
335
+ originalSourceHash: params.originalSourceHash,
336
+ replacementSelector: params.replacementSelector ?? '',
337
+ replacementAstHash: params.patchHash,
338
+ fullDiffHash: params.fullDiffHash ?? params.patchHash,
339
+ patchedSourceHash: params.patchedSourceHash ?? params.patchHash,
340
+ testFilePath: canonicalTest,
341
+ testFileHash: params.testFileHash ?? '',
342
+ executionVerified,
343
+ issuedAt: now,
344
+ expiresAt: now + ttl,
345
+ used: false,
346
+ nonce,
347
+ };
348
+
349
+ this.tokens.set(tokenId, payload);
350
+ try {
351
+ fs.mkdirSync(this.tokensDir, { recursive: true });
352
+ fs.writeFileSync(
353
+ path.join(this.tokensDir, `${tokenId}.json`),
354
+ JSON.stringify(payload, null, 2),
355
+ 'utf-8'
356
+ );
357
+ } catch {}
358
+ return payload;
359
+ }
360
+
361
+ /**
362
+ * Verifies an approval token against the current file state, target member, diff hash,
363
+ * and test file hash, then atomically consumes the token on success.
364
+ */
365
+ public verifyAndConsumeToken(params: {
366
+ tokenId: string;
367
+ targetFilePath: string;
368
+ currentSourceContent: string;
369
+ targetClassName?: string;
370
+ targetMemberName?: string;
371
+ patchHash?: string;
372
+ fullDiffHash?: string;
373
+ currentTestFileContent?: string;
374
+ }): TokenValidationResult {
375
+ this.cleanupExpired();
376
+
377
+ const payload = this.tokens.get(params.tokenId);
378
+ if (!payload) {
379
+ return {
380
+ isValid: false,
381
+ reason: 'Approval token not found or already expired/consumed.',
382
+ };
383
+ }
384
+
385
+ if (payload.used) {
386
+ return {
387
+ isValid: false,
388
+ reason: 'Security Violation: Approval token has already been consumed (replay rejected).',
389
+ };
390
+ }
391
+
392
+ if (Date.now() > payload.expiresAt) {
393
+ this.tokens.delete(params.tokenId);
394
+ return {
395
+ isValid: false,
396
+ reason: 'Approval token has expired.',
397
+ };
398
+ }
399
+
400
+ let canonicalPath = params.targetFilePath;
401
+ try {
402
+ if (fs.existsSync(params.targetFilePath)) {
403
+ canonicalPath = fs.realpathSync(params.targetFilePath);
404
+ }
405
+ } catch {
406
+ // ignore
407
+ }
408
+
409
+ if (canonicalPath !== payload.targetFilePath) {
410
+ return {
411
+ isValid: false,
412
+ reason: `Target file mismatch: token is bound to '${payload.targetFilePath}', received '${canonicalPath}'.`,
413
+ };
414
+ }
415
+
416
+ // Target class and member validation
417
+ if (params.targetClassName && params.targetClassName !== payload.targetClassName) {
418
+ return {
419
+ isValid: false,
420
+ reason: `Class mismatch: token is bound to class '${payload.targetClassName}', received '${params.targetClassName}'.`,
421
+ };
422
+ }
423
+
424
+ if (params.targetMemberName && params.targetMemberName !== payload.targetMemberName) {
425
+ return {
426
+ isValid: false,
427
+ reason: `Member mismatch: token is bound to member '${payload.targetMemberName}', received '${params.targetMemberName}'.`,
428
+ };
429
+ }
430
+
431
+ // Verify source file content has not changed since proposal was generated
432
+ const currentHash = ApprovalService.hashContent(params.currentSourceContent);
433
+ if (currentHash !== payload.originalSourceHash) {
434
+ return {
435
+ isValid: false,
436
+ reason: 'Target file has been modified since the repair proposal was approved (source hash mismatch).',
437
+ };
438
+ }
439
+
440
+ // Verify diff hash if supplied
441
+ if (params.fullDiffHash && params.fullDiffHash !== payload.fullDiffHash) {
442
+ return {
443
+ isValid: false,
444
+ reason: 'Diff hash mismatch: token is bound to a different patch diff.',
445
+ };
446
+ }
447
+
448
+ // Verify patch/ast hash if supplied
449
+ if (params.patchHash && params.patchHash !== payload.replacementAstHash && params.patchHash !== payload.fullDiffHash) {
450
+ return {
451
+ isValid: false,
452
+ reason: 'Patch content mismatch: token is bound to a different patch hash.',
453
+ };
454
+ }
455
+
456
+ // Verify test file content has not changed
457
+ if (params.currentTestFileContent && payload.testFileHash) {
458
+ const currentTestHash = ApprovalService.hashContent(params.currentTestFileContent);
459
+ if (currentTestHash !== payload.testFileHash) {
460
+ return {
461
+ isValid: false,
462
+ reason: 'Test file has been modified since the repair proposal was approved (test hash mismatch).',
463
+ };
464
+ }
465
+ }
466
+
467
+ // Verify execution was actually verified
468
+ if (!payload.executionVerified) {
469
+ return {
470
+ isValid: false,
471
+ reason: 'Security Violation: Cannot consume token for unverified repair execution.',
472
+ };
473
+ }
474
+
475
+ // Atomically mark as consumed in memory and delete on disk
476
+ payload.used = true;
477
+ this.tokens.delete(params.tokenId);
478
+ const diskPath = path.join(this.tokensDir, `${params.tokenId}.json`);
479
+ if (fs.existsSync(diskPath)) {
480
+ try {
481
+ fs.unlinkSync(diskPath);
482
+ } catch {}
483
+ }
484
+
485
+ return {
486
+ isValid: true,
487
+ payload,
488
+ };
489
+ }
490
+
491
+ public revokeToken(tokenId: string): boolean {
492
+ const memDeleted = this.tokens.delete(tokenId);
493
+ const diskPath = path.join(this.tokensDir, `${tokenId}.json`);
494
+ if (fs.existsSync(diskPath)) {
495
+ try {
496
+ fs.unlinkSync(diskPath);
497
+ return true;
498
+ } catch {}
499
+ }
500
+ return memDeleted;
501
+ }
502
+
503
+ public clearAllTokens(): void {
504
+ this.tokens.clear();
505
+ this.proposals.clear();
506
+ try {
507
+ if (fs.existsSync(this.proposalsDir)) {
508
+ for (const f of fs.readdirSync(this.proposalsDir)) {
509
+ try { fs.unlinkSync(path.join(this.proposalsDir, f)); } catch {}
510
+ }
511
+ }
512
+ if (fs.existsSync(this.tokensDir)) {
513
+ for (const f of fs.readdirSync(this.tokensDir)) {
514
+ try { fs.unlinkSync(path.join(this.tokensDir, f)); } catch {}
515
+ }
516
+ }
517
+ } catch {}
518
+ }
519
+
520
+ private cleanupExpired(): void {
521
+ const now = Date.now();
522
+ for (const [id, payload] of this.tokens.entries()) {
523
+ if (now > payload.expiresAt || payload.used) {
524
+ this.tokens.delete(id);
525
+ const diskPath = path.join(this.tokensDir, `${id}.json`);
526
+ if (fs.existsSync(diskPath)) {
527
+ try { fs.unlinkSync(diskPath); } catch {}
528
+ }
529
+ }
530
+ }
531
+ for (const [id, proposal] of this.proposals.entries()) {
532
+ if (now > proposal.expiresAt) {
533
+ this.proposals.delete(id);
534
+ const diskPath = path.join(this.proposalsDir, `${id}.json`);
535
+ if (fs.existsSync(diskPath)) {
536
+ try { fs.unlinkSync(diskPath); } catch {}
537
+ }
538
+ }
539
+ }
540
+ }
541
+ }