Embody puts your ideas on screen as fast as you can describe them. Operators, connections, parameters, the works. Want to try a different direction? Spin up a new approach in seconds. Compare attempts side by side. Branch off the one that works. The tool keeps up with you, instead of the other way around.
Envoy — forward velocity. An embedded MCP server lets Claude Code, Codex, Gemini, VS Code, Cursor, and Windsurf talk directly to your live TouchDesigner session. Create operators, wire them up, set parameters, write extensions, debug errors — by saying what you want. No copy-pasting code. No describing your network in chat. Idea → operators in seconds.
Embody — lateral velocity. Tag any operator and Embody externalizes it to files on disk that mirror your network hierarchy. Try a new direction, branch off a good one, restore the state from yesterday — all in seconds. Your externalized files are the source of truth, so every project opens already in flow.
TDN — the substrate that makes both possible. TouchDesigner networks exported as human-readable YAML. The format is what lets your AI agent understand what's on the screen, what lets you diff one attempt against another, and what lets a network reconstruct itself from text on the next project open. TDN is what makes the rest of this possible.
| What | Why it matters | |
|---|---|---|
| 🤖 | Envoy MCP Server | 49 tools let your AI assistant build, wire, parameterize, and debug live networks. The first time you watch it happen, you stop typing operator names by hand for good. |
| 📄 | TDN Network Format | Networks become text. Diff two versions, revisit any version, hand an LLM a complete picture of what's on screen — all from a single .tdn file. |
| 📦 | Automatic Restoration | Externalized files are written on save, so any COMP can be recovered from disk. By default (Export-on-Save) the .toe stays authoritative on open; switch to Roundtrip mode to rebuild TDN-strategy COMPs from .tdn on every open. |
| 📤 | Portable Tox Export | Pull any COMP out as a self-contained .tox with external references stripped. Ship a piece of your project anywhere. |
Embody writes externalized files relative to your .toe location — no special folder structure required. Embody works in any project folder; if you happen to use git, every change is also a clean diff for free.
my-project/ ← project folder (optionally a git repo)
├── my-project.toe ← your TouchDesigner project
├── base1/ ← externalized operators
│ ├── base2.tox ← COMP (TOX strategy)
│ ├── base3.tdn ← COMP (TDN strategy — diffable YAML)
│ └── text1.py ← DAT
└── ...
- Download the Embody
.toxfrom/releaseand drag it into your TouchDesigner project - Tag operators — hover any COMP or DAT and press
lctrltwice to open the tagger (pick a strategy for a COMP, a file format for a DAT) - Work normally — press
ctrl + shift + uto update all externalizations, orctrl + alt + uto update only the current COMP. Externalized files are written on save; on open, the.toestays authoritative by default (Export-on-Save), while Roundtrip mode also reconstructs TDN-strategy COMPs from disk
Tip: If no operators are tagged, Embody will externalize all eligible COMPs and DATs, which may slow down complex projects. Tagging selectively is recommended.
| Shortcut | Action |
|---|---|
lctrl + lctrl |
Tag or manage the operator under the cursor |
ctrl + shift + u |
Update all externalizations |
ctrl + alt + u |
Update only the current COMP |
ctrl + shift + r |
Refresh tracking state |
ctrl + shift + o |
Open the Manager UI |
ctrl + shift + c |
Copy the selected COMP to the clipboard as a portable TDN envelope |
ctrl + shift + e |
Export entire project to .tdn file |
ctrl + alt + e |
Export current COMP to .tdn file |
For supported formats, folder configuration, duplicate handling, Manager UI, and more — see the Embody docs.
Embody includes Envoy, an embedded MCP server that gives AI coding assistants direct access to your live TouchDesigner session.
- Enable Envoy — toggle the
Envoyenableparameter on the Embody COMP - Server starts on
localhost:9870(configurable viaEnvoyport) - Auto-configuration — Envoy creates a
.mcp.jsonat your project root. By default this is the git repo root; set theAiprojectrootparameter toprojectfolderor a custom path to write it elsewhere. Projects without a git repo still get config generated in the.toefolder - Connect — open a Claude Code session (or restart your IDE) at that root — it picks up
.mcp.jsonautomatically
If you'd rather configure the client by hand, this is the .mcp.json Envoy writes:
{
"mcpServers": {
"envoy": {
"type": "http",
"url": "http://localhost:9870/mcp"
}
}
}| Tool | What It Does |
|---|---|
create_op |
Create any operator type in any network |
set_parameter |
Set values, expressions, or bind modes on any parameter |
connect_ops |
Wire operators together |
execute_python |
Run arbitrary Python in TD's main thread |
export_network |
Export networks to diffable .tdn YAML |
create_extension |
Scaffold a full extension (COMP + DAT + wiring) |
get_op_errors |
Inspect errors on any operator and its children |
...and 42 more. See the full tools reference.
When Envoy starts, it always generates an AGENTS.md file in your project root with TD development patterns and project-specific guidance. It also writes a client-specific config for whichever assistant you select in the Aiclient parameter (CLAUDE.md + .claude/ for Claude Code, Cursor/Windsurf rules, Copilot instructions, GEMINI.md for Gemini; Codex and VS Code read AGENTS.md directly).
TDN (TouchDesigner Network) is the file format that makes the rest of Embody possible. It exports an entire operator network — operators, connections, parameters, layout, annotations, DAT content — as a single human-readable YAML file. Your AI agent can read it. You can read it. Any text tool can diff it. The network can rebuild itself from it.
This is the substrate. Every other capability — AI-driven building, version control, automatic restoration — builds on top of it.
- Entire project:
ctrl + shift + e - Current COMP:
ctrl + alt + e - Via Envoy:
export_network/import_networkMCP tools
See the full TDN specification for format details, import process, and round-trip guarantees.
Logging
Embody provides a multi-destination logging system:
- File logging (default):
dev/logs/<project_name>_YYMMDD.log, auto-rotates at 10 MB - FIFO DAT: Recent entries visible in the TD network editor
- Textport: Enable the
Printparameter to echo logs - Ring buffer: Last 200 entries via the Envoy
get_logsMCP tool
op.Embody.Log('Something happened', 'INFO')
op.Embody.Warn('Check this out')
op.Embody.Error('Something broke')Testing
Embody includes 75 test suites (1,751 tests) covering core externalization, MCP tools, TDN format, the Envoy server/bridge, and palette catalogs. Tests run inside TouchDesigner using a custom test runner with sandbox isolation.
op.unit_tests.RunTests() # All tests (non-blocking)
op.unit_tests.RunTests(suite_name='test_path_utils') # Single suite
op.unit_tests.RunTestsSync() # All in one frame (blocks TD)Via Envoy MCP: use the run_tests tool. See the full testing docs for coverage details and how to write new tests.
Troubleshooting
- Timeline Paused: Embody requires the timeline to be running. An error appears if paused.
- Clone/Replicant Operators: Cannot be externalized. Embody warns if you try to tag them.
- Engine COMPs: Engine, time, and annotate COMPs are not supported for externalization.
For more, see Troubleshooting.
See the full changelog for detailed version history.
Recent releases:
- 6.0.66: A one-click Launch AI Client button on Embody's Envoy page -- pick your assistant in the
Aiclientmenu and Embody opens it at the project root: editors (VS Code, Cursor, Windsurf; Copilot -> VS Code) open the folder as a workspace, terminal CLIs (Claude Code, Codex, Gemini) open in a new terminal. It resolves the REAL app/exe (macOS LaunchServices via/usr/bin/open -b <bundle>; Windows the realCode.exe/Cursor.exefrom install dirs) -- never a hijackablecodeshim -- and runs CLIs through a login-shell terminal so~/.local/bintools resolve despite TD's Dock-truncated PATH. Fixes the Electron "dock bounces then closes" bug (TD setsELECTRON_RUN_AS_NODE=1, forwarded byopen; a new_launchEnv()strips it -- verified live with Cursor). Adds Gemini config (GEMINI.mdimportingAGENTS.md),codex/gemini/vscodemenu tokens, per-tool install hints, and gitignores other clients' generated configs. Hardened by a 10-agent codex cross-platform review (crash-safety,boolhelper contracts, Windows shimshutil.which+ cmd doubled-quote metachar-safety,${SHELL:-/bin/zsh}); macOS verified live, Windows review-verified. Newtest_launch_aiclient(17) + 7test_claude_configtests. Test suite 75 suites / 1,751 tests. - 6.0.62: A performance-rule expansion shipped in the
.tox.rules/performance.md(and itstext_rule_performance.mdtemplate) gains a complete Movie Export / Offline Rendering playbook so an AI agent recording a movie never ships a juddered file: the Realtime flag (project.realTime, ON by default) silently replicates any frame TD can't cook within thecookRatebudget -- the file ends up the right LENGTH but full of duplicates -- so the rule mandates going non-realtime, restoring the prior flag through one_finish(prior)exit helper (there is notry/finallyacross the asyncrun(delayFrames=...)driver), monitoring the Movie File Out Info CHOPtotal_frames_droppedduring the render and aborting on the first drop, and proving the result with SEPARATE length and uniqueness checks (total_frames_dropped == 0+ffmpeg mpdecimate/framemd5). Plus a deterministic per-frame export recipe and a correction thatperformLongOperationis not a real API. Agent guidance shipped in the build; test suite unchanged at 74 suites / 1,727 tests. - 6.0.61: An Embot polish pass that kills the spawn-time frame drops. Copying an annotateCOMP into the network you're viewing pays a ~280ms in-viewport redraw (and a block copy of all 9 parts crashes TD outright --
copyOPs,ui.pasteOPs, and a redraw-suppressed block all crash on repeat into a displayed net), but copying it outside the viewport costs ~100ms; so Embot now assembles at an off-view staging point and swoops in once whole, turning a multi-second fps sag into a clean entrance (dives still snap in place, already cheap). Plus paced/ordered assembly (body -> head -> speech -> limbs -> eyes, one part every 32 frames so hitches stay isolated), an occasional happy squint (eyes flatten + spread every ~9-17s; eyes bumped to 12x13 so the squint clears TD's 10px annotation-size floor), and a shrug that lifts the arms instead of stretching them. Runtime character/camera behavior inEnvoyExt; test suite unchanged at 74 suites / 1,727 tests. - 6.0.57: A live-build-visualization split plus a major embody.tools Collection upgrade. In TouchDesigner, the opt-in build visualization (v6.0.54) becomes two independent toggles -- Embot (the builder character that stands on each operator and narrates, in past tense, what he just did) and Envoy Follow (the network-editor camera that pans to the active op) -- and the camera now frames the operator, so it follows Envoy's work with or without the character shown. A new
_ensureVizParams()recreates both toggles if missing so the feature survives a restart (they were added live and vanished); Embot's per-frame assembly is restored over a blockcopyOPsimplicated in crashes; and follow no longer freezes for ~6s when TD auto-frames a freshly-spawned node. On the web: specimens gain multiple categories (up to 3, via a newspecimen_categoriesjoin table + D1 migration0010, ANY-match facet filtering), private drafts with a one-click publish/unpublish toggle and an owner profile that splits public/private with a list/gallery view + inline edit/delete, a fixed license picker (SPDX vocabulary replacing free text), a better TDN editor (search match counter, go-to-line, paste-unwrap) and viewer (operator/annotation jump menu), an optional edit-time cover image, and a privacy pass that self-hosts Inter (drops the Google Fonts CDN) alongside new cookie + copyright/DMCA pages. Test suite 74 suites / 1,727 tests (the visualization split is runtime UI; +2 web e2e cases). - 6.0.55: A clipboard UX fix. Copying a COMP's network with
Ctrl+Shift+Cno longer immediately prompts to paste it back into TouchDesigner -- the clipboard watcher now distinguishes your own outbound copy (exported to share or paste elsewhere) from an inbound TDN (the web "embody it" button).CopyNetworkToClipboardseeds the watcher's last-seen signature with what it just wrote, so an outbound copy is skipped while a genuinely new inbound TDN still prompts. Smoke-tested clean in the shipped.tox. Newtest_outbound_copy_does_not_prompt+test_inbound_after_outbound_still_prompts. Test suite 74 suites / 1,727 tests, all green. - 6.0.54: A crash-resilience build. Embody now writes a cheap
.tdncheckpoint of whatever changed after the agent (or you) goes idle -- no full project save, no TDN strip/restore, no frame freeze -- so a crash (often agent-induced during a heavy build) loses little unsaved work, and the checkpointed COMPs rebuild on next open. The trick was that a normal export's cost is therglobstale-file scan, not the write, so a newskip_cleanup=Truepath makes a single-COMP checkpoint ~3-6 ms synchronous; it triggers on a ~1s idle-settle (and before a destructivedelete_op), recovers crash-lost COMPs from disk (the tracking table is asyncfile, so rows persist without a save), is bypassed in Perform Mode and during saves, and self-heals its Auto-Save Checkpoints toggle onto older.toes. A 20-agent adversarial review (10 codex + 10 claude) caught and fixed real data-loss / crash / perf defects pre-merge. Also: an opt-in Envoy Follow live build visualization (the network editor glides/navigates to follow Claude's work, with a little builder-bot that hops between nodes), agent threading guidance (Web Client DAT / Thread Manager, not worker threads), and a web contribute-form fix. Newtest_autosave(18). Test suite 74 suites / 1,725 tests, all green. - 6.0.49: A generated-file-safety + web-polish build. Re-running Envoy's config generation now PRESERVES your edits to generated rules/skills instead of clobbering them --
_writeTemplaterecords a SHA-256 of each generated file in.embody/generated-hashes.jsonand skips any whose on-disk content drifted (delete the file to opt back in); files stay byte-identical to their templates. The v6.0.47 annotation dedup reached Embody's own self-externalized.tdnfiles (9 redundantannotateCOMPoperator copies + dead templates dropped, 840 lines, every annotation byte-identical -- confirmed a safe dedup by a 20-agent adversarial review). embody.tools got a deep polish pass: correct TD family colors and dotted op-reference edges in the network viewer, always-prevented node overlap + wires that arc over same-row nodes, a "by user" Collection filter, foldableoperators:/annotations:in the raw-TDN YAML viewer, a sidebar specimen layout, and a fixed 3-10s page-load freeze. New tests B08-B12 intest_claude_config. Test suite 73 suites / 1,707 tests, all green. - 6.0.47: A TDN-format cleanup. Annotation COMPs are no longer double-captured in
.tdnexports -- a stock TD annotate is a palette clone with an extension and ~40 custom parameters, so the exporter was writing it both as a 100-205 lineoperators:entry and in the compactannotations:array;_exportChildrennow skipsannotateCOMPchildren (the importer already rebuilds them fromannotations:, Phase 7a).build: nullis omitted from the header for untracked/portable networks, and all 12 gallery specimens were cleaned (-2,887 lines, pure deletion, validated by live re-import and clean reconstruction on project open). Save UX: the at-risk content check no longer logs a misleading[test]warning on every Ctrl+S -- the test gate and the save gate are now separate (a real test still warns; a save stays quiet). Newtest_tdn_annotation_export(7) + 2test_dialog_suppressiontests; the shippedEmbody-v6.0.47.toxpassed a fresh-install smoke test with both TDN fixes confirmed running live. Test suite 73 suites / 1,702 tests, all green. - 6.0.46: A docs-accuracy + web-polish build. A multi-agent audit reconciled the entire docs site, the AI machine-files (
llms/for-ai), and this README to the live source: the stale "inert by default" community-paste wording is now the real clean/flagged/blocked verdict model; counts corrected (72 suites / 1,693 tests; 49 MCP tools); API/shortcut examples fixed in the docs and the shipped AGENTS template (Ctrl+Shift+Oopens the Manager,getExternalizedOps(COMP)); andllms-full.txt's embedded TDN spec regenerated to v2.0 (ASCII-folded,application/yaml). embody.tools gained an app-native report dialog, a simplified specimen network-preview header, a centred/contributeform (renamed from/submit), and a themed 404. Plus minor custom-parameter organization. - 6.0.44: Community specimens paste in LIVE and working. The safe-import was zeroing EVERY parameter expression (a published specimen's GLSL uniform bindings, resolution, and animation drivers all collapsed to 0 -- a dead frame), so it now preserves provably-pure value expressions (par reads,
absTime,math.*,Par.eval(), arithmetic) via a new AST allowlist and disarms only genuinely side-effecting surfaces; a clean specimen pastes with no warning (live-if-scanned-clean). Fixes the scanner false positives on.eval()/.store()/tdu/GLSL DATs, trusts TD palette extensions (with anopshortcut-hijack defense), and closes adjacent holes (Script OPs bypassed,tox_refstripped, cooking suspended during untrusted import). Paste UX: the new COMP auto-selects and the view pans to centre it (via writablepane.x/y), and the auto-paste prompt fires only while the TD window is the OS-frontmost app (NSWorkspace / GetForegroundWindow). Newtest_collection_pure(14) + standalonetest_safe_import_pure(25) + a 70-case validator corpus. Test suite 72 suites / 1,693 tests. - 6.0.42: Clipboard auto-paste -- bring a TDN into your network with no keyboard shortcut. The conflicting Cmd-Shift-V paste binding is removed (TD's native operator-clipboard paste fires on the same keystroke and can't be suppressed, pasting leftover nodes). Instead Embody watches the OS clipboard (
ui.clipboard) and, when a TDN network appears (web "embody it" button or Cmd-Shift-C), prompts to Embody it into the current network as a new COMP -- debounced, gated on a new Clipboard Auto-Paste toggle, skipped in Perform Mode, and suppressed during saves/tests.test_clipboard_watch(5); no regression. Test suite 71 suites / 1,678 tests. - 6.0.41: The git-uncommitted status axis -- a second manager status axis (after diff_tdn in 6.40), completing the v5.0.437 feature set on the v6 line. Externalized files saved to disk but not yet committed to git now show a distinct orange Strategy badge, kept separate from the red "unsaved" axis. An async
git status --porcelainworker scan (no frame drop,--no-optional-locks) maps changes to op paths and stores them at runtime; achangedfilter keyword shows rows with pending changes on either axis; a shipped refresh-after-commit rule clears the badges after a commit. TheUncommittedcolorparam (already in v6) is now wired.test_git_status(20 tests); full data path verified live. Test suite 70 suites / 1,673 tests, all green. - 6.0.40: The diff_tdn release. Re-integrates the
diff_tdnMCP tool (what is UNSAVED in a TDN network -- the live network vs the on-disk.tdn, the view git can't give -- one COMP or the whole project) and its companion.tdngit textconv driver (keeps committed.tdndiffs clean by stripping the volatile export header) from v5.0.437 into v6's YAML v2.0 world. The textconv is now YAML-aware and the Embody venv carries PyYAML so git's diff driver no longer silently falls back to a raw, noisy diff; the diff engine reconciles a legacy v1.5 arraydat_contentwith the v2.0 string form.get_externalizations/get_externalization_statusnow hintrecommended_tool: diff_tdn. A 4-lens adversarial panel caught two real regressions pre-merge (a dropped_get_externalizationsenrichment; four broken setup-environment tests), both fixed and verified. Test suite 69 suites / 1,653 tests, all green. - 6.0.39: The save-resilience release. The Envoy liveness watchdog now actually self-heals a save-time wedge — its revive cooldown measured a ~2s anti-spam window in
absTime.frame(frames since app launch, resets to 0 each launch) but stored that value in COMP storage, which persists into the.toe/.tdn; a high frame baked from a prior session made every revive compute a negativenow - storeddelta (always "< 2s ago"), so_reviveDeadServerreturned before scheduling the restart, every time, permanently — the watchdog detected the wedge forever but couldn't fix it (MCP stayed down after a save until a manual toggle). The cooldown now usestime.monotonic()on an instance attribute (neverabsTime.frame, never storage) and__init__scrubs the obsolete_last_revive_frame; verified on a real save — the Envoy drop self-heals in ~1s. The watchdog also now trusts the socket, not_init_complete/_starting(both reset by a save, which used to idle the tick on a dead server), andStart()probes the socket before trusting a stale "Running" status. The "Enable Envoy?" onboarding modal no longer fires during a save or test — a single_suppressDialogs()predicate (test run active OR save in progress) gates theVerify()queue site, the deferred_promptEnvoy, and_messageBox, with a_suppress_dialogssave-window flag scrubbed on next open; the file-cleanup and deprecated-externaltox prompts are gated the same way. Plus 169 new v6 tests across 9 suites (clipboard copy/paste 42, collection scanner 22 + safe-import 18, v6 hardening 20, specimen publish 19, the watchdog 21, GLSL externalize 11, layout lint 10, dialog suppression 6) and a layout-lintmaxDepthfix (the v6.0.34 lint usedfindChildren(depth=12)— exactly depth 12, matched nothing). Test suite 67 suites / 1,616 tests, all green. - 6.0.34: Everything since v6.0.26 in one release. GLSL shader DATs now externalize as
.glsl, not.py—EmbodyExt._externalizeDATsinferred the tag from a baredat_type_to_tagmap (['text']='Pytag'), so shaders (typetext, languageglsl) were written as Python; it now resolves the tag from DAT content via_inferDATTagValue, and the 8 newer Specimens' 42 shaders were re-externalized to.glsl. Layout lint at the Envoy tool layer —execute_python(rawcomp.create()/.copy(), no auto-position) was the recurring source of ops piled at (0,0); Envoy now snapshots the op tree before your code runs and emits aLAYOUT WARNINGon the response when the call leaves ops at (0,0), overlapping, or with docked DATs >500u from their host (_lintLayout/_lintNewOps), withnetwork-layout.md+ template DRY'd around the enforcement. Specimen publish hook (specimen_publish.py) — a projectonProjectPostSavehook exports each manifest Specimen self-contained (DAT scripts embedded) tospecimens/<tdn_path>for the embody.tools "embody it" copy-paste, skipping unchanged files. Waveform-stack feedback fix — broke a Feedback-TOP cook-dependency loop by seeding from outside the loop (res_fb) and grabbing the frame-delayed state from its Target TOP. Plus six landscape transmission specimens (essence-streams, vertical-fibers, crosspoint, waveform-stack, packet-fabric, hyper_ntsc; reaction-diffusion landscaped). Test suite 58 suites / 1,439 tests, no regressions. - 6.0.26: A correctness + efficiency release. TDN custom-parameter values now round-trip — exporting a COMP with custom parameters and re-importing reset every value to 0/min (the exporter omits a value equal to its default, but the importer set
.defaultand never.val), which broke every parametric specimen on import; fixed with a default→value fallback in_setCustomParValuesplus 6 regression tests. The save-time watchdog log storm is gone — aproject.save()reinitializes EnvoyExt in a rapid same-frame burst, and ~4s later all the armed liveness-watchdog ticks came due in one frame and each revived + logged ("MCP socket on port None unreachable — reviving server" ×18-21 per save); a monotonic generation token collapses the leftover tick loops on the next reinit, and a frame-cooldown in_reviveDeadServercollapses the same-frame burst to one log + one revive (verified on a real save: 21 → 1), with the genuine self-heal preserved. The pre-save "TDN Content at Risk" dialog no longer fires on annotated specimens — the content-safety scan was walking into palette-clone COMPs (an annotateCOMP's buttonhelptables) and flagging their regenerable internals as user content at risk, so every TDN-tagged COMP containing an annotation popped the dialog on each save; both scans now skip anything inside a palette clone (verified live, 11 at-risk → 0). MCP token-efficiency pass — tool results stay in context, so four changes cut response size: the per-response_logspiggyback is now WARNING/ERROR-only (was up to 20 routine-INFO entries on every call),run_testsreturns counts + failures only (drops ~1,400 PASS objects),export_networkto a file returns a compact summary instead of echoing the whole.tdn, andcapture_topreturns the file path by default (passinline=truefor an embedded preview). Murmuration joins the Specimen Collection (4th) — a dense GPU particle swarm flocking like a starling murmuration at dusk: true per-neighbor Reynolds (a Neighbor POP index list iterated in a GLSL POP for cohesion/alignment/inverse-square separation), a moving attractor, curl-noise, soft containment + drag, rendered as additive point sprites with a dusk speed-ramp + bloom. The TDN clipboard Copy/Paste loop is now complete —Cmd-Shift-Ccopies the selected COMP (v6.0.11 shipped paste but never wired copy — its "Copy button" never existed),Cmd-Shift-Vnow also accepts a raw.tdnfile's text (sandboxed/inert, since bare text has no provenance —ImportNetworkFromFileis the trusted/direct path for local files), the clipboard envelope is pretty-printed, and a pasted COMP is named from itsnetwork_pathbasename instead ofpasted_tdn. POP point-sequence import fixed —op.seq['pt']subscript returnsNonefor POP sequences, so importing aptsequence silently dropped its points; resolution now iterates. Plustest_tdn_file_io/test_tdn_helperswere updated for TDN v2.0 YAML (33 tests red since the v2.0 migration, now green). Test suite 57 suites / 1,439 tests, all green. - 6.0.16: The on-disk
.tdnformat graduates to TDN v2.0: YAML. A network now serializes as a single self-contained YAML document instead of JSON, so a.tdnreads top-to-bottom like the network it describes. Multi-linedat_content(GLSL, Python, anytextDATscript) is stored as a plain string rendered as a YAML literal block scalar (|) — source reads like code with no escaped newlines and diffs line-by-line (this reverts the v1.5 array-of-lines workaround). Round-trips are byte-exact (trailing newlines preserved via|/|-/|+chomping) and deterministic (no key reorder, no anchors), so re-saving an unchanged network produces no diff. Legacy JSON.tdnstill import — importers parse json-first (BOM/whitespace stripped), so back-compat is independent of any YAML C library and tab-indented legacy files load losslessly; migration is lazy (rewritten on next save). Auto-created default docked compute DATs are no longer serialized (TD recreates them on import), and with the YAML representation files are roughly 17% smaller. MIME type is nowapplication/yaml. Docs (spec, examples, schema guide, supported formats) rewritten for v2.0. - 6.0.11: Envoy's MCP connection now self-heals across saves and reinits — the end of "connected:false while TouchDesigner is still running". A liveness watchdog tied to the EnvoyExt instance lifetime (armed from
__init__, one loop per instance) probes the socket every ~4s and revives Envoy whenever it's enabled-but-down — a dead socket, or aproject.save()/ extension reinit that took the server down — force-freeing port 9870 if it's still held and rebinding in ~1s, with no restart and no manual toggle. The priorStart()-armed watchdog missed the save case: a mid-save reinit suppresses the old server thread's exit callback (no auto-restart) and can skip or race the new instance's auto-start, orphaning aStart()-only watchdog. Verified — a live-listener kill self-heals in ~6s, and three consecutiveproject.save()cycles each fired the watchdog (running=False), force-freed the held port, and rebound in ~1s. Plus: TDN clipboard Copy/Paste (the Copy tdn button writes a portable_embody_tdnenvelope; Ctrl+Shift+V pastes it as a new COMP), with community TDN (embody.toolssource) scanned and defaulted inert — Execute DATs disarmed, expressions neutralized, IO bypassed, storage stripped, content preserved — via aCollectionExtextension and self-contained scanner / safe-import DATs (clipboard logic now lives inside the Embody COMP, portable in the.tox). New always-loadedperformance.mdrule (metric-gating protocol + wiki-cited crash-cause table + safe-default caps) andvisual-aestheticsskill (objective composition / value / color / contrast / motion / finishing + a mandatorycapture_toppreview-and-judge loop); preview-and-judge reinforced across create-/debug-operator, mcp-tools-reference, and CLAUDE.md;td-connectivitydocuments the watchdog;network-layouthardened against theexecute_python/.create()(0, 0) bypass — all deployed to user projects via the template map. First v6 Embody/Envoy changelog entry (earlier v6.0.x builds were the embody.tools platform underplatform/). - 5.0.429: A friendlier "Duplicate Path Detected" dialog. New
Template Master Nameparameter (Templatemaster, default__template__): when a group of operators sharing one external path has exactly one whose path contains that name as a whole segment (e.g. a__template__parent COMP), it's auto-selected as master and the rest taggedclonewith no prompt — the durable fix for the common template-plus-runtime-copies pattern (ascene_<id>chain each carrying the template's externalized DATs). Opt-in by convention (projects not using the name are unaffected; set your own like_master, or clear to always choose manually); matches a whole segment not a substring, and only on an unambiguous single match. The manual prompt no longer shows N identical same-named buttons — each is now labeled by the differing path segment, numbered to the dialog body (1: __template__,2: scene_1exalohf, …) — and groups larger than 5 operators get a Keep first as master / Dismiss strategy prompt instead of an unreadable button row. Test suite 57 suites / 1,413 tests (+12), all green. - 5.0.428: Everything since v5.0.414 in one release. Headline is
tdn_exclude— aTdnexcludetagparameter (defaulttdn_exclude) that makes a COMP invisible to the TDN system (never exported, stripped, or reconstructed), the durable opt-out for app-managed children under cascade-autotag (runtimeop.copy()content like Moonshine'sproj_<id>chains). Exclusion is honored at a TDN boundary's direct children; a COMP tagged but nested under a non-excluded intermediate is serialized as normal content and preserved (with a warning) rather than silently lost. TDN dirty detection rebuilt: the fingerprint captures each operator's non-default authored parameter values (expr/bindExpr/val, neverpar.eval()), so a parameter edit flags a TDN COMP dirty while a dependency-driven change to a live expression's evaluated value (animation, audio, a moving CHOP) does not — no more perpetual re-export churn on animated COMPs. The sweep is consolidated to one fingerprint per Refresh (was two) for a real frame-time win on large networks;DirtyCountreads the fingerprint result instead of the always-Trueoper.dirty; a reverted edit clears the dirty flag. Envoy resilience: startup status waits for a real bind handshake before reading "Running" (no optimistic lie, including uvicornSystemExit);restart_tdvalidates real non-zombieTouchDesignerPIDs viaps; the status moved into the window header, prefixed "Envoy". Three issue #21 crash fixes hardened across the whole table-read surface:captureParameterscan't crash on broken expressions (authored read, no eval),_cellValguards every externalizations-table read (and warns on genuine row-level corruption), andonProjectPreSaveis fail-safe end-to-end so a hook exception can't truncate the.toe. Plus: the "TDN Content at Risk" dialog gains a persistent Always Skip;externalizations.tsvno longer churns phantom timestamp rows per save; a calmer first-launch palette scan; the manager expand/collapse glyph on top-level rows; a duplicate-BOM fix inWindowHeaderExt; an AI-first Quickstart page; MCP tool count reconciled to 48. A final 7-angle regression review of the branch caught and fixed the live-expression churn, the nested-exclude data loss, the always-dirty count, the stuck dirty flag, the duplicate BOM, and the double sweep — each with a regression test. Verified by a fresh-install smoke test of the release.tox. Test suite 57 suites / 1,401 tests, all green. - 5.0.414: Third value
CustomforAI Project Root(follow-up to Ten0's feedback on issue #19) — paired with a newAI Project Root (Custom)Folder parameter that's greyed out unless the menu is set toCustom. The custom path can be absolute or relative to the.toedirectory (e.g.../). For monorepos where multiple.toefiles share a parent directory, each.toesets the same relative path and they all converge on oneAGENTS.md/.claude//.mcp.json/.embody/envoy.json— which lets the multi-instance MCP feature work naturally across sibling projects. Flipping the menu or changing the path migrates Embody state and AI config to the new location, same atomic move + marker-aware cleanup as the gitroot↔projectfolder flip. Plus two defense fixes from earlier in the cycle:_messageBoxno longer falls back to a real modal dialog when seeded test responses are exhausted (would freeze TD with stacked dialogs after a test run);Verify()won't re-queue the Envoy opt-in prompt while one is already pending (idempotent flag — multipleVerify()calls during a test's Disable/Enable cycle can no longer stack N prompts)._findSettingsFilegot a walk-up fallback to handle the Custom-mode chicken-and-egg (saved custom path lives inconfig.jsonwhich can't be read until settings are restored — so walk up from the.toelooking for any.embody/config.json). - 5.0.413: Two independent bodies of work bundled into one build. (1) Issue #20: parent
.tdnfiles no longer embed the contents of TOX-externalized child COMPs — emits atox_refpointer instead, mirroring the existingtdn_refpattern; round-trip restoration handled by a new Phase 8.5 (_restoreTOXShells) that setsexternaltoxand calls_reloadToxafter import. Plus_validateTOXRefsfor cross-validation parity, the_stripNestedTOXChildrenbackward-compat path for pre-v1.4 files, and TDN format version bumped to 1.4 with the schema updated accordingly. Envoy-toggle frame-drop fix surfaced during diagnosis —_findAvailablePortwas paying a 1.5stime.sleepon the main thread (108 frames at 60fps) whenever the preferred port was held by any listener, including foreign zombie TD processes the wait could never free. Now skips the wait when force-close has nothing of ours to close (caps at 500ms when it does), and branches directly to range-scan when the holder is a foreign live instance in our registry. Measured: Start time 1797ms → 346ms. (2)AI Project Rootparameter (gitrootdefault /projectfolder) for monorepo setups where the TouchDesigner project lives in a subdirectory of a larger repo — flips Embody's AI/MCP config target between the git root and the.toedirectory, with full migration of persistent state and marker-aware cleanup of the old root that preserves user-authored files. Issue #19 fix — thePath.home()length comparison in_findProjectRoot/_findGitRoot/_checkOrInitGitRepobailed before searching when the project lived on a non-home drive (Windows D:\ with home on C:), so subsequent runs failed to find.git, duplicated.mcp.jsonconfig, and broke the MCP connection. Also: registry I/O (RefreshRegistry,_removeFromRegistry, port-conflict detection) routes through_findProjectRoot()for consistency underprojectfoldermode;_atomicMovehelper (copy-to-tmp +os.replace+ unlink) makes cross-filesystem catalog moves safe against partial writes; settings-restore checks both Aiprojectroot candidate roots; orphan-handling renames any leftover critical files after a failed migration to.orphanso fallback lookups don't pick up stale data; legacy file sweep removes old.envoy.json,.embody.json,.envoy-tools-cache.json, and.claude/envoy-bridge.pyfrom previous Embody versions. 6 new tests;test_tdn_file_io66 → 92. - 5.0.407: Critical Windows-only crash hotfix introduced by v5.0.402's registry GC.
_isPidAlive(pid)was built onos.kill(pid, 0)— on Windows, CPython implements that asOpenProcess(PROCESS_ALL_ACCESS) + TerminateProcess(handle, sig)for all sig values, so the "liveness check" literally told the OS to kill the process being checked. Any time_writeEnvoyConfig's GC loop iteratedinstancesand the row contained the running TD's own PID (which it does any time the project has been saved with Envoy enabled), the loop calledTerminateProcess(self_handle, 0)and TD exited with code 0 — silent, no traceback, repro fingerprint matched perfectly. Replaced with safeOpenProcess(SYNCHRONIZE)pattern via ctypes (already in use byenvoy_bridge.is_process_alive); SYNCHRONIZE access doesn't include termination rights. Also rewired the duplicateos.killinside_findAvailablePortthat would have killed any foreign live TD whose registry entry shared the port. Plus:CatalogManagerpalette scan now snapshots and restorestime.play/time.rate/cookRate/realTimearound each chunk so a misbehaving palette component (e.g. the refactoredPalette:logger v2.7.0on TD 2025.32820) can't leave the timeline paused;_verifyMcpImportableshort-circuits whenmcp.serveris already loaded instead of tearing down 82 submodules every toggle; bridgefind_all_td_pids()filters CEF/Web Render helper subprocesses that were generating ~214k phantom "TD process detected" log lines per session;_osLabel()disambiguates Windows 11 from Windows 10 (both report NT 10.0 — discriminator is build ≥22000);execute_src_ctrlreads/writes README as UTF-8 so emoji don't crash the bumper on Windows non-UTF-8 code pages. 19 new tests across 3 new files. Test file count 50 → 53. - 5.0.403: Hotfix for v5.0.402 —
EmbodyExt.Update()rename-detection usedself.ownerComp(an EnvoyExt-only attribute) instead ofself.my. Every Update tick during a save threwAttributeError, which got caught and logged at WARNING but meant the rename-detect path never actually fired. Layer 2 walk-forward in the bridge masked the symptom (lookups still resolved correctly), but the registry would have stayed perpetually keyed to the previous version. One-character fix. - 5.0.402: Three follow-on registry fixes after v5.0.401 verification surfaced edge cases.
_writeEnvoyConfignow garbage-collects dead-PID rows on every write — registries that previously accumulated dead entries across sessions (hard kills, force-quits, crashes) collapse to live-only on the next save (verified: 28 rows → 1 in one cycle).EmbodyExt.Update()caches_last_toe_nameand triggersRefreshRegistry()on basename mismatch — defensive backstop forexecute.py's postSave hook in case it didn't reload. Bridgehandle_launch_tdadds a PID-aware slow-path scan after the fast-path key lookup: walks-forward each alive instance's registeredtoe_pathand refuses if any resolves to the same target — fixes the stale-key + walk-forward composition that could otherwise spawn a duplicate TD. 6 new tests acrosstest_envoy_registryandtest_envoy_bridge. - 5.0.401:
envoy.jsonregistry walks forward across TD's save-time .toe version bump (Foo-5.398.toe→Foo-5.399.toe). Two-layer fix:EnvoyExt._instanceKeyand_writeEnvoyConfigdetect a PID's existing row under a different basename and rename + prune in one write; newRefreshRegistry()is called fromonProjectPostSaveso the registry walks forward even when Envoy doesn't restart. Bridge-side defensive layer:find_latest_versioned_toe()strips<digits>.toeand finds the highest-versioned sibling on disk;resolve_toe_path()now reads frominstances[active](was legacy-flat-only) and routes through the walk-forward helper. Plus a hotfix for the postSave's'EnvoyExt' object has no attribute 'port'crash —RefreshRegistry()now reads the running port from envoy.json by PID instead of a nonexistent instance attribute. 20 new tests acrosstest_envoy_registry(7) andtest_envoy_bridge(13), one updated for the v5.0.399 instance-specific guard. - 5.0.399: New
edit_dat_contentMCP tool for token-efficient surgical text edits — mirrors Claude Code's Edit tool (old_string/new_string, unique match by default, opt-inreplace_all), so a 2-line edit in a 500-line DAT no longer pays for the whole DAT's content on the wire. Bridge multi-launch fix: Envoy can now launch a TD instance alongside an unrelated TD project —handle_launch_tdswapped the blanket "any TD running" guard for an instance-specific check, macOSopen -nflag forces a new process instead of reusing an existing window, and PID identification diffs against a pre-launch snapshot instead of returning the first TD pid found. Plus 11 new tests for the new tool and a one-line fix fortest_set_dat_content_clearthat had been failing since v5.0.397's wipe guardrail. - 5.0.398: Hotfix for a latent race condition that broke the first-install dialog flow on fresh-project drops without a cached catalog.
Update()raced withEnsureCatalogs(), which setsStatus='Scanning defaults (X/N)'to show progress. The old gateif Status != 'Enabled': returnreturned early on that transient value, so_pending_envoy_promptwas never consumed and the Envoy opt-in dialog never appeared. Both gates (Update,ReconcileMetadata) now short-circuit only when Status is explicitly'Disabled'. Plus 2 regression tests that fail without the fix. - 5.0.397:
confirm_wipeguardrail onset_dat_contentMCP tool blocks accidental DAT wipes from malformed agent calls (refusestext="",rows=[], orclear=Truewith no replacement unless explicitly confirmed); TDN at-risk dialog skips TD-managed read-only DAT types (Info, WebRTC, Folder, Monitors, device-discovery, etc.) so the warning only fires for content the user actually authored;.embody/config.jsonnow byte-stable across saves via sorted iteration of_PERSISTED_PARAMS+sort_keys=True(issue #18); test debt cleanup of 28 orphan.txtfiles, 3 stale envoy_bridge stubs replaced with 6 real tests, ancestor_rename tearDown leak fixed, palette tdn_reconstruction tests aligned with current production contract - 5.0.393: Harden Envoy bootstrap so silent failures surface a useful textport message instead of
No module named 'mcp.server'—_setupEnvironmentnow returnsbool, four previously-silent failure paths log explicit errors,Start()aborts before_runServerif deps aren't ready, and a finalimport mcp.servergate catches partial installs (issue #17) - 5.0.392: Critical Windows-only fix —
subprocess.runfrom inside TD raised[WinError 50] The request is not supportedbecause TD's GUI process stdin handle isn't duplicatable, causing Embody's venv-verify to falsely flag healthy venvs as corrupt andshutil.rmtreethem on every TD restart. Fixed by passingstdin=subprocess.DEVNULLon the 5 affectedsubprocess.runsites in the bootstrap and verify-venv paths - 5.0.391: Per-project TouchDesigner build pinning (committed
.embody/project.json+ Envoy bridge auto-discovers the matching install on fresh clones), thread-conflict fix in the MCP update checker, and a 21-assertion cleanup of bridge tests that had been silently broken since the bridge v2 refactor — bridge tests now 148/151 passing, zero failures - 5.0.386: Batch-confirm prompt for duplicate path detection — when multiple groups remain unresolved, one dialog offers
Auto-resolve all/Review individually/Dismissinstead of a separate modal per group - 5.0.383: Clone detection fix for self-referencing masters (reusable UI components using
iop.*expressions), and a cleaner list UI moving the tree expand/collapse indicator into a dedicated column - 5.0.381: Global Perform Mode toggle disables Embody/Envoy/TDN compute during TD performance (Issue #13), auto-resolve duplicate DATs inside active clones without prompting (Issue #15), ancestor-rename disk handling now uses the externalizations folder prefix so
Moveno longer fails with "source folder not found" (Issue #16), new render-coordinate-system rules for TD's bottom-left origin convention (Issue #14) - 5.0.376: Palette scan skips invasive palettes (TDVR framerate popup, AutoUI widget dialog) on fresh-build startup, rebaked palette catalog covers TD 2025.32460, and false "locked content" warnings suppressed inside clones/replicants (Issue #12)
- 5.0.372: TDN three-mode master switch (Off/Export-on-Save/Roundtrip),
read_tdnMCP tool (~20-90× fewer tokens thanget_opwalks), combined DAT+storage Content Safety dialog with "Never Ask" footgun removed, palette detection fix for nativebuttonCOMPoperators, migration nudge for upgrading users, docs + landing page rewrite - 5.0.362: Palette handling control (Ask/Black Box/Full Export) during TDN export, CatalogManager fires on fresh project drops, palette catalog portability + log-level fixes
- 5.0.356: Palette catalog detection, animationCOMP keyframe preservation, external wire preservation across TDN strip/rebuild
- 5.0.354: Consolidate runtime files into
.embody/folder, fix bridge path resolution - 5.0.352: Fix Envoy failing to start after Embody upgrade (restart counter, port race, reclaim timeout)
- 5.0.351: Creation-defaults catalog, stdin-based bridge lifecycle, Envoy resilience hardening
- 5.0.336: Batch MCP operations, Envoy auto-restart on crash and save, 46 MCP tools
- 5.0.330: Envoy bridge v2 — proactive reconciliation, multi-session safety, zero forced restarts
- 5.0.320: TDN v1.3 — parameter sequence round-trip + companion DAT handling (GLSL/Timer/Script/Ramp companions)
- 5.0.310: Fix first-time Envoy setup stuck on "Disabled" (issues #8, #9), git config generation on fresh install
- 5.0.305: Replicant duplicate detection fix (issue #4), TDN export improvements, ExternalizeProject dialog
- 5.0.302: Fix duplicate path clone detection (issue #4), config file location (issue #5), Envoy startup flow
- 5.0.278: Fix folder change crash (issue #3), regression tests
- 5.0.277: Manager UI improvements, Ctrl+Shift+R shortcut, consistent "Update" terminology
- 5.0.275: TDN export keyboard shortcut pars, keyboard shortcuts documentation
- 5.0.274: Settings persistence across upgrades, extension initialization timing docs
- 5.0.263: DAT content safety, palette clone fidelity, recursive TDN fingerprinting, venv validation
- 5.0.259: Mandatory operator layout rules,
/localpath prohibition, TD connectivity recovery - 5.0.258: Multi-instance Envoy support,
switch_instancetool, auto-suffix collision avoidance - 5.0.252: Windows process-kill fix, reconstruction verification fix
- 5.0.251: Nested TDN child-skip on import, depth-sorted reconstruction ordering
- 5.0.243: Headless smoke testing, file cleanup preferences, specialized COMP support, portable .tox hardening
- 5.0.237: TDN v1.1 format, import error surfacing, save-cycle pane restoration, Envoy troubleshooting docs
- 5.0.235:
restart_tdmeta-tool, local MCP handshake, operator overlap warnings - 5.0.233: Project-level performance monitoring,
/validatecommand, test runner dialog fix - 5.0.228: macOS timezone fix, toolbar hover highlight
- 5.0.227: TDN crash safety — atomic writes, backup rotation, content-equal skip, About page filtering
- 5.0.222: Rename
tag_for_externalization→externalize_op, clarify single-step workflow - 5.0.221: TDN annotation properties, GitHub release rule, templates cleanup
- 5.0.220: Network layout rewrite, commit-push checklist rule, expanded MCP tool allowlist, tooltip fix
- 5.0.217: TDN target COMP parameter preservation, user-prompted file cleanup, dock safety, git init hardening
- 5.0.210: DAT restoration on startup, continuity check hardening, manager list row limiting
- 5.0.208: Settings auto-deploy, bridge template, Envoy startup resilience
- 5.0.206: Metadata reconciliation, network layout tool, TDN companion dedup
- 5.0.204: Custom window header, path portability, TDN cleanup
- 5.0.201: Robust first-install init, table schema expansion, release build hardening
- 5.0.190: Automatic restoration — TOX and TDN strategy COMPs fully restored from disk on project open
- 5.0: Envoy MCP server (46 tools), TDN format, test framework (38 suites), macOS support
Originally derived from External Tox Saver by Tim Franklin. Refactored entirely by Dylan Roscover, with inspiration and guidance from Elburz Sorkhabi, Matthew Ragan and Wieland Hilker.
