tycono 0.1.96-beta.22 → 0.1.96-beta.23

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.22",
3
+ "version": "0.1.96-beta.23",
4
4
  "description": "Build an AI company. Watch them work.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -2,8 +2,7 @@
2
2
  * CommandMode — scrollable terminal mode (like Claude Code)
3
3
  *
4
4
  * Uses Ink's <Static> to push past output into terminal scrollback.
5
- * Only the input prompt + status remain in the re-rendered area.
6
- * User can scroll up with mouse wheel to see history.
5
+ * Shows full output: text, tools, thinking, dispatch no aggressive truncation.
7
6
  */
8
7
 
9
8
  import React, { useState, useCallback, useRef } from 'react';
@@ -32,17 +31,13 @@ interface CommandModeProps {
32
31
 
33
32
  let lineCounter = 0;
34
33
 
35
- /** Filter out system prompt noise from text */
34
+ /** Filter out only truly internal system prompt fragments */
36
35
  function isSystemNoise(text: string): boolean {
37
36
  const t = text.trim();
38
37
  if (!t) return true;
39
- if (t.startsWith('## Your Role')) return true;
40
- if (t.startsWith('You are')) return true;
41
- if (t.startsWith('[CEO Supervisor]')) return true;
42
- if (t.startsWith('[Question from')) return true;
43
- if (t.includes('\u26D4 AKB Rule')) return true;
44
- if (t.includes('\u26D4 Read the')) return true;
45
- if (t.startsWith('\u26D4')) return true;
38
+ // Only filter the injected supervisor system prompt header
39
+ if (t.startsWith('[CEO Supervisor]') && t.includes('Your Role')) return true;
40
+ if (t.startsWith('\u26D4 AKB Rule:')) return true;
46
41
  return false;
47
42
  }
48
43
 
@@ -59,7 +54,7 @@ export function summarizeEvent(event: SSEEvent, allRoleIds: string[]): StreamLin
59
54
  if (isSupervisor) {
60
55
  return {
61
56
  id: ++lineCounter,
62
- text: text.slice(0, 200),
57
+ text,
63
58
  color: 'white',
64
59
  };
65
60
  } else {
@@ -67,17 +62,30 @@ export function summarizeEvent(event: SSEEvent, allRoleIds: string[]): StreamLin
67
62
  id: ++lineCounter,
68
63
  prefix: event.roleId,
69
64
  prefixColor: roleColor,
70
- text: text.slice(0, 80),
65
+ text,
71
66
  color: 'white',
72
67
  indent: true,
73
68
  };
74
69
  }
75
70
  }
76
71
 
72
+ case 'thinking': {
73
+ const text = ((event.data.text as string) ?? '').slice(0, 120);
74
+ if (!text.trim()) return null;
75
+ return {
76
+ id: ++lineCounter,
77
+ prefix: isSupervisor ? undefined : event.roleId,
78
+ prefixColor: roleColor,
79
+ text: `\uD83D\uDCAD ${text}`,
80
+ color: 'gray',
81
+ indent: !isSupervisor,
82
+ };
83
+ }
84
+
77
85
  case 'dispatch:start': {
78
86
  const target = (event.data.targetRole as string) ?? '';
79
87
  const task = ((event.data.task as string) ?? '');
80
- const cleanTask = task.replace(/\u26D4[^\u26D4]*\u26D4[^"]*/g, '').trim().slice(0, 50);
88
+ const cleanTask = task.replace(/\u26D4[^\u26D4]*\u26D4[^"]*/g, '').trim().slice(0, 80);
81
89
  if (isSupervisor) {
82
90
  return {
83
91
  id: ++lineCounter,
@@ -89,7 +97,7 @@ export function summarizeEvent(event: SSEEvent, allRoleIds: string[]): StreamLin
89
97
  id: ++lineCounter,
90
98
  prefix: event.roleId,
91
99
  prefixColor: roleColor,
92
- text: `\u2192 ${target} \uBC30\uC815`,
100
+ text: `\u2192 ${target} \uBC30\uC815${cleanTask ? ': ' + cleanTask : ''}`,
93
101
  color: 'yellow',
94
102
  indent: true,
95
103
  };
@@ -99,7 +107,7 @@ export function summarizeEvent(event: SSEEvent, allRoleIds: string[]): StreamLin
99
107
  const target = (event.data.targetRole as string) ?? '';
100
108
  return {
101
109
  id: ++lineCounter,
102
- prefix: event.roleId,
110
+ prefix: isSupervisor ? undefined : event.roleId,
103
111
  prefixColor: roleColor,
104
112
  text: `\u2190 ${target} \uC644\uB8CC`,
105
113
  color: 'yellow',
@@ -113,31 +121,43 @@ export function summarizeEvent(event: SSEEvent, allRoleIds: string[]): StreamLin
113
121
  let detail = '';
114
122
  if (input && typeof input === 'object') {
115
123
  const inp = input as Record<string, unknown>;
116
- if (inp.file_path) detail = ` ${String(inp.file_path).split('/').pop()}`;
117
- else if (inp.command) detail = ` ${String(inp.command).slice(0, 40)}`;
124
+ if (inp.file_path) detail = ` ${String(inp.file_path)}`;
125
+ else if (inp.command) detail = ` ${String(inp.command).slice(0, 80)}`;
126
+ else if (inp.pattern) detail = ` ${String(inp.pattern)}`;
127
+ else if (inp.description) detail = ` ${String(inp.description).slice(0, 60)}`;
118
128
  }
129
+ return {
130
+ id: ++lineCounter,
131
+ prefix: isSupervisor ? undefined : event.roleId,
132
+ prefixColor: roleColor,
133
+ text: ` \u2192 ${toolName}${detail}`,
134
+ color: 'gray',
135
+ indent: !isSupervisor,
136
+ };
137
+ }
119
138
 
120
- if (isSupervisor) {
121
- return {
122
- id: ++lineCounter,
123
- text: ` \u2192 ${toolName}${detail}`,
124
- color: 'gray',
125
- };
126
- }
139
+ case 'tool:result': {
140
+ const toolName = (event.data.name as string) ?? 'tool';
127
141
  return {
128
142
  id: ++lineCounter,
129
- prefix: event.roleId,
143
+ prefix: isSupervisor ? undefined : event.roleId,
130
144
  prefixColor: roleColor,
131
- text: `\u2192 ${toolName}${detail}`,
145
+ text: ` \u2190 ${toolName} done`,
132
146
  color: 'gray',
133
- indent: true,
147
+ indent: !isSupervisor,
134
148
  };
135
149
  }
136
150
 
137
151
  case 'msg:start': {
138
- if (isSupervisor) return null;
139
152
  const task = ((event.data.task as string) ?? '');
140
- const cleanTask = task.replace(/\u26D4[^\u26D4]*\u26D4[^"]*/g, '').trim().slice(0, 40);
153
+ const cleanTask = task.replace(/\u26D4[^\u26D4]*\u26D4[^"]*/g, '').trim().slice(0, 60);
154
+ if (isSupervisor) {
155
+ return {
156
+ id: ++lineCounter,
157
+ text: `\u25B6 Supervisor started${cleanTask ? ': ' + cleanTask : ''}`,
158
+ color: 'cyan',
159
+ };
160
+ }
141
161
  return {
142
162
  id: ++lineCounter,
143
163
  prefix: event.roleId,
@@ -150,7 +170,13 @@ export function summarizeEvent(event: SSEEvent, allRoleIds: string[]): StreamLin
150
170
 
151
171
  case 'msg:done': {
152
172
  const turns = event.data.turns as number | undefined;
153
- if (isSupervisor) return null;
173
+ if (isSupervisor) {
174
+ return {
175
+ id: ++lineCounter,
176
+ text: `\u2713 Supervisor done${turns ? ` (${turns} turns)` : ''}`,
177
+ color: 'cyan',
178
+ };
179
+ }
154
180
  return {
155
181
  id: ++lineCounter,
156
182
  prefix: event.roleId,
@@ -162,8 +188,14 @@ export function summarizeEvent(event: SSEEvent, allRoleIds: string[]): StreamLin
162
188
  }
163
189
 
164
190
  case 'msg:error': {
165
- if (isSupervisor) return null;
166
- const error = ((event.data.error as string) ?? '').slice(0, 60);
191
+ const error = ((event.data.error as string) ?? (event.data.message as string) ?? '').slice(0, 120);
192
+ if (isSupervisor) {
193
+ return {
194
+ id: ++lineCounter,
195
+ text: `\u2717 Supervisor error: ${error}`,
196
+ color: 'red',
197
+ };
198
+ }
167
199
  return {
168
200
  id: ++lineCounter,
169
201
  prefix: event.roleId,
@@ -174,20 +206,23 @@ export function summarizeEvent(event: SSEEvent, allRoleIds: string[]): StreamLin
174
206
  };
175
207
  }
176
208
 
177
- case 'msg:awaiting_input':
209
+ case 'msg:awaiting_input': {
210
+ const question = (event.data.question as string) ?? '';
178
211
  return {
179
212
  id: ++lineCounter,
180
- text: isSupervisor ? '...' : ` ${event.roleId}: waiting`,
213
+ prefix: isSupervisor ? undefined : event.roleId,
214
+ prefixColor: roleColor,
215
+ text: question ? `? ${question.slice(0, 100)}` : '? Awaiting input...',
181
216
  color: 'yellow',
217
+ indent: !isSupervisor,
182
218
  };
219
+ }
183
220
 
184
- // Hidden
185
- case 'thinking':
221
+ // Hidden (truly internal)
186
222
  case 'heartbeat:tick':
187
223
  case 'heartbeat:skip':
188
224
  case 'prompt:assembled':
189
225
  case 'trace:response':
190
- case 'tool:result':
191
226
  return null;
192
227
 
193
228
  default:
@@ -205,7 +240,7 @@ function StreamLineRow({ line }: { line: StreamLine }) {
205
240
  {(line.prefix).padEnd(12)}
206
241
  </Text>
207
242
  )}
208
- <Text color={line.color}>{line.text}</Text>
243
+ <Text color={line.color} wrap="wrap">{line.text}</Text>
209
244
  </Box>
210
245
  );
211
246
  }
@@ -230,11 +265,9 @@ export const CommandMode: React.FC<CommandModeProps> = ({
230
265
  const allLines = [...systemMessages, ...eventLines];
231
266
 
232
267
  // Split into committed (scrollback) and live (re-rendered)
233
- // Lines up to committedRef are frozen in scrollback
234
268
  const newCommitted = allLines.slice(committedRef.current);
235
- if (newCommitted.length > 5) {
236
- // Keep last 5 lines live, push rest to scrollback
237
- const toCommit = newCommitted.slice(0, -5);
269
+ if (newCommitted.length > 8) {
270
+ const toCommit = newCommitted.slice(0, -8);
238
271
  committedRef.current += toCommit.length;
239
272
  }
240
273
 
@@ -251,12 +284,12 @@ export const CommandMode: React.FC<CommandModeProps> = ({
251
284
 
252
285
  return (
253
286
  <Box flexDirection="column">
254
- {/* Committed lines → pushed to terminal scrollback (scrollable with mouse wheel) */}
287
+ {/* Committed lines → pushed to terminal scrollback */}
255
288
  <Static items={committedLines}>
256
289
  {(line) => <StreamLineRow key={line.id} line={line} />}
257
290
  </Static>
258
291
 
259
- {/* Live lines → re-rendered on each update */}
292
+ {/* Live lines → re-rendered */}
260
293
  {liveLines.map((line) => (
261
294
  <StreamLineRow key={line.id} line={line} />
262
295
  ))}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * StreamView — detailed stream panel for Panel Mode (right side)
3
3
  * Shows full event details with timestamps for a selected role.
4
- * Reuses the rendering logic from StreamPanel v1 but with the v2 layout.
4
+ * No aggressive truncation shows tools, thinking, dispatch like Claude Code.
5
5
  */
6
6
 
7
7
  import React from 'react';
@@ -26,11 +26,11 @@ function formatTime(ts: string): string {
26
26
  }
27
27
  }
28
28
 
29
- function renderEvent(event: SSEEvent, allRoleIds: string[]): { content: string; contentColor: string } | null {
29
+ function renderEvent(event: SSEEvent): { content: string; contentColor: string } | null {
30
30
  switch (event.type) {
31
31
  case 'msg:start':
32
32
  return {
33
- content: `\u25B6 Started: ${(event.data.task as string)?.slice(0, 60) ?? ''}`,
33
+ content: `\u25B6 Started: ${(event.data.task as string)?.replace(/\u26D4[^\u26D4]*\u26D4[^"]*/g, '').trim().slice(0, 80) ?? ''}`,
34
34
  contentColor: 'green',
35
35
  };
36
36
 
@@ -44,16 +44,23 @@ function renderEvent(event: SSEEvent, allRoleIds: string[]): { content: string;
44
44
 
45
45
  case 'msg:error':
46
46
  return {
47
- content: `\u2717 Error: ${(event.data.error as string)?.slice(0, 60) ?? ''}`,
47
+ content: `\u2717 Error: ${(event.data.error as string ?? event.data.message as string ?? '').slice(0, 120)}`,
48
48
  contentColor: 'red',
49
49
  };
50
50
 
51
51
  case 'text': {
52
- const text = ((event.data.text as string) ?? '').slice(0, 120);
52
+ const text = ((event.data.text as string) ?? '');
53
53
  if (!text.trim()) return null;
54
+ // Don't truncate — let terminal wrap
54
55
  return { content: text, contentColor: 'white' };
55
56
  }
56
57
 
58
+ case 'thinking': {
59
+ const text = ((event.data.text as string) ?? '').slice(0, 150);
60
+ if (!text.trim()) return null;
61
+ return { content: `\uD83D\uDCAD ${text}`, contentColor: 'gray' };
62
+ }
63
+
57
64
  case 'tool:start': {
58
65
  const name = (event.data.name as string) ?? 'tool';
59
66
  const input = event.data.input;
@@ -61,8 +68,9 @@ function renderEvent(event: SSEEvent, allRoleIds: string[]): { content: string;
61
68
  if (input && typeof input === 'object') {
62
69
  const inp = input as Record<string, unknown>;
63
70
  if (inp.file_path) detail = ` ${String(inp.file_path)}`;
64
- else if (inp.command) detail = ` ${String(inp.command).slice(0, 60)}`;
65
- else detail = ` ${JSON.stringify(input).slice(0, 60)}`;
71
+ else if (inp.command) detail = ` ${String(inp.command).slice(0, 80)}`;
72
+ else if (inp.pattern) detail = ` ${String(inp.pattern)}`;
73
+ else detail = ` ${JSON.stringify(input).slice(0, 80)}`;
66
74
  }
67
75
  return {
68
76
  content: `\u2192 ${name}${detail}`,
@@ -78,7 +86,7 @@ function renderEvent(event: SSEEvent, allRoleIds: string[]): { content: string;
78
86
 
79
87
  case 'dispatch:start':
80
88
  return {
81
- content: `\u21D2 dispatch ${event.data.targetRole as string ?? ''}: ${(event.data.task as string)?.slice(0, 50) ?? ''}`,
89
+ content: `\u21D2 dispatch ${event.data.targetRole as string ?? ''}: ${(event.data.task as string)?.replace(/\u26D4[^\u26D4]*\u26D4[^"]*/g, '').trim().slice(0, 80) ?? ''}`,
82
90
  contentColor: 'yellow',
83
91
  };
84
92
 
@@ -88,14 +96,15 @@ function renderEvent(event: SSEEvent, allRoleIds: string[]): { content: string;
88
96
  contentColor: 'yellow',
89
97
  };
90
98
 
91
- case 'msg:awaiting_input':
99
+ case 'msg:awaiting_input': {
100
+ const question = (event.data.question as string) ?? '';
92
101
  return {
93
- content: '? Awaiting input...',
102
+ content: question ? `? ${question.slice(0, 120)}` : '? Awaiting input...',
94
103
  contentColor: 'yellow',
95
104
  };
105
+ }
96
106
 
97
- // Hidden events
98
- case 'thinking':
107
+ // Hidden (truly internal only)
99
108
  case 'heartbeat:tick':
100
109
  case 'heartbeat:skip':
101
110
  case 'prompt:assembled':
@@ -114,7 +123,7 @@ export const StreamView: React.FC<StreamViewProps> = ({
114
123
  waveId,
115
124
  roleLabel,
116
125
  }) => {
117
- const maxVisible = 20;
126
+ const maxVisible = 30;
118
127
  const visibleEvents = events.slice(-maxVisible);
119
128
 
120
129
  const turnCount = events.filter(e => e.type === 'text' || e.type === 'tool:start').length;
@@ -146,14 +155,14 @@ export const StreamView: React.FC<StreamViewProps> = ({
146
155
  )}
147
156
 
148
157
  {visibleEvents.map((event, i) => {
149
- const rendered = renderEvent(event, allRoleIds);
158
+ const rendered = renderEvent(event);
150
159
  if (!rendered) return null;
151
160
  const roleColor = getRoleColor(event.roleId, allRoleIds);
152
161
  return (
153
162
  <Box key={`${event.seq}-${i}`}>
154
163
  <Text color="gray" dimColor>{formatTime(event.ts)} </Text>
155
164
  <Text color={roleColor} bold>{event.roleId.padEnd(12)}</Text>
156
- <Text color={rendered.contentColor}>{rendered.content}</Text>
165
+ <Text color={rendered.contentColor} wrap="wrap">{rendered.content}</Text>
157
166
  </Box>
158
167
  );
159
168
  })}