tycono 0.1.96-beta.2 → 0.1.96-beta.20

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/README.md CHANGED
@@ -1,12 +1,12 @@
1
1
  <p align="center">
2
- <img src=".github/assets/hero-office.png" alt="Tycono — AI Office" width="720" />
2
+ <img src=".github/assets/wave-org-propagation.png" alt="Tycono — CEO dispatches through org hierarchy in real time" width="720" />
3
3
  </p>
4
4
 
5
5
  <h1 align="center">tycono</h1>
6
6
 
7
7
  <p align="center">
8
- <strong>Build an AI company. Watch them work.</strong><br>
9
- <sub>Infrastructure-as-Code defined servers. Company-as-Code defines organizations.</sub>
8
+ <strong>Cursor gives you one AI developer. Tycono gives you an AI team.</strong><br>
9
+ <sub>Give one order. Watch your AI team plan, build, and learn together.</sub>
10
10
  </p>
11
11
 
12
12
  <p align="center">
@@ -25,26 +25,76 @@
25
25
 
26
26
  ---
27
27
 
28
- **tycono** is an open-source platform that lets you define and run an AI-powered organization. Roles, authority, knowledge, and workflows all defined in files, executed by AI agents, visualized in real time.
28
+ Cursor, Lovable, Bolt they all give you **one AI agent**. It helps, but you still drive everything.
29
29
 
30
- One command. Your AI company is running.
30
+ **tycono** gives you an **AI team**. A CTO reviews architecture. Engineers write code. A PM breaks down tasks. QA catches bugs. You just give the order and watch them work.
31
+
32
+ One command. Your AI team is running.
31
33
 
32
34
  ```bash
33
35
  npx tycono
34
36
  ```
35
37
 
38
+ ## Core Pillars
39
+
40
+ ### 1. CEO Supervisor — Org-chart orchestration
41
+
42
+ You give one order. The system dispatches through a real hierarchy.
43
+
44
+ ```
45
+ dispatch → watch → relay → quality gate → re-dispatch (if needed)
46
+ ```
47
+
48
+ CEO delegates to C-levels, C-levels dispatch to their teams. Authority is enforced — engineers can't make CEO decisions, PMs can't merge code. The org chart isn't decoration, it's the execution engine.
49
+
50
+ <p align="center">
51
+ <img src=".github/assets/wave-org-propagation.png" alt="Wave Center — org propagation with real-time status" width="640" />
52
+ </p>
53
+
54
+ ### 2. Observability — See everything, intervene anytime
55
+
56
+ Your AI team isn't a black box. Watch every agent work in real time, inject directives mid-execution, and drill down to any level.
57
+
58
+ - **Wave Center** — Org-tree dispatch with real-time streaming
59
+ - **Activity Stream** — Every event logged (dispatches, tool calls, decisions)
60
+ - **CEO Directive** — Change direction while agents are running
61
+ - **Cost Tracking** — Per-role, per-model token breakdown
62
+
63
+ ### 3. Isolation Infrastructure — Agents don't collide
64
+
65
+ Multiple agents working simultaneously without stepping on each other.
66
+
67
+ | Resource | Isolation | Status |
68
+ |----------|-----------|--------|
69
+ | **Code** | Git worktree per session | Designed |
70
+ | **Ports** | Dynamic port registry | ✅ Live |
71
+ | **Browser** | Separate daemon per session | ✅ Live |
72
+ | **Knowledge** | Shared reads, scoped writes | ✅ Live |
73
+
74
+ ### 4. AKB (Pre-K / Post-K) — Knowledge that compounds
75
+
76
+ Every AI tool today: `Plan → Execute → Done`. Knowledge resets. Tycono adds what the industry doesn't have:
77
+
78
+ ```
79
+ Pre-K: Read existing knowledge → Plan grounded in what the company knows
80
+ Execute: Do the work
81
+ Post-K: Extract insights → Cross-link → Register in knowledge graph
82
+ ```
83
+
84
+ Session 50 is dramatically smarter than session 1. Your company learns.
85
+
36
86
  ## Why Tycono?
37
87
 
38
- Coding agents simulate **one developer**. Tycono simulates **the entire company**.
88
+ Same goal as Cursor, Lovable, Bolt **get AI to do your work**. Different method.
39
89
 
40
- | | Single AI Agent | Tycono |
90
+ | | Cursor / Lovable / Bolt | Tycono |
41
91
  |---|---|---|
42
- | **What it runs** | One agent, one context | Multiple roles with org hierarchy |
43
- | **Knowledge** | Resets every session | Compounds forever — file-based, cross-linked |
44
- | **Authority** | Can do anything (or nothing) | Scoped each role has clear boundaries |
45
- | **Delegation** | Manual prompt chaining | CEO dispatches, org chart routes automatically |
46
- | **Scale** | 1 agent | 7 700 agents |
47
- | **Visibility** | Terminal output | Isometric office + Slack-style Pro dashboard |
92
+ | **Agents** | 1 AI helps you | **AI team works for you** |
93
+ | **Your role** | Keep directing | **Give one order, watch** |
94
+ | **Knowledge** | Resets every session | **Compounds forever** |
95
+ | **Quality** | You review everything | **QA agent catches bugs** |
96
+ | **Scale** | 1 task at a time | **Parallel across roles** |
97
+ | **Visibility** | Editor / chat | **Real-time org tree** |
48
98
 
49
99
  ## Company-as-Code
50
100
 
@@ -80,33 +130,46 @@ A setup wizard guides you through:
80
130
  - Node.js >= 18
81
131
  - [Anthropic API key](https://console.anthropic.com/) or Claude Max subscription
82
132
 
83
- ## Two Ways to Work
133
+ ## Interfaces
84
134
 
85
- ### Office ViewWatch your AI team
135
+ ### Web DashboardVisual management
86
136
 
87
- An isometric pixel-art office where your AI agents sit at their desks, work, chat, and think. Click any agent to talk to them directly.
137
+ A browser-based dashboard for visual management. Org tree, Wave dispatch, Knowledge graph, Activity stream.
88
138
 
89
139
  <p align="center">
90
- <img src=".github/assets/hero-office.png" alt="Office View" width="640" />
140
+ <img src=".github/assets/hero-office.png" alt="Web Dashboard" width="640" />
91
141
  </p>
92
142
 
93
- - Pixel-art characters with personalities and levels
94
- - Ambient speech bubbles agents think out loud
95
- - Rooms: Leadership, Engineering, Meeting, Knowledge Library
96
- - Edit mode rearrange furniture, customize your office
143
+ - **Wave Center** — selective org-tree dispatch with target checkboxes
144
+ - **Chats** 1:1 conversations with any role, persistent sessions
145
+ - **Knowledge Base** graph/tree/list views, cross-linked documents
146
+ - **Decisions**CEO strategic decision log with full context
97
147
 
98
- ### Pro View Manage your AI company
148
+ ### TUITerminal-native operations *(coming soon)*
99
149
 
100
- A Slack-style professional dashboard for serious work. Chats, Wave dispatch, Decisions log, Knowledge graph.
150
+ For developers who live in the terminal. A k9s/lazygit-style multi-panel TUI built with [Ink](https://github.com/vadimdemedes/ink).
101
151
 
102
- <p align="center">
103
- <img src=".github/assets/pro-view.png" alt="Pro View" width="640" />
104
- </p>
152
+ ```
153
+ ┌──────────────────────────────────────────────────┐
154
+ │ TYCONO v0.2 │ Wave #37 running │ 3 active │$2.1│
155
+ ├──────────────┬───────────────────────────────────┤
156
+ │ [Org Tree] │ [Real-time Stream] │
157
+ │ CEO │ CTO: "Reviewing architecture..." │
158
+ │ ├ CTO ● │ → dispatch → Engineer │
159
+ │ │ ├ ENG ○ │ CBO: "Market analysis done" ✓ │
160
+ │ │ └ QA ○ │ │
161
+ │ └ CBO ● │ │
162
+ ├──────────────┴───────────────────────────────────┤
163
+ │ > wave "Write the Q1 strategy report" │
164
+ └──────────────────────────────────────────────────┘
165
+ ```
105
166
 
106
- - **Wave Center** — selective org-tree dispatch with target checkboxes
107
- - **Chats** 1:1 conversations with any role, persistent sessions
108
- - **Knowledge Base** graph/tree/list views, 194+ cross-linked documents
109
- - **Decisions** — CEO strategic decision log with full context
167
+ ```bash
168
+ npx tycono --tui # Terminal mode (coming soon)
169
+ npx tycono # Web dashboard (current)
170
+ ```
171
+
172
+ Same API server, same engine — just a different frontend. Use what fits your workflow.
110
173
 
111
174
  ## Key Features
112
175
 
@@ -130,10 +193,6 @@ Every task produces knowledge. Cross-linked Markdown documents that grow with ev
130
193
 
131
194
  Each role has scoped authority defined in `role.yaml`. Engineers can't make CEO decisions. PMs can't merge code. The org chart isn't decoration — it's enforcement.
132
195
 
133
- ### Level System
134
-
135
- Roles gain XP from completed work. Level up unlocks accessories and reflects experience. Your CTO at Lv.14 has seen things your new intern hasn't.
136
-
137
196
  ### Local-First, BYOK
138
197
 
139
198
  Everything runs on your machine. Your data never leaves. Bring your own Anthropic API key — no middleman, no telemetry, no tracking.
@@ -181,7 +240,8 @@ your-company/
181
240
  ## CLI Usage
182
241
 
183
242
  ```bash
184
- npx tycono # Start server + open dashboard
243
+ npx tycono # Start server + web dashboard
244
+ npx tycono --tui # Terminal UI (coming soon)
185
245
  npx tycono --help # Show help
186
246
  npx tycono --version # Show version
187
247
  ```
@@ -194,6 +254,17 @@ npx tycono --version # Show version
194
254
  | `PORT` | Server port | auto-detect |
195
255
  | `COMPANY_ROOT` | Company directory | current directory |
196
256
 
257
+ ## Roadmap
258
+
259
+ - [x] Web dashboard (Office + Pro views)
260
+ - [x] CEO Wave dispatch with org-tree targeting
261
+ - [x] AKB — Pre-K / Post-K knowledge loop
262
+ - [x] Port Registry for multi-agent isolation
263
+ - [ ] **TUI mode** — terminal-native multi-panel interface *(in progress)*
264
+ - [ ] Git worktree isolation per agent session
265
+ - [ ] **Desktop app** (.dmg / .exe) — background execution, notifications, no API key setup needed
266
+ - [ ] Multi-LLM support (OpenAI, local models)
267
+
197
268
  ## Built with Tycono
198
269
 
199
270
  This isn't a demo. Tycono's own landing page, documentation, and knowledge base were built by AI agents running inside Tycono. The PM wrote the PRD. The CTO reviewed architecture. The Designer created UX specs. The Engineer implemented every section.
package/bin/tycono.ts CHANGED
@@ -213,6 +213,27 @@ async function startServerForTui(): Promise<void> {
213
213
  const port = process.env.PORT ? Number(process.env.PORT) : await findFreePort();
214
214
  process.env.PORT = String(port);
215
215
 
216
+ // Suppress ALL server output BEFORE creating server — hijack process streams
217
+ const logFile = path.resolve(process.env.COMPANY_ROOT || process.cwd(), '.tycono', 'server.log');
218
+ try { fs.mkdirSync(path.dirname(logFile), { recursive: true }); } catch {}
219
+ const logFd = fs.openSync(logFile, 'a');
220
+ const logStream = fs.createWriteStream(logFile, { fd: logFd });
221
+ const origStdoutWrite = process.stdout.write.bind(process.stdout);
222
+ const origStderrWrite = process.stderr.write.bind(process.stderr);
223
+ // Intercept all stdout/stderr — only allow Ink's output (ANSI escape sequences)
224
+ const isInkOutput = (s: string) => s.includes('\x1b[') || s.includes('\x1b(');
225
+ process.stdout.write = ((chunk: any, ...args: any[]) => {
226
+ const str = typeof chunk === 'string' ? chunk : chunk.toString();
227
+ if (isInkOutput(str)) return origStdoutWrite(chunk, ...args);
228
+ logStream.write(str);
229
+ return true;
230
+ }) as any;
231
+ process.stderr.write = ((chunk: any, ...args: any[]) => {
232
+ logStream.write(typeof chunk === 'string' ? chunk : chunk.toString());
233
+ return true;
234
+ }) as any;
235
+ const origLog = (...args: unknown[]) => origStdoutWrite(args.join(' ') + '\n');
236
+
216
237
  const { createHttpServer } = await import('../src/api/src/create-server.js');
217
238
  const server = createHttpServer();
218
239
 
@@ -222,7 +243,8 @@ async function startServerForTui(): Promise<void> {
222
243
  server.listen(port, host, () => resolve());
223
244
  });
224
245
 
225
- console.log(` API server started on port ${port}`);
246
+ origLog(` API server started on port ${port}`);
247
+ origLog(` Logs: ${logFile}`);
226
248
 
227
249
  // Graceful shutdown
228
250
  const shutdown = () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tycono",
3
- "version": "0.1.96-beta.2",
3
+ "version": "0.1.96-beta.20",
4
4
  "description": "Build an AI company. Watch them work.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -6,6 +6,7 @@
6
6
  import { Router } from 'express';
7
7
  import { portRegistry } from '../services/port-registry.js';
8
8
  import { executionManager } from '../services/execution-manager.js';
9
+ import { getSession } from '../services/session-store.js';
9
10
 
10
11
  export const activeSessionsRouter = Router();
11
12
 
@@ -17,8 +18,10 @@ activeSessionsRouter.get('/', (_req, res) => {
17
18
 
18
19
  const enriched = sessions.map(s => {
19
20
  const exec = executionManager.getActiveExecution(s.sessionId);
21
+ const session = getSession(s.sessionId);
20
22
  return {
21
23
  ...s,
24
+ waveId: session?.waveId ?? null,
22
25
  messageStatus: exec?.status ?? null,
23
26
  roleName: exec?.roleId ?? s.roleId,
24
27
  alive: s.pid ? isAlive(s.pid) : null,
@@ -212,10 +212,8 @@ function handleStartJob(body: Record<string, unknown>, res: ServerResponse): voi
212
212
  const attachments = body.attachments as ImageAttachment[] | undefined;
213
213
 
214
214
  if (type === 'wave') {
215
- if (!directive) {
216
- jsonResponse(res, 400, { error: 'directive is required for wave jobs' });
217
- return;
218
- }
215
+ // directive가 없으면 idle 상태로 시작 (empty wave)
216
+ const actualDirective = directive || '';
219
217
 
220
218
  const targetRoles = body.targetRoles as string[] | undefined;
221
219
  const continuous = body.continuous === true;
@@ -224,7 +222,7 @@ function handleStartJob(body: Record<string, unknown>, res: ServerResponse): voi
224
222
  {
225
223
  const state = supervisorHeartbeat.start(
226
224
  `wave-${Date.now()}`,
227
- directive,
225
+ actualDirective,
228
226
  targetRoles && targetRoles.length > 0 ? targetRoles : undefined,
229
227
  continuous,
230
228
  );
@@ -238,7 +236,7 @@ function handleStartJob(body: Record<string, unknown>, res: ServerResponse): voi
238
236
  waveId: state.waveId,
239
237
  supervisorSessionId: state.supervisorSessionId,
240
238
  mode: 'supervisor',
241
- directive,
239
+ directive: actualDirective,
242
240
  });
243
241
  return;
244
242
  }
@@ -88,6 +88,14 @@ class SupervisorHeartbeat {
88
88
  };
89
89
 
90
90
  this.supervisors.set(waveId, state);
91
+
92
+ // Empty directive → idle wave (don't spawn supervisor yet)
93
+ if (!directive) {
94
+ state.status = 'stopped';
95
+ console.log(`[Supervisor] Idle wave created: ${waveId} (no directive)`);
96
+ return state;
97
+ }
98
+
91
99
  this.spawnSupervisor(state);
92
100
  return state;
93
101
  }
@@ -133,6 +141,17 @@ class SupervisorHeartbeat {
133
141
 
134
142
  state.pendingDirectives.push(directive);
135
143
  console.log(`[Supervisor] Directive queued for wave ${waveId}: ${text.slice(0, 80)}`);
144
+
145
+ // If supervisor is stopped (agent finished or idle wave), wake it up
146
+ if (state.status === 'stopped') {
147
+ // Update the wave's directive if it was empty (idle wave first message)
148
+ if (!state.directive) {
149
+ state.directive = text;
150
+ }
151
+ state.crashCount = 0;
152
+ this.scheduleRestart(state, 0);
153
+ }
154
+
136
155
  return directive;
137
156
  }
138
157
 
package/src/tui/api.ts CHANGED
@@ -16,7 +16,7 @@ export function getBaseUrl(): string {
16
16
 
17
17
  /* ─── HTTP helpers ─── */
18
18
 
19
- async function fetchJson<T>(path: string, options?: { method?: string; body?: unknown }): Promise<T> {
19
+ export async function fetchJson<T>(path: string, options?: { method?: string; body?: unknown }): Promise<T> {
20
20
  const url = `${BASE_URL}${path}`;
21
21
  const method = options?.method ?? 'GET';
22
22
  const bodyStr = options?.body ? JSON.stringify(options.body) : undefined;
@@ -39,7 +39,12 @@ async function fetchJson<T>(path: string, options?: { method?: string; body?: un
39
39
  res.on('data', (chunk) => { data += chunk; });
40
40
  res.on('end', () => {
41
41
  try {
42
- resolve(JSON.parse(data) as T);
42
+ const parsed = JSON.parse(data);
43
+ if (res.statusCode && res.statusCode >= 400) {
44
+ reject(new Error(parsed.error ?? `HTTP ${res.statusCode} from ${path}`));
45
+ return;
46
+ }
47
+ resolve(parsed as T);
43
48
  } catch {
44
49
  reject(new Error(`Invalid JSON from ${path}: ${data.slice(0, 200)}`));
45
50
  }
@@ -124,7 +129,7 @@ export async function fetchExecStatus(): Promise<ExecStatus> {
124
129
  return fetchJson<ExecStatus>('/api/exec/status');
125
130
  }
126
131
 
127
- export async function dispatchWave(directive: string, options?: {
132
+ export async function dispatchWave(directive?: string, options?: {
128
133
  targetRoles?: string[];
129
134
  continuous?: boolean;
130
135
  }): Promise<WaveResponse> {
@@ -132,7 +137,7 @@ export async function dispatchWave(directive: string, options?: {
132
137
  method: 'POST',
133
138
  body: {
134
139
  type: 'wave',
135
- directive,
140
+ directive: directive ?? '',
136
141
  targetRoles: options?.targetRoles,
137
142
  continuous: options?.continuous ?? false,
138
143
  },
@@ -150,13 +155,38 @@ export async function fetchActiveWaves(): Promise<{ waves: Array<{ waveId: strin
150
155
  return fetchJson('/api/waves/active');
151
156
  }
152
157
 
158
+ /* ─── Active Sessions (port/worktree visibility) ─── */
159
+
160
+ export interface ActiveSessionInfo {
161
+ sessionId: string;
162
+ roleId: string;
163
+ task: string;
164
+ ports: { api: number; vite: number; hmr?: number };
165
+ worktreePath?: string;
166
+ pid?: number;
167
+ startedAt: string;
168
+ status: 'active' | 'idle' | 'dead';
169
+ waveId?: string | null;
170
+ messageStatus?: string | null;
171
+ alive?: boolean | null;
172
+ }
173
+
174
+ export interface ActiveSessionsResponse {
175
+ sessions: ActiveSessionInfo[];
176
+ summary: { active: number; totalPorts: number };
177
+ }
178
+
179
+ export async function fetchActiveSessions(): Promise<ActiveSessionsResponse> {
180
+ return fetchJson<ActiveSessionsResponse>('/api/active-sessions');
181
+ }
182
+
153
183
  /* ─── Setup API calls ─── */
154
184
 
155
185
  export interface TeamTemplate {
156
186
  id: string;
157
187
  name: string;
158
188
  description: string;
159
- roles: string[];
189
+ roles: Array<string | { id: string; name: string; level?: string }>;
160
190
  }
161
191
 
162
192
  export interface ScaffoldResult {
@@ -227,9 +257,12 @@ export function subscribeToWaveStream(
227
257
  }
228
258
  }
229
259
 
230
- if (eventType === 'activity' && data) {
260
+ if ((eventType === 'activity' || eventType === 'wave:event') && data) {
231
261
  try {
232
- onEvent(JSON.parse(data) as SSEEvent);
262
+ const parsed = JSON.parse(data);
263
+ // wave:event wraps the actual event in .event field
264
+ const evt = parsed.event ?? parsed;
265
+ onEvent(evt as SSEEvent);
233
266
  } catch { /* ignore parse errors */ }
234
267
  } else if (eventType === 'stream:end' && data) {
235
268
  try {