sync: Idea Genealogy + Quick Capture Overhaul

From private repo:

Idea Genealogy:
- Required edge explanations across UI/tools/API/MCP
- Inferred edge schema (category/type/confidence)
- Added part_of relationship type
- Connection UI chips (Made by / Part of / Came from / Related)

Quick Capture Overhaul:
- Jina.ai fallback for JS-rendered sites (Twitter, SPAs)
- Simplified UI: removed mode buttons, auto-detect input type
- Renamed to "Add Stuff", moved to top-right
- Auto-edge creation from description entities
- Increased content summary length (500→1500 chars)

Description Generation Fix:
- Simplified prompt to "what is this"
- Added link/dimensions context
- Better creator attribution

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
“BeeRad”
2026-01-14 17:08:26 +11:00
co-authored by Claude Opus 4.5
parent f7b8b2058c
commit 2cbc950fd4
21 changed files with 1296 additions and 389 deletions
+34 -4
View File
@@ -5,12 +5,32 @@ import { nodeService } from '@/services/database/nodes';
import { formatNodeForChat } from '../infrastructure/nodeFormatter';
export const createEdgeTool = tool({
description: 'Create directed relationship between nodes',
description:
'Create directed relationship between nodes.\n\n' +
'Direction rule: FROM node → TO node should read correctly.\n' +
'Prefer the 4 core relations unless the user clearly wants an advanced intellectual relation:\n' +
'- Made by → created_by (attribution)\n' +
'- Part of → part_of (attribution)\n' +
'- Came from → source_of (intellectual)\n' +
'- Related → related_to (intellectual fallback)\n\n' +
'Examples:\n' +
'- Episode → Podcast: "Episode of this podcast"\n' +
'- Book → Author: "Written by"\n' +
'- Company → Founder: "Founded by"\n' +
'- Insight → Source: "Came from / inspired by"\n',
inputSchema: z.object({
from_node_id: z.number().describe('The ID of the source node (where the connection originates)'),
to_node_id: z.number().describe('The ID of the target node (where the connection points to)'),
context: z.record(z.any()).optional().describe('Additional context about this connection - can include explanation, relationship type, strength, notes, etc.'),
source: z.enum(['user', 'ai', 'ai_similarity', 'helper_name']).default('ai').describe('Source of this edge - use "ai" for AI-created connections, "user" for manual connections, "ai_similarity" for similarity-based connections')
explanation: z.string().describe(
'REQUIRED: Why does this connection exist? Be specific. ' +
'Write it as a relationship that reads FROM → TO. ' +
'Examples: "Author of this book", "Guest on this podcast", ' +
'"Episode of this podcast", "This insight came from this podcast episode", "Extends the concept introduced here"'
),
source: z.enum(['user', 'ai', 'ai_similarity', 'helper_name']).default('ai').describe(
'Source of this edge. Use "ai" (or "helper_name") for AI-created connections, ' +
'"user" for manual connections, "ai_similarity" for similarity-based connections.'
)
}),
execute: async (params) => {
console.log('🔗 CreateEdge tool called with params:', JSON.stringify(params, null, 2));
@@ -41,6 +61,15 @@ export const createEdgeTool = tool({
};
}
const explanation = (params.explanation || '').trim();
if (!explanation) {
return {
success: false,
error: 'explanation is required. Provide a clear reason for why these two nodes should be connected.',
data: null
};
}
const [fromNode, toNode] = await Promise.all([
nodeService.getNodeById(params.from_node_id),
nodeService.getNodeById(params.to_node_id)
@@ -84,7 +113,8 @@ export const createEdgeTool = tool({
const newEdge = await edgeService.createEdge({
from_node_id: params.from_node_id,
to_node_id: params.to_node_id,
context: params.context || {},
explanation,
created_via: 'agent',
source
});
+5 -4
View File
@@ -15,14 +15,15 @@ Description: "${description}"
Respond with ONLY valid JSON (no markdown, no code blocks):
{
"enhancedDescription": "Improved 2-3 sentence description explaining what this content is about",
"enhancedDescription": "A comprehensive summary of what this content is about (can be several paragraphs, up to ~1500 characters)",
"tags": ["relevant", "semantic", "tags", "like", "ai", "economics", "research"],
"reasoning": "Brief explanation of why you chose these categories"
}
Guidelines:
- enhancedDescription should be thorough - cover key points, arguments, and takeaways
- Aim for 3-6 paragraphs or 800-1500 characters - don't artificially truncate
- Include 3-8 relevant semantic tags (not just generic ones)
- Make description informative and contextual
- For academic papers, include tags like: research, academic, paper, plus domain-specific tags
- For AI/ML papers, include: ai, machine-learning, artificial-intelligence, deep-learning
- For economics papers, include: economics, finance, markets, policy
@@ -32,7 +33,7 @@ Guidelines:
const response = await generateText({
model: openai('gpt-5-mini'),
prompt,
maxOutputTokens: 500
maxOutputTokens: 800
});
let content = response.text || '{}';
@@ -122,7 +123,7 @@ export const paperExtractTool = tool({
// Step 2: AI Analysis for enhanced metadata
const aiAnalysis = await analyzeContentWithAI(
result.metadata?.title || `PDF: ${new URL(url).pathname.split('/').pop()?.replace('.pdf', '')}`,
result.content?.substring(0, 1000) || 'PDF document content',
result.content?.substring(0, 2000) || 'PDF document content',
'pdf'
);
+5 -4
View File
@@ -15,14 +15,15 @@ Description: "${description}"
Respond with ONLY valid JSON (no markdown, no code blocks):
{
"enhancedDescription": "Improved 2-3 sentence description explaining what this content is about",
"enhancedDescription": "A comprehensive summary of what this content is about (can be several paragraphs, up to ~1500 characters)",
"tags": ["relevant", "semantic", "tags", "like", "ai", "economics", "research"],
"reasoning": "Brief explanation of why you chose these categories"
}
Guidelines:
- enhancedDescription should be thorough - cover key points, arguments, and takeaways
- Aim for 3-6 paragraphs or 800-1500 characters - don't artificially truncate
- Include 3-8 relevant semantic tags (not just generic ones)
- Make description informative and contextual
- For AI/ML content, include tags like: ai, machine-learning, artificial-intelligence, deep-learning
- For economics content, include: economics, finance, markets, policy
- Be specific and insightful
@@ -31,7 +32,7 @@ Guidelines:
const response = await generateText({
model: openai('gpt-5-mini'),
prompt,
maxOutputTokens: 500
maxOutputTokens: 800
});
let content = response.text || '{}';
@@ -113,7 +114,7 @@ export const websiteExtractTool = tool({
// Step 2: AI Analysis for enhanced metadata
const aiAnalysis = await analyzeContentWithAI(
result.metadata?.title || `Website: ${new URL(url).hostname}`,
result.content?.substring(0, 500) || 'Website content',
result.content?.substring(0, 2000) || 'Website content',
'website'
);
+4 -3
View File
@@ -22,7 +22,7 @@ Available types: ${AVAILABLE_SEGMENTS.join(', ')}
Respond with ONLY valid JSON (no markdown, no code blocks):
{
"enhancedDescription": "Improved 2-3 sentence description explaining what this content is about",
"enhancedDescription": "A comprehensive summary of what this content is about (can be several paragraphs, up to ~1500 characters)",
"tags": ["relevant", "semantic", "tags", "like", "ai", "economics", "research"],
"segments": ["research"],
"reasoning": "Brief explanation of why you chose these categories"
@@ -30,8 +30,9 @@ Respond with ONLY valid JSON (no markdown, no code blocks):
Guidelines:
- Choose 1 segment that best fits the content type
- enhancedDescription should be thorough - cover key points, arguments, and takeaways
- Aim for 3-6 paragraphs or 800-1500 characters - don't artificially truncate
- Include 3-8 relevant semantic tags (not just generic ones)
- Make description informative and contextual
- For AI/ML content, include tags like: ai, machine-learning, artificial-intelligence, deep-learning
- For economics content, include: economics, finance, markets, policy
- Be specific and insightful
@@ -40,7 +41,7 @@ Guidelines:
const response = await generateText({
model: openai('gpt-5-mini'),
prompt,
maxOutputTokens: 500
maxOutputTokens: 800
});
let content = response.text || '{}';