@aexol/spectral 0.9.211 → 0.9.212
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/agent/agents.d.ts.map +1 -1
- package/dist/agent/agents.js +40 -0
- package/dist/commands/serve.d.ts.map +1 -1
- package/dist/commands/serve.js +11 -4
- package/dist/generated/zeus/const.d.ts.map +1 -1
- package/dist/generated/zeus/const.js +27 -0
- package/dist/generated/zeus/index.d.ts +122 -0
- package/dist/generated/zeus/index.d.ts.map +1 -1
- package/dist/relay/client.d.ts +11 -0
- package/dist/relay/client.d.ts.map +1 -1
- package/dist/relay/client.js +24 -6
- package/dist/server/session-stream.d.ts +2 -0
- package/dist/server/session-stream.d.ts.map +1 -1
- package/dist/server/session-stream.js +166 -9
- package/package.json +1 -1
package/dist/relay/client.d.ts
CHANGED
|
@@ -122,6 +122,17 @@ export declare class RelayClient extends EventEmitter {
|
|
|
122
122
|
/** Guards the async window of `scheduleReconnect` (health check). */
|
|
123
123
|
private reconnectInFlight;
|
|
124
124
|
constructor(opts: RelayClientOptions);
|
|
125
|
+
/**
|
|
126
|
+
* Emit a non-fatal error without being able to crash the host process.
|
|
127
|
+
*
|
|
128
|
+
* The `error` event is documented as non-fatal (parse failure, transport
|
|
129
|
+
* noise, send failure), but a bare `this.emit("error", ...)` on a Node
|
|
130
|
+
* EventEmitter THROWS when no listener is registered. Embeddings that
|
|
131
|
+
* never subscribe to `error` (SDK consumers, `serve` running in silent
|
|
132
|
+
* mode, ad-hoc clients in tests) would die on the first transport hiccup
|
|
133
|
+
* instead of logging it. When a listener exists, behaviour is unchanged.
|
|
134
|
+
*/
|
|
135
|
+
private emitError;
|
|
125
136
|
/** Open the connection. Idempotent — calling twice is a no-op. */
|
|
126
137
|
connect(): void;
|
|
127
138
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/relay/client.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../src/relay/client.ts"],"names":[],"mappings":"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"}
|
package/dist/relay/client.js
CHANGED
|
@@ -124,6 +124,24 @@ export class RelayClient extends EventEmitter {
|
|
|
124
124
|
this.exit = opts.exit ?? ((code) => process.exit(code));
|
|
125
125
|
this.runtimeInfo = opts.runtimeInfo;
|
|
126
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* Emit a non-fatal error without being able to crash the host process.
|
|
129
|
+
*
|
|
130
|
+
* The `error` event is documented as non-fatal (parse failure, transport
|
|
131
|
+
* noise, send failure), but a bare `this.emit("error", ...)` on a Node
|
|
132
|
+
* EventEmitter THROWS when no listener is registered. Embeddings that
|
|
133
|
+
* never subscribe to `error` (SDK consumers, `serve` running in silent
|
|
134
|
+
* mode, ad-hoc clients in tests) would die on the first transport hiccup
|
|
135
|
+
* instead of logging it. When a listener exists, behaviour is unchanged.
|
|
136
|
+
*/
|
|
137
|
+
emitError(err) {
|
|
138
|
+
if (this.listenerCount("error") > 0) {
|
|
139
|
+
this.emit("error", err);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
143
|
+
this.logger.warn(`Relay error: ${msg}`);
|
|
144
|
+
}
|
|
127
145
|
/** Open the connection. Idempotent — calling twice is a no-op. */
|
|
128
146
|
connect() {
|
|
129
147
|
if (this.disposed)
|
|
@@ -213,7 +231,7 @@ export class RelayClient extends EventEmitter {
|
|
|
213
231
|
parsed = JSON.parse(data.toString());
|
|
214
232
|
}
|
|
215
233
|
catch (err) {
|
|
216
|
-
this.
|
|
234
|
+
this.emitError(err);
|
|
217
235
|
return;
|
|
218
236
|
}
|
|
219
237
|
// Heartbeat: reply pong inline, do NOT propagate.
|
|
@@ -257,7 +275,7 @@ export class RelayClient extends EventEmitter {
|
|
|
257
275
|
// `ws` emits 'error' before 'close'. Reconnect is driven from the
|
|
258
276
|
// `close` handler (or `forceReconnect` for suspected-dead sockets);
|
|
259
277
|
// scheduling it here would double-schedule.
|
|
260
|
-
this.
|
|
278
|
+
this.emitError(err);
|
|
261
279
|
});
|
|
262
280
|
ws.on("close", (code, reason) => {
|
|
263
281
|
this.handleDisconnect(ws, code, reason?.toString() ?? "");
|
|
@@ -312,7 +330,7 @@ export class RelayClient extends EventEmitter {
|
|
|
312
330
|
clearTimeout(this.heartbeatPongTimer);
|
|
313
331
|
this.heartbeatPongTimer = null;
|
|
314
332
|
}
|
|
315
|
-
this.
|
|
333
|
+
this.emitError(err);
|
|
316
334
|
this.forceReconnect("heartbeat-error");
|
|
317
335
|
}
|
|
318
336
|
}, HEARTBEAT_INTERVAL_MS);
|
|
@@ -428,7 +446,7 @@ export class RelayClient extends EventEmitter {
|
|
|
428
446
|
clearTimeout(this.sendTimer);
|
|
429
447
|
this.sendTimer = null;
|
|
430
448
|
}
|
|
431
|
-
this.
|
|
449
|
+
this.emitError(err);
|
|
432
450
|
this.forceReconnect("send-error");
|
|
433
451
|
}
|
|
434
452
|
}
|
|
@@ -456,7 +474,7 @@ export class RelayClient extends EventEmitter {
|
|
|
456
474
|
ws.terminate();
|
|
457
475
|
}
|
|
458
476
|
catch (err) {
|
|
459
|
-
this.
|
|
477
|
+
this.emitError(err);
|
|
460
478
|
}
|
|
461
479
|
this.handleDisconnect(ws, 1006, reason);
|
|
462
480
|
}
|
|
@@ -641,7 +659,7 @@ export class RelayClient extends EventEmitter {
|
|
|
641
659
|
// A synchronous throw from the WebSocket constructor (DNS, bad
|
|
642
660
|
// URL, etc.) must not kill the daemon — log it and re-schedule
|
|
643
661
|
// so the backoff continues forever.
|
|
644
|
-
this.
|
|
662
|
+
this.emitError(err);
|
|
645
663
|
void this.scheduleReconnect().catch(() => { });
|
|
646
664
|
}
|
|
647
665
|
}, delay);
|
|
@@ -471,6 +471,8 @@ export declare class SessionStreamManager {
|
|
|
471
471
|
private scheduleIntervalTick;
|
|
472
472
|
private clearIntervalTimer;
|
|
473
473
|
private clearCurrentTurnSilenceTimer;
|
|
474
|
+
/** Clear subagent-in-flight bookkeeping (used by the silence safety net). */
|
|
475
|
+
private resetSubagentTracking;
|
|
474
476
|
private armCurrentTurnSilenceTimer;
|
|
475
477
|
private handleCurrentTurnSilenceTimeout;
|
|
476
478
|
private intervalTick;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-stream.d.ts","sourceRoot":"","sources":["../../src/server/session-stream.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"session-stream.d.ts","sourceRoot":"","sources":["../../src/server/session-stream.ts"],"names":[],"mappings":"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"}
|
|
@@ -84,8 +84,68 @@ const MAX_LOOP_ITERATIONS = 100;
|
|
|
84
84
|
// `stream.currentTurn` stays non-null forever. On reconnect, `session_ready`
|
|
85
85
|
// then reports `currentTurn != null`, so landing shows a phantom "streaming"
|
|
86
86
|
// status. This timer clears the stale turn and emits a synthetic `agent_end`
|
|
87
|
-
// after
|
|
88
|
-
|
|
87
|
+
// after 30 minutes of silence on streaming-meaningful events.
|
|
88
|
+
//
|
|
89
|
+
// Configurable via `SPECTRAL_TURN_SILENCE_TIMEOUT_MS`:
|
|
90
|
+
// * unset / empty / not-a-number / negative -> 30 minutes (default)
|
|
91
|
+
// * `0` -> DISABLED, the timer is never armed (official opt-out)
|
|
92
|
+
// * a positive fraction (e.g. `0.5`) is rounded UP to 1ms so it can never
|
|
93
|
+
// accidentally disable the safety net
|
|
94
|
+
// * anything larger than Node's max delay is clamped (see below)
|
|
95
|
+
const DEFAULT_TURN_SILENCE_TIMEOUT_MS = 30 * 60 * 1000;
|
|
96
|
+
/**
|
|
97
|
+
* Node emits `TimeoutOverflowWarning` and fires immediately for delays above
|
|
98
|
+
* this value, so anything larger is clamped to it.
|
|
99
|
+
*/
|
|
100
|
+
const MAX_TIMEOUT_DELAY_MS = 2_147_483_647;
|
|
101
|
+
/**
|
|
102
|
+
* Upper bound on how many times the silence timer may be re-armed instead of
|
|
103
|
+
* closing the turn while a subagent is still running. Guards against a
|
|
104
|
+
* counter stuck at >0 forever (e.g. a `subagent_end` that never arrives after
|
|
105
|
+
* a crash) turning the safety net off permanently.
|
|
106
|
+
* `SPECTRAL_TURN_SILENCE_MAX_REARM=0` means unlimited re-arms.
|
|
107
|
+
*/
|
|
108
|
+
const DEFAULT_TURN_SILENCE_MAX_REARM = 8;
|
|
109
|
+
/**
|
|
110
|
+
* Sanity clamp for `SPECTRAL_TURN_SILENCE_MAX_REARM`. Even with the shortest
|
|
111
|
+
* useful timeout this is days of silence, so a typo like `1e10` cannot keep a
|
|
112
|
+
* hung turn open effectively forever.
|
|
113
|
+
*/
|
|
114
|
+
const MAX_TURN_SILENCE_MAX_REARM = 1000;
|
|
115
|
+
function resolveTurnSilenceTimeoutMs() {
|
|
116
|
+
const raw = process.env.SPECTRAL_TURN_SILENCE_TIMEOUT_MS;
|
|
117
|
+
if (raw === undefined || raw.trim() === "") {
|
|
118
|
+
return DEFAULT_TURN_SILENCE_TIMEOUT_MS;
|
|
119
|
+
}
|
|
120
|
+
const parsed = Number(raw);
|
|
121
|
+
// `0` is the documented way to switch the safety net off entirely.
|
|
122
|
+
if (parsed === 0)
|
|
123
|
+
return 0;
|
|
124
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
125
|
+
return DEFAULT_TURN_SILENCE_TIMEOUT_MS;
|
|
126
|
+
}
|
|
127
|
+
// `Math.max(1, ...)`: a fractional value such as `0.5` must floor to 1ms,
|
|
128
|
+
// not to 0 — flooring to 0 would silently switch the safety net off.
|
|
129
|
+
return Math.min(Math.max(1, Math.floor(parsed)), MAX_TIMEOUT_DELAY_MS);
|
|
130
|
+
}
|
|
131
|
+
function resolveTurnSilenceMaxRearm() {
|
|
132
|
+
const raw = process.env.SPECTRAL_TURN_SILENCE_MAX_REARM;
|
|
133
|
+
// Empty / whitespace-only must NOT fall through to `Number(...)`, which
|
|
134
|
+
// coerces `""` to `0` and would therefore mean "unlimited re-arms" —
|
|
135
|
+
// i.e. the original "phantom streaming forever" bug.
|
|
136
|
+
if (raw === undefined || raw.trim() === "") {
|
|
137
|
+
return DEFAULT_TURN_SILENCE_MAX_REARM;
|
|
138
|
+
}
|
|
139
|
+
const parsed = Number(raw);
|
|
140
|
+
// `0` is the documented way to allow unlimited re-arms (opt-out of the
|
|
141
|
+
// re-arm budget).
|
|
142
|
+
if (parsed === 0)
|
|
143
|
+
return 0;
|
|
144
|
+
if (!Number.isFinite(parsed) || parsed < 0) {
|
|
145
|
+
return DEFAULT_TURN_SILENCE_MAX_REARM;
|
|
146
|
+
}
|
|
147
|
+
return Math.min(Math.max(1, Math.floor(parsed)), MAX_TURN_SILENCE_MAX_REARM);
|
|
148
|
+
}
|
|
89
149
|
// Streaming-meaningful events that reset the silence timer while a turn is
|
|
90
150
|
// active. Mirrors Fix C on the landing side, but EXCLUDES `token_usage`
|
|
91
151
|
// (arrives post-turn) and excludes terminal/control events handled by their
|
|
@@ -102,7 +162,17 @@ const STREAMING_MEANINGFUL_EVENT_TYPES = new Set([
|
|
|
102
162
|
"tool_result",
|
|
103
163
|
"message_end",
|
|
104
164
|
"subagent_start",
|
|
165
|
+
// WARNING: `subagent_progress` only resets the timer if it is ALSO
|
|
166
|
+
// replayable. The re-arm below lives inside `if (isReplayable(event))`,
|
|
167
|
+
// and `subagent_progress` is deliberately live-only (see `isReplayable`),
|
|
168
|
+
// so this entry is currently inert — `subagent_tool_progress` is the
|
|
169
|
+
// subagent heartbeat that actually works. Do not rely on this entry until
|
|
170
|
+
// the re-arm is hoisted out of the `isReplayable` gate.
|
|
105
171
|
"subagent_progress",
|
|
172
|
+
// `subagent_tool_progress` is the only live signal a subagent emits while
|
|
173
|
+
// it sits inside one long tool call (bash build/test). Without it a busy
|
|
174
|
+
// but "quiet" subagent looks like silence and the turn gets closed.
|
|
175
|
+
"subagent_tool_progress",
|
|
106
176
|
"subagent_end",
|
|
107
177
|
]);
|
|
108
178
|
/**
|
|
@@ -1207,6 +1277,9 @@ export class SessionStreamManager {
|
|
|
1207
1277
|
// next-turn events (including the upcoming `message_start`) are not
|
|
1208
1278
|
// rejected. The fence is re-armed if this turn later closes.
|
|
1209
1279
|
stream.closedTurnGeneration = null;
|
|
1280
|
+
// Fresh run: drop any subagent tracking left over from a previous turn
|
|
1281
|
+
// that ended without a matching `subagent_end`.
|
|
1282
|
+
this.resetSubagentTracking(stream);
|
|
1210
1283
|
// New logical run. This is the only place `busy` transitions
|
|
1211
1284
|
// false -> true (loop iterations and auto-dequeue also go through
|
|
1212
1285
|
// `prompt()`). Retries/continuations do NOT bump the generation.
|
|
@@ -1324,6 +1397,9 @@ export class SessionStreamManager {
|
|
|
1324
1397
|
// the synthetic agent_end below isn't later double-emitted by a stale
|
|
1325
1398
|
// timer.
|
|
1326
1399
|
this.clearCurrentTurnSilenceTimer(stream);
|
|
1400
|
+
// The bridge is being torn down, so no `subagent_end` will arrive for any
|
|
1401
|
+
// subagent that is still running. Drop the tracking state.
|
|
1402
|
+
this.resetSubagentTracking(stream);
|
|
1327
1403
|
// Dispose the agent bridge immediately — this tears down spectral's session and
|
|
1328
1404
|
// unsubscribe. The bridge's own event handler is detached; no further
|
|
1329
1405
|
// events will flow. We broadcast agent_end ourselves below.
|
|
@@ -1523,11 +1599,21 @@ export class SessionStreamManager {
|
|
|
1523
1599
|
stream.currentTurnSilenceTimer = null;
|
|
1524
1600
|
}
|
|
1525
1601
|
}
|
|
1602
|
+
/** Clear subagent-in-flight bookkeeping (used by the silence safety net). */
|
|
1603
|
+
resetSubagentTracking(stream) {
|
|
1604
|
+
stream.activeSubagentToolCallIds.clear();
|
|
1605
|
+
stream.currentTurnSilenceRearmCount = 0;
|
|
1606
|
+
}
|
|
1526
1607
|
armCurrentTurnSilenceTimer(stream) {
|
|
1527
1608
|
this.clearCurrentTurnSilenceTimer(stream);
|
|
1609
|
+
const timeoutMs = resolveTurnSilenceTimeoutMs();
|
|
1610
|
+
// `0` (or any disabled value) means the safety net is switched off: never
|
|
1611
|
+
// arm the timer at all.
|
|
1612
|
+
if (timeoutMs <= 0)
|
|
1613
|
+
return;
|
|
1528
1614
|
stream.currentTurnSilenceTimer = setTimeout(() => {
|
|
1529
1615
|
this.handleCurrentTurnSilenceTimeout(stream);
|
|
1530
|
-
},
|
|
1616
|
+
}, timeoutMs);
|
|
1531
1617
|
}
|
|
1532
1618
|
handleCurrentTurnSilenceTimeout(stream) {
|
|
1533
1619
|
stream.currentTurnSilenceTimer = null;
|
|
@@ -1538,8 +1624,36 @@ export class SessionStreamManager {
|
|
|
1538
1624
|
if (stream.currentTurn == null)
|
|
1539
1625
|
return;
|
|
1540
1626
|
const sessionId = stream.sessionId;
|
|
1627
|
+
const maxRearm = resolveTurnSilenceMaxRearm();
|
|
1628
|
+
// Set when the re-arm budget is exhausted while subagents are still
|
|
1629
|
+
// tracked in flight: the turn is closed anyway and the UI needs a
|
|
1630
|
+
// distinguishable signal (see the `error` broadcast below).
|
|
1631
|
+
let stuckSubagentCount = 0;
|
|
1632
|
+
// A subagent is still running. Closing the turn now would flush the
|
|
1633
|
+
// buffer and set `closedTurnGeneration`, which silently discards every
|
|
1634
|
+
// replayable event the subagent emits afterwards (`subagent_end`,
|
|
1635
|
+
// `tool_result`, `message_end`) while it keeps burning tokens. Re-arm
|
|
1636
|
+
// instead, up to a bounded number of times.
|
|
1637
|
+
if (stream.activeSubagentToolCallIds.size > 0) {
|
|
1638
|
+
const unlimited = maxRearm === 0;
|
|
1639
|
+
if (unlimited || stream.currentTurnSilenceRearmCount < maxRearm) {
|
|
1640
|
+
stream.currentTurnSilenceRearmCount += 1;
|
|
1641
|
+
console.warn(`[session-stream] currentTurn silence timeout deferred: ` +
|
|
1642
|
+
`${stream.activeSubagentToolCallIds.size} subagent(s) still active ` +
|
|
1643
|
+
`in session ${sessionId}, re-arming ` +
|
|
1644
|
+
`(${stream.currentTurnSilenceRearmCount}/${unlimited ? "unlimited" : maxRearm})`);
|
|
1645
|
+
this.armCurrentTurnSilenceTimer(stream);
|
|
1646
|
+
return;
|
|
1647
|
+
}
|
|
1648
|
+
stuckSubagentCount = stream.activeSubagentToolCallIds.size;
|
|
1649
|
+
console.warn(`[session-stream] currentTurn silence timeout reached max re-arms ` +
|
|
1650
|
+
`(${maxRearm}) with ${stream.activeSubagentToolCallIds.size} ` +
|
|
1651
|
+
`subagent(s) still active in session ${sessionId}; ` +
|
|
1652
|
+
`closing the turn anyway`);
|
|
1653
|
+
}
|
|
1654
|
+
stream.currentTurnSilenceRearmCount = 0;
|
|
1541
1655
|
console.warn(`[session-stream] currentTurn silence timeout fired ` +
|
|
1542
|
-
`(${
|
|
1656
|
+
`(${resolveTurnSilenceTimeoutMs() / 1000}s without a ` +
|
|
1543
1657
|
`streaming-meaningful event), emitting synthetic agent_end ` +
|
|
1544
1658
|
`for session ${sessionId}`);
|
|
1545
1659
|
const finishedTurn = stream.currentTurn;
|
|
@@ -1547,6 +1661,22 @@ export class SessionStreamManager {
|
|
|
1547
1661
|
stream.lastFlushedEventCount = 0;
|
|
1548
1662
|
stream.currentTurn = null;
|
|
1549
1663
|
stream.closedTurnGeneration = stream.bridgeTurnGeneration;
|
|
1664
|
+
// Distinguish "gave up on a still-running subagent" from a clean
|
|
1665
|
+
// synthetic `agent_end`: without this the UI sees an ordinary turn end
|
|
1666
|
+
// and the subagent's result (which is dropped on the
|
|
1667
|
+
// `closedTurnGeneration` fence) disappears without any explanation.
|
|
1668
|
+
if (stuckSubagentCount > 0) {
|
|
1669
|
+
console.warn(`[session-stream] turn silence timeout exhausted after ${maxRearm} ` +
|
|
1670
|
+
`re-arm(s) while ${stuckSubagentCount} subagent(s) were still ` +
|
|
1671
|
+
`active in session ${sessionId}; their remaining events will be ` +
|
|
1672
|
+
`dropped on the closed-turn fence`);
|
|
1673
|
+
this.broadcast(stream, {
|
|
1674
|
+
type: "error",
|
|
1675
|
+
message: `Turn silence timeout exhausted after ${maxRearm} re-arm(s) while ` +
|
|
1676
|
+
`${stuckSubagentCount} subagent(s) were still active. The turn was ` +
|
|
1677
|
+
`closed and any result they emit from now on will be discarded.`,
|
|
1678
|
+
});
|
|
1679
|
+
}
|
|
1550
1680
|
// Reuse the normal broadcast path so relay subscribers (landing
|
|
1551
1681
|
// WebSocket clients) receive agent_end and stop showing "streaming".
|
|
1552
1682
|
this.broadcast(stream, { type: "agent_end" });
|
|
@@ -1706,6 +1836,8 @@ export class SessionStreamManager {
|
|
|
1706
1836
|
currentTurn: null,
|
|
1707
1837
|
bridgeTurnGeneration: 0,
|
|
1708
1838
|
closedTurnGeneration: null,
|
|
1839
|
+
activeSubagentToolCallIds: new Set(),
|
|
1840
|
+
currentTurnSilenceRearmCount: 0,
|
|
1709
1841
|
runState: {
|
|
1710
1842
|
runId: "",
|
|
1711
1843
|
generation: 0,
|
|
@@ -1889,6 +2021,24 @@ export class SessionStreamManager {
|
|
|
1889
2021
|
if (dcpLiteEvent) {
|
|
1890
2022
|
stream.lastDcpEvent = dcpLiteEvent;
|
|
1891
2023
|
}
|
|
2024
|
+
// Track in-flight subagents (tracked BEFORE the `closedTurnGeneration`
|
|
2025
|
+
// fence below so a late `subagent_end` can still clear the counter).
|
|
2026
|
+
// The silence timer uses this to avoid closing a turn out from under a
|
|
2027
|
+
// still-working subagent.
|
|
2028
|
+
if (event.type === "subagent_start") {
|
|
2029
|
+
// Runtime payloads from the SDK are not typechecked. Without this guard
|
|
2030
|
+
// a `toolCallId` of `undefined`/`""` would leave the set size pinned at
|
|
2031
|
+
// 1 forever (the matching `subagent_end` could never delete it), which
|
|
2032
|
+
// permanently disables the silence safety net for the whole session.
|
|
2033
|
+
if (event.toolCallId) {
|
|
2034
|
+
stream.activeSubagentToolCallIds.add(event.toolCallId);
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
2037
|
+
else if (event.type === "subagent_end") {
|
|
2038
|
+
if (event.toolCallId) {
|
|
2039
|
+
stream.activeSubagentToolCallIds.delete(event.toolCallId);
|
|
2040
|
+
}
|
|
2041
|
+
}
|
|
1892
2042
|
// Buffer replayable events into the in-flight turn. We intentionally
|
|
1893
2043
|
// accept events even if currentTurn is null (rare race: spectral emits before
|
|
1894
2044
|
// prompt() opened the turn), in which case we open one defensively so
|
|
@@ -1957,11 +2107,12 @@ export class SessionStreamManager {
|
|
|
1957
2107
|
BATCH_FLUSH_INTERVAL) {
|
|
1958
2108
|
this.flushInFlightTurn(stream);
|
|
1959
2109
|
}
|
|
1960
|
-
// Fix E: reset the
|
|
1961
|
-
//
|
|
1962
|
-
//
|
|
1963
|
-
//
|
|
1964
|
-
//
|
|
2110
|
+
// Fix E: reset the turn silence timer (default 30 minutes, see
|
|
2111
|
+
// `SPECTRAL_TURN_SILENCE_TIMEOUT_MS` / `resolveTurnSilenceTimeoutMs`)
|
|
2112
|
+
// whenever a streaming-meaningful event arrives while a turn is
|
|
2113
|
+
// active. As long as the LLM keeps streaming (even slowly), the timer
|
|
2114
|
+
// keeps resetting. `token_usage` is intentionally excluded (arrives
|
|
2115
|
+
// post-turn and would prevent cleanup of a hung turn).
|
|
1965
2116
|
if (stream.currentTurn &&
|
|
1966
2117
|
STREAMING_MEANINGFUL_EVENT_TYPES.has(event.type)) {
|
|
1967
2118
|
this.armCurrentTurnSilenceTimer(stream);
|
|
@@ -2137,6 +2288,9 @@ export class SessionStreamManager {
|
|
|
2137
2288
|
// Fix E: natural agent_end arrived — cancel the safety-net timer so we
|
|
2138
2289
|
// don't double-emit.
|
|
2139
2290
|
this.clearCurrentTurnSilenceTimer(stream);
|
|
2291
|
+
// Defensive: a `subagent_end` may never arrive (crashed subagent), so
|
|
2292
|
+
// never let the counter stay >0 into the next turn.
|
|
2293
|
+
this.resetSubagentTracking(stream);
|
|
2140
2294
|
// Keep the run busy across the agent_end -> continuation gap when the
|
|
2141
2295
|
// SDK signals it will re-enter the loop (retry, length continuation,
|
|
2142
2296
|
// compact-and-retry). The deferred settle below re-resolves the final
|
|
@@ -2249,6 +2403,9 @@ export class SessionStreamManager {
|
|
|
2249
2403
|
// Fix E: error terminates the turn — cancel the safety-net timer so we
|
|
2250
2404
|
// don't double-emit.
|
|
2251
2405
|
this.clearCurrentTurnSilenceTimer(stream);
|
|
2406
|
+
// Defensive: same as agent_end — a failed turn can leave subagents
|
|
2407
|
+
// without a matching `subagent_end`.
|
|
2408
|
+
this.resetSubagentTracking(stream);
|
|
2252
2409
|
this.runTurnCleanup(failedTurn);
|
|
2253
2410
|
this.settleRunState(stream);
|
|
2254
2411
|
}
|