Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 13 additions & 6 deletions shared/egg_agent/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,12 @@ async def run_agent_async(
"""
model = model or DEFAULT_MODEL

# Resolve cwd: explicit arg > EGG_REPO_PATH > SDK default (os.getcwd()).
# Sandbox agents start at HOME (/home/egg) while the repo lives at
# /home/egg/repos/<repo> (EGG_REPO_PATH). Defaulting to EGG_REPO_PATH
# lands the agent in the repo on its first tool call. See #1993.
resolved_cwd: str | None = str(cwd) if cwd else os.environ.get("EGG_REPO_PATH")

# --- Harness selection (opt-in via EGG_HARNESS env var) ---
harness = os.environ.get("EGG_HARNESS", "claude-sdk")
if harness == "egg":
Expand All @@ -106,7 +112,7 @@ async def run_agent_async(
model=model,
max_turns=max_turns or 200,
system_prompt=system_prompt,
cwd=str(cwd) if cwd else None,
cwd=resolved_cwd,
timeout=timeout,
on_output=on_output,
env=env,
Expand Down Expand Up @@ -194,7 +200,7 @@ async def _check_tool_permission(
options = ClaudeAgentOptions(
permission_mode="bypassPermissions",
model=model,
cwd=str(cwd) if cwd else None,
cwd=resolved_cwd,
env=env or {},
# Read CLAUDE.md and settings.json from the filesystem so the agent
# picks up sandbox rules (BRC protocol, egg-orch CLI, git safety, etc.).
Expand Down Expand Up @@ -275,10 +281,11 @@ async def _check_tool_permission(
actual_model: str | None = None
result_meta: dict[str, Any] = {}

# Log the effective cwd — when the caller did not pass one, the SDK
# inherits os.getcwd(), so log that rather than None. Keeps
# session-init lines diagnostically useful (see #1954).
effective_cwd = str(cwd) if cwd else os.getcwd()
# Log the effective cwd — when the caller did not pass one and
# EGG_REPO_PATH is unset, the SDK inherits os.getcwd(), so log
# that rather than None. Keeps session-init lines diagnostically
# useful (see #1954, #1993).
effective_cwd = resolved_cwd or os.getcwd()
logger.info(
"Agent session init",
event_type="system",
Expand Down
4 changes: 3 additions & 1 deletion shared/egg_harness_integration/harness_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,9 @@ def create_egg_harness(
provider = RetryProvider(inner_provider)

# -- Tool registry -----------------------------------------------------
cwd_str = str(cwd) if cwd is not None else None
# Default cwd to EGG_REPO_PATH so sandbox agents start inside the repo
# rather than at HOME (/home/egg). See #1993.
cwd_str = str(cwd) if cwd is not None else os.environ.get("EGG_REPO_PATH")

registry = ToolRegistry()

Expand Down
42 changes: 40 additions & 2 deletions tests/shared/egg_agent/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -339,8 +339,12 @@ def test_structured_logging_init_and_result(self, mock_query):

@patch("claude_agent_sdk.query", side_effect=_mock_query_success)
def test_init_log_cwd_fallback(self, mock_query):
"""Test that cwd falls back to os.getcwd() when not provided."""
with patch("egg_agent.client.logger") as mock_logger:
"""Test that cwd falls back to os.getcwd() when neither arg nor env is set."""
with (
patch("egg_agent.client.logger") as mock_logger,
patch.dict(os.environ, {}, clear=False) as env,
):
env.pop("EGG_REPO_PATH", None)
_run_async(run_agent_async("test prompt"))

init_calls = [
Expand All @@ -365,6 +369,40 @@ def test_init_log_cwd_explicit(self, mock_query):
assert len(init_calls) == 1
assert init_calls[0].kwargs["cwd"] == "/tmp/test-dir"

@patch("claude_agent_sdk.query", side_effect=_mock_query_success)
def test_init_log_cwd_falls_back_to_egg_repo_path(self, mock_query):
"""When no cwd is passed, EGG_REPO_PATH should be used (see #1993)."""
with (
patch("egg_agent.client.logger") as mock_logger,
patch.dict(os.environ, {"EGG_REPO_PATH": "/home/egg/repos/myrepo"}),
):
_run_async(run_agent_async("test prompt"))

init_calls = [
c
for c in mock_logger.info.call_args_list
if c.args and c.args[0] == "Agent session init"
]
assert len(init_calls) == 1
assert init_calls[0].kwargs["cwd"] == "/home/egg/repos/myrepo"

@patch("claude_agent_sdk.query", side_effect=_mock_query_success)
def test_explicit_cwd_wins_over_egg_repo_path(self, mock_query):
"""Explicit cwd argument must take precedence over EGG_REPO_PATH."""
with (
patch("egg_agent.client.logger") as mock_logger,
patch.dict(os.environ, {"EGG_REPO_PATH": "/home/egg/repos/myrepo"}),
):
_run_async(run_agent_async("test prompt", cwd="/tmp/explicit"))

init_calls = [
c
for c in mock_logger.info.call_args_list
if c.args and c.args[0] == "Agent session init"
]
assert len(init_calls) == 1
assert init_calls[0].kwargs["cwd"] == "/tmp/explicit"

@patch("claude_agent_sdk.query", side_effect=_mock_query_error)
def test_structured_logging_on_error(self, mock_query):
"""Test that system/result log is emitted on error paths."""
Expand Down
Loading