unity-open-mcp 1.4.0 → 1.6.0
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/capabilities/cost-hints.js +73 -0
- package/dist/capabilities/cost-hints.js.map +1 -1
- package/dist/capabilities/intent-groups.js +76 -4
- package/dist/capabilities/intent-groups.js.map +1 -1
- package/dist/capabilities/lifecycle.js +9 -0
- package/dist/capabilities/lifecycle.js.map +1 -1
- package/dist/capabilities/tool-groups.js +75 -17
- package/dist/capabilities/tool-groups.js.map +1 -1
- package/dist/compressible-router.d.ts +4 -1
- package/dist/compressible-router.js +27 -11
- package/dist/compressible-router.js.map +1 -1
- package/dist/constants.d.ts +2 -2
- package/dist/constants.js +2 -2
- package/dist/event-stream.d.ts +10 -0
- package/dist/event-stream.js +29 -0
- package/dist/event-stream.js.map +1 -1
- package/dist/game-view-settle.d.ts +18 -0
- package/dist/game-view-settle.js +81 -0
- package/dist/game-view-settle.js.map +1 -0
- package/dist/jobs/adapters.d.ts +59 -0
- package/dist/jobs/adapters.js +226 -0
- package/dist/jobs/adapters.js.map +1 -1
- package/dist/live-client.d.ts +17 -60
- package/dist/live-client.js +52 -87
- package/dist/live-client.js.map +1 -1
- package/dist/local-tools.js +3 -0
- package/dist/local-tools.js.map +1 -1
- package/dist/offline/api.d.ts +2 -0
- package/dist/offline/api.js +4 -2
- package/dist/offline/api.js.map +1 -1
- package/dist/offline/model.js +1 -1
- package/dist/offline/model.js.map +1 -1
- package/dist/offline/parse.d.ts +1 -1
- package/dist/offline/parse.js +20 -8
- package/dist/offline/parse.js.map +1 -1
- package/dist/playmode-routes.d.ts +17 -0
- package/dist/playmode-routes.js +50 -0
- package/dist/playmode-routes.js.map +1 -0
- package/dist/playtest/png.d.ts +27 -0
- package/dist/playtest/png.js +248 -0
- package/dist/playtest/png.js.map +1 -0
- package/dist/playtest/recorder.d.ts +203 -0
- package/dist/playtest/recorder.js +945 -0
- package/dist/playtest/recorder.js.map +1 -0
- package/dist/playtest/redact.d.ts +16 -0
- package/dist/playtest/redact.js +124 -0
- package/dist/playtest/redact.js.map +1 -0
- package/dist/playtest/report.d.ts +73 -0
- package/dist/playtest/report.js +477 -0
- package/dist/playtest/report.js.map +1 -0
- package/dist/playtest/session-store.d.ts +78 -0
- package/dist/playtest/session-store.js +224 -0
- package/dist/playtest/session-store.js.map +1 -0
- package/dist/skill/SKILL.md +22 -3
- package/dist/skill/references/senses-and-tests.md +3 -0
- package/dist/skill/references/yaml-and-offline-work.md +1 -1
- package/dist/tool-router.d.ts +44 -0
- package/dist/tool-router.js +249 -7
- package/dist/tool-router.js.map +1 -1
- package/dist/tools/batch-execute.js +5 -1
- package/dist/tools/batch-execute.js.map +1 -1
- package/dist/tools/capture-inline.js +30 -12
- package/dist/tools/capture-inline.js.map +1 -1
- package/dist/tools/capture-series.d.ts +46 -0
- package/dist/tools/capture-series.js +116 -0
- package/dist/tools/capture-series.js.map +1 -0
- package/dist/tools/game-view.d.ts +44 -0
- package/dist/tools/game-view.js +103 -0
- package/dist/tools/game-view.js.map +1 -0
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.js +34 -7
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/inputsim-gesture.d.ts +46 -0
- package/dist/tools/inputsim-gesture.js +123 -0
- package/dist/tools/inputsim-gesture.js.map +1 -0
- package/dist/tools/inputsim-pointer.js +35 -2
- package/dist/tools/inputsim-pointer.js.map +1 -1
- package/dist/tools/inputsim-touch.js +42 -22
- package/dist/tools/inputsim-touch.js.map +1 -1
- package/dist/tools/jobs.js +1 -1
- package/dist/tools/jobs.js.map +1 -1
- package/dist/tools/player-state.d.ts +42 -0
- package/dist/tools/player-state.js +142 -0
- package/dist/tools/player-state.js.map +1 -0
- package/dist/tools/playtest-conditions.d.ts +334 -0
- package/dist/tools/playtest-conditions.js +99 -0
- package/dist/tools/playtest-conditions.js.map +1 -0
- package/dist/tools/playtest-session.d.ts +45 -0
- package/dist/tools/playtest-session.js +105 -0
- package/dist/tools/playtest-session.js.map +1 -0
- package/dist/tools/playtest-wait.d.ts +46 -0
- package/dist/tools/playtest-wait.js +86 -0
- package/dist/tools/playtest-wait.js.map +1 -0
- package/dist/tools/screenshot.js +31 -8
- package/dist/tools/screenshot.js.map +1 -1
- package/dist/tools/text-target.d.ts +22 -0
- package/dist/tools/text-target.js +39 -0
- package/dist/tools/text-target.js.map +1 -0
- package/dist/tools/ui-snapshot.d.ts +41 -0
- package/dist/tools/ui-snapshot.js +87 -0
- package/dist/tools/ui-snapshot.js.map +1 -0
- package/package.json +1 -1
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Playtest — wait in play mode until a condition holds.
|
|
2
|
+
//
|
|
3
|
+
// The bridge parses the condition and starts the wait on its shared play-mode
|
|
4
|
+
// scheduler (a player-loop tick at the start of EarlyUpdate), returning an
|
|
5
|
+
// operation id at once; this server polls POST /playmode-operations to the
|
|
6
|
+
// terminal result (tool-router `routePlaymodeOperation`). A wait whose timeout
|
|
7
|
+
// is beyond the inline budget runs as a `jobs` play-mode operation and the call
|
|
8
|
+
// returns its job id. `timeout_ms` is the wait's own limit: the live client
|
|
9
|
+
// forwards it unclamped for this tool (TOOL_OWNED_TIMEOUT_TOOLS); the bridge
|
|
10
|
+
// reads it only as the tool parameter and dispatches the start call on its
|
|
11
|
+
// default budget.
|
|
12
|
+
import { makeTool } from "./schema-fragments.js";
|
|
13
|
+
import { conditionSchema } from "./playtest-conditions.js";
|
|
14
|
+
/** Longest wait (timeout_ms) the direct call polls inline; longer ones become a job. */
|
|
15
|
+
export const PLAYTEST_WAIT_INLINE_LIMIT_MS = 40_000;
|
|
16
|
+
export const PLAYTEST_WAIT_DEFAULT_TIMEOUT_MS = 30_000;
|
|
17
|
+
/** Mirrors the bridge's PlaytestWaitTools.MaxTimeoutMs. */
|
|
18
|
+
export const PLAYTEST_WAIT_MAX_TIMEOUT_MS = 600_000;
|
|
19
|
+
export const PLAYTEST_WAIT_MAX_POLL_EVERY_FRAMES = 600;
|
|
20
|
+
export const playtestWait = makeTool("unity_open_mcp_playtest_wait", "Wait in play mode until a condition holds — instead of polling in a loop of calls. CONDITION " +
|
|
21
|
+
"(exactly one key): log {pattern (regex), level} = a console message logged after the start matches; " +
|
|
22
|
+
"ui {text_contains, path, visible, matches} = a ui_snapshot query matches (matches:false = stops " +
|
|
23
|
+
"matching, e.g. a popup closed); object {path | object_id, state: exists | active | inactive | " +
|
|
24
|
+
"destroyed}; project_command {id, args, field} = the project's own read-only predicate returns true " +
|
|
25
|
+
"(no arbitrary C#); frames N; ms N; all / any [leaves] (one level). The bridge evaluates it inside " +
|
|
26
|
+
"the player loop every poll_every_frames frames and the call returns the terminal result: `fired` " +
|
|
27
|
+
"(which condition — e.g. condition.any[1] — at which frame, observed_frame, play_time, elapsed_ms), " +
|
|
28
|
+
"every leaf's last observed state, frames, and console errors logged meanwhile. timeout_ms is " +
|
|
29
|
+
"wall-clock: on expiry the call fails with timed_out plus the last observed state. time_scale " +
|
|
30
|
+
"applies Time.timeScale for the wait only and is restored on every exit (success, timeout, cancel, " +
|
|
31
|
+
"exit play, domain reload) unless game code changed it meanwhile (reported). fail_on [\"exception\"] " +
|
|
32
|
+
"stops at the first exception the game logs (exception_thrown, with message and stack); \"error\" " +
|
|
33
|
+
"also stops on error logs. ms conditions count `time` (gameplay = scaled by Time.timeScale, default; " +
|
|
34
|
+
"realtime). Waits with timeout_ms over " +
|
|
35
|
+
`${PLAYTEST_WAIT_INLINE_LIMIT_MS / 1000}s return a job_id instead — wait on it with unity_open_mcp_jobs ` +
|
|
36
|
+
"(cancel there; exiting play mode / a reload end it, time scale restored). Play-mode only; gate-free; " +
|
|
37
|
+
"one time-scaled wait at a time (playmode_operation_busy). Not allowed inside batch_execute.", {
|
|
38
|
+
required: ["condition"],
|
|
39
|
+
properties: {
|
|
40
|
+
condition: conditionSchema("What to wait for: one key — log / ui / object / project_command / frames / ms — or all / any over " +
|
|
41
|
+
"those, e.g. {\"log\":{\"pattern\":\"Wave \\\\d+ cleared\"}}, {\"ui\":{\"text_contains\":\"Victory\"}}, " +
|
|
42
|
+
"{\"object\":{\"path\":\"Enemies/Boss\",\"state\":\"destroyed\"}}, " +
|
|
43
|
+
"{\"any\":[{\"ms\":5000},{\"log\":{\"pattern\":\"Game over\"}}]}."),
|
|
44
|
+
timeout_ms: {
|
|
45
|
+
type: "integer",
|
|
46
|
+
minimum: 1,
|
|
47
|
+
maximum: PLAYTEST_WAIT_MAX_TIMEOUT_MS,
|
|
48
|
+
default: PLAYTEST_WAIT_DEFAULT_TIMEOUT_MS,
|
|
49
|
+
description: "Wall-clock limit for the wait (ms). On expiry → timed_out with the last observed state. Over " +
|
|
50
|
+
`${PLAYTEST_WAIT_INLINE_LIMIT_MS} ms the call runs as a job.`,
|
|
51
|
+
},
|
|
52
|
+
poll_every_frames: {
|
|
53
|
+
type: "integer",
|
|
54
|
+
minimum: 1,
|
|
55
|
+
maximum: PLAYTEST_WAIT_MAX_POLL_EVERY_FRAMES,
|
|
56
|
+
default: 1,
|
|
57
|
+
description: "Evaluate the condition every N frames (logs and exceptions are captured every frame regardless). " +
|
|
58
|
+
"Raise it for expensive ui / project_command conditions; fired.frame then lies within the last N frames.",
|
|
59
|
+
},
|
|
60
|
+
time_scale: {
|
|
61
|
+
type: "number",
|
|
62
|
+
exclusiveMinimum: 0,
|
|
63
|
+
maximum: 100,
|
|
64
|
+
description: "Time.timeScale during the wait only (e.g. 4 finishes a 5 s game timer in ~1.25 s); restored on every exit path.",
|
|
65
|
+
},
|
|
66
|
+
time: {
|
|
67
|
+
type: "string",
|
|
68
|
+
enum: ["gameplay", "realtime"],
|
|
69
|
+
default: "gameplay",
|
|
70
|
+
description: "Clock for ms conditions and elapsed_ms: gameplay (scaled by Time.timeScale; a paused game never advances) or realtime (wall clock).",
|
|
71
|
+
},
|
|
72
|
+
fail_on: {
|
|
73
|
+
type: "array",
|
|
74
|
+
items: { type: "string", enum: ["exception", "error"] },
|
|
75
|
+
maxItems: 2,
|
|
76
|
+
description: "Stop early: exception = the game logs an exception (exception_thrown, with stack); error = any error log (error_logged).",
|
|
77
|
+
},
|
|
78
|
+
operation_id: {
|
|
79
|
+
type: "string",
|
|
80
|
+
pattern: "^[A-Za-z0-9-]{8,64}$",
|
|
81
|
+
description: "Optional idempotency handle for the bridge operation (8-64 letters, digits, dashes); a retried call with " +
|
|
82
|
+
"the same id returns the same operation. Omit it normally; jobs always set it to the job id.",
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
//# sourceMappingURL=playtest-wait.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"playtest-wait.js","sourceRoot":"","sources":["../../src/tools/playtest-wait.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,EAAE;AACF,8EAA8E;AAC9E,2EAA2E;AAC3E,2EAA2E;AAC3E,+EAA+E;AAC/E,gFAAgF;AAChF,4EAA4E;AAC5E,6EAA6E;AAC7E,2EAA2E;AAC3E,kBAAkB;AAClB,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE3D,wFAAwF;AACxF,MAAM,CAAC,MAAM,6BAA6B,GAAG,MAAM,CAAC;AACpD,MAAM,CAAC,MAAM,gCAAgC,GAAG,MAAM,CAAC;AACvD,2DAA2D;AAC3D,MAAM,CAAC,MAAM,4BAA4B,GAAG,OAAO,CAAC;AACpD,MAAM,CAAC,MAAM,mCAAmC,GAAG,GAAG,CAAC;AAEvD,MAAM,CAAC,MAAM,YAAY,GAAG,QAAQ,CAClC,8BAA8B,EAC9B,+FAA+F;IAC7F,sGAAsG;IACtG,kGAAkG;IAClG,gGAAgG;IAChG,qGAAqG;IACrG,oGAAoG;IACpG,mGAAmG;IACnG,qGAAqG;IACrG,+FAA+F;IAC/F,+FAA+F;IAC/F,oGAAoG;IACpG,sGAAsG;IACtG,mGAAmG;IACnG,sGAAsG;IACtG,wCAAwC;IACxC,GAAG,6BAA6B,GAAG,IAAI,kEAAkE;IACzG,uGAAuG;IACvG,6FAA6F,EAC/F;IACE,QAAQ,EAAE,CAAC,WAAW,CAAC;IACvB,UAAU,EAAE;QACV,SAAS,EAAE,eAAe,CACxB,oGAAoG;YAClG,yGAAyG;YACzG,oEAAoE;YACpE,kEAAkE,CACrE;QACD,UAAU,EAAE;YACV,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,4BAA4B;YACrC,OAAO,EAAE,gCAAgC;YACzC,WAAW,EACT,+FAA+F;gBAC/F,GAAG,6BAA6B,6BAA6B;SAChE;QACD,iBAAiB,EAAE;YACjB,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,mCAAmC;YAC5C,OAAO,EAAE,CAAC;YACV,WAAW,EACT,mGAAmG;gBACnG,yGAAyG;SAC5G;QACD,UAAU,EAAE;YACV,IAAI,EAAE,QAAQ;YACd,gBAAgB,EAAE,CAAC;YACnB,OAAO,EAAE,GAAG;YACZ,WAAW,EAAE,iHAAiH;SAC/H;QACD,IAAI,EAAE;YACJ,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC;YAC9B,OAAO,EAAE,UAAU;YACnB,WAAW,EAAE,qIAAqI;SACnJ;QACD,OAAO,EAAE;YACP,IAAI,EAAE,OAAO;YACb,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,OAAO,CAAC,EAAE;YACvD,QAAQ,EAAE,CAAC;YACX,WAAW,EAAE,0HAA0H;SACxI;QACD,YAAY,EAAE;YACZ,IAAI,EAAE,QAAQ;YACd,OAAO,EAAE,sBAAsB;YAC/B,WAAW,EACT,2GAA2G;gBAC3G,6FAA6F;SAChG;KACF;CACF,CACF,CAAC"}
|
package/dist/tools/screenshot.js
CHANGED
|
@@ -6,7 +6,13 @@ export const screenshot = makeTool("unity_senses_screenshot", "Capture a screens
|
|
|
6
6
|
"depth plus Screen-Space Overlay canvases; use this to verify UI edits), and " +
|
|
7
7
|
"'isolated' (a clean 2x2 composite — Front/Right/Back/Top — of a single " +
|
|
8
8
|
"GameObject with layer culling and background choice). " +
|
|
9
|
-
"
|
|
9
|
+
"'composed' without width/height returns the frame at the game's own resolution (the Game view " +
|
|
10
|
+
"size or the Device Simulator screen — set it first with unity_open_mcp_game_view); in play mode it " +
|
|
11
|
+
"is the frame the game rendered, without Editor chrome or the Simulator's device bezel. A " +
|
|
12
|
+
"width/height of another aspect is padded (letterbox), never stretched, and reported as " +
|
|
13
|
+
"scaled_from + content_rect. Game/composed responses name what they were taken at in `capturedAt` " +
|
|
14
|
+
"(mode, device or size entry, orientation, screen). Returns the saved PNG file path. Requires a " +
|
|
15
|
+
"live Unity Editor connection.", {
|
|
10
16
|
properties: {
|
|
11
17
|
view: {
|
|
12
18
|
type: "string",
|
|
@@ -15,29 +21,46 @@ export const screenshot = makeTool("unity_senses_screenshot", "Capture a screens
|
|
|
15
21
|
description: "Capture target. 'scene' = Scene view camera, 'game' = Camera.main only " +
|
|
16
22
|
"(Overlay canvases and other cameras are NOT rendered — prefer 'composed' " +
|
|
17
23
|
"to verify UI), 'composed' = the player's full frame (all cameras by depth + " +
|
|
18
|
-
"Screen-Space Overlay UI; in play mode
|
|
19
|
-
"
|
|
20
|
-
"one GameObject.",
|
|
24
|
+
"Screen-Space Overlay UI; in play mode the play-mode view's rendered frame, in edit " +
|
|
25
|
+
"mode enabled cameras composited by depth — Overlay canvases do not render in edit " +
|
|
26
|
+
"mode), 'isolated' = clean 2x2 composite of one GameObject.",
|
|
21
27
|
},
|
|
22
28
|
width: {
|
|
23
29
|
type: "integer",
|
|
24
|
-
default: 1280,
|
|
25
30
|
minimum: 64,
|
|
26
31
|
maximum: 4096,
|
|
27
|
-
description: "Capture width in pixels
|
|
32
|
+
description: "Capture width in pixels (with height for 'composed'). Omitted: 1280 for scene/game/isolated; 'composed' " +
|
|
33
|
+
"returns the game's native resolution. For 'isolated', per quadrant (full image is 2x). For " +
|
|
34
|
+
"'composed', a width/height of another aspect than the game is fitted and padded (letterbox), " +
|
|
35
|
+
"never stretched — the response then carries scaled_from [w, h] and content_rect [x, y, w, h] " +
|
|
36
|
+
"(top-left origin).",
|
|
28
37
|
},
|
|
29
38
|
height: {
|
|
30
39
|
type: "integer",
|
|
31
|
-
default: 720,
|
|
32
40
|
minimum: 64,
|
|
33
41
|
maximum: 4096,
|
|
34
|
-
description: "Capture height in pixels
|
|
42
|
+
description: "Capture height in pixels (with width for 'composed'). Omitted: 720 for scene/game/isolated; native for " +
|
|
43
|
+
"'composed'. For 'isolated', per quadrant (full image is 2x).",
|
|
35
44
|
},
|
|
36
45
|
object_path: {
|
|
37
46
|
type: "string",
|
|
38
47
|
description: "Required for 'isolated' view. Hierarchy path of the target GameObject " +
|
|
39
48
|
"(e.g. \"Player\" or \"Enemies/Goblin\").",
|
|
40
49
|
},
|
|
50
|
+
size: {
|
|
51
|
+
type: "string",
|
|
52
|
+
enum: ["native"],
|
|
53
|
+
description: "'native' = the game's own resolution (the Game view size or the simulated device screen), " +
|
|
54
|
+
"no scaling. Implied for 'composed' when width/height are omitted; also valid for 'game' " +
|
|
55
|
+
"(Camera.main rendered at that size). Do not combine with width/height.",
|
|
56
|
+
},
|
|
57
|
+
overlay: {
|
|
58
|
+
type: "string",
|
|
59
|
+
enum: ["safe_area"],
|
|
60
|
+
description: "[composed] 'safe_area' tints the unsafe band (outside Screen.safeArea: notch, home indicator, " +
|
|
61
|
+
"cutouts) and outlines the safe rect on the returned image, for review. The response repeats " +
|
|
62
|
+
"safe_area [x, y, w, h] in screen pixels (bottom-left origin).",
|
|
63
|
+
},
|
|
41
64
|
background: {
|
|
42
65
|
type: "string",
|
|
43
66
|
enum: ["transparent", "solid", "skybox"],
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../src/tools/screenshot.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"screenshot.js","sourceRoot":"","sources":["../../src/tools/screenshot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAChC,yBAAyB,EACzB,mEAAmE;IACjE,4EAA4E;IAC5E,2EAA2E;IAC3E,6EAA6E;IAC7E,8EAA8E;IAC9E,yEAAyE;IACzE,wDAAwD;IACxD,gGAAgG;IAChG,qGAAqG;IACrG,2FAA2F;IAC3F,yFAAyF;IACzF,mGAAmG;IACnG,iGAAiG;IACjG,+BAA+B,EACjC;IACE,UAAU,EAAE;QACN,IAAI,EAAE;YACJ,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,CAAC;YAC/C,OAAO,EAAE,OAAO;YAChB,WAAW,EACT,yEAAyE;gBACzE,2EAA2E;gBAC3E,8EAA8E;gBAC9E,qFAAqF;gBACrF,oFAAoF;gBACpF,4DAA4D;SAC/D;QACD,KAAK,EAAE;YACL,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,EAAE;YACX,OAAO,EAAE,IAAI;YACb,WAAW,EACT,0GAA0G;gBAC1G,6FAA6F;gBAC7F,+FAA+F;gBAC/F,+FAA+F;gBAC/F,oBAAoB;SACvB;QACD,MAAM,EAAE;YACN,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,EAAE;YACX,OAAO,EAAE,IAAI;YACb,WAAW,EACT,yGAAyG;gBACzG,8DAA8D;SACjE;QACD,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,WAAW,EACT,wEAAwE;gBACxE,0CAA0C;SAC7C;QACD,IAAI,EAAE;YACJ,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,QAAQ,CAAC;YAChB,WAAW,EACT,4FAA4F;gBAC5F,0FAA0F;gBAC1F,wEAAwE;SAC3E;QACD,OAAO,EAAE;YACP,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,WAAW,CAAC;YACnB,WAAW,EACT,gGAAgG;gBAChG,8FAA8F;gBAC9F,+DAA+D;SAClE;QACD,UAAU,EAAE;YACV,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,aAAa,EAAE,OAAO,EAAE,QAAQ,CAAC;YACxC,OAAO,EAAE,QAAQ;YACjB,WAAW,EAAE,kEAAkE;SAChF;KACF;CACN,CACF,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare const TEXT_MATCH_MODES: readonly ["exact", "contains", "regex"];
|
|
2
|
+
export declare function textTargetProps(subject: string): {
|
|
3
|
+
readonly target_text: {
|
|
4
|
+
readonly type: "string";
|
|
5
|
+
readonly minLength: 1;
|
|
6
|
+
readonly description: string;
|
|
7
|
+
};
|
|
8
|
+
readonly under: {
|
|
9
|
+
readonly type: "string";
|
|
10
|
+
readonly description: "target_text only: search the subtree of this hierarchy path or trailing path suffix ('Shop').";
|
|
11
|
+
};
|
|
12
|
+
readonly index: {
|
|
13
|
+
readonly type: "integer";
|
|
14
|
+
readonly minimum: 0;
|
|
15
|
+
readonly description: "target_text only: pick the n-th visible match (hierarchy order, 0-based; see matches[].index).";
|
|
16
|
+
};
|
|
17
|
+
readonly text_match: {
|
|
18
|
+
readonly type: "string";
|
|
19
|
+
readonly enum: readonly ["exact", "contains", "regex"];
|
|
20
|
+
readonly description: string;
|
|
21
|
+
};
|
|
22
|
+
};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Label targeting (`target_text`) shared by inputsim_pointer, inputsim_touch
|
|
2
|
+
// and inputsim_gesture keyframes. The bridge resolves it over the same uGUI
|
|
3
|
+
// screen model ui_snapshot reports: the VISIBLE element whose text matches —
|
|
4
|
+
// a control matches on its label (descendant texts) — tapped at the centre of
|
|
5
|
+
// its visible rect. Several visible matches refuse with ambiguous_target
|
|
6
|
+
// (candidates + matches[] with index/path/text/rect) unless `index` picks one;
|
|
7
|
+
// none visible refuses with target_not_found (hidden_matches lists invisible
|
|
8
|
+
// ones). No schema default on text_match: an omitted value means exact.
|
|
9
|
+
export const TEXT_MATCH_MODES = ["exact", "contains", "regex"];
|
|
10
|
+
export function textTargetProps(subject) {
|
|
11
|
+
return {
|
|
12
|
+
target_text: {
|
|
13
|
+
type: "string",
|
|
14
|
+
minLength: 1,
|
|
15
|
+
description: `${subject} by its on-screen text instead of a name: the visible uGUI element (UI.Text / ` +
|
|
16
|
+
"TextMeshPro) whose text matches; a button matches on its label. Default match is exact after " +
|
|
17
|
+
"normalisation (trim, whitespace collapse, rich-text tags removed, case-insensitive). Ambiguous " +
|
|
18
|
+
"→ ambiguous_target with matches[]; pick with `index` or narrow with `under`. The result " +
|
|
19
|
+
"reports the resolution (`textTarget`; gestures list `textTargets`). Requires com.unity.ugui.",
|
|
20
|
+
},
|
|
21
|
+
under: {
|
|
22
|
+
type: "string",
|
|
23
|
+
description: "target_text only: search the subtree of this hierarchy path or trailing path suffix ('Shop').",
|
|
24
|
+
},
|
|
25
|
+
index: {
|
|
26
|
+
type: "integer",
|
|
27
|
+
minimum: 0,
|
|
28
|
+
description: "target_text only: pick the n-th visible match (hierarchy order, 0-based; see matches[].index).",
|
|
29
|
+
},
|
|
30
|
+
text_match: {
|
|
31
|
+
type: "string",
|
|
32
|
+
enum: [...TEXT_MATCH_MODES],
|
|
33
|
+
description: "target_text only: exact (default when omitted) | contains (case-insensitive substring) | " +
|
|
34
|
+
"regex (.NET, case-insensitive, matched against the normalised text; over 100 ms on a text or " +
|
|
35
|
+
"2 s for the screen → regex_timeout).",
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
//# sourceMappingURL=text-target.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"text-target.js","sourceRoot":"","sources":["../../src/tools/text-target.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,4EAA4E;AAC5E,6EAA6E;AAC7E,8EAA8E;AAC9E,yEAAyE;AACzE,+EAA+E;AAC/E,6EAA6E;AAC7E,wEAAwE;AAExE,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,OAAO,EAAE,UAAU,EAAE,OAAO,CAAU,CAAC;AAExE,MAAM,UAAU,eAAe,CAAC,OAAe;IAC7C,OAAO;QACL,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,SAAS,EAAE,CAAC;YACZ,WAAW,EACT,GAAG,OAAO,gFAAgF;gBAC1F,+FAA+F;gBAC/F,iGAAiG;gBACjG,0FAA0F;gBAC1F,8FAA8F;SACjG;QACD,KAAK,EAAE;YACL,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,+FAA+F;SAC7G;QACD,KAAK,EAAE;YACL,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,CAAC;YACV,WAAW,EAAE,gGAAgG;SAC9G;QACD,UAAU,EAAE;YACV,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,GAAG,gBAAgB,CAAC;YAC3B,WAAW,EACT,2FAA2F;gBAC3F,+FAA+F;gBAC/F,sCAAsC;SACzC;KACO,CAAC;AACb,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export declare const UI_SNAPSHOT_ROLES: readonly ["button", "toggle", "slider", "input", "scroll", "text", "image", "container"];
|
|
2
|
+
export declare const uiSnapshot: {
|
|
3
|
+
inputSchema: {
|
|
4
|
+
[x: string]: unknown;
|
|
5
|
+
type: "object";
|
|
6
|
+
properties?: {
|
|
7
|
+
[x: string]: object;
|
|
8
|
+
} | undefined;
|
|
9
|
+
required?: string[] | undefined;
|
|
10
|
+
};
|
|
11
|
+
name: string;
|
|
12
|
+
description?: string | undefined;
|
|
13
|
+
outputSchema?: {
|
|
14
|
+
[x: string]: unknown;
|
|
15
|
+
type: "object";
|
|
16
|
+
properties?: {
|
|
17
|
+
[x: string]: object;
|
|
18
|
+
} | undefined;
|
|
19
|
+
required?: string[] | undefined;
|
|
20
|
+
} | undefined;
|
|
21
|
+
annotations?: {
|
|
22
|
+
title?: string | undefined;
|
|
23
|
+
readOnlyHint?: boolean | undefined;
|
|
24
|
+
destructiveHint?: boolean | undefined;
|
|
25
|
+
idempotentHint?: boolean | undefined;
|
|
26
|
+
openWorldHint?: boolean | undefined;
|
|
27
|
+
} | undefined;
|
|
28
|
+
execution?: {
|
|
29
|
+
taskSupport?: "optional" | "required" | "forbidden" | undefined;
|
|
30
|
+
} | undefined;
|
|
31
|
+
_meta?: {
|
|
32
|
+
[x: string]: unknown;
|
|
33
|
+
} | undefined;
|
|
34
|
+
icons?: {
|
|
35
|
+
src: string;
|
|
36
|
+
mimeType?: string | undefined;
|
|
37
|
+
sizes?: string[] | undefined;
|
|
38
|
+
theme?: "light" | "dark" | undefined;
|
|
39
|
+
}[] | undefined;
|
|
40
|
+
title?: string | undefined;
|
|
41
|
+
};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
// Playtest — UI screen snapshot.
|
|
2
|
+
//
|
|
3
|
+
// A read-only, gate-free listing of the uGUI on screen shaped like an
|
|
4
|
+
// accessibility tree: role, text (UnityEngine.UI.Text, plus TextMeshPro when
|
|
5
|
+
// it is installed), screen rect, visibility (on_screen | clipped | offscreen |
|
|
6
|
+
// transparent | zero_size), interactable, occluded_by, sprite, depth — and a
|
|
7
|
+
// diagnostics block (text overflow and zero-size controls always; a
|
|
8
|
+
// placeholder pattern and a minimum touch size only when passed). The bridge
|
|
9
|
+
// shares the node model with `target_text` label targeting on inputsim_pointer
|
|
10
|
+
// / inputsim_touch / inputsim_gesture, so the label the snapshot shows is the
|
|
11
|
+
// label a tap resolves. Bridge-side paging (page_size + cursor), like
|
|
12
|
+
// inputsim_probe. Compiles with com.unity.ugui (the uGUI half of input
|
|
13
|
+
// simulation); group `playtest`.
|
|
14
|
+
import { makeTool } from "./schema-fragments.js";
|
|
15
|
+
export const UI_SNAPSHOT_ROLES = ["button", "toggle", "slider", "input", "scroll", "text", "image", "container"];
|
|
16
|
+
export const uiSnapshot = makeTool("unity_open_mcp_ui_snapshot", "Read what the uGUI screen shows, in edit or play mode: a flat accessibility tree of nodes " +
|
|
17
|
+
"{ id, path, role (button | toggle | slider | input | scroll | text | image | container), text, " +
|
|
18
|
+
"rect [x, y, w, h] in Screen pixels (bottom-left origin, the space of inputsim screen_x/screen_y), " +
|
|
19
|
+
"visible, visibility (on_screen | clipped | offscreen | transparent | zero_size; reason: no_camera for " +
|
|
20
|
+
"a World Space canvas nothing can project), interactable, " +
|
|
21
|
+
"occluded_by, sprite, depth } plus `screen` { w, h, safe_area, dpi } and `textSources` " +
|
|
22
|
+
"(ugui, plus tmp when TextMeshPro is installed). A control's text is its label (descendant texts); " +
|
|
23
|
+
"visibility follows the CanvasGroup alpha chain, RectMask2D / Mask clipping and screen bounds; " +
|
|
24
|
+
"occluded_by is the front-most raycast hit covering a raycast-target node (play mode only). " +
|
|
25
|
+
"PROFILES: compact (default) = visible nodes that have text or are controls, labels folded into " +
|
|
26
|
+
"their control, null keys omitted; balanced = every visible node (images, containers) + sprite/depth; " +
|
|
27
|
+
"full = every active node in any visibility state with every key. Bound it with page_size + cursor " +
|
|
28
|
+
"(pagination.next_cursor). DIAGNOSTICS (first page): overflow (text larger than its rect) and " +
|
|
29
|
+
"zero_size_interactables always; placeholder (texts matching placeholder_pattern) and " +
|
|
30
|
+
"small_touch_targets (controls under min_touch_size px, with Screen.dpi) only when passed. " +
|
|
31
|
+
"Then tap by label: inputsim_pointer / inputsim_touch target_text, or inputsim_gesture keyframe " +
|
|
32
|
+
"target_text. uGUI only (no UI Toolkit). Read-only, gate-free. Requires com.unity.ugui.", {
|
|
33
|
+
properties: {
|
|
34
|
+
profile: {
|
|
35
|
+
type: "string",
|
|
36
|
+
enum: ["compact", "balanced", "full"],
|
|
37
|
+
default: "compact",
|
|
38
|
+
description: "compact (default) = visible text/control nodes, labels folded into their control; " +
|
|
39
|
+
"balanced = every visible node + sprite/depth; full = every active node in any visibility " +
|
|
40
|
+
"state (hidden / clipped / offscreen / zero-size included) with every key.",
|
|
41
|
+
},
|
|
42
|
+
page_size: {
|
|
43
|
+
type: "integer",
|
|
44
|
+
default: 50,
|
|
45
|
+
minimum: 1,
|
|
46
|
+
maximum: 200,
|
|
47
|
+
description: "Max nodes per page. Default 50, cap 200. Follow pagination.next_cursor for the rest.",
|
|
48
|
+
},
|
|
49
|
+
cursor: {
|
|
50
|
+
type: "string",
|
|
51
|
+
description: "Opaque cursor from a previous response's pagination.next_cursor. Omit on the first call.",
|
|
52
|
+
},
|
|
53
|
+
text_contains: {
|
|
54
|
+
type: "string",
|
|
55
|
+
description: "Keep nodes whose text contains this (case-insensitive, after trimming, whitespace collapse " +
|
|
56
|
+
"and rich-text tag removal).",
|
|
57
|
+
},
|
|
58
|
+
role: {
|
|
59
|
+
type: "string",
|
|
60
|
+
description: `Keep only these roles (comma-separated): ${UI_SNAPSHOT_ROLES.join(", ")}.`,
|
|
61
|
+
},
|
|
62
|
+
under: {
|
|
63
|
+
type: "string",
|
|
64
|
+
description: "Scope to the subtree of a hierarchy path ('Canvas/Shop') or a trailing path suffix ('Shop'). " +
|
|
65
|
+
"Diagnostics use the same scope. Unknown → under_not_found.",
|
|
66
|
+
},
|
|
67
|
+
visible_only: {
|
|
68
|
+
type: "boolean",
|
|
69
|
+
description: "Drop nodes that are not on screen. Defaults to true for compact/balanced and false for full; " +
|
|
70
|
+
"set false on compact to see hidden text and controls too.",
|
|
71
|
+
},
|
|
72
|
+
placeholder_pattern: {
|
|
73
|
+
type: "string",
|
|
74
|
+
description: "Regex (.NET, case-sensitive) flagging texts that look like unresolved localisation keys, e.g. " +
|
|
75
|
+
"'^[*#{].*[*#}]$'. Only checked when passed; there is no built-in pattern. Over 100 ms on a " +
|
|
76
|
+
"text or 2 s for the snapshot → regex_timeout.",
|
|
77
|
+
},
|
|
78
|
+
min_touch_size: {
|
|
79
|
+
type: "number",
|
|
80
|
+
exclusiveMinimum: 0,
|
|
81
|
+
description: "Flag visible controls whose rect is narrower or shorter than this many screen pixels " +
|
|
82
|
+
"(diagnostics.small_touch_targets, which also reports Screen.dpi so you can convert from dp: " +
|
|
83
|
+
"px = dp × dpi / 160). Only checked when passed.",
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
//# sourceMappingURL=ui-snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ui-snapshot.js","sourceRoot":"","sources":["../../src/tools/ui-snapshot.ts"],"names":[],"mappings":"AAAA,iCAAiC;AACjC,EAAE;AACF,sEAAsE;AACtE,6EAA6E;AAC7E,+EAA+E;AAC/E,6EAA6E;AAC7E,oEAAoE;AACpE,6EAA6E;AAC7E,+EAA+E;AAC/E,8EAA8E;AAC9E,sEAAsE;AACtE,uEAAuE;AACvE,iCAAiC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAEjD,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,CAAU,CAAC;AAE1H,MAAM,CAAC,MAAM,UAAU,GAAG,QAAQ,CAChC,4BAA4B,EAC5B,4FAA4F;IAC1F,iGAAiG;IACjG,oGAAoG;IACpG,wGAAwG;IACxG,2DAA2D;IAC3D,wFAAwF;IACxF,oGAAoG;IACpG,gGAAgG;IAChG,6FAA6F;IAC7F,iGAAiG;IACjG,uGAAuG;IACvG,oGAAoG;IACpG,+FAA+F;IAC/F,uFAAuF;IACvF,4FAA4F;IAC5F,iGAAiG;IACjG,wFAAwF,EAC1F;IACE,UAAU,EAAE;QACV,OAAO,EAAE;YACP,IAAI,EAAE,QAAQ;YACd,IAAI,EAAE,CAAC,SAAS,EAAE,UAAU,EAAE,MAAM,CAAC;YACrC,OAAO,EAAE,SAAS;YAClB,WAAW,EACT,oFAAoF;gBACpF,2FAA2F;gBAC3F,2EAA2E;SAC9E;QACD,SAAS,EAAE;YACT,IAAI,EAAE,SAAS;YACf,OAAO,EAAE,EAAE;YACX,OAAO,EAAE,CAAC;YACV,OAAO,EAAE,GAAG;YACZ,WAAW,EAAE,sFAAsF;SACpG;QACD,MAAM,EAAE;YACN,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,0FAA0F;SACxG;QACD,aAAa,EAAE;YACb,IAAI,EAAE,QAAQ;YACd,WAAW,EACT,6FAA6F;gBAC7F,6BAA6B;SAChC;QACD,IAAI,EAAE;YACJ,IAAI,EAAE,QAAQ;YACd,WAAW,EAAE,4CAA4C,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;SACzF;QACD,KAAK,EAAE;YACL,IAAI,EAAE,QAAQ;YACd,WAAW,EACT,+FAA+F;gBAC/F,4DAA4D;SAC/D;QACD,YAAY,EAAE;YACZ,IAAI,EAAE,SAAS;YACf,WAAW,EACT,+FAA+F;gBAC/F,2DAA2D;SAC9D;QACD,mBAAmB,EAAE;YACnB,IAAI,EAAE,QAAQ;YACd,WAAW,EACT,gGAAgG;gBAChG,6FAA6F;gBAC7F,+CAA+C;SAClD;QACD,cAAc,EAAE;YACd,IAAI,EAAE,QAAQ;YACd,gBAAgB,EAAE,CAAC;YACnB,WAAW,EACT,uFAAuF;gBACvF,8FAA8F;gBAC9F,iDAAiD;SACpD;KACF;CACF,CACF,CAAC"}
|