neoagent 2.3.1-beta.110 → 2.3.1-beta.113

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/lib/manager.js CHANGED
@@ -919,6 +919,7 @@ async function cmdLoginClaudeCode() {
919
919
  redirect_uri: redirectUri,
920
920
  client_id: clientId,
921
921
  code_verifier: codeVerifier,
922
+ state,
922
923
  }),
923
924
  });
924
925
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neoagent",
3
- "version": "2.3.1-beta.110",
3
+ "version": "2.3.1-beta.113",
4
4
  "description": "Proactive personal AI agent with no limits",
5
5
  "license": "MIT",
6
6
  "main": "server/index.js",
@@ -1 +1 @@
1
- 2d978b6c818f6c23b73f1db1d09e0348
1
+ 0c62ef7d28ad8b7fe9751b4f055e1afb
@@ -37,6 +37,6 @@ _flutter.buildConfig = {"engineRevision":"42d3d75a56efe1a2e9902f52dc8006099c45d9
37
37
 
38
38
  _flutter.loader.load({
39
39
  serviceWorkerSettings: {
40
- serviceWorkerVersion: "2005529779" /* Flutter's service worker is deprecated and will be removed in a future Flutter release. */
40
+ serviceWorkerVersion: "699610273" /* 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("mpaxpxur-fb45326").length!==0&&r.b}if(r){r=s.d
129341
+ r=B.b.t("mpb8ba09-f0b0778").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("mpaxpxur-fb45326").length===0||s.a!=null)return
134149
+ if(B.b.t("mpb8ba09-f0b0778").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,"mpaxpxur-fb45326")){s=1
134167
+ if(J.bm(j)===0||J.d(j,"mpb8ba09-f0b0778")){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("mpaxpxur-fb45326").length===0||n.c){s=1
134184
+ s=p}for(;;)switch(s){case 0:if(B.b.t("mpb8ba09-f0b0778").length===0||n.c){s=1
134185
134185
  break}n.c=!0
134186
134186
  n.D()
134187
134187
  p=4
@@ -5,19 +5,44 @@ 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.claude.ai/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
+
13
+ function readTokenValue(data) {
14
+ return data?.claudeAiOauthTokens?.accessToken
15
+ || data?.claudeAiOauth?.accessToken
16
+ || data?.claudeAiOauth?.access
17
+ || null;
18
+ }
9
19
 
10
20
  function readClaudeCliToken() {
11
21
  try {
12
22
  const raw = fs.readFileSync(CLAUDE_CLI_CREDS_PATH, 'utf8');
13
23
  const data = JSON.parse(raw);
14
- const token = data?.claudeAiOauthTokens?.accessToken;
24
+ const token = readTokenValue(data);
15
25
  return typeof token === 'string' && token ? token : null;
16
26
  } catch {
17
27
  return null;
18
28
  }
19
29
  }
20
30
 
31
+ function mergeAnthropicBeta(existing) {
32
+ if (!existing) return CLAUDE_CODE_OAUTH_BETA;
33
+ const seen = new Set();
34
+ return String(existing)
35
+ .split(',')
36
+ .concat(CLAUDE_CODE_OAUTH_BETA.split(','))
37
+ .map((item) => item.trim())
38
+ .filter((item) => {
39
+ if (!item || seen.has(item)) return false;
40
+ seen.add(item);
41
+ return true;
42
+ })
43
+ .join(',');
44
+ }
45
+
21
46
  class ClaudeCodeProvider extends AnthropicProvider {
22
47
  constructor(config = {}) {
23
48
  super(config);
@@ -38,12 +63,37 @@ class ClaudeCodeProvider extends AnthropicProvider {
38
63
  console.warn('[ClaudeCode] No access token. Run `neoagent login claude-code` to authenticate.');
39
64
  }
40
65
 
41
- // OAuth tokens use Authorization: Bearer — authToken option sends that header
66
+ const defaultHeaders = {
67
+ ...(config.defaultHeaders || {}),
68
+ accept: 'application/json',
69
+ 'anthropic-dangerous-direct-browser-access': 'true',
70
+ 'anthropic-beta': mergeAnthropicBeta(config.defaultHeaders?.['anthropic-beta']),
71
+ 'user-agent': `claude-cli/${CLAUDE_CODE_VERSION}`,
72
+ 'x-app': 'cli',
73
+ };
74
+
75
+ // OAuth tokens use Authorization: Bearer. Claude Code subscription inference
76
+ // also requires the Claude Code beta surface headers used by the official CLI.
42
77
  this.client = new Anthropic({
43
78
  authToken: authToken || undefined,
44
79
  baseURL: config.baseUrl || CLAUDE_CODE_BASE_URL,
80
+ defaultHeaders: authToken ? defaultHeaders : config.defaultHeaders,
81
+ ...(config.fetch ? { fetch: config.fetch } : {}),
45
82
  });
46
83
  }
84
+
85
+ convertMessages(messages) {
86
+ const converted = super.convertMessages(messages);
87
+ if (!converted.system) {
88
+ converted.system = [{ type: 'text', text: CLAUDE_CODE_SYSTEM_PROMPT }];
89
+ return converted;
90
+ }
91
+ converted.system = [
92
+ { type: 'text', text: CLAUDE_CODE_SYSTEM_PROMPT },
93
+ { type: 'text', text: converted.system },
94
+ ];
95
+ return converted;
96
+ }
47
97
  }
48
98
 
49
99
  module.exports = { ClaudeCodeProvider, readClaudeCliToken };
@@ -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;
@@ -211,11 +223,9 @@ class OpenAICodexProvider extends BaseProvider {
211
223
 
212
224
  const defaultHeaders = this.usesCodexBackend
213
225
  ? {
214
- // Required by Cloudflare bot detection on chatgpt.com
215
- 'originator': 'Codex Desktop',
216
- 'User-Agent': 'Codex Desktop/1.0.0 (darwin; arm64)',
217
- // Required by the Codex responses endpoint
218
- 'OpenAI-Beta': 'responses_websockets=2026-02-06',
226
+ 'originator': 'openclaw',
227
+ 'version': NEOAGENT_VERSION,
228
+ 'User-Agent': `openclaw/${NEOAGENT_VERSION}`,
219
229
  'x-codex-installation-id': INSTALLATION_ID,
220
230
  'x-openai-internal-codex-residency': process.env.OPENAI_CODEX_RESIDENCY || 'us',
221
231
  ...(accountId ? { 'ChatGPT-Account-Id': accountId } : {}),
@@ -226,6 +236,7 @@ class OpenAICodexProvider extends BaseProvider {
226
236
  apiKey: config.apiKey || process.env.OPENAI_CODEX_ACCESS_TOKEN,
227
237
  baseURL,
228
238
  defaultHeaders,
239
+ ...(config.fetch ? { fetch: config.fetch } : {}),
229
240
  });
230
241
  }
231
242
 
@@ -297,6 +308,13 @@ class OpenAICodexProvider extends BaseProvider {
297
308
  };
298
309
 
299
310
  if (this.usesCodexBackend) {
311
+ if (input.length === 0 && instructions.length > 0) {
312
+ input.push({
313
+ type: 'message',
314
+ role: 'user',
315
+ content: [{ type: 'input_text', text: OPENAI_CODEX_EMPTY_INPUT_TEXT }],
316
+ });
317
+ }
300
318
  // instructions must always be present (even empty) — backend returns 400 if omitted
301
319
  request.instructions = instructions.join('\n\n');
302
320
  request.store = false;
@@ -315,6 +333,7 @@ class OpenAICodexProvider extends BaseProvider {
315
333
  request.reasoning = { effort, summary: 'auto' };
316
334
  request.include = ['reasoning.encrypted_content'];
317
335
  }
336
+ this._sanitizeNativeCodexRequest(request);
318
337
  } else {
319
338
  if (instructions.length > 0) {
320
339
  request.instructions = instructions.join('\n\n');
@@ -337,12 +356,63 @@ class OpenAICodexProvider extends BaseProvider {
337
356
  }
338
357
 
339
358
  _requestHeaders() {
359
+ const requestId = crypto.randomUUID();
340
360
  return this.usesCodexBackend
341
- ? { 'x-client-request-id': crypto.randomUUID() }
361
+ ? {
362
+ 'x-client-request-id': requestId,
363
+ 'x-openclaw-session-id': requestId,
364
+ 'x-openclaw-turn-id': requestId,
365
+ 'x-openclaw-turn-attempt': '1',
366
+ }
342
367
  : undefined;
343
368
  }
344
369
 
370
+ _sanitizeNativeCodexRequest(request) {
371
+ if (!isNativeCodexResponsesBaseUrl(this.baseURL)) return request;
372
+ for (const key of [
373
+ 'max_output_tokens',
374
+ 'metadata',
375
+ 'prompt_cache_retention',
376
+ 'service_tier',
377
+ 'temperature',
378
+ 'top_p',
379
+ ]) {
380
+ delete request[key];
381
+ }
382
+ if (request.text && typeof request.text === 'object' && !Array.isArray(request.text)) {
383
+ const text = { ...request.text };
384
+ delete text.format;
385
+ if (Object.keys(text).length > 0) {
386
+ request.text = text;
387
+ } else {
388
+ delete request.text;
389
+ }
390
+ }
391
+ return request;
392
+ }
393
+
345
394
  async chat(messages, tools = [], options = {}) {
395
+ if (this.usesCodexBackend) {
396
+ let final = null;
397
+ let content = '';
398
+ for await (const event of this.stream(messages, tools, options)) {
399
+ if (event.type === 'content') {
400
+ content += event.content || '';
401
+ continue;
402
+ }
403
+ if (event.type === 'tool_calls' || event.type === 'done') {
404
+ final = event;
405
+ }
406
+ }
407
+ return {
408
+ content: final?.content || content,
409
+ toolCalls: final?.toolCalls || [],
410
+ finishReason: final?.finishReason || (final?.toolCalls?.length > 0 ? 'tool_calls' : 'stop'),
411
+ usage: final?.usage || null,
412
+ model: final?.model || options.model || this.config.model || this.getDefaultModel(),
413
+ };
414
+ }
415
+
346
416
  const model = options.model || this.config.model || this.getDefaultModel();
347
417
  const request = this._buildRequest(messages, tools, options, model);
348
418
  let response;