localnest_memory_capture_event
Capture background work events and automatically promote meaningful ones into durable local memory for AI agents.
Instructions
Ingest a background work event and auto-promote meaningful events into durable memory.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| event_type | No | task | |
| status | No | completed | |
| title | Yes | ||
| summary | No | ||
| content | No | ||
| kind | No | knowledge | |
| importance | No | ||
| confidence | No | ||
| files_changed | No | ||
| has_tests | No | ||
| tags | No | ||
| links | No | ||
| scope | No | ||
| source_ref | No | ||
| response_format | No | json |
Implementation Reference
- src/mcp/tools/memory-store.js:173-202 (handler)The registration of the 'localnest_memory_capture_event' tool. It calls the `memory.captureEvent` method.
registerJsonTool( ['localnest_memory_capture_event'], { title: 'Memory Capture Event', description: 'Ingest a background work event and auto-promote meaningful events into durable memory.', inputSchema: { event_type: MEMORY_EVENT_TYPE_SCHEMA, status: MEMORY_EVENT_STATUS_SCHEMA, title: z.string().min(1).max(400), summary: z.string().max(4000).default(''), content: z.string().max(20000).default(''), kind: MEMORY_KIND_SCHEMA.optional(), importance: z.number().int().min(0).max(100).optional(), confidence: z.number().min(0).max(1).optional(), files_changed: z.number().int().min(0).max(10000).default(0), has_tests: z.boolean().default(false), tags: z.array(z.string()).max(50).default([]), links: z.array(MEMORY_LINK_SCHEMA).max(50).default([]), scope: MEMORY_SCOPE_SCHEMA, source_ref: z.string().max(1000).default('') }, annotations: { readOnlyHint: false, destructiveHint: false, idempotentHint: false, openWorldHint: false } }, async (args) => memory.captureEvent(args) ); - The main handler implementation for `captureEvent`. It processes the input, determines if an event should be promoted to memory, and performs the necessary database operations.
export async function captureEvent(adapter, input, { storeEntry, updateEntry }) { const scope = normalizeScope(input.scope); const eventType = cleanString(input.event_type || input.eventType || 'task', 60) || 'task'; const rawContent = cleanString(input.content, 20000); const summary = deriveSummary(input.summary, rawContent); const content = rawContent || summary; const title = deriveTitle({ title: input.title, summary, content, eventType, scope }); const tags = ensureArray(input.tags); const links = normalizeLinks(input.links); const sourceRef = cleanString(input.source_ref || input.sourceRef, 1000); const createdAt = nowIso(); const signalScore = computeSignalScore({ eventType, status: input.status, importance: input.importance, filesChanged: input.files_changed || input.filesChanged, hasTests: input.has_tests || input.hasTests, tags, title, content, summary }); const promotionThreshold = getPromotionThreshold({ eventType, status: input.status, title, summary, content }); if (!title) throw new Error('title is required'); if (!content && !summary) throw new Error('content or summary is required'); const record = { eventType, title, summary, content: content || summary, scope, tags, links, sourceRef, signalScore, status: signalScore >= promotionThreshold ? 'processed' : 'ignored' }; let promotedMemoryId = null; if (signalScore >= promotionThreshold) { const memoryKind = input.kind || (eventType === 'preference' ? 'preference' : 'knowledge'); const mergeTarget = await findMergeCandidate(adapter, { kind: memoryKind, title, summary, content: content || summary, scope, tags }); if (mergeTarget) { const merged = await updateEntry(mergeTarget.id, { summary: mergeText(mergeTarget.summary, summary), content: mergeText(mergeTarget.content, content || summary), tags: Array.from(new Set([...(mergeTarget.tags || []), ...tags])), links: Array.from(new Map( [...(mergeTarget.links || []), ...links].map((item) => [`${item.path}:${item.line || 0}`, item]) ).values()), importance: Math.max( mergeTarget.importance || 0, input.importance || 0, Math.min(95, Math.round(signalScore * 20)) ), confidence: Math.max( mergeTarget.confidence || 0, input.confidence || 0, Math.min(0.95, 0.45 + (signalScore * 0.1)) ), change_note: `Auto-captured merge from ${eventType} event` }); promotedMemoryId = merged.id; record.status = 'merged'; } else { const result = await storeEntry({ kind: memoryKind, title, summary, content: content || summary, status: input.memory_status || 'active', importance: input.importance === undefined ? Math.min(95, Math.round(signalScore * 20)) : input.importance, confidence: input.confidence === undefined ? Math.min(0.95, 0.45 + (signalScore * 0.1)) : input.confidence, tags, links, scope, source_type: 'capture-event', source_ref: sourceRef, change_note: `Auto-captured from ${eventType} event` }); promotedMemoryId = result.memory?.id || null; record.status = result.duplicate ? 'duplicate' : 'promoted'; } } const insert = await adapter.run( `INSERT INTO memory_events( event_type, title, summary, content, status, signal_score, promoted_memory_id, scope_root_path, scope_project_path, scope_branch_name, topic, feature, tags_json, links_json, source_ref, created_at ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [ record.eventType, record.title, record.summary, record.content, record.status, signalScore, promotedMemoryId, scope.root_path, scope.project_path, scope.branch_name, scope.topic, scope.feature, stableJson(tags), stableJson(links), sourceRef, createdAt ] ); return { event_id: insert.lastInsertRowid, event_type: eventType, signal_score: Number(signalScore.toFixed(2)), promotion_threshold: Number(promotionThreshold.toFixed(2)), status: record.status, promoted_memory_id: promotedMemoryId }; }