Skip to main content
Glama

Update Node

update_node

Because no node is added, dropped, or renamed, every prospect keeps its cursor and each node keeps its on_enter enactment (prompt, code, run history). The edited sequence is re-validated as a whole, so an edge repoint that would dangle a reference, orphan a node, or introduce a cycle is rejected rather than saved. To add or remove nodes, change a node's kind, or edit the start node, re-author the flow with define_sequence instead.

A forked-on condition is often restated in several places — a decision's rule, each arm.case, a downstream terminal's label, and the node's on_enter prompt. When you change one, change the others in the same turn (a further update_node for the node fields, update_trigger for the prompt) so they agree. Dict with success, agent_id, and node_id. Read the updated flow via get_campaign_flow.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
nodeYesThe node's complete new definition — an omitted optional slot (a `follow_up`, a `timeout`) is dropped, not preserved, so carry forward every field the node keeps. A send or connection-request node's `message_templates` is the exception: omitted leaves the step's templates as they are. Its `id` must name an existing body node and its `kind` must match that node's current kind.
agent_idYesID of the agent whose flow holds the node.
on_behalf_ofNoAn agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account.

Schema Changelog

Changes observed during successful MCP inspections.

  1. Changed2 schema fields changed
    • changedInput schema / properties / node / oneOf
      Previous value: -[
      -  {
      -    "additionalProperties": false,
      -    "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept). `accepted.after`\nholds an accepted prospect on this node for that long after the accept is detected before\nthey advance.\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.",
      -    "properties": {
      -      "accepted": {
      -        "additionalProperties": false,
      -        "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.",
      -        "properties": {
      -          "after": {
      -            "anyOf": [
      -              {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "default": null
      -          },
      -          "to": {
      -            "type": "string"
      -          }
      -        },
      -        "required": [
      -          "to"
      -        ],
      -        "title": "Advance",
      -        "type": "object"
      -      },
      -      "already_connected": {
      -        "type": "string"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "connection_request",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "message_templates": {
      -        "anyOf": [
      -          {
      -            "items": {
      -              "maxLength": 300,
      -              "type": "string"
      -            },
      -            "maxItems": 2,
      -            "type": "array"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      },
      -      "timeout": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      -            "properties": {
      -              "delay": {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              "to": {
      -                "type": "string"
      -              }
      -            },
      -            "required": [
      -              "delay",
      -              "to"
      -            ],
      -            "title": "TimedAdvance",
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "accepted",
      -      "already_connected"
      -    ],
      -    "title": "ConnectionRequestNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.",
      -    "properties": {
      -      "action": {
      -        "enum": [
      -          "message",
      -          "inmail",
      -          "email"
      -        ],
      -        "type": "string"
      -      },
      -      "channel": {
      -        "enum": [
      -          "linkedin",
      -          "email"
      -        ],
      -        "type": "string"
      -      },
      -      "follow_up": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      -            "properties": {
      -              "delay": {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              "to": {
      -                "type": "string"
      -              }
      -            },
      -            "required": [
      -              "delay",
      -              "to"
      -            ],
      -            "title": "TimedAdvance",
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "send",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "message_templates": {
      -        "anyOf": [
      -          {
      -            "items": {
      -              "type": "string"
      -            },
      -            "maxItems": 2,
      -            "type": "array"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      },
      -      "replied": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "channel",
      -      "action",
      -      "replied"
      -    ],
      -    "title": "SendNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.",
      -    "properties": {
      -      "action": {
      -        "enum": [
      -          "follow",
      -          "withdraw",
      -          "resolve",
      -          "comment",
      -          "reaction"
      -        ],
      -        "type": "string"
      -      },
      -      "channel": {
      -        "const": "linkedin",
      -        "type": "string"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "action",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "notify": {
      -        "default": false,
      -        "type": "boolean"
      -      },
      -      "then": {
      -        "additionalProperties": false,
      -        "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.",
      -        "properties": {
      -          "after": {
      -            "anyOf": [
      -              {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "default": null
      -          },
      -          "to": {
      -            "type": "string"
      -          }
      -        },
      -        "required": [
      -          "to"
      -        ],
      -        "title": "Advance",
      -        "type": "object"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "channel",
      -      "action",
      -      "then"
      -    ],
      -    "title": "SilentActionNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A physical gift Sliq orders online and ships to each prospect's US address. Entry\nqueues one gift card per prospect onto the user's approval list, and nothing is ordered\nuntil the user approves a card. The card is where the user completes or changes the\nrecipient, shipping address, product, price ceiling and note. `product_url`,\n`max_price_usd` (the most the order may cost, in whole US dollars) and `gift_note` (at\nmost 240 characters) are the step's optional defaults, pre-filled on every card. No\nchannel and no reply to route; `then` is its single advance once the gift is delivered:\nbare (`then.after=None`) to move on at delivery, or held to wait that long after it.",
      -    "properties": {
      -      "gift_note": {
      -        "anyOf": [
      -          {
      -            "maxLength": 240,
      -            "type": "string"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "gift",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "max_price_usd": {
      -        "anyOf": [
      -          {
      -            "exclusiveMinimum": 0,
      -            "type": "integer"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      },
      -      "product_url": {
      -        "anyOf": [
      -          {
      -            "format": "uri",
      -            "maxLength": 2083,
      -            "minLength": 1,
      -            "type": "string"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      },
      -      "then": {
      -        "additionalProperties": false,
      -        "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.",
      -        "properties": {
      -          "after": {
      -            "anyOf": [
      -              {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "default": null
      -          },
      -          "to": {
      -            "type": "string"
      -          }
      -        },
      -        "required": [
      -          "to"
      -        ],
      -        "title": "Advance",
      -        "type": "object"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "then"
      -    ],
      -    "title": "GiftNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a handwritten card, a recorded note). Entry queues a manual_action_queue row\ncarrying `action_description`; the user marking that row done advances the prospect\nalong `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.",
      -    "properties": {
      -      "action_description": {
      -        "type": "string"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "manual",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "then": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "action_description",
      -      "then"
      -    ],
      -    "title": "ManualActionNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.",
      -    "properties": {
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "automation",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "then": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "then"
      -    ],
      -    "title": "AutomationNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').",
      -    "properties": {
      -      "arms": {
      -        "items": {
      -          "additionalProperties": false,
      -          "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.",
      -          "properties": {
      -            "case": {
      -              "minLength": 1,
      -              "type": "string"
      -            },
      -            "to": {
      -              "type": "string"
      -            }
      -          },
      -          "required": [
      -            "case",
      -            "to"
      -          ],
      -          "title": "Arm",
      -          "type": "object"
      -        },
      -        "minItems": 1,
      -        "type": "array"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "decision",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "rule": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "rule",
      -      "arms"
      -    ],
      -    "title": "DecisionNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.",
      -    "properties": {
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "terminal",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind"
      -    ],
      -    "title": "TerminalNode",
      -    "type": "object"
      -  }
      -]New value: +[
      +  {
      +    "additionalProperties": false,
      +    "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept). `accepted.after`\nholds an accepted prospect on this node for that long after the accept is detected before\nthey advance.\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.",
      +    "properties": {
      +      "accepted": {
      +        "additionalProperties": false,
      +        "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.",
      +        "properties": {
      +          "after": {
      +            "anyOf": [
      +              {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "default": null
      +          },
      +          "to": {
      +            "type": "string"
      +          }
      +        },
      +        "required": [
      +          "to"
      +        ],
      +        "title": "Advance",
      +        "type": "object"
      +      },
      +      "already_connected": {
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "connection_request",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "message_templates": {
      +        "anyOf": [
      +          {
      +            "items": {
      +              "maxLength": 300,
      +              "type": "string"
      +            },
      +            "maxItems": 2,
      +            "type": "array"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "timeout": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      +            "properties": {
      +              "delay": {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              "to": {
      +                "type": "string"
      +              }
      +            },
      +            "required": [
      +              "delay",
      +              "to"
      +            ],
      +            "title": "TimedAdvance",
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "accepted",
      +      "already_connected"
      +    ],
      +    "title": "ConnectionRequestNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.",
      +    "properties": {
      +      "action": {
      +        "enum": [
      +          "message",
      +          "inmail",
      +          "email"
      +        ],
      +        "type": "string"
      +      },
      +      "channel": {
      +        "description": "An outreach channel.",
      +        "enum": [
      +          "linkedin",
      +          "email"
      +        ],
      +        "title": "Channel",
      +        "type": "string"
      +      },
      +      "follow_up": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      +            "properties": {
      +              "delay": {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              "to": {
      +                "type": "string"
      +              }
      +            },
      +            "required": [
      +              "delay",
      +              "to"
      +            ],
      +            "title": "TimedAdvance",
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "send",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "message_templates": {
      +        "anyOf": [
      +          {
      +            "items": {
      +              "type": "string"
      +            },
      +            "maxItems": 2,
      +            "type": "array"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "replied": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "channel",
      +      "action",
      +      "replied"
      +    ],
      +    "title": "SendNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.",
      +    "properties": {
      +      "action": {
      +        "enum": [
      +          "follow",
      +          "withdraw",
      +          "resolve",
      +          "comment",
      +          "reaction"
      +        ],
      +        "type": "string"
      +      },
      +      "channel": {
      +        "const": "linkedin",
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "action",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "notify": {
      +        "default": false,
      +        "type": "boolean"
      +      },
      +      "then": {
      +        "additionalProperties": false,
      +        "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.",
      +        "properties": {
      +          "after": {
      +            "anyOf": [
      +              {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "default": null
      +          },
      +          "to": {
      +            "type": "string"
      +          }
      +        },
      +        "required": [
      +          "to"
      +        ],
      +        "title": "Advance",
      +        "type": "object"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "channel",
      +      "action",
      +      "then"
      +    ],
      +    "title": "SilentActionNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A physical gift Sliq orders online and ships to each prospect's US address. Entry\nqueues one gift card per prospect onto the user's approval list, and nothing is ordered\nuntil the user approves a card. The card is where the user completes or changes the\nrecipient, shipping address, product, price ceiling and note. `product_url`,\n`max_price_usd` (the most the item may cost including tax and shipping, in whole US\ndollars) and `gift_note` (at most 240 characters) are the step's optional defaults,\npre-filled on every card. No channel and no reply to route; `then` is its single advance\nonce the gift is delivered: bare (`then.after=None`) to move on at delivery, or held to\nwait that long after it.",
      +    "properties": {
      +      "gift_note": {
      +        "anyOf": [
      +          {
      +            "maxLength": 240,
      +            "type": "string"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "gift",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "max_price_usd": {
      +        "anyOf": [
      +          {
      +            "exclusiveMinimum": 0,
      +            "type": "integer"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "product_url": {
      +        "anyOf": [
      +          {
      +            "format": "uri",
      +            "maxLength": 2083,
      +            "minLength": 1,
      +            "type": "string"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "then": {
      +        "additionalProperties": false,
      +        "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.",
      +        "properties": {
      +          "after": {
      +            "anyOf": [
      +              {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "default": null
      +          },
      +          "to": {
      +            "type": "string"
      +          }
      +        },
      +        "required": [
      +          "to"
      +        ],
      +        "title": "Advance",
      +        "type": "object"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "then"
      +    ],
      +    "title": "GiftNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a handwritten card, a recorded note). Entry queues a manual_action_queue row\ncarrying `action_description`; the user marking that row done advances the prospect\nalong `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.",
      +    "properties": {
      +      "action_description": {
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "manual",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "then": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "action_description",
      +      "then"
      +    ],
      +    "title": "ManualActionNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.",
      +    "properties": {
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "automation",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "then": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "then"
      +    ],
      +    "title": "AutomationNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').",
      +    "properties": {
      +      "arms": {
      +        "items": {
      +          "additionalProperties": false,
      +          "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.",
      +          "properties": {
      +            "case": {
      +              "minLength": 1,
      +              "type": "string"
      +            },
      +            "to": {
      +              "type": "string"
      +            }
      +          },
      +          "required": [
      +            "case",
      +            "to"
      +          ],
      +          "title": "Arm",
      +          "type": "object"
      +        },
      +        "minItems": 1,
      +        "type": "array"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "decision",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "rule": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "rule",
      +      "arms"
      +    ],
      +    "title": "DecisionNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.",
      +    "properties": {
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "terminal",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind"
      +    ],
      +    "title": "TerminalNode",
      +    "type": "object"
      +  }
      +]
    • addedInput schema / properties / on_behalf_of
      Added value: +{
      +  "anyOf": [
      +    {
      +      "type": "string"
      +    },
      +    {
      +      "type": "null"
      +    }
      +  ],
      +  "default": null,
      +  "description": "An agency client's email, from list_agency_clients, to run this call in that client's account: their agents, settings, connected accounts, and credits. Omit it to act on your own account."
      +}
  2. Changed1 schema field changed
    • changedInput schema / properties / node / oneOf
      Previous value: -[
      -  {
      -    "additionalProperties": false,
      -    "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept). `accepted.after`\nholds an accepted prospect on this node for that long after the accept is detected before\nthey advance.\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.",
      -    "properties": {
      -      "accepted": {
      -        "additionalProperties": false,
      -        "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, a gift's delivery,\ntracking time off the start node, or the accept for a connection request's `accepted`.",
      -        "properties": {
      -          "after": {
      -            "anyOf": [
      -              {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "default": null
      -          },
      -          "to": {
      -            "type": "string"
      -          }
      -        },
      -        "required": [
      -          "to"
      -        ],
      -        "title": "Advance",
      -        "type": "object"
      -      },
      -      "already_connected": {
      -        "type": "string"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "connection_request",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "message_templates": {
      -        "anyOf": [
      -          {
      -            "items": {
      -              "maxLength": 300,
      -              "type": "string"
      -            },
      -            "maxItems": 2,
      -            "type": "array"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      },
      -      "timeout": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      -            "properties": {
      -              "delay": {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              "to": {
      -                "type": "string"
      -              }
      -            },
      -            "required": [
      -              "delay",
      -              "to"
      -            ],
      -            "title": "TimedAdvance",
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "accepted",
      -      "already_connected"
      -    ],
      -    "title": "ConnectionRequestNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.",
      -    "properties": {
      -      "action": {
      -        "enum": [
      -          "message",
      -          "inmail",
      -          "email"
      -        ],
      -        "type": "string"
      -      },
      -      "channel": {
      -        "enum": [
      -          "linkedin",
      -          "email"
      -        ],
      -        "type": "string"
      -      },
      -      "follow_up": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      -            "properties": {
      -              "delay": {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              "to": {
      -                "type": "string"
      -              }
      -            },
      -            "required": [
      -              "delay",
      -              "to"
      -            ],
      -            "title": "TimedAdvance",
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "send",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "message_templates": {
      -        "anyOf": [
      -          {
      -            "items": {
      -              "type": "string"
      -            },
      -            "maxItems": 2,
      -            "type": "array"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      },
      -      "replied": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "channel",
      -      "action",
      -      "replied"
      -    ],
      -    "title": "SendNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.",
      -    "properties": {
      -      "action": {
      -        "enum": [
      -          "follow",
      -          "withdraw",
      -          "resolve",
      -          "comment",
      -          "reaction"
      -        ],
      -        "type": "string"
      -      },
      -      "channel": {
      -        "const": "linkedin",
      -        "type": "string"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "action",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "notify": {
      -        "default": false,
      -        "type": "boolean"
      -      },
      -      "then": {
      -        "additionalProperties": false,
      -        "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, a gift's delivery,\ntracking time off the start node, or the accept for a connection request's `accepted`.",
      -        "properties": {
      -          "after": {
      -            "anyOf": [
      -              {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "default": null
      -          },
      -          "to": {
      -            "type": "string"
      -          }
      -        },
      -        "required": [
      -          "to"
      -        ],
      -        "title": "Advance",
      -        "type": "object"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "channel",
      -      "action",
      -      "then"
      -    ],
      -    "title": "SilentActionNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A physical gift Sliq orders online and ships to each prospect's US address. Entry\nqueues one gift card per prospect onto the user's approval list, and nothing is ordered\nuntil the user approves a card. The card is where the user completes or changes the\nrecipient, shipping address, product, price ceiling and note. `product_url`,\n`max_price_usd` (the most the order may cost, in whole US dollars) and `gift_note` (at\nmost 240 characters) are the step's optional defaults, pre-filled on every card. No\nchannel and no reply to route; `then` is its single advance once the gift is delivered:\nbare (`then.after=None`) to move on at delivery, or held to wait that long after it.",
      -    "properties": {
      -      "gift_note": {
      -        "anyOf": [
      -          {
      -            "maxLength": 240,
      -            "type": "string"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "gift",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "max_price_usd": {
      -        "anyOf": [
      -          {
      -            "exclusiveMinimum": 0,
      -            "type": "integer"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      },
      -      "product_url": {
      -        "anyOf": [
      -          {
      -            "format": "uri",
      -            "maxLength": 2083,
      -            "minLength": 1,
      -            "type": "string"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      },
      -      "then": {
      -        "additionalProperties": false,
      -        "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, a gift's delivery,\ntracking time off the start node, or the accept for a connection request's `accepted`.",
      -        "properties": {
      -          "after": {
      -            "anyOf": [
      -              {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "default": null
      -          },
      -          "to": {
      -            "type": "string"
      -          }
      -        },
      -        "required": [
      -          "to"
      -        ],
      -        "title": "Advance",
      -        "type": "object"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "then"
      -    ],
      -    "title": "GiftNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a handwritten card, a recorded note). Entry queues a manual_action_queue row\ncarrying `action_description`; the user marking that row done advances the prospect\nalong `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.",
      -    "properties": {
      -      "action_description": {
      -        "type": "string"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "manual",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "then": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "action_description",
      -      "then"
      -    ],
      -    "title": "ManualActionNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.",
      -    "properties": {
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "automation",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "then": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "then"
      -    ],
      -    "title": "AutomationNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').",
      -    "properties": {
      -      "arms": {
      -        "items": {
      -          "additionalProperties": false,
      -          "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.",
      -          "properties": {
      -            "case": {
      -              "minLength": 1,
      -              "type": "string"
      -            },
      -            "to": {
      -              "type": "string"
      -            }
      -          },
      -          "required": [
      -            "case",
      -            "to"
      -          ],
      -          "title": "Arm",
      -          "type": "object"
      -        },
      -        "minItems": 1,
      -        "type": "array"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "decision",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "rule": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "rule",
      -      "arms"
      -    ],
      -    "title": "DecisionNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.",
      -    "properties": {
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "terminal",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind"
      -    ],
      -    "title": "TerminalNode",
      -    "type": "object"
      -  }
      -]New value: +[
      +  {
      +    "additionalProperties": false,
      +    "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept). `accepted.after`\nholds an accepted prospect on this node for that long after the accept is detected before\nthey advance.\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.",
      +    "properties": {
      +      "accepted": {
      +        "additionalProperties": false,
      +        "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.",
      +        "properties": {
      +          "after": {
      +            "anyOf": [
      +              {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "default": null
      +          },
      +          "to": {
      +            "type": "string"
      +          }
      +        },
      +        "required": [
      +          "to"
      +        ],
      +        "title": "Advance",
      +        "type": "object"
      +      },
      +      "already_connected": {
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "connection_request",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "message_templates": {
      +        "anyOf": [
      +          {
      +            "items": {
      +              "maxLength": 300,
      +              "type": "string"
      +            },
      +            "maxItems": 2,
      +            "type": "array"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "timeout": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      +            "properties": {
      +              "delay": {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              "to": {
      +                "type": "string"
      +              }
      +            },
      +            "required": [
      +              "delay",
      +              "to"
      +            ],
      +            "title": "TimedAdvance",
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "accepted",
      +      "already_connected"
      +    ],
      +    "title": "ConnectionRequestNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.",
      +    "properties": {
      +      "action": {
      +        "enum": [
      +          "message",
      +          "inmail",
      +          "email"
      +        ],
      +        "type": "string"
      +      },
      +      "channel": {
      +        "enum": [
      +          "linkedin",
      +          "email"
      +        ],
      +        "type": "string"
      +      },
      +      "follow_up": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      +            "properties": {
      +              "delay": {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              "to": {
      +                "type": "string"
      +              }
      +            },
      +            "required": [
      +              "delay",
      +              "to"
      +            ],
      +            "title": "TimedAdvance",
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "send",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "message_templates": {
      +        "anyOf": [
      +          {
      +            "items": {
      +              "type": "string"
      +            },
      +            "maxItems": 2,
      +            "type": "array"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "replied": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "channel",
      +      "action",
      +      "replied"
      +    ],
      +    "title": "SendNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.",
      +    "properties": {
      +      "action": {
      +        "enum": [
      +          "follow",
      +          "withdraw",
      +          "resolve",
      +          "comment",
      +          "reaction"
      +        ],
      +        "type": "string"
      +      },
      +      "channel": {
      +        "const": "linkedin",
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "action",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "notify": {
      +        "default": false,
      +        "type": "boolean"
      +      },
      +      "then": {
      +        "additionalProperties": false,
      +        "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.",
      +        "properties": {
      +          "after": {
      +            "anyOf": [
      +              {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "default": null
      +          },
      +          "to": {
      +            "type": "string"
      +          }
      +        },
      +        "required": [
      +          "to"
      +        ],
      +        "title": "Advance",
      +        "type": "object"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "channel",
      +      "action",
      +      "then"
      +    ],
      +    "title": "SilentActionNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A physical gift Sliq orders online and ships to each prospect's US address. Entry\nqueues one gift card per prospect onto the user's approval list, and nothing is ordered\nuntil the user approves a card. The card is where the user completes or changes the\nrecipient, shipping address, product, price ceiling and note. `product_url`,\n`max_price_usd` (the most the order may cost, in whole US dollars) and `gift_note` (at\nmost 240 characters) are the step's optional defaults, pre-filled on every card. No\nchannel and no reply to route; `then` is its single advance once the gift is delivered:\nbare (`then.after=None`) to move on at delivery, or held to wait that long after it.",
      +    "properties": {
      +      "gift_note": {
      +        "anyOf": [
      +          {
      +            "maxLength": 240,
      +            "type": "string"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "gift",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "max_price_usd": {
      +        "anyOf": [
      +          {
      +            "exclusiveMinimum": 0,
      +            "type": "integer"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "product_url": {
      +        "anyOf": [
      +          {
      +            "format": "uri",
      +            "maxLength": 2083,
      +            "minLength": 1,
      +            "type": "string"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "then": {
      +        "additionalProperties": false,
      +        "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold counts from what happened at the source: the event its out-edge's `Anchor` names, or\nthe prospect being tracked, off the start node.",
      +        "properties": {
      +          "after": {
      +            "anyOf": [
      +              {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "default": null
      +          },
      +          "to": {
      +            "type": "string"
      +          }
      +        },
      +        "required": [
      +          "to"
      +        ],
      +        "title": "Advance",
      +        "type": "object"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "then"
      +    ],
      +    "title": "GiftNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a handwritten card, a recorded note). Entry queues a manual_action_queue row\ncarrying `action_description`; the user marking that row done advances the prospect\nalong `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.",
      +    "properties": {
      +      "action_description": {
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "manual",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "then": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "action_description",
      +      "then"
      +    ],
      +    "title": "ManualActionNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.",
      +    "properties": {
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "automation",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "then": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "then"
      +    ],
      +    "title": "AutomationNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').",
      +    "properties": {
      +      "arms": {
      +        "items": {
      +          "additionalProperties": false,
      +          "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.",
      +          "properties": {
      +            "case": {
      +              "minLength": 1,
      +              "type": "string"
      +            },
      +            "to": {
      +              "type": "string"
      +            }
      +          },
      +          "required": [
      +            "case",
      +            "to"
      +          ],
      +          "title": "Arm",
      +          "type": "object"
      +        },
      +        "minItems": 1,
      +        "type": "array"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "decision",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "rule": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "rule",
      +      "arms"
      +    ],
      +    "title": "DecisionNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.",
      +    "properties": {
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "terminal",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind"
      +    ],
      +    "title": "TerminalNode",
      +    "type": "object"
      +  }
      +]
  3. Changed1 schema field changed
    • changedInput schema / properties / node / oneOf
      Previous value: -[
      -  {
      -    "additionalProperties": false,
      -    "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept). `accepted.after`\nholds an accepted prospect on this node for that long after the accept is detected before\nthey advance.\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.",
      -    "properties": {
      -      "accepted": {
      -        "additionalProperties": false,
      -        "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, tracking time off the\nstart node, or the accept for a connection request's `accepted`.",
      -        "properties": {
      -          "after": {
      -            "anyOf": [
      -              {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "default": null
      -          },
      -          "to": {
      -            "type": "string"
      -          }
      -        },
      -        "required": [
      -          "to"
      -        ],
      -        "title": "Advance",
      -        "type": "object"
      -      },
      -      "already_connected": {
      -        "type": "string"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "connection_request",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "message_templates": {
      -        "anyOf": [
      -          {
      -            "items": {
      -              "maxLength": 300,
      -              "type": "string"
      -            },
      -            "maxItems": 2,
      -            "type": "array"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      },
      -      "timeout": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      -            "properties": {
      -              "delay": {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              "to": {
      -                "type": "string"
      -              }
      -            },
      -            "required": [
      -              "delay",
      -              "to"
      -            ],
      -            "title": "TimedAdvance",
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "accepted",
      -      "already_connected"
      -    ],
      -    "title": "ConnectionRequestNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.",
      -    "properties": {
      -      "action": {
      -        "enum": [
      -          "message",
      -          "inmail",
      -          "email"
      -        ],
      -        "type": "string"
      -      },
      -      "channel": {
      -        "enum": [
      -          "linkedin",
      -          "email"
      -        ],
      -        "type": "string"
      -      },
      -      "follow_up": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      -            "properties": {
      -              "delay": {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              "to": {
      -                "type": "string"
      -              }
      -            },
      -            "required": [
      -              "delay",
      -              "to"
      -            ],
      -            "title": "TimedAdvance",
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "send",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "message_templates": {
      -        "anyOf": [
      -          {
      -            "items": {
      -              "type": "string"
      -            },
      -            "maxItems": 2,
      -            "type": "array"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      },
      -      "replied": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "channel",
      -      "action",
      -      "replied"
      -    ],
      -    "title": "SendNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.",
      -    "properties": {
      -      "action": {
      -        "enum": [
      -          "follow",
      -          "withdraw",
      -          "resolve",
      -          "comment",
      -          "reaction"
      -        ],
      -        "type": "string"
      -      },
      -      "channel": {
      -        "const": "linkedin",
      -        "type": "string"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "action",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "notify": {
      -        "default": false,
      -        "type": "boolean"
      -      },
      -      "then": {
      -        "additionalProperties": false,
      -        "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, tracking time off the\nstart node, or the accept for a connection request's `accepted`.",
      -        "properties": {
      -          "after": {
      -            "anyOf": [
      -              {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "default": null
      -          },
      -          "to": {
      -            "type": "string"
      -          }
      -        },
      -        "required": [
      -          "to"
      -        ],
      -        "title": "Advance",
      -        "type": "object"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "channel",
      -      "action",
      -      "then"
      -    ],
      -    "title": "SilentActionNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.",
      -    "properties": {
      -      "action_description": {
      -        "type": "string"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "manual",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "then": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "action_description",
      -      "then"
      -    ],
      -    "title": "ManualActionNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.",
      -    "properties": {
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "automation",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "then": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "then"
      -    ],
      -    "title": "AutomationNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').",
      -    "properties": {
      -      "arms": {
      -        "items": {
      -          "additionalProperties": false,
      -          "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.",
      -          "properties": {
      -            "case": {
      -              "minLength": 1,
      -              "type": "string"
      -            },
      -            "to": {
      -              "type": "string"
      -            }
      -          },
      -          "required": [
      -            "case",
      -            "to"
      -          ],
      -          "title": "Arm",
      -          "type": "object"
      -        },
      -        "minItems": 1,
      -        "type": "array"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "decision",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "rule": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "rule",
      -      "arms"
      -    ],
      -    "title": "DecisionNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.",
      -    "properties": {
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "terminal",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind"
      -    ],
      -    "title": "TerminalNode",
      -    "type": "object"
      -  }
      -]New value: +[
      +  {
      +    "additionalProperties": false,
      +    "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept). `accepted.after`\nholds an accepted prospect on this node for that long after the accept is detected before\nthey advance.\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.",
      +    "properties": {
      +      "accepted": {
      +        "additionalProperties": false,
      +        "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, a gift's delivery,\ntracking time off the start node, or the accept for a connection request's `accepted`.",
      +        "properties": {
      +          "after": {
      +            "anyOf": [
      +              {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "default": null
      +          },
      +          "to": {
      +            "type": "string"
      +          }
      +        },
      +        "required": [
      +          "to"
      +        ],
      +        "title": "Advance",
      +        "type": "object"
      +      },
      +      "already_connected": {
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "connection_request",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "message_templates": {
      +        "anyOf": [
      +          {
      +            "items": {
      +              "maxLength": 300,
      +              "type": "string"
      +            },
      +            "maxItems": 2,
      +            "type": "array"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "timeout": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      +            "properties": {
      +              "delay": {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              "to": {
      +                "type": "string"
      +              }
      +            },
      +            "required": [
      +              "delay",
      +              "to"
      +            ],
      +            "title": "TimedAdvance",
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "accepted",
      +      "already_connected"
      +    ],
      +    "title": "ConnectionRequestNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.",
      +    "properties": {
      +      "action": {
      +        "enum": [
      +          "message",
      +          "inmail",
      +          "email"
      +        ],
      +        "type": "string"
      +      },
      +      "channel": {
      +        "enum": [
      +          "linkedin",
      +          "email"
      +        ],
      +        "type": "string"
      +      },
      +      "follow_up": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      +            "properties": {
      +              "delay": {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              "to": {
      +                "type": "string"
      +              }
      +            },
      +            "required": [
      +              "delay",
      +              "to"
      +            ],
      +            "title": "TimedAdvance",
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "send",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "message_templates": {
      +        "anyOf": [
      +          {
      +            "items": {
      +              "type": "string"
      +            },
      +            "maxItems": 2,
      +            "type": "array"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "replied": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "channel",
      +      "action",
      +      "replied"
      +    ],
      +    "title": "SendNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.",
      +    "properties": {
      +      "action": {
      +        "enum": [
      +          "follow",
      +          "withdraw",
      +          "resolve",
      +          "comment",
      +          "reaction"
      +        ],
      +        "type": "string"
      +      },
      +      "channel": {
      +        "const": "linkedin",
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "action",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "notify": {
      +        "default": false,
      +        "type": "boolean"
      +      },
      +      "then": {
      +        "additionalProperties": false,
      +        "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, a gift's delivery,\ntracking time off the start node, or the accept for a connection request's `accepted`.",
      +        "properties": {
      +          "after": {
      +            "anyOf": [
      +              {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "default": null
      +          },
      +          "to": {
      +            "type": "string"
      +          }
      +        },
      +        "required": [
      +          "to"
      +        ],
      +        "title": "Advance",
      +        "type": "object"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "channel",
      +      "action",
      +      "then"
      +    ],
      +    "title": "SilentActionNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A physical gift Sliq orders online and ships to each prospect's US address. Entry\nqueues one gift card per prospect onto the user's approval list, and nothing is ordered\nuntil the user approves a card. The card is where the user completes or changes the\nrecipient, shipping address, product, price ceiling and note. `product_url`,\n`max_price_usd` (the most the order may cost, in whole US dollars) and `gift_note` (at\nmost 240 characters) are the step's optional defaults, pre-filled on every card. No\nchannel and no reply to route; `then` is its single advance once the gift is delivered:\nbare (`then.after=None`) to move on at delivery, or held to wait that long after it.",
      +    "properties": {
      +      "gift_note": {
      +        "anyOf": [
      +          {
      +            "maxLength": 240,
      +            "type": "string"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "gift",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "max_price_usd": {
      +        "anyOf": [
      +          {
      +            "exclusiveMinimum": 0,
      +            "type": "integer"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "product_url": {
      +        "anyOf": [
      +          {
      +            "format": "uri",
      +            "maxLength": 2083,
      +            "minLength": 1,
      +            "type": "string"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "then": {
      +        "additionalProperties": false,
      +        "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, a gift's delivery,\ntracking time off the start node, or the accept for a connection request's `accepted`.",
      +        "properties": {
      +          "after": {
      +            "anyOf": [
      +              {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "default": null
      +          },
      +          "to": {
      +            "type": "string"
      +          }
      +        },
      +        "required": [
      +          "to"
      +        ],
      +        "title": "Advance",
      +        "type": "object"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "then"
      +    ],
      +    "title": "GiftNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a handwritten card, a recorded note). Entry queues a manual_action_queue row\ncarrying `action_description`; the user marking that row done advances the prospect\nalong `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.",
      +    "properties": {
      +      "action_description": {
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "manual",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "then": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "action_description",
      +      "then"
      +    ],
      +    "title": "ManualActionNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.",
      +    "properties": {
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "automation",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "then": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "then"
      +    ],
      +    "title": "AutomationNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').",
      +    "properties": {
      +      "arms": {
      +        "items": {
      +          "additionalProperties": false,
      +          "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.",
      +          "properties": {
      +            "case": {
      +              "minLength": 1,
      +              "type": "string"
      +            },
      +            "to": {
      +              "type": "string"
      +            }
      +          },
      +          "required": [
      +            "case",
      +            "to"
      +          ],
      +          "title": "Arm",
      +          "type": "object"
      +        },
      +        "minItems": 1,
      +        "type": "array"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "decision",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "rule": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "rule",
      +      "arms"
      +    ],
      +    "title": "DecisionNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.",
      +    "properties": {
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "terminal",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind"
      +    ],
      +    "title": "TerminalNode",
      +    "type": "object"
      +  }
      +]
  4. Changed1 schema field changed
    • changedInput schema / properties / node / oneOf
      Previous value: -[
      -  {
      -    "additionalProperties": false,
      -    "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept).\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.",
      -    "properties": {
      -      "accepted": {
      -        "type": "string"
      -      },
      -      "already_connected": {
      -        "type": "string"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "connection_request",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "message_templates": {
      -        "anyOf": [
      -          {
      -            "items": {
      -              "maxLength": 300,
      -              "type": "string"
      -            },
      -            "maxItems": 2,
      -            "type": "array"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      },
      -      "timeout": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      -            "properties": {
      -              "delay": {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              "to": {
      -                "type": "string"
      -              }
      -            },
      -            "required": [
      -              "delay",
      -              "to"
      -            ],
      -            "title": "TimedAdvance",
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "accepted",
      -      "already_connected"
      -    ],
      -    "title": "ConnectionRequestNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.",
      -    "properties": {
      -      "action": {
      -        "enum": [
      -          "message",
      -          "inmail",
      -          "email"
      -        ],
      -        "type": "string"
      -      },
      -      "channel": {
      -        "enum": [
      -          "linkedin",
      -          "email"
      -        ],
      -        "type": "string"
      -      },
      -      "follow_up": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      -            "properties": {
      -              "delay": {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              "to": {
      -                "type": "string"
      -              }
      -            },
      -            "required": [
      -              "delay",
      -              "to"
      -            ],
      -            "title": "TimedAdvance",
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "send",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "message_templates": {
      -        "anyOf": [
      -          {
      -            "items": {
      -              "type": "string"
      -            },
      -            "maxItems": 2,
      -            "type": "array"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      },
      -      "replied": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "channel",
      -      "action",
      -      "replied"
      -    ],
      -    "title": "SendNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.",
      -    "properties": {
      -      "action": {
      -        "enum": [
      -          "follow",
      -          "withdraw",
      -          "resolve",
      -          "comment",
      -          "reaction"
      -        ],
      -        "type": "string"
      -      },
      -      "channel": {
      -        "const": "linkedin",
      -        "type": "string"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "action",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "notify": {
      -        "default": false,
      -        "type": "boolean"
      -      },
      -      "then": {
      -        "additionalProperties": false,
      -        "description": "An unconditional advance onto `to`. `after=None` fires the instant the source send\ncompletes (the send-completion advance); `after` set holds for that delay first (a\ntimed advance anchored on the send's sent_at, or on tracking time off the start node).",
      -        "properties": {
      -          "after": {
      -            "anyOf": [
      -              {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "default": null
      -          },
      -          "to": {
      -            "type": "string"
      -          }
      -        },
      -        "required": [
      -          "to"
      -        ],
      -        "title": "Advance",
      -        "type": "object"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "channel",
      -      "action",
      -      "then"
      -    ],
      -    "title": "SilentActionNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.",
      -    "properties": {
      -      "action_description": {
      -        "type": "string"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "manual",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "then": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "action_description",
      -      "then"
      -    ],
      -    "title": "ManualActionNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.",
      -    "properties": {
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "automation",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "then": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "then"
      -    ],
      -    "title": "AutomationNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').",
      -    "properties": {
      -      "arms": {
      -        "items": {
      -          "additionalProperties": false,
      -          "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.",
      -          "properties": {
      -            "case": {
      -              "minLength": 1,
      -              "type": "string"
      -            },
      -            "to": {
      -              "type": "string"
      -            }
      -          },
      -          "required": [
      -            "case",
      -            "to"
      -          ],
      -          "title": "Arm",
      -          "type": "object"
      -        },
      -        "minItems": 1,
      -        "type": "array"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "decision",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "rule": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "rule",
      -      "arms"
      -    ],
      -    "title": "DecisionNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.",
      -    "properties": {
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "terminal",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind"
      -    ],
      -    "title": "TerminalNode",
      -    "type": "object"
      -  }
      -]New value: +[
      +  {
      +    "additionalProperties": false,
      +    "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept). `accepted.after`\nholds an accepted prospect on this node for that long after the accept is detected before\nthey advance.\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.",
      +    "properties": {
      +      "accepted": {
      +        "additionalProperties": false,
      +        "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, tracking time off the\nstart node, or the accept for a connection request's `accepted`.",
      +        "properties": {
      +          "after": {
      +            "anyOf": [
      +              {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "default": null
      +          },
      +          "to": {
      +            "type": "string"
      +          }
      +        },
      +        "required": [
      +          "to"
      +        ],
      +        "title": "Advance",
      +        "type": "object"
      +      },
      +      "already_connected": {
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "connection_request",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "message_templates": {
      +        "anyOf": [
      +          {
      +            "items": {
      +              "maxLength": 300,
      +              "type": "string"
      +            },
      +            "maxItems": 2,
      +            "type": "array"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "timeout": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      +            "properties": {
      +              "delay": {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              "to": {
      +                "type": "string"
      +              }
      +            },
      +            "required": [
      +              "delay",
      +              "to"
      +            ],
      +            "title": "TimedAdvance",
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "accepted",
      +      "already_connected"
      +    ],
      +    "title": "ConnectionRequestNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.",
      +    "properties": {
      +      "action": {
      +        "enum": [
      +          "message",
      +          "inmail",
      +          "email"
      +        ],
      +        "type": "string"
      +      },
      +      "channel": {
      +        "enum": [
      +          "linkedin",
      +          "email"
      +        ],
      +        "type": "string"
      +      },
      +      "follow_up": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      +            "properties": {
      +              "delay": {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              "to": {
      +                "type": "string"
      +              }
      +            },
      +            "required": [
      +              "delay",
      +              "to"
      +            ],
      +            "title": "TimedAdvance",
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "send",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "message_templates": {
      +        "anyOf": [
      +          {
      +            "items": {
      +              "type": "string"
      +            },
      +            "maxItems": 2,
      +            "type": "array"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "replied": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "channel",
      +      "action",
      +      "replied"
      +    ],
      +    "title": "SendNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.",
      +    "properties": {
      +      "action": {
      +        "enum": [
      +          "follow",
      +          "withdraw",
      +          "resolve",
      +          "comment",
      +          "reaction"
      +        ],
      +        "type": "string"
      +      },
      +      "channel": {
      +        "const": "linkedin",
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "action",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "notify": {
      +        "default": false,
      +        "type": "boolean"
      +      },
      +      "then": {
      +        "additionalProperties": false,
      +        "description": "An advance onto `to`: immediate when `after` is None, else held that long first. The\nhold anchors on what happened at the source — the send's sent_at, tracking time off the\nstart node, or the accept for a connection request's `accepted`.",
      +        "properties": {
      +          "after": {
      +            "anyOf": [
      +              {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "default": null
      +          },
      +          "to": {
      +            "type": "string"
      +          }
      +        },
      +        "required": [
      +          "to"
      +        ],
      +        "title": "Advance",
      +        "type": "object"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "channel",
      +      "action",
      +      "then"
      +    ],
      +    "title": "SilentActionNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.",
      +    "properties": {
      +      "action_description": {
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "manual",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "then": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "action_description",
      +      "then"
      +    ],
      +    "title": "ManualActionNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.",
      +    "properties": {
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "automation",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "then": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "then"
      +    ],
      +    "title": "AutomationNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').",
      +    "properties": {
      +      "arms": {
      +        "items": {
      +          "additionalProperties": false,
      +          "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.",
      +          "properties": {
      +            "case": {
      +              "minLength": 1,
      +              "type": "string"
      +            },
      +            "to": {
      +              "type": "string"
      +            }
      +          },
      +          "required": [
      +            "case",
      +            "to"
      +          ],
      +          "title": "Arm",
      +          "type": "object"
      +        },
      +        "minItems": 1,
      +        "type": "array"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "decision",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "rule": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "rule",
      +      "arms"
      +    ],
      +    "title": "DecisionNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.",
      +    "properties": {
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "terminal",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind"
      +    ],
      +    "title": "TerminalNode",
      +    "type": "object"
      +  }
      +]
  5. Changed2 schema fields changed
    • changedInput schema / properties / node / description
      Previous value: -"The node's complete new definition — an omitted optional slot (a `follow_up`, a\n`timeout`) is dropped, not preserved, so carry forward every field the node keeps.\nA send node's `message_templates` is the exception: omitted leaves the step's\ntemplates as they are.\nIts `id` must name an existing body node and its `kind` must match that node's\ncurrent kind."New value: +"The node's complete new definition — an omitted optional slot (a `follow_up`, a\n`timeout`) is dropped, not preserved, so carry forward every field the node keeps.\nA send or connection-request node's `message_templates` is the exception: omitted\nleaves the step's templates as they are.\nIts `id` must name an existing body node and its `kind` must match that node's\ncurrent kind."
    • changedInput schema / properties / node / oneOf
      Previous value: -[
      -  {
      -    "additionalProperties": false,
      -    "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept).",
      -    "properties": {
      -      "accepted": {
      -        "type": "string"
      -      },
      -      "already_connected": {
      -        "type": "string"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "connection_request",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "timeout": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      -            "properties": {
      -              "delay": {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              "to": {
      -                "type": "string"
      -              }
      -            },
      -            "required": [
      -              "delay",
      -              "to"
      -            ],
      -            "title": "TimedAdvance",
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "accepted",
      -      "already_connected"
      -    ],
      -    "title": "ConnectionRequestNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.",
      -    "properties": {
      -      "action": {
      -        "enum": [
      -          "message",
      -          "inmail",
      -          "email"
      -        ],
      -        "type": "string"
      -      },
      -      "channel": {
      -        "enum": [
      -          "linkedin",
      -          "email"
      -        ],
      -        "type": "string"
      -      },
      -      "follow_up": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      -            "properties": {
      -              "delay": {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              "to": {
      -                "type": "string"
      -              }
      -            },
      -            "required": [
      -              "delay",
      -              "to"
      -            ],
      -            "title": "TimedAdvance",
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "send",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "message_templates": {
      -        "anyOf": [
      -          {
      -            "items": {
      -              "type": "string"
      -            },
      -            "maxItems": 2,
      -            "type": "array"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      },
      -      "replied": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "channel",
      -      "action",
      -      "replied"
      -    ],
      -    "title": "SendNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.",
      -    "properties": {
      -      "action": {
      -        "enum": [
      -          "follow",
      -          "withdraw",
      -          "resolve",
      -          "comment",
      -          "reaction"
      -        ],
      -        "type": "string"
      -      },
      -      "channel": {
      -        "const": "linkedin",
      -        "type": "string"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "action",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "notify": {
      -        "default": false,
      -        "type": "boolean"
      -      },
      -      "then": {
      -        "additionalProperties": false,
      -        "description": "An unconditional advance onto `to`. `after=None` fires the instant the source send\ncompletes (the send-completion advance); `after` set holds for that delay first (a\ntimed advance anchored on the send's sent_at, or on tracking time off the start node).",
      -        "properties": {
      -          "after": {
      -            "anyOf": [
      -              {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "default": null
      -          },
      -          "to": {
      -            "type": "string"
      -          }
      -        },
      -        "required": [
      -          "to"
      -        ],
      -        "title": "Advance",
      -        "type": "object"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "channel",
      -      "action",
      -      "then"
      -    ],
      -    "title": "SilentActionNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.",
      -    "properties": {
      -      "action_description": {
      -        "type": "string"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "manual",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "then": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "action_description",
      -      "then"
      -    ],
      -    "title": "ManualActionNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.",
      -    "properties": {
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "automation",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "then": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "then"
      -    ],
      -    "title": "AutomationNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').",
      -    "properties": {
      -      "arms": {
      -        "items": {
      -          "additionalProperties": false,
      -          "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.",
      -          "properties": {
      -            "case": {
      -              "minLength": 1,
      -              "type": "string"
      -            },
      -            "to": {
      -              "type": "string"
      -            }
      -          },
      -          "required": [
      -            "case",
      -            "to"
      -          ],
      -          "title": "Arm",
      -          "type": "object"
      -        },
      -        "minItems": 1,
      -        "type": "array"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "decision",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "rule": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "rule",
      -      "arms"
      -    ],
      -    "title": "DecisionNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.",
      -    "properties": {
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "terminal",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind"
      -    ],
      -    "title": "TerminalNode",
      -    "type": "object"
      -  }
      -]New value: +[
      +  {
      +    "additionalProperties": false,
      +    "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept).\n\n`message_templates` is input-only, as on a send node, and holds the request's note (at most\n300 characters): `''` is a version sent without one, so `['', note]` A/B tests a note\nagainst none.",
      +    "properties": {
      +      "accepted": {
      +        "type": "string"
      +      },
      +      "already_connected": {
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "connection_request",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "message_templates": {
      +        "anyOf": [
      +          {
      +            "items": {
      +              "maxLength": 300,
      +              "type": "string"
      +            },
      +            "maxItems": 2,
      +            "type": "array"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "timeout": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      +            "properties": {
      +              "delay": {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              "to": {
      +                "type": "string"
      +              }
      +            },
      +            "required": [
      +              "delay",
      +              "to"
      +            ],
      +            "title": "TimedAdvance",
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "accepted",
      +      "already_connected"
      +    ],
      +    "title": "ConnectionRequestNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.",
      +    "properties": {
      +      "action": {
      +        "enum": [
      +          "message",
      +          "inmail",
      +          "email"
      +        ],
      +        "type": "string"
      +      },
      +      "channel": {
      +        "enum": [
      +          "linkedin",
      +          "email"
      +        ],
      +        "type": "string"
      +      },
      +      "follow_up": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      +            "properties": {
      +              "delay": {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              "to": {
      +                "type": "string"
      +              }
      +            },
      +            "required": [
      +              "delay",
      +              "to"
      +            ],
      +            "title": "TimedAdvance",
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "send",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "message_templates": {
      +        "anyOf": [
      +          {
      +            "items": {
      +              "type": "string"
      +            },
      +            "maxItems": 2,
      +            "type": "array"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "replied": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "channel",
      +      "action",
      +      "replied"
      +    ],
      +    "title": "SendNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.",
      +    "properties": {
      +      "action": {
      +        "enum": [
      +          "follow",
      +          "withdraw",
      +          "resolve",
      +          "comment",
      +          "reaction"
      +        ],
      +        "type": "string"
      +      },
      +      "channel": {
      +        "const": "linkedin",
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "action",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "notify": {
      +        "default": false,
      +        "type": "boolean"
      +      },
      +      "then": {
      +        "additionalProperties": false,
      +        "description": "An unconditional advance onto `to`. `after=None` fires the instant the source send\ncompletes (the send-completion advance); `after` set holds for that delay first (a\ntimed advance anchored on the send's sent_at, or on tracking time off the start node).",
      +        "properties": {
      +          "after": {
      +            "anyOf": [
      +              {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "default": null
      +          },
      +          "to": {
      +            "type": "string"
      +          }
      +        },
      +        "required": [
      +          "to"
      +        ],
      +        "title": "Advance",
      +        "type": "object"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "channel",
      +      "action",
      +      "then"
      +    ],
      +    "title": "SilentActionNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.",
      +    "properties": {
      +      "action_description": {
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "manual",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "then": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "action_description",
      +      "then"
      +    ],
      +    "title": "ManualActionNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.",
      +    "properties": {
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "automation",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "then": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "then"
      +    ],
      +    "title": "AutomationNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').",
      +    "properties": {
      +      "arms": {
      +        "items": {
      +          "additionalProperties": false,
      +          "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.",
      +          "properties": {
      +            "case": {
      +              "minLength": 1,
      +              "type": "string"
      +            },
      +            "to": {
      +              "type": "string"
      +            }
      +          },
      +          "required": [
      +            "case",
      +            "to"
      +          ],
      +          "title": "Arm",
      +          "type": "object"
      +        },
      +        "minItems": 1,
      +        "type": "array"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "decision",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "rule": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "rule",
      +      "arms"
      +    ],
      +    "title": "DecisionNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.",
      +    "properties": {
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "terminal",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind"
      +    ],
      +    "title": "TerminalNode",
      +    "type": "object"
      +  }
      +]
  6. Changed2 schema fields changed
    • changedInput schema / properties / node / description
      Previous value: -"The node's complete new definition — an omitted optional slot (a `follow_up`, a\n`timeout`) is dropped, not preserved, so carry forward every field the node keeps.\nA send node's `message_template` is the exception: omitted leaves its template as it is.\nIts `id` must name an existing body node and its `kind` must match that node's\ncurrent kind."New value: +"The node's complete new definition — an omitted optional slot (a `follow_up`, a\n`timeout`) is dropped, not preserved, so carry forward every field the node keeps.\nA send node's `message_templates` is the exception: omitted leaves the step's\ntemplates as they are.\nIts `id` must name an existing body node and its `kind` must match that node's\ncurrent kind."
    • changedInput schema / properties / node / oneOf
      Previous value: -[
      -  {
      -    "additionalProperties": false,
      -    "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept).",
      -    "properties": {
      -      "accepted": {
      -        "type": "string"
      -      },
      -      "already_connected": {
      -        "type": "string"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "connection_request",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "timeout": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      -            "properties": {
      -              "delay": {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              "to": {
      -                "type": "string"
      -              }
      -            },
      -            "required": [
      -              "delay",
      -              "to"
      -            ],
      -            "title": "TimedAdvance",
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "accepted",
      -      "already_connected"
      -    ],
      -    "title": "ConnectionRequestNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_template` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's template lives in its own rows and is merged back in on read.",
      -    "properties": {
      -      "action": {
      -        "enum": [
      -          "message",
      -          "inmail",
      -          "email"
      -        ],
      -        "type": "string"
      -      },
      -      "channel": {
      -        "enum": [
      -          "linkedin",
      -          "email"
      -        ],
      -        "type": "string"
      -      },
      -      "follow_up": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      -            "properties": {
      -              "delay": {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              "to": {
      -                "type": "string"
      -              }
      -            },
      -            "required": [
      -              "delay",
      -              "to"
      -            ],
      -            "title": "TimedAdvance",
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "send",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "message_template": {
      -        "anyOf": [
      -          {
      -            "type": "string"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      },
      -      "replied": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "channel",
      -      "action",
      -      "replied"
      -    ],
      -    "title": "SendNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.",
      -    "properties": {
      -      "action": {
      -        "enum": [
      -          "follow",
      -          "withdraw",
      -          "resolve",
      -          "comment",
      -          "reaction"
      -        ],
      -        "type": "string"
      -      },
      -      "channel": {
      -        "const": "linkedin",
      -        "type": "string"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "action",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "notify": {
      -        "default": false,
      -        "type": "boolean"
      -      },
      -      "then": {
      -        "additionalProperties": false,
      -        "description": "An unconditional advance onto `to`. `after=None` fires the instant the source send\ncompletes (the send-completion advance); `after` set holds for that delay first (a\ntimed advance anchored on the send's sent_at, or on tracking time off the start node).",
      -        "properties": {
      -          "after": {
      -            "anyOf": [
      -              {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "default": null
      -          },
      -          "to": {
      -            "type": "string"
      -          }
      -        },
      -        "required": [
      -          "to"
      -        ],
      -        "title": "Advance",
      -        "type": "object"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "channel",
      -      "action",
      -      "then"
      -    ],
      -    "title": "SilentActionNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.",
      -    "properties": {
      -      "action_description": {
      -        "type": "string"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "manual",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "then": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "action_description",
      -      "then"
      -    ],
      -    "title": "ManualActionNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.",
      -    "properties": {
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "automation",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "then": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "then"
      -    ],
      -    "title": "AutomationNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').",
      -    "properties": {
      -      "arms": {
      -        "items": {
      -          "additionalProperties": false,
      -          "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.",
      -          "properties": {
      -            "case": {
      -              "minLength": 1,
      -              "type": "string"
      -            },
      -            "to": {
      -              "type": "string"
      -            }
      -          },
      -          "required": [
      -            "case",
      -            "to"
      -          ],
      -          "title": "Arm",
      -          "type": "object"
      -        },
      -        "minItems": 1,
      -        "type": "array"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "decision",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "rule": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "rule",
      -      "arms"
      -    ],
      -    "title": "DecisionNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.",
      -    "properties": {
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "terminal",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind"
      -    ],
      -    "title": "TerminalNode",
      -    "type": "object"
      -  }
      -]New value: +[
      +  {
      +    "additionalProperties": false,
      +    "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept).",
      +    "properties": {
      +      "accepted": {
      +        "type": "string"
      +      },
      +      "already_connected": {
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "connection_request",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "timeout": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      +            "properties": {
      +              "delay": {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              "to": {
      +                "type": "string"
      +              }
      +            },
      +            "required": [
      +              "delay",
      +              "to"
      +            ],
      +            "title": "TimedAdvance",
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "accepted",
      +      "already_connected"
      +    ],
      +    "title": "ConnectionRequestNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_templates` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's templates live in their own rows and are merged back in on read. A step\nwith two is running an A/B test.",
      +    "properties": {
      +      "action": {
      +        "enum": [
      +          "message",
      +          "inmail",
      +          "email"
      +        ],
      +        "type": "string"
      +      },
      +      "channel": {
      +        "enum": [
      +          "linkedin",
      +          "email"
      +        ],
      +        "type": "string"
      +      },
      +      "follow_up": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      +            "properties": {
      +              "delay": {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              "to": {
      +                "type": "string"
      +              }
      +            },
      +            "required": [
      +              "delay",
      +              "to"
      +            ],
      +            "title": "TimedAdvance",
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "send",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "message_templates": {
      +        "anyOf": [
      +          {
      +            "items": {
      +              "type": "string"
      +            },
      +            "maxItems": 2,
      +            "type": "array"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "replied": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "channel",
      +      "action",
      +      "replied"
      +    ],
      +    "title": "SendNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.",
      +    "properties": {
      +      "action": {
      +        "enum": [
      +          "follow",
      +          "withdraw",
      +          "resolve",
      +          "comment",
      +          "reaction"
      +        ],
      +        "type": "string"
      +      },
      +      "channel": {
      +        "const": "linkedin",
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "action",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "notify": {
      +        "default": false,
      +        "type": "boolean"
      +      },
      +      "then": {
      +        "additionalProperties": false,
      +        "description": "An unconditional advance onto `to`. `after=None` fires the instant the source send\ncompletes (the send-completion advance); `after` set holds for that delay first (a\ntimed advance anchored on the send's sent_at, or on tracking time off the start node).",
      +        "properties": {
      +          "after": {
      +            "anyOf": [
      +              {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "default": null
      +          },
      +          "to": {
      +            "type": "string"
      +          }
      +        },
      +        "required": [
      +          "to"
      +        ],
      +        "title": "Advance",
      +        "type": "object"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "channel",
      +      "action",
      +      "then"
      +    ],
      +    "title": "SilentActionNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.",
      +    "properties": {
      +      "action_description": {
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "manual",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "then": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "action_description",
      +      "then"
      +    ],
      +    "title": "ManualActionNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.",
      +    "properties": {
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "automation",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "then": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "then"
      +    ],
      +    "title": "AutomationNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').",
      +    "properties": {
      +      "arms": {
      +        "items": {
      +          "additionalProperties": false,
      +          "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.",
      +          "properties": {
      +            "case": {
      +              "minLength": 1,
      +              "type": "string"
      +            },
      +            "to": {
      +              "type": "string"
      +            }
      +          },
      +          "required": [
      +            "case",
      +            "to"
      +          ],
      +          "title": "Arm",
      +          "type": "object"
      +        },
      +        "minItems": 1,
      +        "type": "array"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "decision",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "rule": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "rule",
      +      "arms"
      +    ],
      +    "title": "DecisionNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.",
      +    "properties": {
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "terminal",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind"
      +    ],
      +    "title": "TerminalNode",
      +    "type": "object"
      +  }
      +]
  7. Changed5 schema fields changed
    • addedInput schema / properties / agent_id
      Added value: +{
      +  "description": "ID of the agent whose flow holds the node.",
      +  "type": "integer"
      +}
    • changedInput schema / properties / node / description
      Previous value: -"The node's complete new definition — an omitted optional slot (a `follow_up`, a\n`timeout`) is dropped, not preserved, so carry forward every field the node keeps.\nIts `id` must name an existing body node and its `kind` must match that node's\ncurrent kind."New value: +"The node's complete new definition — an omitted optional slot (a `follow_up`, a\n`timeout`) is dropped, not preserved, so carry forward every field the node keeps.\nA send node's `message_template` is the exception: omitted leaves its template as it is.\nIts `id` must name an existing body node and its `kind` must match that node's\ncurrent kind."
    • changedInput schema / properties / node / oneOf
      Previous value: -[
      -  {
      -    "additionalProperties": false,
      -    "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept).",
      -    "properties": {
      -      "accepted": {
      -        "type": "string"
      -      },
      -      "already_connected": {
      -        "type": "string"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "connection_request",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "timeout": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      -            "properties": {
      -              "delay": {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              "to": {
      -                "type": "string"
      -              }
      -            },
      -            "required": [
      -              "delay",
      -              "to"
      -            ],
      -            "title": "TimedAdvance",
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "accepted",
      -      "already_connected"
      -    ],
      -    "title": "ConnectionRequestNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).",
      -    "properties": {
      -      "action": {
      -        "enum": [
      -          "message",
      -          "inmail",
      -          "email"
      -        ],
      -        "type": "string"
      -      },
      -      "channel": {
      -        "enum": [
      -          "linkedin",
      -          "email"
      -        ],
      -        "type": "string"
      -      },
      -      "follow_up": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      -            "properties": {
      -              "delay": {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              "to": {
      -                "type": "string"
      -              }
      -            },
      -            "required": [
      -              "delay",
      -              "to"
      -            ],
      -            "title": "TimedAdvance",
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "send",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "replied": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "channel",
      -      "action",
      -      "replied"
      -    ],
      -    "title": "SendNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.",
      -    "properties": {
      -      "action": {
      -        "enum": [
      -          "follow",
      -          "withdraw",
      -          "resolve",
      -          "comment",
      -          "reaction"
      -        ],
      -        "type": "string"
      -      },
      -      "channel": {
      -        "const": "linkedin",
      -        "type": "string"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "action",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "notify": {
      -        "default": false,
      -        "type": "boolean"
      -      },
      -      "then": {
      -        "additionalProperties": false,
      -        "description": "An unconditional advance onto `to`. `after=None` fires the instant the source send\ncompletes (the send-completion advance); `after` set holds for that delay first (a\ntimed advance anchored on the send's sent_at, or on tracking time off the start node).",
      -        "properties": {
      -          "after": {
      -            "anyOf": [
      -              {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "cd",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "default": null
      -          },
      -          "to": {
      -            "type": "string"
      -          }
      -        },
      -        "required": [
      -          "to"
      -        ],
      -        "title": "Advance",
      -        "type": "object"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "channel",
      -      "action",
      -      "then"
      -    ],
      -    "title": "SilentActionNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.",
      -    "properties": {
      -      "action_description": {
      -        "type": "string"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "manual",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "then": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "action_description",
      -      "then"
      -    ],
      -    "title": "ManualActionNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a send node's template and a terminal node's hook — not a field\nhere. Unlike a manual node it boots an agent run (rather than waiting on a human) and unlike\na terminal hook it advances; structurally it is a decision with one implicit 'always' arm —\nthe run does the side-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.",
      -    "properties": {
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "automation",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "then": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "then"
      -    ],
      -    "title": "AutomationNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').",
      -    "properties": {
      -      "arms": {
      -        "items": {
      -          "additionalProperties": false,
      -          "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.",
      -          "properties": {
      -            "case": {
      -              "minLength": 1,
      -              "type": "string"
      -            },
      -            "to": {
      -              "type": "string"
      -            }
      -          },
      -          "required": [
      -            "case",
      -            "to"
      -          ],
      -          "title": "Arm",
      -          "type": "object"
      -        },
      -        "minItems": 1,
      -        "type": "array"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "decision",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "rule": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "rule",
      -      "arms"
      -    ],
      -    "title": "DecisionNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.",
      -    "properties": {
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "terminal",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind"
      -    ],
      -    "title": "TerminalNode",
      -    "type": "object"
      -  }
      -]New value: +[
      +  {
      +    "additionalProperties": false,
      +    "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept).",
      +    "properties": {
      +      "accepted": {
      +        "type": "string"
      +      },
      +      "already_connected": {
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "connection_request",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "timeout": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      +            "properties": {
      +              "delay": {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              "to": {
      +                "type": "string"
      +              }
      +            },
      +            "required": [
      +              "delay",
      +              "to"
      +            ],
      +            "title": "TimedAdvance",
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "accepted",
      +      "already_connected"
      +    ],
      +    "title": "ConnectionRequestNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).\n\n`message_template` is input-only: excluded from every dump, so it never lands in the stored\nsequence — the step's template lives in its own rows and is merged back in on read.",
      +    "properties": {
      +      "action": {
      +        "enum": [
      +          "message",
      +          "inmail",
      +          "email"
      +        ],
      +        "type": "string"
      +      },
      +      "channel": {
      +        "enum": [
      +          "linkedin",
      +          "email"
      +        ],
      +        "type": "string"
      +      },
      +      "follow_up": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      +            "properties": {
      +              "delay": {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              "to": {
      +                "type": "string"
      +              }
      +            },
      +            "required": [
      +              "delay",
      +              "to"
      +            ],
      +            "title": "TimedAdvance",
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "send",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "message_template": {
      +        "anyOf": [
      +          {
      +            "type": "string"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "replied": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "channel",
      +      "action",
      +      "replied"
      +    ],
      +    "title": "SendNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.",
      +    "properties": {
      +      "action": {
      +        "enum": [
      +          "follow",
      +          "withdraw",
      +          "resolve",
      +          "comment",
      +          "reaction"
      +        ],
      +        "type": "string"
      +      },
      +      "channel": {
      +        "const": "linkedin",
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "action",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "notify": {
      +        "default": false,
      +        "type": "boolean"
      +      },
      +      "then": {
      +        "additionalProperties": false,
      +        "description": "An unconditional advance onto `to`. `after=None` fires the instant the source send\ncompletes (the send-completion advance); `after` set holds for that delay first (a\ntimed advance anchored on the send's sent_at, or on tracking time off the start node).",
      +        "properties": {
      +          "after": {
      +            "anyOf": [
      +              {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "default": null
      +          },
      +          "to": {
      +            "type": "string"
      +          }
      +        },
      +        "required": [
      +          "to"
      +        ],
      +        "title": "Advance",
      +        "type": "object"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "channel",
      +      "action",
      +      "then"
      +    ],
      +    "title": "SilentActionNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.",
      +    "properties": {
      +      "action_description": {
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "manual",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "then": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "action_description",
      +      "then"
      +    ],
      +    "title": "ManualActionNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a terminal node's hook — not a field here. Unlike a manual node\nit boots an agent run (rather than waiting on a human) and unlike a terminal hook it\nadvances; structurally it is a decision with one implicit 'always' arm — the run does the\nside-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.",
      +    "properties": {
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "automation",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "then": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "then"
      +    ],
      +    "title": "AutomationNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').",
      +    "properties": {
      +      "arms": {
      +        "items": {
      +          "additionalProperties": false,
      +          "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.",
      +          "properties": {
      +            "case": {
      +              "minLength": 1,
      +              "type": "string"
      +            },
      +            "to": {
      +              "type": "string"
      +            }
      +          },
      +          "required": [
      +            "case",
      +            "to"
      +          ],
      +          "title": "Arm",
      +          "type": "object"
      +        },
      +        "minItems": 1,
      +        "type": "array"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "decision",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "rule": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "rule",
      +      "arms"
      +    ],
      +    "title": "DecisionNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.",
      +    "properties": {
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "terminal",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind"
      +    ],
      +    "title": "TerminalNode",
      +    "type": "object"
      +  }
      +]
    • removedInput schema / properties / task_id
      Removed value: -{
      -  "description": "ID of the task whose flow holds the node.",
      -  "type": "integer"
      -}
    • changedInput schema / required
      Previous value: -[
      -  "task_id",
      -  "node"
      -]New value: +[
      +  "agent_id",
      +  "node"
      +]
  8. Changed1 schema field changed
    • changedInput schema / properties / node / oneOf
      Previous value: -[
      -  {
      -    "additionalProperties": false,
      -    "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept).",
      -    "properties": {
      -      "accepted": {
      -        "type": "string"
      -      },
      -      "already_connected": {
      -        "type": "string"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "connection_request",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "timeout": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      -            "properties": {
      -              "delay": {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit` (w/d/h/m). Validated by construction — no\nfree-form string to parse — and stored JSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              "to": {
      -                "type": "string"
      -              }
      -            },
      -            "required": [
      -              "delay",
      -              "to"
      -            ],
      -            "title": "TimedAdvance",
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "accepted",
      -      "already_connected"
      -    ],
      -    "title": "ConnectionRequestNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).",
      -    "properties": {
      -      "action": {
      -        "enum": [
      -          "message",
      -          "inmail",
      -          "email"
      -        ],
      -        "type": "string"
      -      },
      -      "channel": {
      -        "enum": [
      -          "linkedin",
      -          "email"
      -        ],
      -        "type": "string"
      -      },
      -      "follow_up": {
      -        "anyOf": [
      -          {
      -            "additionalProperties": false,
      -            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      -            "properties": {
      -              "delay": {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit` (w/d/h/m). Validated by construction — no\nfree-form string to parse — and stored JSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              "to": {
      -                "type": "string"
      -              }
      -            },
      -            "required": [
      -              "delay",
      -              "to"
      -            ],
      -            "title": "TimedAdvance",
      -            "type": "object"
      -          },
      -          {
      -            "type": "null"
      -          }
      -        ],
      -        "default": null
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "send",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "replied": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "channel",
      -      "action",
      -      "replied"
      -    ],
      -    "title": "SendNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.",
      -    "properties": {
      -      "action": {
      -        "enum": [
      -          "follow",
      -          "withdraw",
      -          "resolve",
      -          "comment",
      -          "reaction"
      -        ],
      -        "type": "string"
      -      },
      -      "channel": {
      -        "const": "linkedin",
      -        "type": "string"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "action",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "notify": {
      -        "default": false,
      -        "type": "boolean"
      -      },
      -      "then": {
      -        "additionalProperties": false,
      -        "description": "An unconditional advance onto `to`. `after=None` fires the instant the source send\ncompletes (the send-completion advance); `after` set holds for that delay first (a\ntimed advance anchored on the send's sent_at, or on tracking time off the start node).",
      -        "properties": {
      -          "after": {
      -            "anyOf": [
      -              {
      -                "additionalProperties": false,
      -                "description": "A timed hold: `value` units of `unit` (w/d/h/m). Validated by construction — no\nfree-form string to parse — and stored JSON-natively as `{value, unit}`.",
      -                "properties": {
      -                  "unit": {
      -                    "enum": [
      -                      "w",
      -                      "d",
      -                      "h",
      -                      "m"
      -                    ],
      -                    "type": "string"
      -                  },
      -                  "value": {
      -                    "exclusiveMinimum": 0,
      -                    "type": "integer"
      -                  }
      -                },
      -                "required": [
      -                  "value",
      -                  "unit"
      -                ],
      -                "title": "EdgeDelay",
      -                "type": "object"
      -              },
      -              {
      -                "type": "null"
      -              }
      -            ],
      -            "default": null
      -          },
      -          "to": {
      -            "type": "string"
      -          }
      -        },
      -        "required": [
      -          "to"
      -        ],
      -        "title": "Advance",
      -        "type": "object"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "channel",
      -      "action",
      -      "then"
      -    ],
      -    "title": "SilentActionNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.",
      -    "properties": {
      -      "action_description": {
      -        "type": "string"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "manual",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "then": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "action_description",
      -      "then"
      -    ],
      -    "title": "ManualActionNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a send node's template and a terminal node's hook — not a field\nhere. Unlike a manual node it boots an agent run (rather than waiting on a human) and unlike\na terminal hook it advances; structurally it is a decision with one implicit 'always' arm —\nthe run does the side-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.",
      -    "properties": {
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "automation",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "then": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "then"
      -    ],
      -    "title": "AutomationNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').",
      -    "properties": {
      -      "arms": {
      -        "items": {
      -          "additionalProperties": false,
      -          "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.",
      -          "properties": {
      -            "case": {
      -              "minLength": 1,
      -              "type": "string"
      -            },
      -            "to": {
      -              "type": "string"
      -            }
      -          },
      -          "required": [
      -            "case",
      -            "to"
      -          ],
      -          "title": "Arm",
      -          "type": "object"
      -        },
      -        "minItems": 1,
      -        "type": "array"
      -      },
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "decision",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      },
      -      "rule": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind",
      -      "rule",
      -      "arms"
      -    ],
      -    "title": "DecisionNode",
      -    "type": "object"
      -  },
      -  {
      -    "additionalProperties": false,
      -    "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.",
      -    "properties": {
      -      "id": {
      -        "type": "string"
      -      },
      -      "kind": {
      -        "const": "terminal",
      -        "type": "string"
      -      },
      -      "label": {
      -        "type": "string"
      -      }
      -    },
      -    "required": [
      -      "id",
      -      "label",
      -      "kind"
      -    ],
      -    "title": "TerminalNode",
      -    "type": "object"
      -  }
      -]New value: +[
      +  {
      +    "additionalProperties": false,
      +    "description": "A LinkedIn connection request. Routes both send outcomes — `accepted` (invite\ntaken) and `already_connected` (a no-op CR on an existing 1st-degree connection) — and\nan optional `timeout` (a held advance, e.g. withdraw after no accept).",
      +    "properties": {
      +      "accepted": {
      +        "type": "string"
      +      },
      +      "already_connected": {
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "connection_request",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "timeout": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      +            "properties": {
      +              "delay": {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              "to": {
      +                "type": "string"
      +              }
      +            },
      +            "required": [
      +              "delay",
      +              "to"
      +            ],
      +            "title": "TimedAdvance",
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "accepted",
      +      "already_connected"
      +    ],
      +    "title": "ConnectionRequestNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A repliable send — a LinkedIn message/inmail, or an email. Routes `replied` (a reply\nadvances the prospect off the node) and an optional `follow_up` (a held no-reply canned\nfollow-on, itself just another send reached after the delay).",
      +    "properties": {
      +      "action": {
      +        "enum": [
      +          "message",
      +          "inmail",
      +          "email"
      +        ],
      +        "type": "string"
      +      },
      +      "channel": {
      +        "enum": [
      +          "linkedin",
      +          "email"
      +        ],
      +        "type": "string"
      +      },
      +      "follow_up": {
      +        "anyOf": [
      +          {
      +            "additionalProperties": false,
      +            "description": "A held follow-on anchored on this node's send — always delayed (no instant form).\n    ",
      +            "properties": {
      +              "delay": {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              "to": {
      +                "type": "string"
      +              }
      +            },
      +            "required": [
      +              "delay",
      +              "to"
      +            ],
      +            "title": "TimedAdvance",
      +            "type": "object"
      +          },
      +          {
      +            "type": "null"
      +          }
      +        ],
      +        "default": null
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "send",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "replied": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "channel",
      +      "action",
      +      "replied"
      +    ],
      +    "title": "SendNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A bodyless LinkedIn action (follow / withdraw / resolve / comment / reaction) — no\nreply to route. `then` is its single advance: bare (`then.after=None`) flows straight\ninto the next node when the send completes, or held for a delay first.\n\n`notify` applies only to `action='resolve'`: True makes the profile lookup a *visible*\nvisit (\"View Profile\") that notifies the person — a warm-up touch — instead of the\nsilent data lookup. Default False.",
      +    "properties": {
      +      "action": {
      +        "enum": [
      +          "follow",
      +          "withdraw",
      +          "resolve",
      +          "comment",
      +          "reaction"
      +        ],
      +        "type": "string"
      +      },
      +      "channel": {
      +        "const": "linkedin",
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "action",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "notify": {
      +        "default": false,
      +        "type": "boolean"
      +      },
      +      "then": {
      +        "additionalProperties": false,
      +        "description": "An unconditional advance onto `to`. `after=None` fires the instant the source send\ncompletes (the send-completion advance); `after` set holds for that delay first (a\ntimed advance anchored on the send's sent_at, or on tracking time off the start node).",
      +        "properties": {
      +          "after": {
      +            "anyOf": [
      +              {
      +                "additionalProperties": false,
      +                "description": "A timed hold: `value` units of `unit`. A `d` (day) hold counts BUSINESS days — Mon–Fri,\nweekends skipped — the outreach default; `cd` counts calendar days (weekends included), as\ndo `w`/`h`/`m`. Validated by construction (no free-form string to parse) and stored\nJSON-natively as `{value, unit}`.",
      +                "properties": {
      +                  "unit": {
      +                    "enum": [
      +                      "w",
      +                      "cd",
      +                      "d",
      +                      "h",
      +                      "m"
      +                    ],
      +                    "type": "string"
      +                  },
      +                  "value": {
      +                    "exclusiveMinimum": 0,
      +                    "type": "integer"
      +                  }
      +                },
      +                "required": [
      +                  "value",
      +                  "unit"
      +                ],
      +                "title": "EdgeDelay",
      +                "type": "object"
      +              },
      +              {
      +                "type": "null"
      +              }
      +            ],
      +            "default": null
      +          },
      +          "to": {
      +            "type": "string"
      +          }
      +        },
      +        "required": [
      +          "to"
      +        ],
      +        "title": "Advance",
      +        "type": "object"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "channel",
      +      "action",
      +      "then"
      +    ],
      +    "title": "SilentActionNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A step a human performs off-platform that the system surfaces but never enacts (a\ncall, a gift, a recorded note). Entry queues a manual_action_queue row carrying\n`action_description`; the user marking that row done advances the prospect along `then`.\nNo channel and no reply to route; `then` is a single bare advance — completion is\nhuman-paced, so there's no timed hold to anchor on a send's sent_at.",
      +    "properties": {
      +      "action_description": {
      +        "type": "string"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "manual",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "then": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "action_description",
      +      "then"
      +    ],
      +    "title": "ManualActionNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "An automated side-effect the agent performs on entry — update a CRM, post a\nnotification, tag a record — with no outreach send, then advances along `then`. The\nside-effect itself is authored onto the node's on_enter trigger (a `prompt`, or `code` for\na deterministic one), like a send node's template and a terminal node's hook — not a field\nhere. Unlike a manual node it boots an agent run (rather than waiting on a human) and unlike\na terminal hook it advances; structurally it is a decision with one implicit 'always' arm —\nthe run does the side-effect, then moves each prospect onto `then`. `then` is a single bare\nadvance: a side-effect completes on the run, not a send's sent_at, so there's no timed hold.",
      +    "properties": {
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "automation",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "then": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "then"
      +    ],
      +    "title": "AutomationNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A natural-language judgment fork. `arms` are the judged routes — each a `case`\ncondition and a target (the default authored as an explicit arm too, e.g. case='else').",
      +    "properties": {
      +      "arms": {
      +        "items": {
      +          "additionalProperties": false,
      +          "description": "One judged decision arm: `case` is the natural-language condition (stripped,\nnon-empty — the decision judge needs something to evaluate), `to` its target node.",
      +          "properties": {
      +            "case": {
      +              "minLength": 1,
      +              "type": "string"
      +            },
      +            "to": {
      +              "type": "string"
      +            }
      +          },
      +          "required": [
      +            "case",
      +            "to"
      +          ],
      +          "title": "Arm",
      +          "type": "object"
      +        },
      +        "minItems": 1,
      +        "type": "array"
      +      },
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "decision",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      },
      +      "rule": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind",
      +      "rule",
      +      "arms"
      +    ],
      +    "title": "DecisionNode",
      +    "type": "object"
      +  },
      +  {
      +    "additionalProperties": false,
      +    "description": "A resting end-state with no further outreach — structurally a sink (no out-edge\nslots). Its human meaning lives in `label`. A terminal can still carry an on_enter hook —\na notify / tag / webhook side-effect on entry, no send and no movement — but that is just\nan on_enter Trigger added to the node with add_trigger, not a field here.",
      +    "properties": {
      +      "id": {
      +        "type": "string"
      +      },
      +      "kind": {
      +        "const": "terminal",
      +        "type": "string"
      +      },
      +      "label": {
      +        "type": "string"
      +      }
      +    },
      +    "required": [
      +      "id",
      +      "label",
      +      "kind"
      +    ],
      +    "title": "TerminalNode",
      +    "type": "object"
      +  }
      +]
  9. First observed

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Goes well beyond the annotations: it discloses that prospects keep their cursor, nodes keep their on_enter enactment, optional slots are dropped rather than preserved, message_templates is the exception, and edge repoints are whole-flow re-validated so dangling refs/orphans/cycles are rejected rather than saved. Annotations only declare non-read-only/non-destructive; this adds the real behavioral contract.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Front-loaded with a one-line summary, then usage, then constraints, then a returns note. Dense but each paragraph earns its place; the closing restatement of a forked condition across rule/arm.case/label/on_enter prompt is genuinely useful coordination guidance. Slightly long, hence not a 5.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a mutation tool with a large nested schema, the description covers scope, prerequisites, persistence/re-validation behavior, the associated prompt edit via update_trigger, and even the return shape in the returns section. An agent has everything needed to call it correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 100%, so the baseline is 3, but the description meaningfully reinforces the full-new-definition contract ('Pass the node's full new definition'), the id-selects-the-node rule, and the kind-must-match constraint even though the schema also states these. It ties the fields to semantics (which fields to change for which purpose) rather than just repeating the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States a specific verb (edit) and resource (one existing node of a campaign's flow), and immediately scopes it against the alternative: 'Edit one existing node ... leaving the rest of the DAG untouched.' An agent can distinguish this from define_sequence and update_trigger without opening any schema.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives an explicit selection rule ('Use this — not `define_sequence` — whenever the set of nodes isn't changing'), enumerates concrete use cases, and states the exclusions (adding/removing nodes, changing kind, editing the start node) with the tool to use instead. Nothing is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Try in Browser

Glama MCP Gateway

Add one secure layer between your agents and this server.

Resources