@agent-scope/cli 1.20.3 → 1.20.4

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/index.d.cts CHANGED
@@ -363,6 +363,18 @@ interface LayoutShiftData {
363
363
  * Heuristic flags from §6.1 catalog applicable to interaction profiling.
364
364
  */
365
365
  type ProfileHeuristicFlag = "WASTED_RENDER" | "MEMO_INEFFECTIVE" | "EFFECT_EVERY_RENDER" | "MISSING_CLEANUP" | "HIGH_RENDER_COUNT" | "LAYOUT_SHIFT_DETECTED" | "SLOW_INTERACTION";
366
+ type ProfileValidity = "interaction" | "setup_only" | "no_interaction_requested";
367
+ interface SkippedInteraction {
368
+ index: number;
369
+ action: InteractionStep$1["action"];
370
+ target?: string;
371
+ reason: "not_found" | "missing_target" | "missing_value" | "unsupported";
372
+ }
373
+ interface InteractionExecutionSummary {
374
+ attempted: number;
375
+ executed: number;
376
+ skipped: number;
377
+ }
366
378
  /** Full interaction profile result per Spec §5. */
367
379
  interface InteractionProfile {
368
380
  /** Component name profiled. */
@@ -387,6 +399,18 @@ interface InteractionProfile {
387
399
  flags: ProfileHeuristicFlag[];
388
400
  /** Interaction steps that were replayed. */
389
401
  interaction: InteractionStep$1[];
402
+ /** Interaction execution counts for validity-aware profiling. */
403
+ interactionSteps: InteractionExecutionSummary;
404
+ /** Backwards-compatible attempted interaction count. */
405
+ interactionStepsAttempted: number;
406
+ /** Backwards-compatible executed interaction count. */
407
+ interactionStepsExecuted: number;
408
+ /** Backwards-compatible skipped interaction count. */
409
+ interactionStepsSkipped: number;
410
+ /** Details for each skipped interaction step. */
411
+ skippedInteractions: SkippedInteraction[];
412
+ /** Whether timing/flags reflect a real interaction or setup-only work. */
413
+ profileValidity: ProfileValidity;
390
414
  }
391
415
 
392
416
  /** Root cause categories per Instrumentation Spec §2.1 */
package/dist/index.d.ts CHANGED
@@ -363,6 +363,18 @@ interface LayoutShiftData {
363
363
  * Heuristic flags from §6.1 catalog applicable to interaction profiling.
364
364
  */
365
365
  type ProfileHeuristicFlag = "WASTED_RENDER" | "MEMO_INEFFECTIVE" | "EFFECT_EVERY_RENDER" | "MISSING_CLEANUP" | "HIGH_RENDER_COUNT" | "LAYOUT_SHIFT_DETECTED" | "SLOW_INTERACTION";
366
+ type ProfileValidity = "interaction" | "setup_only" | "no_interaction_requested";
367
+ interface SkippedInteraction {
368
+ index: number;
369
+ action: InteractionStep$1["action"];
370
+ target?: string;
371
+ reason: "not_found" | "missing_target" | "missing_value" | "unsupported";
372
+ }
373
+ interface InteractionExecutionSummary {
374
+ attempted: number;
375
+ executed: number;
376
+ skipped: number;
377
+ }
366
378
  /** Full interaction profile result per Spec §5. */
367
379
  interface InteractionProfile {
368
380
  /** Component name profiled. */
@@ -387,6 +399,18 @@ interface InteractionProfile {
387
399
  flags: ProfileHeuristicFlag[];
388
400
  /** Interaction steps that were replayed. */
389
401
  interaction: InteractionStep$1[];
402
+ /** Interaction execution counts for validity-aware profiling. */
403
+ interactionSteps: InteractionExecutionSummary;
404
+ /** Backwards-compatible attempted interaction count. */
405
+ interactionStepsAttempted: number;
406
+ /** Backwards-compatible executed interaction count. */
407
+ interactionStepsExecuted: number;
408
+ /** Backwards-compatible skipped interaction count. */
409
+ interactionStepsSkipped: number;
410
+ /** Details for each skipped interaction step. */
411
+ skippedInteractions: SkippedInteraction[];
412
+ /** Whether timing/flags reflect a real interaction or setup-only work. */
413
+ profileValidity: ProfileValidity;
390
414
  }
391
415
 
392
416
  /** Root cause categories per Instrumentation Spec §2.1 */
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ import { createRequire } from 'module';
9
9
  import { mkdir, access, writeFile } from 'fs/promises';
10
10
  import * as readline from 'readline';
11
11
  import { loadTrace, generateTest, getBrowserEntryScript } from '@agent-scope/playwright';
12
+ import { serialize } from '@agent-scope/core';
12
13
  import { chromium } from 'playwright';
13
14
  import { tmpdir } from 'os';
14
15
  import { createServer } from 'http';
@@ -2278,26 +2279,7 @@ function buildHookInstrumentationScript() {
2278
2279
  // Walk fiber tree and collect hook data per component
2279
2280
  var components = [];
2280
2281
 
2281
- function serializeValue(v) {
2282
- if (v === null) return null;
2283
- if (v === undefined) return undefined;
2284
- var t = typeof v;
2285
- if (t === "string" || t === "number" || t === "boolean") return v;
2286
- if (t === "function") return "[function " + (v.name || "anonymous") + "]";
2287
- if (Array.isArray(v)) {
2288
- try { return v.map(serializeValue); } catch(e) { return "[Array]"; }
2289
- }
2290
- if (t === "object") {
2291
- try {
2292
- var keys = Object.keys(v).slice(0, 5);
2293
- var out = {};
2294
- for (var k of keys) { out[k] = serializeValue(v[k]); }
2295
- if (Object.keys(v).length > 5) out["..."] = "(truncated)";
2296
- return out;
2297
- } catch(e) { return "[Object]"; }
2298
- }
2299
- return String(v);
2300
- }
2282
+ var serializeValue = __SCOPE_SERIALIZE_VALUE__;
2301
2283
 
2302
2284
  // Hook node classifiers (mirrors hooks-extractor.ts logic)
2303
2285
  var HookLayout = 0b00100;
@@ -2518,7 +2500,10 @@ async function runHooksProfiling(componentName, filePath, props) {
2518
2500
  if (renderError !== null) {
2519
2501
  throw new Error(`Component render error: ${renderError}`);
2520
2502
  }
2521
- const instrumentScript = buildHookInstrumentationScript();
2503
+ const instrumentScript = buildHookInstrumentationScript().replace(
2504
+ "__SCOPE_SERIALIZE_VALUE__",
2505
+ serialize.toString()
2506
+ );
2522
2507
  const raw = await page.evaluate(instrumentScript);
2523
2508
  const result = raw;
2524
2509
  if (result.error) {
@@ -2714,44 +2699,79 @@ function buildProfilingCollectScript() {
2714
2699
  `;
2715
2700
  }
2716
2701
  async function replayInteraction(page, steps) {
2717
- for (const step of steps) {
2702
+ let executed = 0;
2703
+ const skippedInteractions = [];
2704
+ const skip = (index, step, reason) => {
2705
+ skippedInteractions.push({ index, action: step.action, target: step.target, reason });
2706
+ };
2707
+ for (const [index, step] of steps.entries()) {
2718
2708
  switch (step.action) {
2719
2709
  case "click":
2720
- if (step.target) {
2721
- await page.click(step.target, { timeout: 5e3 }).catch(() => {
2722
- process.stderr.write(` \u26A0 click target "${step.target}" not found, skipping
2710
+ if (!step.target) {
2711
+ skip(index, step, "missing_target");
2712
+ break;
2713
+ }
2714
+ try {
2715
+ await page.click(step.target, { timeout: 5e3 });
2716
+ executed++;
2717
+ } catch {
2718
+ process.stderr.write(` \u26A0 click target "${step.target}" not found, skipping
2723
2719
  `);
2724
- });
2720
+ skip(index, step, "not_found");
2725
2721
  }
2726
2722
  break;
2727
2723
  case "fill":
2728
- if (step.target && step.value !== void 0) {
2729
- await page.fill(step.target, step.value, { timeout: 5e3 }).catch(() => {
2730
- process.stderr.write(` \u26A0 fill target "${step.target}" not found, skipping
2724
+ if (!step.target) {
2725
+ skip(index, step, "missing_target");
2726
+ break;
2727
+ }
2728
+ if (step.value === void 0) {
2729
+ skip(index, step, "missing_value");
2730
+ break;
2731
+ }
2732
+ try {
2733
+ await page.fill(step.target, step.value, { timeout: 5e3 });
2734
+ executed++;
2735
+ } catch {
2736
+ process.stderr.write(` \u26A0 fill target "${step.target}" not found, skipping
2731
2737
  `);
2732
- });
2738
+ skip(index, step, "not_found");
2733
2739
  }
2734
2740
  break;
2735
2741
  case "hover":
2736
- if (step.target) {
2737
- await page.hover(step.target, { timeout: 5e3 }).catch(() => {
2738
- process.stderr.write(` \u26A0 hover target "${step.target}" not found, skipping
2742
+ if (!step.target) {
2743
+ skip(index, step, "missing_target");
2744
+ break;
2745
+ }
2746
+ try {
2747
+ await page.hover(step.target, { timeout: 5e3 });
2748
+ executed++;
2749
+ } catch {
2750
+ process.stderr.write(` \u26A0 hover target "${step.target}" not found, skipping
2739
2751
  `);
2740
- });
2752
+ skip(index, step, "not_found");
2741
2753
  }
2742
2754
  break;
2743
2755
  case "press":
2744
- if (step.value) {
2745
- await page.keyboard.press(step.value);
2756
+ if (!step.value) {
2757
+ skip(index, step, "missing_value");
2758
+ break;
2746
2759
  }
2760
+ await page.keyboard.press(step.value);
2761
+ executed++;
2747
2762
  break;
2748
2763
  case "wait":
2749
2764
  await page.waitForTimeout(step.delay ?? 500);
2765
+ executed++;
2766
+ break;
2767
+ default:
2768
+ skip(index, step, "unsupported");
2750
2769
  break;
2751
2770
  }
2752
2771
  }
2772
+ return { executed, skippedInteractions };
2753
2773
  }
2754
- function analyzeProfileFlags(totalRenders, wastedRenders, timing, layoutShifts) {
2774
+ function analyzeProfileFlags(totalRenders, wastedRenders, timing, layoutShifts, profileValidity) {
2755
2775
  const flags = /* @__PURE__ */ new Set();
2756
2776
  if (wastedRenders !== null && wastedRenders > 0 && wastedRenders / Math.max(1, totalRenders) > 0.3) {
2757
2777
  flags.add("WASTED_RENDER");
@@ -2762,7 +2782,7 @@ function analyzeProfileFlags(totalRenders, wastedRenders, timing, layoutShifts)
2762
2782
  if (layoutShifts.cumulativeScore > 0.1) {
2763
2783
  flags.add("LAYOUT_SHIFT_DETECTED");
2764
2784
  }
2765
- if (timing.js > 100) {
2785
+ if (profileValidity === "interaction" && timing.js > 100) {
2766
2786
  flags.add("SLOW_INTERACTION");
2767
2787
  }
2768
2788
  return [...flags];
@@ -2801,13 +2821,18 @@ async function runInteractionProfile(componentName, filePath, props, interaction
2801
2821
  throw new Error(`Profiling setup failed: ${setupData.error}`);
2802
2822
  }
2803
2823
  const jsStart = Date.now();
2824
+ let executedInteractions = 0;
2825
+ let skippedInteractions = [];
2804
2826
  if (interaction.length > 0) {
2805
2827
  process.stderr.write(` Replaying ${interaction.length} interaction step(s)\u2026
2806
2828
  `);
2807
- await replayInteraction(page, interaction);
2829
+ const replayResult = await replayInteraction(page, interaction);
2830
+ executedInteractions = replayResult.executed;
2831
+ skippedInteractions = replayResult.skippedInteractions;
2808
2832
  await page.waitForTimeout(300);
2809
2833
  }
2810
2834
  const jsDuration = Date.now() - jsStart;
2835
+ const profileValidity = interaction.length === 0 ? "no_interaction_requested" : executedInteractions === 0 ? "setup_only" : "interaction";
2811
2836
  const collected = await page.evaluate(buildProfilingCollectScript());
2812
2837
  const profileData = collected;
2813
2838
  if (profileData.error) {
@@ -2825,7 +2850,13 @@ async function runInteractionProfile(componentName, filePath, props, interaction
2825
2850
  const totalRenders = profileData.commitCount ?? 0;
2826
2851
  const uniqueComponents = profileData.uniqueComponents ?? 0;
2827
2852
  const wastedRenders = profileData.wastedRenders ?? null;
2828
- const flags = analyzeProfileFlags(totalRenders, wastedRenders, timing, layoutShifts);
2853
+ const flags = analyzeProfileFlags(
2854
+ totalRenders,
2855
+ wastedRenders,
2856
+ timing,
2857
+ layoutShifts,
2858
+ profileValidity
2859
+ );
2829
2860
  return {
2830
2861
  component: componentName,
2831
2862
  totalRenders,
@@ -2839,7 +2870,17 @@ async function runInteractionProfile(componentName, filePath, props, interaction
2839
2870
  timing,
2840
2871
  layoutShifts,
2841
2872
  flags,
2842
- interaction
2873
+ interaction,
2874
+ interactionSteps: {
2875
+ attempted: interaction.length,
2876
+ executed: executedInteractions,
2877
+ skipped: skippedInteractions.length
2878
+ },
2879
+ interactionStepsAttempted: interaction.length,
2880
+ interactionStepsExecuted: executedInteractions,
2881
+ interactionStepsSkipped: skippedInteractions.length,
2882
+ skippedInteractions,
2883
+ profileValidity
2843
2884
  };
2844
2885
  } finally {
2845
2886
  await browser.close();
@@ -2939,6 +2980,7 @@ function flattenSerializedValue(sv) {
2939
2980
  case "string":
2940
2981
  case "number":
2941
2982
  case "boolean":
2983
+ case "date":
2942
2984
  return v.value;
2943
2985
  case "object": {
2944
2986
  if (!Array.isArray(v.entries)) return {};