@field/cascade 0.2.22 → 0.3.1

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 (233) hide show
  1. package/AGENTS.md +47 -7
  2. package/ARCHITECTURE.md +30 -2
  3. package/CHANGELOG.md +107 -0
  4. package/DESIGN.md +10 -5
  5. package/README.md +103 -7
  6. package/dist/assets/CodeEditor-D22fVi-4.js +623 -0
  7. package/dist/assets/CodePanel-DPNEYfcR.js +2 -0
  8. package/dist/assets/CustomNodeDialog-DeHCAvgb.js +1 -0
  9. package/dist/assets/{ExportDialog-DWN4nhXI.js → ExportDialog-BJu2Bpvv.js} +2 -2
  10. package/dist/assets/ProjectSettings-DWpVtWEx.js +2 -0
  11. package/dist/assets/SettingsDialog-za00IS3L.js +1 -0
  12. package/dist/assets/VersionHistoryDialog-DTMGhI0_.js +3 -0
  13. package/dist/assets/{core-Bji4aZUU.js → core-JsyKMO1k.js} +29 -38
  14. package/dist/assets/{cssMode-B6Z_Q1Kh.js → cssMode-emdRheFK.js} +1 -1
  15. package/dist/assets/{editor.api-jMoW_43X.js → editor.api-xHX-fMSo.js} +1 -1
  16. package/dist/assets/{freemarker2-DvvPV6m5.js → freemarker2-C3M8_Gs3.js} +1 -1
  17. package/dist/assets/geo-BAR6lQp5.js +1 -0
  18. package/dist/assets/{handlebars-B8rfuSIa.js → handlebars-CBMurtFk.js} +1 -1
  19. package/dist/assets/{html-DyHm6JCp.js → html-xbLQwknw.js} +1 -1
  20. package/dist/assets/{htmlMode-E9XLWE5u.js → htmlMode-2wKdDTon.js} +1 -1
  21. package/dist/assets/{image-BUcQC7PG.js → image-i1j6nWKe.js} +26 -40
  22. package/dist/assets/index-BDV5xGBZ.css +1 -0
  23. package/dist/assets/index-CMcH8AgP.js +212 -0
  24. package/dist/assets/index-client-BoJgsN9M.js +1 -0
  25. package/dist/assets/{javascript-C_bM5PCN.js → javascript-DJaOo_2I.js} +1 -1
  26. package/dist/assets/{jsonMode-BdyVS7G4.js → jsonMode-Bheas8_0.js} +1 -1
  27. package/dist/assets/{legacy-DIu3XRwl.js → legacy-CAj2_7QE.js} +2 -2
  28. package/dist/assets/{liquid-CDkn3spA.js → liquid-BCj_BGbM.js} +1 -1
  29. package/dist/assets/{lspLanguageFeatures-AB5DMAsr.js → lspLanguageFeatures-BfIsx_xz.js} +1 -1
  30. package/dist/assets/{mdx-BvH_NjDN.js → mdx-DwiQDiVO.js} +1 -1
  31. package/dist/assets/{nodeTypeUtils-CweA3NHd.js → nodeTypeUtils-BBA1JqHx.js} +1 -1
  32. package/dist/assets/{python-B7uii4fi.js → python-BjrB5nqj.js} +1 -1
  33. package/dist/assets/{razor-Z48fWdJc.js → razor-CfoBmGDs.js} +1 -1
  34. package/dist/assets/{tsMode-BDgacVG4.js → tsMode-BcEoMSxp.js} +1 -1
  35. package/dist/assets/{typescript-Derb6Oqa.js → typescript-DIg7TEmQ.js} +1 -1
  36. package/dist/assets/{workers-BtoMX_pm.js → workers-vXYuwtPw.js} +1 -1
  37. package/dist/assets/{xml-B1CaFSrG.js → xml-DexLQIQy.js} +1 -1
  38. package/dist/assets/{yaml-HmIeA0KH.js → yaml-DUnD_1dE.js} +1 -1
  39. package/dist/cli/index.js +16467 -10200
  40. package/dist/cli/runtime/gpu.ts +76 -0
  41. package/dist/cli/runtime/io.ts +93 -7
  42. package/dist/contracts/animation.d.ts +40 -0
  43. package/dist/contracts/animation.js +14 -0
  44. package/dist/contracts/capabilities.d.ts +69 -6
  45. package/dist/contracts/definition.d.ts +60 -2
  46. package/dist/contracts/document.d.ts +15 -0
  47. package/dist/contracts/index.d.ts +1 -0
  48. package/dist/contracts/index.js +1 -0
  49. package/dist/contracts/schema.js +8 -5
  50. package/dist/contracts/validate.d.ts +10 -1
  51. package/dist/contracts/validate.js +20 -3
  52. package/dist/gpu.d.ts +71 -0
  53. package/dist/gpu.js +53 -0
  54. package/dist/graphs/default.cascade +1 -1
  55. package/dist/index.html +5 -5
  56. package/dist/io.d.ts +196 -0
  57. package/dist/io.js +303 -0
  58. package/dist/net.d.ts +1 -0
  59. package/dist/net.js +28 -0
  60. package/dist/runtime/animation/channel.d.ts +108 -0
  61. package/dist/runtime/animation/channel.js +204 -0
  62. package/dist/runtime/animation/frameRange.d.ts +78 -0
  63. package/dist/runtime/animation/frameRange.js +76 -0
  64. package/dist/runtime/animation/index.d.ts +10 -0
  65. package/dist/runtime/animation/index.js +8 -0
  66. package/dist/runtime/builtins/core/random.d.ts +1 -1
  67. package/dist/runtime/builtins/core/remap.d.ts +1 -1
  68. package/dist/runtime/builtins/geo/copyToPoints.d.ts +1 -1
  69. package/dist/runtime/builtins/geo/copyToPoints.js +1 -1
  70. package/dist/runtime/builtins/geo/generators.d.ts +1 -1
  71. package/dist/runtime/builtins/geo/merge.d.ts +1 -1
  72. package/dist/runtime/builtins/geo/svgExport.d.ts +1 -1
  73. package/dist/runtime/builtins/geo/svgExport.js +1 -1
  74. package/dist/runtime/builtins/geo/transform.d.ts +1 -1
  75. package/dist/runtime/builtins/geo/transform.js +1 -1
  76. package/dist/runtime/definition/architecture.d.ts +1 -1
  77. package/dist/runtime/definition/extract.d.ts +1 -1
  78. package/dist/runtime/definition/extract.js +1 -1
  79. package/dist/runtime/error.d.ts +1 -1
  80. package/dist/runtime/expressions/evaluator.d.ts +53 -0
  81. package/dist/runtime/expressions/evaluator.js +193 -0
  82. package/dist/runtime/expressions/index.d.ts +15 -0
  83. package/dist/runtime/expressions/index.js +12 -0
  84. package/dist/runtime/expressions/math.d.ts +75 -0
  85. package/dist/runtime/expressions/math.js +140 -0
  86. package/dist/runtime/expressions/preprocess.d.ts +33 -0
  87. package/dist/runtime/expressions/preprocess.js +102 -0
  88. package/dist/runtime/expressions/scope.d.ts +27 -0
  89. package/dist/runtime/expressions/scope.js +10 -0
  90. package/dist/runtime/expressions/time.d.ts +54 -0
  91. package/dist/runtime/expressions/time.js +71 -0
  92. package/dist/runtime/expressions/types.d.ts +66 -0
  93. package/dist/runtime/expressions/types.js +9 -0
  94. package/dist/runtime/geometry/attributes.d.ts +2 -2
  95. package/dist/runtime/geometry/attributes.js +1 -1
  96. package/dist/runtime/geometry/builder.d.ts +1 -1
  97. package/dist/runtime/geometry/builder.js +1 -1
  98. package/dist/runtime/geometry/copy.d.ts +1 -1
  99. package/dist/runtime/geometry/copy.js +1 -1
  100. package/dist/runtime/geometry/generate.d.ts +1 -1
  101. package/dist/runtime/geometry/groups.d.ts +1 -1
  102. package/dist/runtime/geometry/groups.js +1 -1
  103. package/dist/runtime/geometry/matrix.d.ts +1 -1
  104. package/dist/runtime/geometry/merge.d.ts +1 -1
  105. package/dist/runtime/geometry/merge.js +1 -1
  106. package/dist/runtime/geometry/primitives.d.ts +1 -1
  107. package/dist/runtime/geometry/primitives.js +1 -1
  108. package/dist/runtime/geometry/promote.d.ts +1 -1
  109. package/dist/runtime/geometry/promote.js +1 -1
  110. package/dist/runtime/geometry/resample.d.ts +1 -1
  111. package/dist/runtime/geometry/resample.js +1 -1
  112. package/dist/runtime/geometry/svg.d.ts +1 -1
  113. package/dist/runtime/geometry/svg.js +1 -1
  114. package/dist/runtime/geometry/transform.d.ts +1 -1
  115. package/dist/runtime/geometry/transform.js +1 -1
  116. package/dist/runtime/index.d.ts +4 -1
  117. package/dist/runtime/index.js +4 -1
  118. package/dist/runtime/params/animator.d.ts +74 -0
  119. package/dist/runtime/params/animator.js +137 -0
  120. package/dist/runtime/params/index.d.ts +11 -0
  121. package/dist/runtime/params/index.js +9 -0
  122. package/dist/runtime/params/resolve.d.ts +73 -0
  123. package/dist/runtime/params/resolve.js +62 -0
  124. package/dist/runtime/runtime.d.ts +42 -0
  125. package/dist/runtime/runtime.js +351 -29
  126. package/dist/runtime/types.d.ts +32 -6
  127. package/dist/shell.d.ts +3 -3
  128. package/doc/NODE_REFERENCE.md +395 -0
  129. package/doc/PROJECT_AUTHORING.md +148 -6
  130. package/package.json +30 -3
  131. package/dist/assets/CodeEditor-DoaNEUaM.js +0 -641
  132. package/dist/assets/CodePanel-B0_2XJ-t.js +0 -2
  133. package/dist/assets/CustomNodeDialog-026lzecD.js +0 -1
  134. package/dist/assets/ProjectSettings-BzT164Yx.js +0 -2
  135. package/dist/assets/SettingsDialog-DmHb1V0r.js +0 -1
  136. package/dist/assets/VersionHistoryDialog-hH6VsXLX.js +0 -3
  137. package/dist/assets/geo-CXmCtzMM.js +0 -2
  138. package/dist/assets/index-BXYXSdgh.js +0 -224
  139. package/dist/assets/index-CrNke7SW.css +0 -1
  140. package/dist/assets/index-client-DN_FvjN8.js +0 -1
  141. package/dist/assets/quill-DjZ69wL_.js +0 -121
  142. package/dist/cli/index.js.map +0 -7
  143. package/dist/contracts/capabilities.d.ts.map +0 -1
  144. package/dist/contracts/capabilities.js.map +0 -1
  145. package/dist/contracts/color.d.ts.map +0 -1
  146. package/dist/contracts/color.js.map +0 -1
  147. package/dist/contracts/definition.d.ts.map +0 -1
  148. package/dist/contracts/definition.js.map +0 -1
  149. package/dist/contracts/document.d.ts.map +0 -1
  150. package/dist/contracts/document.js.map +0 -1
  151. package/dist/contracts/geometry.d.ts.map +0 -1
  152. package/dist/contracts/geometry.js.map +0 -1
  153. package/dist/contracts/index.d.ts.map +0 -1
  154. package/dist/contracts/index.js.map +0 -1
  155. package/dist/contracts/schema.d.ts.map +0 -1
  156. package/dist/contracts/schema.js.map +0 -1
  157. package/dist/contracts/validate.d.ts.map +0 -1
  158. package/dist/contracts/validate.js.map +0 -1
  159. package/dist/contracts/values.d.ts.map +0 -1
  160. package/dist/contracts/values.js.map +0 -1
  161. package/dist/graphs/default-orig.cascade +0 -128
  162. package/dist/runtime/browser.d.ts.map +0 -1
  163. package/dist/runtime/browser.js.map +0 -1
  164. package/dist/runtime/builtins/core/index.d.ts.map +0 -1
  165. package/dist/runtime/builtins/core/index.js.map +0 -1
  166. package/dist/runtime/builtins/core/random.d.ts.map +0 -1
  167. package/dist/runtime/builtins/core/random.js.map +0 -1
  168. package/dist/runtime/builtins/core/remap.d.ts.map +0 -1
  169. package/dist/runtime/builtins/core/remap.js.map +0 -1
  170. package/dist/runtime/builtins/core/routing.d.ts.map +0 -1
  171. package/dist/runtime/builtins/core/routing.js.map +0 -1
  172. package/dist/runtime/builtins/core/structural.d.ts.map +0 -1
  173. package/dist/runtime/builtins/core/structural.js.map +0 -1
  174. package/dist/runtime/builtins/geo/copyToPoints.d.ts.map +0 -1
  175. package/dist/runtime/builtins/geo/copyToPoints.js.map +0 -1
  176. package/dist/runtime/builtins/geo/definitions.d.ts.map +0 -1
  177. package/dist/runtime/builtins/geo/definitions.js.map +0 -1
  178. package/dist/runtime/builtins/geo/generators.d.ts.map +0 -1
  179. package/dist/runtime/builtins/geo/generators.js.map +0 -1
  180. package/dist/runtime/builtins/geo/index.d.ts.map +0 -1
  181. package/dist/runtime/builtins/geo/index.js.map +0 -1
  182. package/dist/runtime/builtins/geo/merge.d.ts.map +0 -1
  183. package/dist/runtime/builtins/geo/merge.js.map +0 -1
  184. package/dist/runtime/builtins/geo/namespace.d.ts.map +0 -1
  185. package/dist/runtime/builtins/geo/namespace.js.map +0 -1
  186. package/dist/runtime/builtins/geo/svgExport.d.ts.map +0 -1
  187. package/dist/runtime/builtins/geo/svgExport.js.map +0 -1
  188. package/dist/runtime/builtins/geo/transform.d.ts.map +0 -1
  189. package/dist/runtime/builtins/geo/transform.js.map +0 -1
  190. package/dist/runtime/builtins/index.d.ts.map +0 -1
  191. package/dist/runtime/builtins/index.js.map +0 -1
  192. package/dist/runtime/definition/architecture.d.ts.map +0 -1
  193. package/dist/runtime/definition/architecture.js.map +0 -1
  194. package/dist/runtime/definition/extract.d.ts.map +0 -1
  195. package/dist/runtime/definition/extract.js.map +0 -1
  196. package/dist/runtime/error.d.ts.map +0 -1
  197. package/dist/runtime/error.js.map +0 -1
  198. package/dist/runtime/geometry/attributes.d.ts.map +0 -1
  199. package/dist/runtime/geometry/attributes.js.map +0 -1
  200. package/dist/runtime/geometry/builder.d.ts.map +0 -1
  201. package/dist/runtime/geometry/builder.js.map +0 -1
  202. package/dist/runtime/geometry/copy.d.ts.map +0 -1
  203. package/dist/runtime/geometry/copy.js.map +0 -1
  204. package/dist/runtime/geometry/generate.d.ts.map +0 -1
  205. package/dist/runtime/geometry/generate.js.map +0 -1
  206. package/dist/runtime/geometry/groups.d.ts.map +0 -1
  207. package/dist/runtime/geometry/groups.js.map +0 -1
  208. package/dist/runtime/geometry/index.d.ts.map +0 -1
  209. package/dist/runtime/geometry/index.js.map +0 -1
  210. package/dist/runtime/geometry/matrix.d.ts.map +0 -1
  211. package/dist/runtime/geometry/matrix.js.map +0 -1
  212. package/dist/runtime/geometry/merge.d.ts.map +0 -1
  213. package/dist/runtime/geometry/merge.js.map +0 -1
  214. package/dist/runtime/geometry/primitives.d.ts.map +0 -1
  215. package/dist/runtime/geometry/primitives.js.map +0 -1
  216. package/dist/runtime/geometry/promote.d.ts.map +0 -1
  217. package/dist/runtime/geometry/promote.js.map +0 -1
  218. package/dist/runtime/geometry/resample.d.ts.map +0 -1
  219. package/dist/runtime/geometry/resample.js.map +0 -1
  220. package/dist/runtime/geometry/svg.d.ts.map +0 -1
  221. package/dist/runtime/geometry/svg.js.map +0 -1
  222. package/dist/runtime/geometry/transform.d.ts.map +0 -1
  223. package/dist/runtime/geometry/transform.js.map +0 -1
  224. package/dist/runtime/index.d.ts.map +0 -1
  225. package/dist/runtime/index.js.map +0 -1
  226. package/dist/runtime/node.d.ts.map +0 -1
  227. package/dist/runtime/node.js.map +0 -1
  228. package/dist/runtime/runtime.d.ts.map +0 -1
  229. package/dist/runtime/runtime.js.map +0 -1
  230. package/dist/runtime/types.d.ts.map +0 -1
  231. package/dist/runtime/types.js.map +0 -1
  232. package/dist/shell.js.map +0 -7
  233. package/dist/stage.js.map +0 -7
package/AGENTS.md CHANGED
@@ -6,6 +6,8 @@ Cascade is a reusable generative-design graph runtime with an optional Studio UI
6
6
 
7
7
  - `DESIGN.md` — product and architecture source of truth.
8
8
  - `ARCHITECTURE.md` — repository boundaries and dependency direction.
9
+ - `CHANGELOG.md` — what each release changed, newest first. Read it before assuming how a recent feature works.
10
+ - `doc/NODE_REFERENCE.md` — every built-in node with its declared ports, generated from the definitions. Read it instead of searching `src/nodes/` for what a node does; regenerate with `npm run build:node-reference` rather than editing it.
9
11
  - `packages/contracts/` — public types, deterministic node definitions, capabilities, and schemas.
10
12
  - `packages/runtime/` — environment-neutral loading, validation, scheduling, and headless APIs.
11
13
  - `src/editor/` — Svelte Studio only; never put runtime behavior here.
@@ -21,6 +23,7 @@ A Cascade project is an ordinary directory or Git repository:
21
23
  my-project/
22
24
  ├── cascade.json # optional host policy and project settings
23
25
  ├── index.cascade # default graph; more .cascade files are allowed
26
+ ├── AGENTS.md # written by `cascade new`; authoritative for that project
24
27
  ├── nodes/
25
28
  │ └── Multiply/
26
29
  │ └── index.ts # project.Multiply
@@ -32,7 +35,7 @@ my-project/
32
35
  └── tsconfig.json
33
36
  ```
34
37
 
35
- Use `cascade new <name>` to create a project. Run `cascade .` from its root to open Studio, `cascade validate <graph.cascade>` for validation, `cascade check <graph.cascade>` for static definition/type checks, `cascade inspect <graph.cascade>` for a JSON summary, and `cascade run <graph.cascade>` for headless execution.
38
+ Use `cascade new <name>` to create a project and `cascade node <Name>` to scaffold a custom node in one. Run `cascade .` from a project root to open Studio, `cascade validate <graph.cascade>` for validation, `cascade check <graph.cascade>` for static definition/type checks, `cascade inspect <graph.cascade>` for a JSON summary, `cascade run <graph.cascade>` for headless execution, and `cascade run <graph.cascade> --frames 1-100` for an offline image sequence. `cascade projects` shows or sets the default projects directory.
36
39
 
37
40
  ## Launch Studio
38
41
 
@@ -70,7 +73,9 @@ Project panels are trusted Studio extensions, not graph/runtime modules. Put the
70
73
 
71
74
  ## Create a deterministic node
72
75
 
73
- Prefer a statically inspectable definition over legacy setup-time calls:
76
+ Two node styles exist and both are current. A **definition-v1** module exports a literal `definition` and a typed `execute(context)`; it is statically inspectable, it is what `cascade check` requires, and it runs through the deterministic runtime. A **dynamic** module exports `execute(node, graph)` and declares its ports and parameters imperatively inside it; that is what `cascade run --frames` renders. Studio cooks both, dispatching on what the compiled module exports (`src/nodes/definition/projectDefinition.ts`), so a project node written either way runs in the editor. Prefer definition-v1 for anything new inside this repository, and do not call dynamic modules "legacy" — the naming was retired in 0.3.0, and while Studio no longer blocks the migration, the existing sketches are still dynamic.
77
+
78
+ A definition-v1 module looks like this:
74
79
 
75
80
  ```ts
76
81
  import type { NodeDefinition, NodeExecutionContext } from 'cascade/contracts';
@@ -128,6 +133,37 @@ is +Y up; the flip into SVG's frame lives in `SvgExport` alone.
128
133
 
129
134
  These modules are reserved and need no file under `nodes/`. Do not override a
130
135
  `cascade.core.*` module or use ambient random/time state in a portable node.
136
+ Time and randomness reach a parameter as an expression or a keyframe channel,
137
+ which is why a node reading the clock itself is still wrong.
138
+
139
+ Anything with an x and a y is **one** `vec2`, not two floats — likewise `vec3`
140
+ and `vec4`, and `vec2i`/`vec3i`/`vec4i` for integer counts and pixel sizes.
141
+ Positions, offsets, sizes, scales and resolutions are single ports. Two floats
142
+ that are really one vector cannot connect to a `vec2` output, cost two
143
+ Inspector rows and two keyframes, and let a graph carry an x without its y. The
144
+ type system knows what a `vec2` is; nothing tells it that `offset_x` and
145
+ `offset_y` are related. Split them only when the components differ in kind.
146
+
147
+ ## Animation, expressions, and parameters
148
+
149
+ - **`Node.evalParm` in `src/nodes/Node.ts` is the only place a parameter's bindings resolve**, in the order channel, expression, value. Never read `prop.value` to obtain an animated parameter, and never add a second resolution path: an expression badge and a viewer disagreeing about the same frame is exactly what one produced.
150
+ - **A parameter is one store.** `param()` declares the parameter; the value, the expression and the channel live in the prop of the same name. `NodeParameter.value` reads *resolved*; `rawParameterValue()` reads what the author typed, and is what serialization and cache keys must use. `param()` is re-declared on every cook of a dynamic node, so it must stay idempotent — it may never reset a value, an expression, or a channel.
151
+ - **Mechanisms belong to the runtime, composition to the host.** `packages/runtime/src/expressions` owns preprocessing, scope and the maths library; `packages/runtime/src/animation` owns the channel model, sampling, interpolation, serialization and frame-range walking. Both depend only on contracts. Do not implement interpolation, a frame clock, or a second `$T` anywhere else.
152
+ - **The serialized shape is owned by contracts** (`packages/contracts/src/animation.ts`): a channel is `{ keys: [{ frame, value, interpolation? }] }` inside the node's `props`, with `interpolation` omitted when it is the `smooth` default. There is no timeline section in the document, and adding one would need reconciling on every rename, delete and embed.
153
+ - Bare trigonometry is **radians**, deliberately unlike Houdini; `sind`/`cosd`/`tand`/`radians()`/`degrees()` exist for formulae carried over. Only `ch()`, `chs()` and `chv()` are scanned for dependencies, so a new way to reach another parameter also needs a dependency pattern or it will not recook.
154
+ - The deterministic runtime has no parameter animation. `cascade run --frames` refuses a definition-v1 graph rather than rendering frame 1 repeatedly. Extending animation to that runtime is a migration step; do not add a compatibility branch to Studio for it.
155
+ - Studio's timeline panel holds its own frame range, loop and playhead. Only fps reaches the engine clock. Nothing about playback is saved with the document.
156
+
157
+ ## Agent sessions
158
+
159
+ `server/src/agent` is a project host service at the same layer as shell and Python, under the same policy. Read the file headers there before changing it; they carry the reasoning.
160
+
161
+ - A run is keyed by agent alias and document, and **owned by the server**. The request that starts it is only the first reader, and a closed socket detaches a reader. Never tie a run's lifetime to a request: a browser reload sending SIGTERM to an agent mid-edit is the fault this design exists to prevent.
162
+ - The server drains the child's stdout whether or not anyone is attached. An unread pipe strands the agent.
163
+ - The transcript is a bounded ring with absolute sequence numbers. A reader attaches with `since`, gets one `attached` event carrying `running`, `replayed` and `dropped`, then the replay and the live stream with nothing awaited in between. Report `dropped` rather than hiding it; a transcript that silently loses its middle reads as an agent that did something inexplicable.
164
+ - Executables resolve server-side from the `commands` allowlist in `cascade.json` — the shell service's allowlist, deliberately the same one. A path from the browser is never accepted, and `agent.<alias>.args` supplies fixed flags. The routes need the capability token and are absent when sensitive capabilities are not allowed, so transport is NDJSON over POST rather than SSE.
165
+ - Cancellation signals the process group, because agents spawn children.
166
+ - Freshness is a watcher, not a poll: `server/src/graphWatch.ts` publishes `.cascade` changes over SSE and `src/editor/graphDocumentWatch.ts` fingerprints the document with volatile metadata stripped, so Studio's own save does not return as an external change. A reload is declined while there are unsaved edits rather than picking a winner.
131
167
 
132
168
  ## Navigate and edit graphs
133
169
 
@@ -156,13 +192,17 @@ the TypeScript 7 preview until it is stable and the repository gates pass.
156
192
  3. Prefer deletion or an existing utility over another wrapper.
157
193
  4. Run the focused test, then `npm run check`, `npm run test:run`, `npm --prefix server run build`, `npm run build`, and `npm run build:cli` when the affected surface warrants it. Keep the CLI build last because Vite refreshes `dist/`.
158
194
  5. For package/public API changes, also test the packed tarball from an empty temporary project.
195
+ 6. When a built-in definition or a node library registry changes, regenerate the node reference with `npm run build:node-reference` and commit the result. `build:cli` runs it too, so a full build will produce the diff whether or not you meant to. Nothing tests that the committed file is current, so a stale one ships silently — and it is the first thing a project's agent reads.
159
196
 
160
197
  Do not add dependencies without a concrete need. Do not create extra packages unless they need independent publication or versioning.
161
198
 
162
199
  ## Versioning
163
200
 
164
- Cascade `0.2.0` is the 2026 architecture rework. The current release is
165
- `0.2.22`. Increment the root package and CLI patch version for every committed
166
- feature or release change (`0.2.22`, `0.2.22`, …), keeping `package.json`, the
167
- lockfile, and CLI output aligned. Internal private workspaces do not receive
168
- independent versions unless they become separately published packages.
201
+ Cascade `0.2.0` is the 2026 architecture rework. The current release is `0.3.0`,
202
+ which added the animation system and the agent console; `CHANGELOG.md` records
203
+ what changed and is the file to update before a release commit. Increment the
204
+ root package and CLI patch version for every committed feature or release
205
+ change, keeping `package.json`, the lockfile, and CLI output aligned. A minor
206
+ bump is for a feature large enough to need its own `CHANGELOG.md` section.
207
+ Internal private workspaces do not receive independent versions unless they
208
+ become separately published packages.
package/ARCHITECTURE.md CHANGED
@@ -21,7 +21,7 @@ Contracts contain no DOM, Svelte, Express, filesystem, process, or implementatio
21
21
  The runtime is environment-neutral. It works in Node and browsers without importing either platform’s APIs, and it never imports Studio. Registration is sealed after the first graph load. Each graph permits one active run and has explicit lifecycle states.
22
22
 
23
23
  The reserved `cascade.core.*` registry is owned here. `Subnet`, `Input`,
24
- `Output`, `Switch`, `Merge`, `Select`, seeded `Random`, and `Remap` resolve
24
+ `Output`, `Switch`, `Merge`, `Select`, `Null`, seeded `Random`, and `Remap` resolve
25
25
  without a project module loader. Named, typed root Input/Output nodes form an
26
26
  explicit headless graph interface; typed child Input/Output nodes form a subnet interface.
27
27
  Randomness is a pure function of an authored seed, never ambient process state.
@@ -61,7 +61,7 @@ Custom server applications and browser frontends use `cascade/runtime` directly
61
61
 
62
62
  The repository has exactly two internal npm workspaces: `@cascade/contracts` and `@cascade/runtime`. The root `cascade` package is the only published package and the only owner of the executable.
63
63
 
64
- Public subpaths are `cascade/contracts`, `cascade/contracts/schema`, `cascade/runtime`, `cascade/runtime/node`, `cascade/runtime/browser`, and the compatibility `cascade/shell`.
64
+ Public subpaths are `cascade/contracts`, `cascade/contracts/schema`, `cascade/runtime`, `cascade/runtime/node`, `cascade/runtime/browser`, `cascade/runtime/expressions`, `cascade/runtime/animation`, `cascade/runtime/definition/extract`, `cascade/io`, `cascade/net`, `cascade/stage`, and the compatibility `cascade/shell`.
65
65
 
66
66
  Studio-only project extensions use the type-only `cascade/studio/panel` contract. They are compiled and loaded only by Studio; the neutral runtime and headless hosts never discover or import them.
67
67
 
@@ -113,6 +113,28 @@ Data runs follow graph dependencies. Explicit triggers use a FIFO queue, authore
113
113
 
114
114
  Execution failures resolve to a failed `RunResult`; API misuse and invalid preflight reject. Cancellation propagates through capability calls. Inspection and output values are immutable snapshots; browser-only live resources remain host-owned handles.
115
115
 
116
+ ## Animation
117
+
118
+ A parameter carries up to three bindings: a raw value, an expression, and a keyframe channel. They resolve in exactly one place, `Node.evalParm` in the compatibility engine (`src/nodes/Node.ts`), in the order channel, expression, value. A channel that samples takes precedence and leaves the expression stored but inert, so emptying the channel restores it. Nothing else in Studio may read a prop's `value` directly to obtain an animated parameter; a second resolution path is how an expression badge and a viewer disagree about the same frame.
119
+
120
+ The mechanisms are runtime-owned and the composition is not. `packages/runtime/src/expressions` owns preprocessing (`$F`, `$FF`, `$T`, `$FPS` rewritten to plain identifiers), the evaluation scope, and the maths library; `packages/runtime/src/animation` owns the channel model, sampling, interpolation, serialization, and frame-range walking. Both depend only on contracts, so `cascade run` and Studio animate a dynamic graph through the same code rather than two implementations of Hermite interpolation.
121
+
122
+ `packages/contracts/src/animation.ts` owns the serialized shape. A channel lives inside the node's `props`, not in a separate timeline object, because a parameter's animation belongs to the parameter; a document with a timeline section would need reconciling every time a node was renamed, deleted, or embedded.
123
+
124
+ The deterministic runtime has no parameter animation. A definition-v1 graph owns its own clock, so `cascade run --frames` refuses it explicitly rather than producing a hundred copies of frame 1. Extending animation to the deterministic runtime is a migration step, not a compatibility branch to add in Studio.
125
+
126
+ ## Agent sessions
127
+
128
+ `server/src/agent` is a project-scoped host service, at the same layer as the shell and Python services and under the same policy. `session.ts` owns run lifecycle, `eventBuffer.ts` the bounded transcript, `allowlist.ts` the executable resolution.
129
+
130
+ A run is keyed by agent alias and document, and it is owned by the server rather than by the HTTP request that started it. The request that sends a prompt is registered as the first *reader*; a closed socket detaches that reader and nothing else, and the server keeps draining the child's stdout whether anyone is listening or not, because an unread pipe is what strands an agent. Terminating a run is only ever explicit, and it signals the process group rather than the process, because agents spawn children.
131
+
132
+ The transcript is a ring buffer with absolute monotonic sequence numbers, bounded by both event count and bytes. A reader attaches with a `since` sequence and receives one `attached` event carrying `running`, `replayed`, and `dropped`, then the replay, then the live stream, with nothing awaited in between so a concurrent event cannot land in the gap. Reporting `dropped` matters more than the buffer size: a transcript that silently loses its middle reads as an agent that did something inexplicable.
133
+
134
+ Executables come from the same `commands` allowlist in `cascade.json` that the shell service uses, resolved server-side; a path from the browser is never accepted. Transport is NDJSON over POST rather than SSE, because `EventSource` cannot carry the capability-token header these routes require, and the whole router is absent unless the session allows sensitive capabilities.
135
+
136
+ Document freshness is a watcher, not a poll of the agent. `server/src/graphWatch.ts` watches the project root non-recursively for `.cascade` files and publishes over SSE; `src/editor/graphDocumentWatch.ts` fingerprints the document with volatile metadata stripped so Studio's own save does not return as an external change. Studio reloads the graph in place through the normal candidate/commit path, and declines while there are unsaved edits rather than choosing which side wins.
137
+
116
138
  ## Security
117
139
 
118
140
  Project node modules are trusted code. Capabilities describe support and portability; they are not a sandbox.
@@ -130,6 +152,12 @@ routes. Sensitive routes are enabled on loopback or through explicit exact
130
152
  trusted-host configuration; an untrusted remote bind exposes no capability
131
153
  bootstrap.
132
154
 
155
+ Agent runs are launched under the same policy: the same `commands` allowlist,
156
+ `shell: false`, a project-confined working directory, and its own capability
157
+ token. The child environment additionally drops the variables that would let a
158
+ launched agent reach a chat channel, so an agent started from Studio cannot
159
+ inherit another agent's transport.
160
+
133
161
  ## Compatibility and migration
134
162
 
135
163
  `src/nodes` and `src/engine` remain the Studio compatibility surface while Studio migrates onto the neutral runtime. Runtime-owned algorithms stay in `packages/runtime`; compatibility nodes may adapt them but must not fork their semantics. New features belong at the owning contracts/runtime/host/controller layer.
package/CHANGELOG.md ADDED
@@ -0,0 +1,107 @@
1
+ # Changelog
2
+
3
+ Notable changes to Cascade. Newest first.
4
+
5
+ ## Unreleased
6
+
7
+ ### Node authoring
8
+
9
+ - **A definition-v1 node knows its own id.** `context.nodeId` is the instance's id, which is what lets `cachePath(context.nodeId, '.png')` name a scratch file per instance rather than per module — `field-logo` runs one module three times at three sizes, and a literal path made them overwrite each other. An id is deliberately all a node gets: it names a namespace, not a position, so a v1 node stays a pure function of its own inputs.
10
+ - **A stored value left under `params` is now reported instead of lost.** The deterministic runtime reads `props` and nothing else, so a document converted to definition-v1 without moving its values reverted every one of them to its default and reported success. `preflight()` names them (`runtime/stray-params`), and `cascade check` prints them under their own heading. It is a warning, not an error — such a graph runs perfectly well on its defaults, which is exactly the problem.
11
+ - **One rule for what stops a run.** `PREFLIGHT_WARNING_CODES` lives in the runtime and is read by both the run path and the CLI's `classifyPreflight`, replacing a copy that existed only in the CLI. A duplicated preflight rule is what previously let `validate` and `check` pass graphs `run` then rejected.
12
+
13
+ ### Authoring, continued
14
+
15
+ - **A prop can declare a default expression**, so a node arrives already moving — `sine-oscillator` and `ripple` shipped a static `0` once converted, where their dynamic versions baked in `$T`. It is a *default*: a document that saved a plain number keeps it and does not start animating on load. Applied once per prop, so a deleted expression is not reinstated when a retarget re-runs setup, and visible in the Inspector rather than a hidden fallback.
16
+
17
+ - **A vector prop can declare `min`, `max` and `step`.** All six vector types were typed `never` for those three fields, so the standing "one `vec2`, not two floats" convention silently cost the Inspector its clamp and its drag granularity every time it was followed. One range across all components; matrices stay excluded.
18
+ - **A module with no file is named, with the paths it was looked for.** `prepare()` synthesises a definition from the ports the document saved for any unresolvable module, which keeps legacy nodes loadable and also let a typo validate clean. A warning, because such a node can never run through the deterministic runtime anyway.
19
+ - **The stray-`params` warning covers inputs as well as props** — the case the conversions produce most, since a parameter another node drives must be an input.
20
+
21
+ ### Interface
22
+
23
+ - **Port and wire colours resolve through per-theme tokens.** The palette was thirteen hex values chosen against a black canvas; in light mode gold sat at 1.3:1 against white. Also collapses `DATA_TYPE_COLORS`, which was a second palette with different values for the same families.
24
+ - **A parameter's label reaches its control.** `SelectInput` declared an `id` and never applied it, so every select-shaped parameter's `<label for>` pointed at no element.
25
+
26
+ ### Build
27
+
28
+ - **The CLI build resolves `@cascade/runtime/params`.** `src/nodes/Node.ts` imports `resolvePropBinding` from it, the browser build resolved it through `vite.config.ts`, and the CLI build had no alias — so the CLI, which is what the sketches actually run, was the broken half.
29
+
30
+ ## 0.3.1
31
+
32
+ ### Fixed
33
+
34
+ - **A stored value equal to its default is no longer dropped on save.** A prop declaring a default expression would start animating on reload if you set it *back* to its default: the save filters out any parameter matching its default, so nothing was written and the expression re-applied to a number you had chosen. Shipped in 0.3.0. Diagnosed and fixed by Marcus's Codex session.
35
+
36
+ The lesson is in how it survived three tests. Every proof of "a stored value beats the default expression" used a value obviously *different* from the default — `7` against `2`, `7` against `0`, `0.4` against `0` — because a distant value reads as the stronger test. Only *equal to the default* fails, and the boundary was the case nobody tried. The regression test now saves and reloads, because the fault is in what gets written rather than in what gets read.
37
+
38
+ - **A browser-only node is refused on the run path again.** A change earlier the same day made the static checker's downgrades apply to execution too, so `cascade run` executed a browser node headlessly and failed on an undefined capability instead of reporting that it needs a browser. The run gate is now separate from static classification: only a stranded `params` value is tolerated.
39
+
40
+ - **`cascade run` reports stranded `params` values.** Previously only `cascade check` did, so a run went onto the defaults in silence.
41
+
42
+ ### Added
43
+
44
+ - **A `gpu` capability, stage one of WebGPU support.** One `GPUDevice` per page, shared by every node that declares the capability, with somewhere to keep a compiled pipeline between cooks. No `texture` port yet, so nodes still exchange images and the rendered picture is unchanged — what it removes is each node holding its own device, which is what made two GPU nodes unable to share anything. Design: `PLAN webgpu` in the vault.
45
+
46
+ ## 0.3.0
47
+
48
+ Two features carry the minor bump: an **animation system** and an **agent console**. Everything else is either a consequence of those or a fault they exposed.
49
+
50
+ ### Animation
51
+
52
+ - **A timeline panel** with transport, a playhead, scrubbing and a dope sheet.
53
+ - **Keyframe channels** on parameters — a third binding beside the raw value and the expression, resolved in one place (`Node.evalParm`: channel, then expression, then value). Constant, linear and smooth interpolation.
54
+ - **Houdini's gestures**, because they are the ones people already have in their hands: alt-click a parameter to set a key, ctrl-click to delete one, right-click to open its channel.
55
+ - **Expressions on parameters**, evaluated by the neutral runtime so a headless cook resolves them with the same code Studio does. `$F`, `$FF`, `$T` (zero on frame 1, as in Houdini), `$FPS`, `ch()`, `chs()`, `chv()`, plus the maths library exposed bare — `sin(...)`, not `Math.sin(...)`. Angles are radians.
56
+ - **Offline rendering.** `cascade run` renders a graph cold, `--frames 1-100` writes a sequence, and image nodes rasterise through Skia (`@napi-rs/canvas`, an optional dependency) rather than a browser. Measured against Studio's own render of the same node at a maximum channel difference of 1.
57
+
58
+ ### The agent console
59
+
60
+ - **A coding agent per sketch**, running in the project directory, with a document watcher so the graph picks up what it rewrites without a reload.
61
+ - **It reconnects rather than stranding.** A run belongs to the server, not to the HTTP request, so closing the browser detaches a reader and nothing else. Reopening replays the buffered transcript from a sequence number and rejoins the live stream.
62
+ - Only commands in the project's own `cascade.json` allowlist are launched, and the document is saved before the prompt is handed over.
63
+
64
+ ### Authoring
65
+
66
+ - **A right-click menu on nodes**, a **Definition panel** naming where a node comes from, and the Inspector's parameter list becoming *Parameters*.
67
+ - **Retargeting a node keeps the parameters someone tuned.**
68
+ - **Extract and embed**, both ways: lift a node out of the graph into a file, or pull an external node in to make local edits. Relative imports are rewritten with the module so neither direction breaks.
69
+ - **A generated node reference**, so the built-in library documents itself.
70
+ - Dynamic nodes are no longer called "legacy". Statically inspectable ES modules are the preferred style, and definition-v1 nodes are classified by their definition rather than only by their exports.
71
+ - **Parameters and props are one store.** They were two, which is why expressions and keyframes were unreachable from the `param()` declaration every sketch actually uses.
72
+
73
+ ### Portability
74
+
75
+ - Image nodes ask the host for a drawing surface instead of reaching for `document`, so the same node renders in the browser and headless. `runsOn` defaults to `portable` for an undeclared module rather than `browser`.
76
+ - **`cascade run` executes the standard node library.** It never registered the class-based nodes, so every `cascade.image.*` threw `Unknown Cascade node type` and a graph built in Studio would not render headlessly. The registration lived beside Vite `?raw` imports, which do not resolve under Node.
77
+ - **`cascade.geo.SvgExport` can write from the CLI.** It declares the `assets` capability, which only the browser supplied — so `validate` and `check` both passed on a graph that then refused to run. The Node host provides it through the existing `cascade/io` policy, so writes stay confined to `.cascade-cache/`.
78
+
79
+ - **`validate` and `check` run against the host that would run the graph.** They used to construct a *different* host from `run` — the runner installed `shell` and `assets`, the static commands installed nothing and never called the runtime's preflight — so both printed "passed" on a graph that then refused to run. The static path now runs the same code `run` throws from, and reads what the host provides rather than keeping its own list. An unsatisfiable capability is an error; a node declaring `runsOn: 'browser'` is a warning, because it asserts nothing about the CLI.
80
+
81
+ ### Packaging
82
+
83
+ - **Published as `@field/cascade`, under MIT.** Scaffolded projects alias it, so `cascade` in a project's imports is this package and not the unrelated `cascade` on npm.
84
+ - An installed copy compiles `cascade/shell`; the Studio's browser dependencies are no longer runtime dependencies.
85
+ - Types ship for `cascade/io` and `cascade/net`.
86
+
87
+ ### Interface
88
+
89
+ - Slimmer panel tabs, and a control to fold a group down to a hairline.
90
+ - `View > Focus <panel>` opens a panel that is not there rather than doing nothing, on ⌘1 through ⌘7.
91
+ - Node names and parameter names are drag sources carrying their address, so a path can be dragged into the agent console instead of typed.
92
+ - Text zoom in the agent console on the standard keys, scoped to the panel under the pointer.
93
+
94
+ ### Fixes worth naming
95
+
96
+ Each of these was silent — nothing errored, and the interface looked correct.
97
+
98
+ - **Studio never handed the graph to the engine context**, so `markTimeDependentDirty()` marked nothing and scrubbing the timeline recooked nothing at all. `ch()` paths could never have resolved either.
99
+ - **Every geometry fingerprinted to the constant string `"object:x:"`**, because the fingerprint read `width`/`height`/`size` and a `Geometry` has none of them. The viewer's first raster stayed on screen for the session, so parameter changes appeared to do nothing on any geometry node.
100
+ - **A browser reload sent SIGTERM to the running agent**, mid-edit, every time.
101
+ - **`graph.execute()` was not always a full render**: a node that never cooked was still marked clean, because it "ran" during port discovery with empty inputs. A cold `cascade run` could silently drop its terminal node.
102
+ - **An image drawn without `await image.decode()`** reports the right dimensions and paints nothing.
103
+ - An expression badge that never recomputed, so `$T * 0.5` read `0` at every frame.
104
+
105
+ ## 0.2.22 and earlier
106
+
107
+ Not recorded. This file starts at 0.3.0.
package/DESIGN.md CHANGED
@@ -3,9 +3,9 @@
3
3
  ## Source of truth
4
4
 
5
5
  - Status: Active
6
- - Last refreshed: 2026-08-29
7
- - Primary product surfaces: reusable graph runtime, optional Studio workbench, headless Node/browser hosts, Inspector, Viewer, node ports, project-defined workbenches
8
- - Evidence reviewed: `VISION.md`, `ARCHITECTURE.md`, `spec/CASCADE_SUBNET_AND_SHELL_SPEC.md`, `src/types/coreTypes.ts`, `src/editor/Inspector.svelte`, `src/editor/Viewer.svelte`, `src/editor/components/PortEditor.svelte`, `src/editor/components/typeRenderers.ts`, and the approved plans under `.omx/plans/`
6
+ - Last refreshed: 2026-09-08 (0.3.0)
7
+ - Primary product surfaces: reusable graph runtime, optional Studio workbench, headless Node/browser hosts, Inspector, Viewer, Timeline, agent console, node ports, project-defined workbenches
8
+ - Evidence reviewed: `VISION.md`, `ARCHITECTURE.md`, `CHANGELOG.md`, `spec/CASCADE_SUBNET_AND_SHELL_SPEC.md`, `src/types/coreTypes.ts`, `src/editor/Inspector.svelte`, `src/editor/Viewer.svelte`, `src/editor/panels/TimelinePanel.svelte`, `src/editor/panels/AgentPanel.svelte`, `src/editor/components/PortEditor.svelte`, `src/editor/components/typeRenderers.ts`, `packages/runtime/src/expressions/`, `packages/runtime/src/animation/`, `server/src/agent/`, and the approved plans under `.omx/plans/`
9
9
 
10
10
  ## Brand
11
11
 
@@ -28,7 +28,7 @@
28
28
  ## Information architecture
29
29
 
30
30
  - Primary navigation: canvas selection determines Inspector and Viewer context
31
- - Core screens: Graph, Inspector, Viewer, Logs/diagnostics
31
+ - Core screens: Graph, Inspector, Viewer, Timeline, Definition, Agent console, Logs/diagnostics
32
32
  - Content hierarchy: value preview first, type and shape metadata second, expandable raw structure last
33
33
 
34
34
  ## Design principles
@@ -40,6 +40,10 @@
40
40
  - Project types extend by registration, not central switch statements.
41
41
  - The graph runtime is a product surface independent from Studio. Headless hosts consume it now; Studio's compatibility graph has one scheduler/controller boundary but still requires a deliberate built-in and structural-command migration before it consumes the neutral runtime directly.
42
42
  - Node metadata is deterministic. A literal exported definition declares ports, properties, types, execution locus, and capabilities; `execute` performs computation only.
43
+ - A parameter is one store with three bindings. A value, an expression, and a keyframe channel live on the same parameter and resolve in one place in a fixed order: channel, then expression, then value. Two stores is what 0.3.0 removed — `param()` wrote to one and every binding read the other, so no parameter a sketch actually declared could carry an expression or a key, and nothing errored. A stronger binding hides a weaker one rather than destroying it, so emptying a channel returns the expression that was already there.
44
+ - Time is an expression, not a node. A parameter holding `sin($T) * 40` is the whole of an oscillator, which is why there is no oscillator node and why portable nodes still must not read ambient time or randomness themselves. The variables and the function names are Houdini's, because the people this is for already have them in their hands; the one deliberate divergence is that bare trigonometry is in radians, with `sind`/`cosd`/`tand` provided for formulae carried across.
45
+ - An agent run belongs to the server, not to the browser looking at it. Closing a tab detaches a reader; the transcript is buffered with sequence numbers so reopening replays what was missed and says how much it dropped. The alternative — a run owned by an HTTP request — kills the agent mid-edit on a reload, which is what 0.3.0 fixed.
46
+ - Rendering offline stops at a numbered image sequence. Handing frames to an encoder is a separate decision with its own dependency, and a directory of frames is the input every encoder already takes.
43
47
  - Hosts provide explicit capabilities. File, Python, media, WebGL, and shell access are never ambient runtime assumptions. Provider-specific AI abstractions belong to projects or embedding hosts, not Cascade core.
44
48
  - Neutral-runtime graph loads are atomic. Compatibility Studio collapse/extract are a known migration gap and must move behind transactional structural commands before that guarantee applies to every editor operation.
45
49
  - Tradeoff: geometry editors favor transparent structured editing and previews over specialized CAD interactions in this pass.
@@ -48,9 +52,10 @@
48
52
 
49
53
  - `@cascade/contracts` is the dependency-free source of truth for graph documents, core and namespaced types, deterministic node definitions, diagnostics, run results, capabilities, and schemas.
50
54
  - `@cascade/runtime` is the deterministic environment-neutral graph loader, validator, scheduler, serializer, trigger engine, and hierarchy implementation. It depends only on contracts; dynamic compatibility remains in Studio/CLI during migration.
51
- - The root `cascade` package is the sole published distribution and CLI owner. It exposes `cascade/contracts`, `cascade/contracts/schema`, `cascade/runtime`, `cascade/runtime/node`, `cascade/runtime/browser`, and the compatibility `cascade/shell` entry point.
55
+ - The root `cascade` package is the sole published distribution and CLI owner. It exposes `cascade/contracts`, `cascade/contracts/schema`, `cascade/runtime`, `cascade/runtime/node`, `cascade/runtime/browser`, `cascade/runtime/expressions`, `cascade/runtime/animation`, `cascade/runtime/definition/extract`, `cascade/io`, `cascade/net`, `cascade/stage`, and the compatibility `cascade/shell` entry point.
52
56
  - Node and browser hosts inject only the capabilities they support. Browser-only WebGL and server-only file, Python, and shell capabilities make portability constraints explicit before execution.
53
57
  - The Studio owns canvas/view classes and a single `StudioGraphController`; its compatibility `Graph`/`Node` engine is still being reduced. Custom frontends and backend services use the neutral runtime directly and do not load editor code.
58
+ - **Web technology is the default renderer, and Python is the exception.** Canvas 2D, WebGL and WebGPU first; reach for a Python stage only for work that genuinely cannot be pushed to the browser — exotic ML libraries and the like. Marcus's ruling, 2026-09-07: *"Cascade should prefer web tech canvas/webgpu to keep things smooth and fast. only use Python for exotic ML libraries and similar things that can't be easily pushed to the browser."* The reason is interaction rather than taste: a Python stage costs a process spawn and a round trip per cook, which is invisible on one still frame and fatal to dragging a parameter or playing an animation. It also tends to be more faithful, not less — a gradient drawn with `createRadialGradient` is the same primitive the source artwork carries, where a per-pixel reimplementation is a guess about what that primitive means.
54
59
  - Python interoperability remains a host capability for exotic libraries and compute stages. Exchange values cross explicit typed node boundaries rather than leaking Python process details into the neutral runtime.
55
60
  - Internal packages remain two workspaces until independent versioning or external consumption justifies separate publication. More npm packages are not a goal by themselves.
56
61
 
package/README.md CHANGED
@@ -11,11 +11,34 @@ Created by Marcus Wendt at [FIELD.IO](https://www.field.io).
11
11
  - The same runtime in Studio, a Node service, or a UI-free browser application.
12
12
  - Project-native `.cascade` files, assets, presets, and Git-backed version history.
13
13
  - Nested subnets with persistent hierarchy.
14
+ - Expressions on parameters, with Houdini's variables and functions: `$F`, `$FF`, `$T`, `$FPS`, and `ch()` to read another parameter.
15
+ - Keyframe channels on parameters, edited through a timeline panel with transport, playhead, scrubbing, and a dope sheet.
16
+ - Offline rendering with `cascade run`, including `--frames 1-100` for a numbered image sequence, rasterised through Skia rather than a browser.
17
+ - A coding-agent console per project, running in the project directory, with a watcher that picks up the files it rewrites without a reload.
14
18
  - Explicit browser and server capabilities for WebGL, files, media, Python, and allowlisted shell commands.
15
- - A Svelte Studio with an infinite canvas, Inspector, Viewer, and project workbench.
19
+ - A Svelte Studio with an infinite canvas, Inspector, Viewer, timeline, and project workbench.
16
20
 
17
21
  Cascade separates the generative algorithm from the platform. Custom nodes describe stages of a pipeline; the runtime schedules and validates them; hosts supply environment-specific capabilities; Studio is only one possible frontend.
18
22
 
23
+ ## Install
24
+
25
+ Cascade needs Node.js 22.13 or newer.
26
+
27
+ ```bash
28
+ npm install -g @field/cascade
29
+ cascade --version
30
+ ```
31
+
32
+ That installs the `cascade` command: `cascade new` scaffolds a project, `cascade <directory>` opens it in Studio, and `cascade run` renders a graph headlessly.
33
+
34
+ Offline image rendering uses Skia through `@napi-rs/canvas`, an optional dependency of about 26 MB. It installs by default; if you skipped optional dependencies, add it yourself:
35
+
36
+ ```bash
37
+ npm install -g @napi-rs/canvas
38
+ ```
39
+
40
+ Inside a project, `cascade` is aliased to this package, so a node importing `cascade/contracts` or `cascade/runtime` resolves here rather than to the unrelated `cascade` package on npm.
41
+
19
42
  ## Develop Cascade
20
43
 
21
44
  Cascade development requires Node.js 22.13 or newer. The repository uses
@@ -64,6 +87,7 @@ A project is an ordinary directory or Git repository:
64
87
  my-artwork/
65
88
  ├── cascade.json # optional host policy and settings
66
89
  ├── index.cascade # default graph
90
+ ├── AGENTS.md # written by `cascade new`: what an agent working here needs
67
91
  ├── nodes/
68
92
  │ └── Multiply/
69
93
  │ └── index.ts # project.Multiply
@@ -83,6 +107,75 @@ See [Project authoring](doc/PROJECT_AUTHORING.md) for project layout, nodes, `.c
83
107
 
84
108
  Projects may also provide trusted, dockable Studio panels as plain TypeScript modules under `panels/`. These extensions remain outside graph documents and are never loaded by headless execution.
85
109
 
110
+ ## Animation
111
+
112
+ A parameter can hold three things, and they resolve in one place in this order: a **keyframe channel** first, then an **expression**, then the raw **value**. A channel therefore takes over from an expression without erasing it: the expression stays on the parameter, inert, and comes back when the channel is emptied.
113
+
114
+ Expressions use Houdini's vocabulary, because that is the vocabulary the people using this already have:
115
+
116
+ | | |
117
+ | --- | --- |
118
+ | `$F` | frame, integer |
119
+ | `$FF` | frame, fractional |
120
+ | `$T` | seconds, zero on frame 1, as `($FF - 1) / $FPS` |
121
+ | `$FPS` | frame rate |
122
+ | `ch("NODE/parm")`, `ch("./parm")` | read another parameter; `chs()` for a string, `chv()` for a vector |
123
+
124
+ The maths library is in scope bare, so it is `sin(x)` rather than `Math.sin(x)`: `sin cos tan asin acos atan atan2 sinh cosh tanh sqrt cbrt pow exp log log2 log10 hypot abs sign floor ceil round trunc min max` and the constants `PI`, `TAU`, `E`. **Angles are radians**, unlike Houdini, and `sind`, `cosd`, `tand`, `radians()` and `degrees()` are there for formulae carried over from it. Also available: `fit`, `fit01`, `clamp`, `lerp`, `smooth`, `noise`, `random`, `padzero`, `opexist` and `opinput`.
125
+
126
+ So the answer to anything time-based is usually an expression rather than a node. `sin($T) * 40` in a rotation parameter is the whole of an oscillator, and there is no oscillator node.
127
+
128
+ Keyframes are edited with Houdini's gestures on a parameter in the Inspector: **alt-click** sets a key at the playhead, **ctrl-click** deletes the key on the current frame, and **right-click** opens the Timeline focused on that channel. The diamond beside the parameter is the same thing for anyone not using a modifier, and it toggles. Interpolation is per key, applying to the segment to its right: `constant`, `linear`, or `smooth` (the default, a Hermite curve with automatic tangents, flat at the first and last key). Outside the keyed range a channel holds its end values rather than extrapolating.
129
+
130
+ Channels serialize into the node's `props`, beside the value and the expression:
131
+
132
+ ```json
133
+ "props": {
134
+ "angle": {
135
+ "value": 12,
136
+ "channel": { "keys": [{ "frame": 1, "value": 0, "interpolation": "linear" }, { "frame": 48, "value": 360 }] }
137
+ }
138
+ }
139
+ ```
140
+
141
+ The Timeline panel supplies transport, a playhead, scrubbing, and a dope sheet. Playback is wall-clock driven and drops frames rather than falling behind. The frame range and loop setting are panel state and are not saved with the document.
142
+
143
+ Rendering a sequence offline needs no browser:
144
+
145
+ ```bash
146
+ cascade run index.cascade --frames 1-100
147
+ cascade run index.cascade --frames 1-100x2 --fps 25 --out frames
148
+ cascade run index.cascade --frames 42 --entry-node logo-1024
149
+ ```
150
+
151
+ Frames are written as `<out>/<node id>.<frame>.<ext>`, zero-padded to at least four digits, into `renders/` unless `--out` says otherwise. One rule regardless of how many outputs the graph has, because a graph writing the same mark at three sizes from one cook is the normal case. Every image output with nothing downstream of it is saved, or the outputs of `--entry-node` when one is named. Cascade stops at the sequence; a directory of numbered frames is what every encoder already takes.
152
+
153
+ Drawing offline goes through Skia rather than a browser. The optional `@napi-rs/canvas` supplies `OffscreenCanvas`, `Image`, `Path2D` and the rest as globals in the Node process, deliberately not `document`, since that is how a node detects its host. So a node that draws on an `OffscreenCanvas` needs no separate headless implementation, and the built-in `cascade.image.*` library rasterises through Skia in Node too, so a graph built from it renders headlessly with no browser involved. A run without the renderer installed still works for graphs that draw nothing, and says what is missing if a cook fails in a canvas-shaped way.
154
+
155
+ `--frames` renders dynamic graphs. A definition-v1 graph runs through the deterministic runtime, which owns its own clock and has no parameter animation, and `cascade run` refuses the flag rather than rendering frame 1 a hundred times.
156
+
157
+ ## The agent console
158
+
159
+ Studio has a panel that runs a coding agent inside the project directory, so the agent edits the same `nodes/` and `.cascade` files the graph is loaded from. The document is saved before the prompt is sent, and a watcher on the project's `.cascade` files reloads the graph in place when the agent rewrites it — no page reload, and Studio declines the reload rather than discarding unsaved edits.
160
+
161
+ Nothing is launchable until the project says what may be launched. The agent command comes from the same `commands` allowlist `cascade/shell` uses, in the project's `cascade.json`, and a scaffolded project ships without one:
162
+
163
+ ```json
164
+ {
165
+ "name": "My Artwork",
166
+ "commands": { "claude": "claude" },
167
+ "agent": { "claude": { "args": ["--permission-mode", "acceptEdits"] } }
168
+ }
169
+ ```
170
+
171
+ `commands` maps an alias to an executable: an absolute path, a `~`-relative path, or a bare name found on `PATH`. Paths supplied by the browser are never accepted. `agent.<alias>.args` adds fixed flags for that agent; `claude` defaults to `--permission-mode acceptEdits`, and an explicit empty array launches it bare.
172
+
173
+ A run belongs to the server rather than to the HTTP request that started it. Closing the browser detaches a reader and leaves the agent working; reopening the panel replays the buffered transcript and rejoins the live stream. The buffer is bounded (2,000 events or 4 MB, oldest evicted), a run has a fifteen-minute timeout, and cancelling terminates the agent's whole process group because agents spawn children. One run at a time per document; a second prompt is refused rather than queued.
174
+
175
+ The console is a sensitive route. It is available on loopback or in an explicitly enabled trusted-host session, requires a process-lifetime capability token, and the agent process is started with `shell: false` and without the environment variables that would let it reach a chat channel.
176
+
177
+ Node and parameter names in the graph and Inspector are drag sources carrying the expression address that names them, `TRANSFORM` for a node and `TRANSFORM/tx` for one of its parameters, so a path can be dragged into the console instead of retyped.
178
+
86
179
  ## Deterministic custom nodes
87
180
 
88
181
  A node exports a literal interface and a typed computation:
@@ -126,7 +219,7 @@ host's executor, so the graph stays portable and contains no backend selector.
126
219
  This is the intended pattern for media transforms such as crop or resize; use
127
220
  shared parity tests to keep their outputs and metadata aligned.
128
221
 
129
- Existing dynamic nodes continue through Cascade's current Studio/CLI compatibility engine while projects migrate. The headless runtime accepts deterministic definitions only; malformed definitions never fall back to dynamic execution.
222
+ There is a second node style, and it is not deprecated. A **dynamic** module exports `execute(node, graph)` and declares its ports and parameters imperatively inside it, as `node.param('size', 1024, { min: 16, max: 4096 })`, running once for discovery and again on every cook. Studio cooks both styles and a Studio graph may mix them: it dispatches on what a compiled module exports, building a definition-v1 node's ports from its literal and calling `execute` with a real context, and calling a dynamic module as `execute(node, graph)`. `cascade run --frames` renders dynamic graphs, and the deterministic runtime cooks definition-v1 ones, which are what `cascade check` inspects statically. A malformed definition never falls back to dynamic execution — it is reported. A project's own `AGENTS.md`, written by `cascade new`, states which style that project expects.
130
223
 
131
224
  Provider integrations are project code, not Cascade infrastructure. A project
132
225
  can call a remote service from a Studio/browser node, invoke Python or an
@@ -162,7 +255,7 @@ embedded application a stable public boundary instead of requiring it to know
162
255
  internal node IDs.
163
256
 
164
257
  The runtime natively provides `Subnet`, `Input`, `Output`, `Switch`, `Merge`,
165
- `Select`, seeded `Random`, and scalar `Remap`. These reserved core modules need
258
+ `Select`, `Null`, seeded `Random`, and scalar `Remap`. These reserved core modules need
166
259
  no project registration and run identically in Node and browser hosts. The
167
260
  runtime also supports inspection, presets, triggers, targeted or full runs,
168
261
  cancellation, event subscriptions, output retrieval, and disposal. Hosts
@@ -180,11 +273,12 @@ second set of node algorithms.
180
273
  ```text
181
274
  packages/contracts/ dependency-free public types and schemas
182
275
  packages/runtime/ environment-neutral graph runtime and adapters
183
- src/editor/ optional Svelte Studio
184
- src/nodes/ current built-in and legacy node implementation
276
+ src/editor/ optional Svelte Studio, including the timeline and agent panels
277
+ src/nodes/ current class-based built-in node libraries (core, geo, image)
185
278
  src/engine/ current compatibility services
186
- server/ project-scoped Node host and transports
187
- scripts/ package build and release verification tooling
279
+ src/cli/ the `cascade` command: new, validate, check, inspect, run, Studio
280
+ server/ project-scoped Node host and transports, including the agent session host
281
+ scripts/ package build, node-reference generation, and release verification tooling
188
282
  tests/ behavior, workflow, security, and performance tests
189
283
  spec/ accepted feature specifications
190
284
  doc/ authoring guides and implementation plans
@@ -220,8 +314,10 @@ trusted-host session; an untrusted remote bind does not expose them.
220
314
 
221
315
  ## More documentation
222
316
 
317
+ - [Changelog](CHANGELOG.md)
223
318
  - [Agent guide](AGENTS.md)
224
319
  - [Project authoring](doc/PROJECT_AUTHORING.md)
320
+ - [Node reference](doc/NODE_REFERENCE.md) — every built-in node and its declared ports, generated from the definitions
225
321
  - [Design source of truth](DESIGN.md)
226
322
  - [Architecture](ARCHITECTURE.md)
227
323
  - [Subnet and shell specification](spec/CASCADE_SUBNET_AND_SHELL_SPEC.md)