From a77d9a128806198fea52e530de2b7754be26240f Mon Sep 17 00:00:00 2001 From: kami Date: Thu, 4 Jun 2026 00:58:22 +0400 Subject: [PATCH] feat(config): [project] block for cross-session project memory --- .../com/correx/core/config/ConfigLoader.kt | 8 +++++ .../com/correx/core/config/CorrexConfig.kt | 13 +++++++ .../correx/core/config/ConfigLoaderTest.kt | 36 +++++++++++++++++++ docs/sample-config.toml | 8 +++++ 4 files changed, 65 insertions(+) diff --git a/core/config/src/main/kotlin/com/correx/core/config/ConfigLoader.kt b/core/config/src/main/kotlin/com/correx/core/config/ConfigLoader.kt index b7175d5c..588cb994 100644 --- a/core/config/src/main/kotlin/com/correx/core/config/ConfigLoader.kt +++ b/core/config/src/main/kotlin/com/correx/core/config/ConfigLoader.kt @@ -413,6 +413,13 @@ object ConfigLoader { ) } + val projectSection = sections["project"] ?: emptyMap() + val project = ProjectConfig( + enabled = asBoolean(projectSection["enabled"], false), + root = asString(projectSection["root"], ""), + memoryK = asInt(projectSection["memory_k"], 5), + ) + val modelsSettings = ModelsSettings( defaultModel = asStringOrNull(modelsSection["default_model"]), llamaServerBin = asString(modelsSection["llama_server_bin"], "llama-server"), @@ -440,6 +447,7 @@ object ConfigLoader { models = models, modelsSettings = modelsSettings, artifacts = artifacts, + project = project, ) } diff --git a/core/config/src/main/kotlin/com/correx/core/config/CorrexConfig.kt b/core/config/src/main/kotlin/com/correx/core/config/CorrexConfig.kt index b54ddc26..4c4ab97d 100644 --- a/core/config/src/main/kotlin/com/correx/core/config/CorrexConfig.kt +++ b/core/config/src/main/kotlin/com/correx/core/config/CorrexConfig.kt @@ -13,6 +13,19 @@ data class CorrexConfig( val models: List = emptyList(), val modelsSettings: ModelsSettings = ModelsSettings(), val artifacts: List = emptyList(), + val project: ProjectConfig = ProjectConfig(), +) + +/** + * Project-scoped, cross-session memory. When [enabled], the decision journal is distilled + * to durable per-repo memory at session end and retrieved (top-[memoryK]) at session start. + * [root] is the repo root key; empty means the current working directory. + */ +@Serializable +data class ProjectConfig( + val enabled: Boolean = false, + val root: String = "", + val memoryK: Int = 5, ) /** diff --git a/core/config/src/test/kotlin/com/correx/core/config/ConfigLoaderTest.kt b/core/config/src/test/kotlin/com/correx/core/config/ConfigLoaderTest.kt index 0d2070ff..4684c780 100644 --- a/core/config/src/test/kotlin/com/correx/core/config/ConfigLoaderTest.kt +++ b/core/config/src/test/kotlin/com/correx/core/config/ConfigLoaderTest.kt @@ -183,6 +183,42 @@ class ConfigLoaderTest { assertEquals("python3", result.tools.shellAllowedExecutables[2]) } + @Test + fun `parseToml parses project block`() { + val toml = """ + [project] + enabled = true + root = "/home/me/repo" + memory_k = 8 + """.trimIndent() + + val loader = ConfigLoader::class.java + val parseTomlMethod = loader.getDeclaredMethod("parseToml", String::class.java) + parseTomlMethod.isAccessible = true + val result = parseTomlMethod.invoke(ConfigLoader, toml) as CorrexConfig + + assertEquals(true, result.project.enabled) + assertEquals("/home/me/repo", result.project.root) + assertEquals(8, result.project.memoryK) + } + + @Test + fun `parseToml project defaults when block absent`() { + val toml = """ + [server] + host = "localhost" + """.trimIndent() + + val loader = ConfigLoader::class.java + val parseTomlMethod = loader.getDeclaredMethod("parseToml", String::class.java) + parseTomlMethod.isAccessible = true + val result = parseTomlMethod.invoke(ConfigLoader, toml) as CorrexConfig + + assertEquals(false, result.project.enabled) + assertEquals("", result.project.root) + assertEquals(5, result.project.memoryK) + } + @Test fun `parseToml parses JSON array shell_allowed_executables`() { val toml = """ diff --git a/docs/sample-config.toml b/docs/sample-config.toml index 88d6796b..519e60cd 100644 --- a/docs/sample-config.toml +++ b/docs/sample-config.toml @@ -73,6 +73,14 @@ capabilities = { General = 1.0, Coding = 0.7, Reasoning = 0.6, Summarization = 0 # url = "http://127.0.0.1:10000" # capabilities = { General = 1.0, Coding = 0.7, Reasoning = 0.6, Summarization = 0.8, ToolCalling = 0.5 } +# Project-scoped, cross-session memory (distilled decision journal + repo map). +# When enabled, decisions are distilled to durable per-repo memory at session end and +# retrieved at the next session's start via the router L3 path. +[project] +enabled = false +root = "" # repo root key; empty = current working dir +memory_k = 5 # distilled decisions retrieved per session start + # Router configuration (optional, defaults shown below) [router] conversation_keep_last = 6 # how many recent chat turns to keep in context