@pascal-app/mcp 1.0.1 → 1.0.4
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 +47 -26
- package/dist/bin/pascal-mcp.js +8 -2
- package/dist/bridge/scene-bridge.d.ts +41 -6
- package/dist/bridge/scene-bridge.d.ts.map +1 -1
- package/dist/bridge/scene-bridge.js +230 -45
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/operations/scene-operations.d.ts +23 -3
- package/dist/operations/scene-operations.d.ts.map +1 -1
- package/dist/operations/scene-operations.js +31 -1
- package/dist/prompts/from-brief.js +1 -1
- package/dist/prompts/iterate-on-feedback.js +1 -1
- package/dist/prompts/scene-guidance.js +3 -3
- package/dist/resources/agent-guide.d.ts.map +1 -1
- package/dist/resources/agent-guide.js +22 -1
- package/dist/resources/catalog-items.d.ts +4 -5
- package/dist/resources/catalog-items.d.ts.map +1 -1
- package/dist/resources/catalog-items.js +7 -9
- package/dist/resources/design-schema.d.ts +9 -0
- package/dist/resources/design-schema.d.ts.map +1 -0
- package/dist/resources/design-schema.js +21 -0
- package/dist/resources/index.d.ts +3 -1
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/resources/index.js +5 -2
- package/dist/server.d.ts +19 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +14 -6
- package/dist/storage/disk-artifact-store.d.ts +9 -0
- package/dist/storage/disk-artifact-store.d.ts.map +1 -0
- package/dist/storage/disk-artifact-store.js +46 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/index.js +1 -0
- package/dist/storage/sqlite-scene-store.d.ts +2 -2
- package/dist/storage/sqlite-scene-store.d.ts.map +1 -1
- package/dist/storage/sqlite-scene-store.js +4 -3
- package/dist/storage/types.d.ts +32 -3
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/storage/types.js +12 -0
- package/dist/tools/add-column.d.ts +5 -0
- package/dist/tools/add-column.d.ts.map +1 -0
- package/dist/tools/add-column.js +58 -0
- package/dist/tools/add-object.d.ts +60 -0
- package/dist/tools/add-object.d.ts.map +1 -0
- package/dist/tools/add-object.js +156 -0
- package/dist/tools/apply-patch.d.ts +3 -0
- package/dist/tools/apply-patch.d.ts.map +1 -1
- package/dist/tools/apply-patch.js +28 -2
- package/dist/tools/asset-catalog.d.ts +9 -5
- package/dist/tools/asset-catalog.d.ts.map +1 -1
- package/dist/tools/asset-catalog.js +10 -12
- package/dist/tools/check-collisions.d.ts +1 -1
- package/dist/tools/check-collisions.d.ts.map +1 -1
- package/dist/tools/check-collisions.js +1 -2
- package/dist/tools/clear-scene.d.ts +9 -0
- package/dist/tools/clear-scene.d.ts.map +1 -0
- package/dist/tools/clear-scene.js +59 -0
- package/dist/tools/collections.d.ts +4 -0
- package/dist/tools/collections.d.ts.map +1 -0
- package/dist/tools/collections.js +52 -0
- package/dist/tools/construction-tools.d.ts +8 -41
- package/dist/tools/construction-tools.d.ts.map +1 -1
- package/dist/tools/construction-tools.js +120 -230
- package/dist/tools/create-unit.d.ts +2 -0
- package/dist/tools/create-unit.d.ts.map +1 -1
- package/dist/tools/describe-node.d.ts.map +1 -1
- package/dist/tools/describe-node.js +8 -6
- package/dist/tools/errors.d.ts +11 -0
- package/dist/tools/errors.d.ts.map +1 -1
- package/dist/tools/errors.js +10 -0
- package/dist/tools/find-nodes.d.ts +3 -19
- package/dist/tools/find-nodes.d.ts.map +1 -1
- package/dist/tools/find-nodes.js +150 -53
- package/dist/tools/geometry-script-worker.d.ts +2 -0
- package/dist/tools/geometry-script-worker.d.ts.map +1 -0
- package/dist/tools/geometry-script-worker.js +21 -0
- package/dist/tools/get-scene.d.ts +1 -0
- package/dist/tools/get-scene.d.ts.map +1 -1
- package/dist/tools/get-scene.js +3 -1
- package/dist/tools/honest-patch-guard.d.ts +2 -0
- package/dist/tools/honest-patch-guard.d.ts.map +1 -0
- package/dist/tools/honest-patch-guard.js +32 -0
- package/dist/tools/hosted-services.d.ts +4 -0
- package/dist/tools/hosted-services.d.ts.map +1 -0
- package/dist/tools/hosted-services.js +30 -0
- package/dist/tools/index.d.ts +10 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +17 -26
- package/dist/tools/live-sync.d.ts +24 -5
- package/dist/tools/live-sync.d.ts.map +1 -1
- package/dist/tools/live-sync.js +47 -26
- package/dist/tools/local-geometry-scripts.d.ts +8 -0
- package/dist/tools/local-geometry-scripts.d.ts.map +1 -0
- package/dist/tools/local-geometry-scripts.js +67 -0
- package/dist/tools/measure.d.ts +6 -0
- package/dist/tools/measure.d.ts.map +1 -1
- package/dist/tools/measure.js +33 -65
- package/dist/tools/measurement.d.ts +1 -36
- package/dist/tools/measurement.d.ts.map +1 -1
- package/dist/tools/measurement.js +2 -94
- package/dist/tools/node-world-point.d.ts +37 -0
- package/dist/tools/node-world-point.d.ts.map +1 -0
- package/dist/tools/node-world-point.js +457 -0
- package/dist/tools/normalize-schema-dialect.d.ts.map +1 -1
- package/dist/tools/normalize-schema-dialect.js +20 -0
- package/dist/tools/patch-guards.d.ts +58 -0
- package/dist/tools/patch-guards.d.ts.map +1 -0
- package/dist/tools/patch-guards.js +253 -0
- package/dist/tools/photo-to-scene/photo-to-scene.d.ts.map +1 -1
- package/dist/tools/photo-to-scene/photo-to-scene.js +3 -2
- package/dist/tools/place-design.d.ts +34 -0
- package/dist/tools/place-design.d.ts.map +1 -0
- package/dist/tools/place-design.js +91 -0
- package/dist/tools/redo.d.ts +2 -0
- package/dist/tools/redo.d.ts.map +1 -1
- package/dist/tools/redo.js +2 -2
- package/dist/tools/room-tools.d.ts +50 -113
- package/dist/tools/room-tools.d.ts.map +1 -1
- package/dist/tools/room-tools.js +129 -565
- package/dist/tools/scene-lifecycle/create-project.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/create-project.js +26 -1
- package/dist/tools/scene-lifecycle/delete-scene.d.ts +2 -0
- package/dist/tools/scene-lifecycle/delete-scene.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/delete-scene.js +4 -3
- package/dist/tools/scene-lifecycle/save-scene.d.ts +1 -0
- package/dist/tools/scene-lifecycle/save-scene.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/save-scene.js +46 -6
- package/dist/tools/scene-lifecycle/test-utils.d.ts +2 -2
- package/dist/tools/scene-lifecycle/test-utils.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/test-utils.js +2 -1
- package/dist/tools/set-unit-members.d.ts +2 -0
- package/dist/tools/set-unit-members.d.ts.map +1 -1
- package/dist/tools/set-zone.d.ts +2 -0
- package/dist/tools/set-zone.d.ts.map +1 -1
- package/dist/tools/set-zone.js +2 -2
- package/dist/tools/shared-tools.d.ts +30 -0
- package/dist/tools/shared-tools.d.ts.map +1 -0
- package/dist/tools/shared-tools.js +361 -0
- package/dist/tools/structure-tools.d.ts +57 -0
- package/dist/tools/structure-tools.d.ts.map +1 -0
- package/dist/tools/structure-tools.js +57 -0
- package/dist/tools/undo.d.ts +2 -0
- package/dist/tools/undo.d.ts.map +1 -1
- package/dist/tools/undo.js +2 -2
- package/dist/tools/validate-design.d.ts +136 -0
- package/dist/tools/validate-design.d.ts.map +1 -0
- package/dist/tools/validate-design.js +28 -0
- package/dist/tools/validate-scene.d.ts +5 -0
- package/dist/tools/validate-scene.d.ts.map +1 -1
- package/dist/tools/validate-scene.js +8 -6
- package/dist/tools/variants/mutations.js +1 -1
- package/dist/tools/view-scene.d.ts +38 -0
- package/dist/tools/view-scene.d.ts.map +1 -0
- package/dist/tools/view-scene.js +63 -0
- package/dist/version.d.ts +5 -0
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +17 -3
- package/examples/embed-in-agent.ts +1 -1
- package/examples/generate-apartment.md +9 -11
- package/examples/photo-to-scene.md +1 -1
- package/examples/renovate-from-photos.md +3 -4
- package/package.json +12 -6
- package/dist/tools/create-level.d.ts +0 -21
- package/dist/tools/create-level.d.ts.map +0 -1
- package/dist/tools/create-level.js +0 -63
- package/dist/tools/create-wall.d.ts +0 -22
- package/dist/tools/create-wall.d.ts.map +0 -1
- package/dist/tools/create-wall.js +0 -57
- package/dist/tools/cut-opening.d.ts +0 -25
- package/dist/tools/cut-opening.d.ts.map +0 -1
- package/dist/tools/cut-opening.js +0 -61
- package/dist/tools/delete-node.d.ts +0 -19
- package/dist/tools/delete-node.d.ts.map +0 -1
- package/dist/tools/delete-node.js +0 -40
- package/dist/tools/door-clearance.d.ts +0 -135
- package/dist/tools/door-clearance.d.ts.map +0 -1
- package/dist/tools/door-clearance.js +0 -334
- package/dist/tools/duplicate-level.d.ts +0 -19
- package/dist/tools/duplicate-level.d.ts.map +0 -1
- package/dist/tools/duplicate-level.js +0 -60
- package/dist/tools/geometry.d.ts +0 -24
- package/dist/tools/geometry.d.ts.map +0 -1
- package/dist/tools/geometry.js +0 -94
- package/dist/tools/get-node.d.ts +0 -11
- package/dist/tools/get-node.d.ts.map +0 -1
- package/dist/tools/get-node.js +0 -29
- package/dist/tools/layout-clearance.d.ts +0 -105
- package/dist/tools/layout-clearance.d.ts.map +0 -1
- package/dist/tools/layout-clearance.js +0 -190
- package/dist/tools/place-item.d.ts +0 -22
- package/dist/tools/place-item.d.ts.map +0 -1
- package/dist/tools/place-item.js +0 -89
- package/dist/tools/scene-query.d.ts +0 -62
- package/dist/tools/scene-query.d.ts.map +0 -1
- package/dist/tools/scene-query.js +0 -670
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { createZone, generateId } from '@pascal-app/core';
|
|
2
|
+
import { unknownMaterialPresetRefusal } from '@pascal-app/core/agent-operations';
|
|
3
|
+
import { getActiveRoofHeight, LevelNode, RoofNode, RoofSegmentNode } from '@pascal-app/core/schema';
|
|
3
4
|
import { z } from 'zod';
|
|
4
|
-
import { ADDITIVE_TOOL_ANNOTATIONS
|
|
5
|
+
import { ADDITIVE_TOOL_ANNOTATIONS } from './annotations.js';
|
|
5
6
|
import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync.js';
|
|
6
7
|
import { measurement } from './measurement.js';
|
|
7
8
|
import { NodeIdSchema, Vec2Schema, Vec3Schema } from './schemas.js';
|
|
@@ -15,7 +16,6 @@ const ROOF_TYPES = [
|
|
|
15
16
|
'flat',
|
|
16
17
|
'conical',
|
|
17
18
|
];
|
|
18
|
-
const RAILING_MODES = ['none', 'left', 'right', 'both'];
|
|
19
19
|
export const createStoryShellInput = {
|
|
20
20
|
levelId: NodeIdSchema,
|
|
21
21
|
footprint: z.array(Vec2Schema).min(3),
|
|
@@ -27,9 +27,17 @@ export const createStoryShellInput = {
|
|
|
27
27
|
positive: true,
|
|
28
28
|
description: 'Wall thickness.',
|
|
29
29
|
}).default(0.16),
|
|
30
|
-
createSlab: z
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
createSlab: z
|
|
31
|
+
.boolean()
|
|
32
|
+
.default(true)
|
|
33
|
+
.describe('Floor intent for the rooms inside the shell. False records hasFloor: false.'),
|
|
34
|
+
createCeiling: z
|
|
35
|
+
.boolean()
|
|
36
|
+
.default(true)
|
|
37
|
+
.describe('Ceiling intent for the rooms inside the shell. False records hasCeiling: false.'),
|
|
38
|
+
slabElevation: measurement('length', 'm', {
|
|
39
|
+
description: "Walking surface of the derived plate, stored as the room's floor elevation.",
|
|
40
|
+
}).default(0.1),
|
|
33
41
|
ceilingHeight: measurement('length', 'm', {
|
|
34
42
|
positive: true,
|
|
35
43
|
description: 'Ceiling height.',
|
|
@@ -42,7 +50,11 @@ export const createStoryShellInput = {
|
|
|
42
50
|
export const createStoryShellOutput = {
|
|
43
51
|
levelId: z.string(),
|
|
44
52
|
wallIds: z.array(z.string()),
|
|
53
|
+
/** Zones the reconciler derived for the enclosed faces. */
|
|
54
|
+
zoneIds: z.array(z.string()),
|
|
55
|
+
/** Derived floor plate. `null` when floors were declined or nothing reconciled. */
|
|
45
56
|
slabId: z.string().nullable(),
|
|
57
|
+
/** Derived ceiling. `null` when ceilings were declined or nothing reconciled. */
|
|
46
58
|
ceilingId: z.string().nullable(),
|
|
47
59
|
createdIds: z.array(z.string()),
|
|
48
60
|
...liveSyncOutput,
|
|
@@ -80,48 +92,6 @@ export const createRoofOutput = {
|
|
|
80
92
|
roofSegmentId: z.string(),
|
|
81
93
|
...liveSyncOutput,
|
|
82
94
|
};
|
|
83
|
-
export const createStairBetweenLevelsInput = {
|
|
84
|
-
fromLevelId: NodeIdSchema,
|
|
85
|
-
toLevelId: NodeIdSchema,
|
|
86
|
-
position: Vec3Schema,
|
|
87
|
-
rotation: measurement('angle', 'rad', { description: 'Y-axis rotation.' }).default(0),
|
|
88
|
-
width: measurement('length', 'm', { positive: true, description: 'Stair width.' }).default(1),
|
|
89
|
-
runLength: measurement('length', 'm', {
|
|
90
|
-
positive: true,
|
|
91
|
-
description: 'Horizontal run length.',
|
|
92
|
-
}).default(3),
|
|
93
|
-
totalRise: measurement('length', 'm', {
|
|
94
|
-
positive: true,
|
|
95
|
-
description: 'Total vertical rise.',
|
|
96
|
-
}).optional(),
|
|
97
|
-
stepCount: z.number().int().positive().default(14),
|
|
98
|
-
railingMode: z.enum(RAILING_MODES).default('both'),
|
|
99
|
-
destinationSlabId: NodeIdSchema.optional(),
|
|
100
|
-
sourceCeilingId: NodeIdSchema.optional(),
|
|
101
|
-
createDestinationSlabOpening: z.boolean().default(true),
|
|
102
|
-
createSourceCeilingOpening: z.boolean().default(true),
|
|
103
|
-
openingWidth: measurement('length', 'm', {
|
|
104
|
-
positive: true,
|
|
105
|
-
description: 'Floor opening width.',
|
|
106
|
-
}).optional(),
|
|
107
|
-
openingLength: measurement('length', 'm', {
|
|
108
|
-
positive: true,
|
|
109
|
-
description: 'Floor opening length.',
|
|
110
|
-
}).optional(),
|
|
111
|
-
openingOffset: measurement('length', 'm', { min: 0, description: 'Opening offset.' }).default(0),
|
|
112
|
-
openingCenter: Vec2Schema.optional(),
|
|
113
|
-
openingRotation: measurement('angle', 'rad', { description: 'Opening rotation.' }).optional(),
|
|
114
|
-
materialPreset: z.string().optional(),
|
|
115
|
-
name: z.string().optional(),
|
|
116
|
-
};
|
|
117
|
-
export const createStairBetweenLevelsOutput = {
|
|
118
|
-
stairId: z.string(),
|
|
119
|
-
stairSegmentId: z.string(),
|
|
120
|
-
destinationSlabId: z.string().nullable(),
|
|
121
|
-
sourceCeilingId: z.string().nullable(),
|
|
122
|
-
openingPolygon: z.array(Vec2Schema),
|
|
123
|
-
...liveSyncOutput,
|
|
124
|
-
};
|
|
125
95
|
function textResult(payload) {
|
|
126
96
|
return {
|
|
127
97
|
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
@@ -159,114 +129,124 @@ function nextLevelIndex(bridge, buildingId, referenceLevel) {
|
|
|
159
129
|
const candidate = referenceIndex + 1;
|
|
160
130
|
return existing.includes(candidate) ? Math.max(candidate, ...existing) + 1 : candidate;
|
|
161
131
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
132
|
+
/** Room zones that appeared on the level while this tool call ran. */
|
|
133
|
+
function newRoomZoneIds(bridge, levelId, before) {
|
|
134
|
+
return Object.values(bridge.getNodes())
|
|
135
|
+
.filter((node) => node.type === 'zone' &&
|
|
136
|
+
node.parentId === levelId &&
|
|
137
|
+
node.spaceRole === 'room' &&
|
|
138
|
+
!before.has(node.id))
|
|
139
|
+
.map((node) => node.id)
|
|
140
|
+
.sort();
|
|
167
141
|
}
|
|
168
|
-
function
|
|
169
|
-
const
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
args.position[2] + args.length / 2,
|
|
179
|
-
];
|
|
180
|
-
const rotation = args.openingRotation ?? args.rotation;
|
|
181
|
-
const halfW = width / 2;
|
|
182
|
-
const halfL = length / 2;
|
|
183
|
-
const local = [
|
|
184
|
-
[-halfW, -halfL],
|
|
185
|
-
[halfW, -halfL],
|
|
186
|
-
[halfW, halfL],
|
|
187
|
-
[-halfW, halfL],
|
|
188
|
-
];
|
|
189
|
-
return local.map(([x, z]) => {
|
|
190
|
-
const [rx, rz] = rotatePoint(x, z, rotation);
|
|
191
|
-
return [center[0] + rx, center[1] + rz];
|
|
192
|
-
});
|
|
193
|
-
}
|
|
194
|
-
function withHole(surface, hole) {
|
|
195
|
-
return {
|
|
196
|
-
holes: [...(surface.holes ?? []), hole],
|
|
197
|
-
holeMetadata: [...(surface.holeMetadata ?? []), { source: 'manual' }],
|
|
198
|
-
};
|
|
142
|
+
function derivedShellSurfaces(bridge, levelId, zoneIds) {
|
|
143
|
+
const children = Object.values(bridge.getNodes()).filter((node) => node.parentId === levelId);
|
|
144
|
+
const plate = children
|
|
145
|
+
.filter((node) => node.type === 'slab')
|
|
146
|
+
.sort((a, b) => Number(a.plateRole === 'base') - Number(b.plateRole === 'base'))
|
|
147
|
+
.find((node) => node.type === 'slab' &&
|
|
148
|
+
node.boundary === 'auto' &&
|
|
149
|
+
(node.zoneIds ?? []).some((id) => zoneIds.includes(id)));
|
|
150
|
+
const ceiling = children.find((node) => node.type === 'ceiling' && node.boundary === 'auto' && zoneIds.includes(node.zoneId ?? ''));
|
|
151
|
+
return { slabId: plate?.id ?? null, ceilingId: ceiling?.id ?? null };
|
|
199
152
|
}
|
|
200
153
|
export function registerConstructionTools(server, bridge) {
|
|
201
154
|
server.registerTool('create_story_shell', {
|
|
202
155
|
title: 'Create story shell',
|
|
203
|
-
description: 'Create one level-owned building shell from a footprint: perimeter walls
|
|
156
|
+
description: 'Create one level-owned building shell from a footprint: the perimeter walls. The floor plate and the ceiling are DERIVED from the rooms the walls enclose — never author a slab or a ceiling here, and never pass boundary/autoFromWalls; createSlab / createCeiling / slabElevation are recorded as room intent instead. Use once per story; do not make first-floor walls span multiple stories.',
|
|
204
157
|
inputSchema: createStoryShellInput,
|
|
205
158
|
outputSchema: createStoryShellOutput,
|
|
206
159
|
annotations: ADDITIVE_TOOL_ANNOTATIONS,
|
|
207
160
|
}, async ({ levelId, footprint, wallHeight, wallThickness, createSlab, createCeiling, slabElevation, ceilingHeight, namePrefix, wallMaterialPreset, slabMaterialPreset, ceilingMaterialPreset, }) => {
|
|
161
|
+
const preset = unknownMaterialPresetRefusal({
|
|
162
|
+
wallMaterialPreset,
|
|
163
|
+
slabMaterialPreset,
|
|
164
|
+
ceilingMaterialPreset,
|
|
165
|
+
});
|
|
166
|
+
if (preset)
|
|
167
|
+
throw new Error(preset);
|
|
208
168
|
const level = assertNode(bridge, levelId, 'level');
|
|
209
169
|
if (isRoofLevel(level)) {
|
|
210
170
|
throw new Error(`Cannot create a story shell on roof support level ${levelId}; create or choose an occupied story level instead`);
|
|
211
171
|
}
|
|
212
172
|
const points = footprint;
|
|
213
|
-
const
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
173
|
+
const before = new Set(Object.keys(bridge.getNodes()));
|
|
174
|
+
const plan = createZone(bridge.getNodes(), {
|
|
175
|
+
levelId,
|
|
176
|
+
polygon: points,
|
|
177
|
+
name: namePrefix ?? 'Room',
|
|
178
|
+
enclose: true,
|
|
179
|
+
mintId: generateId,
|
|
180
|
+
wall: {
|
|
220
181
|
thickness: wallThickness,
|
|
221
182
|
...(wallHeight !== undefined ? { height: wallHeight } : {}),
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
183
|
+
},
|
|
184
|
+
intent: {
|
|
185
|
+
floor: { elevation: slabElevation },
|
|
186
|
+
hasFloor: createSlab,
|
|
187
|
+
hasCeiling: createCeiling,
|
|
188
|
+
},
|
|
189
|
+
});
|
|
190
|
+
const wallIds = plan.changes.flatMap((change) => change.op === 'create' && change.node.type === 'wall' ? [change.node.id] : []);
|
|
191
|
+
bridge.runAsSingleHistoryStep(() => {
|
|
192
|
+
bridge.applyPatch(plan.changes.map((change) => change.op === 'create'
|
|
193
|
+
? {
|
|
194
|
+
...change,
|
|
195
|
+
parentId: change.node.parentId,
|
|
196
|
+
node: change.node.type === 'wall'
|
|
197
|
+
? {
|
|
198
|
+
...change.node,
|
|
199
|
+
...(namePrefix
|
|
200
|
+
? { name: `${namePrefix} Wall ${wallIds.indexOf(change.node.id) + 1}` }
|
|
201
|
+
: {}),
|
|
202
|
+
...(wallMaterialPreset ? { materialPreset: wallMaterialPreset } : {}),
|
|
203
|
+
metadata: { role: 'exterior', storyShell: true },
|
|
204
|
+
}
|
|
205
|
+
: change.node,
|
|
206
|
+
}
|
|
207
|
+
: change));
|
|
208
|
+
bridge.deriveStructure([levelId]);
|
|
209
|
+
});
|
|
210
|
+
const zoneIds = newRoomZoneIds(bridge, levelId, before);
|
|
211
|
+
// Finishes and an explicit ceiling height stay editable on derived
|
|
212
|
+
// construction, so they are applied to whatever the reconciler built.
|
|
213
|
+
const derived = derivedShellSurfaces(bridge, levelId, zoneIds);
|
|
214
|
+
const explicitCeilingHeight = ceilingHeight ?? wallHeight;
|
|
215
|
+
const finishes = [
|
|
216
|
+
...(derived.slabId && slabMaterialPreset
|
|
217
|
+
? [
|
|
218
|
+
{
|
|
219
|
+
op: 'update',
|
|
220
|
+
id: derived.slabId,
|
|
221
|
+
data: {
|
|
222
|
+
...(slabMaterialPreset ? { materialPreset: slabMaterialPreset } : {}),
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
]
|
|
226
|
+
: []),
|
|
227
|
+
...(derived.ceilingId && (ceilingMaterialPreset || explicitCeilingHeight !== undefined)
|
|
228
|
+
? [
|
|
229
|
+
{
|
|
230
|
+
op: 'update',
|
|
231
|
+
id: derived.ceilingId,
|
|
232
|
+
data: {
|
|
233
|
+
...(ceilingMaterialPreset ? { materialPreset: ceilingMaterialPreset } : {}),
|
|
234
|
+
...(explicitCeilingHeight !== undefined ? { height: explicitCeilingHeight } : {}),
|
|
235
|
+
},
|
|
236
|
+
},
|
|
237
|
+
]
|
|
238
|
+
: []),
|
|
239
|
+
];
|
|
240
|
+
if (finishes.length > 0)
|
|
241
|
+
bridge.applyPatch(finishes);
|
|
263
242
|
const persistence = await publishLiveSceneSnapshot(bridge, 'create_story_shell');
|
|
264
243
|
return textResult({
|
|
265
244
|
levelId,
|
|
266
245
|
wallIds,
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
246
|
+
zoneIds,
|
|
247
|
+
slabId: derived.slabId,
|
|
248
|
+
ceilingId: derived.ceilingId,
|
|
249
|
+
createdIds: Object.keys(bridge.getNodes()).filter((id) => !before.has(id)),
|
|
270
250
|
...persistencePayload(persistence),
|
|
271
251
|
});
|
|
272
252
|
});
|
|
@@ -277,6 +257,9 @@ export function registerConstructionTools(server, bridge) {
|
|
|
277
257
|
outputSchema: createRoofOutput,
|
|
278
258
|
annotations: ADDITIVE_TOOL_ANNOTATIONS,
|
|
279
259
|
}, async ({ levelId, roofLevelId, useDedicatedRoofLevel, roofLevelLabel, roofLevelElevation, roofLevelHeight, center, width, depth, roofType, pitch, wallHeight, wallThickness, overhang, materialPreset, name, }) => {
|
|
260
|
+
const preset = unknownMaterialPresetRefusal({ materialPreset });
|
|
261
|
+
if (preset)
|
|
262
|
+
throw new Error(preset);
|
|
280
263
|
const effectiveWidth = roofType === 'conical' ? Math.max(width, depth) : width;
|
|
281
264
|
const effectiveDepth = roofType === 'conical' ? effectiveWidth : depth;
|
|
282
265
|
// Peak height is derived from pitch + footprint + type; we still
|
|
@@ -350,97 +333,4 @@ export function registerConstructionTools(server, bridge) {
|
|
|
350
333
|
...persistencePayload(persistence),
|
|
351
334
|
});
|
|
352
335
|
});
|
|
353
|
-
server.registerTool('create_stair_between_levels', {
|
|
354
|
-
title: 'Create stair between levels',
|
|
355
|
-
description: 'Create a straight stair and a single rectangular manual opening in the destination slab/source ceiling. This disables stair auto-opening mode to avoid duplicate or irregular holes.',
|
|
356
|
-
inputSchema: createStairBetweenLevelsInput,
|
|
357
|
-
outputSchema: createStairBetweenLevelsOutput,
|
|
358
|
-
annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
|
|
359
|
-
}, async ({ fromLevelId, toLevelId, position, rotation, width, runLength, totalRise, stepCount, railingMode, destinationSlabId, sourceCeilingId, createDestinationSlabOpening, createSourceCeilingOpening, openingWidth, openingLength, openingOffset, openingCenter, openingRotation, materialPreset, name, }) => {
|
|
360
|
-
const fromLevel = assertNode(bridge, fromLevelId, 'level');
|
|
361
|
-
const toLevel = assertNode(bridge, toLevelId, 'level');
|
|
362
|
-
if (isRoofLevel(fromLevel) || isRoofLevel(toLevel)) {
|
|
363
|
-
throw new Error('Roof support levels are not occupied stories; create a separate occupied attic/story level if a stair-accessible attic is required');
|
|
364
|
-
}
|
|
365
|
-
const stairDraft = StairNode.parse({
|
|
366
|
-
name: name ?? 'Stair',
|
|
367
|
-
position: position,
|
|
368
|
-
rotation,
|
|
369
|
-
stairType: 'straight',
|
|
370
|
-
fromLevelId,
|
|
371
|
-
toLevelId,
|
|
372
|
-
slabOpeningMode: 'none',
|
|
373
|
-
openingOffset,
|
|
374
|
-
width,
|
|
375
|
-
...(totalRise !== undefined ? { totalRise } : {}),
|
|
376
|
-
stepCount,
|
|
377
|
-
railingMode,
|
|
378
|
-
children: [],
|
|
379
|
-
...(materialPreset ? { materialPreset } : {}),
|
|
380
|
-
metadata: {
|
|
381
|
-
openingManaged: 'manual-rectangular',
|
|
382
|
-
},
|
|
383
|
-
});
|
|
384
|
-
const riseNodes = {
|
|
385
|
-
...bridge.getNodes(),
|
|
386
|
-
[fromLevel.id]: {
|
|
387
|
-
...fromLevel,
|
|
388
|
-
children: [...fromLevel.children, stairDraft.id],
|
|
389
|
-
},
|
|
390
|
-
[stairDraft.id]: stairDraft,
|
|
391
|
-
};
|
|
392
|
-
const resolvedTotalRise = resolveStairTotalRise(stairDraft, riseNodes);
|
|
393
|
-
const segment = StairSegmentNode.parse({
|
|
394
|
-
segmentType: 'stair',
|
|
395
|
-
width,
|
|
396
|
-
length: runLength,
|
|
397
|
-
height: resolvedTotalRise,
|
|
398
|
-
stepCount,
|
|
399
|
-
...(materialPreset ? { materialPreset } : {}),
|
|
400
|
-
});
|
|
401
|
-
const stair = { ...stairDraft, children: [segment.id] };
|
|
402
|
-
const openingPolygon = rectangularOpening({
|
|
403
|
-
position: position,
|
|
404
|
-
rotation,
|
|
405
|
-
width: openingWidth ?? width,
|
|
406
|
-
length: openingLength ?? runLength,
|
|
407
|
-
offset: openingOffset,
|
|
408
|
-
center: openingCenter,
|
|
409
|
-
openingRotation,
|
|
410
|
-
});
|
|
411
|
-
const patches = [
|
|
412
|
-
{ op: 'create', node: stair, parentId: fromLevelId },
|
|
413
|
-
{ op: 'create', node: segment, parentId: stair.id },
|
|
414
|
-
];
|
|
415
|
-
const destinationSlab = destinationSlabId !== undefined
|
|
416
|
-
? assertNode(bridge, destinationSlabId, 'slab')
|
|
417
|
-
: firstNodeOnLevel(bridge, toLevelId, 'slab');
|
|
418
|
-
if (createDestinationSlabOpening && destinationSlab?.type === 'slab') {
|
|
419
|
-
patches.push({
|
|
420
|
-
op: 'update',
|
|
421
|
-
id: destinationSlab.id,
|
|
422
|
-
data: withHole(destinationSlab, openingPolygon),
|
|
423
|
-
});
|
|
424
|
-
}
|
|
425
|
-
const sourceCeiling = sourceCeilingId !== undefined
|
|
426
|
-
? assertNode(bridge, sourceCeilingId, 'ceiling')
|
|
427
|
-
: firstNodeOnLevel(bridge, fromLevelId, 'ceiling');
|
|
428
|
-
if (createSourceCeilingOpening && sourceCeiling?.type === 'ceiling') {
|
|
429
|
-
patches.push({
|
|
430
|
-
op: 'update',
|
|
431
|
-
id: sourceCeiling.id,
|
|
432
|
-
data: withHole(sourceCeiling, openingPolygon),
|
|
433
|
-
});
|
|
434
|
-
}
|
|
435
|
-
bridge.applyPatch(patches);
|
|
436
|
-
const persistence = await publishLiveSceneSnapshot(bridge, 'create_stair_between_levels');
|
|
437
|
-
return textResult({
|
|
438
|
-
stairId: stair.id,
|
|
439
|
-
stairSegmentId: segment.id,
|
|
440
|
-
destinationSlabId: destinationSlab?.id ?? null,
|
|
441
|
-
sourceCeilingId: sourceCeiling?.id ?? null,
|
|
442
|
-
openingPolygon,
|
|
443
|
-
...persistencePayload(persistence),
|
|
444
|
-
});
|
|
445
|
-
});
|
|
446
336
|
}
|
|
@@ -14,6 +14,8 @@ export declare const createUnitInput: {
|
|
|
14
14
|
memberZoneIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
15
15
|
};
|
|
16
16
|
export declare const createUnitOutput: {
|
|
17
|
+
project: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
18
|
+
unsaved: z.ZodOptional<z.ZodString>;
|
|
17
19
|
persistence: z.ZodOptional<z.ZodObject<{
|
|
18
20
|
status: z.ZodEnum<{
|
|
19
21
|
unbound: "unbound";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-unit.d.ts","sourceRoot":"","sources":["../../src/tools/create-unit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAMpD,eAAO,MAAM,eAAe;;;;;;;;;;;CAM3B,CAAA;AAED,eAAO,MAAM,gBAAgB
|
|
1
|
+
{"version":3,"file":"create-unit.d.ts","sourceRoot":"","sources":["../../src/tools/create-unit.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAGxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAMpD,eAAO,MAAM,eAAe;;;;;;;;;;;CAM3B,CAAA;AAED,eAAO,MAAM,gBAAgB;;;;;;;;;;;CAG5B,CAAA;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAuBnF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"describe-node.d.ts","sourceRoot":"","sources":["../../src/tools/describe-node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;
|
|
1
|
+
{"version":3,"file":"describe-node.d.ts","sourceRoot":"","sources":["../../src/tools/describe-node.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAIxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAKpD,eAAO,MAAM,iBAAiB;;CAE7B,CAAA;AAED,eAAO,MAAM,kBAAkB;;;;;;;;CAQ9B,CAAA;AA4CD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAiDrF"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { resolveCeilingHeight } from '@pascal-app/core';
|
|
1
|
+
import { floorFootprintName, getLevelDisplayName, resolveCeilingHeight } from '@pascal-app/core';
|
|
2
|
+
import { wallResolvedHeight } from '@pascal-app/core/agent-operations';
|
|
2
3
|
import { z } from 'zod';
|
|
3
4
|
import { READ_ONLY_TOOL_ANNOTATIONS } from './annotations.js';
|
|
4
5
|
import { ErrorCode, throwMcpError } from './errors.js';
|
|
5
|
-
import { resolveReportedWallHeight } from './scene-query.js';
|
|
6
6
|
import { NodeIdSchema } from './schemas.js';
|
|
7
7
|
export const describeNodeInput = {
|
|
8
8
|
id: NodeIdSchema,
|
|
@@ -26,11 +26,11 @@ function describe(node, bridge) {
|
|
|
26
26
|
const [x1, z1] = node.start;
|
|
27
27
|
const [x2, z2] = node.end;
|
|
28
28
|
const t = node.thickness ?? 0.1;
|
|
29
|
-
const h =
|
|
29
|
+
const h = wallResolvedHeight(bridge.getNodes(), node);
|
|
30
30
|
return `Wall from (${x1},${z1}) to (${x2},${z2}), thickness ${t.toFixed(2)}m, height ${h.toFixed(2)}m`;
|
|
31
31
|
}
|
|
32
32
|
case 'level':
|
|
33
|
-
return
|
|
33
|
+
return getLevelDisplayName(node);
|
|
34
34
|
case 'building': {
|
|
35
35
|
const [x, y, z] = node.position;
|
|
36
36
|
return `Building at (${x},${y},${z})`;
|
|
@@ -40,7 +40,9 @@ function describe(node, bridge) {
|
|
|
40
40
|
case 'zone':
|
|
41
41
|
return `Zone "${node.name}" with ${node.polygon.length} vertices`;
|
|
42
42
|
case 'slab':
|
|
43
|
-
return
|
|
43
|
+
return node.plateRole === 'base'
|
|
44
|
+
? `Floor footprint "${floorFootprintName(bridge.getNodes(), node)}" with ${node.polygon.length} vertices`
|
|
45
|
+
: `Slab with ${node.polygon.length} vertices`;
|
|
44
46
|
case 'ceiling':
|
|
45
47
|
return `Ceiling with ${node.polygon.length} vertices, height ${resolveCeilingHeight(node, bridge.getNodes()).toFixed(2)}m`;
|
|
46
48
|
case 'door':
|
|
@@ -76,7 +78,7 @@ export function registerDescribeNode(server, bridge) {
|
|
|
76
78
|
const properties = n.type === 'wall'
|
|
77
79
|
? {
|
|
78
80
|
...n,
|
|
79
|
-
resolvedHeight:
|
|
81
|
+
resolvedHeight: wallResolvedHeight(bridge.getNodes(), n),
|
|
80
82
|
heightIsExplicit: n.height !== undefined,
|
|
81
83
|
}
|
|
82
84
|
: n;
|
package/dist/tools/errors.d.ts
CHANGED
|
@@ -16,5 +16,16 @@ export declare function toolError(message: string, data?: Record<string, unknown
|
|
|
16
16
|
}[];
|
|
17
17
|
isError: true;
|
|
18
18
|
};
|
|
19
|
+
/**
|
|
20
|
+
* A core operation's refusal (`AgentRefusal`) as the same `{ error, code, ...details }` answer the
|
|
21
|
+
* hosted chat returns; anything else is a real failure and is rethrown.
|
|
22
|
+
*/
|
|
23
|
+
export declare function refusalResult(error: unknown): {
|
|
24
|
+
content: {
|
|
25
|
+
type: "text";
|
|
26
|
+
text: string;
|
|
27
|
+
}[];
|
|
28
|
+
isError: true;
|
|
29
|
+
};
|
|
19
30
|
export { ErrorCode, McpError };
|
|
20
31
|
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/tools/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAA;
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/tools/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAA;AAGxE;;;GAGG;AACH,wBAAgB,aAAa,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,KAAK,CAErF;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,OAAO,EAAE,MAAM,EACf,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B;IACD,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;IACzC,OAAO,EAAE,IAAI,CAAA;CACd,CAUA;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO;aAlBjC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE;aAChC,IAAI;EAoBd;AAED,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAA"}
|
package/dist/tools/errors.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
2
|
+
import { isAgentRefusal } from '@pascal-app/core/agent-tools';
|
|
2
3
|
/**
|
|
3
4
|
* Throw a structured MCP error. The SDK translates `McpError` into a
|
|
4
5
|
* JSON-RPC error response automatically.
|
|
@@ -22,4 +23,13 @@ export function toolError(message, data) {
|
|
|
22
23
|
isError: true,
|
|
23
24
|
};
|
|
24
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* A core operation's refusal (`AgentRefusal`) as the same `{ error, code, ...details }` answer the
|
|
28
|
+
* hosted chat returns; anything else is a real failure and is rethrown.
|
|
29
|
+
*/
|
|
30
|
+
export function refusalResult(error) {
|
|
31
|
+
if (isAgentRefusal(error))
|
|
32
|
+
return toolError(error.message, { code: error.code, ...error.details });
|
|
33
|
+
throw error;
|
|
34
|
+
}
|
|
25
35
|
export { ErrorCode, McpError };
|
|
@@ -2,28 +2,12 @@ import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import type { SceneOperations } from '../operations/index.js';
|
|
4
4
|
export declare const findNodesInput: {
|
|
5
|
-
type: z.ZodOptional<z.
|
|
6
|
-
site: "site";
|
|
7
|
-
building: "building";
|
|
8
|
-
level: "level";
|
|
9
|
-
wall: "wall";
|
|
10
|
-
fence: "fence";
|
|
11
|
-
slab: "slab";
|
|
12
|
-
door: "door";
|
|
13
|
-
item: "item";
|
|
14
|
-
zone: "zone";
|
|
15
|
-
stair: "stair";
|
|
16
|
-
ceiling: "ceiling";
|
|
17
|
-
roof: "roof";
|
|
18
|
-
"roof-segment": "roof-segment";
|
|
19
|
-
"stair-segment": "stair-segment";
|
|
20
|
-
scan: "scan";
|
|
21
|
-
guide: "guide";
|
|
22
|
-
window: "window";
|
|
23
|
-
}>>;
|
|
5
|
+
type: z.ZodOptional<z.ZodString>;
|
|
24
6
|
parentId: z.ZodOptional<z.ZodString>;
|
|
25
7
|
levelId: z.ZodOptional<z.ZodString>;
|
|
26
8
|
zoneId: z.ZodOptional<z.ZodString>;
|
|
9
|
+
sourceId: z.ZodOptional<z.ZodString>;
|
|
10
|
+
sourceIdPrefix: z.ZodOptional<z.ZodString>;
|
|
27
11
|
};
|
|
28
12
|
export declare const findNodesOutput: {
|
|
29
13
|
nodes: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"find-nodes.d.ts","sourceRoot":"","sources":["../../src/tools/find-nodes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;
|
|
1
|
+
{"version":3,"file":"find-nodes.d.ts","sourceRoot":"","sources":["../../src/tools/find-nodes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAA;AAKxE,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAOpD,eAAO,MAAM,cAAc;;;;;;;CAyB1B,CAAA;AAED,eAAO,MAAM,eAAe;;CAE3B,CAAA;AAmHD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAuFlF"}
|