"""Fresh subscription benchmark; never installs tools or changes a production config.
Requires Python >=3.11. Explicitly pass the pinned executable(s). AGNT must be a
separately authorized isolated host; this script does not launch or patch AGNT.
See LIVE-REPRODUCTION.md. Outputs go to a NEW directory; existing runs are refused.
"""
import argparse,json,os,sys,subprocess,time,uuid,urllib.request,hashlib
from pathlib import Path
P=argparse.ArgumentParser();P.add_argument('--harness',choices=['agnt','claude','codex','omp','openclaw','hermes'],required=True);P.add_argument('--track',choices=['claude','codex'],required=True);P.add_argument('--output',required=True);P.add_argument('--exe');P.add_argument('--entry');P.add_argument('--base-url');P.add_argument('--retention',choices=['default','long'],default='default');P.add_argument('--dry-run',action='store_true');a=P.parse_args()
ROOT=Path(__file__).resolve().parents[2];OUT=Path(a.output).resolve();OUT.mkdir(parents=True,exist_ok=False)
W=json.loads((ROOT/f'artifacts/data/workload-{a.track}.json').read_text());model='claude-sonnet-5' if a.track=='claude' else 'gpt-6-astra';session=str(uuid.uuid4());history=[];env=os.environ.copy();HOME=OUT/'home';CWD=OUT/'work';HOME.mkdir();CWD.mkdir()
# Do not reuse a funded API key accidentally. Subscription credentials must be
# supplied through the provider's documented OAuth environment or login path.
for name in ['ANTHROPIC_API_KEY','ANTHROPIC_AUTH_TOKEN','OPENAI_API_KEY','CLAUDECODE']:env.pop(name,None)
if a.harness=='claude':
 if a.track!='claude':raise ValueError('Claude CLI track must be Claude')
 env.update(CLAUDE_CONFIG_DIR=str(HOME),DISABLE_AUTOUPDATER='1',CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC='1')
elif a.harness=='codex':
 if a.track!='codex':raise ValueError('Codex CLI track must be Codex')
 if not env.get('CODEX_HOME'):raise ValueError('Set CODEX_HOME to an isolated, already authenticated login directory')
elif a.harness=='omp':
 env['PI_CODING_AGENT_DIR']=str(HOME);env.pop('PI_CACHE_RETENTION',None)
 if a.retention=='long':env['PI_CACHE_RETENTION']='long'
elif a.harness=='openclaw':
 ref=('anthropic/' if a.track=='claude' else 'openai/')+model
 defaults={'workspace':str(CWD),'model':{'primary':ref}}
 if a.track=='codex':defaults['models']={ref:{'agentRuntime':{'id':'openclaw'}}}
 elif a.retention=='long':defaults['models']={ref:{'params':{'cacheRetention':'long'}}}
 config=HOME/'openclaw.json';config.write_text(json.dumps({'agents':{'defaults':defaults},'tools':{'deny':['*']},'gateway':{'mode':'local'}}))
 env.update(OPENCLAW_HOME=str(HOME),OPENCLAW_STATE_DIR=str(HOME),OPENCLAW_CONFIG_PATH=str(config))
elif a.harness=='hermes':
 env['HERMES_HOME']=str(HOME)
 if a.retention=='long':(HOME/'config.yaml').write_text('prompt_caching:\n  cache_ttl: "1h"\n')
(OUT/'protocol.json').write_text(json.dumps({'harness':a.harness,'track':a.track,'model':model,'retention':a.retention,'pauseSeconds':390,'payloadSource':'published per-track workload','mode':'dry-run' if a.dry_run else 'live'},indent=2))

def command(turn,prompt):
 if not a.exe:raise ValueError('--exe is required for a CLI')
 cmd=[a.exe]+([a.entry] if a.entry else [])
 if a.harness=='claude':
  cmd+=['-p','--model',model,'--output-format','json','--tools','','--strict-mcp-config','--setting-sources','','--permission-mode','dontAsk']
  if turn>1:cmd+=['--resume',session]
  return cmd,prompt
 if a.harness=='codex':
  cmd+=['exec']+(['resume',session] if turn>1 else [])+['--skip-git-repo-check','--json','-m',model,'-c','model_reasoning_effort="low"']
  if turn==1:cmd+=['-s','read-only']
  return cmd+['-'],prompt
 if a.harness=='omp':
  cmd+=['-p','--model',('anthropic/' if a.track=='claude' else 'openai-codex/')+model,'--mode','json','--session-dir',str(HOME/'sessions'),'--no-tools','--no-lsp','--no-skills','--no-rules','--no-title','--no-extensions','--thinking','off' if a.track=='claude' else 'low','--max-time','90']
  if turn>1:cmd+=['--continue']
  return cmd+[prompt],''
 if a.harness=='openclaw':return cmd+['agent','--local','--session-id',session,'--model',('anthropic/' if a.track=='claude' else 'openai/')+model,'--thinking','off' if a.track=='claude' else 'low','--message',prompt,'--json','--timeout','90'],''
 raise ValueError('No CLI command')
agent=None
if a.harness=='hermes' and not a.dry_run:
 os.environ.update(env);os.chdir(CWD)
 from run_agent import AIAgent
 options=dict(model=model,provider='anthropic' if a.track=='claude' else 'openai-codex',api_key=env.get('ANTHROPIC_TOKEN') if a.track=='claude' else env.get('CODEX_ACCESS_TOKEN'),max_iterations=1,enabled_toolsets=[],quiet_mode=True,session_id=session,skip_background_review=True,skip_memory=True,skip_context_files=True,reasoning_config={'enabled':False} if a.track=='claude' else {'effort':'low'},run_budget_seconds=90)
 if a.track=='codex':options.update(api_mode='codex_responses',base_url='https://chatgpt.com/backend-api/codex')
 agent=AIAgent(**options)
ended=None
for turn in range(1,6):
 prompt=W['turns'][turn-1]['text'];assert hashlib.sha256(prompt.encode()).hexdigest()==W['turns'][turn-1]['sha256']
 if turn==5 and not a.dry_run:time.sleep(max(0,390-(time.time()-ended)))
 started=time.time();gap=None if ended is None else (started-ended)*1000
 if a.dry_run:
  if a.harness not in ['agnt','hermes']:cmd,_=command(turn,prompt)
  print(json.dumps({'turn':turn,'promptBytes':len(prompt.encode()),'sha256':W['turns'][turn-1]['sha256'],'plannedPause':390 if turn==5 else 0}));continue
 if a.harness=='agnt':
  if not a.base_url:raise ValueError('Supply isolated --base-url, e.g. http://127.0.0.1:PORT')
  token=env.get('AGNT_AUTH_TOKEN');assert token,'AGNT_AUTH_TOKEN required'
  body={'messages':history+[{'role':'user','content':prompt}],'conversationId':session,'provider':'claude-code' if a.track=='claude' else 'openai-codex','model':model,'enabledTools':[],'reasoningEnabled':False,'persistDefault':False,'routingMode':'pinned'}
  request=urllib.request.Request(a.base_url.rstrip('/')+'/api/orchestrator/chat',data=json.dumps(body).encode(),headers={'Authorization':'Bearer '+token,'Content-Type':'application/json'})
  with urllib.request.urlopen(request,timeout=110) as response:raw=response.read().decode()
  events=[]
  for frame in raw.replace('\r\n','\n').split('\n\n'):
   lines=frame.splitlines();typ=next((l[6:].strip() for l in lines if l.startswith('event:')),None);data=next((l[5:].strip() for l in lines if l.startswith('data:')),None)
   if data and data!='[DONE]':events.append((typ,json.loads(data)))
  done=next(d for t,d in events if t=='agent_execution_completed');reply=next(d['content'] for t,d in events if t=='final_content');usage=done['tokenUsage']
  assert done['provider']==body['provider'] and done['model']==model and done['toolCallsCount']==0 and done['status']=='completed'
  public_raw={'completed':done,'reply':reply}
 elif a.harness=='hermes':
  fields=['session_input_tokens','session_cache_read_tokens','session_cache_write_tokens','session_output_tokens','session_api_calls'];before={k:getattr(agent,k,0) for k in fields};result=agent.run_conversation(prompt,conversation_history=history);reply=result['final_response'];history=result['messages'];usage={k:getattr(agent,k,0)-before[k] for k in fields};assert usage['session_api_calls']==1 and not result.get('failed');public_raw={'usage':usage,'reply':reply,'model':agent.model,'cacheTtl':getattr(agent,'_cache_ttl',None)}
 else:
  cmd,stdin=command(turn,prompt)
  process=subprocess.Popen(cmd,stdin=subprocess.PIPE,stdout=subprocess.PIPE,stderr=subprocess.PIPE,cwd=CWD,env=env,text=True,encoding='utf8')
  try:stdout,stderr=process.communicate(stdin,timeout=110)
  except subprocess.TimeoutExpired:
   if os.name=='nt':subprocess.run(['taskkill','/PID',str(process.pid),'/T','/F'],capture_output=True)
   else:process.kill()
   raise RuntimeError('Timed out: discard this sequence; do not retry into a warmed cache')
  if process.returncode:raise RuntimeError('CLI failed; receipt sequence invalid. Inspect local execution, not a favorable retry.')
  if a.harness=='claude':
   public_raw=json.loads(stdout);reply=public_raw['result'];usage=public_raw['usage'];session=public_raw['session_id'];assert not public_raw.get('is_error')
  elif a.harness=='codex':
   events=[json.loads(l) for l in stdout.splitlines() if l.strip()];session=next((e['thread_id'] for e in events if e['type']=='thread.started'),session);usage=next(e['usage'] for e in events if e['type']=='turn.completed');items=[e['item'] for e in events if e['type']=='item.completed'];assert all(i['type'] not in ['command_execution','mcp_tool_call','web_search'] for i in items);reply=''.join(i['text'] for i in items if i['type']=='agent_message');public_raw={'events':events}
  elif a.harness=='omp':
   events=[json.loads(l) for l in stdout.splitlines() if l.startswith('{')];end=next(e for e in events if e['type']=='agent_end');assistant=next(m for m in reversed(end['messages']) if m['role']=='assistant' and m.get('usage'));reply=''.join(b['text'] for b in assistant['content'] if b['type']=='text');usage=assistant['usage'];public_raw={'usage':usage,'model':assistant.get('model'),'provider':assistant.get('provider'),'reply':reply};assert assistant['model']==model
  else:
   public_raw=json.loads(stdout[stdout.index('{'):]);meta=public_raw['meta']['agentMeta'];usage=meta['lastCallUsage'];reply=''.join(p.get('text','') for p in public_raw.get('payloads',[])) or public_raw['meta'].get('finalAssistantVisibleText');assert meta['model']==model
 ended=time.time();assert reply.strip()==f'OK{turn}', 'Fixed reply validation failed'
 row={'harness':a.harness,'track':a.track,'model':model,'turn':turn,'sessionId':session,'startedAt':started,'endedAt':ended,'gapMs':gap,'promptSha256':W['turns'][turn-1]['sha256'],'reply':reply,'usage':usage,'captured':public_raw}
 with open(OUT/f'turn-{turn}.json','x',encoding='utf8') as f:json.dump(row,f,indent=2)
 if a.harness!='hermes':history.extend([{'role':'user','content':prompt},{'role':'assistant','content':reply}])
 print(json.dumps({'turn':turn,'reply':reply,'usage':usage,'gapMs':gap}),flush=True)
print('DRY RUN COMPLETE' if a.dry_run else 'LIVE SEQUENCE COMPLETE')
