Improve local evidence search and AI capabilities
This commit is contained in:
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user