@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 +5 -0
- package/lib/native-http.js +12 -3
- package/lib/native-websocket.js +11 -15
- package/lib/responses.d.ts +13 -0
- package/lib/responses.js +23 -1
- package/package.json +1 -1
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.
|
package/lib/native-http.js
CHANGED
|
@@ -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
|
}
|
package/lib/native-websocket.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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?.(
|
|
415
|
+
this.options.warn?.(socketRejectionDiagnostic(rawEvent));
|
|
420
416
|
throw wrapped;
|
|
421
417
|
}
|
|
422
418
|
if (entry.turnState === undefined) {
|
package/lib/responses.d.ts
CHANGED
|
@@ -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
|
-
|
|
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