← Back to task

Commit 86cb0d0f

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.