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=]: + +
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.
+
+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 origintoolactivated" 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.
Document=].
+
+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, inputObject
1. If |signal| is [=AbortSignal/aborted=], then return [=a promise rejected with=] |signal|'s
[=AbortSignal/abort reason=].
+ 1. Let |traversable| be |targetDocument|'s [=node navigable=]'s [=navigable/traversable
+ navigable=].
+
1. [=AbortSignal/add|Add the following abort steps=] to |signal|:
1. [=Reject=] |promise| with |signal|'s [=AbortSignal/abort reason=].
- Issue(#48): Wire up |signal| to the tool execution callback in the tool host's document, so
- that tool abort is communicated all the way through. This should also fire the
- "toolcanceled" 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, inputObject
Issue: Support more granular errors than "{{UnknownError}}", based on each failure case.
- 1. Let |uuid| be a new [=unique internal value=].
-
1. Let |completionSteps| be an algorithm that takes a [=string=]-or-null |result| and a
[=boolean=] |success|, and runs the following steps:
@@ -922,7 +1024,8 @@ 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; 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 [=traversable navigable/pending tool executions map=][|uuid|] to |execution|. 1. [=Queue a global task=] on the [=webmcp task source=] given |targetWindow| to run the [=tool - execute steps=] given |toolName|, |targetDocument|, |inputArguments|, and |completionSteps|. + execute steps=] given |toolName|, |targetDocument|, |inputArguments|, |completionSteps|, + and |uuid|. Note: Because documents only process tasks on their event loops when [=Document/fully active=], if |targetDocument| is not [=Document/fully active=], this will simply queue the @@ -1011,7 +1115,11 @@ dictionary ToolAnnotations { boolean untrustedContentHint = false; }; -callback ToolExecuteCallback = Promisetool["{{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.
options["{{ToolExecuteCallbackOptions/signal}}"]
+ :: An {{AbortSignal}} that communicates when the execution of the tool has been cancelled.
+