Improve local evidence search and AI capabilities

This commit is contained in:
lix-2026
2026-06-05 23:00:53 +08:00
parent a1dcfc9f76
commit 4dbd9a978b
52 changed files with 6415 additions and 527 deletions
@@ -724,6 +724,89 @@ export function createSidebarPageAiRuntime(context) {
});
}
function pageAiEvidenceRangeFromParam(value) {
var text = String(value || '').trim();
if (!text) return null;
var match = text.match(/^(\d+)[-:,](\d+)$/);
if (!match) return text;
return { start: Number(match[1]), end: Number(match[2]) };
}
function pageAiEvidenceBboxFromParam(value) {
var text = String(value || '').trim();
if (!text) return null;
var parts = text.split(',').map(function(part) { return Number(part.trim()); });
if (parts.length < 4 || parts.slice(0, 4).some(function(part) { return !Number.isFinite(part); })) return text;
return { x0: parts[0], y0: parts[1], x1: parts[2], y1: parts[3] };
}
function pageAiDocumentIdFromUrl(url) {
var prefix = '/documents/';
if (!url.pathname.startsWith(prefix)) return '';
try {
return decodeURIComponent(url.pathname.slice(prefix.length).split('/')[0] || '');
} catch (_error) {
return url.pathname.slice(prefix.length).split('/')[0] || '';
}
}
function pageAiCitationResourcePath(url, rootUri) {
var explicitPath = String(url.searchParams.get('resourcePath') || '').trim();
if (explicitPath) return explicitPath;
var raw = String(url.searchParams.get('resourceTab') || '').trim();
if (raw.indexOf('::') >= 0) raw = raw.slice(raw.indexOf('::') + 2);
if (!raw.startsWith('resource:file:')) return '';
var rest = raw.slice('resource:file:'.length);
var prefix = String(rootUri || '').trim() + ':';
if (!prefix.trim() || !rest.startsWith(prefix)) return '';
return rest.slice(prefix.length).replace(/^\/+/, '');
}
function pageAiOpenCitationUrl(href) {
var url;
try {
url = new URL(String(href || ''), window.location.origin);
} catch (_error) {
return false;
}
if (url.origin !== window.location.origin || !url.pathname.startsWith('/documents/')) return false;
var rootUri = String(url.searchParams.get('rootUri') || currentRootUri() || '').trim();
var resourcePath = pageAiCitationResourcePath(url, rootUri);
var documentId = pageAiDocumentIdFromUrl(url) || currentDocumentId() || '';
var workspaceId = String(url.searchParams.get('workspaceId') || resolveWorkspaceId(document.body) || '').trim();
if (resourcePath) {
void openLocalResourceInActiveTab({
path: resourcePath,
rootUri: rootUri,
documentId: documentId,
workspaceId: workspaceId,
sourceKind: String(url.searchParams.get('sourceKind') || currentSourceKind() || 'local_folder').trim(),
page: url.searchParams.get('page') || undefined,
bbox: pageAiEvidenceBboxFromParam(url.searchParams.get('bbox')),
sourceMapPath: String(url.searchParams.get('sourceMapPath') || '').trim(),
blockId: String(url.searchParams.get('blockId') || '').trim(),
lineRange: pageAiEvidenceRangeFromParam(url.searchParams.get('lineRange')),
charRange: pageAiEvidenceRangeFromParam(url.searchParams.get('charRange')),
openTarget: 'active-tab',
paneRole: 'primary'
}).then(function(opened) {
if (!opened) window.location.assign(url.pathname + url.search + url.hash);
});
return true;
}
if (typeof window.__mnoteDocumentPaneRuntime?.openPrimaryDocument === 'function') {
void window.__mnoteDocumentPaneRuntime.openPrimaryDocument({
documentId: documentId,
workspaceId: workspaceId,
sourceKind: String(url.searchParams.get('sourceKind') || currentSourceKind() || '').trim(),
rootUri: rootUri,
url: url
});
return true;
}
return false;
}
function pageAiChatOnlyProfileEntries() {
var profiles = pageAiNormalizeArray(pageUiState.pageAiProfiles);
return PAGE_AI_CHAT_ONLY_PROFILE_REGISTRY.map(function(spec) {
@@ -786,10 +869,13 @@ export function createSidebarPageAiRuntime(context) {
var archived = pageAiNormalizeArray(upstream && upstream.archived ? upstream.archived : []);
return {
categories: categories.map(function(category) {
var skillRows = pageAiNormalizeArray(category && (category.skills || category.capabilities));
return {
name: String(category && category.name || '').trim() || 'misc',
description: String(category && category.description || '').trim(),
skills: pageAiNormalizeArray(category && category.skills).map(function(skill) {
title: String(category && category.title || '').trim(),
capabilities: pageAiNormalizeArray(category && category.capabilities),
skills: skillRows.map(function(skill) {
return {
name: String(skill && skill.name || '').trim(),
id: String(skill && skill.id || skill && skill.name || '').trim(),
@@ -807,8 +893,18 @@ export function createSidebarPageAiRuntime(context) {
configScope: String(skill && skill.configScope || '').trim(),
skillKind: String(skill && skill.skillKind || '').trim(),
profileId: String(skill && skill.profileId || '').trim(),
toolNames: pageAiNormalizeArray(skill && skill.toolNames),
tools: pageAiNormalizeArray(skill && skill.tools),
toolCount: Number(skill && skill.toolCount || 0),
disabledToolCount: Number(skill && skill.disabledToolCount || 0),
status: String(skill && skill.status || '').trim(),
capabilityId: String(skill && skill.capabilityId || '').trim(),
capabilityKind: String(skill && skill.capabilityKind || '').trim(),
uiKind: String(skill && skill.uiKind || '').trim(),
requiresContextRefs: pageAiNormalizeArray(skill && skill.requiresContextRefs),
readOnly: Boolean(skill && (skill.readOnly || skill.readonly || skill.configurable === false)),
category: String(category && category.name || '').trim()
category: String(skill && skill.category || category && category.name || '').trim(),
categoryTitle: String(skill && skill.categoryTitle || category && category.title || category && category.name || '').trim()
};
})
};
@@ -831,6 +927,16 @@ export function createSidebarPageAiRuntime(context) {
configScope: String(skill && skill.configScope || '').trim(),
skillKind: String(skill && skill.skillKind || '').trim(),
profileId: String(skill && skill.profileId || '').trim(),
toolNames: pageAiNormalizeArray(skill && skill.toolNames),
tools: pageAiNormalizeArray(skill && skill.tools),
toolCount: Number(skill && skill.toolCount || 0),
disabledToolCount: Number(skill && skill.disabledToolCount || 0),
status: String(skill && skill.status || '').trim(),
capabilityId: String(skill && skill.capabilityId || '').trim(),
capabilityKind: String(skill && skill.capabilityKind || '').trim(),
uiKind: String(skill && skill.uiKind || '').trim(),
requiresContextRefs: pageAiNormalizeArray(skill && skill.requiresContextRefs),
categoryTitle: String(skill && skill.categoryTitle || '').trim(),
readOnly: Boolean(skill && (skill.readOnly || skill.readonly || skill.configurable === false))
};
})
@@ -859,6 +965,16 @@ export function createSidebarPageAiRuntime(context) {
configScope: skill.configScope || '',
skillKind: skill.skillKind || '',
profileId: skill.profileId || '',
toolNames: skill.toolNames || [],
tools: skill.tools || [],
toolCount: Number(skill.toolCount || 0),
disabledToolCount: Number(skill.disabledToolCount || 0),
status: skill.status || '',
capabilityId: skill.capabilityId || '',
capabilityKind: skill.capabilityKind || '',
uiKind: skill.uiKind || '',
requiresContextRefs: skill.requiresContextRefs || [],
categoryTitle: skill.categoryTitle || category.title || category.name || '',
readOnly: Boolean(skill.readOnly || skill.readonly || skill.configurable === false)
});
});
@@ -1310,18 +1426,21 @@ export function createSidebarPageAiRuntime(context) {
function pageAiLoadSkillCatalog(runtime, profile) {
var params = new URLSearchParams();
params.set('runtime', runtime);
var endpoint = '/api/hermes/client/skills';
if (runtime === 'mnote') {
params.set('agentId', pageUiState.pageAiAgentId || 'reasonix');
endpoint = '/api/hermes/client/capabilities';
params.set('agentId', 'reasonix');
if (profile) params.set('profile', profile);
} else if (runtime === 'reasonix') {
params.set('runtime', 'reasonix');
} else if (runtime === 'hermes' && profile) {
params.set('profileId', profile);
}
return fetch('/api/hermes/client/skills?' + params.toString(), {
return fetch(endpoint + '?' + params.toString(), {
headers: { 'accept': 'application/json' }
}).then(function(response) {
return response.json().catch(function(){ return null; }).then(function(payload) {
if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'skills_failed_' + response.status));
if (!response.ok) throw new Error(pageAiErrorMessage(payload, 'capabilities_failed_' + response.status));
return pageAiNormalizeSkills(payload);
});
});
@@ -1478,21 +1597,30 @@ export function createSidebarPageAiRuntime(context) {
}
if (skillGroup === 'mnote') {
try {
var mnoteResponse = await fetch('/api/hermes/client/skills/toggle', {
var mnoteResponse = await fetch('/api/hermes/client/capabilities/toggle', {
method: 'PUT',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
skillKind: 'mnote_builtin',
name: name,
runtime: 'mnote',
profile: skillProfile || pageAiCurrentProfile(),
id: name,
enabled: Boolean(enabled)
})
});
var mnotePayload = await mnoteResponse.json().catch(function(){ return null; });
if (!mnoteResponse.ok) throw new Error(pageAiErrorMessage(mnotePayload, 'mnote_skill_toggle_failed_' + mnoteResponse.status));
if (!mnoteResponse.ok) throw new Error(pageAiErrorMessage(mnotePayload, 'mnote_capability_toggle_failed_' + mnoteResponse.status));
pageAiSetSkillPreference(skillGroup, name, Boolean(enabled), skillProfile);
pageAiNormalizeArray(pageUiState.pageAiSkillCatalogs && pageUiState.pageAiSkillCatalogs.mnote && pageUiState.pageAiSkillCatalogs.mnote.categories).forEach(function(category) {
pageAiNormalizeArray(category.skills).forEach(function(skill) {
if (skill.id === name || skill.name === name) skill.enabled = Boolean(enabled);
if (skill.id === name || skill.name === name) {
skill.enabled = Boolean(enabled);
skill.status = enabled ? 'available' : 'disabled';
pageAiNormalizeArray(skill.tools).forEach(function(tool) {
tool.enabled = Boolean(enabled);
tool.status = enabled ? 'available' : 'disabled';
});
skill.disabledToolCount = enabled ? 0 : Number(skill.toolCount || pageAiNormalizeArray(skill.tools).length || 0);
}
});
});
document.documentElement.setAttribute('data-mnote-page-ai-skill-toggled', skillGroup + ':' + name);
@@ -1612,8 +1740,6 @@ export function createSidebarPageAiRuntime(context) {
pageAiLoadBackendSessions()
]).then(function() {
renderPageAiControls();
}).catch(function() {}).then(function() {
return pageAiEnsureHermesSession();
}).then(function() {
return pageAiRestoreHermesSession();
}).then(function() {
@@ -2119,6 +2245,14 @@ export function createSidebarPageAiRuntime(context) {
function handlePageAiClick(event, helpers) {
var closestAction = helpers && helpers.closestAction;
if (typeof closestAction !== 'function') return false;
var pageAiCitationLink = closestAction(event.target, 'a[data-page-ai-citation-link="true"]');
if (pageAiCitationLink && !(event.metaKey || event.ctrlKey || event.shiftKey || event.altKey)) {
var href = pageAiCitationLink.getAttribute('href') || '';
if (pageAiOpenCitationUrl(href)) {
event.preventDefault();
return true;
}
}
var pageAiClose = closestAction(event.target, '[data-page-ai-action="close"]');
if (pageAiClose) {
event.preventDefault();