@nexface/tools 0.1.1-alpha.0 → 0.1.1-alpha.10
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/controller.js +5 -4
- package/dist/browser/controller.js.map +1 -1
- package/dist/browser/dom.js +1 -2
- package/dist/browser/dom.js.map +1 -1
- package/dist/browser/feedback.js.map +1 -1
- package/dist/browser/focus.d.ts +2 -0
- package/dist/browser/focus.js +15 -0
- package/dist/browser/focus.js.map +1 -0
- package/dist/browser/locator.js.map +1 -1
- package/dist/browser/prompt.d.ts +1 -1
- package/dist/browser/prompt.generated.d.ts +1 -1
- package/dist/browser/prompt.generated.js +2 -2
- package/dist/browser/prompt.generated.js.map +1 -1
- package/dist/browser/prompt.js.map +1 -1
- package/dist/browser/snapshot.js.map +1 -1
- package/dist/browser/toolset.d.ts +12 -6
- package/dist/browser/toolset.js +18 -7
- package/dist/browser/toolset.js.map +1 -1
- package/dist/browser/types.js.map +1 -1
- package/dist/browser/worker-runner.js.map +1 -1
- package/dist/definition.js +10 -5
- package/dist/definition.js.map +1 -1
- package/dist/errors.d.ts +10 -0
- package/dist/errors.js +34 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.browser.d.ts +3 -0
- package/dist/index.browser.js +4 -0
- package/dist/index.browser.js.map +1 -0
- package/dist/index.d.ts +4 -5
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/internal-react.d.ts +1 -1
- package/dist/internal-react.js +0 -1
- package/dist/internal-react.js.map +1 -1
- package/dist/internal-types.d.ts +9 -0
- package/dist/internal-types.js.map +1 -1
- package/dist/internal.d.ts +3 -2
- package/dist/internal.js +1 -0
- package/dist/internal.js.map +1 -1
- package/dist/navigation.js.map +1 -1
- package/dist/operation-lock.d.ts +3 -0
- package/dist/operation-lock.js +46 -0
- package/dist/operation-lock.js.map +1 -0
- package/dist/operation-runtime.d.ts +44 -0
- package/dist/operation-runtime.js +153 -0
- package/dist/operation-runtime.js.map +1 -0
- package/dist/operation-surface.d.ts +2 -2
- package/dist/operation-surface.js +110 -65
- package/dist/operation-surface.js.map +1 -1
- package/dist/operation-types.d.ts +1 -1
- package/dist/operation-types.js.map +1 -1
- package/dist/operation-view.d.ts +8 -2
- package/dist/operation-view.js +30 -61
- package/dist/operation-view.js.map +1 -1
- package/dist/react.d.ts +24 -0
- package/dist/react.js.map +1 -1
- package/dist/registration.d.ts +11 -0
- package/dist/registration.js +12 -0
- package/dist/registration.js.map +1 -0
- package/dist/runtime.d.ts +58 -13
- package/dist/runtime.js +73 -81
- package/dist/runtime.js.map +1 -1
- package/dist/schema.d.ts +1 -0
- package/dist/schema.js +185 -32
- package/dist/schema.js.map +1 -1
- package/dist/styles.css +11 -7
- package/dist/types.d.ts +11 -1
- package/dist/types.js.map +1 -1
- package/dist/webmcp.js +163 -9
- package/dist/webmcp.js.map +1 -1
- package/package.json +7 -12
- package/dist/operation-host.d.ts +0 -17
- package/dist/operation-host.js +0 -9
- package/dist/operation-host.js.map +0 -1
package/dist/internal-types.d.ts
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
1
|
import type { RuntimeAgentTool } from "./runtime.js";
|
|
2
|
+
/**
|
|
3
|
+
* @experimental Status of an experimental execution notification.
|
|
4
|
+
* @beta
|
|
5
|
+
*/
|
|
2
6
|
export type AgentToolExecutionStatus = "running" | "success" | "error";
|
|
7
|
+
/**
|
|
8
|
+
* @experimental Payload inferred by experimental execution listeners; not a named public export.
|
|
9
|
+
* @beta
|
|
10
|
+
*/
|
|
3
11
|
export interface AgentToolExecutionEvent {
|
|
4
12
|
tool: RuntimeAgentTool<any, any>;
|
|
5
13
|
cancelled?: boolean;
|
|
6
14
|
progressLabel?: string;
|
|
7
15
|
id: string;
|
|
16
|
+
operationId?: string;
|
|
8
17
|
name: string;
|
|
9
18
|
status: AgentToolExecutionStatus;
|
|
10
19
|
startedAt: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internal-types.js","sourceRoot":"","sources":["../src/internal-types.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"internal-types.js","sourceRoot":"","sources":["../src/internal-types.ts"],"names":[],"mappings":"","sourcesContent":["import type { RuntimeAgentTool } from \"./runtime.js\";\n\n/**\n * @experimental Status of an experimental execution notification.\n * @beta\n */\nexport type AgentToolExecutionStatus = \"running\" | \"success\" | \"error\";\n\n/**\n * @experimental Payload inferred by experimental execution listeners; not a named public export.\n * @beta\n */\nexport interface AgentToolExecutionEvent {\n tool: RuntimeAgentTool<any, any>;\n cancelled?: boolean;\n progressLabel?: string;\n id: string;\n operationId?: string;\n name: string;\n status: AgentToolExecutionStatus;\n startedAt: number;\n finishedAt?: number;\n input: unknown;\n result?: unknown;\n error?: unknown;\n}\n"]}
|
package/dist/internal.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export { AgentContextError, ToolRuntimeImplementation, ToolRuntimeImplementation as ToolRuntime, AgentToolError, createToolRuntime, getInternalToolRuntime, } from "./runtime.js";
|
|
2
|
-
export type { ToolRuntime as PublicToolRuntime, ToolRuntimeOptions, InternalToolRuntime, } from "./runtime.js";
|
|
2
|
+
export type { ToolRuntime as PublicToolRuntime, ToolRuntimeOptions, ToolOperation, InternalToolRuntime, } from "./runtime.js";
|
|
3
3
|
export type { AgentToolExecutionEvent } from "./internal-types.js";
|
|
4
4
|
export { defineSystemAgentTool } from "./definition.js";
|
|
5
5
|
export { browserToolsAvailable, createBrowserToolset, executeCodeTool, getSnapshotTool, startPageTransitionTracker, } from "./browser/toolset.js";
|
|
6
6
|
export { BROWSER_TOOLS_SKILL, serializeBrowserToolsSkill, } from "./browser/prompt.js";
|
|
7
|
-
export type { BrowserToolHandler,
|
|
7
|
+
export type { BrowserToolHandler, PageAutomationOptions, BrowserToolset, PageTransitionSettleResult, PageTransitionTracker, } from "./browser/toolset.js";
|
|
8
8
|
export type * from "./browser/types.js";
|
|
9
9
|
export type { AgentContextRegistration } from "./types.js";
|
|
10
|
+
export { assertAgentToolInputSchema, validateAgentToolInput } from "./schema.js";
|
package/dist/internal.js
CHANGED
|
@@ -2,4 +2,5 @@ export { AgentContextError, ToolRuntimeImplementation, ToolRuntimeImplementation
|
|
|
2
2
|
export { defineSystemAgentTool } from "./definition.js";
|
|
3
3
|
export { browserToolsAvailable, createBrowserToolset, executeCodeTool, getSnapshotTool, startPageTransitionTracker, } from "./browser/toolset.js";
|
|
4
4
|
export { BROWSER_TOOLS_SKILL, serializeBrowserToolsSkill, } from "./browser/prompt.js";
|
|
5
|
+
export { assertAgentToolInputSchema, validateAgentToolInput } from "./schema.js";
|
|
5
6
|
//# sourceMappingURL=internal.js.map
|
package/dist/internal.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"internal.js","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EACzB,yBAAyB,IAAI,WAAW,EACxC,cAAc,EACd,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"internal.js","sourceRoot":"","sources":["../src/internal.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EACzB,yBAAyB,IAAI,WAAW,EACxC,cAAc,EACd,iBAAiB,EACjB,sBAAsB,GACvB,MAAM,cAAc,CAAC;AAQtB,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EACL,qBAAqB,EACrB,oBAAoB,EACpB,eAAe,EACf,eAAe,EACf,0BAA0B,GAC3B,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EACL,mBAAmB,EACnB,0BAA0B,GAC3B,MAAM,qBAAqB,CAAC;AAW7B,OAAO,EAAE,0BAA0B,EAAE,sBAAsB,EAAE,MAAM,aAAa,CAAC","sourcesContent":["export {\n AgentContextError,\n ToolRuntimeImplementation,\n ToolRuntimeImplementation as ToolRuntime,\n AgentToolError,\n createToolRuntime,\n getInternalToolRuntime,\n} from \"./runtime.js\";\nexport type {\n ToolRuntime as PublicToolRuntime,\n ToolRuntimeOptions,\n ToolOperation,\n InternalToolRuntime,\n} from \"./runtime.js\";\nexport type { AgentToolExecutionEvent } from \"./internal-types.js\";\nexport { defineSystemAgentTool } from \"./definition.js\";\nexport {\n browserToolsAvailable,\n createBrowserToolset,\n executeCodeTool,\n getSnapshotTool,\n startPageTransitionTracker,\n} from \"./browser/toolset.js\";\nexport {\n BROWSER_TOOLS_SKILL,\n serializeBrowserToolsSkill,\n} from \"./browser/prompt.js\";\nexport type {\n BrowserToolHandler,\n PageAutomationOptions,\n BrowserToolset,\n PageTransitionSettleResult,\n PageTransitionTracker,\n} from \"./browser/toolset.js\";\nexport type * from \"./browser/types.js\";\nexport type { AgentContextRegistration } from \"./types.js\";\n\nexport { assertAgentToolInputSchema, validateAgentToolInput } from \"./schema.js\";\n"]}
|
package/dist/navigation.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"navigation.js","sourceRoot":"","sources":["../src/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AA6BlD,MAAM,wBAAwB,GAAG,UAAU,CAAC;AA4B5C,MAAM,iBAAiB,GAAG,IAAI,OAAO,EAGlC,CAAC;AAEJ,MAAM,UAAU,qBAAqB,CAGnC,MAOC;IAED,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QACjE,MAAM,IAAI,GAAG,GAAG,wBAAwB,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;QACxD,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW;YAClC,CAAC,CAAC,eAAe,CAAiC;gBAC9C,IAAI;gBACJ,MAAM,EAAE,UAAU;gBAClB,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,WAAW,EAAE,KAAK,CAAC,WAAW;aAC/B,CAAC;YACJ,CAAC,CAAC,eAAe,CAAwB;gBACrC,IAAI;gBACJ,MAAM,EAAE,UAAU;gBAClB,WAAW,EAAE,KAAK,CAAC,WAAW;aAC/B,CAAC,CAAC;QACP,IAAI,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CAAC,mCAAmC,UAAU,CAAC,IAAI,GAAG,CAAC,CAAC;QACzE,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC3B,OAAO,MAAM,CAAC,MAAM,CAAC;YACnB,GAAG;YACH,UAAU;SACX,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAwC,CAAC;IAC5E,iBAAiB,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IAC1D,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,yBAAyB,CACvC,UAAgC;IAEhC,MAAM,OAAO,GAAG,iBAAiB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAClD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
1
|
+
{"version":3,"file":"navigation.js","sourceRoot":"","sources":["../src/navigation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AA6BlD,MAAM,wBAAwB,GAAG,UAAU,CAAC;AA4B5C,MAAM,iBAAiB,GAAG,IAAI,OAAO,EAGlC,CAAC;AAEJ,MAAM,UAAU,qBAAqB,CAGnC,MAOC;IAED,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE;QACjE,MAAM,IAAI,GAAG,GAAG,wBAAwB,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;QACxD,MAAM,UAAU,GAAG,KAAK,CAAC,WAAW;YAClC,CAAC,CAAC,eAAe,CAAiC;gBAC9C,IAAI;gBACJ,MAAM,EAAE,UAAU;gBAClB,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,WAAW,EAAE,KAAK,CAAC,WAAW;aAC/B,CAAC;YACJ,CAAC,CAAC,eAAe,CAAwB;gBACrC,IAAI;gBACJ,MAAM,EAAE,UAAU;gBAClB,WAAW,EAAE,KAAK,CAAC,WAAW;aAC/B,CAAC,CAAC;QACP,IAAI,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;YAC/B,MAAM,IAAI,KAAK,CAAC,mCAAmC,UAAU,CAAC,IAAI,GAAG,CAAC,CAAC;QACzE,CAAC;QACD,KAAK,CAAC,GAAG,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC3B,OAAO,MAAM,CAAC,MAAM,CAAC;YACnB,GAAG;YACH,UAAU;SACX,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IACH,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,EAAE,CAAwC,CAAC;IAC5E,iBAAiB,CAAC,GAAG,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC;IAC1D,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,MAAM,UAAU,yBAAyB,CACvC,UAAgC;IAEhC,MAAM,OAAO,GAAG,iBAAiB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IAClD,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAC;IAC5E,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC","sourcesContent":["import { defineAgentTool } from \"./definition.js\";\nimport type {\n AgentToolDefinition,\n AgentToolExecutionContext,\n AgentToolInputSchema,\n InferJsonSchema,\n} from \"./types.js\";\n\nexport interface AgentNavigationResult {\n status: \"navigated\";\n}\n\ndeclare const agentNavigationBrand: unique symbol;\n\nexport interface AgentNavigationRouteConfig {\n name: string;\n description: string;\n inputSchema?: AgentToolInputSchema;\n}\n\nexport interface AgentNavigation<\n TRoutes extends Record<string, AgentNavigationRouteConfig> = Record<\n string,\n AgentNavigationRouteConfig\n >,\n> {\n readonly [agentNavigationBrand]: TRoutes;\n}\n\nconst navigationToolNamePrefix = \"nxf_nav_\";\n\nexport interface AgentNavigationConfig<\n TRoutes extends Record<string, AgentNavigationRouteConfig>,\n> {\n routes: TRoutes;\n}\n\nexport type AgentNavigationExecutors<\n TNavigation extends AgentNavigation,\n> = TNavigation extends AgentNavigation<infer TRoutes>\n ? {\n [TKey in keyof TRoutes]: (\n input: TRoutes[TKey] extends {\n readonly inputSchema: infer TSchema extends AgentToolInputSchema;\n }\n ? InferJsonSchema<TSchema>\n : Record<string, never>,\n context: AgentToolExecutionContext,\n ) => void | Promise<void>;\n }\n : never;\n\ninterface InternalAgentNavigationRoute {\n key: string;\n definition: AgentToolDefinition<any, AgentNavigationResult>;\n}\n\nconst navigationEntries = new WeakMap<\n AgentNavigation<any>,\n readonly InternalAgentNavigationRoute[]\n>();\n\nexport function defineAgentNavigation<\n const TRoutes extends Record<string, AgentNavigationRouteConfig>,\n>(\n config: AgentNavigationConfig<TRoutes> & {\n routes: {\n [TKey in keyof TRoutes]: Record<\n Exclude<keyof TRoutes[TKey], keyof AgentNavigationRouteConfig>,\n never\n >;\n };\n },\n): AgentNavigation<TRoutes> {\n const names = new Set<string>();\n const entries = Object.entries(config.routes).map(([key, route]) => {\n const name = `${navigationToolNamePrefix}${route.name}`;\n const definition = route.inputSchema\n ? defineAgentTool<unknown, AgentNavigationResult>({\n name,\n effect: \"navigate\",\n description: route.description,\n inputSchema: route.inputSchema,\n })\n : defineAgentTool<AgentNavigationResult>({\n name,\n effect: \"navigate\",\n description: route.description,\n });\n if (names.has(definition.name)) {\n throw new Error(`Duplicate navigation tool name: ${definition.name}.`);\n }\n names.add(definition.name);\n return Object.freeze({\n key,\n definition,\n });\n });\n const navigation = Object.freeze({}) as unknown as AgentNavigation<TRoutes>;\n navigationEntries.set(navigation, Object.freeze(entries));\n return navigation;\n}\n\nexport function getAgentNavigationEntries(\n navigation: AgentNavigation<any>,\n): readonly InternalAgentNavigationRoute[] {\n const entries = navigationEntries.get(navigation);\n if (!entries) {\n throw new Error(\"Navigation must be created by defineAgentNavigation().\");\n }\n return entries;\n}\n"]}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { isAgentFocus } from "./browser/focus.js";
|
|
2
|
+
const lockedSurfaces = new WeakSet();
|
|
3
|
+
export function isOperationSurfaceLocked(surface) {
|
|
4
|
+
return lockedSurfaces.has(surface);
|
|
5
|
+
}
|
|
6
|
+
/** Block user input inside the surface without hiding DOM from page tools. */
|
|
7
|
+
export function lockOperationSurface(surface, redirectFocus) {
|
|
8
|
+
const previousBusy = surface.getAttribute("aria-busy");
|
|
9
|
+
const block = (event) => {
|
|
10
|
+
if (!event.isTrusted)
|
|
11
|
+
return;
|
|
12
|
+
if (event.type === "focus" || event.type === "focusin") {
|
|
13
|
+
if (isAgentFocus())
|
|
14
|
+
return;
|
|
15
|
+
event.stopImmediatePropagation();
|
|
16
|
+
if (event.target instanceof HTMLElement)
|
|
17
|
+
event.target.blur();
|
|
18
|
+
redirectFocus?.();
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
if (event.type === "keydown" && event.key === "Tab") {
|
|
22
|
+
event.preventDefault();
|
|
23
|
+
event.stopImmediatePropagation();
|
|
24
|
+
redirectFocus?.();
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
event.preventDefault();
|
|
28
|
+
event.stopImmediatePropagation();
|
|
29
|
+
};
|
|
30
|
+
// Wheel and touch defaults are blocked here: React delegates them passively.
|
|
31
|
+
const events = ["focus", "focusin", "mouseenter", "mouseleave", "pointerenter", "pointerleave", "pointerdown", "pointerup", "pointermove", "pointerover", "pointerout", "mousedown", "mouseup", "mousemove", "mouseover", "mouseout", "click", "dblclick", "auxclick", "contextmenu", "keydown", "keyup", "beforeinput", "paste", "cut", "drop", "dragstart", "touchstart", "touchmove", "touchend", "touchcancel", "wheel", "submit", "reset"];
|
|
32
|
+
lockedSurfaces.add(surface);
|
|
33
|
+
surface.setAttribute("aria-busy", "true");
|
|
34
|
+
for (const name of events)
|
|
35
|
+
surface.addEventListener(name, block, { capture: true, passive: false });
|
|
36
|
+
return () => {
|
|
37
|
+
lockedSurfaces.delete(surface);
|
|
38
|
+
for (const name of events)
|
|
39
|
+
surface.removeEventListener(name, block, true);
|
|
40
|
+
if (previousBusy === null)
|
|
41
|
+
surface.removeAttribute("aria-busy");
|
|
42
|
+
else
|
|
43
|
+
surface.setAttribute("aria-busy", previousBusy);
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=operation-lock.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operation-lock.js","sourceRoot":"","sources":["../src/operation-lock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,cAAc,GAAG,IAAI,OAAO,EAAe,CAAC;AAElD,MAAM,UAAU,wBAAwB,CAAC,OAAoB;IAC3D,OAAO,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;AACrC,CAAC;AAED,8EAA8E;AAC9E,MAAM,UAAU,oBAAoB,CAAC,OAAoB,EAAE,aAA0B;IACnF,MAAM,YAAY,GAAG,OAAO,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;IACvD,MAAM,KAAK,GAAG,CAAC,KAAY,EAAE,EAAE;QAC7B,IAAI,CAAC,KAAK,CAAC,SAAS;YAAE,OAAO;QAC7B,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,EAAE,CAAC;YACvD,IAAI,YAAY,EAAE;gBAAE,OAAO;YAC3B,KAAK,CAAC,wBAAwB,EAAE,CAAC;YACjC,IAAI,KAAK,CAAC,MAAM,YAAY,WAAW;gBAAE,KAAK,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC;YAC7D,aAAa,EAAE,EAAE,CAAC;YAClB,OAAO;QACT,CAAC;QACD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAK,KAAuB,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;YACvE,KAAK,CAAC,cAAc,EAAE,CAAC;YACvB,KAAK,CAAC,wBAAwB,EAAE,CAAC;YACjC,aAAa,EAAE,EAAE,CAAC;YAClB,OAAO;QACT,CAAC;QACD,KAAK,CAAC,cAAc,EAAE,CAAC;QACvB,KAAK,CAAC,wBAAwB,EAAE,CAAC;IACnC,CAAC,CAAC;IACF,6EAA6E;IAC7E,MAAM,MAAM,GAAG,CAAC,OAAO,EAAE,SAAS,EAAE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,UAAU,EAAE,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,EAAE,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAChb,cAAc,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAC5B,OAAO,CAAC,YAAY,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;IAC1C,KAAK,MAAM,IAAI,IAAI,MAAM;QAAE,OAAO,CAAC,gBAAgB,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC;IACpG,OAAO,GAAG,EAAE;QACV,cAAc,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QAC/B,KAAK,MAAM,IAAI,IAAI,MAAM;YAAE,OAAO,CAAC,mBAAmB,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;QAC1E,IAAI,YAAY,KAAK,IAAI;YAAE,OAAO,CAAC,eAAe,CAAC,WAAW,CAAC,CAAC;;YAC3D,OAAO,CAAC,YAAY,CAAC,WAAW,EAAE,YAAY,CAAC,CAAC;IACvD,CAAC,CAAC;AACJ,CAAC","sourcesContent":["import { isAgentFocus } from \"./browser/focus.js\";\n\nconst lockedSurfaces = new WeakSet<HTMLElement>();\n\nexport function isOperationSurfaceLocked(surface: HTMLElement): boolean {\n return lockedSurfaces.has(surface);\n}\n\n/** Block user input inside the surface without hiding DOM from page tools. */\nexport function lockOperationSurface(surface: HTMLElement, redirectFocus?: () => void): () => void {\n const previousBusy = surface.getAttribute(\"aria-busy\");\n const block = (event: Event) => {\n if (!event.isTrusted) return;\n if (event.type === \"focus\" || event.type === \"focusin\") {\n if (isAgentFocus()) return;\n event.stopImmediatePropagation();\n if (event.target instanceof HTMLElement) event.target.blur();\n redirectFocus?.();\n return;\n }\n if (event.type === \"keydown\" && (event as KeyboardEvent).key === \"Tab\") {\n event.preventDefault();\n event.stopImmediatePropagation();\n redirectFocus?.();\n return;\n }\n event.preventDefault();\n event.stopImmediatePropagation();\n };\n // Wheel and touch defaults are blocked here: React delegates them passively.\n const events = [\"focus\", \"focusin\", \"mouseenter\", \"mouseleave\", \"pointerenter\", \"pointerleave\", \"pointerdown\", \"pointerup\", \"pointermove\", \"pointerover\", \"pointerout\", \"mousedown\", \"mouseup\", \"mousemove\", \"mouseover\", \"mouseout\", \"click\", \"dblclick\", \"auxclick\", \"contextmenu\", \"keydown\", \"keyup\", \"beforeinput\", \"paste\", \"cut\", \"drop\", \"dragstart\", \"touchstart\", \"touchmove\", \"touchend\", \"touchcancel\", \"wheel\", \"submit\", \"reset\"];\n lockedSurfaces.add(surface);\n surface.setAttribute(\"aria-busy\", \"true\");\n for (const name of events) surface.addEventListener(name, block, { capture: true, passive: false });\n return () => {\n lockedSurfaces.delete(surface);\n for (const name of events) surface.removeEventListener(name, block, true);\n if (previousBusy === null) surface.removeAttribute(\"aria-busy\");\n else surface.setAttribute(\"aria-busy\", previousBusy);\n };\n}\n"]}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import type { AgentToolExecutionEvent } from "./internal-types.js";
|
|
2
|
+
import type { ToolOperation } from "./runtime.js";
|
|
3
|
+
export type TaskProgress = {
|
|
4
|
+
readonly phase: "processing";
|
|
5
|
+
} | {
|
|
6
|
+
readonly phase: "tool";
|
|
7
|
+
readonly toolCallId: string;
|
|
8
|
+
readonly progressLabel?: string;
|
|
9
|
+
};
|
|
10
|
+
export interface OperationSnapshot {
|
|
11
|
+
readonly taskProgress?: TaskProgress;
|
|
12
|
+
readonly id: string;
|
|
13
|
+
readonly source: "host" | "webmcp";
|
|
14
|
+
readonly status: "running" | "waiting" | "cancelling" | "ending";
|
|
15
|
+
readonly hasExecuted: boolean;
|
|
16
|
+
readonly hasStartedTool: boolean;
|
|
17
|
+
readonly outcome?: "completed" | "failed" | "cancelled";
|
|
18
|
+
readonly reason?: "bridge_disposed";
|
|
19
|
+
readonly canCancel: boolean;
|
|
20
|
+
}
|
|
21
|
+
export interface RuntimeActivitySnapshot {
|
|
22
|
+
readonly operation: OperationSnapshot | null;
|
|
23
|
+
readonly executions: readonly AgentToolExecutionEvent[];
|
|
24
|
+
}
|
|
25
|
+
export declare class OperationTracker {
|
|
26
|
+
#private;
|
|
27
|
+
getSnapshot: () => RuntimeActivitySnapshot;
|
|
28
|
+
subscribe: (listener: () => void) => (() => void);
|
|
29
|
+
begin(options?: {
|
|
30
|
+
signal?: AbortSignal;
|
|
31
|
+
onCancel?: () => void;
|
|
32
|
+
source?: "host" | "webmcp";
|
|
33
|
+
replaceWaiting?: ToolOperation;
|
|
34
|
+
taskProgress?: TaskProgress;
|
|
35
|
+
}): ToolOperation;
|
|
36
|
+
setTaskProgress(handle: ToolOperation, progress: TaskProgress): void;
|
|
37
|
+
disposeBridge(handle: ToolOperation): Promise<void>;
|
|
38
|
+
requestCancel(id: string): void;
|
|
39
|
+
admit(): {
|
|
40
|
+
operationId?: string;
|
|
41
|
+
release(): void;
|
|
42
|
+
};
|
|
43
|
+
observe(event: AgentToolExecutionEvent): void;
|
|
44
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import { AgentToolError } from "./errors.js";
|
|
2
|
+
export class OperationTracker {
|
|
3
|
+
#operation;
|
|
4
|
+
#unscoped = 0;
|
|
5
|
+
#executions = new Map();
|
|
6
|
+
#listeners = new Set();
|
|
7
|
+
#snapshot = Object.freeze({ operation: null, executions: Object.freeze([]) });
|
|
8
|
+
getSnapshot = () => this.#snapshot;
|
|
9
|
+
subscribe = (listener) => {
|
|
10
|
+
this.#listeners.add(listener);
|
|
11
|
+
return () => { this.#listeners.delete(listener); };
|
|
12
|
+
};
|
|
13
|
+
begin(options = {}) {
|
|
14
|
+
options.signal?.throwIfAborted();
|
|
15
|
+
const previous = this.#operation;
|
|
16
|
+
const replacing = previous && previous.handle === options.replaceWaiting
|
|
17
|
+
&& previous.snapshot.status === "waiting" && previous.pending === 0;
|
|
18
|
+
if (this.#unscoped || (previous && !replacing)) {
|
|
19
|
+
throw new AgentToolError("OPERATION_BUSY", "The Runtime already has an operation or outstanding tool calls.");
|
|
20
|
+
}
|
|
21
|
+
if (replacing) {
|
|
22
|
+
previous.removeAbort();
|
|
23
|
+
previous.resolveEnd();
|
|
24
|
+
}
|
|
25
|
+
let resolveEnd;
|
|
26
|
+
const endPromise = new Promise((resolve) => { resolveEnd = resolve; });
|
|
27
|
+
const id = globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random()}`;
|
|
28
|
+
const operation = {
|
|
29
|
+
snapshot: Object.freeze({ id, source: options.source ?? "host", status: "running", hasExecuted: false, hasStartedTool: options.taskProgress?.phase === "tool", canCancel: Boolean(options.onCancel), ...(options.taskProgress ? { taskProgress: Object.freeze(options.taskProgress) } : {}) }),
|
|
30
|
+
handle: undefined, pending: 0, resolveEnd,
|
|
31
|
+
...(options.onCancel ? { onCancel: options.onCancel } : {}),
|
|
32
|
+
removeAbort: () => options.signal?.removeEventListener("abort", onAbort),
|
|
33
|
+
};
|
|
34
|
+
const onAbort = () => {
|
|
35
|
+
if (this.#operation !== operation || operation.snapshot.status === "ending")
|
|
36
|
+
return;
|
|
37
|
+
this.#update(operation, { status: "cancelling" });
|
|
38
|
+
};
|
|
39
|
+
const handle = Object.freeze({
|
|
40
|
+
id,
|
|
41
|
+
setWaitingForUser: (waiting) => {
|
|
42
|
+
if (this.#operation !== operation || !["running", "waiting"].includes(operation.snapshot.status))
|
|
43
|
+
return;
|
|
44
|
+
const status = waiting ? "waiting" : "running";
|
|
45
|
+
if (status !== operation.snapshot.status)
|
|
46
|
+
this.#update(operation, { status });
|
|
47
|
+
},
|
|
48
|
+
end: (endOptions) => {
|
|
49
|
+
if (this.#operation !== operation || operation.snapshot.status === "ending")
|
|
50
|
+
return endPromise;
|
|
51
|
+
operation.removeAbort();
|
|
52
|
+
operation.snapshot = Object.freeze({ ...operation.snapshot, status: "ending", outcome: endOptions?.outcome ?? "completed" });
|
|
53
|
+
this.#publish();
|
|
54
|
+
if (this.#finish(operation))
|
|
55
|
+
this.#publish();
|
|
56
|
+
return endPromise;
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
operation.handle = handle;
|
|
60
|
+
this.#operation = operation;
|
|
61
|
+
options.signal?.addEventListener("abort", onAbort, { once: true });
|
|
62
|
+
this.#publish();
|
|
63
|
+
return handle;
|
|
64
|
+
}
|
|
65
|
+
setTaskProgress(handle, progress) {
|
|
66
|
+
const operation = this.#operation;
|
|
67
|
+
if (operation?.handle !== handle || operation.snapshot.status === "ending")
|
|
68
|
+
return;
|
|
69
|
+
this.#update(operation, {
|
|
70
|
+
taskProgress: Object.freeze(progress),
|
|
71
|
+
hasStartedTool: operation.snapshot.hasStartedTool || progress.phase === "tool",
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
disposeBridge(handle) {
|
|
75
|
+
const operation = this.#operation;
|
|
76
|
+
if (operation?.handle === handle)
|
|
77
|
+
operation.snapshot = Object.freeze({ ...operation.snapshot, reason: "bridge_disposed" });
|
|
78
|
+
return handle.end();
|
|
79
|
+
}
|
|
80
|
+
requestCancel(id) {
|
|
81
|
+
const operation = this.#operation;
|
|
82
|
+
if (!operation || operation.snapshot.id !== id || !operation.onCancel
|
|
83
|
+
|| operation.snapshot.status === "ending" || operation.snapshot.status === "cancelling")
|
|
84
|
+
return;
|
|
85
|
+
const previous = operation.snapshot.status;
|
|
86
|
+
this.#update(operation, { status: "cancelling" });
|
|
87
|
+
try {
|
|
88
|
+
operation.onCancel();
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
if (this.#operation === operation && this.#snapshot.operation?.status === "cancelling") {
|
|
92
|
+
this.#update(operation, { status: previous });
|
|
93
|
+
}
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Admission is captured before execution and released exactly once, including queued aborts.
|
|
98
|
+
admit() {
|
|
99
|
+
const operation = this.#operation?.snapshot.status === "ending" ? undefined : this.#operation;
|
|
100
|
+
if (operation)
|
|
101
|
+
operation.pending += 1;
|
|
102
|
+
else
|
|
103
|
+
this.#unscoped += 1;
|
|
104
|
+
let released = false;
|
|
105
|
+
return {
|
|
106
|
+
...(operation ? { operationId: operation.snapshot.id } : {}),
|
|
107
|
+
release: () => {
|
|
108
|
+
if (released)
|
|
109
|
+
return;
|
|
110
|
+
released = true;
|
|
111
|
+
if (operation) {
|
|
112
|
+
operation.pending -= 1;
|
|
113
|
+
if (this.#finish(operation))
|
|
114
|
+
this.#publish();
|
|
115
|
+
}
|
|
116
|
+
else
|
|
117
|
+
this.#unscoped -= 1;
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
observe(event) {
|
|
122
|
+
if (event.status === "running") {
|
|
123
|
+
this.#executions.set(event.id, event);
|
|
124
|
+
const operation = this.#operation;
|
|
125
|
+
if (operation && event.operationId === operation.snapshot.id && !operation.snapshot.hasExecuted) {
|
|
126
|
+
operation.snapshot = Object.freeze({ ...operation.snapshot, hasExecuted: true });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
else
|
|
130
|
+
this.#executions.delete(event.id);
|
|
131
|
+
this.#publish();
|
|
132
|
+
}
|
|
133
|
+
#finish(operation) {
|
|
134
|
+
if (operation.pending || operation.snapshot.status !== "ending" || this.#operation !== operation)
|
|
135
|
+
return false;
|
|
136
|
+
this.#operation = undefined;
|
|
137
|
+
operation.resolveEnd();
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
#update(operation, patch) {
|
|
141
|
+
operation.snapshot = Object.freeze({ ...operation.snapshot, ...patch });
|
|
142
|
+
this.#publish();
|
|
143
|
+
}
|
|
144
|
+
#publish() {
|
|
145
|
+
this.#snapshot = Object.freeze({
|
|
146
|
+
operation: this.#operation?.snapshot ?? null,
|
|
147
|
+
executions: Object.freeze([...this.#executions.values()]),
|
|
148
|
+
});
|
|
149
|
+
for (const listener of this.#listeners)
|
|
150
|
+
listener();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=operation-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"operation-runtime.js","sourceRoot":"","sources":["../src/operation-runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AgentToolExecutionEvent } from \"./internal-types.js\";\nimport { AgentToolError } from \"./errors.js\";\nimport type { ToolOperation } from \"./runtime.js\";\n\nexport type TaskProgress = { readonly phase: \"processing\" } | { readonly phase: \"tool\"; readonly toolCallId: string; readonly progressLabel?: string };\n\nexport interface OperationSnapshot {\n readonly taskProgress?: TaskProgress;\n readonly id: string;\n readonly source: \"host\" | \"webmcp\";\n readonly status: \"running\" | \"waiting\" | \"cancelling\" | \"ending\";\n readonly hasExecuted: boolean;\n readonly hasStartedTool: boolean;\n readonly outcome?: \"completed\" | \"failed\" | \"cancelled\";\n readonly reason?: \"bridge_disposed\";\n readonly canCancel: boolean;\n}\n\nexport interface RuntimeActivitySnapshot {\n readonly operation: OperationSnapshot | null;\n readonly executions: readonly AgentToolExecutionEvent[];\n}\n\ntype Operation = {\n snapshot: OperationSnapshot;\n handle: ToolOperation;\n pending: number;\n onCancel?: () => void;\n removeAbort(): void;\n resolveEnd(): void;\n};\n\nexport class OperationTracker {\n #operation: Operation | undefined;\n #unscoped = 0;\n #executions = new Map<string, AgentToolExecutionEvent>();\n #listeners = new Set<() => void>();\n #snapshot: RuntimeActivitySnapshot = Object.freeze({ operation: null, executions: Object.freeze([]) });\n\n getSnapshot = (): RuntimeActivitySnapshot => this.#snapshot;\n subscribe = (listener: () => void): (() => void) => {\n this.#listeners.add(listener);\n return () => { this.#listeners.delete(listener); };\n };\n\n begin(options: {\n signal?: AbortSignal;\n onCancel?: () => void;\n source?: \"host\" | \"webmcp\";\n replaceWaiting?: ToolOperation;\n taskProgress?: TaskProgress;\n } = {}): ToolOperation {\n options.signal?.throwIfAborted();\n const previous = this.#operation;\n const replacing = previous && previous.handle === options.replaceWaiting\n && previous.snapshot.status === \"waiting\" && previous.pending === 0;\n if (this.#unscoped || (previous && !replacing)) {\n throw new AgentToolError(\"OPERATION_BUSY\", \"The Runtime already has an operation or outstanding tool calls.\");\n }\n if (replacing) {\n previous.removeAbort();\n previous.resolveEnd();\n }\n let resolveEnd!: () => void;\n const endPromise = new Promise<void>((resolve) => { resolveEnd = resolve; });\n const id = globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random()}`;\n const operation: Operation = {\n snapshot: Object.freeze({ id, source: options.source ?? \"host\", status: \"running\", hasExecuted: false, hasStartedTool: options.taskProgress?.phase === \"tool\", canCancel: Boolean(options.onCancel), ...(options.taskProgress ? { taskProgress: Object.freeze(options.taskProgress) } : {}) }),\n handle: undefined!, pending: 0, resolveEnd,\n ...(options.onCancel ? { onCancel: options.onCancel } : {}),\n removeAbort: () => options.signal?.removeEventListener(\"abort\", onAbort),\n };\n const onAbort = () => {\n if (this.#operation !== operation || operation.snapshot.status === \"ending\") return;\n this.#update(operation, { status: \"cancelling\" });\n };\n const handle: ToolOperation = Object.freeze({\n id,\n setWaitingForUser: (waiting: boolean) => {\n if (this.#operation !== operation || ![\"running\", \"waiting\"].includes(operation.snapshot.status)) return;\n const status = waiting ? \"waiting\" : \"running\";\n if (status !== operation.snapshot.status) this.#update(operation, { status });\n },\n end: (endOptions?: { outcome?: \"completed\" | \"failed\" | \"cancelled\" }) => {\n if (this.#operation !== operation || operation.snapshot.status === \"ending\") return endPromise;\n operation.removeAbort();\n operation.snapshot = Object.freeze({ ...operation.snapshot, status: \"ending\", outcome: endOptions?.outcome ?? \"completed\" });\n this.#publish();\n if (this.#finish(operation)) this.#publish();\n return endPromise;\n },\n });\n operation.handle = handle;\n this.#operation = operation;\n options.signal?.addEventListener(\"abort\", onAbort, { once: true });\n this.#publish();\n return handle;\n }\n\n setTaskProgress(handle: ToolOperation, progress: TaskProgress): void {\n const operation = this.#operation;\n if (operation?.handle !== handle || operation.snapshot.status === \"ending\") return;\n this.#update(operation, {\n taskProgress: Object.freeze(progress),\n hasStartedTool: operation.snapshot.hasStartedTool || progress.phase === \"tool\",\n });\n }\n\n disposeBridge(handle: ToolOperation): Promise<void> {\n const operation = this.#operation;\n if (operation?.handle === handle) operation.snapshot = Object.freeze({ ...operation.snapshot, reason: \"bridge_disposed\" });\n return handle.end();\n }\n\n requestCancel(id: string): void {\n const operation = this.#operation;\n if (!operation || operation.snapshot.id !== id || !operation.onCancel\n || operation.snapshot.status === \"ending\" || operation.snapshot.status === \"cancelling\") return;\n const previous = operation.snapshot.status;\n this.#update(operation, { status: \"cancelling\" });\n try {\n operation.onCancel();\n } catch (error) {\n if (this.#operation === operation && this.#snapshot.operation?.status === \"cancelling\") {\n this.#update(operation, { status: previous });\n }\n throw error;\n }\n }\n\n // Admission is captured before execution and released exactly once, including queued aborts.\n admit(): { operationId?: string; release(): void } {\n const operation = this.#operation?.snapshot.status === \"ending\" ? undefined : this.#operation;\n if (operation) operation.pending += 1;\n else this.#unscoped += 1;\n let released = false;\n return {\n ...(operation ? { operationId: operation.snapshot.id } : {}),\n release: () => {\n if (released) return;\n released = true;\n if (operation) {\n operation.pending -= 1;\n if (this.#finish(operation)) this.#publish();\n } else this.#unscoped -= 1;\n },\n };\n }\n\n observe(event: AgentToolExecutionEvent): void {\n if (event.status === \"running\") {\n this.#executions.set(event.id, event);\n const operation = this.#operation;\n if (operation && event.operationId === operation.snapshot.id && !operation.snapshot.hasExecuted) {\n operation.snapshot = Object.freeze({ ...operation.snapshot, hasExecuted: true });\n }\n } else this.#executions.delete(event.id);\n this.#publish();\n }\n\n #finish(operation: Operation): boolean {\n if (operation.pending || operation.snapshot.status !== \"ending\" || this.#operation !== operation) return false;\n this.#operation = undefined;\n operation.resolveEnd();\n return true;\n }\n\n #update(operation: Operation, patch: Partial<OperationSnapshot>): void {\n operation.snapshot = Object.freeze({ ...operation.snapshot, ...patch });\n this.#publish();\n }\n\n #publish(): void {\n this.#snapshot = Object.freeze({\n operation: this.#operation?.snapshot ?? null,\n executions: Object.freeze([...this.#executions.values()]),\n });\n for (const listener of this.#listeners) listener();\n }\n}\n"]}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { type ReactNode } from "react";
|
|
2
|
-
import type
|
|
2
|
+
import { type ToolRuntime } from "./runtime.js";
|
|
3
3
|
export interface AgentOperationSurfaceProps {
|
|
4
4
|
toolRuntime: ToolRuntime;
|
|
5
5
|
children: ReactNode;
|
|
6
6
|
}
|
|
7
|
-
export declare function AgentOperationSurface(
|
|
7
|
+
export declare function AgentOperationSurface(props: AgentOperationSurfaceProps): import("react").JSX.Element;
|
|
@@ -1,82 +1,127 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useCallback, useLayoutEffect, useRef, useState } from "react";
|
|
3
|
-
import {
|
|
2
|
+
import { useCallback, useLayoutEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
3
|
+
import { getInternalToolRuntime } from "./runtime.js";
|
|
4
|
+
import { isOperationSurfaceLocked } from "./operation-lock.js";
|
|
4
5
|
import { OperationOverlayView } from "./operation-view.js";
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
if (chat.toolRuntime !== toolRuntime)
|
|
8
|
-
throw new Error("AgentOperationSurface requires the NexfaceProvider Runtime.");
|
|
6
|
+
const surfaces = new WeakSet();
|
|
7
|
+
export function AgentOperationSurface(props) {
|
|
9
8
|
const element = useRef(null);
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const turn = chat.turnId;
|
|
15
|
-
const running = chat.running;
|
|
16
|
-
const waiting = chat.waiting && !running;
|
|
17
|
-
useLayoutEffect(() => {
|
|
18
|
-
if (chat.operationSurface.current)
|
|
19
|
-
throw new Error("Only one AgentOperationSurface is supported per NexfaceProvider.");
|
|
20
|
-
chat.operationSurface.current = owner.current;
|
|
21
|
-
return () => { chat.operationSurface.current = null; };
|
|
22
|
-
}, [chat.operationSurface]);
|
|
9
|
+
const initial = useRef(props.toolRuntime);
|
|
10
|
+
if (initial.current !== props.toolRuntime) {
|
|
11
|
+
throw new Error("AgentOperationSurface requires a stable Runtime. Remount it to change Runtime.");
|
|
12
|
+
}
|
|
23
13
|
useLayoutEffect(() => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
14
|
+
if (surfaces.has(props.toolRuntime))
|
|
15
|
+
throw new Error("Only one AgentOperationSurface is supported per Runtime.");
|
|
16
|
+
surfaces.add(props.toolRuntime);
|
|
17
|
+
return () => { surfaces.delete(props.toolRuntime); };
|
|
18
|
+
}, [props.toolRuntime]);
|
|
19
|
+
const blockUserInput = useCallback((event) => {
|
|
20
|
+
if (!element.current || !isOperationSurfaceLocked(element.current) || !event.nativeEvent.isTrusted)
|
|
21
|
+
return;
|
|
22
|
+
event.preventDefault();
|
|
23
|
+
event.stopPropagation();
|
|
24
|
+
}, []);
|
|
25
|
+
const blockPassiveUserInput = useCallback((event) => {
|
|
26
|
+
if (!element.current || !isOperationSurfaceLocked(element.current) || !event.nativeEvent.isTrusted)
|
|
32
27
|
return;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
28
|
+
// Stop React's capture dispatch, but let the native event reach our non-passive
|
|
29
|
+
// surface listener. React's passive root listener cannot prevent scrolling.
|
|
30
|
+
event.isPropagationStopped = () => true;
|
|
31
|
+
}, []);
|
|
32
|
+
return _jsxs(_Fragment, { children: [_jsx("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, children: props.children }), _jsx(OperationSurfaceContent, { ...props, element: element })] });
|
|
33
|
+
}
|
|
34
|
+
function OperationSurfaceContent({ toolRuntime, element }) {
|
|
35
|
+
const runtime = getInternalToolRuntime(toolRuntime);
|
|
36
|
+
const activity = useSyncExternalStore(runtime.subscribeActivity, runtime.getActivitySnapshot, runtime.getActivitySnapshot);
|
|
37
|
+
const cycle = activity.operation;
|
|
38
|
+
const [outcome, setOutcome] = useState();
|
|
39
|
+
useLayoutEffect(() => runtime.subscribeActivity(() => {
|
|
40
|
+
const current = runtime.getActivitySnapshot().operation;
|
|
41
|
+
// Task progress does not replace page feedback. Only lifecycle boundaries clear it.
|
|
42
|
+
setVisual((visual) => {
|
|
43
|
+
if (current)
|
|
44
|
+
return current.status === "running" && visual?.event.operationId === current.id ? visual : undefined;
|
|
45
|
+
return visual && runtime.getActivitySnapshot().executions.some((event) => event.id === visual.event.id) ? visual : undefined;
|
|
49
46
|
});
|
|
50
|
-
|
|
47
|
+
if (current?.status === "ending" && current.outcome)
|
|
48
|
+
setOutcome({ id: current.id, status: current.outcome });
|
|
49
|
+
}), [runtime]);
|
|
50
|
+
const [visual, setVisual] = useState();
|
|
51
|
+
const [history, setHistory] = useState({ scope: "", entries: [] });
|
|
52
|
+
useLayoutEffect(() => {
|
|
53
|
+
const onExecution = (event) => {
|
|
54
|
+
const scope = event.operationId ?? event.id;
|
|
55
|
+
const operation = runtime.getActivitySnapshot().operation;
|
|
56
|
+
const canDisplay = !event.operationId || (operation?.id === event.operationId && operation.status === "running");
|
|
57
|
+
if (canDisplay && event.status === "running") {
|
|
58
|
+
const target = event.tool.definition.effect === "navigate"
|
|
59
|
+
? element.current
|
|
60
|
+
: runtime.resolveTarget(event.tool, event.input);
|
|
61
|
+
if (target?.isConnected && element.current?.contains(target)) {
|
|
62
|
+
const rect = target.getBoundingClientRect();
|
|
63
|
+
setVisual({ event, target, position: { left: rect.left + rect.width / 2, top: rect.top + rect.height / 2 } });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
else if (canDisplay) {
|
|
67
|
+
setVisual((current) => current?.event.id === event.id && current.event.operationId === event.operationId
|
|
68
|
+
? { ...current, event: { ...current.event, ...event } }
|
|
69
|
+
: current);
|
|
70
|
+
}
|
|
71
|
+
setHistory((current) => {
|
|
72
|
+
if ((operation && event.operationId !== operation.id) || (event.status !== "running" && current.scope !== scope))
|
|
73
|
+
return current;
|
|
74
|
+
const entries = current.scope === scope ? current.entries : [];
|
|
75
|
+
const entry = {
|
|
76
|
+
toolCallId: event.id,
|
|
77
|
+
progressLabel: `${event.progressLabel || "正在处理你的请求"} · ${event.cancelled ? "已取消" : event.status === "running" ? "执行中" : event.status === "success" ? "已完成" : "失败"}`,
|
|
78
|
+
};
|
|
79
|
+
return { scope, entries: entries.some((item) => item.toolCallId === event.id)
|
|
80
|
+
? entries.map((item) => item.toolCallId === event.id ? entry : item)
|
|
81
|
+
: [...entries, entry] };
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
const unsubscribe = toolRuntime.on("toolexecution", onExecution);
|
|
85
|
+
for (const event of runtime.getActivitySnapshot().executions)
|
|
86
|
+
onExecution(event);
|
|
87
|
+
return unsubscribe;
|
|
88
|
+
}, [runtime, toolRuntime, element]);
|
|
89
|
+
const activated = Boolean(cycle && (cycle.hasStartedTool || cycle.source === "webmcp" || cycle.hasExecuted));
|
|
90
|
+
const executing = activity.executions.length > 0;
|
|
91
|
+
const waiting = cycle?.status === "waiting" && !executing;
|
|
92
|
+
const running = executing || (activated && cycle?.status !== "waiting");
|
|
93
|
+
const stopping = cycle?.status === "cancelling" || cycle?.status === "ending";
|
|
51
94
|
useLayoutEffect(() => {
|
|
52
|
-
if (!running)
|
|
95
|
+
if (!running || waiting)
|
|
53
96
|
setVisual(undefined);
|
|
54
|
-
}
|
|
55
97
|
}, [running, waiting]);
|
|
56
|
-
const
|
|
98
|
+
const currentVisual = !stopping && !waiting && visual && (cycle
|
|
99
|
+
? cycle.status === "running" && visual.event.operationId === cycle.id
|
|
100
|
+
: activity.executions.some((event) => event.id === visual.event.id)) ? visual : undefined;
|
|
101
|
+
const resolveSurface = useCallback(() => element.current ?? undefined, [element]);
|
|
57
102
|
const resolveTarget = useCallback(() => {
|
|
58
|
-
if (!
|
|
103
|
+
if (!currentVisual)
|
|
59
104
|
return undefined;
|
|
60
|
-
const target =
|
|
61
|
-
if (target
|
|
105
|
+
const target = currentVisual.target;
|
|
106
|
+
if (target.isConnected && element.current?.contains(target)
|
|
107
|
+
&& (currentVisual.event.tool.definition.effect === "navigate" || toolRuntime.getTools().includes(currentVisual.event.tool)))
|
|
62
108
|
return { element: target };
|
|
63
|
-
return
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
progressLabel: visual.event.progressLabel || "正在处理你的请求",
|
|
73
|
-
status: visual.event.status === "running" ? "running" : "completed",
|
|
109
|
+
return undefined;
|
|
110
|
+
}, [currentVisual, element, toolRuntime]);
|
|
111
|
+
const operation = currentVisual ? {
|
|
112
|
+
toolCallId: currentVisual.event.id,
|
|
113
|
+
name: currentVisual.event.name,
|
|
114
|
+
input: currentVisual.event.input,
|
|
115
|
+
effect: currentVisual.event.tool.definition.effect,
|
|
116
|
+
progressLabel: currentVisual.event.progressLabel || "正在处理你的请求",
|
|
117
|
+
status: currentVisual.event.status === "running" ? "running" : currentVisual.event.status === "success" ? "completed" : "failed",
|
|
74
118
|
} : {
|
|
75
|
-
toolCallId:
|
|
119
|
+
toolCallId: cycle?.taskProgress?.phase === "tool" ? cycle.taskProgress.toolCallId : cycle?.id ?? history.scope,
|
|
76
120
|
name: "agent",
|
|
77
|
-
progressLabel: waiting ? "等待你完成操作" :
|
|
78
|
-
status:
|
|
121
|
+
progressLabel: waiting ? "等待你完成操作" : cycle?.status === "cancelling" ? "正在终止" : cycle?.taskProgress?.phase === "tool" ? cycle.taskProgress.progressLabel || "正在执行工具" : cycle?.taskProgress ? "正在处理" : "正在处理你的请求",
|
|
122
|
+
status: running || waiting ? "running" : outcome?.id === history.scope ? outcome.status : "completed",
|
|
79
123
|
};
|
|
80
|
-
|
|
124
|
+
const entries = !cycle || history.scope === cycle.id ? history.entries : [];
|
|
125
|
+
return activated || executing || entries.length ? _jsx(OperationOverlayView, { operation: operation, progressHistory: entries, running: running, feedbackActive: Boolean(currentVisual) && running && !waiting && !stopping, humanHasControl: waiting, ...(cycle?.canCancel ? { onCancel: () => runtime.requestOperationCancel(cycle.id) } : {}), cancelling: cycle?.status === "cancelling" || cycle?.status === "ending", ...(currentVisual ? { fallbackPosition: currentVisual.position } : {}), resolveSurface: resolveSurface, resolveTarget: resolveTarget }) : null;
|
|
81
126
|
}
|
|
82
127
|
//# sourceMappingURL=operation-surface.js.map
|