@pascal-app/mcp 1.0.3 → 1.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +76 -0
- package/README.md +181 -56
- package/dist/bin/pascal-mcp.js +9 -3
- package/dist/bridge/agent-session.d.ts +18 -0
- package/dist/bridge/agent-session.d.ts.map +1 -0
- package/dist/bridge/agent-session.js +15 -0
- package/dist/bridge/index.d.ts +1 -1
- package/dist/bridge/index.d.ts.map +1 -1
- package/dist/bridge/scene-bridge.d.ts +42 -6
- package/dist/bridge/scene-bridge.d.ts.map +1 -1
- package/dist/bridge/scene-bridge.js +232 -45
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/lib/safe-fetch.d.ts +22 -4
- package/dist/lib/safe-fetch.d.ts.map +1 -1
- package/dist/lib/safe-fetch.js +233 -134
- package/dist/operations/scene-operations.d.ts +41 -3
- package/dist/operations/scene-operations.d.ts.map +1 -1
- package/dist/operations/scene-operations.js +42 -1
- package/dist/prompts/from-brief.d.ts +1 -1
- package/dist/prompts/from-brief.d.ts.map +1 -1
- package/dist/prompts/from-brief.js +3 -3
- package/dist/prompts/index.d.ts +1 -1
- package/dist/prompts/index.d.ts.map +1 -1
- package/dist/prompts/iterate-on-feedback.d.ts +1 -1
- package/dist/prompts/iterate-on-feedback.d.ts.map +1 -1
- package/dist/prompts/iterate-on-feedback.js +3 -3
- package/dist/prompts/renovation-from-photos.d.ts +1 -1
- package/dist/prompts/renovation-from-photos.d.ts.map +1 -1
- package/dist/prompts/renovation-from-photos.js +2 -2
- package/dist/prompts/scene-guidance.d.ts.map +1 -1
- package/dist/prompts/scene-guidance.js +13 -6
- package/dist/resources/agent-guide.d.ts +1 -1
- package/dist/resources/agent-guide.d.ts.map +1 -1
- package/dist/resources/agent-guide.js +62 -33
- package/dist/resources/catalog-items.d.ts +5 -6
- package/dist/resources/catalog-items.d.ts.map +1 -1
- package/dist/resources/catalog-items.js +7 -9
- package/dist/resources/constraints.d.ts +1 -1
- package/dist/resources/constraints.d.ts.map +1 -1
- package/dist/resources/constraints.js +1 -1
- 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 +4 -2
- package/dist/resources/index.d.ts.map +1 -1
- package/dist/resources/index.js +5 -2
- package/dist/resources/scene-current.d.ts +1 -1
- package/dist/resources/scene-current.d.ts.map +1 -1
- package/dist/resources/scene-summary.d.ts +1 -1
- package/dist/resources/scene-summary.d.ts.map +1 -1
- package/dist/server.d.ts +25 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +71 -25
- 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 +3 -3
- 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 +36 -7
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/storage/types.js +12 -0
- package/dist/templates/empty-studio.d.ts +3 -3
- package/dist/templates/empty-studio.d.ts.map +1 -1
- package/dist/templates/garden-house.d.ts +3 -3
- package/dist/templates/garden-house.d.ts.map +1 -1
- package/dist/templates/index.d.ts.map +1 -1
- package/dist/templates/two-bedroom.d.ts +3 -3
- package/dist/templates/two-bedroom.d.ts.map +1 -1
- 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 +59 -0
- package/dist/tools/add-object.d.ts +76 -0
- package/dist/tools/add-object.d.ts.map +1 -0
- package/dist/tools/add-object.js +187 -0
- package/dist/tools/annotations.d.ts.map +1 -1
- package/dist/tools/apply-patch.d.ts +9 -2
- package/dist/tools/apply-patch.d.ts.map +1 -1
- package/dist/tools/apply-patch.js +36 -5
- 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 +33 -12
- package/dist/tools/author-scene.d.ts +10 -0
- package/dist/tools/author-scene.d.ts.map +1 -0
- package/dist/tools/author-scene.js +140 -0
- package/dist/tools/check-collisions.d.ts +7 -7
- package/dist/tools/check-collisions.d.ts.map +1 -1
- package/dist/tools/check-collisions.js +7 -8
- 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 +53 -0
- package/dist/tools/construction-tools.d.ts +12 -88
- package/dist/tools/construction-tools.d.ts.map +1 -1
- package/dist/tools/construction-tools.js +122 -381
- package/dist/tools/create-unit.d.ts +5 -3
- package/dist/tools/create-unit.d.ts.map +1 -1
- package/dist/tools/create-unit.js +2 -2
- package/dist/tools/errors.d.ts +15 -4
- package/dist/tools/errors.d.ts.map +1 -1
- package/dist/tools/errors.js +14 -4
- package/dist/tools/export-glb.d.ts +1 -1
- package/dist/tools/export-glb.d.ts.map +1 -1
- package/dist/tools/export-glb.js +2 -2
- package/dist/tools/export-json.d.ts +1 -1
- package/dist/tools/export-json.d.ts.map +1 -1
- package/dist/tools/export-json.js +2 -2
- 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 +2 -1
- package/dist/tools/get-scene.d.ts.map +1 -1
- package/dist/tools/get-scene.js +5 -3
- 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 +40 -0
- package/dist/tools/index.d.ts +12 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +18 -32
- package/dist/tools/level-height-guard.d.ts +2 -0
- package/dist/tools/level-height-guard.d.ts.map +1 -0
- package/dist/tools/level-height-guard.js +11 -0
- package/dist/tools/list-units.d.ts +2 -2
- package/dist/tools/list-units.d.ts.map +1 -1
- package/dist/tools/list-units.js +2 -2
- package/dist/tools/live-sync.d.ts +25 -6
- package/dist/tools/live-sync.d.ts.map +1 -1
- package/dist/tools/live-sync.js +48 -27
- 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 +7 -1
- package/dist/tools/measure.d.ts.map +1 -1
- package/dist/tools/measure.js +38 -70
- 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 +1 -1
- package/dist/tools/normalize-schema-dialect.d.ts.map +1 -1
- package/dist/tools/normalize-schema-dialect.js +22 -3
- package/dist/tools/patch-guards.d.ts +60 -0
- package/dist/tools/patch-guards.d.ts.map +1 -0
- package/dist/tools/patch-guards.js +256 -0
- package/dist/tools/photo-to-scene/index.d.ts +1 -1
- package/dist/tools/photo-to-scene/index.d.ts.map +1 -1
- package/dist/tools/photo-to-scene/photo-to-scene.d.ts +3 -3
- package/dist/tools/photo-to-scene/photo-to-scene.d.ts.map +1 -1
- package/dist/tools/photo-to-scene/photo-to-scene.js +10 -9
- package/dist/tools/project-files.d.ts +4 -0
- package/dist/tools/project-files.d.ts.map +1 -0
- package/dist/tools/project-files.js +30 -0
- package/dist/tools/redo.d.ts +4 -2
- package/dist/tools/redo.d.ts.map +1 -1
- package/dist/tools/redo.js +4 -4
- package/dist/tools/retired-tools.d.ts +10 -0
- package/dist/tools/retired-tools.d.ts.map +1 -0
- package/dist/tools/retired-tools.js +22 -0
- package/dist/tools/roof-level-guard.d.ts +2 -0
- package/dist/tools/roof-level-guard.d.ts.map +1 -0
- package/dist/tools/roof-level-guard.js +22 -0
- package/dist/tools/room-tools.d.ts +36 -98
- package/dist/tools/room-tools.d.ts.map +1 -1
- package/dist/tools/room-tools.js +130 -565
- package/dist/tools/scene-lifecycle/create-project.d.ts +1 -1
- package/dist/tools/scene-lifecycle/create-project.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/create-project.js +31 -6
- package/dist/tools/scene-lifecycle/delete-scene.d.ts +3 -1
- package/dist/tools/scene-lifecycle/delete-scene.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/delete-scene.js +10 -9
- package/dist/tools/scene-lifecycle/get-project-status.d.ts +1 -1
- package/dist/tools/scene-lifecycle/get-project-status.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/get-project-status.js +6 -6
- package/dist/tools/scene-lifecycle/index.d.ts +1 -1
- package/dist/tools/scene-lifecycle/index.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/list-scenes.d.ts +1 -1
- package/dist/tools/scene-lifecycle/list-scenes.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/list-scenes.js +4 -4
- package/dist/tools/scene-lifecycle/load-scene.d.ts +2 -2
- package/dist/tools/scene-lifecycle/load-scene.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/load-scene.js +5 -5
- package/dist/tools/scene-lifecycle/metadata.d.ts +1 -1
- package/dist/tools/scene-lifecycle/rename-scene.d.ts +1 -1
- package/dist/tools/scene-lifecycle/rename-scene.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/rename-scene.js +6 -6
- package/dist/tools/scene-lifecycle/save-scene.d.ts +6 -3
- package/dist/tools/scene-lifecycle/save-scene.d.ts.map +1 -1
- package/dist/tools/scene-lifecycle/save-scene.js +124 -16
- package/dist/tools/scene-lifecycle/test-utils.d.ts +3 -3
- 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 +4 -2
- package/dist/tools/set-unit-members.d.ts.map +1 -1
- package/dist/tools/set-unit-members.js +6 -6
- package/dist/tools/set-zone.d.ts +4 -2
- package/dist/tools/set-zone.d.ts.map +1 -1
- package/dist/tools/set-zone.js +30 -18
- package/dist/tools/shared-tools.d.ts +40 -0
- package/dist/tools/shared-tools.d.ts.map +1 -0
- package/dist/tools/shared-tools.js +574 -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/templates/create-from-template.d.ts +2 -2
- package/dist/tools/templates/create-from-template.d.ts.map +1 -1
- package/dist/tools/templates/create-from-template.js +6 -6
- package/dist/tools/templates/create-house-from-brief.d.ts +1 -1
- package/dist/tools/templates/create-house-from-brief.d.ts.map +1 -1
- package/dist/tools/templates/create-house-from-brief.js +6 -6
- package/dist/tools/templates/index.d.ts +1 -1
- package/dist/tools/templates/index.d.ts.map +1 -1
- package/dist/tools/templates/list-templates.d.ts +1 -1
- package/dist/tools/templates/list-templates.d.ts.map +1 -1
- package/dist/tools/templates/list-templates.js +2 -2
- package/dist/tools/undo.d.ts +4 -2
- package/dist/tools/undo.d.ts.map +1 -1
- package/dist/tools/undo.js +4 -4
- package/dist/tools/unit-members.d.ts.map +1 -1
- package/dist/tools/unit-members.js +6 -6
- package/dist/tools/variants/generate-variants.d.ts +6 -6
- package/dist/tools/variants/generate-variants.d.ts.map +1 -1
- package/dist/tools/variants/generate-variants.js +6 -6
- package/dist/tools/variants/index.d.ts +1 -1
- package/dist/tools/variants/index.d.ts.map +1 -1
- package/dist/tools/variants/mutations.js +1 -1
- package/dist/tools/view-scene.d.ts +44 -0
- package/dist/tools/view-scene.d.ts.map +1 -0
- package/dist/tools/view-scene.js +75 -0
- package/dist/tools/vision/analyze-floorplan-image.d.ts +1 -1
- package/dist/tools/vision/analyze-floorplan-image.d.ts.map +1 -1
- package/dist/tools/vision/analyze-floorplan-image.js +7 -7
- package/dist/tools/vision/analyze-room-photo.d.ts +1 -1
- package/dist/tools/vision/analyze-room-photo.d.ts.map +1 -1
- package/dist/tools/vision/analyze-room-photo.js +7 -7
- package/dist/tools/vision/index.d.ts +1 -1
- package/dist/tools/vision/index.d.ts.map +1 -1
- package/dist/transports/http.d.ts +8 -2
- package/dist/transports/http.d.ts.map +1 -1
- package/dist/transports/http.js +9 -3
- package/dist/transports/stdio.d.ts +15 -9
- package/dist/transports/stdio.d.ts.map +1 -1
- package/dist/transports/stdio.js +18 -11
- package/dist/version.d.ts +5 -0
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +17 -3
- package/examples/coordinate-conventions-demo.md +12 -13
- package/examples/embed-in-agent.ts +6 -7
- package/examples/generate-apartment.md +13 -15
- package/examples/photo-to-scene.md +3 -4
- package/examples/renovate-from-photos.md +6 -9
- package/package.json +19 -9
- 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/describe-node.d.ts +0 -17
- package/dist/tools/describe-node.d.ts.map +0 -1
- package/dist/tools/describe-node.js +0 -97
- 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/find-nodes.d.ts +0 -32
- package/dist/tools/find-nodes.d.ts.map +0 -1
- package/dist/tools/find-nodes.js +0 -107
- 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
- package/dist/tools/validate-scene.d.ts +0 -19
- package/dist/tools/validate-scene.d.ts.map +0 -1
- package/dist/tools/validate-scene.js +0 -28
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { nodeRegistry, planNodeDeletion, previewDefaultGutterRefresh, validateNodeRelations, } from '@pascal-app/core';
|
|
2
|
+
import { scriptedFieldRefusal, unknownMaterialPresetRefusal, } from '@pascal-app/core/agent-operations';
|
|
3
|
+
import { AnyNode, nodeKindOf, parseNode } from '@pascal-app/core/schema';
|
|
4
|
+
const guards = [];
|
|
5
|
+
/** Runs a guard on every apply_patch from now on; registering a guard again replaces it. */
|
|
6
|
+
export function registerPatchGuard(guard) {
|
|
7
|
+
const at = guards.findIndex((known) => known.name === guard.name);
|
|
8
|
+
if (at >= 0)
|
|
9
|
+
guards[at] = guard;
|
|
10
|
+
else
|
|
11
|
+
guards.push(guard);
|
|
12
|
+
guards.sort((a, b) => a.order - b.order);
|
|
13
|
+
}
|
|
14
|
+
/** Runs every guard in order; the notes are what the guards changed in the patch. */
|
|
15
|
+
export function runPatchGuards(patches, nodes) {
|
|
16
|
+
return guards.flatMap((guard) => {
|
|
17
|
+
const notes = guard.run(patches, nodes);
|
|
18
|
+
return Array.isArray(notes) ? notes : [];
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* A patch op refused because it would break node identity, the hierarchy or
|
|
23
|
+
* the node's schema. apply_patch returns it as a tool error whose text is
|
|
24
|
+
* JSON `{ code, patchIndex, id, message }`.
|
|
25
|
+
*/
|
|
26
|
+
export class PatchRefusedError extends Error {
|
|
27
|
+
code;
|
|
28
|
+
patchIndex;
|
|
29
|
+
nodeId;
|
|
30
|
+
constructor(code, patchIndex, nodeId, detail) {
|
|
31
|
+
super(`${code}: patches[${patchIndex}] ${detail}`);
|
|
32
|
+
this.code = code;
|
|
33
|
+
this.patchIndex = patchIndex;
|
|
34
|
+
this.nodeId = nodeId;
|
|
35
|
+
this.name = 'PatchRefusedError';
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Fields an update may restate but not change: `id` and `type` are the node's
|
|
40
|
+
* identity, `object` and `children` its place in the graph. `parentId` stays
|
|
41
|
+
* writable, checked below: the store reconciles both parents' `children`.
|
|
42
|
+
*/
|
|
43
|
+
const IMMUTABLE_FIELDS = [
|
|
44
|
+
['id', 'identity_change'],
|
|
45
|
+
['type', 'identity_change'],
|
|
46
|
+
['object', 'immutable_field'],
|
|
47
|
+
['children', 'immutable_field'],
|
|
48
|
+
];
|
|
49
|
+
const CORE_KINDS = new Set(AnyNode.options.map(nodeKindOf));
|
|
50
|
+
const sameValue = (a, b) => JSON.stringify(a) === JSON.stringify(b);
|
|
51
|
+
/**
|
|
52
|
+
* Schema issues of a node, keyed `path: message`, from its kind's schema: the
|
|
53
|
+
* registered definition's, or the core per-kind schema. `null` only for a kind
|
|
54
|
+
* with neither (a plugin kind this runtime has not registered), which is then
|
|
55
|
+
* not validated.
|
|
56
|
+
*/
|
|
57
|
+
function schemaIssues(node) {
|
|
58
|
+
const type = node.type;
|
|
59
|
+
if (typeof type !== 'string')
|
|
60
|
+
return null;
|
|
61
|
+
const schema = nodeRegistry.get(type)?.schema;
|
|
62
|
+
if (!(schema || CORE_KINDS.has(type)))
|
|
63
|
+
return null;
|
|
64
|
+
const result = schema ? schema.safeParse(node) : parseNode(node);
|
|
65
|
+
const issues = result.success ? [] : (result.error?.issues ?? []);
|
|
66
|
+
return new Set(issues.map((issue) => `${issue.path.join('.')}: ${issue.message}`));
|
|
67
|
+
}
|
|
68
|
+
function withChild(parent, childId) {
|
|
69
|
+
const children = parent.children;
|
|
70
|
+
if (!Array.isArray(children) || children.includes(childId))
|
|
71
|
+
return parent;
|
|
72
|
+
return { ...parent, children: [...children, childId] };
|
|
73
|
+
}
|
|
74
|
+
function withoutChild(parent, childId) {
|
|
75
|
+
const children = parent.children;
|
|
76
|
+
if (!Array.isArray(children))
|
|
77
|
+
return parent;
|
|
78
|
+
return { ...parent, children: children.filter((id) => id !== childId) };
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Dry-runs a patch against the scene as each op leaves it and refuses:
|
|
82
|
+
* - a create whose id is already present (`node_exists`): a silent replace
|
|
83
|
+
* orphans the old node's children;
|
|
84
|
+
* - an update that changes `id`, `type`, `object` or `children`
|
|
85
|
+
* (`immutable_field`); restating the current value passes;
|
|
86
|
+
* - an update whose merged node has schema issues the node did not have
|
|
87
|
+
* before (`invalid_update`); kinds without a schema in this runtime pass;
|
|
88
|
+
* - an update writing a material preset the catalog does not know
|
|
89
|
+
* (`invalid_update`; a create is rejected like other invalid creates): it
|
|
90
|
+
* would render as the default finish;
|
|
91
|
+
* - an update of what a node's script owns, its `source` or the size it
|
|
92
|
+
* built (`scripted_field`): only a rebuild keeps them and the geometry in step;
|
|
93
|
+
* - any op on a node an earlier delete in the patch removed, or on a default
|
|
94
|
+
* gutter or downspout that delete regenerates, and any update of the roof
|
|
95
|
+
* segment holding them (`regenerated_default`).
|
|
96
|
+
*
|
|
97
|
+
* Deletes run through the bridge's own deletion preview (`planDeletion`; the
|
|
98
|
+
* local bridge uses core's store planner), one call per run of consecutive
|
|
99
|
+
* deletes as the bridge batches them, so cascades and wall merges are seen as
|
|
100
|
+
* that bridge applies them. A bridge without a preview gets a stricter rule:
|
|
101
|
+
* a create may not reuse any id present at the start of the patch.
|
|
102
|
+
* An update of a roof segment unsettles the default gutters its refresh may
|
|
103
|
+
* regenerate, as a delete does.
|
|
104
|
+
* Lives in the tool layer so every bridge behind `apply_patch` inherits it.
|
|
105
|
+
*/
|
|
106
|
+
export function assertPatchKeepsIdentity(patches, nodes, rootNodeIds, planDeletion) {
|
|
107
|
+
const initialIds = new Set(Object.keys(nodes));
|
|
108
|
+
let scene = {
|
|
109
|
+
nodes: { ...nodes },
|
|
110
|
+
rootNodeIds: [...rootNodeIds],
|
|
111
|
+
collections: {},
|
|
112
|
+
};
|
|
113
|
+
const at = (id) => scene.nodes[id];
|
|
114
|
+
const put = (node) => {
|
|
115
|
+
scene.nodes[node.id] = node;
|
|
116
|
+
};
|
|
117
|
+
// Default gutters and downspouts a delete run refreshes: the store may keep,
|
|
118
|
+
// move or replace them with freshly minted ids, so later ops in the same
|
|
119
|
+
// patch cannot address them reliably.
|
|
120
|
+
const regenerated = new Map();
|
|
121
|
+
// Roof segments whose `children` that refresh rewrites: an update restating
|
|
122
|
+
// their pre-delete children would bring back obsolete ids.
|
|
123
|
+
const regeneratedHosts = new Map();
|
|
124
|
+
const refuseRegenerated = (index, id) => {
|
|
125
|
+
const deleteIndex = regenerated.get(id);
|
|
126
|
+
if (deleteIndex === undefined)
|
|
127
|
+
return;
|
|
128
|
+
throw new PatchRefusedError('regenerated_default', index, id, `"${id}" is a default gutter or downspout that the edit at patches[${deleteIndex}] regenerates. Address it in a separate apply_patch call.`);
|
|
129
|
+
};
|
|
130
|
+
// Updating a roof or roof segment makes the store refresh that roof's
|
|
131
|
+
// default gutters, with the same effect on ids as a delete's refresh.
|
|
132
|
+
const unsettleRoofRefresh = (node, index) => {
|
|
133
|
+
const roofId = node.type === 'roof' ? node.id : node.type === 'roof-segment' ? node.parentId : null;
|
|
134
|
+
if (!roofId)
|
|
135
|
+
return;
|
|
136
|
+
const preview = previewDefaultGutterRefresh(scene.nodes, [roofId]);
|
|
137
|
+
for (const id of preview.unsettledIds)
|
|
138
|
+
regenerated.set(id, index);
|
|
139
|
+
for (const id of preview.regeneratedHostIds)
|
|
140
|
+
regeneratedHosts.set(id, index);
|
|
141
|
+
};
|
|
142
|
+
let pendingDeletes = [];
|
|
143
|
+
const flushDeletes = (index) => {
|
|
144
|
+
const plan = planDeletion
|
|
145
|
+
? planDeletion(scene, pendingDeletes)
|
|
146
|
+
: planNodeDeletion(scene, pendingDeletes, { mintDefaults: false });
|
|
147
|
+
for (const id of plan.unsettledIds)
|
|
148
|
+
regenerated.set(id, index);
|
|
149
|
+
for (const id of plan.regeneratedHostIds)
|
|
150
|
+
regeneratedHosts.set(id, index);
|
|
151
|
+
scene = { nodes: plan.nodes, rootNodeIds: plan.rootNodeIds, collections: plan.collections };
|
|
152
|
+
pendingDeletes = [];
|
|
153
|
+
};
|
|
154
|
+
patches.forEach((patch, index) => {
|
|
155
|
+
if (patch.op === 'create') {
|
|
156
|
+
// Parse like the store does, so defaults such as an empty `children`
|
|
157
|
+
// list exist for later ops in the patch; the bridge reports schema errors.
|
|
158
|
+
const parsed = parseNode(patch.node);
|
|
159
|
+
const node = (parsed.success ? parsed.data : patch.node);
|
|
160
|
+
if (typeof node?.id !== 'string')
|
|
161
|
+
return;
|
|
162
|
+
const preset = unknownMaterialPresetRefusal(node);
|
|
163
|
+
if (preset)
|
|
164
|
+
throw new Error(`invalid patch: patches[${index}] create "${node.id}": ${preset}`);
|
|
165
|
+
refuseRegenerated(index, node.id);
|
|
166
|
+
const effectiveParentId = patch.parentId ?? node.parentId;
|
|
167
|
+
if (effectiveParentId)
|
|
168
|
+
refuseRegenerated(index, effectiveParentId);
|
|
169
|
+
if (at(node.id) || (!planDeletion && initialIds.has(node.id))) {
|
|
170
|
+
throw new PatchRefusedError('node_exists', index, node.id, planDeletion || at(node.id)
|
|
171
|
+
? `create id "${node.id}" already exists. Use op "update" to change it, or delete it earlier in the same patch to replace it.`
|
|
172
|
+
: `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.`);
|
|
173
|
+
}
|
|
174
|
+
const parentId = patch.parentId ?? node.parentId ?? null;
|
|
175
|
+
if (patch.parentId !== undefined && !at(patch.parentId)) {
|
|
176
|
+
throw new Error(`invalid patch: patches[${index}] create parentId "${patch.parentId}" not found`);
|
|
177
|
+
}
|
|
178
|
+
put({ ...node, parentId });
|
|
179
|
+
const parent = parentId ? at(parentId) : undefined;
|
|
180
|
+
if (parent)
|
|
181
|
+
put(withChild(parent, node.id));
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
if (patch.op === 'update') {
|
|
185
|
+
refuseRegenerated(index, patch.id);
|
|
186
|
+
const hostDeleteIndex = regeneratedHosts.get(patch.id);
|
|
187
|
+
if (hostDeleteIndex !== undefined) {
|
|
188
|
+
throw new PatchRefusedError('regenerated_default', index, patch.id, `the edit at patches[${hostDeleteIndex}] regenerates the default gutters and downspouts under "${patch.id}", so its children are not known yet. Update it in a separate apply_patch call.`);
|
|
189
|
+
}
|
|
190
|
+
if (typeof patch.data?.parentId === 'string')
|
|
191
|
+
refuseRegenerated(index, patch.data.parentId);
|
|
192
|
+
const current = at(patch.id);
|
|
193
|
+
if (!current)
|
|
194
|
+
throw new Error(`invalid patch: patches[${index}] update id "${patch.id}" not found`);
|
|
195
|
+
if (!patch.data || typeof patch.data !== 'object')
|
|
196
|
+
return;
|
|
197
|
+
const data = patch.data;
|
|
198
|
+
for (const [field, code] of IMMUTABLE_FIELDS) {
|
|
199
|
+
const before = current[field];
|
|
200
|
+
if (field in data && !sameValue(data[field], before)) {
|
|
201
|
+
throw new PatchRefusedError(code, index, patch.id, `update cannot change "${field}" of "${patch.id}" (${JSON.stringify(before)} → ${JSON.stringify(data[field])}). Create a new node, delete the old one, or reparent children through their own parentId.`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const scripted = scriptedFieldRefusal(current, data);
|
|
205
|
+
if (scripted)
|
|
206
|
+
throw new PatchRefusedError('scripted_field', index, patch.id, scripted);
|
|
207
|
+
const preset = unknownMaterialPresetRefusal(data, current);
|
|
208
|
+
if (preset)
|
|
209
|
+
throw new PatchRefusedError('invalid_update', index, patch.id, preset);
|
|
210
|
+
const merged = { ...current, ...data };
|
|
211
|
+
const issuesAfter = schemaIssues(merged);
|
|
212
|
+
if (issuesAfter && issuesAfter.size > 0) {
|
|
213
|
+
const issuesBefore = schemaIssues(current) ?? new Set();
|
|
214
|
+
const added = [...issuesAfter].filter((issue) => !issuesBefore.has(issue));
|
|
215
|
+
if (added.length > 0) {
|
|
216
|
+
throw new PatchRefusedError('invalid_update', index, patch.id, `update of ${current.type} "${patch.id}" fails its schema: ${added.slice(0, 5).join('; ')}`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
if ('parentId' in data && data.parentId !== current.parentId) {
|
|
220
|
+
const newParent = typeof data.parentId === 'string' ? at(data.parentId) : undefined;
|
|
221
|
+
if (!(newParent && Array.isArray(newParent.children))) {
|
|
222
|
+
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
|
+
}
|
|
224
|
+
const before = { ...scene.nodes };
|
|
225
|
+
const oldParent = current.parentId ? at(current.parentId) : undefined;
|
|
226
|
+
if (oldParent)
|
|
227
|
+
put(withoutChild(oldParent, patch.id));
|
|
228
|
+
put(withChild(newParent, patch.id));
|
|
229
|
+
put(merged);
|
|
230
|
+
unsettleRoofRefresh(merged, index);
|
|
231
|
+
try {
|
|
232
|
+
validateNodeRelations(before, scene.nodes, [patch.id]);
|
|
233
|
+
}
|
|
234
|
+
catch (err) {
|
|
235
|
+
throw new PatchRefusedError('invalid_parent', index, patch.id, `${newParent.type} "${newParent.id}" does not accept "${patch.id}": ${err instanceof Error ? err.message : String(err)}`);
|
|
236
|
+
}
|
|
237
|
+
return;
|
|
238
|
+
}
|
|
239
|
+
put(merged);
|
|
240
|
+
unsettleRoofRefresh(merged, index);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
if (patch.op === 'delete') {
|
|
244
|
+
refuseRegenerated(index, patch.id);
|
|
245
|
+
if (!at(patch.id) || pendingDeletes.includes(patch.id)) {
|
|
246
|
+
throw new Error(`invalid patch: patches[${index}] delete id "${patch.id}" not found`);
|
|
247
|
+
}
|
|
248
|
+
pendingDeletes.push(patch.id);
|
|
249
|
+
// The bridge applies a run of consecutive deletes as one deleteNodes
|
|
250
|
+
// call, and wall merges and kind cascades depend on the whole id list,
|
|
251
|
+
// so plan the run together once it ends.
|
|
252
|
+
if (patches[index + 1]?.op !== 'delete')
|
|
253
|
+
flushDeletes(index);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/server';
|
|
2
2
|
import type { SceneOperations } from '../../operations/index.js';
|
|
3
3
|
/**
|
|
4
4
|
* Register the `photo_to_scene` orchestrator tool. Chains the vision
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/photo-to-scene/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/photo-to-scene/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAC7D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAGvD;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAEzF;AAED,OAAO,EACL,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,kBAAkB,CAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/server';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import type { SceneOperations } from '../../operations/index.js';
|
|
4
4
|
/**
|
|
@@ -10,8 +10,8 @@ export declare const photoToSceneInput: {
|
|
|
10
10
|
scaleHint: z.ZodOptional<z.ZodString>;
|
|
11
11
|
name: z.ZodDefault<z.ZodString>;
|
|
12
12
|
save: z.ZodDefault<z.ZodBoolean>;
|
|
13
|
-
defaultWallThickness: z.ZodDefault<z.ZodPipe<z.ZodUnion<
|
|
14
|
-
defaultWallHeight: z.ZodDefault<z.ZodPipe<z.ZodUnion<
|
|
13
|
+
defaultWallThickness: z.ZodDefault<z.ZodPipe<z.ZodUnion<[z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
|
|
14
|
+
defaultWallHeight: z.ZodDefault<z.ZodPipe<z.ZodUnion<[z.ZodNumber, z.ZodString]>, z.ZodTransform<number, string | number>>>;
|
|
15
15
|
};
|
|
16
16
|
export declare const photoToSceneOutput: {
|
|
17
17
|
sceneId: z.ZodOptional<z.ZodString>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"photo-to-scene.d.ts","sourceRoot":"","sources":["../../../src/tools/photo-to-scene/photo-to-scene.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"photo-to-scene.d.ts","sourceRoot":"","sources":["../../../src/tools/photo-to-scene/photo-to-scene.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAM7D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AAKvD;;;GAGG;AACH,eAAO,MAAM,iBAAiB;IAC5B,KAAK;IACL,SAAS;IACT,IAAI;IACJ,IAAI;IACJ,oBAAoB;IAIpB,iBAAiB;CAIlB,CAAA;AAED,eAAO,MAAM,kBAAkB;IAC7B,OAAO;IACP,GAAG;IACH,KAAK;IACL,KAAK;IACL,UAAU;IACV,KAAK;IACL,KAAK;CACN,CAAA;AAmTD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CA8ErF"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { ProtocolError, ProtocolErrorCode } from '@modelcontextprotocol/server';
|
|
2
|
+
import { newZone } from '@pascal-app/core';
|
|
3
|
+
import { AnyNode, BuildingNode, LevelNode, SiteNode, WallNode } from '@pascal-app/core/schema';
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
import { DESTRUCTIVE_OPEN_WORLD_TOOL_ANNOTATIONS } from '../annotations.js';
|
|
5
6
|
import { appendLiveSceneEvent } from '../live-sync.js';
|
|
@@ -121,7 +122,7 @@ function extractText(content) {
|
|
|
121
122
|
async function callVisionSampling(server, image, scaleHint) {
|
|
122
123
|
const caps = server.server.getClientCapabilities();
|
|
123
124
|
if (!caps?.sampling) {
|
|
124
|
-
throw new
|
|
125
|
+
throw new ProtocolError(ProtocolErrorCode.InvalidRequest, 'sampling_unavailable');
|
|
125
126
|
}
|
|
126
127
|
const imageBlock = await resolveImageBlock(image);
|
|
127
128
|
const instruction = scaleHint
|
|
@@ -140,7 +141,7 @@ async function callVisionSampling(server, image, scaleHint) {
|
|
|
140
141
|
});
|
|
141
142
|
const text = extractText(response.content);
|
|
142
143
|
if (!text) {
|
|
143
|
-
throw new
|
|
144
|
+
throw new ProtocolError(ProtocolErrorCode.InternalError, 'sampling_response_unparseable', {
|
|
144
145
|
reason: 'no text content returned by host',
|
|
145
146
|
});
|
|
146
147
|
}
|
|
@@ -149,14 +150,14 @@ async function callVisionSampling(server, image, scaleHint) {
|
|
|
149
150
|
parsed = JSON.parse(text);
|
|
150
151
|
}
|
|
151
152
|
catch (err) {
|
|
152
|
-
throw new
|
|
153
|
+
throw new ProtocolError(ProtocolErrorCode.InternalError, 'sampling_response_unparseable', {
|
|
153
154
|
raw: text,
|
|
154
155
|
reason: err instanceof Error ? err.message : String(err),
|
|
155
156
|
});
|
|
156
157
|
}
|
|
157
158
|
const validation = VisionResponseSchema.safeParse(parsed);
|
|
158
159
|
if (!validation.success) {
|
|
159
|
-
throw new
|
|
160
|
+
throw new ProtocolError(ProtocolErrorCode.InternalError, 'sampling_response_invalid', {
|
|
160
161
|
raw: text,
|
|
161
162
|
errors: validation.error.issues,
|
|
162
163
|
});
|
|
@@ -236,7 +237,7 @@ function buildSceneGraphFromVision(vision, defaultWallThickness, defaultWallHeig
|
|
|
236
237
|
for (let i = 0; i < vision.rooms.length; i++) {
|
|
237
238
|
const r = vision.rooms[i];
|
|
238
239
|
try {
|
|
239
|
-
const zone =
|
|
240
|
+
const zone = newZone({
|
|
240
241
|
name: r.name,
|
|
241
242
|
polygon: r.polygon,
|
|
242
243
|
});
|
|
@@ -279,8 +280,8 @@ export function registerPhotoToScene(server, bridge) {
|
|
|
279
280
|
server.registerTool('photo_to_scene', {
|
|
280
281
|
title: 'Photo to Pascal scene',
|
|
281
282
|
description: 'Orchestrator: analyse a floor-plan photo via MCP sampling, translate the structured vision result into a Pascal SceneGraph (site → building → level with walls and zones), optionally save it, and swap the bridge to the new scene. Requires host support for sampling.',
|
|
282
|
-
inputSchema: photoToSceneInput,
|
|
283
|
-
outputSchema: photoToSceneOutput,
|
|
283
|
+
inputSchema: z.object(photoToSceneInput),
|
|
284
|
+
outputSchema: z.object(photoToSceneOutput),
|
|
284
285
|
annotations: DESTRUCTIVE_OPEN_WORLD_TOOL_ANNOTATIONS,
|
|
285
286
|
}, async ({ image, scaleHint, name, save, defaultWallThickness, defaultWallHeight }) => {
|
|
286
287
|
// 1. Vision.
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/server';
|
|
2
|
+
import { type ProjectFileToolExecutor } from '@pascal-app/core/agent-operations';
|
|
3
|
+
export declare function registerProjectFileTools(server: McpServer, execute: ProjectFileToolExecutor): void;
|
|
4
|
+
//# sourceMappingURL=project-files.d.ts.map
|
|
@@ -0,0 +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;AAQ1C,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,uBAAuB,QAmC3F"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { executeProjectFileTool, } from '@pascal-app/core/agent-operations';
|
|
2
|
+
import { isAgentRefusal, PROJECT_FILE_TOOL_CONTRACTS, refusalPayload, } from '@pascal-app/core/agent-tools';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
export function registerProjectFileTools(server, execute) {
|
|
5
|
+
for (const contract of PROJECT_FILE_TOOL_CONTRACTS) {
|
|
6
|
+
server.registerTool(contract.name, {
|
|
7
|
+
title: contract.title,
|
|
8
|
+
description: contract.description,
|
|
9
|
+
inputSchema: z.object(contract.input).strict(),
|
|
10
|
+
annotations: contract.annotations,
|
|
11
|
+
}, async (input, ctx) => {
|
|
12
|
+
try {
|
|
13
|
+
const result = await executeProjectFileTool(contract.name, input, execute, ctx.mcpReq.signal);
|
|
14
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
15
|
+
}
|
|
16
|
+
catch (error) {
|
|
17
|
+
const result = isAgentRefusal(error)
|
|
18
|
+
? refusalPayload(error)
|
|
19
|
+
: {
|
|
20
|
+
code: 'file_error',
|
|
21
|
+
error: 'Could not complete the file request. List project files before retrying an upload.',
|
|
22
|
+
};
|
|
23
|
+
return {
|
|
24
|
+
isError: true,
|
|
25
|
+
content: [{ type: 'text', text: JSON.stringify(result) }],
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
package/dist/tools/redo.d.ts
CHANGED
|
@@ -1,14 +1,16 @@
|
|
|
1
|
-
import type { McpServer } from '@modelcontextprotocol/
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/server';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import type { SceneOperations } from '../operations/index.js';
|
|
4
4
|
export declare const redoInput: {
|
|
5
5
|
steps: z.ZodOptional<z.ZodNumber>;
|
|
6
6
|
};
|
|
7
7
|
export declare const redoOutput: {
|
|
8
|
+
project: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
9
|
+
unsaved: z.ZodOptional<z.ZodString>;
|
|
8
10
|
persistence: z.ZodOptional<z.ZodObject<{
|
|
9
11
|
status: z.ZodEnum<{
|
|
10
|
-
unbound: "unbound";
|
|
11
12
|
events_unsupported: "events_unsupported";
|
|
13
|
+
unbound: "unbound";
|
|
12
14
|
}>;
|
|
13
15
|
warning: z.ZodString;
|
|
14
16
|
}, z.core.$strip>>;
|
package/dist/tools/redo.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redo.d.ts","sourceRoot":"","sources":["../../src/tools/redo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"redo.d.ts","sourceRoot":"","sources":["../../src/tools/redo.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAC7D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AACvB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AASpD,eAAO,MAAM,SAAS;IACpB,KAAK;CACN,CAAA;AAED,eAAO,MAAM,UAAU;;;;;;;;;;;CAGtB,CAAA;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,eAAe,GAAG,IAAI,CAsB7E"}
|
package/dist/tools/redo.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { DESTRUCTIVE_TOOL_ANNOTATIONS } from './annotations.js';
|
|
3
|
-
import { liveSyncOutput, persistencePayload, publishLiveSceneSnapshot } from './live-sync.js';
|
|
3
|
+
import { currentLiveSync, liveSyncOutput, persistencePayload, publishLiveSceneSnapshot, } from './live-sync.js';
|
|
4
4
|
export const redoInput = {
|
|
5
5
|
steps: z.number().int().positive().optional(),
|
|
6
6
|
};
|
|
@@ -12,12 +12,12 @@ export function registerRedo(server, bridge) {
|
|
|
12
12
|
server.registerTool('redo', {
|
|
13
13
|
title: 'Redo',
|
|
14
14
|
description: 'Redo the next N previously-undone steps (default 1). Returns the number of steps actually redone.',
|
|
15
|
-
inputSchema: redoInput,
|
|
16
|
-
outputSchema: redoOutput,
|
|
15
|
+
inputSchema: z.object(redoInput),
|
|
16
|
+
outputSchema: z.object(redoOutput),
|
|
17
17
|
annotations: DESTRUCTIVE_TOOL_ANNOTATIONS,
|
|
18
18
|
}, async ({ steps }) => {
|
|
19
19
|
const redone = bridge.redo(steps ?? 1);
|
|
20
|
-
const persistence = redone > 0 ? await publishLiveSceneSnapshot(bridge, 'redo') :
|
|
20
|
+
const persistence = redone > 0 ? await publishLiveSceneSnapshot(bridge, 'redo') : currentLiveSync(bridge);
|
|
21
21
|
const payload = { redone, ...persistencePayload(persistence) };
|
|
22
22
|
return {
|
|
23
23
|
content: [{ type: 'text', text: JSON.stringify(payload) }],
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { McpServer } from '@modelcontextprotocol/server';
|
|
2
|
+
import { type RetiredTool } from '@pascal-app/core/agent-tools';
|
|
3
|
+
/**
|
|
4
|
+
* A retired tool (`RETIRED_MCP_TOOLS`, or a host's own map) is listed nowhere and still answered: a
|
|
5
|
+
* call to its name gets `tool_retired` with the tool that replaced it and how to use that one,
|
|
6
|
+
* where the SDK would answer an unknown name with a bare error. Install it once the tools are
|
|
7
|
+
* registered.
|
|
8
|
+
*/
|
|
9
|
+
export declare function answerRetiredTools(server: McpServer, retired?: Readonly<Record<string, RetiredTool>>): void;
|
|
10
|
+
//# sourceMappingURL=retired-tools.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retired-tools.d.ts","sourceRoot":"","sources":["../../src/tools/retired-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,8BAA8B,CAAA;AAC7D,OAAO,EAEL,KAAK,WAAW,EAEjB,MAAM,8BAA8B,CAAA;AAKrC;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,SAAS,EACjB,OAAO,GAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAqB,GACjE,IAAI,CAYN"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { RETIRED_MCP_TOOLS, retiredToolRefusal, } from '@pascal-app/core/agent-tools';
|
|
2
|
+
import { toolError } from './errors.js';
|
|
3
|
+
/**
|
|
4
|
+
* A retired tool (`RETIRED_MCP_TOOLS`, or a host's own map) is listed nowhere and still answered: a
|
|
5
|
+
* call to its name gets `tool_retired` with the tool that replaced it and how to use that one,
|
|
6
|
+
* where the SDK would answer an unknown name with a bare error. Install it once the tools are
|
|
7
|
+
* registered.
|
|
8
|
+
*/
|
|
9
|
+
export function answerRetiredTools(server, retired = RETIRED_MCP_TOOLS) {
|
|
10
|
+
const registry = server.server;
|
|
11
|
+
const original = registry._requestHandlers.get('tools/call');
|
|
12
|
+
if (!original)
|
|
13
|
+
return;
|
|
14
|
+
server.server.removeRequestHandler('tools/call');
|
|
15
|
+
server.server.setRequestHandler('tools/call', async (request, ctx) => {
|
|
16
|
+
const { name } = request.params;
|
|
17
|
+
if (!Object.hasOwn(retired, name))
|
|
18
|
+
return (await original(request, ctx));
|
|
19
|
+
const { error, ...answer } = retiredToolRefusal(name, retired[name]);
|
|
20
|
+
return toolError(error, answer);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"roof-level-guard.d.ts","sourceRoot":"","sources":["../../src/tools/roof-level-guard.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { seatWrittenRoofs } from '@pascal-app/core/agent-operations';
|
|
2
|
+
import { isAgentRefusal } from '@pascal-app/core/agent-tools';
|
|
3
|
+
import { PatchRefusedError, registerPatchGuard } from './patch-guards.js';
|
|
4
|
+
/**
|
|
5
|
+
* The roof-level rule: a roof the patch puts on its walls' storey is seated on the level above by
|
|
6
|
+
* ops appended to the patch, said in notes, so every refusal's patchIndex stays on the model's own
|
|
7
|
+
* op; storey content on a roof level, a walled level declared the roof level and a roof level
|
|
8
|
+
* moved under a storey are refused. After the level heights, before the identity check, which
|
|
9
|
+
* checks the appended ops too.
|
|
10
|
+
*/
|
|
11
|
+
function roofLevels(patches, scene) {
|
|
12
|
+
try {
|
|
13
|
+
return seatWrittenRoofs(patches, scene);
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
if (!isAgentRefusal(error))
|
|
17
|
+
throw error;
|
|
18
|
+
const { index, id } = error.details;
|
|
19
|
+
throw new PatchRefusedError(error.code, index, id, error.message);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
registerPatchGuard({ name: 'roof-levels', order: 40, run: roofLevels });
|