@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.
Files changed (39) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +3 -2
  3. package/dist/capabilities/geometry.d.ts +17 -5
  4. package/dist/capabilities/geometry.js +11 -12
  5. package/dist/generated/geometry-declarations.d.ts +1 -1
  6. package/dist/generated/geometry-declarations.js +1 -1
  7. package/dist/geometry/backendRegistry.d.ts +1 -0
  8. package/dist/geometry/common.d.ts +5 -2
  9. package/dist/geometry/common.js +18 -3
  10. package/dist/geometry/compiler.js +2 -2
  11. package/dist/geometry/diagnostics.d.ts +2 -7
  12. package/dist/geometry/diagnostics.js +3 -51
  13. package/dist/geometry/graph.d.ts +1 -1
  14. package/dist/geometry/graph.js +1 -0
  15. package/dist/geometry/index.d.ts +0 -1
  16. package/dist/geometry/index.js +0 -3
  17. package/dist/geometry/lift.js +105 -113
  18. package/dist/geometry/lower.js +29 -49
  19. package/dist/geometry/schemas/flowir-dialect.json +117 -117
  20. package/dist/geometry/writeParamData.js +4 -4
  21. package/dist/geometry.d.ts +2 -5
  22. package/dist/geometry.js +7 -11
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.js +0 -1
  25. package/dist/intent-bench.js +1 -1
  26. package/dist/kakapo.d.ts +1 -1
  27. package/dist/scene-operations.d.ts +2 -17
  28. package/dist/scene-operations.js +4 -40
  29. package/dist/sdk-geometry-host.d.ts +1 -1
  30. package/dist/sdk-geometry-host.js +252 -251
  31. package/dist/sdk-openscad-host.js +1 -3
  32. package/dist/sdk-scene-host.d.ts +3 -5
  33. package/dist/sdk-scene-host.js +5 -15
  34. package/dist/server/sqlite-store.d.ts +1 -5
  35. package/dist/server/sqlite-store.js +3 -40
  36. package/dist/server/standalone.js +0 -1
  37. package/dist/womp-scene-api.d.ts +9 -1
  38. package/dist/womp-scene-api.js +41 -32
  39. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -4,6 +4,73 @@ Notable changes to `@womp/spark-agent`, newest first. Versions follow
4
4
  [Semantic Versioning](https://semver.org/). Add an entry with each version bump; describe what
5
5
  changed for hosts and users, and how to migrate across breaking changes.
6
6
 
7
+ ## 9.0.0 — 2026-10-12
8
+
9
+ The scene operation journal is gone: every SDK scene adapter applies each edit directly to the
10
+ scene and saves it, as the browser has since it moved to scene events. The journal only deduped an
11
+ edit whose tool call ran again with the same operation id and settled uncertain edits that turned
12
+ out to change nothing; no host re-runs a tool call within a live run, and a restart ends the run.
13
+
14
+ ### Removed
15
+
16
+ - `SceneOperationStore`, `recoverSceneOperation` and the `operations` option of
17
+ `SdkSceneEditorOptions` (and so of `createSdkSceneEditor` and `createSdkSceneHost`).
18
+ - `SdkSceneEditor.getOperation`.
19
+ - `SceneOperation.revision`, which only versioned journal records.
20
+ - `executeSceneOperation`'s `store` option and the `reused` flag on its result.
21
+ - The standalone SQLite store's `scene_operations` table. Opening an older database drops it and
22
+ sets the schema version to 5.
23
+
24
+ ### Changed
25
+
26
+ - Every edit that fails after it began throws `SceneOperationError`, whose `operation` reports
27
+ whether it was committed, rolled back or left uncertain. Before, hosts that supplied
28
+ `operations` got the original error unwrapped; it is now the error's `cause`.
29
+ - Receipts no longer carry `details.synchronized`; `details.synchronizationError` still reports
30
+ a failed `onCommitted`.
31
+
32
+ ### Migrating
33
+
34
+ Stop passing `operations` and delete any `SceneOperationStore` implementation. A custom
35
+ `SdkSceneEditor` drops `getOperation`. Code that read an edit's outcome from the journal after a
36
+ failure reads `error.operation` from the `SceneOperationError` instead.
37
+
38
+ ## 8.0.0 — 2026-10-12
39
+
40
+ Geometry Nodes moves to FlowIR 0.5.0. The dialect is FlowIR's strict, flat format, the
41
+ `geometry-nodes-reference` skill is the prompt FlowIR renders from it, and the names the agent
42
+ writes come back when it reads the graph, so a follow-up edit can refer to them.
43
+
44
+ ### Changed
45
+
46
+ - `@womp/flowir` is 0.5.0. The geometry dialect declares its types, enum ports, casts and the
47
+ `Geometry` program output. `param` and `const` declare Int, Float and Vec3; other types are
48
+ rejected instead of being dropped when the graph is written.
49
+ - A write stores each statement's name in `graphExt.nodes[id].flowirSymbol`, and a read returns
50
+ those names. Graphs written without stored names read back as before.
51
+ - Compiler diagnostics are formatted by FlowIR, within the existing count and size bounds.
52
+ - `geometry-nodes` (skill version 6) and `geometry-nodes-reference` (version 3) are rewritten
53
+ for the new dialect.
54
+
55
+ ### Added
56
+
57
+ - `GeometryTarget.transform` and `GeometryTarget.bounds`: the target's position, rotation and
58
+ size relative to its parent, and its world-space extent, in project units. Both are `null`
59
+ where they do not apply.
60
+ - An applied `GeometryWriteResult` also carries the committed `source`, `transform` and
61
+ `bounds`, read back from the scene.
62
+
63
+ ### Removed
64
+
65
+ - `loadGeoNodesDialect` from `@womp/spark-agent/geometry`.
66
+
67
+ ### Migration
68
+
69
+ - `geometryReference()` returns a string instead of `{ rules, catalog }`, and
70
+ `createGeometryCapability` takes that string. Pass `geometryReference()` through unchanged.
71
+ - A host that builds `GeometryTarget` values itself, rather than through
72
+ `createSdkGeometryHost`, must set `transform` and `bounds` (`null` when unknown).
73
+
7
74
  ## 7.0.0 — 2026-10-12
8
75
 
9
76
  Compaction is now the only thing that takes messages out of what the model reads, and it can run
package/README.md CHANGED
@@ -59,8 +59,9 @@ locks. The run stays active with `requires_action` progress and resumes the same
59
59
  input; the wait is excluded from the execution budget. Abort settles the wait and late answers
60
60
  cannot revive it. Custom executors must honor the optional host-owned `userWait` argument to `execute`.
61
61
 
62
- Shared SDK scene adapters may omit `operations` to edit and save directly. Supplying `operations`
63
- adds a durable operation journal and recovery of uncertain edits.
62
+ Shared SDK scene adapters apply each edit directly to the scene and save it; nothing is journaled
63
+ or replayed. An edit that fails after it began throws `SceneOperationError`, whose `operation`
64
+ reports whether the edit was committed, rolled back or left uncertain.
64
65
 
65
66
  ### Persistence and run lifecycle
66
67
 
@@ -1,9 +1,19 @@
1
1
  import type { SparkCapability, SparkInvocationContext } from "../capabilities.js";
2
- export type GeometryParamValue = number | {
2
+ export type GeometryVector = {
3
3
  x: number;
4
4
  y: number;
5
5
  z: number;
6
6
  };
7
+ export type GeometryParamValue = number | GeometryVector;
8
+ export type GeometryTransform = {
9
+ position: GeometryVector;
10
+ rotation: GeometryVector;
11
+ size: GeometryVector;
12
+ };
13
+ export type GeometryBounds = {
14
+ min: GeometryVector;
15
+ max: GeometryVector;
16
+ };
7
17
  export type GeometryInstance = {
8
18
  instanceId: number;
9
19
  objectIds: number[];
@@ -16,6 +26,8 @@ export type GeometryTarget = {
16
26
  graphId?: number;
17
27
  instanceId?: number;
18
28
  source: string | null;
29
+ transform: GeometryTransform | null;
30
+ bounds: GeometryBounds | null;
19
31
  instances: GeometryInstance[];
20
32
  diagnostics: string[];
21
33
  };
@@ -28,6 +40,9 @@ export type GeometryWriteResult = {
28
40
  objectId: number;
29
41
  graphId?: number;
30
42
  instanceId?: number;
43
+ source?: string;
44
+ transform?: GeometryTransform | null;
45
+ bounds?: GeometryBounds | null;
31
46
  affectedObjectIds: number[];
32
47
  diagnostics: string[];
33
48
  };
@@ -39,7 +54,4 @@ export interface GeometryHost {
39
54
  read(objectId: number, context: SparkInvocationContext): Promise<GeometryTarget>;
40
55
  write(options: GeometryWrite, context: SparkInvocationContext): Promise<GeometryWriteResult>;
41
56
  }
42
- export declare function createGeometryCapability(host: GeometryHost, reference: {
43
- rules: string;
44
- catalog: string;
45
- }): SparkCapability<WompGeometryApi>;
57
+ export declare function createGeometryCapability(host: GeometryHost, reference: string): SparkCapability<WompGeometryApi>;
@@ -12,23 +12,22 @@ export function createGeometryCapability(host, reference) {
12
12
  skills: [
13
13
  {
14
14
  name: "geometry-nodes",
15
- version: "5",
15
+ version: "6",
16
16
  description: "Create and edit visual Geometry Nodes graphs with adjustable parameters and animation.",
17
17
  instructions: `Use womp.geometry for requested Geometry Nodes, parametric organic/procedural geometry, or animation.
18
18
  Keep follow-up edits to an existing Geometry Nodes object in its graph, preserving its adjustable parameters and instances.
19
- Read the target after any scene edit this build needs. read(0) reads the scene root as a creation container.
19
+ An Area is a scene object that holds one Geometry Nodes graph; a Group is a container. read(0) reads the scene root as a creation container.
20
+ read returns the target's FlowIR source, its instances with their parameter overrides (in FlowIR units), its transform and its bounds. transform is position, rotation and size relative to the parent, in project units as the scene API reports them; an Area's size is a dimensionless scale, and the scene root's transform is null. bounds is the world-space extent in project units, null unless the target is a top-level Area whose graph renders geometry. Read the target again after another scene edit changes it.
21
+ If source is null and diagnostics is non-empty, the existing graph cannot be edited without loss. Explain the limitation; never treat it as empty or create a replacement to bypass the refusal.
22
+ Read geometry-nodes-reference before writing source. source is the complete FlowIR text for the target; JavaScript calls execute JavaScript, and source is a FlowIR string, not JavaScript or JSON.
20
23
  write updates an existing shared graph, attaches a new graph to an Area without one, or creates a new Area beneath the root/Group target. Use the returned objectId for follow-up edits.
21
- Read geometry-nodes-reference before writing source. Supply the complete FlowIR text after inspecting the target. JavaScript calls execute JavaScript; source is a FlowIR string, not JavaScript or JSON.
22
- Inspect the current target before replacing its graph; preserve unrelated geometry and parameter overrides.
23
- If source is null and diagnostics are present, the existing graph cannot be edited without loss. Explain the limitation; never treat it as empty or create a replacement to bypass the refusal.
24
- Preserve unrelated statements, parameter names, and FlowIR node IDs from the read. Shared graph edits affect all listed instances; report the affected objects. Valid overrides migrate by parameter name and type. Removed or type-changed parameters return to graph defaults and are reported.
24
+ Preserve unrelated statements, parameter names, FlowIR node IDs and parameter overrides from the read. Shared graph edits affect all listed instances; report the affected objects. Valid overrides migrate by parameter name and type. Removed or type-changed parameters return to graph defaults and are reported.
25
25
  Use min/max/step for naturally bounded parameters and omit ranges for free-form positions/rotations. Meaningful parameter names become editor labels. Do not invent label fields.
26
26
  SetMaterial assigns one scene material to every primitive it receives. Its Material input is a scene material ID, not a color; 0 selects the scene default material.
27
- Obtain that ID from the scene-editing skill inside one scene.access, where those lookups live: materialList with materialGetName and materialGetColor to reuse an existing material, materialCreate with materialSetColor and materialSetName for a new one, or listSuperMaterials with materialImport for a library material. Prefer reusing a material that already matches the request over creating another, and do not recolor a shared material unless every object using it should change.
28
- Create or import the material inside scene.access before writing the geometry target, all within one JavaScript call. Each call has a fresh scope and cannot read an earlier call's variables.
29
- Positions and sizes are millimetres; Boolean Blend uses the conversion in geometry-nodes-reference. Coordinates are right-handed, +X right, +Y up, +Z forward. Rotations are degrees, intrinsic X-Z-Y.
27
+ Obtain that ID from the scene-editing skill inside womp.scene.access, in the same JavaScript call as the write and before it: materialList with materialGetName and materialGetColor to reuse an existing material, materialCreate with materialSetColor and materialSetName for a new one, or listSuperMaterials with materialImport for a library material. Prefer reusing a material that already matches the request over creating another, and do not recolor a shared material unless every object using it should change.
28
+ FlowIR positions and sizes are millimetres; Boolean Blend uses the conversion in geometry-nodes-reference. Coordinates are right-handed, +X right, +Y up, +Z forward. Rotations are degrees, intrinsic X-Z-Y.
30
29
  On compiler errors, repair the source in this same conversation and retry with a fresh read. A rejected write made no change. Committed/unknown effects remain in execution receipts even if a later call fails; inspect before retrying.
31
- After applying, read the returned object and use the scene screenshot API with emitImage to inspect the applied result.`,
30
+ When status is "applied", the write result also holds the committed target read back from the scene: source, transform and bounds. Print it; it verifies the graph, so do not read the target again to check it. When the target's latest source is already printed in this conversation, make its next edit in a single call: read, replace and write. Use the scene screenshot API with emitImage to inspect the applied result.`,
32
31
  declarations: WOMP_GEOMETRY_DECLARATIONS,
33
32
  examples: [
34
33
  "const target = await womp.geometry.read(objectId); print(target);",
@@ -38,9 +37,9 @@ After applying, read the returned object and use the scene screenshot API with e
38
37
  },
39
38
  {
40
39
  name: "geometry-nodes-reference",
41
- version: "2",
40
+ version: "3",
42
41
  description: "FlowIR syntax, types, enums, and supported Geometry Nodes catalog; read before writing a graph.",
43
- instructions: `${reference.rules}\n${reference.catalog}`,
42
+ instructions: reference,
44
43
  declarations: "",
45
44
  examples: [],
46
45
  },
@@ -1 +1 @@
1
- export declare const WOMP_GEOMETRY_DECLARATIONS = "declare const womp: { geometry: {\n\"read\": ((objectId: number) => Promise<{\n\"kind\": \"container\" | \"graph\";\n\"objectId\": number;\n\"title\": string;\n\"graphId\"?: undefined | number;\n\"instanceId\"?: undefined | number;\n\"source\": null | string;\n\"instances\": Array<{\n\"instanceId\": number;\n\"objectIds\": Array<number>;\n\"params\": {\n[key: string]: number | {\n\"x\": number;\n\"y\": number;\n\"z\": number;\n};\n};\n}>;\n\"diagnostics\": Array<string>;\n}>);\n\"write\": ((options: {\n\"objectId\": number;\n\"source\": string;\n}) => Promise<{\n\"status\": \"applied\" | \"rejected\";\n\"objectId\": number;\n\"graphId\"?: undefined | number;\n\"instanceId\"?: undefined | number;\n\"affectedObjectIds\": Array<number>;\n\"diagnostics\": Array<string>;\n}>);\n} };\ndeclare function print(...values: unknown[]): void;\ndeclare function emitImage(ref: { id: string; mediaType: string; name?: string }, options?: { inspect?: boolean }): Promise<void>;\ndeclare function emitFile(ref: { id: string; mediaType: string; name?: string }): Promise<void>;\n";
1
+ export declare const WOMP_GEOMETRY_DECLARATIONS = "declare const womp: { geometry: {\n\"read\": ((objectId: number) => Promise<{\n\"kind\": \"container\" | \"graph\";\n\"objectId\": number;\n\"title\": string;\n\"graphId\"?: undefined | number;\n\"instanceId\"?: undefined | number;\n\"source\": null | string;\n\"transform\": null | {\n\"position\": {\n\"x\": number;\n\"y\": number;\n\"z\": number;\n};\n\"rotation\": {\n\"x\": number;\n\"y\": number;\n\"z\": number;\n};\n\"size\": {\n\"x\": number;\n\"y\": number;\n\"z\": number;\n};\n};\n\"bounds\": null | {\n\"min\": {\n\"x\": number;\n\"y\": number;\n\"z\": number;\n};\n\"max\": {\n\"x\": number;\n\"y\": number;\n\"z\": number;\n};\n};\n\"instances\": Array<{\n\"instanceId\": number;\n\"objectIds\": Array<number>;\n\"params\": {\n[key: string]: number | {\n\"x\": number;\n\"y\": number;\n\"z\": number;\n};\n};\n}>;\n\"diagnostics\": Array<string>;\n}>);\n\"write\": ((options: {\n\"objectId\": number;\n\"source\": string;\n}) => Promise<{\n\"status\": \"applied\" | \"rejected\";\n\"objectId\": number;\n\"graphId\"?: undefined | number;\n\"instanceId\"?: undefined | number;\n\"source\"?: undefined | string;\n\"transform\"?: undefined | null | {\n\"position\": {\n\"x\": number;\n\"y\": number;\n\"z\": number;\n};\n\"rotation\": {\n\"x\": number;\n\"y\": number;\n\"z\": number;\n};\n\"size\": {\n\"x\": number;\n\"y\": number;\n\"z\": number;\n};\n};\n\"bounds\"?: undefined | null | {\n\"min\": {\n\"x\": number;\n\"y\": number;\n\"z\": number;\n};\n\"max\": {\n\"x\": number;\n\"y\": number;\n\"z\": number;\n};\n};\n\"affectedObjectIds\": Array<number>;\n\"diagnostics\": Array<string>;\n}>);\n} };\ndeclare function print(...values: unknown[]): void;\ndeclare function emitImage(ref: { id: string; mediaType: string; name?: string }, options?: { inspect?: boolean }): Promise<void>;\ndeclare function emitFile(ref: { id: string; mediaType: string; name?: string }): Promise<void>;\n";
@@ -1,3 +1,3 @@
1
1
  // Generated by scripts/generate-skill-declarations.mjs.
2
2
  // prettier-ignore
3
- export const WOMP_GEOMETRY_DECLARATIONS = "declare const womp: { geometry: {\n\"read\": ((objectId: number) => Promise<{\n\"kind\": \"container\" | \"graph\";\n\"objectId\": number;\n\"title\": string;\n\"graphId\"?: undefined | number;\n\"instanceId\"?: undefined | number;\n\"source\": null | string;\n\"instances\": Array<{\n\"instanceId\": number;\n\"objectIds\": Array<number>;\n\"params\": {\n[key: string]: number | {\n\"x\": number;\n\"y\": number;\n\"z\": number;\n};\n};\n}>;\n\"diagnostics\": Array<string>;\n}>);\n\"write\": ((options: {\n\"objectId\": number;\n\"source\": string;\n}) => Promise<{\n\"status\": \"applied\" | \"rejected\";\n\"objectId\": number;\n\"graphId\"?: undefined | number;\n\"instanceId\"?: undefined | number;\n\"affectedObjectIds\": Array<number>;\n\"diagnostics\": Array<string>;\n}>);\n} };\ndeclare function print(...values: unknown[]): void;\ndeclare function emitImage(ref: { id: string; mediaType: string; name?: string }, options?: { inspect?: boolean }): Promise<void>;\ndeclare function emitFile(ref: { id: string; mediaType: string; name?: string }): Promise<void>;\n";
3
+ export const WOMP_GEOMETRY_DECLARATIONS = "declare const womp: { geometry: {\n\"read\": ((objectId: number) => Promise<{\n\"kind\": \"container\" | \"graph\";\n\"objectId\": number;\n\"title\": string;\n\"graphId\"?: undefined | number;\n\"instanceId\"?: undefined | number;\n\"source\": null | string;\n\"transform\": null | {\n\"position\": {\n\"x\": number;\n\"y\": number;\n\"z\": number;\n};\n\"rotation\": {\n\"x\": number;\n\"y\": number;\n\"z\": number;\n};\n\"size\": {\n\"x\": number;\n\"y\": number;\n\"z\": number;\n};\n};\n\"bounds\": null | {\n\"min\": {\n\"x\": number;\n\"y\": number;\n\"z\": number;\n};\n\"max\": {\n\"x\": number;\n\"y\": number;\n\"z\": number;\n};\n};\n\"instances\": Array<{\n\"instanceId\": number;\n\"objectIds\": Array<number>;\n\"params\": {\n[key: string]: number | {\n\"x\": number;\n\"y\": number;\n\"z\": number;\n};\n};\n}>;\n\"diagnostics\": Array<string>;\n}>);\n\"write\": ((options: {\n\"objectId\": number;\n\"source\": string;\n}) => Promise<{\n\"status\": \"applied\" | \"rejected\";\n\"objectId\": number;\n\"graphId\"?: undefined | number;\n\"instanceId\"?: undefined | number;\n\"source\"?: undefined | string;\n\"transform\"?: undefined | null | {\n\"position\": {\n\"x\": number;\n\"y\": number;\n\"z\": number;\n};\n\"rotation\": {\n\"x\": number;\n\"y\": number;\n\"z\": number;\n};\n\"size\": {\n\"x\": number;\n\"y\": number;\n\"z\": number;\n};\n};\n\"bounds\"?: undefined | null | {\n\"min\": {\n\"x\": number;\n\"y\": number;\n\"z\": number;\n};\n\"max\": {\n\"x\": number;\n\"y\": number;\n\"z\": number;\n};\n};\n\"affectedObjectIds\": Array<number>;\n\"diagnostics\": Array<string>;\n}>);\n} };\ndeclare function print(...values: unknown[]): void;\ndeclare function emitImage(ref: { id: string; mediaType: string; name?: string }, options?: { inspect?: boolean }): Promise<void>;\ndeclare function emitFile(ref: { id: string; mediaType: string; name?: string }): Promise<void>;\n";
@@ -31,6 +31,7 @@ export type GraphExt = {
31
31
  nodes: Record<string, {
32
32
  name?: string;
33
33
  position?: number[];
34
+ flowirSymbol?: string;
34
35
  [key: string]: unknown;
35
36
  }>;
36
37
  [key: string]: unknown;
@@ -1,4 +1,5 @@
1
- import type { BackendPort, BackendRegistry, BackendValue } from "./backendRegistry.js";
1
+ import type * as flowir from "@womp/flowir";
2
+ import type { BackendPort, BackendRegistry, BackendValue, GraphExt } from "./backendRegistry.js";
2
3
  /** Scalar backend storage is zero-initialized. Empty arrays have node-specific semantics. */
3
4
  export declare function defaultScalar(port: BackendPort): BackendValue | null;
4
5
  export type Edge = {
@@ -14,11 +15,13 @@ export type Edge = {
14
15
  export declare const VALUE_BACKEND_NAMES: Record<string, string>;
15
16
  /** FlowIR scalar type → backend node name (inverse of VALUE_BACKEND_NAMES). */
16
17
  export declare function valueBackendName(typeName: string): string | undefined;
17
- export declare function titleToAstName(title: string): string;
18
18
  export declare function astNameToTitle(s: string): string;
19
+ export declare function storedSymbol(graphExt: GraphExt, bid: string): string | undefined;
20
+ export declare function paramName(graphExt: GraphExt, valueBid: string, inputBid: string): string | undefined;
19
21
  export declare function isArrayPortType(type: string): boolean;
20
22
  export declare function parseConnection(key: string): Edge | null;
21
23
  export declare function buildIncomingMap(connections: Record<string, 0>): Map<string, Edge[]>;
24
+ export declare function backendTypeId(flowirRegistry: flowir.Registry, name: string): number;
22
25
  /** Resolve the GIP and value-node type IDs from the backend registry. */
23
26
  export declare function resolveParamTypeIds(backendRegistry: BackendRegistry): {
24
27
  gipTypeId: number | null;
@@ -4,7 +4,7 @@ export function defaultScalar(port) {
4
4
  return 0;
5
5
  if (port.type === "Bool")
6
6
  return false;
7
- if (port.type === "Vec" || port.type === "Vec3")
7
+ if (port.type === "Vec")
8
8
  return { x: 0, y: 0, z: 0 };
9
9
  return null;
10
10
  }
@@ -25,7 +25,7 @@ export function valueBackendName(typeName) {
25
25
  }
26
26
  return undefined;
27
27
  }
28
- export function titleToAstName(title) {
28
+ function titleToAstName(title) {
29
29
  return title
30
30
  .trim()
31
31
  .replace(/\s+(.)/g, (_, c) => "_" + c.toLowerCase())
@@ -36,8 +36,15 @@ export function astNameToTitle(s) {
36
36
  .replace(/_./g, (m) => " " + m[1].toUpperCase())
37
37
  .replace(/^./, (m) => m.toUpperCase());
38
38
  }
39
+ export function storedSymbol(graphExt, bid) {
40
+ return graphExt.nodes?.[bid]?.flowirSymbol;
41
+ }
42
+ export function paramName(graphExt, valueBid, inputBid) {
43
+ const label = graphExt.nodes?.[inputBid]?.name;
44
+ return storedSymbol(graphExt, valueBid) ?? (label ? titleToAstName(label) : undefined);
45
+ }
39
46
  export function isArrayPortType(type) {
40
- return type.endsWith("[]") || type.endsWith("Array");
47
+ return type.endsWith("Array");
41
48
  }
42
49
  export function parseConnection(key) {
43
50
  const m = key.match(/^(\d+):(\d+)-(\d+):(\d+)$/);
@@ -65,6 +72,14 @@ export function buildIncomingMap(connections) {
65
72
  }
66
73
  return incoming;
67
74
  }
75
+ export function backendTypeId(flowirRegistry, name) {
76
+ const node = flowirRegistry.getNode(name);
77
+ if (!node)
78
+ throw new Error(`Unknown dialect node ${name}.`);
79
+ if (!/^\d+$/.test(node.type))
80
+ throw new Error(`Dialect node ${name} has type ${JSON.stringify(node.type)}; the geometry backend needs a decimal type id.`);
81
+ return Number(node.type);
82
+ }
68
83
  /** Resolve the GIP and value-node type IDs from the backend registry. */
69
84
  export function resolveParamTypeIds(backendRegistry) {
70
85
  const gipTypeId = backendRegistry.getSchemaByName("GroupInputParam")?.typeId ?? null;
@@ -16,9 +16,9 @@ export function compileGeometry(source) {
16
16
  // of the text, not by whoever forwards it.
17
17
  if (!parsed.ok)
18
18
  throw new Error(formatBoundedDiagnostics(source, parsed.diagnostics));
19
- const result = flowir.compileFromProgram(parsed.program, source, flowirRegistry);
19
+ const result = flowir.compileFromProgram(parsed.program, flowirRegistry);
20
20
  if (!result.ok)
21
- throw new Error(formatBoundedDiagnostics(result.source, result.diagnostics));
21
+ throw new Error(formatBoundedDiagnostics(source, result.diagnostics));
22
22
  const compiled = lower(parsed.program, flowirRegistry, backendRegistry);
23
23
  for (const statement of parsed.program.statements) {
24
24
  const name = statement.kind === "node"
@@ -1,11 +1,7 @@
1
- import type { Diagnostic } from "@womp/flowir";
1
+ import * as flowir from "@womp/flowir";
2
2
  export declare const MAX_REPORTED_DIAGNOSTICS = 5;
3
3
  export declare const MAX_DIAGNOSTIC_BYTES: number;
4
- type DiagnosticLike = Pick<Diagnostic, "code" | "message" | "line" | "column" | "hint">;
5
- export declare function formatBoundedDiagnostics(source: string, diagnostics: readonly DiagnosticLike[], limits?: {
6
- count?: number;
7
- bytes?: number;
8
- }): string;
4
+ export declare function formatBoundedDiagnostics(source: string, diagnostics: readonly flowir.Diagnostic[]): string;
9
5
  /**
10
6
  * A host-assembled diagnostic list, bounded the same way: each entry to
11
7
  * `MAX_DIAGNOSTIC_BYTES`, the list to `MAX_REPORTED_DIAGNOSTICS` entries, with
@@ -15,4 +11,3 @@ export declare function boundedDiagnostics(diagnostics: readonly string[], limit
15
11
  count?: number;
16
12
  bytes?: number;
17
13
  }): string[];
18
- export {};
@@ -1,57 +1,9 @@
1
+ import * as flowir from "@womp/flowir";
1
2
  import { boundedText } from "../bounded-output.js";
2
- // WHY THIS FILE EXISTS. `@womp/flowir`'s own `formatDiagnostics` emits one
3
- // four-line block per diagnostic -- header, source line, caret, hint -- and
4
- // joins them, so a compiler that reports the same one-character mistake five
5
- // times repeats the offending source line five times and its hint five times.
6
- // That string is what `compileGeometry` throws and what a rejected
7
- // `geometry.write` hands the model as a diagnostic; on staging one bad enum
8
- // value cost a whole turn. The compiler holds the structured diagnostics
9
- // already, so the bound belongs here rather than behind a package this one
10
- // only consumes.
11
- //
12
- // What is kept: every diagnostic's header (code, line, column, message), the
13
- // source line once per distinct line, its caret, and each distinct hint once.
14
- // What is dropped is only repetition and the tail past the limits, and the
15
- // message says so.
16
3
  export const MAX_REPORTED_DIAGNOSTICS = 5;
17
4
  export const MAX_DIAGNOSTIC_BYTES = 4 * 1024;
18
- export function formatBoundedDiagnostics(source, diagnostics, limits = {}) {
19
- const count = limits.count ?? MAX_REPORTED_DIAGNOSTICS;
20
- const bytes = limits.bytes ?? MAX_DIAGNOSTIC_BYTES;
21
- if (diagnostics.length === 0)
22
- return "";
23
- const lines = source.replace(/\r\n?/gu, "\n").split("\n");
24
- const gutterWidth = String(lines.length || 1).length;
25
- const pointerPrefix = `${" ".repeat(gutterWidth)} | `;
26
- const shownLines = new Set();
27
- const shownCarets = new Set();
28
- const shownHints = new Set();
29
- const out = [];
30
- for (const diagnostic of diagnostics.slice(0, count)) {
31
- out.push(`error[${diagnostic.code}] L${diagnostic.line}:${diagnostic.column} ${diagnostic.message}`);
32
- const sourceLine = lines[diagnostic.line - 1];
33
- if (sourceLine !== undefined) {
34
- // A line already quoted is not quoted again -- that repetition is the
35
- // defect this bounds. Its caret still is, once per column, so every
36
- // diagnostic keeps a marker against the quoted line above it.
37
- if (shownLines.has(diagnostic.line) === false) {
38
- shownLines.add(diagnostic.line);
39
- out.push(`${String(diagnostic.line).padStart(gutterWidth, " ")} | ${sourceLine}`);
40
- }
41
- const caret = `${pointerPrefix}${" ".repeat(Math.max(diagnostic.column - 1, 0))}^`;
42
- if (shownCarets.has(`${diagnostic.line}:${caret}`) === false) {
43
- shownCarets.add(`${diagnostic.line}:${caret}`);
44
- out.push(caret);
45
- }
46
- }
47
- if (shownHints.has(diagnostic.hint) === false) {
48
- shownHints.add(diagnostic.hint);
49
- out.push(`${pointerPrefix}hint: ${diagnostic.hint}`);
50
- }
51
- }
52
- if (diagnostics.length > count)
53
- out.push(`${diagnostics.length - count} more diagnostics were omitted (${count} of ${diagnostics.length} shown). Fix these, then compile again.`);
54
- return boundedText(out.join("\n"), bytes, "Diagnostics");
5
+ export function formatBoundedDiagnostics(source, diagnostics) {
6
+ return boundedText(flowir.formatDiagnostics(source, diagnostics, MAX_REPORTED_DIAGNOSTICS), MAX_DIAGNOSTIC_BYTES, "Diagnostics");
55
7
  }
56
8
  /**
57
9
  * A host-assembled diagnostic list, bounded the same way: each entry to
@@ -31,9 +31,9 @@ export declare const graphExtSchema: z.ZodObject<{
31
31
  nodes: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodObject<{
32
32
  name: z.ZodOptional<z.ZodString>;
33
33
  position: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
34
+ flowirSymbol: z.ZodOptional<z.ZodString>;
34
35
  }, z.core.$catchall<z.ZodJSONSchema>>>>;
35
36
  }, z.core.$catchall<z.ZodJSONSchema>>;
36
37
  export type GeometryGraph = z.infer<typeof geometryGraphSchema>;
37
38
  export type GraphData = z.infer<typeof graphDataSchema>;
38
- /** FlowIR statement roles map to backend IDs only for the current conversion. */
39
39
  export type StatementNodes = Record<string, string>;
@@ -25,6 +25,7 @@ export const graphExtSchema = z
25
25
  .object({
26
26
  name: z.string().optional(),
27
27
  position: z.array(z.number()).length(2).optional(),
28
+ flowirSymbol: z.string().optional(),
28
29
  })
29
30
  .catchall(z.json()))
30
31
  .default({}),
@@ -5,5 +5,4 @@ export { lower } from "./lower.js";
5
5
  export type { LowerResult } from "./lower.js";
6
6
  export { lift } from "./lift.js";
7
7
  export { writeParamData } from "./writeParamData.js";
8
- export declare function loadGeoNodesDialect(): flowir.DialectSchema;
9
8
  export declare function createGeoNodesRegistry(): flowir.Registry;
@@ -4,9 +4,6 @@ export { BackendRegistry } from "./backendRegistry.js";
4
4
  export { lower } from "./lower.js";
5
5
  export { lift } from "./lift.js";
6
6
  export { writeParamData } from "./writeParamData.js";
7
- export function loadGeoNodesDialect() {
8
- return dialectJson;
9
- }
10
7
  export function createGeoNodesRegistry() {
11
8
  return flowir.Registry.fromJsonObject(dialectJson);
12
9
  }