tycono 0.1.96-beta.40 → 0.1.96-beta.41
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/package.json +1 -1
- package/src/api/src/routes/execute.ts +24 -29
package/package.json
CHANGED
|
@@ -51,25 +51,21 @@ export function handleExecRequest(req: IncomingMessage, res: ServerResponse): vo
|
|
|
51
51
|
// ── /api/waves/active — restore active waves after refresh ──
|
|
52
52
|
if (method === 'GET' && url === '/api/waves/active') {
|
|
53
53
|
// Recovery: rebuild wave→session mapping from session-store if lost
|
|
54
|
+
// Only recover ACTIVE sessions to prevent OOM on large datasets (140+ sessions)
|
|
54
55
|
const waves = waveMultiplexer.getActiveWaves();
|
|
55
56
|
if (waves.length === 0) {
|
|
56
57
|
const allSessions = listSessions();
|
|
57
|
-
|
|
58
|
+
let recovered = 0;
|
|
58
59
|
for (const ses of allSessions) {
|
|
59
|
-
if (ses.waveId)
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
if (!ses.waveId || ses.status !== 'active') continue;
|
|
61
|
+
const exec = executionManager.getActiveExecution(ses.id);
|
|
62
|
+
if (exec) {
|
|
63
|
+
waveMultiplexer.registerSession(ses.waveId, exec);
|
|
64
|
+
recovered++;
|
|
62
65
|
}
|
|
63
66
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
for (const sid of sids) {
|
|
67
|
-
// getActiveExecution falls back to stream recovery for completed sessions
|
|
68
|
-
const exec = executionManager.getActiveExecution(sid);
|
|
69
|
-
if (exec) {
|
|
70
|
-
waveMultiplexer.registerSession(wid, exec);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
67
|
+
if (recovered > 0) {
|
|
68
|
+
console.log(`[WaveRecovery] Recovered ${recovered} active sessions`);
|
|
73
69
|
}
|
|
74
70
|
}
|
|
75
71
|
jsonResponse(res, 200, { waves: waveMultiplexer.getActiveWaves() });
|
|
@@ -480,13 +476,11 @@ function handleWaveStream(waveId: string, url: string, res: ServerResponse, req:
|
|
|
480
476
|
|
|
481
477
|
let sessionIds = waveMultiplexer.getWaveSessionIds(waveId);
|
|
482
478
|
|
|
483
|
-
// Recovery:
|
|
484
|
-
// rebuild from session-store (sessions have waveId) + executionManager
|
|
479
|
+
// Recovery: only recover ACTIVE sessions for this wave (not all 140)
|
|
485
480
|
if (sessionIds.length === 0) {
|
|
486
481
|
const allSessions = listSessions();
|
|
487
|
-
const waveSessions = allSessions.filter(s => s.waveId === waveId);
|
|
482
|
+
const waveSessions = allSessions.filter(s => s.waveId === waveId && s.status === 'active');
|
|
488
483
|
for (const ses of waveSessions) {
|
|
489
|
-
// getActiveExecution recovers from stream files for completed sessions too
|
|
490
484
|
const exec = executionManager.getActiveExecution(ses.id);
|
|
491
485
|
if (exec) {
|
|
492
486
|
waveMultiplexer.registerSession(waveId, exec);
|
|
@@ -763,25 +757,26 @@ function handleStatus(res: ServerResponse): void {
|
|
|
763
757
|
);
|
|
764
758
|
|
|
765
759
|
const recovered: typeof activeExecs = [];
|
|
766
|
-
|
|
767
|
-
|
|
760
|
+
// Limit recovery scan to prevent OOM on large session stores
|
|
761
|
+
const MAX_RECOVERY_SCAN = 20;
|
|
762
|
+
const recentActive = activeSessions.slice(-MAX_RECOVERY_SCAN);
|
|
763
|
+
|
|
764
|
+
for (const ses of recentActive) {
|
|
768
765
|
if (!ActivityStream.exists(ses.id)) continue;
|
|
766
|
+
// Only read last few events to check done/error (not entire stream)
|
|
769
767
|
const events = ActivityStream.readFrom(ses.id, 0);
|
|
770
768
|
if (events.length === 0) continue;
|
|
771
769
|
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
const errorEvent = events.find(e => e.type === 'msg:error');
|
|
777
|
-
|
|
778
|
-
// Only include sessions that haven't finished yet
|
|
779
|
-
if (doneEvent || errorEvent) continue;
|
|
770
|
+
// Check last 5 events for done/error (optimization: don't scan entire file)
|
|
771
|
+
const tail = events.slice(-5);
|
|
772
|
+
const isDone = tail.some(e => e.type === 'msg:done' || e.type === 'msg:error');
|
|
773
|
+
if (isDone) continue;
|
|
780
774
|
|
|
781
|
-
const
|
|
775
|
+
const startEvent = events.find(e => e.type === 'msg:start');
|
|
776
|
+
const task = (startEvent?.data?.task as string) ?? ses.title ?? '';
|
|
782
777
|
recovered.push({
|
|
783
778
|
id: `recovered-${ses.id}`,
|
|
784
|
-
type: (startEvent
|
|
779
|
+
type: (startEvent?.data?.type as string ?? 'assign') as 'assign' | 'wave' | 'consult',
|
|
785
780
|
roleId: ses.roleId,
|
|
786
781
|
task,
|
|
787
782
|
status: 'running',
|