feat: v2.2.64 — LM Studio 모델 발견/에러 표시 + macOS 셸 호환성
- LM Studio 모델 dropdown을 SDK system.listDownloadedModels('llm') 으로
조회하도록 변경. REST /v1/models 는 JIT 옵션이 꺼져 있으면 로드된 모델만
반환하여 macOS 환경에서 dropdown 이 비거나 fallback 한 줄만 남던 문제 해결.
SDK 실패 시 REST 로 자동 fallback.
- LM Studio 로드/언로드 실패를 readyBar 의 영속 segment 로 표시. 모델을
다시 선택하면 clearLmStudioError() 로 해제.
- src/security.ts: PowerShell '&&' rewrite 를 win32 에서만 수행. macOS/Linux
에서는 'if (\$?) { ... }' 가 zsh/bash 문법 오류라 명령 자체가 깨졌음.
- src/utils.ts: system prompt 에 OS 별 [ENVIRONMENT] 블록 동적 주입
(셸/경로 스타일/체이닝 연산자). 'cd E:\\... ; ...' 같은 Windows 전용
예시를 macOS 에서 그대로 따라하던 회귀 차단.
- 테스트 mock 에 listDownloaded() 추가.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"result": "Final report with inconsistencies. This should be long enough to pass validation.",
|
||||
"createdAt": 1779444327842,
|
||||
"createdAt": 1779495116625,
|
||||
"modelVersion": "unknown"
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"result": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.",
|
||||
"createdAt": 1779444327840,
|
||||
"createdAt": 1779495116625,
|
||||
"modelVersion": "unknown"
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"result": "Detailed Execution Plan: 1. Research 2. Analyze 3. Write report with high quality.",
|
||||
"createdAt": 1779444327837,
|
||||
"createdAt": 1779495116624,
|
||||
"modelVersion": "unknown"
|
||||
}
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"result": "---\nid: stress_conflict_1779444327825\ndate: 2026-05-22T10:05:27.844Z\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]** 전략 수립 중... (11ms)\n- **[RESEARCHER]** 핵심 정보 수집 및 분석 중... (2ms)\n- **[WRITER]** 최종 리포트 작성 및 편집 중... (3ms)\n",
|
||||
"createdAt": 1779444327844,
|
||||
"result": "---\nid: stress_conflict_1779495116612\ndate: 2026-05-23T00:11:56.625Z\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]** 전략 수립 중... (11ms)\n- **[RESEARCHER]** 핵심 정보 수집 및 분석 중... (0ms)\n- **[WRITER]** 최종 리포트 작성 및 편집 중... (1ms)\n",
|
||||
"createdAt": 1779495116625,
|
||||
"modelVersion": "unknown"
|
||||
}
|
||||
+10
-10
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"missionId": "stress_conflict_1779444327825",
|
||||
"missionId": "stress_conflict_1779495116612",
|
||||
"status": "completed",
|
||||
"startTime": "2026-05-22T10:05:27.825Z",
|
||||
"totalElapsedMs": 20,
|
||||
"startTime": "2026-05-23T00:11:56.613Z",
|
||||
"totalElapsedMs": 13,
|
||||
"results": {
|
||||
"planner": "Detailed Execution Plan: 1. Research 2. Analyze 3. Write report with high quality.",
|
||||
"researcher": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.",
|
||||
@@ -18,28 +18,28 @@
|
||||
"to": "planner",
|
||||
"durationMs": 11,
|
||||
"message": "전략 수립 중...",
|
||||
"ts": "2026-05-22T10:05:27.836Z"
|
||||
"ts": "2026-05-23T00:11:56.624Z"
|
||||
},
|
||||
{
|
||||
"from": "planner",
|
||||
"to": "researcher",
|
||||
"durationMs": 2,
|
||||
"durationMs": 0,
|
||||
"message": "핵심 정보 수집 및 분석 중...",
|
||||
"ts": "2026-05-22T10:05:27.838Z"
|
||||
"ts": "2026-05-23T00:11:56.624Z"
|
||||
},
|
||||
{
|
||||
"from": "researcher",
|
||||
"to": "writer",
|
||||
"durationMs": 3,
|
||||
"durationMs": 1,
|
||||
"message": "최종 리포트 작성 및 편집 중...",
|
||||
"ts": "2026-05-22T10:05:27.841Z"
|
||||
"ts": "2026-05-23T00:11:56.625Z"
|
||||
},
|
||||
{
|
||||
"from": "writer",
|
||||
"to": "completed",
|
||||
"durationMs": 4,
|
||||
"durationMs": 1,
|
||||
"message": "미션 완료",
|
||||
"ts": "2026-05-22T10:05:27.845Z"
|
||||
"ts": "2026-05-23T00:11:56.626Z"
|
||||
}
|
||||
],
|
||||
"resilienceMetrics": {
|
||||
@@ -392,6 +392,9 @@
|
||||
}
|
||||
segs.push(`<span class="rb-seg ${s.memory ? '' : 'rb-dim'}">메모리 ${s.memory ? 'On' : 'Off'}</span>`);
|
||||
if (s.multiAgent) segs.push(`<span class="rb-seg">멀티에이전트</span>`);
|
||||
if (s.lmStudioError) {
|
||||
segs.push(`<span class="rb-seg bad" title="${escAttr(s.lmStudioError)}">⚠ LM Studio 로드 실패</span>`);
|
||||
}
|
||||
rbContent.innerHTML = segs.join('<span class="rb-sep">·</span>');
|
||||
if (rbDot) {
|
||||
const on = s.engine && s.engine.online;
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
"name": "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.",
|
||||
"version": "2.2.63",
|
||||
"version": "2.2.64",
|
||||
"publisher": "g1nation",
|
||||
"license": "MIT",
|
||||
"icon": "assets/icon.png",
|
||||
|
||||
@@ -157,6 +157,7 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
lifecycle,
|
||||
activity: activityTracker,
|
||||
loadedModels: () => lmStudioClient.listLoadedCached(),
|
||||
downloadedModels: () => lmStudioClient.listDownloaded(),
|
||||
});
|
||||
// One-time repair: rewrite any chronicle projects that were saved with the
|
||||
// workspace parent as their `projectRoot` (a side-effect of the old
|
||||
|
||||
@@ -7,6 +7,14 @@ export interface ILMStudioClient {
|
||||
listLoaded(): Promise<string[]>;
|
||||
/** Like listLoaded() but caches the result for `ttlMs` to avoid hammering the SDK. */
|
||||
listLoadedCached(ttlMs?: number): Promise<string[]>;
|
||||
/**
|
||||
* List every LLM the user has downloaded into LM Studio, regardless of
|
||||
* whether it is currently loaded. Returns the SDK `modelKey` of each entry —
|
||||
* the exact identifier `llm.load()` accepts. Use this for the dropdown so
|
||||
* the list does not depend on LM Studio's JIT setting (REST `/v1/models`
|
||||
* only returns loaded models when JIT is off).
|
||||
*/
|
||||
listDownloaded(): Promise<string[]>;
|
||||
/**
|
||||
* Resolve a chat-ready handle for an already-loaded (or just-loaded) model.
|
||||
*
|
||||
@@ -117,6 +125,19 @@ export class LMStudioClient implements ILMStudioClient {
|
||||
}
|
||||
}
|
||||
|
||||
async listDownloaded(): Promise<string[]> {
|
||||
try {
|
||||
const items: any[] = await this.getSdk().system.listDownloadedModels('llm');
|
||||
return items
|
||||
.map((m) => m?.modelKey ?? null)
|
||||
.filter((k): k is string => typeof k === 'string' && k.length > 0);
|
||||
} catch (e: any) {
|
||||
const msg = e?.message ?? String(e);
|
||||
logError('Failed to list downloaded LM Studio models.', { error: msg });
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async getModelHandle(modelKey: string, options?: { refresh?: boolean }): Promise<LLM> {
|
||||
try {
|
||||
if (options?.refresh) {
|
||||
|
||||
@@ -98,8 +98,13 @@ function splitTopLevelAnd(command: string): string[] {
|
||||
* `$?` reflects the success of the previous command, so a failed step still
|
||||
* short-circuits the rest — important so e.g. a failed `cd` never lets `git`
|
||||
* run in the wrong directory.
|
||||
*
|
||||
* On macOS/Linux this rewrite is actively harmful — `&&` is the native
|
||||
* POSIX-shell operator and the PowerShell `if ($?) { ... }` shape is a zsh/bash
|
||||
* syntax error. So skip the rewrite entirely off-Windows.
|
||||
*/
|
||||
function rewriteForPowerShell(command: string): string {
|
||||
if (process.platform !== 'win32') { return command; }
|
||||
if (!command.includes('&&')) { return command; }
|
||||
const parts = splitTopLevelAnd(command);
|
||||
if (parts.length <= 1) { return command; }
|
||||
|
||||
@@ -302,6 +302,9 @@ export async function handleChatMessage(provider: SidebarChatProvider, data: any
|
||||
const { target } = pickConfigTarget('g1nation', 'defaultModel');
|
||||
await vscode.workspace.getConfiguration('g1nation').update('defaultModel', data.value, target);
|
||||
logInfo(`Default model updated to: ${data.value}`, { target });
|
||||
// Wipe any persistent LM Studio error segment from the readyBar — the
|
||||
// next load attempt will repaint it if it also fails.
|
||||
provider.clearLmStudioError();
|
||||
provider._lmStudio?.lifecycle.onModelSelected(data.value);
|
||||
return true;
|
||||
}
|
||||
|
||||
+52
-3
@@ -60,6 +60,13 @@ export interface SidebarLmStudioDeps {
|
||||
activity: IActivityTracker;
|
||||
/** Returns the list of model identifiers currently loaded in LM Studio (cached). */
|
||||
loadedModels: () => Promise<string[]>;
|
||||
/**
|
||||
* Returns every model downloaded into LM Studio (modelKey form). Used by the
|
||||
* dropdown so it does not depend on LM Studio's JIT setting — REST
|
||||
* `/v1/models` only lists loaded models when JIT is off, which on macOS
|
||||
* commonly leaves the dropdown with just the fallback `defaultModel`.
|
||||
*/
|
||||
downloadedModels: () => Promise<string[]>;
|
||||
}
|
||||
|
||||
interface LastVisibleChatSnapshot {
|
||||
@@ -790,9 +797,24 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
});
|
||||
}
|
||||
|
||||
/** Latest LM Studio load/unload error — surfaced as a persistent segment in the readyBar
|
||||
* until the next successful model selection clears it. The transient chat toast alone
|
||||
* was getting missed (scrolled away, blended into chat noise). */
|
||||
private _lmStudioLastError: string | undefined;
|
||||
|
||||
/** Surface LM Studio lifecycle errors (load/unload failures) to the chat UI as a non-fatal toast. */
|
||||
public postLmStudioError(message: string): void {
|
||||
this._lmStudioLastError = message;
|
||||
this._view?.webview.postMessage({ type: 'lmStudioError', value: message });
|
||||
void this._sendReadyStatus();
|
||||
}
|
||||
|
||||
/** Clear the persistent LM Studio error segment. Called when the user picks a new
|
||||
* model so a stale failure does not haunt the next attempt. */
|
||||
public clearLmStudioError(): void {
|
||||
if (this._lmStudioLastError === undefined) return;
|
||||
this._lmStudioLastError = undefined;
|
||||
void this._sendReadyStatus();
|
||||
}
|
||||
|
||||
public resolveWebviewView(
|
||||
@@ -1221,6 +1243,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
contextLength: effectiveContextLength,
|
||||
nominalContextLength: config.contextLength,
|
||||
cappedForSmallModel,
|
||||
lmStudioError: this._lmStudioLastError ?? null,
|
||||
};
|
||||
} catch (err: any) {
|
||||
logError('Failed to build ready status.', { error: err?.message || String(err) });
|
||||
@@ -3915,8 +3938,33 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
} else {
|
||||
const engine = resolveEngine(url); // 단일 엔진만
|
||||
const modelsUrl = buildApiUrl(url, engine, 'models');
|
||||
|
||||
// LM Studio: prefer the SDK's `system.listDownloadedModels('llm')` over the
|
||||
// REST `/v1/models` endpoint. REST only returns models that are currently
|
||||
// loaded when LM Studio's "Just-In-Time Model Loading" setting is off —
|
||||
// which on macOS leaves the dropdown with zero entries (or one fallback
|
||||
// entry that may not match any real `modelKey`). The SDK call enumerates
|
||||
// every downloaded LLM regardless of JIT and returns the exact `modelKey`
|
||||
// that `llm.load()` accepts. Falls back to REST if the SDK round-trip
|
||||
// throws (e.g. LM Studio app open but local server not enabled).
|
||||
if (engine === 'lmstudio' && this._lmStudio) {
|
||||
try {
|
||||
logInfo('Model discovery started.', { engine, modelsUrl, force });
|
||||
logInfo('Model discovery started (SDK).', { engine, force });
|
||||
const sdkModels = await this._lmStudio.downloadedModels();
|
||||
if (sdkModels.length > 0) {
|
||||
models = sdkModels;
|
||||
logInfo('Model discovery succeeded (SDK).', { engine, count: models.length, modelsPreview: models.slice(0, 5) });
|
||||
} else {
|
||||
logInfo('LM Studio SDK returned no downloaded models — falling back to REST /v1/models.', { engine });
|
||||
}
|
||||
} catch (e: any) {
|
||||
logError('LM Studio SDK model discovery failed — falling back to REST.', { engine, error: e?.message || String(e) });
|
||||
}
|
||||
}
|
||||
|
||||
if (models.length === 0) {
|
||||
try {
|
||||
logInfo('Model discovery started (REST).', { engine, modelsUrl, force });
|
||||
const res = await fetch(modelsUrl, { signal: AbortSignal.timeout(5000) });
|
||||
const rawText = await res.text();
|
||||
if (!res.ok) {
|
||||
@@ -3928,11 +3976,12 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
: (data.models || []).map((m: any) => m.name);
|
||||
|
||||
if (models.length > 0) {
|
||||
logInfo('Model discovery succeeded.', { engine, count: models.length, modelsPreview: models.slice(0, 5) });
|
||||
logInfo('Model discovery succeeded (REST).', { engine, count: models.length, modelsPreview: models.slice(0, 5) });
|
||||
}
|
||||
}
|
||||
} catch (e: any) {
|
||||
logError('Model discovery failed.', { engine, modelsUrl, error: e?.message || String(e) });
|
||||
logError('Model discovery failed (REST).', { engine, modelsUrl, error: e?.message || String(e) });
|
||||
}
|
||||
}
|
||||
|
||||
online = models.length > 0;
|
||||
|
||||
+50
-6
@@ -219,15 +219,15 @@ When the user asks to run, start, launch, boot, or serve something (실행/구
|
||||
- NEVER invent a script name, port number, or environment variable. If you have not seen it in a file THIS session, do not state it as fact.
|
||||
- If you do not know the exact start command, FIRST read the project's package.json with <read_file>, then emit <run_command> with the real script name.
|
||||
- <run_command> runs in a real terminal. If the target folder differs from the workspace, cd into its absolute path first.
|
||||
- The terminal is Windows PowerShell. Chain steps with ";" — NEVER "&&" (it is a syntax error in PowerShell 5.1). Example: cd 'C:\proj'; git add .; git commit -m 'msg'; git push
|
||||
- The terminal type, shell, path style, and command-chaining operator are NOT fixed — they depend on the host OS. ALWAYS follow the [ENVIRONMENT] block at the bottom of this prompt; that block is the single source of truth. Never assume Windows / PowerShell / drive letters, never assume macOS / zsh / forward slashes — read the block first.
|
||||
- After acting, reply with ONE short line: what you started and where. No tutorial, no follow-up checklist.
|
||||
|
||||
Worked example — user says: "E:\Wiki\Datacollect 서버 실행해줘"
|
||||
Worked example pattern (adapt path style and chaining operator to whatever the [ENVIRONMENT] block says — the literal paths below are illustrative only):
|
||||
Step 1 (only when the start script is unknown):
|
||||
<read_file path="E:\Wiki\Datacollect\package.json"/>
|
||||
<read_file path="<absolute project path>/package.json"/>
|
||||
Step 2 (after the real scripts are known — pick the actual one, never a guessed name):
|
||||
<run_command>cd 'E:\Wiki\Datacollect'; npm run start-full</run_command>
|
||||
Then reply: "Datacollect 서버를 start-full 스크립트로 터미널에서 실행했습니다."
|
||||
<run_command>cd '<absolute project path>' <chain-op> npm run <real-script-name></run_command>
|
||||
Then reply with one short line stating what was started and where.
|
||||
|
||||
[STRICT GLOBAL RULES]
|
||||
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.
|
||||
@@ -387,11 +387,55 @@ When you do ask: it is ONE plain sentence on its own line. NEVER put it under a
|
||||
2. File paths are relative to the workspace or absolute under /Volumes/Data/project/Antigravity.
|
||||
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 "시작할까요?".`;
|
||||
|
||||
function getEnvironmentBlock(): string {
|
||||
const platform = process.platform;
|
||||
const homeDir = os.homedir();
|
||||
const cwd = process.cwd();
|
||||
const wsFolder = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
|
||||
|
||||
let osLabel: string;
|
||||
let shellLabel: string;
|
||||
let chainRule: string;
|
||||
let pathRule: string;
|
||||
let example: string;
|
||||
|
||||
if (platform === 'win32') {
|
||||
osLabel = 'Windows';
|
||||
shellLabel = process.env.ComSpec?.toLowerCase().includes('powershell') ? 'PowerShell' : 'PowerShell / cmd';
|
||||
chainRule = `Chain steps with ";" — NEVER "&&" (it is a hard parser error in PowerShell 5.1 and the whole command fails). Use "if ($?) { ... }" for short-circuit semantics when the next step must depend on the previous step succeeding.`;
|
||||
pathRule = `Paths use drive letters and backslashes: C:\\Users\\name\\project, E:\\Wiki\\Datacollect. Quote with single quotes when the path contains spaces.`;
|
||||
example = `cd 'C:\\proj'; git add .; git commit -m 'msg'; git push`;
|
||||
} else if (platform === 'darwin') {
|
||||
osLabel = 'macOS';
|
||||
shellLabel = process.env.SHELL?.split('/').pop() || 'zsh';
|
||||
chainRule = `Chain steps with "&&" (POSIX short-circuit). Use ";" only when steps are independent. NEVER emit PowerShell-only syntax like "if ($?) { ... }" — that is a zsh/bash parser error.`;
|
||||
pathRule = `Paths are POSIX with forward slashes: /Volumes/Data/project/..., /Users/<name>/.... NEVER use Windows drive letters like C:\\ or E:\\ — those are not valid paths on macOS and "cd C:/..." will fail with "no such file or directory".`;
|
||||
example = `cd '/Volumes/Data/project/Antigravity/Wiki' && git pull`;
|
||||
} else {
|
||||
osLabel = `Linux (${platform})`;
|
||||
shellLabel = process.env.SHELL?.split('/').pop() || 'bash';
|
||||
chainRule = `Chain steps with "&&" (POSIX short-circuit). Use ";" only when steps are independent. NEVER emit PowerShell-only syntax like "if ($?) { ... }" — that is a bash/zsh parser error.`;
|
||||
pathRule = `Paths are POSIX with forward slashes: /home/<name>/..., /opt/.... NEVER use Windows drive letters like C:\\ or E:\\.`;
|
||||
example = `cd /home/user/proj && git pull`;
|
||||
}
|
||||
|
||||
return `\n\n[ENVIRONMENT — authoritative, overrides any conflicting example elsewhere in this prompt]
|
||||
- OS: ${osLabel} (process.platform=${platform})
|
||||
- Shell: ${shellLabel}
|
||||
- Home: ${homeDir}
|
||||
- Workspace: ${wsFolder ?? '(no workspace open)'}
|
||||
- CWD at extension start: ${cwd}
|
||||
- Terminal chaining: ${chainRule}
|
||||
- Path style: ${pathRule}
|
||||
- Canonical <run_command> example for this OS: ${example}`;
|
||||
}
|
||||
|
||||
export function getSystemPrompt(): string {
|
||||
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];
|
||||
const base = `${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").\n\n[출력 위생 규칙 — 반드시 준수]\n- 자연스러운 한국어로 작성하고, 한 단어 안에 한글과 영문 알파벳을 섞지 마시오 ("결ently", "인orp" 같은 깨진 합성 표기 절대 금지).\n- 외래어·기술 용어는 완전한 한글 표기 또는 완전한 영문 단어 중 하나로 일관되게 쓰시오.\n- 내부 검증·체크 로그(Consistency/Completeness/Accuracy 등) 블록을 사용자 출력에 포함하지 마시오.`;
|
||||
const envBlock = getEnvironmentBlock();
|
||||
const base = `${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").\n\n[출력 위생 규칙 — 반드시 준수]\n- 자연스러운 한국어로 작성하고, 한 단어 안에 한글과 영문 알파벳을 섞지 마시오 ("결ently", "인orp" 같은 깨진 합성 표기 절대 금지).\n- 외래어·기술 용어는 완전한 한글 표기 또는 완전한 영문 단어 중 하나로 일관되게 쓰시오.\n- 내부 검증·체크 로그(Consistency/Completeness/Accuracy 등) 블록을 사용자 출력에 포함하지 마시오.${envBlock}`;
|
||||
// Self-Reflector Phase A — 사용자 설정이 켜져 있으면 답변 끝에 자기검증
|
||||
// 블록을 강제하는 룰을 prepend. require로 동적 로드해 순환 import 회피.
|
||||
try {
|
||||
|
||||
@@ -78,6 +78,10 @@ class FakeLMStudioClient implements ILMStudioClient {
|
||||
return [];
|
||||
}
|
||||
|
||||
async listDownloaded(): Promise<string[]> {
|
||||
return [];
|
||||
}
|
||||
|
||||
async getModelHandle(_modelKey: string): Promise<any> {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -79,6 +79,7 @@ class FakeClient implements ILMStudioClient {
|
||||
async unload(_: string): Promise<void> { /* noop */ }
|
||||
async listLoaded(): Promise<string[]> { return []; }
|
||||
async listLoadedCached(): Promise<string[]> { return []; }
|
||||
async listDownloaded(): Promise<string[]> { return []; }
|
||||
async isReachable(): Promise<boolean> { return true; }
|
||||
|
||||
async getModelHandle(modelKey: string): Promise<any> {
|
||||
|
||||
Reference in New Issue
Block a user