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,243 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+
4
+ export interface SelectorHistoryRecord {
5
+ selector: string;
6
+ successes: number;
7
+ failures: number;
8
+ semanticVerifications: number;
9
+ repairsAccepted: number;
10
+ falseRepairIncidents: number;
11
+ lastSuccess?: number | undefined;
12
+ lastFailure?: number | undefined;
13
+ failureCategories: string[];
14
+ reliabilityScore: number;
15
+ historicalExecutionReliability: number;
16
+ }
17
+
18
+ export interface StabilityLedgerData {
19
+ version: number;
20
+ updatedAt: number;
21
+ scope: {
22
+ projectRoot: string;
23
+ repositoryName: string;
24
+ };
25
+ selectors: Record<string, SelectorHistoryRecord>;
26
+ }
27
+
28
+ export type RepairFeedbackOutcome = 'accepted' | 'rejected';
29
+
30
+ /** Applies bounded time decay toward the neutral prior without mutating history. */
31
+ export function effectiveReliabilityScore(
32
+ record: SelectorHistoryRecord,
33
+ now = Date.now(),
34
+ halfLifeMs = 30 * 24 * 60 * 60 * 1000,
35
+ ): number {
36
+ if (record.falseRepairIncidents > 0) return 0.1;
37
+ const latestEvidence = Math.max(record.lastSuccess ?? 0, record.lastFailure ?? 0);
38
+ if (latestEvidence <= 0 || halfLifeMs <= 0) return record.reliabilityScore;
39
+ const ageMs = Math.max(0, now - latestEvidence);
40
+ const decay = Math.exp(-ageMs / halfLifeMs);
41
+ const score = 0.8 + (record.reliabilityScore - 0.8) * decay;
42
+ return Number(Math.max(0, Math.min(1, score)).toFixed(3));
43
+ }
44
+
45
+ export class StabilityLedger {
46
+ private readonly ledgerPath: string;
47
+ private data: StabilityLedgerData | null = null;
48
+
49
+ constructor(baseDir = '.') {
50
+ this.ledgerPath = path.resolve(baseDir, '.pw-intelligence', 'stability-ledger.json');
51
+ }
52
+
53
+ public async init(): Promise<void> {
54
+ await fs.mkdir(path.dirname(this.ledgerPath), { recursive: true });
55
+ try {
56
+ const raw = await fs.readFile(this.ledgerPath, 'utf8');
57
+ const parsed = JSON.parse(raw) as Partial<StabilityLedgerData>;
58
+ this.data = {
59
+ version: parsed.version ?? 3,
60
+ updatedAt: parsed.updatedAt ?? Date.now(),
61
+ scope: parsed.scope ?? {
62
+ projectRoot: path.resolve(path.dirname(this.ledgerPath), '..'),
63
+ repositoryName: path.basename(path.resolve(path.dirname(this.ledgerPath), '..')),
64
+ },
65
+ selectors: parsed.selectors ?? {},
66
+ };
67
+ if (!parsed.scope || parsed.version !== 3) await this.save();
68
+ } catch {
69
+ this.data = {
70
+ version: 3,
71
+ updatedAt: Date.now(),
72
+ scope: {
73
+ projectRoot: path.resolve(path.dirname(this.ledgerPath), '..'),
74
+ repositoryName: path.basename(path.resolve(path.dirname(this.ledgerPath), '..')),
75
+ },
76
+ selectors: {},
77
+ };
78
+ await this.save();
79
+ }
80
+ }
81
+
82
+ public getScore(selector: string): number {
83
+ if (!this.data) return 0.8; // Default baseline score
84
+ const record = this.data.selectors[selector];
85
+ if (!record) return 0.8;
86
+ return record.reliabilityScore;
87
+ }
88
+
89
+ public getEffectiveScore(selector: string, now = Date.now(), halfLifeMs?: number): number {
90
+ if (!this.data) return 0.8;
91
+ const record = this.data.selectors[selector];
92
+ return record ? effectiveReliabilityScore(record, now, halfLifeMs) : 0.8;
93
+ }
94
+
95
+ public async recordSuccess(selector: string): Promise<void> {
96
+ await this.ensureLoaded();
97
+ const existing = this.data!.selectors[selector] ?? {
98
+ selector,
99
+ successes: 0,
100
+ failures: 0,
101
+ semanticVerifications: 1,
102
+ repairsAccepted: 0,
103
+ falseRepairIncidents: 0,
104
+ failureCategories: [],
105
+ reliabilityScore: 0.8,
106
+ historicalExecutionReliability: 0.8,
107
+ };
108
+
109
+ existing.successes += 1;
110
+ existing.lastSuccess = Date.now();
111
+ existing.reliabilityScore = this.computeScore(existing);
112
+ existing.historicalExecutionReliability = existing.reliabilityScore;
113
+ this.data!.selectors[selector] = existing;
114
+ await this.save();
115
+ }
116
+
117
+ public async recordFailure(selector: string, category: string): Promise<void> {
118
+ await this.ensureLoaded();
119
+ const existing = this.data!.selectors[selector] ?? {
120
+ selector,
121
+ successes: 0,
122
+ failures: 0,
123
+ semanticVerifications: 0,
124
+ repairsAccepted: 0,
125
+ falseRepairIncidents: 0,
126
+ failureCategories: [],
127
+ reliabilityScore: 0.8,
128
+ historicalExecutionReliability: 0.8,
129
+ };
130
+
131
+ existing.failures += 1;
132
+ existing.lastFailure = Date.now();
133
+ if (!existing.failureCategories.includes(category)) {
134
+ existing.failureCategories.push(category);
135
+ }
136
+ existing.reliabilityScore = this.computeScore(existing);
137
+ existing.historicalExecutionReliability = existing.reliabilityScore;
138
+ this.data!.selectors[selector] = existing;
139
+ await this.save();
140
+ }
141
+
142
+ public async recordSemanticVerification(selector: string): Promise<void> {
143
+ await this.ensureLoaded();
144
+ const existing = this.data!.selectors[selector] ?? this.createRecord(selector);
145
+ existing.semanticVerifications += 1;
146
+ // A semantic verification is produced only after the isolated execution
147
+ // and intent gates pass. Count it as one successful observation so future
148
+ // candidate ranking can learn from verified repairs, while keeping the
149
+ // separate repairsAccepted field as operator-approval metadata.
150
+ existing.successes += 1;
151
+ existing.lastSuccess = Date.now();
152
+ existing.reliabilityScore = this.computeScore(existing);
153
+ existing.historicalExecutionReliability = existing.reliabilityScore;
154
+ this.data!.selectors[selector] = existing;
155
+ await this.save();
156
+ }
157
+
158
+ public async recordRepairAccepted(selector: string): Promise<void> {
159
+ await this.ensureLoaded();
160
+ const existing = this.data!.selectors[selector] ?? this.createRecord(selector);
161
+ existing.repairsAccepted += 1;
162
+ this.data!.selectors[selector] = existing;
163
+ await this.save();
164
+ }
165
+
166
+ public getSnapshot(): StabilityLedgerData {
167
+ if (!this.data) {
168
+ return {
169
+ version: 3,
170
+ updatedAt: 0,
171
+ scope: {
172
+ projectRoot: path.resolve(path.dirname(this.ledgerPath), '..'),
173
+ repositoryName: path.basename(path.resolve(path.dirname(this.ledgerPath), '..')),
174
+ },
175
+ selectors: {},
176
+ };
177
+ }
178
+ return JSON.parse(JSON.stringify(this.data)) as StabilityLedgerData;
179
+ }
180
+
181
+ public async recordFalseRepairIncident(selector: string): Promise<void> {
182
+ await this.ensureLoaded();
183
+ const existing = this.data!.selectors[selector] ?? {
184
+ selector,
185
+ successes: 0,
186
+ failures: 1,
187
+ semanticVerifications: 0,
188
+ repairsAccepted: 0,
189
+ falseRepairIncidents: 0,
190
+ failureCategories: ['FALSE_REPAIR'],
191
+ reliabilityScore: 0.2,
192
+ historicalExecutionReliability: 0.2,
193
+ };
194
+
195
+ existing.falseRepairIncidents += 1;
196
+ existing.reliabilityScore = this.computeScore(existing);
197
+ existing.historicalExecutionReliability = existing.reliabilityScore;
198
+ this.data!.selectors[selector] = existing;
199
+ await this.save();
200
+ }
201
+
202
+ /** Records operator review feedback without erasing the underlying history. */
203
+ public async recordRepairFeedback(selector: string, outcome: RepairFeedbackOutcome): Promise<void> {
204
+ if (outcome === 'accepted') await this.recordRepairAccepted(selector);
205
+ else await this.recordFalseRepairIncident(selector);
206
+ }
207
+
208
+ private computeScore(record: { successes: number; failures: number; falseRepairIncidents: number }): number {
209
+ // If any false repair incident occurred, severely penalize (fail-safe against confirmation bias)
210
+ if (record.falseRepairIncidents > 0) {
211
+ return 0.1;
212
+ }
213
+ const total = record.successes + record.failures * 2; // Penalize failures with double weight
214
+ if (total === 0) return 0.8;
215
+ const score = record.successes / total;
216
+ return parseFloat(score.toFixed(3));
217
+ }
218
+
219
+ private createRecord(selector: string): SelectorHistoryRecord {
220
+ return {
221
+ selector,
222
+ successes: 0,
223
+ failures: 0,
224
+ semanticVerifications: 0,
225
+ repairsAccepted: 0,
226
+ falseRepairIncidents: 0,
227
+ failureCategories: [],
228
+ reliabilityScore: 0.8,
229
+ historicalExecutionReliability: 0.8,
230
+ };
231
+ }
232
+
233
+ private async ensureLoaded(): Promise<void> {
234
+ if (!this.data) await this.init();
235
+ }
236
+
237
+ private async save(): Promise<void> {
238
+ if (!this.data) return;
239
+ this.data.updatedAt = Date.now();
240
+ await fs.writeFile(this.ledgerPath, JSON.stringify(this.data, null, 2), 'utf8');
241
+ }
242
+ }
243
+
@@ -0,0 +1,32 @@
1
+ import { z } from 'zod';
2
+
3
+ export const AppElementSchema = z.object({
4
+ tag: z.string().min(1),
5
+ role: z.string().optional(),
6
+ accessibleName: z.string().optional(),
7
+ label: z.string().optional(),
8
+ testId: z.string().optional(),
9
+ placeholder: z.string().optional(),
10
+ type: z.string().optional(),
11
+ frameSelector: z.string().optional(),
12
+ count: z.number().int().min(1).default(1),
13
+ visible: z.boolean().default(true),
14
+ });
15
+ export type AppElement = z.infer<typeof AppElementSchema>;
16
+
17
+ export const AppRouteSchema = z.object({
18
+ route: z.string().min(1),
19
+ title: z.string().optional(),
20
+ elements: z.array(AppElementSchema).default([]),
21
+ });
22
+ export type AppRoute = z.infer<typeof AppRouteSchema>;
23
+
24
+ export const AppModelSchema = z.object({
25
+ version: z.literal('1').default('1'),
26
+ baseUrl: z.string().min(1),
27
+ fingerprint: z.string().min(1),
28
+ capturedAt: z.string(),
29
+ routes: z.array(AppRouteSchema).default([]),
30
+ });
31
+ export type AppModel = z.infer<typeof AppModelSchema>;
32
+
@@ -0,0 +1,263 @@
1
+ import fs from 'node:fs/promises';
2
+ import path from 'node:path';
3
+ import crypto from 'node:crypto';
4
+ import { AppModel, AppModelSchema, AppRoute, AppElement } from './app-model.js';
5
+
6
+ export interface CrawlerOptions {
7
+ baseUrl: string;
8
+ fingerprint?: string;
9
+ maxRoutes?: number;
10
+ maxDepth?: number;
11
+ storageStatePath?: string;
12
+ cacheDir?: string;
13
+ browser?: any;
14
+ page?: any;
15
+ }
16
+
17
+ export function computeCacheKey(baseUrl: string, fingerprint?: string): string {
18
+ const normalized = baseUrl.replace(/\/+$/, '').toLowerCase();
19
+ const raw = `${normalized}::${fingerprint ?? 'default'}`;
20
+ return crypto.createHash('sha256').update(raw).digest('hex').slice(0, 16);
21
+ }
22
+
23
+ export async function getCachedAppModel(cacheDir: string, baseUrl: string, fingerprint?: string): Promise<AppModel | null> {
24
+ try {
25
+ const key = computeCacheKey(baseUrl, fingerprint);
26
+ const filePath = path.join(cacheDir, `app-model-${key}.json`);
27
+ const content = await fs.readFile(filePath, 'utf8');
28
+ const parsed = JSON.parse(content);
29
+ const validated = AppModelSchema.safeParse(parsed);
30
+ if (validated.success) {
31
+ return validated.data;
32
+ }
33
+ return null;
34
+ } catch {
35
+ return null;
36
+ }
37
+ }
38
+
39
+ export async function saveCachedAppModel(cacheDir: string, model: AppModel): Promise<void> {
40
+ try {
41
+ await fs.mkdir(cacheDir, { recursive: true });
42
+ const key = computeCacheKey(model.baseUrl, model.fingerprint);
43
+ const filePath = path.join(cacheDir, `app-model-${key}.json`);
44
+ await fs.writeFile(filePath, JSON.stringify(model, null, 2), 'utf8');
45
+ } catch {
46
+ // Non-fatal if cache cannot be written
47
+ }
48
+ }
49
+
50
+ async function extractFrameSelector(frame: any): Promise<string> {
51
+ try {
52
+ if (typeof frame.name === 'function' && frame.name()) {
53
+ return `iframe[name="${frame.name()}"]`;
54
+ }
55
+ if (typeof frame.frameElement === 'function') {
56
+ const handle = await frame.frameElement();
57
+ if (handle) {
58
+ const selector = await handle.evaluate((el: any) => {
59
+ const title = el.getAttribute('title');
60
+ if (title) return `iframe[title="${title}"]`;
61
+ const name = el.getAttribute('name');
62
+ if (name) return `iframe[name="${name}"]`;
63
+ const id = el.getAttribute('id');
64
+ if (id) return `iframe#${id}`;
65
+ const src = el.getAttribute('src');
66
+ if (src) return `iframe[src="${src}"]`;
67
+ return 'iframe';
68
+ });
69
+ if (selector) return selector;
70
+ }
71
+ }
72
+ } catch {
73
+ // Fallback if frame element lookup fails
74
+ }
75
+ return 'iframe';
76
+ }
77
+
78
+ async function collectFrameElements(frameHandle: any, frameSelector?: string): Promise<AppElement[]> {
79
+ try {
80
+ const raw = await frameHandle.$$eval('button, a, input, select, textarea, [role="button"], [role="textbox"], [role="status"], [role="list"], [role="listitem"], ul, ol, heading, h1, h2, h3', (nodes: any[]) => {
81
+ return nodes.map((el) => {
82
+ const tag = el.tagName.toLowerCase();
83
+ const type = el.getAttribute('type') || undefined;
84
+ let role = el.getAttribute('role');
85
+ if (!role) {
86
+ if (tag === 'button') role = 'button';
87
+ else if (tag === 'input') {
88
+ if (type === 'checkbox') role = 'checkbox';
89
+ else if (type === 'radio') role = 'radio';
90
+ else if (type === 'submit' || type === 'button') role = 'button';
91
+ else role = 'textbox';
92
+ } else if (tag === 'textarea') role = 'textbox';
93
+ else if (tag === 'select') role = 'combobox';
94
+ else if (/^h[1-6]$/.test(tag)) role = 'heading';
95
+ else if (tag === 'ul' || tag === 'ol') role = 'list';
96
+ }
97
+ const placeholder = el.getAttribute('placeholder') || undefined;
98
+ const accessibleName = el.getAttribute('aria-label') || el.innerText?.trim() || placeholder || undefined;
99
+ const label = el.getAttribute('aria-label') || el.getAttribute('name') || undefined;
100
+ const testId = el.getAttribute('data-testid') || el.getAttribute('data-test') || undefined;
101
+ const visible = Boolean(el.offsetWidth || el.offsetHeight || el.getClientRects().length);
102
+ return { tag, role, accessibleName, label, testId, placeholder, type, visible };
103
+ });
104
+ });
105
+
106
+ const elements: AppElement[] = raw.map((item: any) => ({
107
+ tag: item.tag,
108
+ role: item.role,
109
+ accessibleName: item.accessibleName,
110
+ label: item.label,
111
+ testId: item.testId,
112
+ placeholder: item.placeholder,
113
+ type: item.type,
114
+ frameSelector,
115
+ count: 1,
116
+ visible: item.visible,
117
+ }));
118
+ return elements;
119
+ } catch {
120
+ if (frameSelector) {
121
+ return [{
122
+ tag: 'iframe',
123
+ role: 'cross-origin-unavailable',
124
+ frameSelector,
125
+ count: 1,
126
+ visible: true,
127
+ }];
128
+ }
129
+ return [];
130
+ }
131
+ }
132
+
133
+ export class AppCrawler {
134
+ public static async crawl(options: CrawlerOptions): Promise<AppModel> {
135
+ const cacheDir = options.cacheDir ?? path.join(process.cwd(), '.pie', 'cache');
136
+ const fingerprint = options.fingerprint ?? 'default';
137
+
138
+ // 1. Check cache
139
+ const cached = await getCachedAppModel(cacheDir, options.baseUrl, fingerprint);
140
+ if (cached) {
141
+ return cached;
142
+ }
143
+
144
+ const maxRoutes = options.maxRoutes ?? 20;
145
+ const maxDepth = options.maxDepth ?? 2;
146
+ const baseHost = new URL(options.baseUrl).host;
147
+
148
+ const routes: AppRoute[] = [];
149
+ let browserToClose: any = null;
150
+
151
+ try {
152
+ let page = options.page;
153
+ if (!page) {
154
+ if (options.browser) {
155
+ const context = await options.browser.newContext({
156
+ storageState: options.storageStatePath,
157
+ });
158
+ page = await context.newPage();
159
+ } else {
160
+ // Attempt dynamic import of playwright
161
+ try {
162
+ const { chromium } = await import('@playwright/test');
163
+ browserToClose = await chromium.launch({ headless: true });
164
+ const context = await browserToClose.newContext({
165
+ storageState: options.storageStatePath,
166
+ });
167
+ page = await context.newPage();
168
+ } catch {
169
+ // If browser cannot be launched, return minimal bounded model
170
+ const fallbackModel: AppModel = {
171
+ version: '1',
172
+ baseUrl: options.baseUrl,
173
+ fingerprint,
174
+ capturedAt: new Date().toISOString(),
175
+ routes: [{ route: '/', elements: [] }],
176
+ };
177
+ return fallbackModel;
178
+ }
179
+ }
180
+ }
181
+
182
+ const queue: Array<{ url: string; depth: number }> = [{ url: options.baseUrl, depth: 0 }];
183
+ const visited = new Set<string>();
184
+
185
+ while (queue.length > 0 && routes.length < maxRoutes) {
186
+ const item = queue.shift()!;
187
+ if (visited.has(item.url)) continue;
188
+ visited.add(item.url);
189
+
190
+ try {
191
+ await page.goto(item.url, { waitUntil: 'domcontentloaded', timeout: 15_000 });
192
+ } catch {
193
+ continue;
194
+ }
195
+
196
+ const elements: AppElement[] = [];
197
+
198
+ // Main frame elements
199
+ const mainElements = await collectFrameElements(page, undefined);
200
+ elements.push(...mainElements);
201
+
202
+ // Child frame elements
203
+ if (typeof page.frames === 'function') {
204
+ const childFrames = page.frames().slice(1);
205
+ for (const childFrame of childFrames) {
206
+ const frameSelector = await extractFrameSelector(childFrame);
207
+ const childElements = await collectFrameElements(childFrame, frameSelector);
208
+ elements.push(...childElements);
209
+ }
210
+ }
211
+
212
+ const urlObj = new URL(item.url);
213
+ routes.push({
214
+ route: urlObj.pathname || '/',
215
+ title: typeof page.title === 'function' ? await page.title().catch(() => undefined) : undefined,
216
+ elements,
217
+ });
218
+
219
+ // Bounded exploration: collect links without clicking destructive elements
220
+ if (item.depth < maxDepth && routes.length < maxRoutes) {
221
+ try {
222
+ const hrefs: string[] = await page.$$eval('a[href]', (anchors: any[]) =>
223
+ anchors.map((a) => a.getAttribute('href')).filter(Boolean),
224
+ );
225
+ for (const href of hrefs) {
226
+ try {
227
+ const resolved = new URL(href, item.url);
228
+ // Allowlisted host only, no logout/delete/submit paths
229
+ if (resolved.host === baseHost && !/logout|signout|delete|destroy/i.test(resolved.pathname)) {
230
+ const cleaned = resolved.origin + resolved.pathname;
231
+ if (!visited.has(cleaned) && !queue.some((q) => q.url === cleaned)) {
232
+ queue.push({ url: cleaned, depth: item.depth + 1 });
233
+ }
234
+ }
235
+ } catch {
236
+ // Ignore invalid URLs
237
+ }
238
+ }
239
+ } catch {
240
+ // Ignore link evaluation failure
241
+ }
242
+ }
243
+ }
244
+ } catch {
245
+ // Bounded fallback
246
+ } finally {
247
+ if (browserToClose) {
248
+ await browserToClose.close().catch(() => {});
249
+ }
250
+ }
251
+
252
+ const model: AppModel = {
253
+ version: '1',
254
+ baseUrl: options.baseUrl,
255
+ fingerprint,
256
+ capturedAt: new Date().toISOString(),
257
+ routes: routes.length > 0 ? routes : [{ route: '/', elements: [] }],
258
+ };
259
+
260
+ await saveCachedAppModel(cacheDir, model);
261
+ return model;
262
+ }
263
+ }
@@ -0,0 +1,3 @@
1
+ export * from './app-model.js';
2
+ export * from './crawler.js';
3
+