@0xmaxma/claude-gateway 1.7.0 → 1.7.2
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/runner.d.ts +10 -0
- package/dist/agent/runner.d.ts.map +1 -1
- package/dist/agent/runner.js +141 -13
- package/dist/agent/runner.js.map +1 -1
- package/dist/session/process.d.ts +10 -0
- package/dist/session/process.d.ts.map +1 -1
- package/dist/session/process.js +42 -3
- package/dist/session/process.js.map +1 -1
- package/mcp/server.ts +35 -4
- package/mcp/tools/image/module.ts +86 -11
- package/mcp/types.ts +9 -3
- package/package.json +1 -1
package/dist/agent/runner.d.ts
CHANGED
|
@@ -188,6 +188,16 @@ export declare class AgentRunner extends EventEmitter {
|
|
|
188
188
|
*/
|
|
189
189
|
static remapImageParamsRefs(p: ImageParams | undefined, stagedPaths: readonly string[] | undefined, promotedPaths: readonly string[] | undefined): ImageParams | undefined;
|
|
190
190
|
private static buildChannelXml;
|
|
191
|
+
/**
|
|
192
|
+
* Await a SessionProcess's in-flight crash-triggered auto-restart
|
|
193
|
+
* (scheduleRestart's setTimeout → spawnProcess) instead of handing back a
|
|
194
|
+
* process whose sendMessage() will silently no-op. Resolves once the new
|
|
195
|
+
* child is attached ('restarted'); rejects on a definitive restart failure
|
|
196
|
+
* ('restartFailed', or 'failed' once MAX_RESTARTS is exhausted) or, as a
|
|
197
|
+
* last resort, on SESSION_RESTART_WAIT_TIMEOUT_MS so a caller is never left
|
|
198
|
+
* hanging past that bound.
|
|
199
|
+
*/
|
|
200
|
+
private waitForSessionRestart;
|
|
191
201
|
private getOrSpawnSession;
|
|
192
202
|
private spawnSession;
|
|
193
203
|
static isApiBuiltinCommand(content: string): boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/agent/runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAMtC,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,EAAW,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAS7H,OAAO,EAA0C,KAAK,aAAa,EAAE,MAAM,WAAW,CAAC;AAOvF,OAAO,EAAiC,KAAK,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAG7F,OAAO,EAAE,SAAS,EAAE,MAAM,eAAe,CAAC;AAI1C,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAmB7D,eAAO,MAAM,oBAAoB,QAAmB,CAAC;AAErD,eAAO,MAAM,cAAc,EAAE,WAAW,EAYvC,CAAC;
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../src/agent/runner.ts"],"names":[],"mappings":"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"}
|
package/dist/agent/runner.js
CHANGED
|
@@ -100,6 +100,11 @@ const MAX_API_IMAGES = 5;
|
|
|
100
100
|
* CLI turn is still producing a result that must reach history, exactly like
|
|
101
101
|
* the client-disconnect path. This cap bounds a genuinely hung turn. */
|
|
102
102
|
const API_TIMEOUT_HARD_CAP_EXTRA_MS = 600000;
|
|
103
|
+
// Bound on getOrSpawnSession's wait for a crash-triggered auto-restart to land
|
|
104
|
+
// (session/process.ts's AUTO_RESTART_DELAY_MS is 5s, plus real CLI spawn time
|
|
105
|
+
// observed up to a few seconds more) — generous, but must not be the long
|
|
106
|
+
// API_TIMEOUT_HARD_CAP_EXTRA_MS chain: this is a pre-turn gap, not a turn.
|
|
107
|
+
const SESSION_RESTART_WAIT_TIMEOUT_MS = 20000;
|
|
103
108
|
// Hard timeout for the one-shot local `claude -p` triage during recovery
|
|
104
109
|
// (Epic #195, Phase 3b). A slow/hung triage collapses to a safe notify-only.
|
|
105
110
|
const RECOVERY_TRIAGE_TIMEOUT_MS = 15000;
|
|
@@ -1162,6 +1167,37 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
1162
1167
|
`message_id="${meta['message_id'] ?? ''}" user="${AgentRunner.escapeXmlAttr(meta['user'] ?? '')}" ` +
|
|
1163
1168
|
`ts="${meta['ts'] ?? new Date().toISOString()}"${optionalAttrs}>${repliedBlock}${params.content ?? ''}</channel>`);
|
|
1164
1169
|
}
|
|
1170
|
+
/**
|
|
1171
|
+
* Await a SessionProcess's in-flight crash-triggered auto-restart
|
|
1172
|
+
* (scheduleRestart's setTimeout → spawnProcess) instead of handing back a
|
|
1173
|
+
* process whose sendMessage() will silently no-op. Resolves once the new
|
|
1174
|
+
* child is attached ('restarted'); rejects on a definitive restart failure
|
|
1175
|
+
* ('restartFailed', or 'failed' once MAX_RESTARTS is exhausted) or, as a
|
|
1176
|
+
* last resort, on SESSION_RESTART_WAIT_TIMEOUT_MS so a caller is never left
|
|
1177
|
+
* hanging past that bound.
|
|
1178
|
+
*/
|
|
1179
|
+
waitForSessionRestart(proc) {
|
|
1180
|
+
if (proc.isRunning())
|
|
1181
|
+
return Promise.resolve();
|
|
1182
|
+
return new Promise((resolve, reject) => {
|
|
1183
|
+
const timer = setTimeout(() => {
|
|
1184
|
+
cleanup();
|
|
1185
|
+
reject(new Error('Session restart did not complete in time'));
|
|
1186
|
+
}, SESSION_RESTART_WAIT_TIMEOUT_MS);
|
|
1187
|
+
const onRestarted = () => { cleanup(); resolve(); };
|
|
1188
|
+
const onRestartFailed = (err) => { cleanup(); reject(err); };
|
|
1189
|
+
const onFailed = () => { cleanup(); reject(new Error('Session failed permanently (max restarts exceeded)')); };
|
|
1190
|
+
const cleanup = () => {
|
|
1191
|
+
clearTimeout(timer);
|
|
1192
|
+
proc.off('restarted', onRestarted);
|
|
1193
|
+
proc.off('restartFailed', onRestartFailed);
|
|
1194
|
+
proc.off('failed', onFailed);
|
|
1195
|
+
};
|
|
1196
|
+
proc.once('restarted', onRestarted);
|
|
1197
|
+
proc.once('restartFailed', onRestartFailed);
|
|
1198
|
+
proc.once('failed', onFailed);
|
|
1199
|
+
});
|
|
1200
|
+
}
|
|
1165
1201
|
async getOrSpawnSession(mapKey, // Map lookup key (chatId for telegram/discord, sessionId for API)
|
|
1166
1202
|
source, sessionId, // actual session UUID (only for channel sessions; equals mapKey for API)
|
|
1167
1203
|
modelOverride) {
|
|
@@ -1177,6 +1213,19 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
1177
1213
|
return pending;
|
|
1178
1214
|
const existing = this.sessions.get(mapKey);
|
|
1179
1215
|
if (existing) {
|
|
1216
|
+
// The mapped SessionProcess can be mid auto-restart (its child crashed —
|
|
1217
|
+
// see the 'exit'/scheduleRestart cycle in session/process.ts — and the
|
|
1218
|
+
// AUTO_RESTART_DELAY_MS timer hasn't fired the replacement child yet).
|
|
1219
|
+
// existing.sendMessage() silently no-ops while .process is null (only a
|
|
1220
|
+
// warning log, no error, no event), which previously left the caller's
|
|
1221
|
+
// pendingApiSessions entry stuck until the long timeout chain (the same
|
|
1222
|
+
// failure mode this whole area was already patched for — see the
|
|
1223
|
+
// session.on('exit', onExit) fix in sendApiMessage/sendApiMessageStream —
|
|
1224
|
+
// but THIS gap is hit before a turn even starts, so that fix can't see
|
|
1225
|
+
// it). Wait for the pending respawn to actually land before handing the
|
|
1226
|
+
// session back.
|
|
1227
|
+
if (!existing.isRunning())
|
|
1228
|
+
await this.waitForSessionRestart(existing);
|
|
1180
1229
|
// Restart if model changed (including switching back to the agent default)
|
|
1181
1230
|
if (existing.modelOverride !== effectiveOverride) {
|
|
1182
1231
|
this.logger.info('Model changed, restarting session', { mapKey, oldModel: existing.modelOverride, newModel: effectiveOverride ?? agentDefaultModel });
|
|
@@ -2515,17 +2564,31 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2515
2564
|
let quietTimer;
|
|
2516
2565
|
// Track partial message text for delta computation (--include-partial-messages)
|
|
2517
2566
|
let lastPartialText = '';
|
|
2518
|
-
|
|
2567
|
+
// Guards done/fail against a double-fire — e.g. onExit racing a 'result'
|
|
2568
|
+
// line that already settled this turn (see onExit below).
|
|
2569
|
+
let settled = false;
|
|
2570
|
+
const done = (result, interrupted = false) => {
|
|
2571
|
+
if (settled)
|
|
2572
|
+
return;
|
|
2573
|
+
settled = true;
|
|
2519
2574
|
cleanup();
|
|
2520
2575
|
const attachments = this.popApiAttachments(sessionId);
|
|
2576
|
+
const trimmed = result.trim();
|
|
2577
|
+
// A /stop that lands before any text or attachment came out of this turn
|
|
2578
|
+
// would otherwise persist nothing at all — the last committed message
|
|
2579
|
+
// stays the user's forever, and the web sidebar's "typing…" indicator
|
|
2580
|
+
// never clears (it only clears once the last message is from the
|
|
2581
|
+
// assistant). Same wording buildInitialPrompt uses to patch this same
|
|
2582
|
+
// dangling-turn shape for the next spawn's in-memory context.
|
|
2583
|
+
const finalText = !trimmed && !attachments.length && interrupted ? process_1.INTERRUPTED_NO_REPLY_TEXT : trimmed;
|
|
2521
2584
|
// Persist assistant reply. Image-only replies (empty text but attachments
|
|
2522
2585
|
// present) must also persist — otherwise the screenshot vanishes from history.
|
|
2523
|
-
if (
|
|
2586
|
+
if (finalText || attachments.length) {
|
|
2524
2587
|
const apiAssistantTs = Date.now();
|
|
2525
2588
|
this.sessionStore
|
|
2526
2589
|
.appendMessage(this.agentConfig.id, sessionId, {
|
|
2527
2590
|
role: 'assistant',
|
|
2528
|
-
content:
|
|
2591
|
+
content: finalText,
|
|
2529
2592
|
ts: apiAssistantTs,
|
|
2530
2593
|
})
|
|
2531
2594
|
.catch(() => { });
|
|
@@ -2534,14 +2597,17 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2534
2597
|
sessionId,
|
|
2535
2598
|
source: 'api',
|
|
2536
2599
|
role: 'assistant',
|
|
2537
|
-
content:
|
|
2600
|
+
content: finalText,
|
|
2538
2601
|
mediaFiles: attachments.length ? attachments.map((a) => `media/${a.relPath}`) : undefined,
|
|
2539
2602
|
ts: apiAssistantTs,
|
|
2540
2603
|
});
|
|
2541
2604
|
}
|
|
2542
|
-
resolve({ text:
|
|
2605
|
+
resolve({ text: finalText, attachments });
|
|
2543
2606
|
};
|
|
2544
2607
|
const fail = (err) => {
|
|
2608
|
+
if (settled)
|
|
2609
|
+
return;
|
|
2610
|
+
settled = true;
|
|
2545
2611
|
// Terminal close for a turn that produced no result (#75): record it so
|
|
2546
2612
|
// history does not end on a dangling user message, and drain the
|
|
2547
2613
|
// attachment buffer so nothing leaks into the next turn.
|
|
@@ -2549,6 +2615,26 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2549
2615
|
cleanup();
|
|
2550
2616
|
reject(err); // no-op when the soft timeout already rejected
|
|
2551
2617
|
};
|
|
2618
|
+
// The subprocess died without ever emitting a final `result` line (crash,
|
|
2619
|
+
// an unexpected signal — distinct from a graceful /stop, whose SIGINT
|
|
2620
|
+
// handler still emits a terminal result before exiting, so `done()` wins
|
|
2621
|
+
// that race and this is a no-op). Without this, a mid-turn crash left
|
|
2622
|
+
// pendingApiSessions — and the caller's pending-response spinner — stuck
|
|
2623
|
+
// until the up-to-15-minute opts.timeoutMs + API_TIMEOUT_HARD_CAP_EXTRA_MS
|
|
2624
|
+
// safety net finally fired.
|
|
2625
|
+
//
|
|
2626
|
+
// If the exit followed a /stop (the CLI's SIGINT handler fell through to
|
|
2627
|
+
// default termination instead of flushing a result line), that's a
|
|
2628
|
+
// successful stop, not a crash — resolve like the graceful path instead
|
|
2629
|
+
// of surfacing "process exited unexpectedly" for a stop the user asked for.
|
|
2630
|
+
const onExit = () => {
|
|
2631
|
+
const interrupted = session.consumeInterruptFlag();
|
|
2632
|
+
if (interrupted) {
|
|
2633
|
+
done(buffer.join(''), true);
|
|
2634
|
+
return;
|
|
2635
|
+
}
|
|
2636
|
+
fail(Object.assign(new Error('Session process exited unexpectedly before responding.'), { code: 'PROCESS_EXITED' }));
|
|
2637
|
+
};
|
|
2552
2638
|
const cleanup = () => {
|
|
2553
2639
|
clearTimeout(globalTimer);
|
|
2554
2640
|
if (hardCapTimer)
|
|
@@ -2556,12 +2642,13 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2556
2642
|
if (quietTimer)
|
|
2557
2643
|
clearTimeout(quietTimer);
|
|
2558
2644
|
session.off('output', onOutput);
|
|
2645
|
+
session.off('exit', onExit);
|
|
2559
2646
|
this.pendingApiSessions.delete(sessionId);
|
|
2560
2647
|
};
|
|
2561
2648
|
const resetQuiet = () => {
|
|
2562
2649
|
if (quietTimer)
|
|
2563
2650
|
clearTimeout(quietTimer);
|
|
2564
|
-
quietTimer = setTimeout(() => done(buffer.join('')), 2000);
|
|
2651
|
+
quietTimer = setTimeout(() => done(buffer.join(''), session.consumeInterruptFlag()), 2000);
|
|
2565
2652
|
};
|
|
2566
2653
|
const onOutput = (line) => {
|
|
2567
2654
|
try {
|
|
@@ -2597,7 +2684,12 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2597
2684
|
// accumulated buffer (needed for non-Anthropic models e.g. OpenRouter,
|
|
2598
2685
|
// which emit result:"" even though the text arrived via stream_event chunks).
|
|
2599
2686
|
const resultText = obj['result'] || buffer.join('');
|
|
2600
|
-
|
|
2687
|
+
// Consumed here (rather than left for onExit) so the graceful path — a
|
|
2688
|
+
// result line flushed before the subprocess exits — still gets credit
|
|
2689
|
+
// for the interrupt; onExit's own consumeInterruptFlag() call would
|
|
2690
|
+
// otherwise see it already cleared and take the (harmless, settled-
|
|
2691
|
+
// guarded) fail() branch instead.
|
|
2692
|
+
done(resultText, session.consumeInterruptFlag());
|
|
2601
2693
|
}
|
|
2602
2694
|
}
|
|
2603
2695
|
catch {
|
|
@@ -2617,6 +2709,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2617
2709
|
}, API_TIMEOUT_HARD_CAP_EXTRA_MS);
|
|
2618
2710
|
}, opts.timeoutMs);
|
|
2619
2711
|
session.on('output', onOutput);
|
|
2712
|
+
session.on('exit', onExit);
|
|
2620
2713
|
session.setProcessing(true);
|
|
2621
2714
|
session.sendMessage(channelXml);
|
|
2622
2715
|
// Do NOT call resetQuiet() here — the quiet timer should only start
|
|
@@ -2697,21 +2790,29 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2697
2790
|
let lastPartialText = '';
|
|
2698
2791
|
// Accumulate tool_use blocks from stream_event (content_block_start → delta → stop)
|
|
2699
2792
|
const toolBlocks = new Map();
|
|
2700
|
-
const done = (result) => {
|
|
2793
|
+
const done = (result, interrupted = false) => {
|
|
2701
2794
|
if (settled)
|
|
2702
2795
|
return;
|
|
2703
2796
|
settled = true;
|
|
2704
2797
|
cleanup();
|
|
2705
2798
|
const attachments = this.popApiAttachments(sessionId);
|
|
2799
|
+
const trimmed = result.trim();
|
|
2800
|
+
// A /stop that lands before any text or attachment came out of this turn
|
|
2801
|
+
// would otherwise persist nothing at all — the last committed message
|
|
2802
|
+
// stays the user's forever, and the web sidebar's "typing…" indicator
|
|
2803
|
+
// never clears (it only clears once the last message is from the
|
|
2804
|
+
// assistant). Same wording buildInitialPrompt uses to patch this same
|
|
2805
|
+
// dangling-turn shape for the next spawn's in-memory context.
|
|
2806
|
+
const finalText = !trimmed && !attachments.length && interrupted ? process_1.INTERRUPTED_NO_REPLY_TEXT : trimmed;
|
|
2706
2807
|
// Persist assistant reply regardless of whether the SSE client is still connected.
|
|
2707
2808
|
// Image-only replies (empty text but attachments present) must also persist —
|
|
2708
2809
|
// otherwise the screenshot vanishes from history once the stream ends.
|
|
2709
|
-
if (
|
|
2810
|
+
if (finalText || attachments.length) {
|
|
2710
2811
|
const streamAssistantTs = Date.now();
|
|
2711
2812
|
this.sessionStore
|
|
2712
2813
|
.appendMessage(this.agentConfig.id, sessionId, {
|
|
2713
2814
|
role: 'assistant',
|
|
2714
|
-
content:
|
|
2815
|
+
content: finalText,
|
|
2715
2816
|
ts: streamAssistantTs,
|
|
2716
2817
|
})
|
|
2717
2818
|
.catch(() => { });
|
|
@@ -2720,12 +2821,12 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2720
2821
|
sessionId,
|
|
2721
2822
|
source: 'api',
|
|
2722
2823
|
role: 'assistant',
|
|
2723
|
-
content:
|
|
2824
|
+
content: finalText,
|
|
2724
2825
|
mediaFiles: attachments.length ? attachments.map((a) => `media/${a.relPath}`) : undefined,
|
|
2725
2826
|
ts: streamAssistantTs,
|
|
2726
2827
|
});
|
|
2727
2828
|
}
|
|
2728
|
-
callbacks.onDone(
|
|
2829
|
+
callbacks.onDone(finalText, attachments);
|
|
2729
2830
|
};
|
|
2730
2831
|
const fail = (err) => {
|
|
2731
2832
|
if (settled)
|
|
@@ -2739,11 +2840,32 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2739
2840
|
this.persistFailedApiTurn(chatId, sessionId, err, opts.skipUserMessage);
|
|
2740
2841
|
notifyError(err);
|
|
2741
2842
|
};
|
|
2843
|
+
// The subprocess died without ever emitting a final `result` line (crash,
|
|
2844
|
+
// an unexpected signal — distinct from a graceful /stop, whose SIGINT
|
|
2845
|
+
// handler still emits a terminal result before exiting, so `done()` wins
|
|
2846
|
+
// that race and this is a no-op via the `settled` guard in fail()).
|
|
2847
|
+
// Without this, a mid-turn crash left pendingApiSessions — and the web's
|
|
2848
|
+
// pending-response spinner — stuck until the up-to-15-minute
|
|
2849
|
+
// opts.timeoutMs + API_TIMEOUT_HARD_CAP_EXTRA_MS safety net finally fired.
|
|
2850
|
+
//
|
|
2851
|
+
// If the exit followed a /stop (the CLI's SIGINT handler fell through to
|
|
2852
|
+
// default termination instead of flushing a result line), that's a
|
|
2853
|
+
// successful stop, not a crash — resolve like the graceful path instead
|
|
2854
|
+
// of surfacing "process exited unexpectedly" for a stop the user asked for.
|
|
2855
|
+
const onExit = () => {
|
|
2856
|
+
const interrupted = session.consumeInterruptFlag();
|
|
2857
|
+
if (interrupted) {
|
|
2858
|
+
done(buffer.join(''), true);
|
|
2859
|
+
return;
|
|
2860
|
+
}
|
|
2861
|
+
fail(Object.assign(new Error('Session process exited unexpectedly before responding.'), { code: 'PROCESS_EXITED' }));
|
|
2862
|
+
};
|
|
2742
2863
|
const cleanup = () => {
|
|
2743
2864
|
clearTimeout(globalTimer);
|
|
2744
2865
|
if (hardCapTimer)
|
|
2745
2866
|
clearTimeout(hardCapTimer);
|
|
2746
2867
|
session.off('output', onOutput);
|
|
2868
|
+
session.off('exit', onExit);
|
|
2747
2869
|
this.pendingApiSessions.delete(sessionId);
|
|
2748
2870
|
};
|
|
2749
2871
|
// Called when the SSE client disconnects. Marks clientGone so SSE writes
|
|
@@ -2816,7 +2938,12 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2816
2938
|
lastPartialText = ''; // reset for next turn
|
|
2817
2939
|
// Use || so empty-string result falls back to buffer (OpenRouter emits result:"").
|
|
2818
2940
|
const resultText = obj['result'] || buffer.join('');
|
|
2819
|
-
|
|
2941
|
+
// Consumed here (rather than left for onExit) so the graceful path — a
|
|
2942
|
+
// result line flushed before the subprocess exits — still gets credit
|
|
2943
|
+
// for the interrupt; onExit's own consumeInterruptFlag() call would
|
|
2944
|
+
// otherwise see it already cleared and take the (harmless, settled-
|
|
2945
|
+
// guarded) fail() branch instead.
|
|
2946
|
+
done(resultText, session.consumeInterruptFlag());
|
|
2820
2947
|
}
|
|
2821
2948
|
}
|
|
2822
2949
|
catch {
|
|
@@ -2845,6 +2972,7 @@ class AgentRunner extends events_1.EventEmitter {
|
|
|
2845
2972
|
}, API_TIMEOUT_HARD_CAP_EXTRA_MS);
|
|
2846
2973
|
}, opts.timeoutMs);
|
|
2847
2974
|
session.on('output', onOutput);
|
|
2975
|
+
session.on('exit', onExit);
|
|
2848
2976
|
// Image paths only work when allowTools:true — Claude needs the Read tool to access them
|
|
2849
2977
|
const allowToolsStream = opts.allowTools ?? false;
|
|
2850
2978
|
if (!allowToolsStream && imagePathsStream.length) {
|