@avi770/testteam 1.2.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 (325) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/LICENSE +21 -0
  3. package/README.md +167 -0
  4. package/agents/01-analyst.ts +100 -0
  5. package/agents/02-seed-architect.ts +59 -0
  6. package/agents/03-test-generator.ts +191 -0
  7. package/agents/04-unit-runner.ts +160 -0
  8. package/agents/05-browser-crawler.ts +790 -0
  9. package/agents/06-api-exerciser.ts +311 -0
  10. package/agents/07-security-scout.ts +188 -0
  11. package/agents/08-a11y-guardian.ts +212 -0
  12. package/agents/09-healer.ts +228 -0
  13. package/agents/10-reporter.ts +266 -0
  14. package/agents/11-fixer.ts +253 -0
  15. package/agents/12-ux-inspector.ts +444 -0
  16. package/agents/13-performance-profiler.ts +271 -0
  17. package/agents/14-data-integrity-auditor.ts +417 -0
  18. package/agents/15-regression-sentinel.ts +307 -0
  19. package/agents/16-chaos-agent.ts +228 -0
  20. package/agents/17-documentation-validator.ts +266 -0
  21. package/agents/18-integration-watchdog.ts +178 -0
  22. package/agents/19-tenant-isolation-auditor.ts +199 -0
  23. package/agents/20-workflow-completion-tester.ts +203 -0
  24. package/agents/21-state-session-tester.ts +262 -0
  25. package/agents/22-email-notification-verifier.ts +244 -0
  26. package/agents/23-migration-tester.ts +80 -0
  27. package/agents/__tests__/01-analyst.test.ts +188 -0
  28. package/agents/__tests__/02-seed-architect.test.ts +152 -0
  29. package/agents/__tests__/03-test-generator-full.test.ts +321 -0
  30. package/agents/__tests__/03-test-generator.test.ts +318 -0
  31. package/agents/__tests__/04-unit-runner.test.ts +320 -0
  32. package/agents/__tests__/05-browser-crawler-beta.test.ts +492 -0
  33. package/agents/__tests__/05-browser-crawler-release.test.ts +412 -0
  34. package/agents/__tests__/05-browser-crawler-uat.test.ts +578 -0
  35. package/agents/__tests__/05-browser-crawler.test.ts +518 -0
  36. package/agents/__tests__/06-api-exerciser.test.ts +619 -0
  37. package/agents/__tests__/07-security-scout.test.ts +382 -0
  38. package/agents/__tests__/08-a11y-guardian.test.ts +530 -0
  39. package/agents/__tests__/09-healer.test.ts +384 -0
  40. package/agents/__tests__/10-reporter.test.ts +366 -0
  41. package/agents/__tests__/11-fixer.test.ts +406 -0
  42. package/agents/__tests__/12-ux-inspector-extended.test.ts +465 -0
  43. package/agents/__tests__/12-ux-inspector.test.ts +443 -0
  44. package/agents/__tests__/13-performance-profiler.test.ts +411 -0
  45. package/agents/__tests__/14-data-integrity-auditor-extended.test.ts +573 -0
  46. package/agents/__tests__/14-data-integrity-auditor.test.ts +407 -0
  47. package/agents/__tests__/15-regression-sentinel.test.ts +657 -0
  48. package/agents/__tests__/16-chaos-agent.test.ts +427 -0
  49. package/agents/__tests__/17-documentation-validator.test.ts +402 -0
  50. package/agents/__tests__/18-integration-watchdog.test.ts +263 -0
  51. package/agents/__tests__/19-tenant-isolation-auditor.test.ts +400 -0
  52. package/agents/__tests__/20-workflow-completion-tester.test.ts +586 -0
  53. package/agents/__tests__/21-state-session-tester.test.ts +374 -0
  54. package/agents/__tests__/22-email-notification-verifier.test.ts +441 -0
  55. package/agents/__tests__/23-migration-tester.test.ts +145 -0
  56. package/agents/__tests__/base-agent.test.ts +188 -0
  57. package/agents/__tests__/registry.test.ts +218 -0
  58. package/agents/base-agent.ts +77 -0
  59. package/agents/registry.ts +136 -0
  60. package/baselines/api-schemas/.gitkeep +0 -0
  61. package/baselines/performance/.gitkeep +0 -0
  62. package/baselines/screenshots/.gitkeep +0 -0
  63. package/bin/testteam.js +10 -0
  64. package/core/__tests__/ci-output.test.ts +430 -0
  65. package/core/__tests__/cli.test.ts +387 -0
  66. package/core/__tests__/config.test.ts +78 -0
  67. package/core/__tests__/cost-tracker.test.ts +158 -0
  68. package/core/__tests__/evidence.test.ts +265 -0
  69. package/core/__tests__/fix-loop.test.ts +210 -0
  70. package/core/__tests__/health-check.test.ts +44 -0
  71. package/core/__tests__/init.test.ts +609 -0
  72. package/core/__tests__/integration.test.ts +204 -0
  73. package/core/__tests__/license-gen.test.ts +227 -0
  74. package/core/__tests__/license.test.ts +326 -0
  75. package/core/__tests__/multi-browser.test.ts +278 -0
  76. package/core/__tests__/orchestrator.test.ts +519 -0
  77. package/core/__tests__/phase-gate.test.ts +43 -0
  78. package/core/__tests__/report-html.test.ts +398 -0
  79. package/core/__tests__/report-upload.test.ts +325 -0
  80. package/core/__tests__/run-counter.test.ts +234 -0
  81. package/core/ci-output.ts +240 -0
  82. package/core/cli.ts +232 -0
  83. package/core/config.ts +178 -0
  84. package/core/cost-tracker.ts +59 -0
  85. package/core/evidence.ts +132 -0
  86. package/core/fix-loop.ts +85 -0
  87. package/core/health-check.ts +54 -0
  88. package/core/init.ts +546 -0
  89. package/core/license-gen.ts +212 -0
  90. package/core/license.ts +211 -0
  91. package/core/messages.ts +67 -0
  92. package/core/multi-browser.ts +136 -0
  93. package/core/orchestrator.ts +354 -0
  94. package/core/phase-gate.ts +55 -0
  95. package/core/report-html.ts +657 -0
  96. package/core/report-upload.ts +188 -0
  97. package/core/run-counter.ts +175 -0
  98. package/core/types.ts +57 -0
  99. package/dist/agents/01-analyst.d.ts +11 -0
  100. package/dist/agents/01-analyst.d.ts.map +1 -0
  101. package/dist/agents/01-analyst.js +75 -0
  102. package/dist/agents/01-analyst.js.map +1 -0
  103. package/dist/agents/02-seed-architect.d.ts +11 -0
  104. package/dist/agents/02-seed-architect.d.ts.map +1 -0
  105. package/dist/agents/02-seed-architect.js +51 -0
  106. package/dist/agents/02-seed-architect.js.map +1 -0
  107. package/dist/agents/03-test-generator.d.ts +9 -0
  108. package/dist/agents/03-test-generator.d.ts.map +1 -0
  109. package/dist/agents/03-test-generator.js +167 -0
  110. package/dist/agents/03-test-generator.js.map +1 -0
  111. package/dist/agents/04-unit-runner.d.ts +9 -0
  112. package/dist/agents/04-unit-runner.d.ts.map +1 -0
  113. package/dist/agents/04-unit-runner.js +113 -0
  114. package/dist/agents/04-unit-runner.js.map +1 -0
  115. package/dist/agents/05-browser-crawler.d.ts +30 -0
  116. package/dist/agents/05-browser-crawler.d.ts.map +1 -0
  117. package/dist/agents/05-browser-crawler.js +685 -0
  118. package/dist/agents/05-browser-crawler.js.map +1 -0
  119. package/dist/agents/06-api-exerciser.d.ts +23 -0
  120. package/dist/agents/06-api-exerciser.d.ts.map +1 -0
  121. package/dist/agents/06-api-exerciser.js +253 -0
  122. package/dist/agents/06-api-exerciser.js.map +1 -0
  123. package/dist/agents/07-security-scout.d.ts +11 -0
  124. package/dist/agents/07-security-scout.d.ts.map +1 -0
  125. package/dist/agents/07-security-scout.js +142 -0
  126. package/dist/agents/07-security-scout.js.map +1 -0
  127. package/dist/agents/08-a11y-guardian.d.ts +13 -0
  128. package/dist/agents/08-a11y-guardian.d.ts.map +1 -0
  129. package/dist/agents/08-a11y-guardian.js +176 -0
  130. package/dist/agents/08-a11y-guardian.js.map +1 -0
  131. package/dist/agents/09-healer.d.ts +33 -0
  132. package/dist/agents/09-healer.d.ts.map +1 -0
  133. package/dist/agents/09-healer.js +167 -0
  134. package/dist/agents/09-healer.js.map +1 -0
  135. package/dist/agents/10-reporter.d.ts +26 -0
  136. package/dist/agents/10-reporter.d.ts.map +1 -0
  137. package/dist/agents/10-reporter.js +215 -0
  138. package/dist/agents/10-reporter.js.map +1 -0
  139. package/dist/agents/11-fixer.d.ts +26 -0
  140. package/dist/agents/11-fixer.d.ts.map +1 -0
  141. package/dist/agents/11-fixer.js +195 -0
  142. package/dist/agents/11-fixer.js.map +1 -0
  143. package/dist/agents/12-ux-inspector.d.ts +15 -0
  144. package/dist/agents/12-ux-inspector.d.ts.map +1 -0
  145. package/dist/agents/12-ux-inspector.js +364 -0
  146. package/dist/agents/12-ux-inspector.js.map +1 -0
  147. package/dist/agents/13-performance-profiler.d.ts +13 -0
  148. package/dist/agents/13-performance-profiler.d.ts.map +1 -0
  149. package/dist/agents/13-performance-profiler.js +216 -0
  150. package/dist/agents/13-performance-profiler.js.map +1 -0
  151. package/dist/agents/14-data-integrity-auditor.d.ts +12 -0
  152. package/dist/agents/14-data-integrity-auditor.d.ts.map +1 -0
  153. package/dist/agents/14-data-integrity-auditor.js +356 -0
  154. package/dist/agents/14-data-integrity-auditor.js.map +1 -0
  155. package/dist/agents/15-regression-sentinel.d.ts +25 -0
  156. package/dist/agents/15-regression-sentinel.d.ts.map +1 -0
  157. package/dist/agents/15-regression-sentinel.js +251 -0
  158. package/dist/agents/15-regression-sentinel.js.map +1 -0
  159. package/dist/agents/16-chaos-agent.d.ts +9 -0
  160. package/dist/agents/16-chaos-agent.d.ts.map +1 -0
  161. package/dist/agents/16-chaos-agent.js +207 -0
  162. package/dist/agents/16-chaos-agent.js.map +1 -0
  163. package/dist/agents/17-documentation-validator.d.ts +31 -0
  164. package/dist/agents/17-documentation-validator.d.ts.map +1 -0
  165. package/dist/agents/17-documentation-validator.js +246 -0
  166. package/dist/agents/17-documentation-validator.js.map +1 -0
  167. package/dist/agents/18-integration-watchdog.d.ts +10 -0
  168. package/dist/agents/18-integration-watchdog.d.ts.map +1 -0
  169. package/dist/agents/18-integration-watchdog.js +138 -0
  170. package/dist/agents/18-integration-watchdog.js.map +1 -0
  171. package/dist/agents/19-tenant-isolation-auditor.d.ts +9 -0
  172. package/dist/agents/19-tenant-isolation-auditor.d.ts.map +1 -0
  173. package/dist/agents/19-tenant-isolation-auditor.js +166 -0
  174. package/dist/agents/19-tenant-isolation-auditor.js.map +1 -0
  175. package/dist/agents/20-workflow-completion-tester.d.ts +12 -0
  176. package/dist/agents/20-workflow-completion-tester.d.ts.map +1 -0
  177. package/dist/agents/20-workflow-completion-tester.js +159 -0
  178. package/dist/agents/20-workflow-completion-tester.js.map +1 -0
  179. package/dist/agents/21-state-session-tester.d.ts +10 -0
  180. package/dist/agents/21-state-session-tester.d.ts.map +1 -0
  181. package/dist/agents/21-state-session-tester.js +233 -0
  182. package/dist/agents/21-state-session-tester.js.map +1 -0
  183. package/dist/agents/22-email-notification-verifier.d.ts +11 -0
  184. package/dist/agents/22-email-notification-verifier.d.ts.map +1 -0
  185. package/dist/agents/22-email-notification-verifier.js +199 -0
  186. package/dist/agents/22-email-notification-verifier.js.map +1 -0
  187. package/dist/agents/23-migration-tester.d.ts +10 -0
  188. package/dist/agents/23-migration-tester.d.ts.map +1 -0
  189. package/dist/agents/23-migration-tester.js +74 -0
  190. package/dist/agents/23-migration-tester.js.map +1 -0
  191. package/dist/agents/base-agent.d.ts +19 -0
  192. package/dist/agents/base-agent.d.ts.map +1 -0
  193. package/dist/agents/base-agent.js +67 -0
  194. package/dist/agents/base-agent.js.map +1 -0
  195. package/dist/agents/registry.d.ts +29 -0
  196. package/dist/agents/registry.d.ts.map +1 -0
  197. package/dist/agents/registry.js +117 -0
  198. package/dist/agents/registry.js.map +1 -0
  199. package/dist/core/ci-output.d.ts +35 -0
  200. package/dist/core/ci-output.d.ts.map +1 -0
  201. package/dist/core/ci-output.js +193 -0
  202. package/dist/core/ci-output.js.map +1 -0
  203. package/dist/core/cli.d.ts +11 -0
  204. package/dist/core/cli.d.ts.map +1 -0
  205. package/dist/core/cli.js +197 -0
  206. package/dist/core/cli.js.map +1 -0
  207. package/dist/core/config.d.ts +111 -0
  208. package/dist/core/config.d.ts.map +1 -0
  209. package/dist/core/config.js +42 -0
  210. package/dist/core/config.js.map +1 -0
  211. package/dist/core/cost-tracker.d.ts +22 -0
  212. package/dist/core/cost-tracker.d.ts.map +1 -0
  213. package/dist/core/cost-tracker.js +41 -0
  214. package/dist/core/cost-tracker.js.map +1 -0
  215. package/dist/core/evidence.d.ts +28 -0
  216. package/dist/core/evidence.d.ts.map +1 -0
  217. package/dist/core/evidence.js +95 -0
  218. package/dist/core/evidence.js.map +1 -0
  219. package/dist/core/fix-loop.d.ts +29 -0
  220. package/dist/core/fix-loop.d.ts.map +1 -0
  221. package/dist/core/fix-loop.js +70 -0
  222. package/dist/core/fix-loop.js.map +1 -0
  223. package/dist/core/health-check.d.ts +21 -0
  224. package/dist/core/health-check.d.ts.map +1 -0
  225. package/dist/core/health-check.js +26 -0
  226. package/dist/core/health-check.js.map +1 -0
  227. package/dist/core/init.d.ts +2 -0
  228. package/dist/core/init.d.ts.map +1 -0
  229. package/dist/core/init.js +435 -0
  230. package/dist/core/init.js.map +1 -0
  231. package/dist/core/license-gen.d.ts +12 -0
  232. package/dist/core/license-gen.d.ts.map +1 -0
  233. package/dist/core/license-gen.js +169 -0
  234. package/dist/core/license-gen.js.map +1 -0
  235. package/dist/core/license.d.ts +33 -0
  236. package/dist/core/license.d.ts.map +1 -0
  237. package/dist/core/license.js +170 -0
  238. package/dist/core/license.js.map +1 -0
  239. package/dist/core/messages.d.ts +10 -0
  240. package/dist/core/messages.d.ts.map +1 -0
  241. package/dist/core/messages.js +47 -0
  242. package/dist/core/messages.js.map +1 -0
  243. package/dist/core/multi-browser.d.ts +36 -0
  244. package/dist/core/multi-browser.d.ts.map +1 -0
  245. package/dist/core/multi-browser.js +88 -0
  246. package/dist/core/multi-browser.js.map +1 -0
  247. package/dist/core/orchestrator.d.ts +48 -0
  248. package/dist/core/orchestrator.d.ts.map +1 -0
  249. package/dist/core/orchestrator.js +291 -0
  250. package/dist/core/orchestrator.js.map +1 -0
  251. package/dist/core/phase-gate.d.ts +4 -0
  252. package/dist/core/phase-gate.d.ts.map +1 -0
  253. package/dist/core/phase-gate.js +39 -0
  254. package/dist/core/phase-gate.js.map +1 -0
  255. package/dist/core/report-html.d.ts +9 -0
  256. package/dist/core/report-html.d.ts.map +1 -0
  257. package/dist/core/report-html.js +617 -0
  258. package/dist/core/report-html.js.map +1 -0
  259. package/dist/core/report-upload.d.ts +16 -0
  260. package/dist/core/report-upload.d.ts.map +1 -0
  261. package/dist/core/report-upload.js +124 -0
  262. package/dist/core/report-upload.js.map +1 -0
  263. package/dist/core/run-counter.d.ts +40 -0
  264. package/dist/core/run-counter.d.ts.map +1 -0
  265. package/dist/core/run-counter.js +120 -0
  266. package/dist/core/run-counter.js.map +1 -0
  267. package/dist/core/types.d.ts +53 -0
  268. package/dist/core/types.d.ts.map +1 -0
  269. package/dist/core/types.js +2 -0
  270. package/dist/core/types.js.map +1 -0
  271. package/dist/helpers/api-client.d.ts +30 -0
  272. package/dist/helpers/api-client.d.ts.map +1 -0
  273. package/dist/helpers/api-client.js +77 -0
  274. package/dist/helpers/api-client.js.map +1 -0
  275. package/dist/helpers/element-discovery.d.ts +18 -0
  276. package/dist/helpers/element-discovery.d.ts.map +1 -0
  277. package/dist/helpers/element-discovery.js +82 -0
  278. package/dist/helpers/element-discovery.js.map +1 -0
  279. package/dist/helpers/env-resolver.d.ts +29 -0
  280. package/dist/helpers/env-resolver.d.ts.map +1 -0
  281. package/dist/helpers/env-resolver.js +51 -0
  282. package/dist/helpers/env-resolver.js.map +1 -0
  283. package/dist/helpers/form-filler.d.ts +13 -0
  284. package/dist/helpers/form-filler.d.ts.map +1 -0
  285. package/dist/helpers/form-filler.js +98 -0
  286. package/dist/helpers/form-filler.js.map +1 -0
  287. package/dist/helpers/modal-handler.d.ts +16 -0
  288. package/dist/helpers/modal-handler.d.ts.map +1 -0
  289. package/dist/helpers/modal-handler.js +95 -0
  290. package/dist/helpers/modal-handler.js.map +1 -0
  291. package/dist/helpers/navigation.d.ts +37 -0
  292. package/dist/helpers/navigation.d.ts.map +1 -0
  293. package/dist/helpers/navigation.js +83 -0
  294. package/dist/helpers/navigation.js.map +1 -0
  295. package/dist/helpers/quality-gate.d.ts +17 -0
  296. package/dist/helpers/quality-gate.d.ts.map +1 -0
  297. package/dist/helpers/quality-gate.js +144 -0
  298. package/dist/helpers/quality-gate.js.map +1 -0
  299. package/dist/helpers/screenshot.d.ts +24 -0
  300. package/dist/helpers/screenshot.d.ts.map +1 -0
  301. package/dist/helpers/screenshot.js +76 -0
  302. package/dist/helpers/screenshot.js.map +1 -0
  303. package/dist/helpers/seed-validator.d.ts +15 -0
  304. package/dist/helpers/seed-validator.d.ts.map +1 -0
  305. package/dist/helpers/seed-validator.js +53 -0
  306. package/dist/helpers/seed-validator.js.map +1 -0
  307. package/helpers/__tests__/api-client.test.ts +199 -0
  308. package/helpers/__tests__/element-discovery.test.ts +202 -0
  309. package/helpers/__tests__/form-filler-extended.test.ts +212 -0
  310. package/helpers/__tests__/form-filler.test.ts +99 -0
  311. package/helpers/__tests__/modal-handler.test.ts +152 -0
  312. package/helpers/__tests__/navigation.test.ts +214 -0
  313. package/helpers/__tests__/quality-gate.test.ts +117 -0
  314. package/helpers/__tests__/screenshot.test.ts +139 -0
  315. package/helpers/__tests__/seed-validator.test.ts +114 -0
  316. package/helpers/api-client.ts +111 -0
  317. package/helpers/element-discovery.ts +105 -0
  318. package/helpers/env-resolver.ts +69 -0
  319. package/helpers/form-filler.ts +126 -0
  320. package/helpers/modal-handler.ts +108 -0
  321. package/helpers/navigation.ts +100 -0
  322. package/helpers/quality-gate.ts +180 -0
  323. package/helpers/screenshot.ts +111 -0
  324. package/helpers/seed-validator.ts +70 -0
  325. package/package.json +88 -0
@@ -0,0 +1,188 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { BaseAgent } from '../base-agent';
3
+ import type { Finding, Phase } from '../../core/types';
4
+ import type { ValidatedConfig } from '../../core/config';
5
+
6
+ const stubConfig = {
7
+ schemaVersion: 1,
8
+ name: 'test',
9
+ auth: { loginUrl: 'http://localhost' },
10
+ modules: [{ id: 'mod', route: '/mod', sidebarIcon: true }],
11
+ environments: {},
12
+ portals: [],
13
+ breakpoints: [],
14
+ integrations: [],
15
+ workers: [],
16
+ workflows: [],
17
+ accuracy: { enabled: false, decimalPrecision: 2, currencySymbol: '$', timezone: 'UTC' },
18
+ tenancy: { enabled: false, isolationField: 'firmId', testFirms: [] },
19
+ costBudget: { maxPerRun: 100, maxPerAgent: 10, alertThreshold: 0.8, onExceeded: 'abort' as const },
20
+ agentClassification: { blocking: [], advisory: [] },
21
+ thresholds: {
22
+ unitCoverage: { lines: 80, functions: 80, branches: 80, statements: 80 },
23
+ lighthouse: { accessibility: 90, performance: 90, bestPractices: 90, seo: 90 },
24
+ apiResponseTime: 2000,
25
+ bundleSizeLimit: 500000,
26
+ maxConsoleErrors: 0,
27
+ },
28
+ disabledAgents: [],
29
+ disabledAgentSet: new Set<number>(),
30
+ } satisfies ValidatedConfig;
31
+
32
+ class TestAgent extends BaseAgent {
33
+ readonly agentId = 99;
34
+ readonly agentName = 'test-agent';
35
+ preFlightFn: () => Promise<void> = async () => {};
36
+ executeFn: () => Promise<Finding[]> = async () => [];
37
+
38
+ protected async preFlight(): Promise<void> { await this.preFlightFn(); }
39
+ protected async execute(): Promise<Finding[]> { return this.executeFn(); }
40
+ }
41
+
42
+ function createAgent(phase: Phase = 'alpha'): TestAgent {
43
+ return new TestAgent(stubConfig, phase, '/tmp/test-run');
44
+ }
45
+
46
+ describe('BaseAgent', () => {
47
+ it('calls preFlight() then execute() in order', async () => {
48
+ const agent = createAgent();
49
+ const callOrder: string[] = [];
50
+ agent.preFlightFn = async () => { callOrder.push('preFlight'); };
51
+ agent.executeFn = async () => { callOrder.push('execute'); return []; };
52
+
53
+ await agent.run();
54
+
55
+ expect(callOrder).toEqual(['preFlight', 'execute']);
56
+ });
57
+
58
+ it('returns failed status with error when preFlight() throws', async () => {
59
+ const agent = createAgent();
60
+ agent.preFlightFn = async () => { throw new Error('pre-flight boom'); };
61
+
62
+ const result = await agent.run();
63
+
64
+ expect(result.status).toBe('failed');
65
+ expect(result.findings).toHaveLength(1);
66
+ expect(result.findings[0].severity).toBe('critical');
67
+ expect(result.findings[0].description).toBe('pre-flight boom');
68
+ expect(result.findings[0].type).toBe('infra-issue');
69
+ expect(result.summary).toContain('pre-flight boom');
70
+ });
71
+
72
+ it('returns failed status when execute() returns critical findings', async () => {
73
+ const agent = createAgent();
74
+ agent.executeFn = async () => [{
75
+ id: '99-crit',
76
+ type: 'code-bug-logic',
77
+ severity: 'critical',
78
+ agentId: 99,
79
+ module: 'test',
80
+ description: 'critical issue',
81
+ }];
82
+
83
+ const result = await agent.run();
84
+
85
+ expect(result.status).toBe('failed');
86
+ expect(result.findings).toHaveLength(1);
87
+ });
88
+
89
+ it('returns failed status when execute() returns high severity findings', async () => {
90
+ const agent = createAgent();
91
+ agent.executeFn = async () => [{
92
+ id: '99-high',
93
+ type: 'code-bug-logic',
94
+ severity: 'high',
95
+ agentId: 99,
96
+ module: 'test',
97
+ description: 'high issue',
98
+ }];
99
+
100
+ const result = await agent.run();
101
+
102
+ expect(result.status).toBe('failed');
103
+ });
104
+
105
+ it('returns passed status when execute() returns only low findings', async () => {
106
+ const agent = createAgent();
107
+ agent.executeFn = async () => [{
108
+ id: '99-low',
109
+ type: 'code-bug-cosmetic',
110
+ severity: 'low',
111
+ agentId: 99,
112
+ module: 'test',
113
+ description: 'minor issue',
114
+ }];
115
+
116
+ const result = await agent.run();
117
+
118
+ expect(result.status).toBe('passed');
119
+ expect(result.summary).toContain('1 findings');
120
+ expect(result.summary).toContain('1 low');
121
+ });
122
+
123
+ it('returns passed status when execute() returns only medium findings', async () => {
124
+ const agent = createAgent();
125
+ agent.executeFn = async () => [{
126
+ id: '99-med',
127
+ type: 'code-bug-cosmetic',
128
+ severity: 'medium',
129
+ agentId: 99,
130
+ module: 'test',
131
+ description: 'medium issue',
132
+ }];
133
+
134
+ const result = await agent.run();
135
+
136
+ expect(result.status).toBe('passed');
137
+ });
138
+
139
+ it('includes evidence items added during execution in the result', async () => {
140
+ const agent = createAgent();
141
+ agent.executeFn = async () => {
142
+ agent['addEvidence']({ type: 'log', path: '/tmp/log.txt', description: 'test log' });
143
+ agent['addEvidence']({ type: 'screenshot', path: '/tmp/shot.png', description: 'test shot' });
144
+ return [];
145
+ };
146
+
147
+ const result = await agent.run();
148
+
149
+ expect(result.evidence).toHaveLength(2);
150
+ expect(result.evidence[0].type).toBe('log');
151
+ expect(result.evidence[1].type).toBe('screenshot');
152
+ });
153
+
154
+ it('produces a clean summary when no findings exist', async () => {
155
+ const agent = createAgent();
156
+ const result = await agent.run();
157
+
158
+ expect(result.summary).toBe('test-agent: All checks passed');
159
+ });
160
+
161
+ it('captures non-Error throws in the error finding', async () => {
162
+ const agent = createAgent();
163
+ agent.preFlightFn = async () => { throw 'string-error'; };
164
+
165
+ const result = await agent.run();
166
+
167
+ expect(result.status).toBe('failed');
168
+ expect(result.findings[0].description).toBe('string-error');
169
+ expect(result.findings[0].stackTrace).toBeUndefined();
170
+ });
171
+
172
+ it('populates agentId, agentName, and phase correctly', async () => {
173
+ const agent = createAgent('beta');
174
+ const result = await agent.run();
175
+
176
+ expect(result.agentId).toBe(99);
177
+ expect(result.agentName).toBe('test-agent');
178
+ expect(result.phase).toBe('beta');
179
+ });
180
+
181
+ it('computes durationMs as a non-negative number', async () => {
182
+ const agent = createAgent();
183
+ const result = await agent.run();
184
+
185
+ expect(result.durationMs).toBeGreaterThanOrEqual(0);
186
+ expect(typeof result.durationMs).toBe('number');
187
+ });
188
+ });
@@ -0,0 +1,218 @@
1
+ import { describe, it, expect, vi } from 'vitest';
2
+ import {
3
+ AGENT_TIERS,
4
+ getAgentTier,
5
+ isAgentAvailable,
6
+ type LicenseTier,
7
+ } from '../registry';
8
+
9
+ describe('AGENT_TIERS', () => {
10
+ it('should contain exactly 23 agents', () => {
11
+ expect(Object.keys(AGENT_TIERS)).toHaveLength(23);
12
+ });
13
+
14
+ it('should have 8 free-tier agents', () => {
15
+ const freeAgents = Object.entries(AGENT_TIERS).filter(([, t]) => t === 'free');
16
+ expect(freeAgents).toHaveLength(8);
17
+ });
18
+
19
+ it('should have 9 pro-tier agents', () => {
20
+ const proAgents = Object.entries(AGENT_TIERS).filter(([, t]) => t === 'pro');
21
+ expect(proAgents).toHaveLength(9);
22
+ });
23
+
24
+ it('should have 6 enterprise-tier agents', () => {
25
+ const entAgents = Object.entries(AGENT_TIERS).filter(([, t]) => t === 'enterprise');
26
+ expect(entAgents).toHaveLength(6);
27
+ });
28
+
29
+ it('should classify known agents correctly', () => {
30
+ expect(AGENT_TIERS[1]).toBe('free'); // Analyst
31
+ expect(AGENT_TIERS[5]).toBe('free'); // Browser Crawler
32
+ expect(AGENT_TIERS[7]).toBe('pro'); // Security Scout
33
+ expect(AGENT_TIERS[13]).toBe('pro'); // Performance Profiler
34
+ expect(AGENT_TIERS[16]).toBe('enterprise'); // Chaos Agent
35
+ expect(AGENT_TIERS[19]).toBe('enterprise'); // Tenant Isolation Auditor
36
+ });
37
+ });
38
+
39
+ describe('getAgentTier', () => {
40
+ it('should return the correct tier for each agent', () => {
41
+ expect(getAgentTier(1)).toBe('free');
42
+ expect(getAgentTier(6)).toBe('pro');
43
+ expect(getAgentTier(14)).toBe('enterprise');
44
+ });
45
+
46
+ it('should throw for an unknown agent number', () => {
47
+ expect(() => getAgentTier(99)).toThrow('Unknown agent number: 99');
48
+ expect(() => getAgentTier(0)).toThrow('Unknown agent number: 0');
49
+ expect(() => getAgentTier(-1)).toThrow('Unknown agent number: -1');
50
+ });
51
+ });
52
+
53
+ describe('isAgentAvailable', () => {
54
+ const freeAgents = [1, 2, 3, 4, 5, 9, 10, 11];
55
+ const proAgents = [6, 7, 8, 12, 13, 15, 21, 22, 23];
56
+ const enterpriseAgents = [14, 16, 17, 18, 19, 20];
57
+
58
+ describe('free license', () => {
59
+ it('should allow free-tier agents', () => {
60
+ for (const id of freeAgents) {
61
+ expect(isAgentAvailable(id, 'free')).toBe(true);
62
+ }
63
+ });
64
+
65
+ it('should deny pro-tier agents', () => {
66
+ for (const id of proAgents) {
67
+ expect(isAgentAvailable(id, 'free')).toBe(false);
68
+ }
69
+ });
70
+
71
+ it('should deny enterprise-tier agents', () => {
72
+ for (const id of enterpriseAgents) {
73
+ expect(isAgentAvailable(id, 'free')).toBe(false);
74
+ }
75
+ });
76
+ });
77
+
78
+ describe('pro license', () => {
79
+ it('should allow free-tier agents', () => {
80
+ for (const id of freeAgents) {
81
+ expect(isAgentAvailable(id, 'pro')).toBe(true);
82
+ }
83
+ });
84
+
85
+ it('should allow pro-tier agents', () => {
86
+ for (const id of proAgents) {
87
+ expect(isAgentAvailable(id, 'pro')).toBe(true);
88
+ }
89
+ });
90
+
91
+ it('should deny enterprise-tier agents', () => {
92
+ for (const id of enterpriseAgents) {
93
+ expect(isAgentAvailable(id, 'pro')).toBe(false);
94
+ }
95
+ });
96
+ });
97
+
98
+ describe('enterprise license', () => {
99
+ it('should allow all agents', () => {
100
+ for (const id of [...freeAgents, ...proAgents, ...enterpriseAgents]) {
101
+ expect(isAgentAvailable(id, 'enterprise')).toBe(true);
102
+ }
103
+ });
104
+ });
105
+
106
+ it('should throw for unknown agent numbers', () => {
107
+ expect(() => isAgentAvailable(99, 'free')).toThrow('Unknown agent number: 99');
108
+ });
109
+ });
110
+
111
+ // ---------------------------------------------------------------------------
112
+ // registerAllAgents
113
+ // ---------------------------------------------------------------------------
114
+
115
+ describe('registerAllAgents', () => {
116
+ // We need to test that registerAllAgents creates 23 agents and registers them all.
117
+ // We mock the orchestrator's registerAgent method.
118
+
119
+ it('registers all 23 agents with the orchestrator', async () => {
120
+ // We need to dynamically import to avoid circular dependencies with mocked modules
121
+ // Instead, test the function signature by checking it registers the right count
122
+ const { registerAllAgents } = await import('../registry');
123
+
124
+ const mockOrchestrator = {
125
+ registerAgent: vi.fn(),
126
+ };
127
+
128
+ const stubConfig = {
129
+ schemaVersion: 1,
130
+ name: 'test',
131
+ auth: { loginUrl: '/login' },
132
+ modules: [],
133
+ environments: {},
134
+ portals: [],
135
+ breakpoints: [],
136
+ integrations: [],
137
+ workers: [],
138
+ workflows: [],
139
+ accuracy: { enabled: false, decimalPrecision: 2, currencySymbol: '$', timezone: 'UTC' },
140
+ tenancy: { enabled: false, isolationField: 'firmId', testFirms: [] },
141
+ costBudget: { maxPerRun: 100, maxPerAgent: 10, alertThreshold: 0.8, onExceeded: 'abort' as const },
142
+ agentClassification: { blocking: [], advisory: [] },
143
+ thresholds: {
144
+ unitCoverage: { lines: 80, functions: 80, branches: 80, statements: 80 },
145
+ lighthouse: { accessibility: 90, performance: 90, bestPractices: 90, seo: 90 },
146
+ apiResponseTime: 2000,
147
+ bundleSizeLimit: 500000,
148
+ maxConsoleErrors: 0,
149
+ },
150
+ disabledAgents: [],
151
+ disabledAgentSet: new Set<number>(),
152
+ };
153
+
154
+ registerAllAgents(
155
+ mockOrchestrator as any,
156
+ stubConfig as any,
157
+ 'alpha',
158
+ '/tmp/run',
159
+ );
160
+
161
+ expect(mockOrchestrator.registerAgent).toHaveBeenCalledTimes(23);
162
+
163
+ // Verify agent IDs 1-23 are all registered
164
+ const registeredIds = mockOrchestrator.registerAgent.mock.calls.map(
165
+ (call: unknown[]) => call[0],
166
+ );
167
+ for (let i = 1; i <= 23; i++) {
168
+ expect(registeredIds).toContain(i);
169
+ }
170
+ });
171
+
172
+ it('passes correct config, phase, and runDir to each agent', async () => {
173
+ const { registerAllAgents } = await import('../registry');
174
+
175
+ const mockOrchestrator = {
176
+ registerAgent: vi.fn(),
177
+ };
178
+
179
+ const stubConfig = {
180
+ schemaVersion: 1,
181
+ name: 'my-project',
182
+ auth: { loginUrl: '/login' },
183
+ modules: [],
184
+ environments: {},
185
+ portals: [],
186
+ breakpoints: [],
187
+ integrations: [],
188
+ workers: [],
189
+ workflows: [],
190
+ accuracy: { enabled: false, decimalPrecision: 2, currencySymbol: '$', timezone: 'UTC' },
191
+ tenancy: { enabled: false, isolationField: 'firmId', testFirms: [] },
192
+ costBudget: { maxPerRun: 100, maxPerAgent: 10, alertThreshold: 0.8, onExceeded: 'abort' as const },
193
+ agentClassification: { blocking: [], advisory: [] },
194
+ thresholds: {
195
+ unitCoverage: { lines: 80, functions: 80, branches: 80, statements: 80 },
196
+ lighthouse: { accessibility: 90, performance: 90, bestPractices: 90, seo: 90 },
197
+ apiResponseTime: 2000,
198
+ bundleSizeLimit: 500000,
199
+ maxConsoleErrors: 0,
200
+ },
201
+ disabledAgents: [],
202
+ disabledAgentSet: new Set<number>(),
203
+ };
204
+
205
+ registerAllAgents(
206
+ mockOrchestrator as any,
207
+ stubConfig as any,
208
+ 'beta',
209
+ '/tmp/my-run',
210
+ );
211
+
212
+ // Check that each registered agent has the right agentId property matching the first arg
213
+ for (const call of mockOrchestrator.registerAgent.mock.calls) {
214
+ const [agentId, agent] = call as [number, { agentId: number }];
215
+ expect(agent.agentId).toBe(agentId);
216
+ }
217
+ });
218
+ });
@@ -0,0 +1,77 @@
1
+ import type { AgentResult, Finding, Phase, EvidenceItem } from '../core/types';
2
+ import type { ValidatedConfig } from '../core/config';
3
+
4
+ export abstract class BaseAgent {
5
+ constructor(
6
+ protected readonly config: ValidatedConfig,
7
+ protected readonly phase: Phase,
8
+ protected readonly runDir: string,
9
+ ) {}
10
+
11
+ abstract readonly agentId: number;
12
+ abstract readonly agentName: string;
13
+
14
+ async run(): Promise<AgentResult> {
15
+ const startedAt = new Date().toISOString();
16
+ try {
17
+ await this.preFlight();
18
+ const findings = await this.execute();
19
+ const completedAt = new Date().toISOString();
20
+ return {
21
+ agentId: this.agentId,
22
+ agentName: this.agentName,
23
+ phase: this.phase,
24
+ status: findings.some(f => f.severity === 'critical' || f.severity === 'high') ? 'failed' : 'passed',
25
+ startedAt,
26
+ completedAt,
27
+ durationMs: new Date(completedAt).getTime() - new Date(startedAt).getTime(),
28
+ findings,
29
+ evidence: this.evidence,
30
+ summary: this.buildSummary(findings),
31
+ };
32
+ } catch (error) {
33
+ const completedAt = new Date().toISOString();
34
+ return {
35
+ agentId: this.agentId,
36
+ agentName: this.agentName,
37
+ phase: this.phase,
38
+ status: 'failed',
39
+ startedAt,
40
+ completedAt,
41
+ durationMs: new Date(completedAt).getTime() - new Date(startedAt).getTime(),
42
+ findings: [{
43
+ id: `${this.agentId}-error`,
44
+ type: 'infra-issue',
45
+ severity: 'critical',
46
+ agentId: this.agentId,
47
+ module: 'agent-runtime',
48
+ description: error instanceof Error ? error.message : String(error),
49
+ stackTrace: error instanceof Error ? error.stack : undefined,
50
+ }],
51
+ evidence: this.evidence,
52
+ summary: `Agent ${this.agentName} failed with error: ${error instanceof Error ? error.message : String(error)}`,
53
+ };
54
+ }
55
+ }
56
+
57
+ protected evidence: EvidenceItem[] = [];
58
+
59
+ /** AVI-OS pre-flight: audit dependencies, validate plan, check pre-conditions */
60
+ protected abstract preFlight(): Promise<void>;
61
+
62
+ /** Main execution logic -- single pass, no incremental fix-and-see */
63
+ protected abstract execute(): Promise<Finding[]>;
64
+
65
+ protected addEvidence(item: EvidenceItem): void {
66
+ this.evidence.push(item);
67
+ }
68
+
69
+ private buildSummary(findings: Finding[]): string {
70
+ if (findings.length === 0) return `${this.agentName}: All checks passed`;
71
+ const critical = findings.filter(f => f.severity === 'critical').length;
72
+ const high = findings.filter(f => f.severity === 'high').length;
73
+ const medium = findings.filter(f => f.severity === 'medium').length;
74
+ const low = findings.filter(f => f.severity === 'low').length;
75
+ return `${this.agentName}: ${findings.length} findings (${critical} critical, ${high} high, ${medium} medium, ${low} low)`;
76
+ }
77
+ }
@@ -0,0 +1,136 @@
1
+ import type { ValidatedConfig } from '../core/config';
2
+ import type { Phase } from '../core/types';
3
+ import type { Orchestrator } from '../core/orchestrator';
4
+
5
+ /** License tier type used for agent gating */
6
+ export type LicenseTier = 'free' | 'pro' | 'enterprise';
7
+
8
+ /**
9
+ * Maps each agent number to its license tier.
10
+ * - free (8 agents): core testing pipeline
11
+ * - pro (9 agents): advanced testing capabilities
12
+ * - enterprise (6 agents): full platform auditing
13
+ */
14
+ export const AGENT_TIERS: Record<number, LicenseTier> = {
15
+ /* ── Free tier (8) ── */
16
+ 1: 'free', // Analyst
17
+ 2: 'free', // Seed Architect
18
+ 3: 'free', // Test Generator
19
+ 4: 'free', // Unit Runner
20
+ 5: 'free', // Browser Crawler
21
+ 9: 'free', // Healer
22
+ 10: 'free', // Reporter
23
+ 11: 'free', // Fixer
24
+
25
+ /* ── Pro tier (9) ── */
26
+ 6: 'pro', // API Exerciser
27
+ 7: 'pro', // Security Scout
28
+ 8: 'pro', // A11y Guardian
29
+ 12: 'pro', // UX Inspector
30
+ 13: 'pro', // Performance Profiler
31
+ 15: 'pro', // Regression Sentinel
32
+ 21: 'pro', // State & Session Tester
33
+ 22: 'pro', // Email Notification Verifier
34
+ 23: 'pro', // Migration Tester
35
+
36
+ /* ── Enterprise tier (6) ── */
37
+ 14: 'enterprise', // Data Integrity Auditor
38
+ 16: 'enterprise', // Chaos Agent
39
+ 17: 'enterprise', // Documentation Validator
40
+ 18: 'enterprise', // Integration Watchdog
41
+ 19: 'enterprise', // Tenant Isolation Auditor
42
+ 20: 'enterprise', // Workflow Completion Tester
43
+ };
44
+
45
+ /** Returns the tier classification for a given agent number. */
46
+ export function getAgentTier(agentNumber: number): LicenseTier {
47
+ const tier = AGENT_TIERS[agentNumber];
48
+ if (!tier) {
49
+ throw new Error(`Unknown agent number: ${agentNumber}`);
50
+ }
51
+ return tier;
52
+ }
53
+
54
+ const TIER_HIERARCHY: Record<LicenseTier, ReadonlySet<LicenseTier>> = {
55
+ free: new Set<LicenseTier>(['free']),
56
+ pro: new Set<LicenseTier>(['free', 'pro']),
57
+ enterprise: new Set<LicenseTier>(['free', 'pro', 'enterprise']),
58
+ };
59
+
60
+ /**
61
+ * Checks whether an agent is available under the given license tier.
62
+ * - free: only free agents
63
+ * - pro: free + pro agents
64
+ * - enterprise: all agents
65
+ */
66
+ export function isAgentAvailable(agentNumber: number, licenseTier: LicenseTier): boolean {
67
+ const agentTier = getAgentTier(agentNumber);
68
+ return TIER_HIERARCHY[licenseTier].has(agentTier);
69
+ }
70
+
71
+ import { AnalystAgent } from './01-analyst';
72
+ import { SeedArchitectAgent } from './02-seed-architect';
73
+ import { TestGeneratorAgent } from './03-test-generator';
74
+ import { UnitRunnerAgent } from './04-unit-runner';
75
+ import { BrowserCrawlerAgent } from './05-browser-crawler';
76
+ import { ApiExerciserAgent } from './06-api-exerciser';
77
+ import { SecurityScoutAgent } from './07-security-scout';
78
+ import { A11yGuardianAgent } from './08-a11y-guardian';
79
+ import { HealerAgent } from './09-healer';
80
+ import { ReporterAgent } from './10-reporter';
81
+ import { FixerAgent } from './11-fixer';
82
+ import { UxInspectorAgent } from './12-ux-inspector';
83
+ import { PerformanceProfilerAgent } from './13-performance-profiler';
84
+ import { DataIntegrityAuditorAgent } from './14-data-integrity-auditor';
85
+ import { RegressionSentinelAgent } from './15-regression-sentinel';
86
+ import { ChaosAgent } from './16-chaos-agent';
87
+ import { DocumentationValidatorAgent } from './17-documentation-validator';
88
+ import { IntegrationWatchdogAgent } from './18-integration-watchdog';
89
+ import { TenantIsolationAuditorAgent } from './19-tenant-isolation-auditor';
90
+ import { WorkflowCompletionTesterAgent } from './20-workflow-completion-tester';
91
+ import { StateSessionTesterAgent } from './21-state-session-tester';
92
+ import { EmailNotificationVerifierAgent } from './22-email-notification-verifier';
93
+ import { MigrationTesterAgent } from './23-migration-tester';
94
+
95
+ /**
96
+ * Register all 23 agents with the orchestrator.
97
+ * Each agent is instantiated with the validated config, phase, and run directory.
98
+ * Since agents may run in different phases, we register them for all phases
99
+ * and the orchestrator handles dispatching only the ones needed per phase.
100
+ */
101
+ export function registerAllAgents(
102
+ orchestrator: Orchestrator,
103
+ config: ValidatedConfig,
104
+ phase: Phase,
105
+ runDir: string,
106
+ ): void {
107
+ const agents = [
108
+ new AnalystAgent(config, phase, runDir),
109
+ new SeedArchitectAgent(config, phase, runDir),
110
+ new TestGeneratorAgent(config, phase, runDir),
111
+ new UnitRunnerAgent(config, phase, runDir),
112
+ new BrowserCrawlerAgent(config, phase, runDir),
113
+ new ApiExerciserAgent(config, phase, runDir),
114
+ new SecurityScoutAgent(config, phase, runDir),
115
+ new A11yGuardianAgent(config, phase, runDir),
116
+ new HealerAgent(config, phase, runDir),
117
+ new ReporterAgent(config, phase, runDir),
118
+ new FixerAgent(config, phase, runDir),
119
+ new UxInspectorAgent(config, phase, runDir),
120
+ new PerformanceProfilerAgent(config, phase, runDir),
121
+ new DataIntegrityAuditorAgent(config, phase, runDir),
122
+ new RegressionSentinelAgent(config, phase, runDir),
123
+ new ChaosAgent(config, phase, runDir),
124
+ new DocumentationValidatorAgent(config, phase, runDir),
125
+ new IntegrationWatchdogAgent(config, phase, runDir),
126
+ new TenantIsolationAuditorAgent(config, phase, runDir),
127
+ new WorkflowCompletionTesterAgent(config, phase, runDir),
128
+ new StateSessionTesterAgent(config, phase, runDir),
129
+ new EmailNotificationVerifierAgent(config, phase, runDir),
130
+ new MigrationTesterAgent(config, phase, runDir),
131
+ ];
132
+
133
+ for (const agent of agents) {
134
+ orchestrator.registerAgent(agent.agentId, agent);
135
+ }
136
+ }
File without changes
File without changes
File without changes
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+ import { execFileSync } from 'node:child_process';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { dirname, join } from 'node:path';
5
+
6
+ const __filename = fileURLToPath(import.meta.url);
7
+ const __dirname = dirname(__filename);
8
+ const cli = join(__dirname, '..', 'core', 'cli.ts');
9
+
10
+ execFileSync('npx', ['tsx', cli, ...process.argv.slice(2)], { stdio: 'inherit' });