Skip to main content
A reply to a message that came in through a channel goes back the same way. noteRun finds the via that receiveVia stored under the message’s key before the run was admitted, records the reply in pendingDeliveries under the run id, and hands it to sendPending, the first of its tries:
state is what getState() returned at the start of noteRun, and the writes go through setState().
  • replyTo holds the via that receiveVia stored, under the message’s idempotency key before the run was queued, so a run that completes at once still finds it.
  • pendingDeliveries[run.id] is written with the outcome before deliver runs, so a redelivered task still finds it; one entry per run, so a second run’s reply never displaces a first run’s that is still waiting on a retry.
  • runStarts[run.id] is the message-log position written before the run’s first dispatch; a redelivered task reads the same boundary, so its own reply is never mistaken for one already there.
  • deliveredRuns lists the runs sent, and a null under pendingDeliveries deletes the entry; sendPending writes both once deliver returns. A redelivered task continues the attempt count instead of starting a second chain.
deliver sends the reply:
this.env.TELNYX is the Telnyx binding of the instance’s env, declared by [telnyx] in telnyx.toml. It holds no API key, and the runtime adds the credential before each request leaves. For a text the reply is sent from the number the text was sent to, back to the sender, on the same messaging profile. For an email the reply goes through the inbox’s reply action on the message that came in, and the platform threads and addresses it. A deliver that throws never fails the run’s task; the reply has a retry window of its own:
  • retryableDelivery is true for no answer, a 429 or a 5xx; any other 4xx is final at once, since the API would refuse it again.
  • Each try is one log line, delivery: "failed" with retryInSeconds or delivery: "gave up", with the channel, the target, the status and the API’s reason.
  • deliveryErrors[run.id] in state says what stands between the person and that reply: status, message, attempts, nextAt while a try is due, final once given up. It is cleared when the reply goes out. (An agent deployed before this shape held one pendingDelivery/deliveryError slot; those are read as one entry and cleared by the next write.)
  • The DELIVERY_TASK fires through schedule() and arrives at onTask, which The Run shows; retryDelivery finds the pendingDelivery for that run and calls sendPending with the next attempt. A reply that went out in the meantime, or a run that already delivered, is not delivered twice.
  • The window spent is final too: after the seventh wait the pending entry is dropped and final is set on its error.
  • A run whose approval expired is reconciled by ready() after recover(): it gets failedRunId and lastError, and its replyTo entry is forgotten, so the state stream shows the outcome.
A reply to invoke or chat is never delivered anywhere, since it had no channel. It is read from the state stream. deliver is overridable, which is how a class changes where replies go.