@hasna/testers 0.0.4 → 0.0.5

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/cli/index.js CHANGED
@@ -3641,7 +3641,8 @@ async function runAgentLoop(options) {
3641
3641
  screenshotter,
3642
3642
  model,
3643
3643
  runId,
3644
- maxTurns = 30
3644
+ maxTurns = 30,
3645
+ onStep
3645
3646
  } = options;
3646
3647
  const systemPrompt = [
3647
3648
  "You are an expert QA testing agent. Your job is to thoroughly test web application scenarios.",
@@ -3700,8 +3701,8 @@ async function runAgentLoop(options) {
3700
3701
  }
3701
3702
  const toolUseBlocks = response.content.filter((block) => block.type === "tool_use");
3702
3703
  if (toolUseBlocks.length === 0 && response.stop_reason === "end_turn") {
3703
- const textBlocks = response.content.filter((block) => block.type === "text");
3704
- const textReasoning = textBlocks.map((b) => b.text).join(`
3704
+ const textBlocks2 = response.content.filter((block) => block.type === "text");
3705
+ const textReasoning = textBlocks2.map((b) => b.text).join(`
3705
3706
  `);
3706
3707
  return {
3707
3708
  status: "error",
@@ -3712,10 +3713,22 @@ async function runAgentLoop(options) {
3712
3713
  };
3713
3714
  }
3714
3715
  const toolResults = [];
3716
+ const textBlocks = response.content.filter((block) => block.type === "text");
3717
+ if (textBlocks.length > 0 && onStep) {
3718
+ const thinking = textBlocks.map((b) => b.text).join(`
3719
+ `);
3720
+ onStep({ type: "thinking", thinking, stepNumber });
3721
+ }
3715
3722
  for (const toolBlock of toolUseBlocks) {
3716
3723
  stepNumber++;
3717
3724
  const toolInput = toolBlock.input;
3725
+ if (onStep) {
3726
+ onStep({ type: "tool_call", toolName: toolBlock.name, toolInput, stepNumber });
3727
+ }
3718
3728
  const execResult = await executeTool(page, screenshotter, toolBlock.name, toolInput, { runId, scenarioSlug, stepNumber });
3729
+ if (onStep) {
3730
+ onStep({ type: "tool_result", toolName: toolBlock.name, toolResult: execResult.result, stepNumber });
3731
+ }
3719
3732
  if (execResult.screenshot) {
3720
3733
  screenshots.push({
3721
3734
  ...execResult.screenshot,
@@ -3824,6 +3837,9 @@ function loadConfig() {
3824
3837
 
3825
3838
  // src/lib/runner.ts
3826
3839
  var eventHandler = null;
3840
+ function onRunEvent(handler) {
3841
+ eventHandler = handler;
3842
+ }
3827
3843
  function emit(event) {
3828
3844
  if (eventHandler)
3829
3845
  eventHandler(event);
@@ -3858,7 +3874,20 @@ async function runSingleScenario(scenario, runId, options) {
3858
3874
  screenshotter,
3859
3875
  model,
3860
3876
  runId,
3861
- maxTurns: 30
3877
+ maxTurns: 30,
3878
+ onStep: (stepEvent) => {
3879
+ emit({
3880
+ type: `step:${stepEvent.type}`,
3881
+ scenarioId: scenario.id,
3882
+ scenarioName: scenario.name,
3883
+ runId,
3884
+ toolName: stepEvent.toolName,
3885
+ toolInput: stepEvent.toolInput,
3886
+ toolResult: stepEvent.toolResult,
3887
+ thinking: stepEvent.thinking,
3888
+ stepNumber: stepEvent.stepNumber
3889
+ });
3890
+ }
3862
3891
  });
3863
3892
  for (const ss of agentResult.screenshots) {
3864
3893
  createScreenshot({
@@ -5362,8 +5391,17 @@ function deleteAuthPreset(name) {
5362
5391
 
5363
5392
  // src/cli/index.tsx
5364
5393
  import { existsSync as existsSync7, mkdirSync as mkdirSync4 } from "fs";
5394
+ function formatToolInput(input) {
5395
+ const parts = [];
5396
+ for (const [key, value] of Object.entries(input)) {
5397
+ const str = typeof value === "string" ? value : JSON.stringify(value);
5398
+ const truncated = str.length > 60 ? str.slice(0, 60) + "..." : str;
5399
+ parts.push(`${key}="${truncated}"`);
5400
+ }
5401
+ return parts.join(" ");
5402
+ }
5365
5403
  var program2 = new Command;
5366
- program2.name("testers").version("0.0.1").description("AI-powered browser testing CLI");
5404
+ program2.name("testers").version("0.0.4").description("AI-powered browser testing CLI");
5367
5405
  var CONFIG_DIR2 = join6(process.env["HOME"] ?? "~", ".testers");
5368
5406
  var CONFIG_PATH2 = join6(CONFIG_DIR2, "config.json");
5369
5407
  function getActiveProject() {
@@ -5543,6 +5581,47 @@ program2.command("run <url> [description]").description("Run test scenarios agai
5543
5581
  console.log(chalk4.dim(` Check progress: testers results ${runId.slice(0, 8)}`));
5544
5582
  process.exit(0);
5545
5583
  }
5584
+ if (!opts.json && !opts.output) {
5585
+ onRunEvent((event) => {
5586
+ switch (event.type) {
5587
+ case "scenario:start":
5588
+ console.log(chalk4.blue(` [start] ${event.scenarioName ?? event.scenarioId}`));
5589
+ break;
5590
+ case "step:thinking":
5591
+ if (event.thinking) {
5592
+ const preview = event.thinking.length > 120 ? event.thinking.slice(0, 120) + "..." : event.thinking;
5593
+ console.log(chalk4.dim(` [think] ${preview}`));
5594
+ }
5595
+ break;
5596
+ case "step:tool_call":
5597
+ console.log(chalk4.cyan(` [step ${event.stepNumber}] ${event.toolName}${event.toolInput ? ` ${formatToolInput(event.toolInput)}` : ""}`));
5598
+ break;
5599
+ case "step:tool_result":
5600
+ if (event.toolName === "report_result") {
5601
+ console.log(chalk4.bold(` [result] ${event.toolResult}`));
5602
+ } else {
5603
+ const resultPreview = (event.toolResult ?? "").length > 100 ? (event.toolResult ?? "").slice(0, 100) + "..." : event.toolResult ?? "";
5604
+ console.log(chalk4.dim(` [done] ${resultPreview}`));
5605
+ }
5606
+ break;
5607
+ case "screenshot:captured":
5608
+ console.log(chalk4.dim(` [screenshot] ${event.screenshotPath}`));
5609
+ break;
5610
+ case "scenario:pass":
5611
+ console.log(chalk4.green(` [PASS] ${event.scenarioName}`));
5612
+ break;
5613
+ case "scenario:fail":
5614
+ console.log(chalk4.red(` [FAIL] ${event.scenarioName}`));
5615
+ break;
5616
+ case "scenario:error":
5617
+ console.log(chalk4.yellow(` [ERR] ${event.scenarioName}: ${event.error}`));
5618
+ break;
5619
+ }
5620
+ });
5621
+ console.log("");
5622
+ console.log(chalk4.bold(` Running tests against ${url}`));
5623
+ console.log("");
5624
+ }
5546
5625
  if (description) {
5547
5626
  const scenario = createScenario({
5548
5627
  name: description,
package/dist/index.js CHANGED
@@ -1951,7 +1951,8 @@ async function runAgentLoop(options) {
1951
1951
  screenshotter,
1952
1952
  model,
1953
1953
  runId,
1954
- maxTurns = 30
1954
+ maxTurns = 30,
1955
+ onStep
1955
1956
  } = options;
1956
1957
  const systemPrompt = [
1957
1958
  "You are an expert QA testing agent. Your job is to thoroughly test web application scenarios.",
@@ -2010,8 +2011,8 @@ async function runAgentLoop(options) {
2010
2011
  }
2011
2012
  const toolUseBlocks = response.content.filter((block) => block.type === "tool_use");
2012
2013
  if (toolUseBlocks.length === 0 && response.stop_reason === "end_turn") {
2013
- const textBlocks = response.content.filter((block) => block.type === "text");
2014
- const textReasoning = textBlocks.map((b) => b.text).join(`
2014
+ const textBlocks2 = response.content.filter((block) => block.type === "text");
2015
+ const textReasoning = textBlocks2.map((b) => b.text).join(`
2015
2016
  `);
2016
2017
  return {
2017
2018
  status: "error",
@@ -2022,10 +2023,22 @@ async function runAgentLoop(options) {
2022
2023
  };
2023
2024
  }
2024
2025
  const toolResults = [];
2026
+ const textBlocks = response.content.filter((block) => block.type === "text");
2027
+ if (textBlocks.length > 0 && onStep) {
2028
+ const thinking = textBlocks.map((b) => b.text).join(`
2029
+ `);
2030
+ onStep({ type: "thinking", thinking, stepNumber });
2031
+ }
2025
2032
  for (const toolBlock of toolUseBlocks) {
2026
2033
  stepNumber++;
2027
2034
  const toolInput = toolBlock.input;
2035
+ if (onStep) {
2036
+ onStep({ type: "tool_call", toolName: toolBlock.name, toolInput, stepNumber });
2037
+ }
2028
2038
  const execResult = await executeTool(page, screenshotter, toolBlock.name, toolInput, { runId, scenarioSlug, stepNumber });
2039
+ if (onStep) {
2040
+ onStep({ type: "tool_result", toolName: toolBlock.name, toolResult: execResult.result, stepNumber });
2041
+ }
2029
2042
  if (execResult.screenshot) {
2030
2043
  screenshots.push({
2031
2044
  ...execResult.screenshot,
@@ -2115,7 +2128,20 @@ async function runSingleScenario(scenario, runId, options) {
2115
2128
  screenshotter,
2116
2129
  model,
2117
2130
  runId,
2118
- maxTurns: 30
2131
+ maxTurns: 30,
2132
+ onStep: (stepEvent) => {
2133
+ emit({
2134
+ type: `step:${stepEvent.type}`,
2135
+ scenarioId: scenario.id,
2136
+ scenarioName: scenario.name,
2137
+ runId,
2138
+ toolName: stepEvent.toolName,
2139
+ toolInput: stepEvent.toolInput,
2140
+ toolResult: stepEvent.toolResult,
2141
+ thinking: stepEvent.thinking,
2142
+ stepNumber: stepEvent.stepNumber
2143
+ });
2144
+ }
2119
2145
  });
2120
2146
  for (const ss of agentResult.screenshots) {
2121
2147
  createScreenshot({
@@ -31,6 +31,14 @@ interface ToolExecutionResult {
31
31
  * Returns the result string and an optional screenshot capture.
32
32
  */
33
33
  export declare function executeTool(page: Page, screenshotter: Screenshotter, toolName: string, toolInput: Record<string, unknown>, context: ToolContext): Promise<ToolExecutionResult>;
34
+ export type StepEventHandler = (event: {
35
+ type: "tool_call" | "tool_result" | "thinking";
36
+ toolName?: string;
37
+ toolInput?: Record<string, unknown>;
38
+ toolResult?: string;
39
+ thinking?: string;
40
+ stepNumber: number;
41
+ }) => void;
34
42
  interface AgentLoopOptions {
35
43
  client: Anthropic;
36
44
  page: Page;
@@ -39,6 +47,7 @@ interface AgentLoopOptions {
39
47
  model: string;
40
48
  runId: string;
41
49
  maxTurns?: number;
50
+ onStep?: StepEventHandler;
42
51
  }
43
52
  interface AgentLoopResult {
44
53
  status: "passed" | "failed" | "error";
@@ -1 +1 @@
1
- {"version":3,"file":"ai-client.d.ts","sourceRoot":"","sources":["../../src/lib/ai-client.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,mBAAmB,CAAC;AAC1C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAExD,OAAO,KAAK,EAAe,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAI/D;;;GAGG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAKzD;AAID,eAAO,MAAM,aAAa,EAAE,SAAS,CAAC,IAAI,EAyTzC,CAAC;AAIF,UAAU,WAAW;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,UAAU,gBAAgB;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,UAAU,mBAAmB;IAC3B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC/B;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,IAAI,EACV,aAAa,EAAE,aAAa,EAC5B,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClC,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,mBAAmB,CAAC,CA8P9B;AAID,UAAU,gBAAgB;IACxB,MAAM,EAAE,SAAS,CAAC;IAClB,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,QAAQ,CAAC;IACnB,aAAa,EAAE,aAAa,CAAC;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,eAAe;IACvB,MAAM,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;IACtC,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;IACvB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,KAAK,CAAC;QACjB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,MAAM,CAAC;QACd,MAAM,EAAE,MAAM,CAAC;QACf,SAAS,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;QAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;QACvB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;KAC9B,CAAC,CAAC;CACJ;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,eAAe,CAAC,CAyK1B;AAID;;;GAGG;AACH,wBAAgB,YAAY,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAQvD"}
1
+ {"version":3,"file":"ai-client.d.ts","sourceRoot":"","sources":["../../src/lib/ai-client.ts"],"names":[],"mappings":"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"}
@@ -10,13 +10,18 @@ export interface RunOptions {
10
10
  screenshotDir?: string;
11
11
  }
12
12
  export interface RunEvent {
13
- type: "scenario:start" | "scenario:pass" | "scenario:fail" | "scenario:error" | "screenshot:captured" | "run:complete";
13
+ type: "scenario:start" | "scenario:pass" | "scenario:fail" | "scenario:error" | "screenshot:captured" | "run:complete" | "step:tool_call" | "step:tool_result" | "step:thinking";
14
14
  scenarioId?: string;
15
15
  scenarioName?: string;
16
16
  resultId?: string;
17
17
  runId?: string;
18
18
  error?: string;
19
19
  screenshotPath?: string;
20
+ toolName?: string;
21
+ toolInput?: Record<string, unknown>;
22
+ toolResult?: string;
23
+ thinking?: string;
24
+ stepNumber?: number;
20
25
  }
21
26
  export type RunEventHandler = (event: RunEvent) => void;
22
27
  export declare function onRunEvent(handler: RunEventHandler): void;
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/lib/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAW/D,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,gBAAgB,GAAG,eAAe,GAAG,eAAe,GAAG,gBAAgB,GAAG,qBAAqB,GAAG,cAAc,CAAC;IACvH,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;AAIxD,wBAAgB,UAAU,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAEzD;AAMD,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,MAAM,CAAC,CAoFjB;AAED,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,QAAQ,EAAE,EACrB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC;IAAE,GAAG,EAAE,GAAG,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CA4D1C;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,UAAU,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GACnF,OAAO,CAAC;IAAE,GAAG,EAAE,GAAG,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAuB1C;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,UAAU,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GACnF;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CA+E1C"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/lib/runner.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAW/D,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EACA,gBAAgB,GAChB,eAAe,GACf,eAAe,GACf,gBAAgB,GAChB,qBAAqB,GACrB,cAAc,GACd,gBAAgB,GAChB,kBAAkB,GAClB,eAAe,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,eAAe,GAAG,CAAC,KAAK,EAAE,QAAQ,KAAK,IAAI,CAAC;AAIxD,wBAAgB,UAAU,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,CAEzD;AAMD,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,QAAQ,EAClB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,MAAM,CAAC,CAiGjB;AAED,wBAAsB,QAAQ,CAC5B,SAAS,EAAE,QAAQ,EAAE,EACrB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC;IAAE,GAAG,EAAE,GAAG,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CA4D1C;AAED,wBAAsB,WAAW,CAC/B,OAAO,EAAE,UAAU,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GACnF,OAAO,CAAC;IAAE,GAAG,EAAE,GAAG,CAAC;IAAC,OAAO,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAuB1C;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,OAAO,EAAE,UAAU,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GACnF;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CA+E1C"}
package/dist/mcp/index.js CHANGED
@@ -5565,7 +5565,8 @@ async function runAgentLoop(options) {
5565
5565
  screenshotter,
5566
5566
  model,
5567
5567
  runId,
5568
- maxTurns = 30
5568
+ maxTurns = 30,
5569
+ onStep
5569
5570
  } = options;
5570
5571
  const systemPrompt = [
5571
5572
  "You are an expert QA testing agent. Your job is to thoroughly test web application scenarios.",
@@ -5624,8 +5625,8 @@ async function runAgentLoop(options) {
5624
5625
  }
5625
5626
  const toolUseBlocks = response.content.filter((block) => block.type === "tool_use");
5626
5627
  if (toolUseBlocks.length === 0 && response.stop_reason === "end_turn") {
5627
- const textBlocks = response.content.filter((block) => block.type === "text");
5628
- const textReasoning = textBlocks.map((b) => b.text).join(`
5628
+ const textBlocks2 = response.content.filter((block) => block.type === "text");
5629
+ const textReasoning = textBlocks2.map((b) => b.text).join(`
5629
5630
  `);
5630
5631
  return {
5631
5632
  status: "error",
@@ -5636,10 +5637,22 @@ async function runAgentLoop(options) {
5636
5637
  };
5637
5638
  }
5638
5639
  const toolResults = [];
5640
+ const textBlocks = response.content.filter((block) => block.type === "text");
5641
+ if (textBlocks.length > 0 && onStep) {
5642
+ const thinking = textBlocks.map((b) => b.text).join(`
5643
+ `);
5644
+ onStep({ type: "thinking", thinking, stepNumber });
5645
+ }
5639
5646
  for (const toolBlock of toolUseBlocks) {
5640
5647
  stepNumber++;
5641
5648
  const toolInput = toolBlock.input;
5649
+ if (onStep) {
5650
+ onStep({ type: "tool_call", toolName: toolBlock.name, toolInput, stepNumber });
5651
+ }
5642
5652
  const execResult = await executeTool(page, screenshotter, toolBlock.name, toolInput, { runId, scenarioSlug, stepNumber });
5653
+ if (onStep) {
5654
+ onStep({ type: "tool_result", toolName: toolBlock.name, toolResult: execResult.result, stepNumber });
5655
+ }
5643
5656
  if (execResult.screenshot) {
5644
5657
  screenshots.push({
5645
5658
  ...execResult.screenshot,
@@ -5781,7 +5794,20 @@ async function runSingleScenario(scenario, runId, options) {
5781
5794
  screenshotter,
5782
5795
  model,
5783
5796
  runId,
5784
- maxTurns: 30
5797
+ maxTurns: 30,
5798
+ onStep: (stepEvent) => {
5799
+ emit({
5800
+ type: `step:${stepEvent.type}`,
5801
+ scenarioId: scenario.id,
5802
+ scenarioName: scenario.name,
5803
+ runId,
5804
+ toolName: stepEvent.toolName,
5805
+ toolInput: stepEvent.toolInput,
5806
+ toolResult: stepEvent.toolResult,
5807
+ thinking: stepEvent.thinking,
5808
+ stepNumber: stepEvent.stepNumber
5809
+ });
5810
+ }
5785
5811
  });
5786
5812
  for (const ss of agentResult.screenshots) {
5787
5813
  createScreenshot({
@@ -1499,7 +1499,8 @@ async function runAgentLoop(options) {
1499
1499
  screenshotter,
1500
1500
  model,
1501
1501
  runId,
1502
- maxTurns = 30
1502
+ maxTurns = 30,
1503
+ onStep
1503
1504
  } = options;
1504
1505
  const systemPrompt = [
1505
1506
  "You are an expert QA testing agent. Your job is to thoroughly test web application scenarios.",
@@ -1558,8 +1559,8 @@ async function runAgentLoop(options) {
1558
1559
  }
1559
1560
  const toolUseBlocks = response.content.filter((block) => block.type === "tool_use");
1560
1561
  if (toolUseBlocks.length === 0 && response.stop_reason === "end_turn") {
1561
- const textBlocks = response.content.filter((block) => block.type === "text");
1562
- const textReasoning = textBlocks.map((b) => b.text).join(`
1562
+ const textBlocks2 = response.content.filter((block) => block.type === "text");
1563
+ const textReasoning = textBlocks2.map((b) => b.text).join(`
1563
1564
  `);
1564
1565
  return {
1565
1566
  status: "error",
@@ -1570,10 +1571,22 @@ async function runAgentLoop(options) {
1570
1571
  };
1571
1572
  }
1572
1573
  const toolResults = [];
1574
+ const textBlocks = response.content.filter((block) => block.type === "text");
1575
+ if (textBlocks.length > 0 && onStep) {
1576
+ const thinking = textBlocks.map((b) => b.text).join(`
1577
+ `);
1578
+ onStep({ type: "thinking", thinking, stepNumber });
1579
+ }
1573
1580
  for (const toolBlock of toolUseBlocks) {
1574
1581
  stepNumber++;
1575
1582
  const toolInput = toolBlock.input;
1583
+ if (onStep) {
1584
+ onStep({ type: "tool_call", toolName: toolBlock.name, toolInput, stepNumber });
1585
+ }
1576
1586
  const execResult = await executeTool(page, screenshotter, toolBlock.name, toolInput, { runId, scenarioSlug, stepNumber });
1587
+ if (onStep) {
1588
+ onStep({ type: "tool_result", toolName: toolBlock.name, toolResult: execResult.result, stepNumber });
1589
+ }
1577
1590
  if (execResult.screenshot) {
1578
1591
  screenshots.push({
1579
1592
  ...execResult.screenshot,
@@ -1715,7 +1728,20 @@ async function runSingleScenario(scenario, runId, options) {
1715
1728
  screenshotter,
1716
1729
  model,
1717
1730
  runId,
1718
- maxTurns: 30
1731
+ maxTurns: 30,
1732
+ onStep: (stepEvent) => {
1733
+ emit({
1734
+ type: `step:${stepEvent.type}`,
1735
+ scenarioId: scenario.id,
1736
+ scenarioName: scenario.name,
1737
+ runId,
1738
+ toolName: stepEvent.toolName,
1739
+ toolInput: stepEvent.toolInput,
1740
+ toolResult: stepEvent.toolResult,
1741
+ thinking: stepEvent.thinking,
1742
+ stepNumber: stepEvent.stepNumber
1743
+ });
1744
+ }
1719
1745
  });
1720
1746
  for (const ss of agentResult.screenshots) {
1721
1747
  createScreenshot({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hasna/testers",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "AI-powered QA testing CLI — spawns cheap AI agents to test web apps with headless browsers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",