import argparse,json,subprocess,time,urllib.request
from pathlib import Path
from urllib.parse import quote
p=argparse.ArgumentParser();p.add_argument('--checkout',required=True);p.add_argument('--origin',required=True);p.add_argument('--browser',required=True);p.add_argument('--session',required=True);p.add_argument('--output',required=True);a=p.parse_args()
out=Path(a.output)/time.strftime('%Y%m%d-%H%M%S');out.mkdir(parents=True)
def cli(*args):
 r=subprocess.run(['node','apps/server/src/bin.ts',*args,'--base-dir','.t3'],cwd=a.checkout,text=True,capture_output=True,check=True);return r.stdout.strip()
def browser(*args):
 r=subprocess.run([a.browser,'--session',a.session,*args],text=True,capture_output=True,check=True);return r.stdout.strip()
project=Path(a.output)/'demo'
prompt='Run sleep 120 using the bash tool with mode async and detach false. Do not wait for it, do not check its status, and reply only: ok'
started=json.loads(cli('thread','start',str(project),prompt,'--provider','acpRegistry_copilot','--model','claude-haiku-5.5','--title','ACP completion proof','--json'))
tid=started['threadId'];environment=json.load(urllib.request.urlopen(a.origin+'/.well-known/t3/environment'))['environmentId']
browser('open',a.origin+'/'+environment+'/'+quote(tid,safe=''));browser('wait','--text','ACP completion proof')
# Hide auxiliary panes so only the synthetic conversation appears in public captures.
snapshot=browser('snapshot','-i')
if 'region "Thread details"' in snapshot:browser('find','role','button','click','--name','Toggle thread details panel')
browser('find','role','button','click','--name','Toggle main sidebar')
browser('record','start',str(out/'interaction.mp4'),'--fps','10')
observations=[]
def observe(label):
 d=json.loads(cli('thread','show',tid,'--json'))
 summary={'label':label,'elapsedSeconds':round(time.monotonic()-start,1),'status':d['thread']['status'],'activeRun':d['thread']['activeRunId'] is not None,'messages':[{'role':m['role'],'text':m['text']} for m in d['messages']]};observations.append(summary);return summary
start=time.monotonic()
while time.monotonic()-start<40:
 d=observe('await reply')
 if any(m['role']=='assistant' and m['text']=='ok' for m in d['messages']):break
 time.sleep(.5)
time.sleep(3)
observe('before follow-up')
send=cli('thread','send',tid,'reply with: second');observations.append({'sendResult':send})
# Both sides are observed for the same interval, well before sleep 120 exits.
time.sleep(20)
observe('20 seconds after follow-up')
(out/'snapshot.txt').write_text(browser('snapshot'))
# Require stable pixels 500 ms apart before retaining a still.
for i in range(12):
 browser('screenshot',str(out/'candidate-a.png'));time.sleep(.5);browser('screenshot',str(out/'candidate-b.png'))
 if (out/'candidate-a.png').read_bytes()==(out/'candidate-b.png').read_bytes():
  (out/'result.png').write_bytes((out/'candidate-b.png').read_bytes());break
browser('record','stop')
(out/'observations.json').write_text(json.dumps(observations,indent=2))
print(json.dumps({'output':str(out),'observations':observations[-3:]},indent=2))
