tycono 0.1.96-beta.23 → 0.1.96-beta.24

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tycono",
3
- "version": "0.1.96-beta.23",
3
+ "version": "0.1.96-beta.24",
4
4
  "description": "Build an AI company. Watch them work.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/tui/app.tsx CHANGED
@@ -194,14 +194,14 @@ export const App: React.FC = () => {
194
194
  const addSystemMessage = useCallback((text: string, color: string = 'yellow') => {
195
195
  setSystemMessages(prev => {
196
196
  const next = [...prev, { id: ++sysLineId, text, color }];
197
- return next.length > 50 ? next.slice(-50) : next;
197
+ return next.length > 30 ? next.slice(-30) : next;
198
198
  });
199
199
  }, []);
200
200
 
201
201
  const addSystemLines = useCallback((lines: StreamLine[]) => {
202
202
  setSystemMessages(prev => {
203
203
  const next = [...prev, ...lines];
204
- return next.length > 80 ? next.slice(-80) : next;
204
+ return next.length > 40 ? next.slice(-40) : next;
205
205
  });
206
206
  }, []);
207
207
 
@@ -261,8 +261,8 @@ export const CommandMode: React.FC<CommandModeProps> = ({
261
261
  if (line) eventLines.push(line);
262
262
  }
263
263
 
264
- // Merge system messages and event lines
265
- const allLines = [...systemMessages, ...eventLines];
264
+ // Merge system messages and event lines (cap total to prevent memory bloat)
265
+ const allLines = [...systemMessages, ...eventLines].slice(-200);
266
266
 
267
267
  // Split into committed (scrollback) and live (re-rendered)
268
268
  const newCommitted = allLines.slice(committedRef.current);
@@ -271,7 +271,9 @@ export const CommandMode: React.FC<CommandModeProps> = ({
271
271
  committedRef.current += toCommit.length;
272
272
  }
273
273
 
274
- const committedLines = allLines.slice(0, committedRef.current);
274
+ // Cap committed to prevent Static from holding too many items
275
+ const rawCommitted = allLines.slice(0, committedRef.current);
276
+ const committedLines = rawCommitted.length > 100 ? rawCommitted.slice(-100) : rawCommitted;
275
277
  const liveLines = allLines.slice(committedRef.current);
276
278
 
277
279
  const handleSubmit = useCallback((value: string) => {
@@ -1,14 +1,53 @@
1
1
  /**
2
2
  * useSSE — subscribe to wave SSE stream with auto-reconnect
3
+ *
4
+ * Events are stored with trimmed data to prevent OOM:
5
+ * - text/thinking: keep only displayable portion
6
+ * - msg:start task: truncate long supervisor prompts
7
+ * - tool inputs: summarize
3
8
  */
4
9
 
5
10
  import { useState, useEffect, useRef, useCallback } from 'react';
6
11
  import { subscribeToWaveStream, type SSEEvent, type SSEConnection } from '../api';
7
12
 
8
- const MAX_EVENTS = 500;
13
+ const MAX_EVENTS = 150;
9
14
  const RECONNECT_DELAY_MS = 3000;
10
15
  const MAX_RECONNECT_DELAY_MS = 15000;
11
16
 
17
+ /** Trim event data to prevent memory bloat */
18
+ function trimEvent(event: SSEEvent): SSEEvent {
19
+ const data = { ...event.data };
20
+
21
+ // Truncate large text fields
22
+ if (typeof data.text === 'string' && data.text.length > 500) {
23
+ data.text = data.text.slice(0, 500);
24
+ }
25
+ if (typeof data.task === 'string' && data.task.length > 200) {
26
+ data.task = data.task.slice(0, 200);
27
+ }
28
+ if (typeof data.error === 'string' && data.error.length > 300) {
29
+ data.error = data.error.slice(0, 300);
30
+ }
31
+ if (typeof data.message === 'string' && data.message.length > 300) {
32
+ data.message = data.message.slice(0, 300);
33
+ }
34
+ // Summarize tool inputs
35
+ if (data.input && typeof data.input === 'object') {
36
+ const inp = data.input as Record<string, unknown>;
37
+ const summary: Record<string, unknown> = {};
38
+ for (const [k, v] of Object.entries(inp)) {
39
+ if (typeof v === 'string' && v.length > 200) {
40
+ summary[k] = v.slice(0, 200);
41
+ } else {
42
+ summary[k] = v;
43
+ }
44
+ }
45
+ data.input = summary;
46
+ }
47
+
48
+ return { ...event, data };
49
+ }
50
+
12
51
  export interface SSEState {
13
52
  events: SSEEvent[];
14
53
  streamStatus: 'idle' | 'streaming' | 'done' | 'error';
@@ -30,7 +69,6 @@ export function useSSE(waveId: string | null): SSEState {
30
69
  }, []);
31
70
 
32
71
  useEffect(() => {
33
- // Disconnect if waveId changed
34
72
  if (waveIdRef.current !== waveId) {
35
73
  connRef.current?.close();
36
74
  connRef.current = null;
@@ -50,7 +88,6 @@ export function useSSE(waveId: string | null): SSEState {
50
88
  const connect = (fromSeq: number) => {
51
89
  setStreamStatus('streaming');
52
90
 
53
- // Only clear events on first connect (not reconnects)
54
91
  if (fromSeq === 0) {
55
92
  setEvents([]);
56
93
  maxSeqRef.current = 0;
@@ -59,14 +96,14 @@ export function useSSE(waveId: string | null): SSEState {
59
96
  const conn = subscribeToWaveStream(
60
97
  waveId,
61
98
  (event) => {
62
- // Track max seq for reconnect resume
63
99
  if (event.seq !== undefined && event.seq > maxSeqRef.current) {
64
100
  maxSeqRef.current = event.seq;
65
101
  }
66
- reconnectAttemptRef.current = 0; // Reset on successful event
102
+ reconnectAttemptRef.current = 0;
67
103
 
104
+ const trimmed = trimEvent(event);
68
105
  setEvents((prev) => {
69
- const next = [...prev, event];
106
+ const next = [...prev, trimmed];
70
107
  return next.length > MAX_EVENTS ? next.slice(-MAX_EVENTS) : next;
71
108
  });
72
109
  },
@@ -74,15 +111,12 @@ export function useSSE(waveId: string | null): SSEState {
74
111
  if (reason === 'done') {
75
112
  setStreamStatus('done');
76
113
  } else {
77
- // Disconnected or error → auto-reconnect
78
114
  const attempt = reconnectAttemptRef.current++;
79
115
  const delay = Math.min(
80
116
  RECONNECT_DELAY_MS * Math.pow(1.5, attempt),
81
117
  MAX_RECONNECT_DELAY_MS,
82
118
  );
83
-
84
- setStreamStatus('streaming'); // Keep showing streaming during reconnect
85
-
119
+ setStreamStatus('streaming');
86
120
  reconnectTimerRef.current = setTimeout(() => {
87
121
  reconnectTimerRef.current = null;
88
122
  if (waveIdRef.current === waveId) {