@dexto/core 1.6.12 → 1.6.14
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/events/index.cjs +1 -0
- package/dist/events/index.d.ts +16 -2
- package/dist/events/index.d.ts.map +1 -1
- package/dist/events/index.js +1 -0
- package/dist/llm/executor/turn-executor.cjs +11 -1
- package/dist/llm/executor/turn-executor.d.ts +3 -1
- package/dist/llm/executor/turn-executor.d.ts.map +1 -1
- package/dist/llm/executor/turn-executor.js +11 -1
- package/dist/llm/index.cjs +16 -0
- package/dist/llm/index.d.ts +2 -0
- package/dist/llm/index.d.ts.map +1 -1
- package/dist/llm/index.js +18 -0
- package/dist/llm/providers/codex-app-server.cjs +1391 -0
- package/dist/llm/providers/codex-app-server.d.ts +150 -0
- package/dist/llm/providers/codex-app-server.d.ts.map +1 -0
- package/dist/llm/providers/codex-app-server.js +1367 -0
- package/dist/llm/providers/codex-base-url.cjs +97 -0
- package/dist/llm/providers/codex-base-url.d.ts +9 -0
- package/dist/llm/providers/codex-base-url.d.ts.map +1 -0
- package/dist/llm/providers/codex-base-url.js +70 -0
- package/dist/llm/services/factory.cjs +19 -1
- package/dist/llm/services/factory.d.ts +3 -0
- package/dist/llm/services/factory.d.ts.map +1 -1
- package/dist/llm/services/factory.js +21 -1
- package/dist/session/chat-session.cjs +17 -0
- package/dist/session/chat-session.d.ts +1 -0
- package/dist/session/chat-session.d.ts.map +1 -1
- package/dist/session/chat-session.js +17 -0
- package/dist/session/session-manager.cjs +27 -1
- package/dist/session/session-manager.d.ts +6 -0
- package/dist/session/session-manager.d.ts.map +1 -1
- package/dist/session/session-manager.js +27 -1
- package/dist/utils/result.cjs +3 -2
- package/dist/utils/result.d.ts.map +1 -1
- package/dist/utils/result.js +3 -2
- package/package.json +1 -1
|
@@ -0,0 +1,1391 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __export = (target, all) => {
|
|
7
|
+
for (var name in all)
|
|
8
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
+
};
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
12
|
+
for (let key of __getOwnPropNames(from))
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
14
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
15
|
+
}
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
var codex_app_server_exports = {};
|
|
20
|
+
__export(codex_app_server_exports, {
|
|
21
|
+
CodexAppServerClient: () => CodexAppServerClient,
|
|
22
|
+
createCodexLanguageModel: () => createCodexLanguageModel
|
|
23
|
+
});
|
|
24
|
+
module.exports = __toCommonJS(codex_app_server_exports);
|
|
25
|
+
var import_node_child_process = require("node:child_process");
|
|
26
|
+
var import_node_readline = require("node:readline");
|
|
27
|
+
var import_DextoRuntimeError = require("../../errors/DextoRuntimeError.js");
|
|
28
|
+
var import_types = require("../../errors/types.js");
|
|
29
|
+
var import_safe_stringify = require("../../utils/safe-stringify.js");
|
|
30
|
+
var import_error_codes = require("../error-codes.js");
|
|
31
|
+
var import_errors = require("../errors.js");
|
|
32
|
+
var import_codex_base_url = require("./codex-base-url.js");
|
|
33
|
+
const CODEX_PROTOCOL_ERROR_CODE = "llm_codex_protocol_invalid";
|
|
34
|
+
const CODEX_CLIENT_RUNTIME_ERROR_CODE = "llm_codex_client_runtime";
|
|
35
|
+
function createCodexProtocolError(message, context) {
|
|
36
|
+
return new import_DextoRuntimeError.DextoRuntimeError(
|
|
37
|
+
CODEX_PROTOCOL_ERROR_CODE,
|
|
38
|
+
import_types.ErrorScope.LLM,
|
|
39
|
+
import_types.ErrorType.THIRD_PARTY,
|
|
40
|
+
message,
|
|
41
|
+
context
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
function createCodexClientRuntimeError(message, context, type = import_types.ErrorType.SYSTEM) {
|
|
45
|
+
return new import_DextoRuntimeError.DextoRuntimeError(
|
|
46
|
+
CODEX_CLIENT_RUNTIME_ERROR_CODE,
|
|
47
|
+
import_types.ErrorScope.LLM,
|
|
48
|
+
type,
|
|
49
|
+
message,
|
|
50
|
+
context
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
function createCodexClientExitedError(input) {
|
|
54
|
+
return createCodexClientRuntimeError(
|
|
55
|
+
`Codex app-server exited unexpectedly (${input.signal ?? `code ${input.code ?? "unknown"}`})`,
|
|
56
|
+
{
|
|
57
|
+
...input.code !== void 0 ? { code: input.code } : {},
|
|
58
|
+
...input.signal !== void 0 ? { signal: input.signal } : {}
|
|
59
|
+
},
|
|
60
|
+
import_types.ErrorType.THIRD_PARTY
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
64
|
+
const DEFAULT_CLIENT_INFO = {
|
|
65
|
+
name: "dexto",
|
|
66
|
+
title: "Dexto",
|
|
67
|
+
version: "1.0.0"
|
|
68
|
+
};
|
|
69
|
+
const CODEX_DEVELOPER_INSTRUCTIONS = [
|
|
70
|
+
"You are providing model responses for a host application.",
|
|
71
|
+
"Treat the provided input as the full conversation transcript.",
|
|
72
|
+
"Use the host-provided dynamic tools when tool use is needed.",
|
|
73
|
+
"Do not use Codex built-in tools, shell commands, file edits, approvals, MCP tools, or ask-user flows.",
|
|
74
|
+
"When you are not calling a tool, answer with the assistant response only."
|
|
75
|
+
].join(" ");
|
|
76
|
+
function isRecord(value) {
|
|
77
|
+
return typeof value === "object" && value !== null;
|
|
78
|
+
}
|
|
79
|
+
function getString(value) {
|
|
80
|
+
return typeof value === "string" && value.trim().length > 0 ? value : null;
|
|
81
|
+
}
|
|
82
|
+
function getBoolean(value) {
|
|
83
|
+
return typeof value === "boolean" ? value : null;
|
|
84
|
+
}
|
|
85
|
+
function getNumber(value) {
|
|
86
|
+
if (typeof value === "number" && Number.isFinite(value)) {
|
|
87
|
+
return value;
|
|
88
|
+
}
|
|
89
|
+
if (typeof value === "string") {
|
|
90
|
+
const parsed = Number(value);
|
|
91
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
function getArray(value) {
|
|
96
|
+
return Array.isArray(value) ? value : null;
|
|
97
|
+
}
|
|
98
|
+
function normalizeError(error) {
|
|
99
|
+
return error instanceof Error ? error : createCodexClientRuntimeError(String(error));
|
|
100
|
+
}
|
|
101
|
+
function parseCodexAccount(value) {
|
|
102
|
+
if (!isRecord(value)) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
const type = getString(value["type"]);
|
|
106
|
+
if (type === "apiKey") {
|
|
107
|
+
return { type: "apiKey" };
|
|
108
|
+
}
|
|
109
|
+
if (type !== "chatgpt") {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
const email = getString(value["email"]);
|
|
113
|
+
const planType = getString(value["planType"]);
|
|
114
|
+
if (!email || !planType) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
type: "chatgpt",
|
|
119
|
+
email,
|
|
120
|
+
planType
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function parseReadAccountResponse(value) {
|
|
124
|
+
if (!isRecord(value)) {
|
|
125
|
+
throw createCodexProtocolError("Invalid account/read response from Codex", {
|
|
126
|
+
method: "account/read"
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
const requiresOpenaiAuth = getBoolean(value["requiresOpenaiAuth"]);
|
|
130
|
+
if (requiresOpenaiAuth === null) {
|
|
131
|
+
throw createCodexProtocolError(
|
|
132
|
+
"Codex account/read response is missing requiresOpenaiAuth",
|
|
133
|
+
{
|
|
134
|
+
method: "account/read"
|
|
135
|
+
}
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
account: parseCodexAccount(value["account"]),
|
|
140
|
+
requiresOpenaiAuth
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
function parseLoginResponse(value) {
|
|
144
|
+
if (!isRecord(value)) {
|
|
145
|
+
throw createCodexProtocolError("Invalid account/login/start response from Codex", {
|
|
146
|
+
method: "account/login/start"
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
const type = getString(value["type"]);
|
|
150
|
+
if (!type) {
|
|
151
|
+
throw createCodexProtocolError("Codex login response is missing type", {
|
|
152
|
+
method: "account/login/start"
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
if (type === "apiKey") {
|
|
156
|
+
return { type };
|
|
157
|
+
}
|
|
158
|
+
if (type === "chatgptAuthTokens") {
|
|
159
|
+
return { type };
|
|
160
|
+
}
|
|
161
|
+
if (type !== "chatgpt") {
|
|
162
|
+
throw createCodexProtocolError(`Unsupported Codex login response type: ${type}`, {
|
|
163
|
+
method: "account/login/start",
|
|
164
|
+
type
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
const loginId = getString(value["loginId"]);
|
|
168
|
+
const authUrl = getString(value["authUrl"]);
|
|
169
|
+
if (!loginId || !authUrl) {
|
|
170
|
+
throw createCodexProtocolError("Codex ChatGPT login response is missing login details", {
|
|
171
|
+
method: "account/login/start"
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
type,
|
|
176
|
+
loginId,
|
|
177
|
+
authUrl
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
function parseModelListResponse(value) {
|
|
181
|
+
if (!isRecord(value)) {
|
|
182
|
+
throw createCodexProtocolError("Invalid model/list response from Codex", {
|
|
183
|
+
method: "model/list"
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
const data = getArray(value["data"]);
|
|
187
|
+
if (!data) {
|
|
188
|
+
throw createCodexProtocolError("Codex model/list response is missing data", {
|
|
189
|
+
method: "model/list"
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
const models = [];
|
|
193
|
+
for (const entry of data) {
|
|
194
|
+
if (!isRecord(entry)) {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
const id = getString(entry["id"]);
|
|
198
|
+
const model = getString(entry["model"]);
|
|
199
|
+
const displayName = getString(entry["displayName"]);
|
|
200
|
+
const description = getString(entry["description"]);
|
|
201
|
+
const hidden = getBoolean(entry["hidden"]);
|
|
202
|
+
const isDefault = getBoolean(entry["isDefault"]);
|
|
203
|
+
const defaultReasoningEffort = getString(entry["defaultReasoningEffort"]);
|
|
204
|
+
const supportedReasoningEffortsRaw = getArray(entry["supportedReasoningEfforts"]);
|
|
205
|
+
if (!id || !model || !displayName || description === null || hidden === null || isDefault === null || !defaultReasoningEffort || !supportedReasoningEffortsRaw) {
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
208
|
+
const supportedReasoningEfforts = supportedReasoningEffortsRaw.map((candidate) => getString(candidate)).filter((candidate) => candidate !== null);
|
|
209
|
+
models.push({
|
|
210
|
+
id,
|
|
211
|
+
model,
|
|
212
|
+
displayName,
|
|
213
|
+
description,
|
|
214
|
+
hidden,
|
|
215
|
+
isDefault,
|
|
216
|
+
supportedReasoningEfforts,
|
|
217
|
+
defaultReasoningEffort
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
return {
|
|
221
|
+
data: models,
|
|
222
|
+
nextCursor: getString(value["nextCursor"])
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function parseThreadStartResponse(value) {
|
|
226
|
+
if (!isRecord(value) || !isRecord(value["thread"])) {
|
|
227
|
+
throw createCodexProtocolError("Invalid thread/start response from Codex", {
|
|
228
|
+
method: "thread/start"
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
const threadId = getString(value["thread"]["id"]);
|
|
232
|
+
if (!threadId) {
|
|
233
|
+
throw createCodexProtocolError("Codex thread/start response is missing a thread ID", {
|
|
234
|
+
method: "thread/start"
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
return {
|
|
238
|
+
thread: {
|
|
239
|
+
id: threadId
|
|
240
|
+
}
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
function parseTurnStartResponse(value) {
|
|
244
|
+
if (!isRecord(value) || !isRecord(value["turn"])) {
|
|
245
|
+
throw createCodexProtocolError("Invalid turn/start response from Codex", {
|
|
246
|
+
method: "turn/start"
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
const turnId = getString(value["turn"]["id"]);
|
|
250
|
+
if (!turnId) {
|
|
251
|
+
throw createCodexProtocolError("Codex turn/start response is missing a turn ID", {
|
|
252
|
+
method: "turn/start"
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
return {
|
|
256
|
+
turn: {
|
|
257
|
+
id: turnId
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
function parseRateLimitEntry(value) {
|
|
262
|
+
if (!isRecord(value)) {
|
|
263
|
+
return null;
|
|
264
|
+
}
|
|
265
|
+
const usedPercent = getNumber(value["usedPercent"]);
|
|
266
|
+
if (usedPercent === null) {
|
|
267
|
+
return null;
|
|
268
|
+
}
|
|
269
|
+
const normalizedUsedPercent = Math.max(0, Math.min(100, usedPercent));
|
|
270
|
+
const windowMinutes = getNumber(value["windowDurationMins"]) ?? getNumber(value["windowMinutes"]);
|
|
271
|
+
const limitId = getString(value["limitId"]);
|
|
272
|
+
const limitName = getString(value["limitName"]);
|
|
273
|
+
const resetsAt = getString(value["resetsAt"]);
|
|
274
|
+
return {
|
|
275
|
+
source: "chatgpt-login",
|
|
276
|
+
usedPercent: normalizedUsedPercent,
|
|
277
|
+
exceeded: normalizedUsedPercent >= 100,
|
|
278
|
+
...limitId ? { limitId } : {},
|
|
279
|
+
...limitName ? { limitName } : {},
|
|
280
|
+
...resetsAt ? { resetsAt } : {},
|
|
281
|
+
...windowMinutes !== null ? { windowMinutes } : {}
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
function collectRateLimitEntries(value) {
|
|
285
|
+
const entries = [];
|
|
286
|
+
const directEntry = parseRateLimitEntry(value);
|
|
287
|
+
if (directEntry) {
|
|
288
|
+
entries.push(directEntry);
|
|
289
|
+
}
|
|
290
|
+
if (!isRecord(value)) {
|
|
291
|
+
return entries;
|
|
292
|
+
}
|
|
293
|
+
const rateLimits = getArray(value["rateLimits"]);
|
|
294
|
+
if (rateLimits) {
|
|
295
|
+
for (const candidate of rateLimits) {
|
|
296
|
+
const entry = parseRateLimitEntry(candidate);
|
|
297
|
+
if (entry) {
|
|
298
|
+
entries.push(entry);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
const rateLimitsByLimitId = isRecord(value["rateLimitsByLimitId"]) ? value["rateLimitsByLimitId"] : null;
|
|
303
|
+
if (rateLimitsByLimitId) {
|
|
304
|
+
for (const candidate of Object.values(rateLimitsByLimitId)) {
|
|
305
|
+
if (Array.isArray(candidate)) {
|
|
306
|
+
for (const item of candidate) {
|
|
307
|
+
const entry2 = parseRateLimitEntry(item);
|
|
308
|
+
if (entry2) {
|
|
309
|
+
entries.push(entry2);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
continue;
|
|
313
|
+
}
|
|
314
|
+
const entry = parseRateLimitEntry(candidate);
|
|
315
|
+
if (entry) {
|
|
316
|
+
entries.push(entry);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return entries;
|
|
321
|
+
}
|
|
322
|
+
function pickPrimaryRateLimitSnapshot(value) {
|
|
323
|
+
const entries = collectRateLimitEntries(value);
|
|
324
|
+
if (entries.length === 0) {
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
entries.sort((left, right) => {
|
|
328
|
+
if (left.exceeded !== right.exceeded) {
|
|
329
|
+
return left.exceeded ? -1 : 1;
|
|
330
|
+
}
|
|
331
|
+
if (left.usedPercent !== right.usedPercent) {
|
|
332
|
+
return right.usedPercent - left.usedPercent;
|
|
333
|
+
}
|
|
334
|
+
const leftReset = left.resetsAt ? Date.parse(left.resetsAt) : Number.POSITIVE_INFINITY;
|
|
335
|
+
const rightReset = right.resetsAt ? Date.parse(right.resetsAt) : Number.POSITIVE_INFINITY;
|
|
336
|
+
return leftReset - rightReset;
|
|
337
|
+
});
|
|
338
|
+
return entries[0] ?? null;
|
|
339
|
+
}
|
|
340
|
+
function parseCodexErrorDetails(value) {
|
|
341
|
+
if (!isRecord(value)) {
|
|
342
|
+
return null;
|
|
343
|
+
}
|
|
344
|
+
const codexErrorInfo = isRecord(value["codexErrorInfo"]) ? value["codexErrorInfo"] : isRecord(value["codex_error_info"]) ? value["codex_error_info"] : null;
|
|
345
|
+
return {
|
|
346
|
+
message: getString(value["message"]),
|
|
347
|
+
additionalDetails: getString(value["additionalDetails"]) ?? getString(value["additional_details"]),
|
|
348
|
+
errorInfoKeys: codexErrorInfo ? Object.keys(codexErrorInfo) : []
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
function isUsageLimitError(details) {
|
|
352
|
+
if (!details) {
|
|
353
|
+
return false;
|
|
354
|
+
}
|
|
355
|
+
const normalizedInfoKeys = details.errorInfoKeys.map((key) => key.toLowerCase());
|
|
356
|
+
if (normalizedInfoKeys.includes("usagelimitexceeded")) {
|
|
357
|
+
return true;
|
|
358
|
+
}
|
|
359
|
+
const combinedText = `${details.message ?? ""} ${details.additionalDetails ?? ""}`.toLowerCase();
|
|
360
|
+
return combinedText.includes("usage limit") || combinedText.includes("rate limit") || combinedText.includes("quota exceeded") || combinedText.includes("purchase more credits");
|
|
361
|
+
}
|
|
362
|
+
function buildUsageLimitSnapshot(existing) {
|
|
363
|
+
if (!existing) {
|
|
364
|
+
return {
|
|
365
|
+
source: "chatgpt-login",
|
|
366
|
+
usedPercent: 100,
|
|
367
|
+
exceeded: true
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
return {
|
|
371
|
+
...existing,
|
|
372
|
+
usedPercent: Math.max(100, existing.usedPercent),
|
|
373
|
+
exceeded: true
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
function toChatGPTUsageLimitError(details, modelId, snapshot) {
|
|
377
|
+
const message = details.message ?? "You have reached your ChatGPT usage limit.";
|
|
378
|
+
return new import_DextoRuntimeError.DextoRuntimeError(
|
|
379
|
+
import_error_codes.LLMErrorCode.RATE_LIMIT_EXCEEDED,
|
|
380
|
+
import_types.ErrorScope.LLM,
|
|
381
|
+
import_types.ErrorType.RATE_LIMIT,
|
|
382
|
+
message,
|
|
383
|
+
{
|
|
384
|
+
provider: "openai-compatible",
|
|
385
|
+
model: modelId,
|
|
386
|
+
source: "chatgpt-login",
|
|
387
|
+
...snapshot?.limitId ? { limitId: snapshot.limitId } : {},
|
|
388
|
+
...snapshot?.limitName ? { limitName: snapshot.limitName } : {},
|
|
389
|
+
...snapshot?.resetsAt ? { resetsAt: snapshot.resetsAt } : {},
|
|
390
|
+
...snapshot?.windowMinutes !== void 0 ? { windowMinutes: snapshot.windowMinutes } : {},
|
|
391
|
+
usedPercent: snapshot?.usedPercent ?? 100,
|
|
392
|
+
additionalDetails: details.additionalDetails ?? void 0,
|
|
393
|
+
errorInfoKeys: details.errorInfoKeys
|
|
394
|
+
},
|
|
395
|
+
[
|
|
396
|
+
"Wait for your ChatGPT usage window to reset, or switch this session to an OpenAI API key.",
|
|
397
|
+
"Use `/model` to move this session onto your API key-backed OpenAI provider."
|
|
398
|
+
]
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
function createUsage() {
|
|
402
|
+
return {
|
|
403
|
+
inputTokens: void 0,
|
|
404
|
+
outputTokens: void 0,
|
|
405
|
+
totalTokens: void 0
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
function promptMessageToTranscript(message) {
|
|
409
|
+
if (message.role === "system") {
|
|
410
|
+
return `System:
|
|
411
|
+
${message.content}`;
|
|
412
|
+
}
|
|
413
|
+
const parts = message.role === "tool" ? message.content.map((part) => {
|
|
414
|
+
const output = (0, import_safe_stringify.safeStringify)(part.output);
|
|
415
|
+
return `[Tool Result: ${part.toolName}#${part.toolCallId}]
|
|
416
|
+
${output}`;
|
|
417
|
+
}) : message.content.map((part) => {
|
|
418
|
+
switch (part.type) {
|
|
419
|
+
case "text":
|
|
420
|
+
return part.text;
|
|
421
|
+
case "reasoning":
|
|
422
|
+
return `[Reasoning]
|
|
423
|
+
${part.text}`;
|
|
424
|
+
case "tool-call":
|
|
425
|
+
return `[Tool Call: ${part.toolName}#${part.toolCallId}]
|
|
426
|
+
${(0, import_safe_stringify.safeStringify)(part.input)}`;
|
|
427
|
+
case "tool-result":
|
|
428
|
+
return `[Tool Result: ${part.toolName}#${part.toolCallId}]
|
|
429
|
+
${(0, import_safe_stringify.safeStringify)(part.output)}`;
|
|
430
|
+
case "file": {
|
|
431
|
+
const url = part.data instanceof URL ? part.data.toString() : typeof part.data === "string" && /^(https?:)?\/\//i.test(part.data) ? part.data : null;
|
|
432
|
+
const fileLabel = part.filename ?? part.mediaType;
|
|
433
|
+
if (url) {
|
|
434
|
+
return `[File: ${fileLabel}] ${url}`;
|
|
435
|
+
}
|
|
436
|
+
return `[File: ${fileLabel}] data omitted`;
|
|
437
|
+
}
|
|
438
|
+
default: {
|
|
439
|
+
const unknownType = isRecord(part) && typeof part["type"] === "string" ? part["type"] : "unknown";
|
|
440
|
+
return `[Unknown Prompt Part: ${unknownType}]`;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
const roleLabel = message.role === "user" ? "User" : message.role === "assistant" ? "Assistant" : "Tool";
|
|
445
|
+
return `${roleLabel}:
|
|
446
|
+
${parts.join("\n")}`;
|
|
447
|
+
}
|
|
448
|
+
function buildTranscript(prompt) {
|
|
449
|
+
return prompt.map((message) => promptMessageToTranscript(message)).join("\n\n");
|
|
450
|
+
}
|
|
451
|
+
function isFunctionTool(tool) {
|
|
452
|
+
return tool.type === "function";
|
|
453
|
+
}
|
|
454
|
+
function selectCodexFunctionTools(options) {
|
|
455
|
+
const functionTools = (options.tools ?? []).filter(isFunctionTool);
|
|
456
|
+
const toolChoice = options.toolChoice;
|
|
457
|
+
if (!toolChoice || toolChoice.type === "auto" || toolChoice.type === "required") {
|
|
458
|
+
return functionTools;
|
|
459
|
+
}
|
|
460
|
+
if (toolChoice.type === "none") {
|
|
461
|
+
return [];
|
|
462
|
+
}
|
|
463
|
+
return functionTools.filter((tool) => tool.name === toolChoice.toolName);
|
|
464
|
+
}
|
|
465
|
+
function buildDynamicTools(options) {
|
|
466
|
+
return selectCodexFunctionTools(options).map((tool) => ({
|
|
467
|
+
name: tool.name,
|
|
468
|
+
...tool.description ? { description: tool.description } : {},
|
|
469
|
+
inputSchema: tool.inputSchema
|
|
470
|
+
}));
|
|
471
|
+
}
|
|
472
|
+
function buildWarnings(options) {
|
|
473
|
+
const warnings = [];
|
|
474
|
+
const unsupportedSettings = [
|
|
475
|
+
"maxOutputTokens",
|
|
476
|
+
"temperature",
|
|
477
|
+
"stopSequences",
|
|
478
|
+
"topP",
|
|
479
|
+
"topK",
|
|
480
|
+
"presencePenalty",
|
|
481
|
+
"frequencyPenalty",
|
|
482
|
+
"seed"
|
|
483
|
+
];
|
|
484
|
+
for (const setting of unsupportedSettings) {
|
|
485
|
+
if (options[setting] !== void 0) {
|
|
486
|
+
warnings.push({
|
|
487
|
+
type: "unsupported-setting",
|
|
488
|
+
setting,
|
|
489
|
+
details: "Codex app-server manages this setting internally."
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
if (options.tools) {
|
|
494
|
+
for (const tool of options.tools) {
|
|
495
|
+
if (!isFunctionTool(tool)) {
|
|
496
|
+
warnings.push({
|
|
497
|
+
type: "unsupported-tool",
|
|
498
|
+
tool,
|
|
499
|
+
details: "Codex app-server integration only supports host function tools."
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
return warnings;
|
|
505
|
+
}
|
|
506
|
+
function getRequestedReasoningEffort(options) {
|
|
507
|
+
if (!isRecord(options.providerOptions)) {
|
|
508
|
+
return null;
|
|
509
|
+
}
|
|
510
|
+
const openAiCompatibleOptions = options.providerOptions["openaiCompatible"];
|
|
511
|
+
if (!isRecord(openAiCompatibleOptions)) {
|
|
512
|
+
return null;
|
|
513
|
+
}
|
|
514
|
+
const reasoningEffort = getString(openAiCompatibleOptions["reasoningEffort"]);
|
|
515
|
+
return reasoningEffort ?? null;
|
|
516
|
+
}
|
|
517
|
+
function buildDeveloperInstructions(options, dynamicTools) {
|
|
518
|
+
const instructions = [CODEX_DEVELOPER_INSTRUCTIONS];
|
|
519
|
+
if (dynamicTools.length > 0) {
|
|
520
|
+
instructions.push(
|
|
521
|
+
"Host-provided dynamic tools are available for this turn. Prefer them over guessing."
|
|
522
|
+
);
|
|
523
|
+
if (options.toolChoice?.type === "required") {
|
|
524
|
+
instructions.push(
|
|
525
|
+
"You must call at least one host-provided dynamic tool before your final answer."
|
|
526
|
+
);
|
|
527
|
+
}
|
|
528
|
+
} else {
|
|
529
|
+
instructions.push("No host-provided dynamic tools are available for this turn.");
|
|
530
|
+
}
|
|
531
|
+
if (options.toolChoice?.type === "tool") {
|
|
532
|
+
instructions.push(
|
|
533
|
+
`If you need a tool, use only the host tool named "${options.toolChoice.toolName}".`
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
if (options.responseFormat?.type === "json" && !options.responseFormat.schema) {
|
|
537
|
+
instructions.push("Return valid JSON only. Do not wrap it in Markdown fences.");
|
|
538
|
+
}
|
|
539
|
+
return instructions.join(" ");
|
|
540
|
+
}
|
|
541
|
+
function toCodexFailureMessage(error, modelId) {
|
|
542
|
+
if (error instanceof import_DextoRuntimeError.DextoRuntimeError) {
|
|
543
|
+
return error;
|
|
544
|
+
}
|
|
545
|
+
const normalized = normalizeError(error);
|
|
546
|
+
if (normalized.message.includes("spawn codex ENOENT")) {
|
|
547
|
+
return import_errors.LLMError.missingConfig(
|
|
548
|
+
"openai-compatible",
|
|
549
|
+
"the Codex CLI on PATH (install Codex to use ChatGPT Login in Dexto)"
|
|
550
|
+
);
|
|
551
|
+
}
|
|
552
|
+
const fallbackDetails = {
|
|
553
|
+
message: normalized.message,
|
|
554
|
+
additionalDetails: null,
|
|
555
|
+
errorInfoKeys: []
|
|
556
|
+
};
|
|
557
|
+
if (isUsageLimitError(fallbackDetails)) {
|
|
558
|
+
return toChatGPTUsageLimitError(fallbackDetails, modelId, null);
|
|
559
|
+
}
|
|
560
|
+
return import_errors.LLMError.generationFailed(normalized.message, "openai-compatible", modelId);
|
|
561
|
+
}
|
|
562
|
+
function enforceAuthMode(authMode, accountState) {
|
|
563
|
+
if (!accountState.account) {
|
|
564
|
+
if (accountState.requiresOpenaiAuth) {
|
|
565
|
+
throw import_errors.LLMError.missingConfig(
|
|
566
|
+
"openai-compatible",
|
|
567
|
+
"Codex authentication (run `codex login` or re-run `dexto setup` and choose ChatGPT Login)"
|
|
568
|
+
);
|
|
569
|
+
}
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
if (authMode === "auto") {
|
|
573
|
+
return;
|
|
574
|
+
}
|
|
575
|
+
if (authMode === "chatgpt" && accountState.account.type !== "chatgpt") {
|
|
576
|
+
throw import_errors.LLMError.missingConfig(
|
|
577
|
+
"openai-compatible",
|
|
578
|
+
"a ChatGPT-backed Codex login (run `codex logout` and sign in with ChatGPT, or re-run `dexto setup`)"
|
|
579
|
+
);
|
|
580
|
+
}
|
|
581
|
+
if (authMode === "apikey" && accountState.account.type !== "apiKey") {
|
|
582
|
+
throw import_errors.LLMError.missingConfig(
|
|
583
|
+
"openai-compatible",
|
|
584
|
+
"an API key-backed Codex login (run `codex login --with-api-key` or re-run `dexto setup`)"
|
|
585
|
+
);
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
function extractErrorMessage(params) {
|
|
589
|
+
if (!isRecord(params) || !isRecord(params["error"])) {
|
|
590
|
+
return null;
|
|
591
|
+
}
|
|
592
|
+
return getString(params["error"]["message"]);
|
|
593
|
+
}
|
|
594
|
+
function stringifyToolInput(input) {
|
|
595
|
+
try {
|
|
596
|
+
const serialized = JSON.stringify(input ?? {});
|
|
597
|
+
return serialized === void 0 ? "{}" : serialized;
|
|
598
|
+
} catch {
|
|
599
|
+
return "{}";
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
function parseDynamicToolCallRequest(params) {
|
|
603
|
+
if (!isRecord(params)) {
|
|
604
|
+
return null;
|
|
605
|
+
}
|
|
606
|
+
const threadId = getString(params["threadId"]);
|
|
607
|
+
const turnId = getString(params["turnId"]);
|
|
608
|
+
const callId = getString(params["callId"]);
|
|
609
|
+
const toolName = getString(params["tool"]);
|
|
610
|
+
if (!threadId || !turnId || !callId || !toolName) {
|
|
611
|
+
return null;
|
|
612
|
+
}
|
|
613
|
+
return {
|
|
614
|
+
threadId,
|
|
615
|
+
turnId,
|
|
616
|
+
callId,
|
|
617
|
+
toolName,
|
|
618
|
+
input: stringifyToolInput(params["arguments"])
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
class CodexAppServerClient {
|
|
622
|
+
command;
|
|
623
|
+
cwd;
|
|
624
|
+
requestTimeoutMs;
|
|
625
|
+
clientInfo;
|
|
626
|
+
child = null;
|
|
627
|
+
reader = null;
|
|
628
|
+
nextId = 1;
|
|
629
|
+
pending = /* @__PURE__ */ new Map();
|
|
630
|
+
listeners = /* @__PURE__ */ new Set();
|
|
631
|
+
requestListeners = /* @__PURE__ */ new Set();
|
|
632
|
+
started = false;
|
|
633
|
+
closed = false;
|
|
634
|
+
constructor(options = {}) {
|
|
635
|
+
this.command = options.command ?? "codex";
|
|
636
|
+
this.cwd = options.cwd;
|
|
637
|
+
this.requestTimeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
|
|
638
|
+
this.clientInfo = options.clientInfo ?? DEFAULT_CLIENT_INFO;
|
|
639
|
+
}
|
|
640
|
+
static async create(options = {}) {
|
|
641
|
+
const client = new CodexAppServerClient(options);
|
|
642
|
+
try {
|
|
643
|
+
await client.start();
|
|
644
|
+
return client;
|
|
645
|
+
} catch (error) {
|
|
646
|
+
await client.close().catch(() => void 0);
|
|
647
|
+
throw error;
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
async close() {
|
|
651
|
+
if (this.closed) {
|
|
652
|
+
return;
|
|
653
|
+
}
|
|
654
|
+
this.closed = true;
|
|
655
|
+
this.started = false;
|
|
656
|
+
this.rejectPending(createCodexClientRuntimeError("Codex app-server client closed"));
|
|
657
|
+
this.listeners.clear();
|
|
658
|
+
this.requestListeners.clear();
|
|
659
|
+
if (this.reader) {
|
|
660
|
+
this.reader.close();
|
|
661
|
+
this.reader = null;
|
|
662
|
+
}
|
|
663
|
+
const child = this.child;
|
|
664
|
+
this.child = null;
|
|
665
|
+
if (!child || child.killed || child.exitCode !== null || child.signalCode !== null) {
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
await new Promise((resolve) => {
|
|
669
|
+
child.once("exit", () => resolve());
|
|
670
|
+
child.kill();
|
|
671
|
+
});
|
|
672
|
+
}
|
|
673
|
+
onNotification(listener) {
|
|
674
|
+
this.listeners.add(listener);
|
|
675
|
+
return () => {
|
|
676
|
+
this.listeners.delete(listener);
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
onServerRequest(listener) {
|
|
680
|
+
this.requestListeners.add(listener);
|
|
681
|
+
return () => {
|
|
682
|
+
this.requestListeners.delete(listener);
|
|
683
|
+
};
|
|
684
|
+
}
|
|
685
|
+
async readAccount(refreshToken = false) {
|
|
686
|
+
const result = await this.request("account/read", { refreshToken });
|
|
687
|
+
return parseReadAccountResponse(result);
|
|
688
|
+
}
|
|
689
|
+
async logout() {
|
|
690
|
+
await this.request("account/logout", void 0);
|
|
691
|
+
}
|
|
692
|
+
async startLogin(params) {
|
|
693
|
+
const result = await this.request("account/login/start", params);
|
|
694
|
+
return parseLoginResponse(result);
|
|
695
|
+
}
|
|
696
|
+
async waitForLoginCompleted(loginId, options = {}) {
|
|
697
|
+
const params = await this.waitForNotification(
|
|
698
|
+
"account/login/completed",
|
|
699
|
+
(candidate) => {
|
|
700
|
+
if (!isRecord(candidate)) {
|
|
701
|
+
return false;
|
|
702
|
+
}
|
|
703
|
+
const candidateLoginId = getString(candidate["loginId"]);
|
|
704
|
+
return candidateLoginId === loginId;
|
|
705
|
+
},
|
|
706
|
+
options
|
|
707
|
+
);
|
|
708
|
+
if (!isRecord(params)) {
|
|
709
|
+
throw createCodexProtocolError(
|
|
710
|
+
"Invalid account/login/completed notification from Codex",
|
|
711
|
+
{
|
|
712
|
+
method: "account/login/completed"
|
|
713
|
+
}
|
|
714
|
+
);
|
|
715
|
+
}
|
|
716
|
+
const success = getBoolean(params["success"]);
|
|
717
|
+
if (success === null) {
|
|
718
|
+
throw createCodexProtocolError("Codex login completion is missing success", {
|
|
719
|
+
method: "account/login/completed"
|
|
720
|
+
});
|
|
721
|
+
}
|
|
722
|
+
return {
|
|
723
|
+
loginId: getString(params["loginId"]),
|
|
724
|
+
success,
|
|
725
|
+
error: getString(params["error"])
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
async listModels() {
|
|
729
|
+
const models = [];
|
|
730
|
+
let cursor = null;
|
|
731
|
+
while (true) {
|
|
732
|
+
const result = await this.request("model/list", {
|
|
733
|
+
includeHidden: false,
|
|
734
|
+
...cursor ? { cursor } : {}
|
|
735
|
+
});
|
|
736
|
+
const parsed = parseModelListResponse(result);
|
|
737
|
+
models.push(...parsed.data);
|
|
738
|
+
if (!parsed.nextCursor) {
|
|
739
|
+
return models;
|
|
740
|
+
}
|
|
741
|
+
cursor = parsed.nextCursor;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
async readRateLimits() {
|
|
745
|
+
const result = await this.request("account/rateLimits/read", void 0);
|
|
746
|
+
return pickPrimaryRateLimitSnapshot(result);
|
|
747
|
+
}
|
|
748
|
+
async startEphemeralThread(params) {
|
|
749
|
+
const result = await this.request("thread/start", {
|
|
750
|
+
model: params.model,
|
|
751
|
+
...params.cwd ? { cwd: params.cwd } : {},
|
|
752
|
+
approvalPolicy: "untrusted",
|
|
753
|
+
sandbox: "read-only",
|
|
754
|
+
developerInstructions: params.developerInstructions ?? CODEX_DEVELOPER_INSTRUCTIONS,
|
|
755
|
+
...params.dynamicTools && params.dynamicTools.length > 0 ? { dynamicTools: params.dynamicTools } : {},
|
|
756
|
+
ephemeral: true,
|
|
757
|
+
experimentalRawEvents: false,
|
|
758
|
+
persistExtendedHistory: false
|
|
759
|
+
});
|
|
760
|
+
return parseThreadStartResponse(result);
|
|
761
|
+
}
|
|
762
|
+
async startTurn(params) {
|
|
763
|
+
const result = await this.request("turn/start", {
|
|
764
|
+
threadId: params.threadId,
|
|
765
|
+
model: params.model,
|
|
766
|
+
input: [
|
|
767
|
+
{
|
|
768
|
+
type: "text",
|
|
769
|
+
text: params.transcript,
|
|
770
|
+
text_elements: []
|
|
771
|
+
}
|
|
772
|
+
],
|
|
773
|
+
...params.reasoningEffort ? { effort: params.reasoningEffort } : {},
|
|
774
|
+
...params.outputSchema ? { outputSchema: params.outputSchema } : {}
|
|
775
|
+
});
|
|
776
|
+
return parseTurnStartResponse(result);
|
|
777
|
+
}
|
|
778
|
+
async waitForNotification(method, predicate, options = {}) {
|
|
779
|
+
if (this.closed) {
|
|
780
|
+
throw createCodexClientRuntimeError("Codex app-server client is closed");
|
|
781
|
+
}
|
|
782
|
+
const timeoutMs = options.timeoutMs ?? this.requestTimeoutMs;
|
|
783
|
+
return await new Promise((resolve, reject) => {
|
|
784
|
+
let timeout = null;
|
|
785
|
+
let offAbort = null;
|
|
786
|
+
const cleanup = (unsubscribe2) => {
|
|
787
|
+
unsubscribe2();
|
|
788
|
+
if (timeout) {
|
|
789
|
+
clearTimeout(timeout);
|
|
790
|
+
}
|
|
791
|
+
if (offAbort) {
|
|
792
|
+
offAbort();
|
|
793
|
+
}
|
|
794
|
+
};
|
|
795
|
+
const unsubscribe = this.onNotification((message) => {
|
|
796
|
+
if (message.method !== method) {
|
|
797
|
+
return;
|
|
798
|
+
}
|
|
799
|
+
if (predicate && !predicate(message.params)) {
|
|
800
|
+
return;
|
|
801
|
+
}
|
|
802
|
+
cleanup(unsubscribe);
|
|
803
|
+
resolve(message.params);
|
|
804
|
+
});
|
|
805
|
+
timeout = setTimeout(() => {
|
|
806
|
+
cleanup(unsubscribe);
|
|
807
|
+
reject(
|
|
808
|
+
createCodexClientRuntimeError(
|
|
809
|
+
`Timed out waiting for Codex notification: ${method}`,
|
|
810
|
+
{ method },
|
|
811
|
+
import_types.ErrorType.TIMEOUT
|
|
812
|
+
)
|
|
813
|
+
);
|
|
814
|
+
}, timeoutMs);
|
|
815
|
+
if (options.signal) {
|
|
816
|
+
const onAbort = () => {
|
|
817
|
+
cleanup(unsubscribe);
|
|
818
|
+
reject(
|
|
819
|
+
options.signal?.reason instanceof Error ? options.signal.reason : createCodexClientRuntimeError(
|
|
820
|
+
"Codex operation aborted",
|
|
821
|
+
{ method },
|
|
822
|
+
import_types.ErrorType.USER
|
|
823
|
+
)
|
|
824
|
+
);
|
|
825
|
+
};
|
|
826
|
+
if (options.signal.aborted) {
|
|
827
|
+
onAbort();
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
options.signal.addEventListener("abort", onAbort, { once: true });
|
|
831
|
+
offAbort = () => options.signal?.removeEventListener("abort", onAbort);
|
|
832
|
+
}
|
|
833
|
+
});
|
|
834
|
+
}
|
|
835
|
+
async start() {
|
|
836
|
+
if (this.started) {
|
|
837
|
+
return;
|
|
838
|
+
}
|
|
839
|
+
const child = (0, import_node_child_process.spawn)(this.command, ["app-server"], {
|
|
840
|
+
cwd: this.cwd,
|
|
841
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
842
|
+
});
|
|
843
|
+
this.child = child;
|
|
844
|
+
this.reader = (0, import_node_readline.createInterface)({
|
|
845
|
+
input: child.stdout,
|
|
846
|
+
crlfDelay: Infinity
|
|
847
|
+
});
|
|
848
|
+
const drainStderr = () => void 0;
|
|
849
|
+
child.stderr.on("data", drainStderr);
|
|
850
|
+
child.on("error", (error) => {
|
|
851
|
+
this.rejectPending(error);
|
|
852
|
+
});
|
|
853
|
+
child.on("exit", (code, signal) => {
|
|
854
|
+
const wasClosed = this.closed;
|
|
855
|
+
const error = createCodexClientExitedError({
|
|
856
|
+
...code !== null ? { code } : {},
|
|
857
|
+
...signal !== null ? { signal } : {}
|
|
858
|
+
});
|
|
859
|
+
child.stderr.off("data", drainStderr);
|
|
860
|
+
if (!wasClosed) {
|
|
861
|
+
this.publishNotification({
|
|
862
|
+
method: "codex/client-exited",
|
|
863
|
+
params: {
|
|
864
|
+
...code !== null ? { code } : {},
|
|
865
|
+
...signal !== null ? { signal } : {}
|
|
866
|
+
}
|
|
867
|
+
});
|
|
868
|
+
this.rejectPending(error);
|
|
869
|
+
}
|
|
870
|
+
});
|
|
871
|
+
this.reader.on("line", (line) => {
|
|
872
|
+
this.handleLine(line);
|
|
873
|
+
});
|
|
874
|
+
await this.request("initialize", {
|
|
875
|
+
clientInfo: this.clientInfo,
|
|
876
|
+
capabilities: {
|
|
877
|
+
experimentalApi: true
|
|
878
|
+
}
|
|
879
|
+
});
|
|
880
|
+
this.notify("initialized", {});
|
|
881
|
+
this.started = true;
|
|
882
|
+
}
|
|
883
|
+
handleLine(line) {
|
|
884
|
+
if (!line.trim()) {
|
|
885
|
+
return;
|
|
886
|
+
}
|
|
887
|
+
let payload;
|
|
888
|
+
try {
|
|
889
|
+
payload = JSON.parse(line);
|
|
890
|
+
} catch {
|
|
891
|
+
return;
|
|
892
|
+
}
|
|
893
|
+
if (!isRecord(payload)) {
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
const id = payload["id"];
|
|
897
|
+
const method = getString(payload["method"]);
|
|
898
|
+
if ((typeof id === "number" || typeof id === "string") && method) {
|
|
899
|
+
const request = {
|
|
900
|
+
id,
|
|
901
|
+
method,
|
|
902
|
+
...payload["params"] !== void 0 ? { params: payload["params"] } : {}
|
|
903
|
+
};
|
|
904
|
+
for (const listener of this.requestListeners) {
|
|
905
|
+
listener(request);
|
|
906
|
+
}
|
|
907
|
+
return;
|
|
908
|
+
}
|
|
909
|
+
if (typeof id === "number") {
|
|
910
|
+
const pending = this.pending.get(id);
|
|
911
|
+
if (!pending) {
|
|
912
|
+
return;
|
|
913
|
+
}
|
|
914
|
+
this.pending.delete(id);
|
|
915
|
+
clearTimeout(pending.timeout);
|
|
916
|
+
if (isRecord(payload["error"])) {
|
|
917
|
+
const message = getString(payload["error"]["message"]) ?? "Codex JSON-RPC request failed";
|
|
918
|
+
pending.reject(
|
|
919
|
+
createCodexClientRuntimeError(message, { id }, import_types.ErrorType.THIRD_PARTY)
|
|
920
|
+
);
|
|
921
|
+
return;
|
|
922
|
+
}
|
|
923
|
+
pending.resolve(payload["result"]);
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
if (!method) {
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
const notification = {
|
|
930
|
+
method,
|
|
931
|
+
...payload["params"] !== void 0 ? { params: payload["params"] } : {}
|
|
932
|
+
};
|
|
933
|
+
this.publishNotification(notification);
|
|
934
|
+
}
|
|
935
|
+
publishNotification(notification) {
|
|
936
|
+
for (const listener of this.listeners) {
|
|
937
|
+
listener(notification);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
notify(method, params) {
|
|
941
|
+
this.write({ method, params });
|
|
942
|
+
}
|
|
943
|
+
async request(method, params) {
|
|
944
|
+
if (this.closed) {
|
|
945
|
+
throw createCodexClientRuntimeError("Codex app-server client is closed");
|
|
946
|
+
}
|
|
947
|
+
const id = this.nextId++;
|
|
948
|
+
return await new Promise((resolve, reject) => {
|
|
949
|
+
const timeout = setTimeout(() => {
|
|
950
|
+
this.pending.delete(id);
|
|
951
|
+
reject(
|
|
952
|
+
createCodexClientRuntimeError(
|
|
953
|
+
`Codex request timed out: ${method}`,
|
|
954
|
+
{ method, id },
|
|
955
|
+
import_types.ErrorType.TIMEOUT
|
|
956
|
+
)
|
|
957
|
+
);
|
|
958
|
+
}, this.requestTimeoutMs);
|
|
959
|
+
this.pending.set(id, {
|
|
960
|
+
resolve,
|
|
961
|
+
reject,
|
|
962
|
+
timeout
|
|
963
|
+
});
|
|
964
|
+
try {
|
|
965
|
+
this.write({
|
|
966
|
+
method,
|
|
967
|
+
id,
|
|
968
|
+
...params !== void 0 ? { params } : {}
|
|
969
|
+
});
|
|
970
|
+
} catch (error) {
|
|
971
|
+
clearTimeout(timeout);
|
|
972
|
+
this.pending.delete(id);
|
|
973
|
+
reject(error);
|
|
974
|
+
}
|
|
975
|
+
});
|
|
976
|
+
}
|
|
977
|
+
respondToServerRequest(id, result) {
|
|
978
|
+
this.write({
|
|
979
|
+
id,
|
|
980
|
+
result
|
|
981
|
+
});
|
|
982
|
+
}
|
|
983
|
+
rejectServerRequest(id, message, code = -32601) {
|
|
984
|
+
this.write({
|
|
985
|
+
id,
|
|
986
|
+
error: {
|
|
987
|
+
code,
|
|
988
|
+
message
|
|
989
|
+
}
|
|
990
|
+
});
|
|
991
|
+
}
|
|
992
|
+
write(payload) {
|
|
993
|
+
if (!this.child?.stdin.writable) {
|
|
994
|
+
throw createCodexClientRuntimeError("Codex app-server stdin is not writable");
|
|
995
|
+
}
|
|
996
|
+
this.child.stdin.write(`${JSON.stringify(payload)}
|
|
997
|
+
`);
|
|
998
|
+
}
|
|
999
|
+
rejectPending(error) {
|
|
1000
|
+
const normalized = normalizeError(error);
|
|
1001
|
+
for (const [id, pending] of this.pending.entries()) {
|
|
1002
|
+
clearTimeout(pending.timeout);
|
|
1003
|
+
pending.reject(normalized);
|
|
1004
|
+
this.pending.delete(id);
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
function createCodexLanguageModel(options) {
|
|
1009
|
+
const parsedBaseURL = (0, import_codex_base_url.parseCodexBaseURL)(options.baseURL);
|
|
1010
|
+
const authMode = parsedBaseURL?.authMode ?? "auto";
|
|
1011
|
+
async function executeTurn(callOptions) {
|
|
1012
|
+
const warnings = buildWarnings(callOptions);
|
|
1013
|
+
const transcript = buildTranscript(callOptions.prompt);
|
|
1014
|
+
const reasoningEffort = getRequestedReasoningEffort(callOptions);
|
|
1015
|
+
const dynamicTools = buildDynamicTools(callOptions);
|
|
1016
|
+
const developerInstructions = buildDeveloperInstructions(callOptions, dynamicTools);
|
|
1017
|
+
const outputSchema = callOptions.responseFormat?.type === "json" ? callOptions.responseFormat.schema : void 0;
|
|
1018
|
+
let latestRateLimitSnapshot = null;
|
|
1019
|
+
const emitRateLimitStatus = (snapshot) => {
|
|
1020
|
+
if (!snapshot) {
|
|
1021
|
+
return;
|
|
1022
|
+
}
|
|
1023
|
+
latestRateLimitSnapshot = snapshot;
|
|
1024
|
+
options.onRateLimitStatus?.(snapshot);
|
|
1025
|
+
};
|
|
1026
|
+
let client = null;
|
|
1027
|
+
try {
|
|
1028
|
+
client = await CodexAppServerClient.create({
|
|
1029
|
+
...options.cwd ? { cwd: options.cwd } : {}
|
|
1030
|
+
});
|
|
1031
|
+
const activeClient = client;
|
|
1032
|
+
const account = await activeClient.readAccount(false);
|
|
1033
|
+
enforceAuthMode(authMode, account);
|
|
1034
|
+
const shouldTrackRateLimits = account.account?.type === "chatgpt" || authMode === "chatgpt";
|
|
1035
|
+
if (shouldTrackRateLimits) {
|
|
1036
|
+
void activeClient.readRateLimits().then((snapshot) => emitRateLimitStatus(snapshot)).catch(() => void 0);
|
|
1037
|
+
}
|
|
1038
|
+
const thread = await activeClient.startEphemeralThread({
|
|
1039
|
+
model: options.modelId,
|
|
1040
|
+
...options.cwd ? { cwd: options.cwd } : {},
|
|
1041
|
+
developerInstructions,
|
|
1042
|
+
dynamicTools
|
|
1043
|
+
});
|
|
1044
|
+
const turn = await activeClient.startTurn({
|
|
1045
|
+
threadId: thread.thread.id,
|
|
1046
|
+
model: options.modelId,
|
|
1047
|
+
transcript,
|
|
1048
|
+
...reasoningEffort ? { reasoningEffort } : {},
|
|
1049
|
+
...outputSchema ? { outputSchema } : {}
|
|
1050
|
+
});
|
|
1051
|
+
const stream = new ReadableStream({
|
|
1052
|
+
start(controller) {
|
|
1053
|
+
let closed = false;
|
|
1054
|
+
let streamStarted = false;
|
|
1055
|
+
let textStarted = false;
|
|
1056
|
+
let textEnded = false;
|
|
1057
|
+
let emittedText = "";
|
|
1058
|
+
let offAbort = null;
|
|
1059
|
+
const textPartId = `codex-text-${turn.turn.id}`;
|
|
1060
|
+
const cleanup = () => {
|
|
1061
|
+
if (closed) {
|
|
1062
|
+
return;
|
|
1063
|
+
}
|
|
1064
|
+
closed = true;
|
|
1065
|
+
unsubscribeNotifications();
|
|
1066
|
+
unsubscribeRequests();
|
|
1067
|
+
if (offAbort) {
|
|
1068
|
+
offAbort();
|
|
1069
|
+
offAbort = null;
|
|
1070
|
+
}
|
|
1071
|
+
void activeClient.close();
|
|
1072
|
+
};
|
|
1073
|
+
const ensureStreamStarted = () => {
|
|
1074
|
+
if (streamStarted) {
|
|
1075
|
+
return;
|
|
1076
|
+
}
|
|
1077
|
+
streamStarted = true;
|
|
1078
|
+
controller.enqueue({
|
|
1079
|
+
type: "stream-start",
|
|
1080
|
+
warnings
|
|
1081
|
+
});
|
|
1082
|
+
};
|
|
1083
|
+
const endText = () => {
|
|
1084
|
+
if (textStarted && !textEnded) {
|
|
1085
|
+
controller.enqueue({
|
|
1086
|
+
type: "text-end",
|
|
1087
|
+
id: textPartId
|
|
1088
|
+
});
|
|
1089
|
+
textEnded = true;
|
|
1090
|
+
}
|
|
1091
|
+
};
|
|
1092
|
+
const finishStream = (finishReason) => {
|
|
1093
|
+
ensureStreamStarted();
|
|
1094
|
+
endText();
|
|
1095
|
+
controller.enqueue({
|
|
1096
|
+
type: "finish",
|
|
1097
|
+
finishReason,
|
|
1098
|
+
usage: createUsage()
|
|
1099
|
+
});
|
|
1100
|
+
controller.close();
|
|
1101
|
+
cleanup();
|
|
1102
|
+
};
|
|
1103
|
+
const failStream = (error) => {
|
|
1104
|
+
if (closed) {
|
|
1105
|
+
return;
|
|
1106
|
+
}
|
|
1107
|
+
controller.enqueue({
|
|
1108
|
+
type: "error",
|
|
1109
|
+
error
|
|
1110
|
+
});
|
|
1111
|
+
cleanup();
|
|
1112
|
+
controller.close();
|
|
1113
|
+
};
|
|
1114
|
+
const failWithTurnError = (details, fallbackMessage) => {
|
|
1115
|
+
if (details && isUsageLimitError(details)) {
|
|
1116
|
+
const snapshot = buildUsageLimitSnapshot(latestRateLimitSnapshot);
|
|
1117
|
+
emitRateLimitStatus(snapshot);
|
|
1118
|
+
failStream(
|
|
1119
|
+
toChatGPTUsageLimitError(details, options.modelId, snapshot)
|
|
1120
|
+
);
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
failStream(
|
|
1124
|
+
import_errors.LLMError.generationFailed(
|
|
1125
|
+
details?.message ?? fallbackMessage,
|
|
1126
|
+
"openai-compatible",
|
|
1127
|
+
options.modelId
|
|
1128
|
+
)
|
|
1129
|
+
);
|
|
1130
|
+
};
|
|
1131
|
+
const unsubscribeNotifications = activeClient.onNotification((message) => {
|
|
1132
|
+
if (message.method === "codex/client-exited") {
|
|
1133
|
+
const params = isRecord(message.params) ? message.params : {};
|
|
1134
|
+
failStream(
|
|
1135
|
+
createCodexClientExitedError({
|
|
1136
|
+
...getNumber(params["code"]) !== null ? { code: getNumber(params["code"]) ?? void 0 } : {},
|
|
1137
|
+
...getString(params["signal"]) !== null ? { signal: getString(params["signal"]) ?? void 0 } : {}
|
|
1138
|
+
})
|
|
1139
|
+
);
|
|
1140
|
+
return;
|
|
1141
|
+
}
|
|
1142
|
+
if (message.method === "account/rateLimits/updated") {
|
|
1143
|
+
emitRateLimitStatus(pickPrimaryRateLimitSnapshot(message.params));
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
if (message.method === "item/agentMessage/delta") {
|
|
1147
|
+
if (!isRecord(message.params)) {
|
|
1148
|
+
return;
|
|
1149
|
+
}
|
|
1150
|
+
const threadId = getString(message.params["threadId"]);
|
|
1151
|
+
const turnId = getString(message.params["turnId"]);
|
|
1152
|
+
const delta = getString(message.params["delta"]);
|
|
1153
|
+
if (threadId !== thread.thread.id || turnId !== turn.turn.id || delta === null) {
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
if (!textStarted) {
|
|
1157
|
+
ensureStreamStarted();
|
|
1158
|
+
controller.enqueue({
|
|
1159
|
+
type: "text-start",
|
|
1160
|
+
id: textPartId
|
|
1161
|
+
});
|
|
1162
|
+
textStarted = true;
|
|
1163
|
+
}
|
|
1164
|
+
emittedText += delta;
|
|
1165
|
+
controller.enqueue({
|
|
1166
|
+
type: "text-delta",
|
|
1167
|
+
id: textPartId,
|
|
1168
|
+
delta
|
|
1169
|
+
});
|
|
1170
|
+
return;
|
|
1171
|
+
}
|
|
1172
|
+
if (message.method === "item/completed") {
|
|
1173
|
+
if (!isRecord(message.params)) {
|
|
1174
|
+
return;
|
|
1175
|
+
}
|
|
1176
|
+
const threadId = getString(message.params["threadId"]);
|
|
1177
|
+
const turnId = getString(message.params["turnId"]);
|
|
1178
|
+
const item = isRecord(message.params["item"]) ? message.params["item"] : null;
|
|
1179
|
+
if (threadId !== thread.thread.id || turnId !== turn.turn.id || !item) {
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
if (item["type"] !== "agentMessage") {
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
const text = getString(item["text"]);
|
|
1186
|
+
if (!text) {
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
const missingText = text.startsWith(emittedText) ? text.slice(emittedText.length) : text;
|
|
1190
|
+
if (!textStarted) {
|
|
1191
|
+
ensureStreamStarted();
|
|
1192
|
+
controller.enqueue({
|
|
1193
|
+
type: "text-start",
|
|
1194
|
+
id: textPartId
|
|
1195
|
+
});
|
|
1196
|
+
textStarted = true;
|
|
1197
|
+
}
|
|
1198
|
+
if (missingText) {
|
|
1199
|
+
emittedText += missingText;
|
|
1200
|
+
controller.enqueue({
|
|
1201
|
+
type: "text-delta",
|
|
1202
|
+
id: textPartId,
|
|
1203
|
+
delta: missingText
|
|
1204
|
+
});
|
|
1205
|
+
}
|
|
1206
|
+
return;
|
|
1207
|
+
}
|
|
1208
|
+
if (message.method === "error") {
|
|
1209
|
+
if (!isRecord(message.params)) {
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
const willRetry = getBoolean(message.params["willRetry"]);
|
|
1213
|
+
if (willRetry === true) {
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
const threadId = getString(message.params["threadId"]);
|
|
1217
|
+
const turnId = getString(message.params["turnId"]);
|
|
1218
|
+
if (threadId !== thread.thread.id || turnId !== turn.turn.id) {
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
const details = parseCodexErrorDetails(
|
|
1222
|
+
isRecord(message.params["error"]) ? message.params["error"] : message.params
|
|
1223
|
+
);
|
|
1224
|
+
failWithTurnError(
|
|
1225
|
+
details,
|
|
1226
|
+
extractErrorMessage(message.params) ?? "Codex turn failed"
|
|
1227
|
+
);
|
|
1228
|
+
return;
|
|
1229
|
+
}
|
|
1230
|
+
if (message.method === "turn/completed") {
|
|
1231
|
+
if (!isRecord(message.params)) {
|
|
1232
|
+
return;
|
|
1233
|
+
}
|
|
1234
|
+
const threadId = getString(message.params["threadId"]);
|
|
1235
|
+
const turnInfo = isRecord(message.params["turn"]) ? message.params["turn"] : null;
|
|
1236
|
+
const turnId = turnInfo ? getString(turnInfo["id"]) : null;
|
|
1237
|
+
const status = turnInfo ? getString(turnInfo["status"]) : null;
|
|
1238
|
+
if (threadId !== thread.thread.id || turnId !== turn.turn.id || status === null) {
|
|
1239
|
+
return;
|
|
1240
|
+
}
|
|
1241
|
+
if (status === "failed") {
|
|
1242
|
+
const details = turnInfo && isRecord(turnInfo["error"]) ? parseCodexErrorDetails(turnInfo["error"]) : null;
|
|
1243
|
+
failWithTurnError(details, "Codex turn failed");
|
|
1244
|
+
return;
|
|
1245
|
+
}
|
|
1246
|
+
finishStream(status === "completed" ? "stop" : "other");
|
|
1247
|
+
}
|
|
1248
|
+
});
|
|
1249
|
+
const unsubscribeRequests = activeClient.onServerRequest((request) => {
|
|
1250
|
+
if (request.method !== "item/tool/call") {
|
|
1251
|
+
activeClient.rejectServerRequest(
|
|
1252
|
+
request.id,
|
|
1253
|
+
`Codex request "${request.method}" is not supported because Dexto executes tools and approvals itself.`
|
|
1254
|
+
);
|
|
1255
|
+
return;
|
|
1256
|
+
}
|
|
1257
|
+
const toolCall = parseDynamicToolCallRequest(request.params);
|
|
1258
|
+
if (!toolCall || toolCall.threadId !== thread.thread.id || toolCall.turnId !== turn.turn.id) {
|
|
1259
|
+
activeClient.rejectServerRequest(
|
|
1260
|
+
request.id,
|
|
1261
|
+
"Received an invalid Codex dynamic tool call payload."
|
|
1262
|
+
);
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
ensureStreamStarted();
|
|
1266
|
+
endText();
|
|
1267
|
+
controller.enqueue({
|
|
1268
|
+
type: "tool-input-start",
|
|
1269
|
+
id: toolCall.callId,
|
|
1270
|
+
toolName: toolCall.toolName
|
|
1271
|
+
});
|
|
1272
|
+
controller.enqueue({
|
|
1273
|
+
type: "tool-input-delta",
|
|
1274
|
+
id: toolCall.callId,
|
|
1275
|
+
delta: toolCall.input
|
|
1276
|
+
});
|
|
1277
|
+
controller.enqueue({
|
|
1278
|
+
type: "tool-input-end",
|
|
1279
|
+
id: toolCall.callId
|
|
1280
|
+
});
|
|
1281
|
+
controller.enqueue({
|
|
1282
|
+
type: "tool-call",
|
|
1283
|
+
toolCallId: toolCall.callId,
|
|
1284
|
+
toolName: toolCall.toolName,
|
|
1285
|
+
input: toolCall.input
|
|
1286
|
+
});
|
|
1287
|
+
finishStream("tool-calls");
|
|
1288
|
+
});
|
|
1289
|
+
if (callOptions.abortSignal) {
|
|
1290
|
+
const onAbort = () => {
|
|
1291
|
+
failStream(
|
|
1292
|
+
callOptions.abortSignal?.reason instanceof Error ? callOptions.abortSignal.reason : createCodexClientRuntimeError(
|
|
1293
|
+
"Codex generation aborted",
|
|
1294
|
+
{ modelId: options.modelId },
|
|
1295
|
+
import_types.ErrorType.USER
|
|
1296
|
+
)
|
|
1297
|
+
);
|
|
1298
|
+
};
|
|
1299
|
+
if (callOptions.abortSignal.aborted) {
|
|
1300
|
+
onAbort();
|
|
1301
|
+
return;
|
|
1302
|
+
}
|
|
1303
|
+
callOptions.abortSignal.addEventListener("abort", onAbort, { once: true });
|
|
1304
|
+
offAbort = () => callOptions.abortSignal?.removeEventListener("abort", onAbort);
|
|
1305
|
+
}
|
|
1306
|
+
},
|
|
1307
|
+
cancel() {
|
|
1308
|
+
void activeClient.close();
|
|
1309
|
+
}
|
|
1310
|
+
});
|
|
1311
|
+
return {
|
|
1312
|
+
stream,
|
|
1313
|
+
request: {
|
|
1314
|
+
body: {
|
|
1315
|
+
provider: "codex-app-server",
|
|
1316
|
+
model: options.modelId,
|
|
1317
|
+
transcript,
|
|
1318
|
+
dynamicTools: dynamicTools.map((tool) => tool.name)
|
|
1319
|
+
}
|
|
1320
|
+
}
|
|
1321
|
+
};
|
|
1322
|
+
} catch (error) {
|
|
1323
|
+
await client?.close().catch(() => void 0);
|
|
1324
|
+
const mappedError = toCodexFailureMessage(error, options.modelId);
|
|
1325
|
+
if (mappedError instanceof import_DextoRuntimeError.DextoRuntimeError && mappedError.code === import_error_codes.LLMErrorCode.RATE_LIMIT_EXCEEDED) {
|
|
1326
|
+
emitRateLimitStatus(buildUsageLimitSnapshot(latestRateLimitSnapshot));
|
|
1327
|
+
}
|
|
1328
|
+
throw mappedError;
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
return {
|
|
1332
|
+
specificationVersion: "v2",
|
|
1333
|
+
provider: "codex-app-server",
|
|
1334
|
+
modelId: options.modelId,
|
|
1335
|
+
supportedUrls: {},
|
|
1336
|
+
async doGenerate(callOptions) {
|
|
1337
|
+
const execution = await executeTurn(callOptions);
|
|
1338
|
+
const reader = execution.stream.getReader();
|
|
1339
|
+
const content = [];
|
|
1340
|
+
let text = "";
|
|
1341
|
+
let finishReason = "other";
|
|
1342
|
+
const flushText = () => {
|
|
1343
|
+
if (!text) {
|
|
1344
|
+
return;
|
|
1345
|
+
}
|
|
1346
|
+
content.push({
|
|
1347
|
+
type: "text",
|
|
1348
|
+
text
|
|
1349
|
+
});
|
|
1350
|
+
text = "";
|
|
1351
|
+
};
|
|
1352
|
+
while (true) {
|
|
1353
|
+
const { done, value } = await reader.read();
|
|
1354
|
+
if (done) {
|
|
1355
|
+
break;
|
|
1356
|
+
}
|
|
1357
|
+
if (value.type === "text-delta") {
|
|
1358
|
+
text += value.delta;
|
|
1359
|
+
} else if (value.type === "tool-call") {
|
|
1360
|
+
flushText();
|
|
1361
|
+
content.push({
|
|
1362
|
+
type: "tool-call",
|
|
1363
|
+
toolCallId: value.toolCallId,
|
|
1364
|
+
toolName: value.toolName,
|
|
1365
|
+
input: value.input
|
|
1366
|
+
});
|
|
1367
|
+
} else if (value.type === "error") {
|
|
1368
|
+
throw toCodexFailureMessage(value.error, options.modelId);
|
|
1369
|
+
} else if (value.type === "finish") {
|
|
1370
|
+
finishReason = value.finishReason;
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
flushText();
|
|
1374
|
+
return {
|
|
1375
|
+
content,
|
|
1376
|
+
finishReason,
|
|
1377
|
+
usage: createUsage(),
|
|
1378
|
+
warnings: buildWarnings(callOptions),
|
|
1379
|
+
request: execution.request
|
|
1380
|
+
};
|
|
1381
|
+
},
|
|
1382
|
+
async doStream(callOptions) {
|
|
1383
|
+
return await executeTurn(callOptions);
|
|
1384
|
+
}
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
1387
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1388
|
+
0 && (module.exports = {
|
|
1389
|
+
CodexAppServerClient,
|
|
1390
|
+
createCodexLanguageModel
|
|
1391
|
+
});
|