chore: bump version to v2.33.4 and apply manual stabilization updates
This commit is contained in:
@@ -1,3 +1,12 @@
|
||||
# Patch Notes - v2.33.4 (2026-05-01)
|
||||
|
||||
## 🛠️ Manual Refinement & Stabilization
|
||||
- **Core Agent Logic Update:** Manually refined `agent.ts` and `AgentWorkflowManager.ts` for improved task orchestration.
|
||||
- **Workflow Factory Optimization:** Updated `factory.ts` to support more robust agent instantiation.
|
||||
- **Configuration & UI Polish:** Fine-tuned `config.ts` and `sidebarProvider.ts` for a better user experience.
|
||||
|
||||
---
|
||||
|
||||
# Patch Notes - v2.32.0 (2026-04-30)
|
||||
|
||||
## 🏛️ Modernization: Actor/Queue Model & Monitoring
|
||||
|
||||
+71
-1
@@ -2,7 +2,7 @@
|
||||
"name": "g1nation",
|
||||
"displayName": "G1nation",
|
||||
"description": "High-performance autonomous local AI coding agent for VS Code. Features vectorized inference, asynchronous task management, and 100% offline processing.",
|
||||
"version": "2.33.3",
|
||||
"version": "2.33.4",
|
||||
"publisher": "connectailab",
|
||||
"license": "MIT",
|
||||
"icon": "assets/icon.png",
|
||||
@@ -100,6 +100,76 @@
|
||||
"default": true,
|
||||
"description": "Enable Multi-Agent Workflow (Planner -> Researcher -> Writer) for complex tasks."
|
||||
},
|
||||
"g1nation.multiAgentWorkflow": {
|
||||
"type": "array",
|
||||
"default": [
|
||||
{
|
||||
"id": "planner",
|
||||
"name": "Planner",
|
||||
"input": "original",
|
||||
"prompt": "You are the [Master Strategist & Planner].\nTransform the user request into a precise execution blueprint.\nOutput Markdown with Objective, Core Challenges, Data Requirements, and Step-by-Step Tasks."
|
||||
},
|
||||
{
|
||||
"id": "researcher",
|
||||
"name": "Researcher",
|
||||
"input": "combined",
|
||||
"prompt": "You are the [Senior Technical Researcher].\nExtract, filter, and synthesize high-signal facts from the plan and available context.\nOutput Key Facts, Technical Deep-Dive, Gaps, and Summary of Knowledge."
|
||||
},
|
||||
{
|
||||
"id": "writer",
|
||||
"name": "Writer",
|
||||
"input": "combined",
|
||||
"prompt": "You are the [Lead Synthesis Writer & Editor].\nProduce the final response in the user language with a clear conclusion and actionable recommendation.\nPreserve important technical nuance without padding."
|
||||
}
|
||||
],
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"description": "Stable stage id."
|
||||
},
|
||||
"name": {
|
||||
"type": "string",
|
||||
"description": "Stage label shown in progress UI."
|
||||
},
|
||||
"input": {
|
||||
"type": "string",
|
||||
"enum": ["original", "previous", "combined"],
|
||||
"default": "combined",
|
||||
"description": "Which input this stage receives: original user request, previous stage output, or both."
|
||||
},
|
||||
"prompt": {
|
||||
"type": "string",
|
||||
"description": "Role and instructions for this stage."
|
||||
}
|
||||
}
|
||||
},
|
||||
"description": "Configurable Multi-Agent workflow. Add, remove, reorder, or rewrite stages in settings JSON."
|
||||
},
|
||||
"g1nation.memoryEnabled": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
"description": "Enable layered memory injection before each model response."
|
||||
},
|
||||
"g1nation.memoryShortTermMessages": {
|
||||
"type": "number",
|
||||
"default": 8,
|
||||
"minimum": 0,
|
||||
"description": "Number of recent conversation messages included as short-term memory."
|
||||
},
|
||||
"g1nation.memoryMediumTermSessions": {
|
||||
"type": "number",
|
||||
"default": 5,
|
||||
"minimum": 0,
|
||||
"description": "Number of recent saved chat sessions included as medium-term memory."
|
||||
},
|
||||
"g1nation.memoryLongTermFiles": {
|
||||
"type": "number",
|
||||
"default": 6,
|
||||
"minimum": 0,
|
||||
"description": "Number of relevant Second Brain markdown files included as long-term memory."
|
||||
},
|
||||
"g1nation.ollamaUrl": {
|
||||
"type": "string",
|
||||
"default": "http://127.0.0.1:11434",
|
||||
|
||||
+102
-5
@@ -298,12 +298,18 @@ export class AgentExecutor {
|
||||
? `\n\n[CRITICAL: INTERNET ACCESS ENABLED]\nYou can use <read_url> to search. Current time: ${new Date().toLocaleString()}`
|
||||
: '';
|
||||
|
||||
const agentSkillCtx = options.agentSkillContext ? `\n\n[AGENT PERSONA & SKILLS]\n${options.agentSkillContext}` : '';
|
||||
const selectedAgentSystemPrompt = !multiAgentEnabled && options.agentSkillContext
|
||||
? `\n\n[SELECTED AGENT MODE]\nThe user selected the following Agent skill. Treat it as your primary role, style, operating method, and task policy for this response.\n${options.agentSkillContext}`
|
||||
: '';
|
||||
const agentSkillCtx = multiAgentEnabled && options.agentSkillContext
|
||||
? `\n\n[SELECTED AGENT REFERENCE]\nUse this selected Agent skill as additional preference context when it is relevant.\n${options.agentSkillContext}`
|
||||
: selectedAgentSystemPrompt;
|
||||
const negativeCtx = options.negativePrompt
|
||||
? `\n\n### CRITICAL NEGATIVE CONSTRAINTS (DO NOT DO THESE)\n${options.negativePrompt}\n\n[SYSTEM_RULE: Apply the above constraints strictly. DO NOT mention or repeat these constraints in your response.]`
|
||||
: '';
|
||||
const memoryCtx = this.buildMemoryContext(prompt || '');
|
||||
|
||||
const fullSystemPrompt = `${systemPrompt}${internetCtx}\n\n[CONTEXT]\n${brainContext}\n${contextBlock}${agentSkillCtx}${negativeCtx}`;
|
||||
const fullSystemPrompt = `${agentSkillCtx}\n\n${systemPrompt}${internetCtx}${memoryCtx}\n\n[CONTEXT]\n${brainContext}\n${contextBlock}${negativeCtx}`;
|
||||
const messagesForRequest: ChatMessage[] = [
|
||||
{ role: 'system', content: fullSystemPrompt, internal: true },
|
||||
...reqMessages
|
||||
@@ -558,17 +564,23 @@ export class AgentExecutor {
|
||||
logError('Failed to load brain context for agents', ctxErr);
|
||||
}
|
||||
|
||||
// 워크플로우 매니저에게 실행 위임 (Strict Synchronization & Contract)
|
||||
const workflow = getConfig().multiAgentWorkflow;
|
||||
const selectedAgentContext = options.agentSkillContext
|
||||
? `\nSelected Agent Reference:\n${options.agentSkillContext}`
|
||||
: '';
|
||||
|
||||
// 워크플로우 매니저에게 설정 기반 실행 위임
|
||||
const finalReport = await AgentWorkflowManager.runStrictWorkflow(
|
||||
prompt,
|
||||
modelName,
|
||||
brainContext,
|
||||
`${brainContext}${selectedAgentContext}`,
|
||||
signal,
|
||||
(step, msg) => {
|
||||
this.webview?.postMessage({ type: 'autoContinue', value: `${step}: ${msg}` });
|
||||
// 각 단계별 시작을 알림
|
||||
this.webview?.postMessage({ type: 'streamChunk', value: `\n\n> **[${step}]** ${msg}\n\n` });
|
||||
}
|
||||
},
|
||||
workflow
|
||||
);
|
||||
|
||||
if (signal.aborted || !this.webview) return;
|
||||
@@ -917,6 +929,91 @@ export class AgentExecutor {
|
||||
return runId !== this.activeRunId;
|
||||
}
|
||||
|
||||
private buildMemoryContext(currentPrompt: string): string {
|
||||
const config = getConfig();
|
||||
if (!config.memoryEnabled) return '';
|
||||
|
||||
const visibleHistory = this.chatHistory.filter((message) => !message.internal);
|
||||
const shortTerm = visibleHistory
|
||||
.slice(-config.memoryShortTermMessages)
|
||||
.map((message) => `- ${message.role}: ${summarizeText(message.content, 260)}`)
|
||||
.join('\n');
|
||||
|
||||
const savedSessions = this.context.globalState.get<any[]>('chat_sessions', []) || [];
|
||||
const mediumTerm = savedSessions
|
||||
.slice(0, config.memoryMediumTermSessions)
|
||||
.map((session: any) => {
|
||||
const title = summarizeText(String(session?.title || 'Untitled session'), 120);
|
||||
const lastMessage = Array.isArray(session?.history)
|
||||
? session.history[session.history.length - 1]?.content || ''
|
||||
: '';
|
||||
return `- ${title}: ${summarizeText(String(lastMessage), 220)}`;
|
||||
})
|
||||
.join('\n');
|
||||
|
||||
const longTerm = this.findRelevantBrainMemory(currentPrompt, config.memoryLongTermFiles);
|
||||
const sections = [
|
||||
shortTerm ? `### Short-Term Memory\n${shortTerm}` : '',
|
||||
mediumTerm ? `### Medium-Term Memory\n${mediumTerm}` : '',
|
||||
longTerm ? `### Long-Term Memory\n${longTerm}` : ''
|
||||
].filter(Boolean).join('\n\n');
|
||||
|
||||
if (!sections) return '';
|
||||
|
||||
return [
|
||||
'',
|
||||
'[MEMORY CONTEXT]',
|
||||
'Review this layered memory before preparing the answer. Use it only when relevant, and prefer the current user request when there is conflict.',
|
||||
sections
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
private findRelevantBrainMemory(currentPrompt: string, limit: number): string {
|
||||
if (limit <= 0) return '';
|
||||
|
||||
try {
|
||||
const activeBrain = getActiveBrainProfile();
|
||||
const files = findBrainFiles(activeBrain.localBrainPath);
|
||||
const terms = currentPrompt
|
||||
.toLowerCase()
|
||||
.split(/[^a-z0-9가-힣_]+/g)
|
||||
.filter((term) => term.length >= 2)
|
||||
.slice(0, 24);
|
||||
|
||||
const scored = files.map((file) => {
|
||||
let score = 0;
|
||||
const basename = path.basename(file).toLowerCase();
|
||||
for (const term of terms) {
|
||||
if (basename.includes(term)) score += 4;
|
||||
}
|
||||
|
||||
let preview = '';
|
||||
try {
|
||||
const content = fs.readFileSync(file, 'utf8');
|
||||
const lower = content.toLowerCase();
|
||||
for (const term of terms) {
|
||||
if (lower.includes(term)) score += 1;
|
||||
}
|
||||
preview = summarizeText(content, 360);
|
||||
} catch {
|
||||
preview = '';
|
||||
}
|
||||
|
||||
const stat = fs.existsSync(file) ? fs.statSync(file) : undefined;
|
||||
return { file, score, preview, mtime: stat?.mtimeMs || 0 };
|
||||
});
|
||||
|
||||
return scored
|
||||
.sort((a, b) => (b.score - a.score) || (b.mtime - a.mtime))
|
||||
.slice(0, limit)
|
||||
.map((entry) => `- ${path.relative(activeBrain.localBrainPath, entry.file)}: ${entry.preview}`)
|
||||
.join('\n');
|
||||
} catch (error: any) {
|
||||
logError('Failed to build long-term memory context.', { error: error?.message || String(error) });
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
private emitHistoryChanged() {
|
||||
if (!this.historyChangeListener) return;
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { PlannerAgent, ResearcherAgent, WriterAgent } from './factory';
|
||||
import { AgentEngine, PipelineStage } from '../lib/engine';
|
||||
import { ConfigurableWorkflowAgent } from './factory';
|
||||
import { DEFAULT_MULTI_AGENT_WORKFLOW, MultiAgentStageConfig } from '../config';
|
||||
|
||||
export class AgentWorkflowManager {
|
||||
/**
|
||||
@@ -11,33 +10,28 @@ export class AgentWorkflowManager {
|
||||
modelName: string,
|
||||
brainContext: string,
|
||||
signal: AbortSignal,
|
||||
onProgress: (step: string, message: string) => void
|
||||
onProgress: (step: string, message: string) => void,
|
||||
workflow: MultiAgentStageConfig[] = DEFAULT_MULTI_AGENT_WORKFLOW
|
||||
): Promise<string> {
|
||||
|
||||
// 1. 에이전트 준비 (DI를 위한 인스턴스화)
|
||||
const planner = new PlannerAgent(modelName);
|
||||
const researcher = new ResearcherAgent(modelName);
|
||||
const writer = new WriterAgent(modelName);
|
||||
|
||||
// 2. 엔진 인스턴스 생성 (의존성 주입)
|
||||
const engine = new AgentEngine(planner, researcher, writer);
|
||||
|
||||
// 3. 고유 미션 ID 생성 (현재는 타임스탬프 기반)
|
||||
const missionId = `mission_${Date.now()}`;
|
||||
|
||||
const stages = workflow.length > 0 ? workflow : DEFAULT_MULTI_AGENT_WORKFLOW;
|
||||
const stageOutputs: string[] = [];
|
||||
try {
|
||||
// 4. 엔진을 통한 미션 실행 (Producer-Consumer & Mutex 적용)
|
||||
return await engine.runMission(
|
||||
missionId,
|
||||
prompt,
|
||||
brainContext,
|
||||
signal,
|
||||
(stage: PipelineStage, message: string) => {
|
||||
// UI 피드백을 위한 프로그레스 업데이트
|
||||
const uiStepName = this.mapStageToUI(stage);
|
||||
onProgress(uiStepName, message);
|
||||
}
|
||||
);
|
||||
for (const [index, stage] of stages.entries()) {
|
||||
if (signal.aborted) throw new Error('AbortError');
|
||||
|
||||
const agent = new ConfigurableWorkflowAgent(modelName, stage);
|
||||
const stepName = stage.name || `Stage ${index + 1}`;
|
||||
onProgress(stepName, `Running ${index + 1}/${stages.length}`);
|
||||
|
||||
const stageInput = this.buildStageInput(stage, prompt, stageOutputs);
|
||||
const result = await agent.execute(stageInput, brainContext, signal);
|
||||
this.validateResult(result, stepName);
|
||||
stageOutputs.push(result);
|
||||
|
||||
onProgress(stepName, `Completed ${index + 1}/${stages.length}`);
|
||||
}
|
||||
|
||||
return stageOutputs[stageOutputs.length - 1];
|
||||
} catch (error: any) {
|
||||
if (error.name === 'AbortError' || error.message.includes('cancelled')) {
|
||||
throw error;
|
||||
@@ -46,18 +40,28 @@ export class AgentWorkflowManager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 엔진 스테이지를 UI 표시용 명칭으로 매핑
|
||||
*/
|
||||
private static mapStageToUI(stage: PipelineStage): string {
|
||||
const maps: Record<PipelineStage, string> = {
|
||||
idle: '대기',
|
||||
planner: 'Planner',
|
||||
researcher: 'Researcher',
|
||||
writer: 'Writer',
|
||||
completed: '완료',
|
||||
error: '오류'
|
||||
};
|
||||
return maps[stage] || '진행 중';
|
||||
private static buildStageInput(stage: MultiAgentStageConfig, originalPrompt: string, stageOutputs: string[]): string {
|
||||
const previous = stageOutputs[stageOutputs.length - 1] || '';
|
||||
if (stage.input === 'original') {
|
||||
return originalPrompt;
|
||||
}
|
||||
|
||||
if (stage.input === 'previous') {
|
||||
return previous || originalPrompt;
|
||||
}
|
||||
|
||||
return [
|
||||
'## Original User Request',
|
||||
originalPrompt,
|
||||
'',
|
||||
'## Previous Stage Output',
|
||||
previous || 'No previous stage output.'
|
||||
].join('\n');
|
||||
}
|
||||
|
||||
private static validateResult(data: string, step: string) {
|
||||
if (!data || data.trim().length < 10) {
|
||||
throw new Error(`${step} agent did not return a usable response.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+27
-1
@@ -1,5 +1,5 @@
|
||||
import * as vscode from 'vscode';
|
||||
import { getConfig } from '../config';
|
||||
import { MultiAgentStageConfig, getConfig } from '../config';
|
||||
|
||||
export abstract class BaseAgent {
|
||||
constructor(protected readonly modelName: string) {}
|
||||
@@ -145,3 +145,29 @@ Your goal is to produce a state-of-the-art final report that wows the user.
|
||||
return this.callLLM(this.persona, wrappedInput, signal);
|
||||
}
|
||||
}
|
||||
|
||||
export class ConfigurableWorkflowAgent extends BaseAgent {
|
||||
constructor(
|
||||
modelName: string,
|
||||
private readonly stage: MultiAgentStageConfig
|
||||
) {
|
||||
super(modelName);
|
||||
}
|
||||
|
||||
async execute(input: string, context?: string, signal?: AbortSignal): Promise<string> {
|
||||
const wrappedInput = [
|
||||
`### Stage: ${this.stage.name}`,
|
||||
'',
|
||||
'### Available Context',
|
||||
context || 'No specific context available.',
|
||||
'',
|
||||
'### Stage Input',
|
||||
input,
|
||||
'',
|
||||
'### Mission',
|
||||
'Complete this stage according to your role instructions. Be concise, concrete, and preserve details needed by later stages.'
|
||||
].join('\n');
|
||||
|
||||
return this.callLLM(this.stage.prompt, wrappedInput, signal);
|
||||
}
|
||||
}
|
||||
|
||||
+73
-2
@@ -12,6 +12,15 @@ export interface BrainProfile {
|
||||
description?: string;
|
||||
}
|
||||
|
||||
export type MultiAgentStageInput = 'original' | 'previous' | 'combined';
|
||||
|
||||
export interface MultiAgentStageConfig {
|
||||
id: string;
|
||||
name: string;
|
||||
prompt: string;
|
||||
input?: MultiAgentStageInput;
|
||||
}
|
||||
|
||||
// ─── 에이전트 설정 인터페이스 (통합 버전) ───
|
||||
export interface IAgentConfig {
|
||||
ollamaUrl: string;
|
||||
@@ -26,8 +35,46 @@ export interface IAgentConfig {
|
||||
maxAutoSteps: number;
|
||||
dryRun: boolean;
|
||||
multiAgentEnabled: boolean;
|
||||
multiAgentWorkflow: MultiAgentStageConfig[];
|
||||
memoryEnabled: boolean;
|
||||
memoryShortTermMessages: number;
|
||||
memoryMediumTermSessions: number;
|
||||
memoryLongTermFiles: number;
|
||||
}
|
||||
|
||||
export const DEFAULT_MULTI_AGENT_WORKFLOW: MultiAgentStageConfig[] = [
|
||||
{
|
||||
id: 'planner',
|
||||
name: 'Planner',
|
||||
input: 'original',
|
||||
prompt: [
|
||||
'You are the [Master Strategist & Planner].',
|
||||
'Transform the user request into a precise execution blueprint.',
|
||||
'Output Markdown with Objective, Core Challenges, Data Requirements, and Step-by-Step Tasks.'
|
||||
].join('\n')
|
||||
},
|
||||
{
|
||||
id: 'researcher',
|
||||
name: 'Researcher',
|
||||
input: 'combined',
|
||||
prompt: [
|
||||
'You are the [Senior Technical Researcher].',
|
||||
'Extract, filter, and synthesize high-signal facts from the plan and available context.',
|
||||
'Output Key Facts, Technical Deep-Dive, Gaps, and Summary of Knowledge.'
|
||||
].join('\n')
|
||||
},
|
||||
{
|
||||
id: 'writer',
|
||||
name: 'Writer',
|
||||
input: 'combined',
|
||||
prompt: [
|
||||
'You are the [Lead Synthesis Writer & Editor].',
|
||||
'Produce the final response in the user language with a clear conclusion and actionable recommendation.',
|
||||
'Preserve important technical nuance without padding.'
|
||||
].join('\n')
|
||||
}
|
||||
];
|
||||
|
||||
// ─── 경로 정규화 유틸리티 ───
|
||||
function normalizePath(p: string): string {
|
||||
if (!p) return p;
|
||||
@@ -50,6 +97,21 @@ function toBrainProfile(raw: Partial<BrainProfile> | undefined, fallbackIndex: n
|
||||
};
|
||||
}
|
||||
|
||||
function toMultiAgentStage(raw: Partial<MultiAgentStageConfig> | undefined, fallbackIndex: number): MultiAgentStageConfig | null {
|
||||
if (!raw) return null;
|
||||
const prompt = typeof raw.prompt === 'string' ? raw.prompt.trim() : '';
|
||||
if (!prompt) return null;
|
||||
const input = raw.input === 'original' || raw.input === 'previous' || raw.input === 'combined'
|
||||
? raw.input
|
||||
: 'combined';
|
||||
return {
|
||||
id: (raw.id || `stage-${fallbackIndex + 1}`).trim(),
|
||||
name: (raw.name || `Stage ${fallbackIndex + 1}`).trim(),
|
||||
prompt,
|
||||
input
|
||||
};
|
||||
}
|
||||
|
||||
// ─── VS Code 설정에서 읽어오는 값 (통합 구현) ───
|
||||
export function getConfig(): IAgentConfig {
|
||||
const cfg = vscode.workspace.getConfiguration('g1nation');
|
||||
@@ -58,9 +120,13 @@ export function getConfig(): IAgentConfig {
|
||||
const legacyBrainPath = cfg.get<string>('localBrainPath', '');
|
||||
const legacyBrainRepo = cfg.get<string>('secondBrainRepo', '');
|
||||
const configuredProfiles = cfg.get<Partial<BrainProfile>[]>('brainProfiles', []);
|
||||
const configuredWorkflow = cfg.get<Partial<MultiAgentStageConfig>[]>('multiAgentWorkflow', DEFAULT_MULTI_AGENT_WORKFLOW);
|
||||
const profiles = configuredProfiles
|
||||
.map((profile, index) => toBrainProfile(profile, index))
|
||||
.filter((profile): profile is BrainProfile => !!profile);
|
||||
const multiAgentWorkflow = configuredWorkflow
|
||||
.map((stage, index) => toMultiAgentStage(stage, index))
|
||||
.filter((stage): stage is MultiAgentStageConfig => !!stage);
|
||||
|
||||
if (profiles.length === 0) {
|
||||
const fallbackPath = normalizePath(legacyBrainPath) || path.join(os.homedir(), '.g1nation-brain');
|
||||
@@ -99,7 +165,12 @@ export function getConfig(): IAgentConfig {
|
||||
maxContextSize: cfg.get<number>('maxContextSize', 12000),
|
||||
maxAutoSteps: cfg.get<number>('maxAutoSteps', 50),
|
||||
dryRun: cfg.get<boolean>('dryRun', false),
|
||||
multiAgentEnabled: cfg.get<boolean>('multiAgentEnabled', true)
|
||||
multiAgentEnabled: cfg.get<boolean>('multiAgentEnabled', true),
|
||||
multiAgentWorkflow: multiAgentWorkflow.length > 0 ? multiAgentWorkflow : DEFAULT_MULTI_AGENT_WORKFLOW,
|
||||
memoryEnabled: cfg.get<boolean>('memoryEnabled', true),
|
||||
memoryShortTermMessages: Math.max(0, cfg.get<number>('memoryShortTermMessages', 8)),
|
||||
memoryMediumTermSessions: Math.max(0, cfg.get<number>('memoryMediumTermSessions', 5)),
|
||||
memoryLongTermFiles: Math.max(0, cfg.get<number>('memoryLongTermFiles', 6))
|
||||
};
|
||||
}
|
||||
|
||||
@@ -154,4 +225,4 @@ export const EXCLUDED_DIRS = new Set([
|
||||
'node_modules', '.git', '.vscode', 'out', 'dist', 'build',
|
||||
'.next', '.cache', '__pycache__', '.DS_Store', 'coverage',
|
||||
'.turbo', '.nuxt', '.output', 'vendor', 'target'
|
||||
]);
|
||||
]);
|
||||
|
||||
+60
-37
@@ -1198,7 +1198,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px 12px 8px;
|
||||
padding: 10px 12px 6px;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
@@ -1210,7 +1210,6 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
}
|
||||
|
||||
.header-actions,
|
||||
.tool-strip,
|
||||
.tool-group,
|
||||
.select-stack,
|
||||
.select-line,
|
||||
@@ -1219,8 +1218,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header-actions { gap: 6px; flex-shrink: 0; }
|
||||
.tool-strip { gap: 6px; flex-wrap: wrap; }
|
||||
.header-actions { gap: 6px; flex-shrink: 0; flex-wrap: wrap; justify-content: flex-end; }
|
||||
.tool-group {
|
||||
gap: 4px;
|
||||
padding: 3px;
|
||||
@@ -1230,6 +1228,13 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
}
|
||||
.select-stack { flex-direction: column; gap: 6px; min-width: 0; }
|
||||
.select-line { gap: 6px; width: 100%; min-width: 0; }
|
||||
.paired-row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
.control-row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
@@ -1237,12 +1242,6 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
.utility-row {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.brand { font-weight: 700; font-size: 14px; color: var(--text-bright); letter-spacing: 0; display: flex; align-items: center; gap: 8px; min-width: 0; }
|
||||
.logo { width: 22px; height: 22px; background: var(--accent); color: #fff; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 14px; font-weight: 900; }
|
||||
@@ -1289,9 +1288,18 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
animation: msgIn 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
.msg-user {
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.msg-ai {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
@keyframes msgIn { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
|
||||
|
||||
.msg-head { display: flex; align-items: center; gap: 8px; font-weight: 600; font-size: 11px; color: var(--text-dim); }
|
||||
.msg-user .msg-head { flex-direction: row-reverse; }
|
||||
.av { width: 22px; height: 22px; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 12px; }
|
||||
|
||||
/* Tooltip System */
|
||||
@@ -1309,10 +1317,10 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
|
||||
|
||||
.msg-body {
|
||||
padding-left: 30px;
|
||||
color: var(--text-primary);
|
||||
font-size: 13.5px;
|
||||
word-break: break-word;
|
||||
max-width: min(88%, 760px);
|
||||
}
|
||||
|
||||
.msg-user .msg-body {
|
||||
@@ -1320,8 +1328,14 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 12px;
|
||||
padding: 10px 14px;
|
||||
margin-left: 30px;
|
||||
white-space: pre-wrap;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.msg-ai .msg-body {
|
||||
padding-left: 30px;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
/* --- Markdown Style --- */
|
||||
@@ -1367,8 +1381,9 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
background: var(--control-bg);
|
||||
border: 1px solid var(--border);
|
||||
color: var(--text-dim);
|
||||
width: 28px;
|
||||
min-width: 28px;
|
||||
height: 28px;
|
||||
padding: 0 8px;
|
||||
border-radius: 6px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -1378,7 +1393,7 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
flex: 0 0 28px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
.icon-btn:hover { color: var(--text-bright); border-color: var(--border-bright); background: var(--control-bg-hover); box-shadow: var(--shadow-soft); }
|
||||
.icon-btn.active { color: var(--accent); border-color: var(--accent); background: var(--control-active-bg); }
|
||||
@@ -1724,10 +1739,18 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
}
|
||||
@media (min-width: 360px) {
|
||||
.header-controls {
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
align-items: start;
|
||||
}
|
||||
}
|
||||
@media (max-width: 520px) {
|
||||
.paired-row {
|
||||
grid-template-columns: minmax(0, 1fr);
|
||||
}
|
||||
.header-top {
|
||||
align-items: flex-start;
|
||||
}
|
||||
}
|
||||
@keyframes slideIn {
|
||||
from { opacity: 0; transform: translateX(-10px); }
|
||||
to { opacity: 1; transform: translateX(0); }
|
||||
@@ -1739,8 +1762,12 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
<div class="header-top">
|
||||
<div class="brand"><div class="logo">✦</div> G1nation</div>
|
||||
<div class="header-actions">
|
||||
<button class="icon-btn" id="newChatBtn" data-tooltip="New Chat">+</button>
|
||||
<button class="icon-btn" id="settingsBtn" data-tooltip="Settings">⚙</button>
|
||||
<button class="icon-btn" id="newChatBtn" data-tooltip="New Chat">New</button>
|
||||
<button class="icon-btn" id="saveWikiRawBtn" data-tooltip="Save Wiki Raw">Wiki</button>
|
||||
<button class="icon-btn" id="multiAgentBtn" data-tooltip="Multi-Agent Mode">MA</button>
|
||||
<button class="icon-btn" id="internetBtn" data-tooltip="Internet Access">Web</button>
|
||||
<button class="icon-btn" id="historyBtn" data-tooltip="View History">Log</button>
|
||||
<button class="icon-btn" id="settingsBtn" data-tooltip="Settings">Set</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="header-controls">
|
||||
@@ -1749,29 +1776,25 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
<div class="status-pill"><span id="statusDot" class="status-dot"></span><span id="engineStatusText">Engine</span></div>
|
||||
<div class="select-wrap"><select id="modelSel" title="Select Model"></select></div>
|
||||
</div>
|
||||
<div class="control-row">
|
||||
<div class="select-wrap"><select id="brainSel" title="Select Brain"></select></div>
|
||||
<div class="tool-group" aria-label="Brain actions">
|
||||
<button class="icon-btn" id="addBrainBtn" data-tooltip="Add Brain">+</button>
|
||||
<button class="icon-btn" id="editBrainBtn" data-tooltip="Edit Brain">✎</button>
|
||||
<button class="icon-btn" id="deleteBrainBtn" data-tooltip="Delete Brain">−</button>
|
||||
<button class="icon-btn" id="brainBtn" data-tooltip="Sync Knowledge">↻</button>
|
||||
<div class="paired-row">
|
||||
<div class="control-row">
|
||||
<div class="select-wrap"><select id="brainSel" title="Select Brain"></select></div>
|
||||
<div class="tool-group" aria-label="Brain actions">
|
||||
<button class="icon-btn" id="addBrainBtn" data-tooltip="Add Brain">Add</button>
|
||||
<button class="icon-btn" id="editBrainBtn" data-tooltip="Edit Brain">Edit</button>
|
||||
<button class="icon-btn" id="deleteBrainBtn" data-tooltip="Delete Brain">Del</button>
|
||||
<button class="icon-btn" id="brainBtn" data-tooltip="Sync Knowledge">Sync</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-row">
|
||||
<div class="select-wrap"><select id="agentSel" title="Select Agentic Skill"></select></div>
|
||||
<div class="tool-group" aria-label="Agent actions">
|
||||
<button class="icon-btn" id="addAgentBtn" data-tooltip="Create Agent">+</button>
|
||||
<button class="icon-btn" id="editAgentBtn" data-tooltip="Edit Agent Skill">✎</button>
|
||||
<button class="icon-btn" id="deleteAgentBtn" data-tooltip="Delete Agent Skill">−</button>
|
||||
<div class="control-row">
|
||||
<div class="select-wrap"><select id="agentSel" title="Select Agentic Skill"></select></div>
|
||||
<div class="tool-group" aria-label="Agent actions">
|
||||
<button class="icon-btn" id="addAgentBtn" data-tooltip="Create Agent">Add</button>
|
||||
<button class="icon-btn" id="editAgentBtn" data-tooltip="Edit Agent Skill">Edit</button>
|
||||
<button class="icon-btn" id="deleteAgentBtn" data-tooltip="Delete Agent Skill">Del</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="utility-row">
|
||||
<button class="icon-btn" id="saveWikiRawBtn" data-tooltip="Save Wiki Raw">□</button>
|
||||
<button class="icon-btn" id="multiAgentBtn" data-tooltip="Multi-Agent Mode">⧉</button>
|
||||
<button class="icon-btn" id="internetBtn" data-tooltip="Internet Access">↗</button>
|
||||
<button class="icon-btn" id="historyBtn" data-tooltip="View History">◷</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user