Skip to content

[FEATURE]: SDK invocation accounting boundary for background-shell continuations #59953

Open

Description

@AjayThorve

Feature hasn't been suggested before.

Describe the enhancement you want to request

Provide a supported SDK boundary for attributing and finalizing model usage for one caller invocation when native background shells can later resume the same session.

We integrate OpenCode into NVIDIA NeMo Fabric and need to distinguish an idle root execution from an invocation whose attributable model work is complete. Native background execution is intentional; this request concerns lifecycle and attribution support, not incorrect token values.

Version and evidence

Verified with @opencode/sdk / @opencode/core 2.0.23, Bun 1.4.2. This report does not assert that later releases have the same limitation.

In the v2.0.23 shell completion path, a completed background job posts a synthetic input into its session. That notification can wake another model turn after an embedding caller has received its initial result. The public session.interrupt({ sessionID, resume: false }) stops current execution; we could not establish that it fences future notifications or wakeups. A session ID alone does not tell a warm embedding which invocation owns those later requests.

Reproduction and current downstream handling

A controlled real-SDK regression uses a local mock OpenAI-compatible streaming endpoint with known usage:

  1. Return a title response and an initial primary response containing a native shell tool call.
  2. Compare { command: "printf done", background: false } with { command: "sleep 1", background: true }.
  3. Return the final primary response; each of the three model responses reports input 10/output 2.
  4. Observe input 30/output 6 in both cases. Fabric can certify the foreground result, but leaves complete counters unknown for the background case and records scope_reasons: ["background_shell"].

The regression stops the embedded SDK after the result; it does not claim to measure a subsequent post-result model request. The future-wakeup concern follows the native completion path above. An end-to-end Harbor task also reaches this background-shell guard while completing successfully. NeMo-Fabric#525 retains partial observations without presenting them as complete totals.

Requested contract

A supported capability that lets an embedding establish an unambiguous invocation boundary, either by fencing/deferring future model continuations without killing background services, or by attributing continuations to a stable invocation identity and exposing when its usage is final. Please advise if an existing supported API already provides this contract.

The boundary should handle a shell exiting after the initial root result, completion racing with interruption, and a subsequent invocation on the same warm runtime. An authoritative idle check or canceling one already-queued inbox entry alone does not cover a future completion notification.

Waiting indefinitely or terminating a task's long-running server is not a usable workaround. Until a boundary is established, unknown complete counters are the truthful downstream behavior.

Related: #56115 requests plugin inbox cancellation/busy queries; #36348 covers shell restart semantics. This request is specifically about a live embedding's invocation ownership and completion.

Activity

  1. opencodejr commented on Oct 28, 2026

    @opencodejr

    Thanks for the detailed write-up and the regression test.

    To restate the request: you'd like a supported SDK boundary for one caller invocation. That would mean either a way to hold off model turns that background-shell completions would later trigger in the same session (without stopping the background processes), or a way to tag those later turns with a stable invocation ID and get a signal when that invocation's usage is final.

    As far as I can tell, no existing API does this. On the current dev branch, a finished background shell still posts a synthetic notification into its session (notifyWhenDone in packages/core/src/tool/plugin/shell.ts). session.interrupt only stops work that is running at that moment and does nothing when the session is idle, so it doesn't block notifications that arrive later. A plugin can't fill this gap either, because the plugin host doesn't expose inbox control (see #56115), and even with it a plugin can't stop or tag a notification that hasn't been created yet. So this would need to be added to core and the SDK.

    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