Skip to content

Add AbortSignal support to TypeScript SDK#6378

Merged
pakrym-oai merged 8 commits intoopenai:mainfrom
danfhernandez:thread-abort-signal
Nov 13, 2025
Merged

Add AbortSignal support to TypeScript SDK#6378
pakrym-oai merged 8 commits intoopenai:mainfrom
danfhernandez:thread-abort-signal

Conversation

@danfhernandez
Copy link
Copy Markdown
Contributor

Summary

Adds AbortSignal support to the TypeScript SDK for canceling thread execution using AbortController.

Changes

  • Add signal?: AbortSignal property to TurnOptions type
  • Pass signal through Thread class methods to exec layer
  • Add signal parameter to CodexExecArgs
  • Leverage Node.js native spawn() signal support for automatic cancellation
  • Add comprehensive test coverage (6 tests covering all abort scenarios)

Implementation

The implementation uses Node.js's built-in AbortSignal support in spawn() (available since Node v15, SDK requires >=18), which automatically handles:

  • Checking if already aborted before starting
  • Killing the child process when abort is triggered
  • Emitting appropriate error events
  • All cleanup operations

This is a one-line change to the core implementation (signal: args.signal passed to spawn), making it simple, reliable, and maintainable.

Usage Example

import { Codex } from '@openai/codex-sdk';

const codex = new Codex({ apiKey: 'your-api-key' });
const thread = codex.startThread();

// Create AbortController
const controller = new AbortController();

// Run with abort signal
const resultPromise = thread.run("Your prompt here", {
  signal: controller.signal
});

// Cancel anytime
controller.abort('User requested cancellation');

Testing

All tests pass (23 total across SDK):

  • ✅ Aborts when signal is already aborted (both run and runStreamed)
  • ✅ Aborts during execution/iteration
  • ✅ Completes normally when not aborted
  • ✅ Backward compatible (signal is optional)

Tests verified to fail correctly when signal support is removed (no false positives).

danfhernandez and others added 3 commits November 7, 2025 17:34
This change allows users to cancel thread execution using an AbortController.

Changes:
- Add signal property to TurnOptions type
- Pass signal through Thread methods to exec layer
- Add signal parameter to CodexExecArgs
- Leverage Node.js native spawn() signal support for clean cancellation
- Add comprehensive test coverage for abort scenarios

The implementation uses Node.js's built-in AbortSignal support in spawn(),
which automatically handles process termination and cleanup when aborted.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Adds a test that creates an infinite-streaming server to verify the abort
signal actually kills a running process, not just catches pre-aborted signals.

The test:
- Creates a server that streams events indefinitely (every 100ms)
- Starts a thread operation
- Aborts after 200ms
- Verifies completion in <500ms (proves process was killed)
- Without signal support, test times out after 10s

This ensures AbortSignal actually interrupts long-running operations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Improves the long-running abort test to use an infinite tool call loop
instead of simple progress events. This better simulates a real scenario
where the agent continuously executes tools and is more rigorous because
it tests killing during active event processing, not just I/O wait.

The test now:
- Streams tool call events (in_progress -> completed) every 100ms
- Simulates an agent stuck in an execution loop
- Verifies abort kills the process during active tool execution
- Hangs without signal support (verified)
- Completes in ~203ms with signal support

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@etraut-openai
Copy link
Copy Markdown
Collaborator

@danfhernandez, I noticed that you opened this PR in draft mode. When it's ready for review, please move it out of draft mode. Looks like there's a lint (formatting) error detected that needs to be addressed.

@etraut-openai etraut-openai added the needs-response Additional information is requested label Nov 13, 2025
- Update eslint config to allow underscore-prefixed unused vars
- Fix abort.test.ts to use void pattern for consumed events
@danfhernandez danfhernandez marked this pull request as ready for review November 13, 2025 00:40
@etraut-openai etraut-openai removed the needs-response Additional information is requested label Nov 13, 2025
Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread sdk/typescript/tests/abort.test.ts Outdated
danfhernandez and others added 2 commits November 12, 2025 19:50
The tests were passing async functions to expect().rejects instead of
invoking them and passing the resulting promises. This caused Jest to
fail immediately with "Received value must be a promise" without
running the test bodies.

Fixed by immediately invoking the async functions: (async () => {...})()

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@pakrym-oai
Copy link
Copy Markdown
Collaborator

@codex review this

Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines 101 to 105
res.statusCode = status;
res.setHeader("content-type", "text/event-stream");

const responseBody = responseBodies[Math.min(responseIndex, responseBodies.length - 1)]!;
responseIndex += 1;
const responseBody = responseBodies.next().value;
for (const event of responseBody.events) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Handle exhaustion of custom response generators

The new ResponsesProxyOptions allows callers to supply a Generator<SseResponseBody>, but startResponsesTestProxy now blindly does const responseBody = responseBodies.next().value; and immediately dereferences responseBody.events. If a finite generator is provided (which the type signature allows), once it finishes .next() returns { done: true, value: undefined } and the server crashes with TypeError: Cannot read properties of undefined (reading 'events'), yielding a 500 response instead of a deterministic error. Arrays converted via createGenerator already throw a clear “not enough responses provided” error, so generators should get the same treatment by checking the .done flag and throwing an explicit error before touching events.

Useful? React with 👍 / 👎.

@pakrym-oai pakrym-oai merged commit 439bc5d into openai:main Nov 13, 2025
25 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Nov 13, 2025
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants