Skip to content
42 changes: 30 additions & 12 deletions autogpt_platform/backend/backend/copilot/sdk/service.py

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Already fixed — sdk_cwd creation is now inside the outer try: block (line 518), with _cleanup_sdk_tool_results(sdk_cwd) and lock.release() both in the corresponding finally: block (lines 1116–1120). The stale diff showed it outside the try block, but the current code has it properly scoped.

Original file line number Diff line number Diff line change
Expand Up @@ -83,20 +83,30 @@ def available(self) -> bool:
# IMPORTANT: Must be less than frontend timeout (12s in useCopilotPage.ts)
_HEARTBEAT_INTERVAL = 10.0 # seconds


# Appended to the system prompt to inform the agent about available tools.
# The SDK built-in Bash is NOT available — use mcp__copilot__bash_exec instead,
# which has kernel-level network isolation (unshare --net).
_SDK_TOOL_SUPPLEMENT = """
def _build_sdk_tool_supplement(cwd: str) -> str:
"""Build the SDK tool supplement with the actual working directory injected."""
return f"""

## Tool notes

### Shell commands
- The SDK built-in Bash tool is NOT available. Use the `bash_exec` MCP tool
for shell commands — it runs in a network-isolated sandbox.

### Working directory
- Your working directory is: `{cwd}`
- All SDK Read/Write/Edit/Glob/Grep tools AND `bash_exec` operate inside this
directory. This is the ONLY writable path — do not attempt to read or write
anywhere else on the filesystem.
- Use relative paths or absolute paths under `{cwd}` for all file operations.

### Two storage systems — CRITICAL to understand

1. **Ephemeral working directory** (`/tmp/copilot-<session>/`):
1. **Ephemeral working directory** (`{cwd}`):
- Shared by SDK Read/Write/Edit/Glob/Grep tools AND `bash_exec`
- Files here are **lost between turns** — do NOT rely on them persisting
- Use for temporary work: running scripts, processing data, etc.
Expand Down Expand Up @@ -132,6 +142,7 @@ def available(self) -> bool:
All tasks must run in the foreground.
"""


STREAM_LOCK_PREFIX = "copilot:stream:lock:"


Expand Down Expand Up @@ -460,12 +471,27 @@ async def stream_chat_completion_sdk(
_background_tasks.add(task)
task.add_done_callback(_background_tasks.discard)

# Build system prompt (reuses non-SDK path with Langfuse support)
# Build system prompt (reuses non-SDK path with Langfuse support).
# Pre-compute the cwd here so the exact working directory path can be
# injected into the supplement instead of the generic placeholder.
# Catch ValueError early so the failure yields a clean StreamError rather
# than propagating outside the stream error-handling path.
has_history = len(session.messages) > 1
sdk_cwd = ""
try:
sdk_cwd = _make_sdk_cwd(session_id)
os.makedirs(sdk_cwd, exist_ok=True)
except (ValueError, OSError) as e:
logger.error("[SDK] [%s] Invalid SDK cwd: %s", session_id[:12], e)
yield StreamError(
errorText="Unable to initialize working directory.",
code="sdk_cwd_error",
)
return
Comment thread
majdyz marked this conversation as resolved.
Outdated
system_prompt, _ = await _build_system_prompt(
user_id, has_conversation_history=has_history
)
system_prompt += _SDK_TOOL_SUPPLEMENT
system_prompt += _build_sdk_tool_supplement(sdk_cwd)
message_id = str(uuid.uuid4())
stream_id = str(uuid.uuid4())

Expand Down Expand Up @@ -493,19 +519,11 @@ async def stream_chat_completion_sdk(
yield StreamStart(messageId=message_id, sessionId=session_id)

stream_completed = False
# Initialise variables before the try so the finally block can
# always attempt transcript upload regardless of errors.
sdk_cwd = ""
use_resume = False
resume_file: str | None = None
captured_transcript = CapturedTranscript()

try:
# Use a session-specific temp dir to avoid cleanup race conditions
# between concurrent sessions.
sdk_cwd = _make_sdk_cwd(session_id)
os.makedirs(sdk_cwd, exist_ok=True)

set_execution_context(user_id, session)
try:
from claude_agent_sdk import ClaudeAgentOptions, ClaudeSDKClient
Expand Down