@womp/spark-agent 7.0.0 → 9.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/CHANGELOG.md +67 -0
- package/README.md +3 -2
- package/dist/capabilities/geometry.d.ts +17 -5
- package/dist/capabilities/geometry.js +11 -12
- package/dist/generated/geometry-declarations.d.ts +1 -1
- package/dist/generated/geometry-declarations.js +1 -1
- package/dist/geometry/backendRegistry.d.ts +1 -0
- package/dist/geometry/common.d.ts +5 -2
- package/dist/geometry/common.js +18 -3
- package/dist/geometry/compiler.js +2 -2
- package/dist/geometry/diagnostics.d.ts +2 -7
- package/dist/geometry/diagnostics.js +3 -51
- package/dist/geometry/graph.d.ts +1 -1
- package/dist/geometry/graph.js +1 -0
- package/dist/geometry/index.d.ts +0 -1
- package/dist/geometry/index.js +0 -3
- package/dist/geometry/lift.js +105 -113
- package/dist/geometry/lower.js +29 -49
- package/dist/geometry/schemas/flowir-dialect.json +117 -117
- package/dist/geometry/writeParamData.js +4 -4
- package/dist/geometry.d.ts +2 -5
- package/dist/geometry.js +7 -11
- package/dist/index.d.ts +1 -1
- package/dist/index.js +0 -1
- package/dist/intent-bench.js +1 -1
- package/dist/kakapo.d.ts +1 -1
- package/dist/scene-operations.d.ts +2 -17
- package/dist/scene-operations.js +4 -40
- package/dist/sdk-geometry-host.d.ts +1 -1
- package/dist/sdk-geometry-host.js +252 -251
- package/dist/sdk-openscad-host.js +1 -3
- package/dist/sdk-scene-host.d.ts +3 -5
- package/dist/sdk-scene-host.js +5 -15
- package/dist/server/sqlite-store.d.ts +1 -5
- package/dist/server/sqlite-store.js +3 -40
- package/dist/server/standalone.js +0 -1
- package/dist/womp-scene-api.d.ts +9 -1
- package/dist/womp-scene-api.js +41 -32
- package/package.json +2 -2
package/dist/sdk-scene-host.js
CHANGED
|
@@ -10,7 +10,7 @@ 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
|
-
/**
|
|
13
|
+
/** A failed edit's outcome, for callers that settle per-edit state such as repair budgets. */
|
|
14
14
|
export class SceneOperationError extends Error {
|
|
15
15
|
operation;
|
|
16
16
|
constructor(operation, cause) {
|
|
@@ -74,7 +74,6 @@ export function createSdkSceneDraft(api, scene, services) {
|
|
|
74
74
|
export function createSdkSceneEditor(options) {
|
|
75
75
|
const ordinals = new WeakMap();
|
|
76
76
|
return {
|
|
77
|
-
getOperation: (operationId) => options.operations?.getSceneOperation(options.targetKey, operationId) ?? Promise.resolve(undefined),
|
|
78
77
|
withConnection: (context, action) => options.withConnection(context, async (api, settle, active = context) => {
|
|
79
78
|
active.signal.throwIfAborted();
|
|
80
79
|
return action(api, settle);
|
|
@@ -131,7 +130,6 @@ export function createSdkSceneEditor(options) {
|
|
|
131
130
|
result: operation.result,
|
|
132
131
|
save: operation.save,
|
|
133
132
|
error: operation.error,
|
|
134
|
-
...(options.operations ? { synchronized } : {}),
|
|
135
133
|
}),
|
|
136
134
|
});
|
|
137
135
|
};
|
|
@@ -142,7 +140,6 @@ export function createSdkSceneEditor(options) {
|
|
|
142
140
|
try {
|
|
143
141
|
result = await executeSceneOperation({
|
|
144
142
|
api,
|
|
145
|
-
store: options.operations,
|
|
146
143
|
onOutcome: operation => { outcome = operation; },
|
|
147
144
|
saveToRenderer: options.saveToRenderer,
|
|
148
145
|
operation: {
|
|
@@ -168,10 +165,9 @@ export function createSdkSceneEditor(options) {
|
|
|
168
165
|
catch (error) {
|
|
169
166
|
// Receipt persistence must not replace the original SDK/tool error.
|
|
170
167
|
try {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
await record(operation);
|
|
168
|
+
if (outcome) {
|
|
169
|
+
synchronized = outcome.status === "rolled_back";
|
|
170
|
+
await record(outcome);
|
|
175
171
|
}
|
|
176
172
|
}
|
|
177
173
|
catch {
|
|
@@ -180,12 +176,6 @@ export function createSdkSceneEditor(options) {
|
|
|
180
176
|
throw error;
|
|
181
177
|
}
|
|
182
178
|
saveSettled = !result.operation.changed || !["unknown", "requested"].includes(result.operation.save?.status ?? "");
|
|
183
|
-
// A duplicate returns its journaled result; retain its original receipt
|
|
184
|
-
// and let explicit recovery handle any earlier synchronization failure.
|
|
185
|
-
if (result.reused) {
|
|
186
|
-
assertSceneSaved(result.operation);
|
|
187
|
-
return result;
|
|
188
|
-
}
|
|
189
179
|
synchronized = !result.operation.changed || !options.onCommitted;
|
|
190
180
|
await record(result.operation);
|
|
191
181
|
if (!synchronized) {
|
|
@@ -216,7 +206,7 @@ export function createSdkSceneEditor(options) {
|
|
|
216
206
|
return result;
|
|
217
207
|
}
|
|
218
208
|
catch (error) {
|
|
219
|
-
if (
|
|
209
|
+
if (outcome)
|
|
220
210
|
throw new SceneOperationError(outcome, error);
|
|
221
211
|
throw error;
|
|
222
212
|
}
|
|
@@ -2,11 +2,10 @@ import type { SparkGoal } from "../goals.js";
|
|
|
2
2
|
import type { ExecutionScreenshot } from "../types.js";
|
|
3
3
|
import type { SparkArtifact, StoredSparkChunk } from "../ui.js";
|
|
4
4
|
import type { SparkArtifactStore, StoredSparkArtifact } from "./artifacts.js";
|
|
5
|
-
import type { SceneOperation, SceneOperationStore } from "../scene-operations.js";
|
|
6
5
|
import { type RepairStateStore } from "../repair-state.js";
|
|
7
6
|
import type { SparkAgentStore, SparkConversation, SparkConversationSummary, SparkRun, SparkRunSettings } from "./types.js";
|
|
8
7
|
/** Local development storage. Execution ownership belongs to the host service. */
|
|
9
|
-
export declare class SqliteSparkStore implements SparkAgentStore, SparkArtifactStore
|
|
8
|
+
export declare class SqliteSparkStore implements SparkAgentStore, SparkArtifactStore {
|
|
10
9
|
private readonly artifactBaseUrl;
|
|
11
10
|
private readonly database;
|
|
12
11
|
private ownershipToken;
|
|
@@ -15,9 +14,6 @@ export declare class SqliteSparkStore implements SparkAgentStore, SparkArtifactS
|
|
|
15
14
|
close(): void;
|
|
16
15
|
claimExecutionOwnership(): void;
|
|
17
16
|
listInterruptedRuns(): Promise<SparkRun[]>;
|
|
18
|
-
getSceneOperation(targetKey: string, id: string): Promise<SceneOperation | undefined>;
|
|
19
|
-
createSceneOperation(operation: SceneOperation): Promise<boolean>;
|
|
20
|
-
saveSceneOperation(operation: SceneOperation): Promise<SceneOperation>;
|
|
21
17
|
createConversation(value: SparkConversation): Promise<void>;
|
|
22
18
|
getConversation(id: string): Promise<SparkConversation | undefined>;
|
|
23
19
|
listConversations(ownerId: string): Promise<SparkConversationSummary[]>;
|
|
@@ -2,7 +2,6 @@ import { mkdirSync } from "node:fs";
|
|
|
2
2
|
import { dirname } from "node:path";
|
|
3
3
|
import { DatabaseSync } from "node:sqlite";
|
|
4
4
|
import { emptyRepairState, } from "../repair-state.js";
|
|
5
|
-
const completedSceneOperationLimit = 128;
|
|
6
5
|
/** Local development storage. Execution ownership belongs to the host service. */
|
|
7
6
|
export class SqliteSparkStore {
|
|
8
7
|
artifactBaseUrl;
|
|
@@ -13,7 +12,7 @@ export class SqliteSparkStore {
|
|
|
13
12
|
mkdirSync(dirname(path), { recursive: true });
|
|
14
13
|
this.database = new DatabaseSync(path);
|
|
15
14
|
const version = this.database.prepare("PRAGMA user_version").get()?.user_version;
|
|
16
|
-
if (version !== 0 && version !== 1 && version !== 2 && version !== 3 && version !== 4) {
|
|
15
|
+
if (version !== 0 && version !== 1 && version !== 2 && version !== 3 && version !== 4 && version !== 5) {
|
|
17
16
|
this.database.close();
|
|
18
17
|
throw new Error(`Unsupported Spark database version: ${version}`);
|
|
19
18
|
}
|
|
@@ -39,14 +38,11 @@ export class SqliteSparkStore {
|
|
|
39
38
|
CREATE TABLE IF NOT EXISTS execution_owner (
|
|
40
39
|
singleton INTEGER PRIMARY KEY CHECK (singleton = 1), pid INTEGER NOT NULL, token TEXT NOT NULL
|
|
41
40
|
);
|
|
42
|
-
|
|
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
|
-
);
|
|
41
|
+
DROP TABLE IF EXISTS scene_operations;
|
|
46
42
|
CREATE TABLE IF NOT EXISTS repair_states (
|
|
47
43
|
target_key TEXT PRIMARY KEY, revision INTEGER NOT NULL, data TEXT NOT NULL
|
|
48
44
|
);
|
|
49
|
-
PRAGMA user_version =
|
|
45
|
+
PRAGMA user_version = 5;
|
|
50
46
|
`);
|
|
51
47
|
}
|
|
52
48
|
repairStateStore(targetKey) {
|
|
@@ -111,39 +107,6 @@ export class SqliteSparkStore {
|
|
|
111
107
|
throw new Error("Claim execution ownership before recovering runs");
|
|
112
108
|
return this.database.prepare("SELECT data FROM runs WHERE json_extract(data, '$.status') IN ('queued', 'running')").all().map((row) => JSON.parse(String(row.data)));
|
|
113
109
|
}
|
|
114
|
-
async getSceneOperation(targetKey, id) {
|
|
115
|
-
const row = this.database.prepare("SELECT data FROM scene_operations WHERE target_key = ? AND id = ?").get(targetKey, id);
|
|
116
|
-
return row ? JSON.parse(String(row.data)) : undefined;
|
|
117
|
-
}
|
|
118
|
-
async createSceneOperation(operation) {
|
|
119
|
-
const result = this.database.prepare("INSERT OR IGNORE INTO scene_operations VALUES (?, ?, ?, ?)").run(operation.targetKey, operation.id, operation.revision, JSON.stringify(operation));
|
|
120
|
-
return result.changes === 1;
|
|
121
|
-
}
|
|
122
|
-
async saveSceneOperation(operation) {
|
|
123
|
-
const next = { ...operation, revision: operation.revision + 1 };
|
|
124
|
-
this.database.exec("BEGIN IMMEDIATE");
|
|
125
|
-
try {
|
|
126
|
-
const result = this.database.prepare("UPDATE scene_operations SET revision = ?, data = ? WHERE target_key = ? AND id = ? AND revision = ?").run(next.revision, JSON.stringify(next), operation.targetKey, operation.id, operation.revision);
|
|
127
|
-
if (result.changes !== 1)
|
|
128
|
-
throw new Error("Scene operation changed concurrently or is missing");
|
|
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
|
-
)
|
|
137
|
-
`).run(next.targetKey, completedSceneOperationLimit);
|
|
138
|
-
}
|
|
139
|
-
this.database.exec("COMMIT");
|
|
140
|
-
return next;
|
|
141
|
-
}
|
|
142
|
-
catch (error) {
|
|
143
|
-
this.database.exec("ROLLBACK");
|
|
144
|
-
throw error;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
110
|
async createConversation(value) {
|
|
148
111
|
this.database.prepare("INSERT INTO conversations VALUES (?, ?, ?, ?)")
|
|
149
112
|
.run(value.id, value.ownerId, value.updatedAt, JSON.stringify(value));
|
|
@@ -102,7 +102,6 @@ export async function createStandaloneSparkAgentApp(options) {
|
|
|
102
102
|
const targetKey = `${context.ownerId}:${sceneIdentity}`;
|
|
103
103
|
const editorOptions = {
|
|
104
104
|
targetKey,
|
|
105
|
-
operations: store,
|
|
106
105
|
saveToRenderer: true,
|
|
107
106
|
withConnection: (invocation, action) => sceneAccess.run(invocation.signal, async (signal, beginSettlement) => {
|
|
108
107
|
if (!api.isConnected)
|
package/dist/womp-scene-api.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FontFamily, KakapoAPI, KakapoNode, NodeKind, SceneEdit, Vec2, Vec3 } from "@womp/kakapo-sdk";
|
|
1
|
+
import type { BoundingBox, FontFamily, KakapoAPI, KakapoNode, NodeKind, SceneEdit, Vec2, Vec3 } from "@womp/kakapo-sdk";
|
|
2
2
|
import { type SceneHdr } from "./scene-environment.js";
|
|
3
3
|
import { type MirrorAxis } from "./coordinates.js";
|
|
4
4
|
export declare const NodeType: {
|
|
@@ -81,6 +81,14 @@ export type WompSceneApiContext = {
|
|
|
81
81
|
};
|
|
82
82
|
export type WompSceneApi = ReturnType<typeof createWompSceneApi>;
|
|
83
83
|
export declare function sceneNodeType(kind: NodeKind): number;
|
|
84
|
+
export declare function convertAABB(box: BoundingBox, unitMultiplier: number): AABB;
|
|
85
|
+
export type SceneNodeReader = {
|
|
86
|
+
requireNode(id: number): KakapoNode;
|
|
87
|
+
getNode(id: number): KakapoNode;
|
|
88
|
+
};
|
|
89
|
+
export declare function sceneNodePosition(reader: SceneNodeReader, rootId: number, unitMultiplier: number, id: number): Vec3;
|
|
90
|
+
export declare function sceneNodeRotation(reader: SceneNodeReader, id: number): Vec3;
|
|
91
|
+
export declare function sceneNodeSize(reader: SceneNodeReader, rootId: number, unitMultiplier: number, id: number): Vec3;
|
|
84
92
|
/** Align local +Z toward target. Both points are in the same parent-local coordinates. */
|
|
85
93
|
declare function lookAt(origin: Vec3, target: Vec3): Vec3;
|
|
86
94
|
/** XYZ degrees aligning the selected positive local axis to a parent-local direction. */
|
package/dist/womp-scene-api.js
CHANGED
|
@@ -133,7 +133,7 @@ function normalizePrimitiveSize(primitive, size) {
|
|
|
133
133
|
function scale(value, amount) {
|
|
134
134
|
return { x: value.x * amount, y: value.y * amount, z: value.z * amount };
|
|
135
135
|
}
|
|
136
|
-
function convertAABB(box, unitMultiplier) {
|
|
136
|
+
export function convertAABB(box, unitMultiplier) {
|
|
137
137
|
return {
|
|
138
138
|
min: {
|
|
139
139
|
x: fromEnginePosition({ x: 0, y: 0, z: box.max.z }, unitMultiplier).x,
|
|
@@ -147,6 +147,41 @@ function convertAABB(box, unitMultiplier) {
|
|
|
147
147
|
},
|
|
148
148
|
};
|
|
149
149
|
}
|
|
150
|
+
function sceneAncestorScale(reader, rootId, id, includeSelf = false) {
|
|
151
|
+
let total = 1;
|
|
152
|
+
let current = includeSelf
|
|
153
|
+
? reader.requireNode(id)
|
|
154
|
+
: reader.getNode(reader.requireNode(id).parentId ?? rootId);
|
|
155
|
+
const visited = new Set();
|
|
156
|
+
while (current.id !== rootId && !visited.has(current.id)) {
|
|
157
|
+
visited.add(current.id);
|
|
158
|
+
if (current.kind === "union")
|
|
159
|
+
total *= current.transform.scale.x;
|
|
160
|
+
const parentId = current.parentId;
|
|
161
|
+
if (parentId === null || parentId === rootId)
|
|
162
|
+
break;
|
|
163
|
+
current = reader.getNode(parentId);
|
|
164
|
+
}
|
|
165
|
+
return total;
|
|
166
|
+
}
|
|
167
|
+
export function sceneNodePosition(reader, rootId, unitMultiplier, id) {
|
|
168
|
+
return fromEnginePosition(scale(reader.requireNode(id).transform.position, sceneAncestorScale(reader, rootId, id)), unitMultiplier);
|
|
169
|
+
}
|
|
170
|
+
export function sceneNodeRotation(reader, id) {
|
|
171
|
+
return convertRotationBasis(reader.requireNode(id).transform.rotation);
|
|
172
|
+
}
|
|
173
|
+
export function sceneNodeSize(reader, rootId, unitMultiplier, id) {
|
|
174
|
+
const node = reader.requireNode(id);
|
|
175
|
+
const parentScale = sceneAncestorScale(reader, rootId, id);
|
|
176
|
+
if (node.kind === "group")
|
|
177
|
+
return { x: 1, y: 1, z: 1 };
|
|
178
|
+
if (node.kind === "union") {
|
|
179
|
+
const size = node.transform.scale.x * parentScale;
|
|
180
|
+
return { x: size, y: size, z: size };
|
|
181
|
+
}
|
|
182
|
+
const size = fromEngineDimensions(scale(node.transform.scale, parentScale), unitMultiplier);
|
|
183
|
+
return node.kind === "primitive" ? normalizePrimitiveSize(node.primitive, size) : size;
|
|
184
|
+
}
|
|
150
185
|
/** Align local +Z toward target. Both points are in the same parent-local coordinates. */
|
|
151
186
|
function lookAt(origin, target) {
|
|
152
187
|
if (![origin.x, origin.y, origin.z, target.x, target.y, target.z].every(Number.isFinite)) {
|
|
@@ -209,25 +244,10 @@ export function createWompSceneApi(context) {
|
|
|
209
244
|
throw new Error("Curve V1 and V2 transforms are read-only");
|
|
210
245
|
return node;
|
|
211
246
|
};
|
|
212
|
-
const
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
: scene.getNode(requireNode(id).parentId ?? context.rootId);
|
|
217
|
-
const visited = new Set();
|
|
218
|
-
while (current.id !== context.rootId && !visited.has(current.id)) {
|
|
219
|
-
visited.add(current.id);
|
|
220
|
-
if (current.kind === "union")
|
|
221
|
-
total *= current.transform.scale.x;
|
|
222
|
-
const parentId = current.parentId;
|
|
223
|
-
if (parentId === null || parentId === context.rootId)
|
|
224
|
-
break;
|
|
225
|
-
current = scene.getNode(parentId);
|
|
226
|
-
}
|
|
227
|
-
return total;
|
|
228
|
-
};
|
|
229
|
-
const nodePosition = (id) => fromEnginePosition(scale(requireNode(id).transform.position, ancestorScale(id)), unitMultiplier);
|
|
230
|
-
const nodeRotation = (id) => convertRotationBasis(requireNode(id).transform.rotation);
|
|
247
|
+
const reader = { requireNode, getNode: (id) => scene.getNode(id) };
|
|
248
|
+
const ancestorScale = (id, includeSelf = false) => sceneAncestorScale(reader, context.rootId, id, includeSelf);
|
|
249
|
+
const nodePosition = (id) => sceneNodePosition(reader, context.rootId, unitMultiplier, id);
|
|
250
|
+
const nodeRotation = (id) => sceneNodeRotation(reader, id);
|
|
231
251
|
const hasCsgBaseBefore = (parentId, nodeId, index) => {
|
|
232
252
|
const siblings = scene.getNodeChildren(parentId).filter(({ id }) => id !== nodeId);
|
|
233
253
|
const targetIndex = index ?? siblings.length;
|
|
@@ -237,18 +257,7 @@ export function createWompSceneApi(context) {
|
|
|
237
257
|
sibling.kind === "group" ||
|
|
238
258
|
("operation" in sibling && sibling.operation === "union"));
|
|
239
259
|
};
|
|
240
|
-
const nodeSize = (id) =>
|
|
241
|
-
const node = requireNode(id);
|
|
242
|
-
const parentScale = ancestorScale(id);
|
|
243
|
-
if (node.kind === "group")
|
|
244
|
-
return { x: 1, y: 1, z: 1 };
|
|
245
|
-
if (node.kind === "union") {
|
|
246
|
-
const size = node.transform.scale.x * parentScale;
|
|
247
|
-
return { x: size, y: size, z: size };
|
|
248
|
-
}
|
|
249
|
-
const size = fromEngineDimensions(scale(node.transform.scale, parentScale), unitMultiplier);
|
|
250
|
-
return node.kind === "primitive" ? normalizePrimitiveSize(node.primitive, size) : size;
|
|
251
|
-
};
|
|
260
|
+
const nodeSize = (id) => sceneNodeSize(reader, context.rootId, unitMultiplier, id);
|
|
252
261
|
const update = (id, changes) => {
|
|
253
262
|
context.call("updateNode", [id, changes], () => scene.updateNode(id, changes));
|
|
254
263
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@womp/spark-agent",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.0.0",
|
|
4
4
|
"description": "Portable skills-based AI agent with local and durable chat transports for Womp",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -113,7 +113,7 @@
|
|
|
113
113
|
"@ai-sdk/google": "4.0.69",
|
|
114
114
|
"@ai-sdk/openai": "4.0.63",
|
|
115
115
|
"@jitl/quickjs-wasmfile-release-sync": "0.32.0",
|
|
116
|
-
"@womp/flowir": "0.
|
|
116
|
+
"@womp/flowir": "0.5.0",
|
|
117
117
|
"@womp/kakapo-sdk": "0.8.0",
|
|
118
118
|
"ai": "7.0.95",
|
|
119
119
|
"eventsource-parser": "^3.0.6",
|