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>
This commit is contained in:
g1nation
2026-05-25 09:59:32 +09:00
co-authored by Claude Opus 4.7
parent 4153f640c2
commit 0a97324f1b
149 changed files with 14628 additions and 6927 deletions
+40 -1
View File
@@ -427,10 +427,21 @@ function _pauseManagedIntervals(){
function _resumeManagedIntervals(){
for(const rec of _managedIntervals){ if(rec.id===null){ rec.id=setInterval(rec.fn,rec.ms); } }
}
// 명시적 cleanup — webview iframe 이 destroy 되면 JS 컨텍스트 통째로 GC 되니
// 현실적 누수는 없지만, 미래에 같은 iframe 안에서 runtime 을 reinit 하는 코드
// 추가될 수 있으므로 *명시적* dispose 경로를 둔다. window 에 노출해서 panel
// 호스트 (sidebarProvider.openPixelOfficePanel) 측에서 명시 호출 가능.
function _disposeAllManagedIntervals(){
for(const rec of _managedIntervals){ if(rec.id!==null) clearInterval(rec.id); }
_managedIntervals.length=0;
}
(window).__astraOfficeDisposeIntervals=_disposeAllManagedIntervals;
document.addEventListener('visibilitychange',()=>{
if(document.hidden) _pauseManagedIntervals();
else _resumeManagedIntervals();
});
// pagehide / beforeunload — webview navigation 또는 panel dispose 시 fallback.
window.addEventListener('pagehide',_disposeAllManagedIntervals,{once:true});
_managedInterval(()=>{Object.keys(chars).forEach(k=>{const a=anim[k];if(a.mode==='walk'){a.frame=(a.frame+1)%5;setSprite(k,'walk',a.frame,a.dir)}else if(a.mode==='work'){a.frame=(a.frame+1)%4;setSprite(k,'work',a.frame)} });},286)
_managedInterval(()=>{Object.keys(chars).forEach(k=>{const a=anim[k];if(a.mode==='sit'){a.frame=(a.frame+1)%2;setSprite(k,'sit',a.frame)} });},700)
// ── 책상 회피 path planner ──
@@ -935,6 +946,12 @@ function routeBubble(b){
const role = roleMap[b?.agentId] || 'ceo';
bubble(role, b?.text || '');
}
// ⚠️ 새 phase 추가 시 다음 *세* 곳을 모두 갱신:
// 1. STATUS_COPY (이 객체) — label / note / tone
// 2. BANTER_SCRIPTS (위) — banter 시퀀스 (없으면 banter 없는 정적 phase)
// 3. PHASE_META.allPhases() 호출처 — 신규 phase 등장 시 fallback 검증
// 데이터 통합 (한 객체로 합치기) 은 BANTER 객체가 매우 커서 다음 라운드로. 지금은
// 통합된 *접근 API* (getPhaseMeta) 만 제공해 호출자가 두 객체를 직접 안 봐도 되게.
const STATUS_COPY = {
idle: { label:'대기 중', note:'새로운 작업 요청을 기다리고 있습니다.', tone:'neutral' },
intake: { label:'요청 수신', note:'요청을 읽고 작업 범위를 정리하고 있습니다.', tone:'neutral' },
@@ -948,8 +965,30 @@ const STATUS_COPY = {
error: { label:'주의 필요', note:'흐름을 멈춘 이슈를 확인해야 합니다.', tone:'danger' },
done: { label:'완료', note:'이번 작업 라운드가 정리되었습니다.', tone:'success' },
};
/**
* 통합된 phase 메타 접근 API — STATUS_COPY 와 BANTER_SCRIPTS 두 객체를 *한 entry
* point* 로 노출. 호출자는 이 함수만 알면 됨.
*
* const meta = getPhaseMeta('executing');
* meta.label // '실행 중'
* meta.tone // 'neutral'
* meta.banter // 시퀀스 array 또는 null (banter 없는 정적 phase)
*
* 모르는 phase 면 idle fallback.
*/
function getPhaseMeta(phase){
const copy = STATUS_COPY[phase] || STATUS_COPY.idle;
return {
label: copy.label,
note: copy.note,
tone: copy.tone,
banter: BANTER_SCRIPTS[phase] || null,
};
}
function _statusMeta(status){
return STATUS_COPY[status] || STATUS_COPY.idle;
// 옛 _statusMeta 호출처 호환 — banter 까지 같이 반환해도 무해. 호출자가 label/note/tone 만 쓰면 banter 는 무시됨.
return getPhaseMeta(status);
}
function _pct(v){
return Math.max(0, Math.min(100, Math.round((typeof v === 'number' ? v : 0) * 100)));