pi-ui-extend 1.0.41 → 1.0.45

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 (236) hide show
  1. package/README.md +50 -13
  2. package/dist/app/app.d.ts +13 -0
  3. package/dist/app/app.js +158 -8
  4. package/dist/app/cli/install.js +2 -2
  5. package/dist/app/commands/command-controller.d.ts +4 -0
  6. package/dist/app/commands/command-controller.js +11 -0
  7. package/dist/app/commands/command-git-actions.d.ts +25 -0
  8. package/dist/app/commands/command-git-actions.js +381 -0
  9. package/dist/app/commands/command-host.d.ts +4 -0
  10. package/dist/app/commands/command-host.js +5 -2
  11. package/dist/app/commands/command-model-actions.d.ts +1 -0
  12. package/dist/app/commands/command-model-actions.js +57 -35
  13. package/dist/app/commands/command-navigation-actions.d.ts +1 -0
  14. package/dist/app/commands/command-navigation-actions.js +16 -3
  15. package/dist/app/commands/command-registry.d.ts +2 -0
  16. package/dist/app/commands/command-registry.js +15 -1
  17. package/dist/app/commands/command-session-actions.js +9 -3
  18. package/dist/app/commands/reload-context-inventory.d.ts +11 -0
  19. package/dist/app/commands/reload-context-inventory.js +70 -0
  20. package/dist/app/extensions/extension-actions-controller.d.ts +1 -0
  21. package/dist/app/extensions/extension-actions-controller.js +6 -0
  22. package/dist/app/extensions/subagent-catalog-state.d.ts +9 -0
  23. package/dist/app/extensions/subagent-catalog-state.js +23 -0
  24. package/dist/app/input/autocomplete-controller.js +61 -34
  25. package/dist/app/input/input-action-controller.d.ts +2 -0
  26. package/dist/app/input/input-action-controller.js +8 -1
  27. package/dist/app/input/input-controller.d.ts +2 -1
  28. package/dist/app/input/input-controller.js +7 -2
  29. package/dist/app/input/prompt-enhancer-controller.js +53 -35
  30. package/dist/app/input/voice-controller.d.ts +41 -45
  31. package/dist/app/input/voice-controller.js +351 -419
  32. package/dist/app/model/model-usage-controller.js +17 -7
  33. package/dist/app/model/model-usage-status.d.ts +4 -1
  34. package/dist/app/model/model-usage-status.js +270 -46
  35. package/dist/app/popup/menu-items-controller.d.ts +13 -3
  36. package/dist/app/popup/menu-items-controller.js +37 -21
  37. package/dist/app/popup/popup-action-controller.d.ts +12 -2
  38. package/dist/app/popup/popup-action-controller.js +77 -24
  39. package/dist/app/popup/popup-menu-controller.d.ts +36 -14
  40. package/dist/app/popup/popup-menu-controller.js +239 -69
  41. package/dist/app/rendering/dcp-stats.d.ts +9 -4
  42. package/dist/app/rendering/dcp-stats.js +40 -425
  43. package/dist/app/rendering/editor-panels.js +10 -4
  44. package/dist/app/rendering/popup-menu-renderer.d.ts +3 -5
  45. package/dist/app/rendering/popup-menu-renderer.js +42 -37
  46. package/dist/app/rendering/render-controller.js +23 -2
  47. package/dist/app/rendering/status-line-renderer.d.ts +5 -0
  48. package/dist/app/rendering/status-line-renderer.js +41 -5
  49. package/dist/app/rendering/tab-line-renderer.js +26 -20
  50. package/dist/app/runtime.d.ts +12 -1
  51. package/dist/app/runtime.js +120 -12
  52. package/dist/app/screen/mouse-controller.d.ts +2 -0
  53. package/dist/app/screen/mouse-controller.js +17 -7
  54. package/dist/app/screen/status-controller.d.ts +4 -0
  55. package/dist/app/screen/status-controller.js +5 -0
  56. package/dist/app/session/lazy-session-manager.js +34 -0
  57. package/dist/app/session/session-event-controller.d.ts +1 -0
  58. package/dist/app/session/session-event-controller.js +10 -1
  59. package/dist/app/session/session-history.d.ts +1 -0
  60. package/dist/app/session/session-history.js +12 -1
  61. package/dist/app/session/session-lifecycle-controller.d.ts +7 -1
  62. package/dist/app/session/session-lifecycle-controller.js +16 -1
  63. package/dist/app/session/tabs-controller.d.ts +26 -1
  64. package/dist/app/session/tabs-controller.js +454 -147
  65. package/dist/app/subagents/subagents-files.js +60 -1
  66. package/dist/app/subagents/subagents-model.d.ts +1 -0
  67. package/dist/app/subagents/subagents-model.js +18 -2
  68. package/dist/app/subagents/subagents-widget-controller.d.ts +1 -0
  69. package/dist/app/subagents/subagents-widget-controller.js +6 -0
  70. package/dist/app/types.d.ts +16 -1
  71. package/dist/app/workspace/workspace-actions-controller.js +10 -2
  72. package/dist/app/workspace/workspace-undo.d.ts +1 -0
  73. package/dist/app/workspace/workspace-undo.js +1 -0
  74. package/dist/bundled-extensions/question/index.js +9 -1
  75. package/dist/bundled-extensions/question/remote.d.ts +4 -0
  76. package/dist/bundled-extensions/question/remote.js +33 -0
  77. package/dist/bundled-extensions/telegram-connector/bot.d.ts +43 -0
  78. package/dist/bundled-extensions/telegram-connector/bot.js +166 -0
  79. package/dist/bundled-extensions/telegram-connector/config.d.ts +8 -0
  80. package/dist/bundled-extensions/telegram-connector/config.js +87 -0
  81. package/dist/bundled-extensions/telegram-connector/coordinator.d.ts +66 -0
  82. package/dist/bundled-extensions/telegram-connector/coordinator.js +413 -0
  83. package/dist/bundled-extensions/telegram-connector/index.d.ts +3 -0
  84. package/dist/bundled-extensions/telegram-connector/index.js +195 -0
  85. package/dist/bundled-extensions/terminal-bell/index.d.ts +0 -8
  86. package/dist/bundled-extensions/terminal-bell/index.js +0 -76
  87. package/dist/bundled-extensions/workspace-undo/index.d.ts +26 -0
  88. package/dist/bundled-extensions/workspace-undo/index.js +191 -0
  89. package/dist/config.d.ts +21 -2
  90. package/dist/config.js +159 -32
  91. package/dist/default-pix-config.js +25 -5
  92. package/dist/schemas/index.d.ts +1 -0
  93. package/dist/schemas/index.js +1 -0
  94. package/dist/schemas/pi-tools-suite-schema.d.ts +88 -62
  95. package/dist/schemas/pi-tools-suite-schema.js +54 -83
  96. package/dist/schemas/pix-schema.d.ts +19 -2
  97. package/dist/schemas/pix-schema.js +47 -5
  98. package/dist/schemas/tasks-schema.d.ts +18 -0
  99. package/dist/schemas/tasks-schema.js +41 -0
  100. package/docs/concurrency.md +9 -1
  101. package/docs/desktop-mvp.md +22 -6
  102. package/docs/desktop-task-manager.md +148 -84
  103. package/docs/release.md +30 -6
  104. package/external/pi-tools-suite/README.md +197 -84
  105. package/external/pi-tools-suite/docs/evals.md +1 -1
  106. package/external/pi-tools-suite/docs/session-recovery.md +47 -14
  107. package/external/pi-tools-suite/docs/subagent-model-pools.md +48 -30
  108. package/external/pi-tools-suite/docs/ui-qa-subagent.md +440 -0
  109. package/external/pi-tools-suite/package.json +6 -1
  110. package/external/pi-tools-suite/src/antigravity-auth/auth-store.ts +2 -1
  111. package/external/pi-tools-suite/src/antigravity-auth/constants.ts +9 -3
  112. package/external/pi-tools-suite/src/antigravity-auth/headers.ts +41 -3
  113. package/external/pi-tools-suite/src/antigravity-auth/models.ts +94 -21
  114. package/external/pi-tools-suite/src/antigravity-auth/oauth.ts +5 -17
  115. package/external/pi-tools-suite/src/antigravity-auth/payload.ts +72 -7
  116. package/external/pi-tools-suite/src/antigravity-auth/stream.ts +13 -1
  117. package/external/pi-tools-suite/src/async-subagents/agents/frontier-review.md +23 -0
  118. package/external/pi-tools-suite/src/async-subagents/agents/implement.md +1 -1
  119. package/external/pi-tools-suite/src/async-subagents/agents/presets.jsonc +16 -0
  120. package/external/pi-tools-suite/src/async-subagents/agents/research.md +5 -3
  121. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/browser.mjs +346 -0
  122. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/desktop.mjs +1038 -0
  123. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/backends/tui.mjs +759 -0
  124. package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/scripts/browser-qa-runner.mjs +32 -23
  125. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/chrome-devtools/chrome-devtools-provider.mjs +895 -0
  126. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/linux/linux-atspi.py +494 -0
  127. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/macos/macos-accessibility.swift +1085 -0
  128. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/native-terminal/bridge-client.mjs +50 -0
  129. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/native-terminal/native-terminal-host.mjs +501 -0
  130. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/drivers/windows/windows-uia.ps1 +454 -0
  131. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-auth.md +114 -0
  132. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-chrome-devtools.md +91 -0
  133. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser-playwright.md +145 -0
  134. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/browser.md +82 -0
  135. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-linux.md +26 -0
  136. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-macos.md +29 -0
  137. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop-windows.md +23 -0
  138. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/desktop.md +54 -0
  139. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui-native-terminal.md +59 -0
  140. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui-pty.md +37 -0
  141. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/guides/tui.md +60 -0
  142. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa/scripts/ui-qa-runner.mjs +535 -0
  143. package/external/pi-tools-suite/src/async-subagents/agents/ui-qa.md +75 -0
  144. package/external/pi-tools-suite/src/async-subagents/commands.ts +15 -71
  145. package/external/pi-tools-suite/src/async-subagents/core/activity.ts +33 -0
  146. package/external/pi-tools-suite/src/async-subagents/core/agent-catalog.ts +5 -4
  147. package/external/pi-tools-suite/src/async-subagents/core/agent-strategy.ts +1 -1
  148. package/external/pi-tools-suite/src/async-subagents/core/agents-dir.ts +8 -5
  149. package/external/pi-tools-suite/src/async-subagents/core/browser-qa.ts +16 -2
  150. package/external/pi-tools-suite/src/async-subagents/core/config.ts +160 -327
  151. package/external/pi-tools-suite/src/async-subagents/core/model-selection.ts +2 -1
  152. package/external/pi-tools-suite/src/async-subagents/core/registry.ts +158 -33
  153. package/external/pi-tools-suite/src/async-subagents/core/routing.ts +21 -5
  154. package/external/pi-tools-suite/src/async-subagents/core/spawn.ts +40 -26
  155. package/external/pi-tools-suite/src/async-subagents/core/state.ts +40 -0
  156. package/external/pi-tools-suite/src/async-subagents/core/types.ts +7 -0
  157. package/external/pi-tools-suite/src/async-subagents/core/ultrawork-auto.ts +51 -45
  158. package/external/pi-tools-suite/src/async-subagents/index.ts +69 -5
  159. package/external/pi-tools-suite/src/async-subagents/lib.ts +16 -9
  160. package/external/pi-tools-suite/src/async-subagents/tools/spawn.ts +8 -5
  161. package/external/pi-tools-suite/src/async-subagents/tools/subagents.ts +5 -4
  162. package/external/pi-tools-suite/src/async-subagents/types.ts +1 -0
  163. package/external/pi-tools-suite/src/coding-discipline/index.ts +90 -59
  164. package/external/pi-tools-suite/src/config.ts +55 -1
  165. package/external/pi-tools-suite/src/context-gateway/accounting-log.ts +282 -0
  166. package/external/pi-tools-suite/src/context-gateway/config.ts +77 -3
  167. package/external/pi-tools-suite/src/context-gateway/efficiency.ts +414 -0
  168. package/external/pi-tools-suite/src/context-gateway/enforcement.ts +222 -0
  169. package/external/pi-tools-suite/src/context-gateway/index.ts +206 -19
  170. package/external/pi-tools-suite/src/context-gateway/storeless-capabilities.ts +7 -7
  171. package/external/pi-tools-suite/src/context-gateway/telemetry.ts +101 -28
  172. package/external/pi-tools-suite/src/context-gateway/types.ts +16 -3
  173. package/external/pi-tools-suite/src/context-inventory.ts +99 -0
  174. package/external/pi-tools-suite/src/dcp/auto-compress-budget.ts +39 -4
  175. package/external/pi-tools-suite/src/dcp/auto-compress.ts +128 -49
  176. package/external/pi-tools-suite/src/dcp/commands.ts +32 -156
  177. package/external/pi-tools-suite/src/dcp/compress-tool.ts +177 -46
  178. package/external/pi-tools-suite/src/dcp/compression-blocks.ts +6 -58
  179. package/external/pi-tools-suite/src/dcp/compression-preview.ts +9 -0
  180. package/external/pi-tools-suite/src/dcp/compression-progress.ts +22 -0
  181. package/external/pi-tools-suite/src/dcp/config.ts +114 -17
  182. package/external/pi-tools-suite/src/dcp/conversation-index.ts +36 -9
  183. package/external/pi-tools-suite/src/dcp/diagnostics.ts +41 -0
  184. package/external/pi-tools-suite/src/dcp/fresh-tool-results.ts +38 -0
  185. package/external/pi-tools-suite/src/dcp/index.ts +338 -66
  186. package/external/pi-tools-suite/src/dcp/journal.ts +126 -4
  187. package/external/pi-tools-suite/src/dcp/progress-controller.ts +2 -1
  188. package/external/pi-tools-suite/src/dcp/prompts.ts +83 -192
  189. package/external/pi-tools-suite/src/dcp/protected-continuity.ts +175 -0
  190. package/external/pi-tools-suite/src/dcp/pruner-candidates.ts +93 -3
  191. package/external/pi-tools-suite/src/dcp/pruner-message-ids.ts +6 -10
  192. package/external/pi-tools-suite/src/dcp/pruner-nudge.ts +36 -33
  193. package/external/pi-tools-suite/src/dcp/pruner-tools.ts +6 -3
  194. package/external/pi-tools-suite/src/dcp/pruner.ts +1 -0
  195. package/external/pi-tools-suite/src/dcp/routine-pressure.ts +86 -0
  196. package/external/pi-tools-suite/src/dcp/state.ts +9 -0
  197. package/external/pi-tools-suite/src/dcp/statistics.d.ts +8 -0
  198. package/external/pi-tools-suite/src/dcp/statistics.js +156 -0
  199. package/external/pi-tools-suite/src/default-pi-tools-suite-config.ts +78 -22
  200. package/external/pi-tools-suite/src/index.ts +7 -1
  201. package/external/pi-tools-suite/src/lib/project.ts +36 -1
  202. package/external/pi-tools-suite/src/model-tools/index.ts +10 -7
  203. package/external/pi-tools-suite/src/repo-discovery/index.ts +304 -4
  204. package/external/pi-tools-suite/src/resource-registry/index.ts +2551 -0
  205. package/external/pi-tools-suite/src/session-recovery/index.ts +17 -0
  206. package/external/pi-tools-suite/src/shell-command-policy.ts +219 -0
  207. package/external/pi-tools-suite/src/todo/index.ts +21 -0
  208. package/external/pi-tools-suite/src/todo/todo.ts +19 -1
  209. package/external/pi-tools-suite/src/tool-descriptions.ts +47 -19
  210. package/package.json +10 -9
  211. package/schemas/pi-tools-suite.json +466 -287
  212. package/schemas/pix.json +129 -11
  213. package/schemas/tasks.json +131 -0
  214. package/skills/simplify/SKILL.md +33 -5
  215. package/docs/desktop-markdown-media.md +0 -77
  216. package/external/pi-tools-suite/docs/browser-qa-subagent.md +0 -177
  217. package/external/pi-tools-suite/docs/dcp-emergency-current-turn.md +0 -102
  218. package/external/pi-tools-suite/src/async-subagents/agents/browser-qa.md +0 -598
  219. package/external/pi-tools-suite/src/async-subagents/async-subagents.sample.jsonc +0 -54
  220. package/external/pi-tools-suite/src/skill-installer/index.ts +0 -333
  221. package/skills/playwright-cli/SKILL.md +0 -420
  222. package/skills/playwright-cli/references/element-attributes.md +0 -23
  223. package/skills/playwright-cli/references/playwright-tests.md +0 -50
  224. package/skills/playwright-cli/references/request-mocking.md +0 -87
  225. package/skills/playwright-cli/references/running-code.md +0 -241
  226. package/skills/playwright-cli/references/session-management.md +0 -273
  227. package/skills/playwright-cli/references/spec-driven-testing.md +0 -311
  228. package/skills/playwright-cli/references/storage-state.md +0 -290
  229. package/skills/playwright-cli/references/test-generation.md +0 -142
  230. package/skills/playwright-cli/references/tracing.md +0 -154
  231. package/skills/playwright-cli/references/video-recording.md +0 -147
  232. package/skills/spec-lite/SKILL.md +0 -140
  233. /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/examples/qa-auth.example.jsonc +0 -0
  234. /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/examples/qa-flow.example.jsonc +0 -0
  235. /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/vendor/fflate.LICENSE +0 -0
  236. /package/external/pi-tools-suite/src/async-subagents/agents/{browser-qa → ui-qa/browser}/vendor/fflate.mjs +0 -0
@@ -0,0 +1,346 @@
1
+ import { spawn } from "node:child_process";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ import {
6
+ CHROME_DEVTOOLS_DRIVER,
7
+ CHROME_DEVTOOLS_ONLY_ACTIONS,
8
+ chromeDevtoolsRequiredByFlow,
9
+ probeChromeDevtoolsProvider,
10
+ runChromeDevtoolsProvider,
11
+ } from "../drivers/chrome-devtools/chrome-devtools-provider.mjs";
12
+
13
+ const MAX_OUTPUT_BYTES = 1024 * 1024;
14
+ const SAFE_NAME = /^[A-Za-z0-9._-]+$/;
15
+ const INSTALLED_BROWSER_RUNNER = fileURLToPath(new URL("../browser/scripts/browser-qa-runner.mjs", import.meta.url));
16
+ const PLAYWRIGHT_DRIVER = "playwright-trusted-runner";
17
+
18
+ export function resolveBrowserDriver(flow) {
19
+ const target = isObject(flow?.target) ? flow.target : {};
20
+ const requested = target.browserDriver ?? "auto";
21
+ if (!["auto", "playwright", "chrome-devtools"].includes(requested)) {
22
+ throw new Error("target.browserDriver must be auto, playwright, or chrome-devtools");
23
+ }
24
+ if (requested !== "auto") return requested;
25
+ if (typeof target.profile === "string") return "playwright";
26
+ if (target.devtools !== undefined) return "chrome-devtools";
27
+ return chromeDevtoolsRequiredByFlow(flow) ? "chrome-devtools" : "playwright";
28
+ }
29
+
30
+ export async function probeBrowserBackend(context = {}) {
31
+ const driver = resolveBrowserDriver(context.flow);
32
+ const target = isObject(context.flow?.target) ? context.flow.target : {};
33
+ if (driver === "chrome-devtools") {
34
+ if (typeof target.profile === "string") {
35
+ return {
36
+ available: false,
37
+ guideTopic: "chrome-devtools",
38
+ platformDriver: CHROME_DEVTOOLS_DRIVER,
39
+ supportedCapabilities: [],
40
+ missingCapabilities: ["trustedAuthentication"],
41
+ reason: "target.profile authentication is owned by the trusted Playwright provider and cannot be delegated to Chrome DevTools",
42
+ remediation: "use target.browserDriver `playwright` for QA credential profiles, or remove target.profile when the scenario does not require trusted authentication",
43
+ };
44
+ }
45
+ return { ...(await probeChromeDevtoolsProvider(context)), guideTopic: "chrome-devtools" };
46
+ }
47
+ if (target.devtools !== undefined) {
48
+ return {
49
+ available: false,
50
+ guideTopic: "playwright",
51
+ platformDriver: PLAYWRIGHT_DRIVER,
52
+ supportedCapabilities: [],
53
+ missingCapabilities: ["chromeDevtoolsOptions"],
54
+ reason: "target.devtools options cannot be applied by the Playwright provider",
55
+ remediation: typeof target.profile === "string"
56
+ ? "remove target.devtools when using target.profile; trusted authentication is Playwright-owned"
57
+ : "use target.browserDriver `chrome-devtools`, or leave it as `auto` so target.devtools selects Chrome DevTools",
58
+ };
59
+ }
60
+ const devtoolsOnly = Array.isArray(context.flow?.steps)
61
+ ? [...new Set(context.flow.steps.map((step) => step?.action).filter((action) => CHROME_DEVTOOLS_ONLY_ACTIONS.has(action)))]
62
+ : [];
63
+ if (devtoolsOnly.length > 0) {
64
+ return {
65
+ available: false,
66
+ guideTopic: "playwright",
67
+ platformDriver: PLAYWRIGHT_DRIVER,
68
+ supportedCapabilities: [],
69
+ missingCapabilities: devtoolsOnly.map((action) => `action:${action}`),
70
+ reason: `the Playwright provider does not implement these DevTools-only actions: ${devtoolsOnly.join(", ")}`,
71
+ remediation: "use target.browserDriver `chrome-devtools`, or leave it as `auto` so DevTools-only actions select the Chrome DevTools provider",
72
+ };
73
+ }
74
+ const runner = context.browserRunnerPath ?? INSTALLED_BROWSER_RUNNER;
75
+ const available = fs.existsSync(runner) && fs.statSync(runner).isFile();
76
+ return {
77
+ available,
78
+ guideTopic: "playwright",
79
+ platformDriver: PLAYWRIGHT_DRIVER,
80
+ supportedCapabilities: [
81
+ "navigate", "semanticLocators", "pointerInput", "keyboardInput", "domState", "deterministicAssertions",
82
+ "screenshot", "video", "trace", "downloads", "originIsolation", "trustedAuthentication", "ownedCleanup",
83
+ ],
84
+ missingCapabilities: available ? [] : ["browserRunner"],
85
+ reason: available ? "URL target selects the installed trusted browser runner" : "the installed trusted browser runner is missing",
86
+ remediation: available ? undefined : "reinstall or update pi-tools-suite so the browser QA runner resource is present",
87
+ };
88
+ }
89
+
90
+ export async function runBrowserBackend(context) {
91
+ const config = validateBrowserFlow(context.flow);
92
+ if (config.browserDriver === "chrome-devtools") {
93
+ return runChromeDevtoolsProvider(context, config);
94
+ }
95
+ const generatedFlow = createAdapterFlow(context, config.flow);
96
+ const args = ["run", "--flow", generatedFlow, "--run-id", context.runId];
97
+ if (config.profile) args.push("--profile", config.profile);
98
+ if (config.baseUrl) args.push("--base-url", config.baseUrl);
99
+ for (const origin of config.additionalAllowedOrigins) args.push("--allow-origin", origin);
100
+ const remaining = Math.max(100, Math.min(100_000, context.deadline - Date.now()));
101
+ args.push("--runner-timeout-ms", String(remaining));
102
+ context.progress("browser_adapter_started", { authenticated: Boolean(config.profile), allowedOriginCount: config.allowedOrigins.length });
103
+ const execution = await runOwnedRunner(context.browserRunnerPath, args, {
104
+ cwd: context.projectRoot,
105
+ env: process.env,
106
+ timeoutMs: remaining + 5_000,
107
+ });
108
+ const payload = parseRunnerPayload(execution.stdout);
109
+ const status = normalizeStatus(payload?.status, execution.code, execution.timedOut);
110
+ const artifacts = normalizeArtifacts(payload?.artifacts);
111
+ const assertions = browserAssertionLedger(config.flow.steps, status);
112
+ const reason = payload?.reason ?? (status === "PASSED" ? undefined : execution.stderr || `browser runner exited ${execution.code}`);
113
+ context.progress("browser_adapter_finished", { status, legacyStatus: payload?.status, exitCode: execution.code });
114
+ return {
115
+ status,
116
+ ...(reason ? { reason } : {}),
117
+ ...(payload?.profile ? { profile: payload.profile } : {}),
118
+ ...(typeof payload?.file === "string" ? { file: payload.file } : {}),
119
+ ...(typeof payload?.action === "string" ? { action: payload.action } : {}),
120
+ ...(typeof payload?.templateCreated === "boolean" ? { templateCreated: payload.templateCreated } : {}),
121
+ ...(Number.isInteger(payload?.placeholderCount) ? { placeholderCount: payload.placeholderCount } : {}),
122
+ ...(payload?.remediation ? { remediation: payload.remediation } : {}),
123
+ ...(execution.timedOut || payload?.timedOut ? { timedOut: true } : {}),
124
+ assertions,
125
+ observations: Array.isArray(payload?.observations) ? payload.observations : [],
126
+ artifacts,
127
+ legacyBrowserResult: payload ? {
128
+ status: payload.status,
129
+ ...(payload.evidenceDir ? { evidenceDir: payload.evidenceDir } : {}),
130
+ } : undefined,
131
+ };
132
+ }
133
+
134
+ function validateBrowserFlow(flow) {
135
+ assertKnownFields(flow, new Set(["version", "target", "steps", "viewport", "environment", "timeoutMs"]), "browser flow");
136
+ const target = flow.target;
137
+ if (!isObject(target)) throw new Error("browser flow requires target");
138
+ assertKnownFields(target, new Set(["kind", "url", "baseUrl", "profile", "allowedOrigins", "browserDriver", "devtools"]), "browser target");
139
+ const rawUrl = target.url ?? target.baseUrl;
140
+ const baseUrl = rawUrl === undefined ? undefined : normalizeHttpUrl(rawUrl, "target.url/baseUrl");
141
+ const profile = target.profile === undefined ? undefined : safeName(target.profile, "target.profile");
142
+ if (!profile && !baseUrl) throw new Error("public browser flow requires target.url or target.baseUrl");
143
+ if (profile && baseUrl !== undefined) {
144
+ // A profile may still use an explicit target URL, but the legacy runner owns its allowlist check.
145
+ }
146
+ const allowedOrigins = target.allowedOrigins === undefined ? [] : normalizeOrigins(target.allowedOrigins);
147
+ const effectiveAllowedOrigins = [...new Set([
148
+ ...(baseUrl ? [new URL(baseUrl).origin] : []),
149
+ ...allowedOrigins,
150
+ ])];
151
+ const browserDriver = resolveBrowserDriver(flow);
152
+ const devtools = validateDevtoolsOptions(target.devtools);
153
+ if (browserDriver === "chrome-devtools" && profile) throw new Error("Chrome DevTools browser provider does not accept target.profile authentication");
154
+ if (browserDriver === "playwright" && target.devtools !== undefined) throw new Error("target.devtools options require browserDriver chrome-devtools or auto without target.profile");
155
+ if (profile && target.devtools !== undefined) throw new Error("target.profile cannot be combined with target.devtools; trusted authentication is owned by the Playwright provider");
156
+ if (!Array.isArray(flow.steps) || flow.steps.length < 1 || flow.steps.length > 100) throw new Error("browser flow requires 1..100 steps");
157
+ for (const [index, step] of flow.steps.entries()) {
158
+ if (!isObject(step) || typeof step.action !== "string") throw new Error(`browser step ${index + 1} must define an action`);
159
+ }
160
+ const adapter = { steps: flow.steps };
161
+ for (const key of ["viewport", "environment", "timeoutMs"]) if (flow[key] !== undefined) adapter[key] = flow[key];
162
+ return {
163
+ baseUrl,
164
+ profile,
165
+ allowedOrigins: effectiveAllowedOrigins,
166
+ additionalAllowedOrigins: allowedOrigins,
167
+ browserDriver,
168
+ devtools,
169
+ flow: adapter,
170
+ originalFlow: flow,
171
+ };
172
+ }
173
+
174
+ function validateDevtoolsOptions(value) {
175
+ if (value === undefined) return {};
176
+ if (!isObject(value)) throw new Error("target.devtools must be an object");
177
+ assertKnownFields(value, new Set(["headless", "browserUrl", "reuseExistingBrowserSession"]), "target.devtools");
178
+ const result = {};
179
+ if (value.headless !== undefined) {
180
+ if (typeof value.headless !== "boolean") throw new Error("target.devtools.headless must be boolean");
181
+ result.headless = value.headless;
182
+ }
183
+ if (value.browserUrl !== undefined) result.browserUrl = normalizeLoopbackBrowserUrl(value.browserUrl);
184
+ if (value.reuseExistingBrowserSession !== undefined) {
185
+ if (typeof value.reuseExistingBrowserSession !== "boolean") throw new Error("target.devtools.reuseExistingBrowserSession must be boolean");
186
+ if (value.reuseExistingBrowserSession && !result.browserUrl) throw new Error("target.devtools.reuseExistingBrowserSession requires target.devtools.browserUrl");
187
+ result.reuseExistingBrowserSession = value.reuseExistingBrowserSession;
188
+ }
189
+ return result;
190
+ }
191
+
192
+ function normalizeLoopbackBrowserUrl(value) {
193
+ if (typeof value !== "string" || value.length > 500) throw new Error("target.devtools.browserUrl must be a bounded loopback URL");
194
+ let url;
195
+ try { url = new URL(value); } catch { throw new Error("target.devtools.browserUrl is not a valid URL"); }
196
+ if (url.protocol !== "http:" || url.username || url.password || url.pathname !== "/" || url.search || url.hash) {
197
+ throw new Error("target.devtools.browserUrl must be an exact credential-free http loopback origin");
198
+ }
199
+ const host = url.hostname.toLowerCase();
200
+ if (!["localhost", "127.0.0.1", "::1", "[::1]"].includes(host)) throw new Error("target.devtools.browserUrl must use localhost/loopback");
201
+ return url.origin;
202
+ }
203
+
204
+ function createAdapterFlow(context, flow) {
205
+ const browserFlows = path.join(context.agentDir, "browser-qa", "flows");
206
+ validatePrivateDirectory(context.agentDir, path.join(context.agentDir, "browser-qa"), "browser QA workspace");
207
+ validatePrivateDirectory(path.join(context.agentDir, "browser-qa"), browserFlows, "browser QA flow workspace");
208
+ const file = path.join(browserFlows, `ui-qa-${context.runId}.jsonc`);
209
+ if (fs.existsSync(file)) throw new Error("browser adapter flow already exists for this run id");
210
+ fs.writeFileSync(file, `${JSON.stringify(flow, null, 2)}\n`, { encoding: "utf8", mode: 0o600, flag: "wx" });
211
+ if (process.platform !== "win32") fs.chmodSync(file, 0o600);
212
+ return file;
213
+ }
214
+
215
+ function runOwnedRunner(file, args, options) {
216
+ return new Promise((resolve, reject) => {
217
+ const child = spawn(process.execPath, [file, ...args], {
218
+ cwd: options.cwd,
219
+ env: options.env,
220
+ stdio: ["ignore", "pipe", "pipe"],
221
+ detached: process.platform !== "win32",
222
+ windowsHide: true,
223
+ });
224
+ let stdout = Buffer.alloc(0);
225
+ let stderr = Buffer.alloc(0);
226
+ let outputTruncated = false;
227
+ let settled = false;
228
+ const append = (current, chunk) => {
229
+ const bytes = Buffer.from(chunk);
230
+ if (current.length >= MAX_OUTPUT_BYTES) {
231
+ outputTruncated = true;
232
+ return current;
233
+ }
234
+ const kept = bytes.subarray(0, MAX_OUTPUT_BYTES - current.length);
235
+ if (kept.length < bytes.length) outputTruncated = true;
236
+ return Buffer.concat([current, kept]);
237
+ };
238
+ child.stdout?.on("data", (chunk) => { stdout = append(stdout, chunk); });
239
+ child.stderr?.on("data", (chunk) => { stderr = append(stderr, chunk); });
240
+ const timer = setTimeout(() => {
241
+ if (settled) return;
242
+ settled = true;
243
+ killOwned(child.pid, "SIGKILL");
244
+ resolve({ code: 124, stdout: stdout.toString("utf8"), stderr: stderr.toString("utf8").trim(), timedOut: true, outputTruncated });
245
+ }, options.timeoutMs);
246
+ child.once("error", (error) => {
247
+ if (settled) return;
248
+ settled = true;
249
+ clearTimeout(timer);
250
+ reject(error);
251
+ });
252
+ child.once("close", (code) => {
253
+ if (settled) return;
254
+ settled = true;
255
+ clearTimeout(timer);
256
+ resolve({ code: code ?? 1, stdout: stdout.toString("utf8"), stderr: stderr.toString("utf8").trim(), timedOut: false, outputTruncated });
257
+ });
258
+ });
259
+ }
260
+
261
+ function parseRunnerPayload(stdout) {
262
+ for (const line of stdout.trim().split(/\r?\n/).reverse()) {
263
+ try {
264
+ const value = JSON.parse(line);
265
+ if (isObject(value) && typeof value.status === "string") return value;
266
+ } catch { /* progress or diagnostic line */ }
267
+ }
268
+ return undefined;
269
+ }
270
+
271
+ function normalizeStatus(status, exitCode, timedOut) {
272
+ if (timedOut || exitCode === 124) return "FAILED";
273
+ if (status === "QA_PASSED" || status === "passed" || status === "PASSED") return "PASSED";
274
+ if (status === "QA_AUTH_UPDATE_REQUIRED" || status === "BLOCKED") return "BLOCKED";
275
+ return "FAILED";
276
+ }
277
+
278
+ function normalizeArtifacts(value) {
279
+ const source = isObject(value) ? value : {};
280
+ const result = emptyArtifacts();
281
+ for (const key of ["screenshots", "videos", "traces", "terminalCaptures", "accessibilitySnapshots", "observations", "downloads"]) {
282
+ if (Array.isArray(source[key])) result[key] = source[key].filter((entry) => isObject(entry) && typeof entry.path === "string" && typeof entry.uri === "string");
283
+ }
284
+ return result;
285
+ }
286
+
287
+ function browserAssertionLedger(steps, status) {
288
+ const assertions = steps.filter((step) => typeof step.action === "string" && step.action.startsWith("assert"));
289
+ if (status !== "PASSED") return assertions.map((step) => ({ action: step.action, passed: undefined, delegated: true }));
290
+ return assertions.map((step) => ({ action: step.action, passed: true, delegated: true }));
291
+ }
292
+
293
+ function normalizeOrigins(value) {
294
+ if (!Array.isArray(value) || value.length > 20) throw new Error("target.allowedOrigins must be an array with at most 20 exact origins");
295
+ return value.map((entry, index) => {
296
+ const url = new URL(normalizeHttpUrl(entry, `target.allowedOrigins[${index}]`));
297
+ if (url.pathname !== "/" || url.search || url.hash || url.username || url.password) throw new Error(`target.allowedOrigins[${index}] must be an exact origin`);
298
+ return url.origin;
299
+ });
300
+ }
301
+
302
+ function normalizeHttpUrl(value, label) {
303
+ if (typeof value !== "string" || value.length > 4096) throw new Error(`${label} must be a bounded URL`);
304
+ let url;
305
+ try { url = new URL(value); } catch { throw new Error(`${label} is not a valid URL`); }
306
+ if (url.protocol !== "http:" && url.protocol !== "https:") throw new Error(`${label} must use http or https`);
307
+ if (url.username || url.password) throw new Error(`${label} must not contain credentials`);
308
+ return url.href;
309
+ }
310
+
311
+ function safeName(value, label) {
312
+ if (typeof value !== "string" || !SAFE_NAME.test(value) || value.includes("..")) throw new Error(`${label} must be a safe identifier`);
313
+ return value;
314
+ }
315
+
316
+ function validatePrivateDirectory(root, target, label) {
317
+ if (!isInside(root, target) || !fs.existsSync(target)) throw new Error(`${label} is missing or outside its owning agent directory`);
318
+ const stat = fs.lstatSync(target);
319
+ if (stat.isSymbolicLink() || !stat.isDirectory()) throw new Error(`${label} must be a real directory`);
320
+ if (process.platform !== "win32" && (stat.mode & 0o077) !== 0) throw new Error(`${label} must use private permissions (0700)`);
321
+ }
322
+
323
+ function killOwned(pid, signal) {
324
+ if (!Number.isInteger(pid) || pid <= 0) return;
325
+ if (process.platform !== "win32") {
326
+ try { process.kill(-pid, signal); return; } catch { /* fall back to the exact owned pid */ }
327
+ }
328
+ try { process.kill(pid, signal); } catch { /* already exited */ }
329
+ }
330
+
331
+ function assertKnownFields(value, allowed, label) {
332
+ for (const key of Object.keys(value)) if (!allowed.has(key)) throw new Error(`${label} contains unsupported field: ${key}`);
333
+ }
334
+
335
+ function emptyArtifacts() {
336
+ return { screenshots: [], videos: [], traces: [], terminalCaptures: [], accessibilitySnapshots: [], observations: [], downloads: [] };
337
+ }
338
+
339
+ function isInside(root, target) {
340
+ const relative = path.relative(root, target);
341
+ return !relative || (!relative.startsWith("..") && !path.isAbsolute(relative));
342
+ }
343
+
344
+ function isObject(value) {
345
+ return value !== null && typeof value === "object" && !Array.isArray(value);
346
+ }