@poolzin/pool-bot 2026.2.23 → 2026.2.25

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 (235) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/acp/client.js +207 -18
  3. package/dist/acp/secret-file.js +22 -0
  4. package/dist/agents/agent-scope.js +10 -0
  5. package/dist/agents/bash-process-registry.test-helpers.js +29 -0
  6. package/dist/agents/bash-tools.exec-approval-request.js +20 -0
  7. package/dist/agents/bash-tools.exec-host-gateway.js +230 -0
  8. package/dist/agents/bash-tools.exec-host-node.js +235 -0
  9. package/dist/agents/bash-tools.exec-types.js +1 -0
  10. package/dist/agents/bash-tools.process.js +224 -218
  11. package/dist/agents/content-blocks.js +16 -0
  12. package/dist/agents/model-fallback.js +96 -101
  13. package/dist/agents/models-config.providers.js +299 -182
  14. package/dist/agents/pi-embedded-payloads.js +1 -0
  15. package/dist/agents/pi-embedded-runner/run.overflow-compaction.fixture.js +34 -0
  16. package/dist/agents/skills.test-helpers.js +13 -0
  17. package/dist/agents/stable-stringify.js +12 -0
  18. package/dist/agents/subagent-registry.mocks.shared.js +12 -0
  19. package/dist/agents/test-helpers/assistant-message-fixtures.js +29 -0
  20. package/dist/agents/test-helpers/pi-tools-sandbox-context.js +27 -0
  21. package/dist/agents/tool-policy-shared.js +108 -0
  22. package/dist/agents/tools/browser-tool.js +160 -54
  23. package/dist/agents/tools/cron-tool.test-helpers.js +12 -0
  24. package/dist/agents/tools/discord-actions-moderation-shared.js +27 -0
  25. package/dist/agents/tools/image-tool.js +214 -99
  26. package/dist/agents/tools/sessions-history-tool.js +140 -108
  27. package/dist/agents/workspace.js +222 -46
  28. package/dist/auto-reply/commands-registry.js +15 -18
  29. package/dist/auto-reply/fallback-state.js +114 -0
  30. package/dist/auto-reply/model-runtime.js +68 -0
  31. package/dist/auto-reply/reply/agent-runner-execution.js +36 -4
  32. package/dist/auto-reply/reply/agent-runner.js +165 -39
  33. package/dist/auto-reply/reply/commands-setunset-standard.js +13 -0
  34. package/dist/browser/config.js +26 -0
  35. package/dist/browser/navigation-guard.js +31 -0
  36. package/dist/browser/routes/agent.act.js +431 -424
  37. package/dist/browser/routes/agent.shared.js +47 -3
  38. package/dist/browser/routes/agent.snapshot.js +122 -116
  39. package/dist/browser/routes/agent.storage.js +303 -297
  40. package/dist/browser/routes/tabs.js +154 -100
  41. package/dist/browser/server-lifecycle.js +37 -0
  42. package/dist/build-info.json +3 -3
  43. package/dist/channels/allow-from.js +25 -0
  44. package/dist/channels/plugins/account-action-gate.js +13 -0
  45. package/dist/channels/plugins/message-actions.js +10 -0
  46. package/dist/channels/telegram/api.js +18 -0
  47. package/dist/cli/argv.js +84 -21
  48. package/dist/cli/banner.js +2 -1
  49. package/dist/cli/exec-approvals-cli.js +92 -124
  50. package/dist/cli/memory-cli.js +158 -61
  51. package/dist/cli/nodes-cli/register.push.js +63 -0
  52. package/dist/cli/nodes-media-utils.js +21 -0
  53. package/dist/cli/plugins-cli.js +245 -61
  54. package/dist/cli/program/build-program.js +3 -1
  55. package/dist/cli/program/command-registry.js +223 -136
  56. package/dist/cli/program/help.js +43 -12
  57. package/dist/cli/route.js +1 -1
  58. package/dist/cli/test-runtime-capture.js +24 -0
  59. package/dist/commands/agent.js +163 -87
  60. package/dist/commands/channels.mock-harness.js +23 -0
  61. package/dist/commands/daemon-install-runtime-warning.js +11 -0
  62. package/dist/commands/onboard-helpers.js +4 -4
  63. package/dist/commands/sessions.test-helpers.js +61 -0
  64. package/dist/compat/legacy-names.js +2 -2
  65. package/dist/config/commands.js +3 -0
  66. package/dist/config/config.js +1 -1
  67. package/dist/config/env-substitution.js +62 -34
  68. package/dist/config/env-vars.js +9 -0
  69. package/dist/config/io.js +571 -171
  70. package/dist/config/merge-patch.js +50 -4
  71. package/dist/config/redact-snapshot.js +404 -76
  72. package/dist/config/schema.js +58 -570
  73. package/dist/config/validation.js +140 -85
  74. package/dist/config/zod-schema.hooks.js +40 -11
  75. package/dist/config/zod-schema.installs.js +20 -0
  76. package/dist/config/zod-schema.js +8 -7
  77. package/dist/control-ui/assets/{index-HRr1grwl.js → index-Dvkl4Xlx.js} +2 -1
  78. package/dist/control-ui/assets/{index-HRr1grwl.js.map → index-Dvkl4Xlx.js.map} +1 -1
  79. package/dist/control-ui/index.html +1 -1
  80. package/dist/daemon/cmd-argv.js +21 -0
  81. package/dist/daemon/cmd-set.js +58 -0
  82. package/dist/daemon/service-types.js +1 -0
  83. package/dist/discord/monitor/exec-approvals.js +357 -162
  84. package/dist/gateway/auth.js +38 -3
  85. package/dist/gateway/call.js +149 -68
  86. package/dist/gateway/canvas-capability.js +75 -0
  87. package/dist/gateway/control-plane-audit.js +28 -0
  88. package/dist/gateway/control-plane-rate-limit.js +53 -0
  89. package/dist/gateway/events.js +1 -0
  90. package/dist/gateway/hooks.js +109 -54
  91. package/dist/gateway/http-common.js +22 -0
  92. package/dist/gateway/method-scopes.js +169 -0
  93. package/dist/gateway/net.js +23 -0
  94. package/dist/gateway/openresponses-http.js +120 -110
  95. package/dist/gateway/probe-auth.js +2 -0
  96. package/dist/gateway/protocol/index.js +3 -2
  97. package/dist/gateway/protocol/schema/protocol-schemas.js +2 -0
  98. package/dist/gateway/protocol/schema/push.js +18 -0
  99. package/dist/gateway/protocol/schema.js +1 -0
  100. package/dist/gateway/server-http.js +236 -52
  101. package/dist/gateway/server-methods/agent.js +162 -24
  102. package/dist/gateway/server-methods/chat.js +461 -130
  103. package/dist/gateway/server-methods/config.js +193 -150
  104. package/dist/gateway/server-methods/nodes.helpers.js +12 -0
  105. package/dist/gateway/server-methods/nodes.js +251 -69
  106. package/dist/gateway/server-methods/push.js +53 -0
  107. package/dist/gateway/server-reload-handlers.js +2 -3
  108. package/dist/gateway/server-runtime-config.js +5 -0
  109. package/dist/gateway/server-runtime-state.js +2 -0
  110. package/dist/gateway/server-ws-runtime.js +1 -0
  111. package/dist/gateway/server.impl.js +296 -139
  112. package/dist/gateway/session-preview.test-helpers.js +11 -0
  113. package/dist/gateway/startup-auth.js +126 -0
  114. package/dist/gateway/test-helpers.agent-results.js +15 -0
  115. package/dist/gateway/test-helpers.mocks.js +37 -14
  116. package/dist/gateway/test-helpers.server.js +161 -77
  117. package/dist/hooks/bundled/session-memory/handler.js +165 -34
  118. package/dist/hooks/gmail-watcher-lifecycle.js +23 -0
  119. package/dist/infra/archive-path.js +49 -0
  120. package/dist/infra/device-pairing.js +148 -167
  121. package/dist/infra/exec-approvals-allowlist.js +19 -70
  122. package/dist/infra/exec-approvals-analysis.js +44 -17
  123. package/dist/infra/exec-safe-bin-policy.js +269 -0
  124. package/dist/infra/fixed-window-rate-limit.js +33 -0
  125. package/dist/infra/git-root.js +61 -0
  126. package/dist/infra/heartbeat-active-hours.js +2 -2
  127. package/dist/infra/heartbeat-reason.js +40 -0
  128. package/dist/infra/heartbeat-runner.js +72 -32
  129. package/dist/infra/install-source-utils.js +91 -7
  130. package/dist/infra/node-pairing.js +50 -105
  131. package/dist/infra/npm-integrity.js +45 -0
  132. package/dist/infra/npm-pack-install.js +40 -0
  133. package/dist/infra/outbound/channel-adapters.js +20 -7
  134. package/dist/infra/outbound/message-action-runner.js +107 -327
  135. package/dist/infra/outbound/message.js +59 -36
  136. package/dist/infra/outbound/outbound-policy.js +52 -25
  137. package/dist/infra/outbound/outbound-send-service.js +58 -71
  138. package/dist/infra/pairing-files.js +10 -0
  139. package/dist/infra/plain-object.js +9 -0
  140. package/dist/infra/push-apns.js +365 -0
  141. package/dist/infra/restart-sentinel.js +16 -1
  142. package/dist/infra/restart.js +229 -26
  143. package/dist/infra/scp-host.js +54 -0
  144. package/dist/infra/update-startup.js +86 -9
  145. package/dist/media/inbound-path-policy.js +114 -0
  146. package/dist/media/input-files.js +16 -0
  147. package/dist/memory/test-manager.js +8 -0
  148. package/dist/plugin-sdk/temp-path.js +47 -0
  149. package/dist/plugins/discovery.js +217 -23
  150. package/dist/plugins/hook-runner-global.js +16 -0
  151. package/dist/plugins/loader.js +192 -26
  152. package/dist/plugins/logger.js +8 -0
  153. package/dist/plugins/manifest-registry.js +3 -0
  154. package/dist/plugins/path-safety.js +34 -0
  155. package/dist/plugins/registry.js +5 -2
  156. package/dist/plugins/runtime/index.js +271 -206
  157. package/dist/providers/github-copilot-models.js +4 -1
  158. package/dist/security/audit-channel.js +8 -19
  159. package/dist/security/audit-extra.async.js +354 -182
  160. package/dist/security/audit-extra.js +11 -1
  161. package/dist/security/audit-extra.sync.js +340 -33
  162. package/dist/security/audit-fs.js +31 -13
  163. package/dist/security/audit.js +145 -371
  164. package/dist/security/dm-policy-shared.js +24 -0
  165. package/dist/security/external-content.js +20 -8
  166. package/dist/security/fix.js +49 -85
  167. package/dist/security/scan-paths.js +20 -0
  168. package/dist/security/secret-equal.js +3 -7
  169. package/dist/security/windows-acl.js +30 -15
  170. package/dist/shared/node-list-parse.js +13 -0
  171. package/dist/shared/operator-scope-compat.js +37 -0
  172. package/dist/shared/text-chunking.js +29 -0
  173. package/dist/slack/blocks.test-helpers.js +31 -0
  174. package/dist/slack/monitor/mrkdwn.js +8 -0
  175. package/dist/telegram/bot-message-dispatch.js +366 -164
  176. package/dist/telegram/draft-stream.js +30 -7
  177. package/dist/telegram/reasoning-lane-coordinator.js +128 -0
  178. package/dist/terminal/prompt-select-styled.js +9 -0
  179. package/dist/test-utils/command-runner.js +6 -0
  180. package/dist/test-utils/internal-hook-event-payload.js +10 -0
  181. package/dist/test-utils/model-auth-mock.js +12 -0
  182. package/dist/test-utils/provider-usage-fetch.js +14 -0
  183. package/dist/test-utils/temp-home.js +33 -0
  184. package/dist/tui/components/chat-log.js +9 -0
  185. package/dist/tui/tui-command-handlers.js +36 -27
  186. package/dist/tui/tui-event-handlers.js +122 -32
  187. package/dist/tui/tui.js +181 -45
  188. package/dist/utils/mask-api-key.js +10 -0
  189. package/dist/utils/run-with-concurrency.js +39 -0
  190. package/dist/web/media.js +4 -0
  191. package/docs/tools/slash-commands.md +5 -1
  192. package/extensions/bluebubbles/package.json +1 -1
  193. package/extensions/copilot-proxy/package.json +1 -1
  194. package/extensions/diagnostics-otel/package.json +1 -1
  195. package/extensions/discord/package.json +1 -1
  196. package/extensions/feishu/package.json +1 -1
  197. package/extensions/feishu/src/external-keys.ts +19 -0
  198. package/extensions/google-antigravity-auth/package.json +1 -1
  199. package/extensions/google-gemini-cli-auth/package.json +1 -1
  200. package/extensions/googlechat/package.json +1 -1
  201. package/extensions/imessage/package.json +1 -1
  202. package/extensions/irc/package.json +1 -1
  203. package/extensions/line/package.json +1 -1
  204. package/extensions/llm-task/package.json +1 -1
  205. package/extensions/lobster/package.json +1 -1
  206. package/extensions/lobster/src/windows-spawn.ts +193 -0
  207. package/extensions/matrix/CHANGELOG.md +5 -0
  208. package/extensions/matrix/package.json +1 -1
  209. package/extensions/matrix/src/matrix/actions/limits.ts +6 -0
  210. package/extensions/mattermost/package.json +1 -1
  211. package/extensions/mattermost/src/mattermost/reactions.test-helpers.ts +83 -0
  212. package/extensions/memory-core/package.json +1 -1
  213. package/extensions/memory-lancedb/package.json +1 -1
  214. package/extensions/minimax-portal-auth/package.json +1 -1
  215. package/extensions/msteams/CHANGELOG.md +5 -0
  216. package/extensions/msteams/package.json +1 -1
  217. package/extensions/nextcloud-talk/package.json +1 -1
  218. package/extensions/nostr/CHANGELOG.md +5 -0
  219. package/extensions/nostr/package.json +1 -1
  220. package/extensions/open-prose/package.json +1 -1
  221. package/extensions/openai-codex-auth/package.json +1 -1
  222. package/extensions/signal/package.json +1 -1
  223. package/extensions/slack/package.json +1 -1
  224. package/extensions/telegram/package.json +1 -1
  225. package/extensions/tlon/package.json +1 -1
  226. package/extensions/twitch/CHANGELOG.md +5 -0
  227. package/extensions/twitch/package.json +1 -1
  228. package/extensions/voice-call/CHANGELOG.md +5 -0
  229. package/extensions/voice-call/package.json +1 -1
  230. package/extensions/whatsapp/package.json +1 -1
  231. package/extensions/zalo/CHANGELOG.md +5 -0
  232. package/extensions/zalo/package.json +1 -1
  233. package/extensions/zalouser/CHANGELOG.md +5 -0
  234. package/extensions/zalouser/package.json +1 -1
  235. package/package.json +1 -1
@@ -11,14 +11,24 @@ export const SELECTOR_UNSUPPORTED_MESSAGE = [
11
11
  ].join("\n");
12
12
  export function readBody(req) {
13
13
  const body = req.body;
14
- if (!body || typeof body !== "object" || Array.isArray(body))
14
+ if (!body || typeof body !== "object" || Array.isArray(body)) {
15
15
  return {};
16
+ }
16
17
  return body;
17
18
  }
19
+ export function resolveTargetIdFromBody(body) {
20
+ const targetId = typeof body.targetId === "string" ? body.targetId.trim() : "";
21
+ return targetId || undefined;
22
+ }
23
+ export function resolveTargetIdFromQuery(query) {
24
+ const targetId = typeof query.targetId === "string" ? query.targetId.trim() : "";
25
+ return targetId || undefined;
26
+ }
18
27
  export function handleRouteError(ctx, res, err) {
19
28
  const mapped = ctx.mapTabError(err);
20
- if (mapped)
29
+ if (mapped) {
21
30
  return jsonError(res, mapped.status, mapped.message);
31
+ }
22
32
  jsonError(res, 500, String(err));
23
33
  }
24
34
  export function resolveProfileContext(req, res, ctx) {
@@ -34,8 +44,9 @@ export async function getPwAiModule() {
34
44
  }
35
45
  export async function requirePwAi(res, feature) {
36
46
  const mod = await getPwAiModule();
37
- if (mod)
47
+ if (mod) {
38
48
  return mod;
49
+ }
39
50
  jsonError(res, 501, [
40
51
  `Playwright is not available in this gateway build; '${feature}' is unsupported.`,
41
52
  "Install the full Playwright package (not playwright-core) and restart the gateway, or reinstall with browser support.",
@@ -43,3 +54,36 @@ export async function requirePwAi(res, feature) {
43
54
  ].join("\n"));
44
55
  return null;
45
56
  }
57
+ export async function withRouteTabContext(params) {
58
+ const profileCtx = resolveProfileContext(params.req, params.res, params.ctx);
59
+ if (!profileCtx) {
60
+ return undefined;
61
+ }
62
+ try {
63
+ const tab = await profileCtx.ensureTabAvailable(params.targetId);
64
+ return await params.run({
65
+ profileCtx,
66
+ tab,
67
+ cdpUrl: profileCtx.profile.cdpUrl,
68
+ });
69
+ }
70
+ catch (err) {
71
+ handleRouteError(params.ctx, params.res, err);
72
+ return undefined;
73
+ }
74
+ }
75
+ export async function withPlaywrightRouteContext(params) {
76
+ return await withRouteTabContext({
77
+ req: params.req,
78
+ res: params.res,
79
+ ctx: params.ctx,
80
+ targetId: params.targetId,
81
+ run: async ({ profileCtx, tab, cdpUrl }) => {
82
+ const pw = await requirePwAi(params.res, params.feature);
83
+ if (!pw) {
84
+ return undefined;
85
+ }
86
+ return await params.run({ profileCtx, tab, cdpUrl, pw });
87
+ },
88
+ });
89
+ }
@@ -2,67 +2,71 @@ import path from "node:path";
2
2
  import { ensureMediaDir, saveMediaBuffer } from "../../media/store.js";
3
3
  import { captureScreenshot, snapshotAria } from "../cdp.js";
4
4
  import { DEFAULT_AI_SNAPSHOT_EFFICIENT_DEPTH, DEFAULT_AI_SNAPSHOT_EFFICIENT_MAX_CHARS, DEFAULT_AI_SNAPSHOT_MAX_CHARS, } from "../constants.js";
5
+ import { withBrowserNavigationPolicy } from "../navigation-guard.js";
5
6
  import { DEFAULT_BROWSER_SCREENSHOT_MAX_BYTES, DEFAULT_BROWSER_SCREENSHOT_MAX_SIDE, normalizeBrowserScreenshot, } from "../screenshot.js";
6
- import { getPwAiModule, handleRouteError, readBody, requirePwAi, resolveProfileContext, } from "./agent.shared.js";
7
+ import { getPwAiModule, handleRouteError, readBody, requirePwAi, resolveProfileContext, withPlaywrightRouteContext, withRouteTabContext, } from "./agent.shared.js";
7
8
  import { jsonError, toBoolean, toNumber, toStringOrEmpty } from "./utils.js";
9
+ async function saveBrowserMediaResponse(params) {
10
+ await ensureMediaDir();
11
+ const saved = await saveMediaBuffer(params.buffer, params.contentType, "browser", params.maxBytes);
12
+ params.res.json({
13
+ ok: true,
14
+ path: path.resolve(saved.path),
15
+ targetId: params.targetId,
16
+ url: params.url,
17
+ });
18
+ }
8
19
  export function registerBrowserAgentSnapshotRoutes(app, ctx) {
9
20
  app.post("/navigate", async (req, res) => {
10
- const profileCtx = resolveProfileContext(req, res, ctx);
11
- if (!profileCtx)
12
- return;
13
21
  const body = readBody(req);
14
22
  const url = toStringOrEmpty(body.url);
15
23
  const targetId = toStringOrEmpty(body.targetId) || undefined;
16
- if (!url)
24
+ if (!url) {
17
25
  return jsonError(res, 400, "url is required");
18
- try {
19
- const tab = await profileCtx.ensureTabAvailable(targetId);
20
- const pw = await requirePwAi(res, "navigate");
21
- if (!pw)
22
- return;
23
- const result = await pw.navigateViaPlaywright({
24
- cdpUrl: profileCtx.profile.cdpUrl,
25
- targetId: tab.targetId,
26
- url,
27
- });
28
- res.json({ ok: true, targetId: tab.targetId, ...result });
29
- }
30
- catch (err) {
31
- handleRouteError(ctx, res, err);
32
26
  }
27
+ await withPlaywrightRouteContext({
28
+ req,
29
+ res,
30
+ ctx,
31
+ targetId,
32
+ feature: "navigate",
33
+ run: async ({ cdpUrl, tab, pw }) => {
34
+ const result = await pw.navigateViaPlaywright({
35
+ cdpUrl,
36
+ targetId: tab.targetId,
37
+ url,
38
+ ...withBrowserNavigationPolicy(ctx.state().resolved.ssrfPolicy),
39
+ });
40
+ res.json({ ok: true, targetId: tab.targetId, ...result });
41
+ },
42
+ });
33
43
  });
34
44
  app.post("/pdf", async (req, res) => {
35
- const profileCtx = resolveProfileContext(req, res, ctx);
36
- if (!profileCtx)
37
- return;
38
45
  const body = readBody(req);
39
46
  const targetId = toStringOrEmpty(body.targetId) || undefined;
40
- try {
41
- const tab = await profileCtx.ensureTabAvailable(targetId);
42
- const pw = await requirePwAi(res, "pdf");
43
- if (!pw)
44
- return;
45
- const pdf = await pw.pdfViaPlaywright({
46
- cdpUrl: profileCtx.profile.cdpUrl,
47
- targetId: tab.targetId,
48
- });
49
- await ensureMediaDir();
50
- const saved = await saveMediaBuffer(pdf.buffer, "application/pdf", "browser", pdf.buffer.byteLength);
51
- res.json({
52
- ok: true,
53
- path: path.resolve(saved.path),
54
- targetId: tab.targetId,
55
- url: tab.url,
56
- });
57
- }
58
- catch (err) {
59
- handleRouteError(ctx, res, err);
60
- }
47
+ await withPlaywrightRouteContext({
48
+ req,
49
+ res,
50
+ ctx,
51
+ targetId,
52
+ feature: "pdf",
53
+ run: async ({ cdpUrl, tab, pw }) => {
54
+ const pdf = await pw.pdfViaPlaywright({
55
+ cdpUrl,
56
+ targetId: tab.targetId,
57
+ });
58
+ await saveBrowserMediaResponse({
59
+ res,
60
+ buffer: pdf.buffer,
61
+ contentType: "application/pdf",
62
+ maxBytes: pdf.buffer.byteLength,
63
+ targetId: tab.targetId,
64
+ url: tab.url,
65
+ });
66
+ },
67
+ });
61
68
  });
62
69
  app.post("/screenshot", async (req, res) => {
63
- const profileCtx = resolveProfileContext(req, res, ctx);
64
- if (!profileCtx)
65
- return;
66
70
  const body = readBody(req);
67
71
  const targetId = toStringOrEmpty(body.targetId) || undefined;
68
72
  const fullPage = toBoolean(body.fullPage) ?? false;
@@ -72,53 +76,60 @@ export function registerBrowserAgentSnapshotRoutes(app, ctx) {
72
76
  if (fullPage && (ref || element)) {
73
77
  return jsonError(res, 400, "fullPage is not supported for element screenshots");
74
78
  }
75
- try {
76
- const tab = await profileCtx.ensureTabAvailable(targetId);
77
- let buffer;
78
- const shouldUsePlaywright = profileCtx.profile.driver === "extension" || !tab.wsUrl || Boolean(ref) || Boolean(element);
79
- if (shouldUsePlaywright) {
80
- const pw = await requirePwAi(res, "screenshot");
81
- if (!pw)
82
- return;
83
- const snap = await pw.takeScreenshotViaPlaywright({
84
- cdpUrl: profileCtx.profile.cdpUrl,
85
- targetId: tab.targetId,
86
- ref,
87
- element,
88
- fullPage,
89
- type,
79
+ await withRouteTabContext({
80
+ req,
81
+ res,
82
+ ctx,
83
+ targetId,
84
+ run: async ({ profileCtx, tab, cdpUrl }) => {
85
+ let buffer;
86
+ const shouldUsePlaywright = profileCtx.profile.driver === "extension" ||
87
+ !tab.wsUrl ||
88
+ Boolean(ref) ||
89
+ Boolean(element);
90
+ if (shouldUsePlaywright) {
91
+ const pw = await requirePwAi(res, "screenshot");
92
+ if (!pw) {
93
+ return;
94
+ }
95
+ const snap = await pw.takeScreenshotViaPlaywright({
96
+ cdpUrl,
97
+ targetId: tab.targetId,
98
+ ref,
99
+ element,
100
+ fullPage,
101
+ type,
102
+ });
103
+ buffer = snap.buffer;
104
+ }
105
+ else {
106
+ buffer = await captureScreenshot({
107
+ wsUrl: tab.wsUrl ?? "",
108
+ fullPage,
109
+ format: type,
110
+ quality: type === "jpeg" ? 85 : undefined,
111
+ });
112
+ }
113
+ const normalized = await normalizeBrowserScreenshot(buffer, {
114
+ maxSide: DEFAULT_BROWSER_SCREENSHOT_MAX_SIDE,
115
+ maxBytes: DEFAULT_BROWSER_SCREENSHOT_MAX_BYTES,
90
116
  });
91
- buffer = snap.buffer;
92
- }
93
- else {
94
- buffer = await captureScreenshot({
95
- wsUrl: tab.wsUrl ?? "",
96
- fullPage,
97
- format: type,
98
- quality: type === "jpeg" ? 85 : undefined,
117
+ await saveBrowserMediaResponse({
118
+ res,
119
+ buffer: normalized.buffer,
120
+ contentType: normalized.contentType ?? `image/${type}`,
121
+ maxBytes: DEFAULT_BROWSER_SCREENSHOT_MAX_BYTES,
122
+ targetId: tab.targetId,
123
+ url: tab.url,
99
124
  });
100
- }
101
- const normalized = await normalizeBrowserScreenshot(buffer, {
102
- maxSide: DEFAULT_BROWSER_SCREENSHOT_MAX_SIDE,
103
- maxBytes: DEFAULT_BROWSER_SCREENSHOT_MAX_BYTES,
104
- });
105
- await ensureMediaDir();
106
- const saved = await saveMediaBuffer(normalized.buffer, normalized.contentType ?? `image/${type}`, "browser", DEFAULT_BROWSER_SCREENSHOT_MAX_BYTES);
107
- res.json({
108
- ok: true,
109
- path: path.resolve(saved.path),
110
- targetId: tab.targetId,
111
- url: tab.url,
112
- });
113
- }
114
- catch (err) {
115
- handleRouteError(ctx, res, err);
116
- }
125
+ },
126
+ });
117
127
  });
118
128
  app.get("/snapshot", async (req, res) => {
119
129
  const profileCtx = resolveProfileContext(req, res, ctx);
120
- if (!profileCtx)
130
+ if (!profileCtx) {
121
131
  return;
132
+ }
122
133
  const targetId = typeof req.query.targetId === "string" ? req.query.targetId.trim() : "";
123
134
  const mode = req.query.mode === "efficient" ? "efficient" : undefined;
124
135
  const labels = toBoolean(req.query.labels) ?? undefined;
@@ -148,6 +159,8 @@ export function registerBrowserAgentSnapshotRoutes(app, ctx) {
148
159
  const depth = depthRaw ?? (mode === "efficient" ? DEFAULT_AI_SNAPSHOT_EFFICIENT_DEPTH : undefined);
149
160
  const selector = toStringOrEmpty(req.query.selector);
150
161
  const frameSelector = toStringOrEmpty(req.query.frame);
162
+ const selectorValue = selector.trim() || undefined;
163
+ const frameSelectorValue = frameSelector.trim() || undefined;
151
164
  try {
152
165
  const tab = await profileCtx.ensureTabAvailable(targetId || undefined);
153
166
  if ((labels || mode === "efficient") && format === "aria") {
@@ -155,28 +168,30 @@ export function registerBrowserAgentSnapshotRoutes(app, ctx) {
155
168
  }
156
169
  if (format === "ai") {
157
170
  const pw = await requirePwAi(res, "ai snapshot");
158
- if (!pw)
171
+ if (!pw) {
159
172
  return;
173
+ }
160
174
  const wantsRoleSnapshot = labels === true ||
161
175
  mode === "efficient" ||
162
176
  interactive === true ||
163
177
  compact === true ||
164
178
  depth !== undefined ||
165
- Boolean(selector.trim()) ||
166
- Boolean(frameSelector.trim());
179
+ Boolean(selectorValue) ||
180
+ Boolean(frameSelectorValue);
181
+ const roleSnapshotArgs = {
182
+ cdpUrl: profileCtx.profile.cdpUrl,
183
+ targetId: tab.targetId,
184
+ selector: selectorValue,
185
+ frameSelector: frameSelectorValue,
186
+ refsMode,
187
+ options: {
188
+ interactive: interactive ?? undefined,
189
+ compact: compact ?? undefined,
190
+ maxDepth: depth ?? undefined,
191
+ },
192
+ };
167
193
  const snap = wantsRoleSnapshot
168
- ? await pw.snapshotRoleViaPlaywright({
169
- cdpUrl: profileCtx.profile.cdpUrl,
170
- targetId: tab.targetId,
171
- selector: selector.trim() || undefined,
172
- frameSelector: frameSelector.trim() || undefined,
173
- refsMode,
174
- options: {
175
- interactive: interactive ?? undefined,
176
- compact: compact ?? undefined,
177
- maxDepth: depth ?? undefined,
178
- },
179
- })
194
+ ? await pw.snapshotRoleViaPlaywright(roleSnapshotArgs)
180
195
  : await pw
181
196
  .snapshotAiViaPlaywright({
182
197
  cdpUrl: profileCtx.profile.cdpUrl,
@@ -186,18 +201,7 @@ export function registerBrowserAgentSnapshotRoutes(app, ctx) {
186
201
  .catch(async (err) => {
187
202
  // Public-API fallback when Playwright's private _snapshotForAI is missing.
188
203
  if (String(err).toLowerCase().includes("_snapshotforai")) {
189
- return await pw.snapshotRoleViaPlaywright({
190
- cdpUrl: profileCtx.profile.cdpUrl,
191
- targetId: tab.targetId,
192
- selector: selector.trim() || undefined,
193
- frameSelector: frameSelector.trim() || undefined,
194
- refsMode,
195
- options: {
196
- interactive: interactive ?? undefined,
197
- compact: compact ?? undefined,
198
- maxDepth: depth ?? undefined,
199
- },
200
- });
204
+ return await pw.snapshotRoleViaPlaywright(roleSnapshotArgs);
201
205
  }
202
206
  throw err;
203
207
  });
@@ -241,8 +245,9 @@ export function registerBrowserAgentSnapshotRoutes(app, ctx) {
241
245
  // Extension relay doesn't expose per-page WS URLs; run AX snapshot via Playwright CDP session.
242
246
  // Also covers cases where wsUrl is missing/unusable.
243
247
  return requirePwAi(res, "aria snapshot").then(async (pw) => {
244
- if (!pw)
248
+ if (!pw) {
245
249
  return null;
250
+ }
246
251
  return await pw.snapshotAriaViaPlaywright({
247
252
  cdpUrl: profileCtx.profile.cdpUrl,
248
253
  targetId: tab.targetId,
@@ -252,8 +257,9 @@ export function registerBrowserAgentSnapshotRoutes(app, ctx) {
252
257
  })()
253
258
  : snapshotAria({ wsUrl: tab.wsUrl ?? "", limit });
254
259
  const resolved = await Promise.resolve(snap);
255
- if (!resolved)
260
+ if (!resolved) {
256
261
  return;
262
+ }
257
263
  return res.json({
258
264
  ok: true,
259
265
  format,