Add AbortSignal support to TypeScript SDK (#6378)

## 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
```typescript
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).

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: pakrym-oai <pakrym@openai.com>
This commit is contained in:
Dan Hernandez
2025-11-13 13:35:42 -08:00
committed by GitHub
co-authored by Claude pakrym-oai
parent c95bd345ea
commit 439bc5dbbe
7 changed files with 218 additions and 15 deletions
+7 -1
View File
@@ -24,6 +24,8 @@ export type CodexExecArgs = {
outputSchemaFile?: string;
// --config model_reasoning_effort
modelReasoningEffort?: ModelReasoningEffort;
// AbortSignal to cancel the execution
signal?: AbortSignal;
// --config sandbox_workspace_write.network_access
networkAccessEnabled?: boolean;
// --config features.web_search_request
@@ -69,7 +71,10 @@ export class CodexExec {
}
if (args.networkAccessEnabled !== undefined) {
commandArgs.push("--config", `sandbox_workspace_write.network_access=${args.networkAccessEnabled}`);
commandArgs.push(
"--config",
`sandbox_workspace_write.network_access=${args.networkAccessEnabled}`,
);
}
if (args.webSearchEnabled !== undefined) {
@@ -105,6 +110,7 @@ export class CodexExec {
const child = spawn(this.executablePath, commandArgs, {
env,
signal: args.signal,
});
let spawnError: unknown | null = null;
+1
View File
@@ -86,6 +86,7 @@ export class Thread {
skipGitRepoCheck: options?.skipGitRepoCheck,
outputSchemaFile: schemaPath,
modelReasoningEffort: options?.modelReasoningEffort,
signal: turnOptions.signal,
networkAccessEnabled: options?.networkAccessEnabled,
webSearchEnabled: options?.webSearchEnabled,
approvalPolicy: options?.approvalPolicy,
+2
View File
@@ -1,4 +1,6 @@
export type TurnOptions = {
/** JSON schema describing the expected agent output. */
outputSchema?: unknown;
/** AbortSignal to cancel the turn. */
signal?: AbortSignal;
};