commit 86cb0d0ffed85c9e717fb2e1e741cc332c9ac394
Author: Coder Agent <coder@agents.omni>
Date: Mon Apr 20 14:13:57 2026
feat(agentd): load workspace direnv env for persistent agents
Run persistent agent sessions through direnv exec in AGENTD_CWD so
workspace .envrc/nix shells are applied (fixes missing tools like
typecheck.sh in omni-coder). Fail with a clear error when .envrc
exists but direnv is unavailable, and document behavior in SPEC.
Task-Id: t-812
diff --git a/Omni/Agentd/Daemon.hs b/Omni/Agentd/Daemon.hs
index 54535706..8a3ffa55 100644
--- a/Omni/Agentd/Daemon.hs
+++ b/Omni/Agentd/Daemon.hs
@@ -898,6 +898,14 @@ renderAgentExecScript agentCmd =
" cd \"$AGENTD_CWD\" || exit 1",
"fi",
"",
+ "AGENT_PREFIX=()",
+ "if command -v direnv >/dev/null 2>&1; then",
+ " AGENT_PREFIX=(direnv exec \"$PWD\")",
+ "elif [[ -f .envrc ]]; then",
+ " echo \"agentd: .envrc found in $PWD but direnv is not available on PATH\" >&2",
+ " exit 1",
+ "fi",
+ "",
"STATE_DIR=\"${AGENTD_STATE_DIR:-${XDG_STATE_HOME:-${HOME}/.local/state}/agentd-agents}\"",
"FIFO=\"${STATE_DIR}/${AGENTD_AGENT_NAME}.fifo\"",
"SESSIONS_DIR=\"${STATE_DIR}/sessions\"",
@@ -929,7 +937,7 @@ renderAgentExecScript agentCmd =
" EXTRA_ARGS=(${AGENTD_EXTRA_ARGS})",
"fi",
"",
- "exec \"" <> agentCmd <> "\" \\",
+ "exec \"${AGENT_PREFIX[@]}\" \"" <> agentCmd <> "\" \\",
" --provider \"${AGENTD_PROVIDER}\" \\",
" --model \"${AGENTD_MODEL}\" \\",
" --run-id \"${AGENTD_AGENT_NAME}\" \\",
@@ -2566,7 +2574,9 @@ test =
Test.assertBool "script should persist checkpoints" ("--checkpoint-dir \"$CHECKPOINT_DIR\"" `Text.isInfixOf` script)
Test.assertBool "script should look for latest session checkpoint" ("SESSION_CHECKPOINT=\"${CHECKPOINT_DIR}/session-latest.json\"" `Text.isInfixOf` script)
Test.assertBool "script should wire resume args" ("RESUME_ARGS=(--resume \"$SESSION_CHECKPOINT\")" `Text.isInfixOf` script)
- Test.assertBool "script should execute bundled agent path" ("exec \"/nix/store/abc-agent/bin/agent\" \\" `Text.isInfixOf` script)
+ Test.assertBool "script should set direnv execution prefix" ("AGENT_PREFIX=(direnv exec \"$PWD\")" `Text.isInfixOf` script)
+ Test.assertBool "script should fail clearly when .envrc exists but direnv is missing" (".envrc found in $PWD but direnv is not available on PATH" `Text.isInfixOf` script)
+ Test.assertBool "script should execute bundled agent path" ("exec \"${AGENT_PREFIX[@]}\" \"/nix/store/abc-agent/bin/agent\" \\" `Text.isInfixOf` script)
Test.assertBool "script should not require AGENTD_AGENT_COMMAND env var" (not ("AGENTD_AGENT_COMMAND" `Text.isInfixOf` script))
Test.assertBool "script should not reference legacy agentd-rpc" (not ("agentd-rpc" `Text.isInfixOf` script))
Test.assertBool "script should not use rpc mode flag" (not ("--mode rpc" `Text.isInfixOf` script)),
diff --git a/Omni/Agentd/SPEC.md b/Omni/Agentd/SPEC.md
index 0d8b0ac5..f33f15e9 100644
--- a/Omni/Agentd/SPEC.md
+++ b/Omni/Agentd/SPEC.md
@@ -124,6 +124,10 @@ 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 launch the agent through `direnv exec "$PWD" ...` when `direnv` is available,
+so per-workspace `.envrc`/nix-direnv environments are applied after `AGENTD_CWD` is set. If `.envrc`
+exists in the workspace and `direnv` is missing from PATH, startup fails with a clear error.
+
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.