@topolo/cli 0.13.10 → 0.13.12

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.
@@ -606,6 +606,12 @@ class BackendProxy:
606
606
 
607
607
  async def qualify_organization(self,thread_id):
608
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'])
609
615
  if role and not self.permission_configuration and (pathlib.Path(self.runtime)/'permissions-operation.json').exists():
610
616
  raise PackError('Unresolved native permission operation requires reconciliation before owned execution or grants')
611
617
  if not role or not role.get('installationConsent'):
@@ -635,8 +641,6 @@ class BackendProxy:
635
641
  raise PackError('Fresh organization policy prohibits the child-only manual verification profile')
636
642
  role['organizationPolicyProof']=proof
637
643
  if role.get('dynamicLaneId'):
638
- from .lanes import Registry
639
- record=Registry(self.lib.root,'codex').read(role['dynamicLaneId'])
640
644
  current=await self.dynamic_current(record['plan'])
641
645
  configuring=self._lane_configuration_record
642
646
  if configuring and configuring['nativeId']==thread_id and self.lane_busy:
@@ -1010,6 +1014,8 @@ class BackendProxy:
1010
1014
  self.lane_idle();store=lanes.Registry(self.lib.root,'codex');record=store.read(params['laneId'])
1011
1015
  if not record or record['state']!='loaded-context-ACK':
1012
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'])
1013
1019
  current=await self.dynamic_current(record['plan'])
1014
1020
  if current!={k:record['plan'][k] for k in ['role','intake','ceiling']}:
1015
1021
  raise PackError('Current bootstrap source or policy differs')
@@ -1086,6 +1092,8 @@ class BackendProxy:
1086
1092
  self.lane_idle();store=lanes.Registry(self.lib.root,'codex');record=store.read(params['laneId'])
1087
1093
  if not record or record['plan'].get('verificationProfile')!='manual':
1088
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'])
1089
1097
  current=await self.dynamic_current(record['plan'])
1090
1098
  if current!={k:record['plan'][k] for k in ['role','intake','ceiling']}:
1091
1099
  raise PackError('Current source/intake/policy changed before own fixture verification')
@@ -1149,6 +1157,8 @@ class BackendProxy:
1149
1157
  self.lane_idle();store=lanes.Registry(self.lib.root,'codex');planned=store.read_plan(params['laneId'])
1150
1158
  if store.read(params['laneId']) is not None:
1151
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'])
1152
1162
  current=await self.dynamic_current(planned)
1153
1163
  if current!={k:planned[k] for k in ['role','intake','ceiling']}:
1154
1164
  raise PackError('Source, intake or policy changed before native creation')
@@ -1202,6 +1212,9 @@ class BackendProxy:
1202
1212
 
1203
1213
  async def dynamic_lane(self, operation, params):
1204
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'))
1205
1218
  self.lane_idle()
1206
1219
  if self.lane_busy:
1207
1220
  raise PackError('An owned dynamic lane operation is already active')
@@ -1254,6 +1267,8 @@ class BackendProxy:
1254
1267
  record=store.read(params['laneId'])
1255
1268
  if not record:
1256
1269
  raise PackError('No owned dynamic lane registration')
1270
+ if record['plan'].get('continuityTransport')=='native-cli':
1271
+ lanes.native_cli_admission(record['plan']['continuity'])
1257
1272
  current=await self.dynamic_current(record['plan'])
1258
1273
  if current!={k:record['plan'][k] for k in ['role','intake','ceiling']}:
1259
1274
  raise PackError('Dynamic source, intake or policy drift; no native mutation')
@@ -1973,3 +1988,407 @@ def bind(lib,runtime):
1973
1988
  if backup.exists():raise PackError('Cannot preserve existing desktop-home file: '+name)
1974
1989
  dest.rename(backup)
1975
1990
  dest.symlink_to(expected)
1991
+
1992
+
1993
+ def adoption_require(value,message):
1994
+ if not value:raise PackError('Runtime adoption refused: '+message)
1995
+
1996
+
1997
+ def adoption_keys(value,keys):
1998
+ adoption_require(isinstance(value,dict) and set(value)==set(keys),'closed fields required: '+','.join(keys))
1999
+
2000
+
2001
+ def adoption_json(raw):
2002
+ def unique(pairs):
2003
+ adoption_require(len(pairs)==len({key for key,_ in pairs}),'duplicate JSON fields')
2004
+ return dict(pairs)
2005
+ return json.loads(raw,object_pairs_hook=unique)
2006
+
2007
+
2008
+ def adoption_snapshot(path,read=True,missing=False,profile=None):
2009
+ """Single-file custody. Credential profiles use read=False exclusively."""
2010
+ import stat
2011
+ if read and profile is not None:adoption_read_guard(profile,[path])
2012
+ path=pathlib.Path(path)
2013
+ adoption_require(path.is_absolute() and path==path.resolve(),'absolute physical path required')
2014
+ parents=[]
2015
+ for parent in path.parents:
2016
+ st=parent.lstat()
2017
+ adoption_require(stat.S_ISDIR(st.st_mode) and st.st_uid in {0,os.getuid()},'unowned parent')
2018
+ parents.append([str(parent),st.st_dev,st.st_ino])
2019
+ try:st=path.lstat()
2020
+ except FileNotFoundError:
2021
+ adoption_require(missing,'required file absent')
2022
+ return None,None
2023
+ adoption_require(stat.S_ISREG(st.st_mode) and st.st_nlink==1 and st.st_uid==os.getuid(),'owned single regular file required')
2024
+ value={'device':st.st_dev,'inode':st.st_ino,'uid':st.st_uid,'mode':st.st_mode&0o777,
2025
+ 'bytes':st.st_size,'mtimeNs':st.st_mtime_ns,'ctimeNs':st.st_ctime_ns,'parents':parents}
2026
+ raw=None
2027
+ if read:
2028
+ adoption_require(st.st_size<=128*1024*1024,'file exceeds limit')
2029
+ fd=os.open(path,os.O_RDONLY|os.O_NOFOLLOW)
2030
+ with os.fdopen(fd,'rb') as stream:
2031
+ opened=os.fstat(stream.fileno())
2032
+ adoption_require((opened.st_dev,opened.st_ino,opened.st_uid,opened.st_mode,opened.st_nlink,opened.st_size,opened.st_mtime_ns,opened.st_ctime_ns)==
2033
+ (st.st_dev,st.st_ino,st.st_uid,st.st_mode,st.st_nlink,st.st_size,st.st_mtime_ns,st.st_ctime_ns),'file changed before content read')
2034
+ raw=stream.read()
2035
+ after=path.lstat()
2036
+ adoption_require((after.st_dev,after.st_ino,after.st_size,after.st_mtime_ns,after.st_ctime_ns)==
2037
+ (st.st_dev,st.st_ino,st.st_size,st.st_mtime_ns,st.st_ctime_ns),'file changed while reading')
2038
+ value['sha256']=hashlib.sha256(raw).hexdigest()
2039
+ return value,raw
2040
+
2041
+
2042
+ def adoption_pin(value,profile=None):
2043
+ adoption_keys(value,['path','sha256'])
2044
+ snapshot,raw=adoption_snapshot(value['path'],profile=profile)
2045
+ adoption_require(snapshot['sha256']==value['sha256'],'source or preservation pin changed')
2046
+ return raw
2047
+
2048
+
2049
+ def adoption_profile(profile):
2050
+ adoption_keys(profile,['path','publicPath','metadata','environment','contextKey','principalId'])
2051
+ adoption_require(profile['environment'] in {'production','staging','development'} and
2052
+ isinstance(profile['contextKey'],str) and re.fullmatch(r'org:[A-Za-z0-9_-]+',profile['contextKey']) and
2053
+ isinstance(profile['principalId'],str) and re.fullmatch(r'[A-Za-z0-9_-]+',profile['principalId']), 'explicit private identity tuple required')
2054
+ current,_=adoption_snapshot(profile['path'],read=False)
2055
+ adoption_require(current==profile['metadata'] and current['mode']==0o600,'private profile metadata changed')
2056
+ public,_=adoption_snapshot(profile['publicPath'],read=False,missing=True)
2057
+ adoption_require(profile['path']!=profile['publicPath'] and (public is None or
2058
+ (public['device'],public['inode'])!=(current['device'],current['inode'])),'private/public profile alias')
2059
+
2060
+
2061
+ def adoption_read_guard(profile,paths):
2062
+ """Classify every declared readable input before any non-manifest content read."""
2063
+ adoption_profile(profile)
2064
+ protected=[(str(pathlib.Path(profile[key]).resolve()),adoption_snapshot(profile[key],read=False,missing=True)[0])
2065
+ for key in ['path','publicPath']]
2066
+ for path in paths:
2067
+ current,_=adoption_snapshot(path,read=False,missing=True)
2068
+ adoption_require(all(str(pathlib.Path(path).resolve())!=name and
2069
+ (current is None or saved is None or (current['device'],current['inode'])!=(saved['device'],saved['inode']))
2070
+ for name,saved in protected),'readable input aliases protected profile; no contents read')
2071
+
2072
+
2073
+ def adoption_runtime_paths(runtime):
2074
+ return [runtime[key]['path'] for key in ['node','cli','mcp']]+[pin['path'] for pin in runtime['cohort'].values()]+[pin['path'] for pin in runtime['adapterFiles']]
2075
+
2076
+
2077
+ def adoption_read_inputs(value):
2078
+ paths=[value['ownerGeneration']['path'],*adoption_runtime_paths(value['runtime']),*[pin['path'] for pin in value['preserve']],
2079
+ *[target['path'] for target in value.get('targets',[])]]
2080
+ if 'launcher' in value:paths.append(value['launcher']['path'])
2081
+ adoption_read_guard(value['profile'],paths)
2082
+
2083
+
2084
+ def adoption_runtime(runtime,profile):
2085
+ adoption_keys(runtime,['node','cli','mcp','cohort','pythonSource','adapterFiles','osEnvironment'])
2086
+ adoption_read_guard(profile,adoption_runtime_paths(runtime))
2087
+ for key in ['node','cli','mcp']:adoption_pin(runtime[key],profile)
2088
+ adoption_keys(runtime['cohort'],['cli','mcp','sdk'])
2089
+ for key,name,version in [('cli','@topolo/cli','0.13.11'),('mcp','@topolo/mcp','0.14.26'),('sdk','@topolo/sdk','0.23.21')]:
2090
+ package=json.loads(adoption_pin(runtime['cohort'][key],profile))
2091
+ adoption_require(package.get('name')==name and package.get('version')==version,'immutable target cohort differs')
2092
+ if key!='sdk':
2093
+ adoption_require(runtime[key]['path']==str(pathlib.Path(runtime['cohort'][key]['path']).parent/'dist/index.js') and
2094
+ package.get('dependencies',{}).get('@topolo/sdk')=='0.23.21','entry/dependency cohort differs')
2095
+ source=pathlib.Path(runtime['pythonSource'])
2096
+ adoption_require(source.is_absolute() and source==source.resolve(),'physical adapter source required')
2097
+ adoption_require(isinstance(runtime['adapterFiles'],list) and len(runtime['adapterFiles'])==3 and
2098
+ {p['path'] for p in runtime['adapterFiles']}=={str(source/'topolo_agent_pack'/n) for n in ['cli.py','live.py','topolo.py']},'exact successor adapter source pins required')
2099
+ for value in runtime['adapterFiles']:adoption_pin(value,profile)
2100
+ env=runtime['osEnvironment']
2101
+ adoption_require(isinstance(env,dict) and {'HOME','PATH'}<=set(env)<=
2102
+ {'HOME','PATH','USER','LOGNAME','LANG','LC_ALL','TMPDIR'} and
2103
+ all(isinstance(v,str) and v and len(v)<=4096 and not any(ord(c)<32 for c in v) for v in env.values()),'closed nonsecret OS environment required')
2104
+ env=dict(env)
2105
+ env.update(TOPOLO_CONFIG_PATH=profile['path'],TOPOLO_ENV=profile['environment'],TOPOLO_CONTEXT=profile['contextKey'],
2106
+ TOPOLO_API_KEY='',TOPOLO_ACCESS_TOKEN='')
2107
+ prefix='TOPOLO_'+profile['environment'].upper()
2108
+ env.update({prefix+'_API_KEY':'',prefix+'_ACCESS_TOKEN':''})
2109
+ return env
2110
+
2111
+
2112
+ def adoption_alias(alias,target):
2113
+ adoption_keys(alias,['path','linkTarget','metadata']);p=pathlib.Path(alias['path']);st=p.lstat()
2114
+ value=[st.st_dev,st.st_ino,st.st_uid,st.st_mode,st.st_size,st.st_mtime_ns,st.st_ctime_ns]
2115
+ adoption_require(p.is_symlink() and os.readlink(p)==alias['linkTarget'] and str(p.resolve())==target and
2116
+ st.st_uid==os.getuid() and value==alias['metadata'],'shared alias changed')
2117
+
2118
+
2119
+ def adoption_config(raw,target,mcp_launcher):
2120
+ """Registration-only transform; never installs skills or rewrites context."""
2121
+ import tomllib
2122
+ adoption_require(target['format'] in {'json','toml'} and target['rootKey'] in {'mcp_servers','mcpServers'},'unsupported configuration format')
2123
+ config=adoption_json(raw) if target['format']=='json' else tomllib.loads(raw.decode())
2124
+ servers=config.get(target['rootKey'])
2125
+ adoption_require(isinstance(servers,dict),'existing server map required')
2126
+ names=[v['name'] for v in target['transports']]
2127
+ adoption_require(isinstance(target['untouchedServers'],list) and all(isinstance(n,str) for n in target['untouchedServers']) and
2128
+ len(set(target['untouchedServers']))==len(target['untouchedServers']) and
2129
+ not set(names)&set(target['untouchedServers']) and set(servers)==set(names)|set(target['untouchedServers']), 'incomplete physical server map')
2130
+ for transport in target['transports']:
2131
+ name=transport['name']
2132
+ adoption_require(isinstance(name,str) and re.fullmatch(r'[A-Za-z0-9_-]+',name) and isinstance(servers.get(name),dict),'unambiguous existing local transport required')
2133
+ entry=servers[name]
2134
+ adoption_require(isinstance(entry.get('command'),str) and 'url' not in entry and 'env_vars' not in entry,'local explicit transport required')
2135
+ adoption_require(not entry.get('env',{}).get('TOPOLO_AGENT_INSTALLATION_ID'),'installation mode requires a separate explicit adoption')
2136
+ agent=entry.get('env',{}).get('TOPOLO_AGENT_NAME','')
2137
+ adoption_require(isinstance(agent,str) and (not agent or re.fullmatch(r'[A-Za-z0-9_-]+',agent)),'agent name differs')
2138
+ entry['command']=mcp_launcher;entry['args']=[agent] if agent else [];entry['env']={'TOPOLO_AGENT_NAME':agent} if agent else {}
2139
+ if target['format']=='json':return (json.dumps(config,indent=2)+'\n').encode()
2140
+ text=raw.decode()
2141
+ for transport in target['transports']:
2142
+ name=transport['name'];entry=servers[name]
2143
+ # Qualified simple tables only. Quoted/dotted/array-table variants refuse;
2144
+ # they cannot silently coexist with a new duplicate registration.
2145
+ section='[mcp_servers.'+name+']';environment='[mcp_servers.'+name+'.env]'
2146
+ adoption_require(target['rootKey']=='mcp_servers' and text.splitlines().count(section)==1 and
2147
+ text.splitlines().count(environment)<=1,'unsupported TOML registration layout')
2148
+ kept=[];skipping=False
2149
+ for line in text.splitlines():
2150
+ if line.strip() in {section,environment}:skipping=True
2151
+ elif line.lstrip().startswith('['):
2152
+ adoption_require(not line.strip().startswith('[mcp_servers.'+name+'.'),'unsupported nested transport table')
2153
+ skipping=False
2154
+ if not skipping:kept.append(line)
2155
+ values=[]
2156
+ for key,value in entry.items():
2157
+ if key=='env':continue
2158
+ adoption_require(re.fullmatch(r'[A-Za-z0-9_-]+',key) and
2159
+ (isinstance(value,(str,bool,int)) or isinstance(value,list) and all(isinstance(v,str) for v in value)), 'unsupported TOML transport value')
2160
+ values.append(key+' = '+json.dumps(value))
2161
+ text='\n'.join(kept).rstrip()+'\n\n'+section+'\n'+'\n'.join(values)+'\n\n'+environment+'\n'+\
2162
+ '\n'.join(key+' = '+json.dumps(value) for key,value in entry['env'].items())+'\n'
2163
+ adoption_require(tomllib.loads(text)==config,'TOML semantic preservation differs')
2164
+ return text.encode()
2165
+
2166
+
2167
+ def adoption_bootstrap(source,descriptor,runtime):
2168
+ """Check successor bytes before importing them, including the verifier itself."""
2169
+ pins=[(p['path'],p['sha256']) for p in runtime['runtime']['adapterFiles']]
2170
+ pins.append((str(descriptor),hashlib.sha256(canonical(runtime)+b'\n').hexdigest()))
2171
+ return 'import hashlib,os,pathlib,stat,sys\nsys.dont_write_bytecode=True\nfor name,digest in '+repr(pins)+':\n'+\
2172
+ ' p=pathlib.Path(name);s=p.lstat()\n'+\
2173
+ ' if p!=p.resolve() or not stat.S_ISREG(s.st_mode) or s.st_uid!=os.getuid() or s.st_nlink!=1:raise SystemExit("Runtime source custody changed")\n'+\
2174
+ ' with p.open("rb") as f: actual=hashlib.file_digest(f,"sha256").hexdigest()\n'+\
2175
+ ' if actual!=digest:raise SystemExit("Runtime source changed before import")\n'+\
2176
+ 'sys.path.insert(0,'+repr(source)+')\n'
2177
+
2178
+
2179
+ def adoption_known_launcher(raw,root,profile=None):
2180
+ import ast
2181
+ lines=raw.decode().splitlines();tail=['from topolo_agent_pack.live import proxy_main','raise SystemExit(proxy_main())']
2182
+ if len(lines)==5 and lines[1]=='import sys' and lines[2].startswith('sys.path.insert(0,'):
2183
+ return lines[0].startswith('#!/') and lines[-2:]==tail
2184
+ assignments=[line for line in lines if line.startswith('os.environ["TOPOLO_AGENT_PACK_RUNTIME"]=')]
2185
+ if len(assignments)!=1:return False
2186
+ descriptor=pathlib.Path(ast.literal_eval(assignments[0].split('=',1)[1]))
2187
+ adoption_require(descriptor.parent==root/'launchers' and descriptor.name.startswith('private-runtime-'),'unknown managed launcher descriptor')
2188
+ _,value=adoption_snapshot(descriptor,profile=profile);runtime=json.loads(value)
2189
+ expected=lines[0]+'\n'+adoption_bootstrap(runtime['runtime']['pythonSource'],descriptor,runtime)+assignments[0]+'\n'+'\n'.join(tail)+'\n'
2190
+ return raw==expected.encode()
2191
+
2192
+
2193
+ def adoption_preflight(store,manifest):
2194
+ adoption_keys(manifest,['schemaVersion','operationId','library','ownerGeneration','consumers','profile','runtime','targets','launcher','preserve'])
2195
+ root=pathlib.Path(store)
2196
+ adoption_require(manifest['schemaVersion']==1 and type(manifest['schemaVersion']) is int and
2197
+ isinstance(manifest['operationId'],str) and re.fullmatch(r'[a-f0-9]{8}-(?:[a-f0-9]{4}-){3}[a-f0-9]{12}',manifest['operationId']) and
2198
+ root.is_absolute() and str(root)==manifest['library'] and root==root.resolve() and root.stat().st_uid==os.getuid(),'owned library/operation required')
2199
+ def inside(path):
2200
+ adoption_require(pathlib.Path(path).is_relative_to(root),'destination outside owned library')
2201
+ adoption_read_inputs(manifest)
2202
+ inside(manifest['ownerGeneration']['path']);adoption_pin(manifest['ownerGeneration'],manifest['profile'])
2203
+ consumers=manifest['consumers']
2204
+ adoption_require(isinstance(consumers,list) and 1<=len(consumers)<=256 and all(isinstance(c,str) and c for c in consumers) and len(set(consumers))==len(consumers),'closed unique consumer set required')
2205
+ adoption_profile(manifest['profile']);adoption_runtime(manifest['runtime'],manifest['profile'])
2206
+ descriptor=root/'launchers'/('private-runtime-'+manifest['operationId']+'.json')
2207
+ mcp_launcher=root/'launchers'/('private-mcp-'+manifest['operationId'])
2208
+ adoption_require(isinstance(manifest['preserve'],list) and 1<=len(manifest['preserve'])<=2048,'preservation pins required')
2209
+ for value in manifest['preserve']:inside(value['path']);adoption_pin(value,manifest['profile'])
2210
+ targets=manifest['targets'];adoption_require(isinstance(targets,list) and 1<=len(targets)<=256,'nonempty effective transport targets required')
2211
+ writes=[];seen=set();pairs=set()
2212
+ for target in targets:
2213
+ adoption_keys(target,['path','before','aliases','format','rootKey','consumers','transports','untouchedServers'])
2214
+ inside(target['path']);before,raw=adoption_snapshot(target['path'],profile=manifest['profile'])
2215
+ adoption_require(before==target['before'] and target['path'] not in seen,'target preimage changed or duplicated')
2216
+ seen.add(target['path'])
2217
+ adoption_require(isinstance(target['consumers'],list) and target['consumers'] and set(target['consumers'])<=set(consumers),'out-of-scope shared consumer')
2218
+ for alias in target['aliases']:
2219
+ inside(alias['path']);adoption_alias(alias,target['path'])
2220
+ adoption_require(isinstance(target['transports'],list) and target['transports'],'effective transport rows required')
2221
+ for transport in target['transports']:
2222
+ adoption_keys(transport,['name','origin','consumers'])
2223
+ pair=(target['path'],transport['name'])
2224
+ adoption_require(pair not in pairs and transport['origin']==target['path'] and isinstance(transport['consumers'],list) and
2225
+ transport['consumers'] and set(transport['consumers'])<=set(target['consumers']),'transport origin/consumer/precedence conflict')
2226
+ pairs.add(pair)
2227
+ writes.append((target['path'],before,adoption_config(raw,target,str(mcp_launcher))))
2228
+ adoption_require(set(consumers)=={c for t in targets for v in t['transports'] for c in v['consumers']},'incomplete effective consumer manifest')
2229
+ launcher=manifest['launcher'];adoption_keys(launcher,['path','before'])
2230
+ adoption_require(launcher['path']==str(root/'launchers/topolo-agent-pack-backend') and launcher['path'] not in seen,'exact owned launcher required')
2231
+ before,raw=adoption_snapshot(launcher['path'],profile=manifest['profile']);adoption_require(before==launcher['before'],'launcher preimage changed')
2232
+ lines=raw.decode().splitlines()
2233
+ adoption_require(adoption_known_launcher(raw,root,manifest['profile']),'unrecognized launcher source')
2234
+ current,_=adoption_snapshot(descriptor,missing=True,profile=manifest['profile']);adoption_require(current is None,'runtime descriptor already exists; reconcile without replay')
2235
+ current,_=adoption_snapshot(mcp_launcher,missing=True,profile=manifest['profile']);adoption_require(current is None,'MCP launcher already exists; reconcile without replay')
2236
+ runtime={'schemaVersion':1,'library':str(root),'ownerGeneration':manifest['ownerGeneration'],'profile':manifest['profile'],
2237
+ 'runtime':manifest['runtime'],'preserve':manifest['preserve']}
2238
+ writes.append((str(descriptor),None,canonical(runtime)+b'\n'))
2239
+ source=manifest['runtime']['pythonSource']
2240
+ adoption_require(' ' not in lines[0],'MCP wrapper requires an absolute Python shebang without flags')
2241
+ mcp_code=lines[0]+' -I\n'+adoption_bootstrap(source,descriptor,runtime)+'from topolo_agent_pack.topolo import managed_mcp_main\nraise SystemExit(managed_mcp_main('+repr(str(descriptor))+',sys.argv[1:]))\n'
2242
+ writes.append((str(mcp_launcher),None,mcp_code.encode()))
2243
+ code=lines[0]+'\n'+adoption_bootstrap(source,descriptor,runtime)+'os.environ["TOPOLO_AGENT_PACK_RUNTIME"]='+repr(str(descriptor))+'\nfrom topolo_agent_pack.live import proxy_main\nraise SystemExit(proxy_main())\n'
2244
+ writes.append((launcher['path'],before,code.encode()))
2245
+ protected={manifest['profile']['path'],manifest['profile']['publicPath'],manifest['ownerGeneration']['path'],*[p['path'] for p in manifest['preserve']]}
2246
+ protected.update(pin['path'] for pin in [manifest['runtime'][k] for k in ['node','cli','mcp']]+
2247
+ list(manifest['runtime']['cohort'].values())+manifest['runtime']['adapterFiles'])
2248
+ adoption_require(not protected&{p for p,_,_ in writes},'destination overlaps protected profile/generation/context')
2249
+ return writes
2250
+
2251
+
2252
+ def private_runtime_adoption(store,manifest_path,operation,plan_digest=None,confirm=False):
2253
+ try:return _private_runtime_adoption(store,manifest_path,operation,plan_digest,confirm)
2254
+ except (KeyError,TypeError,AttributeError,SyntaxError,UnicodeError) as error:
2255
+ raise PackError('Runtime adoption refused: malformed typed manifest or owned launcher') from error
2256
+
2257
+
2258
+ def _private_runtime_adoption(store,manifest_path,operation,plan_digest=None,confirm=False):
2259
+ """Owner-reviewed configuration transaction, NOT native/Auth admission.
2260
+
2261
+ The closed manifest is the owner's effect scope. It does not certify a native
2262
+ inventory, current grants, health, startup or a successful tool result.
2263
+ """
2264
+ adoption_require(operation in {'plan','apply'} and (operation=='apply' and confirm is True and
2265
+ isinstance(plan_digest,str) and re.fullmatch(r'[a-f0-9]{64}',plan_digest) or
2266
+ operation=='plan' and plan_digest is None and confirm is False),'closed plan/confirmed apply required')
2267
+ manifest_before,raw=adoption_snapshot(manifest_path);adoption_require(len(raw)<=2*1024*1024,'manifest exceeds limit')
2268
+ manifest=adoption_json(raw);writes=adoption_preflight(store,manifest)
2269
+ journal=pathlib.Path(store)/'launchers'/('adoption-'+manifest['operationId']+'.json')
2270
+ adoption_read_guard(manifest['profile'],[journal,*[p+'.'+manifest['operationId']+'.tmp' for p,_,_ in writes]])
2271
+ adoption_require(not journal.exists() and not journal.is_symlink(),'operation already reserved; reconcile without replay')
2272
+ plan={'schemaVersion':1,'operationId':manifest['operationId'],'manifestDigest':hashlib.sha256(raw).hexdigest(),
2273
+ 'writes':[{'path':p,'before':before,'afterDigest':hashlib.sha256(value).hexdigest()} for p,before,value in writes],
2274
+ 'claim':'CONFIGURATION_ONLY_NO_NATIVE_IDENTITY_HEALTH_PERMISSION_OR_ADOPTION_ACK'}
2275
+ digest=hashlib.sha256(canonical(plan)).hexdigest()
2276
+ if operation=='plan':return {**plan,'planDigest':digest}
2277
+ adoption_require(digest==plan_digest,'reviewed plan changed')
2278
+ profile=manifest['profile']
2279
+ adoption_require(adoption_snapshot(manifest_path,profile=profile)[0]==manifest_before,'manifest changed before reservation')
2280
+ from contextlib import ExitStack
2281
+ with ExitStack() as custody:
2282
+ parent_fd=os.open(journal.parent,os.O_RDONLY|os.O_DIRECTORY|os.O_NOFOLLOW);custody.callback(os.close,parent_fd)
2283
+ fd=os.open(journal.name,os.O_RDWR|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,0o600,dir_fd=parent_fd);custody.callback(os.close,fd)
2284
+ def generation(st):
2285
+ return (st.st_dev,st.st_ino,st.st_uid,st.st_mode,st.st_nlink,st.st_size,st.st_mtime_ns,st.st_ctime_ns)
2286
+ def parent_identity(st):
2287
+ return (st.st_dev,st.st_ino,st.st_uid,st.st_mode)
2288
+ parent_generation=parent_identity(os.fstat(parent_fd));journal_generation=generation(os.fstat(fd))
2289
+ journal_parents=adoption_snapshot(journal,read=False)[0]['parents']
2290
+ record={'operationId':manifest['operationId'],'planDigest':digest,'state':'may-have-config-effects','completed':[]}
2291
+ def journal_guard():
2292
+ try:
2293
+ current=adoption_snapshot(journal,read=False)[0];held=generation(os.fstat(fd));named=generation(journal.lstat())
2294
+ parent=parent_identity(os.fstat(parent_fd));named_parent=parent_identity(journal.parent.lstat())
2295
+ except OSError as error:
2296
+ raise PackError('Runtime adoption refused: journal custody changed; may have config effects; retain evidence') from error
2297
+ adoption_require(held==named==journal_generation and held[4]==1 and current['mode']==0o600 and
2298
+ parent==named_parent==parent_generation and current['parents']==journal_parents,
2299
+ 'journal custody changed; may have config effects; retain evidence')
2300
+ def checkpoint():
2301
+ nonlocal journal_generation
2302
+ journal_guard();data=canonical(record)+b'\n'
2303
+ os.lseek(fd,0,os.SEEK_SET);os.ftruncate(fd,0)
2304
+ adoption_require(os.write(fd,data)==len(data),'journal checkpoint incomplete; retain evidence');os.fsync(fd)
2305
+ os.lseek(fd,0,os.SEEK_SET)
2306
+ adoption_require(os.read(fd,len(data)+1)==data,'journal checkpoint differs; retain evidence')
2307
+ updated=generation(os.fstat(fd))
2308
+ adoption_require(updated[:5]==journal_generation[:5],'journal descriptor generation changed; retain evidence')
2309
+ journal_generation=updated;journal_guard()
2310
+ def revalidate():
2311
+ adoption_read_inputs(manifest)
2312
+ adoption_pin(manifest['ownerGeneration'],profile);adoption_profile(profile);adoption_runtime(manifest['runtime'],profile)
2313
+ for keep in manifest['preserve']:adoption_pin(keep,profile)
2314
+ for completed in record['completed']:adoption_require(adoption_snapshot(completed['path'],profile=profile)[0]==completed['after'],'completed file changed')
2315
+ for target in manifest['targets']:
2316
+ for alias in target['aliases']:adoption_alias(alias,target['path'])
2317
+ adoption_require(adoption_snapshot(manifest_path,profile=profile)[0]==manifest_before,'manifest changed during apply')
2318
+ journal_guard()
2319
+ checkpoint()
2320
+ # Recheck the whole completed set at both sides of each replacement,
2321
+ # including the last launcher; prepared is never inferred from one file.
2322
+ for path,before,value in writes:
2323
+ revalidate()
2324
+ current,_=adoption_snapshot(path,missing=True,profile=profile);adoption_require(current==before,'destination changed before write')
2325
+ temporary=path+'.'+manifest['operationId']+'.tmp'
2326
+ mode=before['mode'] if before else (0o700 if path.endswith('/private-mcp-'+manifest['operationId']) else 0o600)
2327
+ adoption_read_guard(profile,[temporary])
2328
+ handle=os.open(temporary,os.O_WRONLY|os.O_CREAT|os.O_EXCL|os.O_NOFOLLOW,mode)
2329
+ with os.fdopen(handle,'wb') as stream:
2330
+ temporary_identity=os.fstat(stream.fileno());os.fchmod(stream.fileno(),mode)
2331
+ stream.write(value);stream.flush();os.fsync(stream.fileno())
2332
+ revalidate()
2333
+ temp,actual=adoption_snapshot(temporary,profile=profile)
2334
+ adoption_require((temp['device'],temp['inode'])==(temporary_identity.st_dev,temporary_identity.st_ino) and actual==value,'temporary custody changed; retain journal')
2335
+ adoption_require(adoption_snapshot(manifest_path,profile=profile)[0]==manifest_before,'manifest changed during apply')
2336
+ current,_=adoption_snapshot(path,missing=True,profile=profile);adoption_require(current==before,'destination changed during write; retained temp and journal')
2337
+ journal_guard();os.replace(temporary,path)
2338
+ after,actual=adoption_snapshot(path,profile=profile);adoption_require(actual==value,'afterimage differs; retain journal')
2339
+ record['completed'].append({'path':path,'after':after});revalidate();checkpoint()
2340
+ revalidate();record['state']='configuration-prepared-native-adoption-unverified';checkpoint();journal_guard()
2341
+ return {'operationId':manifest['operationId'],'planDigest':digest,'state':record['state'],'journal':str(journal),'nativeACK':False}
2342
+
2343
+
2344
+ def compile_native_cli_command(value,store,pack,input_path,environment):
2345
+ """Compile a fixed native tool command; no shell/model text is accepted."""
2346
+ import shlex
2347
+ from . import lanes
2348
+ value=lanes.native_cli_input(value)
2349
+ allowed={'HOME','USER','LOGNAME','PATH','LANG','LC_ALL','TMPDIR','TOPOLO_CONFIG_PATH','TOPOLO_ENV','TOPOLO_CONTEXT'}
2350
+ if (not isinstance(environment,dict) or not set(environment)<=allowed or
2351
+ any(not isinstance(v,str) or len(v)>4096 or any(ord(c)<32 for c in v) for v in environment.values()) or
2352
+ not environment.get('TOPOLO_CONFIG_PATH') or not pathlib.Path(environment['TOPOLO_CONFIG_PATH']).is_absolute() or
2353
+ environment.get('TOPOLO_ENV')!=value['context']['environment'] or environment.get('TOPOLO_CONTEXT')!=value['context']['contextKey']):
2354
+ raise PackError('Exact child-only native-cli environment required; inherited credentials are forbidden')
2355
+ if any(not pathlib.Path(p).is_absolute() for p in [store,input_path]) or pack!=value['sourceRoleKey'].split('/')[0]:
2356
+ raise PackError('Exact owned store, pack and input references required')
2357
+ argv=['/usr/bin/env','-i',*[k+'='+environment[k] for k in sorted(environment)],value['cli']['nodeExecutable'],value['cli']['entry'],
2358
+ 'agent-packs','--store',str(store),'remote-context',pack,'--environment',value['context']['environment'],
2359
+ '--context-key',value['context']['contextKey'],'--role',value['context']['logicalId'],'--continuity-input',str(input_path)]
2360
+ return shlex.join(argv)
2361
+
2362
+
2363
+ def native_cli_codex_tool_proof(value,command,result,turn_id,cwd,guide_command,guide_digest):
2364
+ """Verify actual owned Codex tool DTOs; printed/foreign receipts do not ACK."""
2365
+ import shlex
2366
+ from . import lanes
2367
+ value=lanes.native_cli_input(value);lanes.native_cli_hash(guide_digest)
2368
+ if guide_digest!=value['source']['charterDigest']:
2369
+ raise PackError('Native-cli actual charter read differs from the pinned receipt source')
2370
+ thread=result.get('thread',{});turns=[t for t in thread.get('turns',[]) if t.get('id')==turn_id]
2371
+ if (thread.get('id')!=value['nativeId'] or thread.get('cwd')!=str(cwd) or len(turns)!=1 or
2372
+ turns[0].get('status')!='completed' or turns[0].get('error')):
2373
+ raise PackError('Exact same-child native-cli turn/cwd completion required')
2374
+ receipts=[];reads=[]
2375
+ for item in turns[0].get('items',[]):
2376
+ 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'):
2377
+ continue
2378
+ actual=item.get('command');args=shlex.split(actual) if isinstance(actual,str) else []
2379
+ unwrapped=args[2] if len(args)==3 and args[0] in {'/bin/sh','/bin/bash','/bin/zsh'} and args[1]=='-c' else actual
2380
+ if unwrapped==guide_command and hashlib.sha256(item.get('aggregatedOutput','').encode()).hexdigest()==guide_digest:
2381
+ reads.append(item['id'])
2382
+ if unwrapped!=command:
2383
+ continue
2384
+ try:
2385
+ receipt=json.loads(item.get('aggregatedOutput',''))
2386
+ except (ValueError,TypeError):
2387
+ continue
2388
+ receipt=lanes.native_cli_receipt(value,receipt);receipts.append((item['id'],receipt))
2389
+ if len(receipts)!=1 or len(reads)!=1 or receipts[0][0]==reads[0]:
2390
+ raise PackError('Actual native-cli command/receipt and own charter read require unique successful tool results')
2391
+ identity,receipt=receipts[0]
2392
+ proof={'harness':'codex','nativeId':value['nativeId'],'generation':value['nativeConnectionGeneration'],'turnId':turn_id,
2393
+ 'itemId':identity,'commandDigest':lanes.digest(command),'exitCode':0,'receiptDigest':lanes.digest(receipt)}
2394
+ return {'receipt':receipt,'proof':proof,'nativeCharterReadItemId':reads[0]}