@0xmaxma/claude-gateway 1.7.7 → 1.7.9

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.
Files changed (34) hide show
  1. package/dist/agent/runner.d.ts.map +1 -1
  2. package/dist/agent/runner.js +64 -3
  3. package/dist/agent/runner.js.map +1 -1
  4. package/dist/api/line-webhook-router.d.ts.map +1 -1
  5. package/dist/api/line-webhook-router.js +7 -15
  6. package/dist/api/line-webhook-router.js.map +1 -1
  7. package/dist/api/slack-client.d.ts +25 -2
  8. package/dist/api/slack-client.d.ts.map +1 -1
  9. package/dist/api/slack-client.js +102 -0
  10. package/dist/api/slack-client.js.map +1 -1
  11. package/dist/api/slack-webhook-router.d.ts +15 -1
  12. package/dist/api/slack-webhook-router.d.ts.map +1 -1
  13. package/dist/api/slack-webhook-router.js +128 -1
  14. package/dist/api/slack-webhook-router.js.map +1 -1
  15. package/dist/session/compactor.d.ts +32 -2
  16. package/dist/session/compactor.d.ts.map +1 -1
  17. package/dist/session/compactor.js +134 -19
  18. package/dist/session/compactor.js.map +1 -1
  19. package/dist/session/process.d.ts +14 -0
  20. package/dist/session/process.d.ts.map +1 -1
  21. package/dist/session/process.js +85 -3
  22. package/dist/session/process.js.map +1 -1
  23. package/dist/shared/image-sniff.d.ts +15 -0
  24. package/dist/shared/image-sniff.d.ts.map +1 -0
  25. package/dist/shared/image-sniff.js +29 -0
  26. package/dist/shared/image-sniff.js.map +1 -0
  27. package/dist/shell/claude-pty-shell.js +13 -0
  28. package/dist/shell/claude-pty-shell.js.map +1 -1
  29. package/mcp/tools/discord/module.ts +1 -2
  30. package/mcp/tools/shared/limits.ts +14 -0
  31. package/mcp/tools/slack/module.ts +66 -4
  32. package/mcp/tools/telegram/module.ts +1 -1
  33. package/mcp/tools/telegram/receiver-server.ts +1 -1
  34. package/package.json +1 -1
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.SessionCompactor = exports.NotEnoughMessagesError = void 0;
37
+ exports.defaultSpawnClaude = defaultSpawnClaude;
37
38
  const fs = __importStar(require("fs"));
38
39
  const path = __importStar(require("path"));
39
40
  const child_process_1 = require("child_process");
@@ -55,15 +56,83 @@ const MERGE_SUMMARIES_PROMPT = 'These are summaries of sequential parts of a con
55
56
  const SINGLE_SUMMARY_INSTRUCTION = 'Summarize this conversation concisely, preserving key facts, decisions, context the assistant should remember, and any open questions or unfinished tasks.';
56
57
  // System prompt prepended to every claude CLI summarization call
57
58
  const COMPACTOR_SYSTEM_PROMPT = 'You are a conversation archiver. Your ONLY task is to produce a concise summary of the conversation transcript below. Do NOT respond to the conversation. Do NOT ask questions. Output ONLY the summary.';
59
+ // Max concurrent `claude --print` chunk-summary calls in flight at once. Bounded
60
+ // (not "all N chunks at once") to cap worst-case concurrent model calls/memory,
61
+ // while still cutting sequential wall time roughly by this factor on large sessions.
62
+ const CHUNK_CONCURRENCY = 4;
63
+ // Attempts per chunk (1 initial + retries) before degrading to a truncated raw
64
+ // excerpt instead of failing the whole compact job over one bad chunk.
65
+ const CHUNK_MAX_ATTEMPTS = 2;
66
+ // Raw-excerpt length used when a chunk exhausts its summarization attempts.
67
+ const DEGRADED_EXCERPT_CHARS = 2000;
68
+ // Hard ceiling on a single `claude --print` summarization spawn — mirrors the
69
+ // prior spawnSync timeout, but non-blocking (see defaultSpawnClaude below).
70
+ const SUMMARY_TIMEOUT_MS = 300000;
58
71
  // Rough token estimate: ~4 chars per token
59
72
  function estimateTokens(messages) {
60
73
  return Math.round(messages.reduce((acc, m) => acc + m.content.length, 0) / 4);
61
74
  }
75
+ /**
76
+ * Async `claude` CLI spawn. Replaces the prior `spawnSync` call: `spawnSync`
77
+ * blocks the daemon's single event loop for the full duration of the call — and
78
+ * every agent shares one process, so one compaction call froze ALL agents/
79
+ * channels until it returned (up to SUMMARY_TIMEOUT_MS, x N chunks sequentially
80
+ * on large sessions). `spawn` yields the event loop while the child runs.
81
+ * Mirrors the sync `{status, stdout, stderr, error}` contract so callers'
82
+ * existing error handling is unchanged.
83
+ */
84
+ function defaultSpawnClaude(bin, args, input) {
85
+ return new Promise((resolve) => {
86
+ let settled = false;
87
+ let stdout = '';
88
+ let stderr = '';
89
+ const settle = (r) => {
90
+ if (settled)
91
+ return;
92
+ settled = true;
93
+ clearTimeout(timer);
94
+ resolve(r);
95
+ };
96
+ const timer = setTimeout(() => {
97
+ try {
98
+ child.kill('SIGKILL');
99
+ }
100
+ catch {
101
+ /* already gone */
102
+ }
103
+ settle({ status: null, stdout: '', stderr, error: new Error(`claude CLI timed out after ${SUMMARY_TIMEOUT_MS}ms`) });
104
+ }, SUMMARY_TIMEOUT_MS);
105
+ const child = (0, child_process_1.spawn)(bin, args, {
106
+ stdio: ['pipe', 'pipe', 'pipe'],
107
+ env: { ...process.env, PATH: (0, claude_bin_1.pathWithNativeBin)() },
108
+ });
109
+ child.stdout?.on('data', (d) => {
110
+ stdout += String(d);
111
+ });
112
+ child.stderr?.on('data', (d) => {
113
+ stderr += String(d);
114
+ });
115
+ child.on('error', (err) => settle({ status: null, stdout: '', stderr, error: err }));
116
+ child.on('close', (code) => settle({ status: code, stdout, stderr }));
117
+ // stdin emits its own async 'error' (e.g. EPIPE if the child exits before we
118
+ // finish writing) separate from the child's 'error' event — must be handled
119
+ // or an unhandled stream error crashes the daemon.
120
+ child.stdin?.on('error', () => { });
121
+ try {
122
+ child.stdin?.write(input);
123
+ child.stdin?.end();
124
+ }
125
+ catch (err) {
126
+ settle({ status: null, stdout: '', stderr, error: err });
127
+ }
128
+ });
129
+ }
62
130
  class SessionCompactor {
63
- constructor(sessionStore) {
131
+ constructor(sessionStore, spawnClaude = defaultSpawnClaude) {
64
132
  this.sessionStore = sessionStore;
133
+ this.spawnClaude = spawnClaude;
65
134
  }
66
- async compact(agentId, chatId, sessionId, model, contextWindow, channel = 'telegram') {
135
+ async compact(agentId, chatId, sessionId, model, contextWindow, channel = 'telegram', onProgress) {
67
136
  // Load current history
68
137
  const messages = await this.sessionStore.loadTelegramSession(agentId, chatId, sessionId, channel);
69
138
  if (messages.length < 5) {
@@ -83,7 +152,7 @@ class SessionCompactor {
83
152
  const historyText = toSummarize
84
153
  .map(m => `${m.role === 'user' ? 'User' : 'Assistant'}: ${m.content}`)
85
154
  .join('\n\n');
86
- const summaryText = await this.summarizeWithChunking(historyText, model);
155
+ const summaryText = await this.summarizeWithChunking(historyText, model, onProgress);
87
156
  const compacted = [
88
157
  { role: 'system', content: `[Conversation Summary]\n${summaryText}`, ts: Date.now() },
89
158
  ...tail,
@@ -97,21 +166,72 @@ class SessionCompactor {
97
166
  const contextPctAfter = Math.round((afterTokens / contextWindow) * 100);
98
167
  return { beforeMessages, afterMessages, beforeTokens, afterTokens, reductionPct, contextPctBefore, contextPctAfter };
99
168
  }
100
- async summarizeWithChunking(historyText, model) {
101
- // If small enough, summarize directly
169
+ async summarizeWithChunking(historyText, model, onProgress) {
170
+ // If small enough, summarize directly. No chunking → no chunk to retry/degrade
171
+ // against, so a failure here still fails the compact job (original behavior).
102
172
  if (historyText.length <= CHUNK_CHARS) {
103
173
  return this.callClaudeForSummary(historyText, model);
104
174
  }
105
- // Split into chunks and summarize each
175
+ // Split into chunks and summarize with bounded concurrency; a single bad
176
+ // chunk degrades to a raw excerpt instead of failing the whole job.
106
177
  const chunks = this.splitIntoChunks(historyText, CHUNK_CHARS);
107
- const chunkSummaries = [];
108
- for (let i = 0; i < chunks.length; i++) {
109
- const summary = await this.callClaudeForSummary(chunks[i], model, `This is part ${i + 1} of ${chunks.length} of a longer conversation. Summarize this segment concisely.`);
110
- chunkSummaries.push(`[Part ${i + 1}/${chunks.length}]\n${summary}`);
111
- }
112
- // Merge chunk summaries into final summary
178
+ const chunkSummaries = await this.summarizeChunksConcurrently(chunks, model, onProgress);
179
+ // Merge chunk summaries into final summary. If the merge call itself can't
180
+ // be completed, fall back to the concatenated per-part summaries rather than
181
+ // losing the whole compaction — still far smaller than the raw history.
113
182
  const mergedText = chunkSummaries.join('\n\n');
114
- return this.callClaudeForSummary(mergedText, model, MERGE_SUMMARIES_PROMPT);
183
+ return this.mergeChunkSummaries(mergedText, model);
184
+ }
185
+ /** Bounded worker pool (mirrors the pattern in apps/installer.ts restoreRunningApps): at
186
+ * most CHUNK_CONCURRENCY `claude --print` calls in flight; results land by index so
187
+ * output order matches chunk order regardless of completion order. */
188
+ async summarizeChunksConcurrently(chunks, model, onProgress) {
189
+ const results = new Array(chunks.length);
190
+ let completed = 0;
191
+ let cursor = 0;
192
+ const worker = async () => {
193
+ while (cursor < chunks.length) {
194
+ const i = cursor++;
195
+ results[i] = await this.summarizeChunkWithFallback(chunks[i], model, i, chunks.length);
196
+ completed++;
197
+ onProgress?.(completed, chunks.length);
198
+ }
199
+ };
200
+ const poolSize = Math.min(CHUNK_CONCURRENCY, chunks.length);
201
+ await Promise.all(Array.from({ length: poolSize }, () => worker()));
202
+ return results;
203
+ }
204
+ /** Summarize one chunk; after CHUNK_MAX_ATTEMPTS failures, degrade to a truncated raw
205
+ * excerpt instead of throwing, so one bad chunk can't fail the whole compact job. */
206
+ async summarizeChunkWithFallback(chunk, model, index, total) {
207
+ const instruction = `This is part ${index + 1} of ${total} of a longer conversation. Summarize this segment concisely.`;
208
+ let lastErr;
209
+ for (let attempt = 1; attempt <= CHUNK_MAX_ATTEMPTS; attempt++) {
210
+ try {
211
+ const summary = await this.callClaudeForSummary(chunk, model, instruction);
212
+ return `[Part ${index + 1}/${total}]\n${summary}`;
213
+ }
214
+ catch (err) {
215
+ lastErr = err;
216
+ }
217
+ }
218
+ const excerpt = chunk.length > DEGRADED_EXCERPT_CHARS ? `${chunk.slice(0, DEGRADED_EXCERPT_CHARS)}…(truncated)` : chunk;
219
+ console.error(`[SessionCompactor] Chunk ${index + 1}/${total} summarization failed after ${CHUNK_MAX_ATTEMPTS} attempts (${lastErr?.message ?? 'unknown error'}); using truncated raw excerpt.`);
220
+ return `[Part ${index + 1}/${total} — summarization failed after ${CHUNK_MAX_ATTEMPTS} attempts (${lastErr?.message ?? 'unknown error'}); showing truncated raw excerpt]\n${excerpt}`;
221
+ }
222
+ /** Merge per-chunk summaries into one; degrade to the unmerged concatenation on repeated failure. */
223
+ async mergeChunkSummaries(mergedText, model) {
224
+ let lastErr;
225
+ for (let attempt = 1; attempt <= CHUNK_MAX_ATTEMPTS; attempt++) {
226
+ try {
227
+ return await this.callClaudeForSummary(mergedText, model, MERGE_SUMMARIES_PROMPT);
228
+ }
229
+ catch (err) {
230
+ lastErr = err;
231
+ }
232
+ }
233
+ console.error(`[SessionCompactor] Merge-summaries call failed after ${CHUNK_MAX_ATTEMPTS} attempts (${lastErr?.message ?? 'unknown error'}); using unmerged per-chunk summaries.`);
234
+ return mergedText;
115
235
  }
116
236
  splitIntoChunks(text, maxChars) {
117
237
  const chunks = [];
@@ -139,12 +259,7 @@ class SessionCompactor {
139
259
  // spawn after the native-installer migration.
140
260
  const claudeBinRaw = process.env.CLAUDE_BIN ?? (0, claude_bin_1.resolveClaudeBin)().bin;
141
261
  const [claudeBin, ...claudeBinArgs] = claudeBinRaw.split(' ');
142
- const result = (0, child_process_1.spawnSync)(claudeBin, [...claudeBinArgs, '--print', '--model', model], {
143
- input: prompt,
144
- encoding: 'utf-8',
145
- timeout: 300000,
146
- env: { ...process.env, PATH: (0, claude_bin_1.pathWithNativeBin)() },
147
- });
262
+ const result = await this.spawnClaude(claudeBin, [...claudeBinArgs, '--print', '--model', model], prompt);
148
263
  if (result.error) {
149
264
  throw new Error(`claude CLI error: ${result.error.message}`);
150
265
  }
@@ -1 +1 @@
1
- {"version":3,"file":"compactor.js","sourceRoot":"","sources":["../../src/session/compactor.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"compactor.js","sourceRoot":"","sources":["../../src/session/compactor.ts"],"names":[],"mappings":"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"}
@@ -32,9 +32,12 @@ export declare class SessionProcess extends EventEmitter {
32
32
  private process;
33
33
  private stopping;
34
34
  private restartCount;
35
+ private lastSpawnAt;
36
+ private _restartScheduled;
35
37
  private restartRequested;
36
38
  private _processing;
37
39
  private _pendingRestart;
40
+ private _exited;
38
41
  private interruptRequested;
39
42
  private restartWatcher;
40
43
  private readonly sessionStore;
@@ -159,6 +162,17 @@ export declare class SessionProcess extends EventEmitter {
159
162
  * the restart instead (see the deferredRestartReady handler in runner.ts).
160
163
  */
161
164
  hasPendingRestart(): boolean;
165
+ /**
166
+ * True while a crash-triggered auto-restart is in flight — armed by
167
+ * scheduleRestart() on child death and cleared once the replacement child
168
+ * attaches ('restarted'), the respawn fails ('restartFailed'), or the restart
169
+ * is abandoned because stop() raced in. Distinct from hasPendingRestart(),
170
+ * which tracks a *deferred* (graceful, turn-boundary) restart. The runner
171
+ * reads this to tell "a restart is coming, wait for it" apart from "dead with
172
+ * nothing coming, respawn a fresh session" — the latter would otherwise block
173
+ * on waitForSessionRestart's full timeout and wedge the caller. See #371.
174
+ */
175
+ isRestartScheduled(): boolean;
162
176
  touch(): void;
163
177
  isIdle(idleMs: number): boolean;
164
178
  isRunning(): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../src/session/process.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"process.d.ts","sourceRoot":"","sources":["../../src/session/process.ts"],"names":[],"mappings":"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"}
@@ -86,6 +86,14 @@ function resolveMaxHistoryMessages(agentMax, globalMax) {
86
86
  }
87
87
  const AUTO_RESTART_DELAY_MS = 5000;
88
88
  const MAX_RESTARTS = 3;
89
+ // A respawned child that stays alive at least this long counts as a healthy run
90
+ // rather than a member of a crash loop: on its eventual death the crash budget
91
+ // (restartCount) is reset so sporadic crashes spread across a long-lived
92
+ // session don't slowly accumulate toward a false permanent `failed`. Must be
93
+ // comfortably longer than the crash-loop window (AUTO_RESTART_DELAY_MS *
94
+ // MAX_RESTARTS = 15s) so a genuine tight crash loop never survives it and the
95
+ // MAX_RESTARTS backstop still fires. See issue #371.
96
+ const RESTART_COUNT_RESET_MS = 60000;
89
97
  // Bound how many times a single session may auto-respawn to recover from a
90
98
  // corrupted thinking block, so a recovery that never helps can't loop forever.
91
99
  const MAX_THINKING_RECOVERIES = 2;
@@ -133,9 +141,31 @@ class SessionProcess extends events_1.EventEmitter {
133
141
  this.process = null;
134
142
  this.stopping = false;
135
143
  this.restartCount = 0;
144
+ // Wall-clock time the current (or most recent) child was spawned. Used by the
145
+ // exit handler to measure how long that child survived, so a death after
146
+ // sustained healthy uptime resets the crash budget instead of counting toward
147
+ // MAX_RESTARTS. See RESTART_COUNT_RESET_MS.
148
+ this.lastSpawnAt = 0;
149
+ // True while a crash-triggered respawn is in flight — from the moment
150
+ // scheduleRestart() arms the AUTO_RESTART_DELAY_MS timer until the replacement
151
+ // child has attached (emit 'restarted') or the respawn failed/was abandoned.
152
+ // The runner reads this via isRestartScheduled() to distinguish "a restart is
153
+ // coming, wait for it" from "dead with nothing coming, respawn fresh" — the
154
+ // latter would otherwise wedge on waitForSessionRestart's full timeout. #371.
155
+ this._restartScheduled = false;
136
156
  this.restartRequested = false;
137
157
  this._processing = false;
138
158
  this._pendingRestart = false;
159
+ // True once we have OBSERVED the child actually exit (the 'exit' handler
160
+ // fired). Distinct from Node's ChildProcess.killed, which flips true the
161
+ // instant ANY signal is delivered — including a graceful /stop SIGINT that
162
+ // the pty-shell traps and SURVIVES (it forwards ESC to interrupt the TUI turn
163
+ // and keeps running). Liveness checks must use _exited, never .killed: a
164
+ // survived-SIGINT child is still alive, and treating it as dead wedges the
165
+ // session forever (isRunning() → false → the next turn waits on a restart
166
+ // that never comes; see runner.ts waitForSessionRestart). Reset to false on
167
+ // every fresh spawn.
168
+ this._exited = false;
139
169
  // Set by interrupt() right before SIGINT, cleared at the start of the next
140
170
  // sendMessage(). Lets a caller's process-exit handler tell a /stop-triggered
141
171
  // exit (CLI's SIGINT handler fell through to default termination instead of
@@ -658,6 +688,10 @@ class SessionProcess extends events_1.EventEmitter {
658
688
  stdio: ['pipe', 'pipe', 'pipe'],
659
689
  });
660
690
  this.process = proc;
691
+ // Fresh child is alive: clear any exit observed for a prior process (e.g.
692
+ // after an auto-restart), so isRunning()/interrupt() see it as live.
693
+ this._exited = false;
694
+ this.lastSpawnAt = Date.now();
661
695
  // Send initial prompt only for Telegram/Discord sessions.
662
696
  // API sessions receive the first message directly via sendApiMessage(),
663
697
  // so we cannot send an activation prompt here — it would race with the
@@ -956,6 +990,7 @@ class SessionProcess extends events_1.EventEmitter {
956
990
  if (ptyStreamSocketPath)
957
991
  pty_stream_registry_1.ptyStreamRegistry.close(ptyStreamSocketPath);
958
992
  this.process = null;
993
+ this._exited = true;
959
994
  // Notify listeners that the underlying subprocess died. The runner relies
960
995
  // on this to tear down per-chat typing/processing state when a session is
961
996
  // stopped or restarted mid-turn (without a final result/session_idle).
@@ -964,6 +999,16 @@ class SessionProcess extends events_1.EventEmitter {
964
999
  // rmSync(force)), so emitting on every child exit — including auto-restart
965
1000
  // — is safe.
966
1001
  this.emit('exit', code, signal);
1002
+ // A child that ran healthily for a sustained period before dying is not
1003
+ // part of a crash loop — reset the crash budget so an occasional crash
1004
+ // over a long-lived session doesn't slowly accumulate toward MAX_RESTARTS
1005
+ // and trip a false permanent `failed` (which would tear down a healthy
1006
+ // session and, for an API session, previously wedge it — see #371). A
1007
+ // rapid crash loop never survives RESTART_COUNT_RESET_MS, so the
1008
+ // MAX_RESTARTS backstop is preserved.
1009
+ if (this.lastSpawnAt && Date.now() - this.lastSpawnAt >= RESTART_COUNT_RESET_MS) {
1010
+ this.restartCount = 0;
1011
+ }
967
1012
  if (!this.stopping)
968
1013
  this.scheduleRestart();
969
1014
  });
@@ -999,6 +1044,10 @@ class SessionProcess extends events_1.EventEmitter {
999
1044
  return;
1000
1045
  }
1001
1046
  this.restartCount++;
1047
+ // A crash-triggered respawn is now committed (timer armed below). Mark it in
1048
+ // flight so waitForSessionRestart knows to wait for the replacement child
1049
+ // rather than fail fast. Cleared once the respawn settles (or is skipped).
1050
+ this._restartScheduled = true;
1002
1051
  this.logger.warn(`Scheduling session restart in ${AUTO_RESTART_DELAY_MS}ms`, {
1003
1052
  attempt: this.restartCount,
1004
1053
  });
@@ -1010,12 +1059,26 @@ class SessionProcess extends events_1.EventEmitter {
1010
1059
  // sendMessage() will no longer silently no-op. Emitted on every
1011
1060
  // successful crash-triggered respawn — cheap, and nothing currently
1012
1061
  // listens outside that one call site.
1013
- .then(() => this.emit('restarted'))
1062
+ .then(() => {
1063
+ this._restartScheduled = false;
1064
+ this.emit('restarted');
1065
+ })
1014
1066
  .catch(err => {
1067
+ this._restartScheduled = false;
1015
1068
  this.logger.error('restart failed', { error: err.message });
1016
1069
  this.emit('restartFailed', err);
1017
1070
  });
1018
1071
  }
1072
+ else {
1073
+ // stop() won the race before the timer fired — no respawn will happen.
1074
+ // Wake any waiter blocked on this restart (waitForSessionRestart) so it
1075
+ // rejects immediately instead of hanging for the full timeout: the
1076
+ // restarted/restartFailed/failed event it waits on would otherwise never
1077
+ // fire. Clearing the flag first also lets a fresh getOrSpawnSession call
1078
+ // take the respawn-fresh path. See #371.
1079
+ this._restartScheduled = false;
1080
+ this.emit('restartFailed', new Error('Session restart abandoned: session stopping'));
1081
+ }
1019
1082
  }, AUTO_RESTART_DELAY_MS);
1020
1083
  }
1021
1084
  /**
@@ -1183,7 +1246,10 @@ class SessionProcess extends events_1.EventEmitter {
1183
1246
  }
1184
1247
  }
1185
1248
  interrupt() {
1186
- if (!this.process || this.process.killed)
1249
+ // .killed is NOT liveness — it flips true on the FIRST SIGINT and stays
1250
+ // true, so a prior /stop would make every later interrupt() no-op even on a
1251
+ // live, actively-processing turn. Gate on _exited (child actually gone).
1252
+ if (!this.process || this._exited)
1187
1253
  return false;
1188
1254
  if (!this._processing)
1189
1255
  return false;
@@ -1221,6 +1287,19 @@ class SessionProcess extends events_1.EventEmitter {
1221
1287
  hasPendingRestart() {
1222
1288
  return this._pendingRestart;
1223
1289
  }
1290
+ /**
1291
+ * True while a crash-triggered auto-restart is in flight — armed by
1292
+ * scheduleRestart() on child death and cleared once the replacement child
1293
+ * attaches ('restarted'), the respawn fails ('restartFailed'), or the restart
1294
+ * is abandoned because stop() raced in. Distinct from hasPendingRestart(),
1295
+ * which tracks a *deferred* (graceful, turn-boundary) restart. The runner
1296
+ * reads this to tell "a restart is coming, wait for it" apart from "dead with
1297
+ * nothing coming, respawn a fresh session" — the latter would otherwise block
1298
+ * on waitForSessionRestart's full timeout and wedge the caller. See #371.
1299
+ */
1300
+ isRestartScheduled() {
1301
+ return this._restartScheduled;
1302
+ }
1224
1303
  touch() {
1225
1304
  this.lastActivityAt = Date.now();
1226
1305
  }
@@ -1228,7 +1307,10 @@ class SessionProcess extends events_1.EventEmitter {
1228
1307
  return Date.now() - this.lastActivityAt > idleMs;
1229
1308
  }
1230
1309
  isRunning() {
1231
- return this.process !== null && !this.process.killed;
1310
+ // Use _exited (child's 'exit' observed), NOT .killed — .killed is true after
1311
+ // any signal send, including a /stop SIGINT the pty-shell survives, which
1312
+ // would falsely report a healthy interrupted session as not-running.
1313
+ return this.process !== null && !this._exited;
1232
1314
  }
1233
1315
  async stop() {
1234
1316
  this.stopping = true;