Feature/memory pr (#894)

* feat: multi-turn context and conversation arc memory

PR 2E - Section 2.9, 2.10:
- Add multiTurnContext.ts with turn tracking and state preservation
- Add conversationArc.ts with goal/decision/milestone tracking
- Wire into query.ts after tool execution
- Feature-flags: MULTI_TURN_CONTEXT, CONVERSATION_ARC
- Add comprehensive tests (22 passing)

* feat(cli): add /knowledge command to manage native memory

- Add /knowledge enable <yes|no> to toggle Knowledge Graph learning\n- Add /knowledge clear to reset memory\n- Add persistent knowledgeGraphEnabled setting to global config\n- Integrated user setting into the query execution loop

* feat(cli): add /knowledge command (stable local-jsx version)

- Resolve conflicts between .ts and .tsx files\n- Align with LocalJSXCommandCall signature\n- Fix onDone and args errors

* test(cli): fix knowledge command tests by properly isolating global config

* fix(cli): make knowledge command defensive against undefined args and leaky tests

* fix(cli): correct data source for entity count and fix test isolation

* fix(cli): reinforce knowledge test by explicitly defining property on test config

* fix(cli): explicitly define property in test config to avoid undefined in CI

* fix(cli): make knowledge tests resistant to global config mocks in CI

* chore(memory): surgical improvements from architectural audit

- Fix: Implement entity deduplication in Knowledge Graph\n- Fix: Ensure fact extraction from user messages in query loop\n- Fix: Refine regexes for better quality learning (less noise)

---------

Co-authored-by: LifeJiggy <Bloomtonjovish@gmail.com>
This commit is contained in:
3kin0x
2026-04-25 01:19:41 +02:00
committed by GitHub
parent ff2a380723
commit 44f9cac70d
10 changed files with 247 additions and 84 deletions

View File

@@ -119,25 +119,26 @@ function extractFactsAutomatically(content: string): void {
const arc = getArc()
if (!arc) return
// 1. Detect Environment Variables (KEY=VALUE)
const envMatches = content.matchAll(/(?:export\s+)?([A-Z_]+)=([^\s\n"']+)/g)
// 1. Detect Environment Variables (KEY=VALUE) - strictly uppercase keys
const envMatches = content.matchAll(/(?:export\s+)?([A-Z_]{3,})=([^\s\n"']+)/g)
for (const match of envMatches) {
addEntity('environment_variable', match[1], { value: match[2] })
}
// 2. Detect Absolute Paths
// 2. Detect Absolute Paths - ensure it looks like a path and not a div or code
const pathMatches = content.matchAll(/(\/(?:[\w.-]+\/)+[\w.-]+)/g)
for (const match of pathMatches) {
const path = match[1]
if (path.length > 5 && !path.includes('node_modules')) {
// Exclude common noise and ensure it's a long enough path
if (path.length > 8 && !path.includes('node_modules') && !path.includes('://')) {
addEntity('path', path, { type: 'absolute' })
}
}
// 3. Detect Versions (v1.2.3 or version 1.2.3)
// 3. Detect Versions - require vX.Y.Z or version X.Y.Z
const versionMatches = content.matchAll(/(?:v|version\s+)(\d+\.\d+(?:\.\d+)?)/gi)
for (const match of versionMatches) {
addEntity('version', match[0], { semver: match[1] })
addEntity('version', match[0].toLowerCase(), { semver: match[1] })
}
// 4. Detect Hostnames/URLs
@@ -145,7 +146,9 @@ function extractFactsAutomatically(content: string): void {
for (const match of urlMatches) {
try {
const url = new URL(match[1])
addEntity('endpoint', url.hostname, { url: url.toString() })
if (url.hostname.includes('.')) {
addEntity('endpoint', url.hostname, { url: url.toString() })
}
} catch {
// Ignore invalid URLs
}
@@ -262,6 +265,17 @@ export function addEntity(
const arc = getArc()
if (!arc) throw new Error('Arc not initialized')
// Check for existing entity to avoid duplicates (Deduplication Logic)
const existingEntity = Object.values(arc.knowledgeGraph.entities).find(
e => e.type === type && e.name === name,
)
if (existingEntity) {
existingEntity.attributes = { ...existingEntity.attributes, ...attributes }
arc.lastUpdateTime = Date.now()
return existingEntity
}
const id = `entity_${Date.now()}_${Math.random().toString(36).slice(2, 7)}`
const entity: Entity = { id, type, name, attributes }
@@ -360,4 +374,4 @@ export function getArcStats() {
milestoneCount: arc.milestones.length,
durationMs: arc.lastUpdateTime - arc.startTime,
}
}
}