@womp/spark-agent 5.2.1 → 5.2.3
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 +430 -423
- package/dist/capabilities/geometry.js +14 -14
- package/dist/capabilities/openscad-instructions.js +77 -77
- package/dist/capabilities/operations.js +28 -28
- package/dist/capabilities/scene.js +69 -69
- package/dist/geometry/schemas/backend-schema.json +3393 -3393
- package/dist/geometry/schemas/flowir-dialect.json +1023 -1023
- package/dist/geometry.js +6 -6
- package/dist/prompt.js +41 -41
- package/dist/scene-operations.d.ts +3 -2
- package/dist/scene-operations.js +19 -11
- package/dist/sdk-geometry-host.js +3 -1
- package/dist/sdk-openscad-host.js +3 -1
- package/dist/sdk-scene-host.d.ts +6 -1
- package/dist/sdk-scene-host.js +19 -3
- package/dist/server/fixtures/materials/matte-blue.json +14 -14
- package/dist/server/fixtures/materials/polished-copper.json +14 -14
- package/dist/server/sqlite-store.js +37 -37
- package/dist/womp-scene-api.js +3 -1
- package/package.json +139 -139
package/dist/geometry.js
CHANGED
|
@@ -7,12 +7,12 @@ export { migrateParameterOverrides, readParameterOverrides, } from "./geometry/p
|
|
|
7
7
|
let reference;
|
|
8
8
|
export function geometryReference() {
|
|
9
9
|
reference ??= {
|
|
10
|
-
rules: `Write one statement per line:
|
|
11
|
-
param Float width = 20.0 min=1 max=100 step=1
|
|
12
|
-
node box Primitive Type=Cube Position=Vec3(0.0,0.0,0.0) Rotation=Vec3(0.0,0.0,0.0) Size=width Roundness=0.0
|
|
13
|
-
out Geometry=box.Geometry
|
|
14
|
-
Use decimal components in Vec3 parameter/constant literals, e.g. Vec3(20.0,20.0,20.0). Output name is case-sensitive: Geometry.
|
|
15
|
-
Exactly one cast applies per port and casts do not chain: Float[] -> Vec3 is unavailable even though Float[] -> Float and Float -> Vec3 are both listed. Build the vector with CombineXYZ instead.
|
|
10
|
+
rules: `Write one statement per line:
|
|
11
|
+
param Float width = 20.0 min=1 max=100 step=1
|
|
12
|
+
node box Primitive Type=Cube Position=Vec3(0.0,0.0,0.0) Rotation=Vec3(0.0,0.0,0.0) Size=width Roundness=0.0
|
|
13
|
+
out Geometry=box.Geometry
|
|
14
|
+
Use decimal components in Vec3 parameter/constant literals, e.g. Vec3(20.0,20.0,20.0). Output name is case-sensitive: Geometry.
|
|
15
|
+
Exactly one cast applies per port and casts do not chain: Float[] -> Vec3 is unavailable even though Float[] -> Float and Float -> Vec3 are both listed. Build the vector with CombineXYZ instead.
|
|
16
16
|
${flowir.renderDslRulesBlock()}`,
|
|
17
17
|
catalog: flowir.renderSchemaBlock(flowirRegistry.getSchema()),
|
|
18
18
|
};
|
package/dist/prompt.js
CHANGED
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
export const SPARK_AGENT_PROMPT = `You are Spark, Womp's unified assistant.
|
|
2
|
-
Use read_skill to discover the APIs needed for the user's request. Skills provide TypeScript
|
|
3
|
-
declarations for documentation; execute_javascript runs JavaScript, with top-level await.
|
|
4
|
-
Only the listed capabilities are available. Reading a skill does not grant additional permissions.
|
|
5
|
-
Use only the supplied womp APIs and output helpers to access application services and state.
|
|
6
|
-
If a capability is unavailable, explain what is missing and ask the user to enable it when applicable.
|
|
7
|
-
Do not work around missing capabilities by inspecting page globals, DOM or framework internals,
|
|
8
|
-
or making direct network requests. Native execution is not a security sandbox; it does not expand
|
|
9
|
-
the supported API or the user's granted capabilities.
|
|
10
|
-
Give each execute_javascript call a concise present-progressive purpose, such as "Generating a car image", "Inspecting the scene", or "Importing the model". Write purpose before code.
|
|
11
|
-
Each execution has fresh local variables. Keep stable object, job, and artifact references.
|
|
12
|
-
Use print for concise, filtered observations; await emitImage or emitFile with a host-issued artifact
|
|
13
|
-
reference to show actual output. emitImage inspects by default; use { inspect: false } to display without adding image bytes to model context.
|
|
14
|
-
Completed generations are shown automatically to the user; image bytes are intentionally omitted from your context. Acknowledge completion without listing, re-emitting, or inspecting outputs unless the task requires it. Use returned artifact references for follow-up work.
|
|
15
|
-
Do not claim visual verification without inspecting the image. Missing artifact references must be recovered or reported; omitted image bytes alone are not a missing result.
|
|
16
|
-
Inspect the scene before editing and verify the committed result.
|
|
17
|
-
Preserve existing content unless the user asks to replace or delete it. Use the latest target revision
|
|
18
|
-
when a write requires one. Capabilities own transactions: an entire script is not atomic.
|
|
19
|
-
If an operation fails after earlier effects, use its receipts to inspect and retry only unresolved work.
|
|
20
|
-
Never claim an uncertain commit rolled back. Never resubmit a job just because waiting timed out.
|
|
21
|
-
Complete related work in this conversation without delegating to another agent. Report limits,
|
|
22
|
-
unsupported actions, cancellation, and incomplete work truthfully. Finish with a concise explanation
|
|
23
|
-
of the result in the user's language; keep implementation details out of that explanation.
|
|
24
|
-
|
|
25
|
-
Creation intent
|
|
26
|
-
|
|
1
|
+
export const SPARK_AGENT_PROMPT = `You are Spark, Womp's unified assistant.
|
|
2
|
+
Use read_skill to discover the APIs needed for the user's request. Skills provide TypeScript
|
|
3
|
+
declarations for documentation; execute_javascript runs JavaScript, with top-level await.
|
|
4
|
+
Only the listed capabilities are available. Reading a skill does not grant additional permissions.
|
|
5
|
+
Use only the supplied womp APIs and output helpers to access application services and state.
|
|
6
|
+
If a capability is unavailable, explain what is missing and ask the user to enable it when applicable.
|
|
7
|
+
Do not work around missing capabilities by inspecting page globals, DOM or framework internals,
|
|
8
|
+
or making direct network requests. Native execution is not a security sandbox; it does not expand
|
|
9
|
+
the supported API or the user's granted capabilities.
|
|
10
|
+
Give each execute_javascript call a concise present-progressive purpose, such as "Generating a car image", "Inspecting the scene", or "Importing the model". Write purpose before code.
|
|
11
|
+
Each execution has fresh local variables. Keep stable object, job, and artifact references.
|
|
12
|
+
Use print for concise, filtered observations; await emitImage or emitFile with a host-issued artifact
|
|
13
|
+
reference to show actual output. emitImage inspects by default; use { inspect: false } to display without adding image bytes to model context.
|
|
14
|
+
Completed generations are shown automatically to the user; image bytes are intentionally omitted from your context. Acknowledge completion without listing, re-emitting, or inspecting outputs unless the task requires it. Use returned artifact references for follow-up work.
|
|
15
|
+
Do not claim visual verification without inspecting the image. Missing artifact references must be recovered or reported; omitted image bytes alone are not a missing result.
|
|
16
|
+
Inspect the scene before editing and verify the committed result.
|
|
17
|
+
Preserve existing content unless the user asks to replace or delete it. Use the latest target revision
|
|
18
|
+
when a write requires one. Capabilities own transactions: an entire script is not atomic.
|
|
19
|
+
If an operation fails after earlier effects, use its receipts to inspect and retry only unresolved work.
|
|
20
|
+
Never claim an uncertain commit rolled back. Never resubmit a job just because waiting timed out.
|
|
21
|
+
Complete related work in this conversation without delegating to another agent. Report limits,
|
|
22
|
+
unsupported actions, cancellation, and incomplete work truthfully. Finish with a concise explanation
|
|
23
|
+
of the result in the user's language; keep implementation details out of that explanation.
|
|
24
|
+
|
|
25
|
+
Creation intent
|
|
26
|
+
|
|
27
27
|
Resolve the requested outcome from the current message and preceding dialogue, not keyword matches.
|
|
28
28
|
Explicit method choices take precedence. Continue edits in the existing representation unless the user
|
|
29
29
|
asks to change it. Creation defaults do not apply to help, discussion, imports, or account questions.
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
- Build ordinary or underspecified creations directly with editable scene shapes, including
|
|
32
32
|
"a car" and "make X in 3D". Proceed without an image preview or method-selection menu.
|
|
33
33
|
Organic appearance alone does not require mesh generation or Geometry Nodes.
|
|
34
|
-
|
|
34
|
+
|
|
35
35
|
- Use OpenSCAD for parametric, measured, mechanical, printable, or real-world-fit objects,
|
|
36
36
|
except when another method is explicit (including primitives with dimensions), or when
|
|
37
37
|
parametric organic/procedural geometry, animation, or Geometry Nodes is requested:
|
|
38
38
|
use Geometry Nodes for those, not Workbench Flow. Do not claim print readiness without validation.
|
|
39
|
-
|
|
39
|
+
|
|
40
40
|
- Generate/edit images or generate meshes when requested. An attached image or reference alone
|
|
41
41
|
does not request generation; use it for the requested workflow or clarify an unresolved request.
|
|
42
42
|
Generating a result does not authorize importing, converting, or building it in the scene.
|
|
43
|
-
|
|
43
|
+
|
|
44
44
|
- Use Workbench Flow for image/mesh generation explicitly requested through Flow/nodes or
|
|
45
45
|
continued edits to that Flow workflow, unless the user asks for an inline result. Flow must be
|
|
46
46
|
open; explain that prerequisite if closed instead of silently generating inline. Merely opening
|
|
47
47
|
Flow does not redirect ordinary scene requests or inline generation to it.
|
|
48
|
-
|
|
48
|
+
|
|
49
49
|
- After completing explicitly requested generation, you may briefly suggest a relevant next step
|
|
50
50
|
using available capabilities. Describe its outcome, not internal tools. Suggestions are optional,
|
|
51
51
|
not a mandatory menu, and never authorize actions. Wait for a clear choice and follow existing
|
|
52
52
|
confirmations. Reuse the existing artifact/reference for the chosen follow-up, including as the
|
|
53
53
|
image-to-mesh input, instead of regenerating the source. Preserve its defining features when building it.
|
|
54
|
-
|
|
54
|
+
|
|
55
55
|
- Choose construction methods by the user's requested workflow and the
|
|
56
56
|
available capabilities, not by assuming editable shapes require lower
|
|
57
57
|
fidelity. A sculpted appearance does not by itself require mesh generation.
|
|
@@ -59,15 +59,15 @@ asks to change it. Creation defaults do not apply to help, discussion, imports,
|
|
|
59
59
|
and detail with that method. Offer alternatives only for a specific
|
|
60
60
|
demonstrated limitation; ask before switching or simplifying the requested
|
|
61
61
|
result. No method guarantees exact reconstruction from a single image.
|
|
62
|
-
|
|
63
|
-
- Ask at most one focused question at a time, only when a sensible default
|
|
64
|
-
would likely produce the wrong outcome. Do not repeat resolved questions.
|
|
65
|
-
Interpret replies using the preceding offer; if several options were
|
|
66
|
-
offered and “yes” does not identify one, clarify briefly.
|
|
67
|
-
|
|
68
|
-
- Treat a clear answer to your question as an instruction to proceed.
|
|
69
|
-
Continue from existing results rather than restarting the workflow.
|
|
70
|
-
|
|
62
|
+
|
|
63
|
+
- Ask at most one focused question at a time, only when a sensible default
|
|
64
|
+
would likely produce the wrong outcome. Do not repeat resolved questions.
|
|
65
|
+
Interpret replies using the preceding offer; if several options were
|
|
66
|
+
offered and “yes” does not identify one, clarify briefly.
|
|
67
|
+
|
|
68
|
+
- Treat a clear answer to your question as an instruction to proceed.
|
|
69
|
+
Continue from existing results rather than restarting the workflow.
|
|
70
|
+
|
|
71
71
|
Offer only workflows available in this host. Follow their authorization
|
|
72
72
|
and confirmation requirements before generation or scene changes. If a requested workflow is unavailable,
|
|
73
73
|
explain the limitation rather than silently substituting another.`;
|
|
@@ -30,10 +30,11 @@ export declare function recoverSceneOperation(options: {
|
|
|
30
30
|
id: string;
|
|
31
31
|
assertActive(): void | Promise<void>;
|
|
32
32
|
}): Promise<SceneOperation | undefined>;
|
|
33
|
-
/**
|
|
33
|
+
/** Hosts may omit persistence; journal-backed hosts retain duplicate-operation protection. */
|
|
34
34
|
export declare function executeSceneOperation<T>(options: {
|
|
35
35
|
api: KakapoAPI;
|
|
36
|
-
store
|
|
36
|
+
store?: SceneOperationStore;
|
|
37
|
+
onOutcome?(operation: SceneOperation): void;
|
|
37
38
|
operation: Pick<SceneOperation, "id" | "targetKey">;
|
|
38
39
|
assertEditable(): void | Promise<void>;
|
|
39
40
|
edit(draft: SceneEdit): T | Promise<T>;
|
package/dist/scene-operations.js
CHANGED
|
@@ -41,13 +41,13 @@ export async function recoverSceneOperation(options) {
|
|
|
41
41
|
}
|
|
42
42
|
return previous;
|
|
43
43
|
}
|
|
44
|
-
/**
|
|
44
|
+
/** Hosts may omit persistence; journal-backed hosts retain duplicate-operation protection. */
|
|
45
45
|
export async function executeSceneOperation(options) {
|
|
46
46
|
const { api, store, operation: identity } = options;
|
|
47
47
|
const savesScene = options.saveToRenderer === true;
|
|
48
48
|
await options.assertEditable();
|
|
49
|
-
const previous = await store
|
|
50
|
-
if (previous) {
|
|
49
|
+
const previous = await store?.getSceneOperation(identity.targetKey, identity.id);
|
|
50
|
+
if (previous && store) {
|
|
51
51
|
if ((previous.save !== undefined) !== savesScene)
|
|
52
52
|
throw new Error("Scene operation identity was reused with a different save policy");
|
|
53
53
|
if (previous.status === "committed")
|
|
@@ -65,7 +65,7 @@ export async function executeSceneOperation(options) {
|
|
|
65
65
|
...identity, revision: 0, status: "started",
|
|
66
66
|
...(savesScene ? { save: { status: "unknown" } } : {}),
|
|
67
67
|
};
|
|
68
|
-
if (!await store.createSceneOperation(operation))
|
|
68
|
+
if (store && !await store.createSceneOperation(operation))
|
|
69
69
|
throw new Error("Scene operation is already in progress");
|
|
70
70
|
try {
|
|
71
71
|
const edit = async (draft) => {
|
|
@@ -77,10 +77,12 @@ export async function executeSceneOperation(options) {
|
|
|
77
77
|
save: savesScene,
|
|
78
78
|
beforeCommit: async (changed) => {
|
|
79
79
|
await options.assertEditable();
|
|
80
|
-
operation =
|
|
80
|
+
operation = {
|
|
81
81
|
...operation, status: "prepared", changed,
|
|
82
82
|
...(savesScene && !changed ? { save: { status: "not_needed" } } : {}),
|
|
83
|
-
}
|
|
83
|
+
};
|
|
84
|
+
if (store)
|
|
85
|
+
operation = await store.saveSceneOperation(operation);
|
|
84
86
|
await options.assertEditable();
|
|
85
87
|
if (changed)
|
|
86
88
|
await options.prepareCommit?.(operation);
|
|
@@ -88,18 +90,23 @@ export async function executeSceneOperation(options) {
|
|
|
88
90
|
});
|
|
89
91
|
if (savesScene)
|
|
90
92
|
operation = { ...operation, save: committed.save };
|
|
93
|
+
operation = complete(operation, "committed");
|
|
94
|
+
options.onOutcome?.(operation);
|
|
91
95
|
if (committed.changed)
|
|
92
96
|
options.onCommittedScene?.(committed.scene);
|
|
93
97
|
}
|
|
94
98
|
catch (error) {
|
|
95
|
-
const status =
|
|
96
|
-
: "rolled_back";
|
|
99
|
+
const status = operation.status === "committed" ? "committed"
|
|
100
|
+
: error instanceof KakapoSceneCommitError ? error.outcome : "rolled_back";
|
|
97
101
|
try {
|
|
98
|
-
|
|
102
|
+
operation = {
|
|
99
103
|
...(status === "rolled_back" ? complete(operation, status) : { ...operation, status }),
|
|
100
104
|
error: error instanceof Error ? error.message : String(error),
|
|
101
105
|
...(operation.save && status === "rolled_back" ? { save: { status: "not_needed" } } : {}),
|
|
102
|
-
}
|
|
106
|
+
};
|
|
107
|
+
options.onOutcome?.(operation);
|
|
108
|
+
if (store)
|
|
109
|
+
operation = await store.saveSceneOperation(operation);
|
|
103
110
|
}
|
|
104
111
|
catch {
|
|
105
112
|
// Keep the original error; recovery can inspect the prepared record.
|
|
@@ -107,6 +114,7 @@ export async function executeSceneOperation(options) {
|
|
|
107
114
|
throw error;
|
|
108
115
|
}
|
|
109
116
|
// A prepared record alone never proves an uncertain commit succeeded.
|
|
110
|
-
|
|
117
|
+
if (store)
|
|
118
|
+
operation = await store.saveSceneOperation(complete(operation, "committed"));
|
|
111
119
|
return { value: resultValue(operation), operation };
|
|
112
120
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SceneOperationError } from "./sdk-scene-host.js";
|
|
1
2
|
import { compileGeometry, migrateGraphExt, readEditableGeometry, } from "./geometry/compiler.js";
|
|
2
3
|
import { graphExtSchema } from "./geometry/graph.js";
|
|
3
4
|
import { boundedDiagnostics } from "./geometry/diagnostics.js";
|
|
@@ -264,7 +265,8 @@ export function createSdkGeometryHost(options) {
|
|
|
264
265
|
}
|
|
265
266
|
catch (error) {
|
|
266
267
|
if (operationId !== undefined && target.graphId !== undefined) {
|
|
267
|
-
const operation =
|
|
268
|
+
const operation = error instanceof SceneOperationError
|
|
269
|
+
? error.operation : await editor.getOperation(operationId);
|
|
268
270
|
if (operation?.status === "rolled_back")
|
|
269
271
|
await repairs.refund(String(target.graphId), operationId);
|
|
270
272
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { SceneOperationError } from "./sdk-scene-host.js";
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
import { getOpenScadGroundedPosition, MAX_PRINTABILITY_REPAIR_ATTEMPTS, requestTurnOpenScadPrintability, toOpenScadCompileResponse, } from "./openscad.js";
|
|
3
4
|
import { createGeometryRepairTracker, createRepairTracker, } from "./repair-state.js";
|
|
@@ -237,7 +238,8 @@ export function createSdkOpenScadHost(options) {
|
|
|
237
238
|
}
|
|
238
239
|
catch (error) {
|
|
239
240
|
if (creating === false && operationId !== "") {
|
|
240
|
-
const operation =
|
|
241
|
+
const operation = error instanceof SceneOperationError
|
|
242
|
+
? error.operation : await editor.getOperation(operationId);
|
|
241
243
|
if (operation?.status === "rolled_back") {
|
|
242
244
|
await geometryRepairs.refund(resolved.repairKey, operationId);
|
|
243
245
|
await printabilityRepairs.refund(resolved.repairKey, operationId);
|
package/dist/sdk-scene-host.d.ts
CHANGED
|
@@ -18,7 +18,7 @@ export type SdkSceneServices = {
|
|
|
18
18
|
export type SdkSceneEditorOptions = {
|
|
19
19
|
targetKey: string;
|
|
20
20
|
projectId?: number;
|
|
21
|
-
operations
|
|
21
|
+
operations?: SceneOperationStore;
|
|
22
22
|
saveToRenderer?: boolean;
|
|
23
23
|
withConnection<T>(context: SparkInvocationContext, action: (api: KakapoAPI, beginSettlement: () => void,
|
|
24
24
|
/** Supply this when the host adds a stricter connection lifetime. */
|
|
@@ -34,6 +34,11 @@ export type SdkSceneEditIdentity = {
|
|
|
34
34
|
operation: string;
|
|
35
35
|
target?: Record<string, unknown> | (() => Record<string, unknown>);
|
|
36
36
|
};
|
|
37
|
+
/** Carries this invocation's outcome without requiring a persistent journal. */
|
|
38
|
+
export declare class SceneOperationError extends Error {
|
|
39
|
+
readonly operation: SceneOperation;
|
|
40
|
+
constructor(operation: SceneOperation, cause: unknown);
|
|
41
|
+
}
|
|
37
42
|
export type SdkSceneEditor = {
|
|
38
43
|
getOperation(operationId: string): Promise<SceneOperation | undefined>;
|
|
39
44
|
withConnection<T>(context: SparkInvocationContext, action: (api: KakapoAPI, beginSettlement: () => void) => T | Promise<T>): Promise<T>;
|
package/dist/sdk-scene-host.js
CHANGED
|
@@ -10,6 +10,15 @@ function assertSceneSaved(operation) {
|
|
|
10
10
|
if (operation.save?.status === "unknown" || operation.save?.status === "requested")
|
|
11
11
|
throw new Error("The scene edit was applied, but its save outcome is unknown. Inspect persistence before continuing; do not repeat the edit.");
|
|
12
12
|
}
|
|
13
|
+
/** Carries this invocation's outcome without requiring a persistent journal. */
|
|
14
|
+
export class SceneOperationError extends Error {
|
|
15
|
+
operation;
|
|
16
|
+
constructor(operation, cause) {
|
|
17
|
+
super(cause instanceof Error ? cause.message : String(cause), { cause });
|
|
18
|
+
this.operation = operation;
|
|
19
|
+
this.name = "SceneOperationError";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
13
22
|
export function createSdkSceneDraft(api, scene, services) {
|
|
14
23
|
let defaultMaterialId = scene
|
|
15
24
|
.listMaterials()
|
|
@@ -65,7 +74,7 @@ export function createSdkSceneDraft(api, scene, services) {
|
|
|
65
74
|
export function createSdkSceneEditor(options) {
|
|
66
75
|
const ordinals = new WeakMap();
|
|
67
76
|
return {
|
|
68
|
-
getOperation: (operationId) => options.operations
|
|
77
|
+
getOperation: (operationId) => options.operations?.getSceneOperation(options.targetKey, operationId) ?? Promise.resolve(undefined),
|
|
69
78
|
withConnection: (context, action) => options.withConnection(context, async (api, settle, active = context) => {
|
|
70
79
|
active.signal.throwIfAborted();
|
|
71
80
|
return action(api, settle);
|
|
@@ -102,6 +111,7 @@ export function createSdkSceneEditor(options) {
|
|
|
102
111
|
let synchronized = false;
|
|
103
112
|
let saveSettled = true;
|
|
104
113
|
let committedScene;
|
|
114
|
+
let outcome;
|
|
105
115
|
let hostDetails = {};
|
|
106
116
|
const record = async (operation, details) => {
|
|
107
117
|
hostDetails = { ...hostDetails, ...details };
|
|
@@ -121,7 +131,7 @@ export function createSdkSceneEditor(options) {
|
|
|
121
131
|
result: operation.result,
|
|
122
132
|
save: operation.save,
|
|
123
133
|
error: operation.error,
|
|
124
|
-
synchronized,
|
|
134
|
+
...(options.operations ? { synchronized } : {}),
|
|
125
135
|
}),
|
|
126
136
|
});
|
|
127
137
|
};
|
|
@@ -133,6 +143,7 @@ export function createSdkSceneEditor(options) {
|
|
|
133
143
|
result = await executeSceneOperation({
|
|
134
144
|
api,
|
|
135
145
|
store: options.operations,
|
|
146
|
+
onOutcome: operation => { outcome = operation; },
|
|
136
147
|
saveToRenderer: options.saveToRenderer,
|
|
137
148
|
operation: {
|
|
138
149
|
id,
|
|
@@ -157,7 +168,7 @@ export function createSdkSceneEditor(options) {
|
|
|
157
168
|
catch (error) {
|
|
158
169
|
// Receipt persistence must not replace the original SDK/tool error.
|
|
159
170
|
try {
|
|
160
|
-
const operation = await options.operations
|
|
171
|
+
const operation = outcome ?? await options.operations?.getSceneOperation(options.targetKey, id);
|
|
161
172
|
if (operation) {
|
|
162
173
|
synchronized = operation.status === "rolled_back";
|
|
163
174
|
await record(operation);
|
|
@@ -204,6 +215,11 @@ export function createSdkSceneEditor(options) {
|
|
|
204
215
|
}
|
|
205
216
|
return result;
|
|
206
217
|
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
if (!options.operations && outcome)
|
|
220
|
+
throw new SceneOperationError(outcome, error);
|
|
221
|
+
throw error;
|
|
222
|
+
}
|
|
207
223
|
finally {
|
|
208
224
|
await releaseCommit?.(synchronized && saveSettled);
|
|
209
225
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "Development: Matte Blue",
|
|
3
|
-
"resource": {
|
|
4
|
-
"data": {
|
|
5
|
-
"color.x": 0.08,
|
|
6
|
-
"color.y": 0.3,
|
|
7
|
-
"color.z": 0.8,
|
|
8
|
-
"roughness": 0.85,
|
|
9
|
-
"metalness": 0,
|
|
10
|
-
"index_of_refraction": 1.5
|
|
11
|
-
},
|
|
12
|
-
"shaders": []
|
|
13
|
-
}
|
|
14
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "Development: Matte Blue",
|
|
3
|
+
"resource": {
|
|
4
|
+
"data": {
|
|
5
|
+
"color.x": 0.08,
|
|
6
|
+
"color.y": 0.3,
|
|
7
|
+
"color.z": 0.8,
|
|
8
|
+
"roughness": 0.85,
|
|
9
|
+
"metalness": 0,
|
|
10
|
+
"index_of_refraction": 1.5
|
|
11
|
+
},
|
|
12
|
+
"shaders": []
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "Development: Polished Copper",
|
|
3
|
-
"resource": {
|
|
4
|
-
"data": {
|
|
5
|
-
"color.x": 0.955,
|
|
6
|
-
"color.y": 0.638,
|
|
7
|
-
"color.z": 0.538,
|
|
8
|
-
"roughness": 0.18,
|
|
9
|
-
"metalness": 1,
|
|
10
|
-
"index_of_refraction": 1.5
|
|
11
|
-
},
|
|
12
|
-
"shaders": []
|
|
13
|
-
}
|
|
14
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "Development: Polished Copper",
|
|
3
|
+
"resource": {
|
|
4
|
+
"data": {
|
|
5
|
+
"color.x": 0.955,
|
|
6
|
+
"color.y": 0.638,
|
|
7
|
+
"color.z": 0.538,
|
|
8
|
+
"roughness": 0.18,
|
|
9
|
+
"metalness": 1,
|
|
10
|
+
"index_of_refraction": 1.5
|
|
11
|
+
},
|
|
12
|
+
"shaders": []
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -17,36 +17,36 @@ export class SqliteSparkStore {
|
|
|
17
17
|
this.database.close();
|
|
18
18
|
throw new Error(`Unsupported Spark database version: ${version}`);
|
|
19
19
|
}
|
|
20
|
-
this.database.exec(`
|
|
21
|
-
PRAGMA busy_timeout = 5000;
|
|
22
|
-
PRAGMA journal_mode = WAL;
|
|
23
|
-
PRAGMA synchronous = FULL;
|
|
24
|
-
PRAGMA foreign_keys = ON;
|
|
25
|
-
CREATE TABLE IF NOT EXISTS conversations (
|
|
26
|
-
id TEXT PRIMARY KEY, owner_id TEXT NOT NULL, updated_at TEXT NOT NULL, data TEXT NOT NULL
|
|
27
|
-
);
|
|
28
|
-
CREATE INDEX IF NOT EXISTS conversation_owner ON conversations(owner_id, updated_at);
|
|
29
|
-
CREATE TABLE IF NOT EXISTS runs (
|
|
30
|
-
id TEXT PRIMARY KEY, conversation_id TEXT NOT NULL, data TEXT NOT NULL
|
|
31
|
-
);
|
|
32
|
-
CREATE TABLE IF NOT EXISTS chunks (
|
|
33
|
-
run_id TEXT NOT NULL REFERENCES runs(id), sequence INTEGER NOT NULL, data TEXT NOT NULL,
|
|
34
|
-
PRIMARY KEY (run_id, sequence)
|
|
35
|
-
);
|
|
36
|
-
CREATE TABLE IF NOT EXISTS artifacts (
|
|
37
|
-
id TEXT PRIMARY KEY, owner_id TEXT NOT NULL, metadata TEXT NOT NULL, bytes BLOB NOT NULL
|
|
38
|
-
);
|
|
39
|
-
CREATE TABLE IF NOT EXISTS execution_owner (
|
|
40
|
-
singleton INTEGER PRIMARY KEY CHECK (singleton = 1), pid INTEGER NOT NULL, token TEXT NOT NULL
|
|
41
|
-
);
|
|
42
|
-
CREATE TABLE IF NOT EXISTS scene_operations (
|
|
43
|
-
target_key TEXT NOT NULL, id TEXT NOT NULL, revision INTEGER NOT NULL, data TEXT NOT NULL,
|
|
44
|
-
PRIMARY KEY (target_key, id)
|
|
45
|
-
);
|
|
46
|
-
CREATE TABLE IF NOT EXISTS repair_states (
|
|
47
|
-
target_key TEXT PRIMARY KEY, revision INTEGER NOT NULL, data TEXT NOT NULL
|
|
48
|
-
);
|
|
49
|
-
PRAGMA user_version = 4;
|
|
20
|
+
this.database.exec(`
|
|
21
|
+
PRAGMA busy_timeout = 5000;
|
|
22
|
+
PRAGMA journal_mode = WAL;
|
|
23
|
+
PRAGMA synchronous = FULL;
|
|
24
|
+
PRAGMA foreign_keys = ON;
|
|
25
|
+
CREATE TABLE IF NOT EXISTS conversations (
|
|
26
|
+
id TEXT PRIMARY KEY, owner_id TEXT NOT NULL, updated_at TEXT NOT NULL, data TEXT NOT NULL
|
|
27
|
+
);
|
|
28
|
+
CREATE INDEX IF NOT EXISTS conversation_owner ON conversations(owner_id, updated_at);
|
|
29
|
+
CREATE TABLE IF NOT EXISTS runs (
|
|
30
|
+
id TEXT PRIMARY KEY, conversation_id TEXT NOT NULL, data TEXT NOT NULL
|
|
31
|
+
);
|
|
32
|
+
CREATE TABLE IF NOT EXISTS chunks (
|
|
33
|
+
run_id TEXT NOT NULL REFERENCES runs(id), sequence INTEGER NOT NULL, data TEXT NOT NULL,
|
|
34
|
+
PRIMARY KEY (run_id, sequence)
|
|
35
|
+
);
|
|
36
|
+
CREATE TABLE IF NOT EXISTS artifacts (
|
|
37
|
+
id TEXT PRIMARY KEY, owner_id TEXT NOT NULL, metadata TEXT NOT NULL, bytes BLOB NOT NULL
|
|
38
|
+
);
|
|
39
|
+
CREATE TABLE IF NOT EXISTS execution_owner (
|
|
40
|
+
singleton INTEGER PRIMARY KEY CHECK (singleton = 1), pid INTEGER NOT NULL, token TEXT NOT NULL
|
|
41
|
+
);
|
|
42
|
+
CREATE TABLE IF NOT EXISTS scene_operations (
|
|
43
|
+
target_key TEXT NOT NULL, id TEXT NOT NULL, revision INTEGER NOT NULL, data TEXT NOT NULL,
|
|
44
|
+
PRIMARY KEY (target_key, id)
|
|
45
|
+
);
|
|
46
|
+
CREATE TABLE IF NOT EXISTS repair_states (
|
|
47
|
+
target_key TEXT PRIMARY KEY, revision INTEGER NOT NULL, data TEXT NOT NULL
|
|
48
|
+
);
|
|
49
|
+
PRAGMA user_version = 4;
|
|
50
50
|
`);
|
|
51
51
|
}
|
|
52
52
|
repairStateStore(targetKey) {
|
|
@@ -127,13 +127,13 @@ export class SqliteSparkStore {
|
|
|
127
127
|
if (result.changes !== 1)
|
|
128
128
|
throw new Error("Scene operation changed concurrently or is missing");
|
|
129
129
|
if (next.status === "committed" || next.status === "rolled_back") {
|
|
130
|
-
this.database.prepare(`
|
|
131
|
-
DELETE FROM scene_operations WHERE rowid IN (
|
|
132
|
-
SELECT rowid FROM scene_operations
|
|
133
|
-
WHERE target_key = ? AND json_extract(data, '$.status') IN ('committed', 'rolled_back')
|
|
134
|
-
ORDER BY COALESCE(json_extract(data, '$.completedAt'), '') DESC
|
|
135
|
-
LIMIT -1 OFFSET ?
|
|
136
|
-
)
|
|
130
|
+
this.database.prepare(`
|
|
131
|
+
DELETE FROM scene_operations WHERE rowid IN (
|
|
132
|
+
SELECT rowid FROM scene_operations
|
|
133
|
+
WHERE target_key = ? AND json_extract(data, '$.status') IN ('committed', 'rolled_back')
|
|
134
|
+
ORDER BY COALESCE(json_extract(data, '$.completedAt'), '') DESC
|
|
135
|
+
LIMIT -1 OFFSET ?
|
|
136
|
+
)
|
|
137
137
|
`).run(next.targetKey, completedSceneOperationLimit);
|
|
138
138
|
}
|
|
139
139
|
this.database.exec("COMMIT");
|
package/dist/womp-scene-api.js
CHANGED
|
@@ -828,7 +828,7 @@ export function createWompSceneApi(context) {
|
|
|
828
828
|
update(id, { lineHeight: clamp(finite(lineHeight, "lineHeight"), 0.5, 3) });
|
|
829
829
|
},
|
|
830
830
|
nodeGetDensity: (id) => requireCurve(id).density,
|
|
831
|
-
nodeSetDensity: (id, density) => update(id, { density: clamp(Math.round(finite(density, "density")),
|
|
831
|
+
nodeSetDensity: (id, density) => update(id, { density: clamp(Math.round(finite(density, "density")), 0, 250) }),
|
|
832
832
|
nodeGetRoundness: (id) => requireCurve(id).roundness,
|
|
833
833
|
nodeSetRoundness: (id, roundness) => update(id, { roundness: clamp(Math.round(finite(roundness, "roundness")), 1, 10) }),
|
|
834
834
|
nodeGetSmoothing: (id) => requireCurve(id).smoothing,
|
|
@@ -910,6 +910,8 @@ export function createWompSceneApi(context) {
|
|
|
910
910
|
throw new Error("Thickness is unsupported");
|
|
911
911
|
return node.thickness * unitMultiplier;
|
|
912
912
|
},
|
|
913
|
+
// Kakapo keeps a node solid for thickness <= 0 and has no maximum. The 1000 cap is this API's
|
|
914
|
+
// own; very large values can overflow the modeller's fp16 encoding in small, high-resolution areas.
|
|
913
915
|
nodeSetThickness: (id, thickness) => update(id, { thickness: clamp(finite(thickness, "thickness") / unitMultiplier, 0, 1000) }),
|
|
914
916
|
curveGetClosed: (curveId) => requireBezierCurve(curveId).closed ?? false,
|
|
915
917
|
curveSetClosed: (curveId, closed) => {
|