chore: version up to 2.80.38 and package with refined recovery
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": 1778597639298,
|
||||
"createdAt": 1778598898519,
|
||||
"modelVersion": "unknown"
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"result": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.",
|
||||
"createdAt": 1778597639290,
|
||||
"createdAt": 1778598898518,
|
||||
"modelVersion": "unknown"
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"result": "Detailed Execution Plan: 1. Research 2. Analyze 3. Write report with high quality.",
|
||||
"createdAt": 1778597639286,
|
||||
"createdAt": 1778598898517,
|
||||
"modelVersion": "unknown"
|
||||
}
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"result": "---\nid: stress_conflict_1778597639274\ndate: 2026-05-12T14:53:59.302Z\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]** 핵심 정보 수집 및 분석 중... (1ms)\n- **[WRITER]** 최종 리포트 작성 및 편집 중... (8ms)\n",
|
||||
"createdAt": 1778597639302,
|
||||
"result": "---\nid: stress_conflict_1778598898506\ndate: 2026-05-12T15:14:58.520Z\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]** 전략 수립 중... (10ms)\n- **[RESEARCHER]** 핵심 정보 수집 및 분석 중... (1ms)\n- **[WRITER]** 최종 리포트 작성 및 편집 중... (2ms)\n",
|
||||
"createdAt": 1778598898520,
|
||||
"modelVersion": "unknown"
|
||||
}
|
||||
+10
-10
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"missionId": "stress_conflict_1778597639274",
|
||||
"missionId": "stress_conflict_1778598898506",
|
||||
"status": "completed",
|
||||
"startTime": "2026-05-12T14:53:59.274Z",
|
||||
"totalElapsedMs": 28,
|
||||
"startTime": "2026-05-12T15:14:58.506Z",
|
||||
"totalElapsedMs": 14,
|
||||
"results": {
|
||||
"planner": "Detailed Execution Plan: 1. Research 2. Analyze 3. Write report with high quality.",
|
||||
"researcher": "[CONFLICT WARNING] 성능이 200% 증가했습니다. vs 그러나 동시에 50% 감소했습니다. 최적화와 성능 저하가 동시에 발견됨.",
|
||||
@@ -16,30 +16,30 @@
|
||||
{
|
||||
"from": "idle",
|
||||
"to": "planner",
|
||||
"durationMs": 11,
|
||||
"durationMs": 10,
|
||||
"message": "전략 수립 중...",
|
||||
"ts": "2026-05-12T14:53:59.285Z"
|
||||
"ts": "2026-05-12T15:14:58.516Z"
|
||||
},
|
||||
{
|
||||
"from": "planner",
|
||||
"to": "researcher",
|
||||
"durationMs": 1,
|
||||
"message": "핵심 정보 수집 및 분석 중...",
|
||||
"ts": "2026-05-12T14:53:59.286Z"
|
||||
"ts": "2026-05-12T15:14:58.517Z"
|
||||
},
|
||||
{
|
||||
"from": "researcher",
|
||||
"to": "writer",
|
||||
"durationMs": 8,
|
||||
"durationMs": 2,
|
||||
"message": "최종 리포트 작성 및 편집 중...",
|
||||
"ts": "2026-05-12T14:53:59.294Z"
|
||||
"ts": "2026-05-12T15:14:58.519Z"
|
||||
},
|
||||
{
|
||||
"from": "writer",
|
||||
"to": "completed",
|
||||
"durationMs": 8,
|
||||
"durationMs": 1,
|
||||
"message": "미션 완료",
|
||||
"ts": "2026-05-12T14:53:59.302Z"
|
||||
"ts": "2026-05-12T15:14:58.520Z"
|
||||
}
|
||||
],
|
||||
"resilienceMetrics": {
|
||||
@@ -6,6 +6,6 @@
|
||||
"description": "Auto-detected from the local project path in the conversation.",
|
||||
"corePurpose": "Capture project direction, architecture discussion, decisions, and development notes as Markdown.",
|
||||
"detailLevel": "standard",
|
||||
"createdAt": "2026-05-10T07:42:38.921Z",
|
||||
"updatedAt": "2026-05-10T07:42:38.928Z"
|
||||
"createdAt": "2026-05-12T15:13:04.937Z",
|
||||
"updatedAt": "2026-05-12T15:13:04.938Z"
|
||||
}
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
# Development Log: /Volumes/Data/project/Antigravity/ConnectAI 분석하고 부족한 부분이나 개선이 필요한 부분을 알려주면 좋겠어.
|
||||
|
||||
## Purpose
|
||||
Record the implementation or verification outcome from the current conversation.
|
||||
|
||||
## Implementation Summary
|
||||
## 요약 현재 프로젝트 구조는 기본적인 개발 환경 설정 파일과 아스트라(Astra)의 배포 관련 파일들로 구성되어 있습니다. 전반적으로 기능적인 코드가 눈에 띄지 않아, 이 디렉토리 자체만으로는 시스템 전체의 아키텍처나 로직 흐름을 파악하기 어렵습니다. 개선이 필요하다면, 핵심 비즈니스 로직과 관련된 소스 코드와 API 문서화가 가장 시급합니다. ## 상세 설명 - **현재 구조 분석:** `/Volumes/Data/project/Antigravity/ConnectAI` 디렉토리는 프로젝트의 메타 정보(README.md, LICENSE, PATCHNOTES.md), 의존성 관리 파일(package.json, package-lock.json), 빌드 설정(jest.config.js, tsconfig.json), 그리고 여러 버전의 Astra 관련 바이너리 및 스킨 파일들(`astra-*.vsix`)을 포함하고 있습니다. - **핵심 문제:** 이 디렉토리에는 `src/` 폴더가 존재...
|
||||
|
||||
## Architecture
|
||||
Captured automatically from the assistant response and active project context.
|
||||
|
||||
## Changed Files
|
||||
- ARCHITECTURE_ANALYSIS.md
|
||||
|
||||
## Dependency Notes
|
||||
No new dependency note was captured automatically.
|
||||
|
||||
## Bugs
|
||||
No bugs recorded.
|
||||
|
||||
## Lessons
|
||||
- Automatic project records should be generated in the background when the turn contains durable project knowledge.
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
# Development Log: /Volumes/Data/project/Antigravity/ConnectAI 분석하고 부족한 부분이나 개선이 필요한 부분을 알려주면 좋겠어.
|
||||
|
||||
## Purpose
|
||||
Record the implementation or verification outcome from the current conversation.
|
||||
|
||||
## Implementation Summary
|
||||
## 요약
|
||||
|
||||
## Architecture
|
||||
Captured automatically from the assistant response and active project context.
|
||||
|
||||
## Changed Files
|
||||
- No explicit changed file list was captured automatically.
|
||||
|
||||
## Dependency Notes
|
||||
No new dependency note was captured automatically.
|
||||
|
||||
## Bugs
|
||||
No bugs recorded.
|
||||
|
||||
## Lessons
|
||||
- Automatic project records should be generated in the background when the turn contains durable project knowledge.
|
||||
@@ -84,3 +84,9 @@
|
||||
|
||||
## 2026-05-10
|
||||
- Auto decision record created: decisions/ADR-0007-volumes-data-project-antigravity-connectai-이거에-기능-개선을-하고-싶어-.md
|
||||
|
||||
## 2026-05-12
|
||||
- Auto development record created: development/2026-05-12_volumes-data-project-antigravity-connectai-분석하고-부족한-부분이나-개선이_implementation.md
|
||||
|
||||
## 2026-05-12
|
||||
- Auto development record created: development/2026-05-12_volumes-data-project-antigravity-connectai-분석하고-부족한-부분이나-개선이_implementation-2.md
|
||||
|
||||
+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.80.37",
|
||||
"version": "2.80.38",
|
||||
"publisher": "g1nation",
|
||||
"license": "MIT",
|
||||
"icon": "assets/icon.png",
|
||||
|
||||
+32
-18
@@ -45,6 +45,7 @@ import {
|
||||
extractVisibleFinal,
|
||||
shouldFinalOnlyRetry,
|
||||
shouldAutoContinue,
|
||||
looksCutOff,
|
||||
mergeContinuationParts,
|
||||
buildContinuationUserPrompt,
|
||||
FINAL_ONLY_DIRECTIVE,
|
||||
@@ -485,27 +486,36 @@ export class AgentExecutor {
|
||||
let fullSystemPrompt: string;
|
||||
|
||||
if (isAgentMode) {
|
||||
// 1. 기본 시스템 프롬프트에서 에이전트 포맷과 충돌하는 섹션 제거
|
||||
// The Agent's prompt IS the primary directive (role / persona / tone / output format),
|
||||
// so it LEADS the system prompt — models anchor on the first persona they see, not the
|
||||
// last, especially small ones. The Astra base prompt is reduced to neutral scaffolding
|
||||
// (action tags, current date, anti-leak rules) and follows; a short reminder at the very
|
||||
// end keeps the model from drifting back to a generic assistant.
|
||||
const strippedSystemPrompt = this.stripAstraFormattingForAgentMode(systemPrompt);
|
||||
const agentPromptText = (options.agentSkillContext || '').trim();
|
||||
if (estimateTokens(agentPromptText) > Math.floor(config.contextLength * 0.5)) {
|
||||
logInfo('Agent prompt is unusually large relative to the context window.', {
|
||||
model: actualModel, agentPromptTokens: estimateTokens(agentPromptText), contextLength: config.contextLength,
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Astra 전용 컨텍스트는 에이전트 모드에서 비활성화
|
||||
// (astraStanceCtx, thinkingPartnerCtx, v4PolicyCtx → 에이전트 역할과 충돌)
|
||||
const agentDirective = [
|
||||
'\n\n[AGENT MODE — ABSOLUTE OVERRIDE]',
|
||||
'You are NOT operating as Astra for this response.',
|
||||
'A specialized Agent has been selected by the user.',
|
||||
'ALL output format, role, persona, and style instructions from the Agent below',
|
||||
'take ABSOLUTE PRECEDENCE over any previous formatting rules (including ## 요약, ## 상세 설명, ## 제안).',
|
||||
'You MUST follow the Agent\'s 📄 Output Format exactly. Do NOT fall back to Astra\'s default format.',
|
||||
const agentBlock = [
|
||||
'[AGENT MODE — PRIMARY DIRECTIVE]',
|
||||
'A specialized Agent has been selected by the user. The Agent System Prompt below is your',
|
||||
'PRIMARY directive: it defines your role, persona, tone, and output format. Follow it exactly.',
|
||||
'Everything after the Agent block (action-tag reference, date, brain/project context) is technical',
|
||||
'scaffolding — use it only as the Agent\'s task requires. Do NOT impose a generic assistant',
|
||||
'format (e.g. ## 요약 / ## 상세 설명 / ## 제안) unless the Agent explicitly asks for one.',
|
||||
'',
|
||||
'--- AGENT SYSTEM PROMPT START ---',
|
||||
options.agentSkillContext,
|
||||
'--- AGENT SYSTEM PROMPT END ---'
|
||||
agentPromptText || '(this agent has no instructions yet — fall back to being a concise, direct assistant)',
|
||||
'--- AGENT SYSTEM PROMPT END ---',
|
||||
].join('\n');
|
||||
const agentTailReminder = '\n\n[REMINDER] You are operating as the Agent defined above. Keep its role, persona, and output format. Do not fall back to a default assistant style or section format.';
|
||||
|
||||
// 3. 조립: 기본(축소) → 유틸리티 컨텍스트 → 에이전트 프롬프트(최후단)
|
||||
// [CONTEXT] … [/CONTEXT] 사이만 컨텍스트 초과 시 trim 대상 — agentDirective/negative 는 보호.
|
||||
fullSystemPrompt = `${strippedSystemPrompt}${internetCtx}${memoryCtx}${designerCtx}${secondBrainTraceCtx}\n\n[CONTEXT]\n${knowledgeContextForPrompt}\n${contextBlock}\n[/CONTEXT]\n${negativeCtx}${agentDirective}`;
|
||||
// [CONTEXT] … [/CONTEXT] 사이만 컨텍스트 초과 시 trim 대상 — agentBlock(앞)·reminder(뒤)·negative 는 보호.
|
||||
// memoryCtx(RAG/메모리/lessons)도 [CONTEXT] 안에 넣어 토큰이 빡빡할 때 대화 기록보다 먼저 잘리게 한다.
|
||||
fullSystemPrompt = `${agentBlock}\n\n${strippedSystemPrompt}${internetCtx}${designerCtx}${secondBrainTraceCtx}\n\n[CONTEXT]\n${memoryCtx}\n${knowledgeContextForPrompt}\n${contextBlock}\n[/CONTEXT]\n${negativeCtx}${agentTailReminder}`;
|
||||
} else {
|
||||
// 기존 Astra 모드 (에이전트 미선택)
|
||||
const localProjectKnowledgeCtx = prompt && localPathContext && this.isProjectKnowledgeCreationRequest(prompt)
|
||||
@@ -530,7 +540,8 @@ export class AgentExecutor {
|
||||
const casualCtx = isCasualConversation
|
||||
? '\n\n[CASUAL CONVERSATION MODE]\nThe user sent a greeting, acknowledgement, or light conversational message. Reply naturally and briefly to the message itself. Do not use Second Brain, memory, project records, reports, references, or analysis unless the user explicitly asks for them.'
|
||||
: '';
|
||||
fullSystemPrompt = `${systemPrompt}${internetCtx}${memoryCtx}${designerCtx}${localProjectKnowledgeCtx}${thinkingPartnerCtx}${astraStanceCtx}${secondBrainTraceCtx}${v4PolicyCtx}${casualCtx}\n\n[CONTEXT]\n${knowledgeContextForPrompt}\n${contextBlock}\n[/CONTEXT]\n${negativeCtx}`;
|
||||
// memoryCtx(RAG/메모리/lessons)는 [CONTEXT] 안에 — 토큰이 빡빡하면 대화 기록보다 먼저 잘림.
|
||||
fullSystemPrompt = `${systemPrompt}${internetCtx}${designerCtx}${localProjectKnowledgeCtx}${thinkingPartnerCtx}${astraStanceCtx}${secondBrainTraceCtx}${v4PolicyCtx}${casualCtx}\n\n[CONTEXT]\n${memoryCtx}\n${knowledgeContextForPrompt}\n${contextBlock}\n[/CONTEXT]\n${negativeCtx}`;
|
||||
}
|
||||
// ──────────────────────────────────────────────────────────────────
|
||||
// [Context Limit Manager] context length 는 "답변을 그만큼 길게 써도 된다"
|
||||
@@ -980,8 +991,11 @@ export class AgentExecutor {
|
||||
});
|
||||
}
|
||||
const outputTokens = estimateTokens(assistantContent);
|
||||
const notice = shouldShowTruncationNotice(stopKind, outputTokens, maxOutputTokens)
|
||||
? truncationNotice(stopKind)
|
||||
// Show the "incomplete" notice when the engine said output-limit/context-overflow/error,
|
||||
// OR when (after all auto-continuation rounds) the answer still plainly ends mid-sentence.
|
||||
const notice =
|
||||
shouldShowTruncationNotice(stopKind, outputTokens, maxOutputTokens) ? truncationNotice(stopKind)
|
||||
: looksCutOff(assistantContent) ? truncationNotice('output-limit')
|
||||
: '';
|
||||
if (notice && assistantContent.trim()) {
|
||||
assistantContent = assistantContent.trimEnd() + notice;
|
||||
|
||||
@@ -125,8 +125,36 @@ export function shouldFinalOnlyRetry(cleaned: CleanedAssistantOutput): boolean {
|
||||
}
|
||||
|
||||
/**
|
||||
* Should we silently continue from where the answer was cut off? Only when it actually hit the
|
||||
* output-token ceiling and we already have a non-trivial visible answer to continue from.
|
||||
* Does the answer plainly end mid-sentence / mid-structure? Conservative — only flags *unambiguous*
|
||||
* incompleteness (a complete Korean sentence may legitimately end without a period, so we never flag
|
||||
* a plain syllable like `다`/`요`; we only flag connective particles, mid-English-words, mid-clause
|
||||
* commas/colons, unclosed code fences/brackets, and dangling markdown bullets/headings).
|
||||
*/
|
||||
export function looksCutOff(text: string): boolean {
|
||||
const t = (text || '').replace(/\s+$/, '');
|
||||
if (t.length < 12) return false;
|
||||
// unclosed code fence
|
||||
if ((t.match(/```/g) || []).length % 2 === 1) return true;
|
||||
// ends with an opening bracket / quote (unclosed pair)
|
||||
if (/[([{“‘"'`]$/.test(t)) return true;
|
||||
// dangling markdown bullet / heading / blockquote with no content after the marker
|
||||
if (/(?:^|\n)\s*(?:[-*+]|#{1,6}|>|\d+\.)\s*$/.test(t)) return true;
|
||||
// ends mid-English-word or mid-number
|
||||
if (/[A-Za-z0-9]$/.test(t)) return true;
|
||||
// ends mid-clause (comma / colon / semicolon / list separator)
|
||||
if (/[,:;·、,]$/.test(t)) return true;
|
||||
// ends with a Korean particle / connective ending that NEVER closes a sentence
|
||||
if (/(?:으로|로서|로써|로|의|에서|에게|한테|에|을|를|과|와|이랑|랑|는|은|이|가|도|만|까지|부터|마다|조차|마저|밖에|뿐|처럼|같이|보다|이나|거나|든지|든가|고|며|면서|면|어서|아서|여서|니까|는데|은데|ㄴ데|지만|던|도록)$/.test(t)) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should we silently continue from where the answer was cut off? The point is to recover regardless
|
||||
* of *why* it stopped, since local engines / SDKs often report the stop reason wrongly or not at all:
|
||||
* - the engine said it hit the output cap (`output-limit`), OR
|
||||
* - it generated close to the cap (a complete answer wouldn't dangle that early), OR
|
||||
* - the visible answer plainly ends mid-sentence and the engine didn't give a clean "done" reason.
|
||||
* Never continues from a too-short fragment, and never from a clean ending (terminal punctuation).
|
||||
*/
|
||||
export function shouldAutoContinue(
|
||||
stopKind: GenerationStopKind,
|
||||
@@ -134,10 +162,14 @@ export function shouldAutoContinue(
|
||||
outputTokens: number,
|
||||
maxOutputTokens: number
|
||||
): boolean {
|
||||
if (stopKind !== 'output-limit') return false;
|
||||
if (!visibleAnswer || visibleAnswer.trim().length < 40) return false;
|
||||
if (!Number.isFinite(maxOutputTokens) || maxOutputTokens <= 0) return true;
|
||||
return outputTokens >= Math.floor(maxOutputTokens * 0.8);
|
||||
const v = (visibleAnswer || '').trim();
|
||||
if (v.length < 24) return false;
|
||||
// These won't be fixed by generating more text — don't auto-continue.
|
||||
if (stopKind === 'user-stopped' || stopKind === 'context-overflow' || stopKind === 'error' || stopKind === 'tool-calls') return false;
|
||||
if (stopKind === 'output-limit') return true;
|
||||
if (Number.isFinite(maxOutputTokens) && maxOutputTokens > 0 && outputTokens >= Math.floor(maxOutputTokens * 0.85)) return true;
|
||||
// 'complete' (eosFound) or 'unknown' but the text is plainly unfinished → continue.
|
||||
return looksCutOff(v);
|
||||
}
|
||||
|
||||
/** Appended to the system prompt for a final-only retry — the previous reply was reasoning-only. */
|
||||
|
||||
@@ -128,7 +128,9 @@ export class LMStudioStreamer implements IChatStreamer {
|
||||
logInfo('LM Studio SDK chat stream finished.', { model: trimmedModel, stopReason, tokensYielded: yielded });
|
||||
}
|
||||
} catch { /* result unavailable on some SDK versions — non-fatal */ }
|
||||
yield { token: '', stopReason: stopReason ?? 'eosFound' };
|
||||
// Don't claim `eosFound` if we couldn't actually read the stop reason — leave it
|
||||
// undefined so the caller treats it as 'unknown' (and its mid-sentence heuristics kick in).
|
||||
yield { token: '', stopReason };
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
+16
-7
@@ -1789,13 +1789,21 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
this._currentSessionBrainId = selectedBrainId;
|
||||
|
||||
let agentSkillContext = undefined;
|
||||
if (agentFile && fs.existsSync(agentFile)) {
|
||||
agentSkillContext = fs.readFileSync(agentFile, 'utf8');
|
||||
|
||||
// Merge in any external skill .md files the user has mapped to this
|
||||
// agent. We concatenate into the same agentSkillContext blob so the
|
||||
// rest of the pipeline (agent.ts, agent-mode override) treats them
|
||||
// identically to the agent's own .md — no further changes needed.
|
||||
if (agentFile && agentFile !== 'none' && fs.existsSync(agentFile)) {
|
||||
const fileContent = fs.readFileSync(agentFile, 'utf8');
|
||||
// Guard: a freshly-created agent still has only the placeholder template
|
||||
// ("# Agent Persona: …\n\nAdd your instructions here…"). Treating that as a real
|
||||
// agent prompt just confuses the model — fall back to normal mode and tell the user.
|
||||
const body = fileContent.replace(/^?#\s*Agent\s*Persona\s*:.*$/im, '').trim();
|
||||
const isPlaceholder = !body || /^add your instructions here/i.test(body);
|
||||
if (isPlaceholder) {
|
||||
logInfo('Selected agent has no real instructions — running without agent mode.', { agentFile });
|
||||
this._view?.webview.postMessage({ type: 'lmStudioError', value: '선택한 에이전트에 내용이 없습니다 — 에이전트 프롬프트를 작성한 뒤 다시 시도하세요. (이번 응답은 에이전트 없이 처리합니다)' });
|
||||
} else {
|
||||
agentSkillContext = fileContent;
|
||||
// Merge in any external skill .md files the user has mapped to this agent. We concatenate
|
||||
// into the same agentSkillContext blob so the rest of the pipeline (agent.ts, agent-mode
|
||||
// override) treats them identically to the agent's own .md — no further changes needed.
|
||||
try {
|
||||
const entry = getOrCreateAgentEntry(agentFile);
|
||||
const bundle = loadExternalSkills(entry.skillFolders);
|
||||
@@ -1807,6 +1815,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
logError('External skill load failed.', { error: e?.message || String(e) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const designerContext = designerGuard !== false ? this._buildDesignerGuardContext() : undefined;
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ import {
|
||||
extractVisibleFinal,
|
||||
shouldFinalOnlyRetry,
|
||||
shouldAutoContinue,
|
||||
looksCutOff,
|
||||
mergeContinuationParts,
|
||||
buildContinuationUserPrompt,
|
||||
} from '../src/core/responseRecovery';
|
||||
@@ -74,14 +75,46 @@ describe('responseRecovery.extractVisibleFinal — thought quarantine', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('responseRecovery.looksCutOff', () => {
|
||||
it('flags answers that plainly end mid-sentence / mid-structure', () => {
|
||||
expect(looksCutOff('당신은 복잡한 아이디어나 목표를 구체적인 실행 계획과 체계적인 문서화로')).toBe(true); // ends with the particle "로"
|
||||
expect(looksCutOff('우리는 이 문제를 해결하기 위해 다음과 같은 단계를')).toBe(true); // ends with object marker "를"
|
||||
expect(looksCutOff('the implementation is not yet complete and we need to')).toBe(true); // mid-English
|
||||
expect(looksCutOff('the items are: foo, bar,')).toBe(true); // trailing comma
|
||||
expect(looksCutOff('here is the code:\n```python\nprint("hi")')).toBe(true); // unclosed fence
|
||||
expect(looksCutOff('정리하면 다음 항목들이 중요합니다:\n- 첫 번째 항목\n- 두 번째 항목\n- ')).toBe(true); // dangling bullet
|
||||
});
|
||||
it('does NOT flag complete-looking answers', () => {
|
||||
expect(looksCutOff('이것은 완전히 끝난 답변이고 마침표도 붙어 있습니다.')).toBe(false);
|
||||
expect(looksCutOff('이것은 마침표 없이 끝나는 한국어 문장입니다')).toBe(false); // ends with "다" — valid
|
||||
expect(looksCutOff('네, 그렇게 하면 됩니다')).toBe(false);
|
||||
expect(looksCutOff('done.')).toBe(false);
|
||||
expect(looksCutOff('짧음')).toBe(false); // too short to judge
|
||||
});
|
||||
});
|
||||
|
||||
describe('responseRecovery.shouldAutoContinue', () => {
|
||||
it('continues only when output-limit AND a real visible answer AND near the cap', () => {
|
||||
it('continues when the engine reports the output cap was hit', () => {
|
||||
expect(shouldAutoContinue('output-limit', 'x'.repeat(200), 3500, 4096)).toBe(true);
|
||||
expect(shouldAutoContinue('output-limit', 'short', 4000, 4096)).toBe(false); // no real answer
|
||||
expect(shouldAutoContinue('output-limit', 'x'.repeat(200), 100, 4096)).toBe(false); // didn't actually hit the cap
|
||||
expect(shouldAutoContinue('complete', 'x'.repeat(200), 4000, 4096)).toBe(false);
|
||||
expect(shouldAutoContinue('output-limit', 'x'.repeat(200), 50, 4096)).toBe(true); // engine said so → trust it
|
||||
});
|
||||
it('continues when generation reached ~the cap even if the engine said "complete"', () => {
|
||||
expect(shouldAutoContinue('complete', 'x'.repeat(200), 4000, 4096)).toBe(true);
|
||||
});
|
||||
it('continues when the answer plainly ends mid-sentence (engine reason unclear)', () => {
|
||||
expect(shouldAutoContinue('unknown', '당신은 복잡한 아이디어나 목표를 구체적인 실행 계획과 체계적인 문서화로', 60, 4096)).toBe(true);
|
||||
expect(shouldAutoContinue('complete', 'the implementation continues here and we still need to', 100, 4096)).toBe(true);
|
||||
});
|
||||
it('does NOT continue from a tiny fragment or a complete-looking answer', () => {
|
||||
expect(shouldAutoContinue('output-limit', 'short', 4000, 4096)).toBe(false); // < 24 chars
|
||||
expect(shouldAutoContinue('complete', '이것은 완전히 끝난 답변이고 마침표도 붙어 있습니다.', 100, 4096)).toBe(false);
|
||||
expect(shouldAutoContinue('unknown', '이것은 마침표 없이 끝나는 한국어 문장이고 충분히 길다고 본다', 100, 4096)).toBe(false);
|
||||
});
|
||||
it('does NOT continue on stop reasons that more text cannot fix', () => {
|
||||
expect(shouldAutoContinue('context-overflow', 'x'.repeat(200), 4000, 4096)).toBe(false);
|
||||
expect(shouldAutoContinue('error', 'x'.repeat(200), 4000, 4096)).toBe(false);
|
||||
expect(shouldAutoContinue('user-stopped', 'x'.repeat(200), 4000, 4096)).toBe(false);
|
||||
expect(shouldAutoContinue('tool-calls', 'x'.repeat(200), 4000, 4096)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user