feat(config): [project] block for cross-session project memory

This commit is contained in:
2026-06-04 00:58:22 +04:00
parent 8d67f5b03e
commit a77d9a1288
4 changed files with 65 additions and 0 deletions
@@ -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( val modelsSettings = ModelsSettings(
defaultModel = asStringOrNull(modelsSection["default_model"]), defaultModel = asStringOrNull(modelsSection["default_model"]),
llamaServerBin = asString(modelsSection["llama_server_bin"], "llama-server"), llamaServerBin = asString(modelsSection["llama_server_bin"], "llama-server"),
@@ -440,6 +447,7 @@ object ConfigLoader {
models = models, models = models,
modelsSettings = modelsSettings, modelsSettings = modelsSettings,
artifacts = artifacts, artifacts = artifacts,
project = project,
) )
} }
@@ -13,6 +13,19 @@ data class CorrexConfig(
val models: List<ModelConfig> = emptyList(), val models: List<ModelConfig> = emptyList(),
val modelsSettings: ModelsSettings = ModelsSettings(), val modelsSettings: ModelsSettings = ModelsSettings(),
val artifacts: List<ArtifactKindConfig> = emptyList(), val artifacts: List<ArtifactKindConfig> = 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,
) )
/** /**
@@ -183,6 +183,42 @@ class ConfigLoaderTest {
assertEquals("python3", result.tools.shellAllowedExecutables[2]) 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 @Test
fun `parseToml parses JSON array shell_allowed_executables`() { fun `parseToml parses JSON array shell_allowed_executables`() {
val toml = """ val toml = """
+8
View File
@@ -73,6 +73,14 @@ capabilities = { General = 1.0, Coding = 0.7, Reasoning = 0.6, Summarization = 0
# url = "http://127.0.0.1:10000" # url = "http://127.0.0.1:10000"
# capabilities = { General = 1.0, Coding = 0.7, Reasoning = 0.6, Summarization = 0.8, ToolCalling = 0.5 } # 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 configuration (optional, defaults shown below)
[router] [router]
conversation_keep_last = 6 # how many recent chat turns to keep in context conversation_keep_last = 6 # how many recent chat turns to keep in context