@browserbridge/bbx 1.7.3 → 1.7.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -7
- package/packages/agent-client/src/atomic-write.js +99 -0
- package/packages/agent-client/src/cli-helpers.js +20 -3
- package/packages/agent-client/src/cli.js +273 -76
- package/packages/agent-client/src/command-registry.js +12 -6
- package/packages/agent-client/src/install.js +39 -4
- package/packages/agent-client/src/mcp-config.js +28 -14
- package/packages/agent-client/src/remotes.js +93 -15
- package/packages/agent-client/src/runtime.js +6 -3
- package/packages/mcp-server/src/bin.js +10 -1
- package/packages/mcp-server/src/guidance.js +2 -2
- package/packages/mcp-server/src/handlers-capture.js +62 -32
- package/packages/mcp-server/src/handlers-dom.js +2 -2
- package/packages/mcp-server/src/handlers-navigation.js +30 -9
- package/packages/mcp-server/src/handlers-page.js +170 -143
- package/packages/mcp-server/src/handlers-utils.js +277 -8
- package/packages/mcp-server/src/handlers.js +31 -10
- package/packages/mcp-server/src/lifecycle.js +413 -0
- package/packages/mcp-server/src/server.js +59 -30
- package/packages/native-host/src/daemon.js +3 -1
- package/packages/protocol/src/capabilities.js +1 -1
- package/packages/protocol/src/defaults.js +2 -0
- package/packages/protocol/src/protocol.js +122 -9
- package/packages/protocol/src/registry.js +14 -14
- package/packages/protocol/src/summary.js +61 -30
- package/packages/protocol/src/types.ts +30 -1
- package/skills/browser-bridge/SKILL.md +4 -4
- package/skills/browser-bridge/references/interaction.md +1 -1
- package/skills/browser-bridge/references/protocol.md +3 -3
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
|
+
BridgeError,
|
|
4
5
|
bridgeMethodNeedsTab,
|
|
6
|
+
DEFAULT_CONSOLE_LIMIT,
|
|
7
|
+
DEFAULT_LOG_TAIL_LIMIT,
|
|
5
8
|
DEFAULT_MAX_HTML_LENGTH,
|
|
9
|
+
DEFAULT_MAX_NODES,
|
|
10
|
+
DEFAULT_NETWORK_LIMIT,
|
|
6
11
|
DEFAULT_PAGE_TEXT_BUDGET,
|
|
12
|
+
DEFAULT_TEXT_BUDGET,
|
|
7
13
|
estimateJsonPayloadCost,
|
|
8
14
|
getBudgetPreset,
|
|
9
15
|
getErrorRecovery,
|
|
@@ -121,10 +127,28 @@ export function createToolResult(
|
|
|
121
127
|
/**
|
|
122
128
|
* @param {BridgeResponse} response
|
|
123
129
|
* @param {string} [method]
|
|
130
|
+
* @param {Record<string, unknown>} [params]
|
|
124
131
|
* @returns {ToolResult}
|
|
125
132
|
*/
|
|
126
|
-
export function summarizeToolResponse(response, method) {
|
|
133
|
+
export function summarizeToolResponse(response, method, params = {}) {
|
|
127
134
|
const summary = annotateBridgeSummary(summarizeBridgeResponse(response, method), response);
|
|
135
|
+
const summaryRecord = /** @type {Record<string, unknown>} */ (summary);
|
|
136
|
+
if (response.ok) {
|
|
137
|
+
const evidence = getRequestAwareEvidence(response.result, method, params, summary.evidence);
|
|
138
|
+
summary.evidence = evidence.value;
|
|
139
|
+
if (evidence.truncated) {
|
|
140
|
+
summaryRecord.outputTruncated = true;
|
|
141
|
+
summaryRecord.outputLimit = evidence.limit;
|
|
142
|
+
}
|
|
143
|
+
} else {
|
|
144
|
+
const bounded = boundToolValue(summary.evidence);
|
|
145
|
+
summary.evidence = bounded.value;
|
|
146
|
+
summaryRecord.error = {
|
|
147
|
+
...response.error,
|
|
148
|
+
details: boundToolValue(response.error.details).value,
|
|
149
|
+
};
|
|
150
|
+
if (bounded.truncated) summaryRecord.outputTruncated = true;
|
|
151
|
+
}
|
|
128
152
|
return createToolResult(summary.summary, summary, !summary.ok);
|
|
129
153
|
}
|
|
130
154
|
|
|
@@ -134,11 +158,25 @@ export function summarizeToolResponse(response, method) {
|
|
|
134
158
|
*/
|
|
135
159
|
export function summarizeToolError(error) {
|
|
136
160
|
const message = error instanceof Error ? error.message : String(error);
|
|
161
|
+
const record =
|
|
162
|
+
error && typeof error === 'object' ? /** @type {Record<string, unknown>} */ (error) : {};
|
|
163
|
+
const code =
|
|
164
|
+
typeof record.code === 'string'
|
|
165
|
+
? record.code
|
|
166
|
+
: typeof error === 'string'
|
|
167
|
+
? 'INVALID_REQUEST'
|
|
168
|
+
: 'INTERNAL_ERROR';
|
|
169
|
+
const details = Object.hasOwn(record, 'details') ? record.details : null;
|
|
170
|
+
const boundedDetails = boundToolValue(details);
|
|
171
|
+
const recovery = getErrorRecovery(code);
|
|
137
172
|
return createToolResult(
|
|
138
|
-
|
|
173
|
+
`${code}: ${message}${recovery?.hint ? ` ${recovery.hint}` : ''}`,
|
|
139
174
|
{
|
|
140
175
|
ok: false,
|
|
141
|
-
evidence:
|
|
176
|
+
evidence: boundedDetails.value,
|
|
177
|
+
error: { code, message, details: boundedDetails.value },
|
|
178
|
+
recovery,
|
|
179
|
+
...(boundedDetails.truncated ? { outputTruncated: true } : {}),
|
|
142
180
|
},
|
|
143
181
|
true
|
|
144
182
|
);
|
|
@@ -179,7 +217,7 @@ export async function resolveToolRef(client, input, tabId = null) {
|
|
|
179
217
|
if (typeof input.selector === 'string' && input.selector) {
|
|
180
218
|
return resolveRef(client, input.selector, tabId, REQUEST_SOURCE);
|
|
181
219
|
}
|
|
182
|
-
throw new
|
|
220
|
+
throw new BridgeError('INVALID_REQUEST', 'Provide either elementRef or selector.');
|
|
183
221
|
}
|
|
184
222
|
|
|
185
223
|
/**
|
|
@@ -308,6 +346,233 @@ export function applyHtmlBudgetPreset(args) {
|
|
|
308
346
|
});
|
|
309
347
|
}
|
|
310
348
|
|
|
349
|
+
/**
|
|
350
|
+
* Apply a preset to method parameters before dispatch. Explicit method params
|
|
351
|
+
* always win over preset defaults.
|
|
352
|
+
*
|
|
353
|
+
* @param {BridgeMethod} method
|
|
354
|
+
* @param {Record<string, unknown>} params
|
|
355
|
+
* @param {unknown} budgetPreset
|
|
356
|
+
* @returns {Record<string, unknown>}
|
|
357
|
+
*/
|
|
358
|
+
export function applyMethodBudgetPreset(method, params, budgetPreset) {
|
|
359
|
+
const args = { ...params, budgetPreset };
|
|
360
|
+
let normalized = args;
|
|
361
|
+
if (method === 'dom.query' || method === 'dom.get_accessibility_tree') {
|
|
362
|
+
normalized = applyTreeBudgetPreset(args);
|
|
363
|
+
} else if (method === 'dom.get_text') {
|
|
364
|
+
normalized = applyTextBudgetPreset(args);
|
|
365
|
+
} else if (method === 'dom.get_html') {
|
|
366
|
+
normalized = applyHtmlBudgetPreset(args);
|
|
367
|
+
} else if (method === 'page.get_text') {
|
|
368
|
+
normalized = applyPageTextBudgetPreset(args);
|
|
369
|
+
} else if (method === 'page.get_console') {
|
|
370
|
+
normalized = applyLimitBudgetPreset(args, {
|
|
371
|
+
quick: 10,
|
|
372
|
+
normal: DEFAULT_CONSOLE_LIMIT,
|
|
373
|
+
deep: 100,
|
|
374
|
+
});
|
|
375
|
+
} else if (method === 'page.get_network') {
|
|
376
|
+
normalized = applyLimitBudgetPreset(args, {
|
|
377
|
+
quick: 10,
|
|
378
|
+
normal: DEFAULT_NETWORK_LIMIT,
|
|
379
|
+
deep: 100,
|
|
380
|
+
});
|
|
381
|
+
} else if (method === 'log.tail') {
|
|
382
|
+
normalized = applyLimitBudgetPreset(args, {
|
|
383
|
+
quick: 10,
|
|
384
|
+
normal: DEFAULT_LOG_TAIL_LIMIT,
|
|
385
|
+
deep: 100,
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
const { budgetPreset: _budgetPreset, ...methodParams } = normalized;
|
|
389
|
+
return methodParams;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* @param {unknown} input
|
|
394
|
+
* @param {{ maxEntries?: number, maxCharacters?: number, maxStringLength?: number }} [options]
|
|
395
|
+
* @returns {{ value: unknown, truncated: boolean, limit: Record<string, number> }}
|
|
396
|
+
*/
|
|
397
|
+
export function boundToolValue(input, options = {}) {
|
|
398
|
+
const maxEntries = options.maxEntries ?? 500;
|
|
399
|
+
const maxCharacters = options.maxCharacters ?? 20_000;
|
|
400
|
+
const maxStringLength = options.maxStringLength ?? 4_000;
|
|
401
|
+
const state = { entries: 0, characters: 0, truncated: false };
|
|
402
|
+
/** @type {(value: unknown, depth: number) => unknown} */
|
|
403
|
+
const visit = (value, depth) => {
|
|
404
|
+
if (state.entries >= maxEntries || state.characters >= maxCharacters || depth > 8) {
|
|
405
|
+
state.truncated = true;
|
|
406
|
+
return '[truncated]';
|
|
407
|
+
}
|
|
408
|
+
state.entries += 1;
|
|
409
|
+
if (typeof value === 'string') {
|
|
410
|
+
const limit = Math.min(value.length, maxStringLength, maxCharacters - state.characters);
|
|
411
|
+
state.characters += limit;
|
|
412
|
+
if (limit === value.length) return value;
|
|
413
|
+
state.truncated = true;
|
|
414
|
+
return value.slice(0, Math.max(0, limit));
|
|
415
|
+
}
|
|
416
|
+
if (Array.isArray(value)) {
|
|
417
|
+
const limit = Math.min(value.length, 100, maxEntries - state.entries);
|
|
418
|
+
if (limit < value.length) state.truncated = true;
|
|
419
|
+
return value.slice(0, limit).map((entry) => visit(entry, depth + 1));
|
|
420
|
+
}
|
|
421
|
+
if (value && typeof value === 'object') {
|
|
422
|
+
/** @type {Record<string, unknown>} */
|
|
423
|
+
const output = {};
|
|
424
|
+
const entries = Object.entries(/** @type {Record<string, unknown>} */ (value));
|
|
425
|
+
const limit = Math.min(entries.length, 100);
|
|
426
|
+
if (limit < entries.length) state.truncated = true;
|
|
427
|
+
for (const [key, entry] of entries.slice(0, limit)) {
|
|
428
|
+
if (state.entries >= maxEntries || state.characters >= maxCharacters) {
|
|
429
|
+
state.truncated = true;
|
|
430
|
+
break;
|
|
431
|
+
}
|
|
432
|
+
state.characters += key.length;
|
|
433
|
+
output[key] = visit(entry, depth + 1);
|
|
434
|
+
}
|
|
435
|
+
return output;
|
|
436
|
+
}
|
|
437
|
+
return value;
|
|
438
|
+
};
|
|
439
|
+
return {
|
|
440
|
+
value: visit(input, 0),
|
|
441
|
+
truncated: state.truncated,
|
|
442
|
+
limit: { maxEntries, maxCharacters, maxStringLength },
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Preserve useful evidence up to the caller's requested limits instead of
|
|
448
|
+
* fetching normal/deep results and silently returning only a fixed preview.
|
|
449
|
+
*
|
|
450
|
+
* @param {unknown} rawResult
|
|
451
|
+
* @param {string | undefined} method
|
|
452
|
+
* @param {Record<string, unknown>} params
|
|
453
|
+
* @param {unknown} fallback
|
|
454
|
+
* @returns {{ value: unknown, truncated: boolean, limit: Record<string, number> }}
|
|
455
|
+
*/
|
|
456
|
+
function getRequestAwareEvidence(rawResult, method, params, fallback) {
|
|
457
|
+
const result =
|
|
458
|
+
rawResult && typeof rawResult === 'object' && !Array.isArray(rawResult)
|
|
459
|
+
? /** @type {Record<string, unknown>} */ (rawResult)
|
|
460
|
+
: {};
|
|
461
|
+
if (method === 'page.get_text' || method === 'dom.get_text') {
|
|
462
|
+
const text = typeof result.text === 'string' ? result.text : result.value;
|
|
463
|
+
if (typeof text === 'string') {
|
|
464
|
+
const defaultLimit =
|
|
465
|
+
method === 'page.get_text' ? DEFAULT_PAGE_TEXT_BUDGET : DEFAULT_TEXT_BUDGET;
|
|
466
|
+
const requested = positiveInteger(params.textBudget) ?? defaultLimit;
|
|
467
|
+
const maxCharacters = Math.min(requested, 16_000);
|
|
468
|
+
const outputTruncated = text.length > maxCharacters;
|
|
469
|
+
return {
|
|
470
|
+
value: {
|
|
471
|
+
text: text.slice(0, maxCharacters),
|
|
472
|
+
length: typeof result.length === 'number' ? result.length : text.length,
|
|
473
|
+
truncated: result.truncated === true,
|
|
474
|
+
returnedChars: Math.min(text.length, maxCharacters),
|
|
475
|
+
},
|
|
476
|
+
truncated: outputTruncated,
|
|
477
|
+
limit: { maxCharacters },
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
if (method === 'dom.get_html' && typeof result.html === 'string') {
|
|
482
|
+
const requested = positiveInteger(params.maxLength) ?? DEFAULT_MAX_HTML_LENGTH;
|
|
483
|
+
const maxCharacters = Math.min(requested, 10_000);
|
|
484
|
+
return {
|
|
485
|
+
value: {
|
|
486
|
+
html: result.html.slice(0, maxCharacters),
|
|
487
|
+
truncated: result.truncated === true,
|
|
488
|
+
returnedChars: Math.min(result.html.length, maxCharacters),
|
|
489
|
+
},
|
|
490
|
+
truncated: result.html.length > maxCharacters,
|
|
491
|
+
limit: { maxCharacters },
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
if (method === 'dom.query' && Array.isArray(result.nodes)) {
|
|
495
|
+
const maxEntries = Math.min(positiveInteger(params.maxNodes) ?? DEFAULT_MAX_NODES, 100);
|
|
496
|
+
const nodes = result.nodes.slice(0, maxEntries).map(compactDomNode);
|
|
497
|
+
return {
|
|
498
|
+
value: nodes,
|
|
499
|
+
truncated: result.truncated === true || result.nodes.length > maxEntries,
|
|
500
|
+
limit: { maxEntries },
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
if (method === 'dom.get_accessibility_tree' && Array.isArray(result.nodes)) {
|
|
504
|
+
const maxEntries = Math.min(positiveInteger(params.maxNodes) ?? DEFAULT_MAX_NODES, 100);
|
|
505
|
+
const nodes = result.nodes.slice(0, maxEntries).map((value) => {
|
|
506
|
+
const node =
|
|
507
|
+
value && typeof value === 'object' ? /** @type {Record<string, unknown>} */ (value) : {};
|
|
508
|
+
return {
|
|
509
|
+
role: node.role,
|
|
510
|
+
name: node.name,
|
|
511
|
+
...(node.interactive === true ? { interactive: true } : {}),
|
|
512
|
+
...(node.value !== undefined ? { value: node.value } : {}),
|
|
513
|
+
};
|
|
514
|
+
});
|
|
515
|
+
return {
|
|
516
|
+
value: nodes,
|
|
517
|
+
truncated: result.truncated === true || result.nodes.length > maxEntries,
|
|
518
|
+
limit: { maxEntries },
|
|
519
|
+
};
|
|
520
|
+
}
|
|
521
|
+
if (
|
|
522
|
+
(method === 'page.get_console' || method === 'page.get_network' || method === 'log.tail') &&
|
|
523
|
+
Array.isArray(result.entries)
|
|
524
|
+
) {
|
|
525
|
+
const defaultLimit =
|
|
526
|
+
method === 'page.get_console'
|
|
527
|
+
? DEFAULT_CONSOLE_LIMIT
|
|
528
|
+
: method === 'page.get_network'
|
|
529
|
+
? DEFAULT_NETWORK_LIMIT
|
|
530
|
+
: DEFAULT_LOG_TAIL_LIMIT;
|
|
531
|
+
const maxEntries = Math.min(positiveInteger(params.limit) ?? defaultLimit, 100);
|
|
532
|
+
const selected =
|
|
533
|
+
method === 'log.tail'
|
|
534
|
+
? result.entries.slice(-maxEntries)
|
|
535
|
+
: result.entries.slice(0, maxEntries);
|
|
536
|
+
const bounded = boundToolValue(selected, { maxEntries: 500, maxCharacters: 20_000 });
|
|
537
|
+
return {
|
|
538
|
+
value: bounded.value,
|
|
539
|
+
truncated: result.entries.length > maxEntries || bounded.truncated,
|
|
540
|
+
limit: { maxEntries, maxCharacters: 20_000 },
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
return boundToolValue(fallback);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/** @param {unknown} value */
|
|
547
|
+
function positiveInteger(value) {
|
|
548
|
+
return typeof value === 'number' && Number.isInteger(value) && value > 0 ? value : null;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* @param {unknown} value
|
|
553
|
+
* @returns {Record<string, unknown>}
|
|
554
|
+
*/
|
|
555
|
+
function compactDomNode(value) {
|
|
556
|
+
const node =
|
|
557
|
+
value && typeof value === 'object' ? /** @type {Record<string, unknown>} */ (value) : {};
|
|
558
|
+
const attrs =
|
|
559
|
+
node.attrs && typeof node.attrs === 'object'
|
|
560
|
+
? /** @type {Record<string, unknown>} */ (node.attrs)
|
|
561
|
+
: {};
|
|
562
|
+
/** @type {Record<string, unknown>} */
|
|
563
|
+
const compact = { ref: node.elementRef, tag: node.tag };
|
|
564
|
+
if (node.id ?? attrs.id) compact.id = node.id ?? attrs.id;
|
|
565
|
+
if (typeof attrs.class === 'string') compact.cls = attrs.class.split(' ').slice(0, 3).join(' ');
|
|
566
|
+
if (node.role ?? attrs.role) compact.role = node.role ?? attrs.role;
|
|
567
|
+
if (node.name ?? attrs['aria-label']) compact.label = node.name ?? attrs['aria-label'];
|
|
568
|
+
if (attrs['data-testid']) compact.testId = attrs['data-testid'];
|
|
569
|
+
const text = typeof node.textExcerpt === 'string' ? node.textExcerpt : node.text;
|
|
570
|
+
if (typeof text === 'string' && text) compact.text = text.slice(0, 120);
|
|
571
|
+
if (Array.isArray(node.children) && node.children.length)
|
|
572
|
+
compact.childCount = node.children.length;
|
|
573
|
+
return compact;
|
|
574
|
+
}
|
|
575
|
+
|
|
311
576
|
/**
|
|
312
577
|
* @param {import('../../agent-client/src/client.js').BridgeClient} client
|
|
313
578
|
* @param {BridgeMethod} method
|
|
@@ -317,7 +582,10 @@ export function applyHtmlBudgetPreset(args) {
|
|
|
317
582
|
*/
|
|
318
583
|
export async function requestBridgeWithRetry(client, method, params, options) {
|
|
319
584
|
const response = await requestBridge(client, method, params, options);
|
|
320
|
-
const recovery =
|
|
585
|
+
const recovery =
|
|
586
|
+
!response.ok && response.error
|
|
587
|
+
? (response.error.recovery ?? getErrorRecovery(response.error.code))
|
|
588
|
+
: null;
|
|
321
589
|
if (!response.ok && recovery?.retry && isRetrySafeBridgeMethod(method, params)) {
|
|
322
590
|
const delay = recovery.retryAfterMs ?? 1000;
|
|
323
591
|
process.stderr.write(
|
|
@@ -343,7 +611,7 @@ export async function callBridgeTool(method, params = {}, options = {}) {
|
|
|
343
611
|
source: REQUEST_SOURCE,
|
|
344
612
|
tokenBudget: options.tokenBudget ?? null,
|
|
345
613
|
});
|
|
346
|
-
return summarizeToolResponse(response, options.summaryMethod || method);
|
|
614
|
+
return summarizeToolResponse(response, options.summaryMethod || method, params);
|
|
347
615
|
},
|
|
348
616
|
{ destinationId: options.destinationId ?? null }
|
|
349
617
|
);
|
|
@@ -379,12 +647,13 @@ export async function dispatchToolAction(actions, args, toolName) {
|
|
|
379
647
|
requestedTabId
|
|
380
648
|
)
|
|
381
649
|
: undefined;
|
|
382
|
-
const
|
|
650
|
+
const params = entry.params(args, ref);
|
|
651
|
+
const response = await requestBridgeWithRetry(client, entry.method, params, {
|
|
383
652
|
tabId: requestedTabId,
|
|
384
653
|
source: REQUEST_SOURCE,
|
|
385
654
|
tokenBudget: getToolTokenBudget(/** @type {{ budgetPreset?: unknown }} */ (args)),
|
|
386
655
|
});
|
|
387
|
-
return summarizeToolResponse(response, entry.method);
|
|
656
|
+
return summarizeToolResponse(response, entry.method, params);
|
|
388
657
|
},
|
|
389
658
|
{ destinationId: typeof args.destinationId === 'string' ? args.destinationId : null }
|
|
390
659
|
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// @ts-check
|
|
2
2
|
|
|
3
3
|
import os from 'node:os';
|
|
4
|
-
import {
|
|
4
|
+
import { DEFAULT_LOG_TAIL_LIMIT, createRuntimeContext } from '../../protocol/src/index.js';
|
|
5
5
|
import { collectSetupStatus } from '../../agent-client/src/setup-status.js';
|
|
6
6
|
import {
|
|
7
7
|
callBridgeTool,
|
|
@@ -82,15 +82,17 @@ export async function handleStatusTool(args = {}) {
|
|
|
82
82
|
);
|
|
83
83
|
const result = await callRemoteHealth(requestedDestinationId);
|
|
84
84
|
return createToolResult(
|
|
85
|
-
result.
|
|
86
|
-
? `Browser Bridge destination "${requestedDestinationId}" is
|
|
87
|
-
:
|
|
85
|
+
result.routeReady
|
|
86
|
+
? `Browser Bridge destination "${requestedDestinationId}" is ready.`
|
|
87
|
+
: result.reachable
|
|
88
|
+
? `Browser Bridge destination "${requestedDestinationId}" is reachable, but its browser route is not ready.`
|
|
89
|
+
: `Browser Bridge destination "${requestedDestinationId}" is not reachable.`,
|
|
88
90
|
{
|
|
89
|
-
ok: result.
|
|
91
|
+
ok: result.routeReady === true,
|
|
90
92
|
destination: destination ?? { id: requestedDestinationId, local: false },
|
|
91
93
|
...result,
|
|
92
94
|
},
|
|
93
|
-
result.
|
|
95
|
+
result.routeReady !== true
|
|
94
96
|
);
|
|
95
97
|
}
|
|
96
98
|
|
|
@@ -131,17 +133,36 @@ export async function handleStatusTool(args = {}) {
|
|
|
131
133
|
*/
|
|
132
134
|
async function callRemoteHealth(destinationId) {
|
|
133
135
|
const result = await callBridgeTool('health.ping', {}, { destinationId });
|
|
136
|
+
const evidence =
|
|
137
|
+
result.structuredContent.evidence && typeof result.structuredContent.evidence === 'object'
|
|
138
|
+
? /** @type {Record<string, unknown>} */ (result.structuredContent.evidence)
|
|
139
|
+
: {};
|
|
140
|
+
const access =
|
|
141
|
+
evidence.access && typeof evidence.access === 'object'
|
|
142
|
+
? /** @type {Record<string, unknown>} */ (evidence.access)
|
|
143
|
+
: {};
|
|
144
|
+
const reachable = result.structuredContent.ok === true && evidence.daemon === 'ok';
|
|
134
145
|
return {
|
|
135
|
-
reachable
|
|
146
|
+
reachable,
|
|
147
|
+
daemonReachable: reachable,
|
|
148
|
+
extensionConnected: evidence.extensionConnected === true,
|
|
149
|
+
accessEnabled: access.enabled === true,
|
|
150
|
+
routeReady: reachable && access.routeReady === true,
|
|
151
|
+
routeTabId: typeof access.routeTabId === 'number' ? access.routeTabId : null,
|
|
152
|
+
routeReason: typeof access.reason === 'string' ? access.reason : null,
|
|
136
153
|
health: result.structuredContent,
|
|
137
154
|
};
|
|
138
155
|
}
|
|
139
156
|
|
|
140
157
|
/**
|
|
158
|
+
* @param {{ destinationId?: string }} [args]
|
|
141
159
|
* @returns {Promise<ToolResult>}
|
|
142
160
|
*/
|
|
143
|
-
export async function handleSkillTool() {
|
|
161
|
+
export async function handleSkillTool(args = {}) {
|
|
144
162
|
try {
|
|
163
|
+
if (typeof args.destinationId === 'string') {
|
|
164
|
+
return callBridgeTool('skill.get_runtime_context', {}, { destinationId: args.destinationId });
|
|
165
|
+
}
|
|
145
166
|
const ctx = createRuntimeContext();
|
|
146
167
|
return createToolResult('Runtime context retrieved.', {
|
|
147
168
|
ok: true,
|
|
@@ -176,13 +197,13 @@ export async function handleSetupTool(args) {
|
|
|
176
197
|
export async function handleLogTool(args) {
|
|
177
198
|
const normalizedArgs = applyLimitBudgetPreset(args, {
|
|
178
199
|
quick: 10,
|
|
179
|
-
normal:
|
|
200
|
+
normal: DEFAULT_LOG_TAIL_LIMIT,
|
|
180
201
|
deep: 100,
|
|
181
202
|
});
|
|
182
203
|
return callBridgeTool(
|
|
183
204
|
'log.tail',
|
|
184
205
|
{
|
|
185
|
-
limit: normalizedArgs.limit ??
|
|
206
|
+
limit: normalizedArgs.limit ?? DEFAULT_LOG_TAIL_LIMIT,
|
|
186
207
|
},
|
|
187
208
|
{
|
|
188
209
|
tokenBudget: getToolTokenBudget(normalizedArgs),
|