@aexol/spectral 0.9.176 → 0.9.178
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/dist/server/agent-bridge.d.ts +20 -0
- package/dist/server/agent-bridge.d.ts.map +1 -1
- package/dist/server/agent-bridge.js +65 -4
- package/dist/server/error-humanizer.d.ts +9 -4
- package/dist/server/error-humanizer.d.ts.map +1 -1
- package/dist/server/error-humanizer.js +75 -5
- package/package.json +1 -1
|
@@ -186,6 +186,16 @@ export declare class AgentBridge {
|
|
|
186
186
|
private promptReject?;
|
|
187
187
|
/** Per-turn tool execution backstop timer (see TOOL_EXECUTION_TIMEOUT_MS). */
|
|
188
188
|
private toolExecutionTimer?;
|
|
189
|
+
/**
|
|
190
|
+
* Number of in-flight `subagent` tool calls. While >0 the parent prompt
|
|
191
|
+
* inactivity watchdog is paused: the parent is legitimately waiting on the
|
|
192
|
+
* child agent, and the child carries its own independent deadline
|
|
193
|
+
* (SPECTRAL_SUBAGENT_TIMEOUT_MS). Pausing (rather than just bumping on
|
|
194
|
+
* progress events) prevents a silent subagent phase — a long LLM thinking
|
|
195
|
+
* stretch or a quiet build/test — from tripping the parent watchdog and
|
|
196
|
+
* aborting the whole prompt mid-delegation.
|
|
197
|
+
*/
|
|
198
|
+
private activeSubagents;
|
|
189
199
|
/** Current model's credit rates (from availableAgentModels), used for token_usage. */
|
|
190
200
|
private activeCreditRates;
|
|
191
201
|
private memoryPhase;
|
|
@@ -393,6 +403,16 @@ export declare class AgentBridge {
|
|
|
393
403
|
* streaming tool output is never killed by the inactivity timeout.
|
|
394
404
|
*/
|
|
395
405
|
private bumpPromptActivity;
|
|
406
|
+
/** Arm a fresh inactivity watchdog timeout (used by bump/resume paths). */
|
|
407
|
+
private schedulePromptActivityTimeout;
|
|
408
|
+
/**
|
|
409
|
+
* Pause the parent inactivity watchdog while a subagent tool is running.
|
|
410
|
+
* Idempotent per active subagent: the watchdog stays cleared until the last
|
|
411
|
+
* in-flight subagent finishes.
|
|
412
|
+
*/
|
|
413
|
+
private pausePromptActivityForSubagent;
|
|
414
|
+
/** Re-arm the inactivity watchdog once no subagents remain in flight. */
|
|
415
|
+
private resumePromptActivityAfterSubagent;
|
|
396
416
|
private clearPromptTimers;
|
|
397
417
|
/**
|
|
398
418
|
* Start the per-turn tool execution backstop. Only the first
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-bridge.d.ts","sourceRoot":"","sources":["../../src/server/agent-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAaH,OAAO,EAQL,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EAEtB,MAAM,8BAA8B,CAAC;AAatC,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,8BAA8B,CAAC;AAatC,OAAO,EACL,kBAAkB,IAAI,yBAAyB,EAEhD,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAI3E,UAAU,4BAA4B;IACpC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,cAAc,CAAC,EAAE,QAAQ,GAAG,MAAM,CAAC;CACpC;AAkBD,sEAAsE;AACtE,MAAM,MAAM,oBAAoB,GAAG,OAAO,yBAAyB,CAAC;AAmCpE,MAAM,WAAW,kBAAkB;IACjC,kFAAkF;IAClF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACnC,qFAAqF;IACrF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,2EAA2E;IAC3E,GAAG,EAAE,MAAM,CAAC;IACZ,qFAAqF;IACrF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,8EAA8E;IAC9E,IAAI,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAC;IACnC;;;;;OAKG;IACH,0BAA0B,EAAE,CAAC,GAAG,EAAE;QAChC,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW,EAAE,MAAM,CAAC;QACpB,6EAA6E;QAC7E,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,KAAK,IAAI,CAAC;IACX;;;OAGG;IACH,yBAAyB,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACxD;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,KAAK,KAAK,IAAI,CAAC;IAC/B;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,WAAW,EAAE,CAAC;IACxB;;;;OAIG;IACH,cAAc,CAAC,EAAE,qBAAqB,GAAG,IAAI,CAAC;IAC9C;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,oBAAoB,CAAC;IAC1C;;;;;OAKG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B;;;;;;;;;;;OAWG;IACH,yBAAyB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACtE;
|
|
1
|
+
{"version":3,"file":"agent-bridge.d.ts","sourceRoot":"","sources":["../../src/server/agent-bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -570,8 +570,13 @@ const PROMPT_INACTIVITY_TIMEOUT_MS = Number(process.env.SPECTRAL_PROMPT_INACTIVI
|
|
|
570
570
|
* though those chunks no longer reset the inactivity watchdog, this timer
|
|
571
571
|
* provides an absolute backstop per tool invocation.
|
|
572
572
|
*
|
|
573
|
-
* Default 60 minutes (raised from 30 — long builds
|
|
573
|
+
* Default 60 minutes (raised from 30 — long builds were hitting it),
|
|
574
574
|
* overridable via `SPECTRAL_TOOL_EXECUTION_TIMEOUT_MS`.
|
|
575
|
+
*
|
|
576
|
+
* The `subagent` tool is exempt: it carries its own independent deadline
|
|
577
|
+
* (`SPECTRAL_SUBAGENT_TIMEOUT_MS`, default 4h) and streams progress events
|
|
578
|
+
* that keep the inactivity watchdog fed, so the per-tool backstop must not
|
|
579
|
+
* kill a long-running subagent mid-delegation.
|
|
575
580
|
* On expiry it forces a prompt reject (same abort path as the inactivity
|
|
576
581
|
* watchdog) with a descriptive message.
|
|
577
582
|
*/
|
|
@@ -609,6 +614,16 @@ export class AgentBridge {
|
|
|
609
614
|
promptReject;
|
|
610
615
|
/** Per-turn tool execution backstop timer (see TOOL_EXECUTION_TIMEOUT_MS). */
|
|
611
616
|
toolExecutionTimer;
|
|
617
|
+
/**
|
|
618
|
+
* Number of in-flight `subagent` tool calls. While >0 the parent prompt
|
|
619
|
+
* inactivity watchdog is paused: the parent is legitimately waiting on the
|
|
620
|
+
* child agent, and the child carries its own independent deadline
|
|
621
|
+
* (SPECTRAL_SUBAGENT_TIMEOUT_MS). Pausing (rather than just bumping on
|
|
622
|
+
* progress events) prevents a silent subagent phase — a long LLM thinking
|
|
623
|
+
* stretch or a quiet build/test — from tripping the parent watchdog and
|
|
624
|
+
* aborting the whole prompt mid-delegation.
|
|
625
|
+
*/
|
|
626
|
+
activeSubagents = 0;
|
|
612
627
|
/** Current model's credit rates (from availableAgentModels), used for token_usage. */
|
|
613
628
|
activeCreditRates = null;
|
|
614
629
|
memoryPhase = "idle";
|
|
@@ -1530,10 +1545,33 @@ export class AgentBridge {
|
|
|
1530
1545
|
if (!this.promptActivityTimer)
|
|
1531
1546
|
return;
|
|
1532
1547
|
clearTimeout(this.promptActivityTimer);
|
|
1533
|
-
this.promptActivityTimer =
|
|
1548
|
+
this.promptActivityTimer = this.schedulePromptActivityTimeout();
|
|
1549
|
+
}
|
|
1550
|
+
/** Arm a fresh inactivity watchdog timeout (used by bump/resume paths). */
|
|
1551
|
+
schedulePromptActivityTimeout() {
|
|
1552
|
+
return setTimeout(() => {
|
|
1534
1553
|
this.promptReject?.(new Error(`Agent prompt inactive for ${PROMPT_INACTIVITY_TIMEOUT_MS / 1000}s`));
|
|
1535
1554
|
}, PROMPT_INACTIVITY_TIMEOUT_MS);
|
|
1536
1555
|
}
|
|
1556
|
+
/**
|
|
1557
|
+
* Pause the parent inactivity watchdog while a subagent tool is running.
|
|
1558
|
+
* Idempotent per active subagent: the watchdog stays cleared until the last
|
|
1559
|
+
* in-flight subagent finishes.
|
|
1560
|
+
*/
|
|
1561
|
+
pausePromptActivityForSubagent() {
|
|
1562
|
+
this.activeSubagents += 1;
|
|
1563
|
+
if (this.promptActivityTimer) {
|
|
1564
|
+
clearTimeout(this.promptActivityTimer);
|
|
1565
|
+
this.promptActivityTimer = undefined;
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
/** Re-arm the inactivity watchdog once no subagents remain in flight. */
|
|
1569
|
+
resumePromptActivityAfterSubagent() {
|
|
1570
|
+
this.activeSubagents = Math.max(0, this.activeSubagents - 1);
|
|
1571
|
+
if (this.activeSubagents === 0 && this.promptReject) {
|
|
1572
|
+
this.promptActivityTimer = this.schedulePromptActivityTimeout();
|
|
1573
|
+
}
|
|
1574
|
+
}
|
|
1537
1575
|
clearPromptTimers() {
|
|
1538
1576
|
if (this.promptActivityTimer) {
|
|
1539
1577
|
clearTimeout(this.promptActivityTimer);
|
|
@@ -1541,6 +1579,7 @@ export class AgentBridge {
|
|
|
1541
1579
|
}
|
|
1542
1580
|
this.clearToolExecutionTimer();
|
|
1543
1581
|
this.promptReject = undefined;
|
|
1582
|
+
this.activeSubagents = 0;
|
|
1544
1583
|
}
|
|
1545
1584
|
/**
|
|
1546
1585
|
* Start the per-turn tool execution backstop. Only the first
|
|
@@ -1777,9 +1816,22 @@ export class AgentBridge {
|
|
|
1777
1816
|
return;
|
|
1778
1817
|
}
|
|
1779
1818
|
case "tool_execution_start": {
|
|
1780
|
-
|
|
1819
|
+
// Subagents are exempt from the per-tool execution backstop: they run
|
|
1820
|
+
// with their own independent deadline (SPECTRAL_SUBAGENT_TIMEOUT_MS,
|
|
1821
|
+
// default 4h) and stream subagent_progress/subagent_tool_progress
|
|
1822
|
+
// events that already reset the inactivity watchdog. Applying the
|
|
1823
|
+
// shared TOOL_EXECUTION_TIMEOUT_MS ceiling here would abort a
|
|
1824
|
+
// legitimately long-running subagent (and its parent prompt) with a
|
|
1825
|
+
// bare "aborted" result.
|
|
1826
|
+
if (ev.toolName !== "subagent") {
|
|
1827
|
+
this.startToolExecutionTimer();
|
|
1828
|
+
}
|
|
1781
1829
|
// Emit subagent_start for subagent tool invocations (Option B frontend panels).
|
|
1782
1830
|
if (ev.toolName === "subagent") {
|
|
1831
|
+
// The parent prompt is now waiting on the child agent. Pause the
|
|
1832
|
+
// inactivity watchdog until the subagent finishes so a silent
|
|
1833
|
+
// subagent phase cannot abort the parent mid-delegation.
|
|
1834
|
+
this.pausePromptActivityForSubagent();
|
|
1783
1835
|
const args = ev.args;
|
|
1784
1836
|
const mode = args?.chain?.length > 0
|
|
1785
1837
|
? "chain"
|
|
@@ -1874,9 +1926,18 @@ export class AgentBridge {
|
|
|
1874
1926
|
return;
|
|
1875
1927
|
}
|
|
1876
1928
|
case "tool_execution_end": {
|
|
1877
|
-
|
|
1929
|
+
// Subagents don't arm the per-tool backstop (see tool_execution_start),
|
|
1930
|
+
// so they must not clear it either — otherwise a subagent ending while
|
|
1931
|
+
// a non-subagent tool is still running would drop that tool's ceiling.
|
|
1932
|
+
if (ev.toolName !== "subagent") {
|
|
1933
|
+
this.clearToolExecutionTimer();
|
|
1934
|
+
}
|
|
1878
1935
|
// Emit subagent_end for subagent tool completions.
|
|
1879
1936
|
if (ev.toolName === "subagent") {
|
|
1937
|
+
// Re-arm the parent inactivity watchdog once the last subagent
|
|
1938
|
+
// finishes. The subagent's own timeout already governed the child;
|
|
1939
|
+
// the parent resumes normal silence-based protection from here.
|
|
1940
|
+
this.resumePromptActivityAfterSubagent();
|
|
1880
1941
|
const result = ev.result;
|
|
1881
1942
|
const details = result?.details;
|
|
1882
1943
|
if (details?.results && details.results.length > 0) {
|
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
*
|
|
8
8
|
* This module converts any caught error into a short, safe, user-facing
|
|
9
9
|
* string. Raw errors are still logged server-side at the catch sites; this
|
|
10
|
-
* helper only controls the user-facing message.
|
|
10
|
+
* helper only controls the user-facing message. Unknown, non-transient errors
|
|
11
|
+
* keep a sanitized version of their underlying cause so the UI can show what
|
|
12
|
+
* actually happened instead of only a generic fallback.
|
|
11
13
|
*/
|
|
12
14
|
/**
|
|
13
15
|
* Classification regex reused from agent-loop.ts (retry classification).
|
|
@@ -22,10 +24,13 @@ export declare const PROVIDER_ERROR_PATTERNS: RegExp;
|
|
|
22
24
|
* 1. Rate limit / 429 / overloaded / 529 / "too many requests" /
|
|
23
25
|
* "temporarily rate-limited" → "Rate limit reached. Please try again in a moment."
|
|
24
26
|
* 2. timeout / econnreset / "fetch failed" / network → "Network error. Please try again."
|
|
25
|
-
* 3. Any other Error
|
|
27
|
+
* 3. Any other Error / string → a sanitized, concise version of the underlying
|
|
28
|
+
* cause. Non-string/non-Error values (or values whose cause is empty after
|
|
29
|
+
* sanitization) → "Something went wrong. Please try again."
|
|
26
30
|
*
|
|
27
|
-
*
|
|
28
|
-
* user_id, provider_name, is_byok,
|
|
31
|
+
* The sanitizer still strips JSON blobs, stack frames, URLs, absolute paths,
|
|
32
|
+
* and internal identifiers (org_..., user_id, provider_name, is_byok,
|
|
33
|
+
* upstream model names).
|
|
29
34
|
*/
|
|
30
35
|
export declare function humanizeProviderError(err: unknown): string;
|
|
31
36
|
//# sourceMappingURL=error-humanizer.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-humanizer.d.ts","sourceRoot":"","sources":["../../src/server/error-humanizer.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"error-humanizer.d.ts","sourceRoot":"","sources":["../../src/server/error-humanizer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,QACsN,CAAC;AAqC3P;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAa1D"}
|
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
*
|
|
8
8
|
* This module converts any caught error into a short, safe, user-facing
|
|
9
9
|
* string. Raw errors are still logged server-side at the catch sites; this
|
|
10
|
-
* helper only controls the user-facing message.
|
|
10
|
+
* helper only controls the user-facing message. Unknown, non-transient errors
|
|
11
|
+
* keep a sanitized version of their underlying cause so the UI can show what
|
|
12
|
+
* actually happened instead of only a generic fallback.
|
|
11
13
|
*/
|
|
12
14
|
/**
|
|
13
15
|
* Classification regex reused from agent-loop.ts (retry classification).
|
|
@@ -18,6 +20,7 @@ export const PROVIDER_ERROR_PATTERNS = /rate.?limit|429|overloaded|529|timeout|n
|
|
|
18
20
|
const RATE_LIMIT_MESSAGE = "Rate limit reached. Please try again in a moment.";
|
|
19
21
|
const NETWORK_ERROR_MESSAGE = "Network error. Please try again.";
|
|
20
22
|
const GENERIC_MESSAGE = "Something went wrong. Please try again.";
|
|
23
|
+
const MAX_USER_ERROR_LENGTH = 240;
|
|
21
24
|
/**
|
|
22
25
|
* Coerce any caught value into a single string suitable for regex matching,
|
|
23
26
|
* without ever returning it to the user. This is only used internally for
|
|
@@ -57,10 +60,13 @@ function toErrorString(err) {
|
|
|
57
60
|
* 1. Rate limit / 429 / overloaded / 529 / "too many requests" /
|
|
58
61
|
* "temporarily rate-limited" → "Rate limit reached. Please try again in a moment."
|
|
59
62
|
* 2. timeout / econnreset / "fetch failed" / network → "Network error. Please try again."
|
|
60
|
-
* 3. Any other Error
|
|
63
|
+
* 3. Any other Error / string → a sanitized, concise version of the underlying
|
|
64
|
+
* cause. Non-string/non-Error values (or values whose cause is empty after
|
|
65
|
+
* sanitization) → "Something went wrong. Please try again."
|
|
61
66
|
*
|
|
62
|
-
*
|
|
63
|
-
* user_id, provider_name, is_byok,
|
|
67
|
+
* The sanitizer still strips JSON blobs, stack frames, URLs, absolute paths,
|
|
68
|
+
* and internal identifiers (org_..., user_id, provider_name, is_byok,
|
|
69
|
+
* upstream model names).
|
|
64
70
|
*/
|
|
65
71
|
export function humanizeProviderError(err) {
|
|
66
72
|
const text = toErrorString(err);
|
|
@@ -73,5 +79,69 @@ export function humanizeProviderError(err) {
|
|
|
73
79
|
if (PROVIDER_ERROR_PATTERNS.test(text)) {
|
|
74
80
|
return NETWORK_ERROR_MESSAGE;
|
|
75
81
|
}
|
|
76
|
-
return GENERIC_MESSAGE;
|
|
82
|
+
return extractSafeErrorCause(err) || GENERIC_MESSAGE;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Extract the deepest, non-empty, human-readable cause from an Error/string
|
|
86
|
+
* and remove anything that could leak internal identifiers, credentials,
|
|
87
|
+
* JSON payloads, stack frames, URLs, or absolute filesystem paths.
|
|
88
|
+
*
|
|
89
|
+
* Non-Error, non-string values intentionally have no safe textual cause and
|
|
90
|
+
* fall back to the generic message.
|
|
91
|
+
*/
|
|
92
|
+
function extractSafeErrorCause(err) {
|
|
93
|
+
if (err instanceof Error) {
|
|
94
|
+
const parts = [];
|
|
95
|
+
let current = err;
|
|
96
|
+
while (current instanceof Error) {
|
|
97
|
+
const sanitized = sanitizeErrorCause(current.message);
|
|
98
|
+
if (sanitized)
|
|
99
|
+
parts.push(sanitized);
|
|
100
|
+
current = current.cause;
|
|
101
|
+
}
|
|
102
|
+
// Some SDKs attach a non-Error cause (usually a string or parsed JSON).
|
|
103
|
+
if (typeof current === "string") {
|
|
104
|
+
const sanitized = sanitizeErrorCause(current);
|
|
105
|
+
if (sanitized)
|
|
106
|
+
parts.push(sanitized);
|
|
107
|
+
}
|
|
108
|
+
return parts.length > 0 ? parts[parts.length - 1] : "";
|
|
109
|
+
}
|
|
110
|
+
if (typeof err === "string")
|
|
111
|
+
return sanitizeErrorCause(err);
|
|
112
|
+
return "";
|
|
113
|
+
}
|
|
114
|
+
function sanitizeErrorCause(raw) {
|
|
115
|
+
if (!raw)
|
|
116
|
+
return "";
|
|
117
|
+
let text = raw;
|
|
118
|
+
// Remove whole JSON payloads. Raw provider errors often wrap the cause in a
|
|
119
|
+
// JSON blob carrying org ids, provider names, and model names.
|
|
120
|
+
text = text.replace(/\{[\s\S]*\}/g, " ");
|
|
121
|
+
text = text.replace(/\[[\s\S]*\]/g, " ");
|
|
122
|
+
// Remove URLs (may embed signed tokens).
|
|
123
|
+
text = text.replace(/\bhttps?:\/\/\S+/gi, " ");
|
|
124
|
+
// Remove absolute filesystem paths (POSIX and Windows).
|
|
125
|
+
text = text.replace(/(?:^|[\s("'])\/[\w.@-]+(?:\/[\w.@-]+)*/g, " ");
|
|
126
|
+
text = text.replace(/(?:^|[\s("'])[A-Za-z]:[\\/][^\s]*/g, " ");
|
|
127
|
+
// Remove internal identifiers and sensitive key/value metadata. Key/value
|
|
128
|
+
// metadata is stripped first so an identifier inside a value is consumed
|
|
129
|
+
// with the key rather than leaving a dangling key that then eats the next
|
|
130
|
+
// word in the message.
|
|
131
|
+
text = text.replace(/\b(?:provider_name|user_id|is_byok|api[_-]?key|authorization|secret|token)\s*[:=]\s*[^\s,;]+/gi, " ");
|
|
132
|
+
text = text.replace(/\bmodel\s*[:=]\s*[^\s,;]+/gi, " ");
|
|
133
|
+
text = text.replace(/\borg_[A-Za-z0-9_-]+\b/gi, " ");
|
|
134
|
+
// Remove stack-trace frames (multi-line Error messages).
|
|
135
|
+
text = text.replace(/^\s+at\s+[^\n]*/gm, " ");
|
|
136
|
+
// Remove ANSI escape sequences and normalize whitespace/punctuation noise.
|
|
137
|
+
text = text
|
|
138
|
+
.replace(/\u001b\[[0-9;]*m/g, " ")
|
|
139
|
+
.replace(/\s+/g, " ")
|
|
140
|
+
.replace(/\s*([,;:])\s*(?=[,;:.])/g, "")
|
|
141
|
+
.trim();
|
|
142
|
+
if (!text)
|
|
143
|
+
return "";
|
|
144
|
+
if (text.length <= MAX_USER_ERROR_LENGTH)
|
|
145
|
+
return text;
|
|
146
|
+
return `${text.slice(0, MAX_USER_ERROR_LENGTH - 1).trimEnd()}…`;
|
|
77
147
|
}
|