neoagent 2.3.1-beta.111 → 2.3.1-beta.114
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
|
|
1
|
+
d24ae5d149080cae1dd694a9d7d1cca7
|
|
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"42d3d75a56efe1a2e9902f52dc8006099c45d9
|
|
|
37
37
|
|
|
38
38
|
_flutter.loader.load({
|
|
39
39
|
serviceWorkerSettings: {
|
|
40
|
-
serviceWorkerVersion: "
|
|
40
|
+
serviceWorkerVersion: "2783772789" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
|
|
41
41
|
}
|
|
42
42
|
});
|
|
@@ -129338,7 +129338,7 @@ r===$&&A.b()
|
|
|
129338
129338
|
o.push(A.ii(p,A.iY(!1,new A.a3(B.tM,A.dT(new A.cI(B.he,new A.a5V(r,p),p),p,p),p),!1,B.I,!0),p,p,0,0,0,p))}r=!1
|
|
129339
129339
|
if(!s.ay)if(!s.ch){r=s.e
|
|
129340
129340
|
r===$&&A.b()
|
|
129341
|
-
r=B.b.t("
|
|
129341
|
+
r=B.b.t("mpb91fkt-6e44b2b").length!==0&&r.b}if(r){r=s.d
|
|
129342
129342
|
r===$&&A.b()
|
|
129343
129343
|
r=r.ag&&!r.V?84:0
|
|
129344
129344
|
q=s.e
|
|
@@ -134146,7 +134146,7 @@ $S:236}
|
|
|
134146
134146
|
A.Ys.prototype={}
|
|
134147
134147
|
A.Rr.prototype={
|
|
134148
134148
|
mT(a){var s=this
|
|
134149
|
-
if(B.b.t("
|
|
134149
|
+
if(B.b.t("mpb91fkt-6e44b2b").length===0||s.a!=null)return
|
|
134150
134150
|
s.A5()
|
|
134151
134151
|
s.a=A.q1(B.PP,new A.b58(s))},
|
|
134152
134152
|
A5(){var s=0,r=A.l(t.H),q,p=2,o=[],n=this,m,l,k,j,i,h,g,f
|
|
@@ -134164,7 +134164,7 @@ if(!t.f.b(k)){s=1
|
|
|
134164
134164
|
break}i=J.Z(k,"buildId")
|
|
134165
134165
|
h=i==null?null:B.b.t(J.r(i))
|
|
134166
134166
|
j=h==null?"":h
|
|
134167
|
-
if(J.bm(j)===0||J.d(j,"
|
|
134167
|
+
if(J.bm(j)===0||J.d(j,"mpb91fkt-6e44b2b")){s=1
|
|
134168
134168
|
break}n.b=!0
|
|
134169
134169
|
n.D()
|
|
134170
134170
|
p=2
|
|
@@ -134181,7 +134181,7 @@ case 2:return A.i(o.at(-1),r)}})
|
|
|
134181
134181
|
return A.k($async$A5,r)},
|
|
134182
134182
|
vb(){var s=0,r=A.l(t.H),q,p=2,o=[],n=this,m,l,k,j,i,h,g,f,e,d,c,b,a,a0,a1
|
|
134183
134183
|
var $async$vb=A.h(function(a2,a3){if(a2===1){o.push(a3)
|
|
134184
|
-
s=p}for(;;)switch(s){case 0:if(B.b.t("
|
|
134184
|
+
s=p}for(;;)switch(s){case 0:if(B.b.t("mpb91fkt-6e44b2b").length===0||n.c){s=1
|
|
134185
134185
|
break}n.c=!0
|
|
134186
134186
|
n.D()
|
|
134187
134187
|
p=4
|
|
@@ -5,17 +5,143 @@ const Anthropic = require('@anthropic-ai/sdk');
|
|
|
5
5
|
const { AnthropicProvider } = require('./anthropic');
|
|
6
6
|
|
|
7
7
|
const CLAUDE_CLI_CREDS_PATH = path.join(os.homedir(), '.claude', '.credentials.json');
|
|
8
|
-
const CLAUDE_CODE_BASE_URL = 'https://api.
|
|
8
|
+
const CLAUDE_CODE_BASE_URL = 'https://api.anthropic.com';
|
|
9
|
+
const CLAUDE_CODE_VERSION = process.env.CLAUDE_CODE_VERSION || '2.1.75';
|
|
10
|
+
const CLAUDE_CODE_OAUTH_BETA = 'claude-code-20250219,oauth-2025-04-20,fine-grained-tool-streaming-2025-05-14';
|
|
11
|
+
const CLAUDE_CODE_SYSTEM_PROMPT = "You are Claude Code, Anthropic's official CLI for Claude.";
|
|
12
|
+
const CLAUDE_CODE_CLIENT_ID = '9d1c250a-e61b-44d9-88ed-5944d1962f5e';
|
|
13
|
+
const CLAUDE_CODE_TOKEN_URL = 'https://platform.claude.com/v1/oauth/token';
|
|
9
14
|
|
|
10
|
-
function
|
|
15
|
+
function readTokenRecord(data) {
|
|
16
|
+
const tokens = data?.claudeAiOauthTokens || data?.claudeAiOauth || {};
|
|
17
|
+
const access = tokens.accessToken || tokens.access;
|
|
18
|
+
const refresh = tokens.refreshToken || tokens.refresh;
|
|
19
|
+
const expires = tokens.expiresAt || tokens.expires;
|
|
20
|
+
return {
|
|
21
|
+
access: typeof access === 'string' && access ? access : null,
|
|
22
|
+
refresh: typeof refresh === 'string' && refresh ? refresh : null,
|
|
23
|
+
expires: typeof expires === 'number' && Number.isFinite(expires) ? expires : null,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function readTokenValue(data) {
|
|
28
|
+
return readTokenRecord(data).access;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function readClaudeCliTokenRecord() {
|
|
11
32
|
try {
|
|
12
33
|
const raw = fs.readFileSync(CLAUDE_CLI_CREDS_PATH, 'utf8');
|
|
13
34
|
const data = JSON.parse(raw);
|
|
14
|
-
|
|
15
|
-
|
|
35
|
+
return readTokenRecord(data);
|
|
36
|
+
} catch {
|
|
37
|
+
return { access: null, refresh: null, expires: null };
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function readClaudeCliToken() {
|
|
42
|
+
return readClaudeCliTokenRecord().access;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function mergeAnthropicBeta(existing) {
|
|
46
|
+
if (!existing) return CLAUDE_CODE_OAUTH_BETA;
|
|
47
|
+
const seen = new Set();
|
|
48
|
+
return String(existing)
|
|
49
|
+
.split(',')
|
|
50
|
+
.concat(CLAUDE_CODE_OAUTH_BETA.split(','))
|
|
51
|
+
.map((item) => item.trim())
|
|
52
|
+
.filter((item) => {
|
|
53
|
+
if (!item || seen.has(item)) return false;
|
|
54
|
+
seen.add(item);
|
|
55
|
+
return true;
|
|
56
|
+
})
|
|
57
|
+
.join(',');
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function normalizeExpiresAt(data) {
|
|
61
|
+
if (typeof data.expires_at === 'number' && Number.isFinite(data.expires_at)) {
|
|
62
|
+
return data.expires_at > 10_000_000_000 ? data.expires_at : data.expires_at * 1000;
|
|
63
|
+
}
|
|
64
|
+
if (typeof data.expiresAt === 'number' && Number.isFinite(data.expiresAt)) {
|
|
65
|
+
return data.expiresAt > 10_000_000_000 ? data.expiresAt : data.expiresAt * 1000;
|
|
66
|
+
}
|
|
67
|
+
if (typeof data.expires_in === 'number' && Number.isFinite(data.expires_in)) {
|
|
68
|
+
return Date.now() + (data.expires_in * 1000);
|
|
69
|
+
}
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function sanitizeEnvKey(key) {
|
|
74
|
+
return String(key).replace(/[\r\n]/g, '');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function sanitizeEnvValue(value) {
|
|
78
|
+
return String(value).replace(/[\r\n]/g, '');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function persistEnvValue(key, value) {
|
|
82
|
+
if (!value) return;
|
|
83
|
+
try {
|
|
84
|
+
const { ENV_FILE } = require('../../../../runtime/paths');
|
|
85
|
+
const safeKey = sanitizeEnvKey(key);
|
|
86
|
+
const safeValue = sanitizeEnvValue(value);
|
|
87
|
+
const raw = fs.existsSync(ENV_FILE) ? fs.readFileSync(ENV_FILE, 'utf8') : '';
|
|
88
|
+
const lines = raw ? raw.split('\n') : [];
|
|
89
|
+
let replaced = false;
|
|
90
|
+
for (let i = 0; i < lines.length; i++) {
|
|
91
|
+
if (lines[i].startsWith(`${safeKey}=`)) {
|
|
92
|
+
lines[i] = `${safeKey}=${safeValue}`;
|
|
93
|
+
replaced = true;
|
|
94
|
+
break;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
if (!replaced) lines.push(`${safeKey}=${safeValue}`);
|
|
98
|
+
const output = lines.filter((_, idx, arr) => idx !== arr.length - 1 || arr[idx] !== '').join('\n') + '\n';
|
|
99
|
+
fs.mkdirSync(path.dirname(ENV_FILE), { recursive: true });
|
|
100
|
+
fs.writeFileSync(ENV_FILE, output, { mode: 0o600 });
|
|
101
|
+
} catch { }
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function refreshClaudeCodeAccessToken(refreshToken, fetchImpl = fetch) {
|
|
105
|
+
if (!refreshToken) return null;
|
|
106
|
+
const response = await fetchImpl(CLAUDE_CODE_TOKEN_URL, {
|
|
107
|
+
method: 'POST',
|
|
108
|
+
headers: {
|
|
109
|
+
'Content-Type': 'application/json',
|
|
110
|
+
'Accept': 'application/json',
|
|
111
|
+
'anthropic-version': '2023-06-01',
|
|
112
|
+
},
|
|
113
|
+
body: JSON.stringify({
|
|
114
|
+
grant_type: 'refresh_token',
|
|
115
|
+
refresh_token: refreshToken,
|
|
116
|
+
client_id: CLAUDE_CODE_CLIENT_ID,
|
|
117
|
+
}),
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
const text = await response.text();
|
|
121
|
+
let data = {};
|
|
122
|
+
try {
|
|
123
|
+
data = text ? JSON.parse(text) : {};
|
|
16
124
|
} catch {
|
|
17
|
-
|
|
125
|
+
data = {};
|
|
18
126
|
}
|
|
127
|
+
|
|
128
|
+
if (!response.ok) {
|
|
129
|
+
const detail = data?.error?.message || data?.error_description || data?.error || text || 'Unknown error';
|
|
130
|
+
throw new Error(`Claude Code OAuth refresh failed: HTTP ${response.status} ${detail}`);
|
|
131
|
+
}
|
|
132
|
+
if (!data.access_token) {
|
|
133
|
+
throw new Error('Claude Code OAuth refresh succeeded but no access_token was returned.');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
access: data.access_token,
|
|
138
|
+
refresh: data.refresh_token || refreshToken,
|
|
139
|
+
expires: normalizeExpiresAt(data),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function isAuthenticationError(err) {
|
|
144
|
+
return err?.status === 401 || err?.error?.type === 'authentication_error';
|
|
19
145
|
}
|
|
20
146
|
|
|
21
147
|
class ClaudeCodeProvider extends AnthropicProvider {
|
|
@@ -33,17 +159,86 @@ class ClaudeCodeProvider extends AnthropicProvider {
|
|
|
33
159
|
'claude-haiku-4-5-20251001': 200000,
|
|
34
160
|
};
|
|
35
161
|
|
|
36
|
-
const
|
|
162
|
+
const cliTokenRecord = readClaudeCliTokenRecord();
|
|
163
|
+
const authToken = config.apiKey || process.env.CLAUDE_CODE_OAUTH_TOKEN || cliTokenRecord.access;
|
|
37
164
|
if (!authToken) {
|
|
38
165
|
console.warn('[ClaudeCode] No access token. Run `neoagent login claude-code` to authenticate.');
|
|
39
166
|
}
|
|
40
167
|
|
|
41
|
-
|
|
42
|
-
this.
|
|
168
|
+
this.authToken = authToken || null;
|
|
169
|
+
this.refreshToken = config.refreshToken || process.env.CLAUDE_CODE_REFRESH_TOKEN || cliTokenRecord.refresh || null;
|
|
170
|
+
this.fetchImpl = config.fetch || fetch;
|
|
171
|
+
this.baseURL = config.baseUrl || CLAUDE_CODE_BASE_URL;
|
|
172
|
+
this.defaultHeaders = {
|
|
173
|
+
...(config.defaultHeaders || {}),
|
|
174
|
+
accept: 'application/json',
|
|
175
|
+
'anthropic-dangerous-direct-browser-access': 'true',
|
|
176
|
+
'anthropic-beta': mergeAnthropicBeta(config.defaultHeaders?.['anthropic-beta']),
|
|
177
|
+
'user-agent': `claude-cli/${CLAUDE_CODE_VERSION}`,
|
|
178
|
+
'x-app': 'cli',
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
this.client = this.createClient(this.authToken, config);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
createClient(authToken, config = this.config) {
|
|
185
|
+
// OAuth tokens use Authorization: Bearer. Claude Code subscription inference
|
|
186
|
+
// also requires the Claude Code beta surface headers used by the official CLI.
|
|
187
|
+
return new Anthropic({
|
|
43
188
|
authToken: authToken || undefined,
|
|
44
|
-
baseURL:
|
|
189
|
+
baseURL: this.baseURL,
|
|
190
|
+
defaultHeaders: authToken ? this.defaultHeaders : config.defaultHeaders,
|
|
191
|
+
...(this.fetchImpl ? { fetch: this.fetchImpl } : {}),
|
|
45
192
|
});
|
|
46
193
|
}
|
|
194
|
+
|
|
195
|
+
async refreshClient() {
|
|
196
|
+
const refreshed = await refreshClaudeCodeAccessToken(this.refreshToken, this.fetchImpl);
|
|
197
|
+
if (!refreshed?.access) return false;
|
|
198
|
+
this.authToken = refreshed.access;
|
|
199
|
+
this.refreshToken = refreshed.refresh || this.refreshToken;
|
|
200
|
+
process.env.CLAUDE_CODE_OAUTH_TOKEN = this.authToken;
|
|
201
|
+
persistEnvValue('CLAUDE_CODE_OAUTH_TOKEN', this.authToken);
|
|
202
|
+
if (this.refreshToken) {
|
|
203
|
+
process.env.CLAUDE_CODE_REFRESH_TOKEN = this.refreshToken;
|
|
204
|
+
persistEnvValue('CLAUDE_CODE_REFRESH_TOKEN', this.refreshToken);
|
|
205
|
+
}
|
|
206
|
+
this.client = this.createClient(this.authToken);
|
|
207
|
+
return true;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
convertMessages(messages) {
|
|
211
|
+
const converted = super.convertMessages(messages);
|
|
212
|
+
if (!converted.system) {
|
|
213
|
+
converted.system = [{ type: 'text', text: CLAUDE_CODE_SYSTEM_PROMPT }];
|
|
214
|
+
return converted;
|
|
215
|
+
}
|
|
216
|
+
converted.system = [
|
|
217
|
+
{ type: 'text', text: CLAUDE_CODE_SYSTEM_PROMPT },
|
|
218
|
+
{ type: 'text', text: converted.system },
|
|
219
|
+
];
|
|
220
|
+
return converted;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
async chat(messages, tools = [], options = {}) {
|
|
224
|
+
try {
|
|
225
|
+
return await super.chat(messages, tools, options);
|
|
226
|
+
} catch (err) {
|
|
227
|
+
if (!isAuthenticationError(err) || !this.refreshToken) throw err;
|
|
228
|
+
await this.refreshClient();
|
|
229
|
+
return await super.chat(messages, tools, options);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
async *stream(messages, tools = [], options = {}) {
|
|
234
|
+
try {
|
|
235
|
+
yield* super.stream(messages, tools, options);
|
|
236
|
+
} catch (err) {
|
|
237
|
+
if (!isAuthenticationError(err) || !this.refreshToken) throw err;
|
|
238
|
+
await this.refreshClient();
|
|
239
|
+
yield* super.stream(messages, tools, options);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
47
242
|
}
|
|
48
243
|
|
|
49
|
-
module.exports = { ClaudeCodeProvider, readClaudeCliToken };
|
|
244
|
+
module.exports = { ClaudeCodeProvider, readClaudeCliToken, refreshClaudeCodeAccessToken };
|
|
@@ -3,6 +3,14 @@ const OpenAI = require('openai');
|
|
|
3
3
|
const { BaseProvider } = require('./base');
|
|
4
4
|
|
|
5
5
|
const DEFAULT_BASE_URL = 'https://chatgpt.com/backend-api/codex';
|
|
6
|
+
const OPENAI_CODEX_EMPTY_INPUT_TEXT = ' ';
|
|
7
|
+
const NEOAGENT_VERSION = (() => {
|
|
8
|
+
try {
|
|
9
|
+
return require('../../../../package.json').version || 'unknown';
|
|
10
|
+
} catch {
|
|
11
|
+
return 'unknown';
|
|
12
|
+
}
|
|
13
|
+
})();
|
|
6
14
|
|
|
7
15
|
// Stable per-process installation ID — Codex backend uses it for request tracking.
|
|
8
16
|
const INSTALLATION_ID = crypto.randomUUID();
|
|
@@ -26,7 +34,7 @@ function isCodexBackendBaseUrl(baseURL) {
|
|
|
26
34
|
const url = new URL(trimmed);
|
|
27
35
|
const path = url.pathname.replace(/\/+$/, '');
|
|
28
36
|
return url.hostname === 'chatgpt.com'
|
|
29
|
-
&& (path === '/backend-api' || path === '/backend-api/codex' || path === '/backend-api/codex/v1');
|
|
37
|
+
&& (path === '/backend-api' || path === '/backend-api/v1' || path === '/backend-api/codex' || path === '/backend-api/codex/v1');
|
|
30
38
|
} catch {
|
|
31
39
|
return false;
|
|
32
40
|
}
|
|
@@ -52,6 +60,10 @@ function normalizeCodexBaseUrl(baseURL) {
|
|
|
52
60
|
return baseURL;
|
|
53
61
|
}
|
|
54
62
|
|
|
63
|
+
function isNativeCodexResponsesBaseUrl(baseURL) {
|
|
64
|
+
return isCodexBackendBaseUrl(baseURL);
|
|
65
|
+
}
|
|
66
|
+
|
|
55
67
|
function normalizeContent(content) {
|
|
56
68
|
if (content == null) return '';
|
|
57
69
|
if (typeof content === 'string') return content;
|
|
@@ -67,33 +79,45 @@ function normalizeContent(content) {
|
|
|
67
79
|
return String(content);
|
|
68
80
|
}
|
|
69
81
|
|
|
70
|
-
function
|
|
82
|
+
function normalizeMessageContent(content, role = 'user') {
|
|
71
83
|
if (content == null) return [];
|
|
84
|
+
const isAssistant = role === 'assistant';
|
|
85
|
+
const textType = isAssistant ? 'output_text' : 'input_text';
|
|
72
86
|
|
|
73
87
|
if (typeof content === 'string') {
|
|
74
|
-
return [{ type:
|
|
88
|
+
return [{ type: textType, text: content, ...(isAssistant ? { annotations: [] } : {}) }];
|
|
75
89
|
}
|
|
76
90
|
|
|
77
91
|
if (!Array.isArray(content)) {
|
|
78
92
|
const text = String(content);
|
|
79
|
-
return text ? [{ type:
|
|
93
|
+
return text ? [{ type: textType, text, ...(isAssistant ? { annotations: [] } : {}) }] : [];
|
|
80
94
|
}
|
|
81
95
|
|
|
82
96
|
const parts = [];
|
|
83
97
|
for (const part of content) {
|
|
84
98
|
if (!part) continue;
|
|
85
99
|
if (typeof part === 'string') {
|
|
86
|
-
if (part.trim()) parts.push({ type:
|
|
100
|
+
if (part.trim()) parts.push({ type: textType, text: part, ...(isAssistant ? { annotations: [] } : {}) });
|
|
87
101
|
continue;
|
|
88
102
|
}
|
|
89
103
|
if (part.type === 'text' && typeof part.text === 'string') {
|
|
90
|
-
parts.push({ type:
|
|
104
|
+
parts.push({ type: textType, text: part.text, ...(isAssistant ? { annotations: [] } : {}) });
|
|
91
105
|
continue;
|
|
92
106
|
}
|
|
93
107
|
if (part.type === 'input_text' && typeof part.text === 'string') {
|
|
94
|
-
parts.push({ type:
|
|
108
|
+
parts.push({ type: textType, text: part.text, ...(isAssistant ? { annotations: [] } : {}) });
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
if (part.type === 'output_text' && typeof part.text === 'string') {
|
|
112
|
+
parts.push({ type: textType, text: part.text, ...(isAssistant ? { annotations: part.annotations || [] } : {}) });
|
|
95
113
|
continue;
|
|
96
114
|
}
|
|
115
|
+
if (isAssistant && part.type === 'refusal') {
|
|
116
|
+
const refusal = typeof part.refusal === 'string' ? part.refusal : part.text;
|
|
117
|
+
if (typeof refusal === 'string') parts.push({ type: 'refusal', refusal });
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
if (isAssistant) continue;
|
|
97
121
|
if (part.type === 'image_url') {
|
|
98
122
|
const imageUrl = typeof part.image_url === 'string' ? part.image_url : part.image_url?.url;
|
|
99
123
|
if (imageUrl) {
|
|
@@ -211,11 +235,9 @@ class OpenAICodexProvider extends BaseProvider {
|
|
|
211
235
|
|
|
212
236
|
const defaultHeaders = this.usesCodexBackend
|
|
213
237
|
? {
|
|
214
|
-
|
|
215
|
-
'
|
|
216
|
-
'User-Agent':
|
|
217
|
-
// Required by the Codex responses endpoint
|
|
218
|
-
'OpenAI-Beta': 'responses_websockets=2026-02-06',
|
|
238
|
+
'originator': 'openclaw',
|
|
239
|
+
'version': NEOAGENT_VERSION,
|
|
240
|
+
'User-Agent': `openclaw/${NEOAGENT_VERSION}`,
|
|
219
241
|
'x-codex-installation-id': INSTALLATION_ID,
|
|
220
242
|
'x-openai-internal-codex-residency': process.env.OPENAI_CODEX_RESIDENCY || 'us',
|
|
221
243
|
...(accountId ? { 'ChatGPT-Account-Id': accountId } : {}),
|
|
@@ -226,6 +248,7 @@ class OpenAICodexProvider extends BaseProvider {
|
|
|
226
248
|
apiKey: config.apiKey || process.env.OPENAI_CODEX_ACCESS_TOKEN,
|
|
227
249
|
baseURL,
|
|
228
250
|
defaultHeaders,
|
|
251
|
+
...(config.fetch ? { fetch: config.fetch } : {}),
|
|
229
252
|
});
|
|
230
253
|
}
|
|
231
254
|
|
|
@@ -267,11 +290,12 @@ class OpenAICodexProvider extends BaseProvider {
|
|
|
267
290
|
continue;
|
|
268
291
|
}
|
|
269
292
|
|
|
270
|
-
const
|
|
293
|
+
const role = msg.role === 'assistant' ? 'assistant' : 'user';
|
|
294
|
+
const content = normalizeMessageContent(msg.content, role);
|
|
271
295
|
if (content.length > 0) {
|
|
272
296
|
input.push({
|
|
273
297
|
type: 'message',
|
|
274
|
-
role
|
|
298
|
+
role,
|
|
275
299
|
content,
|
|
276
300
|
});
|
|
277
301
|
}
|
|
@@ -297,6 +321,13 @@ class OpenAICodexProvider extends BaseProvider {
|
|
|
297
321
|
};
|
|
298
322
|
|
|
299
323
|
if (this.usesCodexBackend) {
|
|
324
|
+
if (input.length === 0 && instructions.length > 0) {
|
|
325
|
+
input.push({
|
|
326
|
+
type: 'message',
|
|
327
|
+
role: 'user',
|
|
328
|
+
content: [{ type: 'input_text', text: OPENAI_CODEX_EMPTY_INPUT_TEXT }],
|
|
329
|
+
});
|
|
330
|
+
}
|
|
300
331
|
// instructions must always be present (even empty) — backend returns 400 if omitted
|
|
301
332
|
request.instructions = instructions.join('\n\n');
|
|
302
333
|
request.store = false;
|
|
@@ -315,6 +346,7 @@ class OpenAICodexProvider extends BaseProvider {
|
|
|
315
346
|
request.reasoning = { effort, summary: 'auto' };
|
|
316
347
|
request.include = ['reasoning.encrypted_content'];
|
|
317
348
|
}
|
|
349
|
+
this._sanitizeNativeCodexRequest(request);
|
|
318
350
|
} else {
|
|
319
351
|
if (instructions.length > 0) {
|
|
320
352
|
request.instructions = instructions.join('\n\n');
|
|
@@ -337,12 +369,63 @@ class OpenAICodexProvider extends BaseProvider {
|
|
|
337
369
|
}
|
|
338
370
|
|
|
339
371
|
_requestHeaders() {
|
|
372
|
+
const requestId = crypto.randomUUID();
|
|
340
373
|
return this.usesCodexBackend
|
|
341
|
-
? {
|
|
374
|
+
? {
|
|
375
|
+
'x-client-request-id': requestId,
|
|
376
|
+
'x-openclaw-session-id': requestId,
|
|
377
|
+
'x-openclaw-turn-id': requestId,
|
|
378
|
+
'x-openclaw-turn-attempt': '1',
|
|
379
|
+
}
|
|
342
380
|
: undefined;
|
|
343
381
|
}
|
|
344
382
|
|
|
383
|
+
_sanitizeNativeCodexRequest(request) {
|
|
384
|
+
if (!isNativeCodexResponsesBaseUrl(this.baseURL)) return request;
|
|
385
|
+
for (const key of [
|
|
386
|
+
'max_output_tokens',
|
|
387
|
+
'metadata',
|
|
388
|
+
'prompt_cache_retention',
|
|
389
|
+
'service_tier',
|
|
390
|
+
'temperature',
|
|
391
|
+
'top_p',
|
|
392
|
+
]) {
|
|
393
|
+
delete request[key];
|
|
394
|
+
}
|
|
395
|
+
if (request.text && typeof request.text === 'object' && !Array.isArray(request.text)) {
|
|
396
|
+
const text = { ...request.text };
|
|
397
|
+
delete text.format;
|
|
398
|
+
if (Object.keys(text).length > 0) {
|
|
399
|
+
request.text = text;
|
|
400
|
+
} else {
|
|
401
|
+
delete request.text;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
return request;
|
|
405
|
+
}
|
|
406
|
+
|
|
345
407
|
async chat(messages, tools = [], options = {}) {
|
|
408
|
+
if (this.usesCodexBackend) {
|
|
409
|
+
let final = null;
|
|
410
|
+
let content = '';
|
|
411
|
+
for await (const event of this.stream(messages, tools, options)) {
|
|
412
|
+
if (event.type === 'content') {
|
|
413
|
+
content += event.content || '';
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
if (event.type === 'tool_calls' || event.type === 'done') {
|
|
417
|
+
final = event;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
return {
|
|
421
|
+
content: final?.content || content,
|
|
422
|
+
toolCalls: final?.toolCalls || [],
|
|
423
|
+
finishReason: final?.finishReason || (final?.toolCalls?.length > 0 ? 'tool_calls' : 'stop'),
|
|
424
|
+
usage: final?.usage || null,
|
|
425
|
+
model: final?.model || options.model || this.config.model || this.getDefaultModel(),
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
|
|
346
429
|
const model = options.model || this.config.model || this.getDefaultModel();
|
|
347
430
|
const request = this._buildRequest(messages, tools, options, model);
|
|
348
431
|
let response;
|