@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.
- package/dist/agent/context/codecs/browser-state.codec.d.ts +33 -0
- package/dist/agent/context/codecs/browser-state.codec.d.ts.map +1 -0
- package/dist/agent/context/codecs/browser-state.codec.js +46 -0
- package/dist/agent/context/codecs/index.d.ts +3 -0
- package/dist/agent/context/codecs/index.d.ts.map +1 -0
- package/dist/agent/context/codecs/index.js +2 -0
- package/dist/agent/context/codecs/qa-scenario.codec.d.ts +21 -0
- package/dist/agent/context/codecs/qa-scenario.codec.d.ts.map +1 -0
- package/dist/agent/context/codecs/qa-scenario.codec.js +44 -0
- package/dist/agent/context/index.d.ts +4 -0
- package/dist/agent/context/index.d.ts.map +1 -0
- package/dist/agent/context/index.js +3 -0
- package/dist/agent/context/qa-context.service.d.ts +24 -0
- package/dist/agent/context/qa-context.service.d.ts.map +1 -0
- package/dist/agent/context/qa-context.service.js +53 -0
- package/dist/agent/context/qa-policy.d.ts +24 -0
- package/dist/agent/context/qa-policy.d.ts.map +1 -0
- package/dist/agent/context/qa-policy.js +47 -0
- package/dist/agent/loop.d.ts +28 -0
- package/dist/agent/loop.d.ts.map +1 -0
- package/dist/agent/loop.js +111 -0
- package/dist/agent/system-prompt.d.ts +2 -0
- package/dist/agent/system-prompt.d.ts.map +1 -0
- package/dist/agent/system-prompt.js +39 -0
- package/dist/agent/tools/index.d.ts +5 -0
- package/dist/agent/tools/index.d.ts.map +1 -0
- package/dist/agent/tools/index.js +152 -0
- package/dist/browser/controller.d.ts +82 -0
- package/dist/browser/controller.d.ts.map +1 -0
- package/dist/browser/controller.js +289 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/report/builder.d.ts +23 -0
- package/dist/report/builder.d.ts.map +1 -0
- package/dist/report/builder.js +69 -0
- package/dist/runner.d.ts +10 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +143 -0
- package/dist/types.d.ts +69 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/vision/analyzer.d.ts +8 -0
- package/dist/vision/analyzer.d.ts.map +1 -0
- package/dist/vision/analyzer.js +49 -0
- package/dist/vision/prompts.d.ts +2 -0
- package/dist/vision/prompts.d.ts.map +1 -0
- package/dist/vision/prompts.js +28 -0
- package/package.json +49 -0
- package/src/agent/context/codecs/browser-state.codec.ts +57 -0
- package/src/agent/context/codecs/index.ts +2 -0
- package/src/agent/context/codecs/qa-scenario.codec.ts +55 -0
- package/src/agent/context/index.ts +3 -0
- package/src/agent/context/qa-context.service.ts +90 -0
- package/src/agent/context/qa-policy.ts +54 -0
- package/src/agent/loop.ts +147 -0
- package/src/agent/system-prompt.ts +39 -0
- package/src/agent/tools/index.ts +162 -0
- package/src/browser/controller.ts +321 -0
- package/src/index.ts +19 -0
- package/src/report/builder.ts +94 -0
- package/src/runner.ts +166 -0
- package/src/types.ts +68 -0
- package/src/vision/analyzer.ts +61 -0
- package/src/vision/prompts.ts +28 -0
- package/tsconfig.json +20 -0
- 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 @@
|
|
|
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,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 @@
|
|
|
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,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 @@
|
|
|
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"}
|