Skip to content

Interação nativa & ações seguras no editor

O tdmcp agora pode pedir uma decisão destrutiva dentro do TouchDesigner sem manter a request HTTP original aberta. A Wave 1 adiciona um broker pequeno e autenticado, além de ações estruturadas que continuam funcionando com TDMCP_BRIDGE_ALLOW_EXEC=0.

Inbox nativa de decisões

O bridge de runtime tem uma página de parâmetros customizados chamada Interactions. Uma exclusão ou sobrescrita entra na fila, retorna como request ID opaco e é apresentada no frame seguinte do TouchDesigner. Selecione uma opção em Choice e pulse Apply Choice. Safe Close cancela com segurança: prompts destrutivos/de arquivo resolvem como Keep, enquanto consentimento OAuth resolve como Deny.

A inbox é não modal de propósito. Ela não chama ui.messageBox no callback do Web Server DAT, portanto nenhum prompt segura a request original além do timeout HTTP normal do cliente. Só um prompt é apresentado por vez; fila, TTL e registros retidos têm limites.

Todo encerramento inseguro significa Keep: timeout, close/cancel, perda do cliente, UI ausente, Perform Mode, headless, falha de agendamento ou consumo duplicado. Um prompt autoriza exatamente um alvo, uma única vez. Ele não executa Python, não recebe callables e não aprova outro operador ou arquivo.

Panic, blackout e outros caminhos de emergência não usam nem esperam o broker.

Delete / Bypass / Keep

delete_td_node com mode: "delete" apresenta exatamente:

  • Delete — destrói o operador depois do consentimento;
  • Bypass — liga a flag de bypass e preserva o operador;
  • Keep — não altera o operador.

O prompt mostra path, tipo e nome do operador e resume o impacto local. O resultado traz decisão, path original/final, ação aplicada, política de confirmação, request ID e o nome de undo por request quando o build live o expõe.

Migração do comportamento antigo de delete

Chamadas existentes com { path, mode } continuam válidas, mas o default seguro mudou:

  • mode: "delete" agora exige consentimento nativo e retorna Keep quando a UI não consegue decidir;
  • mode: "bypass" continua imediato e reversível;
  • TDMCP_YOLO=1 é a única política de skip no nível da tool. Ela aparece no resultado como confirmation_policy: "yolo"; ausência de UI nunca vira aprovação.

Reinstale ou recarregue o bridge de runtime com este código antes de usar o novo fluxo. Um bridge antigo sem o broker falha em vez de voltar ao delete legado sem confirmação.

Delete dentro de batch não pode pausar o batch para consentimento na UI. Um delete legado em batch agora retorna Keep com ok: false. Use o fluxo isolado de delete_td_node para consentimento nativo, ou torne a política do batch explícita com mode: "bypass" ou confirmation_policy: "yolo".

Delete não é intrinsecamente “impossível de desfazer”. A mutação REST final já passa pelo wrapper ui.undo do bridge. Um probe live confirmou que blocos aninhados comuns podem virar um único item com o nome do bloco externo, mas também mostrou que criar um annotateCOMP fecha/substitui o bloco externo do caller. Portanto ainda não é seguro habilitar um único undo para qualquer tool MCP de alto nível.

Save e Save As

save_td_project salva o .toe atual ou recebe um path absoluto .toe para Save As. Um alvo diferente já existente exige uma decisão Overwrite / Keep do broker. Não há diálogo de arquivo, load, quit nem fallback para raw Python.

Sucesso só é retornado depois que o arquivo aparece no disco. Projetos sem nome exigem um path de Save As. O ticket fica vinculado ao path normalizado e não pode autorizar outro arquivo.

Primitives conscientes do editor

  • get_editor_context retorna projeto/build, Perform Mode, panes, o Network Editor explicitamente ativo, owner/current/selected, rollover de operador e parâmetro e viewport. Campos indisponíveis ficam ausentes ou nulos com warnings; a topologia não é despejada.
  • pulse_td_parameter resolve operador e parâmetro, verifica o estilo Pulse, chama .pulse() e retorna erros tipados para operador inválido, parâmetro ausente ou tipo incorreto.
  • edit_td_node_metadata edita nome, parent, posição exata, cor, comentário e flags graváveis suportadas. Faz readback e rollback em falha parcial. Moves copiam e validam o destino antes de destruir a origem.
  • create_td_node aceita placement: "auto" | "explicit", node_x / node_y exatos e viewer. Omissão preserva o drop antigo do TD; nodes idempotentes reutilizados preservam o layout existente.

Estado do undo

O bridge mantém um bloco de undo por request REST mutante. As novas mutações de metadata, Pulse, create e delete final recebem receipts úteis. O receipt agora traz o item nativo realmente visível no topo quando exatamente um item foi adicionado; quando o TouchDesigner troca o nome pelo built-in Delete Node ou Change Bypass Flag, a resposta raw do bridge informa separadamente o nome pedido pelo wrapper.

A validação live no TouchDesigner 2025.32820 provou que um bloco externo comum consegue desfazer e refazer duas edições aninhadas como um único item e que o finally fecha o bloco após uma exceção. Ela também reproduziu o blocker: criar um annotateCOMP dentro do bloco externo criou sua própria entrada Add Annotate, fechou/substituiu o bloco externo e fez o endBlock() do caller falhar. Transações entre requests e undo de uma tool MCP inteira seguem retidos até existir um protocolo de ownership que sobreviva ao undo específico de operadores, cancelamentos e timeouts sem deixar transação órfã.

Undo/redo automatizado de um item também está HELD. Um probe live com dois itens provou que este build expõe o stack do mais novo para o mais antigo (stack[0]) e que undo/redo nativo funciona, mas o probe de identidade mostrou que cada leitura retorna uma nova string simples. Duas ações diferentes podem se chamar Delete Node ou Change Bypass Flag; depois de uma edição interveniente do artista, um teste só por label ainda pode casar e desfazer a ação errada (ABA com o mesmo label). Contagens reduzem, mas não eliminam essa ambiguidade. Nenhuma tool ou route pública é registrada até o TD expor identidade estável ou um protocolo mais forte e consciente de edições do artista ser provado.

Wave 7: workflows transacionais e conscientes da ação

A Wave 7 estende tools existentes em vez de criar aliases. focus_network_editor, manage_component, make_portable_tox, manage_packages e add_custom_parameters agora compartilham primitives estruturados e limitados no bridge, e a nova tool insert_operator_at_selection adiciona a mutação de editor que faltava. As routes continuam autenticadas e funcionam com TDMCP_BRIDGE_ALLOW_EXEC=0; o caminho legado de load em manage_component não mudou e ainda usa sua implementação protegida pelo gate de exec.

Estado das evidências

ÁreaEstadoO que a evidência prova
Follow do Network Editor consciente da açãoPASS — route no TD 2025.32820A route autenticada reutilizou panes visíveis/compatíveis, substituiu current/selection exatamente, executou seis frames com verificação de geração, cancelou gerações antigas rápidas e suprimiu Perform Mode. Processo headless real, outros builds e layouts incomuns com panes múltiplos/flutuantes seguem UNVERIFIED.
Inserção na seleção ativaPASS — route autenticada, TD 2025.32820Com auth e exec desabilitado, cadeia simples, fan-out, multi-input, placement determinístico, replay/conflito, rollback induzido e undo/redo da route passaram sem conflito de thread. Proxies live de Connector exigiram identidade estrutural por owner/path/índice, não identidade Python.
Exportação .tox transacionalPASS — route autenticada, TD 2025.32820as_is/portable, Overwrite / Keep, recuperação após perda de resposta, retry deduplicado, cancelamento, hash e limpeza passaram com exec desabilitado. Artefatos de filesystem continuam fora do undo do grafo TD.
Reconciliação do namespace de pacotesPASS — TD autenticado + storage localKeep, Bypass/Delete nativos, YOLO explícito auditado, rejeição de plano stale, quarentena/commit ou restore e undo/redo no TD passaram end to end. O fix live alinha fingerprints no OPType; undo restaura só o TD, não registry/filesystem já commitados.
Ciclo de parâmetros customizadosPASS — route autenticada, TD 2025.32820Add/edit/delete/sort/rename/delete-page, undo/redo exato, replay/conflito, rollback induzido e segurança do item de undo passaram. Os fixes live cobrem ParMode em módulo importado, identidade estrutural de ParGroup, rollback por estilo e ordem determinística de clamp/value. EXPORT segue HELD.
Undo/redo nativo automatizado de um itemREJEITADOUndo/redo nativo funciona, mas o stack live expõe apenas labels repetíveis sem identidade estável. ABA com o mesmo label pode atingir uma ação interveniente do artista; uma route/tool genérica está permanentemente rejeitada.
Operação estruturada em uma requestPASS OFFLINE / ROUTE LIVE NÃO VERIFICADAA Wave 15 adiciona preview com token obrigatório, commit em uma callback e observação de receipt autorizada por capability fora do wrapper genérico. O adapter já passou undo/redo do journal e rollback live, mas a nova route pública não pôde ser exercitada porque o segundo TD descartável não abriu listener. Nenhuma tool MCP foi registrada.
Undo de uma tool inteira entre requests RESTFAIL / DESIGN REJEITADOO TD encerra um bloco de undo pendente quando cada callback do Web Server DAT retorna. Uma request posterior não consegue entrar nele com segurança; um undo removeu apenas a primeira mutação do probe. O bridge mantém um bloco nomeado por request REST mutante legado e nunca carrega um bloco entre requests.
Highlight por corHELDFraming/current/selection foram entregues. A restauração transitória de cor sob ações sobrepostas e edições do artista não tem contrato compare-and-swap aceito; a Wave 7 não altera cores de nodes.

PASS vale apenas para a evidência indicada na linha. Ele não promove TouchDesigner realmente headless, TouchPlayer, outros builds, comportamento de filesystem externo nem um caminho end-to-end autenticado que não foi executado.

Fronteira de operações da Wave 15

POST /api/operations/preview, /commit e /receipt são primitives guardados do bridge na árvore de código, não tools MCP. Eles exigem bearer token configurado antes do parsing do body e continuam utilizáveis com TDMCP_BRIDGE_ALLOW_EXEC=0. Preview é read-only. Commit possui uma transação síncrona com callback journal, e a recuperação do receipt exige ID opaco da operação, capability independente de 256 bits, o mesmo principal autenticado e a mesma instância do bridge. Capabilities ficam em bodies POST e nunca em labels de undo ou query strings. A idempotency key apenas deduplica um commit idêntico e é deliberadamente ausente dos receipts terminais.

A árvore de código ainda não expõe undo/redo genérico, revert por receipt, selection-to-component nem orquestração agent de plan/preview/commit. Revert exige journal de compensação consciente da direção e consentimento nativo Apply/Keep; collapse da seleção exige prova live de rollback exato de topologia e referências. O processo descartável da Wave 15 não chegou a um listener do bridge, portanto esses caminhos permanecem UNVERIFIED em vez de usar o projeto não salvo do artista como evidência.

Follow do Network Editor consciente da ação

A chamada legada { paths, animate } continua válida. Novos campos opcionais tornam o receipt e a política de UI explícitos:

  • action: create, edit, inspect, view, layout ou delete;
  • framing: auto, selection, owner ou none;
  • enabled: opt-out explícito que retorna uma supressão tipada.

Os alvos precisam pertencer à mesma network parent. O bridge prefere o Network Editor ativo ou já dono do contexto, nunca cria um pane, substitui selection antiga, define um current explícito e faz readback final de owner, selection e viewport. animate:true usa seis passos ease-out limitados no frame seguinte; cada passo revalida a geração do pane e somente o sexto publica o readback final. Uma geração mais nova cancela passos antigos. Follow desabilitado, Perform Mode e sessões sem UI não movem o editor. O comportamento headless real segue UNVERIFIED, e o highlight por cor permanece HELD.

Inserção na seleção ativa

insert_operator_at_selection exige owner, único operador selecionado e current exatos do Network Editor ativo retornados por get_editor_context, além de uma chave opaca de idempotência. Qualquer drift falha antes da criação. O bridge só aceita tipo same-family criável no build live, define coordenadas explícitas, determinísticas e sem sobreposição imediatamente, desliga o viewer do novo node e substitui uma aresta downstream estável, preservando irmãos de fan-out e outros inputs downstream.

Criação, parâmetros, placement, conectores e readback final acontecem numa única mutação REST autenticada com um label de undo por request. Em falha, somente o novo node é desconectado/destruído e o snapshot exato de arestas é verificado; retry idêntico reapresenta receipt redigido e chave reutilizada com outro input falha fechada. placeOPs interativo, inferência de wire selecionado, inserção múltipla e raw Python não são expostos. O harness final exclusivamente no main thread passou pela route autenticada no 2025.32820 com exec desabilitado, incluindo rollback exato, replay/conflito e undo/redo da route.

Exportação TOX transacional

manage_component action:"save" agora usa a transação compartilhada as_is, e make_portable_tox usa portable. Ambas validam um alvo .tox absoluto, mantêm no máximo uma exportação ativa, gravam um arquivo temporário único no mesmo diretório, verificam tamanho/hash/build, promovem atomicamente e retêm um receipt de status limitado para polling e retry idempotente.

Overwrite agora usa overwrite_policy:"refuse" por default. Use "ask" para pedir um ticket Overwrite / Keep vinculado ao alvo exato; UI ausente nunca é consentimento. O modo portable tira snapshot de links/conteúdo de Text/Table DAT e do estado externaltox de COMPs, restaura tudo em finally e falha fechado fora do build provado live, a menos que o operador habilite explicitamente outro build testado separadamente. O .tox pode concluir enquanto sidecars de README/manifest falham; isso retorna partial_failure, nunca sucesso completo do pacote.

Com exec desabilitado no bridge, a exportação portable estruturada ainda roda. O helper existente de introspecção do README é best-effort e pode ser pulado com warning porque esse sidecar continua no caminho legado protegido pelo gate de exec.

Uma escrita no filesystem não é undo do grafo TouchDesigner. Não existe alegação de que ui.undo remova ou restaure um artefato exportado. Timeout/disconnect do cliente depois do dispatch também pode ser ambíguo; consulte o operation ID opaco antes de repetir.

Reconciliação do namespace de pacotes

manage_packages action:"reconcile" começa em dry-run. O plano varre um namespace limitado do projeto e só age sobre um marker único cujo package ID, fingerprint da source, ref e scope correspondem ao registro local de instalação. Candidatos estrangeiros, ilegíveis, incompatíveis, sem marker ou duplicados não são acionáveis.

Apply exige o plan_id ainda válido e revalida ownership. Keep não altera, Bypass preserva o COMP live, e Delete exige consentimento nativo, salvo política YOLO explícita. Arquivos locais staged são colocados em quarentena antes da mudança no registro; uma falha restaura a quarentena quando possível, e uma limpeza incompleta retorna partial_failure com remediação. Um uninstall legado que ainda tem alvo live no TD agora retorna esse plano seguro em vez de apagar primeiro o estado local.

Esse workflow nunca executa scripts de terceiros, instala dependências Python, baixa modelos nem configura aplicações externas.

Ciclo de parâmetros customizados

Chamadas existentes { comp_path, page, params } continuam significando add transacional. operations adiciona ações limitadas add, edit_parameter, delete_parameter, sort_page, rename_page e delete_page na mesma tool. Os estilos suportados são Float, Int, Toggle, Str, Menu, Pulse, Header, OP, TOP, File, Folder, XYZW e RGBA; inputs legados RGB/XYZ continuam aceitos. Modos EXPRESSION e BIND são suportados. O modo EXPORT está HELD e falha antes da mutação porque não foi provado um contrato reversível de export source.

A migração endurece de propósito o add parcial antigo: uma definição existente idêntica vira unchanged, uma definição conflitante falha sem replacement, e uma falha posterior restaura o snapshot completo das páginas customizadas ou retorna partial_failure. Built-ins nunca são editáveis. Sort precisa nomear cada ParGroup exatamente uma vez e passa ao TD apenas objetos par.parGroup, preservando componentes XYZW/RGBA. Resultados vêm por operação e campo; valores, expressões e bind expressions não entram nos receipts de idempotência.

Alerta de plugin externo específico deste ambiente

O alerta do macOS sobre não conseguir abrir serviços de criptografia do OS foi diagnosticado nesta máquina como FAIL de confiança da instalação local, não como falha do bridge tdmcp: o bundle atual do TouchDesigner tem requisito de código de recursos selados inválido, enquanto o FreenectTOP instalado tem assinatura ad-hoc e é rejeitado pelo Gatekeeper. O binário estar mapeado não prova que o operador registra ou cozinha; a ativação funcional segue UNVERIFIED.

Não apague itens do keychain, remova quarantine nem re-assine o app principal como atalho. O próximo experimento seguro é uma instalação oficial limpa do TouchDesigner verificada antes do primeiro launch, primeiro sem plugins externos e depois com um plugin assinado/notarizado pelo fornecedor em projeto isolado.

Exemplos de resultado da Wave 8

PASS — transação verificada

json
{
  "status": "succeeded",
  "operation_id": "opaque-export-id",
  "verification": { "level": "load_independent" },
  "cleanup": { "pending": false }
}

FAIL — ownership não pôde ser provado

json
{
  "status": "failed",
  "code": "package_not_recorded",
  "storage": { "quarantined": false, "recordRemoved": false }
}

UNVERIFIED — limite da evidência

json
{
  "status": "UNVERIFIED",
  "checks": [
    "TouchDesigner realmente headless",
    "outros builds do TouchDesigner",
    "registro e cook de plugin externo"
  ]
}

Wave 9: componentes portáteis confiáveis e layout consciente de annotations

A Wave 9 estende tools existentes; não cria duplicatas de annotation, layout ou pacotes. Todas as operações novas no TouchDesigner usam routes estruturadas e autenticadas e funcionam com TDMCP_BRIDGE_ALLOW_EXEC=0.

manage_annotation action:"edit" edita título, corpo, cor RGBA e bounds exatos x, y, w, h de um annotateCOMP existente. O bridge resolve todos os aliases graváveis antes da mutação, fotografa o estado completo suportado, aplica e relê cada campo pedido e restaura o snapshot após uma falha parcial. Texto e comentário ficam redigidos nos logs e receipts. O edit não aceita os Text DATs de fallback que o create legado pode produzir.

arrange_network preserva o comportamento legado, salvo quando annotation_aware:true. O caminho opt-in lê um snapshot limitado de geometria, rejeita membership ambíguo entre annotations sobrepostas, planeja grupos sem Python raw e aplica posições com fingerprint do snapshot. Contexto stale falha antes de mutar. DATs docked seguem o delta do host; resize_annotations:true ajusta boxes não vazios ao conteúdo mais annotation_padding (default 80). Uma segunda execução idêntica reporta zero nodes movidos.

Para artefatos portáteis, validate_library_asset aceita validation_mode:"deep_roundtrip" somente com um bridge autenticado, explicitamente em quarantine e fora da porta 9980. Ele carrega o .tox num holder scratch único, espera frames com limite, compara o contrato declarado, captura erros/referências externas limitados e sempre tenta cleanup. Runtime ou prova ausente retorna UNVERIFIED, nunca PASS.

make_portable_tox agora grava por default um sidecar versionado de provenance. Ele liga o hash final do TOX ao hash canônico do manifest, identidade do COMP de origem, builds do TD/tdmcp e somente commit/estado dirty do Git. Nunca grava tokens, variáveis de ambiente, diffs, conteúdo do projeto ou raiz do repositório. Use provenance_policy:"require_clean" (e opcionalmente expected_git_commit) para um preflight estrito; Git indisponível, dirty ou com commit divergente falha antes do export. Alvos existentes ainda exigem consentimento nativo explícito, e TOX mais provenance são promovidos como um par recuperável.

O help_snapshot opcional inventaria tipos de operadores e APIs Python do TD explicitamente nomeadas com limites, lê somente o corpus OfflineHelp instalado do build exato, escreve índice/README determinísticos em docs/td-help e roda novamente o round-trip em quarantine após anexar. Caps, páginas ausentes ou build divergente viram UNVERIFIED honestamente. attach_docs_as_assets pode atualizar o snapshot depois e também atualiza atomicamente o hash do manifest na provenance existente.

Exemplos de resultado da Wave 9

PASS — contrato exact-build e cleanup verificados

json
{
  "validation_mode": "deep_roundtrip",
  "roundtrip": {
    "verdict": "PASS",
    "runtime": { "td_build": "2025.32820" },
    "cleanup": { "verified": true }
  }
}

FAIL — provenance estrita recusa antes do export

json
{
  "status": "FAIL",
  "code": "git_worktree_dirty",
  "export_started": false
}

UNVERIFIED — inventário limitado não prova todas as entradas

json
{
  "status": "UNVERIFIED",
  "reason": "operator_type_cap",
  "available": 1,
  "truncated": 7
}

Exemplos honestos de resultado

PASS — contrato offline e estado confirmados

json
{
  "status": "PASS",
  "decision": "Keep",
  "action_applied": "keep",
  "applied": false,
  "final_path": "/project1/noise1"
}

FAIL — operação estruturada inválida

json
{
  "status": "FAIL",
  "error": {
    "code": "invalid_parameter_type",
    "message": "pulse: parameter Gain has style Float, expected Pulse"
  }
}

UNVERIFIED — fora do runtime testado

json
{
  "status": "UNVERIFIED",
  "reason": "runtime não exercitado",
  "checks": ["TouchDesigner realmente headless", "builds além de 2025.32820"]
}

UNVERIFIED não é PASS. A matriz gráfica 2025.32820 passou; faça validação live separada antes de depender das mesmas semânticas em headless, outro build ou filesystem externo crítico para show.

Follow-ups adiados

OAuth/PKCE chegou numa wave posterior opt-in de autorização HTTP; veja OAuth, PKCE & consentimento no TouchDesigner. Estas waves não adicionam catálogos/instaladores remotos de skills, snapshot/restore do workspace, selection-to-component, highlight global animado, refactor amplo do bridge nem migração de todos os comandos destrutivos para o broker. Esses itens seguem separados. Skills curadas empacotadas e docs locais build-aware chegaram na wave seguinte; veja Agent build-aware & readiness do runtime.