@agentionai/agents 1.12.0 → 1.13.0
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/dist/agents/Agent.d.ts +1 -1
- package/dist/agents/AgentConfig.d.ts +23 -0
- package/dist/agents/openai/CodexAgent.d.ts +135 -0
- package/dist/agents/openai/CodexAgent.js +179 -0
- package/dist/agents/openai/OpenAiAgent.d.ts +89 -6
- package/dist/agents/openai/OpenAiAgent.js +190 -17
- package/dist/agents/openai/codex-auth.d.ts +166 -0
- package/dist/agents/openai/codex-auth.js +264 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +12 -1
- package/dist/openai.d.ts +4 -1
- package/dist/openai.js +16 -1
- package/package.json +1 -1
|
@@ -5,6 +5,8 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.OpenAiAgent = void 0;
|
|
7
7
|
exports.lowestReasoningEffort = lowestReasoningEffort;
|
|
8
|
+
exports.wrapErrorBodyFetch = wrapErrorBodyFetch;
|
|
9
|
+
exports.describeOpenAIError = describeOpenAIError;
|
|
8
10
|
const openai_1 = __importDefault(require("openai"));
|
|
9
11
|
const BaseAgent_1 = require("../BaseAgent");
|
|
10
12
|
const AgentEvent_1 = require("../AgentEvent");
|
|
@@ -38,6 +40,81 @@ function lowestReasoningEffort(model) {
|
|
|
38
40
|
const group = model_types_1.OPENAI_REASONING_SUPPORT.find((entry) => entry.models.includes(base));
|
|
39
41
|
return group?.efforts[0];
|
|
40
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* `fetch` wrapper that rewrites a non-OpenAI-shaped error body into the shape
|
|
45
|
+
* the SDK can read.
|
|
46
|
+
*
|
|
47
|
+
* `APIError.generate` takes the message from `body.error` and throws the rest
|
|
48
|
+
* away (`openai/core/error.js`), so a backend that reports failures as
|
|
49
|
+
* `{"detail": "..."}` — which the ChatGPT Codex endpoint does, for all four of
|
|
50
|
+
* its body validations plus auth failures — surfaces as the useless
|
|
51
|
+
* `400 status code (no body)`. Nesting the original body under `error` puts the
|
|
52
|
+
* real reason back in the thrown error.
|
|
53
|
+
*
|
|
54
|
+
* Only touches error responses; successful (streaming) responses pass straight
|
|
55
|
+
* through untouched.
|
|
56
|
+
*/
|
|
57
|
+
function wrapErrorBodyFetch(baseFetch = fetch) {
|
|
58
|
+
return async (input, init) => {
|
|
59
|
+
const res = await baseFetch(input, init);
|
|
60
|
+
if (res.ok)
|
|
61
|
+
return res;
|
|
62
|
+
const text = await res.text().catch(() => "");
|
|
63
|
+
let body = text;
|
|
64
|
+
try {
|
|
65
|
+
const parsed = JSON.parse(text);
|
|
66
|
+
if (parsed && typeof parsed === "object" && !("error" in parsed)) {
|
|
67
|
+
body = JSON.stringify({
|
|
68
|
+
error: {
|
|
69
|
+
message: typeof parsed.detail === "string"
|
|
70
|
+
? parsed.detail
|
|
71
|
+
: JSON.stringify(parsed),
|
|
72
|
+
...parsed,
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Not JSON (an HTML error page, say) — hand the text back unchanged so
|
|
79
|
+
// the SDK reports it as the message.
|
|
80
|
+
}
|
|
81
|
+
// Reading the body consumed it, so the Response has to be rebuilt. Drop the
|
|
82
|
+
// length/encoding headers, which no longer describe the new payload.
|
|
83
|
+
const headers = new Headers(res.headers);
|
|
84
|
+
headers.delete("content-length");
|
|
85
|
+
headers.delete("content-encoding");
|
|
86
|
+
// `globalThis.Response`, not `Response`: this module imports the Responses
|
|
87
|
+
// API's `Response` *type*, which shadows the global class name here.
|
|
88
|
+
return new globalThis.Response(body, {
|
|
89
|
+
status: res.status,
|
|
90
|
+
statusText: res.statusText,
|
|
91
|
+
headers,
|
|
92
|
+
});
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Pull a human-readable message out of an OpenAI-shaped error.
|
|
97
|
+
*
|
|
98
|
+
* `api.openai.com` answers with `{ error: { message, code } }`, but not every
|
|
99
|
+
* host behind this SDK does — the ChatGPT Codex backend reports its validation
|
|
100
|
+
* failures as `{ detail: "Instructions are required" }`. Reading
|
|
101
|
+
* `error.error.message` blindly turns those into a `TypeError` that hides the
|
|
102
|
+
* real cause, so every field is probed defensively and the SDK's own `message`
|
|
103
|
+
* is the last resort.
|
|
104
|
+
*/
|
|
105
|
+
function describeOpenAIError(error) {
|
|
106
|
+
const err = error;
|
|
107
|
+
const body = err?.error;
|
|
108
|
+
const fromBody = typeof body === "string"
|
|
109
|
+
? body
|
|
110
|
+
: (body?.message ?? body?.detail ?? undefined);
|
|
111
|
+
return {
|
|
112
|
+
message: fromBody ?? err?.detail ?? err?.message ?? "Unknown error",
|
|
113
|
+
code: typeof body === "object" ? body?.code : undefined,
|
|
114
|
+
status: err?.status,
|
|
115
|
+
body: body ?? err?.detail,
|
|
116
|
+
};
|
|
117
|
+
}
|
|
41
118
|
/**
|
|
42
119
|
* Agent for OpenAI models using the Responses API.
|
|
43
120
|
*
|
|
@@ -54,17 +131,39 @@ function lowestReasoningEffort(model) {
|
|
|
54
131
|
* ```
|
|
55
132
|
*/
|
|
56
133
|
class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
134
|
+
/**
|
|
135
|
+
* Whether a non-streaming call must be issued as a stream and collapsed.
|
|
136
|
+
* `false` here; `CodexAgent` overrides it, since that backend refuses
|
|
137
|
+
* `stream: false` outright.
|
|
138
|
+
*/
|
|
139
|
+
get forceStreaming() {
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Last chance to reshape a request body before it goes out. Identity here —
|
|
144
|
+
* `CodexAgent` overrides it to satisfy that backend's extra validations.
|
|
145
|
+
*/
|
|
146
|
+
transformRequestParams(params) {
|
|
147
|
+
return params;
|
|
148
|
+
}
|
|
57
149
|
constructor(config, history) {
|
|
150
|
+
// Cast: `BaseAgentConfig.apiKey` is `string`, while this agent also accepts
|
|
151
|
+
// a token-returning function. BaseAgent never reads the field — it only
|
|
152
|
+
// declares it — so widening the base config for one provider would be the
|
|
153
|
+
// more invasive fix.
|
|
58
154
|
super({ ...config, vendor: "openai" }, history);
|
|
59
155
|
/** Count of tool calls in current execution */
|
|
60
156
|
this.currentToolCallCount = 0;
|
|
157
|
+
// Merge flat config (deprecated) with nested vendorConfig
|
|
158
|
+
// Flat config takes precedence for backward compatibility
|
|
159
|
+
const vendorConfig = config.vendorConfig?.openai || {};
|
|
160
|
+
const baseURL = config.baseURL ?? vendorConfig.baseURL;
|
|
61
161
|
this.client = new openai_1.default({
|
|
62
162
|
apiKey: config.apiKey,
|
|
163
|
+
baseURL,
|
|
63
164
|
defaultHeaders: config.defaultHeaders,
|
|
165
|
+
fetch: config.fetch,
|
|
64
166
|
});
|
|
65
|
-
// Merge flat config (deprecated) with nested vendorConfig
|
|
66
|
-
// Flat config takes precedence for backward compatibility
|
|
67
|
-
const vendorConfig = config.vendorConfig?.openai || {};
|
|
68
167
|
const disableParallelToolUse = config.disableParallelToolUse ??
|
|
69
168
|
vendorConfig.disableParallelToolUse ??
|
|
70
169
|
false;
|
|
@@ -87,6 +186,7 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
87
186
|
user,
|
|
88
187
|
builtInTools,
|
|
89
188
|
apiKey: config.apiKey,
|
|
189
|
+
baseURL,
|
|
90
190
|
temperature: config.temperature,
|
|
91
191
|
topP: config.topP,
|
|
92
192
|
seed: config.seed,
|
|
@@ -111,6 +211,8 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
111
211
|
id: model.id,
|
|
112
212
|
created: model.created ? new Date(model.created * 1000) : undefined,
|
|
113
213
|
ownedBy: model.owned_by,
|
|
214
|
+
// Cast: this implementation always returns OpenAI's own cards; a
|
|
215
|
+
// subclass that reports a different shape overrides the whole method.
|
|
114
216
|
raw: model,
|
|
115
217
|
}));
|
|
116
218
|
}
|
|
@@ -118,6 +220,11 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
118
220
|
throw new AgentError_1.ExecutionError(`Failed to list OpenAI models: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
119
221
|
}
|
|
120
222
|
}
|
|
223
|
+
/** The configured key, resolving the function form if that is what was given. */
|
|
224
|
+
async resolveApiKey() {
|
|
225
|
+
const key = this.config.apiKey;
|
|
226
|
+
return typeof key === "function" ? await key() : (key ?? "");
|
|
227
|
+
}
|
|
121
228
|
getToolDefinitions() {
|
|
122
229
|
return Array.from(this.tools.values()).map((tool) => {
|
|
123
230
|
const prompt = tool.getPrompt();
|
|
@@ -152,6 +259,66 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
152
259
|
...(this.config.builtInTools ?? []),
|
|
153
260
|
];
|
|
154
261
|
}
|
|
262
|
+
/**
|
|
263
|
+
* Rebuild a terminal response's `output` from the items streamed alongside it.
|
|
264
|
+
*
|
|
265
|
+
* The Codex backend sends `response.completed` with `output: []` and no
|
|
266
|
+
* `output_text`, unlike the platform API which fills both in — the content
|
|
267
|
+
* only ever arrives as `response.output_item.done` events. Everything
|
|
268
|
+
* downstream (tool-call detection, the text written to history) reads
|
|
269
|
+
* `output`, so without this a Codex turn silently commits an empty assistant
|
|
270
|
+
* message and drops every tool call.
|
|
271
|
+
*
|
|
272
|
+
* A no-op wherever `output` is already populated, so the platform path is
|
|
273
|
+
* untouched.
|
|
274
|
+
*/
|
|
275
|
+
repairStreamedOutput(response, streamedItems) {
|
|
276
|
+
if (response.output?.length || streamedItems.length === 0)
|
|
277
|
+
return response;
|
|
278
|
+
const output = streamedItems;
|
|
279
|
+
const outputText = output
|
|
280
|
+
.filter((item) => item.type === "message")
|
|
281
|
+
.flatMap((item) => ("content" in item ? (item.content ?? []) : []))
|
|
282
|
+
.filter((part) => part?.type === "output_text")
|
|
283
|
+
.map((part) => ("text" in part ? part.text : ""))
|
|
284
|
+
.join("");
|
|
285
|
+
return { ...response, output, output_text: outputText };
|
|
286
|
+
}
|
|
287
|
+
/**
|
|
288
|
+
* Issue a non-streaming Responses API call.
|
|
289
|
+
*
|
|
290
|
+
* When {@link forceStreaming} is set the request is streamed and the terminal
|
|
291
|
+
* event's `response` handed back instead — giving callers the same `Response`
|
|
292
|
+
* either way, at the cost of buffering the turn.
|
|
293
|
+
*/
|
|
294
|
+
async createResponse(params, requestOptions) {
|
|
295
|
+
const body = this.transformRequestParams(params);
|
|
296
|
+
if (!this.forceStreaming) {
|
|
297
|
+
return this.client.responses.create({ ...body, stream: false }, requestOptions);
|
|
298
|
+
}
|
|
299
|
+
const stream = (await this.client.responses.create({ ...body, stream: true }, requestOptions));
|
|
300
|
+
let terminal;
|
|
301
|
+
const streamedItems = [];
|
|
302
|
+
for await (const event of stream) {
|
|
303
|
+
// Collected because the Codex backend leaves `output` empty on the
|
|
304
|
+
// terminal event — see repairStreamedOutput().
|
|
305
|
+
if (event.type === "response.output_item.done") {
|
|
306
|
+
streamedItems.push(event.item);
|
|
307
|
+
}
|
|
308
|
+
// `incomplete` and `failed` carry a Response too — handleResponse()
|
|
309
|
+
// already reads `status` off it, so let it report the reason rather than
|
|
310
|
+
// failing here with a vaguer message.
|
|
311
|
+
if (event.type === "response.completed" ||
|
|
312
|
+
event.type === "response.incomplete" ||
|
|
313
|
+
event.type === "response.failed") {
|
|
314
|
+
terminal = event.response;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
if (!terminal) {
|
|
318
|
+
throw new AgentError_1.ExecutionError("OpenAI stream ended without a terminal response event");
|
|
319
|
+
}
|
|
320
|
+
return this.repairStreamedOutput(terminal, streamedItems);
|
|
321
|
+
}
|
|
155
322
|
/**
|
|
156
323
|
* Build the `reasoning` field for a Responses API request, as an object to
|
|
157
324
|
* spread into the request params.
|
|
@@ -217,7 +384,7 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
217
384
|
try {
|
|
218
385
|
const inputMessages = transformers_1.openAiTransformer.toProvider(this.history.getEntries());
|
|
219
386
|
this.startTurnTimer();
|
|
220
|
-
const response = await this.
|
|
387
|
+
const response = await this.createResponse({
|
|
221
388
|
model: this.config.model,
|
|
222
389
|
max_output_tokens: this.config.maxTokens,
|
|
223
390
|
input: inputMessages,
|
|
@@ -242,16 +409,16 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
242
409
|
throw abortError;
|
|
243
410
|
}
|
|
244
411
|
if (error && typeof error === "object" && "error" in error) {
|
|
245
|
-
const openAIError = error;
|
|
246
|
-
const apiError = new AgentError_1.ApiError(`OpenAI API error: ${openAIError.
|
|
247
|
-
if (openAIError.
|
|
412
|
+
const openAIError = describeOpenAIError(error);
|
|
413
|
+
const apiError = new AgentError_1.ApiError(`OpenAI API error: ${openAIError.message}`, openAIError.status, openAIError.body);
|
|
414
|
+
if (openAIError.code === "insufficient_quota") {
|
|
248
415
|
apiError.message =
|
|
249
416
|
"OpenAI API quota exceeded. Please check your billing details.";
|
|
250
417
|
}
|
|
251
418
|
this.emit(AgentEvent_1.AgentEvent.ERROR, apiError);
|
|
252
419
|
// Report error to viz
|
|
253
420
|
if (this.vizEventId) {
|
|
254
|
-
VizReporter_1.vizReporter.agentError(this.vizEventId, "ApiError", apiError.message, openAIError.
|
|
421
|
+
VizReporter_1.vizReporter.agentError(this.vizEventId, "ApiError", apiError.message, openAIError.code === "rate_limit_exceeded");
|
|
255
422
|
this.vizEventId = undefined;
|
|
256
423
|
}
|
|
257
424
|
throw apiError;
|
|
@@ -344,7 +511,7 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
344
511
|
try {
|
|
345
512
|
const inputMessages = transformers_1.openAiTransformer.toProvider(this.history.getEntries());
|
|
346
513
|
this.startTurnTimer();
|
|
347
|
-
const newResponse = await this.
|
|
514
|
+
const newResponse = await this.createResponse({
|
|
348
515
|
model: this.config.model,
|
|
349
516
|
max_output_tokens: this.config.maxTokens,
|
|
350
517
|
input: inputMessages,
|
|
@@ -361,8 +528,8 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
361
528
|
}
|
|
362
529
|
catch (error) {
|
|
363
530
|
if (error && typeof error === "object" && "error" in error) {
|
|
364
|
-
const openAIError = error;
|
|
365
|
-
const apiError = new AgentError_1.ApiError(`OpenAI API error during tool response: ${openAIError.
|
|
531
|
+
const openAIError = describeOpenAIError(error);
|
|
532
|
+
const apiError = new AgentError_1.ApiError(`OpenAI API error during tool response: ${openAIError.message}`, openAIError.status, openAIError.body);
|
|
366
533
|
this.emit(AgentEvent_1.AgentEvent.ERROR, apiError);
|
|
367
534
|
throw apiError;
|
|
368
535
|
}
|
|
@@ -501,11 +668,11 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
501
668
|
throw this.withPartialTurn(error);
|
|
502
669
|
}
|
|
503
670
|
if (error && typeof error === "object" && "error" in error) {
|
|
504
|
-
const openAIError = error;
|
|
505
|
-
const apiError = new AgentError_1.ApiError(`OpenAI API error: ${openAIError.
|
|
671
|
+
const openAIError = describeOpenAIError(error);
|
|
672
|
+
const apiError = new AgentError_1.ApiError(`OpenAI API error: ${openAIError.message}`, openAIError.status, openAIError.body);
|
|
506
673
|
this.emit(AgentEvent_1.AgentEvent.ERROR, apiError);
|
|
507
674
|
if (this.vizEventId) {
|
|
508
|
-
VizReporter_1.vizReporter.agentError(this.vizEventId, "ApiError", apiError.message, openAIError.
|
|
675
|
+
VizReporter_1.vizReporter.agentError(this.vizEventId, "ApiError", apiError.message, openAIError.code === "rate_limit_exceeded");
|
|
509
676
|
this.vizEventId = undefined;
|
|
510
677
|
}
|
|
511
678
|
throw this.withPartialTurn(apiError);
|
|
@@ -525,7 +692,7 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
525
692
|
async *streamTurn(options) {
|
|
526
693
|
const inputMessages = transformers_1.openAiTransformer.toProvider(this.history.getEntries());
|
|
527
694
|
this.startTurnTimer();
|
|
528
|
-
const stream = await this.client.responses.create({
|
|
695
|
+
const stream = await this.client.responses.create(this.transformRequestParams({
|
|
529
696
|
model: this.config.model,
|
|
530
697
|
max_output_tokens: this.config.maxTokens,
|
|
531
698
|
input: inputMessages,
|
|
@@ -536,8 +703,9 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
536
703
|
top_p: this.config.topP,
|
|
537
704
|
user: this.config.user,
|
|
538
705
|
...this.buildReasoningParams("auto"),
|
|
539
|
-
}, { signal: options?.signal });
|
|
706
|
+
}), { signal: options?.signal });
|
|
540
707
|
let completedEvent = null;
|
|
708
|
+
const streamedItems = [];
|
|
541
709
|
// The Responses API builds the committed turn out of `response.completed`,
|
|
542
710
|
// which only arrives on success, so the deltas are mirrored here as well:
|
|
543
711
|
// without them a stream that dies mid-flight leaves nothing behind at all,
|
|
@@ -580,6 +748,11 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
580
748
|
if (acc)
|
|
581
749
|
acc.arguments += event.delta;
|
|
582
750
|
}
|
|
751
|
+
if (event.type === "response.output_item.done") {
|
|
752
|
+
// The Codex backend leaves `output` empty on the terminal event, so
|
|
753
|
+
// the finished items are kept here — see repairStreamedOutput().
|
|
754
|
+
streamedItems.push(event.item);
|
|
755
|
+
}
|
|
583
756
|
if (event.type === "response.completed") {
|
|
584
757
|
completedEvent = event;
|
|
585
758
|
if (event.response.usage) {
|
|
@@ -597,7 +770,7 @@ class OpenAiAgent extends BaseAgent_1.BaseAgent {
|
|
|
597
770
|
if (!completedEvent) {
|
|
598
771
|
throw new AgentError_1.ExecutionError("OpenAI stream ended without a completed event");
|
|
599
772
|
}
|
|
600
|
-
const response = completedEvent.response;
|
|
773
|
+
const response = this.repairStreamedOutput(completedEvent.response, streamedItems);
|
|
601
774
|
const toolCalls = response.output.filter((o) => o.type === "function_call");
|
|
602
775
|
if (toolCalls.length > 0) {
|
|
603
776
|
// As in handleResponse(): bail out before the assistant turn is written,
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OAuth against a ChatGPT subscription, as used by OpenAI's Codex CLI.
|
|
3
|
+
*
|
|
4
|
+
* This is a different product surface from the platform API: the credentials are
|
|
5
|
+
* a ChatGPT login rather than a `sk-...` platform key, and requests are billed
|
|
6
|
+
* against the subscription instead of an API account. The endpoint differs too —
|
|
7
|
+
* see {@link CODEX_BASE_URL}.
|
|
8
|
+
*
|
|
9
|
+
* None of it is a documented public API. The values here were cross-checked
|
|
10
|
+
* against the Codex CLI's own behaviour and several independent
|
|
11
|
+
* reimplementations, but OpenAI can change them without notice.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Base URL for the ChatGPT-backed Codex Responses API.
|
|
15
|
+
*
|
|
16
|
+
* The SDK appends `/responses`, giving
|
|
17
|
+
* `https://chatgpt.com/backend-api/codex/responses`.
|
|
18
|
+
*/
|
|
19
|
+
export declare const CODEX_BASE_URL = "https://chatgpt.com/backend-api/codex";
|
|
20
|
+
/** Public OAuth client id the Codex CLI uses. Not a secret. */
|
|
21
|
+
export declare const CODEX_CLIENT_ID = "app_EMoamEEZ73f0CkXaXp7hrann";
|
|
22
|
+
/** Token endpoint used to exchange a refresh token for a fresh access token. */
|
|
23
|
+
export declare const CODEX_TOKEN_URL = "https://auth.openai.com/oauth/token";
|
|
24
|
+
/**
|
|
25
|
+
* Default `originator` header value.
|
|
26
|
+
*
|
|
27
|
+
* OpenAI gates parts of the model catalog on this, so an unrecognised value can
|
|
28
|
+
* quietly change which models an account may reach.
|
|
29
|
+
*/
|
|
30
|
+
export declare const CODEX_ORIGINATOR = "codex_cli_rs";
|
|
31
|
+
/**
|
|
32
|
+
* `client_version` for the Codex models endpoint, which 400s without one.
|
|
33
|
+
*
|
|
34
|
+
* Each model also carries a `minimal_client_version`; the backend hides models
|
|
35
|
+
* newer than the version claimed here, so an old value quietly shortens the
|
|
36
|
+
* list rather than erroring.
|
|
37
|
+
*/
|
|
38
|
+
export declare const CODEX_CLIENT_VERSION = "0.153.4";
|
|
39
|
+
/**
|
|
40
|
+
* One entry from the Codex `/models` response.
|
|
41
|
+
*
|
|
42
|
+
* Nothing like the platform API's `/v1/models` — richer, and shaped for the
|
|
43
|
+
* Codex client. Only the fields worth relying on are named; the rest come
|
|
44
|
+
* through on `ModelInfo.raw`.
|
|
45
|
+
*/
|
|
46
|
+
export interface CodexModelCard {
|
|
47
|
+
slug: string;
|
|
48
|
+
display_name?: string;
|
|
49
|
+
description?: string;
|
|
50
|
+
/** Default context window for this account's plan. */
|
|
51
|
+
context_window?: number;
|
|
52
|
+
/** Largest window the model can be driven at. */
|
|
53
|
+
max_context_window?: number;
|
|
54
|
+
input_modalities?: string[];
|
|
55
|
+
supported_reasoning_levels?: {
|
|
56
|
+
effort: string;
|
|
57
|
+
description?: string;
|
|
58
|
+
}[];
|
|
59
|
+
default_reasoning_level?: string;
|
|
60
|
+
/** Subscription plans that may select this model. */
|
|
61
|
+
available_in_plans?: string[];
|
|
62
|
+
/** `"list"` for models meant to be shown in a picker. */
|
|
63
|
+
visibility?: string;
|
|
64
|
+
supported_in_api?: boolean;
|
|
65
|
+
minimal_client_version?: string;
|
|
66
|
+
supports_parallel_tool_calls?: boolean;
|
|
67
|
+
[key: string]: unknown;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Credentials for the ChatGPT/Codex backend.
|
|
71
|
+
*/
|
|
72
|
+
export interface CodexCredentials {
|
|
73
|
+
/** Bearer token sent as `Authorization`. */
|
|
74
|
+
accessToken: string;
|
|
75
|
+
/** Used to mint a new access token once the current one expires. */
|
|
76
|
+
refreshToken?: string;
|
|
77
|
+
/**
|
|
78
|
+
* Workspace/account the request is billed to, sent as the
|
|
79
|
+
* `chatgpt-account-id` header. Read from `auth.json`, or decoded from the
|
|
80
|
+
* `id_token` when absent.
|
|
81
|
+
*/
|
|
82
|
+
accountId?: string;
|
|
83
|
+
/** Account e-mail, when the `id_token` carried one. Informational. */
|
|
84
|
+
email?: string;
|
|
85
|
+
/** Subscription tier (`plus`, `pro`, …), when present. Informational. */
|
|
86
|
+
planType?: string;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Decode a JWT's payload without verifying its signature.
|
|
90
|
+
*
|
|
91
|
+
* Verification is the token endpoint's job — we are only reading claims out of a
|
|
92
|
+
* token we were just handed over TLS, never making a trust decision on it.
|
|
93
|
+
* Returns `undefined` for anything that does not parse, so a malformed or
|
|
94
|
+
* opaque token degrades to "no claims" rather than throwing.
|
|
95
|
+
*/
|
|
96
|
+
export declare function decodeJwtClaims<T = Record<string, unknown>>(token: string): T | undefined;
|
|
97
|
+
/**
|
|
98
|
+
* Seconds-since-epoch expiry of a JWT, or `undefined` if it has no `exp`.
|
|
99
|
+
*/
|
|
100
|
+
export declare function jwtExpiry(token: string): number | undefined;
|
|
101
|
+
/** Default location of Codex's credential file. */
|
|
102
|
+
export declare function codexAuthFilePath(codexHome?: string): string;
|
|
103
|
+
/**
|
|
104
|
+
* Read the credentials the Codex CLI stored at `$CODEX_HOME/auth.json`
|
|
105
|
+
* (`~/.codex/auth.json` by default).
|
|
106
|
+
*
|
|
107
|
+
* Sign in with `codex login` first — this only reads what that wrote, it does
|
|
108
|
+
* not run the OAuth flow itself.
|
|
109
|
+
*
|
|
110
|
+
* @throws if the file is missing, unreadable, not JSON, or holds no access token.
|
|
111
|
+
*/
|
|
112
|
+
export declare function loadCodexCredentials(codexHome?: string): Promise<CodexCredentials>;
|
|
113
|
+
/**
|
|
114
|
+
* Exchange a refresh token for a fresh access token.
|
|
115
|
+
*
|
|
116
|
+
* The returned credentials carry the new `refresh_token` when the server
|
|
117
|
+
* rotated it, and the previous one otherwise.
|
|
118
|
+
*/
|
|
119
|
+
export declare function refreshCodexCredentials(refreshToken: string, options?: {
|
|
120
|
+
clientId?: string;
|
|
121
|
+
tokenUrl?: string;
|
|
122
|
+
signal?: AbortSignal;
|
|
123
|
+
}): Promise<CodexCredentials>;
|
|
124
|
+
/** Options for {@link createCodexTokenProvider}. */
|
|
125
|
+
export interface CodexTokenProviderOptions {
|
|
126
|
+
/** OAuth client id. Defaults to {@link CODEX_CLIENT_ID}. */
|
|
127
|
+
clientId?: string;
|
|
128
|
+
/** Token endpoint. Defaults to {@link CODEX_TOKEN_URL}. */
|
|
129
|
+
tokenUrl?: string;
|
|
130
|
+
/**
|
|
131
|
+
* Refresh this many seconds before the access token actually expires, so a
|
|
132
|
+
* request is never sent with a token that dies in flight.
|
|
133
|
+
*
|
|
134
|
+
* @default 300
|
|
135
|
+
*/
|
|
136
|
+
refreshSkewSeconds?: number;
|
|
137
|
+
/**
|
|
138
|
+
* Called after every successful refresh, e.g. to persist the rotated refresh
|
|
139
|
+
* token. Errors thrown here are ignored — a failed write must not fail the
|
|
140
|
+
* request the token was minted for.
|
|
141
|
+
*/
|
|
142
|
+
onRefresh?: (credentials: CodexCredentials) => void | Promise<void>;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* A token source that hands out an access token and silently refreshes it.
|
|
146
|
+
*
|
|
147
|
+
* The `getToken` function is shaped for the OpenAI SDK's `apiKey` option, which
|
|
148
|
+
* accepts an async function and calls it before *every* request — so a
|
|
149
|
+
* long-running agent keeps working past the ~1h life of an access token without
|
|
150
|
+
* anyone reaching for the credential file again.
|
|
151
|
+
*/
|
|
152
|
+
export interface CodexTokenProvider {
|
|
153
|
+
/** Current access token, refreshed on demand. Pass as the SDK's `apiKey`. */
|
|
154
|
+
getToken: () => Promise<string>;
|
|
155
|
+
/** Latest known credentials, including `accountId`. */
|
|
156
|
+
current: () => CodexCredentials;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Wrap credentials in a self-refreshing token provider.
|
|
160
|
+
*
|
|
161
|
+
* Refreshes lazily — only when a token is actually asked for and the current
|
|
162
|
+
* one is within `refreshSkewSeconds` of expiry. Concurrent callers share a
|
|
163
|
+
* single in-flight refresh rather than each starting their own.
|
|
164
|
+
*/
|
|
165
|
+
export declare function createCodexTokenProvider(credentials: CodexCredentials, options?: CodexTokenProviderOptions): CodexTokenProvider;
|
|
166
|
+
//# sourceMappingURL=codex-auth.d.ts.map
|