@nexface/tools 0.1.15-alpha.1 → 0.1.15-alpha.3
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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { flushSync } from "react-dom";
|
|
3
|
-
import { useCallback, useLayoutEffect, useRef, useState } from "react";
|
|
3
|
+
import { useCallback, useLayoutEffect, useRef, useState, } from "react";
|
|
4
4
|
import { useSyncExternalStore } from "use-sync-external-store/shim";
|
|
5
5
|
import { PageCommandError } from "./browser/locator.js";
|
|
6
6
|
import { getInternalToolRuntime } from "./runtime.js";
|
|
@@ -9,7 +9,8 @@ import { isAgentActivation } from "./browser/focus.js";
|
|
|
9
9
|
import { OperationOverlayView } from "./operation-view.js";
|
|
10
10
|
const BROWSER_TOOL_NAMES = new Set(["nxf_getSnapshot", "nxf_executeAction", "nxf_applyAction"]);
|
|
11
11
|
const belongsToOperation = (event, operation) => !operation || event.operationId === operation.id;
|
|
12
|
-
const canPresentInOperation = (event, operation) => belongsToOperation(event, operation) &&
|
|
12
|
+
const canPresentInOperation = (event, operation) => belongsToOperation(event, operation) &&
|
|
13
|
+
(!operation || operation.status === "running" || operation.status === "ending");
|
|
13
14
|
const surfaces = new WeakSet();
|
|
14
15
|
export function AgentOperationSurface(props) {
|
|
15
16
|
const element = useRef(null);
|
|
@@ -21,28 +22,34 @@ export function AgentOperationSurface(props) {
|
|
|
21
22
|
if (surfaces.has(props.toolRuntime))
|
|
22
23
|
throw new Error("Only one AgentOperationSurface is supported per Runtime.");
|
|
23
24
|
surfaces.add(props.toolRuntime);
|
|
24
|
-
return () => {
|
|
25
|
+
return () => {
|
|
26
|
+
surfaces.delete(props.toolRuntime);
|
|
27
|
+
};
|
|
25
28
|
}, [props.toolRuntime]);
|
|
26
29
|
const blockUserInput = useCallback((event) => {
|
|
27
30
|
if ((event.type === "submit" || event.type === "reset") && isAgentActivation())
|
|
28
31
|
return;
|
|
29
|
-
if (!element.current ||
|
|
32
|
+
if (!element.current ||
|
|
33
|
+
!isOperationSurfaceLocked(element.current) ||
|
|
34
|
+
!event.nativeEvent.isTrusted)
|
|
30
35
|
return;
|
|
31
36
|
event.preventDefault();
|
|
32
37
|
event.stopPropagation();
|
|
33
38
|
}, []);
|
|
34
39
|
const blockPassiveUserInput = useCallback((event) => {
|
|
35
|
-
if (!element.current ||
|
|
40
|
+
if (!element.current ||
|
|
41
|
+
!isOperationSurfaceLocked(element.current) ||
|
|
42
|
+
!event.nativeEvent.isTrusted)
|
|
36
43
|
return;
|
|
37
44
|
// Stop React's capture dispatch, but let the native event reach our non-passive
|
|
38
45
|
// surface listener. React's passive root listener cannot prevent scrolling.
|
|
39
46
|
event.isPropagationStopped = () => true;
|
|
40
47
|
}, []);
|
|
41
|
-
return React.createElement(React.Fragment, null,
|
|
48
|
+
return (React.createElement(React.Fragment, null,
|
|
42
49
|
React.createElement("div", { ref: element, className: "nexface-operation-surface", onClickCapture: blockUserInput, onDoubleClickCapture: blockUserInput, onAuxClickCapture: blockUserInput, onPointerDownCapture: blockUserInput, onPointerUpCapture: blockUserInput, onPointerMoveCapture: blockUserInput, onPointerOverCapture: blockUserInput, onPointerOutCapture: blockUserInput, onMouseDownCapture: blockUserInput, onMouseUpCapture: blockUserInput, onContextMenuCapture: blockUserInput, onMouseMoveCapture: blockUserInput, onMouseOverCapture: blockUserInput, onMouseOutCapture: blockUserInput, onWheelCapture: blockPassiveUserInput, onTouchStartCapture: blockPassiveUserInput, onTouchMoveCapture: blockPassiveUserInput, onTouchEndCapture: blockUserInput, onTouchCancelCapture: blockUserInput, onKeyDownCapture: blockUserInput, onKeyUpCapture: blockUserInput, onKeyPressCapture: blockUserInput, onBeforeInputCapture: blockUserInput, onPasteCapture: blockUserInput, onCutCapture: blockUserInput, onDropCapture: blockUserInput, onDragStartCapture: blockUserInput, onSubmitCapture: blockUserInput, onResetCapture: blockUserInput }, props.children),
|
|
43
|
-
React.createElement(OperationSurfaceContent, { ...props, element: element }));
|
|
50
|
+
React.createElement(OperationSurfaceContent, { ...props, element: element })));
|
|
44
51
|
}
|
|
45
|
-
function OperationSurfaceContent({ toolRuntime, element }) {
|
|
52
|
+
function OperationSurfaceContent({ toolRuntime, element, }) {
|
|
46
53
|
const runtime = getInternalToolRuntime(toolRuntime);
|
|
47
54
|
const activity = useSyncExternalStore(runtime.subscribeActivity, runtime.getActivitySnapshot, runtime.getActivitySnapshot);
|
|
48
55
|
const cycle = activity.operation;
|
|
@@ -58,15 +65,24 @@ function OperationSurfaceContent({ toolRuntime, element }) {
|
|
|
58
65
|
useLayoutEffect(() => runtime.setPageActionFeedback((event, feedback, signal) => {
|
|
59
66
|
const operation = runtime.getActivitySnapshot().operation;
|
|
60
67
|
if (signal?.aborted || !canPresentInOperation(event, operation))
|
|
61
|
-
throw new PageCommandError({
|
|
68
|
+
throw new PageCommandError({
|
|
69
|
+
code: "EXECUTION_CANCELLED",
|
|
70
|
+
message: "Page action presentation was interrupted.",
|
|
71
|
+
});
|
|
62
72
|
const target = feedback.target;
|
|
63
|
-
if (!feedback.effect ||
|
|
73
|
+
if (!feedback.effect ||
|
|
74
|
+
(feedback.effect === "action" &&
|
|
75
|
+
(!target?.isConnected || !element.current?.contains(target)))) {
|
|
64
76
|
setVisual(undefined);
|
|
65
77
|
return;
|
|
66
78
|
}
|
|
67
79
|
const next = {
|
|
68
|
-
id: crypto.randomUUID(),
|
|
69
|
-
|
|
80
|
+
id: crypto.randomUUID(),
|
|
81
|
+
effect: feedback.effect,
|
|
82
|
+
label: label(event),
|
|
83
|
+
centered: feedback.effect === "read",
|
|
84
|
+
persistentCursor: true,
|
|
85
|
+
requiresTarget: feedback.effect === "action",
|
|
70
86
|
...(target ? { target } : {}),
|
|
71
87
|
};
|
|
72
88
|
if (feedback.effect !== "action") {
|
|
@@ -98,32 +114,61 @@ function OperationSurfaceContent({ toolRuntime, element }) {
|
|
|
98
114
|
const page = BROWSER_TOOL_NAMES.has(event.name);
|
|
99
115
|
if (canDisplay && event.status === "running") {
|
|
100
116
|
if (page)
|
|
101
|
-
setVisual(event.name === "nxf_getSnapshot"
|
|
102
|
-
|
|
103
|
-
|
|
117
|
+
setVisual(event.name === "nxf_getSnapshot"
|
|
118
|
+
? {
|
|
119
|
+
event,
|
|
120
|
+
feedback: {
|
|
121
|
+
id: event.id,
|
|
122
|
+
effect: "read",
|
|
123
|
+
label: label(event),
|
|
124
|
+
centered: true,
|
|
125
|
+
},
|
|
126
|
+
}
|
|
127
|
+
: undefined);
|
|
104
128
|
else {
|
|
105
|
-
const target = event.tool.definition.effect === "navigate"
|
|
129
|
+
const target = event.tool.definition.effect === "navigate"
|
|
130
|
+
? element.current
|
|
131
|
+
: runtime.resolveTarget(event.tool, event.input);
|
|
106
132
|
if (target?.isConnected && element.current?.contains(target)) {
|
|
107
133
|
const rect = target.getBoundingClientRect();
|
|
108
|
-
setVisual({
|
|
109
|
-
|
|
110
|
-
|
|
134
|
+
setVisual({
|
|
135
|
+
event,
|
|
136
|
+
feedback: {
|
|
137
|
+
id: event.id,
|
|
138
|
+
effect: event.tool.definition.effect ?? "action",
|
|
139
|
+
label: label(event),
|
|
140
|
+
resolveTarget: () => event.tool.definition.effect === "navigate"
|
|
141
|
+
? (element.current ?? undefined)
|
|
142
|
+
: runtime.resolveTarget(event.tool, event.input),
|
|
143
|
+
position: { left: rect.left + rect.width / 2, top: rect.top + rect.height / 2 },
|
|
144
|
+
},
|
|
145
|
+
});
|
|
111
146
|
}
|
|
112
147
|
}
|
|
113
148
|
}
|
|
114
149
|
else if (canDisplay)
|
|
115
150
|
setVisual((current) => current?.event.id === event.id && current.event.operationId === event.operationId
|
|
116
|
-
? page
|
|
151
|
+
? page
|
|
152
|
+
? undefined
|
|
153
|
+
: { ...current, event }
|
|
154
|
+
: current);
|
|
117
155
|
if (page && !operation && event.status === "error")
|
|
118
156
|
setOutcome({ id: scope, status: event.cancelled ? "cancelled" : "failed" });
|
|
119
157
|
setHistory((current) => {
|
|
120
|
-
if (!belongsToOperation(event, operation) ||
|
|
158
|
+
if (!belongsToOperation(event, operation) ||
|
|
159
|
+
(event.status !== "running" && current.scope !== scope))
|
|
121
160
|
return current;
|
|
122
161
|
const entries = current.scope === scope ? current.entries : [];
|
|
123
|
-
const entry = {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
162
|
+
const entry = {
|
|
163
|
+
toolCallId: event.id,
|
|
164
|
+
progressLabel: `${event.progressLabel || "正在处理你的请求"} · ${event.cancelled ? "已取消" : event.status === "running" ? "执行中" : event.status === "success" ? "已完成" : "失败"}`,
|
|
165
|
+
};
|
|
166
|
+
return {
|
|
167
|
+
scope,
|
|
168
|
+
entries: entries.some((item) => item.toolCallId === event.id)
|
|
169
|
+
? entries.map((item) => (item.toolCallId === event.id ? entry : item))
|
|
170
|
+
: [...entries, entry],
|
|
171
|
+
};
|
|
127
172
|
});
|
|
128
173
|
};
|
|
129
174
|
const unsubscribe = toolRuntime.on("toolexecution", onExecution);
|
|
@@ -136,25 +181,57 @@ function OperationSurfaceContent({ toolRuntime, element }) {
|
|
|
136
181
|
const waiting = cycle?.status === "waiting" && !executing;
|
|
137
182
|
const running = executing || (activated && cycle?.status !== "waiting");
|
|
138
183
|
const stopping = cycle?.status === "cancelling";
|
|
139
|
-
useLayoutEffect(() => {
|
|
140
|
-
|
|
184
|
+
useLayoutEffect(() => {
|
|
185
|
+
if (!running || waiting)
|
|
186
|
+
setVisual(undefined);
|
|
187
|
+
}, [running, waiting]);
|
|
141
188
|
const serverExecuting = cycle?.taskProgress?.phase === "tool";
|
|
142
189
|
const visualExecuting = Boolean(visual && activity.executions.some((event) => event.id === visual.event.id));
|
|
143
|
-
const currentVisual = !stopping &&
|
|
144
|
-
|
|
145
|
-
|
|
190
|
+
const currentVisual = !stopping &&
|
|
191
|
+
!waiting &&
|
|
192
|
+
visual &&
|
|
193
|
+
(!serverExecuting || (BROWSER_TOOL_NAMES.has(visual.event.name) && visualExecuting)) &&
|
|
194
|
+
canPresentInOperation(visual.event, cycle) &&
|
|
195
|
+
(cycle || visualExecuting)
|
|
196
|
+
? visual
|
|
197
|
+
: undefined;
|
|
146
198
|
const browserExecution = activity.executions.find((event) => BROWSER_TOOL_NAMES.has(event.name));
|
|
147
|
-
const progressLabel = currentVisual?.feedback.label ??
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
199
|
+
const progressLabel = currentVisual?.feedback.label ??
|
|
200
|
+
(waiting
|
|
201
|
+
? "等待你完成操作"
|
|
202
|
+
: stopping
|
|
203
|
+
? "正在终止"
|
|
204
|
+
: browserExecution
|
|
205
|
+
? browserExecution.progressLabel || "正在浏览网页"
|
|
206
|
+
: cycle?.taskProgress?.phase === "tool"
|
|
207
|
+
? cycle.taskProgress.progressLabel || "正在执行工具"
|
|
208
|
+
: cycle?.taskProgress
|
|
209
|
+
? "正在处理"
|
|
210
|
+
: "正在处理你的请求");
|
|
211
|
+
const operation = {
|
|
212
|
+
progressLabel,
|
|
213
|
+
status: currentVisual
|
|
214
|
+
? currentVisual.event.status === "running"
|
|
215
|
+
? "running"
|
|
216
|
+
: currentVisual.event.status === "success"
|
|
217
|
+
? "completed"
|
|
218
|
+
: "failed"
|
|
219
|
+
: running || waiting
|
|
220
|
+
? "running"
|
|
221
|
+
: outcome?.id === history.scope
|
|
222
|
+
? outcome.status
|
|
223
|
+
: "completed",
|
|
224
|
+
};
|
|
225
|
+
const feedback = currentVisual?.feedback ??
|
|
226
|
+
(serverExecuting && running && !waiting && !stopping
|
|
227
|
+
? {
|
|
228
|
+
id: cycle.taskProgress.toolCallId,
|
|
229
|
+
effect: "read",
|
|
230
|
+
label: progressLabel,
|
|
231
|
+
centered: true,
|
|
232
|
+
}
|
|
233
|
+
: undefined);
|
|
156
234
|
const resolveSurface = useCallback(() => element.current ?? undefined, [element]);
|
|
157
235
|
const entries = !cycle || history.scope === cycle.id ? history.entries : [];
|
|
158
|
-
return activated || executing || entries.length ? React.createElement(OperationOverlayView, { key: cycle?.id ?? history.scope, operation: operation, progressHistory: entries, running: running, humanHasControl: waiting, cancelling: stopping, ...(feedback ? { feedback } : {}), ...(cycle?.canCancel ? { onCancel: () => runtime.requestOperationCancel(cycle.id) } : {}), resolveSurface: resolveSurface }) : null;
|
|
236
|
+
return activated || executing || entries.length ? (React.createElement(OperationOverlayView, { key: cycle?.id ?? history.scope, operation: operation, progressHistory: entries, running: running, humanHasControl: waiting, cancelling: stopping, ...(feedback ? { feedback } : {}), ...(cycle?.canCancel ? { onCancel: () => runtime.requestOperationCancel(cycle.id) } : {}), resolveSurface: resolveSurface })) : null;
|
|
159
237
|
}
|
|
160
|
-
//# sourceMappingURL=operation-surface.js.map
|
package/dist/operation-types.js
CHANGED
package/dist/operation-view.d.ts
CHANGED
|
@@ -10,4 +10,4 @@ export interface OperationOverlayViewProps {
|
|
|
10
10
|
onCancel?(): void;
|
|
11
11
|
resolveSurface?(): HTMLElement | undefined;
|
|
12
12
|
}
|
|
13
|
-
export declare function OperationOverlayView({ operation, feedback, progressHistory, running, humanHasControl, cancelling, onCancel: cancel, resolveSurface }: OperationOverlayViewProps): React.ReactPortal | null;
|
|
13
|
+
export declare function OperationOverlayView({ operation, feedback, progressHistory, running, humanHasControl, cancelling, onCancel: cancel, resolveSurface, }: OperationOverlayViewProps): React.ReactPortal | null;
|
package/dist/operation-view.js
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
import { CursorIcon as Cursor } from "@phosphor-icons/react/Cursor";
|
|
2
2
|
import { SparkleIcon as Sparkle } from "@phosphor-icons/react/Sparkle";
|
|
3
3
|
import * as React from "react";
|
|
4
|
-
import { useCallback, useLayoutEffect, useEffect, useRef, useState } from "react";
|
|
4
|
+
import { useCallback, useLayoutEffect, useEffect, useRef, useState, } from "react";
|
|
5
5
|
import { createPortal } from "react-dom";
|
|
6
6
|
import { PageCommandError } from "./browser/locator.js";
|
|
7
7
|
import { lockOperationSurface } from "./operation-lock.js";
|
|
8
|
-
const center = (rect) => ({
|
|
8
|
+
const center = (rect) => ({
|
|
9
|
+
left: rect.left + rect.width / 2,
|
|
10
|
+
top: rect.top + rect.height / 2,
|
|
11
|
+
});
|
|
9
12
|
const distance = (a, b) => Math.hypot(a.left - b.left, a.top - b.top);
|
|
10
|
-
const interrupted = () => new PageCommandError({
|
|
13
|
+
const interrupted = () => new PageCommandError({
|
|
14
|
+
code: "EXECUTION_CANCELLED",
|
|
15
|
+
message: "Page action presentation was interrupted.",
|
|
16
|
+
});
|
|
11
17
|
function presentPointer(pointer, target, presentation) {
|
|
12
18
|
let finished = false;
|
|
13
19
|
const finish = (error) => {
|
|
@@ -19,45 +25,57 @@ function presentPointer(pointer, target, presentation) {
|
|
|
19
25
|
presentation.signal?.removeEventListener("abort", onAbort);
|
|
20
26
|
presentation.finish(error);
|
|
21
27
|
};
|
|
22
|
-
const reached = () => pointer.isConnected &&
|
|
28
|
+
const reached = () => pointer.isConnected &&
|
|
29
|
+
target.isConnected &&
|
|
30
|
+
distance(pointer.getBoundingClientRect(), center(target.getBoundingClientRect())) < 2;
|
|
23
31
|
const onTransition = (event) => {
|
|
24
32
|
if (event.target === pointer && event.propertyName === "transform" && reached())
|
|
25
33
|
finish();
|
|
26
34
|
};
|
|
27
35
|
const onAbort = () => finish(interrupted());
|
|
28
|
-
const timer = setTimeout(() => reached()
|
|
29
|
-
|
|
30
|
-
|
|
36
|
+
const timer = setTimeout(() => reached()
|
|
37
|
+
? finish()
|
|
38
|
+
: finish(new PageCommandError({
|
|
39
|
+
code: "ACTION_TIMEOUT",
|
|
40
|
+
message: "Page action pointer did not reach its target in time.",
|
|
41
|
+
})), 750);
|
|
31
42
|
pointer.addEventListener("transitionend", onTransition);
|
|
32
43
|
presentation.signal?.addEventListener("abort", onAbort, { once: true });
|
|
33
|
-
queueMicrotask(() => {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
44
|
+
queueMicrotask(() => {
|
|
45
|
+
if (presentation.signal?.aborted)
|
|
46
|
+
onAbort();
|
|
47
|
+
else if (reached())
|
|
48
|
+
finish();
|
|
49
|
+
});
|
|
37
50
|
return () => finish(interrupted());
|
|
38
51
|
}
|
|
39
52
|
function FeedbackLabel({ feedback }) {
|
|
40
|
-
return React.createElement("span", { className: "agent-operation__pointer-label", "data-browser-tool": feedback.centered ? "true" : undefined, "data-operation-effect": feedback.effect, title: feedback.label },
|
|
41
|
-
feedback.effect === "processing" ? React.createElement("span", { className: "agent-operation__pointer-processing" }) : null,
|
|
42
|
-
feedback.effect === "read" ? React.createElement("span", { className: "agent-operation__pointer-read-scan" },
|
|
53
|
+
return (React.createElement("span", { className: "agent-operation__pointer-label", "data-browser-tool": feedback.centered ? "true" : undefined, "data-operation-effect": feedback.effect, title: feedback.label },
|
|
54
|
+
feedback.effect === "processing" ? (React.createElement("span", { className: "agent-operation__pointer-processing" })) : null,
|
|
55
|
+
feedback.effect === "read" ? (React.createElement("span", { className: "agent-operation__pointer-read-scan" },
|
|
43
56
|
React.createElement("i", null),
|
|
44
57
|
React.createElement("i", null),
|
|
45
58
|
React.createElement("i", null),
|
|
46
59
|
React.createElement("i", null),
|
|
47
|
-
React.createElement("i", null)) : null,
|
|
48
|
-
feedback.effect === "navigate" ? React.createElement("span", { className: "agent-operation__pointer-navigation-trail" }) : null,
|
|
49
|
-
React.createElement("span", { className: "agent-operation__pointer-label-text" }, feedback.label));
|
|
60
|
+
React.createElement("i", null))) : null,
|
|
61
|
+
feedback.effect === "navigate" ? (React.createElement("span", { className: "agent-operation__pointer-navigation-trail" })) : null,
|
|
62
|
+
React.createElement("span", { className: "agent-operation__pointer-label-text" }, feedback.label)));
|
|
50
63
|
}
|
|
51
|
-
function FeedbackIndicator({ feedback, position }) {
|
|
64
|
+
function FeedbackIndicator({ feedback, position, }) {
|
|
52
65
|
const [lastPosition, setLastPosition] = useState(position ?? feedback.position);
|
|
53
|
-
useLayoutEffect(() => {
|
|
54
|
-
|
|
66
|
+
useLayoutEffect(() => {
|
|
67
|
+
if (position)
|
|
68
|
+
setLastPosition(position);
|
|
69
|
+
}, [position?.left, position?.top]);
|
|
55
70
|
const current = position ?? lastPosition;
|
|
56
|
-
return current ? React.createElement("div", { "aria-hidden": true, className: "agent-operation__pointer-effect", "data-active": "true", "data-operation-effect": feedback.effect, style: {
|
|
57
|
-
"--agent-operation-pointer-
|
|
58
|
-
|
|
71
|
+
return current ? (React.createElement("div", { "aria-hidden": true, className: "agent-operation__pointer-effect", "data-active": "true", "data-operation-effect": feedback.effect, style: {
|
|
72
|
+
"--agent-operation-pointer-duration": "0ms",
|
|
73
|
+
"--agent-operation-pointer-x": `${current.left}px`,
|
|
74
|
+
"--agent-operation-pointer-y": `${current.top}px`,
|
|
75
|
+
} },
|
|
76
|
+
React.createElement(FeedbackLabel, { feedback: feedback }))) : null;
|
|
59
77
|
}
|
|
60
|
-
function AgentPointer({ active, feedback, targetPosition, surfaceCenter }) {
|
|
78
|
+
function AgentPointer({ active, feedback, targetPosition, surfaceCenter, }) {
|
|
61
79
|
const pointer = useRef(null);
|
|
62
80
|
const [motion, setMotion] = useState();
|
|
63
81
|
const action = feedback?.effect === "action" && (!feedback.requiresTarget || Boolean(targetPosition));
|
|
@@ -75,8 +93,11 @@ function AgentPointer({ active, feedback, targetPosition, surfaceCenter }) {
|
|
|
75
93
|
return;
|
|
76
94
|
}
|
|
77
95
|
if (!motion) {
|
|
78
|
-
setMotion({
|
|
79
|
-
|
|
96
|
+
setMotion({
|
|
97
|
+
position: feedback?.persistentCursor && surfaceCenter ? surfaceCenter : targetPosition,
|
|
98
|
+
durationMs: 0,
|
|
99
|
+
persistent: Boolean(feedback?.persistentCursor),
|
|
100
|
+
});
|
|
80
101
|
return;
|
|
81
102
|
}
|
|
82
103
|
if (distance(motion.position, targetPosition) < 1) {
|
|
@@ -85,9 +106,14 @@ function AgentPointer({ active, feedback, targetPosition, surfaceCenter }) {
|
|
|
85
106
|
return;
|
|
86
107
|
}
|
|
87
108
|
const from = pointer.current?.getBoundingClientRect() ?? motion.position;
|
|
88
|
-
const durationMs = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches
|
|
109
|
+
const durationMs = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches
|
|
110
|
+
? 0
|
|
89
111
|
: Math.round(Math.min(360, Math.max(180, 180 + distance(from, targetPosition) * 0.18)));
|
|
90
|
-
setMotion({
|
|
112
|
+
setMotion({
|
|
113
|
+
position: targetPosition,
|
|
114
|
+
durationMs,
|
|
115
|
+
persistent: motion.persistent || Boolean(feedback?.persistentCursor),
|
|
116
|
+
});
|
|
91
117
|
}, [active, action, feedback?.id, targetPosition?.left, targetPosition?.top, motion]);
|
|
92
118
|
const show = Boolean(active && motion && (motion.persistent || action));
|
|
93
119
|
useLayoutEffect(() => {
|
|
@@ -100,18 +126,22 @@ function AgentPointer({ active, feedback, targetPosition, surfaceCenter }) {
|
|
|
100
126
|
if (show && pointer.current)
|
|
101
127
|
return presentPointer(pointer.current, feedback.target, feedback.presentation);
|
|
102
128
|
}, [active, show, feedback?.presentation, feedback?.target, Boolean(targetPosition)]);
|
|
103
|
-
return show && motion ? React.createElement("div", { "aria-hidden": true, ref: pointer, className: "agent-operation__pointer-effect", "data-active": action, "data-operation-effect": action ? "action" : undefined, style: {
|
|
104
|
-
"--agent-operation-pointer-
|
|
129
|
+
return show && motion ? (React.createElement("div", { "aria-hidden": true, ref: pointer, className: "agent-operation__pointer-effect", "data-active": action, "data-operation-effect": action ? "action" : undefined, style: {
|
|
130
|
+
"--agent-operation-pointer-duration": `${motion.durationMs}ms`,
|
|
131
|
+
"--agent-operation-pointer-x": `${motion.position.left}px`,
|
|
132
|
+
"--agent-operation-pointer-y": `${motion.position.top}px`,
|
|
133
|
+
} },
|
|
105
134
|
React.createElement("span", { className: "agent-operation__pointer-cursor" },
|
|
106
135
|
React.createElement(Cursor, { className: "agent-operation__pointer", size: 24, weight: "fill" })),
|
|
107
|
-
action && feedback ? React.createElement(FeedbackLabel, { feedback: feedback }) : null) : null;
|
|
136
|
+
action && feedback ? React.createElement(FeedbackLabel, { feedback: feedback }) : null)) : null;
|
|
108
137
|
}
|
|
109
|
-
export function OperationOverlayView({ operation, feedback, progressHistory = [], running, humanHasControl = false, cancelling = false, onCancel: cancel, resolveSurface }) {
|
|
138
|
+
export function OperationOverlayView({ operation, feedback, progressHistory = [], running, humanHasControl = false, cancelling = false, onCancel: cancel, resolveSurface, }) {
|
|
110
139
|
const [, setGeometry] = useState("");
|
|
111
140
|
const [historyOpen, setHistoryOpen] = useState(false);
|
|
112
141
|
const [historyId] = useState(() => `nexface-operation-history-${globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random()}`}`);
|
|
113
142
|
const cancelButtonRef = useRef(null);
|
|
114
|
-
const controllerShouldBeVisible = operation.status !== "cancelled" &&
|
|
143
|
+
const controllerShouldBeVisible = operation.status !== "cancelled" &&
|
|
144
|
+
(running || humanHasControl || operation.status === "failed");
|
|
115
145
|
const [controllerVisibility, setControllerVisibility] = useState(controllerShouldBeVisible ? "visible" : undefined);
|
|
116
146
|
useEffect(() => {
|
|
117
147
|
if (controllerShouldBeVisible) {
|
|
@@ -119,12 +149,14 @@ export function OperationOverlayView({ operation, feedback, progressHistory = []
|
|
|
119
149
|
return;
|
|
120
150
|
}
|
|
121
151
|
setHistoryOpen(false);
|
|
122
|
-
setControllerVisibility((current) => current ? "exiting" : undefined);
|
|
152
|
+
setControllerVisibility((current) => (current ? "exiting" : undefined));
|
|
123
153
|
const timer = setTimeout(() => setControllerVisibility(undefined), 300);
|
|
124
154
|
return () => clearTimeout(timer);
|
|
125
155
|
}, [controllerShouldBeVisible]);
|
|
126
|
-
useEffect(() => {
|
|
127
|
-
|
|
156
|
+
useEffect(() => {
|
|
157
|
+
if (!progressHistory.length)
|
|
158
|
+
setHistoryOpen(false);
|
|
159
|
+
}, [progressHistory.length]);
|
|
128
160
|
useLayoutEffect(() => {
|
|
129
161
|
const surface = resolveSurface?.();
|
|
130
162
|
if (!surface || !running || humanHasControl)
|
|
@@ -144,17 +176,22 @@ export function OperationOverlayView({ operation, feedback, progressHistory = []
|
|
|
144
176
|
useLayoutEffect(() => {
|
|
145
177
|
const update = () => {
|
|
146
178
|
const { surfaceRect, targetRect } = readGeometry();
|
|
147
|
-
const geometry = [surfaceRect, targetRect]
|
|
179
|
+
const geometry = [surfaceRect, targetRect]
|
|
180
|
+
.map((rect) => {
|
|
148
181
|
if (!rect)
|
|
149
182
|
return "";
|
|
150
183
|
return [rect.left, rect.top, rect.width, rect.height].join(",");
|
|
151
|
-
})
|
|
184
|
+
})
|
|
185
|
+
.join(";");
|
|
152
186
|
setGeometry(geometry);
|
|
153
187
|
};
|
|
154
188
|
update();
|
|
155
189
|
if (!running && !humanHasControl)
|
|
156
190
|
return;
|
|
157
|
-
let frame = requestAnimationFrame(function track() {
|
|
191
|
+
let frame = requestAnimationFrame(function track() {
|
|
192
|
+
update();
|
|
193
|
+
frame = requestAnimationFrame(track);
|
|
194
|
+
});
|
|
158
195
|
return () => cancelAnimationFrame(frame);
|
|
159
196
|
}, [readGeometry, operation, running, humanHasControl]);
|
|
160
197
|
if (typeof document === "undefined")
|
|
@@ -170,29 +207,53 @@ export function OperationOverlayView({ operation, feedback, progressHistory = []
|
|
|
170
207
|
const controllerExiting = controllerVisibility === "exiting";
|
|
171
208
|
const controllerLabel = humanHasControl ? undefined : operation.progressLabel;
|
|
172
209
|
const compactStatusLabel = humanHasControl ? "等待你完成操作" : operation.progressLabel;
|
|
173
|
-
const statusLabel = humanHasControl
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
210
|
+
const statusLabel = humanHasControl
|
|
211
|
+
? "等待你完成操作"
|
|
212
|
+
: {
|
|
213
|
+
running: "Agent 正在控制",
|
|
214
|
+
completed: running ? "工具已完成,任务继续中" : "操作已完成",
|
|
215
|
+
failed: running ? "Agent 正在处理错误" : "操作失败",
|
|
216
|
+
cancelled: "任务已终止",
|
|
217
|
+
}[operation.status];
|
|
177
218
|
const targetPosition = rect ? center(rect) : undefined;
|
|
178
219
|
return createPortal(React.createElement("div", { className: "agent-operation", "data-nexface-ignore": true, "data-feedback-active": operationFeedbackActive, "data-structured-target-lost": structuredTargetLost, "data-human-control": humanHasControl, "data-operation-effect": effect ?? "action", "data-run-active": running, "data-status": operation.status },
|
|
179
|
-
agentControlsSurface && surfaceRect ? React.createElement("div", { "aria-hidden": true, className: "agent-operation__surface", style: {
|
|
180
|
-
...(highlightedRect
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
220
|
+
agentControlsSurface && surfaceRect ? (React.createElement("div", { "aria-hidden": true, className: "agent-operation__surface", style: {
|
|
221
|
+
...(highlightedRect
|
|
222
|
+
? {
|
|
223
|
+
"--agent-operation-cutout-height": `${highlightedRect.height}px`,
|
|
224
|
+
"--agent-operation-cutout-left": `${highlightedRect.left - surfaceRect.left}px`,
|
|
225
|
+
"--agent-operation-cutout-top": `${highlightedRect.top - surfaceRect.top}px`,
|
|
226
|
+
"--agent-operation-cutout-width": `${highlightedRect.width}px`,
|
|
227
|
+
}
|
|
228
|
+
: {}),
|
|
229
|
+
height: surfaceRect.height,
|
|
230
|
+
left: surfaceRect.left,
|
|
231
|
+
top: surfaceRect.top,
|
|
232
|
+
width: surfaceRect.width,
|
|
233
|
+
} })) : null,
|
|
234
|
+
activeFeedback && highlightedRect ? (React.createElement("div", { "aria-hidden": true, className: "agent-operation__target", "data-operation-effect": effect, style: {
|
|
235
|
+
height: highlightedRect.height,
|
|
236
|
+
left: highlightedRect.left,
|
|
237
|
+
top: highlightedRect.top,
|
|
238
|
+
width: highlightedRect.width,
|
|
239
|
+
} })) : null,
|
|
186
240
|
React.createElement(AgentPointer, { active: agentControlsSurface && !cancelling, feedback: feedback?.presentation ? feedback : activeFeedback, targetPosition: targetPosition, surfaceCenter: surfaceRect ? center(surfaceRect) : undefined }),
|
|
187
|
-
activeFeedback && effect !== "action" && surfaceRect ? React.createElement(FeedbackIndicator, { key: activeFeedback.id, feedback: activeFeedback, position: activeFeedback.centered ? center(surfaceRect) : targetPosition }) : null,
|
|
188
|
-
controllerMounted ? React.createElement("section", { "aria-live": "polite", className: "agent-operation__controller", "data-visibility": controllerExiting ? "exiting" : "visible", style: surfaceRect
|
|
189
|
-
|
|
241
|
+
activeFeedback && effect !== "action" && surfaceRect ? (React.createElement(FeedbackIndicator, { key: activeFeedback.id, feedback: activeFeedback, position: activeFeedback.centered ? center(surfaceRect) : targetPosition })) : null,
|
|
242
|
+
controllerMounted ? (React.createElement("section", { "aria-live": "polite", className: "agent-operation__controller", "data-visibility": controllerExiting ? "exiting" : "visible", style: surfaceRect
|
|
243
|
+
? {
|
|
244
|
+
left: surfaceRect.left + surfaceRect.width / 2,
|
|
245
|
+
right: "auto",
|
|
246
|
+
"--agent-operation-controller-x": "-50%",
|
|
247
|
+
maxWidth: Math.max(0, surfaceRect.width - 32),
|
|
248
|
+
}
|
|
249
|
+
: undefined },
|
|
250
|
+
historyOpen ? (React.createElement("aside", { "aria-label": "\u672C\u6B21\u8FD0\u884C\u7684\u64CD\u4F5C\u8BB0\u5F55", className: "agent-operation__history", id: historyId },
|
|
190
251
|
React.createElement("header", null,
|
|
191
252
|
React.createElement("strong", null, "\u64CD\u4F5C\u8BB0\u5F55"),
|
|
192
253
|
React.createElement("span", null, progressHistory.length)),
|
|
193
|
-
React.createElement("ol", null, progressHistory.map(({ progressLabel: label, toolCallId }, index) => React.createElement("li", { key: toolCallId },
|
|
254
|
+
React.createElement("ol", null, progressHistory.map(({ progressLabel: label, toolCallId }, index) => (React.createElement("li", { key: toolCallId },
|
|
194
255
|
React.createElement("span", { "aria-hidden": true }, index + 1),
|
|
195
|
-
React.createElement("p", null, label))))) : null,
|
|
256
|
+
React.createElement("p", null, label))))))) : null,
|
|
196
257
|
React.createElement("span", { "aria-hidden": true, className: "agent-operation__edge-flow" }),
|
|
197
258
|
React.createElement("span", { "aria-hidden": true, className: "agent-operation__pause-mark" },
|
|
198
259
|
React.createElement("i", null),
|
|
@@ -204,9 +265,8 @@ export function OperationOverlayView({ operation, feedback, progressHistory = []
|
|
|
204
265
|
React.createElement("div", null,
|
|
205
266
|
React.createElement("span", { className: "agent-operation__heading" },
|
|
206
267
|
React.createElement("strong", null, controllerLabel)),
|
|
207
|
-
statusLabel ? React.createElement("small", { className: "agent-operation__description" },
|
|
208
|
-
React.createElement("span", { className: "agent-operation__status" }, statusLabel)) : null),
|
|
209
|
-
(running || humanHasControl) && operation.status !== "cancelled" ? React.createElement("span", { className: "agent-operation__compact-status", title: compactStatusLabel }, compactStatusLabel) : null,
|
|
210
|
-
cancel && (running || humanHasControl) && operation.status !== "cancelled" ? React.createElement("button", { ref: cancelButtonRef, className: "agent-operation__stop", type: "button", disabled: cancelling, onClick: cancel }, cancelling ? "正在终止" : "终止任务") : null) : null), document.body);
|
|
268
|
+
statusLabel ? (React.createElement("small", { className: "agent-operation__description" },
|
|
269
|
+
React.createElement("span", { className: "agent-operation__status" }, statusLabel))) : null),
|
|
270
|
+
(running || humanHasControl) && operation.status !== "cancelled" ? (React.createElement("span", { className: "agent-operation__compact-status", title: compactStatusLabel }, compactStatusLabel)) : null,
|
|
271
|
+
cancel && (running || humanHasControl) && operation.status !== "cancelled" ? (React.createElement("button", { ref: cancelButtonRef, className: "agent-operation__stop", type: "button", disabled: cancelling, onClick: cancel }, cancelling ? "正在终止" : "终止任务")) : null)) : null), document.body);
|
|
211
272
|
}
|
|
212
|
-
//# sourceMappingURL=operation-view.js.map
|
package/dist/react.js
CHANGED
|
@@ -3,7 +3,7 @@ import { Children, Fragment, cloneElement, useCallback, useEffect, useLayoutEffe
|
|
|
3
3
|
import { defineAgentTool } from "./definition.js";
|
|
4
4
|
import { getAgentNavigationEntries, } from "./navigation.js";
|
|
5
5
|
import { AgentToolError, getInternalToolRuntime } from "./runtime.js";
|
|
6
|
-
import { InternalToolRuntimeScope, useInternalToolRuntime
|
|
6
|
+
import { InternalToolRuntimeScope, useInternalToolRuntime } from "./internal-react.js";
|
|
7
7
|
import { connectToolRuntimeToWebMcp } from "./webmcp.js";
|
|
8
8
|
const bindingInternals = new WeakMap();
|
|
9
9
|
export function getAgentToolBindingInternals(binding) {
|
|
@@ -55,10 +55,10 @@ function createAgentToolBinding(toolName) {
|
|
|
55
55
|
function useAgentBinding(definition, execute, options) {
|
|
56
56
|
const runtime = useInternalToolRuntime();
|
|
57
57
|
const confirmation = options.requiresConfirmation;
|
|
58
|
-
if (confirmation !== undefined
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
if (confirmation !== undefined &&
|
|
59
|
+
(!confirmation ||
|
|
60
|
+
typeof confirmation !== "object" ||
|
|
61
|
+
(confirmation.mode !== "chat" && confirmation.mode !== "page"))) {
|
|
62
62
|
throw new Error("requiresConfirmation must explicitly use chat or page mode.");
|
|
63
63
|
}
|
|
64
64
|
const executeRef = useRef(execute);
|
|
@@ -82,9 +82,7 @@ export function useAgentTool(definition, execute, options = {}) {
|
|
|
82
82
|
return useAgentBinding(definition, execute, options);
|
|
83
83
|
}
|
|
84
84
|
function getAgentNavigationExecutor(executors, key) {
|
|
85
|
-
if (!executors
|
|
86
|
-
|| typeof executors !== "object"
|
|
87
|
-
|| !Object.hasOwn(executors, key)) {
|
|
85
|
+
if (!executors || typeof executors !== "object" || !Object.hasOwn(executors, key)) {
|
|
88
86
|
throw new Error(`Missing navigation executor: ${key}.`);
|
|
89
87
|
}
|
|
90
88
|
const executor = executors[key];
|
|
@@ -203,9 +201,7 @@ function isHTMLElement(value) {
|
|
|
203
201
|
return ElementConstructor !== undefined && value instanceof ElementConstructor;
|
|
204
202
|
}
|
|
205
203
|
function warnAgentUIAction(message) {
|
|
206
|
-
|
|
207
|
-
console.warn(message);
|
|
208
|
-
}
|
|
204
|
+
console.warn(message);
|
|
209
205
|
}
|
|
210
206
|
const eventAdapters = {
|
|
211
207
|
click: {
|
|
@@ -286,8 +282,8 @@ export function AgentUIAction({ name, description, event = "click", children, })
|
|
|
286
282
|
if (warnedChildRef.current !== warningType) {
|
|
287
283
|
warnedChildRef.current = warningType;
|
|
288
284
|
warnAgentUIAction(isFragment
|
|
289
|
-
? `AgentUIAction "${name}" does not support React.Fragment children.`
|
|
290
|
-
: `AgentUIAction "${name}" needs a child with onClick.`);
|
|
285
|
+
? `AgentUIAction "${name}" does not support React.Fragment children; the Tool is not registered.`
|
|
286
|
+
: `AgentUIAction "${name}" needs a child with onClick; the Tool is not registered.`);
|
|
291
287
|
}
|
|
292
288
|
return;
|
|
293
289
|
}
|
|
@@ -365,4 +361,3 @@ export function AgentUIAction({ name, description, event = "click", children, })
|
|
|
365
361
|
ref: mergedRef,
|
|
366
362
|
});
|
|
367
363
|
}
|
|
368
|
-
//# sourceMappingURL=react.js.map
|
package/dist/registration.js
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { assertAgentToolInputSchema } from "./shared/schema.js";
|
|
2
2
|
export function registerAgentTool(definition, options) {
|
|
3
3
|
assertAgentToolRegistration(definition, options);
|
|
4
|
-
assertAgentToolInputSchema(definition.inputSchema, `Tool "${definition.name}"
|
|
4
|
+
assertAgentToolInputSchema(definition.inputSchema, `Tool "${definition.name}" was not registered: inputSchema`);
|
|
5
5
|
return Object.freeze({ definition, execute: options.execute });
|
|
6
6
|
}
|
|
7
7
|
export function assertAgentToolRegistration(definition, options) {
|
|
8
|
-
if (!definition ||
|
|
8
|
+
if (!definition ||
|
|
9
|
+
typeof definition.name !== "string" ||
|
|
10
|
+
typeof options?.execute !== "function") {
|
|
9
11
|
throw new TypeError("registerAgentTool requires a definition and execute function.");
|
|
10
12
|
}
|
|
11
13
|
if ("requiresConfirmation" in options) {
|
|
12
14
|
throw new TypeError("Registered Agent tools do not support requiresConfirmation.");
|
|
13
15
|
}
|
|
14
16
|
}
|
|
15
|
-
//# sourceMappingURL=registration.js.map
|