tycono 0.1.96-beta.41 → 0.1.96-beta.42

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.41",
3
+ "version": "0.1.96-beta.42",
4
4
  "description": "Build an AI company. Watch them work.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,11 +1,40 @@
1
1
  import { COMPANY_ROOT } from './services/file-reader.js';
2
2
  import { applyConfig } from './services/company-config.js';
3
3
  import { createHttpServer } from './create-server.js';
4
+ import { listSessions, updateSession } from './services/session-store.js';
5
+ import { ActivityStream } from './services/activity-stream.js';
4
6
 
5
7
  // Load .tycono/config.json and apply to process.env
6
8
  const config = applyConfig(COMPANY_ROOT);
7
9
  console.log(`[STARTUP] Engine: ${config.engine}, API key: ${config.apiKey ? 'set' : 'none'}`);
8
10
 
11
+ // Startup: mark orphaned 'active' sessions as 'interrupted'
12
+ // These are sessions from a previous server that crashed or was killed
13
+ {
14
+ const allSessions = listSessions();
15
+ let orphaned = 0;
16
+ for (const ses of allSessions) {
17
+ if (ses.status !== 'active') continue;
18
+ // Check activity stream — if it has msg:done/msg:error, mark done
19
+ // If not, mark interrupted (previous server died mid-execution)
20
+ if (ActivityStream.exists(ses.id)) {
21
+ const events = ActivityStream.readFrom(ses.id, 0);
22
+ const tail = events.slice(-5);
23
+ const isDone = tail.some(e => e.type === 'msg:done' || e.type === 'msg:error');
24
+ if (isDone) {
25
+ updateSession(ses.id, { status: 'done' });
26
+ orphaned++;
27
+ continue;
28
+ }
29
+ }
30
+ updateSession(ses.id, { status: 'interrupted' as any });
31
+ orphaned++;
32
+ }
33
+ if (orphaned > 0) {
34
+ console.log(`[STARTUP] Cleaned ${orphaned} orphaned sessions (active → done/interrupted)`);
35
+ }
36
+ }
37
+
9
38
  const PORT = Number(process.env.PORT) || 3001;
10
39
  const server = createHttpServer();
11
40
 
@@ -13,3 +42,18 @@ server.listen(PORT, () => {
13
42
  console.log(`[API] Server running on http://localhost:${PORT}`);
14
43
  console.log(`[API] COMPANY_ROOT: ${COMPANY_ROOT}`);
15
44
  });
45
+
46
+ // Graceful shutdown: mark running sessions as interrupted
47
+ function gracefulShutdown(signal: string) {
48
+ console.log(`[SHUTDOWN] ${signal} received, marking active sessions as interrupted...`);
49
+ const sessions = listSessions();
50
+ for (const ses of sessions) {
51
+ if (ses.status === 'active') {
52
+ updateSession(ses.id, { status: 'interrupted' as any });
53
+ }
54
+ }
55
+ process.exit(0);
56
+ }
57
+
58
+ process.on('SIGINT', () => gracefulShutdown('SIGINT'));
59
+ process.on('SIGTERM', () => gracefulShutdown('SIGTERM'));
@@ -10,7 +10,7 @@ import { estimateCost } from './pricing.js';
10
10
  import { readConfig, getConversationLimits, resolveCodeRoot } from './company-config.js';
11
11
  import { postKnowledgingCheck, type KnowledgeDebtItem } from '../engine/knowledge-gate.js';
12
12
  import { earnCoinsInternal } from '../routes/coins.js';
13
- import { getSession, createSession, addMessage, updateMessage as updateSessionMessage, appendMessageEvent, type Message, type ImageAttachment } from './session-store.js';
13
+ import { getSession, createSession, addMessage, updateMessage as updateSessionMessage, updateSession, appendMessageEvent, type Message, type ImageAttachment } from './session-store.js';
14
14
  import { portRegistry, type PortAllocation } from './port-registry.js';
15
15
  import { type MessageStatus, isMessageActive, canTransition, messageStatusToRoleStatus } from '../../../shared/types.js';
16
16
 
@@ -603,6 +603,12 @@ class ExecutionManager {
603
603
  knowledgeDebt: execution.knowledgeDebt.map(d => ({ type: d.type, file: d.file, message: d.message })),
604
604
  }),
605
605
  });
606
+
607
+ // Mark session as done in session-store (persisted to file)
608
+ // Skip CEO supervisor sessions — they stay active for wave lifecycle
609
+ if (session.roleId !== 'ceo' || session.source !== 'wave') {
610
+ updateSession(execution.sessionId, { status: 'done' });
611
+ }
606
612
  }
607
613
 
608
614
  private cleanupOrphanedChildren(parentSessionId: string): void {