Skip to content

0.2.0: shutdown(), maxTokens and status on HTTP errors - #5

Merged
gabemule merged 2 commits into
mainfrom
feat/shutdown-max-tokens
Oct 20, 2026
Merged

gabemule merged 2 commits into
mainfrom
feat/shutdown-max-tokens

Conversation

@gabemule

Copy link
Copy Markdown
Contributor

Why

A host that exited left opencode serve running and holding its port, and the package offered no way to stop it. Children are spawned detached, so Ctrl-C never reaches them. max_tokens could not be set per call, and an error chunk carried its HTTP status only inside the message text.

What

  • shutdown({ graceMs = 3000 }):
    • stops every process the package started, which covers the CLIs of in-flight calls and the shared opencode serve;
    • sends SIGTERM to each process group, then SIGKILL to each of them after the grace;
    • shares one in-flight promise and never rejects;
    • installs no signal handler of its own; a host that traps SIGINT calls it.
  • StreamOptions.maxTokens, also on complete():
    • sent by anthropic and openai-compatible when it is a positive safe integer;
    • anthropic keeps its 32000 default, and openai-compatible still sends nothing unless asked;
    • ignored by the two CLI adapters.
  • LLMStreamChunk.status: the provider's HTTP status on an error chunk from a non-ok response. The error text is unchanged.
  • Fixed:
    • An opencode call in flight when its server died by a signal waited out the 30 s boot timeout. It now fails at once.
    • A claude-code failure caused by a signal now names the signal.

Additive only: no existing consumer changes behaviour. The version is 0.2.0 because the release adds public API.

Verified locally

  • Node 24.14.1: 166/166. Node 22.22.2: 166/166. Bun 1.3.14: 156 pass, 0 fail.
  • typecheck and examples typecheck clean; verify:pack ok (26 files).
  • New tests cover TERM-ignoring children and grandchildren, SIGKILL escalation, concurrent and repeated calls, failed spawns, invalid graceMs, maxTokens validation and status. Each one was checked to fail against the code it guards.
  • No process was left running after 44 suite runs, including under parallel and CPU-loaded runs.

Known limits

  • Windows: the process-tree kill falls back to the direct child.
  • A process spawned while a shutdown is already running is not covered by that call.
  • A group whose leader had already exited cannot be reached.
  • kill -9 of the host still leaves the server running.

…tatus

A host that exited left `opencode serve` running and holding its port, and
nothing in the package could stop it: children are spawned detached, so a
terminal's Ctrl-C never reaches them, and the shared server is module-private.
Every spawn now goes through one registry, and `shutdown({ graceMs })` sends
SIGTERM to every group the package still owns, SIGKILL to each of them after
the grace, and resolves once they are gone. It shares one in-flight promise,
never rejects, and the package still installs no signal handler: a host that
traps SIGINT calls it. An opencode call in flight when its server dies by a
signal now fails at once instead of waiting out the 30 s boot timeout, and a
claude-code failure caused by a signal names the signal.

`StreamOptions.maxTokens` (also on complete()) is sent by the two HTTP
adapters when it is a positive safe integer; anthropic keeps its 32000
default and openai-compatible still sends nothing unless asked. Error chunks
from a non-ok HTTP response carry the provider's `status`.
@gabemule
gabemule merged commit f56dd72 into main Oct 20, 2026
3 checks passed
@gabemule
gabemule deleted the feat/shutdown-max-tokens branch October 20, 2026 02:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant