tycono 0.3.12 → 0.3.13-beta.1

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
@@ -11,6 +11,7 @@ if (!process.env.__TYCONO_HEAP_SET && !process.execArgv.some(a => a.includes('ma
11
11
  try {
12
12
  execFileSync(process.execPath, [
13
13
  '--max-old-space-size=8192',
14
+ '--expose-gc',
14
15
  ...process.execArgv,
15
16
  fileURLToPath(import.meta.url),
16
17
  ...process.argv.slice(2),
package/bin/tycono.ts CHANGED
@@ -270,11 +270,37 @@ async function startServerForTui(): Promise<void> {
270
270
  origLog(` API server started on port ${port}`);
271
271
  origLog(` Logs: ${logFile}`);
272
272
 
273
- // Memory monitor — log heap usage every 30s to detect leaks
273
+ // Memory monitor — 10s interval, with spike detection + forced GC
274
+ let lastHeap = 0;
274
275
  setInterval(() => {
275
276
  const mem = process.memoryUsage();
276
- logStream.write(`[MEM] heap=${Math.round(mem.heapUsed / 1024 / 1024)}MB/${Math.round(mem.heapTotal / 1024 / 1024)}MB rss=${Math.round(mem.rss / 1024 / 1024)}MB\n`);
277
- }, 30_000).unref();
277
+ const heapMB = Math.round(mem.heapUsed / 1024 / 1024);
278
+ const heapTotalMB = Math.round(mem.heapTotal / 1024 / 1024);
279
+ const rssMB = Math.round(mem.rss / 1024 / 1024);
280
+ const spike = heapMB - lastHeap;
281
+
282
+ // Always log
283
+ logStream.write(`[MEM] heap=${heapMB}MB/${heapTotalMB}MB rss=${rssMB}MB${spike > 50 ? ` ⚠️ SPIKE +${spike}MB` : ''}\n`);
284
+
285
+ // Spike detection: >100MB jump in 10s
286
+ if (spike > 100) {
287
+ logStream.write(`[MEM] ⛔ MEMORY SPIKE: +${spike}MB in 10s (${lastHeap}MB → ${heapMB}MB)\n`);
288
+ }
289
+
290
+ // Warning at 2GB
291
+ if (heapMB > 2048 && lastHeap <= 2048) {
292
+ logStream.write(`[MEM] ⚠️ HEAP WARNING: ${heapMB}MB — approaching limit. Forcing GC.\n`);
293
+ if (global.gc) global.gc();
294
+ }
295
+
296
+ // Critical at 4GB — force GC
297
+ if (heapMB > 4096) {
298
+ logStream.write(`[MEM] ⛔ HEAP CRITICAL: ${heapMB}MB — forcing GC\n`);
299
+ if (global.gc) global.gc();
300
+ }
301
+
302
+ lastHeap = heapMB;
303
+ }, 10_000).unref();
278
304
 
279
305
  // Graceful shutdown — mark active sessions as interrupted
280
306
  const shutdown = () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tycono",
3
- "version": "0.3.12",
3
+ "version": "0.3.13-beta.1",
4
4
  "description": "Build an AI company. Watch them work.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -176,7 +176,7 @@ class ExecutionManager {
176
176
  orgTree: ReturnType<typeof buildOrgTree>,
177
177
  ): Promise<void> {
178
178
  try {
179
- const ports = await portRegistry.allocate(execution.id, params.roleId, params.task);
179
+ const ports = await portRegistry.allocate(execution.sessionId || execution.id, params.roleId, params.task);
180
180
  execution.ports = ports;
181
181
  console.log(`[ExecMgr] Allocated ports for ${execution.id} (${params.roleId}): API :${ports.api}, Vite :${ports.vite}`);
182
182
  } catch (err) {
@@ -534,7 +534,7 @@ class ExecutionManager {
534
534
  })
535
535
  .finally(() => {
536
536
  if (execution.ports) {
537
- const released = portRegistry.release(execution.id);
537
+ const released = portRegistry.release(execution.sessionId || execution.id);
538
538
  if (released) {
539
539
  console.log(`[ExecMgr] Released ports for ${execution.id}: API :${execution.ports.api}, Vite :${execution.ports.vite}`);
540
540
  }
@@ -156,7 +156,9 @@ export const PanelMode: React.FC<PanelModeProps> = ({
156
156
  return () => { process.stdout.off('resize', onResize); };
157
157
  }, []);
158
158
 
159
- const separatorStr = useMemo(() => '\u2502\n'.repeat(Math.max(5, termHeight - 8)), [termHeight]);
159
+ // OOM fix: single separator character instead of repeated newlines
160
+ // Previous: '│\n'.repeat(30) created 30 yoga nodes → layout explosion on large terminals
161
+ const separatorStr = '\u2502';
160
162
 
161
163
  const waveScopedStatuses = useMemo(
162
164
  () => getWaveScopedStatuses(allSessions, focusedWaveId),
@@ -379,10 +381,8 @@ export const PanelMode: React.FC<PanelModeProps> = ({
379
381
  )}
380
382
  </Box>
381
383
 
382
- {/* Vertical separator */}
383
- <Box flexDirection="column" marginX={0}>
384
- <Text color="gray">{separatorStr}</Text>
385
- </Box>
384
+ {/* Vertical separator — single character, not repeated newlines */}
385
+ <Text color="gray">{separatorStr}</Text>
386
386
 
387
387
  {/* Right: Tabbed panel */}
388
388
  <Box flexGrow={1} flexDirection="column" overflow="hidden">