chore: v2.2.73 — ASTRA-DEBUG 로그 레벨 + webview CSP font-src 보강
- ASTRA-DEBUG 정상 흐름 로그를 console.error → logInfo/console.log 로 강등 (chatHandlers, extension, slashRouter): DevTools에 ERR로 찍히던 오탐 제거 - sidebar webview에 명시적 CSP meta 추가 + font-src에 data: 허용 (sidebar.html, sidebarProvider._getHtml): VS Code outer iframe이 codicon.ttf를 data:font/ttf 로 inject하면서 기본 CSP에 막혀 매 prompt 마다 violation 경고가 찍히던 문제 해소 - 누적된 LM Studio / agent / 컨텍스트 매니저 / 테스트 갱신 동반 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -49,7 +49,9 @@ describe('contextManager.computeOutputBudget', () => {
|
||||
});
|
||||
|
||||
describe('contextManager.trimHistoryToBudget', () => {
|
||||
const marker = (n: number): BudgetMessage => ({ role: 'system', content: `[dropped ${n}]`, internal: true });
|
||||
// v2.2.69: makeMarker now also receives the dropped messages array so callers can build a real summary.
|
||||
// Tests don't need the dropped payload — just keep the signature compatible.
|
||||
const marker = (n: number, _dropped?: BudgetMessage[]): BudgetMessage => ({ role: 'system', content: `[dropped ${n}]`, internal: true });
|
||||
it('keeps everything when under budget', () => {
|
||||
const msgs: BudgetMessage[] = [{ role: 'user', content: 'hi' }, { role: 'assistant', content: 'hello' }];
|
||||
const r = trimHistoryToBudget(msgs, 10_000, marker);
|
||||
@@ -63,7 +65,25 @@ describe('contextManager.trimHistoryToBudget', () => {
|
||||
expect(r.messages[0].content).toMatch(/^\[dropped \d+\]$/);
|
||||
// most recent message survives
|
||||
expect(r.messages[r.messages.length - 1]).toEqual(msgs[msgs.length - 1]);
|
||||
expect(r.tokensAfter).toBeLessThanOrEqual(250 + estimateMessagesTokens([marker(1)]));
|
||||
expect(r.tokensAfter).toBeLessThanOrEqual(250 + estimateMessagesTokens([marker(1, [])]));
|
||||
});
|
||||
it('passes the dropped messages array to the marker factory (v2.2.69)', () => {
|
||||
const msgs: BudgetMessage[] = Array.from({ length: 6 }, (_, i) => ({
|
||||
role: i % 2 ? 'assistant' : 'user',
|
||||
content: 'x'.repeat(400),
|
||||
}));
|
||||
let observedDropped: BudgetMessage[] | undefined;
|
||||
const factory = (n: number, dropped: BudgetMessage[]): BudgetMessage => {
|
||||
observedDropped = dropped;
|
||||
return { role: 'system', content: `[summary of ${n}: first=${dropped[0]?.role}]`, internal: true };
|
||||
};
|
||||
const r = trimHistoryToBudget(msgs, 250, factory);
|
||||
expect(r.droppedCount).toBeGreaterThan(0);
|
||||
expect(observedDropped).toBeDefined();
|
||||
expect(observedDropped!.length).toBe(r.droppedCount);
|
||||
// Dropped messages are the OLDEST ones, in order.
|
||||
expect(observedDropped![0]).toEqual(msgs[0]);
|
||||
expect(r.messages[0].content).toMatch(/^\[summary of \d+: first=user\]$/);
|
||||
});
|
||||
it('always keeps at least the last message even if it alone exceeds the budget', () => {
|
||||
const msgs: BudgetMessage[] = [{ role: 'user', content: 'short' }, { role: 'user', content: 'y'.repeat(5000) }];
|
||||
|
||||
@@ -82,6 +82,10 @@ class FakeLMStudioClient implements ILMStudioClient {
|
||||
return [];
|
||||
}
|
||||
|
||||
async listDownloadedCached(): Promise<string[]> {
|
||||
return [];
|
||||
}
|
||||
|
||||
async getModelHandle(_modelKey: string): Promise<any> {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ class FakeClient implements ILMStudioClient {
|
||||
async listLoaded(): Promise<string[]> { return []; }
|
||||
async listLoadedCached(): Promise<string[]> { return []; }
|
||||
async listDownloaded(): Promise<string[]> { return []; }
|
||||
async listDownloadedCached(): Promise<string[]> { return []; }
|
||||
async isReachable(): Promise<boolean> { return true; }
|
||||
|
||||
async getModelHandle(modelKey: string): Promise<any> {
|
||||
|
||||
@@ -110,11 +110,13 @@ describe('local project path preflight', () => {
|
||||
const guidance = agent.buildLocalProjectIntentGuidance('review-evaluation');
|
||||
|
||||
expect(guidance).toContain('Intent operating contract — Code Review');
|
||||
expect(guidance).toContain('## 한 줄 판단');
|
||||
expect(guidance).toContain('## 잘된 점');
|
||||
expect(guidance).toContain('## 부족한 점');
|
||||
expect(guidance).toContain('## 사용자 관점 개선');
|
||||
expect(guidance).toContain('## 다음 한 수');
|
||||
// v2.2.64: review labels switched from markdown headers ("## 한 줄 판단") to plain-text
|
||||
// numbered labels ("1) 한 줄 판단") so the model doesn't learn to emit `##` in the answer.
|
||||
expect(guidance).toContain('1) 한 줄 판단');
|
||||
expect(guidance).toContain('2) 잘된 점');
|
||||
expect(guidance).toContain('3) 부족한 점');
|
||||
expect(guidance).toContain('4) 사용자 관점 개선');
|
||||
expect(guidance).toContain('5) 다음 한 수');
|
||||
});
|
||||
|
||||
it('adds an Astra stance layer for opinionated project collaboration', () => {
|
||||
|
||||
@@ -22,8 +22,11 @@ describe('base system prompt', () => {
|
||||
const prompt = getSystemPrompt();
|
||||
|
||||
expect(prompt).toContain('[STRICT GLOBAL RULES]');
|
||||
expect(prompt).toContain('[OUTPUT FORMAT]');
|
||||
expect(prompt).toContain('[FOLLOW-UP QUESTION RULES]');
|
||||
expect(prompt).toContain('Ask ONE focused question at the very end');
|
||||
// v2.2.68: [OUTPUT FORMAT] header now annotated with "— 7 hard rules";
|
||||
// [FOLLOW-UP QUESTION RULES] is absorbed into R6 of the 7-rule block.
|
||||
expect(prompt).toContain('[OUTPUT FORMAT — 7 hard rules]');
|
||||
expect(prompt).toContain('R1. CONCLUSION FIRST');
|
||||
expect(prompt).toContain('R6. ASK ONE QUESTION ONLY WHEN');
|
||||
expect(prompt).toContain('R7. GUESS-AND-ACT WITH STATED ASSUMPTION');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user