Skip to content

[FEATURE]: plugin input delivered only at a session's next run start (synthetic without step-counter reset) #62917

Open

Description

@Blind-Striker

Feature hasn't been suggested before.

  • I have verified this feature I'm about to request hasn't been suggested before.

Describe the enhancement you want to request

A way for a server plugin to admit input that is delivered only at the start of a session's next
run: never at a later step of a run in progress, and never as a run's own input.

Why. A plugin that keeps model-visible state (counts, limits, routing state) current has to
deliver it as durable history. A transient message that a context hook inserts on every request
makes each step an edit on transports that keep their own copy of the conversation, such as
claude-agent: forks, then full checkpoint rebuilds. ctx.session.synthetic is the right
durable channel, but neither delivery mode fits:

  • delivery: "steer" is promoted at the next step boundary, and promoting any steer resets the
    run's step counter (packages/core/src/session/runner/llm.ts:202, if (promoted > 0) step = 1;
    the limit is checked at :253). Input admitted during a run therefore restarts the agent's
    steps allowance.
  • delivery: "queue" is promoted as a run's own input, so it starts a model step of its own
    (packages/core/src/session/inbox.ts, promote with the "input" scope).

So a plugin can only admit while the session is idle, judged by Session.Info.time.busy against
time.idle. That read and the admission are not atomic: a run that passes its entry boundary in
between receives the steer at its second step. Plugins cannot cancel an inbox item, so the plugin
has no way to close that window.

Request, any one of:

  1. a delivery mode, or a synthetic option, that is promoted only at an entry boundary, together
    with whatever input starts that run;
  2. or: promoting plugin-admitted synthetic steers leaves the step counter alone;
  3. or: an atomic "admit if idle" for synthetic.

Each of these keeps plugin state current as append-only history, which history-syncing transports
need, and leaves the steps limit intact.

Version: 2.2.5. The cited lines are unchanged on dev.

Activity

  1. opencodejr commented on Nov 9, 2026

    @opencodejr

    Thanks for the detailed write-up, @Blind-Striker!

    To restate the request: plugins need a way to admit durable ctx.session.synthetic input that is delivered only at the start of a session's next run, together with whatever input starts that run. It should never be promoted mid-run (which resets the step counter and so the agent's steps allowance), and it should never start a run of its own.

    I checked this against dev. The behaviour is as you describe: step is reset to 1 whenever anything is promoted at a step boundary (packages/core/src/session/runner/llm.ts), and with the "input" scope SessionInbox.promote treats steer and queue items alike. A plugin can't work around this today. It can't admit only at an entry boundary, there is no atomic "admit if idle", and it can't cancel an inbox item afterwards. So this needs an addition to the plugin API, such as an entry-only delivery mode for synthetic, or not resetting the step counter for plugin-admitted synthetic steers. Maintainers will consider it.

    Related issues:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions