feat: implement Agentic Skill and Negative Prompt injection
This commit is contained in:
+8
-2
@@ -85,7 +85,9 @@ export class AgentExecutor {
|
||||
visionContent?: any[],
|
||||
temperature?: number,
|
||||
systemPrompt?: string,
|
||||
runId?: number
|
||||
runId?: number,
|
||||
agentSkillContext?: string,
|
||||
negativePrompt?: string
|
||||
}
|
||||
) {
|
||||
const {
|
||||
@@ -191,7 +193,11 @@ export class AgentExecutor {
|
||||
const internetCtx = internetEnabled
|
||||
? `\n\n[CRITICAL: INTERNET ACCESS ENABLED]\nYou can use <read_url> to search. Current time: ${new Date().toLocaleString()}`
|
||||
: '';
|
||||
const fullSystemPrompt = `${systemPrompt}${internetCtx}\n\n[CONTEXT]\n${brainContext}\n${contextBlock}`;
|
||||
|
||||
const agentSkillCtx = options.agentSkillContext ? `\n\n[AGENT PERSONA & SKILLS]\n${options.agentSkillContext}` : '';
|
||||
const negativeCtx = options.negativePrompt ? `\n\n[STRICT NEGATIVE PROMPT - DO NOT DO THIS]\n${options.negativePrompt}` : '';
|
||||
|
||||
const fullSystemPrompt = `${systemPrompt}${internetCtx}\n\n[CONTEXT]\n${brainContext}\n${contextBlock}${agentSkillCtx}${negativeCtx}`;
|
||||
const messagesForRequest: ChatMessage[] = [
|
||||
{ role: 'system', content: fullSystemPrompt, internal: true },
|
||||
...reqMessages
|
||||
|
||||
+108
-3
@@ -89,6 +89,12 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
case 'getModels':
|
||||
await this._sendModels();
|
||||
break;
|
||||
case 'getAgents':
|
||||
await this._sendAgentsList();
|
||||
break;
|
||||
case 'createAgent':
|
||||
await this._createAgent();
|
||||
break;
|
||||
case 'newChat':
|
||||
this._currentSessionId = null;
|
||||
this._currentSessionBrainId = getActiveBrainProfile().id;
|
||||
@@ -560,16 +566,79 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
});
|
||||
}
|
||||
|
||||
private _getAgentsDir(): string {
|
||||
const defaultPath = 'E:\\Wiki\\Agent\\.agent\\skills';
|
||||
if (fs.existsSync(defaultPath)) return defaultPath;
|
||||
|
||||
const workspaceFolders = vscode.workspace.workspaceFolders;
|
||||
if (workspaceFolders) {
|
||||
const localPath = path.join(workspaceFolders[0].uri.fsPath, '.agent', 'skills');
|
||||
if (!fs.existsSync(localPath)) {
|
||||
fs.mkdirSync(localPath, { recursive: true });
|
||||
}
|
||||
return localPath;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
private async _sendAgentsList() {
|
||||
if (!this._view) return;
|
||||
const dir = this._getAgentsDir();
|
||||
const agents = [];
|
||||
if (dir && fs.existsSync(dir)) {
|
||||
const files = fs.readdirSync(dir);
|
||||
for (const f of files) {
|
||||
if (f.endsWith('.md')) {
|
||||
agents.push({ name: f.replace('.md', ''), path: path.join(dir, f) });
|
||||
}
|
||||
}
|
||||
}
|
||||
this._view.webview.postMessage({ type: 'agentsList', value: agents });
|
||||
}
|
||||
|
||||
private async _createAgent() {
|
||||
const name = await vscode.window.showInputBox({
|
||||
prompt: 'Name of the new Agent (e.g., frontend_expert)',
|
||||
placeHolder: 'Agent name...'
|
||||
});
|
||||
if (!name) return;
|
||||
|
||||
const safeName = name.trim().replace(/[^a-zA-Z0-9_\\-\\u3131-\\uD79D]/g, '_');
|
||||
if (!safeName) return;
|
||||
|
||||
const dir = this._getAgentsDir();
|
||||
if (!dir) {
|
||||
vscode.window.showErrorMessage('Agent directory could not be determined.');
|
||||
return;
|
||||
}
|
||||
|
||||
const filePath = path.join(dir, `${safeName}.md`);
|
||||
if (!fs.existsSync(filePath)) {
|
||||
fs.writeFileSync(filePath, `# Agent Persona: ${safeName}\\n\\nAdd your instructions here...\\n`, 'utf8');
|
||||
}
|
||||
|
||||
const doc = await vscode.workspace.openTextDocument(filePath);
|
||||
await vscode.window.showTextDocument(doc);
|
||||
await this._sendAgentsList();
|
||||
}
|
||||
|
||||
private async _handlePrompt(data: any) {
|
||||
if (!this._view) return;
|
||||
|
||||
const { value, model, internet, files } = data;
|
||||
const { value, model, internet, files, agentFile, negativePrompt } = data;
|
||||
this._currentSessionBrainId = getActiveBrainProfile().id;
|
||||
|
||||
let agentSkillContext = undefined;
|
||||
if (agentFile && fs.existsSync(agentFile)) {
|
||||
agentSkillContext = fs.readFileSync(agentFile, 'utf8');
|
||||
}
|
||||
|
||||
try {
|
||||
await this._agent.handlePrompt(value, model, {
|
||||
internetEnabled: internet,
|
||||
visionContent: files // Agent seems to handle files via visionContent
|
||||
visionContent: files,
|
||||
agentSkillContext,
|
||||
negativePrompt
|
||||
});
|
||||
} catch (error: any) {
|
||||
logError('Prompt handling failed in sidebar provider.', { error: error?.message || String(error), promptPreview: summarizeText(value || '', 200) });
|
||||
@@ -905,6 +974,8 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
<div id="statusDot" style="width:6px; height:6px; border-radius:50%; background:var(--text-dim);"></div>
|
||||
<select id="modelSel" title="Select Model"></select>
|
||||
<select id="brainSel" title="Select Brain"></select>
|
||||
<select id="agentSel" title="Select Agentic Skill"></select>
|
||||
<button class="icon-btn" id="addAgentBtn" title="Create Agent">+</button>
|
||||
<button class="icon-btn" id="internetBtn" title="Internet Access">🌐</button>
|
||||
<button class="icon-btn" id="brainBtn" title="Sync Knowledge">🧠</button>
|
||||
<button class="icon-btn" id="historyBtn" title="History">📜</button>
|
||||
@@ -930,6 +1001,9 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
</div>
|
||||
|
||||
<div class="input-wrap">
|
||||
<div id="agentConfigPanel" style="display:none; padding-bottom:8px;">
|
||||
<textarea id="negativePrompt" rows="2" placeholder="Negative Prompt (What NOT to do)..." style="font-size:11.5px; padding:8px; border-radius:8px; border:1px solid var(--border); background:var(--input-bg); color:var(--text-bright); width:100%; resize:vertical; font-family:inherit; outline:none;"></textarea>
|
||||
</div>
|
||||
<div class="input-box">
|
||||
<div id="attachPreview" class="attachment-preview"></div>
|
||||
<textarea id="input" rows="1" placeholder="Type your request..."></textarea>
|
||||
@@ -959,6 +1033,10 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
const attachBtn = document.getElementById('attachBtn');
|
||||
const fileInput = document.getElementById('fileInput');
|
||||
const attachPreview = document.getElementById('attachPreview');
|
||||
const agentSel = document.getElementById('agentSel');
|
||||
const addAgentBtn = document.getElementById('addAgentBtn');
|
||||
const agentConfigPanel = document.getElementById('agentConfigPanel');
|
||||
const negativePrompt = document.getElementById('negativePrompt');
|
||||
|
||||
let streamBody = null;
|
||||
let internetEnabled = false;
|
||||
@@ -1078,6 +1156,13 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
statusLabel.innerText = msg.value; thinkingBar.classList.add('active');
|
||||
setTimeout(() => { thinkingBar.classList.remove('active'); }, 3000);
|
||||
break;
|
||||
case 'agentsList':
|
||||
agentSel.innerHTML = '<option value="none">No Agent</option>';
|
||||
msg.value.forEach(a => {
|
||||
const o = document.createElement('option'); o.value = a.path; o.innerText = a.name;
|
||||
agentSel.appendChild(o);
|
||||
});
|
||||
break;
|
||||
case 'error':
|
||||
thinkingBar.classList.remove('active'); sendBtn.disabled = false;
|
||||
addMsg(msg.value, 'error');
|
||||
@@ -1114,7 +1199,15 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
const val = input.value.trim();
|
||||
if (!val && pendingFiles.length === 0) return;
|
||||
addMsg(val || (pendingFiles.length > 0 ? \`[Sent \${pendingFiles.length} files]\` : ''), 'user');
|
||||
vscode.postMessage({ type: 'prompt', value: val, model: modelSel.value, internet: internetEnabled, files: pendingFiles.length > 0 ? pendingFiles : undefined });
|
||||
vscode.postMessage({
|
||||
type: 'prompt',
|
||||
value: val,
|
||||
model: modelSel.value,
|
||||
internet: internetEnabled,
|
||||
files: pendingFiles.length > 0 ? pendingFiles : undefined,
|
||||
agentFile: agentSel.value === 'none' ? undefined : agentSel.value,
|
||||
negativePrompt: negativePrompt.value.trim() || undefined
|
||||
});
|
||||
input.value = ''; input.style.height = 'auto'; pendingFiles = []; renderAttachments();
|
||||
sendBtn.disabled = true; thinkingBar.classList.add('active');
|
||||
}
|
||||
@@ -1147,7 +1240,19 @@ export class SidebarChatProvider implements vscode.WebviewViewProvider, BridgeIn
|
||||
}
|
||||
};
|
||||
|
||||
agentSel.onchange = () => {
|
||||
if (agentSel.value !== 'none') {
|
||||
agentConfigPanel.style.display = 'block';
|
||||
} else {
|
||||
agentConfigPanel.style.display = 'none';
|
||||
negativePrompt.value = '';
|
||||
}
|
||||
};
|
||||
|
||||
addAgentBtn.onclick = () => vscode.postMessage({ type: 'createAgent' });
|
||||
|
||||
vscode.postMessage({ type: 'getModels' });
|
||||
vscode.postMessage({ type: 'getAgents' });
|
||||
vscode.postMessage({ type: 'ready' });
|
||||
</script>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user