diff --git a/index.bs b/index.bs index ed842c5..0fa178e 100644 --- a/index.bs +++ b/index.bs @@ -134,8 +134,18 @@ A model context is a [=struct=] with the following [=struct/items=]: : tool map :: a [=map=] whose [=map/keys=] are [=strings=] and whose [=map/values=] are [=tool definition=] [=structs=]. - + : local pending tool executions map + :: a [=map=] whose [=map/keys=] are [=unique internal values=] and whose [=map/values=] are + [=local pending tool execution=] [=structs=]. It is initially empty. + + Note: this map is similar to a [=traversable navigable=]'s [=traversable navigable/pending tool + executions map=], but it only contains pending execution information for tools under a single + {{ModelContext}} object. It is used to store objects that can only be accessed from that + object's event loop, and because it is event-loop-local, it can get out of sync from the + [=traversable navigable=]'s more "global" map. + + A tool definition is a [=struct=] with the following [=struct/items=]: @@ -170,8 +180,8 @@ A tool definition is a [=struct=] with the following [=struct/items=] : execute steps :: an algorithm that takes a {{Document}} targetDocument, a [=string=] inputArguments, and an algorithm completionSteps that takes a - [=string=]-or-null and a [=boolean=]. + ignore>inputArguments, an algorithm completionSteps that takes a + [=string=]-or-null and a [=boolean=], and a [=unique internal value=] uuid. Note: For tools registered imperatively, these steps will simply invoke the [=imperative execute steps=]. For tools registered @@ -186,6 +196,13 @@ A tool definition is a [=struct=] with the following [=struct/items=] :: a [=list=] or [=origins=], initially [=list/empty=]. +A local pending tool execution is a [=struct=] with the following [=struct/items=]: + +
+ : abort controller + :: an {{AbortController}}. +
+ An annotations is a [=struct=] with the following [=struct/items=]:
@@ -223,6 +240,53 @@ authoritative "browser process" that most modern browsers implement, where execu outside any individual Document process's event loop, and is accessed asynchronously via some inter-process communication mechanism. +
+To cancel a pending tool execution given a [=traversable navigable=] |traversable| and a +[=unique internal value=] |uuid|: + +1. [=Assert=]: these steps are running [=in parallel=]. + +1. If |traversable|'s [=traversable navigable/pending tool executions map=][|uuid|] does not + [=map/exist=], then return. + + Note: See this note to learn how a tool's natural + resolution/rejection can race with the caller's cancellation. This might result in the pending + execution entry for |uuid| being removed before we get here. In that case, the + {{ModelContext/executeTool()}} promise will still be rejected with the abort + [=AbortSignal/abort reason=], and will never observe the tool's natural resolution/rejection. + +1. Let |execution| be |traversable|'s [=traversable navigable/pending tool executions map=][|uuid|]. + +1. [=map/Remove=] |traversable|'s [=traversable navigable/pending tool executions map=][|uuid|]. + +1. Let |targetDocument| be |execution|'s [=pending tool execution/target document=]. + + Note: |targetDocument| is guaranteed to still exist (i.e., not be unloaded or destroyed) when + these steps run, because if |targetDocument| had been destroyed, then this specification's unloading document cleanup steps would + have already removed |execution| from the map, and we'd have ended up in the early return path + above. + +1. [=Queue a global task=] on the [=webmcp task source=] given |targetDocument|'s [=relevant global + object=] to run the following steps: + + 1. Let |localExecutions| be |targetDocument|'s [=Document/associated ModelContext|associated + ModelContext=]'s [=ModelContext/internal context=]'s [=model context/local + pending tool executions map=]. + + 1. If |localExecutions|[|uuid|] does not [=map/exist=], then return. + + 1. Let |localExecution| be |localExecutions|[|uuid|]. + + 1. [=map/Remove=] |localExecutions|[|uuid|]. + + 1. [=AbortController/signal abort|Signal abort=] on |localExecution|'s [=local pending tool + execution/abort controller=]. + + Issue(#146): Fire the "toolcanceled" event at |targetDocument|'s relevant global object. + +
+
@@ -247,20 +311,20 @@ follows: 1. Let |execution| be |traversable|'s [=traversable navigable/pending tool executions map=][|uuid|]. - 1.

If |document| is |execution|'s [=pending tool - execution/target document=], then run |execution|'s [=pending tool execution/completion - steps=] given null and false.

+ 1.

If |document| is |execution|'s [=pending tool + execution/target document=] and is not |execution|'s [=pending tool execution/caller + document=], then run |execution|'s [=pending tool execution/completion steps=] given null + and false.

Note: This removes |execution| from the [=traversable navigable/pending tool executions map=]. - 1. If |document| is |execution|'s [=pending tool execution/caller document=], then: - - 1. [=map/Remove=] |traversable|'s [=traversable navigable/pending tool executions - map=][|uuid|]. + 1.

Otherwise, if |document| is |execution|'s [=pending tool + execution/caller document=] and is not |execution|'s [=pending tool execution/target + document=], then [=cancel a pending tool execution=] given |traversable| and |uuid|.

- 1. Issue(#48): Abort the `AbortSignal` in |execution|'s [=pending tool execution/target - document=], so the tool can abort early now that the caller is dead. + 1. Otherwise, [=map/Remove=] |traversable|'s [=traversable navigable/pending tool executions + map=][|uuid|]. 1. [=Assert=]: |traversable|'s [=traversable navigable/pending tool executions map=][|uuid|] does not [=map/exist=]. @@ -336,9 +400,9 @@ a [=list=] of [=origins=] |exposed origins|, and an [=origin=] |accessing origin
The tool execute steps, given a [=string=] |toolName|, a {{Document}} |targetDocument|, a -[=string=] |inputArguments|, and an algorithm |completionSteps|, are as follows. The -|completionSteps| algorithm takes a [=string=]-or-null result and a [=boolean=] -success. +[=string=] |inputArguments|, an algorithm |completionSteps|, and a [=unique internal value=] |uuid|, +are as follows. The |completionSteps| algorithm takes a [=string=]-or-null result +and a [=boolean=] success. 1. [=Assert=]: these steps are running on |targetDocument|'s [=relevant agent=]'s [=agent/event loop=]. @@ -387,8 +451,8 @@ The tool execute steps, given a [=string=] |toolName|, a {{Document}} 1. Let |tool| be |toolMap|[|toolName|]. -1. Run |tool|'s [=tool definition/execute steps=] given |targetDocument|, |inputArguments|, and - |completionSteps|. +1. Run |tool|'s [=tool definition/execute steps=] given |targetDocument|, |inputArguments|, + |completionSteps|, and |uuid|. Note: This is the point where we branch into either the [=imperative execute steps=] or the [=declarative execute steps=]. @@ -397,7 +461,8 @@ The tool execute steps, given a [=string=] |toolName|, a {{Document}}
The imperative execute steps, given a {{ModelContextTool}} |tool|, a {{Document}} -|targetDocument|, a [=string=] |inputArguments|, and an algorithm |completionSteps|, are as follows: +|targetDocument|, a [=string=] |inputArguments|, an algorithm |completionSteps|, and a [=unique +internal value=] |uuid|, are as follows: 1. [=Assert=]: these steps are running on |targetDocument|'s [=relevant agent=]'s [=agent/event loop=]. @@ -415,13 +480,41 @@ The imperative execute steps, given a {{ModelContextTool}} |tool|, a Issue(#146): Specify and fire the "toolactivated" event. +1. Let |controller| be a [=new=] {{AbortController}} created in |targetDocument|'s [=relevant + realm=]. + +1. Let |localExecution| be a new [=local pending tool execution=] with the following [=struct/items=]: + + : [=local pending tool execution/abort controller=] + :: |controller| + +1. Set |targetDocument|'s [=Document/associated ModelContext|associated + ModelContext=]'s [=ModelContext/internal context=]'s [=model context/local pending + tool executions map=][|uuid|] to |localExecution|. +1. Let |options| be a new {{ToolExecuteCallbackOptions}} dictionary, with the following fields: + + : {{ToolExecuteCallbackOptions/signal}} + :: |controller|'s [=AbortController/signal=] + 1. Let |toolPromise| be the result of [=invoke|invoking=] |tool|'s {{ModelContextTool/execute}} with - |inputObject|. + |inputObject| and |options|. 1. [=promise/React=] to |toolPromise|: - If |toolPromise| was fulfilled with value |v|: + 1. Let |localExecutions| be |targetDocument|'s [=Document/associated ModelContext|associated + ModelContext=]'s [=ModelContext/internal context=]'s [=model context/local + pending tool executions map=]. + + 1. If |localExecutions|[|uuid|] does not [=map/exist=], then return. + + Note: The entry corresponding to |uuid| will not exist if the execution was [=cancel a + pending tool execution|cancelled=] (and thus the corresponding entry was removed) before + the developer's |toolPromise| settles. + + 1. [=map/Remove=] |localExecutions|[|uuid|]. + 1. Let |serializedResult| be the result of [=serializing a JavaScript value to a JSON string=] given |v|. If this throws an exception, run |completionSteps| given null and false, and abort these steps. @@ -432,6 +525,14 @@ The imperative execute steps, given a {{ModelContextTool}} |tool|, a 1. Optionally [=report a warning to the console=] describing |r|. + 1. Let |localExecutions| be |targetDocument|'s [=Document/associated ModelContext|associated + ModelContext=]'s [=ModelContext/internal context=]'s [=model context/local + pending tool executions map=]. + + 1. If |localExecutions|[|uuid|] does not [=map/exist=], then return. + + 1. [=map/Remove=] |localExecutions|[|uuid|]. + 1. Run |completionSteps| given null and false.
@@ -664,9 +765,10 @@ The registerTool(tool, optionsexecuteTool(tool, inputObjectDocument=]. + +1. Let |uuid| be a new [=unique internal value=]. + 1. If |options|'s {{ModelContextExecuteToolOptions/signal}} [=map/exists=], then: 1. Let |signal| be |options|'s {{ModelContextExecuteToolOptions/signal}}. @@ -851,19 +960,14 @@ The executeTool(tool, inputObjecttoolcanceled" event in the target document. See also pull request #146. - -1. Let |targetWindow| be |tool|'s {{RegisteredTool/window}}. - -1. Let |targetDocument| be |targetWindow|'s [=associated Document|associated - Document=]. + 1. [=In parallel=], [=cancel a pending tool execution=] given |traversable| and |uuid|. 1. Run the following steps [=in parallel=]: @@ -908,8 +1012,6 @@ The executeTool(tool, inputObjectexecuteTool(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; and (b) tool promise resolution. Both + href=#caller-destroyed-cleanup>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.

@@ -979,7 +1082,8 @@ The executeTool(tool, inputObject (object inputObject); +dictionary ToolExecuteCallbackOptions { + required AbortSignal signal; +}; + +callback ToolExecuteCallback = Promise (object inputObject, ToolExecuteCallbackOptions options);
@@ -1041,7 +1149,8 @@ callback ToolExecuteCallback = Promise (object inputObject);
tool["{{ModelContextTool/execute}}"]
-

A callback function that is invoked when an [=agent=] calls the tool. The function receives the input parameters. +

A callback function that is invoked when an [=agent=] calls the tool. The function receives + the input parameters and execution options.

The function can be asynchronous and return a promise, in which case the [=agent=] will receive the result once the promise is resolved.

@@ -1062,6 +1171,15 @@ The {{ToolAnnotations}} dictionary provides optional metadata about a tool: :: If true, indicates that the tool's output contains data that is untrusted, from the perspective of the author registering the tool.
+

ToolExecuteCallbackOptions Dictionary

+ +The {{ToolExecuteCallbackOptions}} dictionary carries options passed to a tool's +{{ToolExecuteCallback}} when the tool is executed. + +
+ : options["{{ToolExecuteCallbackOptions/signal}}"] + :: An {{AbortSignal}} that communicates when the execution of the tool has been cancelled. +

ModelContextRegisterToolOptions Dictionary