{"version":3,"file":"unity-inspect.d.ts","sourceRoot":"","sources":["../../../src/core/unity-mcp/unity-inspect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yCAAyC,CAAC;AAEhF,OAAO,KAAK,EAAE,mBAAmB,EAAmC,MAAM,gCAAgC,CAAC;AAE3G,OAAO,EAGN,KAAK,qBAAqB,EAG1B,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,MAAM,kBAAkB,CAAC;AAE1B,MAAM,WAAW,mBAAmB;IACnC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,OAAO,EAAE,gBAAgB,CAAC;IAC1B,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,6BAA6B;IAC7C,UAAU,EAAE,mBAAmB,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,2EAA2E;IAC3E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CACnB;AAiDD;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAAC,OAAO,EAAE,6BAA6B,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAkGnH;AAED;;;;GAIG;AACH,wBAAsB,YAAY,CAAC,OAAO,EAAE,mBAAmB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAW/F;AAED,mDAAmD;AACnD,wBAAgB,cAAc,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAElE;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,oBAAoB,EAAE,GAAG,MAAM,EAAE,CAE1E","sourcesContent":["/**\n * Unity MCP Vertical Slice — read-only inspection (2.13.0).\n *\n * One-shot, process-lifetime operation that reaches the real LOTG Unity Editor\n * through the existing MCP Client Foundation and normalizes what the enabled\n * Unity MCP tools actually expose. Generic connect/discover/invoke flows through\n * `McpClientPort`; this module adds only safe discovery, normalization, and\n * Evidence correlation. It never fabricates project state and never enables or\n * invokes mutating tools.\n */\n\nimport type { McpClientService } from \"../mcp-foundation/mcp-client-service.js\";\nimport { McpClientError } from \"../mcp-foundation/mcp-error.js\";\nimport type { McpServerDefinition, McpServerSession, McpToolResult } from \"../mcp-foundation/mcp-types.js\";\nimport { buildUnityMcpServerDefinition } from \"./unity-server-config.js\";\nimport {\n\tUNITY_INSPECTION_CATEGORIES,\n\tUNITY_KNOWN_TOOLS,\n\ttype UnityInspectionResult,\n\ttype UnityObservation,\n\ttype UnityObservationStatus,\n\ttype UnityServerTarget,\n\ttype UnityToolObservation,\n} from \"./unity-types.js\";\n\nexport interface UnityInspectOptions {\n\ttarget: UnityServerTarget;\n\tservice: McpClientService;\n\tnow?: () => number;\n}\n\nexport interface UnityInspectDefinitionOptions {\n\tdefinition: McpServerDefinition;\n\tserverId: string;\n\tmachine: string;\n\tservice: McpClientService;\n\t/** Optional project path for the project-path observation (when known). */\n\tprojectPath?: string;\n\tnow?: () => number;\n}\n\nconst READ_ONLY_CONSOLE_ARGS = { maxEntries: 50, includeStackTrace: false };\n\n/** Parse Unity_GetConsoleLogs output without fabricating a shape it lacks. */\nfunction extractConsolePayload(result: McpToolResult): unknown {\n\tconst text = result.content.find((item) => item.type === \"text\" && typeof item.text === \"string\")?.text;\n\tif (text === undefined) return undefined;\n\ttry {\n\t\tconst parsed = JSON.parse(text) as unknown;\n\t\tif (typeof parsed === \"object\" && parsed !== null && \"data\" in (parsed as Record<string, unknown>)) {\n\t\t\treturn (parsed as Record<string, unknown>).data;\n\t\t}\n\t\treturn parsed;\n\t} catch {\n\t\treturn text;\n\t}\n}\n\nfunction observation(\n\tcategory: string,\n\tstatus: UnityObservationStatus,\n\textra: Partial<UnityObservation> = {},\n): UnityObservation {\n\treturn { category, status, ...extra };\n}\n\nfunction failureResult(\n\tserverId: string,\n\tmachine: string,\n\tconnectionReason: string,\n\tstartedAtMs: number,\n\tnow: () => number,\n): UnityInspectionResult {\n\treturn {\n\t\tkind: \"unity-inspection\",\n\t\tversion: 1,\n\t\tserverId,\n\t\tmachine,\n\t\tconnection: \"FAIL\",\n\t\tconnectionReason,\n\t\ttools: [],\n\t\tobservations: [],\n\t\tevidenceIds: [],\n\t\tstartedAtMs,\n\t\tcompletedAtMs: now(),\n\t};\n}\n\n/**\n * Run a bounded, read-only Unity inspection against an explicit server\n * definition. Always disconnects; a failure never fabricates a connected\n * session or an observation. Exposed separately from `inspectUnity` so failure\n * cases are testable with a deterministic spawn.\n */\nexport async function inspectUnityDefinition(options: UnityInspectDefinitionOptions): Promise<UnityInspectionResult> {\n\tconst { definition, serverId, machine, service, projectPath } = options;\n\tconst now = options.now ?? Date.now;\n\tconst startedAtMs = now();\n\tconst evidenceIds: string[] = [];\n\n\tlet session: McpServerSession | undefined;\n\ttry {\n\t\tsession = await service.connect(definition);\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tconst code = error instanceof McpClientError ? error.code : undefined;\n\t\treturn failureResult(serverId, machine, code ? `${code}: ${message}` : message, startedAtMs, now);\n\t}\n\n\ttry {\n\t\tconst tools = await service.listTools(session.sessionId);\n\t\tconst toolObservations: UnityToolObservation[] = tools.map((tool) => {\n\t\t\tconst known = UNITY_KNOWN_TOOLS[tool.name];\n\t\t\treturn known\n\t\t\t\t? { name: tool.name, mutating: known.mutating }\n\t\t\t\t: { name: tool.name, mutating: false, description: tool.description };\n\t\t});\n\n\t\tconst observations: UnityObservation[] = [];\n\n\t\t// Console: read-only and always attempted when the tool is enabled.\n\t\tconst hasConsole = tools.some((tool) => tool.name === \"Unity_GetConsoleLogs\");\n\t\tif (hasConsole) {\n\t\t\tconst result = await service.callTool(session.sessionId, {\n\t\t\t\ttoolName: \"Unity_GetConsoleLogs\",\n\t\t\t\targuments: READ_ONLY_CONSOLE_ARGS,\n\t\t\t});\n\t\t\tif (result.evidenceId) evidenceIds.push(result.evidenceId);\n\t\t\tif (result.status === \"success\") {\n\t\t\t\tobservations.push(\n\t\t\t\t\tobservation(\"console\", \"OBSERVED\", {\n\t\t\t\t\t\tvalue: extractConsolePayload(result),\n\t\t\t\t\t\tnote: \"Unity_GetConsoleLogs (read-only)\",\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t} else {\n\t\t\t\tobservations.push(\n\t\t\t\t\tobservation(\"console\", \"ERROR\", {\n\t\t\t\t\t\tnote: `${result.errorCode ?? \"UNKNOWN\"}: ${result.errorMessage ?? \"tool call failed\"}`,\n\t\t\t\t\t\tmissingTool: \"Unity_GetConsoleLogs\",\n\t\t\t\t\t}),\n\t\t\t\t);\n\t\t\t}\n\t\t} else {\n\t\t\tobservations.push(\n\t\t\t\tobservation(\"console\", \"NOT_ENABLED\", {\n\t\t\t\t\tnote: \"Console inspection requires Unity_GetConsoleLogs\",\n\t\t\t\t\tmissingTool: \"Unity_GetConsoleLogs\",\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\t// Project path: proven by the relay's project-targeted bridge connection.\n\t\tobservations.push(\n\t\t\tobservation(\"project-path\", \"OBSERVED\", {\n\t\t\t\tvalue: projectPath,\n\t\t\t\tnote: \"relay targeted the configured Unity project path\",\n\t\t\t}),\n\t\t);\n\n\t\t// The remaining read-only categories are not exposed by the currently\n\t\t// enabled Unity MCP tool surface. Classified honestly, never fabricated.\n\t\tfor (const category of UNITY_INSPECTION_CATEGORIES) {\n\t\t\tif (category === \"console\" || category === \"project-path\") continue;\n\t\t\tobservations.push(\n\t\t\t\tobservation(category, \"NOT_EXPOSED\", {\n\t\t\t\t\tnote: \"no enabled Unity MCP tool surfaces this category\",\n\t\t\t\t}),\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tkind: \"unity-inspection\",\n\t\t\tversion: 1,\n\t\t\tserverId,\n\t\t\tmachine,\n\t\t\tconnection: \"PASS\",\n\t\t\tprotocolEra: session.protocolEra,\n\t\t\tprotocolVersion: session.protocolVersion,\n\t\t\tserverIdentity: session.identity,\n\t\t\tsessionId: session.sessionId,\n\t\t\ttools: toolObservations,\n\t\t\tobservations,\n\t\t\tevidenceIds,\n\t\t\tstartedAtMs,\n\t\t\tcompletedAtMs: now(),\n\t\t};\n\t} finally {\n\t\tawait service.disconnect(session.sessionId).catch(() => {\n\t\t\t// Disconnect failures never mask the inspection outcome.\n\t\t});\n\t}\n}\n\n/**\n * Run a bounded, read-only Unity inspection for a `UnityServerTarget`. This is\n * the operator-facing entry point: it builds the SSH stdio server definition\n * and delegates to `inspectUnityDefinition`.\n */\nexport async function inspectUnity(options: UnityInspectOptions): Promise<UnityInspectionResult> {\n\tconst { target, service, now } = options;\n\tconst definition = buildUnityMcpServerDefinition(target);\n\treturn inspectUnityDefinition({\n\t\tdefinition,\n\t\tserverId: target.serverId,\n\t\tmachine: target.sshTarget,\n\t\tservice,\n\t\tprojectPath: target.unityProjectPath,\n\t\tnow,\n\t});\n}\n\n/** Helpers exposed for tests and CLI rendering. */\nexport function isMutatingTool(tool: UnityToolObservation): boolean {\n\treturn tool.mutating;\n}\n\nexport function readOnlyUnityTools(tools: UnityToolObservation[]): string[] {\n\treturn tools.filter((tool) => !tool.mutating).map((tool) => tool.name);\n}\n"]}