@diyor28/qa-core 0.1.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 (67) hide show
  1. package/dist/agent/context/codecs/browser-state.codec.d.ts +33 -0
  2. package/dist/agent/context/codecs/browser-state.codec.d.ts.map +1 -0
  3. package/dist/agent/context/codecs/browser-state.codec.js +46 -0
  4. package/dist/agent/context/codecs/index.d.ts +3 -0
  5. package/dist/agent/context/codecs/index.d.ts.map +1 -0
  6. package/dist/agent/context/codecs/index.js +2 -0
  7. package/dist/agent/context/codecs/qa-scenario.codec.d.ts +21 -0
  8. package/dist/agent/context/codecs/qa-scenario.codec.d.ts.map +1 -0
  9. package/dist/agent/context/codecs/qa-scenario.codec.js +44 -0
  10. package/dist/agent/context/index.d.ts +4 -0
  11. package/dist/agent/context/index.d.ts.map +1 -0
  12. package/dist/agent/context/index.js +3 -0
  13. package/dist/agent/context/qa-context.service.d.ts +24 -0
  14. package/dist/agent/context/qa-context.service.d.ts.map +1 -0
  15. package/dist/agent/context/qa-context.service.js +53 -0
  16. package/dist/agent/context/qa-policy.d.ts +24 -0
  17. package/dist/agent/context/qa-policy.d.ts.map +1 -0
  18. package/dist/agent/context/qa-policy.js +47 -0
  19. package/dist/agent/loop.d.ts +28 -0
  20. package/dist/agent/loop.d.ts.map +1 -0
  21. package/dist/agent/loop.js +111 -0
  22. package/dist/agent/system-prompt.d.ts +2 -0
  23. package/dist/agent/system-prompt.d.ts.map +1 -0
  24. package/dist/agent/system-prompt.js +39 -0
  25. package/dist/agent/tools/index.d.ts +5 -0
  26. package/dist/agent/tools/index.d.ts.map +1 -0
  27. package/dist/agent/tools/index.js +152 -0
  28. package/dist/browser/controller.d.ts +82 -0
  29. package/dist/browser/controller.d.ts.map +1 -0
  30. package/dist/browser/controller.js +289 -0
  31. package/dist/index.d.ts +7 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +7 -0
  34. package/dist/report/builder.d.ts +23 -0
  35. package/dist/report/builder.d.ts.map +1 -0
  36. package/dist/report/builder.js +69 -0
  37. package/dist/runner.d.ts +10 -0
  38. package/dist/runner.d.ts.map +1 -0
  39. package/dist/runner.js +143 -0
  40. package/dist/types.d.ts +69 -0
  41. package/dist/types.d.ts.map +1 -0
  42. package/dist/types.js +1 -0
  43. package/dist/vision/analyzer.d.ts +8 -0
  44. package/dist/vision/analyzer.d.ts.map +1 -0
  45. package/dist/vision/analyzer.js +49 -0
  46. package/dist/vision/prompts.d.ts +2 -0
  47. package/dist/vision/prompts.d.ts.map +1 -0
  48. package/dist/vision/prompts.js +28 -0
  49. package/package.json +49 -0
  50. package/src/agent/context/codecs/browser-state.codec.ts +57 -0
  51. package/src/agent/context/codecs/index.ts +2 -0
  52. package/src/agent/context/codecs/qa-scenario.codec.ts +55 -0
  53. package/src/agent/context/index.ts +3 -0
  54. package/src/agent/context/qa-context.service.ts +90 -0
  55. package/src/agent/context/qa-policy.ts +54 -0
  56. package/src/agent/loop.ts +147 -0
  57. package/src/agent/system-prompt.ts +39 -0
  58. package/src/agent/tools/index.ts +162 -0
  59. package/src/browser/controller.ts +321 -0
  60. package/src/index.ts +19 -0
  61. package/src/report/builder.ts +94 -0
  62. package/src/runner.ts +166 -0
  63. package/src/types.ts +68 -0
  64. package/src/vision/analyzer.ts +61 -0
  65. package/src/vision/prompts.ts +28 -0
  66. package/tsconfig.json +20 -0
  67. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,33 @@
1
+ import { z } from 'zod';
2
+ import type { BlockCodec } from '@diyor28/context';
3
+ export declare const BrowserStatePayloadSchema: z.ZodObject<{
4
+ currentUrl: z.ZodString;
5
+ viewport: z.ZodObject<{
6
+ width: z.ZodNumber;
7
+ height: z.ZodNumber;
8
+ }, "strip", z.ZodTypeAny, {
9
+ width: number;
10
+ height: number;
11
+ }, {
12
+ width: number;
13
+ height: number;
14
+ }>;
15
+ screenshotCount: z.ZodNumber;
16
+ }, "strip", z.ZodTypeAny, {
17
+ currentUrl: string;
18
+ viewport: {
19
+ width: number;
20
+ height: number;
21
+ };
22
+ screenshotCount: number;
23
+ }, {
24
+ currentUrl: string;
25
+ viewport: {
26
+ width: number;
27
+ height: number;
28
+ };
29
+ screenshotCount: number;
30
+ }>;
31
+ export type BrowserStatePayload = z.infer<typeof BrowserStatePayloadSchema>;
32
+ export declare const BrowserStateCodec: BlockCodec<BrowserStatePayload>;
33
+ //# sourceMappingURL=browser-state.codec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-state.codec.d.ts","sourceRoot":"","sources":["../../../../src/agent/context/codecs/browser-state.codec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,UAAU,EAAiC,MAAM,kBAAkB,CAAC;AAGlF,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;EAOpC,CAAC;AAEH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,yBAAyB,CAAC,CAAC;AAE5E,eAAO,MAAM,iBAAiB,EAAE,UAAU,CAAC,mBAAmB,CAyC7D,CAAC"}
@@ -0,0 +1,46 @@
1
+ import { z } from 'zod';
2
+ import { defaultHash, sortObjectKeys } from '@diyor28/context';
3
+ export const BrowserStatePayloadSchema = z.object({
4
+ currentUrl: z.string(),
5
+ viewport: z.object({
6
+ width: z.number(),
7
+ height: z.number(),
8
+ }),
9
+ screenshotCount: z.number(),
10
+ });
11
+ export const BrowserStateCodec = {
12
+ codecId: 'qa:browser-state',
13
+ version: '1.0.0',
14
+ payloadSchema: BrowserStatePayloadSchema,
15
+ canonicalize(payload) {
16
+ return sortObjectKeys(payload);
17
+ },
18
+ hash(canonicalized) {
19
+ return defaultHash(canonicalized);
20
+ },
21
+ render(block) {
22
+ const { currentUrl, viewport, screenshotCount } = block.payload;
23
+ const content = `## Browser State
24
+
25
+ Current URL: ${currentUrl}
26
+ Viewport: ${viewport.width}x${viewport.height}
27
+ Screenshots captured: ${screenshotCount}`;
28
+ return {
29
+ anthropic: {
30
+ role: 'user',
31
+ content: [
32
+ {
33
+ type: 'text',
34
+ text: content,
35
+ cache_control: { type: 'ephemeral' }, // State blocks use ephemeral cache
36
+ },
37
+ ],
38
+ },
39
+ openai: { role: 'user', content },
40
+ gemini: { role: 'user', parts: [{ text: content }] },
41
+ };
42
+ },
43
+ validate(payload) {
44
+ return BrowserStatePayloadSchema.parse(payload);
45
+ },
46
+ };
@@ -0,0 +1,3 @@
1
+ export { QaScenarioCodec, QaScenarioPayloadSchema, type QaScenarioPayload } from './qa-scenario.codec.js';
2
+ export { BrowserStateCodec, BrowserStatePayloadSchema, type BrowserStatePayload } from './browser-state.codec.js';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/agent/context/codecs/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,uBAAuB,EAAE,KAAK,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC1G,OAAO,EAAE,iBAAiB,EAAE,yBAAyB,EAAE,KAAK,mBAAmB,EAAE,MAAM,0BAA0B,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { QaScenarioCodec, QaScenarioPayloadSchema } from './qa-scenario.codec.js';
2
+ export { BrowserStateCodec, BrowserStatePayloadSchema } from './browser-state.codec.js';
@@ -0,0 +1,21 @@
1
+ import { z } from 'zod';
2
+ import type { BlockCodec } from '@diyor28/context';
3
+ export declare const QaScenarioPayloadSchema: z.ZodObject<{
4
+ scenarioName: z.ZodString;
5
+ instructions: z.ZodString;
6
+ baseUrl: z.ZodString;
7
+ defectsToCheck: z.ZodArray<z.ZodString, "many">;
8
+ }, "strip", z.ZodTypeAny, {
9
+ scenarioName: string;
10
+ instructions: string;
11
+ baseUrl: string;
12
+ defectsToCheck: string[];
13
+ }, {
14
+ scenarioName: string;
15
+ instructions: string;
16
+ baseUrl: string;
17
+ defectsToCheck: string[];
18
+ }>;
19
+ export type QaScenarioPayload = z.infer<typeof QaScenarioPayloadSchema>;
20
+ export declare const QaScenarioCodec: BlockCodec<QaScenarioPayload>;
21
+ //# sourceMappingURL=qa-scenario.codec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qa-scenario.codec.d.ts","sourceRoot":"","sources":["../../../../src/agent/context/codecs/qa-scenario.codec.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,UAAU,EAAiC,MAAM,kBAAkB,CAAC;AAGlF,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;EAKlC,CAAC;AAEH,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE,eAAO,MAAM,eAAe,EAAE,UAAU,CAAC,iBAAiB,CAyCzD,CAAC"}
@@ -0,0 +1,44 @@
1
+ import { z } from 'zod';
2
+ import { defaultHash, sortObjectKeys } from '@diyor28/context';
3
+ export const QaScenarioPayloadSchema = z.object({
4
+ scenarioName: z.string(),
5
+ instructions: z.string(),
6
+ baseUrl: z.string().url(),
7
+ defectsToCheck: z.array(z.string()),
8
+ });
9
+ export const QaScenarioCodec = {
10
+ codecId: 'qa:scenario',
11
+ version: '1.0.0',
12
+ payloadSchema: QaScenarioPayloadSchema,
13
+ canonicalize(payload) {
14
+ return sortObjectKeys({
15
+ scenarioName: payload.scenarioName.trim(),
16
+ instructions: payload.instructions.trim(),
17
+ baseUrl: payload.baseUrl,
18
+ defectsToCheck: [...payload.defectsToCheck].sort(),
19
+ });
20
+ },
21
+ hash(canonicalized) {
22
+ return defaultHash(canonicalized);
23
+ },
24
+ render(block) {
25
+ const { scenarioName, instructions, baseUrl, defectsToCheck } = block.payload;
26
+ const content = `# Test Scenario: ${scenarioName}
27
+
28
+ **Target URL:** ${baseUrl}
29
+
30
+ ## Instructions
31
+ ${instructions}
32
+
33
+ ## Defects to Check
34
+ ${defectsToCheck.map((d) => `- ${d}`).join('\n')}`;
35
+ return {
36
+ anthropic: { role: 'user', content },
37
+ openai: { role: 'user', content },
38
+ gemini: { role: 'user', parts: [{ text: content }] },
39
+ };
40
+ },
41
+ validate(payload) {
42
+ return QaScenarioPayloadSchema.parse(payload);
43
+ },
44
+ };
@@ -0,0 +1,4 @@
1
+ export { QaContextService, type BuildQaContextOptions } from './qa-context.service.js';
2
+ export { QA_POLICY } from './qa-policy.js';
3
+ export * from './codecs/index.js';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/context/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AACvF,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAC3C,cAAc,mBAAmB,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { QaContextService } from './qa-context.service.js';
2
+ export { QA_POLICY } from './qa-policy.js';
3
+ export * from './codecs/index.js';
@@ -0,0 +1,24 @@
1
+ import { type OpenAICompiledContext, type ConversationMessage } from '@diyor28/context';
2
+ export interface BuildQaContextOptions {
3
+ scenarioName: string;
4
+ scenarioInstructions: string;
5
+ baseUrl: string;
6
+ defectsToCheck: string[];
7
+ browserState: {
8
+ currentUrl: string;
9
+ viewport: {
10
+ width: number;
11
+ height: number;
12
+ };
13
+ screenshotCount: number;
14
+ };
15
+ conversationHistory: ConversationMessage[];
16
+ systemPrompt: string;
17
+ }
18
+ export declare class QaContextService {
19
+ private readonly tokenEstimator;
20
+ private readonly codecRegistry;
21
+ constructor();
22
+ buildContext(options: BuildQaContextOptions): Promise<OpenAICompiledContext>;
23
+ }
24
+ //# sourceMappingURL=qa-context.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qa-context.service.d.ts","sourceRoot":"","sources":["../../../src/agent/context/qa-context.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,qBAAqB,EAK1B,KAAK,mBAAmB,EAEzB,MAAM,kBAAkB,CAAC;AAI1B,MAAM,WAAW,qBAAqB;IACpC,YAAY,EAAE,MAAM,CAAC;IACrB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,YAAY,EAAE;QACZ,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE;YAAE,KAAK,EAAE,MAAM,CAAC;YAAC,MAAM,EAAE,MAAM,CAAA;SAAE,CAAC;QAC5C,eAAe,EAAE,MAAM,CAAC;KACzB,CAAC;IACF,mBAAmB,EAAE,mBAAmB,EAAE,CAAC;IAC3C,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAuB;IACtD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAmC;;IAe3D,YAAY,CAAC,OAAO,EAAE,qBAAqB,GAAG,OAAO,CAAC,qBAAqB,CAAC;CA4CnF"}
@@ -0,0 +1,53 @@
1
+ import { ContextBuilder, compileOpenAIContext, OpenAITokenEstimator, ConversationHistoryCodec, SystemRulesCodec, } from '@diyor28/context';
2
+ import { QaScenarioCodec, BrowserStateCodec } from './codecs/index.js';
3
+ import { QA_POLICY } from './qa-policy.js';
4
+ export class QaContextService {
5
+ tokenEstimator;
6
+ codecRegistry;
7
+ constructor() {
8
+ // OpenAI token estimator (compatible with Cerebras/GLM-4.7)
9
+ this.tokenEstimator = new OpenAITokenEstimator('gpt-4');
10
+ // Build codec registry
11
+ this.codecRegistry = new Map([
12
+ [SystemRulesCodec.codecId, SystemRulesCodec],
13
+ [QaScenarioCodec.codecId, QaScenarioCodec],
14
+ [BrowserStateCodec.codecId, BrowserStateCodec],
15
+ [ConversationHistoryCodec.codecId, ConversationHistoryCodec],
16
+ ]);
17
+ }
18
+ async buildContext(options) {
19
+ const builder = new ContextBuilder();
20
+ // System prompt (pinned - always at top)
21
+ builder.system({
22
+ text: options.systemPrompt,
23
+ cacheable: false, // OpenAI doesn't have prompt caching yet
24
+ });
25
+ // QA scenario (reference - test instructions)
26
+ builder.reference(QaScenarioCodec, {
27
+ scenarioName: options.scenarioName,
28
+ instructions: options.scenarioInstructions,
29
+ baseUrl: options.baseUrl,
30
+ defectsToCheck: options.defectsToCheck,
31
+ });
32
+ // Browser state (state - current browser info)
33
+ builder.state(BrowserStateCodec, options.browserState);
34
+ // Conversation history (if any)
35
+ if (options.conversationHistory.length > 0) {
36
+ builder.history(options.conversationHistory);
37
+ }
38
+ // Get graph and create view
39
+ const graph = builder.getGraph();
40
+ const maxTokens = QA_POLICY.contextWindow - QA_POLICY.completionReserve;
41
+ const view = await graph.createView({
42
+ tokenEstimator: this.tokenEstimator,
43
+ maxTokens,
44
+ });
45
+ // Compile to OpenAI format
46
+ const compiled = compileOpenAIContext([...view.blocks], QA_POLICY, {
47
+ codecRegistry: this.codecRegistry,
48
+ });
49
+ // Log context usage (for observability)
50
+ console.log(`[QA Context] Tokens: ${compiled.estimatedTokens}, Blocks: ${view.blocks.length}`);
51
+ return compiled;
52
+ }
53
+ }
@@ -0,0 +1,24 @@
1
+ import type { ContextPolicy } from '@diyor28/context';
2
+ /**
3
+ * QA Context Policy
4
+ *
5
+ * Defines context management strategy for QA agent execution.
6
+ * Optimized for browser automation with tool outputs and screenshot management.
7
+ *
8
+ * ## Context Structure
9
+ *
10
+ * 1. System prompt (pinned)
11
+ * 2. QA scenario (reference - test instructions)
12
+ * 3. Browser state (state - current URL, viewport)
13
+ * 4. Tool outputs (tool_output - navigation, clicks, screenshots)
14
+ * 5. Conversation history (history - agent reasoning)
15
+ *
16
+ * ## Compaction Triggers
17
+ *
18
+ * When context exceeds available tokens:
19
+ * - Old tool outputs are pruned (keep last 5)
20
+ * - History is summarized
21
+ * - State blocks are never truncated
22
+ */
23
+ export declare const QA_POLICY: ContextPolicy;
24
+ //# sourceMappingURL=qa-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qa-policy.d.ts","sourceRoot":"","sources":["../../../src/agent/context/qa-policy.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,SAAS,EAAE,aA8BvB,CAAC"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * QA Context Policy
3
+ *
4
+ * Defines context management strategy for QA agent execution.
5
+ * Optimized for browser automation with tool outputs and screenshot management.
6
+ *
7
+ * ## Context Structure
8
+ *
9
+ * 1. System prompt (pinned)
10
+ * 2. QA scenario (reference - test instructions)
11
+ * 3. Browser state (state - current URL, viewport)
12
+ * 4. Tool outputs (tool_output - navigation, clicks, screenshots)
13
+ * 5. Conversation history (history - agent reasoning)
14
+ *
15
+ * ## Compaction Triggers
16
+ *
17
+ * When context exceeds available tokens:
18
+ * - Old tool outputs are pruned (keep last 5)
19
+ * - History is summarized
20
+ * - State blocks are never truncated
21
+ */
22
+ export const QA_POLICY = {
23
+ provider: 'openai', // Cerebras is OpenAI-compatible
24
+ modelId: 'glm-4.7', // GLM-4.7 via Cerebras
25
+ contextWindow: 200_000, // GLM-4.7 context window
26
+ completionReserve: 8_000, // Reserve for agent response
27
+ overflowStrategy: 'compact', // Auto-compact old tool outputs
28
+ kindPriorities: [
29
+ { kind: 'pinned', minTokens: 500, maxTokens: 2000, truncatable: false },
30
+ { kind: 'reference', minTokens: 500, maxTokens: 3000, truncatable: false },
31
+ { kind: 'state', minTokens: 200, maxTokens: 1000, truncatable: false },
32
+ { kind: 'tool_output', minTokens: 1000, maxTokens: 50000, truncatable: true },
33
+ { kind: 'history', minTokens: 2000, maxTokens: 100000, truncatable: true },
34
+ { kind: 'turn', minTokens: 500, maxTokens: 5000, truncatable: false },
35
+ ],
36
+ compaction: {
37
+ pruneToolOutputs: true,
38
+ maxToolOutputAge: 3600, // 1 hour
39
+ maxToolOutputsPerKind: 5, // Keep last 5 tool outputs
40
+ summarizeHistory: true,
41
+ maxHistoryMessages: 20, // Keep last 20 turns
42
+ },
43
+ sensitivity: {
44
+ maxSensitivity: 'public',
45
+ redactRestricted: true,
46
+ },
47
+ };
@@ -0,0 +1,28 @@
1
+ import type { BrowserController } from '../browser/controller.js';
2
+ import type { VisionAnalyzer } from '../vision/analyzer.js';
3
+ import type { QaFinding, QaProgressEvent } from '../types.js';
4
+ export interface AgentLoopConfig {
5
+ cerebrasApiKey: string;
6
+ browserController: BrowserController;
7
+ visionAnalyzer: VisionAnalyzer;
8
+ scenarioName: string;
9
+ scenarioContent: string;
10
+ baseUrl: string;
11
+ defectsToCheck: string[];
12
+ maxIterations?: number;
13
+ onProgress?: (event: QaProgressEvent) => void;
14
+ }
15
+ export interface AgentLoopResult {
16
+ findings: QaFinding[];
17
+ summary: string;
18
+ status: 'passed' | 'failed' | 'issues_found';
19
+ }
20
+ export declare class AgentLoop {
21
+ private config;
22
+ private contextService;
23
+ constructor(config: AgentLoopConfig);
24
+ run(): Promise<AgentLoopResult>;
25
+ private parseFindingsFromResponse;
26
+ private emitProgress;
27
+ }
28
+ //# sourceMappingURL=loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loop.d.ts","sourceRoot":"","sources":["../../src/agent/loop.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAK9D,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,iBAAiB,CAAC;IACrC,cAAc,EAAE,cAAc,CAAC;IAC/B,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAC;CAC/C;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,EAAE,SAAS,EAAE,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,cAAc,CAAC;CAC9C;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,MAAM,CAAkB;IAChC,OAAO,CAAC,cAAc,CAAmB;gBAE7B,MAAM,EAAE,eAAe;IAK7B,GAAG,IAAI,OAAO,CAAC,eAAe,CAAC;IAiErC,OAAO,CAAC,yBAAyB;IAoCjC,OAAO,CAAC,YAAY;CASrB"}
@@ -0,0 +1,111 @@
1
+ import { createOpenAI } from '@ai-sdk/openai';
2
+ import { generateText } from 'ai';
3
+ import { createAgentTools } from './tools/index.js';
4
+ import { buildSystemPrompt } from './system-prompt.js';
5
+ import { QaContextService } from './context/index.js';
6
+ export class AgentLoop {
7
+ config;
8
+ contextService;
9
+ constructor(config) {
10
+ this.config = config;
11
+ this.contextService = new QaContextService();
12
+ }
13
+ async run() {
14
+ const cerebras = createOpenAI({
15
+ baseURL: 'https://inference.cerebras.ai/v1',
16
+ apiKey: this.config.cerebrasApiKey,
17
+ });
18
+ const model = cerebras('glm-4.7');
19
+ const tools = createAgentTools(this.config.browserController, this.config.visionAnalyzer);
20
+ const systemPrompt = buildSystemPrompt(this.config.defectsToCheck, this.config.scenarioContent);
21
+ try {
22
+ this.emitProgress('step_started', { step: 'agent_execution', message: 'Starting QA agent loop' });
23
+ const result = await generateText({
24
+ model,
25
+ system: systemPrompt,
26
+ prompt: `Execute the test scenario. Start by navigating to the base URL: ${this.config.baseUrl}`,
27
+ tools,
28
+ maxSteps: this.config.maxIterations || 15,
29
+ temperature: 0.15,
30
+ maxTokens: 1000,
31
+ });
32
+ this.emitProgress('step_completed', {
33
+ step: 'agent_execution',
34
+ message: 'QA agent loop completed',
35
+ usage: result.usage,
36
+ });
37
+ // Parse findings from agent's final response
38
+ const findings = this.parseFindingsFromResponse(result.text);
39
+ // Determine status
40
+ let status = 'passed';
41
+ if (findings.some((f) => f.severity === 'critical' || f.severity === 'high')) {
42
+ status = 'failed';
43
+ }
44
+ else if (findings.length > 0) {
45
+ status = 'issues_found';
46
+ }
47
+ return {
48
+ findings,
49
+ summary: result.text,
50
+ status,
51
+ };
52
+ }
53
+ catch (error) {
54
+ console.error('[AgentLoop] Error during execution:', error);
55
+ return {
56
+ findings: [
57
+ {
58
+ severity: 'critical',
59
+ title: 'Agent execution failed',
60
+ description: `Error during test execution: ${error instanceof Error ? error.message : String(error)}`,
61
+ reproSteps: ['Agent loop encountered an error'],
62
+ category: 'functional',
63
+ },
64
+ ],
65
+ summary: `Test execution failed: ${error instanceof Error ? error.message : String(error)}`,
66
+ status: 'failed',
67
+ };
68
+ }
69
+ }
70
+ parseFindingsFromResponse(responseText) {
71
+ const findings = [];
72
+ // Look for structured findings in the response
73
+ // Pattern 1: JSON findings array
74
+ const jsonMatch = responseText.match(/\{[\s\S]*"findings"[\s\S]*\}/);
75
+ if (jsonMatch) {
76
+ try {
77
+ const parsed = JSON.parse(jsonMatch[0]);
78
+ if (Array.isArray(parsed.findings)) {
79
+ findings.push(...parsed.findings);
80
+ }
81
+ }
82
+ catch {
83
+ // Not valid JSON, continue
84
+ }
85
+ }
86
+ // Pattern 2: Look for severity keywords in text
87
+ const severityPattern = /(critical|high|medium|low)\s*(issue|bug|defect|problem):\s*(.+)/gi;
88
+ let match;
89
+ while ((match = severityPattern.exec(responseText)) !== null) {
90
+ const severity = match[1].toLowerCase();
91
+ const description = match[3];
92
+ findings.push({
93
+ severity,
94
+ title: description.substring(0, 100),
95
+ description,
96
+ reproSteps: ['Extracted from agent response'],
97
+ category: 'functional',
98
+ });
99
+ }
100
+ return findings;
101
+ }
102
+ emitProgress(type, data) {
103
+ if (this.config.onProgress) {
104
+ this.config.onProgress({
105
+ type,
106
+ data,
107
+ timestamp: Date.now(),
108
+ });
109
+ }
110
+ }
111
+ }
@@ -0,0 +1,2 @@
1
+ export declare function buildSystemPrompt(defectsToCheck: string[], scenarioContent: string): string;
2
+ //# sourceMappingURL=system-prompt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system-prompt.d.ts","sourceRoot":"","sources":["../../src/agent/system-prompt.ts"],"names":[],"mappings":"AAAA,wBAAgB,iBAAiB,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE,eAAe,EAAE,MAAM,GAAG,MAAM,CAsC3F"}
@@ -0,0 +1,39 @@
1
+ export function buildSystemPrompt(defectsToCheck, scenarioContent) {
2
+ return `You are a QA testing agent executing browser-based test scenarios.
3
+
4
+ ## Your Mission
5
+ Execute the test scenario step-by-step, validate behavior, and report any issues discovered.
6
+
7
+ ## Execution Guidelines
8
+ 1. Start by navigating to the base URL
9
+ 2. Follow the scenario instructions precisely
10
+ 3. After each significant action (navigation, form submission), take a screenshot
11
+ 4. Use the analyze_ui_ux tool after taking screenshots to check for visual/UX issues
12
+ 5. Verify expected outcomes with assertions
13
+ 6. When you find an issue:
14
+ - Take a screenshot
15
+ - Document expected vs actual behavior with severity (low, medium, high, critical)
16
+ - Provide clear reproduction steps
17
+ 7. Use parallel tool calls when actions are independent (e.g., multiple assertions)
18
+
19
+ ## Defects to Check
20
+ ${defectsToCheck.map((d) => `- ${d}`).join('\n')}
21
+
22
+ ## Important Rules
23
+ - Use getByRole/getByText locators when possible (more reliable than CSS selectors)
24
+ - Wait for elements before interacting
25
+ - Take screenshots at key checkpoints for visual verification
26
+ - Request UX analysis on key screens using analyze_ui_ux tool
27
+ - If a tool fails, analyze the error and try an alternative approach (different selector, wait longer, etc.)
28
+
29
+ ## On Completion
30
+ When you've completed all scenario steps, respond with a summary of:
31
+ - Test result (passed/failed)
32
+ - Issues discovered with severity and details
33
+ - Overall assessment
34
+
35
+ Do not make additional tool calls after providing your final summary.
36
+
37
+ ## Scenario to Execute
38
+ ${scenarioContent}`;
39
+ }
@@ -0,0 +1,5 @@
1
+ import type { CoreTool } from 'ai';
2
+ import type { BrowserController } from '../../browser/controller.js';
3
+ import type { VisionAnalyzer } from '../../vision/analyzer.js';
4
+ export declare function createAgentTools(browserController: BrowserController, visionAnalyzer: VisionAnalyzer): Record<string, CoreTool>;
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/tools/index.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AACnC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,6BAA6B,CAAC;AACrE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAE/D,wBAAgB,gBAAgB,CAC9B,iBAAiB,EAAE,iBAAiB,EACpC,cAAc,EAAE,cAAc,GAC7B,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAyJ1B"}