@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.
@@ -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":"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"}
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/subagents were hitting it),
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 = setTimeout(() => {
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
- this.startToolExecutionTimer();
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
- this.clearToolExecutionTimer();
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 or non-Error value "Something went wrong. Please try again."
27
+ * 3. Any other Error / stringa 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
- * Never leaks raw messages, JSON blobs, or internal identifiers (org_...,
28
- * user_id, provider_name, is_byok, upstream model names).
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;;;;;;;;;;GAUG;AAEH;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,QACsN,CAAC;AAoC3P;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAa1D"}
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 or non-Error value "Something went wrong. Please try again."
63
+ * 3. Any other Error / stringa 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
- * Never leaks raw messages, JSON blobs, or internal identifiers (org_...,
63
- * user_id, provider_name, is_byok, upstream model names).
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aexol/spectral",
3
- "version": "0.9.176",
3
+ "version": "0.9.178",
4
4
  "description": "AI coding agent for Aexol with relay-based browser access.",
5
5
  "type": "module",
6
6
  "private": false,