This commit is contained in:
2026-05-06 11:46:38 +09:00
parent f20388e2d5
commit 00f62bdc34
12 changed files with 353 additions and 50 deletions
@@ -1,5 +1,5 @@
{ {
"result": "Final report with inconsistencies. This should be long enough to pass validation.", "result": "Final report with inconsistencies. This should be long enough to pass validation.",
"createdAt": 1777985740400, "createdAt": 1778033752470,
"modelVersion": "unknown" "modelVersion": "unknown"
} }
@@ -1,5 +1,5 @@
{ {
"result": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.", "result": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.",
"createdAt": 1777985740393, "createdAt": 1778033752468,
"modelVersion": "unknown" "modelVersion": "unknown"
} }
@@ -1,5 +1,5 @@
{ {
"result": "Detailed Execution Plan: 1. Research 2. Analyze 3. Write report with high quality.", "result": "Detailed Execution Plan: 1. Research 2. Analyze 3. Write report with high quality.",
"createdAt": 1777985740387, "createdAt": 1778033752466,
"modelVersion": "unknown" "modelVersion": "unknown"
} }
@@ -1,5 +1,5 @@
{ {
"result": "---\nid: stress_conflict_1777985740371\ndate: 2026-05-05T12:55:40.404Z\ntype: knowledge_artifact\nstandard: P-Reinforce v3.0\ntags: [automated, connect_ai, brain_sync]\n---\n\n## 📌 Brief Summary\nFinal report with inconsistencies. This should be long enough to pass validation.\n\nFinal report with inconsistencies. This should be long enough to pass validation.\n\n---\n## 💡 Astra의 선제적 제안 (Proactive Next Actions)\nFinal report with inconsistencies. This should be long enough to pass validation.\n---\n## 🛡️ Reliability & Audit Summary\n> [!NOTE]\n> 이 문서는 ConnectAI의 **Intelligent Resilience** 엔진에 의해 검증 및 정제되었습니다.\n\n| Metric | Value | Status |\n| :--- | :--- | :--- |\n| **Conflict Risk** | `60/100` | ⚠️ Medium |\n| **Fallbacks Used** | `0` | ✅ None |\n| **Auto Retries** | `0` | ✅ Stable |\n| **Deduplication** | `0` | Standard |\n| **Processing Time** | `0.0s` | ✅ Fast |\n\n### 🔍 Decision Audit Trail\n- **[PLANNER]** 전략 수립 중... (12ms)\n- **[RESEARCHER]** 핵심 정보 수집 및 분석 중... (4ms)\n- **[WRITER]** 최종 리포트 작성 및 편집 중... (9ms)\n", "result": "---\nid: stress_conflict_1778033752447\ndate: 2026-05-06T02:15:52.471Z\ntype: knowledge_artifact\nstandard: P-Reinforce v3.0\ntags: [automated, connect_ai, brain_sync]\n---\n\n## 📌 Brief Summary\nFinal report with inconsistencies. This should be long enough to pass validation.\n\nFinal report with inconsistencies. This should be long enough to pass validation.\n\n---\n## 💡 Astra의 선제적 제안 (Proactive Next Actions)\nFinal report with inconsistencies. This should be long enough to pass validation.\n---\n## 🛡️ Reliability & Audit Summary\n> [!NOTE]\n> 이 문서는 ConnectAI의 **Intelligent Resilience** 엔진에 의해 검증 및 정제되었습니다.\n\n| Metric | Value | Status |\n| :--- | :--- | :--- |\n| **Conflict Risk** | `60/100` | ⚠️ Medium |\n| **Fallbacks Used** | `0` | ✅ None |\n| **Auto Retries** | `0` | ✅ Stable |\n| **Deduplication** | `0` | Standard |\n| **Processing Time** | `0.0s` | ✅ Fast |\n\n### 🔍 Decision Audit Trail\n- **[PLANNER]** 전략 수립 중... (18ms)\n- **[RESEARCHER]** 핵심 정보 수집 및 분석 중... (2ms)\n- **[WRITER]** 최종 리포트 작성 및 편집 중... (2ms)\n",
"createdAt": 1777985740404, "createdAt": 1778033752471,
"modelVersion": "unknown" "modelVersion": "unknown"
} }
@@ -1,8 +1,8 @@
{ {
"missionId": "stress_conflict_1777985740371", "missionId": "stress_conflict_1778033752447",
"status": "completed", "status": "completed",
"startTime": "2026-05-05T12:55:40.371Z", "startTime": "2026-05-06T02:15:52.447Z",
"totalElapsedMs": 33, "totalElapsedMs": 25,
"results": { "results": {
"planner": "Detailed Execution Plan: 1. Research 2. Analyze 3. Write report with high quality.", "planner": "Detailed Execution Plan: 1. Research 2. Analyze 3. Write report with high quality.",
"researcher": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.", "researcher": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.",
@@ -16,30 +16,30 @@
{ {
"from": "idle", "from": "idle",
"to": "planner", "to": "planner",
"durationMs": 12, "durationMs": 18,
"message": "전략 수립 중...", "message": "전략 수립 중...",
"ts": "2026-05-05T12:55:40.383Z" "ts": "2026-05-06T02:15:52.465Z"
}, },
{ {
"from": "planner", "from": "planner",
"to": "researcher", "to": "researcher",
"durationMs": 4, "durationMs": 2,
"message": "핵심 정보 수집 및 분석 중...", "message": "핵심 정보 수집 및 분석 중...",
"ts": "2026-05-05T12:55:40.387Z" "ts": "2026-05-06T02:15:52.467Z"
}, },
{ {
"from": "researcher", "from": "researcher",
"to": "writer", "to": "writer",
"durationMs": 9, "durationMs": 2,
"message": "최종 리포트 작성 및 편집 중...", "message": "최종 리포트 작성 및 편집 중...",
"ts": "2026-05-05T12:55:40.396Z" "ts": "2026-05-06T02:15:52.469Z"
}, },
{ {
"from": "writer", "from": "writer",
"to": "completed", "to": "completed",
"durationMs": 8, "durationMs": 3,
"message": "미션 완료", "message": "미션 완료",
"ts": "2026-05-05T12:55:40.404Z" "ts": "2026-05-06T02:15:52.472Z"
} }
], ],
"resilienceMetrics": { "resilienceMetrics": {
+224 -3
View File
@@ -1,15 +1,16 @@
{ {
"name": "g1nation", "name": "astra",
"version": "2.78.0", "version": "2.78.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "g1nation", "name": "astra",
"version": "2.78.0", "version": "2.78.0",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"marked": "^18.0.2" "marked": "^18.0.2",
"pdf-parse": "^2.4.5"
}, },
"devDependencies": { "devDependencies": {
"@types/jest": "^29.5.14", "@types/jest": "^29.5.14",
@@ -57,6 +58,7 @@
"integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@babel/code-frame": "^7.29.0", "@babel/code-frame": "^7.29.0",
"@babel/generator": "^7.29.0", "@babel/generator": "^7.29.0",
@@ -1333,6 +1335,190 @@
"@jridgewell/sourcemap-codec": "^1.4.14" "@jridgewell/sourcemap-codec": "^1.4.14"
} }
}, },
"node_modules/@napi-rs/canvas": {
"version": "0.1.80",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.80.tgz",
"integrity": "sha512-DxuT1ClnIPts1kQx8FBmkk4BQDTfI5kIzywAaMjQSXfNnra5UFU9PwurXrl+Je3bJ6BGsp/zmshVVFbCmyI+ww==",
"license": "MIT",
"workspaces": [
"e2e/*"
],
"engines": {
"node": ">= 10"
},
"optionalDependencies": {
"@napi-rs/canvas-android-arm64": "0.1.80",
"@napi-rs/canvas-darwin-arm64": "0.1.80",
"@napi-rs/canvas-darwin-x64": "0.1.80",
"@napi-rs/canvas-linux-arm-gnueabihf": "0.1.80",
"@napi-rs/canvas-linux-arm64-gnu": "0.1.80",
"@napi-rs/canvas-linux-arm64-musl": "0.1.80",
"@napi-rs/canvas-linux-riscv64-gnu": "0.1.80",
"@napi-rs/canvas-linux-x64-gnu": "0.1.80",
"@napi-rs/canvas-linux-x64-musl": "0.1.80",
"@napi-rs/canvas-win32-x64-msvc": "0.1.80"
}
},
"node_modules/@napi-rs/canvas-android-arm64": {
"version": "0.1.80",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.80.tgz",
"integrity": "sha512-sk7xhN/MoXeuExlggf91pNziBxLPVUqF2CAVnB57KLG/pz7+U5TKG8eXdc3pm0d7Od0WreB6ZKLj37sX9muGOQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"android"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-darwin-arm64": {
"version": "0.1.80",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.80.tgz",
"integrity": "sha512-O64APRTXRUiAz0P8gErkfEr3lipLJgM6pjATwavZ22ebhjYl/SUbpgM0xcWPQBNMP1n29afAC/Us5PX1vg+JNQ==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-darwin-x64": {
"version": "0.1.80",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.80.tgz",
"integrity": "sha512-FqqSU7qFce0Cp3pwnTjVkKjjOtxMqRe6lmINxpIZYaZNnVI0H5FtsaraZJ36SiTHNjZlUB69/HhxNDT1Aaa9vA==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-linux-arm-gnueabihf": {
"version": "0.1.80",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.80.tgz",
"integrity": "sha512-eyWz0ddBDQc7/JbAtY4OtZ5SpK8tR4JsCYEZjCE3dI8pqoWUC8oMwYSBGCYfsx2w47cQgQCgMVRVTFiiO38hHQ==",
"cpu": [
"arm"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-linux-arm64-gnu": {
"version": "0.1.80",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.80.tgz",
"integrity": "sha512-qwA63t8A86bnxhuA/GwOkK3jvb+XTQaTiVML0vAWoHyoZYTjNs7BzoOONDgTnNtr8/yHrq64XXzUoLqDzU+Uuw==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-linux-arm64-musl": {
"version": "0.1.80",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.80.tgz",
"integrity": "sha512-1XbCOz/ymhj24lFaIXtWnwv/6eFHXDrjP0jYkc6iHQ9q8oXKzUX1Lc6bu+wuGiLhGh2GS/2JlfORC5ZcXimRcg==",
"cpu": [
"arm64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-linux-riscv64-gnu": {
"version": "0.1.80",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.80.tgz",
"integrity": "sha512-XTzR125w5ZMs0lJcxRlS1K3P5RaZ9RmUsPtd1uGt+EfDyYMu4c6SEROYsxyatbbu/2+lPe7MPHOO/0a0x7L/gw==",
"cpu": [
"riscv64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-linux-x64-gnu": {
"version": "0.1.80",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.80.tgz",
"integrity": "sha512-BeXAmhKg1kX3UCrJsYbdQd3hIMDH/K6HnP/pG2LuITaXhXBiNdh//TVVVVCBbJzVQaV5gK/4ZOCMrQW9mvuTqA==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-linux-x64-musl": {
"version": "0.1.80",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.80.tgz",
"integrity": "sha512-x0XvZWdHbkgdgucJsRxprX/4o4sEed7qo9rCQA9ugiS9qE2QvP0RIiEugtZhfLH3cyI+jIRFJHV4Fuz+1BHHMg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@napi-rs/canvas-win32-x64-msvc": {
"version": "0.1.80",
"resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.80.tgz",
"integrity": "sha512-Z8jPsM6df5V8B1HrCHB05+bDiCxjE9QA//3YrkKIdVDEwn5RKaqOxCJDRJkl48cJbylcrJbW4HxZbTte8juuPg==",
"cpu": [
"x64"
],
"license": "MIT",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">= 10"
}
},
"node_modules/@sinclair/typebox": { "node_modules/@sinclair/typebox": {
"version": "0.27.10", "version": "0.27.10",
"resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz",
@@ -1757,6 +1943,7 @@
} }
], ],
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"baseline-browser-mapping": "^2.10.12", "baseline-browser-mapping": "^2.10.12",
"caniuse-lite": "^1.0.30001782", "caniuse-lite": "^1.0.30001782",
@@ -2654,6 +2841,7 @@
"integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"peer": true,
"dependencies": { "dependencies": {
"@jest/core": "^29.7.0", "@jest/core": "^29.7.0",
"@jest/types": "^29.6.3", "@jest/types": "^29.6.3",
@@ -3662,6 +3850,38 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/pdf-parse": {
"version": "2.4.5",
"resolved": "https://registry.npmjs.org/pdf-parse/-/pdf-parse-2.4.5.tgz",
"integrity": "sha512-mHU89HGh7v+4u2ubfnevJ03lmPgQ5WU4CxAVmTSh/sxVTEDYd1er/dKS/A6vg77NX47KTEoihq8jZBLr8Cxuwg==",
"license": "Apache-2.0",
"dependencies": {
"@napi-rs/canvas": "0.1.80",
"pdfjs-dist": "5.4.296"
},
"bin": {
"pdf-parse": "bin/cli.mjs"
},
"engines": {
"node": ">=20.16.0 <21 || >=22.3.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/mehmet-kozan"
}
},
"node_modules/pdfjs-dist": {
"version": "5.4.296",
"resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.4.296.tgz",
"integrity": "sha512-DlOzet0HO7OEnmUmB6wWGJrrdvbyJKftI1bhMitK7O2N8W2gc757yyYBbINy9IDafXAV9wmKr9t7xsTaNKRG5Q==",
"license": "Apache-2.0",
"engines": {
"node": ">=20.16.0 || >=22.3.0"
},
"optionalDependencies": {
"@napi-rs/canvas": "^0.1.80"
}
},
"node_modules/picocolors": { "node_modules/picocolors": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -4178,6 +4398,7 @@
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true, "dev": true,
"license": "Apache-2.0", "license": "Apache-2.0",
"peer": true,
"bin": { "bin": {
"tsc": "bin/tsc", "tsc": "bin/tsc",
"tsserver": "bin/tsserver" "tsserver": "bin/tsserver"
+3 -2
View File
@@ -2,7 +2,7 @@
"name": "astra", "name": "astra",
"displayName": "Astra", "displayName": "Astra",
"description": "The personal intelligence layer for Antigravity and VS Code. A private cognitive partner for deep project context, memory, and proactive strategic decision-making.", "description": "The personal intelligence layer for Antigravity and VS Code. A private cognitive partner for deep project context, memory, and proactive strategic decision-making.",
"version": "2.78.0", "version": "2.79.0",
"publisher": "g1nation", "publisher": "g1nation",
"license": "MIT", "license": "MIT",
"icon": "assets/icon.png", "icon": "assets/icon.png",
@@ -226,6 +226,7 @@
"typescript": "^5.1.3" "typescript": "^5.1.3"
}, },
"dependencies": { "dependencies": {
"marked": "^18.0.2" "marked": "^18.0.2",
"pdf-parse": "^2.4.5"
} }
} }
+2 -3
View File
@@ -333,9 +333,8 @@ export class AgentExecutor {
} }
} }
// 3. API Request Setup // 3. API Request Setup (라인 229에서 이미 추출한 ollamaUrl, configDefaultModel 재사용)
const { ollamaUrl, defaultModel: configDefaultModel, timeout } = getConfig(); const actualModel = (modelName && modelName.trim()) || configDefaultModel;
const actualModel = modelName || configDefaultModel;
const reqMessages = this.buildRequestHistory(this.chatHistory); const reqMessages = this.buildRequestHistory(this.chatHistory);
// Handle Vision Content Injection // Handle Vision Content Injection
+94 -15
View File
@@ -74,6 +74,16 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
localResourceRoots: [this._extensionUri] localResourceRoots: [this._extensionUri]
}; };
// [State Persistence Fix] 사이드바가 다시 보여질 때 세팅값 자동 복원
webviewView.onDidChangeVisibility(() => {
if (webviewView.visible) {
logInfo('Sidebar became visible, restoring state...');
void this._sendModels();
void this._sendBrainProfiles();
void this._sendAgentsList();
}
});
webviewView.webview.html = this._getHtml(webviewView.webview); webviewView.webview.html = this._getHtml(webviewView.webview);
this._agent.setWebview(webviewView.webview); this._agent.setWebview(webviewView.webview);
@@ -204,6 +214,10 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
case 'deleteAgent': case 'deleteAgent':
await this._deleteAgent(data.path); await this._deleteAgent(data.path);
break; break;
case 'saveAgentSelection':
await this._context.globalState.update(SidebarChatProvider.lastAgentStateKey, data.path || 'none');
logInfo(`Agent selection saved: ${data.path}`);
break;
case 'refreshModels': case 'refreshModels':
await this._sendModels(); await this._sendModels();
break; break;
@@ -1824,10 +1838,75 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
const designerContext = designerGuard !== false ? this._buildDesignerGuardContext() : undefined; const designerContext = designerGuard !== false ? this._buildDesignerGuardContext() : undefined;
// [File Processing v2] 파일 타입별 분류 처리
let processedPrompt = value || '';
let imageFiles: any[] | undefined = undefined;
if (files && Array.isArray(files) && files.length > 0) {
const textContents: string[] = [];
const images: any[] = [];
for (const file of files) {
const name = file.name?.toLowerCase() || '';
const type = file.type || '';
if (name.endsWith('.pdf') || type === 'application/pdf') {
// PDF: 서버사이드 텍스트 추출 (pdf-parse v2 API)
try {
const { PDFParse } = require('pdf-parse');
const rawBuffer = Buffer.from(file.data, 'base64');
const uint8 = new Uint8Array(rawBuffer.buffer, rawBuffer.byteOffset, rawBuffer.byteLength);
const parser = new PDFParse(uint8);
await parser.load();
const textResult = await parser.getText();
// pdf-parse v2: getText() returns {pages: [{text, num}], text: string, total: number}
const extracted = (typeof textResult === 'string' ? textResult : (textResult?.text || '')).trim();
// 페이지 구분 마커 제거하여 깔끔한 텍스트 추출
const cleanText = extracted.replace(/\n*-- \d+ of \d+ --\n*/g, '\n').trim();
if (cleanText && cleanText.length > 10) {
textContents.push(`\n[PDF: ${file.name}]\n${cleanText}`);
logInfo(`PDF text extracted successfully.`, { fileName: file.name, chars: cleanText.length });
} else {
textContents.push(`\n[PDF: ${file.name}]\n(텍스트 추출 결과 없음 - 이미지 기반 PDF일 수 있습니다. 텍스트 레이어가 없는 스캔 문서는 OCR 변환 후 재시도하세요.)`);
logInfo(`PDF text extraction returned empty/minimal result.`, { fileName: file.name, rawLength: extracted.length });
}
} catch (pdfError: any) {
logError(`PDF parsing failed.`, { fileName: file.name, error: pdfError?.message || String(pdfError) });
textContents.push(`\n[PDF: ${file.name}]\n(PDF 파싱 오류: ${pdfError?.message || '알 수 없는 오류'})`);
}
} else if (
type.startsWith('text/') ||
type === 'application/json' ||
/\.(txt|md|csv|json|js|ts|jsx|tsx|html|css|py|java|rs|go|yaml|yml|xml|toml|sql|sh)$/i.test(name)
) {
// 텍스트 파일: base64 디코딩
try {
const decoded = Buffer.from(file.data, 'base64').toString('utf-8');
textContents.push(`\n[FILE: ${file.name}]\n\`\`\`\n${decoded}\n\`\`\``);
} catch (decodeError: any) {
logError(`Text file decode failed.`, { fileName: file.name, error: decodeError?.message });
textContents.push(`\n[FILE: ${file.name}]\n(디코딩 오류)`);
}
} else if (type.startsWith('image/')) {
// 이미지: 기존 vision 방식 유지
images.push(file);
} else {
// 미지원 타입: 파일명만 기록
textContents.push(`\n[ATTACHMENT: ${file.name}]\n(지원하지 않는 파일 형식: ${type || 'unknown'})`);
}
}
// 추출된 텍스트를 프롬프트에 주입
if (textContents.length > 0) {
processedPrompt = `${processedPrompt}\n\n--- 첨부 파일 내용 ---${textContents.join('\n')}`;
}
imageFiles = images.length > 0 ? images : undefined;
}
try { try {
await this._agent.handlePrompt(value, model, { await this._agent.handlePrompt(processedPrompt, model, {
internetEnabled: internet, internetEnabled: internet,
visionContent: files, visionContent: imageFiles,
agentSkillContext, agentSkillContext,
negativePrompt, negativePrompt,
designerContext, designerContext,
@@ -1894,19 +1973,15 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
await vscode.workspace.getConfiguration('g1nation').update('defaultModel', defaultModel, vscode.ConfigurationTarget.Global); await vscode.workspace.getConfiguration('g1nation').update('defaultModel', defaultModel, vscode.ConfigurationTarget.Global);
} }
if (models.length > 0 && (!defaultModel || !models.includes(defaultModel))) { if (models.length > 0 && !defaultModel) {
// [State Persistence Fix] 저장된 모델이 목록에 없을 때: // [State Persistence Fix v2] defaultModel이 완전히 비어있을 때만 첫 번째 모델로 설정
// 즉시 강제 리셋하는 대신, 현재 모델 목록의 첫 번째를 '폴백 후보'로만 사용. defaultModel = models[0];
// 단, defaultModel이 완전히 없는 경우에만 실제로 저장함. await vscode.workspace.getConfiguration('g1nation').update('defaultModel', defaultModel, vscode.ConfigurationTarget.Global);
if (!defaultModel) { } else if (models.length > 0 && defaultModel && !models.includes(defaultModel)) {
defaultModel = models[0]; // [State Persistence Fix v2] 저장된 모델이 로컬 엔진 목록에 없는 경우:
await vscode.workspace.getConfiguration('g1nation').update('defaultModel', defaultModel, vscode.ConfigurationTarget.Global); // 강제 리셋하지 않고, 저장된 모델을 목록 선두에 추가하여 사용자 선택을 보존
} else { logInfo('Saved model not in local engine list. Preserving user selection.', { saved: defaultModel, localModels: models.slice(0, 3) });
// 저장된 모델명은 유지하고, UI에는 첫 번째 모델을 보여주되 models.unshift(defaultModel);
// 설정은 건드리지 않아 다음 번에 같은 모델이 다시 로드될 경우 복원 가능하도록 함
logInfo('Saved model not found in current list, using first available as fallback.', { saved: defaultModel, fallback: models[0] });
defaultModel = models[0];
}
} }
const defaultIdx = models.indexOf(defaultModel); const defaultIdx = models.indexOf(defaultModel);
@@ -3309,6 +3384,8 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
agentSel.onchange = () => { agentSel.onchange = () => {
if (agentSel.value !== 'none') { if (agentSel.value !== 'none') {
vscode.postMessage({ type: 'getAgentContent', path: agentSel.value }); vscode.postMessage({ type: 'getAgentContent', path: agentSel.value });
// [State Persistence Fix] 에이전트 선택값을 즉시 백엔드에 저장
vscode.postMessage({ type: 'saveAgentSelection', path: agentSel.value });
if (editMode) agentConfigPanel.style.display = 'flex'; if (editMode) agentConfigPanel.style.display = 'flex';
} else { } else {
agentConfigPanel.style.display = 'none'; agentConfigPanel.style.display = 'none';
@@ -3316,6 +3393,8 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
editAgentBtn.classList.remove('active'); editAgentBtn.classList.remove('active');
agentPrompt.value = ''; agentPrompt.value = '';
negativePrompt.value = ''; negativePrompt.value = '';
// [State Persistence Fix] 에이전트 해제도 즉시 저장
vscode.postMessage({ type: 'saveAgentSelection', path: 'none' });
} }
}; };
+6 -3
View File
@@ -154,7 +154,7 @@ Never say "upload the source code", "provide the files", or "파일 내용을
If access fails after trying, explain the failure and only then ask for an upload. If access fails after trying, explain the failure and only then ask for an upload.
[STRICT GLOBAL RULES] [STRICT GLOBAL RULES]
1. [NO EMOJIS] Never use emojis, icons, or pictorial symbols. Professional text-only output. Exception: the 💡 icon in the suggestion header only. 1. [NO EMOJIS - ABSOLUTE RULE] NEVER use ANY emojis, emoticons, Unicode pictorial symbols (including but not limited to emoji, kaomoji, Unicode icons), or decorative symbols anywhere in your response. NO EXCEPTIONS. Use plain text dashes (-) or asterisks (*) for bullets. Use plain markdown ## for headers. This rule overrides ALL other formatting instructions.
2. [UNIQUE HEADINGS] Every markdown heading must be unique and appear exactly once. 2. [UNIQUE HEADINGS] Every markdown heading must be unique and appear exactly once.
3. [NO INTERNAL LOGS] Never output <details>, "2nd Brain Trace", or "Debug JSON" blocks. 3. [NO INTERNAL LOGS] Never output <details>, "2nd Brain Trace", or "Debug JSON" blocks.
4. [NO SECTION LEAKAGE] Never output sections named "요청 요약", "사용자 의도 추론", "프로젝트 기록 대상 확인", "핵심 확인 질문", or "근거 파일 경로". 4. [NO SECTION LEAKAGE] Never output sections named "요청 요약", "사용자 의도 추론", "프로젝트 기록 대상 확인", "핵심 확인 질문", or "근거 파일 경로".
@@ -170,7 +170,7 @@ For conversational replies, quick facts, or simple updates — answer directly w
- Root cause of the problem. - Root cause of the problem.
- Concrete step-by-step instructions: what to change, which files to edit, which commands to run. - Concrete step-by-step instructions: what to change, which files to edit, which commands to run.
## 💡 제안 ← Optional. Only include if a meaningfully better alternative exists. Omit otherwise. ## 제안 ← Optional. Only include if a meaningfully better alternative exists. Omit otherwise.
[FOLLOW-UP QUESTION RULES] [FOLLOW-UP QUESTION RULES]
A follow-up question is a precision tool, not a ritual. A follow-up question is a precision tool, not a ritual.
@@ -228,7 +228,10 @@ If neither condition is met, give a definitive answer and stop.
3. When the user says "분석해줘", "봐줘", "확인해줘", "리뷰해줘" with a path — that IS the confirmation. Access the path immediately and run the full analysis in one continuous response. Do not pause to ask "진행할까요?" or "시작할까요?".`; 3. When the user says "분석해줘", "봐줘", "확인해줘", "리뷰해줘" with a path — that IS the confirmation. Access the path immediately and run the full analysis in one continuous response. Do not pause to ask "진행할까요?" or "시작할까요?".`;
export function getSystemPrompt(): string { export function getSystemPrompt(): string {
return BASE_SYSTEM_PROMPT; const now = new Date();
const dateTimeStr = now.toLocaleString('ko-KR', { timeZone: 'Asia/Seoul', year: 'numeric', month: '2-digit', day: '2-digit', weekday: 'long', hour: '2-digit', minute: '2-digit' });
const isoDate = now.toISOString().split('T')[0];
return `${BASE_SYSTEM_PROMPT}\n\n[CURRENT DATE/TIME]\nToday: ${isoDate} (${dateTimeStr})\nUse this date as the absolute reference for any date-related calculations (e.g., "this week", "today", "yesterday").`;
} }
export const SYSTEM_PROMPT = BASE_SYSTEM_PROMPT; export const SYSTEM_PROMPT = BASE_SYSTEM_PROMPT;
+1 -1
View File
@@ -40,7 +40,7 @@ describe('local project path preflight', () => {
expect(inspected).toContain('Access: succeeded'); expect(inspected).toContain('Access: succeeded');
expect(inspected).toContain('Priority file previews'); expect(inspected).toContain('Priority file previews');
expect(inspected).toContain('package.json'); expect(inspected).toContain('package.json');
expect(inspected).toContain('src/features/game/systems/CombatSystem.ts'); expect(inspected).toContain(path.join('src', 'features', 'game', 'systems', 'CombatSystem.ts'));
expect(inspected).toContain('export class CombatSystem'); expect(inspected).toContain('export class CombatSystem');
}); });
+7 -7
View File
@@ -155,11 +155,11 @@ describe('ProjectChronicleManager', () => {
expect(fs.existsSync(result.filePath)).toBe(true); expect(fs.existsSync(result.filePath)).toBe(true);
} }
expect(discussion.relativePath).toContain('discussions/'); expect(discussion.relativePath).toContain('discussions' + path.sep);
expect(decision.relativePath).toContain('decisions/'); expect(decision.relativePath).toContain('decisions' + path.sep);
expect(development.relativePath).toContain('development/'); expect(development.relativePath).toContain('development' + path.sep);
expect(bug.relativePath).toContain('bugs/'); expect(bug.relativePath).toContain('bugs' + path.sep);
expect(retrospective.relativePath).toContain('retrospectives/'); expect(retrospective.relativePath).toContain('retrospectives' + path.sep);
}); });
it('lists chronicle records across sections with relative paths', () => { it('lists chronicle records across sections with relative paths', () => {
@@ -191,8 +191,8 @@ describe('ProjectChronicleManager', () => {
expect(records.length).toBe(2); expect(records.length).toBe(2);
expect(records.map(record => record.relativePath)).toEqual( expect(records.map(record => record.relativePath)).toEqual(
expect.arrayContaining([ expect.arrayContaining([
'planning/2026-05-02_record-list.md', path.join('planning', '2026-05-02_record-list.md'),
'bugs/BUG-0001-list-bug.md' path.join('bugs', 'BUG-0001-list-bug.md')
]) ])
); );
}); });