From 36eb44fa84e31c5b569425a4f93ff6a2d069e300 Mon Sep 17 00:00:00 2001 From: Yifan Chen Date: Sat, 5 Sep 2026 20:09:38 -0700 Subject: [PATCH 1/2] Fix inverted input type check and Document-scoped navigable lookups Two normative defects in algorithms that were introduced or partially fixed by earlier changes. `imperative execute steps` reads "If |inputObject| [=Object type|is not an Object=] is false", which inverts the intended condition: an Object input fails and a JSON scalar passes. The trailing "is false" is a leftover; PR #251 writes the same construct without it. `notify documents of a tool change` and `getTools()` read HTML's [=Document/inclusive descendant navigables=] off a traversable navigable. HTML defines that operation for a Document (data-dfn-for=Document) and its own callers route through the traversable's active document. This completes the fix started in #244, which corrected the same line from "descendant navigables" to "inclusive descendant navigables" but left the receiver wrong. `perform an observation` already does this correctly. --- index.bs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/index.bs b/index.bs index 46c95ea..99fcfce 100644 --- a/index.bs +++ b/index.bs @@ -343,7 +343,7 @@ To notify documents of a tool change given a {{Document}} |tool owner 1. [=Assert=]: these steps are running [=in parallel=]. 1. Let |navigablesToNotify| be |tool owner|'s [=node navigable=]'s [=navigable/traversable - navigable=]'s [=Document/inclusive descendant navigables=]. + navigable=]'s [=navigable/active document=]'s [=Document/inclusive descendant navigables=]. 1. [=list/For each=] |navigable| of |navigablesToNotify|: @@ -478,7 +478,7 @@ internal value=] |uuid|, are as follows: Issue: Support more granular errors; here we should return something that prompts the caller to reject its {{Promise}} with a "{{DataError}}" {{DOMException}}. -1. If |inputObject| [=Object type|is not an Object=] is false, then run |completionSteps| given null +1. If |inputObject| [=Object type|is not an Object=], then run |completionSteps| given null and false, and abort these steps. Issue(#146): Specify and fire the "toolactivated" event. @@ -820,7 +820,7 @@ The getTools(options) method steps 1. Let |tools| be an empty [=list=] of {{RegisteredTool}} dictionaries. 1. Let |navigables| be |toolRequestor|'s [=node navigable=]'s [=navigable/traversable - navigable=]'s [=Document/inclusive descendant navigables=]. + navigable=]'s [=navigable/active document=]'s [=Document/inclusive descendant navigables=]. 1. [=list/For each=] |navigable| of |navigables|: From e6e6376403a98ff8d711a4ad043ec7e9ab368784 Mon Sep 17 00:00:00 2001 From: Yifan Chen Date: Mon, 7 Sep 2026 08:55:04 -0700 Subject: [PATCH 2/2] Run executeTool completion steps in parallel The completion callback removes entries from the traversable's pending tool executions map. Its existing assertion says that it runs in parallel, but its callers commonly run on a Document event loop. Make the parallel execution explicit around the existing completion body so the mutation matches the map invariant. Fixes #305. --- index.bs | 50 +++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/index.bs b/index.bs index 99fcfce..363acf8 100644 --- a/index.bs +++ b/index.bs @@ -1001,31 +1001,31 @@ The executeTool(tool, inputObject -

It is possible that a pending execution identified by |uuid| no longer exists. This can - happen due to a race between (a) tool cancellation when the caller document gets destroyed or when the caller aborts the - execution via the options signal; and (b) tool promise resolution. Both - of these race to invoke |completionSteps|, and the first invocation will remove the - pending execution by its key |uuid|, this check protects subsequent racing - invocations.

- - - 1. [=map/Remove=] |targetDocument|'s [=node navigable=]'s [=navigable/traversable - navigable=]'s [=traversable navigable/pending tool executions map=][|uuid|]. - - 1. If |success| is true, then [=queue a global task=] on the [=webmcp task source=] given - |callerDocument|'s [=relevant global object=] to [=resolve=] |promise| with |result|. - - 1. Otherwise, [=queue a global task=] on the [=webmcp task source=] given |callerDocument|'s - [=relevant global object=] to [=reject=] |promise| with an "{{UnknownError}}" - {{DOMException}}. + 1. Run the following steps [=in parallel=]: + + 1. If |targetDocument|'s [=node navigable=]'s [=navigable/traversable navigable=]'s + [=traversable navigable/pending tool executions map=][|uuid|] does not [=map/exist=], + then return. + +
+

It is possible that a pending execution identified by |uuid| no longer exists. This can + happen due to a race between (a) tool cancellation when the caller document gets destroyed or when the caller aborts the + execution via the options signal; and (b) tool promise resolution. Both + of these race to invoke |completionSteps|, and the first invocation will remove the + pending execution by its key |uuid|, this check protects subsequent racing + invocations.

+
+ + 1. [=map/Remove=] |targetDocument|'s [=node navigable=]'s [=navigable/traversable + navigable=]'s [=traversable navigable/pending tool executions map=][|uuid|]. + + 1. If |success| is true, then [=queue a global task=] on the [=webmcp task source=] given + |callerDocument|'s [=relevant global object=] to [=resolve=] |promise| with |result|. + + 1. Otherwise, [=queue a global task=] on the [=webmcp task source=] given |callerDocument|'s + [=relevant global object=] to [=reject=] |promise| with an "{{UnknownError}}" + {{DOMException}}. 1. Let |execution| be a new [=pending tool execution=], with the following [=struct/items=]: