tycono 0.1.94-beta.7 → 0.1.94-beta.9

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.94-beta.7",
3
+ "version": "0.1.94-beta.9",
4
4
  "description": "Build an AI company. Watch them work.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -128,7 +128,8 @@ export function createHttpServer(): http.Server {
128
128
  }
129
129
 
130
130
  // SSE 엔드포인트: Express 우회하여 raw HTTP로 처리
131
- if ((url.startsWith('/api/exec/') || url.startsWith('/api/jobs') || url === '/api/waves/save' || url === '/api/setup/import-knowledge') && method === 'POST') {
131
+ // BUG-008: /api/waves/:waveId/directive and /api/waves/:waveId/question POST 포함
132
+ if ((url.startsWith('/api/exec/') || url.startsWith('/api/jobs') || url.startsWith('/api/waves/') || url === '/api/waves/save' || url === '/api/setup/import-knowledge') && method === 'POST') {
132
133
  setExecCors(req, res);
133
134
  if (url === '/api/setup/import-knowledge') {
134
135
  handleImportKnowledge(req, res);
@@ -263,6 +263,11 @@ export function deleteSession(id: string, force = false): boolean {
263
263
  console.warn(`[SessionStore] BLOCKED deletion of wave session ${id} (waveId=${session.waveId}, roleId=${session.roleId}). Use force=true to override.`);
264
264
  return false;
265
265
  }
266
+ // BUG-008 hard guard: CEO supervisor session is NEVER deletable during wave
267
+ if (session.roleId === 'ceo' && session.waveId && session.source === 'wave') {
268
+ console.error(`[SessionStore] HARD BLOCK: CEO supervisor session ${id} cannot be deleted (waveId=${session.waveId}). This is a 1:1:1 invariant.`);
269
+ return false;
270
+ }
266
271
 
267
272
  console.log(`[SessionStore] Deleting session ${id} (roleId=${session.roleId}, waveId=${session.waveId ?? 'none'}, messages=${session.messages.length})`);
268
273
  cache.delete(id);
@@ -328,27 +328,33 @@ Re-dispatch pattern:
328
328
  6. If gaps exist → re-dispatch with specific feedback. Repeat 3-5.
329
329
  7. Only when ALL requirements are met → compile results and report`;
330
330
 
331
- // Create supervisor session
332
- const session = createSession('ceo', {
333
- mode: 'do',
334
- source: 'wave',
335
- waveId: state.waveId,
336
- });
337
-
338
- state.supervisorSessionId = session.id;
331
+ // BUG-008 fix: Wave:Supervisor:Session = 1:1:1 invariant.
332
+ // Reuse existing session on restart instead of creating a new one.
333
+ let sessionId = state.supervisorSessionId;
334
+ if (sessionId && getSession(sessionId)) {
335
+ console.log(`[Supervisor] Reusing existing session ${sessionId} for wave ${state.waveId}`);
336
+ } else {
337
+ const session = createSession('ceo', {
338
+ mode: 'do',
339
+ source: 'wave',
340
+ waveId: state.waveId,
341
+ });
342
+ sessionId = session.id;
343
+ state.supervisorSessionId = sessionId;
344
+
345
+ // Add the directive as CEO message so the session isn't empty (prevents deleteEmpty cleanup)
346
+ const ceoMsg: Message = {
347
+ id: `msg-${Date.now()}-ceo-supervisor`,
348
+ from: 'ceo',
349
+ content: state.directive,
350
+ type: 'directive',
351
+ status: 'done',
352
+ timestamp: new Date().toISOString(),
353
+ };
354
+ addMessage(sessionId, ceoMsg);
355
+ }
339
356
  state.status = 'running';
340
357
 
341
- // Add the directive as CEO message so the session isn't empty (prevents deleteEmpty cleanup)
342
- const ceoMsg: Message = {
343
- id: `msg-${Date.now()}-ceo-supervisor`,
344
- from: 'ceo',
345
- content: state.directive,
346
- type: 'directive',
347
- status: 'done',
348
- timestamp: new Date().toISOString(),
349
- };
350
- addMessage(session.id, ceoMsg);
351
-
352
358
  try {
353
359
  const exec = executionManager.startExecution({
354
360
  type: 'wave',
@@ -356,14 +362,14 @@ Re-dispatch pattern:
356
362
  task: supervisorTask,
357
363
  sourceRole: 'ceo',
358
364
  targetRoles: state.targetRoles,
359
- sessionId: session.id,
365
+ sessionId,
360
366
  });
361
367
 
362
368
  state.executionId = exec.id;
363
369
 
364
370
  this.watchExecution(state, exec);
365
371
 
366
- console.log(`[Supervisor] Started for wave ${state.waveId} | session=${session.id} | exec=${exec.id}`);
372
+ console.log(`[Supervisor] Started for wave ${state.waveId} | session=${sessionId} | exec=${exec.id}`);
367
373
  } catch (err) {
368
374
  console.error(`[Supervisor] Failed to start for wave ${state.waveId}:`, err);
369
375
  state.status = 'error';
@@ -378,6 +384,12 @@ Re-dispatch pattern:
378
384
  } else if (event.type === 'msg:error') {
379
385
  exec.stream.unsubscribe(subscriber);
380
386
  this.onSupervisorCrash(state, String(event.data.message ?? 'unknown error'));
387
+ } else if (event.type === 'msg:awaiting_input') {
388
+ // BUG-016: turn:limit causes awaiting_input — treat as done-guard
389
+ // If all children are done → complete wave. Otherwise restart supervisor.
390
+ exec.stream.unsubscribe(subscriber);
391
+ console.log(`[Supervisor] awaiting_input (turn limit) for wave ${state.waveId}. Running done-guard.`);
392
+ this.onSupervisorDone(state);
381
393
  }
382
394
  };
383
395