@pascal-app/mcp 1.0.5 → 1.0.7
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 +31 -10
- package/dist/bin/pascal-mcp.js +2 -0
- package/dist/bridge/agent-session.d.ts +2 -1
- package/dist/bridge/agent-session.d.ts.map +1 -1
- package/dist/bridge/agent-session.js +0 -1
- package/dist/bridge/scene-bridge.d.ts +21 -10
- package/dist/bridge/scene-bridge.d.ts.map +1 -1
- package/dist/bridge/scene-bridge.js +178 -194
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/lib/safe-fetch.d.ts +6 -0
- package/dist/lib/safe-fetch.d.ts.map +1 -1
- package/dist/lib/safe-fetch.js +135 -41
- package/dist/operations/scene-operations.d.ts +7 -0
- package/dist/operations/scene-operations.d.ts.map +1 -1
- package/dist/operations/scene-operations.js +4 -0
- package/dist/prompts/from-brief.js +1 -1
- package/dist/prompts/iterate-on-feedback.js +1 -1
- package/dist/prompts/renovation-from-photos.js +1 -1
- package/dist/prompts/scene-guidance.js +2 -2
- package/dist/resources/agent-guide.d.ts.map +1 -1
- package/dist/resources/agent-guide.js +8 -3
- package/dist/resources/catalog-items.d.ts +4 -1
- package/dist/resources/catalog-items.d.ts.map +1 -1
- package/dist/resources/catalog-items.js +14 -6
- package/dist/server.d.ts +9 -5
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +21 -5
- package/dist/templates/empty-studio.d.ts.map +1 -1
- package/dist/templates/empty-studio.js +2 -3
- package/dist/templates/garden-house.d.ts.map +1 -1
- package/dist/templates/garden-house.js +2 -3
- package/dist/templates/two-bedroom.d.ts.map +1 -1
- package/dist/templates/two-bedroom.js +2 -3
- package/dist/tools/add-column.d.ts.map +1 -1
- package/dist/tools/add-column.js +9 -6
- package/dist/tools/add-object.d.ts +44 -5
- package/dist/tools/add-object.d.ts.map +1 -1
- package/dist/tools/add-object.js +75 -21
- package/dist/tools/asset-catalog.d.ts +5 -6
- package/dist/tools/asset-catalog.d.ts.map +1 -1
- package/dist/tools/asset-catalog.js +1 -9
- package/dist/tools/author-scene.d.ts +8 -5
- package/dist/tools/author-scene.d.ts.map +1 -1
- package/dist/tools/author-scene.js +302 -51
- package/dist/tools/clear-scene.js +2 -2
- package/dist/tools/collections.d.ts.map +1 -1
- package/dist/tools/collections.js +8 -5
- package/dist/tools/honest-patch-guard.js +6 -5
- package/dist/tools/hosted-services.d.ts.map +1 -1
- package/dist/tools/hosted-services.js +2 -2
- package/dist/tools/index.d.ts +7 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +3 -9
- package/dist/tools/local-geometry-scripts.d.ts.map +1 -1
- package/dist/tools/local-geometry-scripts.js +7 -2
- package/dist/tools/patch-guards.d.ts.map +1 -1
- package/dist/tools/patch-guards.js +24 -14
- package/dist/tools/project-files.d.ts.map +1 -1
- package/dist/tools/project-files.js +2 -2
- package/dist/tools/room-tools.d.ts +1 -0
- package/dist/tools/room-tools.d.ts.map +1 -1
- package/dist/tools/room-tools.js +11 -8
- package/dist/tools/scene-scope.d.ts +4 -0
- package/dist/tools/scene-scope.d.ts.map +1 -0
- package/dist/tools/scene-scope.js +4 -0
- package/dist/tools/shared-tools.d.ts +0 -2
- package/dist/tools/shared-tools.d.ts.map +1 -1
- package/dist/tools/shared-tools.js +146 -31
- package/dist/tools/templates/create-house-from-brief.js +1 -1
- package/dist/tools/view-scene.js +2 -2
- package/examples/coordinate-conventions-demo.md +1 -0
- package/examples/photo-to-scene.md +1 -0
- package/examples/renovate-from-photos.md +2 -0
- package/package.json +10 -10
- package/dist/tools/create-unit.d.ts +0 -29
- package/dist/tools/create-unit.d.ts.map +0 -1
- package/dist/tools/create-unit.js +0 -36
- package/dist/tools/list-units.d.ts +0 -38
- package/dist/tools/list-units.d.ts.map +0 -1
- package/dist/tools/list-units.js +0 -61
- package/dist/tools/set-unit-members.d.ts +0 -22
- package/dist/tools/set-unit-members.d.ts.map +0 -1
- package/dist/tools/set-unit-members.js +0 -43
- package/dist/tools/unit-members.d.ts +0 -4
- package/dist/tools/unit-members.d.ts.map +0 -1
- package/dist/tools/unit-members.js +0 -24
|
@@ -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,
|
|
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"}
|
|
@@ -104,15 +104,25 @@ function withoutChild(parent, childId) {
|
|
|
104
104
|
* Lives in the tool layer so every bridge behind `apply_patch` inherits it.
|
|
105
105
|
*/
|
|
106
106
|
export function assertPatchKeepsIdentity(patches, nodes, rootNodeIds, planDeletion) {
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
107
|
+
// What the patch writes, over the scene; the whole scene is copied only for a preview that reads
|
|
108
|
+
// all of it (deletes, gutter refreshes, a reparent), so an edit reads only the ids it names.
|
|
109
|
+
const written = new Map();
|
|
110
|
+
let whole = null;
|
|
111
|
+
const scene = () => {
|
|
112
|
+
if (!whole) {
|
|
113
|
+
const all = { ...nodes };
|
|
114
|
+
for (const [id, node] of written)
|
|
115
|
+
all[id] = node;
|
|
116
|
+
whole = { nodes: all, rootNodeIds: [...rootNodeIds], collections: {} };
|
|
117
|
+
}
|
|
118
|
+
return whole;
|
|
112
119
|
};
|
|
113
|
-
const at = (id) =>
|
|
120
|
+
const at = (id) => whole ? whole.nodes[id] : (written.get(id) ?? nodes[id]);
|
|
114
121
|
const put = (node) => {
|
|
115
|
-
|
|
122
|
+
if (whole)
|
|
123
|
+
whole.nodes[node.id] = node;
|
|
124
|
+
else
|
|
125
|
+
written.set(node.id, node);
|
|
116
126
|
};
|
|
117
127
|
// Default gutters and downspouts a delete run refreshes: the store may keep,
|
|
118
128
|
// move or replace them with freshly minted ids, so later ops in the same
|
|
@@ -133,7 +143,7 @@ export function assertPatchKeepsIdentity(patches, nodes, rootNodeIds, planDeleti
|
|
|
133
143
|
const roofId = node.type === 'roof' ? node.id : node.type === 'roof-segment' ? node.parentId : null;
|
|
134
144
|
if (!roofId)
|
|
135
145
|
return;
|
|
136
|
-
const preview = previewDefaultGutterRefresh(scene.nodes, [roofId]);
|
|
146
|
+
const preview = previewDefaultGutterRefresh(scene().nodes, [roofId]);
|
|
137
147
|
for (const id of preview.unsettledIds)
|
|
138
148
|
regenerated.set(id, index);
|
|
139
149
|
for (const id of preview.regeneratedHostIds)
|
|
@@ -142,13 +152,13 @@ export function assertPatchKeepsIdentity(patches, nodes, rootNodeIds, planDeleti
|
|
|
142
152
|
let pendingDeletes = [];
|
|
143
153
|
const flushDeletes = (index) => {
|
|
144
154
|
const plan = planDeletion
|
|
145
|
-
? planDeletion(scene, pendingDeletes)
|
|
146
|
-
: planNodeDeletion(scene, pendingDeletes, { mintDefaults: false });
|
|
155
|
+
? planDeletion(scene(), pendingDeletes)
|
|
156
|
+
: planNodeDeletion(scene(), pendingDeletes, { mintDefaults: false });
|
|
147
157
|
for (const id of plan.unsettledIds)
|
|
148
158
|
regenerated.set(id, index);
|
|
149
159
|
for (const id of plan.regeneratedHostIds)
|
|
150
160
|
regeneratedHosts.set(id, index);
|
|
151
|
-
|
|
161
|
+
whole = { nodes: plan.nodes, rootNodeIds: plan.rootNodeIds, collections: plan.collections };
|
|
152
162
|
pendingDeletes = [];
|
|
153
163
|
};
|
|
154
164
|
patches.forEach((patch, index) => {
|
|
@@ -166,7 +176,7 @@ export function assertPatchKeepsIdentity(patches, nodes, rootNodeIds, planDeleti
|
|
|
166
176
|
const effectiveParentId = patch.parentId ?? node.parentId;
|
|
167
177
|
if (effectiveParentId)
|
|
168
178
|
refuseRegenerated(index, effectiveParentId);
|
|
169
|
-
if (at(node.id) || (!planDeletion &&
|
|
179
|
+
if (at(node.id) || (!planDeletion && nodes[node.id] !== undefined)) {
|
|
170
180
|
throw new PatchRefusedError('node_exists', index, node.id, planDeletion || at(node.id)
|
|
171
181
|
? `create id "${node.id}" already exists. Use op "update" to change it, or delete it earlier in the same patch to replace it.`
|
|
172
182
|
: `create id "${node.id}" existed when the patch started, and this bridge cannot preview its deletes. Delete it in one apply_patch call and create it in the next.`);
|
|
@@ -221,7 +231,7 @@ export function assertPatchKeepsIdentity(patches, nodes, rootNodeIds, planDeleti
|
|
|
221
231
|
if (!(newParent && Array.isArray(newParent.children))) {
|
|
222
232
|
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'}.`);
|
|
223
233
|
}
|
|
224
|
-
const before = { ...scene.nodes };
|
|
234
|
+
const before = { ...scene().nodes };
|
|
225
235
|
const oldParent = current.parentId ? at(current.parentId) : undefined;
|
|
226
236
|
if (oldParent)
|
|
227
237
|
put(withoutChild(oldParent, patch.id));
|
|
@@ -229,7 +239,7 @@ export function assertPatchKeepsIdentity(patches, nodes, rootNodeIds, planDeleti
|
|
|
229
239
|
put(merged);
|
|
230
240
|
unsettleRoofRefresh(merged, index);
|
|
231
241
|
try {
|
|
232
|
-
validateNodeRelations(before, scene.nodes, [patch.id]);
|
|
242
|
+
validateNodeRelations(before, scene().nodes, [patch.id]);
|
|
233
243
|
}
|
|
234
244
|
catch (err) {
|
|
235
245
|
throw new PatchRefusedError('invalid_parent', index, patch.id, `${newParent.type} "${newParent.id}" does not accept "${patch.id}": ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-files.d.ts","sourceRoot":"","sources":["../../src/tools/project-files.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAC7D,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,mCAAmC,CAAA;
|
|
1
|
+
{"version":3,"file":"project-files.d.ts","sourceRoot":"","sources":["../../src/tools/project-files.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAC7D,OAAO,EAEL,KAAK,uBAAuB,EAC7B,MAAM,mCAAmC,CAAA;AAS1C,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,uBAAuB,QAmC3F"}
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { executeProjectFileTool, } from '@pascal-app/core/agent-operations';
|
|
2
|
-
import { isAgentRefusal, PROJECT_FILE_TOOL_CONTRACTS, refusalPayload, } from '@pascal-app/core/agent-tools';
|
|
2
|
+
import { isAgentRefusal, PROJECT_FILE_TOOL_CONTRACTS, refusalPayload, surfaceDescription, } from '@pascal-app/core/agent-tools';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
export function registerProjectFileTools(server, execute) {
|
|
5
5
|
for (const contract of PROJECT_FILE_TOOL_CONTRACTS) {
|
|
6
6
|
server.registerTool(contract.name, {
|
|
7
7
|
title: contract.title,
|
|
8
|
-
description: contract
|
|
8
|
+
description: surfaceDescription(contract, 'mcp'),
|
|
9
9
|
inputSchema: z.object(contract.input).strict(),
|
|
10
10
|
annotations: contract.annotations,
|
|
11
11
|
}, async (input, ctx) => {
|
|
@@ -19,6 +19,7 @@ export declare const addDoorOutput: {
|
|
|
19
19
|
unchanged: z.ZodOptional<z.ZodLiteral<true>>;
|
|
20
20
|
}, z.core.$strip>;
|
|
21
21
|
doorId: z.ZodString;
|
|
22
|
+
sillHeight: z.ZodOptional<z.ZodNumber>;
|
|
22
23
|
project: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
23
24
|
unsaved: z.ZodOptional<z.ZodString>;
|
|
24
25
|
persistence: z.ZodOptional<z.ZodObject<{
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"room-tools.d.ts","sourceRoot":"","sources":["../../src/tools/room-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;
|
|
1
|
+
{"version":3,"file":"room-tools.d.ts","sourceRoot":"","sources":["../../src/tools/room-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AA0B7D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AACpD,OAAO,EAAmB,KAAK,kBAAkB,EAAc,MAAM,cAAc,CAAA;AAOnF,mFAAmF;AACnF,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;;;;;;;CAA0C,CAAA;AACpE,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;CAA4C,CAAA;AA0HxE,wBAAgB,eAAe,CAC7B,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,eAAe,EACvB,eAAe,CAAC,EAAE,kBAAkB,GACnC,IAAI,CAiCN;AAED,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,eAAe,EACvB,eAAe,CAAC,EAAE,kBAAkB,GACnC,IAAI,CAiCN;AAED,gGAAgG;AAChG,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,eAAe,EACvB,eAAe,CAAC,EAAE,kBAAkB,GACnC,IAAI,CAGN"}
|
package/dist/tools/room-tools.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { isScriptedNode, scriptedObjectMeta } from '@pascal-app/core';
|
|
2
2
|
import { achievedChanges, addWallOpening, editedScriptLanguage, editedScriptParams, rebuiltOpeningResult, rescriptOpening, } from '@pascal-app/core/agent-operations';
|
|
3
|
-
import { addDoorOutput as addDoorResult, addDoorTool, addWindowOutput as addWindowResult, addWindowTool, isAgentRefusal, } from '@pascal-app/core/agent-tools';
|
|
3
|
+
import { addDoorOutput as addDoorResult, addDoorTool, addWindowOutput as addWindowResult, addWindowTool, isAgentRefusal, surfaceDescription, } from '@pascal-app/core/agent-tools';
|
|
4
4
|
import { GeometryReuseFields } from '@pascal-app/core/schema';
|
|
5
5
|
import { z } from 'zod';
|
|
6
6
|
import { compileAndStore, readScript } from './add-object.js';
|
|
7
7
|
import { ADDITIVE_TOOL_ANNOTATIONS } from './annotations.js';
|
|
8
8
|
import { refusalResult, toolError } from './errors.js';
|
|
9
9
|
import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync.js';
|
|
10
|
+
import { inScene } from './scene-scope.js';
|
|
10
11
|
import { toPatches } from './shared-tools.js';
|
|
11
12
|
/** The contract's one result (core `addWallOpening`), and the live sync's note. */
|
|
12
13
|
export const addDoorOutput = { ...addDoorResult, ...liveSyncOutput };
|
|
@@ -43,7 +44,7 @@ async function rebuildOpening(kind, bridge, host, input) {
|
|
|
43
44
|
...GeometryReuseFields.parse(input),
|
|
44
45
|
},
|
|
45
46
|
});
|
|
46
|
-
outcome = rescriptOpening(nodes, { ...input, compiled }, { activeLevelId: bridge.activeLevelId?.() ?? null });
|
|
47
|
+
outcome = inScene(bridge, () => rescriptOpening(nodes, { ...input, compiled }, { activeLevelId: bridge.activeLevelId?.() ?? null }));
|
|
47
48
|
}
|
|
48
49
|
catch (error) {
|
|
49
50
|
if (isAgentRefusal(error))
|
|
@@ -94,7 +95,7 @@ async function compileOpeningScript(kind, bridge, host, input) {
|
|
|
94
95
|
export function registerAddDoor(server, bridge, geometryScripts) {
|
|
95
96
|
server.registerTool(addDoorTool.name, {
|
|
96
97
|
title: addDoorTool.title,
|
|
97
|
-
description: addDoorTool
|
|
98
|
+
description: surfaceDescription(addDoorTool, 'mcp'),
|
|
98
99
|
inputSchema: z.object(addDoorTool.input),
|
|
99
100
|
outputSchema: z.object(addDoorOutput),
|
|
100
101
|
annotations: ADDITIVE_TOOL_ANNOTATIONS,
|
|
@@ -106,11 +107,12 @@ export function registerAddDoor(server, bridge, geometryScripts) {
|
|
|
106
107
|
return compiled.error;
|
|
107
108
|
let opening;
|
|
108
109
|
try {
|
|
109
|
-
|
|
110
|
+
// Planned after the compile was awaited: back in the scene's scope for its ids.
|
|
111
|
+
opening = inScene(bridge, () => addWallOpening(bridge.getNodes(), {
|
|
110
112
|
kind: 'door',
|
|
111
113
|
...input,
|
|
112
114
|
compiled: compiled.script,
|
|
113
|
-
});
|
|
115
|
+
}));
|
|
114
116
|
}
|
|
115
117
|
catch (error) {
|
|
116
118
|
return refusalResult(error);
|
|
@@ -123,7 +125,7 @@ export function registerAddDoor(server, bridge, geometryScripts) {
|
|
|
123
125
|
export function registerAddWindow(server, bridge, geometryScripts) {
|
|
124
126
|
server.registerTool(addWindowTool.name, {
|
|
125
127
|
title: addWindowTool.title,
|
|
126
|
-
description: addWindowTool
|
|
128
|
+
description: surfaceDescription(addWindowTool, 'mcp'),
|
|
127
129
|
inputSchema: z.object(addWindowTool.input),
|
|
128
130
|
outputSchema: z.object(addWindowOutput),
|
|
129
131
|
annotations: ADDITIVE_TOOL_ANNOTATIONS,
|
|
@@ -135,11 +137,12 @@ export function registerAddWindow(server, bridge, geometryScripts) {
|
|
|
135
137
|
return compiled.error;
|
|
136
138
|
let opening;
|
|
137
139
|
try {
|
|
138
|
-
|
|
140
|
+
// Planned after the compile was awaited: back in the scene's scope for its ids.
|
|
141
|
+
opening = inScene(bridge, () => addWallOpening(bridge.getNodes(), {
|
|
139
142
|
kind: 'window',
|
|
140
143
|
...input,
|
|
141
144
|
compiled: compiled.script,
|
|
142
|
-
});
|
|
145
|
+
}));
|
|
143
146
|
}
|
|
144
147
|
catch (error) {
|
|
145
148
|
return refusalResult(error);
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { SceneOperations } from '../operations/index.js';
|
|
2
|
+
/** Runs `run` in the scene's scope when the operations have one (`SceneOperations.runInScene`). */
|
|
3
|
+
export declare function inScene<T>(operations: SceneOperations, run: () => T): T;
|
|
4
|
+
//# sourceMappingURL=scene-scope.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scene-scope.d.ts","sourceRoot":"","sources":["../../src/tools/scene-scope.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAEpD,mGAAmG;AACnG,wBAAgB,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,eAAe,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,CAEvE"}
|
|
@@ -18,8 +18,6 @@ export type OperationTool = {
|
|
|
18
18
|
annotations: AgentToolAnnotations;
|
|
19
19
|
outputSchema?: Record<string, z.ZodType>;
|
|
20
20
|
envelope?: (bridge: SceneOperations) => Record<string, unknown>;
|
|
21
|
-
/** Reads the host's item library: only these calls wait for it (the hosted one is a query). */
|
|
22
|
-
catalog?: true;
|
|
23
21
|
};
|
|
24
22
|
export type PlanReadingHosts = {
|
|
25
23
|
/** Reads catalog pictures and surfaces before view_assets and paint. */
|
|
@@ -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,
|
|
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,13 +1,15 @@
|
|
|
1
|
-
import { AGENT_OPERATIONS, achievedChanges, applyAgentOutcome, assetViewNeeds,
|
|
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';
|
|
1
|
+
import { AGENT_OPERATIONS, achievedChanges, applyAgentOutcome, assetViewNeeds, buildBatchObjects, createAssetViewLoader, createFetchAssetViewIO, furnishingRefusal, readCatalog, startOfSession, writeCollections, } from '@pascal-app/core/agent-operations';
|
|
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
|
-
import {
|
|
4
|
+
import { untracked } from '@pascal-app/core/runtime';
|
|
5
|
+
import { MaterialSchema, SceneMaterial, UnitNode, } from '@pascal-app/core/schema';
|
|
5
6
|
import { z } from 'zod';
|
|
6
7
|
import { ADDITIVE_TOOL_ANNOTATIONS, DESTRUCTIVE_TOOL_ANNOTATIONS, READ_ONLY_TOOL_ANNOTATIONS, } from './annotations.js';
|
|
7
8
|
import { builtInCatalog } from './asset-catalog.js';
|
|
8
9
|
import { registerCollectionTools } from './collections.js';
|
|
9
10
|
import { refusalResult } from './errors.js';
|
|
10
11
|
import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync.js';
|
|
12
|
+
import { inScene } from './scene-scope.js';
|
|
11
13
|
import { ROOM_TOOL_ANNOTATIONS, structureOutput } from './structure-tools.js';
|
|
12
14
|
const defaultAssetViews = createAssetViewLoader(createFetchAssetViewIO());
|
|
13
15
|
const jsonObject = z.record(z.string(), z.unknown());
|
|
@@ -118,7 +120,11 @@ const SHARED_TOOLS = [
|
|
|
118
120
|
contract: getWallsTool,
|
|
119
121
|
operation: AGENT_OPERATIONS.get_walls,
|
|
120
122
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
121
|
-
outputSchema: {
|
|
123
|
+
outputSchema: {
|
|
124
|
+
levelId: z.string(),
|
|
125
|
+
walls: z.array(jsonObject),
|
|
126
|
+
wallTypes: z.array(jsonObject),
|
|
127
|
+
},
|
|
122
128
|
},
|
|
123
129
|
{
|
|
124
130
|
contract: getZonesTool,
|
|
@@ -181,7 +187,7 @@ const SHARED_TOOLS = [
|
|
|
181
187
|
issues: z.array(z.looseObject({
|
|
182
188
|
type: z.string(),
|
|
183
189
|
message: z.string(),
|
|
184
|
-
severity: z.
|
|
190
|
+
severity: z.enum(['info', 'warning']).optional(),
|
|
185
191
|
nodeId: z.string().optional(),
|
|
186
192
|
path: z.string().optional(),
|
|
187
193
|
zoneId: z.string().optional(),
|
|
@@ -230,6 +236,11 @@ const SHARED_TOOLS = [
|
|
|
230
236
|
operation: AGENT_OPERATIONS.add_wall,
|
|
231
237
|
annotations: ADDITIVE_TOOL_ANNOTATIONS,
|
|
232
238
|
},
|
|
239
|
+
{
|
|
240
|
+
contract: setWallTypeTool,
|
|
241
|
+
operation: AGENT_OPERATIONS.set_wall_type,
|
|
242
|
+
annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
|
|
243
|
+
},
|
|
233
244
|
{
|
|
234
245
|
contract: addFenceTool,
|
|
235
246
|
operation: AGENT_OPERATIONS.add_fence,
|
|
@@ -269,7 +280,6 @@ const SHARED_TOOLS = [
|
|
|
269
280
|
contract: placeItemsTool,
|
|
270
281
|
operation: AGENT_OPERATIONS.place_items,
|
|
271
282
|
annotations: ADDITIVE_TOOL_ANNOTATIONS,
|
|
272
|
-
catalog: true,
|
|
273
283
|
},
|
|
274
284
|
{
|
|
275
285
|
contract: deleteNodeTool,
|
|
@@ -277,6 +287,18 @@ const SHARED_TOOLS = [
|
|
|
277
287
|
annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
|
|
278
288
|
outputSchema: { deletedIds: z.array(z.string()), ...achievedOutput, ...liveSyncOutput },
|
|
279
289
|
},
|
|
290
|
+
{
|
|
291
|
+
contract: moveNodeTool,
|
|
292
|
+
operation: AGENT_OPERATIONS.move_node,
|
|
293
|
+
annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
|
|
294
|
+
outputSchema: { ...moveNodeOutput, ...liveSyncOutput },
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
contract: duplicateNodeTool,
|
|
298
|
+
operation: AGENT_OPERATIONS.duplicate_node,
|
|
299
|
+
annotations: ADDITIVE_TOOL_ANNOTATIONS,
|
|
300
|
+
outputSchema: { ...duplicateNodeOutput, ...liveSyncOutput },
|
|
301
|
+
},
|
|
280
302
|
{
|
|
281
303
|
contract: createRoomTool,
|
|
282
304
|
operation: AGENT_OPERATIONS.create_room,
|
|
@@ -310,7 +332,6 @@ const SHARED_TOOLS = [
|
|
|
310
332
|
contract: furnishRoomTool,
|
|
311
333
|
operation: AGENT_OPERATIONS.furnish_room,
|
|
312
334
|
annotations: ADDITIVE_TOOL_ANNOTATIONS,
|
|
313
|
-
catalog: true,
|
|
314
335
|
outputSchema: {
|
|
315
336
|
ok: z.literal(true),
|
|
316
337
|
placed: z.number(),
|
|
@@ -343,7 +364,6 @@ const SHARED_TOOLS = [
|
|
|
343
364
|
contract: placeInRoomTool,
|
|
344
365
|
operation: AGENT_OPERATIONS.place_in_room,
|
|
345
366
|
annotations: ADDITIVE_TOOL_ANNOTATIONS,
|
|
346
|
-
catalog: true,
|
|
347
367
|
outputSchema: {
|
|
348
368
|
ok: z.literal(true),
|
|
349
369
|
itemId: z.string(),
|
|
@@ -379,13 +399,11 @@ const SHARED_TOOLS = [
|
|
|
379
399
|
contract: searchAssetsTool,
|
|
380
400
|
operation: AGENT_OPERATIONS.search_assets,
|
|
381
401
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
382
|
-
catalog: true,
|
|
383
402
|
},
|
|
384
403
|
{
|
|
385
404
|
contract: viewAssetsTool,
|
|
386
405
|
operation: AGENT_OPERATIONS.view_assets,
|
|
387
406
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
388
|
-
catalog: true,
|
|
389
407
|
},
|
|
390
408
|
{
|
|
391
409
|
contract: searchMaterialsTool,
|
|
@@ -401,6 +419,51 @@ const SHARED_TOOLS = [
|
|
|
401
419
|
outputSchema: { ...structureOutput, ...achievedOutput },
|
|
402
420
|
};
|
|
403
421
|
}),
|
|
422
|
+
{
|
|
423
|
+
contract: createUnitTool,
|
|
424
|
+
operation: AGENT_OPERATIONS.create_unit,
|
|
425
|
+
annotations: ADDITIVE_TOOL_ANNOTATIONS,
|
|
426
|
+
outputSchema: { unitId: z.string(), ...achievedOutput, ...liveSyncOutput },
|
|
427
|
+
},
|
|
428
|
+
{
|
|
429
|
+
contract: setUnitMembersTool,
|
|
430
|
+
operation: AGENT_OPERATIONS.set_unit_members,
|
|
431
|
+
annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
|
|
432
|
+
outputSchema: {
|
|
433
|
+
unitId: z.string(),
|
|
434
|
+
memberCount: z.number(),
|
|
435
|
+
...achievedOutput,
|
|
436
|
+
...liveSyncOutput,
|
|
437
|
+
},
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
contract: listUnitsTool,
|
|
441
|
+
operation: AGENT_OPERATIONS.list_units,
|
|
442
|
+
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
443
|
+
outputSchema: {
|
|
444
|
+
units: z.array(z.object({
|
|
445
|
+
unitId: z.string(),
|
|
446
|
+
name: z.string(),
|
|
447
|
+
kind: UnitNode.shape.kind,
|
|
448
|
+
color: z.string(),
|
|
449
|
+
members: z.array(z.string()),
|
|
450
|
+
report: z.object({
|
|
451
|
+
memberCount: z.number(),
|
|
452
|
+
levelSpan: z
|
|
453
|
+
.object({ minOrdinal: z.number(), maxOrdinal: z.number(), count: z.number() })
|
|
454
|
+
.nullable(),
|
|
455
|
+
grossAreaM2: z.number(),
|
|
456
|
+
members: z.array(z.object({
|
|
457
|
+
zoneId: z.string(),
|
|
458
|
+
name: z.string(),
|
|
459
|
+
levelId: z.string().nullable(),
|
|
460
|
+
levelOrdinal: z.number().nullable(),
|
|
461
|
+
areaM2: z.number(),
|
|
462
|
+
})),
|
|
463
|
+
}),
|
|
464
|
+
})),
|
|
465
|
+
},
|
|
466
|
+
},
|
|
404
467
|
{
|
|
405
468
|
contract: runBatchTool,
|
|
406
469
|
operation: AGENT_OPERATIONS.run_batch,
|
|
@@ -439,7 +502,8 @@ export function agentRuntime(bridge) {
|
|
|
439
502
|
if (next.length)
|
|
440
503
|
bridge.applyPatch(next);
|
|
441
504
|
if (changes.collections && Object.keys(changes.collections).length)
|
|
442
|
-
|
|
505
|
+
// Copying the others is not a read: the operation recorded what it read of them.
|
|
506
|
+
bridge.setCollections(writeCollections(untracked(bridge.getCollections()), changes.collections));
|
|
443
507
|
},
|
|
444
508
|
reconcile: () => {
|
|
445
509
|
bridge.deriveStructure();
|
|
@@ -458,6 +522,37 @@ function writesScene(changes) {
|
|
|
458
522
|
(toPatches(changes).length ||
|
|
459
523
|
(changes.collections && Object.keys(changes.collections).length)));
|
|
460
524
|
}
|
|
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.
|
|
528
|
+
*/
|
|
529
|
+
function searchAssetsModelResult(output) {
|
|
530
|
+
const groups = output.groups;
|
|
531
|
+
return {
|
|
532
|
+
...output,
|
|
533
|
+
groups: groups.map((group) => ({
|
|
534
|
+
...group,
|
|
535
|
+
results: group.results.map(({ id, name, dimensions, attachTo }) => ({
|
|
536
|
+
id,
|
|
537
|
+
name,
|
|
538
|
+
dimensions,
|
|
539
|
+
...(attachTo && { attachTo }),
|
|
540
|
+
})),
|
|
541
|
+
})),
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* What the model reads of a tool's result: for verify_scene, the findings grouped and trimmed
|
|
546
|
+
* (`verifySceneModelResult`); for search_assets, the items without what placing them does not
|
|
547
|
+
* need; every other result as it is. The structured result keeps the whole.
|
|
548
|
+
*/
|
|
549
|
+
function modelResult(name, output, input) {
|
|
550
|
+
if (name === verifySceneTool.name)
|
|
551
|
+
return verifySceneModelResult(output, input);
|
|
552
|
+
if (name === searchAssetsTool.name)
|
|
553
|
+
return searchAssetsModelResult(output);
|
|
554
|
+
return output;
|
|
555
|
+
}
|
|
461
556
|
export function registerSharedTools(server, bridge, catalog = builtInCatalog, hosts = {}) {
|
|
462
557
|
for (const tool of SHARED_TOOLS)
|
|
463
558
|
registerOperationTool(server, bridge, tool, catalog, hosts);
|
|
@@ -470,7 +565,7 @@ export function registerSharedTools(server, bridge, catalog = builtInCatalog, ho
|
|
|
470
565
|
export function registerOperationTool(server, bridge, tool, catalog = builtInCatalog, hosts = {}) {
|
|
471
566
|
server.registerTool(tool.contract.name, {
|
|
472
567
|
title: tool.contract.title,
|
|
473
|
-
description: tool.contract
|
|
568
|
+
description: surfaceDescription(tool.contract, 'mcp'),
|
|
474
569
|
inputSchema: z.object(tool.contract.input),
|
|
475
570
|
// Loose: a client that listed the tools rejects any field the schema leaves out, and the
|
|
476
571
|
// operations in core grow fields (verify_scene's guesses) that this list would miss.
|
|
@@ -478,26 +573,34 @@ export function registerOperationTool(server, bridge, tool, catalog = builtInCat
|
|
|
478
573
|
annotations: tool.annotations,
|
|
479
574
|
}, async (input) => {
|
|
480
575
|
let outcome;
|
|
576
|
+
const nodes = bridge.getNodes();
|
|
481
577
|
// A copy of the map: a host may write its own in place (the hosted bridge does), and a
|
|
482
|
-
// "before" that grows with the call reads every creation as unchanged.
|
|
483
|
-
|
|
578
|
+
// "before" that grows with the call reads every creation as unchanged. Copying is not a
|
|
579
|
+
// read: on a detached scene, the operation records what it selects.
|
|
580
|
+
const before = { ...untracked(nodes) };
|
|
484
581
|
// The agent's session begins with its first call, before anything is written.
|
|
485
582
|
const first = bridge.agentSession && startOfSession(bridge.agentSession.read(), before);
|
|
486
583
|
if (first)
|
|
487
584
|
bridge.agentSession?.keep(first);
|
|
488
|
-
// Gate before
|
|
585
|
+
// Gate before module compilation. Under an open source the gate reads the catalog's tags to
|
|
586
|
+
// let technical equipment and plants through; a batch's calls are checked one by one. Any
|
|
587
|
+
// other refused call reads nothing of the library.
|
|
489
588
|
const designBrief = briefInForce(bridge);
|
|
589
|
+
const gateRead = designBrief?.source === 'open'
|
|
590
|
+
? await readCatalog(before, tool.contract.name, input, catalog)
|
|
591
|
+
: undefined;
|
|
490
592
|
if (designBrief !== undefined) {
|
|
491
|
-
const refusal = furnishingRefusal(tool.contract.name, input, designBrief
|
|
492
|
-
|
|
593
|
+
const refusal = furnishingRefusal(tool.contract.name, input, designBrief, {
|
|
594
|
+
session: bridge.agentSession?.read(),
|
|
595
|
+
catalog: gateRead?.catalog,
|
|
596
|
+
});
|
|
597
|
+
if (refusal) {
|
|
598
|
+
// A refusal can ask the session to remember what it refused (a catalog piece under an open source).
|
|
599
|
+
if (refusal.keep)
|
|
600
|
+
bridge.agentSession?.keep(refusal.keep);
|
|
493
601
|
return refusalResult(refusal);
|
|
602
|
+
}
|
|
494
603
|
}
|
|
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
604
|
const batchesObjects = tool.contract.name === runBatchTool.name &&
|
|
502
605
|
Array.isArray(input.calls) &&
|
|
503
606
|
input.calls.some((call) => typeof call === 'object' &&
|
|
@@ -512,12 +615,10 @@ export function registerOperationTool(server, bridge, tool, catalog = builtInCat
|
|
|
512
615
|
...(bridge.nodeSchema && { nodeSchema: (kind) => bridge.nodeSchema(kind) }),
|
|
513
616
|
session: bridge.agentSession?.read(),
|
|
514
617
|
...(designBrief !== undefined && { designBrief }),
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
assetViewsReadCatalog(tool.contract.name, input) ||
|
|
518
|
-
briefReadsCatalog(tool.contract.name, input)) && { catalog: await catalog() }),
|
|
618
|
+
// What the call reads of the library: all of a list, or what it names of a source.
|
|
619
|
+
...(gateRead ?? (await readCatalog(before, tool.contract.name, input, catalog))),
|
|
519
620
|
...(batchesObjects && {
|
|
520
|
-
batchObjects: await buildBatchObjects(input.calls, hosts.buildObject ?? refuseScripts),
|
|
621
|
+
batchObjects: await buildBatchObjects(input.calls, hosts.buildObject ?? refuseScripts, designBrief),
|
|
521
622
|
}),
|
|
522
623
|
};
|
|
523
624
|
const needs = assetViewNeeds(before, tool.contract.name, input, context);
|
|
@@ -526,9 +627,12 @@ export function registerOperationTool(server, bridge, tool, catalog = builtInCat
|
|
|
526
627
|
pictures: needs.pictures,
|
|
527
628
|
});
|
|
528
629
|
try {
|
|
529
|
-
outcome = tool.operation(
|
|
630
|
+
outcome = inScene(bridge, () => tool.operation(nodes, input, context));
|
|
530
631
|
}
|
|
531
632
|
catch (error) {
|
|
633
|
+
// A refusal can ask the session to remember what it refused (a catalog piece under an open source).
|
|
634
|
+
if (isAgentRefusal(error) && error.keep)
|
|
635
|
+
bridge.agentSession?.keep(error.keep);
|
|
532
636
|
return refusalResult(error);
|
|
533
637
|
}
|
|
534
638
|
let result = outcome.result;
|
|
@@ -563,11 +667,22 @@ export function registerOperationTool(server, bridge, tool, catalog = builtInCat
|
|
|
563
667
|
});
|
|
564
668
|
if (pictures.length)
|
|
565
669
|
return {
|
|
566
|
-
content: [
|
|
670
|
+
content: [
|
|
671
|
+
{
|
|
672
|
+
type: 'text',
|
|
673
|
+
text: JSON.stringify(modelResult(tool.contract.name, shown, input)),
|
|
674
|
+
},
|
|
675
|
+
...pictures,
|
|
676
|
+
],
|
|
567
677
|
structuredContent: shown,
|
|
568
678
|
};
|
|
569
679
|
return {
|
|
570
|
-
content: [
|
|
680
|
+
content: [
|
|
681
|
+
{
|
|
682
|
+
type: 'text',
|
|
683
|
+
text: JSON.stringify(modelResult(tool.contract.name, payload, input)),
|
|
684
|
+
},
|
|
685
|
+
],
|
|
571
686
|
structuredContent: payload,
|
|
572
687
|
};
|
|
573
688
|
});
|
|
@@ -64,7 +64,7 @@ function countNodeTypes(nodes) {
|
|
|
64
64
|
export function registerCreateHouseFromBrief(server, bridge) {
|
|
65
65
|
server.registerTool('create_house_from_brief', {
|
|
66
66
|
title: 'Create house from brief',
|
|
67
|
-
description: '
|
|
67
|
+
description: "Replace the session's scene with a starter template and save it as an unpublished draft, returning its editor URL. The template follows bedroomCount, rooms and landscaping only: garden-house for landscaping or a garden, patio or yard room, else empty-studio up to one bedroom, two-bedroom for two and garden-house for more. brief, style and constraints neither choose nor shape it; the summary repeats brief. It saves into projectId's project, or a new one named projectName or after the template; with no scene store it saves nothing. Refine it with the semantic tools afterward.",
|
|
68
68
|
inputSchema: z.object(createHouseFromBriefInput),
|
|
69
69
|
outputSchema: z.object(createHouseFromBriefOutput),
|
|
70
70
|
annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
|
package/dist/tools/view-scene.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { confirmSceneViewPresentation, sceneViewNote, sceneViewPlan, } from '@pascal-app/core/agent-operations';
|
|
2
|
-
import { refuse, viewSceneTool } from '@pascal-app/core/agent-tools';
|
|
2
|
+
import { refuse, surfaceDescription, viewSceneTool } from '@pascal-app/core/agent-tools';
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { READ_ONLY_TOOL_ANNOTATIONS } from './annotations.js';
|
|
5
5
|
import { refusalResult } from './errors.js';
|
|
6
6
|
export function registerViewScene(server, operations, views) {
|
|
7
7
|
server.registerTool(viewSceneTool.name, {
|
|
8
8
|
title: viewSceneTool.title,
|
|
9
|
-
description: viewSceneTool
|
|
9
|
+
description: surfaceDescription(viewSceneTool, 'mcp'),
|
|
10
10
|
inputSchema: z.object(viewSceneTool.input),
|
|
11
11
|
annotations: READ_ONLY_TOOL_ANNOTATIONS,
|
|
12
12
|
}, async (input) => {
|
|
@@ -32,6 +32,7 @@ you're in.
|
|
|
32
32
|
|
|
33
33
|
Open the scene in Pascal (or render it from the JSON) and you can read
|
|
34
34
|
each claim directly off the geometry:
|
|
35
|
+
|
|
35
36
|
1. **`[x, z] → world (x, 0, z)` is exact, no sign flip.** Demo A and
|
|
36
37
|
Demo B both sit flat on the floor at Y = 0; their polygon vertices
|
|
37
38
|
round-trip through `save_scene` / `get_scene` byte-identical to what
|
|
@@ -33,6 +33,7 @@ The agent reads the attachment as a data URI and issues a single tool call:
|
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
Optional knobs:
|
|
36
|
+
|
|
36
37
|
- `save` (default `true`) — if `false`, the response includes `graph`
|
|
37
38
|
inline instead of persisting to the `SceneStore`.
|
|
38
39
|
- `defaultWallThickness` (default `0.2` m) — used when the vision model
|
|
@@ -12,6 +12,7 @@ tools to propose a renovation plan grounded in real photos.
|
|
|
12
12
|
## The brief
|
|
13
13
|
|
|
14
14
|
The user drops four photos into the chat:
|
|
15
|
+
|
|
15
16
|
1. A floorplan PDF page (exported as PNG).
|
|
16
17
|
2. A photo of the current living room.
|
|
17
18
|
3. A photo of the current kitchen.
|
|
@@ -147,6 +148,7 @@ against the corresponding perimeter wall:
|
|
|
147
148
|
|
|
148
149
|
Guided by the reference photo's analysis (bright neutrals, open plan,
|
|
149
150
|
minimal furnishing), the agent proposes a single logical patch:
|
|
151
|
+
|
|
150
152
|
- Remove the partition wall between Living and Kitchen.
|
|
151
153
|
- Relocate the kitchen island further west.
|
|
152
154
|
- Delete the bulky TV unit item; leave the sofa and coffee table.
|