Files
connectai/src/sidebar/managers/companyTurn.ts
T
g1nation 0a97324f1b feat: v2.2.92 → v2.2.158 — god-file 분해 + Stocks feature + 대화 연속성
R56–R59: agent.ts 2731→1529줄 god-file 분해 (25 modules)
  · attrParsers + LLM 메서드 8개 (callNonStreaming, streamChatOnce 등)
  · executeActions 415줄 → 8 handler 그룹 (file/run/list/brain/calendar/sheets/tasks)
  · handlePrompt 1100줄 → 7 phase 모듈 (system prompt + budget + autoContinue 등)

R50–R55: extension.ts 1145→349줄 (telegram/settings/provider commands 분리)

Stocks feature 신규: /stocks slash command (v2.2.152~158)
  · .astra/stocks.json 저장소 + Yahoo Finance 현재가 갱신
  · 8 키워드 필터 (ROE/성장성/유동성/수익성/영업효율/기술력/안정성/PBR)
  · Naver 시가총액 페이지 JSON API (m.stock.naver.com) 발굴
  · LLM Top 5 매력도 분석 + Telegram 자동 보고서
  · KST 09:00/15:00 watcher 자동 모니터링

대화 연속성 (v2.2.150~157):
  · [PRIOR TURN CONCLUSION] block 으로 직전 결론 anchor
  · thin follow-up 분류 → boilerplate 헤더 suppression
  · slash 명령 결과 chatHistory mirror (capture wrapper)
  · echo/parrot 금지 system prompt rule

기타: /stocks 슬래시 자동완성 dropdown UI, Naver JSON API 전환 (cheerio 제거)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-25 09:59:32 +09:00

117 lines
4.3 KiB
TypeScript

import type { RequirementContract } from '../../features/company';
/**
* 1인 기업 모드 turn 의 runtime 상태 — *진행 중* turn 의 abort handle 과
* *방금 끝난* turn 의 요약 스냅샷을 함께 보관.
*
* 옛 코드는 sidebarProvider 의 두 slot(`_companyAbort`, `_lastCompanyTurnSummary`)
* 으로 흩어져 있어, "turn 끝" 시 abort 정리와 summary 갱신이 서로 다른 분기에서
* 일어났음. Manager 로 묶어 turn lifecycle 의 진입/종료 책임을 한 곳에 모았다.
*
* Summary 슬롯은 *완료된* turn 만 채운다 (abort/error 는 비움 유지) — intent
* classifier 가 "이전 turn 후속" vs "신규 task" 를 판단할 때 abort 된 turn 을
* 후속의 기준으로 삼으면 안 되기 때문.
*/
export class CompanyTurnRuntime {
private abortController?: AbortController;
private lastSummary?: {
brief: string;
reportTail: string;
finishedAt: number;
};
/** Turn 시작 — fresh AbortController 등록. dispatcher signal 로 전달됨. */
startTurn(): AbortController {
const ctrl = new AbortController();
this.abortController = ctrl;
return ctrl;
}
/**
* Turn 종료(완료/실패/abort 무관) — finally 절에서 호출. 우리가 발급한 그
* controller 일 때만 비워 race 방지 (느린 cleanup 이 새 turn 의 controller
* 를 잘못 지우는 경우 차단).
*/
endTurn(ctrl: AbortController): void {
if (this.abortController === ctrl) this.abortController = undefined;
}
/**
* Stop 버튼 — abort 신호 emit. 호출 시점에 진행 중 turn 없으면 false.
* 호출자(provider) 는 추가로 ApprovalGateManager.abortAll() 도 호출해야
* dispatcher 의 stage gate await 가 함께 깨어남.
*/
abort(): boolean {
if (!this.abortController) return false;
this.abortController.abort();
return true;
}
/** report-done 시점에 호출. abort / error 케이스는 호출 안 됨 (intent 의도). */
recordSummary(brief: string, reportTail: string): void {
this.lastSummary = { brief, reportTail, finishedAt: Date.now() };
}
/** Intent classifier 가 follow-up 판단할 때 읽음. cold start 면 undefined. */
getLastSummary() {
return this.lastSummary;
}
/** 새 chat / 다른 세션 load 시 — 옛 report 가 의도 분류 오염하지 않게. */
clearLastSummary(): void {
this.lastSummary = undefined;
}
}
/**
* Intent Alignment 의 *현재 미해결* 상태. 분석기가 round 를 끝낸 후 사용자
* 확인이 필요한 경우 이 슬롯에 contract 를 보관 → 다음 사용자 메시지를 답변으로
* 해석. 한 번에 한 alignment 만 진행 (회사 모드는 sequential).
*
* 옛 코드는 sidebarProvider 의 단일 slot 으로 4개 메서드에 흩어져 있었음.
* Manager 로 모아 set/get/clear 책임 단일화 + 외부에서는 isPending() / consume()
* 으로 안전하게 사용.
*/
export interface PendingAlignment {
userOriginalPrompt: string;
contract: RequirementContract;
/** 이번 alignment 동안 사용자가 답한 누적 라운드 수 — 무한 라운드 방지. */
roundsAsked: number;
/** 이번 turn 한정 pipeline override (Phase 4 분류기에서 전달된 추천). */
pipelineIdOverride?: string;
}
export class AlignmentSession {
private pending?: PendingAlignment;
isPending(): boolean {
return !!this.pending;
}
/** 카드 push 시 호출 — 분석기 결과 보관. */
set(state: PendingAlignment): void {
this.pending = state;
}
/** 현재 보류 상태 조회 (consume 안 함). */
get(): PendingAlignment | undefined {
return this.pending;
}
/**
* 한 번에 *읽고 비움* — 사용자가 진행 / 답변 시 호출. consume 패턴으로 race
* (사용자가 진행 누르고 곧바로 답변 메시지 보내는 경우) 의 두 번째 호출이
* 자동으로 noop 이 되어 안전.
*/
consume(): PendingAlignment | undefined {
const cur = this.pending;
this.pending = undefined;
return cur;
}
/** 취소 — 카드 / streamEnd push 는 호출자(provider) 책임. */
clear(): void {
this.pending = undefined;
}
}