@pascal-app/mcp 1.0.7 → 1.1.1

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/README.md CHANGED
@@ -431,7 +431,7 @@ is a "pitch" fraction like `"1/4 pitch"`, which is rise over span.
431
431
  | `furnish_room` | Furnish a room for its type from the host's item catalog: the main piece against the wall facing the door, the rest along a side wall. Nothing lands in a door's clear zone or on another item: a piece that does not fit is nudged, else skipped, and `skipped` says why. | `{ zoneId?, polygon?, levelId?, roomType, doorWallIndex? }` | `{ placed, itemIds, skipped, doorWallIndex, doorsDetected }`; refusals `room_required`, `roof_level`, `level_not_found`, `no_catalog` |
432
432
  | `add_corner_window` | A window wrapping the corner where two walls meet: one window per wall, both running to the corner and joined, the glass fused (a fixed window) or at a thin post (the other window types, whose frames stay), at any angle from 10° to 170° (a square corner, a bay's 135°, an acute one). `corner` is the point the walls of `levelId` end at (default the floor in view: stacked storeys share their corners' points), `[x, z]` in the level's plan as `get_walls` gives their ends; `width` is how far the glass runs along each wall from the corner (default 1.2 m) and `widths` overrides it per wall id; `post` is `'none'` (the glass fused, a fixed window's default) or `'post'` (the other types' default and only choice); `height` defaults to 1.5 m and the window is centred on the wall unless `sillHeight` (its bottom edge above the floor) is given; `wallIds` (two) is needed only when more than two walls end at the corner; `windowType` and `style` are `add_window`'s (a style needs a Fixed window). By hand: select a window that ends at a corner and use the window panel's Corner section (Wrap the corner, This side and Other side, Fused glass or Corner post for a fixed window, Unwrap). | `{ corner: [x, z], levelId? (or level), wallIds?, width?, widths?, height?, sillHeight?, post?, windowType?, style? }` | `{ windowIds, wallIds, angle, post, achieved }`; refusals `no_corner` (no two walls end there), `corner_ambiguous` (more than two do: pass `wallIds`), `corner_angle` (under 10° or over 170°: use `add_window`), `corner_not_fused` (`post: 'none'` on a type other than fixed), `width_exceeds_wall`, `style_needs_fixed_window` |
433
433
  | `merge_windows` | Make two windows that exist one window, as the editor's Merge windows button does; nothing is recreated. On two walls that meet at a corner, each window within 0.30 m of it, they become one corner window (the `add_corner_window` result): each is slid to the corner keeping its width, the glass fused (a fixed window's; other types meet at a post), and both take the first window's height, sill and type, so name first the window whose look you want; the corner is refused over 170° or under 10°. On one wall, two windows with at most 0.30 m between them and nothing between become one window spanning both (overlapping or one inside the other, their union), from the lower sill to the higher head, with the first window's type and style. Use it for two windows already built that open a corner or one wide opening (`verify_scene`'s `corner_unjoined` names a pair for the first); `add_corner_window` builds a new pair. By hand: select two windows and use Merge windows in the multi-selection panel, or place or drag a window near a corner where one waits and click or release (Alt places it plain). | `{ windowIds: [a, b] }` | `{ kind: 'corner' \| 'same_wall', windowIds, removedIds, achieved }`; refusals `window_not_found`, `not_a_window`, `same_window`, `window_not_on_wall`, `window_already_joined` (unwrap it first), `windows_already_joined`, `windows_not_adjacent`, `opening_between`, `walls_not_meeting`, `corner_angle`, `window_not_at_corner`, `height_exceeds_wall`, `opening_overlap` |
434
- | `apply_patch` | Batched create/update/delete/move, validated and dry-run before commit. Batch-first is the default: send all create/update/delete ops for a build step in one atomic call (stable order, later ops may reference earlier created ids); do not loop one-op calls. A create with an id already in the scene is refused (`node_exists`); delete it earlier in the same patch to replace it. Updates cannot change `id` or `type` (`identity_change`), `object` or `children` (`immutable_field`), move a node under a missing or childless parent (`invalid_parent`), or add schema issues (`invalid_update`); default gutters and downspouts a delete regenerates are addressable only in a later call (`regenerated_default`). Refusals are tool errors with JSON text `{ code, patchIndex, id, message }`. A level created without a `height`, or an update that clears it, gets the height the editor's load derives from what the level holds (an empty level: 2.5 m), one line per level in `notes`. A roof the patch makes or moves onto the storey whose walls carry it is seated on the level above those walls, one line per roof in `notes`; a new storey at or above the roof level moves it up. Refused: storey content on a roof level (`roof_level`), declaring a level that holds storey content the roof level (`roof_level_holds_storey`), floor indexes that put the roof level under a storey (`roof_level_on_top`). | `{ patches: Patch[] }` | `{ applied: number, notes?: string[] }` |
434
+ | `apply_patch` | Batched create/update/delete/move, validated and dry-run before commit. Batch-first is the default: send all create/update/delete ops for a build step in one atomic call (stable order, later ops may reference earlier created ids); do not loop one-op calls. A create with an id already in the scene is refused (`node_exists`); delete it earlier in the same patch to replace it. Updates cannot change `id` or `type` (`identity_change`), `object` or `children` (`immutable_field`), move a node under a missing or childless parent, itself or one of its descendants (`invalid_parent`), or add schema issues (`invalid_update`); default gutters and downspouts a delete regenerates are addressable only in a later call (`regenerated_default`). Refusals are tool errors with JSON text `{ code, patchIndex, id, message }`. A level created without a `height`, or an update that clears it, gets the height the editor's load derives from what the level holds (an empty level: 2.5 m), one line per level in `notes`. A roof the patch makes or moves onto the storey whose walls carry it is seated on the level above those walls, one line per roof in `notes`; a new storey at or above the roof level moves it up. Refused: storey content on a roof level (`roof_level`), declaring a level that holds storey content the roof level (`roof_level_holds_storey`), floor indexes that put the roof level under a storey (`roof_level_on_top`). | `{ patches: Patch[] }` | `{ applied: number, notes?: string[] }` |
435
435
  | `add_level` | Add an empty level to a building, as the editor does: on top of its storeys, under its roof level (which moves up one), or below the lowest for a basement (every existing floor, the ground floor included, keeps its place). Omit `buildingId` for the scene's only building. | `{ buildingId?, position?, name?, height? }` | `{ levelId, buildingId, floorIndex, height, shiftedLevelIds? }`; refusals `building_required`, `building_not_found`, `no_building` |
436
436
  | `set_ground_level` | Make a level the building's ground floor, as the level panel's "Set as ground floor": the building names it, is seated so that floor stands on grade (height only, the plan stays), and the floor indexes are renumbered so it is 0 and the levels under it are the basements. | `{ levelId }` | `{ levelId, buildingId, floorElevation, gradeElevation, movedBuildingBy, renumbered, unchanged? }`; refusals `level_not_found`, `not_a_level`, `no_building`, `not_a_storey` |
437
437
  | `add_wall` | Add a wall to a level, straight or, with `curveOffset`, an arc. Omit `levelId` for the lowest storey. wallType railing builds a guard (default 0.9144 m high); railing sets style, posts, infill and cap; postSize sets thickness. | `{ start, end, levelId?, thickness?, height?, curveOffset?, type?, wallType?, railing? }` | `{ wallId, levelId, length }`; refusals `roof_level`, `wall_too_short`, `level_not_found`, `unknown_wall_type` |
@@ -174,10 +174,7 @@ export declare class SceneBridge {
174
174
  undo(steps?: number): number;
175
175
  /** Redo. Returns the number of steps actually redone. */
176
176
  redo(steps?: number): number;
177
- /**
178
- * Zod-validate every node in the scene. Reports one error per failed node,
179
- * concatenating Zod issue paths.
180
- */
177
+ /** Validate node schemas and global parent-chain acyclicity. */
181
178
  validateScene(): ValidationResult;
182
179
  /**
183
180
  * Drain the dirtyNodes set. Returns the ids that were present. No-op for
@@ -1 +1 @@
1
- {"version":3,"file":"scene-bridge.d.ts","sourceRoot":"","sources":["../../src/bridge/scene-bridge.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scene-bridge.d.ts","sourceRoot":"","sources":["../../src/bridge/scene-bridge.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  // Side-effect import MUST come first: installs RAF polyfill before core loads.
2
2
  import './node-shims.js';
3
- import { applyStructureReconciliation, assertDerivedNodeWrites, HIDDEN_SITE_NOTE, pauseSceneHistory, pauseSpaceDetection, planNodeDeletion, planWallDeletion, resumeSceneHistory, resumeSpaceDetection, structureChangeBatch, } from '@pascal-app/core';
3
+ import { applyStructureReconciliation, assertDerivedNodeWrites, HIDDEN_SITE_NOTE, hierarchyCycleErrors, pauseSceneHistory, pauseSpaceDetection, planNodeDeletion, planWallDeletion, resumeSceneHistory, resumeSpaceDetection, structureChangeBatch, } from '@pascal-app/core';
4
4
  import { declareActionDependencies, liveSceneRuntime, materialize, runAsSettledSceneStep, } from '@pascal-app/core/runtime';
5
5
  import { AnyNode as AnyNodeSchema, generateId, parseNode, SceneMaterial, } from '@pascal-app/core/schema';
6
6
  import { InMemoryAgentSession } from './agent-session.js';
@@ -555,10 +555,7 @@ export class SceneBridge {
555
555
  return Math.max(0, before - this.store.temporal.getState().futureStates.length);
556
556
  });
557
557
  }
558
- /**
559
- * Zod-validate every node in the scene. Reports one error per failed node,
560
- * concatenating Zod issue paths.
561
- */
558
+ /** Validate node schemas and global parent-chain acyclicity. */
562
559
  validateScene() {
563
560
  const errors = [];
564
561
  const warnings = [];
@@ -578,6 +575,7 @@ export class SceneBridge {
578
575
  });
579
576
  }
580
577
  }
578
+ errors.push(...hierarchyCycleErrors(nodes));
581
579
  return { valid: errors.length === 0, errors, warnings };
582
580
  }
583
581
  /**
package/dist/server.d.ts CHANGED
@@ -46,6 +46,8 @@ export type CreatePascalMcpServerOptions = {
46
46
  sceneViews?: SceneViewHost;
47
47
  /** Reads catalog items' pictures and surfaces before view_assets and paint. */
48
48
  assetViews?: PlanReadingHosts['assetViews'];
49
+ /** A pause after each call of a run_batch, for a host with a scene of its own per request. */
50
+ betweenBatchCalls?: PlanReadingHosts['betweenBatchCalls'];
49
51
  /** Lines the host adds to what a client reads at connect (the person's own settings). */
50
52
  instructions?: string;
51
53
  };
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EAKV,MAAM,8BAA8B,CAAA;AACrC,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAG7B,MAAM,mCAAmC,CAAA;AAE1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAA;AAG1E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEjD,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAC7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAKzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAIvD,MAAM,MAAM,qBAAqB,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE;IAClD,IAAI,EAAE,MAAM,CAAA;IACZ,wEAAwE;IACxE,SAAS,EAAE,OAAO,CAAA;IAClB,MAAM,EAAE,WAAW,CAAA;IACnB,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;CAC/B,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;AAErB,MAAM,MAAM,4BAA4B,GAAG;IACzC,MAAM,EAAE,WAAW,CAAA;IACnB,UAAU,CAAC,EAAE,eAAe,CAAA;IAC5B,mFAAmF;IACnF,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,WAAW,CAAC,EAAE,qBAAqB,CAAA;IACnC;;;OAGG;IACH,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB,+FAA+F;IAC/F,eAAe,CAAC,EAAE,kBAAkB,CAAA;IACpC,kGAAkG;IAClG,YAAY,CAAC,EAAE,eAAe,CAAA;IAC9B,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,qBAAqB,CAAA;IAChC,yFAAyF;IACzF,KAAK,CAAC,EAAE,uBAAuB,CAAA;IAC/B,8FAA8F;IAC9F,QAAQ,CAAC,EAAE,iBAAiB,CAAA;IAC5B,4FAA4F;IAC5F,UAAU,CAAC,EAAE,aAAa,CAAA;IAC1B,+EAA+E;IAC/E,UAAU,CAAC,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAA;IAC3C,yFAAyF;IACzF,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,4BAA4B,GAAG,SAAS,CA8BnF"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,SAAS,EAKV,MAAM,8BAA8B,CAAA;AACrC,OAAO,EACL,KAAK,qBAAqB,EAC1B,KAAK,iBAAiB,EACtB,KAAK,uBAAuB,EAG7B,MAAM,mCAAmC,CAAA;AAE1C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,EAAyB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAA;AAG1E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAA;AAEjD,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAC7E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAKzD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAA;AAIvD,MAAM,MAAM,qBAAqB,GAAG,CAAC,MAAM,EAAE,KAAK,EAAE;IAClD,IAAI,EAAE,MAAM,CAAA;IACZ,wEAAwE;IACxE,SAAS,EAAE,OAAO,CAAA;IAClB,MAAM,EAAE,WAAW,CAAA;IACnB,OAAO,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;CAC/B,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;AAErB,MAAM,MAAM,4BAA4B,GAAG;IACzC,MAAM,EAAE,WAAW,CAAA;IACnB,UAAU,CAAC,EAAE,eAAe,CAAA;IAC5B,mFAAmF;IACnF,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;;;;OAIG;IACH,WAAW,CAAC,EAAE,qBAAqB,CAAA;IACnC;;;OAGG;IACH,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB,+FAA+F;IAC/F,eAAe,CAAC,EAAE,kBAAkB,CAAA;IACpC,kGAAkG;IAClG,YAAY,CAAC,EAAE,eAAe,CAAA;IAC9B,4FAA4F;IAC5F,QAAQ,CAAC,EAAE,qBAAqB,CAAA;IAChC,yFAAyF;IACzF,KAAK,CAAC,EAAE,uBAAuB,CAAA;IAC/B,8FAA8F;IAC9F,QAAQ,CAAC,EAAE,iBAAiB,CAAA;IAC5B,4FAA4F;IAC5F,UAAU,CAAC,EAAE,aAAa,CAAA;IAC1B,+EAA+E;IAC/E,UAAU,CAAC,EAAE,gBAAgB,CAAC,YAAY,CAAC,CAAA;IAC3C,8FAA8F;IAC9F,iBAAiB,CAAC,EAAE,gBAAgB,CAAC,mBAAmB,CAAC,CAAA;IACzD,yFAAyF;IACzF,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,4BAA4B,GAAG,SAAS,CA+BnF"}
package/dist/server.js CHANGED
@@ -29,6 +29,7 @@ export function createPascalMcpServer(opts) {
29
29
  readFile: opts.readFile,
30
30
  sceneViews: opts.sceneViews,
31
31
  assetViews: opts.assetViews,
32
+ betweenBatchCalls: opts.betweenBatchCalls,
32
33
  });
33
34
  registerVisionTools(server, operations);
34
35
  if (opts.services)
@@ -22,7 +22,7 @@ export const applyPatchOutput = {
22
22
  export function registerApplyPatch(server, bridge) {
23
23
  server.registerTool('apply_patch', {
24
24
  title: 'Apply patch',
25
- description: "Apply a batch of create/update/delete operations atomically. All patches are validated before any are applied; the entire batch forms a single undo step. Batch-first is the default: prefer one apply_patch call containing all create/update/delete ops for a build step, in stable order so later ops can reference ids created by earlier ops. A single call is atomic (all or nothing) and pays the snapshot and save cost once; do not loop one-op calls. A create whose id already exists fails the whole patch with node_exists; to replace a node, delete it earlier in the same patch. An update cannot change id or type (identity_change) or object or children (immutable_field); restating the current value is fine. A parentId change must name an existing node that holds children (invalid_parent). An update cannot add schema issues to a node (invalid_update). After a delete or a roof update that regenerates a roof's default gutters, those gutters and their downspouts are addressable only in a later call (regenerated_default). A roof created or moved onto the storey whose walls carry it goes to the level above them (the roof level over a top storey), said in notes; storey content (walls, zones, slabs, ceilings, items, stairs) on a roof level (roof_level), a walled level declared the roof level (roof_level_holds_storey) and floor indexes putting a roof level under a storey (roof_level_on_top) are refused. Refusals come back as a tool error whose text is JSON {code, patchIndex, id, message}.",
25
+ description: "Apply a batch of create/update/delete operations atomically. All patches are validated before any are applied; the entire batch forms a single undo step. Batch-first is the default: prefer one apply_patch call containing all create/update/delete ops for a build step, in stable order so later ops can reference ids created by earlier ops. A single call is atomic (all or nothing) and pays the snapshot and save cost once; do not loop one-op calls. A create whose id already exists fails the whole patch with node_exists; to replace a node, delete it earlier in the same patch. An update cannot change id or type (identity_change) or object or children (immutable_field); restating the current value is fine. A parentId change must name an existing node that holds children and cannot point to the node or one of its descendants (invalid_parent). An update cannot add schema issues to a node (invalid_update). After a delete or a roof update that regenerates a roof's default gutters, those gutters and their downspouts are addressable only in a later call (regenerated_default). A roof created or moved onto the storey whose walls carry it goes to the level above them (the roof level over a top storey), said in notes; storey content (walls, zones, slabs, ceilings, items, stairs) on a roof level (roof_level), a walled level declared the roof level (roof_level_holds_storey) and floor indexes putting a roof level under a storey (roof_level_on_top) are refused. Refusals come back as a tool error whose text is JSON {code, patchIndex, id, message}.",
26
26
  inputSchema: z.object(applyPatchInput),
27
27
  outputSchema: z.object(applyPatchOutput),
28
28
  annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
@@ -23,5 +23,5 @@ export type ToolHosts = PlanReadingHosts & {
23
23
  readFile?: ProjectFileReader;
24
24
  sceneViews?: SceneViewHost;
25
25
  };
26
- export declare function registerTools(server: McpServer, operations: SceneOperations, { catalog, geometryScripts, objectMakers, readFile, sceneViews, assetViews }?: ToolHosts): void;
26
+ export declare function registerTools(server: McpServer, operations: SceneOperations, { catalog, geometryScripts, objectMakers, readFile, sceneViews, assetViews, betweenBatchCalls, }?: ToolHosts): void;
27
27
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAC7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAA;AAC1E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAEpD,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,eAAe,EAGrB,MAAM,cAAc,CAAA;AAErB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAcnD,OAAO,EAAE,KAAK,gBAAgB,EAAuB,MAAM,gBAAgB,CAAA;AAI3E,OAAO,EAAqB,KAAK,aAAa,EAAE,MAAM,cAAc,CAAA;AAEpE;;;;;;;GAOG;AACH,gFAAgF;AAChF,MAAM,MAAM,SAAS,GAAG,gBAAgB,GAAG;IACzC,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB,eAAe,CAAC,EAAE,kBAAkB,CAAA;IACpC,gGAAgG;IAChG,YAAY,CAAC,EAAE,eAAe,CAAA;IAC9B,6FAA6F;IAC7F,QAAQ,CAAC,EAAE,iBAAiB,CAAA;IAC5B,UAAU,CAAC,EAAE,aAAa,CAAA;CAC3B,CAAA;AAED,wBAAgB,aAAa,CAC3B,MAAM,EAAE,SAAS,EACjB,UAAU,EAAE,eAAe,EAC3B,EAAE,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,UAAU,EAAE,GAAE,SAAc,GAC3F,IAAI,CA4BN"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAC7D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAA;AAC1E,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAEpD,OAAO,EAEL,KAAK,kBAAkB,EACvB,KAAK,eAAe,EAGrB,MAAM,cAAc,CAAA;AAErB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAcnD,OAAO,EAAE,KAAK,gBAAgB,EAAuB,MAAM,gBAAgB,CAAA;AAI3E,OAAO,EAAqB,KAAK,aAAa,EAAE,MAAM,cAAc,CAAA;AAEpE;;;;;;;GAOG;AACH,gFAAgF;AAChF,MAAM,MAAM,SAAS,GAAG,gBAAgB,GAAG;IACzC,OAAO,CAAC,EAAE,YAAY,CAAA;IACtB,eAAe,CAAC,EAAE,kBAAkB,CAAA;IACpC,gGAAgG;IAChG,YAAY,CAAC,EAAE,eAAe,CAAA;IAC9B,6FAA6F;IAC7F,QAAQ,CAAC,EAAE,iBAAiB,CAAA;IAC5B,UAAU,CAAC,EAAE,aAAa,CAAA;CAC3B,CAAA;AAED,wBAAgB,aAAa,CAC3B,MAAM,EAAE,SAAS,EACjB,UAAU,EAAE,eAAe,EAC3B,EACE,OAAO,EACP,eAAe,EACf,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,UAAU,EACV,iBAAiB,GAClB,GAAE,SAAc,GAChB,IAAI,CA6BN"}
@@ -19,10 +19,11 @@ import { registerTemplateTools } from './templates/index.js';
19
19
  import { registerUndo } from './undo.js';
20
20
  import { registerVariantTools } from './variants/index.js';
21
21
  import { registerViewScene } from './view-scene.js';
22
- export function registerTools(server, operations, { catalog, geometryScripts, objectMakers, readFile, sceneViews, assetViews } = {}) {
22
+ export function registerTools(server, operations, { catalog, geometryScripts, objectMakers, readFile, sceneViews, assetViews, betweenBatchCalls, } = {}) {
23
23
  registerGetScene(server, operations);
24
24
  registerSharedTools(server, operations, catalog, {
25
25
  assetViews,
26
+ betweenBatchCalls,
26
27
  buildObject: (input) => compileObjectOnHost(geometryScripts, operations, input),
27
28
  });
28
29
  registerAddColumn(server, operations, geometryScripts);
@@ -1 +1 @@
1
- {"version":3,"file":"patch-guards.d.ts","sourceRoot":"","sources":["../../src/tools/patch-guards.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EAKvB,MAAM,kBAAkB,CAAA;AAKzB,OAAO,EAAE,OAAO,EAAE,KAAK,SAAS,EAAyB,MAAM,yBAAyB,CAAA;AACxF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AAEnD,MAAM,MAAM,gBAAgB,GACxB,aAAa,GACb,iBAAiB,GACjB,iBAAiB,GACjB,gBAAgB,GAChB,gBAAgB,GAChB,qBAAqB,GACrB,gBAAgB,GAEhB,eAAe,GACf,kBAAkB,GAClB,gBAAgB,GAChB,gBAAgB,GAEhB,YAAY,GACZ,yBAAyB,GACzB,mBAAmB,CAAA;AAEvB;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,EAAE,GAAG,IAAI,CAAA;CACjF,CAAA;AAID,4FAA4F;AAC5F,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,QAKnD;AAED,qFAAqF;AACrF,wBAAgB,cAAc,CAC5B,OAAO,EAAE,KAAK,EAAE,EAChB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACvC,MAAM,EAAE,CAKV;AAED;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IAExC,QAAQ,CAAC,IAAI,EAAE,gBAAgB;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM;IAHzB,YACW,IAAI,EAAE,gBAAgB,EACtB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACvB,MAAM,EAAE,MAAM,EAIf;CACF;AAkDD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,SAAS,KAAK,EAAE,EACzB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACxC,WAAW,EAAE,SAAS,SAAS,EAAE,EACjC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,gBAAgB,GAC9E,IAAI,CAuLN"}
1
+ {"version":3,"file":"patch-guards.d.ts","sourceRoot":"","sources":["../../src/tools/patch-guards.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,gBAAgB,EACrB,KAAK,iBAAiB,EAMvB,MAAM,kBAAkB,CAAA;AAKzB,OAAO,EAAE,OAAO,EAAE,KAAK,SAAS,EAAyB,MAAM,yBAAyB,CAAA;AACxF,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AAEnD,MAAM,MAAM,gBAAgB,GACxB,aAAa,GACb,iBAAiB,GACjB,iBAAiB,GACjB,gBAAgB,GAChB,gBAAgB,GAChB,qBAAqB,GACrB,gBAAgB,GAEhB,eAAe,GACf,kBAAkB,GAClB,gBAAgB,GAChB,gBAAgB,GAEhB,YAAY,GACZ,yBAAyB,GACzB,mBAAmB,CAAA;AAEvB;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,EAAE,GAAG,IAAI,CAAA;CACjF,CAAA;AAID,4FAA4F;AAC5F,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,QAKnD;AAED,qFAAqF;AACrF,wBAAgB,cAAc,CAC5B,OAAO,EAAE,KAAK,EAAE,EAChB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GACvC,MAAM,EAAE,CAKV;AAED;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IAExC,QAAQ,CAAC,IAAI,EAAE,gBAAgB;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM;IAHzB,YACW,IAAI,EAAE,gBAAgB,EACtB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACvB,MAAM,EAAE,MAAM,EAIf;CACF;AAkDD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,SAAS,KAAK,EAAE,EACzB,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACxC,WAAW,EAAE,SAAS,SAAS,EAAE,EACjC,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE,iBAAiB,EAAE,GAAG,EAAE,SAAS,EAAE,KAAK,gBAAgB,GAC9E,IAAI,CAwMN"}
@@ -1,4 +1,4 @@
1
- import { nodeRegistry, planNodeDeletion, previewDefaultGutterRefresh, validateNodeRelations, } from '@pascal-app/core';
1
+ import { nodeRegistry, planNodeDeletion, previewDefaultGutterRefresh, validateNodeRelations, wouldCreateHierarchyCycle, } from '@pascal-app/core';
2
2
  import { scriptedFieldRefusal, unknownMaterialPresetRefusal, } from '@pascal-app/core/agent-operations';
3
3
  import { AnyNode, nodeKindOf, parseNode } from '@pascal-app/core/schema';
4
4
  const guards = [];
@@ -185,6 +185,9 @@ export function assertPatchKeepsIdentity(patches, nodes, rootNodeIds, planDeleti
185
185
  if (patch.parentId !== undefined && !at(patch.parentId)) {
186
186
  throw new Error(`invalid patch: patches[${index}] create parentId "${patch.parentId}" not found`);
187
187
  }
188
+ if (wouldCreateHierarchyCycle(node.id, parentId, at)) {
189
+ throw new PatchRefusedError('invalid_parent', index, node.id, `create cannot place "${node.id}" under ${JSON.stringify(parentId)}: it would create a hierarchy cycle.`);
190
+ }
188
191
  put({ ...node, parentId });
189
192
  const parent = parentId ? at(parentId) : undefined;
190
193
  if (parent)
@@ -227,6 +230,10 @@ export function assertPatchKeepsIdentity(patches, nodes, rootNodeIds, planDeleti
227
230
  }
228
231
  }
229
232
  if ('parentId' in data && data.parentId !== current.parentId) {
233
+ const newParentId = typeof data.parentId === 'string' ? data.parentId : null;
234
+ if (wouldCreateHierarchyCycle(patch.id, newParentId, at)) {
235
+ throw new PatchRefusedError('invalid_parent', index, patch.id, `update cannot move "${patch.id}" under ${JSON.stringify(data.parentId)}: it would create a hierarchy cycle.`);
236
+ }
230
237
  const newParent = typeof data.parentId === 'string' ? at(data.parentId) : undefined;
231
238
  if (!(newParent && Array.isArray(newParent.children))) {
232
239
  throw new PatchRefusedError('invalid_parent', index, patch.id, `update cannot move "${patch.id}" under ${JSON.stringify(data.parentId)}: ${newParent ? `a ${newParent.type} holds no children` : 'no such node'}.`);
@@ -24,6 +24,13 @@ export type PlanReadingHosts = {
24
24
  assetViews?: AssetViewLoader;
25
25
  /** Compiles one add_object module for a batch before the synchronous operations run. */
26
26
  buildObject?: (input: Omit<AddObjectInput, 'compiled'>) => Promise<CompiledGeometryScript>;
27
+ /**
28
+ * Awaited after each call of a run_batch, so the host's timers and I/O run during a long one.
29
+ * Only for a host whose scene no other call can reach meanwhile (a bridge of its own for each
30
+ * request): the batch is planned on the scene as its first call found it and applied after its
31
+ * last. Without it a batch runs uninterrupted.
32
+ */
33
+ betweenBatchCalls?: () => Promise<void>;
27
34
  };
28
35
  export declare function toPatches(changes: SceneChanges): Patch[];
29
36
  /** How an outcome reaches this bridge: its patches, its derived construction, its session record. */
@@ -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,8BAA8B,CAAA;AAC7D,OAAO,EACL,KAAK,cAAc,EAGnB,KAAK,gBAAgB,EACrB,KAAK,cAAc,EACnB,KAAK,eAAe,EASpB,KAAK,YAAY,EAGlB,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,iBAAiB,EAUtB,KAAK,WAAW,EAyCjB,MAAM,8BAA8B,CAAA;AAGrC,OAAO,EAGL,KAAK,sBAAsB,EAI5B,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;AAUnE;;;;;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;CAChE,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;AAoeD,wBAAgB,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,KAAK,EAAE,CAmBxD;AAED,qGAAqG;AACrG,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,GAAG,gBAAgB,CAyBtE;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,GAAG,WAAW,GAAG,IAAI,GAAG,SAAS,CAIpF;AA+CD,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,CAwIN"}
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,EAUtB,KAAK,WAAW,EAyCjB,MAAM,8BAA8B,CAAA;AAGrC,OAAO,EAGL,KAAK,sBAAsB,EAI5B,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;AAUnE;;;;;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;CAChE,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;IAC1F;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CACxC,CAAA;AAoeD,wBAAgB,SAAS,CAAC,OAAO,EAAE,YAAY,GAAG,KAAK,EAAE,CAmBxD;AAED,qGAAqG;AACrG,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,GAAG,gBAAgB,CAyBtE;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,eAAe,GAAG,WAAW,GAAG,IAAI,GAAG,SAAS,CAIpF;AAoED,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,CA+IN"}
@@ -1,4 +1,4 @@
1
- import { AGENT_OPERATIONS, achievedChanges, applyAgentOutcome, assetViewNeeds, buildBatchObjects, createAssetViewLoader, createFetchAssetViewIO, furnishingRefusal, readCatalog, startOfSession, writeCollections, } from '@pascal-app/core/agent-operations';
1
+ import { AGENT_OPERATIONS, achievedChanges, applyAgentOutcome, assetViewNeeds, buildBatchObjects, createAssetViewLoader, createFetchAssetViewIO, furnishingRefusal, RUN_BATCH_STEPS, readCatalog, startOfSession, writeCollections, } from '@pascal-app/core/agent-operations';
2
2
  import { addCornerWindowTool, addFenceTool, addLevelTool, addObjectTool, addWallTool, createRoofTool, createRoomTool, createStairTool, createUnitTool, deleteNodeTool, designBriefTool, duplicateLevelTool, duplicateNodeOutput, duplicateNodeTool, findByTypeTool, fitStairTool, furnishRoomTool, getLevelSummaryTool, getNodeTool, getSiteTool, getWallsTool, getZonesTool, isAgentRefusal, listLevelsTool, listUnitsTool, measureStairTool, mergeWindowsTool, moveNodeOutput, moveNodeTool, moveRoofsToRoofLevelTool, paintTool, placeBuildingTool, placeDesignTool, placeInRoomTool, placeItemsTool, ROOM_TOOL_CONTRACTS, refuse, runBatchTool, searchAssetsTool, searchMaterialsTool, setCheckpointTool, setGroundLevelTool, setUnitMembersTool, setWallTypeTool, surfaceDescription, validateDesignTool, verifySceneModelResult, verifySceneTool, viewAssetsTool, } from '@pascal-app/core/agent-tools';
3
3
  import { DesignValidationSchema } from '@pascal-app/core/procedural-items';
4
4
  import { untracked } from '@pascal-app/core/runtime';
@@ -523,8 +523,9 @@ function writesScene(changes) {
523
523
  (changes.collections && Object.keys(changes.collections).length)));
524
524
  }
525
525
  /**
526
- * search_assets as the model reads it: each item's id, name and placement facts; the category
527
- * and the floor's null attachTo stay in the structured result.
526
+ * search_assets as the model reads it: each item's id, name and placement facts, and the params
527
+ * of one built from a script (add_object changes them once placed); the category and the floor's
528
+ * null attachTo stay in the structured result.
528
529
  */
529
530
  function searchAssetsModelResult(output) {
530
531
  const groups = output.groups;
@@ -532,11 +533,12 @@ function searchAssetsModelResult(output) {
532
533
  ...output,
533
534
  groups: groups.map((group) => ({
534
535
  ...group,
535
- results: group.results.map(({ id, name, dimensions, attachTo }) => ({
536
+ results: group.results.map(({ id, name, dimensions, attachTo, params }) => ({
536
537
  id,
537
538
  name,
538
539
  dimensions,
539
540
  ...(attachTo && { attachTo }),
541
+ ...(params && { params }),
540
542
  })),
541
543
  })),
542
544
  };
@@ -553,6 +555,15 @@ function modelResult(name, output, input) {
553
555
  return searchAssetsModelResult(output);
554
556
  return output;
555
557
  }
558
+ /** A batch one call at a time, each in the scene's scope, the host's pause after each. */
559
+ async function runBatchInSteps(bridge, batch, pause) {
560
+ for (;;) {
561
+ const step = inScene(bridge, () => batch.next());
562
+ if (step.done)
563
+ return step.value;
564
+ await pause();
565
+ }
566
+ }
556
567
  export function registerSharedTools(server, bridge, catalog = builtInCatalog, hosts = {}) {
557
568
  for (const tool of SHARED_TOOLS)
558
569
  registerOperationTool(server, bridge, tool, catalog, hosts);
@@ -627,7 +638,10 @@ export function registerOperationTool(server, bridge, tool, catalog = builtInCat
627
638
  pictures: needs.pictures,
628
639
  });
629
640
  try {
630
- outcome = inScene(bridge, () => tool.operation(nodes, input, context));
641
+ outcome =
642
+ tool.contract.name === runBatchTool.name && hosts.betweenBatchCalls
643
+ ? await runBatchInSteps(bridge, RUN_BATCH_STEPS(nodes, input, context), hosts.betweenBatchCalls)
644
+ : inScene(bridge, () => tool.operation(nodes, input, context));
631
645
  }
632
646
  catch (error) {
633
647
  // A refusal can ask the session to remember what it refused (a catalog piece under an open source).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pascal-app/mcp",
3
- "version": "1.0.7",
3
+ "version": "1.1.1",
4
4
  "description": "Model Context Protocol server for Pascal 3D editor",
5
5
  "mcpName": "app.pascal/editor",
6
6
  "type": "module",
@@ -62,19 +62,19 @@
62
62
  "prepublishOnly": "bun run build && bun test && bun run smoke:node"
63
63
  },
64
64
  "peerDependencies": {
65
- "@pascal-app/core": "^1.0.7"
65
+ "@pascal-app/core": "^1.1.1"
66
66
  },
67
67
  "dependencies": {
68
68
  "@hono/node-server": "^2.1.6",
69
69
  "@modelcontextprotocol/node": "^2.1.2",
70
70
  "@modelcontextprotocol/server": "^2.4.0",
71
- "@pascal-app/geometry-script": "^1.0.7",
71
+ "@pascal-app/geometry-script": "^1.1.1",
72
72
  "@pascal-app/lingo": "^0.5.0",
73
73
  "zod": ">=4.6.7 <4.7"
74
74
  },
75
75
  "devDependencies": {
76
76
  "@modelcontextprotocol/client": "^2.4.0",
77
- "@pascal-app/core": "^1.0.7",
77
+ "@pascal-app/core": "^1.1.1",
78
78
  "@pascal/typescript-config": "*",
79
79
  "@types/node": "^26.6.6",
80
80
  "typescript": "7.0.2"