@field/cascade 0.10.0 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +25 -5
- package/ARCHITECTURE.md +5 -4
- package/CHANGELOG.md +382 -0
- package/README.md +1 -2
- package/dist/assets/AgentPanel-1JugFjE4.css +1 -0
- package/dist/assets/AgentPanel-BDL5MddA.js +5 -0
- package/dist/assets/{CodeEditor-DSZBpzdy.js → CodeEditor-C-xYG-Wn.js} +120 -120
- package/dist/assets/CodePanel-CtXd50m6.js +2 -0
- package/dist/assets/CookInfoPanel-B3NMhKJF.css +1 -0
- package/dist/assets/CookInfoPanel-DG4_TRsX.js +2 -0
- package/dist/assets/CustomNodeDialog-CHfZUs-r.js +1 -0
- package/dist/assets/DefinitionNode-CaRTXQ_b.js +1 -0
- package/dist/assets/DefinitionPanel-C3CxZY9r.js +1 -0
- package/dist/assets/DefinitionPanel-VNLnAiQD.css +1 -0
- package/dist/assets/OpenGraphDialog-BLdr6k_q.js +1 -0
- package/dist/assets/ProjectSettings-ZnvmIAir.js +2 -0
- package/dist/assets/{SettingsDialog-p25h3tj0.js → SettingsDialog-CBX3Wa87.js} +1 -1
- package/dist/assets/TimelinePanel-CDeR0arY.css +1 -0
- package/dist/assets/TimelinePanel-CWgnFH1e.js +1 -0
- package/dist/assets/{VersionHistoryDialog-DzFLUotB.js → VersionHistoryDialog-BOSoW1R-.js} +1 -1
- package/dist/assets/assert-BO2_erPD.js +1 -0
- package/dist/assets/assert-hvykGCnP.js +1 -0
- package/dist/assets/builder-BmBKcDix.js +1 -0
- package/dist/assets/camera-DFF7YA3F.js +1 -0
- package/dist/assets/checks-DAvyO9Gf.js +1 -0
- package/dist/assets/contactSheet-N1NcaWxw.js +2 -0
- package/dist/assets/{core-BICrEt0y.js → core-DB-MkGjj.js} +3 -4
- package/dist/assets/coreTypes-DtnDATZ7.js +1 -0
- package/dist/assets/{cssMode-D7TXw57I.js → cssMode-DPVckSOc.js} +1 -1
- package/dist/assets/definitions-B227e7f_.js +1 -0
- package/dist/assets/definitions-D0WOUJd7.js +1 -0
- package/dist/assets/{editor.api-DvkCO3ko.js → editor.api-Dkutg_Z1.js} +1 -1
- package/dist/assets/expressions-X-Z-GrgW.js +5 -0
- package/dist/assets/file-DAL6IfjV.js +1 -0
- package/dist/assets/filters-D3k1LRMa.js +1 -0
- package/dist/assets/{freemarker2-BA2lxsF1.js → freemarker2-CLiLKk3B.js} +1 -1
- package/dist/assets/generators-BhDxRAFC.js +1 -0
- package/dist/assets/geo-BsMImJIJ.js +1 -0
- package/dist/assets/geo-C4YngDob.js +2 -0
- package/dist/assets/{handlebars-BcDiyPd8.js → handlebars-CcD3aMeL.js} +1 -1
- package/dist/assets/{html-C7O4TyhX.js → html-DvZWGfqb.js} +1 -1
- package/dist/assets/{htmlMode-WaDGF3Mj.js → htmlMode-DUD3VaoN.js} +1 -1
- package/dist/assets/image-0SfH-snh.js +1 -0
- package/dist/assets/image-Ck-RdGU4.js +2 -0
- package/dist/assets/index-BWnrmIB9.js +137 -0
- package/dist/assets/index-DIaxEONI.css +1 -0
- package/dist/assets/io-BGmt2g0D.js +1 -0
- package/dist/assets/{javascript-CpIEK7tz.js → javascript-Cap2ahgh.js} +1 -1
- package/dist/assets/{jsonMode-B2FltW0f.js → jsonMode-B0uLjp9C.js} +1 -1
- package/dist/assets/{liquid-CN6FFHZE.js → liquid-CnxwhfLn.js} +1 -1
- package/dist/assets/{lspLanguageFeatures-QhV9XtLp.js → lspLanguageFeatures-Dn9hlKX6.js} +1 -1
- package/dist/assets/math-GeULS-sk.js +1 -0
- package/dist/assets/{mdx-DE89pafV.js → mdx-BV3jbrVa.js} +1 -1
- package/dist/assets/pop-KMF_qloi.js +1 -0
- package/dist/assets/preload-helper-Czpn1I53.js +1 -0
- package/dist/assets/{python-ByTllTB9.js → python-CGyFk4XK.js} +1 -1
- package/dist/assets/{razor-USYlRRCE.js → razor-CQfzX3-N.js} +1 -1
- package/dist/assets/remap-5SjXQU-q.js +1 -0
- package/dist/assets/renderMarkdown-ork0Oir7.js +67 -0
- package/dist/assets/studioImages-CAyH-0Pl.js +1 -0
- package/dist/assets/text-ZpX7qNkx.js +2 -0
- package/dist/assets/time-P_OiBhcf.js +1 -0
- package/dist/assets/{tsMode-BXpN8iUz.js → tsMode-BOeXzO9H.js} +1 -1
- package/dist/assets/{typescript-DInBjGQQ.js → typescript-J1SnE_Tu.js} +1 -1
- package/dist/assets/{workers-C7Ye5B7D.js → workers-CYwwTiVF.js} +1 -1
- package/dist/assets/{xml-U0Uocqyo.js → xml-DnPRNzK9.js} +1 -1
- package/dist/assets/{yaml-CBw_PVYf.js → yaml-BmmYBNa9.js} +1 -1
- package/dist/cli/DefinitionNode-P7AA4TAU.js +15 -0
- package/dist/cli/Graph-OB6EWSGL.js +2975 -0
- package/dist/cli/assert-GROGQNCQ.js +25 -0
- package/dist/cli/checks-3WB44XBL.js +189 -0
- package/dist/cli/chunk-2JEU3MGB.js +1125 -0
- package/dist/cli/chunk-2KXB47FP.js +115 -0
- package/dist/cli/chunk-3RXL5GHB.js +816 -0
- package/dist/cli/chunk-53LGFPWX.js +138 -0
- package/dist/cli/chunk-5ORUQ6ES.js +267 -0
- package/dist/cli/chunk-73S4JZXK.js +336 -0
- package/dist/cli/chunk-7QE2ZNSN.js +121 -0
- package/dist/cli/chunk-BP7UKF36.js +376 -0
- package/dist/cli/chunk-BPODRH52.js +201 -0
- package/dist/cli/chunk-BZ63XQ3I.js +1301 -0
- package/dist/cli/chunk-CKS2FQWR.js +89 -0
- package/dist/cli/chunk-CO22XHNG.js +593 -0
- package/dist/cli/chunk-CTPLRSWF.js +2197 -0
- package/dist/cli/chunk-DEQSEFFH.js +153 -0
- package/dist/cli/chunk-EU4ZFZ7Y.js +122 -0
- package/dist/cli/chunk-EZRWJ4G2.js +104 -0
- package/dist/cli/chunk-FWMSGKEH.js +71 -0
- package/dist/cli/chunk-G23A2BZZ.js +705 -0
- package/dist/cli/chunk-GRIN2DDP.js +1465 -0
- package/dist/cli/chunk-HFHWYL55.js +139 -0
- package/dist/cli/chunk-HSNOBIVT.js +263 -0
- package/dist/cli/chunk-ICH7N5PN.js +26 -0
- package/dist/cli/chunk-J7VK7MNF.js +418 -0
- package/dist/cli/chunk-JVOCK5W4.js +83 -0
- package/dist/cli/chunk-KBGUO27Q.js +15 -0
- package/dist/cli/chunk-KH4MUBKF.js +1551 -0
- package/dist/cli/chunk-L6B6FUQP.js +330 -0
- package/dist/cli/chunk-LTXWBXOM.js +438 -0
- package/dist/cli/chunk-MI5JLQYA.js +220 -0
- package/dist/cli/chunk-MP653A5O.js +3766 -0
- package/dist/cli/chunk-NJFMDXHR.js +320 -0
- package/dist/cli/chunk-OM6BEME6.js +267 -0
- package/dist/cli/chunk-OWWBYLHC.js +136 -0
- package/dist/cli/chunk-QQPA2ZHH.js +273 -0
- package/dist/cli/chunk-RUU3YWXU.js +99 -0
- package/dist/cli/chunk-RXDIMXN5.js +98 -0
- package/dist/cli/chunk-ST6LROUG.js +267 -0
- package/dist/cli/chunk-TJW67QSI.js +225 -0
- package/dist/cli/chunk-WD62JIP6.js +118 -0
- package/dist/cli/chunk-WGKPJZDY.js +364 -0
- package/dist/cli/chunk-X4WKAOQP.js +634 -0
- package/dist/cli/chunk-X566FWBV.js +156 -0
- package/dist/cli/chunk-YH637PDL.js +317 -0
- package/dist/cli/chunk-ZL4JTG5Y.js +37 -0
- package/dist/cli/chunk-ZSCUZE6I.js +112 -0
- package/dist/cli/cliCommands-JJN5Z3ZC.js +3470 -0
- package/dist/cli/commands-PHYFYA3F.js +1374 -0
- package/dist/cli/contactSheet-2PQVRUFW.js +11 -0
- package/dist/cli/definitions-BPOPW52L.js +32 -0
- package/dist/cli/doctorCommand-ORWQRNYP.js +106 -0
- package/dist/cli/edit-S5KEQ5RA.js +28 -0
- package/dist/cli/editorCommands-NFR32JOY.js +41 -0
- package/dist/cli/encode-ICPEPMH5.js +17 -0
- package/dist/cli/file-3HTQHPYQ.js +41 -0
- package/dist/cli/filters-6ODFWYK4.js +379 -0
- package/dist/cli/frames-LSAUBZ7P.js +16 -0
- package/dist/cli/generators-YU7GB4MD.js +210 -0
- package/dist/cli/geo-6ZPGKZRU.js +43 -0
- package/dist/cli/graphCommands-EOUOBOIJ.js +184 -0
- package/dist/cli/graphFile-YTGJB5F5.js +292 -0
- package/dist/cli/headlessCanvas-ENRHC7GS.js +16 -0
- package/dist/cli/helpCommand-PULH6ZYB.js +157 -0
- package/dist/cli/image-TXELZZK5.js +55 -0
- package/dist/cli/index.js +100 -30756
- package/dist/cli/math-RSYRQ3C6.js +16 -0
- package/dist/cli/mcp-JVZFQOEX.js +164 -0
- package/dist/cli/parity-X6DEH2DV.js +334 -0
- package/dist/cli/pop-B7SIIYKN.js +39 -0
- package/dist/cli/project-VYGIA7JX.js +12 -0
- package/dist/cli/projectDefinition-F3D7UIHJ.js +27 -0
- package/dist/cli/projectRuntime-2Q4JKEH3.js +55 -0
- package/dist/cli/registerStandardNodes-BXJCB6E4.js +744 -0
- package/dist/cli/render-PT4MWI6U.js +438 -0
- package/dist/cli/runCommand-HNPQVZMS.js +198 -0
- package/dist/cli/runner-5XPXVW7J.js +43 -0
- package/dist/cli/runtime/image.ts +302 -0
- package/dist/cli/runtime/io.ts +78 -0
- package/dist/cli/runtime/stage.ts +4 -1
- package/dist/cli/share-MHF6TNRK.js +166 -0
- package/dist/cli/stageCache-TY2BFZJR.js +15 -0
- package/dist/cli/stageRunner-KY2EJP7X.js +10 -0
- package/dist/cli/text-MB3EKR4H.js +126 -0
- package/dist/cli/verbs-PI2JH5FI.js +19 -0
- package/dist/cli/webBuild-WPPMGGW7.js +44 -0
- package/dist/contracts/capabilities.d.ts +18 -1
- package/dist/contracts/color.d.ts +8 -0
- package/dist/contracts/color.js +23 -0
- package/dist/contracts/definition.d.ts +10 -0
- package/dist/contracts/documentFormat.d.ts +18 -0
- package/dist/contracts/documentFormat.js +195 -0
- package/dist/contracts/editor.d.ts +17 -0
- package/dist/contracts/index.d.ts +1 -0
- package/dist/contracts/index.js +1 -0
- package/dist/contracts/schema.d.ts +1 -1
- package/dist/contracts/schema.js +1 -0
- package/dist/contracts/validate.js +11 -1
- package/dist/contracts/values.d.ts +24 -0
- package/dist/image.d.ts +156 -0
- package/dist/image.js +161 -0
- package/dist/index.html +8 -2
- package/dist/io.d.ts +29 -0
- package/dist/io.js +40 -0
- package/dist/player/app.js +3 -2
- package/dist/player/editor.js +35 -9
- package/dist/player/editorEngine.js +3 -2
- package/dist/runtime/builtins/assert/checks.d.ts +18 -0
- package/dist/runtime/builtins/assert/checks.js +207 -0
- package/dist/runtime/builtins/assert/definitions.d.ts +130 -0
- package/dist/runtime/builtins/assert/definitions.js +54 -0
- package/dist/runtime/builtins/assert/index.d.ts +152 -0
- package/dist/runtime/builtins/assert/index.js +36 -0
- package/dist/runtime/builtins/image/contactSheet.d.ts +64 -0
- package/dist/runtime/builtins/image/contactSheet.js +150 -0
- package/dist/runtime/builtins/image/definitions.d.ts +950 -0
- package/dist/runtime/builtins/image/definitions.js +343 -0
- package/dist/runtime/builtins/image/file.d.ts +8 -0
- package/dist/runtime/builtins/image/file.js +37 -0
- package/dist/runtime/builtins/image/filters.d.ts +8 -0
- package/dist/runtime/builtins/image/filters.js +394 -0
- package/dist/runtime/builtins/image/generators.d.ts +7 -0
- package/dist/runtime/builtins/image/generators.js +144 -0
- package/dist/runtime/builtins/image/index.d.ts +952 -0
- package/dist/runtime/builtins/image/index.js +44 -0
- package/dist/runtime/builtins/image/io.d.ts +100 -0
- package/dist/runtime/builtins/image/io.js +142 -0
- package/dist/runtime/builtins/image/planar.d.ts +55 -0
- package/dist/runtime/builtins/image/planar.js +274 -0
- package/dist/runtime/builtins/image/simplex.d.ts +22 -0
- package/dist/runtime/builtins/image/simplex.js +92 -0
- package/dist/runtime/builtins/image/text.d.ts +4 -0
- package/dist/runtime/builtins/image/text.js +141 -0
- package/dist/runtime/builtins/index.d.ts +3 -0
- package/dist/runtime/builtins/index.js +7 -0
- package/dist/runtime/gpu.d.ts +25 -0
- package/dist/runtime/gpu.js +40 -0
- package/dist/runtime/runtime.d.ts +2 -0
- package/dist/runtime/runtime.js +268 -44
- package/dist/runtime/types.d.ts +30 -0
- package/dist/stage.js +4 -1
- package/doc/CLI_REFERENCE.md +91 -2
- package/doc/EDITOR_EXPORT.md +37 -2
- package/doc/MCP.md +18 -1
- package/doc/NODE_REFERENCE.md +424 -10
- package/doc/PROJECT_AUTHORING.md +222 -7
- package/package.json +19 -4
- package/dist/assets/CodePanel-sY6sZa7B.js +0 -2
- package/dist/assets/CustomNodeDialog-BRKnqZhp.js +0 -1
- package/dist/assets/ExportDialog-C7dVGua4.js +0 -358
- package/dist/assets/ExportDialog-DOq0flMV.css +0 -1
- package/dist/assets/OpenGraphDialog-ZsgL-iwr.js +0 -1
- package/dist/assets/ProjectSettings-Cw17nNe3.js +0 -2
- package/dist/assets/definitions-DUww2Rt-.js +0 -1
- package/dist/assets/geo-C2RHOVHC.js +0 -1
- package/dist/assets/image-DR4oeyJB.js +0 -2062
- package/dist/assets/index-7vae6YFC.css +0 -1
- package/dist/assets/index-DyvDC6pS.js +0 -219
- package/dist/assets/math-DaDEgWKt.js +0 -1
- package/dist/assets/pop-pLH7L0PW.js +0 -1
package/AGENTS.md
CHANGED
|
@@ -35,10 +35,12 @@ my-project/
|
|
|
35
35
|
└── tsconfig.json
|
|
36
36
|
```
|
|
37
37
|
|
|
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.
|
|
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 render <graph.cascade> --node <id>` renders an animation across worker processes (`--workers N`, default cores minus a few) and encodes a half-size H.264 preview (`--final` full size, `--prores` a master); a node must write each frame to its own file (frame in the name, or `saveImage(…, cachePath(…))`), and one that writes a fixed path is refused before the render starts. `cascade projects` shows or sets the default projects directory.
|
|
39
39
|
|
|
40
40
|
Never hand-edit a `.cascade` file. Change graphs with `cascade node add|rm`, `connect`, `disconnect`, `set` and `subnet create|move|dissolve` (or `cascade edit` for a batch); they check the edit like `cascade check`, keep the diff minimal and lay new nodes out. Every command takes `--json`, and `cascade mcp` serves the same operations as MCP tools. Read `doc/CLI_REFERENCE.md` and `doc/MCP.md`.
|
|
41
41
|
|
|
42
|
+
For agents working headlessly: `cascade help node <type>` prints a node's ports and description from the installed package; `cascade run <graph> --out <dir> [--out-node <id>]` saves image outputs as `<node>[.<port>].<ext>` (and `run --json` lists them in `data.images`); `cascade.assert.*` nodes fail a run with a readable message; what nodes and stages print is in the run trace under `.cascade/runs/`; `--env NAME=value` replaces a shell prefix; `cascade doctor --fix` builds the Python environment a project declares in `cascade.json` `python`; every MCP tool takes `project` to work in another directory. To have Marcus choose a variant on his phone: `cascade ladder <graph> <node> --name <name> --seeds a,b,c --cairn <Substrate vault>/<space>/_attachments/ladders/<id> --json`, post `data.cairnChoice` (a `cairn:choice` fence, at most 8 pictures) in the Cairn chat as is, and when "Picked: <label> (pick:<id>:<n>)" comes back run `cascade pick <id> <n> --project <project>` and reply with that locked image (`doc/CLI_REFERENCE.md`, "Picking a ladder variant in Cairn").
|
|
43
|
+
|
|
42
44
|
`cascade build <graph.cascade> --out <new-directory>` exports a static browser
|
|
43
45
|
player. Use repeatable `--asset <project-relative-file>` for inputs whose paths
|
|
44
46
|
are computed in code. Read `doc/WEB_PLAYER.md` before exporting: server stages
|
|
@@ -219,18 +221,36 @@ type system knows what a `vec2` is; nothing tells it that `offset_x` and
|
|
|
219
221
|
- Studio uses `StudioGraphController` over its compatibility `Graph`/`Node` engine and reuses runtime mechanisms. Custom frontends consume the neutral runtime directly; full Studio migration remains incomplete.
|
|
220
222
|
- User modules are trusted project code, not a sandbox. Static definitions improve tooling and portability, not isolation.
|
|
221
223
|
- Keep legacy support inside the existing Studio/CLI compatibility engine; do not spread compatibility branches into the deterministic runtime.
|
|
224
|
+
- `src/engine`, `src/nodes`, `src/cli` and `src/player` never import the Studio editor (`src/editor`, `src/App.svelte`); the server never imports `src/`. `tests/architecture-layers.test.ts` enforces these and the two above, and allows no new import cycle.
|
|
222
225
|
|
|
223
226
|
## Change workflow
|
|
224
227
|
|
|
225
|
-
Use Node.js 22.13 or newer. Cascade compiles with TypeScript 6
|
|
226
|
-
the
|
|
228
|
+
Use Node.js 22.13 or newer. Cascade compiles with TypeScript 6 (the
|
|
229
|
+
`typescript` dependency: the CLI and server use its JS API, and svelte-check
|
|
230
|
+
runs on it). The plain-tsc gates in `scripts/verify.mjs` run on TypeScript 7
|
|
231
|
+
(the `typescript7` devDependency), which reports the same errors about seven
|
|
232
|
+
times faster; keep code clean under both.
|
|
227
233
|
|
|
228
234
|
1. Add or update a focused regression test.
|
|
229
235
|
2. Make the smallest change at the owning layer.
|
|
230
236
|
3. Prefer deletion or an existing utility over another wrapper.
|
|
231
|
-
4.
|
|
237
|
+
4. While iterating, run the focused test or `npm run verify:fast` (types for src, tests and server, the node reference, the architecture ratchets, and `vitest related` for the files you changed). Before you commit, run `npm run verify`: it builds the packages, then runs the typechecks, `check:packages`, svelte-check, the whole vitest suite, the package tests and the Studio bundle budget, stopping at the first failure. It never touches `dist/`. Run `npm run build` and `npm run build:cli` only when you need the built CLI or Studio; both replace `dist/` only on success, so a failed build leaves the previous CLI working. `build:cli` skips the package build when every `packages/*/dist` is newer than its sources (`-- --force` rebuilds them). For a runtime or CLI change, `npx vitest run --project node` runs the suite without the Studio component tests. `git push` runs a pre-push gate (`.githooks/pre-push`, installed by `npm install`): when the pushed commits touch code it runs `verify --push` (the fast gate, svelte-check alongside, and the tests related to the pushed files, leaving out `tests/performance/`), 10–14 s. It checks the working tree, so someone else's uncommitted edit can fail it; `git push --no-verify` skips it once, and it does not replace `npm run verify` before you commit.
|
|
232
238
|
5. For package/public API changes, also test the packed tarball from an empty temporary project.
|
|
233
|
-
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. `
|
|
239
|
+
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. `verify` fails on a stale one (`npm run check:node-reference`).
|
|
240
|
+
7. The ratchets in `tests/architecture-*.test.ts`, `scripts/bundle-budget.mjs` and the test type-error list in `scripts/verify.mjs` only go down. When one fails, fix the code: split the file, move the shared code down a layer, break the cycle, lazy-load the new Studio code. Never add to a list or raise a number; when your change lowers one, lower the number in the same commit.
|
|
241
|
+
|
|
242
|
+
### Where a change goes
|
|
243
|
+
|
|
244
|
+
| To add | Touch |
|
|
245
|
+
| --- | --- |
|
|
246
|
+
| A node in a project | `cascade node <Name>`, then `nodes/<Name>/index.ts` (see "Create a deterministic node") |
|
|
247
|
+
| A built-in node in an existing namespace | `packages/runtime/src/builtins/<ns>/<node>.ts`, its entry in `builtins/<ns>/index.ts`, a test in `packages/runtime/test/`, then `npm run build:node-reference` |
|
|
248
|
+
| A built-in namespace | `packages/runtime/src/builtins/<ns>/` and `builtins/index.ts`; Studio's `src/nodes/<ns>/index.ts` and `library.ts`, and `src/nodes/initializeLibraries.ts`; `scripts/build-node-reference.mjs` |
|
|
249
|
+
| A CLI verb or MCP tool | One row in `src/cli/verbs.ts`, its handler, and its line in `doc/CLI_REFERENCE.md` (plus `doc/MCP.md` for a tool); `tests/cli-verbs.test.ts` checks the docs |
|
|
250
|
+
| A graph edit operation | `packages/runtime/src/edit/operations.ts` and `graphEditor.ts`, then the verb row above |
|
|
251
|
+
| A param type | `packages/contracts/src/values.ts`, `definition.ts`, `validate.ts`; `packages/runtime/src/runtime.ts`; `src/types/coreTypes.ts`, `node.types.ts`, `src/utils/propUtils.ts`; `src/editor/Inspector.svelte`, `CodeEditor.svelte`; `server/src/projectTemplate.ts`; `doc/PROJECT_AUTHORING.md` |
|
|
252
|
+
| A Studio panel | `src/editor/panels/<Name>Panel.svelte` and its `registerPanelComponent` (or lazy) line in `src/editor/dockview/DockviewContainer.svelte`; keep it out of the entry chunk if it is not on first paint |
|
|
253
|
+
| Shared code for the server and the CLI | `server/src/` (the CLI imports it); pure document code such as the `.cascade` formatter (`formatCompactJSON`) lives in `packages/contracts/src/` |
|
|
234
254
|
|
|
235
255
|
Do not add dependencies without a concrete need. Do not create extra packages unless they need independent publication or versioning.
|
|
236
256
|
|
package/ARCHITECTURE.md
CHANGED
|
@@ -80,8 +80,9 @@ Studio-only project extensions use the type-only `cascade/studio/panel` contract
|
|
|
80
80
|
The packed root tarball contains compiled contracts, runtime, declarations, and a self-contained CLI. It must install and type-resolve from an empty project without workspace links or development dependencies. Additional packages require a concrete independent publication/versioning need.
|
|
81
81
|
|
|
82
82
|
The repository requires Node.js 22.13 or newer and builds with TypeScript 6,
|
|
83
|
-
Vite 8, and the Svelte Vite plugin 7. TypeScript 7
|
|
84
|
-
|
|
83
|
+
Vite 8, and the Svelte Vite plugin 7. TypeScript 7 runs the plain-tsc gates in
|
|
84
|
+
`scripts/verify.mjs` only; the build, the CLI's node compiler and svelte-check
|
|
85
|
+
stay on 6, which has the JS API they use.
|
|
85
86
|
|
|
86
87
|
Model providers are deliberately outside Cascade. Projects own provider SDKs,
|
|
87
88
|
credentials, model selection, retries, and result normalization, using the same
|
|
@@ -121,9 +122,9 @@ Callbacks and destruction hooks run after a valid structural commit. They are no
|
|
|
121
122
|
|
|
122
123
|
## Execution
|
|
123
124
|
|
|
124
|
-
Data runs follow graph dependencies. Explicit triggers use a FIFO queue, authored fan-out order, monotonic per-run sequence IDs, and cycle rejection. A triggered node receives cooked data ancestors before delivery.
|
|
125
|
+
Data runs follow graph dependencies; with `concurrency` > 1 (headless runs with a stage worker pool) independent nodes cook at once, still starting in topological order so events and traces keep one order. Explicit triggers use a FIFO queue, authored fan-out order, monotonic per-run sequence IDs, and cycle rejection. A triggered node receives cooked data ancestors before delivery.
|
|
125
126
|
|
|
126
|
-
Execution failures resolve to a failed `RunResult`; API misuse and invalid preflight reject. Cancellation propagates through capability calls. Inspection and output values are immutable
|
|
127
|
+
Execution failures resolve to a failed `RunResult`; API misuse and invalid preflight reject. Cancellation propagates through capability calls. Inspection and output values are immutable; browser-only live resources remain host-owned handles. A value a node hands to `set()` is adopted, not copied: under the default `values: "freeze"` it is checked and deep-frozen where it stands, and every later read (`getOutputs`, `inspect`, `node:output`, the next node's input) is that same value. Headless runs (`cascade run`) use `values: "trust"`, which checks the top-level type and freezes shallowly without walking the contents. Caller-supplied values (`setInput`, presets, definitions) are still copied.
|
|
127
128
|
|
|
128
129
|
### Geometry, simulation and feedback
|
|
129
130
|
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,388 @@
|
|
|
2
2
|
|
|
3
3
|
Notable changes to Cascade. Newest first.
|
|
4
4
|
|
|
5
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
## 0.12.0 — 2026-11-09
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- **GPU nodes get the whole GPU, and can say what they need.** Studio and
|
|
12
|
+
the headless host now ask for every limit the adapter allows instead of
|
|
13
|
+
WebGPU's defaults: on an M3 Max, 4 GiB per storage binding instead of
|
|
14
|
+
128 MiB, 1,024 workgroup invocations instead of 256, 32 KiB workgroup
|
|
15
|
+
storage instead of 16, 10 storage buffers per stage instead of 8. A node
|
|
16
|
+
may declare `gpuLimits` (`{ maxStorageBufferBindingSize: 512 * 2 ** 20 }`);
|
|
17
|
+
on a GPU below it the node fails before cooking, naming the limit and what
|
|
18
|
+
the GPU gives. An adapter that refuses its own ceiling still gets a device
|
|
19
|
+
with the defaults.
|
|
20
|
+
- **Scrub number in the Inspector.** Every number field and each vector
|
|
21
|
+
component drags anywhere on the field (at `scrub.ts`'s rates, Shift ×10,
|
|
22
|
+
Cmd ÷10), with a tick ruler that slides under the pointer and stops at a
|
|
23
|
+
declared min/max while that edge lights; hover shows the ruler and ‹ ›
|
|
24
|
+
steppers; a click without a drag opens typing, and plain arithmetic
|
|
25
|
+
(`320/2`, `10+5`) commits the number it works out to, not an expression. A
|
|
26
|
+
sideways touch drag scrubs and a vertical one still scrolls. Middle-drag,
|
|
27
|
+
label-drag and Alt-drag are unchanged. Vector fields are now text, so they
|
|
28
|
+
take the same sums and arrow-key steps.
|
|
29
|
+
- **Scrub number on every node's vector parameters.** The vector components
|
|
30
|
+
most nodes render through (CoreValue: `vec2`/`vec3`/`vec2i`… params and
|
|
31
|
+
ports) are the same scrub field: drag anywhere, click to type `10+5`, ‹ ›
|
|
32
|
+
and arrow-key steps, the ruler stopping at a declared range with the edge
|
|
33
|
+
lit. A bounded component keeps its range hairline. Read-only values stay
|
|
34
|
+
plain. Middle-, Alt- and label-drag and the Σ expression path are unchanged.
|
|
35
|
+
- **`cascade check` warns on nodes that have grown into a pipeline.**
|
|
36
|
+
`shape/long-execute` when `execute` passes 150 lines; `shape/mixed-jobs`
|
|
37
|
+
when one module loads, generates, colours, draws and exports (four of the
|
|
38
|
+
five, or loading and exporting together), judged from the calls it makes.
|
|
39
|
+
Advice only: the check still passes, `--strict` included, and `validate`
|
|
40
|
+
and `run` stay quiet. Points at "Design the graph" in
|
|
41
|
+
`doc/PROJECT_AUTHORING.md`.
|
|
42
|
+
- **Ladder variants as a Cairn choice card.** `cascade ladder … --cairn
|
|
43
|
+
<vault dir>` (MCP `ladder` `cairn`) copies each variant to
|
|
44
|
+
`<dir>/<id>-<nn>.png` in the Substrate vault and prints a `cairn:choice`
|
|
45
|
+
fence (`--json`: `data.cairnChoice`): one option per variant, labelled with
|
|
46
|
+
what it changed (`seed 42`, `hue=0.3`), its image relative to the vault
|
|
47
|
+
(nearest `.obsidian` or `.git`, or `--vault`), and the value
|
|
48
|
+
`pick:<id>:<n>`. Marcus taps a picture on his phone; the agent gets
|
|
49
|
+
"Picked: … (pick:<id>:<n>)", runs `cascade pick <id> <n>` and replies with
|
|
50
|
+
the locked image. At most 8 variants, refused before cooking. The loop is in
|
|
51
|
+
AGENTS.md, `doc/CLI_REFERENCE.md` and the `cascade new` template.
|
|
52
|
+
- **`cascade share`: output reaches people as a link.** `cascade share
|
|
53
|
+
<graph | site-directory> [--name <slug>]` exports the editor (or takes a
|
|
54
|
+
static directory with an `index.html`) into a scratch directory, with the
|
|
55
|
+
export allowlist and leak scan, deploys it to the Vercel project
|
|
56
|
+
`cascade-<slug>` and prints the production URL (`data.url` with `--json`).
|
|
57
|
+
Each share is recorded in the project's `shares.json`. Public unless
|
|
58
|
+
`--protected`. Also the MCP tool `share`. The deploy step is a `Deployer`
|
|
59
|
+
(`deployVercel` in `editorExport.ts`), which tests stub; `cascade export
|
|
60
|
+
--deploy vercel` uses the same one and now checks the production alias
|
|
61
|
+
Vercel reports rather than assuming `<project>.vercel.app`.
|
|
62
|
+
|
|
63
|
+
### Changed
|
|
64
|
+
|
|
65
|
+
- **`git push` runs a pre-push gate.** `npm install` points the checkout's
|
|
66
|
+
hooks at `.githooks/`; a push whose commits touch code runs
|
|
67
|
+
`verify --push`: types, node reference, architecture, svelte-check in
|
|
68
|
+
parallel, and the tests related to the pushed files, without the
|
|
69
|
+
load-sensitive `tests/performance/` timings. 10.8 s for a Studio change,
|
|
70
|
+
10–14 s for a `Graph.ts` change on a loaded machine. A push of docs alone
|
|
71
|
+
skips it; `--no-verify` skips it once.
|
|
72
|
+
- **The test suite runs in about 13 s, down from 18 s.** Studio imported
|
|
73
|
+
Lucide through its `@lucide/svelte` barrel, which loads every icon: 4.8 s
|
|
74
|
+
of module loading in each component test file and on every dev-server
|
|
75
|
+
cold start. Icons are now imported one by one
|
|
76
|
+
(`@lucide/svelte/icons/<name>`), and an architecture test fails if the
|
|
77
|
+
barrel comes back. The Studio bundle stays inside its budget.
|
|
78
|
+
- **Independent nodes cook at once in a headless run.** A project with a
|
|
79
|
+
warm stage worker gets a small pool of them (2–4, sized from cores and
|
|
80
|
+
memory; `exec.stages.workers` or `CASCADE_STAGE_WORKERS` sets it, a
|
|
81
|
+
`cascade render` worker keeps one), and the run cooks that many nodes at
|
|
82
|
+
once, a node only after everything it reads. Nodes still start in
|
|
83
|
+
topological order, so the run trace's order is the same every run, and a
|
|
84
|
+
stage call or log line goes to the node that made it. cloud-plots
|
|
85
|
+
`morph.cascade`, cold (Python cache missed), frames 101–107: 873 → 271 ms a
|
|
86
|
+
frame with 4 workers, the four renders overlapping; the first frame, which
|
|
87
|
+
starts the workers, 6.3 → 5.1 s. Runtime option `concurrency` (default 1);
|
|
88
|
+
feedback, trigger-wired and GPU nodes still cook alone.
|
|
89
|
+
- **The runtime stops copying values.** A value a node hands to `set()` is
|
|
90
|
+
checked and deep-frozen where it stands (`values: "freeze"`, the default)
|
|
91
|
+
instead of being cloned, and every later read (the `node:output` event,
|
|
92
|
+
`getOutputs`, a run's result, `inspect()`, the next node's input) is that
|
|
93
|
+
same value rather than another copy. `cascade run` uses `values: "trust"`:
|
|
94
|
+
the top-level type is checked and the value frozen shallowly, its contents
|
|
95
|
+
are not walked. `run --frames` works out which ports to save once per run
|
|
96
|
+
instead of calling `inspect()` every frame. A 1M-point plain array through
|
|
97
|
+
three nodes: 1,335 → 8 ms a frame under `trust` (105 ms under `freeze`),
|
|
98
|
+
`inspect()` 427 → 0 ms. A node that mutates a plain array or object after
|
|
99
|
+
handing it to `set()` now throws; it used to change nothing.
|
|
100
|
+
- **The image library is definition-v1 and runs everywhere.** Color,
|
|
101
|
+
Checkers, Noise, Ramp, Text, File (`cascade.image.Image`), Blur, Normal Map,
|
|
102
|
+
Resize, Transform and Composite were Studio classes over `ImageNodeBase`;
|
|
103
|
+
they are portable built-ins in `packages/runtime` with the same module ids,
|
|
104
|
+
ports and props, so saved graphs load unchanged and now also run under
|
|
105
|
+
`cascade run`/`render`, MCP, exported editors and the web player. Images pass
|
|
106
|
+
between them through the in-memory image contract; in Studio they stay in
|
|
107
|
+
the page (`.cascade-studio/…` paths the Viewer draws from memory) rather than
|
|
108
|
+
becoming files. 53 golden fixtures captured on the class nodes render pixel
|
|
109
|
+
for pixel the same on the runtime and in Studio. A colour saved as
|
|
110
|
+
`{ r, g, b, a }` reads as the four-component colour on load
|
|
111
|
+
(`Color.upgrade`). Noise no longer fetches simplex-noise from esm.sh (4.0.3
|
|
112
|
+
is vendored). Text takes a project font file in `font` and falls back to
|
|
113
|
+
sans-serif when it is missing. The Ramp's stops edit as JSON in the
|
|
114
|
+
Parameters section rather than with the ramp widget, and File's path is a
|
|
115
|
+
text field. A dynamic node that read an `ImageBuffer` from these ports now
|
|
116
|
+
gets an `ImageRef`.
|
|
117
|
+
- **Contact Sheet and the assertions are in Studio.** `cascade.image.ContactSheet`
|
|
118
|
+
is in the Image library's Composite category and `cascade.assert.*` has its
|
|
119
|
+
own Assert library. The assertions are portable now (Not Blank takes kept
|
|
120
|
+
pixels or decodes with `DecompressionStream`), so they live in the runtime's
|
|
121
|
+
built-in list and `src/cli/assertNodes.ts` and the headless-only list are
|
|
122
|
+
gone. Their bodies load lazily: Studio's first-paint JS went down, 969,706 →
|
|
123
|
+
964,958 B, with the class library and `ImageBuffer` gone; the budget is
|
|
124
|
+
lowered to match.
|
|
125
|
+
- **Studio paints the viewed node first.** After the Viewer's node cooks,
|
|
126
|
+
nothing outside its upstream starts until the Viewer has shown the new
|
|
127
|
+
picture (image loaded, then a frame; at most 300 ms), and while a slider
|
|
128
|
+
is held or a number field scrubs, only the viewed node's upstream cooks:
|
|
129
|
+
the rest waits for release. field-logo hue slider, input to painted:
|
|
130
|
+
440 → 141 ms median; a held drag no longer runs watercolor (~300 ms long
|
|
131
|
+
task) at all until release. Studio only (`src/editor/stores/viewedFirstCook.ts`);
|
|
132
|
+
the CLI and the scheduler are unchanged.
|
|
133
|
+
- **Smaller Studio first paint.** The Agent, Definition, Node Info and
|
|
134
|
+
Timeline panels load when their panel is created, and marked + DOMPurify
|
|
135
|
+
load with the Agent panel or the first text annotation (which shows plain
|
|
136
|
+
text for that moment). First-paint JS 1,135,042 → 968,597 B (gzip 325 →
|
|
137
|
+
273 KB), CSS 239,446 → 223,490 B. `scripts/bundle-budget.mjs` now counts
|
|
138
|
+
the chunks the entry modulepreloads too, and its budgets are lowered to the
|
|
139
|
+
new size plus 25 KB of JS and 10 KB of CSS headroom.
|
|
140
|
+
- **Faster dev loop.** `npm run build:cli` skips the package build when
|
|
141
|
+
every `packages/*/dist` is newer than its sources (3.5 → 2.0 s; `--force`
|
|
142
|
+
rebuilds). Vitest runs two projects, `node` and `studio` (the Svelte
|
|
143
|
+
plugin only where components are tested): `vitest run --project node` is
|
|
144
|
+
1,767 tests in ~10 s against ~18 s for the whole suite, which `verify`
|
|
145
|
+
still runs in full; `verify --fast` uses the node project when nothing
|
|
146
|
+
Studio-side changed. The plain-tsc gates in `verify` run on TypeScript 7
|
|
147
|
+
(`typescript7` devDependency), same errors per file, src + tests 3.0 →
|
|
148
|
+
0.4 s; the build, the CLI's node compiler and svelte-check stay on
|
|
149
|
+
TypeScript 6.
|
|
150
|
+
- **Intermediate images encode six times faster headlessly.** `saveImage`
|
|
151
|
+
(`cascade/io`) and `writeImage` (`cascade/image`) hand a canvas to the
|
|
152
|
+
headless host as pixels, and it writes a lossless PNG at zlib level 1
|
|
153
|
+
through sharp instead of Skia's default encoder: 157 → 26 ms per 1024²
|
|
154
|
+
image. field-logo renders at 887 ms a frame instead of 1,791, with
|
|
155
|
+
pixel-identical outputs. The CLI now bundles `cascade/io` from its own
|
|
156
|
+
install, as it already did `cascade/image`, so a sketch pinned to an older
|
|
157
|
+
package gets it too. New optional `IoBridge.keep` and
|
|
158
|
+
`AssetCapability.keepImage`; Studio and the browser player are unchanged.
|
|
159
|
+
- **Images pass between nodes in memory under `cascade run`/`render`.** The
|
|
160
|
+
headless host holds an intermediate's pixels (`ImagePixels` in contracts:
|
|
161
|
+
RGBA8, sRGB, unpremultiplied), names its file by their hash and writes the
|
|
162
|
+
level-1 PNG behind the run; `readImage`/`loadBitmap` get those pixels back
|
|
163
|
+
with no read and no decode (`AssetCapability.imagePixels`,
|
|
164
|
+
`IoBridge.pixels`). Every file reader still finds a whole file: Python stage
|
|
165
|
+
calls, `assets.read`, server nodes, the end of a run, freeze/lock/ladder all
|
|
166
|
+
wait for the write behind. Saved outputs are encoded from the pixels at
|
|
167
|
+
full compression (zlib 6, adaptive filtering), not copied from the
|
|
168
|
+
intermediate. field-logo 887 → 834 ms a frame (1,791 before round 2),
|
|
169
|
+
outputs pixel-identical; what remains is the nodes' own pixel loops. The
|
|
170
|
+
contract is in `doc/PROJECT_AUTHORING.md`, "How an image travels between
|
|
171
|
+
nodes".
|
|
172
|
+
- **`cascade render` workers start Python during the probe frames.** Each
|
|
173
|
+
worker process starts the project's warm Python worker as it boots
|
|
174
|
+
(`installProjectStages(…, { prewarm: true })`, `PythonWorker.warm()`), so
|
|
175
|
+
the interpreter start and imports overlap the first two serial frames
|
|
176
|
+
instead of following them. A project whose worker takes 3 s to import
|
|
177
|
+
renders 24 frames on 4 workers in 4.0 s instead of 7.0. A worker whose
|
|
178
|
+
interpreter cannot start now fails the call that needs it instead of
|
|
179
|
+
crashing the process. `run --frames` over 30 frames prints one line naming
|
|
180
|
+
`cascade render` for the same range.
|
|
181
|
+
- **Run traces time the frame and the save.** Each run in
|
|
182
|
+
`.cascade/runs/*.json` has `ms` (the cook), `frameMs` (to outputs on disk)
|
|
183
|
+
and `saveMs` (the difference), so a slow save shows without a profiler
|
|
184
|
+
(field-logo now: 747 ms cook, 82 ms save a frame).
|
|
185
|
+
|
|
186
|
+
### Fixed
|
|
187
|
+
|
|
188
|
+
- **`run --frames … --mp4` with an absolute `--out`.** ffmpeg was handed the
|
|
189
|
+
project root joined to the absolute sequence path and found no files; the
|
|
190
|
+
sequence is now resolved, not joined.
|
|
191
|
+
- **`cascade doctor` fails a project whose stages cannot import numpy.** With
|
|
192
|
+
no `python` environment declared, a stage source importing numpy and an
|
|
193
|
+
interpreter without it is a `stage imports` failure naming the file and the
|
|
194
|
+
fix. cloud-wall and cloud-volumes passed doctor and could not render.
|
|
195
|
+
|
|
196
|
+
### Known gaps
|
|
197
|
+
|
|
198
|
+
- **Ramp's stop editor and Image's file picker are not in Studio yet.** The
|
|
199
|
+
definition-v1 port of the image nodes left them out. Graphs that already
|
|
200
|
+
set them render unchanged.
|
|
201
|
+
|
|
202
|
+
## 0.11.0 — 2026-11-07
|
|
203
|
+
|
|
204
|
+
### Added
|
|
205
|
+
|
|
206
|
+
- **`cascade/image`: one node body for the browser and headless.** A
|
|
207
|
+
portable node declaring `capabilities: ['assets']` draws on `surface()` /
|
|
208
|
+
`createCanvas()` (the realm's `OffscreenCanvas` in Studio and an exported
|
|
209
|
+
editor, Skia's under `cascade run`/`render`/MCP), reads with `readImage` /
|
|
210
|
+
`readBytes` and writes with `writeImage` through the assets capability (the
|
|
211
|
+
host picks the stored path), and registers fonts with `registerFont`
|
|
212
|
+
(`FontFace` in a browser realm, Skia's `GlobalFonts` headless). No canvas or
|
|
213
|
+
no declared `assets` is refused with a sentence. Resolved from Cascade's own
|
|
214
|
+
install by Studio, the editor export, the web player and the CLI. This was
|
|
215
|
+
the missing browser image contract that kept commons nodes headless-only:
|
|
216
|
+
commons `plotter-social` (plotter, social-layout, foil ported; source
|
|
217
|
+
frozen) now exports as an editor, and renders pixel-identically to the old
|
|
218
|
+
server nodes headless. `cascade freeze --fields path,...` on an image output
|
|
219
|
+
now ships the image file with the snapshot.
|
|
220
|
+
- **The sketch kit, `@field/cascade-kit` (`packages/kit`).** One package for
|
|
221
|
+
what the sketch projects kept rebuilding: OKLCH colour and gamut mapping,
|
|
222
|
+
k-means palettes in OKLab, seeded random (`Rng`, mulberry32, Python-equal
|
|
223
|
+
`hashParts`) and noise, paper, pen plot documents for a 0.1 mm pen, frames
|
|
224
|
+
to MP4, Cairn and Observatory snapshot loaders, and a disk cache keyed on
|
|
225
|
+
the code's import closure. Each module's tests pin it to the exact bits of
|
|
226
|
+
the copies it replaces. `cascade-kit nodes <provider>` writes the stubs for
|
|
227
|
+
using another project's nodes from a `file:` dependency, replacing the
|
|
228
|
+
`shared/` symlink and hand-copied stubs; `--check` reports drift. Outside the
|
|
229
|
+
core: Cascade does not import it and the published package does not ship
|
|
230
|
+
it. See `doc/KIT.md`. cloud-volumes, commons and cairn-strata-hd use it.
|
|
231
|
+
- **`npm run verify`, one gate for everything.** Packages build, types for
|
|
232
|
+
src, tests and server, `check:packages`, svelte-check, vitest, the package
|
|
233
|
+
tests (192, outside `test:run` until now) and a Studio bundle budget, in
|
|
234
|
+
order, stopping at the first failure; `verify:fast` for the edit loop.
|
|
235
|
+
Architecture ratchets in `tests/architecture-*.test.ts`: the layer rules
|
|
236
|
+
from ARCHITECTURE.md, no new import cycle, no new file over 750 lines, no
|
|
237
|
+
unimported source file, duplicated lines capped. `build-node-reference
|
|
238
|
+
--check` fails on a stale `doc/NODE_REFERENCE.md`.
|
|
239
|
+
- **Ladder, pick, lock.** `cascade ladder <graph> <node> --name
|
|
240
|
+
--vary node.prop=a,b,c | --seeds a,b,c` cooks three to five variants from
|
|
241
|
+
the stored code and writes each image, a labelled contact sheet and
|
|
242
|
+
`ladders/<id>.json` (per variant: graph, params, seed, inputs, output
|
|
243
|
+
fingerprint; the code and git commit once). `cascade pick <ladder> <n>`
|
|
244
|
+
writes the lock from that record at approval without cooking again, so
|
|
245
|
+
`cascade replay` reproduces the approved image pixel for pixel with no
|
|
246
|
+
worktree. Both are MCP tools (`ladder`, `pick`). New built-in
|
|
247
|
+
`cascade.image.ContactSheet` (definition-v1, headless; not yet in Studio):
|
|
248
|
+
images in, one labelled grid out.
|
|
249
|
+
- **`cascade run --out`, outputs as files.** A still run with `--out <dir>`
|
|
250
|
+
copies each image output to `<dir>/<node>[.<port>].<ext>` (it was ignored
|
|
251
|
+
without `--frames`), and `run --json` lists every image output with its file
|
|
252
|
+
in `data.images`. A node with several image outputs writes one file per port
|
|
253
|
+
instead of overwriting itself; a `cascade.core.Null` passing an image counts
|
|
254
|
+
as an output; `--out-node <id>` (repeatable, MCP `outNodes`) saves a
|
|
255
|
+
mid-graph node as well; an absolute `--out` is accepted.
|
|
256
|
+
- **Assertion nodes, `cascade.assert.Range`, `Equals`, `True` and
|
|
257
|
+
`NotBlank`.** A check in the graph that fails `cascade run` (exit 1, and
|
|
258
|
+
`run --json` gives `error.message: "Assertion failed in \"<node>\" …"`)
|
|
259
|
+
when it does not hold: a value in a range, equal to an expected JSON value,
|
|
260
|
+
true, or an image (PNG/PPM) that is not one flat colour or fully
|
|
261
|
+
transparent. Each passes its input through. Headless only for now: the CLI
|
|
262
|
+
registers them, Studio does not (its entry bundle has no room) and shows
|
|
263
|
+
them unresolved. Listed in `doc/NODE_REFERENCE.md`.
|
|
264
|
+
- **MCP: the project directory per call.** Every tool takes an optional
|
|
265
|
+
`project`; the call's paths resolve against it and may not leave it, and
|
|
266
|
+
CLI-backed tools run there. One server started in commons can work on any
|
|
267
|
+
sketch. See `doc/MCP.md`.
|
|
268
|
+
- **`cascade help node [<type>]`, node docs from the package.** Prints a node
|
|
269
|
+
type's inputs, props and outputs (type, default, range, description), where
|
|
270
|
+
it runs and its capabilities, from the definitions bundled in the CLI or
|
|
271
|
+
read statically from the project's `nodes/`; without a type, lists every
|
|
272
|
+
type. Short names work (`Circle`, `NotBlank`); a typo gets suggestions; a
|
|
273
|
+
class-based Studio node says so and points at the shipped
|
|
274
|
+
`doc/NODE_REFERENCE.md`. `--json`, and MCP `node_help`.
|
|
275
|
+
- **Debug output in the run trace, and `--env`.** What a node prints while it
|
|
276
|
+
cooks (console, and its Python stages' stderr, which was shown only with
|
|
277
|
+
`--verbose`) is kept per node in `.cascade/runs/…json` and in
|
|
278
|
+
`run --json` as `data.logs`. A failed run now writes its trace too and
|
|
279
|
+
names it in the error. `cascade run|render --env NAME=value` (MCP `env`)
|
|
280
|
+
sets a variable for the run's stages and render workers without a shell
|
|
281
|
+
prefix; the trace records the names only.
|
|
282
|
+
- **A declared Python environment for stages, and `cascade doctor`.**
|
|
283
|
+
`cascade.json` `"python": { "requirements": ["numpy", "Pillow"] }` (or a
|
|
284
|
+
requirements file) declares what a project's stages import; `cascade
|
|
285
|
+
doctor --fix` builds a venv for it in `.cascade-cache/python/venv`, never
|
|
286
|
+
system-wide, and stages then run under it instead of a bare `python3`. A
|
|
287
|
+
stage call with the environment missing or stale fails naming the fix,
|
|
288
|
+
not later on an ImportError. `cascade doctor` (MCP `doctor`) also checks
|
|
289
|
+
the stage interpreter, ffmpeg and the headless canvas.
|
|
290
|
+
|
|
291
|
+
### Removed
|
|
292
|
+
|
|
293
|
+
- **The Studio's single-file HTML export (File > Export…, ⌘E).** It ran a
|
|
294
|
+
third, minimal engine that understood neither definition-v1, expressions
|
|
295
|
+
nor animation. `cascade build` exports the browser player and `cascade
|
|
296
|
+
export` the shareable editor; the other exports are unchanged. Five unused Studio components and the code-history store only
|
|
297
|
+
they used are gone too.
|
|
298
|
+
|
|
299
|
+
### Changed
|
|
300
|
+
|
|
301
|
+
- **The CLI starts in 30 ms, not 300.** Each verb's handler loads when the
|
|
302
|
+
verb runs and the published bundle is split, so `cascade --version`, the
|
|
303
|
+
graph commands and every child the MCP server spawns no longer load the
|
|
304
|
+
engine, TypeScript, esbuild, sharp and express first (start-up: 3 modules,
|
|
305
|
+
was 225; `--version` 29 ms and 46 MB, was 286 ms and 155 MB).
|
|
306
|
+
- **One verb table, `src/cli/verbs.ts`.** Dispatch, `--help`, `--json`
|
|
307
|
+
handling, the graph commands' options and the MCP tools all read it, so a
|
|
308
|
+
new verb is one row, its handler and its line in `doc/CLI_REFERENCE.md`,
|
|
309
|
+
which now lists every command.
|
|
310
|
+
- `cascade graph list` skips `renders/` and `out/`.
|
|
311
|
+
- The `.cascade` compact formatter has one copy, `formatCompactJSON` in
|
|
312
|
+
`cascade/contracts`, used by Studio, the server and the CLI; the server and
|
|
313
|
+
CLI share one atomic-write helper.
|
|
314
|
+
- **The stage cache is on by default for node cooks.** A stage called from a
|
|
315
|
+
node's cook through `cascade/stage` is treated as pure and cached on its
|
|
316
|
+
code, arguments and input file bytes; a panel's `api.runStage` call is an
|
|
317
|
+
action and is not cached unless declared. A re-cook of
|
|
318
|
+
cloud-plots/relief.cascade went from 4.2 s to 0.45 s (all 17 stage calls
|
|
319
|
+
hit; no Python started). Opt a stage out with
|
|
320
|
+
`exec.stages.cache.exclude`, or everything with `"cache": false`; see
|
|
321
|
+
"The stage cache default" in `doc/PROJECT_AUTHORING.md`.
|
|
322
|
+
|
|
323
|
+
### Fixed
|
|
324
|
+
|
|
325
|
+
- **A node fed a different image of the same size re-cooks.** The input
|
|
326
|
+
fingerprint sampled three values of a typed array and only the dimensions of
|
|
327
|
+
an image, so two same-size images looked identical and a clean node kept
|
|
328
|
+
its old picture. A connected input is now keyed by its producer's output
|
|
329
|
+
version (O(1), exact), and a value set directly on a port is hashed in full.
|
|
330
|
+
- The node and graph file watchers take an injected clock and watcher, and
|
|
331
|
+
their tests no longer sleep on real FSEvents; they had failed in every
|
|
332
|
+
loaded full run.
|
|
333
|
+
- The MCP `locks` tool always answered "no locks command yet": the lock tools
|
|
334
|
+
probed `cascade --help` first and the probe never matched. The probe is
|
|
335
|
+
gone, so `lock`, `replay` and `locks` also spawn one process instead of two.
|
|
336
|
+
|
|
337
|
+
## 0.10.2 — 2026-11-06
|
|
338
|
+
|
|
339
|
+
### Added
|
|
340
|
+
|
|
341
|
+
- **`cascade render`: an animation across worker processes.** `cascade render
|
|
342
|
+
<graph> --node <id> [--frames <range>] [--workers N]` splits the range across
|
|
343
|
+
processes (default: cores minus a few), each pulling contiguous batches from
|
|
344
|
+
one queue, and writes the same `<out>/<node>.<frame>.<ext>` sequence a serial
|
|
345
|
+
`run --frames` writes. A node that writes every frame to one fixed file is
|
|
346
|
+
refused before the other workers start: the first worker renders the first
|
|
347
|
+
two frames alone, and two frames on one path stop the render with the node
|
|
348
|
+
and the path. 144 ripple frames at 540 px on a shared 16-core machine at load
|
|
349
|
+
33-44: 59 s serial, 8-14 s on 8-12 workers, frames byte-identical to the
|
|
350
|
+
serial render.
|
|
351
|
+
- **Preview by default.** An animation render also encodes
|
|
352
|
+
`<out>/<node>.preview.mp4`, half size, H.264 yuv420p with faststart.
|
|
353
|
+
`--final` encodes `<node>.mp4` at full size, `--prores` adds a ProRes
|
|
354
|
+
`<node>.mov` (4444 with alpha when the frames have any, else 422 HQ),
|
|
355
|
+
`--no-video` skips it. ffmpeg is checked before the render, not after.
|
|
356
|
+
- **Progress and ETA.** One line every N frames (`--progress N`, default about
|
|
357
|
+
twenty a render), and the same as a note on a Cairn job with `--job <id>` or
|
|
358
|
+
`CAIRN_JOB` (`cairn jobs update <id> --note`).
|
|
359
|
+
|
|
360
|
+
### Fixed
|
|
361
|
+
|
|
362
|
+
- **Project nodes compile atomically.** The bundle is written beside its name
|
|
363
|
+
and renamed into place, so processes compiling the same node at once never
|
|
364
|
+
import a half-written file.
|
|
365
|
+
|
|
366
|
+
## 0.10.1 — 2026-10-12
|
|
367
|
+
|
|
368
|
+
### Fixed
|
|
369
|
+
|
|
370
|
+
- **A phone never scales the editor page.** The viewport is fixed at the
|
|
371
|
+
device width (`maximum-scale=1`). iOS ignores `user-scalable=no`, so the shell
|
|
372
|
+
also blocks Safari's gesture events and two-finger moves outside the preview,
|
|
373
|
+
and sets `touch-action: manipulation` on controls so a double-tap on a button
|
|
374
|
+
does not zoom. Pinching the preview still zooms the preview. The page clips
|
|
375
|
+
any horizontal overflow, and the header truncates within the screen width.
|
|
376
|
+
|
|
377
|
+
### Added
|
|
378
|
+
|
|
379
|
+
- **Fonts in exported editors.** `editor.fonts` lists OFL-1.1 fonts that
|
|
380
|
+
`cascade export` ships and loads in the page and the engine worker, so
|
|
381
|
+
canvas and panel text draw with the project's font. Other licences and
|
|
382
|
+
`licensed/` files are refused.
|
|
383
|
+
- **`cascade export --deploy vercel --public`** for an editor meant to be
|
|
384
|
+
public: an anonymously readable site is reported as intended instead of
|
|
385
|
+
failing the deploy.
|
|
386
|
+
|
|
5
387
|
## 0.10.0 — 2026-10-12
|
|
6
388
|
|
|
7
389
|
### Added
|
package/README.md
CHANGED
|
@@ -36,7 +36,7 @@ cascade . # reopen it later
|
|
|
36
36
|
cascade run index.cascade --frames 1 --json # render headlessly
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
-
The npm package is at 0.
|
|
39
|
+
The npm package is at 0.12.0 and includes every command below. 0.12 adds `cascade share`, `ladder --cairn`, scrub number fields and the full GPU limits; see the [changelog](CHANGELOG.md).
|
|
40
40
|
|
|
41
41
|
## New in 0.9: shareable editors
|
|
42
42
|
|
|
@@ -383,7 +383,6 @@ a second set of node algorithms.
|
|
|
383
383
|
| Embedded runtime | `cascade/runtime` is the neutral definition-v1 API for Node and browser hosts. It does not load Studio or interpret dynamic nodes. |
|
|
384
384
|
| Static browser player | `cascade build` exports definition-v1 browser/portable graphs with assets and optional browser WebGPU. No server operations, dynamic modules or project Studio panels. Same-origin JavaScript controls; ordinary cross-origin iframe embeds. |
|
|
385
385
|
| GPU | Studio and the optional Dawn CLI host provide a shared device, adapter metadata, limits, and per-node caches. Portable GPU nodes render offscreen and explicitly read RGBA8 pixels through `cascade/gpu`; graph ports still carry images. See [headless GPU rendering](doc/HEADLESS_GPU.md). |
|
|
386
|
-
| Studio HTML export | The older Studio exporter uses a separate minimal runtime and supports only a subset of graph behavior. Use `cascade build` for supported definition-v1 web deployment. |
|
|
387
386
|
| External services | Project-owned and optional. Cascade itself requires no paid provider or model subscription. |
|
|
388
387
|
|
|
389
388
|
## Repository map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.console.svelte-1cqop5k{background:var(--surface-panel);width:100%;height:100%;color:var(--text-primary);flex-direction:column;min-height:0;font-family:SF Mono,Monaco,Cascadia Code,Consolas,monospace;font-size:12px;display:flex}.console.drop-active.svelte-1cqop5k{outline:1px solid var(--accent);outline-offset:-1px}.bar.svelte-1cqop5k{border-bottom:1px solid var(--border-divider);background:var(--surface-panel-alt);align-items:center;gap:6px;padding:4px 6px;display:flex}.bar.svelte-1cqop5k select:where(.svelte-1cqop5k),.bar.svelte-1cqop5k button:where(.svelte-1cqop5k),.send.svelte-1cqop5k{background:var(--surface-control);color:var(--text-primary);border:1px solid var(--border);font:inherit;cursor:pointer;border-radius:4px;padding:2px 6px}.send.svelte-1cqop5k{flex:none;width:30px;height:30px;padding:0;font-size:15px;line-height:1}.bar.svelte-1cqop5k button.icon:where(.svelte-1cqop5k){width:22px;height:22px;padding:0;font-size:12px;line-height:1}.bar.svelte-1cqop5k button.stop:where(.svelte-1cqop5k){color:var(--accent)}.zoom.svelte-1cqop5k{color:var(--text-secondary);font:inherit;cursor:pointer;background:0 0;border:none;padding:0 4px}.zoom.svelte-1cqop5k:hover{color:var(--text-primary)}.bar.svelte-1cqop5k button:where(.svelte-1cqop5k):hover,.send.svelte-1cqop5k:hover:not(:disabled){background:var(--surface-control-hover)}.send.svelte-1cqop5k:disabled{color:var(--text-disabled);cursor:default}.sketch.svelte-1cqop5k{color:var(--text-tertiary);text-overflow:ellipsis;white-space:nowrap;max-width:40%;overflow:hidden}.spacer.svelte-1cqop5k{flex:1}.notice.svelte-1cqop5k{background:var(--banner-warn-bg);color:var(--banner-warn-text);border-bottom:1px solid var(--banner-warn-border);padding:6px 8px;line-height:1.5}.notice.svelte-1cqop5k code:where(.svelte-1cqop5k){color:var(--text-code);white-space:pre-wrap;margin-top:4px;display:block}.transcript.svelte-1cqop5k{min-height:0;font-size:calc(12px * var(--agent-zoom,1));flex-direction:column;flex:1;gap:4px;padding:6px 8px;line-height:1.45;display:flex;overflow:auto}.hint.svelte-1cqop5k{color:var(--text-faint);line-height:1.6}.entry.svelte-1cqop5k{white-space:pre-wrap;word-break:break-word;line-height:1.5}.entry.you.svelte-1cqop5k{color:var(--accent)}.entry.agent.svelte-1cqop5k{color:var(--text-primary)}.entry.result.svelte-1cqop5k{color:var(--text-bright)}.entry.tool.svelte-1cqop5k{color:var(--text-tertiary)}.entry.system.svelte-1cqop5k{color:var(--text-faint)}.entry.detail.svelte-1cqop5k{color:var(--text-faint);font-size:.9em}.entry.error.svelte-1cqop5k{color:var(--status-error)}.detail-toggle.svelte-1cqop5k{border:1px solid var(--border-faint);color:var(--text-faint);font:inherit;cursor:pointer;background:0 0;border-radius:3px;align-self:flex-start;margin-top:.25rem;padding:.1rem .4rem;font-size:.85em}.detail-toggle.svelte-1cqop5k:hover{color:var(--text-secondary);border-color:var(--border-divider)}.composer.svelte-1cqop5k{border-top:1px solid var(--border-divider);background:var(--surface-panel-alt);flex-direction:column;gap:8px;padding:10px;display:flex}.composer-context.svelte-1cqop5k,.composer-row.svelte-1cqop5k{align-items:center;gap:8px;display:flex}.composer-context.svelte-1cqop5k button:where(.svelte-1cqop5k){border:1px solid var(--border);background:var(--surface-control);color:var(--text-secondary);font:inherit;cursor:pointer;border-radius:4px;padding:3px 7px}.composer-context.svelte-1cqop5k button.active:where(.svelte-1cqop5k){border-color:var(--accent);color:var(--text-primary);background:var(--accent-tint)}.composer-context.svelte-1cqop5k button:where(.svelte-1cqop5k):disabled{color:var(--text-disabled);cursor:default}.composer-context.svelte-1cqop5k span:where(.svelte-1cqop5k){min-width:0;color:var(--text-tertiary);text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.composer-row.svelte-1cqop5k{align-items:flex-end}.composer.svelte-1cqop5k textarea:where(.svelte-1cqop5k){resize:none;background:var(--surface-input);max-height:50%;color:var(--text-primary);border:1px solid var(--border);font-family:inherit;font-size:calc(12px * var(--agent-zoom,1));border-radius:4px;flex:1;padding:8px 10px;line-height:1.45;overflow-y:auto}.composer.svelte-1cqop5k textarea:where(.svelte-1cqop5k):focus{outline:1px solid var(--accent);outline-offset:-1px}.entry.markdown.svelte-1cqop5k{white-space:normal}.entry.markdown.svelte-1cqop5k>:first-child{margin-top:0}.entry.markdown.svelte-1cqop5k>:last-child{margin-bottom:0}.entry.markdown.svelte-1cqop5k p{margin:0 0 .6em}.entry.markdown.svelte-1cqop5k h1,.entry.markdown.svelte-1cqop5k h2,.entry.markdown.svelte-1cqop5k h3,.entry.markdown.svelte-1cqop5k h4,.entry.markdown.svelte-1cqop5k h5,.entry.markdown.svelte-1cqop5k h6{color:var(--text-bright);margin:.9em 0 .35em;font-weight:600;line-height:1.3}.entry.markdown.svelte-1cqop5k h1{font-size:1.3em}.entry.markdown.svelte-1cqop5k h2{font-size:1.18em}.entry.markdown.svelte-1cqop5k h3{font-size:1.08em}.entry.markdown.svelte-1cqop5k h4,.entry.markdown.svelte-1cqop5k h5,.entry.markdown.svelte-1cqop5k h6{font-size:1em}.entry.markdown.svelte-1cqop5k ul,.entry.markdown.svelte-1cqop5k ol{margin:0 0 .6em;padding-left:1.4em}.entry.markdown.svelte-1cqop5k li{margin:.15em 0}.entry.markdown.svelte-1cqop5k li>p{margin:0}.entry.markdown.svelte-1cqop5k strong{color:var(--text-bright);font-weight:600}.entry.markdown.svelte-1cqop5k blockquote{border-left:2px solid var(--border-divider);color:var(--text-tertiary);margin:0 0 .6em;padding-left:.8em}.entry.markdown.svelte-1cqop5k hr{border:0;border-top:1px solid var(--border-faint);margin:.8em 0}.entry.markdown.svelte-1cqop5k a{color:var(--accent)}.entry.markdown.svelte-1cqop5k code{background:var(--surface-raised);color:var(--text-code);border-radius:3px;padding:.1em .3em;font-size:.92em}.entry.markdown.svelte-1cqop5k pre{background:var(--surface-raised);border:1px solid var(--border-faint);white-space:pre;border-radius:4px;margin:0 0 .6em;padding:.5em .6em;overflow-x:auto}.entry.markdown.svelte-1cqop5k pre code{white-space:pre;background:0 0;border-radius:0;padding:0}.entry.markdown.svelte-1cqop5k table{border-collapse:collapse;max-width:100%;margin:0 0 .6em;display:block;overflow-x:auto}.entry.markdown.svelte-1cqop5k th,.entry.markdown.svelte-1cqop5k td{border:1px solid var(--border-faint);text-align:left;vertical-align:top;white-space:nowrap;word-break:normal;padding:.25em .5em}.entry.markdown.svelte-1cqop5k th{background:var(--surface-raised);color:var(--text-bright);font-weight:600}.entry.markdown.svelte-1cqop5k img{max-width:100%}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import{$ as e,D as t,Dt as n,F as r,G as i,I as a,L as o,O as s,Ot as ee,Q as c,T as te,U as l,V as u,W as ne,X as d,Z as re,_t as ie,at as ae,ct as f,dt as p,et as m,ft as oe,gt as se,it as h,jt as g,l as ce,lt as _,n as le,nt as v,ot as y,p as ue,pt as b,q as de,r as fe,rt as pe,st as me,v as he,vt as ge,w as _e,x as ve,yt as ye,z as x}from"./legacy-CTEeXeqF.js";import{_ as be,dt as xe,g as Se,h as Ce,m as we,o as S,p as Te,v as Ee,y as De}from"./index-BWnrmIB9.js";import{renderMarkdown as Oe}from"./renderMarkdown-ork0Oir7.js";var C=class extends Error{constructor(e,t=null,n=null){super(e),this.hint=t,this.code=n,this.name=`AgentUnavailableError`}},w=null,T=null;function ke(){return w?Promise.resolve(w):(T??=Ae().finally(()=>{T=null}),T)}async function Ae(){let e=await fetch(`/api/agent/capability`);if(e.status===404)throw new C(`The agent console is disabled on this server. It is available only on a loopback server or one started with an explicit trusted host.`);if(!e.ok)throw new C(`Could not start an agent session (${e.status})`);let{capability:t}=await e.json();if(typeof t!=`string`||!t)throw new C(`Server issued no agent capability`);return w=t,t}async function E(e,t){let n=e=>({...t.headers??{},"X-Cascade-Agent-Capability":e}),r=await fetch(e,{...t,headers:n(await ke())});return r.status===403&&(w=null,r=await fetch(e,{...t,headers:n(await ke())})),r}async function je(e){let t=await E(`/api/agent/status${e?`?sketch=${encodeURIComponent(e)}`:``}`,{method:`GET`});if(!t.ok)throw new C(`Could not read agent status (${t.status})`);let n=await t.json();return{root:String(n?.root??``),agents:Array.isArray(n?.agents)?n.agents:[],sessions:Array.isArray(n?.sessions)?n.sessions:[]}}var D=2e3,O=3e4;function Me(e){let t=!1,n=null,r=null,i=D,a=e.sketch?`?sketch=${encodeURIComponent(e.sketch)}`:``,o=typeof document<`u`,s=()=>!o||document.visibilityState!==`hidden`,ee=()=>{t||r||!s()||(r=setTimeout(()=>{r=null,c()},i),i=Math.min(i*2,O))};async function c(){if(t||n||!s())return;let r=new AbortController;n=r;try{let n=await E(`/api/agent/watch${a}`,{method:`GET`,signal:r.signal});if(n.status===404){t=!0;return}if(!n.ok)throw Error(`agent watch ${n.status}`);i=D,await k(n,t=>{t?.type===`status`&&Array.isArray(t.sessions)&&e.onSessions(t.sessions)})}catch(e){if(e instanceof C){t=!0;return}if(e?.name===`AbortError`)return}finally{n===r&&(n=null)}ee()}let te=()=>{s()?(i=D,c()):(n?.abort(),n=null,r&&clearTimeout(r),r=null)};return o&&document.addEventListener(`visibilitychange`,te),c(),()=>{t=!0,n?.abort(),n=null,r&&clearTimeout(r),o&&document.removeEventListener(`visibilitychange`,te)}}async function k(e,t){if(!e.body)return;let n=e.body.getReader(),r=new TextDecoder,i=``,a=e=>{if(e.trim())try{t(JSON.parse(e))}catch{}};for(;;){let{value:e,done:t}=await n.read();if(t)break;i+=r.decode(e,{stream:!0});let o=i.split(`
|
|
2
|
+
`);i=o.pop()??``;for(let e of o)a(e)}a(i)}async function Ne(e,t){let n=await E(`/api/agent/cancel`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({agent:e,sketch:t})});return n.ok?(await n.json().catch(()=>null))?.cancelled===!0:!1}async function Pe(e,t){await E(`/api/agent/reset`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({agent:e,sketch:t})})}async function Fe(e){let t=await E(`/api/agent/attach`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({agent:e.agent,sketch:e.sketch,since:e.since??0}),signal:e.signal});if(!t.ok){let e=await t.json().catch(()=>null);throw new C(String(e?.error??`Could not reconnect to the agent (${t.status})`))}await A(t,e.onEvent)}async function Ie(e){let t=await E(`/api/agent/prompt`,{method:`POST`,headers:{"Content-Type":`application/json`},body:JSON.stringify({agent:e.agent,sketch:e.sketch,prompt:e.prompt}),signal:e.signal});if(!t.ok){let e=await t.json().catch(()=>null);throw new C(String(e?.error??`Agent request failed (${t.status})`),e?.hint??null,e?.code??null)}await A(t,e.onEvent)}async function A(e,t){if(!e.body)throw new C(`Agent produced no stream`);let n=e.body.getReader(),r=new TextDecoder,i=``;for(;;){let{value:e,done:a}=await n.read();if(a)break;i+=r.decode(e,{stream:!0});let o=i.split(`
|
|
3
|
+
`);i=o.pop()??``;for(let e of o)Le(e,t)}Le(i,t)}function Le(e,t){if(e.trim())try{t(JSON.parse(e))}catch{t({type:`stderr`,text:e})}}function Re(e){let t=e.trim();if(!t)return null;if(!t.startsWith(`{`))return{kind:`raw`,text:t};let n;try{n=JSON.parse(t)}catch{return{kind:`raw`,text:t}}if(n?.type===`system`)return null;if(n?.type===`assistant`&&Array.isArray(n?.message?.content)){let e=[];for(let t of n.message.content)t?.type===`text`&&typeof t.text==`string`&&t.text.trim()?e.push(t.text.trim()):t?.type===`tool_use`&&e.push(`· ${t.name??`tool`}${ze(t.input)}`);let t=e.join(`
|
|
4
|
+
`);return t?{kind:e.every(e=>e.startsWith(`· `))?`tool`:`text`,text:t}:null}if(n?.type===`user`)return null;if(n?.type===`result`){let e=typeof n.result==`string`&&n.result.trim()?n.result.trim():null;return e?{kind:`result`,text:e}:null}if(n?.type===`item.completed`||n?.type===`item.started`){let e=n?.item;if(e?.type===`agent_message`&&typeof e.text==`string`&&e.text.trim())return n.type===`item.completed`?{kind:`text`,text:e.text.trim()}:null;if(e?.type===`command_execution`){if(n.type===`item.started`&&typeof e.command==`string`&&e.command.trim())return{kind:`tool`,text:`· Command ${e.command.trim()}`};if(n.type===`item.completed`){let t=typeof e.aggregated_output==`string`?e.aggregated_output.trim():``,n=typeof e.status==`string`?e.status:`completed`,r=`· Command ${(typeof e.command==`string`?e.command.trim():``)||n}`;return{kind:t?`result`:`tool`,text:t?`${r}\n${t}`:r}}}if(e?.type===`file_change`){let t=(Array.isArray(e.changes)?e.changes:[]).map(e=>e?.path).filter(e=>typeof e==`string`&&!!e);return{kind:`tool`,text:`· File change${t.length?` ${t.join(`, `)}`:``}`}}}return n?.type===`thread.started`||n?.type===`turn.started`||n?.type===`turn.completed`?null:typeof n?.type==`string`?{kind:`detail`,text:t}:{kind:`raw`,text:t}}function ze(e){if(!e||typeof e!=`object`)return``;let t=e,n=t.file_path??t.path??t.pattern??t.command;return typeof n==`string`&&n?` ${n}`:``}var Be=i(`<option> </option>`),Ve=i(`<span class="sketch svelte-1cqop5k" title="Open the sketch through the cascade CLI">no project file</span>`),He=i(`<button type="button" class="zoom svelte-1cqop5k" title="Text size — click to reset (⌘0)"> </button>`),Ue=i(`<button type="button" class="icon svelte-1cqop5k" title="Copy the whole transcript, including hidden stream events" aria-label="Copy transcript"> </button>`),We=i(`<button type="button" class="icon stop svelte-1cqop5k" title="Stop the agent" aria-label="Stop the agent">■</button>`),Ge=i(`<button type="button" title="Forget the conversation for this sketch" class="svelte-1cqop5k">Reset</button>`),Ke=i(`<div class="notice svelte-1cqop5k"> </div>`),qe=i(`<code class="svelte-1cqop5k"> </code>`),Je=i(`<div class="notice svelte-1cqop5k"><div> </div> <!></div>`),Ye=i(`<div class="hint svelte-1cqop5k"><!></div>`),Xe=i(`<div></div>`),Ze=i(`<div> </div>`),Qe=i(`<button class="detail-toggle svelte-1cqop5k"> </button>`),$e=i(`<span class="svelte-1cqop5k"> </span>`),et=i(`<div data-panel-scope="agent" role="group" aria-label="Agent console"><div class="bar svelte-1cqop5k"><select aria-label="Agent" class="svelte-1cqop5k"></select> <!> <span class="spacer svelte-1cqop5k"></span> <!> <!> <!></div> <!> <div class="transcript svelte-1cqop5k"><!> <!> <!></div> <div class="composer svelte-1cqop5k"><div class="composer-context svelte-1cqop5k"><button type="button">Use selected node</button> <!></div> <div class="composer-row svelte-1cqop5k"><textarea rows="2" spellcheck="false" placeholder="Prompt the sketch" class="svelte-1cqop5k"></textarea> <button type="button" class="send svelte-1cqop5k" title="Send (⌘↵)" aria-label="Send prompt">↑</button></div></div></div>`);function tt(i,w){ee(w,!1);let T=()=>ye(xe,`$sharedContextStore`,ke),[ke,Ae]=ge(),E=p(),D=p(),O=p(),k=p(),A=p(),Le=p(),ze=new Set([`agent`,`result`]),tt=[`claude`,`codex`],j=p(`claude`),M=p(``),N=p([]),P=p(!1),F=p(!1),I=p([]),nt=p(``),L=p(null),rt=p(!1),R=null,z=null,it=null,at=null,B=p(!1),V=p(null),H=p(null),U=p(!1),W=p(null),G=p(!1);fe(()=>Ee.subscribe(t=>{t.length!==0&&(b(M,be(c(M),Se())),e().then(()=>{_t(),c(H)?.focus()}))}));let ot=`cascade.agentPanel.zoom`,K=[.75,.85,1,1.15,1.3,1.5,1.75,2],q=p(st());function st(){try{let e=Number(localStorage.getItem(ot));return K.includes(e)?e:1}catch{return 1}}function ct(e){b(q,e);try{localStorage.setItem(ot,String(e))}catch{}}function lt(e){let t=K.indexOf(c(q)),n=t===-1?K.indexOf(1):t,r=Math.min(K.length-1,Math.max(0,n+e));r!==n&&ct(K[r])}function ut(e){if(e.altKey)return;let t=e.metaKey||e.ctrlKey;if(!c(W)||!we(`agent`))return;let n=e.target instanceof HTMLTextAreaElement||e.target instanceof HTMLInputElement;!t&&n||(e.key===`0`?(e.preventDefault(),ct(1)):e.key===`+`||e.key===`=`?(e.preventDefault(),lt(1)):(e.key===`-`||e.key===`_`)&&(e.preventDefault(),lt(-1)))}let J=p(S()?.file()??null),dt=[];fe(()=>{ht(c(J));let e=Me({onSessions:e=>{dt=e,gt()}}),t=setInterval(()=>{let e=S()?.file()??null;e===c(J)?!c(F)&&at!==c(j)?ht(e):gt():(b(J,e),ht(e))},1e3);return()=>{clearInterval(t),e()}}),ft();async function ft(){try{let e=await je(c(J)??void 0);if(b(I,e.agents),b(nt,e.root),!c(I).find(e=>e.alias===c(j)&&e.configured&&e.resolved)){let e=c(I).find(e=>e.configured&&e.resolved);e&&b(j,e.alias)}}catch(e){b(L,e instanceof Error?e.message:String(e))}finally{b(rt,!0)}}function pt(e){if(e.type===`attached`)b(B,e.running),e.running||mt(),e.dropped>0&&Y(`system`,`${e.dropped} earlier ${e.dropped===1?`line`:`lines`} were dropped from the server's buffer.`),e.running?Y(`system`,`Reconnected to the ${e.agent} session running for ${e.sketch}.`):e.replayed>0&&Y(`system`,`Showing the last ${e.agent} run for ${e.sketch}. Nothing is running now.`);else if(e.type===`started`)b(B,!0),Y(`system`,`${e.agent} ${e.resumed?`resumed`:`started`} for ${e.sketch}`);else if(e.type===`stdout`){let t=Re(e.text);t&&Y(t.kind===`raw`?`system`:t.kind===`text`?`agent`:t.kind,t.text)}else e.type===`stderr`?Y(`error`,e.text.trimEnd()):e.type===`exit`?(b(B,!1),mt(),e.cancelled?Y(`system`,`Stopped.`):e.timedOut?Y(`error`,`The agent was still running after 15 minutes and was stopped.`):e.code!==0&&Y(`error`,`${c(j)} exited with code ${e.code}.`)):e.type===`error`&&(b(B,!1),mt(),Y(`error`,e.message))}function mt(){dt=dt.map(e=>e.sketch===c(J)&&e.agent===c(j)?{...e,running:!1}:e)}async function ht(e){if(z?.abort(),z=null,it=e,at=c(j),b(B,!1),c(F)||(b(N,[]),!e))return;let t=new AbortController;z=t;try{await Fe({agent:c(j),sketch:e,onEvent:t=>{it===e&&pt(t)},signal:t.signal})}catch(e){e?.name!==`AbortError`&&Y(`error`,e instanceof Error?e.message:String(e))}finally{z===t&&(z=null),b(B,!1)}}async function gt(){c(F)||c(B)||z||!c(J)||c(L)||dt.find(e=>e.sketch===c(J)&&e.agent===c(j)&&e.running)&&await ht(c(J))}function Y(e,t){t.trim()&&(b(N,[...c(N),{kind:e,text:t}]),vt())}function _t(){if(!c(H))return;oe(H,c(H).style.height=`auto`);let e=Math.max(120,Math.round((c(V)?.clientHeight??400)/2));oe(H,c(H).style.height=`${Math.min(c(H).scrollHeight,e)}px`)}async function vt(){await e(),c(V)&&oe(V,c(V).scrollTop=c(V).scrollHeight)}async function yt(){let e=S();if(!e)return Y(`error`,`Studio has not registered the open document, so it cannot be saved before the prompt runs. Nothing was sent.`),null;let t=e.file();if(!t)return Y(`error`,"This graph is not a project file, so the agent has nothing to edit. Open the sketch through the `cascade` CLI, or save it into the project first."),null;if(e.isDirty()){if(!await e.save())return Y(`error`,`Could not save ${t}, so the prompt was not sent — the agent would have rewritten the file underneath your unsaved changes.`),null;Y(`system`,`Saved ${t} before handing the prompt over.`)}return t}async function bt(){let t=c(M).trim();if(!t||c(F))return;let n=await yt();if(!n)return;let r=c(G)?De(T()):null,i=Ce(n,t,r);z?.abort(),z=null,Y(`you`,t),b(M,``),e().then(_t),b(F,!0),R=new AbortController;try{await Ie({agent:c(j),sketch:n,prompt:i,signal:R.signal,onEvent:pt})}catch(e){e instanceof C?(Y(`error`,e.message),e.hint&&Y(`system`,e.hint)):e?.name===`AbortError`?Y(`system`,`Stopped.`):Y(`error`,e instanceof Error?e.message:String(e))}finally{b(F,!1),R=null,b(B,!1)}}async function xt(){let e=c(J)??S()?.file()??null;e&&await Ne(c(j),e),R?.abort(),z?.abort()}async function St(){let e=S()?.file();z?.abort(),z=null,e&&await Pe(c(j),e),b(N,[]),b(B,!1),Y(`system`,`New ${c(j)} session for ${e??`this sketch`}.`)}function Ct(e){e.key===`Enter`&&(e.metaKey||e.ctrlKey||!e.shiftKey)&&(e.preventDefault(),bt())}function wt(e){let t=e.dataTransfer?.getData(`text/plain`);b(U,!1),t&&(e.preventDefault(),e.stopPropagation(),At(t.trim()))}function Tt(e){let t=Array.from(e.dataTransfer?.types??[]);!t.includes(`text/plain`)||t.includes(`Files`)||(e.preventDefault(),e.stopPropagation(),e.dataTransfer&&(e.dataTransfer.dropEffect=`copy`),b(U,!0))}function Et(e){if(!c(W)){b(U,!1);return}let t=c(W).getBoundingClientRect();e.clientX>=t.left&&e.clientX<=t.right&&e.clientY>=t.top&&e.clientY<=t.bottom||b(U,!1)}let Dt=p(!1),Ot;async function kt(){let e=c(N).map(e=>e.text).join(`
|
|
5
|
+
`);if(await Te(e)){b(Dt,!0),clearTimeout(Ot),Ot=setTimeout(()=>b(Dt,!1),1200);return}Y(`error`,`Could not reach the clipboard. Select the transcript and copy it by hand.`)}function At(t){if(!t)return;let n=c(H)?.selectionStart??c(M).length,r=c(M).slice(0,n),i=c(M).slice(n),a=r&&!/\s$/.test(r)?` `:``;b(M,`${r}${a}${t}${i}`),e().then(()=>{c(H)?.focus();let e=(r+a+t).length;c(H)?.setSelectionRange(e,e)})}le(()=>{R?.abort(),z?.abort(),clearTimeout(Ot)}),v(()=>T(),()=>{b(E,T()?.selectedNode??null)}),v(()=>c(E),()=>{c(E)||b(G,!1)}),v(()=>(c(I),c(j)),()=>{b(D,c(I).find(e=>e.alias===c(j))??null)}),v(()=>(c(F),c(B)),()=>{b(O,c(F)||c(B))}),v(()=>(c(rt),c(D)),()=>{b(k,!c(rt)||!!(c(D)?.configured&&c(D)?.resolved))}),v(()=>c(N),()=>{b(A,c(N).filter(e=>e.kind===`detail`).length)}),v(()=>(c(P),c(N)),()=>{b(Le,c(P)?c(N):c(N).filter(e=>e.kind!==`detail`))}),pe(),ce();var X=et();d(`keydown`,ae,ut),d(`dragend`,ae,()=>b(U,!1));let jt;var Mt=y(X),Z=y(Mt);a(Z,5,()=>tt,o,(e,t)=>{var n=Be(),r=f(n,!0),i={};h(()=>{u(r,c(t)),i!==(i=c(t))&&(n.value=(n.__value=i)??``)}),l(e,n)}),g(Z),te(Z);var Nt=_(Z,2),Pt=e=>{var t=Ve();l(e,t)};x(Nt,e=>{c(J)||e(Pt)});var Ft=_(Nt,4),It=e=>{var t=He(),n=f(t);h(e=>u(n,`${e??``}%`),[()=>(c(q),m(()=>Math.round(c(q)*100)))]),d(`click`,t,()=>ct(1)),l(e,t)};x(Ft,e=>{c(q)!==1&&e(It)});var Lt=_(Ft,2),Rt=e=>{var t=Ue(),n=f(t,!0);h(()=>u(n,c(Dt)?`✓`:`⧉`)),d(`click`,t,kt),l(e,t)};x(Lt,e=>{c(N),m(()=>c(N).length>0)&&e(Rt)});var zt=_(Lt,2),Bt=e=>{var t=We();d(`click`,t,xt),l(e,t)},Vt=e=>{var t=Ge();d(`click`,t,St),l(e,t)};x(zt,e=>{c(O)?e(Bt):e(Vt,-1)}),g(Mt);var Ht=_(Mt,2),Ut=e=>{var t=Ke(),n=f(t,!0);h(()=>u(n,c(L))),l(e,t)},Wt=e=>{var t=Je(),n=y(t),r=f(n,!0),i=_(n,2),a=e=>{var t=qe(),n=f(t);h(()=>u(n,`"commands": { "${c(j)??``}": "<path>" }`)),l(e,t)};x(i,e=>{c(nt),c(D),m(()=>c(nt)&&!c(D)?.configured)&&e(a)}),g(t),h(()=>u(r,(c(D),c(j),m(()=>c(D)?.hint??`Command "${c(j)}" is not allowed.`)))),l(e,t)};x(Ht,e=>{c(L)?e(Ut):c(rt)&&!c(k)&&e(Wt,1)});var Gt=_(Ht,2),Kt=y(Gt),qt=e=>{var t=Ye(),n=y(t),r=e=>{var t=de(`No project file is open, so there is nothing for an agent to edit.`);l(e,t)},i=e=>{var t=de(`Say what the sketch should become. Drag in a node or a parameter for its exact path.`);l(e,t)};x(n,e=>{c(J)?e(i,-1):e(r)}),g(t),l(e,t)};x(Kt,e=>{c(N),m(()=>c(N).length===0)&&e(qt)});var Jt=_(Kt,2);a(Jt,1,()=>c(Le),o,(e,t)=>{var n=ne(),i=me(n),a=e=>{var n=Xe();r(n,()=>(re(Oe),c(t),m(()=>Oe(c(t).text))),!0),g(n),h(()=>s(n,1,`entry ${c(t),m(()=>c(t).kind)??``} markdown`,`svelte-1cqop5k`)),l(e,n)},o=se(()=>(c(t),m(()=>ze.has(c(t).kind)))),ee=e=>{var n=Ze(),r=f(n,!0);h(()=>{s(n,1,`entry ${c(t),m(()=>c(t).kind)??``}`,`svelte-1cqop5k`),u(r,(c(t),m(()=>c(t).text)))}),l(e,n)};x(i,e=>{c(o)?e(a):e(ee,-1)}),l(e,n)});var Yt=_(Jt,2),Xt=e=>{var t=Qe(),n=f(t);h(()=>u(n,`${c(P)?`Hide`:`Show`} ${c(A)??``} stream ${c(A)===1?`event`:`events`}`)),d(`click`,t,()=>b(P,!c(P))),l(e,t)};x(Yt,e=>{c(A)>0&&e(Xt)}),g(Gt),ue(Gt,e=>b(V,e),()=>c(V));var Zt=_(Gt,2),Qt=y(Zt),Q=y(Qt);let $t;var en=_(Q,2),tn=e=>{var t=$e(),n=f(t,!0);h(()=>u(n,(c(E),m(()=>c(E).id)))),l(e,t)};x(en,e=>{c(G)&&c(E)&&e(tn)}),g(Qt);var nn=_(Qt,2),$=y(nn);ie($),ue($,e=>b(H,e),()=>c(H));var rn=_($,2);g(nn),g(Zt),g(X),ue(X,e=>b(W,e),()=>c(W)),h(e=>{jt=s(X,1,`console svelte-1cqop5k`,null,jt,{"drop-active":c(U)}),t(X,`--agent-zoom: ${c(q)??``}`),Z.disabled=c(O),ve(Q,`aria-pressed`,c(G)),Q.disabled=!c(E)||c(O),$t=s(Q,1,`svelte-1cqop5k`,null,$t,{active:c(G)}),$.disabled=c(O),rn.disabled=e},[()=>(c(O),c(M),c(k),m(()=>c(O)||!c(M).trim()||!c(k)))]),_e(Z,()=>c(j),e=>b(j,e)),d(`click`,Q,()=>b(G,!c(G))),he($,()=>c(M),e=>b(M,e)),d(`keydown`,$,Ct),d(`input`,$,_t),d(`click`,rn,bt),d(`dragover`,X,Tt),d(`dragleave`,X,Et),d(`drop`,X,wt),l(i,X),n(),Ae()}export{tt as default};
|