commit 9784bb4005c2a4d27c1ed5a69394abbc97ce28d6
Author: Coder Agent <coder@agents.omni>
Date: Wed Apr 15 10:12:31 2026
feat(agent): add persistent-only context compaction toggle
Add --enable-compaction flag and wire it in agentd persistent wrapper only.
Task-Id: t-796
diff --git a/Omni/Agent.hs b/Omni/Agent.hs
index 6a2ccfc9..e6b2856d 100755
--- a/Omni/Agent.hs
+++ b/Omni/Agent.hs
@@ -57,6 +57,7 @@ import qualified Omni.Agent.Memory as Memory
import qualified Omni.Agent.Models as Models
import qualified Omni.Agent.Op as Op
import qualified Omni.Agent.Programs.Agent as OpAgent
+import qualified Omni.Agent.Programs.Compaction as Compaction
import qualified Omni.Agent.Provider as Provider
import qualified Omni.Agent.Tools as Tools
import qualified Omni.Agent.Trace as Trace
@@ -189,6 +190,10 @@ runParser =
<> Cli.help "Resume from checkpoint file"
)
)
+ <*> Cli.switch
+ ( Cli.long "enable-compaction"
+ <> Cli.help "Enable context compaction when nearing model context limit"
+ )
<*> Cli.optional
( Cli.strArgument
( Cli.metavar "PROMPT"
@@ -197,7 +202,7 @@ runParser =
)
)
where
- mkAgentOptions providerOpt modelOpt maxCostOpt maxTokensOpt maxIterOpt verbosity jsonFlag dryRunFlag runIdOpt parentRunIdOpt checkpointDirOpt resumeOpt promptArg =
+ mkAgentOptions providerOpt modelOpt maxCostOpt maxTokensOpt maxIterOpt verbosity jsonFlag dryRunFlag runIdOpt parentRunIdOpt checkpointDirOpt resumeOpt enableCompaction promptArg =
AgentOptions
{ optProvider = maybe "auto" Text.pack providerOpt,
optModel = Text.pack </ modelOpt,
@@ -218,6 +223,7 @@ runParser =
optMaxIterSet = isJust maxIterOpt,
optCheckpointDir = checkpointDirOpt,
optResume = resumeOpt,
+ optEnableCompaction = enableCompaction,
optPromptArg = promptArg,
optOnEvent = Nothing
}
@@ -299,6 +305,7 @@ runOneShotWithPromptArg signalState baseOpts = do
TextIO.hPutStrLn IO.stderr <| " Max cost: " <> tshow (optMaxCost opts) <> " cents"
TextIO.hPutStrLn IO.stderr <| " Max iter: " <> tshow (optMaxIter opts)
TextIO.hPutStrLn IO.stderr <| " Verbosity: " <> tshow (optVerbosity opts)
+ TextIO.hPutStrLn IO.stderr <| " Compaction: " <> (if optEnableCompaction opts then "enabled" else "disabled")
TextIO.hPutStrLn IO.stderr <| " Prompt: " <> Text.take 100 (optPrompt opts)
Exit.exitSuccess
@@ -320,6 +327,7 @@ runNullDelimitedStdinMode signalState baseOpts = do
TextIO.hPutStrLn IO.stderr <| " Max cost: " <> tshow (optMaxCost opts) <> " cents"
TextIO.hPutStrLn IO.stderr <| " Max iter: " <> tshow (optMaxIter opts)
TextIO.hPutStrLn IO.stderr <| " Verbosity: " <> tshow (optVerbosity opts)
+ TextIO.hPutStrLn IO.stderr <| " Compaction: " <> (if optEnableCompaction opts then "enabled" else "disabled")
TextIO.hPutStrLn IO.stderr " Prompt source: stdin (\\0-delimited)"
Exit.exitSuccess
@@ -429,6 +437,7 @@ test =
optMaxCost opts Test.@=? 0
optMaxIter opts Test.@=? 0
optProvider opts Test.@=? "auto"
+ optEnableCompaction opts Test.@=? False
]
-- | Verbosity level for agent output
@@ -459,6 +468,7 @@ data AgentOptions = AgentOptions
optMaxIterSet :: Bool,
optCheckpointDir :: Maybe FilePath,
optResume :: Maybe FilePath,
+ optEnableCompaction :: Bool,
optPromptArg :: Maybe String,
-- | Optional event callback for programmatic use (e.g., Telegram integration)
optOnEvent :: Maybe (Trace.Event -> IO ())
@@ -486,6 +496,7 @@ defaultOptions =
optMaxIterSet = False,
optCheckpointDir = Nothing,
optResume = Nothing,
+ optEnableCompaction = False,
optPromptArg = Nothing,
optOnEvent = Nothing
}
@@ -937,6 +948,11 @@ runAgentWithState signalState opts initialState = do
(if optMaxTokens opts' > 0 then optMaxTokens opts' else maxBound)
)
else Nothing
+ compactionCfg =
+ Compaction.defaultCompactionConfig
+ { Compaction.ccContextLimit = Models.getContextWindow modelName,
+ Compaction.ccSummarizationModel = Op.Model modelName
+ }
baseConfig =
OpAgent.defaultAgentConfig
{ OpAgent.acModel = Op.Model modelName,
@@ -944,7 +960,8 @@ runAgentWithState signalState opts initialState = do
OpAgent.acMaxIterations = optMaxIter opts',
OpAgent.acBudget = budget,
OpAgent.acTools = workflowTools,
- OpAgent.acCheckpointInterval = if isJust (optCheckpointDir opts') then 5 else 0
+ OpAgent.acCheckpointInterval = if isJust (optCheckpointDir opts') then 5 else 0,
+ OpAgent.acCompaction = if optEnableCompaction opts' then Just compactionCfg else Nothing
}
(opConfig, seqToolsAllowed) = OpAgent.resolveAgentTools baseConfig seqTools
diff --git a/Omni/Agentd/Daemon.hs b/Omni/Agentd/Daemon.hs
index 28dc10b3..c17caa55 100644
--- a/Omni/Agentd/Daemon.hs
+++ b/Omni/Agentd/Daemon.hs
@@ -962,6 +962,7 @@ renderAgentExecScript agentCmd =
" --model \"${AGENTD_MODEL}\" \\",
" --run-id \"${AGENTD_AGENT_NAME}\" \\",
" --json \\",
+ " --enable-compaction \\",
" --checkpoint-dir \"$CHECKPOINT_DIR\" \\",
" \"${RESUME_ARGS[@]}\" \\",
" \"${EXTRA_ARGS[@]}\" <&3"
@@ -2664,6 +2665,7 @@ test =
Test.unit "persistent runtime script uses agent stdin mode" <| do
let script = renderAgentExecScript "/nix/store/abc-agent/bin/agent"
Test.assertBool "script should launch agent with json output" ("--json" `Text.isInfixOf` script)
+ Test.assertBool "script should enable compaction for persistent sessions" ("--enable-compaction" `Text.isInfixOf` script)
Test.assertBool "script should forward run-id" ("--run-id \"${AGENTD_AGENT_NAME}\"" `Text.isInfixOf` script)
Test.assertBool "script should append stdout to per-agent sessions jsonl" ("exec > >(tee -a \"$SESSION_FILE\")" `Text.isInfixOf` script)
Test.assertBool "script should keep per-agent checkpoint dir" ("CHECKPOINT_DIR=\"${CHECKPOINTS_DIR}/${AGENTD_AGENT_NAME}\"" `Text.isInfixOf` script)
diff --git a/Omni/Agentd/SPEC.md b/Omni/Agentd/SPEC.md
index e82a3a8a..12b05614 100644
--- a/Omni/Agentd/SPEC.md
+++ b/Omni/Agentd/SPEC.md
@@ -113,6 +113,9 @@ when that file exists, so `agentd restart` restores prior conversation context.
The agent executable path is baked into the generated runtime wrapper from agentd's bundled
runtime dependency, so persistent sessions do not rely on `AGENTD_AGENT_COMMAND` or ambient PATH drift.
+Persistent wrappers also pass `--enable-compaction`, so long-lived agentd sessions compact history
+near context limits while standard direct `agent` runs remain no-compaction by default.
+
Persistent runtime assets are generated by `agentd` itself on create/start:
- `~/.config/systemd/user/agentd-agent@.service`