@browserbridge/bbx 1.0.0 → 1.1.0

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 (72) hide show
  1. package/README.md +6 -4
  2. package/package.json +53 -53
  3. package/packages/agent-client/src/cli-helpers.js +43 -5
  4. package/packages/agent-client/src/cli.js +176 -171
  5. package/packages/agent-client/src/client.js +66 -21
  6. package/packages/agent-client/src/command-registry.js +104 -69
  7. package/packages/agent-client/src/detect.js +162 -54
  8. package/packages/agent-client/src/install.js +34 -28
  9. package/packages/agent-client/src/mcp-config.js +40 -40
  10. package/packages/agent-client/src/runtime.js +41 -20
  11. package/packages/agent-client/src/setup-status.js +23 -30
  12. package/packages/mcp-server/src/bin.js +57 -5
  13. package/packages/mcp-server/src/handlers.js +573 -256
  14. package/packages/mcp-server/src/server.js +568 -257
  15. package/packages/native-host/bin/bridge-daemon.js +39 -6
  16. package/packages/native-host/bin/install-manifest.js +26 -4
  17. package/packages/native-host/bin/postinstall.js +4 -2
  18. package/packages/native-host/src/config.js +142 -13
  19. package/packages/native-host/src/daemon-process.js +396 -0
  20. package/packages/native-host/src/daemon.js +350 -150
  21. package/packages/native-host/src/framing.js +131 -11
  22. package/packages/native-host/src/install-manifest.js +194 -29
  23. package/packages/native-host/src/native-host.js +154 -102
  24. package/packages/protocol/src/budget.js +3 -7
  25. package/packages/protocol/src/capabilities.js +6 -3
  26. package/packages/protocol/src/defaults.js +1 -0
  27. package/packages/protocol/src/errors.js +15 -11
  28. package/packages/protocol/src/payload-cost.js +19 -6
  29. package/packages/protocol/src/protocol.js +242 -73
  30. package/packages/protocol/src/registry.js +311 -45
  31. package/packages/protocol/src/summary.js +260 -109
  32. package/packages/protocol/src/types.js +29 -4
  33. package/skills/browser-bridge/SKILL.md +3 -2
  34. package/skills/browser-bridge/agents/openai.yaml +3 -3
  35. package/skills/browser-bridge/references/interaction.md +34 -11
  36. package/skills/browser-bridge/references/patch-workflow.md +3 -0
  37. package/skills/browser-bridge/references/protocol.md +127 -71
  38. package/skills/browser-bridge/references/tailwind.md +12 -11
  39. package/skills/browser-bridge/references/token-efficiency.md +23 -22
  40. package/skills/browser-bridge/references/ui-workflows.md +8 -0
  41. package/CHANGELOG.md +0 -55
  42. package/assets/banner.jpg +0 -0
  43. package/assets/logo.png +0 -0
  44. package/assets/logo.svg +0 -65
  45. package/docs/api-reference.md +0 -157
  46. package/docs/cli-guide.md +0 -128
  47. package/docs/index.md +0 -25
  48. package/docs/manual-setup.md +0 -140
  49. package/docs/mcp-vs-cli.md +0 -258
  50. package/docs/publishing.md +0 -114
  51. package/docs/quickstart.md +0 -104
  52. package/docs/troubleshooting.md +0 -59
  53. package/docs/usage-scenarios.md +0 -136
  54. package/manifest.json +0 -52
  55. package/packages/extension/assets/icon-128.png +0 -0
  56. package/packages/extension/assets/icon-16.png +0 -0
  57. package/packages/extension/assets/icon-32.png +0 -0
  58. package/packages/extension/assets/icon-48.png +0 -0
  59. package/packages/extension/src/background-helpers.js +0 -459
  60. package/packages/extension/src/background-routing.js +0 -91
  61. package/packages/extension/src/background.js +0 -3227
  62. package/packages/extension/src/content-script-helpers.js +0 -281
  63. package/packages/extension/src/content-script.js +0 -1977
  64. package/packages/extension/src/debugger-coordinator.js +0 -188
  65. package/packages/extension/src/sidepanel-helpers.js +0 -102
  66. package/packages/extension/ui/offscreen.html +0 -6
  67. package/packages/extension/ui/offscreen.js +0 -61
  68. package/packages/extension/ui/popup.html +0 -35
  69. package/packages/extension/ui/popup.js +0 -279
  70. package/packages/extension/ui/sidepanel.html +0 -102
  71. package/packages/extension/ui/sidepanel.js +0 -1854
  72. package/packages/extension/ui/ui.css +0 -1159
@@ -1,7 +1,9 @@
1
1
  // @ts-check
2
2
 
3
+ import os from 'node:os';
3
4
  import {
4
5
  bridgeMethodNeedsTab,
6
+ createRuntimeContext,
5
7
  DEFAULT_CONSOLE_LIMIT,
6
8
  DEFAULT_MAX_HTML_LENGTH,
7
9
  DEFAULT_NETWORK_LIMIT,
@@ -9,7 +11,7 @@ import {
9
11
  getBudgetPreset,
10
12
  getErrorRecovery,
11
13
  isBudgetPresetName,
12
- METHODS,
14
+ METHOD_SET,
13
15
  summarizeBatchErrorItem,
14
16
  summarizeBatchResponseItem,
15
17
  } from '../../protocol/src/index.js';
@@ -17,17 +19,16 @@ import {
17
19
  getDoctorReport,
18
20
  requestBridge,
19
21
  resolveRef,
20
- withBridgeClient
22
+ withBridgeClient,
21
23
  } from '../../agent-client/src/runtime.js';
22
- import {
23
- annotateBridgeSummary,
24
- summarizeBridgeResponse,
25
- } from '../../agent-client/src/subagent.js';
24
+ import { annotateBridgeSummary, summarizeBridgeResponse } from '../../agent-client/src/subagent.js';
25
+ import { collectSetupStatus } from '../../agent-client/src/setup-status.js';
26
26
 
27
27
  /** @typedef {import('../../protocol/src/types.js').BridgeMethod} BridgeMethod */
28
28
  /** @typedef {import('../../protocol/src/types.js').BridgeResponse} BridgeResponse */
29
29
 
30
30
  const REQUEST_SOURCE = 'mcp';
31
+ const HOME_DIR = os.homedir();
31
32
 
32
33
  /**
33
34
  * @typedef {{
@@ -47,7 +48,7 @@ function createToolResult(summary, structuredContent = {}, isError = false) {
47
48
  const toolResult = {
48
49
  content: [{ type: /** @type {'text'} */ ('text'), text: summary }],
49
50
  structuredContent,
50
- ...(isError ? { isError: true } : {})
51
+ ...(isError ? { isError: true } : {}),
51
52
  };
52
53
  const delivered = estimateJsonPayloadCost(toolResult);
53
54
  return {
@@ -77,10 +78,14 @@ function summarizeToolResponse(response, method) {
77
78
  */
78
79
  function summarizeToolError(error) {
79
80
  const message = error instanceof Error ? error.message : String(error);
80
- return createToolResult(`ERROR: ${message}`, {
81
- ok: false,
82
- evidence: null
83
- }, true);
81
+ return createToolResult(
82
+ `ERROR: ${message}`,
83
+ {
84
+ ok: false,
85
+ evidence: null,
86
+ },
87
+ true
88
+ );
84
89
  }
85
90
 
86
91
  /**
@@ -235,12 +240,12 @@ function applyHtmlBudgetPreset(args) {
235
240
  */
236
241
  async function requestBridgeWithRetry(client, method, params, options) {
237
242
  const response = await requestBridge(client, method, params, options);
238
- const recovery = !response.ok && response.error
239
- ? getErrorRecovery(response.error.code)
240
- : null;
243
+ const recovery = !response.ok && response.error ? getErrorRecovery(response.error.code) : null;
241
244
  if (!response.ok && recovery?.retry) {
242
245
  const delay = recovery.retryAfterMs ?? 1000;
243
- process.stderr.write(`[bbx-mcp] Retrying ${method} after ${delay}ms (${response.error.code})\n`);
246
+ process.stderr.write(
247
+ `[bbx-mcp] Retrying ${method} after ${delay}ms (${response.error.code})\n`
248
+ );
244
249
  await new Promise((r) => setTimeout(r, delay));
245
250
  return requestBridge(client, method, params, options);
246
251
  }
@@ -285,10 +290,10 @@ async function dispatchToolAction(actions, args, toolName) {
285
290
  const requestedTabId = typeof args.tabId === 'number' ? args.tabId : null;
286
291
  const ref = entry.ref
287
292
  ? await resolveToolRef(
288
- client,
289
- /** @type {{ elementRef?: string, selector?: string }} */ (args),
290
- requestedTabId,
291
- )
293
+ client,
294
+ /** @type {{ elementRef?: string, selector?: string }} */ (args),
295
+ requestedTabId
296
+ )
292
297
  : undefined;
293
298
  const response = await requestBridgeWithRetry(client, entry.method, entry.params(args, ref), {
294
299
  tabId: requestedTabId,
@@ -305,12 +310,13 @@ async function dispatchToolAction(actions, args, toolName) {
305
310
  export async function handleStatusTool() {
306
311
  try {
307
312
  const report = await getDoctorReport();
308
- const summary = report.issues.length === 0
309
- ? 'Browser Bridge is ready.'
310
- : `Browser Bridge has ${report.issues.length} setup issue(s).`;
313
+ const summary =
314
+ report.issues.length === 0
315
+ ? 'Browser Bridge is ready.'
316
+ : `Browser Bridge has ${report.issues.length} readiness issue(s).`;
311
317
  return createToolResult(summary, {
312
318
  ok: report.issues.length === 0,
313
- evidence: report
319
+ evidence: report,
314
320
  });
315
321
  } catch (error) {
316
322
  return summarizeToolError(error);
@@ -328,7 +334,7 @@ export async function handleTabsTool(args) {
328
334
  if (args.action === 'create') {
329
335
  return callBridgeTool('tabs.create', {
330
336
  url: args.url,
331
- active: args.active
337
+ active: args.active,
332
338
  });
333
339
  }
334
340
  if (args.action === 'close') {
@@ -343,15 +349,78 @@ export async function handleTabsTool(args) {
343
349
  /**
344
350
  /** @type {Record<string, ToolAction>} */
345
351
  export const DOM_ACTIONS = {
346
- query: { ref: false, method: 'dom.query', params: a => ({ selector: a.selector || 'body', withinRef: a.withinRef, maxNodes: a.maxNodes, maxDepth: a.maxDepth, textBudget: a.textBudget, includeBbox: a.includeBbox, attributeAllowlist: a.attributeAllowlist }) },
347
- describe: { ref: true, method: 'dom.describe', params: (_, r) => ({ elementRef: r }) },
348
- text: { ref: true, method: 'dom.get_text', params: (a, r) => ({ elementRef: r, textBudget: a.textBudget }) },
349
- attributes: { ref: true, method: 'dom.get_attributes', params: (a, r) => ({ elementRef: r, attributes: a.attributes || [] }) },
350
- wait: { ref: false, method: 'dom.wait_for', params: a => ({ selector: a.selector, text: a.text, state: a.state, timeoutMs: a.timeoutMs }) },
351
- find_text: { ref: false, method: 'dom.find_by_text', params: a => ({ text: a.text, exact: a.exact, selector: a.selector, maxResults: a.maxResults }) },
352
- find_role: { ref: false, method: 'dom.find_by_role', params: a => ({ role: a.role, name: a.name, selector: a.selector, maxResults: a.maxResults }) },
353
- html: { ref: true, method: 'dom.get_html', params: (a, r) => ({ elementRef: r, outer: a.outer, maxLength: a.maxLength }) },
354
- accessibility_tree: { ref: false, method: 'dom.get_accessibility_tree', params: a => ({ maxNodes: a.maxNodes, maxDepth: a.maxDepth }) },
352
+ query: {
353
+ ref: false,
354
+ method: 'dom.query',
355
+ params: (a) => ({
356
+ selector: a.selector || 'body',
357
+ withinRef: a.withinRef,
358
+ maxNodes: a.maxNodes,
359
+ maxDepth: a.maxDepth,
360
+ textBudget: a.textBudget,
361
+ includeBbox: a.includeBbox,
362
+ attributeAllowlist: a.attributeAllowlist,
363
+ }),
364
+ },
365
+ describe: {
366
+ ref: true,
367
+ method: 'dom.describe',
368
+ params: (_, r) => ({ elementRef: r }),
369
+ },
370
+ text: {
371
+ ref: true,
372
+ method: 'dom.get_text',
373
+ params: (a, r) => ({ elementRef: r, textBudget: a.textBudget }),
374
+ },
375
+ attributes: {
376
+ ref: true,
377
+ method: 'dom.get_attributes',
378
+ params: (a, r) => ({ elementRef: r, attributes: a.attributes || [] }),
379
+ },
380
+ wait: {
381
+ ref: false,
382
+ method: 'dom.wait_for',
383
+ params: (a) => ({
384
+ selector: a.selector,
385
+ text: a.text,
386
+ state: a.state,
387
+ timeoutMs: a.timeoutMs,
388
+ }),
389
+ },
390
+ find_text: {
391
+ ref: false,
392
+ method: 'dom.find_by_text',
393
+ params: (a) => ({
394
+ text: a.text,
395
+ exact: a.exact,
396
+ selector: a.selector,
397
+ maxResults: a.maxResults,
398
+ }),
399
+ },
400
+ find_role: {
401
+ ref: false,
402
+ method: 'dom.find_by_role',
403
+ params: (a) => ({
404
+ role: a.role,
405
+ name: a.name,
406
+ selector: a.selector,
407
+ maxResults: a.maxResults,
408
+ }),
409
+ },
410
+ html: {
411
+ ref: true,
412
+ method: 'dom.get_html',
413
+ params: (a, r) => ({
414
+ elementRef: r,
415
+ outer: a.outer,
416
+ maxLength: a.maxLength,
417
+ }),
418
+ },
419
+ accessibility_tree: {
420
+ ref: false,
421
+ method: 'dom.get_accessibility_tree',
422
+ params: (a) => ({ maxNodes: a.maxNodes, maxDepth: a.maxDepth }),
423
+ },
355
424
  };
356
425
 
357
426
  /**
@@ -375,10 +444,26 @@ export async function handleDomTool(args) {
375
444
 
376
445
  /** @type {Record<string, ToolAction>} */
377
446
  export const STYLES_LAYOUT_ACTIONS = {
378
- computed: { ref: true, method: 'styles.get_computed', params: (a, r) => ({ elementRef: r, properties: a.properties }) },
379
- matched_rules: { ref: true, method: 'styles.get_matched_rules', params: (_, r) => ({ elementRef: r }) },
380
- box_model: { ref: true, method: 'layout.get_box_model', params: (_, r) => ({ elementRef: r }) },
381
- hit_test: { ref: false, method: 'layout.hit_test', params: a => ({ x: a.x, y: a.y }) },
447
+ computed: {
448
+ ref: true,
449
+ method: 'styles.get_computed',
450
+ params: (a, r) => ({ elementRef: r, properties: a.properties }),
451
+ },
452
+ matched_rules: {
453
+ ref: true,
454
+ method: 'styles.get_matched_rules',
455
+ params: (_, r) => ({ elementRef: r }),
456
+ },
457
+ box_model: {
458
+ ref: true,
459
+ method: 'layout.get_box_model',
460
+ params: (_, r) => ({ elementRef: r }),
461
+ },
462
+ hit_test: {
463
+ ref: false,
464
+ method: 'layout.hit_test',
465
+ params: (a) => ({ x: a.x, y: a.y }),
466
+ },
382
467
  };
383
468
 
384
469
  /**
@@ -391,14 +476,41 @@ export async function handleStylesLayoutTool(args) {
391
476
 
392
477
  /** @type {Record<string, { method: BridgeMethod, params: (a: Record<string, unknown>) => Record<string, unknown> }>} */
393
478
  export const PAGE_ACTIONS = {
394
- state: { method: 'page.get_state', params: () => ({}) },
395
- evaluate: { method: 'page.evaluate', params: a => ({ expression: a.expression, awaitPromise: a.awaitPromise, timeoutMs: a.timeoutMs, returnByValue: a.returnByValue }) },
396
- console: { method: 'page.get_console', params: a => ({ level: a.level, clear: a.clear, limit: a.limit }) },
397
- wait_for_load: { method: 'page.wait_for_load_state', params: a => ({ timeoutMs: a.timeoutMs }) },
398
- storage: { method: 'page.get_storage', params: a => ({ type: a.type, keys: a.keys }) },
399
- text: { method: 'page.get_text', params: a => ({ textBudget: a.textBudget }) },
400
- network: { method: 'page.get_network', params: a => ({ clear: a.clear, limit: a.limit, urlPattern: a.urlPattern }) },
401
- performance: { method: 'performance.get_metrics', params: () => ({}) },
479
+ state: { method: 'page.get_state', params: () => ({}) },
480
+ evaluate: {
481
+ method: 'page.evaluate',
482
+ params: (a) => ({
483
+ expression: a.expression,
484
+ awaitPromise: a.awaitPromise,
485
+ timeoutMs: a.timeoutMs,
486
+ returnByValue: a.returnByValue,
487
+ }),
488
+ },
489
+ console: {
490
+ method: 'page.get_console',
491
+ params: (a) => ({ level: a.level, clear: a.clear, limit: a.limit }),
492
+ },
493
+ wait_for_load: {
494
+ method: 'page.wait_for_load_state',
495
+ params: (a) => ({ timeoutMs: a.timeoutMs }),
496
+ },
497
+ storage: {
498
+ method: 'page.get_storage',
499
+ params: (a) => ({ type: a.type, keys: a.keys }),
500
+ },
501
+ text: {
502
+ method: 'page.get_text',
503
+ params: (a) => ({ textBudget: a.textBudget }),
504
+ },
505
+ network: {
506
+ method: 'page.get_network',
507
+ params: (a) => ({
508
+ clear: a.clear,
509
+ limit: a.limit,
510
+ urlPattern: a.urlPattern,
511
+ }),
512
+ },
513
+ performance: { method: 'performance.get_metrics', params: () => ({}) },
402
514
  };
403
515
 
404
516
  /**
@@ -432,12 +544,39 @@ export async function handlePageTool(args) {
432
544
 
433
545
  /** @type {Record<string, { method: BridgeMethod, params: (a: Record<string, unknown>) => Record<string, unknown> }>} */
434
546
  export const NAVIGATION_ACTIONS = {
435
- navigate: { method: 'navigation.navigate', params: a => ({ url: a.url, waitForLoad: a.waitForLoad, timeoutMs: a.timeoutMs }) },
436
- reload: { method: 'navigation.reload', params: a => ({ waitForLoad: a.waitForLoad, timeoutMs: a.timeoutMs }) },
437
- go_back: { method: 'navigation.go_back', params: a => ({ waitForLoad: a.waitForLoad, timeoutMs: a.timeoutMs }) },
438
- go_forward: { method: 'navigation.go_forward', params: a => ({ waitForLoad: a.waitForLoad, timeoutMs: a.timeoutMs }) },
439
- scroll: { method: 'viewport.scroll', params: a => ({ top: a.top, left: a.left, behavior: a.behavior, relative: a.relative }) },
440
- resize: { method: 'viewport.resize', params: a => ({ width: a.width, height: a.height, reset: a.reset }) },
547
+ navigate: {
548
+ method: 'navigation.navigate',
549
+ params: (a) => ({
550
+ url: a.url,
551
+ waitForLoad: a.waitForLoad,
552
+ timeoutMs: a.timeoutMs,
553
+ }),
554
+ },
555
+ reload: {
556
+ method: 'navigation.reload',
557
+ params: (a) => ({ waitForLoad: a.waitForLoad, timeoutMs: a.timeoutMs }),
558
+ },
559
+ go_back: {
560
+ method: 'navigation.go_back',
561
+ params: (a) => ({ waitForLoad: a.waitForLoad, timeoutMs: a.timeoutMs }),
562
+ },
563
+ go_forward: {
564
+ method: 'navigation.go_forward',
565
+ params: (a) => ({ waitForLoad: a.waitForLoad, timeoutMs: a.timeoutMs }),
566
+ },
567
+ scroll: {
568
+ method: 'viewport.scroll',
569
+ params: (a) => ({
570
+ top: a.top,
571
+ left: a.left,
572
+ behavior: a.behavior,
573
+ relative: a.relative,
574
+ }),
575
+ },
576
+ resize: {
577
+ method: 'viewport.resize',
578
+ params: (a) => ({ width: a.width, height: a.height, reset: a.reset }),
579
+ },
441
580
  };
442
581
 
443
582
  /**
@@ -459,15 +598,16 @@ export const INPUT_ACTION_METHODS = {
459
598
  focus: 'input.focus',
460
599
  type: 'input.type',
461
600
  press_key: 'input.press_key',
601
+ cdp_press_key: 'cdp.dispatch_key_event',
462
602
  set_checked: 'input.set_checked',
463
603
  select_option: 'input.select_option',
464
604
  hover: 'input.hover',
465
605
  drag: 'input.drag',
466
- scroll_into_view: 'input.scroll_into_view'
606
+ scroll_into_view: 'input.scroll_into_view',
467
607
  };
468
608
 
469
609
  /**
470
- * @param {{ action: string, elementRef?: string, selector?: string, button?: string, clickCount?: number, text?: string, clear?: boolean, submit?: boolean, key?: string, modifiers?: string[], checked?: boolean, values?: string[], labels?: string[], indexes?: number[], duration?: number, sourceElementRef?: string, sourceSelector?: string, destinationElementRef?: string, destinationSelector?: string, offsetX?: number, offsetY?: number, tabId?: number, budgetPreset?: 'quick' | 'normal' | 'deep' }} args
610
+ * @param {{ action: string, elementRef?: string, selector?: string, button?: string, clickCount?: number, text?: string, clear?: boolean, submit?: boolean, key?: string, code?: string, modifiers?: string[], checked?: boolean, values?: string[], labels?: string[], indexes?: number[], duration?: number, sourceElementRef?: string, sourceSelector?: string, destinationElementRef?: string, destinationSelector?: string, offsetX?: number, offsetY?: number, tabId?: number, budgetPreset?: 'quick' | 'normal' | 'deep' }} args
471
611
  * @returns {Promise<ToolResult>}
472
612
  */
473
613
  export async function handleInputTool(args) {
@@ -479,118 +619,190 @@ export async function handleInputTool(args) {
479
619
 
480
620
  switch (args.action) {
481
621
  case 'click': {
482
- const response = await requestBridge(client, 'input.click', {
483
- target: await elementTarget(),
484
- button: args.button,
485
- clickCount: args.clickCount
486
- }, {
487
- tabId: requestedTabId,
488
- source: REQUEST_SOURCE,
489
- tokenBudget: getToolTokenBudget(args),
490
- });
622
+ const response = await requestBridge(
623
+ client,
624
+ 'input.click',
625
+ {
626
+ target: await elementTarget(),
627
+ button: args.button,
628
+ clickCount: args.clickCount,
629
+ },
630
+ {
631
+ tabId: requestedTabId,
632
+ source: REQUEST_SOURCE,
633
+ tokenBudget: getToolTokenBudget(args),
634
+ }
635
+ );
491
636
  return summarizeToolResponse(response, 'input.click');
492
637
  }
493
638
  case 'focus': {
494
- const response = await requestBridge(client, 'input.focus', {
495
- target: await elementTarget()
496
- }, {
497
- tabId: requestedTabId,
498
- source: REQUEST_SOURCE,
499
- tokenBudget: getToolTokenBudget(args),
500
- });
639
+ const response = await requestBridge(
640
+ client,
641
+ 'input.focus',
642
+ {
643
+ target: await elementTarget(),
644
+ },
645
+ {
646
+ tabId: requestedTabId,
647
+ source: REQUEST_SOURCE,
648
+ tokenBudget: getToolTokenBudget(args),
649
+ }
650
+ );
501
651
  return summarizeToolResponse(response, 'input.focus');
502
652
  }
503
653
  case 'type': {
504
- const response = await requestBridge(client, 'input.type', {
505
- target: await elementTarget(),
506
- text: args.text,
507
- clear: args.clear,
508
- submit: args.submit
509
- }, {
510
- tabId: requestedTabId,
511
- source: REQUEST_SOURCE,
512
- tokenBudget: getToolTokenBudget(args),
513
- });
654
+ const response = await requestBridge(
655
+ client,
656
+ 'input.type',
657
+ {
658
+ target: await elementTarget(),
659
+ text: args.text,
660
+ clear: args.clear,
661
+ submit: args.submit,
662
+ },
663
+ {
664
+ tabId: requestedTabId,
665
+ source: REQUEST_SOURCE,
666
+ tokenBudget: getToolTokenBudget(args),
667
+ }
668
+ );
514
669
  return summarizeToolResponse(response, 'input.type');
515
670
  }
516
671
  case 'press_key': {
517
- const target = (args.elementRef || args.selector) ? await elementTarget() : undefined;
518
- const response = await requestBridge(client, 'input.press_key', {
519
- target,
520
- key: args.key,
521
- modifiers: args.modifiers
522
- }, {
523
- tabId: requestedTabId,
524
- source: REQUEST_SOURCE,
525
- tokenBudget: getToolTokenBudget(args),
526
- });
672
+ const target = args.elementRef || args.selector ? await elementTarget() : undefined;
673
+ const response = await requestBridge(
674
+ client,
675
+ 'input.press_key',
676
+ {
677
+ target,
678
+ key: args.key,
679
+ modifiers: args.modifiers,
680
+ },
681
+ {
682
+ tabId: requestedTabId,
683
+ source: REQUEST_SOURCE,
684
+ tokenBudget: getToolTokenBudget(args),
685
+ }
686
+ );
527
687
  return summarizeToolResponse(response, 'input.press_key');
528
688
  }
689
+ case 'cdp_press_key': {
690
+ const response = await requestBridge(
691
+ client,
692
+ 'cdp.dispatch_key_event',
693
+ {
694
+ key: args.key,
695
+ code: args.code,
696
+ modifiers: args.modifiers,
697
+ },
698
+ {
699
+ tabId: requestedTabId,
700
+ source: REQUEST_SOURCE,
701
+ tokenBudget: getToolTokenBudget(args),
702
+ }
703
+ );
704
+ return summarizeToolResponse(response, 'cdp.dispatch_key_event');
705
+ }
529
706
  case 'set_checked': {
530
- const response = await requestBridge(client, 'input.set_checked', {
531
- target: await elementTarget(),
532
- checked: args.checked
533
- }, {
534
- tabId: requestedTabId,
535
- source: REQUEST_SOURCE,
536
- tokenBudget: getToolTokenBudget(args),
537
- });
707
+ const response = await requestBridge(
708
+ client,
709
+ 'input.set_checked',
710
+ {
711
+ target: await elementTarget(),
712
+ checked: args.checked,
713
+ },
714
+ {
715
+ tabId: requestedTabId,
716
+ source: REQUEST_SOURCE,
717
+ tokenBudget: getToolTokenBudget(args),
718
+ }
719
+ );
538
720
  return summarizeToolResponse(response, 'input.set_checked');
539
721
  }
540
722
  case 'select_option': {
541
- const response = await requestBridge(client, 'input.select_option', {
542
- target: await elementTarget(),
543
- values: args.values,
544
- labels: args.labels,
545
- indexes: args.indexes
546
- }, {
547
- tabId: requestedTabId,
548
- source: REQUEST_SOURCE,
549
- tokenBudget: getToolTokenBudget(args),
550
- });
723
+ const response = await requestBridge(
724
+ client,
725
+ 'input.select_option',
726
+ {
727
+ target: await elementTarget(),
728
+ values: args.values,
729
+ labels: args.labels,
730
+ indexes: args.indexes,
731
+ },
732
+ {
733
+ tabId: requestedTabId,
734
+ source: REQUEST_SOURCE,
735
+ tokenBudget: getToolTokenBudget(args),
736
+ }
737
+ );
551
738
  return summarizeToolResponse(response, 'input.select_option');
552
739
  }
553
740
  case 'hover': {
554
- const response = await requestBridge(client, 'input.hover', {
555
- target: await elementTarget(),
556
- duration: args.duration
557
- }, {
558
- tabId: requestedTabId,
559
- source: REQUEST_SOURCE,
560
- tokenBudget: getToolTokenBudget(args),
561
- });
741
+ const response = await requestBridge(
742
+ client,
743
+ 'input.hover',
744
+ {
745
+ target: await elementTarget(),
746
+ duration: args.duration,
747
+ },
748
+ {
749
+ tabId: requestedTabId,
750
+ source: REQUEST_SOURCE,
751
+ tokenBudget: getToolTokenBudget(args),
752
+ }
753
+ );
562
754
  return summarizeToolResponse(response, 'input.hover');
563
755
  }
564
756
  case 'drag': {
565
757
  const source = {
566
- elementRef: args.sourceElementRef || (args.sourceSelector ? await resolveRef(client, args.sourceSelector, requestedTabId, REQUEST_SOURCE) : '')
758
+ elementRef:
759
+ args.sourceElementRef ||
760
+ (args.sourceSelector
761
+ ? await resolveRef(client, args.sourceSelector, requestedTabId, REQUEST_SOURCE)
762
+ : ''),
567
763
  };
568
764
  const destination = {
569
- elementRef: args.destinationElementRef || (args.destinationSelector ? await resolveRef(client, args.destinationSelector, requestedTabId, REQUEST_SOURCE) : '')
765
+ elementRef:
766
+ args.destinationElementRef ||
767
+ (args.destinationSelector
768
+ ? await resolveRef(client, args.destinationSelector, requestedTabId, REQUEST_SOURCE)
769
+ : ''),
570
770
  };
571
771
  if (!source.elementRef || !destination.elementRef) {
572
- return summarizeToolError('sourceElementRef/sourceSelector and destinationElementRef/destinationSelector are required for drag.');
772
+ return summarizeToolError(
773
+ 'sourceElementRef/sourceSelector and destinationElementRef/destinationSelector are required for drag.'
774
+ );
573
775
  }
574
- const response = await requestBridge(client, 'input.drag', {
575
- source,
576
- destination,
577
- offsetX: args.offsetX,
578
- offsetY: args.offsetY
579
- }, {
580
- tabId: requestedTabId,
581
- source: REQUEST_SOURCE,
582
- tokenBudget: getToolTokenBudget(args),
583
- });
776
+ const response = await requestBridge(
777
+ client,
778
+ 'input.drag',
779
+ {
780
+ source,
781
+ destination,
782
+ offsetX: args.offsetX,
783
+ offsetY: args.offsetY,
784
+ },
785
+ {
786
+ tabId: requestedTabId,
787
+ source: REQUEST_SOURCE,
788
+ tokenBudget: getToolTokenBudget(args),
789
+ }
790
+ );
584
791
  return summarizeToolResponse(response, 'input.drag');
585
792
  }
586
793
  case 'scroll_into_view': {
587
- const response = await requestBridge(client, 'input.scroll_into_view', {
588
- target: await elementTarget()
589
- }, {
590
- tabId: requestedTabId,
591
- source: REQUEST_SOURCE,
592
- tokenBudget: getToolTokenBudget(args),
593
- });
794
+ const response = await requestBridge(
795
+ client,
796
+ 'input.scroll_into_view',
797
+ {
798
+ target: await elementTarget(),
799
+ },
800
+ {
801
+ tabId: requestedTabId,
802
+ source: REQUEST_SOURCE,
803
+ tokenBudget: getToolTokenBudget(args),
804
+ }
805
+ );
594
806
  return summarizeToolResponse(response, 'input.scroll_into_view');
595
807
  }
596
808
  default:
@@ -601,23 +813,50 @@ export async function handleInputTool(args) {
601
813
 
602
814
  /** @type {Record<string, ToolAction>} */
603
815
  export const PATCH_ACTIONS = {
604
- apply_styles: { ref: true, method: 'patch.apply_styles', params: (a, r) => ({ target: { elementRef: r }, declarations: a.declarations, important: a.important, verify: a.verify }) },
605
- apply_dom: { ref: true, method: 'patch.apply_dom', params: (a, r) => {
606
- const operation = typeof a.operation === 'string' ? a.operation : '';
607
- /** @type {Record<string, string>} */
608
- const opMap = {
609
- setAttribute: 'set_attribute',
610
- removeAttribute: 'remove_attribute',
611
- addClass: 'toggle_class',
612
- removeClass: 'toggle_class',
613
- setTextContent: 'set_text',
614
- setProperty: 'set_attribute',
615
- };
616
- return { target: { elementRef: r }, operation: opMap[operation] || operation, value: a.value, name: a.name, verify: a.verify };
617
- }},
618
- list: { ref: false, method: 'patch.list', params: () => ({}) },
619
- rollback: { ref: false, method: 'patch.rollback', params: a => ({ patchId: a.patchId }) },
620
- commit_baseline: { ref: false, method: 'patch.commit_session_baseline', params: () => ({}) },
816
+ apply_styles: {
817
+ ref: true,
818
+ method: 'patch.apply_styles',
819
+ params: (a, r) => ({
820
+ target: { elementRef: r },
821
+ declarations: a.declarations,
822
+ important: a.important,
823
+ verify: a.verify,
824
+ }),
825
+ },
826
+ apply_dom: {
827
+ ref: true,
828
+ method: 'patch.apply_dom',
829
+ params: (a, r) => {
830
+ const operation = typeof a.operation === 'string' ? a.operation : '';
831
+ /** @type {Record<string, string>} */
832
+ const opMap = {
833
+ setAttribute: 'set_attribute',
834
+ removeAttribute: 'remove_attribute',
835
+ addClass: 'toggle_class',
836
+ removeClass: 'toggle_class',
837
+ setTextContent: 'set_text',
838
+ setProperty: 'set_attribute',
839
+ };
840
+ return {
841
+ target: { elementRef: r },
842
+ operation: opMap[operation] || operation,
843
+ value: a.value,
844
+ name: a.name,
845
+ verify: a.verify,
846
+ };
847
+ },
848
+ },
849
+ list: { ref: false, method: 'patch.list', params: () => ({}) },
850
+ rollback: {
851
+ ref: false,
852
+ method: 'patch.rollback',
853
+ params: (a) => ({ patchId: a.patchId }),
854
+ },
855
+ commit_baseline: {
856
+ ref: false,
857
+ method: 'patch.commit_session_baseline',
858
+ params: () => ({}),
859
+ },
621
860
  };
622
861
 
623
862
  /**
@@ -630,13 +869,37 @@ export async function handlePatchTool(args) {
630
869
 
631
870
  /** @type {Record<string, ToolAction>} */
632
871
  export const CAPTURE_ACTIONS = {
633
- element: { ref: true, method: 'screenshot.capture_element', params: (_, r) => ({ elementRef: r }) },
634
- region: { ref: false, method: 'screenshot.capture_region', params: a => /** @type {Record<string, unknown>} */ (a.rect || {}) },
635
- full_page: { ref: false, method: 'screenshot.capture_full_page', params: () => ({}) },
636
- cdp_document: { ref: false, method: 'cdp.get_document', params: () => ({}) },
637
- cdp_dom_snapshot: { ref: false, method: 'cdp.get_dom_snapshot', params: () => ({}) },
638
- cdp_box_model: { ref: true, method: 'cdp.get_box_model', params: (_, r) => ({ elementRef: r }) },
639
- cdp_computed_styles: { ref: true, method: 'cdp.get_computed_styles_for_node', params: (_, r) => ({ elementRef: r }) },
872
+ element: {
873
+ ref: true,
874
+ method: 'screenshot.capture_element',
875
+ params: (_, r) => ({ elementRef: r }),
876
+ },
877
+ region: {
878
+ ref: false,
879
+ method: 'screenshot.capture_region',
880
+ params: (a) => /** @type {Record<string, unknown>} */ (a.rect || {}),
881
+ },
882
+ full_page: {
883
+ ref: false,
884
+ method: 'screenshot.capture_full_page',
885
+ params: () => ({}),
886
+ },
887
+ cdp_document: { ref: false, method: 'cdp.get_document', params: () => ({}) },
888
+ cdp_dom_snapshot: {
889
+ ref: false,
890
+ method: 'cdp.get_dom_snapshot',
891
+ params: () => ({}),
892
+ },
893
+ cdp_box_model: {
894
+ ref: true,
895
+ method: 'cdp.get_box_model',
896
+ params: (_, r) => ({ elementRef: r }),
897
+ },
898
+ cdp_computed_styles: {
899
+ ref: true,
900
+ method: 'cdp.get_computed_styles_for_node',
901
+ params: (_, r) => ({ elementRef: r }),
902
+ },
640
903
  };
641
904
 
642
905
  /**
@@ -655,9 +918,11 @@ export async function handleCaptureTool(args) {
655
918
  */
656
919
  export async function handleSkillTool() {
657
920
  try {
658
- const { createRuntimeContext } = await import('../../protocol/src/index.js');
659
921
  const ctx = createRuntimeContext();
660
- return createToolResult('Runtime context retrieved.', { ok: true, runtimeContext: ctx });
922
+ return createToolResult('Runtime context retrieved.', {
923
+ ok: true,
924
+ runtimeContext: ctx,
925
+ });
661
926
  } catch (error) {
662
927
  return summarizeToolError(error);
663
928
  }
@@ -670,18 +935,15 @@ export async function handleSkillTool() {
670
935
  * @returns {Promise<ToolResult>}
671
936
  */
672
937
  export async function handleSetupTool(args) {
673
- const { collectSetupStatus } = await import('../../agent-client/src/setup-status.js');
674
- const projectPath = args.global !== false ? (await import('node:os')).homedir() : process.cwd();
938
+ const projectPath = args.global !== false ? HOME_DIR : process.cwd();
675
939
  const status = await collectSetupStatus({
676
940
  global: args.global !== false,
677
941
  cwd: process.cwd(),
678
- projectPath
942
+ projectPath,
679
943
  });
680
944
  const configuredMcp = status.mcpClients.filter((e) => e.configured).length;
681
945
  const installedSkills = status.skillTargets.filter((e) => e.installed).length;
682
- const summary = configuredMcp === 0 && installedSkills === 0
683
- ? 'No MCP or skill setup found. Run `bbx install-mcp` and `bbx install-skill`.'
684
- : `Setup: ${configuredMcp}/${status.mcpClients.length} MCP clients configured, ${installedSkills}/${status.skillTargets.length} skills installed.`;
946
+ const summary = `Optional agent integration status: ${configuredMcp}/${status.mcpClients.length} MCP clients configured, ${installedSkills}/${status.skillTargets.length} skills installed.`;
685
947
  return createToolResult(summary, { ok: true, status });
686
948
  }
687
949
 
@@ -697,11 +959,15 @@ export async function handleLogTool(args) {
697
959
  normal: DEFAULT_CONSOLE_LIMIT,
698
960
  deep: 100,
699
961
  });
700
- return callBridgeTool('log.tail', {
701
- limit: normalizedArgs.limit ?? DEFAULT_CONSOLE_LIMIT,
702
- }, {
703
- tokenBudget: getToolTokenBudget(normalizedArgs),
704
- });
962
+ return callBridgeTool(
963
+ 'log.tail',
964
+ {
965
+ limit: normalizedArgs.limit ?? DEFAULT_CONSOLE_LIMIT,
966
+ },
967
+ {
968
+ tokenBudget: getToolTokenBudget(normalizedArgs),
969
+ }
970
+ );
705
971
  }
706
972
 
707
973
  /**
@@ -711,7 +977,10 @@ export async function handleLogTool(args) {
711
977
  */
712
978
  export async function handleHealthTool() {
713
979
  return withToolClient(async (client) => {
714
- const response = await client.request({ method: 'health.ping', meta: { source: REQUEST_SOURCE } });
980
+ const response = await client.request({
981
+ method: 'health.ping',
982
+ meta: { source: REQUEST_SOURCE },
983
+ });
715
984
  return summarizeToolResponse(response, 'health.ping');
716
985
  });
717
986
  }
@@ -727,75 +996,87 @@ export async function handleBatchTool(args) {
727
996
 
728
997
  const calls = args.calls;
729
998
  return withToolClient(async (client) => {
730
- const results = await Promise.all(calls.map(async (call) => {
731
- if (!call || typeof call !== 'object' || typeof call.method !== 'string') {
732
- return {
733
- method: '',
734
- tabId: null,
735
- ok: false,
736
- summary: 'INVALID_REQUEST: Each batch call needs a method.',
737
- evidence: null,
738
- error: { code: 'INVALID_REQUEST', message: 'Each batch call needs a method.' },
739
- response: null,
740
- };
741
- }
999
+ const results = await Promise.all(
1000
+ calls.map(async (call) => {
1001
+ if (!call || typeof call !== 'object' || typeof call.method !== 'string') {
1002
+ return {
1003
+ method: '',
1004
+ tabId: null,
1005
+ ok: false,
1006
+ summary: 'INVALID_REQUEST: Each batch call needs a method.',
1007
+ evidence: null,
1008
+ error: {
1009
+ code: 'INVALID_REQUEST',
1010
+ message: 'Each batch call needs a method.',
1011
+ },
1012
+ response: null,
1013
+ };
1014
+ }
742
1015
 
743
- if (!METHODS.includes(/** @type {BridgeMethod} */ (call.method))) {
744
- return {
745
- method: call.method,
746
- tabId: null,
747
- ok: false,
748
- summary: `INVALID_REQUEST: Unknown bridge method "${call.method}".`,
749
- evidence: null,
750
- error: {
751
- code: 'INVALID_REQUEST',
752
- message: `Unknown bridge method "${call.method}".`,
753
- },
754
- response: null,
755
- };
756
- }
1016
+ if (!METHOD_SET.has(/** @type {BridgeMethod} */ (call.method))) {
1017
+ return {
1018
+ method: call.method,
1019
+ tabId: null,
1020
+ ok: false,
1021
+ summary: `INVALID_REQUEST: Unknown bridge method "${call.method}".`,
1022
+ evidence: null,
1023
+ error: {
1024
+ code: 'INVALID_REQUEST',
1025
+ message: `Unknown bridge method "${call.method}".`,
1026
+ },
1027
+ response: null,
1028
+ };
1029
+ }
757
1030
 
758
- const method = /** @type {BridgeMethod} */ (call.method);
759
- const tabId = bridgeMethodNeedsTab(method)
760
- ? (typeof call.tabId === 'number' ? call.tabId : null)
761
- : null;
762
- const tokenBudget = getToolTokenBudget(call);
1031
+ const method = /** @type {BridgeMethod} */ (call.method);
1032
+ const tabId = bridgeMethodNeedsTab(method)
1033
+ ? typeof call.tabId === 'number'
1034
+ ? call.tabId
1035
+ : null
1036
+ : null;
1037
+ const tokenBudget = getToolTokenBudget(call);
763
1038
 
764
- const startTime = Date.now();
765
- try {
766
- const response = await client.request({
767
- method,
768
- params: call.params || {},
769
- tabId,
770
- meta: {
771
- source: REQUEST_SOURCE,
772
- ...(tokenBudget != null ? { token_budget: tokenBudget } : {}),
773
- },
774
- });
775
- return summarizeBatchResponseItem({
776
- method,
777
- tabId,
778
- response,
779
- durationMs: Date.now() - startTime,
780
- });
781
- } catch (error) {
782
- return summarizeBatchErrorItem({
783
- method,
784
- tabId,
785
- error,
786
- durationMs: Date.now() - startTime,
787
- });
788
- }
789
- }));
1039
+ const startTime = Date.now();
1040
+ try {
1041
+ const response = await client.request({
1042
+ method,
1043
+ params: call.params || {},
1044
+ tabId,
1045
+ meta: {
1046
+ source: REQUEST_SOURCE,
1047
+ ...(tokenBudget != null ? { token_budget: tokenBudget } : {}),
1048
+ },
1049
+ });
1050
+ return summarizeBatchResponseItem({
1051
+ method,
1052
+ tabId,
1053
+ response,
1054
+ durationMs: Date.now() - startTime,
1055
+ });
1056
+ } catch (error) {
1057
+ return summarizeBatchErrorItem({
1058
+ method,
1059
+ tabId,
1060
+ error,
1061
+ durationMs: Date.now() - startTime,
1062
+ });
1063
+ }
1064
+ })
1065
+ );
790
1066
 
791
1067
  const failureCount = results.filter((result) => !result.ok).length;
792
- const summary = failureCount === 0
793
- ? `Batch executed ${results.length} call(s).`
794
- : `Batch executed ${results.length} call(s) with ${failureCount} error(s).`;
795
- return createToolResult(summary, {
796
- ok: failureCount === 0,
797
- results,
798
- }, failureCount > 0);
1068
+ const summary =
1069
+ failureCount === 0
1070
+ ? `Batch executed ${results.length} call(s).`
1071
+ : `Batch executed ${results.length} call(s) with ${failureCount} error(s).`;
1072
+ return createToolResult(
1073
+ summary,
1074
+ {
1075
+ ok: failureCount === 0,
1076
+ results,
1077
+ },
1078
+ failureCount > 0
1079
+ );
799
1080
  });
800
1081
  }
801
1082
 
@@ -804,7 +1085,7 @@ export async function handleBatchTool(args) {
804
1085
  * @returns {Promise<ToolResult>}
805
1086
  */
806
1087
  export async function handleRawCallTool(args) {
807
- if (!METHODS.includes(/** @type {BridgeMethod} */ (args.method))) {
1088
+ if (!METHOD_SET.has(/** @type {BridgeMethod} */ (args.method))) {
808
1089
  return summarizeToolError(`Unknown bridge method "${args.method}".`);
809
1090
  }
810
1091
 
@@ -815,21 +1096,25 @@ export async function handleRawCallTool(args) {
815
1096
  args.params || {},
816
1097
  {
817
1098
  tabId: typeof args.tabId === 'number' ? args.tabId : null,
818
- source: REQUEST_SOURCE
1099
+ source: REQUEST_SOURCE,
819
1100
  }
820
1101
  );
821
1102
 
822
1103
  if (!response.ok) {
823
- return createToolResult(response.error.message, {
824
- ok: false,
825
- error: response.error,
826
- response
827
- }, true);
1104
+ return createToolResult(
1105
+ response.error.message,
1106
+ {
1107
+ ok: false,
1108
+ error: response.error,
1109
+ response,
1110
+ },
1111
+ true
1112
+ );
828
1113
  }
829
1114
 
830
1115
  return createToolResult(`Called ${args.method}.`, {
831
1116
  ok: true,
832
- response: response.result
1117
+ response: response.result,
833
1118
  });
834
1119
  });
835
1120
  }
@@ -858,14 +1143,30 @@ const INVESTIGATE_SCOPES = {
858
1143
  label: 'quick',
859
1144
  steps: [
860
1145
  { method: 'page.get_state', params: () => ({}) },
861
- { method: 'dom.query', params: (a) => ({ selector: a.selector || 'body', maxNodes: 10, maxDepth: 2, textBudget: 300 }) },
1146
+ {
1147
+ method: 'dom.query',
1148
+ params: (a) => ({
1149
+ selector: a.selector || 'body',
1150
+ maxNodes: 10,
1151
+ maxDepth: 2,
1152
+ textBudget: 300,
1153
+ }),
1154
+ },
862
1155
  ],
863
1156
  },
864
1157
  normal: {
865
1158
  label: 'normal',
866
1159
  steps: [
867
1160
  { method: 'page.get_state', params: () => ({}) },
868
- { method: 'dom.query', params: (a) => ({ selector: a.selector || 'body', maxNodes: 25, maxDepth: 4, textBudget: 600 }) },
1161
+ {
1162
+ method: 'dom.query',
1163
+ params: (a) => ({
1164
+ selector: a.selector || 'body',
1165
+ maxNodes: 25,
1166
+ maxDepth: 4,
1167
+ textBudget: 600,
1168
+ }),
1169
+ },
869
1170
  { method: 'page.get_text', params: () => ({ textBudget: 4000 }) },
870
1171
  ],
871
1172
  },
@@ -873,9 +1174,20 @@ const INVESTIGATE_SCOPES = {
873
1174
  label: 'deep',
874
1175
  steps: [
875
1176
  { method: 'page.get_state', params: () => ({}) },
876
- { method: 'dom.query', params: (a) => ({ selector: a.selector || 'body', maxNodes: 50, maxDepth: 6, textBudget: 1000 }) },
1177
+ {
1178
+ method: 'dom.query',
1179
+ params: (a) => ({
1180
+ selector: a.selector || 'body',
1181
+ maxNodes: 50,
1182
+ maxDepth: 6,
1183
+ textBudget: 1000,
1184
+ }),
1185
+ },
877
1186
  { method: 'page.get_text', params: () => ({ textBudget: 8000 }) },
878
- { method: 'page.get_console', params: () => ({ level: 'warn', limit: 20 }) },
1187
+ {
1188
+ method: 'page.get_console',
1189
+ params: () => ({ level: 'warn', limit: 20 }),
1190
+ },
879
1191
  { method: 'page.get_network', params: () => ({ limit: 20 }) },
880
1192
  ],
881
1193
  },
@@ -947,12 +1259,17 @@ export async function handleInvestigateTool(args) {
947
1259
  ? `Investigation complete (${scope.label}, ${stepResults.length} steps, ${totalDuration}ms). Objective: ${objective}`
948
1260
  : `Investigation partial (${scope.label}, ${stepResults.length} steps, ${failedSteps.length} failed, ${totalDuration}ms). Objective: ${objective}`;
949
1261
 
950
- return createToolResult(summaryText, {
951
- ok: allOk,
952
- objective,
953
- scope: scopeName,
954
- heuristicFallback: true,
955
- steps: stepResults,
956
- }, !allOk);
1262
+ return createToolResult(
1263
+ summaryText,
1264
+ {
1265
+ ok: allOk,
1266
+ objective,
1267
+ scope: scopeName,
1268
+ heuristicFallback: true,
1269
+ steps: stepResults,
1270
+ failedSteps,
1271
+ },
1272
+ !allOk
1273
+ );
957
1274
  });
958
1275
  }