create-claude-workspace 1.1.79 → 1.1.80
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.
|
@@ -407,7 +407,8 @@ export function runClaude(opts, log, runOpts = {}) {
|
|
|
407
407
|
}
|
|
408
408
|
// Flush remaining buffer
|
|
409
409
|
if (buffer.trim()) {
|
|
410
|
-
const
|
|
410
|
+
const cleaned = buffer.replace(/\r/g, '');
|
|
411
|
+
const event = parseStreamEvent(cleaned);
|
|
411
412
|
if (event) {
|
|
412
413
|
if (event.session_id && !sessionId)
|
|
413
414
|
sessionId = event.session_id;
|
|
@@ -416,6 +417,14 @@ export function runClaude(opts, log, runOpts = {}) {
|
|
|
416
417
|
resultReceived = true;
|
|
417
418
|
}
|
|
418
419
|
}
|
|
420
|
+
else {
|
|
421
|
+
// Non-JSON buffer (e.g. "You've hit your limit" without trailing newline)
|
|
422
|
+
const lower = cleaned.toLowerCase();
|
|
423
|
+
detectTextSignals(lower);
|
|
424
|
+
if (USAGE_LIMIT_RE.test(lower) || RATE_LIMIT_RE.test(lower) || AUTH_ERROR_RE.test(lower)) {
|
|
425
|
+
stderr += cleaned + '\n';
|
|
426
|
+
}
|
|
427
|
+
}
|
|
419
428
|
}
|
|
420
429
|
// Final stderr-based detection
|
|
421
430
|
detectTextSignals(stderr.toLowerCase());
|
|
@@ -431,6 +440,7 @@ export function runClaude(opts, log, runOpts = {}) {
|
|
|
431
440
|
timedOut: killReason === 'process_timeout',
|
|
432
441
|
activityTimedOut: killReason === 'activity_timeout',
|
|
433
442
|
hasResult: resultReceived,
|
|
443
|
+
durationMs,
|
|
434
444
|
});
|
|
435
445
|
if (resolved)
|
|
436
446
|
return;
|
|
@@ -49,7 +49,10 @@ export function classifyError(signals) {
|
|
|
49
49
|
return category;
|
|
50
50
|
}
|
|
51
51
|
// Exit code 1 with no error indicators → likely max-turns
|
|
52
|
-
if
|
|
52
|
+
// But only if process ran long enough (>10s). Instant exit (e.g. usage limit printed
|
|
53
|
+
// as plain text that wasn't caught) should NOT be classified as max-turns to prevent
|
|
54
|
+
// infinite restart loops with no backoff.
|
|
55
|
+
if (signals.code === 1 && stderr.trim().length < 50 && (signals.durationMs ?? 60_000) > 10_000)
|
|
53
56
|
return 'max_turns';
|
|
54
57
|
// Non-zero exit with content
|
|
55
58
|
if (signals.code !== null && signals.code !== 0)
|
|
@@ -56,8 +56,11 @@ describe('classifyError', () => {
|
|
|
56
56
|
it('detects context exhaustion from stderr', () => {
|
|
57
57
|
expect(classifyError({ ...base, code: 1, stderr: 'context_length_exceeded' })).toBe('context_exhausted');
|
|
58
58
|
});
|
|
59
|
-
it('classifies exit code 1 with minimal stderr as max_turns', () => {
|
|
60
|
-
expect(classifyError({ ...base, code: 1, stderr: '' })).toBe('max_turns');
|
|
59
|
+
it('classifies exit code 1 with minimal stderr as max_turns when process ran >10s', () => {
|
|
60
|
+
expect(classifyError({ ...base, code: 1, stderr: '', durationMs: 60_000 })).toBe('max_turns');
|
|
61
|
+
});
|
|
62
|
+
it('classifies instant exit (code 1, <10s, no stderr) as cli_crash not max_turns', () => {
|
|
63
|
+
expect(classifyError({ ...base, code: 1, stderr: '', durationMs: 1_000 })).toBe('cli_crash');
|
|
61
64
|
});
|
|
62
65
|
it('classifies exit code 1 with content as cli_crash', () => {
|
|
63
66
|
expect(classifyError({ ...base, code: 1, stderr: 'Some unexpected internal error happened during execution blah blah' })).toBe('cli_crash');
|
|
@@ -674,9 +674,12 @@ describe('Loop integration: classifyError → getErrorAction → checkpoint', ()
|
|
|
674
674
|
expect(classifyError({ ...base, code: 1, stderr: 'ENOTFOUND' })).toBe('network_error');
|
|
675
675
|
expect(classifyError({ ...base, code: 1, stderr: 'context_length_exceeded' })).toBe('context_exhausted');
|
|
676
676
|
});
|
|
677
|
-
it('exit 1 with minimal stderr → max_turns', () => {
|
|
678
|
-
expect(classifyError({ ...base, code: 1, stderr: '' })).toBe('max_turns');
|
|
679
|
-
expect(classifyError({ ...base, code: 1, stderr: 'short' })).toBe('max_turns');
|
|
677
|
+
it('exit 1 with minimal stderr and long duration → max_turns', () => {
|
|
678
|
+
expect(classifyError({ ...base, code: 1, stderr: '', durationMs: 60_000 })).toBe('max_turns');
|
|
679
|
+
expect(classifyError({ ...base, code: 1, stderr: 'short', durationMs: 60_000 })).toBe('max_turns');
|
|
680
|
+
});
|
|
681
|
+
it('exit 1 with minimal stderr and instant exit → cli_crash (not max_turns)', () => {
|
|
682
|
+
expect(classifyError({ ...base, code: 1, stderr: '', durationMs: 2_000 })).toBe('cli_crash');
|
|
680
683
|
});
|
|
681
684
|
it('exit 1 with long stderr → cli_crash', () => {
|
|
682
685
|
expect(classifyError({ ...base, code: 1, stderr: 'a'.repeat(60) })).toBe('cli_crash');
|