@womp/spark-agent 4.4.1 → 5.0.0
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/.env.example +6 -0
- package/README.md +21 -2
- package/dist/capabilities/geometry.d.ts +0 -3
- package/dist/capabilities/geometry.js +7 -8
- package/dist/capabilities/openscad-instructions.d.ts +1 -1
- package/dist/capabilities/openscad-instructions.js +80 -81
- package/dist/capabilities/openscad.d.ts +0 -3
- package/dist/capabilities/openscad.js +1 -2
- package/dist/capabilities/scene.d.ts +4 -11
- package/dist/capabilities/scene.js +38 -55
- package/dist/generated/geometry-declarations.d.ts +1 -1
- package/dist/generated/geometry-declarations.js +1 -1
- package/dist/generated/openscad-declarations.d.ts +1 -1
- package/dist/generated/openscad-declarations.js +1 -1
- package/dist/generated/scene-declarations.d.ts +1 -1
- package/dist/generated/scene-declarations.js +1 -1
- package/dist/native-executor.js +2 -2
- package/dist/openscad.d.ts +1 -2
- package/dist/openscad.js +1 -19
- package/dist/quickjs-executor.js +1 -1
- package/dist/repair-state.d.ts +2 -0
- package/dist/repair-state.js +15 -0
- package/dist/runtime.d.ts +1 -5
- package/dist/runtime.js +8 -14
- package/dist/scene-environment.js +1 -1
- package/dist/scene-health.d.ts +1 -2
- package/dist/scene-health.js +0 -17
- package/dist/scene-operations.d.ts +5 -5
- package/dist/scene-operations.js +24 -43
- package/dist/sdk-geometry-host.d.ts +2 -2
- package/dist/sdk-geometry-host.js +44 -61
- package/dist/sdk-openscad-host.js +63 -102
- package/dist/sdk-scene-health-host.d.ts +2 -1
- package/dist/sdk-scene-health-host.js +54 -4
- package/dist/sdk-scene-host.d.ts +20 -5
- package/dist/sdk-scene-host.js +34 -31
- package/dist/sdk-scene-screenshot.d.ts +2 -2
- package/dist/sdk-scene-screenshot.js +3 -16
- package/dist/server/scene-access.d.ts +4 -0
- package/dist/server/scene-access.js +40 -0
- package/dist/server/standalone.js +24 -19
- package/dist/womp-scene-api.d.ts +15 -0
- package/dist/womp-scene-api.js +18 -1
- package/package.json +139 -139
- package/dist/server/scene-initialization.d.ts +0 -3
- package/dist/server/scene-initialization.js +0 -44
package/dist/runtime.js
CHANGED
|
@@ -46,7 +46,7 @@ export class KakapoRuntime {
|
|
|
46
46
|
this.eventSink(event);
|
|
47
47
|
}
|
|
48
48
|
async connect() {
|
|
49
|
-
await this.sdkCall("connect", [], () => this.api.connect());
|
|
49
|
+
await this.sdkCall("connect", [], () => this.api.connect({ refreshScene: false }));
|
|
50
50
|
this.fonts = await this.sdkCall("listFonts", [], () => this.api.listFonts());
|
|
51
51
|
this.rootId = 0;
|
|
52
52
|
}
|
|
@@ -135,31 +135,25 @@ export class KakapoRuntime {
|
|
|
135
135
|
print,
|
|
136
136
|
});
|
|
137
137
|
}
|
|
138
|
-
async
|
|
139
|
-
await this.api.refreshScene();
|
|
140
|
-
return { revision: await this.api.getSceneRevision(), scene: this.api.getScene() };
|
|
141
|
-
}
|
|
142
|
-
async edit(callback, expectedRevision, context) {
|
|
138
|
+
async access(callback, context) {
|
|
143
139
|
let changed = false;
|
|
144
140
|
const ordinal = this.editOrdinals.get(context) ?? 0;
|
|
145
141
|
this.editOrdinals.set(context, ordinal + 1);
|
|
146
142
|
const receipt = {
|
|
147
|
-
id: `${context.runId}:${context.toolCallId}:scene.
|
|
143
|
+
id: `${context.runId}:${context.toolCallId}:scene.access:${ordinal}`,
|
|
148
144
|
capability: "scene",
|
|
149
|
-
operation: "
|
|
145
|
+
operation: "access",
|
|
150
146
|
};
|
|
151
147
|
let value;
|
|
152
148
|
try {
|
|
153
|
-
|
|
149
|
+
const accessed = await this.api.accessScene(async (scene) => {
|
|
154
150
|
context.signal.throwIfAborted();
|
|
155
|
-
if ((await this.api.getSceneRevision()) !== expectedRevision) {
|
|
156
|
-
throw new Error("Scene changed since inspection. Read the scene again before editing.");
|
|
157
|
-
}
|
|
158
151
|
const result = await callback(this.sceneApi(scene, () => undefined));
|
|
159
152
|
context.signal.throwIfAborted();
|
|
160
|
-
changed = (await this.api.getSceneRevision()) !== expectedRevision;
|
|
161
153
|
return result;
|
|
162
154
|
});
|
|
155
|
+
value = accessed.value;
|
|
156
|
+
changed = accessed.changed;
|
|
163
157
|
}
|
|
164
158
|
catch (error) {
|
|
165
159
|
await context.recordReceipt({
|
|
@@ -191,7 +185,7 @@ export class KakapoRuntime {
|
|
|
191
185
|
error: (...values) => consoleFacade.log("ERROR", ...values),
|
|
192
186
|
};
|
|
193
187
|
try {
|
|
194
|
-
const value = await this.api.
|
|
188
|
+
const { value } = await this.api.accessScene((scene) => {
|
|
195
189
|
const api = this.sceneApi(scene, (...values) => consoleFacade.log(...values));
|
|
196
190
|
return this.executor.execute(code, { ...api, console: consoleFacade }, 60_000);
|
|
197
191
|
});
|
package/dist/scene-health.d.ts
CHANGED
|
@@ -63,10 +63,9 @@ export declare function receiptSceneHealth(health: SceneHealth, limit?: number):
|
|
|
63
63
|
* changes the one payload that is unbounded by construction and nothing else.
|
|
64
64
|
*/
|
|
65
65
|
export declare function boundedReceiptDetails(details: Record<string, unknown>): Record<string, unknown>;
|
|
66
|
-
export declare function getSdkSceneAreaId(api: KakapoAPI, objectId: number): string;
|
|
66
|
+
export declare function getSdkSceneAreaId(api: Pick<KakapoAPI, "getNode">, objectId: number): string;
|
|
67
67
|
export declare function getSdkSceneAreaIds(api: KakapoAPI): string[];
|
|
68
68
|
export declare function hasGeometryOverflow(health: SceneHealth): boolean;
|
|
69
69
|
export declare function hasSceneResourcePressure(health: SceneHealth): boolean;
|
|
70
70
|
export declare function parseSceneHealth(response: unknown, affectedAreaIds: string[]): SceneHealth;
|
|
71
71
|
export declare function readSdkSceneHealth(api: KakapoAPI, affectedAreaIds: string[], signal?: AbortSignal): Promise<SceneHealth>;
|
|
72
|
-
export declare function readStableSdkSceneHealth(api: KakapoAPI, affectedAreaIds: string[], signal?: AbortSignal): Promise<SceneHealth>;
|
package/dist/scene-health.js
CHANGED
|
@@ -236,20 +236,3 @@ export async function readSdkSceneHealth(api, affectedAreaIds, signal) {
|
|
|
236
236
|
removeAbortListener?.();
|
|
237
237
|
}
|
|
238
238
|
}
|
|
239
|
-
export async function readStableSdkSceneHealth(api, affectedAreaIds, signal) {
|
|
240
|
-
try {
|
|
241
|
-
signal?.throwIfAborted();
|
|
242
|
-
const before = await api.getSceneRevision();
|
|
243
|
-
const health = await readSdkSceneHealth(api, affectedAreaIds, signal);
|
|
244
|
-
signal?.throwIfAborted();
|
|
245
|
-
await api.refreshScene();
|
|
246
|
-
signal?.throwIfAborted();
|
|
247
|
-
const after = await api.getSceneRevision();
|
|
248
|
-
if (before === after)
|
|
249
|
-
return health;
|
|
250
|
-
return unverifiedSceneHealth(affectedAreaIds, "The scene changed while reading its health.");
|
|
251
|
-
}
|
|
252
|
-
catch (error) {
|
|
253
|
-
return unverifiedSceneHealth(affectedAreaIds, String(error));
|
|
254
|
-
}
|
|
255
|
-
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type JsonValue, type KakapoAPI, type SceneEdit, type SceneSaveResult } from "@womp/kakapo-sdk";
|
|
1
|
+
import { type JsonValue, type KakapoAPI, type NativeSceneSnapshot, type SceneEdit, type SceneSaveResult } from "@womp/kakapo-sdk";
|
|
2
2
|
export type SceneEditResult = {
|
|
3
3
|
value: JsonValue;
|
|
4
4
|
} | {
|
|
@@ -7,8 +7,6 @@ export type SceneEditResult = {
|
|
|
7
7
|
export type SceneOperation = {
|
|
8
8
|
id: string;
|
|
9
9
|
targetKey: string;
|
|
10
|
-
expectedRevision: string;
|
|
11
|
-
resultRevision?: string;
|
|
12
10
|
revision: number;
|
|
13
11
|
status: "started" | "prepared" | "committed" | "rolled_back" | "unknown";
|
|
14
12
|
result?: SceneEditResult;
|
|
@@ -24,7 +22,7 @@ export interface SceneOperationStore {
|
|
|
24
22
|
createSceneOperation(operation: SceneOperation): Promise<boolean>;
|
|
25
23
|
saveSceneOperation(operation: SceneOperation): Promise<SceneOperation>;
|
|
26
24
|
}
|
|
27
|
-
/**
|
|
25
|
+
/** Recover acknowledged/no-op results without replaying or refetching an uncertain edit. */
|
|
28
26
|
export declare function recoverSceneOperation(options: {
|
|
29
27
|
api: KakapoAPI;
|
|
30
28
|
store: SceneOperationStore;
|
|
@@ -36,12 +34,14 @@ export declare function recoverSceneOperation(options: {
|
|
|
36
34
|
export declare function executeSceneOperation<T>(options: {
|
|
37
35
|
api: KakapoAPI;
|
|
38
36
|
store: SceneOperationStore;
|
|
39
|
-
operation: Pick<SceneOperation, "id" | "targetKey"
|
|
37
|
+
operation: Pick<SceneOperation, "id" | "targetKey">;
|
|
40
38
|
assertEditable(): void | Promise<void>;
|
|
41
39
|
edit(draft: SceneEdit): T | Promise<T>;
|
|
42
40
|
prepareCommit?(operation: SceneOperation): Promise<void>;
|
|
41
|
+
onCommittedScene?(scene: NativeSceneSnapshot): void;
|
|
43
42
|
saveToRenderer?: boolean;
|
|
44
43
|
}): Promise<{
|
|
45
44
|
value: T;
|
|
46
45
|
operation: SceneOperation;
|
|
46
|
+
reused?: boolean;
|
|
47
47
|
}>;
|
package/dist/scene-operations.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { KakapoSceneCommitError } from "@womp/kakapo-sdk";
|
|
2
|
-
import { metadataObject } from "./scene-metadata.js";
|
|
3
2
|
function savedResult(value) {
|
|
4
3
|
if (value === undefined)
|
|
5
4
|
return { empty: true };
|
|
@@ -11,9 +10,9 @@ function savedResult(value) {
|
|
|
11
10
|
return;
|
|
12
11
|
if (typeof item !== "object" || item === null ||
|
|
13
12
|
(!Array.isArray(item) && Object.getPrototypeOf(item) !== Object.prototype && Object.getPrototypeOf(item) !== null))
|
|
14
|
-
throw new Error("scene.
|
|
13
|
+
throw new Error("scene.access must return JSON data or undefined");
|
|
15
14
|
if (active.has(item))
|
|
16
|
-
throw new Error("scene.
|
|
15
|
+
throw new Error("scene.access cannot return cyclic data");
|
|
17
16
|
active.add(item);
|
|
18
17
|
for (const nested of Object.values(item))
|
|
19
18
|
validate(nested);
|
|
@@ -27,26 +26,17 @@ function resultValue(operation) {
|
|
|
27
26
|
throw new Error("The saved scene operation result is missing");
|
|
28
27
|
return ("value" in operation.result ? structuredClone(operation.result.value) : undefined);
|
|
29
28
|
}
|
|
30
|
-
function committedIds(ext) {
|
|
31
|
-
const ids = metadataObject(ext.spark).committedSceneEdits;
|
|
32
|
-
return Array.isArray(ids) ? ids.filter((id) => typeof id === "string") : [];
|
|
33
|
-
}
|
|
34
29
|
function complete(operation, status) {
|
|
35
30
|
return { ...operation, status, completedAt: operation.completedAt ?? new Date().toISOString() };
|
|
36
31
|
}
|
|
37
|
-
/**
|
|
32
|
+
/** Recover acknowledged/no-op results without replaying or refetching an uncertain edit. */
|
|
38
33
|
export async function recoverSceneOperation(options) {
|
|
39
34
|
await options.assertActive();
|
|
40
35
|
const previous = await options.store.getSceneOperation(options.targetKey, options.id);
|
|
41
36
|
if (!previous || previous.status === "committed" || previous.status === "rolled_back")
|
|
42
37
|
return previous;
|
|
43
|
-
await options.api.refreshScene();
|
|
44
|
-
const ext = options.api.getSceneExt();
|
|
45
|
-
const revision = await options.api.getSceneRevision();
|
|
46
38
|
await options.assertActive();
|
|
47
|
-
|
|
48
|
-
previous.resultRevision === revision && committedIds(ext).includes(previous.id);
|
|
49
|
-
if ((previous.changed === false || matches) && previous.result) {
|
|
39
|
+
if (previous.changed === false && previous.result) {
|
|
50
40
|
return options.store.saveSceneOperation(complete(previous, "committed"));
|
|
51
41
|
}
|
|
52
42
|
return previous;
|
|
@@ -58,19 +48,17 @@ export async function executeSceneOperation(options) {
|
|
|
58
48
|
await options.assertEditable();
|
|
59
49
|
const previous = await store.getSceneOperation(identity.targetKey, identity.id);
|
|
60
50
|
if (previous) {
|
|
61
|
-
if (previous.expectedRevision !== identity.expectedRevision)
|
|
62
|
-
throw new Error("Scene operation identity was reused with a different revision");
|
|
63
51
|
if ((previous.save !== undefined) !== savesScene)
|
|
64
52
|
throw new Error("Scene operation identity was reused with a different save policy");
|
|
65
53
|
if (previous.status === "committed")
|
|
66
|
-
return { value: resultValue(previous), operation: previous };
|
|
54
|
+
return { value: resultValue(previous), operation: previous, reused: true };
|
|
67
55
|
if (previous.status === "rolled_back")
|
|
68
56
|
throw new Error(previous.error ?? "Scene operation was rolled back");
|
|
69
57
|
const recovered = await recoverSceneOperation({
|
|
70
58
|
api, store, targetKey: identity.targetKey, id: identity.id, assertActive: options.assertEditable,
|
|
71
59
|
});
|
|
72
60
|
if (recovered?.status === "committed")
|
|
73
|
-
return { value: resultValue(recovered), operation: recovered };
|
|
61
|
+
return { value: resultValue(recovered), operation: recovered, reused: true };
|
|
74
62
|
throw new Error("Scene edit outcome is uncertain. Inspect the scene before making a new edit; this operation was not replayed.");
|
|
75
63
|
}
|
|
76
64
|
let operation = {
|
|
@@ -82,33 +70,26 @@ export async function executeSceneOperation(options) {
|
|
|
82
70
|
try {
|
|
83
71
|
const edit = async (draft) => {
|
|
84
72
|
await options.assertEditable();
|
|
85
|
-
|
|
86
|
-
throw new Error("Scene changed since inspection. Read the scene again before editing.");
|
|
87
|
-
const result = savedResult(await options.edit(draft));
|
|
88
|
-
await options.assertEditable();
|
|
89
|
-
const changed = await api.getSceneRevision() !== identity.expectedRevision;
|
|
90
|
-
if (changed) {
|
|
91
|
-
const spark = metadataObject(draft.getSceneExt().spark);
|
|
92
|
-
const ids = [...committedIds(api.getSceneExt()), identity.id].slice(-64);
|
|
93
|
-
draft.patchSceneExt([{ op: "add", path: "/spark", value: { ...spark, committedSceneEdits: ids } }]);
|
|
94
|
-
}
|
|
95
|
-
operation = await store.saveSceneOperation({
|
|
96
|
-
...operation, status: "prepared", result, changed,
|
|
97
|
-
...(changed ? { resultRevision: await api.getSceneRevision() } : {}),
|
|
98
|
-
...(savesScene && !changed ? { save: { status: "not_needed" } } : {}),
|
|
99
|
-
});
|
|
100
|
-
await options.assertEditable();
|
|
101
|
-
if (changed)
|
|
102
|
-
await options.prepareCommit?.(operation);
|
|
73
|
+
operation = { ...operation, result: savedResult(await options.edit(draft)) };
|
|
103
74
|
await options.assertEditable();
|
|
104
75
|
};
|
|
105
|
-
|
|
106
|
-
|
|
76
|
+
const committed = await api.accessScene(edit, {
|
|
77
|
+
save: savesScene,
|
|
78
|
+
beforeCommit: async (changed) => {
|
|
79
|
+
await options.assertEditable();
|
|
80
|
+
operation = await store.saveSceneOperation({
|
|
81
|
+
...operation, status: "prepared", changed,
|
|
82
|
+
...(savesScene && !changed ? { save: { status: "not_needed" } } : {}),
|
|
83
|
+
});
|
|
84
|
+
await options.assertEditable();
|
|
85
|
+
if (changed)
|
|
86
|
+
await options.prepareCommit?.(operation);
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
if (savesScene)
|
|
107
90
|
operation = { ...operation, save: committed.save };
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
await api.editScene(edit);
|
|
111
|
-
}
|
|
91
|
+
if (committed.changed)
|
|
92
|
+
options.onCommittedScene?.(committed.scene);
|
|
112
93
|
}
|
|
113
94
|
catch (error) {
|
|
114
95
|
const status = error instanceof KakapoSceneCommitError ? error.outcome
|
|
@@ -125,7 +106,7 @@ export async function executeSceneOperation(options) {
|
|
|
125
106
|
}
|
|
126
107
|
throw error;
|
|
127
108
|
}
|
|
128
|
-
//
|
|
109
|
+
// A prepared record alone never proves an uncertain commit succeeded.
|
|
129
110
|
operation = await store.saveSceneOperation(complete(operation, "committed"));
|
|
130
111
|
return { value: resultValue(operation), operation };
|
|
131
112
|
}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { type SdkSceneEditor } from "./sdk-scene-host.js";
|
|
2
2
|
import type { GeometryHost } from "./capabilities.js";
|
|
3
|
-
import {
|
|
3
|
+
import { readSdkSceneHealth } from "./scene-health.js";
|
|
4
4
|
import { type RepairStateStore } from "./repair-state.js";
|
|
5
5
|
export type SdkGeometryHostOptions = {
|
|
6
6
|
editor: SdkSceneEditor;
|
|
7
7
|
repairs: RepairStateStore;
|
|
8
8
|
canGrow?: () => boolean;
|
|
9
|
-
onHealth?: (health: Awaited<ReturnType<typeof
|
|
9
|
+
onHealth?: (health: Awaited<ReturnType<typeof readSdkSceneHealth>>) => void;
|
|
10
10
|
onSelect?: (objectId: number) => void | Promise<void>;
|
|
11
11
|
};
|
|
12
12
|
export declare function createSdkGeometryHost(options: SdkGeometryHostOptions): GeometryHost;
|
|
@@ -2,7 +2,7 @@ import { compileGeometry, migrateGraphExt, readEditableGeometry, } from "./geome
|
|
|
2
2
|
import { graphExtSchema } from "./geometry/graph.js";
|
|
3
3
|
import { boundedDiagnostics } from "./geometry/diagnostics.js";
|
|
4
4
|
import { migrateParameterOverrides, readParameterOverrides, } from "./geometry/parameters.js";
|
|
5
|
-
import {
|
|
5
|
+
import { unverifiedSceneHealth } from "./scene-health.js";
|
|
6
6
|
import { createGeometryRepairTracker, } from "./repair-state.js";
|
|
7
7
|
function errorMessage(error) {
|
|
8
8
|
return error instanceof Error ? error.message : String(error);
|
|
@@ -57,7 +57,6 @@ async function readTarget(api, objectId, target = resolveTarget(api, objectId))
|
|
|
57
57
|
instanceId: target.instanceId,
|
|
58
58
|
source: null,
|
|
59
59
|
instances: [],
|
|
60
|
-
revision: await api.getSceneRevision(),
|
|
61
60
|
diagnostics: [],
|
|
62
61
|
};
|
|
63
62
|
if (target.graphId === undefined || target.graph === undefined)
|
|
@@ -93,7 +92,7 @@ function writeGraphStorage(draft, graphId, graph, instances) {
|
|
|
93
92
|
path: "/scripts",
|
|
94
93
|
value: { graphs: {}, instances: {} },
|
|
95
94
|
},
|
|
96
|
-
]);
|
|
95
|
+
], { returnScene: false });
|
|
97
96
|
}
|
|
98
97
|
else {
|
|
99
98
|
const record = scripts;
|
|
@@ -111,7 +110,7 @@ function writeGraphStorage(draft, graphId, graph, instances) {
|
|
|
111
110
|
value: {},
|
|
112
111
|
});
|
|
113
112
|
if (additions.length)
|
|
114
|
-
draft.patchSceneStorage(additions);
|
|
113
|
+
draft.patchSceneStorage(additions, { returnScene: false });
|
|
115
114
|
}
|
|
116
115
|
draft.patchSceneStorage([
|
|
117
116
|
{
|
|
@@ -124,7 +123,7 @@ function writeGraphStorage(draft, graphId, graph, instances) {
|
|
|
124
123
|
path: `/scripts/instances/${id}`,
|
|
125
124
|
value: instance,
|
|
126
125
|
})),
|
|
127
|
-
]);
|
|
126
|
+
], { returnScene: false });
|
|
128
127
|
}
|
|
129
128
|
export function createSdkGeometryHost(options) {
|
|
130
129
|
const editor = options.editor;
|
|
@@ -134,17 +133,7 @@ export function createSdkGeometryHost(options) {
|
|
|
134
133
|
read: (objectId, invocation) => editor.read(invocation, (api) => readTarget(api, objectId)),
|
|
135
134
|
write: async (input, invocation) => {
|
|
136
135
|
invocation.signal.throwIfAborted();
|
|
137
|
-
const
|
|
138
|
-
const current = resolveTarget(api, input.objectId);
|
|
139
|
-
return {
|
|
140
|
-
target: await readTarget(api, input.objectId, current),
|
|
141
|
-
current,
|
|
142
|
-
graphExt: current.graphId === undefined
|
|
143
|
-
? undefined
|
|
144
|
-
: api.getSceneExt().graphsExt?.[current.graphId],
|
|
145
|
-
};
|
|
146
|
-
});
|
|
147
|
-
const { target } = inspected;
|
|
136
|
+
const target = await editor.read(invocation, api => readTarget(api, input.objectId));
|
|
148
137
|
const pendingKey = target.graphId === undefined
|
|
149
138
|
? `object:${input.objectId}`
|
|
150
139
|
: `graph:${target.graphId}`;
|
|
@@ -152,8 +141,6 @@ export function createSdkGeometryHost(options) {
|
|
|
152
141
|
throw new Error("This Geometry Nodes target is still being updated. Await its current update first.");
|
|
153
142
|
pendingTargets.add(pendingKey);
|
|
154
143
|
try {
|
|
155
|
-
if (target.revision !== input.expectedRevision)
|
|
156
|
-
throw new Error("The scene changed. Read it again before editing.");
|
|
157
144
|
const repairingGeometry = target.graphId !== undefined &&
|
|
158
145
|
(await repairs.isPending(String(target.graphId)));
|
|
159
146
|
if (target.graphId !== undefined)
|
|
@@ -164,39 +151,13 @@ export function createSdkGeometryHost(options) {
|
|
|
164
151
|
graphId: target.graphId,
|
|
165
152
|
instanceId: target.instanceId,
|
|
166
153
|
affectedObjectIds: [],
|
|
167
|
-
revision: target.revision,
|
|
168
154
|
diagnostics: boundedDiagnostics(diagnostics),
|
|
169
155
|
});
|
|
170
156
|
let compiled;
|
|
171
|
-
let nextExt;
|
|
172
|
-
const updates = new Map();
|
|
173
157
|
const diagnostics = [];
|
|
174
158
|
try {
|
|
175
159
|
compiled = compileGeometry(input.source);
|
|
176
160
|
readEditableGeometry(compiled.graph, compiled.graphExt);
|
|
177
|
-
if (target.graphId === undefined) {
|
|
178
|
-
nextExt = migrateGraphExt({ nodes: {} }, {}, compiled);
|
|
179
|
-
}
|
|
180
|
-
else {
|
|
181
|
-
const { current } = inspected;
|
|
182
|
-
if (current.graph === undefined)
|
|
183
|
-
throw new Error("The Geometry Nodes graph is missing.");
|
|
184
|
-
const ext = graphExtSchema.parse(inspected.graphExt ?? {});
|
|
185
|
-
const previous = readEditableGeometry(current.graph, ext);
|
|
186
|
-
nextExt = migrateGraphExt(ext, previous.nodeIds, compiled);
|
|
187
|
-
for (const [id, instance] of Object.entries(current.scripts.instances)) {
|
|
188
|
-
if (instance.graph_id !== target.graphId)
|
|
189
|
-
continue;
|
|
190
|
-
const old = readParameterOverrides(previous.graph, ext, instance.data ?? {});
|
|
191
|
-
const migrated = migrateParameterOverrides(previous.graph, ext, compiled.graph, nextExt, old.data);
|
|
192
|
-
updates.set(id, {
|
|
193
|
-
graph_id: target.graphId,
|
|
194
|
-
data: migrated.data,
|
|
195
|
-
});
|
|
196
|
-
if (migrated.removed.length)
|
|
197
|
-
diagnostics.push(`Instance ${id}: removed or type-changed overrides ${migrated.removed.join(", ")} now use graph defaults where present.`);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
161
|
}
|
|
201
162
|
catch (error) {
|
|
202
163
|
return rejected([errorMessage(error)]);
|
|
@@ -209,7 +170,7 @@ export function createSdkGeometryHost(options) {
|
|
|
209
170
|
let operationId;
|
|
210
171
|
let applied;
|
|
211
172
|
try {
|
|
212
|
-
applied = await editor.
|
|
173
|
+
applied = await editor.access({
|
|
213
174
|
capability: "geometry",
|
|
214
175
|
operation: "write",
|
|
215
176
|
target: () => receiptTarget,
|
|
@@ -220,6 +181,36 @@ export function createSdkGeometryHost(options) {
|
|
|
220
181
|
throw new Error("Scene memory limits prevent applying this graph.");
|
|
221
182
|
if (current.graphId !== target.graphId)
|
|
222
183
|
throw new Error("The Geometry Nodes target changed. Read it again.");
|
|
184
|
+
let nextExt;
|
|
185
|
+
const updates = new Map();
|
|
186
|
+
try {
|
|
187
|
+
if (target.graphId === undefined) {
|
|
188
|
+
nextExt = migrateGraphExt({ nodes: {} }, {}, compiled);
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
if (current.graph === undefined)
|
|
192
|
+
throw new Error("The Geometry Nodes graph is missing.");
|
|
193
|
+
const graphExt = draft.getSceneExt(["graphsExt"]).graphsExt;
|
|
194
|
+
const ext = graphExtSchema.parse(graphExt?.[target.graphId] ?? {});
|
|
195
|
+
const previous = readEditableGeometry(current.graph, ext);
|
|
196
|
+
nextExt = migrateGraphExt(ext, previous.nodeIds, compiled);
|
|
197
|
+
for (const [id, instance] of Object.entries(current.scripts.instances)) {
|
|
198
|
+
if (instance.graph_id !== target.graphId)
|
|
199
|
+
continue;
|
|
200
|
+
const old = readParameterOverrides(previous.graph, ext, instance.data ?? {});
|
|
201
|
+
const migrated = migrateParameterOverrides(previous.graph, ext, compiled.graph, nextExt, old.data);
|
|
202
|
+
updates.set(id, {
|
|
203
|
+
graph_id: target.graphId,
|
|
204
|
+
data: migrated.data,
|
|
205
|
+
});
|
|
206
|
+
if (migrated.removed.length)
|
|
207
|
+
diagnostics.push(`Instance ${id}: removed or type-changed overrides ${migrated.removed.join(", ")} now use graph defaults where present.`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
return { rejected: [errorMessage(error)] };
|
|
213
|
+
}
|
|
223
214
|
if (current.graphId !== undefined)
|
|
224
215
|
await repairs.reserve(String(current.graphId), id);
|
|
225
216
|
let objectId = input.objectId;
|
|
@@ -247,34 +238,25 @@ export function createSdkGeometryHost(options) {
|
|
|
247
238
|
if (!ext.graphsExt)
|
|
248
239
|
draft.patchSceneExt([
|
|
249
240
|
{ op: "add", path: "/graphsExt", value: {} },
|
|
250
|
-
]);
|
|
241
|
+
], { returnScene: false });
|
|
251
242
|
draft.patchSceneExt([
|
|
252
243
|
{
|
|
253
244
|
op: "add",
|
|
254
245
|
path: `/graphsExt/${graphId}`,
|
|
255
246
|
value: nextExt,
|
|
256
247
|
},
|
|
257
|
-
]);
|
|
248
|
+
], { returnScene: false });
|
|
258
249
|
const affectedObjectIds = Object.entries(scriptStorage(draft).instances)
|
|
259
250
|
.filter(([, instance]) => instance.graph_id === graphId)
|
|
260
251
|
.flatMap(([id]) => objectIdsFor(draft, Number(id)));
|
|
261
252
|
Object.assign(receiptTarget, { objectId, graphId, instanceId });
|
|
262
253
|
return { objectId, graphId, instanceId, affectedObjectIds };
|
|
263
|
-
},
|
|
264
|
-
const
|
|
265
|
-
if (
|
|
254
|
+
}, invocation, async (operation) => {
|
|
255
|
+
const graphId = receiptTarget.graphId;
|
|
256
|
+
if (graphId === undefined)
|
|
266
257
|
return;
|
|
267
|
-
const
|
|
268
|
-
.filter(([, instance]) => instance.graph_id === committed.graphId)
|
|
269
|
-
.flatMap(([id]) => objectIdsFor(api, Number(id)));
|
|
270
|
-
const areaIds = [
|
|
271
|
-
...new Set(affectedObjectIds.map((id) => getSdkSceneAreaId(api, id))),
|
|
272
|
-
];
|
|
273
|
-
const sceneHealth = await readStableSdkSceneHealth(api, areaIds, invocation.signal);
|
|
274
|
-
options.onHealth?.(sceneHealth);
|
|
275
|
-
const recorded = await repairs.record(String(committed.graphId), _operation.id, sceneHealth);
|
|
258
|
+
const recorded = await repairs.record(String(graphId), operation.id, unverifiedSceneHealth([], "Health was not requested."));
|
|
276
259
|
return {
|
|
277
|
-
sceneHealth,
|
|
278
260
|
geometryRepairAttempts: recorded.repairAttempts,
|
|
279
261
|
geometryRepairLimit: recorded.repairLimit,
|
|
280
262
|
};
|
|
@@ -288,6 +270,8 @@ export function createSdkGeometryHost(options) {
|
|
|
288
270
|
}
|
|
289
271
|
throw error;
|
|
290
272
|
}
|
|
273
|
+
if (applied.value.rejected)
|
|
274
|
+
return rejected(applied.value.rejected);
|
|
291
275
|
invocation.signal.throwIfAborted();
|
|
292
276
|
try {
|
|
293
277
|
await options.onSelect?.(applied.value.objectId);
|
|
@@ -296,7 +280,6 @@ export function createSdkGeometryHost(options) {
|
|
|
296
280
|
return {
|
|
297
281
|
status: "applied",
|
|
298
282
|
...applied.value,
|
|
299
|
-
revision: applied.operation.resultRevision ?? input.expectedRevision,
|
|
300
283
|
diagnostics: boundedDiagnostics(diagnostics),
|
|
301
284
|
};
|
|
302
285
|
}
|