tycono 0.1.94-beta.0 → 0.1.94-beta.10

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 (34) hide show
  1. package/package.json +3 -2
  2. package/src/api/src/create-server.ts +2 -1
  3. package/src/api/src/engine/agent-loop.ts +149 -142
  4. package/src/api/src/engine/context-assembler.ts +147 -66
  5. package/src/api/src/engine/org-tree.ts +1 -1
  6. package/src/api/src/engine/role-lifecycle.ts +1 -1
  7. package/src/api/src/engine/runners/claude-cli.ts +2 -3
  8. package/src/api/src/engine/tools/definitions.ts +7 -4
  9. package/src/api/src/engine/tools/executor.ts +37 -3
  10. package/src/api/src/routes/execute.ts +171 -148
  11. package/src/api/src/routes/operations.ts +1 -1
  12. package/src/api/src/routes/sessions.ts +2 -0
  13. package/src/api/src/services/company-config.ts +3 -0
  14. package/src/api/src/services/digest-engine.ts +2 -2
  15. package/src/api/src/services/execution-manager.ts +82 -4
  16. package/src/api/src/services/scaffold.ts +24 -6
  17. package/src/api/src/services/session-store.ts +20 -2
  18. package/src/api/src/services/supervisor-heartbeat.ts +461 -0
  19. package/src/api/src/services/wave-multiplexer.ts +11 -3
  20. package/src/api/src/services/wave-tracker.ts +175 -0
  21. package/src/shared/types.ts +3 -2
  22. package/src/web/dist/assets/index-B8yxzPmd.css +1 -0
  23. package/src/web/dist/assets/index-C9U34tT1.js +138 -0
  24. package/src/web/dist/assets/index-DuB5baFp.js +1 -0
  25. package/src/web/dist/assets/preview-app-QWV7zpb0.js +1 -0
  26. package/src/web/dist/index.html +2 -2
  27. package/src/web/dist/tyconoforge.js +1 -0
  28. package/templates/teams/agency.json +31 -7
  29. package/templates/teams/research.json +32 -8
  30. package/templates/teams/startup.json +31 -7
  31. package/src/web/dist/assets/index-BMTSnwP3.js +0 -1
  32. package/src/web/dist/assets/index-BnhRwHgb.css +0 -1
  33. package/src/web/dist/assets/index-Cs1ZyjC9.js +0 -116
  34. package/src/web/dist/assets/preview-app-DgScvta-.js +0 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tycono",
3
- "version": "0.1.94-beta.0",
3
+ "version": "0.1.94-beta.10",
4
4
  "description": "Build an AI company. Watch them work.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -23,7 +23,7 @@
23
23
  "dev:web": "npm run dev --prefix src/web",
24
24
  "build": "npm run build:web && npm run build:forge",
25
25
  "build:web": "npm run build --prefix src/web",
26
- "build:forge": "tsup --config tsup.forge.ts",
26
+ "build:forge": "cp node_modules/tyconoforge/dist/tyconoforge.js src/web/dist/tyconoforge.js",
27
27
  "typecheck": "npm run typecheck:api && npm run typecheck:web",
28
28
  "typecheck:api": "cd src/api && npx tsc --noEmit",
29
29
  "typecheck:web": "cd src/web && npx tsc --noEmit",
@@ -38,6 +38,7 @@
38
38
  "gray-matter": "^4.0.3",
39
39
  "marked": "^15.0.6",
40
40
  "tsx": "^4.19.3",
41
+ "tyconoforge": "^0.1.0-beta.0",
41
42
  "yaml": "^2.7.0"
42
43
  },
43
44
  "devDependencies": {
@@ -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);
@@ -172,7 +172,12 @@ export async function runAgentLoop(config: AgentConfig): Promise<AgentResult> {
172
172
  const hasBash = !readOnly && !!config.codeRoot;
173
173
  const node = orgTree.nodes.get(roleId);
174
174
  const heartbeatEnabled = node?.heartbeat?.enabled === true && subordinates.length > 0;
175
- const tools = getToolsForRole(subordinates.length > 0, readOnly, hasBash, heartbeatEnabled);
175
+ // Peers = other roles with the same reportsTo (same parent in org tree)
176
+ const parentId = node?.reportsTo;
177
+ const hasPeers = parentId
178
+ ? (getSubordinates(orgTree, parentId).filter(id => id !== roleId).length > 0)
179
+ : false;
180
+ const tools = getToolsForRole(subordinates.length > 0, readOnly, hasBash, heartbeatEnabled, hasPeers);
176
181
 
177
182
  // 3. Set up tool executor
178
183
  const toolExecOptions: ToolExecutorOptions = {
@@ -550,163 +555,63 @@ export async function runAgentLoop(config: AgentConfig): Promise<AgentResult> {
550
555
  }
551
556
  }
552
557
 
553
- // Phase B: Engineer/CTO Verificationtype checking + visual verification
554
- const verifiableRoles = ['engineer', 'cto'];
555
- if (verifiableRoles.includes(roleId)) {
556
- const hasFileChanges = allToolCalls.some((tc) =>
557
- ['write', 'edit', 'bash'].includes(tc.name.toLowerCase()),
558
- );
558
+ // Detect file changes onceused by Phase B and Post-K
559
+ const hasFileChanges = allToolCalls.some((tc) =>
560
+ ['write', 'edit', 'bash'].includes(tc.name.toLowerCase()),
561
+ );
559
562
 
560
- if (hasFileChanges) {
561
- const verifyPrompt = [
562
- '[AUTO-VERIFICATION] 작업이 완료되었습니다. 아래 검증을 수행하세요:',
563
- '1. `cd src/api && npx tsc --noEmit` — 타입 에러 확인',
564
- '2. `cd src/web && npx tsc --noEmit` — 프론트엔드 타입 에러 확인',
565
- '3. UI/CSS 변경이 있었다면 Playwright MCP로 스크린샷을 촬영하여 시각 검증',
566
- '검증 결과를 간단히 보고하세요.',
567
- ].join('\n');
568
-
569
- messages.push({ role: 'user', content: verifyPrompt });
570
-
571
- if (turns < maxTurns) {
572
- turns++;
573
- const verifyResponse = await llm.chat(context.systemPrompt, messages, tools, abortSignal);
574
- totalInput += verifyResponse.usage.inputTokens;
575
- totalOutput += verifyResponse.usage.outputTokens;
576
- config.tokenLedger?.record({
577
- ts: new Date().toISOString(),
578
- sessionId: config.sessionId,
579
- roleId,
580
- model: config.model ?? 'unknown',
581
- inputTokens: verifyResponse.usage.inputTokens,
582
- outputTokens: verifyResponse.usage.outputTokens,
583
- });
563
+ // Phase B: Member Self-Verification — type checking + visual verification
564
+ // Any non-C-Level role that made file changes gets verification (no hardcoded role IDs)
565
+ if (!isCLevel && hasFileChanges) {
566
+ const verifyPrompt = [
567
+ '[AUTO-VERIFICATION] 작업이 완료되었습니다. 아래 검증을 수행하세요:',
568
+ '1. `cd src/api && npx tsc --noEmit` 타입 에러 확인',
569
+ '2. `cd src/web && npx tsc --noEmit` — 프론트엔드 타입 에러 확인',
570
+ '3. UI/CSS 변경이 있었다면 Playwright MCP로 스크린샷을 촬영하여 시각 검증',
571
+ '검증 결과를 간단히 보고하세요.',
572
+ ].join('\n');
584
573
 
585
- messages.push({ role: 'assistant', content: verifyResponse.content });
586
- for (const block of verifyResponse.content) {
587
- if (block.type === 'text' && block.text) {
588
- outputParts.push(block.text);
589
- onText?.(block.text);
590
- }
591
- }
574
+ messages.push({ role: 'user', content: verifyPrompt });
592
575
 
593
- // Execute verification tool calls if needed
594
- if (verifyResponse.stopReason === 'tool_use') {
595
- const verifyToolCalls = verifyResponse.content.filter(
596
- (b): b is MessageContent & { type: 'tool_use' } => b.type === 'tool_use',
597
- );
598
- const verifyResults: ToolResult[] = [];
599
- for (const tc of verifyToolCalls) {
600
- allToolCalls.push({ name: tc.name, input: tc.input });
601
- const result = await executeTool(
602
- { id: tc.id, name: tc.name, input: tc.input },
603
- toolExecOptions,
604
- );
605
- verifyResults.push(result);
606
- }
607
- messages.push({
608
- role: 'user',
609
- content: verifyResults.map((r) => ({
610
- type: 'tool_result' as const,
611
- tool_use_id: r.tool_use_id,
612
- content: r.content,
613
- is_error: r.is_error,
614
- })) as unknown as MessageContent[],
615
- });
576
+ if (turns < maxTurns) {
577
+ turns++;
578
+ const verifyResponse = await llm.chat(context.systemPrompt, messages, tools, abortSignal);
579
+ totalInput += verifyResponse.usage.inputTokens;
580
+ totalOutput += verifyResponse.usage.outputTokens;
581
+ config.tokenLedger?.record({
582
+ ts: new Date().toISOString(),
583
+ sessionId: config.sessionId,
584
+ roleId,
585
+ model: config.model ?? 'unknown',
586
+ inputTokens: verifyResponse.usage.inputTokens,
587
+ outputTokens: verifyResponse.usage.outputTokens,
588
+ });
616
589
 
617
- if (turns < maxTurns) {
618
- turns++;
619
- const summaryResponse = await llm.chat(context.systemPrompt, messages, tools, abortSignal);
620
- totalInput += summaryResponse.usage.inputTokens;
621
- totalOutput += summaryResponse.usage.outputTokens;
622
- config.tokenLedger?.record({
623
- ts: new Date().toISOString(),
624
- sessionId: config.sessionId,
625
- roleId,
626
- model: config.model ?? 'unknown',
627
- inputTokens: summaryResponse.usage.inputTokens,
628
- outputTokens: summaryResponse.usage.outputTokens,
629
- });
630
- for (const block of summaryResponse.content) {
631
- if (block.type === 'text' && block.text) {
632
- outputParts.push(block.text);
633
- onText?.(block.text);
634
- }
635
- }
636
- }
590
+ messages.push({ role: 'assistant', content: verifyResponse.content });
591
+ for (const block of verifyResponse.content) {
592
+ if (block.type === 'text' && block.text) {
593
+ outputParts.push(block.text);
594
+ onText?.(block.text);
637
595
  }
638
-
639
- onTurnComplete?.(turns);
640
596
  }
641
597
 
642
- // ── Post-Knowledging: ④⑤ 수행 프롬프트 (KP-008) ──
643
- // Engineer/CTO가 구현 완료 후 The Loop 마무리 (Knowledge 업데이트 + Task 상태 갱신) 수행하도록 유도
644
- const postKPrompt = [
645
- '[POST-KNOWLEDGING] 구현이 완료되었습니다. The Loop 마무리를 수행하세요:',
646
- '',
647
- '## ④ Knowledge 업데이트 (The Loop Step 4)',
648
- '다음 중 해당하는 항목을 수행하세요:',
649
- '- 본인 journal 업데이트 (`roles/' + roleId + '/journal/YYYY-MM-DD.md` — 오늘 날짜 파일)',
650
- '- 구현 중 새로 발견한 패턴/아키텍처 결정이 있다면 관련 문서 업데이트',
651
- ' (예: architecture/web-app-ia.md, architecture/session-worktree-isolation.md 등)',
652
- '- 중요한 기술 결정은 operations/decisions/ 또는 architecture/ 반영',
653
- '',
654
- '## ⑤ Task 상태 갱신 (The Loop Step 5)',
655
- '- `projects/tycono-platform/tasks.md` (또는 관련 tasks 파일)에서 완료한 태스크 상태를 DONE으로 변경',
656
- '- 다음 작업이 있다면 식별하여 메모',
657
- '',
658
- '⛔ **필수**: ④와 ⑤를 모두 수행해야 The Loop이 완료됩니다.',
659
- '이제 ④⑤를 수행하세요 (Read, Edit 도구 사용).',
660
- ].join('\n');
661
-
662
- messages.push({ role: 'user', content: postKPrompt });
663
-
664
- // Run Post-K loop (최대 2턴 — C-Level의 3턴보다 짧게)
665
- const maxPostKRounds = 2;
666
- for (let round = 0; round < maxPostKRounds && turns < maxTurns; round++) {
667
- if (abortSignal?.aborted) break;
668
- turns++;
669
-
670
- const postKResponse = await llm.chat(context.systemPrompt, messages, tools, abortSignal);
671
- totalInput += postKResponse.usage.inputTokens;
672
- totalOutput += postKResponse.usage.outputTokens;
673
- config.tokenLedger?.record({
674
- ts: new Date().toISOString(),
675
- sessionId: config.sessionId,
676
- roleId,
677
- model: config.model ?? 'unknown',
678
- inputTokens: postKResponse.usage.inputTokens,
679
- outputTokens: postKResponse.usage.outputTokens,
680
- });
681
-
682
- messages.push({ role: 'assistant', content: postKResponse.content });
683
- for (const block of postKResponse.content) {
684
- if (block.type === 'text' && block.text) {
685
- outputParts.push(block.text);
686
- onText?.(block.text);
687
- }
688
- }
689
-
690
- // If no tool calls, Post-K is done
691
- if (postKResponse.stopReason !== 'tool_use') break;
692
-
693
- // Execute Post-K tool calls
694
- const postKToolCalls = postKResponse.content.filter(
598
+ // Execute verification tool calls if needed
599
+ if (verifyResponse.stopReason === 'tool_use') {
600
+ const verifyToolCalls = verifyResponse.content.filter(
695
601
  (b): b is MessageContent & { type: 'tool_use' } => b.type === 'tool_use',
696
602
  );
697
- const postKResults: ToolResult[] = [];
698
- for (const tc of postKToolCalls) {
603
+ const verifyResults: ToolResult[] = [];
604
+ for (const tc of verifyToolCalls) {
699
605
  allToolCalls.push({ name: tc.name, input: tc.input });
700
606
  const result = await executeTool(
701
607
  { id: tc.id, name: tc.name, input: tc.input },
702
608
  toolExecOptions,
703
609
  );
704
- postKResults.push(result);
610
+ verifyResults.push(result);
705
611
  }
706
-
707
612
  messages.push({
708
613
  role: 'user',
709
- content: postKResults.map((r) => ({
614
+ content: verifyResults.map((r) => ({
710
615
  type: 'tool_result' as const,
711
616
  tool_use_id: r.tool_use_id,
712
617
  content: r.content,
@@ -714,8 +619,110 @@ export async function runAgentLoop(config: AgentConfig): Promise<AgentResult> {
714
619
  })) as unknown as MessageContent[],
715
620
  });
716
621
 
717
- onTurnComplete?.(turns);
622
+ if (turns < maxTurns) {
623
+ turns++;
624
+ const summaryResponse = await llm.chat(context.systemPrompt, messages, tools, abortSignal);
625
+ totalInput += summaryResponse.usage.inputTokens;
626
+ totalOutput += summaryResponse.usage.outputTokens;
627
+ config.tokenLedger?.record({
628
+ ts: new Date().toISOString(),
629
+ sessionId: config.sessionId,
630
+ roleId,
631
+ model: config.model ?? 'unknown',
632
+ inputTokens: summaryResponse.usage.inputTokens,
633
+ outputTokens: summaryResponse.usage.outputTokens,
634
+ });
635
+ for (const block of summaryResponse.content) {
636
+ if (block.type === 'text' && block.text) {
637
+ outputParts.push(block.text);
638
+ onText?.(block.text);
639
+ }
640
+ }
641
+ }
642
+ }
643
+
644
+ onTurnComplete?.(turns);
645
+ }
646
+ }
647
+
648
+ // ── Post-K: ④⑤ Knowledge/Task update (KP-008) ──
649
+ // ALL roles (C-Level and members) update journal/tasks after significant work.
650
+ // Runs for: members who made file changes, C-Level who dispatched work.
651
+ if (hasFileChanges || dispatches.length > 0) {
652
+ const postKPrompt = [
653
+ '[POST-KNOWLEDGING] 작업이 완료되었습니다. The Loop 마무리를 수행하세요:',
654
+ '',
655
+ '## ④ Knowledge 업데이트 (The Loop Step 4)',
656
+ '다음 중 해당하는 항목을 수행하세요:',
657
+ '- 본인 journal 업데이트 (`roles/' + roleId + '/journal/YYYY-MM-DD.md` — 오늘 날짜 파일)',
658
+ '- 구현 중 새로 발견한 패턴/아키텍처 결정이 있다면 관련 문서 업데이트',
659
+ ' (예: architecture/web-app-ia.md, architecture/session-worktree-isolation.md 등)',
660
+ '- 중요한 기술 결정은 operations/decisions/ 또는 architecture/ 반영',
661
+ '',
662
+ '## ⑤ Task 상태 갱신 (The Loop Step 5)',
663
+ '- `projects/tycono-platform/tasks.md` (또는 관련 tasks 파일)에서 완료한 태스크 상태를 DONE으로 변경',
664
+ '- 다음 작업이 있다면 식별하여 메모',
665
+ '',
666
+ '⛔ **필수**: ④와 ⑤를 모두 수행해야 The Loop이 완료됩니다.',
667
+ '이제 ④⑤를 수행하세요 (Read, Edit 도구 사용).',
668
+ ].join('\n');
669
+
670
+ messages.push({ role: 'user', content: postKPrompt });
671
+
672
+ // Run Post-K loop (최대 2턴)
673
+ const maxPostKRounds = 2;
674
+ for (let round = 0; round < maxPostKRounds && turns < maxTurns; round++) {
675
+ if (abortSignal?.aborted) break;
676
+ turns++;
677
+
678
+ const postKResponse = await llm.chat(context.systemPrompt, messages, tools, abortSignal);
679
+ totalInput += postKResponse.usage.inputTokens;
680
+ totalOutput += postKResponse.usage.outputTokens;
681
+ config.tokenLedger?.record({
682
+ ts: new Date().toISOString(),
683
+ sessionId: config.sessionId,
684
+ roleId,
685
+ model: config.model ?? 'unknown',
686
+ inputTokens: postKResponse.usage.inputTokens,
687
+ outputTokens: postKResponse.usage.outputTokens,
688
+ });
689
+
690
+ messages.push({ role: 'assistant', content: postKResponse.content });
691
+ for (const block of postKResponse.content) {
692
+ if (block.type === 'text' && block.text) {
693
+ outputParts.push(block.text);
694
+ onText?.(block.text);
695
+ }
696
+ }
697
+
698
+ // If no tool calls, Post-K is done
699
+ if (postKResponse.stopReason !== 'tool_use') break;
700
+
701
+ // Execute Post-K tool calls
702
+ const postKToolCalls = postKResponse.content.filter(
703
+ (b): b is MessageContent & { type: 'tool_use' } => b.type === 'tool_use',
704
+ );
705
+ const postKResults: ToolResult[] = [];
706
+ for (const tc of postKToolCalls) {
707
+ allToolCalls.push({ name: tc.name, input: tc.input });
708
+ const result = await executeTool(
709
+ { id: tc.id, name: tc.name, input: tc.input },
710
+ toolExecOptions,
711
+ );
712
+ postKResults.push(result);
718
713
  }
714
+
715
+ messages.push({
716
+ role: 'user',
717
+ content: postKResults.map((r) => ({
718
+ type: 'tool_result' as const,
719
+ tool_use_id: r.tool_use_id,
720
+ content: r.content,
721
+ is_error: r.is_error,
722
+ })) as unknown as MessageContent[],
723
+ });
724
+
725
+ onTurnComplete?.(turns);
719
726
  }
720
727
  }
721
728
  }