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,331 @@
1
+ import { ResolvedElement } from '../trace/snapshot-resolver.js';
2
+ import { CandidateExtractor } from '../selector/candidate-extractor.js';
3
+
4
+ export type DisambiguationStrategy =
5
+ | 'UNIQUE_DIRECT_MATCH'
6
+ | 'VISIBILITY_FILTER'
7
+ | 'CONTAINER_ANCHOR'
8
+ | 'LANDMARK_SECTION'
9
+ | 'EXPLICIT_POSITIONAL_INTENT'
10
+ | 'ABSTAIN_AMBIGUOUS';
11
+
12
+ export interface DisambiguationContext {
13
+ originalSelector?: string | undefined;
14
+ intendedAction?: string | undefined;
15
+ testIntentText?: string | undefined;
16
+ rootDom?: ResolvedElement | undefined;
17
+ }
18
+
19
+ export interface DisambiguationResult {
20
+ strategy: DisambiguationStrategy;
21
+ resolvedCandidate?: ResolvedElement;
22
+ resolvedExpression?: string;
23
+ scopedContainer?: ResolvedElement;
24
+ containerAnchorText?: string;
25
+ matchCount: number;
26
+ explanation: string;
27
+ confidence: number;
28
+ isUnique: boolean;
29
+ }
30
+
31
+ export class LocatorDisambiguationEngine {
32
+ /**
33
+ * Evaluates multiple candidate elements through the evidence-based disambiguation hierarchy:
34
+ * 1. Unique Direct Match (matchCount === 1)
35
+ * 2. Visibility Filtering (filter { visible: true })
36
+ * 3. Container Anchoring (tr, card, list item with distinguishing text)
37
+ * 4. Landmark / Section Scoping (header, nav, main, dialog)
38
+ * 5. Explicit Positional Intent (.first(), .nth() only if original test explicitly declared it)
39
+ * 6. Safe Abstention (never guess positional intent)
40
+ */
41
+ public static disambiguate(
42
+ candidates: ResolvedElement[],
43
+ context: DisambiguationContext = {},
44
+ pageProperty = 'page'
45
+ ): DisambiguationResult {
46
+ if (candidates.length === 0) {
47
+ return {
48
+ strategy: 'ABSTAIN_AMBIGUOUS',
49
+ matchCount: 0,
50
+ explanation: 'Zero matching candidate elements found.',
51
+ confidence: 0,
52
+ isUnique: false,
53
+ };
54
+ }
55
+
56
+ // 1. Single Unique Candidate
57
+ if (candidates.length === 1) {
58
+ const candidate = candidates[0]!;
59
+ const expr = CandidateExtractor.generateLocator(candidate, pageProperty);
60
+ return {
61
+ strategy: 'UNIQUE_DIRECT_MATCH',
62
+ resolvedCandidate: candidate,
63
+ resolvedExpression: expr,
64
+ matchCount: 1,
65
+ explanation: 'Candidate is strictly unique in the DOM snapshot (matchCount === 1).',
66
+ confidence: 1.0,
67
+ isUnique: true,
68
+ };
69
+ }
70
+
71
+ // 2. Visibility Filtering
72
+ const visibleCandidates = candidates.filter((c) => this.isElementVisible(c));
73
+ if (visibleCandidates.length === 1) {
74
+ const candidate = visibleCandidates[0]!;
75
+ const baseExpr = CandidateExtractor.generateLocator(candidate, pageProperty);
76
+ const resolvedExpression = `${baseExpr}.filter({ visible: true })`;
77
+ return {
78
+ strategy: 'VISIBILITY_FILTER',
79
+ resolvedCandidate: candidate,
80
+ resolvedExpression,
81
+ matchCount: candidates.length,
82
+ explanation: `Disambiguated by visibility: exactly 1 candidate is visible and interactive while ${candidates.length - 1} duplicate(s) are hidden or off-screen.`,
83
+ confidence: 0.95,
84
+ isUnique: true,
85
+ };
86
+ }
87
+
88
+ // 3. Container Anchoring
89
+ if (context.rootDom) {
90
+ const anchorResult = this.attemptContainerAnchoring(candidates, context, pageProperty);
91
+ if (anchorResult) {
92
+ return anchorResult;
93
+ }
94
+ }
95
+
96
+ // 4. Landmark / Section Scoping
97
+ if (context.rootDom && (context.originalSelector || context.testIntentText)) {
98
+ const landmarkResult = this.attemptLandmarkScoping(candidates, context, pageProperty);
99
+ if (landmarkResult) {
100
+ return landmarkResult;
101
+ }
102
+ }
103
+
104
+ // 5. Explicit Positional Intent Verification
105
+ const hasExplicitPositionalIntent = this.verifyExplicitPositionalIntent(context);
106
+ if (hasExplicitPositionalIntent) {
107
+ const candidate = candidates[0]!;
108
+ const baseExpr = CandidateExtractor.generateLocator(candidate, pageProperty);
109
+ const positionalMethod = hasExplicitPositionalIntent.method; // '.first()', '.last()', or '.nth(N)'
110
+ const resolvedExpression = `${baseExpr}${positionalMethod}`;
111
+ return {
112
+ strategy: 'EXPLICIT_POSITIONAL_INTENT',
113
+ resolvedCandidate: candidate,
114
+ resolvedExpression,
115
+ matchCount: candidates.length,
116
+ explanation: `Verified explicit positional intent: original test or requirement explicitly specified ${positionalMethod}. Positional selection preserved with verified evidence.`,
117
+ confidence: 0.90,
118
+ isUnique: true,
119
+ };
120
+ }
121
+
122
+ // 6. Safe Abstention (Never invent positional intent!)
123
+ return {
124
+ strategy: 'ABSTAIN_AMBIGUOUS',
125
+ matchCount: candidates.length,
126
+ explanation: `NON_UNIQUE_MATCH: Locator matches ${candidates.length} elements. Disambiguation failed across visibility filtering, container anchoring, and landmark scoping. In accordance with PIE safety invariants, positional intent (.first()/.nth()) is NOT invented. Safe abstention enforced.`,
127
+ confidence: 0,
128
+ isUnique: false,
129
+ };
130
+ }
131
+
132
+ // ---------------------------------------------------------------------------
133
+ // Internal Resolution Strategies
134
+ // ---------------------------------------------------------------------------
135
+
136
+ private static isElementVisible(el: ResolvedElement): boolean {
137
+ if (el.attributes['hidden'] === 'true' || el.attributes['hidden'] !== undefined) {
138
+ return false;
139
+ }
140
+ if (el.attributes['aria-hidden'] === 'true') {
141
+ return false;
142
+ }
143
+ const style = (el.attributes['style'] ?? '').toLowerCase();
144
+ if (style.includes('display: none') || style.includes('visibility: hidden') || style.includes('opacity: 0')) {
145
+ return false;
146
+ }
147
+ return true;
148
+ }
149
+
150
+ private static attemptContainerAnchoring(
151
+ candidates: ResolvedElement[],
152
+ context: DisambiguationContext,
153
+ pageProperty: string
154
+ ): DisambiguationResult | null {
155
+ if (!context.rootDom) return null;
156
+
157
+ // For each candidate, find its parent container chain
158
+ const candidatePaths = candidates.map((c) => this.findPathToElement(context.rootDom!, c));
159
+
160
+ // Try finding the closest enclosing container: tr, li, article, card
161
+ for (let cIdx = 0; cIdx < candidates.length; cIdx++) {
162
+ const path = candidatePaths[cIdx];
163
+ if (!path || path.length < 2) continue;
164
+
165
+ // Search bottom-up for a container
166
+ for (let i = path.length - 2; i >= 0; i--) {
167
+ const container = path[i]!;
168
+ const isContainer =
169
+ container.tag === 'tr' ||
170
+ container.tag === 'li' ||
171
+ container.tag === 'article' ||
172
+ container.tag === 'section' ||
173
+ (container.attributes['data-testid'] ?? '').includes('card') ||
174
+ (container.attributes['data-testid'] ?? '').includes('item') ||
175
+ (container.attributes['class'] ?? '').includes('card') ||
176
+ (container.attributes['class'] ?? '').includes('item') ||
177
+ container.role === 'row' ||
178
+ container.role === 'listitem';
179
+
180
+ if (isContainer) {
181
+ // Check distinguishing text inside this container
182
+ const containerText = this.getCleanText(container);
183
+ if (containerText.length > 2 && containerText.length < 80) {
184
+ // Check if other candidates' containers have different text
185
+ const otherContainersHaveText = candidatePaths.some((otherPath, oIdx) => {
186
+ if (oIdx === cIdx || !otherPath) return false;
187
+ const otherContainer = otherPath[i];
188
+ if (!otherContainer) return false;
189
+ return this.getCleanText(otherContainer).includes(containerText);
190
+ });
191
+
192
+ // Distinguishing text must NOT appear in other candidates' containers
193
+ if (!otherContainersHaveText) {
194
+ const candidate = candidates[cIdx]!;
195
+ const innerPlan = CandidateExtractor.generateLocatorPlan(candidate);
196
+ const innerExpr = CandidateExtractor.planToExpression(innerPlan, '').replace(/^\./, '');
197
+ const containerTag = container.attributes['data-testid']
198
+ ? `[data-testid="${container.attributes['data-testid']}"]`
199
+ : container.tag;
200
+ const escapedText = CandidateExtractor.escapeLocatorString(containerText);
201
+ const resolvedExpression = `${pageProperty}.locator('${containerTag}').filter({ hasText: '${escapedText}' }).${innerExpr}`;
202
+
203
+ return {
204
+ strategy: 'CONTAINER_ANCHOR',
205
+ resolvedCandidate: candidate,
206
+ resolvedExpression,
207
+ scopedContainer: container,
208
+ containerAnchorText: containerText,
209
+ matchCount: candidates.length,
210
+ explanation: `Disambiguated via container anchoring: target is uniquely scoped within <${container.tag}> anchored by distinguishing text '${containerText}'.`,
211
+ confidence: 0.92,
212
+ isUnique: true,
213
+ };
214
+ }
215
+ }
216
+ }
217
+ }
218
+ }
219
+
220
+ return null;
221
+ }
222
+
223
+ private static attemptLandmarkScoping(
224
+ candidates: ResolvedElement[],
225
+ context: DisambiguationContext,
226
+ pageProperty: string
227
+ ): DisambiguationResult | null {
228
+ if (!context.rootDom) return null;
229
+
230
+ const landmarks = ['header', 'nav', 'main', 'footer', 'aside', 'dialog'];
231
+ const textToCheck = `${context.originalSelector ?? ''} ${context.testIntentText ?? ''}`.toLowerCase();
232
+
233
+ for (const landmark of landmarks) {
234
+ if (textToCheck.includes(landmark)) {
235
+ // Collect all candidates residing inside this landmark
236
+ const candidatesInLandmark = candidates.filter((candidate) => {
237
+ const path = this.findPathToElement(context.rootDom!, candidate);
238
+ return Boolean(path && path.some((el) => el.tag === landmark || el.role === landmark));
239
+ });
240
+
241
+ // ONLY when exactly one candidate is inside this landmark is it unique
242
+ if (candidatesInLandmark.length === 1) {
243
+ const candidate = candidatesInLandmark[0]!;
244
+ const innerPlan = CandidateExtractor.generateLocatorPlan(candidate);
245
+ const innerExpr = CandidateExtractor.planToExpression(innerPlan, '').replace(/^\./, '');
246
+ const resolvedExpression = `${pageProperty}.locator('${landmark}').${innerExpr}`;
247
+
248
+ return {
249
+ strategy: 'LANDMARK_SECTION',
250
+ resolvedCandidate: candidate,
251
+ resolvedExpression,
252
+ matchCount: candidates.length,
253
+ explanation: `Disambiguated via landmark scoping: exactly one target candidate scoped to <${landmark}> based on context.`,
254
+ confidence: 0.88,
255
+ isUnique: true,
256
+ };
257
+ }
258
+ }
259
+ }
260
+
261
+ return null;
262
+ }
263
+
264
+ private static verifyExplicitPositionalIntent(
265
+ context: DisambiguationContext
266
+ ): { method: string; index: number } | null {
267
+ const sel = context.originalSelector ?? '';
268
+ const intent = (context.testIntentText ?? '').toLowerCase();
269
+
270
+ // 1. Check original selector method calls
271
+ if (sel.includes('.first()')) {
272
+ return { method: '.first()', index: 0 };
273
+ }
274
+ if (sel.includes('.last()')) {
275
+ return { method: '.last()', index: -1 };
276
+ }
277
+ const nthMatch = sel.match(/\.nth\((\d+)\)/);
278
+ if (nthMatch && nthMatch[1] !== undefined) {
279
+ const idx = parseInt(nthMatch[1], 10);
280
+ return { method: `.nth(${idx})`, index: idx };
281
+ }
282
+
283
+ // 2. Check explicit test intent text
284
+ if (intent.includes('first ') || intent.includes('1st ') || intent.includes('top item') || intent.includes('top result')) {
285
+ return { method: '.first()', index: 0 };
286
+ }
287
+ if (intent.includes('second ') || intent.includes('2nd ')) {
288
+ return { method: '.nth(1)', index: 1 };
289
+ }
290
+ if (intent.includes('third ') || intent.includes('3rd ')) {
291
+ return { method: '.nth(2)', index: 2 };
292
+ }
293
+ if (intent.includes('last item') || intent.includes('last row')) {
294
+ return { method: '.last()', index: -1 };
295
+ }
296
+
297
+ return null;
298
+ }
299
+
300
+ private static findPathToElement(
301
+ root: ResolvedElement,
302
+ target: ResolvedElement,
303
+ currentPath: ResolvedElement[] = []
304
+ ): ResolvedElement[] | null {
305
+ const nextPath = [...currentPath, root];
306
+ if (root === target) {
307
+ return nextPath;
308
+ }
309
+
310
+ for (const child of root.children ?? []) {
311
+ const found = this.findPathToElement(child, target, nextPath);
312
+ if (found) return found;
313
+ }
314
+
315
+ return null;
316
+ }
317
+
318
+ private static getCleanText(el: ResolvedElement): string {
319
+ if (el.text && el.text.trim().length > 0) {
320
+ return el.text.trim();
321
+ }
322
+ // Collect child text
323
+ const childTexts: string[] = [];
324
+ for (const child of el.children ?? []) {
325
+ if (child.text && child.text.trim().length > 0) {
326
+ childTexts.push(child.text.trim());
327
+ }
328
+ }
329
+ return childTexts.join(' ').trim();
330
+ }
331
+ }
@@ -0,0 +1,205 @@
1
+ export type ModelTransport = 'openai-compatible' | 'anthropic-compatible' | 'local' | 'unknown';
2
+ export type StructuredOutputCapability = 'json_schema' | 'json_object' | 'prompt_only' | 'unknown';
3
+ export type ReasoningCapability = 'separate_channel' | 'inline' | 'unsupported' | 'unknown';
4
+ export type ToolCallCapability = 'native' | 'application_protocol' | 'unsupported' | 'unknown';
5
+
6
+ export interface ModelCapabilities {
7
+ transport: ModelTransport;
8
+ structuredOutput: StructuredOutputCapability;
9
+ reasoning: ReasoningCapability;
10
+ toolCalls: ToolCallCapability;
11
+ maxOutputTokens?: number | undefined;
12
+ }
13
+
14
+ export interface ModelUsageEnvelope {
15
+ inputTokens?: number | undefined;
16
+ outputTokens?: number | undefined;
17
+ totalTokens?: number | undefined;
18
+ }
19
+
20
+ /** Provider-neutral response shape consumed by PIE's agent protocol. */
21
+ export interface ModelResponseEnvelope {
22
+ finalText?: string | undefined;
23
+ reasoningText?: string | undefined;
24
+ structuredOutput?: unknown;
25
+ toolCalls?: unknown[] | undefined;
26
+ usage?: ModelUsageEnvelope | undefined;
27
+ finishReason?: string | undefined;
28
+ transport: ModelTransport;
29
+ }
30
+
31
+ export type ModelRequestKind = 'proposal' | 'agent_step';
32
+
33
+ export interface ModelResponseDiagnostics {
34
+ requestKind: ModelRequestKind;
35
+ transport: ModelTransport;
36
+ topLevelKeys: string[];
37
+ choiceKeys: string[];
38
+ messageKeys: string[];
39
+ contentKind: 'string' | 'array' | 'missing' | 'null';
40
+ hasFinalText: boolean;
41
+ finalTextLength: number;
42
+ reasoningTextLength: number;
43
+ toolCallCount: number;
44
+ finishReason?: string | undefined;
45
+ usagePresent: boolean;
46
+ candidateKeys: string[];
47
+ candidateResponseType?: string | undefined;
48
+ candidateStatus?: string | undefined;
49
+ candidateDecision?: string | undefined;
50
+ candidateActionKind?: string | undefined;
51
+ candidateProposalVersion?: string | undefined;
52
+ /** Transport metadata keys recognized and removed before protocol validation. */
53
+ recognizedResponseMetadataKeys: string[];
54
+ validationErrors: string[];
55
+ normalizationNotes: string[];
56
+ redactedPreview?: string | undefined;
57
+ }
58
+
59
+ export interface OpenAiCompatibleResponsePayload {
60
+ choices?: Array<{
61
+ finish_reason?: string;
62
+ message?: {
63
+ content?: string | Array<{ type?: string; text?: string }> | null;
64
+ reasoning_content?: string | null;
65
+ tool_calls?: unknown[];
66
+ };
67
+ }>;
68
+ output_text?: string;
69
+ usage?: ModelUsageEnvelope & {
70
+ prompt_tokens?: number;
71
+ completion_tokens?: number;
72
+ total_tokens?: number;
73
+ };
74
+ }
75
+
76
+ export interface AnthropicResponsePayload {
77
+ content?: Array<{ type?: string; text?: string }>;
78
+ usage?: { input_tokens?: number; output_tokens?: number };
79
+ stop_reason?: string;
80
+ }
81
+
82
+ export function normalizeOpenAiResponse(payload: OpenAiCompatibleResponsePayload): ModelResponseEnvelope {
83
+ const message = payload.choices?.[0]?.message;
84
+ const content = message?.content;
85
+ const finalText = typeof payload.output_text === 'string'
86
+ ? payload.output_text
87
+ : typeof content === 'string'
88
+ ? content
89
+ : Array.isArray(content)
90
+ ? content.map((part) => part.text ?? '').join('')
91
+ : undefined;
92
+ const inputTokens = payload.usage?.inputTokens ?? payload.usage?.prompt_tokens;
93
+ const outputTokens = payload.usage?.outputTokens ?? payload.usage?.completion_tokens;
94
+ const totalTokens = payload.usage?.totalTokens ?? payload.usage?.total_tokens
95
+ ?? (inputTokens === undefined && outputTokens === undefined ? undefined : (inputTokens ?? 0) + (outputTokens ?? 0));
96
+ return {
97
+ finalText: finalText || undefined,
98
+ reasoningText: message?.reasoning_content ?? undefined,
99
+ toolCalls: message?.tool_calls,
100
+ usage: inputTokens === undefined && outputTokens === undefined && totalTokens === undefined
101
+ ? undefined
102
+ : { inputTokens, outputTokens, totalTokens },
103
+ finishReason: payload.choices?.[0]?.finish_reason,
104
+ transport: 'openai-compatible',
105
+ };
106
+ }
107
+
108
+ export function normalizeAnthropicResponse(payload: AnthropicResponsePayload): ModelResponseEnvelope {
109
+ const textParts = payload.content?.filter((part) => part.type === 'text').map((part) => part.text ?? '') ?? [];
110
+ const reasoningParts = payload.content?.filter((part) => part.type === 'thinking').map((part) => part.text ?? '') ?? [];
111
+ const inputTokens = payload.usage?.input_tokens;
112
+ const outputTokens = payload.usage?.output_tokens;
113
+ return {
114
+ finalText: textParts.join('') || undefined,
115
+ reasoningText: reasoningParts.join('') || undefined,
116
+ usage: inputTokens === undefined && outputTokens === undefined
117
+ ? undefined
118
+ : { inputTokens, outputTokens, totalTokens: (inputTokens ?? 0) + (outputTokens ?? 0) },
119
+ finishReason: payload.stop_reason,
120
+ transport: 'anthropic-compatible',
121
+ };
122
+ }
123
+
124
+ function redactPreview(value: string): string {
125
+ return value
126
+ .replace(/Bearer\s+[A-Za-z0-9._-]+/gi, 'Bearer [REDACTED]')
127
+ .replace(/(api[_-]?key|token|password|secret)\s*[:=]\s*["']?[^\s,"'}]+/gi, '$1=[REDACTED]')
128
+ .slice(0, 1000);
129
+ }
130
+
131
+ interface CandidateInspection {
132
+ keys: string[];
133
+ responseType?: string | undefined;
134
+ status?: string | undefined;
135
+ decision?: string | undefined;
136
+ actionKind?: string | undefined;
137
+ proposalVersion?: string | undefined;
138
+ }
139
+
140
+ function inspectCandidate(text: string | undefined): CandidateInspection {
141
+ if (!text) return { keys: [] };
142
+ const fenced = text.trim().match(/```(?:json)?\s*([\s\S]*?)\s*```/i)?.[1];
143
+ const candidateText = (fenced ?? text).trim();
144
+ const start = candidateText.indexOf('{');
145
+ const end = candidateText.lastIndexOf('}');
146
+ if (start < 0 || end <= start) return { keys: [] };
147
+ try {
148
+ const candidate = JSON.parse(candidateText.slice(start, end + 1)) as Record<string, unknown>;
149
+ return {
150
+ keys: Object.keys(candidate),
151
+ responseType: typeof candidate.responseType === 'string' ? candidate.responseType : undefined,
152
+ status: typeof candidate.status === 'string' ? candidate.status : undefined,
153
+ decision: typeof candidate.decision === 'string' ? candidate.decision : undefined,
154
+ actionKind: typeof (candidate.action as Record<string, unknown> | undefined)?.kind === 'string'
155
+ ? String((candidate.action as Record<string, unknown>).kind)
156
+ : undefined,
157
+ proposalVersion: typeof candidate.proposalVersion === 'string' ? candidate.proposalVersion : undefined,
158
+ };
159
+ } catch {
160
+ return { keys: [] };
161
+ }
162
+ }
163
+
164
+ export function createModelResponseDiagnostics(
165
+ payload: unknown,
166
+ envelope: ModelResponseEnvelope,
167
+ requestKind: ModelRequestKind,
168
+ validationErrors: string[] = [],
169
+ normalizationNotes: string[] = [],
170
+ recognizedResponseMetadataKeys: string[] = [],
171
+ includePreview = false,
172
+ ): ModelResponseDiagnostics {
173
+ const record = payload && typeof payload === 'object' ? payload as Record<string, unknown> : {};
174
+ const choices = Array.isArray(record.choices) ? record.choices : [];
175
+ const firstChoice = choices[0] && typeof choices[0] === 'object' ? choices[0] as Record<string, unknown> : {};
176
+ const message = firstChoice.message && typeof firstChoice.message === 'object' ? firstChoice.message as Record<string, unknown> : {};
177
+ const content = message.content;
178
+ const contentKind = content === null ? 'null' : typeof content === 'string' ? 'string' : Array.isArray(content) ? 'array' : content === undefined ? 'missing' : 'missing';
179
+ const candidate = inspectCandidate(envelope.finalText);
180
+ const diagnostics: ModelResponseDiagnostics = {
181
+ requestKind,
182
+ transport: envelope.transport,
183
+ topLevelKeys: Object.keys(record),
184
+ choiceKeys: Object.keys(firstChoice),
185
+ messageKeys: Object.keys(message),
186
+ contentKind,
187
+ hasFinalText: Boolean(envelope.finalText),
188
+ finalTextLength: envelope.finalText?.length ?? 0,
189
+ reasoningTextLength: envelope.reasoningText?.length ?? 0,
190
+ toolCallCount: envelope.toolCalls?.length ?? 0,
191
+ finishReason: envelope.finishReason,
192
+ usagePresent: envelope.usage !== undefined,
193
+ candidateKeys: candidate.keys,
194
+ candidateResponseType: candidate.responseType,
195
+ candidateStatus: candidate.status,
196
+ candidateDecision: candidate.decision,
197
+ candidateActionKind: candidate.actionKind,
198
+ candidateProposalVersion: candidate.proposalVersion,
199
+ recognizedResponseMetadataKeys,
200
+ validationErrors,
201
+ normalizationNotes,
202
+ };
203
+ if (includePreview && envelope.finalText) diagnostics.redactedPreview = redactPreview(envelope.finalText);
204
+ return diagnostics;
205
+ }