Astra v2.2.52

- 채팅 기록 목록 누락 수정: 후처리 예외로 _saveCurrentSession 이 건너뛰던 회귀를
  try/finally 로 보장, _saveCurrentSession 자체도 throw 방지. 1인 기업 모드 업무
  턴(_runCompanyTurn)도 요청/보고서 쌍으로 기록 (_saveCompanyTurnSession).
- Self-Reflector 실행 검증 크로스플랫폼화: .py 는 python3 자동 탐지, .ts 는 로컬
  node_modules/typescript/bin/tsc 직접 호출.
- 버전 2.2.52 상향 + package-lock 동기화 + 재패키징.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
g1nation
2026-05-20 23:48:39 +09:00
parent eeb527c242
commit dea5953f59
11 changed files with 173 additions and 33 deletions
+73 -4
View File
@@ -931,6 +931,17 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
}
async _saveCurrentSession() {
try {
await this._saveCurrentSessionInner();
} catch (err: any) {
// Never let a persistence failure escape — callers run this from a
// `finally` block, so a throw here would mask the original error
// and (worse) is itself the thing that drops chats from 기록.
logError('Failed to save current chat session.', { error: err?.message ?? String(err) });
}
}
private async _saveCurrentSessionInner() {
const history = this._agent.getHistory();
if (history.length === 0) return;
@@ -2358,9 +2369,14 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
type: 'companyIntentDecision',
value: { intent, reason, label },
});
await this._handlePrompt(originalData);
await this._autoWriteChronicleAfterPrompt();
await this._saveCurrentSession();
try {
await this._handlePrompt(originalData);
await this._autoWriteChronicleAfterPrompt();
} finally {
// Same guarantee as the normal chat path — a throw after the
// answer streamed must not skip the 기록 save.
await this._saveCurrentSession();
}
}
/**
@@ -2561,12 +2577,18 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
// 잡아낸다 — turn이 중간 abort되면 plan만 남고 reportTail은 비어
// 있게 되는데, 그 상태로도 followup 매칭에는 충분히 도움된다.
let stagingBrief = '';
// Full final report of this turn — captured so the company turn can be
// recorded in the 기록(Chat History) list, same as a normal chat. The
// dispatcher runs outside AgentExecutor, so `_agent` history stays
// empty and `_saveCurrentSession()` would save nothing for it.
let finalReport = '';
const emit = (event: CompanyTurnEvent) => {
this._view?.webview.postMessage({ type: 'companyTurnUpdate', value: event });
if (event.phase === 'plan-ready') {
stagingBrief = event.plan?.brief || '';
} else if (event.phase === 'report-done') {
const tail = (event.report || '').trim().slice(-600);
finalReport = (event.report || '').trim();
const tail = finalReport.slice(-600);
this._lastCompanyTurnSummary = {
brief: stagingBrief,
reportTail: tail,
@@ -2654,6 +2676,53 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
// turn이 끝났으면(완료든 abort든) resume 가능 세션 목록을 새로 푸시 —
// 방금 abort된 세션이 곧장 목록에 떠야 하므로.
void this._sendCompanyResumable();
// 완료된 회사 turn을 채팅 기록(기록 목록)에도 한 줄로 남긴다 — 보고서가
// 나온 경우(report-done)에만. abort돼서 보고서가 없으면 기록 목록은
// 건드리지 않고 resume 목록에만 남는다.
if (finalReport) {
void this._saveCompanyTurnSession(userPrompt, finalReport);
}
}
}
/**
* Records a completed 1인 기업 turn as a standalone entry in the 기록(Chat
* History) list. Company turns run through the dispatcher, not AgentExecutor,
* so they never populate `_agent` history and `_saveCurrentSession()` can't
* see them — without this they only ever appeared in the resume list.
*
* Each turn gets its own entry (it doesn't touch `_currentSessionId`): a
* dispatch is a discrete unit of work, not a back-and-forth chat thread.
*/
async _saveCompanyTurnSession(userPrompt: string, report: string) {
try {
const prompt = (userPrompt || '').trim();
const body = (report || '').trim();
if (!prompt && !body) return;
const history: ChatMessage[] = [
{ role: 'user', content: prompt || '(요청 내용 없음)' },
{ role: 'assistant', content: body || '(보고서가 생성되지 않았습니다.)' },
];
const title = prompt
? prompt.substring(0, 50).replace(/\n/g, ' ') + (prompt.length > 50 ? '...' : '')
: '1인 기업 업무';
const brainProfileId = this._currentSessionBrainId || getActiveBrainProfile().id;
let sessions = this._getSessions();
sessions.unshift({
id: Date.now().toString(),
title,
timestamp: Date.now(),
history,
brainProfileId,
negativePrompt: this._currentNegativePrompt,
});
if (sessions.length > 50) sessions = sessions.slice(0, 50);
await this._putSessions(sessions);
await this._sendSessionList();
} catch (err: any) {
logError('Failed to record company turn into chat history.', { error: err?.message ?? String(err) });
}
}