170 lines
7.6 KiB
TypeScript
170 lines
7.6 KiB
TypeScript
import * as path from 'path';
|
||
import * as vscode from 'vscode';
|
||
import { SidebarChatProvider } from '../sidebarProvider';
|
||
import { logError, logInfo } from '../utils';
|
||
import {
|
||
resolveScopeForAgent,
|
||
openKnowledgeMapEditor,
|
||
getOrCreateAgentEntry,
|
||
upsertAgentEntry,
|
||
} from '../skills/agentKnowledgeMap';
|
||
import { getActiveBrainProfile } from '../utils';
|
||
|
||
/**
|
||
* Handles agent-skill messages: the per-conversation agent picker, agent CRUD,
|
||
* persisting the user's last selected agent, and the knowledge-map dropdown.
|
||
*/
|
||
export async function handleAgentMessage(provider: SidebarChatProvider, data: any): Promise<boolean> {
|
||
switch (data.type) {
|
||
case 'getAgents':
|
||
await provider._sendAgentsList();
|
||
return true;
|
||
case 'createAgent':
|
||
await provider._createAgent();
|
||
return true;
|
||
case 'getAgentContent':
|
||
await provider._sendAgentContent(data.path);
|
||
return true;
|
||
case 'updateAgent':
|
||
await provider._updateAgent(data.path, data.content, data.negativePrompt);
|
||
return true;
|
||
case 'deleteAgent':
|
||
await provider._deleteAgent(data.path);
|
||
return true;
|
||
case 'saveAgentSelection':
|
||
await provider._context.globalState.update(SidebarChatProvider.lastAgentStateKey, data.path || 'none');
|
||
logInfo(`Agent selection saved: ${data.path}`);
|
||
void provider._sendReadyStatus();
|
||
return true;
|
||
case 'getKnowledgeScope': {
|
||
const view = (provider as any)._view as vscode.WebviewView | undefined;
|
||
if (!view) return true;
|
||
const brain = getActiveBrainProfile();
|
||
const brainRoot = brain?.localBrainPath || '';
|
||
const scope = resolveScopeForAgent(data.agentPath || '', brainRoot);
|
||
const folders = scope.folders.map((abs) => ({
|
||
absolute: abs,
|
||
relative: brainRoot ? path.relative(brainRoot, abs) || abs : abs,
|
||
}));
|
||
view.webview.postMessage({
|
||
type: 'knowledgeScope',
|
||
value: {
|
||
agent: scope.agent?.name ?? null,
|
||
folders,
|
||
source: scope.source,
|
||
brainRoot,
|
||
},
|
||
});
|
||
void provider._sendReadyStatus();
|
||
return true;
|
||
}
|
||
case 'editKnowledgeMap':
|
||
await openKnowledgeMapEditor();
|
||
return true;
|
||
case 'getAgentMap': {
|
||
const view = (provider as any)._view as vscode.WebviewView | undefined;
|
||
if (!view) return true;
|
||
try {
|
||
const entry = getOrCreateAgentEntry(data.agentPath || '');
|
||
const hasWeightOverride = typeof entry.secondBrainWeight === 'number';
|
||
const knowledgeMapHasEntry = entry.knowledgeFolders.length > 0
|
||
|| (entry.skillFolders?.length ?? 0) > 0
|
||
|| !!(entry.model && entry.model.trim())
|
||
|| hasWeightOverride;
|
||
view.webview.postMessage({
|
||
type: 'agentMapData',
|
||
value: {
|
||
name: entry.name,
|
||
knowledgeFolders: entry.knowledgeFolders,
|
||
skillFolders: entry.skillFolders || [],
|
||
// Per-agent model override — empty string means "use current default model".
|
||
model: entry.model || '',
|
||
// null = no override (fall back to global slider); number = pinned 0–100.
|
||
secondBrainWeight: hasWeightOverride ? entry.secondBrainWeight : null,
|
||
exists: knowledgeMapHasEntry,
|
||
},
|
||
});
|
||
} catch (e: any) {
|
||
logError('agent-map: load failed.', { error: e?.message ?? String(e) });
|
||
view.webview.postMessage({
|
||
type: 'agentMapData',
|
||
value: { name: '', knowledgeFolders: [], skillFolders: [], model: '', secondBrainWeight: null, exists: false },
|
||
});
|
||
}
|
||
return true;
|
||
}
|
||
case 'saveAgentMap': {
|
||
const view = (provider as any)._view as vscode.WebviewView | undefined;
|
||
if (!view) return true;
|
||
const agentPath = typeof data.agentPath === 'string' ? data.agentPath : '';
|
||
const name = path.basename(agentPath).replace(/\.(md|markdown)$/i, '').trim();
|
||
const knowledgeFolders = Array.isArray(data.knowledgeFolders)
|
||
? data.knowledgeFolders.filter((f: unknown) => typeof f === 'string')
|
||
: [];
|
||
const skillFolders = Array.isArray(data.skillFolders)
|
||
? data.skillFolders.filter((f: unknown) => typeof f === 'string')
|
||
: [];
|
||
// Treat blank / "Use current model" as no override — drop the field entirely
|
||
// so the JSON stays clean and the resolver falls back to the global default.
|
||
const modelOverride = typeof data.model === 'string' ? data.model.trim() : '';
|
||
// null / undefined / non-finite = "Use global setting" → drop the field.
|
||
let weightOverride: number | undefined;
|
||
if (typeof data.secondBrainWeight === 'number' && Number.isFinite(data.secondBrainWeight)) {
|
||
weightOverride = Math.max(0, Math.min(100, Math.round(data.secondBrainWeight)));
|
||
}
|
||
const result = upsertAgentEntry({
|
||
name,
|
||
knowledgeFolders,
|
||
skillFolders,
|
||
model: modelOverride || undefined,
|
||
secondBrainWeight: weightOverride,
|
||
});
|
||
view.webview.postMessage({
|
||
type: 'agentMapSaved',
|
||
value: { ok: result.ok, path: result.path, error: result.error },
|
||
});
|
||
return true;
|
||
}
|
||
case 'pickPath': {
|
||
const view = (provider as any)._view as vscode.WebviewView | undefined;
|
||
if (!view) return true;
|
||
const kind = data.kind === 'skillFile' ? 'skillFile'
|
||
: data.kind === 'skillFolder' ? 'skillFolder'
|
||
: 'knowledgeFolder';
|
||
const isFile = kind === 'skillFile';
|
||
const label = kind === 'knowledgeFolder' ? 'Select Knowledge Folder'
|
||
: kind === 'skillFolder' ? 'Select Skill Folder'
|
||
: 'Select Skill File (.md)';
|
||
const defaultUri = (() => {
|
||
if (kind === 'knowledgeFolder') {
|
||
const brain = getActiveBrainProfile();
|
||
if (brain?.localBrainPath) return vscode.Uri.file(brain.localBrainPath);
|
||
}
|
||
return undefined;
|
||
})();
|
||
try {
|
||
const picked = await vscode.window.showOpenDialog({
|
||
canSelectFiles: isFile,
|
||
canSelectFolders: !isFile,
|
||
canSelectMany: false,
|
||
openLabel: label,
|
||
defaultUri,
|
||
filters: isFile ? { Markdown: ['md', 'markdown'] } : undefined,
|
||
});
|
||
const fsPath = picked?.[0]?.fsPath || '';
|
||
if (fsPath) {
|
||
view.webview.postMessage({
|
||
type: 'pickedPath',
|
||
value: { kind, path: fsPath },
|
||
});
|
||
}
|
||
} catch (e: any) {
|
||
logError('agent-map: pick failed.', { kind, error: e?.message ?? String(e) });
|
||
}
|
||
return true;
|
||
}
|
||
default:
|
||
return false;
|
||
}
|
||
}
|