@nexface/tools 0.1.15-alpha.1 → 0.1.15-alpha.2
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/browser/actions.js +34 -10
- package/dist/browser/controller.js +53 -74
- package/dist/browser/diagnostics.js +8 -3
- package/dist/browser/dom.js +12 -28
- package/dist/browser/feedback.js +6 -5
- package/dist/browser/focus.js +0 -1
- package/dist/browser/locator.js +3 -9
- package/dist/browser/prompt.generated.js +0 -1
- package/dist/browser/prompt.js +0 -1
- package/dist/browser/snapshot.js +10 -22
- package/dist/browser/toolset.js +42 -36
- package/dist/browser/types.d.ts +1 -1
- package/dist/browser/types.js +0 -1
- package/dist/browser/worker-runner.js +0 -1
- package/dist/definition.d.ts +1 -3
- package/dist/definition.js +2 -2
- package/dist/index.browser.js +0 -1
- package/dist/index.js +0 -1
- package/dist/internal-react.d.ts +1 -1
- package/dist/internal-react.js +3 -4
- package/dist/internal-types.js +0 -1
- package/dist/navigation.js +0 -1
- package/dist/operation-lock.js +36 -2
- package/dist/operation-runtime.js +41 -13
- package/dist/operation-surface.js +118 -41
- package/dist/operation-types.js +0 -1
- package/dist/operation-view.d.ts +1 -1
- package/dist/operation-view.js +120 -60
- package/dist/react.js +9 -14
- package/dist/registration.js +4 -3
- package/dist/runtime.d.ts +4 -2
- package/dist/runtime.js +112 -47
- package/dist/shared/definition.js +2 -3
- package/dist/shared/errors.d.ts +1 -1
- package/dist/shared/errors.js +0 -1
- package/dist/shared/page-protocol.js +0 -1
- package/dist/shared/page-tools.js +0 -1
- package/dist/shared/runtime-controller.d.ts +1 -0
- package/dist/shared/runtime-controller.js +0 -1
- package/dist/shared/runtime-execution.js +0 -1
- package/dist/shared/schema.js +45 -19
- package/dist/styles.css +879 -109
- package/dist/tool-search.d.ts +11 -0
- package/dist/tool-search.js +117 -0
- package/dist/types.js +0 -1
- package/dist/webmcp.js +39 -13
- package/package.json +6 -2
- package/dist/browser/actions.js.map +0 -1
- package/dist/browser/controller.js.map +0 -1
- package/dist/browser/diagnostics.js.map +0 -1
- package/dist/browser/dom.js.map +0 -1
- package/dist/browser/feedback.js.map +0 -1
- package/dist/browser/focus.js.map +0 -1
- package/dist/browser/locator.js.map +0 -1
- package/dist/browser/prompt.generated.js.map +0 -1
- package/dist/browser/prompt.js.map +0 -1
- package/dist/browser/snapshot.js.map +0 -1
- package/dist/browser/toolset.js.map +0 -1
- package/dist/browser/types.js.map +0 -1
- package/dist/browser/worker-runner.js.map +0 -1
- package/dist/definition.js.map +0 -1
- package/dist/index.browser.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/internal-react.js.map +0 -1
- package/dist/internal-types.js.map +0 -1
- package/dist/navigation.js.map +0 -1
- package/dist/operation-lock.js.map +0 -1
- package/dist/operation-runtime.js.map +0 -1
- package/dist/operation-surface.js.map +0 -1
- package/dist/operation-types.js.map +0 -1
- package/dist/operation-view.js.map +0 -1
- package/dist/react.js.map +0 -1
- package/dist/registration.js.map +0 -1
- package/dist/runtime.js.map +0 -1
- package/dist/shared/definition.js.map +0 -1
- package/dist/shared/errors.js.map +0 -1
- package/dist/shared/page-protocol.js.map +0 -1
- package/dist/shared/page-tools.js.map +0 -1
- package/dist/shared/runtime-controller.js.map +0 -1
- package/dist/shared/runtime-execution.js.map +0 -1
- package/dist/shared/schema.js.map +0 -1
- package/dist/types.js.map +0 -1
- package/dist/webmcp.js.map +0 -1
package/dist/runtime.d.ts
CHANGED
|
@@ -8,13 +8,14 @@ import type { AgentContextEntry, AgentContextInput, AgentContextRegistration, Ag
|
|
|
8
8
|
import type { AgentToolExecutionEvent } from "./internal-types.js";
|
|
9
9
|
export interface ToolRuntimeOptions {
|
|
10
10
|
readonly pageAutomation?: boolean;
|
|
11
|
+
readonly routeSearch?: boolean;
|
|
11
12
|
}
|
|
12
13
|
export interface ToolRuntimeToolChangeEvent {
|
|
13
14
|
readonly toolName: string;
|
|
14
|
-
readonly change: "registered" | "unregistered";
|
|
15
|
+
readonly change: "registered" | "unregistered" | "visibility";
|
|
15
16
|
}
|
|
16
17
|
declare const CONTEXT_ERROR_CODES: readonly ["CONTEXT_DISPOSED", "CONTEXT_NAME_MISMATCH", "DUPLICATE_CONTEXT", "INVALID_CONTEXT"];
|
|
17
|
-
type AgentContextErrorCode = typeof CONTEXT_ERROR_CODES[number];
|
|
18
|
+
type AgentContextErrorCode = (typeof CONTEXT_ERROR_CODES)[number];
|
|
18
19
|
export declare function isToolRuntimeError(error: unknown): error is {
|
|
19
20
|
readonly type: "tool_runtime_error";
|
|
20
21
|
readonly domain: "tool" | "context";
|
|
@@ -183,6 +184,7 @@ export declare class ToolRuntimeImplementation extends EventTarget implements To
|
|
|
183
184
|
getContexts(options?: {
|
|
184
185
|
signal?: AbortSignal;
|
|
185
186
|
}): Promise<readonly AgentContextEntry[]>;
|
|
187
|
+
getRegisteredTools(): readonly RuntimeAgentTool[];
|
|
186
188
|
getWebMcpTools(): readonly RuntimeAgentTool[];
|
|
187
189
|
isRegistered(name: string): boolean;
|
|
188
190
|
getTools(): readonly RuntimeAgentTool[];
|
package/dist/runtime.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
+
import { searchNavigationTools, searchToolDefinition } from "./tool-search.js";
|
|
1
2
|
import { pageDecisionSymbol } from "./shared/runtime-execution.js";
|
|
2
3
|
import { PageCommandError } from "./browser/locator.js";
|
|
3
4
|
import { measurePagePhase } from "./browser/diagnostics.js";
|
|
4
|
-
import { runtimeControllerSymbol } from "./shared/runtime-controller.js";
|
|
5
|
+
import { runtimeControllerSymbol, } from "./shared/runtime-controller.js";
|
|
5
6
|
import { AgentToolError, TOOL_ERROR_CODES } from "./shared/errors.js";
|
|
6
7
|
export { AgentToolError } from "./shared/errors.js";
|
|
7
8
|
import { assertAgentToolRegistration } from "./registration.js";
|
|
8
9
|
import { OperationTracker } from "./operation-runtime.js";
|
|
9
10
|
import { defineAgentTool, isValidAgentContextName } from "./definition.js";
|
|
10
11
|
import { assertAgentToolInputSchema, validateAgentToolInput } from "./shared/schema.js";
|
|
11
|
-
import { createBrowserToolset
|
|
12
|
+
import { createBrowserToolset } from "./browser/toolset.js";
|
|
12
13
|
const CONTEXT_ERROR_CODES = [
|
|
13
14
|
"CONTEXT_DISPOSED",
|
|
14
15
|
"CONTEXT_NAME_MISMATCH",
|
|
@@ -21,9 +22,9 @@ export function isToolRuntimeError(error) {
|
|
|
21
22
|
if (!error || typeof error !== "object")
|
|
22
23
|
return false;
|
|
23
24
|
const candidate = error;
|
|
24
|
-
if (candidate.type !== "tool_runtime_error"
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
if (candidate.type !== "tool_runtime_error" ||
|
|
26
|
+
typeof candidate.message !== "string" ||
|
|
27
|
+
typeof candidate.code !== "string") {
|
|
27
28
|
return false;
|
|
28
29
|
}
|
|
29
30
|
if (candidate.domain === "tool") {
|
|
@@ -44,9 +45,7 @@ function assertContextName(name) {
|
|
|
44
45
|
}
|
|
45
46
|
}
|
|
46
47
|
function normalizeContextValue(value, path = "value", seen = new WeakSet()) {
|
|
47
|
-
if (value === null
|
|
48
|
-
|| typeof value === "string"
|
|
49
|
-
|| typeof value === "boolean") {
|
|
48
|
+
if (value === null || typeof value === "string" || typeof value === "boolean") {
|
|
50
49
|
return value;
|
|
51
50
|
}
|
|
52
51
|
if (typeof value === "number") {
|
|
@@ -83,8 +82,8 @@ function normalizeContextValue(value, path = "value", seen = new WeakSet()) {
|
|
|
83
82
|
}
|
|
84
83
|
function normalizeContext(context) {
|
|
85
84
|
assertContextName(context.name);
|
|
86
|
-
if (context.description !== undefined
|
|
87
|
-
|
|
85
|
+
if (context.description !== undefined &&
|
|
86
|
+
(typeof context.description !== "string" || !context.description.trim())) {
|
|
88
87
|
throw new AgentContextError("INVALID_CONTEXT", "Context description must be a non-empty string when provided.");
|
|
89
88
|
}
|
|
90
89
|
return {
|
|
@@ -124,6 +123,8 @@ export function getInternalToolRuntime(runtime) {
|
|
|
124
123
|
export class ToolRuntimeImplementation extends EventTarget {
|
|
125
124
|
[toolRuntimeBrand] = true;
|
|
126
125
|
#bindings = new Map();
|
|
126
|
+
#routeSearch;
|
|
127
|
+
#visibleNavigation = new Set();
|
|
127
128
|
#contexts = new Map();
|
|
128
129
|
#contextRevision = 0;
|
|
129
130
|
#browserToolset;
|
|
@@ -137,16 +138,22 @@ export class ToolRuntimeImplementation extends EventTarget {
|
|
|
137
138
|
beginInternalOperation(options = {}) {
|
|
138
139
|
return this.#operations.begin(options);
|
|
139
140
|
}
|
|
140
|
-
endBridgeOperation(handle) {
|
|
141
|
+
endBridgeOperation(handle) {
|
|
142
|
+
return this.#operations.disposeBridge(handle);
|
|
143
|
+
}
|
|
141
144
|
setTaskProgress = (handle, progress) => this.#operations.setTaskProgress(handle, progress);
|
|
142
145
|
setOperationWaiting = (handle, waiting) => this.#operations.setWaitingForUser(handle, waiting);
|
|
143
146
|
getActivitySnapshot = () => this.#operations.getSnapshot();
|
|
144
147
|
subscribeActivity = (listener) => this.#operations.subscribe(listener);
|
|
145
|
-
requestOperationCancel(id) {
|
|
148
|
+
requestOperationCancel(id) {
|
|
149
|
+
this.#operations.requestCancel(id);
|
|
150
|
+
}
|
|
146
151
|
setPageActionFeedback(listener) {
|
|
147
152
|
this.#pageActionFeedback = listener;
|
|
148
|
-
return () => {
|
|
149
|
-
this.#pageActionFeedback
|
|
153
|
+
return () => {
|
|
154
|
+
if (this.#pageActionFeedback === listener)
|
|
155
|
+
this.#pageActionFeedback = undefined;
|
|
156
|
+
};
|
|
150
157
|
}
|
|
151
158
|
#emitExecution(event) {
|
|
152
159
|
this.#operations.observe(event);
|
|
@@ -154,10 +161,40 @@ export class ToolRuntimeImplementation extends EventTarget {
|
|
|
154
161
|
}
|
|
155
162
|
constructor(options = {}) {
|
|
156
163
|
super();
|
|
157
|
-
if (options.pageAutomation !== undefined &&
|
|
158
|
-
typeof options.pageAutomation !== "boolean") {
|
|
164
|
+
if (options.pageAutomation !== undefined && typeof options.pageAutomation !== "boolean") {
|
|
159
165
|
throw new AgentToolError("INVALID_INPUT", "pageAutomation must be a boolean.");
|
|
160
166
|
}
|
|
167
|
+
if (options.routeSearch !== undefined && typeof options.routeSearch !== "boolean") {
|
|
168
|
+
throw new AgentToolError("INVALID_INPUT", "routeSearch must be a boolean.");
|
|
169
|
+
}
|
|
170
|
+
this.#routeSearch = options.routeSearch ?? false;
|
|
171
|
+
if (this.#routeSearch)
|
|
172
|
+
this.bindTool(searchToolDefinition, ({ query, queries }) => {
|
|
173
|
+
if (query !== undefined && queries !== undefined)
|
|
174
|
+
throw new AgentToolError("INVALID_INPUT", "Use either query or queries, not both.");
|
|
175
|
+
const searchQueries = queries === undefined ? (query === undefined ? undefined : [query]) : [...queries];
|
|
176
|
+
if (searchQueries?.some((phrase) => typeof phrase !== "string" || !phrase.trim()))
|
|
177
|
+
throw new AgentToolError("INVALID_INPUT", "Search queries must contain nonblank strings.");
|
|
178
|
+
const navigation = this.getRegisteredTools().filter(({ definition }) => definition.effect === "navigate");
|
|
179
|
+
const candidates = searchQueries === undefined
|
|
180
|
+
? navigation
|
|
181
|
+
: searchNavigationTools(navigation, searchQueries.map((phrase) => phrase.trim()));
|
|
182
|
+
if (searchQueries !== undefined) {
|
|
183
|
+
const previous = this.#visibleNavigation;
|
|
184
|
+
this.#visibleNavigation = new Set(candidates);
|
|
185
|
+
for (const tool of new Set([...previous, ...candidates])) {
|
|
186
|
+
if (previous.has(tool) === this.#visibleNavigation.has(tool))
|
|
187
|
+
continue;
|
|
188
|
+
this.dispatchEvent(new DetailEvent("toolchange", Object.freeze({
|
|
189
|
+
toolName: tool.definition.name,
|
|
190
|
+
change: "visibility",
|
|
191
|
+
})));
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return {
|
|
195
|
+
tools: candidates.map(({ definition: { name, description } }) => ({ name, description })),
|
|
196
|
+
};
|
|
197
|
+
});
|
|
161
198
|
this.#browserToolset = options.pageAutomation ? createBrowserToolset() : undefined;
|
|
162
199
|
runtimeInternals.set(this, this);
|
|
163
200
|
const controller = Object.freeze({
|
|
@@ -166,6 +203,7 @@ export class ToolRuntimeImplementation extends EventTarget {
|
|
|
166
203
|
setTaskProgress: this.setTaskProgress,
|
|
167
204
|
getOperationSnapshot: () => this.getActivitySnapshot().operation,
|
|
168
205
|
subscribeOperation: this.subscribeActivity,
|
|
206
|
+
getRegisteredTools: () => this.getRegisteredTools(),
|
|
169
207
|
});
|
|
170
208
|
Object.defineProperty(this, runtimeControllerSymbol, { value: controller });
|
|
171
209
|
}
|
|
@@ -191,13 +229,19 @@ export class ToolRuntimeImplementation extends EventTarget {
|
|
|
191
229
|
assertAgentToolRegistration(definition, options);
|
|
192
230
|
const execute = options.execute;
|
|
193
231
|
if (definition.inputSchema?.type !== "object") {
|
|
194
|
-
throw new TypeError(`Tool "${definition.name}"
|
|
232
|
+
throw new TypeError(`Tool "${definition.name}" was not registered: inputSchema root type must be object.`);
|
|
233
|
+
}
|
|
234
|
+
let copied;
|
|
235
|
+
try {
|
|
236
|
+
copied = defineAgentTool(definition);
|
|
237
|
+
}
|
|
238
|
+
catch (cause) {
|
|
239
|
+
throw new TypeError(`Tool "${definition.name}" was not registered: ${cause instanceof Error ? cause.message : String(cause)}`, { cause });
|
|
195
240
|
}
|
|
196
|
-
const copied = defineAgentTool(definition);
|
|
197
241
|
return this.#bindTool(copied, (input, { signal }) => execute(input, { signal }));
|
|
198
242
|
}
|
|
199
243
|
bindTool(definition, execute, options = {}) {
|
|
200
|
-
assertAgentToolInputSchema(definition.inputSchema, `Tool "${definition.name}"
|
|
244
|
+
assertAgentToolInputSchema(definition.inputSchema, `Tool "${definition.name}" was not registered: inputSchema`);
|
|
201
245
|
return this.#bindTool(definition, execute, options);
|
|
202
246
|
}
|
|
203
247
|
#bindTool(definition, execute, options = {}) {
|
|
@@ -209,9 +253,7 @@ export class ToolRuntimeImplementation extends EventTarget {
|
|
|
209
253
|
definition,
|
|
210
254
|
get confirmation() {
|
|
211
255
|
const confirmation = options.getConfirmation?.();
|
|
212
|
-
return confirmation === undefined
|
|
213
|
-
? undefined
|
|
214
|
-
: Object.freeze({ ...confirmation });
|
|
256
|
+
return confirmation === undefined ? undefined : Object.freeze({ ...confirmation });
|
|
215
257
|
},
|
|
216
258
|
lifecycleSignal: lifecycleController.signal,
|
|
217
259
|
});
|
|
@@ -220,9 +262,7 @@ export class ToolRuntimeImplementation extends EventTarget {
|
|
|
220
262
|
definition,
|
|
221
263
|
runtimeTool,
|
|
222
264
|
execute,
|
|
223
|
-
...(options.getConfirmation
|
|
224
|
-
? { getConfirmation: options.getConfirmation }
|
|
225
|
-
: {}),
|
|
265
|
+
...(options.getConfirmation ? { getConfirmation: options.getConfirmation } : {}),
|
|
226
266
|
lifecycleController,
|
|
227
267
|
};
|
|
228
268
|
this.#bindings.set(definition.name, binding);
|
|
@@ -237,6 +277,7 @@ export class ToolRuntimeImplementation extends EventTarget {
|
|
|
237
277
|
disposed = true;
|
|
238
278
|
options.signal?.removeEventListener("abort", dispose);
|
|
239
279
|
this.#bindings.delete(definition.name);
|
|
280
|
+
this.#visibleNavigation.delete(runtimeTool);
|
|
240
281
|
lifecycleController.abort();
|
|
241
282
|
this.dispatchEvent(new DetailEvent("toolchange", Object.freeze({
|
|
242
283
|
toolName: definition.name,
|
|
@@ -350,21 +391,27 @@ export class ToolRuntimeImplementation extends EventTarget {
|
|
|
350
391
|
revision: this.#contextRevision,
|
|
351
392
|
}));
|
|
352
393
|
}
|
|
394
|
+
getRegisteredTools() {
|
|
395
|
+
return [...this.#bindings.values()]
|
|
396
|
+
.map(({ runtimeTool }) => runtimeTool)
|
|
397
|
+
.filter(({ definition }) => !this.#routeSearch || definition !== searchToolDefinition)
|
|
398
|
+
.sort((left, right) => compareStrings(left.definition.name, right.definition.name));
|
|
399
|
+
}
|
|
353
400
|
getWebMcpTools() {
|
|
354
401
|
return [...this.#bindings.values()]
|
|
355
402
|
.map(({ runtimeTool }) => runtimeTool)
|
|
403
|
+
.filter((tool) => !this.#routeSearch ||
|
|
404
|
+
tool.definition.effect !== "navigate" ||
|
|
405
|
+
this.#visibleNavigation.has(tool))
|
|
356
406
|
.sort((left, right) => compareStrings(left.definition.name, right.definition.name));
|
|
357
407
|
}
|
|
358
408
|
isRegistered(name) {
|
|
359
409
|
return this.#bindings.has(name);
|
|
360
410
|
}
|
|
361
411
|
getTools() {
|
|
362
|
-
const applicationTools =
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
.map((definition) => this.#getBrowserRuntimeTool(definition));
|
|
366
|
-
return [...applicationTools, ...browserTools]
|
|
367
|
-
.sort((left, right) => compareStrings(left.definition.name, right.definition.name));
|
|
412
|
+
const applicationTools = this.getWebMcpTools();
|
|
413
|
+
const browserTools = (this.#browserToolset?.definitions ?? []).map((definition) => this.#getBrowserRuntimeTool(definition));
|
|
414
|
+
return [...applicationTools, ...browserTools].sort((left, right) => compareStrings(left.definition.name, right.definition.name));
|
|
368
415
|
}
|
|
369
416
|
waitForTool(definition, options = {}) {
|
|
370
417
|
const isTargetRegistered = () => this.#bindings.has(definition.name);
|
|
@@ -387,8 +434,7 @@ export class ToolRuntimeImplementation extends EventTarget {
|
|
|
387
434
|
};
|
|
388
435
|
const onToolChange = (event) => {
|
|
389
436
|
const detail = event.detail;
|
|
390
|
-
if (detail?.toolName !== definition.name
|
|
391
|
-
|| detail.change !== "registered")
|
|
437
|
+
if (detail?.toolName !== definition.name || detail.change !== "registered")
|
|
392
438
|
return;
|
|
393
439
|
if (isTargetRegistered())
|
|
394
440
|
settle(resolve);
|
|
@@ -414,7 +460,11 @@ export class ToolRuntimeImplementation extends EventTarget {
|
|
|
414
460
|
}
|
|
415
461
|
execute(tool, input, options = {}) {
|
|
416
462
|
const label = options.progressLabel?.trim().replace(/\s+/g, " ").slice(0, 48);
|
|
417
|
-
options = {
|
|
463
|
+
options = {
|
|
464
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
465
|
+
...(label ? { progressLabel: label } : {}),
|
|
466
|
+
...(options[pageDecisionSymbol] ? { [pageDecisionSymbol]: true } : {}),
|
|
467
|
+
};
|
|
418
468
|
const definition = tool.definition;
|
|
419
469
|
const name = definition.name;
|
|
420
470
|
const binding = this.#bindings.get(name);
|
|
@@ -430,8 +480,8 @@ export class ToolRuntimeImplementation extends EventTarget {
|
|
|
430
480
|
if (!browserHandler) {
|
|
431
481
|
return Promise.reject(new AgentToolError("UNKNOWN_TOOL", `Unknown tool: ${name}.`));
|
|
432
482
|
}
|
|
433
|
-
if (!browserToolset?.definitions.some((candidate) => candidate === definition)
|
|
434
|
-
|
|
483
|
+
if (!browserToolset?.definitions.some((candidate) => candidate === definition) ||
|
|
484
|
+
this.#getBrowserRuntimeTool(definition) !== tool) {
|
|
435
485
|
return Promise.reject(new AgentToolError("CONTRACT_MISMATCH", `Tool "${name}" is registered with a different Definition.`));
|
|
436
486
|
}
|
|
437
487
|
return this.#enqueue((operationId) => this.#observeExecution(tool, input, options.signal, async (onFeedback, assertActive) => {
|
|
@@ -473,7 +523,9 @@ export class ToolRuntimeImplementation extends EventTarget {
|
|
|
473
523
|
};
|
|
474
524
|
// Reserve the tail before invoking executors or notifying synchronous listeners.
|
|
475
525
|
let settleTail;
|
|
476
|
-
const tail = new Promise((resolve) => {
|
|
526
|
+
const tail = new Promise((resolve) => {
|
|
527
|
+
settleTail = resolve;
|
|
528
|
+
});
|
|
477
529
|
this.#executionTail = tail;
|
|
478
530
|
const queuedExecution = previous ? previous.then(start) : start();
|
|
479
531
|
let execution = queuedExecution;
|
|
@@ -505,9 +557,13 @@ export class ToolRuntimeImplementation extends EventTarget {
|
|
|
505
557
|
const target = binding.resolveTarget?.(input);
|
|
506
558
|
return target?.isConnected ? target : undefined;
|
|
507
559
|
}
|
|
508
|
-
if (this.#browserRuntimeTools.get(tool.definition) !== tool ||
|
|
560
|
+
if (this.#browserRuntimeTools.get(tool.definition) !== tool ||
|
|
561
|
+
!input ||
|
|
562
|
+
typeof input !== "object")
|
|
509
563
|
return undefined;
|
|
510
|
-
return this.#browserToolset?.handlers
|
|
564
|
+
return this.#browserToolset?.handlers
|
|
565
|
+
.get(tool.definition.name)
|
|
566
|
+
?.resolveTarget?.(input);
|
|
511
567
|
}
|
|
512
568
|
async #observeExecution(tool, input, signal, execute, progressLabel, operationId) {
|
|
513
569
|
const running = {
|
|
@@ -524,7 +580,10 @@ export class ToolRuntimeImplementation extends EventTarget {
|
|
|
524
580
|
const assertActive = () => {
|
|
525
581
|
signal?.throwIfAborted();
|
|
526
582
|
if (presentation && this.#pageActionFeedback !== presentation) {
|
|
527
|
-
throw new PageCommandError({
|
|
583
|
+
throw new PageCommandError({
|
|
584
|
+
code: "EXECUTION_CANCELLED",
|
|
585
|
+
message: "Page action presentation was detached.",
|
|
586
|
+
});
|
|
528
587
|
}
|
|
529
588
|
};
|
|
530
589
|
this.#emitExecution(running);
|
|
@@ -533,26 +592,33 @@ export class ToolRuntimeImplementation extends EventTarget {
|
|
|
533
592
|
const result = await execute(async (feedback) => {
|
|
534
593
|
assertActive();
|
|
535
594
|
if (presentation)
|
|
536
|
-
await measurePagePhase("presentation", async () => {
|
|
595
|
+
await measurePagePhase("presentation", async () => {
|
|
596
|
+
await presentation(running, feedback, signal);
|
|
597
|
+
});
|
|
537
598
|
assertActive();
|
|
538
599
|
}, assertActive);
|
|
539
600
|
signal?.throwIfAborted();
|
|
540
601
|
this.#emitExecution({
|
|
541
|
-
...running,
|
|
602
|
+
...running,
|
|
603
|
+
status: "success",
|
|
604
|
+
finishedAt: Date.now(),
|
|
605
|
+
result,
|
|
542
606
|
});
|
|
543
607
|
return result;
|
|
544
608
|
}
|
|
545
609
|
catch (error) {
|
|
546
610
|
this.#emitExecution({
|
|
547
|
-
...running,
|
|
548
|
-
|
|
611
|
+
...running,
|
|
612
|
+
status: "error",
|
|
613
|
+
cancelled: signal?.aborted === true,
|
|
614
|
+
finishedAt: Date.now(),
|
|
615
|
+
error,
|
|
549
616
|
});
|
|
550
617
|
throw error;
|
|
551
618
|
}
|
|
552
619
|
}
|
|
553
620
|
async #executeApplication(name, input, options, binding, operationId) {
|
|
554
|
-
const id = globalThis.crypto?.randomUUID?.()
|
|
555
|
-
?? `${Date.now()}-${Math.random()}`;
|
|
621
|
+
const id = globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random()}`;
|
|
556
622
|
const startedAt = Date.now();
|
|
557
623
|
const running = {
|
|
558
624
|
...(operationId ? { operationId } : {}),
|
|
@@ -612,4 +678,3 @@ export class ToolRuntimeImplementation extends EventTarget {
|
|
|
612
678
|
return runtimeTool;
|
|
613
679
|
}
|
|
614
680
|
}
|
|
615
|
-
//# sourceMappingURL=runtime.js.map
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
import { assertAgentToolInputSchema } from "./schema.js";
|
|
3
3
|
export function createAgentToolDefinition(metadata) {
|
|
4
4
|
if (!metadata.description.trim())
|
|
5
|
-
throw new Error("Tool description is required
|
|
5
|
+
throw new Error(`Tool "${metadata.name}": Tool description is required.`);
|
|
6
6
|
if (metadata.inputSchema?.type !== "object") {
|
|
7
7
|
throw new TypeError(`Tool inputSchema root type must be object. Tool "${metadata.name}".inputSchema.type is invalid.`);
|
|
8
8
|
}
|
|
9
9
|
assertAgentToolInputSchema(metadata.inputSchema, `Tool "${metadata.name}".inputSchema`);
|
|
10
10
|
const effect = metadata.effect ?? "action";
|
|
11
11
|
if (!["read", "action", "navigate"].includes(effect)) {
|
|
12
|
-
throw new Error(`Invalid agent tool effect: ${String(effect)}.`);
|
|
12
|
+
throw new Error(`Tool "${metadata.name}": Invalid agent tool effect: ${String(effect)}.`);
|
|
13
13
|
}
|
|
14
14
|
return Object.freeze({
|
|
15
15
|
name: metadata.name,
|
|
@@ -30,4 +30,3 @@ function cloneAndFreeze(value) {
|
|
|
30
30
|
}
|
|
31
31
|
return value;
|
|
32
32
|
}
|
|
33
|
-
//# sourceMappingURL=definition.js.map
|
package/dist/shared/errors.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export declare const TOOL_ERROR_CODES: readonly ["EXECUTION_OUTCOME_UNKNOWN", "ACTION_IN_FLIGHT", "ACTION_NOT_HANDLED", "ACTION_TARGET_UNAVAILABLE", "BROWSER_EXECUTION_FAILED", "BROWSER_SNAPSHOT_REQUIRED", "BROWSER_TOOLS_UNAVAILABLE", "CONTRACT_MISMATCH", "DUPLICATE_BINDING", "INVALID_INPUT", "INVALID_TOOL_RUNTIME", "OPERATION_BUSY", "TOOL_CANCELLED", "TOOL_CONFIRMATION_INVALIDATED", "TOOL_CONFIRMATION_REJECTED", "TOOL_ERROR", "TOOL_REGISTRATION_TIMEOUT", "TOOL_SKIPPED", "TOOL_UNAVAILABLE", "UNKNOWN_TOOL"];
|
|
2
|
-
type AgentToolErrorCode = typeof TOOL_ERROR_CODES[number];
|
|
2
|
+
type AgentToolErrorCode = (typeof TOOL_ERROR_CODES)[number];
|
|
3
3
|
export declare class AgentToolError extends Error {
|
|
4
4
|
readonly code: AgentToolErrorCode;
|
|
5
5
|
readonly details?: unknown | undefined;
|
package/dist/shared/errors.js
CHANGED
|
@@ -12,6 +12,7 @@ interface OperationSnapshot {
|
|
|
12
12
|
readonly taskProgress?: TaskProgress;
|
|
13
13
|
}
|
|
14
14
|
export interface RuntimeController {
|
|
15
|
+
getRegisteredTools(): ReturnType<ToolRuntime["getTools"]>;
|
|
15
16
|
beginInternalOperation(options?: {
|
|
16
17
|
signal?: AbortSignal;
|
|
17
18
|
onCancel?: () => void;
|
package/dist/shared/schema.js
CHANGED
|
@@ -3,8 +3,17 @@ const types = new Set(["object", "array", "string", "number", "integer", "boolea
|
|
|
3
3
|
const counts = ["minLength", "maxLength", "minItems", "maxItems"];
|
|
4
4
|
const bounds = ["minimum", "maximum", "exclusiveMinimum", "exclusiveMaximum"];
|
|
5
5
|
const keywords = new Set([
|
|
6
|
-
"type",
|
|
7
|
-
|
|
6
|
+
"type",
|
|
7
|
+
"description",
|
|
8
|
+
"properties",
|
|
9
|
+
"required",
|
|
10
|
+
"items",
|
|
11
|
+
"enum",
|
|
12
|
+
"additionalProperties",
|
|
13
|
+
...counts,
|
|
14
|
+
...bounds,
|
|
15
|
+
"pattern",
|
|
16
|
+
"uniqueItems",
|
|
8
17
|
]);
|
|
9
18
|
const checkedSchemas = new WeakSet();
|
|
10
19
|
const patterns = new WeakMap();
|
|
@@ -24,7 +33,9 @@ function assertJson(value, path, ancestors = new Set()) {
|
|
|
24
33
|
invalid(path, "must be JSON-compatible.");
|
|
25
34
|
if (ancestors.has(value))
|
|
26
35
|
invalid(path, "must not contain circular references.");
|
|
27
|
-
if (!Array.isArray(value) &&
|
|
36
|
+
if (!Array.isArray(value) &&
|
|
37
|
+
Object.getPrototypeOf(value) !== Object.prototype &&
|
|
38
|
+
Object.getPrototypeOf(value) !== null) {
|
|
28
39
|
invalid(path, "must be a plain JSON object.");
|
|
29
40
|
}
|
|
30
41
|
if (Object.getOwnPropertySymbols(value).length)
|
|
@@ -85,7 +96,8 @@ function assertSchema(value, path) {
|
|
|
85
96
|
invalid(`${path}.${key}`, "must be a non-negative integer.");
|
|
86
97
|
}
|
|
87
98
|
for (const key of bounds) {
|
|
88
|
-
if (value[key] !== undefined &&
|
|
99
|
+
if (value[key] !== undefined &&
|
|
100
|
+
(typeof value[key] !== "number" || !Number.isFinite(value[key])))
|
|
89
101
|
invalid(`${path}.${key}`, "must be a finite number.");
|
|
90
102
|
}
|
|
91
103
|
if (value.pattern !== undefined) {
|
|
@@ -95,7 +107,8 @@ function assertSchema(value, path) {
|
|
|
95
107
|
}
|
|
96
108
|
if (value.enum !== undefined && (!Array.isArray(value.enum) || value.enum.length === 0))
|
|
97
109
|
invalid(`${path}.enum`, "must be a non-empty array.");
|
|
98
|
-
if (Array.isArray(value.enum) &&
|
|
110
|
+
if (Array.isArray(value.enum) &&
|
|
111
|
+
value.enum.some((item, index, entries) => entries.slice(0, index).some((other) => jsonEqual(item, other))))
|
|
99
112
|
invalid(`${path}.enum`, "must contain unique values.");
|
|
100
113
|
if (value.properties !== undefined) {
|
|
101
114
|
if (value.type !== "object" || !isRecord(value.properties))
|
|
@@ -104,7 +117,10 @@ function assertSchema(value, path) {
|
|
|
104
117
|
assertSchema(property, `${path}.properties.${name}`);
|
|
105
118
|
}
|
|
106
119
|
if (value.required !== undefined) {
|
|
107
|
-
if (value.type !== "object" ||
|
|
120
|
+
if (value.type !== "object" ||
|
|
121
|
+
!Array.isArray(value.required) ||
|
|
122
|
+
value.required.some((name) => typeof name !== "string") ||
|
|
123
|
+
new Set(value.required).size !== value.required.length)
|
|
108
124
|
invalid(`${path}.required`, "must contain unique property names.");
|
|
109
125
|
const properties = value.properties;
|
|
110
126
|
if (value.required.some((name) => !properties || !Object.hasOwn(properties, name)))
|
|
@@ -118,7 +134,7 @@ function assertSchema(value, path) {
|
|
|
118
134
|
}
|
|
119
135
|
export function assertAgentToolInputSchema(value, path = "inputSchema") {
|
|
120
136
|
if (!isRecord(value) || value.type !== "object")
|
|
121
|
-
invalid(path,
|
|
137
|
+
invalid(path, "root type must be object.");
|
|
122
138
|
if (checkedSchemas.has(value))
|
|
123
139
|
return;
|
|
124
140
|
const frozen = assertJson(value, path);
|
|
@@ -130,21 +146,29 @@ function jsonEqual(left, right) {
|
|
|
130
146
|
if (left === right)
|
|
131
147
|
return true;
|
|
132
148
|
if (Array.isArray(left) && Array.isArray(right))
|
|
133
|
-
return left.length === right.length && left.every((item, index) => jsonEqual(item, right[index]));
|
|
149
|
+
return (left.length === right.length && left.every((item, index) => jsonEqual(item, right[index])));
|
|
134
150
|
if (!isRecord(left) || !isRecord(right))
|
|
135
151
|
return false;
|
|
136
152
|
const keys = Object.keys(left);
|
|
137
|
-
return keys.length === Object.keys(right).length &&
|
|
153
|
+
return (keys.length === Object.keys(right).length &&
|
|
154
|
+
keys.every((key) => Object.hasOwn(right, key) && jsonEqual(left[key], right[key])));
|
|
138
155
|
}
|
|
139
156
|
function typeMatches(schema, value) {
|
|
140
157
|
switch (schema.type) {
|
|
141
|
-
case undefined:
|
|
142
|
-
|
|
143
|
-
case "
|
|
144
|
-
|
|
145
|
-
case "
|
|
146
|
-
|
|
147
|
-
|
|
158
|
+
case undefined:
|
|
159
|
+
return true;
|
|
160
|
+
case "null":
|
|
161
|
+
return value === null;
|
|
162
|
+
case "array":
|
|
163
|
+
return Array.isArray(value);
|
|
164
|
+
case "object":
|
|
165
|
+
return isRecord(value);
|
|
166
|
+
case "integer":
|
|
167
|
+
return typeof value === "number" && Number.isInteger(value);
|
|
168
|
+
case "number":
|
|
169
|
+
return typeof value === "number" && Number.isFinite(value);
|
|
170
|
+
default:
|
|
171
|
+
return typeof value === schema.type;
|
|
148
172
|
}
|
|
149
173
|
}
|
|
150
174
|
export function validateAgentToolInput(schema, value, path = "input") {
|
|
@@ -177,7 +201,8 @@ export function validateAgentToolInput(schema, value, path = "input") {
|
|
|
177
201
|
errors.push(`${path} must have at least ${schema.minItems} items.`);
|
|
178
202
|
if (schema.maxItems !== undefined && value.length > schema.maxItems)
|
|
179
203
|
errors.push(`${path} must have at most ${schema.maxItems} items.`);
|
|
180
|
-
if (schema.uniqueItems &&
|
|
204
|
+
if (schema.uniqueItems &&
|
|
205
|
+
value.some((item, index) => value.slice(0, index).some((other) => jsonEqual(item, other))))
|
|
181
206
|
errors.push(`${path} must contain unique items.`);
|
|
182
207
|
if (schema.items)
|
|
183
208
|
value.forEach((item, index) => errors.push(...validateAgentToolInput(schema.items, item, `${path}[${index}]`)));
|
|
@@ -188,7 +213,9 @@ export function validateAgentToolInput(schema, value, path = "input") {
|
|
|
188
213
|
errors.push(`${path}.${key} is required.`);
|
|
189
214
|
}
|
|
190
215
|
for (const [key, item] of Object.entries(value)) {
|
|
191
|
-
const property = schema.properties && Object.hasOwn(schema.properties, key)
|
|
216
|
+
const property = schema.properties && Object.hasOwn(schema.properties, key)
|
|
217
|
+
? schema.properties[key]
|
|
218
|
+
: undefined;
|
|
192
219
|
if (property)
|
|
193
220
|
errors.push(...validateAgentToolInput(property, item, `${path}.${key}`));
|
|
194
221
|
else if (schema.additionalProperties === false)
|
|
@@ -197,4 +224,3 @@ export function validateAgentToolInput(schema, value, path = "input") {
|
|
|
197
224
|
}
|
|
198
225
|
return errors;
|
|
199
226
|
}
|
|
200
|
-
//# sourceMappingURL=schema.js.map
|