@@ -42,6 +42,16 @@ def run_claude_code_in_sandbox(
4242
4343 Returns the sandbox and process handle for streaming output.
4444 """
45+ import logfire
46+
47+ from policyengine_api .config import settings
48+
49+ logfire .info (
50+ "run_claude_code_in_sandbox: starting" ,
51+ question = question [:100 ],
52+ api_base_url = api_base_url ,
53+ )
54+
4555 # MCP config for Claude Code (type: sse for HTTP SSE transport)
4656 mcp_config = f"""{{
4757 "mcpServers": {{
@@ -53,31 +63,91 @@ def run_claude_code_in_sandbox(
5363}}"""
5464
5565 # Get reference to deployed app (required when calling from outside Modal)
66+ logfire .info ("run_claude_code_in_sandbox: looking up Modal app" )
5667 sandbox_app = modal .App .lookup ("policyengine-sandbox" , create_if_missing = True )
68+ logfire .info ("run_claude_code_in_sandbox: Modal app found" )
5769
70+ logfire .info ("run_claude_code_in_sandbox: creating sandbox" )
5871 sb = modal .Sandbox .create (
5972 app = sandbox_app ,
6073 image = sandbox_image ,
6174 secrets = [anthropic_secret , logfire_secret ],
6275 timeout = 600 ,
6376 workdir = "/tmp" ,
6477 )
78+ logfire .info ("run_claude_code_in_sandbox: sandbox created" )
79+
80+ # Log from inside the sandbox via Python
81+ logfire .info ("run_claude_code_in_sandbox: logging from inside sandbox" )
82+ escaped_question = question [:50 ].replace ("'" , "\\ '" ).replace ('"' , '\\ "' )
83+ sandbox_log = sb .exec (
84+ "python" ,
85+ "-c" ,
86+ f"""
87+ import logfire
88+ logfire.configure(token='{ settings .logfire_token } ', service_name='modal-sandbox-inner')
89+ logfire.info('Inside Modal sandbox', question='{ escaped_question } ')
90+ print('Logfire configured inside sandbox')
91+ """ ,
92+ )
93+ sandbox_log .wait ()
94+ logfire .info (
95+ "run_claude_code_in_sandbox: sandbox inner log result" ,
96+ stdout = sandbox_log .stdout .read ()[:200 ],
97+ stderr = sandbox_log .stderr .read ()[:200 ],
98+ returncode = sandbox_log .returncode ,
99+ )
100+
101+ # Check environment inside sandbox
102+ logfire .info ("run_claude_code_in_sandbox: checking sandbox environment" )
103+ env_check = sb .exec (
104+ "sh" ,
105+ "-c" ,
106+ "which claude && echo PATH=$PATH && echo ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:0:10}..." ,
107+ )
108+ env_check .wait ()
109+ env_stdout = env_check .stdout .read ()
110+ env_stderr = env_check .stderr .read ()
111+ logfire .info (
112+ "run_claude_code_in_sandbox: env check" ,
113+ stdout = env_stdout [:500 ] if env_stdout else None ,
114+ stderr = env_stderr [:500 ] if env_stderr else None ,
115+ returncode = env_check .returncode ,
116+ )
65117
66118 # Write MCP config
119+ logfire .info ("run_claude_code_in_sandbox: writing MCP config" )
67120 sb .exec ("mkdir" , "-p" , "/root/.claude" )
68121 config_process = sb .exec (
69122 "sh" , "-c" , f"cat > /root/.claude/mcp_servers.json << 'EOF'\n { mcp_config } \n EOF"
70123 )
71124 config_process .wait ()
125+ logfire .info (
126+ "run_claude_code_in_sandbox: MCP config written" ,
127+ returncode = config_process .returncode ,
128+ )
129+
130+ # Verify config was written
131+ verify_process = sb .exec ("cat" , "/root/.claude/mcp_servers.json" )
132+ verify_process .wait ()
133+ logfire .info (
134+ "run_claude_code_in_sandbox: MCP config contents" ,
135+ config = verify_process .stdout .read ()[:500 ],
136+ )
72137
73138 # Run Claude Code with the question
139+ logfire .info ("run_claude_code_in_sandbox: starting claude CLI" )
74140 process = sb .exec (
75141 "claude" ,
76142 "-p" ,
77143 question ,
144+ "--output-format" ,
145+ "stream-json" ,
146+ "--verbose" ,
78147 "--allowedTools" ,
79148 "mcp__policyengine__*,Bash,Read,Grep,Glob,Write,Edit" ,
80149 )
150+ logfire .info ("run_claude_code_in_sandbox: claude CLI process started, returning" )
81151
82152 return sb , process
83153
0 commit comments