@empiricalrun/test-gen 0.79.3 → 0.79.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.
Files changed (117) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/agent/chat/index.d.ts.map +1 -1
  3. package/dist/agent/chat/index.js +21 -2
  4. package/dist/agent/chat/prompt/pw-utils-docs.d.ts +1 -1
  5. package/dist/agent/chat/prompt/pw-utils-docs.d.ts.map +1 -1
  6. package/dist/agent/chat/prompt/pw-utils-docs.js +1 -1
  7. package/dist/agent/chat/prompt/trace-utils-docs.d.ts +2 -0
  8. package/dist/agent/chat/prompt/trace-utils-docs.d.ts.map +1 -0
  9. package/dist/agent/chat/prompt/trace-utils-docs.js +208 -0
  10. package/dist/agent/code-review/index.d.ts +1 -1
  11. package/dist/agent/code-review/index.d.ts.map +1 -1
  12. package/dist/agent/code-review/index.js +4 -4
  13. package/dist/agent/code-review/{parser.d.ts → xml-parser.d.ts} +1 -1
  14. package/dist/agent/code-review/xml-parser.d.ts.map +1 -0
  15. package/dist/agent/code-review/{parser.js → xml-parser.js} +1 -1
  16. package/dist/agent/fast-triage/index.d.ts.map +1 -1
  17. package/dist/agent/fast-triage/index.js +31 -3
  18. package/dist/agent/master/element-annotation.d.ts.map +1 -1
  19. package/dist/agent/master/element-annotation.js +2 -19
  20. package/dist/bin/index.js +0 -2
  21. package/dist/constants/index.d.ts +1 -1
  22. package/dist/constants/index.js +1 -1
  23. package/dist/dashboard/client.d.ts +5 -0
  24. package/dist/dashboard/client.d.ts.map +1 -1
  25. package/dist/dashboard/client.js +11 -0
  26. package/dist/dashboard/index.d.ts +1 -0
  27. package/dist/dashboard/index.d.ts.map +1 -1
  28. package/dist/dashboard/index.js +4 -1
  29. package/dist/dashboard/tool-response-from-sandbox.d.ts +5 -0
  30. package/dist/dashboard/tool-response-from-sandbox.d.ts.map +1 -0
  31. package/dist/dashboard/tool-response-from-sandbox.js +40 -0
  32. package/dist/dashboard/tool-response.d.ts +1 -0
  33. package/dist/dashboard/tool-response.d.ts.map +1 -1
  34. package/dist/dashboard/tool-response.js +1 -0
  35. package/dist/file-info/adapters/github/reader.d.ts.map +1 -1
  36. package/dist/file-info/adapters/github/reader.js +9 -3
  37. package/dist/generate-summary/generate-error-stack-summary.js +1 -4
  38. package/dist/generate-summary/generate-failed-step-screenshot-diff-summary.d.ts.map +1 -1
  39. package/dist/generate-summary/generate-failed-step-screenshot-diff-summary.js +8 -8
  40. package/dist/generate-summary/generate-grouped-summary.js +1 -4
  41. package/dist/generate-summary/merge-summary.js +1 -4
  42. package/dist/telemetry/index.d.ts +3 -3
  43. package/dist/telemetry/index.d.ts.map +1 -1
  44. package/dist/telemetry/index.js +8 -5
  45. package/dist/tools/analyse-video/index.js +1 -1
  46. package/dist/tools/definitions/publish-markdown-report.d.ts +8 -0
  47. package/dist/tools/definitions/publish-markdown-report.d.ts.map +1 -0
  48. package/dist/tools/definitions/publish-markdown-report.js +29 -0
  49. package/dist/tools/diagnosis-fetcher.d.ts +5 -0
  50. package/dist/tools/diagnosis-fetcher.d.ts.map +1 -1
  51. package/dist/tools/diagnosis-fetcher.js +17 -12
  52. package/dist/tools/executor/index.d.ts.map +1 -1
  53. package/dist/tools/executor/index.js +2 -2
  54. package/dist/tools/fetch-file/index.d.ts.map +1 -1
  55. package/dist/tools/fetch-file/index.js +11 -10
  56. package/dist/tools/fetch-file/utils.d.ts +13 -1
  57. package/dist/tools/fetch-file/utils.d.ts.map +1 -1
  58. package/dist/tools/fetch-file/utils.js +44 -118
  59. package/dist/tools/index.d.ts +1 -1
  60. package/dist/tools/index.d.ts.map +1 -1
  61. package/dist/tools/index.js +9 -5
  62. package/dist/tools/issues-v1/index.d.ts +0 -2
  63. package/dist/tools/issues-v1/index.d.ts.map +1 -1
  64. package/dist/tools/issues-v1/index.js +1 -5
  65. package/dist/tools/publish-markdown-report/index.d.ts +3 -0
  66. package/dist/tools/publish-markdown-report/index.d.ts.map +1 -0
  67. package/dist/tools/publish-markdown-report/index.js +196 -0
  68. package/dist/tools/test-gen-browser.js +1 -1
  69. package/dist/tools/trace-dot-zip/index.d.ts.map +1 -1
  70. package/dist/tools/trace-dot-zip/index.js +19 -9
  71. package/dist/utils/model.d.ts.map +1 -1
  72. package/dist/utils/model.js +2 -6
  73. package/package.json +2 -11
  74. package/tsconfig.tsbuildinfo +1 -1
  75. package/dist/agent/code-review/parser.d.ts.map +0 -1
  76. package/dist/agent/master/icon-descriptor/index.d.ts +0 -22
  77. package/dist/agent/master/icon-descriptor/index.d.ts.map +0 -1
  78. package/dist/agent/master/icon-descriptor/index.js +0 -249
  79. package/dist/agent/master/icon-descriptor/normalize-svg.d.ts +0 -2
  80. package/dist/agent/master/icon-descriptor/normalize-svg.d.ts.map +0 -1
  81. package/dist/agent/master/icon-descriptor/normalize-svg.js +0 -247
  82. package/dist/tools/issues-v1/create-issue.d.ts +0 -3
  83. package/dist/tools/issues-v1/create-issue.d.ts.map +0 -1
  84. package/dist/tools/issues-v1/create-issue.js +0 -72
  85. package/dist/tools/issues-v1/update-issue.d.ts +0 -3
  86. package/dist/tools/issues-v1/update-issue.d.ts.map +0 -1
  87. package/dist/tools/issues-v1/update-issue.js +0 -74
  88. package/dist/trace-utils/cli.d.ts +0 -3
  89. package/dist/trace-utils/cli.d.ts.map +0 -1
  90. package/dist/trace-utils/cli.js +0 -302
  91. package/dist/trace-utils/console.d.ts +0 -11
  92. package/dist/trace-utils/console.d.ts.map +0 -1
  93. package/dist/trace-utils/console.js +0 -74
  94. package/dist/trace-utils/dom-snapshot.d.ts +0 -19
  95. package/dist/trace-utils/dom-snapshot.d.ts.map +0 -1
  96. package/dist/trace-utils/dom-snapshot.js +0 -328
  97. package/dist/trace-utils/index.d.ts +0 -12
  98. package/dist/trace-utils/index.d.ts.map +0 -1
  99. package/dist/trace-utils/index.js +0 -28
  100. package/dist/trace-utils/network.d.ts +0 -16
  101. package/dist/trace-utils/network.d.ts.map +0 -1
  102. package/dist/trace-utils/network.js +0 -178
  103. package/dist/trace-utils/normalize-trace-url.d.ts +0 -2
  104. package/dist/trace-utils/normalize-trace-url.d.ts.map +0 -1
  105. package/dist/trace-utils/normalize-trace-url.js +0 -15
  106. package/dist/trace-utils/screenshots.d.ts +0 -24
  107. package/dist/trace-utils/screenshots.d.ts.map +0 -1
  108. package/dist/trace-utils/screenshots.js +0 -197
  109. package/dist/trace-utils/steps.d.ts +0 -10
  110. package/dist/trace-utils/steps.d.ts.map +0 -1
  111. package/dist/trace-utils/steps.js +0 -126
  112. package/dist/trace-utils/types.d.ts +0 -51
  113. package/dist/trace-utils/types.d.ts.map +0 -1
  114. package/dist/trace-utils/types.js +0 -2
  115. package/dist/utils/playwright-report-parser.d.ts +0 -2
  116. package/dist/utils/playwright-report-parser.d.ts.map +0 -1
  117. package/dist/utils/playwright-report-parser.js +0 -10
package/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # @empiricalrun/test-gen
2
2
 
3
+ ## 0.79.4
4
+
5
+ ### Patch Changes
6
+
7
+ - dc2e6c9: feat: dashboard api client upgrade
8
+
3
9
  ## 0.79.3
4
10
 
5
11
  ### Patch Changes
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAC;AAapE,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAKpC,qBAAa,SAAS,CAAC,CAAC,CAAE,SAAQ,SAAS,CAAC,CAAC,CAAC;IAC5C,SAAS,CAAC,QAAQ,IAAI,WAAW;IAiB3B,iBAAiB,CACrB,eAAe,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,GACvC,OAAO,CAAC,MAAM,CAAC;CAsGnB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/chat/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAC;AAsBpE,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAMpC,qBAAa,SAAS,CAAC,CAAC,CAAE,SAAQ,SAAS,CAAC,CAAC,CAAC;IAC5C,SAAS,CAAC,QAAQ,IAAI,WAAW;IA0B3B,iBAAiB,CACrB,eAAe,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,GACvC,OAAO,CAAC,MAAM,CAAC;CAyGnB"}
@@ -3,32 +3,49 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ChatAgent = void 0;
4
4
  const tools_1 = require("../../tools");
5
5
  const analyse_video_1 = require("../../tools/definitions/analyse-video");
6
+ const download_build_1 = require("../../tools/definitions/download-build");
7
+ const grep_1 = require("../../tools/definitions/grep");
8
+ const list_tests_and_projects_1 = require("../../tools/definitions/list-tests-and-projects");
9
+ const run_test_1 = require("../../tools/definitions/run-test");
6
10
  const diagnosis_fetcher_1 = require("../../tools/diagnosis-fetcher");
11
+ const fetch_file_1 = require("../../tools/fetch-file");
12
+ const list_environments_1 = require("../../tools/list-environments");
7
13
  const test_run_fetcher_1 = require("../../tools/test-run-fetcher");
8
14
  const base_1 = require("../base");
9
15
  const pw_utils_docs_1 = require("./prompt/pw-utils-docs");
10
16
  const repo_1 = require("./prompt/repo");
11
17
  const test_case_def_1 = require("./prompt/test-case-def");
18
+ const trace_utils_docs_1 = require("./prompt/trace-utils-docs");
12
19
  class ChatAgent extends base_1.BaseAgent {
13
20
  getTools() {
14
21
  const custom = [
15
22
  analyse_video_1.analyseVideo,
16
- ...tools_1.commonTools,
23
+ run_test_1.runTestTool,
24
+ grep_1.grepTool,
25
+ list_environments_1.listEnvironmentsTool,
26
+ download_build_1.downloadBuildTool,
27
+ fetch_file_1.fetchFileTool,
28
+ list_tests_and_projects_1.listProjectsTool,
29
+ list_tests_and_projects_1.listTestsForProjectTool,
17
30
  ...tools_1.testGenerationTools,
18
31
  test_run_fetcher_1.fetchTestRunDetailsTool,
19
32
  diagnosis_fetcher_1.fetchDiagnosisReportTool,
20
33
  ...(0, tools_1.textEditorToolsForModel)(this.selectedModel),
21
34
  tools_1.safeBashTool,
22
- ...(this.featureFlags.includes("useScrapeHtml") ? [tools_1.scrapeHtmlTool] : []),
35
+ ...(!(0, tools_1.hasBuiltInWebFetch)(this.selectedModel) ? [tools_1.scrapeHtmlTool] : []),
23
36
  ];
24
37
  return {
25
38
  custom,
26
39
  builtInTextEditor: (0, tools_1.hasBuiltInTextEditor)(this.selectedModel),
40
+ builtInWebFetch: (0, tools_1.hasBuiltInWebFetch)(this.selectedModel)
41
+ ? { enabled: true, max_uses: 1 }
42
+ : undefined,
27
43
  };
28
44
  }
29
45
  async buildSystemPrompt(repoInfoBuilder) {
30
46
  const repoInfo = await repoInfoBuilder();
31
47
  const repoContext = await (0, repo_1.getRepoContextPrompt)(repoInfo);
48
+ const hasTraceUtils = this.featureFlags.includes("enableTraceUtilsViaBash");
32
49
  const preamble = `
33
50
  You are a helpful assistant that can answer questions and help with tasks related to writing and maintaining Playwright tests.
34
51
 
@@ -121,6 +138,8 @@ ${test_case_def_1.testCasesDefinitionPrompt}
121
138
  # Recipes
122
139
  ${pw_utils_docs_1.playwrightUtilsDocs}
123
140
 
141
+ ${trace_utils_docs_1.traceUtilsDoc}
142
+
124
143
  # Repo context
125
144
  ${repoContext}
126
145
 
@@ -1,2 +1,2 @@
1
- export declare const playwrightUtilsDocs = "\nYou can refer to the following recipes to learn how to write tests for different scenarios.\n\n<email-automation>\n\n# Email automation\n\n## Example usage\n\n### Dynamic email\n\nThis dynamically generates a random email address that can \nbe used for the test (e.g. invite a new user).\n\n```ts\nimport { EmailClient } from \"@empiricalrun/playwright-utils\";\nimport { expect } from \"@playwright/test\";\n\nconst client = new EmailClient();\nconst address = client.getAddress();\n\n// Input the `address` in the application\n// that sends the email.\n\n// Get email received on the `address`\nconst email = await client.waitForEmail();\nexpect(\n email.links.find((l) => l.text === \"Join your team\")\n).toBeTruthy();\n```\n\n### Static email\n\nThis uses a known (static) email that can be used to login\ninto an application.\n\nThis needs an email id (e.g. `test-login-user`). The email id\nis appended with the domain (managed internally) to get the full\nemail address.\n\n```ts\nimport { EmailClient } from \"@empiricalrun/playwright-utils\";\n\nconst emailId = `test-login-user`;\n\nconst client = new EmailClient({ emailId });\nconst address = client.getAddress(); // Returns full address with domain\n\n// Get email received on the `address`\nconst email = await client.waitForEmail();\n\n// Get login OTP\nconst loginCode = email.codes[0];\n```\n\n</email-automation>\n\n<fixtures>\n\nThe playwright-utils package provides fixtures that wrap around Playwright's built-in\n`page`, `context` fixtures to provide a mouse highlighter (which makes it easier to\nsee actions taken in a video).\n\nTo use this, you can use the `baseTestFixture` and `extendExpect` imports\nin your fixtures file.\n\n```ts\nimport { test as base, expect as baseExpect } from \"@playwright/test\";\nimport { baseTestFixture, extendExpect } from \"@empiricalrun/playwright-utils/test\";\n\nexport const test = baseTestFixture(base);\nexport const expect = extendExpect(baseExpect);\n```\n\n### Get a new browser context\n\nThis package provides a fixture `customContextPageProvider` which is a good way to create\na fresh, new browser context, and a page inside it.\n\nThere are two benefits of using this to create contexts or pages:\n1. Videos get recorded and attached to the test report\n2. Mouse highlights are available\n\n```ts\nimport { test, expect } from \"./fixtures\";\n\ntest(\"Example test\", async ({ page: builtInPage, customContextPageProvider }) => {\n // builtInPage is from default browser context\n const { page: newPage, context } = await customContextPageProvider();\n // newPage is from this other browser context\n});\n```\n\ncustomContextPageProvider can accept options to customize the browser context.\n\n```\n customContextPageProvider: (\n options?: BrowserContextOptions,\n ) => Promise<{ context: BrowserContext; page: Page }>;\n```\n\nFor example, pass { storageState: undefined } to create a new browser context without\nthe auth state of the current browser context. This is useful for multi-user scenarios.\n\n</fixtures>\n\n<video-labels>\n\n# Video Labels\n\nPages generate video recordings after test execution, with 1 page generating 1 video file (webm). \n\nIf your test case relies on multiple pages (e.g. for multi-user or multi-app flows), it can get difficult to\nknow which page does \"video-1.webm\" belong to.\n\nTo solve this, you should set video labels for pages. This will enable you to identify videos faster.\n\n## Usage\n\n```typescript\nimport { setVideoLabel } from '@empiricalrun/playwright-utils/test';\n\ntest('my test', async ({ page }) => {\n setVideoLabel(page, 'checkout-flow');\n // Video will be saved as 'checkout-flow.webm'\n});\n```\n\n## Multiple Contexts\n\n```typescript\ntest('multi-user scenario', async ({ page, customContextPageProvider }) => {\n setVideoLabel(page, 'host-page');\n\n const { page: guestPage } = await customContextPageProvider({ storageState: undefined });\n setVideoLabel(guestPage, 'guest-page');\n // Videos saved as 'guest-page.webm' and 'host-page.webm'\n});\n```\n\n## Notes\n\n- The default behavior is to label videos for multiple pages as: `video-0.webm`, `video-1.webm`, etc.\n- If setVideoLabel is called twice for the same page, the last label will be set\n\n</video-labels>\n\n";
1
+ export declare const playwrightUtilsDocs = "\nYou can refer to the following recipes to learn how to write or diagnose tests for different scenarios.\n\n<email-automation>\n\n# Email automation\n\n## Example usage\n\n### Dynamic email\n\nThis dynamically generates a random email address that can \nbe used for the test (e.g. invite a new user).\n\n```ts\nimport { EmailClient } from \"@empiricalrun/playwright-utils\";\nimport { expect } from \"@playwright/test\";\n\nconst client = new EmailClient();\nconst address = client.getAddress();\n\n// Input the `address` in the application\n// that sends the email.\n\n// Get email received on the `address`\nconst email = await client.waitForEmail();\nexpect(\n email.links.find((l) => l.text === \"Join your team\")\n).toBeTruthy();\n```\n\n### Static email\n\nThis uses a known (static) email that can be used to login\ninto an application.\n\nThis needs an email id (e.g. `test-login-user`). The email id\nis appended with the domain (managed internally) to get the full\nemail address.\n\n```ts\nimport { EmailClient } from \"@empiricalrun/playwright-utils\";\n\nconst emailId = `test-login-user`;\n\nconst client = new EmailClient({ emailId });\nconst address = client.getAddress(); // Returns full address with domain\n\n// Get email received on the `address`\nconst email = await client.waitForEmail();\n\n// Get login OTP\nconst loginCode = email.codes[0];\n```\n\n</email-automation>\n\n<fixtures>\n\nThe playwright-utils package provides fixtures that wrap around Playwright's built-in\n`page`, `context` fixtures to provide a mouse highlighter (which makes it easier to\nsee actions taken in a video).\n\nTo use this, you can use the `baseTestFixture` and `extendExpect` imports\nin your fixtures file.\n\n```ts\nimport { test as base, expect as baseExpect } from \"@playwright/test\";\nimport { baseTestFixture, extendExpect } from \"@empiricalrun/playwright-utils/test\";\n\nexport const test = baseTestFixture(base);\nexport const expect = extendExpect(baseExpect);\n```\n\n### Get a new browser context\n\nThis package provides a fixture `customContextPageProvider` which is a good way to create\na fresh, new browser context, and a page inside it.\n\nThere are two benefits of using this to create contexts or pages:\n1. Videos get recorded and attached to the test report\n2. Mouse highlights are available\n\n```ts\nimport { test, expect } from \"./fixtures\";\n\ntest(\"Example test\", async ({ page: builtInPage, customContextPageProvider }) => {\n // builtInPage is from default browser context\n const { page: newPage, context } = await customContextPageProvider();\n // newPage is from this other browser context\n});\n```\n\ncustomContextPageProvider can accept options to customize the browser context.\n\n```\n customContextPageProvider: (\n options?: BrowserContextOptions,\n ) => Promise<{ context: BrowserContext; page: Page }>;\n```\n\nFor example, pass { storageState: undefined } to create a new browser context without\nthe auth state of the current browser context. This is useful for multi-user scenarios.\n\n</fixtures>\n\n<video-labels>\n\n# Video Labels\n\nPages generate video recordings after test execution, with 1 page generating 1 video file (webm). \n\nIf your test case relies on multiple pages (e.g. for multi-user or multi-app flows), it can get difficult to\nknow which page does \"video-1.webm\" belong to.\n\nTo solve this, you should set video labels for pages. This will enable you to identify videos faster.\n\n## Usage\n\n```typescript\nimport { setVideoLabel } from '@empiricalrun/playwright-utils/test';\n\ntest('my test', async ({ page }) => {\n setVideoLabel(page, 'checkout-flow');\n // Video will be saved as 'checkout-flow.webm'\n});\n```\n\n## Multiple Contexts\n\n```typescript\ntest('multi-user scenario', async ({ page, customContextPageProvider }) => {\n setVideoLabel(page, 'host-page');\n\n const { page: guestPage } = await customContextPageProvider({ storageState: undefined });\n setVideoLabel(guestPage, 'guest-page');\n // Videos saved as 'guest-page.webm' and 'host-page.webm'\n});\n```\n\n## Notes\n\n- The default behavior is to label videos for multiple pages as: `video-0.webm`, `video-1.webm`, etc.\n- If setVideoLabel is called twice for the same page, the last label will be set\n\n</video-labels>\n\n";
2
2
  //# sourceMappingURL=pw-utils-docs.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pw-utils-docs.d.ts","sourceRoot":"","sources":["../../../../src/agent/chat/prompt/pw-utils-docs.ts"],"names":[],"mappings":"AA4IA,eAAO,MAAM,mBAAmB,qsIAe/B,CAAC"}
1
+ {"version":3,"file":"pw-utils-docs.d.ts","sourceRoot":"","sources":["../../../../src/agent/chat/prompt/pw-utils-docs.ts"],"names":[],"mappings":"AA4IA,eAAO,MAAM,mBAAmB,itIAe/B,CAAC"}
@@ -139,7 +139,7 @@ test('multi-user scenario', async ({ page, customContextPageProvider }) => {
139
139
  - If setVideoLabel is called twice for the same page, the last label will be set
140
140
  `;
141
141
  exports.playwrightUtilsDocs = `
142
- You can refer to the following recipes to learn how to write tests for different scenarios.
142
+ You can refer to the following recipes to learn how to write or diagnose tests for different scenarios.
143
143
 
144
144
  <email-automation>
145
145
  ${emailRecipe}
@@ -0,0 +1,2 @@
1
+ export declare const traceUtilsDoc = "\n<trace-utils-cli>\n# Trace Utils\n\nCLI and library for inspecting Playwright `trace.zip` files.\n\n## Usage\n\nVia sandbox-cli:\n```bash\nsandbox-cli trace-utils <command> [options]\n```\n\n---\n\n## `steps`\n\nList all test steps from a trace.\n\n```bash\nsandbox-cli trace-utils steps --file <path> [--json]\n```\n\n| Option | Description | Default |\n|--------|-------------|---------|\n| `--file` | Path or URL to trace.zip | *required* |\n| `--json` | Output as JSON | `false` |\n\n---\n\n## `screenshots`\n\nExtract screenshots from a trace. Use either `--step` or `--start`/`--end` (one is required).\n\n```bash\n# By step ID\nsandbox-cli trace-utils screenshots --file <path> --step <id> [options]\n\n# By time range\nsandbox-cli trace-utils screenshots --file <path> --start <time> --end <time> [options]\n```\n\n| Option | Description | Default |\n|--------|-------------|---------|\n| `--file` | Path or URL to trace.zip | *required* |\n| `--step` | Step callId to extract around | - |\n| `--start` | Start time (HH:MM:SS.mmm) | - |\n| `--end` | End time (HH:MM:SS.mmm) | - |\n| `--page` | Page ID to filter by (required for multi-page traces with time-based extraction) | - |\n| `--before` | Milliseconds before step (only with --step) | `2000` |\n| `--after` | Milliseconds after step (only with --step) | `2000` |\n| `--max` | Maximum screenshots | `10` |\n| `--output` | Output directory | auto-generated in `/tmp` |\n\n**Examples:**\n\n```bash\n# 1. List steps to find callId or timestamps\nsandbox-cli trace-utils steps --file ./trace.zip\n\n# Output:\n# [test.step@36] locator.click(getByRole('button', { name: 'Create' })) (page@abc123)\n# 00:02:06.434 \u2192 00:02:06.543 (110ms)\n\n# 2a. Extract by step ID\nsandbox-cli trace-utils screenshots --file ./trace.zip --step \"test.step@36\"\n\n# 2b. Extract by time range (for multi-page traces, --page is required)\nsandbox-cli trace-utils screenshots --file ./trace.zip --start 00:02:06.000 --end 00:02:07.000 --page \"page@abc123\"\n```\n\n---\n\n## `network`\n\nSearch network requests. For multi-page traces, either `--step` or `--page` is required.\n\n```bash\nsandbox-cli trace-utils network --file <path> [filters] [options]\n```\n\n| Option | Description | Default |\n|--------|-------------|---------|\n| `--file` | Path or URL to trace.zip | *required* |\n| `--step <callId>` | Filter to requests during a specific step | - |\n| `--page` | Page ID to filter by (required for multi-page traces without --step) | - |\n| `--before <ms>` | Time before step to include (with --step) | `2000` |\n| `--after <ms>` | Time after step to include (with --step) | `2000` |\n| `--errors` | Show only failed requests (4xx, 5xx, no response) | - |\n| `--url <pattern>` | Filter by URL pattern (regex) | - |\n| `--method <method>` | Filter by HTTP method | - |\n| `--status <codes>` | Filter by status codes (e.g., `404,500`) | - |\n| `--json` | Output as JSON | `false` |\n\n**Examples:**\n\n```bash\n# Show all failed requests (for multi-page traces, specify --page)\nsandbox-cli trace-utils network --file ./trace.zip --errors --page \"page@abc123\"\n\n# Search for specific API calls\nsandbox-cli trace-utils network --file ./trace.zip --url \"/api/v1/users\" --page \"page@abc123\"\n\n# Failed POST requests\nsandbox-cli trace-utils network --file ./trace.zip --errors --method POST --page \"page@abc123\"\n\n# Network requests during a specific step (--page not required)\nsandbox-cli trace-utils network --file ./trace.zip --step \"pw:api@32\"\n```\n\n---\n\n## `console`\n\nView browser console output. For multi-page traces, `--page` is required.\n\n```bash\nsandbox-cli trace-utils console --file <path> [options]\n```\n\n| Option | Description | Default |\n|--------|-------------|---------|\n| `--file` | Path or URL to trace.zip | *required* |\n| `--page` | Page ID to filter by (required for multi-page traces) | - |\n| `--level` | Minimum level: `error`, `warning`, `info`, `debug` | `error` |\n| `--json` | Output as JSON | `false` |\n| `--search` | Filter logs containing text | - |\n\n---\n\n## `dom-snapshot`\n\nExtract DOM snapshot from a trace. Use either `--step` or `--time` (one is required).\n\n```bash\n# By step ID\nsandbox-cli trace-utils dom-snapshot --file <path> --step <id> [options]\n\n# By time (for multi-page traces, --page is required)\nsandbox-cli trace-utils dom-snapshot --file <path> --time <time> --page <pageId> [options]\n```\n\n| Option | Description | Default |\n|--------|-------------|---------|\n| `--file` | Path or URL to trace.zip | *required* |\n| `--step` | Step callId to extract snapshot for | - |\n| `--time` | Time to extract snapshot at (HH:MM:SS.mmm) | - |\n| `--page` | Page ID to filter by (required for multi-page traces with --time) | - |\n| `--type` | Snapshot type: `before`, `after`, `input` (only with --step) | `before` |\n| `--output` | Output file path | - |\n| `--json` | Output as JSON | `false` |\n\n**Snapshot types:**\n\n- `before` - DOM state when the step starts (default)\n- `after` - DOM state when the step completes\n- `input` - DOM state at the moment of user input (for click/fill actions)\n\n> **Tip for assertions:** For `expect` steps like `toBeVisible`, use `--type after` to see the DOM state when the assertion passed. The `before` snapshot may show a loading state if the test was waiting for an element to appear.\n\n**Examples:**\n\n```bash\n# 1. List steps to find callId\nsandbox-cli trace-utils steps --file ./trace.zip\n\n# 2. Extract DOM snapshot before a click step\nsandbox-cli trace-utils dom-snapshot --file ./trace.zip --step \"pw:api@32\" --type before\n\n# 3. Extract DOM snapshot after a step and save to file\nsandbox-cli trace-utils dom-snapshot --file ./trace.zip --step \"pw:api@32\" --type after --output ./snapshot.html\n\n# 4. For assertions, use --type after to see the DOM when assertion passed\nsandbox-cli trace-utils dom-snapshot --file ./trace.zip --step \"expect@43\" --type after\n\n# 5. Extract DOM snapshot at a specific time\nsandbox-cli trace-utils dom-snapshot --file ./trace.zip --time 00:02:06.434\n\n# 6. Get snapshot as JSON\nsandbox-cli trace-utils dom-snapshot --file ./trace.zip --step \"pw:api@32\" --json\n```\n\n**Output (default):**\n```\n# DOM Snapshot: locator.click\n# Step: pw:api@32\n# URL: https://example.com/dashboard\n# Viewport: 1280x720\n# Snapshot type: before\n\n<!DOCTYPE html>\n<html>\n <head>\n <title>Dashboard</title>\n ...\n </head>\n <body>\n ...\n </body>\n</html>\n```\n\n</trace-utils-cli>\n";
2
+ //# sourceMappingURL=trace-utils-docs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"trace-utils-docs.d.ts","sourceRoot":"","sources":["../../../../src/agent/chat/prompt/trace-utils-docs.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,aAAa,w4MA2MzB,CAAC"}
@@ -0,0 +1,208 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.traceUtilsDoc = void 0;
4
+ // TODO: Need a better way to keep this in sync with trace-utils/README.md
5
+ exports.traceUtilsDoc = `
6
+ <trace-utils-cli>
7
+ # Trace Utils
8
+
9
+ CLI and library for inspecting Playwright \`trace.zip\` files.
10
+
11
+ ## Usage
12
+
13
+ Via sandbox-cli:
14
+ \`\`\`bash
15
+ sandbox-cli trace-utils <command> [options]
16
+ \`\`\`
17
+
18
+ ---
19
+
20
+ ## \`steps\`
21
+
22
+ List all test steps from a trace.
23
+
24
+ \`\`\`bash
25
+ sandbox-cli trace-utils steps --file <path> [--json]
26
+ \`\`\`
27
+
28
+ | Option | Description | Default |
29
+ |--------|-------------|---------|
30
+ | \`--file\` | Path or URL to trace.zip | *required* |
31
+ | \`--json\` | Output as JSON | \`false\` |
32
+
33
+ ---
34
+
35
+ ## \`screenshots\`
36
+
37
+ Extract screenshots from a trace. Use either \`--step\` or \`--start\`/\`--end\` (one is required).
38
+
39
+ \`\`\`bash
40
+ # By step ID
41
+ sandbox-cli trace-utils screenshots --file <path> --step <id> [options]
42
+
43
+ # By time range
44
+ sandbox-cli trace-utils screenshots --file <path> --start <time> --end <time> [options]
45
+ \`\`\`
46
+
47
+ | Option | Description | Default |
48
+ |--------|-------------|---------|
49
+ | \`--file\` | Path or URL to trace.zip | *required* |
50
+ | \`--step\` | Step callId to extract around | - |
51
+ | \`--start\` | Start time (HH:MM:SS.mmm) | - |
52
+ | \`--end\` | End time (HH:MM:SS.mmm) | - |
53
+ | \`--page\` | Page ID to filter by (required for multi-page traces with time-based extraction) | - |
54
+ | \`--before\` | Milliseconds before step (only with --step) | \`2000\` |
55
+ | \`--after\` | Milliseconds after step (only with --step) | \`2000\` |
56
+ | \`--max\` | Maximum screenshots | \`10\` |
57
+ | \`--output\` | Output directory | auto-generated in \`/tmp\` |
58
+
59
+ **Examples:**
60
+
61
+ \`\`\`bash
62
+ # 1. List steps to find callId or timestamps
63
+ sandbox-cli trace-utils steps --file ./trace.zip
64
+
65
+ # Output:
66
+ # [test.step@36] locator.click(getByRole('button', { name: 'Create' })) (page@abc123)
67
+ # 00:02:06.434 → 00:02:06.543 (110ms)
68
+
69
+ # 2a. Extract by step ID
70
+ sandbox-cli trace-utils screenshots --file ./trace.zip --step "test.step@36"
71
+
72
+ # 2b. Extract by time range (for multi-page traces, --page is required)
73
+ sandbox-cli trace-utils screenshots --file ./trace.zip --start 00:02:06.000 --end 00:02:07.000 --page "page@abc123"
74
+ \`\`\`
75
+
76
+ ---
77
+
78
+ ## \`network\`
79
+
80
+ Search network requests. For multi-page traces, either \`--step\` or \`--page\` is required.
81
+
82
+ \`\`\`bash
83
+ sandbox-cli trace-utils network --file <path> [filters] [options]
84
+ \`\`\`
85
+
86
+ | Option | Description | Default |
87
+ |--------|-------------|---------|
88
+ | \`--file\` | Path or URL to trace.zip | *required* |
89
+ | \`--step <callId>\` | Filter to requests during a specific step | - |
90
+ | \`--page\` | Page ID to filter by (required for multi-page traces without --step) | - |
91
+ | \`--before <ms>\` | Time before step to include (with --step) | \`2000\` |
92
+ | \`--after <ms>\` | Time after step to include (with --step) | \`2000\` |
93
+ | \`--errors\` | Show only failed requests (4xx, 5xx, no response) | - |
94
+ | \`--url <pattern>\` | Filter by URL pattern (regex) | - |
95
+ | \`--method <method>\` | Filter by HTTP method | - |
96
+ | \`--status <codes>\` | Filter by status codes (e.g., \`404,500\`) | - |
97
+ | \`--json\` | Output as JSON | \`false\` |
98
+
99
+ **Examples:**
100
+
101
+ \`\`\`bash
102
+ # Show all failed requests (for multi-page traces, specify --page)
103
+ sandbox-cli trace-utils network --file ./trace.zip --errors --page "page@abc123"
104
+
105
+ # Search for specific API calls
106
+ sandbox-cli trace-utils network --file ./trace.zip --url "/api/v1/users" --page "page@abc123"
107
+
108
+ # Failed POST requests
109
+ sandbox-cli trace-utils network --file ./trace.zip --errors --method POST --page "page@abc123"
110
+
111
+ # Network requests during a specific step (--page not required)
112
+ sandbox-cli trace-utils network --file ./trace.zip --step "pw:api@32"
113
+ \`\`\`
114
+
115
+ ---
116
+
117
+ ## \`console\`
118
+
119
+ View browser console output. For multi-page traces, \`--page\` is required.
120
+
121
+ \`\`\`bash
122
+ sandbox-cli trace-utils console --file <path> [options]
123
+ \`\`\`
124
+
125
+ | Option | Description | Default |
126
+ |--------|-------------|---------|
127
+ | \`--file\` | Path or URL to trace.zip | *required* |
128
+ | \`--page\` | Page ID to filter by (required for multi-page traces) | - |
129
+ | \`--level\` | Minimum level: \`error\`, \`warning\`, \`info\`, \`debug\` | \`error\` |
130
+ | \`--json\` | Output as JSON | \`false\` |
131
+ | \`--search\` | Filter logs containing text | - |
132
+
133
+ ---
134
+
135
+ ## \`dom-snapshot\`
136
+
137
+ Extract DOM snapshot from a trace. Use either \`--step\` or \`--time\` (one is required).
138
+
139
+ \`\`\`bash
140
+ # By step ID
141
+ sandbox-cli trace-utils dom-snapshot --file <path> --step <id> [options]
142
+
143
+ # By time (for multi-page traces, --page is required)
144
+ sandbox-cli trace-utils dom-snapshot --file <path> --time <time> --page <pageId> [options]
145
+ \`\`\`
146
+
147
+ | Option | Description | Default |
148
+ |--------|-------------|---------|
149
+ | \`--file\` | Path or URL to trace.zip | *required* |
150
+ | \`--step\` | Step callId to extract snapshot for | - |
151
+ | \`--time\` | Time to extract snapshot at (HH:MM:SS.mmm) | - |
152
+ | \`--page\` | Page ID to filter by (required for multi-page traces with --time) | - |
153
+ | \`--type\` | Snapshot type: \`before\`, \`after\`, \`input\` (only with --step) | \`before\` |
154
+ | \`--output\` | Output file path | - |
155
+ | \`--json\` | Output as JSON | \`false\` |
156
+
157
+ **Snapshot types:**
158
+
159
+ - \`before\` - DOM state when the step starts (default)
160
+ - \`after\` - DOM state when the step completes
161
+ - \`input\` - DOM state at the moment of user input (for click/fill actions)
162
+
163
+ > **Tip for assertions:** For \`expect\` steps like \`toBeVisible\`, use \`--type after\` to see the DOM state when the assertion passed. The \`before\` snapshot may show a loading state if the test was waiting for an element to appear.
164
+
165
+ **Examples:**
166
+
167
+ \`\`\`bash
168
+ # 1. List steps to find callId
169
+ sandbox-cli trace-utils steps --file ./trace.zip
170
+
171
+ # 2. Extract DOM snapshot before a click step
172
+ sandbox-cli trace-utils dom-snapshot --file ./trace.zip --step "pw:api@32" --type before
173
+
174
+ # 3. Extract DOM snapshot after a step and save to file
175
+ sandbox-cli trace-utils dom-snapshot --file ./trace.zip --step "pw:api@32" --type after --output ./snapshot.html
176
+
177
+ # 4. For assertions, use --type after to see the DOM when assertion passed
178
+ sandbox-cli trace-utils dom-snapshot --file ./trace.zip --step "expect@43" --type after
179
+
180
+ # 5. Extract DOM snapshot at a specific time
181
+ sandbox-cli trace-utils dom-snapshot --file ./trace.zip --time 00:02:06.434
182
+
183
+ # 6. Get snapshot as JSON
184
+ sandbox-cli trace-utils dom-snapshot --file ./trace.zip --step "pw:api@32" --json
185
+ \`\`\`
186
+
187
+ **Output (default):**
188
+ \`\`\`
189
+ # DOM Snapshot: locator.click
190
+ # Step: pw:api@32
191
+ # URL: https://example.com/dashboard
192
+ # Viewport: 1280x720
193
+ # Snapshot type: before
194
+
195
+ <!DOCTYPE html>
196
+ <html>
197
+ <head>
198
+ <title>Dashboard</title>
199
+ ...
200
+ </head>
201
+ <body>
202
+ ...
203
+ </body>
204
+ </html>
205
+ \`\`\`
206
+
207
+ </trace-utils-cli>
208
+ `;
@@ -4,7 +4,7 @@ import { BaseAgent } from "../base";
4
4
  import { type CodeReviewResultV0, type CodeReviewResultV1, type CodeReviewResultV2, CodeReviewSeverity, CodeReviewVerdict } from "./types";
5
5
  export type { CodeReviewResultV1, CodeReviewResultV0, CodeReviewResultV2 };
6
6
  export { CodeReviewVerdict, CodeReviewSeverity };
7
- export { convertXmlToV2Format } from "./parser";
7
+ export { convertXmlToV2Format } from "./xml-parser";
8
8
  export type CodeReviewVersionedResult = CodeReviewResultV0 | CodeReviewResultV1 | CodeReviewResultV2;
9
9
  export declare class CodeReviewAgent<T> extends BaseAgent<T> {
10
10
  protected getTools(): ToolsForLLM;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/code-review/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,WAAW,EACZ,MAAM,uCAAuC,CAAC;AAC/C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAC;AAGpE,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAGpC,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,kBAAkB,EAClB,iBAAiB,EAClB,MAAM,SAAS,CAAC;AAEjB,YAAY,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,CAAC;AAC3E,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAEhD,MAAM,MAAM,yBAAyB,GACjC,kBAAkB,GAClB,kBAAkB,GAClB,kBAAkB,CAAC;AAEvB,qBAAa,eAAe,CAAC,CAAC,CAAE,SAAQ,SAAS,CAAC,CAAC,CAAC;IAClD,SAAS,CAAC,QAAQ,IAAI,WAAW;IAWjC,SAAS,IAAI,yBAAyB,GAAG,SAAS;cAmBlC,iBAAiB,CAC/B,eAAe,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,GACvC,OAAO,CAAC,MAAM,CAAC;CAiInB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/code-review/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,WAAW,EACZ,MAAM,uCAAuC,CAAC;AAC/C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAC;AAGpE,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEpC,OAAO,EACL,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,EACvB,kBAAkB,EAClB,iBAAiB,EAClB,MAAM,SAAS,CAAC;AAGjB,YAAY,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,CAAC;AAC3E,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,CAAC;AACjD,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAEpD,MAAM,MAAM,yBAAyB,GACjC,kBAAkB,GAClB,kBAAkB,GAClB,kBAAkB,CAAC;AAEvB,qBAAa,eAAe,CAAC,CAAC,CAAE,SAAQ,SAAS,CAAC,CAAC,CAAC;IAClD,SAAS,CAAC,QAAQ,IAAI,WAAW;IAWjC,SAAS,IAAI,yBAAyB,GAAG,SAAS;cAmBlC,iBAAiB,CAC/B,eAAe,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,GACvC,OAAO,CAAC,MAAM,CAAC;CAiInB"}
@@ -5,12 +5,12 @@ const tools_1 = require("../../tools");
5
5
  const fetch_session_diff_1 = require("../../tools/fetch-session-diff");
6
6
  const base_1 = require("../base");
7
7
  const repo_1 = require("../chat/prompt/repo");
8
- const parser_1 = require("./parser");
9
8
  const types_1 = require("./types");
10
9
  Object.defineProperty(exports, "CodeReviewSeverity", { enumerable: true, get: function () { return types_1.CodeReviewSeverity; } });
11
10
  Object.defineProperty(exports, "CodeReviewVerdict", { enumerable: true, get: function () { return types_1.CodeReviewVerdict; } });
12
- var parser_2 = require("./parser");
13
- Object.defineProperty(exports, "convertXmlToV2Format", { enumerable: true, get: function () { return parser_2.convertXmlToV2Format; } });
11
+ const xml_parser_1 = require("./xml-parser");
12
+ var xml_parser_2 = require("./xml-parser");
13
+ Object.defineProperty(exports, "convertXmlToV2Format", { enumerable: true, get: function () { return xml_parser_2.convertXmlToV2Format; } });
14
14
  class CodeReviewAgent extends base_1.BaseAgent {
15
15
  getTools() {
16
16
  const custom = [
@@ -35,7 +35,7 @@ class CodeReviewAgent extends base_1.BaseAgent {
35
35
  if (!text) {
36
36
  return undefined;
37
37
  }
38
- return (0, parser_1.convertXmlToV2Format)(text);
38
+ return (0, xml_parser_1.convertXmlToV2Format)(text);
39
39
  }
40
40
  async buildSystemPrompt(repoInfoBuilder) {
41
41
  const repoInfo = await repoInfoBuilder();
@@ -2,4 +2,4 @@ import { type CodeReviewResultV2 } from "./types";
2
2
  export type { CodeReviewLineComment, CodeReviewResultV0, CodeReviewResultV1, CodeReviewResultV2, } from "./types";
3
3
  export { CodeReviewSeverity, CodeReviewVerdict } from "./types";
4
4
  export declare function convertXmlToV2Format(output: string): CodeReviewResultV2;
5
- //# sourceMappingURL=parser.d.ts.map
5
+ //# sourceMappingURL=xml-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xml-parser.d.ts","sourceRoot":"","sources":["../../../src/agent/code-review/xml-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,KAAK,kBAAkB,EAGxB,MAAM,SAAS,CAAC;AAEjB,YAAY,EACV,qBAAqB,EACrB,kBAAkB,EAClB,kBAAkB,EAClB,kBAAkB,GACnB,MAAM,SAAS,CAAC;AAEjB,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAchE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,kBAAkB,CAmFvE"}
@@ -41,7 +41,7 @@ function convertXmlToV2Format(output) {
41
41
  : null;
42
42
  if (severity !== null) {
43
43
  lineComments.push({
44
- file: fileMatch[1].trim(),
44
+ file: fileMatch[1].trim().replace(/^\/repo\//, ""),
45
45
  line_start: parseInt(lineStartMatch[1].trim(), 10),
46
46
  line_end: parseInt(lineEndMatch[1].trim(), 10),
47
47
  severity: severity,
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/fast-triage/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAC;AASpE,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEpC,qBAAa,eAAe,CAAC,CAAC,CAAE,SAAQ,SAAS,CAAC,CAAC,CAAC;IAClD,SAAS,CAAC,QAAQ,IAAI,WAAW;cAiBjB,iBAAiB,CAC/B,eAAe,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,GACvC,OAAO,CAAC,MAAM,CAAC;CAqBnB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/agent/fast-triage/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qCAAqC,CAAC;AAapE,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAGpC,qBAAa,eAAe,CAAC,CAAC,CAAE,SAAQ,SAAS,CAAC,CAAC,CAAC;IAClD,SAAS,CAAC,QAAQ,IAAI,WAAW;cAkBjB,iBAAiB,CAC/B,eAAe,EAAE,MAAM,OAAO,CAAC,QAAQ,CAAC,GACvC,OAAO,CAAC,MAAM,CAAC;CA+CnB"}
@@ -2,13 +2,14 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FastTriageAgent = void 0;
4
4
  const tools_1 = require("../../tools");
5
- // import { traceUtils } from "../../tools/definitions/trace-utils";
6
5
  // import { grepTool } from "../../tools/definitions/grep";
7
6
  const diagnosis_fetcher_1 = require("../../tools/diagnosis-fetcher");
8
7
  const fetch_file_1 = require("../../tools/fetch-file");
9
8
  const list_environments_1 = require("../../tools/list-environments");
9
+ const publish_markdown_report_1 = require("../../tools/publish-markdown-report");
10
10
  const test_run_fetcher_1 = require("../../tools/test-run-fetcher");
11
11
  const base_1 = require("../base");
12
+ const trace_utils_docs_1 = require("../chat/prompt/trace-utils-docs");
12
13
  class FastTriageAgent extends base_1.BaseAgent {
13
14
  getTools() {
14
15
  const tools = [
@@ -16,8 +17,9 @@ class FastTriageAgent extends base_1.BaseAgent {
16
17
  test_run_fetcher_1.fetchTestRunDetailsTool,
17
18
  list_environments_1.listEnvironmentsTool,
18
19
  fetch_file_1.fetchFileTool,
19
- // TODO: Bring these back - once we have a remote tool executor for them
20
- // traceUtils,
20
+ tools_1.safeBashTool,
21
+ publish_markdown_report_1.publishMarkdownReportTool,
22
+ // TODO: Do we need grep?
21
23
  // grepTool,
22
24
  ...(0, tools_1.textEditorToolsForModel)(this.selectedModel),
23
25
  ];
@@ -28,6 +30,7 @@ class FastTriageAgent extends base_1.BaseAgent {
28
30
  }
29
31
  async buildSystemPrompt(repoInfoBuilder) {
30
32
  const _repoInfo = await repoInfoBuilder();
33
+ // TODO: Use repo knowledge - can that also replace "issues" as memory?
31
34
  return `
32
35
  You are a fast triage agent that quickly analyzes test failures. [PLACEHOLDER - to be refined]
33
36
 
@@ -44,6 +47,31 @@ Provide a concise summary of:
44
47
  - Why it failed (root cause)
45
48
  - Whether this is an app issue or a test issue
46
49
 
50
+ Use the bash tool to make use of trace-utils and fetch steps from traces,
51
+ errors in network calls, and screenshots around failures.
52
+
53
+ ${trace_utils_docs_1.traceUtilsDoc}
54
+
55
+ # Expected output
56
+
57
+ A well-structured markdown report published via the publishMarkdownReport tool.
58
+
59
+ Your report must include:
60
+ - What failed and why (root cause analysis)
61
+ - Whether this is an app issue or a test issue
62
+ - Evidence from the failure: include screenshots (as file:// URLs), network logs, console logs, etc.
63
+ - Evidence from the last passing run when available: include screenshots showing the expected behavior
64
+
65
+ Use trace-utils to extract screenshots from both failing and passing runs. Reference them in your
66
+ markdown using file:// URLs (e.g. file:///path/to/screenshot.png). The publishMarkdownReport tool
67
+ will upload these files and replace them with public URLs automatically.
68
+
69
+ As your final step, always call the publishMarkdownReport tool with your complete markdown report.
70
+
71
+ # Things to keep in mind
72
+
73
+ Follow the action, not just the assertion - The console showed deletePlayer error. I should have immediately looked at screenshots around the click that triggered it (test.step@126 - the "Yes" click) rather than the failing expect (expect@92).
74
+
47
75
  Today's date is ${new Date().toDateString()}
48
76
  `;
49
77
  }
@@ -1 +1 @@
1
- {"version":3,"file":"element-annotation.d.ts","sourceRoot":"","sources":["../../../src/agent/master/element-annotation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,GAAG,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAQlC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AA2DjD,wBAAsB,oBAAoB,CAAC,EACzC,kBAAkB,EAClB,WAAW,EACX,mBAAmB,EACnB,KAAK,EACL,GAAG,EACH,OAAO,EACP,UAAU,GACX,EAAE;IACD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B,UAAU,EAAE,oBAAoB,CAAC;CAClC,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA8C9B;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,UAAU,EACN,KAAK,GACL,UAAU,CAAC,IAAI,GACf,UAAU,CAAC,WAAW,GACtB,UAAU,CAAC,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC,CAAC;AAgBF,wBAAsB,iBAAiB,CAAC,EACtC,IAAI,EACJ,UAAU,EACV,KAAK,GACN,EAAE;IACD,IAAI,EAAE,IAAI,CAAC;IACX,UAAU,EAAE,oBAAoB,CAAC;IACjC,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,GAAG,OAAO,CAAC;IACV,cAAc,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACtD,gBAAgB,EAAE,MAAM,CAAC;IACzB,uBAAuB,EAAE,MAAM,CAAC;CACjC,CAAC,CAmFD"}
1
+ {"version":3,"file":"element-annotation.d.ts","sourceRoot":"","sources":["../../../src/agent/master/element-annotation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAiB,GAAG,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,IAAI,EAAE,MAAM,YAAY,CAAC;AAQlC,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AA0DjD,wBAAsB,oBAAoB,CAAC,EACzC,kBAAkB,EAClB,WAAW,EACX,mBAAmB,EACnB,KAAK,EACL,GAAG,EACH,OAAO,EACP,UAAU,GACX,EAAE;IACD,kBAAkB,EAAE,MAAM,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,KAAK,CAAC,EAAE,WAAW,CAAC;IACpB,GAAG,CAAC,EAAE,GAAG,CAAC;IACV,OAAO,CAAC,EAAE,oBAAoB,CAAC;IAC/B,UAAU,EAAE,oBAAoB,CAAC;CAClC,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA8C9B;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,UAAU,EACN,KAAK,GACL,UAAU,CAAC,IAAI,GACf,UAAU,CAAC,WAAW,GACtB,UAAU,CAAC,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACpC,CAAC;AAgBF,wBAAsB,iBAAiB,CAAC,EACtC,IAAI,EACJ,UAAU,EACV,KAAK,GACN,EAAE;IACD,IAAI,EAAE,IAAI,CAAC;IACX,UAAU,EAAE,oBAAoB,CAAC;IACjC,KAAK,CAAC,EAAE,WAAW,CAAC;CACrB,GAAG,OAAO,CAAC;IACV,cAAc,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACtD,gBAAgB,EAAE,MAAM,CAAC;IACzB,uBAAuB,EAAE,MAAM,CAAC;CACjC,CAAC,CAmFD"}
@@ -5,7 +5,6 @@ exports.getAnnotationKeys = getAnnotationKeys;
5
5
  const llm_1 = require("@empiricalrun/llm");
6
6
  const constants_1 = require("../../constants");
7
7
  const promptTemplate_0 = "{{#section \"system\"}}\nYou are an expert in describing the images and it's content. You need to provide the descriptions of annotated elements\npresent in the image.\n\nYou will be provided with an annotated screenshot where interact-able / clickable elements are annotated. The annotation\nis done by drawing a red box around the element and a small yellow box on it which contains unique element id.\n\nYou are given a Annotations which contains list of unique element id and description of the element separated by \":\".\n\nYou are also given the description of the element on which the action needs to be taken. The description includes\ninformation about how the element looks, it's position etc.\n\nYour task is to provide the annotation of the element on which the action needs to be performed based on the element\ndescription.\n\nFollow steps to fulfil your task:\n- Using the list of all element Ids provided to you, map all the element Ids on the annotated screen and describe each\nelement.\n- For describing each element Id\n-- iterate over each element Id in annotation list\n-- check if the description is already present for the element Id in the Annotation provided to you. If present skip\ndescribing it and use it as is.\n-- if the description is NA, then identify the element in the annotated screenshot and describe it using the image or\nicon enclosed in the element.\n- Respond with the mapped element Ids as \"enriched_annotations\"\n- Based on the description provided to you and the enriched annotations, first identify the element Id whose description\nmatches the task provided\n\nNote:\n- Ensure providing the description of all the elements in the list.\n- Don't update the description if its already present in the given annotations\n- Replace all the \"NA\" with description of the element. Its position, how does it look like etc.\n- There should be no \"NA\" present in any of the element description\n{{/section}}\n\n{{#section \"user\"}}\nElement description:\n{{elementDescription}}\n\nAnnotations:\n{{annotations}}\n\n{{image annotatedScreenshot}}\n{{/section}}";
8
- const icon_descriptor_1 = require("./icon-descriptor");
9
8
  const annotationToolAction = {
10
9
  name: "element_annotation",
11
10
  schema: {
@@ -163,29 +162,13 @@ async function getAnnotationKeys({ page, preference, trace, }) {
163
162
  annotatedPageScreenshot,
164
163
  };
165
164
  }
166
- async function enrichAnnotations(annotatedElements, pageHtml, trace) {
167
- // create icon descriptor span
168
- const iconDescriptionSpan = trace?.span({
169
- name: "describe-icons",
170
- });
165
+ async function enrichAnnotations(annotatedElements, _pageHtml, _trace) {
171
166
  const results = [];
172
167
  for (const element of annotatedElements) {
173
168
  const text = element.innerText?.trim() || element.placeholder?.trim();
174
- if (text) {
175
- results.push({
176
- elementID: element.elementID,
177
- text,
178
- });
179
- continue;
180
- }
181
- const description = await (0, icon_descriptor_1.getIconDescription)({
182
- htmlString: element.outerHTML,
183
- pageHtml: pageHtml,
184
- trace: iconDescriptionSpan,
185
- });
186
169
  results.push({
187
170
  elementID: element.elementID,
188
- text: description || "NA",
171
+ text: text || "NA",
189
172
  });
190
173
  }
191
174
  return results;
package/dist/bin/index.js CHANGED
@@ -14,7 +14,6 @@ const client_1 = require("../dashboard/client");
14
14
  const recorder_1 = require("../recorder");
15
15
  const validation_1 = require("../recorder/validation");
16
16
  const test_build_1 = require("../test-build");
17
- const cli_2 = require("../trace-utils/cli");
18
17
  const environments_1 = require("./environments");
19
18
  const setup_1 = require("./setup");
20
19
  const utils_1 = require("./utils");
@@ -226,7 +225,6 @@ async function main() {
226
225
  }
227
226
  process.exit(0);
228
227
  });
229
- program.addCommand((0, cli_2.buildTraceUtilsCommand)());
230
228
  program.parse(process.argv);
231
229
  }
232
230
  main().catch((error) => {
@@ -6,7 +6,7 @@ export declare const DEFAULT_MODEL_PARAMETERS: ModelParameters;
6
6
  export declare const DEFAULT_O1_MODEL_PARAMETERS: ModelParameters;
7
7
  export declare const VIDEO_ANALYSIS: {
8
8
  readonly FRAME_INDEX_PADDING: 6;
9
- readonly DEFAULT_MODEL: "claude-sonnet-4-20250514";
9
+ readonly DEFAULT_MODEL: "claude-sonnet-4-6";
10
10
  readonly DEFAULT_FPS: 25;
11
11
  readonly DEFAULT_THRESHOLD: 0.01;
12
12
  readonly DEFAULT_FALLBACK_FPS: 30;
@@ -21,7 +21,7 @@ exports.VIDEO_ANALYSIS = {
21
21
  // Frame processing
22
22
  FRAME_INDEX_PADDING: 6,
23
23
  // Default analysis parameters
24
- DEFAULT_MODEL: "claude-sonnet-4-20250514",
24
+ DEFAULT_MODEL: "claude-sonnet-4-6",
25
25
  DEFAULT_FPS: 25,
26
26
  DEFAULT_THRESHOLD: 0.01,
27
27
  DEFAULT_FALLBACK_FPS: 30,
@@ -32,6 +32,11 @@ export declare class DashboardAPIClient implements IDashboardAPIClient {
32
32
  path: string;
33
33
  body?: any;
34
34
  }): Promise<T>;
35
+ callWebhookSiteProxy<T>({ method, path, body, }: {
36
+ method: "GET" | "POST" | "PATCH" | "PUT" | "DELETE";
37
+ path: string;
38
+ body?: any;
39
+ }): Promise<T>;
35
40
  getBaseUrl(): string;
36
41
  ensureUserIsAuthenticated(): Promise<void>;
37
42
  private _makeRequest;
@@ -1 +1 @@
1
- {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/dashboard/client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,cAAc,EACf,MAAM,qCAAqC,CAAC;AAY7C,MAAM,MAAM,kBAAkB,GAC1B,mBAAmB,GACnB,iBAAiB,GACjB,YAAY,CAAC;AAEjB,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC;IACxC,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,IAAI,CAAC,CAAC;AAEV,qBAAa,kBAAmB,YAAW,mBAAmB;IAC5D,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,aAAa,CAAC,CAAS;IAC/B,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAC,CAAgB;gBAE1B,EACV,QAAQ,EACR,aAAa,EACb,SAAS,EACT,OAAO,EACP,aAAa,GACd,EAAE;QACD,QAAQ,EAAE,kBAAkB,CAAC;QAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,aAAa,CAAC,EAAE,aAAa,CAAC;KAC/B;YAwBa,SAAS;IAOjB,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;IA+CjE,eAAe,CAAC,CAAC,EAAE,EACvB,MAAM,EACN,GAAG,EACH,IAAI,GACL,EAAE;QACD,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;QACpD,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,CAAC,EAAE,GAAG,CAAC;KACZ,GAAG,OAAO,CAAC,CAAC,CAAC;IAYR,kBAAkB,CAAC,CAAC,EAAE,EAC1B,MAAM,EACN,IAAI,EACJ,IAAI,GACL,EAAE;QACD,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;QACpD,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,GAAG,CAAC;KACZ,GAAG,OAAO,CAAC,CAAC,CAAC;IAYd,UAAU,IAAI,MAAM;IAId,yBAAyB,IAAI,OAAO,CAAC,IAAI,CAAC;YAmBlC,YAAY;YAoDZ,gBAAgB;CAyC/B;AAED,eAAO,MAAM,SAAS,oBAEpB,CAAC"}
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/dashboard/client.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,mBAAmB,EACnB,cAAc,EACf,MAAM,qCAAqC,CAAC;AAY7C,MAAM,MAAM,kBAAkB,GAC1B,mBAAmB,GACnB,iBAAiB,GACjB,YAAY,CAAC;AAEjB,MAAM,MAAM,aAAa,GAAG,MAAM,OAAO,CAAC;IACxC,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,IAAI,CAAC,CAAC;AAEV,qBAAa,kBAAmB,YAAW,mBAAmB;IAC5D,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAAqB;IACrC,OAAO,CAAC,aAAa,CAAC,CAAS;IAC/B,OAAO,CAAC,SAAS,CAAC,CAAS;IAC3B,OAAO,CAAC,aAAa,CAAC,CAAgB;gBAE1B,EACV,QAAQ,EACR,aAAa,EACb,SAAS,EACT,OAAO,EACP,aAAa,GACd,EAAE;QACD,QAAQ,EAAE,kBAAkB,CAAC;QAC7B,aAAa,CAAC,EAAE,MAAM,CAAC;QACvB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,aAAa,CAAC,EAAE,aAAa,CAAC;KAC/B;YAwBa,SAAS;IAOjB,OAAO,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,CAAC,CAAC;IA+CjE,eAAe,CAAC,CAAC,EAAE,EACvB,MAAM,EACN,GAAG,EACH,IAAI,GACL,EAAE;QACD,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;QACpD,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,CAAC,EAAE,GAAG,CAAC;KACZ,GAAG,OAAO,CAAC,CAAC,CAAC;IAYR,kBAAkB,CAAC,CAAC,EAAE,EAC1B,MAAM,EACN,IAAI,EACJ,IAAI,GACL,EAAE;QACD,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;QACpD,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,GAAG,CAAC;KACZ,GAAG,OAAO,CAAC,CAAC,CAAC;IAYR,oBAAoB,CAAC,CAAC,EAAE,EAC5B,MAAM,EACN,IAAI,EACJ,IAAI,GACL,EAAE;QACD,MAAM,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,GAAG,QAAQ,CAAC;QACpD,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,GAAG,CAAC;KACZ,GAAG,OAAO,CAAC,CAAC,CAAC;IAYd,UAAU,IAAI,MAAM;IAKd,yBAAyB,IAAI,OAAO,CAAC,IAAI,CAAC;YAmBlC,YAAY;YAoDZ,gBAAgB;CAyC/B;AAED,eAAO,MAAM,SAAS,oBAEpB,CAAC"}
@@ -102,6 +102,17 @@ class DashboardAPIClient {
102
102
  body: requestBody,
103
103
  });
104
104
  }
105
+ async callWebhookSiteProxy({ method, path, body, }) {
106
+ const requestBody = {
107
+ method,
108
+ path,
109
+ body,
110
+ };
111
+ return await this.request(`/api/webhook-site/proxy`, {
112
+ method: "POST",
113
+ body: requestBody,
114
+ });
115
+ }
105
116
  getBaseUrl() {
106
117
  return this.baseUrl;
107
118
  }
@@ -1,4 +1,5 @@
1
1
  export { DashboardAPIClient } from "./client";
2
2
  export { sendErrorToolResponse, sendToolResponse } from "./tool-response";
3
+ export { sendErrorToolResponseFromSandbox, sendToolResponseFromSandbox, } from "./tool-response-from-sandbox";
3
4
  export { HTTPError, NonRetryableHTTPError, RetryableHTTPError } from "./types";
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dashboard/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dashboard/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAC9C,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EACL,gCAAgC,EAChC,2BAA2B,GAC5B,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAC"}