@pure01fx/dsh-openai-codex-auth 0.13.1 → 0.13.2

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/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 0.13.2
6
+
7
+ - Stops putting `is_error` on a replayed `function_call_output`. The Responses wire has no such field (Codex reports a failed tool through the output text and keeps its own `success` flag internal), one Lite model answers the unknown item field with a 400 `INVALID_REQUEST`, and because the item is replayed from durable history, a single failed tool call then fails every later request in that session.
8
+ - Logs the provider's own rejection code, message, status and request id when an HTTP/SSE failure cannot be classified into a known code, matching the existing WebSocket diagnostic, so an opaque `INVALID_REQUEST` names its cause in the operator log.
9
+
5
10
  ## 0.13.1
6
11
 
7
12
  - Serves a concurrent or pool-saturated request over HTTP/SSE instead of failing the turn.
@@ -6,7 +6,7 @@ import { nativeCodexAuthorityHash } from './catalog.js';
6
6
  import { nativeCodexEndpoint } from './endpoint.js';
7
7
  import { retryAfterMs, retryError, remainingRetryDelay, sleepRetry, inheritRetryDeadline } from './retry-after.js';
8
8
  import { NATIVE_CODEX_CONNECTION_FAILED_CODE, NATIVE_CODEX_STREAM_INTERRUPTED_CODE, isNativeCodexConnectionFailure, } from './native-adapter.js';
9
- import { boundedCodexTurnState, codexRequestBody, codexResponseTurnState, streamResponses, responsesFailure, } from './responses.js';
9
+ import { boundedCodexTurnState, codexRequestBody, codexResponseTurnState, rejectionDiagnostic, streamResponses, responsesFailure, } from './responses.js';
10
10
  import { hasNativeCodexReplayKind } from './replay.js';
11
11
  import { parseCodexResponseUsageMetadata, publishCodexResponseUsage, } from './response-usage.js';
12
12
  import { parseCodexRateLimitEvent, parseCodexRateLimitHeaders, publishCodexRateLimits, } from './rate-limits.js';
@@ -246,7 +246,7 @@ function errorFacts(response) {
246
246
  : { requestId: ProviderRequestId(requestId) },
247
247
  };
248
248
  }
249
- async function httpFailure(response, signal, telemetry) {
249
+ async function httpFailure(response, signal, telemetry, warn) {
250
250
  const facts = errorFacts(response);
251
251
  const advice = retryError('native Codex HTTP retry advice', 'SERVER', facts);
252
252
  const fail = (message, code, options) => inheritRetryDeadline(fixedFailure(message, code, options), advice);
@@ -271,6 +271,15 @@ async function httpFailure(response, signal, telemetry) {
271
271
  if (response.status >= 500) {
272
272
  return fail('native Codex server request failed', 'SERVER', facts);
273
273
  }
274
+ // Every classified refusal above names itself. This branch is the only place
275
+ // where the provider's own reason would otherwise be discarded, so record it
276
+ // for the operator without turning it into the provider-neutral message.
277
+ warn?.(rejectionDiagnostic('HTTP', {
278
+ status: response.status,
279
+ ...detail.code === undefined ? {} : { code: detail.code },
280
+ ...detail.message === undefined ? {} : { message: detail.message },
281
+ ...facts?.requestId === undefined ? {} : { requestId: String(facts.requestId) },
282
+ }));
274
283
  return fail('native Codex request was rejected', 'INVALID_REQUEST', facts);
275
284
  }
276
285
  function mappedFailure(error, watchdog, parent) {
@@ -520,7 +529,7 @@ export class NativeCodexHttpTransport {
520
529
  }
521
530
  }
522
531
  if (!response.ok) {
523
- let failure = await httpFailure(response, watchdog.signal, telemetry);
532
+ let failure = await httpFailure(response, watchdog.signal, telemetry, this.options.warn);
524
533
  if (generation.signal?.aborted || watchdog.timedOut()) {
525
534
  failure = mappedFailure(failure, watchdog, generation.signal);
526
535
  }
@@ -9,7 +9,7 @@ import { NativeCodexHttpTransport, } from './native-http.js';
9
9
  import { replayableItemId } from './replay.js';
10
10
  import { parseCodexResponseUsageMetadata, publishCodexResponseUsage, } from './response-usage.js';
11
11
  import { parseCodexRateLimitEvent, parseCodexRateLimitHeaders, publishCodexRateLimits, } from './rate-limits.js';
12
- import { ResponsesStreamTranslator, responsesFailure, codexResponseTurnState, } from './responses.js';
12
+ import { ResponsesStreamTranslator, rejectionDiagnostic, responsesFailure, codexResponseTurnState, } from './responses.js';
13
13
  import { NodeNativeCodexWebSocketFactory, } from './native-websocket-socket.js';
14
14
  import { NativeCodexWebSocketSessionState } from './native-websocket-session.js';
15
15
  const WS_BETA = 'responses_websockets=2026-02-06';
@@ -191,21 +191,17 @@ function eventFailure(event) {
191
191
  }
192
192
  return retryError('native Codex WebSocket request was rejected', 'INVALID_REQUEST', facts);
193
193
  }
194
- /**
195
- * One bounded log line naming the provider's own rejection, so an operator can
196
- * see why the socket refused a request. The provider's free-form text stays in
197
- * the log; it never becomes the provider-neutral error message.
198
- */
199
- function rejectionDiagnostic(event) {
194
+ /** Adapt one raw socket failure event to the shared operator-facing rejection line. */
195
+ function socketRejectionDiagnostic(event) {
200
196
  const error = typeof event.error === 'object' && event.error !== null
201
197
  ? event.error : event;
202
- const code = typeof error.code === 'string' && error.code.length > 0 ? error.code : undefined;
203
- const raw = typeof error.message === 'string' ? error.message : '';
204
- const message = raw.replace(/\s+/g, ' ').trim().slice(0, 300);
205
- const status = typeof event.status === 'number' ? ` status=${String(event.status)}` : '';
206
- const detail = [code, message.length === 0 ? undefined : message]
207
- .filter((part) => part !== undefined).join(': ');
208
- return `native Codex WebSocket rejected the request${status}${detail.length === 0 ? '' : ` (${detail})`}`;
198
+ const requestId = eventHeader(event, 'x-request-id');
199
+ return rejectionDiagnostic('WebSocket', {
200
+ ...typeof event.status === 'number' ? { status: event.status } : {},
201
+ ...typeof error.code === 'string' ? { code: error.code } : {},
202
+ ...typeof error.message === 'string' ? { message: error.message } : {},
203
+ ...requestId === undefined ? {} : { requestId },
204
+ });
209
205
  }
210
206
  /** Prefer WebSocket v2; once safe retries exhaust, keep that DSH session on HTTP. */
211
207
  export class NativeCodexWebSocketTransport {
@@ -416,7 +412,7 @@ export class NativeCodexWebSocketTransport {
416
412
  ? rawEvent.headers : undefined), this.options.onRateLimits, this.options.warn);
417
413
  const wrapped = eventFailure(rawEvent);
418
414
  if (wrapped !== undefined) {
419
- this.options.warn?.(rejectionDiagnostic(rawEvent));
415
+ this.options.warn?.(socketRejectionDiagnostic(rawEvent));
420
416
  throw wrapped;
421
417
  }
422
418
  if (entry.turnState === undefined) {
@@ -54,6 +54,19 @@ export interface ResponsesUsage {
54
54
  export declare function mapResponsesUsage(usage: ResponsesUsage): TokenUsage;
55
55
  /** Classify in-band failure data without reflecting provider text. */
56
56
  export declare function responsesFailure(code?: string, message?: string, headers?: unknown): LlmError;
57
+ export interface ProviderRejectionFacts {
58
+ status?: number;
59
+ code?: string;
60
+ message?: string;
61
+ requestId?: string;
62
+ }
63
+ /**
64
+ * One bounded operator-facing line naming the provider's own rejection, so a
65
+ * transport that cannot classify a refusal still records why it happened. The
66
+ * provider's free-form text stays in the log; it never becomes the
67
+ * provider-neutral error message.
68
+ */
69
+ export declare function rejectionDiagnostic(transport: 'HTTP' | 'WebSocket', facts: ProviderRejectionFacts): string;
57
70
  interface ResponsesOutputItem {
58
71
  type?: string;
59
72
  id?: string;
package/lib/responses.js CHANGED
@@ -77,7 +77,12 @@ export function toResponsesInput(messages, system) {
77
77
  type: 'function_call_output',
78
78
  call_id: String(message.toolCallId),
79
79
  output: toolOutput(message.content),
80
- ...message.isError === true ? { is_error: true } : {},
80
+ // The Responses wire has no error flag on a function call output: Codex
81
+ // keeps its own `success` metadata off the wire and reports a failed tool
82
+ // only through the output text. A Lite model rejects the unknown item
83
+ // field with a 400 INVALID_REQUEST, and because the item is replayed from
84
+ // durable history, every later request in that session fails the same way.
85
+ // `message.isError` stays DSH-side provenance for cloud context only.
81
86
  });
82
87
  continue;
83
88
  }
@@ -326,6 +331,23 @@ export function responsesFailure(code, message, headers) {
326
331
  ...delay === undefined ? {} : { providerRetryAfterMs: delay },
327
332
  });
328
333
  }
334
+ /**
335
+ * One bounded operator-facing line naming the provider's own rejection, so a
336
+ * transport that cannot classify a refusal still records why it happened. The
337
+ * provider's free-form text stays in the log; it never becomes the
338
+ * provider-neutral error message.
339
+ */
340
+ export function rejectionDiagnostic(transport, facts) {
341
+ const code = facts.code !== undefined && facts.code.length > 0 ? facts.code : undefined;
342
+ const message = (facts.message ?? '').replace(/\s+/g, ' ').trim().slice(0, 300);
343
+ const status = facts.status === undefined ? '' : ` status=${String(facts.status)}`;
344
+ const requestId = facts.requestId === undefined || facts.requestId.length === 0
345
+ ? '' : ` request_id=${facts.requestId}`;
346
+ const detail = [code, message.length === 0 ? undefined : message]
347
+ .filter((part) => part !== undefined).join(': ');
348
+ return `native Codex ${transport} rejected the request${status}${requestId}`
349
+ + `${detail.length === 0 ? '' : ` (${detail})`}`;
350
+ }
329
351
  function eventItemId(event) {
330
352
  if (typeof event.item_id === 'string' && event.item_id.length > 0)
331
353
  return event.item_id;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pure01fx/dsh-openai-codex-auth",
3
- "version": "0.13.1",
3
+ "version": "0.13.2",
4
4
  "description": "Native ChatGPT Codex provider, device-code-first login, and same-origin Web integration for DeepSeek Harness",
5
5
  "license": "MIT",
6
6
  "publishConfig": {