@jobshimo/browser-link 0.16.0 → 0.17.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.
- package/dist/agent-instructions/content.js +10 -0
- package/dist/agent-instructions/content.js.map +1 -1
- package/dist/commands/about.js +2 -0
- package/dist/commands/about.js.map +1 -1
- package/dist/extension/background.js +297 -194
- package/dist/extension/background.js.map +1 -1
- package/dist/extension/flow.d.ts +161 -0
- package/dist/extension/flow.js +213 -0
- package/dist/extension/flow.js.map +1 -0
- package/dist/extension/manifest.json +1 -1
- package/dist/permissions.js +6 -0
- package/dist/permissions.js.map +1 -1
- package/dist/tools/browser-definitions.js +155 -0
- package/dist/tools/browser-definitions.js.map +1 -1
- package/dist/tools/browser-dispatch.js +211 -2
- package/dist/tools/browser-dispatch.js.map +1 -1
- package/dist/tools/server-instructions.js +13 -0
- package/dist/tools/server-instructions.js.map +1 -1
- package/package.json +1 -1
|
@@ -77,6 +77,16 @@ function body() {
|
|
|
77
77
|
'`browser.evaluate` is the escape hatch. Every dedicated tool below is',
|
|
78
78
|
'cheaper in tokens AND more reliable than a hand-rolled expression.',
|
|
79
79
|
'',
|
|
80
|
+
'- Any multi-step interaction (find a target → act on it → wait → act',
|
|
81
|
+
' again) → `browser.flow({ steps: [...] })` is now the DEFAULT, not a',
|
|
82
|
+
' fallback. find → click → wait_for → type → press used to cost 5+',
|
|
83
|
+
' separate MCP round trips — each one a full LLM inference — and now',
|
|
84
|
+
' costs ONE `browser.flow` call (two if a step fails and you need a',
|
|
85
|
+
" corrective follow-up). A `find` step's selector becomes the implicit",
|
|
86
|
+
' target for the very next click/type/press step that omits',
|
|
87
|
+
' `selector`. Flow is strictly sequential and fails fast — reach for',
|
|
88
|
+
' individual click/type/press/find/wait_for calls only when you need',
|
|
89
|
+
' to branch on an intermediate result.',
|
|
80
90
|
'- Finding an element by visible text → `browser.find({ text, role? })`,',
|
|
81
91
|
' NOT `browser.evaluate` with a `textContent` grep. `find` covers',
|
|
82
92
|
' `<div onclick>` (which a naive `querySelectorAll("button")` MISSES',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/agent-instructions/content.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC;;;;;;;;;;GAUG;AAEH,MAAM,CAAC,MAAM,YAAY,GAAG,sCAAsC,CAAC;AACnE,MAAM,CAAC,MAAM,UAAU,GAAG,wCAAwC,CAAC;AAEnE,MAAM,UAAU,WAAW,CAAC,OAAO,GAAG,OAAO;IAC3C,OAAO,GAAG,YAAY,KAAK,OAAO,MAAM,CAAC;AAC3C,CAAC;AAED;;;;yCAIyC;AACzC,MAAM,CAAC,MAAM,QAAQ,GACnB,gFAAgF,CAAC;AACnF,MAAM,CAAC,MAAM,MAAM,GAAG,4CAA4C,CAAC;AAEnE;;;;mBAImB;AACnB,SAAS,IAAI;IACX,OAAO;QACL,mDAAmD;QACnD,EAAE;QACF,kFAAkF;QAClF,wEAAwE;QACxE,+DAA+D;QAC/D,EAAE;QACF,0DAA0D;QAC1D,EAAE;QACF,kDAAkD;QAClD,qEAAqE;QACrE,+DAA+D;QAC/D,yEAAyE;QACzE,oDAAoD;QACpD,yEAAyE;QACzE,sEAAsE;QACtE,kFAAkF;QAClF,EAAE;QACF,qDAAqD;QACrD,qEAAqE;QACrE,EAAE;QACF,yCAAyC;QACzC,gEAAgE;QAChE,EAAE;QACF,yEAAyE;QACzE,mBAAmB;QACnB,qEAAqE;QACrE,EAAE;QACF,4DAA4D;QAC5D,wEAAwE;QACxE,0CAA0C;QAC1C,EAAE;QACF,wCAAwC;QACxC,EAAE;QACF,8EAA8E;QAC9E,2EAA2E;QAC3E,EAAE;QACF,WAAW;QACX,EAAE;QACF,iDAAiD;QACjD,4EAA4E;QAC5E,2DAA2D;QAC3D,2EAA2E;QAC3E,EAAE;QACF,8EAA8E;QAC9E,EAAE;QACF,uEAAuE;QACvE,oEAAoE;QACpE,EAAE;QACF,yEAAyE;QACzE,mEAAmE;QACnE,sEAAsE;QACtE,iEAAiE;QACjE,kEAAkE;QAClE,gDAAgD;QAChD,yEAAyE;QACzE,sEAAsE;QACtE,8DAA8D;QAC9D,kEAAkE;QAClE,uEAAuE;QACvE,oEAAoE;QACpE,4BAA4B;QAC5B,oEAAoE;QACpE,sEAAsE;QACtE,wEAAwE;QACxE,6DAA6D;QAC7D,iHAAiH;QACjH,2EAA2E;QAC3E,oEAAoE;QACpE,mDAAmD;QACnD,kEAAkE;QAClE,oEAAoE;QACpE,2CAA2C;QAC3C,+DAA+D;QAC/D,uEAAuE;QACvE,uEAAuE;QACvE,sEAAsE;QACtE,qEAAqE;QACrE,wEAAwE;QACxE,wEAAwE;QACxE,yEAAyE;QACzE,sDAAsD;QACtD,kEAAkE;QAClE,wEAAwE;QACxE,sEAAsE;QACtE,mEAAmE;QACnE,sEAAsE;QACtE,sEAAsE;QACtE,sEAAsE;QACtE,qEAAqE;QACrE,kEAAkE;QAClE,gDAAgD;QAChD,sEAAsE;QACtE,qEAAqE;QACrE,+CAA+C;KAChD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;;;;;;kDAOkD;AAClD,MAAM,UAAU,KAAK,CAAC,OAAO,GAAG,OAAO,EAAE,MAAqB,IAAI;IAChE,MAAM,KAAK,GAAG;QACZ,WAAW,CAAC,OAAO,CAAC;QACpB,kCAAkC,OAAO,0FAA0F;QACnI,EAAE;QACF,IAAI,EAAE;QACN,EAAE;QACF,UAAU;QACV,EAAE;KACH,CAAC;IACF,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1C,4EAA4E;IAC5E,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACjE,CAAC;AAED;;+DAE+D;AAC/D,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,qEAAqE,CAAC,IAAI,EAAE,CAAC;SAC7F,MAAM,CAAC;AACZ,CAAC;AAED;;4DAE4D;AAC5D,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;IAChD,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;IACjD,MAAM,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IAC9B,OAAO,IAAI,GAAG,MAAM,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;AACnD,CAAC;AAWD,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACjD,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;IAChD,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,MAAM,sBAAsB,GAAG,UAAU,GAAG,GAAG,CAAC,KAAK,CAAC;IACtD,MAAM,oBAAoB,GAAG,sBAAsB,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACpE,6DAA6D;IAC7D,MAAM,QAAQ,GAAG,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAG,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,oBAAoB,GAAG,CAAC,CAAC,CAAC,CAAC,oBAAoB,CAAC;IACrF,OAAO;QACL,UAAU,EAAE,KAAK,CAAC,KAAK;QACvB,QAAQ;QACR,gBAAgB,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,IAAI,IAAI;KAChD,CAAC;AACJ,CAAC"}
|
|
1
|
+
{"version":3,"file":"content.js","sourceRoot":"","sources":["../../src/agent-instructions/content.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAExC;;;;;;;;;;GAUG;AAEH,MAAM,CAAC,MAAM,YAAY,GAAG,sCAAsC,CAAC;AACnE,MAAM,CAAC,MAAM,UAAU,GAAG,wCAAwC,CAAC;AAEnE,MAAM,UAAU,WAAW,CAAC,OAAO,GAAG,OAAO;IAC3C,OAAO,GAAG,YAAY,KAAK,OAAO,MAAM,CAAC;AAC3C,CAAC;AAED;;;;yCAIyC;AACzC,MAAM,CAAC,MAAM,QAAQ,GACnB,gFAAgF,CAAC;AACnF,MAAM,CAAC,MAAM,MAAM,GAAG,4CAA4C,CAAC;AAEnE;;;;mBAImB;AACnB,SAAS,IAAI;IACX,OAAO;QACL,mDAAmD;QACnD,EAAE;QACF,kFAAkF;QAClF,wEAAwE;QACxE,+DAA+D;QAC/D,EAAE;QACF,0DAA0D;QAC1D,EAAE;QACF,kDAAkD;QAClD,qEAAqE;QACrE,+DAA+D;QAC/D,yEAAyE;QACzE,oDAAoD;QACpD,yEAAyE;QACzE,sEAAsE;QACtE,kFAAkF;QAClF,EAAE;QACF,qDAAqD;QACrD,qEAAqE;QACrE,EAAE;QACF,yCAAyC;QACzC,gEAAgE;QAChE,EAAE;QACF,yEAAyE;QACzE,mBAAmB;QACnB,qEAAqE;QACrE,EAAE;QACF,4DAA4D;QAC5D,wEAAwE;QACxE,0CAA0C;QAC1C,EAAE;QACF,wCAAwC;QACxC,EAAE;QACF,8EAA8E;QAC9E,2EAA2E;QAC3E,EAAE;QACF,WAAW;QACX,EAAE;QACF,iDAAiD;QACjD,4EAA4E;QAC5E,2DAA2D;QAC3D,2EAA2E;QAC3E,EAAE;QACF,8EAA8E;QAC9E,EAAE;QACF,uEAAuE;QACvE,oEAAoE;QACpE,EAAE;QACF,sEAAsE;QACtE,uEAAuE;QACvE,oEAAoE;QACpE,sEAAsE;QACtE,qEAAqE;QACrE,wEAAwE;QACxE,6DAA6D;QAC7D,sEAAsE;QACtE,sEAAsE;QACtE,wCAAwC;QACxC,yEAAyE;QACzE,mEAAmE;QACnE,sEAAsE;QACtE,iEAAiE;QACjE,kEAAkE;QAClE,gDAAgD;QAChD,yEAAyE;QACzE,sEAAsE;QACtE,8DAA8D;QAC9D,kEAAkE;QAClE,uEAAuE;QACvE,oEAAoE;QACpE,4BAA4B;QAC5B,oEAAoE;QACpE,sEAAsE;QACtE,wEAAwE;QACxE,6DAA6D;QAC7D,iHAAiH;QACjH,2EAA2E;QAC3E,oEAAoE;QACpE,mDAAmD;QACnD,kEAAkE;QAClE,oEAAoE;QACpE,2CAA2C;QAC3C,+DAA+D;QAC/D,uEAAuE;QACvE,uEAAuE;QACvE,sEAAsE;QACtE,qEAAqE;QACrE,wEAAwE;QACxE,wEAAwE;QACxE,yEAAyE;QACzE,sDAAsD;QACtD,kEAAkE;QAClE,wEAAwE;QACxE,sEAAsE;QACtE,mEAAmE;QACnE,sEAAsE;QACtE,sEAAsE;QACtE,sEAAsE;QACtE,qEAAqE;QACrE,kEAAkE;QAClE,gDAAgD;QAChD,sEAAsE;QACtE,qEAAqE;QACrE,+CAA+C;KAChD,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;;;;;;;kDAOkD;AAClD,MAAM,UAAU,KAAK,CAAC,OAAO,GAAG,OAAO,EAAE,MAAqB,IAAI;IAChE,MAAM,KAAK,GAAG;QACZ,WAAW,CAAC,OAAO,CAAC;QACpB,kCAAkC,OAAO,0FAA0F;QACnI,EAAE;QACF,IAAI,EAAE;QACN,EAAE;QACF,UAAU;QACV,EAAE;KACH,CAAC;IACF,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC1C,4EAA4E;IAC5E,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;AACjE,CAAC;AAED;;+DAE+D;AAC/D,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,qEAAqE,CAAC,IAAI,EAAE,CAAC;SAC7F,MAAM,CAAC;AACZ,CAAC;AAED;;4DAE4D;AAC5D,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,MAAM,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;IAChD,MAAM,OAAO,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC;IACjD,MAAM,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IAC9B,OAAO,IAAI,GAAG,MAAM,IAAI,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC;AACnD,CAAC;AAWD,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,MAAM,UAAU,GAAG,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACjD,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;IAChD,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,MAAM,sBAAsB,GAAG,UAAU,GAAG,GAAG,CAAC,KAAK,CAAC;IACtD,MAAM,oBAAoB,GAAG,sBAAsB,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IACpE,6DAA6D;IAC7D,MAAM,QAAQ,GAAG,IAAI,CAAC,oBAAoB,CAAC,CAAC;IAC5C,MAAM,QAAQ,GAAG,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,oBAAoB,GAAG,CAAC,CAAC,CAAC,CAAC,oBAAoB,CAAC;IACrF,OAAO;QACL,UAAU,EAAE,KAAK,CAAC,KAAK;QACvB,QAAQ;QACR,gBAAgB,EAAE,KAAK,CAAC,MAAM,EAAE,OAAO,IAAI,IAAI;KAChD,CAAC;AACJ,CAAC"}
|
package/dist/commands/about.js
CHANGED
|
@@ -46,6 +46,7 @@ export const I18N_ABOUT = {
|
|
|
46
46
|
' browser.click click an element by CSS selector',
|
|
47
47
|
' browser.type type into an input',
|
|
48
48
|
' browser.press press a key (trusted CDP input, not synthetic)',
|
|
49
|
+
' browser.flow run a find/click/type/press/wait_for sequence in one call',
|
|
49
50
|
' browser.evaluate run JavaScript in the page context',
|
|
50
51
|
' browser.console recent console messages (rolling 200)',
|
|
51
52
|
' browser.network recent network requests (rolling 200)',
|
|
@@ -143,6 +144,7 @@ export const I18N_ABOUT = {
|
|
|
143
144
|
' browser.click hacer click en un elemento por CSS selector',
|
|
144
145
|
' browser.type escribir en un input',
|
|
145
146
|
' browser.press presionar una tecla (input CDP confiable, no sintético)',
|
|
147
|
+
' browser.flow encadenar find/click/type/press/wait_for en una sola llamada',
|
|
146
148
|
' browser.evaluate ejecutar JavaScript en el contexto de la página',
|
|
147
149
|
' browser.console mensajes recientes de consola (200 últimos)',
|
|
148
150
|
' browser.network peticiones de red recientes (200 últimas)',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"about.js","sourceRoot":"","sources":["../../src/commands/about.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"about.js","sourceRoot":"","sources":["../../src/commands/about.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { buildSnapshotJs, buildFindJs, buildClickResolveJs, buildTypeResolveJs, buildFocusJs, } from './inpage/builders.js';
|
|
2
2
|
import { buildKeyEventSequence, resolveKey, modifiersToBitmask, MODIFIER_BITS, } from './keymap.js';
|
|
3
3
|
import { resolveSettleParams, settleSafely } from './settle.js';
|
|
4
|
+
import { runFlow, } from './flow.js';
|
|
4
5
|
const WS_URL = 'ws://127.0.0.1:17529';
|
|
5
6
|
const CDP_VERSION = '1.3';
|
|
6
7
|
const CONSOLE_BUFFER_MAX = 200;
|
|
@@ -528,6 +529,233 @@ async function waitForLoad(tabId, timeoutMs = 20_000) {
|
|
|
528
529
|
chrome.tabs.onUpdated.addListener(handler);
|
|
529
530
|
});
|
|
530
531
|
}
|
|
532
|
+
/**
|
|
533
|
+
* `performFind` / `performClick` / `performType` / `performPress` /
|
|
534
|
+
* `performWaitFor` — the extracted bodies of the standalone `find` /
|
|
535
|
+
* `click` / `type` / `press` / `wait_for` cases in `handleTool` below.
|
|
536
|
+
*
|
|
537
|
+
* Both the standalone `case` handlers AND `browser.flow`'s step executor
|
|
538
|
+
* (`runFlow` in `./flow.js`) call these exact functions — there is one
|
|
539
|
+
* implementation of each action, never a copy that can drift. Each
|
|
540
|
+
* returns an `ActionOutcome<T>` (`{ok:true,result} | {ok:false,error}`)
|
|
541
|
+
* instead of a wire-level `tool.response`, so the two call sites can
|
|
542
|
+
* unwrap it however they need to (the standalone case wraps it straight
|
|
543
|
+
* into a `tool.response`; `runFlow` inspects `result` for its own
|
|
544
|
+
* fail-fast rules — e.g. `wait_for`'s `matched:false` is `ok:true` here,
|
|
545
|
+
* matching the standalone tool's contract, and it is `runFlow`, not this
|
|
546
|
+
* function, that decides a non-match should stop the flow).
|
|
547
|
+
*
|
|
548
|
+
* `performType`'s `selector` is optional here (the standalone tool's JSON
|
|
549
|
+
* schema still requires it, so that path never changes): when omitted,
|
|
550
|
+
* this skips element resolution entirely and types into whatever
|
|
551
|
+
* currently has focus, mirroring how `performPress` already behaves with
|
|
552
|
+
* no `selector`. That is what lets a `browser.flow` step continue typing
|
|
553
|
+
* into a freshly-appeared, auto-focused input (e.g. a search box that
|
|
554
|
+
* opened after a preceding click) without a selector for it.
|
|
555
|
+
*/
|
|
556
|
+
async function performFind(tabId, params) {
|
|
557
|
+
try {
|
|
558
|
+
const { text } = params;
|
|
559
|
+
if (!text)
|
|
560
|
+
return { ok: false, error: 'find: text required' };
|
|
561
|
+
const value = await evaluateInTab(tabId, buildFindJs({ text, role: params.role, exact: params.exact === true }));
|
|
562
|
+
return { ok: true, result: value };
|
|
563
|
+
}
|
|
564
|
+
catch (err) {
|
|
565
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
async function performClick(tabId, params) {
|
|
569
|
+
try {
|
|
570
|
+
const { selector, force = false } = params;
|
|
571
|
+
const settle = resolveSettleParams({
|
|
572
|
+
settle_ms: params.settle_ms,
|
|
573
|
+
settle_timeout_ms: params.settle_timeout_ms,
|
|
574
|
+
});
|
|
575
|
+
const resolved = await evaluateInTab(tabId, buildClickResolveJs({ selector, force }));
|
|
576
|
+
if (!resolved.ok) {
|
|
577
|
+
if (resolved.reason === 'occluded') {
|
|
578
|
+
return {
|
|
579
|
+
ok: false,
|
|
580
|
+
error: `Element covered by ${resolved.blocker} — click the covering element or dismiss it first`,
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
return { ok: false, error: `Element not found: ${selector}` };
|
|
584
|
+
}
|
|
585
|
+
await cdp(tabId, 'Input.dispatchMouseEvent', {
|
|
586
|
+
type: 'mouseMoved',
|
|
587
|
+
x: resolved.x,
|
|
588
|
+
y: resolved.y,
|
|
589
|
+
});
|
|
590
|
+
await cdp(tabId, 'Input.dispatchMouseEvent', {
|
|
591
|
+
type: 'mousePressed',
|
|
592
|
+
x: resolved.x,
|
|
593
|
+
y: resolved.y,
|
|
594
|
+
button: 'left',
|
|
595
|
+
clickCount: 1,
|
|
596
|
+
});
|
|
597
|
+
await cdp(tabId, 'Input.dispatchMouseEvent', {
|
|
598
|
+
type: 'mouseReleased',
|
|
599
|
+
x: resolved.x,
|
|
600
|
+
y: resolved.y,
|
|
601
|
+
button: 'left',
|
|
602
|
+
clickCount: 1,
|
|
603
|
+
});
|
|
604
|
+
const settleResult = await runSettle(tabId, settle);
|
|
605
|
+
const result = { clicked: selector, tag: resolved.tag };
|
|
606
|
+
if (settleResult)
|
|
607
|
+
result.settle = settleResult;
|
|
608
|
+
return { ok: true, result };
|
|
609
|
+
}
|
|
610
|
+
catch (err) {
|
|
611
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
async function performType(tabId, params) {
|
|
615
|
+
try {
|
|
616
|
+
const { selector, text, clear = false } = params;
|
|
617
|
+
const settle = resolveSettleParams({
|
|
618
|
+
settle_ms: params.settle_ms,
|
|
619
|
+
settle_timeout_ms: params.settle_timeout_ms,
|
|
620
|
+
});
|
|
621
|
+
if (selector !== undefined) {
|
|
622
|
+
const focused = await evaluateInTab(tabId, buildTypeResolveJs({ selector, clear }));
|
|
623
|
+
if (!focused) {
|
|
624
|
+
return { ok: false, error: `Element not found: ${selector}` };
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
await cdp(tabId, 'Input.insertText', { text });
|
|
628
|
+
const settleResult = await runSettle(tabId, settle);
|
|
629
|
+
const result = { typed: text.length };
|
|
630
|
+
if (selector !== undefined)
|
|
631
|
+
result.selector = selector;
|
|
632
|
+
if (settleResult)
|
|
633
|
+
result.settle = settleResult;
|
|
634
|
+
return { ok: true, result };
|
|
635
|
+
}
|
|
636
|
+
catch (err) {
|
|
637
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
async function performPress(tabId, params) {
|
|
641
|
+
try {
|
|
642
|
+
const key = params.key;
|
|
643
|
+
if (!key)
|
|
644
|
+
return { ok: false, error: 'press: key required' };
|
|
645
|
+
const def = resolveKey(key);
|
|
646
|
+
if (!def)
|
|
647
|
+
return { ok: false, error: `press: unrecognized key "${key}"` };
|
|
648
|
+
const modifierNames = params.modifiers ?? [];
|
|
649
|
+
const modifiers = modifiersToBitmask(modifierNames) | (def.needsShift ? MODIFIER_BITS.Shift : 0);
|
|
650
|
+
const settle = resolveSettleParams({
|
|
651
|
+
settle_ms: params.settle_ms,
|
|
652
|
+
settle_timeout_ms: params.settle_timeout_ms,
|
|
653
|
+
});
|
|
654
|
+
if (params.selector) {
|
|
655
|
+
const focused = await evaluateInTab(tabId, buildFocusJs({ selector: params.selector }));
|
|
656
|
+
if (!focused) {
|
|
657
|
+
return { ok: false, error: `Element not found: ${params.selector}` };
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
await dispatchKeyEvent(tabId, def, modifiers);
|
|
661
|
+
const settleResult = await runSettle(tabId, settle);
|
|
662
|
+
const result = { key, modifiers: modifierNames };
|
|
663
|
+
if (settleResult)
|
|
664
|
+
result.settle = settleResult;
|
|
665
|
+
return { ok: true, result };
|
|
666
|
+
}
|
|
667
|
+
catch (err) {
|
|
668
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
async function performWaitFor(tabId, state, params) {
|
|
672
|
+
try {
|
|
673
|
+
const waitSelector = params.selector;
|
|
674
|
+
const waitExpression = params.expression;
|
|
675
|
+
const waitNetworkUrl = params.network_url;
|
|
676
|
+
const waitCondition = params.condition ?? 'visible';
|
|
677
|
+
// Defensive caps mirror the dispatcher's contract and keep CodeQL
|
|
678
|
+
// happy about setTimeout durations driven by request params.
|
|
679
|
+
const MAX_WAIT_TIMEOUT_MS = 30_000;
|
|
680
|
+
const MIN_POLL_MS = 50;
|
|
681
|
+
const MAX_POLL_MS = 1_000;
|
|
682
|
+
const requestedTimeout = params.timeout_ms ?? 5_000;
|
|
683
|
+
const timeoutMs = requestedTimeout < MAX_WAIT_TIMEOUT_MS ? requestedTimeout : MAX_WAIT_TIMEOUT_MS;
|
|
684
|
+
const requestedPoll = params.poll_interval_ms ?? 100;
|
|
685
|
+
const pollIntervalMs = requestedPoll < MIN_POLL_MS
|
|
686
|
+
? MIN_POLL_MS
|
|
687
|
+
: requestedPoll > MAX_POLL_MS
|
|
688
|
+
? MAX_POLL_MS
|
|
689
|
+
: requestedPoll;
|
|
690
|
+
// Build the check function based on which mode the caller picked. The
|
|
691
|
+
// dispatcher already enforced "exactly one of selector / expression /
|
|
692
|
+
// network_url" so we just pick the first defined one. If none are
|
|
693
|
+
// defined (shouldn't happen from a well-formed call), the check stays
|
|
694
|
+
// null and we return matched=false immediately.
|
|
695
|
+
let check = null;
|
|
696
|
+
if (waitSelector !== undefined) {
|
|
697
|
+
const expr = buildWaitSelectorExpr(waitSelector, waitCondition);
|
|
698
|
+
check = async () => {
|
|
699
|
+
try {
|
|
700
|
+
const result = await evaluateInTab(tabId, expr);
|
|
701
|
+
return Boolean(result);
|
|
702
|
+
}
|
|
703
|
+
catch {
|
|
704
|
+
// If the page is unreachable mid-poll, count as "not yet matched".
|
|
705
|
+
return false;
|
|
706
|
+
}
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
else if (waitExpression !== undefined) {
|
|
710
|
+
const wrapped = `Boolean(${waitExpression})`;
|
|
711
|
+
check = async () => {
|
|
712
|
+
try {
|
|
713
|
+
const result = await evaluateInTab(tabId, wrapped);
|
|
714
|
+
return Boolean(result);
|
|
715
|
+
}
|
|
716
|
+
catch {
|
|
717
|
+
return false;
|
|
718
|
+
}
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
else if (waitNetworkUrl !== undefined) {
|
|
722
|
+
const needle = waitNetworkUrl.toLowerCase();
|
|
723
|
+
check = () => {
|
|
724
|
+
for (const id of state.networkOrder) {
|
|
725
|
+
const r = state.networkBuffer.get(id);
|
|
726
|
+
if (!r)
|
|
727
|
+
continue;
|
|
728
|
+
if (r.finished_at === undefined)
|
|
729
|
+
continue;
|
|
730
|
+
if (r.url.toLowerCase().includes(needle))
|
|
731
|
+
return Promise.resolve(true);
|
|
732
|
+
}
|
|
733
|
+
return Promise.resolve(false);
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
const startedAt = Date.now();
|
|
737
|
+
let checks = 0;
|
|
738
|
+
let matched = false;
|
|
739
|
+
while (check) {
|
|
740
|
+
checks++;
|
|
741
|
+
if (await check()) {
|
|
742
|
+
matched = true;
|
|
743
|
+
break;
|
|
744
|
+
}
|
|
745
|
+
if (Date.now() - startedAt >= timeoutMs)
|
|
746
|
+
break;
|
|
747
|
+
await sleep(pollIntervalMs);
|
|
748
|
+
}
|
|
749
|
+
const elapsedMs = Date.now() - startedAt;
|
|
750
|
+
const result = matched
|
|
751
|
+
? { matched: true, elapsed_ms: elapsedMs, checks }
|
|
752
|
+
: { matched: false, elapsed_ms: elapsedMs, checks, reason: 'timeout' };
|
|
753
|
+
return { ok: true, result };
|
|
754
|
+
}
|
|
755
|
+
catch (err) {
|
|
756
|
+
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
757
|
+
}
|
|
758
|
+
}
|
|
531
759
|
async function handleTool(state, msg) {
|
|
532
760
|
const tabId = state.tabId;
|
|
533
761
|
// Params come over the wire as JSON: each field is unknown until we
|
|
@@ -573,21 +801,15 @@ async function handleTool(state, msg) {
|
|
|
573
801
|
return { kind: 'tool.response', id: msg.id, ok: true, result: value };
|
|
574
802
|
}
|
|
575
803
|
case 'find': {
|
|
576
|
-
const
|
|
577
|
-
|
|
578
|
-
return {
|
|
579
|
-
kind: 'tool.response',
|
|
580
|
-
id: msg.id,
|
|
581
|
-
ok: false,
|
|
582
|
-
error: 'find: text required',
|
|
583
|
-
};
|
|
584
|
-
}
|
|
585
|
-
const value = await evaluateInTab(tabId, buildFindJs({
|
|
586
|
-
text,
|
|
804
|
+
const outcome = await performFind(tabId, {
|
|
805
|
+
text: str('text'),
|
|
587
806
|
role: optStr('role'),
|
|
588
807
|
exact: p.exact === true,
|
|
589
|
-
})
|
|
590
|
-
|
|
808
|
+
});
|
|
809
|
+
if (!outcome.ok) {
|
|
810
|
+
return { kind: 'tool.response', id: msg.id, ok: false, error: outcome.error };
|
|
811
|
+
}
|
|
812
|
+
return { kind: 'tool.response', id: msg.id, ok: true, result: outcome.result };
|
|
591
813
|
}
|
|
592
814
|
case 'canvas_screenshot': {
|
|
593
815
|
const selector = optStr('selector');
|
|
@@ -629,109 +851,67 @@ async function handleTool(state, msg) {
|
|
|
629
851
|
return { kind: 'tool.response', id: msg.id, ok: true, result: body };
|
|
630
852
|
}
|
|
631
853
|
case 'click': {
|
|
632
|
-
const
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
if (resolved.reason === 'occluded') {
|
|
638
|
-
return {
|
|
639
|
-
kind: 'tool.response',
|
|
640
|
-
id: msg.id,
|
|
641
|
-
ok: false,
|
|
642
|
-
error: `Element covered by ${resolved.blocker} — click the covering element or dismiss it first`,
|
|
643
|
-
};
|
|
644
|
-
}
|
|
645
|
-
return {
|
|
646
|
-
kind: 'tool.response',
|
|
647
|
-
id: msg.id,
|
|
648
|
-
ok: false,
|
|
649
|
-
error: `Element not found: ${selector}`,
|
|
650
|
-
};
|
|
651
|
-
}
|
|
652
|
-
await cdp(tabId, 'Input.dispatchMouseEvent', {
|
|
653
|
-
type: 'mouseMoved',
|
|
654
|
-
x: resolved.x,
|
|
655
|
-
y: resolved.y,
|
|
656
|
-
});
|
|
657
|
-
await cdp(tabId, 'Input.dispatchMouseEvent', {
|
|
658
|
-
type: 'mousePressed',
|
|
659
|
-
x: resolved.x,
|
|
660
|
-
y: resolved.y,
|
|
661
|
-
button: 'left',
|
|
662
|
-
clickCount: 1,
|
|
854
|
+
const outcome = await performClick(tabId, {
|
|
855
|
+
selector: str('selector'),
|
|
856
|
+
force: p.force === true,
|
|
857
|
+
settle_ms: typeof p.settle_ms === 'number' ? p.settle_ms : undefined,
|
|
858
|
+
settle_timeout_ms: typeof p.settle_timeout_ms === 'number' ? p.settle_timeout_ms : undefined,
|
|
663
859
|
});
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
button: 'left',
|
|
669
|
-
clickCount: 1,
|
|
670
|
-
});
|
|
671
|
-
const settleResult = await runSettle(tabId, settle);
|
|
672
|
-
const result = { clicked: selector, tag: resolved.tag };
|
|
673
|
-
if (settleResult)
|
|
674
|
-
result.settle = settleResult;
|
|
675
|
-
return { kind: 'tool.response', id: msg.id, ok: true, result };
|
|
860
|
+
if (!outcome.ok) {
|
|
861
|
+
return { kind: 'tool.response', id: msg.id, ok: false, error: outcome.error };
|
|
862
|
+
}
|
|
863
|
+
return { kind: 'tool.response', id: msg.id, ok: true, result: outcome.result };
|
|
676
864
|
}
|
|
677
865
|
case 'type': {
|
|
678
|
-
const
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
ok: false,
|
|
688
|
-
error: `Element not found: ${selector}`,
|
|
689
|
-
};
|
|
866
|
+
const outcome = await performType(tabId, {
|
|
867
|
+
selector: str('selector'),
|
|
868
|
+
text: str('text'),
|
|
869
|
+
clear: p.clear === true,
|
|
870
|
+
settle_ms: typeof p.settle_ms === 'number' ? p.settle_ms : undefined,
|
|
871
|
+
settle_timeout_ms: typeof p.settle_timeout_ms === 'number' ? p.settle_timeout_ms : undefined,
|
|
872
|
+
});
|
|
873
|
+
if (!outcome.ok) {
|
|
874
|
+
return { kind: 'tool.response', id: msg.id, ok: false, error: outcome.error };
|
|
690
875
|
}
|
|
691
|
-
|
|
692
|
-
const settleResult = await runSettle(tabId, settle);
|
|
693
|
-
const result = { typed: text.length, selector };
|
|
694
|
-
if (settleResult)
|
|
695
|
-
result.settle = settleResult;
|
|
696
|
-
return { kind: 'tool.response', id: msg.id, ok: true, result };
|
|
876
|
+
return { kind: 'tool.response', id: msg.id, ok: true, result: outcome.result };
|
|
697
877
|
}
|
|
698
878
|
case 'press': {
|
|
699
|
-
const key = optStr('key');
|
|
700
|
-
if (!key) {
|
|
701
|
-
return { kind: 'tool.response', id: msg.id, ok: false, error: 'press: key required' };
|
|
702
|
-
}
|
|
703
|
-
const def = resolveKey(key);
|
|
704
|
-
if (!def) {
|
|
705
|
-
return {
|
|
706
|
-
kind: 'tool.response',
|
|
707
|
-
id: msg.id,
|
|
708
|
-
ok: false,
|
|
709
|
-
error: `press: unrecognized key "${key}"`,
|
|
710
|
-
};
|
|
711
|
-
}
|
|
712
879
|
const modifierNames = Array.isArray(p.modifiers)
|
|
713
880
|
? p.modifiers.filter((m) => typeof m === 'string')
|
|
714
881
|
: [];
|
|
715
|
-
const
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
ok: false,
|
|
725
|
-
error: `Element not found: ${selector}`,
|
|
726
|
-
};
|
|
727
|
-
}
|
|
882
|
+
const outcome = await performPress(tabId, {
|
|
883
|
+
key: optStr('key'),
|
|
884
|
+
modifiers: modifierNames,
|
|
885
|
+
selector: optStr('selector'),
|
|
886
|
+
settle_ms: typeof p.settle_ms === 'number' ? p.settle_ms : undefined,
|
|
887
|
+
settle_timeout_ms: typeof p.settle_timeout_ms === 'number' ? p.settle_timeout_ms : undefined,
|
|
888
|
+
});
|
|
889
|
+
if (!outcome.ok) {
|
|
890
|
+
return { kind: 'tool.response', id: msg.id, ok: false, error: outcome.error };
|
|
728
891
|
}
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
892
|
+
return { kind: 'tool.response', id: msg.id, ok: true, result: outcome.result };
|
|
893
|
+
}
|
|
894
|
+
case 'flow': {
|
|
895
|
+
// Wire-boundary narrowing: `p.steps` is untrusted JSON. Keep only
|
|
896
|
+
// object-shaped entries here — `runFlow`'s own `stepKind()` guard
|
|
897
|
+
// re-validates each one at runtime regardless, so a malformed
|
|
898
|
+
// entry fails the flow cleanly instead of throwing.
|
|
899
|
+
const rawSteps = Array.isArray(p.steps)
|
|
900
|
+
? p.steps.filter((s) => typeof s === 'object' && s !== null)
|
|
901
|
+
: [];
|
|
902
|
+
const flowResult = await runFlow(rawSteps, {
|
|
903
|
+
performFind: (params) => performFind(tabId, params),
|
|
904
|
+
performClick: (params) => performClick(tabId, params),
|
|
905
|
+
performType: (params) => performType(tabId, params),
|
|
906
|
+
performPress: (params) => performPress(tabId, params),
|
|
907
|
+
performWaitFor: (params) => performWaitFor(tabId, state, params),
|
|
908
|
+
buildRecoverySnapshot: () => evaluateInTab(tabId, buildSnapshotJs({ only_interactive: true, max_interactive: 40 })),
|
|
909
|
+
});
|
|
910
|
+
// The wire-level response is ok:true whenever the flow RAN (even a
|
|
911
|
+
// failed step is a legitimate business outcome, same pattern as
|
|
912
|
+
// wait_for's matched:false) — flowResult itself carries the
|
|
913
|
+
// ok:true/false the agent reads.
|
|
914
|
+
return { kind: 'tool.response', id: msg.id, ok: true, result: flowResult };
|
|
735
915
|
}
|
|
736
916
|
case 'drag': {
|
|
737
917
|
const optNum = (key) => {
|
|
@@ -1019,95 +1199,18 @@ async function handleTool(state, msg) {
|
|
|
1019
1199
|
return { kind: 'tool.response', id: msg.id, ok: true, result: value };
|
|
1020
1200
|
}
|
|
1021
1201
|
case 'wait_for': {
|
|
1022
|
-
const
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
const MAX_WAIT_TIMEOUT_MS = 30_000;
|
|
1033
|
-
const MIN_POLL_MS = 50;
|
|
1034
|
-
const MAX_POLL_MS = 1_000;
|
|
1035
|
-
const requestedTimeout = optNum('timeout_ms') ?? 5_000;
|
|
1036
|
-
const timeoutMs = requestedTimeout < MAX_WAIT_TIMEOUT_MS ? requestedTimeout : MAX_WAIT_TIMEOUT_MS;
|
|
1037
|
-
const requestedPoll = optNum('poll_interval_ms') ?? 100;
|
|
1038
|
-
const pollIntervalMs = requestedPoll < MIN_POLL_MS
|
|
1039
|
-
? MIN_POLL_MS
|
|
1040
|
-
: requestedPoll > MAX_POLL_MS
|
|
1041
|
-
? MAX_POLL_MS
|
|
1042
|
-
: requestedPoll;
|
|
1043
|
-
// Build the check function based on which mode the caller picked.
|
|
1044
|
-
// The dispatcher already enforced "exactly one of selector / expression
|
|
1045
|
-
// / network_url" so we just pick the first defined one. If none are
|
|
1046
|
-
// defined (shouldn't happen from a well-formed call), the check stays
|
|
1047
|
-
// null and we return matched=false immediately.
|
|
1048
|
-
let check = null;
|
|
1049
|
-
if (waitSelector !== undefined) {
|
|
1050
|
-
const expr = buildWaitSelectorExpr(waitSelector, waitCondition);
|
|
1051
|
-
check = async () => {
|
|
1052
|
-
try {
|
|
1053
|
-
const result = await evaluateInTab(tabId, expr);
|
|
1054
|
-
return Boolean(result);
|
|
1055
|
-
}
|
|
1056
|
-
catch {
|
|
1057
|
-
// If the page is unreachable mid-poll, count as "not yet matched".
|
|
1058
|
-
return false;
|
|
1059
|
-
}
|
|
1060
|
-
};
|
|
1061
|
-
}
|
|
1062
|
-
else if (waitExpression !== undefined) {
|
|
1063
|
-
const wrapped = `Boolean(${waitExpression})`;
|
|
1064
|
-
check = async () => {
|
|
1065
|
-
try {
|
|
1066
|
-
const result = await evaluateInTab(tabId, wrapped);
|
|
1067
|
-
return Boolean(result);
|
|
1068
|
-
}
|
|
1069
|
-
catch {
|
|
1070
|
-
return false;
|
|
1071
|
-
}
|
|
1072
|
-
};
|
|
1073
|
-
}
|
|
1074
|
-
else if (waitNetworkUrl !== undefined) {
|
|
1075
|
-
const needle = waitNetworkUrl.toLowerCase();
|
|
1076
|
-
check = () => {
|
|
1077
|
-
for (const id of state.networkOrder) {
|
|
1078
|
-
const r = state.networkBuffer.get(id);
|
|
1079
|
-
if (!r)
|
|
1080
|
-
continue;
|
|
1081
|
-
if (r.finished_at === undefined)
|
|
1082
|
-
continue;
|
|
1083
|
-
if (r.url.toLowerCase().includes(needle))
|
|
1084
|
-
return Promise.resolve(true);
|
|
1085
|
-
}
|
|
1086
|
-
return Promise.resolve(false);
|
|
1087
|
-
};
|
|
1088
|
-
}
|
|
1089
|
-
const startedAt = Date.now();
|
|
1090
|
-
let checks = 0;
|
|
1091
|
-
let matched = false;
|
|
1092
|
-
while (check) {
|
|
1093
|
-
checks++;
|
|
1094
|
-
if (await check()) {
|
|
1095
|
-
matched = true;
|
|
1096
|
-
break;
|
|
1097
|
-
}
|
|
1098
|
-
if (Date.now() - startedAt >= timeoutMs)
|
|
1099
|
-
break;
|
|
1100
|
-
await sleep(pollIntervalMs);
|
|
1202
|
+
const outcome = await performWaitFor(tabId, state, {
|
|
1203
|
+
selector: optStr('selector'),
|
|
1204
|
+
expression: optStr('expression'),
|
|
1205
|
+
network_url: optStr('network_url'),
|
|
1206
|
+
condition: optStr('condition'),
|
|
1207
|
+
timeout_ms: typeof p.timeout_ms === 'number' ? p.timeout_ms : undefined,
|
|
1208
|
+
poll_interval_ms: typeof p.poll_interval_ms === 'number' ? p.poll_interval_ms : undefined,
|
|
1209
|
+
});
|
|
1210
|
+
if (!outcome.ok) {
|
|
1211
|
+
return { kind: 'tool.response', id: msg.id, ok: false, error: outcome.error };
|
|
1101
1212
|
}
|
|
1102
|
-
|
|
1103
|
-
return {
|
|
1104
|
-
kind: 'tool.response',
|
|
1105
|
-
id: msg.id,
|
|
1106
|
-
ok: true,
|
|
1107
|
-
result: matched
|
|
1108
|
-
? { matched: true, elapsed_ms: elapsedMs, checks }
|
|
1109
|
-
: { matched: false, elapsed_ms: elapsedMs, checks, reason: 'timeout' },
|
|
1110
|
-
};
|
|
1213
|
+
return { kind: 'tool.response', id: msg.id, ok: true, result: outcome.result };
|
|
1111
1214
|
}
|
|
1112
1215
|
case 'dialog_respond': {
|
|
1113
1216
|
const accept = p.accept === true;
|