@pascal-app/mcp 1.0.4 → 1.0.5

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.
Files changed (244) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/README.md +166 -62
  3. package/dist/bin/pascal-mcp.js +1 -1
  4. package/dist/bridge/agent-session.d.ts +18 -0
  5. package/dist/bridge/agent-session.d.ts.map +1 -0
  6. package/dist/bridge/agent-session.js +15 -0
  7. package/dist/bridge/index.d.ts +1 -1
  8. package/dist/bridge/index.d.ts.map +1 -1
  9. package/dist/bridge/scene-bridge.d.ts +3 -0
  10. package/dist/bridge/scene-bridge.d.ts.map +1 -1
  11. package/dist/bridge/scene-bridge.js +7 -0
  12. package/dist/lib/safe-fetch.d.ts +22 -4
  13. package/dist/lib/safe-fetch.d.ts.map +1 -1
  14. package/dist/lib/safe-fetch.js +233 -134
  15. package/dist/operations/scene-operations.d.ts +18 -0
  16. package/dist/operations/scene-operations.d.ts.map +1 -1
  17. package/dist/operations/scene-operations.js +11 -0
  18. package/dist/prompts/from-brief.d.ts +1 -1
  19. package/dist/prompts/from-brief.d.ts.map +1 -1
  20. package/dist/prompts/from-brief.js +3 -3
  21. package/dist/prompts/index.d.ts +1 -1
  22. package/dist/prompts/index.d.ts.map +1 -1
  23. package/dist/prompts/iterate-on-feedback.d.ts +1 -1
  24. package/dist/prompts/iterate-on-feedback.d.ts.map +1 -1
  25. package/dist/prompts/iterate-on-feedback.js +2 -2
  26. package/dist/prompts/renovation-from-photos.d.ts +1 -1
  27. package/dist/prompts/renovation-from-photos.d.ts.map +1 -1
  28. package/dist/prompts/renovation-from-photos.js +2 -2
  29. package/dist/prompts/scene-guidance.d.ts.map +1 -1
  30. package/dist/prompts/scene-guidance.js +10 -3
  31. package/dist/resources/agent-guide.d.ts +1 -1
  32. package/dist/resources/agent-guide.d.ts.map +1 -1
  33. package/dist/resources/agent-guide.js +52 -44
  34. package/dist/resources/catalog-items.d.ts +1 -1
  35. package/dist/resources/catalog-items.d.ts.map +1 -1
  36. package/dist/resources/constraints.d.ts +1 -1
  37. package/dist/resources/constraints.d.ts.map +1 -1
  38. package/dist/resources/constraints.js +1 -1
  39. package/dist/resources/design-schema.d.ts +1 -1
  40. package/dist/resources/design-schema.d.ts.map +1 -1
  41. package/dist/resources/index.d.ts +1 -1
  42. package/dist/resources/index.d.ts.map +1 -1
  43. package/dist/resources/scene-current.d.ts +1 -1
  44. package/dist/resources/scene-current.d.ts.map +1 -1
  45. package/dist/resources/scene-summary.d.ts +1 -1
  46. package/dist/resources/scene-summary.d.ts.map +1 -1
  47. package/dist/server.d.ts +7 -2
  48. package/dist/server.d.ts.map +1 -1
  49. package/dist/server.js +59 -21
  50. package/dist/storage/disk-artifact-store.d.ts.map +1 -1
  51. package/dist/storage/sqlite-scene-store.d.ts +1 -1
  52. package/dist/storage/sqlite-scene-store.d.ts.map +1 -1
  53. package/dist/storage/types.d.ts +4 -4
  54. package/dist/storage/types.d.ts.map +1 -1
  55. package/dist/templates/empty-studio.d.ts +3 -3
  56. package/dist/templates/empty-studio.d.ts.map +1 -1
  57. package/dist/templates/garden-house.d.ts +3 -3
  58. package/dist/templates/garden-house.d.ts.map +1 -1
  59. package/dist/templates/index.d.ts.map +1 -1
  60. package/dist/templates/two-bedroom.d.ts +3 -3
  61. package/dist/templates/two-bedroom.d.ts.map +1 -1
  62. package/dist/tools/add-column.d.ts +1 -1
  63. package/dist/tools/add-column.d.ts.map +1 -1
  64. package/dist/tools/add-column.js +3 -2
  65. package/dist/tools/add-object.d.ts +19 -3
  66. package/dist/tools/add-object.d.ts.map +1 -1
  67. package/dist/tools/add-object.js +66 -35
  68. package/dist/tools/annotations.d.ts.map +1 -1
  69. package/dist/tools/apply-patch.d.ts +6 -2
  70. package/dist/tools/apply-patch.d.ts.map +1 -1
  71. package/dist/tools/apply-patch.js +13 -8
  72. package/dist/tools/asset-catalog.d.ts.map +1 -1
  73. package/dist/tools/asset-catalog.js +23 -0
  74. package/dist/tools/author-scene.d.ts +10 -0
  75. package/dist/tools/author-scene.d.ts.map +1 -0
  76. package/dist/tools/author-scene.js +140 -0
  77. package/dist/tools/check-collisions.d.ts +6 -6
  78. package/dist/tools/check-collisions.d.ts.map +1 -1
  79. package/dist/tools/check-collisions.js +6 -6
  80. package/dist/tools/clear-scene.d.ts +1 -1
  81. package/dist/tools/clear-scene.d.ts.map +1 -1
  82. package/dist/tools/clear-scene.js +3 -3
  83. package/dist/tools/collections.d.ts +1 -1
  84. package/dist/tools/collections.d.ts.map +1 -1
  85. package/dist/tools/collections.js +3 -2
  86. package/dist/tools/construction-tools.d.ts +6 -49
  87. package/dist/tools/construction-tools.d.ts.map +1 -1
  88. package/dist/tools/construction-tools.js +9 -158
  89. package/dist/tools/create-unit.d.ts +3 -3
  90. package/dist/tools/create-unit.d.ts.map +1 -1
  91. package/dist/tools/create-unit.js +2 -2
  92. package/dist/tools/errors.d.ts +5 -5
  93. package/dist/tools/errors.d.ts.map +1 -1
  94. package/dist/tools/errors.js +4 -4
  95. package/dist/tools/export-glb.d.ts +1 -1
  96. package/dist/tools/export-glb.d.ts.map +1 -1
  97. package/dist/tools/export-glb.js +2 -2
  98. package/dist/tools/export-json.d.ts +1 -1
  99. package/dist/tools/export-json.d.ts.map +1 -1
  100. package/dist/tools/export-json.js +2 -2
  101. package/dist/tools/get-scene.d.ts +1 -1
  102. package/dist/tools/get-scene.d.ts.map +1 -1
  103. package/dist/tools/get-scene.js +2 -2
  104. package/dist/tools/hosted-services.d.ts +1 -1
  105. package/dist/tools/hosted-services.d.ts.map +1 -1
  106. package/dist/tools/hosted-services.js +13 -3
  107. package/dist/tools/index.d.ts +4 -3
  108. package/dist/tools/index.d.ts.map +1 -1
  109. package/dist/tools/index.js +8 -13
  110. package/dist/tools/level-height-guard.d.ts +2 -0
  111. package/dist/tools/level-height-guard.d.ts.map +1 -0
  112. package/dist/tools/level-height-guard.js +11 -0
  113. package/dist/tools/list-units.d.ts +2 -2
  114. package/dist/tools/list-units.d.ts.map +1 -1
  115. package/dist/tools/list-units.js +2 -2
  116. package/dist/tools/live-sync.d.ts +1 -1
  117. package/dist/tools/live-sync.d.ts.map +1 -1
  118. package/dist/tools/live-sync.js +4 -4
  119. package/dist/tools/measure.d.ts +1 -1
  120. package/dist/tools/measure.d.ts.map +1 -1
  121. package/dist/tools/measure.js +7 -7
  122. package/dist/tools/node-world-point.d.ts +1 -1
  123. package/dist/tools/node-world-point.d.ts.map +1 -1
  124. package/dist/tools/normalize-schema-dialect.d.ts +1 -1
  125. package/dist/tools/normalize-schema-dialect.d.ts.map +1 -1
  126. package/dist/tools/normalize-schema-dialect.js +2 -3
  127. package/dist/tools/patch-guards.d.ts +6 -4
  128. package/dist/tools/patch-guards.d.ts.map +1 -1
  129. package/dist/tools/patch-guards.js +5 -2
  130. package/dist/tools/photo-to-scene/index.d.ts +1 -1
  131. package/dist/tools/photo-to-scene/index.d.ts.map +1 -1
  132. package/dist/tools/photo-to-scene/photo-to-scene.d.ts +3 -3
  133. package/dist/tools/photo-to-scene/photo-to-scene.d.ts.map +1 -1
  134. package/dist/tools/photo-to-scene/photo-to-scene.js +7 -7
  135. package/dist/tools/project-files.d.ts +4 -0
  136. package/dist/tools/project-files.d.ts.map +1 -0
  137. package/dist/tools/project-files.js +30 -0
  138. package/dist/tools/redo.d.ts +2 -2
  139. package/dist/tools/redo.d.ts.map +1 -1
  140. package/dist/tools/redo.js +2 -2
  141. package/dist/tools/retired-tools.d.ts +10 -0
  142. package/dist/tools/retired-tools.d.ts.map +1 -0
  143. package/dist/tools/retired-tools.js +22 -0
  144. package/dist/tools/roof-level-guard.d.ts +2 -0
  145. package/dist/tools/roof-level-guard.d.ts.map +1 -0
  146. package/dist/tools/roof-level-guard.js +22 -0
  147. package/dist/tools/room-tools.d.ts +47 -46
  148. package/dist/tools/room-tools.d.ts.map +1 -1
  149. package/dist/tools/room-tools.js +6 -5
  150. package/dist/tools/scene-lifecycle/create-project.d.ts +1 -1
  151. package/dist/tools/scene-lifecycle/create-project.d.ts.map +1 -1
  152. package/dist/tools/scene-lifecycle/create-project.js +5 -5
  153. package/dist/tools/scene-lifecycle/delete-scene.d.ts +1 -1
  154. package/dist/tools/scene-lifecycle/delete-scene.d.ts.map +1 -1
  155. package/dist/tools/scene-lifecycle/delete-scene.js +6 -6
  156. package/dist/tools/scene-lifecycle/get-project-status.d.ts +1 -1
  157. package/dist/tools/scene-lifecycle/get-project-status.d.ts.map +1 -1
  158. package/dist/tools/scene-lifecycle/get-project-status.js +6 -6
  159. package/dist/tools/scene-lifecycle/index.d.ts +1 -1
  160. package/dist/tools/scene-lifecycle/index.d.ts.map +1 -1
  161. package/dist/tools/scene-lifecycle/list-scenes.d.ts +1 -1
  162. package/dist/tools/scene-lifecycle/list-scenes.d.ts.map +1 -1
  163. package/dist/tools/scene-lifecycle/list-scenes.js +4 -4
  164. package/dist/tools/scene-lifecycle/load-scene.d.ts +2 -2
  165. package/dist/tools/scene-lifecycle/load-scene.d.ts.map +1 -1
  166. package/dist/tools/scene-lifecycle/load-scene.js +5 -5
  167. package/dist/tools/scene-lifecycle/metadata.d.ts +1 -1
  168. package/dist/tools/scene-lifecycle/rename-scene.d.ts +1 -1
  169. package/dist/tools/scene-lifecycle/rename-scene.d.ts.map +1 -1
  170. package/dist/tools/scene-lifecycle/rename-scene.js +6 -6
  171. package/dist/tools/scene-lifecycle/save-scene.d.ts +5 -3
  172. package/dist/tools/scene-lifecycle/save-scene.d.ts.map +1 -1
  173. package/dist/tools/scene-lifecycle/save-scene.js +80 -12
  174. package/dist/tools/scene-lifecycle/test-utils.d.ts +1 -1
  175. package/dist/tools/scene-lifecycle/test-utils.d.ts.map +1 -1
  176. package/dist/tools/set-unit-members.d.ts +2 -2
  177. package/dist/tools/set-unit-members.d.ts.map +1 -1
  178. package/dist/tools/set-unit-members.js +6 -6
  179. package/dist/tools/set-zone.d.ts +2 -2
  180. package/dist/tools/set-zone.d.ts.map +1 -1
  181. package/dist/tools/set-zone.js +30 -18
  182. package/dist/tools/shared-tools.d.ts +15 -5
  183. package/dist/tools/shared-tools.d.ts.map +1 -1
  184. package/dist/tools/shared-tools.js +242 -29
  185. package/dist/tools/structure-tools.d.ts +3 -3
  186. package/dist/tools/templates/create-from-template.d.ts +2 -2
  187. package/dist/tools/templates/create-from-template.d.ts.map +1 -1
  188. package/dist/tools/templates/create-from-template.js +6 -6
  189. package/dist/tools/templates/create-house-from-brief.d.ts +1 -1
  190. package/dist/tools/templates/create-house-from-brief.d.ts.map +1 -1
  191. package/dist/tools/templates/create-house-from-brief.js +6 -6
  192. package/dist/tools/templates/index.d.ts +1 -1
  193. package/dist/tools/templates/index.d.ts.map +1 -1
  194. package/dist/tools/templates/list-templates.d.ts +1 -1
  195. package/dist/tools/templates/list-templates.d.ts.map +1 -1
  196. package/dist/tools/templates/list-templates.js +2 -2
  197. package/dist/tools/undo.d.ts +2 -2
  198. package/dist/tools/undo.d.ts.map +1 -1
  199. package/dist/tools/undo.js +2 -2
  200. package/dist/tools/unit-members.d.ts.map +1 -1
  201. package/dist/tools/unit-members.js +6 -6
  202. package/dist/tools/variants/generate-variants.d.ts +6 -6
  203. package/dist/tools/variants/generate-variants.d.ts.map +1 -1
  204. package/dist/tools/variants/generate-variants.js +6 -6
  205. package/dist/tools/variants/index.d.ts +1 -1
  206. package/dist/tools/variants/index.d.ts.map +1 -1
  207. package/dist/tools/view-scene.d.ts +8 -2
  208. package/dist/tools/view-scene.d.ts.map +1 -1
  209. package/dist/tools/view-scene.js +17 -5
  210. package/dist/tools/vision/analyze-floorplan-image.d.ts +1 -1
  211. package/dist/tools/vision/analyze-floorplan-image.d.ts.map +1 -1
  212. package/dist/tools/vision/analyze-floorplan-image.js +7 -7
  213. package/dist/tools/vision/analyze-room-photo.d.ts +1 -1
  214. package/dist/tools/vision/analyze-room-photo.d.ts.map +1 -1
  215. package/dist/tools/vision/analyze-room-photo.js +7 -7
  216. package/dist/tools/vision/index.d.ts +1 -1
  217. package/dist/tools/vision/index.d.ts.map +1 -1
  218. package/dist/transports/http.d.ts +8 -2
  219. package/dist/transports/http.d.ts.map +1 -1
  220. package/dist/transports/http.js +9 -3
  221. package/dist/transports/stdio.d.ts +15 -9
  222. package/dist/transports/stdio.d.ts.map +1 -1
  223. package/dist/transports/stdio.js +18 -11
  224. package/examples/coordinate-conventions-demo.md +12 -13
  225. package/examples/embed-in-agent.ts +5 -6
  226. package/examples/generate-apartment.md +4 -4
  227. package/examples/photo-to-scene.md +2 -3
  228. package/examples/renovate-from-photos.md +3 -5
  229. package/package.json +13 -9
  230. package/dist/tools/describe-node.d.ts +0 -17
  231. package/dist/tools/describe-node.d.ts.map +0 -1
  232. package/dist/tools/describe-node.js +0 -99
  233. package/dist/tools/find-nodes.d.ts +0 -16
  234. package/dist/tools/find-nodes.d.ts.map +0 -1
  235. package/dist/tools/find-nodes.js +0 -204
  236. package/dist/tools/place-design.d.ts +0 -34
  237. package/dist/tools/place-design.d.ts.map +0 -1
  238. package/dist/tools/place-design.js +0 -91
  239. package/dist/tools/validate-design.d.ts +0 -136
  240. package/dist/tools/validate-design.d.ts.map +0 -1
  241. package/dist/tools/validate-design.js +0 -28
  242. package/dist/tools/validate-scene.d.ts +0 -19
  243. package/dist/tools/validate-scene.d.ts.map +0 -1
  244. package/dist/tools/validate-scene.js +0 -28
@@ -1,7 +1,8 @@
1
- import { newZone } from '@pascal-app/core';
1
+ import { isAllocatedRoomName, newZone, walledRoomAt } from '@pascal-app/core';
2
+ import { roofLevelRefusal } from '@pascal-app/core/agent-operations';
2
3
  import { z } from 'zod';
3
4
  import { ADDITIVE_TOOL_ANNOTATIONS } from './annotations.js';
4
- import { ErrorCode, throwMcpError } from './errors.js';
5
+ import { ProtocolErrorCode, refusalResult, throwMcpError } from './errors.js';
5
6
  import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync.js';
6
7
  import { NodeIdSchema, Vec2Schema } from './schemas.js';
7
8
  export const setZoneInput = {
@@ -17,30 +18,41 @@ export const setZoneOutput = {
17
18
  export function registerSetZone(server, bridge) {
18
19
  server.registerTool('set_zone', {
19
20
  title: 'Set zone',
20
- description: 'Create a polygonal zone on the given level. label is stored as the zone name and properties are merged into metadata.',
21
- inputSchema: setZoneInput,
22
- outputSchema: setZoneOutput,
21
+ description: 'Create a polygonal zone on the given level. label is stored as the zone name and properties are merged into metadata. A polygon along walls that already enclose a room is that room, never a second zone: a room still named "Room N", or already named label, takes the label and properties; a room a person named otherwise is refused (rename it with update_node only when the person asked).',
22
+ inputSchema: z.object(setZoneInput),
23
+ outputSchema: z.object(setZoneOutput),
23
24
  annotations: ADDITIVE_TOOL_ANNOTATIONS,
24
25
  }, async ({ levelId, polygon, label, properties }) => {
25
26
  const parent = bridge.getNode(levelId);
26
27
  if (!parent) {
27
- throwMcpError(ErrorCode.InvalidParams, `Level not found: ${levelId}`);
28
+ throwMcpError(ProtocolErrorCode.InvalidParams, `Level not found: ${levelId}`);
28
29
  }
29
30
  if (parent.type !== 'level') {
30
- throwMcpError(ErrorCode.InvalidParams, `Node ${levelId} is a ${parent.type}, expected level`);
31
+ throwMcpError(ProtocolErrorCode.InvalidParams, `Node ${levelId} is a ${parent.type}, expected level`);
31
32
  }
32
- if (typeof parent.metadata === 'object' &&
33
- parent.metadata !== null &&
34
- 'role' in parent.metadata &&
35
- parent.metadata.role === 'roof') {
36
- throwMcpError(ErrorCode.InvalidParams, `Roof support level ${levelId} is not an occupied story; create zones on an occupied level instead`);
33
+ const onRoofLevel = roofLevelRefusal(bridge.getNodes(), levelId, 'a zone');
34
+ if (onRoofLevel)
35
+ return refusalResult(onRoofLevel);
36
+ const walled = walledRoomAt(bridge.getNodes(), levelId, polygon);
37
+ if (walled && !isAllocatedRoomName(walled.name) && walled.name !== label) {
38
+ throwMcpError(ProtocolErrorCode.InvalidParams, `The walls here already enclose the room "${walled.name}" (${walled.id}), which a person named; a zone "${label}" would stack a second room on it. Rename it with update_node only if the person asked; otherwise leave it.`);
39
+ }
40
+ let id;
41
+ if (walled) {
42
+ id = walled.id;
43
+ bridge.updateNode(id, {
44
+ name: label,
45
+ metadata: { ...walled.metadata, ...properties },
46
+ });
47
+ }
48
+ else {
49
+ const zone = newZone({
50
+ name: label,
51
+ polygon: polygon,
52
+ metadata: properties ?? {},
53
+ });
54
+ id = bridge.createNode(zone, levelId);
37
55
  }
38
- const zone = newZone({
39
- name: label,
40
- polygon: polygon,
41
- metadata: properties ?? {},
42
- });
43
- const id = bridge.createNode(zone, levelId);
44
56
  const persistence = await publishLiveSceneSnapshot(bridge, 'set_zone');
45
57
  const payload = { zoneId: id, ...persistencePayload(persistence) };
46
58
  return {
@@ -1,6 +1,7 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
2
- import { type AgentOperation, type SceneChanges } from '@pascal-app/core/agent-operations';
3
- import { type AgentToolAnnotations, type AgentToolContract } from '@pascal-app/core/agent-tools';
1
+ import type { McpServer } from '@modelcontextprotocol/server';
2
+ import { type AddObjectInput, type AgentHostRuntime, type AgentOperation, type AssetViewLoader, type SceneChanges } from '@pascal-app/core/agent-operations';
3
+ import { type AgentToolAnnotations, type AgentToolContract, type DesignBrief } from '@pascal-app/core/agent-tools';
4
+ import { type CompiledGeometryScript } from '@pascal-app/core/schema';
4
5
  import { z } from 'zod';
5
6
  import type { Patch } from '../bridge/scene-bridge.js';
6
7
  import type { SceneOperations } from '../operations/index.js';
@@ -20,11 +21,20 @@ export type OperationTool = {
20
21
  /** Reads the host's item library: only these calls wait for it (the hosted one is a query). */
21
22
  catalog?: true;
22
23
  };
24
+ export type PlanReadingHosts = {
25
+ /** Reads catalog pictures and surfaces before view_assets and paint. */
26
+ assetViews?: AssetViewLoader;
27
+ /** Compiles one add_object module for a batch before the synchronous operations run. */
28
+ buildObject?: (input: Omit<AddObjectInput, 'compiled'>) => Promise<CompiledGeometryScript>;
29
+ };
23
30
  export declare function toPatches(changes: SceneChanges): Patch[];
24
- export declare function registerSharedTools(server: McpServer, bridge: SceneOperations, catalog?: AssetCatalog): void;
31
+ /** How an outcome reaches this bridge: its patches, its derived construction, its session record. */
32
+ export declare function agentRuntime(bridge: SceneOperations): AgentHostRuntime;
33
+ export declare function briefInForce(bridge: SceneOperations): DesignBrief | null | undefined;
34
+ export declare function registerSharedTools(server: McpServer, bridge: SceneOperations, catalog?: AssetCatalog, hosts?: PlanReadingHosts): void;
25
35
  /**
26
36
  * Registers one operation tool, as the shared tools are: a host adds a plugin's `'operation'`
27
37
  * tools with it, from the plugin's `./agent-tools` contract and `./agent-operations` operation.
28
38
  */
29
- export declare function registerOperationTool(server: McpServer, bridge: SceneOperations, tool: OperationTool, catalog?: AssetCatalog): void;
39
+ export declare function registerOperationTool(server: McpServer, bridge: SceneOperations, tool: OperationTool, catalog?: AssetCatalog, hosts?: PlanReadingHosts): void;
30
40
  //# sourceMappingURL=shared-tools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"shared-tools.d.ts","sourceRoot":"","sources":["../../src/tools/shared-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AACxE,OAAO,EAGL,KAAK,cAAc,EAGnB,KAAK,YAAY,EAElB,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EAsBvB,MAAM,8BAA8B,CAAA;AAOrC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAMpD,OAAO,EAAE,KAAK,YAAY,EAAkB,MAAM,iBAAiB,CAAA;AASnE;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,iBAAiB,CAAA;IAC3B,SAAS,EAAE,cAAc,CAAA;IACzB,WAAW,EAAE,oBAAoB,CAAA;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAA;IACxC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/D,+FAA+F;IAC/F,OAAO,CAAC,EAAE,IAAI,CAAA;CACf,CAAA;AAmRD,wBAAgB,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,KAAK,EAAE,CAmBxD;AAUD,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,eAAe,EACvB,OAAO,GAAE,YAA6B,GACrC,IAAI,CAGN;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,aAAa,EACnB,OAAO,GAAE,YAA6B,GACrC,IAAI,CAkEN"}
1
+ {"version":3,"file":"shared-tools.d.ts","sourceRoot":"","sources":["../../src/tools/shared-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAC7D,OAAO,EACL,KAAK,cAAc,EAGnB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,eAAe,EAUpB,KAAK,YAAY,EAGlB,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EAStB,KAAK,WAAW,EA+BjB,MAAM,8BAA8B,CAAA;AAErC,OAAO,EAGL,KAAK,sBAAsB,EAG5B,MAAM,yBAAyB,CAAA;AAChC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAMpD,OAAO,EAAE,KAAK,YAAY,EAAkB,MAAM,iBAAiB,CAAA;AASnE;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,iBAAiB,CAAA;IAC3B,SAAS,EAAE,cAAc,CAAA;IACzB,WAAW,EAAE,oBAAoB,CAAA;IACjC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAA;IACxC,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/D,+FAA+F;IAC/F,OAAO,CAAC,EAAE,IAAI,CAAA;CACf,CAAA;AAED,MAAM,MAAM,gBAAgB,GAAG;IAC7B,wEAAwE;IACxE,UAAU,CAAC,EAAE,eAAe,CAAA;IAC5B,wFAAwF;IACxF,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,IAAI,CAAC,cAAc,EAAE,UAAU,CAAC,KAAK,OAAO,CAAC,sBAAsB,CAAC,CAAA;CAC3F,CAAA;AAmaD,wBAAgB,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,KAAK,EAAE,CAmBxD;AAED,qGAAqG;AACrG,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,GAAG,gBAAgB,CAsBtE;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,GAAG,WAAW,GAAG,IAAI,GAAG,SAAS,CAIpF;AAUD,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,eAAe,EACvB,OAAO,GAAE,YAA6B,EACtC,KAAK,GAAE,gBAAqB,GAC3B,IAAI,CAGN;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,eAAe,EACvB,IAAI,EAAE,aAAa,EACnB,OAAO,GAAE,YAA6B,EACtC,KAAK,GAAE,gBAAqB,GAC3B,IAAI,CAuHN"}
@@ -1,5 +1,6 @@
1
- import { AGENT_OPERATIONS, achievedChanges, applyAgentOutcome, writeCollections, } from '@pascal-app/core/agent-operations';
2
- import { addLevelTool, addWallTool, createRoomTool, createStairTool, deleteNodeTool, duplicateLevelTool, findByTypeTool, fitStairTool, furnishRoomTool, getLevelSummaryTool, getNodeTool, getWallsTool, getZonesTool, listLevelsTool, measureStairTool, paintTool, placeItemsTool, ROOM_TOOL_CONTRACTS, searchAssetsTool, searchMaterialsTool, verifySceneTool, } from '@pascal-app/core/agent-tools';
1
+ import { AGENT_OPERATIONS, achievedChanges, applyAgentOutcome, assetViewNeeds, assetViewsReadCatalog, briefReadsCatalog, buildBatchObjects, createAssetViewLoader, createFetchAssetViewIO, furnishingRefusal, startOfSession, writeCollections, } from '@pascal-app/core/agent-operations';
2
+ import { addCornerWindowTool, addFenceTool, addLevelTool, addObjectTool, addWallTool, createRoofTool, createRoomTool, createStairTool, deleteNodeTool, designBriefTool, duplicateLevelTool, findByTypeTool, fitStairTool, furnishRoomTool, getLevelSummaryTool, getNodeTool, getSiteTool, getWallsTool, getZonesTool, listLevelsTool, measureStairTool, mergeWindowsTool, moveRoofsToRoofLevelTool, paintTool, placeBuildingTool, placeDesignTool, placeInRoomTool, placeItemsTool, ROOM_TOOL_CONTRACTS, refuse, runBatchTool, searchAssetsTool, searchMaterialsTool, setCheckpointTool, setGroundLevelTool, validateDesignTool, verifySceneTool, viewAssetsTool, } from '@pascal-app/core/agent-tools';
3
+ import { DesignValidationSchema } from '@pascal-app/core/procedural-items';
3
4
  import { MaterialSchema, SceneMaterial, } from '@pascal-app/core/schema';
4
5
  import { z } from 'zod';
5
6
  import { ADDITIVE_TOOL_ANNOTATIONS, DESTRUCTIVE_TOOL_ANNOTATIONS, READ_ONLY_TOOL_ANNOTATIONS, } from './annotations.js';
@@ -8,7 +9,11 @@ import { registerCollectionTools } from './collections.js';
8
9
  import { refusalResult } from './errors.js';
9
10
  import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync.js';
10
11
  import { ROOM_TOOL_ANNOTATIONS, structureOutput } from './structure-tools.js';
12
+ const defaultAssetViews = createAssetViewLoader(createFetchAssetViewIO());
11
13
  const jsonObject = z.record(z.string(), z.unknown());
14
+ function refuseScripts() {
15
+ refuse('scripts_unavailable', 'This Pascal server cannot run geometry scripts.');
16
+ }
12
17
  /** What the scene holds after a mutating call (core achievedChanges). */
13
18
  const achievedOutput = {
14
19
  achieved: z
@@ -31,6 +36,23 @@ const levelRoleOutput = {
31
36
  referenceLevelId: z.string().nullable(),
32
37
  };
33
38
  const SHARED_TOOLS = [
39
+ {
40
+ contract: validateDesignTool,
41
+ operation: AGENT_OPERATIONS.validate_design,
42
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
43
+ outputSchema: DesignValidationSchema.shape,
44
+ },
45
+ {
46
+ contract: placeDesignTool,
47
+ operation: AGENT_OPERATIONS.place_design,
48
+ annotations: ADDITIVE_TOOL_ANNOTATIONS,
49
+ outputSchema: {
50
+ designId: z.string(),
51
+ parentId: z.string(),
52
+ surfaceId: z.string().nullable(),
53
+ ...liveSyncOutput,
54
+ },
55
+ },
34
56
  {
35
57
  contract: measureStairTool,
36
58
  operation: AGENT_OPERATIONS.measure_stair,
@@ -67,7 +89,12 @@ const SHARED_TOOLS = [
67
89
  contract: getNodeTool,
68
90
  operation: AGENT_OPERATIONS.get_node,
69
91
  annotations: READ_ONLY_TOOL_ANNOTATIONS,
70
- outputSchema: { node: jsonObject, materials: z.record(z.string(), SceneMaterial).optional() },
92
+ outputSchema: {
93
+ node: jsonObject,
94
+ ancestorIds: z.array(z.string()),
95
+ missingChildIds: z.array(z.string()).optional(),
96
+ materials: z.record(z.string(), SceneMaterial).optional(),
97
+ },
71
98
  },
72
99
  {
73
100
  contract: getLevelSummaryTool,
@@ -117,6 +144,24 @@ const SHARED_TOOLS = [
117
144
  ...liveSyncOutput,
118
145
  },
119
146
  },
147
+ {
148
+ contract: setGroundLevelTool,
149
+ operation: AGENT_OPERATIONS.set_ground_level,
150
+ annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
151
+ outputSchema: {
152
+ ok: z.boolean(),
153
+ levelId: z.string(),
154
+ buildingId: z.string(),
155
+ floorElevation: z.number(),
156
+ gradeElevation: z.number().optional(),
157
+ movedBuildingBy: z.number().optional(),
158
+ renumbered: z.boolean().optional(),
159
+ unchanged: z.boolean().optional(),
160
+ message: z.string(),
161
+ ...achievedOutput,
162
+ ...liveSyncOutput,
163
+ },
164
+ },
120
165
  {
121
166
  contract: verifySceneTool,
122
167
  operation: AGENT_OPERATIONS.verify_scene,
@@ -132,17 +177,33 @@ const SHARED_TOOLS = [
132
177
  activeLevelId: z.string().nullable(),
133
178
  levels: z.array(jsonObject),
134
179
  emptyLevelIds: z.array(z.string()),
135
- issues: z.array(z.object({
180
+ // Typed by `type`; a check registered with verify_scene may add fields of its own.
181
+ issues: z.array(z.looseObject({
136
182
  type: z.string(),
137
183
  message: z.string(),
138
184
  severity: z.literal('info').optional(),
185
+ nodeId: z.string().optional(),
186
+ path: z.string().optional(),
187
+ zoneId: z.string().optional(),
188
+ wallIds: z.array(z.string()).optional(),
189
+ area: z.number().optional(),
190
+ at: z.tuple([z.number(), z.number()]).optional(),
139
191
  wallId: z.string().optional(),
192
+ slabId: z.string().optional(),
193
+ levelId: z.string().optional(),
194
+ wallTop: z.number().optional(),
195
+ slabUnderside: z.number().optional(),
196
+ slabTop: z.number().optional(),
197
+ penetration: z.number().optional(),
198
+ coplanar: z.boolean().optional(),
140
199
  end: z.enum(['start', 'end']).optional(),
141
200
  reason: z.enum(['gap', 'crosses', 'parallel', 'rejected']).optional(),
142
201
  gap: z.number().optional(),
143
202
  nearestWallId: z.string().optional(),
144
203
  })),
145
204
  hasIssues: z.boolean(),
205
+ checkpoints: z.array(z.string()).optional(),
206
+ since: jsonObject.optional(),
146
207
  authoredObjects: z
147
208
  .array(z.object({
148
209
  id: z.string(),
@@ -154,11 +215,36 @@ const SHARED_TOOLS = [
154
215
  },
155
216
  envelope: (bridge) => ({ activeSceneId: bridge.getActiveScene()?.id ?? null }),
156
217
  },
218
+ {
219
+ contract: setCheckpointTool,
220
+ operation: AGENT_OPERATIONS.set_checkpoint,
221
+ annotations: ADDITIVE_TOOL_ANNOTATIONS,
222
+ },
223
+ {
224
+ contract: designBriefTool,
225
+ operation: AGENT_OPERATIONS.design_brief,
226
+ annotations: ADDITIVE_TOOL_ANNOTATIONS,
227
+ },
157
228
  {
158
229
  contract: addWallTool,
159
230
  operation: AGENT_OPERATIONS.add_wall,
160
231
  annotations: ADDITIVE_TOOL_ANNOTATIONS,
161
232
  },
233
+ {
234
+ contract: addFenceTool,
235
+ operation: AGENT_OPERATIONS.add_fence,
236
+ annotations: ADDITIVE_TOOL_ANNOTATIONS,
237
+ },
238
+ {
239
+ contract: addCornerWindowTool,
240
+ operation: AGENT_OPERATIONS.add_corner_window,
241
+ annotations: ADDITIVE_TOOL_ANNOTATIONS,
242
+ },
243
+ {
244
+ contract: mergeWindowsTool,
245
+ operation: AGENT_OPERATIONS.merge_windows,
246
+ annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
247
+ },
162
248
  {
163
249
  contract: addLevelTool,
164
250
  operation: AGENT_OPERATIONS.add_level,
@@ -169,6 +255,16 @@ const SHARED_TOOLS = [
169
255
  operation: AGENT_OPERATIONS.create_stair,
170
256
  annotations: ADDITIVE_TOOL_ANNOTATIONS,
171
257
  },
258
+ {
259
+ contract: createRoofTool,
260
+ operation: AGENT_OPERATIONS.create_roof,
261
+ annotations: ADDITIVE_TOOL_ANNOTATIONS,
262
+ },
263
+ {
264
+ contract: moveRoofsToRoofLevelTool,
265
+ operation: AGENT_OPERATIONS.move_roofs_to_roof_level,
266
+ annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
267
+ },
172
268
  {
173
269
  contract: placeItemsTool,
174
270
  operation: AGENT_OPERATIONS.place_items,
@@ -227,12 +323,6 @@ const SHARED_TOOLS = [
227
323
  ...liveSyncOutput,
228
324
  },
229
325
  },
230
- {
231
- contract: searchAssetsTool,
232
- operation: AGENT_OPERATIONS.search_assets,
233
- annotations: READ_ONLY_TOOL_ANNOTATIONS,
234
- catalog: true,
235
- },
236
326
  {
237
327
  contract: paintTool,
238
328
  operation: AGENT_OPERATIONS.paint,
@@ -249,6 +339,54 @@ const SHARED_TOOLS = [
249
339
  ...liveSyncOutput,
250
340
  },
251
341
  },
342
+ {
343
+ contract: placeInRoomTool,
344
+ operation: AGENT_OPERATIONS.place_in_room,
345
+ annotations: ADDITIVE_TOOL_ANNOTATIONS,
346
+ catalog: true,
347
+ outputSchema: {
348
+ ok: z.literal(true),
349
+ itemId: z.string(),
350
+ kind: z.string(),
351
+ placed: z.object({ position: z.array(z.number()), rotationDeg: z.number() }),
352
+ against: z.object({ edge: z.number(), wall: z.string() }).optional(),
353
+ around: z.string().optional(),
354
+ facing: z.string().optional(),
355
+ message: z.string(),
356
+ ...achievedOutput,
357
+ ...liveSyncOutput,
358
+ },
359
+ },
360
+ {
361
+ contract: getSiteTool,
362
+ operation: AGENT_OPERATIONS.get_site,
363
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
364
+ },
365
+ {
366
+ contract: placeBuildingTool,
367
+ operation: AGENT_OPERATIONS.place_building,
368
+ annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
369
+ outputSchema: {
370
+ buildingId: z.string(),
371
+ moved: z.boolean(),
372
+ fit: z.enum(['buildable', 'lot', 'outside']),
373
+ message: z.string(),
374
+ ...achievedOutput,
375
+ ...liveSyncOutput,
376
+ },
377
+ },
378
+ {
379
+ contract: searchAssetsTool,
380
+ operation: AGENT_OPERATIONS.search_assets,
381
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
382
+ catalog: true,
383
+ },
384
+ {
385
+ contract: viewAssetsTool,
386
+ operation: AGENT_OPERATIONS.view_assets,
387
+ annotations: READ_ONLY_TOOL_ANNOTATIONS,
388
+ catalog: true,
389
+ },
252
390
  {
253
391
  contract: searchMaterialsTool,
254
392
  operation: AGENT_OPERATIONS.search_materials,
@@ -263,6 +401,11 @@ const SHARED_TOOLS = [
263
401
  outputSchema: { ...structureOutput, ...achievedOutput },
264
402
  };
265
403
  }),
404
+ {
405
+ contract: runBatchTool,
406
+ operation: AGENT_OPERATIONS.run_batch,
407
+ annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
408
+ },
266
409
  ];
267
410
  export function toPatches(changes) {
268
411
  return [
@@ -284,25 +427,51 @@ export function toPatches(changes) {
284
427
  })),
285
428
  ];
286
429
  }
430
+ /** How an outcome reaches this bridge: its patches, its derived construction, its session record. */
431
+ export function agentRuntime(bridge) {
432
+ return {
433
+ getNodes: () => bridge.getNodes(),
434
+ applyChanges: (changes) => {
435
+ if (changes.materials?.length &&
436
+ (bridge.supportsMaterialUpserts !== true || bridge.getMaterials?.() === undefined))
437
+ refuse('materials_unavailable', 'This host cannot atomically persist native materials; nothing was painted.');
438
+ const next = toPatches(changes);
439
+ if (next.length)
440
+ bridge.applyPatch(next);
441
+ if (changes.collections && Object.keys(changes.collections).length)
442
+ bridge.setCollections(writeCollections(bridge.getCollections(), changes.collections));
443
+ },
444
+ reconcile: () => {
445
+ bridge.deriveStructure();
446
+ },
447
+ keep: (keep) => bridge.agentSession?.keep(keep),
448
+ };
449
+ }
450
+ export function briefInForce(bridge) {
451
+ if (bridge.designBriefRequired === false)
452
+ return undefined;
453
+ const session = bridge.agentSession;
454
+ return session ? (session.read().designBrief ?? null) : undefined;
455
+ }
287
456
  function writesScene(changes) {
288
457
  return Boolean(changes &&
289
458
  (toPatches(changes).length ||
290
459
  (changes.collections && Object.keys(changes.collections).length)));
291
460
  }
292
- export function registerSharedTools(server, bridge, catalog = builtInCatalog) {
461
+ export function registerSharedTools(server, bridge, catalog = builtInCatalog, hosts = {}) {
293
462
  for (const tool of SHARED_TOOLS)
294
- registerOperationTool(server, bridge, tool, catalog);
463
+ registerOperationTool(server, bridge, tool, catalog, hosts);
295
464
  registerCollectionTools(server, bridge);
296
465
  }
297
466
  /**
298
467
  * Registers one operation tool, as the shared tools are: a host adds a plugin's `'operation'`
299
468
  * tools with it, from the plugin's `./agent-tools` contract and `./agent-operations` operation.
300
469
  */
301
- export function registerOperationTool(server, bridge, tool, catalog = builtInCatalog) {
470
+ export function registerOperationTool(server, bridge, tool, catalog = builtInCatalog, hosts = {}) {
302
471
  server.registerTool(tool.contract.name, {
303
472
  title: tool.contract.title,
304
473
  description: tool.contract.description,
305
- inputSchema: tool.contract.input,
474
+ inputSchema: z.object(tool.contract.input),
306
475
  // Loose: a client that listed the tools rejects any field the schema leaves out, and the
307
476
  // operations in core grow fields (verify_scene's guesses) that this list would miss.
308
477
  ...(tool.outputSchema ? { outputSchema: z.looseObject(tool.outputSchema) } : {}),
@@ -312,13 +481,50 @@ export function registerOperationTool(server, bridge, tool, catalog = builtInCat
312
481
  // A copy of the map: a host may write its own in place (the hosted bridge does), and a
313
482
  // "before" that grows with the call reads every creation as unchanged.
314
483
  const before = { ...bridge.getNodes() };
484
+ // The agent's session begins with its first call, before anything is written.
485
+ const first = bridge.agentSession && startOfSession(bridge.agentSession.read(), before);
486
+ if (first)
487
+ bridge.agentSession?.keep(first);
488
+ // Gate before catalog I/O or module compilation.
489
+ const designBrief = briefInForce(bridge);
490
+ if (designBrief !== undefined) {
491
+ const refusal = furnishingRefusal(tool.contract.name, input, designBrief);
492
+ if (refusal)
493
+ return refusalResult(refusal);
494
+ }
495
+ const batchPlacesInRoom = tool.contract.name === runBatchTool.name &&
496
+ Array.isArray(input.calls) &&
497
+ input.calls.some((call) => typeof call === 'object' &&
498
+ call !== null &&
499
+ 'tool' in call &&
500
+ call.tool === placeInRoomTool.name);
501
+ const batchesObjects = tool.contract.name === runBatchTool.name &&
502
+ Array.isArray(input.calls) &&
503
+ input.calls.some((call) => typeof call === 'object' &&
504
+ call !== null &&
505
+ 'tool' in call &&
506
+ call.tool === addObjectTool.name);
315
507
  const context = {
316
- activeLevelId: null,
317
- ...(tool.catalog && { catalog: await catalog() }),
508
+ activeLevelId: bridge.activeLevelId?.() ?? null,
318
509
  ...(bridge.supportsMaterialUpserts && bridge.getMaterials
319
510
  ? { materials: bridge.getMaterials() }
320
511
  : {}),
512
+ ...(bridge.nodeSchema && { nodeSchema: (kind) => bridge.nodeSchema(kind) }),
513
+ session: bridge.agentSession?.read(),
514
+ ...(designBrief !== undefined && { designBrief }),
515
+ ...((tool.catalog ||
516
+ batchPlacesInRoom ||
517
+ assetViewsReadCatalog(tool.contract.name, input) ||
518
+ briefReadsCatalog(tool.contract.name, input)) && { catalog: await catalog() }),
519
+ ...(batchesObjects && {
520
+ batchObjects: await buildBatchObjects(input.calls, hosts.buildObject ?? refuseScripts),
521
+ }),
321
522
  };
523
+ const needs = assetViewNeeds(before, tool.contract.name, input, context);
524
+ if (needs.subjects.length)
525
+ context.assetViews = await (hosts.assetViews ?? defaultAssetViews)(needs.subjects, {
526
+ pictures: needs.pictures,
527
+ });
322
528
  try {
323
529
  outcome = tool.operation(before, input, context);
324
530
  }
@@ -330,21 +536,13 @@ export function registerOperationTool(server, bridge, tool, catalog = builtInCat
330
536
  // An outcome edits the scene through its node changes, its collection writes, or the
331
537
  // follow-up it plans once the host has reconciled; any of them is one history step.
332
538
  if (writesScene(outcome.changes) || outcome.afterReconcile) {
333
- result = bridge.runAsSingleHistoryStep(() => applyAgentOutcome(outcome, {
334
- getNodes: () => bridge.getNodes(),
335
- applyChanges: (changes) => {
336
- const next = toPatches(changes);
337
- if (next.length)
338
- bridge.applyPatch(next);
339
- if (changes.collections && Object.keys(changes.collections).length)
340
- bridge.setCollections(writeCollections(bridge.getCollections(), changes.collections));
341
- },
342
- reconcile: () => {
343
- bridge.deriveStructure();
344
- },
345
- }));
539
+ result = bridge.runAsSingleHistoryStep(() => applyAgentOutcome(outcome, agentRuntime(bridge)));
346
540
  persistence = persistencePayload(await publishLiveSceneSnapshot(bridge, tool.contract.name));
347
541
  }
542
+ else if (outcome.keep) {
543
+ // A checkpoint: kept with the session, nothing in the scene to save.
544
+ result = applyAgentOutcome(outcome, agentRuntime(bridge));
545
+ }
348
546
  // What the scene holds after the call, not only what the call says it built.
349
547
  const achieved = outcome.changes ? achievedChanges(before, outcome.changes) : null;
350
548
  const payload = {
@@ -353,6 +551,21 @@ export function registerOperationTool(server, bridge, tool, catalog = builtInCat
353
551
  ...(tool.envelope?.(bridge) ?? {}),
354
552
  ...persistence,
355
553
  };
554
+ const { previews, ...shown } = payload;
555
+ const pictures = (Array.isArray(previews) ? previews : []).flatMap(({ label, image }) => {
556
+ const [, mimeType, data] = /^data:(image\/[\w.+-]+);base64,(.+)$/.exec(image) ?? [];
557
+ return mimeType && data
558
+ ? [
559
+ { type: 'text', text: label },
560
+ { type: 'image', data, mimeType },
561
+ ]
562
+ : [];
563
+ });
564
+ if (pictures.length)
565
+ return {
566
+ content: [{ type: 'text', text: JSON.stringify(shown) }, ...pictures],
567
+ structuredContent: shown,
568
+ };
356
569
  return {
357
570
  content: [{ type: 'text', text: JSON.stringify(payload) }],
358
571
  structuredContent: payload,
@@ -8,8 +8,8 @@ export declare const structureOutput: {
8
8
  unsaved: z.ZodOptional<z.ZodString>;
9
9
  persistence: z.ZodOptional<z.ZodObject<{
10
10
  status: z.ZodEnum<{
11
- unbound: "unbound";
12
11
  events_unsupported: "events_unsupported";
12
+ unbound: "unbound";
13
13
  }>;
14
14
  warning: z.ZodString;
15
15
  }, z.core.$strip>>;
@@ -31,9 +31,9 @@ export declare const structureOutput: {
31
31
  zoneId: z.ZodString;
32
32
  name: z.ZodString;
33
33
  mode: z.ZodEnum<{
34
- merge: "merge";
35
- delete: "delete";
36
34
  blocked: "blocked";
35
+ delete: "delete";
36
+ merge: "merge";
37
37
  }>;
38
38
  mergedIntoZoneId: z.ZodOptional<z.ZodString>;
39
39
  contents: z.ZodEnum<{
@@ -1,4 +1,4 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
1
+ import type { McpServer } from '@modelcontextprotocol/server';
2
2
  import { z } from 'zod';
3
3
  import type { SceneOperations } from '../../operations/index.js';
4
4
  export declare const createFromTemplateInput: {
@@ -36,8 +36,8 @@ export declare const createFromTemplateOutput: {
36
36
  published: z.ZodBoolean;
37
37
  isDraft: z.ZodBoolean;
38
38
  saveMode: z.ZodEnum<{
39
- draft: "draft";
40
39
  checkpoint: "checkpoint";
40
+ draft: "draft";
41
41
  }>;
42
42
  graphHash: z.ZodOptional<z.ZodString>;
43
43
  levelIds: z.ZodArray<z.ZodString>;
@@ -1 +1 @@
1
- {"version":3,"file":"create-from-template.d.ts","sourceRoot":"","sources":["../../../src/tools/templates/create-from-template.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAOvD,eAAO,MAAM,uBAAuB;;;IAYlC;;;;;OAKG;;;CAGJ,CAAA;AAED,eAAO,MAAM,wBAAwB;;;;IAInC,yEAAyE;;IAEzE,6DAA6D;;;;;;;;;;;;;;;;;;;;;;;;CAuB9D,CAAA;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CA+F3F"}
1
+ {"version":3,"file":"create-from-template.d.ts","sourceRoot":"","sources":["../../../src/tools/templates/create-from-template.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAG7D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAOvD,eAAO,MAAM,uBAAuB;IAClC,EAAE;IAKF,IAAI;IAMJ;;;;;OAKG;IACH,IAAI;IACJ,SAAS;CACV,CAAA;AAED,eAAO,MAAM,wBAAwB;IACnC,UAAU;IACV,WAAW;IACX,SAAS;IACT,yEAAyE;IACzE,WAAW;IACX,6DAA6D;IAC7D,KAAK;;;;;;;;;;;;;;;;;;;;;;;CAsBN,CAAA;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CA+F3F"}
@@ -3,7 +3,7 @@ import { z } from 'zod';
3
3
  import { rehydrateSiteChildren } from '../../lib/rehydrate-site-children.js';
4
4
  import { isTemplateId, TEMPLATES } from '../../templates/index.js';
5
5
  import { DESTRUCTIVE_TOOL_ANNOTATIONS } from '../annotations.js';
6
- import { ErrorCode, throwMcpError } from '../errors.js';
6
+ import { ProtocolErrorCode, throwMcpError } from '../errors.js';
7
7
  import { appendLiveSceneEvent } from '../live-sync.js';
8
8
  import { currentLevelContext, sceneMetaPayload } from '../scene-lifecycle/metadata.js';
9
9
  export const createFromTemplateInput = {
@@ -67,12 +67,12 @@ export function registerCreateFromTemplate(server, bridge) {
67
67
  server.registerTool('create_from_template', {
68
68
  title: 'Create scene from template',
69
69
  description: 'Instantiate a seed Pascal scene template into the bridge. Regenerates all ids before applying. When `save: true` and a SceneStore is wired, also persists the new scene and returns the SceneMeta.',
70
- inputSchema: createFromTemplateInput,
71
- outputSchema: createFromTemplateOutput,
70
+ inputSchema: z.object(createFromTemplateInput),
71
+ outputSchema: z.object(createFromTemplateOutput),
72
72
  annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
73
73
  }, async ({ id, name, save, projectId }) => {
74
74
  if (!isTemplateId(id)) {
75
- throwMcpError(ErrorCode.InvalidParams, `unknown_template: ${id}. Call list_templates for the set of valid ids.`);
75
+ throwMcpError(ProtocolErrorCode.InvalidParams, `unknown_template: ${id}. Call list_templates for the set of valid ids.`);
76
76
  }
77
77
  const entry = TEMPLATES[id];
78
78
  // Clone: regenerate ids so each instantiation is independent.
@@ -87,7 +87,7 @@ export function registerCreateFromTemplate(server, bridge) {
87
87
  }
88
88
  catch (err) {
89
89
  const msg = err instanceof Error ? err.message : String(err);
90
- throwMcpError(ErrorCode.InternalError, `apply_failed: ${msg}`);
90
+ throwMcpError(ProtocolErrorCode.InternalError, `apply_failed: ${msg}`);
91
91
  }
92
92
  const basePayload = {
93
93
  templateId: entry.id,
@@ -143,7 +143,7 @@ export function registerCreateFromTemplate(server, bridge) {
143
143
  }
144
144
  catch (err) {
145
145
  const msg = err instanceof Error ? err.message : String(err);
146
- throwMcpError(ErrorCode.InternalError, `save_failed: ${msg}`);
146
+ throwMcpError(ProtocolErrorCode.InternalError, `save_failed: ${msg}`);
147
147
  }
148
148
  });
149
149
  }
@@ -1,4 +1,4 @@
1
- import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
1
+ import type { McpServer } from '@modelcontextprotocol/server';
2
2
  import { z } from 'zod';
3
3
  import type { SceneOperations } from '../../operations/index.js';
4
4
  export declare const createHouseFromBriefInput: {
@@ -1 +1 @@
1
- {"version":3,"file":"create-house-from-brief.d.ts","sourceRoot":"","sources":["../../../src/tools/templates/create-house-from-brief.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAOvD,eAAO,MAAM,yBAAyB;;;;;;;;;CASrC,CAAA;AAED,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;;CAmBtC,CAAA;AAmCD,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAsI7F"}
1
+ {"version":3,"file":"create-house-from-brief.d.ts","sourceRoot":"","sources":["../../../src/tools/templates/create-house-from-brief.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAG7D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAOvD,eAAO,MAAM,yBAAyB;IACpC,KAAK;IACL,SAAS;IACT,WAAW;IACX,YAAY;IACZ,KAAK;IACL,KAAK;IACL,WAAW;IACX,WAAW;CACZ,CAAA;AAED,eAAO,MAAM,0BAA0B;IACrC,SAAS;IACT,SAAS;IACT,GAAG;IACH,OAAO;IACP,SAAS;IACT,OAAO;IACP,UAAU;IACV,SAAS;IACT,SAAS;IACT,QAAQ;IACR,cAAc;IACd,UAAU;;;;IAIV,OAAO;IACP,WAAW;IACX,QAAQ;CACT,CAAA;AAmCD,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAsI7F"}