1const maxLineLength = 4096;
2// Cursor also uses RetriableError for agent-loop failures; preserve those diagnostics.
3const transportError =
4 /^Error: (?:RetriableError: (?!\[internal\]).+|ConnectError: \[(?:unavailable|aborted|deadline_exceeded)\].*)$/;
5const serverError = "Something went wrong communicating with the server. Please try again.";
6
7interface ReplyState {
8 disqualified: boolean;
9 failure: string | undefined;
10}
11
12function consumeLine(state: ReplyState, line: string) {
13 if (state.disqualified) return;
14 const text = line.trimEnd();
15 if (transportError.test(text) || text === serverError) {
16 state.failure = text;
17 } else if (text.trim() !== "" && !(state.failure && /^\s+at\s/.test(text))) {
18 // An explanation or code sample can quote the same diagnostic. Only
19 // classify an assistant item consisting entirely of a transport dump.
20 state.disqualified = true;
21 state.failure = undefined;
22 }
23}
24
25/** Tracks a standalone Cursor diagnostic without retaining an entire streamed answer. */
26export class CursorTransportFailure {
27 private state: ReplyState = { disqualified: false, failure: undefined };
28 private line = "";
29
30 push(text: string) {
31 for (const [index, part] of text.split("\n").entries()) {
32 if (this.state.disqualified) return;
33 if (index > 0) {
34 consumeLine(this.state, this.line);
35 this.line = "";
36 }
37 if (this.line.length + part.length > maxLineLength) {
38 this.state.disqualified = true;
39 this.state.failure = undefined;
40 this.line = "";
41 return;
42 }
43 this.line += part;
44 }
45 }
46
47 get failure() {
48 const state = { ...this.state };
49 consumeLine(state, this.line);
50 return state.failure;
51 }
52}