remote-codex 0.11.26 → 0.11.28
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/README.md +23 -0
- package/apps/supervisor-api/dist/index.js +329 -24
- package/apps/supervisor-web/dist/assets/{index-CIcJHgFF.js → index-Dpf8YNxk.js} +1 -1
- package/apps/supervisor-web/dist/assets/index-IJffwc2e.css +1 -0
- package/apps/supervisor-web/dist/assets/thread-ui-BnLdOiXc.js +3679 -0
- package/apps/supervisor-web/dist/index.html +3 -3
- package/bin/remote-codex.mjs +216 -12
- package/package.json +1 -1
- package/packages/claude/src/historyItems.ts +71 -0
- package/packages/claude/src/runtimeAdapter.test.ts +259 -0
- package/packages/claude/src/runtimeAdapter.ts +52 -3
- package/apps/supervisor-web/dist/assets/index-BVYGmxwf.css +0 -1
- package/apps/supervisor-web/dist/assets/thread-ui-CaDgVQIY.js +0 -3679
|
@@ -4,16 +4,16 @@
|
|
|
4
4
|
<meta charset="UTF-8" />
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
6
|
<title>Remote Codex Supervisor</title>
|
|
7
|
-
<script type="module" crossorigin src="/assets/index-
|
|
7
|
+
<script type="module" crossorigin src="/assets/index-Dpf8YNxk.js"></script>
|
|
8
8
|
<link rel="modulepreload" crossorigin href="/assets/react-vendor-CgLzZcV4.js">
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/ui-vendor-CeKGesq3.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/graph-vendor-DVPtkh3h.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/terminal-vendor-B365Go3Z.js">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/markdown-vendor-BQJfKm05.js">
|
|
13
|
-
<link rel="modulepreload" crossorigin href="/assets/thread-ui-
|
|
13
|
+
<link rel="modulepreload" crossorigin href="/assets/thread-ui-BnLdOiXc.js">
|
|
14
14
|
<link rel="stylesheet" crossorigin href="/assets/graph-vendor-C5ap-Sga.css">
|
|
15
15
|
<link rel="stylesheet" crossorigin href="/assets/terminal-vendor-Beg8tuEN.css">
|
|
16
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
16
|
+
<link rel="stylesheet" crossorigin href="/assets/index-IJffwc2e.css">
|
|
17
17
|
</head>
|
|
18
18
|
<body class="bg-stone-950">
|
|
19
19
|
<div id="root"></div>
|
package/bin/remote-codex.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import path from 'node:path';
|
|
|
5
5
|
import os from 'node:os';
|
|
6
6
|
import crypto from 'node:crypto';
|
|
7
7
|
import readline from 'node:readline/promises';
|
|
8
|
-
import { spawn } from 'node:child_process';
|
|
8
|
+
import { spawn, spawnSync } from 'node:child_process';
|
|
9
9
|
import { fileURLToPath } from 'node:url';
|
|
10
10
|
|
|
11
11
|
const binDir = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -19,6 +19,30 @@ const supervisorSourceEntry = path.join(packageRoot, 'apps', 'supervisor-api', '
|
|
|
19
19
|
const relaySupervisorConfigPath = process.env.REMOTE_CODEX_RELAY_SUPERVISOR_CONFIG
|
|
20
20
|
? path.resolve(process.env.REMOTE_CODEX_RELAY_SUPERVISOR_CONFIG)
|
|
21
21
|
: path.join(os.homedir(), '.remote-codex', 'relay-supervisor.json');
|
|
22
|
+
const relaySupervisorTmuxSession = process.env.REMOTE_CODEX_RELAY_SUPERVISOR_TMUX_SESSION?.trim()
|
|
23
|
+
|| 'remote-codex-relay-supervisor';
|
|
24
|
+
const relaySupervisorConfigKeys = [
|
|
25
|
+
'REMOTE_CODEX_RELAY_SERVER_URL',
|
|
26
|
+
'REMOTE_CODEX_RELAY_AGENT_TOKEN',
|
|
27
|
+
'REMOTE_CODEX_ADMIN_USERNAME',
|
|
28
|
+
'REMOTE_CODEX_ADMIN_PASSWORD',
|
|
29
|
+
'REMOTE_CODEX_SESSION_SECRET',
|
|
30
|
+
'REMOTE_CODEX_RELAY_SUPERVISOR_HOST',
|
|
31
|
+
'REMOTE_CODEX_RELAY_SUPERVISOR_PORT',
|
|
32
|
+
'DATABASE_URL',
|
|
33
|
+
'WORKSPACE_ROOT',
|
|
34
|
+
'CODEX_HOME',
|
|
35
|
+
'CODEX_COMMAND',
|
|
36
|
+
'CLAUDE_HOME',
|
|
37
|
+
'CLAUDE_COMMAND',
|
|
38
|
+
'OPENCODE_HOME',
|
|
39
|
+
'OPENCODE_COMMAND',
|
|
40
|
+
'REMOTE_CODEX_ENABLED_AGENT_PROVIDERS',
|
|
41
|
+
'LOG_LEVEL',
|
|
42
|
+
'REMOTE_CODEX_E2E_FAKE_RUNTIME',
|
|
43
|
+
'REMOTE_CODEX_DISABLE_BUILD_RESTART',
|
|
44
|
+
'REMOTE_CODEX_PACKAGE_ROOT',
|
|
45
|
+
];
|
|
22
46
|
const sourceCheckout =
|
|
23
47
|
fs.existsSync(path.join(packageRoot, 'pnpm-workspace.yaml')) &&
|
|
24
48
|
fs.existsSync(path.join(packageRoot, 'scripts', 'service-restart.mjs'));
|
|
@@ -174,11 +198,33 @@ function runRelayServer() {
|
|
|
174
198
|
}
|
|
175
199
|
|
|
176
200
|
async function runRelaySupervisor() {
|
|
177
|
-
|
|
201
|
+
const action = process.argv[3] ?? 'start';
|
|
202
|
+
if (action === 'reset') {
|
|
178
203
|
resetRelaySupervisorConfig();
|
|
179
204
|
return;
|
|
180
205
|
}
|
|
206
|
+
if (action === 'status') {
|
|
207
|
+
relaySupervisorStatus();
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
if (action === 'stop') {
|
|
211
|
+
stopRelaySupervisorTmux();
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (action !== 'start' && action !== 'run') {
|
|
215
|
+
console.error(`Unknown relay-supervisor action: ${action}`);
|
|
216
|
+
console.error('Use one of: start, run, status, stop, reset.');
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
181
219
|
await ensureRelaySupervisorConfig();
|
|
220
|
+
if (action === 'start' && shouldStartRelaySupervisorInTmux()) {
|
|
221
|
+
startRelaySupervisorTmux();
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
runRelaySupervisorForeground();
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function runRelaySupervisorForeground() {
|
|
182
228
|
const guidance = {
|
|
183
229
|
commandName: 'remote-codex relay-supervisor',
|
|
184
230
|
description: 'Run the private-machine supervisor backend that connects outward to a public relay.',
|
|
@@ -198,6 +244,8 @@ async function runRelaySupervisor() {
|
|
|
198
244
|
],
|
|
199
245
|
example: [
|
|
200
246
|
'remote-codex relay-supervisor',
|
|
247
|
+
'# foreground/debug mode:',
|
|
248
|
+
'remote-codex relay-supervisor run',
|
|
201
249
|
'# or reset saved interactive configuration:',
|
|
202
250
|
'remote-codex relay-supervisor reset',
|
|
203
251
|
],
|
|
@@ -278,6 +326,125 @@ async function runRelaySupervisor() {
|
|
|
278
326
|
});
|
|
279
327
|
}
|
|
280
328
|
|
|
329
|
+
function shouldStartRelaySupervisorInTmux() {
|
|
330
|
+
const setting = process.env.REMOTE_CODEX_RELAY_SUPERVISOR_TMUX?.trim().toLowerCase();
|
|
331
|
+
if (['0', 'false', 'no', 'off'].includes(setting ?? '')) {
|
|
332
|
+
return false;
|
|
333
|
+
}
|
|
334
|
+
if (process.env.TMUX) {
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
return commandExists('tmux');
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
function startRelaySupervisorTmux() {
|
|
341
|
+
persistRelaySupervisorRuntimeConfig();
|
|
342
|
+
if (tmuxSessionExists(relaySupervisorTmuxSession)) {
|
|
343
|
+
console.log(`remote-codex relay-supervisor is already running in tmux session: ${relaySupervisorTmuxSession}`);
|
|
344
|
+
printRelaySupervisorTmuxCommands();
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const command = relaySupervisorTmuxCommand();
|
|
349
|
+
const result = spawnSync('tmux', ['new-session', '-d', '-s', relaySupervisorTmuxSession, command], {
|
|
350
|
+
cwd: packageRoot,
|
|
351
|
+
env: relaySupervisorTmuxLaunchEnv(),
|
|
352
|
+
encoding: 'utf8',
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
if (result.status !== 0) {
|
|
356
|
+
console.error('Failed to start relay-supervisor in tmux.');
|
|
357
|
+
if (result.stderr?.trim()) {
|
|
358
|
+
console.error(result.stderr.trim());
|
|
359
|
+
}
|
|
360
|
+
console.error('Falling back to foreground mode.');
|
|
361
|
+
runRelaySupervisorForeground();
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
console.log(`Started remote-codex relay-supervisor in tmux session: ${relaySupervisorTmuxSession}`);
|
|
366
|
+
printRelaySupervisorTmuxCommands();
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function relaySupervisorTmuxCommand() {
|
|
370
|
+
const envPrefix = nonEmptyEnv('REMOTE_CODEX_RELAY_SUPERVISOR_CONFIG')
|
|
371
|
+
? `REMOTE_CODEX_RELAY_SUPERVISOR_CONFIG=${shellQuote(process.env.REMOTE_CODEX_RELAY_SUPERVISOR_CONFIG)}`
|
|
372
|
+
: '';
|
|
373
|
+
const command = `${shellQuote(process.execPath)} ${shellQuote(fileURLToPath(import.meta.url))} relay-supervisor run`;
|
|
374
|
+
return envPrefix ? `${envPrefix} ${command}` : command;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
function relaySupervisorTmuxLaunchEnv() {
|
|
378
|
+
const env = {};
|
|
379
|
+
for (const name of ['PATH', 'HOME', 'SHELL', 'USER', 'LOGNAME', 'LANG', 'LC_ALL', 'TERM']) {
|
|
380
|
+
if (nonEmptyEnv(name)) {
|
|
381
|
+
env[name] = process.env[name];
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
if (nonEmptyEnv('REMOTE_CODEX_RELAY_SUPERVISOR_CONFIG')) {
|
|
385
|
+
env.REMOTE_CODEX_RELAY_SUPERVISOR_CONFIG = process.env.REMOTE_CODEX_RELAY_SUPERVISOR_CONFIG;
|
|
386
|
+
}
|
|
387
|
+
return env;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function relaySupervisorStatus() {
|
|
391
|
+
if (!commandExists('tmux')) {
|
|
392
|
+
console.log('tmux is not installed; no managed relay-supervisor session can be inspected.');
|
|
393
|
+
process.exit(1);
|
|
394
|
+
}
|
|
395
|
+
if (!tmuxSessionExists(relaySupervisorTmuxSession)) {
|
|
396
|
+
console.log(`remote-codex relay-supervisor is not running in tmux session: ${relaySupervisorTmuxSession}`);
|
|
397
|
+
process.exit(1);
|
|
398
|
+
}
|
|
399
|
+
console.log(`remote-codex relay-supervisor is running in tmux session: ${relaySupervisorTmuxSession}`);
|
|
400
|
+
printRelaySupervisorTmuxCommands();
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
function stopRelaySupervisorTmux() {
|
|
404
|
+
if (!commandExists('tmux')) {
|
|
405
|
+
console.log('tmux is not installed; no managed relay-supervisor session can be stopped.');
|
|
406
|
+
return;
|
|
407
|
+
}
|
|
408
|
+
if (!tmuxSessionExists(relaySupervisorTmuxSession)) {
|
|
409
|
+
console.log(`remote-codex relay-supervisor is not running in tmux session: ${relaySupervisorTmuxSession}`);
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
const result = spawnSync('tmux', ['kill-session', '-t', relaySupervisorTmuxSession], {
|
|
413
|
+
cwd: packageRoot,
|
|
414
|
+
encoding: 'utf8',
|
|
415
|
+
});
|
|
416
|
+
if (result.status !== 0) {
|
|
417
|
+
console.error(`Failed to stop tmux session: ${relaySupervisorTmuxSession}`);
|
|
418
|
+
if (result.stderr?.trim()) {
|
|
419
|
+
console.error(result.stderr.trim());
|
|
420
|
+
}
|
|
421
|
+
process.exit(1);
|
|
422
|
+
}
|
|
423
|
+
console.log(`Stopped remote-codex relay-supervisor tmux session: ${relaySupervisorTmuxSession}`);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
function printRelaySupervisorTmuxCommands() {
|
|
427
|
+
console.log(`Attach logs: tmux attach -t ${shellQuote(relaySupervisorTmuxSession)}`);
|
|
428
|
+
console.log('Status: remote-codex relay-supervisor status');
|
|
429
|
+
console.log('Stop: remote-codex relay-supervisor stop');
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function tmuxSessionExists(sessionName) {
|
|
433
|
+
const result = spawnSync('tmux', ['has-session', '-t', sessionName], {
|
|
434
|
+
cwd: packageRoot,
|
|
435
|
+
stdio: 'ignore',
|
|
436
|
+
});
|
|
437
|
+
return result.status === 0;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function commandExists(commandName) {
|
|
441
|
+
const result = spawnSync(commandName, ['-V'], {
|
|
442
|
+
cwd: packageRoot,
|
|
443
|
+
stdio: 'ignore',
|
|
444
|
+
});
|
|
445
|
+
return result.error?.code !== 'ENOENT';
|
|
446
|
+
}
|
|
447
|
+
|
|
281
448
|
async function ensureRelaySupervisorConfig() {
|
|
282
449
|
const existing = readRelaySupervisorConfig();
|
|
283
450
|
const generated = {
|
|
@@ -296,9 +463,11 @@ async function ensureRelaySupervisorConfig() {
|
|
|
296
463
|
const needsRelayUrl = !nonEmptyEnv('REMOTE_CODEX_RELAY_SERVER_URL');
|
|
297
464
|
const needsAgentToken = !nonEmptyEnv('REMOTE_CODEX_RELAY_AGENT_TOKEN');
|
|
298
465
|
if (!needsRelayUrl && !needsAgentToken) {
|
|
466
|
+
persistRelaySupervisorRuntimeConfig(config);
|
|
299
467
|
return;
|
|
300
468
|
}
|
|
301
469
|
if (!process.stdin.isTTY || !process.stderr.isTTY) {
|
|
470
|
+
persistRelaySupervisorRuntimeConfig(config);
|
|
302
471
|
return;
|
|
303
472
|
}
|
|
304
473
|
|
|
@@ -326,15 +495,7 @@ async function ensureRelaySupervisorConfig() {
|
|
|
326
495
|
rl.close();
|
|
327
496
|
}
|
|
328
497
|
|
|
329
|
-
|
|
330
|
-
...config,
|
|
331
|
-
REMOTE_CODEX_RELAY_SERVER_URL: process.env.REMOTE_CODEX_RELAY_SERVER_URL,
|
|
332
|
-
REMOTE_CODEX_RELAY_AGENT_TOKEN: process.env.REMOTE_CODEX_RELAY_AGENT_TOKEN,
|
|
333
|
-
REMOTE_CODEX_ADMIN_USERNAME: process.env.REMOTE_CODEX_ADMIN_USERNAME,
|
|
334
|
-
REMOTE_CODEX_ADMIN_PASSWORD: process.env.REMOTE_CODEX_ADMIN_PASSWORD,
|
|
335
|
-
REMOTE_CODEX_SESSION_SECRET: process.env.REMOTE_CODEX_SESSION_SECRET,
|
|
336
|
-
DATABASE_URL: process.env.DATABASE_URL,
|
|
337
|
-
});
|
|
498
|
+
persistRelaySupervisorRuntimeConfig(config);
|
|
338
499
|
}
|
|
339
500
|
|
|
340
501
|
async function promptRelaySupervisorValue(rl, prompt, validate, invalidMessage) {
|
|
@@ -368,6 +529,16 @@ function writeRelaySupervisorConfig(config) {
|
|
|
368
529
|
}
|
|
369
530
|
}
|
|
370
531
|
|
|
532
|
+
function persistRelaySupervisorRuntimeConfig(base = readRelaySupervisorConfig()) {
|
|
533
|
+
const config = { ...base };
|
|
534
|
+
for (const name of relaySupervisorConfigKeys) {
|
|
535
|
+
if (nonEmptyEnv(name)) {
|
|
536
|
+
config[name] = process.env[name];
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
writeRelaySupervisorConfig(config);
|
|
540
|
+
}
|
|
541
|
+
|
|
371
542
|
function resetRelaySupervisorConfig() {
|
|
372
543
|
try {
|
|
373
544
|
fs.unlinkSync(relaySupervisorConfigPath);
|
|
@@ -527,6 +698,14 @@ function envValue(names, defaultValue) {
|
|
|
527
698
|
return defaultValue;
|
|
528
699
|
}
|
|
529
700
|
|
|
701
|
+
function shellQuote(value) {
|
|
702
|
+
const stringValue = String(value ?? '');
|
|
703
|
+
if (/^[A-Za-z0-9_./:@%+=,~-]+$/.test(stringValue)) {
|
|
704
|
+
return stringValue;
|
|
705
|
+
}
|
|
706
|
+
return `'${stringValue.replace(/'/g, `'\\''`)}'`;
|
|
707
|
+
}
|
|
708
|
+
|
|
530
709
|
function relayServerEnv() {
|
|
531
710
|
const env = { ...process.env };
|
|
532
711
|
for (const name of [
|
|
@@ -806,11 +985,24 @@ public internet; it connects outward to a public relay server.
|
|
|
806
985
|
|
|
807
986
|
Usage:
|
|
808
987
|
remote-codex relay-supervisor
|
|
988
|
+
remote-codex relay-supervisor run
|
|
989
|
+
remote-codex relay-supervisor status
|
|
990
|
+
remote-codex relay-supervisor stop
|
|
809
991
|
remote-codex relay-supervisor reset
|
|
810
992
|
|
|
811
993
|
This command automatically sets for the child supervisor:
|
|
812
994
|
REMOTE_CODEX_MODE=relay
|
|
813
995
|
|
|
996
|
+
Default process management:
|
|
997
|
+
By default, this command tries to start the supervisor in a detached tmux
|
|
998
|
+
session named "${relaySupervisorTmuxSession}" so the device stays online
|
|
999
|
+
after the launching terminal closes. If tmux is not installed, or if
|
|
1000
|
+
REMOTE_CODEX_RELAY_SUPERVISOR_TMUX=0 is set, it runs in the foreground.
|
|
1001
|
+
|
|
1002
|
+
Use "remote-codex relay-supervisor run" for explicit foreground/debug mode.
|
|
1003
|
+
Use "remote-codex relay-supervisor status" to inspect the tmux session.
|
|
1004
|
+
Use "remote-codex relay-supervisor stop" to stop the tmux session.
|
|
1005
|
+
|
|
814
1006
|
Interactive setup:
|
|
815
1007
|
When REMOTE_CODEX_RELAY_SERVER_URL or REMOTE_CODEX_RELAY_AGENT_TOKEN is
|
|
816
1008
|
missing and stdin is interactive, the command asks for those values and saves
|
|
@@ -818,7 +1010,8 @@ Interactive setup:
|
|
|
818
1010
|
${relaySupervisorConfigPath}
|
|
819
1011
|
|
|
820
1012
|
The saved config also includes generated local supervisor auth/session values
|
|
821
|
-
|
|
1013
|
+
and copied setup values such as REMOTE_CODEX_RELAY_SUPERVISOR_PORT, so the
|
|
1014
|
+
tmux child process can start with the same effective configuration.
|
|
822
1015
|
|
|
823
1016
|
Use "remote-codex relay-supervisor reset" to delete the saved config.
|
|
824
1017
|
|
|
@@ -851,10 +1044,21 @@ Recommended environment:
|
|
|
851
1044
|
Codex executable. Default codex.
|
|
852
1045
|
REMOTE_CODEX_ENABLED_AGENT_PROVIDERS
|
|
853
1046
|
Comma-separated provider ids, for example codex,claude.
|
|
1047
|
+
REMOTE_CODEX_RELAY_SUPERVISOR_TMUX
|
|
1048
|
+
Set to 0/false/no/off to disable default tmux management.
|
|
1049
|
+
REMOTE_CODEX_RELAY_SUPERVISOR_TMUX_SESSION
|
|
1050
|
+
tmux session name. Default ${relaySupervisorTmuxSession}.
|
|
854
1051
|
|
|
855
1052
|
Example:
|
|
856
1053
|
remote-codex relay-supervisor
|
|
857
1054
|
|
|
1055
|
+
Foreground/debug example:
|
|
1056
|
+
remote-codex relay-supervisor run
|
|
1057
|
+
|
|
1058
|
+
Management:
|
|
1059
|
+
remote-codex relay-supervisor status
|
|
1060
|
+
remote-codex relay-supervisor stop
|
|
1061
|
+
|
|
858
1062
|
Non-interactive example:
|
|
859
1063
|
REMOTE_CODEX_RELAY_SERVER_URL=wss://relay.example.com \\
|
|
860
1064
|
REMOTE_CODEX_RELAY_AGENT_TOKEN=rcd_device_token_from_relay_portal \\
|
package/package.json
CHANGED
|
@@ -28,6 +28,15 @@ const HIDDEN_ASK_USER_QUESTION_CONTINUATION_PREFIX =
|
|
|
28
28
|
const SUPPRESSED_ASSISTANT_TEXTS = new Set([
|
|
29
29
|
'No response requested.',
|
|
30
30
|
]);
|
|
31
|
+
const CLAUDE_LIMIT_ERROR_PATTERNS = [
|
|
32
|
+
/\byou(?:'|’)ve hit your session limit\b/i,
|
|
33
|
+
/\byou have hit your session limit\b/i,
|
|
34
|
+
/\b(?:hit|reached|exceeded) (?:the )?(?:session|usage|rate) limit\b/i,
|
|
35
|
+
/\b(?:session|usage|rate) limit (?:hit|reached|exceeded)\b/i,
|
|
36
|
+
/\bquota exceeded\b/i,
|
|
37
|
+
/\bcredit balance (?:is )?(?:too low|insufficient|exhausted)\b/i,
|
|
38
|
+
/\binsufficient credits?\b/i,
|
|
39
|
+
];
|
|
31
40
|
|
|
32
41
|
function normalizedToolName(toolName: string) {
|
|
33
42
|
return toolName.replace(/[\s_-]+/g, '').toLowerCase();
|
|
@@ -265,6 +274,30 @@ function shouldSuppressAssistantText(text: string) {
|
|
|
265
274
|
return SUPPRESSED_ASSISTANT_TEXTS.has(text.trim());
|
|
266
275
|
}
|
|
267
276
|
|
|
277
|
+
export function claudeLimitErrorMessage(text: string | null | undefined) {
|
|
278
|
+
const normalized = text?.trim();
|
|
279
|
+
if (!normalized) {
|
|
280
|
+
return null;
|
|
281
|
+
}
|
|
282
|
+
return CLAUDE_LIMIT_ERROR_PATTERNS.some((pattern) => pattern.test(normalized))
|
|
283
|
+
? normalized
|
|
284
|
+
: null;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export function limitErrorFromHistoryItems(items: AgentHistoryItem[]) {
|
|
288
|
+
for (let index = items.length - 1; index >= 0; index -= 1) {
|
|
289
|
+
const item = items[index];
|
|
290
|
+
if (item?.kind !== 'agentMessage') {
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
const error = claudeLimitErrorMessage(item.text);
|
|
294
|
+
if (error) {
|
|
295
|
+
return error;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
|
|
268
301
|
export function userMessageToHistoryItem(id: string, message: unknown): AgentHistoryItem {
|
|
269
302
|
return {
|
|
270
303
|
id,
|
|
@@ -434,6 +467,44 @@ export function toolResultBlocks(message: unknown): Array<{ toolUseId: string; r
|
|
|
434
467
|
.filter((block): block is { toolUseId: string; result: unknown } => Boolean(block));
|
|
435
468
|
}
|
|
436
469
|
|
|
470
|
+
function xmlTagText(input: string, tagName: string) {
|
|
471
|
+
const match = new RegExp(`<${tagName}>([\\s\\S]*?)</${tagName}>`, 'i').exec(input);
|
|
472
|
+
return match?.[1]?.trim() || null;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function decodeBasicXmlEntities(input: string) {
|
|
476
|
+
return input
|
|
477
|
+
.replace(/"/g, '"')
|
|
478
|
+
.replace(/'/g, "'")
|
|
479
|
+
.replace(/>/g, '>')
|
|
480
|
+
.replace(/</g, '<')
|
|
481
|
+
.replace(/&/g, '&');
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
export function taskNotificationToolResult(
|
|
485
|
+
message: unknown,
|
|
486
|
+
): { toolUseId: string; result: string } | null {
|
|
487
|
+
const text = messageContentText(message).trim();
|
|
488
|
+
if (!text.startsWith('<task-notification>') || !text.includes('</task-notification>')) {
|
|
489
|
+
return null;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
const toolUseId = xmlTagText(text, 'tool-use-id');
|
|
493
|
+
if (!toolUseId) {
|
|
494
|
+
return null;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
const result =
|
|
498
|
+
xmlTagText(text, 'result') ??
|
|
499
|
+
xmlTagText(text, 'summary') ??
|
|
500
|
+
text;
|
|
501
|
+
|
|
502
|
+
return {
|
|
503
|
+
toolUseId,
|
|
504
|
+
result: decodeBasicXmlEntities(result),
|
|
505
|
+
};
|
|
506
|
+
}
|
|
507
|
+
|
|
437
508
|
export function askUserQuestionToolUseIds(message: unknown): Set<string> {
|
|
438
509
|
const ids = new Set<string>();
|
|
439
510
|
for (const block of contentBlocks(message)) {
|