tycono 0.3.13-beta.6 → 0.3.13-beta.7

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/bin/cli.js CHANGED
@@ -10,9 +10,8 @@ if (!process.env.__TYCONO_HEAP_SET && !process.execArgv.some(a => a.includes('ma
10
10
  process.env.__TYCONO_HEAP_SET = '1';
11
11
  try {
12
12
  execFileSync(process.execPath, [
13
- '--max-old-space-size=4096',
13
+ '--max-old-space-size=8192',
14
14
  '--expose-gc',
15
- '--heapsnapshot-near-heap-limit=1',
16
15
  ...process.execArgv,
17
16
  fileURLToPath(import.meta.url),
18
17
  ...process.argv.slice(2),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tycono",
3
- "version": "0.3.13-beta.6",
3
+ "version": "0.3.13-beta.7",
4
4
  "description": "Build an AI company. Watch them work.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -27,6 +27,9 @@ executionManager.onExecutionCreated((exec) => {
27
27
  waveMultiplexer.onExecutionCreated(exec);
28
28
  });
29
29
 
30
+ // OOM fix: wave recovery runs once, not on every 5s poll
31
+ let waveRecoveryDone = false;
32
+
30
33
  /* ─── Runner — lazy, re-created when engine changes ── */
31
34
 
32
35
  function getRunner() {
@@ -50,15 +53,15 @@ export function handleExecRequest(req: IncomingMessage, res: ServerResponse): vo
50
53
 
51
54
  // ── /api/waves/active — restore active waves after refresh ──
52
55
  if (method === 'GET' && url === '/api/waves/active') {
53
- // Recovery: rebuild wave→session mapping from session-store
54
- // Include done sessions (persistent channel model) but limit to CEO sessions
55
- const waves = waveMultiplexer.getActiveWaves();
56
- if (waves.length === 0) {
56
+ // Recovery: rebuild wave→session mapping from session-store (ONE TIME ONLY)
57
+ // Previous bug: recovery ran on EVERY poll (5s) because getActiveWaves()
58
+ // returns empty for done executions → recovery loop → OOM
59
+ if (!waveRecoveryDone) {
60
+ waveRecoveryDone = true;
57
61
  const allSessions = listSessions();
58
62
  let recovered = 0;
59
63
  for (const ses of allSessions) {
60
64
  if (!ses.waveId) continue;
61
- // Only recover CEO sessions for wave display (team sessions loaded on demand)
62
65
  if (ses.roleId !== 'ceo') continue;
63
66
  const exec = executionManager.getActiveExecution(ses.id);
64
67
  if (exec) {
@@ -67,7 +70,7 @@ export function handleExecRequest(req: IncomingMessage, res: ServerResponse): vo
67
70
  }
68
71
  }
69
72
  if (recovered > 0) {
70
- console.log(`[WaveRecovery] Recovered ${recovered} active sessions`);
73
+ console.log(`[WaveRecovery] Recovered ${recovered} sessions (one-time)`);
71
74
  }
72
75
  }
73
76
  jsonResponse(res, 200, { waves: waveMultiplexer.getActiveWaves() });
@@ -139,26 +139,11 @@ function readFilePreview(filePath: string, maxLines: number): string[] {
139
139
  }
140
140
  }
141
141
 
142
- // OOM debug: track render count
143
- let panelRenderCount = 0;
144
-
145
142
  export const PanelMode: React.FC<PanelModeProps> = ({
146
143
  tree, flatRoles, events, selectedRoleIndex, selectedRoleId,
147
144
  streamStatus, waveId, activeSessions, allSessions, companyRoot, waves,
148
145
  focusedWaveId, onMove, onSelect, onEscape, onFocusWave,
149
146
  }) => {
150
- panelRenderCount++;
151
- if (panelRenderCount % 100 === 0) {
152
- const mem = process.memoryUsage();
153
- console.error(`[PanelMode] render #${panelRenderCount} heap=${Math.round(mem.heapUsed/1024/1024)}MB events=${events.length}`);
154
- }
155
- if (panelRenderCount > 1000) {
156
- console.error(`[PanelMode] ⛔ RENDER LOOP DETECTED: ${panelRenderCount} renders. Bailing out.`);
157
- onEscape(); // Force back to command mode
158
- panelRenderCount = 0;
159
- return null;
160
- }
161
-
162
147
  const [termHeight, setTermHeight] = useState(process.stdout.rows || 30);
163
148
  const [rightTab, setRightTab] = useState<RightTab>('stream');
164
149
  const [docsFilter, setDocsFilter] = useState<DocsFilter>('all');