Skip to content

feat: AbortSignal support across session and core (closes #60)#61

Merged
uchouT merged 3 commits intomainfrom
feat/abort-signal
Apr 28, 2026
Merged

feat: AbortSignal support across session and core (closes #60)#61
uchouT merged 3 commits intomainfrom
feat/abort-signal

Conversation

@uchouT
Copy link
Copy Markdown
Collaborator

@uchouT uchouT commented Apr 28, 2026

Summary

Threads an optional AbortSignal from host through Session.send/stream and TurnRunner all the way down to the LLM call, with checkpoints at every round boundary in the tool loop. Closes #60.

  • session (0.6.0 → 0.7.0): LLMCompleteOptions.signal, SessionSendOptions for Session and MainSession. Built-in OpenAI / Anthropic adapters forward to SDK request options. Aborted calls reject with DOMException('aborted', 'AbortError') and write no L3 (both user and assistant records dropped — atomic with non-stream behavior).
  • core (0.7.2 → 0.8.0): TurnRunnerOptions.signal, TurnRunnerSession.send/stream + EngineRuntimeSession.send/stream accept { signal? }, TurnRunnerToolExecutor.executeTool gains options arg, ToolExecutionContext.signal for tools to opt-in. run / finishFromStreamResult call throwIfAborted() between rounds, before each tool call, and after each tool result is collected (suppresses phantom onToolResult). runStream short-circuits on already-aborted signal and re-throws AbortError from the iterator. SessionCompatible and adaptSessionToEngineRuntime forward through. Engine / Orchestrator / Agent passthrough preserves signal via existing TurnRunnerOptions spread.

All changes are additive — existing callers unaffected, no breaking changes.

Open-question decisions

  • L3 persistence on stream abort: drop entirely (atomic with non-stream send, no Message metadata bloat, host already received chunks via the iterator).
  • onToolResult ordering on abort: suppress callback if signal aborts after tool result is collected — host bookkeeping stays consistent.
  • Tool that ignores ctx.signal: runner waits for the in-flight tool to return, then throws on the next round boundary. Documented at the tool runtime contract.

Test plan

  • @stello-ai/session — 6 new tests in abort.test.ts (send abort + reject, no L3; pre-aborted signal short-circuits; signal forwarded to adapter; stream mid-flight abort; pre-aborted stream rejects; signal forwarded to adapter.stream). Plus 1 added assertion in openai-compatible.test.ts for SDK signal forwarding. 134 passed.
  • @stello-ai/core — 5 new tests in turn-runner-abort.test.ts (round-boundary abort, pre-aborted, signal threading; runStream mid-stream abort, pre-aborted runStream). 2 new tests in session-runtime.test.ts (signal forwarding through adapter for both send and stream). 1 new e2e test in stello-agent.test.ts (agent.stream({ signal }) aborts iterator + result). Existing assertions updated for new call signatures. 282 passed.
  • Full workspace pnpm -r test: 431 passed across session/core/devtools.
  • Full workspace pnpm -r build: green.

Release

Stello uses manual git-tag releases (no Changesets / Version PR flow). Maintainer publishes manually after merge.

uchouT added 3 commits April 28, 2026 21:15
…send/stream

Threads an optional AbortSignal from Session.send / Session.stream (and
MainSession variants) through to LLMAdapter.complete / LLMAdapter.stream.
Built-in OpenAI and Anthropic adapters forward the signal to their SDK
request options. Aborting cancels the in-flight LLM call with a standard
DOMException('aborted', 'AbortError') and skips L3 persistence entirely
(both user and assistant records are dropped — atomic with non-stream
behavior, no partial messages polluting context).

Bumps @stello-ai/session 0.6.0 → 0.7.0 (additive; existing callers
unaffected). Closes part A of #60.
… runtime

Adds signal threading across the entire turn lifecycle:
- TurnRunnerOptions.signal — tool loop checks throwIfAborted() at every
  round boundary, before each tool call, and after each tool result is
  collected (suppresses phantom onToolResult after cancel).
- TurnRunnerSession.send/stream and EngineRuntimeSession.send/stream
  accept { signal? }; runStream short-circuits when the signal is already
  aborted, otherwise forwards to session.stream and re-throws AbortError
  from the iterator instead of silently closing.
- TurnRunnerToolExecutor.executeTool gains an options object carrying the
  signal; ToolExecutionContext.signal lets individual tools opt-in to
  honoring cancellation for long-running work (tools that ignore it still
  work — the runner aborts at the next round boundary).
- SessionCompatible and adaptSessionToEngineRuntime forward the signal to
  the underlying @stello-ai/session contract.
- Engine.turn / Engine.stream / Orchestrator / StelloAgent passthrough
  preserves signal via TurnRunnerOptions spread; no field stripping.

Bumps @stello-ai/core 0.7.2 → 0.8.0 (additive). Closes part B of #60.
@uchouT uchouT merged commit 256bf15 into main Apr 28, 2026
2 checks passed
@uchouT uchouT deleted the feat/abort-signal branch April 28, 2026 19:10
uchouT added a commit that referenced this pull request Apr 28, 2026
Add CHANGELOG entries for AbortSignal support (#60, #61) in both
packages, and the streaming Anthropic adapter tool_use id/name fix
in @stello-ai/session.
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.

feat: AbortSignal support across LLMAdapter / Session / TurnRunner

1 participant