1818 "export BUN_INSTALL=/root/.bun && export PATH=$BUN_INSTALL/bin:$PATH && "
1919 "ln -s $BUN_INSTALL/bin/bun /usr/local/bin/node && "
2020 "bun install -g @anthropic-ai/claude-code" ,
21+ # Pre-accept ToS and configure for non-interactive use
22+ "mkdir -p /root/.claude && "
23+ 'echo \' {"hasCompletedOnboarding": true, "hasAcknowledgedCostThreshold": true}\' '
24+ "> /root/.claude/settings.json" ,
2125 )
2226 .env (
2327 {
@@ -44,8 +48,6 @@ def run_claude_code_in_sandbox(
4448 """
4549 import logfire
4650
47- from policyengine_api .config import settings
48-
4951 print ("[SANDBOX] run_claude_code_in_sandbox starting" , flush = True )
5052 logfire .info (
5153 "run_claude_code_in_sandbox: starting" ,
@@ -82,44 +84,6 @@ def run_claude_code_in_sandbox(
8284 print ("[SANDBOX] sandbox created" , flush = True )
8385 logfire .info ("run_claude_code_in_sandbox: sandbox created" )
8486
85- # Log from inside the sandbox via Python
86- logfire .info ("run_claude_code_in_sandbox: logging from inside sandbox" )
87- escaped_question = question [:50 ].replace ("'" , "\\ '" ).replace ('"' , '\\ "' )
88- sandbox_log = sb .exec (
89- "python" ,
90- "-c" ,
91- f"""
92- import logfire
93- logfire.configure(token='{ settings .logfire_token } ', service_name='modal-sandbox-inner')
94- logfire.info('Inside Modal sandbox', question='{ escaped_question } ')
95- print('Logfire configured inside sandbox')
96- """ ,
97- )
98- sandbox_log .wait ()
99- logfire .info (
100- "run_claude_code_in_sandbox: sandbox inner log result" ,
101- stdout = sandbox_log .stdout .read ()[:200 ],
102- stderr = sandbox_log .stderr .read ()[:200 ],
103- returncode = sandbox_log .returncode ,
104- )
105-
106- # Check environment inside sandbox
107- logfire .info ("run_claude_code_in_sandbox: checking sandbox environment" )
108- env_check = sb .exec (
109- "sh" ,
110- "-c" ,
111- "which claude && echo PATH=$PATH && echo ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY:0:10}..." ,
112- )
113- env_check .wait ()
114- env_stdout = env_check .stdout .read ()
115- env_stderr = env_check .stderr .read ()
116- logfire .info (
117- "run_claude_code_in_sandbox: env check" ,
118- stdout = env_stdout [:500 ] if env_stdout else None ,
119- stderr = env_stderr [:500 ] if env_stderr else None ,
120- returncode = env_check .returncode ,
121- )
122-
12387 # Write MCP config
12488 logfire .info ("run_claude_code_in_sandbox: writing MCP config" )
12589 sb .exec ("mkdir" , "-p" , "/root/.claude" )
@@ -132,53 +96,6 @@ def run_claude_code_in_sandbox(
13296 returncode = config_process .returncode ,
13397 )
13498
135- # Verify config was written
136- verify_process = sb .exec ("cat" , "/root/.claude/mcp_servers.json" )
137- verify_process .wait ()
138- logfire .info (
139- "run_claude_code_in_sandbox: MCP config contents" ,
140- config = verify_process .stdout .read ()[:500 ],
141- )
142-
143- # Test Claude CLI works at all
144- print ("[SANDBOX] Testing claude --version" , flush = True )
145- logfire .info ("run_claude_code_in_sandbox: testing claude --version" )
146- version_check = sb .exec ("claude" , "--version" )
147- version_check .wait ()
148- version_stdout = version_check .stdout .read ()
149- version_stderr = version_check .stderr .read ()
150- print (f"[SANDBOX] claude --version: stdout={ version_stdout } , stderr={ version_stderr } , rc={ version_check .returncode } " , flush = True )
151- logfire .info (
152- "run_claude_code_in_sandbox: claude --version result" ,
153- stdout = version_stdout [:200 ] if version_stdout else None ,
154- stderr = version_stderr [:500 ] if version_stderr else None ,
155- returncode = version_check .returncode ,
156- )
157-
158- # Quick test: run Claude with a simple query WITHOUT MCP to verify it works
159- print ("[SANDBOX] Testing simple claude query (no MCP)" , flush = True )
160- logfire .info ("run_claude_code_in_sandbox: testing simple query without MCP" )
161- simple_test = sb .exec (
162- "claude" ,
163- "-p" ,
164- "Say hello in one word" ,
165- "--output-format" ,
166- "text" ,
167- "--dangerously-skip-permissions" ,
168- "--max-turns" ,
169- "1" ,
170- )
171- simple_test .wait ()
172- simple_stdout = simple_test .stdout .read ()
173- simple_stderr = simple_test .stderr .read ()
174- print (f"[SANDBOX] Simple test result: stdout={ simple_stdout [:200 ] if simple_stdout else 'empty' } , stderr={ simple_stderr [:200 ] if simple_stderr else 'empty' } , rc={ simple_test .returncode } " , flush = True )
175- logfire .info (
176- "run_claude_code_in_sandbox: simple test result" ,
177- stdout = simple_stdout [:500 ] if simple_stdout else None ,
178- stderr = simple_stderr [:500 ] if simple_stderr else None ,
179- returncode = simple_test .returncode ,
180- )
181-
18299 # Run Claude Code with the question
183100 # --dangerously-skip-permissions: auto-accept permission prompts (required for non-interactive)
184101 # --max-turns: limit execution to prevent runaway
@@ -242,6 +159,9 @@ def run_policy_analysis(
242159 "claude" ,
243160 "-p" ,
244161 question ,
162+ "--dangerously-skip-permissions" ,
163+ "--max-turns" ,
164+ "10" ,
245165 "--allowedTools" ,
246166 "mcp__policyengine__*,Bash,Read,Grep,Glob,Write,Edit" ,
247167 ],
0 commit comments