feat: port MCP retrieval and write hardening
- align os MCP/runtime/docs with ra-h contract - add safe direct node lookup and context-optional flows - gate edge and context writes behind confirmation
This commit is contained in:
@@ -432,32 +432,6 @@ export default function FocusPanel({
|
||||
}
|
||||
};
|
||||
|
||||
const createEdgeAuto = async (targetNodeId: number) => {
|
||||
if (activeTab === null) return;
|
||||
try {
|
||||
const response = await fetch('/api/edges', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
from_node_id: activeTab,
|
||||
to_node_id: targetNodeId,
|
||||
source: 'user',
|
||||
explanation: '',
|
||||
}),
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to create edge');
|
||||
}
|
||||
|
||||
await fetchEdgesData(activeTab);
|
||||
} catch (error) {
|
||||
console.error('Error creating edge:', error);
|
||||
window.alert('Failed to create connection. Please try again.');
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
const createEdgeWithExplanation = async (targetNodeId: number, explanation: string) => {
|
||||
if (activeTab === null) return;
|
||||
try {
|
||||
@@ -1433,11 +1407,11 @@ export default function FocusPanel({
|
||||
onClose={() => setEdgeSearchOpen(false)}
|
||||
excludeNodeId={activeTab}
|
||||
onEdgeCreate={async (nodeId, explanation) => {
|
||||
if (explanation && explanation.trim()) {
|
||||
await createEdgeWithExplanation(nodeId, explanation.trim());
|
||||
} else {
|
||||
await createEdgeAuto(nodeId);
|
||||
if (!explanation || !explanation.trim()) {
|
||||
window.alert('Add a short explanation for the relationship before creating the edge.');
|
||||
return;
|
||||
}
|
||||
await createEdgeWithExplanation(nodeId, explanation.trim());
|
||||
}}
|
||||
/>
|
||||
|
||||
|
||||
@@ -315,12 +315,12 @@ export default function NodeSearchModal({
|
||||
value={explanation}
|
||||
onChange={(e) => setExplanation(e.target.value.slice(0, 500))}
|
||||
onKeyDown={(e) => {
|
||||
if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') {
|
||||
if ((e.metaKey || e.ctrlKey) && e.key === 'Enter' && explanation.trim()) {
|
||||
e.preventDefault();
|
||||
void handleCreate(selectedNode, explanation);
|
||||
}
|
||||
}}
|
||||
placeholder="Describe this connection... (optional, leave blank to auto-infer)"
|
||||
placeholder="Describe this connection in one clear sentence"
|
||||
rows={3}
|
||||
style={{
|
||||
width: '100%',
|
||||
@@ -351,7 +351,7 @@ export default function NodeSearchModal({
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => { void handleCreate(selectedNode, explanation); }}
|
||||
disabled={submitting}
|
||||
disabled={submitting || !explanation.trim()}
|
||||
style={{
|
||||
width: '100%',
|
||||
display: 'flex',
|
||||
@@ -368,7 +368,7 @@ export default function NodeSearchModal({
|
||||
opacity: submitting ? 0.6 : 1,
|
||||
}}
|
||||
>
|
||||
{submitting ? 'Creating…' : explanation.trim() ? 'Create connection' : 'Create connection (auto-infer)'}
|
||||
{submitting ? 'Creating…' : 'Create connection'}
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user