tycono 0.3.3 → 0.3.5

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/tui/app.tsx +36 -24
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tycono",
3
- "version": "0.3.3",
3
+ "version": "0.3.5",
4
4
  "description": "Build an AI company. Watch them work.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/tui/app.tsx CHANGED
@@ -258,30 +258,6 @@ export const App: React.FC = () => {
258
258
  });
259
259
  }, []);
260
260
 
261
- // Load wave history into SSE events (for Panel Mode Stream tab)
262
- const loadWaveHistoryEvents = useCallback(async (waveId: string) => {
263
- try {
264
- const sessions = api.sessions.filter(s => s.waveId === waveId && s.roleId === 'ceo');
265
- const allEvents: import('./api').SSEEvent[] = [];
266
-
267
- for (const ses of sessions.slice(-2)) {
268
- try {
269
- const resp = await import('./api').then(m => m.fetchJson<{ events: any[] }>(`/api/jobs/${ses.id}/history`));
270
- const events = resp?.events ?? [];
271
- for (const e of events) {
272
- if (['text', 'tool:start', 'tool:result', 'msg:start', 'msg:done', 'msg:error', 'dispatch:start', 'thinking'].includes(e.type)) {
273
- allEvents.push(e as import('./api').SSEEvent);
274
- }
275
- }
276
- } catch { /* skip */ }
277
- }
278
-
279
- if (allEvents.length > 0) {
280
- sse.loadHistory(allEvents);
281
- }
282
- } catch { /* ignore */ }
283
- }, [api.sessions, sse]);
284
-
285
261
  // Load previous conversation from wave's activity stream into system messages
286
262
  const loadPreviousConversation = useCallback(async (waveId: string) => {
287
263
  try {
@@ -384,6 +360,42 @@ export const App: React.FC = () => {
384
360
  // SSE subscription to focused wave
385
361
  const sse = useSSE(focusedWaveId);
386
362
 
363
+ // Load wave history into SSE events (for Panel Mode Stream tab)
364
+ const historyLoadingRef = useRef<string | null>(null);
365
+ const loadWaveHistoryEvents = useCallback(async (waveId: string) => {
366
+ // Guard: skip if already loading this wave or another load in progress
367
+ if (historyLoadingRef.current === waveId) return;
368
+ historyLoadingRef.current = waveId;
369
+
370
+ try {
371
+ const sessions = api.sessions.filter(s => s.waveId === waveId && s.roleId === 'ceo');
372
+ const allEvents: import('./api').SSEEvent[] = [];
373
+
374
+ for (const ses of sessions.slice(-2)) {
375
+ // Abort if wave changed during loading
376
+ if (historyLoadingRef.current !== waveId) return;
377
+ try {
378
+ const resp = await import('./api').then(m => m.fetchJson<{ events: any[] }>(`/api/jobs/${ses.id}/history`));
379
+ const events = resp?.events ?? [];
380
+ for (const e of events) {
381
+ if (['text', 'tool:start', 'msg:start', 'msg:done', 'msg:error', 'dispatch:start', 'thinking'].includes(e.type)) {
382
+ allEvents.push(e as import('./api').SSEEvent);
383
+ }
384
+ }
385
+ } catch { /* skip */ }
386
+ }
387
+
388
+ // Only apply if still on this wave
389
+ if (historyLoadingRef.current === waveId && allEvents.length > 0) {
390
+ sse.loadHistory(allEvents);
391
+ }
392
+ } catch { /* ignore */ } finally {
393
+ if (historyLoadingRef.current === waveId) {
394
+ historyLoadingRef.current = null;
395
+ }
396
+ }
397
+ }, [api.sessions, sse]);
398
+
387
399
  // Build org tree — flatRoleIds follows visual top-to-bottom order
388
400
  const roles = api.company?.roles ?? [];
389
401
  const statuses = api.execStatus?.statuses ?? {};