@ottocode/sdk 0.1.282 → 0.1.283

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ottocode/sdk",
3
- "version": "0.1.282",
3
+ "version": "0.1.283",
4
4
  "description": "AI agent SDK for building intelligent assistants - tree-shakable and comprehensive",
5
5
  "author": "nitishxyz",
6
6
  "license": "MIT",
@@ -468,6 +468,108 @@ function trackResponsesStream(
468
468
  });
469
469
  }
470
470
 
471
+ async function waitForCodexStreamStart(
472
+ response: Response,
473
+ args: {
474
+ sessionId?: string;
475
+ model?: string;
476
+ parentSignal?: AbortSignal | null;
477
+ },
478
+ ): Promise<Response> {
479
+ if (!response.ok || !response.body) {
480
+ return response;
481
+ }
482
+
483
+ const reader = response.body.getReader();
484
+ const idleTimeoutMs = getCodexStreamIdleTimeoutMs();
485
+ let timeout: Timer | undefined;
486
+ let removeAbortListener: (() => void) | undefined;
487
+ const cleanup = () => {
488
+ if (timeout) clearTimeout(timeout);
489
+ timeout = undefined;
490
+ removeAbortListener?.();
491
+ removeAbortListener = undefined;
492
+ };
493
+
494
+ try {
495
+ const first = await Promise.race([
496
+ reader.read(),
497
+ new Promise<never>((_resolve, reject) => {
498
+ timeout = setTimeout(() => {
499
+ reject(
500
+ new Error(
501
+ `OpenAI OAuth Codex stream idle timeout before first chunk after ${idleTimeoutMs}ms`,
502
+ ),
503
+ );
504
+ }, idleTimeoutMs);
505
+
506
+ if (args.parentSignal) {
507
+ const onAbort = () => {
508
+ reject(args.parentSignal?.reason ?? new Error('Request aborted'));
509
+ };
510
+ if (args.parentSignal.aborted) {
511
+ onAbort();
512
+ } else {
513
+ args.parentSignal.addEventListener('abort', onAbort, {
514
+ once: true,
515
+ });
516
+ removeAbortListener = () =>
517
+ args.parentSignal?.removeEventListener('abort', onAbort);
518
+ }
519
+ }
520
+ }),
521
+ ]);
522
+ cleanup();
523
+
524
+ if (first.done) {
525
+ return new Response(null, {
526
+ status: response.status,
527
+ statusText: response.statusText,
528
+ headers: response.headers,
529
+ });
530
+ }
531
+
532
+ const body = new ReadableStream<Uint8Array>({
533
+ start(controller) {
534
+ controller.enqueue(first.value);
535
+ },
536
+ async pull(controller) {
537
+ try {
538
+ const next = await reader.read();
539
+ if (next.done) {
540
+ controller.close();
541
+ return;
542
+ }
543
+ controller.enqueue(next.value);
544
+ } catch (error) {
545
+ controller.error(error);
546
+ }
547
+ },
548
+ async cancel(reason) {
549
+ await reader.cancel(reason);
550
+ },
551
+ });
552
+
553
+ return new Response(body, {
554
+ status: response.status,
555
+ statusText: response.statusText,
556
+ headers: response.headers,
557
+ });
558
+ } catch (error) {
559
+ cleanup();
560
+ loggerWarn('[openai-oauth] response stream did not start before timeout', {
561
+ sessionId: args.sessionId,
562
+ model: args.model,
563
+ timeoutMs: idleTimeoutMs,
564
+ error: summarizeError(error),
565
+ });
566
+ try {
567
+ await reader.cancel(error);
568
+ } catch {}
569
+ throw error;
570
+ }
571
+ }
572
+
471
573
  async function fetchWithCodexRequestTimeout(
472
574
  url: string,
473
575
  init: RequestInit,
@@ -491,10 +593,15 @@ async function fetchWithCodexRequestTimeout(
491
593
  for (let attempt = 0; attempt <= maxRetries; attempt++) {
492
594
  const attemptStartedAt = Date.now();
493
595
  try {
494
- return await fetchCodexRequestAttemptWithTimeout(url, init, {
596
+ const response = await fetchCodexRequestAttemptWithTimeout(url, init, {
495
597
  ...args,
496
598
  requestStartedAt: attemptStartedAt,
497
599
  });
600
+ return await waitForCodexStreamStart(response, {
601
+ sessionId: args.sessionId,
602
+ model: args.model,
603
+ parentSignal: init.signal,
604
+ });
498
605
  } catch (error) {
499
606
  lastError = error;
500
607
  if (init.signal?.aborted || attempt >= maxRetries) {
@@ -502,13 +609,14 @@ async function fetchWithCodexRequestTimeout(
502
609
  }
503
610
 
504
611
  const retryDelayMs = getCodexRequestRetryDelayMs() * (attempt + 1);
505
- loggerWarn('[openai-oauth] request attempt failed before response', {
612
+ loggerWarn('[openai-oauth] request attempt failed before stream start', {
506
613
  sessionId: args.sessionId,
507
614
  model: args.model,
508
615
  attempt: attempt + 1,
509
616
  maxRetries,
510
617
  nextAttempt: attempt + 2,
511
- timeoutMs: getCodexRequestTimeoutMs(),
618
+ requestTimeoutMs: getCodexRequestTimeoutMs(),
619
+ streamIdleTimeoutMs: getCodexStreamIdleTimeoutMs(),
512
620
  durationMs: Date.now() - attemptStartedAt,
513
621
  retryDelayMs,
514
622
  error: summarizeError(error),