@topolo/cli 0.13.9 → 0.13.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -275,7 +275,7 @@ def request(lib,operation,params=None):
275
275
  record=read_json(lib.root/'live-bridge.json',{})
276
276
  if not record: raise PackError('This desktop profile has no live bridge. Open it through topolo-agent-pack first.')
277
277
  with socket.socket(socket.AF_UNIX,socket.SOCK_STREAM) as connection:
278
- connection.settimeout(3 if operation=='status' else 60 if operation in {'permissions-configure','owned-roster-read','smoke-start'} else 20)
278
+ connection.settimeout(3 if operation=='status' else 240 if operation.startswith('lane-') else 60 if operation in {'permissions-configure','owned-roster-read','smoke-start'} else 20)
279
279
  try: connection.connect(record['socket'])
280
280
  except OSError as exc: raise PackError('Managed live bridge is unavailable: '+str(exc)) from exc
281
281
  connection.sendall(canonical({'token':record['token'],'operation':operation,**({'params':params} if params is not None else {})})+b'\n')
@@ -315,8 +315,15 @@ class BackendProxy:
315
315
  self.permission_batch=None
316
316
  self._permission_configuration_inputs=None
317
317
  self.smoke_busy=False
318
+ self.lane_busy=False
319
+ self.emit_native=True
320
+ self.lane_connection_binding=None
321
+ self.managed_transport=None
322
+ self._lane_configuration_record=None
318
323
 
319
324
  async def start_child(self):
325
+ if self.lane_connection_binding:
326
+ raise PackError('Managed dynamic lanes require the existing native owner; no alternate app-server startup')
320
327
  active=self.lib.active()
321
328
  if not active.get('runtime'): raise PackError('No selected managed runtime')
322
329
  self.runtime=active['runtime'];env=os.environ.copy()
@@ -359,6 +366,7 @@ class BackendProxy:
359
366
  method=value.get('method');params=value.get('params') or {}
360
367
  if self.approvals:
361
368
  record=self.approvals.capture(value)
369
+ if record:self.dynamic_permission_event(record)
362
370
  if record and record['sourceThreadId']!=record['intakeThreadId'] and record['notificationState']!='delivered':
363
371
  self.schedule_notification(record)
364
372
  if method=='turn/started': self.active_turns.add(params['threadId'])
@@ -370,7 +378,7 @@ class BackendProxy:
370
378
  for row in self.approvals.records():
371
379
  if row['nativeConnectionCurrent'] and row['intakeThreadId']==params['threadId'] and row['notificationState']!='delivered':
372
380
  self.schedule_notification(row)
373
- sys.stdout.buffer.write(line);sys.stdout.buffer.flush()
381
+ if self.emit_native:sys.stdout.buffer.write(line);sys.stdout.buffer.flush()
374
382
 
375
383
  async def stop_child(self):
376
384
  if self.child and self.child.returncode is None:
@@ -406,7 +414,12 @@ class BackendProxy:
406
414
  if 'id' in value and 'method' in value:self.pending.add(str(value['id']))
407
415
  if self.approvals and 'method' not in value and ('result' in value or 'error' in value):self.approvals.sending(value)
408
416
  self.child.stdin.write(canonical(value)+b'\n');await self.child.stdin.drain()
409
- if self.approvals and 'method' not in value and ('result' in value or 'error' in value):self.approvals.settled(value)
417
+ if self.approvals and 'method' not in value and ('result' in value or 'error' in value):
418
+ entry=self.approvals.pending.get(str(value['id']))
419
+ self.approvals.settled(value)
420
+ decision=(value.get('result') or {}).get('decision')
421
+ if entry and decision in {'accept','decline','cancel'}:
422
+ self.dynamic_permission_event(entry['record'],'approved' if decision=='accept' else 'denied')
410
423
  if self.execution and value.get('method')=='initialized':
411
424
  await self.request_catalog()
412
425
 
@@ -417,6 +430,8 @@ class BackendProxy:
417
430
  await self.child.stdin.drain()
418
431
 
419
432
  async def call_native(self,method,params,before_dispatch=None):
433
+ if self.managed_transport:
434
+ return await self.call_managed_native(method,params,before_dispatch)
420
435
  identity='topolo-relay-'+uuid.uuid4().hex
421
436
  request={'id':identity,'method':method,'params':params}
422
437
  if method in {'thread/resume','turn/start'}:
@@ -441,6 +456,49 @@ class BackendProxy:
441
456
  raise PackError('Native intake relay refused; original request remains pending')
442
457
  return reply['result']
443
458
 
459
+ async def call_managed_native(self,method,params,before_dispatch=None):
460
+ identity='topolo-managed-'+uuid.uuid4().hex
461
+ request={'id':identity,'method':method,'params':params}
462
+ intake=next((r for r in self.execution.roles.values() if r['key']==self.lane_connection_binding['intakeKey']),None)
463
+ intake_notification=method=='turn/start' and intake and params.get('threadId')==intake['threadId']
464
+ if method in {'thread/resume','turn/start'}:
465
+ await self.qualify_organization(params['threadId'])
466
+ if not intake_notification:request=self.execution.prepare(request)
467
+ elif set(params)!={'threadId','input'}:
468
+ raise PackError('Original intake native settings must remain unchanged')
469
+ if before_dispatch is not None:before_dispatch()
470
+ self.pending.add(identity)
471
+ try:
472
+ result=await self.managed_transport.evaluate('request',{'method':method,'params':request['params']})
473
+ reply=self.execution.reply({'id':identity,'result':result})
474
+ return reply['result']
475
+ finally:self.pending.discard(identity)
476
+
477
+ async def managed_native_events(self):
478
+ while not self.managed_transport.closed:
479
+ messages=await self.managed_transport.evaluate('next',{},events=True)
480
+ if not isinstance(messages,list):
481
+ raise PackError('Actual managed native event batch differs')
482
+ for value in messages:
483
+ if value.get('method')=='topolo/managed-request-unavailable':
484
+ raise PackError('Actual managed own-child request is unavailable; preserve custody')
485
+ record=self.approvals.capture(value)
486
+ if record:
487
+ self.dynamic_permission_event(record)
488
+ self.schedule_notification(record)
489
+ params=value.get('params') or {};method=value.get('method')
490
+ if method=='turn/started':self.active_turns.add(params['threadId'])
491
+ elif method=='turn/completed':self.active_turns.discard(params['threadId'])
492
+ elif method=='thread/status/changed':
493
+ if params.get('status',{}).get('type')=='active':self.active_turns.add(params['threadId'])
494
+ elif params.get('status',{}).get('type') in {'idle','notLoaded','systemError'}:
495
+ self.active_turns.discard(params['threadId'])
496
+
497
+ async def send_native_approval(self,entry,reply):
498
+ if self.managed_transport:
499
+ return await self.managed_transport.evaluate('approval',{'request':entry['message'],'reply':reply})
500
+ self.child.stdin.write(canonical(reply)+b'\n');await self.child.stdin.drain()
501
+
444
502
  def smoke_current(self,roles,resuming=None):
445
503
  if (not self.execution or self.permission_configuration or self.quiesced or not self.initialized or self.closed or
446
504
  not self.child or self.child.returncode is not None or self.active_turns or self.pending or self.notifications or
@@ -548,6 +606,12 @@ class BackendProxy:
548
606
 
549
607
  async def qualify_organization(self,thread_id):
550
608
  role=self.execution.roles.get(thread_id) if self.execution else None
609
+ record=None
610
+ if role and role.get('dynamicLaneId'):
611
+ from . import lanes
612
+ record=lanes.Registry(self.lib.root,'codex').read(role['dynamicLaneId'])
613
+ if record['plan'].get('continuityTransport')=='native-cli':
614
+ lanes.native_cli_admission(record['plan']['continuity'])
551
615
  if role and not self.permission_configuration and (pathlib.Path(self.runtime)/'permissions-operation.json').exists():
552
616
  raise PackError('Unresolved native permission operation requires reconciliation before owned execution or grants')
553
617
  if not role or not role.get('installationConsent'):
@@ -557,20 +621,31 @@ class BackendProxy:
557
621
  from . import topolo
558
622
  consent=role['installationConsent']
559
623
  agents=self.lib.load(consent['pack'])[0]['agents']
560
- matches=[a for a in agents if '/'.join([a['pack'],a['set'],a['id']])==role['key'] and a['threadId']==role['threadId']]
624
+ matches=[a for a in agents if '/'.join([a['pack'],a['set'],a['id']])==role.get('sourceRoleKey',role['key']) and a['threadId']==role.get('sourceRoleNativeId',role['threadId'])]
561
625
  if len(matches)!=1:
562
626
  raise PackError('Current consent has no unique live owned role')
563
- if role['settings']!=permission_settings(matches[0],role['settings'],consent):
627
+ effective_consent=consent
628
+ if role.get('dynamicVerificationProfile')=='manual':
629
+ effective_consent={**consent,'profile':'manual','overrides':{}}
630
+ if role['settings']!=permission_settings(matches[0],role['settings'],effective_consent):
564
631
  raise PackError('Effective native permission intent differs from the current consent profile')
565
632
  if self.permission_batch and self.permission_batch['consent']==consent:
566
633
  if time.monotonic()>=self.permission_batch['deadline']:
567
634
  raise PackError('Qualified native permission batch expired; reconcile without replay')
568
635
  role['organizationPolicyProof']=self.permission_batch['proof'];return
569
- _,proof=await asyncio.to_thread(topolo.organization_execution_policy,
636
+ remote,proof=await asyncio.to_thread(topolo.organization_execution_policy,
570
637
  topolo.Client(consent['environment'],consent['contextKey']),consent,agents,consent.get('agentWorkspaceId'))
571
638
  from .permissions import authorize_native_scope
572
639
  authorize_native_scope(proof)
640
+ if role.get('dynamicVerificationProfile')=='manual' and 'manual' not in remote['allowedProfiles']:
641
+ raise PackError('Fresh organization policy prohibits the child-only manual verification profile')
573
642
  role['organizationPolicyProof']=proof
643
+ if role.get('dynamicLaneId'):
644
+ current=await self.dynamic_current(record['plan'])
645
+ configuring=self._lane_configuration_record
646
+ if configuring and configuring['nativeId']==thread_id and self.lane_busy:
647
+ self.execution.prepare_dynamic_configuration(configuring,current)
648
+ else:self.execution.qualify_dynamic(record,current)
574
649
 
575
650
  def existing_runtime_intent(self):
576
651
  """Full existing composition only; never use a new composition as migration."""
@@ -588,7 +663,12 @@ class BackendProxy:
588
663
  declared={(r['key'],r['threadId']) for r in document['roles']}
589
664
  with closing(sqlite3.connect('file:'+str(pathlib.Path(self.runtime)/'home/state_5.sqlite')+'?mode=ro',uri=True)) as db:
590
665
  native_ids=[r[0] for r in db.execute('SELECT id FROM threads')]
591
- if len(native_ids)!=len(declared) or set(native_ids)!={tid for _,tid in declared}:
666
+ from .lanes import Registry
667
+ dynamic=[r for r in Registry(self.lib.root,'codex').records() if r['state']!='retired']
668
+ dynamic_ids={r['nativeId'] for r in dynamic}
669
+ if dynamic_ids & {tid for _,tid in declared} or len(dynamic_ids)!=len(dynamic):
670
+ raise PackError('Dynamic identity overlaps the fixed roster')
671
+ if len(native_ids)!=len(declared)+len(dynamic_ids) or set(native_ids)!={tid for _,tid in declared}|dynamic_ids:
592
672
  raise PackError('Native runtime membership differs from full selected composition')
593
673
  if identities(roster)!=declared:
594
674
  raise PackError('Runtime roster differs from selected source membership')
@@ -773,7 +853,7 @@ class BackendProxy:
773
853
  state=await self.call_native('thread/read',{'threadId':record['intakeThreadId'],'includeTurns':False})
774
854
  if state.get('thread',{}).get('status',{}).get('type') not in {'idle','notLoaded'}:
775
855
  self.approvals.notified(record['requestId'],False);return
776
- if record['intakeThreadId'] not in self.execution.verified:
856
+ if not self.managed_transport and record['intakeThreadId'] not in self.execution.verified:
777
857
  await self.call_native('thread/resume',{'threadId':record['intakeThreadId']})
778
858
  self.approvals.live(record['key'],record['intakeThreadId'],record['fingerprint'])
779
859
  # A lost turn/start reply cannot be retried on the next idle event.
@@ -789,8 +869,468 @@ class BackendProxy:
789
869
  finally:self.notifications.discard(record['key'])
790
870
  asyncio.create_task(deliver())
791
871
 
872
+ def dynamic_history(self, identity):
873
+ with closing(sqlite3.connect('file:'+str(pathlib.Path(self.runtime)/'home/state_5.sqlite')+'?mode=ro',uri=True)) as db:
874
+ row=db.execute('SELECT rollout_path FROM threads WHERE id=?',(identity,)).fetchone()
875
+ if not row:
876
+ raise PackError('Native transcript identity is missing')
877
+ path=pathlib.Path(row[0]);OwnedSmoke.owned(path)
878
+ if not path.resolve().is_relative_to(pathlib.Path(self.runtime).resolve()/'home'):
879
+ raise PackError('Native transcript is outside the owned home')
880
+ return hashlib.sha256(path.read_bytes()).hexdigest()
881
+
882
+ def dynamic_permission_event(self, approval, decision=None):
883
+ from . import lanes
884
+ role=self.execution.roles.get(approval['sourceThreadId']) if self.execution else None
885
+ if not role or not role.get('dynamicLaneId'):
886
+ return
887
+ store=lanes.Registry(self.lib.root,'codex');record=store.read(role['dynamicLaneId'])
888
+ if decision is None:
889
+ prior=record.get('permissionRequest')
890
+ if prior and prior['fingerprint']==approval['fingerprint'] and prior['requestId']==str(approval['requestId']):
891
+ return
892
+ changed=lanes.request_permission(record,approval['fingerprint'],str(approval['requestId']),approval['generation'])
893
+ changed['permissionRequest'].update(turnId=approval.get('turnId'),itemId=approval.get('itemId'))
894
+ else:
895
+ changed=lanes.record_decision(record,decision,approval['intakeKey'],approval['fingerprint'],approval['generation'])
896
+ store.write(changed,lanes.digest(record))
897
+
898
+ async def dynamic_outcome(self, record):
899
+ from . import lanes
900
+ if record['state']=='executed-outcome-verified':
901
+ return record
902
+ if record['state']!='permission-decision-forwarded':
903
+ raise PackError('Lane settlement needs a real native request, intake decision and operation outcome')
904
+ request=record['permissionRequest']
905
+ result=await self.call_native('thread/read',{'threadId':record['nativeId'],'includeTurns':True})
906
+ if result.get('thread',{}).get('id')!=record['nativeId']:
907
+ raise PackError('Native outcome thread identity differs')
908
+ turns=[t for t in result.get('thread',{}).get('turns',[]) if t.get('id')==request.get('turnId')]
909
+ if len(turns)!=1 or turns[0].get('status') not in {'completed','failed','interrupted'}:
910
+ raise PackError('Exact native permission turn has not reached a terminal outcome')
911
+ items=[i for i in turns[0].get('items',[]) if i.get('id')==request.get('itemId')]
912
+ if len(items)!=1 or items[0].get('type') not in {'commandExecution','fileChange'}:
913
+ raise PackError('Native permission operation outcome cannot be independently attributed')
914
+ item=items[0];status=item.get('status')
915
+ if record['permissionDecision']=='denied':
916
+ if status!='declined':
917
+ raise PackError('Denied native request has no declined operation ACK')
918
+ state='denied'
919
+ elif status=='completed' and (item['type']!='commandExecution' or item.get('exitCode')==0): state='completed'
920
+ elif status=='failed' or (item['type']=='commandExecution' and status=='completed' and item.get('exitCode')!=0): state='failed'
921
+ else: raise PackError('Native permission outcome is unresolved')
922
+ return lanes.record_outcome(record,{'nativeId':record['nativeId'],'requestFingerprint':request['fingerprint'],
923
+ 'state':state,'verificationDigest':lanes.digest({'turn':turns[0],'item':item})})
924
+
925
+ def lane_idle(self):
926
+ if (not self.execution or not self.approvals or not self.initialized or self.quiesced or self.closed or
927
+ (not self.managed_transport and (not self.child or self.child.returncode is not None)) or
928
+ (self.managed_transport and not self.managed_transport.ready) or self.permission_configuration or self.smoke_busy):
929
+ raise PackError('Dynamic lanes require their current qualified native connection')
930
+
931
+ def dynamic_role(self, row):
932
+ """Collect identity refs from the supported connection, never caller JSON."""
933
+ from . import topolo, lanes
934
+ consent=row.get('installationConsent')
935
+ if (not isinstance(consent,dict) or not isinstance(consent.get('contextKey'),str) or
936
+ not consent['contextKey'].startswith('org:') or consent['contextKey']!=row.get('contextKey') or
937
+ consent.get('environment') not in {'development','staging','production'}):
938
+ raise PackError('Dynamic lanes need an organization-bound source installation')
939
+ pack=row['key'].split('/')[0];logical='/'.join(row['key'].split('/')[1:])
940
+ cwd=pathlib.Path(row['cwd']);home=pathlib.Path(self.runtime).resolve()
941
+ if not cwd.resolve().is_relative_to(home):
942
+ raise PackError('Dynamic source workspace is outside the selected native runtime')
943
+ files=[cwd/'AGENTS.md',cwd/'topolo-connection.json',cwd/'.mcp.json']
944
+ for file in files: OwnedSmoke.owned(file)
945
+ hashes={str(f):hashlib.sha256(f.read_bytes()).hexdigest() for f in files}
946
+ descriptor=read_json(files[1]);binding=read_json(topolo.binding_path(self.lib,pack,consent['environment'],consent['contextKey']))
947
+ roles=[r for r in binding['input']['roles'] if r['logicalId']==logical]
948
+ identity=binding['identity']
949
+ if (len(roles)!=1 or identity['organizationId']!=consent['contextKey'][4:] or
950
+ any(descriptor.get(k)!=v for k,v in {'pack':pack,'role':logical,'environment':consent['environment'],
951
+ 'contextKey':consent['contextKey'],'instanceId':binding['input']['instanceId'],
952
+ 'agentWorkspaceId':binding['agentWorkspaceId'],'bytesWorkspaceId':binding['input']['bytesWorkspaceId'],
953
+ 'agentId':roles[0]['agentId'],'agentVersionId':roles[0]['agentVersionId'],
954
+ 'identity':{key:identity[key] for key in ['userId','organizationId']}}.items())):
955
+ raise PackError('Hosted role, Bytes and organization descriptors differ')
956
+ return {'key':row['key'],'harness':'codex','nativeId':row['threadId'],'cwd':row['cwd'],
957
+ 'settings':row['settings'],'contextDigest':lanes.digest(hashes),'packId':pack,'packRevision':row['sourceRevision'],
958
+ 'contextKey':consent['contextKey'],'organizationId':identity['organizationId'],'environment':consent['environment'],
959
+ 'installationId':binding['input']['instanceId'],'AgentLogicalId':roles[0]['agentId'],
960
+ 'AgentVersionId':roles[0]['agentVersionId'],'AgentWorkspaceId':binding['agentWorkspaceId'],
961
+ 'BytesWorkspaceId':binding['input']['bytesWorkspaceId'],'privateMCPDescriptorHash':hashes[str(files[2])]}
962
+
963
+ def dynamic_model_inputs(self, source):
964
+ """Explicit versioned pack/group/role model constraints; no inferred defaults."""
965
+ path=self.lib.root/'dynamic-models'/(source['packId']+'.json');OwnedSmoke.owned(path)
966
+ document=read_json(path)
967
+ if (set(document)!={'format','schemaVersion','sourceRevision','configuration','constraints','authorizedOverrides'} or
968
+ document['format']!='topolo-pack-dynamic-models' or document['schemaVersion']!=1 or
969
+ document['sourceRevision']!=source['packRevision']):
970
+ raise PackError('Versioned dynamic model configuration is missing for this exact source role')
971
+ from .task_models import role_constraints
972
+ document={**document,'effectiveConstraints':role_constraints(document['constraints'],source['key'])}
973
+ catalog={'harness':'codex','source':'native-account','connectionGeneration':self.approvals.generation,
974
+ 'models':[{'model':m,'efforts':sorted(e)} for m,e in sorted(self.execution.catalog.items())]}
975
+ return document,catalog
976
+
977
+ async def dynamic_current(self, plan):
978
+ self.lane_idle()
979
+ source=self.execution.roles.get(plan['role']['nativeId']);intake=self.execution.roles.get(plan['intake']['nativeId'])
980
+ if not source or not intake or source.get('dynamicLaneId') or intake.get('dynamicLaneId'):
981
+ raise PackError('Dynamic source role or original intake is not currently owned')
982
+ await self.qualify_organization(source['threadId'])
983
+ self.lane_idle()
984
+ role=self.dynamic_role(source);route=self.dynamic_role(intake);proof=source['organizationPolicyProof']
985
+ consent=source['installationConsent']
986
+ ceiling={'policyRevision':proof['revision'],'policyDigest':proof['policyDigest'],
987
+ 'profile':consent.get('overrides',{}).get(source['key'],consent['profile']),'intakeKey':source['intakeKey']}
988
+ document,catalog=self.dynamic_model_inputs(role)
989
+ from . import task_models,lanes
990
+ task=plan['resolution']['task']
991
+ resolution=task_models.resolve(document['configuration'],task,'codex',catalog,document['effectiveConstraints'],
992
+ override_authorized=lanes.digest(task) in document['authorizedOverrides'])
993
+ if resolution!=plan['resolution']:
994
+ raise PackError('Task profile, native catalog, intake override or configuration changed; no fallback')
995
+ return {'role':role,'intake':route,'ceiling':ceiling}
996
+
997
+ async def dynamic_native(self, identity, cwd):
998
+ result=await self.call_native('thread/read',{'threadId':identity,'includeTurns':False})
999
+ thread=result.get('thread',{})
1000
+ with closing(sqlite3.connect('file:'+str(pathlib.Path(self.runtime)/'home/state_5.sqlite')+'?mode=ro',uri=True)) as db:
1001
+ rows=db.execute('SELECT id,cwd,archived FROM threads WHERE id=?',(identity,)).fetchall()
1002
+ if rows!=[(identity,cwd,0)] or thread.get('id')!=identity or thread.get('cwd')!=cwd:
1003
+ raise PackError('Dynamic native descriptor is not the exact active owned runtime identity')
1004
+ return {'nativeId':identity,'cwd':cwd,'nativeParentId':None,'archived':False,'backend':'local',
1005
+ 'foreignWorkspace':False,'busy':thread.get('status',{}).get('type') not in {'idle','notLoaded'},
1006
+ 'pendingApprovals':sum(e['record']['sourceThreadId']==identity for e in self.approvals.pending.values())}
1007
+
1008
+ async def dynamic_context(self,operation,params):
1009
+ from . import lanes
1010
+ import shlex
1011
+ expected_fields={'laneId','operationId'} if operation=='lane-context-start' else {'laneId'}
1012
+ if set(params)!=expected_fields:
1013
+ raise PackError('Closed own-child context operation required')
1014
+ self.lane_idle();store=lanes.Registry(self.lib.root,'codex');record=store.read(params['laneId'])
1015
+ if not record or record['state']!='loaded-context-ACK':
1016
+ raise PackError('Actual native settings and instruction-source ACK required before bootstrap')
1017
+ if record['plan'].get('continuityTransport')=='native-cli':
1018
+ lanes.native_cli_admission(record['plan']['continuity'])
1019
+ current=await self.dynamic_current(record['plan'])
1020
+ if current!={k:record['plan'][k] for k in ['role','intake','ceiling']}:
1021
+ raise PackError('Current bootstrap source or policy differs')
1022
+ native=await self.dynamic_native(record['nativeId'],record['creationReceipt']['cwd'])
1023
+ if native['busy'] or native['pendingApprovals']:
1024
+ return {'state':'awaiting-own-native-idle','native':native}
1025
+ if operation=='lane-context-read':
1026
+ probe=record.get('contextProbe')
1027
+ if not probe or probe['state']!='native-delivered':
1028
+ raise PackError('Exact own bootstrap delivery is unresolved; no replay')
1029
+ result=await self.call_native('thread/read',{'threadId':record['nativeId'],'includeTurns':True})
1030
+ thread=result.get('thread',{});turns=[t for t in thread.get('turns',[]) if t.get('id')==probe['turnId']]
1031
+ if thread.get('id')!=record['nativeId'] or len(turns)!=1 or turns[0].get('status')!='completed' or turns[0].get('error'):
1032
+ raise PackError('Exact same-child bootstrap turn is not completed')
1033
+ reads=[]
1034
+ for item in turns[0].get('items',[]):
1035
+ if item.get('type')!='commandExecution' or item.get('status')!='completed' or item.get('exitCode')!=0:
1036
+ continue
1037
+ command=item.get('command','');args=shlex.split(command) if isinstance(command,str) else []
1038
+ exact=command==probe['command'] or (len(args)==3 and pathlib.Path(args[0]).name in {'sh','zsh','bash'} and args[1] in {'-c','-lc'} and args[2]==probe['command'])
1039
+ if not exact:
1040
+ continue
1041
+ try:value=json.loads(item.get('aggregatedOutput',''))
1042
+ except (ValueError,TypeError):continue
1043
+ if value==probe['expected'] and item.get('id'):
1044
+ reads.append(item['id'])
1045
+ if len(reads)!=1:
1046
+ raise PackError('Actual same-child native file-read command and exact metadata result are unproven')
1047
+ if any(hashlib.sha256(pathlib.Path(path).read_bytes()).hexdigest()!=sha for path,sha in probe['fileHashes'].items()):
1048
+ raise PackError('Own role context changed during native bootstrap')
1049
+ updated={**record,'contextProbe':{k:v for k,v in probe.items() if k not in {'text','command'}}|
1050
+ {'state':'native-file-read-ACK','nativeItemId':reads[0],'proofDigest':lanes.digest({'turnId':probe['turnId'],'itemId':reads[0],'expected':probe['expected']})}}
1051
+ return store.write(updated,lanes.digest(record))
1052
+ if record.get('contextProbe'):
1053
+ raise PackError('Owned native bootstrap is already reserved; no second turn')
1054
+ nonce=OwnedSmoke.operation_id(params['operationId']);cwd=pathlib.Path(record['creationReceipt']['cwd'])
1055
+ guide=cwd/'AGENTS.md';connection=cwd/'topolo-connection.json'
1056
+ for path in [guide,connection]:OwnedSmoke.owned(path)
1057
+ descriptor=read_json(connection);role=record['plan']['role'];principal=descriptor.get('identity',{}).get('userId')
1058
+ if not isinstance(principal,str) or not principal:
1059
+ raise PackError('Current connected role principal is unknown')
1060
+ expected={'operationId':nonce,'nativeId':record['nativeId'],'AgentLogicalId':role['AgentLogicalId'],'AgentVersionId':role['AgentVersionId'],
1061
+ 'AgentWorkspaceId':role['AgentWorkspaceId'],'BytesWorkspaceId':role['BytesWorkspaceId'],'principalId':principal,
1062
+ 'organizationId':role['organizationId'],'environment':role['environment'],'charterSHA256':hashlib.sha256(guide.read_bytes()).hexdigest()}
1063
+ script=("import pathlib,json,hashlib;d=json.loads(pathlib.Path("+repr(str(connection))+").read_text());"
1064
+ "v={'operationId':"+repr(nonce)+",'nativeId':"+repr(record['nativeId'])+",'AgentLogicalId':d['agentId'],'AgentVersionId':d['agentVersionId'],"
1065
+ "'AgentWorkspaceId':d['agentWorkspaceId'],'BytesWorkspaceId':d['bytesWorkspaceId'],'principalId':d['identity']['userId'],"
1066
+ "'organizationId':d['identity']['organizationId'],'environment':d['environment'],'charterSHA256':hashlib.sha256(pathlib.Path("+repr(str(guide))+").read_bytes()).hexdigest()};print(json.dumps(v))")
1067
+ command='python3 -c '+shlex.quote(script)
1068
+ text=('Bounded own-child context bootstrap '+nonce+'. Before role work, read only your own AGENTS.md and topolo-connection.json. '
1069
+ 'Use the native exec_command tool to run exactly this read-only metadata command: '+command+'. '
1070
+ 'It outputs only public role identities and a charter hash. Do not write files, access credentials, use the network, or perform other work. '
1071
+ 'Return the same JSON object. This file-read proof does not verify private MCP connectivity.')
1072
+ probe={'operationId':nonce,'expected':expected,'command':command,'text':text,'state':'start-may-have-effects',
1073
+ 'fileHashes':{str(p):hashlib.sha256(p.read_bytes()).hexdigest() for p in [guide,connection]},
1074
+ 'generation':record['nativeConnectionGeneration'],'privateMCPVerified':False}
1075
+ held={**record,'contextProbe':probe};store.write(held,lanes.digest(record))
1076
+ try:
1077
+ self.execution.qualify_dynamic(record,current)
1078
+ result=await self.call_native('turn/start',{'threadId':record['nativeId'],'input':[{'type':'text','text':text,'text_elements':[]}]})
1079
+ turn_id=result.get('turn',{}).get('id')
1080
+ if not isinstance(turn_id,str) or not turn_id:
1081
+ raise PackError('Actual bootstrap turn acknowledgement is missing')
1082
+ updated={**held,'contextProbe':{**probe,'state':'native-delivered','turnId':turn_id}}
1083
+ store.write(updated,lanes.digest(held));return {'state':'own-context-bootstrap-delivered','turnId':turn_id,'operationId':nonce}
1084
+ except BaseException:
1085
+ store.write({**held,'contextProbe':{**probe,'state':'uncertain-effect-hold'}},lanes.digest(held));raise
1086
+
1087
+ async def dynamic_fixture(self,operation,params):
1088
+ from . import lanes
1089
+ expected={'laneId','operationId'} if operation=='lane-fixture-start' else {'laneId'}
1090
+ if set(params)!=expected:
1091
+ raise PackError('Closed own-lane fixture operation required')
1092
+ self.lane_idle();store=lanes.Registry(self.lib.root,'codex');record=store.read(params['laneId'])
1093
+ if not record or record['plan'].get('verificationProfile')!='manual':
1094
+ raise PackError('Actual routing proof requires the bound two-child manual verification plan')
1095
+ if record['plan'].get('continuityTransport')=='native-cli':
1096
+ lanes.native_cli_admission(record['plan']['continuity'])
1097
+ current=await self.dynamic_current(record['plan'])
1098
+ if current!={k:record['plan'][k] for k in ['role','intake','ceiling']}:
1099
+ raise PackError('Current source/intake/policy changed before own fixture verification')
1100
+ if record.get('contextProbe',{}).get('state')!='native-file-read-ACK':
1101
+ raise PackError('Actual own-child native file-read bootstrap is required before fixture work')
1102
+ if operation=='lane-fixture-read':
1103
+ fixture=record.get('fixtureVerification')
1104
+ if not fixture or fixture['state']!='native-delivered':
1105
+ raise PackError('Own native fixture delivery is unresolved; no replay')
1106
+ native=await self.dynamic_native(record['nativeId'],record['creationReceipt']['cwd'])
1107
+ if native['busy'] or native['pendingApprovals']:
1108
+ return {'state':record['state'],'nativeBusy':native['busy'],'pendingApprovals':native['pendingApprovals']}
1109
+ verified=await self.dynamic_outcome(record)
1110
+ if verified['outcome']['state']=='completed':
1111
+ path=pathlib.Path(fixture['path']);OwnedSmoke.owned(path)
1112
+ if read_json(path)!=fixture['expected']:
1113
+ raise PackError('Real native item result is not the exact own nonce-file bytes')
1114
+ verified['fixtureVerification']={k:v for k,v in fixture.items() if k!='text'}|{'state':'outcome-verified','fileSHA256':hashlib.sha256(path.read_bytes()).hexdigest()}
1115
+ elif verified['outcome']['state']=='denied':
1116
+ if pathlib.Path(fixture['path']).exists():
1117
+ raise PackError('Denied request has an unexpected fixture file; retain unresolved effects')
1118
+ settled=lanes.settle(verified,native);return store.write(settled,lanes.digest(record))
1119
+ nonce=OwnedSmoke.operation_id(params['operationId'])
1120
+ if record['state']!='loaded-context-ACK' or record.get('fixtureVerification'):
1121
+ raise PackError('Own fixture requires current loaded context and one fresh bounded operation')
1122
+ native=await self.dynamic_native(record['nativeId'],record['creationReceipt']['cwd'])
1123
+ if native['busy'] or native['pendingApprovals']:
1124
+ raise PackError('Own fixture child is not idle')
1125
+ role=record['plan']['role'];path=pathlib.Path(record['creationReceipt']['cwd'])/('.topolo-dynamic-'+record['laneId']+'-'+nonce+'.json')
1126
+ if path.exists() or path.is_symlink():
1127
+ raise PackError('Own fixture path already exists; never overwrite')
1128
+ body={'operationId':nonce,'laneId':record['laneId'],'nativeId':record['nativeId'],'AgentVersionId':role['AgentVersionId'],
1129
+ 'AgentWorkspaceId':role['AgentWorkspaceId'],'BytesWorkspaceId':role['BytesWorkspaceId']}
1130
+ text=('Topolo bounded own-worker verification '+nonce+'. Read the already loaded role charter and connected Agent/Bytes identity. '
1131
+ 'Use the native apply_patch/file-change tool to create only '+str(path)+' with exactly this JSON: '+json.dumps(body,separators=(',',':'))+'. '
1132
+ 'Request native permission through the original intake; do not bypass policy or auto-approve. Read back only your fixture and report its nonce and status. '
1133
+ 'Do not edit source, business content, other workspaces, credentials, global settings, provider resources or external messages.')
1134
+ fixture={'operationId':nonce,'state':'start-may-have-effects','path':str(path),'expected':body,'text':text,
1135
+ 'generation':self.approvals.generation}
1136
+ prepared={**record,'fixtureVerification':fixture};store.write(prepared,lanes.digest(record))
1137
+ try:
1138
+ await self.call_native('thread/resume',{'threadId':record['nativeId']})
1139
+ result=await self.call_native('turn/start',{'threadId':record['nativeId'],'input':[{'type':'text','text':text,'text_elements':[]}]})
1140
+ turn=result.get('turn',{}).get('id')
1141
+ if not isinstance(turn,str) or not turn:
1142
+ raise PackError('Native fixture turn has no actual identity')
1143
+ latest=store.read(record['laneId']);updated={**latest,'fixtureVerification':{**fixture,'state':'native-delivered','turnId':turn}}
1144
+ return store.write(updated,lanes.digest(latest))
1145
+ except BaseException:
1146
+ latest=store.read(record['laneId']);store.write({**latest,'fixtureVerification':{**fixture,'state':'uncertain'}},lanes.digest(latest));raise
1147
+
1148
+ async def dynamic_create(self,params):
1149
+ """Root-invoked supported ThreadStart on this exact owned native home.
1150
+
1151
+ No requested task or turn is started. A possible-effect creation is
1152
+ durably reserved before dispatch and never automatically replayed.
1153
+ """
1154
+ from . import lanes
1155
+ if not self.lane_connection_binding or set(params)!={'laneId'} or params['laneId']!=self.lane_connection_binding['laneId']:
1156
+ raise PackError('Supported native creation requires the exact explicit owned Root lane connection')
1157
+ self.lane_idle();store=lanes.Registry(self.lib.root,'codex');planned=store.read_plan(params['laneId'])
1158
+ if store.read(params['laneId']) is not None:
1159
+ raise PackError('This lane already has an actual native identity')
1160
+ if planned.get('continuityTransport')=='native-cli':
1161
+ lanes.native_cli_admission(planned['continuity'])
1162
+ current=await self.dynamic_current(planned)
1163
+ if current!={k:planned[k] for k in ['role','intake','ceiling']}:
1164
+ raise PackError('Source, intake or policy changed before native creation')
1165
+ await asyncio.to_thread(managed_lane_frontend,self.lib,self.runtime,planned)
1166
+ # Independent fixed-ID reads before the single fresh worker is spent.
1167
+ for role in [current['role'],current['intake']]:await self.dynamic_native(role['nativeId'],role['cwd'])
1168
+ directory=store.directory/'creation';directory.mkdir(mode=0o700,exist_ok=True);store._owned(directory,True)
1169
+ marker=directory/(planned['laneId']+'.json')
1170
+ if marker.exists() or marker.is_symlink():
1171
+ raise PackError('Native creation already reserved; reconcile without replay')
1172
+ fd=os.open(marker,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600)
1173
+ phase={'format':'topolo-owned-native-creation','schemaVersion':1,'laneId':planned['laneId'],
1174
+ 'planDigest':lanes.digest(planned),'state':'may-have-effects','nativeId':None,'generation':self.approvals.generation}
1175
+ with os.fdopen(fd,'wb') as stream:stream.write(canonical(phase)+b'\n');stream.flush();os.fsync(stream.fileno())
1176
+ spec=dict(planned['role']['settings'])
1177
+ if planned.get('verificationProfile')=='manual':
1178
+ from .permissions import apply_settings
1179
+ source=planned['role'];spec=apply_settings({'pack':source['packId'],'set':source['key'].split('/')[1],'id':source['key'].split('/')[2]},
1180
+ 'codex',spec,{'profile':'manual','overrides':{},'intakeRoutes':{source['key']:planned['intake']['key']}})
1181
+ choice=planned['resolution']['resolved'];spec.update(model=choice['model'],model_reasoning_effort=choice['effort'])
1182
+ guide=pathlib.Path(planned['role']['cwd'])/'AGENTS.md';charter=guide.read_text()
1183
+ if len(charter.encode())>131072:
1184
+ raise PackError('Reviewed role charter exceeds bounded native creation context')
1185
+ try:
1186
+ result=await self.call_native('thread/start',{'cwd':planned['role']['cwd'],'model':choice['model'],
1187
+ 'allowProviderModelFallback':False,
1188
+ 'approvalPolicy':spec['approval_policy'],'sandbox':spec['sandbox_mode'],
1189
+ 'config':{'model_reasoning_effort':choice['effort'],'sandbox_workspace_write':spec['sandbox_workspace_write'],'apps':spec['apps']},
1190
+ 'developerInstructions':charter})
1191
+ thread=result.get('thread',{});identity=thread.get('id')
1192
+ if not isinstance(identity,str) or not identity or identity in planned['nativeIdsBeforeCreation']:
1193
+ raise PackError('Native ThreadStart has no proven fresh created identity')
1194
+ phase={**phase,'nativeId':identity};atomic_json(marker,phase)
1195
+ native=await self.dynamic_native(identity,planned['role']['cwd'])
1196
+ if native['busy'] or native['pendingApprovals'] or await self.dynamic_current(planned)!=current:
1197
+ raise PackError('Actual created child placement, idle state or current context differs')
1198
+ frontend=await asyncio.to_thread(managed_lane_frontend,self.lib,self.runtime,planned,identity)
1199
+ receipt={'format':'topolo-native-lane-created','schemaVersion':1,'state':'ready','planDigest':lanes.digest(planned),
1200
+ 'harness':'codex','nativeId':identity,'logicalOriginThreadId':planned['logicalOriginThreadId'],'cwd':native['cwd'],
1201
+ 'model':result.get('model'),'effort':result.get('reasoningEffort'),'nativeParentId':None,
1202
+ 'fresh':True,'creationSource':'supported-native-host-tool'}
1203
+ if receipt['model']!=choice['model'] or receipt['effort']!=choice['effort']:
1204
+ raise PackError('Native creation model/effort tuple differs; no fallback or task dispatch')
1205
+ record=lanes.adopt(planned,receipt,native,current)
1206
+ record.update(frontendVisibility=frontend['state'],frontendProof=frontend)
1207
+ record=store.write(record,None)
1208
+ atomic_json(marker,{**phase,'state':'ready','receipt':receipt,'modelTurnsStarted':False,'nativeParentAvailability':'UNAVAILABLE'})
1209
+ return {'state':'created-adopted-unconfigured','receipt':receipt,'modelTurnsStarted':False,'nativeParentAvailability':'UNAVAILABLE'}
1210
+ except BaseException:
1211
+ atomic_json(marker,{**phase,'state':'uncertain-effect-hold','replayAuthorized':False});raise
1212
+
1213
+ async def dynamic_lane(self, operation, params):
1214
+ from . import lanes,task_models,policy
1215
+ requested=params.get('request',params.get('plan',{})) if isinstance(params,dict) else {}
1216
+ if isinstance(requested,dict) and (requested.get('continuityTransport')=='native-cli' or requested.get('schemaVersion')==2 or 'continuity' in requested):
1217
+ lanes.native_cli_admission(requested.get('continuity'))
1218
+ self.lane_idle()
1219
+ if self.lane_busy:
1220
+ raise PackError('An owned dynamic lane operation is already active')
1221
+ self.lane_busy=True;store=lanes.Registry(self.lib.root,'codex');changed=False;record=None
1222
+ try:
1223
+ if operation=='lane-plan':
1224
+ if set(params)!={'request'}:
1225
+ raise PackError('Lane plan accepts only its exact intake request')
1226
+ request=params['request'];source=next((r for r in self.execution.roles.values() if r['key']==request['sourceRoleKey']),None)
1227
+ if not source or source.get('dynamicLaneId'):
1228
+ raise PackError('Exact fixed source role required')
1229
+ intake=next((r for r in self.execution.roles.values() if r['key']==source['intakeKey']),None)
1230
+ if not intake:
1231
+ raise PackError('Original native intake is missing')
1232
+ await self.qualify_organization(source['threadId'])
1233
+ self.execution.set_catalog(await self.read_catalog())
1234
+ role=self.dynamic_role(source);route=self.dynamic_role(intake);document,catalog=self.dynamic_model_inputs(role)
1235
+ choice=policy.dynamic_task_settings(document['configuration'],request['task'],'codex',catalog,
1236
+ document['effectiveConstraints'],override_authorized=lanes.digest(request['task']) in document['authorizedOverrides'])
1237
+ proof=source['organizationPolicyProof'];consent=source['installationConsent']
1238
+ if request.get('verificationProfile')=='manual':
1239
+ from . import topolo
1240
+ remote,_=await asyncio.to_thread(topolo.organization_execution_policy,topolo.Client(consent['environment'],consent['contextKey']),
1241
+ consent,self.lib.load(role['packId'])[0]['agents'],consent.get('agentWorkspaceId'))
1242
+ if 'manual' not in remote['allowedProfiles']:
1243
+ raise PackError('Fresh organization policy does not allow the narrower test profile')
1244
+ plan=lanes.plan(request,role,route,choice,{'policyRevision':proof['revision'],'policyDigest':proof['policyDigest'],
1245
+ 'profile':consent.get('overrides',{}).get(source['key'],consent['profile']),'intakeKey':source['intakeKey']})
1246
+ with closing(sqlite3.connect('file:'+str(pathlib.Path(self.runtime)/'home/state_5.sqlite')+'?mode=ro',uri=True)) as db:
1247
+ plan['nativeIdsBeforeCreation']=sorted(r[0] for r in db.execute('SELECT id FROM threads'))
1248
+ return store.save_plan(plan)
1249
+ if operation=='lane-adopt':
1250
+ if set(params)!={'plan','receipt'}:
1251
+ raise PackError('Lane adoption requires exact plan and native creation receipt')
1252
+ plan=params['plan'];receipt=params['receipt']
1253
+ if store.read_plan(plan['laneId'])!=plan:
1254
+ raise PackError('Creation plan differs from the owned immutable native snapshot')
1255
+ if receipt['nativeId'] in plan.get('nativeIdsBeforeCreation',[]):
1256
+ raise PackError('Native lane was already present at planning; fresh creation not proven')
1257
+ if receipt['cwd']!=plan['role']['cwd']:
1258
+ raise PackError('Dynamic lane must use the reviewed source role context workspace')
1259
+ current=await self.dynamic_current(plan)
1260
+ native=await self.dynamic_native(receipt['nativeId'],receipt['cwd'])
1261
+ if native['busy'] or native['pendingApprovals']:
1262
+ raise PackError('Created native lane must be idle before adoption')
1263
+ record=lanes.adopt(plan,receipt,native,current)
1264
+ return store.write(record,None)
1265
+ if set(params)!={'laneId'}:
1266
+ raise PackError('Lane operation accepts only its exact registered identity')
1267
+ record=store.read(params['laneId'])
1268
+ if not record:
1269
+ raise PackError('No owned dynamic lane registration')
1270
+ if record['plan'].get('continuityTransport')=='native-cli':
1271
+ lanes.native_cli_admission(record['plan']['continuity'])
1272
+ current=await self.dynamic_current(record['plan'])
1273
+ if current!={k:record['plan'][k] for k in ['role','intake','ceiling']}:
1274
+ raise PackError('Dynamic source, intake or policy drift; no native mutation')
1275
+ native=await self.dynamic_native(record['nativeId'],record['creationReceipt']['cwd'])
1276
+ if operation=='lane-read':
1277
+ return {'record':record,'native':native,'proof':'current-own-native-read'}
1278
+ if native['busy'] or native['pendingApprovals']:
1279
+ raise PackError('Dynamic lane is busy or has a pending original approval')
1280
+ if operation=='lane-configure':
1281
+ self.execution.prepare_dynamic_configuration(record,current)
1282
+ self.approvals.register_dynamic(self.execution.roles[record['nativeId']])
1283
+ # Persist uncertainty before the first actual settings effect.
1284
+ before=lanes.digest(record);hold={**record,'state':'uncertain-effect-hold'}
1285
+ store.write(hold,before);changed=True
1286
+ self._lane_configuration_record=record
1287
+ result=await self.call_native('thread/resume',{'threadId':record['nativeId']})
1288
+ actual={'model':result.get('model'),'effort':result.get('reasoningEffort')}
1289
+ source=record['plan']['role'];document,catalog=self.dynamic_model_inputs(source)
1290
+ choice=task_models.acknowledge(record['plan']['resolution'],actual,configuration=document['configuration'],
1291
+ catalog=catalog,constraints=document['effectiveConstraints'])
1292
+ configured=lanes.acknowledge_settings(record,actual,choice,self.approvals.generation)
1293
+ guide=str(pathlib.Path(source['cwd'])/'AGENTS.md')
1294
+ if guide not in result.get('instructionSources',[]):
1295
+ raise PackError('Actual native loaded instruction sources omit the owned role charter')
1296
+ if await self.dynamic_current(record['plan'])!=current:
1297
+ raise PackError('Context or policy changed during native settings application')
1298
+ configured=lanes.acknowledge_context(configured,source['contextDigest'])
1299
+ self.execution.qualify_dynamic(configured,current)
1300
+ return store.write(configured,lanes.digest(hold))
1301
+ if operation=='lane-settle':
1302
+ verified=await self.dynamic_outcome(record)
1303
+ return store.write(lanes.settle(verified,native),lanes.digest(record))
1304
+ if operation=='lane-retire':
1305
+ if record['state']!='settled':
1306
+ raise PackError('Only a settled verified child can archive')
1307
+ # Archive through native API; preserve the durable possible-effect state.
1308
+ history=self.dynamic_history(record['nativeId'])
1309
+ hold={**record,'state':'uncertain-effect-hold'};store.write(hold,lanes.digest(record));changed=True
1310
+ await self.call_native('thread/archive',{'threadId':record['nativeId']})
1311
+ with closing(sqlite3.connect('file:'+str(pathlib.Path(self.runtime)/'home/state_5.sqlite')+'?mode=ro',uri=True)) as db:
1312
+ archived=db.execute('SELECT archived FROM threads WHERE id=?',(record['nativeId'],)).fetchone()
1313
+ if archived!=(1,):
1314
+ raise PackError('Native archive readback is unavailable; preserve uncertainty')
1315
+ if self.dynamic_history(record['nativeId'])!=history:
1316
+ raise PackError('Native archived transcript bytes differ; history preservation is unproven')
1317
+ retired=lanes.retire(record,{**native,'archived':True,'historyPreserved':True})
1318
+ self.approvals.retire_dynamic(record['nativeId']);self.execution.retire_dynamic(record['nativeId'])
1319
+ return store.write(retired,lanes.digest(hold))
1320
+ raise PackError('Unsupported closed lane operation')
1321
+ except BaseException:
1322
+ if changed and record and record['nativeId'] in self.execution.roles:
1323
+ self.execution.roles[record['nativeId']]['dynamicCurrentQualified']=False
1324
+ self.execution.verified.discard(record['nativeId'])
1325
+ raise
1326
+ finally:
1327
+ self._lane_configuration_record=None
1328
+ self.lane_busy=False
1329
+
792
1330
  def status(self):
793
- return {'pid':os.getpid(),'childPid':self.child.pid if self.child else None,'desktopPid':os.getppid(),'runtime':self.runtime,
1331
+ return {'pid':os.getpid(),'childPid':self.child.pid if self.child else None,'desktopPid':None if self.managed_transport else os.getppid(),'runtime':self.runtime,
1332
+ 'nativeTransport':'existing-managed-window' if self.managed_transport else 'owned-stdio',
1333
+ 'managedTargetId':self.managed_transport.target_id if self.managed_transport else None,
794
1334
  'quiesced':self.quiesced,'initialized':self.initialized,
795
1335
  'activeTurns':sorted(self.active_turns),'pendingRequests':len(self.pending),
796
1336
  'executionPolicy':self.execution.status() if self.execution else {'state':'not-configured'},
@@ -799,17 +1339,53 @@ class BackendProxy:
799
1339
  async def control(self,reader,writer):
800
1340
  try:
801
1341
  message=json.loads(await asyncio.wait_for(reader.readline(),5))
802
- if not secrets.compare_digest(message.get('token',''),self.token): raise PackError('Live bridge authentication failed')
1342
+ if not secrets.compare_digest(message.get('token',''),self.token):
1343
+ raise PackError('Live bridge authentication failed')
803
1344
  op=message.get('operation')
1345
+ if self.lane_connection_binding:
1346
+ scoped=self.lane_connection_binding;params=message.get('params') or {}
1347
+ if op.startswith('lane-'):
1348
+ lane=params.get('request',params.get('plan',params)).get('laneId')
1349
+ if lane!=scoped['laneId']:
1350
+ raise PackError('Operation is outside the exact owned lane connection')
1351
+ if op=='lane-plan' and params['request']['sourceRoleKey']!=scoped['sourceRoleKey']:
1352
+ raise PackError('Source role differs from the connection binding')
1353
+ elif op in {'approval-details','approval-respond'}:
1354
+ entry=self.approvals.live(params['key'],params['intakeThreadId'],params['fingerprint'])
1355
+ role=self.execution.roles.get(entry['record']['sourceThreadId'],{})
1356
+ if role.get('dynamicLaneId')!=scoped['laneId']:
1357
+ raise PackError('Approval is outside the exact owned lane')
1358
+ elif op not in {'status','approval-list'}:
1359
+ raise PackError('Operation is outside the foreground lane connection')
804
1360
  if op=='quiesce':
805
1361
  if self.permission_configuration:
806
1362
  raise PackError('Permission configuration in progress; preserve the owning native connection')
807
- if self.active_turns: raise PackError('Outgoing agents have active turns. Wait for their current turns; Codex can stay open.')
808
- if self.pending: raise PackError('Backend requests are in flight. Retry the switch; Codex can stay open.')
1363
+ if self.active_turns:
1364
+ raise PackError('Outgoing agents have active turns. Wait for their current turns; Codex can stay open.')
1365
+ if self.pending:
1366
+ raise PackError('Backend requests are in flight. Retry the switch; Codex can stay open.')
809
1367
  self.quiesced=True
810
1368
  await self.stop_child()
811
1369
  elif op=='resume':
812
- if self.quiesced: raise PackError('Use the desktop reconnect action to resume a quiesced bridge')
1370
+ if self.quiesced:
1371
+ raise PackError('Use the desktop reconnect action to resume a quiesced bridge')
1372
+ elif op in {'lane-context-start','lane-context-read'}:
1373
+ result=await self.dynamic_context(op,message.get('params') or {})
1374
+ elif op in {'lane-fixture-start','lane-fixture-read'}:
1375
+ result=await self.dynamic_fixture(op,message.get('params') or {})
1376
+ elif op=='lane-create':
1377
+ result=await self.dynamic_create(message.get('params') or {})
1378
+ elif op=='lane-close':
1379
+ from .lanes import Registry
1380
+ params=message.get('params') or {}
1381
+ if set(params)!={'laneId'} or not self.lane_connection_binding:
1382
+ raise PackError('Explicit foreground own-lane close required')
1383
+ record=Registry(self.lib.root,'codex').read(params['laneId'])
1384
+ if not record or record['state']!='retired' or self.active_turns or self.pending or self.notifications or self.approvals.pending:
1385
+ raise PackError('Own lane must be retired with no pending native work before normal connection closure')
1386
+ self.lane_close_event.set();result={'state':'normal-own-connection-close-requested'}
1387
+ elif op in {'lane-plan','lane-adopt','lane-configure','lane-read','lane-settle','lane-retire'}:
1388
+ result=await self.dynamic_lane(op,message.get('params') or {})
813
1389
  elif op=='owned-roster-read':
814
1390
  result=await self.owned_roster_read(message.get('params') or {})
815
1391
  elif op in {'smoke-start','smoke-read'}:
@@ -836,10 +1412,15 @@ class BackendProxy:
836
1412
  await self.qualify_organization(entry['record']['sourceThreadId'])
837
1413
  self.approvals.live(params['key'],params['intakeThreadId'],params['fingerprint'])
838
1414
  self.approvals.sending(reply)
839
- self.child.stdin.write(canonical(reply)+b'\n');await self.child.stdin.drain();self.approvals.settled(reply)
1415
+ await self.send_native_approval(entry,reply);self.approvals.settled(reply)
1416
+ decision=reply['result'].get('decision')
1417
+ if decision in {'accept','decline','cancel'}:
1418
+ self.dynamic_permission_event(entry['record'],'approved' if decision=='accept' else 'denied')
840
1419
  result={'state':'native-response-forwarded','operationEffectVerified':False}
841
- elif op!='status': raise PackError('Unknown live bridge operation')
842
- if op not in {'approval-list','approval-details','approval-respond','permissions-configure','owned-roster-read','smoke-start','smoke-read'}:result=self.status()
1420
+ elif op!='status':
1421
+ raise PackError('Unknown live bridge operation')
1422
+ if op not in {'lane-context-start','lane-context-read','lane-fixture-start','lane-fixture-read','lane-create','lane-close','lane-plan','lane-adopt','lane-configure','lane-read','lane-settle','lane-retire','approval-list','approval-details','approval-respond','permissions-configure','owned-roster-read','smoke-start','smoke-read'}:
1423
+ result=self.status()
843
1424
  except Exception as exc: result={'error':str(exc)}
844
1425
  writer.write(canonical(result)+b'\n');await writer.drain();writer.close();await writer.wait_closed()
845
1426
 
@@ -869,6 +1450,250 @@ class BackendProxy:
869
1450
  if record.get('pid')==os.getpid():
870
1451
  (self.lib.root/'live-bridge.json').unlink(missing_ok=True);self.path.unlink(missing_ok=True)
871
1452
 
1453
+ class ManagedNativeTransport:
1454
+ """One captured, current desktop manager owns every native lane effect."""
1455
+ def __init__(self,lib,runtime,lane_id):
1456
+ self.lib=lib;self.runtime=runtime;self.lane_id=lane_id;self.token=secrets.token_hex(32)
1457
+ self.ws=None;self.events_ws=None;self.ready=False;self.closed=False;self.subscriptions_closed=False;self.lock=asyncio.Lock()
1458
+ self.scope=None;self.target_id=None
1459
+
1460
+ async def open(self,planned):
1461
+ self.scope=await asyncio.to_thread(managed_lane_frontend,self.lib,self.runtime,planned)
1462
+ self.ws,target=await asyncio.to_thread(renderer,self.lib)
1463
+ self.events_ws,event_target=await asyncio.to_thread(renderer,self.lib)
1464
+ if target!=self.scope['targetId'] or event_target!=target:
1465
+ raise PackError('Existing managed renderer identity changed before connection')
1466
+ self.target_id=target
1467
+ await self.evaluate('open',{'homes':[self.scope['home']],'source':planned['role'],'intake':planned['intake'],
1468
+ 'adapter':self.scope['capability']})
1469
+ self.ready=True
1470
+
1471
+ def expression(self,action,value):
1472
+ params={'action':action,'value':value,'laneId':self.lane_id,'token':self.token}
1473
+ return """(async()=>{
1474
+ const p=PARAMS,key=Symbol.for('topolo.agent-pack.managed-native.v1');
1475
+ let registry=window[key];
1476
+ if(p.action==='open'){
1477
+ window[key]??=new Map();registry=window[key];
1478
+ if(registry.has(p.laneId))throw new Error('Owned managed connection exists; no replay or takeover');
1479
+ const v=p.value,services=(await import(v.adapter.module))[v.adapter.export];
1480
+ const opened=await services?.appServerManagers?.open('local');
1481
+ if(opened?.status!=='ready')throw new Error('Existing managed native backend unavailable');
1482
+ const manager=opened.manager,home=await manager.getCodexHome();
1483
+ if(!v.homes.includes(home))throw new Error('Managed native home differs');
1484
+ const state={manager,home,source:v.source,intake:v.intake,token:p.token,childId:null,creationReserved:false,
1485
+ queue:[],waiters:[],subscriptions:[],closed:false};
1486
+ registry.set(p.laneId,state);
1487
+ const push=value=>{if(state.closed)return;if(state.queue.length>=1024){state.closed=true;return;}
1488
+ state.queue.push(value);state.waiters.splice(0).forEach(f=>f());};
1489
+ state.subscriptions.push(await manager.subscribe({type:'event',key:{hostId:'local'},event:'approval-request',listener:async event=>{
1490
+ if(event.conversationId!==state.childId)return;
1491
+ const conversation=await manager.getConversation(state.childId);
1492
+ const request=conversation?.requests?.find(r=>r.id===event.requestId);
1493
+ if(request&&request.params?.threadId===state.childId&&['item/fileChange/requestApproval','item/commandExecution/requestApproval'].includes(request.method))
1494
+ push(request);
1495
+ else push({method:'topolo/managed-request-unavailable',params:{threadId:state.childId}});
1496
+ }}));
1497
+ state.subscriptions.push(await manager.subscribe({type:'notification',key:{hostId:'local'},
1498
+ methods:['turn/started','turn/completed','thread/status/changed'],listener:notification=>{
1499
+ if([state.childId,state.intake.nativeId].includes(notification.params?.threadId))push(notification);
1500
+ }}));
1501
+ return {state:'managed-owning-connection-ready',home};
1502
+ }
1503
+ const s=registry?.get(p.laneId);
1504
+ if(p.action==='close'){
1505
+ if(s&&s.token!==p.token)throw new Error('Foreign managed native connection must be preserved');
1506
+ if(s){s.closed=true;s.waiters.splice(0).forEach(f=>f());
1507
+ for(const subscription of s.subscriptions)await subscription.unsubscribe();registry.delete(p.laneId);}
1508
+ return {state:'normal-own-managed-subscriptions-closed',nativeOwnerStopped:false};
1509
+ }
1510
+ if(!s||s.token!==p.token||s.closed)throw new Error('Original managed native connection unavailable; no fallback');
1511
+ if(await s.manager.getCodexHome()!==s.home)throw new Error('Captured managed native instance/home changed');
1512
+ if(p.action==='request'){
1513
+ const {method,params}=p.value;
1514
+ const allowed=['model/list','thread/read','thread/start','thread/resume','thread/archive','turn/start'];
1515
+ if(!allowed.includes(method))throw new Error('Unsupported closed managed native method');
1516
+ const id=params.threadId;
1517
+ if(method==='thread/start'){
1518
+ if(s.creationReserved||params.cwd!==s.source.cwd)throw new Error('Managed native creation is reserved or placement differs');
1519
+ s.creationReserved=true;
1520
+ }else if(method!=='model/list'){
1521
+ if(id!==s.childId&&!(method==='thread/read'&&[s.source.nativeId,s.intake.nativeId].includes(id))&&
1522
+ !(method==='turn/start'&&id===s.intake.nativeId))throw new Error('Managed native request is outside the own child/intake');
1523
+ if(method==='turn/start'&&id===s.intake.nativeId&&Object.keys(params).some(k=>!['threadId','input'].includes(k)))
1524
+ throw new Error('Original intake settings must remain unchanged');
1525
+ }
1526
+ const result=await s.manager.sendRequest(method,params);
1527
+ if(method==='thread/start'){
1528
+ const id=result?.thread?.id;
1529
+ if(typeof id!=='string'||[s.source.nativeId,s.intake.nativeId].includes(id))throw new Error('Fresh managed native identity unavailable');
1530
+ s.childId=id;
1531
+ }
1532
+ return result;
1533
+ }
1534
+ if(p.action==='approval'){
1535
+ const {request,reply}=p.value;
1536
+ if(request.params?.threadId!==s.childId||request.id!==reply.id||!['accept','decline','cancel'].includes(reply.result?.decision))
1537
+ throw new Error('Exact original own-child native approval required');
1538
+ const current=await s.manager.getConversation(s.childId);
1539
+ const actual=current?.requests?.find(r=>r.id===request.id);
1540
+ const canonical=v=>JSON.stringify(v,(_k,x)=>x&&typeof x==='object'&&!Array.isArray(x)?Object.fromEntries(Object.keys(x).sort().map(k=>[k,x[k]])):x);
1541
+ if(!actual||canonical(actual)!==canonical(request))throw new Error('Actual native pending request body changed');
1542
+ if(request.method==='item/fileChange/requestApproval')await s.manager.replyWithFileChangeApprovalDecision(s.childId,request.id,reply.result.decision);
1543
+ else if(request.method==='item/commandExecution/requestApproval')await s.manager.replyWithCommandExecutionApprovalDecision(s.childId,request.id,reply.result.decision);
1544
+ else throw new Error('Unsupported own-child native permission decision');
1545
+ const after=await s.manager.getConversation(s.childId);
1546
+ if(after?.requests?.some(r=>r.id===request.id))throw new Error('Native decision forwarding is not acknowledged; preserve uncertainty');
1547
+ return {state:'native-response-forwarded',operationEffectVerified:false};
1548
+ }
1549
+ if(p.action==='next'){
1550
+ if(!s.queue.length)await new Promise(resolve=>{const wake=()=>{clearTimeout(timer);resolve();};
1551
+ const timer=setTimeout(()=>{s.waiters=s.waiters.filter(f=>f!==wake);resolve();},10000);s.waiters.push(wake);});
1552
+ if(s.closed)throw new Error('Managed own native event custody was lost');
1553
+ return s.queue.splice(0);
1554
+ }
1555
+ throw new Error('Unsupported closed managed transport operation');
1556
+ })()""".replace('PARAMS',json.dumps(params),1)
1557
+
1558
+ async def evaluate(self,action,value,events=False):
1559
+ ws=self.events_ws if events else self.ws
1560
+ if not ws or self.closed:
1561
+ raise PackError('Owned managed transport is unavailable')
1562
+ if action!='close' and managed_backend_generation(self.lib,self.runtime)!=self.scope['proxyGeneration']:
1563
+ raise PackError('Original selected native execution owner changed; no retargeting')
1564
+ if events:
1565
+ return await asyncio.to_thread(ws.evaluate,self.expression(action,value))
1566
+ async with self.lock:return await asyncio.to_thread(ws.evaluate,self.expression(action,value))
1567
+
1568
+ async def close(self):
1569
+ try:
1570
+ if self.ws and not self.closed:
1571
+ result=await self.evaluate('close',{})
1572
+ self.subscriptions_closed=result.get('state')=='normal-own-managed-subscriptions-closed'
1573
+ finally:
1574
+ self.closed=True;self.ready=False
1575
+ for ws in [self.ws,self.events_ws]:
1576
+ if ws:
1577
+ ws.close()
1578
+
1579
+
1580
+ class OwnedLaneConnection:
1581
+ """Explicit, foreground native stdio connection for one reviewed new lane.
1582
+
1583
+ The existing window manager owns creation, settings, turns and native approval
1584
+ responses. No replacement app-server or unrelated profile is started.
1585
+ """
1586
+ def __init__(self,lib,connection_id,binding):
1587
+ from .storage import safe_id
1588
+ self.lib=lib;self.connection_id=safe_id(connection_id);self.binding=binding
1589
+ fields={'format','schemaVersion','laneId','runtime','binary','binaryDigest','protocolDigest','sourceRoleKey','intakeKey','transport'}
1590
+ if (not isinstance(binding,dict) or set(binding)!=fields or binding['format']!='topolo-owned-lane-connection' or
1591
+ binding['schemaVersion']!=1 or binding['transport']!='codex-managed-window' or binding['laneId']!=self.connection_id or
1592
+ str(lib.active().get('runtime'))!=binding['runtime'] or not pathlib.Path(binding['binary']).is_absolute() or
1593
+ '/intakes/' not in binding['intakeKey']):
1594
+ raise PackError('Explicit exact owned lane connection binding required')
1595
+ binary=pathlib.Path(binding['binary']);info=binary.lstat()
1596
+ if binary.is_symlink() or not binary.is_file() or info.st_uid not in {0,os.getuid()} or info.st_mode&0o022:
1597
+ raise PackError('Native binary must be a regular qualified installed executable')
1598
+ if hashlib.sha256(binary.read_bytes()).hexdigest()!=binding['binaryDigest']:
1599
+ raise PackError('Native binary differs from the reviewed connection binding')
1600
+ self.protocol=qualify_protocol(str(binary),lane_creation=True)
1601
+ from .lanes import digest
1602
+ if digest(self.protocol)!=binding['protocolDigest']:
1603
+ raise PackError('Native protocol differs from the reviewed connection binding')
1604
+ self.directory=lib.root/'dynamic-connections'/self.connection_id
1605
+ self.proxy=BackendProxy(lib,str(binary),['app-server']);self.proxy.emit_native=False
1606
+ self.proxy.lane_connection_binding=binding
1607
+ self.proxy.lane_close_event=asyncio.Event()
1608
+
1609
+ async def run(self):
1610
+ directory=self.directory
1611
+ active=self.lib.active();self.proxy.runtime=self.binding['runtime']
1612
+ self.proxy.execution=load_gate(self.proxy.runtime) or ExecutionGate(prepare_runtime(self.lib,active['components'],self.proxy.runtime))
1613
+ self.proxy.approvals=ApprovalRouter(pathlib.Path(self.proxy.runtime)/'approval-inbox',list(self.proxy.execution.roles.values()),uuid.uuid4().hex)
1614
+ source=[r for r in self.proxy.execution.roles.values() if r['key']==self.binding['sourceRoleKey']]
1615
+ intake=[r for r in self.proxy.execution.roles.values() if r['key']==self.binding['intakeKey']]
1616
+ if len(source)!=1 or len(intake)!=1 or source[0]['intakeKey']!=intake[0]['key']:
1617
+ raise PackError('Owned managed connection has no exact original source and intake route')
1618
+ planned={name:{'nativeId':row['threadId'],'cwd':row['cwd']} for name,row in [('role',source[0]),('intake',intake[0])]}
1619
+ transport=ManagedNativeTransport(self.lib,self.proxy.runtime,self.connection_id)
1620
+ self.proxy.managed_transport=transport;self.proxy.protocol=self.protocol
1621
+ server=None;stop=asyncio.Event();loop=asyncio.get_running_loop();event_task=None;created=False
1622
+ for sig in [signal.SIGINT,signal.SIGTERM]:loop.add_signal_handler(sig,stop.set)
1623
+ try:
1624
+ # A read-only preflight precedes durable custody for native subscriptions.
1625
+ preflight=await asyncio.to_thread(managed_lane_frontend,self.lib,self.proxy.runtime,planned)
1626
+ directory.parent.mkdir(mode=0o700,exist_ok=True);OwnedSmoke.owned(directory.parent,directory=True)
1627
+ directory.mkdir(mode=0o700);created=True;OwnedSmoke.owned(directory,directory=True)
1628
+ atomic_json(directory/'managed-custody.json',{'state':'native-subscriptions-may-follow','laneId':self.connection_id,
1629
+ 'token':transport.token,'targetId':preflight['targetId'],'runtime':self.proxy.runtime,'binding':self.binding})
1630
+ await transport.open(planned)
1631
+ self.proxy.path=lane_socket_path(self.lib,self.connection_id)
1632
+ if self.proxy.path.exists() or self.proxy.path.is_symlink():
1633
+ raise PackError('Own managed control path is occupied; no takeover')
1634
+ self.proxy.initialized=True
1635
+ self.proxy.execution.set_catalog(await self.proxy.read_catalog());self.proxy.catalog_ready.set()
1636
+ server=await asyncio.start_unix_server(self.proxy.control,path=str(self.proxy.path),limit=1024*1024)
1637
+ self.proxy.path.chmod(0o600)
1638
+ atomic_json(directory/'connection.json',{'pid':os.getpid(),'socket':str(self.proxy.path),'token':self.proxy.token,
1639
+ 'connectionId':self.connection_id,'generation':self.proxy.approvals.generation,'binding':self.binding,
1640
+ 'nativeTransport':'existing-managed-window','managedTargetId':transport.target_id})
1641
+ print(json.dumps({'state':'owned-managed-native-lane-connection-ready','connectionId':self.connection_id,'pid':os.getpid(),
1642
+ 'managedTargetId':transport.target_id,'modelTurnsStarted':False,'generation':self.proxy.approvals.generation}),flush=True)
1643
+ event_task=asyncio.create_task(self.proxy.managed_native_events())
1644
+ stopped=asyncio.create_task(stop.wait());normal=asyncio.create_task(self.proxy.lane_close_event.wait())
1645
+ done,pending=await asyncio.wait({stopped,normal,event_task},return_when=asyncio.FIRST_COMPLETED)
1646
+ for task in pending:task.cancel()
1647
+ if event_task in done:
1648
+ await event_task
1649
+ finally:
1650
+ await self.close_owned_controls(transport,server,event_task,created)
1651
+
1652
+ async def close_owned_controls(self,transport,server,event_task,created):
1653
+ directory=self.directory
1654
+ try:await transport.close()
1655
+ finally:
1656
+ if event_task:
1657
+ event_task.cancel()
1658
+ if server:
1659
+ server.close();await server.wait_closed()
1660
+ if created:
1661
+ pending=self.proxy.approvals.records()
1662
+ normal=self.proxy.lane_close_event.is_set() and transport.subscriptions_closed and not self.proxy.active_turns and not any(r.get('state')=='pending' for r in pending)
1663
+ atomic_json(directory/'closure.json',{'state':'closed' if normal else 'closed-with-unresolved-work',
1664
+ 'normalLifecycleClosureVerified':normal,'nativeTransport':'existing-managed-window',
1665
+ 'nativeOwnerStopped':False,'ownSubscriptionsClosed':transport.subscriptions_closed,
1666
+ 'pendingNativeApprovals':sum(r.get('state')=='pending' for r in pending),'replayAuthorized':False})
1667
+ (directory/'connection.json').unlink(missing_ok=True)
1668
+ if server:
1669
+ self.proxy.path.unlink(missing_ok=True)
1670
+
1671
+
1672
+ def lane_socket_path(lib,connection_id):
1673
+ # A fixed short path fits macOS AF_UNIX even with a long library/lane name.
1674
+ # The authenticated socket is mode0600; an existing path is never taken over.
1675
+ identity=hashlib.sha256(canonical([str(lib.root),connection_id])).hexdigest()[:24]
1676
+ return pathlib.Path('/tmp')/('topolo-lane-'+str(os.getuid())+'-'+identity+'.sock')
1677
+
1678
+
1679
+ def request_lane(lib,connection_id,operation,params):
1680
+ from .storage import safe_id
1681
+ directory=lib.root/'dynamic-connections'/safe_id(connection_id)
1682
+ OwnedSmoke.owned(directory,directory=True);OwnedSmoke.owned(directory/'connection.json')
1683
+ record=read_json(directory/'connection.json')
1684
+ if record.get('connectionId')!=connection_id or record.get('socket')!=str(lane_socket_path(lib,connection_id)):
1685
+ raise PackError('Owned lane connection receipt differs')
1686
+ with socket.socket(socket.AF_UNIX,socket.SOCK_STREAM) as connection:
1687
+ connection.settimeout(240);connection.connect(record['socket'])
1688
+ connection.sendall(canonical({'token':record['token'],'operation':operation,'params':params})+b'\n')
1689
+ with connection.makefile('rb') as stream:line=stream.readline(1024*1024)
1690
+ if not line:
1691
+ raise PackError('Owned lane connection closed without ACK; reconcile without replay')
1692
+ result=json.loads(line)
1693
+ if 'error' in result:
1694
+ raise PackError(result['error'])
1695
+ return result
1696
+
872
1697
  def proxy_main():
873
1698
  lib=Library(os.environ['CODEX_PACK_HOME']);real=os.environ['CODEX_PACK_REAL_CLI']
874
1699
  args=sys.argv[1:]
@@ -976,6 +1801,94 @@ def thread_ids(runtime):
976
1801
  def rendered_threads(ws):
977
1802
  return ws.evaluate('Array.from(document.querySelectorAll("[data-app-action-sidebar-thread-row][data-app-action-sidebar-thread-host-id=local]")).map(e=>({id:e.getAttribute("data-app-action-sidebar-thread-id").replace(/^local:/,""),title:e.getAttribute("data-app-action-sidebar-thread-title")}))')
978
1803
 
1804
+
1805
+ def managed_backend_generation(lib,runtime):
1806
+ """Authenticate the existing proxy; a manifest or symlink is not liveness."""
1807
+ active=lib.active()
1808
+ if active.get('runtime')!=str(runtime):
1809
+ raise PackError('Selected native runtime changed')
1810
+ status=request(lib,'status')
1811
+ if (not isinstance(status,dict) or status.get('runtime')!=str(runtime) or status.get('initialized') is not True or
1812
+ status.get('quiesced') is not False or any(type(status.get(k)) is not int or status[k]<1 for k in ['pid','childPid'])):
1813
+ raise PackError('Current authenticated selected backend is unavailable')
1814
+ return {k:status[k] for k in ['pid','childPid','runtime']}
1815
+
1816
+
1817
+ def managed_lane_frontend(lib,runtime,planned,native_id=None,app='/Applications/ChatGPT.app'):
1818
+ """Use only the current managed window's exported native manager/catalog.
1819
+
1820
+ A separate selected-home app-server cannot assert managed visibility. The
1821
+ existing manager must already own the selected storage before any refresh.
1822
+ This function never reconnects, changes home, restarts or creates a worker.
1823
+ """
1824
+ from . import lanes
1825
+ root=pathlib.Path(runtime);home=root/'home'
1826
+ if not home.is_dir():
1827
+ raise PackError('Selected owned native home is unavailable')
1828
+ generation=managed_backend_generation(lib,str(root))
1829
+ expected=[str(home)]
1830
+ layout=lib.root/'desktop-layout.json';stable=lib.root/'desktop-home';layout_hash=None
1831
+ if layout.exists():
1832
+ OwnedSmoke.owned(layout)
1833
+ if read_json(layout).get('owner') not in {'topolo-agent-pack','codex-pack'}:
1834
+ raise PackError('Stable app-host layout is not owned by this pack')
1835
+ OwnedSmoke.owned(stable,directory=True)
1836
+ expected.append(str(stable));layout_hash=hashlib.sha256(layout.read_bytes()).hexdigest()
1837
+ ws,target=renderer(lib)
1838
+ try:
1839
+ capability=adapter(app)
1840
+ params={'adapter':capability,'homes':expected,'configFile':str(home/'config.toml'),
1841
+ 'roles':[planned['role'],planned['intake']],'nativeId':native_id}
1842
+ expression="""(async()=>{
1843
+ const p=PARAMS,services=(await import(p.adapter.module))[p.adapter.export];
1844
+ if(!services?.appServerManagers||!services.localThreadCatalog)throw new Error('Managed native manager/catalog unavailable; no fallback');
1845
+ const opened=await services.appServerManagers.open('local');
1846
+ if(opened?.status!=='ready'||!opened.manager)throw new Error('Existing managed local backend is unavailable');
1847
+ const manager=opened.manager,home=await manager.getCodexHome();
1848
+ if(typeof home!=='string'||!p.homes.includes(home))throw new Error('Existing managed backend is outside the selected owned home');
1849
+ const provenance=await manager.sendRequest('config/read',{cwd:p.roles[0].cwd,includeLayers:true});
1850
+ const users=provenance?.layers?.filter(layer=>layer.name?.type==='user'&&!layer.disabledReason);
1851
+ if(!Array.isArray(users)||!users.length||users.some(layer=>layer.name.file!==p.configFile||typeof layer.version!=='string'||!layer.version))
1852
+ throw new Error('Actual native execution-home config provenance differs');
1853
+ const configOrigins=users.map(layer=>({name:layer.name,version:layer.version}));
1854
+ for(const role of p.roles){const value=await manager.readThread(role.nativeId,{includeTurns:false}),thread=value?.thread;
1855
+ if(thread?.id!==role.nativeId||thread.cwd!==role.cwd||thread.archived===true)throw new Error('Managed backend cannot read exact fixed source/intake');}
1856
+ if(p.nativeId===null)return {home,configOrigins,sourceIds:p.roles.map(r=>r.nativeId),nativeId:null,state:'managed-preflight-read-verified'};
1857
+ if(p.roles.some(r=>r.nativeId===p.nativeId))throw new Error('Cannot register a fixed source/intake as a new lane');
1858
+ const value=await manager.readThread(p.nativeId,{includeTurns:false}),thread=value?.thread;
1859
+ if(thread?.id!==p.nativeId||thread.cwd!==p.roles[0].cwd||thread.archived===true)throw new Error('Managed native own-child read differs');
1860
+ let cursor=null,listed=false;
1861
+ for(let page=0;page<20;page++){
1862
+ const result=await manager.sendRequest('thread/list',{cursor,limit:100,archived:false,modelProviders:[],useStateDbOnly:true});
1863
+ if(!Array.isArray(result?.data))throw new Error('Managed native thread-list response unavailable');
1864
+ if(result.data.some(t=>t.id===p.nativeId)){listed=true;break;}
1865
+ cursor=result.nextCursor;if(cursor===null||cursor===undefined)break;
1866
+ }
1867
+ if(!listed)throw new Error('Exact own child is absent from the managed native list');
1868
+ const catalog=services.localThreadCatalog;
1869
+ await catalog.notifyThread({hostId:'local',threadId:p.nativeId},'upsert');
1870
+ await catalog.requestSync(['local'],'immediate');
1871
+ const entries=await catalog.readEntries([{hostId:'local',threadId:p.nativeId}]);
1872
+ if(!Array.isArray(entries)||entries.length!==1||entries[0].threadId!==p.nativeId||entries[0].hostId!=='local')
1873
+ throw new Error('Managed own-child catalog registration was not acknowledged');
1874
+ if(await manager.getCodexHome()!==home)throw new Error('Managed backend home changed during native registration');
1875
+ return {state:'managed-native-read-list-catalog-verified',home,configOrigins,nativeId:p.nativeId,sourceIds:p.roles.map(r=>r.nativeId),
1876
+ nativeRead:true,nativeList:true,catalogRead:true,rendered:!!document.querySelector('[data-app-action-sidebar-thread-row][data-app-action-sidebar-thread-id="local:'+CSS.escape(p.nativeId)+'"]')};
1877
+ })()""".replace('PARAMS',json.dumps(params),1)
1878
+ result=ws.evaluate(expression)
1879
+ if (not isinstance(result,dict) or result.get('home') not in expected or result.get('nativeId')!=native_id or
1880
+ result.get('sourceIds')!=[r['nativeId'] for r in params['roles']]):
1881
+ raise PackError('Exact existing managed backend acknowledgement differs')
1882
+ if native_id is not None and (result.get('state')!='managed-native-read-list-catalog-verified' or
1883
+ any(result.get(k) is not True for k in ['nativeRead','nativeList','catalogRead'])):
1884
+ raise PackError('Managed native child read/list/catalog proof is incomplete')
1885
+ if managed_backend_generation(lib,str(root))!=generation:
1886
+ raise PackError('Selected native backend generation changed during managed proof')
1887
+ if layout_hash is not None and hashlib.sha256(layout.read_bytes()).hexdigest()!=layout_hash:
1888
+ raise PackError('Stable owned app-host layout changed during managed proof')
1889
+ return {**result,'targetId':target,'selectedRuntime':str(root),'capability':capability,'proxyGeneration':generation}
1890
+ finally:ws.close()
1891
+
979
1892
  def checkpoint_ui(lib,runtime):
980
1893
  ws,_=renderer(lib)
981
1894
  try:
@@ -1075,3 +1988,56 @@ def bind(lib,runtime):
1075
1988
  if backup.exists():raise PackError('Cannot preserve existing desktop-home file: '+name)
1076
1989
  dest.rename(backup)
1077
1990
  dest.symlink_to(expected)
1991
+
1992
+
1993
+ def compile_native_cli_command(value,store,pack,input_path,environment):
1994
+ """Compile a fixed native tool command; no shell/model text is accepted."""
1995
+ import shlex
1996
+ from . import lanes
1997
+ value=lanes.native_cli_input(value)
1998
+ allowed={'HOME','USER','LOGNAME','PATH','LANG','LC_ALL','TMPDIR','TOPOLO_CONFIG_PATH','TOPOLO_ENV','TOPOLO_CONTEXT'}
1999
+ if (not isinstance(environment,dict) or not set(environment)<=allowed or
2000
+ any(not isinstance(v,str) or len(v)>4096 or any(ord(c)<32 for c in v) for v in environment.values()) or
2001
+ not environment.get('TOPOLO_CONFIG_PATH') or not pathlib.Path(environment['TOPOLO_CONFIG_PATH']).is_absolute() or
2002
+ environment.get('TOPOLO_ENV')!=value['context']['environment'] or environment.get('TOPOLO_CONTEXT')!=value['context']['contextKey']):
2003
+ raise PackError('Exact child-only native-cli environment required; inherited credentials are forbidden')
2004
+ if any(not pathlib.Path(p).is_absolute() for p in [store,input_path]) or pack!=value['sourceRoleKey'].split('/')[0]:
2005
+ raise PackError('Exact owned store, pack and input references required')
2006
+ argv=['/usr/bin/env','-i',*[k+'='+environment[k] for k in sorted(environment)],value['cli']['nodeExecutable'],value['cli']['entry'],
2007
+ 'agent-packs','--store',str(store),'remote-context',pack,'--environment',value['context']['environment'],
2008
+ '--context-key',value['context']['contextKey'],'--role',value['context']['logicalId'],'--continuity-input',str(input_path)]
2009
+ return shlex.join(argv)
2010
+
2011
+
2012
+ def native_cli_codex_tool_proof(value,command,result,turn_id,cwd,guide_command,guide_digest):
2013
+ """Verify actual owned Codex tool DTOs; printed/foreign receipts do not ACK."""
2014
+ import shlex
2015
+ from . import lanes
2016
+ value=lanes.native_cli_input(value);lanes.native_cli_hash(guide_digest)
2017
+ if guide_digest!=value['source']['charterDigest']:
2018
+ raise PackError('Native-cli actual charter read differs from the pinned receipt source')
2019
+ thread=result.get('thread',{});turns=[t for t in thread.get('turns',[]) if t.get('id')==turn_id]
2020
+ if (thread.get('id')!=value['nativeId'] or thread.get('cwd')!=str(cwd) or len(turns)!=1 or
2021
+ turns[0].get('status')!='completed' or turns[0].get('error')):
2022
+ raise PackError('Exact same-child native-cli turn/cwd completion required')
2023
+ receipts=[];reads=[]
2024
+ for item in turns[0].get('items',[]):
2025
+ if item.get('type')!='commandExecution' or item.get('status')!='completed' or type(item.get('exitCode'))is not int or item['exitCode']!=0 or not item.get('id'):
2026
+ continue
2027
+ actual=item.get('command');args=shlex.split(actual) if isinstance(actual,str) else []
2028
+ unwrapped=args[2] if len(args)==3 and args[0] in {'/bin/sh','/bin/bash','/bin/zsh'} and args[1]=='-c' else actual
2029
+ if unwrapped==guide_command and hashlib.sha256(item.get('aggregatedOutput','').encode()).hexdigest()==guide_digest:
2030
+ reads.append(item['id'])
2031
+ if unwrapped!=command:
2032
+ continue
2033
+ try:
2034
+ receipt=json.loads(item.get('aggregatedOutput',''))
2035
+ except (ValueError,TypeError):
2036
+ continue
2037
+ receipt=lanes.native_cli_receipt(value,receipt);receipts.append((item['id'],receipt))
2038
+ if len(receipts)!=1 or len(reads)!=1 or receipts[0][0]==reads[0]:
2039
+ raise PackError('Actual native-cli command/receipt and own charter read require unique successful tool results')
2040
+ identity,receipt=receipts[0]
2041
+ proof={'harness':'codex','nativeId':value['nativeId'],'generation':value['nativeConnectionGeneration'],'turnId':turn_id,
2042
+ 'itemId':identity,'commandDigest':lanes.digest(command),'exitCode':0,'receiptDigest':lanes.digest(receipt)}
2043
+ return {'receipt':receipt,'proof':proof,'nativeCharterReadItemId':reads[0]}