Skip to main content
Glama

Define Sequence

define_sequence

Author (or replace) the campaign's flow sequence — a DAG of outreach nodes. Each node carries its own out-edges as typed fields, so the grammar is the schema: a connection request has accepted / already_connected / timeout slots, a send has replied / follow_up, a silent action, a manual step and a gift step each have then, a decision has arms, a terminal has none. Derive the DAG from the agent's goal + triggers: one node per step, each natural-language fork a decision, and terminal only for resting end-states (auto-skip, human handoff, done). A fork is any rule about who gets a step or which steps they get — a filter (keep only people in the right location, role or seniority, company or industry, skipping the rest) or a route (a different path of steps by role, school or sector) — and each is its own decision node rather than a condition inside a send step's on_enter code or prompt. A wording difference within one step is a {slot} in its message_templates, not a fork. The flow must be acyclic, every node reachable from start, and every path capped by a terminal (only a terminal may dead-end) — all enforced.

The shape:

  • start (exactly one, the DAG head): {id, label, entry}. Holds prospects no send has reached (label it "Not started"). entry is the single first touch — {to} for an immediate open, or {to, after: {value, unit}} (units w/d/h/m) to hold that long from when each prospect is tracked. The flow fires it on its own: do NOT queue the first sends yourself on a sequenced campaign — track prospects and let the flow open.

  • connection_request node (LinkedIn): {id, label, kind, accepted, already_connected, timeout?, message_templates?}. message_templates holds its note (see below); without one the step's on_enter code or instructions decide (its minted default code sends no note). Routes BOTH send outcomes; both required. accepted (invite taken) is an advance like a silent action's then — {to} to move on once the accept is detected, or {to, after: {value, unit}} to hold the prospect on the request that long after the accept is detected (detection lags the real accept by up to ~8 hours, so the wait counts from detection). already_connected (a CR onto an existing 1st-degree connection, a no-op) is a target node id. Route already_connected to a terminal by default (surface the existing relationship for opt-in rather than cold-messaging it); point it at the message step only when the user says to reconnect with existing connections. timeout is an optional held advance {delay: {value, unit}, to} (e.g. withdraw after no accept).

  • send node (a repliable send — LinkedIn message / inmail, or an email): {id, label, kind, channel, action, replied, follow_up?, message_templates?}. channel is linkedin or email (so one sequence spans both); action is a LinkedIn verb for channel linkedin or email for channel email. Routes replied (required) so a reply advances the prospect off the node; follow_up is an optional held no-reply follow-on {delay, to}. message_templates holds the step's message (see below).

  • action node (a bodyless LinkedIn action — follow / withdraw / resolve / comment / reaction): {id, label, kind: "action", channel: "linkedin", action, then}. Nothing to reply to; then is its single advance — {to} fires the instant the action's send lands (a bodyless step flows straight into the next node with no hold), or {to, after} to hold first. A resolve action may add notify: true — the visible "View Profile" visit (LinkedIn notifies the person you viewed them), a warm-up touch typically placed later; the default silent resolve just reads profile data to branch on.

  • manual node (a step a HUMAN performs off-platform that the system can't enact — a call, a handwritten card, a recorded note): {id, label, kind: "manual", action_description, then}. No channel. action_description is the fully-authored ask (no send is composed, no agent run boots — the step is purely a note for the user); entry queues it onto the user's home approval list. then is the next node's id (a bare string, not a {to} object — unlike a silent action's or a gift's then): the prospect advances onto it when the user marks the queued step done, with no timed hold (completion is human-paced). Use manual ONLY when a human must act — never for a step the agent can do itself (that step never happens, since a manual node boots no run): an automatable side-effect (update a CRM, create a task in a connected tool, post a notification) is an automation node, and an automatable outreach touch is an action/send node.

  • gift node (a physical gift Sliq orders online and ships to the prospect's US address — use it whenever the user wants a gift sent as a step): {id, label, kind: "gift", product_url?, max_price_usd?, gift_note?, then}. No channel. Entry queues one gift card per prospect onto the user's approval list, with a short note the enactment run writes for each prospect; nothing is ordered until the user approves the card, where they complete or change the recipient, shipping address, product, price ceiling and note. product_url (the product page) and max_price_usd (the most the item may cost including tax and shipping, in whole US dollars) are optional defaults pre-filled on every card, and gift_note (at most 240 characters) is the optional step note each prospect's own note is written from; set the ones the user named and leave the rest out. Each approved gift charges the owner's card for gifts the order's actual total, at most max_price_usd, plus about $3.30 + 2.9% in fees on top. Set a budget the user names for each gift as max_price_usd as is, even one they call all-in, and tell them the fees come on top of it: a lower limit can make the order fail for the item they picked, so lowering it is their call. then is the gift's single advance once it is delivered — {to} to move on at delivery, or {to, after: {value, unit}} to hold that long after it.

  • automation node (an automated non-send side-effect the AGENT performs — update a CRM record, create a HubSpot task, post a Slack/webhook notification, tag a record): {id, label, kind: "automation", then}. No channel. then is the next node's id (a bare string, like a manual node's). Author the side-effect itself onto this node's on_enter trigger with update_trigger (a prompt, or code for a deterministic one) — the same way a terminal node's hook is authored, not a field here; the enactment run carries it out for each prospect then advances. Unlike manual it boots an agent run and needs no human; unlike a terminal hook it advances. Reach for this when the user wants a step that "does something" automatically mid-sequence and then continues.

  • decision node: {id, label, kind, rule, arms}. rule is the NL fork judgment; arms is a list of {case, to} — each case a non-blank condition, the default authored as one too (e.g. case: "else"). A decision has no timed edge and no bare advance — only arms. A case arm may target any enactable node — a send / action / automation step, another decision, or a manual or gift step — or a terminal: the decision run moves each prospect onto its arm, and entering it fires that node's own on_enter enactment on its next run (a send/action composes its send, a nested decision judges its fork, an automation runs its side-effect, a manual step queues the human's task, a gift step queues its gift card), never an inline send. Chain a downstream decision when several arms share one judgment (DAG reuse); use compound arms on one rule ("A and B" / "A and not B" / "else") when one prospect needs several independent conditions judged together; target a manual step to hand a judged arm off to a human.

  • terminal node: {id, label, kind}. A resting end-state, no out-edges.

Every timed hold — a start after, a connection_request's held accepted or timeout, a send follow_up, a silent action's or gift step's held then — is {value, unit}. A d (day) unit counts BUSINESS days — Mon–Fri, skipping Saturday/Sunday — and is the DEFAULT for a day-count delay: a plain "follow up in 3 days" (like "3 business days") is {value: 3, unit: "d"}. Reach for cd (calendar day) ONLY when the user explicitly wants calendar days including weekends. w/h/m (week/hour/minute) are always calendar.

Entering a node fires its enactment: a connection_request / send / action node composes and enqueues its step, a manual node queues its step onto the user's approval list, a gift node queues a gift card for each prospect onto that list, an automation node runs its authored side-effect and advances, a decision judges its fork and routes each prospect. Run a second channel alongside the first by chaining it off a step's held advance (a CR timeout, a send follow_up, or a silent action's held then) — not a second start entry: a prospect holds one flow position, so start takes exactly one entry.

A send node's message is its message_templates: a one-entry list holding the message itself, with each personalized part a {slot} — a named field ({first_name}, {company}) or a described slot ({a line about their recent post}); the enactment run fills every {slot} per prospect and, unless the step's prompt lets it rephrase, keeps the rest of the wording. When the user wants to write part of each message themselves, put that part in double square brackets, like [[where you met]]: the run leaves it as written, and each message waits in their approval queue, even on a step that would otherwise send on its own; approving it is refused until they fill it in there. Every other part of the queued message is complete as it stands, one the user could approve without changing a word (the queue rejects a leftover {slot}). The templates are saved with the step and come back on get_campaign_flow; omit message_templates to leave a step's templates as they are, or pass [] to remove them. To A/B test a step's message, add a second version to the list: prospects reaching the step alternate between the two, and get_message_experiments reports which gets more replies. At current volumes a test takes weeks and only separates meaningfully different messages (a different opener or ask, not a reworded phrase), so propose versions that differ that much. Changing either version's text restarts the test. To end a test, pass just the version you keep. A connection_request node's message_templates holds its note the same way (at most 300 characters, filled per prospect), where "" is a version sent with no note: ["", note] A/B tests a note against none, and get_message_experiments reports which gets more requests accepted. Anything else the run should know for that step (tone, who to mention, what to skip, whether it may rephrase the template, what to write when a slot can't be filled) goes in the step's on_enter trigger prompt via update_trigger — keep the message itself in message_templates, not in that prompt or in workspace.inputs, so the user sees and edits it on the step. When you first set a template on a step whose prompt already carries its message, rewrite that prompt down to the extra instructions. (workspace.inputs stays the home for a flowless campaign's templates and for per-agent data no single step owns — criteria, audience, exclude lists.)

Authoring (or replacing) a sequence on an agent that ALREADY holds untouched prospects enrolls that whole backlog into it — so before defining a sequence on an agent with an existing roster (a monitoring or engager-capture agent especially), confirm with the user that the accumulated prospects should all enter the campaign.

Re-authoring replaces the prior sequence (allowed on a live campaign); a prospect still resting on a dropped node returns under off_sequence_nodes (with a note on re-homing) rather than being lost. The node-owned on_enter triggers reconcile in the same call: every node kept in the new DAG (same id, any kind) keeps its on_enter row and the prompt/code on it, a new enactable node gets a fresh row auto-minted, and only a node dropped from the DAG entirely has its on_enter row deleted. A new resolve / connection_request / follow node is minted with a default deterministic enactment code (it enacts clean with no agent run — edit it for a non-standard step; a connection request's note goes in its message_templates, not the code); every other kind mints code-less. A reused id keeps its existing row — so after changing what that id's node does, review its on_enter content; an unmodified minted default is regenerated for the new kind automatically, but an edited one is left as you wrote it.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
agent_idYesID of the agent to attach the sequence to.
sequenceYesThe flow DAG (typed per-kind nodes). Validated for structural integrity, reachability, and acyclicity.
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
    • 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."
      +}
    • changedInput schema / properties / sequence / properties / nodes / items / 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"
      +  }
      +]
  2. Changed2 schema fields changed
    • changedInput schema / properties / sequence / properties / nodes / items / 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"
      +  }
      +]
    • changedInput schema / properties / sequence / properties / start / properties / entry / description
      Previous value: -"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`."New value: +"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."
  3. Changed2 schema fields changed
    • changedInput schema / properties / sequence / properties / nodes / items / 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"
      +  }
      +]
    • changedInput schema / properties / sequence / properties / start / properties / entry / description
      Previous value: -"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`."New value: +"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`."
  4. Changed2 schema fields changed
    • changedInput schema / properties / sequence / properties / nodes / items / 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"
      +  }
      +]
    • changedInput schema / properties / sequence / properties / start / properties / entry / description
      Previous value: -"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)."New value: +"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`."
  5. Changed1 schema field changed
    • changedInput schema / properties / sequence / properties / nodes / items / 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. Changed1 schema field changed
    • changedInput schema / properties / sequence / properties / nodes / items / 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. Changed4 schema fields changed
    • addedInput schema / properties / agent_id
      Added value: +{
      +  "description": "ID of the agent to attach the sequence to.",
      +  "type": "integer"
      +}
    • changedInput schema / properties / sequence / properties / nodes / items / 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 to attach the sequence to.",
      -  "type": "integer"
      -}
    • changedInput schema / required
      Previous value: -[
      -  "task_id",
      -  "sequence"
      -]New value: +[
      +  "agent_id",
      +  "sequence"
      +]
  8. Changed2 schema fields changed
    • changedInput schema / properties / sequence / properties / nodes / items / 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"
      +  }
      +]
    • changedInput schema / properties / sequence / properties / start / properties / entry / properties / after / anyOf
      Previous value: -[
      -  {
      -    "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"
      -  }
      -]New value: +[
      +  {
      +    "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"
      +  }
      +]
  9. First observed

TDQS

A4.6/5.0
Behavior5/5

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

Annotations only give readOnlyHint=false and destructiveHint=false. The description goes far beyond: re-authoring replaces the prior sequence on a live campaign, displaced prospects surface under `off_sequence_nodes`, node on_enter rows reconcile by id (kept/added/deleted), a minted default code is regenerated for the new kind but an edited one is preserved, gift approvals and fee mechanics, and backlog enrollment. This is exactly the disclosure the annotations cannot provide.

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?

Purpose is front-loaded in the first sentence and the rest is organized by node kind, then by timing/template behavior and re-authoring rules. It is very long, but for a nested discriminated-union DAG grammar most of it earns its place; some overlap with the schema's own node descriptions keeps it out of the top tier.

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?

A highly complex tool with no output schema and thin annotations, yet the description covers DAG construction rules, node semantics, timing units, template/A-B behavior, and the reconciliation side-effects of re-authoring. An agent has what it needs to construct a valid sequence and to predict the call's consequences.

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 coverage is 100%, so the baseline is 3, but the description adds real meaning beyond it: the per-kind grammar and why each edge exists, the `d`-is-business-days/`cd`-is-calendar distinction, the bare-string vs `{to}` `then` divergence between manual/automation and other nodes, and the `[]`/omit semantics for `message_templates`. That is substantive semantic enrichment over 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?

Opens with a specific verb+resource: "Author (or replace) the campaign's flow sequence — a DAG of outreach nodes." It immediately distinguishes itself from siblings like update_node, setup_linkedin_sequence, and add_trigger by establishing that this is the full-DAG authoring call, and it names its own read-back counterpart (get_campaign_flow) later.

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

Usage Guidelines4/5

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

Extensive when-to-use guidance for node-kind selection (manual ONLY when a human must act; a side-effect is automation not manual; a wording difference is a `{slot}` not a fork; route `already_connected` to terminal by default unless the user says reconnect). It also carries a critical pre-condition (confirm before enrolling an existing backlog). It stops short of an explicit "use this instead of setup_linkedin_sequence" statement, so it is clear context rather than full when/when-not/alternatives.

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