diff --git a/index.bs b/index.bs index 48c44a1..e884548 100644 --- a/index.bs +++ b/index.bs @@ -94,8 +94,13 @@ spec:html; type:dfn; text:browsing context group set text:unique internal value text:is origin-keyed +spec: webidl; type: dfn; + text: an exception was thrown
+spec: html; urlPrefix: https://html.spec.whatwg.org/C
+ type: dfn
+ text: browsing context group
spec: SECURE-CONTEXTS; urlPrefix: https://w3c.github.io/webappsec-secure-contexts/
type: abstract-op
text: is origin potentially trustworthy?; url: is-origin-trustworthy
@@ -164,10 +169,11 @@ A tool definition is a [=struct=] with the following [=struct/items=]
[[!JSON-SCHEMA]]
: execute steps
- :: a set of steps to invoke the tool.
+ :: an algorithm that takes both a [=string=] and an algorithm completionSteps
+ that itself takes a [=string=]-or-null and a [=boolean=].
- Note: For tools registered imperatively, these steps will simply invoke the supplied
- {{ToolExecuteCallback}} callback. For tools registered
+ Note: For tools registered imperatively, these steps will simply invoke the [=imperative
+ execute steps=]. For tools registered
[declaratively](https://github.com/webmachinelearning/webmcp/pull/76), this will be a set of
"internal" steps that have not been defined yet, that describe how to fill out a <{form}> and
its [=form-associated elements=].
@@ -189,6 +195,76 @@ An annotations is a [=struct=] with the following [=struct/items=]:
:: a [=boolean=], initially false.
+Pending tool executions
+
+A pending tool execution is a [=struct=] with the following [=struct/items=]:
+
+If |document| is |execution|'s [=pending tool + execution/target 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. 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. [=Assert=]: |traversable|'s [=traversable navigable/pending tool executions map=][|uuid|] + does not [=map/exist=]. + +ModelContext=]'s [=ModelContext/internal context=]'s [=model context/tool map=].
+
+1. If |toolMap|[|toolName|] does not [=map/exist=], then run |completionSteps| given null and false,
+ and abort these steps.
+
+ Issue: Support the plumbing of more granular errors back to the invoker; this should result in a
+ "{{NotFoundError}}" in the invoking document.
+
+ This protects us against a race between tool unregistration and execution. While tool + existence is protected from this race, tool unregistration followed by a quick + re-registration of a tool with the same |toolName| but input schema is not protected + against.
+ +This might result in |inputArguments| for an old tool being applied to the [=tool + definition/input schema=] of a newer tool, and causing whatever error that might cause, when issue #92 is resolved.
+ +toolactivated" event.
+
+1. Let |toolPromise| be the result of [=invoke|invoking=] |tool|'s {{ModelContextTool/execute}} with
+ |inputObject|.
+
+1. [=promise/React=] to |toolPromise|:
+
+ - If |toolPromise| was fulfilled with value |v|:
+
+ 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.
+
+ 1. Run |completionSteps| given |serializedResult| and true.
+
+ - If |toolPromise| was rejected with reason |r|, then:
+
+ 1. Optionally [=report a warning to the console=] describing |r|.
+
+ 1. Run |completionSteps| given null and false.
+
+Document=].
+
+1. Let |traversable| be |targetDocument|'s [=node navigable=]'s [=navigable/traversable navigable=].
+
1. Run the following steps [=in parallel=]:
- 1. [=Notify documents of a tool change=] given |modelContext|'s [=relevant global object=]'s
- [=associated Document|associated Document=] and |exposed origins|.
+ 1. Let |canceledExecutions| be an empty [=list=].
+
+ 1. [=map/For each=] |uuid| → |execution| of |traversable|'s [=traversable navigable/pending
+ tool executions map=]:
+
+ 1. If |execution|'s [=pending tool execution/target document=] is |targetDocument| and
+ |execution|'s [=pending tool execution/tool name=] is |tool name|, then [=list/append=]
+ |uuid| to |canceledExecutions|.
+
+ 1. [=list/For each=] |uuid| of |canceledExecutions|:
+
+ 1. Let |execution| be |traversable|'s [=traversable navigable/pending tool executions
+ map=][|uuid|].
+
+ 1. 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. [=Notify documents of a tool change=] given |targetDocument| and |exposed origins|.
document.{{Document/modelContext}}.{{ModelContext/executeTool(tool, inputArguments, options)}}Executes a tool on the document it was registered on. Returns a promise that resolves to the + stringified result of the tool's execution.
+Document=].
+
+1. If |invokingDocument| is not [=Document/fully active=], then return [=a promise rejected with=]
+ an "{{InvalidStateError}}" {{DOMException}}.
+
+1. If [=this=]'s [=surrounding agent=]'s [=agent cluster=]'s [=is origin-keyed=] is false and
+ [=this=]'s [=relevant settings object=]'s [=environment settings object/origin=]'s
+ [=origin/scheme=] is not "file", then return [=a promise rejected with=] a
+ "{{SecurityError}}" {{DOMException}}.
+
+1. If |invokingDocument| is not [=allowed to use=] the "{{tools}}" feature, then return [=a promise
+ rejected with=] a "{{NotAllowedError}}" {{DOMException}}.
+
+1. Let |expectedTargetOriginURL| be the result of [=URL parser|parsing=] |tool|'s
+ {{RegisteredTool/origin}}.
+
+1. If |expectedTargetOriginURL| is failure, or |expectedTargetOriginURL|'s [=url/origin=] is an
+ [=opaque origin=], then return [=a promise rejected with=] a "{{NotSupportedError}}"
+ {{DOMException}}.
+
+1. Let |expectedTargetOrigin| be |expectedTargetOriginURL|'s [=url/origin=].
+
+1. [=Assert=]: |expectedTargetOrigin| is not an [=opaque origin=].
+
+1. Let |promise| be [=a new promise=] created in [=this=]'s [=relevant realm=].
+
+1. If |options|'s {{ModelContextExecuteToolOptions/signal}} [=map/exists=], then:
+
+ 1. Let |signal| be |options|'s {{ModelContextExecuteToolOptions/signal}}.
+
+ 1. If |signal| is [=AbortSignal/aborted=], then return [=a promise rejected with=] |signal|'s
+ [=AbortSignal/abort reason=].
+
+ 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 issue #146.
+
+1. Let |targetWindow| be |tool|'s {{RegisteredTool/window}}.
+
+1. Let |targetDocument| be |targetWindow|'s [=associated Document|associated
+ Document=].
+
+1. Run the following steps [=in parallel=]:
+
+ 1. If |targetDocument|'s [=node navigable=]'s [=navigable/traversable navigable=] is not
+ |invokingDocument|'s [=node navigable=]'s [=navigable/traversable navigable=], then [=queue a
+ global task=] on the [=webmcp task source=] given |invokingDocument|'s [=relevant global
+ object=] to [=reject=] |promise| with an "{{UnknownError}}" {{DOMException}}, and abort these
+ steps.
+
+ Issue(#227): Consider supporting tool execution across top-level documents in the same
+ [=browsing context group=].
+
+ Issue: Support more granular errors than "{{UnknownError}}", based on each failure case.
+
+ 1. Let |targetOrigin| be |targetDocument|'s [=Document/origin=].
+
+ 1. Let |callerOrigin| be |invokingDocument|'s [=Document/origin=].
+
+ 1. If |targetOrigin| is not [=same origin=] with |expectedTargetOrigin|, then [=queue a global
+ task=] on the [=webmcp task source=] given |invokingDocument|'s [=relevant global object=] to
+ [=reject=] |promise| with an "{{UnknownError}}" {{DOMException}}, and abort these steps.
+
+ Issue: Support more granular errors than "{{UnknownError}}", based on each failure case.
+
+ 1. Let |targetToolMap| be |targetDocument|'s [=Document/associated ModelContext|associated
+ ModelContext=]'s [=ModelContext/internal context=]'s [=model context/tool map=].
+
+ 1. Let |toolName| be |tool|'s {{RegisteredTool/name}}.
+
+ 1. If |targetToolMap|[|toolName|] does not [=map/exist=], then [=queue a global task=] on the
+ [=webmcp task source=] given |invokingDocument|'s [=relevant global object=] to [=reject=]
+ |promise| with an "{{UnknownError}}" {{DOMException}}, and abort these steps.
+
+ Issue: Support more granular errors than "{{UnknownError}}", based on each failure case.
+
+ 1. Let |tool definition| be |targetToolMap|[|toolName|].
+
+ 1. If [=tool is exposed to an origin=] given |targetOrigin|, |tool definition|'s [=tool
+ definition/exposed origins=], and |callerOrigin| returns false, then [=queue a global task=]
+ on the [=webmcp task source=] given |invokingDocument|'s [=relevant global object=] to
+ [=reject=] |promise| with an "{{UnknownError}}" {{DOMException}}, and abort these steps.
+
+ 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:
+
+ 1. [=Assert=]: these steps are running [=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; 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.
+ +This can also happen due to a limitation with tool unregistration. Consider this + example:
+ +In this case, tool unregistration happens after a tool Promise is resolved, but before + its fulfillment handler runs. Since both run |completionSteps|, this check protects the + second invocation from trying to remove the same pending execution twice.
+tool["{{ModelContextTool/name}}"]A unique identifier for the tool. This is used by [=agents=] to reference the tool when making tool calls. +
A unique identifier for the tool. This is used by [=agents=] to reference the tool when + making tool calls.
tool["{{ModelContextTool/title}}"]A label for the tool. This is used by the user agent to reference the tool in the user interface. -
It is recommended that this string be localized to the user's -{{NavigatorLanguage/language}}. +
A label for the tool. This is used by the user agent to reference the tool in the user + interface.
It is recommended that this string be localized to the user's + {{NavigatorLanguage/language}}.
tool["{{ModelContextTool/description}}"]A natural language description of the tool's functionality. This helps [=agents=] understand when and how to use the tool. +
A natural language description of the tool's functionality. This helps [=agents=] understand + when and how to use the tool.
tool["{{ModelContextTool/inputSchema}}"]options["{{ModelContextExecuteToolOptions/signal}}"]
+ :: An {{AbortSignal}} that can be used to cancel the execution of the tool.
+