| 1 | const maxLineLength = 4096; |
| 2 | // Cursor also uses RetriableError for agent-loop failures; preserve those diagnostics. |
| 3 | const transportError = |
| 4 | /^Error: (?:RetriableError: (?!\[internal\]).+|ConnectError: \[(?:unavailable|aborted|deadline_exceeded)\].*)$/; |
| 5 | const serverError = "Something went wrong communicating with the server. Please try again."; |
| 6 | |
| 7 | interface ReplyState { |
| 8 | disqualified: boolean; |
| 9 | failure: string | undefined; |
| 10 | } |
| 11 | |
| 12 | function 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. */ |
| 26 | export 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 | } |